mirror of
https://github.com/Noratrieb/crapderive.git
synced 2026-01-14 16:45:08 +01:00
246 lines
7 KiB
Rust
246 lines
7 KiB
Rust
//!
|
|
//! The interpreter is little endian
|
|
//!
|
|
//! ```text
|
|
//! | 0 | 1 | 2 | 3
|
|
//! ---------------
|
|
//! 1 0 0 0
|
|
//! --------------
|
|
//! Decimal 1
|
|
//! ```
|
|
|
|
use std::io::{Read, Write};
|
|
|
|
use logos::Span;
|
|
|
|
use crate::{
|
|
error::Result,
|
|
ir::{Place, Register, Stmt, Value},
|
|
};
|
|
|
|
impl Register {
|
|
fn as_index(self) -> usize {
|
|
self.0.into()
|
|
}
|
|
}
|
|
|
|
const MEMORY_SIZE: usize = 1024 * 1024 * 1024;
|
|
|
|
struct InterpretCtx {
|
|
memory: Vec<u8>,
|
|
registers: [u64; 16],
|
|
flag: bool,
|
|
ip: usize,
|
|
}
|
|
|
|
impl InterpretCtx {
|
|
fn interpret(&mut self, stmts: Vec<Stmt>) -> Result<()> {
|
|
while self.ip < stmts.len() {
|
|
let stmt_i = self.ip;
|
|
self.ip += 1;
|
|
self.interpret_stmt(stmt_i, &stmts)?
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn interpret_stmt(&mut self, stmt_i: usize, stmts: &[Stmt]) -> Result<()> {
|
|
let stmt = &stmts[stmt_i];
|
|
match stmt {
|
|
Stmt::Mov { from, to } => {
|
|
let value = self.read_value(from);
|
|
self.write_place(to, value);
|
|
}
|
|
Stmt::Movb { from, to } => {
|
|
let value = self.read_byte_value(from);
|
|
self.write_byte_place(to, value);
|
|
}
|
|
Stmt::Add { to, value } => {
|
|
let old = self.read_place(to);
|
|
let value = self.read_value(value);
|
|
let new = old.wrapping_add(value);
|
|
self.write_place(to, new);
|
|
}
|
|
Stmt::Sub { to, value } => {
|
|
let old = self.read_place(to);
|
|
let value = self.read_value(value);
|
|
let new = old.wrapping_sub(value);
|
|
self.write_place(to, new);
|
|
}
|
|
Stmt::Mul { to, value } => {
|
|
let old = self.read_place(to);
|
|
let value = self.read_value(value);
|
|
let new = old.wrapping_mul(value);
|
|
self.write_place(to, new);
|
|
}
|
|
Stmt::Div { to, value } => {
|
|
let old = self.read_place(to);
|
|
let value = self.read_value(value);
|
|
let new = old.wrapping_div(value);
|
|
self.write_place(to, new);
|
|
}
|
|
Stmt::Int { number } => {
|
|
self.interrupt(*number);
|
|
}
|
|
Stmt::Jmp { to } => {
|
|
let index = to.index;
|
|
self.ip = index;
|
|
}
|
|
Stmt::Cmp { lhs, rhs } => {
|
|
let lhs = self.read_value(lhs);
|
|
let rhs = self.read_value(rhs);
|
|
self.flag = lhs == rhs;
|
|
}
|
|
Stmt::Je { to } => {
|
|
let index = to.index;
|
|
if self.flag {
|
|
self.ip = index;
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn interrupt(&mut self, number: u64) {
|
|
match number {
|
|
0 => {
|
|
let code = self.reg(Register(0));
|
|
std::process::exit(code as i32);
|
|
}
|
|
1 => {
|
|
let str_addr = self.reg_addr(Register(0));
|
|
let str_len = self.reg_addr(Register(1));
|
|
let slice = &self.memory[str_addr..][..str_len];
|
|
let is_ok = std::io::stdout().lock().write_all(slice).is_ok();
|
|
*self.reg_mut(Register(0)) = if is_ok { 0 } else { 1 };
|
|
}
|
|
2 => {
|
|
let buffer_addr = self.reg_addr(Register(0));
|
|
let buffer_len = self.reg_addr(Register(1));
|
|
let slice = &mut self.memory[buffer_addr..][..buffer_len];
|
|
let is_ok = std::io::stdin().read_exact(slice).is_ok();
|
|
*self.reg_mut(Register(0)) = if is_ok { 0 } else { 1 };
|
|
}
|
|
_ => unreachable!("checked in compiler"),
|
|
}
|
|
}
|
|
|
|
fn read_value(&self, value: &Value) -> u64 {
|
|
match value {
|
|
Value::Literal(n) => *n,
|
|
Value::Place(place) => self.read_place(place),
|
|
}
|
|
}
|
|
|
|
fn read_byte_value(&self, value: &Value) -> u8 {
|
|
match value {
|
|
Value::Literal(n) => *n as u8,
|
|
Value::Place(place) => self.read_byte_place(place),
|
|
}
|
|
}
|
|
|
|
fn read_place(&self, place: &Place) -> u64 {
|
|
match place {
|
|
Place::Register(reg) => self.reg(*reg),
|
|
Place::AddrRegister(reg) => {
|
|
let addr = self.reg_addr(*reg);
|
|
self.read_addr(addr)
|
|
}
|
|
Place::AddrLiteral(addr) => {
|
|
let addr = *addr as usize;
|
|
self.read_addr(addr)
|
|
}
|
|
}
|
|
}
|
|
|
|
fn read_byte_place(&self, place: &Place) -> u8 {
|
|
match place {
|
|
Place::Register(reg) => self.reg(*reg) as u8,
|
|
Place::AddrRegister(reg) => {
|
|
let addr = self.reg_addr(*reg);
|
|
self.memory[addr]
|
|
}
|
|
Place::AddrLiteral(addr) => {
|
|
let addr = *addr as usize;
|
|
self.memory[addr]
|
|
}
|
|
}
|
|
}
|
|
|
|
fn write_place(&mut self, place: &Place, value: u64) {
|
|
match place {
|
|
Place::Register(reg) => {
|
|
*self.reg_mut(*reg) = value;
|
|
}
|
|
Place::AddrRegister(reg) => {
|
|
let addr = self.reg_addr(*reg);
|
|
self.write_addr(addr, value);
|
|
}
|
|
Place::AddrLiteral(addr) => {
|
|
let addr = *addr as usize;
|
|
self.write_addr(addr, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn write_byte_place(&mut self, place: &Place, value: u8) {
|
|
match place {
|
|
Place::Register(reg) => {
|
|
*self.reg_mut(*reg) = value.into();
|
|
}
|
|
Place::AddrRegister(reg) => {
|
|
let addr = self.reg_addr(*reg);
|
|
self.memory[addr] = value;
|
|
}
|
|
Place::AddrLiteral(addr) => {
|
|
let addr = *addr as usize;
|
|
self.memory[addr] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn read_addr(&self, addr: usize) -> u64 {
|
|
u64::from_le_bytes([
|
|
self.memory[addr],
|
|
self.memory[addr + 1],
|
|
self.memory[addr + 2],
|
|
self.memory[addr + 3],
|
|
self.memory[addr + 4],
|
|
self.memory[addr + 5],
|
|
self.memory[addr + 6],
|
|
self.memory[addr + 7],
|
|
])
|
|
}
|
|
|
|
fn write_addr(&mut self, addr: usize, value: u64) {
|
|
assert!(addr + 7 < self.memory.len());
|
|
let bytes = value.to_le_bytes();
|
|
for i in 0..8 {
|
|
self.memory[addr + i] = bytes[i];
|
|
}
|
|
}
|
|
|
|
fn reg(&self, reg: Register) -> u64 {
|
|
self.registers[reg.as_index()]
|
|
}
|
|
|
|
fn reg_mut(&mut self, reg: Register) -> &mut u64 {
|
|
&mut self.registers[reg.as_index()]
|
|
}
|
|
|
|
fn reg_addr(&self, reg: Register) -> usize {
|
|
self.reg(reg) as usize
|
|
}
|
|
}
|
|
|
|
// tag::interpret[]
|
|
pub fn interpret(stmts: Vec<Stmt>) -> Result<()> {
|
|
// end::interpret[]
|
|
let mut ctx = InterpretCtx {
|
|
memory: vec![0; MEMORY_SIZE],
|
|
registers: [0; 16],
|
|
flag: false,
|
|
ip: 0,
|
|
};
|
|
|
|
ctx.interpret(stmts)
|
|
}
|