368 lines
9.1 KiB
Rust
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();
|
|
}
|