initial
This commit is contained in:
commit
512826ce1d
7 changed files with 355 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/target
|
28
Cargo.lock
generated
Normal file
28
Cargo.lock
generated
Normal file
|
@ -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"
|
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[package]
|
||||||
|
name = "emu"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["IOException <ioexceptionosu@gmail.com>"]
|
||||||
|
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"
|
1
mandelflag.rom
Normal file
1
mandelflag.rom
Normal file
|
@ -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
|
158
src/instr.rs
Normal file
158
src/instr.rs
Normal file
|
@ -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}
|
||||||
|
}
|
||||||
|
}
|
125
src/main.rs
Normal file
125
src/main.rs
Normal file
|
@ -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<u8, usize>, HashMap<usize, u8>) = {
|
||||||
|
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<Instr>,
|
||||||
|
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::<Vec<_>>().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::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut cpu = Control::default();
|
||||||
|
cpu.instrs = instrs;
|
||||||
|
|
||||||
|
for _ in 0..1000 {
|
||||||
|
cpu.step();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
30
src/memory.rs
Normal file
30
src/memory.rs
Normal file
|
@ -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] }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue