diff --git a/src/disasm.rs b/src/disasm.rs index 6606ca5..b0974a8 100644 --- a/src/disasm.rs +++ b/src/disasm.rs @@ -184,32 +184,34 @@ impl Disassembler { /// offset. /// Returns the read byte added to the address of the subsequent instruction /// to act as a relative offset (Intel Jb operand). - fn parse_j_byte(&mut self) -> Result { + /// The returned `usize` will be the subsequent instruction to jump to. + fn parse_j_byte(&mut self) -> Result { log::debug!("Attempting to parse Jb at {:#04x} ...", self.offset); // first interpret as 2-complement, then cast for addition let byte = self.parse_byte()? as IByte as isize; let next_addr = (self.offset + 1) as isize; log::debug!( "Parsed Jb consists of {byte:#04x} + {next_addr:#04x} = {:#04x}", - byte + next_addr + (byte + next_addr) as usize ); - Ok(byte + next_addr) + Ok((byte + next_addr) as usize) } /// Parse a word of the binary, interpret it as signed and advance the /// offset. /// Returns the read word added to the address of the subsequent instruction /// to act as a relative offset (Intel Jw/Jv operand). - pub fn parse_j_word(&mut self) -> Result { + /// The returned `usize` will be the subsequent instruction to jump to. + pub fn parse_j_word(&mut self) -> Result { log::debug!("Attempting to parse Jv at {:#04x} ...", self.offset); // first interpret as 2-complement, then cast for addition let word = self.parse_word()? as IWord as isize; let next_addr = (self.offset + 1) as isize; log::debug!( "Parsed Jv consists of {word:#04x} + {next_addr:#04x} = {:#04x}", - word + next_addr + (word + next_addr) as usize ); - Ok(word + next_addr) + Ok((word + next_addr) as usize) } /// Parse a single pointer of the binary and advance the offset. diff --git a/src/instructions.rs b/src/instructions.rs index 861e3a3..1595697 100644 --- a/src/instructions.rs +++ b/src/instructions.rs @@ -136,27 +136,27 @@ pub enum Mnemonic { // DEC DEC_Reg(Register), DEC_Mod(ModRmTarget), - // Jumps - JO(isize), - JNO(isize), - JB(isize), - JNB(isize), - JZ(isize), - JNZ(isize), - JBE(isize), - JA(isize), - JS(isize), - JNS(isize), - JPE(isize), - JPO(isize), - JL(isize), - JGE(isize), - JLE(isize), - JG(isize), - LOOPNZ(isize), - LOOPZ(isize), - LOOP(isize), - JCXZ(isize), + // Jumps conditional + JO(usize), + JNO(usize), + JB(usize), + JNB(usize), + JZ(usize), + JNZ(usize), + JBE(usize), + JA(usize), + JS(usize), + JNS(usize), + JPE(usize), + JPO(usize), + JL(usize), + JGE(usize), + JLE(usize), + JG(usize), + LOOPNZ(usize), + LOOPZ(usize), + LOOP(usize), + JCXZ(usize), // TEST TEST(ModRmTarget, Register), @@ -203,13 +203,13 @@ pub enum Mnemonic { CWD, // CALL CALL_p(Pointer32), - CALL_v(isize), + CALL_v(usize), CALL_Mod(ModRmTarget), CALL_Mp(Pointer16), // JUMP JMP_p(Pointer32), - JMP_b(isize), // parses IByte, but stores as isize - JMP_v(isize), // parwses IWord, but stores as isize + JMP_b(usize), // parses IByte, but stores as isize + JMP_v(usize), // parwses IWord, but stores as isize JMP_Mod(ModRmTarget), JMP_Mp(Pointer16), // WAIT diff --git a/src/interpreter/computer.rs b/src/interpreter/computer.rs index 118eba0..635a006 100644 --- a/src/interpreter/computer.rs +++ b/src/interpreter/computer.rs @@ -190,7 +190,7 @@ impl Computer { impl fmt::Display for Computer { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} | {}", self.regs, self.flags) + write!(f, "{} {}", self.regs, self.flags) } } diff --git a/src/interpreter/flags.rs b/src/interpreter/flags.rs index 6c1a7a2..641300c 100644 --- a/src/interpreter/flags.rs +++ b/src/interpreter/flags.rs @@ -33,16 +33,16 @@ impl fmt::Display for Flags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "OF({}) DF({}) IF({}) TF({}) SF({}) ZF({}) NF({}) PF({}) CF({})", - self.of as i32, - self.df as i32, - self.r#if as i32, - self.tf as i32, - self.sf as i32, - self.zf as i32, - self.nf as i32, - self.pf as i32, - self.cf as i32, + "{}{}{}{}{}{}{}{}{}", + if self.of { "O" } else { "-" }, + if self.df { "D" } else { "-" }, + if self.r#if { "I" } else { "-" }, + if self.tf { "T" } else { "-" }, + if self.sf { "S" } else { "-" }, + if self.zf { "Z" } else { "-" }, + if self.nf { "N" } else { "-" }, + if self.pf { "P" } else { "-" }, + if self.cf { "C" } else { "-" }, ) } } diff --git a/src/interpreter/interpreter.rs b/src/interpreter/interpreter.rs index 8acbf65..76e71f9 100644 --- a/src/interpreter/interpreter.rs +++ b/src/interpreter/interpreter.rs @@ -1,5 +1,5 @@ use core::fmt; -use std::{fmt::Debug, process::exit}; +use std::{fmt::Debug, process::exit, slice::Iter}; use crate::{ instructions::{Instruction, Mnemonic}, @@ -12,10 +12,13 @@ use super::{ interrupt::InterruptMessage, }; +type InstructionPointer<'a> = std::slice::Iter<'a, Instruction>; + #[derive(Debug, Clone)] pub enum InterpreterError { EndOfData, InvalidSyscall(u8), + InstructionNotFound(usize), } impl fmt::Display for InterpreterError { @@ -25,6 +28,9 @@ impl fmt::Display for InterpreterError { InterpreterError::InvalidSyscall(id) => { write!(f, "The syscall with ID {} is unknown", id) } + InterpreterError::InstructionNotFound(addr) => { + write!(f, "IP({addr}) points at invalid instruction") + } } } } @@ -46,15 +52,18 @@ impl Interpreter { } pub fn interpret(&mut self) -> Result<(), InterpreterError> { - for instr in self.instructions.iter() { + let mut ip = Self::find_instruction(&self.instructions, 0) + .ok_or(InterpreterError::InstructionNotFound(0))?; + + while let Some(cur_instr) = ip.next() { log::info!( - "IP({:04x})\t {:<30} | {}", - instr.start, - instr.opcode.to_string(), - self.computer + "{} IP({:04x})\t {:<32}", + self.computer, + cur_instr.start, + cur_instr.opcode.to_string(), ); - match instr.opcode { + match cur_instr.opcode { /* * ADD */ @@ -261,8 +270,73 @@ impl Interpreter { }, /* - * Jumps + * Conditional short jumps */ + Mnemonic::JO(offset) + | Mnemonic::JNO(offset) + | Mnemonic::JB(offset) + | Mnemonic::JNB(offset) + | Mnemonic::JZ(offset) + | Mnemonic::JNZ(offset) + | Mnemonic::JBE(offset) + | Mnemonic::JA(offset) + | Mnemonic::JS(offset) + | Mnemonic::JNS(offset) + | Mnemonic::JPE(offset) + | Mnemonic::JPO(offset) + | Mnemonic::JL(offset) + | Mnemonic::JGE(offset) + | Mnemonic::JLE(offset) + | Mnemonic::JG(offset) + | Mnemonic::JMP_b(offset) + | Mnemonic::JMP_v(offset) => { + let flags = self.computer.flags.clone(); + let flag = match cur_instr.opcode { + Mnemonic::JO(_) => flags.of, + Mnemonic::JNO(_) => !flags.of, + Mnemonic::JB(_) => flags.cf, + Mnemonic::JNB(_) => !flags.cf, + Mnemonic::JZ(_) => flags.zf, + Mnemonic::JNZ(_) => !flags.zf, + Mnemonic::JBE(_) => flags.cf || flags.zf, + Mnemonic::JA(_) => !flags.cf && !flags.zf, + Mnemonic::JS(_) => flags.sf, + Mnemonic::JNS(_) => !flags.sf, + Mnemonic::JPE(_) => flags.pf, + Mnemonic::JPO(_) => !flags.pf, + Mnemonic::JL(_) => flags.sf != flags.of, + Mnemonic::JLE(_) => flags.zf || (flags.sf != flags.of), + Mnemonic::JGE(_) => flags.sf == flags.of, + Mnemonic::JG(_) => !flags.zf || (flags.sf == flags.of), + Mnemonic::JMP_b(_) | Mnemonic::JMP_v(_) => true, + _ => panic!("unreachable"), + }; + if flag { + Self::ip_jump(&self.instructions, &mut ip, offset); + } + } + + /* + * Long jumps and calls + */ + Mnemonic::JMP_p(ptr) => { + todo!() + } + Mnemonic::JMP_Mp(ptr) => { + todo!() + } + Mnemonic::JMP_Mod(target) => match target { + ModRmTarget::Memory(idx) => Self::ip_jump( + &self.instructions, + &mut ip, + self.computer.memory.read(&self.computer.regs, idx).into(), + ), + ModRmTarget::Register(register) => Self::ip_jump( + &self.instructions, + &mut ip, + self.computer.regs.read(register).into(), + ), + }, /* * Test @@ -285,14 +359,6 @@ impl Interpreter { * Sign extensions */ - /* - * Call - */ - - /* - * Jump - */ - /* * Wait */ @@ -416,4 +482,30 @@ impl Interpreter { Ok(()) } + + /// Find the starting addr of an instruction in the list of all parsed + /// instructions and return the iterator to that matching instruction, to + /// allow for further traversal from that point on. + /// I bet, that this is not really fast, but I could'nt come up with a + /// better idea so far. + fn find_instruction<'a>( + items: &'a Vec, + addr: usize, + ) -> Option { + items + .iter() + .position(|i| i.start == addr) + .map(|index| items[index..].iter()) + } + + /// Jump [`InstructionPointer`] `ip` to an `offset`. + fn ip_jump<'a>( + instructions: &'a Vec, + ip: &mut InstructionPointer<'a>, + offset: usize, + ) { + if let Some(next_instr) = Self::find_instruction(&instructions, offset) { + *ip = next_instr; + } + } } diff --git a/src/operands.rs b/src/operands.rs index fd21823..aee0a72 100644 --- a/src/operands.rs +++ b/src/operands.rs @@ -95,6 +95,15 @@ impl ImmediateOperand { } } +impl Into for ImmediateOperand { + fn into(self) -> usize { + match self { + ImmediateOperand::Byte(b) => b as usize, + ImmediateOperand::Word(w) => w as usize, + } + } +} + impl Add for ImmediateOperand { type Output = Self;