Files
Odin/core/rexcode/isa/mos65816/operands.odin
Brendan Punsky 95df04fbe1 rexcode: re-house ISA packages under core:rexcode/isa/<arch>
Move all ten ISA packages (x86, arm32, arm64, mips, riscv, ppc, ppc_vle,
rsp, mos6502, mos65816) from core/rexcode/<arch> to core/rexcode/isa/<arch>,
so the import pattern is now `import "core:rexcode/isa/x86"`. The shared
core stays at core:rexcode/isa.

Mechanical: relative `import "../isa"` / "../../isa" -> absolute
"core:rexcode/isa" (the only path that survives the move; the "../" and
"../.." self/generated imports move with their packages). build.lua now
builds paths as <root>/isa/<name>; stale `cd <arch>` hints in the verify
tools and the doc.odin paths updated.

WASM stays at core/rexcode/wasm for now -- it is an IR, not an ISA, and
will move under the forthcoming core:rexcode/ir once that layer lands.

All 10 arches gen/builders/check/test green; import core:rexcode/isa/x86
verified working; wasm still compiles.
2026-06-18 19:03:27 -04:00

125 lines
6.0 KiB
Odin

// rexcode · Brendan Punsky (dotbmp@github), original author
package rexcode_mos65816
// =============================================================================
// W65C816S OPERANDS
// =============================================================================
//
// 24-bit address space with banked addressing. Address modes:
//
// Direct Page (formerly zero-page; D register makes it movable)
// DP $nn
// DP_X $nn,X
// DP_Y $nn,Y
// DP_IND ($nn) -- 16-bit indirect via DP, uses DBR
// DP_IND_X ($nn,X)
// DP_IND_Y ($nn),Y
// DP_IND_LONG [$nn] -- 24-bit indirect (65816 NEW)
// DP_IND_LONG_Y [$nn],Y -- 24-bit indirect indexed (65816 NEW)
//
// Absolute (16-bit within DBR / PBR)
// ABS $nnnn
// ABS_X $nnnn,X
// ABS_Y $nnnn,Y
// ABS_IND ($nnnn) -- JMP only (PBR)
// ABS_IND_LONG [$nnnn] -- JML only (24-bit target)
// ABS_IND_X ($nnnn,X) -- JMP/JSR
//
// Long (24-bit; bypasses DBR)
// LONG $nnnnnn -- 65816 NEW
// LONG_X $nnnnnn,X -- 65816 NEW
//
// Stack relative
// SR $nn,S -- 65816 NEW
// SR_IND_Y ($nn,S),Y -- 65816 NEW
//
// Branch offsets (REL, REL_LONG) and block-move bank pairs (MVN/MVP) are
// handled as separate Operand_Type values; they don't ride in Memory.
Operand_Kind :: enum u8 {
NONE,
REGISTER,
IMMEDIATE,
MEMORY,
RELATIVE,
}
Address_Mode :: enum u8 {
DP, DP_X, DP_Y,
DP_IND, DP_IND_X, DP_IND_Y,
DP_IND_LONG, DP_IND_LONG_Y,
ABS, ABS_X, ABS_Y,
ABS_IND, ABS_IND_LONG, ABS_IND_X,
LONG, LONG_X,
SR, SR_IND_Y,
}
// 24-bit address packed alongside the mode in 4 bytes.
Memory :: bit_field u32 {
address: u32 | 24,
mode: Address_Mode | 8,
}
#assert(size_of(Memory) == 4)
@(require_results) mem_dp :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP } }
@(require_results) mem_dp_x :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP_X } }
@(require_results) mem_dp_y :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP_Y } }
@(require_results) mem_dp_ind :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP_IND } }
@(require_results) mem_dp_ind_x :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP_IND_X } }
@(require_results) mem_dp_ind_y :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP_IND_Y } }
@(require_results) mem_dp_ind_long :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP_IND_LONG } }
@(require_results) mem_dp_ind_long_y:: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .DP_IND_LONG_Y } }
@(require_results) mem_abs :: #force_inline proc "contextless" (a: u16) -> Memory { return Memory{address = u32(a), mode = .ABS } }
@(require_results) mem_abs_x :: #force_inline proc "contextless" (a: u16) -> Memory { return Memory{address = u32(a), mode = .ABS_X } }
@(require_results) mem_abs_y :: #force_inline proc "contextless" (a: u16) -> Memory { return Memory{address = u32(a), mode = .ABS_Y } }
@(require_results) mem_abs_ind :: #force_inline proc "contextless" (a: u16) -> Memory { return Memory{address = u32(a), mode = .ABS_IND } }
@(require_results) mem_abs_ind_long :: #force_inline proc "contextless" (a: u16) -> Memory { return Memory{address = u32(a), mode = .ABS_IND_LONG } }
@(require_results) mem_abs_ind_x :: #force_inline proc "contextless" (a: u16) -> Memory { return Memory{address = u32(a), mode = .ABS_IND_X } }
@(require_results) mem_long :: #force_inline proc "contextless" (a: u32) -> Memory { return Memory{address = a & 0xFFFFFF, mode = .LONG } }
@(require_results) mem_long_x :: #force_inline proc "contextless" (a: u32) -> Memory { return Memory{address = a & 0xFFFFFF, mode = .LONG_X } }
@(require_results) mem_sr :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .SR } }
@(require_results) mem_sr_ind_y :: #force_inline proc "contextless" (a: u8) -> Memory { return Memory{address = u32(a), mode = .SR_IND_Y } }
Operand :: struct #packed {
using _: struct #raw_union {
reg: Register,
mem: Memory,
immediate: i64,
relative: i64,
},
kind: Operand_Kind,
size: u8,
}
#assert(size_of(Operand) == 10)
@(require_results) op_reg :: #force_inline proc "contextless" (r: Register) -> Operand { return Operand{reg = r, kind = .REGISTER, size = 1} }
@(require_results) op_imm8 :: #force_inline proc "contextless" (v: i64) -> Operand { return Operand{immediate = v, kind = .IMMEDIATE, size = 1} }
@(require_results) op_imm16 :: #force_inline proc "contextless" (v: i64) -> Operand { return Operand{immediate = v, kind = .IMMEDIATE, size = 2} }
@(require_results)
op_mem :: #force_inline proc "contextless" (m: Memory) -> Operand {
// Operand size = width of the address literal as encoded.
size: u8 = 2
switch m.mode {
case .DP, .DP_X, .DP_Y, .DP_IND, .DP_IND_X, .DP_IND_Y,
.DP_IND_LONG, .DP_IND_LONG_Y, .SR, .SR_IND_Y:
size = 1
case .ABS, .ABS_X, .ABS_Y, .ABS_IND, .ABS_IND_LONG, .ABS_IND_X:
size = 2
case .LONG, .LONG_X:
size = 3
}
return Operand{mem = m, kind = .MEMORY, size = size}
}
@(require_results)
op_label :: #force_inline proc "contextless" (label_id: u32, size: u8 = 1) -> Operand {
return Operand{relative = i64(label_id), kind = .RELATIVE, size = size}
}
@(require_results)
op_rel_offset :: #force_inline proc "contextless" (off: i64) -> Operand {
return Operand{relative = off, kind = .RELATIVE, size = 1}
}