xenua
1 year ago
commit
c7a118cee8
14 changed files with 1480 additions and 0 deletions
@ -0,0 +1,297 @@
@@ -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 @@
@@ -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 @@
@@ -0,0 +1,7 @@
|
||||
## what |
||||
|
||||
virtual cpu |
||||
|
||||
goes beep boop |
||||
|
||||
complete enough to do hello world |
@ -0,0 +1,2 @@
@@ -0,0 +1,2 @@
|
||||
/target |
||||
/Cargo.lock |
@ -0,0 +1,11 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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 @@
@@ -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