mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-20 09:02:32 +00:00
133 lines
6.3 KiB
Odin
133 lines
6.3 KiB
Odin
package rexcode_mos6502
|
|
|
|
// =============================================================================
|
|
// MOS 6502 OPERANDS
|
|
// =============================================================================
|
|
//
|
|
// The 6502 has more *addressing modes* than it has registers. The
|
|
// addressing mode is encoded into the operand as part of the Memory
|
|
// type so the matcher can dispatch to the correct opcode form for a
|
|
// given mnemonic. Layout:
|
|
//
|
|
// LDA #$12 IMMEDIATE imm = 0x12
|
|
// LDA $12 MEMORY {mode = ZP, address = 0x0012}
|
|
// LDA $12,X MEMORY {mode = ZP_X, address = 0x0012}
|
|
// LDA $1234 MEMORY {mode = ABS, address = 0x1234}
|
|
// LDA $1234,X MEMORY {mode = ABS_X, address = 0x1234}
|
|
// LDA $1234,Y MEMORY {mode = ABS_Y, address = 0x1234}
|
|
// LDA ($12,X) MEMORY {mode = IND_X, address = 0x0012}
|
|
// LDA ($12),Y MEMORY {mode = IND_Y, address = 0x0012}
|
|
// LDA ($12) MEMORY {mode = IND_ZP, address = 0x0012} -- 65C02
|
|
// JMP ($1234) MEMORY {mode = IND, address = 0x1234}
|
|
// JMP ($1234,X) MEMORY {mode = IND_ABS_X, address = 0x1234} -- 65C02
|
|
// BEQ label RELATIVE relative = label_id
|
|
// ROL A REGISTER reg = A
|
|
|
|
Operand_Kind :: enum u8 {
|
|
NONE,
|
|
REGISTER, // mostly used for `A` in `ROL A` (implicit; rarely needed)
|
|
IMMEDIATE,
|
|
MEMORY,
|
|
RELATIVE, // PC-relative target (label or raw byte offset)
|
|
}
|
|
|
|
Address_Mode :: enum u8 {
|
|
ZP, // $nn
|
|
ZP_X, // $nn,X
|
|
ZP_Y, // $nn,Y
|
|
ABS, // $nnnn
|
|
ABS_X, // $nnnn,X
|
|
ABS_Y, // $nnnn,Y
|
|
IND, // ($nnnn) -- JMP only
|
|
IND_X, // ($nn,X)
|
|
IND_Y, // ($nn),Y
|
|
IND_ZP, // ($nn) -- 65C02
|
|
IND_ABS_X, // ($nnnn,X) -- 65C02 JMP only
|
|
}
|
|
|
|
Memory :: struct #packed {
|
|
address: u16,
|
|
mode: Address_Mode,
|
|
_: u8,
|
|
}
|
|
#assert(size_of(Memory) == 4)
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Memory constructors (one per addressing mode)
|
|
// -----------------------------------------------------------------------------
|
|
|
|
mem_zp :: #force_inline proc "contextless" (addr: u8) -> Memory { return Memory{address = u16(addr), mode = .ZP } }
|
|
mem_zp_x :: #force_inline proc "contextless" (addr: u8) -> Memory { return Memory{address = u16(addr), mode = .ZP_X } }
|
|
mem_zp_y :: #force_inline proc "contextless" (addr: u8) -> Memory { return Memory{address = u16(addr), mode = .ZP_Y } }
|
|
mem_abs :: #force_inline proc "contextless" (addr: u16) -> Memory { return Memory{address = addr, mode = .ABS } }
|
|
mem_abs_x :: #force_inline proc "contextless" (addr: u16) -> Memory { return Memory{address = addr, mode = .ABS_X } }
|
|
mem_abs_y :: #force_inline proc "contextless" (addr: u16) -> Memory { return Memory{address = addr, mode = .ABS_Y } }
|
|
mem_ind :: #force_inline proc "contextless" (addr: u16) -> Memory { return Memory{address = addr, mode = .IND } }
|
|
mem_ind_x :: #force_inline proc "contextless" (addr: u8) -> Memory { return Memory{address = u16(addr), mode = .IND_X } }
|
|
mem_ind_y :: #force_inline proc "contextless" (addr: u8) -> Memory { return Memory{address = u16(addr), mode = .IND_Y } }
|
|
mem_ind_zp :: #force_inline proc "contextless" (addr: u8) -> Memory { return Memory{address = u16(addr), mode = .IND_ZP } }
|
|
mem_ind_abs_x :: #force_inline proc "contextless" (addr: u16) -> Memory { return Memory{address = addr, mode = .IND_ABS_X } }
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Operand: kind-tagged union, 16 bytes
|
|
// -----------------------------------------------------------------------------
|
|
|
|
Operand :: struct #packed {
|
|
using _: struct #raw_union {
|
|
reg: Register, // 2 bytes
|
|
mem: Memory, // 4 bytes
|
|
immediate: i64, // 8 bytes
|
|
relative: i64, // 8 bytes (label id pre-resolution; byte offset post)
|
|
},
|
|
kind: Operand_Kind, // 1
|
|
size: u8, // 1
|
|
_: [6]u8, // 6
|
|
}
|
|
#assert(size_of(Operand) == 16)
|
|
|
|
// Generic constructors -------------------------------------------------------
|
|
|
|
op_reg :: #force_inline proc "contextless" (r: Register) -> Operand {
|
|
return Operand{reg = r, kind = .REGISTER, size = 1}
|
|
}
|
|
|
|
op_imm8 :: #force_inline proc "contextless" (v: i64) -> Operand {
|
|
return Operand{immediate = v, kind = .IMMEDIATE, size = 1}
|
|
}
|
|
|
|
op_imm16 :: #force_inline proc "contextless" (v: i64) -> Operand {
|
|
return Operand{immediate = v, kind = .IMMEDIATE, size = 2}
|
|
}
|
|
|
|
op_mem :: #force_inline proc "contextless" (m: Memory) -> Operand {
|
|
size: u8 = 2
|
|
switch m.mode {
|
|
case .ZP, .ZP_X, .ZP_Y, .IND_X, .IND_Y, .IND_ZP:
|
|
size = 1
|
|
case .ABS, .ABS_X, .ABS_Y, .IND, .IND_ABS_X:
|
|
size = 2
|
|
}
|
|
return Operand{mem = m, kind = .MEMORY, size = size}
|
|
}
|
|
|
|
op_label :: #force_inline proc "contextless" (label_id: u32, size: u8 = 1) -> Operand {
|
|
return Operand{relative = i64(label_id), kind = .RELATIVE, size = size}
|
|
}
|
|
|
|
op_rel_offset :: #force_inline proc "contextless" (offset: i64) -> Operand {
|
|
return Operand{relative = offset, kind = .RELATIVE, size = 1}
|
|
}
|
|
|
|
// Address-mode-named operand helpers (call site reads like asm).
|
|
op_zp :: #force_inline proc "contextless" (a: u8) -> Operand { return op_mem(mem_zp(a)) }
|
|
op_zp_x :: #force_inline proc "contextless" (a: u8) -> Operand { return op_mem(mem_zp_x(a)) }
|
|
op_zp_y :: #force_inline proc "contextless" (a: u8) -> Operand { return op_mem(mem_zp_y(a)) }
|
|
op_abs :: #force_inline proc "contextless" (a: u16) -> Operand { return op_mem(mem_abs(a)) }
|
|
op_abs_x :: #force_inline proc "contextless" (a: u16) -> Operand { return op_mem(mem_abs_x(a)) }
|
|
op_abs_y :: #force_inline proc "contextless" (a: u16) -> Operand { return op_mem(mem_abs_y(a)) }
|
|
op_ind :: #force_inline proc "contextless" (a: u16) -> Operand { return op_mem(mem_ind(a)) }
|
|
op_ind_x :: #force_inline proc "contextless" (a: u8) -> Operand { return op_mem(mem_ind_x(a)) }
|
|
op_ind_y :: #force_inline proc "contextless" (a: u8) -> Operand { return op_mem(mem_ind_y(a)) }
|
|
op_ind_zp :: #force_inline proc "contextless" (a: u8) -> Operand { return op_mem(mem_ind_zp(a)) }
|
|
op_ind_abs_x :: #force_inline proc "contextless" (a: u16) -> Operand { return op_mem(mem_ind_abs_x(a)) }
|