ft(interpreter): impl short jumps

This commit is contained in:
2025-06-11 15:44:42 +09:00
parent a4dc420d60
commit c9bf8fdc46
6 changed files with 160 additions and 57 deletions

View File

@@ -184,32 +184,34 @@ impl Disassembler {
/// offset. /// offset.
/// Returns the read byte added to the address of the subsequent instruction /// Returns the read byte added to the address of the subsequent instruction
/// to act as a relative offset (Intel Jb operand). /// to act as a relative offset (Intel Jb operand).
fn parse_j_byte(&mut self) -> Result<isize, DisasmError> { /// The returned `usize` will be the subsequent instruction to jump to.
fn parse_j_byte(&mut self) -> Result<usize, DisasmError> {
log::debug!("Attempting to parse Jb at {:#04x} ...", self.offset); log::debug!("Attempting to parse Jb at {:#04x} ...", self.offset);
// first interpret as 2-complement, then cast for addition // first interpret as 2-complement, then cast for addition
let byte = self.parse_byte()? as IByte as isize; let byte = self.parse_byte()? as IByte as isize;
let next_addr = (self.offset + 1) as isize; let next_addr = (self.offset + 1) as isize;
log::debug!( log::debug!(
"Parsed Jb consists of {byte:#04x} + {next_addr:#04x} = {:#04x}", "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 /// Parse a word of the binary, interpret it as signed and advance the
/// offset. /// offset.
/// Returns the read word added to the address of the subsequent instruction /// Returns the read word added to the address of the subsequent instruction
/// to act as a relative offset (Intel Jw/Jv operand). /// to act as a relative offset (Intel Jw/Jv operand).
pub fn parse_j_word(&mut self) -> Result<isize, DisasmError> { /// The returned `usize` will be the subsequent instruction to jump to.
pub fn parse_j_word(&mut self) -> Result<usize, DisasmError> {
log::debug!("Attempting to parse Jv at {:#04x} ...", self.offset); log::debug!("Attempting to parse Jv at {:#04x} ...", self.offset);
// first interpret as 2-complement, then cast for addition // first interpret as 2-complement, then cast for addition
let word = self.parse_word()? as IWord as isize; let word = self.parse_word()? as IWord as isize;
let next_addr = (self.offset + 1) as isize; let next_addr = (self.offset + 1) as isize;
log::debug!( log::debug!(
"Parsed Jv consists of {word:#04x} + {next_addr:#04x} = {:#04x}", "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. /// Parse a single pointer of the binary and advance the offset.

View File

@@ -136,27 +136,27 @@ pub enum Mnemonic {
// DEC // DEC
DEC_Reg(Register), DEC_Reg(Register),
DEC_Mod(ModRmTarget), DEC_Mod(ModRmTarget),
// Jumps // Jumps conditional
JO(isize), JO(usize),
JNO(isize), JNO(usize),
JB(isize), JB(usize),
JNB(isize), JNB(usize),
JZ(isize), JZ(usize),
JNZ(isize), JNZ(usize),
JBE(isize), JBE(usize),
JA(isize), JA(usize),
JS(isize), JS(usize),
JNS(isize), JNS(usize),
JPE(isize), JPE(usize),
JPO(isize), JPO(usize),
JL(isize), JL(usize),
JGE(isize), JGE(usize),
JLE(isize), JLE(usize),
JG(isize), JG(usize),
LOOPNZ(isize), LOOPNZ(usize),
LOOPZ(isize), LOOPZ(usize),
LOOP(isize), LOOP(usize),
JCXZ(isize), JCXZ(usize),
// TEST // TEST
TEST(ModRmTarget, Register), TEST(ModRmTarget, Register),
@@ -203,13 +203,13 @@ pub enum Mnemonic {
CWD, CWD,
// CALL // CALL
CALL_p(Pointer32), CALL_p(Pointer32),
CALL_v(isize), CALL_v(usize),
CALL_Mod(ModRmTarget), CALL_Mod(ModRmTarget),
CALL_Mp(Pointer16), CALL_Mp(Pointer16),
// JUMP // JUMP
JMP_p(Pointer32), JMP_p(Pointer32),
JMP_b(isize), // parses IByte, but stores as isize JMP_b(usize), // parses IByte, but stores as isize
JMP_v(isize), // parwses IWord, but stores as isize JMP_v(usize), // parwses IWord, but stores as isize
JMP_Mod(ModRmTarget), JMP_Mod(ModRmTarget),
JMP_Mp(Pointer16), JMP_Mp(Pointer16),
// WAIT // WAIT

View File

@@ -190,7 +190,7 @@ impl Computer {
impl fmt::Display for Computer { impl fmt::Display for Computer {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} | {}", self.regs, self.flags) write!(f, "{} {}", self.regs, self.flags)
} }
} }

View File

@@ -33,16 +33,16 @@ impl fmt::Display for Flags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"OF({}) DF({}) IF({}) TF({}) SF({}) ZF({}) NF({}) PF({}) CF({})", "{}{}{}{}{}{}{}{}{}",
self.of as i32, if self.of { "O" } else { "-" },
self.df as i32, if self.df { "D" } else { "-" },
self.r#if as i32, if self.r#if { "I" } else { "-" },
self.tf as i32, if self.tf { "T" } else { "-" },
self.sf as i32, if self.sf { "S" } else { "-" },
self.zf as i32, if self.zf { "Z" } else { "-" },
self.nf as i32, if self.nf { "N" } else { "-" },
self.pf as i32, if self.pf { "P" } else { "-" },
self.cf as i32, if self.cf { "C" } else { "-" },
) )
} }
} }

View File

@@ -1,5 +1,5 @@
use core::fmt; use core::fmt;
use std::{fmt::Debug, process::exit}; use std::{fmt::Debug, process::exit, slice::Iter};
use crate::{ use crate::{
instructions::{Instruction, Mnemonic}, instructions::{Instruction, Mnemonic},
@@ -12,10 +12,13 @@ use super::{
interrupt::InterruptMessage, interrupt::InterruptMessage,
}; };
type InstructionPointer<'a> = std::slice::Iter<'a, Instruction>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum InterpreterError { pub enum InterpreterError {
EndOfData, EndOfData,
InvalidSyscall(u8), InvalidSyscall(u8),
InstructionNotFound(usize),
} }
impl fmt::Display for InterpreterError { impl fmt::Display for InterpreterError {
@@ -25,6 +28,9 @@ impl fmt::Display for InterpreterError {
InterpreterError::InvalidSyscall(id) => { InterpreterError::InvalidSyscall(id) => {
write!(f, "The syscall with ID {} is unknown", 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> { 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!( log::info!(
"IP({:04x})\t {:<30} | {}", "{} IP({:04x})\t {:<32}",
instr.start, self.computer,
instr.opcode.to_string(), cur_instr.start,
self.computer cur_instr.opcode.to_string(),
); );
match instr.opcode { match cur_instr.opcode {
/* /*
* ADD * 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 * Test
@@ -285,14 +359,6 @@ impl Interpreter {
* Sign extensions * Sign extensions
*/ */
/*
* Call
*/
/*
* Jump
*/
/* /*
* Wait * Wait
*/ */
@@ -416,4 +482,30 @@ impl Interpreter {
Ok(()) 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<Instruction>,
addr: usize,
) -> Option<InstructionPointer> {
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<Instruction>,
ip: &mut InstructionPointer<'a>,
offset: usize,
) {
if let Some(next_instr) = Self::find_instruction(&instructions, offset) {
*ip = next_instr;
}
}
} }

View File

@@ -95,6 +95,15 @@ impl ImmediateOperand {
} }
} }
impl Into<usize> for ImmediateOperand {
fn into(self) -> usize {
match self {
ImmediateOperand::Byte(b) => b as usize,
ImmediateOperand::Word(w) => w as usize,
}
}
}
impl Add for ImmediateOperand { impl Add for ImmediateOperand {
type Output = Self; type Output = Self;