Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4cca0b4663 | ||
|
|
5a2e057e69 | ||
|
|
da88aadda2 | ||
|
|
657b69da6c | ||
|
|
f251e54174 | ||
|
|
fcc57d9be4 | ||
|
|
88eba2cdcb | ||
|
|
0e49b197b3 | ||
|
|
b59fa63b63 | ||
|
|
eff98118d5 | ||
|
|
c599df1cfb | ||
|
|
66c52c8826 | ||
|
|
8335cf5421 |
No files matched your search
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user