use crate::*; use bedrock_core::*; pub struct HeadlessDeviceBus { pub sys: SystemDevice, pub mem: MemoryDevice, pub mat: MathDevice, pub clk: ClockDevice, pub loc: LocalDevice, pub rem: RemoteDevice, pub fs1: FileDevice, pub fs2: FileDevice, } impl HeadlessDeviceBus { pub fn new(config: &EmulatorConfig) -> Self { Self { sys: SystemDevice::new(), mem: MemoryDevice::new(), mat: MathDevice::new(), clk: ClockDevice::new(), loc: LocalDevice::new(config), rem: RemoteDevice::new(), fs1: FileDevice::new(), fs2: FileDevice::new(), } } } impl DeviceBus for HeadlessDeviceBus { fn read(&mut self, port: u8) -> u8 { match port & 0xf0 { 0x00 => self.sys.read(port & 0x0f), 0x10 => self.mem.read(port & 0x0f), 0x20 => self.mat.read(port & 0x0f), 0x30 => self.clk.read(port & 0x0f), 0x80 => self.loc.read(port & 0x0f), 0x90 => self.rem.read(port & 0x0f), 0xa0 => self.fs1.read(port & 0x0f), 0xb0 => self.fs2.read(port & 0x0f), _ => 0 } } fn write(&mut self, port: u8, value: u8) -> Option { match port & 0xf0 { 0x00 => self.sys.write(port & 0x0f, value), 0x10 => self.mem.write(port & 0x0f, value), 0x20 => self.mat.write(port & 0x0f, value), 0x30 => self.clk.write(port & 0x0f, value), 0x80 => self.loc.write(port & 0x0f, value), 0x90 => self.rem.write(port & 0x0f, value), 0xa0 => self.fs1.write(port & 0x0f, value), 0xb0 => self.fs2.write(port & 0x0f, value), _ => None } } fn wake(&mut self) -> bool { macro_rules! rouse { ($id:expr, $dev:ident) => { if self.sys.can_wake($id) && self.$dev.wake() { self.sys.wake_id = $id; self.sys.asleep = false; return true; } }; } rouse!(0xb, fs2); rouse!(0xa, fs1); rouse!(0x9, rem); rouse!(0x8, loc); rouse!(0x3, clk); rouse!(0x2, mat); rouse!(0x1, mem); rouse!(0x0, sys); return false; } } pub struct HeadlessEmulator { pub br: BedrockEmulator, pub debug: DebugState, } impl HeadlessEmulator { pub fn new(config: &EmulatorConfig, debug: bool, verbose: bool) -> Self { Self { br: BedrockEmulator::new(HeadlessDeviceBus::new(config)), debug: DebugState::new(debug, verbose, config.symbols_path.as_ref()), } } pub fn load_program(&mut self, bytecode: &[u8]) { self.br.core.mem.load_program(bytecode); } pub fn run(&mut self, debug: bool) -> EmulatorSignal { loop { match self.br.evaluate(BATCH_SIZE, self.debug.enabled) { Some(Signal::Fork) => { self.br.core.mem.pc = 0; self.br.core.wst.sp = 0; self.br.core.rst.sp = 0; } Some(Signal::Sleep) => loop { if self.br.dev.wake() { break; } std::thread::sleep(MIN_TICK_DURATION); } Some(Signal::Halt) => { self.br.dev.loc.flush(); self.debug.info("Program halted, exiting."); self.debug.debug_summary(&self.br.core); return EmulatorSignal::Halt; } Some(Signal::Debug1) => if debug { self.debug.debug_summary(&self.br.core); } _ => (), } } } }