summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Bridle <bridle.benjamin@gmail.com>2025-07-03 14:53:16 +1200
committerBen Bridle <bridle.benjamin@gmail.com>2025-07-03 14:53:16 +1200
commit701448be2c3c58e30960d46f090bf08adfc02832 (patch)
tree409be74cdbb4bf311cb98fbee14f67fcd9c680d0
downloadbedrock-core-701448be2c3c58e30960d46f090bf08adfc02832.zip
Initial commit
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml10
-rw-r--r--src/components/device_bus.rs48
-rw-r--r--src/components/metadata.rs104
-rw-r--r--src/components/mod.rs10
-rw-r--r--src/components/processor.rs342
-rw-r--r--src/components/program_memory.rs66
-rw-r--r--src/components/stack.rs43
-rw-r--r--src/core.rs31
-rw-r--r--src/emulator.rs21
-rw-r--r--src/lib.rs29
-rw-r--r--src/signal.rs19
13 files changed, 731 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..d085c99
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "bedrock-core"
+version = "0.0.1"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..4e1cb8f
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "bedrock-core"
+version = "0.0.1"
+authors = ["Ben Bridle"]
+edition = "2024"
+description = "Bedrock core architecture"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/components/device_bus.rs b/src/components/device_bus.rs
new file mode 100644
index 0000000..a15d836
--- /dev/null
+++ b/src/components/device_bus.rs
@@ -0,0 +1,48 @@
+use crate::*;
+
+
+pub trait DeviceBus {
+ /// Attempt to wake the system.
+ fn wake(&mut self) -> bool;
+ /// Get a device by slot number.
+ fn get_device(&mut self, slot: u8) -> Option<&mut dyn Device>;
+ // Read from a device port.
+ fn read(&mut self, port: u8) -> u8 {
+ match self.get_device(port >> 4) {
+ Some(device) => device.read(port & 0xF),
+ None => 0,
+ }
+ }
+ // Write to a device port.
+ fn write(&mut self, port: u8, value: u8) -> Option<Signal> {
+ match self.get_device(port >> 4) {
+ Some(device) => device.write(port & 0xF, value),
+ None => None,
+ }
+ }
+ /// Reset all connected devices.
+ fn reset(&mut self) {
+ for slot in 0..16 {
+ if let Some(device) = self.get_device(slot) {
+ device.reset();
+ }
+ }
+ }
+}
+
+
+pub trait Device {
+ fn read(&mut self, port: u8) -> u8;
+ fn write(&mut self, port: u8, value: u8) -> Option<Signal>;
+ /// Return and clear the wake flag for this device.
+ fn wake(&mut self) -> bool;
+ /// Reset device to the initial state.
+ fn reset(&mut self);
+}
+
+impl Device for () {
+ fn read(&mut self, _: u8) -> u8 { 0 }
+ fn write(&mut self, _: u8, _: u8) -> Option<Signal> { None }
+ fn wake(&mut self) -> bool { false }
+ fn reset(&mut self) { }
+}
diff --git a/src/components/metadata.rs b/src/components/metadata.rs
new file mode 100644
index 0000000..5c2361b
--- /dev/null
+++ b/src/components/metadata.rs
@@ -0,0 +1,104 @@
+pub struct Metadata<'a> {
+ program: &'a [u8],
+}
+
+impl<'a> Metadata<'a> {
+ pub fn from(program: &'a [u8]) -> Option<Self> {
+ if program.len() < 10 {
+ return None;
+ }
+ let identifier = [0xE8,0x00,0x18,0x42,0x45,0x44,0x52,0x4F,0x43,0x4B];
+ match program[0..10] == identifier {
+ true => Some(Self { program }),
+ false => None,
+ }
+ }
+
+ pub fn name(&self) -> Option<String> {
+ let pointer = self.get_pointer(0x000A)?;
+ self.get_string(pointer)
+ }
+
+ pub fn authors(&self) -> Option<Vec<String>> {
+ let pointer = self.get_pointer(0x000C)?;
+ let string = self.get_string(pointer)?;
+ Some(string.lines().map(String::from).collect())
+ }
+
+ pub fn description(&self) -> Option<String> {
+ let pointer = self.get_pointer(0x000E)?;
+ self.get_string(pointer)
+ }
+
+ pub fn bg_colour(&self) -> Option<MetadataColour> {
+ let pointer = self.get_pointer(0x0010)?;
+ let colour = self.get_pointer(pointer)?;
+ Some(MetadataColour::from_double(colour))
+ }
+
+ pub fn fg_colour(&self) -> Option<MetadataColour> {
+ let pointer = self.get_pointer(0x0012)?;
+ let colour = self.get_pointer(pointer)?;
+ Some(MetadataColour::from_double(colour))
+ }
+
+ pub fn small_icon(&self) -> Option<Vec<u8>> {
+ let pointer = self.get_pointer(0x0014)?;
+ self.get_icon(pointer, 72)
+ }
+
+ pub fn large_icon(&self) -> Option<Vec<u8>> {
+ let pointer = self.get_pointer(0x0016)?;
+ self.get_icon(pointer, 512)
+ }
+
+ fn get_byte(&self, address: u16) -> Option<u8> {
+ self.program.get(address as usize).copied()
+ }
+
+ fn get_pointer(&self, address: u16) -> Option<u16> {
+ let high = self.get_byte(address)?;
+ let low = self.get_byte(address.checked_add(1)?)?;
+ let pointer = u16::from_be_bytes([high, low]);
+ if pointer != 0 { Some(pointer) } else { None }
+ }
+
+ fn get_string(&self, address: u16) -> Option<String> {
+ let start = address as usize;
+ let mut end = address as usize;
+ loop {
+ match self.program.get(end) {
+ Some(0) => break,
+ Some(_) => end += 1,
+ None => return None,
+ }
+ }
+ let vec = self.program[start..end].to_vec();
+ String::from_utf8(vec).ok()
+ }
+
+ fn get_icon(&self, address: u16, length: usize) -> Option<Vec<u8>> {
+ let address = address as usize;
+ let data = &self.program[address..address+length];
+ match data.len() == length {
+ true => Some(data.to_vec()),
+ false => None,
+ }
+ }
+}
+
+
+pub struct MetadataColour {
+ pub red: u8,
+ pub green: u8,
+ pub blue: u8,
+}
+
+impl MetadataColour {
+ pub fn from_double(colour: u16) -> Self {
+ let red = (colour & 0x0F00 >> 8) as u8 * 17;
+ let green = (colour & 0x00F0 >> 4) as u8 * 17;
+ let blue = (colour & 0x000F >> 0) as u8 * 17;
+ Self { red, green, blue }
+ }
+}
diff --git a/src/components/mod.rs b/src/components/mod.rs
new file mode 100644
index 0000000..448015f
--- /dev/null
+++ b/src/components/mod.rs
@@ -0,0 +1,10 @@
+mod device_bus;
+mod metadata;
+mod processor;
+mod program_memory;
+mod stack;
+
+pub use device_bus::*;
+pub use metadata::*;
+pub use program_memory::*;
+pub use stack::*;
diff --git a/src/components/processor.rs b/src/components/processor.rs
new file mode 100644
index 0000000..c1eca06
--- /dev/null
+++ b/src/components/processor.rs
@@ -0,0 +1,342 @@
+use crate::*;
+
+use std::ops::*;
+
+
+impl <DB: DeviceBus> BedrockEmulator<DB> {
+ pub fn evaluate(&mut self, cycles: usize, debug: bool) -> Option<Signal> {
+ macro_rules! WPSH1 { ($x:expr) => { self.core.wst.push_u8($x) }; }
+ macro_rules! RPSH1 { ($x:expr) => { self.core.rst.push_u8($x) }; }
+ macro_rules! WPSH2 { ($x:expr,$y:expr) => { WPSH1!($x); WPSH1!($y); }; }
+ macro_rules! RPSH2 { ($x:expr,$y:expr) => { RPSH1!($x); RPSH1!($y); }; }
+ macro_rules! WPSHD { ($d:expr) => { self.core.wst.push_u16($d) }; }
+ macro_rules! RPSHD { ($d:expr) => { self.core.rst.push_u16($d) }; }
+ macro_rules! WPSHB { ($x:expr) => { self.core.wst.push_u8(read_b!($x)) }; }
+ macro_rules! RPSHB { ($x:expr) => { self.core.rst.push_u8(read_b!($x)) }; }
+
+ macro_rules! WPOP1 { ($x:ident) => { let $x = self.core.wst.pop_u8(); }; }
+ macro_rules! RPOP1 { ($x:ident) => { let $x = self.core.rst.pop_u8(); }; }
+ macro_rules! WPOP2 { ($x:ident,$y:ident) => { WPOP1!($y); WPOP1!($x); }; }
+ macro_rules! RPOP2 { ($x:ident,$y:ident) => { RPOP1!($y); RPOP1!($x); }; }
+ macro_rules! WPOPD { ($d:ident) => { let $d = self.core.wst.pop_u16(); }; }
+ macro_rules! RPOPD { ($d:ident) => { let $d = self.core.rst.pop_u16(); }; }
+
+ macro_rules! WGETV { ($i:expr) => { self.core.wst.mem[self.core.wst.sp.wrapping_sub($i) as usize] }; }
+ macro_rules! RGETV { ($i:expr) => { self.core.rst.mem[self.core.rst.sp.wrapping_sub($i) as usize] }; }
+ macro_rules! WGET1 { ($x:ident) => { let $x = WGETV!(1); }; }
+ macro_rules! RGET1 { ($x:ident) => { let $x = RGETV!(1); }; }
+ macro_rules! WGET1N { ($x:ident) => { let $x = WGETV!(2); }; }
+ macro_rules! RGET1N { ($x:ident) => { let $x = RGETV!(2); }; }
+ macro_rules! WGET2 { ($x:ident,$y:ident) => { let $x = WGETV!(2); let $y = WGETV!(1); }; }
+ macro_rules! RGET2 { ($x:ident,$y:ident) => { let $x = RGETV!(2); let $y = RGETV!(1); }; }
+ macro_rules! WGET2N { ($x:ident,$y:ident) => { let $x = WGETV!(4); let $y = WGETV!(3); }; }
+ macro_rules! RGET2N { ($x:ident,$y:ident) => { let $x = RGETV!(4); let $y = RGETV!(3); }; }
+ macro_rules! WGETD { ($d:ident) => { let $d = u16::from_be_bytes([WGETV!(2),WGETV!(1)]); }; }
+ macro_rules! RGETD { ($d:ident) => { let $d = u16::from_be_bytes([RGETV!(2),RGETV!(1)]); }; }
+ macro_rules! WGETDN { ($d:ident) => { let $d = u16::from_be_bytes([WGETV!(4),WGETV!(3)]); }; }
+ macro_rules! RGETDN { ($d:ident) => { let $d = u16::from_be_bytes([RGETV!(4),RGETV!(3)]); }; }
+
+ macro_rules! PCSET { ($a:expr) => { self.core.mem.pc=$a }; }
+ macro_rules! MEM { ($a:expr) => { self.core.mem.mem[$a as usize] }; }
+ macro_rules! MEMN { ($a:expr) => { self.core.mem.mem[$a.wrapping_add(1) as usize] }; }
+ macro_rules! DEV { ($p:expr) => { self.dev.read($p) }; }
+ macro_rules! DEVN { ($p:expr) => { self.dev.read($p.wrapping_add(1)) }; }
+
+ macro_rules! MLIT1 { ($x:ident) => { let $x = self.core.mem.read_u8_next(); }; }
+ macro_rules! MLIT2 { ($x:ident,$y:ident) => { MLIT1!($x); MLIT1!($y); }; }
+ macro_rules! MLITD { ($d:ident) => { let $d = self.core.mem.read_u16_next(); }; }
+
+ macro_rules! DSET1 { ($p:expr,$x:expr) => {
+ let s = self.dev.write($p, $x);
+ if s.is_some() { return s };
+ }; }
+ macro_rules! DSET2 { ($p:expr,$x:expr,$y:expr) => {
+ let s1 = self.dev.write($p, $x);
+ let s2 = self.dev.write($p.wrapping_add(1), $y);
+ if s1.is_some() { return s1 };
+ if s2.is_some() { return s2 };
+ }; }
+
+ macro_rules! math {
+ ($v:expr; ADD $i:expr) => { $v = $v.wrapping_add($i); };
+ ($v:expr; SUB $i:expr) => { $v = $v.wrapping_sub($i); };
+ ($v:expr; IOR $i:expr) => { $v = $v.bitor($i); };
+ ($v:expr; XOR $i:expr) => { $v = $v.bitxor($i); };
+ ($v:expr; AND $i:expr) => { $v = $v.bitand($i); };
+ ($v:expr; NOT ) => { $v = $v.not(); };
+ }
+
+ let end = self.core.cycle + cycles;
+ while self.core.cycle < end {
+ self.core.cycle += 1;
+
+ match self.core.mem.read_u8_next() {
+ /* HLT */ 0x00 => { return Some(Signal::Halt); }
+ /* PSH */ 0x01 => { RPOP1!(x); WPSH1!(x); }
+ /* POP */ 0x02 => { math!(self.core.wst.sp; SUB 1); }
+ /* CPY */ 0x03 => { RGET1!(x); WPSH1!(x); }
+ /* DUP */ 0x04 => { WGET1!(x); WPSH1!(x); }
+ /* OVR */ 0x05 => { WGET1N!(x); WPSH1!(x); }
+ /* SWP */ 0x06 => { WPOP1!(y); WPOP1!(x); WPSH1!(y); WPSH1!(x); }
+ /* ROT */ 0x07 => { WPOP1!(z); WPOP1!(y); WPOP1!(x); WPSH1!(y); WPSH1!(z); WPSH1!(x); }
+ /* JMP */ 0x08 => { WPOPD!(a); PCSET!(a); }
+ /* JMS */ 0x09 => { WPOPD!(a); RPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCN */ 0x0A => { WPOPD!(a); WPOP1!(t); if t!=0 {PCSET!(a)}; }
+ /* JCS */ 0x0B => { WPOPD!(a); WPOP1!(t); if t!=0 {RPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDA */ 0x0C => { WPOPD!(a); WPSH1!(MEM!(a)); }
+ /* STA */ 0x0D => { WPOPD!(a); WPOP1!(x); MEM!(a)=x; }
+ /* LDD */ 0x0E => { WPOP1!(p); WPSH1!(DEV!(p)); }
+ /* STD */ 0x0F => { WPOP1!(p); WPOP1!(x); DSET1!(p,x); }
+ /* ADD */ 0x10 => { WPOP1!(y); math!(WGETV!(1); ADD y); }
+ /* SUB */ 0x11 => { WPOP1!(y); math!(WGETV!(1); SUB y); }
+ /* INC */ 0x12 => { math!(WGETV!(1); ADD 1); }
+ /* DEC */ 0x13 => { math!(WGETV!(1); SUB 1); }
+ /* LTH */ 0x14 => { WPOP1!(y); WPOP1!(x); WPSHB!(x < y); }
+ /* GTH */ 0x15 => { WPOP1!(y); WPOP1!(x); WPSHB!(x > y); }
+ /* EQU */ 0x16 => { WPOP1!(y); WPOP1!(x); WPSHB!(x==y); }
+ /* NQK */ 0x17 => { WGET2!(x,y); WPSHB!(x!=y); }
+ /* SHL */ 0x18 => { WPOP1!(y); WPOP1!(x); WPSH1!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHR */ 0x19 => { WPOP1!(y); WPOP1!(x); WPSH1!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROL */ 0x1A => { WPOP1!(y); WPOP1!(x); WPSH1!(x.rotate_left(y as u32)); }
+ /* ROR */ 0x1B => { WPOP1!(y); WPOP1!(x); WPSH1!(x.rotate_right(y as u32)); }
+ /* IOR */ 0x1C => { WPOP1!(y); math!(WGETV!(1); IOR y); }
+ /* XOR */ 0x1D => { WPOP1!(y); math!(WGETV!(1); XOR y); }
+ /* AND */ 0x1E => { WPOP1!(y); math!(WGETV!(1); AND y); }
+ /* NOT */ 0x1F => { math!(WGETV!(1); NOT); }
+
+ /* NOP */ 0x20 => { }
+ /* PSH: */ 0x21 => { MLIT1!(x); WPSH1!(x); }
+ /* POP: */ 0x22 => { math!(self.core.mem.pc; ADD 1); }
+ /* CPY: */ 0x23 => { MLIT1!(x); WPSH1!(x); RPSH1!(x); }
+ /* DUP: */ 0x24 => { MLIT1!(x); WPSH1!(x); WPSH1!(x); }
+ /* OVR: */ 0x25 => { MLIT1!(y); WGET1!(x); WPSH1!(y); WPSH1!(x); }
+ /* SWP: */ 0x26 => { MLIT1!(y); WPOP1!(x); WPSH1!(y); WPSH1!(x); }
+ /* ROT: */ 0x27 => { MLIT1!(z); WPOP1!(y); WPOP1!(x); WPSH1!(y); WPSH1!(z); WPSH1!(x); }
+ /* JMP: */ 0x28 => { MLITD!(a); PCSET!(a); }
+ /* JMS: */ 0x29 => { MLITD!(a); RPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCN: */ 0x2A => { MLITD!(a); WPOP1!(t); if t!=0 {PCSET!(a)}; }
+ /* JCS: */ 0x2B => { MLITD!(a); WPOP1!(t); if t!=0 {RPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDA: */ 0x2C => { MLITD!(a); WPSH1!(MEM!(a)); }
+ /* STA: */ 0x2D => { MLITD!(a); WPOP1!(x); MEM!(a)=x; }
+ /* LDD: */ 0x2E => { MLIT1!(p); WPSH1!(DEV!(p)); }
+ /* STD: */ 0x2F => { MLIT1!(p); WPOP1!(x); DSET1!(p,x); }
+ /* ADD: */ 0x30 => { MLIT1!(y); math!(WGETV!(1); ADD y); }
+ /* SUB: */ 0x31 => { MLIT1!(y); math!(WGETV!(1); SUB y); }
+ /* INC: */ 0x32 => { MLIT1!(x); WPSH1!(x.wrapping_add(1)); }
+ /* DEC: */ 0x33 => { MLIT1!(x); WPSH1!(x.wrapping_sub(1)); }
+ /* LTH: */ 0x34 => { MLIT1!(y); WPOP1!(x); WPSHB!(x < y); }
+ /* GTH: */ 0x35 => { MLIT1!(y); WPOP1!(x); WPSHB!(x > y); }
+ /* EQU: */ 0x36 => { MLIT1!(y); WPOP1!(x); WPSHB!(x==y); }
+ /* NQK: */ 0x37 => { MLIT1!(y); WGET1!(x); WPSH1!(y); WPSHB!(x!=y); }
+ /* SHL: */ 0x38 => { MLIT1!(y); WPOP1!(x); WPSH1!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHR: */ 0x39 => { MLIT1!(y); WPOP1!(x); WPSH1!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROL: */ 0x3A => { MLIT1!(y); WPOP1!(x); WPSH1!(x.rotate_left(y as u32)); }
+ /* ROR: */ 0x3B => { MLIT1!(y); WPOP1!(x); WPSH1!(x.rotate_right(y as u32)); }
+ /* IOR: */ 0x3C => { MLIT1!(y); math!(WGETV!(1); IOR y); }
+ /* XOR: */ 0x3D => { MLIT1!(y); math!(WGETV!(1); XOR y); }
+ /* AND: */ 0x3E => { MLIT1!(y); math!(WGETV!(1); AND y); }
+ /* NOT: */ 0x3F => { MLIT1!(x); WPSH1!(x.not()); }
+
+ /* DB1 */ 0x40 => { if debug { return Some(Signal::Debug(Debug::Debug1)); } }
+ /* PSH* */ 0x41 => { RPOP2!(x,y); WPSH2!(x,y); }
+ /* POP* */ 0x42 => { math!(self.core.wst.sp; SUB 2); }
+ /* CPY* */ 0x43 => { RGET2!(x,y); WPSH2!(x,y); }
+ /* DUP* */ 0x44 => { WGET2!(x,y); WPSH2!(x,y); }
+ /* OVR* */ 0x45 => { WGET2N!(x,y); WPSH2!(x,y); }
+ /* SWP* */ 0x46 => { WPOP2!(c,d); WPOP2!(a,b); WPSH2!(c,d); WPSH2!(a,b); }
+ /* ROT* */ 0x47 => { WPOP2!(e,f); WPOP2!(c,d); WPOP2!(a,b); WPSH2!(c,d); WPSH2!(e,f); WPSH2!(a,b); }
+ /* JMP* */ 0x48 => { WPOPD!(a); PCSET!(a); }
+ /* JMS* */ 0x49 => { WPOPD!(a); RPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCN* */ 0x4A => { WPOPD!(a); WPOPD!(t); if t!=0 {PCSET!(a)}; }
+ /* JCS* */ 0x4B => { WPOPD!(a); WPOPD!(t); if t!=0 {RPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDA* */ 0x4C => { WPOPD!(a); WPSH2!(MEM!(a),MEMN!(a)); }
+ /* STA* */ 0x4D => { WPOPD!(a); WPOP2!(x,y); MEM!(a)=x; MEMN!(a)=y; }
+ /* LDD* */ 0x4E => { WPOP1!(p); WPSH2!(DEV!(p),DEVN!(p)); }
+ /* STD* */ 0x4F => { WPOP1!(p); WPOP2!(x,y); DSET2!(p,x,y); }
+ /* ADD* */ 0x50 => { WPOPD!(y); WPOPD!(x); WPSHD!(x.wrapping_add(y)); }
+ /* SUB* */ 0x51 => { WPOPD!(y); WPOPD!(x); WPSHD!(x.wrapping_sub(y)); }
+ /* INC* */ 0x52 => { WPOPD!(x); WPSHD!(x.wrapping_add(1)); }
+ /* DEC* */ 0x53 => { WPOPD!(x); WPSHD!(x.wrapping_sub(1)); }
+ /* LTH* */ 0x54 => { WPOPD!(y); WPOPD!(x); WPSHB!(x < y); }
+ /* GTH* */ 0x55 => { WPOPD!(y); WPOPD!(x); WPSHB!(x > y); }
+ /* EQU* */ 0x56 => { WPOPD!(y); WPOPD!(x); WPSHB!(x==y); }
+ /* NQK* */ 0x57 => { WGETD!(y); WGETDN!(x); WPSHB!(x!=y); }
+ /* SHL* */ 0x58 => { WPOP1!(y); WPOPD!(x); WPSHD!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHR* */ 0x59 => { WPOP1!(y); WPOPD!(x); WPSHD!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROL* */ 0x5A => { WPOP1!(y); WPOPD!(x); WPSHD!(x.rotate_left(y as u32)); }
+ /* ROR* */ 0x5B => { WPOP1!(y); WPOPD!(x); WPSHD!(x.rotate_right(y as u32)); }
+ /* IOR* */ 0x5C => { WPOP2!(x,y); math!(WGETV!(2); IOR x); math!(WGETV!(1); IOR y); }
+ /* XOR* */ 0x5D => { WPOP2!(x,y); math!(WGETV!(2); XOR x); math!(WGETV!(1); XOR y); }
+ /* AND* */ 0x5E => { WPOP2!(x,y); math!(WGETV!(2); AND x); math!(WGETV!(1); AND y); }
+ /* NOT* */ 0x5F => { math!(WGETV!(2); NOT); math!(WGETV!(1); NOT); }
+
+ /* DB2 */ 0x60 => { if debug { return Some(Signal::Debug(Debug::Debug2)); } }
+ /* PSH*: */ 0x61 => { MLIT2!(x,y); WPSH2!(x,y); }
+ /* POP*: */ 0x62 => { math!(self.core.mem.pc; ADD 2); }
+ /* CPY*: */ 0x63 => { MLIT2!(x,y); WPSH2!(x,y); RPSH2!(x,y); }
+ /* DUP*: */ 0x64 => { MLIT2!(x,y); WPSH2!(x,y); WPSH2!(x,y); }
+ /* OVR*: */ 0x65 => { MLIT2!(c,d); WGET2!(a,b); WPSH2!(c,d); WPSH2!(a,b); }
+ /* SWP*: */ 0x66 => { MLIT2!(c,d); WPOP2!(a,b); WPSH2!(c,d); WPSH2!(a,b); }
+ /* ROT*: */ 0x67 => { MLIT2!(e,f); WPOP2!(c,d); WPOP2!(a,b); WPSH2!(c,d); WPSH2!(e,f); WPSH2!(a,b); }
+ /* JMP*: */ 0x68 => { MLITD!(a); PCSET!(a); }
+ /* JMS*: */ 0x69 => { MLITD!(a); RPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCN*: */ 0x6A => { MLITD!(a); WPOPD!(t); if t!=0 {PCSET!(a)}; }
+ /* JCS*: */ 0x6B => { MLITD!(a); WPOPD!(t); if t!=0 {RPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDA*: */ 0x6C => { MLITD!(a); WPSH2!(MEM!(a),MEMN!(a)); }
+ /* STA*: */ 0x6D => { MLITD!(a); WPOP2!(x,y); MEM!(a)=x; MEMN!(a)=y; }
+ /* LDD*: */ 0x6E => { MLIT1!(p); WPSH2!(DEV!(p),DEVN!(p)); }
+ /* STD*: */ 0x6F => { MLIT1!(p); WPOP2!(x,y); DSET2!(p,x,y); }
+ /* ADD*: */ 0x70 => { MLITD!(y); WPOPD!(x); WPSHD!(x.wrapping_add(y)); }
+ /* SUB*: */ 0x71 => { MLITD!(y); WPOPD!(x); WPSHD!(x.wrapping_sub(y)); }
+ /* INC*: */ 0x72 => { MLITD!(x); WPSHD!(x.wrapping_add(1)); }
+ /* DEC*: */ 0x73 => { MLITD!(x); WPSHD!(x.wrapping_sub(1)); }
+ /* LTH*: */ 0x74 => { MLITD!(y); WPOPD!(x); WPSHB!(x < y); }
+ /* GTH*: */ 0x75 => { MLITD!(y); WPOPD!(x); WPSHB!(x > y); }
+ /* EQU*: */ 0x76 => { MLITD!(y); WPOPD!(x); WPSHB!(x==y); }
+ /* NQK*: */ 0x77 => { MLITD!(y); WGETD!(x); WPSHD!(y); WPSHB!(x!=y); }
+ /* SHL*: */ 0x78 => { MLIT1!(y); WPOPD!(x); WPSHD!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHR*: */ 0x79 => { MLIT1!(y); WPOPD!(x); WPSHD!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROL*: */ 0x7A => { MLIT1!(y); WPOPD!(x); WPSHD!(x.rotate_left(y as u32)); }
+ /* ROR*: */ 0x7B => { MLIT1!(y); WPOPD!(x); WPSHD!(x.rotate_right(y as u32)); }
+ /* IOR*: */ 0x7C => { MLIT2!(x,y); math!(WGETV!(2); IOR x); math!(WGETV!(1); IOR y); }
+ /* XOR*: */ 0x7D => { MLIT2!(x,y); math!(WGETV!(2); XOR x); math!(WGETV!(1); XOR y); }
+ /* AND*: */ 0x7E => { MLIT2!(x,y); math!(WGETV!(2); AND x); math!(WGETV!(1); AND y); }
+ /* NOT*: */ 0x7F => { MLITD!(x); WPSHD!(x.not()); }
+
+ /* DB3 */ 0x80 => { if debug { return Some(Signal::Debug(Debug::Debug3)); } }
+ /* PSHr */ 0x81 => { WPOP1!(x); RPSH1!(x); }
+ /* POPr */ 0x82 => { math!(self.core.rst.sp; SUB 1); }
+ /* CPYr */ 0x83 => { WGET1!(x); RPSH1!(x); }
+ /* DUPr */ 0x84 => { RGET1!(x); RPSH1!(x); }
+ /* OVRr */ 0x85 => { RGET1N!(x); RPSH1!(x); }
+ /* SWPr */ 0x86 => { RPOP1!(y); RPOP1!(x); RPSH1!(y); RPSH1!(x); }
+ /* ROTr */ 0x87 => { RPOP1!(z); RPOP1!(y); RPOP1!(x); RPSH1!(y); RPSH1!(z); RPSH1!(x); }
+ /* JMPr */ 0x88 => { RPOPD!(a); PCSET!(a); }
+ /* JMSr */ 0x89 => { RPOPD!(a); WPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCNr */ 0x8A => { RPOPD!(a); RPOP1!(t); if t!=0 {PCSET!(a)}; }
+ /* JCSr */ 0x8B => { RPOPD!(a); RPOP1!(t); if t!=0 {WPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDAr */ 0x8C => { RPOPD!(a); RPSH1!(MEM!(a)); }
+ /* STAr */ 0x8D => { RPOPD!(a); RPOP1!(x); MEM!(a)=x; }
+ /* LDDr */ 0x8E => { RPOP1!(p); RPSH1!(DEV!(p)); }
+ /* STDr */ 0x8F => { RPOP1!(p); RPOP1!(x); DSET1!(p,x); }
+ /* ADDr */ 0x90 => { RPOP1!(y); math!(RGETV!(1); ADD y); }
+ /* SUBr */ 0x91 => { RPOP1!(y); math!(RGETV!(1); SUB y); }
+ /* INCr */ 0x92 => { math!(RGETV!(1); ADD 1); }
+ /* DECr */ 0x93 => { math!(RGETV!(1); SUB 1); }
+ /* LTHr */ 0x94 => { RPOP1!(y); RPOP1!(x); RPSHB!(x < y); }
+ /* GTHr */ 0x95 => { RPOP1!(y); RPOP1!(x); RPSHB!(x > y); }
+ /* EQUr */ 0x96 => { RPOP1!(y); RPOP1!(x); RPSHB!(x==y); }
+ /* NQKr */ 0x97 => { RGET2!(x,y); RPSHB!(x!=y); }
+ /* SHLr */ 0x98 => { RPOP1!(y); RPOP1!(x); RPSH1!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHRr */ 0x99 => { RPOP1!(y); RPOP1!(x); RPSH1!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROLr */ 0x9A => { RPOP1!(y); RPOP1!(x); RPSH1!(x.rotate_left(y as u32)); }
+ /* RORr */ 0x9B => { RPOP1!(y); RPOP1!(x); RPSH1!(x.rotate_right(y as u32)); }
+ /* IORr */ 0x9C => { RPOP1!(y); math!(RGETV!(1); IOR y); }
+ /* XORr */ 0x9D => { RPOP1!(y); math!(RGETV!(1); XOR y); }
+ /* ANDr */ 0x9E => { RPOP1!(y); math!(RGETV!(1); AND y); }
+ /* NOTr */ 0x9F => { math!(RGETV!(1); NOT); }
+
+ /* DB4 */ 0xA0 => { if debug { return Some(Signal::Debug(Debug::Debug4)); } }
+ /* PSHr: */ 0xA1 => { MLIT1!(x); RPSH1!(x); }
+ /* POPr: */ 0xA2 => { math!(self.core.mem.pc; ADD 1); }
+ /* CPYr: */ 0xA3 => { MLIT1!(x); RPSH1!(x); WPSH1!(x); }
+ /* DUPr: */ 0xA4 => { MLIT1!(x); RPSH1!(x); RPSH1!(x); }
+ /* OVRr: */ 0xA5 => { MLIT1!(y); RGET1!(x); RPSH1!(y); RPSH1!(x); }
+ /* SWPr: */ 0xA6 => { MLIT1!(y); RPOP1!(x); RPSH1!(y); RPSH1!(x); }
+ /* ROTr: */ 0xA7 => { MLIT1!(z); RPOP1!(y); RPOP1!(x); RPSH1!(y); RPSH1!(z); RPSH1!(x); }
+ /* JMPr: */ 0xA8 => { MLITD!(a); PCSET!(a); }
+ /* JMSr: */ 0xA9 => { MLITD!(a); WPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCNr: */ 0xAA => { MLITD!(a); RPOP1!(t); if t!=0 {PCSET!(a)}; }
+ /* JCSr: */ 0xAB => { MLITD!(a); RPOP1!(t); if t!=0 {WPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDAr: */ 0xAC => { MLITD!(a); RPSH1!(MEM!(a)); }
+ /* STAr: */ 0xAD => { MLITD!(a); RPOP1!(x); MEM!(a)=x; }
+ /* LDDr: */ 0xAE => { MLIT1!(p); RPSH1!(DEV!(p)); }
+ /* STDr: */ 0xAF => { MLIT1!(p); RPOP1!(x); DSET1!(p,x); }
+ /* ADDr: */ 0xB0 => { MLIT1!(y); math!(RGETV!(1); ADD y); }
+ /* SUBr: */ 0xB1 => { MLIT1!(y); math!(RGETV!(1); SUB y); }
+ /* INCr: */ 0xB2 => { MLIT1!(x); RPSH1!(x.wrapping_add(1)); }
+ /* DECr: */ 0xB3 => { MLIT1!(x); RPSH1!(x.wrapping_sub(1)); }
+ /* LTHr: */ 0xB4 => { MLIT1!(y); RPOP1!(x); RPSHB!(x < y); }
+ /* GTHr: */ 0xB5 => { MLIT1!(y); RPOP1!(x); RPSHB!(x > y); }
+ /* EQUr: */ 0xB6 => { MLIT1!(y); RPOP1!(x); RPSHB!(x==y); }
+ /* NQKr: */ 0xB7 => { MLIT1!(y); RGET1!(x); RPSH1!(y); RPSHB!(x!=y); }
+ /* SHLr: */ 0xB8 => { MLIT1!(y); RPOP1!(x); RPSH1!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHRr: */ 0xB9 => { MLIT1!(y); RPOP1!(x); RPSH1!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROLr: */ 0xBA => { MLIT1!(y); RPOP1!(x); RPSH1!(x.rotate_left(y as u32)); }
+ /* RORr: */ 0xBB => { MLIT1!(y); RPOP1!(x); RPSH1!(x.rotate_right(y as u32)); }
+ /* IORr: */ 0xBC => { MLIT1!(y); math!(RGETV!(1); IOR y); }
+ /* XORr: */ 0xBD => { MLIT1!(y); math!(RGETV!(1); XOR y); }
+ /* ANDr: */ 0xBE => { MLIT1!(y); math!(RGETV!(1); AND y); }
+ /* NOTr: */ 0xBF => { MLIT1!(x); RPSH1!(x.not()); }
+
+ /* DB5 */ 0xC0 => { if debug { return Some(Signal::Debug(Debug::Debug5)); } }
+ /* PSHr* */ 0xC1 => { WPOP2!(x,y); RPSH2!(x,y); }
+ /* POPr* */ 0xC2 => { math!(self.core.rst.sp; SUB 2); }
+ /* CPYr* */ 0xC3 => { WGET2!(x,y); RPSH2!(x,y); }
+ /* DUPr* */ 0xC4 => { RGET2!(x,y); RPSH2!(x,y); }
+ /* OVRr* */ 0xC5 => { RGET2N!(x,y); RPSH2!(x,y); }
+ /* SWPr* */ 0xC6 => { RPOP2!(c,d); RPOP2!(a,b); RPSH2!(c,d); RPSH2!(a,b); }
+ /* ROTr* */ 0xC7 => { RPOP2!(e,f); RPOP2!(c,d); RPOP2!(a,b); RPSH2!(c,d); RPSH2!(e,f); RPSH2!(a,b); }
+ /* JMPr* */ 0xC8 => { RPOPD!(a); PCSET!(a); }
+ /* JMSr* */ 0xC9 => { RPOPD!(a); WPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCNr* */ 0xCA => { RPOPD!(a); RPOPD!(t); if t!=0 {PCSET!(a)}; }
+ /* JCSr* */ 0xCB => { RPOPD!(a); RPOPD!(t); if t!=0 {WPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDAr* */ 0xCC => { RPOPD!(a); RPSH2!(MEM!(a),MEMN!(a)); }
+ /* STAr* */ 0xCD => { RPOPD!(a); RPOP2!(x,y); MEM!(a)=x; MEMN!(a)=y; }
+ /* LDDr* */ 0xCE => { RPOP1!(p); RPSH2!(DEV!(p),DEVN!(p)); }
+ /* STDr* */ 0xCF => { RPOP1!(p); RPOP2!(x,y); DSET2!(p,x,y); }
+ /* ADDr* */ 0xD0 => { RPOPD!(y); RPOPD!(x); RPSHD!(x.wrapping_add(y)); }
+ /* SUBr* */ 0xD1 => { RPOPD!(y); RPOPD!(x); RPSHD!(x.wrapping_sub(y)); }
+ /* INCr* */ 0xD2 => { RPOPD!(x); RPSHD!(x.wrapping_add(1)); }
+ /* DECr* */ 0xD3 => { RPOPD!(x); RPSHD!(x.wrapping_sub(1)); }
+ /* LTHr* */ 0xD4 => { RPOPD!(y); RPOPD!(x); RPSHB!(x < y); }
+ /* GTHr* */ 0xD5 => { RPOPD!(y); RPOPD!(x); RPSHB!(x > y); }
+ /* EQUr* */ 0xD6 => { RPOPD!(y); RPOPD!(x); RPSHB!(x==y); }
+ /* NQKr* */ 0xD7 => { RGETD!(y); RGETDN!(x); RPSHB!(x!=y); }
+ /* SHLr* */ 0xD8 => { RPOP1!(y); RPOPD!(x); RPSHD!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHRr* */ 0xD9 => { RPOP1!(y); RPOPD!(x); RPSHD!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROLr* */ 0xDA => { RPOP1!(y); RPOPD!(x); RPSHD!(x.rotate_left(y as u32)); }
+ /* RORr* */ 0xDB => { RPOP1!(y); RPOPD!(x); RPSHD!(x.rotate_right(y as u32)); }
+ /* IORr* */ 0xDC => { RPOP2!(x,y); math!(RGETV!(2); IOR x); math!(RGETV!(1); IOR y); }
+ /* XORr* */ 0xDD => { RPOP2!(x,y); math!(RGETV!(2); XOR x); math!(RGETV!(1); XOR y); }
+ /* ANDr* */ 0xDE => { RPOP2!(x,y); math!(RGETV!(2); AND x); math!(RGETV!(1); AND y); }
+ /* NOTr* */ 0xDF => { math!(RGETV!(2); NOT); math!(RGETV!(1); NOT); }
+
+ /* DB6 */ 0xE0 => { if debug { return Some(Signal::Debug(Debug::Debug6)); } }
+ /* PSHr*: */ 0xE1 => { MLIT2!(x,y); RPSH2!(x,y); }
+ /* POPr*: */ 0xE2 => { math!(self.core.mem.pc; ADD 2); }
+ /* CPYr*: */ 0xE3 => { MLIT2!(x,y); RPSH2!(x,y); WPSH2!(x,y); }
+ /* DUPr*: */ 0xE4 => { MLIT2!(x,y); RPSH2!(x,y); RPSH2!(x,y); }
+ /* OVRr*: */ 0xE5 => { MLIT2!(c,d); RGET2!(a,b); RPSH2!(c,d); RPSH2!(a,b); }
+ /* SWPr*: */ 0xE6 => { MLIT2!(c,d); RPOP2!(a,b); RPSH2!(c,d); RPSH2!(a,b); }
+ /* ROTr*: */ 0xE7 => { MLIT2!(e,f); RPOP2!(c,d); RPOP2!(a,b); RPSH2!(c,d); RPSH2!(e,f); RPSH2!(a,b); }
+ /* JMPr*: */ 0xE8 => { MLITD!(a); PCSET!(a); }
+ /* JMSr*: */ 0xE9 => { MLITD!(a); WPSHD!(self.core.mem.pc); PCSET!(a); }
+ /* JCNr*: */ 0xEA => { MLITD!(a); RPOPD!(t); if t!=0 {PCSET!(a)}; }
+ /* JCSr*: */ 0xEB => { MLITD!(a); RPOPD!(t); if t!=0 {WPSHD!(self.core.mem.pc); PCSET!(a)}; }
+ /* LDAr*: */ 0xEC => { MLITD!(a); RPSH2!(MEM!(a),MEMN!(a)); }
+ /* STAr*: */ 0xED => { MLITD!(a); RPOP2!(x,y); MEM!(a)=x; MEMN!(a)=y; }
+ /* LDDr*: */ 0xEE => { MLIT1!(p); RPSH2!(DEV!(p),DEVN!(p)); }
+ /* STDr*: */ 0xEF => { MLIT1!(p); RPOP2!(x,y); DSET2!(p,x,y); }
+ /* ADDr*: */ 0xF0 => { MLITD!(y); RPOPD!(x); RPSHD!(x.wrapping_add(y)); }
+ /* SUBr*: */ 0xF1 => { MLITD!(y); RPOPD!(x); RPSHD!(x.wrapping_sub(y)); }
+ /* INCr*: */ 0xF2 => { MLITD!(x); RPSHD!(x.wrapping_add(1)); }
+ /* DECr*: */ 0xF3 => { MLITD!(x); RPSHD!(x.wrapping_sub(1)); }
+ /* LTHr*: */ 0xF4 => { MLITD!(y); RPOPD!(x); RPSHB!(x < y); }
+ /* GTHr*: */ 0xF5 => { MLITD!(y); RPOPD!(x); RPSHB!(x > y); }
+ /* EQUr*: */ 0xF6 => { MLITD!(y); RPOPD!(x); RPSHB!(x==y); }
+ /* NQKr*: */ 0xF7 => { MLITD!(y); RGETD!(x); RPSHD!(y); RPSHB!(x!=y); }
+ /* SHLr*: */ 0xF8 => { MLIT1!(y); RPOPD!(x); RPSHD!(x.checked_shl(y as u32).unwrap_or(0)); }
+ /* SHRr*: */ 0xF9 => { MLIT1!(y); RPOPD!(x); RPSHD!(x.checked_shr(y as u32).unwrap_or(0)); }
+ /* ROLr*: */ 0xFA => { MLIT1!(y); RPOPD!(x); RPSHD!(x.rotate_left(y as u32)); }
+ /* RORr*: */ 0xFB => { MLIT1!(y); RPOPD!(x); RPSHD!(x.rotate_right(y as u32)); }
+ /* IORr*: */ 0xFC => { MLIT2!(x,y); math!(RGETV!(2); IOR x); math!(RGETV!(1); IOR y); }
+ /* XORr*: */ 0xFD => { MLIT2!(x,y); math!(RGETV!(2); XOR x); math!(RGETV!(1); XOR y); }
+ /* ANDr*: */ 0xFE => { MLIT2!(x,y); math!(RGETV!(2); AND x); math!(RGETV!(1); AND y); }
+ /* NOTr*: */ 0xFF => { MLITD!(x); RPSHD!(x.not()); }
+ }
+ }
+
+ return None;
+ }
+}
diff --git a/src/components/program_memory.rs b/src/components/program_memory.rs
new file mode 100644
index 0000000..8770556
--- /dev/null
+++ b/src/components/program_memory.rs
@@ -0,0 +1,66 @@
+use crate::*;
+
+
+pub struct ProgramMemory {
+ pub mem: [u8; 65536],
+ pub pc: u16,
+}
+
+impl ProgramMemory {
+ pub fn new() -> Self {
+ Self {
+ mem: [0; 65536],
+ pc: 0,
+ }
+ }
+
+ /// Load a program into memory, resetting the program counter.
+ pub fn load_program(&mut self, bytecode: &[u8]) {
+ let length = std::cmp::min(bytecode.len(), 65536);
+ self.mem[..length].copy_from_slice(&bytecode[..length]);
+ self.mem[length..65536].fill(0);
+ self.pc = 0;
+ }
+
+ pub fn metadata(&self) -> Option<Metadata> {
+ Metadata::from(self.mem.as_slice())
+ }
+
+ /// Read a u8 from a memory address.
+ pub fn read_u8(&self, addr: u16) -> u8 {
+ self.mem[addr as usize]
+ }
+
+ /// Write a u8 to a memory address.
+ pub fn write_u8(&mut self, val: u8, addr: u16) {
+ self.mem[addr as usize] = val;
+ }
+
+ /// Read a u8 using the program counter.
+ pub fn read_u8_next(&mut self) -> u8 {
+ let byte = self.mem[self.pc as usize];
+ self.pc = self.pc.wrapping_add(1);
+ byte
+ }
+
+ /// Read a u16 from a memory address.
+ pub fn read_u16(&self, addr: u16) -> u16 {
+ let byte_high = self.read_u8(addr);
+ let byte_low = self.read_u8(addr.wrapping_add(1));
+ u16::from_be_bytes([byte_high, byte_low])
+ }
+
+ /// Write a u16 to a memory address.
+ pub fn write_u16(&mut self, val: u16, addr: u16) {
+ let [byte_high, byte_low] = val.to_be_bytes();
+ self.write_u8(byte_high, addr);
+ self.write_u8(byte_low, addr.wrapping_add(1));
+ }
+
+ /// Read a u8 using the program counter.
+ pub fn read_u16_next(&mut self) -> u16 {
+ let byte_high = self.read_u8_next();
+ let byte_low = self.read_u8_next();
+ u16::from_be_bytes([byte_high, byte_low])
+ }
+}
diff --git a/src/components/stack.rs b/src/components/stack.rs
new file mode 100644
index 0000000..3a5bdd7
--- /dev/null
+++ b/src/components/stack.rs
@@ -0,0 +1,43 @@
+pub struct Stack {
+ pub mem: [u8; 256],
+ pub sp: u8,
+}
+
+impl Stack {
+ pub fn new() -> Self {
+ Self {
+ mem: [0; 256],
+ sp: 0,
+ }
+ }
+
+ // Push a u8 to the stack.
+ pub fn push_u8(&mut self, val: u8) {
+ self.mem[self.sp as usize] = val;
+ self.sp = self.sp.wrapping_add(1);
+ }
+
+ // Pop a u8 from the stack.
+ pub fn pop_u8(&mut self) -> u8 {
+ self.sp = self.sp.wrapping_sub(1);
+ self.mem[self.sp as usize]
+ }
+
+ // Push a u16 to the stack.
+ pub fn push_u16(&mut self, val: u16) {
+ let [high, low] = u16::to_be_bytes(val);
+ self.mem[self.sp as usize] = high;
+ self.sp = self.sp.wrapping_add(1);
+ self.mem[self.sp as usize] = low;
+ self.sp = self.sp.wrapping_add(1);
+ }
+
+ // Pop a u16 from the stack.
+ pub fn pop_u16(&mut self) -> u16 {
+ self.sp = self.sp.wrapping_sub(1);
+ let low = self.mem[self.sp as usize];
+ self.sp = self.sp.wrapping_sub(1);
+ let high = self.mem[self.sp as usize];
+ u16::from_be_bytes([high, low])
+ }
+}
diff --git a/src/core.rs b/src/core.rs
new file mode 100644
index 0000000..b4b7520
--- /dev/null
+++ b/src/core.rs
@@ -0,0 +1,31 @@
+use crate::*;
+
+
+pub struct BedrockCore {
+ pub mem: ProgramMemory,
+ pub wst: Stack,
+ pub rst: Stack,
+ pub cycle: usize,
+}
+
+impl BedrockCore {
+ pub fn new() -> Self {
+ Self {
+ mem: ProgramMemory::new(),
+ wst: Stack::new(),
+ rst: Stack::new(),
+ cycle: 0,
+ }
+ }
+
+ pub fn with_device_bus<DB>(self, dev: DB) -> BedrockEmulator<DB> {
+ BedrockEmulator { core: self, dev }
+ }
+
+ pub fn reset(&mut self) {
+ self.cycle = 0;
+ self.mem.pc = 0;
+ self.wst.sp = 0;
+ self.rst.sp = 0;
+ }
+}
diff --git a/src/emulator.rs b/src/emulator.rs
new file mode 100644
index 0000000..d5407eb
--- /dev/null
+++ b/src/emulator.rs
@@ -0,0 +1,21 @@
+use crate::*;
+
+
+pub struct BedrockEmulator<DB> {
+ pub core: BedrockCore,
+ pub dev: DB,
+}
+
+impl<DB: DeviceBus> BedrockEmulator<DB> {
+ pub fn new(device_bus: DB) -> Self {
+ Self {
+ core: BedrockCore::new(),
+ dev: device_bus,
+ }
+ }
+
+ pub fn reset(&mut self) {
+ self.core.reset();
+ self.dev.reset();
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..6324e3b
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,29 @@
+mod components;
+mod core;
+mod emulator;
+mod signal;
+
+pub use components::*;
+pub use core::*;
+pub use emulator::*;
+pub use signal::*;
+
+
+pub mod macros {
+ #[macro_export] macro_rules! read_hh { ($v:expr) => { ($v>>24) as u8 }; }
+ #[macro_export] macro_rules! read_hl { ($v:expr) => { ($v>>16) as u8 }; }
+ #[macro_export] macro_rules! read_lh { ($v:expr) => { ($v>>8) as u8 }; }
+ #[macro_export] macro_rules! read_ll { ($v:expr) => { $v as u8 }; }
+ #[macro_export] macro_rules! read_h { ($v:expr) => { ($v>>8) as u8 }; }
+ #[macro_export] macro_rules! read_l { ($v:expr) => { $v as u8 }; }
+ #[macro_export] macro_rules! read_b { ($v:expr) => { 0u8.wrapping_sub($v as u8) }; }
+
+ #[macro_export] macro_rules! write_hh { ($v:expr, $high:expr) => { $v = $v & 0x00ffffff | (($high as u32) << 24) }; }
+ #[macro_export] macro_rules! write_hl { ($v:expr, $low:expr) => { $v = $v & 0xff00ffff | (($low as u32) << 16) }; }
+ #[macro_export] macro_rules! write_lh { ($v:expr, $high:expr) => { $v = $v & 0xffff00ff | (($high as u32) << 8) }; }
+ #[macro_export] macro_rules! write_ll { ($v:expr, $low:expr) => { $v = $v & 0xffffff00 | ($low as u32) }; }
+ #[macro_export] macro_rules! write_h { ($v:expr, $high:expr) => { $v = $v & 0x00ff | (($high as u16) << 8) }; }
+ #[macro_export] macro_rules! write_l { ($v:expr, $low:expr) => { $v = $v & 0xff00 | ($low as u16) }; }
+
+ #[macro_export] macro_rules! test_bit { ($v:expr, $mask:expr) => { $v & $mask != 0 }; }
+}
diff --git a/src/signal.rs b/src/signal.rs
new file mode 100644
index 0000000..6143601
--- /dev/null
+++ b/src/signal.rs
@@ -0,0 +1,19 @@
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum Signal {
+ Break,
+ Debug(Debug),
+ Sleep,
+ Fork,
+ Reset,
+ Halt,
+}
+
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum Debug {
+ Debug1,
+ Debug2,
+ Debug3,
+ Debug4,
+ Debug5,
+ Debug6,
+}