diff options
Diffstat (limited to 'src/emulators/dynamic_emulator.rs')
-rw-r--r-- | src/emulators/dynamic_emulator.rs | 170 |
1 files changed, 170 insertions, 0 deletions
diff --git a/src/emulators/dynamic_emulator.rs b/src/emulators/dynamic_emulator.rs new file mode 100644 index 0000000..b5126d0 --- /dev/null +++ b/src/emulators/dynamic_emulator.rs @@ -0,0 +1,170 @@ +use crate::*; + + +pub struct DynamicEmulator { + pub br: BedrockEmulator<DynamicDeviceBus>, + pub debug: DebugState, +} + + +impl DynamicEmulator { + pub fn new(config: &EmulatorConfig, debug: bool) -> Self { + Self { + br: BedrockEmulator::new(DynamicDeviceBus::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); + } + _ => (), + } + if self.br.dev.input.accessed || self.br.dev.screen.accessed { + return; + } + } + } + + pub fn to_graphical(self, config: EmulatorConfig) -> GraphicalEmulator { + GraphicalEmulator { + br: BedrockEmulator { + core: self.br.core, + dev: GraphicalDeviceBus { + system: self.br.dev.system, + memory: self.br.dev.memory, + math: self.br.dev.math, + clock: self.br.dev.clock, + input: self.br.dev.input, + screen: self.br.dev.screen, + stream: self.br.dev.stream, + file: self.br.dev.file, + } + }, + debug: self.debug, + + fullscreen: config.fullscreen, + scale: config.zoom.into(), + show_override_palette: config.override_palette.is_some(), + render_mark: Instant::now(), + + config, + } + } + + pub fn to_headless(self) -> HeadlessEmulator { + HeadlessEmulator { + br: BedrockEmulator { + core: self.br.core, + dev: HeadlessDeviceBus { + system: self.br.dev.system, + memory: self.br.dev.memory, + math: self.br.dev.math, + clock: self.br.dev.clock, + stream: self.br.dev.stream, + file: self.br.dev.file, + } + }, + debug: self.debug, + } + } +} + + +pub struct DynamicDeviceBus { + pub system: SystemDevice, + pub memory: MemoryDevice, + pub math: MathDevice, + pub clock: ClockDevice, + pub input: InputDevice, + pub screen: ScreenDevice, + pub stream: StreamDevice, + pub file: FileDevice, +} + +impl DynamicDeviceBus { + pub fn new(config: &EmulatorConfig) -> Self { + Self { + system: SystemDevice::new(0b1111_1100_1010_0000), + memory: MemoryDevice::new(), + math: MathDevice::new(), + clock: ClockDevice::new(), + input: InputDevice::new(), + screen: ScreenDevice::new(&config), + stream: StreamDevice::new(&config), + file: FileDevice::new(), + } + } +} + + +impl DeviceBus for DynamicDeviceBus { + 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), + 0x40 => self.input .read(port & 0x0f), + 0x50 => self.screen.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<Signal> { + 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), + 0x40 => { self.input .write(port & 0x0f, value); Some(Signal::Interrupt) } + 0x50 => { self.screen.write(port & 0x0f, value); Some(Signal::Interrupt) } + 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!(0x5, screen); + rouse!(0x4, input ); + rouse!(0x3, clock ); + return false; + } +} |