xenua
1 year ago
commit
c7a118cee8
14 changed files with 1480 additions and 0 deletions
@ -0,0 +1,297 @@ |
|||||||
|
# This file is automatically @generated by Cargo. |
||||||
|
# It is not intended for manual editing. |
||||||
|
version = 3 |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "aho-corasick" |
||||||
|
version = "1.1.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" |
||||||
|
dependencies = [ |
||||||
|
"memchr", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "alea" |
||||||
|
version = "0.1.0" |
||||||
|
dependencies = [ |
||||||
|
"alea-core", |
||||||
|
"bytes", |
||||||
|
"tracing", |
||||||
|
"tracing-subscriber", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "alea-core" |
||||||
|
version = "0.1.0" |
||||||
|
dependencies = [ |
||||||
|
"bitflags", |
||||||
|
"bytes", |
||||||
|
"tracing", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "bitflags" |
||||||
|
version = "2.4.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "bytes" |
||||||
|
version = "1.5.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "cfg-if" |
||||||
|
version = "1.0.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "lazy_static" |
||||||
|
version = "1.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "log" |
||||||
|
version = "0.4.20" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "matchers" |
||||||
|
version = "0.1.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" |
||||||
|
dependencies = [ |
||||||
|
"regex-automata 0.1.10", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "memchr" |
||||||
|
version = "2.6.4" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "nu-ansi-term" |
||||||
|
version = "0.46.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" |
||||||
|
dependencies = [ |
||||||
|
"overload", |
||||||
|
"winapi", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "once_cell" |
||||||
|
version = "1.18.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "overload" |
||||||
|
version = "0.1.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "pin-project-lite" |
||||||
|
version = "0.2.13" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "proc-macro2" |
||||||
|
version = "1.0.69" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" |
||||||
|
dependencies = [ |
||||||
|
"unicode-ident", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "quote" |
||||||
|
version = "1.0.33" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "regex" |
||||||
|
version = "1.10.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" |
||||||
|
dependencies = [ |
||||||
|
"aho-corasick", |
||||||
|
"memchr", |
||||||
|
"regex-automata 0.4.3", |
||||||
|
"regex-syntax 0.8.2", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "regex-automata" |
||||||
|
version = "0.1.10" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" |
||||||
|
dependencies = [ |
||||||
|
"regex-syntax 0.6.29", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "regex-automata" |
||||||
|
version = "0.4.3" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" |
||||||
|
dependencies = [ |
||||||
|
"aho-corasick", |
||||||
|
"memchr", |
||||||
|
"regex-syntax 0.8.2", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "regex-syntax" |
||||||
|
version = "0.6.29" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "regex-syntax" |
||||||
|
version = "0.8.2" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "sharded-slab" |
||||||
|
version = "0.1.7" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" |
||||||
|
dependencies = [ |
||||||
|
"lazy_static", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "smallvec" |
||||||
|
version = "1.11.1" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "syn" |
||||||
|
version = "2.0.38" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"unicode-ident", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "thread_local" |
||||||
|
version = "1.1.7" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" |
||||||
|
dependencies = [ |
||||||
|
"cfg-if", |
||||||
|
"once_cell", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "tracing" |
||||||
|
version = "0.1.40" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" |
||||||
|
dependencies = [ |
||||||
|
"pin-project-lite", |
||||||
|
"tracing-attributes", |
||||||
|
"tracing-core", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "tracing-attributes" |
||||||
|
version = "0.1.27" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" |
||||||
|
dependencies = [ |
||||||
|
"proc-macro2", |
||||||
|
"quote", |
||||||
|
"syn", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "tracing-core" |
||||||
|
version = "0.1.32" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" |
||||||
|
dependencies = [ |
||||||
|
"once_cell", |
||||||
|
"valuable", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "tracing-log" |
||||||
|
version = "0.1.4" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" |
||||||
|
dependencies = [ |
||||||
|
"log", |
||||||
|
"once_cell", |
||||||
|
"tracing-core", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "tracing-subscriber" |
||||||
|
version = "0.3.17" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" |
||||||
|
dependencies = [ |
||||||
|
"matchers", |
||||||
|
"nu-ansi-term", |
||||||
|
"once_cell", |
||||||
|
"regex", |
||||||
|
"sharded-slab", |
||||||
|
"smallvec", |
||||||
|
"thread_local", |
||||||
|
"tracing", |
||||||
|
"tracing-core", |
||||||
|
"tracing-log", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "unicode-ident" |
||||||
|
version = "1.0.12" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "valuable" |
||||||
|
version = "0.1.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "winapi" |
||||||
|
version = "0.3.9" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" |
||||||
|
dependencies = [ |
||||||
|
"winapi-i686-pc-windows-gnu", |
||||||
|
"winapi-x86_64-pc-windows-gnu", |
||||||
|
] |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "winapi-i686-pc-windows-gnu" |
||||||
|
version = "0.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" |
||||||
|
|
||||||
|
[[package]] |
||||||
|
name = "winapi-x86_64-pc-windows-gnu" |
||||||
|
version = "0.4.0" |
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index" |
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" |
@ -0,0 +1,17 @@ |
|||||||
|
[workspace] |
||||||
|
members = [ |
||||||
|
"alea-core" |
||||||
|
] |
||||||
|
|
||||||
|
[package] |
||||||
|
name = "alea" |
||||||
|
version = "0.1.0" |
||||||
|
edition = "2021" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
alea-core = { path = "alea-core" } |
||||||
|
bytes = "1.5.0" |
||||||
|
tracing = "0.1.40" |
||||||
|
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } |
@ -0,0 +1,7 @@ |
|||||||
|
## what |
||||||
|
|
||||||
|
virtual cpu |
||||||
|
|
||||||
|
goes beep boop |
||||||
|
|
||||||
|
complete enough to do hello world |
@ -0,0 +1,11 @@ |
|||||||
|
[package] |
||||||
|
name = "alea-core" |
||||||
|
version = "0.1.0" |
||||||
|
edition = "2021" |
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||||
|
|
||||||
|
[dependencies] |
||||||
|
bitflags = "2.4.1" |
||||||
|
bytes = "1.5.0" |
||||||
|
tracing = "0.1.40" |
@ -0,0 +1,135 @@ |
|||||||
|
use crate::{ |
||||||
|
isa::{CmpFlags, InstVariant, Instruction, InstructionData}, |
||||||
|
vcore::CPUError, |
||||||
|
}; |
||||||
|
|
||||||
|
use tracing::{debug, trace}; |
||||||
|
|
||||||
|
impl TryFrom<u32> for Instruction { |
||||||
|
type Error = CPUError; |
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> { |
||||||
|
use InstVariant::*; |
||||||
|
|
||||||
|
let cmp = value.into(); |
||||||
|
let sign = (value & 0x00080000) != 0; |
||||||
|
|
||||||
|
let id = value.into(); |
||||||
|
|
||||||
|
let inst = match value >> 27 { |
||||||
|
0b00000 => { |
||||||
|
if value > 0 { |
||||||
|
// TODO: change this to only check low 16 bits, i.e. instdata
|
||||||
|
Halt |
||||||
|
} else { |
||||||
|
Nop |
||||||
|
} |
||||||
|
} |
||||||
|
0b00001 => Cmp(id), |
||||||
|
0b00010 => Set, |
||||||
|
0b00011 => Rst, |
||||||
|
0b00100 => Call(id), |
||||||
|
0b00101 => Ret, |
||||||
|
0b00110 => Jmp(id), |
||||||
|
0b00111 => JmpAbs(id), |
||||||
|
0b01000 => Ld(id), |
||||||
|
0b01001 => Sto(id), |
||||||
|
0b01011 => Push(id), |
||||||
|
0b01100 => Pop(id), |
||||||
|
0b01101 => Mov(id), |
||||||
|
0b01110 => return Err(CPUError::InvalidInstruction), |
||||||
|
0b01111 => return Err(CPUError::InvalidInstruction), |
||||||
|
0b10000 => Add(id), |
||||||
|
0b10001 => Mul(id), |
||||||
|
0b10010 => And(id), |
||||||
|
0b10011 => Or(id), |
||||||
|
0b10100 => Xor(id), |
||||||
|
0b10101 => Not(id), |
||||||
|
0b10110 => return Err(CPUError::InvalidInstruction), |
||||||
|
0b10111 => return Err(CPUError::InvalidInstruction), |
||||||
|
0b11000 => Lsl(id), |
||||||
|
0b11001 => Lsr(id), |
||||||
|
0b11010 => Asr(id), |
||||||
|
0b11011 => Ror(id), |
||||||
|
0b11100 => return Err(CPUError::InvalidInstruction), |
||||||
|
0b11101 => return Err(CPUError::InvalidInstruction), |
||||||
|
0b11110 => return Err(CPUError::InvalidInstruction), |
||||||
|
0b11111 => return Err(CPUError::InvalidInstruction), |
||||||
|
|
||||||
|
_ => unreachable!(), |
||||||
|
}; |
||||||
|
let i = Instruction { |
||||||
|
cmp, |
||||||
|
sign, |
||||||
|
var: inst, |
||||||
|
}; |
||||||
|
|
||||||
|
debug!("decoded {:#010x} into {}", value, i); |
||||||
|
|
||||||
|
Ok(i) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<u32> for InstructionData { |
||||||
|
fn from(value: u32) -> Self { |
||||||
|
use InstructionData::*; |
||||||
|
let variant = (value & 0x00070000) >> 16; |
||||||
|
trace!("variant is {}, value is {:#010x}", variant, value); |
||||||
|
|
||||||
|
match variant { |
||||||
|
0b000 => { |
||||||
|
let r1 = ((value & 0x0000F000) >> 12) as u8; |
||||||
|
let v = (value & 0x000000FF) as u8; |
||||||
|
RegisterAndU8(r1.try_into().unwrap(), v) |
||||||
|
} |
||||||
|
0b001 => { |
||||||
|
let r1 = ((value & 0x0000F000) >> 12) as u8; |
||||||
|
RegisterAndU32(r1.try_into().unwrap(), ()) |
||||||
|
} |
||||||
|
0b010 => { |
||||||
|
let r1 = ((value & 0x0000F000) >> 12) as u8; |
||||||
|
let r2 = ((value & 0x00000F00) >> 8) as u8; |
||||||
|
let v = (value & 0x000000FF) as u8; |
||||||
|
TwoRegistersAndU8(r1.try_into().unwrap(), r2.try_into().unwrap(), v) |
||||||
|
} |
||||||
|
0b011 => { |
||||||
|
let r1 = ((value & 0x0000F000) >> 12) as u8; |
||||||
|
let r2 = ((value & 0x00000F00) >> 8) as u8; |
||||||
|
TwoRegistersAndU32(r1.try_into().unwrap(), r2.try_into().unwrap(), ()) |
||||||
|
} |
||||||
|
0b100 => { |
||||||
|
let r1 = ((value & 0x0000F000) >> 12) as u8; |
||||||
|
OneRegister( |
||||||
|
r1.try_into() |
||||||
|
.expect("this should only be 4 bits large hmmm"), |
||||||
|
) |
||||||
|
} |
||||||
|
0b101 => { |
||||||
|
let r1 = ((value & 0x0000F000) >> 12) as u8; |
||||||
|
let r2 = ((value & 0x00000F00) >> 8) as u8; |
||||||
|
TwoRegisters(r1.try_into().expect("fuck"), r2.try_into().expect("fuck")) |
||||||
|
} |
||||||
|
0b110 => { |
||||||
|
let r1 = ((value & 0x0000F000) >> 12) as u8; |
||||||
|
let r2 = ((value & 0x00000F00) >> 8) as u8; |
||||||
|
let r3 = ((value & 0x000000F0) >> 4) as u8; |
||||||
|
ThreeRegisters( |
||||||
|
r1.try_into().unwrap(), |
||||||
|
r2.try_into().unwrap(), |
||||||
|
r3.try_into().unwrap(), |
||||||
|
) |
||||||
|
} |
||||||
|
0b111 => ImmediateOnly(()), |
||||||
|
_ => unreachable!(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl From<u32> for CmpFlags { |
||||||
|
fn from(value: u32) -> Self { |
||||||
|
let gt = (value & 0x04000000) != 0; |
||||||
|
let eq = (value & 0x02000000) != 0; |
||||||
|
let lt = (value & 0x01000000) != 0; |
||||||
|
CmpFlags { gt, eq, lt } |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,404 @@ |
|||||||
|
/// xgayuh instruction set
|
||||||
|
/// fuck it we ball
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// 32 bit instruction width
|
||||||
|
/// all instructions optional via state flag comparison
|
||||||
|
/// mmapped i/o
|
||||||
|
/// 16 registers
|
||||||
|
/// 32bit address space
|
||||||
|
///
|
||||||
|
use std::{ |
||||||
|
fmt::Display, |
||||||
|
ops::{Index, IndexMut}, |
||||||
|
}; |
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)] |
||||||
|
pub enum Register { |
||||||
|
R0, |
||||||
|
R1, |
||||||
|
R2, |
||||||
|
R3, |
||||||
|
R4, |
||||||
|
R5, |
||||||
|
R6, |
||||||
|
R7, |
||||||
|
R8, |
||||||
|
R9, |
||||||
|
CS, |
||||||
|
CA, |
||||||
|
ST, |
||||||
|
IA, |
||||||
|
RA, |
||||||
|
SA, |
||||||
|
} |
||||||
|
|
||||||
|
const ALL_REGISTERS: [Register; 16] = [ |
||||||
|
Register::R0, |
||||||
|
Register::R1, |
||||||
|
Register::R2, |
||||||
|
Register::R3, |
||||||
|
Register::R4, |
||||||
|
Register::R5, |
||||||
|
Register::R6, |
||||||
|
Register::R7, |
||||||
|
Register::R8, |
||||||
|
Register::R9, |
||||||
|
Register::CS, |
||||||
|
Register::CA, |
||||||
|
Register::ST, |
||||||
|
Register::IA, |
||||||
|
Register::RA, |
||||||
|
Register::SA, |
||||||
|
]; |
||||||
|
|
||||||
|
impl TryFrom<u8> for Register { |
||||||
|
type Error = (); |
||||||
|
|
||||||
|
fn try_from(value: u8) -> Result<Self, Self::Error> { |
||||||
|
use Register::*; |
||||||
|
|
||||||
|
match value { |
||||||
|
0b0000 => Ok(R0), |
||||||
|
0b0001 => Ok(R1), |
||||||
|
0b0010 => Ok(R2), |
||||||
|
0b0011 => Ok(R3), |
||||||
|
0b0100 => Ok(R4), |
||||||
|
0b0101 => Ok(R5), |
||||||
|
0b0110 => Ok(R6), |
||||||
|
0b0111 => Ok(R7), |
||||||
|
0b1000 => Ok(R8), |
||||||
|
0b1001 => Ok(R9), |
||||||
|
0b1010 => Ok(CS), |
||||||
|
0b1011 => Ok(CA), |
||||||
|
0b1100 => Ok(ST), |
||||||
|
0b1101 => Ok(IA), |
||||||
|
0b1110 => Ok(RA), |
||||||
|
0b1111 => Ok(SA), |
||||||
|
_ => Err(()), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] |
||||||
|
pub struct State(pub u32); |
||||||
|
|
||||||
|
bitflags::bitflags! { |
||||||
|
impl State: u32 { |
||||||
|
const CMP_GT = 1; |
||||||
|
const CMP_EQ = 1 << 1; |
||||||
|
const CMP_LT = 1 << 2; |
||||||
|
const CMP_EN = 1 << 3; |
||||||
|
const RET_ST = 1 << 4; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Clone, Copy, Default, Debug)] |
||||||
|
pub struct CmpFlags { |
||||||
|
pub gt: bool, |
||||||
|
pub eq: bool, |
||||||
|
pub lt: bool, |
||||||
|
} |
||||||
|
|
||||||
|
impl CmpFlags { |
||||||
|
pub fn new<T: Ord>(a: T, b: T) -> Self { |
||||||
|
CmpFlags { |
||||||
|
gt: a > b, |
||||||
|
eq: a == b, |
||||||
|
lt: a < b, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for CmpFlags { |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
write!( |
||||||
|
f, |
||||||
|
"CmpFlags({})", |
||||||
|
match (self.gt, self.eq, self.lt) { |
||||||
|
(true, true, true) => "ALL", |
||||||
|
(true, true, false) => "GE", |
||||||
|
(true, false, true) => "NE", |
||||||
|
(false, true, true) => "LE", |
||||||
|
(true, false, false) => "GT", |
||||||
|
(false, true, false) => "EQ", |
||||||
|
(false, false, true) => "LT", |
||||||
|
(false, false, false) => "NONE", |
||||||
|
}, |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Into<State> for CmpFlags { |
||||||
|
fn into(self) -> State { |
||||||
|
self.gt.then_some(State::CMP_GT).unwrap_or(State(0)) |
||||||
|
| self.eq.then_some(State::CMP_EQ).unwrap_or(State(0)) |
||||||
|
| self.lt.then_some(State::CMP_LT).unwrap_or(State(0)) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Default for State { |
||||||
|
fn default() -> Self { |
||||||
|
Self::CMP_EQ | Self::CMP_GT | Self::CMP_LT |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl State { |
||||||
|
pub fn set_cmp(&mut self, new: CmpFlags) { |
||||||
|
self.set(Self::CMP_GT, new.gt); |
||||||
|
self.set(Self::CMP_EQ, new.eq); |
||||||
|
self.set(Self::CMP_LT, new.lt); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get_cmp(&self) -> CmpFlags { |
||||||
|
CmpFlags { |
||||||
|
gt: self.contains(Self::CMP_GT), |
||||||
|
eq: self.contains(Self::CMP_EQ), |
||||||
|
lt: self.contains(Self::CMP_LT), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[repr(C)] |
||||||
|
#[derive(Default)] |
||||||
|
pub struct RegisterFile { |
||||||
|
pub r0: u32, |
||||||
|
pub r1: u32, |
||||||
|
pub r2: u32, |
||||||
|
pub r3: u32, |
||||||
|
pub r4: u32, |
||||||
|
pub r5: u32, |
||||||
|
pub r6: u32, |
||||||
|
pub r7: u32, |
||||||
|
pub r8: u32, |
||||||
|
pub r9: u32, |
||||||
|
/// call stack
|
||||||
|
pub cs: u32, |
||||||
|
/// "carry", used by the alu for extended results
|
||||||
|
pub ca: u32, |
||||||
|
/// "state", internal state and alu flags
|
||||||
|
pub st: State, |
||||||
|
/// instruction address
|
||||||
|
pub ia: u32, |
||||||
|
/// return address
|
||||||
|
pub ra: u32, |
||||||
|
/// stack address
|
||||||
|
pub sa: u32, |
||||||
|
} |
||||||
|
|
||||||
|
impl Index<u8> for RegisterFile { |
||||||
|
type Output = u32; |
||||||
|
|
||||||
|
fn index(&self, index: u8) -> &Self::Output { |
||||||
|
assert!(index < 16, "there's only 16 registers"); |
||||||
|
match index { |
||||||
|
0 => &self.r0, |
||||||
|
1 => &self.r1, |
||||||
|
2 => &self.r2, |
||||||
|
3 => &self.r3, |
||||||
|
4 => &self.r4, |
||||||
|
5 => &self.r5, |
||||||
|
6 => &self.r6, |
||||||
|
7 => &self.r7, |
||||||
|
8 => &self.r8, |
||||||
|
9 => &self.r9, |
||||||
|
10 => &self.cs, |
||||||
|
11 => &self.ca, |
||||||
|
12 => &self.st.0, |
||||||
|
13 => &self.ia, |
||||||
|
14 => &self.ra, |
||||||
|
15 => &self.sa, |
||||||
|
_ => unreachable!(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl IndexMut<u8> for RegisterFile { |
||||||
|
fn index_mut(&mut self, index: u8) -> &mut Self::Output { |
||||||
|
assert!(index < 16, "there's only 16 registers"); |
||||||
|
match index { |
||||||
|
0 => &mut self.r0, |
||||||
|
1 => &mut self.r1, |
||||||
|
2 => &mut self.r2, |
||||||
|
3 => &mut self.r3, |
||||||
|
4 => &mut self.r4, |
||||||
|
5 => &mut self.r5, |
||||||
|
6 => &mut self.r6, |
||||||
|
7 => &mut self.r7, |
||||||
|
8 => &mut self.r8, |
||||||
|
9 => &mut self.r9, |
||||||
|
10 => &mut self.cs, |
||||||
|
11 => &mut self.ca, |
||||||
|
12 => &mut self.st.0, |
||||||
|
13 => &mut self.ia, |
||||||
|
14 => &mut self.ra, |
||||||
|
15 => &mut self.sa, |
||||||
|
_ => unreachable!(), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Index<Register> for RegisterFile { |
||||||
|
type Output = u32; |
||||||
|
|
||||||
|
fn index(&self, index: Register) -> &Self::Output { |
||||||
|
use Register::*; |
||||||
|
|
||||||
|
match index { |
||||||
|
R0 => &self.r0, |
||||||
|
R1 => &self.r1, |
||||||
|
R2 => &self.r2, |
||||||
|
R3 => &self.r3, |
||||||
|
R4 => &self.r4, |
||||||
|
R5 => &self.r5, |
||||||
|
R6 => &self.r6, |
||||||
|
R7 => &self.r7, |
||||||
|
R8 => &self.r8, |
||||||
|
R9 => &self.r9, |
||||||
|
CS => &self.cs, |
||||||
|
CA => &self.ca, |
||||||
|
ST => &self.st.0, |
||||||
|
IA => &self.ia, |
||||||
|
RA => &self.ra, |
||||||
|
SA => &self.sa, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl IndexMut<Register> for RegisterFile { |
||||||
|
fn index_mut(&mut self, index: Register) -> &mut Self::Output { |
||||||
|
use Register::*; |
||||||
|
|
||||||
|
match index { |
||||||
|
R0 => &mut self.r0, |
||||||
|
R1 => &mut self.r1, |
||||||
|
R2 => &mut self.r2, |
||||||
|
R3 => &mut self.r3, |
||||||
|
R4 => &mut self.r4, |
||||||
|
R5 => &mut self.r5, |
||||||
|
R6 => &mut self.r6, |
||||||
|
R7 => &mut self.r7, |
||||||
|
R8 => &mut self.r8, |
||||||
|
R9 => &mut self.r9, |
||||||
|
CS => &mut self.cs, |
||||||
|
CA => &mut self.ca, |
||||||
|
ST => &mut self.st.0, |
||||||
|
IA => &mut self.ia, |
||||||
|
RA => &mut self.ra, |
||||||
|
SA => &mut self.sa, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for RegisterFile { |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
let mut out = String::new(); |
||||||
|
out += "RegisterFile {\n"; |
||||||
|
for regs in ALL_REGISTERS.chunks(4) { |
||||||
|
for r in regs { |
||||||
|
out += format!(" {:?}: {:#010x}", r, self[*r]).as_str(); |
||||||
|
} |
||||||
|
out += "\n"; |
||||||
|
} |
||||||
|
out += "};"; |
||||||
|
write!(f, "{}", out) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)] |
||||||
|
pub struct Instruction { |
||||||
|
pub cmp: CmpFlags, |
||||||
|
pub sign: bool, |
||||||
|
pub var: InstVariant, |
||||||
|
} |
||||||
|
|
||||||
|
impl Instruction { |
||||||
|
pub fn has_upcoming_value(&self) -> bool { |
||||||
|
use InstVariant::*; |
||||||
|
use InstructionData::*; |
||||||
|
match self.var { |
||||||
|
// this might be the worst match i've ever written
|
||||||
|
Set | Rst | Ret | Nop | Halt => false, |
||||||
|
Add(id) | Mul(id) | And(id) | Or(id) | Xor(id) | Not(id) | Cmp(id) | Lsl(id) |
||||||
|
| Lsr(id) | Asr(id) | Ror(id) | Ld(id) | Sto(id) | Push(id) | Pop(id) | Mov(id) |
||||||
|
| Call(id) | Jmp(id) | JmpAbs(id) => match id { |
||||||
|
RegisterAndU32(_, _) | TwoRegistersAndU32(_, _, _) | ImmediateOnly(_) => true, |
||||||
|
_ => false, |
||||||
|
}, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for Instruction { |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
write!( |
||||||
|
f, |
||||||
|
"Instruction({} {} {:?})", |
||||||
|
self.cmp, |
||||||
|
if self.sign { "Neg" } else { "Pos" }, |
||||||
|
self.var |
||||||
|
) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy, Debug)] |
||||||
|
/// first 5 bits: instruction variant
|
||||||
|
/// last 16 bits: decode into instruction data
|
||||||
|
/// bits 6-8: cmp bits, gt eq lt
|
||||||
|
/// 9-12: 4 bits of cozy padding
|
||||||
|
/// 13: sign bit
|
||||||
|
/// 14-16: instruction data variant
|
||||||
|
pub enum InstVariant { |
||||||
|
// data processing
|
||||||
|
Add(InstructionData), |
||||||
|
Mul(InstructionData), |
||||||
|
And(InstructionData), |
||||||
|
Or(InstructionData), |
||||||
|
Xor(InstructionData), |
||||||
|
Not(InstructionData), |
||||||
|
|
||||||
|
// comparison, branch control
|
||||||
|
Cmp(InstructionData), |
||||||
|
Set, |
||||||
|
Rst, |
||||||
|
|
||||||
|
// shifts
|
||||||
|
Lsl(InstructionData), |
||||||
|
Lsr(InstructionData), |
||||||
|
Asr(InstructionData), |
||||||
|
Ror(InstructionData), |
||||||
|
|
||||||
|
// Load and store and move
|
||||||
|
Ld(InstructionData), |
||||||
|
Sto(InstructionData), |
||||||
|
Push(InstructionData), |
||||||
|
Pop(InstructionData), |
||||||
|
Mov(InstructionData), |
||||||
|
|
||||||
|
// subroutine and jumps
|
||||||
|
Call(InstructionData), |
||||||
|
Ret, |
||||||
|
Jmp(InstructionData), |
||||||
|
JmpAbs(InstructionData), |
||||||
|
|
||||||
|
// misc
|
||||||
|
#[default] |
||||||
|
Nop, |
||||||
|
Halt, |
||||||
|
} |
||||||
|
|
||||||
|
/// a marker to make the cpu read the next 4 bytes from the program code as a literal value
|
||||||
|
pub type UpcomingU32 = (); |
||||||
|
|
||||||
|
// up to 16 bits large
|
||||||
|
#[derive(Clone, Copy, Debug)] |
||||||
|
pub enum InstructionData { |
||||||
|
OneRegister(Register), |
||||||
|
TwoRegisters(Register, Register), |
||||||
|
ThreeRegisters(Register, Register, Register), |
||||||
|
RegisterAndU8(Register, u8), |
||||||
|
RegisterAndU32(Register, UpcomingU32), |
||||||
|
TwoRegistersAndU8(Register, Register, u8), |
||||||
|
TwoRegistersAndU32(Register, Register, UpcomingU32), |
||||||
|
ImmediateOnly(UpcomingU32), |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
mod decode; |
||||||
|
pub mod isa; |
||||||
|
pub mod memory; |
||||||
|
pub mod vcore; |
||||||
|
|
||||||
|
pub mod prelude { |
||||||
|
pub use crate::isa::{ |
||||||
|
CmpFlags, InstVariant, Instruction, InstructionData, Register, RegisterFile, State, |
||||||
|
}; |
||||||
|
pub use crate::memory::{MappedMemory, Memory, MemoryMap}; |
||||||
|
pub use crate::vcore::{CPUError, CPU}; |
||||||
|
} |
@ -0,0 +1,177 @@ |
|||||||
|
use std::fmt::{Debug, Display}; |
||||||
|
|
||||||
|
use bytes::BytesMut; |
||||||
|
use tracing::{trace, warn}; |
||||||
|
|
||||||
|
pub trait Memory { |
||||||
|
fn size(&self) -> u32; |
||||||
|
fn read(&self, addr: u32) -> u8; |
||||||
|
fn write(&mut self, addr: u32, value: u8); |
||||||
|
|
||||||
|
fn read_u16(&self, addr: u32) -> u16 { |
||||||
|
let b1 = self.read(addr); |
||||||
|
let b2 = self.read(addr + 1); |
||||||
|
|
||||||
|
u16::from_le_bytes([b1, b2]) |
||||||
|
} |
||||||
|
|
||||||
|
fn read_u32(&self, addr: u32) -> u32 { |
||||||
|
let b1 = self.read(addr); |
||||||
|
let b2 = self.read(addr + 1); |
||||||
|
let b3 = self.read(addr + 2); |
||||||
|
let b4 = self.read(addr + 3); |
||||||
|
|
||||||
|
u32::from_le_bytes([b1, b2, b3, b4]) |
||||||
|
} |
||||||
|
|
||||||
|
fn write_u16(&mut self, addr: u32, value: u16) { |
||||||
|
let [b1, b2] = value.to_le_bytes(); |
||||||
|
self.write(addr, b1); |
||||||
|
self.write(addr + 1, b2); |
||||||
|
} |
||||||
|
|
||||||
|
fn write_u32(&mut self, addr: u32, value: u32) { |
||||||
|
let [b1, b2, b3, b4] = value.to_le_bytes(); |
||||||
|
self.write(addr, b1); |
||||||
|
self.write(addr + 1, b2); |
||||||
|
self.write(addr + 2, b3); |
||||||
|
self.write(addr + 3, b4); |
||||||
|
} |
||||||
|
|
||||||
|
fn pre_step(&mut self) {} |
||||||
|
fn post_step(&mut self) { |
||||||
|
warn!("mem-trait default: post-step"); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
type BoxedMem = Box<dyn Memory>; |
||||||
|
|
||||||
|
impl Memory for BytesMut { |
||||||
|
fn size(&self) -> u32 { |
||||||
|
self.len() as u32 |
||||||
|
} |
||||||
|
|
||||||
|
fn read(&self, addr: u32) -> u8 { |
||||||
|
self[addr as usize] |
||||||
|
} |
||||||
|
|
||||||
|
fn write(&mut self, addr: u32, value: u8) { |
||||||
|
self.get_mut(addr as usize).map(|v| *v = value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Default)] |
||||||
|
pub struct MemoryMap { |
||||||
|
pub mems: Vec<MappedMemory>, |
||||||
|
} |
||||||
|
|
||||||
|
impl MemoryMap { |
||||||
|
fn pos_resp(&self, addr: u32) -> Option<usize> { |
||||||
|
self.mems |
||||||
|
.iter() |
||||||
|
.position(|mem| mem.start <= addr && mem.end >= addr) |
||||||
|
} |
||||||
|
|
||||||
|
fn get_responsible(&self, addr: u32) -> Option<&MappedMemory> { |
||||||
|
self.pos_resp(addr).map(|idx| &self.mems[idx]) |
||||||
|
} |
||||||
|
|
||||||
|
fn get_responsible_mut(&mut self, addr: u32) -> Option<&mut MappedMemory> { |
||||||
|
self.pos_resp(addr).map(|idx| &mut self.mems[idx]) |
||||||
|
} |
||||||
|
|
||||||
|
fn collides(&self, b: &MappedMemory) -> bool { |
||||||
|
self.mems.iter().any(|a| { |
||||||
|
if a.start == b.start || a.end == b.end { |
||||||
|
false |
||||||
|
} else if a.start < b.start { |
||||||
|
a.end > b.start |
||||||
|
} else { |
||||||
|
a.start < b.end |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn add(&mut self, mem: impl Memory + 'static, at: u32) -> Result<(), ()> { |
||||||
|
let mapped = MappedMemory::new(Box::new(mem), at); |
||||||
|
if self.collides(&mapped) { |
||||||
|
return Err(()); |
||||||
|
} |
||||||
|
self.mems.push(mapped); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Display for MemoryMap { |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
write!(f, "MemoryMap({:?})", self.mems) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Memory for MemoryMap { |
||||||
|
fn size(&self) -> u32 { |
||||||
|
u32::MAX |
||||||
|
} |
||||||
|
|
||||||
|
fn read(&self, addr: u32) -> u8 { |
||||||
|
match self.get_responsible(addr) { |
||||||
|
None => { |
||||||
|
warn!("out of bounds read at {:#010x}", addr); |
||||||
|
0 |
||||||
|
} |
||||||
|
Some(mem) => { |
||||||
|
trace!("mmap: read of {:#x} delegated to {:?}", addr, mem); |
||||||
|
mem.read(addr) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn write(&mut self, addr: u32, value: u8) { |
||||||
|
self.get_responsible_mut(addr) |
||||||
|
.map(|mem| mem.write(addr, value)); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub struct MappedMemory { |
||||||
|
pub start: u32, |
||||||
|
pub end: u32, |
||||||
|
pub inner: BoxedMem, |
||||||
|
} |
||||||
|
|
||||||
|
impl MappedMemory { |
||||||
|
pub fn new(mem: BoxedMem, start: u32) -> MappedMemory { |
||||||
|
let size = mem.size(); |
||||||
|
let end = start + size - 1; |
||||||
|
|
||||||
|
Self { |
||||||
|
start, |
||||||
|
end, |
||||||
|
inner: mem, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Debug for MappedMemory { |
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||||
|
write!(f, "MappedMem({:#x} .. {:#x})", self.start, self.end) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Memory for MappedMemory { |
||||||
|
fn size(&self) -> u32 { |
||||||
|
self.inner.size() |
||||||
|
} |
||||||
|
|
||||||
|
fn read(&self, addr: u32) -> u8 { |
||||||
|
let internal = addr - self.start; |
||||||
|
assert!(internal < self.size()); |
||||||
|
self.inner.read(internal) |
||||||
|
} |
||||||
|
|
||||||
|
fn write(&mut self, addr: u32, value: u8) { |
||||||
|
let internal = addr - self.start; |
||||||
|
assert!(internal < self.size()); |
||||||
|
self.inner.write(internal, value); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,341 @@ |
|||||||
|
use std::io; |
||||||
|
|
||||||
|
use tracing::{debug, info, trace}; |
||||||
|
|
||||||
|
use crate::{ |
||||||
|
isa::{CmpFlags, InstVariant, Instruction, InstructionData, RegisterFile, State}, |
||||||
|
memory::{Memory, MemoryMap}, |
||||||
|
}; |
||||||
|
|
||||||
|
#[derive(Default)] |
||||||
|
pub struct CPU { |
||||||
|
pub mmap: MemoryMap, |
||||||
|
pub reg: RegisterFile, |
||||||
|
|
||||||
|
pub step_wait: bool, |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug)] |
||||||
|
pub enum CPUError { |
||||||
|
InvalidInstruction, |
||||||
|
Unimplemented, |
||||||
|
Halt, |
||||||
|
} |
||||||
|
|
||||||
|
fn wait() { |
||||||
|
io::stdin() |
||||||
|
.read_line(&mut String::new()) |
||||||
|
.expect("couldn't read stdin"); |
||||||
|
} |
||||||
|
|
||||||
|
impl CPU { |
||||||
|
pub fn run_until_halt(&mut self) -> Result<(), CPUError> { |
||||||
|
loop { |
||||||
|
match self.step() { |
||||||
|
Ok(_) => continue, |
||||||
|
Err(CPUError::Halt) => { |
||||||
|
info!("halting"); |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
Err(e) => return Err(e), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn call_peripherals_pre(&mut self) { |
||||||
|
self.mmap.mems.iter_mut().for_each(|m| m.inner.pre_step()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn call_peripherals_post(&mut self) { |
||||||
|
self.mmap.mems.iter_mut().for_each(|m| m.inner.post_step()) |
||||||
|
} |
||||||
|
|
||||||
|
pub fn step(&mut self) -> Result<(), CPUError> { |
||||||
|
use InstVariant::*; |
||||||
|
use InstructionData::*; |
||||||
|
|
||||||
|
debug!("calling peripherals pre-step"); |
||||||
|
self.call_peripherals_pre(); |
||||||
|
|
||||||
|
let current_ia = self.reg.ia; |
||||||
|
let current_inst: Instruction = self.read_ia().try_into()?; |
||||||
|
|
||||||
|
debug!("running instruction at {:#010x}", current_ia); |
||||||
|
trace!("register dump:\n{}", self.reg); |
||||||
|
|
||||||
|
if self.step_wait { |
||||||
|
info!("will run {}", current_inst); |
||||||
|
info!("waiting..."); |
||||||
|
wait(); |
||||||
|
} |
||||||
|
|
||||||
|
if self.reg.st.contains(State::CMP_EN) |
||||||
|
&& (self.reg.st & current_inst.cmp.into()) == State(0) |
||||||
|
{ |
||||||
|
debug!("optional instruction skipped!"); |
||||||
|
if current_inst.has_upcoming_value() { |
||||||
|
self.read_ia(); |
||||||
|
} |
||||||
|
return Ok(()); |
||||||
|
} |
||||||
|
|
||||||
|
match current_inst.var { |
||||||
|
Nop => (), |
||||||
|
Halt => return Err(CPUError::Halt), |
||||||
|
Add(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = if current_inst.sign { |
||||||
|
self.reg[a] - b |
||||||
|
} else { |
||||||
|
self.reg[a] + b |
||||||
|
}; |
||||||
|
} |
||||||
|
And(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = self.reg[a] & b; |
||||||
|
} |
||||||
|
Or(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = self.reg[a] | b; |
||||||
|
} |
||||||
|
Xor(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = self.reg[a] ^ b; |
||||||
|
} |
||||||
|
Not(id) => { |
||||||
|
let (t, v) = match id { |
||||||
|
OneRegister(r) => (r, self.reg[r]), |
||||||
|
TwoRegisters(t, v) => (t, self.reg[v]), |
||||||
|
RegisterAndU8(t, v) => (t, v as u32), |
||||||
|
RegisterAndU32(t, _) => (t, self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = !v; |
||||||
|
} |
||||||
|
Mul(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
RegisterAndU32(r, _) => (r, r, self.read_ia()), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
TwoRegistersAndU32(t, a, _) => (t, a, self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = self.reg[a] * b; |
||||||
|
} |
||||||
|
Cmp(id) => { |
||||||
|
let (a, b) = match id { |
||||||
|
OneRegister(a) => (a, 0), |
||||||
|
TwoRegisters(a, b) => (a, self.reg[b]), |
||||||
|
RegisterAndU8(a, b) => (a, b as u32), |
||||||
|
RegisterAndU32(a, _) => (a, self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg.st.set_cmp(CmpFlags::new(self.reg[a], b)); |
||||||
|
} |
||||||
|
Set => { |
||||||
|
self.reg.st.set(State::CMP_EN, current_inst.sign); |
||||||
|
} |
||||||
|
Rst => self.reg.st.set_cmp(CmpFlags { |
||||||
|
gt: true, |
||||||
|
eq: true, |
||||||
|
lt: true, |
||||||
|
}), |
||||||
|
Lsl(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = self.reg[a] << b; |
||||||
|
} |
||||||
|
Lsr(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = self.reg[a] >> b; |
||||||
|
} |
||||||
|
Asr(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
// rust does asr for signed ints, and lsr for unsigned
|
||||||
|
self.reg[t] = ((self.reg[a] as i32) >> b) as u32; |
||||||
|
} |
||||||
|
Ror(id) => { |
||||||
|
let (t, a, b) = match id { |
||||||
|
TwoRegisters(a, b) => (a, a, self.reg[b]), |
||||||
|
ThreeRegisters(t, a, b) => (t, a, self.reg[b]), |
||||||
|
RegisterAndU8(r, v) => (r, r, v as u32), |
||||||
|
TwoRegistersAndU8(t, a, b) => (t, a, b as u32), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[t] = self.reg[a].rotate_right(b); |
||||||
|
} |
||||||
|
Ld(id) => { |
||||||
|
let (a, r) = match id { |
||||||
|
TwoRegisters(r, a) => (self.reg[a], r), |
||||||
|
RegisterAndU32(r, _) => (self.read_ia(), r), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[r] = self.mmap.read_u32(a); |
||||||
|
} |
||||||
|
Sto(id) => { |
||||||
|
let (v, a) = match id { |
||||||
|
TwoRegisters(v, a) => (self.reg[v], self.reg[a]), |
||||||
|
RegisterAndU32(v, _) => (self.reg[v], self.read_ia()), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.mmap.write_u32(a, v); |
||||||
|
} |
||||||
|
Push(id) => { |
||||||
|
match id { |
||||||
|
OneRegister(a) => vec![self.reg[a]], |
||||||
|
TwoRegisters(a, b) => vec![self.reg[a], self.reg[b]], |
||||||
|
ThreeRegisters(a, b, c) => vec![self.reg[a], self.reg[b], self.reg[c]], |
||||||
|
RegisterAndU8(a, b) => vec![self.reg[a], b as u32], |
||||||
|
RegisterAndU32(a, _) => vec![self.reg[a], self.read_ia()], |
||||||
|
TwoRegistersAndU8(a, b, c) => vec![self.reg[a], self.reg[b], c as u32], |
||||||
|
TwoRegistersAndU32(a, b, _) => vec![self.reg[a], self.reg[b], self.read_ia()], |
||||||
|
ImmediateOnly(_) => vec![self.read_ia()], |
||||||
|
} |
||||||
|
.into_iter() |
||||||
|
.for_each(|val| self.push_stack(val)); |
||||||
|
} |
||||||
|
Pop(id) => { |
||||||
|
match id { |
||||||
|
OneRegister(a) => vec![a], |
||||||
|
TwoRegisters(a, b) => vec![a, b], |
||||||
|
ThreeRegisters(a, b, c) => vec![a, b, c], |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
} |
||||||
|
.into_iter() |
||||||
|
.for_each(|r| self.reg[r] = self.pop_stack()); |
||||||
|
} |
||||||
|
Mov(id) => { |
||||||
|
let (r, v) = match id { |
||||||
|
TwoRegisters(a, b) => (a, self.reg[b]), |
||||||
|
RegisterAndU32(a, _) => (a, self.read_ia()), |
||||||
|
RegisterAndU8(a, v) => (a, v as u32), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg[r] = v; |
||||||
|
} |
||||||
|
Call(id) => { |
||||||
|
let call_addr = match id { |
||||||
|
OneRegister(r) => self.reg[r], |
||||||
|
ImmediateOnly(_) => self.read_ia(), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.push_cs(); |
||||||
|
self.reg.ra = self.reg.ia; |
||||||
|
self.reg.ia = call_addr; |
||||||
|
} |
||||||
|
Ret => { |
||||||
|
self.reg.ia = self.reg.ra; |
||||||
|
self.pop_cs(); |
||||||
|
} |
||||||
|
Jmp(id) => { |
||||||
|
let jump_offset = match id { |
||||||
|
OneRegister(r) => self.reg[r], |
||||||
|
ImmediateOnly(_) => self.read_ia(), |
||||||
|
RegisterAndU8(_, v) | TwoRegistersAndU8(_, _, v) => (v as u32) * 4, |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg.ia = if current_inst.sign { |
||||||
|
current_ia - jump_offset |
||||||
|
} else { |
||||||
|
current_ia + jump_offset |
||||||
|
}; |
||||||
|
} |
||||||
|
JmpAbs(id) => { |
||||||
|
let jump_addr = match id { |
||||||
|
OneRegister(r) => self.reg[r], |
||||||
|
ImmediateOnly(_) => self.read_ia(), |
||||||
|
_ => return Err(CPUError::InvalidInstruction), |
||||||
|
}; |
||||||
|
self.reg.ia = jump_addr; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
debug!("calling peripherals post-step"); |
||||||
|
self.call_peripherals_post(); |
||||||
|
|
||||||
|
Ok(()) |
||||||
|
} |
||||||
|
|
||||||
|
fn read_ia(&mut self) -> u32 { |
||||||
|
let v = self.mmap.read_u32(self.reg.ia); |
||||||
|
self.reg.ia += 4; |
||||||
|
trace!("read_ia: {:#010x}", v); |
||||||
|
v |
||||||
|
} |
||||||
|
|
||||||
|
fn push_stack(&mut self, v: u32) { |
||||||
|
self.reg.sa -= 4; |
||||||
|
debug!("pushed {:#010x} to {:#010x}", v, self.reg.sa); |
||||||
|
self.mmap.write_u32(self.reg.sa, v); |
||||||
|
} |
||||||
|
|
||||||
|
fn pop_stack(&mut self) -> u32 { |
||||||
|
let v = self.mmap.read_u32(self.reg.sa); |
||||||
|
debug!("popped {:#010x} from {:#010x}", v, self.reg.sa); |
||||||
|
self.reg.sa += 4; |
||||||
|
v |
||||||
|
} |
||||||
|
|
||||||
|
fn push_cs(&mut self) { |
||||||
|
self.reg.cs -= 4; |
||||||
|
self.mmap.write_u32(self.reg.cs, self.reg.ra); |
||||||
|
debug!("pushed {:#010x} onto callstack", self.reg.ra); |
||||||
|
} |
||||||
|
|
||||||
|
fn pop_cs(&mut self) { |
||||||
|
self.reg.ra = self.mmap.read_u32(self.reg.cs); |
||||||
|
self.reg.cs += 4; |
||||||
|
debug!("popped {:#010x} from callstack", self.reg.ra); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
use alea_core::prelude::*; |
||||||
|
use bytes::BytesMut; |
||||||
|
|
||||||
|
pub struct Console(pub BytesMut); |
||||||
|
|
||||||
|
impl Memory for Console { |
||||||
|
fn size(&self) -> u32 { |
||||||
|
self.0.size() |
||||||
|
} |
||||||
|
|
||||||
|
fn read(&self, addr: u32) -> u8 { |
||||||
|
self.0.read(addr) |
||||||
|
} |
||||||
|
|
||||||
|
fn write(&mut self, addr: u32, value: u8) { |
||||||
|
self.0.write(addr, value) |
||||||
|
} |
||||||
|
|
||||||
|
fn post_step(&mut self) { |
||||||
|
if self.ready_to_flush() { |
||||||
|
self.flush(); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
impl Console { |
||||||
|
fn ready_to_flush(&self) -> bool { |
||||||
|
self.0.iter().any(|thing| *thing == 0x17) |
||||||
|
} |
||||||
|
|
||||||
|
fn flush(&mut self) { |
||||||
|
let mut bytes = Vec::new(); |
||||||
|
for byte in self.0.iter_mut() { |
||||||
|
let copy = *byte; |
||||||
|
*byte = 0; |
||||||
|
if copy == 0x17 { |
||||||
|
break; |
||||||
|
} |
||||||
|
bytes.push(copy); |
||||||
|
} |
||||||
|
let s = String::from_utf8_lossy(bytes.as_slice()); |
||||||
|
print!("{}", s); |
||||||
|
} |
||||||
|
} |
Binary file not shown.
@ -0,0 +1,32 @@ |
|||||||
|
mod console; |
||||||
|
|
||||||
|
use alea_core::prelude::*; |
||||||
|
|
||||||
|
use bytes::BytesMut; |
||||||
|
use console::Console; |
||||||
|
use tracing::info; |
||||||
|
use tracing_subscriber::EnvFilter; |
||||||
|
|
||||||
|
fn main() { |
||||||
|
tracing_subscriber::fmt() |
||||||
|
.with_env_filter(EnvFilter::from_default_env()) |
||||||
|
.init(); |
||||||
|
|
||||||
|
let mut cpu = CPU::default(); |
||||||
|
|
||||||
|
let hello_world_data = BytesMut::from(b"hello, world!\n\x17".as_slice()); |
||||||
|
let program = BytesMut::from(include_bytes!("hello.alea").as_slice()); |
||||||
|
let mem = BytesMut::zeroed(0x4000); |
||||||
|
let console = Console(BytesMut::zeroed(0x20)); |
||||||
|
|
||||||
|
cpu.mmap.add(program, 0x0).unwrap(); |
||||||
|
cpu.mmap.add(hello_world_data, 0x2000).unwrap(); |
||||||
|
cpu.mmap.add(mem, 0x4000).unwrap(); |
||||||
|
cpu.mmap.add(console, 0x8000).unwrap(); |
||||||
|
|
||||||
|
cpu.step_wait = false; |
||||||
|
|
||||||
|
info!("running with {}", cpu.mmap); |
||||||
|
|
||||||
|
cpu.run_until_halt().unwrap(); |
||||||
|
} |
Loading…
Reference in new issue