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.
/// 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<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);
// 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<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);
// 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.

View File

@@ -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

View File

@@ -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)
}
}

View File

@@ -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 { "-" },
)
}
}

View File

@@ -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<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 {
type Output = Self;