use core::panic; use crate::{glk::Glk, instructions::{Instruction, Operand}}; pub type MemAddress = u32; pub type StackAddress = u32; pub type FunctionPointer = MemAddress; enum StubDestType { DoNotStore = 0, MainMemory = 1, LocalVariable = 2, PushOnStack = 3, ResumeCompressedString = 10, ResumeFunctionFromString = 11, ResumeInteger = 12, ResumeCString = 13, ResumeUnicodeString = 14, } enum ReadWriteDest { Stack, Memory, } pub struct HeapChunk { pub start_address: MemAddress, pub length: u32, pub contents: Option> } pub enum Output { Null, Filter(FunctionPointer), Glk(Glk), } pub struct Interpreter { memory: Vec, stack: Vec, mem_writeable: MemAddress, mem_min: MemAddress, heap: Vec, reg_program: MemAddress, reg_stack: StackAddress, reg_frame: StackAddress, reg_strdec: MemAddress, output_mode: Output, } impl Interpreter { pub fn init( mut source_file: Vec, ram_start: u32, ext_start: u32, ram_end: u32, stack_size: u32, str_decode: u32, ) -> Self { // Trick to ensure everything after ext_start is cleared // even if the original file was longer source_file.resize(ext_start as usize, 0); source_file.resize(ext_start as usize, 0); let mut stack = Vec::new(); stack.resize(stack_size as usize, 0); Self { memory: source_file, stack, mem_writeable: ram_start, mem_min: ram_end, heap: Vec::new(), reg_program: 0, reg_stack: 0, reg_frame: 0, reg_strdec: str_decode, output_mode: Output::Null, } } pub fn run(&mut self, entry_func: MemAddress) { self.reg_program = self.create_stack_frame(entry_func, Vec::new()); loop { let pc = self.reg_program as usize; let mut instruction_slice = &self.memory[pc..pc+100]; let (instruction, bytes_read) = Instruction::read(&mut instruction_slice); self.reg_program += bytes_read; println!("Executing instruction {instruction:?}"); match instruction.operation { crate::instructions::OpCode::Nop => (), crate::instructions::OpCode::Add => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 + op2); }, crate::instructions::OpCode::Sub => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 - op2); }, crate::instructions::OpCode::Mul => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 * op2); }, crate::instructions::OpCode::Div => { let op1 = self.eval_source_operand(&instruction.operands[0]) as i32; let op2 = self.eval_source_operand(&instruction.operands[1]) as i32; let result = (op1 / op2) as u32; self.store_dest_operand(&instruction.operands[2], result); }, crate::instructions::OpCode::Mod => { let op1 = self.eval_source_operand(&instruction.operands[0]) as i32; let op2 = self.eval_source_operand(&instruction.operands[1]) as i32; let result = (op1 % op2) as u32; self.store_dest_operand(&instruction.operands[2], result); }, crate::instructions::OpCode::Neg => { let op1 = self.eval_source_operand(&instruction.operands[0]) as i32; let result = (-op1) as u32; self.store_dest_operand(&instruction.operands[1], result); }, crate::instructions::OpCode::Bitand => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 & op2); }, crate::instructions::OpCode::Bitor => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 | op2); }, crate::instructions::OpCode::Bitxor => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 ^ op2); }, crate::instructions::OpCode::Bitnot => { let op1 = self.eval_source_operand(&instruction.operands[0]); self.store_dest_operand(&instruction.operands[1], !op1); }, crate::instructions::OpCode::Shiftl => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 << op2); }, crate::instructions::OpCode::Sshiftr => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 >> op2); }, crate::instructions::OpCode::Ushiftr => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], op1 >> op2); }, crate::instructions::OpCode::Jump => { let op1 = self.eval_source_operand(&instruction.operands[0]); self.jump_with_offset(op1); }, crate::instructions::OpCode::Jz => { let check = self.eval_source_operand(&instruction.operands[0]); let destination = self.eval_source_operand(&instruction.operands[1]); if check == 0 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jnz => { let check = self.eval_source_operand(&instruction.operands[0]); let destination = self.eval_source_operand(&instruction.operands[1]); if check != 0 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jeq => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); let destination = self.eval_source_operand(&instruction.operands[2]); if op1 == op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jne => { let op1 = self.eval_source_operand(&instruction.operands[0]) as i32; let op2 = self.eval_source_operand(&instruction.operands[1]) as i32; let destination = self.eval_source_operand(&instruction.operands[2]); if op1 != op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jlt => { let op1 = self.eval_source_operand(&instruction.operands[0]) as i32; let op2 = self.eval_source_operand(&instruction.operands[1]) as i32; let destination = self.eval_source_operand(&instruction.operands[2]); if op1 < op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jge => { let op1 = self.eval_source_operand(&instruction.operands[0]) as i32; let op2 = self.eval_source_operand(&instruction.operands[1]) as i32; let destination = self.eval_source_operand(&instruction.operands[2]); if op1 >= op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jgt => { let op1 = self.eval_source_operand(&instruction.operands[0]) as i32; let op2 = self.eval_source_operand(&instruction.operands[1]) as i32; let destination = self.eval_source_operand(&instruction.operands[2]); if op1 > op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jle => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); let destination = self.eval_source_operand(&instruction.operands[2]); if op1 <= op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jltu => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); let destination = self.eval_source_operand(&instruction.operands[2]); if op1 < op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jgeu => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); let destination = self.eval_source_operand(&instruction.operands[2]); if op1 >= op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jgtu => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); let destination = self.eval_source_operand(&instruction.operands[2]); if op1 > op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jleu => { let op1 = self.eval_source_operand(&instruction.operands[0]); let op2 = self.eval_source_operand(&instruction.operands[1]); let destination = self.eval_source_operand(&instruction.operands[2]); if op1 <= op2 { self.jump_with_offset(destination); } }, crate::instructions::OpCode::Jumpabs => { let destination = self.eval_source_operand(&instruction.operands[0]); self.reg_program = destination; }, crate::instructions::OpCode::Copy => { let source = self.eval_source_operand(&instruction.operands[0]); self.store_dest_operand(&instruction.operands[1], source); }, crate::instructions::OpCode::Copys => todo!(), crate::instructions::OpCode::Copyb => todo!(), crate::instructions::OpCode::Sexs => { let source = self.eval_source_operand(&instruction.operands[0]); let result = source as i16 as i32 as u32; self.store_dest_operand(&instruction.operands[1], result); }, crate::instructions::OpCode::Sexb => { let source = self.eval_source_operand(&instruction.operands[0]); let result = source as i8 as i32 as u32; self.store_dest_operand(&instruction.operands[1], result); }, crate::instructions::OpCode::Aload => { let l1 = self.eval_source_operand(&instruction.operands[0]); let l2 = self.eval_source_operand(&instruction.operands[1]); let value = self.get_word(l1.wrapping_add(4 * l2), ReadWriteDest::Memory); self.store_dest_operand(&instruction.operands[2], value); }, crate::instructions::OpCode::Aloads => { let l1 = self.eval_source_operand(&instruction.operands[0]); let l2 = self.eval_source_operand(&instruction.operands[1]); let value = self.get_twobyte(l1.wrapping_add(2 * l2), ReadWriteDest::Memory); self.store_dest_operand(&instruction.operands[2], value as u32); }, crate::instructions::OpCode::Aloadb => { let l1 = self.eval_source_operand(&instruction.operands[0]); let l2 = self.eval_source_operand(&instruction.operands[1]); let value = self.get_twobyte(l1.wrapping_add(l2), ReadWriteDest::Memory); self.store_dest_operand(&instruction.operands[2], value as u32); }, crate::instructions::OpCode::Astore => { let l1 = self.eval_source_operand(&instruction.operands[0]); let l2 = self.eval_source_operand(&instruction.operands[1]); let l3 = self.eval_source_operand(&instruction.operands[2]); self.write_word(l1.wrapping_add(4 * l2), ReadWriteDest::Memory, l3); }, crate::instructions::OpCode::Astores => { let l1 = self.eval_source_operand(&instruction.operands[0]); let l2 = self.eval_source_operand(&instruction.operands[1]); let l3 = self.eval_source_operand(&instruction.operands[2]); self.write_twobyte(l1.wrapping_add(2 * l2), ReadWriteDest::Memory, l3 as u16); }, crate::instructions::OpCode::Astoreb => { let l1 = self.eval_source_operand(&instruction.operands[0]); let l2 = self.eval_source_operand(&instruction.operands[1]); let l3 = self.eval_source_operand(&instruction.operands[2]); self.write_byte(l1.wrapping_add(l2), ReadWriteDest::Memory, l3 as u8); }, crate::instructions::OpCode::Aloadbit => todo!(), crate::instructions::OpCode::Astorebit => todo!(), crate::instructions::OpCode::Stkcount => todo!(), crate::instructions::OpCode::Stkpeek => todo!(), crate::instructions::OpCode::Stkswap => todo!(), crate::instructions::OpCode::Stkroll => todo!(), crate::instructions::OpCode::Stkcopy => todo!(), crate::instructions::OpCode::Call => { let func_addr = self.eval_source_operand(&instruction.operands[0]); let arg_count = self.eval_source_operand(&instruction.operands[1]); let args = (0..arg_count).map(|_| self.pop_stack()).collect::>(); self.call_function(func_addr, args, &instruction.operands[2]); }, crate::instructions::OpCode::Callf => { let func_addr = self.eval_source_operand(&instruction.operands[0]); self.call_function(func_addr, Vec::new(), &instruction.operands[1]); }, crate::instructions::OpCode::Callfi => todo!(), crate::instructions::OpCode::Callfii => todo!(), crate::instructions::OpCode::Callfiii => todo!(), crate::instructions::OpCode::Return => todo!(), crate::instructions::OpCode::Catch => todo!(), crate::instructions::OpCode::Throw => todo!(), crate::instructions::OpCode::Tailcall => todo!(), crate::instructions::OpCode::Streamchar => todo!(), crate::instructions::OpCode::Streamnum => todo!(), crate::instructions::OpCode::Streamstr => todo!(), crate::instructions::OpCode::Streamunichar => todo!(), crate::instructions::OpCode::Gestalt => { let _gestalt_num = self.eval_source_operand(&instruction.operands[0]); let _extra_arg = self.eval_source_operand(&instruction.operands[1]); self.store_dest_operand(&instruction.operands[2], 0); }, crate::instructions::OpCode::Debugtrap => todo!(), crate::instructions::OpCode::Getmemsize => todo!(), crate::instructions::OpCode::Setmemsize => todo!(), crate::instructions::OpCode::Random => todo!(), crate::instructions::OpCode::Setrandom => todo!(), crate::instructions::OpCode::Quit => { println!("Quit called"); return; }, crate::instructions::OpCode::Verify => todo!(), crate::instructions::OpCode::Restart => todo!(), crate::instructions::OpCode::Save => todo!(), crate::instructions::OpCode::Restore => todo!(), crate::instructions::OpCode::Saveundo => todo!(), crate::instructions::OpCode::Restoreundo => todo!(), crate::instructions::OpCode::Protect => todo!(), crate::instructions::OpCode::Hasundo => todo!(), crate::instructions::OpCode::Discardundo => todo!(), crate::instructions::OpCode::Glk => todo!(), crate::instructions::OpCode::Getstringtbl => todo!(), crate::instructions::OpCode::Setstringtbl => todo!(), crate::instructions::OpCode::Getiosys => todo!(), crate::instructions::OpCode::Setiosys => todo!(), crate::instructions::OpCode::Linearsearch => todo!(), crate::instructions::OpCode::Binarysearch => todo!(), crate::instructions::OpCode::Linkedsearch => todo!(), crate::instructions::OpCode::Mzero => todo!(), crate::instructions::OpCode::Mcopy => todo!(), crate::instructions::OpCode::Malloc => todo!(), crate::instructions::OpCode::Mfree => todo!(), crate::instructions::OpCode::Accelfunc => todo!(), crate::instructions::OpCode::Accelparam => todo!(), crate::instructions::OpCode::Numtof => todo!(), crate::instructions::OpCode::Ftonumz => todo!(), crate::instructions::OpCode::Ftonumn => todo!(), crate::instructions::OpCode::Ceil => todo!(), crate::instructions::OpCode::Floor => todo!(), crate::instructions::OpCode::Fadd => todo!(), crate::instructions::OpCode::Fsub => todo!(), crate::instructions::OpCode::Fmul => todo!(), crate::instructions::OpCode::Fdiv => todo!(), crate::instructions::OpCode::Fmod => todo!(), crate::instructions::OpCode::Sqrt => todo!(), crate::instructions::OpCode::Exp => todo!(), crate::instructions::OpCode::Log => todo!(), crate::instructions::OpCode::Pow => todo!(), crate::instructions::OpCode::Sin => todo!(), crate::instructions::OpCode::Cos => todo!(), crate::instructions::OpCode::Tan => todo!(), crate::instructions::OpCode::Asin => todo!(), crate::instructions::OpCode::Acos => todo!(), crate::instructions::OpCode::Atan => todo!(), crate::instructions::OpCode::Atan2 => todo!(), crate::instructions::OpCode::Jfeq => todo!(), crate::instructions::OpCode::Jfne => todo!(), crate::instructions::OpCode::Jflt => todo!(), crate::instructions::OpCode::Jfle => todo!(), crate::instructions::OpCode::Jfgt => todo!(), crate::instructions::OpCode::Jfge => todo!(), crate::instructions::OpCode::Jisnan => todo!(), crate::instructions::OpCode::Jisinf => todo!(), crate::instructions::OpCode::Numtod => todo!(), crate::instructions::OpCode::Dtonumz => todo!(), crate::instructions::OpCode::Dtonumn => todo!(), crate::instructions::OpCode::Ftod => todo!(), crate::instructions::OpCode::Dtof => todo!(), crate::instructions::OpCode::Dceil => todo!(), crate::instructions::OpCode::Dfloor => todo!(), crate::instructions::OpCode::Dadd => todo!(), crate::instructions::OpCode::Dsub => todo!(), crate::instructions::OpCode::Dmul => todo!(), crate::instructions::OpCode::Ddiv => todo!(), crate::instructions::OpCode::Dmodr => todo!(), crate::instructions::OpCode::Dmodq => todo!(), crate::instructions::OpCode::Dsqrt => todo!(), crate::instructions::OpCode::Dexp => todo!(), crate::instructions::OpCode::Dlog => todo!(), crate::instructions::OpCode::Dpow => todo!(), crate::instructions::OpCode::Dsin => todo!(), crate::instructions::OpCode::Dcos => todo!(), crate::instructions::OpCode::Dtan => todo!(), crate::instructions::OpCode::Dasin => todo!(), crate::instructions::OpCode::Dacos => todo!(), crate::instructions::OpCode::Datan => todo!(), crate::instructions::OpCode::Datan2 => todo!(), crate::instructions::OpCode::Jdeq => todo!(), crate::instructions::OpCode::Jdne => todo!(), crate::instructions::OpCode::Jdlt => todo!(), crate::instructions::OpCode::Jdle => todo!(), crate::instructions::OpCode::Jdgt => todo!(), crate::instructions::OpCode::Jdge => todo!(), crate::instructions::OpCode::Jdisnan => todo!(), crate::instructions::OpCode::Jdisinf => todo!(), } } } fn call_function(&mut self, func_addr: FunctionPointer, args: Vec, dest_oper: &Operand) { let (dest_type, dest_addr) = match dest_oper { Operand::Constant(_) => (StubDestType::DoNotStore, 0), Operand::PopStack => (StubDestType::PushOnStack, 0), Operand::FrameIndirect(offset) => (StubDestType::LocalVariable, *offset), Operand::MemIndirect(offset) => (StubDestType::MainMemory, *offset), Operand::MemRamIndirect(offset) => (StubDestType::MainMemory, self.mem_writeable + *offset), }; self.create_return_stub(dest_type, dest_addr, self.reg_program, self.reg_frame); let new_program_counter = self.create_stack_frame(func_addr, args); self.reg_program = new_program_counter; } fn create_return_stub(&mut self, dest_type: StubDestType, dest_addr: u32, program_counter: MemAddress, frame_pointer: StackAddress) { self.push_stack(dest_type as u32); self.push_stack(dest_addr); self.push_stack(program_counter); self.push_stack(frame_pointer); } // Returns program counter after parsing all the args fn create_stack_frame(&mut self, func: FunctionPointer, mut args: Vec) -> MemAddress { // Reading function signature let func_type = self.get_byte(func, ReadWriteDest::Memory); let args_in_locals = match func_type { 0xC0 => false, 0xC1 => true, _ => panic!("Tried to call object that is not a function: {}", func_type), }; let mut local_pointer = func + 1; let mut format_of_locals = Vec::<(u8, u8)>::new(); loop { let format = ( self.get_byte(local_pointer, ReadWriteDest::Memory), self.get_byte(local_pointer + 1, ReadWriteDest::Memory), ); local_pointer += 2; if format == (0,0) { break } format_of_locals.push(format); }; let opcode_start = local_pointer; // Writing stack frame self.reg_frame = self.reg_stack; let mut header_len = 4 + 4 + format_of_locals.len() as StackAddress * 2; Interpreter::align_ptr(&mut header_len, 16); // Write locals pos self.write_word(self.reg_frame + 4, ReadWriteDest::Stack, header_len); // Write format of locals let mut local_format_ptr = self.reg_frame + 8; for (local_type, local_count) in format_of_locals.iter() { self.write_byte(local_format_ptr, ReadWriteDest::Stack, *local_type); self.write_byte(local_format_ptr + 1, ReadWriteDest::Stack, *local_count); local_format_ptr += 2; } // Write locals let mut local_pos = self.reg_frame+ header_len; for (local_type, local_count) in format_of_locals { Interpreter::align_ptr(&mut local_pos, local_type * 8); for _count in 0..local_count { let value = match args_in_locals { true => if args.len() > 0 {args.remove(0)} else {0} false => 0, }; match local_type { 1 => self.write_byte(local_pos, ReadWriteDest::Stack, value as u8), 2 => self.write_twobyte(local_pos, ReadWriteDest::Stack, value as u16), 4 => self.write_word(local_pos, ReadWriteDest::Stack, value), _ => panic!("Invalid local type {local_type}") } local_pos += local_type as u32; } } // Write frame len Interpreter::align_ptr(&mut local_pos, 32); self.write_word(self.reg_frame, ReadWriteDest::Stack, local_pos - self.reg_frame); self.reg_stack = local_pos; // Handle stack args if !args_in_locals { args.reverse(); for arg in args { println!("Adding value to stack {arg}"); self.push_stack(arg); } } return opcode_start; } fn return_from_function(&mut self, value: u32) { todo!("Return not implemented"); } fn jump_with_offset(&mut self, offset: u32) { if offset == 1 || offset == 0 { return self.return_from_function(offset); } let offset = offset as i32; let destination = if offset < 0 { self.reg_program - -offset as u32 } else { self.reg_program + offset as u32 }; self.reg_program = destination; } fn push_stack(&mut self, value: u32) { self.write_word(self.reg_stack, ReadWriteDest::Stack, value); self.reg_stack += 4; } fn pop_stack(&mut self) -> u32 { self.reg_stack -= 4; self.get_word(self.reg_stack, ReadWriteDest::Stack) } fn eval_source_operand(&mut self, operand: &Operand) -> u32 { match operand { Operand::Constant(value) => *value as u32, Operand::PopStack => self.pop_stack(), Operand::FrameIndirect(offset) => { let start_of_locals = self.get_word(self.reg_frame, ReadWriteDest::Stack); assert!(start_of_locals + offset < self.reg_stack, "Frame offset out of frame"); self.get_word(start_of_locals + offset, ReadWriteDest::Stack) }, Operand::MemIndirect(address) => { // TODO: Handle heap assert!(*address < self.memory.len() as u32 - 4, "Memory offset out of memory"); self.get_word(*address, ReadWriteDest::Memory) }, Operand::MemRamIndirect(offset) => { let address = self.mem_writeable + offset; // TODO: Handle heap assert!(address < self.memory.len() as u32 - 4, "Memory offset out of memory"); self.get_word(address, ReadWriteDest::Memory) }, } } fn store_dest_operand(&mut self, operand: &Operand, value: u32) { match operand { Operand::Constant(0) => (), Operand::Constant(constant_dst) => panic!("Cannot store to constant operand {constant_dst}"), Operand::PopStack => self.push_stack(value), Operand::FrameIndirect(offset) => { let start_of_locals = self.get_word(self.reg_frame, ReadWriteDest::Stack); assert!(start_of_locals + offset < self.reg_stack, "Frame offset out of frame"); self.write_word(start_of_locals + offset, ReadWriteDest::Stack, value) }, Operand::MemIndirect(address) => { assert!(*address >= self.mem_writeable, "Cannot write to ROM"); // TODO: Handle heap assert!(*address < self.memory.len() as u32 - 4, "Memory offset out of memory"); self.write_word(*address, ReadWriteDest::Memory, value) }, Operand::MemRamIndirect(offset) => { let address = self.mem_writeable + offset; // TODO: Handle heap assert!(address < self.memory.len() as u32 - 4, "Memory offset out of memory"); self.write_word(address, ReadWriteDest::Memory, value) }, } } fn get_byte(&self, addr: u32, src: ReadWriteDest) -> u8 { let addr = addr as usize; let buffer = match src { ReadWriteDest::Stack => &self.stack, ReadWriteDest::Memory => &self.memory, }; buffer[addr] } fn get_twobyte(&self, addr: u32, src: ReadWriteDest) -> u16 { let addr = addr as usize; let buffer = match src { ReadWriteDest::Stack => &self.stack, ReadWriteDest::Memory => &self.memory, }; u16::from_be_bytes(buffer[addr..addr+2].try_into().unwrap()) } fn get_word(&self, addr: u32, src: ReadWriteDest) -> u32 { let addr = addr as usize; let buffer = match src { ReadWriteDest::Stack => &self.stack, ReadWriteDest::Memory => &self.memory, }; u32::from_be_bytes(buffer[addr..addr+4].try_into().unwrap()) } fn write_byte(&mut self, addr: u32, dst: ReadWriteDest, value: u8) { let addr = addr as usize; let buffer = match dst { ReadWriteDest::Stack => &mut self.stack, ReadWriteDest::Memory => &mut self.memory, }; buffer[addr] = value; } fn write_twobyte(&mut self, addr: u32, dst: ReadWriteDest, value: u16) { let addr = addr as usize; let buffer = match dst { ReadWriteDest::Stack => &mut self.stack, ReadWriteDest::Memory => &mut self.memory, }; buffer[addr..addr+2].copy_from_slice(&value.to_be_bytes()); } fn write_word(&mut self, addr: u32, dst: ReadWriteDest, value: u32) { let addr = addr as usize; let buffer = match dst { ReadWriteDest::Stack => &mut self.stack, ReadWriteDest::Memory => &mut self.memory, }; buffer[addr..addr+4].copy_from_slice(&value.to_be_bytes()); } fn align_ptr(ptr: &mut u32, align_size: u8) { let modulo = match align_size { 8 => 1, 16 => 2, 32 => 4, _ => panic!("Invalid alignment: {align_size}"), }; let bytes_off = *ptr % modulo; *ptr += bytes_off; } }