ft: initial work in interpreter
This commit is contained in:
28
src/interpreter/computer.rs
Normal file
28
src/interpreter/computer.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use core::fmt;
|
||||
|
||||
use crate::operands::Byte;
|
||||
|
||||
use super::{flags::Flags, register::Register};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Computer {
|
||||
pub regs: Register,
|
||||
pub flags: Flags,
|
||||
pub memory: [Byte; 65536],
|
||||
}
|
||||
|
||||
impl Computer {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
regs: Register::new(),
|
||||
flags: Flags::new(),
|
||||
memory: [0; 65536],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Computer {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{} | {}", self.regs, self.flags)
|
||||
}
|
||||
}
|
||||
48
src/interpreter/flags.rs
Normal file
48
src/interpreter/flags.rs
Normal file
@@ -0,0 +1,48 @@
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Flags {
|
||||
pub of: bool,
|
||||
pub df: bool,
|
||||
pub r#if: bool,
|
||||
pub tf: bool,
|
||||
pub sf: bool,
|
||||
pub zf: bool,
|
||||
pub nf: bool,
|
||||
pub pf: bool,
|
||||
pub cf: bool,
|
||||
}
|
||||
|
||||
impl Flags {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
of: false,
|
||||
df: false,
|
||||
r#if: false,
|
||||
tf: false,
|
||||
sf: false,
|
||||
zf: false,
|
||||
nf: false,
|
||||
pf: false,
|
||||
cf: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
129
src/interpreter/interpreter.rs
Normal file
129
src/interpreter/interpreter.rs
Normal file
@@ -0,0 +1,129 @@
|
||||
use core::fmt;
|
||||
use std::{fmt::Debug, process::exit};
|
||||
|
||||
use crate::{
|
||||
instructions::{Instruction, Mnemonic},
|
||||
operands::{Byte, Word},
|
||||
};
|
||||
|
||||
use super::computer::Computer;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum InterpreterError {
|
||||
EndOfData,
|
||||
InvalidSyscall(u8),
|
||||
}
|
||||
|
||||
impl fmt::Display for InterpreterError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
InterpreterError::EndOfData => write!(f, "Read beyond the available data section"),
|
||||
InterpreterError::InvalidSyscall(id) => {
|
||||
write!(f, "The syscall with ID {} is unknown", id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Interpreter {
|
||||
computer: Computer,
|
||||
instructions: Vec<Instruction>,
|
||||
data: Vec<Byte>,
|
||||
}
|
||||
|
||||
impl Interpreter {
|
||||
pub fn new(instructions: Vec<Instruction>, data: Vec<Byte>) -> Self {
|
||||
Self {
|
||||
computer: Computer::new(),
|
||||
instructions,
|
||||
data,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn interpret(&mut self) -> Result<(), InterpreterError> {
|
||||
for instr in self.instructions.iter() {
|
||||
log::info!(
|
||||
"IP({:04x})\t {:<15} | {}",
|
||||
instr.start,
|
||||
instr.opcode.to_string(),
|
||||
self.computer
|
||||
);
|
||||
|
||||
match instr.opcode {
|
||||
Mnemonic::MOV_BXIv(word) => self.computer.regs.bx.write(word),
|
||||
Mnemonic::INT(id) => self.handle_int(id)?,
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_int(&self, id: u8) -> Result<(), InterpreterError> {
|
||||
let bx = self.computer.regs.bx.read() as usize;
|
||||
// a message is always 8 words aligned
|
||||
let len = 2 * 8;
|
||||
let data = self
|
||||
.data
|
||||
.get(bx..bx + len)
|
||||
.ok_or(InterpreterError::EndOfData)?
|
||||
.to_owned();
|
||||
let interrupt_data = InterruptData::new(data);
|
||||
|
||||
// simulate interrupt handler code of MINIX
|
||||
match id {
|
||||
// sofware interrupts
|
||||
0x20 => {
|
||||
match interrupt_data.interrupt_id {
|
||||
0x04 => {
|
||||
let fd = interrupt_data.m_type;
|
||||
let location = interrupt_data.data_position;
|
||||
let len = interrupt_data.count;
|
||||
log::info!("executing write({}, {}, {})", fd, location, len);
|
||||
for byte in &self.data[location as usize..] {
|
||||
if *byte == 0x00 {
|
||||
break;
|
||||
} else {
|
||||
print!("{}", *byte as char);
|
||||
}
|
||||
}
|
||||
}
|
||||
0x01 => {
|
||||
let exit_code = interrupt_data.data_position;
|
||||
log::info!("executing exit({})", exit_code);
|
||||
exit(exit_code.into())
|
||||
}
|
||||
_ => todo!(),
|
||||
};
|
||||
}
|
||||
_ => return Err(InterpreterError::InvalidSyscall(id)),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
// https://cse.unl.edu/~goddard/Courses/CSCE351/Lectures/Lecture8.pdf
|
||||
pub struct InterruptData {
|
||||
pub m_type: Word, // Operation requested
|
||||
pub interrupt_id: Word, // Minor device to use
|
||||
pub proc_nr: Word, // Process requesting the I/O
|
||||
pub count: Word, // Word count or ioctl code
|
||||
pub position: Word, // Position on device
|
||||
pub data_position: Word, // Minor device to use
|
||||
}
|
||||
|
||||
impl InterruptData {
|
||||
pub fn new(data: Vec<u8>) -> Self {
|
||||
Self {
|
||||
m_type: Word::from_le_bytes([data[0], data[1]]),
|
||||
interrupt_id: Word::from_le_bytes([data[2], data[3]]),
|
||||
proc_nr: Word::from_le_bytes([data[4], data[5]]),
|
||||
count: Word::from_le_bytes([data[6], data[7]]),
|
||||
position: Word::from_le_bytes([data[8], data[9]]),
|
||||
data_position: Word::from_le_bytes([data[10], data[11]]),
|
||||
}
|
||||
}
|
||||
}
|
||||
4
src/interpreter/mod.rs
Normal file
4
src/interpreter/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
mod computer;
|
||||
mod flags;
|
||||
pub mod interpreter;
|
||||
mod register;
|
||||
75
src/interpreter/register.rs
Normal file
75
src/interpreter/register.rs
Normal file
@@ -0,0 +1,75 @@
|
||||
use crate::operands::{Byte, Word};
|
||||
use core::fmt;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Register {
|
||||
pub ax: AX,
|
||||
pub bx: BX,
|
||||
pub cx: CX,
|
||||
pub dx: DX,
|
||||
pub sp: Word,
|
||||
pub bp: Word,
|
||||
pub si: Word,
|
||||
pub di: Word,
|
||||
}
|
||||
|
||||
impl Register {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ax: AX::new(),
|
||||
bx: BX::new(),
|
||||
cx: CX::new(),
|
||||
dx: DX::new(),
|
||||
sp: 0,
|
||||
bp: 0,
|
||||
si: 0,
|
||||
di: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Register {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"AX({}) BX({}) CX({}) DX({}) SP({:04x}) BP({:04x}) SI({:04x}) DI({:04x})",
|
||||
self.ax, self.bx, self.cx, self.dx, self.sp, self.bp, self.si, self.di
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! gen_regs {
|
||||
($ident:ident) => {
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct $ident {
|
||||
upper: Byte,
|
||||
lower: Byte,
|
||||
}
|
||||
|
||||
impl $ident {
|
||||
pub fn new() -> Self {
|
||||
Self { upper: 0, lower: 0 }
|
||||
}
|
||||
|
||||
pub fn read(self) -> Word {
|
||||
Word::from_le_bytes([self.lower, self.upper])
|
||||
}
|
||||
|
||||
pub fn write(&mut self, word: Word) {
|
||||
let [low, high]: [u8; 2] = word.to_le_bytes();
|
||||
self.lower = low;
|
||||
self.upper = high;
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for $ident {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{:04x}", Word::from_le_bytes([self.lower, self.upper]))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
gen_regs!(AX);
|
||||
gen_regs!(BX);
|
||||
gen_regs!(CX);
|
||||
gen_regs!(DX);
|
||||
Reference in New Issue
Block a user