summary refs log tree commit diff
path: root/src/interpreter.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/interpreter.rs')
-rw-r--r--src/interpreter.rs471
1 files changed, 451 insertions, 20 deletions
diff --git a/src/interpreter.rs b/src/interpreter.rs
index 39a33a8..800ebf0 100644
--- a/src/interpreter.rs
+++ b/src/interpreter.rs
@@ -1,6 +1,6 @@
 use core::panic;
 
-use crate::{glk::Glk, instructions::Instruction};
+use crate::{glk::Glk, instructions::{Instruction, Operand}};
 
 pub type MemAddress = u32;
 pub type StackAddress = u32;
@@ -92,20 +92,397 @@ impl Interpreter {
             let (instruction, bytes_read) = Instruction::read(&mut instruction_slice);
             self.reg_program += bytes_read;
 
-            println!("Parsed instruction {instruction:?}");
+            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::<Vec<_>>();
+                    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 create_return_stub(&mut self, dest_type: StubDestType, dest_addr: u32, program_counter: MemAddress, frame_pointer: StackAddress) {
+    fn call_function(&mut self, func_addr: FunctionPointer, args: Vec<u32>, 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<u32>) -> MemAddress {
-        println!("create_stack_frame: Loading function from {func:x}");
-
-        println!("Initial frame ptr: {}", self.reg_frame);
-        println!("Initial stack ptr: {}", self.reg_stack);
 
         // Reading function signature
         let func_type = self.get_byte(func, ReadWriteDest::Memory);
@@ -132,7 +509,7 @@ impl Interpreter {
             format_of_locals.push(format);
         };
 
-        let opcode_start = local_pointer + 2;
+        let opcode_start = local_pointer;
 
         // Writing stack frame
         self.reg_frame = self.reg_stack;
@@ -140,13 +517,11 @@ impl Interpreter {
         Interpreter::align_ptr(&mut header_len, 16);
 
         // Write locals pos
-        println!("Writing locals pos ({header_len}) to frame + 4 ({})", self.reg_frame + 4);
         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() {
-            println!("Writing local format ({}, {}) to {}", local_type, local_count, local_format_ptr);
             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;
@@ -163,8 +538,6 @@ impl Interpreter {
                     false => 0,
                 };
 
-                println!("Writing local variable ({}, {} bytes) to {}", value, local_type, local_pos);
-
                 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),
@@ -178,7 +551,6 @@ impl Interpreter {
         
         // Write frame len
         Interpreter::align_ptr(&mut local_pos, 32);
-        println!("Writing frame len ({}) to frame ptr ({})", local_pos - self.reg_frame, self.reg_frame);
         self.write_word(self.reg_frame, ReadWriteDest::Stack, local_pos - self.reg_frame);
         self.reg_stack = local_pos;
 
@@ -191,15 +563,26 @@ impl Interpreter {
             }
         }
 
-        println!("Ending frame ptr: {}", self.reg_frame);
-        println!("Ending stack ptr: {}", self.reg_stack);
+        return opcode_start;
+    }
 
-        for [a, b, c, d] in self.stack[self.reg_frame as usize..self.reg_stack as usize].iter()
-            .array_chunks::<4>() {
-                println!("{a:02x} {b:02x} {c:02x} {d:02x} ({})", u32::from_be_bytes([*a,*b,*c,*d]));
-            }
+    fn return_from_function(&mut self, value: u32) {
+        todo!("Return not implemented");
+    }
 
-        return opcode_start;
+    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) {
@@ -212,6 +595,54 @@ impl Interpreter {
         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 {