diff options
Diffstat (limited to '6502')
| -rw-r--r-- | 6502/include/lcq/6502.h | 24 | ||||
| -rw-r--r-- | 6502/src/6502.c | 200 | ||||
| -rw-r--r-- | 6502/src/test.c | 4 | ||||
| -rw-r--r-- | 6502/test/test.fnl | 101 |
4 files changed, 237 insertions, 92 deletions
diff --git a/6502/include/lcq/6502.h b/6502/include/lcq/6502.h index 3d10301..2a8a845 100644 --- a/6502/include/lcq/6502.h +++ b/6502/include/lcq/6502.h @@ -4,11 +4,12 @@ #include <lcq/6502/types.h> typedef enum { - L6502_FLAG_CARRY = 1 << 1, - L6502_FLAG_ZERO = 1 << 2, - L6502_FLAG_INTERRUPT_DISABLE = 1 << 3, - L6502_FLAG_DECIMAL = 1 << 4, - L6502_FLAG_BREAK_COMMAND = 1 << 5, + L6502_FLAG_CARRY = 1 << 0, + L6502_FLAG_ZERO = 1 << 1, + L6502_FLAG_INTERRUPT_DISABLE = 1 << 2, + L6502_FLAG_DECIMAL = 1 << 3, + L6502_FLAG_BREAK_COMMAND = 1 << 4, + L6502_FLAG_UNUSED = 1 << 5, L6502_FLAG_OVERFLOW = 1 << 6, L6502_FLAG_NEGATIVE = 1 << 7, } l6502_flag; @@ -49,18 +50,25 @@ static inline u16 l6502_read_addr(l6502_emu *e, u16 addr) { u8 ahi = l6502_read(e, addr + 1); return ahi << 8 | alo; } +static inline u16 l6502_read_addr_wrap_page(l6502_emu *e, u16 addr) { + u16 page = addr & 0xff00; + u8 off = (u8) addr; + u8 alo = l6502_read(e, page + off); + u8 ahi = l6502_read(e, page + (u8) (off + 1)); + return ahi << 8 | alo; +} static inline u8 l6502_pop(l6502_emu *e) { return l6502_read(e, 0x0100 + (u16) ++e->regs[L6502_REG_SP]); } static inline void l6502_push(l6502_emu *e, u8 val) { l6502_write(e, 0x0100 + (u16) e->regs[L6502_REG_SP]--, val); } static inline u16 l6502_pop_addr(l6502_emu *e) { - u8 ahi = l6502_pop(e); u8 alo = l6502_pop(e); + u8 ahi = l6502_pop(e); return ahi << 8 | alo; } static inline void l6502_push_addr(l6502_emu *e, u16 addr) { - l6502_push(e, (u8) (addr & 0xff)); l6502_push(e, (u8) (addr >> 8)); + l6502_push(e, (u8) (addr & 0xff)); } -void l6502_emulate(l6502_emu *e); +const char *l6502_emulate(l6502_emu *e); #endif diff --git a/6502/src/6502.c b/6502/src/6502.c index 54013ae..eb0f2c0 100644 --- a/6502/src/6502.c +++ b/6502/src/6502.c @@ -2,6 +2,7 @@ #include <lcq/6502/ins.h> #include <stdlib.h> +#include <stdio.h> static u8 basic_read_handler(void *data, u16 addr) { return ((u8 *) data)[addr]; @@ -42,79 +43,94 @@ static u16 amode_zeropage(l6502_emu *e) { return (u16) l6502_read(e, e->pc++); } static u16 amode_zeropage_x(l6502_emu *e) { - return (u16) (l6502_read(e, e->pc++) + e->regs[L6502_REG_X]); + return (u16) (u8) (l6502_read(e, e->pc++) + e->regs[L6502_REG_X]); +} +static u16 amode_zeropage_y(l6502_emu *e) { + return (u16) (u8) (l6502_read(e, e->pc++) + e->regs[L6502_REG_Y]); } static u16 amode_indirect_zeropage_x(l6502_emu *e) { - return l6502_read_addr(e, l6502_read(e, e->pc++) + (u16) e->regs[L6502_REG_X]); + return l6502_read_addr_wrap_page(e, (u8) (l6502_read(e, e->pc++) + e->regs[L6502_REG_X])); } static u16 amode_indirect_zeropage_y(l6502_emu *e) { - return l6502_read_addr(e, l6502_read(e, e->pc++)) + (u16) e->regs[L6502_REG_Y]; + return l6502_read_addr_wrap_page(e, l6502_read(e, e->pc++)) + (u16) e->regs[L6502_REG_Y]; } -void l6502_emulate(l6502_emu *e) { +const char *l6502_emulate(l6502_emu *e) { u8 ins = l6502_read(e, e->pc++); u8 val = 0; u16 res = 0; bool set_flags = true; + bool set_carry = false; bool set_result = true; l6502_reg set_reg = L6502_REG__MARKER; u16 addr = 0; + const char *nm = NULL; + switch (ins) { /* identify some special-case instructions */ case BRK: - l6502_push_addr(e, e->pc); - l6502_push(e, e->regs[L6502_REG_FLAGS]); - e->pc = l6502_read_addr(e, 0xfffe) - 1; - l6502_flag_set(e, L6502_FLAG_BREAK_COMMAND, true); - break; - case PHP: l6502_push(e, e->regs[L6502_REG_FLAGS]); break; - case CLC: l6502_flag_set(e, L6502_FLAG_CARRY, false); break; - case JSR: - addr = l6502_read_addr(e, e->pc); + nm = "brk"; l6502_push_addr(e, e->pc + 1); - e->pc = addr; - break; - case PLP: e->regs[L6502_REG_FLAGS] = l6502_pop(e); break; - case SEC: l6502_flag_set(e, L6502_FLAG_CARRY, true); break; + l6502_push(e, e->regs[L6502_REG_FLAGS] | L6502_FLAG_BREAK_COMMAND); + e->pc = l6502_read_addr(e, 0xfffe); + l6502_flag_set(e, L6502_FLAG_INTERRUPT_DISABLE, true); + return nm; + case PHP: nm = "php"; l6502_push(e, e->regs[L6502_REG_FLAGS] | L6502_FLAG_BREAK_COMMAND | L6502_FLAG_UNUSED); return nm; + case CLC: nm = "clc"; l6502_flag_set(e, L6502_FLAG_CARRY, false); return nm; + case JSR: { + nm = "jsr"; + u16 alo = l6502_read(e, e->pc); + l6502_push_addr(e, e->pc + 1); + u16 ahi = l6502_read(e, e->pc + 1); + e->pc = ahi << 8 | alo; + return nm; + } + case PLP: nm = "plp"; e->regs[L6502_REG_FLAGS] = l6502_pop(e) & (u8) ~L6502_FLAG_BREAK_COMMAND; return nm; + case SEC: nm = "sec"; l6502_flag_set(e, L6502_FLAG_CARRY, true); return nm; case RTI: - e->regs[L6502_REG_FLAGS] = l6502_pop(e); + nm = "rti"; + e->regs[L6502_REG_FLAGS] = l6502_pop(e) & (u8) ~L6502_FLAG_BREAK_COMMAND; e->pc = l6502_pop_addr(e); - break; - case PHA: l6502_push(e, e->regs[L6502_REG_A]); break; + return nm; + case PHA: nm = "pha"; l6502_push(e, e->regs[L6502_REG_A]); return nm; case JMP_ABSOLUTE: + nm = "jmp"; addr = l6502_read_addr(e, e->pc); e->pc = addr; - break; - case CLI: l6502_flag_set(e, L6502_FLAG_INTERRUPT_DISABLE, false); break; - case RTS: e->pc = l6502_pop_addr(e) + 1; break; - case PLA: e->regs[L6502_REG_A] = l6502_pop(e); break; + return nm; + case CLI: nm = "cli"; l6502_flag_set(e, L6502_FLAG_INTERRUPT_DISABLE, false); return nm; + case RTS: nm = "rts"; e->pc = l6502_pop_addr(e) + 1; return nm; + case PLA: nm = "pla"; e->regs[L6502_REG_A] = l6502_pop(e); flags_check_zn(e, e->regs[L6502_REG_A]); return nm; case JMP_INDIRECT: - addr = l6502_read_addr(e, l6502_read_addr(e, e->pc)); + nm = "jmp"; + addr = l6502_read_addr_wrap_page(e, l6502_read_addr(e, e->pc)); e->pc = addr; - break; - case SEI: l6502_flag_set(e, L6502_FLAG_INTERRUPT_DISABLE, true); break; - case DEY: e->regs[L6502_REG_Y] -= 1; flags_check_zn(e, e->regs[L6502_REG_Y]); break; - case TXA: e->regs[L6502_REG_A] = e->regs[L6502_REG_X]; flags_check_zn(e, e->regs[L6502_REG_A]); break; - case TYA: e->regs[L6502_REG_A] = e->regs[L6502_REG_Y]; flags_check_zn(e, e->regs[L6502_REG_A]); break; - case TXS: e->regs[L6502_REG_SP] = e->regs[L6502_REG_X]; break; /* note TXS does not set flags */ - case TAY: e->regs[L6502_REG_Y] = e->regs[L6502_REG_A]; flags_check_zn(e, e->regs[L6502_REG_Y]); break; - case TAX: e->regs[L6502_REG_X] = e->regs[L6502_REG_A]; flags_check_zn(e, e->regs[L6502_REG_X]); break; - case CLV: l6502_flag_set(e, L6502_FLAG_OVERFLOW, false); break; - case TSX: e->regs[L6502_REG_X] = e->regs[L6502_REG_SP]; flags_check_zn(e, e->regs[L6502_REG_X]); break; - case INY: e->regs[L6502_REG_Y] += 1; flags_check_zn(e, e->regs[L6502_REG_Y]); break; - case DEX: e->regs[L6502_REG_X] -= 1; flags_check_zn(e, e->regs[L6502_REG_X]); break; - case CLD: l6502_flag_set(e, L6502_FLAG_DECIMAL, false); break; - case INX: e->regs[L6502_REG_X] += 1; flags_check_zn(e, e->regs[L6502_REG_X]); break; - case NOP: break; - case SED: l6502_flag_set(e, L6502_FLAG_DECIMAL, true); break; + return nm; + case SEI: nm = "sei"; l6502_flag_set(e, L6502_FLAG_INTERRUPT_DISABLE, true); return nm; + case DEY: nm = "dey"; e->regs[L6502_REG_Y] -= 1; flags_check_zn(e, e->regs[L6502_REG_Y]); return nm; + case TXA: nm = "txa"; e->regs[L6502_REG_A] = e->regs[L6502_REG_X]; flags_check_zn(e, e->regs[L6502_REG_A]); return nm; + case TYA: nm = "tya"; e->regs[L6502_REG_A] = e->regs[L6502_REG_Y]; flags_check_zn(e, e->regs[L6502_REG_A]); return nm; + case TXS: nm = "txs"; e->regs[L6502_REG_SP] = e->regs[L6502_REG_X]; return nm; /* note TXS does not set flags */ + case TAY: nm = "tay"; e->regs[L6502_REG_Y] = e->regs[L6502_REG_A]; flags_check_zn(e, e->regs[L6502_REG_Y]); return nm; + case TAX: nm = "tax"; e->regs[L6502_REG_X] = e->regs[L6502_REG_A]; flags_check_zn(e, e->regs[L6502_REG_X]); return nm; + case CLV: nm = "clv"; l6502_flag_set(e, L6502_FLAG_OVERFLOW, false); return nm; + case TSX: nm = "tsx"; e->regs[L6502_REG_X] = e->regs[L6502_REG_SP]; flags_check_zn(e, e->regs[L6502_REG_X]); return nm; + case INY: nm = "iny"; e->regs[L6502_REG_Y] += 1; flags_check_zn(e, e->regs[L6502_REG_Y]); return nm; + case DEX: nm = "dex"; e->regs[L6502_REG_X] -= 1; flags_check_zn(e, e->regs[L6502_REG_X]); return nm; + case CLD: nm = "cld"; l6502_flag_set(e, L6502_FLAG_DECIMAL, false); return nm; + case INX: nm = "inx"; e->regs[L6502_REG_X] += 1; flags_check_zn(e, e->regs[L6502_REG_X]); return nm; + case NOP: nm = "nop"; return nm; + case SED: nm = "sed"; l6502_flag_set(e, L6502_FLAG_DECIMAL, true); return nm; } + if ((ins & 0b11111) == 0b10000) { /* identify conditional branches */ l6502_flag flags[4] = { L6502_FLAG_NEGATIVE, L6502_FLAG_OVERFLOW, L6502_FLAG_CARRY, L6502_FLAG_ZERO }; u8 off = l6502_read(e, e->pc++); if (l6502_flag_get(e, flags[(ins >> 6) & 0b11]) == ((ins >> 5) & 0b1)) { e->pc = (u16) ((i16) e->pc + (i16) (i8) off); } - return; + return "jcc"; } + switch (ins & 0b11) { /* identify instructions in common forms */ case 0b01: { /* group one */ set_reg = L6502_REG_A; @@ -130,24 +146,38 @@ void l6502_emulate(l6502_emu *e) { } val = l6502_read(e, addr); switch ((ins >> 5) & 0b111) { - case G1_OPCODE_ORA: res = (u16) e->regs[L6502_REG_A] | (u16) val; break; - case G1_OPCODE_AND: res = (u16) e->regs[L6502_REG_A] & (u16) val; break; - case G1_OPCODE_EOR: res = (u16) e->regs[L6502_REG_A] ^ (u16) val; break; - case G1_OPCODE_ADC: - res = (u16) e->regs[L6502_REG_A] + (u16) val; - res += (u16) l6502_flag_get(e, L6502_FLAG_CARRY); - l6502_flag_set(e, L6502_FLAG_OVERFLOW, ((res >> 8) != 0) != ((res >> 7) & 0b1)); + case G1_OPCODE_ORA: nm = "ora"; res = (u16) e->regs[L6502_REG_A] | (u16) val; break; + case G1_OPCODE_AND: nm = "and"; res = (u16) e->regs[L6502_REG_A] & (u16) val; break; + case G1_OPCODE_EOR: nm = "eor"; res = (u16) e->regs[L6502_REG_A] ^ (u16) val; break; + case G1_OPCODE_ADC: { + nm = "adc"; + set_carry = true; + i8 a0 = (i8) e->regs[L6502_REG_A]; + i8 a1 = (i8) val; + i8 a2 = (i8) l6502_flag_get(e, L6502_FLAG_CARRY); + res = (u16) (u8) a0 + (u16) (u8) a1; res += (u16) (u8) a2; + l6502_flag_set(e, L6502_FLAG_OVERFLOW, ((a0 >= 0) == (a1 >= 0)) && ((a0 >= 0) != ((i8) res >= 0))); break; - case G1_OPCODE_STA: l6502_write(e, addr, e->regs[L6502_REG_A]); set_flags = set_result = false; break; - case G1_OPCODE_LDA: res = (u16) l6502_read(e, addr); break; + } + case G1_OPCODE_STA: nm = "sta"; l6502_write(e, addr, e->regs[L6502_REG_A]); set_flags = set_result = false; break; + case G1_OPCODE_LDA: nm = "lda"; res = (u16) l6502_read(e, addr); break; case G1_OPCODE_CMP: + nm = "cmp"; set_result = false; - res = (u16) e->regs[L6502_REG_A] - (u16) val; + set_carry = true; + res = (u16) (u8) (e->regs[L6502_REG_A] - val); + if (e->regs[L6502_REG_A] >= val) res |= 1 << 8; break; case G1_OPCODE_SBC: - res = (u16) e->regs[L6502_REG_A] - (u16) val; - res -= (u16) (1 - l6502_flag_get(e, L6502_FLAG_CARRY)); - l6502_flag_set(e, L6502_FLAG_OVERFLOW, ((res >> 8) != 0) != ((res >> 7) & 0b1)); + nm = "sbc"; + set_carry = true; + i8 a0 = (i8) e->regs[L6502_REG_A]; + i8 a1 = (i8) val; + i8 a2 = (i8) (1 - l6502_flag_get(e, L6502_FLAG_CARRY)); + res = (u16) (u8) a0 - (u16) (u8) a1; res -= (u16) (u8) a2; + res &= 0xff; + if ((u8) a0 >= (u8) a1 + (u8) a2) res |= 1 << 8; + l6502_flag_set(e, L6502_FLAG_OVERFLOW, !((a0 >= 0) == (a1 >= 0)) && ((a0 >= 0) != ((i8) res >= 0))); break; } break; @@ -158,26 +188,48 @@ void l6502_emulate(l6502_emu *e) { case G2_ADDRESSING_MODE_ZEROPAGE: addr = amode_zeropage(e); break; case G2_ADDRESSING_MODE_ACCUMULATOR: addr = (u16) e->regs[L6502_REG_A]; set_reg = L6502_REG_A; break; case G2_ADDRESSING_MODE_ABSOLUTE: addr = amode_absolute(e); break; - case G2_ADDRESSING_MODE_ZEROPAGE_X: addr = amode_zeropage_x(e); break; - case G2_ADDRESSING_MODE_ABSOLUTE_X: addr = amode_absolute_x(e); break; + case G2_ADDRESSING_MODE_ZEROPAGE_X: + switch ((ins >> 5) & 0b111) { + case G2_OPCODE_LDX: case G2_OPCODE_STX: addr = amode_zeropage_y(e); break; + default: addr = amode_zeropage_x(e); break; + } + break; + case G2_ADDRESSING_MODE_ABSOLUTE_X: + switch ((ins >> 5) & 0b111) { + case G2_OPCODE_LDX: case G2_OPCODE_STX: addr = amode_absolute_y(e); break; + default: addr = amode_absolute_x(e); break; + } + break; } - val = set_reg ? (u8) addr : l6502_read(e, addr); + val = set_reg != L6502_REG__MARKER ? (u8) addr : l6502_read(e, addr); switch ((ins >> 5) & 0b111) { - case G2_OPCODE_ASL: res = ((u16) val) << 1; break; - case G2_OPCODE_ROL: res = ((u16) val) << 1 | (u16) l6502_flag_get(e, L6502_FLAG_CARRY); break; + case G2_OPCODE_ASL: + nm = "asl"; + set_carry = true; + res = ((u16) val) << 1; + break; + case G2_OPCODE_ROL: + nm = "rol"; + set_carry = true; + res = ((u16) val) << 1 | (u16) l6502_flag_get(e, L6502_FLAG_CARRY); + break; case G2_OPCODE_LSR: + nm = "lsr"; + set_carry = true; res = ((u16) val) >> 1 | (u16) (val & 0b1) << 8; /* place the previous low bit in carry */ break; case G2_OPCODE_ROR: + nm = "ror"; + set_carry = true; res = ((u16) val) >> 1 | (u16) l6502_flag_get(e, L6502_FLAG_CARRY) << 7 | (u16) (val & 0b1) << 8; /* same as above */ break; - case G2_OPCODE_STX: l6502_write(e, addr, e->regs[L6502_REG_X]); set_flags = set_result = false; break; - case G2_OPCODE_LDX: res = (u16) l6502_read(e, addr); set_reg = L6502_REG_X; break; - case G2_OPCODE_DEC: res = (u16) val - 1; break; - case G2_OPCODE_INC: res = (u16) val + 1; break; + case G2_OPCODE_STX: nm = "stx"; l6502_write(e, addr, e->regs[L6502_REG_X]); set_flags = set_result = false; break; + case G2_OPCODE_LDX: nm = "ldx"; res = (u16) l6502_read(e, addr); set_reg = L6502_REG_X; break; + case G2_OPCODE_DEC: nm = "dec"; res = (u16) val - 1; break; + case G2_OPCODE_INC: nm = "inc"; res = (u16) val + 1; break; } break; } @@ -192,31 +244,39 @@ void l6502_emulate(l6502_emu *e) { val = l6502_read(e, addr); switch ((ins >> 5) & 0b111) { case G3_OPCODE_BIT: + nm = "bit"; set_flags = set_result = false; l6502_flag_set(e, L6502_FLAG_ZERO, (e->regs[L6502_REG_A] & val) == 0); l6502_flag_set(e, L6502_FLAG_OVERFLOW, (val >> 6) & 0b1); l6502_flag_set(e, L6502_FLAG_NEGATIVE, (val >> 7) & 0b1); break; - case G3_OPCODE_STY: l6502_write(e, addr, e->regs[L6502_REG_Y]); set_flags = set_result = false; break; - case G3_OPCODE_LDY: res = (u16) l6502_read(e, addr); set_reg = L6502_REG_Y; break; + case G3_OPCODE_STY: nm = "sty"; l6502_write(e, addr, e->regs[L6502_REG_Y]); set_flags = set_result = false; break; + case G3_OPCODE_LDY: nm = "ldy"; res = (u16) l6502_read(e, addr); set_reg = L6502_REG_Y; break; case G3_OPCODE_CPY: + nm = "cpy"; set_result = false; - res = (u16) e->regs[L6502_REG_Y] - (u16) val; + set_carry = true; + res = (u16) (u8) (e->regs[L6502_REG_Y] - val); + if (e->regs[L6502_REG_Y] >= val) res |= 1 << 8; break; case G3_OPCODE_CPX: + nm = "cpx"; set_result = false; - res = (u16) e->regs[L6502_REG_X] - (u16) val; + set_carry = true; + res = (u16) (u8) (e->regs[L6502_REG_X] - val); + if (e->regs[L6502_REG_X] >= val) res |= 1 << 8; break; } } } if (set_flags) { - l6502_flag_set(e, L6502_FLAG_ZERO, res == 0); + l6502_flag_set(e, L6502_FLAG_ZERO, (res & 0xff) == 0); l6502_flag_set(e, L6502_FLAG_NEGATIVE, (res >> 7) & 0b1); - l6502_flag_set(e, L6502_FLAG_CARRY, (res >> 8) != 0); + if (set_carry) l6502_flag_set(e, L6502_FLAG_CARRY, (res >> 8) != 0); } if (set_result) { if (set_reg != L6502_REG__MARKER) e->regs[set_reg] = (u8) res; else l6502_write(e, addr, (u8) res); } + return nm; } diff --git a/6502/src/test.c b/6502/src/test.c index 1497df6..694108e 100644 --- a/6502/src/test.c +++ b/6502/src/test.c @@ -22,6 +22,7 @@ int emu_summary(lua_State *l) { lua_pushinteger(l, e->regs[L6502_REG_A]); lua_setfield(l, -2, "a"); lua_pushinteger(l, e->regs[L6502_REG_X]); lua_setfield(l, -2, "x"); lua_pushinteger(l, e->regs[L6502_REG_Y]); lua_setfield(l, -2, "y"); + lua_pushinteger(l, e->regs[L6502_REG_FLAGS]); lua_setfield(l, -2, "flags"); lua_setfield(l, -2, "regs"); lua_newtable(l); lua_pushinteger(l, l6502_flag_get(e, L6502_FLAG_CARRY)); lua_setfield(l, -2, "carry"); @@ -107,7 +108,8 @@ int emu_write(lua_State *l) { int emu_emulate(lua_State *l); int emu_emulate(lua_State *l) { l6502_emu *e = get_emu(l); - l6502_emulate(e); + const char *ins = l6502_emulate(e); + lua_pushstring(l, ins); return 1; } diff --git a/6502/test/test.fnl b/6502/test/test.fnl index f99a0d6..a50bfd6 100644 --- a/6502/test/test.fnl +++ b/6502/test/test.fnl @@ -6,12 +6,52 @@ (let [f (io.open path)] (f:read "*all"))) -(fn must-match [fnm a e] +(fn contains? [x xs] + (each [_ y (pairs xs)] + (when (= x y) + (lua "return true"))) + nil) + +(fn must-match [st fnm a e] (if (= a e) true - (io.stderr:write (.. "\n -> in field " fnm ", expected " (tostring e) " but found " (tostring a) "\n")) - (os.exit 1))) + true + (do + (io.stderr:write (.. "\n -> in field " fnm ", expected " (tostring e) " but found " (tostring a))) + ;; (io.stderr:write (.. "\n -> initial emulator state:\n" (inspect st.initial))) + (io.stderr:write (.. "\n -> final emulator state:\n" (inspect st.final))) + (io.stderr:write "\n") + (os.exit 1)))) (fn u8-flag? [v f] (not (= (band v f) 0))) +(local flag-masks + { :carry l6502.flag.CARRY + :zero l6502.flag.ZERO + :interrupt_disable l6502.flag.INTERRUPT_DISABLE + :decimal l6502.flag.DECIMAL + :break_command l6502.flag.BREAK_COMMAND + :overflow l6502.flag.OVERFLOW + :negative l6502.flag.NEGATIVE + }) +(fn must-match-flag [st e nm f] + (must-match st nm (e:flag (. flag-masks nm)) (u8-flag? f.p (. flag-masks nm)))) + +(fn summary-compare-testcase [e s t] + (fn ae [actual expected] { : actual : expected }) + { :pc (ae s.pc t.pc) + :regs + { :sp (ae s.regs.sp t.s) + :a (ae s.regs.a t.a) + :x (ae s.regs.x t.x) + :y (ae s.regs.y t.y) + :flags (ae s.regs.flags t.p) + } + :flags + (collect [nm mask (pairs flag-masks)] + (values nm (ae (. s.flags nm) (if (u8-flag? t.p mask) 1 0)))) + :ram + (collect [_ [addr v] (ipairs t.ram)] + (values (.. :addr (tostring addr)) (ae (e:read addr) v))) + }) (fn run-test [e tc] (io.stderr:write (.. "running test: " tc.name "...")) @@ -24,22 +64,57 @@ (e:set-reg l6502.reg.FLAGS i.p) (each [_ [addr v] (ipairs i.ram)] (e:write addr v))) - (e:emulate) - (let [f tc.final] - (must-match :pc (e:pc) f.pc) - (must-match :sp (e:reg l6502.reg.SP) f.s) - (must-match :a (e:reg l6502.reg.A) f.a) - (must-match :x (e:reg l6502.reg.X) f.x) - (must-match :y (e:reg l6502.reg.Y) f.y)) + (let [ initial-summary (e:summary) + ins (e:emulate) + st + { :initial (summary-compare-testcase e initial-summary tc.initial) + :final (summary-compare-testcase e (e:summary) tc.final) + } + f tc.final] + (io.stderr:write (.. " " ins "...")) + (must-match st :pc (e:pc) f.pc) + (must-match st :sp (e:reg l6502.reg.SP) f.s) + (must-match st :a (e:reg l6502.reg.A) f.a) + (must-match st :x (e:reg l6502.reg.X) f.x) + (must-match st :y (e:reg l6502.reg.Y) f.y) + (must-match-flag st e :carry f) + (must-match-flag st e :zero f) + (must-match-flag st e :interrupt_disable f) + (must-match-flag st e :decimal f) + (must-match-flag st e :break_command f) + (must-match-flag st e :overflow f) + (must-match-flag st e :negative f) + (each [_ [addr v] (ipairs f.ram)] + (must-match st (.. :addr (tostring addr)) (e:read addr) v))) (io.stderr:write " done\n") ) +(local villains + [ 0x02 0x03 0x04 0x07 0x0b 0x0c 0x0f + 0x12 0x13 0x14 0x17 0x1a 0x1b 0x1c 0x1f + 0x22 0x23 0x27 0x2b 0x2f + 0x32 0x33 0x34 0x37 0x3a 0x3b 0x3c 0x3f + 0x42 0x43 0x44 0x47 0x4b 0x4f + 0x52 0x53 0x54 0x57 0x5a 0x5b 0x5c 0x5f ;; 0x539 <3 + 0x62 0x63 0x64 0x67 0x6b 0x6f + 0x72 0x73 0x74 0x77 0x7a 0x7b 0x7c 0x7f + 0x80 0x82 0x83 0x87 0x89 0x8b 0x8f + 0x92 0x93 0x97 0x9b 0x9c 0x9e 0x9f + 0xa3 0xa7 0xab 0xaf + 0xb2 0xb3 0xb7 0xbb 0xbf + 0xc2 0xc3 0xc7 0xcb 0xcf + 0xd2 0xd3 0xd4 0xd7 0xda 0xdb 0xdc 0xdf + 0xe2 0xe3 0xe7 0xeb 0xef + 0xf2 0xf3 0xf4 0xf7 0xfa 0xfb 0xfc 0xff + ]) + (fn run-tests-file [e p] (let [tcs (json.decode (slurp p))] (each [_ tc (ipairs tcs)] - (run-test e tc)))) + (when (not (u8-flag? tc.initial.p l6502.flag.DECIMAL)) + (run-test e tc))))) (let [e (l6502.new)] (for [i 0x00 0xff] - (run-tests-file e (string.format "testcases/6502/v1/%02x.json" i)) - )) + (when (not (contains? i villains)) + (run-tests-file e (string.format "testcases/6502/v1/%02x.json" i))))) |
