1
0
mirror of https://github.com/sethm/symon.git synced 2024-05-31 17:41:30 +00:00

Compare commits

...

65 Commits

Author SHA1 Message Date
Seth Morabito
ecd4bbbd9a README formatting fix 2023-11-18 10:50:07 -08:00
Seth Morabito
a537328852 Minor README update 2023-11-18 10:46:55 -08:00
Seth Morabito
4975fca506 Missed a README update for v1.4.0 2023-11-11 08:20:49 -08:00
Seth Morabito
199d96c025 Prepare release 1.4.0 2023-11-11 08:15:35 -08:00
Seth Morabito
0c026e38dd By default, do not halt on BRK (#14)
This change introduces a new command line flag, '-b', that cause the
simulator to halt on the `BRK` instruction. By default, however, the
simulator will no longer halt on `BRK`.

As before, this behavior can be toggled in the preferences at run-time.
2023-11-10 09:11:24 -08:00
Seth Morabito
5e56627f32 #13: Move cursor backward on ASCII Backspace 2023-10-31 08:28:26 -07:00
Chelsea Wilkinson
67f5e17f78 Added BenEaterMachine 2023-10-31 07:44:55 -07:00
Seth Morabito
5a25750f46 Clarify how to load ROMs in README files 2023-07-23 10:16:36 -07:00
Tim Allen
4a8a803472 Document the quirks of Symon's ACIA emulation.
Fixes #7.
2023-06-11 08:21:37 -07:00
Tim Allen
4423623816 Writing 0 to the 6551 ACIA's control register is not a program reset.
So far as I can tell, ever since the first version of the ACIA emulation in
Symon, writing 0x00 to the control register has been interpreted as a request
to reset, rather than to actually set the control register to 0x00. This is
strange for a number of reasons:

- All-zeros is actually a very sensible value for the control register, and
  is in fact the hardware-reset default.
- I can't find any description of such behaviour in the 6551, W65C51S, or
  W65C51N data sheets.
- The 6551 already has a way to trigger "program reset" by writing to the
  status register.

So I've removed that quirk, and writing to the control register now just
writes to the control register and nothing else.
2023-06-11 08:21:37 -07:00
Tim Allen
5df775bbb0 Make the emulated 6551's soft ("program") reset state match the MOS datasheet.
The only description of the effects of "program reset" in the original MOS
datasheet is in the section for each register. The W65C51S and W65C51N
datasheets have a heading "PROGRAM RESET OPERATION", but it amounts to:

- internal registers are modified as described in the section for each register
- changes to the DTR, DCD, and DSR pins which Symon does not emulate
- the overrun flag is cleared

...which is what this new implementation does.

It would make *sense* for the reset to do things like "cancel transmission or
reception in progress" and stop asserting an interrupt, as the old code did,
but I can't find any evidence of such behaviour in the datasheets.
2023-06-11 08:21:37 -07:00
Tim Allen
d076046f57 Make the emulated 6551's hardware reset state match the MOS datasheet. 2023-06-11 08:21:37 -07:00
Tim Allen
b725fb5fdd Fix comment: The 6551 baud rate is in the lower *four* bits.
The code is correct, the comment is wrong.
2023-06-10 10:19:24 -07:00
Tim Allen
9351d785ae Fix comment: The 6551 ACIA *does* support interrupts. 2023-06-10 10:19:24 -07:00
Tim Allen
66a92f4196 Use ACIA register names where we can. 2023-06-10 10:19:24 -07:00
Seth Morabito
cda9a218af
Merge pull request #6 from Screwtapello/irq-disabled-at-reset
Make the Processor Status register match a real 6502 at power-on.
2023-02-03 16:38:06 -08:00
Tim Allen
b5a470d3ba Make the Processor Status register match a real 6502 at power-on.
When describing the CPU's reset pin, the W65C02S data sheet says:

> All Registers are initialized by software except the Decimal and Interrupt
> disable mode select bits of the Processor Status Register (P) are initialized
> by hardware.

It then has a diagram of the power-on state of the processor status register:

>     7 6 5 4 3 2 1 0
>     * * 1 1 0 1 * *
>     N V - B D I Z C
>
> * = software initialized

Confusingly the text indicates that only the D and I flags are initialised by
hardware, while the diagram indicates that the B flag is initialised too.

Meanwhile, https://www.nesdev.org/wiki/CPU_power_up_state says that
the power-on state of the NES CPU is $34 (exactly matching the diagram above)
but https://www.nesdev.org/wiki/Status_flags#The_B_flag says that the B flag
does not physically exist within P register, it's only relevant in the copy
of P that gets pushed to the stack by BRK (set), PHP (set), or an interrupt
signal (cleared).

As a result, the most sensible power-on state for the processor status register
is with the "interrupt disable" flag set and everything else cleared.
2023-02-03 18:16:57 +11:00
Seth Morabito
e210a40639 Use correct Mockito package 2023-01-12 16:56:28 -08:00
Seth Morabito
0bad9912ce Repaint Video window at 30fps
A bug caused the video window to repaint only when the cursor was
blinking. This meant that if the cursor was disabled, the window would
never update!

This change removes the repaint logic from the cursor blinking timer,
and instead puts it into its own periodic timer callback that runs
independently of cursor blink.
2023-01-11 13:31:22 -08:00
Seth Morabito
b36b442b14 Cleanup some IntelliJ IDEA warnings
Cleaning up some warnings for the first time in many eons.
2022-12-15 09:44:53 -08:00
Seth Morabito
5f8f743f7d Update for 1.3.2 2022-03-08 14:36:59 -08:00
Seth Morabito
4ce8bc86de Update pom.xml
- Switched from maven-assembly-plugin to maven-shade-plugin
  for final packaging
- Updated maven-jar-plugin to 3.2.0
2021-09-25 10:46:36 -07:00
Seth Morabito
4644fe9b55 Update junit and mockito
- JUnit has been bumped to 4.13.2.
- Mockito has gone from 1.10.19 to 3.12.4, and now compiles correctly
  on OpenJDK 16 using Maven 3.8.
2021-09-24 09:39:00 -07:00
Seth Morabito
655bbb3ab0
Merge pull request #26 from sethm/dependabot/maven/junit-junit-4.13.1
Bump junit from 4.12 to 4.13.1
2020-10-13 14:45:47 -07:00
dependabot[bot]
e2d31fd39c
Bump junit from 4.12 to 4.13.1
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-10-13 09:09:28 +00:00
Seth Morabito
087ba28b82 Force metal look-and-feel
* On Linux GTK+, the small text fields in the Status Panel displayed a
  large inner margin, cutting off text. This change forces Java to use the
  Metal look-and-feel for these fields, which forces no default inner
  margin.
2020-09-01 10:48:17 -07:00
Seth Morabito
8edca5d595 Update link in README 2020-05-21 15:01:34 -07:00
Seth Morabito
105a09067f Fix README for 1.3.1 2019-10-12 14:58:16 -07:00
Seth Morabito
08efd81be3 Merge branch 'master' into 1.3.branch 2019-10-12 14:55:43 -07:00
Seth Morabito
0aeb97bb56 Add new options -cpu and -rom 2019-10-12 14:54:05 -07:00
Seth Morabito
d466a21b3e Fix README screenshots (again) 2018-07-09 14:00:32 -07:00
Seth Morabito
31d1f7c5e1 Update screenshots 2018-03-01 19:21:56 -08:00
Seth Morabito
247f2ba2fc Update screenshots 2018-03-01 19:21:21 -08:00
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
b59fa63b63 Merge branch '1.2.branch' 2016-01-08 20:41:25 -08:00
Seth Morabito
c599df1cfb Set version to 1.3.0-SNAPSHOT 2016-01-08 19:14:00 -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
77 changed files with 6067 additions and 866 deletions

2
.gitignore vendored
View File

@ -1,6 +1,8 @@
*~
*.jar
*#
target
.DS_Store
.idea
symon.iml
/dependency-reduced-pom.xml

View File

@ -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

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

View File

@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

136
README.md
View File

@ -1,13 +1,13 @@
SYMON - A 6502 System Simulator
===============================
**Version:** 1.2.1
**Version:** 1.4.0
**Last Updated:** 8 January, 2016
**Last Updated:** 11 November, 2023
See the file COPYING for license.
![Symon Simulator in Action] (https://github.com/sethm/symon/raw/master/screenshots/full.jpg)
![Symon Simulator in Action](https://github.com/sethm/symon/raw/master/screenshots/full.jpg)
## 1.0 About
@ -16,17 +16,17 @@ Technologies 6502 microprocessor and compatibles. Symon is implemented
in Java. Its core goals are accuracy, ease of development, clear
documentation, and extensive test suites for validating correctness.
Symon simulates a complete system with a 1 MHz NMOS 6502, 32KB of RAM,
16KB of ROM, a MOS 6551 or Motorola 6850 ACIA, a MOS 6522 VIA, and an
experimental 6545 CRTC.
Symon simulates a complete system with a 1 MHz NMOS 6502 or CMOS
65C02, 32KB of RAM, 16KB of ROM, a MOS 6551 or Motorola 6850 ACIA, a
MOS 6522 VIA, and an experimental 6545 CRTC.
Symon has extensive unit tests to verify correctness, and fully passes
Klaus Dormann's 6502 Functional Test Suite as of version 0.8.2
(See [this thread on the 6502.org Forums] (http://forum.6502.org/viewtopic.php?f=2&t=2241)
(See [this thread on the 6502.org Forums](http://forum.6502.org/viewtopic.php?f=2&t=2241)
for more information about this functional test suite).
Symon is under constant, active development. Feedback and patches
are always welcome.
Symon is under active maintenance. Feedback and patches are always
welcome.
## 2.0 Requirements
@ -36,9 +36,9 @@ are always welcome.
## 3.0 Features
Symon can simulate multiple 6502 based architectures. At present, three
machines are implemented: Symon (the default), MULTICOMP, and a "Simple"
machine useful for debugging.
Symon can simulate multiple 6502 based architectures. At present, four
machines are implemented: Symon (the default), MULTICOMP, BenEater, and
a "Simple" machine useful for debugging.
### 3.1 Memory Maps
@ -64,27 +64,49 @@ memory.
- `$0000`--`$FFFF`: 64KB RAM
#### 3.1.4 BenEater Memory Map
- `$0000`--`$3FFF`: 16KB RAM
- `$5000`--`$5003`: MOS 6551 ACIA (Serial Console)
- `$6000`--`$600F`: 6522 VIA
- `$8000`--`$FFFF`: 16KB ROM
### 3.2 Serial Console and CPU Status
![Serial Console] (https://github.com/sethm/symon/raw/master/screenshots/console.png)
![Serial Console](https://github.com/sethm/symon/raw/master/screenshots/console.png)
The main window of the simulator acts as the primary Input/Output
system through a virtual serial terminal. The terminal is attached to
a simulated ACIA, including a programmable baud rate generator that
tries to approximate the correct "feel" of the programmed baud rate.
(The sample Enhanced BASIC ROM image is programmed for 9600 baud)
system through a virtual serial terminal. It also provides CPU status.
Contents of the accumulator, index registers, processor status flags,
disassembly of the instruction register, and stack pointer are all displayed.
It also provides CPU status. Contents of the accumulator, index
registers, processor status flags, disassembly of the instruction
register, and stack pointer are all displayed.
The terminal is attached to a simulated MOS 6551 ACIA. It behaves very much
as described in the datasheet, with some exceptions:
![Font Selection] (https://github.com/sethm/symon/raw/master/screenshots/font_selection.png)
- The simulated ACIA is permanently connected to the virtual terminal,
the Data Carrier Detect and Data Set Ready status bits always indicate
a connection is ready.
- The parity, stop-bits and bits-per-character settings are ignored. The
ACIA always sends and receives 8-bit characters, and parity errors
do not occur.
- The ACIA tries to honour the configured baud rate, but as a special case
the default "16x External Clock" rate is interpreted to mean "as fast as
possible" (The sample Enhanced BASIC ROM image is programmed for 9600 baud).
- The ACIA ignores the configured state of the Data Terminal Ready pin;
it is always ready to receive and transmit.
For more information on the MOS 6551 ACIA and its programming model,
see the official datasheet:
- [MOS 6551 ACIA](http://archive.6502.org/datasheets/mos_6551_acia.pdf)
![Font Selection](https://github.com/sethm/symon/raw/master/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](https://github.com/sethm/symon/raw/master/screenshots/load_rom.png)
Symon can load any appropriately sized ROM image. The Symon
architecture expects as 16KB (16384 byte) ROM image, while the
@ -95,32 +117,32 @@ address.
### 3.4 Memory Window
![Memory Window] (https://github.com/sethm/symon/raw/master/screenshots/memory_window.png)
![Memory Window](https://github.com/sethm/symon/raw/master/screenshots/memory_window.png)
Memory contents can be viewed (and edited) one page at a time through the Memory Window.
### 3.5 Trace Log
![Trace Log] (https://github.com/sethm/symon/raw/master/screenshots/trace_log.png)
![Trace Log](https://github.com/sethm/symon/raw/master/screenshots/trace_log.png)
The last 20,000 execution steps are disassembled and logged to the Trace Log
Window.
### 3.6 Simulator Speeds
![Speeds] (https://github.com/sethm/symon/raw/master/screenshots/simulator_menu.png)
![Speeds](https://github.com/sethm/symon/raw/master/screenshots/simulator_menu.png)
Simulated speeds may be set from 1MHz to 8MHz.
### 3.7 Breakpoints
![Breakpoints] (https://github.com/sethm/symon/raw/master/screenshots/breakpoints.png)
![Breakpoints](https://github.com/sethm/symon/raw/master/screenshots/breakpoints.png)
Breakpoints can be set and removed through the Breakpoints window.
### 3.8 Experimental 6545 CRTC Video
![Composite Video] (https://github.com/sethm/symon/raw/master/screenshots/video_window.png)
![Composite Video](https://github.com/sethm/symon/raw/master/screenshots/video_window.png)
This feature is highly experimental. It's possible to open a video window
from the "View" menu. This window simulates the output of a MOS 6545 CRT
@ -154,13 +176,13 @@ between the simulated 6545 and a real 6545:
of 6545 programming tricks that were achieved by updating the
frame address during vertical and horizontal sync times are not
achievable. There is no way (for example) to change the Display Start
Address (R12 and R13) while a frame is being drawn.
Address (R12 and R13) while a frame is being drawn.
For more information on the 6545 CRTC and its programming model, please see the following resources
- [CRTC 6545/6845 Information (André Fachat)] (http://6502.org/users/andre/hwinfo/crtc/index.html)
- [CRTC Operation (André Fachat)] (http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
- [MOS 6545 Datasheet (PDF)] (http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
- [CRTC 6545/6845 Information (André Fachat)](http://6502.org/users/andre/hwinfo/crtc/index.html)
- [CRTC Operation (André Fachat)](http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
- [MOS 6545 Datasheet (PDF)](http://www.6502.org/users/andre/hwinfo/crtc/crtc.html)
#### 3.8.1 Example BASIC Program to test Video
@ -193,6 +215,20 @@ Java 1.8 or greater, just type:
When Symon is running, you should be presented with a simple graphical
interface.
#### 4.1.1 Command Line Options
Two command line options may be passed to the JAR file on startup,
to specify machine type and CPU type. The options are:
- `-c`,`-cpu 6502`: Use the NMOS 6502 CPU type by default.
- `-c`,`-cpu 65c02`: Use the CMOS 65C02 CPU type by default.
- `-c`,`-machine symon`: Use the **Symon** machine type by default.
- `-c`,`-machine multicomp`: Use the **Multicomp** machine type by default.
- `-m`,`-machine simple`: Use the **Simple** machine type by default.
- `-m`,`-machine beneater`: Use the **BenEater** machine type by default.
- `-r`,`-rom <file>`: Use the specified file as the ROM image.
- `-b`,`-brk`: Halt the simulator on a BRK instruction (default is to continue)
### 4.2 ROM images
The simulator requires a ROM image loaded into memory to work
@ -200,10 +236,9 @@ properly. Without a ROM in memory, the simulator will not be able to
reset, since the reset vector for the 6502 is located in the ROM
address space.
By default, any file named `rom.bin` that exists in the same directory
where Symon is launched will be loaded as a ROM image. ROM images can
also be swapped out at run-time with the "Load ROM Image..." in the
File menu.
ROM images can be loaded with the `-rom` argument when running
Symon from the command line. ROM images can also be swapped out at
run-time with the "Load ROM..." item in the File menu.
The "samples" directory contains a ROM image for the Symon
architecture named 'ehbasic.rom', containing Lee Davison's Enhanced
@ -235,16 +270,32 @@ running.
## 5.0 Revision History
- **1.4.0:** 11 November 2023 - Adds a new machine, the Ben Eater
machine. Correct handling of 6551 interrupts, and several 6551
bug fixes. Fixes power-on status of 6502 status register. Fixes a
bug with ASCII backspace character not moving the cursor
backwards. Finally, "halt on BRK" is no longer enabled by default,
but can be set at runtime or by a command line flag. Thank you to
Tim Allen and Chelsea Wilkinson for contributions!
- **1.3.2:** 8 March 2022 - Minor bug fixes.
- **1.3.1:** 12 October, 2019 - Add support for new command line
option `-cpu <type>` to specify one of `6502` or `65c02` on startup,
and new option `-rom <file>` to specify a ROM file to load.
- **1.3.0:** 24 February, 2018 - Adds support for 65C02 opcodes.
- **1.2.1:** 8 January, 2016 - Remove dependency on Java 8. Now
supports compiling and running under Java 1.7.
- **1.2.0:** 3 January, 2016 - Add symbolic disassembly to breakpoints
window.
- **1.1.1:** 2 January, 2016 - Minor enhancement: Allows breakpoints
to be added with the Enter key.
- **1.1.0:** 31 December, 2015 - Fixed delay loop to better
- **1.1.0:** 31 December, 2015 - Fixed delay loop to better
simulate various clock speeds. Added ability to select clock
speed at runtime. Status display now shows the next instruction
to be executed, instead of the last instruction executed.
@ -328,7 +379,12 @@ running.
**Copyright (c) 2014 Seth J. Morabito &lt;web@loomcom.com&gt;**
Portions Copyright (c) 2014 Maik Merten &lt;maikmerten@googlemail.com&gt;
- Portions Copyright (c) 2014 Maik Merten
&lt;maikmerten@googlemail.com&gt;
- Portions Copyright (c) 2022 Tim Allen
&lt;thristian@gmail.com&gt;
- Portions Copyright (c) 2023 Chelsea Wilkinson
&lt;mail@chelseawilkinson.me&gt;
Additional components used in this project are copyright their respective owners.
@ -338,10 +394,10 @@ Additional components used in this project are copyright their respective owners
This project would not have been possible without the following resources:
- [Andrew Jacobs' 6502 Pages] (http://www.obelisk.demon.co.uk/6502/), for
- [Andrew Jacobs' 6502 Pages](http://obelisk.me.uk/6502/), for
wonderfully detailed information about the 6502
- [Neil Parker's "The 6502/65C02/65C816 Instruction Set Decoded"] (http://www.llx.com/~nparker/a2/opcodes.html),
- [Neil Parker's "The 6502/65C02/65C816 Instruction Set Decoded"](http://www.llx.com/~nparker/a2/opcodes.html),
for information about how instructions are coded
## 9.0 Licensing

76
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>com.loomcom.symon</groupId>
<artifactId>symon</artifactId>
<packaging>jar</packaging>
<version>1.2.1</version>
<version>1.4.0</version>
<name>symon</name>
<url>http://www.loomcom.com/symon</url>
<properties>
@ -15,72 +15,60 @@
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.1.3</version>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.grahamedgecombe.jterminal</groupId>
<artifactId>jterminal</artifactId>
<version>1.0.2.3-loomcom</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<artifactId>mockito-core</artifactId>
<version>4.8.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<artifactId>maven-shade-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<appendAssemblyId>false</appendAssemblyId>
<archive>
<manifest>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.loomcom.symon.Main</mainClass>
</manifest>
</archive>
</transformer>
</transformers>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
<goal>shade</goal>
</goals>
</execution>
</executions>
@ -88,11 +76,11 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<version>3.7.0</version>
<configuration>
<compilerArgument>-Xlint:unchecked</compilerArgument>
<source>1.7</source>
<target>1.7</target>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
@ -100,7 +88,7 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.6</version>
<version>3.2.0</version>
<configuration>
<archive>
<manifest>

View File

@ -11,7 +11,6 @@ Sample Programs
When loaded at address $0300, this program will echo back to the console
anything typed.
Both hello.prg and echo_poll.prg were assembled with the Ophis assembler:
https://hkn.eecs.berkeley.edu/~mcmartin/ophis/
@ -19,23 +18,25 @@ Both hello.prg and echo_poll.prg were assembled with the Ophis assembler:
3. echo_irq.rom
This is another echo program, and behaves identically to echo_poll.prg,
except it is interrupt-driven.
except it is interrupt-driven. This ROM can be loaded either through the
"File->Load ROM..." menu in Symon, or by specifying the path to
"echo_irq.rom" with the "-jar" command line option when running Symon,
for example: `java -jar symon-1.3.2.jar -rom samples/echo_irq.rom`
4. ehbasic.rom
This is Lee Davison's Enhanced 6502 BASIC.
To use this ROM image, just copy the file 'ehbasic.rom' into the directory
where you run Symon. Rename the file to 'rom.bin'. When you start Symon,
the ROM file will be automatically loaded at address $d000.
The EhBASIC ROM can be loaded either through the "File -> Load ROM..."
menu in Symon, or by specifying the path to "ehbasic.rom" with
the "-jar" command line option when running Symon, for example:
`java -jar symon-1.3.2.jar -rom samples/ehbasic.rom`
Click the "Run" button and EhBASIC should automatically start running.
Type 'C' to do a cold start.
Then, type $C000 when prompted for the memory size.
NOTE: EhBASIC only wants upper-case input. This confused me at first!
Then, type '49152' (without the quotes) when prompted for the memory size.
More information can be found in the 'ehbasic' directory, and by visiting
the EhBASIC web page:

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

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;
}

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

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;
}

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

View File

@ -1,5 +1,5 @@
MEMORY {
RAM1: start = $0000, size = $8000;
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;
@ -13,6 +13,6 @@ VECTORS: load = ROMV, type = ro;
}
SYMBOLS {
__STACKSIZE__ = $0300;
__STACKSIZE__: type = weak, value = $0300;
}

View File

@ -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,10 +53,10 @@ 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);
@ -74,30 +75,30 @@ 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 Device to add
* @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) {
@ -114,12 +115,12 @@ public class Bus {
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.
*
@ -129,7 +130,7 @@ public class Bus {
public void addDevice(Device device) throws MemoryRangeException {
addDevice(device, 0);
}
/**
* Remove a device from the bus.
@ -137,7 +138,7 @@ public class Bus {
* @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();
@ -154,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) & 0xff;
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));
}
@ -217,17 +218,17 @@ public class Bus {
public SortedSet<Device> getDevices() {
// create an ordered set of devices, ordered by device priorities
SortedSet<Device> devices = new TreeSet<>();
List<Integer> priorities = new ArrayList<>(deviceMap.keySet());
Collections.sort(priorities);
for (int priority : priorities) {
SortedSet<Device> deviceSet = deviceMap.get(priority);
for (Device device : deviceSet) {
devices.add(device);
}
}
return devices;
}

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,7 @@ public class CpuState {
public int a;
/**
* X index regsiter
* X index register
*/
public int x;
@ -61,13 +61,12 @@ public class CpuState {
public CpuState() {}
/**
* Snapshot a copy of the CpuState.
*
* (This is a copy constructor rather than an implementation of <code>Cloneable</code>
* based on Josh Bloch's recommendation)
* Snapshot a copy of the CpuState. (This is a copy constructor rather than an
* implementation of <code>Cloneable</code> based on Josh Bloch's recommendation)
*
* @param s The CpuState to copy.
*/
@SuppressWarnings("CopyConstructorMissesField")
public CpuState(CpuState s) {
this.a = s.a;
this.x = s.x;
@ -83,6 +82,7 @@ public class CpuState {
this.nextArgs[1] = s.nextArgs[1];
this.instSize = s.instSize;
this.opTrap = s.opTrap;
this.nmiAsserted = s.nmiAsserted;
this.irqAsserted = s.irqAsserted;
this.carryFlag = s.carryFlag;
this.negativeFlag = s.negativeFlag;

View File

@ -46,20 +46,17 @@ 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
}
/**
@ -71,7 +68,11 @@ public interface InstructionTable {
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
*/
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
"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
*/
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
*/
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
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.
*/
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
};
}

View File

@ -28,6 +28,9 @@ package com.loomcom.symon;
import com.loomcom.symon.machines.MulticompMachine;
import com.loomcom.symon.machines.SimpleMachine;
import com.loomcom.symon.machines.SymonMachine;
import com.loomcom.symon.machines.BenEaterMachine;
import org.apache.commons.cli.*;
import java.util.Locale;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
@ -41,71 +44,126 @@ public class Main {
*
* @param args Program arguments
*/
public static void main(String args[]) throws Exception {
public static void main(String[] args) throws Exception {
Class machineClass = SymonMachine.class;
for(int i = 0; i < args.length; ++i) {
String arg = args[i].toLowerCase(Locale.ENGLISH);
if(arg.equals("-machine") && (i+1) < args.length) {
String machine = args[i+1].trim().toLowerCase(Locale.ENGLISH);
Options options = new Options();
options.addOption(new Option("m", "machine", true, "Specify machine type."));
options.addOption(new Option("c", "cpu", true, "Specify CPU type."));
options.addOption(new Option("r", "rom", true, "Specify ROM file."));
options.addOption(new Option("b", "brk", false, "Halt on BRK"));
CommandLineParser parser = new DefaultParser();
try {
CommandLine line = parser.parse(options, args);
InstructionTable.CpuBehavior cpuBehavior = null;
String romFile = null;
boolean haltOnBreak = false;
if (line.hasOption("machine")) {
String machine = line.getOptionValue("machine").toLowerCase(Locale.ENGLISH);
switch (machine) {
case "symon":
machineClass = SymonMachine.class;
break;
case "multicomp":
machineClass = MulticompMachine.class;
break;
case "simple":
machineClass = SimpleMachine.class;
break;
case "symon":
machineClass = SymonMachine.class;
break;
case "beneater":
machineClass = BenEaterMachine.class;
break;
default:
System.err.println("Could not start Symon. Unknown machine type " + machine);
return;
}
}
}
while (true) {
if (machineClass == null) {
Object[] possibilities = {"Symon", "Multicomp", "Simple"};
String s = (String)JOptionPane.showInputDialog(
null,
"Please choose the machine type to be emulated:",
"Machine selection",
JOptionPane.PLAIN_MESSAGE,
null,
possibilities,
"Symon");
if (s != null && s.equals("Multicomp")) {
machineClass = MulticompMachine.class;
} else if (s != null && s.equals("Simple")) {
machineClass = SimpleMachine.class;
} else {
machineClass = SymonMachine.class;
if (line.hasOption("cpu")) {
String cpu = line.getOptionValue("cpu").toLowerCase(Locale.ENGLISH);
switch (cpu) {
case "6502":
cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502;
break;
case "65c02":
cpuBehavior = InstructionTable.CpuBehavior.CMOS_6502;
break;
case "65c816":
cpuBehavior = InstructionTable.CpuBehavior.CMOS_65816;
break;
default:
System.err.println("Could not start Symon. Unknown cpu type " + cpu);
return;
}
}
final Simulator simulator = new Simulator(machineClass);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// Create the main UI window
simulator.createAndShowUi();
} catch (Exception e) {
e.printStackTrace();
if (line.hasOption("rom")) {
romFile = line.getOptionValue("rom");
}
if (line.hasOption("brk")) {
haltOnBreak = true;
}
while (true) {
if (machineClass == null) {
Object[] possibilities = {"Symon", "Multicomp", "Simple", "BenEater"};
String s = (String)JOptionPane.showInputDialog(
null,
"Please choose the machine type to be emulated:",
"Machine selection",
JOptionPane.PLAIN_MESSAGE,
null,
possibilities,
"Symon");
if (s != null && s.equals("Multicomp")) {
machineClass = MulticompMachine.class;
} else if (s != null && s.equals("Simple")) {
machineClass = SimpleMachine.class;
} else if (s != null && s.equals("BenEater")) {
machineClass = BenEaterMachine.class;
} else {
machineClass = SymonMachine.class;
}
}
});
Simulator.MainCommand cmd = simulator.waitForCommand();
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
machineClass = null;
} else {
break;
if (cpuBehavior == null) {
cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502;
}
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile, haltOnBreak);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// Create the main UI window
simulator.createAndShowUi();
} catch (Exception e) {
e.printStackTrace();
}
}
});
Simulator.MainCommand cmd = simulator.waitForCommand();
if (cmd.equals(Simulator.MainCommand.SELECTMACHINE)) {
machineClass = null;
} else {
break;
}
}
} catch (ParseException ex) {
System.err.println("Could not start Symon. Reason: " + ex.getMessage());
}
}
}

View File

@ -29,8 +29,6 @@ public interface Preferences {
int DEFAULT_PROGRAM_LOAD_ADDRESS = 0x0300;
boolean DEFAULT_HALT_ON_BREAK = true;
JDialog getDialog();
int getProgramStartAddress();

View File

@ -31,6 +31,7 @@ import com.loomcom.symon.ui.Console;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.monitor.CounterMonitor;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import java.awt.*;
@ -125,6 +126,8 @@ public class Simulator {
private MainCommand command = MainCommand.NONE;
private boolean haltOnBreak;
public enum MainCommand {
NONE,
SELECTMACHINE
@ -136,9 +139,16 @@ public class Simulator {
private static final String[] STEPS = {"1", "5", "10", "20", "50", "100"};
public Simulator(Class machineClass) throws Exception {
this(machineClass, InstructionTable.CpuBehavior.NMOS_6502, null, false);
}
public Simulator(Class machineClass, InstructionTable.CpuBehavior cpuType,
String romFile, boolean haltOnBreak) throws Exception {
this.haltOnBreak = haltOnBreak;
this.breakpoints = new Breakpoints(this);
this.machine = (Machine) machineClass.getConstructors()[0].newInstance();
this.machine = (Machine) machineClass.getConstructors()[0].newInstance(romFile);
this.machine.getCpu().setBehavior(cpuType);
// Initialize final fields in the constructor.
this.traceLog = new TraceLog();
@ -169,7 +179,7 @@ public class Simulator {
// File Chooser
fileChooser = new JFileChooser(System.getProperty("user.dir"));
preferences = new PreferencesDialog(mainWindow, true);
preferences = new PreferencesDialog(mainWindow, true, haltOnBreak);
// Panel for Console and Buttons
JPanel consoleContainer = new JPanel();
@ -348,7 +358,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();
}
@ -678,6 +688,21 @@ public class Simulator {
}
}
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);
@ -868,6 +893,14 @@ public class Simulator {
JMenuItem selectMachineItem = new JMenuItem(new SelectMachineAction());
simulatorMenu.add(selectMachineItem);
// "CPU" sub-menu
JMenu cpuTypeMenu = new JMenu("CPU");
ButtonGroup cpuGroup = new ButtonGroup();
makeCpuMenuItem("6502", Cpu.CpuBehavior.NMOS_6502, cpuTypeMenu, cpuGroup);
makeCpuMenuItem("65C02", Cpu.CpuBehavior.CMOS_6502, cpuTypeMenu, cpuGroup);
makeCpuMenuItem("65C816", Cpu.CpuBehavior.CMOS_65816, cpuTypeMenu, cpuGroup);
// "Clock Speed" sub-menu
JMenu speedSubMenu = new JMenu("Clock Speed");
ButtonGroup speedGroup = new ButtonGroup();
@ -878,6 +911,7 @@ public class Simulator {
makeSpeedMenuItem(8, speedSubMenu, speedGroup);
simulatorMenu.add(speedSubMenu);
simulatorMenu.add(cpuTypeMenu);
// "Breakpoints"
final JCheckBoxMenuItem showBreakpoints = new JCheckBoxMenuItem(new ToggleBreakpointWindowAction());
@ -914,6 +948,17 @@ public class Simulator {
subMenu.add(item);
group.add(item);
}
private void makeCpuMenuItem(String cpu, Cpu.CpuBehavior behavior, JMenu subMenu, ButtonGroup group) {
Action action = new SetCpuAction(cpu, behavior);
JCheckBoxMenuItem item = new JCheckBoxMenuItem(action);
item.setSelected(machine.getCpu().getBehavior() == behavior);
subMenu.add(item);
group.add(item);
}
}
private void updateVisibleState() {

View File

@ -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;
@ -100,41 +101,46 @@ public abstract class Acia extends Device {
/**
* @return The contents of the status register.
*/
public abstract int statusReg();
public abstract int statusReg(boolean cpuAccess);
@Override
public String toString() {
return name + "@" + String.format("%04X", baseAddress);
}
public synchronized int rxRead() {
lastRxRead = System.nanoTime();
overrun = false;
rxFull = false;
public synchronized int rxRead(boolean cpuAccess) {
if (cpuAccess) {
lastRxRead = System.nanoTime();
overrun = false;
rxFull = false;
}
return rxChar;
}
public synchronized void rxWrite(int data) {
if(rxFull) {
if (rxFull) {
overrun = true;
}
rxFull = true;
if (receiveIrqEnabled) {
interrupt = true;
getBus().assertIrq();
}
rxChar = data;
}
public synchronized int txRead() {
txEmpty = true;
public synchronized int txRead(boolean cpuAccess) {
if (cpuAccess) {
txEmpty = true;
if (transmitIrqEnabled) {
getBus().assertIrq();
if (transmitIrqEnabled) {
interrupt = true;
getBus().assertIrq();
}
}
return txChar;
}

View File

@ -28,7 +28,7 @@ import com.loomcom.symon.exceptions.MemoryRangeException;
/**
* This is a simulation of the MOS 6551 ACIA, with limited
* functionality. Interrupts are not supported.
* functionality.
* <p/>
* Unlike a 16550 UART, the 6551 ACIA has only one-byte transmit and
* receive buffers. It is the programmer's responsibility to check the
@ -53,15 +53,23 @@ public class Acia6551 extends Acia {
public Acia6551(int address) throws MemoryRangeException {
super(address, ACIA_SIZE, "ACIA");
// Figure 6 in the 6551 ACIA data sheet says the "hardware reset"
// state of the Control Register is all zeros.
setControlRegister(0b00000000);
// Figure 7 of the 6551 ACIA data sheet says the "hardware reset"
// state of the Command Register is zeros, but Transmitter Control
// is set to "interrupt disabled, ready to send".
setCommandRegister(0b00000010);
}
@Override
public int read(int address) throws MemoryAccessException {
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
switch (address) {
case DATA_REG:
return rxRead();
return rxRead(cpuAccess);
case STAT_REG:
return statusReg();
return statusReg(cpuAccess);
case CMND_REG:
return commandRegister;
case CTRL_REG:
@ -74,16 +82,16 @@ public class Acia6551 extends Acia {
@Override
public void write(int address, int data) throws MemoryAccessException {
switch (address) {
case 0:
case DATA_REG:
txWrite(data);
break;
case 1:
case STAT_REG:
reset();
break;
case 2:
case CMND_REG:
setCommandRegister(data);
break;
case 3:
case CTRL_REG:
setControlRegister(data);
break;
default:
@ -110,67 +118,60 @@ public class Acia6551 extends Acia {
controlRegister = data;
int rate = 0;
// If the value of the data is 0, this is a request to reset,
// otherwise it's a control update.
if (data == 0) {
reset();
} else {
// Mask the lower three bits to get the baud rate.
int baudSelector = data & 0x0f;
switch (baudSelector) {
case 0:
rate = 0;
break;
case 1:
rate = 50;
break;
case 2:
rate = 75;
break;
case 3:
rate = 110; // Real rate is actually 109.92
break;
case 4:
rate = 135; // Real rate is actually 134.58
break;
case 5:
rate = 150;
break;
case 6:
rate = 300;
break;
case 7:
rate = 600;
break;
case 8:
rate = 1200;
break;
case 9:
rate = 1800;
break;
case 10:
rate = 2400;
break;
case 11:
rate = 3600;
break;
case 12:
rate = 4800;
break;
case 13:
rate = 7200;
break;
case 14:
rate = 9600;
break;
case 15:
rate = 19200;
break;
}
setBaudRate(rate);
// Mask the lower four bits to get the baud rate.
int baudSelector = data & 0x0f;
switch (baudSelector) {
case 0:
rate = 0;
break;
case 1:
rate = 50;
break;
case 2:
rate = 75;
break;
case 3:
rate = 110; // Real rate is actually 109.92
break;
case 4:
rate = 135; // Real rate is actually 134.58
break;
case 5:
rate = 150;
break;
case 6:
rate = 300;
break;
case 7:
rate = 600;
break;
case 8:
rate = 1200;
break;
case 9:
rate = 1800;
break;
case 10:
rate = 2400;
break;
case 11:
rate = 3600;
break;
case 12:
rate = 4800;
break;
case 13:
rate = 7200;
break;
case 14:
rate = 9600;
break;
case 15:
rate = 19200;
break;
}
setBaudRate(rate);
}
@ -178,8 +179,8 @@ public class Acia6551 extends Acia {
* @return The contents of the status register.
*/
@Override
public int statusReg() {
// TODO: Parity Error, Framing Error, DTR, DSR, and Interrupt flags.
public int statusReg(boolean cpuAccess) {
// TODO: Parity Error, Framing Error, DTR, and DSR flags.
int stat = 0;
if (rxFull && System.nanoTime() >= (lastRxRead + baudRateDelay)) {
stat |= 0x08;
@ -190,17 +191,29 @@ public class Acia6551 extends Acia {
if (overrun) {
stat |= 0x04;
}
if (interrupt) {
stat |= 0x80;
}
if (cpuAccess) {
interrupt = false;
}
return stat;
}
private synchronized void reset() {
txChar = 0;
txEmpty = true;
rxChar = 0;
rxFull = false;
receiveIrqEnabled = false;
transmitIrqEnabled = false;
}
// Figure 6 in the 6551 ACIA data sheet says the "program reset"
// event does not modify the control register.
// Figure 7 in the 6551 ACIA data sheet says the "program reset"
// event keeps the "parity check" configuration in the command
// register, but resets the other bits to defaults.
setCommandRegister((commandRegister & 0xe0) | 0x02);
// Figure 8 in the 6551 ACIA data sheet says the "program reset"
// event clears the "overrun" flag but otherwise has no effect.
overrun = false;
}
}

View File

@ -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
@ -43,7 +43,7 @@ public class Acia6850 extends Acia {
static final int STAT_REG = 0; // read-only
static final int CTRL_REG = 0; // write-only
static final int RX_REG = 1; // read-only
static final int TX_REG = 1; // write-only
@ -53,12 +53,12 @@ public class Acia6850 extends Acia {
}
@Override
public int read(int address) throws MemoryAccessException {
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
switch (address) {
case RX_REG:
return rxRead();
return rxRead(cpuAccess);
case STAT_REG:
return statusReg();
return statusReg(cpuAccess);
default:
throw new MemoryAccessException("No register.");
@ -78,7 +78,7 @@ public class Acia6850 extends Acia {
throw new MemoryAccessException("No register.");
}
}
private void setCommandRegister(int data) {
// Bits 0 & 1 control the master reset
if((data & 0x01) != 0 && (data & 0x02) != 0) {
@ -97,8 +97,8 @@ public class Acia6850 extends Acia {
* @return The contents of the status register.
*/
@Override
public int statusReg() {
// TODO: Parity Error, Framing Error, DTR, DSR, and Interrupt flags.
public int statusReg(boolean cpuAccess) {
// TODO: Parity Error, Framing Error, DTR, and DSR flags.
int stat = 0;
if (rxFull && System.nanoTime() >= (lastRxRead + baudRateDelay)) {
stat |= 0x01;
@ -109,7 +109,14 @@ public class Acia6850 extends Acia {
if (overrun) {
stat |= 0x20;
}
if (interrupt) {
stat |= 0x80;
}
if (cpuAccess) {
interrupt = false;
}
return stat;
}
@ -118,6 +125,7 @@ public class Acia6850 extends Acia {
overrun = false;
rxFull = false;
txEmpty = true;
interrupt = false;
}
}

View File

@ -119,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) {
@ -142,7 +142,7 @@ public class Crtc extends Device {
public int getCharAtAddress(int address) throws MemoryAccessException {
// TODO: Row/Column addressing
return memory.read(address);
return memory.read(address, false);
}
public int getHorizontalDisplayed() {

View File

@ -72,7 +72,7 @@ public abstract class Device implements Comparable<Device> {
/* Methods required to be implemented by inheriting classes. */
public abstract void write(int address, int data) throws MemoryAccessException;
public abstract int read(int address) throws MemoryAccessException;
public abstract int read(int address, boolean cpuAccess) throws MemoryAccessException;
public abstract String toString();

View File

@ -94,7 +94,7 @@ public class Memory extends Device {
}
public int read(int address) throws MemoryAccessException {
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
return this.mem[address];
}

View File

@ -94,7 +94,7 @@ public class SdController extends Device {
}
@Override
public int read(int address) throws MemoryAccessException {
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
switch (address) {
case 0:
return readData();

View File

@ -75,7 +75,7 @@ public class Via6522 extends Pia {
}
@Override
public int read(int address) throws MemoryAccessException {
public int read(int address, boolean cpuAccess) throws MemoryAccessException {
Register[] registers = Register.values();
if (address >= registers.length) {

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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 */
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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() {
}
}

View File

@ -0,0 +1,494 @@
/*
* 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 8: // ASCII Backspace
case 127: // ASCII Delete
if (cursorColumn > 0) {
cells[cursorRow][--cursorColumn] = null;
}
continue;
case 7:
bellStrategy.soundBell();
continue;
}
if (cursorColumn >= columns) {
cursorColumn = 0;
cursorRow++;
}
if (cursorRow >= bufferSize) {
for (int i = 1; i < bufferSize; i++) {
System.arraycopy(cells[i], 0, cells[i - 1], 0, columns);
}
for (int i = 0; i < columns; i++) {
cells[bufferSize - 1][i] = null;
}
cursorRow--;
}
Color back = backgroundBold ? SgrColor.COLOR_BRIGHT[backgroundColor] : SgrColor.COLOR_NORMAL[backgroundColor];
Color fore = foregroundBold ? SgrColor.COLOR_BRIGHT[foregroundColor] : SgrColor.COLOR_NORMAL[foregroundColor];
if (ch != '\n') {
cells[cursorRow][cursorColumn++] = new TerminalCell(ch, back, fore);
}
}
}
}
/**
* The default number of columns.
*/
private static final int DEFAULT_COLUMNS = 80;
/**
* The default number of rows.
*/
private static final int DEFAULT_ROWS = 25;
/**
* The tab width in characters.
*/
private static final int TAB_WIDTH = 8;
/**
* The default foreground bold flag.
*/
private static final boolean DEFAULT_FOREGROUND_BOLD = false;
/**
* The default background bold flag.
*/
private static final boolean DEFAULT_BACKGROUND_BOLD = false;
/**
* The default foreground color.
*/
private static final int DEFAULT_FOREGROUND_COLOR = 7;
/**
* The default background color.
*/
private static final int DEFAULT_BACKGROUND_COLOR = 0;
/**
* The ANSI control sequence listener.
*/
private final AnsiControlSequenceListener listener = this.new Vt100Listener();
/**
* The ANSI control sequence parser.
*/
private final AnsiControlSequenceParser parser = new AnsiControlSequenceParser(listener);
/**
* The current bell strategy.
*/
private BellStrategy bellStrategy = new NopBellStrategy();
/**
* The array of cells.
*/
private TerminalCell[][] cells;
/**
* The number of columns.
*/
private int columns;
/**
* The number of rows.
*/
private int rows;
/**
* The buffer size.
*/
private int bufferSize;
/**
* The cursor row.
*/
private int cursorRow = 0;
/**
* The cursor column.
*/
private int cursorColumn = 0;
/**
* The current foreground bold flag.
*/
private boolean foregroundBold = DEFAULT_FOREGROUND_BOLD;
/**
* The current background bold flag.
*/
private boolean backgroundBold = DEFAULT_BACKGROUND_BOLD;
/**
* The current foreground color.
*/
private int foregroundColor = DEFAULT_FOREGROUND_COLOR;
/**
* The current background color.
*/
private int backgroundColor = DEFAULT_BACKGROUND_COLOR;
/**
* Creates the terminal model with the default number of columns and rows,
* and the default buffer size.
*/
public Vt100TerminalModel() {
this(DEFAULT_COLUMNS, DEFAULT_ROWS);
}
/**
* Creates the terminal model with the specified number of columns and
* rows. The buffer size is set to the number of rows.
* @param columns The number of columns.
* @param rows The number of rows.
* @throws IllegalArgumentException if the number of rows or columns is
* negative.
*/
public Vt100TerminalModel(int columns, int rows) {
this(columns, rows, rows);
}
/**
* Creates the terminal model with the specified number of columns and rows
* and the specified buffer size.
* @param columns The number of columns.
* @param rows The number of rows.
* @param bufferSize The buffer size.
* @throws IllegalArgumentException if the number of rows or columns is
* negative, or if the buffer size is less than the number of rows.
*/
public Vt100TerminalModel(int columns, int rows, int bufferSize) {
if (columns < 0 || rows < 0 || bufferSize < 0) {
throw new IllegalArgumentException("Zero or positive values only allowed for columns, rows and buffer size.");
}
if (bufferSize < rows) {
throw new IllegalArgumentException("The buffer is too small");
}
this.columns = columns;
this.rows = rows;
this.bufferSize = bufferSize;
init();
}
/**
* Initializes the terminal model.
*/
private void init() {
cells = new TerminalCell[bufferSize][columns];
}
@Override
public int getCursorRow() {
return cursorRow;
}
@Override
public void setCursorRow(int row) {
if (row < 0 || row >= bufferSize) {
throw new IllegalArgumentException("row out of range");
}
cursorRow = row;
}
@Override
public int getCursorColumn() {
return cursorColumn;
}
@Override
public void setCursorColumn(int column) {
if (column < 0 || column >= columns) {
throw new IllegalArgumentException("column out of range");
}
cursorColumn = column;
}
@Override
public TerminalCell getCell(int column, int row) {
if (column < 0 || row < 0 || column >= columns || row >= bufferSize) {
throw new IndexOutOfBoundsException();
}
return cells[row][column];
}
@Override
public void setCell(int column, int row, TerminalCell cell) {
if (column < 0 || row < 0 || column >= columns || row >= bufferSize) {
throw new IndexOutOfBoundsException();
}
cells[row][column] = cell;
}
@Override
public void print(String str) {
if (str == null) {
throw new NullPointerException("str");
}
parser.parse(str);
}
@Override
public int getColumns() {
return columns;
}
@Override
public int getRows() {
return rows;
}
@Override
public int getBufferSize() {
return bufferSize;
}
@Override
public 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];
}
}

View File

@ -0,0 +1,169 @@
/*
* Copyright (c) 2016 Seth J. Morabito <web@loomcom.com>
* Maik Merten <maikmerten@googlemail.com>
* Chelsea Wilkinson <mail@chelseawilkinson.me>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.loomcom.symon.machines;
import com.loomcom.symon.Bus;
import com.loomcom.symon.Cpu;
import com.loomcom.symon.devices.*;
import com.loomcom.symon.exceptions.MemoryRangeException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
public class BenEaterMachine implements Machine {
private final static Logger logger = LoggerFactory.getLogger(BenEaterMachine.class.getName());
// Constants used by the simulated system. These define the memory map.
private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff;
// 16K of RAM from $0000 - $3FFF
private static final int MEMORY_BASE = 0x0000;
private static final int MEMORY_SIZE = 0x4000;
// VIA at $6000-$600F
private static final int PIA_BASE = 0x6000;
// ACIA at $5000-$5003
private static final int ACIA_BASE = 0x5000;
// CRTC at $4000-$4001
private static final int CRTC_BASE = 0x4000;
// 32KB ROM at $8000-$FFFF
private static final int ROM_BASE = 0x8000;
private static final int ROM_SIZE = 0x8000;
// The simulated peripherals
private final Bus bus;
private final Cpu cpu;
private final Acia acia;
private final Pia pia;
private final Crtc crtc;
private final Memory ram;
private Memory rom;
public BenEaterMachine(String romFile) throws Exception {
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
this.pia = new Via6522(PIA_BASE);
this.acia = new Acia6551(ACIA_BASE);
this.crtc = new Crtc(CRTC_BASE, ram);
bus.addCpu(cpu);
bus.addDevice(ram);
bus.addDevice(pia);
bus.addDevice(acia);
bus.addDevice(crtc);
if (romFile != null) {
File romImage = new File(romFile);
if (romImage.canRead()) {
logger.info("Loading ROM image from file {}", romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
} else {
logger.info("Default ROM file {} not found, loading empty R/W memory image.", romImage);
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}
} else {
logger.info("No ROM file specified, loading empty R/W memory image.");
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}
bus.addDevice(rom);
}
@Override
public Bus getBus() {
return bus;
}
@Override
public Cpu getCpu() {
return cpu;
}
@Override
public Memory getRam() {
return ram;
}
@Override
public Acia getAcia() {
return acia;
}
@Override
public Pia getPia() {
return pia;
}
@Override
public Crtc getCrtc() {
return crtc;
}
@Override
public Memory getRom() {
return rom;
}
public void setRom(Memory rom) throws MemoryRangeException {
if(this.rom != null) {
bus.removeDevice(this.rom);
}
this.rom = rom;
bus.addDevice(this.rom);
}
@Override
public int getRomBase() {
return ROM_BASE;
}
@Override
public int getRomSize() {
return ROM_SIZE;
}
@Override
public int getMemorySize() {
return MEMORY_SIZE;
}
@Override
public String getName() {
return "benEater";
}
}

View File

@ -68,7 +68,7 @@ public class MulticompMachine implements Machine {
private Memory rom;
public MulticompMachine() throws Exception {
public MulticompMachine(String romFile) throws Exception {
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
@ -79,20 +79,23 @@ public class MulticompMachine implements Machine {
bus.addDevice(ram);
bus.addDevice(acia, 1);
bus.addDevice(new SdController(SD_BASE), 1);
// TODO: Make this configurable, of course.
File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
} else {
logger.info("Default ROM file " + romImage +
if (romFile != null) {
File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
} else {
logger.info("Default ROM file " + romImage +
" not found, loading empty R/W memory image.");
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}
} else {
logger.info("No ROM file specified, loading empty R/W memory image.");
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}
bus.addDevice(rom);
}
@Override

View File

@ -46,7 +46,7 @@ public class SimpleMachine implements Machine {
private final Memory ram;
private final Cpu cpu;
public SimpleMachine() throws MemoryRangeException {
public SimpleMachine(String romFile) throws MemoryRangeException {
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.ram = new Memory(BUS_BOTTOM, BUS_TOP, false);
this.cpu = new Cpu();

View File

@ -70,7 +70,7 @@ public class SymonMachine implements Machine {
private Memory rom;
public SymonMachine() throws Exception {
public SymonMachine(String romFile) throws Exception {
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_BASE + MEMORY_SIZE - 1, false);
@ -83,14 +83,18 @@ public class SymonMachine implements Machine {
bus.addDevice(pia);
bus.addDevice(acia);
bus.addDevice(crtc);
// TODO: Make this configurable, of course.
File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file {}", romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
if (romFile != null) {
File romImage = new File(romFile);
if (romImage.canRead()) {
logger.info("Loading ROM image from file {}", romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_BASE + ROM_SIZE - 1, romImage);
} else {
logger.info("Default ROM file {} not found, loading empty R/W memory image.", romImage);
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}
} else {
logger.info("Default ROM file {} not found, loading empty R/W memory image.", romImage);
logger.info("No ROM file specified, loading empty R/W memory image.");
this.rom = Memory.makeRAM(ROM_BASE, ROM_BASE + ROM_SIZE - 1);
}

View File

@ -23,8 +23,8 @@
package com.loomcom.symon.ui;
import com.grahamedgecombe.jterminal.JTerminal;
import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel;
import com.loomcom.symon.jterminal.JTerminal;
import com.loomcom.symon.jterminal.vt100.Vt100TerminalModel;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.util.FifoRingBuffer;
@ -44,16 +44,19 @@ import java.awt.event.MouseListener;
public class Console extends JTerminal implements KeyListener, MouseListener {
private static final int DEFAULT_BORDER_WIDTH = 10;
private static final long serialVersionUID = 6633818486963338126L;
private static final int DEFAULT_BORDER_WIDTH = 10;
// If true, swap CR and LF characters.
private static final boolean SWAP_CR_AND_LF = true;
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
private boolean sendCrForLf = false;
private boolean sendCrForLf;
private FifoRingBuffer<Character> typeAheadBuffer;
public Console(int columns, int rows, Font font, boolean sendCrForLf) {
super(new Vt100TerminalModel(columns, rows), font);
//super(new Vt100TerminalModel(columns, rows));
// A small type-ahead buffer, as might be found in any real
// VT100-style serial terminal.
this.typeAheadBuffer = new FifoRingBuffer<>(128);
@ -104,7 +107,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
}
}
if (sendCrForLf && keyTyped == 0x0d) {
if (sendCrForLf && (keyTyped == 0x0d)) {
typeAheadBuffer.push((char) 0x0d);
typeAheadBuffer.push((char) 0x0a);
} else {

View File

@ -331,10 +331,10 @@ public class MemoryWindow extends JFrame implements ActionListener {
return Utils.wordToHex(fullAddress(row, 1));
} else if (column < 9) {
// Display hex value of the data
return Utils.byteToHex(bus.read(fullAddress(row, column)));
return Utils.byteToHex(bus.read(fullAddress(row, column), false));
} else {
// Display the ASCII equivalent (if printable)
return Utils.byteToAscii(bus.read(fullAddress(row, column - 8)));
return Utils.byteToAscii(bus.read(fullAddress(row, column - 8), false));
}
} catch (MemoryAccessException ex) {
return "??";

View File

@ -42,11 +42,11 @@ public class PreferencesDialog extends Observable implements Preferences {
private JTextField programLoadAddressField;
private int programLoadAddress = DEFAULT_PROGRAM_LOAD_ADDRESS;
private boolean haltOnBreak = DEFAULT_HALT_ON_BREAK;
private boolean haltOnBreak;
public PreferencesDialog(Frame parent, boolean modal) {
public PreferencesDialog(Frame parent, boolean modal, boolean haltOnBreak) {
this.dialog = new JDialog(parent, modal);
this.haltOnBreak = haltOnBreak;
createUi();
updateUi();
}

View File

@ -30,6 +30,7 @@ import com.loomcom.symon.machines.Machine;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.plaf.metal.MetalTextFieldUI;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@ -69,7 +70,7 @@ public class StatusPanel extends JPanel {
private JTextField xField;
private JTextField yField;
private Machine machine;
private final Machine machine;
private static final int EMPTY_BORDER = 10;
private static final Border LABEL_BORDER = BorderFactory.createEmptyBorder(0, 5, 0, 0);
@ -392,6 +393,13 @@ public class StatusPanel extends JPanel {
textField.setMaximumSize(size);
textField.setPreferredSize(size);
textField.setBackground(Color.WHITE);
// Although we usually defer to the system look-and-feel, for
// these small text fields in particular, we use a Metal
// look-and-feel because native look-and-feel breaks very small
// text fields under GTK+ (they are drawn with an inner margin
// even if the margin is set to 0)
textField.setUI(new MetalTextFieldUI());
return textField;
}

View File

@ -61,6 +61,7 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
private static final Logger logger = Logger.getLogger(VideoWindow.class.getName());
private static final long WINDOW_REPAINT_INTERVAL = 66; // 30fps rate
private static final int CHAR_WIDTH = 8;
private static final int CHAR_HEIGHT = 8;
@ -127,6 +128,18 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
public void run() {
if (cursorBlinkRate > 0) {
hideCursor = !hideCursor;
}
}
});
}
}
private class WindowPainter implements Runnable {
public void run() {
SwingUtilities.invokeLater(new Runnable () {
@Override
public void run() {
if (VideoWindow.this.isVisible()) {
VideoWindow.this.repaint();
}
}
@ -152,6 +165,11 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
TimeUnit.MILLISECONDS);
}
scheduler.scheduleAtFixedRate(new WindowPainter(),
WINDOW_REPAINT_INTERVAL,
WINDOW_REPAINT_INTERVAL,
TimeUnit.MILLISECONDS);
// Capture some state from the CRTC that will define the
// window size. When these values change, the window will
// need to re-pack and redraw.

View File

@ -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);
}
}

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,100 @@ public class AciaTest {
acia.txWrite('b');
assertEquals(0x08, acia.read(0x0001));
assertEquals(0x08, acia.read(0x0001, true));
assertEquals('a', acia.rxRead());
assertEquals('b', acia.txRead());
assertEquals('a', acia.rxRead(true));
assertEquals('b', acia.txRead(true));
assertEquals(0x10, acia.read(0x0001));
assertEquals(0x10, acia.read(0x0001, true));
}
@Test
public void A()
throws Exception {
Acia acia = new Acia6551(0x0000);
acia.rxWrite('a');
acia.txWrite('b');
assertEquals(0x08, acia.read(0x0001, true));
assertEquals('a', acia.rxRead(false));
assertEquals('b', acia.txRead(false));
assertEquals(0x08, acia.read(0x0001, true));
}
@Test
public void statusRegisterInitializedAtHardwareReset() throws Exception {
Acia6551 acia = new Acia6551(0x0000);
assertEquals(0x10, acia.read(0x0001, false));
}
@Test
public void commandRegisterInitializedAtHardwareReset() throws Exception {
Acia6551 acia = new Acia6551(0x0000);
assertEquals(0x02, acia.read(0x0002, false));
}
@Test
public void controlRegisterInitializedAtHardwareReset() throws Exception {
Acia6551 acia = new Acia6551(0x0000);
assertEquals(0x00, acia.read(0x0003, false));
}
@Test
public void programResetClearsOverrunStatus() throws Exception {
Acia6551 acia = new Acia6551(0x0000);
Bus bus = new Bus(acia.ACIA_SIZE);
acia.setBus(bus);
// Change as many status bits as we can.
acia.write(0x0002, 0x00); // enable receive interrupt
acia.rxWrite('a');
acia.rxWrite('b'); // overrun, receive full, interrupt signalled
acia.write(0x0000, 'c'); // Transmitter Data Register not empty
// Check that all the bits we expected to be set actually are
assertEquals(0x8C, acia.read(0x0001, false));
// Do a "program reset". The value is ignored.
acia.write(0x0001, 0xFF);
// Check that only bit 2 was cleared.
assertEquals(0x88, acia.read(0x0001, false));
}
@Test
public void programResetKeepsParitySettings() throws Exception {
Acia6551 acia = new Acia6551(0x0000);
// Set all the command register bits
acia.write(0x0002, 0xFF);
// Do a "program reset". The value is ignored.
acia.write(0x0001, 0xFF);
// The top 3 bits should be kept as-is,
// the bottom 5 bits should be reset to defaults.
assertEquals(0xE2, acia.read(0x0002, false));
}
@Test
public void programResetLeavesControlRegisterUnchanged() throws Exception {
Acia6551 acia = new Acia6551(0x0000);
// Set all the control register bits
acia.write(0x0003, 0xFF);
// Do a "program reset". The value is ignored.
acia.write(0x0001, 0xFF);
// No bits should have changed.
assertEquals(0xFF, acia.read(0x0003, false));
}
}

View File

@ -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));
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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());
}
}

View File

@ -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
}
}

View File

@ -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());
}
}

View File

@ -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));
}
}

View File

@ -29,7 +29,7 @@ public class CpuAbsoluteModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -121,31 +121,31 @@ public class CpuAbsoluteModeTest extends TestCase {
0x0e, 0x04, 0x12); // ASL $1204
cpu.step();
assertEquals(0x00, bus.read(0x7f00));
assertEquals(0x00, bus.read(0x7f00, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x7f01));
assertEquals(0x02, bus.read(0x7f01, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0x3502));
assertEquals(0x04, bus.read(0x3502, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x88, bus.read(0x3503));
assertEquals(0x88, bus.read(0x3503, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x1204));
assertEquals(0x00, bus.read(0x1204, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
@ -165,11 +165,11 @@ public class CpuAbsoluteModeTest extends TestCase {
// Old PC-1 should be on stack (i.e.: address of third byte of the
// JSR instruction, 0x0204)
assertEquals(0x02, bus.read(0x1ff));
assertEquals(0x04, bus.read(0x1fe));
assertEquals(0x02, bus.read(0x1ff, true));
assertEquals(0x04, bus.read(0x1fe, true));
// No flags should have changed.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/* BIT - Bit Test - $2c */
@ -295,61 +295,61 @@ public class CpuAbsoluteModeTest extends TestCase {
0x2e, 0x01, 0x12); // ROL $1201 (m=%10000001, c=0)
cpu.step();
assertEquals(0x00, bus.read(0x1200));
assertEquals(0x00, bus.read(0x1200, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x1201));
assertEquals(0x02, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step(2);
assertEquals(0x05, bus.read(0x1201));
assertEquals(0x05, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x0a, bus.read(0x1201));
assertEquals(0x0a, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x14, bus.read(0x1201));
assertEquals(0x14, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x28, bus.read(0x1201));
assertEquals(0x28, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x50, bus.read(0x1201));
assertEquals(0x50, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0xa0, bus.read(0x1201));
assertEquals(0xa0, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x1201));
assertEquals(0x40, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x81, bus.read(0x1201));
assertEquals(0x81, bus.read(0x1201, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -362,7 +362,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.step();
assertEquals(0x3400, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/* EOR - Exclusive OR - $4d */
@ -418,38 +418,38 @@ public class CpuAbsoluteModeTest extends TestCase {
0x4e, 0x05, 0x12); // LSR $1205
cpu.step();
assertEquals(0x00, bus.read(0x1200));
assertEquals(0x00, bus.read(0x1200, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x1201));
assertEquals(0x00, bus.read(0x1201, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0x1202));
assertEquals(0x01, bus.read(0x1202, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x22, bus.read(0x1203));
assertEquals(0x22, bus.read(0x1203, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x1204));
assertEquals(0x40, bus.read(0x1204, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
// Setting Carry should not affect the result.
cpu.step(2);
assertEquals(0x01, bus.read(0x1205));
assertEquals(0x01, bus.read(0x1205, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -654,61 +654,61 @@ public class CpuAbsoluteModeTest extends TestCase {
0x6e, 0x11, 0x12); // ROR $1201 (m=%00010000, c=0)
cpu.step();
assertEquals(0x00, bus.read(0x1210));
assertEquals(0x00, bus.read(0x1210, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x08, bus.read(0x1211));
assertEquals(0x08, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0x1211));
assertEquals(0x04, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x1211));
assertEquals(0x02, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0x1211));
assertEquals(0x01, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x1211));
assertEquals(0x00, bus.read(0x1211, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x80, bus.read(0x1211));
assertEquals(0x80, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x1211));
assertEquals(0x40, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x20, bus.read(0x1211));
assertEquals(0x20, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x10, bus.read(0x1211));
assertEquals(0x10, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -720,7 +720,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setYRegister(0x00);
bus.loadProgram(0x8c, 0x10, 0x12);
cpu.step();
assertEquals(0x00, bus.read(0x1210));
assertEquals(0x00, bus.read(0x1210, true));
// STY should have NO effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -730,7 +730,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setYRegister(0x0f);
bus.loadProgram(0x8c, 0x10, 0x12);
cpu.step();
assertEquals(0x0f, bus.read(0x1210));
assertEquals(0x0f, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -739,7 +739,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setYRegister(0x80);
bus.loadProgram(0x8c, 0x10, 0x12);
cpu.step();
assertEquals(0x80, bus.read(0x1210));
assertEquals(0x80, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -750,7 +750,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setAccumulator(0x00);
bus.loadProgram(0x8d, 0x10, 0x12);
cpu.step();
assertEquals(0x00, bus.read(0x1210));
assertEquals(0x00, bus.read(0x1210, true));
// STA should have NO effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -760,7 +760,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setAccumulator(0x0f);
bus.loadProgram(0x8d, 0x10, 0x12);
cpu.step();
assertEquals(0x0f, bus.read(0x1210));
assertEquals(0x0f, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -769,7 +769,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setAccumulator(0x80);
bus.loadProgram(0x8d, 0x10, 0x12);
cpu.step();
assertEquals(0x80, bus.read(0x1210));
assertEquals(0x80, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -780,7 +780,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setXRegister(0x00);
bus.loadProgram(0x8e, 0x10, 0x12);
cpu.step();
assertEquals(0x00, bus.read(0x1210));
assertEquals(0x00, bus.read(0x1210, true));
// STX should have NO effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -790,7 +790,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setXRegister(0x0f);
bus.loadProgram(0x8e, 0x10, 0x12);
cpu.step();
assertEquals(0x0f, bus.read(0x1210));
assertEquals(0x0f, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -799,7 +799,7 @@ public class CpuAbsoluteModeTest extends TestCase {
cpu.setXRegister(0x80);
bus.loadProgram(0x8e, 0x10, 0x12);
cpu.step();
assertEquals(0x80, bus.read(0x1210));
assertEquals(0x80, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -957,22 +957,22 @@ public class CpuAbsoluteModeTest extends TestCase {
0xce, 0x13, 0x12); // DEC $1213
cpu.step();
assertEquals(0xff, bus.read(0x1210));
assertEquals(0xff, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0x1211));
assertEquals(0x00, bus.read(0x1211, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x7f, bus.read(0x1212));
assertEquals(0x7f, bus.read(0x1212, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0xfe, bus.read(0x1213));
assertEquals(0xfe, bus.read(0x1213, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
@ -1213,17 +1213,17 @@ public class CpuAbsoluteModeTest extends TestCase {
0xee, 0x12, 0x12); // INC $1212
cpu.step();
assertEquals(0x01, bus.read(0x1210));
assertEquals(0x01, bus.read(0x1210, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x80, bus.read(0x1211));
assertEquals(0x80, bus.read(0x1211, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0x1212));
assertEquals(0x00, bus.read(0x1212, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());

View File

@ -29,7 +29,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -117,31 +117,31 @@ public class CpuAbsoluteXModeTest extends TestCase {
0x1e, 0x04, 0x2c); // ASL $2c04,X
cpu.step();
assertEquals(0x00, bus.read(0x2c30));
assertEquals(0x00, bus.read(0x2c30, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x2c31));
assertEquals(0x02, bus.read(0x2c31, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0x2c32));
assertEquals(0x04, bus.read(0x2c32, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x88, bus.read(0x2c33));
assertEquals(0x88, bus.read(0x2c33, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x2c34));
assertEquals(0x00, bus.read(0x2c34, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
@ -229,61 +229,61 @@ public class CpuAbsoluteXModeTest extends TestCase {
0x3e, 0x01, 0x10); // ROL $1001,X (m=%10000001, c=0)
cpu.step();
assertEquals(0x00, bus.read(0x1070));
assertEquals(0x00, bus.read(0x1070, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x1071));
assertEquals(0x02, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step(2);
assertEquals(0x05, bus.read(0x1071));
assertEquals(0x05, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x0a, bus.read(0x1071));
assertEquals(0x0a, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x14, bus.read(0x1071));
assertEquals(0x14, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x28, bus.read(0x1071));
assertEquals(0x28, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x50, bus.read(0x1071));
assertEquals(0x50, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0xa0, bus.read(0x1071));
assertEquals(0xa0, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x1071));
assertEquals(0x40, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x81, bus.read(0x1071));
assertEquals(0x81, bus.read(0x1071, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -346,38 +346,38 @@ public class CpuAbsoluteXModeTest extends TestCase {
0x5e, 0x05, 0xab); // LSR $ab05,X
cpu.step();
assertEquals(0x00, bus.read(0xab30));
assertEquals(0x00, bus.read(0xab30, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0xab31));
assertEquals(0x00, bus.read(0xab31, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0xab32));
assertEquals(0x01, bus.read(0xab32, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x22, bus.read(0xab33));
assertEquals(0x22, bus.read(0xab33, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0xab34));
assertEquals(0x40, bus.read(0xab34, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
// Setting Carry should not affect the result.
cpu.step(2);
assertEquals(0x01, bus.read(0xab35));
assertEquals(0x01, bus.read(0xab35, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -609,61 +609,61 @@ public class CpuAbsoluteXModeTest extends TestCase {
cpu.setXRegister(0x30);
cpu.step();
assertEquals(0x00, bus.read(0xab40));
assertEquals(0x00, bus.read(0xab40, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x08, bus.read(0xab41));
assertEquals(0x08, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0xab41));
assertEquals(0x04, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0xab41));
assertEquals(0x02, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0xab41));
assertEquals(0x01, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0xab41));
assertEquals(0x00, bus.read(0xab41, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x80, bus.read(0xab41));
assertEquals(0x80, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0xab41));
assertEquals(0x40, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x20, bus.read(0xab41));
assertEquals(0x20, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x10, bus.read(0xab41));
assertEquals(0x10, bus.read(0xab41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -677,7 +677,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
cpu.setAccumulator(0x00);
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
cpu.step();
assertEquals(0x00, bus.read(0xab40));
assertEquals(0x00, bus.read(0xab40, true));
// STA should have NO affect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -688,7 +688,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
cpu.setAccumulator(0x0f);
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
cpu.step();
assertEquals(0x0f, bus.read(0xab40));
assertEquals(0x0f, bus.read(0xab40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -698,7 +698,7 @@ public class CpuAbsoluteXModeTest extends TestCase {
cpu.setAccumulator(0x80);
bus.loadProgram(0x9d, 0x10, 0xab); // STA $ab10,X
cpu.step();
assertEquals(0x80, bus.read(0xab40));
assertEquals(0x80, bus.read(0xab40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -808,22 +808,22 @@ public class CpuAbsoluteXModeTest extends TestCase {
cpu.setXRegister(0x30);
cpu.step();
assertEquals(0xff, bus.read(0xab40));
assertEquals(0xff, bus.read(0xab40, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0xab41));
assertEquals(0x00, bus.read(0xab41, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x7f, bus.read(0xab42));
assertEquals(0x7f, bus.read(0xab42, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0xfe, bus.read(0xab43));
assertEquals(0xfe, bus.read(0xab43, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
@ -1051,17 +1051,17 @@ public class CpuAbsoluteXModeTest extends TestCase {
0xfe, 0x12, 0xab); // INC $ab12,X
cpu.step();
assertEquals(0x01, bus.read(0xab30));
assertEquals(0x01, bus.read(0xab30, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x80, bus.read(0xab31));
assertEquals(0x80, bus.read(0xab31, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0xab32));
assertEquals(0x00, bus.read(0xab32, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}

View File

@ -29,7 +29,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -411,7 +411,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
cpu.setAccumulator(0x00);
bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y
cpu.step();
assertEquals(0x00, bus.read(0xab40));
assertEquals(0x00, bus.read(0xab40, true));
// STA should have NO effect on flags
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -423,7 +423,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
cpu.setAccumulator(0x0f);
bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y
cpu.step();
assertEquals(0x0f, bus.read(0xab40));
assertEquals(0x0f, bus.read(0xab40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -433,7 +433,7 @@ public class CpuAbsoluteYModeTest extends TestCase {
cpu.setAccumulator(0x80);
bus.loadProgram(0x99, 0x10, 0xab); // STA $ab10,Y
cpu.step();
assertEquals(0x80, bus.read(0xab40));
assertEquals(0x80, bus.read(0xab40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}

View File

@ -30,7 +30,7 @@ public class CpuAccumulatorModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -306,4 +306,4 @@ public class CpuAccumulatorModeTest extends TestCase {
assertFalse(cpu.getCarryFlag());
}
}
}

View File

@ -29,7 +29,7 @@ public class CpuImmediateModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*

View File

@ -33,7 +33,7 @@ public class CpuImpliedModeTest {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -76,6 +76,7 @@ public class CpuImpliedModeTest {
public void test_BRK() throws MemoryAccessException {
cpu.setCarryFlag();
cpu.setOverflowFlag();
cpu.clearIrqDisableFlag();
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW,
cpu.getProcessorStatus());
assertEquals(0x00, cpu.stackPeek());
@ -101,10 +102,10 @@ public class CpuImpliedModeTest {
cpu.step(); // Triggers the BRK
// Was at PC = 0x204. PC+1 should now be on the stack
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
assertEquals(0x05, bus.read(0x1fe)); // PC low byte
assertEquals(0x02, bus.read(0x1ff, true)); // PC high byte
assertEquals(0x05, bus.read(0x1fe, true)); // PC low byte
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK,
bus.read(0x1fd)); // Processor Status, with B set
bus.read(0x1fd, true)); // Processor Status, with B set
// Interrupt vector held 0x1234, so we should be there.
assertEquals(0x1234, cpu.getProgramCounter());
@ -117,9 +118,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 +145,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 {
@ -327,7 +344,7 @@ public class CpuImpliedModeTest {
assertEquals(0, cpu.getYRegister());
assertEquals(0x201, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/* PHA - Push Accumulator - $48 */
@ -416,7 +433,7 @@ public class CpuImpliedModeTest {
cpu.step();
assertEquals(0x0f12, cpu.getProgramCounter());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/* SEC - Set Carry Flag - $38 */
@ -625,4 +642,4 @@ public class CpuImpliedModeTest {
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
}
}

View File

@ -31,7 +31,7 @@ public class CpuIndexedIndirectModeTest {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
@Test
@ -87,7 +87,7 @@ public class CpuIndexedIndirectModeTest {
cpu.step(1);
assertEquals(0x35, cpu.getAccumulator());
assertEquals(0x31, bus.read(0xc51f));
assertEquals(0x31, bus.read(0xc51f, true));
}
@Test
@ -103,6 +103,6 @@ public class CpuIndexedIndirectModeTest {
cpu.step(1);
assertEquals(0x11, cpu.getAccumulator());
assertEquals(0x31, bus.read(0xc51f));
assertEquals(0x31, bus.read(0xc51f, true));
}
}
}

View File

@ -31,7 +31,7 @@ public class CpuIndirectIndexedModeTest {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
@Test
@ -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));
}
}
}

View File

@ -29,7 +29,7 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -48,7 +48,7 @@ public class CpuIndirectModeTest extends TestCase {
cpu.step();
assertEquals(0x5400, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
@ -60,11 +60,11 @@ public class CpuIndirectModeTest extends TestCase {
cpu.step();
assertEquals(0x2200, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
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);
@ -72,11 +72,11 @@ public class CpuIndirectModeTest extends TestCase {
cpu.step();
assertEquals(0x2200, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
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);
@ -84,11 +84,11 @@ public class CpuIndirectModeTest extends TestCase {
cpu.step();
assertEquals(0x5400, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
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);
@ -96,7 +96,7 @@ public class CpuIndirectModeTest extends TestCase {
cpu.step();
assertEquals(0x5400, cpu.getProgramCounter());
// No change to status flags.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
}
}

View File

@ -28,7 +28,7 @@ public class CpuIndirectXModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -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());
}

View File

@ -29,7 +29,7 @@ public class CpuRelativeModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -278,4 +278,4 @@ public class CpuRelativeModeTest extends TestCase {
assertEquals(0x202, cpu.getProgramCounter());
}
}
}

View File

@ -44,7 +44,7 @@ public class CpuTest extends TestCase {
assertEquals(0x0200, cpu.getProgramCounter());
assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertTrue(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
@ -71,61 +71,61 @@ public class CpuTest extends TestCase {
public void testStackPush() throws MemoryAccessException {
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x00, bus.read(0x1ff));
assertEquals(0x00, bus.read(0x1ff, true));
cpu.stackPush(0x06);
assertEquals(0xfe, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x06, bus.read(0x1ff, true));
cpu.stackPush(0x05);
assertEquals(0xfd, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x06, bus.read(0x1ff, true));
assertEquals(0x05, bus.read(0x1fe, true));
cpu.stackPush(0x04);
assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x06, bus.read(0x1ff, true));
assertEquals(0x05, bus.read(0x1fe, true));
assertEquals(0x04, bus.read(0x1fd, true));
cpu.stackPush(0x03);
assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x06, bus.read(0x1ff, true));
assertEquals(0x05, bus.read(0x1fe, true));
assertEquals(0x04, bus.read(0x1fd, true));
assertEquals(0x03, bus.read(0x1fc, true));
cpu.stackPush(0x02);
assertEquals(0xfa, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
assertEquals(0x06, bus.read(0x1ff, true));
assertEquals(0x05, bus.read(0x1fe, true));
assertEquals(0x04, bus.read(0x1fd, true));
assertEquals(0x03, bus.read(0x1fc, true));
assertEquals(0x02, bus.read(0x1fb, true));
cpu.stackPush(0x01);
assertEquals(0xf9, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
assertEquals(0x01, bus.read(0x1fa));
assertEquals(0x06, bus.read(0x1ff, true));
assertEquals(0x05, bus.read(0x1fe, true));
assertEquals(0x04, bus.read(0x1fd, true));
assertEquals(0x03, bus.read(0x1fc, true));
assertEquals(0x02, bus.read(0x1fb, true));
assertEquals(0x01, bus.read(0x1fa, true));
}
public void testStackPushWrapsAroundToStackTop() throws MemoryAccessException {
cpu.setStackPointer(0x01);
cpu.stackPush(0x01);
assertEquals(0x01, bus.read(0x101));
assertEquals(0x01, bus.read(0x101, true));
assertEquals(0x00, cpu.getStackPointer());
cpu.stackPush(0x02);
assertEquals(0x02, bus.read(0x100));
assertEquals(0x02, bus.read(0x100, true));
assertEquals(0xff, cpu.getStackPointer());
cpu.stackPush(0x03);
assertEquals(0x03, bus.read(0x1ff));
assertEquals(0x03, bus.read(0x1ff, true));
assertEquals(0xfe, cpu.getStackPointer());
}
@ -205,14 +205,12 @@ public class CpuTest extends TestCase {
}
public void testGetProcessorStatus() {
// By default, no flags are set. Remember, bit 5
// By default, only "interrupt disable" is set. Remember, bit 5
// is always '1'.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
cpu.setCarryFlag();
assertEquals(0x21, cpu.getProcessorStatus());
assertEquals(0x25, cpu.getProcessorStatus());
cpu.setZeroFlag();
assertEquals(0x23, cpu.getProcessorStatus());
cpu.setIrqDisableFlag();
assertEquals(0x27, cpu.getProcessorStatus());
cpu.setDecimalModeFlag();
assertEquals(0x2f, cpu.getProcessorStatus());
@ -237,13 +235,16 @@ public class CpuTest extends TestCase {
assertEquals(0xa0, cpu.getProcessorStatus());
cpu.clearNegativeFlag();
assertEquals(0x20, cpu.getProcessorStatus());
cpu.setIrqDisableFlag();
assertEquals(0x24, cpu.getProcessorStatus());
}
public void testSetProcessorStatus() {
// Default
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertTrue(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
@ -384,6 +385,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();
@ -498,6 +534,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();
@ -603,4 +670,4 @@ public class CpuTest extends TestCase {
cpu.step();
assertEquals(0x3E, cpu.getAccumulator());
}
}
}

View File

@ -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());
}
}

View File

@ -29,7 +29,7 @@ public class CpuZeroPageModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -119,31 +119,31 @@ public class CpuZeroPageModeTest extends TestCase {
0x06, 0x04);
cpu.step();
assertEquals(0x00, bus.read(0x0000));
assertEquals(0x00, bus.read(0x0000, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x0001));
assertEquals(0x02, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0x0002));
assertEquals(0x04, bus.read(0x0002, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x88, bus.read(0x0003));
assertEquals(0x88, bus.read(0x0003, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x0004));
assertEquals(0x00, bus.read(0x0004, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
@ -274,61 +274,61 @@ public class CpuZeroPageModeTest extends TestCase {
0x26, 0x01); // ROL $01 (m=%10000001, c=0)
cpu.step();
assertEquals(0x00, bus.read(0x0000));
assertEquals(0x00, bus.read(0x0000, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x0001));
assertEquals(0x02, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step(2);
assertEquals(0x05, bus.read(0x0001));
assertEquals(0x05, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x0a, bus.read(0x0001));
assertEquals(0x0a, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x14, bus.read(0x0001));
assertEquals(0x14, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x28, bus.read(0x0001));
assertEquals(0x28, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x50, bus.read(0x0001));
assertEquals(0x50, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0xa0, bus.read(0x0001));
assertEquals(0xa0, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x0001));
assertEquals(0x40, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x81, bus.read(0x0001));
assertEquals(0x81, bus.read(0x0001, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -387,38 +387,38 @@ public class CpuZeroPageModeTest extends TestCase {
0x46, 0x05); // LSR $05
cpu.step();
assertEquals(0x00, bus.read(0x0000));
assertEquals(0x00, bus.read(0x0000, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x0001));
assertEquals(0x00, bus.read(0x0001, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0x0002));
assertEquals(0x01, bus.read(0x0002, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x22, bus.read(0x0003));
assertEquals(0x22, bus.read(0x0003, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x0004));
assertEquals(0x40, bus.read(0x0004, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
// Setting Carry should not affect the result.
cpu.step(2);
assertEquals(0x01, bus.read(0x0005));
assertEquals(0x01, bus.read(0x0005, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -623,61 +623,61 @@ public class CpuZeroPageModeTest extends TestCase {
0x66, 0x11); // ROR $01 (m=%00010000, c=0)
cpu.step();
assertEquals(0x00, bus.read(0x10));
assertEquals(0x00, bus.read(0x10, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x08, bus.read(0x11));
assertEquals(0x08, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0x11));
assertEquals(0x04, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x11));
assertEquals(0x02, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0x11));
assertEquals(0x01, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x11));
assertEquals(0x00, bus.read(0x11, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x80, bus.read(0x11));
assertEquals(0x80, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x11));
assertEquals(0x40, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x20, bus.read(0x11));
assertEquals(0x20, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x10, bus.read(0x11));
assertEquals(0x10, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -689,7 +689,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setYRegister(0x00);
bus.loadProgram(0x84, 0x10);
cpu.step();
assertEquals(0x00, bus.read(0x10));
assertEquals(0x00, bus.read(0x10, true));
// Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -699,7 +699,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setYRegister(0x0f);
bus.loadProgram(0x84, 0x10);
cpu.step();
assertEquals(0x0f, bus.read(0x10));
assertEquals(0x0f, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -708,7 +708,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setYRegister(0x80);
bus.loadProgram(0x84, 0x10);
cpu.step();
assertEquals(0x80, bus.read(0x10));
assertEquals(0x80, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -719,7 +719,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setAccumulator(0x00);
bus.loadProgram(0x85, 0x10);
cpu.step();
assertEquals(0x00, bus.read(0x10));
assertEquals(0x00, bus.read(0x10, true));
// Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -729,7 +729,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setAccumulator(0x0f);
bus.loadProgram(0x85, 0x10);
cpu.step();
assertEquals(0x0f, bus.read(0x10));
assertEquals(0x0f, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -738,7 +738,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setAccumulator(0x80);
bus.loadProgram(0x85, 0x10);
cpu.step();
assertEquals(0x80, bus.read(0x10));
assertEquals(0x80, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -749,7 +749,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setXRegister(0x00);
bus.loadProgram(0x86, 0x10);
cpu.step();
assertEquals(0x00, bus.read(0x10));
assertEquals(0x00, bus.read(0x10, true));
// Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -759,7 +759,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setXRegister(0x0f);
bus.loadProgram(0x86, 0x10);
cpu.step();
assertEquals(0x0f, bus.read(0x10));
assertEquals(0x0f, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -768,7 +768,7 @@ public class CpuZeroPageModeTest extends TestCase {
cpu.setXRegister(0x80);
bus.loadProgram(0x86, 0x10);
cpu.step();
assertEquals(0x80, bus.read(0x10));
assertEquals(0x80, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -926,22 +926,22 @@ public class CpuZeroPageModeTest extends TestCase {
0xc6, 0x13); // DEC $13
cpu.step();
assertEquals(0xff, bus.read(0x10));
assertEquals(0xff, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0x11));
assertEquals(0x00, bus.read(0x11, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x7f, bus.read(0x12));
assertEquals(0x7f, bus.read(0x12, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0xfe, bus.read(0x13));
assertEquals(0xfe, bus.read(0x13, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
@ -1182,17 +1182,17 @@ public class CpuZeroPageModeTest extends TestCase {
0xe6, 0x12); // INC $12
cpu.step();
assertEquals(0x01, bus.read(0x10));
assertEquals(0x01, bus.read(0x10, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x80, bus.read(0x11));
assertEquals(0x80, bus.read(0x11, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0x12));
assertEquals(0x00, bus.read(0x12, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}

View File

@ -29,7 +29,7 @@ public class CpuZeroPageXModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -131,38 +131,38 @@ public class CpuZeroPageXModeTest extends TestCase {
0x16, 0xd2); // ASL $d2,X
cpu.step();
assertEquals(0x00, bus.read(0x30));
assertEquals(0x00, bus.read(0x30, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x31));
assertEquals(0x02, bus.read(0x31, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0x32));
assertEquals(0x04, bus.read(0x32, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x88, bus.read(0x33));
assertEquals(0x88, bus.read(0x33, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x34));
assertEquals(0x00, bus.read(0x34, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
// Should wrap around, d2 + 30 = 02
cpu.step();
assertEquals(0x02, bus.read(0x02));
assertEquals(0x02, bus.read(0x02, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -251,61 +251,61 @@ public class CpuZeroPageXModeTest extends TestCase {
0x36, 0x01); // ROL $01,X (m=%10000001, c=0)
cpu.step();
assertEquals(0x00, bus.read(0x70));
assertEquals(0x00, bus.read(0x70, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x71));
assertEquals(0x02, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step(2);
assertEquals(0x05, bus.read(0x71));
assertEquals(0x05, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x0a, bus.read(0x71));
assertEquals(0x0a, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x14, bus.read(0x71));
assertEquals(0x14, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x28, bus.read(0x71));
assertEquals(0x28, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x50, bus.read(0x71));
assertEquals(0x50, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0xa0, bus.read(0x71));
assertEquals(0xa0, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x71));
assertEquals(0x40, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x81, bus.read(0x71));
assertEquals(0x81, bus.read(0x71, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -368,38 +368,38 @@ public class CpuZeroPageXModeTest extends TestCase {
0x56, 0x05); // LSR $05,X
cpu.step();
assertEquals(0x00, bus.read(0x30));
assertEquals(0x00, bus.read(0x30, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x31));
assertEquals(0x00, bus.read(0x31, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0x32));
assertEquals(0x01, bus.read(0x32, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x22, bus.read(0x33));
assertEquals(0x22, bus.read(0x33, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x34));
assertEquals(0x40, bus.read(0x34, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
// Setting Carry should not affect the result.
cpu.step(2);
assertEquals(0x01, bus.read(0x35));
assertEquals(0x01, bus.read(0x35, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -641,61 +641,61 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setXRegister(0x30);
cpu.step();
assertEquals(0x00, bus.read(0x40));
assertEquals(0x00, bus.read(0x40, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x08, bus.read(0x41));
assertEquals(0x08, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x04, bus.read(0x41));
assertEquals(0x04, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x02, bus.read(0x41));
assertEquals(0x02, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x01, bus.read(0x41));
assertEquals(0x01, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x00, bus.read(0x41));
assertEquals(0x00, bus.read(0x41, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertTrue(cpu.getCarryFlag());
cpu.step();
assertEquals(0x80, bus.read(0x41));
assertEquals(0x80, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x40, bus.read(0x41));
assertEquals(0x40, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x20, bus.read(0x41));
assertEquals(0x20, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
cpu.step();
assertEquals(0x10, bus.read(0x41));
assertEquals(0x10, bus.read(0x41, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
assertFalse(cpu.getCarryFlag());
@ -708,7 +708,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setYRegister(0x00);
bus.loadProgram(0x94, 0x10); // STY $10,X
cpu.step();
assertEquals(0x00, bus.read(0x40));
assertEquals(0x00, bus.read(0x40, true));
// Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -719,7 +719,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setYRegister(0x0f);
bus.loadProgram(0x94, 0x10); // STY $10,X
cpu.step();
assertEquals(0x0f, bus.read(0x40));
assertEquals(0x0f, bus.read(0x40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -729,7 +729,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setYRegister(0x80);
bus.loadProgram(0x94, 0x10); // STY $10,X
cpu.step();
assertEquals(0x80, bus.read(0x40));
assertEquals(0x80, bus.read(0x40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -742,7 +742,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setAccumulator(0x00);
bus.loadProgram(0x95, 0x10); // STA $10,X
cpu.step();
assertEquals(0x00, bus.read(0x40));
assertEquals(0x00, bus.read(0x40, true));
// Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -753,7 +753,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setAccumulator(0x0f);
bus.loadProgram(0x95, 0x10); // STA $10,X
cpu.step();
assertEquals(0x0f, bus.read(0x40));
assertEquals(0x0f, bus.read(0x40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -763,7 +763,7 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setAccumulator(0x80);
bus.loadProgram(0x95, 0x10); // STA $10,X
cpu.step();
assertEquals(0x80, bus.read(0x40));
assertEquals(0x80, bus.read(0x40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@ -873,22 +873,22 @@ public class CpuZeroPageXModeTest extends TestCase {
cpu.setXRegister(0x30);
cpu.step();
assertEquals(0xff, bus.read(0x40));
assertEquals(0xff, bus.read(0x40, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0x41));
assertEquals(0x00, bus.read(0x41, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x7f, bus.read(0x42));
assertEquals(0x7f, bus.read(0x42, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0xfe, bus.read(0x43));
assertEquals(0xfe, bus.read(0x43, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
@ -1116,17 +1116,17 @@ public class CpuZeroPageXModeTest extends TestCase {
0xf6, 0x12); // INC $12,X
cpu.step();
assertEquals(0x01, bus.read(0x30));
assertEquals(0x01, bus.read(0x30, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x80, bus.read(0x31));
assertEquals(0x80, bus.read(0x31, true));
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
cpu.step();
assertEquals(0x00, bus.read(0x32));
assertEquals(0x00, bus.read(0x32, true));
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}

View File

@ -29,7 +29,7 @@ public class CpuZeroPageYModeTest extends TestCase {
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/*
@ -47,7 +47,7 @@ public class CpuZeroPageYModeTest extends TestCase {
cpu.setXRegister(0x00);
bus.loadProgram(0x96, 0x10); // STX $10,Y
cpu.step();
assertEquals(0x00, bus.read(0x40));
assertEquals(0x00, bus.read(0x40, true));
// Should have no effect on flags.
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -57,7 +57,7 @@ public class CpuZeroPageYModeTest extends TestCase {
cpu.setXRegister(0x0f);
bus.loadProgram(0x96, 0x10); // STX $10,Y
cpu.step();
assertEquals(0x0f, bus.read(0x40));
assertEquals(0x0f, bus.read(0x40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
@ -66,7 +66,7 @@ public class CpuZeroPageYModeTest extends TestCase {
cpu.setXRegister(0x80);
bus.loadProgram(0x96, 0x10); // STX $10,Y
cpu.step();
assertEquals(0x80, bus.read(0x40));
assertEquals(0x80, bus.read(0x40, true));
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}

View File

@ -8,7 +8,7 @@ import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@ -242,19 +242,19 @@ public class CrtcTest {
crtc.write(0, 12); // High byte
crtc.write(1, 0x03);
assertEquals(0, crtc.read(1));
assertEquals(0, crtc.read(1, true));
crtc.write(1, 0x70);
assertEquals(0, crtc.read(1));
assertEquals(0, crtc.read(1, true));
crtc.write(0, 13); // Low byte
crtc.write(1, 0xff);
assertEquals(0, crtc.read(1));
assertEquals(0, crtc.read(1, true));
crtc.write(1, 0x0e);
assertEquals(0, crtc.read(1));
assertEquals(0, crtc.read(1, true));
}
@ -278,10 +278,10 @@ public class CrtcTest {
crtc.write(0, 14);
crtc.write(1, 0x3f);
assertEquals(0x3f, crtc.read(1));
assertEquals(0x3f, crtc.read(1, true));
crtc.write(1, 0x70);
assertEquals(0x70, crtc.read(1));
assertEquals(0x70, crtc.read(1, true));
}
@Test
@ -313,13 +313,13 @@ public class CrtcTest {
crtc.write(0, 15);
crtc.write(1, 0x00);
assertEquals(0x00, crtc.read(1));
assertEquals(0x00, crtc.read(1, true));
crtc.write(1, 0x1f);
assertEquals(0x1f, crtc.read(1));
assertEquals(0x1f, crtc.read(1, true));
crtc.write(1, 0xff);
assertEquals(0xff, crtc.read(1));
assertEquals(0xff, crtc.read(1, true));
}

View File

@ -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);
}
}

View File

@ -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]);
}
}