Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cca0b4663 | ||
|
|
5a2e057e69 | ||
|
|
da88aadda2 | ||
|
|
657b69da6c | ||
|
|
f251e54174 | ||
|
|
fcc57d9be4 | ||
|
|
88eba2cdcb | ||
|
|
0e49b197b3 | ||
|
|
b59fa63b63 | ||
|
|
eff98118d5 | ||
|
|
c599df1cfb | ||
|
|
66c52c8826 | ||
|
|
f3a5dd93ad | ||
|
|
4044458baa | ||
|
|
9ab94b3050 | ||
|
|
9a504adc58 | ||
|
|
57cbff42e7 | ||
|
|
634ea933f1 | ||
|
|
0ca30291cb | ||
|
|
95f85b71b1 | ||
|
|
6b5976be8f | ||
|
|
3530e9e99a | ||
|
|
7a9f9bfd55 | ||
|
|
1e04a52d1b | ||
|
|
7daeb9e978 | ||
|
|
da2750f4ee | ||
|
|
df88c54f90 | ||
|
|
69e1985ec1 | ||
|
|
6267d1d777 | ||
|
|
84e5c5ad56 | ||
|
|
6e8fd40014 | ||
|
|
a4a110dcef | ||
|
|
8335cf5421 | ||
|
|
ae5c5d48b2 | ||
|
|
72ba068beb | ||
|
|
142cb470a0 | ||
|
|
cad3673bd2 | ||
|
|
4796b65390 | ||
|
|
e0d6017d95 | ||
|
|
d6ea21bae4 | ||
|
|
af12039686 | ||
|
|
ed1d5f999b | ||
|
|
c595ff5370 | ||
|
|
e9f662cc17 | ||
|
|
70ab2a6fc2 | ||
|
|
f35fbce38b | ||
|
|
eeb246ebc2 | ||
|
|
e157217f50 | ||
|
|
cc12e8f70a | ||
|
|
a9a1c1aa52 | ||
|
|
742e3a1027 | ||
|
|
45f596e0d4 | ||
|
|
4402a28e2b | ||
|
|
693d1959ac | ||
|
|
792366fddb | ||
|
|
b2670aedf1 | ||
|
|
5d488bbcd2 | ||
|
|
e2a1144c7c | ||
|
|
010c42753f | ||
|
|
7a215736fe | ||
|
|
c61a63d5bb | ||
|
|
7cb4e1c945 | ||
|
|
3a40d35fdf | ||
|
|
d658cd0ae5 |
No files matched your search
@@ -1,6 +1,6 @@
|
|||||||
The MIT License
|
The MIT License
|
||||||
|
|
||||||
Copyright (c) 2008-2013 Seth J. Morabito <web@loomcom.com>
|
Copyright (c) 2008-2014 Seth J. Morabito <web@loomcom.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
SYMON - A 6502 System Simulator
|
SYMON - A 6502 System Simulator
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
**Version:** 1.0.0
|
**Version:** 1.2.1
|
||||||
|
|
||||||
**Last Updated:** 11 August, 2014
|
**Last Updated:** 8 January, 2016
|
||||||
|
|
||||||
See the file COPYING for license.
|
See the file COPYING for license.
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ are always welcome.
|
|||||||
|
|
||||||
## 2.0 Requirements
|
## 2.0 Requirements
|
||||||
|
|
||||||
- Java 1.7 or higher
|
- Java 1.8 or higher
|
||||||
- Maven 2.0.x or higher (for building from source)
|
- Maven 2.0.x or higher (for building from source)
|
||||||
- JUnit 4 or higher (for testing)
|
- JUnit 4 or higher (for testing)
|
||||||
|
|
||||||
@@ -58,6 +58,7 @@ memory.
|
|||||||
- `$0000`--`$DFFF`: 56KB RAM
|
- `$0000`--`$DFFF`: 56KB RAM
|
||||||
- `$E000`--`$FFFF`: 8KB ROM
|
- `$E000`--`$FFFF`: 8KB ROM
|
||||||
- `$FFD0`--`$FFD1`: Motorola 6850 ACIA
|
- `$FFD0`--`$FFD1`: Motorola 6850 ACIA
|
||||||
|
- `$FFD8`--`$FFDF`: Controller for SD cards
|
||||||
|
|
||||||
### 3.1.3 Simple Memory Map
|
### 3.1.3 Simple Memory Map
|
||||||
|
|
||||||
@@ -102,19 +103,32 @@ Memory contents can be viewed (and edited) one page at a time through the Memory
|
|||||||
|
|
||||||
![Trace Log] (https://github.com/sethm/symon/raw/master/screenshots/trace_log.png)
|
![Trace Log] (https://github.com/sethm/symon/raw/master/screenshots/trace_log.png)
|
||||||
|
|
||||||
The last 20,000 execution steps are disassembled and logged to the Trace Log Window.
|
The last 20,000 execution steps are disassembled and logged to the Trace Log
|
||||||
|
Window.
|
||||||
|
|
||||||
### 3.6 NEW - Experimental 6545 CRTC Video
|
### 3.6 Simulator Speeds
|
||||||
|
|
||||||
|
![Speeds] (https://github.com/sethm/symon/raw/master/screenshots/simulator_menu.png)
|
||||||
|
|
||||||
|
Simulated speeds may be set from 1MHz to 8MHz.
|
||||||
|
|
||||||
|
### 3.7 Breakpoints
|
||||||
|
|
||||||
|
![Breakpoints] (https://github.com/sethm/symon/raw/master/screenshots/breakpoints.png)
|
||||||
|
|
||||||
|
Breakpoints can be set and removed through the Breakpoints window.
|
||||||
|
|
||||||
|
### 3.8 Experimental 6545 CRTC Video
|
||||||
|
|
||||||
![Composite Video] (https://github.com/sethm/symon/raw/master/screenshots/video_window.png)
|
![Composite Video] (https://github.com/sethm/symon/raw/master/screenshots/video_window.png)
|
||||||
|
|
||||||
This feature is highly experimental. It's possible to open a video window from the "View" menu.
|
This feature is highly experimental. It's possible to open a video window
|
||||||
This window simulates the output of a MOS 6545 CRT Controller located at address `$9000` and
|
from the "View" menu. This window simulates the output of a MOS 6545 CRT
|
||||||
`$9001`.
|
Controller located at address `$9000` and `$9001`.
|
||||||
|
|
||||||
By default, the 40 x 25 character display uses video memory located at base address `$7000`.
|
By default, the 40 x 25 character display uses video memory located at base
|
||||||
This means that the memory from address `$7000` (28672 decimal) to `$73E8` (29672 decimal)
|
address `$7000`. This means that the memory from address `$7000` (28672
|
||||||
is directly mapped to video.
|
decimal) to `$73E8` (29672 decimal) is directly mapped to video.
|
||||||
|
|
||||||
- Address Register (at address `$9000`)
|
- Address Register (at address `$9000`)
|
||||||
- R1: Horizontal Displayed Columns
|
- R1: Horizontal Displayed Columns
|
||||||
@@ -127,18 +141,20 @@ is directly mapped to video.
|
|||||||
- R14: Cursor Position (High Byte)
|
- R14: Cursor Position (High Byte)
|
||||||
- R15: Cursor Position (Low Byte)
|
- R15: Cursor Position (Low Byte)
|
||||||
|
|
||||||
Although the simulation is pretty good, there are a few key differences between
|
Although the simulation is pretty good, there are a few key differences
|
||||||
the simulated 6545 and a real 6545:
|
between the simulated 6545 and a real 6545:
|
||||||
|
|
||||||
- The simulated 6545 supports only the straight binary addressing mode of the real 6545,
|
- The simulated 6545 supports only the straight binary addressing
|
||||||
and not the Row/Column addressing mode.
|
mode of the real 6545, and not the Row/Column addressing mode.
|
||||||
- The simulated 6545 has full 16 bit addressing, where the real 6545 has only
|
|
||||||
a 14-bit address bus.
|
- The simulated 6545 has full 16 bit addressing, where the real 6545
|
||||||
- The simulation is done at a whole-frame level, meaning that lots of
|
has only a 14-bit address bus.
|
||||||
6545 programming tricks that were achieved by updating the frame address
|
|
||||||
during vertical and horizontal sync times are not achievable. There is no way
|
- The simulation is done at a whole-frame level, meaning that lots
|
||||||
(for example) to change the Display Start Address (R12 and R13) while a
|
of 6545 programming tricks that were achieved by updating the
|
||||||
frame is being drawn.
|
frame address during vertical and horizontal sync times are not
|
||||||
|
achievable. There is no way (for example) to change the Display Start
|
||||||
|
Address (R12 and R13) while a frame is being drawn.
|
||||||
|
|
||||||
For more information on the 6545 CRTC and its programming model, please see the following resources
|
For more information on the 6545 CRTC and its programming model, please see the following resources
|
||||||
|
|
||||||
@@ -147,7 +163,7 @@ For more information on the 6545 CRTC and its programming model, please see the
|
|||||||
- [MOS 6545 Datasheet (PDF)] (http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
|
- [MOS 6545 Datasheet (PDF)] (http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
|
||||||
|
|
||||||
|
|
||||||
#### 3.6.1 Example BASIC Program to test Video
|
#### 3.8.1 Example BASIC Program to test Video
|
||||||
|
|
||||||
This program will fill the video screen with all printable characters.
|
This program will fill the video screen with all printable characters.
|
||||||
|
|
||||||
@@ -170,9 +186,9 @@ Maven will build Symon, run unit tests, and produce a jar file in the
|
|||||||
`target` directory containing the compiled simulator.
|
`target` directory containing the compiled simulator.
|
||||||
|
|
||||||
Symon is meant to be invoked directly from the jar file. To run with
|
Symon is meant to be invoked directly from the jar file. To run with
|
||||||
Java 1.5 or greater, just type:
|
Java 1.8 or greater, just type:
|
||||||
|
|
||||||
$ java -jar symon-0.9.1.0.jar
|
$ java -jar symon-1.2.0.jar
|
||||||
|
|
||||||
When Symon is running, you should be presented with a simple graphical
|
When Symon is running, you should be presented with a simple graphical
|
||||||
interface.
|
interface.
|
||||||
@@ -219,7 +235,22 @@ running.
|
|||||||
|
|
||||||
## 5.0 Revision History
|
## 5.0 Revision History
|
||||||
|
|
||||||
- **1.0.0-SNAPSHOT:** 10 August, 2014 - Added "Simple" machine
|
- **1.2.1:** 8 January, 2016 - Remove dependency on Java 8. Now
|
||||||
|
supports compiling and running under Java 1.7.
|
||||||
|
|
||||||
|
- **1.2.0:** 3 January, 2016 - Add symbolic disassembly to breakpoints
|
||||||
|
window.
|
||||||
|
|
||||||
|
- **1.1.1:** 2 January, 2016 - Minor enhancement: Allows breakpoints
|
||||||
|
to be added with the Enter key.
|
||||||
|
|
||||||
|
- **1.1.0:** 31 December, 2015 - Fixed delay loop to better
|
||||||
|
simulate various clock speeds. Added ability to select clock
|
||||||
|
speed at runtime. Status display now shows the next instruction
|
||||||
|
to be executed, instead of the last instruction executed.
|
||||||
|
Added support for breakpoints.
|
||||||
|
|
||||||
|
- **1.0.0:** 10 August, 2014 - Added "Simple" machine
|
||||||
implementation, pure RAM with no IO. Added Klaus Dormann's
|
implementation, pure RAM with no IO. Added Klaus Dormann's
|
||||||
6502 Functional Tests for further machine verification (these
|
6502 Functional Tests for further machine verification (these
|
||||||
tests must be run in the "Simple" machine).
|
tests must be run in the "Simple" machine).
|
||||||
@@ -261,7 +292,15 @@ running.
|
|||||||
|
|
||||||
- **0.1:** 20 January, 2010
|
- **0.1:** 20 January, 2010
|
||||||
|
|
||||||
## 6.0 To Do
|
## 6.0 Roadmap
|
||||||
|
|
||||||
|
- **1.2:** Better breakpoint support. Symbolic debugging of breakpoints.
|
||||||
|
|
||||||
|
- **2.0:** Complete rewrite of the UI in JavaFX instead of Swing. Complete
|
||||||
|
assembler and disassembler built in. Ability to attach source code for
|
||||||
|
symbolic debugging.
|
||||||
|
|
||||||
|
## 7.0 To Do
|
||||||
|
|
||||||
- Feedback (in the form of dialogs, status bar, etc).
|
- Feedback (in the form of dialogs, status bar, etc).
|
||||||
|
|
||||||
@@ -279,9 +318,13 @@ running.
|
|||||||
- Allow displaying ACIA status and dumping ACIA buffers, for
|
- Allow displaying ACIA status and dumping ACIA buffers, for
|
||||||
debugging.
|
debugging.
|
||||||
|
|
||||||
|
- CRTC emulation is very naive. The whole frame is drawn in one
|
||||||
|
CPU step. This should be improved by drawing scan lines during
|
||||||
|
machine steps to approximate real NTSC/PAL refresh rates.
|
||||||
|
|
||||||
- Symbolic debugging.
|
- Symbolic debugging.
|
||||||
|
|
||||||
## 7.0 Copyright and Acknowledgements
|
## 8.0 Copyright and Acknowledgements
|
||||||
|
|
||||||
**Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>**
|
**Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>**
|
||||||
|
|
||||||
@@ -301,7 +344,7 @@ This project would not have been possible without the following resources:
|
|||||||
- [Neil Parker's "The 6502/65C02/65C816 Instruction Set Decoded"] (http://www.llx.com/~nparker/a2/opcodes.html),
|
- [Neil Parker's "The 6502/65C02/65C816 Instruction Set Decoded"] (http://www.llx.com/~nparker/a2/opcodes.html),
|
||||||
for information about how instructions are coded
|
for information about how instructions are coded
|
||||||
|
|
||||||
## 8.0 Licensing
|
## 9.0 Licensing
|
||||||
|
|
||||||
Symon is free software. It is distributed under the MIT License.
|
Symon is free software. It is distributed under the MIT License.
|
||||||
Please see the file 'COPYING' for full details of the license.
|
Please see the file 'COPYING' for full details of the license.
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
<groupId>com.loomcom.symon</groupId>
|
<groupId>com.loomcom.symon</groupId>
|
||||||
<artifactId>symon</artifactId>
|
<artifactId>symon</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.0.0</version>
|
<version>1.3.0-SNAPSHOT</version>
|
||||||
<name>symon</name>
|
<name>symon</name>
|
||||||
<url>http://www.loomcom.com/symon</url>
|
<url>http://www.loomcom.com/symon</url>
|
||||||
<properties>
|
<properties>
|
||||||
@@ -35,6 +35,11 @@
|
|||||||
</repository>
|
</repository>
|
||||||
</repositories>
|
</repositories>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>ch.qos.logback</groupId>
|
||||||
|
<artifactId>logback-classic</artifactId>
|
||||||
|
<version>1.1.3</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
<artifactId>junit</artifactId>
|
<artifactId>junit</artifactId>
|
||||||
@@ -56,31 +61,10 @@
|
|||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
|
||||||
<artifactId>maven-resources-plugin</artifactId>
|
|
||||||
<version>2.6</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>copy-resources</id>
|
|
||||||
<phase>process-resources</phase>
|
|
||||||
<goals>
|
|
||||||
<goal>copy-resources</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<outputDirectory>${basedir}/target/classes/com/loomcom/symon/ui/images</outputDirectory>
|
|
||||||
<resources>
|
|
||||||
<resource>
|
|
||||||
<directory>src/main/java/com/loomcom/symon/ui/images</directory>
|
|
||||||
</resource>
|
|
||||||
</resources>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<version>2.4</version>
|
<version>2.6</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<descriptorRefs>
|
<descriptorRefs>
|
||||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||||
@@ -107,8 +91,8 @@
|
|||||||
<version>3.1</version>
|
<version>3.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<compilerArgument>-Xlint:unchecked</compilerArgument>
|
<compilerArgument>-Xlint:unchecked</compilerArgument>
|
||||||
<source>1.6</source>
|
<source>1.7</source>
|
||||||
<target>1.6</target>
|
<target>1.7</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
@@ -116,7 +100,7 @@
|
|||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-jar-plugin</artifactId>
|
<artifactId>maven-jar-plugin</artifactId>
|
||||||
<version>2.4</version>
|
<version>2.6</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ echo_irq: echo_irq.o
|
|||||||
$(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o
|
$(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o
|
||||||
|
|
||||||
echo_irq.o:
|
echo_irq.o:
|
||||||
$(CA) --listing -o echo_irq.o echo_irq.asm
|
$(CA) --listing echo_irq.lst -o echo_irq.o echo_irq.asm
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.rom *.map *.lst
|
rm -f *.o *.rom *.map *.lst
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
MEMORY {
|
MEMORY {
|
||||||
RAM1: start = $0000, size = $8000;
|
RAM1: start = $0000, size = $8000 - __STACKSIZE__;
|
||||||
ROM1: start = $C000, size = $3FFA, fill = yes;
|
ROM1: start = $C000, size = $3FFA, fill = yes;
|
||||||
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
|
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,6 @@ VECTORS: load = ROMV, type = ro;
|
|||||||
}
|
}
|
||||||
|
|
||||||
SYMBOLS {
|
SYMBOLS {
|
||||||
__STACKSIZE__ = $0300;
|
__STACKSIZE__: type = weak, value = $0300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ ehbasic: ehbasic.o
|
|||||||
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
|
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
|
||||||
|
|
||||||
ehbasic.o:
|
ehbasic.o:
|
||||||
$(CA) --listing -o ehbasic.o min_mon.asm
|
$(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o *.rom *.map *.lst
|
rm -f *.o *.rom *.map *.lst
|
||||||
@@ -150,7 +150,7 @@ END_CODE
|
|||||||
; sign on string
|
; sign on string
|
||||||
|
|
||||||
LAB_mess
|
LAB_mess
|
||||||
.byte $0D,$0A,"Symon (c) 2008-2013, Seth Morabito"
|
.byte $0D,$0A,"Symon (c) 2008-2014, Seth Morabito"
|
||||||
.byte $0D,$0A,"Enhanced 6502 BASIC 2.22 (c) Lee Davison"
|
.byte $0D,$0A,"Enhanced 6502 BASIC 2.22 (c) Lee Davison"
|
||||||
.byte $0D,$0A,"[C]old/[W]arm ?",$00
|
.byte $0D,$0A,"[C]old/[W]arm ?",$00
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
MEMORY {
|
MEMORY {
|
||||||
RAM1: start = $0000, size = $8000;
|
RAM1: start = $0000, size = $8000 - __STACKSIZE__;
|
||||||
ROM1: start = $C000, size = $3F00, fill = yes;
|
ROM1: start = $C000, size = $3F00, fill = yes;
|
||||||
MONITOR: start = $FF00, size = $FA, fill = yes;
|
MONITOR: start = $FF00, size = $FA, fill = yes;
|
||||||
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
|
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
|
||||||
@@ -13,6 +13,6 @@ VECTORS: load = ROMV, type = ro;
|
|||||||
}
|
}
|
||||||
|
|
||||||
SYMBOLS {
|
SYMBOLS {
|
||||||
__STACKSIZE__ = $0300;
|
__STACKSIZE__: type = weak, value = $0300;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
*.rom
|
||||||
|
*.lst
|
||||||
|
*.map
|
||||||
|
*.o
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
CA=ca65
|
||||||
|
LD=ld65
|
||||||
|
|
||||||
|
all: ehbasic
|
||||||
|
|
||||||
|
ehbasic: ehbasic.o
|
||||||
|
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
|
||||||
|
|
||||||
|
ehbasic.o:
|
||||||
|
$(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.rom *.map *.lst
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
This directoy contains a very slightly modified version of EhBASIC 2.22
|
||||||
|
to support the Symon simulator.
|
||||||
|
|
||||||
|
Usage
|
||||||
|
-----
|
||||||
|
|
||||||
|
The pre-assemled ROM image 'ehbasic.rom' in the "samples" directory is all you
|
||||||
|
need unless you want to rebuild this source code.
|
||||||
|
|
||||||
|
Just copy the image 'ehbasic.rom' to the directory where you run Symon, and
|
||||||
|
rename the file to 'rom.bin'. It will be loaded at memory location $D000 when
|
||||||
|
the simulator starts up. Click "Run" and you'll be presented with BASIC.
|
||||||
|
|
||||||
|
At the first prompt, type 'C' for a Cold Start
|
||||||
|
|
||||||
|
When prompted for free memory, type: $C000
|
||||||
|
|
||||||
|
Note that EhBASIC only accepts upper case input, so you'll need to use caps
|
||||||
|
lock (the cruise control for cool) to really make the most of it.
|
||||||
|
|
||||||
|
|
||||||
|
Building
|
||||||
|
--------
|
||||||
|
|
||||||
|
|
||||||
|
To build it, you'll need the CC65 tool chain from http://www.cc65.org/
|
||||||
|
and a suitable version of 'make'. Just typing:
|
||||||
|
|
||||||
|
% make
|
||||||
|
|
||||||
|
in this directory should re-build everything. You'll get a listing,
|
||||||
|
some object files, and the ROM image itself.
|
||||||
|
|
||||||
|
|
||||||
|
Changes from the EhBASIC 2.22
|
||||||
|
-----------------------------
|
||||||
|
|
||||||
|
- Minor syntax changes to allow assembly with the cc65 tool chain.
|
||||||
|
|
||||||
|
- Memory map modified for Symon.
|
||||||
|
|
||||||
|
- At reset, configure the 6551 ACIA for 8-N-1, 2400 baud.
|
||||||
|
|
||||||
|
- Monitor routines 'ACIAin' and 'ACIAout' modified for the 6551 ACIA.
|
||||||
|
Specifically, reading and writing will check the 6551 status register for
|
||||||
|
the status of rx and tx registers before receive or transmit.
|
||||||
|
|
||||||
|
EhBASIC is copyright Lee Davison <leeedavison@googlemail.com>
|
||||||
|
|
||||||
|
My changes are so slight that I hesitate to even say this, but for "CYA"
|
||||||
|
reasons:
|
||||||
|
|
||||||
|
Changes are copyright Seth Morabito <web@loomcom.com> and are distributed under
|
||||||
|
the same license as EhBASIC. I claim no commercial interest whatsoever. Any
|
||||||
|
commercial use must be negotiated directly with Lee Davison.
|
||||||
|
|
||||||
|
|
||||||
|
Original EhBASIC 2.22 README
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
Enhanced BASIC is a BASIC interpreter for the 6502 family microprocessors. It
|
||||||
|
is constructed to be quick and powerful and easily ported between 6502 systems.
|
||||||
|
It requires few resources to run and includes instructions to facilitate easy
|
||||||
|
low level handling of hardware devices. It also retains most of the powerful
|
||||||
|
high level instructions from similar BASICs.
|
||||||
|
|
||||||
|
EhBASIC is free but not copyright free. For non commercial use there is only one
|
||||||
|
restriction, any derivative work should include, in any binary image distributed,
|
||||||
|
the string "Derived from EhBASIC" and in any distribution that includes human
|
||||||
|
readable files a file that includes the above string in a human readable form
|
||||||
|
e.g. not as a comment in an HTML file.
|
||||||
|
|
||||||
|
For commercial use please contact me, Lee Davison, at leeedavison@googlemail.com
|
||||||
|
for conditions.
|
||||||
|
|
||||||
|
For more information on EhBASIC, other versions of EhBASIC and other projects
|
||||||
|
please visit my site at ..
|
||||||
|
|
||||||
|
http://mycorner.no-ip.org/index.html
|
||||||
|
|
||||||
|
|
||||||
|
P.S. c't magazin, henceforth refered to as "those thieving german bastards", are
|
||||||
|
prohibited from using this or any version of EhBASIC for any of their projects
|
||||||
|
or products. The excuse "we don't charge people for it" doesn't wash, it adds
|
||||||
|
value to your product so you owe me.
|
||||||
@@ -0,0 +1,449 @@
|
|||||||
|
|
||||||
|
; minimal monitor for EhBASIC and 6502 simulator V1.05
|
||||||
|
|
||||||
|
; To run EhBASIC on the simulator load and assemble [F7] this file, start the simulator
|
||||||
|
; running [F6] then start the code with the RESET [CTRL][SHIFT]R. Just selecting RUN
|
||||||
|
; will do nothing, you'll still have to do a reset to run the code.
|
||||||
|
|
||||||
|
.feature labels_without_colons
|
||||||
|
.include "basic.asm"
|
||||||
|
|
||||||
|
; put the IRQ and MNI code in RAM so that it can be changed
|
||||||
|
|
||||||
|
IRQ_vec = VEC_SV+2 ; IRQ code vector
|
||||||
|
NMI_vec = IRQ_vec+$0A ; NMI code vector
|
||||||
|
|
||||||
|
; setup for the 6502 simulator environment
|
||||||
|
|
||||||
|
IO_AREA = $8800
|
||||||
|
ACIAdata = IO_AREA ; simulated ACIA r/w port
|
||||||
|
ACIAstatus = IO_AREA+1
|
||||||
|
ACIAcommand = IO_AREA+2
|
||||||
|
ACIAcontrol = IO_AREA+3
|
||||||
|
|
||||||
|
CRTC_A = $9000 ; CRTC register select
|
||||||
|
CRTC_C = $9001 ; CRTC register IO
|
||||||
|
CADDR_L = $E0 ; Cursor address (low)
|
||||||
|
CADDR_H = $E1 ; Cursor address (high)
|
||||||
|
C_COL = $E2 ; Cursor column
|
||||||
|
C_ROW = $E3 ; Cursor row
|
||||||
|
COUTC = $E4 ; Temp storage for char out.
|
||||||
|
R_STRT = $E5 ; E5,E6 contains starting address of
|
||||||
|
; current row
|
||||||
|
R_NXT = $E7 ; E7,E8 contains starting address of
|
||||||
|
; next row
|
||||||
|
|
||||||
|
N_ROWS = 24
|
||||||
|
N_COLS = 40
|
||||||
|
|
||||||
|
|
||||||
|
; now the code. all this does is set up the vectors and interrupt code
|
||||||
|
; and wait for the user to select [C]old or [W]arm start. nothing else
|
||||||
|
; fits in less than 128 bytes
|
||||||
|
|
||||||
|
.segment "MONITOR"
|
||||||
|
.org $FC00 ; pretend this is in a 1/8K ROM
|
||||||
|
|
||||||
|
; reset vector points here
|
||||||
|
|
||||||
|
RES_vec:
|
||||||
|
CLD ; clear decimal mode
|
||||||
|
LDX #$FF ; empty stack
|
||||||
|
TXS ; set the stack
|
||||||
|
|
||||||
|
;; Initialize the CRTC
|
||||||
|
LDA #$01
|
||||||
|
STA CRTC_A
|
||||||
|
LDA #N_COLS ; 40 columns
|
||||||
|
STA CRTC_C
|
||||||
|
LDA #$06
|
||||||
|
STA CRTC_A
|
||||||
|
LDA #N_ROWS ; 24 rows
|
||||||
|
STA CRTC_C
|
||||||
|
LDA #$70
|
||||||
|
STA CADDR_H
|
||||||
|
LDA #$00
|
||||||
|
STA CADDR_L
|
||||||
|
STA C_ROW
|
||||||
|
STA C_COL
|
||||||
|
JSR CLRVID
|
||||||
|
;; TODO: Initialize params on CRTC
|
||||||
|
|
||||||
|
; Initialize the ACIA
|
||||||
|
ACIA_init:
|
||||||
|
LDA #$00
|
||||||
|
STA ACIAstatus ; Soft reset
|
||||||
|
LDA #$0B
|
||||||
|
STA ACIAcommand ; Parity disabled, IRQ disabled
|
||||||
|
LDA #$1E
|
||||||
|
STA ACIAcontrol ; Set output for 8-N-1 9600
|
||||||
|
|
||||||
|
; set up vectors and interrupt code, copy them to page 2
|
||||||
|
|
||||||
|
LDY #END_CODE-LAB_vec ; set index/count
|
||||||
|
LAB_stlp:
|
||||||
|
LDA LAB_vec-1,Y ; get byte from interrupt code
|
||||||
|
STA VEC_IN-1,Y ; save to RAM
|
||||||
|
DEY ; decrement index/count
|
||||||
|
BNE LAB_stlp ; loop if more to do
|
||||||
|
|
||||||
|
; now do the signon message, Y = $00 here
|
||||||
|
|
||||||
|
LAB_signon:
|
||||||
|
LDA LAB_mess,Y ; get byte from sign on message
|
||||||
|
BEQ LAB_nokey ; exit loop if done
|
||||||
|
|
||||||
|
JSR V_OUTP ; output character
|
||||||
|
INY ; increment index
|
||||||
|
BNE LAB_signon ; loop, branch always
|
||||||
|
|
||||||
|
LAB_nokey:
|
||||||
|
JSR V_INPT ; call scan input device
|
||||||
|
BCC LAB_nokey ; loop if no key
|
||||||
|
|
||||||
|
AND #$DF ; mask xx0x xxxx, ensure upper case
|
||||||
|
CMP #'W' ; compare with [W]arm start
|
||||||
|
BEQ LAB_dowarm ; branch if [W]arm start
|
||||||
|
|
||||||
|
CMP #'C' ; compare with [C]old start
|
||||||
|
BNE RES_vec ; loop if not [C]old start
|
||||||
|
|
||||||
|
JMP LAB_COLD ; do EhBASIC cold start
|
||||||
|
|
||||||
|
LAB_dowarm:
|
||||||
|
JMP LAB_WARM ; do EhBASIC warm start
|
||||||
|
|
||||||
|
|
||||||
|
ACIAout:
|
||||||
|
|
||||||
|
PHA
|
||||||
|
@loop: LDA ACIAstatus
|
||||||
|
AND #$10
|
||||||
|
BEQ @loop ; Wait for buffer to empty
|
||||||
|
PLA
|
||||||
|
STA ACIAdata
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;;; Byte out to the CRTC
|
||||||
|
;;;
|
||||||
|
;;; 1. Increment cursor position.
|
||||||
|
;;; 2. Scroll if necessary.
|
||||||
|
;;; 3. Store new cursor position in CRTC.
|
||||||
|
|
||||||
|
CRTCout:
|
||||||
|
JSR ACIAout ; Also echo to terminal for debugging.
|
||||||
|
|
||||||
|
;; In parallel, we're maintaining two states:
|
||||||
|
;; - The address of the cursor, and
|
||||||
|
;; - The Column/Row of the cursor.
|
||||||
|
;;
|
||||||
|
;; The latter state is used to handle scrolling and
|
||||||
|
;; for knowing how far to back up the cursor
|
||||||
|
;; for a carriage-return.
|
||||||
|
|
||||||
|
;; Backspace
|
||||||
|
CMP #$08
|
||||||
|
BEQ DO_BS
|
||||||
|
;; Line Feed
|
||||||
|
CMP #$0a
|
||||||
|
BEQ DO_LF
|
||||||
|
;; Carriage Return
|
||||||
|
CMP #$0d
|
||||||
|
BEQ DO_CR
|
||||||
|
;; Any other character...
|
||||||
|
STA COUTC ; Store the character going out
|
||||||
|
JSR COUT1 ; Output it where the cursor is.
|
||||||
|
;; We might need to scroll...
|
||||||
|
LDA C_ROW
|
||||||
|
CMP #N_ROWS-1
|
||||||
|
BNE @noscr
|
||||||
|
LDA C_COL
|
||||||
|
CMP #N_COLS-1
|
||||||
|
BNE @noscr
|
||||||
|
;; ... yup, we DO need to scroll.
|
||||||
|
JSR DO_SCROLL
|
||||||
|
RTS
|
||||||
|
@noscr: JSR INC_CADDR
|
||||||
|
INC C_COL
|
||||||
|
JSR SET_CURSOR
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Handle a back-space character.
|
||||||
|
DO_BS: LDA C_COL
|
||||||
|
BNE @l1 ; Skip next bit
|
||||||
|
LDA #N_COLS-1
|
||||||
|
STA C_COL
|
||||||
|
BNE @l2
|
||||||
|
@l1: DEC C_COL
|
||||||
|
@l2: JSR DEC_CADDR ; Back up
|
||||||
|
JSR SET_CURSOR
|
||||||
|
|
||||||
|
@l3: LDA #$20
|
||||||
|
STA COUTC ; Echo a space ($20)
|
||||||
|
JSR COUT1
|
||||||
|
RTS
|
||||||
|
|
||||||
|
DO_LF: RTS ; Just swallow LF. CR emulates it.
|
||||||
|
|
||||||
|
DO_CR: SEC
|
||||||
|
LDA CADDR_L ; 1. Carriage return to start of row.
|
||||||
|
SBC C_COL
|
||||||
|
STA CADDR_L
|
||||||
|
LDA CADDR_H
|
||||||
|
SBC #$00 ; Will decrement H if carry was left
|
||||||
|
; set.
|
||||||
|
STA CADDR_H
|
||||||
|
|
||||||
|
;; 1. Are we on the last row? Scroll.
|
||||||
|
LDA C_ROW
|
||||||
|
CMP #N_ROWS-1
|
||||||
|
BNE @inc
|
||||||
|
JSR DO_SCROLL
|
||||||
|
RTS
|
||||||
|
|
||||||
|
@inc: JSR INCROW
|
||||||
|
|
||||||
|
@lf: CLC
|
||||||
|
LDA CADDR_L
|
||||||
|
ADC #$28 ; Now add $28
|
||||||
|
STA CADDR_L
|
||||||
|
LDA CADDR_H
|
||||||
|
ADC #$00 ; Will increment if carry was set
|
||||||
|
STA CADDR_H
|
||||||
|
|
||||||
|
LDA #$00 ; Reset cursor row to 0
|
||||||
|
STA C_COL
|
||||||
|
JSR SET_CURSOR
|
||||||
|
RTS
|
||||||
|
|
||||||
|
SET_CURSOR:
|
||||||
|
LDA #14
|
||||||
|
STA CRTC_A
|
||||||
|
LDA CADDR_H
|
||||||
|
STA CRTC_C
|
||||||
|
LDA #15
|
||||||
|
STA CRTC_A
|
||||||
|
LDA CADDR_L
|
||||||
|
STA CRTC_C
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Increment C_ROW and put new
|
||||||
|
;; line start address in R_STRT,R_STRT+1
|
||||||
|
INCROW: TYA
|
||||||
|
PHA
|
||||||
|
INC C_ROW
|
||||||
|
BNE DECR2 ; Share code with DECROW
|
||||||
|
|
||||||
|
;; Decrement C_ROW and put new
|
||||||
|
;; line start address in R_STRT,R_STRT+1
|
||||||
|
DECROW: TYA
|
||||||
|
PHA
|
||||||
|
DEC C_ROW
|
||||||
|
DECR2: JSR SETRSTRT
|
||||||
|
PLA
|
||||||
|
TAY
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Send the cursor home to the start of the line
|
||||||
|
HOME:
|
||||||
|
LDY C_ROW
|
||||||
|
LDA CRA_LO,Y
|
||||||
|
STA CADDR_L
|
||||||
|
LDA CRA_HI,Y
|
||||||
|
STA CADDR_H
|
||||||
|
LDA #$00
|
||||||
|
STA C_COL
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Set the start address of the current row
|
||||||
|
SETRSTRT:
|
||||||
|
LDY C_ROW
|
||||||
|
LDA CRA_LO,Y
|
||||||
|
STA R_STRT
|
||||||
|
LDA CRA_HI,Y
|
||||||
|
STA R_STRT+1
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Clear the current video line
|
||||||
|
CLRLN: LDA #$20
|
||||||
|
LDY #$00
|
||||||
|
@l1: STA (R_STRT),Y
|
||||||
|
INY
|
||||||
|
CPY #N_COLS
|
||||||
|
BNE @l1
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Clear the video window and put the cursor at the home
|
||||||
|
;; position
|
||||||
|
CLRVID:
|
||||||
|
LDA #$20
|
||||||
|
LDX #N_ROWS-1
|
||||||
|
@l1: STX C_ROW
|
||||||
|
JSR SETRSTRT
|
||||||
|
JSR CLRLN
|
||||||
|
DEX
|
||||||
|
BPL @l1
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Handle a scroll request.
|
||||||
|
;;
|
||||||
|
;; The cursor could be anywhere on the line when this scroll
|
||||||
|
;; request comes in.
|
||||||
|
DO_SCROLL:
|
||||||
|
PHA
|
||||||
|
TYA
|
||||||
|
PHA
|
||||||
|
TXA
|
||||||
|
PHA
|
||||||
|
|
||||||
|
;; The idea here is to copy each line to the line above it
|
||||||
|
LDX #$00
|
||||||
|
|
||||||
|
@l1: LDA CRA_LO,X
|
||||||
|
STA R_STRT
|
||||||
|
LDA CRA_HI,X
|
||||||
|
STA R_STRT+1
|
||||||
|
|
||||||
|
INX
|
||||||
|
|
||||||
|
LDA CRA_LO,X
|
||||||
|
STA R_NXT
|
||||||
|
LDA CRA_HI,X
|
||||||
|
STA R_NXT+1
|
||||||
|
|
||||||
|
LDY #0
|
||||||
|
@l2: LDA (R_NXT),Y
|
||||||
|
STA (R_STRT),Y
|
||||||
|
INY
|
||||||
|
CPY #N_COLS
|
||||||
|
BNE @l2
|
||||||
|
|
||||||
|
CPX #N_ROWS
|
||||||
|
BNE @l1
|
||||||
|
|
||||||
|
;; Clear the line the cursor occupies.
|
||||||
|
JSR CLRLN
|
||||||
|
|
||||||
|
;; Move the cursor to the beginning of the line
|
||||||
|
JSR HOME
|
||||||
|
JSR SET_CURSOR
|
||||||
|
|
||||||
|
PLA
|
||||||
|
TAX
|
||||||
|
PLA
|
||||||
|
TAY ; Restore Y
|
||||||
|
PLA
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;; Decrement the cursor address
|
||||||
|
INC_CADDR:
|
||||||
|
INC CADDR_L
|
||||||
|
BNE @l1 ; Did we increment to 0?
|
||||||
|
INC CADDR_H ; Yes, also increment high
|
||||||
|
@l1: RTS
|
||||||
|
|
||||||
|
;; Increment the cursor address
|
||||||
|
DEC_CADDR:
|
||||||
|
CMP CADDR_L ; Is low alrady 0?
|
||||||
|
BNE @l1
|
||||||
|
DEC CADDR_H ; Yes, decrement high
|
||||||
|
@l1: DEC CADDR_L
|
||||||
|
RTS
|
||||||
|
|
||||||
|
COUT1:
|
||||||
|
TYA
|
||||||
|
PHA ; Save Y
|
||||||
|
LDY #$00
|
||||||
|
LDA COUTC
|
||||||
|
STA (CADDR_L),Y
|
||||||
|
PLA
|
||||||
|
TAY ; Restore Y
|
||||||
|
RTS
|
||||||
|
|
||||||
|
;;;
|
||||||
|
;;; byte in from ACIA. This subroutine will also force
|
||||||
|
;;; all lowercase letters to be uppercase, because EhBASIC
|
||||||
|
;;; only understands upper-case tokens
|
||||||
|
;;;
|
||||||
|
ACIAin:
|
||||||
|
LDA ACIAstatus ; Read 6551 status
|
||||||
|
AND #$08 ;
|
||||||
|
BEQ LAB_nobyw ; If rx buffer empty, no byte
|
||||||
|
|
||||||
|
LDA ACIAdata ; Read byte from 6551
|
||||||
|
CMP #'a' ; Is it < 'a'?
|
||||||
|
BCC @done ; Yes, we're done
|
||||||
|
CMP #'{' ; Is it >= '{'?
|
||||||
|
BCS @done ; Yes, we're done
|
||||||
|
AND #$5f ; Otherwise, mask to uppercase
|
||||||
|
@done:
|
||||||
|
SEC ; Flag byte received
|
||||||
|
RTS
|
||||||
|
|
||||||
|
LAB_nobyw:
|
||||||
|
CLC ; flag no byte received
|
||||||
|
no_load: ; empty load vector for EhBASIC
|
||||||
|
no_save: ; empty save vector for EhBASIC
|
||||||
|
RTS
|
||||||
|
|
||||||
|
; vector tables
|
||||||
|
|
||||||
|
LAB_vec:
|
||||||
|
.word ACIAin ; byte in from simulated ACIA
|
||||||
|
.word CRTCout ; byte out to simulated ACIA
|
||||||
|
.word no_load ; null load vector for EhBASIC
|
||||||
|
.word no_save ; null save vector for EhBASIC
|
||||||
|
|
||||||
|
; EhBASIC IRQ support
|
||||||
|
|
||||||
|
IRQ_CODE:
|
||||||
|
PHA ; save A
|
||||||
|
LDA IrqBase ; get the IRQ flag byte
|
||||||
|
LSR ; shift the set b7 to b6, and on down ...
|
||||||
|
ORA IrqBase ; OR the original back in
|
||||||
|
STA IrqBase ; save the new IRQ flag byte
|
||||||
|
PLA ; restore A
|
||||||
|
RTI
|
||||||
|
|
||||||
|
; EhBASIC NMI support
|
||||||
|
|
||||||
|
NMI_CODE:
|
||||||
|
PHA ; save A
|
||||||
|
LDA NmiBase ; get the NMI flag byte
|
||||||
|
LSR ; shift the set b7 to b6, and on down ...
|
||||||
|
ORA NmiBase ; OR the original back in
|
||||||
|
STA NmiBase ; save the new NMI flag byte
|
||||||
|
PLA ; restore A
|
||||||
|
RTI
|
||||||
|
|
||||||
|
END_CODE:
|
||||||
|
|
||||||
|
|
||||||
|
;;; CRTC row addresses: HIGH
|
||||||
|
CRA_HI:
|
||||||
|
.byte $70,$70,$70,$70,$70,$70,$70,$71
|
||||||
|
.byte $71,$71,$71,$71,$71,$72,$72,$72
|
||||||
|
.byte $72,$72,$72,$72,$73,$73,$73,$73
|
||||||
|
|
||||||
|
|
||||||
|
;;; CRTC row addresses: LOW
|
||||||
|
CRA_LO:
|
||||||
|
.byte $00,$28,$50,$78,$A0,$C8,$F0,$18
|
||||||
|
.byte $40,$68,$90,$B8,$E0,$08,$30,$58
|
||||||
|
.byte $80,$A8,$D0,$F8,$20,$48,$70,$98
|
||||||
|
|
||||||
|
; sign on string
|
||||||
|
LAB_mess:
|
||||||
|
.byte $0D,$0A,"SYMON (C) 2008-2014, SETH MORABITO"
|
||||||
|
.byte $0D,$0A,"ENHANCED 6502 BASIC 2.22 (C) LEE DAVISON"
|
||||||
|
.byte $0D,$0A,"[C]OLD/[W]ARM ?",$00
|
||||||
|
|
||||||
|
|
||||||
|
; system vectors
|
||||||
|
|
||||||
|
.segment "VECTORS"
|
||||||
|
.org $FFFA
|
||||||
|
|
||||||
|
.word NMI_vec ; NMI vector
|
||||||
|
.word RES_vec ; RESET vector
|
||||||
|
.word IRQ_vec ; IRQ vector
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
MEMORY {
|
||||||
|
RAM1: start = $0000, size = $8000 - __STACKSIZE__;
|
||||||
|
ROM1: start = $C000, size = $3C00, fill = yes;
|
||||||
|
MONITOR: start = $FC00, size = $3FA, fill = yes;
|
||||||
|
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEGMENTS {
|
||||||
|
CODE: load = ROM1, type = ro;
|
||||||
|
DATA: load = ROM1, type = ro;
|
||||||
|
MONITOR: load = MONITOR, type = ro;
|
||||||
|
VECTORS: load = ROMV, type = ro;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYMBOLS {
|
||||||
|
__STACKSIZE__: type = weak, value = $0300;
|
||||||
|
}
|
||||||
|
|
||||||
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 259 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 42 KiB |
@@ -0,0 +1,85 @@
|
|||||||
|
package com.loomcom.symon;
|
||||||
|
|
||||||
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
|
import com.loomcom.symon.util.Utils;
|
||||||
|
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
public class Breakpoints extends AbstractTableModel {
|
||||||
|
|
||||||
|
private TreeSet<Integer> breakpoints;
|
||||||
|
private Simulator simulator;
|
||||||
|
|
||||||
|
public Breakpoints(Simulator simulator) {
|
||||||
|
this.breakpoints = new TreeSet<>();
|
||||||
|
this.simulator = simulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean contains(int address) {
|
||||||
|
return this.breakpoints.contains(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addBreakpoint(int address) {
|
||||||
|
this.breakpoints .add(address);
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBreakpoint(int address) {
|
||||||
|
this.breakpoints.remove(address);
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeBreakpointAtIndex(int index) {
|
||||||
|
if (index < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<Integer> values = new ArrayList<>(breakpoints);
|
||||||
|
int value = values.get(index);
|
||||||
|
this.breakpoints.remove(value);
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return "Address";
|
||||||
|
} else {
|
||||||
|
return "Inst";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return breakpoints.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||||
|
ArrayList<Integer> values = new ArrayList<>(breakpoints);
|
||||||
|
|
||||||
|
if (columnIndex == 0) {
|
||||||
|
return "$" + Utils.wordToHex(values.get(rowIndex));
|
||||||
|
} else if (columnIndex == 1) {
|
||||||
|
int address = values.get(rowIndex);
|
||||||
|
try {
|
||||||
|
return simulator.disassembleOpAtAddress(address);
|
||||||
|
} catch (MemoryAccessException ex) {
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -26,6 +26,7 @@ package com.loomcom.symon;
|
|||||||
import com.loomcom.symon.devices.Device;
|
import com.loomcom.symon.devices.Device;
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -62,7 +63,7 @@ public class Bus {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Bus(int startAddress, int endAddress) {
|
public Bus(int startAddress, int endAddress) {
|
||||||
this.deviceMap = new HashMap<Integer, SortedSet<Device>>();
|
this.deviceMap = new HashMap<>();
|
||||||
this.startAddress = startAddress;
|
this.startAddress = startAddress;
|
||||||
this.endAddress = endAddress;
|
this.endAddress = endAddress;
|
||||||
}
|
}
|
||||||
@@ -92,24 +93,26 @@ public class Bus {
|
|||||||
/**
|
/**
|
||||||
* Add a device to the bus.
|
* Add a device to the bus.
|
||||||
*
|
*
|
||||||
* @param device
|
* @param device Device to add
|
||||||
* @param priority
|
* @param priority Bus prioirity.
|
||||||
* @throws MemoryRangeException
|
* @throws MemoryRangeException
|
||||||
*/
|
*/
|
||||||
public void addDevice(Device device, int priority) throws MemoryRangeException {
|
public void addDevice(Device device, int priority) throws MemoryRangeException {
|
||||||
|
|
||||||
MemoryRange range = device.getMemoryRange();
|
MemoryRange range = device.getMemoryRange();
|
||||||
|
|
||||||
if (range.startAddress() < this.startAddress || range.startAddress() > this.endAddress) {
|
if (range.startAddress() < this.startAddress || range.startAddress() > this.endAddress) {
|
||||||
throw new MemoryRangeException("start address of device " + device.getName() + " does not fall within the address range of the bus");
|
throw new MemoryRangeException("start address of device " + device.getName() + " does not fall within the address range of the bus");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (range.endAddress() < this.startAddress || range.endAddress() > this.endAddress) {
|
if (range.endAddress() < this.startAddress || range.endAddress() > this.endAddress) {
|
||||||
throw new MemoryRangeException("end address of device " + device.getName() + " does not fall within the address range of the bus");
|
throw new MemoryRangeException("end address of device " + device.getName() + " does not fall within the address range of the bus");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
||||||
|
|
||||||
if (deviceSet == null) {
|
if (deviceSet == null) {
|
||||||
deviceSet = new TreeSet<Device>();
|
deviceSet = new TreeSet<>();
|
||||||
deviceMap.put(priority, deviceSet);
|
deviceMap.put(priority, deviceSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +124,7 @@ public class Bus {
|
|||||||
/**
|
/**
|
||||||
* Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others.
|
* Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others.
|
||||||
*
|
*
|
||||||
* @param device
|
* @param device Device to add
|
||||||
* @throws MemoryRangeException
|
* @throws MemoryRangeException
|
||||||
*/
|
*/
|
||||||
public void addDevice(Device device) throws MemoryRangeException {
|
public void addDevice(Device device) throws MemoryRangeException {
|
||||||
@@ -132,7 +135,7 @@ public class Bus {
|
|||||||
/**
|
/**
|
||||||
* Remove a device from the bus.
|
* Remove a device from the bus.
|
||||||
*
|
*
|
||||||
* @param device
|
* @param device Device to remove
|
||||||
*/
|
*/
|
||||||
public void removeDevice(Device device) {
|
public void removeDevice(Device device) {
|
||||||
for (SortedSet<Device> deviceSet : deviceMap.values()) {
|
for (SortedSet<Device> deviceSet : deviceMap.values()) {
|
||||||
@@ -170,7 +173,7 @@ public class Bus {
|
|||||||
if (d != null) {
|
if (d != null) {
|
||||||
MemoryRange range = d.getMemoryRange();
|
MemoryRange range = d.getMemoryRange();
|
||||||
int devAddr = address - range.startAddress();
|
int devAddr = address - range.startAddress();
|
||||||
return d.read(devAddr);
|
return d.read(devAddr) & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address));
|
throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address));
|
||||||
@@ -214,9 +217,9 @@ public class Bus {
|
|||||||
|
|
||||||
public SortedSet<Device> getDevices() {
|
public SortedSet<Device> getDevices() {
|
||||||
// create an ordered set of devices, ordered by device priorities
|
// create an ordered set of devices, ordered by device priorities
|
||||||
SortedSet<Device> devices = new TreeSet<Device>();
|
SortedSet<Device> devices = new TreeSet<>();
|
||||||
|
|
||||||
List<Integer> priorities = new ArrayList<Integer>(deviceMap.keySet());
|
List<Integer> priorities = new ArrayList<>(deviceMap.keySet());
|
||||||
Collections.sort(priorities);
|
Collections.sort(priorities);
|
||||||
|
|
||||||
for (int priority : priorities) {
|
for (int priority : priorities) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2012 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -24,9 +24,10 @@
|
|||||||
package com.loomcom.symon;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
import com.loomcom.symon.util.HexUtil;
|
import com.loomcom.symon.util.Utils;
|
||||||
import java.util.logging.Level;
|
import org.slf4j.Logger;
|
||||||
import java.util.logging.Logger;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class provides a simulation of the MOS 6502 CPU's state machine.
|
* This class provides a simulation of the MOS 6502 CPU's state machine.
|
||||||
@@ -35,6 +36,8 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class Cpu implements InstructionTable {
|
public class Cpu implements InstructionTable {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(Cpu.class.getName());
|
||||||
|
|
||||||
/* Process status register mnemonics */
|
/* Process status register mnemonics */
|
||||||
public static final int P_CARRY = 0x01;
|
public static final int P_CARRY = 0x01;
|
||||||
public static final int P_ZERO = 0x02;
|
public static final int P_ZERO = 0x02;
|
||||||
@@ -55,24 +58,19 @@ public class Cpu implements InstructionTable {
|
|||||||
public static final int IRQ_VECTOR_L = 0xfffe;
|
public static final int IRQ_VECTOR_L = 0xfffe;
|
||||||
public static final int IRQ_VECTOR_H = 0xffff;
|
public static final int IRQ_VECTOR_H = 0xffff;
|
||||||
|
|
||||||
|
public static final long DEFAULT_CLOCK_PERIOD_IN_NS = 1000;
|
||||||
|
|
||||||
|
/* Simulated clock speed (default is 1MHz) */
|
||||||
|
private long clockPeriodInNs = DEFAULT_CLOCK_PERIOD_IN_NS;
|
||||||
|
|
||||||
/* Simulated behavior */
|
/* Simulated behavior */
|
||||||
private static CpuBehavior behavior;
|
private CpuBehavior behavior;
|
||||||
|
|
||||||
/* The Bus */
|
/* The Bus */
|
||||||
private Bus bus;
|
private Bus bus;
|
||||||
|
|
||||||
/* The CPU state */
|
/* The CPU state */
|
||||||
private static final CpuState state = new CpuState();
|
private final CpuState state = new CpuState();
|
||||||
|
|
||||||
/* Scratch space for addressing mode and effective address
|
|
||||||
* calculations */
|
|
||||||
private int irAddressMode; // Bits 3-5 of IR: [ | | |X|X|X| | ]
|
|
||||||
private int irOpMode; // Bits 6-7 of IR: [ | | | | | |X|X]
|
|
||||||
private int effectiveAddress;
|
|
||||||
|
|
||||||
/* Internal scratch space */
|
|
||||||
private int lo = 0, hi = 0; // Used in address calculation
|
|
||||||
private int tmp; // Temporary storage
|
|
||||||
|
|
||||||
/* start time of op execution, needed for speed simulation */
|
/* start time of op execution, needed for speed simulation */
|
||||||
private long opBeginTime;
|
private long opBeginTime;
|
||||||
@@ -81,7 +79,7 @@ public class Cpu implements InstructionTable {
|
|||||||
* Construct a new CPU.
|
* Construct a new CPU.
|
||||||
*/
|
*/
|
||||||
public Cpu() {
|
public Cpu() {
|
||||||
this(CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG);
|
this(CpuBehavior.NMOS_6502);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cpu(CpuBehavior behavior) {
|
public Cpu(CpuBehavior behavior) {
|
||||||
@@ -106,10 +104,6 @@ public class Cpu implements InstructionTable {
|
|||||||
this.behavior = behavior;
|
this.behavior = behavior;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CpuBehavior getBehavior() {
|
|
||||||
return behavior;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reset the CPU to known initial values.
|
* Reset the CPU to known initial values.
|
||||||
*/
|
*/
|
||||||
@@ -120,7 +114,7 @@ public class Cpu implements InstructionTable {
|
|||||||
state.sp = 0xff;
|
state.sp = 0xff;
|
||||||
|
|
||||||
// Set the PC to the address stored in the reset vector
|
// Set the PC to the address stored in the reset vector
|
||||||
state.pc = address(bus.read(RST_VECTOR_L), bus.read(RST_VECTOR_H));
|
state.pc = Utils.address(bus.read(RST_VECTOR_L), bus.read(RST_VECTOR_H));
|
||||||
|
|
||||||
// Clear instruction register.
|
// Clear instruction register.
|
||||||
state.ir = 0;
|
state.ir = 0;
|
||||||
@@ -146,6 +140,8 @@ public class Cpu implements InstructionTable {
|
|||||||
state.a = 0;
|
state.a = 0;
|
||||||
state.x = 0;
|
state.x = 0;
|
||||||
state.y = 0;
|
state.y = 0;
|
||||||
|
|
||||||
|
peekAhead();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void step(int num) throws MemoryAccessException {
|
public void step(int num) throws MemoryAccessException {
|
||||||
@@ -159,6 +155,7 @@ public class Cpu implements InstructionTable {
|
|||||||
*/
|
*/
|
||||||
public void step() throws MemoryAccessException {
|
public void step() throws MemoryAccessException {
|
||||||
opBeginTime = System.nanoTime();
|
opBeginTime = System.nanoTime();
|
||||||
|
|
||||||
// Store the address from which the IR was read, for debugging
|
// Store the address from which the IR was read, for debugging
|
||||||
state.lastPc = state.pc;
|
state.lastPc = state.pc;
|
||||||
|
|
||||||
@@ -172,8 +169,8 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
// Fetch memory location for this instruction.
|
// Fetch memory location for this instruction.
|
||||||
state.ir = bus.read(state.pc);
|
state.ir = bus.read(state.pc);
|
||||||
irAddressMode = (state.ir >> 2) & 0x07;
|
int irAddressMode = (state.ir >> 2) & 0x07; // Bits 3-5 of IR: [ | | |X|X|X| | ]
|
||||||
irOpMode = state.ir & 0x03;
|
int irOpMode = state.ir & 0x03; // Bits 6-7 of IR: [ | | | | | |X|X]
|
||||||
|
|
||||||
incrementPC();
|
incrementPC();
|
||||||
|
|
||||||
@@ -190,7 +187,8 @@ public class Cpu implements InstructionTable {
|
|||||||
state.stepCounter++;
|
state.stepCounter++;
|
||||||
|
|
||||||
// Get the data from the effective address (if any)
|
// Get the data from the effective address (if any)
|
||||||
effectiveAddress = 0;
|
int effectiveAddress = 0;
|
||||||
|
int tmp; // Temporary storage
|
||||||
|
|
||||||
switch (irOpMode) {
|
switch (irOpMode) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -204,7 +202,14 @@ public class Cpu implements InstructionTable {
|
|||||||
case 2: // Accumulator - ignored
|
case 2: // Accumulator - ignored
|
||||||
break;
|
break;
|
||||||
case 3: // Absolute
|
case 3: // Absolute
|
||||||
effectiveAddress = address(state.args[0], state.args[1]);
|
effectiveAddress = Utils.address(state.args[0], state.args[1]);
|
||||||
|
break;
|
||||||
|
case 4: // 65C02 (Zero Page)
|
||||||
|
if (behavior == CpuBehavior.CMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.CMOS_65816) {
|
||||||
|
effectiveAddress = Utils.address(bus.read(state.args[0]),
|
||||||
|
bus.read((state.args[0] + 1) & 0xff));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 5: // Zero Page,X / Zero Page,Y
|
case 5: // Zero Page,X / Zero Page,Y
|
||||||
if (state.ir == 0x96 || state.ir == 0xb6) {
|
if (state.ir == 0x96 || state.ir == 0xb6) {
|
||||||
@@ -226,7 +231,7 @@ public class Cpu implements InstructionTable {
|
|||||||
switch (irAddressMode) {
|
switch (irAddressMode) {
|
||||||
case 0: // (Zero Page,X)
|
case 0: // (Zero Page,X)
|
||||||
tmp = (state.args[0] + state.x) & 0xff;
|
tmp = (state.args[0] + state.x) & 0xff;
|
||||||
effectiveAddress = address(bus.read(tmp), bus.read(tmp + 1));
|
effectiveAddress = Utils.address(bus.read(tmp), bus.read(tmp + 1));
|
||||||
break;
|
break;
|
||||||
case 1: // Zero Page
|
case 1: // Zero Page
|
||||||
effectiveAddress = state.args[0];
|
effectiveAddress = state.args[0];
|
||||||
@@ -235,10 +240,10 @@ public class Cpu implements InstructionTable {
|
|||||||
effectiveAddress = -1;
|
effectiveAddress = -1;
|
||||||
break;
|
break;
|
||||||
case 3: // Absolute
|
case 3: // Absolute
|
||||||
effectiveAddress = address(state.args[0], state.args[1]);
|
effectiveAddress = Utils.address(state.args[0], state.args[1]);
|
||||||
break;
|
break;
|
||||||
case 4: // (Zero Page),Y
|
case 4: // (Zero Page),Y
|
||||||
tmp = address(bus.read(state.args[0]),
|
tmp = Utils.address(bus.read(state.args[0]),
|
||||||
bus.read((state.args[0] + 1) & 0xff));
|
bus.read((state.args[0] + 1) & 0xff));
|
||||||
effectiveAddress = (tmp + state.y) & 0xffff;
|
effectiveAddress = (tmp + state.y) & 0xffff;
|
||||||
break;
|
break;
|
||||||
@@ -260,9 +265,7 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
/** Single Byte Instructions; Implied and Relative **/
|
/** Single Byte Instructions; Implied and Relative **/
|
||||||
case 0x00: // BRK - Force Interrupt - Implied
|
case 0x00: // BRK - Force Interrupt - Implied
|
||||||
if (!getIrqDisableFlag()) {
|
handleBrk(state.pc + 1);
|
||||||
handleIrq(state.pc + 1);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case 0x08: // PHP - Push Processor Status - Implied
|
case 0x08: // PHP - Push Processor Status - Implied
|
||||||
// Break flag is always set in the stack value.
|
// Break flag is always set in the stack value.
|
||||||
@@ -279,7 +282,7 @@ public class Cpu implements InstructionTable {
|
|||||||
case 0x20: // JSR - Jump to Subroutine - Implied
|
case 0x20: // JSR - Jump to Subroutine - Implied
|
||||||
stackPush((state.pc - 1 >> 8) & 0xff); // PC high byte
|
stackPush((state.pc - 1 >> 8) & 0xff); // PC high byte
|
||||||
stackPush(state.pc - 1 & 0xff); // PC low byte
|
stackPush(state.pc - 1 & 0xff); // PC low byte
|
||||||
state.pc = address(state.args[0], state.args[1]);
|
state.pc = Utils.address(state.args[0], state.args[1]);
|
||||||
break;
|
break;
|
||||||
case 0x28: // PLP - Pull Processor Status - Implied
|
case 0x28: // PLP - Pull Processor Status - Implied
|
||||||
setProcessorStatus(stackPop());
|
setProcessorStatus(stackPop());
|
||||||
@@ -294,9 +297,9 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
case 0x40: // RTI - Return from Interrupt - Implied
|
case 0x40: // RTI - Return from Interrupt - Implied
|
||||||
setProcessorStatus(stackPop());
|
setProcessorStatus(stackPop());
|
||||||
lo = stackPop();
|
int lo = stackPop();
|
||||||
hi = stackPop();
|
int hi = stackPop();
|
||||||
setProgramCounter(address(lo, hi));
|
setProgramCounter(Utils.address(lo, hi));
|
||||||
break;
|
break;
|
||||||
case 0x48: // PHA - Push Accumulator - Implied
|
case 0x48: // PHA - Push Accumulator - Implied
|
||||||
stackPush(state.a);
|
stackPush(state.a);
|
||||||
@@ -312,7 +315,7 @@ public class Cpu implements InstructionTable {
|
|||||||
case 0x60: // RTS - Return from Subroutine - Implied
|
case 0x60: // RTS - Return from Subroutine - Implied
|
||||||
lo = stackPop();
|
lo = stackPop();
|
||||||
hi = stackPop();
|
hi = stackPop();
|
||||||
setProgramCounter((address(lo, hi) + 1) & 0xffff);
|
setProgramCounter((Utils.address(lo, hi) + 1) & 0xffff);
|
||||||
break;
|
break;
|
||||||
case 0x68: // PLA - Pull Accumulator - Implied
|
case 0x68: // PLA - Pull Accumulator - Implied
|
||||||
state.a = stackPop();
|
state.a = stackPop();
|
||||||
@@ -400,20 +403,21 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
/** JMP *****************************************************************/
|
/** JMP *****************************************************************/
|
||||||
case 0x4c: // JMP - Absolute
|
case 0x4c: // JMP - Absolute
|
||||||
state.pc = address(state.args[0], state.args[1]);
|
state.pc = Utils.address(state.args[0], state.args[1]);
|
||||||
break;
|
break;
|
||||||
|
// TODO: 65C02 instruction 0x7C, JMP (Indirect,X)
|
||||||
case 0x6c: // JMP - Indirect
|
case 0x6c: // JMP - Indirect
|
||||||
lo = address(state.args[0], state.args[1]); // Address of low byte
|
lo = Utils.address(state.args[0], state.args[1]); // Address of low byte
|
||||||
|
|
||||||
if (state.args[0] == 0xff &&
|
if (state.args[0] == 0xff &&
|
||||||
(behavior == CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG ||
|
(behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) {
|
||||||
hi = address(0x00, state.args[1]);
|
hi = Utils.address(0x00, state.args[1]);
|
||||||
} else {
|
} else {
|
||||||
hi = lo + 1;
|
hi = lo + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.pc = address(bus.read(lo), bus.read(hi));
|
state.pc = Utils.address(bus.read(lo), bus.read(hi));
|
||||||
/* TODO: For accuracy, allow a flag to enable broken behavior of early 6502s:
|
/* TODO: For accuracy, allow a flag to enable broken behavior of early 6502s:
|
||||||
*
|
*
|
||||||
* "An original 6502 has does not correctly fetch the target
|
* "An original 6502 has does not correctly fetch the target
|
||||||
@@ -426,13 +430,20 @@ public class Cpu implements InstructionTable {
|
|||||||
* (http://www.obelisk.demon.co.uk/6502/reference.html#JMP)
|
* (http://www.obelisk.demon.co.uk/6502/reference.html#JMP)
|
||||||
*/
|
*/
|
||||||
break;
|
break;
|
||||||
|
case 0x7C: // 65C02 JMP - Absolute,X
|
||||||
|
state.pc = (Utils.address(state.args[0], state.args[1]) + state.x) & 0xffff;
|
||||||
|
break;
|
||||||
|
|
||||||
/** ORA - Logical Inclusive Or ******************************************/
|
/** ORA - Logical Inclusive Or ******************************************/
|
||||||
case 0x09: // #Immediate
|
case 0x09: // #Immediate
|
||||||
state.a |= state.args[0];
|
state.a |= state.args[0];
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
break;
|
break;
|
||||||
|
case 0x12: // 65C02 ORA (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0x01: // (Zero Page,X)
|
case 0x01: // (Zero Page,X)
|
||||||
case 0x05: // Zero Page
|
case 0x05: // Zero Page
|
||||||
case 0x0d: // Absolute
|
case 0x0d: // Absolute
|
||||||
@@ -475,6 +486,11 @@ public class Cpu implements InstructionTable {
|
|||||||
state.a &= state.args[0];
|
state.a &= state.args[0];
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
break;
|
break;
|
||||||
|
case 0x32: // 65C02 AND (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0x21: // (Zero Page,X)
|
case 0x21: // (Zero Page,X)
|
||||||
case 0x25: // Zero Page
|
case 0x25: // Zero Page
|
||||||
case 0x2d: // Absolute
|
case 0x2d: // Absolute
|
||||||
@@ -507,6 +523,11 @@ public class Cpu implements InstructionTable {
|
|||||||
state.a ^= state.args[0];
|
state.a ^= state.args[0];
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
break;
|
break;
|
||||||
|
case 0x52: // 65C02 EOR (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0x41: // (Zero Page,X)
|
case 0x41: // (Zero Page,X)
|
||||||
case 0x45: // Zero Page
|
case 0x45: // Zero Page
|
||||||
case 0x4d: // Absolute
|
case 0x4d: // Absolute
|
||||||
@@ -542,6 +563,11 @@ public class Cpu implements InstructionTable {
|
|||||||
state.a = adc(state.a, state.args[0]);
|
state.a = adc(state.a, state.args[0]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 0x72: // 65C02 ADC (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0x61: // (Zero Page,X)
|
case 0x61: // (Zero Page,X)
|
||||||
case 0x65: // Zero Page
|
case 0x65: // Zero Page
|
||||||
case 0x6d: // Absolute
|
case 0x6d: // Absolute
|
||||||
@@ -573,6 +599,11 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
|
|
||||||
/** STA - Store Accumulator *********************************************/
|
/** STA - Store Accumulator *********************************************/
|
||||||
|
case 0x92: // 65C02 STA (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0x81: // (Zero Page,X)
|
case 0x81: // (Zero Page,X)
|
||||||
case 0x85: // Zero Page
|
case 0x85: // Zero Page
|
||||||
case 0x8d: // Absolute
|
case 0x8d: // Absolute
|
||||||
@@ -633,6 +664,11 @@ public class Cpu implements InstructionTable {
|
|||||||
state.a = state.args[0];
|
state.a = state.args[0];
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
break;
|
break;
|
||||||
|
case 0xb2: // 65C02 LDA (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0xa1: // (Zero Page,X)
|
case 0xa1: // (Zero Page,X)
|
||||||
case 0xa5: // Zero Page
|
case 0xa5: // Zero Page
|
||||||
case 0xad: // Absolute
|
case 0xad: // Absolute
|
||||||
@@ -659,6 +695,11 @@ public class Cpu implements InstructionTable {
|
|||||||
case 0xc9: // #Immediate
|
case 0xc9: // #Immediate
|
||||||
cmp(state.a, state.args[0]);
|
cmp(state.a, state.args[0]);
|
||||||
break;
|
break;
|
||||||
|
case 0xd2: // 65C02 CMP (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0xc1: // (Zero Page,X)
|
case 0xc1: // (Zero Page,X)
|
||||||
case 0xc5: // Zero Page
|
case 0xc5: // Zero Page
|
||||||
case 0xcd: // Absolute
|
case 0xcd: // Absolute
|
||||||
@@ -699,6 +740,11 @@ public class Cpu implements InstructionTable {
|
|||||||
state.a = sbc(state.a, state.args[0]);
|
state.a = sbc(state.a, state.args[0]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 0xf2: // 65C02 SBC (ZP)
|
||||||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
case 0xe1: // (Zero Page,X)
|
case 0xe1: // (Zero Page,X)
|
||||||
case 0xe5: // Zero Page
|
case 0xe5: // Zero Page
|
||||||
case 0xed: // Absolute
|
case 0xed: // Absolute
|
||||||
@@ -732,15 +778,32 @@ public class Cpu implements InstructionTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
delayLoop(state.ir);
|
delayLoop(state.ir);
|
||||||
|
|
||||||
|
// Peek ahead to the next insturction and arguments
|
||||||
|
peekAhead();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void peekAhead() throws MemoryAccessException {
|
||||||
|
state.nextIr = bus.read(state.pc);
|
||||||
|
int nextInstSize = Cpu.instructionSizes[state.nextIr];
|
||||||
|
for (int i = 1; i < nextInstSize; i++) {
|
||||||
|
int nextRead = (state.pc + i) % bus.endAddress();
|
||||||
|
state.nextArgs[i-1] = bus.read(nextRead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleBrk(int returnPc) throws MemoryAccessException {
|
||||||
|
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H, true);
|
||||||
|
clearIrq();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleIrq(int returnPc) throws MemoryAccessException {
|
private void handleIrq(int returnPc) throws MemoryAccessException {
|
||||||
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H);
|
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H, false);
|
||||||
clearIrq();
|
clearIrq();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleNmi() throws MemoryAccessException {
|
private void handleNmi() throws MemoryAccessException {
|
||||||
handleInterrupt(state.pc, NMI_VECTOR_L, NMI_VECTOR_H);
|
handleInterrupt(state.pc, NMI_VECTOR_L, NMI_VECTOR_H, false);
|
||||||
clearNmi();
|
clearNmi();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -749,9 +812,19 @@ public class Cpu implements InstructionTable {
|
|||||||
*
|
*
|
||||||
* @throws MemoryAccessException
|
* @throws MemoryAccessException
|
||||||
*/
|
*/
|
||||||
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh) throws MemoryAccessException {
|
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh, boolean isBreak) throws MemoryAccessException {
|
||||||
|
|
||||||
|
if (isBreak)
|
||||||
|
{
|
||||||
// Set the break flag before pushing.
|
// Set the break flag before pushing.
|
||||||
setBreakFlag();
|
setBreakFlag();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// IRQ & NMI clear break flag
|
||||||
|
clearBreakFlag();
|
||||||
|
}
|
||||||
|
|
||||||
// Push program counter + 1 onto the stack
|
// Push program counter + 1 onto the stack
|
||||||
stackPush((returnPc >> 8) & 0xff); // PC high byte
|
stackPush((returnPc >> 8) & 0xff); // PC high byte
|
||||||
stackPush(returnPc & 0xff); // PC low byte
|
stackPush(returnPc & 0xff); // PC low byte
|
||||||
@@ -760,7 +833,7 @@ public class Cpu implements InstructionTable {
|
|||||||
setIrqDisableFlag();
|
setIrqDisableFlag();
|
||||||
|
|
||||||
// Load interrupt vector address into PC
|
// Load interrupt vector address into PC
|
||||||
state.pc = address(bus.read(vectorLow), bus.read(vectorHigh));
|
state.pc = Utils.address(bus.read(vectorLow), bus.read(vectorHigh));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -770,7 +843,7 @@ public class Cpu implements InstructionTable {
|
|||||||
*
|
*
|
||||||
* @param acc The current value of the accumulator
|
* @param acc The current value of the accumulator
|
||||||
* @param operand The operand
|
* @param operand The operand
|
||||||
* @return
|
* @return The sum of the accumulator and the operand
|
||||||
*/
|
*/
|
||||||
private int adc(int acc, int operand) {
|
private int adc(int acc, int operand) {
|
||||||
int result = (operand & 0xff) + (acc & 0xff) + getCarryBit();
|
int result = (operand & 0xff) + (acc & 0xff) + getCarryBit();
|
||||||
@@ -893,6 +966,14 @@ public class Cpu implements InstructionTable {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param clockPeriodInNs The simulated clock period, in nanoseconds
|
||||||
|
*/
|
||||||
|
public void setClockPeriodInNs(long clockPeriodInNs) {
|
||||||
|
logger.debug("Setting simulated clock period to {} ns.", clockPeriodInNs);
|
||||||
|
this.clockPeriodInNs = clockPeriodInNs;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current Cpu State.
|
* Return the current Cpu State.
|
||||||
*
|
*
|
||||||
@@ -1117,6 +1198,14 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
public void setProgramCounter(int addr) {
|
public void setProgramCounter(int addr) {
|
||||||
state.pc = addr;
|
state.pc = addr;
|
||||||
|
|
||||||
|
// As a side-effect of setting the program counter,
|
||||||
|
// we want to peek ahead at the next state.
|
||||||
|
try {
|
||||||
|
peekAhead();
|
||||||
|
} catch (MemoryAccessException ex) {
|
||||||
|
logger.error("Could not peek ahead at next instruction state.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getStackPointer() {
|
public int getStackPointer() {
|
||||||
@@ -1172,23 +1261,23 @@ public class Cpu implements InstructionTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getAccumulatorStatus() {
|
public String getAccumulatorStatus() {
|
||||||
return "$" + HexUtil.byteToHex(state.a);
|
return "$" + Utils.byteToHex(state.a);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getXRegisterStatus() {
|
public String getXRegisterStatus() {
|
||||||
return "$" + HexUtil.byteToHex(state.x);
|
return "$" + Utils.byteToHex(state.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getYRegisterStatus() {
|
public String getYRegisterStatus() {
|
||||||
return "$" + HexUtil.byteToHex(state.y);
|
return "$" + Utils.byteToHex(state.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getProgramCounterStatus() {
|
public String getProgramCounterStatus() {
|
||||||
return "$" + HexUtil.wordToHex(state.pc);
|
return "$" + Utils.wordToHex(state.pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStackPointerStatus() {
|
public String getStackPointerStatus() {
|
||||||
return "$" + HexUtil.byteToHex(state.sp);
|
return "$" + Utils.byteToHex(state.sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getProcessorStatus() {
|
public int getProcessorStatus() {
|
||||||
@@ -1272,19 +1361,12 @@ public class Cpu implements InstructionTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given two bytes, return an address.
|
|
||||||
*/
|
|
||||||
int address(int lowByte, int hiByte) {
|
|
||||||
return ((hiByte << 8) | lowByte) & 0xffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a hi byte and a low byte, return the Absolute,X
|
* Given a hi byte and a low byte, return the Absolute,X
|
||||||
* offset address.
|
* offset address.
|
||||||
*/
|
*/
|
||||||
int xAddress(int lowByte, int hiByte) {
|
int xAddress(int lowByte, int hiByte) {
|
||||||
return (address(lowByte, hiByte) + state.x) & 0xffff;
|
return (Utils.address(lowByte, hiByte) + state.x) & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1292,7 +1374,7 @@ public class Cpu implements InstructionTable {
|
|||||||
* offset address.
|
* offset address.
|
||||||
*/
|
*/
|
||||||
int yAddress(int lowByte, int hiByte) {
|
int yAddress(int lowByte, int hiByte) {
|
||||||
return (address(lowByte, hiByte) + state.y) & 0xffff;
|
return (Utils.address(lowByte, hiByte) + state.y) & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1302,6 +1384,13 @@ public class Cpu implements InstructionTable {
|
|||||||
return (zp + state.x) & 0xff;
|
return (zp + state.x) & 0xff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a single byte, compute the Zero Page,Y offset address.
|
||||||
|
*/
|
||||||
|
int zpyAddress(int zp) {
|
||||||
|
return (zp + state.y) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a single byte, compute the offset address.
|
* Given a single byte, compute the offset address.
|
||||||
*/
|
*/
|
||||||
@@ -1310,187 +1399,40 @@ public class Cpu implements InstructionTable {
|
|||||||
return (state.pc + (byte) offset) & 0xffff;
|
return (state.pc + (byte) offset) & 0xffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a single byte, compute the Zero Page,Y offset address.
|
|
||||||
*/
|
|
||||||
int zpyAddress(int zp) {
|
|
||||||
return (zp + state.y) & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Perform a busy-loop for CLOCK_IN_NS nanoseconds
|
* Perform a busy-loop until the instruction should complete on the wall clock
|
||||||
*/
|
*/
|
||||||
private void delayLoop(int opcode) {
|
private void delayLoop(int opcode) {
|
||||||
int clockSteps = Cpu.instructionClocks[0xff & opcode];
|
final int clockSteps;
|
||||||
// Just a precaution. This could be better.
|
|
||||||
|
if (behavior == CpuBehavior.NMOS_WITH_ROR_BUG ||
|
||||||
|
behavior == CpuBehavior.NMOS_6502) {
|
||||||
|
clockSteps = Cpu.instructionClocksNmos[0xff & opcode];
|
||||||
|
} else {
|
||||||
|
clockSteps = Cpu.instructionClocksCmos[0xff & opcode];
|
||||||
|
}
|
||||||
|
|
||||||
if (clockSteps == 0) {
|
if (clockSteps == 0) {
|
||||||
clockSteps = 1;
|
logger.warn("Opcode {} has clock step of 0!", String.format("0x%02x", opcode));
|
||||||
}
|
return;
|
||||||
long opScheduledEnd = opBeginTime + clockSteps;
|
|
||||||
long now = System.nanoTime();
|
|
||||||
while(now < opScheduledEnd) {
|
|
||||||
now = System.nanoTime();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long interval = clockSteps * clockPeriodInNs;
|
||||||
|
long end;
|
||||||
|
|
||||||
/**
|
do {
|
||||||
* A compact, struct-like representation of CPU state.
|
end = System.nanoTime();
|
||||||
*/
|
} while (opBeginTime + interval >= end);
|
||||||
public static class CpuState {
|
|
||||||
/**
|
|
||||||
* Accumulator
|
|
||||||
*/
|
|
||||||
public int a;
|
|
||||||
/**
|
|
||||||
* X index regsiter
|
|
||||||
*/
|
|
||||||
public int x;
|
|
||||||
/**
|
|
||||||
* Y index register
|
|
||||||
*/
|
|
||||||
public int y;
|
|
||||||
/**
|
|
||||||
* Stack Pointer
|
|
||||||
*/
|
|
||||||
public int sp;
|
|
||||||
/**
|
|
||||||
* Program Counter
|
|
||||||
*/
|
|
||||||
public int pc;
|
|
||||||
/**
|
|
||||||
* Instruction Register
|
|
||||||
*/
|
|
||||||
public int ir;
|
|
||||||
public int lastPc;
|
|
||||||
public int[] args = new int[2];
|
|
||||||
public int instSize;
|
|
||||||
public boolean opTrap;
|
|
||||||
public boolean irqAsserted;
|
|
||||||
public boolean nmiAsserted;
|
|
||||||
|
|
||||||
/* Status Flag Register bits */
|
|
||||||
public boolean carryFlag;
|
|
||||||
public boolean negativeFlag;
|
|
||||||
public boolean zeroFlag;
|
|
||||||
public boolean irqDisableFlag;
|
|
||||||
public boolean decimalModeFlag;
|
|
||||||
public boolean breakFlag;
|
|
||||||
public boolean overflowFlag;
|
|
||||||
public long stepCounter = 0L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an empty CPU State.
|
|
||||||
*/
|
|
||||||
public CpuState() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Snapshot a copy of the CpuState.
|
|
||||||
*
|
|
||||||
* (This is a copy constructor rather than an implementation of <code>Clonable</code>
|
|
||||||
* based on Josh Bloch's recommendation)
|
|
||||||
*
|
|
||||||
* @param s The CpuState to copy.
|
|
||||||
*/
|
|
||||||
public CpuState(CpuState s) {
|
|
||||||
this.a = s.a;
|
|
||||||
this.x = s.x;
|
|
||||||
this.y = s.y;
|
|
||||||
this.sp = s.sp;
|
|
||||||
this.pc = s.pc;
|
|
||||||
this.ir = s.ir;
|
|
||||||
this.lastPc = s.lastPc;
|
|
||||||
this.args[0] = s.args[0];
|
|
||||||
this.args[1] = s.args[1];
|
|
||||||
this.instSize = s.instSize;
|
|
||||||
this.opTrap = s.opTrap;
|
|
||||||
this.irqAsserted = s.irqAsserted;
|
|
||||||
this.carryFlag = s.carryFlag;
|
|
||||||
this.negativeFlag = s.negativeFlag;
|
|
||||||
this.zeroFlag = s.zeroFlag;
|
|
||||||
this.irqDisableFlag = s.irqDisableFlag;
|
|
||||||
this.decimalModeFlag = s.decimalModeFlag;
|
|
||||||
this.breakFlag = s.breakFlag;
|
|
||||||
this.overflowFlag = s.overflowFlag;
|
|
||||||
this.stepCounter = s.stepCounter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a string formatted for the trace log.
|
* Return a formatted string representing the last instruction and
|
||||||
*
|
* operands that were executed.
|
||||||
* @return a string formatted for the trace log.
|
|
||||||
*/
|
|
||||||
public String toTraceEvent() {
|
|
||||||
String opcode = disassembleOp();
|
|
||||||
StringBuilder sb = new StringBuilder(getInstructionByteStatus());
|
|
||||||
sb.append(" ");
|
|
||||||
sb.append(String.format("%-14s", opcode));
|
|
||||||
sb.append("A:" + HexUtil.byteToHex(a) + " ");
|
|
||||||
sb.append("X:" + HexUtil.byteToHex(x) + " ");
|
|
||||||
sb.append("Y:" + HexUtil.byteToHex(y) + " ");
|
|
||||||
sb.append("F:" + HexUtil.byteToHex(getStatusFlag()) + " ");
|
|
||||||
sb.append("S:1" + HexUtil.byteToHex(sp) + " ");
|
|
||||||
sb.append(getProcessorStatusString() + "\n");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns The value of the Process Status Register, as a byte.
|
|
||||||
*/
|
|
||||||
public int getStatusFlag() {
|
|
||||||
int status = 0x20;
|
|
||||||
if (carryFlag) {
|
|
||||||
status |= P_CARRY;
|
|
||||||
}
|
|
||||||
if (zeroFlag) {
|
|
||||||
status |= P_ZERO;
|
|
||||||
}
|
|
||||||
if (irqDisableFlag) {
|
|
||||||
status |= P_IRQ_DISABLE;
|
|
||||||
}
|
|
||||||
if (decimalModeFlag) {
|
|
||||||
status |= P_DECIMAL;
|
|
||||||
}
|
|
||||||
if (breakFlag) {
|
|
||||||
status |= P_BREAK;
|
|
||||||
}
|
|
||||||
if (overflowFlag) {
|
|
||||||
status |= P_OVERFLOW;
|
|
||||||
}
|
|
||||||
if (negativeFlag) {
|
|
||||||
status |= P_NEGATIVE;
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getInstructionByteStatus() {
|
|
||||||
switch (Cpu.instructionSizes[ir]) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
return HexUtil.wordToHex(lastPc) + " " +
|
|
||||||
HexUtil.byteToHex(ir) + " ";
|
|
||||||
case 2:
|
|
||||||
return HexUtil.wordToHex(lastPc) + " " +
|
|
||||||
HexUtil.byteToHex(ir) + " " +
|
|
||||||
HexUtil.byteToHex(args[0]) + " ";
|
|
||||||
case 3:
|
|
||||||
return HexUtil.wordToHex(lastPc) + " " +
|
|
||||||
HexUtil.byteToHex(ir) + " " +
|
|
||||||
HexUtil.byteToHex(args[0]) + " " +
|
|
||||||
HexUtil.byteToHex(args[1]);
|
|
||||||
default:
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an opcode and its operands, return a formatted name.
|
|
||||||
*
|
*
|
||||||
* @return A string representing the mnemonic and operands of the instruction
|
* @return A string representing the mnemonic and operands of the instruction
|
||||||
*/
|
*/
|
||||||
public String disassembleOp() {
|
public static String disassembleOp(int opCode, int[] args) {
|
||||||
String mnemonic = opcodeNames[ir];
|
String mnemonic = opcodeNames[opCode];
|
||||||
|
|
||||||
if (mnemonic == null) {
|
if (mnemonic == null) {
|
||||||
return "???";
|
return "???";
|
||||||
@@ -1498,37 +1440,37 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
StringBuilder sb = new StringBuilder(mnemonic);
|
StringBuilder sb = new StringBuilder(mnemonic);
|
||||||
|
|
||||||
switch (instructionModes[ir]) {
|
switch (instructionModes[opCode]) {
|
||||||
case ABS:
|
case ABS:
|
||||||
sb.append(" $" + HexUtil.wordToHex(address(args[0], args[1])));
|
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1])));
|
||||||
break;
|
break;
|
||||||
case ABX:
|
case ABX:
|
||||||
sb.append(" $" + HexUtil.wordToHex(address(args[0], args[1])) + ",X");
|
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X");
|
||||||
break;
|
break;
|
||||||
case ABY:
|
case ABY:
|
||||||
sb.append(" $" + HexUtil.wordToHex(address(args[0], args[1])) + ",Y");
|
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",Y");
|
||||||
break;
|
break;
|
||||||
case IMM:
|
case IMM:
|
||||||
sb.append(" #$" + HexUtil.byteToHex(args[0]));
|
sb.append(" #$").append(Utils.byteToHex(args[0]));
|
||||||
break;
|
break;
|
||||||
case IND:
|
case IND:
|
||||||
sb.append(" ($" + HexUtil.wordToHex(address(args[0], args[1])) + ")");
|
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(")");
|
||||||
break;
|
break;
|
||||||
case XIN:
|
case XIN:
|
||||||
sb.append(" ($" + HexUtil.byteToHex(args[0]) + ",X)");
|
sb.append(" ($").append(Utils.byteToHex(args[0])).append(",X)");
|
||||||
break;
|
break;
|
||||||
case INY:
|
case INY:
|
||||||
sb.append(" ($" + HexUtil.byteToHex(args[0]) + "),Y");
|
sb.append(" ($").append(Utils.byteToHex(args[0])).append("),Y");
|
||||||
break;
|
break;
|
||||||
case REL:
|
case REL:
|
||||||
case ZPG:
|
case ZPG:
|
||||||
sb.append(" $" + HexUtil.byteToHex(args[0]));
|
sb.append(" $").append(Utils.byteToHex(args[0]));
|
||||||
break;
|
break;
|
||||||
case ZPX:
|
case ZPX:
|
||||||
sb.append(" $" + HexUtil.byteToHex(args[0]) + ",X");
|
sb.append(" $").append(Utils.byteToHex(args[0])).append(",X");
|
||||||
break;
|
break;
|
||||||
case ZPY:
|
case ZPY:
|
||||||
sb.append(" $" + HexUtil.byteToHex(args[0]) + ",Y");
|
sb.append(" $").append(Utils.byteToHex(args[0])).append(",Y");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1536,28 +1478,28 @@ public class Cpu implements InstructionTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given two bytes, return an address.
|
* Return a formatted string representing the next instruction and
|
||||||
|
* operands to be executed.
|
||||||
|
*
|
||||||
|
* @return A string representing the mnemonic and operands of the instruction
|
||||||
*/
|
*/
|
||||||
private int address(int lowByte, int hiByte) {
|
public String disassembleNextOp() {
|
||||||
return ((hiByte << 8) | lowByte) & 0xffff;
|
return Cpu.disassembleOp(state.nextIr, state.nextArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return A string representing the current status register state.
|
* @param address Address to disassemble
|
||||||
|
* @return String containing the disassembled instruction and operands.
|
||||||
*/
|
*/
|
||||||
public String getProcessorStatusString() {
|
public String disassembleOpAtAddress(int address) throws MemoryAccessException {
|
||||||
StringBuilder sb = new StringBuilder("[");
|
int opCode = bus.read(address);
|
||||||
sb.append(negativeFlag ? 'N' : '.'); // Bit 7
|
int args[] = new int[2];
|
||||||
sb.append(overflowFlag ? 'V' : '.'); // Bit 6
|
int size = Cpu.instructionSizes[opCode];
|
||||||
sb.append("-"); // Bit 5 (always 1)
|
for (int i = 1; i < size; i++) {
|
||||||
sb.append(breakFlag ? 'B' : '.'); // Bit 4
|
int nextRead = (address + i) % bus.endAddress();
|
||||||
sb.append(decimalModeFlag ? 'D' : '.'); // Bit 3
|
args[i-1] = bus.read(nextRead);
|
||||||
sb.append(irqDisableFlag ? 'I' : '.'); // Bit 2
|
|
||||||
sb.append(zeroFlag ? 'Z' : '.'); // Bit 1
|
|
||||||
sb.append(carryFlag ? 'C' : '.'); // Bit 0
|
|
||||||
sb.append("]");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return disassembleOp(opCode, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
package com.loomcom.symon;
|
||||||
|
|
||||||
|
import com.loomcom.symon.util.Utils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A compact, struct-like representation of CPU state.
|
||||||
|
*/
|
||||||
|
public class CpuState {
|
||||||
|
/**
|
||||||
|
* Accumulator
|
||||||
|
*/
|
||||||
|
public int a;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* X index regsiter
|
||||||
|
*/
|
||||||
|
public int x;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Y index register
|
||||||
|
*/
|
||||||
|
public int y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stack Pointer
|
||||||
|
*/
|
||||||
|
public int sp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Program Counter
|
||||||
|
*/
|
||||||
|
public int pc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last Loaded Instruction Register
|
||||||
|
*/
|
||||||
|
public int ir;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Peek-Ahead to next IR
|
||||||
|
*/
|
||||||
|
public int nextIr;
|
||||||
|
public int[] args = new int[2];
|
||||||
|
public int[] nextArgs = new int[2];
|
||||||
|
public int instSize;
|
||||||
|
public boolean opTrap;
|
||||||
|
public boolean irqAsserted;
|
||||||
|
public boolean nmiAsserted;
|
||||||
|
public int lastPc;
|
||||||
|
|
||||||
|
/* Status Flag Register bits */
|
||||||
|
public boolean carryFlag;
|
||||||
|
public boolean negativeFlag;
|
||||||
|
public boolean zeroFlag;
|
||||||
|
public boolean irqDisableFlag;
|
||||||
|
public boolean decimalModeFlag;
|
||||||
|
public boolean breakFlag;
|
||||||
|
public boolean overflowFlag;
|
||||||
|
public long stepCounter = 0L;
|
||||||
|
|
||||||
|
public CpuState() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snapshot a copy of the CpuState.
|
||||||
|
*
|
||||||
|
* (This is a copy constructor rather than an implementation of <code>Cloneable</code>
|
||||||
|
* based on Josh Bloch's recommendation)
|
||||||
|
*
|
||||||
|
* @param s The CpuState to copy.
|
||||||
|
*/
|
||||||
|
public CpuState(CpuState s) {
|
||||||
|
this.a = s.a;
|
||||||
|
this.x = s.x;
|
||||||
|
this.y = s.y;
|
||||||
|
this.sp = s.sp;
|
||||||
|
this.pc = s.pc;
|
||||||
|
this.ir = s.ir;
|
||||||
|
this.nextIr = s.nextIr;
|
||||||
|
this.lastPc = s.lastPc;
|
||||||
|
this.args[0] = s.args[0];
|
||||||
|
this.args[1] = s.args[1];
|
||||||
|
this.nextArgs[0] = s.nextArgs[0];
|
||||||
|
this.nextArgs[1] = s.nextArgs[1];
|
||||||
|
this.instSize = s.instSize;
|
||||||
|
this.opTrap = s.opTrap;
|
||||||
|
this.irqAsserted = s.irqAsserted;
|
||||||
|
this.carryFlag = s.carryFlag;
|
||||||
|
this.negativeFlag = s.negativeFlag;
|
||||||
|
this.zeroFlag = s.zeroFlag;
|
||||||
|
this.irqDisableFlag = s.irqDisableFlag;
|
||||||
|
this.decimalModeFlag = s.decimalModeFlag;
|
||||||
|
this.breakFlag = s.breakFlag;
|
||||||
|
this.overflowFlag = s.overflowFlag;
|
||||||
|
this.stepCounter = s.stepCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a string formatted for the trace log.
|
||||||
|
*
|
||||||
|
* @return a string formatted for the trace log.
|
||||||
|
*/
|
||||||
|
public String toTraceEvent() {
|
||||||
|
String opcode = Cpu.disassembleOp(ir, args);
|
||||||
|
return getInstructionByteStatus() + " " +
|
||||||
|
String.format("%-14s", opcode) +
|
||||||
|
"A:" + Utils.byteToHex(a) + " " +
|
||||||
|
"X:" + Utils.byteToHex(x) + " " +
|
||||||
|
"Y:" + Utils.byteToHex(y) + " " +
|
||||||
|
"F:" + Utils.byteToHex(getStatusFlag()) + " " +
|
||||||
|
"S:1" + Utils.byteToHex(sp) + " " +
|
||||||
|
getProcessorStatusString() + "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The value of the Process Status Register, as a byte.
|
||||||
|
*/
|
||||||
|
public int getStatusFlag() {
|
||||||
|
int status = 0x20;
|
||||||
|
if (carryFlag) {
|
||||||
|
status |= Cpu.P_CARRY;
|
||||||
|
}
|
||||||
|
if (zeroFlag) {
|
||||||
|
status |= Cpu.P_ZERO;
|
||||||
|
}
|
||||||
|
if (irqDisableFlag) {
|
||||||
|
status |= Cpu.P_IRQ_DISABLE;
|
||||||
|
}
|
||||||
|
if (decimalModeFlag) {
|
||||||
|
status |= Cpu.P_DECIMAL;
|
||||||
|
}
|
||||||
|
if (breakFlag) {
|
||||||
|
status |= Cpu.P_BREAK;
|
||||||
|
}
|
||||||
|
if (overflowFlag) {
|
||||||
|
status |= Cpu.P_OVERFLOW;
|
||||||
|
}
|
||||||
|
if (negativeFlag) {
|
||||||
|
status |= Cpu.P_NEGATIVE;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getInstructionByteStatus() {
|
||||||
|
switch (Cpu.instructionSizes[ir]) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
return Utils.wordToHex(lastPc) + " " +
|
||||||
|
Utils.byteToHex(ir) + " ";
|
||||||
|
case 2:
|
||||||
|
return Utils.wordToHex(lastPc) + " " +
|
||||||
|
Utils.byteToHex(ir) + " " +
|
||||||
|
Utils.byteToHex(args[0]) + " ";
|
||||||
|
case 3:
|
||||||
|
return Utils.wordToHex(lastPc) + " " +
|
||||||
|
Utils.byteToHex(ir) + " " +
|
||||||
|
Utils.byteToHex(args[0]) + " " +
|
||||||
|
Utils.byteToHex(args[1]);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A string representing the current status register state.
|
||||||
|
*/
|
||||||
|
public String getProcessorStatusString() {
|
||||||
|
return "[" + (negativeFlag ? 'N' : '.') +
|
||||||
|
(overflowFlag ? 'V' : '.') +
|
||||||
|
"-" +
|
||||||
|
(breakFlag ? 'B' : '.') +
|
||||||
|
(decimalModeFlag ? 'D' : '.') +
|
||||||
|
(irqDisableFlag ? 'I' : '.') +
|
||||||
|
(zeroFlag ? 'Z' : '.') +
|
||||||
|
(carryFlag ? 'C' : '.') +
|
||||||
|
"]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2012 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -31,7 +31,7 @@ public interface InstructionTable {
|
|||||||
*
|
*
|
||||||
* TODO: As of version 0.6, this is still not used! All CPUs are "idealized" NMOS 6502 only.
|
* TODO: As of version 0.6, this is still not used! All CPUs are "idealized" NMOS 6502 only.
|
||||||
*/
|
*/
|
||||||
public enum CpuBehavior {
|
enum CpuBehavior {
|
||||||
/**
|
/**
|
||||||
* The earliest NMOS 6502 includes a bug that causes the ROR instruction
|
* The earliest NMOS 6502 includes a bug that causes the ROR instruction
|
||||||
* to behave like an ASL that does not affect the carry bit. This version
|
* to behave like an ASL that does not affect the carry bit. This version
|
||||||
@@ -46,26 +46,23 @@ public interface InstructionTable {
|
|||||||
*
|
*
|
||||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
||||||
*/
|
*/
|
||||||
NMOS_WITH_INDIRECT_JMP_BUG,
|
NMOS_6502,
|
||||||
|
|
||||||
/**
|
|
||||||
* Emulate an NMOS 6502 without the indirect JMP bug. This type of 6502
|
|
||||||
* does not actually exist in the wild.
|
|
||||||
*
|
|
||||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
|
||||||
*/
|
|
||||||
NMOS_WITHOUT_INDIRECT_JMP_BUG,
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emulate a CMOS 65C02, with all CMOS instructions and addressing modes.
|
* Emulate a CMOS 65C02, with all CMOS instructions and addressing modes.
|
||||||
*/
|
*/
|
||||||
CMOS
|
CMOS_6502,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulate a CMOS 65C816.
|
||||||
|
*/
|
||||||
|
CMOS_65816
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration of Addressing Modes.
|
* Enumeration of Addressing Modes.
|
||||||
*/
|
*/
|
||||||
public enum Mode {
|
enum Mode {
|
||||||
ACC {
|
ACC {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Accumulator";
|
return "Accumulator";
|
||||||
@@ -128,19 +125,25 @@ public interface InstructionTable {
|
|||||||
|
|
||||||
ZPG {
|
ZPG {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Zeropage";
|
return "Zero Page";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ZPX {
|
ZPX {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Zeropage, X-indexed";
|
return "Zero Page, X-indexed";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ZPY {
|
ZPY {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Zeropage, Y-indexed";
|
return "Zero Page, Y-indexed";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
ZPI {
|
||||||
|
public String toString() {
|
||||||
|
return "Zero Page Indirect";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -154,52 +157,55 @@ public interface InstructionTable {
|
|||||||
// 6502 opcodes. No 65C02 opcodes implemented.
|
// 6502 opcodes. No 65C02 opcodes implemented.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instruction opcode names.
|
* Instruction opcode names. This lists all opcodes for
|
||||||
|
* NMOS 6502, CMOS 65C02, and CMOS 65C816
|
||||||
*/
|
*/
|
||||||
public static final String[] opcodeNames = {
|
String[] opcodeNames = {
|
||||||
"BRK", "ORA", null, null, null, "ORA", "ASL", null,
|
"BRK", "ORA", null, null, null, "ORA", "ASL", null, // 0x00-0x07
|
||||||
"PHP", "ORA", "ASL", null, null, "ORA", "ASL", null,
|
"PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, // 0x08-0x0f
|
||||||
"BPL", "ORA", null, null, null, "ORA", "ASL", null,
|
"BPL", "ORA", "ORA", null, null, "ORA", "ASL", null, // 0x10-0x17
|
||||||
"CLC", "ORA", null, null, null, "ORA", "ASL", null,
|
"CLC", "ORA", null, null, null, "ORA", "ASL", null, // 0x18-0x1f
|
||||||
"JSR", "AND", null, null, "BIT", "AND", "ROL", null,
|
"JSR", "AND", null, null, "BIT", "AND", "ROL", null, // 0x20-0x27
|
||||||
"PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null,
|
"PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, // 0x28-0x2f
|
||||||
"BMI", "AND", null, null, null, "AND", "ROL", null,
|
"BMI", "AND", "AND", null, null, "AND", "ROL", null, // 0x30-0x37
|
||||||
"SEC", "AND", null, null, null, "AND", "ROL", null,
|
"SEC", "AND", null, null, null, "AND", "ROL", null, // 0x38-0x3f
|
||||||
"RTI", "EOR", null, null, null, "EOR", "LSR", null,
|
"RTI", "EOR", null, null, null, "EOR", "LSR", null, // 0x40-0x47
|
||||||
"PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null,
|
"PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, // 0x48-0x4f
|
||||||
"BVC", "EOR", null, null, null, "EOR", "LSR", null,
|
"BVC", "EOR", "EOR", null, null, "EOR", "LSR", null, // 0x50-0x57
|
||||||
"CLI", "EOR", null, null, null, "EOR", "LSR", null,
|
"CLI", "EOR", null, null, null, "EOR", "LSR", null, // 0x58-0x5f
|
||||||
"RTS", "ADC", null, null, null, "ADC", "ROR", null,
|
"RTS", "ADC", null, null, null, "ADC", "ROR", null, // 0x60-0x67
|
||||||
"PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null,
|
"PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, // 0x68-0x6f
|
||||||
"BVS", "ADC", null, null, null, "ADC", "ROR", null,
|
"BVS", "ADC", "ADC", null, null, "ADC", "ROR", null, // 0x70-0x77
|
||||||
"SEI", "ADC", null, null, null, "ADC", "ROR", null,
|
"SEI", "ADC", null, null, "JMP", "ADC", "ROR", null, // 0x78-0x7f
|
||||||
"BCS", "STA", null, null, "STY", "STA", "STX", null,
|
"BCS", "STA", null, null, "STY", "STA", "STX", null, // 0x80-0x87
|
||||||
"DEY", null, "TXA", null, "STY", "STA", "STX", null,
|
"DEY", null, "TXA", null, "STY", "STA", "STX", null, // 0x88-0x8f
|
||||||
"BCC", "STA", null, null, "STY", "STA", "STX", null,
|
"BCC", "STA", "STA", null, "STY", "STA", "STX", null, // 0x90-0x97
|
||||||
"TYA", "STA", "TXS", null, null, "STA", null, null,
|
"TYA", "STA", "TXS", null, null, "STA", null, null, // 0x98-0x9f
|
||||||
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null,
|
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, // 0xa0-0xa7
|
||||||
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null,
|
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, // 0xa8-0xaf
|
||||||
"BCS", "LDA", null, null, "LDY", "LDA", "LDX", null,
|
"BCS", "LDA", "LDA", null, "LDY", "LDA", "LDX", null, // 0xb0-0xb7
|
||||||
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null,
|
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, // 0xb8-0xbf
|
||||||
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null,
|
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, // 0xc0-0xc7
|
||||||
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null,
|
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, // 0xc8-0xcf
|
||||||
"BNE", "CMP", null, null, null, "CMP", "DEC", null,
|
"BNE", "CMP", "CMP", null, null, "CMP", "DEC", null, // 0xd0-0xd7
|
||||||
"CLD", "CMP", null, null, null, "CMP", "DEC", null,
|
"CLD", "CMP", null, null, null, "CMP", "DEC", null, // 0xd8-0xdf
|
||||||
"CPX", "SBC", null, null, "CPX", "SBC", "INC", null,
|
"CPX", "SBC", null, null, "CPX", "SBC", "INC", null, // 0xe0-0xe7
|
||||||
"INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null,
|
"INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, // 0xe8-0xef
|
||||||
"BEQ", "SBC", null, null, null, "SBC", "INC", null,
|
"BEQ", "SBC", "SBC", null, null, "SBC", "INC", null, // 0xf0-0xf7
|
||||||
"SED", "SBC", null, null, null, "SBC", "INC", null
|
"SED", "SBC", null, null, null, "SBC", "INC", null // 0xf8-0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instruction addressing modes.
|
* Instruction addressing modes. This table includes sizes
|
||||||
|
* for all instructions for NMOS 6502, CMOS 65C02,
|
||||||
|
* and CMOS 65C816
|
||||||
*/
|
*/
|
||||||
public static final Mode[] instructionModes = {
|
Mode[] instructionModes = {
|
||||||
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
|
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
|
||||||
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
|
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
|
||||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
|
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
|
||||||
Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f
|
Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x10-0x13
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x10-0x13
|
||||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17
|
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17
|
||||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b
|
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b
|
||||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f
|
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f
|
||||||
@@ -207,7 +213,7 @@ public interface InstructionTable {
|
|||||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27
|
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27
|
||||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b
|
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b
|
||||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f
|
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x30-0x33
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x30-0x33
|
||||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37
|
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37
|
||||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b
|
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b
|
||||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f
|
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f
|
||||||
@@ -215,7 +221,7 @@ public interface InstructionTable {
|
|||||||
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47
|
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47
|
||||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b
|
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b
|
||||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f
|
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x50-0x53
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x50-0x53
|
||||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57
|
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57
|
||||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b
|
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b
|
||||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f
|
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f
|
||||||
@@ -223,15 +229,15 @@ public interface InstructionTable {
|
|||||||
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67
|
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67
|
||||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b
|
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b
|
||||||
Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f
|
Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x70-0x73
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x70-0x73
|
||||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77
|
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77
|
||||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b
|
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b
|
||||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
|
Mode.ABX, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
|
||||||
Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83
|
Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83
|
||||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87
|
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87
|
||||||
Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b
|
Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b
|
||||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
|
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x90-0x93
|
||||||
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
|
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
|
||||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
|
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
|
||||||
Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
|
Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
|
||||||
@@ -239,7 +245,7 @@ public interface InstructionTable {
|
|||||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
|
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
|
||||||
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
|
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
|
||||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
|
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xb0-0xb3
|
||||||
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
|
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
|
||||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb
|
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb
|
||||||
Mode.ABX, Mode.ABX, Mode.ABY, Mode.NUL, // 0xbc-0xbf
|
Mode.ABX, Mode.ABX, Mode.ABY, Mode.NUL, // 0xbc-0xbf
|
||||||
@@ -247,7 +253,7 @@ public interface InstructionTable {
|
|||||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7
|
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7
|
||||||
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb
|
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb
|
||||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf
|
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xd0-0xd3
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xd0-0xd3
|
||||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7
|
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7
|
||||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb
|
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb
|
||||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf
|
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf
|
||||||
@@ -255,7 +261,7 @@ public interface InstructionTable {
|
|||||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7
|
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7
|
||||||
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb
|
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb
|
||||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef
|
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef
|
||||||
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xf0-0xf3
|
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xf0-0xf3
|
||||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7
|
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7
|
||||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
|
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
|
||||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
|
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
|
||||||
@@ -263,47 +269,73 @@ public interface InstructionTable {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Size, in bytes, required for each instruction.
|
* Size, in bytes, required for each instruction. This table
|
||||||
|
* includes sizes for all instructions for NMOS 6502, CMOS 65C02,
|
||||||
|
* and CMOS 65C816
|
||||||
*/
|
*/
|
||||||
public static final int[] instructionSizes = {
|
int[] instructionSizes = {
|
||||||
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0,
|
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, // 0x00-0x0f
|
||||||
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
|
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x10-0x1f
|
||||||
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
|
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x20-0x2f
|
||||||
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
|
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x30-0x3f
|
||||||
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
|
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x40-0x4f
|
||||||
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
|
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x50-0x5f
|
||||||
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
|
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x60-0x6f
|
||||||
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
|
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 3, 3, 3, 0, // 0x70-0x7f
|
||||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
|
2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, // 0x80-0x8f
|
||||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
|
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, // 0x90-0x9f
|
||||||
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
|
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xa0-0xaf
|
||||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
|
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 0xb0-0xbf
|
||||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
|
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xc0-0xcf
|
||||||
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
|
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0xd0-0xdf
|
||||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
|
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xe0-0xef
|
||||||
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
|
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 // 0xf0-0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of clock cycles required for each instruction
|
* Number of clock cycles required for each instruction when
|
||||||
|
* in NMOS mode.
|
||||||
*/
|
*/
|
||||||
public static final int[] instructionClocks = {
|
int[] instructionClocksNmos = {
|
||||||
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0,
|
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
|
||||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
|
||||||
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0,
|
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
|
||||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
|
||||||
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0,
|
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
|
||||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
|
||||||
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0,
|
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
|
||||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
|
||||||
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
|
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
|
||||||
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
|
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
|
||||||
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
|
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
|
||||||
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
|
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
|
||||||
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
|
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
|
||||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
|
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
|
||||||
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
|
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
|
||||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0
|
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of clock cycles required for each instruction when
|
||||||
|
* in CMOS mode
|
||||||
|
*/
|
||||||
|
int[] instructionClocksCmos = {
|
||||||
|
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
|
||||||
|
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
|
||||||
|
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
|
||||||
|
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
|
||||||
|
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
|
||||||
|
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
|
||||||
|
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
|
||||||
|
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
|
||||||
|
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
|
||||||
|
2, 6, 5, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
|
||||||
|
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
|
||||||
|
2, 5, 5, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
|
||||||
|
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
|
||||||
|
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
|
||||||
|
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
|
||||||
|
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
* Maik Merten <maikmerten@googlemail.com>
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
@@ -39,7 +39,7 @@ public class Main {
|
|||||||
* Main entry point to the simulator. Creates a simulator and shows the main
|
* Main entry point to the simulator. Creates a simulator and shows the main
|
||||||
* window.
|
* window.
|
||||||
*
|
*
|
||||||
* @param args
|
* @param args Program arguments
|
||||||
*/
|
*/
|
||||||
public static void main(String args[]) throws Exception {
|
public static void main(String args[]) throws Exception {
|
||||||
|
|
||||||
@@ -48,12 +48,16 @@ public class Main {
|
|||||||
String arg = args[i].toLowerCase(Locale.ENGLISH);
|
String arg = args[i].toLowerCase(Locale.ENGLISH);
|
||||||
if(arg.equals("-machine") && (i+1) < args.length) {
|
if(arg.equals("-machine") && (i+1) < args.length) {
|
||||||
String machine = args[i+1].trim().toLowerCase(Locale.ENGLISH);
|
String machine = args[i+1].trim().toLowerCase(Locale.ENGLISH);
|
||||||
if(machine.equals("symon")) {
|
switch (machine) {
|
||||||
|
case "symon":
|
||||||
machineClass = SymonMachine.class;
|
machineClass = SymonMachine.class;
|
||||||
} else if(machine.equals("multicomp")) {
|
break;
|
||||||
|
case "multicomp":
|
||||||
machineClass = MulticompMachine.class;
|
machineClass = MulticompMachine.class;
|
||||||
} else if (machine.equals("simple")) {
|
break;
|
||||||
|
case "simple":
|
||||||
machineClass = SimpleMachine.class;
|
machineClass = SimpleMachine.class;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -83,6 +87,7 @@ public class Main {
|
|||||||
final Simulator simulator = new Simulator(machineClass);
|
final Simulator simulator = new Simulator(machineClass);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||||
@@ -95,8 +100,8 @@ public class Main {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Simulator.MAIN_CMD cmd = simulator.waitForCommand();
|
Simulator.MainCommand cmd = simulator.waitForCommand();
|
||||||
if (cmd.equals(Simulator.MAIN_CMD.SELECTMACHINE)) {
|
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
|
||||||
machineClass = null;
|
machineClass = null;
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -34,9 +34,13 @@ import com.loomcom.symon.exceptions.*;
|
|||||||
*/
|
*/
|
||||||
public class MemoryRange implements Comparable<MemoryRange> {
|
public class MemoryRange implements Comparable<MemoryRange> {
|
||||||
|
|
||||||
/** The starting address of the memory range. */
|
/**
|
||||||
|
* The starting address of the memory range.
|
||||||
|
*/
|
||||||
public int startAddress;
|
public int startAddress;
|
||||||
/** The ending address of the memory range. */
|
/**
|
||||||
|
* The ending address of the memory range.
|
||||||
|
*/
|
||||||
public int endAddress;
|
public int endAddress;
|
||||||
|
|
||||||
public MemoryRange(int startAddress, int endAddress)
|
public MemoryRange(int startAddress, int endAddress)
|
||||||
@@ -53,14 +57,14 @@ public class MemoryRange implements Comparable<MemoryRange> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the starting address.
|
* @return the starting address.
|
||||||
*/
|
*/
|
||||||
public int startAddress() {
|
public int startAddress() {
|
||||||
return startAddress;
|
return startAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns the ending address.
|
* @return the ending address.
|
||||||
*/
|
*/
|
||||||
public int endAddress() {
|
public int endAddress() {
|
||||||
return endAddress;
|
return endAddress;
|
||||||
@@ -69,7 +73,7 @@ public class MemoryRange implements Comparable<MemoryRange> {
|
|||||||
/**
|
/**
|
||||||
* Checks for address inclusion in the range.
|
* Checks for address inclusion in the range.
|
||||||
*
|
*
|
||||||
* @returns true if the address is included within this range,
|
* @return true if the address is included within this range,
|
||||||
* false otherwise.
|
* false otherwise.
|
||||||
*/
|
*/
|
||||||
public boolean includes(int address) {
|
public boolean includes(int address) {
|
||||||
@@ -80,7 +84,7 @@ public class MemoryRange implements Comparable<MemoryRange> {
|
|||||||
/**
|
/**
|
||||||
* Checks for overlapping memory ranges.
|
* Checks for overlapping memory ranges.
|
||||||
*
|
*
|
||||||
* @returns true if this range overlaps in any way with the other.
|
* @return true if this range overlaps in any way with the other.
|
||||||
*/
|
*/
|
||||||
public boolean overlaps(MemoryRange other) {
|
public boolean overlaps(MemoryRange other) {
|
||||||
return (this.includes(other.startAddress()) ||
|
return (this.includes(other.startAddress()) ||
|
||||||
@@ -95,16 +99,13 @@ public class MemoryRange implements Comparable<MemoryRange> {
|
|||||||
if (this == other) {
|
if (this == other) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Integer thisStartAddr = new Integer(this.startAddress());
|
Integer thisStartAddr = this.startAddress();
|
||||||
Integer thatStartAddr = new Integer(other.startAddress());
|
Integer thatStartAddr = other.startAddress();
|
||||||
return thisStartAddr.compareTo(thatStartAddr);
|
return thisStartAddr.compareTo(thatStartAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuffer desc = new StringBuffer("@");
|
return "@" + String.format("0x%04x", startAddress) + "-" +
|
||||||
desc.append(String.format("0x%04x", startAddress));
|
String.format("0x%04x", endAddress);
|
||||||
desc.append("-");
|
|
||||||
desc.append(String.format("0x%04x", endAddress));
|
|
||||||
return desc.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -27,15 +27,15 @@ import javax.swing.*;
|
|||||||
|
|
||||||
public interface Preferences {
|
public interface Preferences {
|
||||||
|
|
||||||
public static final int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
|
int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
|
||||||
|
|
||||||
public static final boolean DEFAULT_HALT_ON_BREAK = true;
|
boolean DEFAULT_HALT_ON_BREAK = true;
|
||||||
|
|
||||||
public JDialog getDialog();
|
JDialog getDialog();
|
||||||
|
|
||||||
public int getProgramStartAddress();
|
int getProgramStartAddress();
|
||||||
|
|
||||||
public boolean getHaltOnBreak();
|
boolean getHaltOnBreak();
|
||||||
|
|
||||||
public void updateUi();
|
void updateUi();
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyrighi (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -24,21 +24,20 @@
|
|||||||
package com.loomcom.symon;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
import com.loomcom.symon.exceptions.FifoUnderrunException;
|
import com.loomcom.symon.exceptions.*;
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
|
||||||
import com.loomcom.symon.exceptions.SymonException;
|
|
||||||
import com.loomcom.symon.machines.Machine;
|
import com.loomcom.symon.machines.Machine;
|
||||||
import com.loomcom.symon.ui.*;
|
import com.loomcom.symon.ui.*;
|
||||||
import com.loomcom.symon.ui.Console;
|
import com.loomcom.symon.ui.Console;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.logging.Level;
|
import java.util.SortedSet;
|
||||||
import java.util.logging.Logger;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Symon Simulator Interface and Control.
|
* Symon Simulator Interface and Control.
|
||||||
@@ -50,11 +49,16 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class Simulator {
|
public class Simulator {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(Simulator.class.getName());
|
||||||
|
|
||||||
// UI constants
|
// UI constants
|
||||||
private static final int DEFAULT_FONT_SIZE = 12;
|
private static final int DEFAULT_FONT_SIZE = 12;
|
||||||
private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
|
private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
|
||||||
private static final int CONSOLE_BORDER_WIDTH = 10;
|
private static final int CONSOLE_BORDER_WIDTH = 10;
|
||||||
|
|
||||||
|
// Clock periods, in NS, for each speed. 0MHz, 1MHz, 2MHz, 3MHz, 4MHz, 5MHz, 6MHz, 7MHz, 8MHz.
|
||||||
|
private static final long[] CLOCK_PERIODS = {0, 1000, 500, 333, 250, 200, 167, 143, 125};
|
||||||
|
|
||||||
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
|
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
|
||||||
// to refresh the status view on every simulated clock cycle. Instead, we will only refresh the status view
|
// to refresh the status view on every simulated clock cycle. Instead, we will only refresh the status view
|
||||||
// after this number of steps when running normally.
|
// after this number of steps when running normally.
|
||||||
@@ -66,14 +70,12 @@ public class Simulator {
|
|||||||
//
|
//
|
||||||
private static final int MAX_STEPS_BETWEEN_UPDATES = 20000;
|
private static final int MAX_STEPS_BETWEEN_UPDATES = 20000;
|
||||||
|
|
||||||
private final static Logger logger = Logger.getLogger(Simulator.class.getName());
|
|
||||||
|
|
||||||
// The simulated machine
|
// The simulated machine
|
||||||
private Machine machine;
|
private Machine machine;
|
||||||
|
|
||||||
// Number of CPU steps between CRT repaints.
|
// Number of CPU steps between CRT repaints.
|
||||||
// TODO: Dynamically refresh the value at runtime based on performance figures to reach ~ 30fps.
|
// TODO: Dynamically refresh the value at runtime based on performance figures to reach ~ 30fps.
|
||||||
private long stepsBetweenCrtcRefreshes = 2500;
|
private static final long STEPS_BETWEEN_CRTC_REFRESHES = 2500;
|
||||||
|
|
||||||
// A counter to keep track of the number of UI updates that have been
|
// A counter to keep track of the number of UI updates that have been
|
||||||
// requested
|
// requested
|
||||||
@@ -93,14 +95,16 @@ public class Simulator {
|
|||||||
/**
|
/**
|
||||||
* The Trace Window shows the most recent 50,000 CPU states.
|
* The Trace Window shows the most recent 50,000 CPU states.
|
||||||
*/
|
*/
|
||||||
private TraceLog traceLog;
|
private final TraceLog traceLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Memory Window shows the contents of one page of memory.
|
* The Memory Window shows the contents of one page of memory.
|
||||||
*/
|
*/
|
||||||
private MemoryWindow memoryWindow;
|
private final MemoryWindow memoryWindow;
|
||||||
|
|
||||||
private VideoWindow videoWindow;
|
private final VideoWindow videoWindow;
|
||||||
|
|
||||||
|
private final BreakpointsWindow breakpointsWindow;
|
||||||
|
|
||||||
private SimulatorMenu menuBar;
|
private SimulatorMenu menuBar;
|
||||||
|
|
||||||
@@ -110,16 +114,18 @@ public class Simulator {
|
|||||||
|
|
||||||
private JButton runStopButton;
|
private JButton runStopButton;
|
||||||
private JButton stepButton;
|
private JButton stepButton;
|
||||||
private JButton softResetButton;
|
|
||||||
private JButton hardResetButton;
|
|
||||||
private JComboBox<String> stepCountBox;
|
private JComboBox<String> stepCountBox;
|
||||||
|
|
||||||
private JFileChooser fileChooser;
|
private JFileChooser fileChooser;
|
||||||
private PreferencesDialog preferences;
|
private PreferencesDialog preferences;
|
||||||
|
|
||||||
|
private Breakpoints breakpoints;
|
||||||
|
|
||||||
private final Object commandMonitorObject = new Object();
|
private final Object commandMonitorObject = new Object();
|
||||||
private MAIN_CMD command = MAIN_CMD.NONE;
|
|
||||||
public static enum MAIN_CMD {
|
private MainCommand command = MainCommand.NONE;
|
||||||
|
|
||||||
|
public enum MainCommand {
|
||||||
NONE,
|
NONE,
|
||||||
SELECTMACHINE
|
SELECTMACHINE
|
||||||
}
|
}
|
||||||
@@ -130,7 +136,20 @@ public class Simulator {
|
|||||||
private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"};
|
private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"};
|
||||||
|
|
||||||
public Simulator(Class machineClass) throws Exception {
|
public Simulator(Class machineClass) throws Exception {
|
||||||
|
this.breakpoints = new Breakpoints(this);
|
||||||
|
|
||||||
this.machine = (Machine) machineClass.getConstructors()[0].newInstance();
|
this.machine = (Machine) machineClass.getConstructors()[0].newInstance();
|
||||||
|
|
||||||
|
// Initialize final fields in the constructor.
|
||||||
|
this.traceLog = new TraceLog();
|
||||||
|
this.memoryWindow = new MemoryWindow(machine.getBus());
|
||||||
|
this.breakpointsWindow = new BreakpointsWindow(breakpoints, mainWindow);
|
||||||
|
|
||||||
|
if (machine.getCrtc() != null) {
|
||||||
|
videoWindow = new VideoWindow(machine.getCrtc(), 2, 2);
|
||||||
|
} else {
|
||||||
|
videoWindow = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -143,7 +162,7 @@ public class Simulator {
|
|||||||
mainWindow.getContentPane().setLayout(new BorderLayout());
|
mainWindow.getContentPane().setLayout(new BorderLayout());
|
||||||
|
|
||||||
// UI components used for I/O.
|
// UI components used for I/O.
|
||||||
this.console = new com.loomcom.symon.ui.Console(80, 25, DEFAULT_FONT);
|
this.console = new com.loomcom.symon.ui.Console(80, 25, DEFAULT_FONT, false);
|
||||||
this.statusPane = new StatusPanel(machine);
|
this.statusPane = new StatusPanel(machine);
|
||||||
|
|
||||||
console.setBorderWidth(CONSOLE_BORDER_WIDTH);
|
console.setBorderWidth(CONSOLE_BORDER_WIDTH);
|
||||||
@@ -162,11 +181,12 @@ public class Simulator {
|
|||||||
|
|
||||||
runStopButton = new JButton("Run");
|
runStopButton = new JButton("Run");
|
||||||
stepButton = new JButton("Step");
|
stepButton = new JButton("Step");
|
||||||
softResetButton = new JButton("Soft Reset");
|
JButton softResetButton = new JButton("Soft Reset");
|
||||||
hardResetButton = new JButton("Hard Reset");
|
JButton hardResetButton = new JButton("Hard Reset");
|
||||||
|
|
||||||
stepCountBox = new JComboBox<String>(STEPS);
|
stepCountBox = new JComboBox<>(STEPS);
|
||||||
stepCountBox.addActionListener(new ActionListener() {
|
stepCountBox.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
try {
|
try {
|
||||||
JComboBox cb = (JComboBox) actionEvent.getSource();
|
JComboBox cb = (JComboBox) actionEvent.getSource();
|
||||||
@@ -195,47 +215,40 @@ public class Simulator {
|
|||||||
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
|
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
|
||||||
|
|
||||||
runStopButton.addActionListener(new ActionListener() {
|
runStopButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
if (runLoop != null && runLoop.isRunning()) {
|
if (runLoop != null && runLoop.isRunning()) {
|
||||||
handleStop();
|
Simulator.this.handleStop();
|
||||||
} else {
|
} else {
|
||||||
handleStart();
|
Simulator.this.handleStart();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stepButton.addActionListener(new ActionListener() {
|
stepButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
handleStep(stepsPerClick);
|
Simulator.this.handleStep(stepsPerClick);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
softResetButton.addActionListener(new ActionListener() {
|
softResetButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
// If this was a CTRL-click, do a hard reset.
|
// If this was a CTRL-click, do a hard reset.
|
||||||
handleReset(false);
|
Simulator.this.handleReset(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
hardResetButton.addActionListener(new ActionListener() {
|
hardResetButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
// If this was a CTRL-click, do a hard reset.
|
// If this was a CTRL-click, do a hard reset.
|
||||||
handleReset(true);
|
Simulator.this.handleReset(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prepare the log window
|
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||||
traceLog = new TraceLog();
|
|
||||||
|
|
||||||
// Prepare the memory window
|
|
||||||
memoryWindow = new MemoryWindow(machine.getBus());
|
|
||||||
|
|
||||||
// Composite Video and 6545 CRTC
|
|
||||||
if(machine.getCrtc() != null) {
|
|
||||||
videoWindow = new VideoWindow(machine.getCrtc(), 2, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
|
||||||
|
|
||||||
// The Menu. This comes last, because it relies on other components having
|
// The Menu. This comes last, because it relies on other components having
|
||||||
// already been initialized.
|
// already been initialized.
|
||||||
@@ -249,7 +262,7 @@ public class Simulator {
|
|||||||
handleReset(false);
|
handleReset(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public MAIN_CMD waitForCommand() {
|
public MainCommand waitForCommand() {
|
||||||
synchronized (commandMonitorObject) {
|
synchronized (commandMonitorObject) {
|
||||||
try {
|
try {
|
||||||
commandMonitorObject.wait();
|
commandMonitorObject.wait();
|
||||||
@@ -287,7 +300,7 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.log(Level.INFO, "Reset requested. Resetting CPU.");
|
logger.debug("Reset requested. Resetting CPU.");
|
||||||
// Reset CPU
|
// Reset CPU
|
||||||
machine.getCpu().reset();
|
machine.getCpu().reset();
|
||||||
// Clear the console.
|
// Clear the console.
|
||||||
@@ -302,15 +315,9 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update status.
|
// Update status.
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
updateVisibleState();
|
||||||
public void run() {
|
|
||||||
// Now update the state
|
|
||||||
statusPane.updateState();
|
|
||||||
memoryWindow.updateState();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (MemoryAccessException ex) {
|
} catch (MemoryAccessException ex) {
|
||||||
logger.log(Level.SEVERE, "Exception during simulator reset: " + ex.getMessage());
|
logger.error("Exception during simulator reset", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,17 +329,9 @@ public class Simulator {
|
|||||||
for (int i = 0; i < numSteps; i++) {
|
for (int i = 0; i < numSteps; i++) {
|
||||||
step();
|
step();
|
||||||
}
|
}
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
updateVisibleState();
|
||||||
public void run() {
|
|
||||||
if (traceLog.isVisible()) {
|
|
||||||
traceLog.refresh();
|
|
||||||
}
|
|
||||||
statusPane.updateState();
|
|
||||||
memoryWindow.updateState();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (SymonException ex) {
|
} catch (SymonException ex) {
|
||||||
logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage());
|
logger.error("Exception during simulator step", ex);
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,27 +358,23 @@ public class Simulator {
|
|||||||
machine.getAcia().rxWrite((int) console.readInputChar());
|
machine.getAcia().rxWrite((int) console.readInputChar());
|
||||||
}
|
}
|
||||||
} catch (FifoUnderrunException ex) {
|
} catch (FifoUnderrunException ex) {
|
||||||
logger.severe("Console type-ahead buffer underrun!");
|
logger.error("Console type-ahead buffer underrun!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoWindow != null && stepsSinceLastCrtcRefresh++ > stepsBetweenCrtcRefreshes) {
|
if (videoWindow != null && stepsSinceLastCrtcRefresh++ > STEPS_BETWEEN_CRTC_REFRESHES) {
|
||||||
stepsSinceLastCrtcRefresh = 0;
|
stepsSinceLastCrtcRefresh = 0;
|
||||||
|
if (videoWindow.isVisible()) {
|
||||||
|
videoWindow.repaint();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a very expensive update, and we're doing it without
|
// This is a very expensive update, and we're doing it without
|
||||||
// a delay, so we don't want to overwhelm the Swing event processing thread
|
// a delay, so we don't want to overwhelm the Swing event processing thread
|
||||||
// with requests. Limit the number of ui updates that can be performed.
|
// with requests. Limit the number of ui updates that can be performed.
|
||||||
if (stepsSinceLastUpdate++ > MAX_STEPS_BETWEEN_UPDATES) {
|
if (stepsSinceLastUpdate++ > MAX_STEPS_BETWEEN_UPDATES) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
updateVisibleState();
|
||||||
public void run() {
|
|
||||||
// Now update the state
|
|
||||||
statusPane.updateState();
|
|
||||||
memoryWindow.updateState();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
stepsSinceLastUpdate = 0;
|
stepsSinceLastUpdate = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -391,8 +386,7 @@ public class Simulator {
|
|||||||
machine.getBus().write(addr++, program[i] & 0xff);
|
machine.getBus().write(addr++, program[i] & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.log(Level.INFO, "Loaded " + i + " bytes at address 0x" +
|
logger.info("Loaded {} bytes at address 0x{}", i, Integer.toString(startAddress, 16));
|
||||||
Integer.toString(startAddress, 16));
|
|
||||||
|
|
||||||
// After loading, be sure to reset and
|
// After loading, be sure to reset and
|
||||||
// Reset (but don't clear memory, naturally)
|
// Reset (but don't clear memory, naturally)
|
||||||
@@ -402,15 +396,8 @@ public class Simulator {
|
|||||||
machine.getCpu().setProgramCounter(preferences.getProgramStartAddress());
|
machine.getCpu().setProgramCounter(preferences.getProgramStartAddress());
|
||||||
|
|
||||||
// Immediately update the UI.
|
// Immediately update the UI.
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
updateVisibleState();
|
||||||
public void run() {
|
|
||||||
// Now update the state
|
|
||||||
statusPane.updateState();
|
|
||||||
memoryWindow.updateState();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main run thread.
|
* The main run thread.
|
||||||
@@ -427,10 +414,11 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
logger.log(Level.INFO, "Starting main run loop.");
|
logger.debug("Starting main run loop.");
|
||||||
isRunning = true;
|
isRunning = true;
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
// Don't allow step while the simulator is running
|
// Don't allow step while the simulator is running
|
||||||
stepButton.setEnabled(false);
|
stepButton.setEnabled(false);
|
||||||
@@ -446,11 +434,11 @@ public class Simulator {
|
|||||||
step();
|
step();
|
||||||
} while (shouldContinue());
|
} while (shouldContinue());
|
||||||
} catch (SymonException ex) {
|
} catch (SymonException ex) {
|
||||||
logger.log(Level.SEVERE, "Exception in main simulator run thread. Exiting run.");
|
logger.error("Exception in main simulator run thread. Exiting run.", ex);
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
statusPane.updateState();
|
statusPane.updateState();
|
||||||
memoryWindow.updateState();
|
memoryWindow.updateState();
|
||||||
@@ -469,15 +457,19 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the run loop should proceed to the next step.
|
|
||||||
*
|
|
||||||
* @return True if the run loop should proceed to the next step.
|
* @return True if the run loop should proceed to the next step.
|
||||||
*/
|
*/
|
||||||
private boolean shouldContinue() {
|
private boolean shouldContinue() {
|
||||||
return isRunning && !(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
|
return !breakpoints.contains(machine.getCpu().getProgramCounter()) &&
|
||||||
|
isRunning &&
|
||||||
|
!(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String disassembleOpAtAddress(int address) throws MemoryAccessException {
|
||||||
|
return machine.getCpu().disassembleOpAtAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
class LoadProgramAction extends AbstractAction {
|
class LoadProgramAction extends AbstractAction {
|
||||||
public LoadProgramAction() {
|
public LoadProgramAction() {
|
||||||
super("Load Program...", null);
|
super("Load Program...", null);
|
||||||
@@ -508,14 +500,17 @@ public class Simulator {
|
|||||||
program[i++] = dis.readByte();
|
program[i++] = dis.readByte();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now load the program at the starting address.
|
||||||
|
loadProgram(program, preferences.getProgramStartAddress());
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
console.reset();
|
console.reset();
|
||||||
|
breakpoints.refresh();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Now load the program at the starting address.
|
|
||||||
loadProgram(program, preferences.getProgramStartAddress());
|
|
||||||
// TODO: "Don't Show Again" checkbox
|
// TODO: "Don't Show Again" checkbox
|
||||||
JOptionPane.showMessageDialog(mainWindow,
|
JOptionPane.showMessageDialog(mainWindow,
|
||||||
"Loaded Successfully At " +
|
"Loaded Successfully At " +
|
||||||
@@ -526,10 +521,10 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.SEVERE, "Unable to read program file: " + ex.getMessage());
|
logger.error("Unable to read program file.", ex);
|
||||||
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
} catch (MemoryAccessException ex) {
|
} catch (MemoryAccessException ex) {
|
||||||
logger.log(Level.SEVERE, "Memory access error loading program: " + ex.getMessage());
|
logger.error("Memory access error loading program", ex);
|
||||||
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -552,7 +547,7 @@ public class Simulator {
|
|||||||
|
|
||||||
if (fileSize != machine.getRomSize()) {
|
if (fileSize != machine.getRomSize()) {
|
||||||
throw new IOException("ROM file must be exactly " + String.valueOf(machine.getRomSize()) + " bytes.");
|
throw new IOException("ROM file must be exactly " + String.valueOf(machine.getRomSize()) + " bytes.");
|
||||||
} else {
|
}
|
||||||
|
|
||||||
// Load the new ROM image
|
// Load the new ROM image
|
||||||
Memory rom = Memory.makeROM(machine.getRomBase(), machine.getRomBase() + machine.getRomSize() - 1, romFile);
|
Memory rom = Memory.makeROM(machine.getRomBase(), machine.getRomBase() + machine.getRomSize() - 1, romFile);
|
||||||
@@ -561,7 +556,12 @@ public class Simulator {
|
|||||||
// Now, reset
|
// Now, reset
|
||||||
machine.getCpu().reset();
|
machine.getCpu().reset();
|
||||||
|
|
||||||
logger.log(Level.INFO, "ROM File `" + romFile.getName() + "' loaded at " +
|
updateVisibleState();
|
||||||
|
|
||||||
|
// Refresh breakpoints to show new memory contents.
|
||||||
|
breakpoints.refresh();
|
||||||
|
|
||||||
|
logger.info("ROM File `{}' loaded at {}", romFile.getName(),
|
||||||
String.format("0x%04X", machine.getRomBase()));
|
String.format("0x%04X", machine.getRomBase()));
|
||||||
// TODO: "Don't Show Again" checkbox
|
// TODO: "Don't Show Again" checkbox
|
||||||
JOptionPane.showMessageDialog(mainWindow,
|
JOptionPane.showMessageDialog(mainWindow,
|
||||||
@@ -569,17 +569,17 @@ public class Simulator {
|
|||||||
String.format("$%04X", machine.getRomBase()),
|
String.format("$%04X", machine.getRomBase()),
|
||||||
"OK",
|
"OK",
|
||||||
JOptionPane.PLAIN_MESSAGE);
|
JOptionPane.PLAIN_MESSAGE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.SEVERE, "Unable to read ROM file: " + ex.getMessage());
|
logger.error("Unable to read ROM file: {}", ex.getMessage());
|
||||||
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
} catch (MemoryRangeException ex) {
|
} catch (MemoryRangeException ex) {
|
||||||
logger.log(Level.SEVERE, "Memory range error while loading ROM file: " + ex.getMessage());
|
logger.error("Memory range error while loading ROM file: {}", ex.getMessage());
|
||||||
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
} catch (MemoryAccessException ex) {
|
} catch (MemoryAccessException ex) {
|
||||||
logger.log(Level.SEVERE, "Memory access error while loading ROM file: " + ex.getMessage());
|
logger.error("Memory access error while loading ROM file: {}", ex.getMessage());
|
||||||
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -616,7 +616,7 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
mainWindow.dispose();
|
mainWindow.dispose();
|
||||||
|
|
||||||
command = MAIN_CMD.SELECTMACHINE;
|
command = MainCommand.SELECTMACHINE;
|
||||||
synchronized (commandMonitorObject) {
|
synchronized (commandMonitorObject) {
|
||||||
commandMonitorObject.notifyAll();
|
commandMonitorObject.notifyAll();
|
||||||
}
|
}
|
||||||
@@ -645,11 +645,12 @@ public class Simulator {
|
|||||||
public SetFontAction(int size) {
|
public SetFontAction(int size) {
|
||||||
super(Integer.toString(size) + " pt", null);
|
super(Integer.toString(size) + " pt", null);
|
||||||
this.size = size;
|
this.size = size;
|
||||||
putValue(SHORT_DESCRIPTION, "Set font to " + Integer.toString(size) + "pt.");
|
putValue(SHORT_DESCRIPTION, "Set font to " + size + "pt.");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
||||||
mainWindow.pack();
|
mainWindow.pack();
|
||||||
@@ -658,6 +659,25 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class SetSpeedAction extends AbstractAction {
|
||||||
|
private int speed;
|
||||||
|
|
||||||
|
public SetSpeedAction(int speed) {
|
||||||
|
super(Integer.toString(speed) + " MHz", null);
|
||||||
|
this.speed = speed;
|
||||||
|
putValue(SHORT_DESCRIPTION, "Set simulated speed to " + speed + " MHz.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
if (speed < 1 || speed > CLOCK_PERIODS.length - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
machine.getCpu().setClockPeriodInNs(CLOCK_PERIODS[speed]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ToggleTraceWindowAction extends AbstractAction {
|
class ToggleTraceWindowAction extends AbstractAction {
|
||||||
public ToggleTraceWindowAction() {
|
public ToggleTraceWindowAction() {
|
||||||
super("Trace Log", null);
|
super("Trace Log", null);
|
||||||
@@ -710,6 +730,23 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ToggleBreakpointWindowAction extends AbstractAction {
|
||||||
|
public ToggleBreakpointWindowAction() {
|
||||||
|
super("Breakpoints...", null);
|
||||||
|
putValue(SHORT_DESCRIPTION, "Show or Hide Breakpoints");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
synchronized (breakpointsWindow) {
|
||||||
|
if (breakpointsWindow.isVisible()) {
|
||||||
|
breakpointsWindow.setVisible(false);
|
||||||
|
} else {
|
||||||
|
breakpointsWindow.setVisible(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SimulatorMenu extends JMenuBar {
|
class SimulatorMenu extends JMenuBar {
|
||||||
// Menu Items
|
// Menu Items
|
||||||
private JMenuItem loadProgramItem;
|
private JMenuItem loadProgramItem;
|
||||||
@@ -762,9 +799,6 @@ public class Simulator {
|
|||||||
JMenuItem prefsItem = new JMenuItem(new ShowPrefsAction());
|
JMenuItem prefsItem = new JMenuItem(new ShowPrefsAction());
|
||||||
fileMenu.add(prefsItem);
|
fileMenu.add(prefsItem);
|
||||||
|
|
||||||
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
|
|
||||||
fileMenu.add(selectMachineItem);
|
|
||||||
|
|
||||||
JMenuItem quitItem = new JMenuItem(new QuitAction());
|
JMenuItem quitItem = new JMenuItem(new QuitAction());
|
||||||
fileMenu.add(quitItem);
|
fileMenu.add(quitItem);
|
||||||
|
|
||||||
@@ -822,16 +856,79 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
add(viewMenu);
|
add(viewMenu);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Simulator Menu
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
JMenu simulatorMenu = new JMenu("Simulator");
|
||||||
|
|
||||||
|
// "Select Machine..." item.
|
||||||
|
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
|
||||||
|
simulatorMenu.add(selectMachineItem);
|
||||||
|
|
||||||
|
// "Clock Speed" sub-menu
|
||||||
|
JMenu speedSubMenu = new JMenu("Clock Speed");
|
||||||
|
ButtonGroup speedGroup = new ButtonGroup();
|
||||||
|
|
||||||
|
makeSpeedMenuItem(1, speedSubMenu, speedGroup);
|
||||||
|
makeSpeedMenuItem(2, speedSubMenu, speedGroup);
|
||||||
|
makeSpeedMenuItem(4, speedSubMenu, speedGroup);
|
||||||
|
makeSpeedMenuItem(8, speedSubMenu, speedGroup);
|
||||||
|
|
||||||
|
simulatorMenu.add(speedSubMenu);
|
||||||
|
|
||||||
|
// "Breakpoints"
|
||||||
|
final JCheckBoxMenuItem showBreakpoints = new JCheckBoxMenuItem(new ToggleBreakpointWindowAction());
|
||||||
|
// Un-check the menu item if the user closes the window directly
|
||||||
|
breakpointsWindow.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent e) {
|
||||||
|
showBreakpoints.setSelected(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
simulatorMenu.add(showBreakpoints);
|
||||||
|
|
||||||
|
add(simulatorMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void makeFontSizeMenuItem(int size, JMenu fontSubMenu, ButtonGroup group) {
|
private void makeFontSizeMenuItem(int size, JMenu subMenu, ButtonGroup group) {
|
||||||
Action action = new SetFontAction(size);
|
Action action = new SetFontAction(size);
|
||||||
|
|
||||||
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
|
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
|
||||||
item.setSelected(size == DEFAULT_FONT_SIZE);
|
item.setSelected(size == DEFAULT_FONT_SIZE);
|
||||||
fontSubMenu.add(item);
|
subMenu.add(item);
|
||||||
|
group.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeSpeedMenuItem(int speed, JMenu subMenu, ButtonGroup group) {
|
||||||
|
if (speed < 1 || speed > CLOCK_PERIODS.length - 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Action action = new SetSpeedAction(speed);
|
||||||
|
|
||||||
|
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
|
||||||
|
item.setSelected(CLOCK_PERIODS[speed] == Cpu.DEFAULT_CLOCK_PERIOD_IN_NS);
|
||||||
|
subMenu.add(item);
|
||||||
group.add(item);
|
group.add(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateVisibleState() {
|
||||||
|
// Immediately update the UI.
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
// Now update the state
|
||||||
|
statusPane.updateState();
|
||||||
|
memoryWindow.updateState();
|
||||||
|
if (traceLog.shouldUpdate()) {
|
||||||
|
traceLog.refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -82,6 +82,7 @@ public abstract class Acia extends Device {
|
|||||||
/**
|
/**
|
||||||
* @return The simulated baud rate in bps.
|
* @return The simulated baud rate in bps.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public int getBaudRate() {
|
public int getBaudRate() {
|
||||||
return baudRate;
|
return baudRate;
|
||||||
}
|
}
|
||||||
@@ -153,6 +154,7 @@ public abstract class Acia extends Device {
|
|||||||
/**
|
/**
|
||||||
* @return true if there is character data in the RX register.
|
* @return true if there is character data in the RX register.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public boolean hasRxChar() {
|
public boolean hasRxChar() {
|
||||||
return rxFull;
|
return rxFull;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -26,7 +26,6 @@ package com.loomcom.symon.devices;
|
|||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is a simulation of the MOS 6551 ACIA, with limited
|
* This is a simulation of the MOS 6551 ACIA, with limited
|
||||||
* functionality. Interrupts are not supported.
|
* functionality. Interrupts are not supported.
|
||||||
@@ -45,7 +44,6 @@ public class Acia6551 extends Acia {
|
|||||||
static final int CMND_REG = 2;
|
static final int CMND_REG = 2;
|
||||||
static final int CTRL_REG = 3;
|
static final int CTRL_REG = 3;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers. These are ignored in the current implementation.
|
* Registers. These are ignored in the current implementation.
|
||||||
*/
|
*/
|
||||||
@@ -106,7 +104,7 @@ public class Acia6551 extends Acia {
|
|||||||
/**
|
/**
|
||||||
* Set the control register and associated state.
|
* Set the control register and associated state.
|
||||||
*
|
*
|
||||||
* @param data
|
* @param data Data to write into the control register
|
||||||
*/
|
*/
|
||||||
private void setControlRegister(int data) {
|
private void setControlRegister(int data) {
|
||||||
controlRegister = data;
|
controlRegister = data;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
* Maik Merten <maikmerten@googlemail.com>
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
@@ -41,20 +41,12 @@ public class Acia6850 extends Acia {
|
|||||||
|
|
||||||
public static final int ACIA_SIZE = 2;
|
public static final int ACIA_SIZE = 2;
|
||||||
|
|
||||||
|
|
||||||
static final int STAT_REG = 0; // read-only
|
static final int STAT_REG = 0; // read-only
|
||||||
static final int CTRL_REG = 0; // write-only
|
static final int CTRL_REG = 0; // write-only
|
||||||
|
|
||||||
static final int RX_REG = 1; // read-only
|
static final int RX_REG = 1; // read-only
|
||||||
static final int TX_REG = 1; // write-only
|
static final int TX_REG = 1; // write-only
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers. These are ignored in the current implementation.
|
|
||||||
*/
|
|
||||||
private int commandRegister;
|
|
||||||
|
|
||||||
|
|
||||||
public Acia6850(int address) throws MemoryRangeException {
|
public Acia6850(int address) throws MemoryRangeException {
|
||||||
super(address, ACIA_SIZE, "ACIA6850");
|
super(address, ACIA_SIZE, "ACIA6850");
|
||||||
setBaudRate(2400);
|
setBaudRate(2400);
|
||||||
@@ -88,17 +80,15 @@ public class Acia6850 extends Acia {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setCommandRegister(int data) {
|
private void setCommandRegister(int data) {
|
||||||
commandRegister = data;
|
|
||||||
|
|
||||||
// Bits 0 & 1 control the master reset
|
// Bits 0 & 1 control the master reset
|
||||||
if((commandRegister & 0x01) != 0 && (commandRegister & 0x02) != 0) {
|
if((data & 0x01) != 0 && (data & 0x02) != 0) {
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bit 7 controls receiver IRQ behavior
|
// Bit 7 controls receiver IRQ behavior
|
||||||
receiveIrqEnabled = (commandRegister & 0x80) != 0;
|
receiveIrqEnabled = (data & 0x80) != 0;
|
||||||
// Bits 5 & 6 controls transmit IRQ behavior
|
// Bits 5 & 6 controls transmit IRQ behavior
|
||||||
transmitIrqEnabled = (commandRegister & 0x20) != 0 && (commandRegister & 0x40) == 0;
|
transmitIrqEnabled = (data & 0x20) != 0 && (data & 0x40) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,37 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.loomcom.symon.devices;
|
package com.loomcom.symon.devices;
|
||||||
|
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulation of a 6545 CRTC and virtual CRT output.
|
* Simulation of a 6545 CRTC and virtual CRT output.
|
||||||
*/
|
*/
|
||||||
public class Crtc extends Device {
|
public class Crtc extends Device {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Crtc.class.getName());
|
|
||||||
|
|
||||||
|
|
||||||
// Memory locations in the CRTC address space
|
// Memory locations in the CRTC address space
|
||||||
public static final int REGISTER_SELECT = 0;
|
public static final int REGISTER_SELECT = 0;
|
||||||
public static final int REGISTER_RW = 1;
|
public static final int REGISTER_RW = 1;
|
||||||
@@ -65,6 +82,11 @@ public class Crtc extends Device {
|
|||||||
|
|
||||||
private int currentRegister = 0;
|
private int currentRegister = 0;
|
||||||
|
|
||||||
|
// Status bits
|
||||||
|
private boolean rowColumnAddressing = false;
|
||||||
|
private boolean displayEnableSkew = false;
|
||||||
|
private boolean cursorSkew = false;
|
||||||
|
|
||||||
private Memory memory;
|
private Memory memory;
|
||||||
|
|
||||||
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException {
|
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException {
|
||||||
@@ -118,8 +140,9 @@ public class Crtc extends Device {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getDmaAccess() {
|
public int getCharAtAddress(int address) throws MemoryAccessException {
|
||||||
return memory.getDmaAccess();
|
// TODO: Row/Column addressing
|
||||||
|
return memory.read(address);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHorizontalDisplayed() {
|
public int getHorizontalDisplayed() {
|
||||||
@@ -162,6 +185,18 @@ public class Crtc extends Device {
|
|||||||
return pageSize;
|
return pageSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getRowColumnAddressing() {
|
||||||
|
return rowColumnAddressing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getDisplayEnableSkew() {
|
||||||
|
return displayEnableSkew;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getCursorSkew() {
|
||||||
|
return cursorSkew;
|
||||||
|
}
|
||||||
|
|
||||||
private void setCurrentRegister(int registerNumber) {
|
private void setCurrentRegister(int registerNumber) {
|
||||||
this.currentRegister = registerNumber;
|
this.currentRegister = registerNumber;
|
||||||
}
|
}
|
||||||
@@ -180,7 +215,9 @@ public class Crtc extends Device {
|
|||||||
pageSize = horizontalDisplayed * verticalDisplayed;
|
pageSize = horizontalDisplayed * verticalDisplayed;
|
||||||
break;
|
break;
|
||||||
case MODE_CONTROL:
|
case MODE_CONTROL:
|
||||||
// TODO: Implement multiple addressing modes and cursor skew.
|
rowColumnAddressing = (data & 0x04) != 0;
|
||||||
|
displayEnableSkew = (data & 0x10) != 0;
|
||||||
|
cursorSkew = (data & 0x20) != 0;
|
||||||
break;
|
break;
|
||||||
case SCAN_LINE:
|
case SCAN_LINE:
|
||||||
scanLinesPerRow = data;
|
scanLinesPerRow = data;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -62,16 +62,11 @@ public abstract class Device implements Comparable<Device> {
|
|||||||
*/
|
*/
|
||||||
private Set<DeviceChangeListener> deviceChangeListeners;
|
private Set<DeviceChangeListener> deviceChangeListeners;
|
||||||
|
|
||||||
public Device(int startAddress, int endAddress, String name)
|
public Device(int startAddress, int endAddress, String name) throws MemoryRangeException {
|
||||||
throws MemoryRangeException {
|
|
||||||
this.memoryRange = new MemoryRange(startAddress, endAddress);
|
this.memoryRange = new MemoryRange(startAddress, endAddress);
|
||||||
this.size = endAddress - startAddress + 1;
|
this.size = endAddress - startAddress + 1;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.deviceChangeListeners = new HashSet<DeviceChangeListener>();
|
this.deviceChangeListeners = new HashSet<>();
|
||||||
}
|
|
||||||
|
|
||||||
public Device(int startAddress, int endAddress) throws MemoryRangeException {
|
|
||||||
this(startAddress, endAddress, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Methods required to be implemented by inheriting classes. */
|
/* Methods required to be implemented by inheriting classes. */
|
||||||
@@ -97,6 +92,7 @@ public abstract class Device implements Comparable<Device> {
|
|||||||
return memoryRange.endAddress();
|
return memoryRange.endAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public int startAddress() {
|
public int startAddress() {
|
||||||
return memoryRange.startAddress();
|
return memoryRange.startAddress();
|
||||||
}
|
}
|
||||||
@@ -105,6 +101,7 @@ public abstract class Device implements Comparable<Device> {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
@@ -118,8 +115,8 @@ public abstract class Device implements Comparable<Device> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void notifyListeners() {
|
public void notifyListeners() {
|
||||||
for (DeviceChangeListener l : deviceChangeListeners) {
|
for (DeviceChangeListener listener : deviceChangeListeners) {
|
||||||
l.deviceStateChanged();
|
listener.deviceStateChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -24,5 +24,5 @@
|
|||||||
package com.loomcom.symon.devices;
|
package com.loomcom.symon.devices;
|
||||||
|
|
||||||
public interface DeviceChangeListener {
|
public interface DeviceChangeListener {
|
||||||
public void deviceStateChanged();
|
void deviceStateChanged();
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -28,8 +28,6 @@ import java.util.*;
|
|||||||
|
|
||||||
import com.loomcom.symon.exceptions.*;
|
import com.loomcom.symon.exceptions.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public class Memory extends Device {
|
public class Memory extends Device {
|
||||||
|
|
||||||
private boolean readOnly;
|
private boolean readOnly;
|
||||||
@@ -57,8 +55,7 @@ public class Memory extends Device {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Memory makeRAM(int startAddress, int endAddress) throws MemoryRangeException {
|
public static Memory makeRAM(int startAddress, int endAddress) throws MemoryRangeException {
|
||||||
Memory memory = new Memory(startAddress, endAddress, false);
|
return new Memory(startAddress, endAddress, false);
|
||||||
return memory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(int address, int data) throws MemoryAccessException {
|
public void write(int address, int data) throws MemoryAccessException {
|
||||||
@@ -108,8 +105,4 @@ public class Memory extends Device {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "Memory: " + getMemoryRange().toString();
|
return "Memory: " + getMemoryRange().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int[] getDmaAccess() {
|
|
||||||
return mem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -23,45 +23,19 @@
|
|||||||
|
|
||||||
package com.loomcom.symon.devices;
|
package com.loomcom.symon.devices;
|
||||||
|
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
public class Via extends Device {
|
public abstract class Pia extends Device {
|
||||||
public static final int VIA_SIZE = 16;
|
|
||||||
|
|
||||||
private static final int ORB = 0;
|
private final String name;
|
||||||
private static final int ORA = 1;
|
|
||||||
private static final int DDRB = 2;
|
|
||||||
private static final int DDRA = 3;
|
|
||||||
private static final int T1C_L = 4;
|
|
||||||
private static final int T1C_H = 5;
|
|
||||||
private static final int T1L_L = 6;
|
|
||||||
private static final int T1L_H = 7;
|
|
||||||
private static final int T2C_L = 8;
|
|
||||||
private static final int T2C_H = 9;
|
|
||||||
private static final int SR = 10;
|
|
||||||
private static final int ACR = 11;
|
|
||||||
private static final int PCR = 12;
|
|
||||||
private static final int IFR = 13;
|
|
||||||
private static final int IER = 14;
|
|
||||||
private static final int ORA_H = 15;
|
|
||||||
|
|
||||||
public Via(int address) throws MemoryRangeException {
|
public Pia(int startAddress, int endAddress, String name) throws MemoryRangeException {
|
||||||
super(address, address + VIA_SIZE - 1, "VIA");
|
super(startAddress, endAddress, name);
|
||||||
}
|
this.name = name;
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(int address, int data) throws MemoryAccessException {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(int address) throws MemoryAccessException {
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return null;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
package com.loomcom.symon.devices;
|
||||||
|
|
||||||
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Emulation for the SD-card controller of the MULTICOMP system.
|
||||||
|
* Neiter comlete nor correct.
|
||||||
|
*/
|
||||||
|
public class SdController extends Device {
|
||||||
|
|
||||||
|
private enum Status {
|
||||||
|
IDLE,
|
||||||
|
READ,
|
||||||
|
WRITE
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int CONTROLLER_SIZE = 8;
|
||||||
|
private final int SECTOR_SIZE = 512;
|
||||||
|
private final static Logger logger = Logger.getLogger(SdController.class.getName());
|
||||||
|
|
||||||
|
private File sdImageFile;
|
||||||
|
private int lba0, lba1, lba2;
|
||||||
|
private int position;
|
||||||
|
private Status status = Status.IDLE;
|
||||||
|
|
||||||
|
private final byte[] readBuffer = new byte[SECTOR_SIZE];
|
||||||
|
private final byte[] writeBuffer = new byte[SECTOR_SIZE];
|
||||||
|
private int readPosition = 0;
|
||||||
|
private int writePosition = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public SdController(int address) throws MemoryRangeException {
|
||||||
|
super(address, address + CONTROLLER_SIZE - 1, "SDCONTROLLER");
|
||||||
|
|
||||||
|
sdImageFile = new File("sd.img");
|
||||||
|
if (!sdImageFile.exists()) {
|
||||||
|
sdImageFile = null;
|
||||||
|
logger.log(Level.INFO, "Could not find SD card image 'sd.img'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int address, int data) throws MemoryAccessException {
|
||||||
|
switch (address) {
|
||||||
|
case 0:
|
||||||
|
writeData(data);
|
||||||
|
return;
|
||||||
|
case 1:
|
||||||
|
writeCommand(data);
|
||||||
|
return;
|
||||||
|
case 2:
|
||||||
|
this.lba0 = data;
|
||||||
|
return;
|
||||||
|
case 3:
|
||||||
|
this.lba1 = data;
|
||||||
|
return;
|
||||||
|
case 4:
|
||||||
|
this.lba2 = data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(int address) throws MemoryAccessException {
|
||||||
|
switch (address) {
|
||||||
|
case 0:
|
||||||
|
return readData();
|
||||||
|
case 1:
|
||||||
|
return readStatus();
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void computePosition() {
|
||||||
|
this.position = lba0 + (lba1 << 8) + (lba2 << 16);
|
||||||
|
// each sector is 512 bytes, so multiply accordingly
|
||||||
|
this.position <<= 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareRead() {
|
||||||
|
this.status = Status.READ;
|
||||||
|
this.readPosition = 0;
|
||||||
|
computePosition();
|
||||||
|
|
||||||
|
if (sdImageFile != null) {
|
||||||
|
try {
|
||||||
|
FileInputStream fis = new FileInputStream(sdImageFile);
|
||||||
|
fis.skip(this.position);
|
||||||
|
int read = fis.read(readBuffer);
|
||||||
|
if (read < SECTOR_SIZE) {
|
||||||
|
logger.log(Level.WARNING, "not enough data to fill read buffer from SD image file");
|
||||||
|
}
|
||||||
|
fis.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "could not fill read buffer from SD image file", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void prepareWrite() {
|
||||||
|
this.status = Status.WRITE;
|
||||||
|
this.writePosition = 0;
|
||||||
|
computePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private int readData() {
|
||||||
|
if (status != Status.READ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int data = readBuffer[readPosition++];
|
||||||
|
|
||||||
|
if (readPosition >= SECTOR_SIZE) {
|
||||||
|
this.status = Status.IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeData(int data) {
|
||||||
|
if (status != Status.WRITE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
writeBuffer[writePosition++] = (byte) data;
|
||||||
|
|
||||||
|
if (writePosition >= SECTOR_SIZE) {
|
||||||
|
if (sdImageFile != null) {
|
||||||
|
try {
|
||||||
|
RandomAccessFile raf = new RandomAccessFile(sdImageFile, "rw");
|
||||||
|
raf.skipBytes(this.position);
|
||||||
|
raf.write(writeBuffer, 0, writeBuffer.length);
|
||||||
|
raf.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "could not write data back to SD image file!", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.status = Status.IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private int readStatus() {
|
||||||
|
switch (this.status) {
|
||||||
|
case IDLE:
|
||||||
|
return 128;
|
||||||
|
case READ:
|
||||||
|
return 224;
|
||||||
|
case WRITE:
|
||||||
|
return 160;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void writeCommand(int data) {
|
||||||
|
switch (data) {
|
||||||
|
case 0:
|
||||||
|
prepareRead();
|
||||||
|
return;
|
||||||
|
case 1:
|
||||||
|
prepareWrite();
|
||||||
|
return;
|
||||||
|
default:
|
||||||
|
this.status = Status.IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getName() + "@" + String.format("%04X", this.getMemoryRange().startAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.loomcom.symon.devices;
|
||||||
|
|
||||||
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Very basic implementation of a MOS 6522 VIA.
|
||||||
|
*
|
||||||
|
* TODO: Implement timers as threads.
|
||||||
|
*/
|
||||||
|
public class Via6522 extends Pia {
|
||||||
|
public static final int VIA_SIZE = 16;
|
||||||
|
|
||||||
|
enum Register {
|
||||||
|
ORB, ORA, DDRB, DDRA, T1C_L, T1C_H, T1L_L, T1L_H,
|
||||||
|
T2C_L, T2C_H, SR, ACR, PCR, IFR, IER, ORA_H
|
||||||
|
}
|
||||||
|
|
||||||
|
public Via6522(int address) throws MemoryRangeException {
|
||||||
|
super(address, address + VIA_SIZE - 1, "MOS 6522 VIA");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(int address, int data) throws MemoryAccessException {
|
||||||
|
Register[] registers = Register.values();
|
||||||
|
|
||||||
|
if (address >= registers.length) {
|
||||||
|
throw new MemoryAccessException("Unknown register: " + address);
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r = registers[address];
|
||||||
|
|
||||||
|
switch (r) {
|
||||||
|
case ORA:
|
||||||
|
case ORB:
|
||||||
|
case DDRA:
|
||||||
|
case DDRB:
|
||||||
|
case T1C_L:
|
||||||
|
case T1C_H:
|
||||||
|
case T1L_L:
|
||||||
|
case T1L_H:
|
||||||
|
case T2C_L:
|
||||||
|
case T2C_H:
|
||||||
|
case SR:
|
||||||
|
case ACR:
|
||||||
|
case PCR:
|
||||||
|
case IFR:
|
||||||
|
case IER:
|
||||||
|
case ORA_H:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(int address) throws MemoryAccessException {
|
||||||
|
Register[] registers = Register.values();
|
||||||
|
|
||||||
|
if (address >= registers.length) {
|
||||||
|
throw new MemoryAccessException("Unknown register: " + address);
|
||||||
|
}
|
||||||
|
|
||||||
|
Register r = registers[address];
|
||||||
|
|
||||||
|
switch (r) {
|
||||||
|
case ORA:
|
||||||
|
case ORB:
|
||||||
|
case DDRA:
|
||||||
|
case DDRB:
|
||||||
|
case T1C_L:
|
||||||
|
case T1C_H:
|
||||||
|
case T1L_L:
|
||||||
|
case T1L_H:
|
||||||
|
case T2C_L:
|
||||||
|
case T2C_H:
|
||||||
|
case SR:
|
||||||
|
case ACR:
|
||||||
|
case PCR:
|
||||||
|
case IFR:
|
||||||
|
case IER:
|
||||||
|
case ORA_H:
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -31,7 +31,4 @@ public class SymonException extends Exception {
|
|||||||
public SymonException(String msg) {
|
public SymonException(String msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
public SymonException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
* Maik Merten <maikmerten@googlemail.com>
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
@@ -29,33 +29,33 @@ import com.loomcom.symon.Cpu;
|
|||||||
import com.loomcom.symon.devices.Acia;
|
import com.loomcom.symon.devices.Acia;
|
||||||
import com.loomcom.symon.devices.Crtc;
|
import com.loomcom.symon.devices.Crtc;
|
||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
import com.loomcom.symon.devices.Via;
|
import com.loomcom.symon.devices.Pia;
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
|
|
||||||
public interface Machine {
|
public interface Machine {
|
||||||
|
|
||||||
public Bus getBus();
|
Bus getBus();
|
||||||
|
|
||||||
public Cpu getCpu();
|
Cpu getCpu();
|
||||||
|
|
||||||
public Memory getRam();
|
Memory getRam();
|
||||||
|
|
||||||
public Acia getAcia();
|
Acia getAcia();
|
||||||
|
|
||||||
public Via getVia();
|
Pia getPia();
|
||||||
|
|
||||||
public Crtc getCrtc();
|
Crtc getCrtc();
|
||||||
|
|
||||||
public Memory getRom();
|
Memory getRom();
|
||||||
|
|
||||||
public void setRom(Memory rom) throws MemoryRangeException;
|
void setRom(Memory rom) throws MemoryRangeException;
|
||||||
|
|
||||||
public int getRomBase();
|
int getRomBase();
|
||||||
|
|
||||||
public int getRomSize();
|
int getRomSize();
|
||||||
|
|
||||||
public int getMemorySize();
|
int getMemorySize();
|
||||||
|
|
||||||
String getName();
|
String getName();
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
* Maik Merten <maikmerten@googlemail.com>
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
@@ -30,7 +30,8 @@ import com.loomcom.symon.devices.Acia;
|
|||||||
import com.loomcom.symon.devices.Acia6850;
|
import com.loomcom.symon.devices.Acia6850;
|
||||||
import com.loomcom.symon.devices.Crtc;
|
import com.loomcom.symon.devices.Crtc;
|
||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
import com.loomcom.symon.devices.Via;
|
import com.loomcom.symon.devices.Pia;
|
||||||
|
import com.loomcom.symon.devices.SdController;
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -51,6 +52,8 @@ public class MulticompMachine implements Machine {
|
|||||||
// ACIA at $FFD0-$FFD1
|
// ACIA at $FFD0-$FFD1
|
||||||
private static final int ACIA_BASE = 0xFFD0;
|
private static final int ACIA_BASE = 0xFFD0;
|
||||||
|
|
||||||
|
// SD controller at $FFD8-$FFDF
|
||||||
|
private static final int SD_BASE = 0xFFD8;
|
||||||
|
|
||||||
// 8KB ROM at $E000-$FFFF
|
// 8KB ROM at $E000-$FFFF
|
||||||
private static final int ROM_BASE = 0xE000;
|
private static final int ROM_BASE = 0xE000;
|
||||||
@@ -70,10 +73,12 @@ public class MulticompMachine implements Machine {
|
|||||||
this.cpu = new Cpu();
|
this.cpu = new Cpu();
|
||||||
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
|
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
|
||||||
this.acia = new Acia6850(ACIA_BASE);
|
this.acia = new Acia6850(ACIA_BASE);
|
||||||
|
this.acia.setBaudRate(0);
|
||||||
|
|
||||||
bus.addCpu(cpu);
|
bus.addCpu(cpu);
|
||||||
bus.addDevice(ram);
|
bus.addDevice(ram);
|
||||||
bus.addDevice(acia, 1);
|
bus.addDevice(acia, 1);
|
||||||
|
bus.addDevice(new SdController(SD_BASE), 1);
|
||||||
|
|
||||||
// TODO: Make this configurable, of course.
|
// TODO: Make this configurable, of course.
|
||||||
File romImage = new File("rom.bin");
|
File romImage = new File("rom.bin");
|
||||||
@@ -111,7 +116,7 @@ public class MulticompMachine implements Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Via getVia() {
|
public Pia getPia() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
package com.loomcom.symon.machines;
|
package com.loomcom.symon.machines;
|
||||||
|
|
||||||
import com.loomcom.symon.Bus;
|
import com.loomcom.symon.Bus;
|
||||||
@@ -5,7 +29,7 @@ import com.loomcom.symon.Cpu;
|
|||||||
import com.loomcom.symon.devices.Acia;
|
import com.loomcom.symon.devices.Acia;
|
||||||
import com.loomcom.symon.devices.Crtc;
|
import com.loomcom.symon.devices.Crtc;
|
||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
import com.loomcom.symon.devices.Via;
|
import com.loomcom.symon.devices.Pia;
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -52,7 +76,7 @@ public class SimpleMachine implements Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Via getVia() {
|
public Pia getPia() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
* Maik Merten <maikmerten@googlemail.com>
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
@@ -26,19 +26,16 @@ package com.loomcom.symon.machines;
|
|||||||
|
|
||||||
import com.loomcom.symon.Bus;
|
import com.loomcom.symon.Bus;
|
||||||
import com.loomcom.symon.Cpu;
|
import com.loomcom.symon.Cpu;
|
||||||
import com.loomcom.symon.devices.Acia;
|
import com.loomcom.symon.devices.*;
|
||||||
import com.loomcom.symon.devices.Acia6551;
|
|
||||||
import com.loomcom.symon.devices.Crtc;
|
|
||||||
import com.loomcom.symon.devices.Memory;
|
|
||||||
import com.loomcom.symon.devices.Via;
|
|
||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
import java.io.File;
|
import org.slf4j.Logger;
|
||||||
import java.util.logging.Logger;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public class SymonMachine implements Machine {
|
public class SymonMachine implements Machine {
|
||||||
|
|
||||||
private final static Logger logger = Logger.getLogger(SymonMachine.class.getName());
|
private final static Logger logger = LoggerFactory.getLogger(SymonMachine.class.getName());
|
||||||
|
|
||||||
// Constants used by the simulated system. These define the memory map.
|
// Constants used by the simulated system. These define the memory map.
|
||||||
private static final int BUS_BOTTOM = 0x0000;
|
private static final int BUS_BOTTOM = 0x0000;
|
||||||
@@ -48,16 +45,15 @@ public class SymonMachine implements Machine {
|
|||||||
private static final int MEMORY_BASE = 0x0000;
|
private static final int MEMORY_BASE = 0x0000;
|
||||||
private static final int MEMORY_SIZE = 0x8000;
|
private static final int MEMORY_SIZE = 0x8000;
|
||||||
|
|
||||||
// VIA at $8000-$800F
|
// PIA at $8000-$800F
|
||||||
|
|
||||||
private static final int VIA_BASE = 0x8000;
|
private static final int PIA_BASE = 0x8000;
|
||||||
|
|
||||||
// ACIA at $8800-$8803
|
// ACIA at $8800-$8803
|
||||||
private static final int ACIA_BASE = 0x8800;
|
private static final int ACIA_BASE = 0x8800;
|
||||||
|
|
||||||
// CRTC at $9000-$9001
|
// CRTC at $9000-$9001
|
||||||
private static final int CRTC_BASE = 0x9000;
|
private static final int CRTC_BASE = 0x9000;
|
||||||
private static final int VIDEO_RAM_BASE = 0x7000;
|
|
||||||
|
|
||||||
// 16KB ROM at $C000-$FFFF
|
// 16KB ROM at $C000-$FFFF
|
||||||
private static final int ROM_BASE = 0xC000;
|
private static final int ROM_BASE = 0xC000;
|
||||||
@@ -68,7 +64,7 @@ public class SymonMachine implements Machine {
|
|||||||
private final Bus bus;
|
private final Bus bus;
|
||||||
private final Cpu cpu;
|
private final Cpu cpu;
|
||||||
private final Acia acia;
|
private final Acia acia;
|
||||||
private final Via via;
|
private final Pia pia;
|
||||||
private final Crtc crtc;
|
private final Crtc crtc;
|
||||||
private final Memory ram;
|
private final Memory ram;
|
||||||
private Memory rom;
|
private Memory rom;
|
||||||
@@ -78,24 +74,23 @@ public class SymonMachine implements Machine {
|
|||||||
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
|
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
|
||||||
this.cpu = new Cpu();
|
this.cpu = new Cpu();
|
||||||
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
|
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
|
||||||
this.via = new Via(VIA_BASE);
|
this.pia = new Via6522(PIA_BASE);
|
||||||
this.acia = new Acia6551(ACIA_BASE);
|
this.acia = new Acia6551(ACIA_BASE);
|
||||||
this.crtc = new Crtc(CRTC_BASE, ram);
|
this.crtc = new Crtc(CRTC_BASE, ram);
|
||||||
|
|
||||||
bus.addCpu(cpu);
|
bus.addCpu(cpu);
|
||||||
bus.addDevice(ram);
|
bus.addDevice(ram);
|
||||||
bus.addDevice(via);
|
bus.addDevice(pia);
|
||||||
bus.addDevice(acia);
|
bus.addDevice(acia);
|
||||||
bus.addDevice(crtc);
|
bus.addDevice(crtc);
|
||||||
|
|
||||||
// TODO: Make this configurable, of course.
|
// TODO: Make this configurable, of course.
|
||||||
File romImage = new File("rom.bin");
|
File romImage = new File("rom.bin");
|
||||||
if (romImage.canRead()) {
|
if (romImage.canRead()) {
|
||||||
logger.info("Loading ROM image from file " + romImage);
|
logger.info("Loading ROM image from file {}", romImage);
|
||||||
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
|
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
|
||||||
} else {
|
} else {
|
||||||
logger.info("Default ROM file " + romImage +
|
logger.info("Default ROM file {} not found, loading empty R/W memory image.", romImage);
|
||||||
" not found, loading empty R/W memory image.");
|
|
||||||
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
|
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,8 +119,8 @@ public class SymonMachine implements Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Via getVia() {
|
public Pia getPia() {
|
||||||
return via;
|
return pia;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.loomcom.symon.ui;
|
||||||
|
|
||||||
|
import com.loomcom.symon.Breakpoints;
|
||||||
|
import com.loomcom.symon.util.Utils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.EmptyBorder;
|
||||||
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
import javax.swing.event.ListSelectionListener;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple window to enter breakpoints.
|
||||||
|
*/
|
||||||
|
public class BreakpointsWindow extends JFrame {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(BreakpointsWindow.class);
|
||||||
|
|
||||||
|
private static final Dimension FRAME_SIZE = new Dimension(240, 280);
|
||||||
|
private static final String EMPTY_STRING = "";
|
||||||
|
|
||||||
|
private JFrame mainWindow;
|
||||||
|
private Breakpoints breakpoints;
|
||||||
|
|
||||||
|
public BreakpointsWindow(Breakpoints breakpoints,
|
||||||
|
JFrame mainWindow) {
|
||||||
|
this.breakpoints = breakpoints;
|
||||||
|
this.mainWindow = mainWindow;
|
||||||
|
createUi();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createUi() {
|
||||||
|
setTitle("Breakpoints");
|
||||||
|
|
||||||
|
JPanel breakpointsPanel = new JPanel();
|
||||||
|
JPanel controlPanel = new JPanel();
|
||||||
|
|
||||||
|
breakpointsPanel.setLayout(new BorderLayout());
|
||||||
|
breakpointsPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
final JButton addButton = new JButton("Add");
|
||||||
|
final JButton removeButton = new JButton("Del");
|
||||||
|
removeButton.setEnabled(false);
|
||||||
|
|
||||||
|
final JTextField addTextField = new JTextField(4);
|
||||||
|
|
||||||
|
final JTable breakpointsTable = new JTable(breakpoints);
|
||||||
|
breakpointsTable.setShowGrid(true);
|
||||||
|
breakpointsTable.setGridColor(Color.LIGHT_GRAY);
|
||||||
|
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
breakpointsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||||
|
@Override
|
||||||
|
public void valueChanged(ListSelectionEvent e) {
|
||||||
|
if (e.getFirstIndex() > -1) {
|
||||||
|
removeButton.setEnabled(true);
|
||||||
|
} else {
|
||||||
|
removeButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
JScrollPane scrollPane = new JScrollPane(breakpointsTable);
|
||||||
|
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
|
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||||
|
|
||||||
|
breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
ActionListener addBreakpointListener = new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
int value;
|
||||||
|
|
||||||
|
String newBreakpoint = addTextField.getText();
|
||||||
|
|
||||||
|
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
logger.warn("Can't parse page number {}", newBreakpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
breakpoints.addBreakpoint(value);
|
||||||
|
|
||||||
|
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
|
||||||
|
|
||||||
|
addTextField.setText(EMPTY_STRING);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
addButton.addActionListener(addBreakpointListener);
|
||||||
|
addTextField.addActionListener(addBreakpointListener);
|
||||||
|
|
||||||
|
removeButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
controlPanel.add(addTextField);
|
||||||
|
controlPanel.add(addButton);
|
||||||
|
controlPanel.add(removeButton);
|
||||||
|
|
||||||
|
setLayout(new BorderLayout());
|
||||||
|
getContentPane().add(breakpointsPanel, BorderLayout.CENTER);
|
||||||
|
getContentPane().add(controlPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
setMinimumSize(FRAME_SIZE);
|
||||||
|
setMaximumSize(FRAME_SIZE);
|
||||||
|
setPreferredSize(FRAME_SIZE);
|
||||||
|
|
||||||
|
setLocationRelativeTo(mainWindow);
|
||||||
|
setResizable(false);
|
||||||
|
|
||||||
|
pack();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -44,21 +44,20 @@ import java.awt.event.MouseListener;
|
|||||||
|
|
||||||
public class Console extends JTerminal implements KeyListener, MouseListener {
|
public class Console extends JTerminal implements KeyListener, MouseListener {
|
||||||
|
|
||||||
private static final int DEFAULT_COLUMNS = 80;
|
|
||||||
private static final int DEFAULT_ROWS = 24;
|
|
||||||
private static final int DEFAULT_BORDER_WIDTH = 10;
|
private static final int DEFAULT_BORDER_WIDTH = 10;
|
||||||
// If true, swap CR and LF characters.
|
// If true, swap CR and LF characters.
|
||||||
private static final boolean SWAP_CR_AND_LF = true;
|
private static final boolean SWAP_CR_AND_LF = true;
|
||||||
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
|
|
||||||
private static final boolean SEND_CR_LF_FOR_CR = false;
|
|
||||||
|
|
||||||
|
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
|
||||||
|
private boolean sendCrForLf = false;
|
||||||
private FifoRingBuffer<Character> typeAheadBuffer;
|
private FifoRingBuffer<Character> typeAheadBuffer;
|
||||||
|
|
||||||
public Console(int columns, int rows, Font font) {
|
public Console(int columns, int rows, Font font, boolean sendCrForLf) {
|
||||||
super(new Vt100TerminalModel(columns, rows), font);
|
super(new Vt100TerminalModel(columns, rows), font);
|
||||||
// A small type-ahead buffer, as might be found in any real
|
// A small type-ahead buffer, as might be found in any real
|
||||||
// VT100-style serial terminal.
|
// VT100-style serial terminal.
|
||||||
this.typeAheadBuffer = new FifoRingBuffer<Character>(128);
|
this.typeAheadBuffer = new FifoRingBuffer<>(128);
|
||||||
|
this.sendCrForLf = sendCrForLf;
|
||||||
setBorderWidth(DEFAULT_BORDER_WIDTH);
|
setBorderWidth(DEFAULT_BORDER_WIDTH);
|
||||||
addKeyListener(this);
|
addKeyListener(this);
|
||||||
addMouseListener(this);
|
addMouseListener(this);
|
||||||
@@ -83,7 +82,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
|
|||||||
/**
|
/**
|
||||||
* Returns true if a key has been pressed since the last time input was read.
|
* Returns true if a key has been pressed since the last time input was read.
|
||||||
*
|
*
|
||||||
* @return
|
* @return true if the type-ahead buffer has data
|
||||||
*/
|
*/
|
||||||
public boolean hasInput() {
|
public boolean hasInput() {
|
||||||
return !typeAheadBuffer.isEmpty();
|
return !typeAheadBuffer.isEmpty();
|
||||||
@@ -105,7 +104,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) {
|
if (sendCrForLf && keyTyped == 0x0d) {
|
||||||
typeAheadBuffer.push((char) 0x0d);
|
typeAheadBuffer.push((char) 0x0d);
|
||||||
typeAheadBuffer.push((char) 0x0a);
|
typeAheadBuffer.push((char) 0x0a);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -25,7 +25,9 @@ package com.loomcom.symon.ui;
|
|||||||
|
|
||||||
import com.loomcom.symon.Bus;
|
import com.loomcom.symon.Bus;
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
import com.loomcom.symon.util.HexUtil;
|
import com.loomcom.symon.util.Utils;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
@@ -37,8 +39,7 @@ import java.awt.*;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.EventObject;
|
import java.util.EventObject;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This Frame displays the contents of a page of memory. The page number to be displayed
|
* This Frame displays the contents of a page of memory. The page number to be displayed
|
||||||
@@ -46,6 +47,8 @@ import java.util.logging.Logger;
|
|||||||
*/
|
*/
|
||||||
public class MemoryWindow extends JFrame implements ActionListener {
|
public class MemoryWindow extends JFrame implements ActionListener {
|
||||||
|
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(MemoryWindow.class);
|
||||||
|
|
||||||
private MemoryTableModel memoryTableModel;
|
private MemoryTableModel memoryTableModel;
|
||||||
private JTable memoryTable;
|
private JTable memoryTable;
|
||||||
private JTextField pageNumberTextField;
|
private JTextField pageNumberTextField;
|
||||||
@@ -104,7 +107,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
|
|
||||||
previousPageButton.setEnabled(pageNumber > 0x00);
|
previousPageButton.setEnabled(pageNumber > 0x00);
|
||||||
nextPageButton.setEnabled(pageNumber < 0xff);
|
nextPageButton.setEnabled(pageNumber < 0xff);
|
||||||
pageNumberTextField.setText(HexUtil.byteToHex(pageNumber));
|
pageNumberTextField.setText(Utils.byteToHex(pageNumber));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,9 +212,9 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
// An invalid number was entered. Log the error, but otherwise
|
// An invalid number was entered. Log the error, but otherwise
|
||||||
// take no action.
|
// take no action.
|
||||||
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Can't parse page number " +
|
logger.warn("Can't parse page number {}", pageNumberInput);
|
||||||
pageNumberInput);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateControls();
|
updateControls();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,13 +328,13 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
public Object getValueAt(int row, int column) {
|
public Object getValueAt(int row, int column) {
|
||||||
try {
|
try {
|
||||||
if (column == 0) {
|
if (column == 0) {
|
||||||
return HexUtil.wordToHex(fullAddress(row, 1));
|
return Utils.wordToHex(fullAddress(row, 1));
|
||||||
} else if (column < 9) {
|
} else if (column < 9) {
|
||||||
// Display hex value of the data
|
// Display hex value of the data
|
||||||
return HexUtil.byteToHex(bus.read(fullAddress(row, column)));
|
return Utils.byteToHex(bus.read(fullAddress(row, column)));
|
||||||
} else {
|
} else {
|
||||||
// Display the ASCII equivalent (if printable)
|
// Display the ASCII equivalent (if printable)
|
||||||
return HexUtil.byteToAscii(bus.read(fullAddress(row, column - 8)));
|
return Utils.byteToAscii(bus.read(fullAddress(row, column - 8)));
|
||||||
}
|
}
|
||||||
} catch (MemoryAccessException ex) {
|
} catch (MemoryAccessException ex) {
|
||||||
return "??";
|
return "??";
|
||||||
@@ -346,12 +349,8 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
int fullAddress = fullAddress(row, column);
|
int fullAddress = fullAddress(row, column);
|
||||||
int newValue = Integer.parseInt(hexValue, 16) & 0xff;
|
int newValue = Integer.parseInt(hexValue, 16) & 0xff;
|
||||||
bus.write(fullAddress, newValue);
|
bus.write(fullAddress, newValue);
|
||||||
} catch (MemoryAccessException ex) {
|
} catch (MemoryAccessException | NumberFormatException | ClassCastException ex) {
|
||||||
;
|
// Intentionally swallow exception
|
||||||
} catch (NumberFormatException ex) {
|
|
||||||
;
|
|
||||||
} catch (ClassCastException ex) {
|
|
||||||
;
|
|
||||||
}
|
}
|
||||||
fireTableCellUpdated(row, column);
|
fireTableCellUpdated(row, column);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -98,19 +98,21 @@ public class PreferencesDialog extends Observable implements Preferences {
|
|||||||
JButton cancelButton = new JButton("Cancel");
|
JButton cancelButton = new JButton("Cancel");
|
||||||
|
|
||||||
cancelButton.addActionListener(new ActionListener() {
|
cancelButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
updateUi();
|
PreferencesDialog.this.updateUi();
|
||||||
dialog.setVisible(false);
|
dialog.setVisible(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
applyButton.addActionListener(new ActionListener() {
|
applyButton.addActionListener(new ActionListener() {
|
||||||
|
@Override
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
haltOnBreak = haltOnBreakCheckBox.isSelected();
|
haltOnBreak = haltOnBreakCheckBox.isSelected();
|
||||||
programLoadAddress = hexToInt(programLoadAddressField.getText());
|
programLoadAddress = PreferencesDialog.this.hexToInt(programLoadAddressField.getText());
|
||||||
updateUi();
|
PreferencesDialog.this.updateUi();
|
||||||
// TODO: Actually check to see if values have changed, don't assume.
|
// TODO: Actually check to see if values have changed, don't assume.
|
||||||
setChanged();
|
PreferencesDialog.this.setChanged();
|
||||||
PreferencesDialog.this.notifyObservers();
|
PreferencesDialog.this.notifyObservers();
|
||||||
dialog.setVisible(false);
|
dialog.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
package com.loomcom.symon.ui;
|
package com.loomcom.symon.ui;
|
||||||
|
|
||||||
import com.loomcom.symon.Cpu;
|
import com.loomcom.symon.Cpu;
|
||||||
|
import com.loomcom.symon.CpuState;
|
||||||
import com.loomcom.symon.machines.Machine;
|
import com.loomcom.symon.machines.Machine;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@@ -32,9 +33,6 @@ import javax.swing.border.EtchedBorder;
|
|||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UI component that displays the current state of the simulated CPU.
|
* UI component that displays the current state of the simulated CPU.
|
||||||
@@ -56,7 +54,6 @@ public class StatusPanel extends JPanel {
|
|||||||
private ImageIcon negativeOn;
|
private ImageIcon negativeOn;
|
||||||
private ImageIcon negativeOff;
|
private ImageIcon negativeOff;
|
||||||
|
|
||||||
private JLabel statusFlagsLabel;
|
|
||||||
private JLabel carryFlagLabel;
|
private JLabel carryFlagLabel;
|
||||||
private JLabel zeroFlagLabel;
|
private JLabel zeroFlagLabel;
|
||||||
private JLabel irqDisableFlagLabel;
|
private JLabel irqDisableFlagLabel;
|
||||||
@@ -72,13 +69,6 @@ public class StatusPanel extends JPanel {
|
|||||||
private JTextField xField;
|
private JTextField xField;
|
||||||
private JTextField yField;
|
private JTextField yField;
|
||||||
|
|
||||||
private JLabel opcodeLabel;
|
|
||||||
private JLabel pcLabel;
|
|
||||||
private JLabel spLabel;
|
|
||||||
private JLabel aLabel;
|
|
||||||
private JLabel xLabel;
|
|
||||||
private JLabel yLabel;
|
|
||||||
|
|
||||||
private Machine machine;
|
private Machine machine;
|
||||||
|
|
||||||
private static final int EMPTY_BORDER = 10;
|
private static final int EMPTY_BORDER = 10;
|
||||||
@@ -153,13 +143,13 @@ public class StatusPanel extends JPanel {
|
|||||||
statusFlagsPanel.add(carryFlagLabel);
|
statusFlagsPanel.add(carryFlagLabel);
|
||||||
|
|
||||||
// Create and add register and address labels
|
// Create and add register and address labels
|
||||||
statusFlagsLabel = makeLabel("Flags");
|
JLabel statusFlagsLabel = makeLabel("Flags");
|
||||||
opcodeLabel = makeLabel("IR");
|
JLabel opcodeLabel = makeLabel("Next IR");
|
||||||
pcLabel = makeLabel("PC");
|
JLabel pcLabel = makeLabel("PC");
|
||||||
spLabel = makeLabel("SP");
|
JLabel spLabel = makeLabel("SP");
|
||||||
aLabel = makeLabel("A");
|
JLabel aLabel = makeLabel("A");
|
||||||
xLabel = makeLabel("X");
|
JLabel xLabel = makeLabel("X");
|
||||||
yLabel = makeLabel("Y");
|
JLabel yLabel = makeLabel("Y");
|
||||||
|
|
||||||
statusFlagsLabel.setToolTipText("6502 Processor Status Flags");
|
statusFlagsLabel.setToolTipText("6502 Processor Status Flags");
|
||||||
opcodeLabel.setToolTipText("Instruction Register");
|
opcodeLabel.setToolTipText("Instruction Register");
|
||||||
@@ -178,13 +168,13 @@ public class StatusPanel extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
try {
|
try {
|
||||||
int newVal = getHexVal(pcField) & 0xffff;
|
int newVal = StatusPanel.this.getHexVal(pcField) & 0xffff;
|
||||||
machine.getCpu().setProgramCounter(newVal);
|
machine.getCpu().setProgramCounter(newVal);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Swallow exception
|
// Swallow exception
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState();
|
StatusPanel.this.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -192,13 +182,13 @@ public class StatusPanel extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
try {
|
try {
|
||||||
int newVal = getHexVal(spField) & 0xff;
|
int newVal = StatusPanel.this.getHexVal(spField) & 0xff;
|
||||||
machine.getCpu().setStackPointer(newVal);
|
machine.getCpu().setStackPointer(newVal);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Swallow exception
|
// Swallow exception
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState();
|
StatusPanel.this.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -206,13 +196,13 @@ public class StatusPanel extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
try {
|
try {
|
||||||
int newVal = getHexVal(aField) & 0xff;
|
int newVal = StatusPanel.this.getHexVal(aField) & 0xff;
|
||||||
machine.getCpu().setAccumulator(newVal);
|
machine.getCpu().setAccumulator(newVal);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Swallow exception
|
// Swallow exception
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState();
|
StatusPanel.this.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -220,13 +210,13 @@ public class StatusPanel extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
try {
|
try {
|
||||||
int newVal = getHexVal(xField) & 0xff;
|
int newVal = StatusPanel.this.getHexVal(xField) & 0xff;
|
||||||
machine.getCpu().setXRegister(newVal);
|
machine.getCpu().setXRegister(newVal);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Swallow exception
|
// Swallow exception
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState();
|
StatusPanel.this.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -234,13 +224,13 @@ public class StatusPanel extends JPanel {
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
try {
|
try {
|
||||||
int newVal = getHexVal(yField) & 0xff;
|
int newVal = StatusPanel.this.getHexVal(yField) & 0xff;
|
||||||
machine.getCpu().setYRegister(newVal);
|
machine.getCpu().setYRegister(newVal);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
// Swallow exception
|
// Swallow exception
|
||||||
}
|
}
|
||||||
|
|
||||||
updateState();
|
StatusPanel.this.updateState();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -303,7 +293,7 @@ public class StatusPanel extends JPanel {
|
|||||||
*/
|
*/
|
||||||
public void updateState() {
|
public void updateState() {
|
||||||
Cpu cpu = machine.getCpu();
|
Cpu cpu = machine.getCpu();
|
||||||
Cpu.CpuState cpuState = cpu.getCpuState();
|
CpuState cpuState = cpu.getCpuState();
|
||||||
|
|
||||||
// Update the Processor Status Flag display
|
// Update the Processor Status Flag display
|
||||||
int status = cpuState.getStatusFlag();
|
int status = cpuState.getStatusFlag();
|
||||||
@@ -317,7 +307,9 @@ public class StatusPanel extends JPanel {
|
|||||||
negativeFlagLabel.setIcon(iconForFlag(status, 7));
|
negativeFlagLabel.setIcon(iconForFlag(status, 7));
|
||||||
|
|
||||||
// Update the register and address displays
|
// Update the register and address displays
|
||||||
opcodeField.setText(cpu.getCpuState().disassembleOp());
|
|
||||||
|
// We always want to show the NEXT instruction that will be executed
|
||||||
|
opcodeField.setText(cpu.disassembleNextOp());
|
||||||
pcField.setText(cpu.getProgramCounterStatus());
|
pcField.setText(cpu.getProgramCounterStatus());
|
||||||
spField.setText(cpu.getStackPointerStatus());
|
spField.setText(cpu.getStackPointerStatus());
|
||||||
aField.setText(cpu.getAccumulatorStatus());
|
aField.setText(cpu.getAccumulatorStatus());
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -23,7 +24,7 @@
|
|||||||
|
|
||||||
package com.loomcom.symon.ui;
|
package com.loomcom.symon.ui;
|
||||||
|
|
||||||
import com.loomcom.symon.Cpu;
|
import com.loomcom.symon.CpuState;
|
||||||
import com.loomcom.symon.util.FifoRingBuffer;
|
import com.loomcom.symon.util.FifoRingBuffer;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
@@ -35,15 +36,15 @@ import java.awt.*;
|
|||||||
*/
|
*/
|
||||||
public class TraceLog extends JFrame {
|
public class TraceLog extends JFrame {
|
||||||
|
|
||||||
private FifoRingBuffer<Cpu.CpuState> traceLog;
|
private final FifoRingBuffer<CpuState> traceLog;
|
||||||
private JTextArea traceLogTextArea;
|
private final JTextArea traceLogTextArea;
|
||||||
|
|
||||||
private static final Dimension MIN_SIZE = new Dimension(320, 200);
|
private static final Dimension MIN_SIZE = new Dimension(320, 200);
|
||||||
private static final Dimension PREFERRED_SIZE = new Dimension(640, 480);
|
private static final Dimension PREFERRED_SIZE = new Dimension(640, 480);
|
||||||
private static final int MAX_LOG_LENGTH = 50000;
|
private static final int MAX_LOG_LENGTH = 50000;
|
||||||
|
|
||||||
public TraceLog() {
|
public TraceLog() {
|
||||||
traceLog = new FifoRingBuffer<Cpu.CpuState>(MAX_LOG_LENGTH);
|
traceLog = new FifoRingBuffer<>(MAX_LOG_LENGTH);
|
||||||
setMinimumSize(MIN_SIZE);
|
setMinimumSize(MIN_SIZE);
|
||||||
setPreferredSize(PREFERRED_SIZE);
|
setPreferredSize(PREFERRED_SIZE);
|
||||||
setResizable(true);
|
setResizable(true);
|
||||||
@@ -67,11 +68,15 @@ public class TraceLog extends JFrame {
|
|||||||
* call.
|
* call.
|
||||||
*/
|
*/
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
synchronized (this) {
|
|
||||||
StringBuilder logString = new StringBuilder();
|
StringBuilder logString = new StringBuilder();
|
||||||
for (Cpu.CpuState state : traceLog) {
|
|
||||||
|
synchronized(traceLog) {
|
||||||
|
for (CpuState state : traceLog) {
|
||||||
logString.append(state.toTraceEvent());
|
logString.append(state.toTraceEvent());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(traceLogTextArea) {
|
||||||
traceLogTextArea.setText(logString.toString());
|
traceLogTextArea.setText(logString.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,8 +85,10 @@ public class TraceLog extends JFrame {
|
|||||||
* Reset the log area.
|
* Reset the log area.
|
||||||
*/
|
*/
|
||||||
public void reset() {
|
public void reset() {
|
||||||
synchronized (this) {
|
synchronized(traceLog) {
|
||||||
traceLog.reset();
|
traceLog.reset();
|
||||||
|
}
|
||||||
|
synchronized(traceLogTextArea) {
|
||||||
traceLogTextArea.setText("");
|
traceLogTextArea.setText("");
|
||||||
traceLogTextArea.setEnabled(true);
|
traceLogTextArea.setEnabled(true);
|
||||||
}
|
}
|
||||||
@@ -92,9 +99,9 @@ public class TraceLog extends JFrame {
|
|||||||
*
|
*
|
||||||
* @param state The CPU State to append.
|
* @param state The CPU State to append.
|
||||||
*/
|
*/
|
||||||
public void append(Cpu.CpuState state) {
|
public void append(CpuState state) {
|
||||||
synchronized(this) {
|
synchronized(traceLog) {
|
||||||
traceLog.push(new Cpu.CpuState(state));
|
traceLog.push(new CpuState(state));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,4 +113,8 @@ public class TraceLog extends JFrame {
|
|||||||
traceLogTextArea.setEnabled(true);
|
traceLogTextArea.setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean shouldUpdate() {
|
||||||
|
return isVisible() && traceLogTextArea.isEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -25,6 +25,7 @@ package com.loomcom.symon.ui;
|
|||||||
|
|
||||||
import com.loomcom.symon.devices.Crtc;
|
import com.loomcom.symon.devices.Crtc;
|
||||||
import com.loomcom.symon.devices.DeviceChangeListener;
|
import com.loomcom.symon.devices.DeviceChangeListener;
|
||||||
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
@@ -36,8 +37,11 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import static java.lang.System.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* VideoWindow represents a graphics framebuffer backed by a 6545 CRTC.
|
* VideoWindow represents a graphics framebuffer backed by a 6545 CRTC.
|
||||||
* Each time the window's VideoPanel is repainted, the video memory is
|
* Each time the window's VideoPanel is repainted, the video memory is
|
||||||
@@ -65,7 +69,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
|
|
||||||
private BufferedImage image;
|
private BufferedImage image;
|
||||||
private int[] charRom;
|
private int[] charRom;
|
||||||
private int[] videoRam;
|
|
||||||
|
|
||||||
private int horizontalDisplayed;
|
private int horizontalDisplayed;
|
||||||
private int verticalDisplayed;
|
private int verticalDisplayed;
|
||||||
@@ -85,6 +88,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
private class VideoPanel extends JPanel {
|
private class VideoPanel extends JPanel {
|
||||||
@Override
|
@Override
|
||||||
public void paintComponent(Graphics g) {
|
public void paintComponent(Graphics g) {
|
||||||
|
try {
|
||||||
for (int i = 0; i < crtc.getPageSize(); i++) {
|
for (int i = 0; i < crtc.getPageSize(); i++) {
|
||||||
int address = crtc.getStartAddress() + i;
|
int address = crtc.getStartAddress() + i;
|
||||||
int originX = (i % horizontalDisplayed) * CHAR_WIDTH;
|
int originX = (i % horizontalDisplayed) * CHAR_WIDTH;
|
||||||
@@ -96,6 +100,9 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
g2d.scale(scaleX, scaleY);
|
g2d.scale(scaleX, scaleY);
|
||||||
}
|
}
|
||||||
g2d.drawImage(image, 0, 0, null);
|
g2d.drawImage(image, 0, 0, null);
|
||||||
|
} catch (MemoryAccessException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Memory Access Exception, can't paint video window! " + ex.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -116,10 +123,11 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
private class CursorBlinker implements Runnable {
|
private class CursorBlinker implements Runnable {
|
||||||
public void run() {
|
public void run() {
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (cursorBlinkRate > 0) {
|
if (cursorBlinkRate > 0) {
|
||||||
hideCursor = !hideCursor;
|
hideCursor = !hideCursor;
|
||||||
repaint();
|
VideoWindow.this.repaint();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -131,8 +139,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
|
|
||||||
this.scheduler = Executors.newSingleThreadScheduledExecutor();
|
this.scheduler = Executors.newSingleThreadScheduledExecutor();
|
||||||
this.crtc = crtc;
|
this.crtc = crtc;
|
||||||
this.charRom = loadCharRom("/pet.rom");
|
this.charRom = loadCharRom("/ascii.rom");
|
||||||
this.videoRam = crtc.getDmaAccess();
|
|
||||||
this.scaleX = scaleX;
|
this.scaleX = scaleX;
|
||||||
this.scaleY = scaleY;
|
this.scaleY = scaleY;
|
||||||
this.shouldScale = (scaleX > 1 || scaleY > 1);
|
this.shouldScale = (scaleX > 1 || scaleY > 1);
|
||||||
@@ -158,13 +165,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshDisplay() {
|
|
||||||
// TODO: Verify whether this is necessary. Does `repaint()' do anything if the window is not visible?
|
|
||||||
if (isVisible()) {
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by the CRTC on state change.
|
* Called by the CRTC on state change.
|
||||||
*/
|
*/
|
||||||
@@ -239,15 +239,13 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
* @param address The address of the character being requested.
|
* @param address The address of the character being requested.
|
||||||
* @return An array of integers representing the pixel data.
|
* @return An array of integers representing the pixel data.
|
||||||
*/
|
*/
|
||||||
private int[] getGlyph(int address) {
|
private int[] getGlyph(int address) throws MemoryAccessException {
|
||||||
int chr = videoRam[address];
|
int chr = crtc.getCharAtAddress(address);
|
||||||
int romOffset = (chr & 0xff) * (CHAR_HEIGHT * CHAR_WIDTH);
|
int romOffset = (chr & 0xff) * (CHAR_HEIGHT * CHAR_WIDTH);
|
||||||
int[] glyph = new int[CHAR_WIDTH * scanLinesPerRow];
|
int[] glyph = new int[CHAR_WIDTH * scanLinesPerRow];
|
||||||
|
|
||||||
// Populate the character
|
// Populate the character
|
||||||
for (int i = 0; i < (CHAR_WIDTH * Math.min(CHAR_HEIGHT, scanLinesPerRow)); i++) {
|
arraycopy(charRom, romOffset, glyph, 0, CHAR_WIDTH * Math.min(CHAR_HEIGHT, scanLinesPerRow));
|
||||||
glyph[i] = charRom[romOffset + i];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overlay the cursor
|
// Overlay the cursor
|
||||||
if (!hideCursor && crtc.isCursorEnabled() && crtc.getCursorPosition() == address) {
|
if (!hideCursor && crtc.isCursorEnabled() && crtc.getCursorPosition() == address) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -38,7 +38,7 @@ public class FifoRingBuffer<E> implements Iterable<E> {
|
|||||||
private int maxLength;
|
private int maxLength;
|
||||||
|
|
||||||
public FifoRingBuffer(int maxLength) {
|
public FifoRingBuffer(int maxLength) {
|
||||||
this.fifoBuffer = new LinkedList<E>();
|
this.fifoBuffer = new LinkedList<>();
|
||||||
this.maxLength = maxLength;
|
this.maxLength = maxLength;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
|
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -24,24 +24,9 @@
|
|||||||
package com.loomcom.symon.util;
|
package com.loomcom.symon.util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hex String Utilities.
|
* Various Utilities
|
||||||
*
|
|
||||||
* <p/>
|
|
||||||
*
|
|
||||||
* But why? Java, after all, has a number of ways to convert an integer into a hex string,
|
|
||||||
* so it may look absurd to go to the trouble of writing yet another conversion! The answer is
|
|
||||||
* performance.
|
|
||||||
*
|
|
||||||
* <p/>
|
|
||||||
*
|
|
||||||
* The most convenient way to get a formatted hex value from an integer is with the <code>String.format</code>
|
|
||||||
* method, but this turns out to be extremely inefficient. Formatting a million integers
|
|
||||||
* with <code>String.format</code> takes something like 1600ms. Formatting the same number of integers
|
|
||||||
* with <code>HexUtil</code> takes only 160ms. This is on par with <code>Integer.toHexString</code>,
|
|
||||||
* but also gives the desired padding.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
public class HexUtil {
|
public class Utils {
|
||||||
|
|
||||||
static final String NON_PRINTABLE = ".";
|
static final String NON_PRINTABLE = ".";
|
||||||
|
|
||||||
@@ -93,6 +78,7 @@ public class HexUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Very fast 8-bit int to ASCII conversion.
|
* Very fast 8-bit int to ASCII conversion.
|
||||||
|
*
|
||||||
* @param val The value of an ASCII character.
|
* @param val The value of an ASCII character.
|
||||||
* @return A string representing the ASCII character.
|
* @return A string representing the ASCII character.
|
||||||
*/
|
*/
|
||||||
@@ -121,9 +107,13 @@ public class HexUtil {
|
|||||||
* @return Four digit, zero padded hexadecimal string.
|
* @return Four digit, zero padded hexadecimal string.
|
||||||
*/
|
*/
|
||||||
public static String wordToHex(int val) {
|
public static String wordToHex(int val) {
|
||||||
StringBuilder sb = new StringBuilder(4);
|
return HEX_CONSTANTS[(val >> 8) & 0xff] + HEX_CONSTANTS[val & 0xff];
|
||||||
sb.append(HEX_CONSTANTS[(val >> 8) & 0xff]);
|
}
|
||||||
sb.append(HEX_CONSTANTS[val & 0xff]);
|
|
||||||
return sb.toString();
|
/**
|
||||||
|
* Given two bytes, return an address.
|
||||||
|
*/
|
||||||
|
public static int address(int lowByte, int hiByte) {
|
||||||
|
return ((hiByte << 8) | lowByte) & 0xffff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -117,9 +117,22 @@ public class CpuImpliedModeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_BRK_HonorsIrqDisableFlag() throws MemoryAccessException {
|
public void test_BRK_IgnoresIrqDisableFlag() throws MemoryAccessException {
|
||||||
cpu.setIrqDisableFlag();
|
cpu.setIrqDisableFlag();
|
||||||
|
|
||||||
|
cpu.setCarryFlag();
|
||||||
|
cpu.setOverflowFlag();
|
||||||
|
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_IRQ_DISABLE,
|
||||||
|
cpu.getProcessorStatus());
|
||||||
|
assertEquals(0x00, cpu.stackPeek());
|
||||||
|
assertFalse(cpu.getBreakFlag());
|
||||||
|
assertEquals(0x0200, cpu.getProgramCounter());
|
||||||
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
|
|
||||||
|
// Set the IRQ vector
|
||||||
|
bus.write(0xffff, 0x12);
|
||||||
|
bus.write(0xfffe, 0x34);
|
||||||
|
|
||||||
bus.loadProgram(0xea,
|
bus.loadProgram(0xea,
|
||||||
0xea,
|
0xea,
|
||||||
0xea,
|
0xea,
|
||||||
@@ -131,23 +144,26 @@ public class CpuImpliedModeTest {
|
|||||||
|
|
||||||
assertEquals(0x203, cpu.getProgramCounter());
|
assertEquals(0x203, cpu.getProgramCounter());
|
||||||
|
|
||||||
// Triggers the BRK, which should do nothing because
|
// Triggers the BRK
|
||||||
// of the Interrupt Disable flag
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
|
|
||||||
// Reset to original contents of PC
|
|
||||||
assertEquals(0x0204, cpu.getProgramCounter());
|
|
||||||
// Empty stack
|
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
|
||||||
|
|
||||||
cpu.step(2); // Two more NOPs
|
// Was at PC = 0x204. PC+1 should now be on the stack
|
||||||
|
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
|
||||||
|
assertEquals(0x05, bus.read(0x1fe)); // PC low byte
|
||||||
|
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_IRQ_DISABLE,
|
||||||
|
bus.read(0x1fd)); // Processor Status, with B set
|
||||||
|
|
||||||
// Reset to original contents of PC
|
// Interrupt vector held 0x1234, so we should be there.
|
||||||
assertEquals(0x0206, cpu.getProgramCounter());
|
assertEquals(0x1234, cpu.getProgramCounter());
|
||||||
// Empty stack
|
assertEquals(0xfc, cpu.getStackPointer());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
|
||||||
|
// B and I flags should have been set on P
|
||||||
|
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_IRQ_DISABLE,
|
||||||
|
cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* CLC - Clear Carry Flag - $18 */
|
/* CLC - Clear Carry Flag - $18 */
|
||||||
@Test
|
@Test
|
||||||
public void test_CLC() throws MemoryAccessException {
|
public void test_CLC() throws MemoryAccessException {
|
||||||
|
|||||||
@@ -25,18 +25,10 @@ public class CpuIndirectIndexedModeTest {
|
|||||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||||
|
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
// Assert initial state
|
|
||||||
assertEquals(0, cpu.getAccumulator());
|
|
||||||
assertEquals(0, cpu.getXRegister());
|
|
||||||
assertEquals(0, cpu.getYRegister());
|
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_LDA() throws Exception {
|
public void test_LDA() throws Exception {
|
||||||
assertEquals(cpu.toString(), 0x00, cpu.getAccumulator());
|
|
||||||
bus.write(0x0014, 0x00);
|
bus.write(0x0014, 0x00);
|
||||||
bus.write(0x0015, 0xd8);
|
bus.write(0x0015, 0xd8);
|
||||||
bus.write(0xd828, 0x03);
|
bus.write(0xd828, 0x03);
|
||||||
|
|||||||
@@ -3,15 +3,19 @@ package com.loomcom.symon;
|
|||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class CpuIndirectModeTest extends TestCase {
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
|
public class CpuIndirectModeTest {
|
||||||
|
|
||||||
protected Cpu cpu;
|
protected Cpu cpu;
|
||||||
protected Bus bus;
|
protected Bus bus;
|
||||||
protected Memory mem;
|
protected Memory mem;
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
this.cpu = new Cpu();
|
this.cpu = new Cpu();
|
||||||
this.bus = new Bus(0x0000, 0xffff);
|
this.bus = new Bus(0x0000, 0xffff);
|
||||||
this.mem = new Memory(0x0000, 0xffff);
|
this.mem = new Memory(0x0000, 0xffff);
|
||||||
@@ -41,6 +45,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
|
|
||||||
/* JMP - Jump - $6c */
|
/* JMP - Jump - $6c */
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_JMP_notOnPageBoundary() throws MemoryAccessException {
|
public void test_JMP_notOnPageBoundary() throws MemoryAccessException {
|
||||||
bus.write(0x3400, 0x00);
|
bus.write(0x3400, 0x00);
|
||||||
bus.write(0x3401, 0x54);
|
bus.write(0x3401, 0x54);
|
||||||
@@ -51,6 +56,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x20, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
|
public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
|
||||||
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG);
|
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG);
|
||||||
bus.write(0x3400, 0x22);
|
bus.write(0x3400, 0x22);
|
||||||
@@ -63,8 +69,9 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x20, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_JMP_withIndirectBug() throws MemoryAccessException {
|
public void test_JMP_withIndirectBug() throws MemoryAccessException {
|
||||||
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG);
|
cpu.setBehavior(Cpu.CpuBehavior.NMOS_6502);
|
||||||
bus.write(0x3400, 0x22);
|
bus.write(0x3400, 0x22);
|
||||||
bus.write(0x34ff, 0x00);
|
bus.write(0x34ff, 0x00);
|
||||||
bus.write(0x3500, 0x54);
|
bus.write(0x3500, 0x54);
|
||||||
@@ -75,8 +82,9 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x20, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
|
public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
|
||||||
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITHOUT_INDIRECT_JMP_BUG);
|
cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
|
||||||
bus.write(0x3400, 0x22);
|
bus.write(0x3400, 0x22);
|
||||||
bus.write(0x34ff, 0x00);
|
bus.write(0x34ff, 0x00);
|
||||||
bus.write(0x3500, 0x54);
|
bus.write(0x3500, 0x54);
|
||||||
@@ -87,8 +95,9 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x20, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_JMP_cmos() throws MemoryAccessException {
|
public void test_JMP_cmos() throws MemoryAccessException {
|
||||||
cpu.setBehavior(Cpu.CpuBehavior.CMOS);
|
cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
|
||||||
bus.write(0x3400, 0x22);
|
bus.write(0x3400, 0x22);
|
||||||
bus.write(0x34ff, 0x00);
|
bus.write(0x34ff, 0x00);
|
||||||
bus.write(0x3500, 0x54);
|
bus.write(0x3500, 0x54);
|
||||||
|
|||||||
@@ -3,14 +3,21 @@ package com.loomcom.symon;
|
|||||||
import com.loomcom.symon.devices.Memory;
|
import com.loomcom.symon.devices.Memory;
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
public class CpuIndirectXModeTest extends TestCase {
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public class CpuIndirectXModeTest {
|
||||||
|
|
||||||
protected Cpu cpu;
|
protected Cpu cpu;
|
||||||
protected Bus bus;
|
protected Bus bus;
|
||||||
protected Memory mem;
|
protected Memory mem;
|
||||||
|
|
||||||
protected void setUp() throws Exception {
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
this.cpu = new Cpu();
|
this.cpu = new Cpu();
|
||||||
this.bus = new Bus(0x0000, 0xffff);
|
this.bus = new Bus(0x0000, 0xffff);
|
||||||
this.mem = new Memory(0x0000, 0xffff);
|
this.mem = new Memory(0x0000, 0xffff);
|
||||||
@@ -47,7 +54,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* ORA - Logical Inclusive OR - $1d */
|
/* ORA - Logical Inclusive OR - $1d */
|
||||||
|
@Test
|
||||||
public void test_ORA() throws MemoryAccessException {
|
public void test_ORA() throws MemoryAccessException {
|
||||||
// Set some initial values in memory
|
// Set some initial values in memory
|
||||||
bus.write(0x2c30, 0x00);
|
bus.write(0x2c30, 0x00);
|
||||||
@@ -93,7 +100,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* AND - Logical AND - $3d */
|
/* AND - Logical AND - $3d */
|
||||||
|
@Test
|
||||||
public void test_AND() throws MemoryAccessException {
|
public void test_AND() throws MemoryAccessException {
|
||||||
bus.write(0x1a30, 0x00);
|
bus.write(0x1a30, 0x00);
|
||||||
bus.write(0x1a31, 0x11);
|
bus.write(0x1a31, 0x11);
|
||||||
@@ -153,7 +160,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* EOR - Exclusive OR - $5d */
|
/* EOR - Exclusive OR - $5d */
|
||||||
|
@Test
|
||||||
public void test_EOR() throws MemoryAccessException {
|
public void test_EOR() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x00);
|
bus.write(0xab40, 0x00);
|
||||||
bus.write(0xab41, 0xff);
|
bus.write(0xab41, 0xff);
|
||||||
@@ -189,7 +196,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ADC - Add with Carry - $7d */
|
/* ADC - Add with Carry - $7d */
|
||||||
|
@Test
|
||||||
public void test_ADC() throws MemoryAccessException {
|
public void test_ADC() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x01);
|
bus.write(0xab40, 0x01);
|
||||||
bus.write(0xab41, 0xff);
|
bus.write(0xab41, 0xff);
|
||||||
@@ -290,6 +297,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
assertTrue(cpu.getCarryFlag());
|
assertTrue(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_ADC_IncludesCarry() throws MemoryAccessException {
|
public void test_ADC_IncludesCarry() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x01);
|
bus.write(0xab40, 0x01);
|
||||||
|
|
||||||
@@ -307,6 +315,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_ADC_DecimalMode() throws MemoryAccessException {
|
public void test_ADC_DecimalMode() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x01);
|
bus.write(0xab40, 0x01);
|
||||||
bus.write(0xab41, 0x99);
|
bus.write(0xab41, 0x99);
|
||||||
@@ -404,7 +413,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* STA - Store Accumulator - $9d */
|
/* STA - Store Accumulator - $9d */
|
||||||
|
@Test
|
||||||
public void test_STA() throws MemoryAccessException {
|
public void test_STA() throws MemoryAccessException {
|
||||||
cpu.setXRegister(0x30);
|
cpu.setXRegister(0x30);
|
||||||
|
|
||||||
@@ -438,7 +447,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* LDA - Load Accumulator - $bd */
|
/* LDA - Load Accumulator - $bd */
|
||||||
|
@Test
|
||||||
public void test_LDA() throws MemoryAccessException {
|
public void test_LDA() throws MemoryAccessException {
|
||||||
bus.write(0xab42, 0x00);
|
bus.write(0xab42, 0x00);
|
||||||
bus.write(0xab43, 0x0f);
|
bus.write(0xab43, 0x0f);
|
||||||
@@ -467,7 +476,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* CMP - Compare Accumulator - $dd */
|
/* CMP - Compare Accumulator - $dd */
|
||||||
|
@Test
|
||||||
public void test_CMP() throws MemoryAccessException {
|
public void test_CMP() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x00);
|
bus.write(0xab40, 0x00);
|
||||||
bus.write(0xab41, 0x80);
|
bus.write(0xab41, 0x80);
|
||||||
@@ -498,7 +507,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* SBC - Subtract with Carry - $fd */
|
/* SBC - Subtract with Carry - $fd */
|
||||||
|
@Test
|
||||||
public void test_SBC() throws MemoryAccessException {
|
public void test_SBC() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x01);
|
bus.write(0xab40, 0x01);
|
||||||
|
|
||||||
@@ -557,6 +566,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
assertTrue(cpu.getCarryFlag());
|
assertTrue(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException {
|
public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x01);
|
bus.write(0xab40, 0x01);
|
||||||
|
|
||||||
@@ -602,6 +612,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
public void test_SBC_DecimalMode() throws MemoryAccessException {
|
public void test_SBC_DecimalMode() throws MemoryAccessException {
|
||||||
bus.write(0xab40, 0x01);
|
bus.write(0xab40, 0x01);
|
||||||
bus.write(0xab50, 0x11);
|
bus.write(0xab50, 0x11);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.loomcom.symon;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
|
import com.loomcom.symon.util.Utils;
|
||||||
import junit.framework.*;
|
import junit.framework.*;
|
||||||
|
|
||||||
import com.loomcom.symon.devices.*;
|
import com.loomcom.symon.devices.*;
|
||||||
@@ -240,6 +241,7 @@ public class CpuTest extends TestCase {
|
|||||||
|
|
||||||
public void testSetProcessorStatus() {
|
public void testSetProcessorStatus() {
|
||||||
// Default
|
// Default
|
||||||
|
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getIrqDisableFlag());
|
assertFalse(cpu.getIrqDisableFlag());
|
||||||
@@ -383,6 +385,41 @@ public class CpuTest extends TestCase {
|
|||||||
assertFalse(cpu.getCpuState().irqAsserted);
|
assertFalse(cpu.getCpuState().irqAsserted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testIrqDoesNotSetBRK() throws Exception {
|
||||||
|
// Ensure the IRQ disable flag is cleared
|
||||||
|
cpu.clearIrqDisableFlag();
|
||||||
|
|
||||||
|
// Set the IRQ vector
|
||||||
|
bus.write(0xffff, 0x12);
|
||||||
|
bus.write(0xfffe, 0x34);
|
||||||
|
|
||||||
|
// Create an IRQ handler at 0x1234
|
||||||
|
cpu.setProgramCounter(0x1234);
|
||||||
|
bus.loadProgram(0xa9, 0x33, // LDA #$33
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
|
||||||
|
cpu.setProgramCounter(0x0200);
|
||||||
|
// Create a little program at 0x0200
|
||||||
|
bus.loadProgram(0x18, // CLC
|
||||||
|
0xa9, 0x01, // LDA #$00
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed.
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x0203, cpu.getProgramCounter());
|
||||||
|
assertEquals(0x01, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.assertIrq();
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the IRQ
|
||||||
|
assertFalse(cpu.getBreakFlag());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void testIrqHonorsIrqDisabledFlag() throws Exception {
|
public void testIrqHonorsIrqDisabledFlag() throws Exception {
|
||||||
// Ensure the IRQ disable flag is set
|
// Ensure the IRQ disable flag is set
|
||||||
cpu.setIrqDisableFlag();
|
cpu.setIrqDisableFlag();
|
||||||
@@ -497,6 +534,37 @@ public class CpuTest extends TestCase {
|
|||||||
assertFalse(cpu.getCpuState().nmiAsserted);
|
assertFalse(cpu.getCpuState().nmiAsserted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testNmiDoesNotSetBRK() throws Exception {
|
||||||
|
// Set the NMI vector to 0x1000
|
||||||
|
bus.write(0xfffb, 0x10);
|
||||||
|
bus.write(0xfffa, 0x00);
|
||||||
|
|
||||||
|
// Create an NMI handler at 0x1000
|
||||||
|
cpu.setProgramCounter(0x1000);
|
||||||
|
bus.loadProgram(0xa9, 0x33, // LDA #$33
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
|
||||||
|
// Create a little program at 0x0200
|
||||||
|
cpu.setProgramCounter(0x0200);
|
||||||
|
bus.loadProgram(0x18, // CLC
|
||||||
|
0xa9, 0x01, // LDA #$00
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed.
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x0203, cpu.getProgramCounter());
|
||||||
|
assertEquals(0x01, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.assertNmi();
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertFalse(cpu.getBreakFlag());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void testNmiIgnoresIrqDisableFlag() throws Exception {
|
public void testNmiIgnoresIrqDisableFlag() throws Exception {
|
||||||
// Set the IRQ disable flag, which should be ignored by the NMI
|
// Set the IRQ disable flag, which should be ignored by the NMI
|
||||||
cpu.setIrqDisableFlag();
|
cpu.setIrqDisableFlag();
|
||||||
@@ -537,11 +605,11 @@ public class CpuTest extends TestCase {
|
|||||||
|
|
||||||
|
|
||||||
public void testAddress() {
|
public void testAddress() {
|
||||||
assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
|
assertEquals(0xf1ea, Utils.address(0xea, 0xf1));
|
||||||
assertEquals(0x00ea, cpu.address(0xea, 0x00));
|
assertEquals(0x00ea, Utils.address(0xea, 0x00));
|
||||||
assertEquals(0xf100, cpu.address(0x00, 0xf1));
|
assertEquals(0xf100, Utils.address(0x00, 0xf1));
|
||||||
assertEquals(0x1234, cpu.address(0x34, 0x12));
|
assertEquals(0x1234, Utils.address(0x34, 0x12));
|
||||||
assertEquals(0xffff, cpu.address(0xff, 0xff));
|
assertEquals(0xffff, Utils.address(0xff, 0xff));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testZpxAddress() {
|
public void testZpxAddress() {
|
||||||
|
|||||||
@@ -0,0 +1,329 @@
|
|||||||
|
package com.loomcom.symon;
|
||||||
|
|
||||||
|
import com.loomcom.symon.devices.Memory;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertFalse;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||||
|
* in the 65C02 and 65816
|
||||||
|
*/
|
||||||
|
public class CpuZeroPageIndirectTest {
|
||||||
|
protected Cpu cpu;
|
||||||
|
protected Bus bus;
|
||||||
|
protected Memory mem;
|
||||||
|
|
||||||
|
private void makeCmosCpu() throws Exception {
|
||||||
|
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeNmosCpu() throws Exception {
|
||||||
|
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||||
|
this.cpu = new Cpu(behavior);
|
||||||
|
this.bus = new Bus(0x0000, 0xffff);
|
||||||
|
this.mem = new Memory(0x0000, 0xffff);
|
||||||
|
bus.addCpu(cpu);
|
||||||
|
bus.addDevice(mem);
|
||||||
|
|
||||||
|
// Load the reset vector.
|
||||||
|
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||||
|
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ora() throws Exception {
|
||||||
|
makeCmosCpu();
|
||||||
|
|
||||||
|
// Set some initial values in zero page.
|
||||||
|
bus.write(0x30, 0x00);
|
||||||
|
bus.write(0x31, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x40, 0x01);
|
||||||
|
bus.write(0x41, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x50, 0x02);
|
||||||
|
bus.write(0x51, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x60, 0x03);
|
||||||
|
bus.write(0x61, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x1000, 0x11);
|
||||||
|
bus.write(0x1001, 0x22);
|
||||||
|
bus.write(0x1002, 0x44);
|
||||||
|
bus.write(0x1003, 0x88);
|
||||||
|
|
||||||
|
bus.loadProgram(0x12, 0x30, // ORA ($30)
|
||||||
|
0x12, 0x40, // ORA ($40)
|
||||||
|
0x12, 0x50, // ORA ($50)
|
||||||
|
0x12, 0x60); // ORA ($60)
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
|
||||||
|
// 0x00 | 0x11 = 0x11
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x11, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
|
||||||
|
// 0x11 | 0x22 = 0x33
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x33, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
|
||||||
|
// 0x33 | 0x44 = 0x77
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x77, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
|
||||||
|
// 0x77 | 0x88 = 0xff
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0xff, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertTrue(cpu.getNegativeFlag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_ora_requiresCmosCpu() throws Exception {
|
||||||
|
makeNmosCpu();
|
||||||
|
|
||||||
|
// Set some initial values in zero page.
|
||||||
|
bus.write(0x30, 0x00);
|
||||||
|
bus.write(0x31, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x40, 0x01);
|
||||||
|
bus.write(0x41, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x50, 0x02);
|
||||||
|
bus.write(0x51, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x60, 0x03);
|
||||||
|
bus.write(0x61, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x1000, 0x11);
|
||||||
|
bus.write(0x1001, 0x22);
|
||||||
|
bus.write(0x1002, 0x44);
|
||||||
|
bus.write(0x1003, 0x88);
|
||||||
|
|
||||||
|
bus.loadProgram(0x12, 0x30, // ORA ($30)
|
||||||
|
0x12, 0x40, // ORA ($40)
|
||||||
|
0x12, 0x50, // ORA ($50)
|
||||||
|
0x12, 0x60); // ORA ($60)
|
||||||
|
|
||||||
|
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
|
||||||
|
boolean zState = cpu.getZeroFlag();
|
||||||
|
boolean nState = cpu.getNegativeFlag();
|
||||||
|
|
||||||
|
// 0x00 | 0x11 = 0x11, but not implemented in NMOS cpu
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
assertEquals(zState, cpu.getZeroFlag()); // unchanged
|
||||||
|
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
|
||||||
|
|
||||||
|
// 0x11 | 0x22 = 0x33, but not implemented in NMOS cpu
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
assertEquals(zState, cpu.getZeroFlag()); // unchanged
|
||||||
|
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
|
||||||
|
|
||||||
|
// 0x33 | 0x44 = 0x77, but not implemented in NMOS cpu
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
assertEquals(zState, cpu.getZeroFlag()); // unchanged
|
||||||
|
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
|
||||||
|
|
||||||
|
// 0x77 | 0x88 = 0xff, but not implemented in NMOS cpu
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
assertEquals(zState, cpu.getZeroFlag()); // unchanged
|
||||||
|
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_and() throws Exception {
|
||||||
|
makeCmosCpu();
|
||||||
|
|
||||||
|
// Set some initial values in zero page.
|
||||||
|
bus.write(0x30, 0x00);
|
||||||
|
bus.write(0x31, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x40, 0x01);
|
||||||
|
bus.write(0x41, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x50, 0x02);
|
||||||
|
bus.write(0x51, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x1000, 0x33);
|
||||||
|
bus.write(0x1001, 0x11);
|
||||||
|
bus.write(0x1002, 0x88);
|
||||||
|
|
||||||
|
bus.loadProgram(0x32, 0x30, // AND ($30)
|
||||||
|
0x32, 0x40, // AND ($40)
|
||||||
|
0x32, 0x50); // AND ($50)
|
||||||
|
|
||||||
|
cpu.setAccumulator(0xff);
|
||||||
|
|
||||||
|
// 0xFF & 0x33 == 0x33
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x33, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
|
||||||
|
// 0x33 & 0x11 == 0x11
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x11, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
|
||||||
|
// 0x11 & 0x80 == 0
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0, cpu.getAccumulator());
|
||||||
|
assertTrue(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_and_requiresCmosCpu() throws Exception {
|
||||||
|
makeNmosCpu();
|
||||||
|
|
||||||
|
// Set some initial values in zero page.
|
||||||
|
bus.write(0x30, 0x00);
|
||||||
|
bus.write(0x31, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x40, 0x01);
|
||||||
|
bus.write(0x41, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x50, 0x02);
|
||||||
|
bus.write(0x51, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x1000, 0x33);
|
||||||
|
bus.write(0x1001, 0x11);
|
||||||
|
bus.write(0x1002, 0x88);
|
||||||
|
|
||||||
|
bus.loadProgram(0x32, 0x30, // AND ($30)
|
||||||
|
0x32, 0x40, // AND ($40)
|
||||||
|
0x32, 0x50); // AND ($50)
|
||||||
|
|
||||||
|
|
||||||
|
cpu.setAccumulator(0xff);
|
||||||
|
|
||||||
|
boolean zState = cpu.getZeroFlag();
|
||||||
|
boolean nState = cpu.getNegativeFlag();
|
||||||
|
|
||||||
|
// 0xFF & 0x33 == 0x33, but not implemented in NMOS cpu
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0xff, cpu.getAccumulator());
|
||||||
|
assertEquals(zState, cpu.getZeroFlag());
|
||||||
|
assertEquals(nState, cpu.getNegativeFlag());
|
||||||
|
|
||||||
|
// 0x33 & 0x11 == 0x11, but not implemented in NMOS cpu
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0xff, cpu.getAccumulator());
|
||||||
|
assertEquals(zState, cpu.getZeroFlag());
|
||||||
|
assertEquals(nState, cpu.getNegativeFlag());
|
||||||
|
|
||||||
|
// 0x11 & 0x80 == 0, but not implemented in NMOS cpu
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0xff, cpu.getAccumulator());
|
||||||
|
assertEquals(zState, cpu.getZeroFlag());
|
||||||
|
assertEquals(nState, cpu.getNegativeFlag());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_eor() throws Exception {
|
||||||
|
makeCmosCpu();
|
||||||
|
|
||||||
|
// Set some initial values in zero page.
|
||||||
|
bus.write(0x30, 0x00);
|
||||||
|
bus.write(0x31, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x40, 0x01);
|
||||||
|
bus.write(0x41, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x50, 0x02);
|
||||||
|
bus.write(0x51, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x60, 0x03);
|
||||||
|
bus.write(0x61, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x1000, 0x00);
|
||||||
|
bus.write(0x1001, 0xff);
|
||||||
|
bus.write(0x1002, 0x33);
|
||||||
|
bus.write(0x1003, 0x44);
|
||||||
|
|
||||||
|
bus.loadProgram(0x52, 0x30, // AND ($30)
|
||||||
|
0x52, 0x40, // AND ($40)
|
||||||
|
0x52, 0x50, // EOR ($50)
|
||||||
|
0x52, 0x60); // AND ($60)
|
||||||
|
|
||||||
|
|
||||||
|
cpu.setAccumulator(0x88);
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x88, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x77, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x44, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_eor_requiresCmosCpu() throws Exception {
|
||||||
|
makeNmosCpu();
|
||||||
|
|
||||||
|
// Set some initial values in zero page.
|
||||||
|
bus.write(0x30, 0x00);
|
||||||
|
bus.write(0x31, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x40, 0x01);
|
||||||
|
bus.write(0x41, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x50, 0x02);
|
||||||
|
bus.write(0x51, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x60, 0x03);
|
||||||
|
bus.write(0x61, 0x10);
|
||||||
|
|
||||||
|
bus.write(0x1000, 0x00);
|
||||||
|
bus.write(0x1001, 0xff);
|
||||||
|
bus.write(0x1002, 0x33);
|
||||||
|
bus.write(0x1003, 0x44);
|
||||||
|
|
||||||
|
bus.loadProgram(0x52, 0x30, // AND ($30)
|
||||||
|
0x52, 0x40, // AND ($40)
|
||||||
|
0x52, 0x50, // EOR ($50)
|
||||||
|
0x52, 0x60); // AND ($60)
|
||||||
|
|
||||||
|
|
||||||
|
cpu.setAccumulator(0x88);
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x88, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x88, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x88, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertEquals(0x88, cpu.getAccumulator());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,19 +19,17 @@ import static org.mockito.Mockito.*;
|
|||||||
public class CrtcTest {
|
public class CrtcTest {
|
||||||
|
|
||||||
Crtc crtc;
|
Crtc crtc;
|
||||||
|
Memory memory;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
DeviceChangeListener changeListener;
|
DeviceChangeListener changeListener;
|
||||||
@Mock
|
|
||||||
Memory memory;
|
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void createDevices() throws Exception {
|
public void createDevices() throws Exception {
|
||||||
|
memory = new Memory(0, 0x7fff);
|
||||||
|
|
||||||
crtc = new Crtc(0x9000, memory);
|
crtc = new Crtc(0x9000, memory);
|
||||||
crtc.registerListener(changeListener);
|
crtc.registerListener(changeListener);
|
||||||
|
|
||||||
when(memory.startAddress()).thenReturn(0);
|
|
||||||
when(memory.endAddress()).thenReturn(0x7fff);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -340,4 +338,58 @@ public class CrtcTest {
|
|||||||
crtc.write(1, 0x80); // Can't position cursor
|
crtc.write(1, 0x80); // Can't position cursor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSetRowColumnAddressing() throws Exception {
|
||||||
|
assertEquals(false, crtc.getRowColumnAddressing());
|
||||||
|
crtc.write(0, 8); // Select mode control register
|
||||||
|
crtc.write(1, 0x04);
|
||||||
|
assertEquals(true, crtc.getRowColumnAddressing());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSetDisplayEnableSkew() throws Exception {
|
||||||
|
assertEquals(false, crtc.getDisplayEnableSkew());
|
||||||
|
crtc.write(0, 8); // Select mode control register
|
||||||
|
crtc.write(1, 0x10);
|
||||||
|
assertEquals(true, crtc.getDisplayEnableSkew());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldSetCursorSkew() throws Exception {
|
||||||
|
assertEquals(false, crtc.getCursorSkew());
|
||||||
|
crtc.write(0, 8); // Select mode control register
|
||||||
|
crtc.write(1, 0x20);
|
||||||
|
assertEquals(true, crtc.getCursorSkew());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldDoStraightBinaryAddressing() throws Exception {
|
||||||
|
crtc.write(0, 8);
|
||||||
|
crtc.write(1, 0); // Select straight binary addressing
|
||||||
|
|
||||||
|
// Fill the memory with a repeating pattern
|
||||||
|
int videoMemoryBase = 0x7000;
|
||||||
|
int j = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2048; i++) {
|
||||||
|
memory.write(videoMemoryBase + i, j);
|
||||||
|
if (j == 255) {
|
||||||
|
j = 0;
|
||||||
|
} else {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now verify that straight-binary addressing of the CRTC works
|
||||||
|
j = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < 2048; i++) {
|
||||||
|
assertEquals(j, crtc.getCharAtAddress(videoMemoryBase + i));
|
||||||
|
if (j == 255) {
|
||||||
|
j = 0;
|
||||||
|
} else {
|
||||||
|
j++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package com.loomcom.symon;
|
|
||||||
|
|
||||||
import com.loomcom.symon.util.HexUtil;
|
|
||||||
import junit.framework.TestCase;
|
|
||||||
|
|
||||||
public class HexUtilTest extends TestCase {
|
|
||||||
public void testByteToHex() {
|
|
||||||
assertEquals("FE", HexUtil.byteToHex(0xfe));
|
|
||||||
assertEquals("00", HexUtil.byteToHex(0));
|
|
||||||
assertEquals("0A", HexUtil.byteToHex(10));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testByteToHexIgnoresSign() {
|
|
||||||
assertEquals("FF", HexUtil.byteToHex(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testByteToHexMasksLowByte() {
|
|
||||||
assertEquals("FE", HexUtil.byteToHex(0xfffe));
|
|
||||||
assertEquals("00", HexUtil.byteToHex(0xff00));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testWordToHex() {
|
|
||||||
assertEquals("0000", HexUtil.wordToHex(0));
|
|
||||||
assertEquals("FFFF", HexUtil.wordToHex(65535));
|
|
||||||
assertEquals("FFFE", HexUtil.wordToHex(65534));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testWordToHexIgnoresSign() {
|
|
||||||
assertEquals("FFFF", HexUtil.wordToHex(-1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testWordToHexMasksTwoLowBytes() {
|
|
||||||
assertEquals("FFFE", HexUtil.wordToHex(0xfffffe));
|
|
||||||
assertEquals("FF00", HexUtil.wordToHex(0xffff00));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAllBytesAreCorrect() {
|
|
||||||
for (int i = 0; i <= 0xff; i++) {
|
|
||||||
assertEquals(String.format("%02X", i), HexUtil.byteToHex(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package com.loomcom.symon;
|
||||||
|
|
||||||
|
import com.loomcom.symon.util.Utils;
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
public class UtilsTest extends TestCase {
|
||||||
|
public void testByteToHex() {
|
||||||
|
assertEquals("FE", Utils.byteToHex(0xfe));
|
||||||
|
assertEquals("00", Utils.byteToHex(0));
|
||||||
|
assertEquals("0A", Utils.byteToHex(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testByteToHexIgnoresSign() {
|
||||||
|
assertEquals("FF", Utils.byteToHex(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testByteToHexMasksLowByte() {
|
||||||
|
assertEquals("FE", Utils.byteToHex(0xfffe));
|
||||||
|
assertEquals("00", Utils.byteToHex(0xff00));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWordToHex() {
|
||||||
|
assertEquals("0000", Utils.wordToHex(0));
|
||||||
|
assertEquals("FFFF", Utils.wordToHex(65535));
|
||||||
|
assertEquals("FFFE", Utils.wordToHex(65534));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWordToHexIgnoresSign() {
|
||||||
|
assertEquals("FFFF", Utils.wordToHex(-1));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testWordToHexMasksTwoLowBytes() {
|
||||||
|
assertEquals("FFFE", Utils.wordToHex(0xfffffe));
|
||||||
|
assertEquals("FF00", Utils.wordToHex(0xffff00));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testAllBytesAreCorrect() {
|
||||||
|
for (int i = 0; i <= 0xff; i++) {
|
||||||
|
assertEquals(String.format("%02X", i), Utils.byteToHex(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||