From ed943687bfa41c85ffeac00fff50dd394be8a2bd Mon Sep 17 00:00:00 2001 From: Seth Morabito Date: Wed, 7 Jan 2009 18:26:11 -0800 Subject: [PATCH] Implemented Relative mode (branch) instructions and unit tests. Fixed a bug in the instruction size table. --- src/main/java/com/loomcom/symon/Cpu.java | 48 ++- .../com/loomcom/symon/InstructionTable.java | 2 +- .../loomcom/symon/CpuRelativeModeTest.java | 280 ++++++++++++++++++ 3 files changed, 321 insertions(+), 9 deletions(-) create mode 100644 src/test/java/com/loomcom/symon/CpuRelativeModeTest.java diff --git a/src/main/java/com/loomcom/symon/Cpu.java b/src/main/java/com/loomcom/symon/Cpu.java index f8ec02f..4ad8d7d 100644 --- a/src/main/java/com/loomcom/symon/Cpu.java +++ b/src/main/java/com/loomcom/symon/Cpu.java @@ -188,7 +188,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(k); break; - case 0x10: // TODO: implement + case 0x10: // BPL - Branch if Positive - Relative + if (!getNegativeFlag()) { + pc = relAddress(operands[0]); + } break; case 0x11: // TODO: implement break; @@ -277,7 +280,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(k); break; - case 0x30: // TODO: implement + case 0x30: // BMI - Branch if Minus - Relative + if (getNegativeFlag()) { + pc = relAddress(operands[0]); + } break; case 0x31: // TODO: implement break; @@ -356,7 +362,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(k); break; - case 0x50: // TODO: implement + case 0x50: // BVC - Branch if Overflow Clear - Relative + if (!getOverflowFlag()) { + pc = relAddress(operands[0]); + } break; case 0x51: // TODO: implement break; @@ -459,7 +468,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(k); break; - case 0x70: // TODO: implement + case 0x70: // BVS - Branch if Overflow Set - Relative + if (getOverflowFlag()) { + pc = relAddress(operands[0]); + } break; case 0x71: // TODO: implement break; @@ -538,7 +550,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(x); break; - case 0x90: // TODO: implement + case 0x90: // BCC - Branch if Carry Clear - Relative + if (!getCarryFlag()) { + pc = relAddress(operands[0]); + } break; case 0x91: // TODO: implement break; @@ -617,7 +632,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(x); break; - case 0xb0: // TODO: implement + case 0xb0: // BCS - Branch if Carry Set - Relative + if (getCarryFlag()) { + pc = relAddress(operands[0]); + } break; case 0xb1: // TODO: implement break; @@ -698,7 +716,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(k); break; - case 0xd0: // TODO: implement + case 0xd0: // BNE - Branch if Not Equal to Zero - Relative + if (!getZeroFlag()) { + pc = relAddress(operands[0]); + } break; case 0xd1: // TODO: implement break; @@ -781,7 +802,10 @@ public class Cpu implements InstructionTable { setArithmeticFlags(k); break; - case 0xf0: // TODO: implement + case 0xf0: // BEQ - Branch if Equal to Zero - Relative + if (getZeroFlag()) { + pc = relAddress(operands[0]); + } break; case 0xf1: // TODO: implement break; @@ -1562,6 +1586,14 @@ public class Cpu implements InstructionTable { return (zp+getXRegister())&0xff; } + /** + * Given a single byte, compute the offset address. + */ + int relAddress(int offset) { + // Cast the offset to a signed byte to handle negative offsets + return (pc + (byte)offset) & 0xffff; + } + /** * Given a single byte, compute the Zero Page,Y offset address. */ diff --git a/src/main/java/com/loomcom/symon/InstructionTable.java b/src/main/java/com/loomcom/symon/InstructionTable.java index e0994b8..c503fcb 100644 --- a/src/main/java/com/loomcom/symon/InstructionTable.java +++ b/src/main/java/com/loomcom/symon/InstructionTable.java @@ -217,7 +217,7 @@ public interface InstructionTable { 2, 2, 0, 0, 2, 2, 2, 0, 1, 0, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 0, 3, 0, 0, 2, 2, 2, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, - 0, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, + 2, 2, 0, 0, 2, 2, 2, 0, 1, 3, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, 2, 2, 0, 0, 0, 2, 2, 0, 1, 3, 0, 0, 0, 3, 3, 0, 2, 2, 0, 0, 2, 2, 2, 0, 1, 2, 1, 0, 3, 3, 3, 0, diff --git a/src/test/java/com/loomcom/symon/CpuRelativeModeTest.java b/src/test/java/com/loomcom/symon/CpuRelativeModeTest.java new file mode 100644 index 0000000..3cd8878 --- /dev/null +++ b/src/test/java/com/loomcom/symon/CpuRelativeModeTest.java @@ -0,0 +1,280 @@ +package com.loomcom.symon; + +import com.loomcom.symon.devices.Memory; +import com.loomcom.symon.exceptions.MemoryRangeException; +import junit.framework.TestCase; + +public class CpuRelativeModeTest extends TestCase { + + protected Cpu cpu; + protected Bus bus; + protected Memory mem; + + protected void setUp() throws Exception { + this.cpu = new Cpu(); + this.bus = new Bus(0x0000, 0xffff); + this.mem = new Memory(0x0000, 0x10000); + bus.addCpu(cpu); + bus.addDevice(mem); + + // Load the reset vector. + bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff); + bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8); + + cpu.reset(); + // Assert initial state + assertEquals(0, cpu.getAccumulator()); + assertEquals(0, cpu.getXRegister()); + assertEquals(0, cpu.getYRegister()); + assertEquals(0x200, cpu.getProgramCounter()); + assertEquals(0xff, cpu.getStackPointer()); + assertEquals(0x20, cpu.getProcessorStatus()); + } + + /* + * The following opcodes are tested for correctness in this file: + * + * BPL - Branch if Positive - 0x10 + * BMI - Branch if Minus - 0x30 + * BVC - Branch if Overflow Clear - 0x50 + * BVS - Branch if Overflow Set - 0x70 + * BCC - Branch if Carry Clear - 0x90 + * BCS - Branch if Carry Set - 0xb0 + * BNE - Branch if Not Equal to Zero - 0xd0 + * BEQ - Branch if Equal to Zero - 0xf0 + * + */ + + /* BPL - Branch if Positive - 0x10 */ + + public void test_BPL() { + // Positive Offset + bus.loadProgram(0x10, 0x05); // BPL $05 ; *=$0202+$05 ($0207) + cpu.setNegativeFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x10, 0x05); // BPL $05 ; *=$0202+$05 ($0207) + cpu.clearNegativeFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0x10, 0xfb); // BPL $fb ; *=$0202-$05 ($01fd) + cpu.setNegativeFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x10, 0xfb); // BPL $fb ; *=$0202-$05 ($01fd) + cpu.clearNegativeFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + } + + /* BMI - Branch if Minus - 0x30 */ + + public void test_BMI() { + // Positive Offset + bus.loadProgram(0x30, 0x05); // BMI $05 ; *=$0202+$05 ($0207) + cpu.setNegativeFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x30, 0x05); // BMI $05 ; *=$0202+$05 ($0207) + cpu.clearNegativeFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0x30, 0xfb); // BMI $fb ; *=$0202-$05 ($01fd) + cpu.setNegativeFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x30, 0xfb); // BMI $fb ; *=$0202-$05 ($01fd) + cpu.clearNegativeFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + } + + /* BVC - Branch if Overflow Clear - 0x50 */ + + public void test_BVC() { + // Positive Offset + bus.loadProgram(0x50, 0x05); // BVC $05 ; *=$0202+$05 ($0207) + cpu.setOverflowFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x50, 0x05); // BVC $05 ; *=$0202+$05 ($0207) + cpu.clearOverflowFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0x50, 0xfb); // BVC $fb ; *=$0202-$05 ($01fd) + cpu.setOverflowFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x50, 0xfb); // BVC $fb ; *=$0202-$05 ($01fd) + cpu.clearOverflowFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + } + + /* BVS - Branch if Overflow Set - 0x70 */ + + public void test_BVS() { + // Positive Offset + bus.loadProgram(0x70, 0x05); // BVS $05 ; *=$0202+$05 ($0207) + cpu.setOverflowFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x70, 0x05); // BVS $05 ; *=$0202+$05 ($0207) + cpu.clearOverflowFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0x70, 0xfb); // BVS $fb ; *=$0202-$05 ($01fd) + cpu.setOverflowFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x70, 0xfb); // BVS $fb ; *=$0202-$05 ($01fd) + cpu.clearOverflowFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + } + + /* BCC - Branch if Carry Clear - 0x90 */ + + public void test_BCC() { + // Positive Offset + bus.loadProgram(0x90, 0x05); // BCC $05 ; *=$0202+$05 ($0207) + cpu.setCarryFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x90, 0x05); // BCC $05 ; *=$0202+$05 ($0207) + cpu.clearCarryFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0x90, 0xfb); // BCC $fb ; *=$0202-$05 ($01fd) + cpu.setCarryFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0x90, 0xfb); // BCC $fb ; *=$0202-$05 ($01fd) + cpu.clearCarryFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + } + + /* BCS - Branch if Carry Set - 0xb0 */ + + public void test_BCS() { + // Positive Offset + bus.loadProgram(0xb0, 0x05); // BCS $05 ; *=$0202+$05 ($0207) + cpu.setCarryFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0xb0, 0x05); // BCS $05 ; *=$0202+$05 ($0207) + cpu.clearCarryFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0xb0, 0xfb); // BCS $fb ; *=$0202-$05 ($01fd) + cpu.setCarryFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0xb0, 0xfb); // BCS $fb ; *=$0202-$05 ($01fd) + cpu.clearCarryFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + } + + /* BNE - Branch if Not Equal to Zero - 0xd0 */ + + public void test_BNE() { + // Positive Offset + bus.loadProgram(0xd0, 0x05); // BNE $05 ; *=$0202+$05 ($0207) + cpu.setZeroFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0xd0, 0x05); // BNE $05 ; *=$0202+$05 ($0207) + cpu.clearZeroFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0xd0, 0xfb); // BNE $fb ; *=$0202-$05 ($01fd) + cpu.setZeroFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0xd0, 0xfb); // BNE $fb ; *=$0202-$05 ($01fd) + cpu.clearZeroFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + } + + /* BEQ - Branch if Equal to Zero - 0xf0 */ + + public void test_BEQ() { + // Positive Offset + bus.loadProgram(0xf0, 0x05); // BEQ $05 ; *=$0202+$05 ($0207) + cpu.setZeroFlag(); + cpu.step(); + assertEquals(0x207, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0xf0, 0x05); // BEQ $05 ; *=$0202+$05 ($0207) + cpu.clearZeroFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + + // Negative Offset + cpu.reset(); + bus.loadProgram(0xf0, 0xfb); // BEQ $fb ; *=$0202-$05 ($01fd) + cpu.setZeroFlag(); + cpu.step(); + assertEquals(0x1fd, cpu.getProgramCounter()); + + cpu.reset(); + bus.loadProgram(0xf0, 0xfb); // BEQ $fb ; *=$0202-$05 ($01fd) + cpu.clearZeroFlag(); + cpu.step(); + assertEquals(0x202, cpu.getProgramCounter()); + } + +} \ No newline at end of file