eis/src/main.rs

177 lines
4.9 KiB
Rust

use byteorder::{ReadBytesExt, LE};
use std::io::Result;
type Word = u16;
//type Memory = [Word; Word::MAX as usize + 1];
struct Memory {
words: [Word; Word::MAX as usize + 1],
}
impl Memory {
fn get(&self, index: Word) -> Word {
self.words[index as usize]
}
fn get_ref(&self, index: Word) -> Word {
// equivalent to get(get(index))
self.words[self.words[index as usize] as usize]
}
fn put(&mut self, index: Word, value: Word) {
self.words[index as usize] = value;
}
//fn put_ref(&mut self, index: Word, value: Word) {}
fn new(path_str: &str) -> Memory {
let mut file = match std::fs::File::open(path_str) {
Result::Ok(x) => x,
Result::Err(_) => panic!("could not open program"),
};
let mut mem = Memory {
words: [0x0; u16::MAX as usize + 1],
};
let mut index = 0;
loop {
match file.read_u16::<LE>() {
Result::Ok(word) => {
mem.words[index] = word;
//println!("DEBUG: WORD: {:#06x}", word);
index += 1;
}
Result::Err(error) => match error.kind() {
std::io::ErrorKind::UnexpectedEof => {
//println!("DEBUG: EOF");
break;
}
other_error => panic!("Other error: {:?}", other_error),
},
}
}
mem
}
}
struct Cpu {
//ax: Word, // Accumulator
//bx: Word, // Base
//cx: Word, // Counter
//dx: Word, // Data
//si: Word, // Source Index
//di: Word, // Destination Index
//bp: Word, // Base Pointer
//sp: Word, // Stack Pointer
ip: Word, // Instruction Pointer
// Segment & Status registers ignored for now
memory: Memory, // for convenience, the CPU has ownership of the memory
//registers are part of the memory now...
// TODO proper input handling (probably read from command line)
input: Vec<u8>,
input_index: usize,
}
impl Cpu {
fn _read_input(&mut self) -> Word {
// TODO length check or something...
let byte = self.input[self.input_index] as Word;
self.input_index += 1;
//println!("DEBUG: read byte {}", byte);
byte
}
fn _first_arg(&self) -> Word {
self.memory
.get(match self.memory.get(self.ip).checked_add(1) {
Some(x) => x,
None => panic!("malformed instruction at {}", self.memory.get(self.ip)),
})
}
fn _second_arg(&self) -> Word {
self.memory
.get(match self.memory.get(self.ip).checked_add(2) {
Some(x) => x,
None => panic!("malformed instruction at {}", self.memory.get(self.ip)),
})
}
fn step(&mut self) {
//println!("DEBUG: IP: {}", self.memory.get(self.ip));
// fetch and decode instruction
let (op, len) = match self.memory.get_ref(self.ip) {
0x10 => (Op::Input(self._first_arg()), 2),
0x11 => (Op::Output(self._first_arg()), 2),
0x20 => (Op::Mov(self._first_arg(), self._second_arg()), 3),
0x90 => (Op::Nop, 1),
0xFF => (Op::Exit(self._first_arg()), 2),
x => panic!("unknown instruction {} at {}", x, self.memory.get(self.ip)),
};
// increment ip
self.memory
.put(self.ip, self.memory.get(self.ip).wrapping_add(len));
// execute instruction
match op {
Op::Input(address) => {
//println!("DEBUG: INPUT: {}", address);
let x = self._read_input();
self.memory.put(address, x);
}
Op::Mov(dest, src) => self.memory.put(dest, self.memory.get(src)),
Op::Nop => {}
Op::Output(address) => {
//println!("DEBUG: OUTPUT: {}", address);
print!("{} ", self.memory.get(address));
}
Op::Exit(status) => std::process::exit(status as i32),
}
}
fn new(memory: Memory) -> Cpu {
// TODO number registers, leave 0 for NULL
// TODO either put at end of address space
// or offset programs
Cpu {
ip: 9,
memory,
input: vec![54, 65, 73, 74], // TODO proper input from command line or stdin
input_index: 0,
}
}
}
enum Op {
Input(Word),
Mov(Word, Word),
Nop,
Output(Word),
Exit(Word),
}
/*
impl Op {
fn length(&self) -> Word {
match *self {
Op::Nop => 1,
Op::Input(_) | Op::Output(_) => 2,
Op::Mov(_, _) => 3,
}
}
}*/
fn main() {
let args: Vec<String> = std::env::args().collect();
let memory = Memory::new(&args[1]);
let mut cpu = Cpu::new(memory);
for _ in 0..=255 {
cpu.step();
}
println!();
}