common: update processor code to use new arg structs

This commit is contained in:
2025-06-15 12:21:35 +01:00
parent e52093f9cd
commit c837876960
2 changed files with 129 additions and 104 deletions
+42 -14
View File
@@ -53,7 +53,7 @@ pub enum Register {
Idr, Idr,
Mmr, Mmr,
Zero, Zero,
None, NoReg,
// system registers - can't be written to by instructions. // system registers - can't be written to by instructions.
Mar, Mar,
@@ -63,6 +63,12 @@ pub enum Register {
Pcx, Pcx,
} }
impl Default for Register {
fn default() -> Self {
Register::NoReg
}
}
#[derive(Debug)] #[derive(Debug)]
/// Error type for parsing register numbers. /// Error type for parsing register numbers.
pub enum RegisterParseError { pub enum RegisterParseError {
@@ -112,7 +118,7 @@ impl TryFrom<u8> for Register {
0x14 => Self::Idr, 0x14 => Self::Idr,
0x15 => Self::Mmr, 0x15 => Self::Mmr,
0x16 => Self::Zero, 0x16 => Self::Zero,
0x17 => Self::None, 0x17 => Self::NoReg,
_ => unreachable!("This is already checked for in top `if` branch."), _ => unreachable!("This is already checked for in top `if` branch."),
}) })
} }
@@ -144,7 +150,7 @@ impl std::fmt::Display for Register {
Self::Idr => write!(f, "Idr"), Self::Idr => write!(f, "Idr"),
Self::Mmr => write!(f, "Mmr"), Self::Mmr => write!(f, "Mmr"),
Self::Zero => write!(f, "Zero"), Self::Zero => write!(f, "Zero"),
Self::None => write!(f, "None"), Self::NoReg => write!(f, "None"),
Self::Mar => write!(f, "Mar"), Self::Mar => write!(f, "Mar"),
Self::Mdr => write!(f, "Mdr"), Self::Mdr => write!(f, "Mdr"),
Self::Sts => write!(f, "Sts"), Self::Sts => write!(f, "Sts"),
@@ -154,26 +160,48 @@ impl std::fmt::Display for Register {
} }
} }
/// Used by instructions with no arguments. // /// Used by instructions with no arguments.
struct NoArgs {} // struct NoArgs {}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
/// Used by instructions with 2 registers and an immediate argument. /// Used by instructions with 2 registers and an immediate argument.
struct ITypeArgs { pub struct ITypeArgs {
immediate: u16, pub immediate: u16,
r1: Register, pub r1: Register,
/// May not actually be used by some instructions taking an immediate e.g. LUI. This is solved by making the constructor take Options. /// May not actually be used by some instructions taking an immediate e.g. LUI. This is solved by making the constructor take Options.
r2: Register, pub r2: Register,
} }
/// Used by instructions not using immediates (besides 5 bit shift values). /// Used by instructions not using immediates (besides 5 bit shift values).
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
struct RTypeArgs { pub struct RTypeArgs {
sr1: Register, pub sr1: Register,
sr2: Register, pub sr2: Register,
dr: Register, pub dr: Register,
/// 5 bit shift amount. /// 5 bit shift amount.
shamt: u8, pub shamt: u8,
}
impl RTypeArgs {
/// Args set to None will be replaced with 0 or [Register::NoReg] depending on their type.
fn new(
sr1: Option<Register>,
sr2: Option<Register>,
dr: Option<Register>,
shamt: Option<u8>,
) -> Self {
let sr1 = sr1.unwrap_or_default();
let shamt = shamt.unwrap_or_default();
let sr2 = sr2.unwrap_or_default();
let dr = dr.unwrap_or_default();
Self {
sr1,
sr2,
dr,
shamt,
}
}
} }
/// TODO: Turn argument tuples into simple structs like `TwoReg`, `TwoRegAndOff` /// TODO: Turn argument tuples into simple structs like `TwoReg`, `TwoRegAndOff`
+87 -90
View File
@@ -156,188 +156,185 @@ impl Executable for Instruction {
// No operation - a blank line. // No operation - a blank line.
Self::Nop => {} Self::Nop => {}
// Copies from SrcReg to DestReg. // Copies from SrcReg to a.drReg.
Self::Mov(src, dest) => { Self::Mov(a) => {
*cpu.reg(dest) = cpu.get(src); *cpu.reg(a.dr) = cpu.get(a.sr1);
} }
// Copies from SrcReg to DestReg, sign extending the value to take up a full word. // Copies from SrcReg to a.drReg, sign extending the value to take up a full word.
Self::MovSigned(src, dest) => { Self::MovSigned(a) => {
*cpu.reg(dest) = sign_extend(cpu.get(src)); *cpu.reg(a.dr) = sign_extend(cpu.get(a.sr1));
} }
// Loads a byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned. // Loads a byte from memory address (base + offset) into a.drReg. The effective address must be byte-aligned.
Self::LoadByte(base, offset, dest) => { Self::LoadByte(a) => {
*cpu.reg(dest) = u32::from(cpu.memory.read_byte(cpu.get(base) + u32::from(offset))); *cpu.reg(a.r2) =
u32::from(cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)));
} }
// Loads a sign-extended byte from memory address (base + offset) into DestReg. The effective address must be byte-aligned. // Loads a sign-extended byte from memory address (base + offset) into a.drReg. The effective address must be byte-aligned.
Self::LoadByteSigned(base, offset, dest) => { Self::LoadByteSigned(a) => {
*cpu.reg(dest) = sign_extend(u32::from( *cpu.reg(a.r2) = sign_extend(u32::from(
cpu.memory.read_byte(cpu.get(base) + u32::from(offset)), cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)),
)); ));
} }
// Loads a half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned. // Loads a half-word from memory address (base + offset) into a.drReg. The effective address must be 2-byte-aligned.
Self::LoadHalfword(base, offset, dest) => { Self::LoadHalfword(a) => {
// we read an entire word, then right shift so we only get the first half of the word // we read an entire word, then right shift so we only get the first half of the word
*cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + u32::from(offset)) >> 16; *cpu.reg(a.r2) = cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16;
} }
// Loads a sign-extended half-word from memory address (base + offset) into DestReg. The effective address must be 2-byte-aligned. // Loads a sign-extended half-word from memory address (base + offset) into a.drReg. The effective address must be 2-byte-aligned.
Self::LoadHalfwordSigned(base, offset, dest) => { Self::LoadHalfwordSigned(a) => {
*cpu.reg(dest) = *cpu.reg(a.r2) =
sign_extend(cpu.memory.read_word(cpu.get(base) + u32::from(offset)) >> 16); sign_extend(cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16);
} }
// Loads a word from memory address (base + offset) into DestReg. The effective address must be 4-byte-aligned. // Loads a word from memory address (base + offset) into a.drReg. The effective address must be 4-byte-aligned.
Self::LoadWord(base, offset, dest) => { Self::LoadWord(a) => {
*cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + u32::from(offset)); *cpu.reg(a.r2) = cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate));
} }
// Stores a byte from SrcReg in memory address (base + offset) The effective address must be byte-aligned. // Stores a byte from SrcReg in memory address (base + offset) The effective address must be byte-aligned.
Self::StoreByte(src, offset, base) => { Self::StoreByte(a) => {
cpu.memory cpu.memory
.write_byte(cpu.get(base) + u32::from(offset), cpu.get(src) as u8); .write_byte(cpu.get(a.r1) + u32::from(a.immediate), cpu.get(a.r2) as u8);
} }
// Stores a half-word from SrcReg in memory address (base + offset) The effective address must be 2-byte-aligned. // Stores a half-word from SrcReg in memory address (base + offset) The effective address must be 2-byte-aligned.
Self::StoreHalfword(src, offset, base) => { Self::StoreHalfword(a) => {
// split the value into bytes and then write two bytes // split the value into bytes and then write two bytes
let bytes = (cpu.get(src) as u16).to_be_bytes(); let bytes = (cpu.get(a.r1) as u16).to_be_bytes();
cpu.memory cpu.memory
.write_byte(cpu.get(base) + u32::from(offset), bytes[0]); .write_byte(cpu.get(a.r1) + u32::from(a.immediate), bytes[0]);
cpu.memory cpu.memory
.write_byte(cpu.get(base) + u32::from(offset) + 1, bytes[1]); .write_byte(cpu.get(a.r1) + u32::from(a.immediate) + 1, bytes[1]);
} }
// Stores a word from SrcReg in memory address (base + offset) The effective address must be 4-byte-aligned. // Stores a word from SrcReg in memory address (base + offset) The effective address must be 4-byte-aligned.
Self::StoreWord(src, offset, base) => { Self::StoreWord(a) => {
cpu.memory cpu.memory
.write_word(cpu.get(base) + u32::from(offset), cpu.get(src)); .write_word(cpu.get(a.r1) + u32::from(a.immediate), cpu.get(a.r2));
} }
// Loads a 16-bit literal value into reg, setting the bottom 16 bits of the word. To populate the upper 16 bits, see LUI. // Loads a 16-bit literal value into reg, setting the bottom 16 bits of the word. To populate the upper 16 bits, see LUI.
Self::LoadLowerImmediate(reg, imm) => { Self::LoadLowerImmediate(a) => {
*cpu.reg(reg) = u32::from(imm); *cpu.reg(a.r1) = u32::from(a.immediate);
} }
// Loads a 16-bit literal value into reg, setting the top 16 bits of the word. To populate the lower 16 bits, see LLI. // Loads a 16-bit literal value into reg, setting the top 16 bits of the word. To populate the lower 16 bits, see LLI.
Self::LoadUpperImmediate(reg, imm) => { Self::LoadUpperImmediate(a) => {
*cpu.reg(reg) = (cpu.get(reg) & 0x0000_FFFF) | u32::from(imm) << 16; *cpu.reg(a.r1) = (cpu.get(a.r1) & 0x0000_FFFF) | u32::from(a.immediate) << 16;
} }
// Unconditionally jumps to the calculated address or direct address // Unconditionally jumps to the calculated address or direct address
Self::Jump(reg, offset) => cpu.jump(reg, offset), Self::Jump(a) => cpu.jump(a.r1, a.immediate),
// Jumps to the calculated address or direct address if equal flag set. // Jumps to the calculated address or direct address if equal flag set.
Self::JumpEq(reg, offset) => { Self::JumpEq(a) => {
if cpu.get_flag(Flag::Equal) { if cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset); cpu.jump(a.r1, a.immediate);
} }
} }
// Jumps to the calculated address or direct address if equal flag not set. // Jumps to the calculated address or direct address if equal flag not set.
Self::JumpNeq(reg, offset) => { Self::JumpNeq(a) => {
if !cpu.get_flag(Flag::Equal) { if !cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset); cpu.jump(a.r1, a.immediate);
} }
} }
// Jumps to the calculated address or direct address if greater than flag set. // Jumps to the calculated address or direct address if greater than flag set.
Self::JumpGt(reg, offset) => { Self::JumpGt(a) => {
if cpu.get_flag(Flag::GreaterThan) { if cpu.get_flag(Flag::GreaterThan) {
cpu.jump(reg, offset); cpu.jump(a.r1, a.immediate);
} }
} }
// Jumps to the calculated address or direct address if greater than flag or equal flag set. // Jumps to the calculated address or direct address if greater than flag or equal flag set.
Self::JumpGe(reg, offset) => { Self::JumpGe(a) => {
if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) { if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset); cpu.jump(a.r1, a.immediate);
} }
} }
// Jumps to the calculated address or direct address if less than flag set. // Jumps to the calculated address or direct address if less than flag set.
Self::JumpLt(reg, offset) => { Self::JumpLt(a) => {
if cpu.get_flag(Flag::LessThan) { if cpu.get_flag(Flag::LessThan) {
cpu.jump(reg, offset); cpu.jump(a.r1, a.immediate);
} }
} }
// Jumps to the calculated address or direct address if less than flag or equal flag set. // Jumps to the calculated address or direct address if less than flag or equal flag set.
Self::JumpLe(reg, offset) => { Self::JumpLe(a) => {
if cpu.get_flag(Flag::LessThan) || cpu.get_flag(Flag::Equal) { if cpu.get_flag(Flag::LessThan) || cpu.get_flag(Flag::Equal) {
cpu.jump(reg, offset); cpu.jump(a.r1, a.immediate);
} }
} }
// Increments the value in the given register // Increments the value in the given register
Self::Increment(reg) => *cpu.reg(reg) = inc(cpu.get(reg)), Self::Increment(a) => *cpu.reg(a.sr1) = inc(cpu.get(a.sr1)),
// Decrements the value in the given register // Decrements the value in the given register
Self::Decrement(reg) => *cpu.reg(reg) = dec(cpu.get(reg)), Self::Decrement(a) => *cpu.reg(a.sr1) = dec(cpu.get(a.sr1)),
// Left shifts the value in Reg by the given amount (either a register, or a literal value) // Left shifts the value in Reg by the given amount (either a register, or a literal value)
Self::ShiftLeft(src, reg, imm) => { Self::ShiftLeft(a) => {
let regval = cpu.get(reg); let regval = cpu.get(a.sr2);
*cpu.reg(reg) = shl( let val = cpu.get(a.sr1);
cpu.get(src),
if regval != 0 { regval as u8 } else { imm as u8 }, *cpu.reg(a.sr1) = shl(val, if regval != 0 { regval as u8 } else { a.shamt });
);
} }
// Right shifts the value in Reg by the given amount (either a register, or a literal value). // Right shifts the value in Reg by the given amount (either a register, or a literal value).
Self::ShiftRight(src, reg, imm) => { Self::ShiftRight(a) => {
let regval = cpu.get(reg); let regval = cpu.get(a.sr2);
*cpu.reg(reg) = shr( let val = cpu.get(a.sr1);
cpu.get(src),
if regval != 0 { regval as u8 } else { imm as u8 }, *cpu.reg(a.sr1) = shr(val, if regval != 0 { regval as u8 } else { a.shamt });
);
} }
// Adds the value of Src2 to Src1 and writes the result to Dest // Adds the value of Src2 to Src1 and writes the result to a.dr
Self::Add(src1, src2, dest) => { Self::Add(a) => {
*cpu.reg(dest) = add(cpu.get(src1), cpu.get(src2)); *cpu.reg(a.dr) = add(cpu.get(a.sr1), cpu.get(a.sr2));
} }
// Subtracts the value of Src2 from Src1 and writes the result to Dest // Subtracts the value of Src2 from Src1 and writes the result to a.dr
Self::Sub(src1, src2, dest) => { Self::Sub(a) => {
*cpu.reg(dest) = sub(cpu.get(src1), cpu.get(src2)); *cpu.reg(a.dr) = sub(cpu.get(a.sr1), cpu.get(a.sr2));
} }
// Performs bitwise AND on Src1 and Src2 storing the result in Dest // Performs bitwise AND on Src1 and Src2 storing the result in a.dr
Self::And(src1, src2, dest) => *cpu.reg(dest) = and(cpu.get(src1), cpu.get(src2)), Self::And(a) => *cpu.reg(a.dr) = and(cpu.get(a.sr1), cpu.get(a.sr2)),
// Performs bitwise OR on Src1 and Src2 storing the result in Dest // Performs bitwise OR on Src1 and Src2 storing the result in a.dr
Self::Or(src1, src2, dest) => *cpu.reg(dest) = or(cpu.get(src1), cpu.get(src2)), Self::Or(a) => *cpu.reg(a.dr) = or(cpu.get(a.sr1), cpu.get(a.sr2)),
// Performs bitwise NOT on Src storing the result in Dest // Performs bitwise NOT on Src storing the result in a.dr
Self::Not(src, dest) => *cpu.reg(dest) = not(cpu.get(src)), Self::Not(a) => *cpu.reg(a.dr) = not(cpu.get(a.sr1)),
// Performs bitwise XOR on Src1 and Src2 storing the result in Dest // Performs bitwise XOR on Src1 and Src2 storing the result in a.dr
Self::Xor(src1, src2, dest) => *cpu.reg(dest) = xor(cpu.get(src1), cpu.get(src2)), Self::Xor(a) => *cpu.reg(a.dr) = xor(cpu.get(a.sr1), cpu.get(a.sr2)),
// Performs bitwise NAND on Src1 and Src2 storing the result in Dest // Performs bitwise NAND on Src1 and Src2 storing the result in a.dr
Self::Nand(src1, src2, dest) => *cpu.reg(dest) = nand(cpu.get(src1), cpu.get(src2)), Self::Nand(a) => *cpu.reg(a.dr) = nand(cpu.get(a.sr1), cpu.get(a.sr2)),
// Performs bitwise NOR on Src1 and Src2 storing the result in Dest // Performs bitwise NOR on Src1 and Src2 storing the result in a.dr
Self::Nor(src1, src2, dest) => *cpu.reg(dest) = nor(cpu.get(src1), cpu.get(src2)), Self::Nor(a) => *cpu.reg(a.dr) = nor(cpu.get(a.sr1), cpu.get(a.sr2)),
// Performs bitwise XNOR on Src1 and Src2 storing the result in Dest // Performs bitwise XNOR on Src1 and Src2 storing the result in a.dr
Self::Xnor(src1, src2, dest) => *cpu.reg(dest) = xnor(cpu.get(src1), cpu.get(src2)), Self::Xnor(a) => *cpu.reg(a.dr) = xnor(cpu.get(a.sr1), cpu.get(a.sr2)),
// Compares the value of Reg1 to the value in Reg2. The results of the comparisons are set in the Status register. // Compares the value of Reg1 to the value in Reg2. The results of the comparisons are set in the Status register.
Self::Compare(src1, src2) => { Self::Compare(a) => {
cpu.cmp(cpu.get(src1), cpu.get(src2)); cpu.cmp(cpu.get(a.sr1), cpu.get(a.sr2));
} }
/* // Initiates an interrupt with the given 8 bit interrupt code.
Initiates an interrupt with the given 8 bit interrupt code. // Triggering an interrupt invokes the following behaviour:
Triggering an interrupt invokes the following behaviour // - The return address is saved to the RET register.
- The return address is saved to the RET register // - The stack base ptr is set to the kernel stack.
- The stack base ptr is set to the kernel stack.
*/
Self::Interrupt(interrupt_code) => { Self::Interrupt(interrupt_code) => {
cpu.begin_interrupt(interrupt_code); cpu.begin_interrupt(interrupt_code);
} }