From 5fa72b2112b23b41ccc92f195ad3cfee8fa1dd34 Mon Sep 17 00:00:00 2001 From: Reid 'arrdem' McKenzie Date: Fri, 23 Dec 2022 20:41:44 -0700 Subject: [PATCH] Almost out of power on the plane --- Cargo.lock | 118 ++++++++ Cargo.toml | 2 + src/asm.rs | 4 + src/isa.rs | 61 ++++ src/lib.rs | 1 + src/memory.rs | 2 +- src/stack.rs | 2 +- src/stack/arrs.rs | 3 +- src/vm.rs | 167 ++++++----- tests/vm_tests.rs | 715 ++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1004 insertions(+), 71 deletions(-) create mode 100644 src/asm.rs create mode 100644 src/isa.rs create mode 100644 tests/vm_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 2ad856f..f6ccdb6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,124 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "nofmt" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06910a54adb901a01dfd9c475e7959c41acd55a078101ec70fb180f01b7435f" + +[[package]] +name = "num_enum" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "once_cell" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" + +[[package]] +name = "proc-macro-crate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +dependencies = [ + "once_cell", + "thiserror", + "toml", +] + +[[package]] +name = "proc-macro2" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.151" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fed41fc1a24994d044e6db6935e69511a1153b52c15eb42493b26fa87feba0" + +[[package]] +name = "syn" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "toml" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1333c76748e868a4d9d1017b5ab53171dfd095f70c712fdb4653a406547f598f" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-ident" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" + [[package]] name = "uxn" version = "0.1.0" +dependencies = [ + "nofmt", + "num_enum", +] diff --git a/Cargo.toml b/Cargo.toml index 7a67870..a70c3f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +nofmt = "1.0" +num_enum = "0.5.7" diff --git a/src/asm.rs b/src/asm.rs new file mode 100644 index 0000000..ce7e2c5 --- /dev/null +++ b/src/asm.rs @@ -0,0 +1,4 @@ +/** + * A woefully incomplete implementation of the TAL assembler. + * Mostly intended to make writing test cases easy. + */ diff --git a/src/isa.rs b/src/isa.rs new file mode 100644 index 0000000..e4d737a --- /dev/null +++ b/src/isa.rs @@ -0,0 +1,61 @@ +// icodes +pub struct Icode {} + +impl Icode { + // Official names + pub const BRK: u8 = 0x00u8; + pub const LIT: u8 = 0x1 << 7; // AKA keep Yes really. Per spec; + pub const INC: u8 = 0x01; + pub const POP: u8 = 0x02; + pub const NIP: u8 = 0x03; + pub const SWP: u8 = 0x04; + pub const ROT: u8 = 0x05; + pub const DUP: u8 = 0x06; + pub const OVR: u8 = 0x07; + pub const EQL: u8 = 0x08; + pub const NEQ: u8 = 0x09; + pub const GTH: u8 = 0x0a; + pub const LTH: u8 = 0x0b; + pub const JMP: u8 = 0x0c; + pub const JCN: u8 = 0x0d; + pub const JSR: u8 = 0x0e; + pub const STH: u8 = 0x0f; + pub const LDZ: u8 = 0x10; + pub const STZ: u8 = 0x11; + pub const LDR: u8 = 0x12; + pub const STR: u8 = 0x13; + pub const LDA: u8 = 0x14; + pub const STA: u8 = 0x15; + pub const DEI: u8 = 0x16; + pub const DEO: u8 = 0x17; + pub const ADD: u8 = 0x18; + pub const SUB: u8 = 0x19; + pub const MUL: u8 = 0x1a; + pub const DIV: u8 = 0x1b; + pub const AND: u8 = 0x1c; + pub const ORA: u8 = 0x1d; + pub const EOR: u8 = 0x1e; + pub const SFT: u8 = 0x1f; + + // Opcode flags + pub const SHORT: u8 = 0x1 << 5; + pub const RETURN: u8 = 0x1 << 6; + pub const KEEP: u8 = 0x1 << 7; + + // Hacking around the weird official names for some things + pub const XOR: u8 = Icode::ORA; + pub const EQ: u8 = Icode::EQL; + pub const NE: u8 = Icode::NEQ; + pub const GT: u8 = Icode::GTH; + pub const LT: u8 = Icode::LTH; + pub const OR: u8 = Icode::ORA; + pub const NOP: u8 = Icode::POP | Icode::KEEP; + + pub const fn parse(opcode: u8) -> (u8, u8, u8, u8) { + let kflag: u8 = (opcode >> 7) & 0x1; + let rflag: u8 = (opcode >> 6) & 0x1; + let sflag: u8 = (opcode >> 5) & 0x1; + let icode: u8 = opcode & 0x1F; + (kflag, rflag, sflag, icode) + } +} diff --git a/src/lib.rs b/src/lib.rs index 0e68c53..a926730 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod device; +pub mod isa; pub mod memory; pub mod stack; pub mod vm; diff --git a/src/memory.rs b/src/memory.rs index bb47e8d..c904237 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -1,4 +1,4 @@ -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum MemoryError { AddressOverflow, AddressUnderflow, diff --git a/src/stack.rs b/src/stack.rs index c4ad363..e982d5f 100644 --- a/src/stack.rs +++ b/src/stack.rs @@ -5,7 +5,7 @@ pub mod wal; use std; use std::result::Result; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum StackError { StackOverflow, StackUnderflow, diff --git a/src/stack/arrs.rs b/src/stack/arrs.rs index 1c0dfcc..3b53ca0 100644 --- a/src/stack/arrs.rs +++ b/src/stack/arrs.rs @@ -1,4 +1,4 @@ -use super::{Stack, StackError}; +use crate::stack::{Stack, StackError}; #[derive(Debug)] pub struct ArrayStack { @@ -69,7 +69,6 @@ impl Stack for ArrayStack { } else { let high = self.buff[_idx as usize] as u16; let low = self.buff[(_idx + 1) as usize] as u16; - println!("_idx {}; high {:#02x}; low {:#02x};", _idx, high, low); Ok((high << 8) + low) } } diff --git a/src/vm.rs b/src/vm.rs index 7171d71..716191e 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -5,11 +5,13 @@ use std::*; 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), @@ -70,6 +72,16 @@ impl 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 is_halted(&mut self) -> bool { self.dei1(0x0f) != 0 } @@ -95,12 +107,12 @@ impl Uxn { } pub fn sta1(&mut self, address: u16, val: u8) -> Result<(), MemoryError> { - self.memory.borrow_mut().set1(address, val)?; + self.memory.clone().borrow_mut().set1(address, val)?; Ok(()) } pub fn sta2(&mut self, address: u16, val: u16) -> Result<(), MemoryError> { - self.memory.borrow_mut().set2(address, val)?; + self.memory.clone().borrow_mut().set2(address, val)?; Ok(()) } @@ -115,16 +127,23 @@ impl Uxn { // Run one clock cycle (instruction) pub fn step(&mut self) -> Result<(), UxnError> { match self.lda1(self.pc) { - Err(e) => return Err(UxnError::MemoryError(e)), - Ok(opcode) => { + Err(e) => Err(UxnError::MemoryError(e)), + Ok(icode) => { // The value of PC is defined to be the value of the NEXT pc ala Mips self.pc += 1; + // Short circuit for fast NOP + if icode == Icode::NOP { + return Ok(()); + } + // Extract flags - let sflag: u8 = opcode & 0x20 >> 5; - let rflag: u8 = opcode & 0x40 >> 6; - let kflag: u8 = opcode & 0x80 >> 7; - let icode: u8 = opcode & 0x1F; + let (kflag, rflag, sflag, icode) = Icode::parse(icode); + + println!( + "pc #{:04X}: #{:02x} ( {:05b} s: {:1x} r: {:1x} k: {:1x} )", + self.pc, icode, icode, sflag, rflag, kflag + ); // Swizzle the stacks as needed let [wst, rst]: [Rc>; 2] = { @@ -181,52 +200,52 @@ impl Uxn { }; match (kflag, rflag, sflag, icode) { - (0, 0, 0, 0x00) => { - // BRK + (0, 0, 0, Icode::BRK) => { + // BRK -- return Err(UxnError::Break); } - (_, _, _, 0x00) => { - // LIT + (1, _, _, Icode::BRK) => { + // BRKk aka LIT -- a push(wst.clone(), load(self.pc)?)?; self.pc += if sflag == 1 { 2 } else { 1 }; } - (_, _, _, 0x01) => { - // INC + (_, _, _, Icode::INC) => { + // INC a -- a push(wst.clone(), pop(wst.clone())? + 1)?; } - (_, _, _, 0x02) => { - // POP + (_, _, _, Icode::POP) => { + // POP a -- pop(wst)?; } - (_, _, _, 0x03) => { - // NIP a b -- a + (_, _, _, Icode::NIP) => { + // NIP a b -- b let keep = pop(wst.clone())?; pop(wst.clone())?; push(wst.clone(), keep)?; } - (_, _, _, 0x04) => { - // SWP - let a = pop(wst.clone())?; + (_, _, _, 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)?; } - (_, _, _, 0x05) => { - // ROT - let a = pop(wst.clone())?; - let b = pop(wst.clone())?; + (_, _, _, 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)?; } - (_, _, _, 0x06) => { - // DUP + (_, _, _, Icode::DUP) => { + // DUP a -- a a let a = pop(wst.clone())?; push(wst.clone(), a)?; push(wst.clone(), a)?; } - (_, _, _, 0x07) => { + (_, _, _, Icode::OVR) => { // OVR a b -- a b a let b = pop(wst.clone())?; let a = pop(wst.clone())?; @@ -234,36 +253,41 @@ impl Uxn { push(wst.clone(), b)?; push(wst.clone(), a)?; } - (_, _, _, 0x08) => { - // EQU + (_, _, _, 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 })?; } - (_, _, _, 0x09) => { - // NEQ + (_, _, _, 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 })?; } - (_, _, _, 0x0a) => { - // GTH a b + (_, _, _, 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 })?; } - (_, _, _, 0x0b) => { - // LTH a b + (_, _, _, 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 })?; } - (_, _, _, 0x0c) => { - // JMP + (_, _, 0, Icode::JMP) => { + // JMP1 a -- let delta = wst.borrow_mut().pop1()? as i8; self.pc = self.pc.wrapping_add(delta as u16); } - (_, _, 0, 0x0d) => { + (_, _, 1, Icode::JMP) => { + // JMP2 a -- + let delta = wst.borrow_mut().pop2()?; + self.pc = delta; + } + (_, _, 0, Icode::JCN) => { // JCN1 cnd8 addr8 (relative) let delta = wst.borrow_mut().pop1()? as i8; let cnd = wst.borrow_mut().pop1()?; @@ -271,7 +295,7 @@ impl Uxn { self.pc = self.pc.wrapping_add(delta as u16); } } - (_, _, 2, 0x0d) => { + (_, _, 1, Icode::JCN) => { // JCN2 cnd8 addr16 (absolute) let addr = wst.borrow_mut().pop2()?; let cnd = wst.borrow_mut().pop1()?; @@ -279,119 +303,127 @@ impl Uxn { self.pc = addr; } } - (_, _, 0, 0x0e) => { + (_, _, 0, Icode::JSR) => { // JSR1 addr8 (relative) rst.borrow_mut().push2(self.pc)?; let delta = wst.borrow_mut().pop1()? as i8; self.pc = self.pc.wrapping_add(delta as u16); } - (_, _, 1, 0x0e) => { + (_, _, 1, Icode::JSR) => { // JSR2 addr16 (absolute) rst.borrow_mut().push2(self.pc)?; self.pc = wst.borrow_mut().pop2()?; } - (_, _, _, 0x0f) => { + (_, _, _, Icode::STH) => { // STH a push(rst, pop(wst)?)?; } - (_, _, _, 0x10) => { - // LDZ a -- b8 - push(wst.clone(), load(wst.borrow_mut().pop1()? as u16)?)?; + (_, _, _, Icode::LDZ) => { + // LDZ a8 -- b + let addr = wst.borrow_mut().pop1()? as u16; + push(wst.clone(), load(addr)?)?; } - (_, _, _, 0x11) => { + (_, _, _, Icode::STZ) => { // STZ val addr8 -- let addr = wst.borrow_mut().pop1()? as u16; store(addr, pop(wst.clone())?)?; } - (_, _, _, 0x12) => { + (_, _, _, Icode::LDR) => { // LDR addr8 -- a8 let delta = wst.borrow_mut().pop1()? as i8; - push(wst.clone(), load(self.pc.wrapping_add(delta as u16))?)?; + let addr = self.pc.wrapping_add(delta as u16); + push(wst, load(addr)?)?; } - (_, _, _, 0x13) => { - // STR val8 addr8 + (_, _, _, Icode::STR) => { + // STR val addr8 -- let delta = wst.borrow_mut().pop1()?; - store(self.pc.wrapping_add(delta as u16), pop(wst.clone())?)?; + let addr = self.pc.wrapping_add(delta as u16); + println!( + "STR] Got delta {}, effective store address #{:04x}", + delta, addr + ); + + store(addr, pop(wst)?)?; } - (_, _, _, 0x14) => { + (_, _, _, Icode::LDA) => { // LDA a16 let addr = wst.borrow_mut().pop2()?; push(wst.clone(), load(addr)?)?; } - (_, _, _, 0x15) => { + (_, _, _, Icode::STA) => { // STA val a16 -- let addr = wst.borrow_mut().pop2()?; store(addr, pop(wst.clone())?)?; } - (_, _, 0, 0x16) => { + (_, _, 0, Icode::DEI) => { // DEI port8 -- a8 let mut wst = wst.borrow_mut(); let port = wst.pop1()?; wst.push1(self.dei1(port))?; } - (_, _, 1, 0x16) => { + (_, _, 1, Icode::DEI) => { // DEI2 port8 -- a16 let mut wst = wst.borrow_mut(); let port = wst.pop1()?; wst.push2(self.dei2(port))?; } - (_, _, 0, 0x17) => { + (_, _, 0, Icode::DEO) => { // DEO1 a8 port8 -- let mut wst = wst.borrow_mut(); let port = wst.pop1()?; let val = wst.pop1()?; self.deo1(port, val); } - (_, _, 1, 0x17) => { + (_, _, 1, Icode::DEO) => { // DEO2 a16 port8 -- let mut wst = wst.borrow_mut(); let port = wst.pop1()?; let val = wst.pop2()?; self.deo2(port, val); } - (_, _, _, 0x18) => { + (_, _, _, Icode::ADD) => { // ADD a b -- c let b = pop(wst.clone())?; let a = pop(wst.clone())?; push(wst.clone(), a + b)?; } - (_, _, _, 0x19) => { + (_, _, _, Icode::SUB) => { // SUB a b -- c let b = pop(wst.clone())?; let a = pop(wst.clone())?; push(wst.clone(), a - b)?; } - (_, _, _, 0x1a) => { + (_, _, _, Icode::MUL) => { // MUL a b -- c let b = pop(wst.clone())?; let a = pop(wst.clone())?; push(wst.clone(), a * b)?; } - (_, _, _, 0x1b) => { + (_, _, _, Icode::DIV) => { // DIV a b -- c let b = pop(wst.clone())?; let a = pop(wst.clone())?; push(wst.clone(), a / b)?; } - (_, _, _, 0x1c) => { + (_, _, _, Icode::AND) => { // AND a b -- c let b = pop(wst.clone())?; let a = pop(wst.clone())?; push(wst.clone(), a & b)?; } - (_, _, _, 0x1d) => { + (_, _, _, Icode::ORA) => { // OR a b -- c let b = pop(wst.clone())?; let a = pop(wst.clone())?; push(wst.clone(), a | b)?; } - (_, _, _, 0x1e) => { + (_, _, _, Icode::EOR) => { // XOR a8 b8 -- c8 let b = pop(wst.clone())?; let a = pop(wst.clone())?; push(wst.clone(), a ^ b)?; } - (_, _, _, 0x1f) => { + (_, _, _, Icode::SFT) => { // SFT a shift8 -- b let shift = wst.borrow_mut().pop1()?; let [left, right] = [shift >> 4, shift & 0xF]; @@ -400,9 +432,10 @@ impl Uxn { } _ => unreachable!(), } + + Ok(()) } } - Ok(()) } pub fn run(&mut self, limit: u16) -> Result<(), UxnError> { diff --git a/tests/vm_tests.rs b/tests/vm_tests.rs new file mode 100644 index 0000000..251ec99 --- /dev/null +++ b/tests/vm_tests.rs @@ -0,0 +1,715 @@ +use uxn::isa::Icode; +use uxn::vm::*; + +#[test] +fn test_brk() { + let mut vm = Uxn::of1(&[Icode::BRK]); + assert_eq!(vm.step(), Err(UxnError::Break)); +} + +#[test] +fn test_lit() { + let mut vm = Uxn::of1(&[Icode::LIT, 0x01, Icode::BRK]); + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x01)); +} + +#[test] +fn test_lit2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0xFF, 0xAA, + Icode::BRK, // BRK + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xFFAA)); +} + +#[test] +fn test_litr() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::RETURN, 0xAA, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.rst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_inc() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0xFF, 0xAA, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xFFAA)); +} + +#[test] +fn test_pop1() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xFF, + Icode::LIT, 0xAA, + Icode::POP, + Icode::LIT, 0xBB, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xFFBB)); +} + +#[test] +fn test_pop2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xFF, + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::POP | Icode::SHORT, + Icode::LIT, 0xCC, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xFFCC)); +} + +#[test] +fn test_popk() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xFF, + Icode::POP | Icode::KEEP, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xFF)); +} + +#[test] +fn test_nip() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::NIP, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xBB)); +} + +#[test] +fn test_nip2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0xAA, 0x00, + Icode::LIT | Icode::SHORT, 0xBB, 0x00, + Icode::NIP | Icode::SHORT, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xBB00)); +} + +#[test] +fn test_nipk() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::NIP | Icode::KEEP, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().get1(1), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(2), Ok(0xBB)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xBB)); +} + +#[test] +fn test_swp() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::SWP, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(1), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_swp2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0xAA, 0x00, + Icode::LIT | Icode::SHORT, 0xBB, 0x00, + Icode::SWP | Icode::SHORT, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get2(0), Ok(0xBB00)); + assert_eq!(vm.wst.borrow().get2(2), Ok(0xAA00)); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xAA00)); +} + +#[test] +fn test_swpk() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::SWP | Icode::KEEP, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().get1(1), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(2), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(3), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_rot() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::LIT, 0xCC, + Icode::ROT, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(1), Ok(0xCC)); + assert_eq!(vm.wst.borrow().get1(2), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_rot_cycle() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::LIT, 0xCC, + Icode::ROT, + Icode::ROT, + Icode::ROT, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().get1(1), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(2), Ok(0xCC)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xCC)); +} + +#[test] +fn test_dup() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::DUP, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().get1(1), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(2), Ok(0xBB)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xBB)); +} + +#[test] +fn test_ovr() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::OVR, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().get1(1), Ok(0xBB)); + assert_eq!(vm.wst.borrow().get1(2), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_eql_neg() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::EQL, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x00)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x00)); +} + +#[test] +fn test_eql_pos() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xAA, + Icode::EQL, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x01)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x01)); +} + +#[test] +fn test_neq_neg() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xAA, + Icode::NEQ, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x00)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x00)); +} + +#[test] +fn test_neq_pos() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0xBB, + Icode::NEQ, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x01)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x01)); +} + +#[test] +fn test_gth_neg() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x00, + Icode::LIT, 0x01, + Icode::GTH, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x00)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x00)); +} + +#[test] +fn test_gth_pos() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x01, + Icode::LIT, 0x00, + Icode::GTH, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x01)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x01)); +} + +#[test] +fn test_lth_neg() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x01, + Icode::LIT, 0x00, + Icode::LTH, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x00)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x00)); +} + +#[test] +fn test_lth_pos() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x00, + Icode::LIT, 0x01, + Icode::LTH, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x01)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x01)); +} + +#[test] +fn test_jmprp() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xAA, + Icode::LIT, 0x03, + Icode::JMP, + Icode::POP, + Icode::LIT, 0xFF, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_jmpa() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0x01, 0x10, Icode::JMP | Icode::SHORT, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + Icode::LIT, 0xAA, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_jmprn() { + // Negative relative jup + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0x01, 0x10, Icode::JMP | Icode::SHORT, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, + // Negative relative jump target + Icode::LIT, 0xAA, Icode::BRK, 0x00, + + // Absolute jump target + Icode::LIT, -7i8 as u8, Icode::JMP, + + // Wrong arm + Icode::LIT, 0xFF, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_jmpc_true() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x01, + Icode::LIT, 0x03, + Icode::JCN, + // Fall through + Icode::LIT, 0xFF, Icode::BRK, + + // Target + Icode::LIT, 0xAA, Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); +} + +#[test] +fn test_jmpc_false() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x00, + Icode::LIT, 0x03, + Icode::JCN, + // Fall through + Icode::LIT, 0xFF, Icode::BRK, + + // Target + Icode::LIT, 0xAA, Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xFF)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xFF)); +} + +#[test] +fn test_jsr() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x05, Icode::JSR, 0x00, + // Fall through + 0x00, 0x00, 0x00, 0x00, + // Target + Icode::LIT, 0xAA, Icode::BRK, 0x00, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); + + assert_eq!(vm.rst.borrow().get2(0), Ok(0x0103)); + assert_eq!(vm.rst.borrow().peek2(), Ok(0x0103)); +} + +#[test] +fn test_jsra() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0x01, 0x08, Icode::JSR | Icode::SHORT, + // Fall through + 0x00, 0x00, 0x00, 0x00, + // Target + Icode::LIT, 0xAA, Icode::BRK, 0x00, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0xAA)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xAA)); + + assert_eq!(vm.rst.borrow().get2(0), Ok(0x0104)); + assert_eq!(vm.rst.borrow().peek2(), Ok(0x0104)); +} + +#[test] +fn test_sth() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0x01, 0x08, Icode::STH, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().get1(0), Ok(0x01)); + assert_eq!(vm.wst.borrow().peek1(), Ok(0x01)); + + assert_eq!(vm.rst.borrow().get1(0), Ok(0x08)); + assert_eq!(vm.rst.borrow().peek1(), Ok(0x08)); +} + +#[test] +fn test_sth2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0x01, 0x08, Icode::STH | Icode::SHORT, + Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.rst.borrow().get2(0), Ok(0x0108)); + assert_eq!(vm.rst.borrow().peek2(), Ok(0x0108)); +} + +#[test] +fn test_ldz() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x0b, Icode::LDZ, Icode::BRK, + ]); + vm.sta1(0x000b, 0xFF).unwrap(); + } + // Make sure the write took + assert_eq!(vm.lda1(0x00b).unwrap(), 0xFF); + // Run the example + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 1); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xFF)); +} + +#[test] +fn test_ldz2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x0b, Icode::LDZ | Icode::SHORT, Icode::BRK, + ]); + vm.sta2(0x000b, 0xFFEE).unwrap(); + } + // Make sure the write took + assert_eq!(vm.lda1(0x00b).unwrap(), 0xFF); + // Run the example + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 2); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xFFEE)); +} + +#[test] +fn test_stz() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xFF, Icode::LIT, 0x0b, + Icode::STZ, Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 0); + assert_eq!(vm.lda1(0x000b), Ok(0xFF)); +} + +#[test] +fn test_stz2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xFF, Icode::LIT, 0x0b, + Icode::STZ, Icode::BRK, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 0); + assert_eq!(vm.lda1(0x000b), Ok(0xFF)); +} + +#[test] +fn test_ldr() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x01, Icode::LDR, Icode::BRK, + 0xFF, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 1); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xFF)); +} + +#[test] +fn test_ldr2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0x01, Icode::LDR | Icode::SHORT, Icode::BRK, + 0xFF, 0xEE + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 2); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xFFEE)); +} + +#[test] +fn test_str() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xEE, Icode::LIT, 0x03, + Icode::STR, Icode::BRK, 0x00, 0x00, + 0xFF, // Will be overwritten + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 0); + assert_eq!(vm.lda1(0x0108), Ok(0xEE)); +} + +#[test] +fn test_str2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0xEE, 0xDD, Icode::LIT, + 0x02, Icode::STR | Icode::SHORT, Icode::BRK, 0x00, + 0xFF, 0xFF // Will be overwritten + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 0); + assert_eq!(vm.lda2(0x0108), Ok(0xEEDD)); +} + +#[test] +fn test_lda() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0x01, 0x08, Icode::LDA, + Icode::BRK, 0x00, 0x00, 0x00, + 0xEE, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 1); + assert_eq!(vm.wst.borrow().peek1(), Ok(0xEE)); +} + +#[test] +fn test_lda2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0x01, 0x08, Icode::LDA | Icode::SHORT, + Icode::BRK, 0x00, 0x00, 0x00, + 0xEE, 0xDD, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 2); + assert_eq!(vm.wst.borrow().peek2(), Ok(0xEEDD)); +} + +#[test] +fn test_sta() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT, 0xFF, Icode::NOP, Icode::NOP, + Icode::LIT | Icode::SHORT, 0x01, 0x0c, Icode::NOP, + Icode::STA, Icode::BRK, 0x00, 0x00, + 0x00, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 0); + assert_eq!(vm.lda1(0x010c), Ok(0xFF)); +} + +#[test] +fn test_sta2() { + nofmt::pls! { + let mut vm = Uxn::of1(&[ + Icode::LIT | Icode::SHORT, 0xFF, 0xEE, Icode::NOP, + Icode::LIT | Icode::SHORT, 0x01, 0x0c, Icode::NOP, + Icode::STA | Icode::SHORT, Icode::BRK, Icode::NOP, + 0x00, 0x00, + ]); + } + assert_eq!(vm.run(64), Err(UxnError::Break)); + assert_eq!(vm.wst.borrow().idx(), 0); + assert_eq!(vm.lda2(0x010c), Ok(0xFFEE)); +}