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