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 | ||
|
|
5f8f743f7d | ||
|
|
4ce8bc86de | ||
|
|
4644fe9b55 | ||
|
|
655bbb3ab0 | ||
|
|
e2d31fd39c | ||
|
|
087ba28b82 | ||
|
|
8edca5d595 | ||
|
|
105a09067f | ||
|
|
08efd81be3 | ||
|
|
0aeb97bb56 | ||
|
|
d466a21b3e | ||
|
|
31d1f7c5e1 | ||
|
|
247f2ba2fc | ||
|
|
922810b34b | ||
|
|
9d6791330d | ||
|
|
73c474d606 | ||
|
|
71905fdb19 | ||
|
|
cb918c9906 | ||
|
|
be72c2ff09 | ||
|
|
18ce120984 | ||
|
|
8ffb130300 | ||
|
|
441f7349a0 | ||
|
|
4f27e78940 | ||
|
|
11f61f50a9 | ||
|
|
59f8ff1bc4 | ||
|
|
a9c6d5964f | ||
|
|
faf5d22660 | ||
|
|
3c3aee30a7 | ||
|
|
92f8fe3dd9 | ||
|
|
356822df71 | ||
|
|
f2ced29979 | ||
|
|
2e33756d49 | ||
|
|
079d3dbeae | ||
|
|
96819f1bf7 | ||
|
|
9cdd718e8d | ||
|
|
5554acd29c |
No files matched your search
@@ -1,6 +1,8 @@
|
||||
*~
|
||||
*.jar
|
||||
*#
|
||||
target
|
||||
.DS_Store
|
||||
.idea
|
||||
symon.iml
|
||||
/dependency-reduced-pom.xml
|
||||
@@ -0,0 +1,5 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//src/main/java=UTF-8
|
||||
encoding//src/main/resources=UTF-8
|
||||
encoding//src/test/java=UTF-8
|
||||
encoding/<project>=UTF-8
|
||||
@@ -0,0 +1,5 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
@@ -0,0 +1,4 @@
|
||||
activeProfiles=
|
||||
eclipse.preferences.version=1
|
||||
resolveWorkspaceProjects=true
|
||||
version=1
|
||||
@@ -1,13 +1,13 @@
|
||||
SYMON - A 6502 System Simulator
|
||||
===============================
|
||||
|
||||
**Version:** 1.2.1
|
||||
**Version:** 1.5.0
|
||||
|
||||
**Last Updated:** 8 January, 2016
|
||||
**Last Updated:** 30 March, 2025
|
||||
|
||||
See the file COPYING for license.
|
||||
|
||||
![Symon Simulator in Action] (https://github.com/sethm/symon/raw/master/screenshots/full.jpg)
|
||||

|
||||
|
||||
## 1.0 About
|
||||
|
||||
@@ -16,29 +16,29 @@ Technologies 6502 microprocessor and compatibles. Symon is implemented
|
||||
in Java. Its core goals are accuracy, ease of development, clear
|
||||
documentation, and extensive test suites for validating correctness.
|
||||
|
||||
Symon simulates a complete system with a 1 MHz NMOS 6502, 32KB of RAM,
|
||||
16KB of ROM, a MOS 6551 or Motorola 6850 ACIA, a MOS 6522 VIA, and an
|
||||
experimental 6545 CRTC.
|
||||
Symon simulates a complete system with a 1 MHz NMOS 6502 or CMOS
|
||||
65C02, 32KB of RAM, 16KB of ROM, a MOS 6551 or Motorola 6850 ACIA, a
|
||||
MOS 6522 VIA, and an experimental 6545 CRTC.
|
||||
|
||||
Symon has extensive unit tests to verify correctness, and fully passes
|
||||
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).
|
||||
|
||||
Symon is under constant, active development. Feedback and patches
|
||||
are always welcome.
|
||||
Symon is under active maintenance. Feedback and patches are always
|
||||
welcome.
|
||||
|
||||
## 2.0 Requirements
|
||||
|
||||
- Java 1.8 or higher
|
||||
- Maven 2.0.x or higher (for building from source)
|
||||
- Java 10 or higher
|
||||
- Maven 3.0.x or higher (for building from source)
|
||||
- JUnit 4 or higher (for testing)
|
||||
|
||||
## 3.0 Features
|
||||
|
||||
Symon can simulate multiple 6502 based architectures. At present, three
|
||||
machines are implemented: Symon (the default), MULTICOMP, and a "Simple"
|
||||
machine useful for debugging.
|
||||
Symon can simulate multiple 6502 based architectures. At present, four
|
||||
machines are implemented: Symon (the default), MULTICOMP, BenEater, and
|
||||
a "Simple" machine useful for debugging.
|
||||
|
||||
### 3.1 Memory Maps
|
||||
|
||||
@@ -64,27 +64,49 @@ memory.
|
||||
|
||||
- `$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
|
||||
|
||||
![Serial Console] (https://github.com/sethm/symon/raw/master/screenshots/console.png)
|
||||

|
||||
|
||||
The main window of the simulator acts as the primary Input/Output
|
||||
system through a virtual serial terminal. The terminal is attached to
|
||||
a simulated ACIA, including a programmable baud rate generator that
|
||||
tries to approximate the correct "feel" of the programmed baud rate.
|
||||
(The sample Enhanced BASIC ROM image is programmed for 9600 baud)
|
||||
system through a virtual serial terminal. It also provides CPU status.
|
||||
Contents of the accumulator, index registers, processor status flags,
|
||||
disassembly of the instruction register, and stack pointer are all displayed.
|
||||
|
||||
It also provides CPU status. Contents of the accumulator, index
|
||||
registers, processor status flags, disassembly of the instruction
|
||||
register, and stack pointer are all displayed.
|
||||
The terminal is attached to a simulated MOS 6551 ACIA. It behaves very much
|
||||
as described in the datasheet, with some exceptions:
|
||||
|
||||
![Font Selection] (https://github.com/sethm/symon/raw/master/screenshots/font_selection.png)
|
||||
- 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.
|
||||
|
||||
### 3.3 ROM Loading
|
||||
|
||||
![ROM Loading] (https://github.com/sethm/symon/raw/master/screenshots/load_rom.png)
|
||||

|
||||
|
||||
Symon can load any appropriately sized ROM image. The Symon
|
||||
architecture expects as 16KB (16384 byte) ROM image, while the
|
||||
@@ -95,32 +117,32 @@ address.
|
||||
|
||||
### 3.4 Memory Window
|
||||
|
||||
![Memory Window] (https://github.com/sethm/symon/raw/master/screenshots/memory_window.png)
|
||||

|
||||
|
||||
Memory contents can be viewed (and edited) one page at a time through the Memory Window.
|
||||
|
||||
### 3.5 Trace Log
|
||||
|
||||
![Trace Log] (https://github.com/sethm/symon/raw/master/screenshots/trace_log.png)
|
||||

|
||||
|
||||
The last 20,000 execution steps are disassembled and logged to the Trace Log
|
||||
Window.
|
||||
|
||||
### 3.6 Simulator Speeds
|
||||
|
||||
![Speeds] (https://github.com/sethm/symon/raw/master/screenshots/simulator_menu.png)
|
||||

|
||||
|
||||
Simulated speeds may be set from 1MHz to 8MHz.
|
||||
|
||||
### 3.7 Breakpoints
|
||||
|
||||
![Breakpoints] (https://github.com/sethm/symon/raw/master/screenshots/breakpoints.png)
|
||||

|
||||
|
||||
Breakpoints can be set and removed through the Breakpoints window.
|
||||
|
||||
### 3.8 Experimental 6545 CRTC Video
|
||||
|
||||
![Composite Video] (https://github.com/sethm/symon/raw/master/screenshots/video_window.png)
|
||||

|
||||
|
||||
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
|
||||
@@ -154,13 +176,13 @@ between the simulated 6545 and a real 6545:
|
||||
of 6545 programming tricks that were achieved by updating the
|
||||
frame address during vertical and horizontal sync times are not
|
||||
achievable. There is no way (for example) to change the Display Start
|
||||
Address (R12 and R13) while a frame is being drawn.
|
||||
Address (R12 and R13) while a frame is being drawn.
|
||||
|
||||
For more information on the 6545 CRTC and its programming model, please see the following resources
|
||||
|
||||
- [CRTC 6545/6845 Information (André Fachat)] (http://6502.org/users/andre/hwinfo/crtc/index.html)
|
||||
- [CRTC Operation (André Fachat)] (http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
|
||||
- [MOS 6545 Datasheet (PDF)] (http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
|
||||
- [CRTC 6545/6845 Information (André Fachat)](http://6502.org/users/andre/hwinfo/crtc/index.html)
|
||||
- [CRTC Operation (André Fachat)](http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
|
||||
- [MOS 6545 Datasheet (PDF)](http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
|
||||
|
||||
|
||||
#### 3.8.1 Example BASIC Program to test Video
|
||||
@@ -193,17 +215,41 @@ Java 1.8 or greater, just type:
|
||||
When Symon is running, you should be presented with a simple graphical
|
||||
interface.
|
||||
|
||||
### 4.2 ROM images
|
||||
#### 4.1.1 Command Line Options
|
||||
|
||||
Two command line options may be passed to the JAR file on startup,
|
||||
to specify machine type and CPU type. The options are:
|
||||
|
||||
- `-c`,`-cpu 6502`: Use the NMOS 6502 CPU type by default.
|
||||
- `-c`,`-cpu 65c02`: Use the CMOS 65C02 CPU type by default.
|
||||
- `-c`,`-machine symon`: Use the **Symon** machine type by default.
|
||||
- `-c`,`-machine multicomp`: Use the **Multicomp** machine type by default.
|
||||
- `-m`,`-machine simple`: Use the **Simple** machine type by default.
|
||||
- `-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 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
|
||||
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
|
||||
address space.
|
||||
|
||||
By default, any file named `rom.bin` that exists in the same directory
|
||||
where Symon is launched will be loaded as a ROM image. ROM images can
|
||||
also be swapped out at run-time with the "Load ROM Image..." in the
|
||||
File menu.
|
||||
ROM images can be loaded with the `-rom` argument when running
|
||||
Symon from the command line. ROM images can also be swapped out at
|
||||
run-time with the "Load ROM..." item in the File menu.
|
||||
|
||||
The "samples" directory contains a ROM image for the Symon
|
||||
architecture named 'ehbasic.rom', containing Lee Davison's Enhanced
|
||||
@@ -212,7 +258,7 @@ architecture named 'ehbasic.rom', containing Lee Davison's Enhanced
|
||||
*Note*: Presently, EhBASIC only works with the Symon machine
|
||||
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
|
||||
be loaded directly into memory from "Load Program..." in the File menu.
|
||||
@@ -228,23 +274,50 @@ for testing.
|
||||
|
||||
- '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
|
||||
running.
|
||||
|
||||
## 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
|
||||
option `-cpu <type>` to specify one of `6502` or `65c02` on startup,
|
||||
and new option `-rom <file>` to specify a ROM file to load.
|
||||
|
||||
- **1.3.0:** 24 February, 2018 - Adds support for 65C02 opcodes.
|
||||
|
||||
- **1.2.1:** 8 January, 2016 - Remove dependency on Java 8. Now
|
||||
supports compiling and running under Java 1.7.
|
||||
|
||||
- **1.2.0:** 3 January, 2016 - Add symbolic disassembly to breakpoints
|
||||
window.
|
||||
|
||||
|
||||
- **1.1.1:** 2 January, 2016 - Minor enhancement: Allows breakpoints
|
||||
to be added with the Enter key.
|
||||
|
||||
- **1.1.0:** 31 December, 2015 - Fixed delay loop to better
|
||||
- **1.1.0:** 31 December, 2015 - Fixed delay loop to better
|
||||
simulate various clock speeds. Added ability to select clock
|
||||
speed at runtime. Status display now shows the next instruction
|
||||
to be executed, instead of the last instruction executed.
|
||||
@@ -304,17 +377,12 @@ running.
|
||||
|
||||
- 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.
|
||||
|
||||
- More extensive testing.
|
||||
|
||||
- Clean up JavaDoc.
|
||||
|
||||
- Implement CMOS 65C02 instructions and NMOS / CMOS mode flag.
|
||||
|
||||
- Allow displaying ACIA status and dumping ACIA buffers, for
|
||||
debugging.
|
||||
|
||||
@@ -324,11 +392,19 @@ running.
|
||||
|
||||
- Symbolic debugging.
|
||||
|
||||
- Better / correct disassembly and tracing of 65C02 and 65C816
|
||||
instructions.
|
||||
|
||||
## 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.
|
||||
|
||||
@@ -338,10 +414,10 @@ Additional components used in this project are copyright their respective owners
|
||||
|
||||
This project would not have been possible without the following resources:
|
||||
|
||||
- [Andrew Jacobs' 6502 Pages] (http://www.obelisk.demon.co.uk/6502/), for
|
||||
- [Andrew Jacobs' 6502 Pages](http://obelisk.me.uk/6502/), for
|
||||
wonderfully detailed information about the 6502
|
||||
|
||||
- [Neil Parker's "The 6502/65C02/65C816 Instruction Set Decoded"] (http://www.llx.com/~nparker/a2/opcodes.html),
|
||||
- [Neil Parker's "The 6502/65C02/65C816 Instruction Set Decoded"](http://www.llx.com/~nparker/a2/opcodes.html),
|
||||
for information about how instructions are coded
|
||||
|
||||
## 9.0 Licensing
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<project xmlns="https://maven.apache.org/POM/4.0.0"
|
||||
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>
|
||||
<groupId>com.loomcom.symon</groupId>
|
||||
<artifactId>symon</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<version>1.3.0-SNAPSHOT</version>
|
||||
<version>1.5.0</version>
|
||||
<name>symon</name>
|
||||
<url>http://www.loomcom.com/symon</url>
|
||||
<url>https://git.loomcom.com/seth/symon</url>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>
|
||||
UTF-8
|
||||
@@ -17,44 +18,41 @@
|
||||
</properties>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jline</id>
|
||||
<name>JLine Project Repository</name>
|
||||
<url>http://jline.sourceforge.net/m2repo</url>
|
||||
</repository>
|
||||
<!-- Loomcom's Maven2 repository for JTerminal -->
|
||||
<repository>
|
||||
<releases>
|
||||
<enabled>true</enabled>
|
||||
<updatePolicy>always</updatePolicy>
|
||||
<checksumPolicy>fail</checksumPolicy>
|
||||
</releases>
|
||||
<id>com.loomcom</id>
|
||||
<name>Loom Communications Maven2 Repository</name>
|
||||
<url>http://www.loomcom.com/maven2</url>
|
||||
<layout>default</layout>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<version>1.1.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>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.11</version>
|
||||
<version>4.13.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.grahamedgecombe.jterminal</groupId>
|
||||
<artifactId>jterminal</artifactId>
|
||||
<version>1.0.2.3-loomcom</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-all</artifactId>
|
||||
<version>1.9.5</version>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>4.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
@@ -63,24 +61,32 @@
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<archive>
|
||||
<manifest>
|
||||
<transformers>
|
||||
<transformer
|
||||
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<mainClass>com.loomcom.symon.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<filters>
|
||||
<filter>
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
@@ -88,11 +94,11 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.1</version>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<compilerArgument>-Xlint:unchecked</compilerArgument>
|
||||
<source>1.7</source>
|
||||
<target>1.7</target>
|
||||
<source>11</source>
|
||||
<target>11</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
@@ -100,7 +106,7 @@
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>2.6</version>
|
||||
<version>3.2.0</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
@@ -132,4 +138,4 @@
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
</project>
|
||||
@@ -11,7 +11,6 @@ Sample Programs
|
||||
When loaded at address $0300, this program will echo back to the console
|
||||
anything typed.
|
||||
|
||||
|
||||
Both hello.prg and echo_poll.prg were assembled with the Ophis assembler:
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
This is Lee Davison's Enhanced 6502 BASIC.
|
||||
|
||||
To use this ROM image, just copy the file 'ehbasic.rom' into the directory
|
||||
where you run Symon. Rename the file to 'rom.bin'. When you start Symon,
|
||||
the ROM file will be automatically loaded at address $d000.
|
||||
The EhBASIC ROM can be loaded either through the "File -> Load ROM..."
|
||||
menu in Symon, or by specifying the path to "ehbasic.rom" with
|
||||
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.
|
||||
|
||||
Type 'C' to do a cold start.
|
||||
|
||||
Then, type $C000 when prompted for the memory size.
|
||||
|
||||
NOTE: EhBASIC only wants upper-case input. This confused me at first!
|
||||
Then, type '49152' (without the quotes) when prompted for the memory size.
|
||||
|
||||
More information can be found in the 'ehbasic' directory, and by visiting
|
||||
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;
|
||||
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
@@ -9,8 +32,8 @@ import java.util.TreeSet;
|
||||
|
||||
public class Breakpoints extends AbstractTableModel {
|
||||
|
||||
private TreeSet<Integer> breakpoints;
|
||||
private Simulator simulator;
|
||||
private final TreeSet<Integer> breakpoints;
|
||||
private final Simulator simulator;
|
||||
|
||||
public Breakpoints(Simulator simulator) {
|
||||
this.breakpoints = new TreeSet<>();
|
||||
@@ -26,11 +49,6 @@ public class Breakpoints extends AbstractTableModel {
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void removeBreakpoint(int address) {
|
||||
this.breakpoints.remove(address);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void removeBreakpointAtIndex(int index) {
|
||||
if (index < 0) {
|
||||
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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -42,19 +42,19 @@ import java.util.TreeSet;
|
||||
public class Bus {
|
||||
|
||||
// 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
|
||||
private int startAddress = 0x0000;
|
||||
private int endAddress = 0xffff;
|
||||
private final int startAddress;
|
||||
private final int endAddress;
|
||||
|
||||
// The CPU
|
||||
private Cpu cpu;
|
||||
|
||||
// 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;
|
||||
|
||||
|
||||
@@ -94,8 +94,8 @@ public class Bus {
|
||||
* Add a device to the bus.
|
||||
*
|
||||
* @param device Device to add
|
||||
* @param priority Bus prioirity.
|
||||
* @throws MemoryRangeException
|
||||
* @param priority Bus priority.
|
||||
* @throws MemoryRangeException Invalid device address range.
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
||||
|
||||
if (deviceSet == null) {
|
||||
deviceSet = new TreeSet<>();
|
||||
deviceMap.put(priority, deviceSet);
|
||||
}
|
||||
SortedSet<Device> deviceSet = deviceMap.computeIfAbsent(priority, k -> new TreeSet<>());
|
||||
|
||||
device.setBus(this);
|
||||
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.
|
||||
*
|
||||
* @param device Device to add
|
||||
* @throws MemoryRangeException
|
||||
* @throws MemoryRangeException Invalid device memory range.
|
||||
*/
|
||||
public void addDevice(Device device) throws MemoryRangeException {
|
||||
addDevice(device, 0);
|
||||
@@ -168,12 +163,12 @@ public class Bus {
|
||||
return true;
|
||||
}
|
||||
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
Device d = deviceAddressArray[address - this.startAddress];
|
||||
if (d != null) {
|
||||
MemoryRange range = d.getMemoryRange();
|
||||
int devAddr = address - range.startAddress();
|
||||
return d.read(devAddr) & 0xff;
|
||||
return d.read(devAddr, cpuAccess) & 0xff;
|
||||
}
|
||||
|
||||
throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address));
|
||||
@@ -224,9 +219,7 @@ public class Bus {
|
||||
|
||||
for (int priority : priorities) {
|
||||
SortedSet<Device> deviceSet = deviceMap.get(priority);
|
||||
for (Device device : deviceSet) {
|
||||
devices.add(device);
|
||||
}
|
||||
devices.addAll(deviceSet);
|
||||
}
|
||||
|
||||
return devices;
|
||||
|
||||
@@ -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;
|
||||
|
||||
import com.loomcom.symon.util.Utils;
|
||||
@@ -12,7 +35,7 @@ public class CpuState {
|
||||
public int a;
|
||||
|
||||
/**
|
||||
* X index regsiter
|
||||
* X index register
|
||||
*/
|
||||
public int x;
|
||||
|
||||
@@ -61,13 +84,12 @@ public class CpuState {
|
||||
public CpuState() {}
|
||||
|
||||
/**
|
||||
* Snapshot a copy of the CpuState.
|
||||
*
|
||||
* (This is a copy constructor rather than an implementation of <code>Cloneable</code>
|
||||
* based on Josh Bloch's recommendation)
|
||||
* Snapshot a copy of the CpuState. (This is a copy constructor rather than an
|
||||
* implementation of <code>Cloneable</code> based on Josh Bloch's recommendation)
|
||||
*
|
||||
* @param s The CpuState to copy.
|
||||
*/
|
||||
@SuppressWarnings("CopyConstructorMissesField")
|
||||
public CpuState(CpuState s) {
|
||||
this.a = s.a;
|
||||
this.x = s.x;
|
||||
@@ -83,6 +105,7 @@ public class CpuState {
|
||||
this.nextArgs[1] = s.nextArgs[1];
|
||||
this.instSize = s.instSize;
|
||||
this.opTrap = s.opTrap;
|
||||
this.nmiAsserted = s.nmiAsserted;
|
||||
this.irqAsserted = s.irqAsserted;
|
||||
this.carryFlag = s.carryFlag;
|
||||
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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -25,26 +25,24 @@ package com.loomcom.symon;
|
||||
|
||||
public interface InstructionTable {
|
||||
|
||||
int BRK = 0x00;
|
||||
int JSR = 0x20;
|
||||
int RTS = 0x60;
|
||||
|
||||
/**
|
||||
* Enumeration of valid CPU behaviors. These determine what behavior and instruction
|
||||
* 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 {
|
||||
/**
|
||||
* 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
|
||||
* is very rare in the wild.
|
||||
*
|
||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
||||
*/
|
||||
NMOS_WITH_ROR_BUG,
|
||||
|
||||
/**
|
||||
* All NMOS 6502's have a bug with the indirect JMP instruction. If the
|
||||
*
|
||||
* NB: Does NOT implement "unimplemented" NMOS instructions.
|
||||
*/
|
||||
NMOS_6502,
|
||||
|
||||
@@ -69,6 +67,12 @@ public interface InstructionTable {
|
||||
}
|
||||
},
|
||||
|
||||
AIX {
|
||||
public String toString() {
|
||||
return "Absolute, X-Indexed Indirect";
|
||||
}
|
||||
},
|
||||
|
||||
ABS {
|
||||
public String toString() {
|
||||
return "Absolute";
|
||||
@@ -129,6 +133,12 @@ public interface InstructionTable {
|
||||
}
|
||||
},
|
||||
|
||||
ZPR {
|
||||
public String toString() {
|
||||
return "Zero Page, Relative";
|
||||
}
|
||||
},
|
||||
|
||||
ZPX {
|
||||
public String toString() {
|
||||
return "Zero Page, X-indexed";
|
||||
@@ -158,41 +168,44 @@ public interface InstructionTable {
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
"BRK", "ORA", null, null, null, "ORA", "ASL", null, // 0x00-0x07
|
||||
"PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, // 0x08-0x0f
|
||||
"BPL", "ORA", "ORA", null, null, "ORA", "ASL", null, // 0x10-0x17
|
||||
"CLC", "ORA", null, null, null, "ORA", "ASL", null, // 0x18-0x1f
|
||||
"JSR", "AND", null, null, "BIT", "AND", "ROL", null, // 0x20-0x27
|
||||
"PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, // 0x28-0x2f
|
||||
"BMI", "AND", "AND", null, null, "AND", "ROL", null, // 0x30-0x37
|
||||
"SEC", "AND", null, null, null, "AND", "ROL", null, // 0x38-0x3f
|
||||
"RTI", "EOR", null, null, null, "EOR", "LSR", null, // 0x40-0x47
|
||||
"PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, // 0x48-0x4f
|
||||
"BVC", "EOR", "EOR", null, null, "EOR", "LSR", null, // 0x50-0x57
|
||||
"CLI", "EOR", null, null, null, "EOR", "LSR", null, // 0x58-0x5f
|
||||
"RTS", "ADC", null, null, null, "ADC", "ROR", null, // 0x60-0x67
|
||||
"PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, // 0x68-0x6f
|
||||
"BVS", "ADC", "ADC", null, null, "ADC", "ROR", null, // 0x70-0x77
|
||||
"SEI", "ADC", null, null, "JMP", "ADC", "ROR", null, // 0x78-0x7f
|
||||
"BCS", "STA", null, null, "STY", "STA", "STX", null, // 0x80-0x87
|
||||
"DEY", null, "TXA", null, "STY", "STA", "STX", null, // 0x88-0x8f
|
||||
"BCC", "STA", "STA", null, "STY", "STA", "STX", null, // 0x90-0x97
|
||||
"TYA", "STA", "TXS", null, null, "STA", null, null, // 0x98-0x9f
|
||||
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, // 0xa0-0xa7
|
||||
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, // 0xa8-0xaf
|
||||
"BCS", "LDA", "LDA", null, "LDY", "LDA", "LDX", null, // 0xb0-0xb7
|
||||
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, // 0xb8-0xbf
|
||||
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, // 0xc0-0xc7
|
||||
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, // 0xc8-0xcf
|
||||
"BNE", "CMP", "CMP", null, null, "CMP", "DEC", null, // 0xd0-0xd7
|
||||
"CLD", "CMP", null, null, null, "CMP", "DEC", null, // 0xd8-0xdf
|
||||
"CPX", "SBC", null, null, "CPX", "SBC", "INC", null, // 0xe0-0xe7
|
||||
"INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, // 0xe8-0xef
|
||||
"BEQ", "SBC", "SBC", null, null, "SBC", "INC", null, // 0xf0-0xf7
|
||||
"SED", "SBC", null, null, null, "SBC", "INC", null // 0xf8-0xff
|
||||
"BRK", "ORA", "NOP", "NOP", "TSB", "ORA", "ASL", "RMB0", // 0x00-0x07
|
||||
"PHP", "ORA", "ASL", "NOP", "TSB", "ORA", "ASL", "BBR0", // 0x08-0x0f
|
||||
"BPL", "ORA", "ORA", "NOP", "TRB", "ORA", "ASL", "RMB1", // 0x10-0x17
|
||||
"CLC", "ORA", "INC", "NOP", "TRB", "ORA", "ASL", "BBR1", // 0x18-0x1f
|
||||
"JSR", "AND", "NOP", "NOP", "BIT", "AND", "ROL", "RMB2", // 0x20-0x27
|
||||
"PLP", "AND", "ROL", "NOP", "BIT", "AND", "ROL", "BBR2", // 0x28-0x2f
|
||||
"BMI", "AND", "AND", "NOP", "BIT", "AND", "ROL", "RMB3", // 0x30-0x37
|
||||
"SEC", "AND", "DEC", "NOP", "BIT", "AND", "ROL", "BBR3", // 0x38-0x3f
|
||||
"RTI", "EOR", "NOP", "NOP", "NOP", "EOR", "LSR", "RMB4", // 0x40-0x47
|
||||
"PHA", "EOR", "LSR", "NOP", "JMP", "EOR", "LSR", "BBR4", // 0x48-0x4f
|
||||
"BVC", "EOR", "EOR", "NOP", "NOP", "EOR", "LSR", "RMB5", // 0x50-0x57
|
||||
"CLI", "EOR", "PHY", "NOP", "NOP", "EOR", "LSR", "BBR5", // 0x58-0x5f
|
||||
"RTS", "ADC", "NOP", "NOP", "STZ", "ADC", "ROR", "RMB6", // 0x60-0x67
|
||||
"PLA", "ADC", "ROR", "NOP", "JMP", "ADC", "ROR", "BBR6", // 0x68-0x6f
|
||||
"BVS", "ADC", "ADC", "NOP", "STZ", "ADC", "ROR", "RMB7", // 0x70-0x77
|
||||
"SEI", "ADC", "PLY", "NOP", "JMP", "ADC", "ROR", "BBR7", // 0x78-0x7f
|
||||
"BRA", "STA", "NOP", "NOP", "STY", "STA", "STX", "SMB0", // 0x80-0x87
|
||||
"DEY", "BIT", "TXA", "NOP", "STY", "STA", "STX", "BBS0", // 0x88-0x8f
|
||||
"BCC", "STA", "STA", "NOP", "STY", "STA", "STX", "SMB1", // 0x90-0x97
|
||||
"TYA", "STA", "TXS", "NOP", "STZ", "STA", "STZ", "BBS1", // 0x98-0x9f
|
||||
"LDY", "LDA", "LDX", "NOP", "LDY", "LDA", "LDX", "SMB2", // 0xa0-0xa7
|
||||
"TAY", "LDA", "TAX", "NOP", "LDY", "LDA", "LDX", "BBS2", // 0xa8-0xaf
|
||||
"BCS", "LDA", "LDA", "NOP", "LDY", "LDA", "LDX", "SMB3", // 0xb0-0xb7
|
||||
"CLV", "LDA", "TSX", "NOP", "LDY", "LDA", "LDX", "BBS3", // 0xb8-0xbf
|
||||
"CPY", "CMP", "NOP", "NOP", "CPY", "CMP", "DEC", "SMB4", // 0xc0-0xc7
|
||||
"INY", "CMP", "DEX", "NOP", "CPY", "CMP", "DEC", "BBS4", // 0xc8-0xcf
|
||||
"BNE", "CMP", "CMP", "NOP", "NOP", "CMP", "DEC", "SMB5", // 0xd0-0xd7
|
||||
"CLD", "CMP", "PHX", "NOP", "NOP", "CMP", "DEC", "BBS5", // 0xd8-0xdf
|
||||
"CPX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "SMB6", // 0xe0-0xe7
|
||||
"INX", "SBC", "NOP", "NOP", "CPX", "SBC", "INC", "BBS6", // 0xe8-0xef
|
||||
"BEQ", "SBC", "SBC", "NOP", "NOP", "SBC", "INC", "SMB7", // 0xf0-0xf7
|
||||
"SED", "SBC", "PLX", "NOP", "NOP", "SBC", "INC", "BBS7" // 0xf8-0xff
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -202,69 +215,69 @@ public interface InstructionTable {
|
||||
*/
|
||||
Mode[] instructionModes = {
|
||||
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
|
||||
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0x04-0x07
|
||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
|
||||
Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x0c-0x0f
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x10-0x13
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17
|
||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f
|
||||
Mode.ZPG, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x14-0x17
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x18-0x1b
|
||||
Mode.ABS, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x1c-0x1f
|
||||
Mode.ABS, Mode.XIN, Mode.NUL, Mode.NUL, // 0x20-0x23
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0x24-0x27
|
||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x2c-0x2f
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x30-0x33
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37
|
||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f
|
||||
Mode.ZPX, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x34-0x37
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x38-0x3b
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x3c-0x3f
|
||||
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x40-0x43
|
||||
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47
|
||||
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0x44-0x47
|
||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x4c-0x4f
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x50-0x53
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57
|
||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x54-0x57
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x58-0x5b
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x5c-0x5f
|
||||
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x60-0x63
|
||||
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0x64-0x67
|
||||
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b
|
||||
Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f
|
||||
Mode.IND, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x6c-0x6f
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x70-0x73
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77
|
||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b
|
||||
Mode.ABX, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
|
||||
Mode.ZPX, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0x74-0x77
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x78-0x7b
|
||||
Mode.AIX, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x7c-0x7f
|
||||
Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87
|
||||
Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0x84-0x87
|
||||
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0x88-0x8b
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x8c-0x8f
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x90-0x93
|
||||
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
|
||||
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.ZPG, // 0x94-0x97
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
|
||||
Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
|
||||
Mode.ABS, Mode.ABX, Mode.ABX, Mode.ZPR, // 0x9c-0x9f
|
||||
Mode.IMM, Mode.XIN, Mode.IMM, Mode.NUL, // 0xa0-0xa3
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0xa4-0xa7
|
||||
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0xac-0xaf
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xb0-0xb3
|
||||
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
|
||||
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.ZPG, // 0xb4-0xb7
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb
|
||||
Mode.ABX, Mode.ABX, Mode.ABY, Mode.NUL, // 0xbc-0xbf
|
||||
Mode.ABX, Mode.ABX, Mode.ABY, Mode.ZPR, // 0xbc-0xbf
|
||||
Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xc0-0xc3
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0xc4-0xc7
|
||||
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0xcc-0xcf
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xd0-0xd3
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7
|
||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0xd4-0xd7
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xd8-0xdb
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR, // 0xdc-0xdf
|
||||
Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xe0-0xe3
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7
|
||||
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.ZPG, // 0xe4-0xe7
|
||||
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef
|
||||
Mode.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0xec-0xef
|
||||
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xf0-0xf3
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7
|
||||
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
|
||||
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.ZPG, // 0xf4-0xf7
|
||||
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xf8-0xfb
|
||||
Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR // 0xfc-0xff
|
||||
};
|
||||
|
||||
|
||||
@@ -274,22 +287,22 @@ public interface InstructionTable {
|
||||
* and CMOS 65C816
|
||||
*/
|
||||
int[] instructionSizes = {
|
||||
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, // 0x00-0x0f
|
||||
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x10-0x1f
|
||||
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x20-0x2f
|
||||
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x30-0x3f
|
||||
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x40-0x4f
|
||||
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x50-0x5f
|
||||
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x60-0x6f
|
||||
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 3, 3, 3, 0, // 0x70-0x7f
|
||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, // 0x80-0x8f
|
||||
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, // 0x90-0x9f
|
||||
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xa0-0xaf
|
||||
2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 0xb0-0xbf
|
||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xc0-0xcf
|
||||
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0xd0-0xdf
|
||||
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xe0-0xef
|
||||
2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 // 0xf0-0xff
|
||||
1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x00-0x0f
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x10-0x1f
|
||||
3, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x20-0x2f
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x30-0x3f
|
||||
1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x40-0x4f
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x50-0x5f
|
||||
1, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x60-0x6f
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x70-0x7f
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0x80-0x8f
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0x90-0x9f
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0xa0-0xaf
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0xb0-0xbf
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0xc0-0xcf
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3, // 0xd0-0xdf
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, // 0xe0-0xef
|
||||
2, 2, 2, 1, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 3 // 0xf0-0xff
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -297,22 +310,22 @@ public interface InstructionTable {
|
||||
* in NMOS mode.
|
||||
*/
|
||||
int[] instructionClocksNmos = {
|
||||
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
|
||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
|
||||
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
|
||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
|
||||
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
|
||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
|
||||
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
|
||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
|
||||
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
|
||||
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
|
||||
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
|
||||
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
|
||||
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
|
||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
|
||||
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
|
||||
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
|
||||
7, 6, 1, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, // 0x00-0x0f
|
||||
2, 5, 1, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 0x10-0x1f
|
||||
6, 6, 1, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, // 0x20-0x2f
|
||||
2, 5, 1, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 0x30-0x3f
|
||||
6, 6, 1, 8, 3, 3, 5, 5, 3, 2, 2, 2, 3, 4, 6, 6, // 0x40-0x4f
|
||||
2, 5, 1, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 0x50-0x5f
|
||||
6, 6, 1, 8, 3, 3, 5, 5, 4, 2, 2, 2, 5, 4, 6, 6, // 0x60-0x6f
|
||||
2, 5, 1, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 0x70-0x7f
|
||||
2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, // 0x80-0x8f
|
||||
2, 6, 1, 6, 4, 4, 4, 4, 2, 5, 2, 5, 5, 5, 5, 5, // 0x90-0x9f
|
||||
2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, // 0xa0-0xaf
|
||||
2, 5, 1, 5, 4, 4, 4, 4, 2, 4, 2, 4, 4, 4, 4, 4, // 0xb0-0xbf
|
||||
2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, // 0xc0-0xcf
|
||||
2, 5, 1, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 0xd0-0xdf
|
||||
2, 6, 2, 8, 3, 3, 5, 5, 2, 2, 2, 2, 4, 4, 6, 6, // 0xe0-0xef
|
||||
2, 5, 1, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7 // 0xf0-0xff
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -320,22 +333,22 @@ public interface InstructionTable {
|
||||
* in CMOS mode
|
||||
*/
|
||||
int[] instructionClocksCmos = {
|
||||
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
|
||||
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
|
||||
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
|
||||
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
|
||||
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
|
||||
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
|
||||
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
|
||||
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
|
||||
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
|
||||
2, 6, 5, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
|
||||
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
|
||||
2, 5, 5, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
|
||||
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
|
||||
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
|
||||
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
|
||||
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
|
||||
7, 6, 2, 1, 5, 3, 5, 5, 3, 2, 2, 1, 6, 4, 6, 5, // 0x00-0x0f
|
||||
2, 5, 5, 1, 5, 4, 6, 5, 2, 4, 2, 1, 6, 4, 6, 5, // 0x10-0x1f
|
||||
6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 4, 4, 6, 5, // 0x20-0x2f
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 2, 1, 4, 4, 6, 5, // 0x30-0x3f
|
||||
6, 6, 2, 1, 2, 3, 5, 3, 3, 2, 2, 1, 3, 4, 6, 5, // 0x40-0x4f
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 1, 8, 4, 6, 5, // 0x50-0x5f
|
||||
6, 6, 2, 1, 3, 3, 5, 5, 4, 2, 2, 1, 6, 4, 6, 5, // 0x60-0x6f
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 3, 6, 4, 6, 5, // 0x70-0x7f
|
||||
3, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, // 0x80-0x8f
|
||||
2, 6, 5, 1, 4, 4, 4, 5, 2, 5, 2, 1, 4, 5, 5, 5, // 0x90-0x9f
|
||||
2, 6, 2, 1, 3, 3, 3, 5, 2, 2, 2, 1, 4, 4, 4, 5, // 0xa0-0xaf
|
||||
2, 5, 5, 1, 4, 4, 4, 5, 2, 4, 2, 1, 4, 4, 4, 5, // 0xb0-0xbf
|
||||
2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 3, 4, 4, 6, 5, // 0xc0-0xcf
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 3, 3, 4, 4, 7, 5, // 0xd0-0xdf
|
||||
2, 6, 2, 1, 3, 3, 5, 5, 2, 2, 2, 1, 4, 4, 6, 5, // 0xe0-0xef
|
||||
2, 5, 5, 1, 4, 4, 6, 5, 2, 4, 4, 1, 4, 4, 7, 5 // 0xf0-0xff
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -22,90 +22,136 @@
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.machines.MulticompMachine;
|
||||
import com.loomcom.symon.machines.SimpleMachine;
|
||||
import com.loomcom.symon.machines.SymonMachine;
|
||||
import com.loomcom.symon.machines.*;
|
||||
import com.loomcom.symon.ui.Ui;
|
||||
|
||||
import org.apache.commons.cli.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Locale;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.UIManager;
|
||||
|
||||
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
|
||||
* window.
|
||||
*
|
||||
* @param args Program arguments
|
||||
*/
|
||||
public static void main(String args[]) throws Exception {
|
||||
|
||||
Class machineClass = SymonMachine.class;
|
||||
for(int i = 0; i < args.length; ++i) {
|
||||
String arg = args[i].toLowerCase(Locale.ENGLISH);
|
||||
if(arg.equals("-machine") && (i+1) < args.length) {
|
||||
String machine = args[i+1].trim().toLowerCase(Locale.ENGLISH);
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
Class<?> machineClass = null;
|
||||
|
||||
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("c", "cpu", true, "Specify CPU type."));
|
||||
options.addOption(new Option("r", "rom", true, "Specify ROM file."));
|
||||
options.addOption(new Option("b", "brk", false, "Halt on BRK"));
|
||||
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
|
||||
try {
|
||||
|
||||
CommandLine line = parser.parse(options, args);
|
||||
InstructionTable.CpuBehavior cpuBehavior = 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")) {
|
||||
String machine = line.getOptionValue("machine").toLowerCase(Locale.ENGLISH);
|
||||
switch (machine) {
|
||||
case "symon":
|
||||
machineClass = SymonMachine.class;
|
||||
break;
|
||||
case "multicomp":
|
||||
machineClass = MulticompMachine.class;
|
||||
break;
|
||||
case "simple":
|
||||
machineClass = SimpleMachine.class;
|
||||
break;
|
||||
case "symon":
|
||||
machineClass = SymonMachine.class;
|
||||
break;
|
||||
case "beneater":
|
||||
machineClass = BenEaterMachine.class;
|
||||
break;
|
||||
default:
|
||||
logger.error("Could not start Symon. Unknown machine type {}", machine);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (machineClass == null) {
|
||||
Object[] possibilities = {"Symon", "Multicomp", "Simple"};
|
||||
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 (line.hasOption("cpu")) {
|
||||
String cpu = line.getOptionValue("cpu").toLowerCase(Locale.ENGLISH);
|
||||
switch (cpu) {
|
||||
case "6502":
|
||||
cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502;
|
||||
break;
|
||||
case "65c02":
|
||||
cpuBehavior = InstructionTable.CpuBehavior.CMOS_6502;
|
||||
break;
|
||||
case "65c816":
|
||||
cpuBehavior = InstructionTable.CpuBehavior.CMOS_65816;
|
||||
break;
|
||||
default:
|
||||
logger.error("Could not start Symon. Unknown cpu type {}", cpu);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Simulator simulator = new Simulator(machineClass);
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (line.hasOption("rom")) {
|
||||
romFile = line.getOptionValue("rom");
|
||||
}
|
||||
|
||||
if (line.hasOption("brk")) {
|
||||
haltOnBreak = true;
|
||||
}
|
||||
|
||||
Ui.initUi();
|
||||
|
||||
while (true) {
|
||||
if (machineClass == null) {
|
||||
machineClass = Ui.showMachineSelectionDialog();
|
||||
}
|
||||
|
||||
if (cpuBehavior == null) {
|
||||
cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502;
|
||||
}
|
||||
|
||||
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile, haltOnBreak);
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// Create the main UI window:
|
||||
try {
|
||||
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
||||
// Create the main UI window
|
||||
simulator.createAndShowUi();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
logger.error("Error during Symon UI initialization: {}", e.getMessage());
|
||||
System.exit(-1);
|
||||
}
|
||||
});
|
||||
|
||||
Simulator.MainCommand cmd = simulator.waitForCommand();
|
||||
|
||||
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
|
||||
machineClass = null;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Simulator.MainCommand cmd = simulator.waitForCommand();
|
||||
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
|
||||
machineClass = null;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} catch (ParseException ex) {
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
* some useful methods for checking address inclusion and range
|
||||
* 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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -29,8 +29,6 @@ public interface Preferences {
|
||||
|
||||
int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
|
||||
|
||||
boolean DEFAULT_HALT_ON_BREAK = true;
|
||||
|
||||
JDialog getDialog();
|
||||
|
||||
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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -36,8 +36,8 @@ import javax.swing.border.EmptyBorder;
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.*;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import static com.loomcom.symon.InstructionTable.*;
|
||||
|
||||
/**
|
||||
* Symon Simulator Interface and Control.
|
||||
@@ -71,7 +71,7 @@ public class Simulator {
|
||||
private static final int MAX_STEPS_BETWEEN_UPDATES = 20000;
|
||||
|
||||
// The simulated machine
|
||||
private Machine machine;
|
||||
private final Machine machine;
|
||||
|
||||
// Number of CPU steps between CRT repaints.
|
||||
// TODO: Dynamically refresh the value at runtime based on performance figures to reach ~ 30fps.
|
||||
@@ -114,17 +114,20 @@ public class Simulator {
|
||||
|
||||
private JButton runStopButton;
|
||||
private JButton stepButton;
|
||||
private JButton stepOverButton;
|
||||
private JComboBox<String> stepCountBox;
|
||||
|
||||
private JFileChooser fileChooser;
|
||||
private PreferencesDialog preferences;
|
||||
|
||||
private Breakpoints breakpoints;
|
||||
private final Breakpoints breakpoints;
|
||||
|
||||
private final Object commandMonitorObject = new Object();
|
||||
|
||||
private MainCommand command = MainCommand.NONE;
|
||||
|
||||
private final boolean haltOnBreak;
|
||||
|
||||
public enum MainCommand {
|
||||
NONE,
|
||||
SELECTMACHINE
|
||||
@@ -135,10 +138,17 @@ public class Simulator {
|
||||
*/
|
||||
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, false);
|
||||
}
|
||||
|
||||
public Simulator(Class<?> machineClass, InstructionTable.CpuBehavior cpuType,
|
||||
String romFile, boolean haltOnBreak) throws Exception {
|
||||
this.haltOnBreak = haltOnBreak;
|
||||
this.breakpoints = new Breakpoints(this);
|
||||
|
||||
this.machine = (Machine) machineClass.getConstructors()[0].newInstance();
|
||||
this.machine = (Machine) machineClass.getConstructors()[0].newInstance(romFile);
|
||||
this.machine.getCpu().setBehavior(cpuType);
|
||||
|
||||
// Initialize final fields in the constructor.
|
||||
this.traceLog = new TraceLog();
|
||||
@@ -155,7 +165,7 @@ public class Simulator {
|
||||
/**
|
||||
* Display the main simulator UI.
|
||||
*/
|
||||
public void createAndShowUi() throws IOException {
|
||||
public void createAndShowUi() {
|
||||
mainWindow = new JFrame();
|
||||
mainWindow.setTitle("6502 Simulator - " + machine.getName());
|
||||
mainWindow.setResizable(false);
|
||||
@@ -169,7 +179,7 @@ public class Simulator {
|
||||
|
||||
// File Chooser
|
||||
fileChooser = new JFileChooser(System.getProperty("user.dir"));
|
||||
preferences = new PreferencesDialog(mainWindow, true);
|
||||
preferences = new PreferencesDialog(mainWindow, true, haltOnBreak);
|
||||
|
||||
// Panel for Console and Buttons
|
||||
JPanel consoleContainer = new JPanel();
|
||||
@@ -181,18 +191,22 @@ public class Simulator {
|
||||
|
||||
runStopButton = new JButton("Run");
|
||||
stepButton = new JButton("Step");
|
||||
stepOverButton = new JButton("Step Over");
|
||||
stepOverButton.setEnabled(false);
|
||||
JButton softResetButton = new JButton("Soft Reset");
|
||||
JButton hardResetButton = new JButton("Hard Reset");
|
||||
|
||||
stepCountBox = new JComboBox<>(STEPS);
|
||||
stepCountBox.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
stepCountBox.addActionListener(actionEvent -> {
|
||||
JComboBox<?> cb = (JComboBox<?>) actionEvent.getSource();
|
||||
String cbStepsPerClick = (String) cb.getSelectedItem();
|
||||
|
||||
stepsPerClick = 1; // Default in case of error
|
||||
|
||||
if (cbStepsPerClick != null) {
|
||||
try {
|
||||
JComboBox cb = (JComboBox) actionEvent.getSource();
|
||||
stepsPerClick = Integer.parseInt((String) cb.getSelectedItem());
|
||||
stepsPerClick = Integer.parseInt(cbStepsPerClick);
|
||||
} catch (NumberFormatException ex) {
|
||||
stepsPerClick = 1;
|
||||
stepCountBox.setSelectedIndex(0);
|
||||
}
|
||||
}
|
||||
@@ -200,6 +214,7 @@ public class Simulator {
|
||||
|
||||
buttonContainer.add(runStopButton);
|
||||
buttonContainer.add(stepButton);
|
||||
buttonContainer.add(stepOverButton);
|
||||
buttonContainer.add(stepCountBox);
|
||||
buttonContainer.add(softResetButton);
|
||||
buttonContainer.add(hardResetButton);
|
||||
@@ -214,38 +229,25 @@ public class Simulator {
|
||||
// Bottom - buttons.
|
||||
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
|
||||
|
||||
runStopButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
if (runLoop != null && runLoop.isRunning()) {
|
||||
Simulator.this.handleStop();
|
||||
} else {
|
||||
Simulator.this.handleStart();
|
||||
}
|
||||
runStopButton.addActionListener(actionEvent -> {
|
||||
if (runLoop != null && runLoop.isRunning()) {
|
||||
Simulator.this.handleStop();
|
||||
} else {
|
||||
Simulator.this.handleStart();
|
||||
}
|
||||
});
|
||||
|
||||
stepButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
Simulator.this.handleStep(stepsPerClick);
|
||||
}
|
||||
stepButton.addActionListener(actionEvent -> Simulator.this.handleStep(stepsPerClick));
|
||||
stepOverButton.addActionListener(actionEvent -> Simulator.this.handleStepOver());
|
||||
|
||||
softResetButton.addActionListener(actionEvent -> {
|
||||
// If this was a CTRL-click, do a hard reset.
|
||||
Simulator.this.handleReset(false);
|
||||
});
|
||||
|
||||
softResetButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
// 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);
|
||||
}
|
||||
hardResetButton.addActionListener(actionEvent -> {
|
||||
// If this was a CTRL-click, do a hard reset.
|
||||
Simulator.this.handleReset(true);
|
||||
});
|
||||
|
||||
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
@@ -267,7 +269,7 @@ public class Simulator {
|
||||
try {
|
||||
commandMonitorObject.wait();
|
||||
} catch (InterruptedException ex) {
|
||||
ex.printStackTrace();
|
||||
logger.error("Interrupted during wait on command monitor.");
|
||||
}
|
||||
}
|
||||
return command;
|
||||
@@ -330,12 +332,28 @@ public class Simulator {
|
||||
step();
|
||||
}
|
||||
updateVisibleState();
|
||||
stepOverButton.setEnabled(machine.getCpu().getNextIr() == JSR);
|
||||
} catch (SymonException 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.
|
||||
*/
|
||||
@@ -348,14 +366,14 @@ public class Simulator {
|
||||
// output ready.
|
||||
if (machine.getAcia() != null && machine.getAcia().hasTxChar()) {
|
||||
// This is thread-safe
|
||||
console.print(Character.toString((char) machine.getAcia().txRead()));
|
||||
console.print(Character.toString((char) machine.getAcia().txRead(true)));
|
||||
console.repaint();
|
||||
}
|
||||
|
||||
// If a key has been pressed, fill the ACIA.
|
||||
try {
|
||||
if (machine.getAcia() != null && console.hasInput()) {
|
||||
machine.getAcia().rxWrite((int) console.readInputChar());
|
||||
machine.getAcia().rxWrite(console.readInputChar());
|
||||
}
|
||||
} catch (FifoUnderrunException ex) {
|
||||
logger.error("Console type-ahead buffer underrun!");
|
||||
@@ -404,53 +422,73 @@ public class Simulator {
|
||||
*/
|
||||
class RunLoop extends Thread {
|
||||
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() {
|
||||
return isRunning;
|
||||
return this.isRunning;
|
||||
}
|
||||
|
||||
public void requestStop() {
|
||||
isRunning = false;
|
||||
this.isRunning = false;
|
||||
}
|
||||
|
||||
public void haltOnRts(boolean value) {
|
||||
this.haltOnRts = value;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
logger.debug("Starting main run loop.");
|
||||
isRunning = true;
|
||||
if (this.haltOnRts) {
|
||||
logger.debug("Running until next RTS instruction");
|
||||
} else {
|
||||
logger.debug("Running");
|
||||
}
|
||||
this.isRunning = true;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Don't allow step while the simulator is running
|
||||
stepButton.setEnabled(false);
|
||||
stepCountBox.setEnabled(false);
|
||||
menuBar.simulatorDidStart();
|
||||
// Toggle the state of the run button
|
||||
runStopButton.setText("Stop");
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// Don't allow step while the simulator is running
|
||||
stepButton.setEnabled(false);
|
||||
stepOverButton.setEnabled(false);
|
||||
stepCountBox.setEnabled(false);
|
||||
menuBar.simulatorDidStart();
|
||||
// Toggle the state of the run button
|
||||
runStopButton.setText("Stop");
|
||||
});
|
||||
|
||||
try {
|
||||
do {
|
||||
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());
|
||||
} catch (SymonException ex) {
|
||||
logger.error("Exception in main simulator run thread. Exiting run.", ex);
|
||||
}
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
statusPane.updateState();
|
||||
memoryWindow.updateState();
|
||||
runStopButton.setText("Run");
|
||||
stepButton.setEnabled(true);
|
||||
stepCountBox.setEnabled(true);
|
||||
if (traceLog.isVisible()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
menuBar.simulatorDidStop();
|
||||
traceLog.simulatorDidStop();
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusPane.updateState();
|
||||
memoryWindow.updateState();
|
||||
runStopButton.setText("Run");
|
||||
stepButton.setEnabled(true);
|
||||
stepOverButton.setEnabled(machine.getCpu().getNextIr() == JSR);
|
||||
stepCountBox.setEnabled(true);
|
||||
if (traceLog.isVisible()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
menuBar.simulatorDidStop();
|
||||
traceLog.simulatorDidStop();
|
||||
});
|
||||
|
||||
isRunning = false;
|
||||
@@ -460,9 +498,13 @@ public class Simulator {
|
||||
* @return True if the run loop should proceed to the next step.
|
||||
*/
|
||||
private boolean shouldContinue() {
|
||||
return !breakpoints.contains(machine.getCpu().getProgramCounter()) &&
|
||||
isRunning &&
|
||||
!(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
|
||||
var instruction = machine.getCpu().getInstruction();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -503,12 +545,9 @@ public class Simulator {
|
||||
// Now load the program at the starting address.
|
||||
loadProgram(program, preferences.getProgramStartAddress());
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
console.reset();
|
||||
breakpoints.refresh();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
console.reset();
|
||||
breakpoints.refresh();
|
||||
});
|
||||
|
||||
// TODO: "Don't Show Again" checkbox
|
||||
@@ -546,7 +585,7 @@ public class Simulator {
|
||||
long fileSize = romFile.length();
|
||||
|
||||
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
|
||||
@@ -640,30 +679,27 @@ public class Simulator {
|
||||
}
|
||||
|
||||
class SetFontAction extends AbstractAction {
|
||||
private int size;
|
||||
private final int size;
|
||||
|
||||
public SetFontAction(int size) {
|
||||
super(Integer.toString(size) + " pt", null);
|
||||
super(size + " pt", null);
|
||||
this.size = size;
|
||||
putValue(SHORT_DESCRIPTION, "Set font to " + size + "pt.");
|
||||
}
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
||||
mainWindow.pack();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
console.setFont(new Font("Monospaced", Font.PLAIN, size));
|
||||
mainWindow.pack();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class SetSpeedAction extends AbstractAction {
|
||||
private int speed;
|
||||
private final int speed;
|
||||
|
||||
public SetSpeedAction(int speed) {
|
||||
super(Integer.toString(speed) + " MHz", null);
|
||||
super(speed + " MHz", null);
|
||||
this.speed = speed;
|
||||
putValue(SHORT_DESCRIPTION, "Set simulated speed to " + speed + " MHz.");
|
||||
}
|
||||
@@ -678,6 +714,21 @@ public class Simulator {
|
||||
}
|
||||
}
|
||||
|
||||
class SetCpuAction extends AbstractAction {
|
||||
private final Cpu.CpuBehavior behavior;
|
||||
|
||||
public SetCpuAction(String cpu, Cpu.CpuBehavior behavior) {
|
||||
super(cpu, null);
|
||||
this.behavior = behavior;
|
||||
putValue(SHORT_DESCRIPTION, "Set CPU to " + cpu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
machine.getCpu().setBehavior(behavior);
|
||||
}
|
||||
}
|
||||
|
||||
class ToggleTraceWindowAction extends AbstractAction {
|
||||
public ToggleTraceWindowAction() {
|
||||
super("Trace Log", null);
|
||||
@@ -704,11 +755,7 @@ public class Simulator {
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
synchronized (memoryWindow) {
|
||||
if (memoryWindow.isVisible()) {
|
||||
memoryWindow.setVisible(false);
|
||||
} else {
|
||||
memoryWindow.setVisible(true);
|
||||
}
|
||||
memoryWindow.setVisible(!memoryWindow.isVisible());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -721,11 +768,7 @@ public class Simulator {
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
synchronized (videoWindow) {
|
||||
if (videoWindow.isVisible()) {
|
||||
videoWindow.setVisible(false);
|
||||
} else {
|
||||
videoWindow.setVisible(true);
|
||||
}
|
||||
videoWindow.setVisible(!videoWindow.isVisible());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -738,11 +781,7 @@ public class Simulator {
|
||||
|
||||
public void actionPerformed(ActionEvent actionEvent) {
|
||||
synchronized (breakpointsWindow) {
|
||||
if (breakpointsWindow.isVisible()) {
|
||||
breakpointsWindow.setVisible(false);
|
||||
} else {
|
||||
breakpointsWindow.setVisible(true);
|
||||
}
|
||||
breakpointsWindow.setVisible(!breakpointsWindow.isVisible());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -868,6 +907,14 @@ public class Simulator {
|
||||
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
|
||||
simulatorMenu.add(selectMachineItem);
|
||||
|
||||
// "CPU" sub-menu
|
||||
JMenu cpuTypeMenu = new JMenu("CPU");
|
||||
ButtonGroup cpuGroup = new ButtonGroup();
|
||||
|
||||
makeCpuMenuItem("6502", Cpu.CpuBehavior.NMOS_6502, cpuTypeMenu, cpuGroup);
|
||||
makeCpuMenuItem("65C02", Cpu.CpuBehavior.CMOS_6502, cpuTypeMenu, cpuGroup);
|
||||
makeCpuMenuItem("65C816", Cpu.CpuBehavior.CMOS_65816, cpuTypeMenu, cpuGroup);
|
||||
|
||||
// "Clock Speed" sub-menu
|
||||
JMenu speedSubMenu = new JMenu("Clock Speed");
|
||||
ButtonGroup speedGroup = new ButtonGroup();
|
||||
@@ -878,6 +925,7 @@ public class Simulator {
|
||||
makeSpeedMenuItem(8, speedSubMenu, speedGroup);
|
||||
|
||||
simulatorMenu.add(speedSubMenu);
|
||||
simulatorMenu.add(cpuTypeMenu);
|
||||
|
||||
// "Breakpoints"
|
||||
final JCheckBoxMenuItem showBreakpoints = new JCheckBoxMenuItem(new ToggleBreakpointWindowAction());
|
||||
@@ -914,19 +962,26 @@ public class Simulator {
|
||||
subMenu.add(item);
|
||||
group.add(item);
|
||||
}
|
||||
|
||||
private void makeCpuMenuItem(String cpu, Cpu.CpuBehavior behavior, JMenu subMenu, ButtonGroup group) {
|
||||
|
||||
Action action = new SetCpuAction(cpu, behavior);
|
||||
|
||||
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
|
||||
item.setSelected(machine.getCpu().getBehavior() == behavior);
|
||||
subMenu.add(item);
|
||||
group.add(item);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateVisibleState() {
|
||||
// Immediately update the UI.
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// Now update the state
|
||||
statusPane.updateState();
|
||||
memoryWindow.updateState();
|
||||
if (traceLog.shouldUpdate()) {
|
||||
traceLog.refresh();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
statusPane.updateState();
|
||||
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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -32,8 +32,8 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
public abstract class Acia extends Device {
|
||||
|
||||
private String name;
|
||||
|
||||
private final String name;
|
||||
|
||||
/**
|
||||
* Register addresses
|
||||
*/
|
||||
@@ -42,13 +42,14 @@ public abstract class Acia extends Device {
|
||||
boolean receiveIrqEnabled = false;
|
||||
boolean transmitIrqEnabled = false;
|
||||
boolean overrun = false;
|
||||
|
||||
long lastTxWrite = 0;
|
||||
boolean interrupt = false;
|
||||
|
||||
long lastTxWrite = 0;
|
||||
long lastRxRead = 0;
|
||||
int baudRate = 0;
|
||||
long baudRateDelay = 0;
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* Read/Write buffers
|
||||
*/
|
||||
int rxChar = 0;
|
||||
@@ -56,8 +57,8 @@ public abstract class Acia extends Device {
|
||||
|
||||
boolean rxFull = false;
|
||||
boolean txEmpty = true;
|
||||
|
||||
|
||||
|
||||
|
||||
public Acia(int address, int size, String name) throws MemoryRangeException {
|
||||
super(address, address + size - 1, name);
|
||||
this.name = name;
|
||||
@@ -100,41 +101,46 @@ public abstract class Acia extends Device {
|
||||
/**
|
||||
* @return The contents of the status register.
|
||||
*/
|
||||
public abstract int statusReg();
|
||||
public abstract int statusReg(boolean cpuAccess);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return name + "@" + String.format("%04X", baseAddress);
|
||||
}
|
||||
|
||||
public synchronized int rxRead() {
|
||||
lastRxRead = System.nanoTime();
|
||||
overrun = false;
|
||||
rxFull = false;
|
||||
public synchronized int rxRead(boolean cpuAccess) {
|
||||
if (cpuAccess) {
|
||||
lastRxRead = System.nanoTime();
|
||||
overrun = false;
|
||||
rxFull = false;
|
||||
}
|
||||
return rxChar;
|
||||
}
|
||||
|
||||
public synchronized void rxWrite(int data) {
|
||||
if(rxFull) {
|
||||
if (rxFull) {
|
||||
overrun = true;
|
||||
}
|
||||
|
||||
|
||||
rxFull = true;
|
||||
|
||||
if (receiveIrqEnabled) {
|
||||
interrupt = true;
|
||||
getBus().assertIrq();
|
||||
}
|
||||
|
||||
rxChar = data;
|
||||
}
|
||||
|
||||
public synchronized int txRead() {
|
||||
txEmpty = true;
|
||||
public synchronized int txRead(boolean cpuAccess) {
|
||||
if (cpuAccess) {
|
||||
txEmpty = true;
|
||||
|
||||
if (transmitIrqEnabled) {
|
||||
getBus().assertIrq();
|
||||
if (transmitIrqEnabled) {
|
||||
interrupt = true;
|
||||
getBus().assertIrq();
|
||||
}
|
||||
}
|
||||
|
||||
return txChar;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
* 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
|
||||
* functionality. Interrupts are not supported.
|
||||
* functionality.
|
||||
* <p/>
|
||||
* Unlike a 16550 UART, the 6551 ACIA has only one-byte transmit and
|
||||
* receive buffers. It is the programmer's responsibility to check the
|
||||
@@ -53,15 +53,23 @@ public class Acia6551 extends Acia {
|
||||
|
||||
public Acia6551(int address) throws MemoryRangeException {
|
||||
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
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
switch (address) {
|
||||
case DATA_REG:
|
||||
return rxRead();
|
||||
return rxRead(cpuAccess);
|
||||
case STAT_REG:
|
||||
return statusReg();
|
||||
return statusReg(cpuAccess);
|
||||
case CMND_REG:
|
||||
return commandRegister;
|
||||
case CTRL_REG:
|
||||
@@ -74,16 +82,16 @@ public class Acia6551 extends Acia {
|
||||
@Override
|
||||
public void write(int address, int data) throws MemoryAccessException {
|
||||
switch (address) {
|
||||
case 0:
|
||||
case DATA_REG:
|
||||
txWrite(data);
|
||||
break;
|
||||
case 1:
|
||||
case STAT_REG:
|
||||
reset();
|
||||
break;
|
||||
case 2:
|
||||
case CMND_REG:
|
||||
setCommandRegister(data);
|
||||
break;
|
||||
case 3:
|
||||
case CTRL_REG:
|
||||
setControlRegister(data);
|
||||
break;
|
||||
default:
|
||||
@@ -110,67 +118,59 @@ public class Acia6551 extends Acia {
|
||||
controlRegister = data;
|
||||
int rate = 0;
|
||||
|
||||
// If the value of the data is 0, this is a request to reset,
|
||||
// otherwise it's a control update.
|
||||
|
||||
if (data == 0) {
|
||||
reset();
|
||||
} else {
|
||||
// Mask the lower three bits to get the baud rate.
|
||||
int baudSelector = data & 0x0f;
|
||||
switch (baudSelector) {
|
||||
case 0:
|
||||
rate = 0;
|
||||
break;
|
||||
case 1:
|
||||
rate = 50;
|
||||
break;
|
||||
case 2:
|
||||
rate = 75;
|
||||
break;
|
||||
case 3:
|
||||
rate = 110; // Real rate is actually 109.92
|
||||
break;
|
||||
case 4:
|
||||
rate = 135; // Real rate is actually 134.58
|
||||
break;
|
||||
case 5:
|
||||
rate = 150;
|
||||
break;
|
||||
case 6:
|
||||
rate = 300;
|
||||
break;
|
||||
case 7:
|
||||
rate = 600;
|
||||
break;
|
||||
case 8:
|
||||
rate = 1200;
|
||||
break;
|
||||
case 9:
|
||||
rate = 1800;
|
||||
break;
|
||||
case 10:
|
||||
rate = 2400;
|
||||
break;
|
||||
case 11:
|
||||
rate = 3600;
|
||||
break;
|
||||
case 12:
|
||||
rate = 4800;
|
||||
break;
|
||||
case 13:
|
||||
rate = 7200;
|
||||
break;
|
||||
case 14:
|
||||
rate = 9600;
|
||||
break;
|
||||
case 15:
|
||||
rate = 19200;
|
||||
break;
|
||||
}
|
||||
|
||||
setBaudRate(rate);
|
||||
// Mask the lower four bits to get the baud rate.
|
||||
int baudSelector = data & 0x0f;
|
||||
switch (baudSelector) {
|
||||
case 1:
|
||||
rate = 50;
|
||||
break;
|
||||
case 2:
|
||||
rate = 75;
|
||||
break;
|
||||
case 3:
|
||||
rate = 110; // Real rate is actually 109.92
|
||||
break;
|
||||
case 4:
|
||||
rate = 135; // Real rate is actually 134.58
|
||||
break;
|
||||
case 5:
|
||||
rate = 150;
|
||||
break;
|
||||
case 6:
|
||||
rate = 300;
|
||||
break;
|
||||
case 7:
|
||||
rate = 600;
|
||||
break;
|
||||
case 8:
|
||||
rate = 1200;
|
||||
break;
|
||||
case 9:
|
||||
rate = 1800;
|
||||
break;
|
||||
case 10:
|
||||
rate = 2400;
|
||||
break;
|
||||
case 11:
|
||||
rate = 3600;
|
||||
break;
|
||||
case 12:
|
||||
rate = 4800;
|
||||
break;
|
||||
case 13:
|
||||
rate = 7200;
|
||||
break;
|
||||
case 14:
|
||||
rate = 9600;
|
||||
break;
|
||||
case 15:
|
||||
rate = 19200;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
setBaudRate(rate);
|
||||
}
|
||||
|
||||
|
||||
@@ -178,8 +178,8 @@ public class Acia6551 extends Acia {
|
||||
* @return The contents of the status register.
|
||||
*/
|
||||
@Override
|
||||
public int statusReg() {
|
||||
// TODO: Parity Error, Framing Error, DTR, DSR, and Interrupt flags.
|
||||
public int statusReg(boolean cpuAccess) {
|
||||
// TODO: Parity Error, Framing Error, DTR, and DSR flags.
|
||||
int stat = 0;
|
||||
if (rxFull && System.nanoTime() >= (lastRxRead + baudRateDelay)) {
|
||||
stat |= 0x08;
|
||||
@@ -190,17 +190,29 @@ public class Acia6551 extends Acia {
|
||||
if (overrun) {
|
||||
stat |= 0x04;
|
||||
}
|
||||
if (interrupt) {
|
||||
stat |= 0x80;
|
||||
}
|
||||
|
||||
if (cpuAccess) {
|
||||
interrupt = false;
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
||||
private synchronized void reset() {
|
||||
txChar = 0;
|
||||
txEmpty = true;
|
||||
rxChar = 0;
|
||||
rxFull = false;
|
||||
receiveIrqEnabled = false;
|
||||
transmitIrqEnabled = false;
|
||||
}
|
||||
// Figure 6 in the 6551 ACIA data sheet says the "program reset"
|
||||
// event does not modify the control register.
|
||||
|
||||
// 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>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -30,8 +30,8 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
/**
|
||||
* This is a simulation of the Motorola 6850 ACIA, with limited
|
||||
* functionality. Interrupts are not supported.
|
||||
* <p/>
|
||||
* functionality.
|
||||
* <p>
|
||||
* Unlike a 16550 UART, the 6850 ACIA has only one-byte transmit and
|
||||
* receive buffers. It is the programmer's responsibility to check the
|
||||
* status (full or empty) for transmit and receive buffers before
|
||||
@@ -43,7 +43,7 @@ public class Acia6850 extends Acia {
|
||||
|
||||
static final int STAT_REG = 0; // read-only
|
||||
static final int CTRL_REG = 0; // write-only
|
||||
|
||||
|
||||
static final int RX_REG = 1; // read-only
|
||||
static final int TX_REG = 1; // write-only
|
||||
|
||||
@@ -53,12 +53,12 @@ public class Acia6850 extends Acia {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
switch (address) {
|
||||
case RX_REG:
|
||||
return rxRead();
|
||||
return rxRead(cpuAccess);
|
||||
case STAT_REG:
|
||||
return statusReg();
|
||||
return statusReg(cpuAccess);
|
||||
|
||||
default:
|
||||
throw new MemoryAccessException("No register.");
|
||||
@@ -78,7 +78,7 @@ public class Acia6850 extends Acia {
|
||||
throw new MemoryAccessException("No register.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void setCommandRegister(int data) {
|
||||
// Bits 0 & 1 control the master reset
|
||||
if((data & 0x01) != 0 && (data & 0x02) != 0) {
|
||||
@@ -97,8 +97,8 @@ public class Acia6850 extends Acia {
|
||||
* @return The contents of the status register.
|
||||
*/
|
||||
@Override
|
||||
public int statusReg() {
|
||||
// TODO: Parity Error, Framing Error, DTR, DSR, and Interrupt flags.
|
||||
public int statusReg(boolean cpuAccess) {
|
||||
// TODO: Parity Error, Framing Error, DTR, and DSR flags.
|
||||
int stat = 0;
|
||||
if (rxFull && System.nanoTime() >= (lastRxRead + baudRateDelay)) {
|
||||
stat |= 0x01;
|
||||
@@ -109,7 +109,14 @@ public class Acia6850 extends Acia {
|
||||
if (overrun) {
|
||||
stat |= 0x20;
|
||||
}
|
||||
|
||||
if (interrupt) {
|
||||
stat |= 0x80;
|
||||
}
|
||||
|
||||
if (cpuAccess) {
|
||||
interrupt = false;
|
||||
}
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
@@ -118,6 +125,7 @@ public class Acia6850 extends Acia {
|
||||
overrun = false;
|
||||
rxFull = false;
|
||||
txEmpty = true;
|
||||
interrupt = false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
* 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.MemoryRangeException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Simulation of a 6545 CRTC and virtual CRT output.
|
||||
*/
|
||||
@@ -87,9 +85,9 @@ public class Crtc extends Device {
|
||||
private boolean displayEnableSkew = 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");
|
||||
this.memory = memory;
|
||||
|
||||
@@ -119,20 +117,18 @@ public class Crtc extends Device {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
switch (address) {
|
||||
case REGISTER_RW:
|
||||
switch (currentRegister) {
|
||||
case CURSOR_POSITION_LOW:
|
||||
return cursorPosition & 0xff;
|
||||
case CURSOR_POSITION_HIGH:
|
||||
return cursorPosition >> 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
return 0;
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
if (address == REGISTER_RW) {
|
||||
switch (currentRegister) {
|
||||
case CURSOR_POSITION_LOW:
|
||||
return cursorPosition & 0xff;
|
||||
case CURSOR_POSITION_HIGH:
|
||||
return cursorPosition >> 8;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,7 +138,7 @@ public class Crtc extends Device {
|
||||
|
||||
public int getCharAtAddress(int address) throws MemoryAccessException {
|
||||
// TODO: Row/Column addressing
|
||||
return memory.read(address);
|
||||
return memory.read(address, false);
|
||||
}
|
||||
|
||||
public int getHorizontalDisplayed() {
|
||||
|
||||
@@ -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
|
||||
* 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.
|
||||
*/
|
||||
private MemoryRange memoryRange;
|
||||
private final MemoryRange memoryRange;
|
||||
|
||||
/**
|
||||
* The name of the device.
|
||||
@@ -60,7 +60,7 @@ public abstract class Device implements Comparable<Device> {
|
||||
/**
|
||||
* Listeners to notify on update.
|
||||
*/
|
||||
private Set<DeviceChangeListener> deviceChangeListeners;
|
||||
private final Set<DeviceChangeListener> deviceChangeListeners;
|
||||
|
||||
public Device(int startAddress, int endAddress, String name) throws MemoryRangeException {
|
||||
this.memoryRange = new MemoryRange(startAddress, endAddress);
|
||||
@@ -72,7 +72,7 @@ public abstract class Device implements Comparable<Device> {
|
||||
/* Methods required to be implemented by inheriting classes. */
|
||||
public abstract void write(int address, int data) throws MemoryAccessException;
|
||||
|
||||
public abstract int read(int address) throws MemoryAccessException;
|
||||
public abstract int read(int address, boolean cpuAccess) throws MemoryAccessException;
|
||||
|
||||
public abstract String toString();
|
||||
|
||||
|
||||
@@ -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
|
||||
* 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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -30,8 +30,8 @@ import com.loomcom.symon.exceptions.*;
|
||||
|
||||
public class Memory extends Device {
|
||||
|
||||
private boolean readOnly;
|
||||
private int[] mem;
|
||||
private final boolean readOnly;
|
||||
private final int[] mem;
|
||||
|
||||
/* Initialize all locations to 0x00 (BRK) */
|
||||
private static final int DEFAULT_FILL = 0x00;
|
||||
@@ -94,7 +94,7 @@ public class Memory extends Device {
|
||||
|
||||
}
|
||||
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
return this.mem[address];
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -94,7 +94,7 @@ public class SdController extends Device {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
switch (address) {
|
||||
case 0:
|
||||
return readData();
|
||||
@@ -119,7 +119,10 @@ public class SdController extends Device {
|
||||
if (sdImageFile != null) {
|
||||
try {
|
||||
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);
|
||||
if (read < SECTOR_SIZE) {
|
||||
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
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Very basic implementation of a MOS 6522 VIA.
|
||||
*
|
||||
* TODO: Implement timers as threads.
|
||||
* Skeleton for future implementation of a MOS 6522 VIA.
|
||||
*/
|
||||
public class Via6522 extends Pia {
|
||||
public static final int VIA_SIZE = 16;
|
||||
|
||||
enum Register {
|
||||
ORB, ORA, DDRB, DDRA, T1C_L, T1C_H, T1L_L, T1L_H,
|
||||
T2C_L, T2C_H, SR, ACR, PCR, IFR, IER, ORA_H
|
||||
}
|
||||
|
||||
public Via6522(int address) throws MemoryRangeException {
|
||||
super(address, address + VIA_SIZE - 1, "MOS 6522 VIA");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(int address, int data) throws MemoryAccessException {
|
||||
Register[] registers = Register.values();
|
||||
|
||||
if (address >= registers.length) {
|
||||
throw new MemoryAccessException("Unknown register: " + address);
|
||||
}
|
||||
|
||||
Register r = registers[address];
|
||||
|
||||
switch (r) {
|
||||
case ORA:
|
||||
case ORB:
|
||||
case DDRA:
|
||||
case DDRB:
|
||||
case T1C_L:
|
||||
case T1C_H:
|
||||
case T1L_L:
|
||||
case T1L_H:
|
||||
case T2C_L:
|
||||
case T2C_H:
|
||||
case SR:
|
||||
case ACR:
|
||||
case PCR:
|
||||
case IFR:
|
||||
case IER:
|
||||
case ORA_H:
|
||||
default:
|
||||
}
|
||||
// TODO: Implement write.
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(int address) throws MemoryAccessException {
|
||||
Register[] registers = Register.values();
|
||||
|
||||
if (address >= registers.length) {
|
||||
throw new MemoryAccessException("Unknown register: " + address);
|
||||
}
|
||||
|
||||
Register r = registers[address];
|
||||
|
||||
switch (r) {
|
||||
case ORA:
|
||||
case ORB:
|
||||
case DDRA:
|
||||
case DDRB:
|
||||
case T1C_L:
|
||||
case T1C_H:
|
||||
case T1L_L:
|
||||
case T1L_H:
|
||||
case T2C_L:
|
||||
case T2C_H:
|
||||
case SR:
|
||||
case ACR:
|
||||
case PCR:
|
||||
case IFR:
|
||||
case IER:
|
||||
case ORA_H:
|
||||
default:
|
||||
}
|
||||
|
||||
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
|
||||
// TODO: Implement read.
|
||||
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
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal;
|
||||
|
||||
/**
|
||||
* A {@link TerminalModel} which implements some common behaviour.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public abstract class AbstractTerminalModel implements TerminalModel {
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
int rows = getRows(), columns = getColumns();
|
||||
for (int column = 0; column < columns; column++) {
|
||||
for (int row = 0; row < rows; row++) {
|
||||
setCell(column, row, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveCursorBack(int n) {
|
||||
if (n < 0) {
|
||||
throw new IllegalArgumentException("n must be positive");
|
||||
}
|
||||
int cursorColumn = getCursorColumn() - n;
|
||||
if (cursorColumn < 0) {
|
||||
cursorColumn = 0;
|
||||
}
|
||||
setCursorColumn(cursorColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveCursorForward(int n) {
|
||||
if (n < 0) {
|
||||
throw new IllegalArgumentException("n must be positive");
|
||||
}
|
||||
int columns = getColumns();
|
||||
int cursorColumn = getCursorColumn() + n;
|
||||
if (cursorColumn >= columns) {
|
||||
cursorColumn = columns - 1;
|
||||
}
|
||||
setCursorColumn(cursorColumn);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveCursorDown(int n) {
|
||||
if (n < 0) {
|
||||
throw new IllegalArgumentException("n must be positive");
|
||||
}
|
||||
int bufferSize = getBufferSize();
|
||||
int cursorRow = getCursorRow() + n;
|
||||
if (cursorRow >= bufferSize) {
|
||||
cursorRow = bufferSize - 1;
|
||||
}
|
||||
setCursorRow(cursorRow);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveCursorUp(int n) {
|
||||
if (n < 0) {
|
||||
throw new IllegalArgumentException("n must be positive");
|
||||
}
|
||||
int cursorRow = getCursorRow() - n;
|
||||
if (cursorRow < 0) {
|
||||
cursorRow = 0;
|
||||
}
|
||||
setCursorRow(cursorRow);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Portions Copyright (c) 2009-2011 Graham Edgecombe
|
||||
*
|
||||
* 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.jterminal;
|
||||
|
||||
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JScrollBar;
|
||||
|
||||
/**
|
||||
* Swing terminal emulation component
|
||||
* @author Seth J. Morabito
|
||||
*
|
||||
*/
|
||||
public class JTerminal extends JComponent {
|
||||
|
||||
private static final long serialVersionUID = 2871625194146986567L;
|
||||
|
||||
private int borderWidth = 0;
|
||||
|
||||
private JScrollBar scrollBar;
|
||||
|
||||
/**
|
||||
* The terminal emulation model
|
||||
*/
|
||||
private TerminalModel model;
|
||||
|
||||
/**
|
||||
* The font this terminal uses
|
||||
*/
|
||||
private Font font;
|
||||
|
||||
/**
|
||||
* The cell width in pixels
|
||||
*/
|
||||
private int cellWidth;
|
||||
|
||||
/**
|
||||
* The cell height in pixels
|
||||
*/
|
||||
private int cellHeight;
|
||||
|
||||
/**
|
||||
* Max descender for font
|
||||
*/
|
||||
private int maxDescender;
|
||||
|
||||
public JTerminal(Font font) {
|
||||
this(new Vt100TerminalModel(), font);
|
||||
}
|
||||
|
||||
public JTerminal(TerminalModel model, Font font) {
|
||||
setModel(model);
|
||||
setFont(font);
|
||||
init();
|
||||
}
|
||||
|
||||
public void setBorderWidth(int borderWidth) {
|
||||
this.borderWidth = borderWidth;
|
||||
revalidate();
|
||||
}
|
||||
|
||||
public void setFont(Font font) {
|
||||
this.font = font;
|
||||
setCellWidthAndHeight(font);
|
||||
revalidate();
|
||||
}
|
||||
|
||||
public Font getFont() {
|
||||
return font;
|
||||
}
|
||||
|
||||
public void setModel(TerminalModel model) {
|
||||
if (model == null) {
|
||||
throw new NullPointerException("model");
|
||||
}
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public TerminalModel getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void print(String str) {
|
||||
model.print(str);
|
||||
}
|
||||
|
||||
public Dimension getMinimumSize() {
|
||||
return new Dimension(model.getColumns() * cellWidth + borderWidth * 2,
|
||||
model.getRows() * cellHeight + borderWidth * 2);
|
||||
}
|
||||
|
||||
public Dimension getMaximumSize() {
|
||||
return getMinimumSize();
|
||||
}
|
||||
|
||||
public Dimension getPreferredSize() {
|
||||
return getMinimumSize();
|
||||
}
|
||||
|
||||
public void paint(Graphics g) {
|
||||
g.setFont(font);
|
||||
|
||||
int width = model.getColumns();
|
||||
int height = model.getBufferSize();
|
||||
|
||||
g.setColor(model.getDefaultBackgroundColor());
|
||||
g.fillRect(0, 0, width * cellWidth + borderWidth * 2, height * cellHeight + borderWidth * 2);
|
||||
|
||||
int start = scrollBar == null ? 0 : scrollBar.getValue();
|
||||
for (int y = start; y < height; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
TerminalCell cell = model.getCell(x, y);
|
||||
boolean cursorHere = (model.getCursorRow() == y) && (model.getCursorColumn() == x);
|
||||
|
||||
if ((cursorHere) && (cell == null)) {
|
||||
cell = new TerminalCell(' ', model.getDefaultBackgroundColor(), model.getDefaultForegroundColor());
|
||||
}
|
||||
|
||||
if (cell != null) {
|
||||
int px = x * cellWidth + borderWidth;
|
||||
int py = (y - start) * cellHeight + borderWidth;
|
||||
|
||||
g.setColor(cursorHere ? cell.getForegroundColor() : cell.getBackgroundColor());
|
||||
g.fillRect(px, py, cellWidth, cellHeight);
|
||||
|
||||
g.setColor(cursorHere ? cell.getBackgroundColor() : cell.getForegroundColor());
|
||||
g.drawChars(new char[] { cell.getCharacter() }, 0, 1, px, py + cellHeight - maxDescender);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void init() {
|
||||
setLayout(new BorderLayout(0, 0));
|
||||
|
||||
int rows = model.getRows();
|
||||
int bufferSize = model.getBufferSize();
|
||||
|
||||
if (bufferSize > rows) {
|
||||
scrollBar = new JScrollBar(Adjustable.VERTICAL, 0, rows, 0, bufferSize + 1);
|
||||
scrollBar.addAdjustmentListener(evt -> repaint());
|
||||
add("After", scrollBar);
|
||||
}
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void setCellWidthAndHeight(Font font) {
|
||||
FontMetrics metrics = getFontMetrics(font);
|
||||
cellWidth = metrics.charWidth('W');
|
||||
cellHeight = metrics.getHeight();
|
||||
maxDescender = metrics.getMaxDescent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* Represents a single terminal cell which contains a character, background
|
||||
* color and foreground color.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class TerminalCell {
|
||||
|
||||
/**
|
||||
* The character.
|
||||
*/
|
||||
private final char character;
|
||||
|
||||
/**
|
||||
* The background color.
|
||||
*/
|
||||
private final Color backgroundColor;
|
||||
|
||||
/**
|
||||
* The foreground color.
|
||||
*/
|
||||
private final Color foregroundColor;
|
||||
|
||||
/**
|
||||
* Creates a terminal cell with the specified character, background color
|
||||
* and foreground color.
|
||||
* @param character The character.
|
||||
* @param backgroundColor The background color.
|
||||
* @param foregroundColor The foreground color.
|
||||
* @throws NullPointerException if the background or foreground color(s)
|
||||
* are {@code null}.
|
||||
*/
|
||||
public TerminalCell(char character, Color backgroundColor, Color foregroundColor) {
|
||||
if (backgroundColor == null) {
|
||||
throw new NullPointerException("backgroundColor");
|
||||
}
|
||||
if (foregroundColor == null) {
|
||||
throw new NullPointerException("foregroundColor");
|
||||
}
|
||||
|
||||
this.character = character;
|
||||
this.backgroundColor = backgroundColor;
|
||||
this.foregroundColor = foregroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the character.
|
||||
* @return The character.
|
||||
*/
|
||||
public char getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the background color.
|
||||
* @return The background color.
|
||||
*/
|
||||
public Color getBackgroundColor() {
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the foreground color.
|
||||
* @return The foreground color.
|
||||
*/
|
||||
public Color getForegroundColor() {
|
||||
return foregroundColor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.loomcom.symon.jterminal.bell.BellStrategy;
|
||||
|
||||
/**
|
||||
* Model for terminals - defines methods for getting/setting cells, printing
|
||||
* text to a terminal and getting the size of the terminal and buffer.
|
||||
*/
|
||||
public interface TerminalModel {
|
||||
|
||||
/**
|
||||
* Sets the bell strategy.
|
||||
* @param strategy The bell strategy.
|
||||
* @throws NullPointerException if the strategy is {@code null}.
|
||||
*/
|
||||
void setBellStrategy(BellStrategy strategy);
|
||||
|
||||
/**
|
||||
* Clears the terminal.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Moves the cursor back n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
void moveCursorBack(int n);
|
||||
|
||||
/**
|
||||
* Moves the cursor forward n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
void moveCursorForward(int n);
|
||||
|
||||
/**
|
||||
* Moves the cursor down n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
void moveCursorDown(int n);
|
||||
|
||||
/**
|
||||
* Moves the cursor up n characters.
|
||||
* @param n The number of characters.
|
||||
* @throws IllegalArgumentException if n is not positive.
|
||||
*/
|
||||
void moveCursorUp(int n);
|
||||
|
||||
/**
|
||||
* Sets a cell.
|
||||
* @param column The column.
|
||||
* @param row The row.
|
||||
* @param cell The cell.
|
||||
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
||||
* out of bounds.
|
||||
*/
|
||||
void setCell(int column, int row, TerminalCell cell);
|
||||
|
||||
/**
|
||||
* Gets a cell.
|
||||
* @param column The column.
|
||||
* @param row The row.
|
||||
* @return The cell.
|
||||
* @throws IndexOutOfBoundsException if the column and/or row number(s) are
|
||||
* out of bounds.
|
||||
*/
|
||||
TerminalCell getCell(int column, int row);
|
||||
|
||||
/**
|
||||
* Prints the specified string to the terminal at the cursor position,
|
||||
* interpreting any escape sequences/special ASCII codes the model may
|
||||
* support. Lines will be wrapped if necessary.
|
||||
* @param str The string to print.
|
||||
* @throws NullPointerException if the string is {@code null}.
|
||||
*/
|
||||
void print(String str);
|
||||
|
||||
/**
|
||||
* Gets the number of columns.
|
||||
* @return The number of columns.
|
||||
*/
|
||||
int getColumns();
|
||||
|
||||
/**
|
||||
* Gets the number of rows.
|
||||
* @return The number of rows.
|
||||
*/
|
||||
int getRows();
|
||||
|
||||
/**
|
||||
* Gets the buffer size.
|
||||
* @return The buffer size.
|
||||
*/
|
||||
int getBufferSize();
|
||||
|
||||
/**
|
||||
* Gets the cursor row.
|
||||
* @return The cursor row.
|
||||
*/
|
||||
int getCursorRow();
|
||||
|
||||
/**
|
||||
* Sets the cursor row.
|
||||
* @param row The cursor row.
|
||||
* @throws IllegalArgumentException if the row is out of the valid range.
|
||||
*/
|
||||
void setCursorRow(int row);
|
||||
|
||||
/**
|
||||
* Gets the cursor column.
|
||||
* @return The cursor column.
|
||||
*/
|
||||
int getCursorColumn();
|
||||
|
||||
/**
|
||||
* Sets the cursor column.
|
||||
* @param column The cursor column.
|
||||
* @throws IllegalArgumentException if the column is out of the valid range.
|
||||
*/
|
||||
void setCursorColumn(int column);
|
||||
|
||||
/**
|
||||
* Gets the default background color.
|
||||
* @return The default background color.
|
||||
*/
|
||||
Color getDefaultBackgroundColor();
|
||||
|
||||
/**
|
||||
* Gets the default foreground color.
|
||||
* @return The default foreground color.
|
||||
*/
|
||||
Color getDefaultForegroundColor();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.bell;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
|
||||
/**
|
||||
* A {@link BellStrategy} which calls {@link Toolkit#beep()}.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class BeepBellStrategy implements BellStrategy {
|
||||
|
||||
@Override
|
||||
public void soundBell() {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.bell;
|
||||
|
||||
/**
|
||||
* A 'strategy' used to sound the bell (US-ASCII character {@code 7}).
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public interface BellStrategy {
|
||||
|
||||
/**
|
||||
* Sounds the bell.
|
||||
*/
|
||||
void soundBell();
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.bell;
|
||||
|
||||
/**
|
||||
* A {@link BellStrategy} which does nothing.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class NopBellStrategy implements BellStrategy {
|
||||
|
||||
@Override
|
||||
public void soundBell() {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.vt100;
|
||||
|
||||
/**
|
||||
* Represents an ANSI control sequence.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class AnsiControlSequence {
|
||||
|
||||
/**
|
||||
* The command character.
|
||||
*/
|
||||
private final char command;
|
||||
|
||||
/**
|
||||
* The parameters.
|
||||
*/
|
||||
private final String[] parameters;
|
||||
|
||||
/**
|
||||
* Creates an ANSI control sequence with the specified command and
|
||||
* parameters.
|
||||
* @param command The command character.
|
||||
* @param parameters The parameters array.
|
||||
* @throws NullPointerException if the parameters array is {@code null}.
|
||||
*/
|
||||
public AnsiControlSequence(char command, String[] parameters) {
|
||||
if (parameters == null) {
|
||||
throw new NullPointerException("parameters");
|
||||
}
|
||||
this.command = command;
|
||||
if (parameters.length == 1 && parameters[0].isEmpty()) {
|
||||
this.parameters = new String[0];
|
||||
} else {
|
||||
this.parameters = parameters.clone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command character.
|
||||
* @return The command character.
|
||||
*/
|
||||
public char getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameters array.
|
||||
* @return The parameters array.
|
||||
*/
|
||||
public String[] getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.vt100;
|
||||
|
||||
/**
|
||||
* An interface which classes may use to listen to events from a
|
||||
* {@link AnsiControlSequenceParser}.
|
||||
*/
|
||||
public interface AnsiControlSequenceListener {
|
||||
|
||||
/**
|
||||
* Called when a control sequence has been parsed.
|
||||
* @param seq The control sequence.
|
||||
*/
|
||||
void parsedControlSequence(AnsiControlSequence seq);
|
||||
|
||||
/**
|
||||
* Called when a string has been parsed.
|
||||
* @param str The string.
|
||||
*/
|
||||
void parsedString(String str);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.vt100;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* A class which parses {@link AnsiControlSequence}s from {@link String}(s).
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class AnsiControlSequenceParser {
|
||||
|
||||
/**
|
||||
* The multibyte control sequence introducer.
|
||||
*/
|
||||
private static final char[] MULTI_CSI = new char[] { 27, '[' };
|
||||
|
||||
/**
|
||||
* The single-byte control sequence introducer.
|
||||
*/
|
||||
private static final char SINGLE_CSI = 155;
|
||||
|
||||
/**
|
||||
* The buffer of data from the last call to "parse()". This is
|
||||
* populated with data if an escape sequence is not complete.
|
||||
*/
|
||||
private StringBuilder buffer = new StringBuilder();
|
||||
|
||||
/**
|
||||
* The ANSI control sequence listener.
|
||||
*/
|
||||
private final AnsiControlSequenceListener listener;
|
||||
|
||||
/**
|
||||
* Creates the ANSI control sequence parser.
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public AnsiControlSequenceParser(AnsiControlSequenceListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the specified string.
|
||||
* @param str The string to parse.
|
||||
*/
|
||||
public void parse(String str) {
|
||||
if (buffer.length() > 0) {
|
||||
str = buffer.toString().concat(str);
|
||||
buffer = new StringBuilder();
|
||||
}
|
||||
try {
|
||||
try (Reader reader = new StringReader(str)) {
|
||||
parse(reader);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses characters from the specified character reader.
|
||||
* @param reader The character reader.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void parse(Reader reader) throws IOException {
|
||||
StringBuilder text = new StringBuilder();
|
||||
int character;
|
||||
while ((character = reader.read()) != -1) {
|
||||
boolean introducedControlSequence = false;
|
||||
if (character == SINGLE_CSI) {
|
||||
introducedControlSequence = true;
|
||||
} else if (character == MULTI_CSI[0]) {
|
||||
int nextCharacter = reader.read();
|
||||
if (nextCharacter == -1) {
|
||||
buffer.append((char) character);
|
||||
break;
|
||||
} else if (nextCharacter == MULTI_CSI[1]) {
|
||||
introducedControlSequence = true;
|
||||
} else {
|
||||
text.append((char) character);
|
||||
text.append((char) nextCharacter);
|
||||
}
|
||||
} else {
|
||||
text.append((char) character);
|
||||
}
|
||||
|
||||
if (introducedControlSequence) {
|
||||
if (text.length() > 0) {
|
||||
listener.parsedString(text.toString());
|
||||
text = new StringBuilder();
|
||||
}
|
||||
parseControlSequence(reader);
|
||||
}
|
||||
}
|
||||
|
||||
if (text.length() > 0) {
|
||||
listener.parsedString(text.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a control sequence.
|
||||
* @param reader The character reader.
|
||||
* @throws IOException if an I/O error occurs.
|
||||
*/
|
||||
private void parseControlSequence(Reader reader) throws IOException {
|
||||
boolean finishedSequence = false;
|
||||
StringBuilder parameters = new StringBuilder();
|
||||
int character;
|
||||
while ((character = reader.read()) != -1) {
|
||||
if ((character >= 'a' && character <= 'z') || (character >= 'A' && character <= 'Z')) {
|
||||
String[] array = parameters.toString().split(";");
|
||||
AnsiControlSequence seq = new AnsiControlSequence((char) character, array);
|
||||
listener.parsedControlSequence(seq);
|
||||
|
||||
finishedSequence = true;
|
||||
break;
|
||||
} else {
|
||||
parameters.append((char) character);
|
||||
}
|
||||
}
|
||||
if (!finishedSequence) {
|
||||
// not an ideal solution if they used the two byte CSI, but it's
|
||||
// easier and cleaner than keeping track of it
|
||||
buffer.append(SINGLE_CSI);
|
||||
buffer.append(parameters);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.vt100;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
/**
|
||||
* Contains colors used by the SGR ANSI escape sequence.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
final class SgrColor {
|
||||
|
||||
/**
|
||||
* An array of normal intensity colors.
|
||||
*/
|
||||
public static final Color[] COLOR_NORMAL = new Color[] {
|
||||
new Color(0, 0, 0),
|
||||
new Color(128, 0, 0),
|
||||
new Color(0, 128, 0),
|
||||
new Color(128, 128, 0),
|
||||
new Color(0, 0, 128),
|
||||
new Color(128, 0, 128),
|
||||
new Color(0, 128, 128),
|
||||
new Color(192, 192, 192)
|
||||
};
|
||||
|
||||
/**
|
||||
* An array of bright intensity colors.
|
||||
*/
|
||||
public static final Color[] COLOR_BRIGHT = new Color[] {
|
||||
new Color(128, 128, 128),
|
||||
new Color(255, 0, 0),
|
||||
new Color(0, 255, 0),
|
||||
new Color(255, 255, 0),
|
||||
new Color(0, 0, 255),
|
||||
new Color(0, 0, 255),
|
||||
new Color(255, 0, 255),
|
||||
new Color(0, 255, 255),
|
||||
new Color(255, 255, 255)
|
||||
};
|
||||
|
||||
/**
|
||||
* Default private constructor to prevent instantiation.
|
||||
*/
|
||||
private SgrColor() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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.jterminal.vt100;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import com.loomcom.symon.jterminal.AbstractTerminalModel;
|
||||
import com.loomcom.symon.jterminal.TerminalCell;
|
||||
import com.loomcom.symon.jterminal.TerminalModel;
|
||||
import com.loomcom.symon.jterminal.bell.BellStrategy;
|
||||
import com.loomcom.symon.jterminal.bell.NopBellStrategy;
|
||||
|
||||
/**
|
||||
* A VT100/ANSI-compatible terminal model.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class Vt100TerminalModel extends AbstractTerminalModel {
|
||||
|
||||
/**
|
||||
* A {@link AnsiControlSequenceListener} which modifies the
|
||||
* {@link TerminalModel} appropriately when an event happens.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
private class Vt100Listener implements AnsiControlSequenceListener {
|
||||
|
||||
/**
|
||||
* The saved cursor row.
|
||||
*/
|
||||
private int savedCursorRow = -1;
|
||||
|
||||
/**
|
||||
* The saved cursor column.
|
||||
*/
|
||||
private int savedCursorColumn = -1;
|
||||
|
||||
@Override
|
||||
public void parsedControlSequence(AnsiControlSequence seq) {
|
||||
char command = seq.getCommand();
|
||||
int n;
|
||||
String[] parameters = seq.getParameters();
|
||||
|
||||
switch (command) {
|
||||
case 'A':
|
||||
case 'B':
|
||||
case 'C':
|
||||
case 'D':
|
||||
n = 1;
|
||||
if (parameters.length == 1) {
|
||||
n = Integer.parseInt(parameters[0]);
|
||||
}
|
||||
if (command == 'A') {
|
||||
moveCursorUp(n);
|
||||
} else if (command == 'B') {
|
||||
moveCursorDown(n);
|
||||
} else if (command == 'C') {
|
||||
moveCursorForward(n);
|
||||
} else {
|
||||
moveCursorBack(n);
|
||||
}
|
||||
break;
|
||||
case 'E':
|
||||
case 'F':
|
||||
n = 1;
|
||||
if (parameters.length == 1) {
|
||||
n = Integer.parseInt(parameters[0]);
|
||||
}
|
||||
if (command == 'E') {
|
||||
moveCursorDown(n);
|
||||
} else {
|
||||
moveCursorUp(n);
|
||||
}
|
||||
setCursorColumn(0);
|
||||
break;
|
||||
case 'G':
|
||||
if (parameters.length == 1) {
|
||||
n = Integer.parseInt(parameters[0]);
|
||||
setCursorColumn(n - 1);
|
||||
}
|
||||
break;
|
||||
case 'H':
|
||||
case 'f':
|
||||
if (parameters.length == 2) {
|
||||
n = 1;
|
||||
int m = 1;
|
||||
if (parameters[0].length() > 0) {
|
||||
n = Integer.parseInt(parameters[0]);
|
||||
}
|
||||
if (parameters[1].length() > 0) {
|
||||
m = Integer.parseInt(parameters[1]);
|
||||
}
|
||||
setCursorRow(n - 1);
|
||||
setCursorColumn(m - 1);
|
||||
}
|
||||
break;
|
||||
case 'J':
|
||||
n = 0;
|
||||
if (parameters.length == 1) {
|
||||
n = Integer.parseInt(parameters[0]);
|
||||
}
|
||||
if (n == 0) {
|
||||
int row = cursorRow;
|
||||
int column = cursorColumn;
|
||||
while(row < rows) {
|
||||
while(column < columns) {
|
||||
cells[row][column] = null;
|
||||
column++;
|
||||
}
|
||||
column = 0;
|
||||
row++;
|
||||
}
|
||||
} else if (n == 1) {
|
||||
int row = cursorRow;
|
||||
int column = cursorColumn;
|
||||
while(row >= 0) {
|
||||
while(column >= 0) {
|
||||
cells[row][column] = null;
|
||||
column--;
|
||||
}
|
||||
column = columns - 1;
|
||||
row--;
|
||||
}
|
||||
} else if (n == 2) {
|
||||
clear();
|
||||
}
|
||||
break;
|
||||
case 'K':
|
||||
n = 0;
|
||||
if (parameters.length == 1) {
|
||||
n = Integer.parseInt(parameters[0]);
|
||||
}
|
||||
if (n == 0) {
|
||||
for (int row = cursorRow; row < rows; row++) {
|
||||
cells[row][cursorColumn] = null;
|
||||
}
|
||||
} else if (n == 1) {
|
||||
for (int row = cursorRow; row >= 0; row--) {
|
||||
cells[row][cursorColumn] = null;
|
||||
}
|
||||
} else if (n == 2) {
|
||||
for (int column = 0; column < columns; column++) {
|
||||
cells[cursorRow][column] = null;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'm':
|
||||
if (parameters.length == 0) {
|
||||
parameters = new String[] { "0" };
|
||||
}
|
||||
for (String parameter : parameters) {
|
||||
if (parameter.equals("0")) {
|
||||
foregroundColor = DEFAULT_FOREGROUND_COLOR;
|
||||
backgroundColor = DEFAULT_BACKGROUND_COLOR;
|
||||
backgroundBold = DEFAULT_BACKGROUND_BOLD;
|
||||
foregroundBold = DEFAULT_FOREGROUND_BOLD;
|
||||
} else if (parameter.equals("2")) {
|
||||
backgroundBold = true;
|
||||
foregroundBold = true;
|
||||
} else if (parameter.equals("22")) {
|
||||
backgroundBold = false;
|
||||
foregroundBold = false;
|
||||
} else if ((parameter.startsWith("3") || parameter.startsWith("4")) && parameter.length() == 2) {
|
||||
int color = Integer.parseInt(parameter.substring(1));
|
||||
if (parameter.startsWith("3")) {
|
||||
foregroundColor = color;
|
||||
} else if (parameter.startsWith("4")) {
|
||||
backgroundColor = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
if (savedCursorColumn != -1 && savedCursorRow != -1) {
|
||||
cursorColumn = savedCursorColumn;
|
||||
cursorRow = savedCursorRow;
|
||||
}
|
||||
break;
|
||||
case 's':
|
||||
savedCursorColumn = cursorColumn;
|
||||
savedCursorRow = cursorRow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsedString(String str) {
|
||||
for (char ch : str.toCharArray()) {
|
||||
switch (ch) {
|
||||
case '\0':
|
||||
continue;
|
||||
case '\r':
|
||||
cursorColumn = 0;
|
||||
continue;
|
||||
case '\n':
|
||||
cursorRow++;
|
||||
break;
|
||||
case '\t':
|
||||
while ((++cursorColumn % TAB_WIDTH) != 0);
|
||||
continue;
|
||||
case 8: // ASCII Backspace
|
||||
case 127: // ASCII Delete
|
||||
if (cursorColumn > 0) {
|
||||
cells[cursorRow][--cursorColumn] = null;
|
||||
}
|
||||
continue;
|
||||
case 7:
|
||||
bellStrategy.soundBell();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cursorColumn >= columns) {
|
||||
cursorColumn = 0;
|
||||
cursorRow++;
|
||||
}
|
||||
|
||||
if (cursorRow >= bufferSize) {
|
||||
for (int i = 1; i < bufferSize; i++) {
|
||||
System.arraycopy(cells[i], 0, cells[i - 1], 0, columns);
|
||||
}
|
||||
for (int i = 0; i < columns; i++) {
|
||||
cells[bufferSize - 1][i] = null;
|
||||
}
|
||||
cursorRow--;
|
||||
}
|
||||
|
||||
Color back = backgroundBold ? SgrColor.COLOR_BRIGHT[backgroundColor] : SgrColor.COLOR_NORMAL[backgroundColor];
|
||||
Color fore = foregroundBold ? SgrColor.COLOR_BRIGHT[foregroundColor] : SgrColor.COLOR_NORMAL[foregroundColor];
|
||||
if (ch != '\n') {
|
||||
cells[cursorRow][cursorColumn++] = new TerminalCell(ch, back, fore);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The default number of columns.
|
||||
*/
|
||||
private static final int DEFAULT_COLUMNS = 80;
|
||||
|
||||
/**
|
||||
* The default number of rows.
|
||||
*/
|
||||
private static final int DEFAULT_ROWS = 25;
|
||||
|
||||
/**
|
||||
* The tab width in characters.
|
||||
*/
|
||||
private static final int TAB_WIDTH = 8;
|
||||
|
||||
/**
|
||||
* The default foreground bold flag.
|
||||
*/
|
||||
private static final boolean DEFAULT_FOREGROUND_BOLD = false;
|
||||
|
||||
/**
|
||||
* The default background bold flag.
|
||||
*/
|
||||
private static final boolean DEFAULT_BACKGROUND_BOLD = false;
|
||||
|
||||
/**
|
||||
* The default foreground color.
|
||||
*/
|
||||
private static final int DEFAULT_FOREGROUND_COLOR = 7;
|
||||
|
||||
/**
|
||||
* The default background color.
|
||||
*/
|
||||
private static final int DEFAULT_BACKGROUND_COLOR = 0;
|
||||
|
||||
/**
|
||||
* The ANSI control sequence listener.
|
||||
*/
|
||||
private final AnsiControlSequenceListener listener = this.new Vt100Listener();
|
||||
|
||||
/**
|
||||
* The ANSI control sequence parser.
|
||||
*/
|
||||
private final AnsiControlSequenceParser parser = new AnsiControlSequenceParser(listener);
|
||||
|
||||
/**
|
||||
* The current bell strategy.
|
||||
*/
|
||||
private BellStrategy bellStrategy = new NopBellStrategy();
|
||||
|
||||
/**
|
||||
* The array of cells.
|
||||
*/
|
||||
private TerminalCell[][] cells;
|
||||
|
||||
/**
|
||||
* The number of columns.
|
||||
*/
|
||||
private int columns;
|
||||
|
||||
/**
|
||||
* The number of rows.
|
||||
*/
|
||||
private int rows;
|
||||
|
||||
/**
|
||||
* The buffer size.
|
||||
*/
|
||||
private int bufferSize;
|
||||
|
||||
/**
|
||||
* The cursor row.
|
||||
*/
|
||||
private int cursorRow = 0;
|
||||
|
||||
/**
|
||||
* The cursor column.
|
||||
*/
|
||||
private int cursorColumn = 0;
|
||||
|
||||
/**
|
||||
* The current foreground bold flag.
|
||||
*/
|
||||
private boolean foregroundBold = DEFAULT_FOREGROUND_BOLD;
|
||||
|
||||
/**
|
||||
* The current background bold flag.
|
||||
*/
|
||||
private boolean backgroundBold = DEFAULT_BACKGROUND_BOLD;
|
||||
|
||||
/**
|
||||
* The current foreground color.
|
||||
*/
|
||||
private int foregroundColor = DEFAULT_FOREGROUND_COLOR;
|
||||
|
||||
/**
|
||||
* The current background color.
|
||||
*/
|
||||
private int backgroundColor = DEFAULT_BACKGROUND_COLOR;
|
||||
|
||||
/**
|
||||
* Creates the terminal model with the default number of columns and rows,
|
||||
* and the default buffer size.
|
||||
*/
|
||||
public Vt100TerminalModel() {
|
||||
this(DEFAULT_COLUMNS, DEFAULT_ROWS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the terminal model with the specified number of columns and
|
||||
* rows. The buffer size is set to the number of rows.
|
||||
* @param columns The number of columns.
|
||||
* @param rows The number of rows.
|
||||
* @throws IllegalArgumentException if the number of rows or columns is
|
||||
* negative.
|
||||
*/
|
||||
public Vt100TerminalModel(int columns, int rows) {
|
||||
this(columns, rows, rows);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the terminal model with the specified number of columns and rows
|
||||
* and the specified buffer size.
|
||||
* @param columns The number of columns.
|
||||
* @param rows The number of rows.
|
||||
* @param bufferSize The buffer size.
|
||||
* @throws IllegalArgumentException if the number of rows or columns is
|
||||
* negative, or if the buffer size is less than the number of rows.
|
||||
*/
|
||||
public Vt100TerminalModel(int columns, int rows, int bufferSize) {
|
||||
if (columns < 0 || rows < 0 || bufferSize < 0) {
|
||||
throw new IllegalArgumentException("Zero or positive values only allowed for columns, rows and buffer size.");
|
||||
}
|
||||
if (bufferSize < rows) {
|
||||
throw new IllegalArgumentException("The buffer is too small");
|
||||
}
|
||||
this.columns = columns;
|
||||
this.rows = rows;
|
||||
this.bufferSize = bufferSize;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the terminal model.
|
||||
*/
|
||||
private void init() {
|
||||
cells = new TerminalCell[bufferSize][columns];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCursorRow() {
|
||||
return cursorRow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCursorRow(int row) {
|
||||
if (row < 0 || row >= bufferSize) {
|
||||
throw new IllegalArgumentException("row out of range");
|
||||
}
|
||||
cursorRow = row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCursorColumn() {
|
||||
return cursorColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCursorColumn(int column) {
|
||||
if (column < 0 || column >= columns) {
|
||||
throw new IllegalArgumentException("column out of range");
|
||||
}
|
||||
cursorColumn = column;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TerminalCell getCell(int column, int row) {
|
||||
if (column < 0 || row < 0 || column >= columns || row >= bufferSize) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return cells[row][column];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCell(int column, int row, TerminalCell cell) {
|
||||
if (column < 0 || row < 0 || column >= columns || row >= bufferSize) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
cells[row][column] = cell;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void print(String str) {
|
||||
if (str == null) {
|
||||
throw new NullPointerException("str");
|
||||
}
|
||||
parser.parse(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumns() {
|
||||
return columns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRows() {
|
||||
return rows;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBufferSize() {
|
||||
return bufferSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBellStrategy(BellStrategy strategy) {
|
||||
if (strategy == null) {
|
||||
throw new NullPointerException("strategy");
|
||||
}
|
||||
this.bellStrategy = strategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getDefaultBackgroundColor() {
|
||||
final int bg = DEFAULT_BACKGROUND_COLOR;
|
||||
return DEFAULT_BACKGROUND_BOLD ? SgrColor.COLOR_BRIGHT[bg] : SgrColor.COLOR_NORMAL[bg];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getDefaultForegroundColor() {
|
||||
final int fg = DEFAULT_FOREGROUND_COLOR;
|
||||
return DEFAULT_FOREGROUND_BOLD ? SgrColor.COLOR_BRIGHT[fg] : SgrColor.COLOR_NORMAL[fg];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -42,9 +42,9 @@ public interface Machine {
|
||||
Memory getRam();
|
||||
|
||||
Acia getAcia();
|
||||
|
||||
|
||||
Pia getPia();
|
||||
|
||||
|
||||
Crtc getCrtc();
|
||||
|
||||
Memory getRom();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -68,7 +68,7 @@ public class MulticompMachine implements Machine {
|
||||
private Memory rom;
|
||||
|
||||
|
||||
public MulticompMachine() throws Exception {
|
||||
public MulticompMachine(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);
|
||||
@@ -79,20 +79,23 @@ public class MulticompMachine implements Machine {
|
||||
bus.addDevice(ram);
|
||||
bus.addDevice(acia, 1);
|
||||
bus.addDevice(new SdController(SD_BASE), 1);
|
||||
|
||||
// TODO: Make this configurable, of course.
|
||||
File romImage = new File("rom.bin");
|
||||
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 " + romImage +
|
||||
|
||||
if (romFile != null) {
|
||||
File romImage = new File("rom.bin");
|
||||
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 " + romImage +
|
||||
" not found, loading empty R/W memory image.");
|
||||
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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -46,7 +46,7 @@ public class SimpleMachine implements Machine {
|
||||
private final Memory ram;
|
||||
private final Cpu cpu;
|
||||
|
||||
public SimpleMachine() throws MemoryRangeException {
|
||||
public SimpleMachine(String romFile) throws MemoryRangeException {
|
||||
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
|
||||
this.ram = new Memory(BUS_BOTTOM, BUS_TOP, false);
|
||||
this.cpu = new Cpu();
|
||||
@@ -91,7 +91,7 @@ public class SimpleMachine implements Machine {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRom(Memory rom) throws MemoryRangeException {
|
||||
public void setRom(Memory rom) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -70,7 +70,7 @@ public class SymonMachine implements Machine {
|
||||
private Memory rom;
|
||||
|
||||
|
||||
public SymonMachine() throws Exception {
|
||||
public SymonMachine(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);
|
||||
@@ -83,14 +83,18 @@ public class SymonMachine implements Machine {
|
||||
bus.addDevice(pia);
|
||||
bus.addDevice(acia);
|
||||
bus.addDevice(crtc);
|
||||
|
||||
// TODO: Make this configurable, of course.
|
||||
File romImage = new File("rom.bin");
|
||||
if (romImage.canRead()) {
|
||||
logger.info("Loading ROM image from file {}", romImage);
|
||||
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
|
||||
|
||||
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("Default ROM file {} not found, loading empty R/W memory image.", romImage);
|
||||
logger.info("No ROM file specified, loading empty R/W memory image.");
|
||||
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -30,10 +30,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
/**
|
||||
@@ -46,8 +43,8 @@ public class BreakpointsWindow extends JFrame {
|
||||
private static final Dimension FRAME_SIZE = new Dimension(240, 280);
|
||||
private static final String EMPTY_STRING = "";
|
||||
|
||||
private JFrame mainWindow;
|
||||
private Breakpoints breakpoints;
|
||||
private final JFrame mainWindow;
|
||||
private final Breakpoints breakpoints;
|
||||
|
||||
public BreakpointsWindow(Breakpoints breakpoints,
|
||||
JFrame mainWindow) {
|
||||
@@ -73,18 +70,8 @@ public class BreakpointsWindow extends JFrame {
|
||||
|
||||
final JTable breakpointsTable = new JTable(breakpoints);
|
||||
breakpointsTable.setShowGrid(true);
|
||||
breakpointsTable.setGridColor(Color.LIGHT_GRAY);
|
||||
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
breakpointsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (e.getFirstIndex() > -1) {
|
||||
removeButton.setEnabled(true);
|
||||
} else {
|
||||
removeButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
breakpointsTable.getSelectionModel().addListSelectionListener(e -> removeButton.setEnabled(e.getFirstIndex() > -1));
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(breakpointsTable);
|
||||
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
@@ -92,45 +79,33 @@ public class BreakpointsWindow extends JFrame {
|
||||
|
||||
breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
ActionListener addBreakpointListener = new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
int value;
|
||||
ActionListener addBreakpointListener = e -> {
|
||||
int value;
|
||||
|
||||
String newBreakpoint = addTextField.getText();
|
||||
String newBreakpoint = addTextField.getText();
|
||||
|
||||
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.warn("Can't parse page number {}", newBreakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
breakpoints.addBreakpoint(value);
|
||||
|
||||
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
|
||||
|
||||
addTextField.setText(EMPTY_STRING);
|
||||
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
|
||||
} catch (NumberFormatException ex) {
|
||||
logger.warn("Can't parse page number {}", newBreakpoint);
|
||||
return;
|
||||
}
|
||||
|
||||
breakpoints.addBreakpoint(value);
|
||||
|
||||
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
|
||||
|
||||
addTextField.setText(EMPTY_STRING);
|
||||
};
|
||||
|
||||
addButton.addActionListener(addBreakpointListener);
|
||||
addTextField.addActionListener(addBreakpointListener);
|
||||
|
||||
removeButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow());
|
||||
}
|
||||
});
|
||||
removeButton.addActionListener(e -> breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow()));
|
||||
|
||||
controlPanel.add(addTextField);
|
||||
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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -23,8 +23,8 @@
|
||||
|
||||
package com.loomcom.symon.ui;
|
||||
|
||||
import com.grahamedgecombe.jterminal.JTerminal;
|
||||
import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel;
|
||||
import com.loomcom.symon.jterminal.JTerminal;
|
||||
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
|
||||
import com.loomcom.symon.exceptions.FifoUnderrunException;
|
||||
import com.loomcom.symon.util.FifoRingBuffer;
|
||||
|
||||
@@ -44,16 +44,19 @@ import java.awt.event.MouseListener;
|
||||
|
||||
public class Console extends JTerminal implements KeyListener, MouseListener {
|
||||
|
||||
private static final int DEFAULT_BORDER_WIDTH = 10;
|
||||
private static final long serialVersionUID = 6633818486963338126L;
|
||||
|
||||
private static final int DEFAULT_BORDER_WIDTH = 10;
|
||||
// If true, swap CR and LF characters.
|
||||
private static final boolean SWAP_CR_AND_LF = true;
|
||||
|
||||
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
|
||||
private boolean sendCrForLf = false;
|
||||
private FifoRingBuffer<Character> typeAheadBuffer;
|
||||
private final boolean sendCrForLf;
|
||||
private final FifoRingBuffer<Character> typeAheadBuffer;
|
||||
|
||||
public Console(int columns, int rows, Font font, boolean sendCrForLf) {
|
||||
super(new Vt100TerminalModel(columns, rows), font);
|
||||
//super(new Vt100TerminalModel(columns, rows));
|
||||
// A small type-ahead buffer, as might be found in any real
|
||||
// VT100-style serial terminal.
|
||||
this.typeAheadBuffer = new FifoRingBuffer<>(128);
|
||||
@@ -104,7 +107,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
|
||||
}
|
||||
}
|
||||
|
||||
if (sendCrForLf && keyTyped == 0x0d) {
|
||||
if (sendCrForLf && (keyTyped == 0x0d)) {
|
||||
typeAheadBuffer.push((char) 0x0d);
|
||||
typeAheadBuffer.push((char) 0x0a);
|
||||
} else {
|
||||
|
||||
@@ -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
|
||||
* 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 MemoryTableModel memoryTableModel;
|
||||
private final MemoryTableModel memoryTableModel;
|
||||
private JTable memoryTable;
|
||||
private JTextField pageNumberTextField;
|
||||
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() {
|
||||
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
|
||||
* being edited.
|
||||
*/
|
||||
private class MemoryTable extends JTable {
|
||||
private static class MemoryTable extends JTable {
|
||||
|
||||
public MemoryTable(TableModel tableModel) {
|
||||
super(tableModel);
|
||||
@@ -242,7 +242,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
|
||||
final Component editor = getEditorComponent();
|
||||
|
||||
if (editor != null && editor instanceof JTextComponent) {
|
||||
if (editor instanceof JTextComponent) {
|
||||
((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
|
||||
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.
|
||||
*/
|
||||
private class MemoryTableModel extends AbstractTableModel {
|
||||
private static class MemoryTableModel extends AbstractTableModel {
|
||||
|
||||
private Bus bus;
|
||||
private final Bus bus;
|
||||
private int pageNumber;
|
||||
|
||||
private static final int COLUMN_COUNT = 17;
|
||||
@@ -331,10 +331,10 @@ public class MemoryWindow extends JFrame implements ActionListener {
|
||||
return Utils.wordToHex(fullAddress(row, 1));
|
||||
} else if (column < 9) {
|
||||
// Display hex value of the data
|
||||
return Utils.byteToHex(bus.read(fullAddress(row, column)));
|
||||
return Utils.byteToHex(bus.read(fullAddress(row, column), false));
|
||||
} else {
|
||||
// Display the ASCII equivalent (if printable)
|
||||
return Utils.byteToAscii(bus.read(fullAddress(row, column - 8)));
|
||||
return Utils.byteToAscii(bus.read(fullAddress(row, column - 8), false));
|
||||
}
|
||||
} catch (MemoryAccessException ex) {
|
||||
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
|
||||
* 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 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.haltOnBreak = haltOnBreak;
|
||||
createUi();
|
||||
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
|
||||
* a copy of this software and associated documentation files (the
|
||||
@@ -30,6 +30,7 @@ import com.loomcom.symon.machines.Machine;
|
||||
import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.EtchedBorder;
|
||||
import javax.swing.plaf.metal.MetalTextFieldUI;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
@@ -69,7 +70,7 @@ public class StatusPanel extends JPanel {
|
||||
private JTextField xField;
|
||||
private JTextField yField;
|
||||
|
||||
private Machine machine;
|
||||
private final Machine machine;
|
||||
|
||||
private static final int EMPTY_BORDER = 10;
|
||||
private static final Border LABEL_BORDER = BorderFactory.createEmptyBorder(0, 5, 0, 0);
|
||||
@@ -85,7 +86,7 @@ public class StatusPanel extends JPanel {
|
||||
|
||||
private void createUi() {
|
||||
Border emptyBorder = BorderFactory.createEmptyBorder(EMPTY_BORDER, EMPTY_BORDER,
|
||||
EMPTY_BORDER, EMPTY_BORDER);
|
||||
EMPTY_BORDER, EMPTY_BORDER);
|
||||
Border etchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED);
|
||||
|
||||
setBorder(BorderFactory.createCompoundBorder(emptyBorder, etchedBorder));
|
||||
@@ -124,16 +125,15 @@ public class StatusPanel extends JPanel {
|
||||
|
||||
// Add tool-tip text
|
||||
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");
|
||||
irqDisableFlagLabel.setToolTipText("Interrupt Disable: Processor will not respond to IRQ");
|
||||
decimalModeFlagLabel.setToolTipText("Decimal Mode");
|
||||
breakFlagLabel.setToolTipText("Break: BRK instruction occurred");
|
||||
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");
|
||||
|
||||
|
||||
statusFlagsPanel.add(negativeFlagLabel);
|
||||
statusFlagsPanel.add(overflowFlagLabel);
|
||||
statusFlagsPanel.add(breakFlagLabel);
|
||||
@@ -242,7 +242,6 @@ public class StatusPanel extends JPanel {
|
||||
constraints.gridy = 1;
|
||||
add(statusFlagsPanel, constraints);
|
||||
|
||||
|
||||
constraints.insets = new Insets(5, 0, 0, 0);
|
||||
constraints.gridy = 2;
|
||||
add(opcodeLabel, constraints);
|
||||
@@ -391,7 +390,7 @@ public class StatusPanel extends JPanel {
|
||||
textField.setMinimumSize(size);
|
||||
textField.setMaximumSize(size);
|
||||
textField.setPreferredSize(size);
|
||||
textField.setBackground(Color.WHITE);
|
||||
|
||||
return textField;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
* Copyright (c) 2008-2025 Seth J. Morabito <web@loomcom.com>
|
||||
* Maik Merten <maikmerten@googlemail.com>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -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
|
||||
* 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 long WINDOW_REPAINT_INTERVAL = 66; // 30fps rate
|
||||
private static final int CHAR_WIDTH = 8;
|
||||
private static final int CHAR_HEIGHT = 8;
|
||||
private static final String ASCII_ROM = "/ascii.rom";
|
||||
|
||||
private final int scaleX, scaleY;
|
||||
private final boolean shouldScale;
|
||||
|
||||
private BufferedImage image;
|
||||
private int[] charRom;
|
||||
private final int[] charRom;
|
||||
|
||||
private int horizontalDisplayed;
|
||||
private int verticalDisplayed;
|
||||
@@ -77,9 +79,9 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
private boolean hideCursor;
|
||||
|
||||
private Dimension dimensions;
|
||||
private Crtc crtc;
|
||||
private final Crtc crtc;
|
||||
|
||||
private ScheduledExecutorService scheduler;
|
||||
private final ScheduledExecutorService scheduler;
|
||||
private ScheduledFuture<?> cursorBlinker;
|
||||
|
||||
/**
|
||||
@@ -122,13 +124,19 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
*/
|
||||
private class CursorBlinker implements Runnable {
|
||||
public void run() {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (cursorBlinkRate > 0) {
|
||||
hideCursor = !hideCursor;
|
||||
VideoWindow.this.repaint();
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (cursorBlinkRate > 0) {
|
||||
hideCursor = !hideCursor;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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.crtc = crtc;
|
||||
this.charRom = loadCharRom("/ascii.rom");
|
||||
this.charRom = loadCharRom();
|
||||
this.scaleX = scaleX;
|
||||
this.scaleY = scaleY;
|
||||
this.shouldScale = (scaleX > 1 || scaleY > 1);
|
||||
@@ -152,6 +160,11 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
scheduler.scheduleAtFixedRate(new WindowPainter(),
|
||||
WINDOW_REPAINT_INTERVAL,
|
||||
WINDOW_REPAINT_INTERVAL,
|
||||
TimeUnit.MILLISECONDS);
|
||||
|
||||
// Capture some state from the CRTC that will define the
|
||||
// window size. When these values change, the window will
|
||||
// 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
|
||||
* 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.
|
||||
* @throws IOException
|
||||
* @throws IOException Failure to load character ROM.
|
||||
*/
|
||||
private int[] loadCharRom(String resource) throws IOException {
|
||||
BufferedInputStream bis = null;
|
||||
try {
|
||||
bis = new BufferedInputStream(this.getClass().getResourceAsStream(resource));
|
||||
private int[] loadCharRom() throws IOException {
|
||||
var resource = this.getClass().getResourceAsStream(ASCII_ROM);
|
||||
|
||||
if (resource == null) {
|
||||
throw new IOException("Resource " + ASCII_ROM + " not found");
|
||||
}
|
||||
|
||||
try (BufferedInputStream bis = new BufferedInputStream(resource)) {
|
||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||
while (bis.available() > 0) {
|
||||
bos.write(bis.read());
|
||||
@@ -296,7 +312,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
int[] converted = new int[raw.length * CHAR_WIDTH];
|
||||
|
||||
int romIndex = 0;
|
||||
for (int i = 0; i < converted.length;) {
|
||||
for (int i = 0; i < converted.length; ) {
|
||||
byte charRow = raw[romIndex++];
|
||||
|
||||
for (int j = 7; j >= 0; j--) {
|
||||
@@ -304,10 +320,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
|
||||
}
|
||||
}
|
||||
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
|
||||
* 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> {
|
||||
|
||||
private Queue<E> fifoBuffer;
|
||||
private int maxLength;
|
||||
private final Queue<E> fifoBuffer;
|
||||
private final int maxLength;
|
||||
|
||||
public FifoRingBuffer(int maxLength) {
|
||||
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
|
||||
* 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
|
||||
@@ -7,7 +7,7 @@ import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
public class AciaTest6850 {
|
||||
public class Acia6850Test {
|
||||
|
||||
private final static int CMD_STAT_REG = 0;
|
||||
private final static int DATA_REG = 1;
|
||||
@@ -67,7 +67,7 @@ public class AciaTest6850 {
|
||||
verify(mockBus, never()).assertIrq();
|
||||
|
||||
// Transmission should cause IRQ
|
||||
acia.txRead();
|
||||
acia.txRead(true);
|
||||
|
||||
verify(mockBus, atLeastOnce()).assertIrq();
|
||||
}
|
||||
@@ -86,16 +86,88 @@ public class AciaTest6850 {
|
||||
acia.write(DATA_REG, 'a');
|
||||
|
||||
// Transmission should cause IRQ
|
||||
acia.txRead();
|
||||
acia.txRead(true);
|
||||
|
||||
verify(mockBus, never()).assertIrq();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTriggerInterruptFlagOnRxFullIfRxIrqEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = newAcia();
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Disable TX IRQ, Enable RX IRQ
|
||||
acia.write(CMD_STAT_REG, 0x80);
|
||||
|
||||
acia.rxWrite('a');
|
||||
|
||||
// Receive should cause IRQ flag to be set
|
||||
assertEquals(0x80, acia.read(0x0000, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotTriggerInterruptFlagOnRxFullIfRxIrqNotEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = newAcia();
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Disable TX IRQ, Disable RX IRQ
|
||||
acia.write(CMD_STAT_REG, 0x00);
|
||||
|
||||
acia.rxWrite('a');
|
||||
|
||||
// Receive should not cause IRQ flag to be set
|
||||
assertEquals(0x00, acia.read(0x0000, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTriggerInterruptFlagOnTxEmptyIfTxIrqEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = newAcia();
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Enable TX IRQ, Disable RX IRQ
|
||||
acia.write(CMD_STAT_REG, 0x20);
|
||||
|
||||
// Write data
|
||||
acia.write(1, 'a');
|
||||
|
||||
verify(mockBus, never()).assertIrq();
|
||||
|
||||
// Transmission should cause IRQ flag to be set
|
||||
acia.txRead(true);
|
||||
|
||||
assertEquals(0x80, acia.read(0x0000, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotTriggerInterruptFlagOnTxEmptyIfTxIrqNotEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = newAcia();
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Disable TX IRQ, Disable RX IRQ
|
||||
acia.write(CMD_STAT_REG, 0x02);
|
||||
|
||||
// Write data
|
||||
acia.write(DATA_REG, 'a');
|
||||
|
||||
// Transmission should not cause IRQ flag to be set
|
||||
acia.txRead(true);
|
||||
|
||||
assertEquals(0x00, acia.read(0x0000, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newAciaShouldHaveTxEmptyStatus() throws Exception {
|
||||
Acia acia = newAcia();
|
||||
|
||||
assertEquals(0x02, acia.read(CMD_STAT_REG) & 0x02);
|
||||
assertEquals(0x02, acia.read(CMD_STAT_REG, true) & 0x02);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,7 +175,7 @@ public class AciaTest6850 {
|
||||
Acia acia = newAcia();
|
||||
|
||||
acia.txWrite('a');
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG) & 0x02);
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG, true) & 0x02);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -112,7 +184,7 @@ public class AciaTest6850 {
|
||||
|
||||
acia.rxWrite('a');
|
||||
|
||||
assertEquals(0x01, acia.read(CMD_STAT_REG) & 0x01);
|
||||
assertEquals(0x01, acia.read(CMD_STAT_REG, true) & 0x01);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -123,12 +195,30 @@ public class AciaTest6850 {
|
||||
acia.rxWrite('a');
|
||||
acia.txWrite('b');
|
||||
|
||||
assertEquals(0x01, acia.read(CMD_STAT_REG) & 0x03);
|
||||
assertEquals(0x01, acia.read(CMD_STAT_REG, true) & 0x03);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aciaShouldOverrunAndReadShouldReset()
|
||||
throws Exception {
|
||||
|
||||
Acia acia = newAcia();
|
||||
|
||||
// overrun ACIA
|
||||
acia.rxWrite('a');
|
||||
acia.rxWrite('b');
|
||||
|
||||
assertEquals(0x20, acia.read(CMD_STAT_REG, true) & 0x20);
|
||||
|
||||
// read should reset
|
||||
acia.rxRead(true);
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG, true) & 0x20);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aciaShouldOverrunAndMemoryWindowReadShouldNotReset()
|
||||
throws Exception {
|
||||
|
||||
Acia acia = newAcia();
|
||||
|
||||
@@ -136,11 +226,11 @@ public class AciaTest6850 {
|
||||
acia.rxWrite('a');
|
||||
acia.rxWrite('b');
|
||||
|
||||
assertEquals(0x20, acia.read(CMD_STAT_REG) & 0x20);
|
||||
assertEquals(0x20, acia.read(CMD_STAT_REG, true) & 0x20);
|
||||
|
||||
// read should reset
|
||||
acia.rxRead();
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG) & 0x20);
|
||||
// memory window read should not reset
|
||||
acia.rxRead(false);
|
||||
assertEquals(0x20, acia.read(CMD_STAT_REG, true) & 0x20);
|
||||
|
||||
}
|
||||
|
||||
@@ -149,15 +239,15 @@ public class AciaTest6850 {
|
||||
throws Exception {
|
||||
Acia acia = newAcia();
|
||||
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG) & 0x01);
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG, true) & 0x01);
|
||||
|
||||
acia.rxWrite('a');
|
||||
|
||||
assertEquals(0x01, acia.read(CMD_STAT_REG) & 0x01);
|
||||
assertEquals(0x01, acia.read(CMD_STAT_REG, true) & 0x01);
|
||||
|
||||
acia.rxRead();
|
||||
acia.rxRead(true);
|
||||
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG) & 0x01);
|
||||
assertEquals(0x00, acia.read(CMD_STAT_REG, true) & 0x01);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class AciaTest {
|
||||
verify(mockBus, never()).assertIrq();
|
||||
|
||||
// Transmission should cause IRQ
|
||||
acia.txRead();
|
||||
acia.txRead(true);
|
||||
|
||||
verify(mockBus, atLeastOnce()).assertIrq();
|
||||
}
|
||||
@@ -73,17 +73,89 @@ public class AciaTest {
|
||||
// Write data
|
||||
acia.write(0, 'a');
|
||||
|
||||
// Transmission should cause IRQ
|
||||
acia.txRead();
|
||||
// Transmission should not cause IRQ
|
||||
acia.txRead(true);
|
||||
|
||||
verify(mockBus, never()).assertIrq();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTriggerInterruptFlagOnRxFullIfRxIrqEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = new Acia6551(0x000);
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Disable TX IRQ, Enable RX IRQ
|
||||
acia.write(2, 0x00);
|
||||
|
||||
acia.rxWrite('a');
|
||||
|
||||
// Receive should cause IRQ flag to be set
|
||||
assertEquals(0x80, acia.read(0x0001, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotTriggerInterruptFlagOnRxFullIfRxIrqNotEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = new Acia6551(0x000);
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Disable TX IRQ, Disable RX IRQ
|
||||
acia.write(2, 0x02);
|
||||
|
||||
acia.rxWrite('a');
|
||||
|
||||
// Receive should not cause IRQ flag to be set
|
||||
assertEquals(0x00, acia.read(0x0001, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTriggerInterruptFlagOnTxEmptyIfTxIrqEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = new Acia6551(0x000);
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Enable TX IRQ, Disable RX IRQ
|
||||
acia.write(2, 0x06);
|
||||
|
||||
// Write data
|
||||
acia.write(0, 'a');
|
||||
|
||||
verify(mockBus, never()).assertIrq();
|
||||
|
||||
// Transmission should cause IRQ flag to be set
|
||||
acia.txRead(true);
|
||||
|
||||
assertEquals(0x80, acia.read(0x0001, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotTriggerInterruptFlagOnTxEmptyIfTxIrqNotEnabled() throws Exception {
|
||||
Bus mockBus = mock(Bus.class);
|
||||
|
||||
Acia acia = new Acia6551(0x000);
|
||||
acia.setBus(mockBus);
|
||||
|
||||
// Disable TX IRQ, Disable RX IRQ
|
||||
acia.write(2, 0x02);
|
||||
|
||||
// Write data
|
||||
acia.write(0, 'a');
|
||||
|
||||
// Transmission should not cause IRQ flag to be set
|
||||
acia.txRead(true);
|
||||
|
||||
assertEquals(0x00, acia.read(0x0001, true) & 0x80);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void newAciaShouldHaveTxEmptyStatus() throws Exception {
|
||||
Acia acia = new Acia6551(0x000);
|
||||
|
||||
assertEquals(0x10, acia.read(0x0001));
|
||||
assertEquals(0x10, acia.read(0x0001, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -91,7 +163,7 @@ public class AciaTest {
|
||||
Acia acia = new Acia6551(0x000);
|
||||
|
||||
acia.txWrite('a');
|
||||
assertEquals(0x00, acia.read(0x0001));
|
||||
assertEquals(0x00, acia.read(0x0001, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -99,7 +171,7 @@ public class AciaTest {
|
||||
Acia acia = new Acia6551(0x000);
|
||||
|
||||
acia.rxWrite('a');
|
||||
assertEquals(0x18, acia.read(0x0001));
|
||||
assertEquals(0x18, acia.read(0x0001, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -110,7 +182,7 @@ public class AciaTest {
|
||||
acia.rxWrite('a');
|
||||
acia.txWrite('b');
|
||||
|
||||
assertEquals(0x08, acia.read(0x0001));
|
||||
assertEquals(0x08, acia.read(0x0001, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -123,14 +195,31 @@ public class AciaTest {
|
||||
acia.rxWrite('a');
|
||||
acia.rxWrite('b');
|
||||
|
||||
assertEquals(0x04, acia.read(0x0001) & 0x04);
|
||||
assertEquals(0x04, acia.read(0x0001, true) & 0x04);
|
||||
|
||||
// read should reset
|
||||
acia.rxRead();
|
||||
assertEquals(0x00, acia.read(0x0001) & 0x04);
|
||||
acia.rxRead(true);
|
||||
assertEquals(0x00, acia.read(0x0001, true) & 0x04);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aciaShouldOverrunAndMemoryWindowReadShouldNotReset()
|
||||
throws Exception {
|
||||
|
||||
Acia acia = new Acia6551(0x0000);
|
||||
|
||||
// overrun ACIA
|
||||
acia.rxWrite('a');
|
||||
acia.rxWrite('b');
|
||||
|
||||
assertEquals(0x04, acia.read(0x0001, true) & 0x04);
|
||||
|
||||
// memory window read should not reset
|
||||
acia.rxRead(false);
|
||||
assertEquals(0x04, acia.read(0x0001, true) & 0x04);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readingBuffersShouldResetStatus()
|
||||
@@ -141,11 +230,100 @@ public class AciaTest {
|
||||
acia.txWrite('b');
|
||||
|
||||
|
||||
assertEquals(0x08, acia.read(0x0001));
|
||||
assertEquals(0x08, acia.read(0x0001, true));
|
||||
|
||||
assertEquals('a', acia.rxRead());
|
||||
assertEquals('b', acia.txRead());
|
||||
assertEquals('a', acia.rxRead(true));
|
||||
assertEquals('b', acia.txRead(true));
|
||||
|
||||
assertEquals(0x10, acia.read(0x0001));
|
||||
assertEquals(0x10, acia.read(0x0001, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void A()
|
||||
throws Exception {
|
||||
Acia acia = new Acia6551(0x0000);
|
||||
|
||||
acia.rxWrite('a');
|
||||
acia.txWrite('b');
|
||||
|
||||
|
||||
assertEquals(0x08, acia.read(0x0001, true));
|
||||
|
||||
assertEquals('a', acia.rxRead(false));
|
||||
assertEquals('b', acia.txRead(false));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||
* in the 65C02 and 65816
|
||||
*/
|
||||
public class Cpu65C02AbsoluteModeTest extends TestCase {
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
private void makeCmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||
}
|
||||
|
||||
private void makeNmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||
}
|
||||
|
||||
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||
this.cpu = new Cpu(behavior);
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
|
||||
public void test_STZ() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.write(0x0010,0xff);
|
||||
|
||||
bus.loadProgram(0x9c, 0x10, 0x00); // STZ Absolute
|
||||
|
||||
// Test STZ Absolute ($0010)
|
||||
assertEquals(0xff, bus.read(0x0010, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x0010, true));
|
||||
|
||||
}
|
||||
|
||||
public void test_STZRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.write(0x0010,0xff);
|
||||
|
||||
bus.loadProgram(0x9c, 0x10, 0x00); // STZ Absolute
|
||||
|
||||
// Test STZ Absolute ($0010)
|
||||
assertEquals(0xff, bus.read(0x0010, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff, bus.read(0x0010, true));
|
||||
|
||||
}
|
||||
|
||||
public void test_TSB() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.loadProgram(0x0c, 0x10, 0x00); // 65C02 TSB Absolute $0010
|
||||
|
||||
bus.write(0x10, 0x01);
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x01,bus.read(0x10,true)); // 0x01 & 0x01 = 0x01
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0x02);
|
||||
cpu.step();
|
||||
assertEquals(0x03,bus.read(0x0010,true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
|
||||
}
|
||||
|
||||
public void test_TSBRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.loadProgram(0x0c, 0x10, 0x00); // 65C02 TSB Absolute $0010
|
||||
|
||||
bus.write(0x10, 0x00);
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0010,true));
|
||||
|
||||
}
|
||||
|
||||
public void test_TRB() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.loadProgram(0x1c, 0x00, 0x01); // 65C02 TRB Absolute $0010
|
||||
|
||||
bus.write(0x0100, 0x03);
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x02,bus.read(0x0100,true)); // $03 &= ~($01) = $02
|
||||
assertFalse(cpu.getZeroFlag()); // Z = !(A & M)
|
||||
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x02,bus.read(0x0100,true)); // $02 &= ~($01) = $02
|
||||
assertTrue(cpu.getZeroFlag()); // Z = !(A & M)
|
||||
|
||||
}
|
||||
|
||||
public void test_TRBRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.loadProgram(0x1c, 0x00, 0x01); // 65C02 TRB Absolute $0010
|
||||
|
||||
bus.write(0x0100, 0xff);
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0100,true));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||
* in the 65C02 and 65816
|
||||
*/
|
||||
public class Cpu65C02AbsoluteXModeTest extends TestCase {
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
private void makeCmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||
}
|
||||
|
||||
private void makeNmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||
}
|
||||
|
||||
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||
this.cpu = new Cpu(behavior);
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
|
||||
public void test_STZ() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.write(0x0011,0xff);
|
||||
|
||||
bus.loadProgram(0x9e, 0x10, 0x00); // STZ Absolute,X
|
||||
|
||||
// Test STZ Absolute,X ($0011)
|
||||
cpu.setXRegister(0x01);
|
||||
assertEquals(0xff, bus.read(0x0011, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x0011, true));
|
||||
}
|
||||
|
||||
public void test_STZRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.write(0x0011,0xff);
|
||||
|
||||
bus.loadProgram(0x9e, 0x10, 0x00); // STZ Absolute,X
|
||||
|
||||
// Test STZ Absolute,X ($0011)
|
||||
cpu.setXRegister(0x01);
|
||||
assertEquals(0xff, bus.read(0x0011, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff, bus.read(0x0011, true));
|
||||
}
|
||||
|
||||
public void test_JMP_Indirect_Absolute_X () throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.write(0x304,00);
|
||||
bus.write(0x0305,04);
|
||||
bus.loadProgram(0x7c, 0x00, 0x03);
|
||||
cpu.setXRegister(0x04);
|
||||
cpu.step();
|
||||
assertEquals(0x0400,cpu.getProgramCounter());
|
||||
}
|
||||
|
||||
public void test_JMP_Indirect_Absolute_XRequiresCmosCpu () throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.write(0x304,00);
|
||||
bus.write(0x0305,04);
|
||||
bus.loadProgram(0x7c, 0x00, 0x03);
|
||||
cpu.setXRegister(0x04);
|
||||
cpu.step();
|
||||
assertEquals(0x0203,cpu.getProgramCounter());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||
* in the 65C02 and 65816
|
||||
*/
|
||||
public class Cpu65C02ImmediateModeTest extends TestCase {
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
private void makeCmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||
}
|
||||
|
||||
private void makeNmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||
}
|
||||
|
||||
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||
this.cpu = new Cpu(behavior);
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
|
||||
public void test_BIT_Immediate() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.loadProgram(0x89, 0xF1); // 65C02 BIT #$01
|
||||
cpu.setAccumulator(0x02);
|
||||
|
||||
cpu.step();
|
||||
assertTrue(cpu.getZeroFlag()); // #$02 & #$F1 = 0
|
||||
assertEquals(0x02,cpu.getAccumulator()); // Accumulator should not be modified
|
||||
assertFalse(cpu.getNegativeFlag()); // BIT #Immediate should not set N or V Flags
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertFalse(cpu.getZeroFlag()); // #$F1 & #$01 = 1
|
||||
assertEquals(0x01,cpu.getAccumulator());
|
||||
|
||||
}
|
||||
|
||||
public void test_BIT_ImmediateRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.loadProgram(0x89, 0xF1); // 65C02 BIT #$01
|
||||
|
||||
cpu.step();
|
||||
cpu.setAccumulator(0x01);
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertEquals(0x01,cpu.getAccumulator());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||
* in the 65C02 and 65816
|
||||
*/
|
||||
public class Cpu65C02ImpliedModeTest extends TestCase {
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
private void makeCmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||
}
|
||||
|
||||
private void makeNmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||
}
|
||||
|
||||
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||
this.cpu = new Cpu(behavior);
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
|
||||
public void test_PHX() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.stackPush(0x00);
|
||||
cpu.setXRegister(0xff);
|
||||
bus.loadProgram(0xda);
|
||||
|
||||
assertEquals(cpu.stackPeek(), 0x00);
|
||||
cpu.step();
|
||||
assertEquals(cpu.stackPeek(), 0xff);
|
||||
|
||||
}
|
||||
|
||||
public void test_PHXRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
cpu.stackPush(0x00);
|
||||
cpu.setXRegister(0xff);
|
||||
bus.loadProgram(0xda);
|
||||
|
||||
assertEquals(cpu.stackPeek(), 0x00);
|
||||
cpu.step();
|
||||
assertEquals(cpu.stackPeek(), 0x00);
|
||||
|
||||
}
|
||||
|
||||
public void test_PLX() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.stackPush(0xff);
|
||||
cpu.setXRegister(0x00);
|
||||
bus.loadProgram(0xfa);
|
||||
|
||||
assertEquals(0x00, cpu.getXRegister());
|
||||
cpu.step();
|
||||
assertEquals(0xff, cpu.getXRegister());
|
||||
|
||||
}
|
||||
|
||||
public void test_PLXRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
cpu.stackPush(0xff);
|
||||
cpu.setXRegister(0x00);
|
||||
bus.loadProgram(0xfa);
|
||||
|
||||
assertEquals(0x00, cpu.getXRegister());
|
||||
cpu.step();
|
||||
assertEquals(0x00, cpu.getXRegister());
|
||||
|
||||
}
|
||||
|
||||
public void test_PHY() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.stackPush(0x00);
|
||||
cpu.setYRegister(0xff);
|
||||
bus.loadProgram(0x5a);
|
||||
|
||||
assertEquals(0x00, cpu.stackPeek());
|
||||
cpu.step();
|
||||
assertEquals(0xff, cpu.stackPeek());
|
||||
|
||||
}
|
||||
|
||||
public void test_PHYRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
cpu.stackPush(0x00);
|
||||
cpu.setYRegister(0xff);
|
||||
bus.loadProgram(0x5a);
|
||||
|
||||
assertEquals(0x00, cpu.stackPeek());
|
||||
cpu.step();
|
||||
assertEquals(0x00, cpu.stackPeek());
|
||||
|
||||
}
|
||||
|
||||
public void test_PLY() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.stackPush(0xff);
|
||||
cpu.setYRegister(0x00);
|
||||
bus.loadProgram(0x7a);
|
||||
|
||||
assertEquals(0x00, cpu.getYRegister());
|
||||
cpu.step();
|
||||
assertEquals(0xff, cpu.getYRegister());
|
||||
|
||||
}
|
||||
|
||||
public void test_PLYRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
cpu.stackPush(0xff);
|
||||
cpu.setYRegister(0x00);
|
||||
bus.loadProgram(0x7a);
|
||||
|
||||
assertEquals(0x00, cpu.getYRegister());
|
||||
cpu.step();
|
||||
assertEquals(0x00, cpu.getYRegister());
|
||||
|
||||
}
|
||||
|
||||
public void test_INC_A() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.setAccumulator(0x10);
|
||||
bus.loadProgram(0x1a);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x11, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
// Incrementing to 0 should set Zero Flag
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0xff);
|
||||
cpu.step();
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
// Should set Negative Flag
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0x7F);
|
||||
cpu.step();
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
|
||||
}
|
||||
|
||||
public void test_INC_ARequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
cpu.setAccumulator(0x10);
|
||||
bus.loadProgram(0x1a);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x10, cpu.getAccumulator());
|
||||
|
||||
}
|
||||
|
||||
public void test_DEC_A() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.setAccumulator(0x10);
|
||||
bus.loadProgram(0x3a);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x0F, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
// Decrementing to 0 should set Zero Flag
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
// Should set Negative Flag
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0x00);
|
||||
cpu.step();
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
|
||||
}
|
||||
|
||||
public void test_DEC_ARequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
cpu.setAccumulator(0x10);
|
||||
bus.loadProgram(0x3a);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x10, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
}
|
||||
|
||||
public void test_BRK_clearsDecimalModeFlag() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.setDecimalModeFlag();
|
||||
assertEquals(0x00, cpu.stackPeek());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertTrue(cpu.getDecimalModeFlag());
|
||||
assertEquals(0x0200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
|
||||
// Set the IRQ vector
|
||||
bus.write(0xffff, 0x12);
|
||||
bus.write(0xfffe, 0x34);
|
||||
|
||||
bus.loadProgram(0xea, // NOP
|
||||
0xea, // NOP
|
||||
0xea, // NOP
|
||||
0x00, // BRK
|
||||
0xea, // NOP
|
||||
0xea); // NOP
|
||||
|
||||
cpu.step(3); // Three NOP instructions
|
||||
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
assertTrue(cpu.getDecimalModeFlag());
|
||||
cpu.step(); // Triggers the BRK
|
||||
|
||||
// Was at PC = 0x204. PC+1 should now be on the stack
|
||||
assertEquals(0x02, bus.read(0x1ff, true)); // PC high byte
|
||||
assertEquals(0x05, bus.read(0x1fe, true)); // PC low byte
|
||||
|
||||
|
||||
// Interrupt vector held 0x1234, so we should be there.
|
||||
assertEquals(0x1234, cpu.getProgramCounter());
|
||||
assertEquals(0xfc, cpu.getStackPointer());
|
||||
|
||||
// B and I flags should have been set on P
|
||||
assertTrue(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,372 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||
* in the 65C02 and 65816
|
||||
*/
|
||||
public class Cpu65C02ZeroPageModeTest extends TestCase {
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
private void makeCmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||
}
|
||||
|
||||
private void makeNmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||
}
|
||||
|
||||
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||
this.cpu = new Cpu(behavior);
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
|
||||
public void test_STZ() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.write(0x0000,0xff);
|
||||
|
||||
bus.loadProgram(0x64,0x00); // STZ Zero Page $00
|
||||
|
||||
// Test STZ Zero Page
|
||||
assertEquals(0xff,bus.read(0x00, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x00, true));
|
||||
}
|
||||
|
||||
public void test_STZRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.write(0x0000,0xff);
|
||||
|
||||
bus.loadProgram(0x64,0x00); // STZ Zero Page $00
|
||||
|
||||
// Test STZ Zero Page
|
||||
assertEquals(0xff,bus.read(0x00, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x00, true));
|
||||
|
||||
}
|
||||
|
||||
public void test_SMB() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.loadProgram(0x87,0x01, // SMB0 $01
|
||||
0x97,0x01, // SMB1 $01
|
||||
0xa7,0x01, // SMB2 $01
|
||||
0xb7,0x01, // SMB3 $01
|
||||
0xc7,0x01, // SMB4 $01
|
||||
0xd7,0x01, // SMB5 $01
|
||||
0xe7,0x01, // SMB6 $01
|
||||
0xf7,0x01); // SMB7 $01
|
||||
|
||||
// SMB0
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 0,bus.read(0x0001, true));
|
||||
|
||||
// SMB1
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 1,bus.read(0x0001, true));
|
||||
|
||||
// SMB2
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 2,bus.read(0x0001, true));
|
||||
|
||||
// SMB3
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 3,bus.read(0x0001, true));
|
||||
|
||||
// SMB4
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 4,bus.read(0x0001, true));
|
||||
|
||||
// SMB5
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 5,bus.read(0x0001, true));
|
||||
|
||||
// SMB6
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 6,bus.read(0x0001, true));
|
||||
|
||||
// SMB7
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(1 << 7,bus.read(0x0001, true));
|
||||
|
||||
}
|
||||
|
||||
public void test_SMBRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.loadProgram(0x87,0x01, // SMB0 $01
|
||||
0x97,0x01, // SMB1 $01
|
||||
0xa7,0x01, // SMB2 $01
|
||||
0xb7,0x01, // SMB3 $01
|
||||
0xc7,0x01, // SMB4 $01
|
||||
0xd7,0x01, // SMB5 $01
|
||||
0xe7,0x01, // SMB6 $01
|
||||
0xf7,0x01); // SMB7 $01
|
||||
|
||||
// SMB0
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
// SMB1
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
// SMB2
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
// SMB3
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
// SMB4
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
// SMB5
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
// SMB6
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
// SMB7
|
||||
bus.write(0x01,0x00);
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x0001, true));
|
||||
|
||||
}
|
||||
|
||||
public void test_RMB() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.loadProgram(0x07,0x01, // SMB0 $01
|
||||
0x17,0x01, // SMB1 $01
|
||||
0x27,0x01, // SMB2 $01
|
||||
0x37,0x01, // SMB3 $01
|
||||
0x47,0x01, // SMB4 $01
|
||||
0x57,0x01, // SMB5 $01
|
||||
0x67,0x01, // SMB6 $01
|
||||
0x77,0x01); // SMB7 $01
|
||||
|
||||
// RMB0
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xfe,bus.read(0x0001, true));
|
||||
|
||||
// RMB1
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xfd,bus.read(0x0001, true));
|
||||
|
||||
// RMB2
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xfb,bus.read(0x0001, true));
|
||||
|
||||
// RMB3
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xf7,bus.read(0x0001, true));
|
||||
|
||||
// RMB4
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xef,bus.read(0x0001, true));
|
||||
|
||||
// RMB5
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xdf,bus.read(0x0001, true));
|
||||
|
||||
// RMB6
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xbf,bus.read(0x0001, true));
|
||||
|
||||
// RMB7
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0x7f,bus.read(0x0001, true));
|
||||
|
||||
}
|
||||
|
||||
public void test_RMBRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.loadProgram(0x07,0x01, // SMB0 $01
|
||||
0x17,0x01, // SMB1 $01
|
||||
0x27,0x01, // SMB2 $01
|
||||
0x37,0x01, // SMB3 $01
|
||||
0x47,0x01, // SMB4 $01
|
||||
0x57,0x01, // SMB5 $01
|
||||
0x67,0x01, // SMB6 $01
|
||||
0x77,0x01); // SMB7 $01
|
||||
|
||||
// RMB0
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
// RMB1
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
// RMB2
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
// RMB3
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
// RMB4
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
// RMB5
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
// RMB6
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
// RMB7
|
||||
bus.write(0x01,0xff);
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0001, true));
|
||||
|
||||
}
|
||||
|
||||
public void test_TSB() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.loadProgram(0x04, 0x10); // 65C02 TSB Zero Page $10
|
||||
|
||||
bus.write(0x10, 0x01);
|
||||
cpu.setAccumulator(0x03);
|
||||
cpu.step();
|
||||
assertEquals(0x03,bus.read(0x10,true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x10, 0x01);
|
||||
cpu.setAccumulator(0x02);
|
||||
cpu.step();
|
||||
assertEquals(0x03,bus.read(0x10,true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
|
||||
}
|
||||
|
||||
public void test_TSBRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.loadProgram(0x04, 0x10); // 65C02 TSB Zero Page $10
|
||||
|
||||
bus.write(0x10, 0x01);
|
||||
cpu.setAccumulator(0x03);
|
||||
cpu.step();
|
||||
assertEquals(0x01,bus.read(0x10,true));
|
||||
|
||||
}
|
||||
|
||||
public void test_TRB() throws Exception {
|
||||
makeCmosCpu();
|
||||
cpu.reset();
|
||||
bus.loadProgram(0x14, 0x40); // 65C02 TRB Zero Page $40
|
||||
|
||||
bus.write(0x0040, 0xff);
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0xfe,bus.read(0x0040,true)); // $03 &= ~($01) = $02
|
||||
assertFalse(cpu.getZeroFlag()); // Z = !(A & M)
|
||||
|
||||
cpu.reset();
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0xfe,bus.read(0x0040,true)); // $02 &= ~($01) = $02
|
||||
assertTrue(cpu.getZeroFlag()); // Z = !(A & M)
|
||||
|
||||
}
|
||||
|
||||
public void test_TRBRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
cpu.reset();
|
||||
bus.loadProgram(0x14, 0x40); // 65C02 TRB Zero Page $40
|
||||
|
||||
bus.write(0x0040, 0xff);
|
||||
cpu.setAccumulator(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x0040,true)); // $03 &= ~($01) = $02
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,585 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||
* in the 65C02 and 65816
|
||||
*/
|
||||
public class Cpu65C02ZeroPageRelativeTest extends TestCase {
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
private void makeCmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||
}
|
||||
|
||||
private void makeNmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||
}
|
||||
|
||||
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||
this.cpu = new Cpu(behavior);
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
public void test_BRA() throws Exception {
|
||||
makeCmosCpu();
|
||||
// Positive Offset
|
||||
bus.loadProgram(0x80, 0x05); // 65C02 BRA $05 ; *=$0202+$05 ($0207)
|
||||
cpu.step();
|
||||
assertEquals(0x207, cpu.getProgramCounter());
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.loadProgram(0x80, 0xfb); // 65C02 BRA $fb ; *=$0202-$05 ($01fd)
|
||||
cpu.step();
|
||||
assertEquals(0x1fd, cpu.getProgramCounter());
|
||||
}
|
||||
|
||||
public void test_BRArequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
// Should be a NOOP on NMOS and end up at $0202
|
||||
// Positive Offset
|
||||
bus.loadProgram(0x80, 0x05); // BRA $05 ; *=$0202+$05 ($0207)
|
||||
cpu.step();
|
||||
assertEquals(0x202, cpu.getProgramCounter());
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.loadProgram(0x80, 0xfb); // BRA $fb ; *=$0202-$05 ($01fd)
|
||||
cpu.step();
|
||||
assertEquals(0x202, cpu.getProgramCounter());
|
||||
}
|
||||
|
||||
public void test_BBR() throws Exception {
|
||||
makeCmosCpu();
|
||||
|
||||
/* BBR0 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x0f, 0x0a, 0x05); // 65C02 BBR0 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfe);
|
||||
bus.loadProgram(0x0f, 0x0a, 0x05); // 65C02 BBR0 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfe);
|
||||
bus.loadProgram(0x0f, 0x0a, 0xfb); // 65C02 BBR0 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBR1 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x1f, 0x0a, 0x05); // 65C02 BBR1 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfd);
|
||||
bus.loadProgram(0x1f, 0x0a, 0x05); // 65C02 BBR1 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfd);
|
||||
bus.loadProgram(0x1f, 0x0a, 0xfb); // 65C02 BBR1 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBR2 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x2f, 0x0a, 0x05); // 65C02 BBR2 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfb);
|
||||
bus.loadProgram(0x2f, 0x0a, 0x05); // 65C02 BBR2 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfb);
|
||||
bus.loadProgram(0x2f, 0x0a, 0xfb); // 65C02 BBR2 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBR3 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x3f, 0x0a, 0x05); // 65C02 BBR3 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xf7);
|
||||
bus.loadProgram(0x3f, 0x0a, 0x05); // 65C02 BBR3 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xf7);
|
||||
bus.loadProgram(0x3f, 0x0a, 0xfb); // 65C02 BBR3 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBR4 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x4f, 0x0a, 0x05); // 65C02 BBR4 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xef);
|
||||
bus.loadProgram(0x4f, 0x0a, 0x05); // 65C02 BBR4 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xef);
|
||||
bus.loadProgram(0x4f, 0x0a, 0xfb); // 65C02 BBR4 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBR5 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x5f, 0x0a, 0x05); // 65C02 BBR5 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xdf);
|
||||
bus.loadProgram(0x5f, 0x0a, 0x05); // 65C02 BBR5 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xdf);
|
||||
bus.loadProgram(0x5f, 0x0a, 0xfb); // 65C02 BBR5 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBR6 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x6f, 0x0a, 0x05); // 65C02 BBR6 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xbf);
|
||||
bus.loadProgram(0x6f, 0x0a, 0x05); // 65C02 BBR6 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xbf);
|
||||
bus.loadProgram(0x6f, 0x0a, 0xfb); // 65C02 BBR6 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBR7 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xff);
|
||||
bus.loadProgram(0x7f, 0x0a, 0x05); // 65C02 BBR7 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x7f);
|
||||
bus.loadProgram(0x7f, 0x0a, 0x05); // 65C02 BBR7 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x7f);
|
||||
bus.loadProgram(0x7f, 0x0a, 0xfb); // 65C02 BBR7 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
}
|
||||
|
||||
public void test_BBRNeedsCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
|
||||
/* BBR0 */
|
||||
bus.write(0x0a,0xfe);
|
||||
bus.loadProgram(0x0f, 0x0a, 0x05); // 65C02 BBR0 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBR1 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfd);
|
||||
bus.loadProgram(0x1f, 0x0a, 0x05); // 65C02 BBR1 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBR2 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xfb);
|
||||
bus.loadProgram(0x2f, 0x0a, 0x05); // 65C02 BBR2 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBR3 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xf7);
|
||||
bus.loadProgram(0x3f, 0x0a, 0x05); // 65C02 BBR3 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBR4 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xef);
|
||||
bus.loadProgram(0x4f, 0x0a, 0x05); // 65C02 BBR4 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBR5 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xdf);
|
||||
bus.loadProgram(0x5f, 0x0a, 0x05); // 65C02 BBR5 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBR6 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0xbf);
|
||||
bus.loadProgram(0x6f, 0x0a, 0x05); // 65C02 BBR6 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBR7 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x7f);
|
||||
bus.loadProgram(0x7f, 0x0a, 0x05); // 65C02 BBR7 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
}
|
||||
|
||||
public void test_BBS() throws Exception {
|
||||
makeCmosCpu();
|
||||
|
||||
/* BBS0 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0x8f, 0x0a, 0x05); // 65C02 BBS0 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x01);
|
||||
bus.loadProgram(0x8f, 0x0a, 0x05); // 65C02 BBS0 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x01);
|
||||
bus.loadProgram(0x8f, 0x0a, 0xfb); // 65C02 BBS0 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBS1 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0x9f, 0x00, 0x05); // 65C02 BBS1 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x02);
|
||||
bus.loadProgram(0x9f, 0x0a, 0x05); // 65C02 BBS1 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x02);
|
||||
bus.loadProgram(0x9f, 0x0a, 0xfb); // 65C02 BBS1 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBS2 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0xaf, 0x0a, 0x05); // 65C02 BBS2 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x04);
|
||||
bus.loadProgram(0xaf, 0x0a, 0x05); // 65C02 BBS2 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x04);
|
||||
bus.loadProgram(0xaf, 0x0a, 0xfb); // 65C02 BBS2 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBS3 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0xbf, 0x0a, 0x05); // 65C02 BBS3 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x08);
|
||||
bus.loadProgram(0xbf, 0x0a, 0x05); // 65C02 BBS3 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x08);
|
||||
bus.loadProgram(0xbf, 0x0a, 0xfb); // 65C02 BBS3 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBS4 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0xcf, 0x0a, 0x05); // 65C02 BBS4 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x10);
|
||||
bus.loadProgram(0xcf, 0x0a, 0x05); // 65C02 BBS4 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x10);
|
||||
bus.loadProgram(0xcf, 0x0a, 0xfb); // 65C02 BBS4 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBS5 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0xdf, 0x0a, 0x05); // 65C02 BBS5 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x20);
|
||||
bus.loadProgram(0xdf, 0x0a, 0x05); // 65C02 BBS5 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x20);
|
||||
bus.loadProgram(0xdf, 0x0a, 0xfb); // 65C02 BBS5 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBS6 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0xef, 0x0a, 0x05); // 65C02 BBS6 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x40);
|
||||
bus.loadProgram(0xef, 0x0a, 0x05); // 65C02 BBS6 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x40);
|
||||
bus.loadProgram(0xef, 0x0a, 0xfb); // 65C02 BBS6 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
/* BBS7 */
|
||||
// Positive Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x00);
|
||||
bus.loadProgram(0xff, 0x0a, 0x05); // 65C02 BBS7 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter()); // Shouldn't have branched
|
||||
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x80);
|
||||
bus.loadProgram(0xff, 0x0a, 0x05); // 65C02 BBS7 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x208, cpu.getProgramCounter()); // Should have branched
|
||||
|
||||
// Negative Offset
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x80);
|
||||
bus.loadProgram(0xff, 0x0a, 0xfb); // 65C02 BBS7 $00 $fb ; *=$0203-$05 ($01fe)
|
||||
cpu.step();
|
||||
assertEquals(0x1fe, cpu.getProgramCounter());
|
||||
|
||||
}
|
||||
|
||||
public void test_BBSNeedsCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
|
||||
/* BBS0 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x01);
|
||||
bus.loadProgram(0x8f, 0x0a, 0x05); // 65C02 BBS0 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBS1 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x02);
|
||||
bus.loadProgram(0x9f, 0x0a, 0x05); // 65C02 BBS1 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBS2 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x04);
|
||||
bus.loadProgram(0xaf, 0x0a, 0x05); // 65C02 BBS2 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBS3 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x08);
|
||||
bus.loadProgram(0xbf, 0x0a, 0x05); // 65C02 BBS3 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBS4 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x10);
|
||||
bus.loadProgram(0xcf, 0x0a, 0x05); // 65C02 BBS4 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBS5 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x20);
|
||||
bus.loadProgram(0xdf, 0x0a, 0x05); // 65C02 BBS5 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBS6 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x40);
|
||||
bus.loadProgram(0xef, 0x0a, 0x05); // 65C02 BBS6 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
/* BBS7 */
|
||||
cpu.reset();
|
||||
bus.write(0x0a,0x80);
|
||||
bus.loadProgram(0xff, 0x0a, 0x05); // 65C02 BBS7 $00 $05 ; *=$0202+$05 ($0208)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x203, cpu.getProgramCounter());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.*;
|
||||
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Test for Zero Page Indirect addressing mode, found on some instructions
|
||||
* in the 65C02 and 65816
|
||||
*/
|
||||
public class Cpu65C02ZeroPageXModeTest extends TestCase {
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
private void makeCmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
|
||||
}
|
||||
|
||||
private void makeNmosCpu() throws Exception {
|
||||
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
|
||||
}
|
||||
|
||||
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
|
||||
this.cpu = new Cpu(behavior);
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
|
||||
public void test_STZ() throws Exception {
|
||||
makeCmosCpu();
|
||||
bus.write(0x0002,0xff);
|
||||
|
||||
bus.loadProgram(0x74,0x01); // STZ Zero Page,X $01
|
||||
|
||||
// Test STZ Zero Page,X ($01+1)
|
||||
assertEquals(0xff,bus.read(0x02, true));
|
||||
cpu.setXRegister(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x00,bus.read(0x02, true));
|
||||
}
|
||||
|
||||
public void test_STZRequiresCmosCpu() throws Exception {
|
||||
makeNmosCpu();
|
||||
bus.write(0x0002,0xff);
|
||||
|
||||
bus.loadProgram(0x74,0x01); // STZ Zero Page,X $01
|
||||
|
||||
// Test STZ Zero Page,X ($01+1)
|
||||
assertEquals(0xff,bus.read(0x02, true));
|
||||
cpu.setXRegister(0x01);
|
||||
cpu.step();
|
||||
assertEquals(0xff,bus.read(0x02, true));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -121,31 +121,31 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
0x0e, 0x04, 0x12); // ASL $1204
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x7f00));
|
||||
assertEquals(0x00, bus.read(0x7f00, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x7f01));
|
||||
assertEquals(0x02, bus.read(0x7f01, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0x3502));
|
||||
assertEquals(0x04, bus.read(0x3502, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x88, bus.read(0x3503));
|
||||
assertEquals(0x88, bus.read(0x3503, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1204));
|
||||
assertEquals(0x00, bus.read(0x1204, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
@@ -165,11 +165,11 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
|
||||
// Old PC-1 should be on stack (i.e.: address of third byte of the
|
||||
// JSR instruction, 0x0204)
|
||||
assertEquals(0x02, bus.read(0x1ff));
|
||||
assertEquals(0x04, bus.read(0x1fe));
|
||||
assertEquals(0x02, bus.read(0x1ff, true));
|
||||
assertEquals(0x04, bus.read(0x1fe, true));
|
||||
|
||||
// No flags should have changed.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/* BIT - Bit Test - $2c */
|
||||
@@ -295,61 +295,61 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
0x2e, 0x01, 0x12); // ROL $1201 (m=%10000001, c=0)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1200));
|
||||
assertEquals(0x00, bus.read(0x1200, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x1201));
|
||||
assertEquals(0x02, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step(2);
|
||||
assertEquals(0x05, bus.read(0x1201));
|
||||
assertEquals(0x05, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x0a, bus.read(0x1201));
|
||||
assertEquals(0x0a, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x14, bus.read(0x1201));
|
||||
assertEquals(0x14, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x28, bus.read(0x1201));
|
||||
assertEquals(0x28, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x50, bus.read(0x1201));
|
||||
assertEquals(0x50, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xa0, bus.read(0x1201));
|
||||
assertEquals(0xa0, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x1201));
|
||||
assertEquals(0x40, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x81, bus.read(0x1201));
|
||||
assertEquals(0x81, bus.read(0x1201, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -362,7 +362,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.step();
|
||||
assertEquals(0x3400, cpu.getProgramCounter());
|
||||
// No change to status flags.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/* EOR - Exclusive OR - $4d */
|
||||
@@ -418,38 +418,38 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
0x4e, 0x05, 0x12); // LSR $1205
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1200));
|
||||
assertEquals(0x00, bus.read(0x1200, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1201));
|
||||
assertEquals(0x00, bus.read(0x1201, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x1202));
|
||||
assertEquals(0x01, bus.read(0x1202, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x22, bus.read(0x1203));
|
||||
assertEquals(0x22, bus.read(0x1203, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x1204));
|
||||
assertEquals(0x40, bus.read(0x1204, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
// Setting Carry should not affect the result.
|
||||
cpu.step(2);
|
||||
assertEquals(0x01, bus.read(0x1205));
|
||||
assertEquals(0x01, bus.read(0x1205, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -654,61 +654,61 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
0x6e, 0x11, 0x12); // ROR $1201 (m=%00010000, c=0)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1210));
|
||||
assertEquals(0x00, bus.read(0x1210, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x08, bus.read(0x1211));
|
||||
assertEquals(0x08, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0x1211));
|
||||
assertEquals(0x04, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x1211));
|
||||
assertEquals(0x02, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x1211));
|
||||
assertEquals(0x01, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1211));
|
||||
assertEquals(0x00, bus.read(0x1211, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x1211));
|
||||
assertEquals(0x80, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x1211));
|
||||
assertEquals(0x40, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x20, bus.read(0x1211));
|
||||
assertEquals(0x20, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x10, bus.read(0x1211));
|
||||
assertEquals(0x10, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -720,7 +720,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setYRegister(0x00);
|
||||
bus.loadProgram(0x8c, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1210));
|
||||
assertEquals(0x00, bus.read(0x1210, true));
|
||||
// STY should have NO effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -730,7 +730,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setYRegister(0x0f);
|
||||
bus.loadProgram(0x8c, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x1210));
|
||||
assertEquals(0x0f, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -739,7 +739,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setYRegister(0x80);
|
||||
bus.loadProgram(0x8c, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x1210));
|
||||
assertEquals(0x80, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -750,7 +750,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x00);
|
||||
bus.loadProgram(0x8d, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1210));
|
||||
assertEquals(0x00, bus.read(0x1210, true));
|
||||
// STA should have NO effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -760,7 +760,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x0f);
|
||||
bus.loadProgram(0x8d, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x1210));
|
||||
assertEquals(0x0f, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -769,7 +769,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x80);
|
||||
bus.loadProgram(0x8d, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x1210));
|
||||
assertEquals(0x80, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -780,7 +780,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setXRegister(0x00);
|
||||
bus.loadProgram(0x8e, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1210));
|
||||
assertEquals(0x00, bus.read(0x1210, true));
|
||||
// STX should have NO effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -790,7 +790,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setXRegister(0x0f);
|
||||
bus.loadProgram(0x8e, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x1210));
|
||||
assertEquals(0x0f, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -799,7 +799,7 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
cpu.setXRegister(0x80);
|
||||
bus.loadProgram(0x8e, 0x10, 0x12);
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x1210));
|
||||
assertEquals(0x80, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -957,22 +957,22 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
0xce, 0x13, 0x12); // DEC $1213
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xff, bus.read(0x1210));
|
||||
assertEquals(0xff, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1211));
|
||||
assertEquals(0x00, bus.read(0x1211, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x7f, bus.read(0x1212));
|
||||
assertEquals(0x7f, bus.read(0x1212, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xfe, bus.read(0x1213));
|
||||
assertEquals(0xfe, bus.read(0x1213, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -1213,17 +1213,17 @@ public class CpuAbsoluteModeTest extends TestCase {
|
||||
0xee, 0x12, 0x12); // INC $1212
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x1210));
|
||||
assertEquals(0x01, bus.read(0x1210, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x1211));
|
||||
assertEquals(0x80, bus.read(0x1211, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1212));
|
||||
assertEquals(0x00, bus.read(0x1212, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -117,31 +117,31 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
0x1e, 0x04, 0x2c); // ASL $2c04,X
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x2c30));
|
||||
assertEquals(0x00, bus.read(0x2c30, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x2c31));
|
||||
assertEquals(0x02, bus.read(0x2c31, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0x2c32));
|
||||
assertEquals(0x04, bus.read(0x2c32, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x88, bus.read(0x2c33));
|
||||
assertEquals(0x88, bus.read(0x2c33, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x2c34));
|
||||
assertEquals(0x00, bus.read(0x2c34, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
@@ -229,61 +229,61 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
0x3e, 0x01, 0x10); // ROL $1001,X (m=%10000001, c=0)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x1070));
|
||||
assertEquals(0x00, bus.read(0x1070, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x1071));
|
||||
assertEquals(0x02, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step(2);
|
||||
assertEquals(0x05, bus.read(0x1071));
|
||||
assertEquals(0x05, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x0a, bus.read(0x1071));
|
||||
assertEquals(0x0a, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x14, bus.read(0x1071));
|
||||
assertEquals(0x14, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x28, bus.read(0x1071));
|
||||
assertEquals(0x28, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x50, bus.read(0x1071));
|
||||
assertEquals(0x50, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xa0, bus.read(0x1071));
|
||||
assertEquals(0xa0, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x1071));
|
||||
assertEquals(0x40, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x81, bus.read(0x1071));
|
||||
assertEquals(0x81, bus.read(0x1071, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -346,38 +346,38 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
0x5e, 0x05, 0xab); // LSR $ab05,X
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab30));
|
||||
assertEquals(0x00, bus.read(0xab30, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab31));
|
||||
assertEquals(0x00, bus.read(0xab31, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0xab32));
|
||||
assertEquals(0x01, bus.read(0xab32, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x22, bus.read(0xab33));
|
||||
assertEquals(0x22, bus.read(0xab33, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0xab34));
|
||||
assertEquals(0x40, bus.read(0xab34, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
// Setting Carry should not affect the result.
|
||||
cpu.step(2);
|
||||
assertEquals(0x01, bus.read(0xab35));
|
||||
assertEquals(0x01, bus.read(0xab35, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -609,61 +609,61 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
cpu.setXRegister(0x30);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab40));
|
||||
assertEquals(0x00, bus.read(0xab40, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x08, bus.read(0xab41));
|
||||
assertEquals(0x08, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0xab41));
|
||||
assertEquals(0x04, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0xab41));
|
||||
assertEquals(0x02, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0xab41));
|
||||
assertEquals(0x01, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab41));
|
||||
assertEquals(0x00, bus.read(0xab41, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0xab41));
|
||||
assertEquals(0x80, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0xab41));
|
||||
assertEquals(0x40, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x20, bus.read(0xab41));
|
||||
assertEquals(0x20, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x10, bus.read(0xab41));
|
||||
assertEquals(0x10, bus.read(0xab41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -677,7 +677,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x00);
|
||||
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab40));
|
||||
assertEquals(0x00, bus.read(0xab40, true));
|
||||
// STA should have NO affect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -688,7 +688,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x0f);
|
||||
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0xab40));
|
||||
assertEquals(0x0f, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -698,7 +698,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x80);
|
||||
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0xab40));
|
||||
assertEquals(0x80, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -808,22 +808,22 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
cpu.setXRegister(0x30);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xff, bus.read(0xab40));
|
||||
assertEquals(0xff, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab41));
|
||||
assertEquals(0x00, bus.read(0xab41, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x7f, bus.read(0xab42));
|
||||
assertEquals(0x7f, bus.read(0xab42, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xfe, bus.read(0xab43));
|
||||
assertEquals(0xfe, bus.read(0xab43, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -1051,17 +1051,17 @@ public class CpuAbsoluteXModeTest extends TestCase {
|
||||
0xfe, 0x12, 0xab); // INC $ab12,X
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0xab30));
|
||||
assertEquals(0x01, bus.read(0xab30, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0xab31));
|
||||
assertEquals(0x80, bus.read(0xab31, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab32));
|
||||
assertEquals(0x00, bus.read(0xab32, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -411,7 +411,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x00);
|
||||
bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab40));
|
||||
assertEquals(0x00, bus.read(0xab40, true));
|
||||
// STA should have NO effect on flags
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -423,7 +423,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x0f);
|
||||
bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0xab40));
|
||||
assertEquals(0x0f, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -433,7 +433,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x80);
|
||||
bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0xab40));
|
||||
assertEquals(0x80, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public class CpuAccumulatorModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -306,4 +306,4 @@ public class CpuAccumulatorModeTest extends TestCase {
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public class CpuImmediateModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -33,7 +33,7 @@ public class CpuImpliedModeTest {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
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 {
|
||||
cpu.setCarryFlag();
|
||||
cpu.setOverflowFlag();
|
||||
cpu.clearIrqDisableFlag();
|
||||
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW,
|
||||
cpu.getProcessorStatus());
|
||||
assertEquals(0x00, cpu.stackPeek());
|
||||
@@ -101,10 +102,10 @@ public class CpuImpliedModeTest {
|
||||
cpu.step(); // Triggers the BRK
|
||||
|
||||
// Was at PC = 0x204. PC+1 should now be on the stack
|
||||
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
|
||||
assertEquals(0x05, bus.read(0x1fe)); // PC low byte
|
||||
assertEquals(0x02, bus.read(0x1ff, true)); // PC high byte
|
||||
assertEquals(0x05, bus.read(0x1fe, true)); // PC low byte
|
||||
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK,
|
||||
bus.read(0x1fd)); // Processor Status, with B set
|
||||
bus.read(0x1fd, true)); // Processor Status, with B set
|
||||
|
||||
// Interrupt vector held 0x1234, so we should be there.
|
||||
assertEquals(0x1234, cpu.getProgramCounter());
|
||||
@@ -149,10 +150,10 @@ public class CpuImpliedModeTest {
|
||||
|
||||
|
||||
// Was at PC = 0x204. PC+1 should now be on the stack
|
||||
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
|
||||
assertEquals(0x05, bus.read(0x1fe)); // PC low byte
|
||||
assertEquals(0x02, bus.read(0x1ff, true)); // PC high byte
|
||||
assertEquals(0x05, bus.read(0x1fe, true)); // PC low byte
|
||||
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_IRQ_DISABLE,
|
||||
bus.read(0x1fd)); // Processor Status, with B set
|
||||
bus.read(0x1fd, true)); // Processor Status, with B set
|
||||
|
||||
// Interrupt vector held 0x1234, so we should be there.
|
||||
assertEquals(0x1234, cpu.getProgramCounter());
|
||||
@@ -343,7 +344,7 @@ public class CpuImpliedModeTest {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x201, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/* PHA - Push Accumulator - $48 */
|
||||
@@ -432,7 +433,7 @@ public class CpuImpliedModeTest {
|
||||
cpu.step();
|
||||
|
||||
assertEquals(0x0f12, cpu.getProgramCounter());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/* SEC - Set Carry Flag - $38 */
|
||||
@@ -641,4 +642,4 @@ public class CpuImpliedModeTest {
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public class CpuIndexedIndirectModeTest {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -87,7 +87,7 @@ public class CpuIndexedIndirectModeTest {
|
||||
cpu.step(1);
|
||||
|
||||
assertEquals(0x35, cpu.getAccumulator());
|
||||
assertEquals(0x31, bus.read(0xc51f));
|
||||
assertEquals(0x31, bus.read(0xc51f, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -103,6 +103,6 @@ public class CpuIndexedIndirectModeTest {
|
||||
cpu.step(1);
|
||||
|
||||
assertEquals(0x11, cpu.getAccumulator());
|
||||
assertEquals(0x31, bus.read(0xc51f));
|
||||
assertEquals(0x31, bus.read(0xc51f, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,18 @@ public class CpuIndirectIndexedModeTest {
|
||||
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
|
||||
|
||||
cpu.reset();
|
||||
// Assert initial state
|
||||
assertEquals(0, cpu.getAccumulator());
|
||||
assertEquals(0, cpu.getXRegister());
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LDA() throws Exception {
|
||||
assertEquals(cpu.toString(), 0x00, cpu.getAccumulator());
|
||||
bus.write(0x0014, 0x00);
|
||||
bus.write(0x0015, 0xd8);
|
||||
bus.write(0xd828, 0x03);
|
||||
@@ -54,7 +62,7 @@ public class CpuIndirectIndexedModeTest {
|
||||
cpu.step(1);
|
||||
|
||||
assertEquals(0xf3, cpu.getAccumulator());
|
||||
assertEquals(0xe3, bus.read(0xd828));
|
||||
assertEquals(0xe3, bus.read(0xd828, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -70,7 +78,7 @@ public class CpuIndirectIndexedModeTest {
|
||||
cpu.step(1);
|
||||
|
||||
assertEquals(0x22, cpu.getAccumulator());
|
||||
assertEquals(0xe3, bus.read(0xd828));
|
||||
assertEquals(0xe3, bus.read(0xd828, true));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,19 +3,15 @@ package com.loomcom.symon;
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class CpuIndirectModeTest {
|
||||
public class CpuIndirectModeTest extends TestCase {
|
||||
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
protected void setUp() throws Exception {
|
||||
this.cpu = new Cpu();
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
@@ -33,7 +29,7 @@ public class CpuIndirectModeTest {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -45,7 +41,6 @@ public class CpuIndirectModeTest {
|
||||
|
||||
/* JMP - Jump - $6c */
|
||||
|
||||
@Test
|
||||
public void test_JMP_notOnPageBoundary() throws MemoryAccessException {
|
||||
bus.write(0x3400, 0x00);
|
||||
bus.write(0x3401, 0x54);
|
||||
@@ -53,10 +48,9 @@ public class CpuIndirectModeTest {
|
||||
cpu.step();
|
||||
assertEquals(0x5400, cpu.getProgramCounter());
|
||||
// No change to status flags.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
|
||||
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG);
|
||||
bus.write(0x3400, 0x22);
|
||||
@@ -66,10 +60,9 @@ public class CpuIndirectModeTest {
|
||||
cpu.step();
|
||||
assertEquals(0x2200, cpu.getProgramCounter());
|
||||
// No change to status flags.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_JMP_withIndirectBug() throws MemoryAccessException {
|
||||
cpu.setBehavior(Cpu.CpuBehavior.NMOS_6502);
|
||||
bus.write(0x3400, 0x22);
|
||||
@@ -79,10 +72,9 @@ public class CpuIndirectModeTest {
|
||||
cpu.step();
|
||||
assertEquals(0x2200, cpu.getProgramCounter());
|
||||
// No change to status flags.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
|
||||
cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
|
||||
bus.write(0x3400, 0x22);
|
||||
@@ -92,10 +84,9 @@ public class CpuIndirectModeTest {
|
||||
cpu.step();
|
||||
assertEquals(0x5400, cpu.getProgramCounter());
|
||||
// No change to status flags.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_JMP_cmos() throws MemoryAccessException {
|
||||
cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
|
||||
bus.write(0x3400, 0x22);
|
||||
@@ -105,7 +96,7 @@ public class CpuIndirectModeTest {
|
||||
cpu.step();
|
||||
assertEquals(0x5400, cpu.getProgramCounter());
|
||||
// No change to status flags.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,21 +3,14 @@ package com.loomcom.symon;
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CpuIndirectXModeTest {
|
||||
public class CpuIndirectXModeTest extends TestCase {
|
||||
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
protected void setUp() throws Exception {
|
||||
this.cpu = new Cpu();
|
||||
this.bus = new Bus(0x0000, 0xffff);
|
||||
this.mem = new Memory(0x0000, 0xffff);
|
||||
@@ -35,7 +28,7 @@ public class CpuIndirectXModeTest {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -54,7 +47,7 @@ public class CpuIndirectXModeTest {
|
||||
*/
|
||||
|
||||
/* ORA - Logical Inclusive OR - $1d */
|
||||
@Test
|
||||
|
||||
public void test_ORA() throws MemoryAccessException {
|
||||
// Set some initial values in memory
|
||||
bus.write(0x2c30, 0x00);
|
||||
@@ -100,7 +93,7 @@ public class CpuIndirectXModeTest {
|
||||
}
|
||||
|
||||
/* AND - Logical AND - $3d */
|
||||
@Test
|
||||
|
||||
public void test_AND() throws MemoryAccessException {
|
||||
bus.write(0x1a30, 0x00);
|
||||
bus.write(0x1a31, 0x11);
|
||||
@@ -160,7 +153,7 @@ public class CpuIndirectXModeTest {
|
||||
}
|
||||
|
||||
/* EOR - Exclusive OR - $5d */
|
||||
@Test
|
||||
|
||||
public void test_EOR() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x00);
|
||||
bus.write(0xab41, 0xff);
|
||||
@@ -196,7 +189,7 @@ public class CpuIndirectXModeTest {
|
||||
}
|
||||
|
||||
/* ADC - Add with Carry - $7d */
|
||||
@Test
|
||||
|
||||
public void test_ADC() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x01);
|
||||
bus.write(0xab41, 0xff);
|
||||
@@ -297,7 +290,6 @@ public class CpuIndirectXModeTest {
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ADC_IncludesCarry() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x01);
|
||||
|
||||
@@ -315,7 +307,6 @@ public class CpuIndirectXModeTest {
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_ADC_DecimalMode() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x01);
|
||||
bus.write(0xab41, 0x99);
|
||||
@@ -413,14 +404,14 @@ public class CpuIndirectXModeTest {
|
||||
}
|
||||
|
||||
/* STA - Store Accumulator - $9d */
|
||||
@Test
|
||||
|
||||
public void test_STA() throws MemoryAccessException {
|
||||
cpu.setXRegister(0x30);
|
||||
|
||||
cpu.setAccumulator(0x00);
|
||||
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0xab40));
|
||||
assertEquals(0x00, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -431,7 +422,7 @@ public class CpuIndirectXModeTest {
|
||||
cpu.setAccumulator(0x0f);
|
||||
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0xab40));
|
||||
assertEquals(0x0f, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -441,13 +432,13 @@ public class CpuIndirectXModeTest {
|
||||
cpu.setAccumulator(0x80);
|
||||
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0xab40));
|
||||
assertEquals(0x80, bus.read(0xab40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
/* LDA - Load Accumulator - $bd */
|
||||
@Test
|
||||
|
||||
public void test_LDA() throws MemoryAccessException {
|
||||
bus.write(0xab42, 0x00);
|
||||
bus.write(0xab43, 0x0f);
|
||||
@@ -476,7 +467,7 @@ public class CpuIndirectXModeTest {
|
||||
}
|
||||
|
||||
/* CMP - Compare Accumulator - $dd */
|
||||
@Test
|
||||
|
||||
public void test_CMP() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x00);
|
||||
bus.write(0xab41, 0x80);
|
||||
@@ -507,7 +498,7 @@ public class CpuIndirectXModeTest {
|
||||
}
|
||||
|
||||
/* SBC - Subtract with Carry - $fd */
|
||||
@Test
|
||||
|
||||
public void test_SBC() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x01);
|
||||
|
||||
@@ -566,7 +557,6 @@ public class CpuIndirectXModeTest {
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x01);
|
||||
|
||||
@@ -612,7 +602,6 @@ public class CpuIndirectXModeTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_SBC_DecimalMode() throws MemoryAccessException {
|
||||
bus.write(0xab40, 0x01);
|
||||
bus.write(0xab50, 0x11);
|
||||
|
||||
@@ -29,7 +29,7 @@ public class CpuRelativeModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -44,7 +44,7 @@ public class CpuTest extends TestCase {
|
||||
assertEquals(0x0200, cpu.getProgramCounter());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertTrue(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
@@ -71,61 +71,61 @@ public class CpuTest extends TestCase {
|
||||
|
||||
public void testStackPush() throws MemoryAccessException {
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x00, bus.read(0x1ff));
|
||||
assertEquals(0x00, bus.read(0x1ff, true));
|
||||
|
||||
cpu.stackPush(0x06);
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x06, bus.read(0x1ff, true));
|
||||
|
||||
cpu.stackPush(0x05);
|
||||
assertEquals(0xfd, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x05, bus.read(0x1fe));
|
||||
assertEquals(0x06, bus.read(0x1ff, true));
|
||||
assertEquals(0x05, bus.read(0x1fe, true));
|
||||
|
||||
cpu.stackPush(0x04);
|
||||
assertEquals(0xfc, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x05, bus.read(0x1fe));
|
||||
assertEquals(0x04, bus.read(0x1fd));
|
||||
assertEquals(0x06, bus.read(0x1ff, true));
|
||||
assertEquals(0x05, bus.read(0x1fe, true));
|
||||
assertEquals(0x04, bus.read(0x1fd, true));
|
||||
|
||||
cpu.stackPush(0x03);
|
||||
assertEquals(0xfb, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x05, bus.read(0x1fe));
|
||||
assertEquals(0x04, bus.read(0x1fd));
|
||||
assertEquals(0x03, bus.read(0x1fc));
|
||||
assertEquals(0x06, bus.read(0x1ff, true));
|
||||
assertEquals(0x05, bus.read(0x1fe, true));
|
||||
assertEquals(0x04, bus.read(0x1fd, true));
|
||||
assertEquals(0x03, bus.read(0x1fc, true));
|
||||
|
||||
cpu.stackPush(0x02);
|
||||
assertEquals(0xfa, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x05, bus.read(0x1fe));
|
||||
assertEquals(0x04, bus.read(0x1fd));
|
||||
assertEquals(0x03, bus.read(0x1fc));
|
||||
assertEquals(0x02, bus.read(0x1fb));
|
||||
assertEquals(0x06, bus.read(0x1ff, true));
|
||||
assertEquals(0x05, bus.read(0x1fe, true));
|
||||
assertEquals(0x04, bus.read(0x1fd, true));
|
||||
assertEquals(0x03, bus.read(0x1fc, true));
|
||||
assertEquals(0x02, bus.read(0x1fb, true));
|
||||
|
||||
cpu.stackPush(0x01);
|
||||
assertEquals(0xf9, cpu.getStackPointer());
|
||||
assertEquals(0x06, bus.read(0x1ff));
|
||||
assertEquals(0x05, bus.read(0x1fe));
|
||||
assertEquals(0x04, bus.read(0x1fd));
|
||||
assertEquals(0x03, bus.read(0x1fc));
|
||||
assertEquals(0x02, bus.read(0x1fb));
|
||||
assertEquals(0x01, bus.read(0x1fa));
|
||||
assertEquals(0x06, bus.read(0x1ff, true));
|
||||
assertEquals(0x05, bus.read(0x1fe, true));
|
||||
assertEquals(0x04, bus.read(0x1fd, true));
|
||||
assertEquals(0x03, bus.read(0x1fc, true));
|
||||
assertEquals(0x02, bus.read(0x1fb, true));
|
||||
assertEquals(0x01, bus.read(0x1fa, true));
|
||||
}
|
||||
|
||||
public void testStackPushWrapsAroundToStackTop() throws MemoryAccessException {
|
||||
cpu.setStackPointer(0x01);
|
||||
|
||||
cpu.stackPush(0x01);
|
||||
assertEquals(0x01, bus.read(0x101));
|
||||
assertEquals(0x01, bus.read(0x101, true));
|
||||
assertEquals(0x00, cpu.getStackPointer());
|
||||
|
||||
cpu.stackPush(0x02);
|
||||
assertEquals(0x02, bus.read(0x100));
|
||||
assertEquals(0x02, bus.read(0x100, true));
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
|
||||
cpu.stackPush(0x03);
|
||||
assertEquals(0x03, bus.read(0x1ff));
|
||||
assertEquals(0x03, bus.read(0x1ff, true));
|
||||
assertEquals(0xfe, cpu.getStackPointer());
|
||||
}
|
||||
|
||||
@@ -205,14 +205,12 @@ public class CpuTest extends TestCase {
|
||||
}
|
||||
|
||||
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'.
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
cpu.setCarryFlag();
|
||||
assertEquals(0x21, cpu.getProcessorStatus());
|
||||
assertEquals(0x25, cpu.getProcessorStatus());
|
||||
cpu.setZeroFlag();
|
||||
assertEquals(0x23, cpu.getProcessorStatus());
|
||||
cpu.setIrqDisableFlag();
|
||||
assertEquals(0x27, cpu.getProcessorStatus());
|
||||
cpu.setDecimalModeFlag();
|
||||
assertEquals(0x2f, cpu.getProcessorStatus());
|
||||
@@ -237,14 +235,16 @@ public class CpuTest extends TestCase {
|
||||
assertEquals(0xa0, cpu.getProcessorStatus());
|
||||
cpu.clearNegativeFlag();
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
|
||||
cpu.setIrqDisableFlag();
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
public void testSetProcessorStatus() {
|
||||
// Default
|
||||
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getIrqDisableFlag());
|
||||
assertTrue(cpu.getIrqDisableFlag());
|
||||
assertFalse(cpu.getDecimalModeFlag());
|
||||
assertFalse(cpu.getBreakFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
@@ -670,4 +670,78 @@ public class CpuTest extends TestCase {
|
||||
cpu.step();
|
||||
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(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -119,31 +119,31 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
0x06, 0x04);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x0000));
|
||||
assertEquals(0x00, bus.read(0x0000, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x0001));
|
||||
assertEquals(0x02, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0x0002));
|
||||
assertEquals(0x04, bus.read(0x0002, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x88, bus.read(0x0003));
|
||||
assertEquals(0x88, bus.read(0x0003, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x0004));
|
||||
assertEquals(0x00, bus.read(0x0004, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
@@ -274,61 +274,61 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
0x26, 0x01); // ROL $01 (m=%10000001, c=0)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x0000));
|
||||
assertEquals(0x00, bus.read(0x0000, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x0001));
|
||||
assertEquals(0x02, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step(2);
|
||||
assertEquals(0x05, bus.read(0x0001));
|
||||
assertEquals(0x05, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x0a, bus.read(0x0001));
|
||||
assertEquals(0x0a, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x14, bus.read(0x0001));
|
||||
assertEquals(0x14, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x28, bus.read(0x0001));
|
||||
assertEquals(0x28, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x50, bus.read(0x0001));
|
||||
assertEquals(0x50, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xa0, bus.read(0x0001));
|
||||
assertEquals(0xa0, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x0001));
|
||||
assertEquals(0x40, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x81, bus.read(0x0001));
|
||||
assertEquals(0x81, bus.read(0x0001, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -387,38 +387,38 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
0x46, 0x05); // LSR $05
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x0000));
|
||||
assertEquals(0x00, bus.read(0x0000, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x0001));
|
||||
assertEquals(0x00, bus.read(0x0001, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x0002));
|
||||
assertEquals(0x01, bus.read(0x0002, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x22, bus.read(0x0003));
|
||||
assertEquals(0x22, bus.read(0x0003, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x0004));
|
||||
assertEquals(0x40, bus.read(0x0004, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
// Setting Carry should not affect the result.
|
||||
cpu.step(2);
|
||||
assertEquals(0x01, bus.read(0x0005));
|
||||
assertEquals(0x01, bus.read(0x0005, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -623,61 +623,61 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
0x66, 0x11); // ROR $01 (m=%00010000, c=0)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x10));
|
||||
assertEquals(0x00, bus.read(0x10, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x08, bus.read(0x11));
|
||||
assertEquals(0x08, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0x11));
|
||||
assertEquals(0x04, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x11));
|
||||
assertEquals(0x02, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x11));
|
||||
assertEquals(0x01, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x11));
|
||||
assertEquals(0x00, bus.read(0x11, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x11));
|
||||
assertEquals(0x80, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x11));
|
||||
assertEquals(0x40, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x20, bus.read(0x11));
|
||||
assertEquals(0x20, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x10, bus.read(0x11));
|
||||
assertEquals(0x10, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -689,7 +689,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setYRegister(0x00);
|
||||
bus.loadProgram(0x84, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x10));
|
||||
assertEquals(0x00, bus.read(0x10, true));
|
||||
// Should have no effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -699,7 +699,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setYRegister(0x0f);
|
||||
bus.loadProgram(0x84, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x10));
|
||||
assertEquals(0x0f, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -708,7 +708,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setYRegister(0x80);
|
||||
bus.loadProgram(0x84, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x10));
|
||||
assertEquals(0x80, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -719,7 +719,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x00);
|
||||
bus.loadProgram(0x85, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x10));
|
||||
assertEquals(0x00, bus.read(0x10, true));
|
||||
// Should have no effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -729,7 +729,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x0f);
|
||||
bus.loadProgram(0x85, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x10));
|
||||
assertEquals(0x0f, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -738,7 +738,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x80);
|
||||
bus.loadProgram(0x85, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x10));
|
||||
assertEquals(0x80, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -749,7 +749,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setXRegister(0x00);
|
||||
bus.loadProgram(0x86, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x10));
|
||||
assertEquals(0x00, bus.read(0x10, true));
|
||||
// Should have no effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -759,7 +759,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setXRegister(0x0f);
|
||||
bus.loadProgram(0x86, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x10));
|
||||
assertEquals(0x0f, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -768,7 +768,7 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
cpu.setXRegister(0x80);
|
||||
bus.loadProgram(0x86, 0x10);
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x10));
|
||||
assertEquals(0x80, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -926,22 +926,22 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
0xc6, 0x13); // DEC $13
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xff, bus.read(0x10));
|
||||
assertEquals(0xff, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x11));
|
||||
assertEquals(0x00, bus.read(0x11, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x7f, bus.read(0x12));
|
||||
assertEquals(0x7f, bus.read(0x12, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xfe, bus.read(0x13));
|
||||
assertEquals(0xfe, bus.read(0x13, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -1182,17 +1182,17 @@ public class CpuZeroPageModeTest extends TestCase {
|
||||
0xe6, 0x12); // INC $12
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x10));
|
||||
assertEquals(0x01, bus.read(0x10, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x11));
|
||||
assertEquals(0x80, bus.read(0x11, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x12));
|
||||
assertEquals(0x00, bus.read(0x12, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -131,38 +131,38 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
0x16, 0xd2); // ASL $d2,X
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x30));
|
||||
assertEquals(0x00, bus.read(0x30, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x31));
|
||||
assertEquals(0x02, bus.read(0x31, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0x32));
|
||||
assertEquals(0x04, bus.read(0x32, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x88, bus.read(0x33));
|
||||
assertEquals(0x88, bus.read(0x33, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x34));
|
||||
assertEquals(0x00, bus.read(0x34, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
// Should wrap around, d2 + 30 = 02
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x02));
|
||||
assertEquals(0x02, bus.read(0x02, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -251,61 +251,61 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
0x36, 0x01); // ROL $01,X (m=%10000001, c=0)
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x70));
|
||||
assertEquals(0x00, bus.read(0x70, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x71));
|
||||
assertEquals(0x02, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step(2);
|
||||
assertEquals(0x05, bus.read(0x71));
|
||||
assertEquals(0x05, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x0a, bus.read(0x71));
|
||||
assertEquals(0x0a, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x14, bus.read(0x71));
|
||||
assertEquals(0x14, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x28, bus.read(0x71));
|
||||
assertEquals(0x28, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x50, bus.read(0x71));
|
||||
assertEquals(0x50, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xa0, bus.read(0x71));
|
||||
assertEquals(0xa0, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x71));
|
||||
assertEquals(0x40, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x81, bus.read(0x71));
|
||||
assertEquals(0x81, bus.read(0x71, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -368,38 +368,38 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
0x56, 0x05); // LSR $05,X
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x30));
|
||||
assertEquals(0x00, bus.read(0x30, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x31));
|
||||
assertEquals(0x00, bus.read(0x31, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x32));
|
||||
assertEquals(0x01, bus.read(0x32, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x22, bus.read(0x33));
|
||||
assertEquals(0x22, bus.read(0x33, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x34));
|
||||
assertEquals(0x40, bus.read(0x34, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
// Setting Carry should not affect the result.
|
||||
cpu.step(2);
|
||||
assertEquals(0x01, bus.read(0x35));
|
||||
assertEquals(0x01, bus.read(0x35, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -641,61 +641,61 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setXRegister(0x30);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x40));
|
||||
assertEquals(0x00, bus.read(0x40, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x08, bus.read(0x41));
|
||||
assertEquals(0x08, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x04, bus.read(0x41));
|
||||
assertEquals(0x04, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x02, bus.read(0x41));
|
||||
assertEquals(0x02, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x41));
|
||||
assertEquals(0x01, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x41));
|
||||
assertEquals(0x00, bus.read(0x41, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x41));
|
||||
assertEquals(0x80, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x40, bus.read(0x41));
|
||||
assertEquals(0x40, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x20, bus.read(0x41));
|
||||
assertEquals(0x20, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x10, bus.read(0x41));
|
||||
assertEquals(0x10, bus.read(0x41, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
@@ -708,7 +708,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setYRegister(0x00);
|
||||
bus.loadProgram(0x94, 0x10); // STY $10,X
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x40));
|
||||
assertEquals(0x00, bus.read(0x40, true));
|
||||
// Should have no effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -719,7 +719,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setYRegister(0x0f);
|
||||
bus.loadProgram(0x94, 0x10); // STY $10,X
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x40));
|
||||
assertEquals(0x0f, bus.read(0x40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -729,7 +729,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setYRegister(0x80);
|
||||
bus.loadProgram(0x94, 0x10); // STY $10,X
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x40));
|
||||
assertEquals(0x80, bus.read(0x40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -742,7 +742,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x00);
|
||||
bus.loadProgram(0x95, 0x10); // STA $10,X
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x40));
|
||||
assertEquals(0x00, bus.read(0x40, true));
|
||||
// Should have no effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -753,7 +753,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x0f);
|
||||
bus.loadProgram(0x95, 0x10); // STA $10,X
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x40));
|
||||
assertEquals(0x0f, bus.read(0x40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -763,7 +763,7 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setAccumulator(0x80);
|
||||
bus.loadProgram(0x95, 0x10); // STA $10,X
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x40));
|
||||
assertEquals(0x80, bus.read(0x40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -873,22 +873,22 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
cpu.setXRegister(0x30);
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xff, bus.read(0x40));
|
||||
assertEquals(0xff, bus.read(0x40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x41));
|
||||
assertEquals(0x00, bus.read(0x41, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x7f, bus.read(0x42));
|
||||
assertEquals(0x7f, bus.read(0x42, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0xfe, bus.read(0x43));
|
||||
assertEquals(0xfe, bus.read(0x43, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
@@ -1116,17 +1116,17 @@ public class CpuZeroPageXModeTest extends TestCase {
|
||||
0xf6, 0x12); // INC $12,X
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x01, bus.read(0x30));
|
||||
assertEquals(0x01, bus.read(0x30, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x31));
|
||||
assertEquals(0x80, bus.read(0x31, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x32));
|
||||
assertEquals(0x00, bus.read(0x32, true));
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class CpuZeroPageYModeTest extends TestCase {
|
||||
assertEquals(0, cpu.getYRegister());
|
||||
assertEquals(0x200, cpu.getProgramCounter());
|
||||
assertEquals(0xff, cpu.getStackPointer());
|
||||
assertEquals(0x20, cpu.getProcessorStatus());
|
||||
assertEquals(0x24, cpu.getProcessorStatus());
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -47,7 +47,7 @@ public class CpuZeroPageYModeTest extends TestCase {
|
||||
cpu.setXRegister(0x00);
|
||||
bus.loadProgram(0x96, 0x10); // STX $10,Y
|
||||
cpu.step();
|
||||
assertEquals(0x00, bus.read(0x40));
|
||||
assertEquals(0x00, bus.read(0x40, true));
|
||||
// Should have no effect on flags.
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
@@ -57,7 +57,7 @@ public class CpuZeroPageYModeTest extends TestCase {
|
||||
cpu.setXRegister(0x0f);
|
||||
bus.loadProgram(0x96, 0x10); // STX $10,Y
|
||||
cpu.step();
|
||||
assertEquals(0x0f, bus.read(0x40));
|
||||
assertEquals(0x0f, bus.read(0x40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
|
||||
@@ -66,7 +66,7 @@ public class CpuZeroPageYModeTest extends TestCase {
|
||||
cpu.setXRegister(0x80);
|
||||
bus.loadProgram(0x96, 0x10); // STX $10,Y
|
||||
cpu.step();
|
||||
assertEquals(0x80, bus.read(0x40));
|
||||
assertEquals(0x80, bus.read(0x40, true));
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
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.assertFalse;
|
||||
@@ -242,19 +242,19 @@ public class CrtcTest {
|
||||
crtc.write(0, 12); // High byte
|
||||
|
||||
crtc.write(1, 0x03);
|
||||
assertEquals(0, crtc.read(1));
|
||||
assertEquals(0, crtc.read(1, true));
|
||||
|
||||
crtc.write(1, 0x70);
|
||||
assertEquals(0, crtc.read(1));
|
||||
assertEquals(0, crtc.read(1, true));
|
||||
|
||||
|
||||
crtc.write(0, 13); // Low byte
|
||||
|
||||
crtc.write(1, 0xff);
|
||||
assertEquals(0, crtc.read(1));
|
||||
assertEquals(0, crtc.read(1, true));
|
||||
|
||||
crtc.write(1, 0x0e);
|
||||
assertEquals(0, crtc.read(1));
|
||||
assertEquals(0, crtc.read(1, true));
|
||||
}
|
||||
|
||||
|
||||
@@ -278,10 +278,10 @@ public class CrtcTest {
|
||||
crtc.write(0, 14);
|
||||
|
||||
crtc.write(1, 0x3f);
|
||||
assertEquals(0x3f, crtc.read(1));
|
||||
assertEquals(0x3f, crtc.read(1, true));
|
||||
|
||||
crtc.write(1, 0x70);
|
||||
assertEquals(0x70, crtc.read(1));
|
||||
assertEquals(0x70, crtc.read(1, true));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -313,13 +313,13 @@ public class CrtcTest {
|
||||
crtc.write(0, 15);
|
||||
|
||||
crtc.write(1, 0x00);
|
||||
assertEquals(0x00, crtc.read(1));
|
||||
assertEquals(0x00, crtc.read(1, true));
|
||||
|
||||
crtc.write(1, 0x1f);
|
||||
assertEquals(0x1f, crtc.read(1));
|
||||
assertEquals(0x1f, crtc.read(1, true));
|
||||
|
||||
crtc.write(1, 0xff);
|
||||
assertEquals(0xff, crtc.read(1));
|
||||
assertEquals(0xff, crtc.read(1, true));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.loomcom.symon.jterminal.vt100.AnsiControlSequenceParser;
|
||||
import com.loomcom.symon.jterminal.vt100.AnsiControlSequence;
|
||||
import com.loomcom.symon.jterminal.vt100.AnsiControlSequenceListener;
|
||||
|
||||
|
||||
/**
|
||||
* A test for the {@link AnsiControlSequenceParser} class.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class TestAnsiControlSequenceParser implements AnsiControlSequenceListener {
|
||||
|
||||
/**
|
||||
* The current parser.
|
||||
*/
|
||||
private AnsiControlSequenceParser parser;
|
||||
|
||||
/**
|
||||
* The list of objects returned through the
|
||||
* {@link AnsiControlSequenceListener} interface.
|
||||
*/
|
||||
private List<Object> objects = new ArrayList<Object>();
|
||||
|
||||
/**
|
||||
* Sets up the parser and object list.
|
||||
*/
|
||||
@Before
|
||||
public void setUp() {
|
||||
objects.clear();
|
||||
parser = new AnsiControlSequenceParser(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a broken sequence with the single byte CSI.
|
||||
*/
|
||||
@Test
|
||||
public void testBrokenSingleSequence() {
|
||||
parser.parse(new String(new char[] { 155 }));
|
||||
parser.parse(new String(new char[] { 'u' }));
|
||||
|
||||
assertEquals(1, objects.size());
|
||||
|
||||
Object obj = objects.get(0);
|
||||
assertEquals(AnsiControlSequence.class, obj.getClass());
|
||||
|
||||
AnsiControlSequence seq = (AnsiControlSequence) obj;
|
||||
String[] params = seq.getParameters();
|
||||
|
||||
assertEquals('u', seq.getCommand());
|
||||
assertEquals(0, params.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a broken sequence with the double byte CSI.
|
||||
*/
|
||||
@Test
|
||||
public void testBrokenDoubleSequence() {
|
||||
char[] ch1 = { 27 };
|
||||
char[] ch2 = { '[' };
|
||||
char[] ch3 = { '3', '0', ';' };
|
||||
char[] ch4 = { '4', '0', 'm' };
|
||||
|
||||
parser.parse(new String(ch1));
|
||||
parser.parse(new String(ch2));
|
||||
parser.parse(new String(ch3));
|
||||
parser.parse(new String(ch4));
|
||||
|
||||
assertEquals(1, objects.size());
|
||||
Object obj = objects.get(0);
|
||||
assertEquals(AnsiControlSequence.class, obj.getClass());
|
||||
|
||||
AnsiControlSequence seq = (AnsiControlSequence) obj;
|
||||
String[] params = seq.getParameters();
|
||||
|
||||
assertEquals('m', seq.getCommand());
|
||||
assertEquals(2, params.length);
|
||||
assertEquals("30", params[0]);
|
||||
assertEquals("40", params[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests an empty string.
|
||||
*/
|
||||
@Test
|
||||
public void testEmpty() {
|
||||
parser.parse("");
|
||||
assertEquals(0, objects.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a sequence embedded within some text.
|
||||
*/
|
||||
@Test
|
||||
public void testTextAndSequence() {
|
||||
char[] ch = { 'h', 'i', 155, 'u', 'b', 'y', 'e' };
|
||||
parser.parse(new String(ch));
|
||||
|
||||
assertEquals(3, objects.size());
|
||||
|
||||
Object o1 = objects.get(0);
|
||||
Object o2 = objects.get(1);
|
||||
Object o3 = objects.get(2);
|
||||
|
||||
assertEquals(String.class, o1.getClass());
|
||||
assertEquals(AnsiControlSequence.class, o2.getClass());
|
||||
assertEquals(String.class, o3.getClass());
|
||||
|
||||
assertEquals("hi", o1);
|
||||
assertEquals("bye", o3);
|
||||
|
||||
AnsiControlSequence seq = (AnsiControlSequence) o2;
|
||||
String[] params = seq.getParameters();
|
||||
|
||||
assertEquals(0, params.length);
|
||||
assertEquals('u', seq.getCommand());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests parameters within a sequence.
|
||||
*/
|
||||
@Test
|
||||
public void testParameters() {
|
||||
char[] ch = { 155, '3', '0', ';', '4', '0', 'm' };
|
||||
parser.parse(new String(ch));
|
||||
|
||||
assertEquals(1, objects.size());
|
||||
Object obj = objects.get(0);
|
||||
assertEquals(AnsiControlSequence.class, obj.getClass());
|
||||
|
||||
AnsiControlSequence seq = (AnsiControlSequence) obj;
|
||||
String[] params = seq.getParameters();
|
||||
|
||||
assertEquals('m', seq.getCommand());
|
||||
assertEquals(2, params.length);
|
||||
assertEquals("30", params[0]);
|
||||
assertEquals("40", params[1]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with plain text.
|
||||
*/
|
||||
@Test
|
||||
public void testText() {
|
||||
parser.parse("Hello, World!");
|
||||
|
||||
assertEquals(1, objects.size());
|
||||
Object obj = objects.get(0);
|
||||
assertEquals(String.class, obj.getClass());
|
||||
assertEquals(obj, "Hello, World!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with a single byte CSI.
|
||||
*/
|
||||
@Test
|
||||
public void testSingleCsi() {
|
||||
char[] ch = { 155, '6', 'n' };
|
||||
parser.parse(new String(ch));
|
||||
|
||||
assertEquals(1, objects.size());
|
||||
Object obj = objects.get(0);
|
||||
assertEquals(AnsiControlSequence.class, obj.getClass());
|
||||
|
||||
AnsiControlSequence seq = (AnsiControlSequence) obj;
|
||||
String[] params = seq.getParameters();
|
||||
assertEquals('n', seq.getCommand());
|
||||
assertEquals(1, params.length);
|
||||
assertEquals("6", params[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests with a double byte CSI.
|
||||
*/
|
||||
@Test
|
||||
public void testDoubleCsi() {
|
||||
char[] ch = { 27, '[', 's' };
|
||||
parser.parse(new String(ch));
|
||||
|
||||
assertEquals(1, objects.size());
|
||||
Object obj = objects.get(0);
|
||||
assertEquals(AnsiControlSequence.class, obj.getClass());
|
||||
|
||||
AnsiControlSequence seq = (AnsiControlSequence) obj;
|
||||
String[] params = seq.getParameters();
|
||||
assertEquals('s', seq.getCommand());
|
||||
assertEquals(0, params.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsedControlSequence(AnsiControlSequence seq) {
|
||||
objects.add(seq);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void parsedString(String str) {
|
||||
objects.add(str);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2011 Graham Edgecombe.
|
||||
*
|
||||
* 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;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.loomcom.symon.jterminal.TerminalCell;
|
||||
import com.loomcom.symon.jterminal.TerminalModel;
|
||||
import com.loomcom.symon.jterminal.bell.BellStrategy;
|
||||
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
|
||||
|
||||
/**
|
||||
* A test for the {@link Vt100TerminalModel} class.
|
||||
* @author Graham Edgecombe
|
||||
*/
|
||||
public class TestVt100TerminalModel {
|
||||
|
||||
/**
|
||||
* The terminal model.
|
||||
*/
|
||||
private TerminalModel model;
|
||||
|
||||
/**
|
||||
* Sets up the terminal model.
|
||||
*/
|
||||
@Before
|
||||
public void setUp(){
|
||||
model = new Vt100TerminalModel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the word wrapping.
|
||||
*/
|
||||
@Test
|
||||
public void testWordWrapping() {
|
||||
int width = model.getColumns();
|
||||
for (int i = 0; i < (width + 1); i++) {
|
||||
model.print("h");
|
||||
}
|
||||
assertEquals('h', model.getCell(0, 1).getCharacter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests special ASCII characters.
|
||||
*/
|
||||
@Test
|
||||
public void testSpecialCharacters() {
|
||||
model.print("\u0000");
|
||||
assertNull(model.getCell(0, 0));
|
||||
|
||||
model.print("a\rb\rc");
|
||||
assertEquals('c', model.getCell(0, 0).getCharacter());
|
||||
|
||||
model.print("\na");
|
||||
assertEquals('c', model.getCell(0, 0).getCharacter());
|
||||
assertEquals('a', model.getCell(1, 1).getCharacter());
|
||||
|
||||
model.print("\u007F");
|
||||
assertNull(model.getCell(0, 1));
|
||||
|
||||
model.print("A\tB");
|
||||
assertEquals('B', model.getCell(8, 1).getCharacter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the terminal scrolls once the buffer is full.
|
||||
*/
|
||||
@Test
|
||||
public void testBuffer() {
|
||||
model = new Vt100TerminalModel(model.getColumns(), 2, 2);
|
||||
model.print("This is line one.\r\n");
|
||||
model.print("This is line two. XXXXXX\r\n");
|
||||
model.print("And this is line three!");
|
||||
assertEquals('A', model.getCell(0, 1).getCharacter());
|
||||
assertNull(model.getCell(23, 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the erase functionality.
|
||||
*/
|
||||
@Test
|
||||
public void testErase() {
|
||||
model.print("Hello");
|
||||
model.print("\u009B2J");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assertNull(model.getCell(i, 0));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests moving the cursor.
|
||||
*/
|
||||
@Test
|
||||
public void testMoveCursor() {
|
||||
model.print("\u009B5B");
|
||||
assertEquals(5, model.getCursorRow());
|
||||
|
||||
model.print("\u009B3A");
|
||||
assertEquals(2, model.getCursorRow());
|
||||
|
||||
model.print("\u009B7C");
|
||||
assertEquals(7, model.getCursorColumn());
|
||||
|
||||
model.print("\u009B4D");
|
||||
assertEquals(3, model.getCursorColumn());
|
||||
|
||||
model.setCursorColumn(15);
|
||||
model.setCursorRow(0);
|
||||
|
||||
model.print("\u009B3E");
|
||||
assertEquals(0, model.getCursorColumn());
|
||||
assertEquals(3, model.getCursorRow());
|
||||
|
||||
model.setCursorColumn(7);
|
||||
|
||||
model.print("\u009BF");
|
||||
assertEquals(0, model.getCursorColumn());
|
||||
assertEquals(2, model.getCursorRow());
|
||||
|
||||
model.print("\u009B4;8H");
|
||||
assertEquals(3, model.getCursorRow());
|
||||
assertEquals(7, model.getCursorColumn());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the SGR escape sequence.
|
||||
*/
|
||||
@Test
|
||||
public void testSgr() {
|
||||
model.print("\u009B2;33;41mX");
|
||||
|
||||
TerminalCell cell = model.getCell(0, 0);
|
||||
assertNotNull(cell);
|
||||
assertEquals('X', cell.getCharacter());
|
||||
|
||||
assertEquals(Color.RED, cell.getBackgroundColor());
|
||||
assertEquals(Color.YELLOW, cell.getForegroundColor());
|
||||
|
||||
model.print("\u009B0m\rX");
|
||||
|
||||
cell = model.getCell(0, 0);
|
||||
|
||||
assertNotNull(cell);
|
||||
assertEquals('X', cell.getCharacter());
|
||||
|
||||
assertEquals(model.getDefaultBackgroundColor(), cell.getBackgroundColor());
|
||||
assertEquals(model.getDefaultForegroundColor(), cell.getForegroundColor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests saving and restoring the cursor.
|
||||
*/
|
||||
@Test
|
||||
public void testSaveAndRestoreCursor() {
|
||||
model.setCursorColumn(3);
|
||||
model.setCursorRow(17);
|
||||
|
||||
model.print("\u009Bs");
|
||||
|
||||
model.setCursorColumn(5);
|
||||
model.setCursorRow(23);
|
||||
|
||||
model.print("\u009Bu");
|
||||
|
||||
assertEquals(3, model.getCursorColumn());
|
||||
assertEquals(17, model.getCursorRow());
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the printing of a simple message.
|
||||
*/
|
||||
@Test
|
||||
public void testPrint() {
|
||||
model.print("Hi");
|
||||
assertEquals('H', model.getCell(0, 0).getCharacter());
|
||||
assertEquals('i', model.getCell(1, 0).getCharacter());
|
||||
assertNull(model.getCell(2, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the bell is sounded.
|
||||
*/
|
||||
@Test
|
||||
public void testBell() {
|
||||
final int[] counter = new int[1];
|
||||
|
||||
BellStrategy strategy = new BellStrategy() {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.loomcom.symon.jterminal.bell.BellStrategy#soundBell()
|
||||
*/
|
||||
@Override
|
||||
public void soundBell() {
|
||||
counter[0]++;
|
||||
}
|
||||
};
|
||||
|
||||
model.setBellStrategy(strategy);
|
||||
|
||||
model.print("\u0007");
|
||||
assertEquals(1, counter[0]);
|
||||
|
||||
model.print("Hello, \u0007World!\u0007\r\n");
|
||||
assertEquals(3, counter[0]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||