diff --git a/redox-core/src/compiling/compiler.rs b/redox-core/src/compiling/compiler.rs index 82e3bbc..6d6193d 100644 --- a/redox-core/src/compiling/compiler.rs +++ b/redox-core/src/compiling/compiler.rs @@ -4,6 +4,7 @@ use crate::{ ins::{instruction::Instruction, op_codes::OpCode}, parsing::asm_parser::AsmParser, reg::registers::RegisterId, + utils, }; pub struct Compiler { @@ -47,11 +48,12 @@ impl Compiler { /// # Arguments /// /// * `assembly` - A string slice containing the string to be parsed and compiled. + /// * `optimize` - Should optimizations be performed on the assembly during compilation? /// /// # Returns /// /// A slice of bytes containing the compiled bytecode. - pub fn compile_assembly(&mut self, assembly: &str) -> &[u8] { + pub fn compile_assembly(&mut self, assembly: &str, optimize: bool) -> &[u8] { let mut parser = AsmParser::new(); parser.parse(assembly); @@ -61,14 +63,16 @@ impl Compiler { self.calculate_label_positions(&instructions); - // TODO - optimizations should ideally go here. + if optimize { + // TODO - optimizations should ideally go here. + } // TODO - the calculate label positions method should be called here again. // TODO - this is because the instructions may have changed, altering the relative positions of the labels. self.handle_labelled_instructions(&mut instructions); - println!("{instructions:?}"); + //println!("{instructions:?}"); self.compile(&instructions); @@ -340,7 +344,28 @@ impl Compiler { /// /// * `opcode` - The [`OpCode`] to be written. fn write_opcode(&mut self, opcode: OpCode) { - self.write_u32(opcode as u32); + let op = opcode as u32; + let bits = op.to_le_bytes(); + + if utils::is_bit_set(op, 23) { + // 4 byte opcodes + self.write_u8(bits[0]); + self.write_u8(bits[1]); + self.write_u8(bits[2]); + self.write_u8(bits[3]); + } else if utils::is_bit_set(op, 15) { + // 3 byte opcodes + self.write_u8(bits[0]); + self.write_u8(bits[1]); + self.write_u8(bits[2]); + } else if utils::is_bit_set(op, 7) { + // 2 byte opcodes + self.write_u8(bits[0]); + self.write_u8(bits[1]); + } else { + // 1 byte opcodes + self.write_u8(bits[0]); + } } /// Write a register ID into the byte sequence. diff --git a/redox-core/src/cpu.rs b/redox-core/src/cpu.rs index 8a4b408..a3c0fd9 100644 --- a/redox-core/src/cpu.rs +++ b/redox-core/src/cpu.rs @@ -1029,10 +1029,10 @@ impl Cpu { } I::JumpAbsU32Reg(reg) => { // jmp cs - let shift_by = self.registers.read_reg_u32(reg, privilege); + let addr = self.registers.read_reg_u32(reg, privilege); // Set the instruction pointer to the jump address. - self.set_instruction_pointer(shift_by); + self.set_instruction_pointer(addr); } /******** [Data Instructions] ********/ @@ -2162,16 +2162,16 @@ mod tests_cpu { let instructions = &[ // Write the handler address into the IVT. - Instruction::MovU32ImmMemSimple(base_offset + 44, 255 * 4), // Starts at [base]. Length = 12. + Instruction::MovU32ImmMemSimple(base_offset + 33, 255 * 4), // Starts at [base]. Length = 10. // Mask every interrupt. - Instruction::MovU32ImmU32Reg(0, RegisterId::EIM), // Starts at [base + 12]. Length = 9. + Instruction::MovU32ImmU32Reg(0, RegisterId::EIM), // Starts at [base + 10]. Length = 7. // And now unmask the specific interrupt we want to enable. - Instruction::UnmaskInterrupt(0xff), // Starts at [base + 21]. Length = 5. - Instruction::Int(0xff), // Starts at [base + 26]. Length = 5. - Instruction::AddU32ImmU32Reg(0x5, RegisterId::EAX), // Starts at [base + 31]. Length = 9. - Instruction::Halt, // Starts at [base + 40]. Length = 4. + Instruction::UnmaskInterrupt(0xff), // Starts at [base + 17]. Length = 4. + Instruction::Int(0xff), // Starts at [base + 21]. Length = 3. + Instruction::AddU32ImmU32Reg(0x5, RegisterId::EAX), // Starts at [base + 24]. Length = 7. + Instruction::Halt, // Starts at [base + 31]. Length = 2. /***** INT_FF - Interrupt handler for interrupt 0xff starts here. *****/ - Instruction::MovU32ImmU32Reg(0x64, RegisterId::EAX), // Starts at [base + 44]. Length = 9. + Instruction::MovU32ImmU32Reg(0x64, RegisterId::EAX), // Starts at [base + 33]. Instruction::IntRet, ]; @@ -2261,14 +2261,14 @@ mod tests_cpu { #[test] fn test_call_return_from_cs_with_offset() { let instructions = &[ - Instruction::PushU32Imm(3), // Starts at [ECS]. Length = 8. This should remain in place. - Instruction::PushU32Imm(2), // Starts at [ECS + 8]. Length = 8. Subroutine argument 1. - Instruction::PushU32Imm(1), // Starts at [ECS + 16]. Length = 8. The number of arguments. - Instruction::CallRelCSU32Offset(45, String::default()), // Starts at [ECS + 24]. Length = 8. - Instruction::AddU32ImmU32Reg(100, RegisterId::EAX), // Starts at [ECS + 32]. Length = 9. - Instruction::Halt, // Starts at [ECS + 41]. Length = 4. + Instruction::PushU32Imm(3), // Starts at [ECS]. Length = 5. This should remain in place. + Instruction::PushU32Imm(2), // Starts at [ECS + 5]. Length = 5. Subroutine argument 1. + Instruction::PushU32Imm(1), // Starts at [ECS + 10]. Length = 5. The number of arguments. + Instruction::CallRelCSU32Offset(30, String::default()), // Starts at [ECS + 15]. Length = 6. + Instruction::AddU32ImmU32Reg(100, RegisterId::EAX), // Starts at [ECS + 21]. Length = 7. + Instruction::Halt, // Starts at [ECS + 28]. Length = 2. /***** FUNC_AAAA - Subroutine starts here. *****/ - Instruction::PushU32Imm(5), // Starts at [ECS + 45]. + Instruction::PushU32Imm(5), // Starts at [ECS + 30]. Instruction::PopU32ToU32Reg(RegisterId::EAX), Instruction::RetArgsU32, ]; @@ -2308,14 +2308,14 @@ mod tests_cpu { #[test] fn test_call_return_with_offset_reg() { let instructions = &[ - Instruction::PushU32Imm(3), // Starts at [ECS]. Length = 8. This should remain in place. - Instruction::PushU32Imm(2), // Starts at [ECS + 8]. Length = 8. Subroutine argument 1. - Instruction::PushU32Imm(1), // Starts at [ECS + 16]. Length = 8. The number of arguments. - Instruction::CallRelU32RegU32Offset(46, RegisterId::ECS), // Starts at [ECS + 24]. Length = 9. - Instruction::AddU32ImmU32Reg(100, RegisterId::EAX), // Starts at [ECS + 33]. Length = 9. - Instruction::Halt, // Starts at [ECS + 42]. Length = 4. + Instruction::PushU32Imm(3), // Starts at [ECS]. Length = 5. This should remain in place. + Instruction::PushU32Imm(2), // Starts at [ECS + 5]. Length = 5. Subroutine argument 1. + Instruction::PushU32Imm(1), // Starts at [ECS + 10]. Length = 5. The number of arguments. + Instruction::CallRelU32RegU32Offset(31, RegisterId::ECS), // Starts at [ECS + 15]. Length = 7. + Instruction::AddU32ImmU32Reg(100, RegisterId::EAX), // Starts at [ECS + 22]. Length = 7. + Instruction::Halt, // Starts at [ECS + 29]. Length = 2. /***** FUNC_AAAA - Subroutine starts here. *****/ - Instruction::PushU32Imm(5), // Starts at [ECS + 46]. + Instruction::PushU32Imm(5), // Starts at [ECS + 31]. Instruction::PopU32ToU32Reg(RegisterId::EAX), Instruction::RetArgsU32, ]; @@ -2357,17 +2357,17 @@ mod tests_cpu { let base_offset = vm::MIN_USER_SEGMENT_SIZE as u32; let instructions = &[ - Instruction::MovU32ImmU32Reg(base_offset + 51, RegisterId::E8X), // Starts at [base]. Length = 9. This should remain in place. - Instruction::PushU32Imm(3), // Starts at [base + 9]. Length = 8. This should remain in place. - Instruction::PushU32Imm(2), // Starts at [base + 17]. Length = 8. Subroutine argument 1. - Instruction::PushU32Imm(1), // Starts at [base + 25]. Length = 8. The number of arguments. - Instruction::CallAbsU32Reg(RegisterId::E8X), // Starts at [base + 33]. Length = 5. - Instruction::AddU32ImmU32Reg(100, RegisterId::EAX), // Starts at [base + 38]. Length = 9. - Instruction::Halt, // Starts at [base + 47]. Length = 4. + Instruction::MovU32ImmU32Reg(base_offset + 34, RegisterId::E8X), // Starts at [base]. Length = 7. This should remain in place. + Instruction::PushU32Imm(3), // Starts at [base + 7]. Length = 5. This should remain in place. + Instruction::PushU32Imm(2), // Starts at [base + 12]. Length = 5. Subroutine argument 1. + Instruction::PushU32Imm(1), // Starts at [base + 17]. Length = 5. The number of arguments. + Instruction::CallAbsU32Reg(RegisterId::E8X), // Starts at [base + 22]. Length = 3. + Instruction::AddU32ImmU32Reg(100, RegisterId::EAX), // Starts at [base + 25]. Length = 7. + Instruction::Halt, // Starts at [base + 32]. Length = 2. /***** FUNC_AAAA - Subroutine starts here. *****/ - Instruction::PushU32Imm(5), // Starts at [base + 51]. Length = 8. - Instruction::PopU32ToU32Reg(RegisterId::EAX), // Starts at [base + 59]. Length = 5. - Instruction::RetArgsU32, // Starts at [base + 64]. Length = 4. + Instruction::PushU32Imm(5), // Starts at [base + 34]. Length = 5. + Instruction::PopU32ToU32Reg(RegisterId::EAX), // Starts at [base + 39]. Length = 2. + Instruction::RetArgsU32, // Starts at [base + 41]. Length = 2. ]; let tests = [TestU32::new( @@ -2420,19 +2420,19 @@ mod tests_cpu { ]; let instructions = &[ - Instruction::MovU32ImmU32Reg(test_registers[0].1, test_registers[0].0), // Starts at [base]. Length = 9. - Instruction::MovU32ImmU32Reg(test_registers[1].1, test_registers[1].0), // Starts at [base + 9]. Length = 9. - Instruction::MovU32ImmU32Reg(test_registers[2].1, test_registers[2].0), // Starts at [base + 18]. Length = 9. - Instruction::MovU32ImmU32Reg(test_registers[3].1, test_registers[3].0), // Starts at [base + 27]. Length = 9. - Instruction::MovU32ImmU32Reg(test_registers[4].1, test_registers[4].0), // Starts at [base + 36]. Length = 9. - Instruction::MovU32ImmU32Reg(test_registers[5].1, test_registers[5].0), // Starts at [base + 45]. Length = 9. - Instruction::MovU32ImmU32Reg(test_registers[6].1, test_registers[6].0), // Starts at [base + 54]. Length = 9. + Instruction::MovU32ImmU32Reg(test_registers[0].1, test_registers[0].0), // Starts at [base]. Length = 7. + Instruction::MovU32ImmU32Reg(test_registers[1].1, test_registers[1].0), // Starts at [base + 7]. Length = 7. + Instruction::MovU32ImmU32Reg(test_registers[2].1, test_registers[2].0), // Starts at [base + 14]. Length = 7. + Instruction::MovU32ImmU32Reg(test_registers[3].1, test_registers[3].0), // Starts at [base + 21]. Length = 7. + Instruction::MovU32ImmU32Reg(test_registers[4].1, test_registers[4].0), // Starts at [base + 28]. Length = 7. + Instruction::MovU32ImmU32Reg(test_registers[5].1, test_registers[5].0), // Starts at [base + 35]. Length = 7. + Instruction::MovU32ImmU32Reg(test_registers[6].1, test_registers[6].0), // Starts at [base + 42]. Length = 7. // The number of arguments for the subroutine. - Instruction::PushU32Imm(0), // Starts at [base + 63]. Length = 8. The number of arguments. - Instruction::CallAbsU32Imm(base_offset + 83, String::default()), // Starts at [base + 71]. Length = 8. - Instruction::Halt, // Starts at [base + 79]. Length = 4. + Instruction::PushU32Imm(0), // Starts at [base + 49]. Length = 5. The number of arguments. + Instruction::CallAbsU32Imm(base_offset + 62, String::default()), // Starts at [base + 54]. Length = 6. + Instruction::Halt, // Starts at [base + 60]. Length = 2. /***** FUNC_AAAA - Subroutine starts here. *****/ - Instruction::MovU32ImmU32Reg(0, RegisterId::EBX), // Starts at [base + 83]. + Instruction::MovU32ImmU32Reg(0, RegisterId::EBX), // Starts at [base + 62]. Instruction::MovU32ImmU32Reg(0, RegisterId::ECX), Instruction::MovU32ImmU32Reg(0, RegisterId::EDX), Instruction::MovU32ImmU32Reg(0, RegisterId::E5X), @@ -2471,18 +2471,18 @@ mod tests_cpu { let base_offset = vm::MIN_USER_SEGMENT_SIZE as u32; let instructions = &[ - Instruction::PushU32Imm(3), // Starts at [base]. Length = 8. This should remain in place. - Instruction::PushU32Imm(2), // Starts at [base + 8]. Length = 8. Subroutine argument 1. - Instruction::PushU32Imm(1), // Starts at [base + 16]. Length = 8. The number of arguments. - Instruction::CallAbsU32Imm(base_offset + 36, String::default()), // Starts at [base + 24]. Length = 8. - Instruction::Halt, // Starts at [base + 32]. Length = 4. + Instruction::PushU32Imm(3), // Starts at [base]. Length = 5. This should remain in place. + Instruction::PushU32Imm(2), // Starts at [base + 5]. Length = 5. Subroutine argument 1. + Instruction::PushU32Imm(1), // Starts at [base + 10]. Length = 5. The number of arguments. + Instruction::CallAbsU32Imm(base_offset + 24, String::default()), // Starts at [base + 15]. Length = 6. + Instruction::Halt, // Starts at [base + 21]. Length = 2. /***** FUNC_AAAA - Subroutine 1 starts here. *****/ - Instruction::PushU32Imm(0), // Starts at [base + 36]. Length = 8. The number of arguments. - Instruction::CallAbsU32Imm(base_offset + 65, String::default()), // Starts at [base + 44]. Length = 8. - Instruction::AddU32ImmU32Reg(5, RegisterId::EAX), // Starts at [base + 52]. Length = 9. - Instruction::RetArgsU32, // Starts at [base + 61]. Length = 4. + Instruction::PushU32Imm(0), // Starts at [base + 23]. Length = 5. The number of arguments. + Instruction::CallAbsU32Imm(base_offset + 43, String::default()), // Starts at [base + 28]. Length = 6. + Instruction::AddU32ImmU32Reg(5, RegisterId::EAX), // Starts at [base + 34]. Length = 7. + Instruction::RetArgsU32, // Starts at [base + 41]. Length = 2. /***** FUNC_BBBB - Subroutine 2 starts here. *****/ - Instruction::PushU32Imm(100), // Starts at [base + 65]. Length = 8. This should NOT be preserved. + Instruction::PushU32Imm(100), // Starts at [base + 43]. Length = 5. This should NOT be preserved. Instruction::PopU32ToU32Reg(RegisterId::EAX), Instruction::RetArgsU32, ]; @@ -6178,7 +6178,7 @@ mod tests_cpu { // start of the second move instruction. // // We expect that the first move instruction will be skipped entirely. - JumpAbsU32Imm(base_offset + 17), + JumpAbsU32Imm(base_offset + 13), // This instruction should be skipped, so R1 should remain at the default value of 0. MovU32ImmU32Reg(0xf, RegisterId::E5X), // The jump should start execution here. @@ -6210,20 +6210,20 @@ mod tests_cpu { // The address is calculated as follows: // Move the value of the code segment register into R8. // - // The instruction 2 is _9 bytes_ in length - // (4 for the instruction, 4 for the u32 immediate argument and 1 for the register ID argument). - // The instruction 3 is _6 bytes_ in length - // (4 for the instruction, 1 for the register ID argument, 1 for the register ID argument). - // The instruction 4 is _5 bytes_ in length - // (4 for the instruction and 1 for the register ID argument). - // The instruction 5 is _9 bytes_ in length - // (4 for the instruction, 4 for the u32 argument and 1 for the register ID argument). + // The instruction 2 is _7 bytes_ in length + // (2 for the instruction, 4 for the u32 immediate argument and 1 for the register ID argument). + // The instruction 3 is _4 bytes_ in length + // (2 for the instruction, 1 for the register ID argument, 1 for the register ID argument). + // The instruction 4 is _3 bytes_ in length + // (2 for the instruction and 1 for the register ID argument). + // The instruction 5 is _7 bytes_ in length + // (2 for the instruction, 4 for the u32 argument and 1 for the register ID argument). // - // This means that we need to add 29 to the value of the CS register to point + // This means that we need to add 21 to the value of the CS register to point // to the start of the second move instruction. // // We expect that the first move instruction will be skipped entirely. - MovU32ImmU32Reg(29, RegisterId::E5X), + MovU32ImmU32Reg(21, RegisterId::E5X), AddU32RegU32Reg(RegisterId::ECS, RegisterId::E5X), JumpAbsU32Reg(RegisterId::E5X), // This instruction should be skipped, so R1 should remain at the default value of 0. diff --git a/redox-core/src/ins/instruction.rs b/redox-core/src/ins/instruction.rs index 7a528a7..51c1c35 100644 --- a/redox-core/src/ins/instruction.rs +++ b/redox-core/src/ins/instruction.rs @@ -3,12 +3,10 @@ use std::fmt::{self, Display, Formatter}; #[cfg(test)] use strum_macros::EnumIter; -use crate::{ins::expression::Expression, reg::registers::RegisterId}; +use crate::{ins::expression::Expression, reg::registers::RegisterId, utils}; use super::op_codes::OpCode; -/// The size of the instruction, in bytes. -const INSTRUCTION_SIZE: usize = 4; /// The size of a f32 argument, in bytes. const ARG_F32_IMM_SIZE: usize = 4; /// The size of a u32 argument, in bytes. @@ -25,6 +23,16 @@ const ARG_REG_ID_SIZE: usize = 1; pub enum Instruction { /// No operation. Nop, + /// Push a f32 immediate value onto the stack. + PushF32Imm(f32), + /// Push a u32 immediate value onto the stack. + PushU32Imm(u32), + /// Push the value of a u32 register onto the stack. + PushU32Reg(RegisterId), + /// Pop a f32 value from the stack to a f32 register. + PopF32ToF32Reg(RegisterId), + /// Pop a u32 value from the stack to a u32 register. + PopU32ToU32Reg(RegisterId), /******** [Arithmetic Instructions] ********/ /// Add a u32 immediate to u32 register. The result is stored in the accumulator register. @@ -115,16 +123,6 @@ pub enum Instruction { ZeroHighBitsByIndexU32Reg(RegisterId, RegisterId, RegisterId), /// Zero the high bits of the source value starting from a specified index. ZeroHighBitsByIndexU32RegU32Imm(u32, RegisterId, RegisterId), - /// Push a f32 immediate value onto the stack. - PushF32Imm(f32), - /// Push a u32 immediate value onto the stack. - PushU32Imm(u32), - /// Push the value of a u32 register onto the stack. - PushU32Reg(RegisterId), - /// Pop a f32 value from the stack to a f32 register. - PopF32ToF32Reg(RegisterId), - /// Pop a u32 value from the stack to a u32 register. - PopU32ToU32Reg(RegisterId), /******** [IO Instructions] ********/ /// Output a f32 immediate value to a specific port. @@ -604,10 +602,23 @@ impl Instruction { /// A usize giving the total size of the [`Instruction`], in bytes. #[inline(always)] pub fn get_total_instruction_size(&self) -> usize { - // TODO - this is a placeholder setup until I implement the incremental extending instruction system. + let mut opcode_size = 1; + let op_id = (Into::::into(self)) as u32; + let instruction_size = match self { Instruction::Label(_) => 0, - _ => INSTRUCTION_SIZE, + _ => { + if utils::is_bit_set(op_id, 7) { + opcode_size += 1; + if utils::is_bit_set(op_id, 15) { + opcode_size += 1; + if utils::is_bit_set(op_id, 23) { + opcode_size += 1; + } + } + } + opcode_size + } }; instruction_size + self.get_instruction_arg_size() } diff --git a/redox-core/src/ins/op_codes.rs b/redox-core/src/ins/op_codes.rs index 4839953..11a8822 100644 --- a/redox-core/src/ins/op_codes.rs +++ b/redox-core/src/ins/op_codes.rs @@ -9,31 +9,43 @@ use super::instruction::Instruction; Clone, Copy, Debug, Default, Eq, PartialOrd, Ord, PartialEq, Hash, FromPrimitive, EnumIter, )] pub enum OpCode { + /* 1 BYTE INSTRUCTIONS - reserved for very common instructions. */ /// No Operation - an empty instruction. - Nop = 0b0, + Nop = 0b0000_0000_0000_0000, + /// Push a f32 immediate value onto the stack. + PushF32Imm = 0b0000_0000_0000_0001, + /// Push a u32 immediate value onto the stack. + PushU32Imm = 0b0000_0000_0000_0010, + /// Push the value of a u32 register onto the stack. + PushU32Reg = 0b0000_0000_0000_0011, + /// Pop a f32 value from the stack to a f32 register. + PopF32ToF32Reg = 0b0000_0000_0000_0100, + /// Pop a u32 value from the stack to a u32 register. + PopU32ToU32Reg = 0b0000_0000_0000_0101, + /* 2 BYTE INSTRUCTIONS - reserved for common instructions. */ /// Add a u32 immediate to u32 register. The result is stored in the accumulator register. - AddU32ImmU32Reg = 0b0000000010000000, + AddU32ImmU32Reg = 0b0000_0000_1000_0000, /// Add a u32 register to u32 register. The result is stored in the accumulator register. - AddU32RegU32Reg = 0b0000000010000001, + AddU32RegU32Reg = 0b0000_0000_1000_0001, /// Subtract a u32 immediate from a u32 register. The result is stored in the accumulator register. - SubU32ImmU32Reg = 0b0000000010000010, + SubU32ImmU32Reg = 0b0000_0000_1000_0010, /// Subtract a u32 register (A) from a u32 register (B). The result is stored in the accumulator register. - SubU32RegU32Reg = 0b0000000010000011, + SubU32RegU32Reg = 0b0000_0000_1000_0011, /// Unsigned multiplication of the register ER1 by a u32 immediate. The result is stored in the register ER1. - MulU32Imm = 0b0000000010000100, + MulU32Imm = 0b0000_0000_1000_0100, /// Unsigned multiplication of the register ER1 by a u32 register. The result is stored in the register ER1. - MulU32Reg = 0b0000000010000101, + MulU32Reg = 0b0000_0000_1000_0101, /// Unsigned division of the register ER1 by a u32 immediate. The quotient is stored in the register ER1 and the modulo is stored in ER4. - DivU32Imm = 0b0000000010000110, + DivU32Imm = 0b0000_0000_1000_0110, /// Unsigned division of the register ER1 by a u32 register. The quotient is stored in the register ER1 and the modulo is stored in ER4. - DivU32Reg = 0b0000000010000111, + DivU32Reg = 0b0000_0000_1000_0111, /// Increment a u32 register. - IncU32Reg = 0b0000000010001000, + IncU32Reg = 0b0000_0000_1000_1000, /// Decrement a u32 register. - DecU32Reg = 0b0000000010001001, + DecU32Reg = 0b0000_0000_1000_1001, /// Perform a logical AND operation on a u32 immediate and a u32 register. The resulting value is stored in the register. - AndU32ImmU32Reg = 0b0000000010001010, + AndU32ImmU32Reg = 0b0000_0000_1000_1010, /******** [Bit Operation Instructions] ********/ /// Left-shift a u32 register by a u8 immediate. The result remains in the origin register. @@ -104,16 +116,6 @@ pub enum OpCode { /// /// The carry and zero flags may be set depending on the result and the overflow flag will always be cleared. ZeroHighBitsByIndexU32RegU32Imm, - /// Push a f32 immediate value onto the stack. - PushF32Imm, - /// Push a u32 immediate value onto the stack. - PushU32Imm, - /// Push the value of a u32 register onto the stack. - PushU32Reg, - /// Pop a f32 value from the stack to a f32 register. - PopF32ToF32Reg, - /// Pop a u32 value from the stack to a u32 register. - PopU32ToU32Reg, /******** [IO Instructions] ********/ /// Output a f32 immediate value to a specific port. @@ -183,11 +185,28 @@ pub enum OpCode { /// Halt the execution of the virtual machine. Halt = 65535, + /* 3 BYTE INSTRUCTIONS */ + + + /* 4 BYTE INSTRUCTIONS */ + + + /* SPECIAL BYTE INSTRUCTIONS */ /// A placeholder opcode for labels. These do not directly compile to anything and are for used for computing jumps. This should never be constructed directly. - Label = u32::MAX - 1, + Label = 0b1111_1111_1111_1111_1111_1111_1111_1110, /// A placeholder for instances where the opcode isn't recognized. This should never be constructed directly. #[default] - Unknown, + Unknown = 0b1111_1111_1111_1111_1111_1111_1111_1111, +} + +impl OpCode { + /// Returns `true` if the op code is [`SubU32ImmU32Reg`]. + /// + /// [`SubU32ImmU32Reg`]: OpCode::SubU32ImmU32Reg + #[must_use] + pub fn is_sub_u32_imm_u32_reg(&self) -> bool { + matches!(self, Self::SubU32ImmU32Reg) + } } impl From<&Instruction> for OpCode { diff --git a/redox-core/src/mem/mapped_memory.rs b/redox-core/src/mem/mapped_memory.rs index e2150f1..1db231c 100644 --- a/redox-core/src/mem/mapped_memory.rs +++ b/redox-core/src/mem/mapped_memory.rs @@ -33,7 +33,7 @@ impl MappedMemory { /// * `end` - The ending memory location. #[inline(always)] pub fn assert_within_bounds(&self, end: usize) { - assert!(end <= self.len()); + assert!(end <= self.end); } /// Check whether a range can completely exist within this memory segment. diff --git a/redox-core/src/mem/memory_handler.rs b/redox-core/src/mem/memory_handler.rs index f7eefba..4053920 100644 --- a/redox-core/src/mem/memory_handler.rs +++ b/redox-core/src/mem/memory_handler.rs @@ -4,6 +4,7 @@ use prettytable::{row, Table}; use crate::{ ins::{instruction::Instruction, op_codes::OpCode}, reg::registers::RegisterId, + utils, }; use super::mapped_memory::MappedMemory; @@ -234,7 +235,7 @@ impl MemoryHandler { } /// Completely clear the type hints vector. - fn clear_stack_type_hints(&mut self) { + pub fn clear_stack_type_hints(&mut self) { #[cfg(feature = "stack-type-hints")] { self.debug_stack_type_hints.clear(); @@ -258,7 +259,6 @@ impl MemoryHandler { while cursor < end { let ins = self.get_instruction(cursor); let size = ins.get_total_instruction_size(); - instructions.push(ins); cursor += size; } @@ -313,8 +313,27 @@ impl MemoryHandler { use Instruction as I; use OpCode as O; + let mut pos = pos; + let mut opcode_bytes: [u8; 4] = [self.get_byte_clone(pos), 0, 0, 0]; + pos += 1; + + if utils::is_bit_set_u8(opcode_bytes[0], 7) { + opcode_bytes[1] = self.get_byte_clone(pos); + pos += 1; + + if utils::is_bit_set_u8(opcode_bytes[1], 7) { + opcode_bytes[2] = self.get_byte_clone(pos); + pos += 1; + + if utils::is_bit_set_u8(opcode_bytes[2], 7) { + opcode_bytes[3] = self.get_byte_clone(pos); + pos += 1; + } + } + } + // Read the OpCode ID. - let opcode_id = self.get_u32(pos); + let opcode_id = u32::from_le_bytes(opcode_bytes); // Validate the opcode is one of the ones we know about. // In the case we encounter an unrecognized opcode ID then we will @@ -326,7 +345,7 @@ impl MemoryHandler { // We will assert here if we can't read enough bytes to meet the expected amount. // This means that subsequent "unsafe" reads are actually safe. - let arg_bytes = self.get_range_ptr(pos + SIZE_OF_INSTRUCTION, arg_len); + let arg_bytes = self.get_range_ptr(pos, arg_len); let mut cursor = 0; @@ -1327,6 +1346,16 @@ impl MemoryHandler { self.stack_base_pointer = self.stack_pointer; } + /// Write a u32 value into memory. + /// + /// # Arguments + /// + /// * `pos` - The position of the first byte to be written into memory. + /// * `value` - The value to be written into memory. + pub fn remove_memory_region_by_name(&mut self, name: &str) { + self.mapped_memory.retain(|r| r.name != name); + } + /// Write a u32 value into memory. /// /// # Arguments diff --git a/redox-terminal/src/main.rs b/redox-terminal/src/main.rs index 6e82559..3bded2e 100644 --- a/redox-terminal/src/main.rs +++ b/redox-terminal/src/main.rs @@ -65,28 +65,29 @@ fn main() { if cfg!(target_endian = "big") { panic!("currently unsupported"); } - let code = "section .text\r\npush 0\r\ncall :LABEL_1\r\nhlt\r\n:LABEL_1\r\nmov 0xdeadbeef, EAX\r\niret"; - let mut compiler = Compiler::new(); - let data = compiler.compile_assembly(code); - - println!("{data:?}"); + let data = compiler.compile_assembly(code, true); /*let instructions = &[ - // Indicate that we want to make a seeded random number generator. + /*// Indicate that we want to make a seeded random number generator. Instruction::OutU8Imm(0x1, 0x0), // Specify our seed. Instruction::OutU32Imm(0xdeadbeef, 0x0), // Read a PRNG from the device. Instruction::InU32Reg(0x0, RegisterId::EAX), Instruction::PushU32Imm(0xdeadbeef), + Instruction::Halt,*/ + Instruction::MovU32ImmU32Reg(0x1, RegisterId::EAX), + //Instruction::AddU32ImmU32Reg(0x2, RegisterId::EAX), Instruction::Halt, ]; let mut compiler = Compiler::new(); let data = compiler.compile(instructions);*/ + println!("{data:?}"); + let mut vm = VirtualMachine::new( vm::MIN_USER_SEGMENT_SIZE, data,