From 512826ce1d1284325ce09d61e05f95a314d02fb6 Mon Sep 17 00:00:00 2001 From: IOException Date: Sat, 12 Dec 2020 04:08:12 -0600 Subject: [PATCH] initial --- .gitignore | 1 + Cargo.lock | 28 +++++++++ Cargo.toml | 12 ++++ mandelflag.rom | 1 + src/instr.rs | 158 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 125 ++++++++++++++++++++++++++++++++++++++ src/memory.rs | 30 ++++++++++ 7 files changed, 355 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 mandelflag.rom create mode 100644 src/instr.rs create mode 100644 src/main.rs create mode 100644 src/memory.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9f84f6f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,28 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "anyhow" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0df63cb2955042487fad3aefd2c6e3ae7389ac5dc1beb28921de0b69f779d4" + +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "emu" +version = "0.1.0" +dependencies = [ + "anyhow", + "base64", + "lazy_static", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f6d4ef8 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "emu" +version = "0.1.0" +authors = ["IOException "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.34" +base64 = "0.13.0" +lazy_static = "1.4.0" diff --git a/mandelflag.rom b/mandelflag.rom new file mode 100644 index 0000000..5986ab7 --- /dev/null +++ b/mandelflag.rom @@ -0,0 +1 @@ +COA6CPAA6QDAQIJACMA4CNAAQIKACLAPCQAACRAACSAACTAAQILA6QDACDSACETACFSACGTAC/ABSQHAQQCBEXBfnQDACUBACVCADCACDBABETCAXBB/CWBACXCACDQACERACFQACGRAC/ACSQHAQQCCEXBfnQDABVVCEGVCXUUBBUUBEXUflQDAnIMABXXCEGXCXWWBBWWBCDSACETACFQACGRAC/ADSQHAQQCDCSBACTCABTTCEGTCXSSBBSSBBRXNEGRNXWWBBQWMBTTPEGTPXSSBBSSOCLL/ETLAmILAQIMAEWLBXLAynLfAEfELXLA6nLfAEWLJXLAmnLfAEfMLXLA7nLfAEWLPXLA8nLfAEfSLXLA0nLfACLAkQLfATACLCNNGEWNGXMMB6QDAETMEmIKACBA+TACBCPPMEWPMXOOBETOGnIJAC1ANSLeA6QDAQQHACIAACBAACCAAEEDAdIIBdEE/dDD/XEEBZSEAXDDBEUFAdIIBdGG/dFF/XGGBZSGAXFFBLHDCLJFCUHJACJAABCCHEGCHXJJBBHADUHGCBCCHEGCHXJJBBHAEUHFCBCCHEGCHXJJBBHAEUHGIBCCHEGCHXJJBCBJACJAABHADUHFCBBBHEGBHmQDABHADUHGIBBBHEGBHmQDABHAEUHFIBBBHEGBHC0AXnQDACJDAUJFIETJAnQDAETIAdCC/dBB/XCCBZSCAXBBBRQC/QLeACBARTACBCBAOTACBCBAiTACBCBA+TACBTAEBQYAATBEAEXBB7YAACBARTACBCBAOTACBCBAiTACBCBAkTACBCzAOCBAUTACBCBASTACBCBANTACBCBA+TACBTAEBQYAATBEAEXBB7YAACBAgTACBCBAKTACBCBAXTACBCBAdTACBCBAkTACBCBAcTACBCBAeTACBCBAWTACBCBA8TACBTACBTACBTAEBQYAATBEAEXBB7YAACBAPTACBCBAVTACBCBAKTACBCBAQTACBCBA1TACBCBAkTACBCBA+TACBQYBATBAAEXBD7YBAT8BAT9BATABATABACCAAQYDABBC8C/ABSYCAQYEBBDABHBC9C/ACSYCAQYECDDDBTACDPDCoCCCBESCM7YDACBAACyAQQYDAOCByTACCCBBBESBE7YDACBAKCCAeCDAyCEA1CFA+CGAVCKAAQYDACHAACMAAQYFALHHBOIMBKIIBFHHIOIMBLIIJPIMBCMMBESMG7YFATACHCKKBESKG7YDARYGAQYHAAAAAQYCAESBjdBBomYE/ESBddBBpmYE/ESBidBB0mYE/ESBQdBBImYE/ESBMdBB2mYE/ESBNdBBFmYE/ESBydBBSmYE/ESBAdBBHmYE/ESBqdBBjmYE/ESBxdBBVmYE/ESBwdBBLmYE/ESBfdBB2mYE/ESBOdBB7mYE/ESBcdBBrmYE/ESBSdBBUmYE/ESB9dBBDmYE/ESBHdBBEmYE/ESB1dBBimYE/ESBzdBBgmYE/ESB2dBBsmYE/ESBXdBBrmYE/ESB/dBBdmYE/ESBJdBBQmYE/ESBadBBemYE/ESB7dBBumYE/ESBDdBBOmYE/ESBGdBBtmYE/ESBgdBBfmYE/ESBCdBBqmYE/ESBpdBBGmYE/ESBZdBBpmYE/ESB3dBBOmYE/ESBUdBB6mYE/ESBKdBBPmYE/ESBbdBBtmYE/ESBLdBBKmYE/ESBEdBBZmYE/ESB6dBB1mYE/ESBkdBB0mYE/ESB8dBBwmYE/ESB4dBB4mYE/ESBIdBBimYE/ESBWdBBYmYE/ESBTdBBRmYE/ESBldBB3mYE/ESBRdBBFmYE/ESBudBBNmYE/ESBndBBWmYE/ESBedBBPmYE/ESB5dBBfmYE/ESBsdBBemYE/ESBFdBBemYE/ESBYdBBrmYE/ESBodBBQmYE/ESBVdBBfmYE/ESBhdBBNmYE/ESBtdBBzmYE/ESB0dBBTmYE/ESBrdBBWmYE/ESBvdBBzmYE/ESBPdBBqmYE/ESBBdBBsmYE/ESBmdBBHmYE/ESB+dBBhmYE/QYGAIoo6IppSIqqbIrrLIssSIttBJBBAQYDAOCBoTACCCBBBETBGmYDARYHA \ No newline at end of file diff --git a/src/instr.rs b/src/instr.rs new file mode 100644 index 0000000..e2ca7a9 --- /dev/null +++ b/src/instr.rs @@ -0,0 +1,158 @@ +#[derive(Clone, Debug)] +pub enum Instr { + Invalid, + Normal { + cond: Cond, + op: Op, + a: u8, + b: u8, + c: u8, + } +} + +#[derive(Clone, Debug)] +pub enum Cond { + Uncond, + IfTrue, + IfFalse, +} + +#[derive(Clone, Debug)] +pub enum Op { + // arithmetic and logic + Add, + AddI, + Sub, + Or, + OrI, + Xor, + XorI, + And, + AndI, + Shl, + Shr, + + // comparison + CmpRR(Cm), + CmpRI(Cm), + CmpII(Cm), + + // shift/rotate by immediate + ShlI, + ShrI, + Sar, + Rol, + + // indirect memory access + Load, + Store, + + // fixed-point multiply + Fmu, + Fms, + + // control flow + Label, + JmpUp, + JmpDn, + + // device io + IO, +} + +#[derive(Clone, Debug)] +pub enum Cm { + Tr, + Fa, + Eq, + Ne, + Sl, + Sg, + Ul, + Ug, +} + +impl Instr { + pub fn decode(instr: u32) -> Instr { + let opc = instr >> 18; + let a = ((instr >> 12) & 0b111111) as u8; + let b = ((instr >> 6) & 0b111111) as u8; + let c = (instr & 0b111111) as u8; + + if opc == 0 { return Instr::Invalid; } + let cond = match (opc - 1) / 21 { + 0 => Cond::Uncond, + 1 => Cond::IfTrue, + 2 => Cond::IfFalse, + _ => unreachable!("invalid cond: {:o}", opc), + }; + let op = match (opc - 1) % 21 { + // arithmetic and logic + 0o000 => Op::Add, + 0o001 => Op::AddI, + 0o002 => Op::Sub, + 0o004 => Op::Or, + 0o005 => Op::OrI, + 0o006 => Op::Xor, + 0o007 => Op::XorI, + 0o010 => Op::And, + 0o011 => Op::AndI, + 0o013 => Op::Shl, + 0o014 => Op::Shr, + + // comparison + 0o003 => { + let cc = match a % 0o010 { + 0 => Cm::Tr, + 1 => Cm::Fa, + 2 => Cm::Eq, + 3 => Cm::Ne, + 4 => Cm::Sl, + 5 => Cm::Sg, + 6 => Cm::Ul, + 7 => Cm::Ug, + v => unreachable!("invalid 003 cc: {:o}", a), + }; + match a / 0o010 { + 0 => Op::CmpRR(cc), + 2 => Op::CmpRI(cc), + 3 => Op::CmpII(cc), + v => unreachable!("invalid 003: {:o}", a), + } + }, + + // shift/rotate by immediate + 0o012 => match c / 0o010 { + 0 => Op::ShlI, + 1 => Op::ShrI, + 2 => Op::Sar, + 3 => Op::Rol, + v => unreachable!("invalid 012: {:o}", c), + } + + // indirect memory access + 0o015 => Op::Load, + 0o016 => Op::Store, + + // fixed-point multiply + 0o023 => match c / 0o020 { + 0 => Op::Fmu, + 1 => Op::Fms, + v => unreachable!("invalid 023: {:o}", c), + } + + // control flow + 0o017 => Op::Label, + 0o020 => Op::JmpUp, + 0o021 => Op::JmpDn, + + // device io + 0o022 => Op::IO, + + v if v >= 21 => unreachable!("invalid {:o}", v), + _ => unreachable!("shiet"), + }; + + Instr::Normal {cond, op, a, b, c} + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..68a91db --- /dev/null +++ b/src/main.rs @@ -0,0 +1,125 @@ +mod instr; +mod memory; + +use std::fs::File; +use std::io::{Read, Write}; +use std::collections::HashMap; + +use anyhow::Result; + +use crate::memory::*; +use crate::instr::*; + +lazy_static::lazy_static! { + static ref CH: (HashMap, HashMap) = { + let mut chmap = HashMap::new(); + let mut chmap2 = HashMap::new(); + let m = [ + b"0123456789ABCDEF", + b"GHIJKLMNOPQRSTUV", + b"WXYZ +-*/<=>()[]", + b"{}#$_?|^&!~,.:\n\x00", + ]; + for (nrow, row) in m.iter().enumerate() { + for (ncol, cell) in row.iter().enumerate() { + let n = nrow * 0o20 + ncol; + chmap.insert(*cell, n); + chmap2.insert(n, *cell); + } + } + (chmap, chmap2) + }; +} + +struct Clock { + +} + +#[derive(Default)] +pub struct Control { + instrs: Vec, + memory: Memory, + condition: bool, + overflow: bool, + zero: bool, + carry: bool, + sign: bool, + ip: usize, +} + +impl Control { + fn step(&mut self) { + let instr = &self.instrs[self.ip]; + self.ip += 1; + + let (cond, op, a, b, c) = match instr { + Instr::Invalid => panic!("halt"), + Instr::Normal { cond, op, a, b, c } => (cond, op, *a, *b, *c), + }; + + println!("instr: {:?}", instr); + // skip conditionals that aren't taken + match (self.condition, cond) { + (false, Cond::IfTrue) | (true, Cond::IfFalse) => return, + _ => {}, + } + + match op { + Op::AddI => { self.memory.write(a, b + c) }, + Op::Label => { /* don't do anything here */ }, + Op::CmpRR(cm) | Op::CmpRI(cm) | Op::CmpII(cm) => { + let (op1, op2) = match op { + Op::CmpRR(_) => (self.memory.read(b), self.memory.read(c)), + Op::CmpRI(_) => (self.memory.read(b), c), + Op::CmpII(_) => (b, c), + _ => unreachable!(), + }; + let result = match cm { + Cm::Tr => true, + Cm::Fa => false, + Cm::Eq => + }; + } + Op::JmpUp | Op::JmpDn => { + let lab = (a, b); + let rc = self.memory.read(c); + loop { + if let Op::JmpUp = op { self.ip -= 1; } else { self.ip += 1; } + let (cond, op, a, b, c) = match &self.instrs[self.ip] { + Instr::Invalid => panic!("halt"), + Instr::Normal { cond, op, a, b, c } => (cond, op, *a, *b, *c), + }; + if lab == (a, b) && c == rc { + println!("JUMPED TO {:?}", self.ip); + break; + } + } + }, + _ => unreachable!("unhandled {:?}", op), + }; + println!("memory: {:?}", self.memory); + println!("{:?}", std::str::from_utf8(self.memory.array.iter().map(|i| *CH.1.get(&(*i as usize)).unwrap()).collect::>().as_slice())); + } +} + +fn main() -> Result<()> { + + let mut file = File::open("mandelflag.rom")?; + let mut contents = String::new(); + file.read_to_string(&mut contents)?; + + let contents = base64::decode(&contents)?; + let instrs = contents.chunks(3) + .map(|c| ((c[0] as u32) << 16) | ((c[1] as u32) << 8) | c[2] as u32) + .map(Instr::decode) + .collect::>(); + + let mut cpu = Control::default(); + cpu.instrs = instrs; + + for _ in 0..1000 { + cpu.step(); + } + + Ok(()) +} diff --git a/src/memory.rs b/src/memory.rs new file mode 100644 index 0000000..fdf88c7 --- /dev/null +++ b/src/memory.rs @@ -0,0 +1,30 @@ +#[derive(Clone, Debug)] +pub struct Memory { + pub array: [u8; 64], +} + +impl Default for Memory { + fn default() -> Self { + Memory { + array: [0; 64], + } + } +} + +pub struct In { + addr: u8, + op1: u8, + op2: u8, +} + +impl Memory { + pub fn write(&mut self, addr: u8, val: u8) { + if addr == 0 { /* nop */ } + else { self.array[addr as usize] = val & 0b111111; } + } + + pub fn read(&mut self, addr: u8) -> u8 { + if addr == 0 { 0 } + else { self.array[addr as usize] } + } +} \ No newline at end of file