ft(interpreter): impl short jumps
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 { "-" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user