retroforth/vm/nga-rust/nga.rs
crc 23e720bec2 add rust implementation from bitmapper in the #retro irc channel
FossilOrigin-Name: 422104f55d03915c5c880725df1dd1a99d3c5e66ed5bb0b96db90124780387df
2021-10-26 14:32:05 +00:00

457 lines
11 KiB
Rust

use std::convert::TryInto;
use std::io::{BufRead, Read, Write};
use std::process::exit;
const IMAGE_SIZE: usize = 242000;
const NUM_DEVICES: usize = 2;
type Cell = i32;
struct VM {
ip: Cell,
data: Vec<Cell>,
address: Vec<Cell>,
memory: Vec<Cell>,
notfound: Cell,
tib: Cell,
instructions: [fn(&mut VM) -> (); 30],
io_device_handlers: [fn(&mut VM) -> (); NUM_DEVICES],
io_query_handlers: [fn(&mut VM) -> (); NUM_DEVICES],
}
impl VM {
fn pop_data(&mut self) -> Cell {
if let Some(value) = self.data.pop() {
return value;
} else {
panic!("stack underflow!");
}
}
fn push_data(&mut self, value: Cell) {
self.data.push(value);
}
fn pop_address(&mut self) -> Cell {
if let Some(value) = self.address.pop() {
return value;
} else {
panic!("stack underflow!");
}
}
fn push_address(&mut self, value: Cell) {
self.address.push(value);
}
fn nga_load_image(&mut self, image_file: &str) {
if let Ok(image) = std::fs::read(image_file) {
for (i, c) in image.chunks(4).enumerate() {
self.memory[i] = i32::from_le_bytes(c.try_into().unwrap());
}
} else {
println!("Unable to find the ngaImage!");
exit(1);
}
}
fn nga_string_extract(&self, at: usize) -> String {
self.memory[at..]
.into_iter()
.take_while(|&x| *x != 0)
.flat_map(|&x| std::char::from_u32(x as u32))
.collect()
}
fn nga_string_inject(&mut self, string: String, at: usize) {
for (i, c) in string.bytes().enumerate() {
self.memory[at + i] = c.into();
self.memory[at + i + 1] = 0;
}
}
fn nga_lookup(&self, name: String) -> usize {
let mut i: usize = self.memory[2] as usize;
while self.memory[i] != 0 && i != 0 {
let target = self.nga_string_extract(i + 4);
if name == target {
return i;
} else {
i = self.memory[i] as usize;
}
}
return 0;
}
fn inst_nop(&mut self) {
return ();
}
fn inst_lit(&mut self) {
self.ip += 1;
self.push_data(self.memory[self.ip as usize]);
}
fn inst_dup(&mut self) {
let temp: Cell = self.pop_data();
self.push_data(temp);
self.push_data(temp);
}
fn inst_drop(&mut self) {
if self.data.len() == 0 {
self.ip = IMAGE_SIZE as i32;
} else {
self.pop_data();
}
}
fn inst_swap(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_a);
self.push_data(temp_b);
}
fn inst_push(&mut self) {
let temp: Cell = self.pop_data();
self.push_address(temp);
}
fn inst_pop(&mut self) {
let temp: Cell = self.pop_address();
self.push_data(temp);
}
fn inst_jump(&mut self) {
self.ip = self.pop_data() - 1;
}
fn inst_call(&mut self) {
self.push_address(self.ip);
self.ip = self.pop_data() - 1;
}
fn inst_ccall(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
if temp_b != 0 {
self.push_address(self.ip);
self.ip = temp_a - 1;
}
}
fn inst_return(&mut self) {
self.ip = self.pop_address();
}
fn inst_eq(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
if temp_b == temp_a {
self.push_data(-1);
} else {
self.push_data(0);
}
}
fn inst_neq(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
if temp_b != temp_a {
self.push_data(-1);
} else {
self.push_data(0);
}
}
fn inst_lt(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
if temp_b < temp_a {
self.push_data(-1);
} else {
self.push_data(0);
}
}
fn inst_gt(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
if temp_b > temp_a {
self.push_data(-1);
} else {
self.push_data(0);
}
}
fn inst_fetch(&mut self) {
match self.pop_data() {
-1 => self.push_data(self.data.len() as i32),
-2 => self.push_data(self.address.len() as i32),
-3 => self.push_data(IMAGE_SIZE as i32),
-4 => self.push_data(-2147483648),
-5 => self.push_data(2147483647),
pt => self.push_data(self.memory[pt as usize]),
}
}
fn inst_store(&mut self) {
let addr: Cell = self.pop_data();
let value: Cell = self.pop_data();
self.memory[addr as usize] = value;
}
fn inst_add(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_b + temp_a);
}
fn inst_sub(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_b - temp_a);
}
fn inst_mul(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_b * temp_a);
}
fn inst_divmod(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_b % temp_a);
self.push_data(temp_b / temp_a);
}
fn inst_and(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_a & temp_b);
}
fn inst_or(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_a | temp_b);
}
fn inst_xor(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
self.push_data(temp_a ^ temp_b);
}
fn inst_shift(&mut self) {
let temp_a: Cell = self.pop_data();
let temp_b: Cell = self.pop_data();
if temp_b < 0 {
self.push_data(temp_b << temp_a);
} else {
self.push_data(temp_b >> temp_a);
}
}
fn inst_zret(&mut self) {
match self.pop_data() {
0 => self.ip = self.pop_address(),
n => self.push_data(n),
}
}
fn inst_halt(&mut self) {
self.ip = IMAGE_SIZE as i32;
}
fn inst_ie(&mut self) {
self.push_data(NUM_DEVICES as i32);
}
fn inst_iq(&mut self) {
self.io_query_handlers[self.pop_data() as usize](self);
}
fn inst_ii(&mut self) {
self.io_device_handlers[self.pop_data() as usize](self);
}
fn nga_process_opcode(&mut self, opcode: Cell) {
if opcode != 0 {
self.instructions[opcode as usize](self);
}
}
fn nga_validate_packed_opcodes(&mut self, opcode: Cell) -> bool {
opcode.to_le_bytes().iter().all(|&x| x & 0xFF <= 29)
}
fn nga_process_packed_opcodes(&mut self, opcode: Cell) {
for &x in opcode.to_le_bytes().iter() {
self.nga_process_opcode(x as Cell & 0xFF)
}
}
fn generic_output(&mut self) {
std::io::stdout()
.lock()
.write(&[self.pop_data() as u8])
.unwrap();
std::io::stdout()
.lock()
.flush()
.unwrap();
}
fn generic_output_query(&mut self) {
self.push_data(0);
self.push_data(0);
}
fn generic_input(&mut self) {
match std::io::stdin().lock().bytes().next().unwrap() {
Ok(127) => self.push_data(8),
Ok(val) => self.push_data(val as i32),
Err(_) => exit(1),
}
}
fn generic_input_query(&mut self) {
self.push_data(0);
self.push_data(1);
}
fn nga_lookup_xt(&mut self, name: String) -> Cell {
self.memory[1 + self.nga_lookup(name)]
}
fn new() -> VM {
VM {
ip: 0,
data: Vec::new(),
address: Vec::new(),
notfound: 0,
tib: 0,
memory: vec![0; IMAGE_SIZE],
instructions: [
VM::inst_nop,
VM::inst_lit,
VM::inst_dup,
VM::inst_drop,
VM::inst_swap,
VM::inst_push,
VM::inst_pop,
VM::inst_jump,
VM::inst_call,
VM::inst_ccall,
VM::inst_return,
VM::inst_eq,
VM::inst_neq,
VM::inst_lt,
VM::inst_gt,
VM::inst_fetch,
VM::inst_store,
VM::inst_add,
VM::inst_sub,
VM::inst_mul,
VM::inst_divmod,
VM::inst_and,
VM::inst_or,
VM::inst_xor,
VM::inst_shift,
VM::inst_zret,
VM::inst_halt,
VM::inst_ie,
VM::inst_iq,
VM::inst_ii,
],
io_query_handlers: [
VM::generic_output_query,
VM::generic_input_query,
],
io_device_handlers: [
VM::generic_output,
VM::generic_input,
],
}
}
fn nga_execute(&mut self, at: Cell) {
self.ip = at;
self.push_address(0);
while self.ip < IMAGE_SIZE as Cell {
if self.ip == self.notfound {
println!("{} ?", self.nga_string_extract(self.tib as usize));
}
let opcode: Cell = self.memory[self.ip as usize];
if self.nga_validate_packed_opcodes(opcode) {
self.nga_process_packed_opcodes(opcode);
} else {
println!("Invalid instruction!");
println!("ip: {}, opcode: {}", self.ip, opcode);
exit(1);
}
self.ip += 1;
if self.address.len() == 0 {
self.ip = IMAGE_SIZE as Cell;
}
}
}
fn nga_evaluate(&mut self, string: String) {
if &string == "bye" {
exit(0);
}
self.notfound = self.nga_lookup_xt(String::from("err:notfound"));
let interpret: Cell = self.nga_lookup_xt(String::from("interpret"));
self.nga_string_inject(string, self.tib as usize);
self.push_data(self.tib as i32);
self.nga_execute(interpret);
}
}
fn main() {
let mut vm: VM = VM::new();
vm.nga_load_image("ngaImage");
vm.tib = vm.memory[7];
println!("RETRO 12 (rx-{}.{})", vm.memory[4] / 100, vm.memory[4] % 100);
println!("{} MAX, TIB @ {}, HEAP @ {}", IMAGE_SIZE, vm.tib, vm.memory[3]);
for input in std::io::stdin().lock().lines() {
for word in input.unwrap().split_whitespace() {
vm.nga_evaluate(String::from(word));
}
}
}