twoapple-reboot/test/d6502/base.d

3659 lines
95 KiB
D
Raw Permalink Normal View History

2012-04-14 15:26:27 +00:00
module test.d6502.base;
2012-03-23 16:14:15 +00:00
2012-04-12 22:42:55 +00:00
import std.algorithm, std.array, std.conv, std.exception, std.getopt,
std.stdio, std.string;
2012-03-23 16:14:15 +00:00
2012-04-14 15:26:27 +00:00
import test.d6502.cpu, test.d6502.opcodes;
2012-04-12 22:42:55 +00:00
import cpu.data_d6502;
2012-03-23 16:14:15 +00:00
version(Strict)
enum strict = true;
else
enum strict = false;
version(Cumulative)
enum cumulative = true;
else
enum cumulative = false;
2012-04-14 10:14:08 +00:00
/*
* A test is a combination of setups, an expectation, a runner, and a
* reporter.
*/
/*
* A setup function for a given opcode puts cpu, data, info, and msg
* into the appropriate state and then calls the next function (see
* testCallNext) with the modified values. To setup multiple
* scenarios, just call the next function multiple times.
*
* Values for cpu registers are set up using the setXXX(cpu, val)
* functions; see testCallNext and OpInfo for descriptions of other
* types of information that may need to be set up.
*
* Example:
*
* // prepare the accumulator with a value different from expected
* setA(cpu, ~0x10);
* // prepare memory with LDA #$10 at address $1000
* setPC(cpu, 0x1000);
* callNext("LDA immediate, positive", [Block(0x1000, [0xA9, 0x10])]);
*/
alias void delegate(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
testsetup;
/*
* A mixin that simplifies calling the next setup function.
*
* newMsg will be appended to the current msg and passed to the next
* function.
*
* To place values in memory, pass an array of Blocks as the second
* parameter. It will be appended to the current data.
*/
template testCallNext()
{
void callNext(string newMsg = "", Block[] newData = [])
{
if (*next !is null)
next.run(opcode, cpu, data ~ newData, info, msg ~ newMsg);
}
}
/*
* A block of memory with a given base address.
*
* For example, `Block(0x1000, [0xA9, 0x10])`
*/
struct Block
{
const ushort base;
const(ubyte[]) data;
string toString() const
{
return format("Block(%0.4X, [%s])", base, formatMemory(data));
}
}
// Information about expected opcode execution.
struct OpInfo
{
// The effective address, if any.
ushort addr;
// The data to be read or written, if any.
ubyte data;
// The length of the opcode + operands.
int len;
}
class TestSetup
{
testsetup setup;
TestSetup next;
auto static opCall(testsetup d)
{
auto obj = new TestSetup();
obj.setup = d;
return obj;
}
void run(ubyte opcode, CpuInfo cpu = CpuInfo(), Block[] data = [],
OpInfo info = OpInfo(), string msg = "")
{
setup(opcode, cpu, data, info, msg, &next);
}
}
TestSetup connect(TestSetup first, TestSetup[] rest...)
{
if (!(rest.empty))
{
auto x = first;
while (x.next !is null) x = x.next;
x.next = connect(rest[0], rest[1..$]);
}
return first;
}
2012-03-25 08:25:41 +00:00
/*
* Emulates zero page, stack, and 3 additional pages of "main memory"
* starting at a user-defined address. Accesses outside the defined
* address space raise an exception.
*
* The contents are initialized to 0xFF. Individual locations can be
* read and written using array index syntax.
2012-03-23 16:14:15 +00:00
*/
struct TestMemory
{
2012-03-25 08:25:41 +00:00
private:
ubyte[0x200] data1 = 0xFF;
ubyte[0x300] data2 = 0xFF;
ushort data2_base;
size_t data2_max;
2012-03-23 16:14:15 +00:00
2012-03-25 08:25:41 +00:00
public:
/*
* Constructs a TestMemory with data filled in from an array of
* Blocks.
*
* The blocks do not need to be contiguous, or ordered by their
2012-03-31 19:44:23 +00:00
* base address, but note that the base of the 3-page "main
* memory" will be the start of the page that contains the first
* block with a base address greater than 0x01FF (there must be at
* least one such block).
2012-03-25 08:25:41 +00:00
*/
this(const(Block[]) blocks ...)
2012-03-23 16:14:15 +00:00
{
foreach (block; blocks)
{
auto base = block.base;
auto data = block.data;
if (base < 0x200)
{
enforce(base + data.length <= 0x200,
format("Address out of bounds %0.4x", base));
2012-03-25 08:25:41 +00:00
data1[base..base + data.length] = data[];
2012-03-23 16:14:15 +00:00
}
else
{
if (!data2_base)
{
2012-03-25 08:25:41 +00:00
if (base > 0xFD00)
data2_base = 0xFD00;
else
2012-03-31 19:44:23 +00:00
data2_base = base & 0xFF00;
data2_max = data2_base + 0x300;
2012-03-23 16:14:15 +00:00
}
enforce(base + data.length <= data2_max,
format("Address out of bounds %0.4x", base));
2012-03-25 08:25:41 +00:00
auto last = base + data.length;
data2[base-data2_base..last-data2_base] = data[];
2012-03-23 16:14:15 +00:00
}
}
enforce(data2_base, "Missing memory > 0x0200");
}
ubyte read(ushort addr) const
2012-03-23 16:14:15 +00:00
{
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) const
2012-03-23 16:14:15 +00:00
{
auto addr = cast(ushort)i1;
enforce(addr < 0x0200 || (addr >= data2_base && addr < data2_max),
"Read out of bounds");
return read(addr);
}
ubyte opIndexAssign(ubyte val, size_t i1)
{
auto addr = cast(ushort)i1;
enforce(addr < 0x0200 || (addr >= data2_base && addr < data2_max),
"Write out of bounds");
write(addr, val);
return val;
}
2012-03-23 16:14:15 +00:00
}
/*
* Formats data as a string of 2-digit hex bytes, separated by spaces.
*
* If data is longer than max, the string will end with an indication
* of the number of extra bytes.
*/
2012-04-06 22:23:09 +00:00
string formatMemory(const(ubyte[]) data, size_t max = 3)
{
if (max > data.length) max = data.length;
auto hexbytes = map!(`format("%0.2X", a)`)(data[0..max]);
auto ret = join(array(hexbytes), " ");
if (data.length > max)
ret ~= format(" (%d more bytes)", data.length - max);
return ret;
}
// Does nothing.
auto setup_none()
2012-03-25 08:25:41 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
if (*next !is null) next.run(opcode, cpu, data, info, msg);
}
return TestSetup(&setup);
2012-03-25 08:25:41 +00:00
}
// Aborts a test.
auto setup_op_abort()
2012-03-25 08:25:41 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
}
return TestSetup(&setup);
2012-03-25 08:25:41 +00:00
}
// Prints the current message.
auto setup_debug()
2012-03-25 08:25:41 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
import std.stdio;
writeln(format("%s\n data %s\n addr %0.4X op %0.2X %s",
msg, data, info.addr, opcode, cpu));
if (*next !is null) next.run(opcode, cpu, data, info, msg);
}
return TestSetup(&setup);
2012-03-25 08:25:41 +00:00
}
// Splits with the given flag set, then cleared.
auto setup_flag(Flag f)
2012-03-25 08:25:41 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
clearFlag(cpu, f);
callNext(format("%s0 ", flagToString(f)));
setFlag(cpu, f);
callNext(format("%s1 ", flagToString(f)));
}
return TestSetup(&setup);
}
2012-03-25 08:25:41 +00:00
// Splits with all flags set, then all flags cleared.
auto setup_mask_flags()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
clearFlag(cpu, Flag.C, Flag.Z, Flag.I, Flag.D, Flag.V, Flag.N);
callNext("F0 ");
setFlag(cpu, Flag.C, Flag.Z, Flag.I, Flag.D, Flag.V, Flag.N);
callNext("F1 ");
2012-03-25 08:25:41 +00:00
}
return TestSetup(&setup);
}
2012-03-25 08:25:41 +00:00
/*
* Used after setup_mask_flags, guarantees coverage of all
* combinations of N and Z.
*/
auto setup_nz()
{
return setup_flag(Flag.N);
2012-03-25 08:25:41 +00:00
}
/*
* Can be used after setup_mask_flags to cover all combinations of
* flags.
*/
auto setup_nvdzc()
{
return connect(setup_flag(Flag.N), setup_flag(Flag.Z),
setup_flag(Flag.V), setup_flag(Flag.D),
setup_flag(Flag.C));
}
// Splits with info.data 0x00, 0x40, and 0x80.
auto setup_data()
2012-03-25 08:25:41 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
info.data = 0x00;
callNext("zero ");
info.data = 0x40;
callNext("positive ");
info.data = 0x80;
callNext("negative ");
}
return connect(TestSetup(&setup));
}
2012-03-25 08:25:41 +00:00
/*
* For register transfer opcodes.
*
* Splits on 0x00, 0x40, and 0x80 in the source register.
*/
auto setup_op_transfer(Reg source, Reg dest)
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
ubyte other = (info.data > 0x00 && info.data < 0x80) ? 0x00 : 0x01;
setX(cpu, other); setY(cpu, other); setA(cpu, other);
setReg(cpu, source, info.data);
callNext("xfer ");
2012-03-25 08:25:41 +00:00
}
return connect(setup_nz(), setup_data(), TestSetup(&setup));
}
2012-03-25 08:25:41 +00:00
// For TXS and TSX.
auto setup_op_stack_xfer()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
setSP(cpu, 0x00);
setX(cpu, 0xFF);
callNext("TXS ");
}
return connect(setup_nz(), TestSetup(&setup));
2012-03-25 08:25:41 +00:00
}
/*
* For implied address mode.
*
* Sets PC to 0x1000 and puts a (1-byte) opcode in memory.
*/
auto setup_addr_implied()
2012-03-25 08:25:41 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
setPC(cpu, 0x1000);
info.len = 1;
callNext("", [Block(0x1000, [opcode])]);
}
return TestSetup(&setup);
}
2012-03-25 08:25:41 +00:00
/*
* For immediate address mode.
*
* Sets PC to 0x1000 and info.addr to 0x1001.
*/
auto setup_addr_imm()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
setPC(cpu, 0x1000);
info.len = 2;
info.addr = 0x1001;
callNext("imm ", [Block(0x1000, [opcode])]);
}
return TestSetup(&setup);
}
/*
* For branch opcodes.
*/
auto setup_addr_branch(bool cmos)
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
if (!cmos || opcode != 0x80)
2012-03-25 08:25:41 +00:00
{
expectNoBranch(cpu, opcode);
setPC(cpu, 0x1000);
info.addr = 0x1002;
callNext("no-branch ", [Block(0x1000, [opcode, 0x10])]);
2012-03-25 08:25:41 +00:00
}
expectBranch(cpu, opcode);
2012-03-25 08:25:41 +00:00
setPC(cpu, 0x1000);
info.addr = 0x1012;
callNext("branch-fwd ", [Block(0x1000, [opcode, 0x10])]);
setPC(cpu, 0x1081);
info.addr = 0x1102;
callNext("branch-fwd-px ", [Block(0x1081, [opcode, 0x7F])]);
setPC(cpu, 0x1000);
info.addr = 0x1000;
callNext("branch-bkwd ", [Block(0x1000, [opcode, 0xFE])]);
setPC(cpu, 0x1100);
info.addr = 0x10F7;
callNext("branch-bkwd-px ", [Block(0x1000, []),
Block(0x1100, [opcode, 0xF5])]);
2012-03-25 08:25:41 +00:00
}
return TestSetup(&setup);
}
2012-03-25 08:25:41 +00:00
/*
* For zeropage adddress mode.
*
* Sets PC to 0x1000 and puts [opcode, 0x70] in memory.
*/
auto setup_addr_zpg()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
setPC(cpu, 0x1000);
info.len = 2;
info.addr = 0x0070;
callNext("zpg ", [Block(0x1000, [opcode, 0x70])]);
2012-03-25 08:25:41 +00:00
}
return TestSetup(&setup);
}
2012-03-25 08:25:41 +00:00
/*
* For absolute address mode.
*
* Sets PC to 0x1000 and puts [opcode, 0xC5, 0x10] in memory.
*/
auto setup_addr_abs()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
setPC(cpu, 0x1000);
info.len = 3;
info.addr = 0x10C5;
callNext("abs ", [Block(0x1000, [opcode, 0xC5, 0x10])]);
2012-03-25 08:25:41 +00:00
}
return TestSetup(&setup);
2012-03-25 08:25:41 +00:00
}
/*
* For zeropage,x/y address modes.
*
* Sets PC to 0x1000 and puts [opcode, 0x70] in memory.
*/
auto setup_addr_zpxy(Reg reg)
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
string name = (reg == Reg.X ? "zpx " : "zpy ");
info.len = 2;
2012-03-25 08:25:41 +00:00
setPC(cpu, 0x1000);
setX(cpu, 0x10); setY(cpu, 0x10); setA(cpu, 0x10);
setReg(cpu, reg, 0x00);
info.addr = pageWrapAdd(0x0070, 0x00);
callNext(name ~ "no-idx ", [Block(0x1000, [opcode, 0x70])]);
setX(cpu, 0x10); setY(cpu, 0x10); setA(cpu, 0x10);
setReg(cpu, reg, 0x20);
info.addr = pageWrapAdd(0x0070, 0x20);
callNext(name ~ "no-wrap ", [Block(0x1000, [opcode, 0x70])]);
setX(cpu, 0x10); setY(cpu, 0x10); setA(cpu, 0x10);
setReg(cpu, reg, 0xA0);
info.addr = pageWrapAdd(0x0070, 0xA0);
callNext(name ~ "wrap ", [Block(0x1000, [opcode, 0x70])]);
2012-03-23 16:14:15 +00:00
}
return TestSetup(&setup);
}
// For absolute,x/y address modes.
auto setup_addr_abxy(Reg reg)
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
string name = (reg == Reg.X ? "abx " : "aby ");
info.len = 3;
setPC(cpu, 0x1000);
setX(cpu, 0x10); setY(cpu, 0x10); setA(cpu, 0x10);
setReg(cpu, reg, 0x00);
info.addr = pageCrossAdd(0x10C5, 0x00);
callNext(name ~ "no-idx ", [Block(0x1000, [opcode, 0xC5, 0x10])]);
2012-03-23 16:14:15 +00:00
setX(cpu, 0x10); setY(cpu, 0x10); setA(cpu, 0x10);
setReg(cpu, reg, 0x20);
info.addr = pageCrossAdd(0x10C5, 0x20);
callNext(name ~ "no-px ", [Block(0x1000, [opcode, 0xC5, 0x10])]);
setX(cpu, 0x10); setY(cpu, 0x10); setA(cpu, 0x10);
setReg(cpu, reg, 0x50);
info.addr = pageCrossAdd(0x10C5, 0x50);
callNext(name ~ "px ", [Block(0x1000, [opcode, 0xC5, 0x10])]);
}
return TestSetup(&setup);
2012-03-23 16:14:15 +00:00
}
// For zeropage indirect,x address mode.
auto setup_addr_izx()
2012-03-25 08:25:41 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
info.addr = 0x10C5;
info.len = 2;
setPC(cpu, 0x1000);
setA(cpu, 0x01); setY(cpu, 0x01);
setX(cpu, 0x20);
callNext("izx no-wrap ", [Block(0x1000, [opcode, 0x70]),
Block(0x0090, [0xC5, 0x10])]);
setX(cpu, 0xA0);
callNext("izx wrap ", [Block(0x1000, [opcode, 0x70]),
Block(0x0010, [0xC5, 0x10])]);
setX(cpu, 0x8F);
callNext("izx px ", [Block(0x1000, [opcode, 0x70]),
Block(0x00FF, [0xC5]),
Block(0x0000, [0x10])]);
}
return TestSetup(&setup);
}
2012-03-25 08:25:41 +00:00
// For zeropage indirect,y address mode.
auto setup_addr_izy()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
info.len = 2;
2012-03-25 08:25:41 +00:00
setPC(cpu, 0x1000);
setA(cpu, 0x01); setX(cpu, 0x01);
info.addr = pageCrossAdd(0x10C5, 0x20);
setY(cpu, 0x20);
callNext("izy no-wrap ", [Block(0x1000, [opcode, 0x70]),
Block(0x0070, [0xC5, 0x10])]);
callNext("izy wrap ", [Block(0x1000, [opcode, 0xFF]),
Block(0x00FF, [0xC5]),
Block(0x0000, [0x10])]);
info.addr = pageCrossAdd(0x10C5, 0x50);
setY(cpu, 0x50);
callNext("izy px ", [Block(0x1000, [opcode, 0x70]),
Block(0x0070, [0xC5, 0x10])]);
2012-03-25 08:25:41 +00:00
}
return TestSetup(&setup);
}
2012-03-23 16:14:15 +00:00
// For zeropage indirect address mode.
auto setup_addr_zpi()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
info.len = 2;
info.addr = 0x10C5;
2012-03-25 08:25:41 +00:00
setPC(cpu, 0x1000);
callNext("zpi no-wrap ", [Block(0x1000, [opcode, 0x70]),
Block(0x0070, [0xC5, 0x10])]);
callNext("zpi wrap ", [Block(0x1000, [opcode, 0xFF]),
Block(0x00FF, [0xC5]),
Block(0x0000, [0x10])]);
2012-03-25 08:25:41 +00:00
}
return TestSetup(&setup);
}
/*
* Splits with the SP at 0xFF, and then at 0x00 (to cover the case of
* pull operations when the stack is "empty").
*/
auto setup_pull_wrap()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
setSP(cpu, 0xFF);
callNext("wrap ");
2012-03-23 16:14:15 +00:00
setSP(cpu, 0x00);
callNext("no-wrap ");
}
return TestSetup(&setup);
2012-03-25 08:25:41 +00:00
}
/*
* For pull opcodes.
*
* Splits with 0x00, 0x40, and 0x80 at the top of the stack (to
* exercise different potential combinations of N and Z).
*/
auto setup_op_pull(Reg reg)
2012-03-23 16:14:15 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
auto sp = pageWrapAdd(getSP(cpu), 1);
ubyte other = (info.data > 0x00 && info.data < 0x80) ? 0x00 : 0x01;
setX(cpu, other); setY(cpu, other); setA(cpu, other);
callNext("pull ", [Block(sp, [info.data])]);
}
return connect(setup_pull_wrap(), setup_nz(), setup_data(),
TestSetup(&setup));
}
2012-03-23 16:14:15 +00:00
/*
* Splits with each possible meaningful status byte value on the
* stack, and with values with break/reserved bits set.
*/
auto setup_status()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 16; j++)
{
info.data = cast(ubyte)(i * 0x40 + j);
callNext(format("S %0.2X ", info.data | 0x30));
}
}
string[] msgs = ["B ", "R ", "BR "];
foreach (val; [0x10, 0x20, 0x30])
{
info.data = cast(ubyte)val;
callNext("S " ~ msgs[(info.data >> 4) - 1]);
}
2012-03-25 08:25:41 +00:00
}
return TestSetup(&setup);
}
2012-03-25 08:25:41 +00:00
// For PLP.
auto setup_op_PLP()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-23 16:14:15 +00:00
{
mixin testCallNext;
auto sp = pageWrapAdd(getSP(cpu), 1);
setStatus(cpu, ~info.data);
callNext("PLP ", [Block(sp, [info.data])]);
2012-03-23 16:14:15 +00:00
}
return connect(setup_pull_wrap(), setup_status(), TestSetup(&setup));
}
2012-03-25 08:25:41 +00:00
/*
* Splits with the SP at 0x00, and then at 0xFF (to cover the case of
* push operations when the stack is "full").
*/
auto setup_push_wrap()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
setSP(cpu, 0x00);
callNext("wrap ");
setSP(cpu, 0xFF);
callNext("no-wrap ");
}
return TestSetup(&setup);
2012-03-23 16:14:15 +00:00
}
/*
* For push opcodes.
*
* Splits with 0x00, 0x40, and 0x80 in the appropriate register.
*/
auto setup_op_push(Reg reg)
2012-03-23 16:14:15 +00:00
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
setX(cpu, 0x01); setY(cpu, 0x01); setA(cpu, 0x01);
setReg(cpu, reg, info.data);
callNext("push ", [Block(getSP(cpu), [~info.data])]);
}
return connect(setup_push_wrap(), setup_data(), TestSetup(&setup));
}
2012-03-23 16:14:15 +00:00
/*
* For PHP.
*
* Puts a garbage value (that does not correspond to the current
* status) just below the top of the stack.
*/
auto setup_op_PHP()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-25 08:25:41 +00:00
{
mixin testCallNext;
info.data = getStatus(cpu);
callNext("PHP ", [Block(getSP(cpu), [~info.data])]);
}
return connect(setup_nvdzc(), setup_push_wrap(), TestSetup(&setup));
}
/*
* For load opcodes.
*
* Splits with 0x00, 0x40, and 0x80 in the source memory location.
*/
auto setup_op_load(Reg reg)
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
callNext("load ", [Block(info.addr, [info.data])]);
2012-03-25 08:25:41 +00:00
}
return connect(setup_nz(), setup_data(), TestSetup(&setup));
}
2012-03-23 16:14:15 +00:00
// Splits with 0x00, 0x40, 0x80 in the source register.
auto setup_op_store(Reg reg)
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
2012-03-23 16:14:15 +00:00
{
mixin testCallNext;
setReg(cpu, reg, info.data);
callNext("store ", [Block(info.addr, [~info.data])]);
}
return connect(setup_data(), TestSetup(&setup));
}
// For SAX.
auto setup_op_SAX()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
if (opcode == 0x83)
{
setA(cpu, 0x00);
callNext("zero SAX ");
setA(cpu, 0x2B);
callNext("positive SAX ");
setA(cpu, 0x8B);
callNext("negative SAX ");
}
else
{
setA(cpu, 0x00); setX(cpu, 0x00);
callNext("zero SAX ");
setA(cpu, 0x10); setX(cpu, 0x01);
callNext("zero SAX ");
setA(cpu, 0x0B); setX(cpu, 0x0D);
callNext("positive SAX ");
setA(cpu, 0x8B); setX(cpu, 0x8D);
callNext("negative SAX ");
}
2012-03-23 16:14:15 +00:00
}
return TestSetup(&setup);
}
2012-03-23 16:14:15 +00:00
// For STZ.
auto setup_op_STZ()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
info.data = 0;
callNext("STZ ", [Block(info.addr, [~info.data])]);
}
return TestSetup(&setup);
}
// setup_bit_val, puts in memory;
auto setup_bit_data()
{
auto setup(ubyte opcode, CpuInfo cpu, Block[] data, OpInfo info,
string msg, TestSetup* next)
{
mixin testCallNext;
info.data = 0x80;
callNext("N1 ", [Block(info.addr, [info.data])]);
info.data = 0x40;
callNext("V1 ", [Block(info.addr, [info.data])]);
info.data = 0xC0;