Files
Odin/core/rexcode/mips/instructions.odin
2026-06-14 21:54:24 +01:00

118 lines
4.9 KiB
Odin

package rexcode_mips
// =============================================================================
// INSTRUCTION
// =============================================================================
Instruction_Flags :: bit_field u8 {
// Reserved for per-instance hints (e.g. taken-prediction, atomic
// suffixes, addressing-mode overrides for future extensions). The
// base ISA needs none of these at the instance level -- everything
// semantic is already in the mnemonic.
_: u8 | 8,
}
Instruction :: struct #packed {
ops: [4]Operand `fmt:"v,operand_count"`, // 64 bytes
mnemonic: Mnemonic, // 2 bytes
operand_count: u8, // 1 byte
flags: Instruction_Flags, // 1 byte
length: u8, // 1 byte — always 4 for now; will be 2 for future μMIPS / MIPS16e
_: [3]u8, // 3 bytes
}
#assert(size_of(Instruction) == 72)
// =============================================================================
// Builders (mirror x86's `inst_*` shapes; per the cross-arch contract)
// =============================================================================
@(require_results)
inst_none :: #force_inline proc "contextless" (m: Mnemonic) -> Instruction {
return Instruction{mnemonic = m, operand_count = 0, length = 4}
}
@(require_results)
inst_r :: #force_inline proc "contextless" (m: Mnemonic, a: Register) -> Instruction {
return Instruction{mnemonic = m, operand_count = 1, length = 4,
ops = {op_reg(a), {}, {}, {}}}
}
@(require_results)
inst_r_r :: #force_inline proc "contextless" (m: Mnemonic, dst, src: Register) -> Instruction {
return Instruction{mnemonic = m, operand_count = 2, length = 4,
ops = {op_reg(dst), op_reg(src), {}, {}}}
}
@(require_results)
inst_r_r_r :: #force_inline proc "contextless" (m: Mnemonic, dst, s1, s2: Register) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(dst), op_reg(s1), op_reg(s2), {}}}
}
// MIPS three-operand immediate: $rt, $rs, imm (e.g. ADDI rt, rs, imm).
@(require_results)
inst_r_r_i :: #force_inline proc "contextless" (m: Mnemonic, rt, rs: Register, imm: i64) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rt), op_reg(rs), op_imm(imm, 2), {}}}
}
// Two-operand immediate (e.g. LUI rt, imm; LI variants).
@(require_results)
inst_r_i :: #force_inline proc "contextless" (m: Mnemonic, rt: Register, imm: i64) -> Instruction {
return Instruction{mnemonic = m, operand_count = 2, length = 4,
ops = {op_reg(rt), op_imm(imm, 2), {}, {}}}
}
// Load/store: reg + memory (LW $rt, disp($rs)).
@(require_results)
inst_r_m :: #force_inline proc "contextless" (m: Mnemonic, r: Register, mm: Memory, size: u8 = 4) -> Instruction {
return Instruction{mnemonic = m, operand_count = 2, length = 4,
ops = {op_reg(r), op_mem(mm, size), {}, {}}}
}
// Shift by immediate: $rd, $rt, shamt (SLL/SRL/SRA).
@(require_results)
inst_shift :: #force_inline proc "contextless" (m: Mnemonic, rd, rt: Register, shamt: u8) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rd), op_reg(rt), op_imm(i64(shamt), 1), {}}}
}
// Variable shift: $rd, $rt, $rs (SLLV/SRLV/SRAV).
inst_shift_v :: inst_r_r_r
// Single-operand: JR $rs, JALR $rd (with implicit RA), MFHI/MFLO/MTHI/MTLO,
// SYSCALL/BREAK take an imm code so they use inst_i below.
@(require_results)
inst_i :: #force_inline proc "contextless" (m: Mnemonic, imm: i64) -> Instruction {
return Instruction{mnemonic = m, operand_count = 1, length = 4,
ops = {op_imm(imm, 4), {}, {}, {}}}
}
// Two-reg branch + label (BEQ/BNE: rs, rt, target).
@(require_results)
inst_branch2 :: #force_inline proc "contextless" (m: Mnemonic, rs, rt: Register, label_id: u32) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rs), op_reg(rt), op_label(label_id), {}}}
}
// One-reg branch + label (BLTZ/BGEZ/BLEZ/BGTZ/BLTZAL/BGEZAL/...).
@(require_results)
inst_branch1 :: #force_inline proc "contextless" (m: Mnemonic, rs: Register, label_id: u32) -> Instruction {
return Instruction{mnemonic = m, operand_count = 2, length = 4,
ops = {op_reg(rs), op_label(label_id), {}, {}}}
}
// J-type jump (J/JAL: target only).
@(require_results)
inst_jump :: #force_inline proc "contextless" (m: Mnemonic, label_id: u32) -> Instruction {
return Instruction{mnemonic = m, operand_count = 1, length = 4,
ops = {op_label(label_id), {}, {}, {}}}
}
// COP0 move with explicit selector: MFC0 $rt, $rd, sel (R2+).
@(require_results)
inst_cp0_sel :: #force_inline proc "contextless" (m: Mnemonic, rt, rd: Register, sel: u8) -> Instruction {
return Instruction{mnemonic = m, operand_count = 3, length = 4,
ops = {op_reg(rt), op_reg(rd), op_imm(i64(sel), 1), {}}}
}