summary refs log tree commit diff
path: root/src/instructions.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/instructions.rs')
-rw-r--r--src/instructions.rs464
1 files changed, 464 insertions, 0 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,
+}