1
0
mirror of https://github.com/sethm/symon.git synced 2024-06-03 07:29:30 +00:00

At long last, implemented BRK. Lots more unit tests for stack processing, as well.

This commit is contained in:
Seth J. Morabito 2008-12-23 02:21:27 -08:00
parent 46a5943736
commit 473b47e832
3 changed files with 546 additions and 18 deletions

View File

@ -9,6 +9,16 @@ public class Cpu implements InstructionTable {
public static final int DEFAULT_BASE_ADDRESS = 0x200;
/* Process status register mnemonics */
public static final int P_CARRY = 0x01;
public static final int P_ZERO = 0x02;
public static final int P_IRQ_DISABLE = 0x04;
public static final int P_DECIMAL = 0x08;
public static final int P_BREAK = 0x10;
// Bit 5 is always '1'
public static final int P_OVERFLOW = 0x40;
public static final int P_NEGATIVE = 0x80;
/* The Bus */
private Bus bus;
@ -19,7 +29,7 @@ public class Cpu implements InstructionTable {
/* Internal Registers */
private int pc; // Program Counter register
private int sp; // Stack Pointer register
private int sp; // Stack Pointer register, offset into page 1
private int ir; // Instruction register
/* Operands for the current instruction */
@ -35,7 +45,6 @@ public class Cpu implements InstructionTable {
private boolean decimalModeFlag;
private boolean breakFlag;
private boolean overflowFlag;
// Note: Zero Flag and Negative Flag are read directly from Accumulator.
/**
* Construct a new CPU.
@ -61,7 +70,7 @@ public class Cpu implements InstructionTable {
*/
public void reset() {
// Registers
sp = 0x01ff;
sp = 0xff;
// Set the PC to the address stored in 0xfffc
pc = CpuUtils.address(bus.read(0xfffc), bus.read(0xfffd));
@ -110,8 +119,15 @@ public class Cpu implements InstructionTable {
// Execute
switch(ir) {
case 0x00: // HLT
// TODO: Halt!
case 0x00: // BRK - Force Interrupt, Implied
if (!getIrqDisableFlag()) {
stackPush((pc >> 8) & 0xff); // PC high byte
stackPush(pc & 0xff); // PC low byte
stackPush(getProcessorStatus());
// Load interrupt vector address into PC
pc = CpuUtils.address(bus.read(0xfffc), bus.read(0xfffd));
setBreakFlag();
}
break;
case 0x01: // n/a
break;
@ -726,6 +742,14 @@ public class Cpu implements InstructionTable {
this.negativeFlag = negativeFlag;
}
public void setNegativeFlag() {
this.negativeFlag = true;
}
public void clearNegativeFlag() {
this.negativeFlag = false;
}
/**
* @return the carry flag
*/
@ -921,6 +945,69 @@ public class Cpu implements InstructionTable {
this.pc = addr;
}
public int getStackPointer() {
return sp;
}
public void setStackPointer(int offset) {
this.sp = offset;
}
/**
* @value The value of the Process Status Register bits to be set.
*/
public void setProcessorStatus(int value) {
if ((value&P_CARRY) != 0)
setCarryFlag();
else
clearCarryFlag();
if ((value&P_ZERO) != 0)
setZeroFlag();
else
clearZeroFlag();
if ((value&P_IRQ_DISABLE) != 0)
setIrqDisableFlag();
else
clearIrqDisableFlag();
if ((value&P_DECIMAL) != 0)
setDecimalModeFlag();
else
clearDecimalModeFlag();
if ((value&P_BREAK) != 0)
setBreakFlag();
else
clearBreakFlag();
if ((value&P_OVERFLOW) != 0)
setOverflowFlag();
else
clearOverflowFlag();
if ((value&P_NEGATIVE) != 0)
setNegativeFlag();
else
clearNegativeFlag();
}
/**
* @returns The value of the Process Status Register, as a byte.
*/
public int getProcessorStatus() {
int status = 0x20;
if (getCarryFlag()) { status |= P_CARRY; }
if (getZeroFlag()) { status |= P_ZERO; }
if (getIrqDisableFlag()) { status |= P_IRQ_DISABLE; }
if (getDecimalModeFlag()) { status |= P_DECIMAL; }
if (getBreakFlag()) { status |= P_BREAK; }
if (getOverflowFlag()) { status |= P_OVERFLOW; }
if (getNegativeFlag()) { status |= P_NEGATIVE; }
return status;
}
/**
* @return A string representing the current status register state.
*/
@ -955,24 +1042,38 @@ public class Cpu implements InstructionTable {
/**
* Push an item onto the stack, and decrement the stack counter.
* Silently fails to push onto the stack if SP is
* TODO: Unit tests.
*/
protected void push(int data) {
bus.write(sp, data);
if (sp > 0x100) { sp--; }
void stackPush(int data) {
bus.write(0x100+sp, data);
if (sp == 0)
sp = 0xff;
else
--sp;
}
/**
* Pop a byte off the user stack, and increment the stack counter.
* TODO: Unit tests.
* Pre-increment the stack pointer, and return the top of the stack.
*/
protected int pop() {
int data = bus.read(sp);
if (sp < 0x1ff) { sp++; }
int stackPop() {
if (sp == 0xff)
sp = 0x00;
else
++sp;
int data = bus.read(0x100+sp);
return data;
}
/**
* Peek at the value currently at the top of the stack
*/
int stackPeek() {
return bus.read(0x100+sp+1);
}
/*
* Increment the PC, rolling over if necessary.
*/

View File

@ -0,0 +1,130 @@
package com.loomcom.symon;
import com.loomcom.symon.devices.Memory;
import com.loomcom.symon.exceptions.MemoryRangeException;
import junit.framework.*;
public class CpuImpliedModeTest extends TestCase {
protected Cpu cpu;
protected Bus bus;
protected Memory mem;
public void setUp() throws MemoryRangeException {
this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff);
this.mem = new Memory(0x0000, 0x10000);
bus.addCpu(cpu);
bus.addDevice(mem);
// Load the reset vector.
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
cpu.reset();
}
/*
* The following opcodes are tested for correctness in this file:
*
* BRK - $00
* CLC - $18
* CLD - $d8
* CLI - $58
* CLV - $B8
*
* DEX - $ca
* DEY - $88
* INX - $e8
* INY - $c8
* NOP - $ea
*
* PHA - $48
* PHP - $08
* PLA - $68
* PLP - $28
* RTI - $40
*
* RTS - $60
* SEC - $38
* SED - $f8
* SEI - $78
* TAX - $aa
*
* TAY - $a8
* TSX - $ba
* TXA - $8a
* TXS - $9a
* TYA - $98
*/
/* BRK Tests - 0x00 */
public void test_BRK() {
cpu.setCarryFlag();
cpu.setOverflowFlag();
assertEquals(0x20|Cpu.P_CARRY|Cpu.P_OVERFLOW,
cpu.getProcessorStatus());
assertEquals(0xff, cpu.stackPeek());
assertFalse(cpu.getBreakFlag());
assertEquals(0x0200, cpu.getProgramCounter());
assertEquals(0xff, cpu.getStackPointer());
bus.loadProgram(0xea,
0xea,
0xea,
0x00);
cpu.step(3); // Three NOP instructions
assertEquals(0x203, cpu.getProgramCounter());
cpu.step(); // Triggers the BRK
// Was at PC = 0x204, which should now be on the stack
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
assertEquals(0x04, bus.read(0x1fe)); // PC low byte
assertEquals(0x20|Cpu.P_CARRY|Cpu.P_OVERFLOW,
bus.read(0x1fd)); // Processor Status
// Reset to original contents of PC
assertEquals(0x0200, cpu.getProgramCounter());
assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0x20|Cpu.P_CARRY|Cpu.P_OVERFLOW|Cpu.P_BREAK,
cpu.getProcessorStatus());
assertEquals(0x20|Cpu.P_CARRY|Cpu.P_OVERFLOW,
cpu.stackPeek());
}
public void test_BRK_HonorsIrqDisableFlag() {
cpu.setIrqDisableFlag();
bus.loadProgram(0xea,
0xea,
0xea,
0x00,
0xea,
0xea);
cpu.step(3); // Three NOP instructions
assertEquals(0x203, cpu.getProgramCounter());
// Triggers the BRK, which should do nothing because
// of the Interrupt Disable flag
cpu.step();
// Reset to original contents of PC
assertEquals(0x0204, cpu.getProgramCounter());
// Empty stack
assertEquals(0xff, cpu.getStackPointer());
cpu.step(2); // Two more NOPs
// Reset to original contents of PC
assertEquals(0x0206, cpu.getProgramCounter());
// Empty stack
assertEquals(0xff, cpu.getStackPointer());
}
}

View File

@ -9,11 +9,11 @@ import com.loomcom.symon.exceptions.*;
*
*/
public class CpuTest extends TestCase {
private Cpu cpu;
private Bus bus;
private Memory mem;
public CpuTest(String testName) {
super(testName);
}
@ -21,7 +21,7 @@ public class CpuTest extends TestCase {
public static Test suite() {
return new TestSuite(CpuTest.class);
}
public void setUp() throws MemoryRangeException {
this.cpu = new Cpu();
this.bus = new Bus(0x0000, 0xffff);
@ -35,7 +35,7 @@ public class CpuTest extends TestCase {
cpu.reset();
}
public void testReset() {
assertEquals(0, cpu.getAccumulator());
assertEquals(0, cpu.getXRegister());
@ -49,4 +49,301 @@ public class CpuTest extends TestCase {
assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag());
}
public void testStack() {
cpu.stackPush(0x13);
assertEquals(0x13, cpu.stackPop());
cpu.stackPush(0x12);
assertEquals(0x12, cpu.stackPop());
for (int i = 0x00; i <= 0xff; i++) {
cpu.stackPush(i);
}
for (int i = 0xff; i >= 0x00; i--) {
assertEquals(i, cpu.stackPop());
}
}
public void testStackPush() {
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0xff, bus.read(0x1ff));
cpu.stackPush(0x06);
assertEquals(0xfe, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
cpu.stackPush(0x05);
assertEquals(0xfd, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
cpu.stackPush(0x04);
assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
cpu.stackPush(0x03);
assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
cpu.stackPush(0x02);
assertEquals(0xfa, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
cpu.stackPush(0x01);
assertEquals(0xf9, cpu.getStackPointer());
assertEquals(0x06, bus.read(0x1ff));
assertEquals(0x05, bus.read(0x1fe));
assertEquals(0x04, bus.read(0x1fd));
assertEquals(0x03, bus.read(0x1fc));
assertEquals(0x02, bus.read(0x1fb));
assertEquals(0x01, bus.read(0x1fa));
}
public void testStackPushWrapsAroundToStackTop() {
cpu.setStackPointer(0x01);
cpu.stackPush(0x01);
assertEquals(0x01, bus.read(0x101));
assertEquals(0x00, cpu.getStackPointer());
cpu.stackPush(0x02);
assertEquals(0x02, bus.read(0x100));
assertEquals(0xff, cpu.getStackPointer());
cpu.stackPush(0x03);
assertEquals(0x03, bus.read(0x1ff));
assertEquals(0xfe, cpu.getStackPointer());
}
public void testStackPop() {
bus.write(0x1ff, 0x06);
bus.write(0x1fe, 0x05);
bus.write(0x1fd, 0x04);
bus.write(0x1fc, 0x03);
bus.write(0x1fb, 0x02);
bus.write(0x1fa, 0x01);
cpu.setStackPointer(0xf9);
assertEquals(0x01, cpu.stackPop());
assertEquals(0xfa, cpu.getStackPointer());
assertEquals(0x02, cpu.stackPop());
assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x03, cpu.stackPop());
assertEquals(0xfc, cpu.getStackPointer());
assertEquals(0x04, cpu.stackPop());
assertEquals(0xfd, cpu.getStackPointer());
assertEquals(0x05, cpu.stackPop());
assertEquals(0xfe, cpu.getStackPointer());
assertEquals(0x06, cpu.stackPop());
assertEquals(0xff, cpu.getStackPointer());
}
public void testStackPopWrapsAroundToStackBottom() {
bus.write(0x1ff, 0x0f); // top of stack
bus.write(0x100, 0xf0); // bottom of stack
bus.write(0x101, 0xf1);
bus.write(0x102, 0xf2);
cpu.setStackPointer(0xfe);
assertEquals(0x0f, cpu.stackPop());
assertEquals(0xff, cpu.getStackPointer());
assertEquals(0xf0, cpu.stackPop());
assertEquals(0x00, cpu.getStackPointer());
assertEquals(0xf1, cpu.stackPop());
assertEquals(0x01, cpu.getStackPointer());
assertEquals(0xf2, cpu.stackPop());
assertEquals(0x02, cpu.getStackPointer());
}
public void testStackPeekDoesNotAlterStackPointer() {
int val = 0;
assertEquals(0xff, cpu.stackPeek());
assertEquals(0xff, cpu.getStackPointer());
cpu.stackPush(0x01);
assertEquals(0x01, cpu.stackPeek());
assertEquals(0xfe, cpu.getStackPointer());
cpu.stackPush(0x02);
assertEquals(0x02, cpu.stackPeek());
assertEquals(0xfd, cpu.getStackPointer());
cpu.stackPush(0x03);
assertEquals(0x03, cpu.stackPeek());
assertEquals(0xfc, cpu.getStackPointer());
cpu.stackPush(0x04);
assertEquals(0x04, cpu.stackPeek());
assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x04, cpu.stackPeek());
assertEquals(0xfb, cpu.getStackPointer());
assertEquals(0x04, cpu.stackPeek());
assertEquals(0xfb, cpu.getStackPointer());
}
public void testGetProcessorStatus() {
// By default, no flags are set. Remember, bit 5
// is always '1'.
assertEquals(0x20, cpu.getProcessorStatus());
cpu.setCarryFlag();
assertEquals(0x21, cpu.getProcessorStatus());
cpu.setZeroFlag();
assertEquals(0x23, cpu.getProcessorStatus());
cpu.setIrqDisableFlag();
assertEquals(0x27, cpu.getProcessorStatus());
cpu.setDecimalModeFlag();
assertEquals(0x2f, cpu.getProcessorStatus());
cpu.setBreakFlag();
assertEquals(0x3f, cpu.getProcessorStatus());
cpu.setOverflowFlag();
assertEquals(0x7f, cpu.getProcessorStatus());
cpu.setNegativeFlag();
assertEquals(0xff, cpu.getProcessorStatus());
cpu.clearCarryFlag();
assertEquals(0xfe, cpu.getProcessorStatus());
cpu.clearZeroFlag();
assertEquals(0xfc, cpu.getProcessorStatus());
cpu.clearIrqDisableFlag();
assertEquals(0xf8, cpu.getProcessorStatus());
cpu.clearDecimalModeFlag();
assertEquals(0xf0, cpu.getProcessorStatus());
cpu.clearBreakFlag();
assertEquals(0xe0, cpu.getProcessorStatus());
cpu.clearOverflowFlag();
assertEquals(0xa0, cpu.getProcessorStatus());
cpu.clearNegativeFlag();
assertEquals(0x20, cpu.getProcessorStatus());
}
public void testSetProcessorStatus() {
// Default
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY);
assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE);
assertTrue(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO);
assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
Cpu.P_OVERFLOW);
assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
Cpu.P_OVERFLOW|Cpu.P_BREAK);
assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertTrue(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL);
assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertTrue(cpu.getDecimalModeFlag());
assertTrue(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20|Cpu.P_CARRY|Cpu.P_NEGATIVE|Cpu.P_ZERO|
Cpu.P_OVERFLOW|Cpu.P_BREAK|Cpu.P_DECIMAL|
Cpu.P_IRQ_DISABLE);
assertTrue(cpu.getCarryFlag());
assertTrue(cpu.getZeroFlag());
assertTrue(cpu.getIrqDisableFlag());
assertTrue(cpu.getDecimalModeFlag());
assertTrue(cpu.getBreakFlag());
assertTrue(cpu.getOverflowFlag());
assertTrue(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x20);
assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag());
cpu.setProcessorStatus(0x00);
assertFalse(cpu.getCarryFlag());
assertFalse(cpu.getZeroFlag());
assertFalse(cpu.getIrqDisableFlag());
assertFalse(cpu.getDecimalModeFlag());
assertFalse(cpu.getBreakFlag());
assertFalse(cpu.getOverflowFlag());
assertFalse(cpu.getNegativeFlag());
}
}