mirror of
https://github.com/sethm/symon.git
synced 2024-11-17 02:06:48 +00:00
Some API changes, lots of test changes.
This commit is contained in:
parent
6443cacdb9
commit
1bd59b048b
@ -63,7 +63,7 @@ public class Bus {
|
||||
* device.
|
||||
*/
|
||||
public boolean isComplete() {
|
||||
// Emtpy maps cannot be complete.
|
||||
// Empty maps cannot be complete.
|
||||
if (devices.isEmpty()) { return false; }
|
||||
|
||||
// Loop over devices and ensure they are contiguous.
|
||||
@ -129,4 +129,16 @@ public class Bus {
|
||||
// Expose a copy of the device list, not the original
|
||||
return new TreeSet<Device>(devices);
|
||||
}
|
||||
|
||||
public Cpu getCpu() {
|
||||
return cpu;
|
||||
}
|
||||
|
||||
public void loadProgram(int... program) {
|
||||
int address = getCpu().getProgramCounter();
|
||||
int i = 0;
|
||||
for (int d : program) {
|
||||
write(address + i++, d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import java.util.Arrays;
|
||||
*/
|
||||
public class Cpu implements InstructionTable {
|
||||
|
||||
public static final int DEFAULT_BASE_ADDRESS = 0x200;
|
||||
|
||||
/* The Bus */
|
||||
private Bus bus;
|
||||
|
||||
@ -75,6 +77,12 @@ public class Cpu implements InstructionTable {
|
||||
overflowFlag = false;
|
||||
}
|
||||
|
||||
public void step(int num) {
|
||||
for (int i = 0; i < num; i++) {
|
||||
step();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an individual machine cycle.
|
||||
*/
|
||||
@ -123,8 +131,7 @@ public class Cpu implements InstructionTable {
|
||||
break;
|
||||
case 0x09: // ORA - Immediate
|
||||
a |= operands[0];
|
||||
setZeroFlag(a);
|
||||
setNegativeFlag(a);
|
||||
setArithmeticFlags(a);
|
||||
break;
|
||||
case 0x0a: // n/a
|
||||
break;
|
||||
@ -192,8 +199,7 @@ public class Cpu implements InstructionTable {
|
||||
break;
|
||||
case 0x29: // n/a
|
||||
a &= operands[0];
|
||||
setZeroFlag(a);
|
||||
setNegativeFlag(a);
|
||||
setArithmeticFlags(a);
|
||||
break;
|
||||
case 0x2a: // n/a
|
||||
break;
|
||||
@ -261,8 +267,7 @@ public class Cpu implements InstructionTable {
|
||||
break;
|
||||
case 0x49: // EOR - Immediate
|
||||
a ^= operands[0];
|
||||
setZeroFlag(a);
|
||||
setNegativeFlag(a);
|
||||
setArithmeticFlags(a);
|
||||
break;
|
||||
case 0x4a: // n/a
|
||||
break;
|
||||
@ -330,19 +335,8 @@ public class Cpu implements InstructionTable {
|
||||
case 0x68: // n/a
|
||||
break;
|
||||
case 0x69: // ADC - Immediate Mode
|
||||
boolean sign = (a < 0x80);
|
||||
a += (operands[0] + (carryFlag ? 1 : 0));
|
||||
// Result overflowed a byte
|
||||
if (a > 0xff) {
|
||||
a &= 0xff;
|
||||
setCarryFlag(true);
|
||||
}
|
||||
// Sign changed
|
||||
if (sign != a < 0x80) {
|
||||
setOverflowFlag(true);
|
||||
}
|
||||
setZeroFlag(a);
|
||||
setNegativeFlag(a);
|
||||
a = adc(a, operands[0]);
|
||||
setArithmeticFlags(a);
|
||||
break;
|
||||
case 0x6a: // n/a
|
||||
break;
|
||||
@ -458,15 +452,13 @@ public class Cpu implements InstructionTable {
|
||||
|
||||
case 0xa0: // LDY - Immediate
|
||||
y = operands[0];
|
||||
setZeroFlag(y);
|
||||
setNegativeFlag(y);
|
||||
setArithmeticFlags(y);
|
||||
break;
|
||||
case 0xa1: // n/a
|
||||
break;
|
||||
case 0xa2: // LDX - Immediate
|
||||
x = operands[0];
|
||||
setZeroFlag(x);
|
||||
setNegativeFlag(x);
|
||||
setArithmeticFlags(x);
|
||||
break;
|
||||
case 0xa3: // n/a
|
||||
break;
|
||||
@ -482,8 +474,7 @@ public class Cpu implements InstructionTable {
|
||||
break;
|
||||
case 0xa9: // LDA - Immediate
|
||||
a = operands[0];
|
||||
setZeroFlag(a);
|
||||
setNegativeFlag(a);
|
||||
setArithmeticFlags(a);
|
||||
break;
|
||||
case 0xaa: // n/a
|
||||
break;
|
||||
@ -665,6 +656,33 @@ public class Cpu implements InstructionTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add with Carry, used by all addressing mode implementations of ADC.
|
||||
* As a side effect, this will set the
|
||||
*
|
||||
* @param acc The current value of the accumulator
|
||||
* @param operand The operand
|
||||
* @return
|
||||
*/
|
||||
public int adc(int acc, int operand) {
|
||||
int result = operand + a + (carryFlag ? 1 : 0);
|
||||
int carry = (operand & 0x7f) + (a & 0x7f) + (carryFlag ? 1 : 0);
|
||||
if (result > 0xff) { setCarryFlag(true); }
|
||||
result = result & 0xff;
|
||||
setOverflowFlag(carryFlag ^ ((carry & 0x80) != 0));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Negative and Zero flags based on the current value of the
|
||||
* register operand.
|
||||
*
|
||||
* @param reg The register.
|
||||
*/
|
||||
public void setArithmeticFlags(int reg) {
|
||||
zeroFlag = (reg == 0);
|
||||
negativeFlag = (reg & 0x80) != 0;
|
||||
}
|
||||
/**
|
||||
* @return the negative flag
|
||||
*/
|
||||
@ -676,7 +694,6 @@ public class Cpu implements InstructionTable {
|
||||
* @param register the register value to test for negativity
|
||||
*/
|
||||
public void setNegativeFlag(int register) {
|
||||
this.negativeFlag = (((register>>>7)&0xff) == 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -707,13 +724,6 @@ public class Cpu implements InstructionTable {
|
||||
return zeroFlag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param register the register to test for zero
|
||||
*/
|
||||
public void setZeroFlag(int register) {
|
||||
this.zeroFlag = (register == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param zeroFlag the zero flag to set
|
||||
*/
|
||||
|
@ -55,7 +55,7 @@ public class Simulator {
|
||||
|
||||
int i = 0;
|
||||
for (int d : program) {
|
||||
bus.write(address + i, program[i++]);
|
||||
bus.write(address + i++, d);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,13 @@ package com.loomcom.symon;
|
||||
|
||||
import com.loomcom.symon.devices.Memory;
|
||||
import com.loomcom.symon.exceptions.MemoryRangeException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import junit.framework.*;
|
||||
|
||||
public class CpuImmediateModeTests extends TestCase {
|
||||
|
||||
private Cpu cpu;
|
||||
private Bus bus;
|
||||
private Memory mem;
|
||||
protected Cpu cpu;
|
||||
protected Bus bus;
|
||||
protected Memory mem;
|
||||
|
||||
public void setUp() throws MemoryRangeException {
|
||||
this.cpu = new Cpu();
|
||||
@ -18,9 +17,9 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
bus.addCpu(cpu);
|
||||
bus.addDevice(mem);
|
||||
|
||||
// All test programs start at 0x0200;
|
||||
bus.write(0xfffc, 0x00);
|
||||
bus.write(0xfffd, 0x02);
|
||||
// Load the reset vector.
|
||||
bus.write(0xfffc, Cpu.DEFAULT_BASE_ADDRESS & 0x00ff);
|
||||
bus.write(0xfffd, (Cpu.DEFAULT_BASE_ADDRESS & 0xff00)>>>8);
|
||||
|
||||
cpu.reset();
|
||||
}
|
||||
@ -28,61 +27,52 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
/* ORA Immediate Mode Tests - 0x09 */
|
||||
|
||||
public void test_ORA_SetsAccumulator() {
|
||||
bus.write(0x0200, 0x09); // ORA #$00
|
||||
bus.write(0x0201, 0x00);
|
||||
bus.loadProgram(0x09, 0x00, // ORA #$00
|
||||
0x09, 0x11, // ORA #$11
|
||||
0x09, 0x22, // ORA #$22
|
||||
0x09, 0x44, // ORA #$44
|
||||
0x09, 0x88); // ORA #$88
|
||||
cpu.step();
|
||||
// 0x00 | 0x00 == 0x00
|
||||
assertEquals(0x00, cpu.getAccumulator());
|
||||
|
||||
bus.write(0x0202, 0x09); // ORA #$11
|
||||
bus.write(0x0203, 0x11);
|
||||
cpu.step();
|
||||
// 0x00 | 0x11 == 0x11
|
||||
assertEquals(0x11, cpu.getAccumulator());
|
||||
|
||||
bus.write(0x0204, 0x09); // ORA #$22
|
||||
bus.write(0x0205, 0x22);
|
||||
cpu.step();
|
||||
// 0x11 | 0x22 == 0x33
|
||||
assertEquals(0x33, cpu.getAccumulator());
|
||||
|
||||
bus.write(0x0206, 0x09); // ORA #$44
|
||||
bus.write(0x0207, 0x44);
|
||||
cpu.step();
|
||||
// 0x33 | 0x44 == 0x77
|
||||
assertEquals(0x77, cpu.getAccumulator());
|
||||
|
||||
bus.write(0x0208, 0x09); // ORA #$88
|
||||
bus.write(0x0209, 0x88);
|
||||
cpu.step();
|
||||
// 0x77 | 0x88 == 0xFF
|
||||
assertEquals(0xff, cpu.getAccumulator());
|
||||
}
|
||||
|
||||
public void test_ORA_SetsZeroFlagIfResultIsZero() {
|
||||
bus.write(0x0200, 0x09); // ORA #$00
|
||||
bus.write(0x0201, 0x00);
|
||||
bus.loadProgram(0x09, 0x00); // ORA #$00
|
||||
cpu.step();
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
}
|
||||
|
||||
public void test_ORA_DoesNotSetZeroFlagIfResultNotZero() {
|
||||
bus.write(0x0200, 0x09); // ORA #$01
|
||||
bus.write(0x0201, 0x01);
|
||||
bus.loadProgram(0x09, 0x01); // ORA #$01
|
||||
cpu.step();
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
}
|
||||
|
||||
public void test_ORA_SetsNegativeFlagIfResultIsNegative() {
|
||||
bus.write(0x0200, 0x09); // ORA #$80
|
||||
bus.write(0x0201, 0x80);
|
||||
bus.loadProgram(0x09, 0x80); // ORA #$80
|
||||
cpu.step();
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
public void test_ORA_DoesNotSetNegativeFlagIfResultNotNegative() {
|
||||
bus.write(0x0200, 0x09); // ORA #$7F
|
||||
bus.write(0x0201, 0x7f);
|
||||
bus.loadProgram(0x09, 0x7f); // ORA #$7F
|
||||
cpu.step();
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
@ -138,58 +128,57 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
}
|
||||
|
||||
public void test_AND_DoesNotSetZeroFlagIfResultNotZero() {
|
||||
bus.write(0x0200, 0xa9); // LDA #$88
|
||||
bus.write(0x0201, 0x88);
|
||||
cpu.step();
|
||||
bus.write(0x0202, 0x29); // AND #$f1
|
||||
bus.write(0x0203, 0xf1);
|
||||
cpu.step();
|
||||
bus.loadProgram(0xa9, 0x88, // LDA #$88
|
||||
0x29, 0xf1); // AND #$F1
|
||||
cpu.step(2);
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
}
|
||||
|
||||
public void test_AND_SetsNegativeFlagIfResultIsNegative() {
|
||||
bus.write(0x0200, 0xa9); // LDA #$88
|
||||
bus.write(0x0201, 0x88);
|
||||
cpu.step();
|
||||
bus.write(0x0202, 0x29); // AND #$F0
|
||||
bus.write(0x0203, 0xf0);
|
||||
cpu.step();
|
||||
bus.loadProgram(0xa9, 0x88, // LDA #$88
|
||||
0x29, 0xf0); // AND #$F0
|
||||
cpu.step(2);
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
public void test_AND_DoesNotSetNegativeFlagIfResultNotNegative() {
|
||||
bus.write(0x0200, 0xa9); // LDA #$88
|
||||
bus.write(0x0201, 0x88);
|
||||
cpu.step();
|
||||
bus.write(0x0202, 0x29); // AND #$0F
|
||||
bus.write(0x0203, 0x0f);
|
||||
cpu.step();
|
||||
bus.loadProgram(0xa9, 0x88, // LDA #$88
|
||||
0x29, 0x0f); // AND #$0F
|
||||
cpu.step(2);
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
/* EOR Immediate Mode Tests - 0x49 */
|
||||
|
||||
public void test_EOR_SetsAccumulator() {
|
||||
bus.write(0x0200, 0xa9); // LDA #$88
|
||||
bus.write(0x0201, 0x88);
|
||||
bus.loadProgram(0xa9, 0x88, // LDA #$88
|
||||
0x49, 0x00, // EOR #$00
|
||||
0x49, 0xff, // EOR #$ff
|
||||
0x49, 0x33); // EOR #$33
|
||||
cpu.step();
|
||||
|
||||
bus.write(0x0202, 0x49); // EOR #$00
|
||||
bus.write(0x0203, 0x00);
|
||||
cpu.step();
|
||||
assertEquals(0x88, cpu.getAccumulator());
|
||||
|
||||
bus.write(0x0204, 0x49); // EOR #$ff
|
||||
bus.write(0x0205, 0xff);
|
||||
cpu.step();
|
||||
assertEquals(0x77, cpu.getAccumulator());
|
||||
|
||||
bus.write(0x0206, 0x49); // EOR #$33
|
||||
bus.write(0x0207, 0x33);
|
||||
cpu.step();
|
||||
assertEquals(0x44, cpu.getAccumulator());
|
||||
}
|
||||
|
||||
public void test_EOR_SetsArithmeticFlags() {
|
||||
bus.loadProgram(0xa9, 0x77, // LDA #$77
|
||||
0x49, 0x77, // EOR #$77
|
||||
0x49, 0xff); // EOR #$ff
|
||||
cpu.step();
|
||||
cpu.step();
|
||||
assertEquals(0x00, cpu.getAccumulator());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
cpu.step();
|
||||
assertEquals(0xff, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
}
|
||||
|
||||
/* ADC Immediate Mode Tests - 0x69 */
|
||||
|
||||
public void test_ADC_SetsAccumulator() {
|
||||
@ -222,7 +211,7 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
assertEquals(0x02, cpu.getAccumulator());
|
||||
}
|
||||
|
||||
public void test_ADC_SetsCarryIfResultOverflows() {
|
||||
public void test_ADC_SetsCarryIfResultCarries() {
|
||||
bus.write(0x200, 0xa9); // LDA #$fe
|
||||
bus.write(0x201, 0xff);
|
||||
cpu.step();
|
||||
@ -231,7 +220,9 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
cpu.step();
|
||||
// $ff + $02 = $101 = [c] + $01
|
||||
assertEquals(0x01, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
}
|
||||
|
||||
public void test_ADC_SetsOverflowIfResultChangesSign() {
|
||||
@ -242,7 +233,51 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
bus.write(0x203, 0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x80, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
|
||||
cpu.reset();
|
||||
|
||||
bus.write(0x200, 0xa9); // LDA #$80
|
||||
bus.write(0x201, 0x80);
|
||||
cpu.step();
|
||||
bus.write(0x202, 0x69); // ADC #$ff
|
||||
bus.write(0x203, 0xff);
|
||||
cpu.step();
|
||||
assertEquals(0x7f, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
}
|
||||
|
||||
public void test_ADC_DoesNotSetOverflowIfNotNeeded() {
|
||||
bus.write(0x200, 0xa9); // LDA #$ff
|
||||
bus.write(0x201, 0xff);
|
||||
cpu.step();
|
||||
bus.write(0x202, 0x69); // ADC #$01
|
||||
bus.write(0x203, 0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x00, cpu.getAccumulator());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
|
||||
cpu.reset();
|
||||
|
||||
bus.write(0x200, 0xa9); // LDA #$01
|
||||
bus.write(0x201, 0x01);
|
||||
cpu.step();
|
||||
bus.write(0x202, 0x69); // ADC #$01
|
||||
bus.write(0x203, 0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x02, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
}
|
||||
|
||||
public void test_ADC_SetsNegativeFlagIfResultIsNegative() {
|
||||
@ -253,7 +288,10 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
bus.write(0x203, 0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x80, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertTrue(cpu.getNegativeFlag());
|
||||
assertTrue(cpu.getOverflowFlag());
|
||||
}
|
||||
|
||||
public void test_ADC_SetsZeroFlagIfResultIsZero() {
|
||||
@ -265,6 +303,9 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
cpu.step();
|
||||
assertEquals(0x00, cpu.getAccumulator());
|
||||
assertTrue(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
}
|
||||
|
||||
public void test_ADC_DoesNotSetNegativeFlagIfResultNotNegative() {
|
||||
@ -275,7 +316,10 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
bus.write(0x203, 0x01);
|
||||
cpu.step();
|
||||
assertEquals(0x7f, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertFalse(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
}
|
||||
|
||||
public void test_ADC_DoesNotSetZeroFlagIfResultNotZero() {
|
||||
@ -287,6 +331,9 @@ public class CpuImmediateModeTests extends TestCase {
|
||||
cpu.step();
|
||||
assertEquals(0x2, cpu.getAccumulator());
|
||||
assertFalse(cpu.getZeroFlag());
|
||||
assertTrue(cpu.getCarryFlag());
|
||||
assertFalse(cpu.getNegativeFlag());
|
||||
assertFalse(cpu.getOverflowFlag());
|
||||
}
|
||||
|
||||
/* LDY Immediate Mode Tests - 0xa0 */
|
||||
|
Loading…
Reference in New Issue
Block a user