diff --git a/.gitignore b/.gitignore
index a1be012..a7e6162 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
*~
*#
target
-.classpath
-.project
-.settings
.DS_Store
+.idea
+symon.iml
diff --git a/pom.xml b/pom.xml
index ea4b299..18d1e36 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,115 +1,144 @@
- 4.0.0
- com.loomcom.symon
- symon
- jar
- 0.1
- symon
- http://www.loomcom.com/symon
-
-
- jline
- JLine Project Repository
- http://jline.sourceforge.net/m2repo
-
-
-
-
- junit
- junit
- 4.7
- test
-
-
- jline
- jline
- 0.9.9
-
-
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ 4.0.0
+ com.loomcom.symon
+ symon
+ jar
+ 0.1
+ symon
+ http://www.loomcom.com/symon
+
+
+ UTF-8
+
+
+ UTF-8
+
+
+
+
+ jline
+ JLine Project Repository
+ http://jline.sourceforge.net/m2repo
+
+
+
+
+ true
+ always
+ fail
+
+ com.loomcom
+ Loom Communications Maven2 Repository
+ http://www.loomcom.com/maven2
+ default
+
+
+
+
+ junit
+ junit
+ 4.7
+ test
+
+
+ jline
+ jline
+ 0.9.9
+
+
+ com.grahamedgecombe.jterminal
+ jterminal
+ 1.0.2-SNAPSHOT
+
+
-
-
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
-
- 1.5
-
-
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+
+
+ 1.5
+
+
-
-
- org.apache.maven.plugins
- maven-jar-plugin
-
-
-
- com.loomcom.symon.Simulator
- com.loomcom.symon
-
-
- development
- ${pom.url}
-
-
-
-
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.3.1
+
+
+
+ com.loomcom.symon.MainWindow
+ com.loomcom.symon
+
+
+ development
+ ${project.url}
+
+
+
+
-
-
- org.codehaus.mojo
- cobertura-maven-plugin
-
-
- false
-
-
- com.loomcom.symon.*
- 90
- 90
-
-
-
-
-
- com/loomcom/symon/*.class
-
-
-
-
-
- clean
- pre-site
-
- clean
-
-
-
- instrument
- site
-
- instrument
- cobertura
- check
-
-
-
-
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ 2.4
+
+
+ false
+
+
+ com.loomcom.symon.*
+ 90
+ 90
+
+
+
+
+
+ com/loomcom/symon/*.class
+
+
+
+
+
+ clean
+ pre-site
+
+ clean
+
+
+
+ instrument
+ site
+
+ instrument
+ cobertura
+ check
+
+
+
+
-
-
+
+
-
-
-
- org.codehaus.mojo
- cobertura-maven-plugin
-
-
-
+
+
+
+ org.codehaus.mojo
+ cobertura-maven-plugin
+ 2.4
+
+
+
diff --git a/src/main/java/com/loomcom/symon/Bus.java b/src/main/java/com/loomcom/symon/Bus.java
index 6979bb9..201a507 100644
--- a/src/main/java/com/loomcom/symon/Bus.java
+++ b/src/main/java/com/loomcom/symon/Bus.java
@@ -1,6 +1,7 @@
package com.loomcom.symon;
import java.util.*;
+
import com.loomcom.symon.devices.*;
import com.loomcom.symon.exceptions.*;
@@ -9,134 +10,140 @@ import com.loomcom.symon.exceptions.*;
*/
public class Bus {
- // By default, our bus starts at 0, and goes up to 64K
- private int startAddress = 0x0000;
- private int endAddress = 0xffff;
- // The CPU
- private Cpu cpu;
- // Ordered list of IO devices.
- private SortedSet devices;
+ // By default, our bus starts at 0, and goes up to 64K
+ private int startAddress = 0x0000;
+ private int endAddress = 0xffff;
+ // The CPU
+ private Cpu cpu;
+ // Ordered list of IO devices.
+ private SortedSet devices;
- public Bus(int size) {
- this(0, size - 1);
- }
+ public Bus(int size) {
+ this(0, size - 1);
+ }
- public Bus(int startAddress, int endAddress) {
- this.devices = new TreeSet();
- this.startAddress = startAddress;
- this.endAddress = endAddress;
- }
+ public Bus(int startAddress, int endAddress) {
+ this.devices = new TreeSet();
+ this.startAddress = startAddress;
+ this.endAddress = endAddress;
+ }
- public int startAddress() {
- return startAddress;
- }
+ public int startAddress() {
+ return startAddress;
+ }
- public int endAddress() {
- return endAddress;
- }
+ public int endAddress() {
+ return endAddress;
+ }
- public void addDevice(Device device)
+ public void addDevice(Device device)
throws MemoryRangeException {
- // Make sure there's no memory overlap.
- MemoryRange memRange = device.getMemoryRange();
- for (Device d : devices) {
- if (d.getMemoryRange().overlaps(memRange)) {
- throw new MemoryRangeException("The device being added " +
- "overlaps with an existing " +
- "device.");
- }
- }
-
- // Add the device
- device.setBus(this);
- devices.add(device);
- }
-
- public void addCpu(Cpu cpu) {
- cpu.setBus(this);
- this.cpu = cpu;
- }
-
- /**
- * Returns true if the memory map is full, i.e., there are no
- * gaps between any IO devices. All memory locations map to some
- * device.
- */
- public boolean isComplete() {
- // Empty maps cannot be complete.
- if (devices.isEmpty()) { return false; }
-
- // Loop over devices and ensure they are contiguous.
- MemoryRange prev = null;
- int i = 0;
- int length = devices.size();
- for (Device d : devices) {
- MemoryRange cur = d.getMemoryRange();
- if (i == 0) {
- // If the first entry doesn't start at 'startAddress', return false.
- if (cur.startAddress() != startAddress) { return false; }
- }
-
- if (prev != null && i < length - 1) {
- // Otherwise, compare previous map's end against this map's
- // endAddress. They must be adjacent!
- if (cur.startAddress() - 1 != prev.endAddress()) {
- return false;
+ // Make sure there's no memory overlap.
+ MemoryRange memRange = device.getMemoryRange();
+ for (Device d : devices) {
+ if (d.getMemoryRange().overlaps(memRange)) {
+ throw new MemoryRangeException("The device being added " +
+ "overlaps with an existing " +
+ "device.");
+ }
}
- }
- if (i == length - 1) {
- // If the last entry doesn't end at endAddress, return false;
- if (cur.endAddress() != endAddress) { return false; }
- }
-
- i++;
- prev = cur;
+ // Add the device
+ device.setBus(this);
+ devices.add(device);
}
- // Must be complete.
- return true;
- }
-
- public int read(int address) throws MemoryAccessException {
- for (Device d : devices) {
- MemoryRange range = d.getMemoryRange();
- if (range.includes(address)) {
- // Compute offset into this device's address space.
- int devAddr = address - range.startAddress();
- return d.read(devAddr);
- }
+ public void addCpu(Cpu cpu) {
+ cpu.setBus(this);
+ this.cpu = cpu;
}
- throw new MemoryAccessException("Read failed! No device at address.");
- }
- public void write(int address, int value) throws MemoryAccessException {
- for (Device d : devices) {
- MemoryRange range = d.getMemoryRange();
- if (range.includes(address)) {
- // Compute offset into this device's address space.
- int devAddr = address - range.startAddress();
- d.write(devAddr, value);
- return;
- }
+ /**
+ * Returns true if the memory map is full, i.e., there are no
+ * gaps between any IO devices. All memory locations map to some
+ * device.
+ */
+ public boolean isComplete() {
+ // Empty maps cannot be complete.
+ if (devices.isEmpty()) {
+ return false;
+ }
+
+ // Loop over devices and ensure they are contiguous.
+ MemoryRange prev = null;
+ int i = 0;
+ int length = devices.size();
+ for (Device d : devices) {
+ MemoryRange cur = d.getMemoryRange();
+ if (i == 0) {
+ // If the first entry doesn't start at 'startAddress', return false.
+ if (cur.startAddress() != startAddress) {
+ return false;
+ }
+ }
+
+ if (prev != null && i < length - 1) {
+ // Otherwise, compare previous map's end against this map's
+ // endAddress. They must be adjacent!
+ if (cur.startAddress() - 1 != prev.endAddress()) {
+ return false;
+ }
+ }
+
+ if (i == length - 1) {
+ // If the last entry doesn't end at endAddress, return false;
+ if (cur.endAddress() != endAddress) {
+ return false;
+ }
+ }
+
+ i++;
+ prev = cur;
+ }
+
+ // Must be complete.
+ return true;
}
- throw new MemoryAccessException("Write failed! No device at address.");
- }
- public SortedSet getDevices() {
- // Expose a copy of the device list, not the original
- return new TreeSet(devices);
- }
-
- public Cpu getCpu() {
- return cpu;
- }
-
- public void loadProgram(int... program) throws MemoryAccessException {
- int address = getCpu().getProgramCounter();
- int i = 0;
- for (int d : program) {
- write(address + i++, d);
+ public int read(int address) throws MemoryAccessException {
+ for (Device d : devices) {
+ MemoryRange range = d.getMemoryRange();
+ if (range.includes(address)) {
+ // Compute offset into this device's address space.
+ int devAddr = address - range.startAddress();
+ return d.read(devAddr);
+ }
+ }
+ throw new MemoryAccessException("Read failed! No device at address.");
+ }
+
+ public void write(int address, int value) throws MemoryAccessException {
+ for (Device d : devices) {
+ MemoryRange range = d.getMemoryRange();
+ if (range.includes(address)) {
+ // Compute offset into this device's address space.
+ int devAddr = address - range.startAddress();
+ d.write(devAddr, value);
+ return;
+ }
+ }
+ throw new MemoryAccessException("Write failed! No device at address.");
+ }
+
+ public SortedSet getDevices() {
+ // Expose a copy of the device list, not the original
+ return new TreeSet(devices);
+ }
+
+ public Cpu getCpu() {
+ return cpu;
+ }
+
+ public void loadProgram(int... program) throws MemoryAccessException {
+ int address = getCpu().getProgramCounter();
+ int i = 0;
+ for (int d : program) {
+ write(address + i++, d);
+ }
}
- }
}
diff --git a/src/main/java/com/loomcom/symon/Cpu.java b/src/main/java/com/loomcom/symon/Cpu.java
index cdf6680..107b0d7 100644
--- a/src/main/java/com/loomcom/symon/Cpu.java
+++ b/src/main/java/com/loomcom/symon/Cpu.java
@@ -1,6 +1,5 @@
package com.loomcom.symon;
-import com.loomcom.symon.InstructionTable;
import com.loomcom.symon.exceptions.MemoryAccessException;
/**
@@ -8,1365 +7,1429 @@ import com.loomcom.symon.exceptions.MemoryAccessException;
*/
public class Cpu implements InstructionTable {
- public static final int DEFAULT_BASE_ADDRESS = 0x200;
+ public static final int DEFAULT_BASE_ADDRESS = 0x200;
- /* Process status register mnemonics */
- public static final int P_CARRY = 0x01;
- public static final int P_ZERO = 0x02;
- public static final int P_IRQ_DISABLE = 0x04;
- public static final int P_DECIMAL = 0x08;
- public static final int P_BREAK = 0x10;
- // Bit 5 is always '1'
- public static final int P_OVERFLOW = 0x40;
- public static final int P_NEGATIVE = 0x80;
+ /* Process status register mnemonics */
+ public static final int P_CARRY = 0x01;
+ public static final int P_ZERO = 0x02;
+ public static final int P_IRQ_DISABLE = 0x04;
+ public static final int P_DECIMAL = 0x08;
+ public static final int P_BREAK = 0x10;
+ // Bit 5 is always '1'
+ public static final int P_OVERFLOW = 0x40;
+ public static final int P_NEGATIVE = 0x80;
- // NMI vector
- public static final int IRQ_VECTOR_L = 0xfffa;
- public static final int IRQ_VECTOR_H = 0xfffb;
- // Reset vector
- public static final int RST_VECTOR_L = 0xfffc;
- public static final int RST_VECTOR_H = 0xfffd;
- // IRQ vector
- public static final int NMI_VECTOR_L = 0xfffe;
- public static final int NMI_VECTOR_H = 0xffff;
+ // NMI vector
+ public static final int IRQ_VECTOR_L = 0xfffa;
+ public static final int IRQ_VECTOR_H = 0xfffb;
+ // Reset vector
+ public static final int RST_VECTOR_L = 0xfffc;
+ public static final int RST_VECTOR_H = 0xfffd;
+ // IRQ vector
+ public static final int NMI_VECTOR_L = 0xfffe;
+ public static final int NMI_VECTOR_H = 0xffff;
- /* The Bus */
- private Bus bus;
+ /* The Bus */
+ private Bus bus;
- /* User Registers */
- private int a; // Accumulator
- private int x; // X index register
- private int y; // Y index register
+ /* User Registers */
+ private int a; // Accumulator
+ private int x; // X index register
+ private int y; // Y index register
- /* Internal Registers */
- private int pc; // Program Counter register
- private int sp; // Stack Pointer register, offset into page 1
- private int ir; // Instruction register
- private int[] args = new int[3]; // Decoded instruction args
- private int instSize; // # of operands for the instruction
+ /* Internal Registers */
+ private int pc; // Program Counter register
+ private int sp; // Stack Pointer register, offset into page 1
+ private int ir; // Instruction register
+ private int[] args = new int[3]; // Decoded instruction args
+ private int instSize; // # of operands for the instruction
- /* Scratch space for addressing mode and effective address
- * calculations */
- private int irAddressMode; // Bits 3-5 of IR: [ | | |X|X|X| | ]
- private int irOpMode; // Bits 6-7 of IR: [ | | | | | |X|X]
- private int effectiveAddress;
+ /* Scratch space for addressing mode and effective address
+ * calculations */
+ private int irAddressMode; // Bits 3-5 of IR: [ | | |X|X|X| | ]
+ private int irOpMode; // Bits 6-7 of IR: [ | | | | | |X|X]
+ private int effectiveAddress;
- /* Internal scratch space */
- private int lo = 0, hi = 0; // Used in address calculation
- private int tmp; // Temporary storage
+ /* Internal scratch space */
+ private int lo = 0, hi = 0; // Used in address calculation
+ private int tmp; // Temporary storage
- /* Unimplemented instruction flag */
- private boolean opTrap = false;
+ /* Unimplemented instruction flag */
+ private boolean opTrap = false;
- private int addr; // The address the most recent instruction
- // was fetched from
+ private int addr; // The address the most recent instruction
+ // was fetched from
- /* Status Flag Register bits */
- private boolean carryFlag;
- private boolean negativeFlag;
- private boolean zeroFlag;
- private boolean irqDisableFlag;
- private boolean decimalModeFlag;
- private boolean breakFlag;
- private boolean overflowFlag;
+ /* Status Flag Register bits */
+ private boolean carryFlag;
+ private boolean negativeFlag;
+ private boolean zeroFlag;
+ private boolean irqDisableFlag;
+ private boolean decimalModeFlag;
+ private boolean breakFlag;
+ private boolean overflowFlag;
- /**
- * Construct a new CPU.
- */
- public Cpu() {}
+ /* The number of steps taken since the last reset. */
+ private long stepCounter = 0L;
- /**
- * Set the bus reference for this CPU.
- */
- public void setBus(Bus bus) {
- this.bus = bus;
- }
-
- /**
- * Return the Bus that this CPU is associated with.
- */
- public Bus getBus() {
- return bus;
- }
-
- /**
- * Reset the CPU to known initial values.
- */
- public void reset() throws MemoryAccessException {
- // Registers
- sp = 0xff;
-
- // Set the PC to the address stored in the reset vector
- pc = address(bus.read(RST_VECTOR_L), bus.read(RST_VECTOR_H));
-
- // Clear instruction register.
- ir = 0;
-
- // Clear status register bits.
- carryFlag = false;
- irqDisableFlag = false;
- decimalModeFlag = false;
- breakFlag = false;
- overflowFlag = false;
-
- // Clear illegal opcode trap.
- opTrap = false;
- }
-
- public void step(int num) throws MemoryAccessException {
- for (int i = 0; i < num; i++) {
- step();
- }
- }
-
- /**
- * Performs an individual machine cycle.
- */
- public void step() throws MemoryAccessException {
- // Store the address from which the IR was read, for debugging
- addr = pc;
-
- // Fetch memory location for this instruction.
- ir = bus.read(pc);
- irAddressMode = (ir >> 2) & 0x07;
- irOpMode = ir & 0x03;
-
- // Increment PC
- incrementPC();
-
- // Clear the illegal opcode trap.
- clearOpTrap();
-
- // Decode the instruction and operands
- instSize = Cpu.instructionSizes[ir];
- for (int i = 0; i < instSize-1; i++) {
- args[i] = bus.read(pc);
- // Increment PC after reading
- incrementPC();
+ /**
+ * Construct a new CPU.
+ */
+ public Cpu() {
}
- // Get the data from the effective address (if any)
+ /**
+ * Set the bus reference for this CPU.
+ */
+ public void setBus(Bus bus) {
+ this.bus = bus;
+ }
- effectiveAddress = 0;
+ /**
+ * Return the Bus that this CPU is associated with.
+ */
+ public Bus getBus() {
+ return bus;
+ }
- switch(irOpMode) {
- case 0:
- case 2:
- switch(irAddressMode) {
- case 0: // #Immediate
- break;
- case 1: // Zero Page
- effectiveAddress = args[0];
- break;
- case 2: // Accumulator - ignored
- break;
- case 3: // Absolute
- effectiveAddress = address(args[0], args[1]);
- break;
- case 5: // Zero Page,X / Zero Page,Y
- if (ir == 0x96 || ir == 0xb6) {
- effectiveAddress = zpyAddress(args[0]);
- } else {
- effectiveAddress = zpxAddress(args[0]);
+ /**
+ * Reset the CPU to known initial values.
+ */
+ public void reset() throws MemoryAccessException {
+ // Registers
+ sp = 0xff;
+
+ // Set the PC to the address stored in the reset vector
+ pc = address(bus.read(RST_VECTOR_L), bus.read(RST_VECTOR_H));
+
+ // Clear instruction register.
+ ir = 0;
+
+ // Clear status register bits.
+ carryFlag = false;
+ irqDisableFlag = false;
+ decimalModeFlag = false;
+ breakFlag = false;
+ overflowFlag = false;
+
+ // Clear illegal opcode trap.
+ opTrap = false;
+
+ // Reset step counter
+ stepCounter = 0L;
+ }
+
+ public void step(int num) throws MemoryAccessException {
+ for (int i = 0; i < num; i++) {
+ step();
}
- break;
- case 7: // Absolute,X / Absolute,Y
- if (ir == 0xbe) {
- effectiveAddress = yAddress(args[0], args[1]);
- } else {
- effectiveAddress = xAddress(args[0], args[1]);
+ }
+
+ /**
+ * Performs an individual machine cycle.
+ */
+ public void step() throws MemoryAccessException {
+ // Store the address from which the IR was read, for debugging
+ addr = pc;
+
+ // Fetch memory location for this instruction.
+ ir = bus.read(pc);
+ irAddressMode = (ir >> 2) & 0x07;
+ irOpMode = ir & 0x03;
+
+ // Increment PC
+ incrementPC();
+
+ // Clear the illegal opcode trap.
+ clearOpTrap();
+
+ // Decode the instruction and operands
+ instSize = Cpu.instructionSizes[ir];
+ for (int i = 0; i < instSize - 1; i++) {
+ args[i] = bus.read(pc);
+ // Increment PC after reading
+ incrementPC();
+ }
+
+ // Increment step counter
+ stepCounter++;
+
+ // Get the data from the effective address (if any)
+ effectiveAddress = 0;
+
+ switch (irOpMode) {
+ case 0:
+ case 2:
+ switch (irAddressMode) {
+ case 0: // #Immediate
+ break;
+ case 1: // Zero Page
+ effectiveAddress = args[0];
+ break;
+ case 2: // Accumulator - ignored
+ break;
+ case 3: // Absolute
+ effectiveAddress = address(args[0], args[1]);
+ break;
+ case 5: // Zero Page,X / Zero Page,Y
+ if (ir == 0x96 || ir == 0xb6) {
+ effectiveAddress = zpyAddress(args[0]);
+ } else {
+ effectiveAddress = zpxAddress(args[0]);
+ }
+ break;
+ case 7: // Absolute,X / Absolute,Y
+ if (ir == 0xbe) {
+ effectiveAddress = yAddress(args[0], args[1]);
+ } else {
+ effectiveAddress = xAddress(args[0], args[1]);
+ }
+ break;
+ }
+ break;
+ case 1:
+ switch (irAddressMode) {
+ case 0: // (Zero Page,X)
+ tmp = args[0] + getXRegister();
+ effectiveAddress = address(bus.read(tmp), bus.read(tmp + 1));
+ break;
+ case 1: // Zero Page
+ effectiveAddress = args[0];
+ break;
+ case 2: // #Immediate
+ effectiveAddress = -1;
+ break;
+ case 3: // Absolute
+ effectiveAddress = address(args[0], args[1]);
+ break;
+ case 4: // (Zero Page),Y
+ tmp = address(bus.read(args[0]),
+ bus.read((args[0] + 1) & 0xff));
+ effectiveAddress = (tmp + getYRegister()) & 0xffff;
+ break;
+ case 5: // Zero Page,X
+ effectiveAddress = zpxAddress(args[0]);
+ break;
+ case 6: // Absolute, Y
+ effectiveAddress = yAddress(args[0], args[1]);
+ break;
+ case 7: // Absolute, X
+ effectiveAddress = xAddress(args[0], args[1]);
+ break;
+ }
+ break;
+ }
+
+ // Execute
+ switch (ir) {
+
+ /** Single Byte Instructions; Implied and Relative **/
+ case 0x00: // BRK - Force Interrupt - Implied
+ if (!getIrqDisableFlag()) {
+ // Set the break flag before pushing.
+ setBreakFlag();
+ // Push program counter + 2 onto the stack
+ stackPush((pc + 2 >> 8) & 0xff); // PC high byte
+ stackPush(pc + 2 & 0xff); // PC low byte
+ stackPush(getProcessorStatus());
+ // Set the Interrupt Disabled flag. RTI will clear it.
+ setIrqDisableFlag();
+ // Load interrupt vector address into PC
+ pc = address(bus.read(IRQ_VECTOR_L), bus.read(IRQ_VECTOR_H));
+ }
+ break;
+ case 0x08: // PHP - Push Processor Status - Implied
+ stackPush(getProcessorStatus());
+ break;
+ case 0x10: // BPL - Branch if Positive - Relative
+ if (!getNegativeFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0x18: // CLC - Clear Carry Flag - Implied
+ clearCarryFlag();
+ break;
+ case 0x20: // JSR - Jump to Subroutine - Implied
+ stackPush((pc - 1 >> 8) & 0xff); // PC high byte
+ stackPush(pc - 1 & 0xff); // PC low byte
+ pc = address(args[0], args[1]);
+ break;
+ case 0x28: // PLP - Pull Processor Status - Implied
+ setProcessorStatus(stackPop());
+ break;
+ case 0x30: // BMI - Branch if Minus - Relative
+ if (getNegativeFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0x38: // SEC - Set Carry Flag - Implied
+ setCarryFlag();
+ break;
+ case 0x40: // RTI - Return from Interrupt - Implied
+ setProcessorStatus(stackPop());
+ lo = stackPop();
+ hi = stackPop();
+ setProgramCounter(address(lo, hi));
+ break;
+ case 0x48: // PHA - Push Accumulator - Implied
+ stackPush(a);
+ break;
+ case 0x50: // BVC - Branch if Overflow Clear - Relative
+ if (!getOverflowFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0x58: // CLI - Clear Interrupt Disable - Implied
+ clearIrqDisableFlag();
+ break;
+ case 0x60: // RTS - Return from Subroutine - Implied
+ lo = stackPop();
+ hi = stackPop();
+ setProgramCounter((address(lo, hi) + 1) & 0xffff);
+ break;
+ case 0x68: // PLA - Pull Accumulator - Implied
+ a = stackPop();
+ setArithmeticFlags(a);
+ break;
+ case 0x70: // BVS - Branch if Overflow Set - Relative
+ if (getOverflowFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0x78: // SEI - Set Interrupt Disable - Implied
+ setIrqDisableFlag();
+ break;
+ case 0x88: // DEY - Decrement Y Register - Implied
+ y = --y & 0xff;
+ setArithmeticFlags(y);
+ break;
+ case 0x8a: // TXA - Transfer X to Accumulator - Implied
+ a = x;
+ setArithmeticFlags(a);
+ break;
+ case 0x90: // BCC - Branch if Carry Clear - Relative
+ if (!getCarryFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0x98: // TYA - Transfer Y to Accumulator - Implied
+ a = y;
+ setArithmeticFlags(a);
+ break;
+ case 0x9a: // TXS - Transfer X to Stack Pointer - Implied
+ setStackPointer(x);
+ break;
+ case 0xa8: // TAY - Transfer Accumulator to Y - Implied
+ y = a;
+ setArithmeticFlags(y);
+ break;
+ case 0xaa: // TAX - Transfer Accumulator to X - Implied
+ x = a;
+ setArithmeticFlags(x);
+ break;
+ case 0xb0: // BCS - Branch if Carry Set - Relative
+ if (getCarryFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0xb8: // CLV - Clear Overflow Flag - Implied
+ clearOverflowFlag();
+ break;
+ case 0xba: // TSX - Transfer Stack Pointer to X - Implied
+ x = getStackPointer();
+ setArithmeticFlags(x);
+ break;
+ case 0xc8: // INY - Increment Y Register - Implied
+ y = ++y & 0xff;
+ setArithmeticFlags(y);
+ break;
+ case 0xca: // DEX - Decrement X Register - Implied
+ x = --x & 0xff;
+ setArithmeticFlags(x);
+ break;
+ case 0xd0: // BNE - Branch if Not Equal to Zero - Relative
+ if (!getZeroFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0xd8: // CLD - Clear Decimal Mode - Implied
+ clearDecimalModeFlag();
+ break;
+ case 0xe8: // INX - Increment X Register - Implied
+ x = ++x & 0xff;
+ setArithmeticFlags(x);
+ break;
+ case 0xea: // NOP
+ // Do nothing.
+ break;
+ case 0xf0: // BEQ - Branch if Equal to Zero - Relative
+ if (getZeroFlag()) {
+ pc = relAddress(args[0]);
+ }
+ break;
+ case 0xf8: // SED - Set Decimal Flag - Implied
+ setDecimalModeFlag();
+ break;
+
+ /** JMP *****************************************************************/
+ case 0x4c: // JMP - Absolute
+ pc = address(args[0], args[1]);
+ break;
+ case 0x6c: // JMP - Indirect
+ lo = address(args[0], args[1]); // Address of low byte
+ hi = lo + 1; // Address of high byte
+ pc = address(bus.read(lo), bus.read(hi));
+ /* TODO: For accuracy, allow a flag to enable broken behavior
+ * of early 6502s:
+ *
+ * "An original 6502 has does not correctly fetch the target
+ * address if the indirect vector falls on a page boundary
+ * (e.g. $xxFF where xx is and value from $00 to $FF). In this
+ * case fetches the LSB from $xxFF as expected but takes the MSB
+ * from $xx00. This is fixed in some later chips like the 65SC02
+ * so for compatibility always ensure the indirect vector is not
+ * at the end of the page."
+ * (http://www.obelisk.demon.co.uk/6502/reference.html#JMP)
+ */
+ break;
+
+
+ /** ORA - Logical Inclusive Or ******************************************/
+ case 0x09: // #Immediate
+ a |= args[0];
+ setArithmeticFlags(a);
+ break;
+ case 0x01: // (Zero Page,X)
+ case 0x05: // Zero Page
+ case 0x0d: // Absolute
+ case 0x11: // (Zero Page),Y
+ case 0x15: // Zero Page,X
+ case 0x19: // Absolute,Y
+ case 0x1d: // Absolute,X
+ a |= bus.read(effectiveAddress);
+ setArithmeticFlags(a);
+ break;
+
+
+ /** ASL - Arithmetic Shift Left *****************************************/
+ case 0x0a: // Accumulator
+ a = asl(a);
+ setArithmeticFlags(a);
+ break;
+ case 0x06: // Zero Page
+ case 0x0e: // Absolute
+ case 0x16: // Zero Page,X
+ case 0x1e: // Absolute,X
+ tmp = asl(bus.read(effectiveAddress));
+ bus.write(effectiveAddress, tmp);
+ setArithmeticFlags(tmp);
+ break;
+
+
+ /** BIT - Bit Test ******************************************************/
+ case 0x24: // Zero Page
+ case 0x2c: // Absolute
+ tmp = a & bus.read(effectiveAddress);
+ setZeroFlag(tmp == 0);
+ setNegativeFlag((tmp & 0x80) != 0);
+ setOverflowFlag((tmp & 0x40) != 0);
+ break;
+
+
+ /** AND - Logical AND ***************************************************/
+ case 0x29: // #Immediate
+ a &= args[0];
+ setArithmeticFlags(a);
+ break;
+ case 0x21: // (Zero Page,X)
+ case 0x25: // Zero Page
+ case 0x2d: // Absolute
+ case 0x31: // (Zero Page),Y
+ case 0x35: // Zero Page,X
+ case 0x39: // Absolute,Y
+ case 0x3d: // Absolute,X
+ a &= bus.read(effectiveAddress);
+ setArithmeticFlags(a);
+ break;
+
+
+ /** ROL - Rotate Left ***************************************************/
+ case 0x2a: // Accumulator
+ a = rol(a);
+ setArithmeticFlags(a);
+ break;
+ case 0x26: // Zero Page
+ case 0x2e: // Absolute
+ case 0x36: // Zero Page,X
+ case 0x3e: // Absolute,X
+ tmp = rol(bus.read(effectiveAddress));
+ bus.write(effectiveAddress, tmp);
+ setArithmeticFlags(tmp);
+ break;
+
+
+ /** EOR - Exclusive OR **************************************************/
+ case 0x49: // #Immediate
+ a ^= args[0];
+ setArithmeticFlags(a);
+ break;
+ case 0x41: // (Zero Page,X)
+ case 0x45: // Zero Page
+ case 0x4d: // Absolute
+ case 0x51: // (Zero Page,Y)
+ case 0x55: // Zero Page,X
+ case 0x59: // Absolute,Y
+ case 0x5d: // Absolute,X
+ a ^= bus.read(effectiveAddress);
+ setArithmeticFlags(a);
+ break;
+
+
+ /** LSR - Logical Shift Right *******************************************/
+ case 0x4a: // Accumulator
+ a = lsr(a);
+ setArithmeticFlags(a);
+ break;
+ case 0x46: // Zero Page
+ case 0x4e: // Absolute
+ case 0x56: // Zero Page,X
+ case 0x5e: // Absolute,X
+ tmp = lsr(bus.read(effectiveAddress));
+ bus.write(effectiveAddress, tmp);
+ setArithmeticFlags(tmp);
+ break;
+
+
+ /** ADC - Add with Carry ************************************************/
+ case 0x69: // #Immediate
+ if (decimalModeFlag) {
+ a = adcDecimal(a, args[0]);
+ } else {
+ a = adc(a, args[0]);
+ }
+ break;
+ case 0x61: // (Zero Page,X)
+ case 0x65: // Zero Page
+ case 0x6d: // Absolute
+ case 0x71: // (Zero Page),Y
+ case 0x75: // Zero Page,X
+ case 0x79: // Absolute,Y
+ case 0x7d: // Absolute,X
+ if (decimalModeFlag) {
+ a = adcDecimal(a, bus.read(effectiveAddress));
+ } else {
+ a = adc(a, bus.read(effectiveAddress));
+ }
+ break;
+
+
+ /** ROR - Rotate Right **************************************************/
+ case 0x6a: // Accumulator
+ a = ror(a);
+ setArithmeticFlags(a);
+ break;
+ case 0x66: // Zero Page
+ case 0x6e: // Absolute
+ case 0x76: // Zero Page,X
+ case 0x7e: // Absolute,X
+ tmp = ror(bus.read(effectiveAddress));
+ bus.write(effectiveAddress, tmp);
+ setArithmeticFlags(tmp);
+ break;
+
+
+ /** STA - Store Accumulator *********************************************/
+ case 0x81: // (Zero Page,X)
+ case 0x85: // Zero Page
+ case 0x8d: // Absolute
+ case 0x91: // (Zero Page),Y
+ case 0x95: // Zero Page,X
+ case 0x99: // Absolute,Y
+ case 0x9d: // Absolute,X
+ bus.write(effectiveAddress, a);
+ setArithmeticFlags(a);
+ break;
+
+
+ /** STY - Store Y Register **********************************************/
+ case 0x84: // Zero Page
+ case 0x8c: // Absolute
+ case 0x94: // Zero Page,X
+ bus.write(effectiveAddress, y);
+ setArithmeticFlags(y);
+ break;
+
+
+ /** STX - Store X Register **********************************************/
+ case 0x86: // Zero Page
+ case 0x8e: // Absolute
+ case 0x96: // Zero Page,Y
+ bus.write(effectiveAddress, x);
+ setArithmeticFlags(x);
+ break;
+
+
+ /** LDY - Load Y Register ***********************************************/
+ case 0xa0: // #Immediate
+ y = args[0];
+ setArithmeticFlags(y);
+ break;
+ case 0xa4: // Zero Page
+ case 0xac: // Absolute
+ case 0xb4: // Zero Page,X
+ case 0xbc: // Absolute,X
+ y = bus.read(effectiveAddress);
+ setArithmeticFlags(y);
+ break;
+
+
+ /** LDX - Load X Register ***********************************************/
+ case 0xa2: // #Immediate
+ x = args[0];
+ setArithmeticFlags(x);
+ break;
+ case 0xa6: // Zero Page
+ case 0xae: // Absolute
+ case 0xb6: // Zero Page,Y
+ case 0xbe: // Absolute,Y
+ x = bus.read(effectiveAddress);
+ setArithmeticFlags(x);
+ break;
+
+
+ /** LDA - Load Accumulator **********************************************/
+ case 0xa9: // #Immediate
+ a = args[0];
+ setArithmeticFlags(a);
+ break;
+ case 0xa1: // (Zero Page,X)
+ case 0xa5: // Zero Page
+ case 0xad: // Absolute
+ case 0xb1: // (Zero Page),Y
+ case 0xb5: // Zero Page,X
+ case 0xb9: // Absolute,Y
+ case 0xbd: // Absolute,X
+ a = bus.read(effectiveAddress);
+ setArithmeticFlags(a);
+ break;
+
+
+ /** CPY - Compare Y Register ********************************************/
+ case 0xc0: // #Immediate
+ cmp(y, args[0]);
+ break;
+ case 0xc4: // Zero Page
+ case 0xcc: // Absolute
+ cmp(y, bus.read(effectiveAddress));
+ break;
+
+
+ /** CMP - Compare Accumulator *******************************************/
+ case 0xc9: // #Immediate
+ cmp(a, args[0]);
+ break;
+ case 0xc1: // (Zero Page,X)
+ case 0xc5: // Zero Page
+ case 0xcd: // Absolute
+ case 0xd1: // (Zero Page),Y
+ case 0xd5: // Zero Page,X
+ case 0xd9: // Absolute,Y
+ case 0xdd: // Absolute,X
+ cmp(a, bus.read(effectiveAddress));
+ break;
+
+
+ /** DEC - Decrement Memory **********************************************/
+ case 0xc6: // Zero Page
+ case 0xce: // Absolute
+ case 0xd6: // Zero Page,X
+ case 0xde: // Absolute,X
+ tmp = (bus.read(effectiveAddress) - 1) & 0xff;
+ bus.write(effectiveAddress, tmp);
+ setArithmeticFlags(tmp);
+ break;
+
+
+ /** CPX - Compare X Register ********************************************/
+ case 0xe0: // #Immediate
+ cmp(x, args[0]);
+ break;
+ case 0xe4: // Zero Page
+ case 0xec: // Absolute
+ cmp(x, bus.read(effectiveAddress));
+ break;
+
+
+ /** SBC - Subtract with Carry (Borrow) **********************************/
+ case 0xe9: // #Immediate
+ if (decimalModeFlag) {
+ a = sbcDecimal(a, args[0]);
+ } else {
+ a = sbc(a, args[0]);
+ }
+ break;
+ case 0xe1: // (Zero Page,X)
+ case 0xe5: // Zero Page
+ case 0xed: // Absolute
+ case 0xf1: // (Zero Page),Y
+ case 0xf5: // Zero Page,X
+ case 0xf9: // Absolute,Y
+ case 0xfd: // Absolute,X
+ if (decimalModeFlag) {
+ a = sbcDecimal(a, bus.read(effectiveAddress));
+ } else {
+ a = sbc(a, bus.read(effectiveAddress));
+ }
+ break;
+
+
+ /** INC - Increment Memory **********************************************/
+ case 0xe6: // Zero Page
+ case 0xee: // Absolute
+ case 0xf6: // Zero Page,X
+ case 0xfe: // Absolute,X
+ tmp = (bus.read(effectiveAddress) + 1) & 0xff;
+ bus.write(effectiveAddress, tmp);
+ setArithmeticFlags(tmp);
+ break;
+
+ /** Unimplemented Instructions ****************************************/
+ default:
+ setOpTrap();
+ break;
}
- break;
- }
- break;
- case 1:
- switch(irAddressMode) {
- case 0: // (Zero Page,X)
- tmp = args[0] + getXRegister();
- effectiveAddress = address(bus.read(tmp), bus.read(tmp + 1));
- break;
- case 1: // Zero Page
- effectiveAddress = args[0];
- break;
- case 2: // #Immediate
- effectiveAddress = -1;
- break;
- case 3: // Absolute
- effectiveAddress = address(args[0], args[1]);
- break;
- case 4: // (Zero Page),Y
- tmp = address(bus.read(args[0]),
- bus.read((args[0]+1)&0xff));
- effectiveAddress = (tmp + getYRegister())&0xffff;
- break;
- case 5: // Zero Page,X
- effectiveAddress = zpxAddress(args[0]);
- break;
- case 6: // Absolute, Y
- effectiveAddress = yAddress(args[0], args[1]);
- break;
- case 7: // Absolute, X
- effectiveAddress = xAddress(args[0], args[1]);
- break;
- }
- break;
}
- // Execute
- switch(ir) {
-
- /** Single Byte Instructions; Implied and Relative **/
- case 0x00: // BRK - Force Interrupt - Implied
- if (!getIrqDisableFlag()) {
- // Set the break flag before pushing.
- setBreakFlag();
- // Push program counter + 2 onto the stack
- stackPush((pc+2 >> 8) & 0xff); // PC high byte
- stackPush(pc+2 & 0xff); // PC low byte
- stackPush(getProcessorStatus());
- // Set the Interrupt Disabled flag. RTI will clear it.
- setIrqDisableFlag();
- // Load interrupt vector address into PC
- pc = address(bus.read(IRQ_VECTOR_L), bus.read(IRQ_VECTOR_H));
- }
- break;
- case 0x08: // PHP - Push Processor Status - Implied
- stackPush(getProcessorStatus());
- break;
- case 0x10: // BPL - Branch if Positive - Relative
- if (!getNegativeFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0x18: // CLC - Clear Carry Flag - Implied
- clearCarryFlag();
- break;
- case 0x20: // JSR - Jump to Subroutine - Implied
- stackPush((pc-1 >> 8) & 0xff); // PC high byte
- stackPush(pc-1 & 0xff); // PC low byte
- pc = address(args[0], args[1]);
- break;
- case 0x28: // PLP - Pull Processor Status - Implied
- setProcessorStatus(stackPop());
- break;
- case 0x30: // BMI - Branch if Minus - Relative
- if (getNegativeFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0x38: // SEC - Set Carry Flag - Implied
- setCarryFlag();
- break;
- case 0x40: // RTI - Return from Interrupt - Implied
- setProcessorStatus(stackPop());
- lo = stackPop();
- hi = stackPop();
- setProgramCounter(address(lo, hi));
- break;
- case 0x48: // PHA - Push Accumulator - Implied
- stackPush(a);
- break;
- case 0x50: // BVC - Branch if Overflow Clear - Relative
- if (!getOverflowFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0x58: // CLI - Clear Interrupt Disable - Implied
- clearIrqDisableFlag();
- break;
- case 0x60: // RTS - Return from Subroutine - Implied
- lo = stackPop();
- hi = stackPop();
- setProgramCounter((address(lo, hi) + 1) & 0xffff);
- break;
- case 0x68: // PLA - Pull Accumulator - Implied
- a = stackPop();
- setArithmeticFlags(a);
- break;
- case 0x70: // BVS - Branch if Overflow Set - Relative
- if (getOverflowFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0x78: // SEI - Set Interrupt Disable - Implied
- setIrqDisableFlag();
- break;
- case 0x88: // DEY - Decrement Y Register - Implied
- y = --y & 0xff;
- setArithmeticFlags(y);
- break;
- case 0x8a: // TXA - Transfer X to Accumulator - Implied
- a = x;
- setArithmeticFlags(a);
- break;
- case 0x90: // BCC - Branch if Carry Clear - Relative
- if (!getCarryFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0x98: // TYA - Transfer Y to Accumulator - Implied
- a = y;
- setArithmeticFlags(a);
- break;
- case 0x9a: // TXS - Transfer X to Stack Pointer - Implied
- setStackPointer(x);
- break;
- case 0xa8: // TAY - Transfer Accumulator to Y - Implied
- y = a;
- setArithmeticFlags(y);
- break;
- case 0xaa: // TAX - Transfer Accumulator to X - Implied
- x = a;
- setArithmeticFlags(x);
- break;
- case 0xb0: // BCS - Branch if Carry Set - Relative
- if (getCarryFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0xb8: // CLV - Clear Overflow Flag - Implied
- clearOverflowFlag();
- break;
- case 0xba: // TSX - Transfer Stack Pointer to X - Implied
- x = getStackPointer();
- setArithmeticFlags(x);
- break;
- case 0xc8: // INY - Increment Y Register - Implied
- y = ++y & 0xff;
- setArithmeticFlags(y);
- break;
- case 0xca: // DEX - Decrement X Register - Implied
- x = --x & 0xff;
- setArithmeticFlags(x);
- break;
- case 0xd0: // BNE - Branch if Not Equal to Zero - Relative
- if (!getZeroFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0xd8: // CLD - Clear Decimal Mode - Implied
- clearDecimalModeFlag();
- break;
- case 0xe8: // INX - Increment X Register - Implied
- x = ++x & 0xff;
- setArithmeticFlags(x);
- break;
- case 0xea: // NOP
- // Do nothing.
- break;
- case 0xf0: // BEQ - Branch if Equal to Zero - Relative
- if (getZeroFlag()) {
- pc = relAddress(args[0]);
- }
- break;
- case 0xf8: // SED - Set Decimal Flag - Implied
- setDecimalModeFlag();
- break;
-
- /** JMP *****************************************************************/
- case 0x4c: // JMP - Absolute
- pc = address(args[0], args[1]);
- break;
- case 0x6c: // JMP - Indirect
- lo = address(args[0], args[1]); // Address of low byte
- hi = lo+1; // Address of high byte
- pc = address(bus.read(lo), bus.read(hi));
- /* TODO: For accuracy, allow a flag to enable broken behavior
- * of early 6502s:
- *
- * "An original 6502 has does not correctly fetch the target
- * address if the indirect vector falls on a page boundary
- * (e.g. $xxFF where xx is and value from $00 to $FF). In this
- * case fetches the LSB from $xxFF as expected but takes the MSB
- * from $xx00. This is fixed in some later chips like the 65SC02
- * so for compatibility always ensure the indirect vector is not
- * at the end of the page."
- * (http://www.obelisk.demon.co.uk/6502/reference.html#JMP)
- */
- break;
-
-
- /** ORA - Logical Inclusive Or ******************************************/
- case 0x09: // #Immediate
- a |= args[0];
- setArithmeticFlags(a);
- break;
- case 0x01: // (Zero Page,X)
- case 0x05: // Zero Page
- case 0x0d: // Absolute
- case 0x11: // (Zero Page),Y
- case 0x15: // Zero Page,X
- case 0x19: // Absolute,Y
- case 0x1d: // Absolute,X
- a |= bus.read(effectiveAddress);
- setArithmeticFlags(a);
- break;
-
-
- /** ASL - Arithmetic Shift Left *****************************************/
- case 0x0a: // Accumulator
- a = asl(a);
- setArithmeticFlags(a);
- break;
- case 0x06: // Zero Page
- case 0x0e: // Absolute
- case 0x16: // Zero Page,X
- case 0x1e: // Absolute,X
- tmp = asl(bus.read(effectiveAddress));
- bus.write(effectiveAddress, tmp);
- setArithmeticFlags(tmp);
- break;
-
-
- /** BIT - Bit Test ******************************************************/
- case 0x24: // Zero Page
- case 0x2c: // Absolute
- tmp = a & bus.read(effectiveAddress);
- setZeroFlag(tmp == 0);
- setNegativeFlag((tmp & 0x80) != 0);
- setOverflowFlag((tmp & 0x40) != 0);
- break;
-
-
- /** AND - Logical AND ***************************************************/
- case 0x29: // #Immediate
- a &= args[0];
- setArithmeticFlags(a);
- break;
- case 0x21: // (Zero Page,X)
- case 0x25: // Zero Page
- case 0x2d: // Absolute
- case 0x31: // (Zero Page),Y
- case 0x35: // Zero Page,X
- case 0x39: // Absolute,Y
- case 0x3d: // Absolute,X
- a &= bus.read(effectiveAddress);
- setArithmeticFlags(a);
- break;
-
-
- /** ROL - Rotate Left ***************************************************/
- case 0x2a: // Accumulator
- a = rol(a);
- setArithmeticFlags(a);
- break;
- case 0x26: // Zero Page
- case 0x2e: // Absolute
- case 0x36: // Zero Page,X
- case 0x3e: // Absolute,X
- tmp = rol(bus.read(effectiveAddress));
- bus.write(effectiveAddress, tmp);
- setArithmeticFlags(tmp);
- break;
-
-
- /** EOR - Exclusive OR **************************************************/
- case 0x49: // #Immediate
- a ^= args[0];
- setArithmeticFlags(a);
- break;
- case 0x41: // (Zero Page,X)
- case 0x45: // Zero Page
- case 0x4d: // Absolute
- case 0x51: // (Zero Page,Y)
- case 0x55: // Zero Page,X
- case 0x59: // Absolute,Y
- case 0x5d: // Absolute,X
- a ^= bus.read(effectiveAddress);
- setArithmeticFlags(a);
- break;
-
-
- /** LSR - Logical Shift Right *******************************************/
- case 0x4a: // Accumulator
- a = lsr(a);
- setArithmeticFlags(a);
- break;
- case 0x46: // Zero Page
- case 0x4e: // Absolute
- case 0x56: // Zero Page,X
- case 0x5e: // Absolute,X
- tmp = lsr(bus.read(effectiveAddress));
- bus.write(effectiveAddress, tmp);
- setArithmeticFlags(tmp);
- break;
-
-
- /** ADC - Add with Carry ************************************************/
- case 0x69: // #Immediate
- if (decimalModeFlag) {
- a = adcDecimal(a, args[0]);
- } else {
- a = adc(a, args[0]);
- }
- break;
- case 0x61: // (Zero Page,X)
- case 0x65: // Zero Page
- case 0x6d: // Absolute
- case 0x71: // (Zero Page),Y
- case 0x75: // Zero Page,X
- case 0x79: // Absolute,Y
- case 0x7d: // Absolute,X
- if (decimalModeFlag) {
- a = adcDecimal(a, bus.read(effectiveAddress));
- } else {
- a = adc(a, bus.read(effectiveAddress));
- }
- break;
-
-
- /** ROR - Rotate Right **************************************************/
- case 0x6a: // Accumulator
- a = ror(a);
- setArithmeticFlags(a);
- break;
- case 0x66: // Zero Page
- case 0x6e: // Absolute
- case 0x76: // Zero Page,X
- case 0x7e: // Absolute,X
- tmp = ror(bus.read(effectiveAddress));
- bus.write(effectiveAddress, tmp);
- setArithmeticFlags(tmp);
- break;
-
-
- /** STA - Store Accumulator *********************************************/
- case 0x81: // (Zero Page,X)
- case 0x85: // Zero Page
- case 0x8d: // Absolute
- case 0x91: // (Zero Page),Y
- case 0x95: // Zero Page,X
- case 0x99: // Absolute,Y
- case 0x9d: // Absolute,X
- bus.write(effectiveAddress, a);
- setArithmeticFlags(a);
- break;
-
-
- /** STY - Store Y Register **********************************************/
- case 0x84: // Zero Page
- case 0x8c: // Absolute
- case 0x94: // Zero Page,X
- bus.write(effectiveAddress, y);
- setArithmeticFlags(y);
- break;
-
-
- /** STX - Store X Register **********************************************/
- case 0x86: // Zero Page
- case 0x8e: // Absolute
- case 0x96: // Zero Page,Y
- bus.write(effectiveAddress, x);
- setArithmeticFlags(x);
- break;
-
-
- /** LDY - Load Y Register ***********************************************/
- case 0xa0: // #Immediate
- y = args[0];
- setArithmeticFlags(y);
- break;
- case 0xa4: // Zero Page
- case 0xac: // Absolute
- case 0xb4: // Zero Page,X
- case 0xbc: // Absolute,X
- y = bus.read(effectiveAddress);
- setArithmeticFlags(y);
- break;
-
-
- /** LDX - Load X Register ***********************************************/
- case 0xa2: // #Immediate
- x = args[0];
- setArithmeticFlags(x);
- break;
- case 0xa6: // Zero Page
- case 0xae: // Absolute
- case 0xb6: // Zero Page,Y
- case 0xbe: // Absolute,Y
- x = bus.read(effectiveAddress);
- setArithmeticFlags(x);
- break;
-
-
- /** LDA - Load Accumulator **********************************************/
- case 0xa9: // #Immediate
- a = args[0];
- setArithmeticFlags(a);
- break;
- case 0xa1: // (Zero Page,X)
- case 0xa5: // Zero Page
- case 0xad: // Absolute
- case 0xb1: // (Zero Page),Y
- case 0xb5: // Zero Page,X
- case 0xb9: // Absolute,Y
- case 0xbd: // Absolute,X
- a = bus.read(effectiveAddress);
- setArithmeticFlags(a);
- break;
-
-
- /** CPY - Compare Y Register ********************************************/
- case 0xc0: // #Immediate
- cmp(y, args[0]);
- break;
- case 0xc4: // Zero Page
- case 0xcc: // Absolute
- cmp(y, bus.read(effectiveAddress));
- break;
-
-
- /** CMP - Compare Accumulator *******************************************/
- case 0xc9: // #Immediate
- cmp(a, args[0]);
- break;
- case 0xc1: // (Zero Page,X)
- case 0xc5: // Zero Page
- case 0xcd: // Absolute
- case 0xd1: // (Zero Page),Y
- case 0xd5: // Zero Page,X
- case 0xd9: // Absolute,Y
- case 0xdd: // Absolute,X
- cmp(a, bus.read(effectiveAddress));
- break;
-
-
- /** DEC - Decrement Memory **********************************************/
- case 0xc6: // Zero Page
- case 0xce: // Absolute
- case 0xd6: // Zero Page,X
- case 0xde: // Absolute,X
- tmp = (bus.read(effectiveAddress) - 1) & 0xff;
- bus.write(effectiveAddress, tmp);
- setArithmeticFlags(tmp);
- break;
-
-
- /** CPX - Compare X Register ********************************************/
- case 0xe0: // #Immediate
- cmp(x, args[0]);
- break;
- case 0xe4: // Zero Page
- case 0xec: // Absolute
- cmp(x, bus.read(effectiveAddress));
- break;
-
-
- /** SBC - Subtract with Carry (Borrow) **********************************/
- case 0xe9: // #Immediate
- if (decimalModeFlag) {
- a = sbcDecimal(a, args[0]);
- } else {
- a = sbc(a, args[0]);
- }
- break;
- case 0xe1: // (Zero Page,X)
- case 0xe5: // Zero Page
- case 0xed: // Absolute
- case 0xf1: // (Zero Page),Y
- case 0xf5: // Zero Page,X
- case 0xf9: // Absolute,Y
- case 0xfd: // Absolute,X
- if (decimalModeFlag) {
- a = sbcDecimal(a, bus.read(effectiveAddress));
- } else {
- a = sbc(a, bus.read(effectiveAddress));
- }
- break;
-
-
- /** INC - Increment Memory **********************************************/
- case 0xe6: // Zero Page
- case 0xee: // Absolute
- case 0xf6: // Zero Page,X
- case 0xfe: // Absolute,X
- tmp = (bus.read(effectiveAddress) + 1) & 0xff;
- bus.write(effectiveAddress, tmp);
- setArithmeticFlags(tmp);
- break;
-
- /** Unimplemented Instructions ****************************************/
- default:
- setOpTrap();
- break;
- }
- }
-
- /**
- * Add with Carry, used by all addressing mode implementations of ADC.
- * As a side effect, this will set the overflow and carry flags as
- * needed.
- *
- * @param acc The current value of the accumulator
- * @param operand The operand
- * @return
- */
- public int adc(int acc, int operand) {
- int result = (operand & 0xff) + (acc & 0xff) + getCarryBit();
- int carry6 = (operand & 0x7f) + (acc & 0x7f) + getCarryBit();
- setCarryFlag((result & 0x100) != 0);
- setOverflowFlag(carryFlag ^ ((carry6 & 0x80) != 0));
- result &= 0xff;
- setArithmeticFlags(result);
- return result;
- }
-
- /**
- * Add with Carry (BCD).
- */
-
- public int adcDecimal(int acc, int operand) {
- int l, h, result;
- l = (acc & 0x0f) + (operand & 0x0f) + getCarryBit();
- if ((l & 0xff) > 9) l += 6;
- h = (acc >> 4) + (operand >> 4) + (l > 15 ? 1 : 0);
- if ((h & 0xff) > 9) h += 6;
- result = (l & 0x0f) | (h << 4);
- result &= 0xff;
- setCarryFlag(h > 15);
- setZeroFlag(result == 0);
- setNegativeFlag(false); // BCD is never negative
- setOverflowFlag(false); // BCD never sets overflow flag
- return result;
- }
-
- /**
- * Common code for Subtract with Carry. Just calls ADC of the
- * one's complement of the operand. This lets the N, V, C, and Z
- * flags work out nicely without any additional logic.
- *
- * @param acc
- * @param operand
- * @return
- */
- public int sbc(int acc, int operand) {
- int result;
- result = adc(acc, ~operand);
- setArithmeticFlags(result);
- return result;
- }
-
- /**
- * Subtract with Carry, BCD mode.
- *
- * @param acc
- * @param operand
- * @return
- */
- public int sbcDecimal(int acc, int operand) {
- int l, h, result;
- l = (acc & 0x0f) - (operand & 0x0f) - (carryFlag ? 0 : 1);
- if ((l & 0x10) != 0) l -= 6;
- h = (acc >> 4) - (operand >> 4) - ((l & 0x10) != 0 ? 1 : 0);
- if ((h & 0x10) != 0) h -= 6;
- result = (l & 0x0f) | (h << 4);
- setCarryFlag((h & 0xff) < 15);
- setZeroFlag(result == 0);
- setNegativeFlag(false); // BCD is never negative
- setOverflowFlag(false); // BCD never sets overflow flag
- return (result & 0xff);
- }
-
- /**
- * Compare two values, and set carry, zero, and negative flags
- * appropriately.
- *
- * @param reg
- * @param operand
- */
- public void cmp(int reg, int operand) {
- setCarryFlag(reg >= operand);
- setZeroFlag(reg == operand);
- setNegativeFlag((reg - operand) > 0);
- }
-
- /**
- * Set the Negative and Zero flags based on the current value of the
- * register operand.
- *
- * @param reg The register.
- */
- public void setArithmeticFlags(int reg) {
- zeroFlag = (reg == 0);
- negativeFlag = (reg & 0x80) != 0;
- }
-
- /**
- * Shifts the given value left by one bit, and sets the carry
- * flag to the high bit of the initial value.
- *
- * @param m The value to shift left.
- * @return the left shifted value (m * 2).
- */
- private int asl(int m) {
- setCarryFlag((m & 0x80) != 0);
- return (m << 1) & 0xff;
- }
-
- /**
- * Shifts the given value right by one bit, filling with zeros,
- * and sets the carry flag to the low bit of the initial value.
- */
- private int lsr(int m) {
- setCarryFlag((m & 0x01) != 0);
- return (m >>> 1) & 0xff;
- }
-
- /**
- * Rotates the given value left by one bit, setting bit 0 to the value
- * of the carry flag, and setting the carry flag to the original value
- * of bit 7.
- */
- private int rol(int m) {
- int result = ((m << 1) | getCarryBit()) & 0xff;
- setCarryFlag((m & 0x80) != 0);
- return result;
- }
-
- /**
- * Rotates the given value right by one bit, setting bit 7 to the value
- * of the carry flag, and setting the carry flag to the original value
- * of bit 1.
- */
- private int ror(int m) {
- int result = ((m >>> 1) | (getCarryBit() << 7)) & 0xff;
- setCarryFlag((m & 0x01) != 0);
- return result;
- }
-
- /**
- * @return the negative flag
- */
- public boolean getNegativeFlag() {
- return negativeFlag;
- }
-
- /**
- * @return 1 if the negative flag is set, 0 if it is clear.
- */
- public int getNegativeBit() {
- return (negativeFlag ? 1 : 0);
- }
-
- /**
- * @param register the register value to test for negativity
- */
- public void setNegativeFlag(int register) {
- negativeFlag = (register < 0);
- }
-
- /**
- * @param negativeFlag the negative flag to set
- */
- public void setNegativeFlag(boolean negativeFlag) {
- this.negativeFlag = negativeFlag;
- }
-
- public void setNegativeFlag() {
- this.negativeFlag = true;
- }
-
- public void clearNegativeFlag() {
- this.negativeFlag = false;
- }
-
- /**
- * @return the carry flag
- */
- public boolean getCarryFlag() {
- return carryFlag;
- }
-
- /**
- * @return 1 if the carry flag is set, 0 if it is clear.
- */
- public int getCarryBit() {
- return (carryFlag ? 1 : 0);
- }
-
- /**
- * @param carryFlag the carry flag to set
- */
- public void setCarryFlag(boolean carryFlag) {
- this.carryFlag = carryFlag;
- }
-
- /**
- * Sets the Carry Flag
- */
- public void setCarryFlag() {
- this.carryFlag = true;
- }
-
- /**
- * Clears the Carry Flag
- */
- public void clearCarryFlag() {
- this.carryFlag = false;
- }
-
- /**
- * @return the zero flag
- */
- public boolean getZeroFlag() {
- return zeroFlag;
- }
-
- /**
- * @return 1 if the zero flag is set, 0 if it is clear.
- */
- public int getZeroBit() {
- return (zeroFlag ? 1 : 0);
- }
-
- /**
- * @param zeroFlag the zero flag to set
- */
- public void setZeroFlag(boolean zeroFlag) {
- this.zeroFlag = zeroFlag;
- }
-
- /**
- * Sets the Zero Flag
- */
- public void setZeroFlag() {
- this.zeroFlag = true;
- }
-
- /**
- * Clears the Zero Flag
- */
- public void clearZeroFlag() {
- this.zeroFlag = false;
- }
-
- /**
- * @return the irq disable flag
- */
- public boolean getIrqDisableFlag() {
- return irqDisableFlag;
- }
-
- /**
- * @return 1 if the interrupt disable flag is set, 0 if it is clear.
- */
- public int getIrqDisableBit() {
- return (irqDisableFlag ? 1 : 0);
- }
-
- /**
- * @param irqDisableFlag the irq disable flag to set
- */
- public void setIrqDisableFlag(boolean irqDisableFlag) {
- this.irqDisableFlag = irqDisableFlag;
- }
-
- public void setIrqDisableFlag() {
- this.irqDisableFlag = true;
- }
-
- public void clearIrqDisableFlag() {
- this.irqDisableFlag = false;
- }
-
-
- /**
- * @return the decimal mode flag
- */
- public boolean getDecimalModeFlag() {
- return decimalModeFlag;
- }
-
- /**
- * @return 1 if the decimal mode flag is set, 0 if it is clear.
- */
- public int getDecimalModeBit() {
- return (decimalModeFlag ? 1 : 0);
- }
-
- /**
- * @param decimalModeFlag the decimal mode flag to set
- */
- public void setDecimalModeFlag(boolean decimalModeFlag) {
- this.decimalModeFlag = decimalModeFlag;
- }
-
- /**
- * Sets the Decimal Mode Flag to true.
- */
- public void setDecimalModeFlag() {
- this.decimalModeFlag = true;
- }
-
- /**
- * Clears the Decimal Mode Flag.
- */
- public void clearDecimalModeFlag() {
- this.decimalModeFlag = false;
- }
-
- /**
- * @return the break flag
- */
- public boolean getBreakFlag() {
- return breakFlag;
- }
-
- /**
- * @return 1 if the break flag is set, 0 if it is clear.
- */
- public int getBreakBit() {
- return (carryFlag ? 1 : 0);
- }
-
- /**
- * @param breakFlag the break flag to set
- */
- public void setBreakFlag(boolean breakFlag) {
- this.breakFlag = breakFlag;
- }
-
- /**
- * Sets the Break Flag
- */
- public void setBreakFlag() {
- this.breakFlag = true;
- }
-
- /**
- * Clears the Break Flag
- */
- public void clearBreakFlag() {
- this.breakFlag = false;
- }
-
- /**
- * @return the overflow flag
- */
- public boolean getOverflowFlag() {
- return overflowFlag;
- }
-
- /**
- * @return 1 if the overflow flag is set, 0 if it is clear.
- */
- public int getOverflowBit() {
- return (overflowFlag ? 1 : 0);
- }
-
- /**
- * @param overflowFlag the overflow flag to set
- */
- public void setOverflowFlag(boolean overflowFlag) {
- this.overflowFlag = overflowFlag;
- }
-
- /**
- * Sets the Overflow Flag
- */
- public void setOverflowFlag() {
- this.overflowFlag = true;
- }
-
- /**
- * Clears the Overflow Flag
- */
- public void clearOverflowFlag() {
- this.overflowFlag = false;
- }
-
- /**
- * Set the illegal instruction trap.
- */
- public void setOpTrap() {
- this.opTrap = true;
- }
-
- /**
- * Clear the illegal instruction trap.
- */
- public void clearOpTrap() {
- this.opTrap = false;
- }
-
- /**
- * Get the status of the illegal instruction trap.
- */
- public boolean getOpTrap() {
- return this.opTrap;
- }
-
- public int getAccumulator() {
- return a;
- }
-
- public void setAccumulator(int val) {
- this.a = val;
- }
-
- public int getXRegister() {
- return x;
- }
-
- public void setXRegister(int val) {
- this.x = val;
- }
-
- public int getYRegister() {
- return y;
- }
-
- public void setYRegister(int val) {
- this.y = val;
- }
-
- public int getProgramCounter() {
- return pc;
- }
-
- public void setProgramCounter(int addr) {
- this.pc = addr;
- }
-
- public int getStackPointer() {
- return sp;
- }
-
- public void setStackPointer(int offset) {
- this.sp = offset;
- }
-
- /**
- * @value The value of the Process Status Register bits to be set.
- */
- public void setProcessorStatus(int value) {
- if ((value&P_CARRY) != 0)
- setCarryFlag();
- else
- clearCarryFlag();
-
- if ((value&P_ZERO) != 0)
- setZeroFlag();
- else
- clearZeroFlag();
-
- if ((value&P_IRQ_DISABLE) != 0)
- setIrqDisableFlag();
- else
- clearIrqDisableFlag();
-
- if ((value&P_DECIMAL) != 0)
- setDecimalModeFlag();
- else
- clearDecimalModeFlag();
-
- if ((value&P_BREAK) != 0)
- setBreakFlag();
- else
- clearBreakFlag();
-
- if ((value&P_OVERFLOW) != 0)
- setOverflowFlag();
- else
- clearOverflowFlag();
-
- if ((value&P_NEGATIVE) != 0)
- setNegativeFlag();
- else
- clearNegativeFlag();
- }
-
- /**
- * @returns The value of the Process Status Register, as a byte.
- */
- public int getProcessorStatus() {
- int status = 0x20;
- if (getCarryFlag()) { status |= P_CARRY; }
- if (getZeroFlag()) { status |= P_ZERO; }
- if (getIrqDisableFlag()) { status |= P_IRQ_DISABLE; }
- if (getDecimalModeFlag()) { status |= P_DECIMAL; }
- if (getBreakFlag()) { status |= P_BREAK; }
- if (getOverflowFlag()) { status |= P_OVERFLOW; }
- if (getNegativeFlag()) { status |= P_NEGATIVE; }
- return status;
- }
-
- /**
- * @return A string representing the current status register state.
- */
- public String statusRegisterString() {
- StringBuffer sb = new StringBuffer("[");
- sb.append(getNegativeFlag() ? 'N' : '.'); // Bit 7
- sb.append(getOverflowFlag() ? 'V' : '.'); // Bit 6
- sb.append("-"); // Bit 5 (always 1)
- sb.append(getBreakFlag() ? 'B' : '.'); // Bit 4
- sb.append(getDecimalModeFlag() ? 'D' : '.'); // Bit 3
- sb.append(getIrqDisableFlag() ? 'I' : '.'); // Bit 2
- sb.append(getZeroFlag() ? 'Z' : '.'); // Bit 1
- sb.append(getCarryFlag() ? 'C' : '.'); // Bit 0
- sb.append("]");
- return sb.toString();
- }
-
- /**
- * Returns a string representing the CPU state.
- */
- public String toString() {
- String opcode = opcode(ir, args[0], args[1]);
- StringBuffer sb = new StringBuffer(String.format("$%04X", addr) +
- " ");
- sb.append(String.format("%-14s", opcode));
- sb.append("A=" + String.format("$%02X", a) + " ");
- sb.append("X=" + String.format("$%02X", x) + " ");
- sb.append("Y=" + String.format("$%02X", y) + " ");
- sb.append("PC=" + String.format("$%04X", pc)+ " ");
- sb.append("P=" + statusRegisterString());
- return sb.toString();
- }
-
- /**
- * Push an item onto the stack, and decrement the stack counter.
- * Silently fails to push onto the stack if SP is
- */
- void stackPush(int data) throws MemoryAccessException {
- bus.write(0x100+sp, data);
-
- if (sp == 0)
- sp = 0xff;
- else
- --sp;
- }
-
-
- /**
- * Pre-increment the stack pointer, and return the top of the stack.
- */
- int stackPop() throws MemoryAccessException {
- if (sp == 0xff)
- sp = 0x00;
- else
- ++sp;
-
- int data = bus.read(0x100+sp);
-
- return data;
- }
-
- /**
- * Peek at the value currently at the top of the stack
- */
- int stackPeek() throws MemoryAccessException {
- return bus.read(0x100+sp+1);
- }
-
- /*
- * Increment the PC, rolling over if necessary.
- */
- void incrementPC() {
- if (pc == 0xffff) {
- pc = 0;
- } else {
- ++pc;
- }
- }
-
- /**
- * Given two bytes, return an address.
- */
- int address(int lowByte, int hiByte) {
- return ((hiByte<<8)|lowByte) & 0xffff;
- }
-
- /**
- * Given a hi byte and a low byte, return the Absolute,X
- * offset address.
- */
- int xAddress(int lowByte, int hiByte) {
- return (address(lowByte, hiByte)+getXRegister()) & 0xffff;
- }
-
- /**
- * Given a hi byte and a low byte, return the Absolute,Y
- * offset address.
- */
- int yAddress(int lowByte, int hiByte) {
- return (address(lowByte, hiByte)+getYRegister()) & 0xffff;
- }
-
- /**
- * Given a single byte, compute the Zero Page,X offset address.
- */
- int zpxAddress(int zp) {
- return (zp+getXRegister())&0xff;
- }
-
- /**
- * Given a single byte, compute the offset address.
- */
- int relAddress(int offset) {
- // Cast the offset to a signed byte to handle negative offsets
- return (pc + (byte)offset) & 0xffff;
- }
-
- /**
- * Given a single byte, compute the Zero Page,Y offset address.
- */
- int zpyAddress(int zp) {
- return (zp+getYRegister())&0xff;
- }
-
- void setResetVector(int address) throws MemoryAccessException {
- bus.write(RST_VECTOR_H, (address&0xff00)>>>8);
- bus.write(RST_VECTOR_L, address&0x00ff);
- }
-
- /**
- * Given an opcode and its operands, return a formatted name.
- *
- * @param opcode
- * @param operands
- * @return
- */
- String opcode(int opcode, int op1, int op2) {
- String opcodeName = Cpu.opcodeNames[opcode];
- if (opcodeName == null) { return "???"; }
-
- StringBuffer sb = new StringBuffer(opcodeName);
-
- switch (Cpu.instructionModes[opcode]) {
- case ABS:
- sb.append(String.format(" $%04X", address(op1, op2)));
- break;
- case ABX:
- sb.append(String.format(" $%04X,X", address(op1, op2)));
- break;
- case ABY:
- sb.append(String.format(" $%04X,Y", address(op1, op2)));
- break;
- case IMM:
- sb.append(String.format(" #$%02X", op1));
- break;
- case IND:
- sb.append(String.format(" ($%04X)", address(op1, op2)));
- break;
- case XIN:
- sb.append(String.format(" ($%02X),X", op1));
- break;
- case INY:
- sb.append(String.format(" ($%02X,Y)", op1));
- break;
- case REL:
- case ZPG:
- sb.append(String.format(" $%02X", op1));
- break;
- case ZPX:
- sb.append(String.format(" $%02X,X", op1));
- break;
- case ZPY:
- sb.append(String.format(" $%02X,Y", op1));
- break;
+ /**
+ * Add with Carry, used by all addressing mode implementations of ADC.
+ * As a side effect, this will set the overflow and carry flags as
+ * needed.
+ *
+ * @param acc The current value of the accumulator
+ * @param operand The operand
+ * @return
+ */
+ public int adc(int acc, int operand) {
+ int result = (operand & 0xff) + (acc & 0xff) + getCarryBit();
+ int carry6 = (operand & 0x7f) + (acc & 0x7f) + getCarryBit();
+ setCarryFlag((result & 0x100) != 0);
+ setOverflowFlag(carryFlag ^ ((carry6 & 0x80) != 0));
+ result &= 0xff;
+ setArithmeticFlags(result);
+ return result;
}
- return sb.toString();
- }
+ /**
+ * Add with Carry (BCD).
+ */
+
+ public int adcDecimal(int acc, int operand) {
+ int l, h, result;
+ l = (acc & 0x0f) + (operand & 0x0f) + getCarryBit();
+ if ((l & 0xff) > 9) l += 6;
+ h = (acc >> 4) + (operand >> 4) + (l > 15 ? 1 : 0);
+ if ((h & 0xff) > 9) h += 6;
+ result = (l & 0x0f) | (h << 4);
+ result &= 0xff;
+ setCarryFlag(h > 15);
+ setZeroFlag(result == 0);
+ setNegativeFlag(false); // BCD is never negative
+ setOverflowFlag(false); // BCD never sets overflow flag
+ return result;
+ }
+
+ /**
+ * Common code for Subtract with Carry. Just calls ADC of the
+ * one's complement of the operand. This lets the N, V, C, and Z
+ * flags work out nicely without any additional logic.
+ *
+ * @param acc
+ * @param operand
+ * @return
+ */
+ public int sbc(int acc, int operand) {
+ int result;
+ result = adc(acc, ~operand);
+ setArithmeticFlags(result);
+ return result;
+ }
+
+ /**
+ * Subtract with Carry, BCD mode.
+ *
+ * @param acc
+ * @param operand
+ * @return
+ */
+ public int sbcDecimal(int acc, int operand) {
+ int l, h, result;
+ l = (acc & 0x0f) - (operand & 0x0f) - (carryFlag ? 0 : 1);
+ if ((l & 0x10) != 0) l -= 6;
+ h = (acc >> 4) - (operand >> 4) - ((l & 0x10) != 0 ? 1 : 0);
+ if ((h & 0x10) != 0) h -= 6;
+ result = (l & 0x0f) | (h << 4);
+ setCarryFlag((h & 0xff) < 15);
+ setZeroFlag(result == 0);
+ setNegativeFlag(false); // BCD is never negative
+ setOverflowFlag(false); // BCD never sets overflow flag
+ return (result & 0xff);
+ }
+
+ /**
+ * Compare two values, and set carry, zero, and negative flags
+ * appropriately.
+ *
+ * @param reg
+ * @param operand
+ */
+ public void cmp(int reg, int operand) {
+ setCarryFlag(reg >= operand);
+ setZeroFlag(reg == operand);
+ setNegativeFlag((reg - operand) > 0);
+ }
+
+ /**
+ * Set the Negative and Zero flags based on the current value of the
+ * register operand.
+ *
+ * @param reg The register.
+ */
+ public void setArithmeticFlags(int reg) {
+ zeroFlag = (reg == 0);
+ negativeFlag = (reg & 0x80) != 0;
+ }
+
+ /**
+ * Shifts the given value left by one bit, and sets the carry
+ * flag to the high bit of the initial value.
+ *
+ * @param m The value to shift left.
+ * @return the left shifted value (m * 2).
+ */
+ private int asl(int m) {
+ setCarryFlag((m & 0x80) != 0);
+ return (m << 1) & 0xff;
+ }
+
+ /**
+ * Shifts the given value right by one bit, filling with zeros,
+ * and sets the carry flag to the low bit of the initial value.
+ */
+ private int lsr(int m) {
+ setCarryFlag((m & 0x01) != 0);
+ return (m >>> 1) & 0xff;
+ }
+
+ /**
+ * Rotates the given value left by one bit, setting bit 0 to the value
+ * of the carry flag, and setting the carry flag to the original value
+ * of bit 7.
+ */
+ private int rol(int m) {
+ int result = ((m << 1) | getCarryBit()) & 0xff;
+ setCarryFlag((m & 0x80) != 0);
+ return result;
+ }
+
+ /**
+ * Rotates the given value right by one bit, setting bit 7 to the value
+ * of the carry flag, and setting the carry flag to the original value
+ * of bit 1.
+ */
+ private int ror(int m) {
+ int result = ((m >>> 1) | (getCarryBit() << 7)) & 0xff;
+ setCarryFlag((m & 0x01) != 0);
+ return result;
+ }
+
+ /**
+ * @return the negative flag
+ */
+ public boolean getNegativeFlag() {
+ return negativeFlag;
+ }
+
+ /**
+ * @return 1 if the negative flag is set, 0 if it is clear.
+ */
+ public int getNegativeBit() {
+ return (negativeFlag ? 1 : 0);
+ }
+
+ /**
+ * @param register the register value to test for negativity
+ */
+ public void setNegativeFlag(int register) {
+ negativeFlag = (register < 0);
+ }
+
+ /**
+ * @param negativeFlag the negative flag to set
+ */
+ public void setNegativeFlag(boolean negativeFlag) {
+ this.negativeFlag = negativeFlag;
+ }
+
+ public void setNegativeFlag() {
+ this.negativeFlag = true;
+ }
+
+ public void clearNegativeFlag() {
+ this.negativeFlag = false;
+ }
+
+ /**
+ * @return the carry flag
+ */
+ public boolean getCarryFlag() {
+ return carryFlag;
+ }
+
+ /**
+ * @return 1 if the carry flag is set, 0 if it is clear.
+ */
+ public int getCarryBit() {
+ return (carryFlag ? 1 : 0);
+ }
+
+ /**
+ * @param carryFlag the carry flag to set
+ */
+ public void setCarryFlag(boolean carryFlag) {
+ this.carryFlag = carryFlag;
+ }
+
+ /**
+ * Sets the Carry Flag
+ */
+ public void setCarryFlag() {
+ this.carryFlag = true;
+ }
+
+ /**
+ * Clears the Carry Flag
+ */
+ public void clearCarryFlag() {
+ this.carryFlag = false;
+ }
+
+ /**
+ * @return the zero flag
+ */
+ public boolean getZeroFlag() {
+ return zeroFlag;
+ }
+
+ /**
+ * @return 1 if the zero flag is set, 0 if it is clear.
+ */
+ public int getZeroBit() {
+ return (zeroFlag ? 1 : 0);
+ }
+
+ /**
+ * @param zeroFlag the zero flag to set
+ */
+ public void setZeroFlag(boolean zeroFlag) {
+ this.zeroFlag = zeroFlag;
+ }
+
+ /**
+ * Sets the Zero Flag
+ */
+ public void setZeroFlag() {
+ this.zeroFlag = true;
+ }
+
+ /**
+ * Clears the Zero Flag
+ */
+ public void clearZeroFlag() {
+ this.zeroFlag = false;
+ }
+
+ /**
+ * @return the irq disable flag
+ */
+ public boolean getIrqDisableFlag() {
+ return irqDisableFlag;
+ }
+
+ /**
+ * @return 1 if the interrupt disable flag is set, 0 if it is clear.
+ */
+ public int getIrqDisableBit() {
+ return (irqDisableFlag ? 1 : 0);
+ }
+
+ /**
+ * @param irqDisableFlag the irq disable flag to set
+ */
+ public void setIrqDisableFlag(boolean irqDisableFlag) {
+ this.irqDisableFlag = irqDisableFlag;
+ }
+
+ public void setIrqDisableFlag() {
+ this.irqDisableFlag = true;
+ }
+
+ public void clearIrqDisableFlag() {
+ this.irqDisableFlag = false;
+ }
+
+
+ /**
+ * @return the decimal mode flag
+ */
+ public boolean getDecimalModeFlag() {
+ return decimalModeFlag;
+ }
+
+ /**
+ * @return 1 if the decimal mode flag is set, 0 if it is clear.
+ */
+ public int getDecimalModeBit() {
+ return (decimalModeFlag ? 1 : 0);
+ }
+
+ /**
+ * @param decimalModeFlag the decimal mode flag to set
+ */
+ public void setDecimalModeFlag(boolean decimalModeFlag) {
+ this.decimalModeFlag = decimalModeFlag;
+ }
+
+ /**
+ * Sets the Decimal Mode Flag to true.
+ */
+ public void setDecimalModeFlag() {
+ this.decimalModeFlag = true;
+ }
+
+ /**
+ * Clears the Decimal Mode Flag.
+ */
+ public void clearDecimalModeFlag() {
+ this.decimalModeFlag = false;
+ }
+
+ /**
+ * @return the break flag
+ */
+ public boolean getBreakFlag() {
+ return breakFlag;
+ }
+
+ /**
+ * @return 1 if the break flag is set, 0 if it is clear.
+ */
+ public int getBreakBit() {
+ return (carryFlag ? 1 : 0);
+ }
+
+ /**
+ * @param breakFlag the break flag to set
+ */
+ public void setBreakFlag(boolean breakFlag) {
+ this.breakFlag = breakFlag;
+ }
+
+ /**
+ * Sets the Break Flag
+ */
+ public void setBreakFlag() {
+ this.breakFlag = true;
+ }
+
+ /**
+ * Clears the Break Flag
+ */
+ public void clearBreakFlag() {
+ this.breakFlag = false;
+ }
+
+ /**
+ * @return the overflow flag
+ */
+ public boolean getOverflowFlag() {
+ return overflowFlag;
+ }
+
+ /**
+ * @return 1 if the overflow flag is set, 0 if it is clear.
+ */
+ public int getOverflowBit() {
+ return (overflowFlag ? 1 : 0);
+ }
+
+ /**
+ * @param overflowFlag the overflow flag to set
+ */
+ public void setOverflowFlag(boolean overflowFlag) {
+ this.overflowFlag = overflowFlag;
+ }
+
+ /**
+ * Sets the Overflow Flag
+ */
+ public void setOverflowFlag() {
+ this.overflowFlag = true;
+ }
+
+ /**
+ * Clears the Overflow Flag
+ */
+ public void clearOverflowFlag() {
+ this.overflowFlag = false;
+ }
+
+ /**
+ * Set the illegal instruction trap.
+ */
+ public void setOpTrap() {
+ this.opTrap = true;
+ }
+
+ /**
+ * Clear the illegal instruction trap.
+ */
+ public void clearOpTrap() {
+ this.opTrap = false;
+ }
+
+ /**
+ * Get the status of the illegal instruction trap.
+ */
+ public boolean getOpTrap() {
+ return this.opTrap;
+ }
+
+ public int getAccumulator() {
+ return a;
+ }
+
+ public void setAccumulator(int val) {
+ this.a = val;
+ }
+
+ public int getXRegister() {
+ return x;
+ }
+
+ public void setXRegister(int val) {
+ this.x = val;
+ }
+
+ public int getYRegister() {
+ return y;
+ }
+
+ public void setYRegister(int val) {
+ this.y = val;
+ }
+
+ public int getProgramCounter() {
+ return pc;
+ }
+
+ public void setProgramCounter(int addr) {
+ this.pc = addr;
+ }
+
+ public int getStackPointer() {
+ return sp;
+ }
+
+ public void setStackPointer(int offset) {
+ this.sp = offset;
+ }
+
+ public int getInstructionRegister() {
+ return this.ir;
+ }
+
+ public void setInstructionRegister(int op) {
+ this.ir = op;
+ }
+
+ public long getStepCounter() {
+ return stepCounter;
+ }
+
+ public void setStepCounter(long stepCount) {
+ this.stepCounter = stepCount;
+ }
+
+ /**
+ * @value The value of the Process Status Register bits to be set.
+ */
+ public void setProcessorStatus(int value) {
+ if ((value & P_CARRY) != 0)
+ setCarryFlag();
+ else
+ clearCarryFlag();
+
+ if ((value & P_ZERO) != 0)
+ setZeroFlag();
+ else
+ clearZeroFlag();
+
+ if ((value & P_IRQ_DISABLE) != 0)
+ setIrqDisableFlag();
+ else
+ clearIrqDisableFlag();
+
+ if ((value & P_DECIMAL) != 0)
+ setDecimalModeFlag();
+ else
+ clearDecimalModeFlag();
+
+ if ((value & P_BREAK) != 0)
+ setBreakFlag();
+ else
+ clearBreakFlag();
+
+ if ((value & P_OVERFLOW) != 0)
+ setOverflowFlag();
+ else
+ clearOverflowFlag();
+
+ if ((value & P_NEGATIVE) != 0)
+ setNegativeFlag();
+ else
+ clearNegativeFlag();
+ }
+
+ /**
+ * @returns The value of the Process Status Register, as a byte.
+ */
+ public int getProcessorStatus() {
+ int status = 0x20;
+ if (getCarryFlag()) {
+ status |= P_CARRY;
+ }
+ if (getZeroFlag()) {
+ status |= P_ZERO;
+ }
+ if (getIrqDisableFlag()) {
+ status |= P_IRQ_DISABLE;
+ }
+ if (getDecimalModeFlag()) {
+ status |= P_DECIMAL;
+ }
+ if (getBreakFlag()) {
+ status |= P_BREAK;
+ }
+ if (getOverflowFlag()) {
+ status |= P_OVERFLOW;
+ }
+ if (getNegativeFlag()) {
+ status |= P_NEGATIVE;
+ }
+ return status;
+ }
+
+ /**
+ * @return A string representing the current status register state.
+ */
+ public String getProcessorStatusString() {
+ StringBuffer sb = new StringBuffer("[");
+ sb.append(getNegativeFlag() ? 'N' : '.'); // Bit 7
+ sb.append(getOverflowFlag() ? 'V' : '.'); // Bit 6
+ sb.append("-"); // Bit 5 (always 1)
+ sb.append(getBreakFlag() ? 'B' : '.'); // Bit 4
+ sb.append(getDecimalModeFlag() ? 'D' : '.'); // Bit 3
+ sb.append(getIrqDisableFlag() ? 'I' : '.'); // Bit 2
+ sb.append(getZeroFlag() ? 'Z' : '.'); // Bit 1
+ sb.append(getCarryFlag() ? 'C' : '.'); // Bit 0
+ sb.append("]");
+ return sb.toString();
+ }
+
+ public String getOpcodeStatus() {
+ return opcode(ir, args[0], args[1]);
+ }
+
+ public String getAddressStatus() {
+ return String.format("$%04X", addr);
+ }
+
+ public String getARegisterStatus() {
+ return String.format("$%02X", a);
+ }
+
+ public String getXRegisterStatus() {
+ return String.format("$%02X", x);
+ }
+
+ public String getYRegisterStatus() {
+ return String.format("$%02X", y);
+ }
+
+ public String getProgramCounterStatus() {
+ return String.format("$%04X", pc);
+ }
+
+ /**
+ * Returns a string representing the CPU state.
+ */
+ public String toString() {
+ String opcode = opcode(ir, args[0], args[1]);
+ StringBuffer sb = new StringBuffer(String.format("$%04X", addr) +
+ " ");
+ sb.append(String.format("%-14s", opcode));
+ sb.append("A=" + String.format("$%02X", a) + " ");
+ sb.append("X=" + String.format("$%02X", x) + " ");
+ sb.append("Y=" + String.format("$%02X", y) + " ");
+ sb.append("PC=" + String.format("$%04X", pc) + " ");
+ sb.append("P=" + getProcessorStatusString());
+ return sb.toString();
+ }
+
+ /**
+ * Push an item onto the stack, and decrement the stack counter.
+ * Silently fails to push onto the stack if SP is
+ */
+ void stackPush(int data) throws MemoryAccessException {
+ bus.write(0x100 + sp, data);
+
+ if (sp == 0)
+ sp = 0xff;
+ else
+ --sp;
+ }
+
+
+ /**
+ * Pre-increment the stack pointer, and return the top of the stack.
+ */
+ int stackPop() throws MemoryAccessException {
+ if (sp == 0xff)
+ sp = 0x00;
+ else
+ ++sp;
+
+ return bus.read(0x100 + sp);
+ }
+
+ /**
+ * Peek at the value currently at the top of the stack
+ */
+ int stackPeek() throws MemoryAccessException {
+ return bus.read(0x100 + sp + 1);
+ }
+
+ /*
+ * Increment the PC, rolling over if necessary.
+ */
+ void incrementPC() {
+ if (pc == 0xffff) {
+ pc = 0;
+ } else {
+ ++pc;
+ }
+ }
+
+ /**
+ * Given two bytes, return an address.
+ */
+ int address(int lowByte, int hiByte) {
+ return ((hiByte << 8) | lowByte) & 0xffff;
+ }
+
+ /**
+ * Given a hi byte and a low byte, return the Absolute,X
+ * offset address.
+ */
+ int xAddress(int lowByte, int hiByte) {
+ return (address(lowByte, hiByte) + getXRegister()) & 0xffff;
+ }
+
+ /**
+ * Given a hi byte and a low byte, return the Absolute,Y
+ * offset address.
+ */
+ int yAddress(int lowByte, int hiByte) {
+ return (address(lowByte, hiByte) + getYRegister()) & 0xffff;
+ }
+
+ /**
+ * Given a single byte, compute the Zero Page,X offset address.
+ */
+ int zpxAddress(int zp) {
+ return (zp + getXRegister()) & 0xff;
+ }
+
+ /**
+ * Given a single byte, compute the offset address.
+ */
+ int relAddress(int offset) {
+ // Cast the offset to a signed byte to handle negative offsets
+ return (pc + (byte) offset) & 0xffff;
+ }
+
+ /**
+ * Given a single byte, compute the Zero Page,Y offset address.
+ */
+ int zpyAddress(int zp) {
+ return (zp + getYRegister()) & 0xff;
+ }
+
+ void setResetVector(int address) throws MemoryAccessException {
+ bus.write(RST_VECTOR_H, (address & 0xff00) >>> 8);
+ bus.write(RST_VECTOR_L, address & 0x00ff);
+ }
+
+ /**
+ * Given an opcode and its operands, return a formatted name.
+ *
+ * @param opcode The opcode
+ * @param op1 The first operand
+ * @param op2 The second operand
+ * @return
+ */
+ String opcode(int opcode, int op1, int op2) {
+ String opcodeName = Cpu.opcodeNames[opcode];
+ if (opcodeName == null) {
+ return "???";
+ }
+
+ StringBuffer sb = new StringBuffer(opcodeName);
+
+ switch (Cpu.instructionModes[opcode]) {
+ case ABS:
+ sb.append(String.format(" $%04X", address(op1, op2)));
+ break;
+ case ABX:
+ sb.append(String.format(" $%04X,X", address(op1, op2)));
+ break;
+ case ABY:
+ sb.append(String.format(" $%04X,Y", address(op1, op2)));
+ break;
+ case IMM:
+ sb.append(String.format(" #$%02X", op1));
+ break;
+ case IND:
+ sb.append(String.format(" ($%04X)", address(op1, op2)));
+ break;
+ case XIN:
+ sb.append(String.format(" ($%02X),X", op1));
+ break;
+ case INY:
+ sb.append(String.format(" ($%02X,Y)", op1));
+ break;
+ case REL:
+ case ZPG:
+ sb.append(String.format(" $%02X", op1));
+ break;
+ case ZPX:
+ sb.append(String.format(" $%02X,X", op1));
+ break;
+ case ZPY:
+ sb.append(String.format(" $%02X,Y", op1));
+ break;
+ }
+
+ return sb.toString();
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/loomcom/symon/MainWindow.java b/src/main/java/com/loomcom/symon/MainWindow.java
new file mode 100644
index 0000000..dc72578
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/MainWindow.java
@@ -0,0 +1,247 @@
+package com.loomcom.symon;
+
+import com.loomcom.symon.exceptions.MemoryAccessException;
+import com.loomcom.symon.exceptions.MemoryRangeException;
+import com.loomcom.symon.exceptions.SymonException;
+import com.loomcom.symon.ui.Console;
+import com.loomcom.symon.ui.StatusPane;
+import com.loomcom.symon.ui.UiUpdater;
+
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.*;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.swing.*;
+
+public class MainWindow extends JFrame implements ActionListener {
+
+ // Hard coded for now.
+ public static final int PROGRAM_START_ADDRESS = 0x0300;
+
+ private final static Logger logger = Logger.getLogger(MainWindow.class.getName());
+
+ private final Simulator simulator;
+ private Thread simulatorThread;
+ private final Console console;
+ private final StatusPane statusPane;
+ private final JButton loadButton;
+ private final JButton runButton;
+ private final JButton stepButton;
+ private final JButton resetButton;
+ private final JButton quitButton;
+ private final JFileChooser fileChooser;
+
+ private final UiUpdater uiUpdater;
+
+ public MainWindow(final Simulator simulator) {
+
+ this.setLayout(new BorderLayout());
+
+ this.simulator = simulator;
+
+ // UI components used for I/O.
+ this.console = new Console();
+ this.statusPane = new StatusPane(simulator.getCpu());
+
+ JPanel buttonContainer = new JPanel();
+ JPanel controlsContainer = new JPanel();
+
+ // File Chooser
+ fileChooser = new JFileChooser();
+
+ buttonContainer.setLayout(new FlowLayout());
+ controlsContainer.setLayout(new BorderLayout());
+
+ this.loadButton = new JButton("Load");
+ this.runButton = new JButton("Run");
+ this.stepButton = new JButton("Step");
+ this.resetButton = new JButton("Reset");
+ this.quitButton = new JButton("Quit");
+
+ buttonContainer.add(loadButton);
+ buttonContainer.add(runButton);
+ buttonContainer.add(stepButton);
+ buttonContainer.add(resetButton);
+ buttonContainer.add(quitButton);
+
+ controlsContainer.add(buttonContainer, BorderLayout.PAGE_START);
+ controlsContainer.add(statusPane, BorderLayout.PAGE_END);
+
+ console.setBorder(BorderFactory.createBevelBorder(1));
+ console.setBackground(Color.BLACK);
+ console.setForeground(Color.WHITE);
+ console.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 20));
+
+ getContentPane().add(console, BorderLayout.CENTER);
+ getContentPane().add(controlsContainer, BorderLayout.PAGE_END);
+
+ quitButton.addActionListener(this);
+ runButton.addActionListener(this);
+ stepButton.addActionListener(this);
+ resetButton.addActionListener(this);
+ loadButton.addActionListener(this);
+
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ console.requestFocus();
+
+ // Finally create the UI Updater and hand a reference to the Simulator.
+ this.uiUpdater = new UiUpdater(this);
+
+ // The simulator must always have a reference to the same UI updater.
+ this.simulator.setUiUpdater(uiUpdater);
+ }
+
+
+ public void actionPerformed(ActionEvent actionEvent) {
+ if (actionEvent.getSource() == loadButton) {
+ handleProgramLoad();
+ } else if (actionEvent.getSource() == resetButton) {
+ handleReset();
+ } else if (actionEvent.getSource() == stepButton) {
+ handleStep();
+ } else if (actionEvent.getSource() == quitButton) {
+ handleQuit();
+ } else if (actionEvent.getSource() == runButton) {
+ if (simulator.isRunning()) {
+ stop();
+ } else {
+ start();
+ }
+ }
+ }
+
+ private void handleProgramLoad() {
+ try {
+ int retVal = fileChooser.showOpenDialog(MainWindow.this);
+ if (retVal == JFileChooser.APPROVE_OPTION) {
+ File f = fileChooser.getSelectedFile();
+ if (f.canRead()) {
+ long fileSize = f.length();
+
+ if (fileSize > simulator.memorySize()) {
+ throw new IOException("Program will not fit in available memory.");
+ } else {
+ byte[] program = new byte[(int) fileSize];
+ int i = 0;
+ FileInputStream fis = new FileInputStream(f);
+ BufferedInputStream bis = new BufferedInputStream(fis);
+ DataInputStream dis = new DataInputStream(bis);
+ while (dis.available() != 0) {
+ program[i++] = dis.readByte();
+ }
+
+ // Now load the program at the starting address.
+ simulator.loadProgram(program, PROGRAM_START_ADDRESS);
+ // Reset (but don't clear memory, naturally)
+ simulator.reset();
+ // Update status and clear the screen
+ console.reset();
+ uiUpdater.updateUi();
+ }
+ }
+ }
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, "Unable to read file: " + ex.getMessage());
+ ex.printStackTrace();
+ } catch (MemoryAccessException ex) {
+ logger.log(Level.SEVERE, "Memory access error loading program");
+ ex.printStackTrace();
+ }
+ }
+
+ private void handleReset() {
+ if (simulator.isRunning()) {
+ stop();
+ }
+
+ try {
+ logger.log(Level.INFO, "Reset requested. Resetting CPU and clearing memory.");
+ // Reset and clear memory
+ simulator.reset();
+ simulator.clearMemory();
+ // Clear the console.
+ console.reset();
+ // Update status.
+ uiUpdater.updateUi();
+ } catch (MemoryAccessException ex) {
+ logger.log(Level.SEVERE, "Exception during simulator reset: " + ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+
+ private void handleStep() {
+ try {
+ simulator.step();
+ // The simulator is lazy about updating the UI for
+ // performance reasons, so always request an immediate update after stepping manually.
+ uiUpdater.updateUi();
+ } catch (SymonException ex) {
+ logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage());
+ ex.printStackTrace();
+ }
+
+ }
+
+ private void handleQuit() {
+ // TODO: Clean up and exit properly
+ System.exit(0);
+ }
+
+ private void stop() {
+ // Allow step while the simulator is stopped
+ simulator.requestStop();
+ if (simulatorThread != null) {
+ simulatorThread.interrupt();
+ simulatorThread = null;
+ }
+ }
+
+ private void start() {
+ simulatorThread = new Thread(simulator);
+ simulatorThread.start();
+ }
+
+ public Console getConsole() {
+ return this.console;
+ }
+
+ public StatusPane getStatusPane() {
+ return statusPane;
+ }
+
+ public JButton getRunButton() {
+ return runButton;
+ }
+
+ public JButton getLoadButton() {
+ return loadButton;
+ }
+
+ public JButton getStepButton() {
+ return stepButton;
+ }
+
+ public static void main(String args[]) {
+ try {
+ // Create the simulated system
+ Simulator simulator = new Simulator();
+
+ // Create the main UI window
+ MainWindow app = new MainWindow(simulator);
+
+ // Pack and display main window
+ app.pack();
+ app.setVisible(true);
+
+ // Reset the simulator.
+ simulator.reset();
+ } catch (MemoryAccessException e) {
+ e.printStackTrace();
+ } catch (MemoryRangeException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java
index 88145a7..d145822 100644
--- a/src/main/java/com/loomcom/symon/Simulator.java
+++ b/src/main/java/com/loomcom/symon/Simulator.java
@@ -1,490 +1,157 @@
package com.loomcom.symon;
-import java.io.*;
-import java.util.StringTokenizer;
+import java.util.logging.*;
import com.loomcom.symon.devices.*;
import com.loomcom.symon.exceptions.*;
+import com.loomcom.symon.ui.UiUpdater;
+
+import javax.swing.*;
/**
- * Main control class for the J6502 Simulator.
+ * Main entry point and control for the Symon Simulator.
+ * This class is responsible for creating the UI and starting
+ * the IO threads that pass data to and from the simulated
+ * Bus and Cpu.
*/
-public class Simulator {
+public class Simulator implements Runnable {
- /**
- * The CPU itself.
- */
- private Cpu cpu;
+ private final static Logger logger = Logger.getLogger(Simulator.class.getName());
- /**
- * The Bus responsible for routing memory read/write requests to the
- * correct IO devices.
- */
- private Bus bus;
+ private Bus bus;
+ private Cpu cpu;
+ private Acia acia;
+ private Memory ram;
+ private Memory rom;
+ private boolean isRunning = false;
- /**
- * The ACIA, used for charater in/out.
- *
- * By default, the simulator uses base address c000 for the ACIA.
- */
- private Acia acia;
+ private int updatesRequested = 0;
- private BufferedReader in;
- private BufferedWriter out;
+ private UiUpdater uiUpdater;
- /* If true, trace execution of the CPU */
- private boolean trace = false;
- private int nextExamineAddress = 0;
+ private static final int BUS_BOTTOM = 0x0000;
+ private static final int BUS_TOP = 0xffff;
- private static final int BUS_BOTTOM = 0x0000;
- private static final int BUS_TOP = 0xffff;
+ private static final int MEMORY_BASE = 0x0000;
+ private static final int MEMORY_SIZE = 0xc000; // 48 KB
- private static final int ACIA_BASE = 0xc000;
+ private static final int ROM_BASE = 0xe000;
+ private static final int ROM_SIZE = 0x2000; // 8 KB
- private static final int MEMORY_BASE = 0x0000;
- private static final int MEMORY_SIZE = 0xc000; // 48 KB
+ public static final int ACIA_BASE = 0xc000;
- private static final int ROM_BASE = 0xe000;
- private static final int ROM_SIZE = 0x2000; // 8 KB
+ private static final int MAX_REQUESTS_BETWEEN_UPDATES = 25000;
- public Simulator() throws MemoryRangeException {
- this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
- this.cpu = new Cpu();
- this.acia = new Acia(ACIA_BASE);
+ public Simulator() throws MemoryRangeException {
+ this.acia = new Acia(ACIA_BASE);
+ this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
+ this.cpu = new Cpu();
+ this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false);
+ // TODO: Load this ROM from a file, naturally!
+ this.rom = new Memory(ROM_BASE, ROM_SIZE, false);
+ bus.addCpu(cpu);
+ bus.addDevice(acia);
+ bus.addDevice(ram);
+ bus.addDevice(rom);
+ }
- bus.addCpu(cpu);
- bus.addDevice(new Memory(MEMORY_BASE, MEMORY_SIZE, false));
- bus.addDevice(acia);
- // TODO: This should be read-only memory. Add a method
- // to allow one-time initialization of ROM with a loaded
- // ROM binary file.
- bus.addDevice(new Memory(ROM_BASE, ROM_SIZE, false));
+ public void loadProgram(byte[] program, int startAddress) throws MemoryAccessException {
+ cpu.setResetVector(startAddress);
- this.in = new BufferedReader(new InputStreamReader(System.in));
- this.out = new BufferedWriter(new OutputStreamWriter(System.out));
- }
+ int addr = startAddress, i;
+ for (i = 0; i < program.length; i++) {
+ bus.write(addr++, program[i] & 0xff);
+ }
+
+ logger.log(Level.INFO, "Loaded " + i + " bytes at address 0x" +
+ Integer.toString(startAddress, 16));
+ }
+
+ public void setUiUpdater(UiUpdater uiUpdater) {
+ this.uiUpdater = uiUpdater;
+ }
+
+ public boolean isRunning() {
+ return isRunning;
+ }
+
+ public void requestStop() {
+ isRunning = false;
+ }
+
+ public void reset() throws MemoryAccessException {
+ cpu.reset();
+ }
+
+ public void clearMemory() {
+ ram.fill(0x00);
+ }
+
+ public int getProcessorStatus() {
+ return cpu.getProcessorStatus();
+ }
+
+ public Cpu getCpu() {
+ return this.cpu;
+ }
+
+ public long memorySize() {
+ return MEMORY_SIZE;
+ }
+
+ public void run() {
+ logger.log(Level.INFO, "Entering 'run' on main Simulator thread");
+ isRunning = true;
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ uiUpdater.simulatorDidStart();
+ }
+ });
- public void run() throws MemoryAccessException, FifoUnderrunException {
- try {
- greeting();
- prompt();
- String command = null;
- while (!shouldQuit(command = readLine())) {
try {
- dispatch(command);
- } catch (CommandFormatException ex) {
- writeLine(ex.getMessage());
- }
- prompt();
- }
- writeLine("\nGoodbye!");
- } catch (IOException ex) {
- System.err.println("Error: " + ex.toString());
- System.exit(1);
- }
- }
-
- /**
- * Dispatch the command.
- */
- public void dispatch(String commandLine) throws MemoryAccessException,
- IOException,
- CommandFormatException,
- FifoUnderrunException {
- Command c = new Command(commandLine);
- String cmd = c.getCommand();
- if (cmd != null) {
- if (cmd.startsWith("ste")) {
- doStep(c);
- } else if (cmd.startsWith("sta")) {
- doGetState();
- } else if (cmd.startsWith("se")) {
- doSet(c);
- } else if (cmd.startsWith("r")) {
- doReset();
- } else if (cmd.startsWith("e")) {
- doExamine(c);
- } else if (cmd.startsWith("d")) {
- doDeposit(c);
- } else if (cmd.startsWith("g")) {
- doGo(c);
- } else if (cmd.startsWith("h")) {
- doHelp(c);
- } else if (cmd.startsWith("t")) {
- doToggleTrace();
- } else if (cmd.startsWith("f")) {
- doFill(c);
- } else if (cmd.startsWith("l")) {
- doLoad(c);
- } else {
- writeLine("? Type h for help");
- }
- }
- }
-
- public void doHelp(Command c) throws IOException {
- writeLine("Symon 6502 Simulator");
- writeLine("");
- writeLine("All addresses must be in hexadecimal.");
- writeLine("Commands may be short or long (e.g. 'e' or 'ex' or 'examine').");
- writeLine("Note that 'go' clears the BREAK processor status flag.");
- writeLine("");
- writeLine("h Show this help file.");
- writeLine("e [start] [end] Examine memory at PC, start, or start-end.");
- writeLine("d Deposit data into address.");
- writeLine("f Fill memory with data.");
- writeLine("set {pc,a,x,y} [data] Set register to data value.");
- writeLine("load Load binary file at address.");
- writeLine("g [address] [steps] Start running at address, or at PC.");
- writeLine("step [address] Step once, optionally starting at address.");
- writeLine("stat Show CPU state.");
- writeLine("reset Reset simulator.");
- writeLine("trace Toggle trace.");
- writeLine("q (or Control-D) Quit.\n");
- }
-
- public void doGetState() throws IOException, MemoryAccessException {
- writeLine(cpu.toString());
- writeLine("Trace is " + (trace ? "on" : "off"));
- }
-
- public void doLoad(Command c) throws IOException,
- MemoryAccessException,
- CommandFormatException {
- if (c.numArgs() != 2) {
- throw new CommandFormatException("load ");
- }
-
- File binFile = new File(c.getArg(0));
- int address = stringToWord(c.getArg(1));
-
- if (!binFile.exists()) {
- throw new CommandFormatException("File '" + binFile +
- "' does not exist.");
- }
- writeLine("Loading file '" + binFile + "' at address " +
- String.format("%04x", address) + "...");
-
- int bytesLoaded = 0;
-
- FileInputStream fis = new FileInputStream(binFile);
-
- try {
- int b = 0;
- while ((b = fis.read()) > -1 && address <= bus.endAddress()) {
- bus.write(address++, b);
- bytesLoaded++;
- }
- } finally {
- fis.close();
- }
-
- writeLine("Loaded " + bytesLoaded + " (" +
- String.format("$%04x", bytesLoaded) + ") bytes");
- }
-
- public void doSet(Command c) throws MemoryAccessException,
- CommandFormatException {
- if (c.numArgs() != 2) {
- throw new CommandFormatException("set {a, x, y, pc} ");
- }
- try {
- String reg = c.getArg(0).toLowerCase();
- String data = c.getArg(1);
- if ("a".equals(reg)) {
- cpu.setAccumulator(stringToByte(data));
- } else if ("x".equals(reg)) {
- cpu.setXRegister(stringToByte(data));
- } else if ("y".equals(reg)) {
- cpu.setYRegister(stringToByte(data));
- } else if ("pc".equals(reg)) {
- cpu.setProgramCounter(stringToWord(data));
- } else {
- throw new CommandFormatException("set {a, x, y, pc} ");
- }
- } catch (NumberFormatException ex) {
- throw new CommandFormatException("Illegal address");
- }
- }
-
- public void doExamine(Command c) throws IOException,
- MemoryAccessException,
- CommandFormatException {
- try {
- if (c.numArgs() == 2) {
- int startAddress = stringToWord(c.getArgs()[0]);
- int endAddress = stringToWord(c.getArgs()[1]);
- while (startAddress < endAddress) {
- StringBuffer line = new StringBuffer();
- int numBytes = 0;
- line.append(String.format("%04x ", startAddress));
- while (numBytes++ < 16 && startAddress <= endAddress) {
- line.append(String.format("%02x ", bus.read(startAddress++)));
- if (numBytes % 8 == 0) {
- line.append(" ");
+ while (isRunning && !cpu.getBreakFlag()) {
+ step();
}
- }
- writeLine(line.toString());
+ } catch (SymonException ex) {
+ logger.log(Level.SEVERE, "Exception in main simulator run thread. Exiting run.");
+ ex.printStackTrace();
}
- nextExamineAddress = endAddress + 1;
- } else if (c.numArgs() == 1) {
- int address = stringToWord(c.getArgs()[0]);
- writeLine(String.format("%04x %02x", address, bus.read(address)));
- nextExamineAddress = address + 1;
- } else if (c.numArgs() == 0) {
- writeLine(String.format("%04x %02x", nextExamineAddress,
- bus.read(nextExamineAddress)));
- nextExamineAddress++;
- } else {
- throw new CommandFormatException("e [start [end]]");
- }
- } catch (NumberFormatException ex) {
- throw new CommandFormatException("Illegal Address");
+
+ logger.log(Level.INFO, "Exiting 'run'. BREAK=" + cpu.getBreakBit() + "; RUN_FLAG=" + isRunning);
+ isRunning = false;
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ uiUpdater.simulatorDidStop();
+ }
+ });
+
}
- }
- public void doDeposit(Command c) throws MemoryAccessException,
- CommandFormatException {
- if (c.numArgs() != 2) {
- throw new CommandFormatException("d [address] [data]");
- }
- try {
- int address = stringToWord(c.getArg(0));
- int data = stringToByte(c.getArg(1));
- bus.write(address, data);
- } catch (NumberFormatException ex) {
- throw new CommandFormatException("Illegal Address");
- }
- }
- public void doFill(Command c) throws MemoryAccessException,
- CommandFormatException {
- if (c.numArgs() != 3) {
- throw new CommandFormatException("f [start] [end] [data]");
- }
- try {
- int start = stringToWord(c.getArg(0));
- int end = stringToWord(c.getArg(1));
- int data = stringToByte(c.getArg(2));
- while (start <= end) {
- bus.write(start, data);
- start++;
- }
- } catch (NumberFormatException ex) {
- throw new CommandFormatException("Illegal Address");
- }
- }
+ public void step() throws MemoryAccessException, FifoUnderrunException {
- public void doStep(Command c) throws IOException,
- MemoryAccessException,
- FifoUnderrunException,
- CommandFormatException {
- try {
- if (c.numArgs() > 0) {
- cpu.setProgramCounter(stringToWord(c.getArg(1)));
- }
- cpu.step();
- writeLine(cpu.toString()); // Always show status after stepping
- } catch (NumberFormatException ex) {
- throw new CommandFormatException("Illegal Address");
- }
- }
-
- public void doGo(Command c) throws IOException,
- MemoryAccessException,
- FifoUnderrunException,
- CommandFormatException {
- int readChar;
- int stepCount = 0;
-
- if (c.numArgs() > 2) {
- throw new CommandFormatException("g [address] [steps]");
- }
- try {
- int start = 0;
- int steps = -1;
-
- if (c.numArgs() > 0) {
- start = stringToWord(c.getArg(0));
- } else {
- start = cpu.getProgramCounter();
- }
- if (c.numArgs() == 2) {
- steps = stringToWord(c.getArg(1));
- }
-
- // Make a gross assumption: Restarting the CPU clears
- // the break flag and the IRQ disable flag.
- cpu.clearBreakFlag();
- cpu.clearIrqDisableFlag();
-
- cpu.setProgramCounter(start);
- outer:
- while (!cpu.getBreakFlag() && (steps == -1 || steps-- > 0)) {
cpu.step();
- if (trace) {
- writeLine(cpu.toString());
+
+ // TODO: ACIA interrupt handling. For now, poll ACIA on each step.
+
+ // Read from the ACIA and add to the UiUpdater buffer
+ while (acia.hasTxChar()) {
+ uiUpdater.consoleWrite(acia.txRead());
}
- // Wake up and scan keyboard every 500 steps
- if (stepCount++ >= 500) {
- // Reset step count
- stepCount = 0;
-
- //
- // Do output if available.
- //
- while (acia.hasTxChar()) {
- out.write(acia.txRead());
- out.flush();
- }
-
- //
- // Consume input if available.
- //
- // NOTE: On UNIX systems, System.in.available() returns 0
- // until Enter is pressed. So to interrupt we must ALWAYS
- // type "^E". Sucks hard. But such is life.
- if (System.in.available() > 0) {
- while ((readChar = in.read()) > -1) {
- // Keep consuming unless ^E is found.
- //
- // TODO: This will probably lead to a lot of spurious keyboard
- // entry. Gotta keep an eye on that.
- //
- if (readChar == 0x05) {
- break outer;
- } else {
- // Buffer keyboard input into the simulated ACIA's
- // read buffer.
- acia.rxWrite(readChar);
- }
- }
- }
+ // This is a very expensive update, and we're doing it without
+ // a delay, so we don't want to overwhelm the Swing event processing thread
+ // with requests. Limit the number of ui updates that can be performed.
+ if (updatesRequested++ > MAX_REQUESTS_BETWEEN_UPDATES) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ uiUpdater.updateUi();
+ }
+ });
+ updatesRequested = 0;
}
- }
- if (!trace) {
- writeLine(cpu.toString());
- }
- } catch (NumberFormatException ex) {
- throw new CommandFormatException("Illegal Address");
}
- }
-
- public void doToggleTrace() throws IOException {
- this.trace = !trace;
- writeLine("Trace is now " + (trace ? "on" : "off"));
- }
-
- public void doReset() throws MemoryAccessException {
- cpu.reset();
- this.trace = false;
- }
-
- /**
- * Main simulator routine.
- */
- public static void main(String[] args) throws MemoryAccessException,
- FifoUnderrunException {
- try {
- new Simulator().run();
- } catch (MemoryRangeException ex) {
- System.err.println("Error: " + ex.toString());
- }
- }
-
- /*******************************************************************
- * Private
- *******************************************************************/
-
- public void load(int address, int[] data)
- throws MemoryAccessException {
- int i = 0;
- for (int d : data) {
- bus.write(address + i++, d);
- }
- }
-
- private int stringToWord(String addrString) {
- return Integer.parseInt(addrString, 16) & 0xffff;
- }
-
- private int stringToByte(String dataString) {
- return Integer.parseInt(dataString, 16) & 0xff;
- }
-
- private void greeting() throws IOException {
- writeLine("Welcome to the Symon 6502 Simulator. Type 'h' for help.");
- }
-
- private void prompt() throws IOException {
- out.write("symon> ");
- out.flush();
- }
-
- private String readLine() throws IOException {
- String line = in.readLine();
- if (line == null) { return null; }
- return line.trim();
- }
-
- private void writeLine(String line) throws IOException {
- out.write(line);
- out.newLine();
- out.flush();
- }
-
- /**
- * Returns true if the line is a quit.
- */
- private boolean shouldQuit(String line) {
- return (line == null || "q".equals(line.toLowerCase()));
- }
-
- /**
- * Command line tokenizer class. Given a command line, tokenize
- * it and give easy access to the command and its arguments.
- */
- public static final class Command {
- private String command;
- private String[] args;
-
- public Command(String commandLine) {
- StringTokenizer st = new StringTokenizer(commandLine);
- int numTokens = st.countTokens();
- int idx = 0;
- args = new String[numTokens > 1 ? numTokens - 1 : 0];
- while (st.hasMoreTokens()) {
- if (command == null) {
- command = st.nextToken();
- } else {
- args[idx++] = st.nextToken();
- }
- }
- }
-
- public String getCommand() {
- return command;
- }
-
- public String[] getArgs() {
- return args;
- }
-
- public String getArg(int argNum) {
- if (argNum > args.length - 1) {
- return null;
- } else {
- return args[argNum];
- }
- }
-
- public int numArgs() {
- return args.length;
- }
-
- public boolean hasArgs() {
- return args.length > 0;
- }
- }
}
+
diff --git a/src/main/java/com/loomcom/symon/devices/Memory.java b/src/main/java/com/loomcom/symon/devices/Memory.java
index 555a94a..e717dc1 100644
--- a/src/main/java/com/loomcom/symon/devices/Memory.java
+++ b/src/main/java/com/loomcom/symon/devices/Memory.java
@@ -6,38 +6,43 @@ import com.loomcom.symon.exceptions.*;
public class Memory extends Device {
- private boolean readOnly;
- private int[] mem;
+ private boolean readOnly;
+ private int[] mem;
- /* Initialize all locations to 0x00 (BRK) */
- private static final int DEFAULT_FILL = 0x00;
+ /* Initialize all locations to 0x00 (BRK) */
+ private static final int DEFAULT_FILL = 0x00;
- public Memory(int address, int size, boolean readOnly)
- throws MemoryRangeException {
- super(address, size, (readOnly ? "RO Memory" : "RW Memory"));
- this.readOnly = readOnly;
- this.mem = new int[size];
- Arrays.fill(this.mem, DEFAULT_FILL);
- }
-
- public Memory(int address, int size) throws MemoryRangeException {
- this(address, size, false);
- }
-
- public void write(int address, int data) throws MemoryAccessException {
- if (readOnly) {
- throw new MemoryAccessException("Cannot write to read-only memory at address " + address);
- } else {
- this.mem[address] = data;
+ public Memory(int address, int size, boolean readOnly)
+ throws MemoryRangeException {
+ super(address, size, (readOnly ? "RO Memory" : "RW Memory"));
+ this.readOnly = readOnly;
+ this.mem = new int[size];
+ this.fill(DEFAULT_FILL);
}
- }
- public int read(int address) throws MemoryAccessException {
- return this.mem[address];
- }
+ public Memory(int address, int size) throws MemoryRangeException {
+ this(address, size, false);
+ }
- public String toString() {
- return "Memory: " + getMemoryRange().toString();
- }
+ public void write(int address, int data) throws MemoryAccessException {
+ if (readOnly) {
+ throw new MemoryAccessException("Cannot write to read-only memory at address " + address);
+ } else {
+ this.mem[address] = data;
+ }
+ }
+
+ public int read(int address) throws MemoryAccessException {
+ return this.mem[address];
+ }
+
+ public void fill(int val) {
+ Arrays.fill(this.mem, val);
+ }
+
+
+ public String toString() {
+ return "Memory: " + getMemoryRange().toString();
+ }
}
\ No newline at end of file
diff --git a/src/main/java/com/loomcom/symon/exceptions/CommandFormatException.java b/src/main/java/com/loomcom/symon/exceptions/CommandFormatException.java
index 2208a6b..6dbb101 100644
--- a/src/main/java/com/loomcom/symon/exceptions/CommandFormatException.java
+++ b/src/main/java/com/loomcom/symon/exceptions/CommandFormatException.java
@@ -1,6 +1,6 @@
package com.loomcom.symon.exceptions;
-public class CommandFormatException extends Exception {
+public class CommandFormatException extends SymonException {
public CommandFormatException(String msg) {
super(msg);
}
diff --git a/src/main/java/com/loomcom/symon/exceptions/FifoUnderrunException.java b/src/main/java/com/loomcom/symon/exceptions/FifoUnderrunException.java
index 0ffb158..162c077 100644
--- a/src/main/java/com/loomcom/symon/exceptions/FifoUnderrunException.java
+++ b/src/main/java/com/loomcom/symon/exceptions/FifoUnderrunException.java
@@ -1,6 +1,6 @@
package com.loomcom.symon.exceptions;
-public class FifoUnderrunException extends Exception {
+public class FifoUnderrunException extends SymonException {
public FifoUnderrunException(String msg) {
super(msg);
}
diff --git a/src/main/java/com/loomcom/symon/exceptions/MemoryAccessException.java b/src/main/java/com/loomcom/symon/exceptions/MemoryAccessException.java
index 465136f..2f210eb 100644
--- a/src/main/java/com/loomcom/symon/exceptions/MemoryAccessException.java
+++ b/src/main/java/com/loomcom/symon/exceptions/MemoryAccessException.java
@@ -4,7 +4,7 @@ package com.loomcom.symon.exceptions;
* Exception that will be thrown if access to memory or IO cannot be
* accessed.
*/
-public class MemoryAccessException extends Exception {
+public class MemoryAccessException extends SymonException {
public MemoryAccessException(String msg) {
super(msg);
}
diff --git a/src/main/java/com/loomcom/symon/exceptions/MemoryRangeException.java b/src/main/java/com/loomcom/symon/exceptions/MemoryRangeException.java
index 02a109e..571c617 100644
--- a/src/main/java/com/loomcom/symon/exceptions/MemoryRangeException.java
+++ b/src/main/java/com/loomcom/symon/exceptions/MemoryRangeException.java
@@ -3,7 +3,7 @@ package com.loomcom.symon.exceptions;
/**
* Exception that will be thrown if devices conflict in the IO map.
*/
-public class MemoryRangeException extends Exception {
+public class MemoryRangeException extends SymonException {
public MemoryRangeException(String msg) {
super(msg);
}
diff --git a/src/main/java/com/loomcom/symon/exceptions/SymonException.java b/src/main/java/com/loomcom/symon/exceptions/SymonException.java
new file mode 100644
index 0000000..bff91a8
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/exceptions/SymonException.java
@@ -0,0 +1,14 @@
+package com.loomcom.symon.exceptions;
+
+
+/**
+ * Superclass for all symon Exceptions.
+ */
+public class SymonException extends Exception {
+ public SymonException(String msg) {
+ super(msg);
+ }
+ public SymonException() {
+ super();
+ }
+}
diff --git a/src/main/java/com/loomcom/symon/ui/Console.java b/src/main/java/com/loomcom/symon/ui/Console.java
new file mode 100644
index 0000000..a1870c5
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/ui/Console.java
@@ -0,0 +1,69 @@
+package com.loomcom.symon.ui;
+
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import com.grahamedgecombe.jterminal.JTerminal;
+
+/**
+ * The Console is a simulated 80 column x 24 row VT-100 terminal attached to
+ * the ACIA of the system. It provides basic keyboard I/O to Symon.
+ */
+
+public class Console extends JTerminal implements KeyListener, MouseListener {
+
+ // TODO: Pass in io threads, read and write to ACIA.
+ public Console() {
+ super();
+ addKeyListener(this);
+ addMouseListener(this);
+ }
+
+ public void reset() {
+ getModel().clear();
+ getModel().setCursorColumn(0);
+ getModel().setCursorRow(0);
+ repaint();
+ }
+
+ public void keyTyped(KeyEvent keyEvent) {
+ keyEvent.consume();
+ }
+
+ public void keyPressed(KeyEvent keyEvent) {
+ int keyCode = keyEvent.getKeyCode();
+ int modifiersMask = keyEvent.getModifiers();
+ int modifiersExMask = keyEvent.getModifiersEx();
+
+ System.out.println("Key Pressed #" + keyEvent.getKeyCode() + " : " +
+ KeyEvent.getKeyText(keyCode) + " MASK : " +
+ modifiersMask + " EXT MASK : " +
+ modifiersExMask
+ );
+
+ keyEvent.consume();
+ }
+
+ public void keyReleased(KeyEvent keyEvent) {
+ keyEvent.consume();
+ }
+
+ public void mouseClicked(MouseEvent mouseEvent) {
+ }
+
+ public void mousePressed(MouseEvent mouseEvent) {
+ requestFocus();
+ mouseEvent.consume();
+ }
+
+ public void mouseReleased(MouseEvent mouseEvent) {
+ }
+
+ public void mouseEntered(MouseEvent mouseEvent) {
+ }
+
+ public void mouseExited(MouseEvent mouseEvent) {
+ }
+}
diff --git a/src/main/java/com/loomcom/symon/ui/StatusPane.java b/src/main/java/com/loomcom/symon/ui/StatusPane.java
new file mode 100644
index 0000000..4616041
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/ui/StatusPane.java
@@ -0,0 +1,207 @@
+/**
+ *
+ */
+package com.loomcom.symon.ui;
+
+import com.loomcom.symon.Cpu;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class StatusPane extends JPanel {
+
+ // The CPU to ask for state information.
+ private final Cpu cpu;
+
+ private final ImageIcon carryOn;
+ private final ImageIcon carryOff;
+ private final ImageIcon zeroOn;
+ private final ImageIcon zeroOff;
+ private final ImageIcon irqOn;
+ private final ImageIcon irqOff;
+ private final ImageIcon decimalOn;
+ private final ImageIcon decimalOff;
+ private final ImageIcon breakOn;
+ private final ImageIcon breakOff;
+ private final ImageIcon overflowOn;
+ private final ImageIcon overflowOff;
+ private final ImageIcon negativeOn;
+ private final ImageIcon negativeOff;
+
+ private final JLabel carryFlagLabel;
+ private final JLabel zeroFlagLabel;
+ private final JLabel irqDisableFlagLabel;
+ private final JLabel decimalModeFlagLabel;
+ private final JLabel breakFlagLabel;
+ private final JLabel overflowFlagLabel;
+ private final JLabel negativeFlagLabel;
+
+ private final JLabel opcodeLabel;
+ private final JLabel pcLabel;
+ private final JLabel aLabel;
+ private final JLabel xLabel;
+ private final JLabel yLabel;
+ private final JLabel stepCountLabel;
+
+ private static final int WIDTH = 134;
+ private static final int HEIGHT = 27;
+
+ public StatusPane(Cpu cpu) {
+ super();
+
+ this.cpu = cpu;
+
+ Dimension dimensions = new Dimension(WIDTH, HEIGHT);
+
+ setMinimumSize(dimensions);
+ setPreferredSize(dimensions);
+ setMaximumSize(dimensions);
+
+ carryOn = new ImageIcon(this.getClass().getResource("images/C_on.png"));
+ carryOff = new ImageIcon(this.getClass().getResource("images/C_off.png"));
+ zeroOn = new ImageIcon(this.getClass().getResource("images/Z_on.png"));
+ zeroOff = new ImageIcon(this.getClass().getResource("images/Z_off.png"));
+ irqOn = new ImageIcon(this.getClass().getResource("images/I_on.png"));
+ irqOff = new ImageIcon(this.getClass().getResource("images/I_off.png"));
+ decimalOn = new ImageIcon(this.getClass().getResource("images/D_on.png"));
+ decimalOff = new ImageIcon(this.getClass().getResource("images/D_off.png"));
+ breakOn = new ImageIcon(this.getClass().getResource("images/B_on.png"));
+ breakOff = new ImageIcon(this.getClass().getResource("images/B_off.png"));
+ overflowOn = new ImageIcon(this.getClass().getResource("images/O_on.png"));
+ overflowOff = new ImageIcon(this.getClass().getResource("images/O_off.png"));
+ negativeOn = new ImageIcon(this.getClass().getResource("images/N_on.png"));
+ negativeOff = new ImageIcon(this.getClass().getResource("images/N_off.png"));
+
+ // Initialize all to off
+ carryFlagLabel = new JLabel(carryOff, JLabel.CENTER);
+ zeroFlagLabel = new JLabel(zeroOff, JLabel.CENTER);
+ irqDisableFlagLabel = new JLabel(irqOff, JLabel.CENTER);
+ decimalModeFlagLabel = new JLabel(decimalOff, JLabel.CENTER);
+ breakFlagLabel = new JLabel(breakOff, JLabel.CENTER);
+ overflowFlagLabel = new JLabel(overflowOff, JLabel.CENTER);
+ negativeFlagLabel = new JLabel(negativeOff, JLabel.CENTER);
+
+ // Create and add register and address labels
+
+ this.opcodeLabel = new JLabel();
+ this.pcLabel = new JLabel();
+ this.aLabel = new JLabel();
+ this.xLabel = new JLabel();
+ this.yLabel = new JLabel();
+ this.stepCountLabel = new JLabel();
+
+ this.opcodeLabel.setMinimumSize(new Dimension(100, 20));
+ this.pcLabel.setMinimumSize(new Dimension(80, 20));
+ this.aLabel.setMinimumSize(new Dimension(60, 20));
+ this.xLabel.setMinimumSize(new Dimension(60, 20));
+ this.yLabel.setMinimumSize(new Dimension(60, 20));
+ this.stepCountLabel.setMinimumSize(new Dimension(120, 20));
+
+ this.opcodeLabel.setPreferredSize(new Dimension(100, 20));
+ this.pcLabel.setPreferredSize(new Dimension(80, 20));
+ this.aLabel.setPreferredSize(new Dimension(60, 20));
+ this.xLabel.setPreferredSize(new Dimension(60, 20));
+ this.yLabel.setPreferredSize(new Dimension(60, 20));
+ this.stepCountLabel.setPreferredSize(new Dimension(120, 20));
+
+ this.setLayout(new FlowLayout());
+
+ this.add(negativeFlagLabel);
+ this.add(overflowFlagLabel);
+ this.add(breakFlagLabel);
+ this.add(decimalModeFlagLabel);
+ this.add(irqDisableFlagLabel);
+ this.add(zeroFlagLabel);
+ this.add(carryFlagLabel);
+
+ this.add(opcodeLabel);
+ this.add(pcLabel);
+ this.add(aLabel);
+ this.add(xLabel);
+ this.add(yLabel);
+ this.add(stepCountLabel);
+
+ updateState();
+ }
+
+ public void updateState() {
+ // Update the Processor Status Flag display
+ int state = this.cpu.getProcessorStatus();
+
+ carryFlagLabel.setIcon(iconForFlag(state, 0));
+ zeroFlagLabel.setIcon(iconForFlag(state, 1));
+ irqDisableFlagLabel.setIcon(iconForFlag(state, 2));
+ decimalModeFlagLabel.setIcon(iconForFlag(state, 3));
+ breakFlagLabel.setIcon(iconForFlag(state, 4));
+ overflowFlagLabel.setIcon(iconForFlag(state, 6));
+ negativeFlagLabel.setIcon(iconForFlag(state, 7));
+
+ // Update the register and address displays
+ opcodeLabel.setText(cpu.getOpcodeStatus());
+ pcLabel.setText(cpu.getProgramCounterStatus());
+ aLabel.setText(cpu.getARegisterStatus());
+ xLabel.setText(cpu.getXRegisterStatus());
+ yLabel.setText(cpu.getYRegisterStatus());
+ stepCountLabel.setText(Long.toString(cpu.getStepCounter()));
+
+ repaint();
+ }
+
+ private ImageIcon iconForFlag(int state, int flagIndex) {
+ ImageIcon imageIcon = null;
+
+ if ((((state & 0xff) >> flagIndex) & 0x01) == 1) {
+ switch (flagIndex) {
+ case 0:
+ imageIcon = carryOn;
+ break;
+ case 1:
+ imageIcon = zeroOn;
+ break;
+ case 2:
+ imageIcon = irqOn;
+ break;
+ case 3:
+ imageIcon = decimalOn;
+ break;
+ case 4:
+ imageIcon = breakOn;
+ break;
+ case 6:
+ imageIcon = overflowOn;
+ break;
+ case 7:
+ imageIcon = negativeOn;
+ break;
+ }
+ } else {
+ switch (flagIndex) {
+ case 0:
+ imageIcon = carryOff;
+ break;
+ case 1:
+ imageIcon = zeroOff;
+ break;
+ case 2:
+ imageIcon = irqOff;
+ break;
+ case 3:
+ imageIcon = decimalOff;
+ break;
+ case 4:
+ imageIcon = breakOff;
+ break;
+ case 6:
+ imageIcon = overflowOff;
+ break;
+ case 7:
+ imageIcon = negativeOff;
+ break;
+ }
+
+ }
+
+ return imageIcon;
+ }
+
+}
diff --git a/src/main/java/com/loomcom/symon/ui/UiUpdater.java b/src/main/java/com/loomcom/symon/ui/UiUpdater.java
new file mode 100644
index 0000000..93f66a2
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/ui/UiUpdater.java
@@ -0,0 +1,63 @@
+package com.loomcom.symon.ui;
+
+import com.loomcom.symon.MainWindow;
+
+/**
+ * Update the console and status display. All direct manipulation of the
+ * main UI should go through this class. When not called from the Swing
+ * event dispatch thread, these methods should be called using
+ * SwingUtilities.invokeLater.
+ */
+public class UiUpdater {
+ private final Console console;
+ private final MainWindow mainWindow;
+ private final StatusPane statusPane;
+
+ private final StringBuffer data;
+
+ public UiUpdater(MainWindow mainWindow) {
+ this.mainWindow = mainWindow;
+ this.console = mainWindow.getConsole();
+ this.statusPane = mainWindow.getStatusPane();
+ this.data = new StringBuffer();
+ }
+
+ public void consoleWrite(int i) {
+ data.append((char) i);
+ }
+
+ /**
+ * Callback called by the simulator before exiting its run method.
+ */
+ public void simulatorDidStop() {
+ mainWindow.getStepButton().setEnabled(true);
+ mainWindow.getLoadButton().setEnabled(true);
+ mainWindow.getRunButton().setText("Run");
+ updateUi();
+ }
+
+ /**
+ * Callback called by the simulator when entering its run method.
+ */
+ public void simulatorDidStart() {
+ // Don't allow step while the simulator is running
+ mainWindow.getStepButton().setEnabled(false);
+ mainWindow.getLoadButton().setEnabled(false);
+ // Toggle the state of the run button
+ mainWindow.getRunButton().setText("Stop");
+ updateUi();
+ }
+
+ public void updateUi() {
+ // Update the console with any text
+ if (data.length() > 0) {
+ console.getModel().print(data.toString());
+ console.repaint();
+ // Clear the buffer
+ data.delete(0, data.length());
+ }
+
+ // Update the status UI.
+ statusPane.updateState();
+ }
+}
diff --git a/src/main/java/com/loomcom/symon/ui/images/B_off.png b/src/main/java/com/loomcom/symon/ui/images/B_off.png
new file mode 100644
index 0000000..145264f
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/B_off.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/B_on.png b/src/main/java/com/loomcom/symon/ui/images/B_on.png
new file mode 100644
index 0000000..3e3da87
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/B_on.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/C_off.png b/src/main/java/com/loomcom/symon/ui/images/C_off.png
new file mode 100644
index 0000000..84986cf
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/C_off.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/C_on.png b/src/main/java/com/loomcom/symon/ui/images/C_on.png
new file mode 100644
index 0000000..8c57a38
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/C_on.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/D_off.png b/src/main/java/com/loomcom/symon/ui/images/D_off.png
new file mode 100644
index 0000000..39db75d
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/D_off.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/D_on.png b/src/main/java/com/loomcom/symon/ui/images/D_on.png
new file mode 100644
index 0000000..a9308be
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/D_on.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/I_off.png b/src/main/java/com/loomcom/symon/ui/images/I_off.png
new file mode 100644
index 0000000..288a487
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/I_off.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/I_on.png b/src/main/java/com/loomcom/symon/ui/images/I_on.png
new file mode 100644
index 0000000..650d081
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/I_on.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/N_off.png b/src/main/java/com/loomcom/symon/ui/images/N_off.png
new file mode 100644
index 0000000..6468771
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/N_off.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/N_on.png b/src/main/java/com/loomcom/symon/ui/images/N_on.png
new file mode 100644
index 0000000..140c2b6
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/N_on.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/O_off.png b/src/main/java/com/loomcom/symon/ui/images/O_off.png
new file mode 100644
index 0000000..51945fb
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/O_off.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/O_on.png b/src/main/java/com/loomcom/symon/ui/images/O_on.png
new file mode 100644
index 0000000..fb9bf8b
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/O_on.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/Z_off.png b/src/main/java/com/loomcom/symon/ui/images/Z_off.png
new file mode 100644
index 0000000..124dc55
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/Z_off.png differ
diff --git a/src/main/java/com/loomcom/symon/ui/images/Z_on.png b/src/main/java/com/loomcom/symon/ui/images/Z_on.png
new file mode 100644
index 0000000..65fe24b
Binary files /dev/null and b/src/main/java/com/loomcom/symon/ui/images/Z_on.png differ
diff --git a/src/test/java/com/loomcom/symon/CommandTest.java b/src/test/java/com/loomcom/symon/CommandTest.java
deleted file mode 100644
index e7b21b6..0000000
--- a/src/test/java/com/loomcom/symon/CommandTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.loomcom.symon;
-
-import org.junit.*;
-import static org.junit.Assert.*;
-
-public class CommandTest {
-
- @Test
- public void testCommandShouldParseCorrectNumberOfArguments() {
- Simulator.Command c;
-
- c = new Simulator.Command("foo");
- assertEquals("foo", c.getCommand());
- assertEquals(0, c.numArgs());
-
- c = new Simulator.Command("foo bar");
- assertEquals("foo", c.getCommand());
- assertEquals(1, c.numArgs());
- assertEquals("bar", c.getArgs()[0]);
-
- c = new Simulator.Command("foo bar baz quux 0 100");
- assertEquals("foo", c.getCommand());
- assertEquals(5, c.numArgs());
- assertEquals("bar", c.getArgs()[0]);
- assertEquals("baz", c.getArgs()[1]);
- assertEquals("quux", c.getArgs()[2]);
- assertEquals("0", c.getArgs()[3]);
- assertEquals("100", c.getArgs()[4]);
- }
-
- @Test
- public void testCommandShouldIgnoreWhitespaceBetweenTokens() {
- Simulator.Command c;
-
- c = new Simulator.Command("foo bar baz");
- assertEquals("foo", c.getCommand());
- assertEquals(2, c.numArgs());
- assertEquals("bar", c.getArgs()[0]);
- assertEquals("baz", c.getArgs()[1]);
- }
-
- @Test
- public void testCommandShouldIgnoreWhitespaceBeforeCommand() {
- Simulator.Command c;
-
- c = new Simulator.Command(" foo bar baz");
- assertEquals("foo", c.getCommand());
- assertEquals(2, c.numArgs());
- assertEquals("bar", c.getArgs()[0]);
- assertEquals("baz", c.getArgs()[1]);
- }
-
- @Test
- public void testCommandShouldIgnoreWhitespaceAfterCommand() {
- Simulator.Command c;
-
- c = new Simulator.Command("foo bar baz ");
- assertEquals("foo", c.getCommand());
- assertEquals(2, c.numArgs());
- assertEquals("bar", c.getArgs()[0]);
- assertEquals("baz", c.getArgs()[1]);
- }
-
-}
diff --git a/src/test/java/com/loomcom/symon/CpuTest.java b/src/test/java/com/loomcom/symon/CpuTest.java
index ea86bf8..7902c1b 100644
--- a/src/test/java/com/loomcom/symon/CpuTest.java
+++ b/src/test/java/com/loomcom/symon/CpuTest.java
@@ -10,387 +10,409 @@ import com.loomcom.symon.exceptions.*;
*/
public class CpuTest extends TestCase {
- private Cpu cpu;
- private Bus bus;
- private Memory mem;
+ private Cpu cpu;
+ private Bus bus;
+ private Memory mem;
- public CpuTest(String testName) {
- super(testName);
- }
-
- public static Test suite() {
- return new TestSuite(CpuTest.class);
- }
-
- public void setUp() throws MemoryRangeException, MemoryAccessException {
- this.cpu = new Cpu();
- this.bus = new Bus(0x0000, 0xffff);
- this.mem = new Memory(0x0000, 0x10000);
- bus.addCpu(cpu);
- bus.addDevice(mem);
-
- // All test programs start at 0x0200;
- bus.write(0xfffc, 0x00);
- bus.write(0xfffd, 0x02);
-
- cpu.reset();
- }
-
- public void testReset() {
- assertEquals(0, cpu.getAccumulator());
- assertEquals(0, cpu.getXRegister());
- assertEquals(0, cpu.getYRegister());
- assertEquals(0x0200, cpu.getProgramCounter());
- assertFalse(cpu.getCarryFlag());
- assertFalse(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertFalse(cpu.getOverflowFlag());
- assertFalse(cpu.getNegativeFlag());
- }
-
- public void testStack() throws MemoryAccessException {
-
- cpu.stackPush(0x13);
- assertEquals(0x13, cpu.stackPop());
-
- cpu.stackPush(0x12);
- assertEquals(0x12, cpu.stackPop());
-
- for (int i = 0x00; i <= 0xff; i++) {
- cpu.stackPush(i);
+ public CpuTest(String testName) {
+ super(testName);
}
- for (int i = 0xff; i >= 0x00; i--) {
- assertEquals(i, cpu.stackPop());
+ public static Test suite() {
+ return new TestSuite(CpuTest.class);
}
- }
+ public void setUp() throws MemoryRangeException, MemoryAccessException {
+ this.cpu = new Cpu();
+ this.bus = new Bus(0x0000, 0xffff);
+ this.mem = new Memory(0x0000, 0x10000);
+ bus.addCpu(cpu);
+ bus.addDevice(mem);
- public void testStackPush() throws MemoryAccessException {
- assertEquals(0xff, cpu.getStackPointer());
- assertEquals(0x00, bus.read(0x1ff));
+ // All test programs start at 0x0200;
+ bus.write(0xfffc, 0x00);
+ bus.write(0xfffd, 0x02);
- cpu.stackPush(0x06);
- assertEquals(0xfe, cpu.getStackPointer());
- assertEquals(0x06, bus.read(0x1ff));
+ cpu.reset();
+ }
- cpu.stackPush(0x05);
- assertEquals(0xfd, cpu.getStackPointer());
- assertEquals(0x06, bus.read(0x1ff));
- assertEquals(0x05, bus.read(0x1fe));
+ public void testReset() {
+ assertEquals(0, cpu.getAccumulator());
+ assertEquals(0, cpu.getXRegister());
+ assertEquals(0, cpu.getYRegister());
+ assertEquals(0x0200, cpu.getProgramCounter());
+ assertFalse(cpu.getCarryFlag());
+ assertFalse(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertFalse(cpu.getOverflowFlag());
+ assertFalse(cpu.getNegativeFlag());
+ }
- cpu.stackPush(0x04);
- assertEquals(0xfc, cpu.getStackPointer());
- assertEquals(0x06, bus.read(0x1ff));
- assertEquals(0x05, bus.read(0x1fe));
- assertEquals(0x04, bus.read(0x1fd));
+ public void testStack() throws MemoryAccessException {
- cpu.stackPush(0x03);
- assertEquals(0xfb, cpu.getStackPointer());
- assertEquals(0x06, bus.read(0x1ff));
- assertEquals(0x05, bus.read(0x1fe));
- assertEquals(0x04, bus.read(0x1fd));
- assertEquals(0x03, bus.read(0x1fc));
+ cpu.stackPush(0x13);
+ assertEquals(0x13, cpu.stackPop());
- cpu.stackPush(0x02);
- assertEquals(0xfa, cpu.getStackPointer());
- assertEquals(0x06, bus.read(0x1ff));
- assertEquals(0x05, bus.read(0x1fe));
- assertEquals(0x04, bus.read(0x1fd));
- assertEquals(0x03, bus.read(0x1fc));
- assertEquals(0x02, bus.read(0x1fb));
+ cpu.stackPush(0x12);
+ assertEquals(0x12, cpu.stackPop());
- cpu.stackPush(0x01);
- assertEquals(0xf9, cpu.getStackPointer());
- assertEquals(0x06, bus.read(0x1ff));
- assertEquals(0x05, bus.read(0x1fe));
- assertEquals(0x04, bus.read(0x1fd));
- assertEquals(0x03, bus.read(0x1fc));
- assertEquals(0x02, bus.read(0x1fb));
- assertEquals(0x01, bus.read(0x1fa));
- }
+ for (int i = 0x00; i <= 0xff; i++) {
+ cpu.stackPush(i);
+ }
- public void testStackPushWrapsAroundToStackTop() throws MemoryAccessException {
- cpu.setStackPointer(0x01);
+ for (int i = 0xff; i >= 0x00; i--) {
+ assertEquals(i, cpu.stackPop());
+ }
- cpu.stackPush(0x01);
- assertEquals(0x01, bus.read(0x101));
- assertEquals(0x00, cpu.getStackPointer());
+ }
- cpu.stackPush(0x02);
- assertEquals(0x02, bus.read(0x100));
- assertEquals(0xff, cpu.getStackPointer());
+ public void testStackPush() throws MemoryAccessException {
+ assertEquals(0xff, cpu.getStackPointer());
+ assertEquals(0x00, bus.read(0x1ff));
- cpu.stackPush(0x03);
- assertEquals(0x03, bus.read(0x1ff));
- assertEquals(0xfe, cpu.getStackPointer());
- }
+ cpu.stackPush(0x06);
+ assertEquals(0xfe, cpu.getStackPointer());
+ assertEquals(0x06, bus.read(0x1ff));
+
+ cpu.stackPush(0x05);
+ assertEquals(0xfd, cpu.getStackPointer());
+ assertEquals(0x06, bus.read(0x1ff));
+ assertEquals(0x05, bus.read(0x1fe));
+
+ cpu.stackPush(0x04);
+ assertEquals(0xfc, cpu.getStackPointer());
+ assertEquals(0x06, bus.read(0x1ff));
+ assertEquals(0x05, bus.read(0x1fe));
+ assertEquals(0x04, bus.read(0x1fd));
+
+ cpu.stackPush(0x03);
+ assertEquals(0xfb, cpu.getStackPointer());
+ assertEquals(0x06, bus.read(0x1ff));
+ assertEquals(0x05, bus.read(0x1fe));
+ assertEquals(0x04, bus.read(0x1fd));
+ assertEquals(0x03, bus.read(0x1fc));
+
+ cpu.stackPush(0x02);
+ assertEquals(0xfa, cpu.getStackPointer());
+ assertEquals(0x06, bus.read(0x1ff));
+ assertEquals(0x05, bus.read(0x1fe));
+ assertEquals(0x04, bus.read(0x1fd));
+ assertEquals(0x03, bus.read(0x1fc));
+ assertEquals(0x02, bus.read(0x1fb));
+
+ cpu.stackPush(0x01);
+ assertEquals(0xf9, cpu.getStackPointer());
+ assertEquals(0x06, bus.read(0x1ff));
+ assertEquals(0x05, bus.read(0x1fe));
+ assertEquals(0x04, bus.read(0x1fd));
+ assertEquals(0x03, bus.read(0x1fc));
+ assertEquals(0x02, bus.read(0x1fb));
+ assertEquals(0x01, bus.read(0x1fa));
+ }
+
+ public void testStackPushWrapsAroundToStackTop() throws MemoryAccessException {
+ cpu.setStackPointer(0x01);
+
+ cpu.stackPush(0x01);
+ assertEquals(0x01, bus.read(0x101));
+ assertEquals(0x00, cpu.getStackPointer());
+
+ cpu.stackPush(0x02);
+ assertEquals(0x02, bus.read(0x100));
+ assertEquals(0xff, cpu.getStackPointer());
+
+ cpu.stackPush(0x03);
+ assertEquals(0x03, bus.read(0x1ff));
+ assertEquals(0xfe, cpu.getStackPointer());
+ }
- public void testStackPop() throws MemoryAccessException {
- bus.write(0x1ff, 0x06);
- bus.write(0x1fe, 0x05);
- bus.write(0x1fd, 0x04);
- bus.write(0x1fc, 0x03);
- bus.write(0x1fb, 0x02);
- bus.write(0x1fa, 0x01);
- cpu.setStackPointer(0xf9);
+ public void testStackPop() throws MemoryAccessException {
+ bus.write(0x1ff, 0x06);
+ bus.write(0x1fe, 0x05);
+ bus.write(0x1fd, 0x04);
+ bus.write(0x1fc, 0x03);
+ bus.write(0x1fb, 0x02);
+ bus.write(0x1fa, 0x01);
+ cpu.setStackPointer(0xf9);
- assertEquals(0x01, cpu.stackPop());
- assertEquals(0xfa, cpu.getStackPointer());
+ assertEquals(0x01, cpu.stackPop());
+ assertEquals(0xfa, cpu.getStackPointer());
- assertEquals(0x02, cpu.stackPop());
- assertEquals(0xfb, cpu.getStackPointer());
+ assertEquals(0x02, cpu.stackPop());
+ assertEquals(0xfb, cpu.getStackPointer());
- assertEquals(0x03, cpu.stackPop());
- assertEquals(0xfc, cpu.getStackPointer());
+ assertEquals(0x03, cpu.stackPop());
+ assertEquals(0xfc, cpu.getStackPointer());
- assertEquals(0x04, cpu.stackPop());
- assertEquals(0xfd, cpu.getStackPointer());
+ assertEquals(0x04, cpu.stackPop());
+ assertEquals(0xfd, cpu.getStackPointer());
- assertEquals(0x05, cpu.stackPop());
- assertEquals(0xfe, cpu.getStackPointer());
+ assertEquals(0x05, cpu.stackPop());
+ assertEquals(0xfe, cpu.getStackPointer());
- assertEquals(0x06, cpu.stackPop());
- assertEquals(0xff, cpu.getStackPointer());
- }
+ assertEquals(0x06, cpu.stackPop());
+ assertEquals(0xff, cpu.getStackPointer());
+ }
- public void testStackPopWrapsAroundToStackBottom() throws MemoryAccessException {
- bus.write(0x1ff, 0x0f); // top of stack
- bus.write(0x100, 0xf0); // bottom of stack
- bus.write(0x101, 0xf1);
- bus.write(0x102, 0xf2);
+ public void testStackPopWrapsAroundToStackBottom() throws MemoryAccessException {
+ bus.write(0x1ff, 0x0f); // top of stack
+ bus.write(0x100, 0xf0); // bottom of stack
+ bus.write(0x101, 0xf1);
+ bus.write(0x102, 0xf2);
- cpu.setStackPointer(0xfe);
+ cpu.setStackPointer(0xfe);
- assertEquals(0x0f, cpu.stackPop());
- assertEquals(0xff, cpu.getStackPointer());
+ assertEquals(0x0f, cpu.stackPop());
+ assertEquals(0xff, cpu.getStackPointer());
- assertEquals(0xf0, cpu.stackPop());
- assertEquals(0x00, cpu.getStackPointer());
+ assertEquals(0xf0, cpu.stackPop());
+ assertEquals(0x00, cpu.getStackPointer());
- assertEquals(0xf1, cpu.stackPop());
- assertEquals(0x01, cpu.getStackPointer());
+ assertEquals(0xf1, cpu.stackPop());
+ assertEquals(0x01, cpu.getStackPointer());
- assertEquals(0xf2, cpu.stackPop());
- assertEquals(0x02, cpu.getStackPointer());
- }
+ assertEquals(0xf2, cpu.stackPop());
+ assertEquals(0x02, cpu.getStackPointer());
+ }
- public void testStackPeekDoesNotAlterStackPointer() throws MemoryAccessException {
- assertEquals(0x00, cpu.stackPeek());
- assertEquals(0xff, cpu.getStackPointer());
+ public void testStackPeekDoesNotAlterStackPointer() throws MemoryAccessException {
+ assertEquals(0x00, cpu.stackPeek());
+ assertEquals(0xff, cpu.getStackPointer());
- cpu.stackPush(0x01);
- assertEquals(0x01, cpu.stackPeek());
- assertEquals(0xfe, cpu.getStackPointer());
+ cpu.stackPush(0x01);
+ assertEquals(0x01, cpu.stackPeek());
+ assertEquals(0xfe, cpu.getStackPointer());
- cpu.stackPush(0x02);
- assertEquals(0x02, cpu.stackPeek());
- assertEquals(0xfd, cpu.getStackPointer());
+ cpu.stackPush(0x02);
+ assertEquals(0x02, cpu.stackPeek());
+ assertEquals(0xfd, cpu.getStackPointer());
- cpu.stackPush(0x03);
- assertEquals(0x03, cpu.stackPeek());
- assertEquals(0xfc, cpu.getStackPointer());
+ cpu.stackPush(0x03);
+ assertEquals(0x03, cpu.stackPeek());
+ assertEquals(0xfc, cpu.getStackPointer());
- cpu.stackPush(0x04);
- assertEquals(0x04, cpu.stackPeek());
- assertEquals(0xfb, cpu.getStackPointer());
- assertEquals(0x04, cpu.stackPeek());
- assertEquals(0xfb, cpu.getStackPointer());
- assertEquals(0x04, cpu.stackPeek());
- assertEquals(0xfb, cpu.getStackPointer());
- }
+ cpu.stackPush(0x04);
+ assertEquals(0x04, cpu.stackPeek());
+ assertEquals(0xfb, cpu.getStackPointer());
+ assertEquals(0x04, cpu.stackPeek());
+ assertEquals(0xfb, cpu.getStackPointer());
+ assertEquals(0x04, cpu.stackPeek());
+ assertEquals(0xfb, cpu.getStackPointer());
+ }
- public void testGetProcessorStatus() {
- // By default, no flags are set. Remember, bit 5
- // is always '1'.
- assertEquals(0x20, cpu.getProcessorStatus());
- cpu.setCarryFlag();
- assertEquals(0x21, cpu.getProcessorStatus());
- cpu.setZeroFlag();
- assertEquals(0x23, cpu.getProcessorStatus());
- cpu.setIrqDisableFlag();
- assertEquals(0x27, cpu.getProcessorStatus());
- cpu.setDecimalModeFlag();
- assertEquals(0x2f, cpu.getProcessorStatus());
- cpu.setBreakFlag();
- assertEquals(0x3f, cpu.getProcessorStatus());
- cpu.setOverflowFlag();
- assertEquals(0x7f, cpu.getProcessorStatus());
- cpu.setNegativeFlag();
- assertEquals(0xff, cpu.getProcessorStatus());
+ public void testGetProcessorStatus() {
+ // By default, no flags are set. Remember, bit 5
+ // is always '1'.
+ assertEquals(0x20, cpu.getProcessorStatus());
+ cpu.setCarryFlag();
+ assertEquals(0x21, cpu.getProcessorStatus());
+ cpu.setZeroFlag();
+ assertEquals(0x23, cpu.getProcessorStatus());
+ cpu.setIrqDisableFlag();
+ assertEquals(0x27, cpu.getProcessorStatus());
+ cpu.setDecimalModeFlag();
+ assertEquals(0x2f, cpu.getProcessorStatus());
+ cpu.setBreakFlag();
+ assertEquals(0x3f, cpu.getProcessorStatus());
+ cpu.setOverflowFlag();
+ assertEquals(0x7f, cpu.getProcessorStatus());
+ cpu.setNegativeFlag();
+ assertEquals(0xff, cpu.getProcessorStatus());
- cpu.clearCarryFlag();
- assertEquals(0xfe, cpu.getProcessorStatus());
- cpu.clearZeroFlag();
- assertEquals(0xfc, cpu.getProcessorStatus());
- cpu.clearIrqDisableFlag();
- assertEquals(0xf8, cpu.getProcessorStatus());
- cpu.clearDecimalModeFlag();
- assertEquals(0xf0, cpu.getProcessorStatus());
- cpu.clearBreakFlag();
- assertEquals(0xe0, cpu.getProcessorStatus());
- cpu.clearOverflowFlag();
- assertEquals(0xa0, cpu.getProcessorStatus());
- cpu.clearNegativeFlag();
- assertEquals(0x20, cpu.getProcessorStatus());
- }
+ cpu.clearCarryFlag();
+ assertEquals(0xfe, cpu.getProcessorStatus());
+ cpu.clearZeroFlag();
+ assertEquals(0xfc, cpu.getProcessorStatus());
+ cpu.clearIrqDisableFlag();
+ assertEquals(0xf8, cpu.getProcessorStatus());
+ cpu.clearDecimalModeFlag();
+ assertEquals(0xf0, cpu.getProcessorStatus());
+ cpu.clearBreakFlag();
+ assertEquals(0xe0, cpu.getProcessorStatus());
+ cpu.clearOverflowFlag();
+ assertEquals(0xa0, cpu.getProcessorStatus());
+ cpu.clearNegativeFlag();
+ assertEquals(0x20, cpu.getProcessorStatus());
+ }
- public void testSetProcessorStatus() {
- // Default
- assertFalse(cpu.getZeroFlag());
- assertFalse(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertFalse(cpu.getOverflowFlag());
- assertFalse(cpu.getNegativeFlag());
+ public void testSetProcessorStatus() {
+ // Default
+ assertFalse(cpu.getZeroFlag());
+ assertFalse(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertFalse(cpu.getOverflowFlag());
+ assertFalse(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20|Cpu.P_CARRY);
+ cpu.setProcessorStatus(0x20 | Cpu.P_CARRY);
- assertTrue(cpu.getCarryFlag());
- assertFalse(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertFalse(cpu.getOverflowFlag());
- assertFalse(cpu.getNegativeFlag());
+ assertTrue(cpu.getCarryFlag());
+ assertFalse(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertFalse(cpu.getOverflowFlag());
+ assertFalse(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE);
+ cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE);
- assertTrue(cpu.getCarryFlag());
- assertFalse(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertFalse(cpu.getOverflowFlag());
- assertTrue(cpu.getNegativeFlag());
+ assertTrue(cpu.getCarryFlag());
+ assertFalse(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertFalse(cpu.getOverflowFlag());
+ assertTrue(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO);
+ cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO);
- assertTrue(cpu.getCarryFlag());
- assertTrue(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertFalse(cpu.getOverflowFlag());
- assertTrue(cpu.getNegativeFlag());
+ assertTrue(cpu.getCarryFlag());
+ assertTrue(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertFalse(cpu.getOverflowFlag());
+ assertTrue(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
- Cpu.P_OVERFLOW);
+ cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
+ Cpu.P_OVERFLOW);
- assertTrue(cpu.getCarryFlag());
- assertTrue(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertTrue(cpu.getOverflowFlag());
- assertTrue(cpu.getNegativeFlag());
+ assertTrue(cpu.getCarryFlag());
+ assertTrue(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertTrue(cpu.getOverflowFlag());
+ assertTrue(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
- Cpu.P_OVERFLOW|Cpu.P_BREAK);
+ cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
+ Cpu.P_OVERFLOW | Cpu.P_BREAK);
- assertTrue(cpu.getCarryFlag());
- assertTrue(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertTrue(cpu.getBreakFlag());
- assertTrue(cpu.getOverflowFlag());
- assertTrue(cpu.getNegativeFlag());
+ assertTrue(cpu.getCarryFlag());
+ assertTrue(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertTrue(cpu.getBreakFlag());
+ assertTrue(cpu.getOverflowFlag());
+ assertTrue(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
- Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL);
+ cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
+ Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_DECIMAL);
- assertTrue(cpu.getCarryFlag());
- assertTrue(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertTrue(cpu.getDecimalModeFlag());
- assertTrue(cpu.getBreakFlag());
- assertTrue(cpu.getOverflowFlag());
- assertTrue(cpu.getNegativeFlag());
+ assertTrue(cpu.getCarryFlag());
+ assertTrue(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertTrue(cpu.getDecimalModeFlag());
+ assertTrue(cpu.getBreakFlag());
+ assertTrue(cpu.getOverflowFlag());
+ assertTrue(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
- Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL|
- Cpu.P_IRQ_DISABLE);
+ cpu.setProcessorStatus(0x20 | Cpu.P_CARRY | Cpu.P_NEGATIVE | Cpu.P_ZERO |
+ Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_DECIMAL |
+ Cpu.P_IRQ_DISABLE);
- assertTrue(cpu.getCarryFlag());
- assertTrue(cpu.getZeroFlag());
- assertTrue(cpu.getIrqDisableFlag());
- assertTrue(cpu.getDecimalModeFlag());
- assertTrue(cpu.getBreakFlag());
- assertTrue(cpu.getOverflowFlag());
- assertTrue(cpu.getNegativeFlag());
+ assertTrue(cpu.getCarryFlag());
+ assertTrue(cpu.getZeroFlag());
+ assertTrue(cpu.getIrqDisableFlag());
+ assertTrue(cpu.getDecimalModeFlag());
+ assertTrue(cpu.getBreakFlag());
+ assertTrue(cpu.getOverflowFlag());
+ assertTrue(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x20);
+ cpu.setProcessorStatus(0x20);
- assertFalse(cpu.getCarryFlag());
- assertFalse(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertFalse(cpu.getOverflowFlag());
- assertFalse(cpu.getNegativeFlag());
+ assertFalse(cpu.getCarryFlag());
+ assertFalse(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertFalse(cpu.getOverflowFlag());
+ assertFalse(cpu.getNegativeFlag());
- cpu.setProcessorStatus(0x00);
+ cpu.setProcessorStatus(0x00);
- assertFalse(cpu.getCarryFlag());
- assertFalse(cpu.getZeroFlag());
- assertFalse(cpu.getIrqDisableFlag());
- assertFalse(cpu.getDecimalModeFlag());
- assertFalse(cpu.getBreakFlag());
- assertFalse(cpu.getOverflowFlag());
- assertFalse(cpu.getNegativeFlag());
- }
+ assertFalse(cpu.getCarryFlag());
+ assertFalse(cpu.getZeroFlag());
+ assertFalse(cpu.getIrqDisableFlag());
+ assertFalse(cpu.getDecimalModeFlag());
+ assertFalse(cpu.getBreakFlag());
+ assertFalse(cpu.getOverflowFlag());
+ assertFalse(cpu.getNegativeFlag());
+ }
- public void testAddress() {
- assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
- assertEquals(0x00ea, cpu.address(0xea, 0x00));
- assertEquals(0xf100, cpu.address(0x00, 0xf1));
- assertEquals(0x1234, cpu.address(0x34, 0x12));
- assertEquals(0xffff, cpu.address(0xff, 0xff));
- }
+ public void testAddress() {
+ assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
+ assertEquals(0x00ea, cpu.address(0xea, 0x00));
+ assertEquals(0xf100, cpu.address(0x00, 0xf1));
+ assertEquals(0x1234, cpu.address(0x34, 0x12));
+ assertEquals(0xffff, cpu.address(0xff, 0xff));
+ }
- public void testZpxAddress() {
- cpu.setXRegister(0x00);
- assertEquals(0x10, cpu.zpxAddress(0x10));
- cpu.setXRegister(0x10);
- assertEquals(0x20, cpu.zpxAddress(0x10));
- cpu.setXRegister(0x25);
- assertEquals(0x35, cpu.zpxAddress(0x10));
- cpu.setXRegister(0xf5);
- assertEquals(0x05, cpu.zpxAddress(0x10));
+ public void testZpxAddress() {
+ cpu.setXRegister(0x00);
+ assertEquals(0x10, cpu.zpxAddress(0x10));
+ cpu.setXRegister(0x10);
+ assertEquals(0x20, cpu.zpxAddress(0x10));
+ cpu.setXRegister(0x25);
+ assertEquals(0x35, cpu.zpxAddress(0x10));
+ cpu.setXRegister(0xf5);
+ assertEquals(0x05, cpu.zpxAddress(0x10));
- cpu.setXRegister(0x00);
- assertEquals(0x80, cpu.zpxAddress(0x80));
- cpu.setXRegister(0x10);
- assertEquals(0x90, cpu.zpxAddress(0x80));
- cpu.setXRegister(0x25);
- assertEquals(0xa5, cpu.zpxAddress(0x80));
- cpu.setXRegister(0x95);
- assertEquals(0x15, cpu.zpxAddress(0x80));
- }
+ cpu.setXRegister(0x00);
+ assertEquals(0x80, cpu.zpxAddress(0x80));
+ cpu.setXRegister(0x10);
+ assertEquals(0x90, cpu.zpxAddress(0x80));
+ cpu.setXRegister(0x25);
+ assertEquals(0xa5, cpu.zpxAddress(0x80));
+ cpu.setXRegister(0x95);
+ assertEquals(0x15, cpu.zpxAddress(0x80));
+ }
- public void testZpyAddress() {
- cpu.setYRegister(0x00);
- assertEquals(0x10, cpu.zpyAddress(0x10));
- cpu.setYRegister(0x10);
- assertEquals(0x20, cpu.zpyAddress(0x10));
- cpu.setYRegister(0x25);
- assertEquals(0x35, cpu.zpyAddress(0x10));
- cpu.setYRegister(0xf5);
- assertEquals(0x05, cpu.zpyAddress(0x10));
+ public void testZpyAddress() {
+ cpu.setYRegister(0x00);
+ assertEquals(0x10, cpu.zpyAddress(0x10));
+ cpu.setYRegister(0x10);
+ assertEquals(0x20, cpu.zpyAddress(0x10));
+ cpu.setYRegister(0x25);
+ assertEquals(0x35, cpu.zpyAddress(0x10));
+ cpu.setYRegister(0xf5);
+ assertEquals(0x05, cpu.zpyAddress(0x10));
- cpu.setYRegister(0x00);
- assertEquals(0x80, cpu.zpyAddress(0x80));
- cpu.setYRegister(0x10);
- assertEquals(0x90, cpu.zpyAddress(0x80));
- cpu.setYRegister(0x25);
- assertEquals(0xa5, cpu.zpyAddress(0x80));
- cpu.setYRegister(0x95);
- assertEquals(0x15, cpu.zpyAddress(0x80));
- }
+ cpu.setYRegister(0x00);
+ assertEquals(0x80, cpu.zpyAddress(0x80));
+ cpu.setYRegister(0x10);
+ assertEquals(0x90, cpu.zpyAddress(0x80));
+ cpu.setYRegister(0x25);
+ assertEquals(0xa5, cpu.zpyAddress(0x80));
+ cpu.setYRegister(0x95);
+ assertEquals(0x15, cpu.zpyAddress(0x80));
+ }
+
+ public void testPcStatus() {
+ cpu.setProgramCounter(0x03fa);
+ assertEquals("$03FA", cpu.getProgramCounterStatus());
+ }
+
+ public void testOpcodeStatus() throws MemoryAccessException {
+ // LDA (immediate)
+ bus.write(0x0200, 0xa9);
+ bus.write(0x0201, 0xef);
+ cpu.step();
+ assertEquals("LDA #$EF", cpu.getOpcodeStatus());
+
+ // BRK instruction
+ bus.write(0x0202, 0x00);
+ cpu.step();
+ assertEquals("BRK", cpu.getOpcodeStatus());
+
+ // Illegal opcode
+ cpu.step();
+ assertEquals("BRK", cpu.getOpcodeStatus());
+ }
}
\ No newline at end of file