diff options
author | gennyble <gen@nyble.dev> | 2025-03-05 05:48:04 -0600 |
---|---|---|
committer | gennyble <gen@nyble.dev> | 2025-03-05 05:48:04 -0600 |
commit | 557766c22a17d42ac58bd39cda9889f1f6b7f5bc (patch) | |
tree | 78ba1a6a78f9c5b74f05c0c96cd4fb59dc260665 | |
download | nokia3310emu-557766c22a17d42ac58bd39cda9889f1f6b7f5bc.tar.gz nokia3310emu-557766c22a17d42ac58bd39cda9889f1f6b7f5bc.zip |
inititty comitty
-rwxr-xr-x | .DS_Store | bin | 0 -> 6148 bytes | |||
-rwxr-xr-x | .gitignore | 4 | ||||
-rwxr-xr-x | .rustfmt.toml | 1 | ||||
-rwxr-xr-x | Cargo.lock | 325 | ||||
-rwxr-xr-x | Cargo.toml | 12 | ||||
-rwxr-xr-x | My 3310 NR1 v5.79.fls | bin | 0 -> 2097152 bytes | |||
-rw-r--r-- | README.md | 2 | ||||
-rwxr-xr-x | src/currentcontroller.rs | 207 | ||||
-rwxr-xr-x | src/main.rs | 199 | ||||
-rwxr-xr-x | src/mem.rs | 244 |
10 files changed, 994 insertions, 0 deletions
diff --git a/.DS_Store b/.DS_Store new file mode 100755 index 0000000..5707b56 --- /dev/null +++ b/.DS_Store Binary files differdiff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..7fa6f53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/target +*.log +FuBu3310 v6.39 (PPM B).fls +mapped_rom.dump \ No newline at end of file diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100755 index 0000000..218e203 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +hard_tabs = true diff --git a/Cargo.lock b/Cargo.lock new file mode 100755 index 0000000..9e550ba --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,325 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "armv4t_emu" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "041e88e25b5fd858fc0091f44a8981aed83d4c29141820b46c9f56f94e42e816" +dependencies = [ + "log", +] + +[[package]] +name = "colored" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" +dependencies = [ + "lazy_static", + "windows-sys 0.59.0", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.170" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" + +[[package]] +name = "log" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" + +[[package]] +name = "nokia3310emu" +version = "0.1.0" +dependencies = [ + "armv4t_emu", + "log", + "simple_logger", + "time", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.218" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8dfc9d19bdbf6d17e22319da49161d5d0108e4188e8b680aef6299eed22df60" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.218" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f09503e191f4e797cb8aac08e9a4a4695c5edf6a2e70e376d961ddd5c969f82b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "simple_logger" +version = "4.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e7e46c8c90251d47d08b28b8a419ffb4aede0f87c2eea95e17d1d5bacbf3ef1" +dependencies = [ + "colored", + "log", + "time", + "windows-sys 0.48.0", +] + +[[package]] +name = "syn" +version = "2.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e02e925281e18ffd9d640e234264753c43edc62d64b2d4cf898f1bc5e75f3fc2" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/Cargo.toml b/Cargo.toml new file mode 100755 index 0000000..d1a5e75 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "nokia3310emu" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +armv4t_emu = "0.1.0" +log = "0.4.20" +simple_logger = "4.3.3" +time = "0.3.31" diff --git a/My 3310 NR1 v5.79.fls b/My 3310 NR1 v5.79.fls new file mode 100755 index 0000000..ccc7627 --- /dev/null +++ b/My 3310 NR1 v5.79.fls Binary files differdiff --git a/README.md b/README.md new file mode 100644 index 0000000..de7ba18 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +"Vintage Firmware Modding Nokia DCT3 Phones" +https://www.youtube.com/watch?v=_7tkjJ-F95U \ No newline at end of file diff --git a/src/currentcontroller.rs b/src/currentcontroller.rs new file mode 100755 index 0000000..7a39770 --- /dev/null +++ b/src/currentcontroller.rs @@ -0,0 +1,207 @@ +use std::time::SystemTime; + +use time::OffsetDateTime; + +use crate::mem::{GensioState, Mem}; + +pub struct CurrentController { + pub reg: Option<u8>, + pub control_reg: u8, + //TODO: make enum + pub charging_mode: u8, + pub tenbit_adc: u16, + pub head_hook_detection: u8, + //TODO: make enum + pub watchdog_setting: u8, + pub rtc_enable: u8, + //TODO: track time in struct + pub alarm_minute: u8, + pub alarm_hour: u8, + pub rtc_calibration: u8, + /// Bit 0 - RTC bat present + /// Bit 3/3 - CContINT 3-7 + pub irq: u8, + /// True(1)/False(0) + pub irq_mask: u8, +} + +impl CurrentController { + pub fn new() -> Self { + Self { + reg: None, + control_reg: 0, + charging_mode: 0, + tenbit_adc: 0, + head_hook_detection: 0, + watchdog_setting: 0, + rtc_enable: 0, + alarm_minute: 0, + alarm_hour: 12, + rtc_calibration: 0, + irq: 0, + irq_mask: 0, + } + } + + pub fn event(&mut self, mem: &mut Mem) { + log::trace!("CCont value={:02X}", mem.inner[Mem::CCONT_WRITE]); + log::trace!( + "CCont write >> 3 value={:02X}", + mem.inner[Mem::CCONT_WRITE] >> 3 + ); + log::trace!( + "CCont read ^ 4 >> 3 value={:02X}", + (mem.inner[Mem::CCONT_WRITE] ^ 0x04) >> 3 + ); + + let and_four = mem.inner[Mem::CCONT_WRITE] & 0x04; + + match self.reg { + None => { + if and_four > 0 { + self.read(mem.inner[Mem::CCONT_WRITE], mem); + } else { + self.reg = Some(mem.inner[Mem::CCONT_WRITE]) + } + } + Some(reg) => { + self.write(reg, mem); + self.reg = None; + } + } + } + + pub fn read(&mut self, addr: u8, mem: &mut Mem) { + log::trace!("ccont::read()"); + let reg = (addr ^ 0x04) >> 3; + + match reg { + 0x02 => { + log::trace!("CCont A/D 0-7 Read"); + let adc = self.tenbit_adc.to_be_bytes(); + mem.inner[Mem::CCONT_READ] = adc[0]; + } + 0x03 => { + log::trace!("CCont Headset/Hooked flag?"); + mem.inner[Mem::CCONT_READ] = self.head_hook_detection; + } + 0x04 => { + log::trace!("CCont A/D 8-9 Read & test bits"); + let adc = self.tenbit_adc.to_be_bytes(); + mem.inner[Mem::CCONT_READ] = ((adc[1] >> 6) & 3) | 0b101100_00; + } + 0x09 => { + log::trace!("CCont RTC Second"); + let time = OffsetDateTime::now_local().unwrap(); + mem.inner[Mem::CCONT_READ] = time.second(); + } + 0x0A => { + log::trace!("CCont RTC Minute"); + let time = OffsetDateTime::now_local().unwrap(); + mem.inner[Mem::CCONT_READ] = time.minute(); + } + 0x0B => { + log::trace!("CCont RTC Hour"); + let time = OffsetDateTime::now_local().unwrap(); + mem.inner[Mem::CCONT_READ] = time.hour(); + } + 0x0C => { + log::trace!("CCont RTC Day"); + let time = OffsetDateTime::now_local().unwrap(); + mem.inner[Mem::CCONT_READ] = time.day(); + } + 0x0D => { + log::trace!("CCont RTC Alarm Minute"); + mem.inner[Mem::CCONT_READ] = self.alarm_minute; + } + 0x0E => { + log::trace!("CCont RTC Alarm Hour"); + mem.inner[Mem::CCONT_READ] = self.alarm_hour; + } + 0x0F => { + log::trace!("CCont RTC Calibration"); + mem.inner[Mem::CCONT_READ] = self.rtc_calibration; + } + 0x10 => { + log::trace!("CCont RTC Interrupt Controller"); + mem.inner[Mem::CCONT_READ] = self.irq; + } + 0x11 => { + log::trace!("CCont RTC IRQ Mask"); + mem.inner[Mem::CCONT_READ] = self.irq_mask; + } + reg => { + log::trace!("CCont !!! {reg:02X} unknown read!"); + panic!() + } + } + + //mem.gensio_status_clear(); + mem.set_gensio_status(GensioState::DataRead); + //mem.set_gensio_status(GensioState::TransactionReady); + } + + pub fn write(&mut self, addr: u8, mem: &mut Mem) { + log::trace!("ccont::write()"); + let reg = addr >> 3; + let val = mem.inner[Mem::CCONT_WRITE]; + + match reg { + 0x00 => { + log::trace!("CCont control register"); + self.control_reg = val; + } + 0x01 => { + log::trace!("CCont PWM (charger)"); + self.charging_mode = val; + } + 0x03 => { + log::trace!("CCont A/D Read 0-7 (why call write?)"); + log::info!("Not writing to AD due to confusion on how that works"); + } + 0x05 => { + log::trace!("CCont Headset/Hooked flag?"); + self.head_hook_detection = val; + } + 0x06 => { + log::trace!("CCont Set Watchdog"); + self.watchdog_setting = val; + } + 0x07 => { + log::trace!("CCont RTC Enable"); + self.rtc_enable = val; + } + 0x08 => { + log::trace!("CCont RTC Second"); + log::info!("Ignoring RTC Second write due to current RTC implementation") + } + 0x0C => { + log::trace!("CCont RTC Day"); + log::info!("Ignoring RTC Day write due to current RTC implementation") + } + 0x0D => { + log::trace!("CCont RTC Alarm Minute"); + self.alarm_minute = val; + } + 0x0E => { + log::trace!("CCont RTC Alarm Hour"); + self.alarm_hour = val; + } + 0x0F => { + log::trace!("CCont RTC Callibration"); + self.rtc_calibration = val; + } + 0x10 => { + log::trace!("CCont RTC Interrupt Controller"); + self.irq = val; + } + 0x11 => { + log::trace!("CCont RTC IRQ Mask"); + self.irq_mask = val; + } + reg => { + log::trace!("CCont !!! {reg:02X} unknown register") + } + } + } +} 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], +} diff --git a/src/mem.rs b/src/mem.rs new file mode 100755 index 0000000..34d16c6 --- /dev/null +++ b/src/mem.rs @@ -0,0 +1,244 @@ +use std::sync::mpsc::SyncSender; + +use armv4t_emu::Memory; + +pub struct Mem<'m> { + pub inner: &'m mut [u8], + pub tx: SyncSender<Event>, +} + +impl<'m> Memory for Mem<'m> { + fn r8(&mut self, addr: u32) -> u8 { + self.special_addr(addr, false); + + self.inner[addr as usize] + } + + fn r16(&mut self, addr: u32) -> u16 { + self.special_addr(addr, false); + + u16::from_be_bytes([self.inner[addr as usize], self.inner[addr as usize + 1]]) + } + + fn r32(&mut self, addr: u32) -> u32 { + self.special_addr(addr, false); + + u32::from_be_bytes([ + self.inner[addr as usize], + self.inner[addr as usize + 1], + self.inner[addr as usize + 2], + self.inner[addr as usize + 3], + ]) + } + + fn w8(&mut self, addr: u32, val: u8) { + self.special_addr(addr, true); + + self.inner[addr as usize] = val; + } + + fn w16(&mut self, addr: u32, val: u16) { + self.special_addr(addr, true); + + let bytes = val.to_be_bytes(); + self.inner[addr as usize] = bytes[0]; + self.inner[addr as usize + 1] = bytes[1]; + } + + fn w32(&mut self, addr: u32, val: u32) { + self.special_addr(addr, true); + + let bytes = val.to_be_bytes(); + self.inner[addr as usize] = bytes[0]; + self.inner[addr as usize + 1] = bytes[1]; + self.inner[addr as usize + 2] = bytes[2]; + self.inner[addr as usize + 3] = bytes[3]; + } +} + +impl<'m> Mem<'m> { + const IO: u32 = 0x00020000; + const IO_END: u32 = 0x000200FF; + + pub const CTSI_ASIC_WATCHDOG: usize = Self::IO as usize | 0x03; + pub const CTSI_IRQ_LINES: usize = Self::IO as usize | 0x09; + pub const CTSI_CLOCK_CONTROL: usize = Self::IO as usize | 0x0D; + + pub const CCONT_WRITE: usize = Self::IO as usize | 0x2C; + pub const GENSIO_START_TRANSACTION: usize = Self::IO as usize | 0x2D; + pub const UIF_CTRL_IO_2: usize = Self::IO as usize | 0x32; + pub const CCONT_READ: usize = Self::IO as usize | 0x6C; + pub const GENSIO_STATUS: usize = Self::IO as usize | 0x6D; + pub const GENSIO_LCD_CTRL: usize = Self::IO as usize | 0x6E; + + pub fn set_gensio_status(&mut self, state: GensioState) { + let byte = &mut self.inner[Self::GENSIO_STATUS]; + + match state { + GensioState::DataWrite => *byte |= 1, + GensioState::TransactionReady => *byte |= 2, + GensioState::DataRead => *byte |= 5, + } + } + + pub fn gensio_status_clear(&mut self) { + self.inner[Self::GENSIO_STATUS] = 0x00; + } + + fn special_addr(&mut self, addr: u32, write: bool) { + if (Self::IO..=Self::IO_END).contains(&addr) { + self.special_io(addr, write) + } + } + + fn special_io(&mut self, addr: u32, write: bool) { + let io_addr = addr ^ Self::IO; + + let meaning = match io_addr { + // 00-3F + 0x00 => "ASIC Version", + 0x01 => "MCU reset control register", + 0x02 => "DSP reset control register", + + 0x03 => { + self.tx.send(Event::Ctsi(Ctsi::AsicWatchdog)).unwrap(); + + "CTSI ASIC Watchdog Write Register" + } + 0x0A => "CTSI FIQ lines mask", + 0x0B => "CTSI IRQ lines mask ", + 0x0D => { + self.tx.send(Event::Ctsi(Ctsi::WriteClockControl)).unwrap(); + + "CTSI Clock Control Register" + } + 0x20 => "McuGenIO Signal Lines", + 0x28 => "Keyboard ROW signal lines", + 0x2A => "Keyboard COL signal lines", + 0x2C => { + self.tx.send(Event::GenSIO(GenSIO::CContWrite)).unwrap(); + + "CCont write" + } + 0x2D => { + self.tx + .send(Event::GenSIO(GenSIO::StartTransaction)) + .unwrap(); + + "GENSIO start transaction" + } + 0x2E => "GENSIO LCD data write", + 0x32 => { + self.tx.send(Event::Uif(Uif::CtrlIo2)).unwrap(); + + "UIF CTRL IO 2" + } + addr if addr >= 0x36 && addr <= 0x3F => "SIM something [0x36 to 0x3F]", + + // 40-7F + 0x6B => "Keyboard COL interrupt mask", + 0x6C => "CCont read", + 0x6D => { + self.gensio_status(); + "GENSIO status" + } + 0x6E => { + self.tx.send(Event::GenSIO(GenSIO::LcdCtrl)).unwrap(); + + "GENSIO LCD Ctrl" + } + 0x6F => "GENSIO - 3/SELECT1", + 0x70 => "CTRL I/O Direction 0,1", + 0x71 => "CTRL I/O Direction 1,1", + 0x72 => "CTRL I/O Direction 2,1", + 0x73 => "CTRL I/O Direction 3,1", + + //80-BF + 0xA8 => "Keyboard ROW I/O direction", + 0xAD => "GENSIO - 1/SELECT2", + 0xAE => "GENSIO - 2/SELECT2", + 0xAF => "GENSIO - 3/SELECT2", + 0xB0 => "CTRL I/O Direction 0,2", + 0xB1 => "CTRL I/O Direction 1,2", + 0xB2 => "CTRL I/O Direction 2,2", + 0xB3 => "CTRL I/O Direction 3,2", + + //C0-FF + 0xED => "GENSIO - 1/SELECT3", + 0xEE => "GENSIO - 2/SELECT3", + 0xEF => "GENSIO - 3/SELECT3", + 0xF0 => "CTRL I/O Input 0", + 0xF1 => "CTRL I/O Input 1", + 0xF2 => "CTRL I/O Input 2", + 0xF3 => "CTRL I/O Input 3", + + _ => "unknown IO", + }; + + let read_write = if write { "w" } else { "r" }; + + log::info!("IO {read_write} [{io_addr:02X}] - {meaning}"); + } + + fn gensio_status(&mut self) { + let base = (Self::IO | 0x2D) as usize; + let ctrl_io_dir_1 = self.inner[base]; + log::info!("CTRL Start Transaction -- {:08b}", ctrl_io_dir_1); + + log::info!("GENSIO Status -- {:08b}", self.inner[Self::GENSIO_STATUS]); + + self.print_ctrl_io_direction_1(); + self.print_ctrl_io_direction_2(); + } + + fn print_ctrl_io_direction_1(&self) { + let base = (Self::IO | 0x70) as usize; + let ctrl_io_dir_1 = &self.inner[base..base + 4]; + log::info!( + "CTRL I/O Direction (1) -- {:08b} {:08b} {:08b} {:08b}", + ctrl_io_dir_1[0], + ctrl_io_dir_1[1], + ctrl_io_dir_1[2], + ctrl_io_dir_1[3] + ); + } + + fn print_ctrl_io_direction_2(&self) { + let base = (Self::IO | 0xB0) as usize; + let ctrl_io_dir_1 = &self.inner[base..base + 4]; + log::info!( + "CTRL I/O Direction (2) -- {:08b} {:08b} {:08b} {:08b}", + ctrl_io_dir_1[0], + ctrl_io_dir_1[1], + ctrl_io_dir_1[2], + ctrl_io_dir_1[3] + ); + } +} + +pub enum GensioState { + DataWrite, + TransactionReady, + DataRead, +} + +pub enum Event { + GenSIO(GenSIO), + Ctsi(Ctsi), + Uif(Uif), +} + +pub enum GenSIO { + StartTransaction, + CContWrite, + LcdCtrl, +} + +pub enum Ctsi { + AsicWatchdog, + WriteClockControl, +} + +pub enum Uif { + CtrlIo2, +} |