ilo-vm/source/ilo.rs

368 lines
9.1 KiB
Rust

// ilo.rs, (c) charles childers, (c) 2024 Matt Keeter
use std::convert::TryInto;
use std::fs::{File, OpenOptions};
use std::io;
use std::io::{Read, Seek, SeekFrom, Write};
struct Ilo {
/// memory
memory: [i32; 65536],
/// data stack
data: [i32; 33],
/// address stack
addr: [i32; 257],
/// data stack pointer
sp: usize,
/// address stack pointer
rp: usize,
/// instruction pointer
ip: usize,
}
impl Default for Ilo {
fn default() -> Self {
Self {
memory: [0; 65536],
data: [0; 33],
addr: [0; 257],
sp: 0,
rp: 0,
ip: 0,
}
}
}
impl Ilo {
fn load_image(&mut self) {
match std::fs::read("ilo.rom") {
Ok(image) => {
for (i, c) in image.chunks(4).enumerate() {
self.store(i32::from_le_bytes(c.try_into().unwrap()), i);
}
}
Err(e) => {
panic!("could not load 'ilo.rom': {}", e);
}
}
}
fn read_block(&mut self, block: i32, buffer: i32) -> io::Result<()> {
let mut f = File::open("ilo.blocks")?;
let mut data = [0; 4096];
f.seek(SeekFrom::Start(4096 * (block as u64)))?;
f.read_exact(&mut data)?;
for (i, c) in data.chunks(4).enumerate() {
let x = c[0] as i32;
self.store(x, (buffer as usize) + i);
}
Ok(())
}
fn write_block(&self, block: i32, buffer: i32) -> io::Result<()> {
let mut f = OpenOptions::new()
.read(true)
.write(true)
.open("ilo.blocks")?;
let mut data = [0; 4096];
for x in 0..1024 {
let v = self.fetch((buffer as usize) + x);
data[(x * 4)..][..4].copy_from_slice(&v.to_le_bytes());
}
f.seek(SeekFrom::Start(4096 * (block as u64)))?;
f.write_all(&data)?;
Ok(())
}
fn push(&mut self, x: i32) {
self.sp += 1;
self.data[self.sp] = x;
}
fn pop(&mut self) -> i32 {
self.sp -= 1;
self.data[self.sp + 1]
}
fn fetch(&self, a: usize) -> i32 {
self.memory[a]
}
fn store(&mut self, v: i32, a: usize) {
self.memory[a] = v;
}
fn reset(&mut self) {
self.ip = 0;
self.sp = 0;
self.rp = 0;
}
fn next(&mut self) {
self.ip += 1;
}
fn li(&mut self) {
self.next();
self.push(self.fetch(self.here()));
}
fn du(&mut self) {
self.push(self.data[self.sp]);
}
fn dr(&mut self) {
self.pop();
}
fn sw(&mut self) {
let x = self.pop();
let y = self.pop();
self.push(x);
self.push(y);
}
fn pu(&mut self) {
self.rp += 1;
self.addr[self.rp] = self.pop();
}
fn po(&mut self) {
self.push(self.addr[self.rp]);
self.rp -= 1;
}
fn ju(&mut self) {
self.ip = (self.pop() - 1) as usize;
}
fn ca(&mut self) {
self.rp += 1;
self.addr[self.rp] = (self.ip) as i32;
self.ip = (self.pop() - 1) as usize;
}
fn cc(&mut self) {
let t = self.pop();
if self.pop() != 0 {
self.push(t);
self.ca();
}
}
fn cj(&mut self) {
let t = self.pop();
if self.pop() != 0 {
self.push(t);
self.ju();
}
}
fn re(&mut self) {
self.ip = (self.addr[self.rp]) as usize;
self.rp -= 1;
}
fn eq(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(if x == y { -1 } else { 0 });
}
fn ne(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(if x != y { -1 } else { 0 });
}
fn lt(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(if x < y { -1 } else { 0 });
}
fn gt(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(if x > y { -1 } else { 0 });
}
fn fe(&mut self) {
let x = self.pop() as usize;
self.push(self.fetch(x));
}
fn st(&mut self) {
let x = self.pop() as usize;
let v = self.pop();
self.store(v, x);
}
fn ad(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x.wrapping_add(y));
}
fn su(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x.wrapping_sub(y));
}
fn mu(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x.wrapping_mul(y));
}
fn di(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x.wrapping_rem(y));
self.push(x.wrapping_div(y));
}
fn an(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x & y);
}
fn or(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x | y);
}
fn xo(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x ^ y);
}
fn sl(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x.wrapping_shl(y.try_into().unwrap()));
}
fn sr(&mut self) {
let y = self.pop();
let x = self.pop();
self.push(x.wrapping_shr(y.try_into().unwrap()));
}
fn cp(&mut self) {
let len = self.pop() as usize;
let dest = self.pop() as usize;
let src = self.pop() as usize;
let slice1 = &self.memory[src..(src + len)];
let slice2 = &self.memory[dest..(dest + len)];
if slice1 == slice2 {
self.push(-1);
} else {
self.push(0);
}
}
fn cy(&mut self) {
let mut len = self.pop();
let mut dest = self.pop() as usize;
let mut src = self.pop() as usize;
while len > 0 {
self.store(self.fetch(src), dest);
len -= 1;
src += 1;
dest += 1;
}
}
fn io(&mut self) {
let dev = self.pop();
match dev {
0 => {
let mut c = self.pop();
if c == 0 {
c = 32;
}
let mut stdout = std::io::stdout().lock();
stdout.write_all(&[c as u8]).unwrap();
stdout.flush().unwrap();
}
1 => match std::io::stdin().lock().bytes().next().unwrap() {
Ok(127) => self.push(8),
Ok(val) => self.push(val as i32),
Err(_) => std::process::exit(1),
},
2 => {
let buffer = self.pop();
let block = self.pop();
if let Err(e) = self.read_block(block, buffer) {
panic!("could not read 'ilo.blocks': {}", e);
}
}
3 => {
let buffer = self.pop();
let block = self.pop();
if let Err(e) = self.write_block(block, buffer) {
panic!("could not write 'ilo.blocks': {}", e);
}
}
4 => {}
5 => {
self.load_image();
self.ip = 70000;
}
6 => {
self.ip = 65536;
}
7 => {
self.push(self.sp as i32);
self.push(self.rp as i32);
}
_ => (),
}
}
fn process(&mut self, inst: u8) {
match inst {
0 => (), // nop
1 => self.li(),
2 => self.du(),
3 => self.dr(),
4 => self.sw(),
5 => self.pu(),
6 => self.po(),
7 => self.ju(),
8 => self.ca(),
9 => self.cc(),
10 => self.cj(),
11 => self.re(),
12 => self.eq(),
13 => self.ne(),
14 => self.lt(),
15 => self.gt(),
16 => self.fe(),
17 => self.st(),
18 => self.ad(),
19 => self.su(),
20 => self.mu(),
21 => self.di(),
22 => self.an(),
23 => self.or(),
24 => self.xo(),
25 => self.sl(),
26 => self.sr(),
27 => self.cp(),
28 => self.cy(),
29 => self.io(),
_ => (),
}
}
fn not_done(&self) -> bool {
self.ip < 65536
}
fn here(&self) -> usize {
self.ip
}
fn execute(&mut self) {
self.reset();
while self.not_done() {
let opcode = self.fetch(self.here());
for op in opcode.to_le_bytes() {
self.process(op);
}
if self.here() == 70000 {
self.reset();
} else {
self.next();
}
}
}
fn dump_stack(&mut self) {
while self.sp > 0 {
let x = self.pop();
println!("{x}");
}
}
}
fn main() {
let mut ilo = Ilo::default();
ilo.load_image();
ilo.execute();
ilo.dump_stack();
}