From 7766e76362a6bf54414527b9a61de4cd17bf7ee5 Mon Sep 17 00:00:00 2001 From: "Seth J. Morabito" Date: Sat, 27 Dec 2008 01:43:07 -0800 Subject: [PATCH] BCD is finally working. I thought that would never happen. All Immediate Mode and Implied Mode opcodes have been implemented. Next up, zero page! --- src/main/java/com/loomcom/symon/Cpu.java | 73 +++++------ .../loomcom/symon/CpuImmediateModeTest.java | 119 ++++++++++++++++++ 2 files changed, 152 insertions(+), 40 deletions(-) diff --git a/src/main/java/com/loomcom/symon/Cpu.java b/src/main/java/com/loomcom/symon/Cpu.java index 5933b61..1bfa10e 100644 --- a/src/main/java/com/loomcom/symon/Cpu.java +++ b/src/main/java/com/loomcom/symon/Cpu.java @@ -381,11 +381,8 @@ public class Cpu implements InstructionTable { case 0x69: // ADC - Add with Carry - Immediate if (decimalModeFlag) { a = adcDecimal(a, operands[0]); - clearNegativeFlag(); // All results of BCD math are positive - setZeroFlag(a == 0); } else { a = adc(a, operands[0]); - setArithmeticFlags(a); } break; case 0x6a: // n/a @@ -684,14 +681,12 @@ public class Cpu implements InstructionTable { case 0xe9: // SBC - Subtract with Carry (Borrow) - Immediate if (decimalModeFlag) { a = sbcDecimal(a, operands[0]); - clearNegativeFlag(); - setZeroFlag(a == 0); } else { a = sbc(a, operands[0]); - setArithmeticFlags(a); } break; case 0xea: // NOP + // Does nothing. break; case 0xeb: // n/a break; @@ -754,33 +749,33 @@ public class Cpu implements InstructionTable { int carry6 = (operand & 0x7f) + (acc & 0x7f) + getCarryBit(); setCarryFlag((result & 0x100) != 0); setOverflowFlag(carryFlag ^ ((carry6 & 0x80) != 0)); - result = result & 0xff; + result &= 0xff; + setArithmeticFlags(result); return result; } /** * Add with Carry (BCD). */ + public int adcDecimal(int acc, int operand) { - // Thank you to Doug Jones for this beautiful BCD algorithm. From: - // http://www.cs.uiowa.edu/~jones/bcd/bcd.html - acc += 0x066; - int t2 = acc + operand; - int t3 = acc ^ operand; - int t4 = t2 ^ t3; - int t5 = ~t4 & 0x110; - int t6 = (t5 >> 2) | (t5 >> 3); - int result = t2 - t6; - if (result > 0xff) { - result &= 0xff; - setCarryFlag(); - } - return result; - } + int l, h, result; + l = (acc & 0x0f) + (operand & 0x0f) + getCarryBit(); + if ((l & 0xff) > 9) l += 6; + h = (acc >> 4) + (operand >> 4) + (l > 15 ? 1 : 0); + if ((h & 0xff) > 9) h += 6; + result = (l & 0x0f) | (h << 4); + result &= 0xff; + setCarryFlag(h > 15); + setZeroFlag(result == 0); + setNegativeFlag(false); // BCD is never negative + setOverflowFlag(false); // BCD never sets overflow flag + return result; + } /** * Common code for Subtract with Carry. Just calls ADC of the - * one's complement of the opeerand. This lets the N, V, C, and Z + * one's complement of the operand. This lets the N, V, C, and Z * flags work out nicely without any additional logic. * * @param acc @@ -788,7 +783,10 @@ public class Cpu implements InstructionTable { * @return */ public int sbc(int acc, int operand) { - return adc(acc, ~operand); + int result; + result = adc(acc, ~operand); + setArithmeticFlags(result); + return result; } /** @@ -799,7 +797,17 @@ public class Cpu implements InstructionTable { * @return */ public int sbcDecimal(int acc, int operand) { - return adcDecimal(acc, tensComplement(operand)); + int l, h, result; + l = (acc & 0x0f) - (operand & 0x0f) - (carryFlag ? 0 : 1); + if ((l & 0x10) != 0) l -= 6; + h = (acc >> 4) - (operand >> 4) - ((l & 0x10) != 0 ? 1 : 0); + if ((h & 0x10) != 0) h -= 6; + result = (l & 0x0f) | (h << 4); + setCarryFlag((h & 0xff) < 15); + setZeroFlag(result == 0); + setNegativeFlag(false); // BCD is never negative + setOverflowFlag(false); // BCD never sets overflow flag + return (result & 0xff); } /** @@ -1241,19 +1249,4 @@ public class Cpu implements InstructionTable { } } - /* - * Calculate the 10's Complement of a byte. Used in BCD mode SBC. - */ - private int tensComplement(int val) { - // Thank you to Doug Jones for this truly beautiful and obscure algorithm. - // http://www.cs.uiowa.edu/~jones/bcd/bcd.html - int t1 = 0xfff - val; - int t2 = -val; - int t3 = t1 ^ 0x001; - int t4 = t2 ^ t3; - int t5 = ~t4 & 0x110; - int t6 = (t5 >> 2) | (t5 >> 3); - return t2 - t6; - } - } \ No newline at end of file diff --git a/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java b/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java index 513e2dc..f411e6c 100644 --- a/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java +++ b/src/test/java/com/loomcom/symon/CpuImmediateModeTest.java @@ -22,7 +22,32 @@ public class CpuImmediateModeTest extends TestCase { 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: + * + * ORA - $09 + * AND - $29 + * EOR - $49 + * ADC - $69 + * LDY - $a0 + * + * LDX - $a2 + * LDA - $a9 + * CPY - $c0 + * CMP - $c9 + * CPX - $e0 + * + * SBC - $e9 + */ /* ORA Immediate Mode Tests - 0x09 */ @@ -732,4 +757,98 @@ public class CpuImmediateModeTest extends TestCase { assertFalse(cpu.getCarryFlag()); } + + public void test_SBC_DecimalMode() { + bus.loadProgram(0xf8, + 0xa9, 0x00, + 0xe9, 0x01); + cpu.step(3); + assertEquals(0x98, cpu.getAccumulator()); + assertFalse(cpu.getCarryFlag()); // borrow = set flag + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getOverflowFlag()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getDecimalModeFlag()); + + cpu.reset(); + + bus.loadProgram(0xf8, + 0xa9, 0x99, + 0xe9, 0x01); + cpu.step(3); + assertEquals(0x97, cpu.getAccumulator()); + assertTrue(cpu.getCarryFlag()); // No borrow = clear flag + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getOverflowFlag()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getDecimalModeFlag()); + + cpu.reset(); + + bus.loadProgram(0xf8, + 0xa9, 0x50, + 0xe9, 0x01); + cpu.step(3); + assertEquals(0x48, cpu.getAccumulator()); + assertTrue(cpu.getCarryFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getOverflowFlag()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getDecimalModeFlag()); + + + cpu.reset(); + + bus.loadProgram(0xf8, // SED + 0xa9, 0x02, // LDA #$02 + 0xe9, 0x01); // SBC #$01 + cpu.step(3); + assertEquals(0x00, cpu.getAccumulator()); + assertTrue(cpu.getCarryFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getOverflowFlag()); + assertTrue(cpu.getZeroFlag()); + assertTrue(cpu.getDecimalModeFlag()); + + cpu.reset(); + + bus.loadProgram(0xf8, // SED + 0xa9, 0x10, // LDA #$10 + 0xe9, 0x11); // SBC #$11 + cpu.step(3); + assertEquals(0x98, cpu.getAccumulator()); + assertFalse(cpu.getCarryFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getOverflowFlag()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getDecimalModeFlag()); + + cpu.reset(); + + bus.loadProgram(0x38, // SEC + 0xf8, // SED + 0xa9, 0x05, // LDA #$05 + 0xe9, 0x01); // SBC #$01 + cpu.step(4); + assertEquals(0x04, cpu.getAccumulator()); + assertTrue(cpu.getCarryFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getOverflowFlag()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getDecimalModeFlag()); + + cpu.reset(); + + bus.loadProgram(0x38, // SEC + 0xf8, // SED + 0xa9, 0x00, // LDA #$00 + 0xe9, 0x01); // SBC #$01 + cpu.step(4); + assertEquals(0x99, cpu.getAccumulator()); + assertFalse(cpu.getCarryFlag()); + assertFalse(cpu.getNegativeFlag()); + assertFalse(cpu.getOverflowFlag()); + assertFalse(cpu.getZeroFlag()); + assertTrue(cpu.getDecimalModeFlag()); + } } \ No newline at end of file