From c8378769601e404392fcabca8ed2fba415e53d66 Mon Sep 17 00:00:00 2001 From: "J. Hinchliffe" Date: Sun, 15 Jun 2025 12:21:35 +0100 Subject: [PATCH] common: update processor code to use new arg structs --- src/common/instructions.rs | 56 +++++++--- src/emulator/system/processor.rs | 177 +++++++++++++++---------------- 2 files changed, 129 insertions(+), 104 deletions(-) diff --git a/src/common/instructions.rs b/src/common/instructions.rs index c083885..c75b6aa 100644 --- a/src/common/instructions.rs +++ b/src/common/instructions.rs @@ -53,7 +53,7 @@ pub enum Register { Idr, Mmr, Zero, - None, + NoReg, // system registers - can't be written to by instructions. Mar, @@ -63,6 +63,12 @@ pub enum Register { Pcx, } +impl Default for Register { + fn default() -> Self { + Register::NoReg + } +} + #[derive(Debug)] /// Error type for parsing register numbers. pub enum RegisterParseError { @@ -112,7 +118,7 @@ impl TryFrom for Register { 0x14 => Self::Idr, 0x15 => Self::Mmr, 0x16 => Self::Zero, - 0x17 => Self::None, + 0x17 => Self::NoReg, _ => 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::Mmr => write!(f, "Mmr"), Self::Zero => write!(f, "Zero"), - Self::None => write!(f, "None"), + Self::NoReg => write!(f, "None"), Self::Mar => write!(f, "Mar"), Self::Mdr => write!(f, "Mdr"), Self::Sts => write!(f, "Sts"), @@ -154,26 +160,48 @@ impl std::fmt::Display for Register { } } -/// Used by instructions with no arguments. -struct NoArgs {} +// /// Used by instructions with no arguments. +// struct NoArgs {} #[derive(Debug, Clone, Copy)] /// Used by instructions with 2 registers and an immediate argument. -struct ITypeArgs { - immediate: u16, - r1: Register, +pub struct ITypeArgs { + pub immediate: u16, + 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. - r2: Register, + pub r2: Register, } /// Used by instructions not using immediates (besides 5 bit shift values). #[derive(Debug, Clone, Copy)] -struct RTypeArgs { - sr1: Register, - sr2: Register, - dr: Register, +pub struct RTypeArgs { + pub sr1: Register, + pub sr2: Register, + pub dr: Register, /// 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, + sr2: Option, + dr: Option, + shamt: Option, + ) -> 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` diff --git a/src/emulator/system/processor.rs b/src/emulator/system/processor.rs index e64dbc6..8dcbfe0 100644 --- a/src/emulator/system/processor.rs +++ b/src/emulator/system/processor.rs @@ -156,188 +156,185 @@ impl Executable for Instruction { // No operation - a blank line. Self::Nop => {} - // Copies from SrcReg to DestReg. - Self::Mov(src, dest) => { - *cpu.reg(dest) = cpu.get(src); + // Copies from SrcReg to a.drReg. + Self::Mov(a) => { + *cpu.reg(a.dr) = cpu.get(a.sr1); } - // Copies from SrcReg to DestReg, sign extending the value to take up a full word. - Self::MovSigned(src, dest) => { - *cpu.reg(dest) = sign_extend(cpu.get(src)); + // Copies from SrcReg to a.drReg, sign extending the value to take up a full word. + Self::MovSigned(a) => { + *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. - Self::LoadByte(base, offset, dest) => { - *cpu.reg(dest) = u32::from(cpu.memory.read_byte(cpu.get(base) + u32::from(offset))); + // Loads a byte from memory address (base + offset) into a.drReg. The effective address must be byte-aligned. + Self::LoadByte(a) => { + *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. - Self::LoadByteSigned(base, offset, dest) => { - *cpu.reg(dest) = sign_extend(u32::from( - cpu.memory.read_byte(cpu.get(base) + u32::from(offset)), + // Loads a sign-extended byte from memory address (base + offset) into a.drReg. The effective address must be byte-aligned. + Self::LoadByteSigned(a) => { + *cpu.reg(a.r2) = sign_extend(u32::from( + 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. - Self::LoadHalfword(base, offset, dest) => { + // Loads a half-word from memory address (base + offset) into a.drReg. The effective address must be 2-byte-aligned. + Self::LoadHalfword(a) => { // 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. - Self::LoadHalfwordSigned(base, offset, dest) => { - *cpu.reg(dest) = - sign_extend(cpu.memory.read_word(cpu.get(base) + u32::from(offset)) >> 16); + // Loads a sign-extended half-word from memory address (base + offset) into a.drReg. The effective address must be 2-byte-aligned. + Self::LoadHalfwordSigned(a) => { + *cpu.reg(a.r2) = + 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. - Self::LoadWord(base, offset, dest) => { - *cpu.reg(dest) = cpu.memory.read_word(cpu.get(base) + u32::from(offset)); + // Loads a word from memory address (base + offset) into a.drReg. The effective address must be 4-byte-aligned. + Self::LoadWord(a) => { + *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. - Self::StoreByte(src, offset, base) => { + Self::StoreByte(a) => { 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. - Self::StoreHalfword(src, offset, base) => { + Self::StoreHalfword(a) => { // 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 - .write_byte(cpu.get(base) + u32::from(offset), bytes[0]); + .write_byte(cpu.get(a.r1) + u32::from(a.immediate), bytes[0]); 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. - Self::StoreWord(src, offset, base) => { + Self::StoreWord(a) => { 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. - Self::LoadLowerImmediate(reg, imm) => { - *cpu.reg(reg) = u32::from(imm); + Self::LoadLowerImmediate(a) => { + *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. - Self::LoadUpperImmediate(reg, imm) => { - *cpu.reg(reg) = (cpu.get(reg) & 0x0000_FFFF) | u32::from(imm) << 16; + Self::LoadUpperImmediate(a) => { + *cpu.reg(a.r1) = (cpu.get(a.r1) & 0x0000_FFFF) | u32::from(a.immediate) << 16; } // 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. - Self::JumpEq(reg, offset) => { + Self::JumpEq(a) => { 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. - Self::JumpNeq(reg, offset) => { + Self::JumpNeq(a) => { 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. - Self::JumpGt(reg, offset) => { + Self::JumpGt(a) => { 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. - Self::JumpGe(reg, offset) => { + Self::JumpGe(a) => { 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. - Self::JumpLt(reg, offset) => { + Self::JumpLt(a) => { 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. - Self::JumpLe(reg, offset) => { + Self::JumpLe(a) => { 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 - 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 - 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) - Self::ShiftLeft(src, reg, imm) => { - let regval = cpu.get(reg); - *cpu.reg(reg) = shl( - cpu.get(src), - if regval != 0 { regval as u8 } else { imm as u8 }, - ); + Self::ShiftLeft(a) => { + let regval = cpu.get(a.sr2); + let val = cpu.get(a.sr1); + + *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). - Self::ShiftRight(src, reg, imm) => { - let regval = cpu.get(reg); - *cpu.reg(reg) = shr( - cpu.get(src), - if regval != 0 { regval as u8 } else { imm as u8 }, - ); + Self::ShiftRight(a) => { + let regval = cpu.get(a.sr2); + let val = cpu.get(a.sr1); + + *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 - Self::Add(src1, src2, dest) => { - *cpu.reg(dest) = add(cpu.get(src1), cpu.get(src2)); + // Adds the value of Src2 to Src1 and writes the result to a.dr + Self::Add(a) => { + *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 - Self::Sub(src1, src2, dest) => { - *cpu.reg(dest) = sub(cpu.get(src1), cpu.get(src2)); + // Subtracts the value of Src2 from Src1 and writes the result to a.dr + Self::Sub(a) => { + *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 - Self::And(src1, src2, dest) => *cpu.reg(dest) = and(cpu.get(src1), cpu.get(src2)), + // Performs bitwise AND on Src1 and Src2 storing the result in a.dr + 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 - Self::Or(src1, src2, dest) => *cpu.reg(dest) = or(cpu.get(src1), cpu.get(src2)), + // Performs bitwise OR on Src1 and Src2 storing the result in a.dr + 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 - Self::Not(src, dest) => *cpu.reg(dest) = not(cpu.get(src)), + // Performs bitwise NOT on Src storing the result in a.dr + Self::Not(a) => *cpu.reg(a.dr) = not(cpu.get(a.sr1)), - // Performs bitwise XOR on Src1 and Src2 storing the result in Dest - Self::Xor(src1, src2, dest) => *cpu.reg(dest) = xor(cpu.get(src1), cpu.get(src2)), + // Performs bitwise XOR on Src1 and Src2 storing the result in a.dr + 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 - Self::Nand(src1, src2, dest) => *cpu.reg(dest) = nand(cpu.get(src1), cpu.get(src2)), + // Performs bitwise NAND on Src1 and Src2 storing the result in a.dr + 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 - Self::Nor(src1, src2, dest) => *cpu.reg(dest) = nor(cpu.get(src1), cpu.get(src2)), + // Performs bitwise NOR on Src1 and Src2 storing the result in a.dr + 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 - Self::Xnor(src1, src2, dest) => *cpu.reg(dest) = xnor(cpu.get(src1), cpu.get(src2)), + // Performs bitwise XNOR on Src1 and Src2 storing the result in a.dr + 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. - Self::Compare(src1, src2) => { - cpu.cmp(cpu.get(src1), cpu.get(src2)); + Self::Compare(a) => { + cpu.cmp(cpu.get(a.sr1), cpu.get(a.sr2)); } - /* - Initiates an interrupt with the given 8 bit interrupt code. - Triggering an interrupt invokes the following behaviour - - The return address is saved to the RET register - - The stack base ptr is set to the kernel stack. - */ + // Initiates an interrupt with the given 8 bit interrupt code. + // Triggering an interrupt invokes the following behaviour: + // - The return address is saved to the RET register. + // - The stack base ptr is set to the kernel stack. Self::Interrupt(interrupt_code) => { cpu.begin_interrupt(interrupt_code); }