diff --git a/README.md b/README.md
index 0a1633c..ef7ad49 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@ SYMON - A 6502 System Simulator
**NOTE: THIS IS BETA QUALITY SOFTWARE UNDER ACTIVE DEVELOPMENT. Feedback is
welcome!**
-**Version:** 0.5
-**Last Updated:** 21 October, 2012
+**Version:** 0.6
+**Last Updated:** 5 November, 2012
Copyright (c) 2008-2012 Seth J. Morabito <web@loomcom.com>
@@ -47,7 +47,7 @@ Maven will build Symon, run unit tests, and produce a jar file in the
Symon is meant to be invoked directly from the jar file. To run with
Java 1.5 or greater, just type:
- $ java -jar symon-0.3-jar-with-dependencies.jar
+ $ java -jar symon-0.6-jar-with-dependencies.jar
When Symon is running, you should be presented with a simple graphical
interface.
diff --git a/pom.xml b/pom.xml
index 4abce27..03e1c2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.loomcom.symon
symon
jar
- 0.3
+ 0.6-snapshot
symon
http://www.loomcom.com/symon
@@ -44,7 +44,7 @@
com.grahamedgecombe.jterminal
jterminal
- 1.0.2-loomcom
+ 1.0.2.1-loomcom
@@ -79,6 +79,7 @@
jar-with-dependencies
+ false
com.loomcom.symon.Simulator
diff --git a/samples/echo.asm b/samples/echo.asm
index 0b50506..3ad09a4 100644
--- a/samples/echo.asm
+++ b/samples/echo.asm
@@ -3,7 +3,7 @@
;;
-.alias iobase $c000
+.alias iobase $8000
.alias iostatus [iobase + 1]
.alias iocmd [iobase + 2]
.alias ioctrl [iobase + 3]
diff --git a/samples/echo.prg b/samples/echo.prg
index 8ed8ad5..ab5f1f7 100644
Binary files a/samples/echo.prg and b/samples/echo.prg differ
diff --git a/samples/ehbasic.rom b/samples/ehbasic.rom
index f3ab283..8e5526e 100644
Binary files a/samples/ehbasic.rom and b/samples/ehbasic.rom differ
diff --git a/samples/ehbasic/basic.asm b/samples/ehbasic/basic.asm
index b843a0e..5d7b380 100644
--- a/samples/ehbasic/basic.asm
+++ b/samples/ehbasic/basic.asm
@@ -441,7 +441,7 @@ Ram_top = $C000 ; end of user RAM+1 (set as needed, should be page aligned)
; This start can be changed to suit your system
- .org $D000
+ .org $C000
; BASIC cold start entry point
diff --git a/samples/ehbasic/min_mon.asm b/samples/ehbasic/min_mon.asm
index 67cb486..693078a 100644
--- a/samples/ehbasic/min_mon.asm
+++ b/samples/ehbasic/min_mon.asm
@@ -15,7 +15,7 @@ NMI_vec = IRQ_vec+$0A ; NMI code vector
; setup for the 6502 simulator environment
-IO_AREA = $C000
+IO_AREA = $8800
ACIAdata = IO_AREA ; simulated ACIA r/w port
ACIAstatus = IO_AREA+1
ACIAcommand = IO_AREA+2
diff --git a/samples/ehbasic/symon.config b/samples/ehbasic/symon.config
index 137d04d..f7eb850 100644
--- a/samples/ehbasic/symon.config
+++ b/samples/ehbasic/symon.config
@@ -1,6 +1,6 @@
MEMORY {
-RAM1: start = $0000, size = $C000;
-ROM1: start = $D000, size = $2F00, fill = yes;
+RAM1: start = $0000, size = $8000;
+ROM1: start = $C000, size = $3F00, fill = yes;
MONITOR: start = $FF00, size = $FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
}
diff --git a/samples/hello.asm b/samples/hello.asm
index 4916ba7..877a690 100644
--- a/samples/hello.asm
+++ b/samples/hello.asm
@@ -3,7 +3,7 @@
;;
-.alias iobase $c000
+.alias iobase $8000
.alias iostatus [iobase + 1]
.alias iocmd [iobase + 2]
.alias ioctrl [iobase + 3]
diff --git a/samples/hello.prg b/samples/hello.prg
index dc26662..1d24a9d 100644
Binary files a/samples/hello.prg and b/samples/hello.prg differ
diff --git a/src/main/java/com/loomcom/symon/Bus.java b/src/main/java/com/loomcom/symon/Bus.java
index 5a0c97f..6b67786 100644
--- a/src/main/java/com/loomcom/symon/Bus.java
+++ b/src/main/java/com/loomcom/symon/Bus.java
@@ -10,11 +10,16 @@ import com.loomcom.symon.exceptions.*;
*/
public class Bus {
+ // The default address at which to load programs
+ public static int DEFAULT_LOAD_ADDRESS = 0x0200;
+
// By default, our bus starts at 0, and goes up to 64K
private int startAddress = 0x0000;
private int endAddress = 0xffff;
+
// The CPU
private Cpu cpu;
+
// Ordered list of IO devices.
private SortedSet devices;
@@ -36,8 +41,13 @@ public class Bus {
return endAddress;
}
- public void addDevice(Device device)
- throws MemoryRangeException {
+ /**
+ * Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others.
+ *
+ * @param device
+ * @throws MemoryRangeException
+ */
+ public void addDevice(Device device) throws MemoryRangeException {
// Make sure there's no memory overlap.
MemoryRange memRange = device.getMemoryRange();
for (Device d : devices) {
@@ -54,9 +64,20 @@ public class Bus {
devices.add(device);
}
+ /**
+ * Remove a device from the bus.
+ *
+ * @param device
+ */
+ public void removeDevice(Device device) {
+ if (devices.contains(device)) {
+ devices.remove(device);
+ }
+ }
+
public void addCpu(Cpu cpu) {
- cpu.setBus(this);
this.cpu = cpu;
+ cpu.setBus(this);
}
/**
diff --git a/src/main/java/com/loomcom/symon/Cpu.java b/src/main/java/com/loomcom/symon/Cpu.java
index 2e0578e..f32e4a5 100644
--- a/src/main/java/com/loomcom/symon/Cpu.java
+++ b/src/main/java/com/loomcom/symon/Cpu.java
@@ -1,14 +1,37 @@
+/*
+ * Copyright (c) 2008-2012 Seth J. Morabito
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
package com.loomcom.symon;
import com.loomcom.symon.exceptions.MemoryAccessException;
/**
- * Main 6502 CPU Simulation.
+ * This class provides a simulation of the MOS 6502 CPU's state machine.
+ * A simple interface allows this 6502 to read and write to a simulated bus,
+ * and exposes some of the internal state for inspection and debugging.
*/
public class Cpu implements InstructionTable {
- 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;
@@ -33,6 +56,9 @@ public class Cpu implements InstructionTable {
// TODO: Make configurable
private static final int CLOCK_IN_NS = 1000;
+ /* Simulated behavior */
+ private static CpuBehavior behavior;
+
/* The Bus */
private Bus bus;
@@ -82,6 +108,11 @@ public class Cpu implements InstructionTable {
* Construct a new CPU.
*/
public Cpu() {
+ this(CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG);
+ }
+
+ public Cpu(CpuBehavior behavior) {
+ this.behavior = behavior;
}
/**
@@ -98,6 +129,14 @@ public class Cpu implements InstructionTable {
return bus;
}
+ public void setBehavior(CpuBehavior behavior) {
+ this.behavior = behavior;
+ }
+
+ public CpuBehavior getBehavior() {
+ return behavior;
+ }
+
/**
* Reset the CPU to known initial values.
*/
@@ -389,7 +428,15 @@ public class Cpu implements InstructionTable {
break;
case 0x6c: // JMP - Indirect
lo = address(args[0], args[1]); // Address of low byte
- hi = lo + 1; // Address of high byte
+
+ if (args[0] == 0xff &&
+ (behavior == CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG ||
+ behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) {
+ hi = address(0x00, args[1]);
+ } else {
+ hi = lo + 1;
+ }
+
pc = address(bus.read(lo), bus.read(hi));
/* TODO: For accuracy, allow a flag to enable broken behavior
* of early 6502s:
@@ -1400,11 +1447,15 @@ public class Cpu implements InstructionTable {
private void delayLoop(int opcode) {
int clockSteps = Cpu.instructionClocks[0xff & opcode];
// Just a precaution. This could be better.
- if (clockSteps == 0) { clockSteps = 1; }
+ if (clockSteps == 0) {
+ clockSteps = 1;
+ }
long startTime = System.nanoTime();
long stopTime = startTime + (CLOCK_IN_NS * clockSteps);
// Busy loop
- while (System.nanoTime() < stopTime) { ; }
+ while (System.nanoTime() < stopTime) {
+ ;
+ }
}
/**
@@ -1459,7 +1510,7 @@ public class Cpu implements InstructionTable {
return sb.toString();
}
-
+
public String getInstructionByteStatus() {
switch (Cpu.instructionSizes[ir]) {
case 0:
diff --git a/src/main/java/com/loomcom/symon/InstructionTable.java b/src/main/java/com/loomcom/symon/InstructionTable.java
index b4ca85e..62226c6 100644
--- a/src/main/java/com/loomcom/symon/InstructionTable.java
+++ b/src/main/java/com/loomcom/symon/InstructionTable.java
@@ -2,248 +2,285 @@ package com.loomcom.symon;
public interface InstructionTable {
- /**
- * Enumeration of Addressing Modes.
- */
- public enum Mode {
- ACC {
- public String toString() {
- return "Accumulator";
- }
- },
+ /**
+ * Enumeration of valid CPU behaviors. These determine what behavior and instruction
+ * set will be simulated, depending on desired version of 6502.
+ *
+ * TODO: As of version 0.6, this is still not used! All CPUs are "idealized" NMOS 6502 only.
+ */
+ public enum CpuBehavior {
+ /**
+ * The earliest NMOS 6502 includes a bug that causes the ROR instruction
+ * to behave like an ASL that does not affect the carry bit. This version
+ * is very rare in the wild.
+ *
+ * NB: Does NOT implement "unimplemented" NMOS instructions.
+ */
+ NMOS_WITH_ROR_BUG,
- ABS {
- public String toString() {
- return "Absolute";
- }
- },
+ /**
+ * All NMOS 6502's have a bug with the indirect JMP instruction. If the
+ *
+ * NB: Does NOT implement "unimplemented" NMOS instructions.
+ */
+ NMOS_WITH_INDIRECT_JMP_BUG,
- ABX {
- public String toString() {
- return "Absolute, X-indexed";
- }
- },
+ /**
+ * Emulate an NMOS 6502 without the indirect JMP bug. This type of 6502
+ * does not actually exist in the wild.
+ *
+ * NB: Does NOT implement "unimplemented" NMOS instructions.
+ */
+ NMOS_WITHOUT_INDIRECT_JMP_BUG,
- ABY {
- public String toString() {
- return "Absolute, Y-indexed";
- }
- },
-
- IMM {
- public String toString() {
- return "Immediate";
- }
- },
-
- IMP {
- public String toString() {
- return "Implied";
- }
- },
-
- IND {
- public String toString() {
- return "Indirect";
- }
- },
-
- XIN {
- public String toString() {
- return "X-indexed Indirect";
- }
- },
-
- INY {
- public String toString() {
- return "Indirect, Y-indexed";
- }
- },
-
- REL {
- public String toString() {
- return "Relative";
- }
- },
-
- ZPG {
- public String toString() {
- return "Zeropage";
- }
- },
-
- ZPX {
- public String toString() {
- return "Zeropage, X-indexed";
- }
- },
-
- ZPY {
- public String toString() {
- return "Zeropage, Y-indexed";
- }
- },
-
- NUL {
- public String toString() {
- return "NULL";
- }
+ /**
+ * Emulate a CMOS 65C02, with all CMOS instructions and addressing modes.
+ */
+ CMOS
}
- }
- // 6502 opcodes. No 65C02 opcodes implemented.
+ /**
+ * Enumeration of Addressing Modes.
+ */
+ public enum Mode {
+ ACC {
+ public String toString() {
+ return "Accumulator";
+ }
+ },
- /**
- * Instruction opcode names.
- */
- public static final String[] opcodeNames = {
- "BRK", "ORA", null, null, null, "ORA", "ASL", null,
- "PHP", "ORA", "ASL", null, null, "ORA", "ASL", null,
- "BPL", "ORA", null, null, null, "ORA", "ASL", null,
- "CLC", "ORA", null, null, null, "ORA", "ASL", null,
- "JSR", "AND", null, null, "BIT", "AND", "ROL", null,
- "PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null,
- "BMI", "AND", null, null, null, "AND", "ROL", null,
- "SEC", "AND", null, null, null, "AND", "ROL", null,
- "RTI", "EOR", null, null, null, "EOR", "LSR", null,
- "PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null,
- "BVC", "EOR", null, null, null, "EOR", "LSR", null,
- "CLI", "EOR", null, null, null, "EOR", "LSR", null,
- "RTS", "ADC", null, null, null, "ADC", "ROR", null,
- "PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null,
- "BVS", "ADC", null, null, null, "ADC", "ROR", null,
- "SEI", "ADC", null, null, null, "ADC", "ROR", null,
- "BCS", "STA", null, null, "STY", "STA", "STX", null,
- "DEY", null, "TXA", null, "STY", "STA", "STX", null,
- "BCC", "STA", null, null, "STY", "STA", "STX", null,
- "TYA", "STA", "TXS", null, null, "STA", null, null,
- "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null,
- "TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null,
- "BCS", "LDA", null, null, "LDY", "LDA", "LDX", null,
- "CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null,
- "CPY", "CMP", null, null, "CPY", "CMP", "DEC", null,
- "INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null,
- "BNE", "CMP", null, null, null, "CMP", "DEC", null,
- "CLD", "CMP", null, null, null, "CMP", "DEC", null,
- "CPX", "SBC", null, null, "CPX", "SBC", "INC", null,
- "INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null,
- "BEQ", "SBC", null, null, null, "SBC", "INC", null,
- "SED", "SBC", null, null, null, "SBC", "INC", null
- };
+ ABS {
+ public String toString() {
+ return "Absolute";
+ }
+ },
- /**
- * Instruction addressing modes.
- */
- public static final Mode[] instructionModes = {
- Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
- Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
- Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
- Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x10-0x13
- Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17
- Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b
- Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f
- Mode.ABS, Mode.XIN, Mode.NUL, Mode.NUL, // 0x20-0x23
- Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27
- Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b
- Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x30-0x33
- Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37
- Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b
- Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f
- Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x40-0x43
- Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47
- Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b
- Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x50-0x53
- Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57
- Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b
- Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f
- Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x60-0x63
- Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67
- Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b
- Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x70-0x73
- Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77
- Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b
- Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
- Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83
- Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87
- Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b
- Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93
- Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
- Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
- Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
- Mode.IMM, Mode.XIN, Mode.IMM, Mode.NUL, // 0xa0-0xa3
- Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
- Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
- Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3
- Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
- Mode.IMP, Mode.ABX, Mode.IMP, Mode.NUL, // 0xb8-0xbb
- Mode.ABX, Mode.ABY, Mode.ABY, Mode.NUL, // 0xbc-0xbf
- Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xc0-0xc3
- Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7
- Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb
- Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xd0-0xd3
- Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7
- Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb
- Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf
- Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xe0-0xe3
- Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7
- Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb
- Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef
- Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xf0-0xf3
- Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7
- Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
- Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
- };
+ ABX {
+ public String toString() {
+ return "Absolute, X-indexed";
+ }
+ },
+
+ ABY {
+ public String toString() {
+ return "Absolute, Y-indexed";
+ }
+ },
+
+ IMM {
+ public String toString() {
+ return "Immediate";
+ }
+ },
+
+ IMP {
+ public String toString() {
+ return "Implied";
+ }
+ },
+
+ IND {
+ public String toString() {
+ return "Indirect";
+ }
+ },
+
+ XIN {
+ public String toString() {
+ return "X-indexed Indirect";
+ }
+ },
+
+ INY {
+ public String toString() {
+ return "Indirect, Y-indexed";
+ }
+ },
+
+ REL {
+ public String toString() {
+ return "Relative";
+ }
+ },
+
+ ZPG {
+ public String toString() {
+ return "Zeropage";
+ }
+ },
+
+ ZPX {
+ public String toString() {
+ return "Zeropage, X-indexed";
+ }
+ },
+
+ ZPY {
+ public String toString() {
+ return "Zeropage, Y-indexed";
+ }
+ },
+
+ NUL {
+ public String toString() {
+ return "NULL";
+ }
+ }
+ }
+
+ // 6502 opcodes. No 65C02 opcodes implemented.
+
+ /**
+ * Instruction opcode names.
+ */
+ public static final String[] opcodeNames = {
+ "BRK", "ORA", null, null, null, "ORA", "ASL", null,
+ "PHP", "ORA", "ASL", null, null, "ORA", "ASL", null,
+ "BPL", "ORA", null, null, null, "ORA", "ASL", null,
+ "CLC", "ORA", null, null, null, "ORA", "ASL", null,
+ "JSR", "AND", null, null, "BIT", "AND", "ROL", null,
+ "PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null,
+ "BMI", "AND", null, null, null, "AND", "ROL", null,
+ "SEC", "AND", null, null, null, "AND", "ROL", null,
+ "RTI", "EOR", null, null, null, "EOR", "LSR", null,
+ "PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null,
+ "BVC", "EOR", null, null, null, "EOR", "LSR", null,
+ "CLI", "EOR", null, null, null, "EOR", "LSR", null,
+ "RTS", "ADC", null, null, null, "ADC", "ROR", null,
+ "PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null,
+ "BVS", "ADC", null, null, null, "ADC", "ROR", null,
+ "SEI", "ADC", null, null, null, "ADC", "ROR", null,
+ "BCS", "STA", null, null, "STY", "STA", "STX", null,
+ "DEY", null, "TXA", null, "STY", "STA", "STX", null,
+ "BCC", "STA", null, null, "STY", "STA", "STX", null,
+ "TYA", "STA", "TXS", null, null, "STA", null, null,
+ "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null,
+ "TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null,
+ "BCS", "LDA", null, null, "LDY", "LDA", "LDX", null,
+ "CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null,
+ "CPY", "CMP", null, null, "CPY", "CMP", "DEC", null,
+ "INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null,
+ "BNE", "CMP", null, null, null, "CMP", "DEC", null,
+ "CLD", "CMP", null, null, null, "CMP", "DEC", null,
+ "CPX", "SBC", null, null, "CPX", "SBC", "INC", null,
+ "INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null,
+ "BEQ", "SBC", null, null, null, "SBC", "INC", null,
+ "SED", "SBC", null, null, null, "SBC", "INC", null
+ };
+
+ /**
+ * Instruction addressing modes.
+ */
+ public static final Mode[] instructionModes = {
+ Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
+ Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
+ Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
+ Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x10-0x13
+ Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17
+ Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b
+ Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f
+ Mode.ABS, Mode.XIN, Mode.NUL, Mode.NUL, // 0x20-0x23
+ Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27
+ Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b
+ Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x30-0x33
+ Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37
+ Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b
+ Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f
+ Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x40-0x43
+ Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47
+ Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b
+ Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x50-0x53
+ Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57
+ Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b
+ Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f
+ Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x60-0x63
+ Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67
+ Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b
+ Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x70-0x73
+ Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77
+ Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b
+ Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
+ Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83
+ Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87
+ Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b
+ Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93
+ Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
+ Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
+ Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
+ Mode.IMM, Mode.XIN, Mode.IMM, Mode.NUL, // 0xa0-0xa3
+ Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
+ Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
+ Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3
+ Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
+ Mode.IMP, Mode.ABX, Mode.IMP, Mode.NUL, // 0xb8-0xbb
+ Mode.ABX, Mode.ABY, Mode.ABY, Mode.NUL, // 0xbc-0xbf
+ Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xc0-0xc3
+ Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7
+ Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb
+ Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xd0-0xd3
+ Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7
+ Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb
+ Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf
+ Mode.IMM, Mode.XIN, Mode.NUL, Mode.NUL, // 0xe0-0xe3
+ Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7
+ Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb
+ Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef
+ Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xf0-0xf3
+ Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7
+ Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
+ Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
+ };
- /**
- * Size, in bytes, required for each instruction.
- */
- public static final int[] instructionSizes = {
- 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0,
- 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
- 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
- 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
- 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
- 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
- 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
- 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
- 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
- };
+ /**
+ * Size, in bytes, required for each instruction.
+ */
+ public static final int[] instructionSizes = {
+ 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0,
+ 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
+ 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
+ 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
+ 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
+ 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0,
+ 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0,
+ 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0,
+ 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0
+ };
- /**
- * Number of clock cycles required for each instruction
- */
- public static final int[] instructionClocks = {
- 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0,
- 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
- 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0,
- 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
- 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0,
- 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
- 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0,
- 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
- 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
- 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
- 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
- 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
- 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
- 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
- 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
- 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0
- };
+ /**
+ * Number of clock cycles required for each instruction
+ */
+ public static final int[] instructionClocks = {
+ 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0,
+ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
+ 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0,
+ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
+ 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0,
+ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
+ 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0,
+ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
+ 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0,
+ 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0,
+ 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0,
+ 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0,
+ 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
+ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0,
+ 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0,
+ 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0
+ };
}
diff --git a/src/main/java/com/loomcom/symon/Simulator.java b/src/main/java/com/loomcom/symon/Simulator.java
index ddf8e89..e0a39cb 100644
--- a/src/main/java/com/loomcom/symon/Simulator.java
+++ b/src/main/java/com/loomcom/symon/Simulator.java
@@ -1,15 +1,41 @@
+/*
+ * Copyright (c) 2008-2012 Seth J. Morabito
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
package com.loomcom.symon;
import com.loomcom.symon.devices.Acia;
import com.loomcom.symon.devices.Memory;
+import com.loomcom.symon.devices.Via;
import com.loomcom.symon.exceptions.FifoUnderrunException;
import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException;
import com.loomcom.symon.exceptions.SymonException;
+import com.loomcom.symon.ui.Console;
import com.loomcom.symon.ui.PreferencesDialog;
import com.loomcom.symon.ui.StatusPanel;
-import com.loomcom.symon.ui.Console;
+import sun.rmi.rmic.iiop.DirectoryLoader;
+import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -19,24 +45,35 @@ import java.util.Observable;
import java.util.Observer;
import java.util.logging.Level;
import java.util.logging.Logger;
-import javax.swing.*;
+/**
+ * Symon Simulator Interface and Control.
+ *
+ * This class provides a control and I/O system for the simulated 6502 system.
+ * It includes the simulated CPU itself, as well as 32KB of RAM, 16KB of ROM,
+ * and a simulated ACIA for serial I/O. The ACIA is attached to a dumb terminal
+ * with a basic 80x25 character display.
+ *
+ */
public class Simulator implements ActionListener, Observer {
// Constants used by the simulated system. These define the memory map.
private static final int BUS_BOTTOM = 0x0000;
private static final int BUS_TOP = 0xffff;
- // 32K of RAM
+ // 32K of RAM from $0000 - $7FFF
private static final int MEMORY_BASE = 0x0000;
- private static final int MEMORY_SIZE = 0xC000;
+ private static final int MEMORY_SIZE = 0x8000;
- // IO area at $D000
- private static final int ACIA_BASE = 0xC000;
+ // VIA at $8000-$800F
+ private static final int VIA_BASE = 0x8000;
- // 8KB ROM at E000-FFFF
- private static final int ROM_BASE = 0xD000;
- private static final int ROM_SIZE = 0x3000;
+ // ACIA at $8800-$8803
+ private static final int ACIA_BASE = 0x8800;
+
+ // 16KB ROM at $C000-$FFFF
+ private static final int ROM_BASE = 0xC000;
+ private static final int ROM_SIZE = 0x4000;
// Since it is very expensive to update the UI with Swing's Event Dispatch Thread, we can't afford
// to refresh the view on every simulated clock cycle. Instead, we will only refresh the view after this
@@ -55,8 +92,9 @@ public class Simulator implements ActionListener, Observer {
private final Bus bus;
private final Cpu cpu;
private final Acia acia;
+ private final Via via;
private final Memory ram;
- private final Memory rom;
+ private Memory rom;
// A counter to keep track of the number of UI updates that have been
// requested
@@ -74,37 +112,36 @@ public class Simulator implements ActionListener, Observer {
// The most recently read key code
private char keyBuffer;
- // TODO: loadMenuItem seriously violates encapsulation!
+ // TODO: loadProgramItem seriously violates encapsulation!
// A far better solution would be to extend JMenu and add callback
// methods to enable and disable menus as required.
// Menu Items
- private JMenuItem loadMenuItem;
+ private JMenuItem loadProgramItem;
+ private JMenuItem loadRomItem;
private JFileChooser fileChooser;
private PreferencesDialog preferences;
public Simulator() throws MemoryRangeException, IOException {
this.acia = new Acia(ACIA_BASE);
+ this.via = new Via(VIA_BASE);
this.bus = new Bus(BUS_BOTTOM, BUS_TOP);
this.cpu = new Cpu();
this.ram = new Memory(MEMORY_BASE, MEMORY_SIZE, false);
+ bus.addCpu(cpu);
+ bus.addDevice(ram);
+ bus.addDevice(via);
+ bus.addDevice(acia);
+
// TODO: Make this configurable, of course.
File romImage = new File("rom.bin");
if (romImage.canRead()) {
logger.info("Loading ROM image from file " + romImage);
this.rom = Memory.makeROM(ROM_BASE, ROM_SIZE, romImage);
- } else {
- logger.info("No ROM file 'rom.bin' found. ROM image will be empty R/W memory.");
- // Just make it a normal RAM image.
- this.rom = Memory.makeRAM(ROM_BASE, ROM_SIZE);
+ bus.addDevice(rom);
}
-
- bus.addCpu(cpu);
- bus.addDevice(ram);
- bus.addDevice(acia);
- bus.addDevice(rom);
}
/**
@@ -124,7 +161,7 @@ public class Simulator implements ActionListener, Observer {
this.statusPane = new StatusPanel();
// File Chooser
- fileChooser = new JFileChooser();
+ fileChooser = new JFileChooser(System.getProperty("user.dir"));
preferences = new PreferencesDialog(mainWindow, true);
preferences.addObserver(this);
@@ -177,8 +214,11 @@ public class Simulator implements ActionListener, Observer {
menuBar.add(fileMenu);
- loadMenuItem = new JMenuItem("Load Program");
- loadMenuItem.setMnemonic(KeyEvent.VK_L);
+ loadProgramItem = new JMenuItem("Load Program");
+ loadProgramItem.setMnemonic(KeyEvent.VK_L);
+
+ loadRomItem = new JMenuItem("Load ROM...");
+ loadRomItem.setMnemonic(KeyEvent.VK_R);
JMenuItem prefsItem = new JMenuItem("Preferences...");
prefsItem.setMnemonic(KeyEvent.VK_P);
@@ -186,12 +226,18 @@ public class Simulator implements ActionListener, Observer {
JMenuItem quitItem = new JMenuItem("Quit");
quitItem.setMnemonic(KeyEvent.VK_Q);
- loadMenuItem.addActionListener(new ActionListener() {
+ loadProgramItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
handleProgramLoad();
}
});
+ loadRomItem.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent actionEvent) {
+ handleRomLoad();
+ }
+ });
+
prefsItem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
showAndUpdatePreferences();
@@ -204,7 +250,8 @@ public class Simulator implements ActionListener, Observer {
}
});
- fileMenu.add(loadMenuItem);
+ fileMenu.add(loadProgramItem);
+ fileMenu.add(loadRomItem);
fileMenu.add(prefsItem);
fileMenu.add(quitItem);
@@ -224,20 +271,79 @@ public class Simulator implements ActionListener, Observer {
} else if (actionEvent.getSource() == stepButton) {
handleStep();
} else if (actionEvent.getSource() == runStopButton) {
- // Shift focus to the console.
- console.requestFocus();
if (runLoop != null && runLoop.isRunning()) {
- runLoop.requestStop();
- runLoop.interrupt();
- runLoop = null;
+ handleStop();
} else {
- // Spin up the new run loop
- runLoop = new RunLoop();
- runLoop.start();
+ handleStart();
}
}
}
+ private void handleStart() {
+ // Shift focus to the console.
+ console.requestFocus();
+ // Spin up the new run loop
+ runLoop = new RunLoop();
+ runLoop.start();
+ }
+
+ private void handleStop() {
+ runLoop.requestStop();
+ runLoop.interrupt();
+ runLoop = null;
+ simulatorDidStop();
+ }
+
+ /*
+ * Handle post-stop actions.
+ */
+ private void simulatorDidStop() {
+ // The simulator is lazy about updating the UI for performance reasons, so always request an
+ // immediate update after stopping.
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ // Now update the state
+ statusPane.updateState(cpu);
+ }
+ });
+
+ // TODO: Write to Log window, if frame is visible.
+ // TODO: Update memory window, if frame is visible.
+ }
+
+ // TODO: Alert user of errors.
+ private void handleRomLoad() {
+ try {
+ int retVal = fileChooser.showOpenDialog(mainWindow);
+ if (retVal == JFileChooser.APPROVE_OPTION) {
+ File romFile = fileChooser.getSelectedFile();
+ if (romFile.canRead()) {
+ long fileSize = romFile.length();
+
+ if (fileSize != ROM_SIZE) {
+ throw new IOException("ROM file must be exactly " + String.valueOf(fileSize) + " bytes.");
+ } else {
+ if (rom != null) {
+ // Unload the existing ROM image.
+ bus.removeDevice(rom);
+ }
+ // Load the new ROM image
+ bus.addDevice(Memory.makeROM(ROM_BASE, ROM_SIZE, romFile));
+
+ logger.log(Level.INFO, "ROM File `" + romFile.getName() + "' loaded at " +
+ String.format("0x%04X", ROM_BASE));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ logger.log(Level.SEVERE, "Unable to read file: " + ex.getMessage());
+ ex.printStackTrace();
+ } catch (MemoryRangeException ex) {
+ logger.log(Level.SEVERE, "Memory range error loading ROM");
+ ex.printStackTrace();
+ }
+ }
+
/**
* Display a file chooser prompting the user to load a binary program.
* After the user selects a file, read it in starting at PROGRAM_START_ADDRESS.
@@ -315,15 +421,7 @@ public class Simulator implements ActionListener, Observer {
private void handleStep() {
try {
step();
-
- // The simulator is lazy about updating the UI for performance reasons, so always request an
- // immediate update after stepping manually.
- SwingUtilities.invokeLater(new Runnable() {
- public void run() {
- // Now update the state
- statusPane.updateState(cpu);
- }
- });
+ simulatorDidStop();
} catch (SymonException ex) {
logger.log(Level.SEVERE, "Exception during simulator step: " + ex.getMessage());
ex.printStackTrace();
@@ -466,9 +564,12 @@ public class Simulator implements ActionListener, Observer {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
+ // Tell the console to start handling key presses
+ console.startListening();
// Don't allow step while the simulator is running
stepButton.setEnabled(false);
- loadMenuItem.setEnabled(false);
+ loadProgramItem.setEnabled(false);
+ loadRomItem.setEnabled(false);
// Toggle the state of the run button
runStopButton.setText("Stop");
}
@@ -486,9 +587,11 @@ public class Simulator implements ActionListener, Observer {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
+ console.stopListening();
// Allow step while the simulator is stopped
stepButton.setEnabled(true);
- loadMenuItem.setEnabled(true);
+ loadProgramItem.setEnabled(true);
+ loadRomItem.setEnabled(true);
runStopButton.setText("Run");
// Now update the state
statusPane.updateState(cpu);
diff --git a/src/main/java/com/loomcom/symon/devices/Via.java b/src/main/java/com/loomcom/symon/devices/Via.java
new file mode 100644
index 0000000..9cf7d34
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/devices/Via.java
@@ -0,0 +1,44 @@
+package com.loomcom.symon.devices;
+
+import com.loomcom.symon.exceptions.MemoryAccessException;
+import com.loomcom.symon.exceptions.MemoryRangeException;
+
+public class Via extends Device {
+ public static final int VIA_SIZE = 16;
+
+ private static final int ORB = 0;
+ private static final int ORA = 1;
+ private static final int DDRB = 2;
+ private static final int DDRA = 3;
+ private static final int T1C_L = 4;
+ private static final int T1C_H = 5;
+ private static final int T1L_L = 6;
+ private static final int T1L_H = 7;
+ private static final int T2C_L = 8;
+ private static final int T2C_H = 9;
+ private static final int SR = 10;
+ private static final int ACR = 11;
+ private static final int PCR = 12;
+ private static final int IFR = 13;
+ private static final int IER = 14;
+ private static final int ORA_H = 15;
+
+ public Via(int address) throws MemoryRangeException {
+ super(address, VIA_SIZE, "VIA");
+ }
+
+ @Override
+ public void write(int address, int data) throws MemoryAccessException {
+
+ }
+
+ @Override
+ public int read(int address) throws MemoryAccessException {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ return null;
+ }
+}
diff --git a/src/main/java/com/loomcom/symon/ui/Console.java b/src/main/java/com/loomcom/symon/ui/Console.java
index 1613b68..370b697 100644
--- a/src/main/java/com/loomcom/symon/ui/Console.java
+++ b/src/main/java/com/loomcom/symon/ui/Console.java
@@ -4,6 +4,8 @@ import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import com.grahamedgecombe.jterminal.JTerminal;
import com.grahamedgecombe.jterminal.vt100.Vt100TerminalModel;
@@ -21,15 +23,18 @@ import javax.swing.border.Border;
public class Console extends JTerminal implements KeyListener, MouseListener {
- private static final int DEFAULT_COLUMNS = 80;
- private static final int DEFAULT_ROWS = 24;
- private static final int DEFAULT_BORDER_WIDTH = 10;
- // If true, swap CR and LF characters
- private static final boolean SWAP_CR_AND_LF = true;
+ private static final int DEFAULT_COLUMNS = 80;
+ private static final int DEFAULT_ROWS = 24;
+ private static final int DEFAULT_BORDER_WIDTH = 10;
+ // If true, swap CR and LF characters.
+ private static final boolean SWAP_CR_AND_LF = true;
// If true, send CRLF (0x0d 0x0a) whenever CR is typed
- private static final boolean SEND_CR_LF_FOR_CR = false;
+ private static final boolean SEND_CR_LF_FOR_CR = false;
- private FifoRingBuffer typeAheadBuffer;
+ // If true, the console is actively listening for key input.
+ private boolean isListening;
+
+ private FifoRingBuffer typeAheadBuffer;
public Console() {
this(DEFAULT_COLUMNS, DEFAULT_ROWS);
@@ -39,7 +44,8 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
super(new Vt100TerminalModel(columns, rows));
// A small type-ahead buffer, as might be found in any real
// VT100-style serial terminal.
- this.typeAheadBuffer = new FifoRingBuffer(128);
+ this.typeAheadBuffer = new FifoRingBuffer(128);
+ this.isListening = false;
setBorderWidth(DEFAULT_BORDER_WIDTH);
addKeyListener(this);
addMouseListener(this);
@@ -52,7 +58,6 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
/**
* Reset the console. This will cause the console to be cleared and the cursor returned to the
* home position.
- *
*/
public void reset() {
typeAheadBuffer.reset();
@@ -62,6 +67,14 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
repaint();
}
+ public void startListening() {
+ this.isListening = true;
+ }
+
+ public void stopListening() {
+ this.isListening = false;
+ }
+
/**
* Returns true if a key has been pressed since the last time input was read.
*
@@ -77,22 +90,23 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @param keyEvent The key event.
*/
public void keyTyped(KeyEvent keyEvent) {
- char keyTyped = keyEvent.getKeyChar();
+ if (isListening) {
+ char keyTyped = keyEvent.getKeyChar();
- if (SWAP_CR_AND_LF) {
- if (keyTyped == 0x0a) {
- keyTyped = 0x0d;
+ if (SWAP_CR_AND_LF) {
+ if (keyTyped == 0x0a) {
+ keyTyped = 0x0d;
+ } else if (keyTyped == 0x0d) {
+ keyTyped = 0x0a;
+ }
}
- else if (keyTyped == 0x0d) {
- keyTyped = 0x0a;
- }
- }
- if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) {
- typeAheadBuffer.push(0x0d);
- typeAheadBuffer.push(0x0a);
- } else {
- typeAheadBuffer.push(keyTyped);
+ if (SEND_CR_LF_FOR_CR && keyTyped == 0x0d) {
+ typeAheadBuffer.push((char) 0x0d);
+ typeAheadBuffer.push((char) 0x0a);
+ } else {
+ typeAheadBuffer.push(keyTyped);
+ }
}
keyEvent.consume();
@@ -113,7 +127,7 @@ public class Console extends JTerminal implements KeyListener, MouseListener {
* @return The character typed.
*/
public char readInputChar() throws FifoUnderrunException {
- return (char)typeAheadBuffer.pop();
+ return typeAheadBuffer.pop();
}
/**
diff --git a/src/main/java/com/loomcom/symon/ui/TraceLog.java b/src/main/java/com/loomcom/symon/ui/TraceLog.java
new file mode 100644
index 0000000..4d32732
--- /dev/null
+++ b/src/main/java/com/loomcom/symon/ui/TraceLog.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2008-2012 Seth J. Morabito
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ */
+
+package com.loomcom.symon.ui;
+
+import com.loomcom.symon.util.FifoRingBuffer;
+
+import javax.swing.*;
+
+/**
+ * This frame displays a trace of CPU execution. The most recent TRACE_LENGTH lines
+ * are captured in a buffer and rendered to the JFrame's main text area upon request.
+ */
+public class TraceLog extends JFrame {
+
+ private FifoRingBuffer traceLog;
+
+
+}
diff --git a/src/main/java/com/loomcom/symon/util/FifoRingBuffer.java b/src/main/java/com/loomcom/symon/util/FifoRingBuffer.java
index 52d193a..c130b51 100644
--- a/src/main/java/com/loomcom/symon/util/FifoRingBuffer.java
+++ b/src/main/java/com/loomcom/symon/util/FifoRingBuffer.java
@@ -2,77 +2,57 @@ package com.loomcom.symon.util;
import com.loomcom.symon.exceptions.*;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Queue;
+
/**
- * A very simple and efficient FIFO ring buffer implementation backed
- * by an array. It can only hold only integers.
+ * A FIFO buffer with a bounded maximum size.
*/
-public class FifoRingBuffer {
+public class FifoRingBuffer implements Iterable {
- private int[] fifoBuffer;
- private int readPtr = 0;
- private int writePtr = 0;
- private int size = 0;
+ private Queue fifoBuffer;
+ private int maxLength;
- public FifoRingBuffer(int size) {
- if (size <= 0) {
- throw new RuntimeException("Cannot create a FifoRingBuffer with size <= 0.");
+ public FifoRingBuffer(int maxLength) {
+ this.fifoBuffer = new LinkedList();
+ this.maxLength = maxLength;
}
- this.size = size;
- fifoBuffer = new int[size];
- }
- public int peek() throws FifoUnderrunException {
- if (isEmpty()) {
- throw new FifoUnderrunException("Buffer Underrun");
+ public E pop() throws FifoUnderrunException {
+ return fifoBuffer.remove();
}
- return fifoBuffer[readPtr];
- }
- public int pop() throws FifoUnderrunException {
- if (isEmpty()) {
- throw new FifoUnderrunException("Buffer Underrun");
+ public boolean isEmpty() {
+ return fifoBuffer.isEmpty();
}
- int val = fifoBuffer[readPtr];
- incrementReadPointer();
- return val;
- }
- public boolean isEmpty() {
- return(readPtr == writePtr);
- }
-
- public boolean isFull() {
- return((readPtr == 0 && writePtr == (size - 1)) ||
- writePtr == (readPtr - 1));
- }
-
- public void push(int val) {
- fifoBuffer[writePtr] = val;
- incrementWritePointer();
- }
-
- public void reset() {
- readPtr = 0;
- writePtr = 0;
- }
-
- private void incrementWritePointer() {
- if (++writePtr == size) {
- writePtr = 0;
+ public void push(E val) {
+ if (fifoBuffer.size() == maxLength) {
+ // Delete the oldest element.
+ fifoBuffer.remove();
+ }
+ fifoBuffer.offer(val);
}
- if (writePtr == readPtr) {
- incrementReadPointer();
- }
- }
- private void incrementReadPointer() {
- if (++readPtr == size) {
- readPtr = 0;
+ public E peek() {
+ return fifoBuffer.peek();
}
- }
- public String toString() {
- return "[FifoRingBuffer: size=" + size + "]";
- }
+ public void reset() {
+ fifoBuffer.clear();
+ }
+
+ public int length() {
+ return fifoBuffer.size();
+ }
+
+ public String toString() {
+ return "[FifoRingBuffer: size=" + fifoBuffer.size() + "]";
+ }
+
+ public Iterator iterator() {
+ return fifoBuffer.iterator();
+ }
}
diff --git a/src/test/java/com/loomcom/symon/CpuAbsoluteModeTest.java b/src/test/java/com/loomcom/symon/CpuAbsoluteModeTest.java
index 0b98465..ea63c58 100644
--- a/src/test/java/com/loomcom/symon/CpuAbsoluteModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuAbsoluteModeTest.java
@@ -19,8 +19,8 @@ public class CpuAbsoluteModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuAbsoluteXModeTest.java b/src/test/java/com/loomcom/symon/CpuAbsoluteXModeTest.java
index 77d9dcd..842f4b2 100644
--- a/src/test/java/com/loomcom/symon/CpuAbsoluteXModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuAbsoluteXModeTest.java
@@ -19,8 +19,8 @@ public class CpuAbsoluteXModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuAbsoluteYModeTest.java b/src/test/java/com/loomcom/symon/CpuAbsoluteYModeTest.java
index 17a1850..8521ab0 100644
--- a/src/test/java/com/loomcom/symon/CpuAbsoluteYModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuAbsoluteYModeTest.java
@@ -19,8 +19,8 @@ public class CpuAbsoluteYModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java b/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java
index 5085b59..5edc066 100644
--- a/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuAccumulatorModeTest.java
@@ -19,8 +19,8 @@ public class CpuAccumulatorModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset();
diff --git a/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java b/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java
index 2fba63f..628a6ec 100644
--- a/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java
@@ -19,8 +19,8 @@ public class CpuImmediateModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuImpliedModeTest.java b/src/test/java/com/loomcom/symon/CpuImpliedModeTest.java
index 1489bfa..5950656 100644
--- a/src/test/java/com/loomcom/symon/CpuImpliedModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuImpliedModeTest.java
@@ -19,8 +19,8 @@ public class CpuImpliedModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset();
diff --git a/src/test/java/com/loomcom/symon/CpuIndexedIndirectModeTest.java b/src/test/java/com/loomcom/symon/CpuIndexedIndirectModeTest.java
index 5885ec0..c6bf2b6 100644
--- a/src/test/java/com/loomcom/symon/CpuIndexedIndirectModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuIndexedIndirectModeTest.java
@@ -20,8 +20,8 @@ public class CpuIndexedIndirectModeTest {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java b/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java
index 3f40542..8238902 100644
--- a/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuIndirectIndexedModeTest.java
@@ -20,8 +20,8 @@ public class CpuIndirectIndexedModeTest {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java b/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java
index 34423b6..b25ea1a 100644
--- a/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuIndirectModeTest.java
@@ -2,53 +2,102 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.MemoryAccessException;
+import com.loomcom.symon.InstructionTable;
import junit.framework.TestCase;
public class CpuIndirectModeTest extends TestCase {
- protected Cpu cpu;
- protected Bus bus;
- protected Memory mem;
+ protected Cpu cpu;
+ protected Bus bus;
+ protected Memory mem;
- protected void setUp() throws Exception {
- this.cpu = new Cpu();
- this.bus = new Bus(0x0000, 0xffff);
- this.mem = new Memory(0x0000, 0x10000);
- bus.addCpu(cpu);
- bus.addDevice(mem);
+ protected void setUp() throws Exception {
+ this.cpu = new Cpu();
+ this.bus = new Bus(0x0000, 0xffff);
+ this.mem = new Memory(0x0000, 0x10000);
+ bus.addCpu(cpu);
+ bus.addDevice(mem);
- // Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
+ // Load the reset vector.
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
- cpu.reset();
- // Assert initial state
- assertEquals(0, cpu.getAccumulator());
- assertEquals(0, cpu.getXRegister());
- assertEquals(0, cpu.getYRegister());
- assertEquals(0x200, cpu.getProgramCounter());
- assertEquals(0xff, cpu.getStackPointer());
- assertEquals(0x20, cpu.getProcessorStatus());
- }
+ cpu.reset();
+ // Assert initial state
+ assertEquals(0, cpu.getAccumulator());
+ assertEquals(0, cpu.getXRegister());
+ assertEquals(0, cpu.getYRegister());
+ assertEquals(0x200, cpu.getProgramCounter());
+ assertEquals(0xff, cpu.getStackPointer());
+ assertEquals(0x20, cpu.getProcessorStatus());
+ }
- /*
- * The following opcodes are tested for correctness in this file:
- *
- * JMP - $6c
- *
- */
+ /*
+ * The following opcodes are tested for correctness in this file:
+ *
+ * JMP - $6c
+ *
+ */
- /* JMP - Jump - $6c */
+ /* JMP - Jump - $6c */
- public void test_JMP() throws MemoryAccessException {
- bus.write(0x3400, 0x00);
- bus.write(0x3401, 0x54);
- bus.loadProgram(0x6c, 0x00, 0x34);
- cpu.step();
- assertEquals(0x5400, cpu.getProgramCounter());
- // No change to status flags.
- assertEquals(0x20, cpu.getProcessorStatus());
- }
+ public void test_JMP_notOnPageBoundary() throws MemoryAccessException {
+ bus.write(0x3400, 0x00);
+ bus.write(0x3401, 0x54);
+ bus.loadProgram(0x6c, 0x00, 0x34);
+ cpu.step();
+ assertEquals(0x5400, cpu.getProgramCounter());
+ // No change to status flags.
+ assertEquals(0x20, cpu.getProcessorStatus());
+ }
+
+ public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
+ cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG);
+ bus.write(0x3400, 0x22);
+ bus.write(0x34ff, 0x00);
+ bus.write(0x3500, 0x54);
+ bus.loadProgram(0x6c, 0xff, 0x34);
+ cpu.step();
+ assertEquals(0x2200, cpu.getProgramCounter());
+ // No change to status flags.
+ assertEquals(0x20, cpu.getProcessorStatus());
+ }
+
+ public void test_JMP_withIndirectBug() throws MemoryAccessException {
+ cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG);
+ bus.write(0x3400, 0x22);
+ bus.write(0x34ff, 0x00);
+ bus.write(0x3500, 0x54);
+ bus.loadProgram(0x6c, 0xff, 0x34);
+ cpu.step();
+ assertEquals(0x2200, cpu.getProgramCounter());
+ // No change to status flags.
+ assertEquals(0x20, cpu.getProcessorStatus());
+ }
+
+ public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
+ cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITHOUT_INDIRECT_JMP_BUG);
+ bus.write(0x3400, 0x22);
+ bus.write(0x34ff, 0x00);
+ bus.write(0x3500, 0x54);
+ bus.loadProgram(0x6c, 0xff, 0x34);
+ cpu.step();
+ assertEquals(0x5400, cpu.getProgramCounter());
+ // No change to status flags.
+ assertEquals(0x20, cpu.getProcessorStatus());
+ }
+
+ public void test_JMP_cmos() throws MemoryAccessException {
+ cpu.setBehavior(Cpu.CpuBehavior.CMOS);
+ bus.write(0x3400, 0x22);
+ bus.write(0x34ff, 0x00);
+ bus.write(0x3500, 0x54);
+ bus.loadProgram(0x6c, 0xff, 0x34);
+ cpu.step();
+ assertEquals(0x5400, cpu.getProgramCounter());
+ // No change to status flags.
+ assertEquals(0x20, cpu.getProcessorStatus());
+ }
}
\ No newline at end of file
diff --git a/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java b/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java
index e66783c..92fcdaa 100644
--- a/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuIndirectXModeTest.java
@@ -18,8 +18,8 @@ public class CpuIndirectXModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuRelativeModeTest.java b/src/test/java/com/loomcom/symon/CpuRelativeModeTest.java
index f1cf407..baf6cc4 100644
--- a/src/test/java/com/loomcom/symon/CpuRelativeModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuRelativeModeTest.java
@@ -19,8 +19,8 @@ public class CpuRelativeModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00)>>>8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuZeroPageModeTest.java b/src/test/java/com/loomcom/symon/CpuZeroPageModeTest.java
index 247ed05..9ba2691 100644
--- a/src/test/java/com/loomcom/symon/CpuZeroPageModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuZeroPageModeTest.java
@@ -19,8 +19,8 @@ public class CpuZeroPageModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuZeroPageXModeTest.java b/src/test/java/com/loomcom/symon/CpuZeroPageXModeTest.java
index 0e651f0..b612d93 100644
--- a/src/test/java/com/loomcom/symon/CpuZeroPageXModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuZeroPageXModeTest.java
@@ -19,8 +19,8 @@ public class CpuZeroPageXModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/CpuZeroPageYModeTest.java b/src/test/java/com/loomcom/symon/CpuZeroPageYModeTest.java
index 161e86a..ed23b98 100644
--- a/src/test/java/com/loomcom/symon/CpuZeroPageYModeTest.java
+++ b/src/test/java/com/loomcom/symon/CpuZeroPageYModeTest.java
@@ -19,8 +19,8 @@ public class CpuZeroPageYModeTest extends TestCase {
bus.addDevice(mem);
// Load the reset vector.
- bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
- bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00) >>> 8);
+ bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
+ bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
// Assert initial state
diff --git a/src/test/java/com/loomcom/symon/FifoRingBufferTest.java b/src/test/java/com/loomcom/symon/FifoRingBufferTest.java
new file mode 100644
index 0000000..decc26c
--- /dev/null
+++ b/src/test/java/com/loomcom/symon/FifoRingBufferTest.java
@@ -0,0 +1,39 @@
+package com.loomcom.symon;
+
+import com.loomcom.symon.util.FifoRingBuffer;
+import junit.framework.TestCase;
+import java.lang.Character;
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: seth
+ * Date: 11/22/12
+ * Time: 11:28 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class FifoRingBufferTest extends TestCase {
+ public void testRingBufferShouldDeleteOldestItemIfAtMaximum() {
+ FifoRingBuffer buffer = new FifoRingBuffer(3);
+
+ assertEquals(0, buffer.length());
+ buffer.push('a');
+ assertEquals(1, buffer.length());
+ assertTrue('a' == buffer.peek());
+
+ buffer.push('b');
+ assertEquals(2, buffer.length());
+ assertTrue('a' == buffer.peek());
+
+ buffer.push('c');
+ assertEquals(3, buffer.length());
+ assertTrue('a' == buffer.peek());
+
+ buffer.push('d');
+ assertEquals(3, buffer.length());
+ assertTrue('b' == buffer.peek());
+
+ buffer.push('e');
+ assertEquals(3, buffer.length());
+ assertTrue('c' == buffer.peek());
+ }
+}