diff --git a/src/device/controller/gbcart.rs b/src/device/controller/gbcart.rs index 0d1ae27..593fc1e 100644 --- a/src/device/controller/gbcart.rs +++ b/src/device/controller/gbcart.rs @@ -1,5 +1,6 @@ use crate::device; +use std::io::Write; const MBC3_RTC_SECONDS: usize = 0; const MBC3_RTC_MINUTES: usize = 1; const MBC3_RTC_HOURS: usize = 2; @@ -21,7 +22,7 @@ pub struct GbCart { pub latch: bool, pub rtc_regs: [u8; MBC3_RTC_REGS_COUNT], pub rtc_regs_latch: [u8; MBC3_RTC_REGS_COUNT], - pub current_time: i64, + pub rtc_timestamp: i64, pub last_time: i64, } @@ -35,9 +36,98 @@ pub enum CartType { MBC5RamBatt, } -fn update_rtc_regs(cart: &mut device::controller::gbcart::GbCart) { - let mut diff = cart.current_time - cart.last_time; - cart.last_time = cart.current_time; +pub fn init(gb_cart: &mut device::controller::gbcart::GbCart, rom: &[u8], ram: &[u8]) { + gb_cart.rom = rom.to_vec(); + let offset; + match ram.len() % 8192 { + 44 => { + offset = ram.len() - 44; + gb_cart.ram = ram[..ram.len() - 44].to_vec(); + gb_cart.rtc_timestamp = + i32::from_le_bytes(ram[offset + 40..offset + 44].try_into().unwrap()) as i64; + } + 48 => { + offset = ram.len() - 48; + gb_cart.ram = ram[..ram.len() - 48].to_vec(); + gb_cart.rtc_timestamp = + i64::from_le_bytes(ram[offset + 40..offset + 48].try_into().unwrap()); + } + _ => { + gb_cart.ram = ram.to_vec(); + return; + } + } + + gb_cart.rtc_regs[MBC3_RTC_SECONDS] = + u32::from_le_bytes(ram[offset..offset + 4].try_into().unwrap()) as u8; + gb_cart.rtc_regs[MBC3_RTC_MINUTES] = + u32::from_le_bytes(ram[offset + 4..offset + 8].try_into().unwrap()) as u8; + gb_cart.rtc_regs[MBC3_RTC_HOURS] = + u32::from_le_bytes(ram[offset + 8..offset + 12].try_into().unwrap()) as u8; + gb_cart.rtc_regs[MBC3_RTC_DAYS_L] = + u32::from_le_bytes(ram[offset + 12..offset + 16].try_into().unwrap()) as u8; + gb_cart.rtc_regs[MBC3_RTC_DAYS_H] = + u32::from_le_bytes(ram[offset + 16..offset + 20].try_into().unwrap()) as u8; + + gb_cart.rtc_regs_latch[MBC3_RTC_SECONDS] = + u32::from_le_bytes(ram[offset + 20..offset + 24].try_into().unwrap()) as u8; + gb_cart.rtc_regs_latch[MBC3_RTC_MINUTES] = + u32::from_le_bytes(ram[offset + 24..offset + 28].try_into().unwrap()) as u8; + gb_cart.rtc_regs_latch[MBC3_RTC_HOURS] = + u32::from_le_bytes(ram[offset + 28..offset + 32].try_into().unwrap()) as u8; + gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_L] = + u32::from_le_bytes(ram[offset + 32..offset + 36].try_into().unwrap()) as u8; + gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_H] = + u32::from_le_bytes(ram[offset + 36..offset + 40].try_into().unwrap()) as u8; +} + +pub fn save( + gb_cart: &mut device::controller::gbcart::GbCart, + elapsed_time: i64, + ram_path: &std::string::String, +) { + update_rtc_regs(gb_cart, elapsed_time); + + if let Ok(file) = std::fs::File::create(ram_path) { + let mut f = std::io::BufWriter::new(file); + f.write_all(&gb_cart.ram).unwrap(); + + if gb_cart.cart_type == CartType::MBC3RamBattRtc { + f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_SECONDS] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_MINUTES] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_HOURS] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_DAYS_L] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs[MBC3_RTC_DAYS_H] as u32).to_le_bytes()) + .unwrap(); + + f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_SECONDS] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_MINUTES] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_HOURS] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_L] as u32).to_le_bytes()) + .unwrap(); + f.write_all(&(gb_cart.rtc_regs_latch[MBC3_RTC_DAYS_H] as u32).to_le_bytes()) + .unwrap(); + + let timestamp = gb_cart.rtc_timestamp.saturating_add(elapsed_time); + f.write_all(×tamp.to_le_bytes()).unwrap(); + + f.flush().unwrap(); + } + } else { + eprintln!("Error saving TransferPak RAM to {ram_path}"); + } +} + +fn update_rtc_regs(cart: &mut device::controller::gbcart::GbCart, elapsed_time: i64) { + let mut diff = elapsed_time - cart.last_time; + cart.last_time = elapsed_time; if diff > 0 { cart.rtc_regs[MBC3_RTC_SECONDS] += (diff % 60) as u8; @@ -154,6 +244,7 @@ fn write_mbc3( address: u16, data: usize, size: usize, + elapsed_time: i64, ) { let value = pif_ram[data + size - 1]; if address < 0x2000 { @@ -169,7 +260,7 @@ fn write_mbc3( } else if address < 0x8000 { if cart.cart_type == CartType::MBC3RamBattRtc { if !cart.latch && (value & 0x1) != 0 { - update_rtc_regs(cart); + update_rtc_regs(cart, elapsed_time); cart.rtc_regs_latch = cart.rtc_regs; } cart.latch = (value & 0x1) != 0; @@ -196,6 +287,7 @@ fn read_mbc3( address: u16, data: usize, size: usize, + elapsed_time: i64, ) { if address < 0x4000 { let banked_address = address & 0x3FFF; @@ -225,7 +317,7 @@ fn read_mbc3( pif_ram[data + i] = cart.rtc_regs_latch[cart.ram_bank as usize - 0x8]; } } else { - update_rtc_regs(cart); + update_rtc_regs(cart, elapsed_time); for i in 0..size { pif_ram[data + i] = cart.rtc_regs[cart.ram_bank as usize - 0x8]; } @@ -306,6 +398,7 @@ pub fn read( address: u16, data: usize, size: usize, + elapsed_time: i64, ) { if !cart.enabled { for i in 0..size { @@ -315,8 +408,8 @@ pub fn read( } match cart.cart_type { CartType::MBC1RamBatt => read_mbc1(pif_ram, cart, address, data, size), - CartType::MBC3RamBatt => read_mbc3(pif_ram, cart, address, data, size), - CartType::MBC3RamBattRtc => read_mbc3(pif_ram, cart, address, data, size), + CartType::MBC3RamBatt => read_mbc3(pif_ram, cart, address, data, size, elapsed_time), + CartType::MBC3RamBattRtc => read_mbc3(pif_ram, cart, address, data, size, elapsed_time), CartType::MBC5RamBatt => read_mbc5(pif_ram, cart, address, data, size), _ => panic!("Unsupported cart type"), } @@ -328,14 +421,15 @@ pub fn write( address: u16, data: usize, size: usize, + elapsed_time: i64, ) { if !cart.enabled { return; } match cart.cart_type { CartType::MBC1RamBatt => write_mbc1(pif_ram, cart, address, data, size), - CartType::MBC3RamBatt => write_mbc3(pif_ram, cart, address, data, size), - CartType::MBC3RamBattRtc => write_mbc3(pif_ram, cart, address, data, size), + CartType::MBC3RamBatt => write_mbc3(pif_ram, cart, address, data, size, elapsed_time), + CartType::MBC3RamBattRtc => write_mbc3(pif_ram, cart, address, data, size, elapsed_time), CartType::MBC5RamBatt => write_mbc5(pif_ram, cart, address, data, size), _ => panic!("Unsupported cart type"), } diff --git a/src/device/controller/transferpak.rs b/src/device/controller/transferpak.rs index dea6cd5..2505532 100644 --- a/src/device/controller/transferpak.rs +++ b/src/device/controller/transferpak.rs @@ -52,13 +52,13 @@ pub fn read(device: &mut device::Device, channel: usize, address: u16, data: usi } } 0xC..=0xF => { - pak.cart.current_time = device.vi.elapsed_time as i64; device::controller::gbcart::read( &mut device.pif.ram, &mut pak.cart, 0x4000 * pak.bank + (address & 0x7fff) - 0x4000, data, size, + device.vi.elapsed_time as i64, ); } _ => { @@ -119,13 +119,13 @@ pub fn write(device: &mut device::Device, channel: usize, address: u16, data: us } } 0xC..=0xF => { - pak.cart.current_time = device.vi.elapsed_time as i64; device::controller::gbcart::write( &device.pif.ram, &mut pak.cart, 0x4000 * pak.bank + (address & 0x7fff) - 0x4000, data, size, + device.vi.elapsed_time as i64, ); } _ => { diff --git a/src/main.rs b/src/main.rs index 4cd1194..7c7153c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -185,8 +185,7 @@ async fn main() -> std::io::Result<()> { && let Ok(rom) = std::fs::read(&device.ui.config.input.gb_rom_path[i]) && let Ok(ram) = std::fs::read(&device.ui.config.input.gb_ram_path[i]) { - device.transferpaks[i].cart.rom = rom; - device.transferpaks[i].cart.ram = ram; + device::controller::gbcart::init(&mut device.transferpaks[i].cart, &rom, &ram); } } @@ -238,13 +237,12 @@ async fn main() -> std::io::Result<()> { if device.ui.config.input.transfer_pak[i] && !device.ui.config.input.gb_ram_path[i].is_empty() && !device.transferpaks[i].cart.ram.is_empty() - && let Err(e) = tokio::fs::write( - &device.ui.config.input.gb_ram_path[i], - &device.transferpaks[i].cart.ram, - ) - .await { - eprintln!("Error writing GB RAM: {}", e); + device::controller::gbcart::save( + &mut device.transferpaks[i].cart, + device.vi.elapsed_time as i64, + &device.ui.config.input.gb_ram_path[i], + ); } } }