Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7ea60d250 | ||
|
|
1bc8eda38d | ||
|
|
76dcbd712a | ||
|
|
b6cc480919 | ||
|
|
0c5035fc56 | ||
|
|
3d36cd5460 |
No files matched your search
@@ -3,7 +3,7 @@ SYMON - A 6502 System Simulator
|
|||||||
|
|
||||||
**NOTE: THIS SOFTWARE IS UNDER ACTIVE DEVELOPMENT. Feedback is welcome!**
|
**NOTE: THIS SOFTWARE IS UNDER ACTIVE DEVELOPMENT. Feedback is welcome!**
|
||||||
|
|
||||||
**Version:** 0.9.0.1
|
**Version:** 0.9.1.0
|
||||||
|
|
||||||
**Last Updated:** 29 December, 2013
|
**Last Updated:** 29 December, 2013
|
||||||
|
|
||||||
@@ -154,7 +154,7 @@ Maven will build Symon, run unit tests, and produce a jar file in the
|
|||||||
Symon is meant to be invoked directly from the jar file. To run with
|
Symon is meant to be invoked directly from the jar file. To run with
|
||||||
Java 1.5 or greater, just type:
|
Java 1.5 or greater, just type:
|
||||||
|
|
||||||
$ java -jar symon-0.9.0.jar
|
$ java -jar symon-0.9.1.0.jar
|
||||||
|
|
||||||
When Symon is running, you should be presented with a simple graphical
|
When Symon is running, you should be presented with a simple graphical
|
||||||
interface.
|
interface.
|
||||||
@@ -196,6 +196,8 @@ running.
|
|||||||
|
|
||||||
## 5.0 Revision History
|
## 5.0 Revision History
|
||||||
|
|
||||||
|
- **0.9.1:** 26 January, 2014 - Support for IRQ and NMI handling.
|
||||||
|
|
||||||
- **0.9.0:** 29 December, 2013 - First pass at a 6545 CRTC simulation.
|
- **0.9.0:** 29 December, 2013 - First pass at a 6545 CRTC simulation.
|
||||||
|
|
||||||
- **0.8.5:** 30 March, 2013 - ASCII display for memory window.
|
- **0.8.5:** 30 March, 2013 - ASCII display for memory window.
|
||||||
@@ -234,8 +236,6 @@ running.
|
|||||||
|
|
||||||
- More accurate timing.
|
- More accurate timing.
|
||||||
|
|
||||||
- Smarter interrupt handling.
|
|
||||||
|
|
||||||
- UI needs a ton more polish.
|
- UI needs a ton more polish.
|
||||||
|
|
||||||
- More extensive testing.
|
- More extensive testing.
|
||||||
|
|||||||
@@ -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>0.9.0.1</version>
|
<version>0.9.1.0</version>
|
||||||
<name>symon</name>
|
<name>symon</name>
|
||||||
<url>http://www.loomcom.com/symon</url>
|
<url>http://www.loomcom.com/symon</url>
|
||||||
<properties>
|
<properties>
|
||||||
|
|||||||
+7
-3
@@ -6,18 +6,22 @@ Sample Programs
|
|||||||
When loaded at address $0300, this program will print "Hello, 6502 World!" in
|
When loaded at address $0300, this program will print "Hello, 6502 World!" in
|
||||||
infinite loop.
|
infinite loop.
|
||||||
|
|
||||||
2. echo.prg
|
2. echo_poll.prg
|
||||||
|
|
||||||
When loaded at address $0300, this program will echo back to the console
|
When loaded at address $0300, this program will echo back to the console
|
||||||
anything typed.
|
anything typed.
|
||||||
|
|
||||||
|
|
||||||
Both hello.prg and echo.prg were assembled with the Ophis assembler:
|
Both hello.prg and echo_poll.prg were assembled with the Ophis assembler:
|
||||||
|
|
||||||
https://hkn.eecs.berkeley.edu/~mcmartin/ophis/
|
https://hkn.eecs.berkeley.edu/~mcmartin/ophis/
|
||||||
|
|
||||||
|
3. echo_irq.rom
|
||||||
|
|
||||||
3. ehbasic.rom
|
This is another echo program, and behaves identically to echo_poll.prg,
|
||||||
|
except it is interrupt-driven.
|
||||||
|
|
||||||
|
4. ehbasic.rom
|
||||||
|
|
||||||
This is Lee Davison's Enhanced 6502 BASIC.
|
This is Lee Davison's Enhanced 6502 BASIC.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,4 @@
|
|||||||
|
*.rom
|
||||||
|
*.lst
|
||||||
|
*.map
|
||||||
|
*.o
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
CA=ca65
|
||||||
|
LD=ld65
|
||||||
|
|
||||||
|
all: echo_irq
|
||||||
|
|
||||||
|
echo_irq: echo_irq.o
|
||||||
|
$(LD) -C symon.config -vm -m echo_irq.map -o echo_irq.rom echo_irq.o
|
||||||
|
|
||||||
|
echo_irq.o:
|
||||||
|
$(CA) --listing -o echo_irq.o echo_irq.asm
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.rom *.map *.lst
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
;;
|
||||||
|
;; Read input from the keyboard, and echo to console.
|
||||||
|
;;
|
||||||
|
|
||||||
|
|
||||||
|
IOBASE = $8800
|
||||||
|
IOSTATUS = IOBASE + 1
|
||||||
|
IOCMD = IOBASE + 2
|
||||||
|
IOCTRL = IOBASE + 3
|
||||||
|
|
||||||
|
.segment "CODE"
|
||||||
|
.org $C000
|
||||||
|
|
||||||
|
START: CLI
|
||||||
|
LDA #$09
|
||||||
|
STA IOCMD ; Set command status
|
||||||
|
LDA #$1A
|
||||||
|
STA IOCTRL ; 0 stop bits, 8 bit word, 2400 baud
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; Infinite idle loop, waiting for interrupt.
|
||||||
|
;;
|
||||||
|
IDLE: LDA #$FF
|
||||||
|
TAX
|
||||||
|
IDLE1: DEX
|
||||||
|
BNE IDLE1
|
||||||
|
JMP IDLE
|
||||||
|
|
||||||
|
;;
|
||||||
|
;; IRQ handler
|
||||||
|
;;
|
||||||
|
IRQ: LDA IOBASE ; Get the character in the ACIA.
|
||||||
|
PHA ; Save accumulator
|
||||||
|
ECHO1: LDA IOSTATUS ; Read the ACIA status
|
||||||
|
AND #$10 ; Is the tx register empty?
|
||||||
|
BEQ ECHO1 ; No, wait for it to empty
|
||||||
|
PLA ; Otherwise, load saved accumulator,
|
||||||
|
STA IOBASE ; write to output,
|
||||||
|
RTI ; and return
|
||||||
|
|
||||||
|
; system vectors
|
||||||
|
|
||||||
|
.segment "VECTORS"
|
||||||
|
.org $FFFA
|
||||||
|
|
||||||
|
.word IRQ ; NMI vector
|
||||||
|
.word START ; RESET vector
|
||||||
|
.word IRQ ; IRQ vector
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
MEMORY {
|
||||||
|
RAM1: start = $0000, size = $8000;
|
||||||
|
ROM1: start = $C000, size = $3FFA, fill = yes;
|
||||||
|
ROMV: start = $FFFA, size = $6, file = %O, fill = yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
SEGMENTS {
|
||||||
|
CODE: load = ROM1, type = ro;
|
||||||
|
DATA: load = ROM1, type = ro;
|
||||||
|
VECTORS: load = ROMV, type = ro;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYMBOLS {
|
||||||
|
__STACKSIZE__ = $0300;
|
||||||
|
}
|
||||||
|
|
||||||
File renamed without changes.
File renamed without changes.
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 33 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 239 KiB After Width: | Height: | Size: 230 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 73 KiB |
@@ -151,6 +151,30 @@ public class Bus {
|
|||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void assertIrq() {
|
||||||
|
if (cpu != null) {
|
||||||
|
cpu.assertIrq();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearIrq() {
|
||||||
|
if (cpu != null) {
|
||||||
|
cpu.clearIrq();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertNmi() {
|
||||||
|
if (cpu != null) {
|
||||||
|
cpu.assertNmi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearNmi() {
|
||||||
|
if (cpu != null) {
|
||||||
|
cpu.clearNmi();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public SortedSet<Device> getDevices() {
|
public SortedSet<Device> getDevices() {
|
||||||
// Expose a copy of the device list, not the original
|
// Expose a copy of the device list, not the original
|
||||||
return new TreeSet<Device>(devices);
|
return new TreeSet<Device>(devices);
|
||||||
|
|||||||
@@ -113,7 +113,9 @@ public class Cpu implements InstructionTable {
|
|||||||
* Reset the CPU to known initial values.
|
* Reset the CPU to known initial values.
|
||||||
*/
|
*/
|
||||||
public void reset() throws MemoryAccessException {
|
public void reset() throws MemoryAccessException {
|
||||||
// Registers
|
/* TODO: In reality, the stack pointer could be anywhere
|
||||||
|
on the stack after reset. This non-deterministic behavior might be
|
||||||
|
worth while to simulate. */
|
||||||
state.sp = 0xff;
|
state.sp = 0xff;
|
||||||
|
|
||||||
// Set the PC to the address stored in the reset vector
|
// Set the PC to the address stored in the reset vector
|
||||||
@@ -131,6 +133,8 @@ public class Cpu implements InstructionTable {
|
|||||||
state.overflowFlag = false;
|
state.overflowFlag = false;
|
||||||
state.negativeFlag = false;
|
state.negativeFlag = false;
|
||||||
|
|
||||||
|
state.irqAsserted = false;
|
||||||
|
|
||||||
// Clear illegal opcode trap.
|
// Clear illegal opcode trap.
|
||||||
state.opTrap = false;
|
state.opTrap = false;
|
||||||
|
|
||||||
@@ -150,12 +154,20 @@ public class Cpu implements InstructionTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs an individual machine cycle.
|
* Performs an individual instruction cycle.
|
||||||
*/
|
*/
|
||||||
public void step() throws MemoryAccessException {
|
public void step() throws MemoryAccessException {
|
||||||
// Store the address from which the IR was read, for debugging
|
// Store the address from which the IR was read, for debugging
|
||||||
state.lastPc = state.pc;
|
state.lastPc = state.pc;
|
||||||
|
|
||||||
|
// Check for Interrupts before doing anything else.
|
||||||
|
// This will set the PC and jump to the interrupt vector.
|
||||||
|
if (state.nmiAsserted) {
|
||||||
|
handleNmi();
|
||||||
|
} else if (state.irqAsserted && !getIrqDisableFlag()) {
|
||||||
|
handleIrq(state.pc);
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch memory location for this instruction.
|
// Fetch memory location for this instruction.
|
||||||
state.ir = bus.read(state.pc);
|
state.ir = bus.read(state.pc);
|
||||||
irAddressMode = (state.ir >> 2) & 0x07;
|
irAddressMode = (state.ir >> 2) & 0x07;
|
||||||
@@ -247,16 +259,7 @@ 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()) {
|
if (!getIrqDisableFlag()) {
|
||||||
// Set the break flag before pushing.
|
handleIrq(state.pc + 1);
|
||||||
setBreakFlag();
|
|
||||||
// Push program counter + 1 onto the stack
|
|
||||||
stackPush((state.pc + 1 >> 8) & 0xff); // PC high byte
|
|
||||||
stackPush(state.pc + 1 & 0xff); // PC low byte
|
|
||||||
stackPush(state.getStatusFlag());
|
|
||||||
// Set the Interrupt Disabled flag. RTI will clear it.
|
|
||||||
setIrqDisableFlag();
|
|
||||||
// Load interrupt vector address into PC
|
|
||||||
state.pc = address(bus.read(IRQ_VECTOR_L), bus.read(IRQ_VECTOR_H));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x08: // PHP - Push Processor Status - Implied
|
case 0x08: // PHP - Push Processor Status - Implied
|
||||||
@@ -729,6 +732,35 @@ public class Cpu implements InstructionTable {
|
|||||||
delayLoop(state.ir);
|
delayLoop(state.ir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleIrq(int returnPc) throws MemoryAccessException {
|
||||||
|
handleInterrupt(returnPc, IRQ_VECTOR_L, IRQ_VECTOR_H);
|
||||||
|
clearIrq();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleNmi() throws MemoryAccessException {
|
||||||
|
handleInterrupt(state.pc, NMI_VECTOR_L, NMI_VECTOR_H);
|
||||||
|
clearNmi();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the common behavior of BRK, /IRQ, and /NMI
|
||||||
|
*
|
||||||
|
* @throws MemoryAccessException
|
||||||
|
*/
|
||||||
|
private void handleInterrupt(int returnPc, int vectorLow, int vectorHigh) throws MemoryAccessException {
|
||||||
|
// Set the break flag before pushing.
|
||||||
|
setBreakFlag();
|
||||||
|
// Push program counter + 1 onto the stack
|
||||||
|
stackPush((returnPc >> 8) & 0xff); // PC high byte
|
||||||
|
stackPush(returnPc & 0xff); // PC low byte
|
||||||
|
stackPush(state.getStatusFlag());
|
||||||
|
// Set the Interrupt Disabled flag. RTI will clear it.
|
||||||
|
setIrqDisableFlag();
|
||||||
|
|
||||||
|
// Load interrupt vector address into PC
|
||||||
|
state.pc = address(bus.read(vectorLow), bus.read(vectorHigh));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add with Carry, used by all addressing mode implementations of ADC.
|
* Add with Carry, used by all addressing mode implementations of ADC.
|
||||||
* As a side effect, this will set the overflow and carry flags as
|
* As a side effect, this will set the overflow and carry flags as
|
||||||
@@ -1161,6 +1193,34 @@ public class Cpu implements InstructionTable {
|
|||||||
return state.getStatusFlag();
|
return state.getStatusFlag();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate transition from logic-high to logic-low on the INT line.
|
||||||
|
*/
|
||||||
|
public void assertIrq() {
|
||||||
|
state.irqAsserted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate transition from logic-low to logic-high of the INT line.
|
||||||
|
*/
|
||||||
|
public void clearIrq() {
|
||||||
|
state.irqAsserted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate transition from logic-high to logic-low on the NMI line.
|
||||||
|
*/
|
||||||
|
public void assertNmi() {
|
||||||
|
state.nmiAsserted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulate transition from logic-low to logic-high of the NMI line.
|
||||||
|
*/
|
||||||
|
public void clearNmi() {
|
||||||
|
state.nmiAsserted = false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push an item onto the stack, and decrement the stack counter.
|
* Push an item onto the stack, and decrement the stack counter.
|
||||||
* Will wrap-around if already at the bottom of the stack (This
|
* Will wrap-around if already at the bottom of the stack (This
|
||||||
@@ -1305,6 +1365,8 @@ public class Cpu implements InstructionTable {
|
|||||||
public int[] args = new int[2];
|
public int[] args = new int[2];
|
||||||
public int instSize;
|
public int instSize;
|
||||||
public boolean opTrap;
|
public boolean opTrap;
|
||||||
|
public boolean irqAsserted;
|
||||||
|
public boolean nmiAsserted;
|
||||||
|
|
||||||
/* Status Flag Register bits */
|
/* Status Flag Register bits */
|
||||||
public boolean carryFlag;
|
public boolean carryFlag;
|
||||||
@@ -1341,6 +1403,7 @@ public class Cpu implements InstructionTable {
|
|||||||
this.args[1] = s.args[1];
|
this.args[1] = s.args[1];
|
||||||
this.instSize = s.instSize;
|
this.instSize = s.instSize;
|
||||||
this.opTrap = s.opTrap;
|
this.opTrap = s.opTrap;
|
||||||
|
this.irqAsserted = s.irqAsserted;
|
||||||
this.carryFlag = s.carryFlag;
|
this.carryFlag = s.carryFlag;
|
||||||
this.negativeFlag = s.negativeFlag;
|
this.negativeFlag = s.negativeFlag;
|
||||||
this.zeroFlag = s.zeroFlag;
|
this.zeroFlag = s.zeroFlag;
|
||||||
|
|||||||
@@ -23,10 +23,8 @@
|
|||||||
|
|
||||||
package com.loomcom.symon.devices;
|
package com.loomcom.symon.devices;
|
||||||
|
|
||||||
import com.loomcom.symon.exceptions.*;
|
import com.loomcom.symon.exceptions.MemoryAccessException;
|
||||||
|
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,6 +56,8 @@ public class Acia extends Device {
|
|||||||
private int commandRegister;
|
private int commandRegister;
|
||||||
private int controlRegister;
|
private int controlRegister;
|
||||||
|
|
||||||
|
private boolean receiveIrqEnabled = false;
|
||||||
|
private boolean transmitIrqEnabled = false;
|
||||||
private boolean overrun = false;
|
private boolean overrun = false;
|
||||||
|
|
||||||
private long lastTxWrite = 0;
|
private long lastTxWrite = 0;
|
||||||
@@ -105,7 +105,7 @@ public class Acia extends Device {
|
|||||||
reset();
|
reset();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
commandRegister = data;
|
setCommandRegister(data);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
setControlRegister(data);
|
setControlRegister(data);
|
||||||
@@ -115,13 +115,23 @@ public class Acia extends Device {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void setCommandRegister(int data) {
|
||||||
|
commandRegister = data;
|
||||||
|
|
||||||
|
// Bit 1 controls receiver IRQ behavior
|
||||||
|
receiveIrqEnabled = (commandRegister & 0x02) == 0;
|
||||||
|
// Bits 2 & 3 controls transmit IRQ behavior
|
||||||
|
transmitIrqEnabled = (commandRegister & 0x08) == 0 && (commandRegister & 0x04) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the control register and associated state.
|
* Set the control register and associated state.
|
||||||
*
|
*
|
||||||
* @param data
|
* @param data
|
||||||
*/
|
*/
|
||||||
public void setControlRegister(int data) {
|
private void setControlRegister(int data) {
|
||||||
this.controlRegister = data;
|
controlRegister = data;
|
||||||
|
|
||||||
// If the value of the data is 0, this is a request to reset,
|
// If the value of the data is 0, this is a request to reset,
|
||||||
// otherwise it's a control update.
|
// otherwise it's a control update.
|
||||||
@@ -245,18 +255,28 @@ public class Acia extends Device {
|
|||||||
|
|
||||||
public synchronized void rxWrite(int data) {
|
public synchronized void rxWrite(int data) {
|
||||||
rxFull = true;
|
rxFull = true;
|
||||||
|
|
||||||
|
if (receiveIrqEnabled) {
|
||||||
|
getBus().assertIrq();
|
||||||
|
}
|
||||||
|
|
||||||
rxChar = data;
|
rxChar = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized int txRead() {
|
public synchronized int txRead() {
|
||||||
txEmpty = true;
|
txEmpty = true;
|
||||||
|
|
||||||
|
if (transmitIrqEnabled) {
|
||||||
|
getBus().assertIrq();
|
||||||
|
}
|
||||||
|
|
||||||
return txChar;
|
return txChar;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void txWrite(int data) {
|
public synchronized void txWrite(int data) {
|
||||||
lastTxWrite = System.nanoTime();
|
lastTxWrite = System.nanoTime();
|
||||||
txEmpty = false;
|
|
||||||
txChar = data;
|
txChar = data;
|
||||||
|
txEmpty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -278,6 +298,8 @@ public class Acia extends Device {
|
|||||||
txEmpty = true;
|
txEmpty = true;
|
||||||
rxChar = 0;
|
rxChar = 0;
|
||||||
rxFull = false;
|
rxFull = false;
|
||||||
|
receiveIrqEnabled = false;
|
||||||
|
transmitIrqEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,83 @@
|
|||||||
package com.loomcom.symon;
|
package com.loomcom.symon;
|
||||||
|
|
||||||
import org.junit.*;
|
|
||||||
|
|
||||||
import com.loomcom.symon.devices.Acia;
|
import com.loomcom.symon.devices.Acia;
|
||||||
import com.loomcom.symon.exceptions.FifoUnderrunException;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
public class AciaTest {
|
public class AciaTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldTriggerInterruptOnRxFullIfRxIrqEnabled() throws Exception {
|
||||||
|
Bus mockBus = mock(Bus.class);
|
||||||
|
|
||||||
|
Acia acia = new Acia(0x000);
|
||||||
|
acia.setBus(mockBus);
|
||||||
|
|
||||||
|
// Disable TX IRQ, Enable RX IRQ
|
||||||
|
acia.write(2, 0x00);
|
||||||
|
|
||||||
|
acia.rxWrite('a');
|
||||||
|
|
||||||
|
verify(mockBus, atLeastOnce()).assertIrq();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotTriggerInterruptOnRxFullIfRxIrqNotEnabled() throws Exception {
|
||||||
|
Bus mockBus = mock(Bus.class);
|
||||||
|
|
||||||
|
Acia acia = new Acia(0x000);
|
||||||
|
acia.setBus(mockBus);
|
||||||
|
|
||||||
|
// Disable TX IRQ, Disable RX IRQ
|
||||||
|
acia.write(2, 0x02);
|
||||||
|
|
||||||
|
acia.rxWrite('a');
|
||||||
|
|
||||||
|
verify(mockBus, never()).assertIrq();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldTriggerInterruptOnTxEmptyIfTxIrqEnabled() throws Exception {
|
||||||
|
Bus mockBus = mock(Bus.class);
|
||||||
|
|
||||||
|
Acia acia = new Acia(0x000);
|
||||||
|
acia.setBus(mockBus);
|
||||||
|
|
||||||
|
// Enable TX IRQ, Disable RX IRQ
|
||||||
|
acia.write(2, 0x06);
|
||||||
|
|
||||||
|
// Write data
|
||||||
|
acia.write(0, 'a');
|
||||||
|
|
||||||
|
verify(mockBus, never()).assertIrq();
|
||||||
|
|
||||||
|
// Transmission should cause IRQ
|
||||||
|
acia.txRead();
|
||||||
|
|
||||||
|
verify(mockBus, atLeastOnce()).assertIrq();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldNotTriggerInterruptOnTxEmptyIfTxIrqNotEnabled() throws Exception {
|
||||||
|
Bus mockBus = mock(Bus.class);
|
||||||
|
|
||||||
|
Acia acia = new Acia(0x000);
|
||||||
|
acia.setBus(mockBus);
|
||||||
|
|
||||||
|
// Disable TX IRQ, Disable RX IRQ
|
||||||
|
acia.write(2, 0x02);
|
||||||
|
|
||||||
|
// Write data
|
||||||
|
acia.write(0, 'a');
|
||||||
|
|
||||||
|
// Transmission should cause IRQ
|
||||||
|
acia.txRead();
|
||||||
|
|
||||||
|
verify(mockBus, never()).assertIrq();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void newAciaShouldHaveTxEmptyStatus() throws Exception {
|
public void newAciaShouldHaveTxEmptyStatus() throws Exception {
|
||||||
Acia acia = new Acia(0x000);
|
Acia acia = new Acia(0x000);
|
||||||
|
|||||||
@@ -10,113 +10,145 @@ import com.loomcom.symon.exceptions.*;
|
|||||||
*/
|
*/
|
||||||
public class BusTest extends TestCase {
|
public class BusTest extends TestCase {
|
||||||
|
|
||||||
public BusTest(String testName) {
|
public BusTest(String testName) {
|
||||||
super(testName);
|
super(testName);
|
||||||
}
|
|
||||||
|
|
||||||
public static Test suite() {
|
|
||||||
return new TestSuite(BusTest.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCreatingWithStartAndEndAddresses() {
|
|
||||||
Bus b = null;
|
|
||||||
|
|
||||||
b = new Bus(0x00, 0xff);
|
|
||||||
assertEquals(0x00, b.startAddress());
|
|
||||||
assertEquals(0xff, b.endAddress());
|
|
||||||
|
|
||||||
b = new Bus(0x20, 0xea);
|
|
||||||
assertEquals(0x20, b.startAddress());
|
|
||||||
assertEquals(0xea, b.endAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testCreatingWithSize() {
|
|
||||||
Bus b = null;
|
|
||||||
|
|
||||||
b = new Bus(256);
|
|
||||||
assertEquals(0x00, b.startAddress());
|
|
||||||
assertEquals(0xff, b.endAddress());
|
|
||||||
|
|
||||||
b = new Bus(4096);
|
|
||||||
assertEquals(0x000, b.startAddress());
|
|
||||||
assertEquals(0xfff, b.endAddress());
|
|
||||||
|
|
||||||
b = new Bus(65536);
|
|
||||||
assertEquals(0x0000, b.startAddress());
|
|
||||||
assertEquals(0xffff, b.endAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testAddDevice() throws MemoryRangeException {
|
|
||||||
Device memory = new Memory(0x0000, 0x00ff, true);
|
|
||||||
Device rom = new Memory(0x0100, 0x02ff, false);
|
|
||||||
|
|
||||||
Bus b = new Bus(0x0000, 0xffff);
|
|
||||||
|
|
||||||
assertEquals(0, b.getDevices().size());
|
|
||||||
b.addDevice(memory);
|
|
||||||
assertEquals(1, b.getDevices().size());
|
|
||||||
b.addDevice(rom);
|
|
||||||
assertEquals(2, b.getDevices().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testOverlappingDevicesShouldFail() throws MemoryRangeException {
|
|
||||||
Device memory = new Memory(0x0000, 0x0100, true);
|
|
||||||
Device rom = new Memory(0x00ff, 0x0200, false);
|
|
||||||
|
|
||||||
Bus b = new Bus(0x0000, 0xffff);
|
|
||||||
|
|
||||||
b.addDevice(memory);
|
|
||||||
|
|
||||||
try {
|
|
||||||
b.addDevice(rom);
|
|
||||||
fail("Should have thrown a MemoryRangeException.");
|
|
||||||
} catch (MemoryRangeException ex) {
|
|
||||||
// expected
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public void testIsCompleteWithFirstDeviceNotStartingAtStartAddress() throws MemoryRangeException {
|
public static Test suite() {
|
||||||
Device memory = new Memory(0x00ff, 0xff00, true);
|
return new TestSuite(BusTest.class);
|
||||||
|
}
|
||||||
|
|
||||||
Bus b = new Bus(0x0000, 0xffff);
|
public void testCreatingWithStartAndEndAddresses() {
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
Bus b = null;
|
||||||
b.addDevice(memory);
|
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIsCompleteWithOneDevice() throws MemoryRangeException {
|
b = new Bus(0x00, 0xff);
|
||||||
Device memory = new Memory(0x0000, 0xffff, true);
|
assertEquals(0x00, b.startAddress());
|
||||||
Bus b = new Bus(0x0000, 0xffff);
|
assertEquals(0xff, b.endAddress());
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
|
||||||
b.addDevice(memory);
|
|
||||||
assertTrue("Address space should have been complete!", b.isComplete());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIsCompleteWithTwoDevices() throws MemoryRangeException {
|
b = new Bus(0x20, 0xea);
|
||||||
Device memory = new Memory(0x0000, 0x7fff, true);
|
assertEquals(0x20, b.startAddress());
|
||||||
Device rom = new Memory(0x8000, 0xffff, false);
|
assertEquals(0xea, b.endAddress());
|
||||||
|
}
|
||||||
|
|
||||||
Bus b = new Bus(0x0000, 0xffff);
|
public void testCreatingWithSize() {
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
Bus b = null;
|
||||||
b.addDevice(memory);
|
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
|
||||||
b.addDevice(rom);
|
|
||||||
assertTrue("Address space should have been complete!", b.isComplete());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void testIsCompleteWithThreeDevices() throws MemoryRangeException {
|
b = new Bus(256);
|
||||||
Device memory = new Memory(0x0000, 0x7fff, true);
|
assertEquals(0x00, b.startAddress());
|
||||||
Device rom1 = new Memory(0x8000, 0xBfff, false);
|
assertEquals(0xff, b.endAddress());
|
||||||
Device rom2 = new Memory(0xC000, 0xffff, false);
|
|
||||||
|
|
||||||
Bus b = new Bus(0x0000, 0xffff);
|
b = new Bus(4096);
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
assertEquals(0x000, b.startAddress());
|
||||||
b.addDevice(memory);
|
assertEquals(0xfff, b.endAddress());
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
|
||||||
b.addDevice(rom1);
|
b = new Bus(65536);
|
||||||
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
assertEquals(0x0000, b.startAddress());
|
||||||
b.addDevice(rom2);
|
assertEquals(0xffff, b.endAddress());
|
||||||
assertTrue("Address space should have been complete!", b.isComplete());
|
}
|
||||||
}
|
|
||||||
|
public void testAddDevice() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x00ff, true);
|
||||||
|
Device rom = new Memory(0x0100, 0x02ff, false);
|
||||||
|
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
|
||||||
|
assertEquals(0, b.getDevices().size());
|
||||||
|
b.addDevice(memory);
|
||||||
|
assertEquals(1, b.getDevices().size());
|
||||||
|
b.addDevice(rom);
|
||||||
|
assertEquals(2, b.getDevices().size());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testOverlappingDevicesShouldFail() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x0100, true);
|
||||||
|
Device rom = new Memory(0x00ff, 0x0200, false);
|
||||||
|
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
|
||||||
|
b.addDevice(memory);
|
||||||
|
|
||||||
|
try {
|
||||||
|
b.addDevice(rom);
|
||||||
|
fail("Should have thrown a MemoryRangeException.");
|
||||||
|
} catch (MemoryRangeException ex) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithFirstDeviceNotStartingAtStartAddress() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x00ff, 0xff00, true);
|
||||||
|
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
b.addDevice(memory);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithOneDevice() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0xffff, true);
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
b.addDevice(memory);
|
||||||
|
assertTrue("Address space should have been complete!", b.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithTwoDevices() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x7fff, true);
|
||||||
|
Device rom = new Memory(0x8000, 0xffff, false);
|
||||||
|
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
b.addDevice(memory);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
b.addDevice(rom);
|
||||||
|
assertTrue("Address space should have been complete!", b.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIsCompleteWithThreeDevices() throws MemoryRangeException {
|
||||||
|
Device memory = new Memory(0x0000, 0x7fff, true);
|
||||||
|
Device rom1 = new Memory(0x8000, 0xBfff, false);
|
||||||
|
Device rom2 = new Memory(0xC000, 0xffff, false);
|
||||||
|
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
b.addDevice(memory);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
b.addDevice(rom1);
|
||||||
|
assertFalse("Address space was unexpectedly complete!", b.isComplete());
|
||||||
|
b.addDevice(rom2);
|
||||||
|
assertTrue("Address space should have been complete!", b.isComplete());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSetAndClearIrq() throws Exception {
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
Cpu c = new Cpu();
|
||||||
|
b.addCpu(c);
|
||||||
|
|
||||||
|
assertFalse(c.getCpuState().irqAsserted);
|
||||||
|
|
||||||
|
b.assertIrq();
|
||||||
|
|
||||||
|
assertTrue(c.getCpuState().irqAsserted);
|
||||||
|
|
||||||
|
b.clearIrq();
|
||||||
|
|
||||||
|
assertFalse(c.getCpuState().irqAsserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testSetAndClearNmi() throws Exception {
|
||||||
|
Bus b = new Bus(0x0000, 0xffff);
|
||||||
|
Cpu c = new Cpu();
|
||||||
|
b.addCpu(c);
|
||||||
|
|
||||||
|
assertFalse(c.getCpuState().nmiAsserted);
|
||||||
|
|
||||||
|
b.assertNmi();
|
||||||
|
|
||||||
|
assertTrue(c.getCpuState().nmiAsserted);
|
||||||
|
|
||||||
|
b.clearNmi();
|
||||||
|
|
||||||
|
assertFalse(c.getCpuState().nmiAsserted);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7,303 +7,303 @@ import junit.framework.*;
|
|||||||
|
|
||||||
public class CpuAccumulatorModeTest extends TestCase {
|
public class CpuAccumulatorModeTest extends TestCase {
|
||||||
|
|
||||||
protected Cpu cpu;
|
protected Cpu cpu;
|
||||||
protected Bus bus;
|
protected Bus bus;
|
||||||
protected Memory mem;
|
protected Memory mem;
|
||||||
|
|
||||||
public void setUp() throws MemoryRangeException, MemoryAccessException {
|
public void setUp() throws MemoryRangeException, MemoryAccessException {
|
||||||
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, 0x10000);
|
this.mem = new Memory(0x0000, 0x10000);
|
||||||
bus.addCpu(cpu);
|
bus.addCpu(cpu);
|
||||||
bus.addDevice(mem);
|
bus.addDevice(mem);
|
||||||
|
|
||||||
// Load the reset vector.
|
// Load the reset vector.
|
||||||
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
bus.write(0xfffc, Bus.DEFAULT_LOAD_ADDRESS & 0x00ff);
|
||||||
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
|
// Assert initial state
|
||||||
assertEquals(0, cpu.getAccumulator());
|
assertEquals(0, cpu.getAccumulator());
|
||||||
assertEquals(0, cpu.getXRegister());
|
assertEquals(0, cpu.getXRegister());
|
||||||
assertEquals(0, cpu.getYRegister());
|
assertEquals(0, cpu.getYRegister());
|
||||||
assertEquals(0x200, cpu.getProgramCounter());
|
assertEquals(0x200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
assertEquals(0x20, cpu.getProcessorStatus());
|
assertEquals(0x20, cpu.getProcessorStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The following opcodes are tested for correctness in this file:
|
* The following opcodes are tested for correctness in this file:
|
||||||
*
|
*
|
||||||
* ASL - $0a
|
* ASL - $0a
|
||||||
* ROL - $2a
|
* ROL - $2a
|
||||||
* LSR - $4a
|
* LSR - $4a
|
||||||
* ROR - $6a
|
* ROR - $6a
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* ASL - Arithmetic Shift Left - $0a */
|
/* ASL - Arithmetic Shift Left - $0a */
|
||||||
|
|
||||||
public void test_ASL() throws MemoryAccessException {
|
public void test_ASL() throws MemoryAccessException {
|
||||||
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
||||||
0x0a, // ASL A
|
0x0a, // ASL A
|
||||||
|
|
||||||
0xa9, 0x01, // LDA #$01
|
0xa9, 0x01, // LDA #$01
|
||||||
0x0a, // ASL A
|
0x0a, // ASL A
|
||||||
|
|
||||||
0xa9, 0x02, // LDA #$02
|
0xa9, 0x02, // LDA #$02
|
||||||
0x0a, // ASL A
|
0x0a, // ASL A
|
||||||
|
|
||||||
0xa9, 0x44, // LDA #$44
|
0xa9, 0x44, // LDA #$44
|
||||||
0x0a, // ASL A
|
0x0a, // ASL A
|
||||||
|
|
||||||
0xa9, 0x80, // LDA #$80
|
0xa9, 0x80, // LDA #$80
|
||||||
0x0a); // ASL A
|
0x0a); // ASL A
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x00, cpu.getAccumulator());
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
assertTrue(cpu.getZeroFlag());
|
assertTrue(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x02, cpu.getAccumulator());
|
assertEquals(0x02, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x04, cpu.getAccumulator());
|
assertEquals(0x04, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x88, cpu.getAccumulator());
|
assertEquals(0x88, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertTrue(cpu.getNegativeFlag());
|
assertTrue(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x00, cpu.getAccumulator());
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
assertTrue(cpu.getZeroFlag());
|
assertTrue(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertTrue(cpu.getCarryFlag());
|
assertTrue(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ROL - Rotate Left - $2a */
|
/* ROL - Rotate Left - $2a */
|
||||||
|
|
||||||
public void test_ROL() throws MemoryAccessException {
|
public void test_ROL() throws MemoryAccessException {
|
||||||
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
||||||
0x2a, // ROL A (m=%00000000, c=0)
|
0x2a, // ROL A (m=%00000000, c=0)
|
||||||
0xa9, 0x01, // LDA #$01
|
0xa9, 0x01, // LDA #$01
|
||||||
0x2a, // ROL A (m=%00000010, c=0)
|
0x2a, // ROL A (m=%00000010, c=0)
|
||||||
0x38, // SEC (m=%00000010, c=1)
|
0x38, // SEC (m=%00000010, c=1)
|
||||||
0x2a, // ROL A (m=%00000101, c=0)
|
0x2a, // ROL A (m=%00000101, c=0)
|
||||||
0x2a, // ROL A (m=%00001010, c=0)
|
0x2a, // ROL A (m=%00001010, c=0)
|
||||||
0x2a, // ROL A (m=%00010100, c=0)
|
0x2a, // ROL A (m=%00010100, c=0)
|
||||||
0x2a, // ROL A (m=%00101000, c=0)
|
0x2a, // ROL A (m=%00101000, c=0)
|
||||||
0x2a, // ROL A (m=%01010000, c=0)
|
0x2a, // ROL A (m=%01010000, c=0)
|
||||||
0x2a, // ROL A (m=%10100000, c=0)
|
0x2a, // ROL A (m=%10100000, c=0)
|
||||||
0x2a, // ROL A (m=%01000000, c=1)
|
0x2a, // ROL A (m=%01000000, c=1)
|
||||||
0x2a); // ROL A (m=%10000001, c=0)
|
0x2a); // ROL A (m=%10000001, c=0)
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x00, cpu.getAccumulator());
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
assertTrue(cpu.getZeroFlag());
|
assertTrue(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x02, cpu.getAccumulator());
|
assertEquals(0x02, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x05, cpu.getAccumulator());
|
assertEquals(0x05, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x0a, cpu.getAccumulator());
|
assertEquals(0x0a, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x14, cpu.getAccumulator());
|
assertEquals(0x14, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x28, cpu.getAccumulator());
|
assertEquals(0x28, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x50, cpu.getAccumulator());
|
assertEquals(0x50, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0xa0, cpu.getAccumulator());
|
assertEquals(0xa0, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertTrue(cpu.getNegativeFlag());
|
assertTrue(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x40, cpu.getAccumulator());
|
assertEquals(0x40, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertTrue(cpu.getCarryFlag());
|
assertTrue(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x81, cpu.getAccumulator());
|
assertEquals(0x81, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertTrue(cpu.getNegativeFlag());
|
assertTrue(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* LSR - Logical Shift Right - $4a */
|
/* LSR - Logical Shift Right - $4a */
|
||||||
|
|
||||||
public void test_LSR() throws MemoryAccessException {
|
public void test_LSR() throws MemoryAccessException {
|
||||||
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
||||||
0x4a, // LSR A
|
0x4a, // LSR A
|
||||||
|
|
||||||
0xa9, 0x01, // LDA #$01
|
0xa9, 0x01, // LDA #$01
|
||||||
0x4a, // LSR A
|
0x4a, // LSR A
|
||||||
|
|
||||||
0xa9, 0x02, // LDA #$02
|
0xa9, 0x02, // LDA #$02
|
||||||
0x4a, // LSR A
|
0x4a, // LSR A
|
||||||
|
|
||||||
0xa9, 0x44, // LDA #$44
|
0xa9, 0x44, // LDA #$44
|
||||||
0x4a, // LSR A
|
0x4a, // LSR A
|
||||||
|
|
||||||
0xa9, 0x80, // LDA #$80
|
0xa9, 0x80, // LDA #$80
|
||||||
0x4a, // LSR A
|
0x4a, // LSR A
|
||||||
|
|
||||||
0x38, // SEC
|
0x38, // SEC
|
||||||
0xa9, 0x02, // LDA #$02
|
0xa9, 0x02, // LDA #$02
|
||||||
0x4a); // LSR $05
|
0x4a); // LSR $05
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x00, cpu.getAccumulator());
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
assertTrue(cpu.getZeroFlag());
|
assertTrue(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x00, cpu.getAccumulator());
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
assertTrue(cpu.getZeroFlag());
|
assertTrue(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertTrue(cpu.getCarryFlag());
|
assertTrue(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x01, cpu.getAccumulator());
|
assertEquals(0x01, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x22, cpu.getAccumulator());
|
assertEquals(0x22, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x40, cpu.getAccumulator());
|
assertEquals(0x40, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
// Setting Carry should not affect the result.
|
// Setting Carry should not affect the result.
|
||||||
cpu.step(3);
|
cpu.step(3);
|
||||||
assertEquals(0x01, cpu.getAccumulator());
|
assertEquals(0x01, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ROR - Rotate Right - $6a */
|
/* ROR - Rotate Right - $6a */
|
||||||
|
|
||||||
public void test_ROR() throws MemoryAccessException {
|
public void test_ROR() throws MemoryAccessException {
|
||||||
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
bus.loadProgram(0xa9, 0x00, // LDA #$00
|
||||||
0x6a, // ROR A (m=%00000000, c=0)
|
0x6a, // ROR A (m=%00000000, c=0)
|
||||||
0xa9, 0x10, // LDA #$10
|
0xa9, 0x10, // LDA #$10
|
||||||
0x6a, // ROR A (m=%00001000, c=0)
|
0x6a, // ROR A (m=%00001000, c=0)
|
||||||
0x6a, // ROR A (m=%00000100, c=0)
|
0x6a, // ROR A (m=%00000100, c=0)
|
||||||
0x6a, // ROR A (m=%00000010, c=0)
|
0x6a, // ROR A (m=%00000010, c=0)
|
||||||
0x6a, // ROR A (m=%00000001, c=0)
|
0x6a, // ROR A (m=%00000001, c=0)
|
||||||
0x6a, // ROR A (m=%00000000, c=1)
|
0x6a, // ROR A (m=%00000000, c=1)
|
||||||
0x6a, // ROR A (m=%10000000, c=0)
|
0x6a, // ROR A (m=%10000000, c=0)
|
||||||
0x6a, // ROR A (m=%01000000, c=0)
|
0x6a, // ROR A (m=%01000000, c=0)
|
||||||
0x6a, // ROR A (m=%00100000, c=0)
|
0x6a, // ROR A (m=%00100000, c=0)
|
||||||
0x6a); // ROR A (m=%00010000, c=0)
|
0x6a); // ROR A (m=%00010000, c=0)
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x00, cpu.getAccumulator());
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
assertTrue(cpu.getZeroFlag());
|
assertTrue(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step(2);
|
cpu.step(2);
|
||||||
assertEquals(0x08, cpu.getAccumulator());
|
assertEquals(0x08, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x04, cpu.getAccumulator());
|
assertEquals(0x04, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x02, cpu.getAccumulator());
|
assertEquals(0x02, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x01, cpu.getAccumulator());
|
assertEquals(0x01, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x00, cpu.getAccumulator());
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
assertTrue(cpu.getZeroFlag());
|
assertTrue(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertTrue(cpu.getCarryFlag());
|
assertTrue(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x80, cpu.getAccumulator());
|
assertEquals(0x80, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertTrue(cpu.getNegativeFlag());
|
assertTrue(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x40, cpu.getAccumulator());
|
assertEquals(0x40, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x20, cpu.getAccumulator());
|
assertEquals(0x20, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
cpu.step();
|
cpu.step();
|
||||||
assertEquals(0x10, cpu.getAccumulator());
|
assertEquals(0x10, cpu.getAccumulator());
|
||||||
assertFalse(cpu.getZeroFlag());
|
assertFalse(cpu.getZeroFlag());
|
||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -345,6 +345,197 @@ public class CpuTest extends TestCase {
|
|||||||
assertFalse(cpu.getNegativeFlag());
|
assertFalse(cpu.getNegativeFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testIrq() 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
|
||||||
|
assertEquals(0x1236, cpu.getProgramCounter());
|
||||||
|
assertEquals(0x33, cpu.getAccumulator());
|
||||||
|
|
||||||
|
// Be sure that the IRQ line is no longer held low
|
||||||
|
assertFalse(cpu.getCpuState().irqAsserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIrqHonorsIrqDisabledFlag() throws Exception {
|
||||||
|
// Ensure the IRQ disable flag is set
|
||||||
|
cpu.setIrqDisableFlag();
|
||||||
|
|
||||||
|
// 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(); // Should be ignored, because the disable flag is set.
|
||||||
|
|
||||||
|
cpu.step();
|
||||||
|
assertTrue(cpu.getIrqDisableFlag()); // Should have been left alone.
|
||||||
|
assertEquals(0x0205, cpu.getProgramCounter());
|
||||||
|
assertEquals(0x02, cpu.getAccumulator());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testIrqPushesCorrectReturnAddressOntoStack() throws Exception {
|
||||||
|
// Ensure the IRQs are enabled
|
||||||
|
cpu.clearIrqDisableFlag();
|
||||||
|
|
||||||
|
// Set the IRQ vector
|
||||||
|
bus.write(0xffff, 0x10);
|
||||||
|
bus.write(0xfffe, 0x00);
|
||||||
|
|
||||||
|
// Create an IRQ handler at 0x1000 that just RTIs.
|
||||||
|
cpu.setProgramCounter(0x1000);
|
||||||
|
bus.loadProgram(0xea, 0xea, 0x40); // NOP, NOP, RTI
|
||||||
|
|
||||||
|
cpu.setProgramCounter(0x0200);
|
||||||
|
|
||||||
|
// Create a little program at 0x0200 with three instruction sizes.
|
||||||
|
bus.loadProgram(0x18, // CLC
|
||||||
|
0xa9, 0x01, // LDA #$01
|
||||||
|
0x6d, 0x06, 0x02, // ADC $0207
|
||||||
|
0x00, // BRK
|
||||||
|
0x03); // $03 (data @ $0206)
|
||||||
|
|
||||||
|
cpu.step(); // CLC
|
||||||
|
assertEquals(0x0201, cpu.getProgramCounter()); // First instruction executed.
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.assertIrq();
|
||||||
|
cpu.step(); // NOP
|
||||||
|
assertEquals(0x1001, cpu.getProgramCounter());
|
||||||
|
cpu.step(); // NOP
|
||||||
|
assertEquals(0x1002, cpu.getProgramCounter());
|
||||||
|
cpu.step(); // RTI: PC -> 0x0201
|
||||||
|
assertEquals(0x0201, cpu.getProgramCounter());
|
||||||
|
cpu.step(); // LDA $#01
|
||||||
|
assertEquals(0x0203, cpu.getProgramCounter());
|
||||||
|
|
||||||
|
cpu.assertIrq();
|
||||||
|
cpu.step(); // NOP
|
||||||
|
assertEquals(0x1001, cpu.getProgramCounter());
|
||||||
|
cpu.step(); // NOP
|
||||||
|
assertEquals(0x1002, cpu.getProgramCounter());
|
||||||
|
cpu.step(); // RTI: PC -> 0x0203
|
||||||
|
assertEquals(0x0203, cpu.getProgramCounter());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNmi() 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();
|
||||||
|
assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the NMI
|
||||||
|
assertEquals(0x1002, cpu.getProgramCounter());
|
||||||
|
assertEquals(0x33, cpu.getAccumulator());
|
||||||
|
|
||||||
|
// Be sure that the NMI line is no longer held low
|
||||||
|
assertFalse(cpu.getCpuState().nmiAsserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testNmiIgnoresIrqDisableFlag() throws Exception {
|
||||||
|
// Set the IRQ disable flag, which should be ignored by the NMI
|
||||||
|
cpu.setIrqDisableFlag();
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
assertTrue(cpu.getIrqDisableFlag()); // Should have been set by the NMI
|
||||||
|
assertEquals(0x1002, cpu.getProgramCounter());
|
||||||
|
assertEquals(0x33, cpu.getAccumulator());
|
||||||
|
|
||||||
|
// Be sure that the NMI line is no longer held low
|
||||||
|
assertFalse(cpu.getCpuState().nmiAsserted);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testAddress() {
|
public void testAddress() {
|
||||||
assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
|
assertEquals(0xf1ea, cpu.address(0xea, 0xf1));
|
||||||
assertEquals(0x00ea, cpu.address(0xea, 0x00));
|
assertEquals(0x00ea, cpu.address(0xea, 0x00));
|
||||||
|
|||||||
Reference in new issue
Block a user