From f07a6d271449fdb717d547c580d1f7ca36abfc6d Mon Sep 17 00:00:00 2001 From: Logan McNaughton <848146+loganmc10@users.noreply.github.com> Date: Wed, 11 Oct 2023 17:05:23 -0600 Subject: [PATCH] flash saves (#13) * flash saves * clean * remove byte swap --- src/device.rs | 8 ++ src/device/sram.rs | 210 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 213 insertions(+), 5 deletions(-) diff --git a/src/device.rs b/src/device.rs index 18e4a4d..33ad0b5 100644 --- a/src/device.rs +++ b/src/device.rs @@ -47,6 +47,7 @@ pub struct Device { ai: ai::Ai, si: si::Si, ri: ri::Ri, + flashram: sram::Flashram, } impl Device { @@ -270,6 +271,13 @@ impl Device { count_per_scanline: 0, limiter: None, }, + flashram: sram::Flashram { + status: 0, + erase_page: 0, + page_buf: [0xff; 128], + silicon_id: [sram::FLASHRAM_TYPE_ID, sram::MX29L1100_ID], + mode: sram::FlashramMode::ReadArray, + }, } } } diff --git a/src/device/sram.rs b/src/device/sram.rs index ee2aded..910ee1f 100644 --- a/src/device/sram.rs +++ b/src/device/sram.rs @@ -3,7 +3,29 @@ use crate::ui; pub const SRAM_MASK: usize = 0xFFFF; pub const SRAM_SIZE: usize = 0x8000; -//pub const FLASHRAM_SIZE: usize = 0x20000; +pub const FLASHRAM_SIZE: usize = 0x20000; +pub const FLASHRAM_TYPE_ID: u32 = 0x11118001; +pub const MX29L1100_ID: u32 = 0x00c2001e; +pub const MX29L0000_ID: u32 = 0x00c20000; +pub const MX29L0001_ID: u32 = 0x00c20001; + +#[derive(PartialEq)] +pub enum FlashramMode { + ReadArray, + ReadSiliconId, + Status, + SectorErase, + ChipErase, + PageProgram, +} + +pub struct Flashram { + pub status: u32, + pub mode: FlashramMode, + pub erase_page: u16, + pub page_buf: [u8; 128], + pub silicon_id: [u32; 2], +} pub fn format_sram(device: &mut device::Device) { if device.ui.saves.sram.len() < SRAM_SIZE { @@ -11,6 +33,12 @@ pub fn format_sram(device: &mut device::Device) { } } +pub fn format_flash(device: &mut device::Device) { + if device.ui.saves.flash.len() < FLASHRAM_SIZE { + device.ui.saves.flash.resize(FLASHRAM_SIZE, 0xFF) + } +} + pub fn read_mem_sram(device: &mut device::Device, address: u64) -> u32 { let masked_address = address as usize & SRAM_MASK; @@ -23,6 +51,20 @@ pub fn read_mem_sram(device: &mut device::Device, address: u64) -> u32 { ); } +pub fn read_mem_flash(device: &mut device::Device, address: u64) -> u32 { + if (address & 0x1ffff) == 0x00000 && device.flashram.mode == FlashramMode::Status { + /* read Status register */ + return device.flashram.status; + } else if (address & 0x1ffff) == 0x0000 && device.flashram.mode == FlashramMode::ReadArray { + /* flashram MMIO read are not supported except for the "dummy" read @0x0000 done before DMA. + * returns a "dummy" value. */ + return 0; + } else { + /* other accesses are not implemented */ + panic!("unknown flashram read") + } +} + pub fn read_mem( device: &mut device::Device, address: u64, @@ -34,7 +76,7 @@ pub fn read_mem( if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { return read_mem_sram(device, address); } else { - panic!("flash read") + return read_mem_flash(device, address); } } @@ -54,11 +96,25 @@ pub fn write_mem_sram(device: &mut device::Device, address: u64, value: u32, mas ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Sram); } +pub fn write_mem_flash(device: &mut device::Device, address: u64, value: u32, mask: u32) { + if (address & 0x1ffff) == 0x00000 && device.flashram.mode == FlashramMode::Status { + /* clear/set Status register */ + device.flashram.status = (value & mask) & 0xff; + } else if (address & 0x1ffff) == 0x10000 { + /* set command */ + format_flash(device); + flashram_command(device, value & mask); + } else { + /* other accesses are not implemented */ + panic!("unknown flashram write") + } +} + pub fn write_mem(device: &mut device::Device, address: u64, value: u32, mask: u32) { if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { write_mem_sram(device, address, value, mask) } else { - panic!("flash write") + write_mem_flash(device, address, value, mask) } device.pi.regs[device::pi::PI_STATUS_REG as usize] |= device::pi::PI_STATUS_IO_BUSY; @@ -94,12 +150,30 @@ pub fn dma_read_sram( ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Sram); } +pub fn dma_read_flash(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) { + format_flash(device); + + if (cart_addr & 0x1ffff) == 0x00000 + && length == 128 + && device.flashram.mode == FlashramMode::PageProgram + { + /* load page buf using DMA */ + for i in 0..length { + device.flashram.page_buf[i as usize] = + device.rdram.mem[(dram_addr + i) as usize ^ device.byte_swap]; + } + } else { + /* other accesses are not implemented */ + panic!("unknown flash dma read") + } +} + // cart is big endian, rdram is native endian pub fn dma_read(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) -> u64 { if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { dma_read_sram(device, cart_addr, dram_addr, length) } else { - panic!("flash dma read") + dma_read_flash(device, cart_addr, dram_addr, length) } return device::pi::calculate_cycles(device, 2, length); } @@ -124,12 +198,138 @@ pub fn dma_write_sram( } } +pub fn dma_write_flash( + device: &mut device::Device, + mut cart_addr: u32, + mut dram_addr: u32, + length: u32, +) { + dram_addr &= device::rdram::RDRAM_MASK as u32; + + if (cart_addr & 0x1ffff) == 0x00000 + && length == 8 + && device.flashram.mode == FlashramMode::ReadSiliconId + { + /* read Silicon ID using DMA */ + device.rdram.mem[dram_addr as usize..dram_addr as usize + 4] + .copy_from_slice(&device.flashram.silicon_id[0].to_ne_bytes()); + dram_addr += 4; + device.rdram.mem[dram_addr as usize..dram_addr as usize + 4] + .copy_from_slice(&device.flashram.silicon_id[1].to_ne_bytes()); + } else if (cart_addr & 0x1ffff) < 0x10000 && device.flashram.mode == FlashramMode::ReadArray { + format_flash(device); + /* adjust flashram address before starting DMA. */ + if device.flashram.silicon_id[1] == MX29L1100_ID + || device.flashram.silicon_id[1] == MX29L0000_ID + || device.flashram.silicon_id[1] == MX29L0001_ID + { + /* "old" flash needs special address adjusting */ + cart_addr = (cart_addr & 0xffff) * 2; + } else { + /* "new" flash doesn't require special address adjusting at DMA start. */ + cart_addr &= 0xffff; + } + + /* do actual DMA */ + for i in 0..length { + device.rdram.mem[(dram_addr + i) as usize ^ device.byte_swap] = + device.ui.saves.flash[(cart_addr + i) as usize]; + } + } else { + /* other accesses are not implemented */ + panic!("unknown flash dma write") + } +} + // cart is big endian, rdram is native endian pub fn dma_write(device: &mut device::Device, cart_addr: u32, dram_addr: u32, length: u32) -> u64 { if device.ui.save_type.contains(&ui::storage::SaveTypes::Sram) { dma_write_sram(device, cart_addr, dram_addr, length) } else { - panic!("flash dma write") + dma_write_flash(device, cart_addr, dram_addr, length) } return device::pi::calculate_cycles(device, 2, length); } + +pub fn flashram_command(device: &mut device::Device, command: u32) { + match command & 0xff000000 { + 0x3c000000 => { + /* set chip erase mode */ + device.flashram.mode = FlashramMode::ChipErase; + } + + 0x4b000000 => { + /* set sector erase mode, set erase sector */ + device.flashram.mode = FlashramMode::SectorErase; + device.flashram.erase_page = command as u16; + } + + 0x78000000 => { + /* set erase busy flag */ + device.flashram.status |= 0x02; + + /* do chip/sector erase */ + if device.flashram.mode == FlashramMode::SectorErase { + let offset = (device.flashram.erase_page & 0xff80) * 128; + for i in 0..128 * 128 { + device.ui.saves.flash[offset as usize + i] = 0xFF; + } + ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Flash); + } else if device.flashram.mode == FlashramMode::ChipErase { + for i in 0..FLASHRAM_SIZE { + device.ui.saves.flash[i] = 0xFF; + } + ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Flash); + } else { + panic!("Unexpected flash erase command") + } + + /* clear erase busy flag, set erase success flag, transition to status mode */ + device.flashram.status &= !0x02; + device.flashram.status |= 0x08; + device.flashram.mode = FlashramMode::Status; + } + + 0xa5000000 => { + /* set program busy flag */ + device.flashram.status |= 0x01; + + /* program selected page */ + let offset = (command as u16) * 128; + for i in 0..128 { + device.ui.saves.flash[(offset + i) as usize] = device.flashram.page_buf[i as usize]; + } + ui::storage::write_save(&mut device.ui, ui::storage::SaveTypes::Flash); + + /* clear program busy flag, set program success flag, transition to status mode */ + device.flashram.status &= !0x01; + device.flashram.status |= 0x04; + device.flashram.mode = FlashramMode::Status; + } + + 0xb4000000 => { + /* set page program mode */ + device.flashram.mode = FlashramMode::PageProgram; + } + + 0xd2000000 => { + /* set status mode */ + device.flashram.mode = FlashramMode::Status; + } + + 0xe1000000 => { + /* set silicon_id mode */ + device.flashram.mode = FlashramMode::ReadSiliconId; + device.flashram.status |= 0x01; /* Needed for Pokemon Puzzle League */ + } + + 0xf0000000 => { + /* set read mode */ + device.flashram.mode = FlashramMode::ReadArray; + } + + _ => { + panic!("unknown flash command") + } + } +}