use crate::*; pub struct HeadlessEmulator { pub br: BedrockEmulator, pub debug: DebugState, } impl HeadlessEmulator { pub fn new(config: &EmulatorConfig, debug: bool) -> Self { Self { br: BedrockEmulator::new(HeadlessDeviceBus::new(config)), debug: DebugState::new(debug, 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) -> ! { 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.stream.flush(); info!("Program halted, exiting."); self.debug.debug_summary(&self.br.core); std::process::exit(0); } Some(Signal::Debug(Debug::Debug1)) => { self.debug.debug_summary(&self.br.core); } _ => (), } } } } pub struct HeadlessDeviceBus { pub system: SystemDevice, pub memory: MemoryDevice, pub math: MathDevice, pub clock: ClockDevice, pub stream: StreamDevice, pub file: FileDevice, } impl HeadlessDeviceBus { pub fn new(config: &EmulatorConfig) -> Self { Self { system: SystemDevice::new(0b1111_0000_1010_0000), memory: MemoryDevice::new(), math: MathDevice::new(), clock: ClockDevice::new(), stream: StreamDevice::new(&config), file: FileDevice::new(), } } } impl DeviceBus for HeadlessDeviceBus { fn read(&mut self, port: u8) -> u8 { match port & 0xf0 { 0x00 => self.system.read(port & 0x0f), 0x10 => self.memory.read(port & 0x0f), 0x20 => self.math .read(port & 0x0f), 0x30 => self.clock .read(port & 0x0f), 0x80 => self.stream.read(port & 0x0f), 0xa0 => self.file .read(port & 0x0f), _ => 0 } } fn write(&mut self, port: u8, value: u8) -> Option { match port & 0xf0 { 0x00 => self.system.write(port & 0x0f, value), 0x10 => self.memory.write(port & 0x0f, value), 0x20 => self.math .write(port & 0x0f, value), 0x30 => self.clock .write(port & 0x0f, value), 0x80 => self.stream.write(port & 0x0f, value), 0xa0 => self.file .write(port & 0x0f, value), _ => None } } fn wake(&mut self) -> bool { macro_rules! rouse { ($id:expr, $dev:ident) => { let is_eligible = test_bit!(self.system.wakers, 0x8000 >> $id); if is_eligible && self.$dev.wake() { self.system.waker = $id; self.system.asleep = false; return true; } }; } rouse!(0xa, file ); rouse!(0x8, stream); rouse!(0x3, clock ); return false; } }