Compare commits

...
86 Commits
Author SHA1 Message Date
Seth Morabito 922810b34b Update POM for 1.3.0 2018-02-25 11:46:02 -08:00
Seth Morabito 9d6791330d Update README 2018-02-25 11:45:16 -08:00
Seth Morabito 73c474d606 POM cleanup 2018-02-04 11:49:26 -08:00
Chris Cureau 71905fdb19 Merge pull request #23 from ccureau/master
Integrate jterminal
2017-09-12 18:23:55 -05:00
ccureau cb918c9906 Remove unnecessary package-info files 2017-08-30 10:40:45 -05:00
ccureau be72c2ff09 implement jterminal in source 2017-08-30 10:34:50 -05:00
ccureau 18ce120984 Add jterminal to source 2017-08-30 09:23:59 -05:00
Seth Morabito 8ffb130300 Merge pull request #21 from LIV2/master
Fix PHY/PLY Addressing mode
2017-08-23 10:47:12 -07:00
Matt Harlum 441f7349a0 Fix PHY/PLY Addressing mode 2017-06-19 11:49:46 +10:00
Seth Morabito 4f27e78940 Merge pull request #20 from LIV2/master
Add cycle counts for illegal NMOS opcodes
2017-06-06 19:47:24 -07:00
Matt Harlum 11f61f50a9 Add cycle counts for illegal NMOS opcodes 2017-06-07 12:20:21 +10:00
Seth Morabito 59f8ff1bc4 Merge pull request #19 from LIV2/master
Adds support for all 65C02 Opcodes
2017-06-06 07:10:02 -07:00
Matt Harlum a9c6d5964f * Add Support for All 65C02 Opcodes and all Rockwell/WDC opcodes except WAI/STP
* Add 65C02 Opcode tests
* All tests pass, Klaus' 6502_functional_tests pass & Klaus' 65C02_extended_opcodes_test also all pass
2017-06-06 19:59:01 +10:00
Matt Harlum faf5d22660 * Add ACIA Interrupt tests.
* Fix ACIA6850 Interrupt behavior, Interrupt should be cleared on status register read.
* Remove unneeded cpuAccess if statement from Acia6850 write that was preventing build completion
* Fix ACIA6850 Tests so they run.
2017-06-06 12:55:35 +10:00
Seth Morabito 3c3aee30a7 Merge pull request #18 from izuannazrin/master
Interrupt bit support for ACIA
2017-06-04 18:53:44 -07:00
Izuan Nazrin 92f8fe3dd9 Fixed 6850 behaviour
+ Fixed interrupt bit reset behaviour
2017-05-30 01:33:12 +08:00
Izuan Nazrin 356822df71 Fixed typo
+ Fixed typo in Acia.java statusReg declaration
2017-05-30 01:14:50 +08:00
Izuan Nazrin f2ced29979 Added interrupt support for ACIA
+ Added interrupt support for ACIA
+ Auto-formatting (in case anyone cares)
2017-05-29 02:03:18 +08:00
Seth Morabito 2e33756d49 Merge pull request #17 from LIV2/master
Memory window should not reset device registers
2017-05-26 09:38:28 -07:00
Matt Harlum 079d3dbeae Fix README links 2017-05-26 17:01:48 +10:00
Matt Harlum 96819f1bf7 Issue #16: Memory window should not reset device registers 2017-05-26 17:01:41 +10:00
Seth Morabito 9cdd718e8d Fix Screenshots part two 2017-03-20 07:49:32 -07:00
Seth Morabito 5554acd29c Fix screenshots 2017-03-20 07:39:49 -07:00
Seth Morabito 5a2e057e69 Minor whitespace cleanup 2016-06-11 11:46:31 -07:00
Seth Morabito da88aadda2 Merge pull request #15 from LIV2/master
Correct BRK/IRQ behavior
2016-03-20 11:43:42 -07:00
Matt Harlum 657b69da6c Cleanup my comments 2016-03-20 17:05:48 +11:00
Chris Cureau f251e54174 Merge pull request #13 from sethm/cc65_update_patches
update Makefiles and configuration for latest cc65
2016-03-14 20:01:12 -05:00
Chris Cureau fcc57d9be4 update Makefiles and configuration for latest cc65 2016-01-12 08:14:38 -06:00
Seth Morabito 88eba2cdcb Merge branch '1.2.branch' 2016-01-08 20:53:54 -08:00
Seth Morabito 0e49b197b3 Update README for 1.2.1 2016-01-08 20:52:02 -08:00
Seth Morabito b59fa63b63 Merge branch '1.2.branch' 2016-01-08 20:41:25 -08:00
Seth Morabito eff98118d5 Fix a few straggling Java 8-isms. 2016-01-08 20:40:23 -08:00
Seth Morabito c599df1cfb Set version to 1.3.0-SNAPSHOT 2016-01-08 19:14:00 -08:00
Seth Morabito 66c52c8826 Revert Java 1.8 changes. Buildable with Java 1.7
There are still active users on Java 1.7, so building with Java 8
was a no-no. This change reverts the recent migration to Java 8,
allowing JDK 1.7 to compile the code.

This means, at least for the time being, no more Lambda expressions
(Boooooooooooooooo!!)
2016-01-08 19:11:42 -08:00
Seth Morabito f3a5dd93ad Release 1.2.0 2016-01-03 11:01:20 -08:00
Seth Morabito 4044458baa Fix typo 2016-01-03 10:59:11 -08:00
Seth Morabito 9ab94b3050 Prep for 1.2 2016-01-03 10:56:16 -08:00
Seth Morabito 9a504adc58 Added roadmap to README 2016-01-03 10:49:41 -08:00
Seth Morabito 57cbff42e7 Merge branch '1.1.branch' 2016-01-02 19:06:23 -08:00
Seth Morabito 634ea933f1 Add disassembled instructions to breakpoints 2016-01-02 19:05:38 -08:00
Seth Morabito 0ca30291cb Merge branch '1.1.branch' 2016-01-02 09:51:20 -08:00
Seth Morabito 95f85b71b1 Enter key can add breakpoints 2016-01-02 09:50:38 -08:00
Seth Morabito 6b5976be8f Update for 1.1.1 release 2016-01-02 09:46:12 -08:00
Seth Morabito 3530e9e99a Enter key can add breakpoints 2016-01-02 09:40:34 -08:00
Seth Morabito 7a9f9bfd55 Update images for 1.1.0 2016-01-01 09:49:39 -08:00
Seth Morabito 1e04a52d1b Prep for 1.2 2016-01-01 09:44:43 -08:00
Seth Morabito 7daeb9e978 Update README for 1.1.0 2015-12-31 16:38:27 -08:00
Seth Morabito da2750f4ee Release 1.1.0 2015-12-31 16:19:06 -08:00
Seth Morabito df88c54f90 Support for breakpoints
- Adds a new window that allows adding and deleting breakpoints.
  Will halt the simulator when a breakpoint is reached.
2015-12-31 16:09:50 -08:00
Seth Morabito 69e1985ec1 Update README 2015-12-30 11:03:23 -08:00
Seth Morabito 6267d1d777 Allow runtime selection of CPU speed 2015-12-30 10:56:03 -08:00
Seth Morabito 84e5c5ad56 Refactor delay loop 2015-12-30 10:08:44 -08:00
Seth Morabito 6e8fd40014 Refactor for Java 1.8
- Clean up and refactor code

- Add 1.8 features

- Clean up IntelliJ inspector warnings
2015-12-29 17:55:41 -08:00
Seth Morabito a4a110dcef Bugfixes, change logger, update copyright
- IR field in status panel now correctly displays the next instruction
  to be executed, instead of the instruction that was just executed.

- Switched from built-in Java util logger to Logback

- Updated all copyright strings to 2016
2015-12-29 14:40:42 -08:00
Matt Harlum 8335cf5421 Correct BRK behaviour
IRQ/NMI clear the BRK flag
BRK is Non-Maskable
2015-06-01 08:58:17 +10:00
Seth Morabito ae5c5d48b2 Merge pull request #12 from maikmerten/master
mask all bits beyond bits 0 to 7 on bus reads to ensure no surplus bits ...
2014-08-23 16:59:56 -07:00
Maik Merten 72ba068beb mask all bits beyond bits 0 to 7 on bus reads to ensure no surplus bits cause unpredictable system behavior. 2014-08-23 16:50:34 +02:00
Seth Morabito 142cb470a0 Converging on a useful CRTC ROM 2014-08-18 19:52:09 -07:00
Seth Morabito cad3673bd2 Formatting; uppercase 2014-08-16 16:54:36 -07:00
Seth Morabito 4796b65390 New ROMs. Better backspace handling. 2014-08-16 14:28:54 -07:00
Seth Morabito e0d6017d95 Unifying and cleaning up copyrights 2014-08-16 13:50:47 -07:00
Seth Morabito d6ea21bae4 Mostly working CRTC 2014-08-15 15:21:02 -07:00
Seth Morabito af12039686 Merge pull request #10 from maikmerten/master
Multicomp: SD card controller emulation
2014-08-15 13:51:26 -07:00
Seth Morabito ed1d5f999b Preparing for real implementation 2014-08-15 12:48:54 -07:00
Maik Merten c595ff5370 README update 2014-08-15 20:15:04 +02:00
Maik Merten e9f662cc17 Merge remote-tracking branch 'upstream/master'
Conflicts:
	src/main/java/com/loomcom/symon/machines/MulticompMachine.java
2014-08-15 20:11:33 +02:00
Seth Morabito 70ab2a6fc2 More CRTC tinkering 2014-08-14 21:03:52 -07:00
Seth Morabito f35fbce38b Add CRTC EhBASIC, switch char rom. 2014-08-14 11:50:51 -07:00
Seth Morabito eeb246ebc2 CRTC refactoring 2014-08-14 10:53:48 -07:00
Seth Morabito e157217f50 Fix missing repaint on Video Window 2014-08-13 15:46:20 -07:00
Maik Merten cc12e8f70a proper status value when SD controller is in write mode 2014-08-13 19:56:40 +02:00
Maik Merten a9a1c1aa52 get writing to a certain position in a file somewhat more correct 2014-08-13 19:38:54 +02:00
Seth Morabito 742e3a1027 Split PIA into PIA/VIA6522 2014-08-12 15:06:08 -07:00
Seth Morabito 45f596e0d4 Rename VIA to PIA
Preparing to create a PIA interface and one or more parallel
IO device implementations.
2014-08-12 14:19:55 -07:00
Seth Morabito 4402a28e2b Update README 2014-08-12 14:05:19 -07:00
Maik Merten 693d1959ac finer grained synchronization in TraceLog. Profiling indicates that this speeds up adding log entries quite considerably. 2014-08-12 21:58:52 +02:00
Maik Merten 792366fddb some cleanup in the SD card controller 2014-08-12 21:34:00 +02:00
Maik Merten b2670aedf1 email address update 2014-08-12 19:25:37 +02:00
Maik Merten 5d488bbcd2 Merge remote-tracking branch 'upstream/master' 2014-08-12 11:48:47 +02:00
Seth Morabito e2a1144c7c Update copyright, prep for 1.1.0 2014-08-11 14:16:41 -07:00
Maik Merten 010c42753f Merge remote-tracking branch 'upstream/master' 2014-08-11 09:34:44 +02:00
Maik Merten 7a215736fe rework SD controller emulation to work with Input- and OutputStreams (no longer load the image into a byte array) 2014-08-09 17:55:18 +02:00
Maik Merten c61a63d5bb more accurate emulation of the SD controller 2014-08-09 16:47:49 +02:00
Maik Merten 7cb4e1c945 NPE fix and reset position on every command 2014-08-09 15:13:59 +02:00
Maik Merten 3a40d35fdf use SD interface in MulticompMachine and lift the speed limit on the ACIA device to better match the experience on the real machine 2014-08-09 14:58:29 +02:00
Maik Merten d658cd0ae5 completely untested and incomplete implementation of the SD card interface of the MULTICOMP 2014-08-09 14:57:18 +02:00
100 changed files with 16289 additions and 1454 deletions

No files matched your search

@@ -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
+5
View File
@@ -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
+4
View File
@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
+1 -1
View File
@@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2008-2013 Seth J. Morabito <web@loomcom.com>
Copyright (c) 2008-2014 Seth J. Morabito <web@loomcom.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
+90 -45
View File
@@ -1,13 +1,13 @@
SYMON - A 6502 System Simulator
===============================
**Version:** 1.0.0
**Version:** 1.3.0
**Last Updated:** 11 August, 2014
**Last Updated:** 25 February, 2018
See the file COPYING for license.
![Symon Simulator in Action] (https://github.com/sethm/symon/raw/master/screenshots/full.jpg)
![Symon Simulator in Action](http://www.loomcom.com/symon/screenshots/full.jpg)
## 1.0 About
@@ -16,13 +16,13 @@ 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
@@ -30,7 +30,7 @@ are always welcome.
## 2.0 Requirements
- Java 1.7 or higher
- Java 1.8 or higher
- Maven 2.0.x or higher (for building from source)
- JUnit 4 or higher (for testing)
@@ -58,6 +58,7 @@ memory.
- `$0000`--`$DFFF`: 56KB RAM
- `$E000`--`$FFFF`: 8KB ROM
- `$FFD0`--`$FFD1`: Motorola 6850 ACIA
- `$FFD8`--`$FFDF`: Controller for SD cards
### 3.1.3 Simple Memory Map
@@ -65,7 +66,7 @@ memory.
### 3.2 Serial Console and CPU Status
![Serial Console] (https://github.com/sethm/symon/raw/master/screenshots/console.png)
![Serial Console](http://www.loomcom.com/symon/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
@@ -77,13 +78,13 @@ 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.
![Font Selection] (https://github.com/sethm/symon/raw/master/screenshots/font_selection.png)
![Font Selection](http://www.loomcom.com/symon/screenshots/font_selection.png)
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)
![ROM Loading](http://www.loomcom.com/symon/screenshots/load_rom.png)
Symon can load any appropriately sized ROM image. The Symon
architecture expects as 16KB (16384 byte) ROM image, while the
@@ -94,27 +95,40 @@ address.
### 3.4 Memory Window
![Memory Window] (https://github.com/sethm/symon/raw/master/screenshots/memory_window.png)
![Memory Window](http://www.loomcom.com/symon/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)
![Trace Log](http://www.loomcom.com/symon/screenshots/trace_log.png)
The last 20,000 execution steps are disassembled and logged to the Trace Log Window.
The last 20,000 execution steps are disassembled and logged to the Trace Log
Window.
### 3.6 NEW - Experimental 6545 CRTC Video
### 3.6 Simulator Speeds
![Composite Video] (https://github.com/sethm/symon/raw/master/screenshots/video_window.png)
![Speeds](http://www.loomcom.com/symon/screenshots/simulator_menu.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 Controller located at address `$9000` and
`$9001`.
Simulated speeds may be set from 1MHz to 8MHz.
By default, the 40 x 25 character display uses video memory located at base address `$7000`.
This means that the memory from address `$7000` (28672 decimal) to `$73E8` (29672 decimal)
is directly mapped to video.
### 3.7 Breakpoints
![Breakpoints](http://www.loomcom.com/symon/screenshots/breakpoints.png)
Breakpoints can be set and removed through the Breakpoints window.
### 3.8 Experimental 6545 CRTC Video
![Composite Video](http://www.loomcom.com/symon/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
Controller located at address `$9000` and `$9001`.
By default, the 40 x 25 character display uses video memory located at base
address `$7000`. This means that the memory from address `$7000` (28672
decimal) to `$73E8` (29672 decimal) is directly mapped to video.
- Address Register (at address `$9000`)
- R1: Horizontal Displayed Columns
@@ -127,27 +141,29 @@ is directly mapped to video.
- R14: Cursor Position (High Byte)
- R15: Cursor Position (Low Byte)
Although the simulation is pretty good, there are a few key differences between
the simulated 6545 and a real 6545:
Although the simulation is pretty good, there are a few key differences
between the simulated 6545 and a real 6545:
- The simulated 6545 supports only the straight binary addressing mode of the real 6545,
and not the Row/Column addressing mode.
- The simulated 6545 has full 16 bit addressing, where the real 6545 has only
a 14-bit address bus.
- The simulation is done at a whole-frame level, meaning that lots 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.
- The simulated 6545 supports only the straight binary addressing
mode of the real 6545, and not the Row/Column addressing mode.
- The simulated 6545 has full 16 bit addressing, where the real 6545
has only a 14-bit address bus.
- The simulation is done at a whole-frame level, meaning that lots
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.
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.6.1 Example BASIC Program to test Video
#### 3.8.1 Example BASIC Program to test Video
This program will fill the video screen with all printable characters.
@@ -170,9 +186,9 @@ Maven will build Symon, run unit tests, and produce a jar file in the
`target` directory containing the compiled simulator.
Symon is meant to be invoked directly from the jar file. To run with
Java 1.5 or greater, just type:
Java 1.8 or greater, just type:
$ java -jar symon-0.9.1.0.jar
$ java -jar symon-1.2.0.jar
When Symon is running, you should be presented with a simple graphical
interface.
@@ -219,7 +235,24 @@ running.
## 5.0 Revision History
- **1.0.0-SNAPSHOT:** 10 August, 2014 - Added "Simple" machine
- **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
simulate various clock speeds. Added ability to select clock
speed at runtime. Status display now shows the next instruction
to be executed, instead of the last instruction executed.
Added support for breakpoints.
- **1.0.0:** 10 August, 2014 - Added "Simple" machine
implementation, pure RAM with no IO. Added Klaus Dormann's
6502 Functional Tests for further machine verification (these
tests must be run in the "Simple" machine).
@@ -261,7 +294,15 @@ running.
- **0.1:** 20 January, 2010
## 6.0 To Do
## 6.0 Roadmap
- **1.2:** Better breakpoint support. Symbolic debugging of breakpoints.
- **2.0:** Complete rewrite of the UI in JavaFX instead of Swing. Complete
assembler and disassembler built in. Ability to attach source code for
symbolic debugging.
## 7.0 To Do
- Feedback (in the form of dialogs, status bar, etc).
@@ -279,9 +320,13 @@ running.
- Allow displaying ACIA status and dumping ACIA buffers, for
debugging.
- CRTC emulation is very naive. The whole frame is drawn in one
CPU step. This should be improved by drawing scan lines during
machine steps to approximate real NTSC/PAL refresh rates.
- Symbolic debugging.
## 7.0 Copyright and Acknowledgements
## 8.0 Copyright and Acknowledgements
**Copyright (c) 2014 Seth J. Morabito &lt;web@loomcom.com&gt;**
@@ -295,13 +340,13 @@ 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://www.obelisk.demon.co.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
## 8.0 Licensing
## 9.0 Licensing
Symon is free software. It is distributed under the MIT License.
Please see the file 'COPYING' for full details of the license.
+13 -53
View File
@@ -4,7 +4,7 @@
<groupId>com.loomcom.symon</groupId>
<artifactId>symon</artifactId>
<packaging>jar</packaging>
<version>1.0.0</version>
<version>1.3.0</version>
<name>symon</name>
<url>http://www.loomcom.com/symon</url>
<properties>
@@ -15,72 +15,32 @@
UTF-8
</project.reporting.outputEncoding>
</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>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>4.12</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>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>copy-resources</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${basedir}/target/classes/com/loomcom/symon/ui/images</outputDirectory>
<resources>
<resource>
<directory>src/main/java/com/loomcom/symon/ui/images</directory>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<version>2.6</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
@@ -104,11 +64,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.6</source>
<target>1.6</target>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
@@ -116,7 +76,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<version>2.6</version>
<configuration>
<archive>
<manifest>
+1 -1
View File
@@ -7,7 +7,7 @@ echo_irq: echo_irq.o
$(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o
echo_irq.o:
$(CA) --listing -o echo_irq.o echo_irq.asm
$(CA) --listing echo_irq.lst -o echo_irq.o echo_irq.asm
clean:
rm -f *.o *.rom *.map *.lst
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY {
RAM1: start = $0000, size = $8000;
RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3FFA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
}
@@ -11,6 +11,6 @@ VECTORS: load = ROMV, type = ro;
}
SYMBOLS {
__STACKSIZE__ = $0300;
__STACKSIZE__: type = weak, value = $0300;
}
+1 -1
View File
@@ -7,7 +7,7 @@ ehbasic: ehbasic.o
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
ehbasic.o:
$(CA) --listing -o ehbasic.o min_mon.asm
$(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
clean:
rm -f *.o *.rom *.map *.lst
+1 -1
View File
@@ -150,7 +150,7 @@ END_CODE
; sign on string
LAB_mess
.byte $0D,$0A,"Symon (c) 2008-2013, Seth Morabito"
.byte $0D,$0A,"Symon (c) 2008-2014, Seth Morabito"
.byte $0D,$0A,"Enhanced 6502 BASIC 2.22 (c) Lee Davison"
.byte $0D,$0A,"[C]old/[W]arm ?",$00
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY {
RAM1: start = $0000, size = $8000;
RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3F00, fill = yes;
MONITOR: start = $FF00, size = $FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
@@ -13,6 +13,6 @@ VECTORS: load = ROMV, type = ro;
}
SYMBOLS {
__STACKSIZE__ = $0300;
__STACKSIZE__: type = weak, value = $0300;
}
+4
View File
@@ -0,0 +1,4 @@
*.rom
*.lst
*.map
*.o
+13
View File
@@ -0,0 +1,13 @@
CA=ca65
LD=ld65
all: ehbasic
ehbasic: ehbasic.o
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
ehbasic.o:
$(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
clean:
rm -f *.o *.rom *.map *.lst
+85
View File
@@ -0,0 +1,85 @@
This directoy contains a very slightly modified version of EhBASIC 2.22
to support the Symon simulator.
Usage
-----
The pre-assemled ROM image 'ehbasic.rom' in the "samples" directory is all you
need unless you want to rebuild this source code.
Just copy the image 'ehbasic.rom' to the directory where you run Symon, and
rename the file to 'rom.bin'. It will be loaded at memory location $D000 when
the simulator starts up. Click "Run" and you'll be presented with BASIC.
At the first prompt, type 'C' for a Cold Start
When prompted for free memory, type: $C000
Note that EhBASIC only accepts upper case input, so you'll need to use caps
lock (the cruise control for cool) to really make the most of it.
Building
--------
To build it, you'll need the CC65 tool chain from http://www.cc65.org/
and a suitable version of 'make'. Just typing:
% make
in this directory should re-build everything. You'll get a listing,
some object files, and the ROM image itself.
Changes from the EhBASIC 2.22
-----------------------------
- Minor syntax changes to allow assembly with the cc65 tool chain.
- Memory map modified for Symon.
- At reset, configure the 6551 ACIA for 8-N-1, 2400 baud.
- Monitor routines 'ACIAin' and 'ACIAout' modified for the 6551 ACIA.
Specifically, reading and writing will check the 6551 status register for
the status of rx and tx registers before receive or transmit.
EhBASIC is copyright Lee Davison <leeedavison@googlemail.com>
My changes are so slight that I hesitate to even say this, but for "CYA"
reasons:
Changes are copyright Seth Morabito <web@loomcom.com> and are distributed under
the same license as EhBASIC. I claim no commercial interest whatsoever. Any
commercial use must be negotiated directly with Lee Davison.
Original EhBASIC 2.22 README
----------------------------
Enhanced BASIC is a BASIC interpreter for the 6502 family microprocessors. It
is constructed to be quick and powerful and easily ported between 6502 systems.
It requires few resources to run and includes instructions to facilitate easy
low level handling of hardware devices. It also retains most of the powerful
high level instructions from similar BASICs.
EhBASIC is free but not copyright free. For non commercial use there is only one
restriction, any derivative work should include, in any binary image distributed,
the string "Derived from EhBASIC" and in any distribution that includes human
readable files a file that includes the above string in a human readable form
e.g. not as a comment in an HTML file.
For commercial use please contact me, Lee Davison, at leeedavison@googlemail.com
for conditions.
For more information on EhBASIC, other versions of EhBASIC and other projects
please visit my site at ..
http://mycorner.no-ip.org/index.html
P.S. c't magazin, henceforth refered to as "those thieving german bastards", are
prohibited from using this or any version of EhBASIC for any of their projects
or products. The excuse "we don't charge people for it" doesn't wash, it adds
value to your product so you owe me.
File diff suppressed because it is too large. Load diff
+449
View File
@@ -0,0 +1,449 @@
; minimal monitor for EhBASIC and 6502 simulator V1.05
; To run EhBASIC on the simulator load and assemble [F7] this file, start the simulator
; running [F6] then start the code with the RESET [CTRL][SHIFT]R. Just selecting RUN
; will do nothing, you'll still have to do a reset to run the code.
.feature labels_without_colons
.include "basic.asm"
; put the IRQ and MNI code in RAM so that it can be changed
IRQ_vec = VEC_SV+2 ; IRQ code vector
NMI_vec = IRQ_vec+$0A ; NMI code vector
; setup for the 6502 simulator environment
IO_AREA = $8800
ACIAdata = IO_AREA ; simulated ACIA r/w port
ACIAstatus = IO_AREA+1
ACIAcommand = IO_AREA+2
ACIAcontrol = IO_AREA+3
CRTC_A = $9000 ; CRTC register select
CRTC_C = $9001 ; CRTC register IO
CADDR_L = $E0 ; Cursor address (low)
CADDR_H = $E1 ; Cursor address (high)
C_COL = $E2 ; Cursor column
C_ROW = $E3 ; Cursor row
COUTC = $E4 ; Temp storage for char out.
R_STRT = $E5 ; E5,E6 contains starting address of
; current row
R_NXT = $E7 ; E7,E8 contains starting address of
; next row
N_ROWS = 24
N_COLS = 40
; now the code. all this does is set up the vectors and interrupt code
; and wait for the user to select [C]old or [W]arm start. nothing else
; fits in less than 128 bytes
.segment "MONITOR"
.org $FC00 ; pretend this is in a 1/8K ROM
; reset vector points here
RES_vec:
CLD ; clear decimal mode
LDX #$FF ; empty stack
TXS ; set the stack
;; Initialize the CRTC
LDA #$01
STA CRTC_A
LDA #N_COLS ; 40 columns
STA CRTC_C
LDA #$06
STA CRTC_A
LDA #N_ROWS ; 24 rows
STA CRTC_C
LDA #$70
STA CADDR_H
LDA #$00
STA CADDR_L
STA C_ROW
STA C_COL
JSR CLRVID
;; TODO: Initialize params on CRTC
; Initialize the ACIA
ACIA_init:
LDA #$00
STA ACIAstatus ; Soft reset
LDA #$0B
STA ACIAcommand ; Parity disabled, IRQ disabled
LDA #$1E
STA ACIAcontrol ; Set output for 8-N-1 9600
; set up vectors and interrupt code, copy them to page 2
LDY #END_CODE-LAB_vec ; set index/count
LAB_stlp:
LDA LAB_vec-1,Y ; get byte from interrupt code
STA VEC_IN-1,Y ; save to RAM
DEY ; decrement index/count
BNE LAB_stlp ; loop if more to do
; now do the signon message, Y = $00 here
LAB_signon:
LDA LAB_mess,Y ; get byte from sign on message
BEQ LAB_nokey ; exit loop if done
JSR V_OUTP ; output character
INY ; increment index
BNE LAB_signon ; loop, branch always
LAB_nokey:
JSR V_INPT ; call scan input device
BCC LAB_nokey ; loop if no key
AND #$DF ; mask xx0x xxxx, ensure upper case
CMP #'W' ; compare with [W]arm start
BEQ LAB_dowarm ; branch if [W]arm start
CMP #'C' ; compare with [C]old start
BNE RES_vec ; loop if not [C]old start
JMP LAB_COLD ; do EhBASIC cold start
LAB_dowarm:
JMP LAB_WARM ; do EhBASIC warm start
ACIAout:
PHA
@loop: LDA ACIAstatus
AND #$10
BEQ @loop ; Wait for buffer to empty
PLA
STA ACIAdata
RTS
;;; Byte out to the CRTC
;;;
;;; 1. Increment cursor position.
;;; 2. Scroll if necessary.
;;; 3. Store new cursor position in CRTC.
CRTCout:
JSR ACIAout ; Also echo to terminal for debugging.
;; In parallel, we're maintaining two states:
;; - The address of the cursor, and
;; - The Column/Row of the cursor.
;;
;; The latter state is used to handle scrolling and
;; for knowing how far to back up the cursor
;; for a carriage-return.
;; Backspace
CMP #$08
BEQ DO_BS
;; Line Feed
CMP #$0a
BEQ DO_LF
;; Carriage Return
CMP #$0d
BEQ DO_CR
;; Any other character...
STA COUTC ; Store the character going out
JSR COUT1 ; Output it where the cursor is.
;; We might need to scroll...
LDA C_ROW
CMP #N_ROWS-1
BNE @noscr
LDA C_COL
CMP #N_COLS-1
BNE @noscr
;; ... yup, we DO need to scroll.
JSR DO_SCROLL
RTS
@noscr: JSR INC_CADDR
INC C_COL
JSR SET_CURSOR
RTS
;; Handle a back-space character.
DO_BS: LDA C_COL
BNE @l1 ; Skip next bit
LDA #N_COLS-1
STA C_COL
BNE @l2
@l1: DEC C_COL
@l2: JSR DEC_CADDR ; Back up
JSR SET_CURSOR
@l3: LDA #$20
STA COUTC ; Echo a space ($20)
JSR COUT1
RTS
DO_LF: RTS ; Just swallow LF. CR emulates it.
DO_CR: SEC
LDA CADDR_L ; 1. Carriage return to start of row.
SBC C_COL
STA CADDR_L
LDA CADDR_H
SBC #$00 ; Will decrement H if carry was left
; set.
STA CADDR_H
;; 1. Are we on the last row? Scroll.
LDA C_ROW
CMP #N_ROWS-1
BNE @inc
JSR DO_SCROLL
RTS
@inc: JSR INCROW
@lf: CLC
LDA CADDR_L
ADC #$28 ; Now add $28
STA CADDR_L
LDA CADDR_H
ADC #$00 ; Will increment if carry was set
STA CADDR_H
LDA #$00 ; Reset cursor row to 0
STA C_COL
JSR SET_CURSOR
RTS
SET_CURSOR:
LDA #14
STA CRTC_A
LDA CADDR_H
STA CRTC_C
LDA #15
STA CRTC_A
LDA CADDR_L
STA CRTC_C
RTS
;; Increment C_ROW and put new
;; line start address in R_STRT,R_STRT+1
INCROW: TYA
PHA
INC C_ROW
BNE DECR2 ; Share code with DECROW
;; Decrement C_ROW and put new
;; line start address in R_STRT,R_STRT+1
DECROW: TYA
PHA
DEC C_ROW
DECR2: JSR SETRSTRT
PLA
TAY
RTS
;; Send the cursor home to the start of the line
HOME:
LDY C_ROW
LDA CRA_LO,Y
STA CADDR_L
LDA CRA_HI,Y
STA CADDR_H
LDA #$00
STA C_COL
RTS
;; Set the start address of the current row
SETRSTRT:
LDY C_ROW
LDA CRA_LO,Y
STA R_STRT
LDA CRA_HI,Y
STA R_STRT+1
RTS
;; Clear the current video line
CLRLN: LDA #$20
LDY #$00
@l1: STA (R_STRT),Y
INY
CPY #N_COLS
BNE @l1
RTS
;; Clear the video window and put the cursor at the home
;; position
CLRVID:
LDA #$20
LDX #N_ROWS-1
@l1: STX C_ROW
JSR SETRSTRT
JSR CLRLN
DEX
BPL @l1
RTS
;; Handle a scroll request.
;;
;; The cursor could be anywhere on the line when this scroll
;; request comes in.
DO_SCROLL:
PHA
TYA
PHA
TXA
PHA
;; The idea here is to copy each line to the line above it
LDX #$00
@l1: LDA CRA_LO,X
STA R_STRT
LDA CRA_HI,X
STA R_STRT+1
INX
LDA CRA_LO,X
STA R_NXT
LDA CRA_HI,X
STA R_NXT+1
LDY #0
@l2: LDA (R_NXT),Y
STA (R_STRT),Y
INY
CPY #N_COLS
BNE @l2
CPX #N_ROWS
BNE @l1
;; Clear the line the cursor occupies.
JSR CLRLN
;; Move the cursor to the beginning of the line
JSR HOME
JSR SET_CURSOR
PLA
TAX
PLA
TAY ; Restore Y
PLA
RTS
;; Decrement the cursor address
INC_CADDR:
INC CADDR_L
BNE @l1 ; Did we increment to 0?
INC CADDR_H ; Yes, also increment high
@l1: RTS
;; Increment the cursor address
DEC_CADDR:
CMP CADDR_L ; Is low alrady 0?
BNE @l1
DEC CADDR_H ; Yes, decrement high
@l1: DEC CADDR_L
RTS
COUT1:
TYA
PHA ; Save Y
LDY #$00
LDA COUTC
STA (CADDR_L),Y
PLA
TAY ; Restore Y
RTS
;;;
;;; byte in from ACIA. This subroutine will also force
;;; all lowercase letters to be uppercase, because EhBASIC
;;; only understands upper-case tokens
;;;
ACIAin:
LDA ACIAstatus ; Read 6551 status
AND #$08 ;
BEQ LAB_nobyw ; If rx buffer empty, no byte
LDA ACIAdata ; Read byte from 6551
CMP #'a' ; Is it < 'a'?
BCC @done ; Yes, we're done
CMP #'{' ; Is it >= '{'?
BCS @done ; Yes, we're done
AND #$5f ; Otherwise, mask to uppercase
@done:
SEC ; Flag byte received
RTS
LAB_nobyw:
CLC ; flag no byte received
no_load: ; empty load vector for EhBASIC
no_save: ; empty save vector for EhBASIC
RTS
; vector tables
LAB_vec:
.word ACIAin ; byte in from simulated ACIA
.word CRTCout ; byte out to simulated ACIA
.word no_load ; null load vector for EhBASIC
.word no_save ; null save vector for EhBASIC
; EhBASIC IRQ support
IRQ_CODE:
PHA ; save A
LDA IrqBase ; get the IRQ flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA IrqBase ; OR the original back in
STA IrqBase ; save the new IRQ flag byte
PLA ; restore A
RTI
; EhBASIC NMI support
NMI_CODE:
PHA ; save A
LDA NmiBase ; get the NMI flag byte
LSR ; shift the set b7 to b6, and on down ...
ORA NmiBase ; OR the original back in
STA NmiBase ; save the new NMI flag byte
PLA ; restore A
RTI
END_CODE:
;;; CRTC row addresses: HIGH
CRA_HI:
.byte $70,$70,$70,$70,$70,$70,$70,$71
.byte $71,$71,$71,$71,$71,$72,$72,$72
.byte $72,$72,$72,$72,$73,$73,$73,$73
;;; CRTC row addresses: LOW
CRA_LO:
.byte $00,$28,$50,$78,$A0,$C8,$F0,$18
.byte $40,$68,$90,$B8,$E0,$08,$30,$58
.byte $80,$A8,$D0,$F8,$20,$48,$70,$98
; sign on string
LAB_mess:
.byte $0D,$0A,"SYMON (C) 2008-2014, SETH MORABITO"
.byte $0D,$0A,"ENHANCED 6502 BASIC 2.22 (C) LEE DAVISON"
.byte $0D,$0A,"[C]OLD/[W]ARM ?",$00
; system vectors
.segment "VECTORS"
.org $FFFA
.word NMI_vec ; NMI vector
.word RES_vec ; RESET vector
.word IRQ_vec ; IRQ vector
+18
View File
@@ -0,0 +1,18 @@
MEMORY {
RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3C00, fill = yes;
MONITOR: start = $FC00, size = $3FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
}
SEGMENTS {
CODE: load = ROM1, type = ro;
DATA: load = ROM1, type = ro;
MONITOR: load = MONITOR, type = ro;
VECTORS: load = ROMV, type = ro;
}
SYMBOLS {
__STACKSIZE__: type = weak, value = $0300;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 259 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

@@ -0,0 +1,85 @@
package com.loomcom.symon;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.util.Utils;
import javax.swing.table.AbstractTableModel;
import java.util.ArrayList;
import java.util.TreeSet;
public class Breakpoints extends AbstractTableModel {
private TreeSet<Integer> breakpoints;
private Simulator simulator;
public Breakpoints(Simulator simulator) {
this.breakpoints = new TreeSet<>();
this.simulator = simulator;
}
public boolean contains(int address) {
return this.breakpoints.contains(address);
}
public void addBreakpoint(int address) {
this.breakpoints .add(address);
fireTableDataChanged();
}
public void removeBreakpoint(int address) {
this.breakpoints.remove(address);
fireTableDataChanged();
}
public void removeBreakpointAtIndex(int index) {
if (index < 0) {
return;
}
ArrayList<Integer> values = new ArrayList<>(breakpoints);
int value = values.get(index);
this.breakpoints.remove(value);
fireTableDataChanged();
}
public void refresh() {
fireTableDataChanged();
}
@Override
public String getColumnName(int index) {
if (index == 0) {
return "Address";
} else {
return "Inst";
}
}
@Override
public int getRowCount() {
return breakpoints.size();
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
ArrayList<Integer> values = new ArrayList<>(breakpoints);
if (columnIndex == 0) {
return "$" + Utils.wordToHex(values.get(rowIndex));
} else if (columnIndex == 1) {
int address = values.get(rowIndex);
try {
return simulator.disassembleOpAtAddress(address);
} catch (MemoryAccessException ex) {
return "???";
}
} else {
return null;
}
}
}
+46 -43
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -26,6 +26,7 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Device;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -42,7 +43,7 @@ public class Bus {
// The default address at which to load programs
public static 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;
@@ -52,17 +53,17 @@ public class Bus {
// Ordered sets of IO devices, associated with their priority
private Map<Integer, SortedSet<Device>> deviceMap;
// an array for quick lookup of adresses, brute-force style
private Device[] deviceAddressArray;
public Bus(int size) {
this(0, size - 1);
}
public Bus(int startAddress, int endAddress) {
this.deviceMap = new HashMap<Integer, SortedSet<Device>>();
this.deviceMap = new HashMap<>();
this.startAddress = startAddress;
this.endAddress = endAddress;
}
@@ -74,68 +75,70 @@ public class Bus {
public int endAddress() {
return endAddress;
}
private void buildDeviceAddressArray() {
int size = (this.endAddress - this.startAddress) + 1;
deviceAddressArray = new Device[size];
// getDevices() provides an OrderedSet with devices ordered by priorities
for(Device device : getDevices()) {
for (Device device : getDevices()) {
MemoryRange range = device.getMemoryRange();
for(int address = range.startAddress; address <= range.endAddress; ++address) {
for (int address = range.startAddress; address <= range.endAddress; ++address) {
deviceAddressArray[address - this.startAddress] = device;
}
}
}
/**
* Add a device to the bus.
*
* @param device
* @param priority
* @param device Device to add
* @param priority Bus prioirity.
* @throws MemoryRangeException
*/
public void addDevice(Device device, int priority) throws MemoryRangeException {
MemoryRange range = device.getMemoryRange();
if(range.startAddress() < this.startAddress || range.startAddress() > this.endAddress) {
if (range.startAddress() < this.startAddress || range.startAddress() > this.endAddress) {
throw new MemoryRangeException("start address of device " + device.getName() + " does not fall within the address range of the bus");
}
if(range.endAddress() < this.startAddress || range.endAddress() > this.endAddress) {
if (range.endAddress() < this.startAddress || range.endAddress() > this.endAddress) {
throw new MemoryRangeException("end address of device " + device.getName() + " does not fall within the address range of the bus");
}
SortedSet<Device> deviceSet = deviceMap.get(priority);
if(deviceSet == null) {
deviceSet = new TreeSet<Device>();
if (deviceSet == null) {
deviceSet = new TreeSet<>();
deviceMap.put(priority, deviceSet);
}
device.setBus(this);
deviceSet.add(device);
buildDeviceAddressArray();
}
/**
* Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others.
*
* @param device
* @param device Device to add
* @throws MemoryRangeException
*/
public void addDevice(Device device) throws MemoryRangeException {
addDevice(device, 0);
}
/**
* Remove a device from the bus.
*
* @param device
* @param device Device to remove
*/
public void removeDevice(Device device) {
for(SortedSet<Device> deviceSet : deviceMap.values()) {
for (SortedSet<Device> deviceSet : deviceMap.values()) {
deviceSet.remove(device);
}
buildDeviceAddressArray();
@@ -152,39 +155,39 @@ public class Bus {
* device.
*/
public boolean isComplete() {
if(deviceAddressArray == null) {
if (deviceAddressArray == null) {
buildDeviceAddressArray();
}
for(int address = startAddress; address <= endAddress; ++address) {
if(deviceAddressArray[address - startAddress] == null) {
for (int address = startAddress; address <= endAddress; ++address) {
if (deviceAddressArray[address - startAddress] == null) {
return false;
}
}
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) {
if (d != null) {
MemoryRange range = d.getMemoryRange();
int devAddr = address - range.startAddress();
return d.read(devAddr);
return d.read(devAddr, cpuAccess) & 0xff;
}
throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address));
}
public void write(int address, int value) throws MemoryAccessException {
Device d = deviceAddressArray[address - this.startAddress];
if(d != null) {
if (d != null) {
MemoryRange range = d.getMemoryRange();
int devAddr = address - range.startAddress();
d.write(devAddr, value);
return;
}
throw new MemoryAccessException("Bus write failed. No device at address " + String.format("$%04X", address));
}
@@ -214,18 +217,18 @@ public class Bus {
public SortedSet<Device> getDevices() {
// create an ordered set of devices, ordered by device priorities
SortedSet<Device> devices = new TreeSet<Device>();
List<Integer> priorities = new ArrayList<Integer>(deviceMap.keySet());
SortedSet<Device> devices = new TreeSet<>();
List<Integer> priorities = new ArrayList<>(deviceMap.keySet());
Collections.sort(priorities);
for(int priority : priorities) {
for (int priority : priorities) {
SortedSet<Device> deviceSet = deviceMap.get(priority);
for(Device device : deviceSet) {
for (Device device : deviceSet) {
devices.add(device);
}
}
return devices;
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,177 @@
package com.loomcom.symon;
import com.loomcom.symon.util.Utils;
/**
* A compact, struct-like representation of CPU state.
*/
public class CpuState {
/**
* Accumulator
*/
public int a;
/**
* X index regsiter
*/
public int x;
/**
* Y index register
*/
public int y;
/**
* Stack Pointer
*/
public int sp;
/**
* Program Counter
*/
public int pc;
/**
* Last Loaded Instruction Register
*/
public int ir;
/**
* Peek-Ahead to next IR
*/
public int nextIr;
public int[] args = new int[2];
public int[] nextArgs = new int[2];
public int instSize;
public boolean opTrap;
public boolean irqAsserted;
public boolean nmiAsserted;
public int lastPc;
/* Status Flag Register bits */
public boolean carryFlag;
public boolean negativeFlag;
public boolean zeroFlag;
public boolean irqDisableFlag;
public boolean decimalModeFlag;
public boolean breakFlag;
public boolean overflowFlag;
public long stepCounter = 0L;
public CpuState() {}
/**
* Snapshot a copy of the CpuState.
*
* (This is a copy constructor rather than an implementation of <code>Cloneable</code>
* based on Josh Bloch's recommendation)
*
* @param s The CpuState to copy.
*/
public CpuState(CpuState s) {
this.a = s.a;
this.x = s.x;
this.y = s.y;
this.sp = s.sp;
this.pc = s.pc;
this.ir = s.ir;
this.nextIr = s.nextIr;
this.lastPc = s.lastPc;
this.args[0] = s.args[0];
this.args[1] = s.args[1];
this.nextArgs[0] = s.nextArgs[0];
this.nextArgs[1] = s.nextArgs[1];
this.instSize = s.instSize;
this.opTrap = s.opTrap;
this.irqAsserted = s.irqAsserted;
this.carryFlag = s.carryFlag;
this.negativeFlag = s.negativeFlag;
this.zeroFlag = s.zeroFlag;
this.irqDisableFlag = s.irqDisableFlag;
this.decimalModeFlag = s.decimalModeFlag;
this.breakFlag = s.breakFlag;
this.overflowFlag = s.overflowFlag;
this.stepCounter = s.stepCounter;
}
/**
* Returns a string formatted for the trace log.
*
* @return a string formatted for the trace log.
*/
public String toTraceEvent() {
String opcode = Cpu.disassembleOp(ir, args);
return getInstructionByteStatus() + " " +
String.format("%-14s", opcode) +
"A:" + Utils.byteToHex(a) + " " +
"X:" + Utils.byteToHex(x) + " " +
"Y:" + Utils.byteToHex(y) + " " +
"F:" + Utils.byteToHex(getStatusFlag()) + " " +
"S:1" + Utils.byteToHex(sp) + " " +
getProcessorStatusString() + "\n";
}
/**
* @return The value of the Process Status Register, as a byte.
*/
public int getStatusFlag() {
int status = 0x20;
if (carryFlag) {
status |= Cpu.P_CARRY;
}
if (zeroFlag) {
status |= Cpu.P_ZERO;
}
if (irqDisableFlag) {
status |= Cpu.P_IRQ_DISABLE;
}
if (decimalModeFlag) {
status |= Cpu.P_DECIMAL;
}
if (breakFlag) {
status |= Cpu.P_BREAK;
}
if (overflowFlag) {
status |= Cpu.P_OVERFLOW;
}
if (negativeFlag) {
status |= Cpu.P_NEGATIVE;
}
return status;
}
public String getInstructionByteStatus() {
switch (Cpu.instructionSizes[ir]) {
case 0:
case 1:
return Utils.wordToHex(lastPc) + " " +
Utils.byteToHex(ir) + " ";
case 2:
return Utils.wordToHex(lastPc) + " " +
Utils.byteToHex(ir) + " " +
Utils.byteToHex(args[0]) + " ";
case 3:
return Utils.wordToHex(lastPc) + " " +
Utils.byteToHex(ir) + " " +
Utils.byteToHex(args[0]) + " " +
Utils.byteToHex(args[1]);
default:
return null;
}
}
/**
* @return A string representing the current status register state.
*/
public String getProcessorStatusString() {
return "[" + (negativeFlag ? 'N' : '.') +
(overflowFlag ? 'V' : '.') +
"-" +
(breakFlag ? 'B' : '.') +
(decimalModeFlag ? 'D' : '.') +
(irqDisableFlag ? 'I' : '.') +
(zeroFlag ? 'Z' : '.') +
(carryFlag ? 'C' : '.') +
"]";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2012 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -31,7 +31,7 @@ public interface InstructionTable {
*
* TODO: As of version 0.6, this is still not used! All CPUs are "idealized" NMOS 6502 only.
*/
public enum CpuBehavior {
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
@@ -46,32 +46,33 @@ public interface InstructionTable {
*
* NB: Does NOT implement "unimplemented" NMOS instructions.
*/
NMOS_WITH_INDIRECT_JMP_BUG,
/**
* Emulate an NMOS 6502 without the indirect JMP bug. This type of 6502
* does not actually exist in the wild.
*
* NB: Does NOT implement "unimplemented" NMOS instructions.
*/
NMOS_WITHOUT_INDIRECT_JMP_BUG,
NMOS_6502,
/**
* Emulate a CMOS 65C02, with all CMOS instructions and addressing modes.
*/
CMOS
CMOS_6502,
/**
* Emulate a CMOS 65C816.
*/
CMOS_65816
}
/**
* Enumeration of Addressing Modes.
*/
public enum Mode {
enum Mode {
ACC {
public String toString() {
return "Accumulator";
}
},
AIX {
public String toString() {
return "Absolute, X-Indexed Indirect";
}
},
ABS {
public String toString() {
return "Absolute";
@@ -128,19 +129,31 @@ public interface InstructionTable {
ZPG {
public String toString() {
return "Zeropage";
return "Zero Page";
}
},
ZPR {
public String toString() {
return "Zero Page, Relative";
}
},
ZPX {
public String toString() {
return "Zeropage, X-indexed";
return "Zero Page, X-indexed";
}
},
ZPY {
public String toString() {
return "Zeropage, Y-indexed";
return "Zero Page, Y-indexed";
}
},
ZPI {
public String toString() {
return "Zero Page Indirect";
}
},
@@ -154,156 +167,185 @@ public interface InstructionTable {
// 6502 opcodes. No 65C02 opcodes implemented.
/**
* Instruction opcode names.
* Instruction opcode names. This lists all opcodes for
* NMOS 6502, CMOS 65C02, and CMOS 65C816
*/
public static final String[] opcodeNames = {
"BRK", "ORA", null, null, null, "ORA", "ASL", null,
"PHP", "ORA", "ASL", null, null, "ORA", "ASL", null,
"BPL", "ORA", null, null, null, "ORA", "ASL", null,
"CLC", "ORA", null, null, null, "ORA", "ASL", null,
"JSR", "AND", null, null, "BIT", "AND", "ROL", null,
"PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null,
"BMI", "AND", null, null, null, "AND", "ROL", null,
"SEC", "AND", null, null, null, "AND", "ROL", null,
"RTI", "EOR", null, null, null, "EOR", "LSR", null,
"PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null,
"BVC", "EOR", null, null, null, "EOR", "LSR", null,
"CLI", "EOR", null, null, null, "EOR", "LSR", null,
"RTS", "ADC", null, null, null, "ADC", "ROR", null,
"PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null,
"BVS", "ADC", null, null, null, "ADC", "ROR", null,
"SEI", "ADC", null, null, null, "ADC", "ROR", null,
"BCS", "STA", null, null, "STY", "STA", "STX", null,
"DEY", null, "TXA", null, "STY", "STA", "STX", null,
"BCC", "STA", null, null, "STY", "STA", "STX", null,
"TYA", "STA", "TXS", null, null, "STA", null, null,
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null,
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null,
"BCS", "LDA", null, null, "LDY", "LDA", "LDX", null,
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null,
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null,
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null,
"BNE", "CMP", null, null, null, "CMP", "DEC", null,
"CLD", "CMP", null, null, null, "CMP", "DEC", null,
"CPX", "SBC", null, null, "CPX", "SBC", "INC", null,
"INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null,
"BEQ", "SBC", null, null, null, "SBC", "INC", null,
"SED", "SBC", null, null, null, "SBC", "INC", null
String[] opcodeNames = {
"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
};
/**
* Instruction addressing modes.
* Instruction addressing modes. This table includes sizes
* for all instructions for NMOS 6502, CMOS 65C02,
* and CMOS 65C816
*/
public static final Mode[] instructionModes = {
Mode[] instructionModes = {
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
Mode.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.REL, Mode.INY, Mode.NUL, 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.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x0c-0x0f
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x10-0x13
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.REL, Mode.INY, Mode.NUL, 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.ABS, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x2c-0x2f
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x30-0x33
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.REL, Mode.INY, Mode.NUL, 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.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.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.REL, Mode.INY, Mode.NUL, 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.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
Mode.IND, Mode.ABS, Mode.ABS, Mode.ZPR, // 0x6c-0x6f
Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x70-0x73
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.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
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.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.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
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.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.REL, Mode.INY, Mode.NUL, 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.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.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.REL, Mode.INY, Mode.NUL, 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.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.ZPG, // 0xf4-0xf7
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xf8-0xfb
Mode.NUL, Mode.ABX, Mode.ABX, Mode.ZPR // 0xfc-0xff
};
/**
* Size, in bytes, required for each instruction.
* Size, in bytes, required for each instruction. This table
* includes sizes for all instructions for NMOS 6502, CMOS 65C02,
* and CMOS 65C816
*/
public static final int[] instructionSizes = {
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
int[] instructionSizes = {
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
};
/**
* Number of clock cycles required for each instruction
* Number of clock cycles required for each instruction when
* in NMOS mode.
*/
public static final int[] instructionClocks = {
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0
int[] instructionClocksNmos = {
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
};
/**
* Number of clock cycles required for each instruction when
* in CMOS mode
*/
int[] instructionClocksCmos = {
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
};
}
+16 -11
View File
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -39,7 +39,7 @@ public class Main {
* Main entry point to the simulator. Creates a simulator and shows the main
* window.
*
* @param args
* @param args Program arguments
*/
public static void main(String args[]) throws Exception {
@@ -48,12 +48,16 @@ public class Main {
String arg = args[i].toLowerCase(Locale.ENGLISH);
if(arg.equals("-machine") && (i+1) < args.length) {
String machine = args[i+1].trim().toLowerCase(Locale.ENGLISH);
if(machine.equals("symon")) {
machineClass = SymonMachine.class;
} else if(machine.equals("multicomp")) {
machineClass = MulticompMachine.class;
} else if (machine.equals("simple")) {
machineClass = SimpleMachine.class;
switch (machine) {
case "symon":
machineClass = SymonMachine.class;
break;
case "multicomp":
machineClass = MulticompMachine.class;
break;
case "simple":
machineClass = SimpleMachine.class;
break;
}
}
}
@@ -83,6 +87,7 @@ public class Main {
final Simulator simulator = new Simulator(machineClass);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
@@ -95,8 +100,8 @@ public class Main {
});
Simulator.MAIN_CMD cmd = simulator.waitForCommand();
if (cmd.equals(Simulator.MAIN_CMD.SELECTMACHINE)) {
Simulator.MainCommand cmd = simulator.waitForCommand();
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
machineClass = null;
} else {
break;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -34,77 +34,78 @@ import com.loomcom.symon.exceptions.*;
*/
public class MemoryRange implements Comparable<MemoryRange> {
/** The starting address of the memory range. */
public int startAddress;
/** The ending address of the memory range. */
public int endAddress;
/**
* The starting address of the memory range.
*/
public int startAddress;
/**
* The ending address of the memory range.
*/
public int endAddress;
public MemoryRange(int startAddress, int endAddress)
throws MemoryRangeException {
if (startAddress < 0 || endAddress < 0) {
throw new MemoryRangeException("Addresses cannot be less than 0.");
public MemoryRange(int startAddress, int endAddress)
throws MemoryRangeException {
if (startAddress < 0 || endAddress < 0) {
throw new MemoryRangeException("Addresses cannot be less than 0.");
}
if (startAddress >= endAddress) {
throw new MemoryRangeException("End address must be greater " +
"than start address.");
}
this.startAddress = startAddress;
this.endAddress = endAddress;
}
if (startAddress >= endAddress) {
throw new MemoryRangeException("End address must be greater " +
"than start address.");
/**
* @return the starting address.
*/
public int startAddress() {
return startAddress;
}
this.startAddress = startAddress;
this.endAddress = endAddress;
}
/**
* @returns the starting address.
*/
public int startAddress() {
return startAddress;
}
/**
* @returns the ending address.
*/
public int endAddress() {
return endAddress;
}
/**
* Checks for address inclusion in the range.
*
* @returns true if the address is included within this range,
* false otherwise.
*/
public boolean includes(int address) {
return (address <= endAddress &&
address >= startAddress);
}
/**
* Checks for overlapping memory ranges.
*
* @returns true if this range overlaps in any way with the other.
*/
public boolean overlaps(MemoryRange other) {
return (this.includes(other.startAddress()) ||
other.includes(this.startAddress()));
}
// Implementation of Comparable interface
public int compareTo(MemoryRange other) {
if (other == null) {
throw new NullPointerException("Cannot compare to null.");
/**
* @return the ending address.
*/
public int endAddress() {
return endAddress;
}
if (this == other) {
return 0;
}
Integer thisStartAddr = new Integer(this.startAddress());
Integer thatStartAddr = new Integer(other.startAddress());
return thisStartAddr.compareTo(thatStartAddr);
}
public String toString() {
StringBuffer desc = new StringBuffer("@");
desc.append(String.format("0x%04x", startAddress));
desc.append("-");
desc.append(String.format("0x%04x", endAddress));
return desc.toString();
}
/**
* Checks for address inclusion in the range.
*
* @return true if the address is included within this range,
* false otherwise.
*/
public boolean includes(int address) {
return (address <= endAddress &&
address >= startAddress);
}
/**
* Checks for overlapping memory ranges.
*
* @return true if this range overlaps in any way with the other.
*/
public boolean overlaps(MemoryRange other) {
return (this.includes(other.startAddress()) ||
other.includes(this.startAddress()));
}
// Implementation of Comparable interface
public int compareTo(MemoryRange other) {
if (other == null) {
throw new NullPointerException("Cannot compare to null.");
}
if (this == other) {
return 0;
}
Integer thisStartAddr = this.startAddress();
Integer thatStartAddr = other.startAddress();
return thisStartAddr.compareTo(thatStartAddr);
}
public String toString() {
return "@" + String.format("0x%04x", startAddress) + "-" +
String.format("0x%04x", endAddress);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -27,15 +27,15 @@ import javax.swing.*;
public interface Preferences {
public static final int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
public static final boolean DEFAULT_HALT_ON_BREAK = true;
boolean DEFAULT_HALT_ON_BREAK = true;
public JDialog getDialog();
JDialog getDialog();
public int getProgramStartAddress();
int getProgramStartAddress();
public boolean getHaltOnBreak();
boolean getHaltOnBreak();
public void updateUi();
void updateUi();
}
+262 -131
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyrighi (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -24,21 +24,20 @@
package com.loomcom.symon;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import com.loomcom.symon.exceptions.SymonException;
import com.loomcom.symon.exceptions.*;
import com.loomcom.symon.machines.Machine;
import com.loomcom.symon.ui.*;
import com.loomcom.symon.ui.Console;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.SortedSet;
import java.util.TreeSet;
/**
* Symon Simulator Interface and Control.
@@ -50,10 +49,15 @@ import java.util.logging.Logger;
*/
public class Simulator {
private final static Logger logger = LoggerFactory.getLogger(Simulator.class.getName());
// UI constants
private static final int DEFAULT_FONT_SIZE = 12;
private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
private static final int CONSOLE_BORDER_WIDTH = 10;
private static final int DEFAULT_FONT_SIZE = 12;
private static final Font DEFAULT_FONT = new Font(Font.MONOSPACED, Font.PLAIN, DEFAULT_FONT_SIZE);
private static final int CONSOLE_BORDER_WIDTH = 10;
// Clock periods, in NS, for each speed. 0MHz, 1MHz, 2MHz, 3MHz, 4MHz, 5MHz, 6MHz, 7MHz, 8MHz.
private static final long[] CLOCK_PERIODS = {0, 1000, 500, 333, 250, 200, 167, 143, 125};
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
// to refresh the status view on every simulated clock cycle. Instead, we will only refresh the status view
@@ -66,14 +70,12 @@ public class Simulator {
//
private static final int MAX_STEPS_BETWEEN_UPDATES = 20000;
private final static Logger logger = Logger.getLogger(Simulator.class.getName());
// The simulated machine
private Machine machine;
// Number of CPU steps between CRT repaints.
// TODO: Dynamically refresh the value at runtime based on performance figures to reach ~ 30fps.
private long stepsBetweenCrtcRefreshes = 2500;
private static final long STEPS_BETWEEN_CRTC_REFRESHES = 2500;
// A counter to keep track of the number of UI updates that have been
// requested
@@ -93,44 +95,61 @@ public class Simulator {
/**
* The Trace Window shows the most recent 50,000 CPU states.
*/
private TraceLog traceLog;
private final TraceLog traceLog;
/**
* The Memory Window shows the contents of one page of memory.
*/
private MemoryWindow memoryWindow;
private final MemoryWindow memoryWindow;
private VideoWindow videoWindow;
private final VideoWindow videoWindow;
private final BreakpointsWindow breakpointsWindow;
private SimulatorMenu menuBar;
private RunLoop runLoop;
private Console console;
private RunLoop runLoop;
private Console console;
private StatusPanel statusPane;
private JButton runStopButton;
private JButton stepButton;
private JButton softResetButton;
private JButton hardResetButton;
private JComboBox<String> stepCountBox;
private JFileChooser fileChooser;
private JFileChooser fileChooser;
private PreferencesDialog preferences;
private Breakpoints breakpoints;
private final Object commandMonitorObject = new Object();
private MAIN_CMD command = MAIN_CMD.NONE;
public static enum MAIN_CMD {
private MainCommand command = MainCommand.NONE;
public enum MainCommand {
NONE,
SELECTMACHINE
}
/**
* The list of step counts that will appear in the "Step" drop-down.
*/
private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"};
public Simulator(Class machineClass) throws Exception {
this.breakpoints = new Breakpoints(this);
this.machine = (Machine) machineClass.getConstructors()[0].newInstance();
// Initialize final fields in the constructor.
this.traceLog = new TraceLog();
this.memoryWindow = new MemoryWindow(machine.getBus());
this.breakpointsWindow = new BreakpointsWindow(breakpoints, mainWindow);
if (machine.getCrtc() != null) {
videoWindow = new VideoWindow(machine.getCrtc(), 2, 2);
} else {
videoWindow = null;
}
}
/**
@@ -143,7 +162,7 @@ public class Simulator {
mainWindow.getContentPane().setLayout(new BorderLayout());
// UI components used for I/O.
this.console = new com.loomcom.symon.ui.Console(80, 25, DEFAULT_FONT);
this.console = new com.loomcom.symon.ui.Console(80, 25, DEFAULT_FONT, false);
this.statusPane = new StatusPanel(machine);
console.setBorderWidth(CONSOLE_BORDER_WIDTH);
@@ -162,11 +181,12 @@ public class Simulator {
runStopButton = new JButton("Run");
stepButton = new JButton("Step");
softResetButton = new JButton("Soft Reset");
hardResetButton = new JButton("Hard Reset");
JButton softResetButton = new JButton("Soft Reset");
JButton hardResetButton = new JButton("Hard Reset");
stepCountBox = new JComboBox<String>(STEPS);
stepCountBox = new JComboBox<>(STEPS);
stepCountBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
try {
JComboBox cb = (JComboBox) actionEvent.getSource();
@@ -195,47 +215,40 @@ public class Simulator {
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
runStopButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (runLoop != null && runLoop.isRunning()) {
handleStop();
Simulator.this.handleStop();
} else {
handleStart();
Simulator.this.handleStart();
}
}
});
stepButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
handleStep(stepsPerClick);
Simulator.this.handleStep(stepsPerClick);
}
});
softResetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset.
handleReset(false);
Simulator.this.handleReset(false);
}
});
hardResetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset.
handleReset(true);
Simulator.this.handleReset(true);
}
});
// Prepare the log window
traceLog = new TraceLog();
// Prepare the memory window
memoryWindow = new MemoryWindow(machine.getBus());
// Composite Video and 6545 CRTC
if(machine.getCrtc() != null) {
videoWindow = new VideoWindow(machine.getCrtc(), 2, 2);
}
mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
// The Menu. This comes last, because it relies on other components having
// already been initialized.
@@ -248,9 +261,9 @@ public class Simulator {
console.requestFocus();
handleReset(false);
}
public MAIN_CMD waitForCommand() {
synchronized(commandMonitorObject) {
public MainCommand waitForCommand() {
synchronized (commandMonitorObject) {
try {
commandMonitorObject.wait();
} catch (InterruptedException ex) {
@@ -259,7 +272,7 @@ public class Simulator {
}
return command;
}
private void handleStart() {
// Shift focus to the console.
@@ -287,7 +300,7 @@ public class Simulator {
}
try {
logger.log(Level.INFO, "Reset requested. Resetting CPU.");
logger.debug("Reset requested. Resetting CPU.");
// Reset CPU
machine.getCpu().reset();
// Clear the console.
@@ -302,15 +315,9 @@ public class Simulator {
}
}
// Update status.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState();
memoryWindow.updateState();
}
});
updateVisibleState();
} catch (MemoryAccessException ex) {
logger.log(Level.SEVERE, "Exception during simulator reset: " + ex.getMessage());
logger.error("Exception during simulator reset", ex);
}
}
@@ -322,17 +329,9 @@ public class Simulator {
for (int i = 0; i < numSteps; i++) {
step();
}
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (traceLog.isVisible()) {
traceLog.refresh();
}
statusPane.updateState();
memoryWindow.updateState();
}
});
updateVisibleState();
} catch (SymonException ex) {
logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage());
logger.error("Exception during simulator step", ex);
ex.printStackTrace();
}
}
@@ -349,7 +348,7 @@ 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();
}
@@ -359,27 +358,23 @@ public class Simulator {
machine.getAcia().rxWrite((int) console.readInputChar());
}
} catch (FifoUnderrunException ex) {
logger.severe("Console type-ahead buffer underrun!");
logger.error("Console type-ahead buffer underrun!");
}
if (videoWindow != null && stepsSinceLastCrtcRefresh++ > stepsBetweenCrtcRefreshes) {
if (videoWindow != null && stepsSinceLastCrtcRefresh++ > STEPS_BETWEEN_CRTC_REFRESHES) {
stepsSinceLastCrtcRefresh = 0;
if (videoWindow.isVisible()) {
videoWindow.repaint();
}
}
// This is a very expensive update, and we're doing it without
// a delay, so we don't want to overwhelm the Swing event processing thread
// with requests. Limit the number of ui updates that can be performed.
if (stepsSinceLastUpdate++ > MAX_STEPS_BETWEEN_UPDATES) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState();
memoryWindow.updateState();
}
});
updateVisibleState();
stepsSinceLastUpdate = 0;
}
}
/**
@@ -391,8 +386,7 @@ public class Simulator {
machine.getBus().write(addr++, program[i] & 0xff);
}
logger.log(Level.INFO, "Loaded " + i + " bytes at address 0x" +
Integer.toString(startAddress, 16));
logger.info("Loaded {} bytes at address 0x{}", i, Integer.toString(startAddress, 16));
// After loading, be sure to reset and
// Reset (but don't clear memory, naturally)
@@ -402,16 +396,9 @@ public class Simulator {
machine.getCpu().setProgramCounter(preferences.getProgramStartAddress());
// Immediately update the UI.
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// Now update the state
statusPane.updateState();
memoryWindow.updateState();
}
});
updateVisibleState();
}
/**
* The main run thread.
*/
@@ -427,10 +414,11 @@ public class Simulator {
}
public void run() {
logger.log(Level.INFO, "Starting main run loop.");
logger.debug("Starting main run loop.");
isRunning = true;
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Don't allow step while the simulator is running
stepButton.setEnabled(false);
@@ -446,11 +434,11 @@ public class Simulator {
step();
} while (shouldContinue());
} catch (SymonException ex) {
logger.log(Level.SEVERE, "Exception in main simulator run thread. Exiting run.");
ex.printStackTrace();
logger.error("Exception in main simulator run thread. Exiting run.", ex);
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
statusPane.updateState();
memoryWindow.updateState();
@@ -469,15 +457,19 @@ public class Simulator {
}
/**
* Returns true if the run loop should proceed to the next step.
*
* @return True if the run loop should proceed to the next step.
*/
private boolean shouldContinue() {
return isRunning && !(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
return !breakpoints.contains(machine.getCpu().getProgramCounter()) &&
isRunning &&
!(preferences.getHaltOnBreak() && machine.getCpu().getInstruction() == 0x00);
}
}
public String disassembleOpAtAddress(int address) throws MemoryAccessException {
return machine.getCpu().disassembleOpAtAddress(address);
}
class LoadProgramAction extends AbstractAction {
public LoadProgramAction() {
super("Load Program...", null);
@@ -508,14 +500,17 @@ public class Simulator {
program[i++] = dis.readByte();
}
// Now load the program at the starting address.
loadProgram(program, preferences.getProgramStartAddress());
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
console.reset();
breakpoints.refresh();
}
});
// Now load the program at the starting address.
loadProgram(program, preferences.getProgramStartAddress());
// TODO: "Don't Show Again" checkbox
JOptionPane.showMessageDialog(mainWindow,
"Loaded Successfully At " +
@@ -526,10 +521,10 @@ public class Simulator {
}
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to read program file: " + ex.getMessage());
logger.error("Unable to read program file.", ex);
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
} catch (MemoryAccessException ex) {
logger.log(Level.SEVERE, "Memory access error loading program: " + ex.getMessage());
logger.error("Memory access error loading program", ex);
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
}
}
@@ -552,34 +547,39 @@ public class Simulator {
if (fileSize != machine.getRomSize()) {
throw new IOException("ROM file must be exactly " + String.valueOf(machine.getRomSize()) + " bytes.");
} else {
// Load the new ROM image
Memory rom = Memory.makeROM(machine.getRomBase(), machine.getRomBase() + machine.getRomSize() - 1, romFile);
machine.setRom(rom);
// Now, reset
machine.getCpu().reset();
logger.log(Level.INFO, "ROM File `" + romFile.getName() + "' loaded at " +
String.format("0x%04X", machine.getRomBase()));
// TODO: "Don't Show Again" checkbox
JOptionPane.showMessageDialog(mainWindow,
"Loaded Successfully At " +
String.format("$%04X", machine.getRomBase()),
"OK",
JOptionPane.PLAIN_MESSAGE);
}
// Load the new ROM image
Memory rom = Memory.makeROM(machine.getRomBase(), machine.getRomBase() + machine.getRomSize() - 1, romFile);
machine.setRom(rom);
// Now, reset
machine.getCpu().reset();
updateVisibleState();
// Refresh breakpoints to show new memory contents.
breakpoints.refresh();
logger.info("ROM File `{}' loaded at {}", romFile.getName(),
String.format("0x%04X", machine.getRomBase()));
// TODO: "Don't Show Again" checkbox
JOptionPane.showMessageDialog(mainWindow,
"Loaded Successfully At " +
String.format("$%04X", machine.getRomBase()),
"OK",
JOptionPane.PLAIN_MESSAGE);
}
}
} catch (IOException ex) {
logger.log(Level.SEVERE, "Unable to read ROM file: " + ex.getMessage());
logger.error("Unable to read ROM file: {}", ex.getMessage());
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
} catch (MemoryRangeException ex) {
logger.log(Level.SEVERE, "Memory range error while loading ROM file: " + ex.getMessage());
logger.error("Memory range error while loading ROM file: {}", ex.getMessage());
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
} catch (MemoryAccessException ex) {
logger.log(Level.SEVERE, "Memory access error while loading ROM file: " + ex.getMessage());
logger.error("Memory access error while loading ROM file: {}", ex.getMessage());
JOptionPane.showMessageDialog(mainWindow, ex.getMessage(), "Failure", JOptionPane.ERROR_MESSAGE);
}
}
@@ -596,7 +596,7 @@ public class Simulator {
preferences.getDialog().setVisible(true);
}
}
class SelectMachineAction extends AbstractAction {
public SelectMachineAction() {
super("Switch emulated machine...", null);
@@ -605,19 +605,19 @@ public class Simulator {
}
public void actionPerformed(ActionEvent actionEvent) {
if(runLoop != null) {
if (runLoop != null) {
runLoop.requestStop();
}
memoryWindow.dispose();
traceLog.dispose();
if(videoWindow != null) {
if (videoWindow != null) {
videoWindow.dispose();
}
mainWindow.dispose();
command = MAIN_CMD.SELECTMACHINE;
synchronized(commandMonitorObject) {
command = MainCommand.SELECTMACHINE;
synchronized (commandMonitorObject) {
commandMonitorObject.notifyAll();
}
}
@@ -645,11 +645,12 @@ public class Simulator {
public SetFontAction(int size) {
super(Integer.toString(size) + " pt", null);
this.size = size;
putValue(SHORT_DESCRIPTION, "Set font to " + Integer.toString(size) + "pt.");
putValue(SHORT_DESCRIPTION, "Set font to " + size + "pt.");
}
public void actionPerformed(ActionEvent actionEvent) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
console.setFont(new Font("Monospaced", Font.PLAIN, size));
mainWindow.pack();
@@ -658,6 +659,40 @@ public class Simulator {
}
}
class SetSpeedAction extends AbstractAction {
private int speed;
public SetSpeedAction(int speed) {
super(Integer.toString(speed) + " MHz", null);
this.speed = speed;
putValue(SHORT_DESCRIPTION, "Set simulated speed to " + speed + " MHz.");
}
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (speed < 1 || speed > CLOCK_PERIODS.length - 1) {
return;
}
machine.getCpu().setClockPeriodInNs(CLOCK_PERIODS[speed]);
}
}
class SetCpuAction extends AbstractAction {
private 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);
@@ -710,6 +745,23 @@ public class Simulator {
}
}
class ToggleBreakpointWindowAction extends AbstractAction {
public ToggleBreakpointWindowAction() {
super("Breakpoints...", null);
putValue(SHORT_DESCRIPTION, "Show or Hide Breakpoints");
}
public void actionPerformed(ActionEvent actionEvent) {
synchronized (breakpointsWindow) {
if (breakpointsWindow.isVisible()) {
breakpointsWindow.setVisible(false);
} else {
breakpointsWindow.setVisible(true);
}
}
}
}
class SimulatorMenu extends JMenuBar {
// Menu Items
private JMenuItem loadProgramItem;
@@ -762,9 +814,6 @@ public class Simulator {
JMenuItem prefsItem = new JMenuItem(new ShowPrefsAction());
fileMenu.add(prefsItem);
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
fileMenu.add(selectMachineItem);
JMenuItem quitItem = new JMenuItem(new QuitAction());
fileMenu.add(quitItem);
@@ -810,7 +859,7 @@ public class Simulator {
});
viewMenu.add(showMemoryTable);
if(videoWindow != null) {
if (videoWindow != null) {
final JCheckBoxMenuItem showVideoWindow = new JCheckBoxMenuItem(new ToggleVideoWindowAction());
videoWindow.addWindowListener(new WindowAdapter() {
@Override
@@ -822,16 +871,98 @@ public class Simulator {
}
add(viewMenu);
/*
* Simulator Menu
*/
JMenu simulatorMenu = new JMenu("Simulator");
// "Select Machine..." item.
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
simulatorMenu.add(selectMachineItem);
// "CPU" sub-menu
JMenu cpuTypeMenu = new JMenu("CPU");
ButtonGroup cpuGroup = new ButtonGroup();
makeCpuMenuItem("NMOS 6502", Cpu.CpuBehavior.NMOS_6502, cpuTypeMenu, cpuGroup);
makeCpuMenuItem("CMOS 65C02", Cpu.CpuBehavior.CMOS_6502, cpuTypeMenu, cpuGroup);
// "Clock Speed" sub-menu
JMenu speedSubMenu = new JMenu("Clock Speed");
ButtonGroup speedGroup = new ButtonGroup();
makeSpeedMenuItem(1, speedSubMenu, speedGroup);
makeSpeedMenuItem(2, speedSubMenu, speedGroup);
makeSpeedMenuItem(4, speedSubMenu, speedGroup);
makeSpeedMenuItem(8, speedSubMenu, speedGroup);
simulatorMenu.add(speedSubMenu);
simulatorMenu.add(cpuTypeMenu);
// "Breakpoints"
final JCheckBoxMenuItem showBreakpoints = new JCheckBoxMenuItem(new ToggleBreakpointWindowAction());
// Un-check the menu item if the user closes the window directly
breakpointsWindow.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
showBreakpoints.setSelected(false);
}
});
simulatorMenu.add(showBreakpoints);
add(simulatorMenu);
}
private void makeFontSizeMenuItem(int size, JMenu fontSubMenu, ButtonGroup group) {
private void makeFontSizeMenuItem(int size, JMenu subMenu, ButtonGroup group) {
Action action = new SetFontAction(size);
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
item.setSelected(size == DEFAULT_FONT_SIZE);
fontSubMenu.add(item);
subMenu.add(item);
group.add(item);
}
private void makeSpeedMenuItem(int speed, JMenu subMenu, ButtonGroup group) {
if (speed < 1 || speed > CLOCK_PERIODS.length - 1) {
return;
}
Action action = new SetSpeedAction(speed);
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
item.setSelected(CLOCK_PERIODS[speed] == Cpu.DEFAULT_CLOCK_PERIOD_IN_NS);
subMenu.add(item);
group.add(item);
}
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();
}
}
});
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -33,7 +33,7 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
public abstract class Acia extends Device {
private 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;
@@ -82,6 +83,7 @@ public abstract class Acia extends Device {
/**
* @return The simulated baud rate in bps.
*/
@SuppressWarnings("unused")
public int getBaudRate() {
return baudRate;
}
@@ -99,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;
}
@@ -153,6 +160,7 @@ public abstract class Acia extends Device {
/**
* @return true if there is character data in the RX register.
*/
@SuppressWarnings("unused")
public boolean hasRxChar() {
return rxFull;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -26,7 +26,6 @@ package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
/**
* This is a simulation of the MOS 6551 ACIA, with limited
* functionality. Interrupts are not supported.
@@ -45,7 +44,6 @@ public class Acia6551 extends Acia {
static final int CMND_REG = 2;
static final int CTRL_REG = 3;
/**
* Registers. These are ignored in the current implementation.
*/
@@ -58,12 +56,12 @@ public class Acia6551 extends Acia {
}
@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:
@@ -106,7 +104,7 @@ public class Acia6551 extends Acia {
/**
* Set the control register and associated state.
*
* @param data
* @param data Data to write into the control register
*/
private void setControlRegister(int data) {
controlRegister = data;
@@ -180,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;
@@ -192,6 +190,14 @@ public class Acia6551 extends Acia {
if (overrun) {
stat |= 0x04;
}
if (interrupt) {
stat |= 0x80;
}
if (cpuAccess) {
interrupt = false;
}
return stat;
}
@@ -203,6 +209,7 @@ public class Acia6551 extends Acia {
rxFull = false;
receiveIrqEnabled = false;
transmitIrqEnabled = false;
interrupt = false;
}
}
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* 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.
*
* 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
@@ -41,32 +41,24 @@ public class Acia6850 extends Acia {
public static final int ACIA_SIZE = 2;
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
/**
* Registers. These are ignored in the current implementation.
*/
private int commandRegister;
public Acia6850(int address) throws MemoryRangeException {
super(address, ACIA_SIZE, "ACIA6850");
setBaudRate(2400);
}
@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.");
@@ -86,19 +78,17 @@ public class Acia6850 extends Acia {
throw new MemoryAccessException("No register.");
}
}
private void setCommandRegister(int data) {
commandRegister = data;
// Bits 0 & 1 control the master reset
if((commandRegister & 0x01) != 0 && (commandRegister & 0x02) != 0) {
if((data & 0x01) != 0 && (data & 0x02) != 0) {
reset();
}
// Bit 7 controls receiver IRQ behavior
receiveIrqEnabled = (commandRegister & 0x80) != 0;
receiveIrqEnabled = (data & 0x80) != 0;
// Bits 5 & 6 controls transmit IRQ behavior
transmitIrqEnabled = (commandRegister & 0x20) != 0 && (commandRegister & 0x40) == 0;
transmitIrqEnabled = (data & 0x20) != 0 && (data & 0x40) == 0;
}
@@ -107,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;
@@ -119,7 +109,14 @@ public class Acia6850 extends Acia {
if (overrun) {
stat |= 0x20;
}
if (interrupt) {
stat |= 0x80;
}
if (cpuAccess) {
interrupt = false;
}
return stat;
}
@@ -128,6 +125,7 @@ public class Acia6850 extends Acia {
overrun = false;
rxFull = false;
txEmpty = true;
interrupt = false;
}
}
@@ -1,20 +1,37 @@
/*
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import java.io.IOException;
import java.util.logging.Logger;
/**
* Simulation of a 6545 CRTC and virtual CRT output.
*/
public class Crtc extends Device {
private static final Logger logger = Logger.getLogger(Crtc.class.getName());
// Memory locations in the CRTC address space
public static final int REGISTER_SELECT = 0;
public static final int REGISTER_RW = 1;
@@ -65,6 +82,11 @@ public class Crtc extends Device {
private int currentRegister = 0;
// Status bits
private boolean rowColumnAddressing = false;
private boolean displayEnableSkew = false;
private boolean cursorSkew = false;
private Memory memory;
public Crtc(int deviceAddress, Memory memory) throws MemoryRangeException, IOException {
@@ -97,7 +119,7 @@ public class Crtc extends Device {
}
@Override
public int read(int address) throws MemoryAccessException {
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
switch (address) {
case REGISTER_RW:
switch (currentRegister) {
@@ -118,8 +140,9 @@ public class Crtc extends Device {
return null;
}
public int[] getDmaAccess() {
return memory.getDmaAccess();
public int getCharAtAddress(int address) throws MemoryAccessException {
// TODO: Row/Column addressing
return memory.read(address, false);
}
public int getHorizontalDisplayed() {
@@ -162,6 +185,18 @@ public class Crtc extends Device {
return pageSize;
}
public boolean getRowColumnAddressing() {
return rowColumnAddressing;
}
public boolean getDisplayEnableSkew() {
return displayEnableSkew;
}
public boolean getCursorSkew() {
return cursorSkew;
}
private void setCurrentRegister(int registerNumber) {
this.currentRegister = registerNumber;
}
@@ -180,7 +215,9 @@ public class Crtc extends Device {
pageSize = horizontalDisplayed * verticalDisplayed;
break;
case MODE_CONTROL:
// TODO: Implement multiple addressing modes and cursor skew.
rowColumnAddressing = (data & 0x04) != 0;
displayEnableSkew = (data & 0x10) != 0;
cursorSkew = (data & 0x20) != 0;
break;
case SCAN_LINE:
scanLinesPerRow = data;
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -62,22 +62,17 @@ public abstract class Device implements Comparable<Device> {
*/
private Set<DeviceChangeListener> deviceChangeListeners;
public Device(int startAddress, int endAddress, String name)
throws MemoryRangeException {
public Device(int startAddress, int endAddress, String name) throws MemoryRangeException {
this.memoryRange = new MemoryRange(startAddress, endAddress);
this.size = endAddress - startAddress + 1;
this.name = name;
this.deviceChangeListeners = new HashSet<DeviceChangeListener>();
}
public Device(int startAddress, int endAddress) throws MemoryRangeException {
this(startAddress, endAddress, null);
this.deviceChangeListeners = new HashSet<>();
}
/* 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();
@@ -97,6 +92,7 @@ public abstract class Device implements Comparable<Device> {
return memoryRange.endAddress();
}
@SuppressWarnings("unused")
public int startAddress() {
return memoryRange.startAddress();
}
@@ -105,6 +101,7 @@ public abstract class Device implements Comparable<Device> {
return name;
}
@SuppressWarnings("unused")
public void setName(String name) {
this.name = name;
}
@@ -118,8 +115,8 @@ public abstract class Device implements Comparable<Device> {
}
public void notifyListeners() {
for (DeviceChangeListener l : deviceChangeListeners) {
l.deviceStateChanged();
for (DeviceChangeListener listener : deviceChangeListeners) {
listener.deviceStateChanged();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -24,5 +24,5 @@
package com.loomcom.symon.devices;
public interface DeviceChangeListener {
public void deviceStateChanged();
void deviceStateChanged();
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -28,8 +28,6 @@ import java.util.*;
import com.loomcom.symon.exceptions.*;
import javax.swing.*;
public class Memory extends Device {
private boolean readOnly;
@@ -57,8 +55,7 @@ public class Memory extends Device {
}
public static Memory makeRAM(int startAddress, int endAddress) throws MemoryRangeException {
Memory memory = new Memory(startAddress, endAddress, false);
return memory;
return new Memory(startAddress, endAddress, false);
}
public void write(int address, int data) throws MemoryAccessException {
@@ -97,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];
}
@@ -108,8 +105,4 @@ public class Memory extends Device {
public String toString() {
return "Memory: " + getMemoryRange().toString();
}
public int[] getDmaAccess() {
return mem;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -23,45 +23,19 @@
package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
public class Via extends Device {
public static final int VIA_SIZE = 16;
public abstract class Pia extends Device {
private static final int ORB = 0;
private static final int ORA = 1;
private static final int DDRB = 2;
private static final int DDRA = 3;
private static final int T1C_L = 4;
private static final int T1C_H = 5;
private static final int T1L_L = 6;
private static final int T1L_H = 7;
private static final int T2C_L = 8;
private static final int T2C_H = 9;
private static final int SR = 10;
private static final int ACR = 11;
private static final int PCR = 12;
private static final int IFR = 13;
private static final int IER = 14;
private static final int ORA_H = 15;
private final String name;
public Via(int address) throws MemoryRangeException {
super(address, address + VIA_SIZE - 1, "VIA");
}
@Override
public void write(int address, int data) throws MemoryAccessException {
}
@Override
public int read(int address) throws MemoryAccessException {
return 0;
public Pia(int startAddress, int endAddress, String name) throws MemoryRangeException {
super(startAddress, endAddress, name);
this.name = name;
}
@Override
public String toString() {
return null;
return name;
}
}
@@ -0,0 +1,210 @@
/*
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Emulation for the SD-card controller of the MULTICOMP system.
* Neiter comlete nor correct.
*/
public class SdController extends Device {
private enum Status {
IDLE,
READ,
WRITE
}
public static final int CONTROLLER_SIZE = 8;
private final int SECTOR_SIZE = 512;
private final static Logger logger = Logger.getLogger(SdController.class.getName());
private File sdImageFile;
private int lba0, lba1, lba2;
private int position;
private Status status = Status.IDLE;
private final byte[] readBuffer = new byte[SECTOR_SIZE];
private final byte[] writeBuffer = new byte[SECTOR_SIZE];
private int readPosition = 0;
private int writePosition = 0;
public SdController(int address) throws MemoryRangeException {
super(address, address + CONTROLLER_SIZE - 1, "SDCONTROLLER");
sdImageFile = new File("sd.img");
if (!sdImageFile.exists()) {
sdImageFile = null;
logger.log(Level.INFO, "Could not find SD card image 'sd.img'");
}
}
@Override
public void write(int address, int data) throws MemoryAccessException {
switch (address) {
case 0:
writeData(data);
return;
case 1:
writeCommand(data);
return;
case 2:
this.lba0 = data;
return;
case 3:
this.lba1 = data;
return;
case 4:
this.lba2 = data;
}
}
@Override
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
switch (address) {
case 0:
return readData();
case 1:
return readStatus();
default:
return 0;
}
}
private void computePosition() {
this.position = lba0 + (lba1 << 8) + (lba2 << 16);
// each sector is 512 bytes, so multiply accordingly
this.position <<= 9;
}
private void prepareRead() {
this.status = Status.READ;
this.readPosition = 0;
computePosition();
if (sdImageFile != null) {
try {
FileInputStream fis = new FileInputStream(sdImageFile);
fis.skip(this.position);
int read = fis.read(readBuffer);
if (read < SECTOR_SIZE) {
logger.log(Level.WARNING, "not enough data to fill read buffer from SD image file");
}
fis.close();
} catch (IOException ex) {
logger.log(Level.WARNING, "could not fill read buffer from SD image file", ex);
}
}
}
private void prepareWrite() {
this.status = Status.WRITE;
this.writePosition = 0;
computePosition();
}
private int readData() {
if (status != Status.READ) {
return 0;
}
int data = readBuffer[readPosition++];
if (readPosition >= SECTOR_SIZE) {
this.status = Status.IDLE;
}
return data;
}
private void writeData(int data) {
if (status != Status.WRITE) {
return;
}
writeBuffer[writePosition++] = (byte) data;
if (writePosition >= SECTOR_SIZE) {
if (sdImageFile != null) {
try {
RandomAccessFile raf = new RandomAccessFile(sdImageFile, "rw");
raf.skipBytes(this.position);
raf.write(writeBuffer, 0, writeBuffer.length);
raf.close();
} catch (IOException ex) {
logger.log(Level.WARNING, "could not write data back to SD image file!", ex);
}
}
this.status = Status.IDLE;
}
}
private int readStatus() {
switch (this.status) {
case IDLE:
return 128;
case READ:
return 224;
case WRITE:
return 160;
default:
return 0;
}
}
private void writeCommand(int data) {
switch (data) {
case 0:
prepareRead();
return;
case 1:
prepareWrite();
return;
default:
this.status = Status.IDLE;
}
}
@Override
public String toString() {
return getName() + "@" + String.format("%04X", this.getMemoryRange().startAddress);
}
}
@@ -0,0 +1,109 @@
/*
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
/**
* Very basic implementation of a MOS 6522 VIA.
*
* TODO: Implement timers as threads.
*/
public class Via6522 extends Pia {
public static final int VIA_SIZE = 16;
enum Register {
ORB, ORA, DDRB, DDRA, T1C_L, T1C_H, T1L_L, T1L_H,
T2C_L, T2C_H, SR, ACR, PCR, IFR, IER, ORA_H
}
public Via6522(int address) throws MemoryRangeException {
super(address, address + VIA_SIZE - 1, "MOS 6522 VIA");
}
@Override
public void write(int address, int data) throws MemoryAccessException {
Register[] registers = Register.values();
if (address >= registers.length) {
throw new MemoryAccessException("Unknown register: " + address);
}
Register r = registers[address];
switch (r) {
case ORA:
case ORB:
case DDRA:
case DDRB:
case T1C_L:
case T1C_H:
case T1L_L:
case T1L_H:
case T2C_L:
case T2C_H:
case SR:
case ACR:
case PCR:
case IFR:
case IER:
case ORA_H:
default:
}
}
@Override
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
Register[] registers = Register.values();
if (address >= registers.length) {
throw new MemoryAccessException("Unknown register: " + address);
}
Register r = registers[address];
switch (r) {
case ORA:
case ORB:
case DDRA:
case DDRB:
case T1C_L:
case T1C_H:
case T1L_L:
case T1L_H:
case T2C_L:
case T2C_H:
case SR:
case ACR:
case PCR:
case IFR:
case IER:
case ORA_H:
default:
}
return 0;
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -31,7 +31,4 @@ public class SymonException extends Exception {
public SymonException(String msg) {
super(msg);
}
public SymonException() {
super();
}
}
@@ -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,198 @@
/*
* Copyright (c) 2016 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.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import javax.swing.JComponent;
import javax.swing.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 int getBorderWidth() {
return borderWidth;
}
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 println(String str) {
if (str == null) {
throw new NullPointerException("str");
}
print(str.concat("\r\n"));
}
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(1, 0, rows, 0, bufferSize + 1);
scrollBar.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent 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,167 @@
/*
* 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 {
/**
* Gets the bell strategy.
* @return The bell strategy.
*/
public BellStrategy getBellStrategy();
/**
* Sets the bell strategy.
* @param strategy The bell strategy.
* @throws NullPointerException if the strategy is {@code null}.
*/
public void setBellStrategy(BellStrategy strategy);
/**
* Clears the terminal.
*/
public void clear();
/**
* Moves the cursor back n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public void moveCursorBack(int n);
/**
* Moves the cursor forward n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public void moveCursorForward(int n);
/**
* Moves the cursor down n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public void moveCursorDown(int n);
/**
* Moves the cursor up n characters.
* @param n The number of characters.
* @throws IllegalArgumentException if n is not positive.
*/
public 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.
*/
public 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.
*/
public 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}.
*/
public void print(String str);
/**
* Gets the number of columns.
* @return The number of columns.
*/
public int getColumns();
/**
* Gets the number of rows.
* @return The number of rows.
*/
public int getRows();
/**
* Gets the buffer size.
* @return The buffer size.
*/
public int getBufferSize();
/**
* Gets the cursor row.
* @return The cursor row.
*/
public int getCursorRow();
/**
* Sets the cursor row.
* @param row The cursor row.
* @throws IllegalArgumentException if the row is out of the valid range.
*/
public void setCursorRow(int row);
/**
* Gets the cursor column.
* @return The cursor column.
*/
public int getCursorColumn();
/**
* Sets the cursor column.
* @param column The cursor column.
* @throws IllegalArgumentException if the column is out of the valid range.
*/
public void setCursorColumn(int column);
/**
* Gets the default background color.
* @return The default background color.
*/
public Color getDefaultBackgroundColor();
/**
* Gets the default foreground color.
* @return The default foreground color.
*/
public 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.
*/
public 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].equals("")) {
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.
*/
public void parsedControlSequence(AnsiControlSequence seq);
/**
* Called when a string has been parsed.
* @param str The string.
*/
public void parsedString(String str);
}
@@ -0,0 +1,156 @@
/*
* 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 multi-byte 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 {@link #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();
}
Reader reader = new StringReader(str);
try {
try {
parse(reader);
} finally {
reader.close();
}
} 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((char) 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,493 @@
/*
* 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();
String[] parameters = seq.getParameters();
switch (command) {
case 'A':
case 'B':
case 'C':
case 'D':
int 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 if (command == 'D') {
moveCursorBack(n);
}
break;
case 'E':
case 'F':
n = 1;
if (parameters.length == 1) {
n = Integer.parseInt(parameters[0]);
}
if (command == 'E') {
moveCursorDown(n);
} else if (command == 'F') {
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 127:
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 BellStrategy getBellStrategy() {
return bellStrategy;
}
@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];
}
}
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -29,33 +29,33 @@ import com.loomcom.symon.Cpu;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.devices.Pia;
import com.loomcom.symon.exceptions.MemoryRangeException;
public interface Machine {
public Bus getBus();
Bus getBus();
Cpu getCpu();
public Cpu getCpu();
Memory getRam();
public Memory getRam();
Acia getAcia();
public Acia getAcia();
Pia getPia();
public Via getVia();
Crtc getCrtc();
public Crtc getCrtc();
Memory getRom();
public Memory getRom();
void setRom(Memory rom) throws MemoryRangeException;
public void setRom(Memory rom) throws MemoryRangeException;
int getRomBase();
public int getRomBase();
int getRomSize();
public int getRomSize();
public int getMemorySize();
int getMemorySize();
String getName();
}
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -30,7 +30,8 @@ import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Acia6850;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.devices.Pia;
import com.loomcom.symon.devices.SdController;
import com.loomcom.symon.exceptions.MemoryRangeException;
import java.io.File;
import java.util.logging.Logger;
@@ -51,6 +52,8 @@ public class MulticompMachine implements Machine {
// ACIA at $FFD0-$FFD1
private static final int ACIA_BASE = 0xFFD0;
// SD controller at $FFD8-$FFDF
private static final int SD_BASE = 0xFFD8;
// 8KB ROM at $E000-$FFFF
private static final int ROM_BASE = 0xE000;
@@ -70,10 +73,12 @@ public class MulticompMachine implements Machine {
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
this.acia = new Acia6850(ACIA_BASE);
this.acia.setBaudRate(0);
bus.addCpu(cpu);
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");
@@ -111,7 +116,7 @@ public class MulticompMachine implements Machine {
}
@Override
public Via getVia() {
public Pia getPia() {
return null;
}
@@ -1,3 +1,27 @@
/*
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.machines;
import com.loomcom.symon.Bus;
@@ -5,7 +29,7 @@ import com.loomcom.symon.Cpu;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.devices.Pia;
import com.loomcom.symon.exceptions.MemoryRangeException;
/**
@@ -52,7 +76,7 @@ public class SimpleMachine implements Machine {
}
@Override
public Via getVia() {
public Pia getPia() {
return null;
}
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2008-2014 Seth J. Morabito <sethm@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -26,19 +26,16 @@ package com.loomcom.symon.machines;
import com.loomcom.symon.Bus;
import com.loomcom.symon.Cpu;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Acia6551;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.devices.Via;
import com.loomcom.symon.devices.*;
import com.loomcom.symon.exceptions.MemoryRangeException;
import java.io.File;
import java.util.logging.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
public class SymonMachine implements Machine {
private final static Logger logger = Logger.getLogger(SymonMachine.class.getName());
private final static Logger logger = LoggerFactory.getLogger(SymonMachine.class.getName());
// Constants used by the simulated system. These define the memory map.
private static final int BUS_BOTTOM = 0x0000;
@@ -48,16 +45,15 @@ public class SymonMachine implements Machine {
private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0x8000;
// VIA at $8000-$800F
// PIA at $8000-$800F
private static final int VIA_BASE = 0x8000;
private static final int PIA_BASE = 0x8000;
// ACIA at $8800-$8803
private static final int ACIA_BASE = 0x8800;
// CRTC at $9000-$9001
private static final int CRTC_BASE = 0x9000;
private static final int VIDEO_RAM_BASE = 0x7000;
// 16KB ROM at $C000-$FFFF
private static final int ROM_BASE = 0xC000;
@@ -68,7 +64,7 @@ public class SymonMachine implements Machine {
private final Bus bus;
private final Cpu cpu;
private final Acia acia;
private final Via via;
private final Pia pia;
private final Crtc crtc;
private final Memory ram;
private Memory rom;
@@ -78,24 +74,23 @@ public class SymonMachine implements Machine {
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.via = new Via(VIA_BASE);
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(via);
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);
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.");
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);
}
@@ -124,8 +119,8 @@ public class SymonMachine implements Machine {
}
@Override
public Via getVia() {
return via;
public Pia getPia() {
return pia;
}
@Override
@@ -0,0 +1,152 @@
/*
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.ui;
import com.loomcom.symon.Breakpoints;
import com.loomcom.symon.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/**
* Simple window to enter breakpoints.
*/
public class BreakpointsWindow extends JFrame {
private static final Logger logger = LoggerFactory.getLogger(BreakpointsWindow.class);
private static final Dimension FRAME_SIZE = new Dimension(240, 280);
private static final String EMPTY_STRING = "";
private JFrame mainWindow;
private Breakpoints breakpoints;
public BreakpointsWindow(Breakpoints breakpoints,
JFrame mainWindow) {
this.breakpoints = breakpoints;
this.mainWindow = mainWindow;
createUi();
}
private void createUi() {
setTitle("Breakpoints");
JPanel breakpointsPanel = new JPanel();
JPanel controlPanel = new JPanel();
breakpointsPanel.setLayout(new BorderLayout());
breakpointsPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
final JButton addButton = new JButton("Add");
final JButton removeButton = new JButton("Del");
removeButton.setEnabled(false);
final JTextField addTextField = new JTextField(4);
final JTable breakpointsTable = new JTable(breakpoints);
breakpointsTable.setShowGrid(true);
breakpointsTable.setGridColor(Color.LIGHT_GRAY);
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
breakpointsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getFirstIndex() > -1) {
removeButton.setEnabled(true);
} else {
removeButton.setEnabled(false);
}
}
});
JScrollPane scrollPane = new JScrollPane(breakpointsTable);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
ActionListener addBreakpointListener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int value;
String newBreakpoint = addTextField.getText();
if (newBreakpoint == null || newBreakpoint.isEmpty()) {
return;
}
try {
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
} catch (NumberFormatException ex) {
logger.warn("Can't parse page number {}", newBreakpoint);
return;
}
if (value < 0) {
return;
}
breakpoints.addBreakpoint(value);
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
addTextField.setText(EMPTY_STRING);
}
};
addButton.addActionListener(addBreakpointListener);
addTextField.addActionListener(addBreakpointListener);
removeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow());
}
});
controlPanel.add(addTextField);
controlPanel.add(addButton);
controlPanel.add(removeButton);
setLayout(new BorderLayout());
getContentPane().add(breakpointsPanel, BorderLayout.CENTER);
getContentPane().add(controlPanel, BorderLayout.SOUTH);
setMinimumSize(FRAME_SIZE);
setMaximumSize(FRAME_SIZE);
setPreferredSize(FRAME_SIZE);
setLocationRelativeTo(mainWindow);
setResizable(false);
pack();
}
}
+14 -12
View File
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* 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,21 +44,23 @@ import java.awt.event.MouseListener;
public class Console extends JTerminal implements KeyListener, MouseListener {
private static final int DEFAULT_COLUMNS = 80;
private static final int DEFAULT_ROWS = 24;
private static final int DEFAULT_BORDER_WIDTH = 10;
private static final 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 static final boolean SEND_CR_LF_FOR_CR = false;
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
private boolean sendCrForLf;
private FifoRingBuffer<Character> typeAheadBuffer;
public Console(int columns, int rows, Font font) {
public Console(int columns, int rows, Font font, boolean sendCrForLf) {
super(new Vt100TerminalModel(columns, rows), font);
//super(new Vt100TerminalModel(columns, rows));
// A small type-ahead buffer, as might be found in any real
// VT100-style serial terminal.
this.typeAheadBuffer = new FifoRingBuffer<Character>(128);
this.typeAheadBuffer = new FifoRingBuffer<>(128);
this.sendCrForLf = sendCrForLf;
setBorderWidth(DEFAULT_BORDER_WIDTH);
addKeyListener(this);
addMouseListener(this);
@@ -83,7 +85,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
/**
* Returns true if a key has been pressed since the last time input was read.
*
* @return
* @return true if the type-ahead buffer has data
*/
public boolean hasInput() {
return !typeAheadBuffer.isEmpty();
@@ -105,7 +107,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
}
}
if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) {
if (sendCrForLf && (keyTyped == 0x0d)) {
typeAheadBuffer.push((char) 0x0d);
typeAheadBuffer.push((char) 0x0a);
} else {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -25,7 +25,9 @@ package com.loomcom.symon.ui;
import com.loomcom.symon.Bus;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.util.HexUtil;
import com.loomcom.symon.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
@@ -37,8 +39,7 @@ import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EventObject;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This Frame displays the contents of a page of memory. The page number to be displayed
@@ -46,6 +47,8 @@ import java.util.logging.Logger;
*/
public class MemoryWindow extends JFrame implements ActionListener {
private static final Logger logger = LoggerFactory.getLogger(MemoryWindow.class);
private MemoryTableModel memoryTableModel;
private JTable memoryTable;
private JTextField pageNumberTextField;
@@ -104,7 +107,7 @@ public class MemoryWindow extends JFrame implements ActionListener {
previousPageButton.setEnabled(pageNumber > 0x00);
nextPageButton.setEnabled(pageNumber < 0xff);
pageNumberTextField.setText(HexUtil.byteToHex(pageNumber));
pageNumberTextField.setText(Utils.byteToHex(pageNumber));
}
/**
@@ -209,9 +212,9 @@ public class MemoryWindow extends JFrame implements ActionListener {
} catch (NumberFormatException ex) {
// An invalid number was entered. Log the error, but otherwise
// take no action.
Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "Can't parse page number " +
pageNumberInput);
logger.warn("Can't parse page number {}", pageNumberInput);
}
updateControls();
}
}
@@ -325,13 +328,13 @@ public class MemoryWindow extends JFrame implements ActionListener {
public Object getValueAt(int row, int column) {
try {
if (column == 0) {
return HexUtil.wordToHex(fullAddress(row, 1));
return Utils.wordToHex(fullAddress(row, 1));
} else if (column < 9) {
// Display hex value of the data
return HexUtil.byteToHex(bus.read(fullAddress(row, column)));
return Utils.byteToHex(bus.read(fullAddress(row, column), false));
} else {
// Display the ASCII equivalent (if printable)
return HexUtil.byteToAscii(bus.read(fullAddress(row, column - 8)));
return Utils.byteToAscii(bus.read(fullAddress(row, column - 8), false));
}
} catch (MemoryAccessException ex) {
return "??";
@@ -346,12 +349,8 @@ public class MemoryWindow extends JFrame implements ActionListener {
int fullAddress = fullAddress(row, column);
int newValue = Integer.parseInt(hexValue, 16) & 0xff;
bus.write(fullAddress, newValue);
} catch (MemoryAccessException ex) {
;
} catch (NumberFormatException ex) {
;
} catch (ClassCastException ex) {
;
} catch (MemoryAccessException | NumberFormatException | ClassCastException ex) {
// Intentionally swallow exception
}
fireTableCellUpdated(row, column);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -98,19 +98,21 @@ public class PreferencesDialog extends Observable implements Preferences {
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
updateUi();
PreferencesDialog.this.updateUi();
dialog.setVisible(false);
}
});
applyButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
haltOnBreak = haltOnBreakCheckBox.isSelected();
programLoadAddress = hexToInt(programLoadAddressField.getText());
updateUi();
programLoadAddress = PreferencesDialog.this.hexToInt(programLoadAddressField.getText());
PreferencesDialog.this.updateUi();
// TODO: Actually check to see if values have changed, don't assume.
setChanged();
PreferencesDialog.this.setChanged();
PreferencesDialog.this.notifyObservers();
dialog.setVisible(false);
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -24,6 +24,7 @@
package com.loomcom.symon.ui;
import com.loomcom.symon.Cpu;
import com.loomcom.symon.CpuState;
import com.loomcom.symon.machines.Machine;
import javax.swing.*;
@@ -32,9 +33,6 @@ import javax.swing.border.EtchedBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* UI component that displays the current state of the simulated CPU.
@@ -56,7 +54,6 @@ public class StatusPanel extends JPanel {
private ImageIcon negativeOn;
private ImageIcon negativeOff;
private JLabel statusFlagsLabel;
private JLabel carryFlagLabel;
private JLabel zeroFlagLabel;
private JLabel irqDisableFlagLabel;
@@ -72,13 +69,6 @@ public class StatusPanel extends JPanel {
private JTextField xField;
private JTextField yField;
private JLabel opcodeLabel;
private JLabel pcLabel;
private JLabel spLabel;
private JLabel aLabel;
private JLabel xLabel;
private JLabel yLabel;
private Machine machine;
private static final int EMPTY_BORDER = 10;
@@ -153,13 +143,13 @@ public class StatusPanel extends JPanel {
statusFlagsPanel.add(carryFlagLabel);
// Create and add register and address labels
statusFlagsLabel = makeLabel("Flags");
opcodeLabel = makeLabel("IR");
pcLabel = makeLabel("PC");
spLabel = makeLabel("SP");
aLabel = makeLabel("A");
xLabel = makeLabel("X");
yLabel = makeLabel("Y");
JLabel statusFlagsLabel = makeLabel("Flags");
JLabel opcodeLabel = makeLabel("Next IR");
JLabel pcLabel = makeLabel("PC");
JLabel spLabel = makeLabel("SP");
JLabel aLabel = makeLabel("A");
JLabel xLabel = makeLabel("X");
JLabel yLabel = makeLabel("Y");
statusFlagsLabel.setToolTipText("6502 Processor Status Flags");
opcodeLabel.setToolTipText("Instruction Register");
@@ -178,13 +168,13 @@ public class StatusPanel extends JPanel {
@Override
public void actionPerformed(ActionEvent e) {
try {
int newVal = getHexVal(pcField) & 0xffff;
int newVal = StatusPanel.this.getHexVal(pcField) & 0xffff;
machine.getCpu().setProgramCounter(newVal);
} catch (Exception ex) {
// Swallow exception
}
updateState();
StatusPanel.this.updateState();
}
});
@@ -192,13 +182,13 @@ public class StatusPanel extends JPanel {
@Override
public void actionPerformed(ActionEvent e) {
try {
int newVal = getHexVal(spField) & 0xff;
int newVal = StatusPanel.this.getHexVal(spField) & 0xff;
machine.getCpu().setStackPointer(newVal);
} catch (Exception ex) {
// Swallow exception
}
updateState();
StatusPanel.this.updateState();
}
});
@@ -206,13 +196,13 @@ public class StatusPanel extends JPanel {
@Override
public void actionPerformed(ActionEvent e) {
try {
int newVal = getHexVal(aField) & 0xff;
int newVal = StatusPanel.this.getHexVal(aField) & 0xff;
machine.getCpu().setAccumulator(newVal);
} catch (Exception ex) {
// Swallow exception
}
updateState();
StatusPanel.this.updateState();
}
});
@@ -220,13 +210,13 @@ public class StatusPanel extends JPanel {
@Override
public void actionPerformed(ActionEvent e) {
try {
int newVal = getHexVal(xField) & 0xff;
int newVal = StatusPanel.this.getHexVal(xField) & 0xff;
machine.getCpu().setXRegister(newVal);
} catch (Exception ex) {
// Swallow exception
}
updateState();
StatusPanel.this.updateState();
}
});
@@ -234,13 +224,13 @@ public class StatusPanel extends JPanel {
@Override
public void actionPerformed(ActionEvent e) {
try {
int newVal = getHexVal(yField) & 0xff;
int newVal = StatusPanel.this.getHexVal(yField) & 0xff;
machine.getCpu().setYRegister(newVal);
} catch (Exception ex) {
// Swallow exception
}
updateState();
StatusPanel.this.updateState();
}
});
@@ -303,7 +293,7 @@ public class StatusPanel extends JPanel {
*/
public void updateState() {
Cpu cpu = machine.getCpu();
Cpu.CpuState cpuState = cpu.getCpuState();
CpuState cpuState = cpu.getCpuState();
// Update the Processor Status Flag display
int status = cpuState.getStatusFlag();
@@ -317,7 +307,9 @@ public class StatusPanel extends JPanel {
negativeFlagLabel.setIcon(iconForFlag(status, 7));
// Update the register and address displays
opcodeField.setText(cpu.getCpuState().disassembleOp());
// We always want to show the NEXT instruction that will be executed
opcodeField.setText(cpu.disassembleNextOp());
pcField.setText(cpu.getProgramCounterStatus());
spField.setText(cpu.getStackPointerStatus());
aField.setText(cpu.getAccumulatorStatus());
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -23,7 +24,7 @@
package com.loomcom.symon.ui;
import com.loomcom.symon.Cpu;
import com.loomcom.symon.CpuState;
import com.loomcom.symon.util.FifoRingBuffer;
import javax.swing.*;
@@ -35,15 +36,15 @@ import java.awt.*;
*/
public class TraceLog extends JFrame {
private FifoRingBuffer<Cpu.CpuState> traceLog;
private JTextArea traceLogTextArea;
private final FifoRingBuffer<CpuState> traceLog;
private final JTextArea traceLogTextArea;
private static final Dimension MIN_SIZE = new Dimension(320, 200);
private static final Dimension PREFERRED_SIZE = new Dimension(640, 480);
private static final int MAX_LOG_LENGTH = 50000;
public TraceLog() {
traceLog = new FifoRingBuffer<Cpu.CpuState>(MAX_LOG_LENGTH);
traceLog = new FifoRingBuffer<>(MAX_LOG_LENGTH);
setMinimumSize(MIN_SIZE);
setPreferredSize(PREFERRED_SIZE);
setResizable(true);
@@ -67,11 +68,15 @@ public class TraceLog extends JFrame {
* call.
*/
public void refresh() {
synchronized (this) {
StringBuilder logString = new StringBuilder();
for (Cpu.CpuState state : traceLog) {
StringBuilder logString = new StringBuilder();
synchronized(traceLog) {
for (CpuState state : traceLog) {
logString.append(state.toTraceEvent());
}
}
synchronized(traceLogTextArea) {
traceLogTextArea.setText(logString.toString());
}
}
@@ -80,8 +85,10 @@ public class TraceLog extends JFrame {
* Reset the log area.
*/
public void reset() {
synchronized (this) {
synchronized(traceLog) {
traceLog.reset();
}
synchronized(traceLogTextArea) {
traceLogTextArea.setText("");
traceLogTextArea.setEnabled(true);
}
@@ -92,9 +99,9 @@ public class TraceLog extends JFrame {
*
* @param state The CPU State to append.
*/
public void append(Cpu.CpuState state) {
synchronized(this) {
traceLog.push(new Cpu.CpuState(state));
public void append(CpuState state) {
synchronized(traceLog) {
traceLog.push(new CpuState(state));
}
}
@@ -106,4 +113,8 @@ public class TraceLog extends JFrame {
traceLogTextArea.setEnabled(true);
}
public boolean shouldUpdate() {
return isVisible() && traceLogTextArea.isEnabled();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2013 Seth J. Morabito <web@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -25,6 +25,7 @@ package com.loomcom.symon.ui;
import com.loomcom.symon.devices.Crtc;
import com.loomcom.symon.devices.DeviceChangeListener;
import com.loomcom.symon.exceptions.MemoryAccessException;
import javax.swing.*;
import java.awt.*;
@@ -36,8 +37,11 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import static java.lang.System.*;
/**
* VideoWindow represents a graphics framebuffer backed by a 6545 CRTC.
* Each time the window's VideoPanel is repainted, the video memory is
@@ -65,7 +69,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
private BufferedImage image;
private int[] charRom;
private int[] videoRam;
private int horizontalDisplayed;
private int verticalDisplayed;
@@ -85,17 +88,21 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
private class VideoPanel extends JPanel {
@Override
public void paintComponent(Graphics g) {
for (int i = 0; i < crtc.getPageSize(); i++) {
int address = crtc.getStartAddress() + i;
int originX = (i % horizontalDisplayed) * CHAR_WIDTH;
int originY = (i / horizontalDisplayed) * scanLinesPerRow;
image.getRaster().setPixels(originX, originY, CHAR_WIDTH, scanLinesPerRow, getGlyph(address));
try {
for (int i = 0; i < crtc.getPageSize(); i++) {
int address = crtc.getStartAddress() + i;
int originX = (i % horizontalDisplayed) * CHAR_WIDTH;
int originY = (i / horizontalDisplayed) * scanLinesPerRow;
image.getRaster().setPixels(originX, originY, CHAR_WIDTH, scanLinesPerRow, getGlyph(address));
}
Graphics2D g2d = (Graphics2D) g;
if (shouldScale) {
g2d.scale(scaleX, scaleY);
}
g2d.drawImage(image, 0, 0, null);
} catch (MemoryAccessException ex) {
logger.log(Level.SEVERE, "Memory Access Exception, can't paint video window! " + ex.getMessage());
}
Graphics2D g2d = (Graphics2D)g;
if (shouldScale) {
g2d.scale(scaleX, scaleY);
}
g2d.drawImage(image, 0, 0, null);
}
@Override
@@ -116,10 +123,11 @@ 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;
repaint();
VideoWindow.this.repaint();
}
}
});
@@ -131,8 +139,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
this.scheduler = Executors.newSingleThreadScheduledExecutor();
this.crtc = crtc;
this.charRom = loadCharRom("/pet.rom");
this.videoRam = crtc.getDmaAccess();
this.charRom = loadCharRom("/ascii.rom");
this.scaleX = scaleX;
this.scaleY = scaleY;
this.shouldScale = (scaleX > 1 || scaleY > 1);
@@ -158,13 +165,6 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
}
public void refreshDisplay() {
// TODO: Verify whether this is necessary. Does `repaint()' do anything if the window is not visible?
if (isVisible()) {
repaint();
}
}
/**
* Called by the CRTC on state change.
*/
@@ -239,15 +239,13 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
* @param address The address of the character being requested.
* @return An array of integers representing the pixel data.
*/
private int[] getGlyph(int address) {
int chr = videoRam[address];
private int[] getGlyph(int address) throws MemoryAccessException {
int chr = crtc.getCharAtAddress(address);
int romOffset = (chr & 0xff) * (CHAR_HEIGHT * CHAR_WIDTH);
int[] glyph = new int[CHAR_WIDTH * scanLinesPerRow];
// Populate the character
for (int i = 0; i < (CHAR_WIDTH * Math.min(CHAR_HEIGHT, scanLinesPerRow)); i++) {
glyph[i] = charRom[romOffset + i];
}
arraycopy(charRom, romOffset, glyph, 0, CHAR_WIDTH * Math.min(CHAR_HEIGHT, scanLinesPerRow));
// Overlay the cursor
if (!hideCursor && crtc.isCursorEnabled() && crtc.getCursorPosition() == address) {
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -38,7 +38,7 @@ public class FifoRingBuffer<E> implements Iterable<E> {
private int maxLength;
public FifoRingBuffer(int maxLength) {
this.fifoBuffer = new LinkedList<E>();
this.fifoBuffer = new LinkedList<>();
this.maxLength = maxLength;
}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008-2013 Seth J. Morabito <sethm@loomcom.com>
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@@ -24,24 +24,9 @@
package com.loomcom.symon.util;
/**
* Hex String Utilities.
*
* <p/>
*
* But why? Java, after all, has a number of ways to convert an integer into a hex string,
* so it may look absurd to go to the trouble of writing yet another conversion! The answer is
* performance.
*
* <p/>
*
* The most convenient way to get a formatted hex value from an integer is with the <code>String.format</code>
* method, but this turns out to be extremely inefficient. Formatting a million integers
* with <code>String.format</code> takes something like 1600ms. Formatting the same number of integers
* with <code>HexUtil</code> takes only 160ms. This is on par with <code>Integer.toHexString</code>,
* but also gives the desired padding.
*
* Various Utilities
*/
public class HexUtil {
public class Utils {
static final String NON_PRINTABLE = ".";
@@ -93,6 +78,7 @@ public class HexUtil {
/**
* Very fast 8-bit int to ASCII conversion.
*
* @param val The value of an ASCII character.
* @return A string representing the ASCII character.
*/
@@ -121,9 +107,13 @@ public class HexUtil {
* @return Four digit, zero padded hexadecimal string.
*/
public static String wordToHex(int val) {
StringBuilder sb = new StringBuilder(4);
sb.append(HEX_CONSTANTS[(val >> 8) & 0xff]);
sb.append(HEX_CONSTANTS[val & 0xff]);
return sb.toString();
return HEX_CONSTANTS[(val >> 8) & 0xff] + HEX_CONSTANTS[val & 0xff];
}
/**
* Given two bytes, return an address.
*/
public static int address(int lowByte, int hiByte) {
return ((hiByte << 8) | lowByte) & 0xffff;
}
}
Binary file not shown.
Binary file not shown.
@@ -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);
}
}
+120 -14
View File
@@ -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,28 @@ 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));
}
}
@@ -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));
}
}
@@ -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,8 +165,8 @@ 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());
@@ -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());
@@ -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());
@@ -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());
}
@@ -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());
}
@@ -101,10 +101,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());
@@ -117,9 +117,22 @@ public class CpuImpliedModeTest {
}
@Test
public void test_BRK_HonorsIrqDisableFlag() throws MemoryAccessException {
public void test_BRK_IgnoresIrqDisableFlag() throws MemoryAccessException {
cpu.setIrqDisableFlag();
cpu.setCarryFlag();
cpu.setOverflowFlag();
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_IRQ_DISABLE,
cpu.getProcessorStatus());
assertEquals(0x00, cpu.stackPeek());
assertFalse(cpu.getBreakFlag());
assertEquals(0x0200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
// Set the IRQ vector
bus.write(0xffff, 0x12);
bus.write(0xfffe, 0x34);
bus.loadProgram(0xea,
0xea,
0xea,
@@ -131,23 +144,26 @@ public class CpuImpliedModeTest {
assertEquals(0x203, cpu.getProgramCounter());
// Triggers the BRK, which should do nothing because
// of the Interrupt Disable flag
// Triggers the BRK
cpu.step();
// Reset to original contents of PC
assertEquals(0x0204, cpu.getProgramCounter());
// Empty stack
assertEquals(0xff, cpu.getStackPointer());
cpu.step(2); // Two more NOPs
// Was at PC = 0x204. PC+1 should now be on the stack
assertEquals(0x02, bus.read(0x1ff, 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, true)); // Processor Status, with B set
// Reset to original contents of PC
assertEquals(0x0206, cpu.getProgramCounter());
// Empty stack
assertEquals(0xff, cpu.getStackPointer());
// 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
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_IRQ_DISABLE,
cpu.getProcessorStatus());
}
/* CLC - Clear Carry Flag - $18 */
@Test
public void test_CLC() throws MemoryAccessException {
@@ -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));
}
}
@@ -62,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
@@ -78,7 +78,7 @@ public class CpuIndirectIndexedModeTest {
cpu.step(1);
assertEquals(0x22, cpu.getAccumulator());
assertEquals(0xe3, bus.read(0xd828));
assertEquals(0xe3, bus.read(0xd828, true));
}
}
@@ -64,7 +64,7 @@ public class CpuIndirectModeTest extends TestCase {
}
public void test_JMP_withIndirectBug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG);
cpu.setBehavior(Cpu.CpuBehavior.NMOS_6502);
bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54);
@@ -76,7 +76,7 @@ public class CpuIndirectModeTest extends TestCase {
}
public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITHOUT_INDIRECT_JMP_BUG);
cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54);
@@ -88,7 +88,7 @@ public class CpuIndirectModeTest extends TestCase {
}
public void test_JMP_cmos() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.CMOS);
cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54);
@@ -162,7 +162,7 @@ public class CpuIndirectXModeTest extends TestCase {
cpu.setXRegister(0x30);
bus.loadProgram(0xa9, 0x88, // LDA #$88
bus.loadProgram(0xa9, 0x88, // LDA #$88
0x5d, 0x10, 0xab, // EOR $ab10,X
0x5d, 0x11, 0xab, // EOR $ab11,X
0x5d, 0x12, 0xab, // EOR $ab12,X
@@ -411,7 +411,7 @@ public class CpuIndirectXModeTest 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));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@@ -422,7 +422,7 @@ public class CpuIndirectXModeTest 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());
@@ -432,7 +432,7 @@ public class CpuIndirectXModeTest 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());
}
+97 -30
View File
@@ -1,5 +1,6 @@
package com.loomcom.symon;
import com.loomcom.symon.util.Utils;
import junit.framework.*;
import com.loomcom.symon.devices.*;
@@ -70,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());
}
@@ -383,6 +384,41 @@ public class CpuTest extends TestCase {
assertFalse(cpu.getCpuState().irqAsserted);
}
public void testIrqDoesNotSetBRK() throws Exception {
// Ensure the IRQ disable flag is cleared
cpu.clearIrqDisableFlag();
// Set the IRQ vector
bus.write(0xffff, 0x12);
bus.write(0xfffe, 0x34);
// Create an IRQ handler at 0x1234
cpu.setProgramCounter(0x1234);
bus.loadProgram(0xa9, 0x33, // LDA #$33
0x69, 0x01); // ADC #$01
cpu.setProgramCounter(0x0200);
// Create a little program at 0x0200
bus.loadProgram(0x18, // CLC
0xa9, 0x01, // LDA #$00
0x69, 0x01); // ADC #$01
cpu.step();
assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed.
assertEquals(0x00, cpu.getAccumulator());
cpu.step();
assertEquals(0x0203, cpu.getProgramCounter());
assertEquals(0x01, cpu.getAccumulator());
cpu.assertIrq();
cpu.step();
assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the IRQ
assertFalse(cpu.getBreakFlag());
}
public void testIrqHonorsIrqDisabledFlag() throws Exception {
// Ensure the IRQ disable flag is set
cpu.setIrqDisableFlag();
@@ -497,6 +533,37 @@ public class CpuTest extends TestCase {
assertFalse(cpu.getCpuState().nmiAsserted);
}
public void testNmiDoesNotSetBRK() throws Exception {
// Set the NMI vector to 0x1000
bus.write(0xfffb, 0x10);
bus.write(0xfffa, 0x00);
// Create an NMI handler at 0x1000
cpu.setProgramCounter(0x1000);
bus.loadProgram(0xa9, 0x33, // LDA #$33
0x69, 0x01); // ADC #$01
// Create a little program at 0x0200
cpu.setProgramCounter(0x0200);
bus.loadProgram(0x18, // CLC
0xa9, 0x01, // LDA #$00
0x69, 0x01); // ADC #$01
cpu.step();
assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed.
assertEquals(0x00, cpu.getAccumulator());
cpu.step();
assertEquals(0x0203, cpu.getProgramCounter());
assertEquals(0x01, cpu.getAccumulator());
cpu.assertNmi();
cpu.step();
assertFalse(cpu.getBreakFlag());
}
public void testNmiIgnoresIrqDisableFlag() throws Exception {
// Set the IRQ disable flag, which should be ignored by the NMI
cpu.setIrqDisableFlag();
@@ -537,11 +604,11 @@ public class CpuTest extends TestCase {
public void testAddress() {
assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
assertEquals(0x00ea, cpu.address(0xea, 0x00));
assertEquals(0xf100, cpu.address(0x00, 0xf1));
assertEquals(0x1234, cpu.address(0x34, 0x12));
assertEquals(0xffff, cpu.address(0xff, 0xff));
assertEquals(0xf1ea, Utils.address(0xea, 0xf1));
assertEquals(0x00ea, Utils.address(0xea, 0x00));
assertEquals(0xf100, Utils.address(0x00, 0xf1));
assertEquals(0x1234, Utils.address(0x34, 0x12));
assertEquals(0xffff, Utils.address(0xff, 0xff));
}
public void testZpxAddress() {
@@ -0,0 +1,329 @@
package com.loomcom.symon;
import com.loomcom.symon.devices.Memory;
import org.junit.Test;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Test for Zero Page Indirect addressing mode, found on some instructions
* in the 65C02 and 65816
*/
public class CpuZeroPageIndirectTest {
protected Cpu cpu;
protected Bus bus;
protected Memory mem;
private void makeCmosCpu() throws Exception {
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
}
private void makeNmosCpu() throws Exception {
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
}
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
this.cpu = new Cpu(behavior);
this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0xffff);
bus.addCpu(cpu);
bus.addDevice(mem);
// Load the reset vector.
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
}
@Test
public void test_ora() throws Exception {
makeCmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x11);
bus.write(0x1001, 0x22);
bus.write(0x1002, 0x44);
bus.write(0x1003, 0x88);
bus.loadProgram(0x12, 0x30, // ORA ($30)
0x12, 0x40, // ORA ($40)
0x12, 0x50, // ORA ($50)
0x12, 0x60); // ORA ($60)
assertEquals(0x00, cpu.getAccumulator());
// 0x00 | 0x11 = 0x11
cpu.step();
assertEquals(0x11, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x11 | 0x22 = 0x33
cpu.step();
assertEquals(0x33, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x33 | 0x44 = 0x77
cpu.step();
assertEquals(0x77, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x77 | 0x88 = 0xff
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
@Test
public void test_ora_requiresCmosCpu() throws Exception {
makeNmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x11);
bus.write(0x1001, 0x22);
bus.write(0x1002, 0x44);
bus.write(0x1003, 0x88);
bus.loadProgram(0x12, 0x30, // ORA ($30)
0x12, 0x40, // ORA ($40)
0x12, 0x50, // ORA ($50)
0x12, 0x60); // ORA ($60)
assertEquals(0x00, cpu.getAccumulator());
boolean zState = cpu.getZeroFlag();
boolean nState = cpu.getNegativeFlag();
// 0x00 | 0x11 = 0x11, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
// 0x11 | 0x22 = 0x33, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
// 0x33 | 0x44 = 0x77, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
// 0x77 | 0x88 = 0xff, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
}
@Test
public void test_and() throws Exception {
makeCmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x1000, 0x33);
bus.write(0x1001, 0x11);
bus.write(0x1002, 0x88);
bus.loadProgram(0x32, 0x30, // AND ($30)
0x32, 0x40, // AND ($40)
0x32, 0x50); // AND ($50)
cpu.setAccumulator(0xff);
// 0xFF & 0x33 == 0x33
cpu.step();
assertEquals(0x33, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x33 & 0x11 == 0x11
cpu.step();
assertEquals(0x11, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x11 & 0x80 == 0
cpu.step();
assertEquals(0, cpu.getAccumulator());
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@Test
public void test_and_requiresCmosCpu() throws Exception {
makeNmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x1000, 0x33);
bus.write(0x1001, 0x11);
bus.write(0x1002, 0x88);
bus.loadProgram(0x32, 0x30, // AND ($30)
0x32, 0x40, // AND ($40)
0x32, 0x50); // AND ($50)
cpu.setAccumulator(0xff);
boolean zState = cpu.getZeroFlag();
boolean nState = cpu.getNegativeFlag();
// 0xFF & 0x33 == 0x33, but not implemented in NMOS cpu
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag());
assertEquals(nState, cpu.getNegativeFlag());
// 0x33 & 0x11 == 0x11, but not implemented in NMOS cpu
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag());
assertEquals(nState, cpu.getNegativeFlag());
// 0x11 & 0x80 == 0, but not implemented in NMOS cpu
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag());
assertEquals(nState, cpu.getNegativeFlag());
}
@Test
public void test_eor() throws Exception {
makeCmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x00);
bus.write(0x1001, 0xff);
bus.write(0x1002, 0x33);
bus.write(0x1003, 0x44);
bus.loadProgram(0x52, 0x30, // AND ($30)
0x52, 0x40, // AND ($40)
0x52, 0x50, // EOR ($50)
0x52, 0x60); // AND ($60)
cpu.setAccumulator(0x88);
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x77, cpu.getAccumulator());
cpu.step();
assertEquals(0x44, cpu.getAccumulator());
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
}
@Test
public void test_eor_requiresCmosCpu() throws Exception {
makeNmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x00);
bus.write(0x1001, 0xff);
bus.write(0x1002, 0x33);
bus.write(0x1003, 0x44);
bus.loadProgram(0x52, 0x30, // AND ($30)
0x52, 0x40, // AND ($40)
0x52, 0x50, // EOR ($50)
0x52, 0x60); // AND ($60)
cpu.setAccumulator(0x88);
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
}
}
@@ -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());
}
@@ -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());
}
@@ -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());
}
+66 -14
View File
@@ -19,19 +19,17 @@ import static org.mockito.Mockito.*;
public class CrtcTest {
Crtc crtc;
Memory memory;
@Mock
DeviceChangeListener changeListener;
@Mock
Memory memory;
@Before
public void createDevices() throws Exception {
memory = new Memory(0, 0x7fff);
crtc = new Crtc(0x9000, memory);
crtc.registerListener(changeListener);
when(memory.startAddress()).thenReturn(0);
when(memory.endAddress()).thenReturn(0x7fff);
}
@Test
@@ -244,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));
}
@@ -280,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
@@ -315,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));
}
@@ -340,4 +338,58 @@ public class CrtcTest {
crtc.write(1, 0x80); // Can't position cursor
}
@Test
public void shouldSetRowColumnAddressing() throws Exception {
assertEquals(false, crtc.getRowColumnAddressing());
crtc.write(0, 8); // Select mode control register
crtc.write(1, 0x04);
assertEquals(true, crtc.getRowColumnAddressing());
}
@Test
public void shouldSetDisplayEnableSkew() throws Exception {
assertEquals(false, crtc.getDisplayEnableSkew());
crtc.write(0, 8); // Select mode control register
crtc.write(1, 0x10);
assertEquals(true, crtc.getDisplayEnableSkew());
}
@Test
public void shouldSetCursorSkew() throws Exception {
assertEquals(false, crtc.getCursorSkew());
crtc.write(0, 8); // Select mode control register
crtc.write(1, 0x20);
assertEquals(true, crtc.getCursorSkew());
}
@Test
public void shouldDoStraightBinaryAddressing() throws Exception {
crtc.write(0, 8);
crtc.write(1, 0); // Select straight binary addressing
// Fill the memory with a repeating pattern
int videoMemoryBase = 0x7000;
int j = 0;
for (int i = 0; i < 2048; i++) {
memory.write(videoMemoryBase + i, j);
if (j == 255) {
j = 0;
} else {
j++;
}
}
// Now verify that straight-binary addressing of the CRTC works
j = 0;
for (int i = 0; i < 2048; i++) {
assertEquals(j, crtc.getCharAtAddress(videoMemoryBase + i));
if (j == 255) {
j = 0;
} else {
j++;
}
}
}
}
@@ -1,42 +0,0 @@
package com.loomcom.symon;
import com.loomcom.symon.util.HexUtil;
import junit.framework.TestCase;
public class HexUtilTest extends TestCase {
public void testByteToHex() {
assertEquals("FE", HexUtil.byteToHex(0xfe));
assertEquals("00", HexUtil.byteToHex(0));
assertEquals("0A", HexUtil.byteToHex(10));
}
public void testByteToHexIgnoresSign() {
assertEquals("FF", HexUtil.byteToHex(-1));
}
public void testByteToHexMasksLowByte() {
assertEquals("FE", HexUtil.byteToHex(0xfffe));
assertEquals("00", HexUtil.byteToHex(0xff00));
}
public void testWordToHex() {
assertEquals("0000", HexUtil.wordToHex(0));
assertEquals("FFFF", HexUtil.wordToHex(65535));
assertEquals("FFFE", HexUtil.wordToHex(65534));
}
public void testWordToHexIgnoresSign() {
assertEquals("FFFF", HexUtil.wordToHex(-1));
}
public void testWordToHexMasksTwoLowBytes() {
assertEquals("FFFE", HexUtil.wordToHex(0xfffffe));
assertEquals("FF00", HexUtil.wordToHex(0xffff00));
}
public void testAllBytesAreCorrect() {
for (int i = 0; i <= 0xff; i++) {
assertEquals(String.format("%02X", i), HexUtil.byteToHex(i));
}
}
}
@@ -0,0 +1,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]);
}
}
@@ -0,0 +1,42 @@
package com.loomcom.symon;
import com.loomcom.symon.util.Utils;
import junit.framework.TestCase;
public class UtilsTest extends TestCase {
public void testByteToHex() {
assertEquals("FE", Utils.byteToHex(0xfe));
assertEquals("00", Utils.byteToHex(0));
assertEquals("0A", Utils.byteToHex(10));
}
public void testByteToHexIgnoresSign() {
assertEquals("FF", Utils.byteToHex(-1));
}
public void testByteToHexMasksLowByte() {
assertEquals("FE", Utils.byteToHex(0xfffe));
assertEquals("00", Utils.byteToHex(0xff00));
}
public void testWordToHex() {
assertEquals("0000", Utils.wordToHex(0));
assertEquals("FFFF", Utils.wordToHex(65535));
assertEquals("FFFE", Utils.wordToHex(65534));
}
public void testWordToHexIgnoresSign() {
assertEquals("FFFF", Utils.wordToHex(-1));
}
public void testWordToHexMasksTwoLowBytes() {
assertEquals("FFFE", Utils.wordToHex(0xfffffe));
assertEquals("FF00", Utils.wordToHex(0xffff00));
}
public void testAllBytesAreCorrect() {
for (int i = 0; i <= 0xff; i++) {
assertEquals(String.format("%02X", i), Utils.byteToHex(i));
}
}
}