[NO TESTS] WIP
This commit is contained in:
parent
d7343b901f
commit
b8eeecb2c3
6 changed files with 471 additions and 275 deletions
19
src/device.rs
Normal file
19
src/device.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
pub mod console;
|
||||||
|
pub mod null;
|
||||||
|
pub mod system;
|
||||||
|
|
||||||
|
use crate::vm::Uxn;
|
||||||
|
|
||||||
|
pub trait Device: std::fmt::Debug {
|
||||||
|
/**
|
||||||
|
* Callbacks used by UXN to do input from a "device".
|
||||||
|
*/
|
||||||
|
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> u8;
|
||||||
|
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used by UXN to do output through a "device".
|
||||||
|
*/
|
||||||
|
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8);
|
||||||
|
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16);
|
||||||
|
}
|
57
src/device/console.rs
Normal file
57
src/device/console.rs
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
/**
|
||||||
|
* The system console device
|
||||||
|
*
|
||||||
|
* This device is ... woefully incomplete relative to the video device.
|
||||||
|
* It allows the UXN vm to poke characters into the standard out device.
|
||||||
|
* It has no standard specified facilities for standard error.
|
||||||
|
*
|
||||||
|
* When the user enters a key onto the emulator's console, the emulator is
|
||||||
|
* expected to enqueue and eventually deliver a signal to the device vector.
|
||||||
|
* As a facility to that end, the
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct ConsoleDevice {
|
||||||
|
vector: u16,
|
||||||
|
inbuffer: [u8; 256],
|
||||||
|
inidx: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ConsoleDevice {
|
||||||
|
pub fn new() -> ConsoleDevice {
|
||||||
|
ConsoleDevice {
|
||||||
|
vector: 0,
|
||||||
|
inidx: 255,
|
||||||
|
inbuffer: [0; 256],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for ConsoleDevice {
|
||||||
|
/**
|
||||||
|
* DEI1 is tricky.
|
||||||
|
*
|
||||||
|
* If the input buffer is empty (inidx == inbuffer.size, default) we want to
|
||||||
|
* perform a buffered line read into inbuffer, and set inidx to 0.
|
||||||
|
*
|
||||||
|
* If inidx < inbuffer.size, we want to return the next character out of the
|
||||||
|
* inbuffer.
|
||||||
|
*/
|
||||||
|
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> u8 {
|
||||||
|
let slot = port & 0xF;
|
||||||
|
match slot {
|
||||||
|
0x2 => 0,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// DEI2 is not supported on the terminal device
|
||||||
|
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback used by UXN to do output through a "device".
|
||||||
|
*/
|
||||||
|
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) {}
|
||||||
|
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) {}
|
||||||
|
}
|
22
src/device/null.rs
Normal file
22
src/device/null.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/**
|
||||||
|
* The null device does nothing when reading or writing.
|
||||||
|
*/
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NullDevice {}
|
||||||
|
|
||||||
|
impl NullDevice {
|
||||||
|
pub fn new() -> NullDevice {
|
||||||
|
NullDevice {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for NullDevice {
|
||||||
|
fn dei1(&mut self, _vm: &mut Uxn, _port: u8) -> u8 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
fn dei2(&mut self, _vm: &mut Uxn, _port: u8) -> u16 {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
fn deo1(&mut self, _vm: &mut Uxn, _port: u8, _val: u8) {}
|
||||||
|
fn deo2(&mut self, _vm: &mut Uxn, _port: u8, _val: u16) {}
|
||||||
|
}
|
50
src/device/system.rs
Normal file
50
src/device/system.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
/**
|
||||||
|
* The system device
|
||||||
|
*/
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SystemDevice {
|
||||||
|
buffer: [u8; 0xF],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SystemDevice {
|
||||||
|
fn new() -> SystemDevice {
|
||||||
|
SystemDevice { buffer: [0; 0xF] }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Device for SystemDevice {
|
||||||
|
fn dei1(&mut self, vm: &mut Uxn, port: u8) -> u8 {
|
||||||
|
let slot = port & 0xF;
|
||||||
|
match slot {
|
||||||
|
0x2 => vm.wst.idx,
|
||||||
|
0x3 => vm.rst.idx,
|
||||||
|
x => self.buffer[x as usize],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn dei2(&mut self, vm: &mut Uxn, port: u8) -> u16 {
|
||||||
|
return ((self.dei1(vm, port) as u16) << 8) + self.dei1(vm, port + 1) as u16;
|
||||||
|
}
|
||||||
|
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) {
|
||||||
|
let slot = port & 0xF;
|
||||||
|
match slot {
|
||||||
|
// Note: This VM does not support mutating the stack pointers.
|
||||||
|
// This operation is not well defined upstream
|
||||||
|
0x2 => panic!("Invoked unsupported mutation of the data stack pointer"),
|
||||||
|
0x3 => panic!("Invoked unsupported mutation of the return stack pointer"),
|
||||||
|
x => self.buffer[x as usize] = val,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) {
|
||||||
|
let slot = port & 0xF;
|
||||||
|
assert!(slot < 0xF, "Double-write beyond the end of the device");
|
||||||
|
match slot {
|
||||||
|
0x2 => panic!("The data stack is single-width, panic on spurious double-write"),
|
||||||
|
0x3 => panic!("The return stack is single-width, panic on spurious double-write"),
|
||||||
|
x => {
|
||||||
|
let [high, low] = val.to_be_bytes();
|
||||||
|
self.deo1(vm, x, high);
|
||||||
|
self.deo1(vm, x + 1, low)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
277
src/lib.rs
277
src/lib.rs
|
@ -1,277 +1,4 @@
|
||||||
|
pub mod device;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod stack;
|
pub mod stack;
|
||||||
|
pub mod vm;
|
||||||
use std::cell::{Cell, RefCell, RefMut};
|
|
||||||
use std::rc::Rc;
|
|
||||||
use std::*;
|
|
||||||
|
|
||||||
use memory::MemoryError;
|
|
||||||
use stack::StackError;
|
|
||||||
|
|
||||||
use crate::memory::{Memory, TrivialMemory};
|
|
||||||
use crate::stack::Stack;
|
|
||||||
|
|
||||||
trait Device: std::fmt::Debug {
|
|
||||||
/**
|
|
||||||
* Callbacks used by UXN to do input from a "device".
|
|
||||||
*/
|
|
||||||
fn dei1(&self, vm: &mut Uxn, port: u8) -> u8;
|
|
||||||
fn dei2(&self, vm: &mut Uxn, port: u8) -> u16;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback used by UXN to do output through a "device".
|
|
||||||
*/
|
|
||||||
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8);
|
|
||||||
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The null device does nothing when reading or writing.
|
|
||||||
*/
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct NullDevice {}
|
|
||||||
|
|
||||||
impl NullDevice {
|
|
||||||
pub fn new() -> NullDevice {
|
|
||||||
NullDevice {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Device for NullDevice {
|
|
||||||
fn dei1(&self, vm: &mut Uxn, port: u8) -> u8 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
fn dei2(&self, vm: &mut Uxn, port: u8) -> u16 {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) {}
|
|
||||||
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The system device
|
|
||||||
*/
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct SystemDevice {
|
|
||||||
buffer: [u8; 0xF],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SystemDevice {
|
|
||||||
fn new() -> SystemDevice {
|
|
||||||
SystemDevice { buffer: [0; 0xF] }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Device for SystemDevice {
|
|
||||||
fn dei1(&self, vm: &mut Uxn, port: u8) -> u8 {
|
|
||||||
let slot = port & 0xF;
|
|
||||||
match slot {
|
|
||||||
0x2 => vm.wst.idx,
|
|
||||||
0x3 => vm.rst.idx,
|
|
||||||
x => self.buffer[x as usize],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn dei2(&self, vm: &mut Uxn, port: u8) -> u16 {
|
|
||||||
return ((self.dei1(vm, port) as u16) << 8) + self.dei1(vm, port + 1) as u16;
|
|
||||||
}
|
|
||||||
fn deo1(&mut self, vm: &mut Uxn, port: u8, val: u8) {
|
|
||||||
let slot = port & 0xF;
|
|
||||||
match slot {
|
|
||||||
// Note: This VM does not support mutating the stack pointers.
|
|
||||||
// This operation is not well defined upstream
|
|
||||||
0x2 => panic!("Invoked unsupported mutation of the data stack pointer"),
|
|
||||||
0x3 => panic!("Invoked unsupported mutation of the return stack pointer"),
|
|
||||||
x => self.buffer[x as usize] = val,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn deo2(&mut self, vm: &mut Uxn, port: u8, val: u16) {
|
|
||||||
let slot = port & 0xF;
|
|
||||||
assert!(slot < 0xF, "Double-write beyond the end of the device");
|
|
||||||
match slot {
|
|
||||||
0x2 => panic!("The data stack is single-width, panic on spurious double-write"),
|
|
||||||
0x3 => panic!("The return stack is single-width, panic on spurious double-write"),
|
|
||||||
x => {
|
|
||||||
let [high, low] = val.to_be_bytes();
|
|
||||||
self.deo1(vm, x, high);
|
|
||||||
self.deo1(vm, x + 1, low)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum UxnError {
|
|
||||||
StackError(StackError),
|
|
||||||
MemoryError(MemoryError),
|
|
||||||
ExecutionLimit(u16),
|
|
||||||
ArithmeticOverflow,
|
|
||||||
ArithmeticUnderflow,
|
|
||||||
DivisionByZero,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Uxn {
|
|
||||||
memory: Rc<RefCell<TrivialMemory>>,
|
|
||||||
// Note: Using Rc so we can start with many NullDevs and replace them
|
|
||||||
devices: [Rc<RefCell<dyn Device>>; 16],
|
|
||||||
pc: u16, // Program counter
|
|
||||||
wst: Stack, // Data stack
|
|
||||||
rst: Stack, // Return stack pointer
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Uxn {
|
|
||||||
pub fn new() -> Uxn {
|
|
||||||
Uxn {
|
|
||||||
memory: Rc::new(RefCell::new(TrivialMemory::new())),
|
|
||||||
devices: [
|
|
||||||
Rc::new(RefCell::new(SystemDevice::new())), // #00
|
|
||||||
Rc::new(RefCell::new(NullDevice::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,
|
|
||||||
wst: Stack::new(),
|
|
||||||
rst: Stack::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_halted(&mut self) -> bool {
|
|
||||||
self.dei1(0x0f) != 0
|
|
||||||
}
|
|
||||||
|
|
||||||
fn device(&self, port: u8) -> Rc<RefCell<dyn Device>> {
|
|
||||||
Rc::clone(&self.devices[(port & 0xF0 >> 4) as usize])
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dei1(&mut self, port: u8) -> u8 {
|
|
||||||
self.device(port).borrow_mut().dei1(self, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dei2(&mut self, port: u8) -> u16 {
|
|
||||||
self.device(port).borrow_mut().dei2(self, port)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deo1(&mut self, port: u8, val: u8) {
|
|
||||||
self.device(port).borrow_mut().deo1(self, port, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deo2(&mut self, port: u8, val: u16) {
|
|
||||||
self.device(port).borrow_mut().deo2(self, port, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sta1(&mut self, address: u16, val: u8) -> Result<(), MemoryError> {
|
|
||||||
self.memory.borrow_mut().set1(address, val)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn sta2(&mut self, address: u16, val: u16) -> Result<(), MemoryError> {
|
|
||||||
self.memory.borrow_mut().set2(address, val)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn lda1(&mut self, address: u16) -> Result<u8, MemoryError> {
|
|
||||||
self.memory.borrow().get1(address)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run one clock cycle (instruction)
|
|
||||||
pub fn step(&mut self) -> Result<(), UxnError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self, limit: u16) -> Result<(), UxnError> {
|
|
||||||
let mut executed = 0;
|
|
||||||
loop {
|
|
||||||
if executed == limit {
|
|
||||||
return Err(UxnError::ExecutionLimit(executed));
|
|
||||||
}
|
|
||||||
if self.is_halted() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
match self.lda1(self.pc) {
|
|
||||||
Err(e) => return Err(UxnError::MemoryError(e)),
|
|
||||||
Ok(0) => break,
|
|
||||||
Ok(opcode) => {
|
|
||||||
// Extract flags
|
|
||||||
let sflag: bool = opcode & 0x20 != 0;
|
|
||||||
let rflag: bool = opcode & 0x40 != 0;
|
|
||||||
let kflag: bool = opcode & 0x80 != 0;
|
|
||||||
|
|
||||||
match opcode & 0x1F {
|
|
||||||
0x00 => {
|
|
||||||
// LIT1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x01 => {
|
|
||||||
// INC1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x02 => {
|
|
||||||
// POP1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x03 => {
|
|
||||||
// NIP1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x04 => {
|
|
||||||
// SWP1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x05 => {
|
|
||||||
// ROT1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x06 => {
|
|
||||||
// DUP1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x07 => {
|
|
||||||
// OVR1
|
|
||||||
}
|
|
||||||
0x08 => {
|
|
||||||
// EQ1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x09 => {
|
|
||||||
// NEQ1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x0a => {
|
|
||||||
// GTH1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x0b => {
|
|
||||||
// LTH1
|
|
||||||
()
|
|
||||||
}
|
|
||||||
0x0c => {
|
|
||||||
// JMP1
|
|
||||||
}
|
|
||||||
0x0d => {
|
|
||||||
// JCN1
|
|
||||||
}
|
|
||||||
0x0e => {
|
|
||||||
// JSR1
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
executed += 1;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
321
src/vm.rs
Normal file
321
src/vm.rs
Normal file
|
@ -0,0 +1,321 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::*;
|
||||||
|
|
||||||
|
use crate::device::*;
|
||||||
|
use crate::memory::*;
|
||||||
|
use crate::stack::*;
|
||||||
|
|
||||||
|
pub enum UxnError {
|
||||||
|
StackError(StackError),
|
||||||
|
MemoryError(MemoryError),
|
||||||
|
ExecutionLimit(u16),
|
||||||
|
ArithmeticOverflow,
|
||||||
|
ArithmeticUnderflow,
|
||||||
|
DivisionByZero,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Uxn {
|
||||||
|
memory: Rc<RefCell<TrivialMemory>>,
|
||||||
|
// Note: Using Rc so we can start with many NullDevs and replace them
|
||||||
|
devices: [Rc<RefCell<dyn Device>>; 16],
|
||||||
|
pc: u16, // Program counter
|
||||||
|
wst: Stack, // Data stack
|
||||||
|
rst: Stack, // Return stack pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Uxn {
|
||||||
|
pub fn new() -> Uxn {
|
||||||
|
Uxn {
|
||||||
|
memory: Rc::new(RefCell::new(TrivialMemory::new())),
|
||||||
|
devices: [
|
||||||
|
Rc::new(RefCell::new(SystemDevice::new())), // #00
|
||||||
|
Rc::new(RefCell::new(NullDevice::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,
|
||||||
|
wst: Stack::new(),
|
||||||
|
rst: Stack::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_halted(&mut self) -> bool {
|
||||||
|
self.dei1(0x0f) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device(&self, port: u8) -> Rc<RefCell<dyn Device>> {
|
||||||
|
Rc::clone(&self.devices[(port & 0xF0 >> 4) as usize])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dei1(&mut self, port: u8) -> u8 {
|
||||||
|
self.device(port).borrow_mut().dei1(self, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dei2(&mut self, port: u8) -> u16 {
|
||||||
|
self.device(port).borrow_mut().dei2(self, port)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deo1(&mut self, port: u8, val: u8) {
|
||||||
|
self.device(port).borrow_mut().deo1(self, port, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deo2(&mut self, port: u8, val: u16) {
|
||||||
|
self.device(port).borrow_mut().deo2(self, port, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sta1(&mut self, address: u16, val: u8) -> Result<(), MemoryError> {
|
||||||
|
self.memory.borrow_mut().set1(address, val)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sta2(&mut self, address: u16, val: u16) -> Result<(), MemoryError> {
|
||||||
|
self.memory.borrow_mut().set2(address, val)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lda1(&mut self, address: u16) -> Result<u8, MemoryError> {
|
||||||
|
self.memory.borrow().get1(address)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run one clock cycle (instruction)
|
||||||
|
pub fn step(&mut self) -> Result<(), UxnError> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(&mut self, limit: u16) -> Result<(), UxnError> {
|
||||||
|
let mut executed = 0;
|
||||||
|
'run: loop {
|
||||||
|
if executed == limit {
|
||||||
|
return Err(UxnError::ExecutionLimit(executed));
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.is_halted() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
executed += 1;
|
||||||
|
|
||||||
|
match self.lda1(self.pc) {
|
||||||
|
Err(e) => return Err(UxnError::MemoryError(e)),
|
||||||
|
Ok(opcode) => {
|
||||||
|
// Extract flags
|
||||||
|
let sflag: bool = opcode & 0x20 != 0;
|
||||||
|
let rflag: bool = opcode & 0x40 != 0;
|
||||||
|
let kflag: bool = opcode & 0x80 != 0;
|
||||||
|
let icode: u8 = opcode & 0x1F;
|
||||||
|
|
||||||
|
let wst = || if rflag { self.wst } else { self.rst };
|
||||||
|
let mut wd = 0u8; // Accumulated pop
|
||||||
|
let mut widx = || {
|
||||||
|
assert!(wd <= wst().idx);
|
||||||
|
wst().idx - wd
|
||||||
|
}; // Current index
|
||||||
|
let rst = || if rflag { self.rst } else { self.wst };
|
||||||
|
let mut rd = 0u8; // Accumulated pop
|
||||||
|
let mut ridx = || rst().idx - rd; // Current index
|
||||||
|
|
||||||
|
match (kflag, rflag, sflag, icode) {
|
||||||
|
(0, 0, 0, 0x00) => {
|
||||||
|
// BRK
|
||||||
|
break 'run;
|
||||||
|
}
|
||||||
|
(_, _, 0, 0x00) => {
|
||||||
|
// LIT1
|
||||||
|
wst().push1(self.ld1(self.pc + 1));
|
||||||
|
self.pc += 1;
|
||||||
|
continue 'run;
|
||||||
|
}
|
||||||
|
(_, _, 1, 0x00) => {
|
||||||
|
// LIT2
|
||||||
|
wst().push2(self.ld2(self.pc + 1));
|
||||||
|
self.pc += 2;
|
||||||
|
continue 'run;
|
||||||
|
}
|
||||||
|
(_, _, 0, 0x01) => {
|
||||||
|
// INC1
|
||||||
|
let a = wst().pop1();
|
||||||
|
wst().push1(a + 1);
|
||||||
|
}
|
||||||
|
(_, _, 1, 0x01) => {
|
||||||
|
// INC1
|
||||||
|
let a = wst().pop2();
|
||||||
|
wst().push2(a + 1);
|
||||||
|
}
|
||||||
|
(_, _, 0, 0x02) => {
|
||||||
|
// POP1
|
||||||
|
wst().pop1();
|
||||||
|
}
|
||||||
|
(_, _, 1, 0x02) => {
|
||||||
|
// POP2
|
||||||
|
wst().pop2();
|
||||||
|
}
|
||||||
|
(_, _, 0, 0x03) => {
|
||||||
|
// NIP1 a b -- a
|
||||||
|
let keep = wst().pop1();
|
||||||
|
wst().pop1();
|
||||||
|
wst().push1(keep);
|
||||||
|
}
|
||||||
|
(_, _, 1, 0x03) => {
|
||||||
|
// NIP2 a b -- a
|
||||||
|
let keep = wst().pop12();
|
||||||
|
wst().pop2();
|
||||||
|
wst().push2(keep);
|
||||||
|
}
|
||||||
|
(_, _, 0, 0x04) => {
|
||||||
|
// SWP1
|
||||||
|
let a = wst().pop1();
|
||||||
|
let b = wst().pop1();
|
||||||
|
wst().push1(a);
|
||||||
|
wst().push1(b);
|
||||||
|
}
|
||||||
|
(_, _, 1, 0x04) => {
|
||||||
|
// SWP2
|
||||||
|
let a = wst().pop2();
|
||||||
|
let b = wst().pop2();
|
||||||
|
wst().push2(a);
|
||||||
|
wst().push2(b);
|
||||||
|
}
|
||||||
|
(_, _, 0, 0x05) => {
|
||||||
|
// ROT
|
||||||
|
let a = wst().pop1();
|
||||||
|
let b = wst().pop1();
|
||||||
|
let c = wst().pop1();
|
||||||
|
wst().push1(b);
|
||||||
|
wst().push1(c);
|
||||||
|
wst().push1(a);
|
||||||
|
}
|
||||||
|
(_, _, 1, 0x05) => {
|
||||||
|
// ROT
|
||||||
|
let a = wst().pop2();
|
||||||
|
let b = wst().pop2();
|
||||||
|
let c = wst().pop2();
|
||||||
|
wst().push2(b);
|
||||||
|
wst().push2(c);
|
||||||
|
wst().push2(a);
|
||||||
|
}
|
||||||
|
(_, _, _, 0x06) => {
|
||||||
|
// DUP
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x07) => {
|
||||||
|
// OVR
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x08) => {
|
||||||
|
// EQ
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x09) => {
|
||||||
|
// NEQ
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x0a) => {
|
||||||
|
// GTH
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x0b) => {
|
||||||
|
// LTH
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x0c) => {
|
||||||
|
// JMP
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x0d) => {
|
||||||
|
// JCN
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x0e) => {
|
||||||
|
// JSR
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x0f) => {
|
||||||
|
// STH
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x10) => {
|
||||||
|
// LDZ
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x11) => {
|
||||||
|
// STZ
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x12) => {
|
||||||
|
// LDR
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x13) => {
|
||||||
|
// STR
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x14) => {
|
||||||
|
// LDA
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x15) => {
|
||||||
|
// STA
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x16) => {
|
||||||
|
// DEI
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x17) => {
|
||||||
|
// DEO
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x18) => {
|
||||||
|
// ADD
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x19) => {
|
||||||
|
// SUB
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x1a) => {
|
||||||
|
// MUL
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x1b) => {
|
||||||
|
// DIV
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x1c) => {
|
||||||
|
// AND
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x1d) => {
|
||||||
|
// OR
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x1e) => {
|
||||||
|
// XOR
|
||||||
|
()
|
||||||
|
}
|
||||||
|
(_, _, _, 0x1f) => {
|
||||||
|
// SFT
|
||||||
|
()
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue