commit 6ab27289a85a0e39b71f486e1feb7847f53b337c Author: shylie Date: Thu Mar 6 20:58:07 2025 -0500 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50d6980 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/.idea/ + +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..c194f8f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,400 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "customasm" +version = "0.13.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "570773ea039d836498dbadc8de4ea40609325b1bdbd159adc82aecc7f790a2dd" +dependencies = [ + "getopts", + "num-bigint", + "vergen", +] + +[[package]] +name = "footvm" +version = "0.1.0" +dependencies = [ + "byteorder", + "customasm", +] + +[[package]] +name = "getopts" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "vergen" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7141e445af09c8919f1d5f8a20dae0b20c3b57a45dee0d5823c6ed5d237f15a" +dependencies = [ + "bitflags", + "chrono", + "rustc_version", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-link" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..acd8f8c --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "footvm" +version = "0.1.0" +edition = "2021" + +[dependencies] +byteorder = "1" +customasm = "0.13.9" \ No newline at end of file diff --git a/src/foot.asm b/src/foot.asm new file mode 100644 index 0000000..f9782be --- /dev/null +++ b/src/foot.asm @@ -0,0 +1,42 @@ +#subruledef operand +{ + #{immediate: s5} => 0b000 @ immediate + r{register: u5} => 0b001 @ register + [r{register: u5}++] => 0b010 @ register + [r{register: u5}--] => 0b011 @ register + [r{register: u5}] => 0b100 @ register + [r{register: u5} + {offset: u2} => 0b1 @ offset @ register +} + +#subruledef crflags +{ + . => 0b0000 + .l => 0b0010 + .le => 0b0100 + .e => 0b0110 + .ge => 0b1000 + .g => 0b1010 + .ne => 0b1100 + .r => 0b0001 + .rl => 0b0011 + .rle => 0b0101 + .re => 0b0111 + .rge => 0b1001 + .re => 0b1011 + .rne => 0b1101 +} + +#ruledef +{ + CNST{flags: crflags} {dst: operand}, {imm: i16} => imm @ 0b0000 @ flags @ dst +} + +#bankdef mem +{ + #bits 16 + #addr 0 + #size 0x10000 + #outp 0 +} + +#bank mem diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..9dbd8ee --- /dev/null +++ b/src/main.rs @@ -0,0 +1,18 @@ +mod vm; + +use vm::{FootVM}; + +fn main() +{ + let mut vm = FootVM::new(); + vm[0] = 0xFEED; + vm[1] = 0b0000_0000_001_00000; + + let result = vm.load("CNST. r0, 0xBEEF", Some(&mut std::io::stderr())); + if result.is_ok() + { + vm.cycle(); + + println!("{}", vm.registers[0]); + } +} diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..86248d2 --- /dev/null +++ b/src/vm.rs @@ -0,0 +1,574 @@ +use std::io::{Cursor}; +use std::ops::{Index, IndexMut}; +use AddressingMode::*; +use ConditionalExecutionFlag::*; +use Instruction::*; +use byteorder::{BigEndian, ReadBytesExt}; + +const ASM_PREFIX: &str = include_str!("foot.asm"); + +#[derive(Clone, Copy)] +enum ConditionalExecutionFlag +{ + Always, + LessThan, + LessThanOrEqual, + Equal, + GreaterThanOrEqual, + GreaterThan, + NotEqual +} + +impl TryFrom for ConditionalExecutionFlag +{ + type Error = (); + + fn try_from(value: u32) -> Result + { + match value + { + 0 => Ok(Always), + 1 => Ok(LessThan), + 2 => Ok(LessThanOrEqual), + 3 => Ok(Equal), + 4 => Ok(GreaterThanOrEqual), + 5 => Ok(GreaterThan), + 6 => Ok(NotEqual), + _ => Err(()) + } + } +} + +#[derive(Clone, Copy)] +enum AddressingMode +{ + Immediate, + Direct, + Indirect(u8), + IndirectAutoIncrement, + IndirectAutoDecrement +} + +impl TryFrom for AddressingMode +{ + type Error = (); + + fn try_from(value: u32) -> Result + { + match value + { + 0 => Ok(Immediate), + 1 => Ok(Direct), + 2 => Ok(IndirectAutoIncrement), + 3 => Ok(IndirectAutoDecrement), + 4..8 => Ok(Indirect((value - 4) as u8)), + _ => Err(()) + } + } +} + +#[derive(Clone, Copy)] +struct Operand +{ + pub addressing_mode: AddressingMode, + pub value: u8 +} + +impl TryFrom for Operand +{ + type Error = (); + + fn try_from(value: u32) -> Result + { + match (value >> 5).try_into() + { + Ok(addressing_mode) => Ok( + Operand + { + addressing_mode, + value: (value as u8) & 0b00011111 + }, + ), + Err(_) => Err(()) + } + } +} + +enum Instruction +{ + CNST(ConditionalExecutionFlag, bool, Operand, u16), + CMPR(ConditionalExecutionFlag, bool, Operand, Operand), + BWNG(ConditionalExecutionFlag, bool, Operand, Operand), + ARNG(ConditionalExecutionFlag, bool, Operand, Operand), + LONG(ConditionalExecutionFlag, bool, Operand, Operand), + BWOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + BAND(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + BXOR(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + URSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + SRSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + ZLSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + CLSH(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + ADDI(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + SUBT(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + MULT(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + DIVI(ConditionalExecutionFlag, bool, Operand, Operand, Operand), + MODU(ConditionalExecutionFlag, bool, Operand, Operand, Operand) +} + +impl TryFrom for Instruction +{ + type Error = (); + + fn try_from(value: u32) -> Result + { + let ceflag: ConditionalExecutionFlag = (value >> 29).try_into()?; + let rflag: bool = (value >> 28 & 1) == 1; + let dst: Operand = (value >> 16).try_into()?; + match value >> 24 + { + 0 => Ok(CNST(ceflag, rflag, dst, (value & 0xFFFF) as u16)), + 1 => + { + let a: Operand = (value & 0xFF).try_into()?; + match (value >> 8) & 0xFF + { + 0 => Ok(CMPR(ceflag, rflag, dst, a)), + 1 => Ok(BWNG(ceflag, rflag, dst, a)), + 2 => Ok(ARNG(ceflag, rflag, dst, a)), + 3 => Ok(LONG(ceflag, rflag, dst, a)), + _ => Err(()) + } + } + 2..14 => + { + let a: Operand = (value & 0xFF).try_into()?; + let b: Operand = ((value >> 8) & 0xFF).try_into()?; + match value >> 24 + { + 2 => Ok(BWOR(ceflag, rflag, dst, b, a)), + 3 => Ok(BAND(ceflag, rflag, dst, b, a)), + 4 => Ok(BXOR(ceflag, rflag, dst, b, a)), + 5 => Ok(URSH(ceflag, rflag, dst, b, a)), + 6 => Ok(SRSH(ceflag, rflag, dst, b, a)), + 7 => Ok(ZLSH(ceflag, rflag, dst, b, a)), + 8 => Ok(CLSH(ceflag, rflag, dst, b, a)), + 9 => Ok(ADDI(ceflag, rflag, dst, b, a)), + 10 => Ok(SUBT(ceflag, rflag, dst, b, a)), + 11 => Ok(MULT(ceflag, rflag, dst, b, a)), + 12 => Ok(DIVI(ceflag, rflag, dst, b, a)), + 13 => Ok(MODU(ceflag, rflag, dst, b, a)), + _ => Err(()) + } + }, + _ => Err(()) + } + } +} + +#[derive(PartialEq, Eq)] +enum Status +{ + Less, + Equal, + Greater +} + +const MEMORY_SIZE: usize = 1 << 16; + +pub struct FootVM +{ + memory: [u16; MEMORY_SIZE], + pub registers: [u16; 32], + status: Status +} + +macro_rules! run_instruction +{ + ($self:ident, $cef:ident, $rep:ident, $name:ident, $d:ident, $a:ident) => + { + if $rep + { + while $self.should_repeat() + { + if $self.check($cef) + { + $self.$name($d, $a) + } + } + } + else if $self.check($cef) + { + $self.$name($d, $a) + } + }; + ($self:ident, $cef:ident, $rep:ident, $name:ident, $d:ident, $a:ident, $b:ident) => + { + if $rep + { + while $self.should_repeat() + { + if $self.check($cef) + { + $self.$name($d, $a, $b) + } + } + } + else if $self.check($cef) + { + $self.$name($d, $a, $b) + } + }; +} + +impl FootVM +{ + pub fn new() -> FootVM + { + FootVM + { + memory: [0; MEMORY_SIZE], + registers: [0; 32], + status: Status::Equal + } + } + + pub fn load(&mut self, asm_src: &str, write: Option<&mut dyn std::io::Write>) -> Result<(), ()> + { + let mut asm_src = asm_src.to_owned(); + asm_src.insert_str(0, ASM_PREFIX); + let asm_src = asm_src; + + let virtual_filename = "loaded"; + let mut report = customasm::diagn::Report::new(); + let mut fileserver = customasm::util::FileServerMock::new(); + fileserver.add(virtual_filename, asm_src); + + let opts = customasm::asm::AssemblyOptions::new(); + + let assembly = customasm::asm::assemble( + &mut report, + &opts, + &mut fileserver, + &[virtual_filename] + ); + + let binary = assembly.output.map(|o| o.format_binary()).ok_or_else( + || { + match write + { + Some(w) => report.print_all(w, &mut fileserver, true), + None => () + } + } + )?; + + let mut cursor = Cursor::new(binary); + let mut index = 0; + + loop + { + match cursor.read_u16::() + { + Ok(value) => + { + self[index] = value; + index += 1; + }, + Err(_) => break + } + } + + Ok(()) + } + + pub fn cycle(&mut self) + { + let first_word = self[self.program_counter()]; + let second_word = self[self.program_counter() + 1]; + match ((second_word as u32) << 16 | first_word as u32).try_into() + { + Ok(CNST(cef, rep, dst, imm)) => + run_instruction!(self, cef, rep, cnst, dst, imm), + Ok(CMPR(cef, rep, a, b)) => + run_instruction!(self, cef, rep, cmpr, a, b), + Ok(BWNG(cef, rep, dst, src)) => + run_instruction!(self, cef, rep, bwng, dst, src), + Ok(ARNG(cef, rep, dst, src)) => + run_instruction!(self, cef, rep, arng, dst, src), + Ok(LONG(cef, rep, dst, src)) => + run_instruction!(self, cef, rep, long, dst, src), + Ok(BWOR(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, bwor, dst, a, b), + Ok(BAND(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, band, dst, a, b), + Ok(BXOR(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, bxor, dst, a, b), + Ok(URSH(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, ursh, dst, a, b), + Ok(SRSH(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, srsh, dst, a, b), + Ok(ZLSH(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, zlsh, dst, a, b), + Ok(CLSH(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, clsh, dst, a, b), + Ok(ADDI(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, addi, dst, a, b), + Ok(SUBT(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, subt, dst, a, b), + Ok(MULT(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, mult, dst, a, b), + Ok(DIVI(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, divi, dst, a, b), + Ok(MODU(cef, rep, dst, b, a)) => + run_instruction!(self, cef, rep, modu, dst, a, b), + Err(_) => () + } + + *self.mut_program_counter() += 2; + } + + fn cnst(&mut self, dst: Operand, imm: u16) + { + self.store_operand(dst, imm) + } + + fn cmpr(&mut self, a: Operand, b: Operand) + { + let a = self.load_operand(a) as i16; + let b = self.load_operand(b) as i16; + + self.status = match a - b + { + ..0 => Status::Less, + 0 => Status::Equal, + 1.. => Status::Greater + } + } + + fn bwng(&mut self, dst: Operand, src: Operand) + { + let value = self.load_operand(src); + self.store_operand(dst, !value) + } + + fn arng(&mut self, dst: Operand, src: Operand) + { + let a = self.load_operand(src); + self.store_operand(dst, (-(a as i16)) as u16); + } + + fn long(&mut self, dst: Operand, src: Operand) + { + let a = self.load_operand(src); + match a + { + 0 => self.store_operand(dst, 1), + _ => self.store_operand(dst, 0) + } + } + + fn bwor(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, a | b); + } + + fn band(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, a & b); + } + + fn bxor(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, a ^ b); + } + + fn ursh(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, a >> b); + } + + fn srsh(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, ((a as i16) >> (b as i16)) as u16); + } + + fn zlsh(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, a << b); + } + + fn clsh(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, a.rotate_left(b as u32)); + } + + fn addi(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, ((a as i16) + (b as i16)) as u16); + } + + fn subt(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, ((a as i16) - (b as i16)) as u16); + } + + fn mult(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, ((a as i16) * (b as i16)) as u16); + } + + fn divi(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, ((a as i16) / (b as i16)) as u16); + } + + fn modu(&mut self, dst: Operand, src_a: Operand, src_b: Operand) + { + let a = self.load_operand(src_a); + let b = self.load_operand(src_b); + self.store_operand(dst, ((a as i16) % (b as i16)) as u16); + } + + fn store_operand(&mut self, dst: Operand, value: u16) + { + match dst.addressing_mode + { + Immediate => (), + Direct => self.registers[dst.value as usize] = value, + Indirect(offset) => + { + let ptr = self.registers[dst.value as usize] + offset as u16; + self[ptr] = value + } + IndirectAutoIncrement => + { + let ptr = self.registers[dst.value as usize]; + self[ptr] = value; + self.registers[dst.value as usize] += 1; + }, + IndirectAutoDecrement => + { + let ptr = self.registers[dst.value as usize]; + self[ptr] = value; + self.registers[dst.value as usize] -= 1; + } + } + } + + fn load_operand(&mut self, src: Operand) -> u16 + { + match src.addressing_mode + { + Immediate => src.value as u16, + Direct => self.registers[src.value as usize], + Indirect(offset) => self[self.registers[src.value as usize] + offset as u16], + IndirectAutoIncrement => + { + let ptr = self.registers[src.value as usize]; + let retval = self[ptr]; + self.registers[src.value as usize] += 1; + retval + }, + IndirectAutoDecrement => + { + let ptr = self.registers[src.value as usize]; + let retval = self[ptr]; + self.registers[src.value as usize] -= 1; + retval + } + } + } + + fn program_counter(&self) -> u16 + { + self.registers[31] + } + + fn mut_program_counter(&mut self) -> &mut u16 + { + &mut self.registers[31] + } + + fn stack_pointer(&self) -> u16 + { + self.registers[30] + } + + fn loop_count(&self) -> u16 + { + self.registers[29] + } + + fn mut_loop_count(&mut self) -> &mut u16 + { + &mut self.registers[29] + } + + fn should_repeat(&mut self) -> bool + { + if self.loop_count() > 0 + { + *self.mut_loop_count() -= 1; + + true + } + else + { + false + } + } + + fn check(&self, cef: ConditionalExecutionFlag) -> bool + { + match cef + { + Always => true, + LessThan => self.status == Status::Less, + LessThanOrEqual => self.status == Status::Less || self.status == Status::Equal, + Equal => self.status == Status::Equal, + GreaterThanOrEqual => self.status == Status::Greater || self.status == Status::Equal, + GreaterThan => self.status == Status::Greater, + NotEqual => self.status != Status::Equal + } + } +} + +impl Index for FootVM +{ + type Output = u16; + + fn index(&self, index: u16) -> &Self::Output + { + &self.memory[index as usize] + } +} + +impl IndexMut for FootVM +{ + fn index_mut(&mut self, index: u16) -> &mut Self::Output + { + &mut self.memory[index as usize] + } +} \ No newline at end of file