From 0606ecac2e8034a7ebbde359c7bdbadeb8a0da40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C5=9Een?= Date: Fri, 31 Jul 2026 15:22:08 +0200 Subject: [PATCH] define instruction set and opcode decoding tables --- src/cpu/m6502/instruction.zig | 150 +++++++++++++++ src/cpu/m6502/opcode/nmos6502.zig | 291 ++++++++++++++++++++++++++++++ src/cpu/m6502/opcode/root.zig | 2 + src/cpu/m6502/opcode/wdc65c02.zig | 291 ++++++++++++++++++++++++++++++ src/cpu/m6502/types.zig | 60 +++++- 5 files changed, 787 insertions(+), 7 deletions(-) create mode 100644 src/cpu/m6502/instruction.zig create mode 100644 src/cpu/m6502/opcode/nmos6502.zig create mode 100644 src/cpu/m6502/opcode/root.zig create mode 100644 src/cpu/m6502/opcode/wdc65c02.zig diff --git a/src/cpu/m6502/instruction.zig b/src/cpu/m6502/instruction.zig new file mode 100644 index 0000000..42043f8 --- /dev/null +++ b/src/cpu/m6502/instruction.zig @@ -0,0 +1,150 @@ +pub const AddressingMode = enum { + implied, + accumulator, + immediate, + zero_page, + zero_page_x, + zero_page_y, + relative, + absolute, + absolute_x, + absolute_y, + indirect, + indexed_indirect_x, + indirect_indexed_y, + + // 65C02 additions + zero_page_indirect, + absolute_indexed_indirect, + zero_page_relative, +}; + +pub const Operation = enum { + adc, + @"and", + asl, + bcc, + bcs, + beq, + bit, + bmi, + bne, + bpl, + brk, + bvc, + bvs, + clc, + cld, + cli, + clv, + cmp, + cpx, + cpy, + dec, + dex, + dey, + eor, + inc, + inx, + iny, + jmp, + jsr, + lda, + ldx, + ldy, + lsr, + nop, + ora, + pha, + php, + pla, + plp, + rol, + ror, + rti, + rts, + sbc, + sec, + sed, + sei, + sta, + stx, + sty, + tax, + tay, + tsx, + txa, + txs, + tya, + + // Undocumented NMOS 6502 instructions + ahx, + alr, + anc, + arr, + axs, + dcp, + isc, + kil, + las, + lax, + rla, + rra, + sax, + shx, + shy, + slo, + sre, + tas, + xaa, + + // 65C02 CMOS additions + bra, + phx, + phy, + plx, + ply, + stz, + trb, + tsb, + rmb0, + rmb1, + rmb2, + rmb3, + rmb4, + rmb5, + rmb6, + rmb7, + smb0, + smb1, + smb2, + smb3, + smb4, + smb5, + smb6, + smb7, + bbr0, + bbr1, + bbr2, + bbr3, + bbr4, + bbr5, + bbr6, + bbr7, + bbs0, + bbs1, + bbs2, + bbs3, + bbs4, + bbs5, + bbs6, + bbs7, + stp, + wai, +}; + +// Instruction contains strictly operation and addressing mode; cycles are dynamically accounted for by bus accesses. +const Instruction = @This(); + +operation: Operation, +mode: AddressingMode, diff --git a/src/cpu/m6502/opcode/nmos6502.zig b/src/cpu/m6502/opcode/nmos6502.zig new file mode 100644 index 0000000..c532053 --- /dev/null +++ b/src/cpu/m6502/opcode/nmos6502.zig @@ -0,0 +1,291 @@ +const Instruction = @import("../instruction.zig"); + +pub const instructions: [256]Instruction = .{ + // 0x00 - 0x0f + .{ .operation = .brk, .mode = .implied }, + .{ .operation = .ora, .mode = .indexed_indirect_x }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .slo, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .zero_page }, + .{ .operation = .ora, .mode = .zero_page }, + .{ .operation = .asl, .mode = .zero_page }, + .{ .operation = .slo, .mode = .zero_page }, + .{ .operation = .php, .mode = .implied }, + .{ .operation = .ora, .mode = .immediate }, + .{ .operation = .asl, .mode = .accumulator }, + .{ .operation = .anc, .mode = .immediate }, + .{ .operation = .nop, .mode = .absolute }, + .{ .operation = .ora, .mode = .absolute }, + .{ .operation = .asl, .mode = .absolute }, + .{ .operation = .slo, .mode = .absolute }, + + // 0x10 - 0x1f + .{ .operation = .bpl, .mode = .relative }, + .{ .operation = .ora, .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .slo, .mode = .indirect_indexed_y }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .ora, .mode = .zero_page_x }, + .{ .operation = .asl, .mode = .zero_page_x }, + .{ .operation = .slo, .mode = .zero_page_x }, + .{ .operation = .clc, .mode = .implied }, + .{ .operation = .ora, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .slo, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .absolute_x }, + .{ .operation = .ora, .mode = .absolute_x }, + .{ .operation = .asl, .mode = .absolute_x }, + .{ .operation = .slo, .mode = .absolute_x }, + + // 0x20 - 0x2f + .{ .operation = .jsr, .mode = .absolute }, + .{ .operation = .@"and", .mode = .indexed_indirect_x }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .rla, .mode = .indexed_indirect_x }, + .{ .operation = .bit, .mode = .zero_page }, + .{ .operation = .@"and", .mode = .zero_page }, + .{ .operation = .rol, .mode = .zero_page }, + .{ .operation = .rla, .mode = .zero_page }, + .{ .operation = .plp, .mode = .implied }, + .{ .operation = .@"and", .mode = .immediate }, + .{ .operation = .rol, .mode = .accumulator }, + .{ .operation = .anc, .mode = .immediate }, + .{ .operation = .bit, .mode = .absolute }, + .{ .operation = .@"and", .mode = .absolute }, + .{ .operation = .rol, .mode = .absolute }, + .{ .operation = .rla, .mode = .absolute }, + + // 0x30 - 0x3f + .{ .operation = .bmi, .mode = .relative }, + .{ .operation = .@"and", .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .rla, .mode = .indirect_indexed_y }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .@"and", .mode = .zero_page_x }, + .{ .operation = .rol, .mode = .zero_page_x }, + .{ .operation = .rla, .mode = .zero_page_x }, + .{ .operation = .sec, .mode = .implied }, + .{ .operation = .@"and", .mode = .absolute_y }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .rla, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .absolute_x }, + .{ .operation = .@"and", .mode = .absolute_x }, + .{ .operation = .rol, .mode = .absolute_x }, + .{ .operation = .rla, .mode = .absolute_x }, + + // 0x40 - 0x4f + .{ .operation = .rti, .mode = .implied }, + .{ .operation = .eor, .mode = .indexed_indirect_x }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .sre, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .zero_page }, + .{ .operation = .eor, .mode = .zero_page }, + .{ .operation = .lsr, .mode = .zero_page }, + .{ .operation = .sre, .mode = .zero_page }, + .{ .operation = .pha, .mode = .implied }, + .{ .operation = .eor, .mode = .immediate }, + .{ .operation = .lsr, .mode = .accumulator }, + .{ .operation = .alr, .mode = .immediate }, + .{ .operation = .jmp, .mode = .absolute }, + .{ .operation = .eor, .mode = .absolute }, + .{ .operation = .lsr, .mode = .absolute }, + .{ .operation = .sre, .mode = .absolute }, + + // 0x50 - 0x5f + .{ .operation = .bvc, .mode = .relative }, + .{ .operation = .eor, .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .sre, .mode = .indirect_indexed_y }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .eor, .mode = .zero_page_x }, + .{ .operation = .lsr, .mode = .zero_page_x }, + .{ .operation = .sre, .mode = .zero_page_x }, + .{ .operation = .cli, .mode = .implied }, + .{ .operation = .eor, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .sre, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .absolute_x }, + .{ .operation = .eor, .mode = .absolute_x }, + .{ .operation = .lsr, .mode = .absolute_x }, + .{ .operation = .sre, .mode = .absolute_x }, + + // 0x60 - 0x6f + .{ .operation = .rts, .mode = .implied }, + .{ .operation = .adc, .mode = .indexed_indirect_x }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .rra, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .zero_page }, + .{ .operation = .adc, .mode = .zero_page }, + .{ .operation = .ror, .mode = .zero_page }, + .{ .operation = .rra, .mode = .zero_page }, + .{ .operation = .pla, .mode = .implied }, + .{ .operation = .adc, .mode = .immediate }, + .{ .operation = .ror, .mode = .accumulator }, + .{ .operation = .arr, .mode = .immediate }, + .{ .operation = .jmp, .mode = .indirect }, + .{ .operation = .adc, .mode = .absolute }, + .{ .operation = .ror, .mode = .absolute }, + .{ .operation = .rra, .mode = .absolute }, + + // 0x70 - 0x7f + .{ .operation = .bvs, .mode = .relative }, + .{ .operation = .adc, .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .rra, .mode = .indirect_indexed_y }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .adc, .mode = .zero_page_x }, + .{ .operation = .ror, .mode = .zero_page_x }, + .{ .operation = .rra, .mode = .zero_page_x }, + .{ .operation = .sei, .mode = .implied }, + .{ .operation = .adc, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .rra, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .absolute_x }, + .{ .operation = .adc, .mode = .absolute_x }, + .{ .operation = .ror, .mode = .absolute_x }, + .{ .operation = .rra, .mode = .absolute_x }, + + // 0x80 - 0x8f + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .sta, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .sax, .mode = .indexed_indirect_x }, + .{ .operation = .sty, .mode = .zero_page }, + .{ .operation = .sta, .mode = .zero_page }, + .{ .operation = .stx, .mode = .zero_page }, + .{ .operation = .sax, .mode = .zero_page }, + .{ .operation = .dey, .mode = .implied }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .txa, .mode = .implied }, + .{ .operation = .xaa, .mode = .immediate }, + .{ .operation = .sty, .mode = .absolute }, + .{ .operation = .sta, .mode = .absolute }, + .{ .operation = .stx, .mode = .absolute }, + .{ .operation = .sax, .mode = .absolute }, + + // 0x90 - 0x9f + .{ .operation = .bcc, .mode = .relative }, + .{ .operation = .sta, .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .ahx, .mode = .indirect_indexed_y }, + .{ .operation = .sty, .mode = .zero_page_x }, + .{ .operation = .sta, .mode = .zero_page_x }, + .{ .operation = .stx, .mode = .zero_page_y }, + .{ .operation = .sax, .mode = .zero_page_y }, + .{ .operation = .tya, .mode = .implied }, + .{ .operation = .sta, .mode = .absolute_y }, + .{ .operation = .txs, .mode = .implied }, + .{ .operation = .tas, .mode = .absolute_y }, + .{ .operation = .shy, .mode = .absolute_x }, + .{ .operation = .sta, .mode = .absolute_x }, + .{ .operation = .shx, .mode = .absolute_y }, + .{ .operation = .ahx, .mode = .absolute_y }, + + // 0xa0 - 0xaf + .{ .operation = .ldy, .mode = .immediate }, + .{ .operation = .lda, .mode = .indexed_indirect_x }, + .{ .operation = .ldx, .mode = .immediate }, + .{ .operation = .lax, .mode = .indexed_indirect_x }, + .{ .operation = .ldy, .mode = .zero_page }, + .{ .operation = .lda, .mode = .zero_page }, + .{ .operation = .ldx, .mode = .zero_page }, + .{ .operation = .lax, .mode = .zero_page }, + .{ .operation = .tay, .mode = .implied }, + .{ .operation = .lda, .mode = .immediate }, + .{ .operation = .tax, .mode = .implied }, + .{ .operation = .lax, .mode = .immediate }, + .{ .operation = .ldy, .mode = .absolute }, + .{ .operation = .lda, .mode = .absolute }, + .{ .operation = .ldx, .mode = .absolute }, + .{ .operation = .lax, .mode = .absolute }, + + // 0xb0 - 0xbf + .{ .operation = .bcs, .mode = .relative }, + .{ .operation = .lda, .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .lax, .mode = .indirect_indexed_y }, + .{ .operation = .ldy, .mode = .zero_page_x }, + .{ .operation = .lda, .mode = .zero_page_x }, + .{ .operation = .ldx, .mode = .zero_page_y }, + .{ .operation = .lax, .mode = .zero_page_y }, + .{ .operation = .clv, .mode = .implied }, + .{ .operation = .lda, .mode = .absolute_y }, + .{ .operation = .tsx, .mode = .implied }, + .{ .operation = .las, .mode = .absolute_y }, + .{ .operation = .ldy, .mode = .absolute_x }, + .{ .operation = .lda, .mode = .absolute_x }, + .{ .operation = .ldx, .mode = .absolute_y }, + .{ .operation = .lax, .mode = .absolute_y }, + + // 0xc0 - 0xcf + .{ .operation = .cpy, .mode = .immediate }, + .{ .operation = .cmp, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .dcp, .mode = .indexed_indirect_x }, + .{ .operation = .cpy, .mode = .zero_page }, + .{ .operation = .cmp, .mode = .zero_page }, + .{ .operation = .dec, .mode = .zero_page }, + .{ .operation = .dcp, .mode = .zero_page }, + .{ .operation = .iny, .mode = .implied }, + .{ .operation = .cmp, .mode = .immediate }, + .{ .operation = .dex, .mode = .implied }, + .{ .operation = .axs, .mode = .immediate }, + .{ .operation = .cpy, .mode = .absolute }, + .{ .operation = .cmp, .mode = .absolute }, + .{ .operation = .dec, .mode = .absolute }, + .{ .operation = .dcp, .mode = .absolute }, + + // 0xd0 - 0xdf + .{ .operation = .bne, .mode = .relative }, + .{ .operation = .cmp, .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .dcp, .mode = .indirect_indexed_y }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .cmp, .mode = .zero_page_x }, + .{ .operation = .dec, .mode = .zero_page_x }, + .{ .operation = .dcp, .mode = .zero_page_x }, + .{ .operation = .cld, .mode = .implied }, + .{ .operation = .cmp, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .dcp, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .absolute_x }, + .{ .operation = .cmp, .mode = .absolute_x }, + .{ .operation = .dec, .mode = .absolute_x }, + .{ .operation = .dcp, .mode = .absolute_x }, + + // 0xe0 - 0xef + .{ .operation = .cpx, .mode = .immediate }, + .{ .operation = .sbc, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .isc, .mode = .indexed_indirect_x }, + .{ .operation = .cpx, .mode = .zero_page }, + .{ .operation = .sbc, .mode = .zero_page }, + .{ .operation = .inc, .mode = .zero_page }, + .{ .operation = .isc, .mode = .zero_page }, + .{ .operation = .inx, .mode = .implied }, + .{ .operation = .sbc, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .sbc, .mode = .immediate }, + .{ .operation = .cpx, .mode = .absolute }, + .{ .operation = .sbc, .mode = .absolute }, + .{ .operation = .inc, .mode = .absolute }, + .{ .operation = .isc, .mode = .absolute }, + + // 0xf0 - 0xff + .{ .operation = .beq, .mode = .relative }, + .{ .operation = .sbc, .mode = .indirect_indexed_y }, + .{ .operation = .kil, .mode = .implied }, + .{ .operation = .isc, .mode = .indirect_indexed_y }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .sbc, .mode = .zero_page_x }, + .{ .operation = .inc, .mode = .zero_page_x }, + .{ .operation = .isc, .mode = .zero_page_x }, + .{ .operation = .sed, .mode = .implied }, + .{ .operation = .sbc, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .isc, .mode = .absolute_y }, + .{ .operation = .nop, .mode = .absolute_x }, + .{ .operation = .sbc, .mode = .absolute_x }, + .{ .operation = .inc, .mode = .absolute_x }, + .{ .operation = .isc, .mode = .absolute_x }, +}; diff --git a/src/cpu/m6502/opcode/root.zig b/src/cpu/m6502/opcode/root.zig new file mode 100644 index 0000000..bc211fb --- /dev/null +++ b/src/cpu/m6502/opcode/root.zig @@ -0,0 +1,2 @@ +pub const nmos6502 = @import("nmos6502.zig"); +pub const wdc65c02 = @import("wdc65c02.zig"); diff --git a/src/cpu/m6502/opcode/wdc65c02.zig b/src/cpu/m6502/opcode/wdc65c02.zig new file mode 100644 index 0000000..fb2fe47 --- /dev/null +++ b/src/cpu/m6502/opcode/wdc65c02.zig @@ -0,0 +1,291 @@ +const Instruction = @import("../instruction.zig"); + +pub const instructions: [256]Instruction = .{ + // 0x00 - 0x0f + .{ .operation = .brk, .mode = .implied }, + .{ .operation = .ora, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .tsb, .mode = .zero_page }, + .{ .operation = .ora, .mode = .zero_page }, + .{ .operation = .asl, .mode = .zero_page }, + .{ .operation = .rmb0, .mode = .zero_page }, + .{ .operation = .php, .mode = .implied }, + .{ .operation = .ora, .mode = .immediate }, + .{ .operation = .asl, .mode = .accumulator }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .tsb, .mode = .absolute }, + .{ .operation = .ora, .mode = .absolute }, + .{ .operation = .asl, .mode = .absolute }, + .{ .operation = .bbr0, .mode = .zero_page_relative }, + + // 0x10 - 0x1f + .{ .operation = .bpl, .mode = .relative }, + .{ .operation = .ora, .mode = .indirect_indexed_y }, + .{ .operation = .ora, .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .trb, .mode = .zero_page }, + .{ .operation = .ora, .mode = .zero_page_x }, + .{ .operation = .asl, .mode = .zero_page_x }, + .{ .operation = .rmb1, .mode = .zero_page }, + .{ .operation = .clc, .mode = .implied }, + .{ .operation = .ora, .mode = .absolute_y }, + .{ .operation = .inc, .mode = .accumulator }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .trb, .mode = .absolute }, + .{ .operation = .ora, .mode = .absolute_x }, + .{ .operation = .asl, .mode = .absolute_x }, + .{ .operation = .bbr1, .mode = .zero_page_relative }, + + // 0x20 - 0x2f + .{ .operation = .jsr, .mode = .absolute }, + .{ .operation = .@"and", .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .bit, .mode = .zero_page }, + .{ .operation = .@"and", .mode = .zero_page }, + .{ .operation = .rol, .mode = .zero_page }, + .{ .operation = .rmb2, .mode = .zero_page }, + .{ .operation = .plp, .mode = .implied }, + .{ .operation = .@"and", .mode = .immediate }, + .{ .operation = .rol, .mode = .accumulator }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .bit, .mode = .absolute }, + .{ .operation = .@"and", .mode = .absolute }, + .{ .operation = .rol, .mode = .absolute }, + .{ .operation = .bbr2, .mode = .zero_page_relative }, + + // 0x30 - 0x3f + .{ .operation = .bmi, .mode = .relative }, + .{ .operation = .@"and", .mode = .indirect_indexed_y }, + .{ .operation = .@"and", .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .bit, .mode = .zero_page_x }, + .{ .operation = .@"and", .mode = .zero_page_x }, + .{ .operation = .rol, .mode = .zero_page_x }, + .{ .operation = .rmb3, .mode = .zero_page }, + .{ .operation = .sec, .mode = .implied }, + .{ .operation = .@"and", .mode = .absolute_y }, + .{ .operation = .dec, .mode = .accumulator }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .bit, .mode = .absolute_x }, + .{ .operation = .@"and", .mode = .absolute_x }, + .{ .operation = .rol, .mode = .absolute_x }, + .{ .operation = .bbr3, .mode = .zero_page_relative }, + + // 0x40 - 0x4f + .{ .operation = .rti, .mode = .implied }, + .{ .operation = .eor, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .nop, .mode = .zero_page }, + .{ .operation = .eor, .mode = .zero_page }, + .{ .operation = .lsr, .mode = .zero_page }, + .{ .operation = .rmb4, .mode = .zero_page }, + .{ .operation = .pha, .mode = .implied }, + .{ .operation = .eor, .mode = .immediate }, + .{ .operation = .lsr, .mode = .accumulator }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .jmp, .mode = .absolute }, + .{ .operation = .eor, .mode = .absolute }, + .{ .operation = .lsr, .mode = .absolute }, + .{ .operation = .bbr4, .mode = .zero_page_relative }, + + // 0x50 - 0x5f + .{ .operation = .bvc, .mode = .relative }, + .{ .operation = .eor, .mode = .indirect_indexed_y }, + .{ .operation = .eor, .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .eor, .mode = .zero_page_x }, + .{ .operation = .lsr, .mode = .zero_page_x }, + .{ .operation = .rmb5, .mode = .zero_page }, + .{ .operation = .cli, .mode = .implied }, + .{ .operation = .eor, .mode = .absolute_y }, + .{ .operation = .phy, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .nop, .mode = .absolute }, + .{ .operation = .eor, .mode = .absolute_x }, + .{ .operation = .lsr, .mode = .absolute_x }, + .{ .operation = .bbr5, .mode = .zero_page_relative }, + + // 0x60 - 0x6f + .{ .operation = .rts, .mode = .implied }, + .{ .operation = .adc, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .stz, .mode = .zero_page }, + .{ .operation = .adc, .mode = .zero_page }, + .{ .operation = .ror, .mode = .zero_page }, + .{ .operation = .rmb6, .mode = .zero_page }, + .{ .operation = .pla, .mode = .implied }, + .{ .operation = .adc, .mode = .immediate }, + .{ .operation = .ror, .mode = .accumulator }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .jmp, .mode = .indirect }, + .{ .operation = .adc, .mode = .absolute }, + .{ .operation = .ror, .mode = .absolute }, + .{ .operation = .bbr6, .mode = .zero_page_relative }, + + // 0x70 - 0x7f + .{ .operation = .bvs, .mode = .relative }, + .{ .operation = .adc, .mode = .indirect_indexed_y }, + .{ .operation = .adc, .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .stz, .mode = .zero_page_x }, + .{ .operation = .adc, .mode = .zero_page_x }, + .{ .operation = .ror, .mode = .zero_page_x }, + .{ .operation = .rmb7, .mode = .zero_page }, + .{ .operation = .sei, .mode = .implied }, + .{ .operation = .adc, .mode = .absolute_y }, + .{ .operation = .ply, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .jmp, .mode = .absolute_indexed_indirect }, + .{ .operation = .adc, .mode = .absolute_x }, + .{ .operation = .ror, .mode = .absolute_x }, + .{ .operation = .bbr7, .mode = .zero_page_relative }, + + // 0x80 - 0x8f + .{ .operation = .bra, .mode = .relative }, + .{ .operation = .sta, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .sty, .mode = .zero_page }, + .{ .operation = .sta, .mode = .zero_page }, + .{ .operation = .stx, .mode = .zero_page }, + .{ .operation = .smb0, .mode = .zero_page }, + .{ .operation = .dey, .mode = .implied }, + .{ .operation = .bit, .mode = .immediate }, + .{ .operation = .txa, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .sty, .mode = .absolute }, + .{ .operation = .sta, .mode = .absolute }, + .{ .operation = .stx, .mode = .absolute }, + .{ .operation = .bbs0, .mode = .zero_page_relative }, + + // 0x90 - 0x9f + .{ .operation = .bcc, .mode = .relative }, + .{ .operation = .sta, .mode = .indirect_indexed_y }, + .{ .operation = .sta, .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .sty, .mode = .zero_page_x }, + .{ .operation = .sta, .mode = .zero_page_x }, + .{ .operation = .stx, .mode = .zero_page_y }, + .{ .operation = .smb1, .mode = .zero_page }, + .{ .operation = .tya, .mode = .implied }, + .{ .operation = .sta, .mode = .absolute_y }, + .{ .operation = .txs, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .stz, .mode = .absolute }, + .{ .operation = .sta, .mode = .absolute_x }, + .{ .operation = .stz, .mode = .absolute_x }, + .{ .operation = .bbs1, .mode = .zero_page_relative }, + + // 0xa0 - 0xaf + .{ .operation = .ldy, .mode = .immediate }, + .{ .operation = .lda, .mode = .indexed_indirect_x }, + .{ .operation = .ldx, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .ldy, .mode = .zero_page }, + .{ .operation = .lda, .mode = .zero_page }, + .{ .operation = .ldx, .mode = .zero_page }, + .{ .operation = .smb2, .mode = .zero_page }, + .{ .operation = .tay, .mode = .implied }, + .{ .operation = .lda, .mode = .immediate }, + .{ .operation = .tax, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .ldy, .mode = .absolute }, + .{ .operation = .lda, .mode = .absolute }, + .{ .operation = .ldx, .mode = .absolute }, + .{ .operation = .bbs2, .mode = .zero_page_relative }, + + // 0xb0 - 0xbf + .{ .operation = .bcs, .mode = .relative }, + .{ .operation = .lda, .mode = .indirect_indexed_y }, + .{ .operation = .lda, .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .ldy, .mode = .zero_page_x }, + .{ .operation = .lda, .mode = .zero_page_x }, + .{ .operation = .ldx, .mode = .zero_page_y }, + .{ .operation = .smb3, .mode = .zero_page }, + .{ .operation = .clv, .mode = .implied }, + .{ .operation = .lda, .mode = .absolute_y }, + .{ .operation = .tsx, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .ldy, .mode = .absolute_x }, + .{ .operation = .lda, .mode = .absolute_x }, + .{ .operation = .ldx, .mode = .absolute_y }, + .{ .operation = .bbs3, .mode = .zero_page_relative }, + + // 0xc0 - 0xcf + .{ .operation = .cpy, .mode = .immediate }, + .{ .operation = .cmp, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .cpy, .mode = .zero_page }, + .{ .operation = .cmp, .mode = .zero_page }, + .{ .operation = .dec, .mode = .zero_page }, + .{ .operation = .smb4, .mode = .zero_page }, + .{ .operation = .iny, .mode = .implied }, + .{ .operation = .cmp, .mode = .immediate }, + .{ .operation = .dex, .mode = .implied }, + .{ .operation = .wai, .mode = .implied }, + .{ .operation = .cpy, .mode = .absolute }, + .{ .operation = .cmp, .mode = .absolute }, + .{ .operation = .dec, .mode = .absolute }, + .{ .operation = .bbs4, .mode = .zero_page_relative }, + + // 0xd0 - 0xdf + .{ .operation = .bne, .mode = .relative }, + .{ .operation = .cmp, .mode = .indirect_indexed_y }, + .{ .operation = .cmp, .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .cmp, .mode = .zero_page_x }, + .{ .operation = .dec, .mode = .zero_page_x }, + .{ .operation = .smb5, .mode = .zero_page }, + .{ .operation = .cld, .mode = .implied }, + .{ .operation = .cmp, .mode = .absolute_y }, + .{ .operation = .phx, .mode = .implied }, + .{ .operation = .stp, .mode = .implied }, + .{ .operation = .nop, .mode = .absolute }, + .{ .operation = .cmp, .mode = .absolute_x }, + .{ .operation = .dec, .mode = .absolute_x }, + .{ .operation = .bbs5, .mode = .zero_page_relative }, + + // 0xe0 - 0xef + .{ .operation = .cpx, .mode = .immediate }, + .{ .operation = .sbc, .mode = .indexed_indirect_x }, + .{ .operation = .nop, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .cpx, .mode = .zero_page }, + .{ .operation = .sbc, .mode = .zero_page }, + .{ .operation = .inc, .mode = .zero_page }, + .{ .operation = .smb6, .mode = .zero_page }, + .{ .operation = .inx, .mode = .implied }, + .{ .operation = .sbc, .mode = .immediate }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .cpx, .mode = .absolute }, + .{ .operation = .sbc, .mode = .absolute }, + .{ .operation = .inc, .mode = .absolute }, + .{ .operation = .bbs6, .mode = .zero_page_relative }, + + // 0xf0 - 0xff + .{ .operation = .beq, .mode = .relative }, + .{ .operation = .sbc, .mode = .indirect_indexed_y }, + .{ .operation = .sbc, .mode = .zero_page_indirect }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .nop, .mode = .zero_page_x }, + .{ .operation = .sbc, .mode = .zero_page_x }, + .{ .operation = .inc, .mode = .zero_page_x }, + .{ .operation = .smb7, .mode = .zero_page }, + .{ .operation = .sed, .mode = .implied }, + .{ .operation = .sbc, .mode = .absolute_y }, + .{ .operation = .plx, .mode = .implied }, + .{ .operation = .nop, .mode = .implied }, + .{ .operation = .nop, .mode = .absolute }, + .{ .operation = .sbc, .mode = .absolute_x }, + .{ .operation = .inc, .mode = .absolute_x }, + .{ .operation = .bbs7, .mode = .zero_page_relative }, +}; diff --git a/src/cpu/m6502/types.zig b/src/cpu/m6502/types.zig index 4ca5a34..e71cb24 100644 --- a/src/cpu/m6502/types.zig +++ b/src/cpu/m6502/types.zig @@ -1,3 +1,6 @@ +const _opcode = @import("opcode/root.zig"); +const Instruction = @import("instruction.zig"); + pub const Status = packed struct(u8) { carry: bool = false, zero: bool = false, @@ -30,13 +33,6 @@ pub const Registers = struct { sp: u8 = 0xfd, pc: u16 = 0, status: Status = .{}, - - pub fn dumpRegisters(self: Registers) void { - const std = @import("std"); - std.debug.print("A:{X:02} X:{X:02} Y:{X:02} SP:{X:02} PC:{X:04}\n", .{ - self.a, self.x, self.y, self.sp, self.pc, - }); - } }; pub const Model = enum { @@ -49,22 +45,26 @@ pub const Model = enum { r65c02, pub fn features(self: Model) CpuFeatures { + // group variants sharing exact feature sets to eliminate duplication return switch (self) { .mos6502, .mos6507, .mos6510 => .{ .decimal_arithmetic = true, .undocumented_opcodes = true, .indirect_jump_bug = true, }, + .ricoh2a03, .ricoh2a07 => .{ .undocumented_opcodes = true, .indirect_jump_bug = true, }, + .wdc65c02 => .{ .decimal_arithmetic = true, .cmos_instructions = true, .fixed_indirect_jump = true, .wait_and_stop = true, }, + .r65c02 => .{ .decimal_arithmetic = true, .cmos_instructions = true, @@ -72,6 +72,24 @@ pub const Model = enum { }, }; } + + pub fn instructionTable(self: Model) *const [256]Instruction { + return switch (self) { + .mos6502, .mos6507, .mos6510, .ricoh2a03, .ricoh2a07 => &_opcode.nmos6502.instructions, + .wdc65c02, .r65c02 => &_opcode.wdc65c02.instructions, + }; + } + + pub fn instruction(self: Model, opcode: u8) Instruction { + return self.instructionTable()[opcode]; + } + + pub fn addressMask(self: Model) u16 { + return switch (self) { + .mos6507 => 0x1fff, + else => 0xffff, + }; + } }; pub const CpuFeatures = packed struct(u8) { @@ -82,5 +100,33 @@ pub const CpuFeatures = packed struct(u8) { fixed_indirect_jump: bool = false, wait_and_stop: bool = false, + // remaining bit fields reserved for packed struct byte layout _reserved: u2 = 0, }; + +test "CPU Model Features & Opcode Map Verification" { + const testing = @import("std").testing; + + // MOS 6502 family + try testing.expect(Model.mos6502.features().decimal_arithmetic); + try testing.expect(Model.mos6507.features().decimal_arithmetic); + try testing.expect(Model.mos6510.features().decimal_arithmetic); + try testing.expect(Model.mos6502.features().undocumented_opcodes); + + // Ricoh family (No BCD) + try testing.expect(!Model.ricoh2a03.features().decimal_arithmetic); + try testing.expect(!Model.ricoh2a07.features().decimal_arithmetic); + try testing.expect(Model.ricoh2a07.features().undocumented_opcodes); + try testing.expect(Model.ricoh2a07.features().indirect_jump_bug); + + // CMOS family + try testing.expect(Model.wdc65c02.features().cmos_instructions); + try testing.expect(Model.r65c02.features().cmos_instructions); + try testing.expect(Model.wdc65c02.features().wait_and_stop); + try testing.expect(!Model.r65c02.features().wait_and_stop); + + // Opcode mapping sanity checks + try testing.expectEqual(Model.mos6502.instruction(0xEA).operation, .nop); + try testing.expectEqual(Model.ricoh2a07.instruction(0xEA).operation, .nop); + try testing.expectEqual(Model.r65c02.instruction(0x80).operation, .bra); +}