From b662904c11e55263af6472aebda3b7e27006e33d Mon Sep 17 00:00:00 2001 From: fmoletta Date: Wed, 10 Dec 2025 18:09:37 -0300 Subject: [PATCH 1/9] fix jalr load and store bugs --- src/vm/execution.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/vm/execution.rs b/src/vm/execution.rs index aaa5d8f4c..53d919ac3 100644 --- a/src/vm/execution.rs +++ b/src/vm/execution.rs @@ -87,6 +87,7 @@ fn run_instruction( } Instruction::JumpAndLink { dst, offset } => { registers.0[*dst as usize] = *pc; + *pc -= 4; *pc += offset; } Instruction::Store { @@ -101,7 +102,9 @@ fn run_instruction( LoadStoreWidth::Half => todo!(), LoadStoreWidth::Word => value, }; - memory.0.insert(*base + *offset, value); + memory + .0 + .insert((registers.0[*base as usize] + *offset), value); } Instruction::Load { dst, @@ -109,7 +112,7 @@ fn run_instruction( base, width, } => { - let value = memory.0[&(*base + *offset)]; + let value = memory.0[&(registers.0[*base as usize] + *offset)]; let value = match width { LoadStoreWidth::Byte => todo!(), LoadStoreWidth::Half => todo!(), From 7ad729de7c777867eec1577880adaee9c23dd6eb Mon Sep 17 00:00:00 2001 From: fmoletta Date: Wed, 10 Dec 2025 18:25:34 -0300 Subject: [PATCH 2/9] JALR/JAL: do not write next pc into zero --- src/vm/execution.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/vm/execution.rs b/src/vm/execution.rs index 53d919ac3..93200955c 100644 --- a/src/vm/execution.rs +++ b/src/vm/execution.rs @@ -82,11 +82,15 @@ fn run_instruction( registers.0[*dst as usize] = res; } Instruction::JumpAndLinkRegister { dst, base, offset } => { - registers.0[*dst as usize] = *pc; + if *dst != 0 { + registers.0[*dst as usize] = *pc; + } *pc = registers.0[*base as usize] + offset; } Instruction::JumpAndLink { dst, offset } => { - registers.0[*dst as usize] = *pc; + if *dst != 0 { + registers.0[*dst as usize] = *pc; + } *pc -= 4; *pc += offset; } From 4457a67ececc50f479783f01072f17e45abec417 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Wed, 10 Dec 2025 18:58:07 -0300 Subject: [PATCH 3/9] MultipleOpcodes: use i32 for immediates --- src/vm/execution.rs | 12 +++++++----- src/vm/instructions.rs | 23 ++++++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/vm/execution.rs b/src/vm/execution.rs index 93200955c..c0a800785 100644 --- a/src/vm/execution.rs +++ b/src/vm/execution.rs @@ -20,6 +20,8 @@ fn load_program(instruction_map: BTreeMap, memory: &mut Memory) { fn run_from_entrypoint(memory: &mut Memory, entrypoint: u32) -> (u32, u32) { let mut pc = entrypoint; let mut registers = Registers::default(); + // TODO: find what the starting value should be + registers.0[2] = 16; while pc != registers.0[1] { let next_instruction = memory.0[&pc]; let instruction = Instruction::parse(next_instruction); @@ -74,25 +76,25 @@ fn run_instruction( *pc += 4; match inst { Instruction::ArithImm { dst, src, imm, op } => { - let (a, b) = (registers.0[*src as usize], imm); + let (a, b) = (registers.0[*src as usize] as i32, imm); let res = match op { ArithOp::Add => a + b, _ => unimplemented!(), }; - registers.0[*dst as usize] = res; + registers.0[*dst as usize] = res as u32; } Instruction::JumpAndLinkRegister { dst, base, offset } => { if *dst != 0 { registers.0[*dst as usize] = *pc; } - *pc = registers.0[*base as usize] + offset; + *pc = (registers.0[*base as usize] as i32 + offset) as u32; } Instruction::JumpAndLink { dst, offset } => { if *dst != 0 { registers.0[*dst as usize] = *pc; } *pc -= 4; - *pc += offset; + *pc = (*pc as i32 + offset) as u32; } Instruction::Store { src, @@ -116,7 +118,7 @@ fn run_instruction( base, width, } => { - let value = memory.0[&(registers.0[*base as usize] + *offset)]; + let value = memory.0[&((registers.0[*base as usize] as i32 + *offset) as u32)]; let value = match width { LoadStoreWidth::Byte => todo!(), LoadStoreWidth::Half => todo!(), diff --git a/src/vm/instructions.rs b/src/vm/instructions.rs index 36e2c0fa7..cd2b9f0e3 100644 --- a/src/vm/instructions.rs +++ b/src/vm/instructions.rs @@ -113,17 +113,17 @@ pub enum Instruction { ArithImm { dst: u32, src: u32, - imm: u32, + imm: i32, op: ArithOp, }, JumpAndLink { dst: u32, - offset: u32, + offset: i32, }, JumpAndLinkRegister { base: u32, dst: u32, - offset: u32, + offset: i32, }, Store { src: u32, @@ -133,7 +133,7 @@ pub enum Instruction { }, Load { dst: u32, - offset: u32, + offset: i32, base: u32, width: LoadStoreWidth, }, @@ -232,11 +232,15 @@ const SLTU_FUNC_IDENTIFIER: u32 = 0x3; // | imm | rs1 |funct3| rd |opcode| // |31..20|19..15|14..12|11..7| 6..0 | fn parse_i_instruction(instruction: u32, opcode: Opcode) -> Instruction { - let func7 = (instruction & FUNC7_MASK) >> 25; let func3 = (instruction & FUNC3_MASK) >> 12; - let rs2 = (instruction & RS2_MASK) >> 20; let rs1 = (instruction & RS1_MASK) >> 15; - let mut imm = func7 | rs2; + let imm = ((instruction >> 20) & 0x7ff) as i32; + let mut imm: i32 = if (instruction & 0x8000_0000) != 0 { + imm as i32 - (1 << 11) + } else { + imm as i32 + }; + let rd = (instruction & RD_MASK) >> 7; match opcode { Opcode::ArithImm => { @@ -354,6 +358,11 @@ fn parse_b_instruction(instruction: u32, opcode: Opcode) -> Instruction { fn parse_j_instruction(instruction: u32, opcode: Opcode) -> Instruction { let imm = instruction & 0xff000 | ((instruction & 0x100000) >> 9) | ((instruction >> 20) & 0x7fe); + let imm: i32 = if (instruction & 0x8000_0000) != 0 { + imm as i32 - (1 << 20) + } else { + imm as i32 + }; let rd = (instruction & RD_MASK) >> 7; match opcode { Opcode::JumpAndLink => Instruction::JumpAndLink { From efa652c9b6d6c9ca8119f91e1e2a57e2d91f21d0 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 11 Dec 2025 10:10:01 -0300 Subject: [PATCH 4/9] fix(dirty): handle mem & register values as i32 + implement missing arithImm operations --- src/vm/execution.rs | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/vm/execution.rs b/src/vm/execution.rs index c0a800785..5968f696e 100644 --- a/src/vm/execution.rs +++ b/src/vm/execution.rs @@ -5,7 +5,7 @@ use std::{ use crate::vm::instructions::{ArithOp, Comparison, Instruction, LoadStoreWidth}; -pub fn run_program(instruction_map: BTreeMap, entrypoint: u32) -> (u32, u32) { +pub fn run_program(instruction_map: BTreeMap, entrypoint: u32) -> (i32, i32) { let mut memory = Memory::default(); load_program(instruction_map, &mut memory); run_from_entrypoint(&mut memory, entrypoint) @@ -13,17 +13,17 @@ pub fn run_program(instruction_map: BTreeMap, entrypoint: u32) -> (u32 fn load_program(instruction_map: BTreeMap, memory: &mut Memory) { for (addr, instruction) in instruction_map { - memory.0.insert(addr, instruction); + memory.0.insert(addr, instruction as i32); } } -fn run_from_entrypoint(memory: &mut Memory, entrypoint: u32) -> (u32, u32) { +fn run_from_entrypoint(memory: &mut Memory, entrypoint: u32) -> (i32, i32) { let mut pc = entrypoint; let mut registers = Registers::default(); // TODO: find what the starting value should be registers.0[2] = 16; - while pc != registers.0[1] { - let next_instruction = memory.0[&pc]; + while pc as i32 != registers.0[1] { + let next_instruction = memory.0[&pc] as u32; let instruction = Instruction::parse(next_instruction); run_instruction(&instruction, &mut registers, &mut pc, memory); } @@ -35,10 +35,10 @@ fn run_from_entrypoint(memory: &mut Memory, entrypoint: u32) -> (u32, u32) { // Toy Memory, TODO: Make expandable memory #[derive(Default, Debug)] -struct Memory(BTreeMap); +struct Memory(BTreeMap); #[derive(Default, Debug)] -struct Registers([u32; 32]); +struct Registers([i32; 32]); // Registers: // 0x zero // a0-ax function arguments: 0x10 -etc @@ -47,6 +47,7 @@ struct Registers([u32; 32]); impl Display for Registers { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!(f, "Registers:")?; + writeln!(f, "Zero(zero): {}", self.0[0])?; writeln!(f, "ReturnAddress(ra): {}", self.0[1])?; writeln!(f, "StackPointer(sp): {}", self.0[2])?; // Not used for now @@ -76,22 +77,30 @@ fn run_instruction( *pc += 4; match inst { Instruction::ArithImm { dst, src, imm, op } => { - let (a, b) = (registers.0[*src as usize] as i32, imm); + let (a, b) = (registers.0[*src as usize], *imm); let res = match op { ArithOp::Add => a + b, - _ => unimplemented!(), + ArithOp::Sub => panic!("SubImm not supported"), + ArithOp::Xor => a ^ b, + ArithOp::Or => a | b, + ArithOp::And => a & b, + ArithOp::ShiftLeftLogical => a << b, + ArithOp::ShiftRightLogical => a >> b, + ArithOp::ShiftRightArith => a >> b, + ArithOp::SetLessThan => ((a as i32) < b) as i32, + ArithOp::SetLessThanU => ((a as u32) < (b as u32)) as i32, }; - registers.0[*dst as usize] = res as u32; + registers.0[*dst as usize] = res; } Instruction::JumpAndLinkRegister { dst, base, offset } => { if *dst != 0 { - registers.0[*dst as usize] = *pc; + registers.0[*dst as usize] = *pc as i32; } *pc = (registers.0[*base as usize] as i32 + offset) as u32; } Instruction::JumpAndLink { dst, offset } => { if *dst != 0 { - registers.0[*dst as usize] = *pc; + registers.0[*dst as usize] = *pc as i32; } *pc -= 4; *pc = (*pc as i32 + offset) as u32; @@ -110,7 +119,7 @@ fn run_instruction( }; memory .0 - .insert((registers.0[*base as usize] + *offset), value); + .insert(registers.0[*base as usize] as u32 + *offset, value); } Instruction::Load { dst, From 2e418d1357f7fc1920a6879b209913cf2a32ebaa Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 11 Dec 2025 10:14:25 -0300 Subject: [PATCH 5/9] clipppy --- src/vm/execution.rs | 6 +++--- src/vm/instructions.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vm/execution.rs b/src/vm/execution.rs index 5968f696e..5041e63e1 100644 --- a/src/vm/execution.rs +++ b/src/vm/execution.rs @@ -87,7 +87,7 @@ fn run_instruction( ArithOp::ShiftLeftLogical => a << b, ArithOp::ShiftRightLogical => a >> b, ArithOp::ShiftRightArith => a >> b, - ArithOp::SetLessThan => ((a as i32) < b) as i32, + ArithOp::SetLessThan => (a < b) as i32, ArithOp::SetLessThanU => ((a as u32) < (b as u32)) as i32, }; registers.0[*dst as usize] = res; @@ -96,7 +96,7 @@ fn run_instruction( if *dst != 0 { registers.0[*dst as usize] = *pc as i32; } - *pc = (registers.0[*base as usize] as i32 + offset) as u32; + *pc = (registers.0[*base as usize] + offset) as u32; } Instruction::JumpAndLink { dst, offset } => { if *dst != 0 { @@ -127,7 +127,7 @@ fn run_instruction( base, width, } => { - let value = memory.0[&((registers.0[*base as usize] as i32 + *offset) as u32)]; + let value = memory.0[&((registers.0[*base as usize] + *offset) as u32)]; let value = match width { LoadStoreWidth::Byte => todo!(), LoadStoreWidth::Half => todo!(), diff --git a/src/vm/instructions.rs b/src/vm/instructions.rs index cd2b9f0e3..252ac0d55 100644 --- a/src/vm/instructions.rs +++ b/src/vm/instructions.rs @@ -236,9 +236,9 @@ fn parse_i_instruction(instruction: u32, opcode: Opcode) -> Instruction { let rs1 = (instruction & RS1_MASK) >> 15; let imm = ((instruction >> 20) & 0x7ff) as i32; let mut imm: i32 = if (instruction & 0x8000_0000) != 0 { - imm as i32 - (1 << 11) + imm - (1 << 11) } else { - imm as i32 + imm }; let rd = (instruction & RD_MASK) >> 7; From 953f2eafd8336ad58ff806a08c1011285d73ee54 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 11 Dec 2025 15:14:35 -0300 Subject: [PATCH 6/9] Store memory and register values as u32 --- src/vm/execution.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vm/execution.rs b/src/vm/execution.rs index 5041e63e1..81c19d273 100644 --- a/src/vm/execution.rs +++ b/src/vm/execution.rs @@ -13,7 +13,7 @@ pub fn run_program(instruction_map: BTreeMap, entrypoint: u32) -> (i32 fn load_program(instruction_map: BTreeMap, memory: &mut Memory) { for (addr, instruction) in instruction_map { - memory.0.insert(addr, instruction as i32); + memory.0.insert(addr, instruction); } } @@ -22,23 +22,23 @@ fn run_from_entrypoint(memory: &mut Memory, entrypoint: u32) -> (i32, i32) { let mut registers = Registers::default(); // TODO: find what the starting value should be registers.0[2] = 16; - while pc as i32 != registers.0[1] { + while pc != registers.0[1] { let next_instruction = memory.0[&pc] as u32; let instruction = Instruction::parse(next_instruction); run_instruction(&instruction, &mut registers, &mut pc, memory); } println!("Final Register Values:\n {}", ®isters); - let return_values = (registers.0[10], registers.0[11]); + let return_values = (registers.0[10] as i32, registers.0[11] as i32); println!("Return Values: {return_values:?}"); return_values } // Toy Memory, TODO: Make expandable memory #[derive(Default, Debug)] -struct Memory(BTreeMap); +struct Memory(BTreeMap); #[derive(Default, Debug)] -struct Registers([i32; 32]); +struct Registers([u32; 32]); // Registers: // 0x zero // a0-ax function arguments: 0x10 -etc @@ -77,7 +77,7 @@ fn run_instruction( *pc += 4; match inst { Instruction::ArithImm { dst, src, imm, op } => { - let (a, b) = (registers.0[*src as usize], *imm); + let (a, b) = (registers.0[*src as usize] as i32, *imm); let res = match op { ArithOp::Add => a + b, ArithOp::Sub => panic!("SubImm not supported"), @@ -90,17 +90,17 @@ fn run_instruction( ArithOp::SetLessThan => (a < b) as i32, ArithOp::SetLessThanU => ((a as u32) < (b as u32)) as i32, }; - registers.0[*dst as usize] = res; + registers.0[*dst as usize] = res as u32; } Instruction::JumpAndLinkRegister { dst, base, offset } => { if *dst != 0 { - registers.0[*dst as usize] = *pc as i32; + registers.0[*dst as usize] = *pc; } - *pc = (registers.0[*base as usize] + offset) as u32; + *pc = (registers.0[*base as usize] as i32 + offset) as u32; } Instruction::JumpAndLink { dst, offset } => { if *dst != 0 { - registers.0[*dst as usize] = *pc as i32; + registers.0[*dst as usize] = *pc; } *pc -= 4; *pc = (*pc as i32 + offset) as u32; @@ -127,7 +127,7 @@ fn run_instruction( base, width, } => { - let value = memory.0[&((registers.0[*base as usize] + *offset) as u32)]; + let value = memory.0[&((registers.0[*base as usize] as i32 + *offset) as u32)]; let value = match width { LoadStoreWidth::Byte => todo!(), LoadStoreWidth::Half => todo!(), From 2039d6523f621a4dfbe405876d48a9345fab3fa0 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 11 Dec 2025 15:15:04 -0300 Subject: [PATCH 7/9] Set starting SP --- src/vm/execution.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vm/execution.rs b/src/vm/execution.rs index 81c19d273..8cb005bb0 100644 --- a/src/vm/execution.rs +++ b/src/vm/execution.rs @@ -20,8 +20,7 @@ fn load_program(instruction_map: BTreeMap, memory: &mut Memory) { fn run_from_entrypoint(memory: &mut Memory, entrypoint: u32) -> (i32, i32) { let mut pc = entrypoint; let mut registers = Registers::default(); - // TODO: find what the starting value should be - registers.0[2] = 16; + registers.0[2] = 0xFFFFFFFF; // 4GB while pc != registers.0[1] { let next_instruction = memory.0[&pc] as u32; let instruction = Instruction::parse(next_instruction); From 2cee0215681868403b0c6e70ff3c5ca701b9d139 Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 11 Dec 2025 15:25:57 -0300 Subject: [PATCH 8/9] Add constants --- vm/src/vm/instructions.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vm/src/vm/instructions.rs b/vm/src/vm/instructions.rs index dd43e13a5..2370dce1d 100644 --- a/vm/src/vm/instructions.rs +++ b/vm/src/vm/instructions.rs @@ -7,7 +7,6 @@ const BRANCH_OPCODE: u32 = 0b1100011; const JUMP_AND_LINK_REGISTER_OPCCODE: u32 = 0b1100111; const JUMP_AND_LINK_OPCCODE: u32 = 0b1101111; -// TODO: consider using num_enum dep to replace TyFrom/ using the constants here enum Opcode { Arith, ArithImm, @@ -150,6 +149,8 @@ const FUNC3_MASK: u32 = 0x00007000; const RS1_MASK: u32 = 0x000f8000; const RS2_MASK: u32 = 0x01f00000; const RD_MASK: u32 = 0x00000f80; +const SIGN_MASK: u32 = 0x80000000; +const I_TYPE_IMM_MASK: u32 = 0x7ff; impl Instruction { pub fn parse(instruction: u32) -> Instruction { @@ -233,8 +234,8 @@ const SLTU_FUNC_IDENTIFIER: u32 = 0x3; fn parse_i_instruction(instruction: u32, opcode: Opcode) -> Instruction { let func3 = (instruction & FUNC3_MASK) >> 12; let rs1 = (instruction & RS1_MASK) >> 15; - let imm = ((instruction >> 20) & 0x7ff) as i32; - let mut imm: i32 = if (instruction & 0x8000_0000) != 0 { + let imm = ((instruction >> 20) & I_TYPE_IMM_MASK) as i32; + let mut imm: i32 = if (instruction & SIGN_MASK) != 0 { imm - (1 << 11) } else { imm @@ -357,7 +358,7 @@ fn parse_b_instruction(instruction: u32, opcode: Opcode) -> Instruction { fn parse_j_instruction(instruction: u32, opcode: Opcode) -> Instruction { let imm = instruction & 0xff000 | ((instruction & 0x100000) >> 9) | ((instruction >> 20) & 0x7fe); - let imm: i32 = if (instruction & 0x8000_0000) != 0 { + let imm: i32 = if (instruction & SIGN_MASK) != 0 { imm as i32 - (1 << 20) } else { imm as i32 From 384d6255cb00393c686d90f7ed0e479175e8c71c Mon Sep 17 00:00:00 2001 From: fmoletta Date: Thu, 11 Dec 2025 15:26:57 -0300 Subject: [PATCH 9/9] clippy --- vm/src/vm/execution.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/vm/execution.rs b/vm/src/vm/execution.rs index 8cb005bb0..822dff66c 100644 --- a/vm/src/vm/execution.rs +++ b/vm/src/vm/execution.rs @@ -22,7 +22,7 @@ fn run_from_entrypoint(memory: &mut Memory, entrypoint: u32) -> (i32, i32) { let mut registers = Registers::default(); registers.0[2] = 0xFFFFFFFF; // 4GB while pc != registers.0[1] { - let next_instruction = memory.0[&pc] as u32; + let next_instruction = memory.0[&pc]; let instruction = Instruction::parse(next_instruction); run_instruction(&instruction, &mut registers, &mut pc, memory); } @@ -118,7 +118,7 @@ fn run_instruction( }; memory .0 - .insert(registers.0[*base as usize] as u32 + *offset, value); + .insert(registers.0[*base as usize] + *offset, value); } Instruction::Load { dst,