mirror of
https://github.com/sethm/symon.git
synced 2024-09-30 10:57:06 +00:00
Fixed the BRK instruction. Added SBC and ADC in Binary Coded Decimal
mode. More unit tests.
This commit is contained in:
parent
b46c4e1bec
commit
06ed74e89c
@ -18,7 +18,17 @@ public class Cpu implements InstructionTable {
|
|||||||
// Bit 5 is always '1'
|
// Bit 5 is always '1'
|
||||||
public static final int P_OVERFLOW = 0x40;
|
public static final int P_OVERFLOW = 0x40;
|
||||||
public static final int P_NEGATIVE = 0x80;
|
public static final int P_NEGATIVE = 0x80;
|
||||||
|
|
||||||
|
// NMI vector
|
||||||
|
public static final int IRQ_VECTOR_L = 0xfffa;
|
||||||
|
public static final int IRQ_VECTOR_H = 0xfffb;
|
||||||
|
// Reset vector
|
||||||
|
public static final int RST_VECTOR_L = 0xfffc;
|
||||||
|
public static final int RST_VECTOR_H = 0xfffd;
|
||||||
|
// IRQ vector
|
||||||
|
public static final int NMI_VECTOR_L = 0xfffe;
|
||||||
|
public static final int NMI_VECTOR_H = 0xffff;
|
||||||
|
|
||||||
/* The Bus */
|
/* The Bus */
|
||||||
private Bus bus;
|
private Bus bus;
|
||||||
|
|
||||||
@ -72,8 +82,8 @@ public class Cpu implements InstructionTable {
|
|||||||
// Registers
|
// Registers
|
||||||
sp = 0xff;
|
sp = 0xff;
|
||||||
|
|
||||||
// Set the PC to the address stored in 0xfffc
|
// Set the PC to the address stored in the reset vector
|
||||||
pc = CpuUtils.address(bus.read(0xfffc), bus.read(0xfffd));
|
pc = CpuUtils.address(bus.read(RST_VECTOR_L), bus.read(RST_VECTOR_H));
|
||||||
|
|
||||||
// Clear instruction register.
|
// Clear instruction register.
|
||||||
ir = 0;
|
ir = 0;
|
||||||
@ -104,18 +114,15 @@ public class Cpu implements InstructionTable {
|
|||||||
// Fetch memory location for this instruction.
|
// Fetch memory location for this instruction.
|
||||||
ir = bus.read(pc);
|
ir = bus.read(pc);
|
||||||
|
|
||||||
// TODO: The way we increment the PC may need
|
|
||||||
// to change when interrupts are implemented
|
|
||||||
|
|
||||||
// Increment PC
|
// Increment PC
|
||||||
incProgramCounter();
|
incrementPC();
|
||||||
|
|
||||||
// Decode the instruction and operands
|
// Decode the instruction and operands
|
||||||
int size = Cpu.instructionSizes[ir];
|
int size = Cpu.instructionSizes[ir];
|
||||||
for (int i = 0; i < size-1; i++) {
|
for (int i = 0; i < size-1; i++) {
|
||||||
operands[i] = bus.read(pc);
|
operands[i] = bus.read(pc);
|
||||||
// Increment PC after reading
|
// Increment PC after reading
|
||||||
incProgramCounter();
|
incrementPC();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute
|
// Execute
|
||||||
@ -123,12 +130,16 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
case 0x00: // BRK - Force Interrupt - Implied
|
case 0x00: // BRK - Force Interrupt - Implied
|
||||||
if (!getIrqDisableFlag()) {
|
if (!getIrqDisableFlag()) {
|
||||||
stackPush((pc >> 8) & 0xff); // PC high byte
|
// Set the break flag before pushing.
|
||||||
stackPush(pc & 0xff); // PC low byte
|
|
||||||
stackPush(getProcessorStatus());
|
|
||||||
// Load interrupt vector address into PC
|
|
||||||
pc = CpuUtils.address(bus.read(0xfffc), bus.read(0xfffd));
|
|
||||||
setBreakFlag();
|
setBreakFlag();
|
||||||
|
// Push program counter + 2 onto the stack
|
||||||
|
stackPush((pc+2 >> 8) & 0xff); // PC high byte
|
||||||
|
stackPush(pc+2 & 0xff); // PC low byte
|
||||||
|
stackPush(getProcessorStatus());
|
||||||
|
// Set the Interrupt Disabled flag. RTI will clear it.
|
||||||
|
setIrqDisableFlag();
|
||||||
|
// Load interrupt vector address into PC
|
||||||
|
pc = CpuUtils.address(bus.read(IRQ_VECTOR_L), bus.read(IRQ_VECTOR_H));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x01: // n/a
|
case 0x01: // n/a
|
||||||
@ -368,8 +379,14 @@ public class Cpu implements InstructionTable {
|
|||||||
setArithmeticFlags(a);
|
setArithmeticFlags(a);
|
||||||
break;
|
break;
|
||||||
case 0x69: // ADC - Add with Carry - Immediate
|
case 0x69: // ADC - Add with Carry - Immediate
|
||||||
a = adc(a, operands[0]);
|
if (decimalModeFlag) {
|
||||||
setArithmeticFlags(a);
|
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;
|
break;
|
||||||
case 0x6a: // n/a
|
case 0x6a: // n/a
|
||||||
break;
|
break;
|
||||||
@ -665,8 +682,14 @@ public class Cpu implements InstructionTable {
|
|||||||
setArithmeticFlags(x);
|
setArithmeticFlags(x);
|
||||||
break;
|
break;
|
||||||
case 0xe9: // SBC - Subtract with Carry (Borrow) - Immediate
|
case 0xe9: // SBC - Subtract with Carry (Borrow) - Immediate
|
||||||
a = sbc(a, operands[0]);
|
if (decimalModeFlag) {
|
||||||
setArithmeticFlags(a);
|
a = sbcDecimal(a, operands[0]);
|
||||||
|
clearNegativeFlag();
|
||||||
|
setZeroFlag(a == 0);
|
||||||
|
} else {
|
||||||
|
a = sbc(a, operands[0]);
|
||||||
|
setArithmeticFlags(a);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 0xea: // NOP
|
case 0xea: // NOP
|
||||||
break;
|
break;
|
||||||
@ -719,26 +742,73 @@ public class Cpu implements InstructionTable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
* As a side effect, this will set the overflow and carry flags as
|
||||||
|
* needed.
|
||||||
*
|
*
|
||||||
* @param acc The current value of the accumulator
|
* @param acc The current value of the accumulator
|
||||||
* @param operand The operand
|
* @param operand The operand
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int adc(int acc, int operand) {
|
public int adc(int acc, int operand) {
|
||||||
int result = operand + acc + (carryFlag ? 1 : 0);
|
int result = (operand & 0xff) + (acc & 0xff) + getCarryBit();
|
||||||
int carry = (operand & 0x7f) + (acc & 0x7f) + (carryFlag ? 1 : 0);
|
int carry6 = (operand & 0x7f) + (acc & 0x7f) + getCarryBit();
|
||||||
setCarryFlag(result > 0xff);
|
setCarryFlag((result & 0x100) != 0);
|
||||||
|
setOverflowFlag(carryFlag ^ ((carry6 & 0x80) != 0));
|
||||||
result = result & 0xff;
|
result = result & 0xff;
|
||||||
setOverflowFlag(carryFlag ^ ((carry & 0x80) != 0));
|
|
||||||
return 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* flags work out nicely without any additional logic.
|
||||||
|
*
|
||||||
|
* @param acc
|
||||||
|
* @param operand
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public int sbc(int acc, int operand) {
|
public int sbc(int acc, int operand) {
|
||||||
// Equivalent to ADC of the 2's complement of the operand, minus the /carry
|
return adc(acc, ~operand);
|
||||||
return adc(acc, (--operand ^ 0xff));
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subtract with Carry, BCD mode.
|
||||||
|
*
|
||||||
|
* @param acc
|
||||||
|
* @param operand
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int sbcDecimal(int acc, int operand) {
|
||||||
|
return adcDecimal(acc, tensComplement(operand));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compare two values, and set carry, zero, and negative flags
|
||||||
|
* appropriately.
|
||||||
|
*
|
||||||
|
* @param reg
|
||||||
|
* @param operand
|
||||||
|
*/
|
||||||
public void cmp(int reg, int operand) {
|
public void cmp(int reg, int operand) {
|
||||||
setCarryFlag(reg >= operand);
|
setCarryFlag(reg >= operand);
|
||||||
setZeroFlag(reg == operand);
|
setZeroFlag(reg == operand);
|
||||||
@ -755,6 +825,7 @@ public class Cpu implements InstructionTable {
|
|||||||
zeroFlag = (reg == 0);
|
zeroFlag = (reg == 0);
|
||||||
negativeFlag = (reg & 0x80) != 0;
|
negativeFlag = (reg & 0x80) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the negative flag
|
* @return the negative flag
|
||||||
*/
|
*/
|
||||||
@ -762,10 +833,18 @@ public class Cpu implements InstructionTable {
|
|||||||
return negativeFlag;
|
return negativeFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the negative flag is set, 0 if it is clear.
|
||||||
|
*/
|
||||||
|
public int getNegativeBit() {
|
||||||
|
return (negativeFlag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param register the register value to test for negativity
|
* @param register the register value to test for negativity
|
||||||
*/
|
*/
|
||||||
public void setNegativeFlag(int register) {
|
public void setNegativeFlag(int register) {
|
||||||
|
negativeFlag = (register < 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -789,6 +868,13 @@ public class Cpu implements InstructionTable {
|
|||||||
public boolean getCarryFlag() {
|
public boolean getCarryFlag() {
|
||||||
return carryFlag;
|
return carryFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the carry flag is set, 0 if it is clear.
|
||||||
|
*/
|
||||||
|
public int getCarryBit() {
|
||||||
|
return (carryFlag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param carryFlag the carry flag to set
|
* @param carryFlag the carry flag to set
|
||||||
@ -818,6 +904,13 @@ public class Cpu implements InstructionTable {
|
|||||||
return zeroFlag;
|
return zeroFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the zero flag is set, 0 if it is clear.
|
||||||
|
*/
|
||||||
|
public int getZeroBit() {
|
||||||
|
return (zeroFlag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param zeroFlag the zero flag to set
|
* @param zeroFlag the zero flag to set
|
||||||
*/
|
*/
|
||||||
@ -845,6 +938,13 @@ public class Cpu implements InstructionTable {
|
|||||||
public boolean getIrqDisableFlag() {
|
public boolean getIrqDisableFlag() {
|
||||||
return irqDisableFlag;
|
return irqDisableFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the interrupt disable flag is set, 0 if it is clear.
|
||||||
|
*/
|
||||||
|
public int getIrqDisableBit() {
|
||||||
|
return (irqDisableFlag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param irqDisableFlag the irq disable flag to set
|
* @param irqDisableFlag the irq disable flag to set
|
||||||
@ -869,6 +969,13 @@ public class Cpu implements InstructionTable {
|
|||||||
return decimalModeFlag;
|
return decimalModeFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the decimal mode flag is set, 0 if it is clear.
|
||||||
|
*/
|
||||||
|
public int getDecimalModeBit() {
|
||||||
|
return (decimalModeFlag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param decimalModeFlag the decimal mode flag to set
|
* @param decimalModeFlag the decimal mode flag to set
|
||||||
*/
|
*/
|
||||||
@ -896,6 +1003,13 @@ public class Cpu implements InstructionTable {
|
|||||||
public boolean getBreakFlag() {
|
public boolean getBreakFlag() {
|
||||||
return breakFlag;
|
return breakFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the break flag is set, 0 if it is clear.
|
||||||
|
*/
|
||||||
|
public int getBreakBit() {
|
||||||
|
return (carryFlag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param breakFlag the break flag to set
|
* @param breakFlag the break flag to set
|
||||||
@ -924,6 +1038,13 @@ public class Cpu implements InstructionTable {
|
|||||||
public boolean getOverflowFlag() {
|
public boolean getOverflowFlag() {
|
||||||
return overflowFlag;
|
return overflowFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return 1 if the overflow flag is set, 0 if it is clear.
|
||||||
|
*/
|
||||||
|
public int getOverflowBit() {
|
||||||
|
return (overflowFlag ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param overflowFlag the overflow flag to set
|
* @param overflowFlag the overflow flag to set
|
||||||
@ -1045,14 +1166,16 @@ public class Cpu implements InstructionTable {
|
|||||||
* @return A string representing the current status register state.
|
* @return A string representing the current status register state.
|
||||||
*/
|
*/
|
||||||
public String statusRegisterString() {
|
public String statusRegisterString() {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer("[");
|
||||||
sb.append("(N:" + (getNegativeFlag() ? '1' : '0') + ", ");
|
sb.append(getNegativeFlag() ? 'N' : '.'); // Bit 7
|
||||||
sb.append("V:" + (getOverflowFlag() ? '1' : '0') + ", ");
|
sb.append(getOverflowFlag() ? 'V' : '.'); // Bit 6
|
||||||
sb.append("B:" + (getBreakFlag() ? '1' : '0') + ", ");
|
sb.append("-"); // Bit 5 (always 1)
|
||||||
sb.append("D:" + (getDecimalModeFlag() ? '1' : '0') + ", ");
|
sb.append(getBreakFlag() ? 'B' : '.'); // Bit 4
|
||||||
sb.append("I:" + (getIrqDisableFlag() ? '1' : '0') + ", ");
|
sb.append(getDecimalModeFlag() ? 'D' : '.'); // Bit 3
|
||||||
sb.append("Z:" + (getZeroFlag() ? '1' : '0') + ", ");
|
sb.append(getIrqDisableFlag() ? 'I' : '.'); // Bit 2
|
||||||
sb.append("C:" + (getCarryFlag() ? '1' : '0') + ")");
|
sb.append(getZeroFlag() ? 'Z' : '.'); // Bit 1
|
||||||
|
sb.append(getCarryFlag() ? 'C' : '.'); // Bit 0
|
||||||
|
sb.append("]");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1110,7 +1233,7 @@ public class Cpu implements InstructionTable {
|
|||||||
/*
|
/*
|
||||||
* Increment the PC, rolling over if necessary.
|
* Increment the PC, rolling over if necessary.
|
||||||
*/
|
*/
|
||||||
protected void incProgramCounter() {
|
protected void incrementPC() {
|
||||||
if (pc == 0xffff) {
|
if (pc == 0xffff) {
|
||||||
pc = 0;
|
pc = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -1118,4 +1241,19 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -252,6 +252,83 @@ public class CpuImmediateModeTest extends TestCase {
|
|||||||
assertFalse(cpu.getCarryFlag());
|
assertFalse(cpu.getCarryFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void test_ADC_DecimalMode() {
|
||||||
|
bus.loadProgram(0xf8, // SED
|
||||||
|
0xa9, 0x01, // LDA #$01
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x02, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
assertFalse(cpu.getOverflowFlag());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
bus.loadProgram(0xf8, // SED
|
||||||
|
0xa9, 0x49, // LDA #$49
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x50, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
assertFalse(cpu.getOverflowFlag());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
bus.loadProgram(0xf8, // SED
|
||||||
|
0xa9, 0x50, // LDA #$50
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x51, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
assertFalse(cpu.getOverflowFlag());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
bus.loadProgram(0xf8, // SED
|
||||||
|
0xa9, 0x99, // LDA #$99
|
||||||
|
0x69, 0x01); // ADC #$01
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x00, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
assertFalse(cpu.getOverflowFlag());
|
||||||
|
assertTrue(cpu.getZeroFlag());
|
||||||
|
assertTrue(cpu.getCarryFlag());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
bus.loadProgram(0xf8, // SED
|
||||||
|
0xa9, 0x00, // LDA #$00
|
||||||
|
0x69, 0x99); // ADC #$01
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x99, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
assertFalse(cpu.getOverflowFlag());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
bus.loadProgram(0xf8, // SED
|
||||||
|
0xa9, 0x49, // LDA #$49
|
||||||
|
0x69, 0x99); // ADC #$99
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x48, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
assertFalse(cpu.getOverflowFlag());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertTrue(cpu.getCarryFlag());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
bus.loadProgram(0xf8, // SED
|
||||||
|
0xa9, 0x50, // LDA #$59
|
||||||
|
0x69, 0x99); // ADC #$99
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x49, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getNegativeFlag());
|
||||||
|
assertFalse(cpu.getOverflowFlag());
|
||||||
|
assertFalse(cpu.getZeroFlag());
|
||||||
|
assertTrue(cpu.getCarryFlag());
|
||||||
|
}
|
||||||
|
|
||||||
/* LDY Immediate Mode Tests - 0xa0 */
|
/* LDY Immediate Mode Tests - 0xa0 */
|
||||||
|
|
||||||
@ -616,11 +693,43 @@ public class CpuImmediateModeTest extends TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void test_SBC_IncludesNotOfCarry() {
|
public void test_SBC_IncludesNotOfCarry() {
|
||||||
|
// Subtrace with Carry Flag cleared
|
||||||
|
bus.loadProgram(0x18, // CLC
|
||||||
|
0xa9, 0x05, // LDA #$00
|
||||||
|
0xe9, 0x01); // SBC #$01
|
||||||
|
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x03, cpu.getAccumulator());
|
||||||
|
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
|
|
||||||
|
// Subtrace with Carry Flag cleared
|
||||||
|
bus.loadProgram(0x18, // CLC
|
||||||
|
0xa9, 0x00, // LDA #$00
|
||||||
|
0xe9, 0x01); // SBC #$01
|
||||||
|
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0xfe, cpu.getAccumulator());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
|
||||||
|
// Subtract with Carry Flag set
|
||||||
|
bus.loadProgram(0x38, // SEC
|
||||||
|
0xa9, 0x05, // LDA #$00
|
||||||
|
0xe9, 0x01); // SBC #$01
|
||||||
|
cpu.step(3);
|
||||||
|
assertEquals(0x04, cpu.getAccumulator());
|
||||||
|
assertTrue(cpu.getCarryFlag());
|
||||||
|
|
||||||
|
cpu.reset();
|
||||||
|
|
||||||
|
// Subtract with Carry Flag set
|
||||||
bus.loadProgram(0x38, // SEC
|
bus.loadProgram(0x38, // SEC
|
||||||
0xa9, 0x00, // LDA #$00
|
0xa9, 0x00, // LDA #$00
|
||||||
0xe9, 0x01); // SBC #$01
|
0xe9, 0x01); // SBC #$01
|
||||||
cpu.step(3);
|
cpu.step(3);
|
||||||
assertEquals(0xff, cpu.getAccumulator());
|
assertEquals(0xff, cpu.getAccumulator());
|
||||||
|
assertFalse(cpu.getCarryFlag());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -78,10 +78,16 @@ public class CpuImpliedModeTest extends TestCase {
|
|||||||
assertEquals(0x0200, cpu.getProgramCounter());
|
assertEquals(0x0200, cpu.getProgramCounter());
|
||||||
assertEquals(0xff, cpu.getStackPointer());
|
assertEquals(0xff, cpu.getStackPointer());
|
||||||
|
|
||||||
|
// Set the IRQ vector
|
||||||
|
bus.write(Cpu.IRQ_VECTOR_H, 0x12);
|
||||||
|
bus.write(Cpu.IRQ_VECTOR_L, 0x34);
|
||||||
|
|
||||||
bus.loadProgram(0xea,
|
bus.loadProgram(0xea,
|
||||||
0xea,
|
0xea,
|
||||||
0xea,
|
0xea,
|
||||||
0x00);
|
0x00,
|
||||||
|
0xea,
|
||||||
|
0xea);
|
||||||
|
|
||||||
cpu.step(3); // Three NOP instructions
|
cpu.step(3); // Three NOP instructions
|
||||||
|
|
||||||
@ -89,19 +95,20 @@ public class CpuImpliedModeTest extends TestCase {
|
|||||||
|
|
||||||
cpu.step(); // Triggers the BRK
|
cpu.step(); // Triggers the BRK
|
||||||
|
|
||||||
// Was at PC = 0x204, which should now be on the stack
|
// Was at PC = 0x204. PC+2 should now be on the stack
|
||||||
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
|
assertEquals(0x02, bus.read(0x1ff)); // PC high byte
|
||||||
assertEquals(0x04, bus.read(0x1fe)); // PC low byte
|
assertEquals(0x06, 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,
|
assertEquals(0x20|Cpu.P_CARRY|Cpu.P_OVERFLOW|Cpu.P_BREAK,
|
||||||
|
bus.read(0x1fd)); // Processor Status, with B set
|
||||||
|
|
||||||
|
// Interrupt vector held 0x1234, so we should be there.
|
||||||
|
assertEquals(0x1234, cpu.getProgramCounter());
|
||||||
|
assertEquals(0xfc, cpu.getStackPointer());
|
||||||
|
|
||||||
|
// B and I flags should have been set on P
|
||||||
|
assertEquals(0x20|Cpu.P_CARRY|Cpu.P_OVERFLOW|Cpu.P_BREAK|
|
||||||
|
Cpu.P_IRQ_DISABLE,
|
||||||
cpu.getProcessorStatus());
|
cpu.getProcessorStatus());
|
||||||
assertEquals(0x20|Cpu.P_CARRY|Cpu.P_OVERFLOW,
|
|
||||||
cpu.stackPeek());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void test_BRK_HonorsIrqDisableFlag() {
|
public void test_BRK_HonorsIrqDisableFlag() {
|
||||||
|
Loading…
Reference in New Issue
Block a user