Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b9997e3edc | ||
|
|
45c8a69d7b | ||
|
|
2e2163fefb | ||
|
|
995b372f55 | ||
|
|
47c87aac56 | ||
|
|
3a59dedc29 | ||
|
|
7268766cd6 | ||
|
|
2284535b7f | ||
|
|
963526621d | ||
|
|
c317544e7b | ||
|
|
ecd4bbbd9a | ||
|
|
a537328852 | ||
|
|
4975fca506 | ||
|
|
199d96c025 | ||
|
|
0c026e38dd | ||
|
|
5e56627f32 | ||
|
|
67f5e17f78 | ||
|
|
5a25750f46 | ||
|
|
4a8a803472 | ||
|
|
4423623816 | ||
|
|
5df775bbb0 | ||
|
|
d076046f57 | ||
|
|
b725fb5fdd | ||
|
|
9351d785ae | ||
|
|
66a92f4196 | ||
|
|
cda9a218af | ||
|
|
b5a470d3ba | ||
|
|
e210a40639 | ||
|
|
0bad9912ce | ||
|
|
b36b442b14 |
No files matched your search
@@ -1,4 +1,5 @@
|
|||||||
*~
|
*~
|
||||||
|
*.jar
|
||||||
*#
|
*#
|
||||||
target
|
target
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
SYMON - A 6502 System Simulator
|
SYMON - A 6502 System Simulator
|
||||||
===============================
|
===============================
|
||||||
|
|
||||||
**Version:** 1.3.2
|
**Version:** 1.5.0
|
||||||
|
|
||||||
**Last Updated:** 08 March, 2021
|
**Last Updated:** 30 March, 2025
|
||||||
|
|
||||||
See the file COPYING for license.
|
See the file COPYING for license.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## 1.0 About
|
## 1.0 About
|
||||||
|
|
||||||
@@ -25,20 +25,20 @@ Klaus Dormann's 6502 Functional Test Suite as of version 0.8.2
|
|||||||
(See [this thread on the 6502.org Forums](http://forum.6502.org/viewtopic.php?f=2&t=2241)
|
(See [this thread on the 6502.org Forums](http://forum.6502.org/viewtopic.php?f=2&t=2241)
|
||||||
for more information about this functional test suite).
|
for more information about this functional test suite).
|
||||||
|
|
||||||
Symon is under constant, active development. Feedback and patches
|
Symon is under active maintenance. Feedback and patches are always
|
||||||
are always welcome.
|
welcome.
|
||||||
|
|
||||||
## 2.0 Requirements
|
## 2.0 Requirements
|
||||||
|
|
||||||
- Java 1.8 or higher
|
- Java 10 or higher
|
||||||
- Maven 2.0.x or higher (for building from source)
|
- Maven 3.0.x or higher (for building from source)
|
||||||
- JUnit 4 or higher (for testing)
|
- JUnit 4 or higher (for testing)
|
||||||
|
|
||||||
## 3.0 Features
|
## 3.0 Features
|
||||||
|
|
||||||
Symon can simulate multiple 6502 based architectures. At present, three
|
Symon can simulate multiple 6502 based architectures. At present, four
|
||||||
machines are implemented: Symon (the default), MULTICOMP, and a "Simple"
|
machines are implemented: Symon (the default), MULTICOMP, BenEater, and
|
||||||
machine useful for debugging.
|
a "Simple" machine useful for debugging.
|
||||||
|
|
||||||
### 3.1 Memory Maps
|
### 3.1 Memory Maps
|
||||||
|
|
||||||
@@ -64,27 +64,49 @@ memory.
|
|||||||
|
|
||||||
- `$0000`--`$FFFF`: 64KB RAM
|
- `$0000`--`$FFFF`: 64KB RAM
|
||||||
|
|
||||||
|
#### 3.1.4 BenEater Memory Map
|
||||||
|
|
||||||
|
- `$0000`--`$3FFF`: 16KB RAM
|
||||||
|
- `$5000`--`$5003`: MOS 6551 ACIA (Serial Console)
|
||||||
|
- `$6000`--`$600F`: 6522 VIA
|
||||||
|
- `$8000`--`$FFFF`: 16KB ROM
|
||||||
|
|
||||||
### 3.2 Serial Console and CPU Status
|
### 3.2 Serial Console and CPU Status
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The main window of the simulator acts as the primary Input/Output
|
The main window of the simulator acts as the primary Input/Output
|
||||||
system through a virtual serial terminal. The terminal is attached to
|
system through a virtual serial terminal. It also provides CPU status.
|
||||||
a simulated ACIA, including a programmable baud rate generator that
|
Contents of the accumulator, index registers, processor status flags,
|
||||||
tries to approximate the correct "feel" of the programmed baud rate.
|
disassembly of the instruction register, and stack pointer are all displayed.
|
||||||
(The sample Enhanced BASIC ROM image is programmed for 9600 baud)
|
|
||||||
|
|
||||||
It also provides CPU status. Contents of the accumulator, index
|
The terminal is attached to a simulated MOS 6551 ACIA. It behaves very much
|
||||||
registers, processor status flags, disassembly of the instruction
|
as described in the datasheet, with some exceptions:
|
||||||
register, and stack pointer are all displayed.
|
|
||||||
|
|
||||||

|
- The simulated ACIA is permanently connected to the virtual terminal,
|
||||||
|
the Data Carrier Detect and Data Set Ready status bits always indicate
|
||||||
|
a connection is ready.
|
||||||
|
- The parity, stop-bits and bits-per-character settings are ignored. The
|
||||||
|
ACIA always sends and receives 8-bit characters, and parity errors
|
||||||
|
do not occur.
|
||||||
|
- The ACIA tries to honour the configured baud rate, but as a special case
|
||||||
|
the default "16x External Clock" rate is interpreted to mean "as fast as
|
||||||
|
possible" (The sample Enhanced BASIC ROM image is programmed for 9600 baud).
|
||||||
|
- The ACIA ignores the configured state of the Data Terminal Ready pin;
|
||||||
|
it is always ready to receive and transmit.
|
||||||
|
|
||||||
|
For more information on the MOS 6551 ACIA and its programming model,
|
||||||
|
see the official datasheet:
|
||||||
|
|
||||||
|
- [MOS 6551 ACIA](http://archive.6502.org/datasheets/mos_6551_acia.pdf)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
The console supports font sizes from 10 to 20 points.
|
The console supports font sizes from 10 to 20 points.
|
||||||
|
|
||||||
### 3.3 ROM Loading
|
### 3.3 ROM Loading
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Symon can load any appropriately sized ROM image. The Symon
|
Symon can load any appropriately sized ROM image. The Symon
|
||||||
architecture expects as 16KB (16384 byte) ROM image, while the
|
architecture expects as 16KB (16384 byte) ROM image, while the
|
||||||
@@ -95,32 +117,32 @@ address.
|
|||||||
|
|
||||||
### 3.4 Memory Window
|
### 3.4 Memory Window
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Memory contents can be viewed (and edited) one page at a time through the Memory Window.
|
Memory contents can be viewed (and edited) one page at a time through the Memory Window.
|
||||||
|
|
||||||
### 3.5 Trace Log
|
### 3.5 Trace Log
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The last 20,000 execution steps are disassembled and logged to the Trace Log
|
The last 20,000 execution steps are disassembled and logged to the Trace Log
|
||||||
Window.
|
Window.
|
||||||
|
|
||||||
### 3.6 Simulator Speeds
|
### 3.6 Simulator Speeds
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Simulated speeds may be set from 1MHz to 8MHz.
|
Simulated speeds may be set from 1MHz to 8MHz.
|
||||||
|
|
||||||
### 3.7 Breakpoints
|
### 3.7 Breakpoints
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
Breakpoints can be set and removed through the Breakpoints window.
|
Breakpoints can be set and removed through the Breakpoints window.
|
||||||
|
|
||||||
### 3.8 Experimental 6545 CRTC Video
|
### 3.8 Experimental 6545 CRTC Video
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This feature is highly experimental. It's possible to open a video window
|
This feature is highly experimental. It's possible to open a video window
|
||||||
from the "View" menu. This window simulates the output of a MOS 6545 CRT
|
from the "View" menu. This window simulates the output of a MOS 6545 CRT
|
||||||
@@ -198,24 +220,36 @@ interface.
|
|||||||
Two command line options may be passed to the JAR file on startup,
|
Two command line options may be passed to the JAR file on startup,
|
||||||
to specify machine type and CPU type. The options are:
|
to specify machine type and CPU type. The options are:
|
||||||
|
|
||||||
- `-cpu 6502`: Use the NMOS 6502 CPU type by default.
|
- `-c`,`-cpu 6502`: Use the NMOS 6502 CPU type by default.
|
||||||
- `-cpu 65c02`: Use the CMOS 65C02 CPU type by default.
|
- `-c`,`-cpu 65c02`: Use the CMOS 65C02 CPU type by default.
|
||||||
- `-machine symon`: Use the **Symon** machine type by default.
|
- `-c`,`-machine symon`: Use the **Symon** machine type by default.
|
||||||
- `-machine multicomp`: Use the **Multicomp** machine type by default.
|
- `-c`,`-machine multicomp`: Use the **Multicomp** machine type by default.
|
||||||
- `-machine simple`: Use the **Simple** machine type by default.
|
- `-m`,`-machine simple`: Use the **Simple** machine type by default.
|
||||||
- `-rom <file>`: Use the specified file as the ROM image.
|
- `-m`,`-machine beneater`: Use the **BenEater** machine type by default.
|
||||||
|
- `-r`,`-rom <file>`: Use the specified file as the ROM image.
|
||||||
|
- `-b`,`-brk`: Halt the simulator on a BRK instruction (default is to continue)
|
||||||
|
|
||||||
### 4.2 ROM images
|
### 4.2 High-DPI UI scaling
|
||||||
|
|
||||||
|
In certain environments with high-DPI (e.g. 4K) displays, the UI may
|
||||||
|
not automatically scale and appear quite tiny. This can be fixed
|
||||||
|
with the command line argument `-Dsun.java2d.uiScale=<factor>`,
|
||||||
|
for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
java -Dsun.java2d.uiScale=2.0 -jar symon-1.5.0.jar
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3 ROM images
|
||||||
|
|
||||||
The simulator requires a ROM image loaded into memory to work
|
The simulator requires a ROM image loaded into memory to work
|
||||||
properly. Without a ROM in memory, the simulator will not be able to
|
properly. Without a ROM in memory, the simulator will not be able to
|
||||||
reset, since the reset vector for the 6502 is located in the ROM
|
reset, since the reset vector for the 6502 is located in the ROM
|
||||||
address space.
|
address space.
|
||||||
|
|
||||||
By default, any file named `rom.bin` that exists in the same directory
|
ROM images can be loaded with the `-rom` argument when running
|
||||||
where Symon is launched will be loaded as a ROM image. ROM images can
|
Symon from the command line. ROM images can also be swapped out at
|
||||||
also be swapped out at run-time with the "Load ROM Image..." in the
|
run-time with the "Load ROM..." item in the File menu.
|
||||||
File menu.
|
|
||||||
|
|
||||||
The "samples" directory contains a ROM image for the Symon
|
The "samples" directory contains a ROM image for the Symon
|
||||||
architecture named 'ehbasic.rom', containing Lee Davison's Enhanced
|
architecture named 'ehbasic.rom', containing Lee Davison's Enhanced
|
||||||
@@ -224,7 +258,7 @@ architecture named 'ehbasic.rom', containing Lee Davison's Enhanced
|
|||||||
*Note*: Presently, EhBASIC only works with the Symon machine
|
*Note*: Presently, EhBASIC only works with the Symon machine
|
||||||
architecture, not with MULTICOMP.
|
architecture, not with MULTICOMP.
|
||||||
|
|
||||||
### 4.3 Loading A Program
|
### 4.4 Loading A Program
|
||||||
|
|
||||||
In addition to ROM images, programs in the form of raw binary object files can
|
In addition to ROM images, programs in the form of raw binary object files can
|
||||||
be loaded directly into memory from "Load Program..." in the File menu.
|
be loaded directly into memory from "Load Program..." in the File menu.
|
||||||
@@ -240,13 +274,34 @@ for testing.
|
|||||||
|
|
||||||
- 'hello.prg' will continuously print "Hello, 6502 World!" to the console.
|
- 'hello.prg' will continuously print "Hello, 6502 World!" to the console.
|
||||||
|
|
||||||
### 4.4 Running
|
### 4.5 Running
|
||||||
|
|
||||||
After loading a program or ROM image, clicking "Run" will start the simulator
|
After loading a program or ROM image, clicking "Run" will start the simulator
|
||||||
running.
|
running.
|
||||||
|
|
||||||
## 5.0 Revision History
|
## 5.0 Revision History
|
||||||
|
|
||||||
|
- **1.5.0:** 30 March 2025 - Fixed a bug that caused some 6502
|
||||||
|
instructions to be disassembled improperly in the trace log
|
||||||
|
(GitHub issue #17).
|
||||||
|
|
||||||
|
Added a new "Step Over" button that allows stepping over the
|
||||||
|
current subroutine. This button only becomes active when the
|
||||||
|
next instruction is a "JSR" while stepping through execution
|
||||||
|
one instruction at a time. It will continue execution until
|
||||||
|
the subroutine returns through a corresponding "RTS" instruction.
|
||||||
|
(GitHub feature request #18).
|
||||||
|
|
||||||
|
- **1.4.0:** 11 November 2023 - Adds a new machine, the Ben Eater
|
||||||
|
machine. Correct handling of 6551 interrupts, and several 6551
|
||||||
|
bug fixes. Fixes power-on status of 6502 status register. Fixes a
|
||||||
|
bug with ASCII backspace character not moving the cursor
|
||||||
|
backwards. Finally, "halt on BRK" is no longer enabled by default,
|
||||||
|
but can be set at runtime or by a command line flag. Thank you to
|
||||||
|
Tim Allen and Chelsea Wilkinson for contributions!
|
||||||
|
|
||||||
|
- **1.3.2:** 8 March 2022 - Minor bug fixes.
|
||||||
|
|
||||||
- **1.3.1:** 12 October, 2019 - Add support for new command line
|
- **1.3.1:** 12 October, 2019 - Add support for new command line
|
||||||
option `-cpu <type>` to specify one of `6502` or `65c02` on startup,
|
option `-cpu <type>` to specify one of `6502` or `65c02` on startup,
|
||||||
and new option `-rom <file>` to specify a ROM file to load.
|
and new option `-rom <file>` to specify a ROM file to load.
|
||||||
@@ -322,17 +377,12 @@ running.
|
|||||||
|
|
||||||
- Feedback (in the form of dialogs, status bar, etc).
|
- Feedback (in the form of dialogs, status bar, etc).
|
||||||
|
|
||||||
- Better debugging tools from the UI, including breakpoints
|
|
||||||
and disassembly.
|
|
||||||
|
|
||||||
- UI needs a ton more polish.
|
- UI needs a ton more polish.
|
||||||
|
|
||||||
- More extensive testing.
|
- More extensive testing.
|
||||||
|
|
||||||
- Clean up JavaDoc.
|
- Clean up JavaDoc.
|
||||||
|
|
||||||
- Implement CMOS 65C02 instructions and NMOS / CMOS mode flag.
|
|
||||||
|
|
||||||
- Allow displaying ACIA status and dumping ACIA buffers, for
|
- Allow displaying ACIA status and dumping ACIA buffers, for
|
||||||
debugging.
|
debugging.
|
||||||
|
|
||||||
@@ -342,11 +392,19 @@ running.
|
|||||||
|
|
||||||
- Symbolic debugging.
|
- Symbolic debugging.
|
||||||
|
|
||||||
|
- Better / correct disassembly and tracing of 65C02 and 65C816
|
||||||
|
instructions.
|
||||||
|
|
||||||
## 8.0 Copyright and Acknowledgements
|
## 8.0 Copyright and Acknowledgements
|
||||||
|
|
||||||
**Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>**
|
**Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>**
|
||||||
|
|
||||||
Portions Copyright (c) 2014 Maik Merten <maikmerten@googlemail.com>
|
- Portions Copyright (c) 2014 Maik Merten
|
||||||
|
<maikmerten@googlemail.com>
|
||||||
|
- Portions Copyright (c) 2022 Tim Allen
|
||||||
|
<thristian@gmail.com>
|
||||||
|
- Portions Copyright (c) 2023 Chelsea Wilkinson
|
||||||
|
<mail@chelseawilkinson.me>
|
||||||
|
|
||||||
Additional components used in this project are copyright their respective owners.
|
Additional components used in this project are copyright their respective owners.
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<project xmlns="https://maven.apache.org/POM/4.0.0"
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>com.loomcom.symon</groupId>
|
<groupId>com.loomcom.symon</groupId>
|
||||||
<artifactId>symon</artifactId>
|
<artifactId>symon</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
<version>1.3.2</version>
|
<version>1.5.0</version>
|
||||||
<name>symon</name>
|
<name>symon</name>
|
||||||
<url>http://www.loomcom.com/symon</url>
|
<url>https://git.loomcom.com/seth/symon</url>
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>
|
<project.build.sourceEncoding>
|
||||||
UTF-8
|
UTF-8
|
||||||
@@ -15,11 +16,32 @@
|
|||||||
UTF-8
|
UTF-8
|
||||||
</project.reporting.outputEncoding>
|
</project.reporting.outputEncoding>
|
||||||
</properties>
|
</properties>
|
||||||
|
<repositories>
|
||||||
|
<repository>
|
||||||
|
<id>jitpack.io</id>
|
||||||
|
<url>https://jitpack.io</url>
|
||||||
|
</repository>
|
||||||
|
</repositories>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>ch.qos.logback</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>logback-classic</artifactId>
|
||||||
<version>1.2.3</version>
|
<version>1.5.16</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>commons-cli</groupId>
|
||||||
|
<artifactId>commons-cli</artifactId>
|
||||||
|
<version>1.5.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.formdev</groupId>
|
||||||
|
<artifactId>flatlaf</artifactId>
|
||||||
|
<version>3.7.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.Dansoftowner</groupId>
|
||||||
|
<artifactId>jSystemThemeDetector</artifactId>
|
||||||
|
<version>3.9.1</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>junit</groupId>
|
<groupId>junit</groupId>
|
||||||
@@ -30,14 +52,9 @@
|
|||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mockito-core</artifactId>
|
<artifactId>mockito-core</artifactId>
|
||||||
<version>3.12.4</version>
|
<version>4.11.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>commons-cli</groupId>
|
|
||||||
<artifactId>commons-cli</artifactId>
|
|
||||||
<version>1.4</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
@@ -48,7 +65,8 @@
|
|||||||
<version>3.2.0</version>
|
<version>3.2.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<transformers>
|
<transformers>
|
||||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
<transformer
|
||||||
|
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||||
<mainClass>com.loomcom.symon.Main</mainClass>
|
<mainClass>com.loomcom.symon.Main</mainClass>
|
||||||
</transformer>
|
</transformer>
|
||||||
</transformers>
|
</transformers>
|
||||||
@@ -79,8 +97,8 @@
|
|||||||
<version>3.7.0</version>
|
<version>3.7.0</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<compilerArgument>-Xlint:unchecked</compilerArgument>
|
<compilerArgument>-Xlint:unchecked</compilerArgument>
|
||||||
<source>1.7</source>
|
<source>11</source>
|
||||||
<target>1.7</target>
|
<target>11</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
@@ -120,4 +138,4 @@
|
|||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
@@ -11,7 +11,6 @@ Sample Programs
|
|||||||
When loaded at address $0300, this program will echo back to the console
|
When loaded at address $0300, this program will echo back to the console
|
||||||
anything typed.
|
anything typed.
|
||||||
|
|
||||||
|
|
||||||
Both hello.prg and echo_poll.prg were assembled with the Ophis assembler:
|
Both hello.prg and echo_poll.prg were assembled with the Ophis assembler:
|
||||||
|
|
||||||
https://hkn.eecs.berkeley.edu/~mcmartin/ophis/
|
https://hkn.eecs.berkeley.edu/~mcmartin/ophis/
|
||||||
@@ -19,23 +18,25 @@ Both hello.prg and echo_poll.prg were assembled with the Ophis assembler:
|
|||||||
3. echo_irq.rom
|
3. echo_irq.rom
|
||||||
|
|
||||||
This is another echo program, and behaves identically to echo_poll.prg,
|
This is another echo program, and behaves identically to echo_poll.prg,
|
||||||
except it is interrupt-driven.
|
except it is interrupt-driven. This ROM can be loaded either through the
|
||||||
|
"File->Load ROM..." menu in Symon, or by specifying the path to
|
||||||
|
"echo_irq.rom" with the "-jar" command line option when running Symon,
|
||||||
|
for example: `java -jar symon-1.3.2.jar -rom samples/echo_irq.rom`
|
||||||
|
|
||||||
4. ehbasic.rom
|
4. ehbasic.rom
|
||||||
|
|
||||||
This is Lee Davison's Enhanced 6502 BASIC.
|
This is Lee Davison's Enhanced 6502 BASIC.
|
||||||
|
|
||||||
To use this ROM image, just copy the file 'ehbasic.rom' into the directory
|
The EhBASIC ROM can be loaded either through the "File -> Load ROM..."
|
||||||
where you run Symon. Rename the file to 'rom.bin'. When you start Symon,
|
menu in Symon, or by specifying the path to "ehbasic.rom" with
|
||||||
the ROM file will be automatically loaded at address $d000.
|
the "-jar" command line option when running Symon, for example:
|
||||||
|
`java -jar symon-1.3.2.jar -rom samples/ehbasic.rom`
|
||||||
|
|
||||||
Click the "Run" button and EhBASIC should automatically start running.
|
Click the "Run" button and EhBASIC should automatically start running.
|
||||||
|
|
||||||
Type 'C' to do a cold start.
|
Type 'C' to do a cold start.
|
||||||
|
|
||||||
Then, type $C000 when prompted for the memory size.
|
Then, type '49152' (without the quotes) when prompted for the memory size.
|
||||||
|
|
||||||
NOTE: EhBASIC only wants upper-case input. This confused me at first!
|
|
||||||
|
|
||||||
More information can be found in the 'ehbasic' directory, and by visiting
|
More information can be found in the 'ehbasic' directory, and by visiting
|
||||||
the EhBASIC web page:
|
the EhBASIC web page:
|
||||||
|
|||||||
@@ -1,3 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008-2025 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;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
@@ -9,8 +32,8 @@ import java.util.TreeSet;
|
|||||||
|
|
||||||
public class Breakpoints extends AbstractTableModel {
|
public class Breakpoints extends AbstractTableModel {
|
||||||
|
|
||||||
private TreeSet<Integer> breakpoints;
|
private final TreeSet<Integer> breakpoints;
|
||||||
private Simulator simulator;
|
private final Simulator simulator;
|
||||||
|
|
||||||
public Breakpoints(Simulator simulator) {
|
public Breakpoints(Simulator simulator) {
|
||||||
this.breakpoints = new TreeSet<>();
|
this.breakpoints = new TreeSet<>();
|
||||||
@@ -26,11 +49,6 @@ public class Breakpoints extends AbstractTableModel {
|
|||||||
fireTableDataChanged();
|
fireTableDataChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeBreakpoint(int address) {
|
|
||||||
this.breakpoints.remove(address);
|
|
||||||
fireTableDataChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeBreakpointAtIndex(int index) {
|
public void removeBreakpointAtIndex(int index) {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -42,19 +42,19 @@ import java.util.TreeSet;
|
|||||||
public class Bus {
|
public class Bus {
|
||||||
|
|
||||||
// The default address at which to load programs
|
// The default address at which to load programs
|
||||||
public static int DEFAULT_LOAD_ADDRESS = 0x0200;
|
public static final int DEFAULT_LOAD_ADDRESS = 0x0200;
|
||||||
|
|
||||||
// By default, our bus starts at 0, and goes up to 64K
|
// By default, our bus starts at 0, and goes up to 64K
|
||||||
private int startAddress = 0x0000;
|
private final int startAddress;
|
||||||
private int endAddress = 0xffff;
|
private final int endAddress;
|
||||||
|
|
||||||
// The CPU
|
// The CPU
|
||||||
private Cpu cpu;
|
private Cpu cpu;
|
||||||
|
|
||||||
// Ordered sets of IO devices, associated with their priority
|
// Ordered sets of IO devices, associated with their priority
|
||||||
private Map<Integer, SortedSet<Device>> deviceMap;
|
private final Map<Integer, SortedSet<Device>> deviceMap;
|
||||||
|
|
||||||
// an array for quick lookup of adresses, brute-force style
|
// an array for quick lookup of addresses, brute-force style
|
||||||
private Device[] deviceAddressArray;
|
private Device[] deviceAddressArray;
|
||||||
|
|
||||||
|
|
||||||
@@ -94,8 +94,8 @@ public class Bus {
|
|||||||
* Add a device to the bus.
|
* Add a device to the bus.
|
||||||
*
|
*
|
||||||
* @param device Device to add
|
* @param device Device to add
|
||||||
* @param priority Bus prioirity.
|
* @param priority Bus priority.
|
||||||
* @throws MemoryRangeException
|
* @throws MemoryRangeException Invalid device address range.
|
||||||
*/
|
*/
|
||||||
public void addDevice(Device device, int priority) throws MemoryRangeException {
|
public void addDevice(Device device, int priority) throws MemoryRangeException {
|
||||||
|
|
||||||
@@ -109,12 +109,7 @@ public class Bus {
|
|||||||
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.computeIfAbsent(priority, k -> new TreeSet<>());
|
||||||
|
|
||||||
if (deviceSet == null) {
|
|
||||||
deviceSet = new TreeSet<>();
|
|
||||||
deviceMap.put(priority, deviceSet);
|
|
||||||
}
|
|
||||||
|
|
||||||
device.setBus(this);
|
device.setBus(this);
|
||||||
deviceSet.add(device);
|
deviceSet.add(device);
|
||||||
@@ -125,7 +120,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 Device to add
|
* @param device Device to add
|
||||||
* @throws MemoryRangeException
|
* @throws MemoryRangeException Invalid device memory range.
|
||||||
*/
|
*/
|
||||||
public void addDevice(Device device) throws MemoryRangeException {
|
public void addDevice(Device device) throws MemoryRangeException {
|
||||||
addDevice(device, 0);
|
addDevice(device, 0);
|
||||||
@@ -224,9 +219,7 @@ public class Bus {
|
|||||||
|
|
||||||
for (int priority : priorities) {
|
for (int priority : priorities) {
|
||||||
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
||||||
for (Device device : deviceSet) {
|
devices.addAll(deviceSet);
|
||||||
devices.add(device);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return devices;
|
return devices;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
@@ -32,7 +32,7 @@ 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.
|
||||||
* A simple interface allows this 6502 to read and write to a simulated bus,
|
* A simple interface allows this 6502 to read and write to a simulated bus,
|
||||||
* and exposes some of the internal state for inspection and debugging.
|
* and exposes some internal state for inspection and debugging.
|
||||||
*/
|
*/
|
||||||
public class Cpu implements InstructionTable {
|
public class Cpu implements InstructionTable {
|
||||||
|
|
||||||
@@ -126,7 +126,7 @@ public class Cpu implements InstructionTable {
|
|||||||
// Clear status register bits.
|
// Clear status register bits.
|
||||||
state.carryFlag = false;
|
state.carryFlag = false;
|
||||||
state.zeroFlag = false;
|
state.zeroFlag = false;
|
||||||
state.irqDisableFlag = false;
|
state.irqDisableFlag = true;
|
||||||
state.decimalModeFlag = false;
|
state.decimalModeFlag = false;
|
||||||
state.breakFlag = false;
|
state.breakFlag = false;
|
||||||
state.overflowFlag = false;
|
state.overflowFlag = false;
|
||||||
@@ -158,6 +158,8 @@ public class Cpu implements InstructionTable {
|
|||||||
* Performs an individual instruction cycle.
|
* Performs an individual instruction cycle.
|
||||||
*/
|
*/
|
||||||
public void step() throws MemoryAccessException {
|
public void step() throws MemoryAccessException {
|
||||||
|
int hi, lo; // Address calculation
|
||||||
|
|
||||||
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
|
||||||
@@ -283,7 +285,7 @@ public class Cpu implements InstructionTable {
|
|||||||
// Execute
|
// Execute
|
||||||
switch (state.ir) {
|
switch (state.ir) {
|
||||||
|
|
||||||
/** Single Byte Instructions; Implied and Relative **/
|
// Single Byte Instructions; Implied and Relative
|
||||||
case 0x00: // BRK - Force Interrupt - Implied
|
case 0x00: // BRK - Force Interrupt - Implied
|
||||||
handleBrk(state.pc + 1);
|
handleBrk(state.pc + 1);
|
||||||
break;
|
break;
|
||||||
@@ -317,8 +319,8 @@ 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());
|
||||||
int lo = stackPop();
|
lo = stackPop();
|
||||||
int hi = stackPop();
|
hi = stackPop();
|
||||||
setProgramCounter(Utils.address(lo, hi));
|
setProgramCounter(Utils.address(lo, hi));
|
||||||
break;
|
break;
|
||||||
case 0x48: // PHA - Push Accumulator - Implied
|
case 0x48: // PHA - Push Accumulator - Implied
|
||||||
@@ -457,7 +459,7 @@ public class Cpu implements InstructionTable {
|
|||||||
setArithmeticFlags(state.x);
|
setArithmeticFlags(state.x);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/** JMP *****************************************************************/
|
// JMP
|
||||||
case 0x4c: // JMP - Absolute
|
case 0x4c: // JMP - Absolute
|
||||||
state.pc = Utils.address(state.args[0], state.args[1]);
|
state.pc = Utils.address(state.args[0], state.args[1]);
|
||||||
break;
|
break;
|
||||||
@@ -495,7 +497,7 @@ public class Cpu implements InstructionTable {
|
|||||||
state.pc = Utils.address(bus.read(lo, true), bus.read(hi, true));
|
state.pc = Utils.address(bus.read(lo, true), bus.read(hi, true));
|
||||||
break;
|
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);
|
||||||
@@ -517,7 +519,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** ASL - Arithmetic Shift Left *****************************************/
|
// ASL - Arithmetic Shift Left
|
||||||
case 0x0a: // Accumulator
|
case 0x0a: // Accumulator
|
||||||
state.a = asl(state.a);
|
state.a = asl(state.a);
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
@@ -532,7 +534,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** BIT - Bit Test ******************************************************/
|
// BIT - Bit Test
|
||||||
case 0x89: // 65C02 #Immediate
|
case 0x89: // 65C02 #Immediate
|
||||||
setZeroFlag((state.a & state.args[0]) == 0);
|
setZeroFlag((state.a & state.args[0]) == 0);
|
||||||
break;
|
break;
|
||||||
@@ -551,7 +553,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** AND - Logical AND ***************************************************/
|
// AND - Logical AND
|
||||||
case 0x29: // #Immediate
|
case 0x29: // #Immediate
|
||||||
state.a &= state.args[0];
|
state.a &= state.args[0];
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
@@ -573,7 +575,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** ROL - Rotate Left ***************************************************/
|
// ROL - Rotate Left
|
||||||
case 0x2a: // Accumulator
|
case 0x2a: // Accumulator
|
||||||
state.a = rol(state.a);
|
state.a = rol(state.a);
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
@@ -588,7 +590,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** EOR - Exclusive OR **************************************************/
|
// EOR - Exclusive OR
|
||||||
case 0x49: // #Immediate
|
case 0x49: // #Immediate
|
||||||
state.a ^= state.args[0];
|
state.a ^= state.args[0];
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
@@ -610,7 +612,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** LSR - Logical Shift Right *******************************************/
|
// LSR - Logical Shift Right
|
||||||
case 0x4a: // Accumulator
|
case 0x4a: // Accumulator
|
||||||
state.a = lsr(state.a);
|
state.a = lsr(state.a);
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
@@ -625,7 +627,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** ADC - Add with Carry ************************************************/
|
// ADC - Add with Carry
|
||||||
case 0x69: // #Immediate
|
case 0x69: // #Immediate
|
||||||
if (state.decimalModeFlag) {
|
if (state.decimalModeFlag) {
|
||||||
state.a = adcDecimal(state.a, state.args[0]);
|
state.a = adcDecimal(state.a, state.args[0]);
|
||||||
@@ -653,7 +655,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** ROR - Rotate Right **************************************************/
|
// ROR - Rotate Right
|
||||||
case 0x6a: // Accumulator
|
case 0x6a: // Accumulator
|
||||||
state.a = ror(state.a);
|
state.a = ror(state.a);
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
@@ -668,7 +670,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** STA - Store Accumulator *********************************************/
|
// STA - Store Accumulator
|
||||||
case 0x92: // 65C02 STA (ZP)
|
case 0x92: // 65C02 STA (ZP)
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
@@ -685,7 +687,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** STY - Store Y Register **********************************************/
|
// STY - Store Y Register
|
||||||
case 0x84: // Zero Page
|
case 0x84: // Zero Page
|
||||||
case 0x8c: // Absolute
|
case 0x8c: // Absolute
|
||||||
case 0x94: // Zero Page,X
|
case 0x94: // Zero Page,X
|
||||||
@@ -693,14 +695,14 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** STX - Store X Register **********************************************/
|
// STX - Store X Register
|
||||||
case 0x86: // Zero Page
|
case 0x86: // Zero Page
|
||||||
case 0x8e: // Absolute
|
case 0x8e: // Absolute
|
||||||
case 0x96: // Zero Page,Y
|
case 0x96: // Zero Page,Y
|
||||||
bus.write(effectiveAddress, state.x);
|
bus.write(effectiveAddress, state.x);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/** STZ - 65C02 Store Zero ****************************************************/
|
// STZ - 65C02 Store Zero
|
||||||
case 0x64: // Zero Page
|
case 0x64: // Zero Page
|
||||||
case 0x74: // Zero Page,X
|
case 0x74: // Zero Page,X
|
||||||
case 0x9c: // Absolute
|
case 0x9c: // Absolute
|
||||||
@@ -712,7 +714,7 @@ public class Cpu implements InstructionTable {
|
|||||||
bus.write(effectiveAddress, 0);
|
bus.write(effectiveAddress, 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/** LDY - Load Y Register ***********************************************/
|
// LDY - Load Y Register
|
||||||
case 0xa0: // #Immediate
|
case 0xa0: // #Immediate
|
||||||
state.y = state.args[0];
|
state.y = state.args[0];
|
||||||
setArithmeticFlags(state.y);
|
setArithmeticFlags(state.y);
|
||||||
@@ -726,7 +728,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** LDX - Load X Register ***********************************************/
|
// LDX - Load X Register
|
||||||
case 0xa2: // #Immediate
|
case 0xa2: // #Immediate
|
||||||
state.x = state.args[0];
|
state.x = state.args[0];
|
||||||
setArithmeticFlags(state.x);
|
setArithmeticFlags(state.x);
|
||||||
@@ -740,7 +742,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** LDA - Load Accumulator **********************************************/
|
// LDA - Load Accumulator
|
||||||
case 0xa9: // #Immediate
|
case 0xa9: // #Immediate
|
||||||
state.a = state.args[0];
|
state.a = state.args[0];
|
||||||
setArithmeticFlags(state.a);
|
setArithmeticFlags(state.a);
|
||||||
@@ -762,7 +764,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** CPY - Compare Y Register ********************************************/
|
// CPY - Compare Y Register
|
||||||
case 0xc0: // #Immediate
|
case 0xc0: // #Immediate
|
||||||
cmp(state.y, state.args[0]);
|
cmp(state.y, state.args[0]);
|
||||||
break;
|
break;
|
||||||
@@ -772,7 +774,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** CMP - Compare Accumulator *******************************************/
|
// CMP - Compare Accumulator
|
||||||
case 0xc9: // #Immediate
|
case 0xc9: // #Immediate
|
||||||
cmp(state.a, state.args[0]);
|
cmp(state.a, state.args[0]);
|
||||||
break;
|
break;
|
||||||
@@ -792,7 +794,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** DEC - Decrement Memory **********************************************/
|
// DEC - Decrement Memory
|
||||||
case 0x3a: // 65C02 Immediate
|
case 0x3a: // 65C02 Immediate
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
@@ -811,7 +813,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** CPX - Compare X Register ********************************************/
|
// CPX - Compare X Register
|
||||||
case 0xe0: // #Immediate
|
case 0xe0: // #Immediate
|
||||||
cmp(state.x, state.args[0]);
|
cmp(state.x, state.args[0]);
|
||||||
break;
|
break;
|
||||||
@@ -821,7 +823,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** SBC - Subtract with Carry (Borrow) **********************************/
|
// SBC - Subtract with Carry (Borrow)
|
||||||
case 0xe9: // #Immediate
|
case 0xe9: // #Immediate
|
||||||
if (state.decimalModeFlag) {
|
if (state.decimalModeFlag) {
|
||||||
state.a = sbcDecimal(state.a, state.args[0]);
|
state.a = sbcDecimal(state.a, state.args[0]);
|
||||||
@@ -849,7 +851,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** INC - Increment Memory **********************************************/
|
// INC - Increment Memory
|
||||||
case 0x1a: // 65C02 Increment Immediate
|
case 0x1a: // 65C02 Increment Immediate
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
@@ -868,14 +870,14 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** 65C02 RMB - Reset Memory Bit **************************************/
|
// 65C02 RMB - Reset Memory Bit
|
||||||
case 0x07: // 65C02 RMB0 - Zero Page
|
case 0x07: // 65C02 RMB0 - Zero Page
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true) & 0xff;
|
tmp = bus.read(effectiveAddress, true) & 0xff;
|
||||||
tmp &= ~(1 << 0);
|
tmp &= ~1;
|
||||||
bus.write(effectiveAddress, tmp);
|
bus.write(effectiveAddress, tmp);
|
||||||
break;
|
break;
|
||||||
case 0x17: // 65C02 RMB1 - Zero Page
|
case 0x17: // 65C02 RMB1 - Zero Page
|
||||||
@@ -943,14 +945,14 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** 65C02 SMB - Set Memory Bit **************************************/
|
// 65C02 SMB - Set Memory Bit
|
||||||
case 0x87: // 65C02 SMB0 - Zero Page
|
case 0x87: // 65C02 SMB0 - Zero Page
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true) & 0xff;
|
tmp = bus.read(effectiveAddress, true) & 0xff;
|
||||||
tmp |= (1);
|
tmp |= 1;
|
||||||
bus.write(effectiveAddress, tmp);
|
bus.write(effectiveAddress, tmp);
|
||||||
break;
|
break;
|
||||||
case 0x97: // 65C02 SMB1 - Zero Page
|
case 0x97: // 65C02 SMB1 - Zero Page
|
||||||
@@ -1017,7 +1019,7 @@ public class Cpu implements InstructionTable {
|
|||||||
bus.write(effectiveAddress, tmp);
|
bus.write(effectiveAddress, tmp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/** 65C02 TRB/TSB - Test and Reset Bit/Test and Set Bit ***************/
|
// 65C02 TRB/TSB - Test and Reset Bit/Test and Set Bit
|
||||||
case 0x14: // 65C02 TRB - Test and Reset bit - Zero Page
|
case 0x14: // 65C02 TRB - Test and Reset bit - Zero Page
|
||||||
case 0x1c: // 65C02 TRB - Test and Reset bit - Absolute
|
case 0x1c: // 65C02 TRB - Test and Reset bit - Absolute
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
@@ -1026,8 +1028,9 @@ public class Cpu implements InstructionTable {
|
|||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
setZeroFlag((state.a & tmp) == 0);
|
setZeroFlag((state.a & tmp) == 0);
|
||||||
tmp = (tmp &= ~(state.a)) & 0xff;
|
tmp &= ~(state.a);
|
||||||
bus.write(effectiveAddress,tmp);
|
tmp &= 0xff;
|
||||||
|
bus.write(effectiveAddress, tmp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x04: // 65C02 TSB - Test and Set bit - Zero Page
|
case 0x04: // 65C02 TSB - Test and Set bit - Zero Page
|
||||||
@@ -1038,18 +1041,19 @@ public class Cpu implements InstructionTable {
|
|||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
setZeroFlag((state.a & tmp) == 0);
|
setZeroFlag((state.a & tmp) == 0);
|
||||||
tmp = (tmp |= (state.a)) & 0xff;
|
tmp |= state.a;
|
||||||
|
tmp = tmp & 0xff;
|
||||||
bus.write(effectiveAddress,tmp);
|
bus.write(effectiveAddress,tmp);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/** 65C02 BBR - Branch if Bit Reset *************************************/
|
// 65C02 BBR - Branch if Bit Reset
|
||||||
case 0x0f: // 65C02 BBR - Branch if bit 0 reset - Zero Page
|
case 0x0f: // 65C02 BBR - Branch if bit 0 reset - Zero Page
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 0) == 0) {
|
if ((tmp & 1) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1060,7 +1064,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 1) == 0) {
|
if ((tmp & (1 << 1)) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1071,7 +1075,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 2) == 0) {
|
if ((tmp & (1 << 2)) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1082,7 +1086,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 3) == 0) {
|
if ((tmp & (1 << 3)) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1093,7 +1097,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 4) == 0) {
|
if ((tmp & (1 << 4)) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1105,7 +1109,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 5) == 0) {
|
if ((tmp & (1 << 5)) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1116,7 +1120,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 6) == 0) {
|
if ((tmp & (1 << 6)) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1127,20 +1131,20 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 7) == 0) {
|
if ((tmp & (1 << 7)) == 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** 65C02 BBS - Branch if Bit Set ************************************/
|
// 65C02 BBS - Branch if Bit Set
|
||||||
case 0x8f: // 65C02 BBS - Branch if bit 0 set - Zero Page
|
case 0x8f: // 65C02 BBS - Branch if bit 0 set - Zero Page
|
||||||
if (behavior == CpuBehavior.NMOS_6502 ||
|
if (behavior == CpuBehavior.NMOS_6502 ||
|
||||||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 0) > 0) {
|
if ((tmp & 1) != 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1151,7 +1155,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 1) > 0) {
|
if ((tmp & (1 << 1)) != 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1162,7 +1166,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 2) > 0) {
|
if ((tmp & (1 << 2)) > 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1173,7 +1177,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 3) > 0) {
|
if ((tmp & (1 << 3)) > 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1185,7 +1189,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 4) > 0) {
|
if ((tmp & (1 << 4)) > 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1197,7 +1201,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 5) > 0) {
|
if ((tmp & (1 << 5)) > 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1208,7 +1212,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 6) > 0) {
|
if ((tmp & (1 << 6)) > 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -1219,13 +1223,13 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
tmp = bus.read(effectiveAddress, true);
|
tmp = bus.read(effectiveAddress, true);
|
||||||
if ((tmp & 1 << 7) > 0) {
|
if ((tmp & (1 << 7)) > 0) {
|
||||||
state.pc = relAddress(state.args[1]);
|
state.pc = relAddress(state.args[1]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
||||||
/** Unimplemented Instructions ****************************************/
|
// Unimplemented Instructions
|
||||||
// TODO: Create a flag to enable highly-accurate emulation of unimplemented instructions.
|
// TODO: Create a flag to enable highly-accurate emulation of unimplemented instructions.
|
||||||
default:
|
default:
|
||||||
setOpTrap();
|
setOpTrap();
|
||||||
@@ -1234,7 +1238,7 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
delayLoop(state.ir);
|
delayLoop(state.ir);
|
||||||
|
|
||||||
// Peek ahead to the next insturction and arguments
|
// Peek ahead to the next instruction and arguments
|
||||||
peekAhead();
|
peekAhead();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1265,7 +1269,7 @@ public class Cpu implements InstructionTable {
|
|||||||
/**
|
/**
|
||||||
* Handle the common behavior of BRK, /IRQ, and /NMI
|
* Handle the common behavior of BRK, /IRQ, and /NMI
|
||||||
*
|
*
|
||||||
* @throws MemoryAccessException
|
* @throws MemoryAccessException on memory access failure
|
||||||
*/
|
*/
|
||||||
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh, boolean isBreak) throws MemoryAccessException {
|
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh, boolean isBreak) throws MemoryAccessException {
|
||||||
|
|
||||||
@@ -1674,7 +1678,7 @@ 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,
|
// As a side effect of setting the program counter,
|
||||||
// we want to peek ahead at the next state.
|
// we want to peek ahead at the next state.
|
||||||
try {
|
try {
|
||||||
peekAhead();
|
peekAhead();
|
||||||
@@ -1695,6 +1699,10 @@ public class Cpu implements InstructionTable {
|
|||||||
return state.ir;
|
return state.ir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNextIr() {
|
||||||
|
return state.nextIr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @value The value of the Process Status Register bits to be set.
|
* @value The value of the Process Status Register bits to be set.
|
||||||
*/
|
*/
|
||||||
@@ -1921,6 +1929,7 @@ public class Cpu implements InstructionTable {
|
|||||||
break;
|
break;
|
||||||
case AIX:
|
case AIX:
|
||||||
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X)");
|
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X)");
|
||||||
|
break;
|
||||||
case ABX:
|
case ABX:
|
||||||
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X");
|
sb.append(" $").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(",X");
|
||||||
break;
|
break;
|
||||||
@@ -1933,6 +1942,9 @@ public class Cpu implements InstructionTable {
|
|||||||
case IND:
|
case IND:
|
||||||
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(")");
|
sb.append(" ($").append(Utils.wordToHex(Utils.address(args[0], args[1]))).append(")");
|
||||||
break;
|
break;
|
||||||
|
case ZPI:
|
||||||
|
sb.append(" ($").append(Utils.byteToHex(args[0])).append(")");
|
||||||
|
break;
|
||||||
case XIN:
|
case XIN:
|
||||||
sb.append(" ($").append(Utils.byteToHex(args[0])).append(",X)");
|
sb.append(" ($").append(Utils.byteToHex(args[0])).append(",X)");
|
||||||
break;
|
break;
|
||||||
@@ -1971,7 +1983,7 @@ public class Cpu implements InstructionTable {
|
|||||||
*/
|
*/
|
||||||
public String disassembleOpAtAddress(int address) throws MemoryAccessException {
|
public String disassembleOpAtAddress(int address) throws MemoryAccessException {
|
||||||
int opCode = bus.read(address, true);
|
int opCode = bus.read(address, true);
|
||||||
int args[] = new int[2];
|
int[] args = new int[2];
|
||||||
int size = Cpu.instructionSizes[opCode];
|
int size = Cpu.instructionSizes[opCode];
|
||||||
for (int i = 1; i < size; i++) {
|
for (int i = 1; i < size; i++) {
|
||||||
int nextRead = (address + i) % bus.endAddress();
|
int nextRead = (address + i) % bus.endAddress();
|
||||||
|
|||||||
@@ -1,3 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008-2025 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;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
import com.loomcom.symon.util.Utils;
|
import com.loomcom.symon.util.Utils;
|
||||||
@@ -12,7 +35,7 @@ public class CpuState {
|
|||||||
public int a;
|
public int a;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* X index regsiter
|
* X index register
|
||||||
*/
|
*/
|
||||||
public int x;
|
public int x;
|
||||||
|
|
||||||
@@ -61,13 +84,12 @@ public class CpuState {
|
|||||||
public CpuState() {}
|
public CpuState() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Snapshot a copy of the 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)
|
||||||
* (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.
|
* @param s The CpuState to copy.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("CopyConstructorMissesField")
|
||||||
public CpuState(CpuState s) {
|
public CpuState(CpuState s) {
|
||||||
this.a = s.a;
|
this.a = s.a;
|
||||||
this.x = s.x;
|
this.x = s.x;
|
||||||
@@ -83,6 +105,7 @@ public class CpuState {
|
|||||||
this.nextArgs[1] = s.nextArgs[1];
|
this.nextArgs[1] = s.nextArgs[1];
|
||||||
this.instSize = s.instSize;
|
this.instSize = s.instSize;
|
||||||
this.opTrap = s.opTrap;
|
this.opTrap = s.opTrap;
|
||||||
|
this.nmiAsserted = s.nmiAsserted;
|
||||||
this.irqAsserted = s.irqAsserted;
|
this.irqAsserted = s.irqAsserted;
|
||||||
this.carryFlag = s.carryFlag;
|
this.carryFlag = s.carryFlag;
|
||||||
this.negativeFlag = s.negativeFlag;
|
this.negativeFlag = s.negativeFlag;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,26 +25,24 @@ package com.loomcom.symon;
|
|||||||
|
|
||||||
public interface InstructionTable {
|
public interface InstructionTable {
|
||||||
|
|
||||||
|
int BRK = 0x00;
|
||||||
|
int JSR = 0x20;
|
||||||
|
int RTS = 0x60;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enumeration of valid CPU behaviors. These determine what behavior and instruction
|
* Enumeration of valid CPU behaviors. These determine what behavior and instruction
|
||||||
* set will be simulated, depending on desired version of 6502.
|
* set will be simulated, depending on desired version of 6502.
|
||||||
*
|
|
||||||
* TODO: As of version 0.6, this is still not used! All CPUs are "idealized" NMOS 6502 only.
|
|
||||||
*/
|
*/
|
||||||
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
|
||||||
* is very rare in the wild.
|
* is very rare in the wild.
|
||||||
*
|
|
||||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
|
||||||
*/
|
*/
|
||||||
NMOS_WITH_ROR_BUG,
|
NMOS_WITH_ROR_BUG,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All NMOS 6502's have a bug with the indirect JMP instruction. If the
|
* All NMOS 6502's have a bug with the indirect JMP instruction. If the
|
||||||
*
|
|
||||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
|
||||||
*/
|
*/
|
||||||
NMOS_6502,
|
NMOS_6502,
|
||||||
|
|
||||||
@@ -68,11 +66,13 @@ public interface InstructionTable {
|
|||||||
return "Accumulator";
|
return "Accumulator";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
AIX {
|
AIX {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Absolute, X-Indexed Indirect";
|
return "Absolute, X-Indexed Indirect";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
ABS {
|
ABS {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Absolute";
|
return "Absolute";
|
||||||
@@ -168,41 +168,44 @@ public interface InstructionTable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Instruction opcode names. This lists all opcodes for
|
* Instruction opcode names. This lists all opcodes for
|
||||||
* NMOS 6502, CMOS 65C02, and CMOS 65C816
|
* NMOS 6502, CMOS 65C02, and CMOS 65C816.
|
||||||
|
* <p>
|
||||||
|
* TODO (2025-03-14): This table is clearly wrong for 65C816.
|
||||||
|
* TODO (2025-03-14): Rockwell/WDC 65C02 names conflict with 65C816 names.
|
||||||
*/
|
*/
|
||||||
String[] opcodeNames = {
|
String[] opcodeNames = {
|
||||||
"BRK", "ORA", "NOP", "NOP", "TSB", "ORA", "ASL", "RMB0", // 0x00-0x07
|
"BRK", "ORA", "NOP", "NOP", "TSB", "ORA", "ASL", "RMB0", // 0x00-0x07
|
||||||
"PHP", "ORA", "ASL", "NOP", "TSB", "ORA", "ASL", "BBR0", // 0x08-0x0f
|
"PHP", "ORA", "ASL", "NOP", "TSB", "ORA", "ASL", "BBR0", // 0x08-0x0f
|
||||||
"BPL", "ORA", "ORA", "NOP", "TRB", "ORA", "ASL", "RMB1", // 0x10-0x17
|
"BPL", "ORA", "ORA", "NOP", "TRB", "ORA", "ASL", "RMB1", // 0x10-0x17
|
||||||
"CLC", "ORA", "INC", "NOP", "TRB", "ORA", "ASL", "BBR1", // 0x18-0x1f
|
"CLC", "ORA", "INC", "NOP", "TRB", "ORA", "ASL", "BBR1", // 0x18-0x1f
|
||||||
"JSR", "AND", "NOP", "NOP", "BIT", "AND", "ROL", "RMB2", // 0x20-0x27
|
"JSR", "AND", "NOP", "NOP", "BIT", "AND", "ROL", "RMB2", // 0x20-0x27
|
||||||
"PLP", "AND", "ROL", "NOP", "BIT", "AND", "ROL", "BBR2", // 0x28-0x2f
|
"PLP", "AND", "ROL", "NOP", "BIT", "AND", "ROL", "BBR2", // 0x28-0x2f
|
||||||
"BMI", "AND", "AND", "NOP", "BIT", "AND", "ROL", "RMB3", // 0x30-0x37
|
"BMI", "AND", "AND", "NOP", "BIT", "AND", "ROL", "RMB3", // 0x30-0x37
|
||||||
"SEC", "AND", "DEC", "NOP", "BIT", "AND", "ROL", "BBR3", // 0x38-0x3f
|
"SEC", "AND", "DEC", "NOP", "BIT", "AND", "ROL", "BBR3", // 0x38-0x3f
|
||||||
"RTI", "EOR", "NOP", "NOP", "NOP", "EOR", "LSR", "RMB4", // 0x40-0x47
|
"RTI", "EOR", "NOP", "NOP", "NOP", "EOR", "LSR", "RMB4", // 0x40-0x47
|
||||||
"PHA", "EOR", "LSR", "NOP", "JMP", "EOR", "LSR", "BBR4", // 0x48-0x4f
|
"PHA", "EOR", "LSR", "NOP", "JMP", "EOR", "LSR", "BBR4", // 0x48-0x4f
|
||||||
"BVC", "EOR", "EOR", "NOP", "NOP", "EOR", "LSR", "RMB5", // 0x50-0x57
|
"BVC", "EOR", "EOR", "NOP", "NOP", "EOR", "LSR", "RMB5", // 0x50-0x57
|
||||||
"CLI", "EOR", "PHY", "NOP", "NOP", "EOR", "LSR", "BBR5", // 0x58-0x5f
|
"CLI", "EOR", "PHY", "NOP", "NOP", "EOR", "LSR", "BBR5", // 0x58-0x5f
|
||||||
"RTS", "ADC", "NOP", "NOP", "STZ", "ADC", "ROR", "RMB6", // 0x60-0x67
|
"RTS", "ADC", "NOP", "NOP", "STZ", "ADC", "ROR", "RMB6", // 0x60-0x67
|
||||||
"PLA", "ADC", "ROR", "NOP", "JMP", "ADC", "ROR", "BBR6", // 0x68-0x6f
|
"PLA", "ADC", "ROR", "NOP", "JMP", "ADC", "ROR", "BBR6", // 0x68-0x6f
|
||||||
"BVS", "ADC", "ADC", "NOP", "STZ", "ADC", "ROR", "RMB7", // 0x70-0x77
|
"BVS", "ADC", "ADC", "NOP", "STZ", "ADC", "ROR", "RMB7", // 0x70-0x77
|
||||||
"SEI", "ADC", "PLY", "NOP", "JMP", "ADC", "ROR", "BBR7", // 0x78-0x7f
|
"SEI", "ADC", "PLY", "NOP", "JMP", "ADC", "ROR", "BBR7", // 0x78-0x7f
|
||||||
"BRA", "STA", "NOP", "NOP", "STY", "STA", "STX", "SMB0", // 0x80-0x87
|
"BRA", "STA", "NOP", "NOP", "STY", "STA", "STX", "SMB0", // 0x80-0x87
|
||||||
"DEY", "BIT", "TXA", "NOP", "STY", "STA", "STX", "BBS0", // 0x88-0x8f
|
"DEY", "BIT", "TXA", "NOP", "STY", "STA", "STX", "BBS0", // 0x88-0x8f
|
||||||
"BCC", "STA", "STA", "NOP", "STY", "STA", "STX", "SMB1", // 0x90-0x97
|
"BCC", "STA", "STA", "NOP", "STY", "STA", "STX", "SMB1", // 0x90-0x97
|
||||||
"TYA", "STA", "TXS", "NOP", "STZ", "STA", "STZ", "BBS1", // 0x98-0x9f
|
"TYA", "STA", "TXS", "NOP", "STZ", "STA", "STZ", "BBS1", // 0x98-0x9f
|
||||||
"LDY", "LDA", "LDX", "NOP", "LDY", "LDA", "LDX", "SMB2", // 0xa0-0xa7
|
"LDY", "LDA", "LDX", "NOP", "LDY", "LDA", "LDX", "SMB2", // 0xa0-0xa7
|
||||||
"TAY", "LDA", "TAX", "NOP", "LDY", "LDA", "LDX", "BBS2", // 0xa8-0xaf
|
"TAY", "LDA", "TAX", "NOP", "LDY", "LDA", "LDX", "BBS2", // 0xa8-0xaf
|
||||||
"BCS", "LDA", "LDA", "NOP", "LDY", "LDA", "LDX", "SMB3", // 0xb0-0xb7
|
"BCS", "LDA", "LDA", "NOP", "LDY", "LDA", "LDX", "SMB3", // 0xb0-0xb7
|
||||||
"CLV", "LDA", "TSX", "NOP", "LDY", "LDA", "LDX", "BBS3", // 0xb8-0xbf
|
"CLV", "LDA", "TSX", "NOP", "LDY", "LDA", "LDX", "BBS3", // 0xb8-0xbf
|
||||||
"CPY", "CMP", "NOP", "NOP", "CPY", "CMP", "DEC", "SMB4", // 0xc0-0xc7
|
"CPY", "CMP", "NOP", "NOP", "CPY", "CMP", "DEC", "SMB4", // 0xc0-0xc7
|
||||||
"INY", "CMP", "DEX", "NOP", "CPY", "CMP", "DEC", "BBS4", // 0xc8-0xcf
|
"INY", "CMP", "DEX", "NOP", "CPY", "CMP", "DEC", "BBS4", // 0xc8-0xcf
|
||||||
"BNE", "CMP", "CMP", "NOP", "NOP", "CMP", "DEC", "SMB5", // 0xd0-0xd7
|
"BNE", "CMP", "CMP", "NOP", "NOP", "CMP", "DEC", "SMB5", // 0xd0-0xd7
|
||||||
"CLD", "CMP", "PHX", "NOP", "NOP", "CMP", "DEC", "BBS5", // 0xd8-0xdf
|
"CLD", "CMP", "PHX", "NOP", "NOP", "CMP", "DEC", "BBS5", // 0xd8-0xdf
|
||||||
"CPX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "SMB6", // 0xe0-0xe7
|
"CPX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "SMB6", // 0xe0-0xe7
|
||||||
"INX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "BBS6", // 0xe8-0xef
|
"INX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "BBS6", // 0xe8-0xef
|
||||||
"BEQ", "SBC", "SBC", "NOP", "NOP", "SBC", "INC", "SMB7", // 0xf0-0xf7
|
"BEQ", "SBC", "SBC", "NOP", "NOP", "SBC", "INC", "SMB7", // 0xf0-0xf7
|
||||||
"SED", "SBC", "PLX", "NOP", "NOP", "SBC", "INC", "BBS7" // 0xf8-0xff
|
"SED", "SBC", "PLX", "NOP", "NOP", "SBC", "INC", "BBS7" // 0xf8-0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -22,43 +22,54 @@
|
|||||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
package com.loomcom.symon;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
import com.loomcom.symon.machines.MulticompMachine;
|
import com.loomcom.symon.machines.*;
|
||||||
import com.loomcom.symon.machines.SimpleMachine;
|
import com.loomcom.symon.ui.Ui;
|
||||||
import com.loomcom.symon.machines.SymonMachine;
|
|
||||||
import org.apache.commons.cli.*;
|
import org.apache.commons.cli.*;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
import javax.swing.UIManager;
|
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(Main.class.getName());
|
||||||
/**
|
|
||||||
|
/**
|
||||||
* 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 Program arguments
|
* @param args Program arguments
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
|
|
||||||
Class machineClass = SymonMachine.class;
|
Class<?> machineClass = null;
|
||||||
|
|
||||||
Options options = new Options();
|
Options options = new Options();
|
||||||
|
|
||||||
|
options.addOption(new Option("h", "help", false, "Print this message."));
|
||||||
options.addOption(new Option("m", "machine", true, "Specify machine type."));
|
options.addOption(new Option("m", "machine", true, "Specify machine type."));
|
||||||
options.addOption(new Option("c", "cpu", true, "Specify CPU type."));
|
options.addOption(new Option("c", "cpu", true, "Specify CPU type."));
|
||||||
options.addOption(new Option("r", "rom", true, "Specify ROM file."));
|
options.addOption(new Option("r", "rom", true, "Specify ROM file."));
|
||||||
|
options.addOption(new Option("b", "brk", false, "Halt on BRK"));
|
||||||
|
|
||||||
CommandLineParser parser = new DefaultParser();
|
CommandLineParser parser = new DefaultParser();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
CommandLine line = parser.parse(options, args);
|
CommandLine line = parser.parse(options, args);
|
||||||
InstructionTable.CpuBehavior cpuBehavior = null;
|
InstructionTable.CpuBehavior cpuBehavior = null;
|
||||||
String romFile = null;
|
String romFile = null;
|
||||||
|
boolean haltOnBreak = false;
|
||||||
|
|
||||||
|
if (line.hasOption("help")) {
|
||||||
|
HelpFormatter formatter = new HelpFormatter();
|
||||||
|
formatter.printHelp("symon", options, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (line.hasOption("machine")) {
|
if (line.hasOption("machine")) {
|
||||||
String machine = line.getOptionValue("machine").toLowerCase(Locale.ENGLISH);
|
String machine = line.getOptionValue("machine").toLowerCase(Locale.ENGLISH);
|
||||||
@@ -72,8 +83,11 @@ public class Main {
|
|||||||
case "symon":
|
case "symon":
|
||||||
machineClass = SymonMachine.class;
|
machineClass = SymonMachine.class;
|
||||||
break;
|
break;
|
||||||
|
case "beneater":
|
||||||
|
machineClass = BenEaterMachine.class;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
System.err.println("Could not start Symon. Unknown machine type " + machine);
|
logger.error("Could not start Symon. Unknown machine type {}", machine);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +105,7 @@ public class Main {
|
|||||||
cpuBehavior = InstructionTable.CpuBehavior.CMOS_65816;
|
cpuBehavior = InstructionTable.CpuBehavior.CMOS_65816;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.err.println("Could not start Symon. Unknown cpu type " + cpu);
|
logger.error("Could not start Symon. Unknown cpu type {}", cpu);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,48 +114,33 @@ public class Main {
|
|||||||
romFile = line.getOptionValue("rom");
|
romFile = line.getOptionValue("rom");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (line.hasOption("brk")) {
|
||||||
|
haltOnBreak = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui.initUi();
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (machineClass == null) {
|
if (machineClass == null) {
|
||||||
Object[] possibilities = {"Symon", "Multicomp", "Simple"};
|
machineClass = Ui.showMachineSelectionDialog();
|
||||||
String s = (String)JOptionPane.showInputDialog(
|
|
||||||
null,
|
|
||||||
"Please choose the machine type to be emulated:",
|
|
||||||
"Machine selection",
|
|
||||||
JOptionPane.PLAIN_MESSAGE,
|
|
||||||
null,
|
|
||||||
possibilities,
|
|
||||||
"Symon");
|
|
||||||
|
|
||||||
|
|
||||||
if (s != null && s.equals("Multicomp")) {
|
|
||||||
machineClass = MulticompMachine.class;
|
|
||||||
} else if (s != null && s.equals("Simple")) {
|
|
||||||
machineClass = SimpleMachine.class;
|
|
||||||
} else {
|
|
||||||
machineClass = SymonMachine.class;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cpuBehavior == null) {
|
if (cpuBehavior == null) {
|
||||||
cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502;
|
cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile);
|
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile, haltOnBreak);
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
// Create the main UI window:
|
||||||
public void run() {
|
try {
|
||||||
try {
|
simulator.createAndShowUi();
|
||||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
} catch (Exception e) {
|
||||||
// Create the main UI window
|
logger.error("Error during Symon UI initialization: {}", e.getMessage());
|
||||||
simulator.createAndShowUi();
|
System.exit(-1);
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Simulator.MainCommand cmd = simulator.waitForCommand();
|
Simulator.MainCommand cmd = simulator.waitForCommand();
|
||||||
|
|
||||||
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
|
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
|
||||||
@@ -151,7 +150,8 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ParseException ex) {
|
} catch (ParseException ex) {
|
||||||
System.err.println("Could not start Symon. Reason: " + ex.getMessage());
|
logger.error("Could not start Symon. Reason: {}", ex.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,7 +27,7 @@ import com.loomcom.symon.exceptions.*;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* MemoryRange is a simple container class representing a literal
|
* MemoryRange is a simple container class representing a literal
|
||||||
* range of memory, with a staraddress, and an end address. It has
|
* range of memory, with a start address, and an end address. It has
|
||||||
* guards against creating impossible memory ranges, and implements
|
* guards against creating impossible memory ranges, and implements
|
||||||
* some useful methods for checking address inclusion and range
|
* some useful methods for checking address inclusion and range
|
||||||
* overlaps.
|
* overlaps.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -29,8 +29,6 @@ public interface Preferences {
|
|||||||
|
|
||||||
int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
|
int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
|
||||||
|
|
||||||
boolean DEFAULT_HALT_ON_BREAK = true;
|
|
||||||
|
|
||||||
JDialog getDialog();
|
JDialog getDialog();
|
||||||
|
|
||||||
int getProgramStartAddress();
|
int getProgramStartAddress();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyrighi (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,14 +31,13 @@ import com.loomcom.symon.ui.Console;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.management.monitor.CounterMonitor;
|
|
||||||
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.SortedSet;
|
|
||||||
import java.util.TreeSet;
|
import static com.loomcom.symon.InstructionTable.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Symon Simulator Interface and Control.
|
* Symon Simulator Interface and Control.
|
||||||
@@ -72,7 +71,7 @@ public class Simulator {
|
|||||||
private static final int MAX_STEPS_BETWEEN_UPDATES = 20000;
|
private static final int MAX_STEPS_BETWEEN_UPDATES = 20000;
|
||||||
|
|
||||||
// The simulated machine
|
// The simulated machine
|
||||||
private Machine machine;
|
private final 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.
|
||||||
@@ -115,17 +114,20 @@ public class Simulator {
|
|||||||
|
|
||||||
private JButton runStopButton;
|
private JButton runStopButton;
|
||||||
private JButton stepButton;
|
private JButton stepButton;
|
||||||
|
private JButton stepOverButton;
|
||||||
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 Breakpoints breakpoints;
|
||||||
|
|
||||||
private final Object commandMonitorObject = new Object();
|
private final Object commandMonitorObject = new Object();
|
||||||
|
|
||||||
private MainCommand command = MainCommand.NONE;
|
private MainCommand command = MainCommand.NONE;
|
||||||
|
|
||||||
|
private final boolean haltOnBreak;
|
||||||
|
|
||||||
public enum MainCommand {
|
public enum MainCommand {
|
||||||
NONE,
|
NONE,
|
||||||
SELECTMACHINE
|
SELECTMACHINE
|
||||||
@@ -136,17 +138,18 @@ 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(machineClass, InstructionTable.CpuBehavior.NMOS_6502, null);
|
this(machineClass, InstructionTable.CpuBehavior.NMOS_6502, null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Simulator(Class machineClass, InstructionTable.CpuBehavior cpuType, String romFile) throws Exception {
|
public Simulator(Class<?> machineClass, InstructionTable.CpuBehavior cpuType,
|
||||||
|
String romFile, boolean haltOnBreak) throws Exception {
|
||||||
|
this.haltOnBreak = haltOnBreak;
|
||||||
this.breakpoints = new Breakpoints(this);
|
this.breakpoints = new Breakpoints(this);
|
||||||
|
|
||||||
this.machine = (Machine) machineClass.getConstructors()[0].newInstance(romFile);
|
this.machine = (Machine) machineClass.getConstructors()[0].newInstance(romFile);
|
||||||
this.machine.getCpu().setBehavior(cpuType);
|
this.machine.getCpu().setBehavior(cpuType);
|
||||||
|
|
||||||
|
|
||||||
// Initialize final fields in the constructor.
|
// Initialize final fields in the constructor.
|
||||||
this.traceLog = new TraceLog();
|
this.traceLog = new TraceLog();
|
||||||
this.memoryWindow = new MemoryWindow(machine.getBus());
|
this.memoryWindow = new MemoryWindow(machine.getBus());
|
||||||
@@ -162,7 +165,7 @@ public class Simulator {
|
|||||||
/**
|
/**
|
||||||
* Display the main simulator UI.
|
* Display the main simulator UI.
|
||||||
*/
|
*/
|
||||||
public void createAndShowUi() throws IOException {
|
public void createAndShowUi() {
|
||||||
mainWindow = new JFrame();
|
mainWindow = new JFrame();
|
||||||
mainWindow.setTitle("6502 Simulator - " + machine.getName());
|
mainWindow.setTitle("6502 Simulator - " + machine.getName());
|
||||||
mainWindow.setResizable(false);
|
mainWindow.setResizable(false);
|
||||||
@@ -176,7 +179,7 @@ public class Simulator {
|
|||||||
|
|
||||||
// File Chooser
|
// File Chooser
|
||||||
fileChooser = new JFileChooser(System.getProperty("user.dir"));
|
fileChooser = new JFileChooser(System.getProperty("user.dir"));
|
||||||
preferences = new PreferencesDialog(mainWindow, true);
|
preferences = new PreferencesDialog(mainWindow, true, haltOnBreak);
|
||||||
|
|
||||||
// Panel for Console and Buttons
|
// Panel for Console and Buttons
|
||||||
JPanel consoleContainer = new JPanel();
|
JPanel consoleContainer = new JPanel();
|
||||||
@@ -188,18 +191,22 @@ public class Simulator {
|
|||||||
|
|
||||||
runStopButton = new JButton("Run");
|
runStopButton = new JButton("Run");
|
||||||
stepButton = new JButton("Step");
|
stepButton = new JButton("Step");
|
||||||
|
stepOverButton = new JButton("Step Over");
|
||||||
|
stepOverButton.setEnabled(false);
|
||||||
JButton softResetButton = new JButton("Soft Reset");
|
JButton softResetButton = new JButton("Soft Reset");
|
||||||
JButton hardResetButton = new JButton("Hard Reset");
|
JButton hardResetButton = new JButton("Hard Reset");
|
||||||
|
|
||||||
stepCountBox = new JComboBox<>(STEPS);
|
stepCountBox = new JComboBox<>(STEPS);
|
||||||
stepCountBox.addActionListener(new ActionListener() {
|
stepCountBox.addActionListener(actionEvent -> {
|
||||||
@Override
|
JComboBox<?> cb = (JComboBox<?>) actionEvent.getSource();
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
String cbStepsPerClick = (String) cb.getSelectedItem();
|
||||||
|
|
||||||
|
stepsPerClick = 1; // Default in case of error
|
||||||
|
|
||||||
|
if (cbStepsPerClick != null) {
|
||||||
try {
|
try {
|
||||||
JComboBox cb = (JComboBox) actionEvent.getSource();
|
stepsPerClick = Integer.parseInt(cbStepsPerClick);
|
||||||
stepsPerClick = Integer.parseInt((String) cb.getSelectedItem());
|
|
||||||
} catch (NumberFormatException ex) {
|
} catch (NumberFormatException ex) {
|
||||||
stepsPerClick = 1;
|
|
||||||
stepCountBox.setSelectedIndex(0);
|
stepCountBox.setSelectedIndex(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +214,7 @@ public class Simulator {
|
|||||||
|
|
||||||
buttonContainer.add(runStopButton);
|
buttonContainer.add(runStopButton);
|
||||||
buttonContainer.add(stepButton);
|
buttonContainer.add(stepButton);
|
||||||
|
buttonContainer.add(stepOverButton);
|
||||||
buttonContainer.add(stepCountBox);
|
buttonContainer.add(stepCountBox);
|
||||||
buttonContainer.add(softResetButton);
|
buttonContainer.add(softResetButton);
|
||||||
buttonContainer.add(hardResetButton);
|
buttonContainer.add(hardResetButton);
|
||||||
@@ -221,38 +229,25 @@ public class Simulator {
|
|||||||
// Bottom - buttons.
|
// Bottom - buttons.
|
||||||
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
|
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
|
||||||
|
|
||||||
runStopButton.addActionListener(new ActionListener() {
|
runStopButton.addActionListener(actionEvent -> {
|
||||||
@Override
|
if (runLoop != null && runLoop.isRunning()) {
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
Simulator.this.handleStop();
|
||||||
if (runLoop != null && runLoop.isRunning()) {
|
} else {
|
||||||
Simulator.this.handleStop();
|
Simulator.this.handleStart();
|
||||||
} else {
|
|
||||||
Simulator.this.handleStart();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
stepButton.addActionListener(new ActionListener() {
|
stepButton.addActionListener(actionEvent -> Simulator.this.handleStep(stepsPerClick));
|
||||||
@Override
|
stepOverButton.addActionListener(actionEvent -> Simulator.this.handleStepOver());
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
|
||||||
Simulator.this.handleStep(stepsPerClick);
|
softResetButton.addActionListener(actionEvent -> {
|
||||||
}
|
// If this was a CTRL-click, do a hard reset.
|
||||||
|
Simulator.this.handleReset(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
softResetButton.addActionListener(new ActionListener() {
|
hardResetButton.addActionListener(actionEvent -> {
|
||||||
@Override
|
// If this was a CTRL-click, do a hard reset.
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
Simulator.this.handleReset(true);
|
||||||
// If this was a CTRL-click, do a hard reset.
|
|
||||||
Simulator.this.handleReset(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
hardResetButton.addActionListener(new ActionListener() {
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
|
||||||
// If this was a CTRL-click, do a hard reset.
|
|
||||||
Simulator.this.handleReset(true);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||||
@@ -274,7 +269,7 @@ public class Simulator {
|
|||||||
try {
|
try {
|
||||||
commandMonitorObject.wait();
|
commandMonitorObject.wait();
|
||||||
} catch (InterruptedException ex) {
|
} catch (InterruptedException ex) {
|
||||||
ex.printStackTrace();
|
logger.error("Interrupted during wait on command monitor.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return command;
|
return command;
|
||||||
@@ -337,12 +332,28 @@ public class Simulator {
|
|||||||
step();
|
step();
|
||||||
}
|
}
|
||||||
updateVisibleState();
|
updateVisibleState();
|
||||||
|
stepOverButton.setEnabled(machine.getCpu().getNextIr() == JSR);
|
||||||
} catch (SymonException ex) {
|
} catch (SymonException ex) {
|
||||||
logger.error("Exception during simulator step", ex);
|
logger.error("Exception during simulator step", ex);
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Step over a JSR / RTS function. This function is much more like a "run()"
|
||||||
|
* than a "step()", and may enter an infinite loop if no RTS is encountered.
|
||||||
|
*/
|
||||||
|
private void handleStepOver() {
|
||||||
|
if (runLoop != null && runLoop.isRunning()) {
|
||||||
|
// This should really never happen...
|
||||||
|
logger.error("Can't step over when the simulator is already running!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
runLoop = new RunLoop();
|
||||||
|
runLoop.haltOnRts(true);
|
||||||
|
runLoop.start();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform a single step of the simulated system.
|
* Perform a single step of the simulated system.
|
||||||
*/
|
*/
|
||||||
@@ -362,7 +373,7 @@ public class Simulator {
|
|||||||
// If a key has been pressed, fill the ACIA.
|
// If a key has been pressed, fill the ACIA.
|
||||||
try {
|
try {
|
||||||
if (machine.getAcia() != null && console.hasInput()) {
|
if (machine.getAcia() != null && console.hasInput()) {
|
||||||
machine.getAcia().rxWrite((int) console.readInputChar());
|
machine.getAcia().rxWrite(console.readInputChar());
|
||||||
}
|
}
|
||||||
} catch (FifoUnderrunException ex) {
|
} catch (FifoUnderrunException ex) {
|
||||||
logger.error("Console type-ahead buffer underrun!");
|
logger.error("Console type-ahead buffer underrun!");
|
||||||
@@ -411,53 +422,73 @@ public class Simulator {
|
|||||||
*/
|
*/
|
||||||
class RunLoop extends Thread {
|
class RunLoop extends Thread {
|
||||||
private boolean isRunning = false;
|
private boolean isRunning = false;
|
||||||
|
private boolean haltOnRts = false;
|
||||||
|
// The number of JSR instructions we've seen since being asked to
|
||||||
|
// step over a subroutine. While stepping over, this is incremented
|
||||||
|
// on RTS instructions, and decremented on JSR instructions.
|
||||||
|
private int callStackDepth = 0;
|
||||||
|
|
||||||
public boolean isRunning() {
|
public boolean isRunning() {
|
||||||
return isRunning;
|
return this.isRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void requestStop() {
|
public void requestStop() {
|
||||||
isRunning = false;
|
this.isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void haltOnRts(boolean value) {
|
||||||
|
this.haltOnRts = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
logger.debug("Starting main run loop.");
|
if (this.haltOnRts) {
|
||||||
isRunning = true;
|
logger.debug("Running until next RTS instruction");
|
||||||
|
} else {
|
||||||
|
logger.debug("Running");
|
||||||
|
}
|
||||||
|
this.isRunning = true;
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
// Don't allow step while the simulator is running
|
||||||
public void run() {
|
stepButton.setEnabled(false);
|
||||||
// Don't allow step while the simulator is running
|
stepOverButton.setEnabled(false);
|
||||||
stepButton.setEnabled(false);
|
stepCountBox.setEnabled(false);
|
||||||
stepCountBox.setEnabled(false);
|
menuBar.simulatorDidStart();
|
||||||
menuBar.simulatorDidStart();
|
// Toggle the state of the run button
|
||||||
// Toggle the state of the run button
|
runStopButton.setText("Stop");
|
||||||
runStopButton.setText("Stop");
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
do {
|
do {
|
||||||
step();
|
step();
|
||||||
|
if (this.haltOnRts) {
|
||||||
|
var instruction = machine.getCpu().getInstruction();
|
||||||
|
if (instruction == JSR) {
|
||||||
|
this.callStackDepth++;
|
||||||
|
logger.trace("Step-over call stack increased to {}", this.callStackDepth);
|
||||||
|
} else if (this.callStackDepth > 0 && instruction == RTS) {
|
||||||
|
this.callStackDepth--;
|
||||||
|
logger.trace("Step-over call stack decreased to {}", this.callStackDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} while (shouldContinue());
|
} while (shouldContinue());
|
||||||
} catch (SymonException ex) {
|
} catch (SymonException ex) {
|
||||||
logger.error("Exception in main simulator run thread. Exiting run.", ex);
|
logger.error("Exception in main simulator run thread. Exiting run.", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
statusPane.updateState();
|
||||||
public void run() {
|
memoryWindow.updateState();
|
||||||
statusPane.updateState();
|
runStopButton.setText("Run");
|
||||||
memoryWindow.updateState();
|
stepButton.setEnabled(true);
|
||||||
runStopButton.setText("Run");
|
stepOverButton.setEnabled(machine.getCpu().getNextIr() == JSR);
|
||||||
stepButton.setEnabled(true);
|
stepCountBox.setEnabled(true);
|
||||||
stepCountBox.setEnabled(true);
|
if (traceLog.isVisible()) {
|
||||||
if (traceLog.isVisible()) {
|
traceLog.refresh();
|
||||||
traceLog.refresh();
|
|
||||||
}
|
|
||||||
menuBar.simulatorDidStop();
|
|
||||||
traceLog.simulatorDidStop();
|
|
||||||
}
|
}
|
||||||
|
menuBar.simulatorDidStop();
|
||||||
|
traceLog.simulatorDidStop();
|
||||||
});
|
});
|
||||||
|
|
||||||
isRunning = false;
|
isRunning = false;
|
||||||
@@ -467,9 +498,13 @@ public class Simulator {
|
|||||||
* @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 !breakpoints.contains(machine.getCpu().getProgramCounter()) &&
|
var instruction = machine.getCpu().getInstruction();
|
||||||
isRunning &&
|
|
||||||
!(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
|
var stepOverHalt = this.haltOnRts && this.callStackDepth == 0 && instruction == RTS;
|
||||||
|
var breakpointHalt = breakpoints.contains(machine.getCpu().getProgramCounter());
|
||||||
|
var brkHalt = preferences.getHaltOnBreak() && instruction == BRK;
|
||||||
|
|
||||||
|
return isRunning && !(stepOverHalt || breakpointHalt || brkHalt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -510,12 +545,9 @@ public class Simulator {
|
|||||||
// Now load the program at the starting address.
|
// Now load the program at the starting address.
|
||||||
loadProgram(program, preferences.getProgramStartAddress());
|
loadProgram(program, preferences.getProgramStartAddress());
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
console.reset();
|
||||||
public void run() {
|
breakpoints.refresh();
|
||||||
console.reset();
|
|
||||||
breakpoints.refresh();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO: "Don't Show Again" checkbox
|
// TODO: "Don't Show Again" checkbox
|
||||||
@@ -553,7 +585,7 @@ public class Simulator {
|
|||||||
long fileSize = romFile.length();
|
long fileSize = romFile.length();
|
||||||
|
|
||||||
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 " + machine.getRomSize() + " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the new ROM image
|
// Load the new ROM image
|
||||||
@@ -647,30 +679,27 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SetFontAction extends AbstractAction {
|
class SetFontAction extends AbstractAction {
|
||||||
private int size;
|
private final int size;
|
||||||
|
|
||||||
public SetFontAction(int size) {
|
public SetFontAction(int size) {
|
||||||
super(Integer.toString(size) + " pt", null);
|
super(size + " pt", null);
|
||||||
this.size = size;
|
this.size = size;
|
||||||
putValue(SHORT_DESCRIPTION, "Set font to " + 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(() -> {
|
||||||
@Override
|
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
||||||
public void run() {
|
mainWindow.pack();
|
||||||
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
|
||||||
mainWindow.pack();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SetSpeedAction extends AbstractAction {
|
class SetSpeedAction extends AbstractAction {
|
||||||
private int speed;
|
private final int speed;
|
||||||
|
|
||||||
public SetSpeedAction(int speed) {
|
public SetSpeedAction(int speed) {
|
||||||
super(Integer.toString(speed) + " MHz", null);
|
super(speed + " MHz", null);
|
||||||
this.speed = speed;
|
this.speed = speed;
|
||||||
putValue(SHORT_DESCRIPTION, "Set simulated speed to " + speed + " MHz.");
|
putValue(SHORT_DESCRIPTION, "Set simulated speed to " + speed + " MHz.");
|
||||||
}
|
}
|
||||||
@@ -686,7 +715,7 @@ public class Simulator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SetCpuAction extends AbstractAction {
|
class SetCpuAction extends AbstractAction {
|
||||||
private Cpu.CpuBehavior behavior;
|
private final Cpu.CpuBehavior behavior;
|
||||||
|
|
||||||
public SetCpuAction(String cpu, Cpu.CpuBehavior behavior) {
|
public SetCpuAction(String cpu, Cpu.CpuBehavior behavior) {
|
||||||
super(cpu, null);
|
super(cpu, null);
|
||||||
@@ -726,11 +755,7 @@ public class Simulator {
|
|||||||
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
synchronized (memoryWindow) {
|
synchronized (memoryWindow) {
|
||||||
if (memoryWindow.isVisible()) {
|
memoryWindow.setVisible(!memoryWindow.isVisible());
|
||||||
memoryWindow.setVisible(false);
|
|
||||||
} else {
|
|
||||||
memoryWindow.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -743,11 +768,7 @@ public class Simulator {
|
|||||||
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
synchronized (videoWindow) {
|
synchronized (videoWindow) {
|
||||||
if (videoWindow.isVisible()) {
|
videoWindow.setVisible(!videoWindow.isVisible());
|
||||||
videoWindow.setVisible(false);
|
|
||||||
} else {
|
|
||||||
videoWindow.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -760,11 +781,7 @@ public class Simulator {
|
|||||||
|
|
||||||
public void actionPerformed(ActionEvent actionEvent) {
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
synchronized (breakpointsWindow) {
|
synchronized (breakpointsWindow) {
|
||||||
if (breakpointsWindow.isVisible()) {
|
breakpointsWindow.setVisible(!breakpointsWindow.isVisible());
|
||||||
breakpointsWindow.setVisible(false);
|
|
||||||
} else {
|
|
||||||
breakpointsWindow.setVisible(true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -960,15 +977,11 @@ public class Simulator {
|
|||||||
|
|
||||||
private void updateVisibleState() {
|
private void updateVisibleState() {
|
||||||
// Immediately update the UI.
|
// Immediately update the UI.
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(() -> {
|
||||||
@Override
|
statusPane.updateState();
|
||||||
public void run() {
|
memoryWindow.updateState();
|
||||||
// Now update the state
|
if (traceLog.shouldUpdate()) {
|
||||||
statusPane.updateState();
|
traceLog.refresh();
|
||||||
memoryWindow.updateState();
|
|
||||||
if (traceLog.shouldUpdate()) {
|
|
||||||
traceLog.refresh();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -32,7 +32,7 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
|
|||||||
|
|
||||||
public abstract class Acia extends Device {
|
public abstract class Acia extends Device {
|
||||||
|
|
||||||
private String name;
|
private final String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register addresses
|
* Register addresses
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,7 +28,7 @@ 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.
|
||||||
* <p/>
|
* <p/>
|
||||||
* Unlike a 16550 UART, the 6551 ACIA has only one-byte transmit and
|
* Unlike a 16550 UART, the 6551 ACIA has only one-byte transmit and
|
||||||
* receive buffers. It is the programmer's responsibility to check the
|
* receive buffers. It is the programmer's responsibility to check the
|
||||||
@@ -53,6 +53,14 @@ public class Acia6551 extends Acia {
|
|||||||
|
|
||||||
public Acia6551(int address) throws MemoryRangeException {
|
public Acia6551(int address) throws MemoryRangeException {
|
||||||
super(address, ACIA_SIZE, "ACIA");
|
super(address, ACIA_SIZE, "ACIA");
|
||||||
|
|
||||||
|
// Figure 6 in the 6551 ACIA data sheet says the "hardware reset"
|
||||||
|
// state of the Control Register is all zeros.
|
||||||
|
setControlRegister(0b00000000);
|
||||||
|
// Figure 7 of the 6551 ACIA data sheet says the "hardware reset"
|
||||||
|
// state of the Command Register is zeros, but Transmitter Control
|
||||||
|
// is set to "interrupt disabled, ready to send".
|
||||||
|
setCommandRegister(0b00000010);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -74,16 +82,16 @@ public class Acia6551 extends Acia {
|
|||||||
@Override
|
@Override
|
||||||
public void write(int address, int data) throws MemoryAccessException {
|
public void write(int address, int data) throws MemoryAccessException {
|
||||||
switch (address) {
|
switch (address) {
|
||||||
case 0:
|
case DATA_REG:
|
||||||
txWrite(data);
|
txWrite(data);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case STAT_REG:
|
||||||
reset();
|
reset();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case CMND_REG:
|
||||||
setCommandRegister(data);
|
setCommandRegister(data);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case CTRL_REG:
|
||||||
setControlRegister(data);
|
setControlRegister(data);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -110,67 +118,59 @@ public class Acia6551 extends Acia {
|
|||||||
controlRegister = data;
|
controlRegister = data;
|
||||||
int rate = 0;
|
int rate = 0;
|
||||||
|
|
||||||
// If the value of the data is 0, this is a request to reset,
|
// Mask the lower four bits to get the baud rate.
|
||||||
// otherwise it's a control update.
|
int baudSelector = data & 0x0f;
|
||||||
|
switch (baudSelector) {
|
||||||
if (data == 0) {
|
case 1:
|
||||||
reset();
|
rate = 50;
|
||||||
} else {
|
break;
|
||||||
// Mask the lower three bits to get the baud rate.
|
case 2:
|
||||||
int baudSelector = data & 0x0f;
|
rate = 75;
|
||||||
switch (baudSelector) {
|
break;
|
||||||
case 0:
|
case 3:
|
||||||
rate = 0;
|
rate = 110; // Real rate is actually 109.92
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 4:
|
||||||
rate = 50;
|
rate = 135; // Real rate is actually 134.58
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 5:
|
||||||
rate = 75;
|
rate = 150;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 6:
|
||||||
rate = 110; // Real rate is actually 109.92
|
rate = 300;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 7:
|
||||||
rate = 135; // Real rate is actually 134.58
|
rate = 600;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 8:
|
||||||
rate = 150;
|
rate = 1200;
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 9:
|
||||||
rate = 300;
|
rate = 1800;
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 10:
|
||||||
rate = 600;
|
rate = 2400;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 11:
|
||||||
rate = 1200;
|
rate = 3600;
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 12:
|
||||||
rate = 1800;
|
rate = 4800;
|
||||||
break;
|
break;
|
||||||
case 10:
|
case 13:
|
||||||
rate = 2400;
|
rate = 7200;
|
||||||
break;
|
break;
|
||||||
case 11:
|
case 14:
|
||||||
rate = 3600;
|
rate = 9600;
|
||||||
break;
|
break;
|
||||||
case 12:
|
case 15:
|
||||||
rate = 4800;
|
rate = 19200;
|
||||||
break;
|
break;
|
||||||
case 13:
|
default:
|
||||||
rate = 7200;
|
break;
|
||||||
break;
|
|
||||||
case 14:
|
|
||||||
rate = 9600;
|
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
rate = 19200;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
setBaudRate(rate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setBaudRate(rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -203,13 +203,16 @@ public class Acia6551 extends Acia {
|
|||||||
|
|
||||||
|
|
||||||
private synchronized void reset() {
|
private synchronized void reset() {
|
||||||
txChar = 0;
|
// Figure 6 in the 6551 ACIA data sheet says the "program reset"
|
||||||
txEmpty = true;
|
// event does not modify the control register.
|
||||||
rxChar = 0;
|
|
||||||
rxFull = false;
|
|
||||||
receiveIrqEnabled = false;
|
|
||||||
transmitIrqEnabled = false;
|
|
||||||
interrupt = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Figure 7 in the 6551 ACIA data sheet says the "program reset"
|
||||||
|
// event keeps the "parity check" configuration in the command
|
||||||
|
// register, but resets the other bits to defaults.
|
||||||
|
setCommandRegister((commandRegister & 0xe0) | 0x02);
|
||||||
|
|
||||||
|
// Figure 8 in the 6551 ACIA data sheet says the "program reset"
|
||||||
|
// event clears the "overrun" flag but otherwise has no effect.
|
||||||
|
overrun = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -31,7 +31,7 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
|
|||||||
/**
|
/**
|
||||||
* This is a simulation of the Motorola 6850 ACIA, with limited
|
* This is a simulation of the Motorola 6850 ACIA, with limited
|
||||||
* functionality.
|
* functionality.
|
||||||
*
|
* <p>
|
||||||
* Unlike a 16550 UART, the 6850 ACIA has only one-byte transmit and
|
* Unlike a 16550 UART, the 6850 ACIA has only one-byte transmit and
|
||||||
* receive buffers. It is the programmer's responsibility to check the
|
* receive buffers. It is the programmer's responsibility to check the
|
||||||
* status (full or empty) for transmit and receive buffers before
|
* status (full or empty) for transmit and receive buffers before
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,8 +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;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulation of a 6545 CRTC and virtual CRT output.
|
* Simulation of a 6545 CRTC and virtual CRT output.
|
||||||
*/
|
*/
|
||||||
@@ -87,9 +85,9 @@ public class Crtc extends Device {
|
|||||||
private boolean displayEnableSkew = false;
|
private boolean displayEnableSkew = false;
|
||||||
private boolean cursorSkew = false;
|
private boolean cursorSkew = false;
|
||||||
|
|
||||||
private Memory memory;
|
private final Memory memory;
|
||||||
|
|
||||||
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException {
|
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException {
|
||||||
super(deviceAddress, deviceAddress + 2, "CRTC");
|
super(deviceAddress, deviceAddress + 2, "CRTC");
|
||||||
this.memory = memory;
|
this.memory = memory;
|
||||||
|
|
||||||
@@ -120,19 +118,17 @@ public class Crtc extends Device {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||||
switch (address) {
|
if (address == REGISTER_RW) {
|
||||||
case REGISTER_RW:
|
switch (currentRegister) {
|
||||||
switch (currentRegister) {
|
case CURSOR_POSITION_LOW:
|
||||||
case CURSOR_POSITION_LOW:
|
return cursorPosition & 0xff;
|
||||||
return cursorPosition & 0xff;
|
case CURSOR_POSITION_HIGH:
|
||||||
case CURSOR_POSITION_HIGH:
|
return cursorPosition >> 8;
|
||||||
return cursorPosition >> 8;
|
default:
|
||||||
default:
|
return 0;
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -45,7 +45,7 @@ public abstract class Device implements Comparable<Device> {
|
|||||||
/**
|
/**
|
||||||
* The memory range for this device.
|
* The memory range for this device.
|
||||||
*/
|
*/
|
||||||
private MemoryRange memoryRange;
|
private final MemoryRange memoryRange;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The name of the device.
|
* The name of the device.
|
||||||
@@ -60,7 +60,7 @@ public abstract class Device implements Comparable<Device> {
|
|||||||
/**
|
/**
|
||||||
* Listeners to notify on update.
|
* Listeners to notify on update.
|
||||||
*/
|
*/
|
||||||
private Set<DeviceChangeListener> deviceChangeListeners;
|
private final Set<DeviceChangeListener> deviceChangeListeners;
|
||||||
|
|
||||||
public Device(int startAddress, int endAddress, String name) throws MemoryRangeException {
|
public Device(int startAddress, int endAddress, String name) throws MemoryRangeException {
|
||||||
this.memoryRange = new MemoryRange(startAddress, endAddress);
|
this.memoryRange = new MemoryRange(startAddress, endAddress);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -30,8 +30,8 @@ import com.loomcom.symon.exceptions.*;
|
|||||||
|
|
||||||
public class Memory extends Device {
|
public class Memory extends Device {
|
||||||
|
|
||||||
private boolean readOnly;
|
private final boolean readOnly;
|
||||||
private int[] mem;
|
private final int[] mem;
|
||||||
|
|
||||||
/* Initialize all locations to 0x00 (BRK) */
|
/* Initialize all locations to 0x00 (BRK) */
|
||||||
private static final int DEFAULT_FILL = 0x00;
|
private static final int DEFAULT_FILL = 0x00;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -119,7 +119,10 @@ public class SdController extends Device {
|
|||||||
if (sdImageFile != null) {
|
if (sdImageFile != null) {
|
||||||
try {
|
try {
|
||||||
FileInputStream fis = new FileInputStream(sdImageFile);
|
FileInputStream fis = new FileInputStream(sdImageFile);
|
||||||
fis.skip(this.position);
|
long bytesSkipped = fis.skip(this.position);
|
||||||
|
if (bytesSkipped < this.position) {
|
||||||
|
logger.log(Level.WARNING, "Failed to seek to position " + this.position);
|
||||||
|
}
|
||||||
int read = fis.read(readBuffer);
|
int read = fis.read(readBuffer);
|
||||||
if (read < SECTOR_SIZE) {
|
if (read < SECTOR_SIZE) {
|
||||||
logger.log(Level.WARNING, "not enough data to fill read buffer from SD image file");
|
logger.log(Level.WARNING, "not enough data to fill read buffer from SD image file");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,83 +27,23 @@ import com.loomcom.symon.exceptions.MemoryAccessException;
|
|||||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Very basic implementation of a MOS 6522 VIA.
|
* Skeleton for future implementation of a MOS 6522 VIA.
|
||||||
*
|
|
||||||
* TODO: Implement timers as threads.
|
|
||||||
*/
|
*/
|
||||||
public class Via6522 extends Pia {
|
public class Via6522 extends Pia {
|
||||||
public static final int VIA_SIZE = 16;
|
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 {
|
public Via6522(int address) throws MemoryRangeException {
|
||||||
super(address, address + VIA_SIZE - 1, "MOS 6522 VIA");
|
super(address, address + VIA_SIZE - 1, "MOS 6522 VIA");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(int address, int data) throws MemoryAccessException {
|
public void write(int address, int data) throws MemoryAccessException {
|
||||||
Register[] registers = Register.values();
|
// TODO: Implement write.
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||||
Register[] registers = Register.values();
|
// TODO: Implement read.
|
||||||
|
|
||||||
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;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||||
* Portions Copyright (c) 2009-2011 Graham Edgecombe
|
* Portions Copyright (c) 2009-2011 Graham Edgecombe
|
||||||
*
|
*
|
||||||
* Permission is hereby granted, free of charge, to any person obtaining
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
@@ -25,13 +25,8 @@
|
|||||||
package com.loomcom.symon.jterminal;
|
package com.loomcom.symon.jterminal;
|
||||||
|
|
||||||
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
|
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.*;
|
||||||
import java.awt.Font;
|
|
||||||
import java.awt.FontMetrics;
|
|
||||||
import java.awt.Graphics;
|
|
||||||
import java.awt.event.AdjustmentEvent;
|
|
||||||
import java.awt.event.AdjustmentListener;
|
|
||||||
import javax.swing.JComponent;
|
import javax.swing.JComponent;
|
||||||
import javax.swing.JScrollBar;
|
import javax.swing.JScrollBar;
|
||||||
|
|
||||||
@@ -88,10 +83,6 @@ public class JTerminal extends JComponent {
|
|||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBorderWidth() {
|
|
||||||
return borderWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFont(Font font) {
|
public void setFont(Font font) {
|
||||||
this.font = font;
|
this.font = font;
|
||||||
setCellWidthAndHeight(font);
|
setCellWidthAndHeight(font);
|
||||||
@@ -113,13 +104,6 @@ public class JTerminal extends JComponent {
|
|||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void println(String str) {
|
|
||||||
if (str == null) {
|
|
||||||
throw new NullPointerException("str");
|
|
||||||
}
|
|
||||||
print(str.concat("\r\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void print(String str) {
|
public void print(String str) {
|
||||||
model.print(str);
|
model.print(str);
|
||||||
}
|
}
|
||||||
@@ -177,12 +161,8 @@ public class JTerminal extends JComponent {
|
|||||||
int bufferSize = model.getBufferSize();
|
int bufferSize = model.getBufferSize();
|
||||||
|
|
||||||
if (bufferSize > rows) {
|
if (bufferSize > rows) {
|
||||||
scrollBar = new JScrollBar(1, 0, rows, 0, bufferSize + 1);
|
scrollBar = new JScrollBar(Adjustable.VERTICAL, 0, rows, 0, bufferSize + 1);
|
||||||
scrollBar.addAdjustmentListener(new AdjustmentListener() {
|
scrollBar.addAdjustmentListener(evt -> repaint());
|
||||||
public void adjustmentValueChanged(AdjustmentEvent evt) {
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add("After", scrollBar);
|
add("After", scrollBar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,51 +32,45 @@ import com.loomcom.symon.jterminal.bell.BellStrategy;
|
|||||||
*/
|
*/
|
||||||
public interface TerminalModel {
|
public interface TerminalModel {
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the bell strategy.
|
|
||||||
* @return The bell strategy.
|
|
||||||
*/
|
|
||||||
public BellStrategy getBellStrategy();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the bell strategy.
|
* Sets the bell strategy.
|
||||||
* @param strategy The bell strategy.
|
* @param strategy The bell strategy.
|
||||||
* @throws NullPointerException if the strategy is {@code null}.
|
* @throws NullPointerException if the strategy is {@code null}.
|
||||||
*/
|
*/
|
||||||
public void setBellStrategy(BellStrategy strategy);
|
void setBellStrategy(BellStrategy strategy);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the terminal.
|
* Clears the terminal.
|
||||||
*/
|
*/
|
||||||
public void clear();
|
void clear();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the cursor back n characters.
|
* Moves the cursor back n characters.
|
||||||
* @param n The number of characters.
|
* @param n The number of characters.
|
||||||
* @throws IllegalArgumentException if n is not positive.
|
* @throws IllegalArgumentException if n is not positive.
|
||||||
*/
|
*/
|
||||||
public void moveCursorBack(int n);
|
void moveCursorBack(int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the cursor forward n characters.
|
* Moves the cursor forward n characters.
|
||||||
* @param n The number of characters.
|
* @param n The number of characters.
|
||||||
* @throws IllegalArgumentException if n is not positive.
|
* @throws IllegalArgumentException if n is not positive.
|
||||||
*/
|
*/
|
||||||
public void moveCursorForward(int n);
|
void moveCursorForward(int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the cursor down n characters.
|
* Moves the cursor down n characters.
|
||||||
* @param n The number of characters.
|
* @param n The number of characters.
|
||||||
* @throws IllegalArgumentException if n is not positive.
|
* @throws IllegalArgumentException if n is not positive.
|
||||||
*/
|
*/
|
||||||
public void moveCursorDown(int n);
|
void moveCursorDown(int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Moves the cursor up n characters.
|
* Moves the cursor up n characters.
|
||||||
* @param n The number of characters.
|
* @param n The number of characters.
|
||||||
* @throws IllegalArgumentException if n is not positive.
|
* @throws IllegalArgumentException if n is not positive.
|
||||||
*/
|
*/
|
||||||
public void moveCursorUp(int n);
|
void moveCursorUp(int n);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a cell.
|
* Sets a cell.
|
||||||
@@ -86,7 +80,7 @@ public interface TerminalModel {
|
|||||||
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
||||||
* out of bounds.
|
* out of bounds.
|
||||||
*/
|
*/
|
||||||
public void setCell(int column, int row, TerminalCell cell);
|
void setCell(int column, int row, TerminalCell cell);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets a cell.
|
* Gets a cell.
|
||||||
@@ -96,7 +90,7 @@ public interface TerminalModel {
|
|||||||
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
||||||
* out of bounds.
|
* out of bounds.
|
||||||
*/
|
*/
|
||||||
public TerminalCell getCell(int column, int row);
|
TerminalCell getCell(int column, int row);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prints the specified string to the terminal at the cursor position,
|
* Prints the specified string to the terminal at the cursor position,
|
||||||
@@ -105,63 +99,63 @@ public interface TerminalModel {
|
|||||||
* @param str The string to print.
|
* @param str The string to print.
|
||||||
* @throws NullPointerException if the string is {@code null}.
|
* @throws NullPointerException if the string is {@code null}.
|
||||||
*/
|
*/
|
||||||
public void print(String str);
|
void print(String str);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of columns.
|
* Gets the number of columns.
|
||||||
* @return The number of columns.
|
* @return The number of columns.
|
||||||
*/
|
*/
|
||||||
public int getColumns();
|
int getColumns();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the number of rows.
|
* Gets the number of rows.
|
||||||
* @return The number of rows.
|
* @return The number of rows.
|
||||||
*/
|
*/
|
||||||
public int getRows();
|
int getRows();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the buffer size.
|
* Gets the buffer size.
|
||||||
* @return The buffer size.
|
* @return The buffer size.
|
||||||
*/
|
*/
|
||||||
public int getBufferSize();
|
int getBufferSize();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the cursor row.
|
* Gets the cursor row.
|
||||||
* @return The cursor row.
|
* @return The cursor row.
|
||||||
*/
|
*/
|
||||||
public int getCursorRow();
|
int getCursorRow();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cursor row.
|
* Sets the cursor row.
|
||||||
* @param row The cursor row.
|
* @param row The cursor row.
|
||||||
* @throws IllegalArgumentException if the row is out of the valid range.
|
* @throws IllegalArgumentException if the row is out of the valid range.
|
||||||
*/
|
*/
|
||||||
public void setCursorRow(int row);
|
void setCursorRow(int row);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the cursor column.
|
* Gets the cursor column.
|
||||||
* @return The cursor column.
|
* @return The cursor column.
|
||||||
*/
|
*/
|
||||||
public int getCursorColumn();
|
int getCursorColumn();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the cursor column.
|
* Sets the cursor column.
|
||||||
* @param column The cursor column.
|
* @param column The cursor column.
|
||||||
* @throws IllegalArgumentException if the column is out of the valid range.
|
* @throws IllegalArgumentException if the column is out of the valid range.
|
||||||
*/
|
*/
|
||||||
public void setCursorColumn(int column);
|
void setCursorColumn(int column);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default background color.
|
* Gets the default background color.
|
||||||
* @return The default background color.
|
* @return The default background color.
|
||||||
*/
|
*/
|
||||||
public Color getDefaultBackgroundColor();
|
Color getDefaultBackgroundColor();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the default foreground color.
|
* Gets the default foreground color.
|
||||||
* @return The default foreground color.
|
* @return The default foreground color.
|
||||||
*/
|
*/
|
||||||
public Color getDefaultForegroundColor();
|
Color getDefaultForegroundColor();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ public interface BellStrategy {
|
|||||||
/**
|
/**
|
||||||
* Sounds the bell.
|
* Sounds the bell.
|
||||||
*/
|
*/
|
||||||
public void soundBell();
|
void soundBell();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ public class AnsiControlSequence {
|
|||||||
throw new NullPointerException("parameters");
|
throw new NullPointerException("parameters");
|
||||||
}
|
}
|
||||||
this.command = command;
|
this.command = command;
|
||||||
if (parameters.length == 1 && parameters[0].equals("")) {
|
if (parameters.length == 1 && parameters[0].isEmpty()) {
|
||||||
this.parameters = new String[0];
|
this.parameters = new String[0];
|
||||||
} else {
|
} else {
|
||||||
this.parameters = parameters.clone();
|
this.parameters = parameters.clone();
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ public interface AnsiControlSequenceListener {
|
|||||||
* Called when a control sequence has been parsed.
|
* Called when a control sequence has been parsed.
|
||||||
* @param seq The control sequence.
|
* @param seq The control sequence.
|
||||||
*/
|
*/
|
||||||
public void parsedControlSequence(AnsiControlSequence seq);
|
void parsedControlSequence(AnsiControlSequence seq);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when a string has been parsed.
|
* Called when a string has been parsed.
|
||||||
* @param str The string.
|
* @param str The string.
|
||||||
*/
|
*/
|
||||||
public void parsedString(String str);
|
void parsedString(String str);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +33,7 @@ import java.io.StringReader;
|
|||||||
public class AnsiControlSequenceParser {
|
public class AnsiControlSequenceParser {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The multi-byte control sequence introducer.
|
* The multibyte control sequence introducer.
|
||||||
*/
|
*/
|
||||||
private static final char[] MULTI_CSI = new char[] { 27, '[' };
|
private static final char[] MULTI_CSI = new char[] { 27, '[' };
|
||||||
|
|
||||||
@@ -43,7 +43,7 @@ public class AnsiControlSequenceParser {
|
|||||||
private static final char SINGLE_CSI = 155;
|
private static final char SINGLE_CSI = 155;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The buffer of data from the last call to {@link #parse()}. This is
|
* The buffer of data from the last call to "parse()". This is
|
||||||
* populated with data if an escape sequence is not complete.
|
* populated with data if an escape sequence is not complete.
|
||||||
*/
|
*/
|
||||||
private StringBuilder buffer = new StringBuilder();
|
private StringBuilder buffer = new StringBuilder();
|
||||||
@@ -70,13 +70,10 @@ public class AnsiControlSequenceParser {
|
|||||||
str = buffer.toString().concat(str);
|
str = buffer.toString().concat(str);
|
||||||
buffer = new StringBuilder();
|
buffer = new StringBuilder();
|
||||||
}
|
}
|
||||||
Reader reader = new StringReader(str);
|
try {
|
||||||
try {
|
try (Reader reader = new StringReader(str)) {
|
||||||
try {
|
parse(reader);
|
||||||
parse(reader);
|
}
|
||||||
} finally {
|
|
||||||
reader.close();
|
|
||||||
}
|
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
/* ignore */
|
/* ignore */
|
||||||
}
|
}
|
||||||
@@ -147,7 +144,7 @@ public class AnsiControlSequenceParser {
|
|||||||
if (!finishedSequence) {
|
if (!finishedSequence) {
|
||||||
// not an ideal solution if they used the two byte CSI, but it's
|
// not an ideal solution if they used the two byte CSI, but it's
|
||||||
// easier and cleaner than keeping track of it
|
// easier and cleaner than keeping track of it
|
||||||
buffer.append((char) SINGLE_CSI);
|
buffer.append(SINGLE_CSI);
|
||||||
buffer.append(parameters);
|
buffer.append(parameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
|||||||
@Override
|
@Override
|
||||||
public void parsedControlSequence(AnsiControlSequence seq) {
|
public void parsedControlSequence(AnsiControlSequence seq) {
|
||||||
char command = seq.getCommand();
|
char command = seq.getCommand();
|
||||||
|
int n;
|
||||||
String[] parameters = seq.getParameters();
|
String[] parameters = seq.getParameters();
|
||||||
|
|
||||||
switch (command) {
|
switch (command) {
|
||||||
@@ -63,7 +64,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
|||||||
case 'B':
|
case 'B':
|
||||||
case 'C':
|
case 'C':
|
||||||
case 'D':
|
case 'D':
|
||||||
int n = 1;
|
n = 1;
|
||||||
if (parameters.length == 1) {
|
if (parameters.length == 1) {
|
||||||
n = Integer.parseInt(parameters[0]);
|
n = Integer.parseInt(parameters[0]);
|
||||||
}
|
}
|
||||||
@@ -73,7 +74,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
|||||||
moveCursorDown(n);
|
moveCursorDown(n);
|
||||||
} else if (command == 'C') {
|
} else if (command == 'C') {
|
||||||
moveCursorForward(n);
|
moveCursorForward(n);
|
||||||
} else if (command == 'D') {
|
} else {
|
||||||
moveCursorBack(n);
|
moveCursorBack(n);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -85,7 +86,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
|||||||
}
|
}
|
||||||
if (command == 'E') {
|
if (command == 'E') {
|
||||||
moveCursorDown(n);
|
moveCursorDown(n);
|
||||||
} else if (command == 'F') {
|
} else {
|
||||||
moveCursorUp(n);
|
moveCursorUp(n);
|
||||||
}
|
}
|
||||||
setCursorColumn(0);
|
setCursorColumn(0);
|
||||||
@@ -215,7 +216,8 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
|||||||
case '\t':
|
case '\t':
|
||||||
while ((++cursorColumn % TAB_WIDTH) != 0);
|
while ((++cursorColumn % TAB_WIDTH) != 0);
|
||||||
continue;
|
continue;
|
||||||
case 127:
|
case 8: // ASCII Backspace
|
||||||
|
case 127: // ASCII Delete
|
||||||
if (cursorColumn > 0) {
|
if (cursorColumn > 0) {
|
||||||
cells[cursorRow][--cursorColumn] = null;
|
cells[cursorRow][--cursorColumn] = null;
|
||||||
}
|
}
|
||||||
@@ -464,12 +466,7 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
|
|||||||
return bufferSize;
|
return bufferSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BellStrategy getBellStrategy() {
|
|
||||||
return bellStrategy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBellStrategy(BellStrategy strategy) {
|
public void setBellStrategy(BellStrategy strategy) {
|
||||||
if (strategy == null) {
|
if (strategy == null) {
|
||||||
throw new NullPointerException("strategy");
|
throw new NullPointerException("strategy");
|
||||||
|
|||||||
@@ -0,0 +1,169 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||||
|
* Maik Merten <maikmerten@googlemail.com>
|
||||||
|
* Chelsea Wilkinson <mail@chelseawilkinson.me>
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import com.loomcom.symon.Bus;
|
||||||
|
import com.loomcom.symon.Cpu;
|
||||||
|
import com.loomcom.symon.devices.*;
|
||||||
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class BenEaterMachine implements Machine {
|
||||||
|
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(BenEaterMachine.class.getName());
|
||||||
|
|
||||||
|
// Constants used by the simulated system. These define the memory map.
|
||||||
|
private static final int BUS_BOTTOM = 0x0000;
|
||||||
|
private static final int BUS_TOP = 0xffff;
|
||||||
|
|
||||||
|
// 16K of RAM from $0000 - $3FFF
|
||||||
|
private static final int MEMORY_BASE = 0x0000;
|
||||||
|
private static final int MEMORY_SIZE = 0x4000;
|
||||||
|
|
||||||
|
// VIA at $6000-$600F
|
||||||
|
private static final int PIA_BASE = 0x6000;
|
||||||
|
|
||||||
|
// ACIA at $5000-$5003
|
||||||
|
private static final int ACIA_BASE = 0x5000;
|
||||||
|
|
||||||
|
// CRTC at $4000-$4001
|
||||||
|
private static final int CRTC_BASE = 0x4000;
|
||||||
|
|
||||||
|
// 32KB ROM at $8000-$FFFF
|
||||||
|
private static final int ROM_BASE = 0x8000;
|
||||||
|
private static final int ROM_SIZE = 0x8000;
|
||||||
|
|
||||||
|
|
||||||
|
// The simulated peripherals
|
||||||
|
private final Bus bus;
|
||||||
|
private final Cpu cpu;
|
||||||
|
private final Acia acia;
|
||||||
|
private final Pia pia;
|
||||||
|
private final Crtc crtc;
|
||||||
|
private final Memory ram;
|
||||||
|
private Memory rom;
|
||||||
|
|
||||||
|
|
||||||
|
public BenEaterMachine(String romFile) throws Exception {
|
||||||
|
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
|
||||||
|
this.cpu = new Cpu();
|
||||||
|
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
|
||||||
|
this.pia = new Via6522(PIA_BASE);
|
||||||
|
this.acia = new Acia6551(ACIA_BASE);
|
||||||
|
this.crtc = new Crtc(CRTC_BASE, ram);
|
||||||
|
|
||||||
|
bus.addCpu(cpu);
|
||||||
|
bus.addDevice(ram);
|
||||||
|
bus.addDevice(pia);
|
||||||
|
bus.addDevice(acia);
|
||||||
|
bus.addDevice(crtc);
|
||||||
|
|
||||||
|
if (romFile != null) {
|
||||||
|
File romImage = new File(romFile);
|
||||||
|
if (romImage.canRead()) {
|
||||||
|
logger.info("Loading ROM image from file {}", romImage);
|
||||||
|
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
|
||||||
|
} else {
|
||||||
|
logger.info("Default ROM file {} not found, loading empty R/W memory image.", romImage);
|
||||||
|
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("No ROM file specified, loading empty R/W memory image.");
|
||||||
|
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bus.addDevice(rom);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bus getBus() {
|
||||||
|
return bus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cpu getCpu() {
|
||||||
|
return cpu;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Memory getRam() {
|
||||||
|
return ram;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Acia getAcia() {
|
||||||
|
return acia;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Pia getPia() {
|
||||||
|
return pia;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Crtc getCrtc() {
|
||||||
|
return crtc;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Memory getRom() {
|
||||||
|
return rom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRom(Memory rom) throws MemoryRangeException {
|
||||||
|
if(this.rom != null) {
|
||||||
|
bus.removeDevice(this.rom);
|
||||||
|
}
|
||||||
|
this.rom = rom;
|
||||||
|
bus.addDevice(this.rom);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRomBase() {
|
||||||
|
return ROM_BASE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRomSize() {
|
||||||
|
return ROM_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMemorySize() {
|
||||||
|
return MEMORY_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "benEater";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -42,9 +42,9 @@ public interface Machine {
|
|||||||
Memory getRam();
|
Memory getRam();
|
||||||
|
|
||||||
Acia getAcia();
|
Acia getAcia();
|
||||||
|
|
||||||
Pia getPia();
|
Pia getPia();
|
||||||
|
|
||||||
Crtc getCrtc();
|
Crtc getCrtc();
|
||||||
|
|
||||||
Memory getRom();
|
Memory getRom();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
@@ -91,7 +91,7 @@ public class SimpleMachine implements Machine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setRom(Memory rom) throws MemoryRangeException {
|
public void setRom(Memory rom) {
|
||||||
// No-op
|
// No-op
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* 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) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -30,10 +30,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.border.EmptyBorder;
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,8 +43,8 @@ public class BreakpointsWindow extends JFrame {
|
|||||||
private static final Dimension FRAME_SIZE = new Dimension(240, 280);
|
private static final Dimension FRAME_SIZE = new Dimension(240, 280);
|
||||||
private static final String EMPTY_STRING = "";
|
private static final String EMPTY_STRING = "";
|
||||||
|
|
||||||
private JFrame mainWindow;
|
private final JFrame mainWindow;
|
||||||
private Breakpoints breakpoints;
|
private final Breakpoints breakpoints;
|
||||||
|
|
||||||
public BreakpointsWindow(Breakpoints breakpoints,
|
public BreakpointsWindow(Breakpoints breakpoints,
|
||||||
JFrame mainWindow) {
|
JFrame mainWindow) {
|
||||||
@@ -73,18 +70,8 @@ public class BreakpointsWindow extends JFrame {
|
|||||||
|
|
||||||
final JTable breakpointsTable = new JTable(breakpoints);
|
final JTable breakpointsTable = new JTable(breakpoints);
|
||||||
breakpointsTable.setShowGrid(true);
|
breakpointsTable.setShowGrid(true);
|
||||||
breakpointsTable.setGridColor(Color.LIGHT_GRAY);
|
|
||||||
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
breakpointsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
breakpointsTable.getSelectionModel().addListSelectionListener(e -> removeButton.setEnabled(e.getFirstIndex() > -1));
|
||||||
@Override
|
|
||||||
public void valueChanged(ListSelectionEvent e) {
|
|
||||||
if (e.getFirstIndex() > -1) {
|
|
||||||
removeButton.setEnabled(true);
|
|
||||||
} else {
|
|
||||||
removeButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
JScrollPane scrollPane = new JScrollPane(breakpointsTable);
|
JScrollPane scrollPane = new JScrollPane(breakpointsTable);
|
||||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||||
@@ -92,45 +79,33 @@ public class BreakpointsWindow extends JFrame {
|
|||||||
|
|
||||||
breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
|
breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
ActionListener addBreakpointListener = new ActionListener() {
|
ActionListener addBreakpointListener = e -> {
|
||||||
@Override
|
int value;
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
int value;
|
|
||||||
|
|
||||||
String newBreakpoint = addTextField.getText();
|
String newBreakpoint = addTextField.getText();
|
||||||
|
|
||||||
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
|
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
|
||||||
return;
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
|
||||||
|
} catch (NumberFormatException ex) {
|
||||||
|
logger.warn("Can't parse page number {}", newBreakpoint);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
breakpoints.addBreakpoint(value);
|
||||||
|
|
||||||
|
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
|
||||||
|
|
||||||
|
addTextField.setText(EMPTY_STRING);
|
||||||
};
|
};
|
||||||
|
|
||||||
addButton.addActionListener(addBreakpointListener);
|
addButton.addActionListener(addBreakpointListener);
|
||||||
addTextField.addActionListener(addBreakpointListener);
|
addTextField.addActionListener(addBreakpointListener);
|
||||||
|
|
||||||
removeButton.addActionListener(new ActionListener() {
|
removeButton.addActionListener(e -> breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow()));
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e) {
|
|
||||||
breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
controlPanel.add(addTextField);
|
controlPanel.add(addTextField);
|
||||||
controlPanel.add(addButton);
|
controlPanel.add(addButton);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -51,8 +51,8 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
|
|||||||
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
|
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
|
||||||
private boolean sendCrForLf;
|
private final boolean sendCrForLf;
|
||||||
private FifoRingBuffer<Character> typeAheadBuffer;
|
private final FifoRingBuffer<Character> typeAheadBuffer;
|
||||||
|
|
||||||
public Console(int columns, int rows, Font font, boolean sendCrForLf) {
|
public Console(int columns, int rows, Font font, boolean sendCrForLf) {
|
||||||
super(new Vt100TerminalModel(columns, rows), font);
|
super(new Vt100TerminalModel(columns, rows), font);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -49,7 +49,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(MemoryWindow.class);
|
private static final Logger logger = LoggerFactory.getLogger(MemoryWindow.class);
|
||||||
|
|
||||||
private MemoryTableModel memoryTableModel;
|
private final MemoryTableModel memoryTableModel;
|
||||||
private JTable memoryTable;
|
private JTable memoryTable;
|
||||||
private JTextField pageNumberTextField;
|
private JTextField pageNumberTextField;
|
||||||
private JButton previousPageButton;
|
private JButton previousPageButton;
|
||||||
@@ -111,7 +111,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set-up the UI.
|
* Set up the UI.
|
||||||
*/
|
*/
|
||||||
private void createUi() {
|
private void createUi() {
|
||||||
setTitle("Memory Contents");
|
setTitle("Memory Contents");
|
||||||
@@ -230,7 +230,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
* A JTable that will automatically select all text in a cell
|
* A JTable that will automatically select all text in a cell
|
||||||
* being edited.
|
* being edited.
|
||||||
*/
|
*/
|
||||||
private class MemoryTable extends JTable {
|
private static class MemoryTable extends JTable {
|
||||||
|
|
||||||
public MemoryTable(TableModel tableModel) {
|
public MemoryTable(TableModel tableModel) {
|
||||||
super(tableModel);
|
super(tableModel);
|
||||||
@@ -242,7 +242,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
|
|
||||||
final Component editor = getEditorComponent();
|
final Component editor = getEditorComponent();
|
||||||
|
|
||||||
if (editor != null && editor instanceof JTextComponent) {
|
if (editor instanceof JTextComponent) {
|
||||||
((JTextComponent) editor).selectAll();
|
((JTextComponent) editor).selectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -250,7 +250,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class MemoryTableCellRenderer extends DefaultTableCellRenderer {
|
private static class MemoryTableCellRenderer extends DefaultTableCellRenderer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Component getTableCellRendererComponent(JTable table, Object value,
|
public Component getTableCellRendererComponent(JTable table, Object value,
|
||||||
@@ -272,9 +272,9 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
|||||||
/**
|
/**
|
||||||
* The model that backs the Memory Table.
|
* The model that backs the Memory Table.
|
||||||
*/
|
*/
|
||||||
private class MemoryTableModel extends AbstractTableModel {
|
private static class MemoryTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
private Bus bus;
|
private final Bus bus;
|
||||||
private int pageNumber;
|
private int pageNumber;
|
||||||
|
|
||||||
private static final int COLUMN_COUNT = 17;
|
private static final int COLUMN_COUNT = 17;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -42,11 +42,11 @@ public class PreferencesDialog extends Observable implements Preferences {
|
|||||||
private JTextField programLoadAddressField;
|
private JTextField programLoadAddressField;
|
||||||
|
|
||||||
private int programLoadAddress = DEFAULT_PROGRAM_LOAD_ADDRESS;
|
private int programLoadAddress = DEFAULT_PROGRAM_LOAD_ADDRESS;
|
||||||
private boolean haltOnBreak = DEFAULT_HALT_ON_BREAK;
|
private boolean haltOnBreak;
|
||||||
|
|
||||||
public PreferencesDialog(Frame parent, boolean modal) {
|
public PreferencesDialog(Frame parent, boolean modal, boolean haltOnBreak) {
|
||||||
this.dialog = new JDialog(parent, modal);
|
this.dialog = new JDialog(parent, modal);
|
||||||
|
this.haltOnBreak = haltOnBreak;
|
||||||
createUi();
|
createUi();
|
||||||
updateUi();
|
updateUi();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -86,7 +86,7 @@ public class StatusPanel extends JPanel {
|
|||||||
|
|
||||||
private void createUi() {
|
private void createUi() {
|
||||||
Border emptyBorder = BorderFactory.createEmptyBorder(EMPTY_BORDER, EMPTY_BORDER,
|
Border emptyBorder = BorderFactory.createEmptyBorder(EMPTY_BORDER, EMPTY_BORDER,
|
||||||
EMPTY_BORDER, EMPTY_BORDER);
|
EMPTY_BORDER, EMPTY_BORDER);
|
||||||
Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
|
Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
|
||||||
|
|
||||||
setBorder(BorderFactory.createCompoundBorder(emptyBorder, etchedBorder));
|
setBorder(BorderFactory.createCompoundBorder(emptyBorder, etchedBorder));
|
||||||
@@ -125,16 +125,15 @@ public class StatusPanel extends JPanel {
|
|||||||
|
|
||||||
// Add tool-tip text
|
// Add tool-tip text
|
||||||
carryFlagLabel.setToolTipText("Carry: The last operation caused an overflow " +
|
carryFlagLabel.setToolTipText("Carry: The last operation caused an overflow " +
|
||||||
"from bit 7 of the result or an underflow from bit 0");
|
"from bit 7 of the result or an underflow from bit 0");
|
||||||
zeroFlagLabel.setToolTipText("Zero: The result of the last operation was 0");
|
zeroFlagLabel.setToolTipText("Zero: The result of the last operation was 0");
|
||||||
irqDisableFlagLabel.setToolTipText("Interrupt Disable: Processor will not respond to IRQ");
|
irqDisableFlagLabel.setToolTipText("Interrupt Disable: Processor will not respond to IRQ");
|
||||||
decimalModeFlagLabel.setToolTipText("Decimal Mode");
|
decimalModeFlagLabel.setToolTipText("Decimal Mode");
|
||||||
breakFlagLabel.setToolTipText("Break: BRK instruction occurred");
|
breakFlagLabel.setToolTipText("Break: BRK instruction occurred");
|
||||||
overflowFlagLabel.setToolTipText("Overflow: The result of the last operation was " +
|
overflowFlagLabel.setToolTipText("Overflow: The result of the last operation was " +
|
||||||
"an invalid 2's complement result");
|
"an invalid 2's complement result");
|
||||||
negativeFlagLabel.setToolTipText("Negative: The result of the last operation set bit 7");
|
negativeFlagLabel.setToolTipText("Negative: The result of the last operation set bit 7");
|
||||||
|
|
||||||
|
|
||||||
statusFlagsPanel.add(negativeFlagLabel);
|
statusFlagsPanel.add(negativeFlagLabel);
|
||||||
statusFlagsPanel.add(overflowFlagLabel);
|
statusFlagsPanel.add(overflowFlagLabel);
|
||||||
statusFlagsPanel.add(breakFlagLabel);
|
statusFlagsPanel.add(breakFlagLabel);
|
||||||
@@ -243,7 +242,6 @@ public class StatusPanel extends JPanel {
|
|||||||
constraints.gridy = 1;
|
constraints.gridy = 1;
|
||||||
add(statusFlagsPanel, constraints);
|
add(statusFlagsPanel, constraints);
|
||||||
|
|
||||||
|
|
||||||
constraints.insets = new Insets(5, 0, 0, 0);
|
constraints.insets = new Insets(5, 0, 0, 0);
|
||||||
constraints.gridy = 2;
|
constraints.gridy = 2;
|
||||||
add(opcodeLabel, constraints);
|
add(opcodeLabel, constraints);
|
||||||
@@ -392,13 +390,6 @@ public class StatusPanel extends JPanel {
|
|||||||
textField.setMinimumSize(size);
|
textField.setMinimumSize(size);
|
||||||
textField.setMaximumSize(size);
|
textField.setMaximumSize(size);
|
||||||
textField.setPreferredSize(size);
|
textField.setPreferredSize(size);
|
||||||
textField.setBackground(Color.WHITE);
|
|
||||||
// Although we usually defer to the system look-and-feel, for
|
|
||||||
// these small text fields in particular, we use a Metal
|
|
||||||
// look-and-feel because native look-and-feel breaks very small
|
|
||||||
// text fields under GTK+ (they are drawn with an inner margin
|
|
||||||
// even if the margin is set to 0)
|
|
||||||
textField.setUI(new MetalTextFieldUI());
|
|
||||||
|
|
||||||
return textField;
|
return textField;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
* a copy of this software and associated documentation files (the
|
* a copy of this software and associated documentation files (the
|
||||||
|
|||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2008-2025 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 javax.swing.JDialog;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JOptionPane;
|
||||||
|
import javax.swing.LookAndFeel;
|
||||||
|
import javax.swing.UIManager;
|
||||||
|
import javax.swing.UnsupportedLookAndFeelException;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.formdev.flatlaf.FlatDarkLaf;
|
||||||
|
import com.formdev.flatlaf.FlatLaf;
|
||||||
|
import com.formdev.flatlaf.FlatLightLaf;
|
||||||
|
import com.formdev.flatlaf.util.SystemInfo;
|
||||||
|
import com.jthemedetecor.OsThemeDetector;
|
||||||
|
import com.loomcom.symon.machines.BenEaterMachine;
|
||||||
|
import com.loomcom.symon.machines.MulticompMachine;
|
||||||
|
import com.loomcom.symon.machines.SimpleMachine;
|
||||||
|
import com.loomcom.symon.machines.SymonMachine;
|
||||||
|
|
||||||
|
public final class Ui {
|
||||||
|
private final static Logger logger = LoggerFactory.getLogger(Ui.class.getName());
|
||||||
|
|
||||||
|
public static void initUi() throws UnsupportedLookAndFeelException {
|
||||||
|
// Location of properties resources for app-specific UI defaults:
|
||||||
|
FlatLaf.registerCustomDefaultsSource("com.loomcom.symon.themes");
|
||||||
|
|
||||||
|
// Enable automatic scaling, based on system DPI settings:
|
||||||
|
System.setProperty("sun.java2d.uiScale.enabled", "true");
|
||||||
|
System.setProperty("sun.java2d.uiScale", "1x");
|
||||||
|
System.setProperty("flatlaf.uiScale", "1x");
|
||||||
|
|
||||||
|
// OS-specific tweaking:
|
||||||
|
|
||||||
|
if (SystemInfo.isLinux) {
|
||||||
|
logger.debug("Adapting UI for Linux.");
|
||||||
|
|
||||||
|
// Enable custom window decorations:
|
||||||
|
JFrame.setDefaultLookAndFeelDecorated(true);
|
||||||
|
JDialog.setDefaultLookAndFeelDecorated(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (SystemInfo.isMacOS) {
|
||||||
|
logger.debug("Adapting UI for MacOS.");
|
||||||
|
|
||||||
|
// Move menu bar from JFrame window to top of screen:
|
||||||
|
System.setProperty("apple.laf.useScreenMenuBar", "true");
|
||||||
|
// Set name used in screen menu bar (first menu after Apple menu):
|
||||||
|
System.setProperty("apple.awt.application.name", "Symon");
|
||||||
|
// Use current macOS appearance (light/dark) for window title bars:
|
||||||
|
System.setProperty("apple.awt.application.appearance", "system");
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (SystemInfo.isWindows) {
|
||||||
|
logger.debug("Adapting UI for Windows.");
|
||||||
|
}
|
||||||
|
|
||||||
|
UIManager.setLookAndFeel(createLookAndFeel());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class<?> showMachineSelectionDialog() {
|
||||||
|
Object[] possibilities = { "Symon", "Multicomp", "Simple", "BenEater" };
|
||||||
|
String s = (String) JOptionPane.showInputDialog(
|
||||||
|
null,
|
||||||
|
"Please choose the machine type to be emulated:",
|
||||||
|
"Machine selection",
|
||||||
|
JOptionPane.PLAIN_MESSAGE,
|
||||||
|
null,
|
||||||
|
possibilities,
|
||||||
|
"Symon");
|
||||||
|
|
||||||
|
if (s != null && s.equals("Multicomp")) {
|
||||||
|
return MulticompMachine.class;
|
||||||
|
} else if (s != null && s.equals("Simple")) {
|
||||||
|
return SimpleMachine.class;
|
||||||
|
} else if (s != null && s.equals("BenEater")) {
|
||||||
|
return BenEaterMachine.class;
|
||||||
|
} else {
|
||||||
|
return SymonMachine.class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LookAndFeel createLookAndFeel() {
|
||||||
|
final OsThemeDetector themeDetector = OsThemeDetector.getDetector();
|
||||||
|
final boolean systemThemeIsDark = themeDetector.isDark();
|
||||||
|
|
||||||
|
// Set light/dark mode depending on what can be detected from the system theme:
|
||||||
|
FlatLaf theme;
|
||||||
|
if (systemThemeIsDark) {
|
||||||
|
logger.debug("Using DARK theme.");
|
||||||
|
theme = new FlatDarkLaf();
|
||||||
|
} else {
|
||||||
|
logger.debug("Using LIGHT theme.");
|
||||||
|
theme = new FlatLightLaf();
|
||||||
|
}
|
||||||
|
return theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
@@ -61,14 +61,16 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(VideoWindow.class.getName());
|
private static final Logger logger = Logger.getLogger(VideoWindow.class.getName());
|
||||||
|
|
||||||
|
private static final long WINDOW_REPAINT_INTERVAL = 66; // 30fps rate
|
||||||
private static final int CHAR_WIDTH = 8;
|
private static final int CHAR_WIDTH = 8;
|
||||||
private static final int CHAR_HEIGHT = 8;
|
private static final int CHAR_HEIGHT = 8;
|
||||||
|
private static final String ASCII_ROM = "/ascii.rom";
|
||||||
|
|
||||||
private final int scaleX, scaleY;
|
private final int scaleX, scaleY;
|
||||||
private final boolean shouldScale;
|
private final boolean shouldScale;
|
||||||
|
|
||||||
private BufferedImage image;
|
private BufferedImage image;
|
||||||
private int[] charRom;
|
private final int[] charRom;
|
||||||
|
|
||||||
private int horizontalDisplayed;
|
private int horizontalDisplayed;
|
||||||
private int verticalDisplayed;
|
private int verticalDisplayed;
|
||||||
@@ -77,9 +79,9 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
private boolean hideCursor;
|
private boolean hideCursor;
|
||||||
|
|
||||||
private Dimension dimensions;
|
private Dimension dimensions;
|
||||||
private Crtc crtc;
|
private final Crtc crtc;
|
||||||
|
|
||||||
private ScheduledExecutorService scheduler;
|
private final ScheduledExecutorService scheduler;
|
||||||
private ScheduledFuture<?> cursorBlinker;
|
private ScheduledFuture<?> cursorBlinker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,13 +124,19 @@ 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(() -> {
|
||||||
@Override
|
if (cursorBlinkRate > 0) {
|
||||||
public void run() {
|
hideCursor = !hideCursor;
|
||||||
if (cursorBlinkRate > 0) {
|
}
|
||||||
hideCursor = !hideCursor;
|
});
|
||||||
VideoWindow.this.repaint();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class WindowPainter implements Runnable {
|
||||||
|
public void run() {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
if (VideoWindow.this.isVisible()) {
|
||||||
|
VideoWindow.this.repaint();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -139,7 +147,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("/ascii.rom");
|
this.charRom = loadCharRom();
|
||||||
this.scaleX = scaleX;
|
this.scaleX = scaleX;
|
||||||
this.scaleY = scaleY;
|
this.scaleY = scaleY;
|
||||||
this.shouldScale = (scaleX > 1 || scaleY > 1);
|
this.shouldScale = (scaleX > 1 || scaleY > 1);
|
||||||
@@ -152,6 +160,11 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
TimeUnit.MILLISECONDS);
|
TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheduler.scheduleAtFixedRate(new WindowPainter(),
|
||||||
|
WINDOW_REPAINT_INTERVAL,
|
||||||
|
WINDOW_REPAINT_INTERVAL,
|
||||||
|
TimeUnit.MILLISECONDS);
|
||||||
|
|
||||||
// Capture some state from the CRTC that will define the
|
// Capture some state from the CRTC that will define the
|
||||||
// window size. When these values change, the window will
|
// window size. When these values change, the window will
|
||||||
// need to re-pack and redraw.
|
// need to re-pack and redraw.
|
||||||
@@ -274,14 +287,17 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
* Since the BufferedImage is a TYPE_BYTE_BINARY, the data must be converted
|
* Since the BufferedImage is a TYPE_BYTE_BINARY, the data must be converted
|
||||||
* into a single byte per pixel, 0 for black and 255 for white.
|
* into a single byte per pixel, 0 for black and 255 for white.
|
||||||
|
|
||||||
* @param resource The ROM file resource to load.
|
|
||||||
* @return An array of glyphs, each ready for insertion.
|
* @return An array of glyphs, each ready for insertion.
|
||||||
* @throws IOException
|
* @throws IOException Failure to load character ROM.
|
||||||
*/
|
*/
|
||||||
private int[] loadCharRom(String resource) throws IOException {
|
private int[] loadCharRom() throws IOException {
|
||||||
BufferedInputStream bis = null;
|
var resource = this.getClass().getResourceAsStream(ASCII_ROM);
|
||||||
try {
|
|
||||||
bis = new BufferedInputStream(this.getClass().getResourceAsStream(resource));
|
if (resource == null) {
|
||||||
|
throw new IOException("Resource " + ASCII_ROM + " not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
try (BufferedInputStream bis = new BufferedInputStream(resource)) {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
while (bis.available() > 0) {
|
while (bis.available() > 0) {
|
||||||
bos.write(bis.read());
|
bos.write(bis.read());
|
||||||
@@ -296,7 +312,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
int[] converted = new int[raw.length * CHAR_WIDTH];
|
int[] converted = new int[raw.length * CHAR_WIDTH];
|
||||||
|
|
||||||
int romIndex = 0;
|
int romIndex = 0;
|
||||||
for (int i = 0; i < converted.length;) {
|
for (int i = 0; i < converted.length; ) {
|
||||||
byte charRow = raw[romIndex++];
|
byte charRow = raw[romIndex++];
|
||||||
|
|
||||||
for (int j = 7; j >= 0; j--) {
|
for (int j = 7; j >= 0; j--) {
|
||||||
@@ -304,10 +320,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return converted;
|
return converted;
|
||||||
} finally {
|
|
||||||
if (bis != null) {
|
|
||||||
bis.close();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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,8 +34,8 @@ import java.util.Queue;
|
|||||||
*/
|
*/
|
||||||
public class FifoRingBuffer<E> implements Iterable<E> {
|
public class FifoRingBuffer<E> implements Iterable<E> {
|
||||||
|
|
||||||
private Queue<E> fifoBuffer;
|
private final Queue<E> fifoBuffer;
|
||||||
private int maxLength;
|
private final int maxLength;
|
||||||
|
|
||||||
public FifoRingBuffer(int maxLength) {
|
public FifoRingBuffer(int maxLength) {
|
||||||
this.fifoBuffer = new LinkedList<>();
|
this.fifoBuffer = new LinkedList<>();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
* Copyright (c) 2008-2025 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
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 191 B After Width: | Height: | Size: 220 B |
|
Before Width: | Height: | Size: 196 B After Width: | Height: | Size: 232 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 224 B |
|
Before Width: | Height: | Size: 183 B After Width: | Height: | Size: 202 B |
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 238 B |
|
Before Width: | Height: | Size: 192 B After Width: | Height: | Size: 221 B |
|
Before Width: | Height: | Size: 198 B After Width: | Height: | Size: 238 B |
@@ -0,0 +1,3 @@
|
|||||||
|
# Loaded for FlatDarkLaf theme only.
|
||||||
|
|
||||||
|
Colors.accent.default = #4B6EAF
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Loaded for all FlatLaf themes.
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Loaded for FlatLightLaf theme only.
|
||||||
|
|
||||||
|
Colors.accent.default = #2675BF
|
||||||
@@ -254,4 +254,76 @@ public class AciaTest {
|
|||||||
|
|
||||||
assertEquals(0x08, acia.read(0x0001, true));
|
assertEquals(0x08, acia.read(0x0001, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void statusRegisterInitializedAtHardwareReset() throws Exception {
|
||||||
|
Acia6551 acia = new Acia6551(0x0000);
|
||||||
|
|
||||||
|
assertEquals(0x10, acia.read(0x0001, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void commandRegisterInitializedAtHardwareReset() throws Exception {
|
||||||
|
Acia6551 acia = new Acia6551(0x0000);
|
||||||
|
|
||||||
|
assertEquals(0x02, acia.read(0x0002, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void controlRegisterInitializedAtHardwareReset() throws Exception {
|
||||||
|
Acia6551 acia = new Acia6551(0x0000);
|
||||||
|
|
||||||
|
assertEquals(0x00, acia.read(0x0003, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void programResetClearsOverrunStatus() throws Exception {
|
||||||
|
Acia6551 acia = new Acia6551(0x0000);
|
||||||
|
Bus bus = new Bus(acia.ACIA_SIZE);
|
||||||
|
acia.setBus(bus);
|
||||||
|
|
||||||
|
// Change as many status bits as we can.
|
||||||
|
acia.write(0x0002, 0x00); // enable receive interrupt
|
||||||
|
acia.rxWrite('a');
|
||||||
|
acia.rxWrite('b'); // overrun, receive full, interrupt signalled
|
||||||
|
acia.write(0x0000, 'c'); // Transmitter Data Register not empty
|
||||||
|
|
||||||
|
// Check that all the bits we expected to be set actually are
|
||||||
|
assertEquals(0x8C, acia.read(0x0001, false));
|
||||||
|
|
||||||
|
// Do a "program reset". The value is ignored.
|
||||||
|
acia.write(0x0001, 0xFF);
|
||||||
|
|
||||||
|
// Check that only bit 2 was cleared.
|
||||||
|
assertEquals(0x88, acia.read(0x0001, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void programResetKeepsParitySettings() throws Exception {
|
||||||
|
Acia6551 acia = new Acia6551(0x0000);
|
||||||
|
|
||||||
|
// Set all the command register bits
|
||||||
|
acia.write(0x0002, 0xFF);
|
||||||
|
|
||||||
|
// Do a "program reset". The value is ignored.
|
||||||
|
acia.write(0x0001, 0xFF);
|
||||||
|
|
||||||
|
// The top 3 bits should be kept as-is,
|
||||||
|
// the bottom 5 bits should be reset to defaults.
|
||||||
|
assertEquals(0xE2, acia.read(0x0002, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void programResetLeavesControlRegisterUnchanged() throws Exception {
|
||||||
|
Acia6551 acia = new Acia6551(0x0000);
|
||||||
|
|
||||||
|
// Set all the control register bits
|
||||||
|
acia.write(0x0003, 0xFF);
|
||||||
|
|
||||||
|
// Do a "program reset". The value is ignored.
|
||||||
|
acia.write(0x0001, 0xFF);
|
||||||
|
|
||||||
|
// No bits should have changed.
|
||||||
|
assertEquals(0xFF, acia.read(0x0003, false));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -169,7 +169,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
|||||||
assertEquals(0x04, bus.read(0x1fe, true));
|
assertEquals(0x04, bus.read(0x1fe, true));
|
||||||
|
|
||||||
// No flags should have changed.
|
// No flags should have changed.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BIT - Bit Test - $2c */
|
/* BIT - Bit Test - $2c */
|
||||||
@@ -362,7 +362,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x3400, cpu.getProgramCounter());
|
assertEquals(0x3400, cpu.getProgramCounter());
|
||||||
// No change to status flags.
|
// No change to status flags.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* EOR - Exclusive OR - $4d */
|
/* EOR - Exclusive OR - $4d */
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class CpuAccumulatorModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -306,4 +306,4 @@ public class CpuAccumulatorModeTest extends TestCase {
|
|||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ public class CpuImmediateModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public class CpuImpliedModeTest {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -76,6 +76,7 @@ public class CpuImpliedModeTest {
|
|||||||
public void test_BRK() throws MemoryAccessException {
|
public void test_BRK() throws MemoryAccessException {
|
||||||
cpu.setCarryFlag();
|
cpu.setCarryFlag();
|
||||||
cpu.setOverflowFlag();
|
cpu.setOverflowFlag();
|
||||||
|
cpu.clearIrqDisableFlag();
|
||||||
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW,
|
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW,
|
||||||
cpu.getProcessorStatus());
|
cpu.getProcessorStatus());
|
||||||
assertEquals(0x00, cpu.stackPeek());
|
assertEquals(0x00, cpu.stackPeek());
|
||||||
@@ -343,7 +344,7 @@ public class CpuImpliedModeTest {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x201, cpu.getProgramCounter());
|
assertEquals(0x201, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PHA - Push Accumulator - $48 */
|
/* PHA - Push Accumulator - $48 */
|
||||||
@@ -432,7 +433,7 @@ public class CpuImpliedModeTest {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
|
|
||||||
assertEquals(0x0f12, cpu.getProgramCounter());
|
assertEquals(0x0f12, cpu.getProgramCounter());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* SEC - Set Carry Flag - $38 */
|
/* SEC - Set Carry Flag - $38 */
|
||||||
@@ -641,4 +642,4 @@ public class CpuImpliedModeTest {
|
|||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertTrue(cpu.getNegativeFlag());
|
assertTrue(cpu.getNegativeFlag());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ public class CpuIndexedIndirectModeTest {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -105,4 +105,4 @@ public class CpuIndexedIndirectModeTest {
|
|||||||
assertEquals(0x11, cpu.getAccumulator());
|
assertEquals(0x11, cpu.getAccumulator());
|
||||||
assertEquals(0x31, bus.read(0xc51f, true));
|
assertEquals(0x31, bus.read(0xc51f, true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,7 +31,7 @@ public class CpuIndirectIndexedModeTest {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -81,4 +81,4 @@ public class CpuIndirectIndexedModeTest {
|
|||||||
assertEquals(0xe3, bus.read(0xd828, true));
|
assertEquals(0xe3, bus.read(0xd828, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -29,7 +29,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -48,7 +48,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x5400, cpu.getProgramCounter());
|
assertEquals(0x5400, cpu.getProgramCounter());
|
||||||
// No change to status flags.
|
// No change to status flags.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
|
public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
|
||||||
@@ -60,7 +60,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x2200, cpu.getProgramCounter());
|
assertEquals(0x2200, cpu.getProgramCounter());
|
||||||
// No change to status flags.
|
// No change to status flags.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test_JMP_withIndirectBug() throws MemoryAccessException {
|
public void test_JMP_withIndirectBug() throws MemoryAccessException {
|
||||||
@@ -72,7 +72,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x2200, cpu.getProgramCounter());
|
assertEquals(0x2200, cpu.getProgramCounter());
|
||||||
// No change to status flags.
|
// No change to status flags.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
|
public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
|
||||||
@@ -84,7 +84,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x5400, cpu.getProgramCounter());
|
assertEquals(0x5400, cpu.getProgramCounter());
|
||||||
// No change to status flags.
|
// No change to status flags.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test_JMP_cmos() throws MemoryAccessException {
|
public void test_JMP_cmos() throws MemoryAccessException {
|
||||||
@@ -96,7 +96,7 @@ public class CpuIndirectModeTest extends TestCase {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x5400, cpu.getProgramCounter());
|
assertEquals(0x5400, cpu.getProgramCounter());
|
||||||
// No change to status flags.
|
// No change to status flags.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ public class CpuIndirectXModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class CpuRelativeModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -278,4 +278,4 @@ public class CpuRelativeModeTest extends TestCase {
|
|||||||
assertEquals(0x202, cpu.getProgramCounter());
|
assertEquals(0x202, cpu.getProgramCounter());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -44,7 +44,7 @@ public class CpuTest extends TestCase {
|
|||||||
assertEquals(0x0200, cpu.getProgramCounter());
|
assertEquals(0x0200, cpu.getProgramCounter());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getIrqDisableFlag());
|
assertTrue(cpu.getIrqDisableFlag());
|
||||||
assertFalse(cpu.getDecimalModeFlag());
|
assertFalse(cpu.getDecimalModeFlag());
|
||||||
assertFalse(cpu.getBreakFlag());
|
assertFalse(cpu.getBreakFlag());
|
||||||
assertFalse(cpu.getOverflowFlag());
|
assertFalse(cpu.getOverflowFlag());
|
||||||
@@ -205,14 +205,12 @@ public class CpuTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void testGetProcessorStatus() {
|
public void testGetProcessorStatus() {
|
||||||
// By default, no flags are set. Remember, bit 5
|
// By default, only "interrupt disable" is set. Remember, bit 5
|
||||||
// is always '1'.
|
// is always '1'.
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
cpu.setCarryFlag();
|
cpu.setCarryFlag();
|
||||||
assertEquals(0x21, cpu.getProcessorStatus());
|
assertEquals(0x25, cpu.getProcessorStatus());
|
||||||
cpu.setZeroFlag();
|
cpu.setZeroFlag();
|
||||||
assertEquals(0x23, cpu.getProcessorStatus());
|
|
||||||
cpu.setIrqDisableFlag();
|
|
||||||
assertEquals(0x27, cpu.getProcessorStatus());
|
assertEquals(0x27, cpu.getProcessorStatus());
|
||||||
cpu.setDecimalModeFlag();
|
cpu.setDecimalModeFlag();
|
||||||
assertEquals(0x2f, cpu.getProcessorStatus());
|
assertEquals(0x2f, cpu.getProcessorStatus());
|
||||||
@@ -237,13 +235,16 @@ public class CpuTest extends TestCase {
|
|||||||
assertEquals(0xa0, cpu.getProcessorStatus());
|
assertEquals(0xa0, cpu.getProcessorStatus());
|
||||||
cpu.clearNegativeFlag();
|
cpu.clearNegativeFlag();
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x20, cpu.getProcessorStatus());
|
||||||
|
|
||||||
|
cpu.setIrqDisableFlag();
|
||||||
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSetProcessorStatus() {
|
public void testSetProcessorStatus() {
|
||||||
// Default
|
// Default
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getIrqDisableFlag());
|
assertTrue(cpu.getIrqDisableFlag());
|
||||||
assertFalse(cpu.getDecimalModeFlag());
|
assertFalse(cpu.getDecimalModeFlag());
|
||||||
assertFalse(cpu.getBreakFlag());
|
assertFalse(cpu.getBreakFlag());
|
||||||
assertFalse(cpu.getOverflowFlag());
|
assertFalse(cpu.getOverflowFlag());
|
||||||
@@ -669,4 +670,78 @@ public class CpuTest extends TestCase {
|
|||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x3E, cpu.getAccumulator());
|
assertEquals(0x3E, cpu.getAccumulator());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
// Have tests:
|
||||||
|
// ACC, AIX, ABS, ABX, ABY, IMP, IMM, IND, XIN, INY,
|
||||||
|
// REL, ZPG, ZPI
|
||||||
|
|
||||||
|
// Need to test:
|
||||||
|
// ZPR, ZPX, ZPY, NUL
|
||||||
|
|
||||||
|
// Question: Should 'NUL' even exist? Isn't it just 'IMP'?
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesAccumulatorCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x2a, new int[]{});
|
||||||
|
assertEquals("ROL", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesAbsoluteXIndexedIndirectCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x7c, new int[]{0x00, 0x1a});
|
||||||
|
assertEquals("JMP ($1A00,X)", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesAbsoluteCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x0d, new int[]{0x10, 0x0c});
|
||||||
|
assertEquals("ORA $0C10", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesAbsoluteXIndexedCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x1d, new int[]{0x00, 0x1c});
|
||||||
|
assertEquals("ORA $1C00,X", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesAbsoluteYIndexedCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x19, new int[]{0x00, 0x1c});
|
||||||
|
assertEquals("ORA $1C00,Y", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesImpliedCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x00, new int[]{});
|
||||||
|
assertEquals("BRK", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesIndirectCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x6c, new int[]{0x00, 0x1c});
|
||||||
|
assertEquals("JMP ($1C00)", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesImmediateCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0xA9, new int[]{0xff});
|
||||||
|
assertEquals("LDA #$FF", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesXIndexedIndirectCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x01, new int[]{0x10});
|
||||||
|
assertEquals("ORA ($10,X)", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesIndirectYIndexedCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x11, new int[]{0x10});
|
||||||
|
assertEquals("ORA ($10),Y", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesRelativeCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x10, new int[]{0xff});
|
||||||
|
assertEquals("BPL $FF", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesZeroPageCorrectly() {
|
||||||
|
var dis = Cpu.disassembleOp(0x24, new int[]{0x10});
|
||||||
|
assertEquals("BIT $10", dis);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testDisassembleOpHandlesZeroPageIndirectCorrectly() throws Exception {
|
||||||
|
var dis = Cpu.disassembleOp(0xB2, new int[]{0x1a});
|
||||||
|
assertEquals("LDA ($1A)", dis);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class CpuZeroPageYModeTest extends TestCase {
|
|||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x24, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import org.junit.Before;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.runners.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
|
|||||||