1
0
mirror of https://github.com/sethm/symon.git synced 2024-06-08 11:00:42 +00:00

Compare commits

...

27 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
32 changed files with 578 additions and 241 deletions

2
.gitignore vendored
View File

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

View File

@ -1,9 +1,9 @@
SYMON - A 6502 System Simulator
===============================
**Version:** 1.3.1
**Version:** 1.4.0
**Last Updated:** 12 October, 2019
**Last Updated:** 11 November, 2023
See the file COPYING for license.
@ -25,8 +25,8 @@ 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)
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,19 +64,41 @@ memory.
- `$0000`--`$FFFF`: 64KB RAM
#### 3.1.4 BenEater Memory Map
- `$0000`--`$3FFF`: 16KB RAM
- `$5000`--`$5003`: MOS 6551 ACIA (Serial Console)
- `$6000`--`$600F`: 6522 VIA
- `$8000`--`$FFFF`: 16KB ROM
### 3.2 Serial Console and CPU Status
![Serial Console](https://github.com/sethm/symon/raw/master/screenshots/console.png)
The main window of the simulator acts as the primary Input/Output
system through a virtual serial terminal. The terminal is attached to
a simulated ACIA, including a programmable baud rate generator that
tries to approximate the correct "feel" of the programmed baud rate.
(The sample Enhanced BASIC ROM image is programmed for 9600 baud)
system through a virtual serial terminal. It also provides CPU status.
Contents of the accumulator, index registers, processor status flags,
disassembly of the instruction register, and stack pointer are all displayed.
It also provides CPU status. Contents of the accumulator, index
registers, processor status flags, disassembly of the instruction
register, and stack pointer are all displayed.
The terminal is attached to a simulated MOS 6551 ACIA. It behaves very much
as described in the datasheet, with some exceptions:
- 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)
@ -198,12 +220,14 @@ interface.
Two command line options may be passed to the JAR file on startup,
to specify machine type and CPU type. The options are:
- `-cpu 6502`: Use the NMOS 6502 CPU type by default.
- `-cpu 65c02`: Use the CMOS 65C02 CPU type by default.
- `-machine symon`: Use the **Symon** machine type by default.
- `-machine multicomp`: Use the **Multicomp** machine type by default.
- `-machine simple`: Use the **Simple** machine type by default.
- `-rom <file>`: Use the specified file as the ROM image.
- `-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
@ -212,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
@ -247,6 +270,16 @@ 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.
@ -346,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.
@ -356,7 +394,7 @@ 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),

47
pom.xml
View File

@ -4,7 +4,7 @@
<groupId>com.loomcom.symon</groupId>
<artifactId>symon</artifactId>
<packaging>jar</packaging>
<version>1.3.1</version>
<version>1.4.0</version>
<name>symon</name>
<url>http://www.loomcom.com/symon</url>
<properties>
@ -19,24 +19,24 @@
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
<version>1.4.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</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.4</version>
<version>1.5.0</version>
</dependency>
</dependencies>
@ -44,24 +44,31 @@
<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>
@ -72,8 +79,8 @@
<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>
@ -81,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

@ -15,7 +15,7 @@
* 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
* NON-INFRINGEMENT. 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.
@ -32,7 +32,7 @@ import org.slf4j.LoggerFactory;
/**
* This class provides a simulation of the MOS 6502 CPU's state machine.
* A simple interface allows this 6502 to read and write to a simulated bus,
* and exposes some of the internal state for inspection and debugging.
* and exposes some internal state for inspection and debugging.
*/
public class Cpu implements InstructionTable {
@ -126,7 +126,7 @@ public class Cpu implements InstructionTable {
// Clear status register bits.
state.carryFlag = false;
state.zeroFlag = false;
state.irqDisableFlag = false;
state.irqDisableFlag = true;
state.decimalModeFlag = false;
state.breakFlag = false;
state.overflowFlag = false;
@ -283,7 +283,7 @@ public class Cpu implements InstructionTable {
// Execute
switch (state.ir) {
/** Single Byte Instructions; Implied and Relative **/
// Single Byte Instructions; Implied and Relative
case 0x00: // BRK - Force Interrupt - Implied
handleBrk(state.pc + 1);
break;
@ -457,7 +457,7 @@ public class Cpu implements InstructionTable {
setArithmeticFlags(state.x);
break;
/** JMP *****************************************************************/
// JMP
case 0x4c: // JMP - Absolute
state.pc = Utils.address(state.args[0], state.args[1]);
break;
@ -495,7 +495,7 @@ public class Cpu implements InstructionTable {
state.pc = Utils.address(bus.read(lo, true), bus.read(hi, true));
break;
/** ORA - Logical Inclusive Or ******************************************/
// ORA - Logical Inclusive Or
case 0x09: // #Immediate
state.a |= state.args[0];
setArithmeticFlags(state.a);
@ -517,7 +517,7 @@ public class Cpu implements InstructionTable {
break;
/** ASL - Arithmetic Shift Left *****************************************/
// ASL - Arithmetic Shift Left
case 0x0a: // Accumulator
state.a = asl(state.a);
setArithmeticFlags(state.a);
@ -532,7 +532,7 @@ public class Cpu implements InstructionTable {
break;
/** BIT - Bit Test ******************************************************/
// BIT - Bit Test
case 0x89: // 65C02 #Immediate
setZeroFlag((state.a & state.args[0]) == 0);
break;
@ -551,7 +551,7 @@ public class Cpu implements InstructionTable {
break;
/** AND - Logical AND ***************************************************/
// AND - Logical AND
case 0x29: // #Immediate
state.a &= state.args[0];
setArithmeticFlags(state.a);
@ -573,7 +573,7 @@ public class Cpu implements InstructionTable {
break;
/** ROL - Rotate Left ***************************************************/
// ROL - Rotate Left
case 0x2a: // Accumulator
state.a = rol(state.a);
setArithmeticFlags(state.a);
@ -588,7 +588,7 @@ public class Cpu implements InstructionTable {
break;
/** EOR - Exclusive OR **************************************************/
// EOR - Exclusive OR
case 0x49: // #Immediate
state.a ^= state.args[0];
setArithmeticFlags(state.a);
@ -610,7 +610,7 @@ public class Cpu implements InstructionTable {
break;
/** LSR - Logical Shift Right *******************************************/
// LSR - Logical Shift Right
case 0x4a: // Accumulator
state.a = lsr(state.a);
setArithmeticFlags(state.a);
@ -625,7 +625,7 @@ public class Cpu implements InstructionTable {
break;
/** ADC - Add with Carry ************************************************/
// ADC - Add with Carry
case 0x69: // #Immediate
if (state.decimalModeFlag) {
state.a = adcDecimal(state.a, state.args[0]);
@ -653,7 +653,7 @@ public class Cpu implements InstructionTable {
break;
/** ROR - Rotate Right **************************************************/
// ROR - Rotate Right
case 0x6a: // Accumulator
state.a = ror(state.a);
setArithmeticFlags(state.a);
@ -668,7 +668,7 @@ public class Cpu implements InstructionTable {
break;
/** STA - Store Accumulator *********************************************/
// STA - Store Accumulator
case 0x92: // 65C02 STA (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
@ -685,7 +685,7 @@ public class Cpu implements InstructionTable {
break;
/** STY - Store Y Register **********************************************/
// STY - Store Y Register
case 0x84: // Zero Page
case 0x8c: // Absolute
case 0x94: // Zero Page,X
@ -693,14 +693,14 @@ public class Cpu implements InstructionTable {
break;
/** STX - Store X Register **********************************************/
// STX - Store X Register
case 0x86: // Zero Page
case 0x8e: // Absolute
case 0x96: // Zero Page,Y
bus.write(effectiveAddress, state.x);
break;
/** STZ - 65C02 Store Zero ****************************************************/
// STZ - 65C02 Store Zero
case 0x64: // Zero Page
case 0x74: // Zero Page,X
case 0x9c: // Absolute
@ -712,7 +712,7 @@ public class Cpu implements InstructionTable {
bus.write(effectiveAddress, 0);
break;
/** LDY - Load Y Register ***********************************************/
// LDY - Load Y Register
case 0xa0: // #Immediate
state.y = state.args[0];
setArithmeticFlags(state.y);
@ -726,7 +726,7 @@ public class Cpu implements InstructionTable {
break;
/** LDX - Load X Register ***********************************************/
// LDX - Load X Register
case 0xa2: // #Immediate
state.x = state.args[0];
setArithmeticFlags(state.x);
@ -740,7 +740,7 @@ public class Cpu implements InstructionTable {
break;
/** LDA - Load Accumulator **********************************************/
// LDA - Load Accumulator
case 0xa9: // #Immediate
state.a = state.args[0];
setArithmeticFlags(state.a);
@ -762,7 +762,7 @@ public class Cpu implements InstructionTable {
break;
/** CPY - Compare Y Register ********************************************/
// CPY - Compare Y Register
case 0xc0: // #Immediate
cmp(state.y, state.args[0]);
break;
@ -772,7 +772,7 @@ public class Cpu implements InstructionTable {
break;
/** CMP - Compare Accumulator *******************************************/
// CMP - Compare Accumulator
case 0xc9: // #Immediate
cmp(state.a, state.args[0]);
break;
@ -792,7 +792,7 @@ public class Cpu implements InstructionTable {
break;
/** DEC - Decrement Memory **********************************************/
// DEC - Decrement Memory
case 0x3a: // 65C02 Immediate
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
@ -811,7 +811,7 @@ public class Cpu implements InstructionTable {
break;
/** CPX - Compare X Register ********************************************/
// CPX - Compare X Register
case 0xe0: // #Immediate
cmp(state.x, state.args[0]);
break;
@ -821,7 +821,7 @@ public class Cpu implements InstructionTable {
break;
/** SBC - Subtract with Carry (Borrow) **********************************/
// SBC - Subtract with Carry (Borrow)
case 0xe9: // #Immediate
if (state.decimalModeFlag) {
state.a = sbcDecimal(state.a, state.args[0]);
@ -849,7 +849,7 @@ public class Cpu implements InstructionTable {
break;
/** INC - Increment Memory **********************************************/
// INC - Increment Memory
case 0x1a: // 65C02 Increment Immediate
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
@ -868,14 +868,14 @@ public class Cpu implements InstructionTable {
break;
/** 65C02 RMB - Reset Memory Bit **************************************/
// 65C02 RMB - Reset Memory Bit
case 0x07: // 65C02 RMB0 - Zero Page
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
tmp = bus.read(effectiveAddress, true) & 0xff;
tmp &= ~(1 << 0);
tmp &= ~1;
bus.write(effectiveAddress, tmp);
break;
case 0x17: // 65C02 RMB1 - Zero Page
@ -943,14 +943,14 @@ public class Cpu implements InstructionTable {
break;
/** 65C02 SMB - Set Memory Bit **************************************/
// 65C02 SMB - Set Memory Bit
case 0x87: // 65C02 SMB0 - Zero Page
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
tmp = bus.read(effectiveAddress, true) & 0xff;
tmp |= (1);
tmp |= 1;
bus.write(effectiveAddress, tmp);
break;
case 0x97: // 65C02 SMB1 - Zero Page
@ -1017,7 +1017,7 @@ public class Cpu implements InstructionTable {
bus.write(effectiveAddress, tmp);
break;
/** 65C02 TRB/TSB - Test and Reset Bit/Test and Set Bit ***************/
// 65C02 TRB/TSB - Test and Reset Bit/Test and Set Bit
case 0x14: // 65C02 TRB - Test and Reset bit - Zero Page
case 0x1c: // 65C02 TRB - Test and Reset bit - Absolute
if (behavior == CpuBehavior.NMOS_6502 ||
@ -1026,8 +1026,9 @@ public class Cpu implements InstructionTable {
}
tmp = bus.read(effectiveAddress, true);
setZeroFlag((state.a & tmp) == 0);
tmp = (tmp &= ~(state.a)) & 0xff;
bus.write(effectiveAddress,tmp);
tmp &= ~(state.a);
tmp &= 0xff;
bus.write(effectiveAddress, tmp);
break;
case 0x04: // 65C02 TSB - Test and Set bit - Zero Page
@ -1038,18 +1039,19 @@ public class Cpu implements InstructionTable {
}
tmp = bus.read(effectiveAddress, true);
setZeroFlag((state.a & tmp) == 0);
tmp = (tmp |= (state.a)) & 0xff;
tmp |= state.a;
tmp = tmp & 0xff;
bus.write(effectiveAddress,tmp);
break;
/** 65C02 BBR - Branch if Bit Reset *************************************/
// 65C02 BBR - Branch if Bit Reset
case 0x0f: // 65C02 BBR - Branch if bit 0 reset - Zero Page
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 0) == 0) {
if ((tmp & 1) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1060,7 +1062,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 1) == 0) {
if ((tmp & (1 << 1)) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1071,7 +1073,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 2) == 0) {
if ((tmp & (1 << 2)) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1082,7 +1084,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 3) == 0) {
if ((tmp & (1 << 3)) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1093,7 +1095,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 4) == 0) {
if ((tmp & (1 << 4)) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1105,7 +1107,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 5) == 0) {
if ((tmp & (1 << 5)) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1116,7 +1118,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 6) == 0) {
if ((tmp & (1 << 6)) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1127,20 +1129,20 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 7) == 0) {
if ((tmp & (1 << 7)) == 0) {
state.pc = relAddress(state.args[1]);
}
break;
/** 65C02 BBS - Branch if Bit Set ************************************/
// 65C02 BBS - Branch if Bit Set
case 0x8f: // 65C02 BBS - Branch if bit 0 set - Zero Page
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 0) > 0) {
if ((tmp & 1) != 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1151,7 +1153,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 1) > 0) {
if ((tmp & (1 << 1)) != 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1162,7 +1164,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 2) > 0) {
if ((tmp & (1 << 2)) > 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1173,7 +1175,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 3) > 0) {
if ((tmp & (1 << 3)) > 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1185,7 +1187,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 4) > 0) {
if ((tmp & (1 << 4)) > 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1197,7 +1199,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 5) > 0) {
if ((tmp & (1 << 5)) > 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1208,7 +1210,7 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 6) > 0) {
if ((tmp & (1 << 6)) > 0) {
state.pc = relAddress(state.args[1]);
}
break;
@ -1219,13 +1221,13 @@ public class Cpu implements InstructionTable {
break;
}
tmp = bus.read(effectiveAddress, true);
if ((tmp & 1 << 7) > 0) {
if ((tmp & (1 << 7)) > 0) {
state.pc = relAddress(state.args[1]);
}
break;
/** Unimplemented Instructions ****************************************/
// Unimplemented Instructions
// TODO: Create a flag to enable highly-accurate emulation of unimplemented instructions.
default:
setOpTrap();
@ -1234,7 +1236,7 @@ public class Cpu implements InstructionTable {
delayLoop(state.ir);
// Peek ahead to the next insturction and arguments
// Peek ahead to the next instruction and arguments
peekAhead();
}
@ -1265,7 +1267,7 @@ public class Cpu implements InstructionTable {
/**
* Handle the common behavior of BRK, /IRQ, and /NMI
*
* @throws MemoryAccessException
* @throws MemoryAccessException on memory access failure
*/
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh, boolean isBreak) throws MemoryAccessException {
@ -1674,7 +1676,7 @@ public class Cpu implements InstructionTable {
public void setProgramCounter(int addr) {
state.pc = addr;
// As a side-effect of setting the program counter,
// As a side effect of setting the program counter,
// we want to peek ahead at the next state.
try {
peekAhead();
@ -1971,7 +1973,7 @@ public class Cpu implements InstructionTable {
*/
public String disassembleOpAtAddress(int address) throws MemoryAccessException {
int opCode = bus.read(address, true);
int args[] = new int[2];
int[] args = new int[2];
int size = Cpu.instructionSizes[opCode];
for (int i = 1; i < size; i++) {
int nextRead = (address + i) % bus.endAddress();

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

@ -28,6 +28,7 @@ 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;
@ -52,6 +53,7 @@ public class Main {
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();
@ -59,6 +61,7 @@ public class Main {
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);
@ -72,6 +75,9 @@ public class Main {
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;
@ -100,9 +106,13 @@ public class Main {
romFile = line.getOptionValue("rom");
}
if (line.hasOption("brk")) {
haltOnBreak = true;
}
while (true) {
if (machineClass == null) {
Object[] possibilities = {"Symon", "Multicomp", "Simple"};
Object[] possibilities = {"Symon", "Multicomp", "Simple", "BenEater"};
String s = (String)JOptionPane.showInputDialog(
null,
"Please choose the machine type to be emulated:",
@ -117,6 +127,8 @@ public class Main {
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;
}
@ -126,7 +138,7 @@ public class Main {
cpuBehavior = InstructionTable.CpuBehavior.NMOS_6502;
}
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile);
final Simulator simulator = new Simulator(machineClass, cpuBehavior, romFile, haltOnBreak);
SwingUtilities.invokeLater(new Runnable() {
@Override

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

@ -126,6 +126,8 @@ public class Simulator {
private MainCommand command = MainCommand.NONE;
private boolean haltOnBreak;
public enum MainCommand {
NONE,
SELECTMACHINE
@ -137,16 +139,17 @@ 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);
this(machineClass, InstructionTable.CpuBehavior.NMOS_6502, null, false);
}
public Simulator(Class machineClass, InstructionTable.CpuBehavior cpuType, String romFile) throws Exception {
public Simulator(Class machineClass, InstructionTable.CpuBehavior cpuType,
String romFile, boolean haltOnBreak) throws Exception {
this.haltOnBreak = haltOnBreak;
this.breakpoints = new Breakpoints(this);
this.machine = (Machine) machineClass.getConstructors()[0].newInstance(romFile);
this.machine.getCpu().setBehavior(cpuType);
// Initialize final fields in the constructor.
this.traceLog = new TraceLog();
this.memoryWindow = new MemoryWindow(machine.getBus());
@ -176,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();

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,6 +53,14 @@ 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
@ -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);
}
@ -203,13 +204,16 @@ public class Acia6551 extends Acia {
private synchronized void reset() {
txChar = 0;
txEmpty = true;
rxChar = 0;
rxFull = false;
receiveIrqEnabled = false;
transmitIrqEnabled = false;
interrupt = 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

@ -215,7 +215,8 @@ public class Vt100TerminalModel extends AbstractTerminalModel {
case '\t':
while ((++cursorColumn % TAB_WIDTH) != 0);
continue;
case 127:
case 8: // ASCII Backspace
case 127: // ASCII Delete
if (cursorColumn > 0) {
cells[cursorRow][--cursorColumn] = null;
}

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

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

@ -254,4 +254,76 @@ public class AciaTest {
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

@ -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());
}
/*
@ -169,7 +169,7 @@ public class CpuAbsoluteModeTest extends TestCase {
assertEquals(0x04, bus.read(0x1fe, true));
// No flags should have changed.
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/* BIT - Bit Test - $2c */
@ -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 */

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

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

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());
@ -343,7 +344,7 @@ public class CpuImpliedModeTest {
assertEquals(0, cpu.getYRegister());
assertEquals(0x201, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/* PHA - Push Accumulator - $48 */
@ -432,7 +433,7 @@ public class CpuImpliedModeTest {
cpu.step();
assertEquals(0x0f12, cpu.getProgramCounter());
assertEquals(0x20, cpu.getProcessorStatus());
assertEquals(0x24, cpu.getProcessorStatus());
}
/* SEC - Set Carry Flag - $38 */
@ -641,4 +642,4 @@ public class CpuImpliedModeTest {
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
}
}

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
@ -105,4 +105,4 @@ public class CpuIndexedIndirectModeTest {
assertEquals(0x11, cpu.getAccumulator());
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
@ -81,4 +81,4 @@ public class CpuIndirectIndexedModeTest {
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,7 +60,7 @@ 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 {
@ -72,7 +72,7 @@ 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 {
@ -84,7 +84,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_cmos() throws MemoryAccessException {
@ -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());
}
/*

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());
@ -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());
@ -669,4 +670,4 @@ public class CpuTest extends TestCase {
cpu.step();
assertEquals(0x3E, 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());
}
/*

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

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

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;