Compare commits

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

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

This means, at least for the time being, no more Lambda expressions
(Boooooooooooooooo!!)
2016-01-08 19:11:42 -08:00
Matt Harlum 8335cf5421 Correct BRK behaviour
IRQ/NMI clear the BRK flag
BRK is Non-Maskable
2015-06-01 08:58:17 +10:00
25 changed files with 944 additions and 337 deletions

No files matched your search

+5 -2
View File
@@ -1,9 +1,9 @@
SYMON - A 6502 System Simulator SYMON - A 6502 System Simulator
=============================== ===============================
**Version:** 1.2.0 **Version:** 1.2.1
**Last Updated:** 3 January, 2016 **Last Updated:** 8 January, 2016
See the file COPYING for license. See the file COPYING for license.
@@ -235,6 +235,9 @@ running.
## 5.0 Revision History ## 5.0 Revision History
- **1.2.1:** 8 January, 2016 - Remove dependency on Java 8. Now
supports compiling and running under Java 1.7.
- **1.2.0:** 3 January, 2016 - Add symbolic disassembly to breakpoints - **1.2.0:** 3 January, 2016 - Add symbolic disassembly to breakpoints
window. window.
+3 -3
View File
@@ -4,7 +4,7 @@
<groupId>com.loomcom.symon</groupId> <groupId>com.loomcom.symon</groupId>
<artifactId>symon</artifactId> <artifactId>symon</artifactId>
<packaging>jar</packaging> <packaging>jar</packaging>
<version>1.2.0</version> <version>1.3.0-SNAPSHOT</version>
<name>symon</name> <name>symon</name>
<url>http://www.loomcom.com/symon</url> <url>http://www.loomcom.com/symon</url>
<properties> <properties>
@@ -91,8 +91,8 @@
<version>3.1</version> <version>3.1</version>
<configuration> <configuration>
<compilerArgument>-Xlint:unchecked</compilerArgument> <compilerArgument>-Xlint:unchecked</compilerArgument>
<source>1.8</source> <source>1.7</source>
<target>1.8</target> <target>1.7</target>
</configuration> </configuration>
</plugin> </plugin>
+1 -1
View File
@@ -7,7 +7,7 @@ echo_irq: echo_irq.o
$(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o $(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o
echo_irq.o: echo_irq.o:
$(CA) --listing -o echo_irq.o echo_irq.asm $(CA) --listing echo_irq.lst -o echo_irq.o echo_irq.asm
clean: clean:
rm -f *.o *.rom *.map *.lst rm -f *.o *.rom *.map *.lst
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY { MEMORY {
RAM1: start = $0000, size = $8000; RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3FFA, fill = yes; ROM1: start = $C000, size = $3FFA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes; ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
} }
@@ -11,6 +11,6 @@ VECTORS: load = ROMV, type = ro;
} }
SYMBOLS { SYMBOLS {
__STACKSIZE__ = $0300; __STACKSIZE__: type = weak, value = $0300;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ ehbasic: ehbasic.o
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o $(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
ehbasic.o: ehbasic.o:
$(CA) --listing -o ehbasic.o min_mon.asm $(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
clean: clean:
rm -f *.o *.rom *.map *.lst rm -f *.o *.rom *.map *.lst
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY { MEMORY {
RAM1: start = $0000, size = $8000; RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3F00, fill = yes; ROM1: start = $C000, size = $3F00, fill = yes;
MONITOR: start = $FF00, size = $FA, fill = yes; MONITOR: start = $FF00, size = $FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes; ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
@@ -13,6 +13,6 @@ VECTORS: load = ROMV, type = ro;
} }
SYMBOLS { SYMBOLS {
__STACKSIZE__ = $0300; __STACKSIZE__: type = weak, value = $0300;
} }
+1 -1
View File
@@ -7,7 +7,7 @@ ehbasic: ehbasic.o
$(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o $(LD) -C symon.config -vm -m ehbasic.map -o ehbasic.rom ehbasic.o
ehbasic.o: ehbasic.o:
$(CA) --listing -o ehbasic.o min_mon.asm $(CA) --listing ehbasic.lst -o ehbasic.o min_mon.asm
clean: clean:
rm -f *.o *.rom *.map *.lst rm -f *.o *.rom *.map *.lst
+2 -2
View File
@@ -1,5 +1,5 @@
MEMORY { MEMORY {
RAM1: start = $0000, size = $8000; RAM1: start = $0000, size = $8000 - __STACKSIZE__;
ROM1: start = $C000, size = $3C00, fill = yes; ROM1: start = $C000, size = $3C00, fill = yes;
MONITOR: start = $FC00, size = $3FA, fill = yes; MONITOR: start = $FC00, size = $3FA, fill = yes;
ROMV: start = $FFFA, size = $6, file = %O, fill = yes; ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
@@ -13,6 +13,6 @@ VECTORS: load = ROMV, type = ro;
} }
SYMBOLS { SYMBOLS {
__STACKSIZE__ = $0300; __STACKSIZE__: type = weak, value = $0300;
} }
+31 -29
View File
@@ -26,6 +26,7 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Device; import com.loomcom.symon.devices.Device;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException; import com.loomcom.symon.exceptions.MemoryRangeException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@@ -34,7 +35,6 @@ import java.util.Map;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.stream.Collectors;
/** /**
* The Bus ties the whole thing together, man. * The Bus ties the whole thing together, man.
@@ -43,7 +43,7 @@ public class Bus {
// The default address at which to load programs // The default address at which to load programs
public static int DEFAULT_LOAD_ADDRESS = 0x0200; public static int DEFAULT_LOAD_ADDRESS = 0x0200;
// By default, our bus starts at 0, and goes up to 64K // By default, our bus starts at 0, and goes up to 64K
private int startAddress = 0x0000; private int startAddress = 0x0000;
private int endAddress = 0xffff; private int endAddress = 0xffff;
@@ -53,10 +53,10 @@ public class Bus {
// Ordered sets of IO devices, associated with their priority // Ordered sets of IO devices, associated with their priority
private Map<Integer, SortedSet<Device>> deviceMap; private Map<Integer, SortedSet<Device>> deviceMap;
// an array for quick lookup of adresses, brute-force style // an array for quick lookup of adresses, brute-force style
private Device[] deviceAddressArray; private Device[] deviceAddressArray;
public Bus(int size) { public Bus(int size) {
this(0, size - 1); this(0, size - 1);
@@ -75,30 +75,30 @@ public class Bus {
public int endAddress() { public int endAddress() {
return endAddress; return endAddress;
} }
private void buildDeviceAddressArray() { private void buildDeviceAddressArray() {
int size = (this.endAddress - this.startAddress) + 1; int size = (this.endAddress - this.startAddress) + 1;
deviceAddressArray = new Device[size]; deviceAddressArray = new Device[size];
// getDevices() provides an OrderedSet with devices ordered by priorities // getDevices() provides an OrderedSet with devices ordered by priorities
for(Device device : getDevices()) { for (Device device : getDevices()) {
MemoryRange range = device.getMemoryRange(); MemoryRange range = device.getMemoryRange();
for(int address = range.startAddress; address <= range.endAddress; ++address) { for (int address = range.startAddress; address <= range.endAddress; ++address) {
deviceAddressArray[address - this.startAddress] = device; deviceAddressArray[address - this.startAddress] = device;
} }
} }
} }
/** /**
* Add a device to the bus. * Add a device to the bus.
* *
* @param device Device to add * @param device Device to add
* @param priority Bus prioirity. * @param priority Bus prioirity.
* @throws MemoryRangeException * @throws MemoryRangeException
*/ */
public void addDevice(Device device, int priority) throws MemoryRangeException { public void addDevice(Device device, int priority) throws MemoryRangeException {
MemoryRange range = device.getMemoryRange(); MemoryRange range = device.getMemoryRange();
if (range.startAddress() < this.startAddress || range.startAddress() > this.endAddress) { if (range.startAddress() < this.startAddress || range.startAddress() > this.endAddress) {
@@ -115,12 +115,12 @@ public class Bus {
deviceSet = new TreeSet<>(); deviceSet = new TreeSet<>();
deviceMap.put(priority, deviceSet); deviceMap.put(priority, deviceSet);
} }
device.setBus(this); device.setBus(this);
deviceSet.add(device); deviceSet.add(device);
buildDeviceAddressArray(); buildDeviceAddressArray();
} }
/** /**
* Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others. * Add a device to the bus. Throws a MemoryRangeException if the device overlaps with any others.
* *
@@ -130,7 +130,7 @@ public class Bus {
public void addDevice(Device device) throws MemoryRangeException { public void addDevice(Device device) throws MemoryRangeException {
addDevice(device, 0); addDevice(device, 0);
} }
/** /**
* Remove a device from the bus. * Remove a device from the bus.
@@ -138,7 +138,7 @@ public class Bus {
* @param device Device to remove * @param device Device to remove
*/ */
public void removeDevice(Device device) { public void removeDevice(Device device) {
for(SortedSet<Device> deviceSet : deviceMap.values()) { for (SortedSet<Device> deviceSet : deviceMap.values()) {
deviceSet.remove(device); deviceSet.remove(device);
} }
buildDeviceAddressArray(); buildDeviceAddressArray();
@@ -155,39 +155,39 @@ public class Bus {
* device. * device.
*/ */
public boolean isComplete() { public boolean isComplete() {
if(deviceAddressArray == null) { if (deviceAddressArray == null) {
buildDeviceAddressArray(); buildDeviceAddressArray();
} }
for(int address = startAddress; address <= endAddress; ++address) { for (int address = startAddress; address <= endAddress; ++address) {
if(deviceAddressArray[address - startAddress] == null) { if (deviceAddressArray[address - startAddress] == null) {
return false; return false;
} }
} }
return true; return true;
} }
public int read(int address) throws MemoryAccessException { public int read(int address) throws MemoryAccessException {
Device d = deviceAddressArray[address - this.startAddress]; Device d = deviceAddressArray[address - this.startAddress];
if(d != null) { if (d != null) {
MemoryRange range = d.getMemoryRange(); MemoryRange range = d.getMemoryRange();
int devAddr = address - range.startAddress(); int devAddr = address - range.startAddress();
return d.read(devAddr) & 0xff; return d.read(devAddr) & 0xff;
} }
throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address)); throw new MemoryAccessException("Bus read failed. No device at address " + String.format("$%04X", address));
} }
public void write(int address, int value) throws MemoryAccessException { public void write(int address, int value) throws MemoryAccessException {
Device d = deviceAddressArray[address - this.startAddress]; Device d = deviceAddressArray[address - this.startAddress];
if(d != null) { if (d != null) {
MemoryRange range = d.getMemoryRange(); MemoryRange range = d.getMemoryRange();
int devAddr = address - range.startAddress(); int devAddr = address - range.startAddress();
d.write(devAddr, value); d.write(devAddr, value);
return; return;
} }
throw new MemoryAccessException("Bus write failed. No device at address " + String.format("$%04X", address)); throw new MemoryAccessException("Bus write failed. No device at address " + String.format("$%04X", address));
} }
@@ -218,15 +218,17 @@ public class Bus {
public SortedSet<Device> getDevices() { public SortedSet<Device> getDevices() {
// create an ordered set of devices, ordered by device priorities // create an ordered set of devices, ordered by device priorities
SortedSet<Device> devices = new TreeSet<>(); SortedSet<Device> devices = new TreeSet<>();
List<Integer> priorities = new ArrayList<>(deviceMap.keySet()); List<Integer> priorities = new ArrayList<>(deviceMap.keySet());
Collections.sort(priorities); Collections.sort(priorities);
for(int priority : priorities) { for (int priority : priorities) {
SortedSet<Device> deviceSet = deviceMap.get(priority); SortedSet<Device> deviceSet = deviceMap.get(priority);
devices.addAll(deviceSet.stream().collect(Collectors.toList())); for (Device device : deviceSet) {
devices.add(device);
}
} }
return devices; return devices;
} }
+91 -21
View File
@@ -79,7 +79,7 @@ public class Cpu implements InstructionTable {
* Construct a new CPU. * Construct a new CPU.
*/ */
public Cpu() { public Cpu() {
this(CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG); this(CpuBehavior.NMOS_6502);
} }
public Cpu(CpuBehavior behavior) { public Cpu(CpuBehavior behavior) {
@@ -204,6 +204,13 @@ public class Cpu implements InstructionTable {
case 3: // Absolute case 3: // Absolute
effectiveAddress = Utils.address(state.args[0], state.args[1]); effectiveAddress = Utils.address(state.args[0], state.args[1]);
break; break;
case 4: // 65C02 (Zero Page)
if (behavior == CpuBehavior.CMOS_6502 ||
behavior == CpuBehavior.CMOS_65816) {
effectiveAddress = Utils.address(bus.read(state.args[0]),
bus.read((state.args[0] + 1) & 0xff));
}
break;
case 5: // Zero Page,X / Zero Page,Y case 5: // Zero Page,X / Zero Page,Y
if (state.ir == 0x96 || state.ir == 0xb6) { if (state.ir == 0x96 || state.ir == 0xb6) {
effectiveAddress = zpyAddress(state.args[0]); effectiveAddress = zpyAddress(state.args[0]);
@@ -258,13 +265,11 @@ public class Cpu implements InstructionTable {
/** Single Byte Instructions; Implied and Relative **/ /** Single Byte Instructions; Implied and Relative **/
case 0x00: // BRK - Force Interrupt - Implied case 0x00: // BRK - Force Interrupt - Implied
if (!getIrqDisableFlag()) { handleBrk(state.pc + 1);
handleIrq(state.pc + 1);
}
break; break;
case 0x08: // PHP - Push Processor Status - Implied case 0x08: // PHP - Push Processor Status - Implied
// Break flag is always set in the stack value. // Break flag is always set in the stack value.
stackPush(state.getStatusFlag() | 0x10); stackPush(state.getStatusFlag() | 0x10);
break; break;
case 0x10: // BPL - Branch if Positive - Relative case 0x10: // BPL - Branch if Positive - Relative
if (!getNegativeFlag()) { if (!getNegativeFlag()) {
@@ -400,11 +405,12 @@ public class Cpu implements InstructionTable {
case 0x4c: // JMP - Absolute case 0x4c: // JMP - Absolute
state.pc = Utils.address(state.args[0], state.args[1]); state.pc = Utils.address(state.args[0], state.args[1]);
break; break;
// TODO: 65C02 instruction 0x7C, JMP (Indirect,X)
case 0x6c: // JMP - Indirect case 0x6c: // JMP - Indirect
lo = Utils.address(state.args[0], state.args[1]); // Address of low byte lo = Utils.address(state.args[0], state.args[1]); // Address of low byte
if (state.args[0] == 0xff && if (state.args[0] == 0xff &&
(behavior == CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG || (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) { behavior == CpuBehavior.NMOS_WITH_ROR_BUG)) {
hi = Utils.address(0x00, state.args[1]); hi = Utils.address(0x00, state.args[1]);
} else { } else {
@@ -424,13 +430,20 @@ public class Cpu implements InstructionTable {
* (http://www.obelisk.demon.co.uk/6502/reference.html#JMP) * (http://www.obelisk.demon.co.uk/6502/reference.html#JMP)
*/ */
break; break;
case 0x7C: // 65C02 JMP - Absolute,X
state.pc = (Utils.address(state.args[0], state.args[1]) + state.x) & 0xffff;
break;
/** ORA - Logical Inclusive Or ******************************************/ /** ORA - Logical Inclusive Or ******************************************/
case 0x09: // #Immediate case 0x09: // #Immediate
state.a |= state.args[0]; state.a |= state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0x12: // 65C02 ORA (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x01: // (Zero Page,X) case 0x01: // (Zero Page,X)
case 0x05: // Zero Page case 0x05: // Zero Page
case 0x0d: // Absolute case 0x0d: // Absolute
@@ -473,6 +486,11 @@ public class Cpu implements InstructionTable {
state.a &= state.args[0]; state.a &= state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0x32: // 65C02 AND (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x21: // (Zero Page,X) case 0x21: // (Zero Page,X)
case 0x25: // Zero Page case 0x25: // Zero Page
case 0x2d: // Absolute case 0x2d: // Absolute
@@ -505,6 +523,11 @@ public class Cpu implements InstructionTable {
state.a ^= state.args[0]; state.a ^= state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0x52: // 65C02 EOR (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x41: // (Zero Page,X) case 0x41: // (Zero Page,X)
case 0x45: // Zero Page case 0x45: // Zero Page
case 0x4d: // Absolute case 0x4d: // Absolute
@@ -540,6 +563,11 @@ public class Cpu implements InstructionTable {
state.a = adc(state.a, state.args[0]); state.a = adc(state.a, state.args[0]);
} }
break; break;
case 0x72: // 65C02 ADC (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x61: // (Zero Page,X) case 0x61: // (Zero Page,X)
case 0x65: // Zero Page case 0x65: // Zero Page
case 0x6d: // Absolute case 0x6d: // Absolute
@@ -571,6 +599,11 @@ public class Cpu implements InstructionTable {
/** STA - Store Accumulator *********************************************/ /** STA - Store Accumulator *********************************************/
case 0x92: // 65C02 STA (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0x81: // (Zero Page,X) case 0x81: // (Zero Page,X)
case 0x85: // Zero Page case 0x85: // Zero Page
case 0x8d: // Absolute case 0x8d: // Absolute
@@ -631,6 +664,11 @@ public class Cpu implements InstructionTable {
state.a = state.args[0]; state.a = state.args[0];
setArithmeticFlags(state.a); setArithmeticFlags(state.a);
break; break;
case 0xb2: // 65C02 LDA (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0xa1: // (Zero Page,X) case 0xa1: // (Zero Page,X)
case 0xa5: // Zero Page case 0xa5: // Zero Page
case 0xad: // Absolute case 0xad: // Absolute
@@ -657,6 +695,11 @@ public class Cpu implements InstructionTable {
case 0xc9: // #Immediate case 0xc9: // #Immediate
cmp(state.a, state.args[0]); cmp(state.a, state.args[0]);
break; break;
case 0xd2: // 65C02 CMP (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0xc1: // (Zero Page,X) case 0xc1: // (Zero Page,X)
case 0xc5: // Zero Page case 0xc5: // Zero Page
case 0xcd: // Absolute case 0xcd: // Absolute
@@ -697,6 +740,11 @@ public class Cpu implements InstructionTable {
state.a = sbc(state.a, state.args[0]); state.a = sbc(state.a, state.args[0]);
} }
break; break;
case 0xf2: // 65C02 SBC (ZP)
if (behavior == CpuBehavior.NMOS_6502 ||
behavior == CpuBehavior.NMOS_WITH_ROR_BUG) {
break;
}
case 0xe1: // (Zero Page,X) case 0xe1: // (Zero Page,X)
case 0xe5: // Zero Page case 0xe5: // Zero Page
case 0xed: // Absolute case 0xed: // Absolute
@@ -744,13 +792,18 @@ public class Cpu implements InstructionTable {
} }
} }
private void handleBrk(int returnPc) throws MemoryAccessException {
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H, true);
clearIrq();
}
private void handleIrq(int returnPc) throws MemoryAccessException { private void handleIrq(int returnPc) throws MemoryAccessException {
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H); handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H, false);
clearIrq(); clearIrq();
} }
private void handleNmi() throws MemoryAccessException { private void handleNmi() throws MemoryAccessException {
handleInterrupt(state.pc, NMI_VECTOR_L, NMI_VECTOR_H); handleInterrupt(state.pc, NMI_VECTOR_L, NMI_VECTOR_H, false);
clearNmi(); clearNmi();
} }
@@ -759,9 +812,19 @@ public class Cpu implements InstructionTable {
* *
* @throws MemoryAccessException * @throws MemoryAccessException
*/ */
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh) throws MemoryAccessException { private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh, boolean isBreak) throws MemoryAccessException {
// Set the break flag before pushing.
setBreakFlag(); if (isBreak)
{
// Set the break flag before pushing.
setBreakFlag();
}
else
{
// IRQ & NMI clear break flag
clearBreakFlag();
}
// Push program counter + 1 onto the stack // Push program counter + 1 onto the stack
stackPush((returnPc >> 8) & 0xff); // PC high byte stackPush((returnPc >> 8) & 0xff); // PC high byte
stackPush(returnPc & 0xff); // PC low byte stackPush(returnPc & 0xff); // PC low byte
@@ -1321,6 +1384,13 @@ public class Cpu implements InstructionTable {
return (zp + state.x) & 0xff; return (zp + state.x) & 0xff;
} }
/**
* Given a single byte, compute the Zero Page,Y offset address.
*/
int zpyAddress(int zp) {
return (zp + state.y) & 0xff;
}
/** /**
* Given a single byte, compute the offset address. * Given a single byte, compute the offset address.
*/ */
@@ -1329,21 +1399,21 @@ public class Cpu implements InstructionTable {
return (state.pc + (byte) offset) & 0xffff; return (state.pc + (byte) offset) & 0xffff;
} }
/**
* Given a single byte, compute the Zero Page,Y offset address.
*/
int zpyAddress(int zp) {
return (zp + state.y) & 0xff;
}
/* /*
* Perform a busy-loop until the instruction should complete on the wall clock * Perform a busy-loop until the instruction should complete on the wall clock
*/ */
private void delayLoop(int opcode) { private void delayLoop(int opcode) {
int clockSteps = Cpu.instructionClocks[0xff & opcode]; final int clockSteps;
if (behavior == CpuBehavior.NMOS_WITH_ROR_BUG ||
behavior == CpuBehavior.NMOS_6502) {
clockSteps = Cpu.instructionClocksNmos[0xff & opcode];
} else {
clockSteps = Cpu.instructionClocksCmos[0xff & opcode];
}
if (clockSteps == 0) { if (clockSteps == 0) {
logger.warn("Opcode {} has clock step of 0!", opcode); logger.warn("Opcode {} has clock step of 0!", String.format("0x%02x", opcode));
return; return;
} }
@@ -46,20 +46,17 @@ public interface InstructionTable {
* *
* NB: Does NOT implement "unimplemented" NMOS instructions. * NB: Does NOT implement "unimplemented" NMOS instructions.
*/ */
NMOS_WITH_INDIRECT_JMP_BUG, NMOS_6502,
/**
* Emulate an NMOS 6502 without the indirect JMP bug. This type of 6502
* does not actually exist in the wild.
*
* NB: Does NOT implement "unimplemented" NMOS instructions.
*/
NMOS_WITHOUT_INDIRECT_JMP_BUG,
/** /**
* Emulate a CMOS 65C02, with all CMOS instructions and addressing modes. * Emulate a CMOS 65C02, with all CMOS instructions and addressing modes.
*/ */
CMOS CMOS_6502,
/**
* Emulate a CMOS 65C816.
*/
CMOS_65816
} }
/** /**
@@ -128,19 +125,25 @@ public interface InstructionTable {
ZPG { ZPG {
public String toString() { public String toString() {
return "Zeropage"; return "Zero Page";
} }
}, },
ZPX { ZPX {
public String toString() { public String toString() {
return "Zeropage, X-indexed"; return "Zero Page, X-indexed";
} }
}, },
ZPY { ZPY {
public String toString() { public String toString() {
return "Zeropage, Y-indexed"; return "Zero Page, Y-indexed";
}
},
ZPI {
public String toString() {
return "Zero Page Indirect";
} }
}, },
@@ -154,52 +157,55 @@ public interface InstructionTable {
// 6502 opcodes. No 65C02 opcodes implemented. // 6502 opcodes. No 65C02 opcodes implemented.
/** /**
* Instruction opcode names. * Instruction opcode names. This lists all opcodes for
* NMOS 6502, CMOS 65C02, and CMOS 65C816
*/ */
String[] opcodeNames = { String[] opcodeNames = {
"BRK", "ORA", null, null, null, "ORA", "ASL", null, "BRK", "ORA", null, null, null, "ORA", "ASL", null, // 0x00-0x07
"PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, "PHP", "ORA", "ASL", null, null, "ORA", "ASL", null, // 0x08-0x0f
"BPL", "ORA", null, null, null, "ORA", "ASL", null, "BPL", "ORA", "ORA", null, null, "ORA", "ASL", null, // 0x10-0x17
"CLC", "ORA", null, null, null, "ORA", "ASL", null, "CLC", "ORA", null, null, null, "ORA", "ASL", null, // 0x18-0x1f
"JSR", "AND", null, null, "BIT", "AND", "ROL", null, "JSR", "AND", null, null, "BIT", "AND", "ROL", null, // 0x20-0x27
"PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, "PLP", "AND", "ROL", null, "BIT", "AND", "ROL", null, // 0x28-0x2f
"BMI", "AND", null, null, null, "AND", "ROL", null, "BMI", "AND", "AND", null, null, "AND", "ROL", null, // 0x30-0x37
"SEC", "AND", null, null, null, "AND", "ROL", null, "SEC", "AND", null, null, null, "AND", "ROL", null, // 0x38-0x3f
"RTI", "EOR", null, null, null, "EOR", "LSR", null, "RTI", "EOR", null, null, null, "EOR", "LSR", null, // 0x40-0x47
"PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, "PHA", "EOR", "LSR", null, "JMP", "EOR", "LSR", null, // 0x48-0x4f
"BVC", "EOR", null, null, null, "EOR", "LSR", null, "BVC", "EOR", "EOR", null, null, "EOR", "LSR", null, // 0x50-0x57
"CLI", "EOR", null, null, null, "EOR", "LSR", null, "CLI", "EOR", null, null, null, "EOR", "LSR", null, // 0x58-0x5f
"RTS", "ADC", null, null, null, "ADC", "ROR", null, "RTS", "ADC", null, null, null, "ADC", "ROR", null, // 0x60-0x67
"PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, "PLA", "ADC", "ROR", null, "JMP", "ADC", "ROR", null, // 0x68-0x6f
"BVS", "ADC", null, null, null, "ADC", "ROR", null, "BVS", "ADC", "ADC", null, null, "ADC", "ROR", null, // 0x70-0x77
"SEI", "ADC", null, null, null, "ADC", "ROR", null, "SEI", "ADC", null, null, "JMP", "ADC", "ROR", null, // 0x78-0x7f
"BCS", "STA", null, null, "STY", "STA", "STX", null, "BCS", "STA", null, null, "STY", "STA", "STX", null, // 0x80-0x87
"DEY", null, "TXA", null, "STY", "STA", "STX", null, "DEY", null, "TXA", null, "STY", "STA", "STX", null, // 0x88-0x8f
"BCC", "STA", null, null, "STY", "STA", "STX", null, "BCC", "STA", "STA", null, "STY", "STA", "STX", null, // 0x90-0x97
"TYA", "STA", "TXS", null, null, "STA", null, null, "TYA", "STA", "TXS", null, null, "STA", null, null, // 0x98-0x9f
"LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, "LDY", "LDA", "LDX", null, // 0xa0-0xa7
"TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, "TAY", "LDA", "TAX", null, "LDY", "LDA", "LDX", null, // 0xa8-0xaf
"BCS", "LDA", null, null, "LDY", "LDA", "LDX", null, "BCS", "LDA", "LDA", null, "LDY", "LDA", "LDX", null, // 0xb0-0xb7
"CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, "CLV", "LDA", "TSX", null, "LDY", "LDA", "LDX", null, // 0xb8-0xbf
"CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, "CPY", "CMP", null, null, "CPY", "CMP", "DEC", null, // 0xc0-0xc7
"INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, "INY", "CMP", "DEX", null, "CPY", "CMP", "DEC", null, // 0xc8-0xcf
"BNE", "CMP", null, null, null, "CMP", "DEC", null, "BNE", "CMP", "CMP", null, null, "CMP", "DEC", null, // 0xd0-0xd7
"CLD", "CMP", null, null, null, "CMP", "DEC", null, "CLD", "CMP", null, null, null, "CMP", "DEC", null, // 0xd8-0xdf
"CPX", "SBC", null, null, "CPX", "SBC", "INC", null, "CPX", "SBC", null, null, "CPX", "SBC", "INC", null, // 0xe0-0xe7
"INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, "INX", "SBC", "NOP", null, "CPX", "SBC", "INC", null, // 0xe8-0xef
"BEQ", "SBC", null, null, null, "SBC", "INC", null, "BEQ", "SBC", "SBC", null, null, "SBC", "INC", null, // 0xf0-0xf7
"SED", "SBC", null, null, null, "SBC", "INC", null "SED", "SBC", null, null, null, "SBC", "INC", null // 0xf8-0xff
}; };
/** /**
* Instruction addressing modes. * Instruction addressing modes. This table includes sizes
* for all instructions for NMOS 6502, CMOS 65C02,
* and CMOS 65C816
*/ */
Mode[] instructionModes = { Mode[] instructionModes = {
Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03 Mode.IMP, Mode.XIN, Mode.NUL, Mode.NUL, // 0x00-0x03
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07 Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x04-0x07
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x08-0x0b
Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f Mode.NUL, Mode.ABS, Mode.ABS, Mode.NUL, // 0x0c-0x0f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x10-0x13 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x10-0x13
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x14-0x17
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x18-0x1b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x1c-0x1f
@@ -207,7 +213,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x24-0x27
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x28-0x2b
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x2c-0x2f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x30-0x33 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x30-0x33
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x34-0x37
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x38-0x3b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x3c-0x3f
@@ -215,7 +221,7 @@ public interface InstructionTable {
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47 Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x44-0x47
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x48-0x4b
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x4c-0x4f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x50-0x53 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x50-0x53
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x54-0x57
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x58-0x5b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x5c-0x5f
@@ -223,15 +229,15 @@ public interface InstructionTable {
Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67 Mode.NUL, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x64-0x67
Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b Mode.IMP, Mode.IMM, Mode.ACC, Mode.NUL, // 0x68-0x6b
Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f Mode.IND, Mode.ABS, Mode.ABS, Mode.NUL, // 0x6c-0x6f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x70-0x73 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x70-0x73
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0x74-0x77
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0x78-0x7b
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f Mode.ABX, Mode.ABX, Mode.ABX, Mode.NUL, // 0x7c-0x7f
Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83 Mode.REL, Mode.XIN, Mode.NUL, Mode.NUL, // 0x80-0x83
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0x84-0x87
Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b Mode.IMP, Mode.NUL, Mode.IMP, Mode.NUL, // 0x88-0x8b
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0x8c-0x8f
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0x90-0x93 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0x90-0x93
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0x94-0x97
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0x98-0x9b
Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f Mode.NUL, Mode.ABX, Mode.NUL, Mode.NUL, // 0x9c-0x9f
@@ -239,7 +245,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xa4-0xa7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xa8-0xab
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xac-0xaf
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xb0-0xb3 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xb0-0xb3
Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7 Mode.ZPX, Mode.ZPX, Mode.ZPY, Mode.NUL, // 0xb4-0xb7
Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb Mode.IMP, Mode.ABY, Mode.IMP, Mode.NUL, // 0xb8-0xbb
Mode.ABX, Mode.ABX, Mode.ABY, Mode.NUL, // 0xbc-0xbf Mode.ABX, Mode.ABX, Mode.ABY, Mode.NUL, // 0xbc-0xbf
@@ -247,7 +253,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xc4-0xc7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xc8-0xcb
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xcc-0xcf
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xd0-0xd3 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xd0-0xd3
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xd4-0xd7
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xd8-0xdb
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL, // 0xdc-0xdf
@@ -255,7 +261,7 @@ public interface InstructionTable {
Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7 Mode.ZPG, Mode.ZPG, Mode.ZPG, Mode.NUL, // 0xe4-0xe7
Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb Mode.IMP, Mode.IMM, Mode.IMP, Mode.NUL, // 0xe8-0xeb
Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef Mode.ABS, Mode.ABS, Mode.ABS, Mode.NUL, // 0xec-0xef
Mode.REL, Mode.INY, Mode.NUL, Mode.NUL, // 0xf0-0xf3 Mode.REL, Mode.INY, Mode.ZPI, Mode.NUL, // 0xf0-0xf3
Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7 Mode.NUL, Mode.ZPX, Mode.ZPX, Mode.NUL, // 0xf4-0xf7
Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb Mode.IMP, Mode.ABY, Mode.NUL, Mode.NUL, // 0xf8-0xfb
Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff Mode.NUL, Mode.ABX, Mode.ABX, Mode.NUL // 0xfc-0xff
@@ -263,47 +269,73 @@ public interface InstructionTable {
/** /**
* Size, in bytes, required for each instruction. * Size, in bytes, required for each instruction. This table
* includes sizes for all instructions for NMOS 6502, CMOS 65C02,
* and CMOS 65C816
*/ */
int[] instructionSizes = { int[] instructionSizes = {
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 0, 3, 3, 0, // 0x00-0x0f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x10-0x1f
3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 3, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x20-0x2f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x30-0x3f
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x40-0x4f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0x50-0x5f
1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 1, 2, 0, 0, 0, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0x60-0x6f
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 3, 3, 3, 0, // 0x70-0x7f
2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, // 0x80-0x8f
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, // 0x90-0x9f
2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xa0-0xaf
2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, // 0xb0-0xbf
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xc0-0xcf
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, // 0xd0-0xdf
2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, // 0xe0-0xef
2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 2, 2, 2, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0 // 0xf0-0xff
}; };
/** /**
* Number of clock cycles required for each instruction * Number of clock cycles required for each instruction when
* in NMOS mode.
*/ */
int[] instructionClocks = { int[] instructionClocksNmos = {
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, 7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, 6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, 6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, 6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, 2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, 2, 6, 0, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, 2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, 2, 5, 0, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, 2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 2, 5, 0, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
};
/**
* Number of clock cycles required for each instruction when
* in CMOS mode
*/
int[] instructionClocksCmos = {
7, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 0, 4, 6, 0, // 0x00-0x0f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x10-0x1f
6, 6, 0, 0, 3, 3, 5, 0, 4, 2, 2, 0, 4, 4, 6, 0, // 0x20-0x2f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x30-0x3f
6, 6, 0, 0, 0, 3, 5, 0, 3, 2, 2, 0, 3, 4, 6, 0, // 0x40-0x4f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x50-0x5f
6, 6, 0, 0, 0, 3, 5, 0, 4, 2, 2, 0, 5, 4, 6, 0, // 0x60-0x6f
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0x70-0x7f
2, 6, 0, 0, 3, 3, 3, 0, 2, 0, 2, 0, 4, 4, 4, 0, // 0x80-0x8f
2, 6, 5, 0, 4, 4, 4, 0, 2, 5, 2, 0, 0, 5, 0, 0, // 0x90-0x9f
2, 6, 2, 0, 3, 3, 3, 0, 2, 2, 2, 0, 4, 4, 4, 0, // 0xa0-0xaf
2, 5, 5, 0, 4, 4, 4, 0, 2, 4, 2, 0, 4, 4, 4, 0, // 0xb0-0xbf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xc0-0xcf
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0, // 0xd0-0xdf
2, 6, 0, 0, 3, 3, 5, 0, 2, 2, 2, 0, 4, 4, 6, 0, // 0xe0-0xef
2, 5, 5, 0, 0, 4, 6, 0, 2, 4, 0, 0, 0, 4, 7, 0 // 0xf0-0xff
}; };
} }
+10 -7
View File
@@ -86,13 +86,16 @@ public class Main {
final Simulator simulator = new Simulator(machineClass); final Simulator simulator = new Simulator(machineClass);
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
try { @Override
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); public void run() {
// Create the main UI window try {
simulator.createAndShowUi(); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) { // Create the main UI window
e.printStackTrace(); simulator.createAndShowUi();
} catch (Exception e) {
e.printStackTrace();
}
} }
}); });
+81 -49
View File
@@ -185,13 +185,16 @@ public class Simulator {
JButton hardResetButton = new JButton("Hard Reset"); JButton hardResetButton = new JButton("Hard Reset");
stepCountBox = new JComboBox<>(STEPS); stepCountBox = new JComboBox<>(STEPS);
stepCountBox.addActionListener(actionEvent -> { stepCountBox.addActionListener(new ActionListener() {
try { @Override
JComboBox cb = (JComboBox) actionEvent.getSource(); public void actionPerformed(ActionEvent actionEvent) {
stepsPerClick = Integer.parseInt((String) cb.getSelectedItem()); try {
} catch (NumberFormatException ex) { JComboBox cb = (JComboBox) actionEvent.getSource();
stepsPerClick = 1; stepsPerClick = Integer.parseInt((String) cb.getSelectedItem());
stepCountBox.setSelectedIndex(0); } catch (NumberFormatException ex) {
stepsPerClick = 1;
stepCountBox.setSelectedIndex(0);
}
} }
}); });
@@ -211,24 +214,38 @@ public class Simulator {
// Bottom - buttons. // Bottom - buttons.
mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END); mainWindow.getContentPane().add(buttonContainer, BorderLayout.PAGE_END);
runStopButton.addActionListener(actionEvent -> { runStopButton.addActionListener(new ActionListener() {
if (runLoop != null && runLoop.isRunning()) { @Override
handleStop(); public void actionPerformed(ActionEvent actionEvent) {
} else { if (runLoop != null && runLoop.isRunning()) {
handleStart(); Simulator.this.handleStop();
} else {
Simulator.this.handleStart();
}
} }
}); });
stepButton.addActionListener(actionEvent -> handleStep(stepsPerClick)); stepButton.addActionListener(new ActionListener() {
@Override
softResetButton.addActionListener(actionEvent -> { public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset. Simulator.this.handleStep(stepsPerClick);
handleReset(false); }
}); });
hardResetButton.addActionListener(actionEvent -> { softResetButton.addActionListener(new ActionListener() {
// If this was a CTRL-click, do a hard reset. @Override
handleReset(true); public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset.
Simulator.this.handleReset(false);
}
});
hardResetButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent actionEvent) {
// If this was a CTRL-click, do a hard reset.
Simulator.this.handleReset(true);
}
}); });
mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); mainWindow.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
@@ -400,13 +417,16 @@ public class Simulator {
logger.debug("Starting main run loop."); logger.debug("Starting main run loop.");
isRunning = true; isRunning = true;
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
// Don't allow step while the simulator is running @Override
stepButton.setEnabled(false); public void run() {
stepCountBox.setEnabled(false); // Don't allow step while the simulator is running
menuBar.simulatorDidStart(); stepButton.setEnabled(false);
// Toggle the state of the run button stepCountBox.setEnabled(false);
runStopButton.setText("Stop"); menuBar.simulatorDidStart();
// Toggle the state of the run button
runStopButton.setText("Stop");
}
}); });
try { try {
@@ -417,17 +437,20 @@ public class Simulator {
logger.error("Exception in main simulator run thread. Exiting run.", ex); logger.error("Exception in main simulator run thread. Exiting run.", ex);
} }
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
statusPane.updateState(); @Override
memoryWindow.updateState(); public void run() {
runStopButton.setText("Run"); statusPane.updateState();
stepButton.setEnabled(true); memoryWindow.updateState();
stepCountBox.setEnabled(true); runStopButton.setText("Run");
if (traceLog.isVisible()) { stepButton.setEnabled(true);
traceLog.refresh(); stepCountBox.setEnabled(true);
if (traceLog.isVisible()) {
traceLog.refresh();
}
menuBar.simulatorDidStop();
traceLog.simulatorDidStop();
} }
menuBar.simulatorDidStop();
traceLog.simulatorDidStop();
}); });
isRunning = false; isRunning = false;
@@ -480,9 +503,12 @@ public class Simulator {
// Now load the program at the starting address. // Now load the program at the starting address.
loadProgram(program, preferences.getProgramStartAddress()); loadProgram(program, preferences.getProgramStartAddress());
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
console.reset(); @Override
breakpoints.refresh(); public void run() {
console.reset();
breakpoints.refresh();
}
}); });
// TODO: "Don't Show Again" checkbox // TODO: "Don't Show Again" checkbox
@@ -623,9 +649,12 @@ public class Simulator {
} }
public void actionPerformed(ActionEvent actionEvent) { public void actionPerformed(ActionEvent actionEvent) {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
console.setFont(new Font("Monospaced", Font.PLAIN, size)); @Override
mainWindow.pack(); public void run() {
console.setFont(new Font("Monospaced", Font.PLAIN, size));
mainWindow.pack();
}
}); });
} }
} }
@@ -889,12 +918,15 @@ public class Simulator {
private void updateVisibleState() { private void updateVisibleState() {
// Immediately update the UI. // Immediately update the UI.
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
// Now update the state @Override
statusPane.updateState(); public void run() {
memoryWindow.updateState(); // Now update the state
if (traceLog.shouldUpdate()) { statusPane.updateState();
traceLog.refresh(); memoryWindow.updateState();
if (traceLog.shouldUpdate()) {
traceLog.refresh();
}
} }
}); });
} }
@@ -115,7 +115,9 @@ public abstract class Device implements Comparable<Device> {
} }
public void notifyListeners() { public void notifyListeners() {
deviceChangeListeners.forEach(DeviceChangeListener::deviceStateChanged); for (DeviceChangeListener listener : deviceChangeListeners) {
listener.deviceStateChanged();
}
} }
/** /**
@@ -27,7 +27,6 @@ package com.loomcom.symon.devices;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import com.loomcom.symon.exceptions.MemoryRangeException; import com.loomcom.symon.exceptions.MemoryRangeException;
import jdk.nashorn.internal.ir.annotations.Ignore;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@@ -30,7 +30,10 @@ import org.slf4j.LoggerFactory;
import javax.swing.*; import javax.swing.*;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
/** /**
@@ -62,21 +65,24 @@ public class BreakpointsWindow extends JFrame {
breakpointsPanel.setLayout(new BorderLayout()); breakpointsPanel.setLayout(new BorderLayout());
breakpointsPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); breakpointsPanel.setBorder(new EmptyBorder(10, 10, 10, 10));
JButton addButton = new JButton("Add"); final JButton addButton = new JButton("Add");
JButton removeButton = new JButton("Del"); final JButton removeButton = new JButton("Del");
removeButton.setEnabled(false); removeButton.setEnabled(false);
JTextField addTextField = new JTextField(4); final JTextField addTextField = new JTextField(4);
JTable breakpointsTable = new JTable(breakpoints); final JTable breakpointsTable = new JTable(breakpoints);
breakpointsTable.setShowGrid(true); breakpointsTable.setShowGrid(true);
breakpointsTable.setGridColor(Color.LIGHT_GRAY); breakpointsTable.setGridColor(Color.LIGHT_GRAY);
breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); breakpointsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
breakpointsTable.getSelectionModel().addListSelectionListener(e -> { breakpointsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
if (e.getFirstIndex() > -1) { @Override
removeButton.setEnabled(true); public void valueChanged(ListSelectionEvent e) {
} else { if (e.getFirstIndex() > -1) {
removeButton.setEnabled(false); removeButton.setEnabled(true);
} else {
removeButton.setEnabled(false);
}
} }
}); });
@@ -86,37 +92,45 @@ public class BreakpointsWindow extends JFrame {
breakpointsPanel.add(scrollPane, BorderLayout.CENTER); breakpointsPanel.add(scrollPane, BorderLayout.CENTER);
ActionListener addBreakpointListener = e -> { ActionListener addBreakpointListener = new ActionListener() {
int value = -1; @Override
public void actionPerformed(ActionEvent e) {
int value;
String newBreakpoint = addTextField.getText(); String newBreakpoint = addTextField.getText();
if (newBreakpoint == null || newBreakpoint.isEmpty()) { if (newBreakpoint == null || newBreakpoint.isEmpty()) {
return; return;
}
try {
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
} catch (NumberFormatException ex) {
logger.warn("Can't parse page number {}", newBreakpoint);
return;
}
if (value < 0) {
return;
}
breakpoints.addBreakpoint(value);
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
addTextField.setText(EMPTY_STRING);
} }
try {
value = (Integer.parseInt(addTextField.getText(), 16) & 0xffff);
} catch (NumberFormatException ex) {
logger.warn("Can't parse page number {}", newBreakpoint);
return;
}
if (value < 0) {
return;
}
breakpoints.addBreakpoint(value);
logger.debug("Added breakpoint ${}", Utils.wordToHex(value));
addTextField.setText(EMPTY_STRING);
}; };
addButton.addActionListener(addBreakpointListener); addButton.addActionListener(addBreakpointListener);
addTextField.addActionListener(addBreakpointListener); addTextField.addActionListener(addBreakpointListener);
removeButton.addActionListener(e -> breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow())); removeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
breakpoints.removeBreakpointAtIndex(breakpointsTable.getSelectedRow());
}
});
controlPanel.add(addTextField); controlPanel.add(addTextField);
controlPanel.add(addButton); controlPanel.add(addButton);
@@ -97,19 +97,25 @@ public class PreferencesDialog extends Observable implements Preferences {
JButton applyButton = new JButton("Apply"); JButton applyButton = new JButton("Apply");
JButton cancelButton = new JButton("Cancel"); JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(actionEvent -> { cancelButton.addActionListener(new ActionListener() {
updateUi(); @Override
dialog.setVisible(false); public void actionPerformed(ActionEvent actionEvent) {
PreferencesDialog.this.updateUi();
dialog.setVisible(false);
}
}); });
applyButton.addActionListener(actionEvent -> { applyButton.addActionListener(new ActionListener() {
haltOnBreak = haltOnBreakCheckBox.isSelected(); @Override
programLoadAddress = hexToInt(programLoadAddressField.getText()); public void actionPerformed(ActionEvent actionEvent) {
updateUi(); haltOnBreak = haltOnBreakCheckBox.isSelected();
// TODO: Actually check to see if values have changed, don't assume. programLoadAddress = PreferencesDialog.this.hexToInt(programLoadAddressField.getText());
setChanged(); PreferencesDialog.this.updateUi();
PreferencesDialog.this.notifyObservers(); // TODO: Actually check to see if values have changed, don't assume.
dialog.setVisible(false); PreferencesDialog.this.setChanged();
PreferencesDialog.this.notifyObservers();
dialog.setVisible(false);
}
}); });
buttonsContainer.add(applyButton); buttonsContainer.add(applyButton);
@@ -31,6 +31,8 @@ import javax.swing.*;
import javax.swing.border.Border; import javax.swing.border.Border;
import javax.swing.border.EtchedBorder; import javax.swing.border.EtchedBorder;
import java.awt.*; import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/** /**
* UI component that displays the current state of the simulated CPU. * UI component that displays the current state of the simulated CPU.
@@ -162,59 +164,74 @@ public class StatusPanel extends JPanel {
yField = makeTextField(SMALL_TEXT_FIELD_SIZE, true); yField = makeTextField(SMALL_TEXT_FIELD_SIZE, true);
// Make fields editable // Make fields editable
pcField.addActionListener(e -> { pcField.addActionListener(new ActionListener() {
try { @Override
int newVal = getHexVal(pcField) & 0xffff; public void actionPerformed(ActionEvent e) {
machine.getCpu().setProgramCounter(newVal); try {
} catch (Exception ex) { int newVal = StatusPanel.this.getHexVal(pcField) & 0xffff;
// Swallow exception machine.getCpu().setProgramCounter(newVal);
} } catch (Exception ex) {
// Swallow exception
}
updateState(); StatusPanel.this.updateState();
}
}); });
spField.addActionListener(e -> { spField.addActionListener(new ActionListener() {
try { @Override
int newVal = getHexVal(spField) & 0xff; public void actionPerformed(ActionEvent e) {
machine.getCpu().setStackPointer(newVal); try {
} catch (Exception ex) { int newVal = StatusPanel.this.getHexVal(spField) & 0xff;
// Swallow exception machine.getCpu().setStackPointer(newVal);
} } catch (Exception ex) {
// Swallow exception
}
updateState(); StatusPanel.this.updateState();
}
}); });
aField.addActionListener(e -> { aField.addActionListener(new ActionListener() {
try { @Override
int newVal = getHexVal(aField) & 0xff; public void actionPerformed(ActionEvent e) {
machine.getCpu().setAccumulator(newVal); try {
} catch (Exception ex) { int newVal = StatusPanel.this.getHexVal(aField) & 0xff;
// Swallow exception machine.getCpu().setAccumulator(newVal);
} } catch (Exception ex) {
// Swallow exception
}
updateState(); StatusPanel.this.updateState();
}
}); });
xField.addActionListener(e -> { xField.addActionListener(new ActionListener() {
try { @Override
int newVal = getHexVal(xField) & 0xff; public void actionPerformed(ActionEvent e) {
machine.getCpu().setXRegister(newVal); try {
} catch (Exception ex) { int newVal = StatusPanel.this.getHexVal(xField) & 0xff;
// Swallow exception machine.getCpu().setXRegister(newVal);
} } catch (Exception ex) {
// Swallow exception
}
updateState(); StatusPanel.this.updateState();
}
}); });
yField.addActionListener(e -> { yField.addActionListener(new ActionListener() {
try { @Override
int newVal = getHexVal(yField) & 0xff; public void actionPerformed(ActionEvent e) {
machine.getCpu().setYRegister(newVal); try {
} catch (Exception ex) { int newVal = StatusPanel.this.getHexVal(yField) & 0xff;
// Swallow exception machine.getCpu().setYRegister(newVal);
} } catch (Exception ex) {
// Swallow exception
}
updateState(); StatusPanel.this.updateState();
}
}); });
constraints.anchor = GridBagConstraints.LINE_START; constraints.anchor = GridBagConstraints.LINE_START;
@@ -122,10 +122,13 @@ public class VideoWindow extends JFrame implements DeviceChangeListener {
*/ */
private class CursorBlinker implements Runnable { private class CursorBlinker implements Runnable {
public void run() { public void run() {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(new Runnable() {
if (cursorBlinkRate > 0) { @Override
hideCursor = !hideCursor; public void run() {
repaint(); if (cursorBlinkRate > 0) {
hideCursor = !hideCursor;
VideoWindow.this.repaint();
}
} }
}); });
} }
@@ -117,9 +117,22 @@ public class CpuImpliedModeTest {
} }
@Test @Test
public void test_BRK_HonorsIrqDisableFlag() throws MemoryAccessException { public void test_BRK_IgnoresIrqDisableFlag() throws MemoryAccessException {
cpu.setIrqDisableFlag(); cpu.setIrqDisableFlag();
cpu.setCarryFlag();
cpu.setOverflowFlag();
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_IRQ_DISABLE,
cpu.getProcessorStatus());
assertEquals(0x00, cpu.stackPeek());
assertFalse(cpu.getBreakFlag());
assertEquals(0x0200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
// Set the IRQ vector
bus.write(0xffff, 0x12);
bus.write(0xfffe, 0x34);
bus.loadProgram(0xea, bus.loadProgram(0xea,
0xea, 0xea,
0xea, 0xea,
@@ -131,23 +144,26 @@ public class CpuImpliedModeTest {
assertEquals(0x203, cpu.getProgramCounter()); assertEquals(0x203, cpu.getProgramCounter());
// Triggers the BRK, which should do nothing because // Triggers the BRK
// of the Interrupt Disable flag
cpu.step(); cpu.step();
// Reset to original contents of PC
assertEquals(0x0204, cpu.getProgramCounter());
// Empty stack
assertEquals(0xff, cpu.getStackPointer());
cpu.step(2); // Two more NOPs // Was at PC = 0x204. PC+1 should now be on the stack
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
assertEquals(0x05, bus.read(0x1fe)); // PC low byte
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_IRQ_DISABLE,
bus.read(0x1fd)); // Processor Status, with B set
// Reset to original contents of PC // Interrupt vector held 0x1234, so we should be there.
assertEquals(0x0206, cpu.getProgramCounter()); assertEquals(0x1234, cpu.getProgramCounter());
// Empty stack assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0xff, cpu.getStackPointer());
// B and I flags should have been set on P
assertEquals(0x20 | Cpu.P_CARRY | Cpu.P_OVERFLOW | Cpu.P_BREAK | Cpu.P_IRQ_DISABLE,
cpu.getProcessorStatus());
} }
/* CLC - Clear Carry Flag - $18 */ /* CLC - Clear Carry Flag - $18 */
@Test @Test
public void test_CLC() throws MemoryAccessException { public void test_CLC() throws MemoryAccessException {
@@ -25,18 +25,10 @@ public class CpuIndirectIndexedModeTest {
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8); bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset(); cpu.reset();
// Assert initial state
assertEquals(0, cpu.getAccumulator());
assertEquals(0, cpu.getXRegister());
assertEquals(0, cpu.getYRegister());
assertEquals(0x200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test @Test
public void test_LDA() throws Exception { public void test_LDA() throws Exception {
assertEquals(cpu.toString(), 0x00, cpu.getAccumulator());
bus.write(0x0014, 0x00); bus.write(0x0014, 0x00);
bus.write(0x0015, 0xd8); bus.write(0x0015, 0xd8);
bus.write(0xd828, 0x03); bus.write(0xd828, 0x03);
@@ -3,15 +3,19 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import junit.framework.TestCase; import org.junit.Before;
import org.junit.Test;
public class CpuIndirectModeTest extends TestCase { import static org.junit.Assert.assertEquals;
public class CpuIndirectModeTest {
protected Cpu cpu; protected Cpu cpu;
protected Bus bus; protected Bus bus;
protected Memory mem; protected Memory mem;
protected void setUp() throws Exception { @Before
public void setUp() throws Exception {
this.cpu = new Cpu(); this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff); this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0xffff); this.mem = new Memory(0x0000, 0xffff);
@@ -41,6 +45,7 @@ public class CpuIndirectModeTest extends TestCase {
/* JMP - Jump - $6c */ /* JMP - Jump - $6c */
@Test
public void test_JMP_notOnPageBoundary() throws MemoryAccessException { public void test_JMP_notOnPageBoundary() throws MemoryAccessException {
bus.write(0x3400, 0x00); bus.write(0x3400, 0x00);
bus.write(0x3401, 0x54); bus.write(0x3401, 0x54);
@@ -51,6 +56,7 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_with_ROR_Bug() throws MemoryAccessException { public void test_JMP_with_ROR_Bug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG); cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_ROR_BUG);
bus.write(0x3400, 0x22); bus.write(0x3400, 0x22);
@@ -63,8 +69,9 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_withIndirectBug() throws MemoryAccessException { public void test_JMP_withIndirectBug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITH_INDIRECT_JMP_BUG); cpu.setBehavior(Cpu.CpuBehavior.NMOS_6502);
bus.write(0x3400, 0x22); bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00); bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54); bus.write(0x3500, 0x54);
@@ -75,8 +82,9 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_withOutIndirectBug() throws MemoryAccessException { public void test_JMP_withOutIndirectBug() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.NMOS_WITHOUT_INDIRECT_JMP_BUG); cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
bus.write(0x3400, 0x22); bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00); bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54); bus.write(0x3500, 0x54);
@@ -87,8 +95,9 @@ public class CpuIndirectModeTest extends TestCase {
assertEquals(0x20, cpu.getProcessorStatus()); assertEquals(0x20, cpu.getProcessorStatus());
} }
@Test
public void test_JMP_cmos() throws MemoryAccessException { public void test_JMP_cmos() throws MemoryAccessException {
cpu.setBehavior(Cpu.CpuBehavior.CMOS); cpu.setBehavior(Cpu.CpuBehavior.CMOS_6502);
bus.write(0x3400, 0x22); bus.write(0x3400, 0x22);
bus.write(0x34ff, 0x00); bus.write(0x34ff, 0x00);
bus.write(0x3500, 0x54); bus.write(0x3500, 0x54);
@@ -3,14 +3,21 @@ package com.loomcom.symon;
import com.loomcom.symon.devices.Memory; import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.MemoryAccessException; import com.loomcom.symon.exceptions.MemoryAccessException;
import junit.framework.TestCase; import junit.framework.TestCase;
import org.junit.Before;
import org.junit.Test;
public class CpuIndirectXModeTest extends TestCase { import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class CpuIndirectXModeTest {
protected Cpu cpu; protected Cpu cpu;
protected Bus bus; protected Bus bus;
protected Memory mem; protected Memory mem;
protected void setUp() throws Exception { @Before
public void setUp() throws Exception {
this.cpu = new Cpu(); this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff); this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0xffff); this.mem = new Memory(0x0000, 0xffff);
@@ -47,7 +54,7 @@ public class CpuIndirectXModeTest extends TestCase {
*/ */
/* ORA - Logical Inclusive OR - $1d */ /* ORA - Logical Inclusive OR - $1d */
@Test
public void test_ORA() throws MemoryAccessException { public void test_ORA() throws MemoryAccessException {
// Set some initial values in memory // Set some initial values in memory
bus.write(0x2c30, 0x00); bus.write(0x2c30, 0x00);
@@ -93,7 +100,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* AND - Logical AND - $3d */ /* AND - Logical AND - $3d */
@Test
public void test_AND() throws MemoryAccessException { public void test_AND() throws MemoryAccessException {
bus.write(0x1a30, 0x00); bus.write(0x1a30, 0x00);
bus.write(0x1a31, 0x11); bus.write(0x1a31, 0x11);
@@ -153,7 +160,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* EOR - Exclusive OR - $5d */ /* EOR - Exclusive OR - $5d */
@Test
public void test_EOR() throws MemoryAccessException { public void test_EOR() throws MemoryAccessException {
bus.write(0xab40, 0x00); bus.write(0xab40, 0x00);
bus.write(0xab41, 0xff); bus.write(0xab41, 0xff);
@@ -162,7 +169,7 @@ public class CpuIndirectXModeTest extends TestCase {
cpu.setXRegister(0x30); cpu.setXRegister(0x30);
bus.loadProgram(0xa9, 0x88, // LDA #$88 bus.loadProgram(0xa9, 0x88, // LDA #$88
0x5d, 0x10, 0xab, // EOR $ab10,X 0x5d, 0x10, 0xab, // EOR $ab10,X
0x5d, 0x11, 0xab, // EOR $ab11,X 0x5d, 0x11, 0xab, // EOR $ab11,X
0x5d, 0x12, 0xab, // EOR $ab12,X 0x5d, 0x12, 0xab, // EOR $ab12,X
@@ -189,7 +196,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* ADC - Add with Carry - $7d */ /* ADC - Add with Carry - $7d */
@Test
public void test_ADC() throws MemoryAccessException { public void test_ADC() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
bus.write(0xab41, 0xff); bus.write(0xab41, 0xff);
@@ -290,6 +297,7 @@ public class CpuIndirectXModeTest extends TestCase {
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
} }
@Test
public void test_ADC_IncludesCarry() throws MemoryAccessException { public void test_ADC_IncludesCarry() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
@@ -307,6 +315,7 @@ public class CpuIndirectXModeTest extends TestCase {
assertFalse(cpu.getCarryFlag()); assertFalse(cpu.getCarryFlag());
} }
@Test
public void test_ADC_DecimalMode() throws MemoryAccessException { public void test_ADC_DecimalMode() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
bus.write(0xab41, 0x99); bus.write(0xab41, 0x99);
@@ -404,7 +413,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* STA - Store Accumulator - $9d */ /* STA - Store Accumulator - $9d */
@Test
public void test_STA() throws MemoryAccessException { public void test_STA() throws MemoryAccessException {
cpu.setXRegister(0x30); cpu.setXRegister(0x30);
@@ -438,7 +447,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* LDA - Load Accumulator - $bd */ /* LDA - Load Accumulator - $bd */
@Test
public void test_LDA() throws MemoryAccessException { public void test_LDA() throws MemoryAccessException {
bus.write(0xab42, 0x00); bus.write(0xab42, 0x00);
bus.write(0xab43, 0x0f); bus.write(0xab43, 0x0f);
@@ -467,7 +476,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* CMP - Compare Accumulator - $dd */ /* CMP - Compare Accumulator - $dd */
@Test
public void test_CMP() throws MemoryAccessException { public void test_CMP() throws MemoryAccessException {
bus.write(0xab40, 0x00); bus.write(0xab40, 0x00);
bus.write(0xab41, 0x80); bus.write(0xab41, 0x80);
@@ -498,7 +507,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
/* SBC - Subtract with Carry - $fd */ /* SBC - Subtract with Carry - $fd */
@Test
public void test_SBC() throws MemoryAccessException { public void test_SBC() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
@@ -557,6 +566,7 @@ public class CpuIndirectXModeTest extends TestCase {
assertTrue(cpu.getCarryFlag()); assertTrue(cpu.getCarryFlag());
} }
@Test
public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException { public void test_SBC_IncludesNotOfCarry() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
@@ -602,6 +612,7 @@ public class CpuIndirectXModeTest extends TestCase {
} }
@Test
public void test_SBC_DecimalMode() throws MemoryAccessException { public void test_SBC_DecimalMode() throws MemoryAccessException {
bus.write(0xab40, 0x01); bus.write(0xab40, 0x01);
bus.write(0xab50, 0x11); bus.write(0xab50, 0x11);
@@ -241,6 +241,7 @@ public class CpuTest extends TestCase {
public void testSetProcessorStatus() { public void testSetProcessorStatus() {
// Default // Default
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getZeroFlag()); assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag()); assertFalse(cpu.getIrqDisableFlag());
@@ -384,6 +385,41 @@ public class CpuTest extends TestCase {
assertFalse(cpu.getCpuState().irqAsserted); assertFalse(cpu.getCpuState().irqAsserted);
} }
public void testIrqDoesNotSetBRK() throws Exception {
// Ensure the IRQ disable flag is cleared
cpu.clearIrqDisableFlag();
// Set the IRQ vector
bus.write(0xffff, 0x12);
bus.write(0xfffe, 0x34);
// Create an IRQ handler at 0x1234
cpu.setProgramCounter(0x1234);
bus.loadProgram(0xa9, 0x33, // LDA #$33
0x69, 0x01); // ADC #$01
cpu.setProgramCounter(0x0200);
// Create a little program at 0x0200
bus.loadProgram(0x18, // CLC
0xa9, 0x01, // LDA #$00
0x69, 0x01); // ADC #$01
cpu.step();
assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed.
assertEquals(0x00, cpu.getAccumulator());
cpu.step();
assertEquals(0x0203, cpu.getProgramCounter());
assertEquals(0x01, cpu.getAccumulator());
cpu.assertIrq();
cpu.step();
assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the IRQ
assertFalse(cpu.getBreakFlag());
}
public void testIrqHonorsIrqDisabledFlag() throws Exception { public void testIrqHonorsIrqDisabledFlag() throws Exception {
// Ensure the IRQ disable flag is set // Ensure the IRQ disable flag is set
cpu.setIrqDisableFlag(); cpu.setIrqDisableFlag();
@@ -498,6 +534,37 @@ public class CpuTest extends TestCase {
assertFalse(cpu.getCpuState().nmiAsserted); assertFalse(cpu.getCpuState().nmiAsserted);
} }
public void testNmiDoesNotSetBRK() throws Exception {
// Set the NMI vector to 0x1000
bus.write(0xfffb, 0x10);
bus.write(0xfffa, 0x00);
// Create an NMI handler at 0x1000
cpu.setProgramCounter(0x1000);
bus.loadProgram(0xa9, 0x33, // LDA #$33
0x69, 0x01); // ADC #$01
// Create a little program at 0x0200
cpu.setProgramCounter(0x0200);
bus.loadProgram(0x18, // CLC
0xa9, 0x01, // LDA #$00
0x69, 0x01); // ADC #$01
cpu.step();
assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed.
assertEquals(0x00, cpu.getAccumulator());
cpu.step();
assertEquals(0x0203, cpu.getProgramCounter());
assertEquals(0x01, cpu.getAccumulator());
cpu.assertNmi();
cpu.step();
assertFalse(cpu.getBreakFlag());
}
public void testNmiIgnoresIrqDisableFlag() throws Exception { public void testNmiIgnoresIrqDisableFlag() throws Exception {
// Set the IRQ disable flag, which should be ignored by the NMI // Set the IRQ disable flag, which should be ignored by the NMI
cpu.setIrqDisableFlag(); cpu.setIrqDisableFlag();
@@ -0,0 +1,329 @@
package com.loomcom.symon;
import com.loomcom.symon.devices.Memory;
import org.junit.Test;
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
/**
* Test for Zero Page Indirect addressing mode, found on some instructions
* in the 65C02 and 65816
*/
public class CpuZeroPageIndirectTest {
protected Cpu cpu;
protected Bus bus;
protected Memory mem;
private void makeCmosCpu() throws Exception {
makeCpu(InstructionTable.CpuBehavior.CMOS_6502);
}
private void makeNmosCpu() throws Exception {
makeCpu(InstructionTable.CpuBehavior.NMOS_6502);
}
private void makeCpu(InstructionTable.CpuBehavior behavior) throws Exception {
this.cpu = new Cpu(behavior);
this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0xffff);
bus.addCpu(cpu);
bus.addDevice(mem);
// Load the reset vector.
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
bus.write(0xfffd, (Bus.DEFAULT_LOAD_ADDRESS & 0xff00) >>> 8);
cpu.reset();
}
@Test
public void test_ora() throws Exception {
makeCmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x11);
bus.write(0x1001, 0x22);
bus.write(0x1002, 0x44);
bus.write(0x1003, 0x88);
bus.loadProgram(0x12, 0x30, // ORA ($30)
0x12, 0x40, // ORA ($40)
0x12, 0x50, // ORA ($50)
0x12, 0x60); // ORA ($60)
assertEquals(0x00, cpu.getAccumulator());
// 0x00 | 0x11 = 0x11
cpu.step();
assertEquals(0x11, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x11 | 0x22 = 0x33
cpu.step();
assertEquals(0x33, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x33 | 0x44 = 0x77
cpu.step();
assertEquals(0x77, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x77 | 0x88 = 0xff
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertTrue(cpu.getNegativeFlag());
}
@Test
public void test_ora_requiresCmosCpu() throws Exception {
makeNmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x11);
bus.write(0x1001, 0x22);
bus.write(0x1002, 0x44);
bus.write(0x1003, 0x88);
bus.loadProgram(0x12, 0x30, // ORA ($30)
0x12, 0x40, // ORA ($40)
0x12, 0x50, // ORA ($50)
0x12, 0x60); // ORA ($60)
assertEquals(0x00, cpu.getAccumulator());
boolean zState = cpu.getZeroFlag();
boolean nState = cpu.getNegativeFlag();
// 0x00 | 0x11 = 0x11, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
// 0x11 | 0x22 = 0x33, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
// 0x33 | 0x44 = 0x77, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
// 0x77 | 0x88 = 0xff, but not implemented in NMOS cpu
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag()); // unchanged
assertEquals(nState, cpu.getNegativeFlag()); // unchanged
}
@Test
public void test_and() throws Exception {
makeCmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x1000, 0x33);
bus.write(0x1001, 0x11);
bus.write(0x1002, 0x88);
bus.loadProgram(0x32, 0x30, // AND ($30)
0x32, 0x40, // AND ($40)
0x32, 0x50); // AND ($50)
cpu.setAccumulator(0xff);
// 0xFF & 0x33 == 0x33
cpu.step();
assertEquals(0x33, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x33 & 0x11 == 0x11
cpu.step();
assertEquals(0x11, cpu.getAccumulator());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
// 0x11 & 0x80 == 0
cpu.step();
assertEquals(0, cpu.getAccumulator());
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getNegativeFlag());
}
@Test
public void test_and_requiresCmosCpu() throws Exception {
makeNmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x1000, 0x33);
bus.write(0x1001, 0x11);
bus.write(0x1002, 0x88);
bus.loadProgram(0x32, 0x30, // AND ($30)
0x32, 0x40, // AND ($40)
0x32, 0x50); // AND ($50)
cpu.setAccumulator(0xff);
boolean zState = cpu.getZeroFlag();
boolean nState = cpu.getNegativeFlag();
// 0xFF & 0x33 == 0x33, but not implemented in NMOS cpu
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag());
assertEquals(nState, cpu.getNegativeFlag());
// 0x33 & 0x11 == 0x11, but not implemented in NMOS cpu
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag());
assertEquals(nState, cpu.getNegativeFlag());
// 0x11 & 0x80 == 0, but not implemented in NMOS cpu
cpu.step();
assertEquals(0xff, cpu.getAccumulator());
assertEquals(zState, cpu.getZeroFlag());
assertEquals(nState, cpu.getNegativeFlag());
}
@Test
public void test_eor() throws Exception {
makeCmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x00);
bus.write(0x1001, 0xff);
bus.write(0x1002, 0x33);
bus.write(0x1003, 0x44);
bus.loadProgram(0x52, 0x30, // AND ($30)
0x52, 0x40, // AND ($40)
0x52, 0x50, // EOR ($50)
0x52, 0x60); // AND ($60)
cpu.setAccumulator(0x88);
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x77, cpu.getAccumulator());
cpu.step();
assertEquals(0x44, cpu.getAccumulator());
cpu.step();
assertEquals(0x00, cpu.getAccumulator());
}
@Test
public void test_eor_requiresCmosCpu() throws Exception {
makeNmosCpu();
// Set some initial values in zero page.
bus.write(0x30, 0x00);
bus.write(0x31, 0x10);
bus.write(0x40, 0x01);
bus.write(0x41, 0x10);
bus.write(0x50, 0x02);
bus.write(0x51, 0x10);
bus.write(0x60, 0x03);
bus.write(0x61, 0x10);
bus.write(0x1000, 0x00);
bus.write(0x1001, 0xff);
bus.write(0x1002, 0x33);
bus.write(0x1003, 0x44);
bus.loadProgram(0x52, 0x30, // AND ($30)
0x52, 0x40, // AND ($40)
0x52, 0x50, // EOR ($50)
0x52, 0x60); // AND ($60)
cpu.setAccumulator(0x88);
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
cpu.step();
assertEquals(0x88, cpu.getAccumulator());
}
}