Compare commits

..
22 Commits
Author SHA1 Message Date
Seth Morabito 4cca0b4663 Initial 65C02 Support
First pass at adding CMOS 65C02 support for the following instructions
and addressing modes:

   * ORA (ZP Indirect)
   * AND (ZP Indirect)
   * EOR (ZP Indirect)
   * ADC (ZP Indirect)
   * STA (ZP Indirect)
   * LDA (ZP Indirect)
   * CMP (ZP Indirect)
   * SBC (ZP Indirect)
2016-06-12 18:55:09 -07:00
Seth Morabito 5a2e057e69 Minor whitespace cleanup 2016-06-11 11:46:31 -07:00
Seth Morabito da88aadda2 Merge pull request #15 from LIV2/master
Correct BRK/IRQ behavior
2016-03-20 11:43:42 -07:00
Matt Harlum 657b69da6c Cleanup my comments 2016-03-20 17:05:48 +11:00
Chris Cureau f251e54174 Merge pull request #13 from sethm/cc65_update_patches
update Makefiles and configuration for latest cc65
2016-03-14 20:01:12 -05:00
Chris Cureau fcc57d9be4 update Makefiles and configuration for latest cc65 2016-01-12 08:14:38 -06:00
Seth Morabito 88eba2cdcb Merge branch '1.2.branch' 2016-01-08 20:53:54 -08:00
Seth Morabito 0e49b197b3 Update README for 1.2.1 2016-01-08 20:52:02 -08:00
Seth Morabito b59fa63b63 Merge branch '1.2.branch' 2016-01-08 20:41:25 -08:00
Seth Morabito eff98118d5 Fix a few straggling Java 8-isms. 2016-01-08 20:40:23 -08:00
Seth Morabito c599df1cfb Set version to 1.3.0-SNAPSHOT 2016-01-08 19:14:00 -08:00
Seth Morabito 66c52c8826 Revert Java 1.8 changes. Buildable with Java 1.7
There are still active users on Java 1.7, so building with Java 8
was a no-no. This change reverts the recent migration to Java 8,
allowing JDK 1.7 to compile the code.

This means, at least for the time being, no more Lambda expressions
(Boooooooooooooooo!!)
2016-01-08 19:11:42 -08:00
Seth Morabito f3a5dd93ad Release 1.2.0 2016-01-03 11:01:20 -08:00
Seth Morabito 4044458baa Fix typo 2016-01-03 10:59:11 -08:00
Seth Morabito 9ab94b3050 Prep for 1.2 2016-01-03 10:56:16 -08:00
Seth Morabito 9a504adc58 Added roadmap to README 2016-01-03 10:49:41 -08:00
Seth Morabito 57cbff42e7 Merge branch '1.1.branch' 2016-01-02 19:06:23 -08:00
Seth Morabito 0ca30291cb Merge branch '1.1.branch' 2016-01-02 09:51:20 -08:00
Seth Morabito 95f85b71b1 Enter key can add breakpoints 2016-01-02 09:50:38 -08:00
Seth Morabito 7a9f9bfd55 Update images for 1.1.0 2016-01-01 09:49:39 -08:00
Seth Morabito 1e04a52d1b Prep for 1.2 2016-01-01 09:44:43 -08:00
Matt Harlum 8335cf5421 Correct BRK behaviour
IRQ/NMI clear the BRK flag
BRK is Non-Maskable
2015-06-01 08:58:17 +10:00
29 changed files with 833 additions and 215 deletions

No files matched your search

+20 -6
View File
@@ -1,9 +1,9 @@
SYMON - A 6502 System Simulator SYMON - A 6502 System Simulator
=============================== ===============================
**Version:** 1.1.1 **Version:** 1.2.1
**Last Updated:** 2 January, 2016 **Last Updated:** 8 January, 2016
See the file COPYING for license. See the file COPYING for license.
@@ -188,7 +188,7 @@ Maven will build Symon, run unit tests, and produce a jar file in the
Symon is meant to be invoked directly from the jar file. To run with Symon is meant to be invoked directly from the jar file. To run with
Java 1.8 or greater, just type: Java 1.8 or greater, just type:
$ java -jar symon-1.1.1.jar $ java -jar symon-1.2.0.jar
When Symon is running, you should be presented with a simple graphical When Symon is running, you should be presented with a simple graphical
interface. interface.
@@ -235,6 +235,12 @@ running.
## 5.0 Revision History ## 5.0 Revision History
- **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 - **1.1.1:** 2 January, 2016 - Minor enhancement: Allows breakpoints
to be added with the Enter key. to be added with the Enter key.
@@ -286,7 +292,15 @@ running.
- **0.1:** 20 January, 2010 - **0.1:** 20 January, 2010
## 6.0 To Do ## 6.0 Roadmap
- **1.2:** Better breakpoint support. Symbolic debugging of breakpoints.
- **2.0:** Complete rewrite of the UI in JavaFX instead of Swing. Complete
assembler and disassembler built in. Ability to attach source code for
symbolic debugging.
## 7.0 To Do
- Feedback (in the form of dialogs, status bar, etc). - Feedback (in the form of dialogs, status bar, etc).
@@ -310,7 +324,7 @@ running.
- Symbolic debugging. - Symbolic debugging.
## 7.0 Copyright and Acknowledgements ## 8.0 Copyright and Acknowledgements
**Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>** **Copyright (c) 2014 Seth J. Morabito <web@loomcom.com>**
@@ -330,7 +344,7 @@ This project would not have been possible without the following resources:
- [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 for information about how instructions are coded
## 8.0 Licensing ## 9.0 Licensing
Symon is free software. It is distributed under the MIT License. Symon is free software. It is distributed under the MIT License.
Please see the file 'COPYING' for full details of the license. Please see the file 'COPYING' for full details of the license.
+3 -3
View File
@@ -4,7 +4,7 @@
<groupId>com.loomcom.symon</groupId> <groupId>com.loomcom.symon</groupId>
<artifactId>symon</artifactId> <artifactId>symon</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.1.1</version> <version>1.3.0-SNAPSHOT</version>
<name>symon</name> <name>symon</name>
<url>http://www.loomcom.com/symon</url> <url>http://www.loomcom.com/symon</url>
<properties> <properties>
@@ -91,8 +91,8 @@
<version>3.1</version> <version>3.1</version>
<configuration> <configuration>
<compilerArgument>-Xlint:unchecked</compilerArgument> <compilerArgument>-Xlint:unchecked</compilerArgument>
<source>1.8</source> <source>1.7</source>
<target>1.8</target> <target>1.7</target>
</configuration> </configuration>
</plugin> </plugin>
+1 -1
View File
@@ -7,7 +7,7 @@ echo_irq: echo_irq.o
$(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o $(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o
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: clean:
rm -f *.o *.rom *.map *.lst rm -f *.o *.rom *.map *.lst
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY { MEMORY {
RAM1: start = $0000, size = $8000; RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3FFA, fill = yes; ROM1: start = $C000, size = $3FFA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes; ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
} }
@@ -11,6 +11,6 @@ VECTORS: load = ROMV, type = ro;
} }
SYMBOLS { SYMBOLS {
__STACKSIZE__ = $0300; __STACKSIZE__: type = weak, value = $0300;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ ehbasic: ehbasic.o
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o $(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
ehbasic.o: ehbasic.o:
$(CA) --listing -o ehbasic.o min_mon.asm $(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
clean: clean:
rm -f *.o *.rom *.map *.lst rm -f *.o *.rom *.map *.lst
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY { MEMORY {
RAM1: start = $0000, size = $8000; RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3F00, fill = yes; ROM1: start = $C000, size = $3F00, fill = yes;
MONITOR: start = $FF00, size = $FA, fill = yes; MONITOR: start = $FF00, size = $FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes; ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
@@ -13,6 +13,6 @@ VECTORS: load = ROMV, type = ro;
} }
SYMBOLS { SYMBOLS {
__STACKSIZE__ = $0300; __STACKSIZE__: type = weak, value = $0300;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ ehbasic: ehbasic.o
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o $(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
ehbasic.o: ehbasic.o:
$(CA) --listing -o ehbasic.o min_mon.asm $(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
clean: clean:
rm -f *.o *.rom *.map *.lst rm -f *.o *.rom *.map *.lst
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY { MEMORY {
RAM1: start = $0000, size = $8000; RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3C00, fill = yes; ROM1: start = $C000, size = $3C00, fill = yes;
MONITOR: start = $FC00, size = $3FA, fill = yes; MONITOR: start = $FC00, size = $3FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes; ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
@@ -13,6 +13,6 @@ VECTORS: load = ROMV, type = ro;
} }
SYMBOLS { SYMBOLS {
__STACKSIZE__ = $0300; __STACKSIZE__: type = weak, value = $0300;
} }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 35 KiB

+4 -2
View File
@@ -26,6 +26,7 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Device; import com.loomcom.symon.devices.Device;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException; import com.loomcom.symon.exceptions.MemoryRangeException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -34,7 +35,6 @@ import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors;
/** /**
* The Bus ties the whole thing together, man. * The Bus ties the whole thing together, man.
@@ -224,7 +224,9 @@ public class Bus {
for (int priority : priorities) { for (int priority : priorities) {
SortedSet<Device> deviceSet = deviceMap.get(priority); SortedSet<Device> deviceSet = deviceMap.get(priority);
devices.addAll(deviceSet.stream().collect(Collectors.toList())); for (Device device : deviceSet) {
devices.add(device);
}
} }
return devices; return devices;
+88 -18
View File
@@ -79,7 +79,7 @@ public class Cpu implements InstructionTable {
* Construct a new CPU. * Construct a new CPU.
*/ */
public Cpu() { public Cpu() {
this(CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG); this(CpuBehavior.NMOS_6502);
} }
public Cpu(CpuBehavior behavior) { public Cpu(CpuBehavior behavior) {
@@ -204,6 +204,13 @@ public class Cpu implements InstructionTable {
case 3: // Absolute case 3: // Absolute
effectiveAddress = Utils.address(state.args[0], state.args[1]); effectiveAddress = Utils.address(state.args[0], state.args[1]);
break; break;
case 4: // 65C02 (Zero Page)
if (behavior == CpuBehavior.CMOS_6502 ||
behavior == CpuBehavior.CMOS_65816) {
effectiveAddress = Utils.address(bus.read(state.args[0]),
bus.read((state.args[0] + 1) & 0xff));
}
break;
case 5: // Zero Page,X / Zero Page,Y case 5: // Zero Page,X / Zero Page,Y
if (state.ir == 0x96 || state.ir == 0xb6) { if (state.ir == 0x96 || state.ir == 0xb6) {
effectiveAddress = zpyAddress(state.args[0]); effectiveAddress = zpyAddress(state.args[0]);
@@ -258,9 +265,7 @@ public class Cpu implements InstructionTable {
/** Single Byte Instructions; Implied and Relative **/ /** Single Byte Instructions; Implied and Relative **/
case 0x00: // BRK - Force Interrupt - Implied case 0x00: // BRK - Force Interrupt - Implied
if (!getIrqDisableFlag()) { handleBrk(state.pc + 1);
handleIrq(state.pc + 1);
}
break; break;
case 0x08: // PHP - Push Processor Status - Implied case 0x08: // PHP - Push Processor Status - Implied
// Break flag is always set in the stack value. // Break flag is always set in the stack value.
@@ -400,11 +405,12 @@ public class Cpu implements InstructionTable {
case 0x4c: // JMP - Absolute case 0x4c: // JMP - Absolute
state.pc = Utils.address(state.args[0], state.args[1]); state.pc = Utils.address(state.args[0], state.args[1]);
break; break;
// TODO: 65C02 instruction 0x7C, JMP (Indirect,X)
case 0x6c: // JMP - Indirect case 0x6c: // JMP - Indirect
lo = Utils.address(state.args[0], state.args[1]); // Address of low byte lo = Utils.address(state.args[0], state.args[1]); // Address of low byte
if (state.args[0] == 0xff && if (state.args[0] == 0xff &&
(behavior == CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG || (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) { behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) {
hi = Utils.address(0x00, state.args[1]); hi = Utils.address(0x00, state.args[1]);
} else { } else {
@@ -424,13 +430,20 @@ public class Cpu implements InstructionTable {
* (http://www.obelisk.demon.co.uk/6502/reference.html#JMP) * (http://www.obelisk.demon.co.uk/6502/reference.html#JMP)
*/ */
break; break;
case 0x7C: // 65C02 JMP - Absolute,X
state.pc = (Utils.address(state.args[0], state.args[1]) + state.x) & 0xffff;
break;
/** ORA - Logical Inclusive Or ******************************************/ /** ORA - Logical Inclusive Or ******************************************/
case 0x09: // #Immediate case 0x09: // #Immediate
state.a |= state.args[0]; state.a |= state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0x12: // 65C02 ORA (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x01: // (Zero Page,X) case 0x01: // (Zero Page,X)
case 0x05: // Zero Page case 0x05: // Zero Page
case 0x0d: // Absolute case 0x0d: // Absolute
@@ -473,6 +486,11 @@ public class Cpu implements InstructionTable {
state.a &= state.args[0]; state.a &= state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0x32: // 65C02 AND (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x21: // (Zero Page,X) case 0x21: // (Zero Page,X)
case 0x25: // Zero Page case 0x25: // Zero Page
case 0x2d: // Absolute case 0x2d: // Absolute
@@ -505,6 +523,11 @@ public class Cpu implements InstructionTable {
state.a ^= state.args[0]; state.a ^= state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0x52: // 65C02 EOR (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x41: // (Zero Page,X) case 0x41: // (Zero Page,X)
case 0x45: // Zero Page case 0x45: // Zero Page
case 0x4d: // Absolute case 0x4d: // Absolute
@@ -540,6 +563,11 @@ public class Cpu implements InstructionTable {
state.a = adc(state.a, state.args[0]); state.a = adc(state.a, state.args[0]);
} }
break; break;
case 0x72: // 65C02 ADC (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x61: // (Zero Page,X) case 0x61: // (Zero Page,X)
case 0x65: // Zero Page case 0x65: // Zero Page
case 0x6d: // Absolute case 0x6d: // Absolute
@@ -571,6 +599,11 @@ public class Cpu implements InstructionTable {
/** STA - Store Accumulator *********************************************/ /** STA - Store Accumulator *********************************************/
case 0x92: // 65C02 STA (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x81: // (Zero Page,X) case 0x81: // (Zero Page,X)
case 0x85: // Zero Page case 0x85: // Zero Page
case 0x8d: // Absolute case 0x8d: // Absolute
@@ -631,6 +664,11 @@ public class Cpu implements InstructionTable {
state.a = state.args[0]; state.a = state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0xb2: // 65C02 LDA (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0xa1: // (Zero Page,X) case 0xa1: // (Zero Page,X)
case 0xa5: // Zero Page case 0xa5: // Zero Page
case 0xad: // Absolute case 0xad: // Absolute
@@ -657,6 +695,11 @@ public class Cpu implements InstructionTable {
case 0xc9: // #Immediate case 0xc9: // #Immediate
cmp(state.a, state.args[0]); cmp(state.a, state.args[0]);
break; break;
case 0xd2: // 65C02 CMP (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0xc1: // (Zero Page,X) case 0xc1: // (Zero Page,X)
case 0xc5: // Zero Page case 0xc5: // Zero Page
case 0xcd: // Absolute case 0xcd: // Absolute
@@ -697,6 +740,11 @@ public class Cpu implements InstructionTable {
state.a = sbc(state.a, state.args[0]); state.a = sbc(state.a, state.args[0]);
} }
break; break;
case 0xf2: // 65C02 SBC (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0xe1: // (Zero Page,X) case 0xe1: // (Zero Page,X)
case 0xe5: // Zero Page case 0xe5: // Zero Page
case 0xed: // Absolute case 0xed: // Absolute
@@ -744,13 +792,18 @@ public class Cpu implements InstructionTable {
} }
} }
private void handleBrk(int returnPc) throws MemoryAccessException {
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H, true);
clearIrq();
}
private void handleIrq(int returnPc) throws MemoryAccessException { private void handleIrq(int returnPc) throws MemoryAccessException {
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H); handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H, false);
clearIrq(); clearIrq();
} }
private void handleNmi() throws MemoryAccessException { private void handleNmi() throws MemoryAccessException {
handleInterrupt(state.pc, NMI_VECTOR_L, NMI_VECTOR_H); handleInterrupt(state.pc, NMI_VECTOR_L, NMI_VECTOR_H, false);
clearNmi(); clearNmi();
} }
@@ -759,9 +812,19 @@ public class Cpu implements InstructionTable {
* *
* @throws MemoryAccessException * @throws MemoryAccessException
*/ */
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh) throws MemoryAccessException { private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh, boolean isBreak) throws MemoryAccessException {
if (isBreak)
{
// Set the break flag before pushing. // Set the break flag before pushing.
setBreakFlag(); setBreakFlag();
}
else
{
// IRQ & NMI clear break flag
clearBreakFlag();
}
// Push program counter + 1 onto the stack // Push program counter + 1 onto the stack
stackPush((returnPc >> 8) & 0xff); // PC high byte stackPush((returnPc >> 8) & 0xff); // PC high byte
stackPush(returnPc & 0xff); // PC low byte stackPush(returnPc & 0xff); // PC low byte
@@ -1321,6 +1384,13 @@ public class Cpu implements InstructionTable {
return (zp + state.x) & 0xff; return (zp + state.x) & 0xff;
} }
/**
* Given a single byte, compute the Zero Page,Y offset address.
*/
int zpyAddress(int zp) {
return (zp + state.y) & 0xff;
}
/** /**
* Given a single byte, compute the offset address. * Given a single byte, compute the offset address.
*/ */
@@ -1329,21 +1399,21 @@ public class Cpu implements InstructionTable {
return (state.pc + (byte) offset) & 0xffff; return (state.pc + (byte) offset) & 0xffff;
} }
/**
* Given a single byte, compute the Zero Page,Y offset address.
*/
int zpyAddress(int zp) {
return (zp + state.y) & 0xff;
}
/* /*
* Perform a busy-loop until the instruction should complete on the wall clock * Perform a busy-loop until the instruction should complete on the wall clock
*/ */
private void delayLoop(int opcode) { private void delayLoop(int opcode) {
int clockSteps = Cpu.instructionClocks[0xff & opcode]; final int clockSteps;
if (behavior == CpuBehavior.NMOS_WITH_ROR_BUG ||
behavior == CpuBehavior.NMOS_6502) {
clockSteps = Cpu.instructionClocksNmos[0xff & opcode];
} else {
clockSteps = Cpu.instructionClocksCmos[0xff & opcode];
}
if (clockSteps == 0) { if (clockSteps == 0) {
logger.warn("Opcode {} has clock step of 0!", opcode); logger.warn("Opcode {} has clock step of 0!", String.format("0x%02x", opcode));
return; return;
} }
@@ -46,20 +46,17 @@ public interface InstructionTable {
* *
* NB: Does NOT implement "unimplemented" NMOS instructions. * NB: Does NOT implement "unimplemented" NMOS instructions.
*/ */
NMOS_WITH_INDIRECT_JMP_BUG, NMOS_6502,
/**
* 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,
/** /**
* Emulate a CMOS 65C02, with all CMOS instructions and addressing modes. * Emulate a CMOS 65C02, with all CMOS instructions and addressing modes.
*/ */
CMOS CMOS_6502,
/**
* Emulate a CMOS 65C816.
*/
CMOS_65816
} }
/** /**
@@ -128,19 +125,25 @@ public interface InstructionTable {
ZPG { ZPG {
public String toString() { public String toString() {
return "Zeropage"; return "Zero Page";
} }
}, },
ZPX { ZPX {
public String toString() { public String toString() {
return "Zeropage, X-indexed"; return "Zero Page, X-indexed";
} }
}, },
ZPY { ZPY {
public String toString() { public String toString() {
return "Zeropage, Y-indexed"; return "Zero Page, Y-indexed";
}
},
ZPI {
public String toString() {
return "Zero Page Indirect";
} }
}, },
@@ -154,52 +157,55 @@ public interface InstructionTable {
// 6502 opcodes. No 65C02 opcodes implemented. // 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 = { String[] opcodeNames = {
"BRK", "ORA", null, null, null, "ORA", "ASL", null, "BRK", "ORA", null, null, null, "ORA", "ASL", null, // 0x00-0x07
"PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, "PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, // 0x08-0x0f
"BPL", "ORA", null, null, null, "ORA", "ASL", null, "BPL", "ORA", "ORA", null, null, "ORA", "ASL", null, // 0x10-0x17
"CLC", "ORA", null, null, null, "ORA", "ASL", null, "CLC", "ORA", null, null, null, "ORA", "ASL", null, // 0x18-0x1f
"JSR", "AND", null, null, "BIT", "AND", "ROL", null, "JSR", "AND", null, null, "BIT", "AND", "ROL", null, // 0x20-0x27
"PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, "PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, // 0x28-0x2f
"BMI", "AND", null, null, null, "AND", "ROL", null, "BMI", "AND", "AND", null, null, "AND", "ROL", null, // 0x30-0x37
"SEC", "AND", null, null, null, "AND", "ROL", null, "SEC", "AND", null, null, null, "AND", "ROL", null, // 0x38-0x3f
"RTI", "EOR", null, null, null, "EOR", "LSR", null, "RTI", "EOR", null, null, null, "EOR", "LSR", null, // 0x40-0x47
"PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, "PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, // 0x48-0x4f
"BVC", "EOR", null, null, null, "EOR", "LSR", null, "BVC", "EOR", "EOR", null, null, "EOR", "LSR", null, // 0x50-0x57
"CLI", "EOR", null, null, null, "EOR", "LSR", null, "CLI", "EOR", null, null, null, "EOR", "LSR", null, // 0x58-0x5f
"RTS", "ADC", null, null, null, "ADC", "ROR", null, "RTS", "ADC", null, null, null, "ADC", "ROR", null, // 0x60-0x67
"PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, "PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, // 0x68-0x6f
"BVS", "ADC", null, null, null, "ADC", "ROR", null, "BVS", "ADC", "ADC", null, null, "ADC", "ROR", null, // 0x70-0x77
"SEI", "ADC", null, null, null, "ADC", "ROR", null, "SEI", "ADC", null, null, "JMP", "ADC", "ROR", null, // 0x78-0x7f
"BCS", "STA", null, null, "STY", "STA", "STX", null, "BCS", "STA", null, null, "STY", "STA", "STX", null, // 0x80-0x87
"DEY", null, "TXA", null, "STY", "STA", "STX", null, "DEY", null, "TXA", null, "STY", "STA", "STX", null, // 0x88-0x8f
"BCC", "STA", null, null, "STY", "STA", "STX", null, "BCC", "STA", "STA", null, "STY", "STA", "STX", null, // 0x90-0x97
"TYA", "STA", "TXS", null, null, "STA", null, null, "TYA", "STA", "TXS", null, null, "STA", null, null, // 0x98-0x9f
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, // 0xa0-0xa7
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, "TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, // 0xa8-0xaf
"BCS", "LDA", null, null, "LDY", "LDA", "LDX", null, "BCS", "LDA", "LDA", null, "LDY", "LDA", "LDX", null, // 0xb0-0xb7
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, "CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, // 0xb8-0xbf
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, "CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, // 0xc0-0xc7
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, "INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, // 0xc8-0xcf
"BNE", "CMP", null, null, null, "CMP", "DEC", null, "BNE", "CMP", "CMP", null, null, "CMP", "DEC", null, // 0xd0-0xd7
"CLD", "CMP", null, null, null, "CMP", "DEC", null, "CLD", "CMP", null, null, null, "CMP", "DEC", null, // 0xd8-0xdf
"CPX", "SBC", null, null, "CPX", "SBC", "INC", null, "CPX", "SBC", null, null, "CPX", "SBC", "INC", null, // 0xe0-0xe7
"INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, "INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, // 0xe8-0xef
"BEQ", "SBC", null, null, null, "SBC", "INC", null, "BEQ", "SBC", "SBC", null, null, "SBC", "INC", null, // 0xf0-0xf7
"SED", "SBC", null, null, null, "SBC", "INC", null "SED", "SBC", null, null, null, "SBC", "INC", null // 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[] instructionModes = {
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03 Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07 Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x10-0x13 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x10-0x13
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f
@@ -207,7 +213,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x30-0x33 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x30-0x33
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f
@@ -215,7 +221,7 @@ public interface InstructionTable {
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47 Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x50-0x53 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x50-0x53
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f
@@ -223,15 +229,15 @@ public interface InstructionTable {
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67 Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b
Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x70-0x73 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x70-0x73
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f Mode.ABX, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83 Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87
Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x90-0x93
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
@@ -239,7 +245,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xb0-0xb3
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb 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.NUL, // 0xbc-0xbf
@@ -247,7 +253,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xd0-0xd3 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xd0-0xd3
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf
@@ -255,7 +261,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xf0-0xf3 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xf0-0xf3
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
@@ -263,47 +269,73 @@ public interface InstructionTable {
/** /**
* 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 = { int[] instructionSizes = {
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, // 0x00-0x0f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x10-0x1f
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x20-0x2f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x30-0x3f
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x40-0x4f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x50-0x5f
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x60-0x6f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 3, 3, 3, 0, // 0x70-0x7f
2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, // 0x80-0x8f
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, 3, 1, 0, 0, 3, 0, 0, // 0x90-0x9f
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xa0-0xaf
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 0xb0-0xbf
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xc0-0xcf
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0xd0-0xdf
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xe0-0xef
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 // 0xf0-0xff
}; };
/** /**
* Number of clock cycles required for each instruction * Number of clock cycles required for each instruction when
* in NMOS mode.
*/ */
int[] instructionClocks = { int[] instructionClocksNmos = {
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
};
/**
* Number of clock cycles required for each instruction when
* in CMOS mode
*/
int[] instructionClocksCmos = {
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
2, 6, 5, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
2, 5, 5, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
}; };
} }
+4 -1
View File
@@ -86,7 +86,9 @@ public class Main {
final Simulator simulator = new Simulator(machineClass); final Simulator simulator = new Simulator(machineClass);
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try { try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
// Create the main UI window // Create the main UI window
@@ -94,6 +96,7 @@ public class Main {
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
}
}); });
+48 -16
View File
@@ -185,7 +185,9 @@ public class Simulator {
JButton hardResetButton = new JButton("Hard Reset"); JButton hardResetButton = new JButton("Hard Reset");
stepCountBox = new JComboBox<>(STEPS); stepCountBox = new JComboBox<>(STEPS);
stepCountBox.addActionListener(actionEvent -> { stepCountBox.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
try { try {
JComboBox cb = (JComboBox) actionEvent.getSource(); JComboBox cb = (JComboBox) actionEvent.getSource();
stepsPerClick = Integer.parseInt((String) cb.getSelectedItem()); stepsPerClick = Integer.parseInt((String) cb.getSelectedItem());
@@ -193,6 +195,7 @@ public class Simulator {
stepsPerClick = 1; stepsPerClick = 1;
stepCountBox.setSelectedIndex(0); stepCountBox.setSelectedIndex(0);
} }
}
}); });
buttonContainer.add(runStopButton); buttonContainer.add(runStopButton);
@@ -211,24 +214,38 @@ public class Simulator {
// Bottom - buttons. // Bottom - buttons.
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END); mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
runStopButton.addActionListener(actionEvent -> { runStopButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
if (runLoop != null && runLoop.isRunning()) { if (runLoop != null && runLoop.isRunning()) {
handleStop(); Simulator.this.handleStop();
} else { } else {
handleStart(); Simulator.this.handleStart();
}
} }
}); });
stepButton.addActionListener(actionEvent -> handleStep(stepsPerClick)); stepButton.addActionListener(new ActionListener() {
@Override
softResetButton.addActionListener(actionEvent -> { public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset. Simulator.this.handleStep(stepsPerClick);
handleReset(false); }
}); });
hardResetButton.addActionListener(actionEvent -> { softResetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset. // If this was a CTRL-click, do a hard reset.
handleReset(true); Simulator.this.handleReset(false);
}
});
hardResetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset.
Simulator.this.handleReset(true);
}
}); });
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
@@ -400,13 +417,16 @@ public class Simulator {
logger.debug("Starting main run loop."); logger.debug("Starting main run loop.");
isRunning = true; isRunning = true;
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Don't allow step while the simulator is running // Don't allow step while the simulator is running
stepButton.setEnabled(false); stepButton.setEnabled(false);
stepCountBox.setEnabled(false); stepCountBox.setEnabled(false);
menuBar.simulatorDidStart(); menuBar.simulatorDidStart();
// Toggle the state of the run button // Toggle the state of the run button
runStopButton.setText("Stop"); runStopButton.setText("Stop");
}
}); });
try { try {
@@ -417,7 +437,9 @@ public class Simulator {
logger.error("Exception in main simulator run thread. Exiting run.", ex); logger.error("Exception in main simulator run thread. Exiting run.", ex);
} }
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
statusPane.updateState(); statusPane.updateState();
memoryWindow.updateState(); memoryWindow.updateState();
runStopButton.setText("Run"); runStopButton.setText("Run");
@@ -428,6 +450,7 @@ public class Simulator {
} }
menuBar.simulatorDidStop(); menuBar.simulatorDidStop();
traceLog.simulatorDidStop(); traceLog.simulatorDidStop();
}
}); });
isRunning = false; isRunning = false;
@@ -480,9 +503,12 @@ public class Simulator {
// Now load the program at the starting address. // Now load the program at the starting address.
loadProgram(program, preferences.getProgramStartAddress()); loadProgram(program, preferences.getProgramStartAddress());
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
console.reset(); console.reset();
breakpoints.refresh(); breakpoints.refresh();
}
}); });
// TODO: "Don't Show Again" checkbox // TODO: "Don't Show Again" checkbox
@@ -623,9 +649,12 @@ public class Simulator {
} }
public void actionPerformed(ActionEvent actionEvent) { public void actionPerformed(ActionEvent actionEvent) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
console.setFont(new Font("Monospaced", Font.PLAIN, size)); console.setFont(new Font("Monospaced", Font.PLAIN, size));
mainWindow.pack(); mainWindow.pack();
}
}); });
} }
} }
@@ -889,13 +918,16 @@ public class Simulator {
private void updateVisibleState() { private void updateVisibleState() {
// Immediately update the UI. // Immediately update the UI.
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Now update the state // Now update the state
statusPane.updateState(); statusPane.updateState();
memoryWindow.updateState(); memoryWindow.updateState();
if (traceLog.shouldUpdate()) { if (traceLog.shouldUpdate()) {
traceLog.refresh(); traceLog.refresh();
} }
}
}); });
} }
@@ -115,7 +115,9 @@ public abstract class Device implements Comparable<Device> {
} }
public void notifyListeners() { public void notifyListeners() {
deviceChangeListeners.forEach(DeviceChangeListener::deviceStateChanged); for (DeviceChangeListener listener : deviceChangeListeners) {
listener.deviceStateChanged();
}
} }
/** /**
@@ -27,7 +27,6 @@ package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException; import com.loomcom.symon.exceptions.MemoryRangeException;
import jdk.nashorn.internal.ir.annotations.Ignore;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -30,7 +30,10 @@ import org.slf4j.LoggerFactory;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
/** /**
@@ -62,22 +65,25 @@ public class BreakpointsWindow extends JFrame {
breakpointsPanel.setLayout(new BorderLayout()); breakpointsPanel.setLayout(new BorderLayout());
breakpointsPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); breakpointsPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
JButton addButton = new JButton("Add"); final JButton addButton = new JButton("Add");
JButton removeButton = new JButton("Del"); final JButton removeButton = new JButton("Del");
removeButton.setEnabled(false); removeButton.setEnabled(false);
JTextField addTextField = new JTextField(4); final JTextField addTextField = new JTextField(4);
JTable breakpointsTable = new JTable(breakpoints); final JTable breakpointsTable = new JTable(breakpoints);
breakpointsTable.setShowGrid(true); breakpointsTable.setShowGrid(true);
breakpointsTable.setGridColor(Color.LIGHT_GRAY); breakpointsTable.setGridColor(Color.LIGHT_GRAY);
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
breakpointsTable.getSelectionModel().addListSelectionListener(e -> { breakpointsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
if (e.getFirstIndex() > -1) { if (e.getFirstIndex() > -1) {
removeButton.setEnabled(true); removeButton.setEnabled(true);
} else { } else {
removeButton.setEnabled(false); removeButton.setEnabled(false);
} }
}
}); });
JScrollPane scrollPane = new JScrollPane(breakpointsTable); JScrollPane scrollPane = new JScrollPane(breakpointsTable);
@@ -86,8 +92,10 @@ public class BreakpointsWindow extends JFrame {
breakpointsPanel.add(scrollPane, BorderLayout.CENTER); breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
ActionListener addBreakpointListener = e -> { ActionListener addBreakpointListener = new ActionListener() {
int value = -1; @Override
public void actionPerformed(ActionEvent e) {
int value;
String newBreakpoint = addTextField.getText(); String newBreakpoint = addTextField.getText();
@@ -111,12 +119,18 @@ public class BreakpointsWindow extends JFrame {
logger.debug("Added breakpoint ${}", Utils.wordToHex(value)); logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
addTextField.setText(EMPTY_STRING); addTextField.setText(EMPTY_STRING);
}
}; };
addButton.addActionListener(addBreakpointListener); addButton.addActionListener(addBreakpointListener);
addTextField.addActionListener(addBreakpointListener); addTextField.addActionListener(addBreakpointListener);
removeButton.addActionListener(e -> breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow())); removeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow());
}
});
controlPanel.add(addTextField); controlPanel.add(addTextField);
controlPanel.add(addButton); controlPanel.add(addButton);
@@ -97,19 +97,25 @@ public class PreferencesDialog extends Observable implements Preferences {
JButton applyButton = new JButton("Apply"); JButton applyButton = new JButton("Apply");
JButton cancelButton = new JButton("Cancel"); JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(actionEvent -> { cancelButton.addActionListener(new ActionListener() {
updateUi(); @Override
public void actionPerformed(ActionEvent actionEvent) {
PreferencesDialog.this.updateUi();
dialog.setVisible(false); dialog.setVisible(false);
}
}); });
applyButton.addActionListener(actionEvent -> { applyButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
haltOnBreak = haltOnBreakCheckBox.isSelected(); haltOnBreak = haltOnBreakCheckBox.isSelected();
programLoadAddress = hexToInt(programLoadAddressField.getText()); programLoadAddress = PreferencesDialog.this.hexToInt(programLoadAddressField.getText());
updateUi(); PreferencesDialog.this.updateUi();
// TODO: Actually check to see if values have changed, don't assume. // TODO: Actually check to see if values have changed, don't assume.
setChanged(); PreferencesDialog.this.setChanged();
PreferencesDialog.this.notifyObservers(); PreferencesDialog.this.notifyObservers();
dialog.setVisible(false); dialog.setVisible(false);
}
}); });
buttonsContainer.add(applyButton); buttonsContainer.add(applyButton);
@@ -31,6 +31,8 @@ import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.border.EtchedBorder; import javax.swing.border.EtchedBorder;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/** /**
* UI component that displays the current state of the simulated CPU. * UI component that displays the current state of the simulated CPU.
@@ -162,59 +164,74 @@ public class StatusPanel extends JPanel {
yField = makeTextField(SMALL_TEXT_FIELD_SIZE, true); yField = makeTextField(SMALL_TEXT_FIELD_SIZE, true);
// Make fields editable // Make fields editable
pcField.addActionListener(e -> { pcField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try { try {
int newVal = getHexVal(pcField) & 0xffff; int newVal = StatusPanel.this.getHexVal(pcField) & 0xffff;
machine.getCpu().setProgramCounter(newVal); machine.getCpu().setProgramCounter(newVal);
} catch (Exception ex) { } catch (Exception ex) {
// Swallow exception // Swallow exception
} }
updateState(); StatusPanel.this.updateState();
}
}); });
spField.addActionListener(e -> { spField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try { try {
int newVal = getHexVal(spField) & 0xff; int newVal = StatusPanel.this.getHexVal(spField) & 0xff;
machine.getCpu().setStackPointer(newVal); machine.getCpu().setStackPointer(newVal);
} catch (Exception ex) { } catch (Exception ex) {
// Swallow exception // Swallow exception
} }
updateState(); StatusPanel.this.updateState();
}
}); });
aField.addActionListener(e -> { aField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try { try {
int newVal = getHexVal(aField) & 0xff; int newVal = StatusPanel.this.getHexVal(aField) & 0xff;
machine.getCpu().setAccumulator(newVal); machine.getCpu().setAccumulator(newVal);
} catch (Exception ex) { } catch (Exception ex) {
// Swallow exception // Swallow exception
} }
updateState(); StatusPanel.this.updateState();
}
}); });
xField.addActionListener(e -> { xField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try { try {
int newVal = getHexVal(xField) & 0xff; int newVal = StatusPanel.this.getHexVal(xField) & 0xff;
machine.getCpu().setXRegister(newVal); machine.getCpu().setXRegister(newVal);
} catch (Exception ex) { } catch (Exception ex) {
// Swallow exception // Swallow exception
} }
updateState(); StatusPanel.this.updateState();
}
}); });
yField.addActionListener(e -> { yField.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try { try {
int newVal = getHexVal(yField) & 0xff; int newVal = StatusPanel.this.getHexVal(yField) & 0xff;
machine.getCpu().setYRegister(newVal); machine.getCpu().setYRegister(newVal);
} catch (Exception ex) { } catch (Exception ex) {
// Swallow exception // Swallow exception
} }
updateState(); StatusPanel.this.updateState();
}
}); });
constraints.anchor = GridBagConstraints.LINE_START; constraints.anchor = GridBagConstraints.LINE_START;
@@ -122,10 +122,13 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
*/ */
private class CursorBlinker implements Runnable { private class CursorBlinker implements Runnable {
public void run() { public void run() {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (cursorBlinkRate > 0) { if (cursorBlinkRate > 0) {
hideCursor = !hideCursor; hideCursor = !hideCursor;
repaint(); VideoWindow.this.repaint();
}
} }
}); });
} }
@@ -117,9 +117,22 @@ public class CpuImpliedModeTest {
} }
@Test @Test
public void test_BRK_HonorsIrqDisableFlag() throws MemoryAccessException { public void test_BRK_IgnoresIrqDisableFlag() throws MemoryAccessException {
cpu.setIrqDisableFlag(); 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, bus.loadProgram(0xea,
0xea, 0xea,
0xea, 0xea,
@@ -131,23 +144,26 @@ public class CpuImpliedModeTest {
assertEquals(0x203, cpu.getProgramCounter()); assertEquals(0x203, cpu.getProgramCounter());
// Triggers the BRK, which should do nothing because // Triggers the BRK
// of the Interrupt Disable flag
cpu.step(); 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)); // PC high byte
assertEquals(0x05, bus.read(0x1fe)); // PC low byte
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_IRQ_DISABLE,
bus.read(0x1fd)); // Processor Status, with B set
// Reset to original contents of PC // Interrupt vector held 0x1234, so we should be there.
assertEquals(0x0206, cpu.getProgramCounter()); assertEquals(0x1234, cpu.getProgramCounter());
// Empty stack assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0xff, 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 */ /* CLC - Clear Carry Flag - $18 */
@Test @Test
public void test_CLC() throws MemoryAccessException { public void test_CLC() throws MemoryAccessException {
@@ -25,18 +25,10 @@ public class CpuIndirectIndexedModeTest {
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state
assertEquals(0, cpu.getAccumulator());
assertEquals(0, cpu.getXRegister());
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test @Test
public void test_LDA() throws Exception { public void test_LDA() throws Exception {
assertEquals(cpu.toString(), 0x00, cpu.getAccumulator());
bus.write(0x0014, 0x00); bus.write(0x0014, 0x00);
bus.write(0x0015, 0xd8); bus.write(0x0015, 0xd8);
bus.write(0xd828, 0x03); bus.write(0xd828, 0x03);
@@ -3,15 +3,19 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import junit.framework.TestCase; import org.junit.Before;
import org.junit.Test;
public class CpuIndirectModeTest extends TestCase { import static org.junit.Assert.assertEquals;
public class CpuIndirectModeTest {
protected Cpu cpu; protected Cpu cpu;
protected Bus bus; protected Bus bus;
protected Memory mem; protected Memory mem;
protected void setUp() throws Exception { @Before
public void setUp() throws Exception {
this.cpu = new Cpu(); this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff); this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0xffff); this.mem = new Memory(0x0000, 0xffff);
@@ -41,6 +45,7 @@ public class CpuIndirectModeTest extends TestCase {
/* JMP - Jump - $6c */ /* JMP - Jump - $6c */
@Test
public void test_JMP_notOnPageBoundary() throws MemoryAccessException { public void test_JMP_notOnPageBoundary() throws MemoryAccessException {
bus.write(0x3400, 0x00); bus.write(0x3400, 0x00);
bus.write(0x3401, 0x54); bus.write(0x3401, 0x54);
@@ -51,6 +56,7 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_with_ROR_Bug() throws MemoryAccessException { public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG); cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG);
bus.write(0x3400, 0x22); bus.write(0x3400, 0x22);
@@ -63,8 +69,9 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_withIndirectBug() throws MemoryAccessException { 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(0x3400, 0x22);
bus.write(0x34ff, 0x00); bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54); bus.write(0x3500, 0x54);
@@ -75,8 +82,9 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_withOutIndirectBug() throws MemoryAccessException { 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(0x3400, 0x22);
bus.write(0x34ff, 0x00); bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54); bus.write(0x3500, 0x54);
@@ -87,8 +95,9 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_cmos() throws MemoryAccessException { public void test_JMP_cmos() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.CMOS); cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
bus.write(0x3400, 0x22); bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00); bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54); bus.write(0x3500, 0x54);
@@ -3,14 +3,21 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.junit.Before;
import org.junit.Test;
public class CpuIndirectXModeTest extends TestCase { import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class CpuIndirectXModeTest {
protected Cpu cpu; protected Cpu cpu;
protected Bus bus; protected Bus bus;
protected Memory mem; protected Memory mem;
protected void setUp() throws Exception { @Before
public void setUp() throws Exception {
this.cpu = new Cpu(); this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff); this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0xffff); this.mem = new Memory(0x0000, 0xffff);
@@ -47,7 +54,7 @@ public class CpuIndirectXModeTest extends TestCase {
*/ */
/* ORA - Logical Inclusive OR - $1d */ /* ORA - Logical Inclusive OR - $1d */
@Test
public void test_ORA() throws MemoryAccessException { public void test_ORA() throws MemoryAccessException {
// Set some initial values in memory // Set some initial values in memory
bus.write(0x2c30, 0x00); bus.write(0x2c30, 0x00);
@@ -93,7 +100,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* AND - Logical AND - $3d */ /* AND - Logical AND - $3d */
@Test
public void test_AND() throws MemoryAccessException { public void test_AND() throws MemoryAccessException {
bus.write(0x1a30, 0x00); bus.write(0x1a30, 0x00);
bus.write(0x1a31, 0x11); bus.write(0x1a31, 0x11);
@@ -153,7 +160,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* EOR - Exclusive OR - $5d */ /* EOR - Exclusive OR - $5d */
@Test
public void test_EOR() throws MemoryAccessException { public void test_EOR() throws MemoryAccessException {
bus.write(0xab40, 0x00); bus.write(0xab40, 0x00);
bus.write(0xab41, 0xff); bus.write(0xab41, 0xff);
@@ -189,7 +196,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* ADC - Add with Carry - $7d */ /* ADC - Add with Carry - $7d */
@Test
public void test_ADC() throws MemoryAccessException { public void test_ADC() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
bus.write(0xab41, 0xff); bus.write(0xab41, 0xff);
@@ -290,6 +297,7 @@ public class CpuIndirectXModeTest extends TestCase {
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
} }
@Test
public void test_ADC_IncludesCarry() throws MemoryAccessException { public void test_ADC_IncludesCarry() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
@@ -307,6 +315,7 @@ public class CpuIndirectXModeTest extends TestCase {
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
} }
@Test
public void test_ADC_DecimalMode() throws MemoryAccessException { public void test_ADC_DecimalMode() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
bus.write(0xab41, 0x99); bus.write(0xab41, 0x99);
@@ -404,7 +413,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* STA - Store Accumulator - $9d */ /* STA - Store Accumulator - $9d */
@Test
public void test_STA() throws MemoryAccessException { public void test_STA() throws MemoryAccessException {
cpu.setXRegister(0x30); cpu.setXRegister(0x30);
@@ -438,7 +447,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* LDA - Load Accumulator - $bd */ /* LDA - Load Accumulator - $bd */
@Test
public void test_LDA() throws MemoryAccessException { public void test_LDA() throws MemoryAccessException {
bus.write(0xab42, 0x00); bus.write(0xab42, 0x00);
bus.write(0xab43, 0x0f); bus.write(0xab43, 0x0f);
@@ -467,7 +476,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* CMP - Compare Accumulator - $dd */ /* CMP - Compare Accumulator - $dd */
@Test
public void test_CMP() throws MemoryAccessException { public void test_CMP() throws MemoryAccessException {
bus.write(0xab40, 0x00); bus.write(0xab40, 0x00);
bus.write(0xab41, 0x80); bus.write(0xab41, 0x80);
@@ -498,7 +507,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* SBC - Subtract with Carry - $fd */ /* SBC - Subtract with Carry - $fd */
@Test
public void test_SBC() throws MemoryAccessException { public void test_SBC() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
@@ -557,6 +566,7 @@ public class CpuIndirectXModeTest extends TestCase {
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
} }
@Test
public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException { public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
@@ -602,6 +612,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
@Test
public void test_SBC_DecimalMode() throws MemoryAccessException { public void test_SBC_DecimalMode() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
bus.write(0xab50, 0x11); bus.write(0xab50, 0x11);
@@ -241,6 +241,7 @@ public class CpuTest extends TestCase {
public void testSetProcessorStatus() { public void testSetProcessorStatus() {
// Default // Default
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
@@ -384,6 +385,41 @@ public class CpuTest extends TestCase {
assertFalse(cpu.getCpuState().irqAsserted); 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 { public void testIrqHonorsIrqDisabledFlag() throws Exception {
// Ensure the IRQ disable flag is set // Ensure the IRQ disable flag is set
cpu.setIrqDisableFlag(); cpu.setIrqDisableFlag();
@@ -498,6 +534,37 @@ public class CpuTest extends TestCase {
assertFalse(cpu.getCpuState().nmiAsserted); 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 { public void testNmiIgnoresIrqDisableFlag() throws Exception {
// Set the IRQ disable flag, which should be ignored by the NMI // Set the IRQ disable flag, which should be ignored by the NMI
cpu.setIrqDisableFlag(); cpu.setIrqDisableFlag();
@@ -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());
}
}