about summary refs log tree commit diff
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rwxr-xr-xsrc/main.rs199
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],
+}