uxn/src/vm.rs

618 lines
22 KiB
Rust

use std::borrow::Borrow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CStr;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::io::Read;
use std::rc::Rc;
use std::*;
use crate::device::console::ConsoleDevice;
use crate::device::null::NullDevice;
use crate::device::system::SystemDevice;
use crate::device::*;
use crate::isa::Icode;
use crate::memory::*;
use crate::stack::arrs::ArrayStack;
use crate::stack::pop::PopStack;
use crate::stack::*;
#[derive(Debug, PartialEq)]
pub enum UxnError {
StackError(StackError),
MemoryError(MemoryError),
DeviceError(DeviceError),
ExecutionLimit(u16),
Break,
ArithmeticOverflow,
ArithmeticUnderflow,
DivisionByZero,
}
impl From<StackError> for UxnError {
fn from(e: StackError) -> UxnError {
UxnError::StackError(e)
}
}
impl From<MemoryError> for UxnError {
fn from(e: MemoryError) -> UxnError {
UxnError::MemoryError(e)
}
}
impl From<DeviceError> for UxnError {
fn from(e: DeviceError) -> UxnError {
UxnError::DeviceError(e)
}
}
#[derive(Debug)]
pub struct Uxn {
pub memory: Rc<RefCell<TrivialMemory>>,
// Note: Using Rc so we can start with many NullDevs and replace them
pub devices: [Rc<RefCell<dyn Device>>; 16],
pub pc: u16, // Program counter
pub clock: u64, // Executed instructions count
pub wst: Rc<RefCell<dyn Stack>>, // Data stack
pub rst: Rc<RefCell<dyn Stack>>, // Return stack pointer
pub symbols: HashMap<u16, String>, // Symbol table
}
impl Uxn {
pub fn new() -> Uxn {
let mut uxn = Uxn {
memory: Rc::new(RefCell::new(TrivialMemory::new())),
devices: [
Rc::new(RefCell::new(SystemDevice::new())), // #00
Rc::new(RefCell::new(ConsoleDevice::new())), // #01
Rc::new(RefCell::new(NullDevice::new())), // #02
Rc::new(RefCell::new(NullDevice::new())), // #03
Rc::new(RefCell::new(NullDevice::new())), // #04
Rc::new(RefCell::new(NullDevice::new())), // #05
Rc::new(RefCell::new(NullDevice::new())), // #06
Rc::new(RefCell::new(NullDevice::new())), // #07
Rc::new(RefCell::new(NullDevice::new())), // #08
Rc::new(RefCell::new(NullDevice::new())), // #09
Rc::new(RefCell::new(NullDevice::new())), // #0a
Rc::new(RefCell::new(NullDevice::new())), // #0b
Rc::new(RefCell::new(NullDevice::new())), // #0c
Rc::new(RefCell::new(NullDevice::new())), // #0d
Rc::new(RefCell::new(NullDevice::new())), // #0e
Rc::new(RefCell::new(NullDevice::new())), // #0f
],
pc: 0x0100,
clock: 0,
wst: Rc::new(RefCell::new(ArrayStack::new())),
rst: Rc::new(RefCell::new(ArrayStack::new())),
symbols: HashMap::new(),
};
uxn.symbols.insert(0x0100u16, "main".into());
uxn
}
pub fn of1(program: &[u8]) -> Uxn {
let mut vm = Uxn::new();
let mut pc = vm.pc;
for icode in program.iter() {
vm.sta1(pc, *icode).unwrap();
pc += 1;
}
vm
}
pub fn load_rom(&mut self, file: File) -> Result<(), UxnError> {
let reader = BufReader::new(file);
let mut i = 0x0100u16;
for b in reader.bytes() {
self.sta1(i, b.unwrap())?;
i += 1;
}
Ok(())
}
pub fn load_symbols(&mut self, file: File) -> Result<(), io::Error> {
let mut reader: BufReader<File> = BufReader::new(file);
loop {
let mut addr_buff = [0u8; 2];
match reader.read_exact(&mut addr_buff) {
Ok(_) => (),
Err(_) => break,
}
let addr = u16::from_le_bytes(addr_buff);
let mut sym_buff: Vec<u8> = Vec::new();
reader.read_until(0u8, &mut sym_buff)?;
let label = CStr::from_bytes_with_nul(sym_buff.as_slice())
.unwrap()
.to_str()
.unwrap()
.to_string();
self.symbols.insert(addr, label);
}
Ok(())
}
pub fn is_halted(&mut self) -> bool {
self.dei1(0x0f).unwrap() != 0
}
pub fn is_tracing(&self) -> bool {
false
}
pub fn debug(&mut self) {
let wst = self.wst.clone();
let rst = self.rst.clone();
print!(
"<symbols> #{:04X}\n<clock> #{:04X}\n<pc> #{:04X}\n<data stack>",
self.symbols.len(),
self.clock,
self.pc
);
let wst_idx = wst.borrow_mut().idx();
if wst_idx != 0 {
for i in 0..wst_idx {
print!(" #{:02X}", self.wst.borrow_mut().get1(i).unwrap());
}
} else {
print!(" empty")
}
print!("\n<return stack>");
let rst_idx = rst.borrow_mut().idx();
if rst_idx != 0 {
for i in 0..rst_idx {
print!(" #{:02X}", self.rst.borrow_mut().get1(i).unwrap());
}
} else {
print!(" empty")
}
println!();
}
pub fn debug_symbols(&self) {
for (k, v) in self.symbols.borrow().iter() {
println!(" #{:02X} {}", k, v);
}
}
fn device(&self, port: u8) -> Rc<RefCell<dyn Device>> {
let devnum = ((port & 0xF0) >> 4) as usize;
Rc::clone(&self.devices[devnum])
}
pub fn dei1(&mut self, port: u8) -> Result<u8, DeviceError> {
self.device(port).borrow_mut().dei1(self, port)
}
pub fn dei2(&mut self, port: u8) -> Result<u16, DeviceError> {
self.device(port).borrow_mut().dei2(self, port)
}
pub fn deo1(&mut self, port: u8, val: u8) -> Result<(), DeviceError> {
self.device(port).borrow_mut().deo1(self, port, val)
}
pub fn deo2(&mut self, port: u8, val: u16) -> Result<(), DeviceError> {
self.device(port).borrow_mut().deo2(self, port, val)
}
pub fn sta1(&mut self, address: u16, val: u8) -> Result<(), MemoryError> {
if self.is_tracing() {
eprintln!(
" STA #{:04X} ({}) <- #{:02X}",
address,
self.symbols.get(&address).unwrap_or(&"".to_string()),
val
)
}
self.memory.clone().borrow_mut().set1(address, val)?;
Ok(())
}
pub fn sta2(&mut self, address: u16, val: u16) -> Result<(), MemoryError> {
if self.is_tracing() {
eprintln!(
" STA #{:04X} ({}) <- #{:04X}",
address,
self.symbols.get(&address).unwrap_or(&"".to_string()),
val
)
}
self.memory.clone().borrow_mut().set2(address, val)?;
Ok(())
}
pub fn lda1(&self, address: u16) -> Result<u8, MemoryError> {
if address != self.pc && self.is_tracing() {
eprintln!(
" LDA #{:04X} ({})",
address,
self.symbols.get(&address).unwrap_or(&"".to_string()),
)
}
self.memory.clone().borrow_mut().get1(address)
}
pub fn lda2(&self, address: u16) -> Result<u16, MemoryError> {
if address != self.pc && self.is_tracing() {
eprintln!(
" LDA #{:04X} ({})",
address,
self.symbols.get(&address).unwrap_or(&"".to_string()),
)
}
self.memory.clone().borrow_mut().get2(address)
}
pub fn branch(&mut self, address: u16) {
match self.symbols.get(&address) {
Some(label) => {
if self.is_tracing() {
eprintln!(
"Branch #{:04X} to {} (#{:04X})",
self.pc - 1,
label,
address
)
}
}
None => (),
}
self.pc = address;
}
// Run one clock cycle (instruction)
pub fn step(&mut self) -> Result<(), UxnError> {
match self.lda1(self.pc) {
Err(e) => Err(UxnError::MemoryError(e)),
Ok(icode) => {
match self.symbols.get(&self.pc) {
Some(sym) => {
if self.is_tracing() {
eprintln!("{}:", sym)
}
}
None => (),
}
// The value of PC is defined to be the value of the NEXT pc ala Mips
self.pc += 1;
self.clock += 1;
// Short circuit for cheap NOPs (POPk, POPkr)
if (icode & !Icode::RETURN) == Icode::NOP {
return Ok(());
}
// Extract flags
let (kflag, rflag, sflag, icode5) = Icode::parse(icode);
if self.is_tracing() {
eprintln!(
" cycle #{:04X}; pc #{:04X}, rom pc: #{:04X}: {} ( {:05b} s: {:1x} r: {:1x} k: {:1x} )",
self.clock,
self.pc - 1,
self.pc - 0x0101,
Icode::nameof(icode),
icode5,
sflag,
rflag,
kflag
)
}
// Swizzle the stacks as needed
let [wst, rst]: [Rc<RefCell<dyn Stack>>; 2] = {
if rflag == 0 {
[self.wst.clone(), self.rst.clone()]
} else {
[self.rst.clone(), self.wst.clone()]
}
};
// Inject a PopStack shim to support the 'keep' bit as needed
let [wst, rst]: [Rc<RefCell<dyn Stack>>; 2] = {
if kflag == 0 {
[wst, rst]
} else {
[
Rc::new(RefCell::new(PopStack::new(wst))),
Rc::new(RefCell::new(PopStack::new(rst))),
]
}
};
// Some procedural abstractions over load/store sizes
let pop = |stack: Rc<RefCell<dyn Stack>>| -> Result<u16, StackError> {
if sflag == 1 {
stack.borrow_mut().pop2()
} else {
Ok(stack.borrow_mut().pop1()? as u16)
}
};
let push = |stack: Rc<RefCell<dyn Stack>>, val: u16| {
if sflag == 1 {
stack.borrow_mut().push2(val)
} else {
stack.borrow_mut().push1(val as u8)
}
};
let load = |addr: u16| -> Result<u16, MemoryError> {
if sflag == 1 {
self.lda2(addr)
} else {
Ok(self.lda1(addr)? as u16)
}
};
// FIXME: Can't use self.sta1/sta2 due to *self reference uniqueness
let store = |this: &mut Uxn, addr: u16, val: u16| {
if sflag == 1 {
this.sta2(addr, val)
} else {
this.sta1(addr, val as u8)
}
};
match (kflag, rflag, sflag, icode5) {
(0, 0, 0, Icode::BRK) => {
// BRK --
return Err(UxnError::Break);
}
(1, _, _, Icode::BRK) => {
// BRKk aka LIT -- a
let val = load(self.pc)?;
push(wst.clone(), val)?;
self.branch(self.pc + if sflag == 1 { 2 } else { 1 });
}
(_, _, _, Icode::INC) => {
// INC a -- a
push(wst.clone(), pop(wst.clone())?.wrapping_add(1))?;
}
(_, _, _, Icode::POP) => {
// POP a --
pop(wst)?;
}
(_, _, _, Icode::NIP) => {
// NIP a b -- b
let keep = pop(wst.clone())?;
pop(wst.clone())?;
push(wst.clone(), keep)?;
}
(_, _, _, Icode::SWP) => {
// SWP a b -- b a
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), b)?;
push(wst.clone(), a)?;
}
(_, _, _, Icode::ROT) => {
// ROT a b c -- b c a
let c = pop(wst.clone())?;
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), b)?;
push(wst.clone(), c)?;
push(wst.clone(), a)?;
}
(_, _, _, Icode::DUP) => {
// DUP a -- a a
let a = pop(wst.clone())?;
push(wst.clone(), a)?;
push(wst.clone(), a)?;
}
(_, _, _, Icode::OVR) => {
// OVR a b -- a b a
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a)?;
push(wst.clone(), b)?;
push(wst.clone(), a)?;
}
(_, _, _, Icode::EQL) => {
// EQU a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
wst.borrow_mut().push1(if a == b { 1 } else { 0 })?;
}
(_, _, _, Icode::NEQ) => {
// NEQ a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
wst.borrow_mut().push1(if a == b { 0 } else { 1 })?;
}
(_, _, _, Icode::GTH) => {
// GTH a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
wst.borrow_mut().push1(if a > b { 1 } else { 0 })?;
}
(_, _, _, Icode::LTH) => {
// LTH a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
wst.borrow_mut().push1(if a < b { 1 } else { 0 })?;
}
(_, _, 0, Icode::JMP) => {
// JMP1 a --
let delta = wst.borrow_mut().pop1()? as i8;
self.branch(self.pc.wrapping_add(delta as u16));
}
(_, _, 1, Icode::JMP) => {
// JMP2 a --
let target = wst.borrow_mut().pop2()?;
self.branch(target);
}
(_, _, 0, Icode::JCN) => {
// JCN1 cnd8 addr8 (relative)
let delta = wst.borrow_mut().pop1()? as i8;
let cnd = wst.borrow_mut().pop1()?;
if cnd != 0 {
self.branch(self.pc.wrapping_add(delta as u16));
}
}
(_, _, 1, Icode::JCN) => {
// JCN2 cnd8 addr16 (absolute)
let addr = wst.borrow_mut().pop2()?;
let cnd = wst.borrow_mut().pop1()?;
if cnd != 0 {
self.branch(addr);
}
}
(_, _, 0, Icode::JSR) => {
// JSR1 addr8 (relative)
rst.borrow_mut().push2(self.pc)?;
let delta = wst.borrow_mut().pop1()? as i8;
self.branch(self.pc.wrapping_add(delta as u16));
}
(_, _, 1, Icode::JSR) => {
// JSR2 addr16 (absolute)
rst.borrow_mut().push2(self.pc)?;
self.branch(wst.borrow_mut().pop2()?);
}
(_, _, _, Icode::STH) => {
// STH a
push(rst, pop(wst)?)?;
}
(_, _, _, Icode::LDZ) => {
// LDZ a8 -- b
let addr = wst.borrow_mut().pop1()? as u16;
push(wst.clone(), load(addr)?)?;
}
(_, _, _, Icode::STZ) => {
// STZ val addr8 --
let addr = wst.borrow_mut().pop1()? as u16;
store(self, addr, pop(wst.clone())?)?;
}
(_, _, _, Icode::LDR) => {
// LDR addr8 -- a8
let delta = wst.borrow_mut().pop1()? as i8;
let addr = self.pc.wrapping_add(delta as u16);
push(wst, load(addr)?)?;
}
(_, _, _, Icode::STR) => {
// STR val addr8 --
let delta = wst.borrow_mut().pop1()? as i8;
let addr = self.pc.wrapping_add(delta as u16);
store(self, addr, pop(wst)?)?;
}
(_, _, _, Icode::LDA) => {
// LDA a16
let addr = wst.borrow_mut().pop2()?;
push(wst.clone(), load(addr)?)?;
}
(_, _, _, Icode::STA) => {
// STA val a16 --
let addr = wst.borrow_mut().pop2()?;
store(self, addr, pop(wst.clone())?)?;
}
(_, _, 0, Icode::DEI) => {
// DEI port8 -- a8
let mut wst = wst.borrow_mut();
let port = wst.pop1()?;
wst.push1(self.dei1(port)?)?;
}
(_, _, 1, Icode::DEI) => {
// DEI2 port8 -- a16
let mut wst = wst.borrow_mut();
let port = wst.pop1()?;
wst.push2(self.dei2(port)?)?;
}
(_, _, 0, Icode::DEO) => {
// DEO1 a8 port8 --
let port = wst.borrow_mut().pop1()?;
let val = wst.borrow_mut().pop1()?;
self.deo1(port, val).unwrap();
}
(_, _, 1, Icode::DEO) => {
// DEO2 a16 port8 --
let mut wst = wst.borrow_mut();
let port = wst.pop1()?;
let val = wst.pop2()?;
self.deo2(port, val).unwrap();
}
(_, _, _, Icode::ADD) => {
// ADD a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a.wrapping_add(b))?;
}
(_, _, _, Icode::SUB) => {
// SUB a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a.wrapping_sub(b))?;
}
(_, _, _, Icode::MUL) => {
// MUL a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a.wrapping_mul(b))?;
}
(_, _, _, Icode::DIV) => {
// DIV a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a / b)?;
}
(_, _, _, Icode::AND) => {
// AND a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a & b)?;
}
(_, _, _, Icode::ORA) => {
// OR a b -- c
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a | b)?;
}
(_, _, _, Icode::EOR) => {
// XOR a b -- c8
let b = pop(wst.clone())?;
let a = pop(wst.clone())?;
push(wst.clone(), a ^ b)?;
}
(_, _, _, Icode::SFT) => {
// SFT a shift8 -- b
let shift = wst.borrow_mut().pop1()?;
let [left, right] = [shift >> 4 & 0xF, shift & 0xF];
let a = pop(wst.clone())?;
push(wst.clone(), (a >> right) << left)?;
}
_ => {
panic!("Unsupported opcode {}", Icode::nameof(icode))
}
}
Ok(())
}
}
}
pub fn run(&mut self, limit: u16) -> Result<(), UxnError> {
let mut executed = 0;
loop {
if executed == limit {
return Err(UxnError::ExecutionLimit(executed));
}
executed += 1;
match self.step() {
Err(e) => return Err(e),
Ok(()) => continue,
};
}
}
}