diff options
Diffstat (limited to 'src/main.rs')
-rwxr-xr-x | src/main.rs | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/src/main.rs b/src/main.rs new file mode 100755 index 0000000..1279ceb --- /dev/null +++ b/src/main.rs @@ -0,0 +1,199 @@ +#![allow(clippy::new_without_default)] +#![allow(clippy::unusual_byte_groupings)] +use std::{ + collections::HashMap, + sync::mpsc::TryRecvError, + time::{Duration, Instant}, +}; + +use armv4t_emu::{reg, Cpu, Exception, Memory, Mode}; + +use crate::mem::{Ctsi, Event, GenSIO, GensioState, Mem, Uif}; + +mod currentcontroller; +mod mem; + +use currentcontroller::CurrentController; + +fn main() { + simple_logger::SimpleLogger::new() + .with_level(log::LevelFilter::Info) + .with_module_level("nokia3310emu::currentcontroller", log::LevelFilter::Trace) + //.with_module_level("pc_all", log::LevelFilter::Trace) + .with_module_level("eec46_jmp", log::LevelFilter::Trace) + .with_module_level("armv4t_emu", log::LevelFilter::Info) + .env() + .with_colors(true) + .init() + .unwrap(); + + // Channel so the memory controller can tell us when memory mapped + // IO is activated + let (mut tx, mut rx) = std::sync::mpsc::sync_channel::<Event>(10); + + // Setup Memory + let mut memory_array = vec![0; 0x800000]; + let file = std::fs::read("My 3310 NR1 v5.79.fls").unwrap(); + //let file = std::fs::read("FuBu3310 v6.39 (PPM B).fls").unwrap(); + + memory_array[0x00200000..0x003FFFFF + 1].copy_from_slice(&file); + + let mut mem = Mem { + inner: &mut memory_array, + tx, + }; + + // Current Controller (CCont) + let mut ccont = CurrentController::new(); + + // Setup CPU + let mut cpu = Cpu::new(); + // We don't have a bootloader. Hope everything is okay and jump straight + // to where the bootloader does + cpu.reg_set(Mode::User, reg::PC, 0x200040); + + let mut hit_eec46 = false; + // Main loop + let mut last_pc = 0x0; + let mut print_all_pc = false; + let mut jump_counter = JumpCounter::default(); + + let known_jumps = vec![ + 0x002ef9e6, // Delay_r0_loop + ]; + + let start = Instant::now(); + let run_duration = Duration::from_millis(5000); + + 'cpustep: while cpu.step(&mut mem) { + // Log jumps to make debugging easier. We can look at these + // addresses in Ghidra + let pc = cpu.reg_get(Mode::User, reg::PC); + if print_all_pc { + log::trace!(target: "pc_all", "[{}] {pc:08x}", pc as isize - last_pc as isize); + } + + if (pc as isize - last_pc as isize).abs() > 4 { + log::trace!("jump detect - {last_pc:08X} => {pc:08X}",); + let count = jump_counter.jump(last_pc, pc); + + if count >= 100 && count % 100 == 0 && !known_jumps.contains(&pc) { + log::trace!(target: "eec46_jmp", "[{count:<6}] {last_pc:08x} => {pc:08x}"); + } + } + + if pc == 0x002eebec && last_pc != 0x002eebec { + log::info!("Entered eebec loop!"); + } + + if pc == 0x002eec44 { + //let r5 = cpu.reg_get(Mode::User, 5); + // We set R5 here because the next two instructions are as follows: + // [MainLoop?] eec46: cmp r5, 0x1 + // eec48: bne [MainLoop?] + // So if we do not set r5 to 0x1, we'll loop back to eec46 forever + log::warn!("hit eec44... setting register 5!"); + cpu.reg_set(Mode::User, 5, 0x1); + } + + if pc == 0x002eec46 && !hit_eec46 { + hit_eec46 = true; + print_all_pc = true; + log::info!("hit eec46!"); + } else if pc != 0x002eec48 && pc != 0x002eec46 && hit_eec46 { + hit_eec46 = false; + print_all_pc = true; + log::warn!("broke from eec46! (pc = {pc:x})"); + } + last_pc = pc; + + if pc == 0x002eec52 { + log::info!("hit eec52!"); + } + + match rx.try_recv() { + Err(TryRecvError::Empty) => (), + Err(TryRecvError::Disconnected) => panic!("event sender disconnected"), + Ok(event) => match event { + Event::GenSIO(GenSIO::StartTransaction) => { + let start_byte = mem.inner[Mem::GENSIO_START_TRANSACTION]; + if start_byte == 0x25 { + mem.gensio_status_clear(); + mem.set_gensio_status(GensioState::DataWrite); + mem.set_gensio_status(GensioState::TransactionReady); + } + } + + Event::GenSIO(GenSIO::CContWrite) => ccont.event(&mut mem), + + Event::GenSIO(GenSIO::LcdCtrl) => { + let dog = mem.inner[Mem::GENSIO_LCD_CTRL]; + log::info!("GENSIO LCD Mystery {dog:02X} -- {dog:08b}"); + //mem.inner[Mem::UIF_CTRL_IO_2] = 0b0010_0000; + } + + Event::Ctsi(Ctsi::AsicWatchdog) => { + let dog = mem.inner[Mem::CTSI_ASIC_WATCHDOG]; + log::info!("CTSI Clock ={dog}"); + } + + Event::Ctsi(Ctsi::WriteClockControl) => { + let clock = mem.inner[Mem::CTSI_CLOCK_CONTROL]; + log::info!("CTSI Clock {clock:08b}"); + } + + Event::Uif(Uif::CtrlIo2) => { + let ctrl = mem.inner[Mem::UIF_CTRL_IO_2]; + log::info!("CTSI Clock {ctrl:08b}"); + } + }, + } + + if start.elapsed() >= run_duration { + log::error!("hit run_duration max runtime. breaking from cpustep loop"); + break 'cpustep; + } + + // Run slow + std::thread::sleep(Duration::from_micros(10)); + } + + println!("cpu halted"); +} + +#[derive(Debug, Default)] +pub struct JumpCounter { + jmps: Vec<Jump>, +} + +impl JumpCounter { + pub fn jump(&mut self, from: u32, to: u32) -> usize { + match self.jmps.iter_mut().find(|j| j.from == from && j.to == to) { + None => { + self.jmps.push(Jump::new(from, to)); + 1 + } + Some(jmp) => { + jmp.count += 1; + jmp.count + } + } + } +} + +#[derive(Debug)] +pub struct Jump { + from: u32, + to: u32, + count: usize, +} + +impl Jump { + pub fn new(from: u32, to: u32) -> Self { + Self { from, to, count: 1 } + } +} + +pub struct Lcd { + pub data: [u8; 44 * 84], +} |