1
0
mirror of https://github.com/pevans/erc-c.git synced 2024-12-21 08:30:55 +00:00
erc-c/tests/mos6502.dis.c
Peter Evans 65f13bb1e4 Add missing output and fix buffer size issue
We use BUFSIZ everywhere, except in setvbuf(), which kinda needs to know
the proper buffer size. Because we were passing 256, which is (much!)
lower than BUFSIZ, we were wrapping output around in an odd, unexpected
way.
2018-02-19 21:49:58 -06:00

278 lines
7.8 KiB
C

#include <criterion/criterion.h>
#include <unistd.h>
#include "mos6502.h"
#include "mos6502.dis.h"
#include "mos6502.enums.h"
/*
* BUFSIZ is the normal block-buffer size that a FILE stream would use
* (possibly amongst other things).
*/
static char buf[BUFSIZ];
/*
* This is the file stream we will be using to write our disassembly
* code into.
*/
static FILE *stream = NULL;
static mos6502 *cpu = NULL;
static vm_segment *mem = NULL;
static void
setup()
{
// Ok, so...there's some...trickery going on here. As you might
// guess by the file path being /dev/null.
stream = fopen("/dev/null", "w");
if (stream == NULL) {
perror("Could not open temporary file for mos6502 disassembly tests");
}
// The C standard library allows us to set an arbitrary buffer for a
// file stream. It also allows us to fully buffer the file stream,
// which means nothing is written to file until an fflush() or an
// fclose() is called (or something else I can't think of). So we
// can use the FILE abstraction to write our disassembly results
// into, but use an underlying string buffer that we can easily
// check with Criterion. Uh, unless we blow out the buffer size...
// don't do that :D
setvbuf(stream, buf, _IOFBF, BUFSIZ);
mem = vm_segment_create(MOS6502_MEMSIZE);
cpu = mos6502_create(mem, mem);
}
static void
teardown()
{
fclose(stream);
mos6502_free(cpu);
vm_segment_free(mem);
}
static void
assert_buf(const char *str)
{
// This will set the cursor position in the file back to the start
// of the file stream.
rewind(stream);
// Our actual assertion. The downside to doing it this way is that
// when Criterion flags an assertion failure, it'll highlight _this_
// line in the file, not in the test. It might be worth macroifying
// this code.
cr_assert_str_eq(buf, str);
// We're not sure what previous tests may have run, and where NUL
// characters were set therein, so to be safe we wipe out the full
// contents of the test buffer after every test.
memset(buf, 0, BUFSIZ);
}
TestSuite(mos6502_dis, .init = setup, .fini = teardown);
Test(mos6502_dis, operand)
{
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, ABS, 0x1234);
assert_buf("$1234");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, ABX, 0x1234);
assert_buf("$1234,X");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, ABY, 0x1234);
assert_buf("$1234,Y");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, IMM, 0x12);
assert_buf("#$12");
mos6502_set(cpu, 0x1234, 0x48);
mos6502_set(cpu, 0x1235, 0x34);
// For JMPs and JSRs (and BRKs), this should be a label and not a
// literal value. So we need to test both the literal and
// jump-labeled version.
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, IND, 0x1234);
assert_buf("($1234)");
mos6502_dis_jump_label(cpu, 0x1234, 0, IND);
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, IND, 0x1234);
assert_buf("ADDR_3448");
// Let's undo our label above...
mos6502_dis_jump_unlabel(0x1234);
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, IDX, 0x12);
assert_buf("($12,X)");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, IDY, 0x34);
assert_buf("($34),Y");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, ZPG, 0x34);
assert_buf("$34");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, ZPX, 0x34);
assert_buf("$34,X");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, ZPY, 0x34);
assert_buf("$34,Y");
// These should both end up printing nothing
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, ACC, 0);
assert_buf("");
mos6502_dis_operand(cpu, buf, sizeof(buf), 0, IMP, 0);
assert_buf("");
// Test a forward jump (operand < 128)
mos6502_dis_operand(cpu, buf, sizeof(buf), 500, REL, 52);
assert_buf("ADDR_0228");
// Test a backward jump (operand >= 128)
mos6502_dis_operand(cpu, buf, sizeof(buf), 500, REL, 152);
assert_buf("ADDR_018c");
}
Test(mos6502_dis, instruction)
{
#define TEST_INST(x) \
mos6502_dis_instruction(buf, sizeof(buf) - 1, x); \
assert_buf(#x)
TEST_INST(ADC);
TEST_INST(AND);
TEST_INST(ASL);
TEST_INST(BCC);
TEST_INST(BCS);
TEST_INST(BEQ);
TEST_INST(BIT);
TEST_INST(BMI);
TEST_INST(BNE);
TEST_INST(BPL);
TEST_INST(BRK);
TEST_INST(BVC);
TEST_INST(BVS);
TEST_INST(CLC);
TEST_INST(CLD);
TEST_INST(CLI);
TEST_INST(CLV);
TEST_INST(CMP);
TEST_INST(CPX);
TEST_INST(CPY);
TEST_INST(DEC);
TEST_INST(DEX);
TEST_INST(DEY);
TEST_INST(EOR);
TEST_INST(INC);
TEST_INST(INX);
TEST_INST(INY);
TEST_INST(JMP);
TEST_INST(JSR);
TEST_INST(LDA);
TEST_INST(LDX);
TEST_INST(LDY);
TEST_INST(LSR);
TEST_INST(NOP);
TEST_INST(ORA);
TEST_INST(PHA);
TEST_INST(PHP);
TEST_INST(PLA);
TEST_INST(PLP);
TEST_INST(ROL);
TEST_INST(ROR);
TEST_INST(RTI);
TEST_INST(RTS);
TEST_INST(SBC);
TEST_INST(SEC);
TEST_INST(SED);
TEST_INST(SEI);
TEST_INST(STA);
TEST_INST(STX);
TEST_INST(STY);
TEST_INST(TAX);
TEST_INST(TAY);
TEST_INST(TSX);
TEST_INST(TXA);
TEST_INST(TXS);
TEST_INST(TYA);
}
Test(mos6502_dis, expected_bytes)
{
#define TEST_BYTES(x, y) \
cr_assert_eq(mos6502_dis_expected_bytes(x), y)
TEST_BYTES(ACC, 0);
TEST_BYTES(ABS, 2);
TEST_BYTES(ABX, 2);
TEST_BYTES(ABY, 2);
TEST_BYTES(IMM, 1);
TEST_BYTES(IMP, 0);
TEST_BYTES(IND, 2);
TEST_BYTES(IDX, 1);
TEST_BYTES(IDY, 1);
TEST_BYTES(REL, 1);
TEST_BYTES(ZPG, 1);
TEST_BYTES(ZPX, 1);
TEST_BYTES(ZPY, 1);
}
Test(mos6502_dis, opcode)
{
int bytes;
mos6502_set(cpu, 0, 0x29); // AND (imm)
mos6502_set(cpu, 1, 0x38);
bytes = mos6502_dis_opcode(cpu, stream, 0);
assert_buf(" AND #$38 ; pc:0000 cy:02 addr:0000 val:38 a:00 x:00 y:00 p:NO-dIZC s:ff | 29 38\n");
cr_assert_eq(bytes, 2);
}
Test(mos6502_dis, scan)
{
mos6502_set(cpu, 0, 0x29); // AND (imm)
mos6502_set(cpu, 1, 0x38);
mos6502_set(cpu, 2, 0x88); // DEY (imp)
mos6502_set(cpu, 3, 0x6C); // JMP (ind)
mos6502_set(cpu, 4, 0x34);
mos6502_set(cpu, 5, 0x12);
mos6502_dis_scan(cpu, stream, 0, 6);
// FIXME: scan does not currently advance the PC byte; _should_ it?
// I'm honestly not sure. There are definitely times (e.g. during
// runtime operation) when you don't want it to, but as a standalone
// disassembler, it feels less useful when PC isn't emulated.
assert_buf(" AND #$38 ; pc:0000 cy:02 addr:0000 val:38 a:00 x:00 y:00 p:NO-dIZC s:ff | 29 38\n"
" DEY ; pc:0000 cy:02 addr:0000 val: a:00 x:00 y:00 p:NO-dIZC s:ff | 88\n"
" JMP ($1234) ; pc:0000 cy:05 addr:0000 val:29 a:00 x:00 y:00 p:NO-dIZC s:ff | 6c 34 12\n"
";;;\n\n"
);
}
/*
* These tests are all contained within the jump_label test below.
* Test(mos6502_dis, jump_unlabel)
* Test(mos6502_dis, is_jump_label)
*/
Test(mos6502_dis, jump_label)
{
cr_assert_eq(mos6502_dis_is_jump_label(123), false);
mos6502_set(cpu, 123, 5);
mos6502_set(cpu, 124, 0);
mos6502_dis_jump_label(cpu, 123, 0, IND);
cr_assert_eq(mos6502_dis_is_jump_label(5), true);
mos6502_dis_jump_unlabel(123);
cr_assert_eq(mos6502_dis_is_jump_label(123), false);
// Testing forward relative
mos6502_dis_jump_label(cpu, 123, 10, REL);
cr_assert_eq(mos6502_dis_is_jump_label(123 + 10), true);
mos6502_dis_jump_unlabel(123 + 10);
// Testing backward relative
mos6502_dis_jump_label(cpu, 133, 1000, REL);
cr_assert_eq(mos6502_dis_is_jump_label(133 + 1000 - 256), true);
mos6502_dis_jump_unlabel(133 + 1000 - 256);
}
Test(mos6502_dis, label)
{
mos6502_dis_label(buf, sizeof(buf) - 1, 123);
assert_buf("ADDR_007b");
}