[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 stack;
|
||||
|
||||
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(())
|
||||
}
|
||||
}
|
||||
pub mod vm;
|
||||
|
|
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