Added basic test framework

This commit is contained in:
edmccard 2012-03-23 12:14:15 -04:00 committed by Ed McCardell
parent 4b27ddbe75
commit 412b1affad
3 changed files with 503 additions and 0 deletions

255
test/base.d Normal file
View File

@ -0,0 +1,255 @@
module test.base;
import std.conv, std.exception, std.string, std.traits;
public import d6502.nmosundoc : NmosUndoc;
public import d6502.cmos : Cmos;
template isNMOS(T)
{
enum isNMOS = __traits(hasMember, T, "_isNMOS");
}
template isCMOS(T)
{
enum isCMOS = __traits(hasMember, T, "_isCMOS");
}
template isCpu(T)
{
enum isCpu = __traits(hasMember, T, "_isCpuBase");
}
template isStrict(T)
{
enum isStrict = __traits(hasMember, T, "_isStrict");
}
template isCumulative(T)
{
enum isCumulative = __traits(hasMember, T, "_isCumulative");
}
class TestException : Exception { this(string msg) { super(msg); } }
struct Block
{
ushort base;
ubyte[] data;
}
/* Emulates zero page, stack, and 3 additional pages starting at a
* user-defined address. Accesses outside the defined address space
* raise an exception.
*/
struct TestMemory
{
ubyte[0x200] data1;
ubyte[0x300] data2;
immutable ushort data2_base;
immutable size_t data2_max;
this(Block[] blocks ...)
{
size_t last_1, last_2;
foreach (block; blocks)
{
auto base = block.base;
auto data = block.data;
if (base < 0x200)
{
enforce(base >= last_1,
format("Overlapping address %0.4x", base));
enforce(base + data.length <= 0x200,
format("Address out of bounds %0.4x", base));
last_1 = base + data.length;
data1[base..last_1] = data[];
}
else
{
if (!data2_base)
{
last_2 = data2_base = base;
data2_max = base + 0x300;
}
enforce(base >= last_2,
format("Overlapping address %0.4x", base));
enforce(base + data.length <= data2_max,
format("Address out of bounds %0.4x", base));
last_2 = base + data.length;
data2[base-data2_base..last_2-data2_base] = data[];
}
}
enforce(data2_base, "Missing memory > 0x0200");
}
ubyte read(ushort addr)
{
if (addr < 0x0200)
return data1[addr];
else if (addr >= data2_base && addr < data2_max)
return data2[addr - data2_base];
else
throw new TestException(format("read %0.4x", addr));
}
void write(ushort addr, ubyte val)
{
if (addr < 0x0200)
data1[addr] = val;
else if (addr >= data2_base && addr < data2_max)
data2[addr - data2_base] = val;
else
throw new TestException(format("write %0.4x", addr));
}
ubyte opIndex(size_t i1)
{
auto addr = cast(ushort)i1;
enforce(addr < 0x0200 || (addr >= data2_base && addr < data2_max),
"Read out of bounds");
return read(addr);
}
}
T makeCpu(T)(ref TestMemory mem)
if (isCpu!T)
{
static if (isCumulative!T) void tick(int cycles) {}
else void tick() {}
auto cpu = new T();
cpu.memoryRead = &mem.read;
cpu.memoryWrite = &mem.write;
cpu.tick = &tick;
return cpu;
}
struct Ref(T)
if (isPointer!T)
{
private const(T) data;
this(T ptr) { data = ptr; }
auto deref() { return *data; }
alias deref this;
string toString () const { return format("%s", *data); }
}
auto constRef(T)(T ptr)
if (isPointer!T)
{
return Ref!(const(T))(ptr);
}
auto recordCycles(T)(T cpu)
if (isCpu!T)
{
auto cycles = new int;
auto wrappedTick = cpu.tick;
static if (isCumulative!T)
{
void tick(int cyc)
{
(*cycles) += cyc;
wrappedTick(cyc);
}
}
else
{
void tick()
{
(*cycles)++;
wrappedTick();
}
}
cpu.tick = &tick;
return constRef(cycles);
}
enum Action : ushort { NONE, READ, WRITE }
struct Bus
{
Action action;
ushort addr;
string toString() const
{
return format("Bus(%s, %0.4X)", to!string(action), addr);
}
}
/*
*
*/
const(Bus[]) recordBus(T)(T cpu, int actions = 8)
if (isCpu!T)
{
auto record = new Bus[actions];
int c;
enforce(cpu.memoryRead !is null && cpu.memoryWrite !is null);
auto wrappedRead = cpu.memoryRead;
auto wrappedWrite = cpu.memoryWrite;
ubyte read(ushort addr)
{
if (c == actions)
throw new TestException(
format("cannot record more than %d actions", actions));
record[c++] = Bus(Action.READ, addr);
return wrappedRead(addr);
}
void write(ushort addr, ubyte val)
{
if (c == actions)
throw new TestException(
format("cannot record more than %d actions", actions));
record[c++] = Bus(Action.WRITE, addr);
wrappedWrite(addr, val);
}
cpu.memoryRead = &read;
cpu.memoryWrite = &write;
return record;
}
class StopException : Exception { this(string msg) { super(msg); } }
void runUntilBRK(T)(T cpu)
if (isCpu!T)
{
enforce(cpu.memoryRead !is null);
auto wrappedRead = cpu.memoryRead;
ubyte read(ushort addr)
{
if (addr == 0xFFFE) throw new StopException("BRK");
return wrappedRead(addr);
}
cpu.memoryRead = &read;
try { cpu.run(true); } catch (StopException e) {}
}
void runOneOpcode(T)(T cpu)
if (isCpu!T)
{
cpu.run(false);
}

2
test/cmdfile Normal file
View File

@ -0,0 +1,2 @@
-unittest -I.. -I../src -inline -release -O

246
test/test_decimal.d Normal file
View File

@ -0,0 +1,246 @@
module test.test_decimal;
import std.stdio;
import test.base;
void testDecimalMode(T)()
if (isCpu!T)
{
auto mem = TestMemory(Block(0x8000, cast(ubyte[])
/* 8000 JMP TEST */ x"4C 14 80"
/* 8003 ERROR DB 0 */ x"00"
/* 8004 N1 DB 0 */ x"00"
/* 8005 N2 DB 0 */ x"00"
/* 8006 N1H DB 0 */ x"00"
/* 8007 N1L DB 0 */ x"00"
/* 8008 N2H DW 0 */ x"0000"
/* 800A N2L DB 0 */ x"00"
/* 800B DA DB 0 */ x"00"
/* 800C DNVZC DB 0 */ x"00"
/* 800D HA DB 0 */ x"00"
/* 800E HNVZC DB 0 */ x"00"
/* 800F AR DB 0 */ x"00"
/* 8010 NF DB 0 */ x"00"
/* 8011 VF DB 0 */ x"00"
/* 8012 ZF DB 0 */ x"00"
/* 8013 CF DB 0 */ x"00"
/* 8014 TEST LDY #1 */ x"A0 01"
/* 8016 STY ERROR */ x"8C 03 80"
/* 8019 LDA #0 */ x"A9 00"
/* 801B STA N1 */ x"8D 04 80"
/* 801E STA N2 */ x"8D 05 80"
/* 8021 LOOP1 LDA N2 */ x"AD 05 80"
/* 8024 AND #$0F */ x"29 0F"
/* 8026 STA N2L */ x"8D 0A 80"
/* 8029 LDA N2 */ x"AD 05 80"
/* 802C AND #$F0 */ x"29 F0"
/* 802E STA N2H */ x"8D 08 80"
/* 8031 ORA #$0F */ x"09 0F"
/* 8033 STA N2H+1 */ x"8D 09 80"
/* 8036 LOOP2 LDA N1 */ x"AD 04 80"
/* 8039 AND #$0F */ x"29 0F"
/* 803B STA N1L */ x"8D 07 80"
/* 803E LDA N1 */ x"AD 04 80"
/* 8041 AND #$F0 */ x"29 F0"
/* 8043 STA N1H */ x"8D 06 80"
/* 8045 JSR ADD */ x"20 6F 80"
/* 8049 JSR A6502 */ x"20 58 81"
/* 804C JSR COMPARE */ x"20 29 81"
/* 804F BNE DONE */ x"D0 1D"
/* 8051 JSR SUB */ x"20 C2 80"
/* 8054 JSR S6502 */ x"20 65 81"
/* 8057 JSR COMPARE */ x"20 29 81"
/* 805A BNE DONE */ x"D0 12"
/* 805C INC N1 */ x"EE 04 80"
/* 805F BNE LOOP2 */ x"D0 D5"
/* 8061 INC N2 */ x"EE 05 80"
/* 8064 BNE LOOP1 */ x"D0 BB"
/* 8066 DEY */ x"88"
/* 8067 BPL LOOP1 */ x"10 B8"
/* 8069 LDA #0 */ x"A9 00"
/* 806B STA ERROR */ x"8D 03 80"
/* 806E DONE BRK */ x"00"
/* 806F ADD SED */ x"F8"
/* 8070 CPY #1 */ x"C0 01"
/* 8072 LDA N1 */ x"AD 04 80"
/* 8075 ADC N2 */ x"6D 05 80"
/* 8078 STA DA */ x"8D 0B 80"
/* 807B PHP */ x"08"
/* 807C PLA */ x"68"
/* 807D STA DNVZC */ x"8D 0C 80"
/* 8080 CLD */ x"D8"
/* 8081 CPY #1 */ x"C0 01"
/* 8083 LDA N1 */ x"AD 04 80"
/* 8086 ADC N2 */ x"6D 05 80"
/* 8089 STA HA */ x"8D 0D 80"
/* 808C PHP */ x"08"
/* 808D PLA */ x"68"
/* 808E STA HNVZC */ x"8D 0E 80"
/* 8091 CPY #1 */ x"C0 01"
/* 8093 LDA N1L */ x"AD 07 80"
/* 8096 ADC N2L */ x"6D 0A 80"
/* 8099 CMP #$0A */ x"C9 0A"
/* 809B LDX #0 */ x"A2 00"
/* 809D BCC A1 */ x"90 06"
/* 809F INX */ x"E8"
/* 80A0 ADC #5 */ x"69 05"
/* 80A2 AND #$0F */ x"29 0F"
/* 80A4 SEC */ x"38"
/* 80A5 A1 ORA N1H */ x"0D 06 80"
/* 80A8 ADC N2H,X */ x"7D 08 80"
/* 80AB PHP */ x"08"
/* 80AC BCS A2 */ x"B0 04"
/* 80AE CMP #$A0 */ x"C9 A0"
/* 80B0 BCC A3 */ x"90 03"
/* 80B2 A2 ADC #$5F */ x"69 5F"
/* 80B4 SEC */ x"38"
/* 80B5 A3 STA AR */ x"8D 0F 80"
/* 80B8 PHP */ x"08"
/* 80B9 PLA */ x"68"
/* 80BA STA CF */ x"8D 13 80"
/* 80BD PLA */ x"68"
/* 80BE STA VF */ x"8D 11 80"
/* 80C1 RTS */ x"60"
/* 80C2 SUB SED */ x"F8"
/* 80C3 CPY #1 */ x"C0 01"
/* 80C5 LDA N1 */ x"AD 04 80"
/* 80C8 SBC N2 */ x"ED 05 80"
/* 80CB STA DA */ x"8D 0B 80"
/* 80CE PHP */ x"08"
/* 80CF PLA */ x"68"
/* 80D0 STA DNVZC */ x"8D 0C 80"
/* 80D3 CLD */ x"D8"
/* 80D4 CPY #1 */ x"C0 01"
/* 80D6 LDA N1 */ x"AD 04 80"
/* 80D9 SBC N2 */ x"ED 05 80"
/* 80DC STA HA */ x"8D 0D 80"
/* 80DF PHP */ x"08"
/* 80E0 PLA */ x"68"
/* 80E1 STA HNVZC */ x"8D 0E 80"
/* 80E4 RTS */ x"60"
/* 80E5 SUB1 CPY #1 */ x"C0 01"
/* 80E7 LDA N1L */ x"AD 07 80"
/* 80EA SBC N2L */ x"ED 0A 80"
/* 80ED LDX #0 */ x"A2 00"
/* 80EF BCS S11 */ x"B0 06"
/* 80F1 INX */ x"E8"
/* 80F2 SBC #5 */ x"E9 05"
/* 80F4 AND #$0F */ x"29 0F"
/* 80F6 CLC */ x"18"
/* 80F7 S11 ORA N1H */ x"0D 06 80"
/* 80FA SBC N2H,X */ x"FD 08 80"
/* 80FD BCS S12 */ x"B0 02"
/* 80FF SBC #$5F */ x"E9 5F"
/* 8101 S12 STA AR */ x"8D 0F 80"
/* 8104 RTS */ x"60"
/* 8105 SUB2 CPY #1 */ x"C0 01"
/* 8107 LDA N1L */ x"AD 07 80"
/* 810A SBC N2L */ x"ED 0A 80"
/* 810D LDX #0 */ x"A2 00"
/* 810F BCS S21 */ x"B0 04"
/* 8111 INX */ x"E8"
/* 8112 AND #$0F */ x"29 0F"
/* 8114 CLC */ x"18"
/* 8115 S21 ORA N1H */ x"0D 06 80"
/* 8118 SBC N2H,X */ x"FD 08 80"
/* 811B BCS S22 */ x"B0 02"
/* 811D SBC #$5F */ x"E9 5F"
/* 811F S22 CPX #0 */ x"E0 00"
/* 8121 BEQ S23 */ x"F0 02"
/* 8123 SBC #6 */ x"E9 06"
/* 8125 S23 STA AR */ x"8D 0F 80"
/* 8128 RTS */ x"60"
/* 8129 COMPARE LDA DA */ x"AD 0B 80"
/* 812C CMP AR */ x"CD 0F 80"
/* 812F BNE C1 */ x"D0 26"
/* 8131 LDA DNVZC */ x"AD 0C 80"
/* 8134 EOR NF */ x"4D 10 80"
/* 8137 AND #$80 */ x"29 80"
/* 8139 BNE C1 */ x"D0 1C"
/* 813B LDA DNVZC */ x"AD 0C 80"
/* 813E EOR VF */ x"4D 11 80"
/* 8141 AND #$40 */ x"29 40"
/* 8143 BNE C1 */ x"D0 12"
/* 8145 LDA DNVZC */ x"AD 0C 80"
/* 8148 EOR ZF */ x"4D 12 80"
/* 814B AND #2 */ x"29 02"
/* 814D BNE C1 */ x"D0 08"
/* 814F LDA DNVZC */ x"AD 0C 80"
/* 8152 EOR CF */ x"4D 13 80"
/* 8155 AND #1 */ x"29 01"
/* 8157 C1 RTS */ x"60"
/* 8158 A6502 LDA VF */ x"AD 11 80"
/* 815B STA NF */ x"8D 10 80"
/* 815E LDA HNVZC */ x"AD 0E 80"
/* 8161 STA ZF */ x"8D 12 80"
/* 8164 RTS */ x"60"
/* 8165 S6502 JSR SUB1 */ x"20 E5 80"
/* 8168 LDA HNVZC */ x"AD 0E 80"
/* 816B STA NF */ x"8D 10 80"
/* 816E STA VF */ x"8D 11 80"
/* 8171 STA ZF */ x"8D 12 80"
/* 8174 STA CF */ x"8D 13 80"
/* 8177 RTS */ x"60"
/* 8178 A65C02 LDA AR */ x"AD 0F 80"
/* 817B PHP */ x"08"
/* 817C PLA */ x"68"
/* 817D STA NF */ x"8D 10 80"
/* 8180 STA ZF */ x"8D 12 80"
/* 8183 RTS */ x"60"
/* 8184 S65C02 JSR SUB2 */ x"20 05 81"
/* 8187 LDA AR */ x"AD 0F 80"
/* 818A PHP */ x"08"
/* 818B PLA */ x"68"
/* 818C STA NF */ x"8D 10 80"
/* 818F STA ZF */ x"8D 12 80"
/* 8192 LDA HNVZC */ x"AD 0E 80"
/* 8195 STA VF */ x"8D 11 80"
/* 8198 STA CF */ x"8D 13 80"
/* 819B RTS */ x"60"
));
// Patch the JSRs at $804A/$8055
static if (isNMOS!T)
{
mem.write(0x804A, 0x58);
mem.write(0x8055, 0x65);
}
else
{
mem.write(0x804A, 0x78);
mem.write(0x8055, 0x84);
}
auto cpu = makeCpu!T(mem);
cpu.programCounter = 0x8000;
runUntilBRK(cpu);
if (mem[0x8003])
throw new TestException("failed decimal mode " ~ T.stringof);
}
unittest
{
writeln("Testing decimal mode, NMOS(Strict.no, Cumulative.no)");
testDecimalMode!(NmosUndoc!(false, false))();
writeln("Testing decimal mode, CMOS(Strict.no, Cumulative.no)");
testDecimalMode!(Cmos!(false, false))();
/+
writeln("Testing decimal mode, NMOS(Strict.no, Cumulative.yes)");
testDecimalMode!(NmosUndoc!(false, true))();
writeln("Testing decimal mode, CMOS(Strict.no, Cumulative.yes)");
testDecimalMode!(Cmos!(false, true))();
writeln("Testing decimal mode, NMOS(Strict.yes, Cumulative.no)");
testDecimalMode!(NmosUndoc!(true, false))();
writeln("Testing decimal mode, CMOS(Strict.yes, Cumulative.no)");
testDecimalMode!(Cmos!(true, false))();
writeln("Testing decimal mode, NMOS(Strict.yes, Cumulative.yes)");
testDecimalMode!(NmosUndoc!(true, true))();
writeln("Testing decimal mode, CMOS(Strict.yes, Cumulative.yes)");
testDecimalMode!(Cmos!(true, true))();
+/
}