summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAshelyn Rose <git@ashen.earth>2024-12-06 15:10:31 -0700
committerAshelyn Rose <git@ashen.earth>2024-12-06 15:10:31 -0700
commitf5cebaf8feed5f5980df63a6c5b6a64d5d5ba092 (patch)
treeb3cbadda8ecb13c77fae245917ac80e4ef2d4593 /src
parentb902c11b89edc2d4e91d4bd1582792c9303bf874 (diff)
Instruction parsing
Diffstat (limited to 'src')
-rw-r--r--src/instructions.rs464
-rw-r--r--src/interpreter.rs30
-rw-r--r--src/main.rs1
3 files changed, 472 insertions, 23 deletions
diff --git a/src/instructions.rs b/src/instructions.rs
new file mode 100644
index 0000000..8fa409c
--- /dev/null
+++ b/src/instructions.rs
@@ -0,0 +1,464 @@
+use core::panic;
+
+use crate::interpreter::{MemAddress, StackAddress};
+
+#[derive(Debug)]
+pub struct Instruction {
+    pub operation: OpCode,
+    pub operands: Vec<Operand>
+}
+
+enum Nybble {
+    High,
+    Low,
+}
+
+#[derive(PartialEq, Debug)]
+pub enum Operand {
+    Constant(i32),
+    PopStack,
+    FrameIndirect(StackAddress),
+    MemIndirect(MemAddress),
+    MemRamIndirect(MemAddress),
+}
+
+impl Instruction {
+    pub fn read<'a, I>(bytes: &mut I) -> (Instruction, u32)
+    where
+        I: IntoIterator<Item = &'a u8> + Copy
+    {
+        let mut bytes = bytes.into_iter().enumerate().peekable();
+        let instruction_length = OpCode::get_length(*bytes.peek().unwrap().1);
+        let mut bytes = bytes.into_iter();
+        let instruction_bytes = (0..instruction_length).into_iter().map(|_n| *bytes.next().unwrap().1).collect();
+        let operation = OpCode::from_bytes(instruction_bytes);
+
+        let operand_count = operation.get_operand_count();
+        println!("Parsing {operand_count} operands for {operation:?}");
+        let operand_bytes = (operand_count + 1) / 2;
+        let skip_last_half = operand_bytes != operand_count / 2;
+        println!("Reading {operand_bytes} bytes for operands and {}", if skip_last_half {"ignoring the last 4 bits"} else {"keeping the last 4 bits"});
+        let operand_bytes = (0..operand_bytes).into_iter().map(|_n| *bytes.next().unwrap().1);
+        println!("- check: byte array is {} long", operand_bytes.len());
+        let halfbytes : Vec<_> = operand_bytes.flat_map(|byte| vec![(byte, Nybble::Low), (byte, Nybble::High)]).collect();
+        println!("- check: halfbyte array is {} long", halfbytes.len());
+
+        // Just ensure other borrows to the byte array have closed by rebinding it
+        // This verifies we're at the operand data position
+        let mut data_bytes = bytes;
+
+        let mut operands : Vec<Operand> = halfbytes.iter().map(|(byte, nybble)| {
+            let value = match nybble {
+                Nybble::High => byte >> 4,
+                Nybble::Low => byte & 0x0f,
+            };
+
+            match value {
+                0 => Operand::Constant(0),
+                1 => Operand::Constant(i8::from_be_bytes([*data_bytes.next().unwrap().1]) as i32),
+                2 => Operand::Constant(i16::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1]) as i32),
+                3 => Operand::Constant(i32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
+                4 => panic!("Invalid operand addressing mode: {value:x}"),
+                5 => Operand::MemIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])),
+                6 => Operand::MemIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
+                7 => Operand::MemIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
+                8 => Operand::PopStack,
+                9 => Operand::FrameIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])),
+                0xa => Operand::FrameIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
+                0xb => Operand::FrameIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
+                0xc => panic!("Invalid operand addressing mode: {value:x}"),
+                0xd => Operand::MemRamIndirect(u32::from_be_bytes([0, 0, 0, *data_bytes.next().unwrap().1])),
+                0xe => Operand::MemRamIndirect(u32::from_be_bytes([0, 0, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
+                0xf => Operand::MemRamIndirect(u32::from_be_bytes([*data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1, *data_bytes.next().unwrap().1])),
+                _ => unreachable!()
+            }
+        }).collect();
+
+        // Should only trim it off if it was all 0
+        if skip_last_half && operands.len() > 0 {
+            assert!(*operands.last().unwrap() == Operand::Constant(0));
+            operands.remove(operands.len() - 1);
+        }
+
+        // Return instruction along with the number of bytes we've read
+        (Instruction {
+            operation,
+            operands,
+        }, data_bytes.peek().unwrap().0 as u32)
+    }
+}
+
+impl OpCode {
+    fn get_length(first_byte: u8) -> usize {
+        let length = match first_byte >> 6 {
+            0b00 | 0b01 => 1,
+            0b10 => 2,
+            0b11 => 4,
+            _ => unreachable!()
+        };
+        println!("Opcode first byte: {first_byte:02x}, instruction length: {length} bytes");
+        return length
+    }
+
+    fn from_bytes(bytes: Vec<u8>) -> Self {
+        let (padded_bytes, mask) = match bytes.len() {
+            1 => ([0,0,0,bytes[0]], 0),
+            2 => ([0,0,bytes[0],bytes[1]], 0x8000),
+            4 => ([bytes[0],bytes[1],bytes[2],bytes[3]], 0xC0000000),
+            _ => panic!("Invalid opcode byte array")
+        };
+        println!("Read {} bytes: {bytes:?}", bytes.len());
+
+        let instruction = u32::from_be_bytes(padded_bytes) & !mask;
+        println!("Transmuting opcode 0x{instruction:03x}");
+        return unsafe { std::mem::transmute(instruction) }
+    }
+
+    fn get_operand_count(&self) -> usize {
+        match *self {
+            // Misc
+            OpCode::Nop => 0,
+            OpCode::Gestalt => 3,
+            OpCode::Debugtrap => 1,
+            OpCode::Glk => 3,
+
+            // Integer math
+            OpCode::Add => 3,
+            OpCode::Sub => 3,
+            OpCode::Mul => 3,
+            OpCode::Div => 3,
+            OpCode::Mod => 3,
+            OpCode::Neg => 2,
+            OpCode::Bitand => 3,
+            OpCode::Bitor => 3,
+            OpCode::Bitxor => 3,
+            OpCode::Bitnot => 3,
+            OpCode::Shiftl => 3,
+            OpCode::Sshiftr => 3,
+            OpCode::Ushiftr => 3,
+
+            // Branches
+            OpCode::Jump => 1,
+            OpCode::Jz => 2,
+            OpCode::Jnz => 2,
+            OpCode::Jeq => 3,
+            OpCode::Jne => 3,
+            OpCode::Jlt => 3,
+            OpCode::Jge => 3,
+            OpCode::Jgt => 3,
+            OpCode::Jle => 3,
+            OpCode::Jltu => 3,
+            OpCode::Jgeu => 3,
+            OpCode::Jgtu => 3,
+            OpCode::Jleu => 3,
+            OpCode::Jumpabs => 1,
+
+            // Moving data
+            OpCode::Copy => 2,
+            OpCode::Copys => 2,
+            OpCode::Copyb => 2,
+            OpCode::Sexs => 2,
+            OpCode::Sexb => 2,
+
+            // Array data
+            OpCode::Aload => 3,
+            OpCode::Aloads => 3,
+            OpCode::Aloadb => 3,
+            OpCode::Aloadbit => 3,
+            OpCode::Astore => 3,
+            OpCode::Astores => 3,
+            OpCode::Astoreb => 3,
+            OpCode::Astorebit => 3,
+
+            // Stack operations
+            OpCode::Stkcount => 1,
+            OpCode::Stkpeek => 2,
+            OpCode::Stkswap => 0,
+            OpCode::Stkroll => 2,
+            OpCode::Stkcopy => 1,
+
+            // Functions
+            OpCode::Call => 3,
+            OpCode::Callf => 2,
+            OpCode::Callfi => 3,
+            OpCode::Callfii => 4,
+            OpCode::Callfiii => 5,
+            OpCode::Return => 1,
+            OpCode::Tailcall => 2,
+
+            // Continuations
+            OpCode::Catch => 2,
+            OpCode::Throw => 2,
+
+            // Memory
+            OpCode::Getmemsize => 1,
+            OpCode::Setmemsize => 2,
+            OpCode::Malloc => 2,
+            OpCode::Mfree => 1,
+
+            // Game state
+            OpCode::Quit => 0,
+            OpCode::Restart => 0,
+            OpCode::Save => 2,
+            OpCode::Restore => 2,
+            OpCode::Saveundo => 1,
+            OpCode::Restoreundo => 1,
+            OpCode::Hasundo => 1,
+            OpCode::Discardundo => 0,
+            OpCode::Protect => 2,
+            OpCode::Verify => 1,
+
+            // Output
+            OpCode::Getiosys => 2,
+            OpCode::Setiosys => 2,
+            OpCode::Streamchar => 1,
+            OpCode::Streamunichar => 1,
+            OpCode::Streamnum => 1,
+            OpCode::Streamstr => 1,
+            OpCode::Getstringtbl => 1,
+            OpCode::Setstringtbl => 1,
+
+            // Floating point math
+            OpCode::Numtof => 2,
+            OpCode::Ftonumz => 2,
+            OpCode::Ftonumn => 2,
+            OpCode::Fadd => 3,
+            OpCode::Fsub => 3,
+            OpCode::Fmul => 3,
+            OpCode::Fdiv => 3,
+            OpCode::Fmod => 4,
+            OpCode::Ceil => 2,
+            OpCode::Floor => 2,
+            OpCode::Sqrt => 2,
+            OpCode::Exp => 2,
+            OpCode::Log => 2,
+            OpCode::Pow => 3,
+            OpCode::Sin => 2,
+            OpCode::Cos => 2,
+            OpCode::Tan => 2,
+            OpCode::Asin => 2,
+            OpCode::Acos => 2,
+            OpCode::Atan => 2,
+            OpCode::Atan2 => 3,
+
+            // Double precision math
+            OpCode::Numtod => 3,
+            OpCode::Dtonumz => 3,
+            OpCode::Dtonumn => 3,
+            OpCode::Ftod => 3,
+            OpCode::Dtof => 3,
+            OpCode::Dadd => 6,
+            OpCode::Dsub => 6,
+            OpCode::Dmul => 6,
+            OpCode::Ddiv => 6,
+            OpCode::Dmodr => 6,
+            OpCode::Dmodq => 6,
+            OpCode::Dceil => 4,
+            OpCode::Dfloor => 4,
+            OpCode::Dsqrt => 4,
+            OpCode::Dexp => 4,
+            OpCode::Dlog => 4,
+            OpCode::Dpow => 6,
+            OpCode::Dsin => 4,
+            OpCode::Dcos => 4,
+            OpCode::Dtan => 4,
+            OpCode::Dasin => 4,
+            OpCode::Dacos => 4,
+            OpCode::Datan => 4,
+            OpCode::Datan2 => 4,
+
+            // Floating point comparisons
+            OpCode::Jisnan => 2,
+            OpCode::Jisinf => 2,
+            OpCode::Jfeq => 4,
+            OpCode::Jfne => 4,
+            OpCode::Jflt => 3,
+            OpCode::Jfle => 3,
+            OpCode::Jfgt => 3,
+            OpCode::Jfge => 3,
+
+            // Double precision comparisons
+            OpCode::Jdisnan => 3,
+            OpCode::Jdisinf => 3,
+            OpCode::Jdeq => 7,
+            OpCode::Jdne => 7,
+            OpCode::Jdlt => 5,
+            OpCode::Jdle => 5,
+            OpCode::Jdgt => 5,
+            OpCode::Jdge => 5,
+
+            // RNG
+            OpCode::Random => 2,
+            OpCode::Setrandom => 1,
+
+            // Block copy and clear
+            OpCode::Mzero => 2,
+            OpCode::Mcopy => 3,
+
+            // Searching
+            OpCode::Linearsearch => 8,
+            OpCode::Binarysearch => 8,
+            OpCode::Linkedsearch => 7,
+
+            // Accelerated functions
+            OpCode::Accelfunc => 2,
+            OpCode::Accelparam => 2,
+        }
+    }
+}
+
+
+#[repr(u32)]
+#[derive(Debug)]
+pub enum OpCode {
+    Nop = 0x00,
+    Add = 0x10,
+    Sub = 0x11,
+    Mul = 0x12,
+    Div = 0x13,
+    Mod = 0x14,
+    Neg = 0x15,
+    Bitand = 0x18,
+    Bitor = 0x19,
+    Bitxor = 0x1A,
+    Bitnot = 0x1B,
+    Shiftl = 0x1C,
+    Sshiftr = 0x1D,
+    Ushiftr = 0x1E,
+    Jump = 0x20,
+    Jz = 0x22,
+    Jnz = 0x23,
+    Jeq = 0x24,
+    Jne = 0x25,
+    Jlt = 0x26,
+    Jge = 0x27,
+    Jgt = 0x28,
+    Jle = 0x29,
+    Jltu = 0x2A,
+    Jgeu = 0x2B,
+    Jgtu = 0x2C,
+    Jleu = 0x2D,
+    Call = 0x30,
+    Return = 0x31,
+    Catch = 0x32,
+    Throw = 0x33,
+    Tailcall = 0x34,
+    Copy = 0x40,
+    Copys = 0x41,
+    Copyb = 0x42,
+    Sexs = 0x44,
+    Sexb = 0x45,
+    Aload = 0x48,
+    Aloads = 0x49,
+    Aloadb = 0x4A,
+    Aloadbit = 0x4B,
+    Astore = 0x4C,
+    Astores = 0x4D,
+    Astoreb = 0x4E,
+    Astorebit = 0x4F,
+    Stkcount = 0x50,
+    Stkpeek = 0x51,
+    Stkswap = 0x52,
+    Stkroll = 0x53,
+    Stkcopy = 0x54,
+    Streamchar = 0x70,
+    Streamnum = 0x71,
+    Streamstr = 0x72,
+    Streamunichar = 0x73,
+    Gestalt = 0x100,
+    Debugtrap = 0x101,
+    Getmemsize = 0x102,
+    Setmemsize = 0x103,
+    Jumpabs = 0x104,
+    Random = 0x110,
+    Setrandom = 0x111,
+    Quit = 0x120,
+    Verify = 0x121,
+    Restart = 0x122,
+    Save = 0x123,
+    Restore = 0x124,
+    Saveundo = 0x125,
+    Restoreundo = 0x126,
+    Protect = 0x127,
+    Hasundo = 0x128,
+    Discardundo = 0x129,
+    Glk = 0x130,
+    Getstringtbl = 0x140,
+    Setstringtbl = 0x141,
+    Getiosys = 0x148,
+    Setiosys = 0x149,
+    Linearsearch = 0x150,
+    Binarysearch = 0x151,
+    Linkedsearch = 0x152,
+    Callf = 0x160,
+    Callfi = 0x161,
+    Callfii = 0x162,
+    Callfiii = 0x163,
+    Mzero = 0x170,
+    Mcopy = 0x171,
+    Malloc = 0x178,
+    Mfree = 0x179,
+    Accelfunc = 0x180,
+    Accelparam = 0x181,
+    Numtof = 0x190,
+    Ftonumz = 0x191,
+    Ftonumn = 0x192,
+    Ceil = 0x198,
+    Floor = 0x199,
+    Fadd = 0x1A0,
+    Fsub = 0x1A1,
+    Fmul = 0x1A2,
+    Fdiv = 0x1A3,
+    Fmod = 0x1A4,
+    Sqrt = 0x1A8,
+    Exp = 0x1A9,
+    Log = 0x1AA,
+    Pow = 0x1AB,
+    Sin = 0x1B0,
+    Cos = 0x1B1,
+    Tan = 0x1B2,
+    Asin = 0x1B3,
+    Acos = 0x1B4,
+    Atan = 0x1B5,
+    Atan2 = 0x1B6,
+    Jfeq = 0x1C0,
+    Jfne = 0x1C1,
+    Jflt = 0x1C2,
+    Jfle = 0x1C3,
+    Jfgt = 0x1C4,
+    Jfge = 0x1C5,
+    Jisnan = 0x1C8,
+    Jisinf = 0x1C9,
+    Numtod = 0x200,
+    Dtonumz = 0x201,
+    Dtonumn = 0x202,
+    Ftod = 0x203,
+    Dtof = 0x204,
+    Dceil = 0x208,
+    Dfloor = 0x209,
+    Dadd = 0x210,
+    Dsub = 0x211,
+    Dmul = 0x212,
+    Ddiv = 0x213,
+    Dmodr = 0x214,
+    Dmodq = 0x215,
+    Dsqrt = 0x218,
+    Dexp = 0x219,
+    Dlog = 0x21A,
+    Dpow = 0x21B,
+    Dsin = 0x220,
+    Dcos = 0x221,
+    Dtan = 0x222,
+    Dasin = 0x223,
+    Dacos = 0x224,
+    Datan = 0x225,
+    Datan2 = 0x226,
+    Jdeq = 0x230,
+    Jdne = 0x231,
+    Jdlt = 0x232,
+    Jdle = 0x233,
+    Jdgt = 0x234,
+    Jdge = 0x235,
+    Jdisnan = 0x238,
+    Jdisinf = 0x239,
+}
diff --git a/src/interpreter.rs b/src/interpreter.rs
index bc51754..39a33a8 100644
--- a/src/interpreter.rs
+++ b/src/interpreter.rs
@@ -1,6 +1,6 @@
 use core::panic;
 
-use crate::glk::Glk;
+use crate::{glk::Glk, instructions::Instruction};
 
 pub type MemAddress = u32;
 pub type StackAddress = u32;
@@ -18,19 +18,6 @@ enum StubDestType {
     ResumeUnicodeString = 14,
 }
 
-enum Nybble {
-    High,
-    Low,
-}
-
-enum Operand {
-    Constant(i32),
-    PopStack,
-    FrameIndirect(StackAddress),
-    MemIndirect(MemAddress),
-    MemRamIndirect(MemAddress),
-}
-
 enum ReadWriteDest {
     Stack,
     Memory,
@@ -100,11 +87,12 @@ impl Interpreter {
         self.reg_program = self.create_stack_frame(entry_func, Vec::new());
 
         loop {
-            let instruction = self.load_program_instruction();
+            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;
 
-            match instruction {
-                _ => todo!("Haven't done any of the instruction loading yet")
-            }
+            println!("Parsed instruction {instruction:?}");
         }
     }
 
@@ -144,7 +132,7 @@ impl Interpreter {
             format_of_locals.push(format);
         };
 
-        let opcode_start = local_pointer + 1;
+        let opcode_start = local_pointer + 2;
 
         // Writing stack frame
         self.reg_frame = self.reg_stack;
@@ -214,10 +202,6 @@ impl Interpreter {
         return opcode_start;
     }
 
-    fn load_program_instruction(&mut self) {
-
-    }
-
     fn push_stack(&mut self, value: u32) {
         self.write_word(self.reg_stack, ReadWriteDest::Stack, value);
         self.reg_stack += 4;
diff --git a/src/main.rs b/src/main.rs
index 33df203..7866a7a 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,6 +1,7 @@
 #![feature(ascii_char, iter_array_chunks)]
 mod interpreter;
 mod glk;
+mod instructions;
 use interpreter::Interpreter;
 
 use std::{env, fs, process::exit};