1//! GBA flash ROM code 2//! 3//! Comes in 64KB and 128KB variants; only difference is that 128KB supports a bank switch command 4 5use bincode::{Decode, Encode}; 6 7#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 8enum Command { 9 EnterIdMode, // 0x90 10 ExitIdMode, // 0xF0 11 PrepareErase, // 0x80 12 EraseChip, // 0x10 13 EraseSector, // 0x30 14 WriteByte, // 0xA0 15 SwitchBank, // 0xB0 16} 17 18impl Command { 19 fn from_byte(byte: u8) -> Option<Self> { 20 match byte { 21 0x10 => Some(Self::EraseChip), 22 0x30 => Some(Self::EraseSector), 23 0x80 => Some(Self::PrepareErase), 24 0x90 => Some(Self::EnterIdMode), 25 0xA0 => Some(Self::WriteByte), 26 0xB0 => Some(Self::SwitchBank), 27 0xF0 => Some(Self::ExitIdMode), 28 _ => None, 29 } 30 } 31} 32 33#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)] 34enum State { 35 Ready, 36 ReceivingCommand1, // Received $E005555 = 0xAA 37 ReceivingCommand2, // Received $E002AAA = 0x55 38 EraseReady, // Received erase command 0x80 39 EraseReceivingCommand1, // Received $E005555 = 0xAA 40 EraseReceivingCommand2, // Received $E002AAA = 0x55 41 WriteByteReady, // Received write byte command 0xA0 42 BankSwitchReady, // Received bank switch command 0xB0 43} 44 45#[derive(Debug, Clone, Encode, Decode)] 46pub struct FlashRom<const BANKED: bool> { 47 memory: Box<[u8]>, 48 state: State, 49 bank_offset: u32, 50 id_mode: bool, 51} 52 53pub type FlashRom64K = FlashRom<false>; 54pub type FlashRom128K = FlashRom<true>; 55 56// Arbitrary choices that are not Atmel or Macronix (which support custom commands) 57const PANASONIC_64K_ID: u16 = 0x1B32; 58const SANYO_128K_ID: u16 = 0x1362; 59 60impl<const BANKED: bool> FlashRom<BANKED> { 61 pub fn new(initial_save: Option<&Vec<u8>>) -> Self { 62 let len = if BANKED { 128 * 1024 } else { 64 * 1024 }; 63 let mut memory = vec![0xFF; len].into_boxed_slice(); 64 65 if let Some(initial_save) = initial_save 66 && initial_save.len() >= len 67 { 68 memory.copy_from_slice(&initial_save[..len]); 69 } 70 71 Self { memory, state: State::Ready, bank_offset: 0, id_mode: false } 72 } 73 74 pub fn read(&self, address: u32) -> u8 { 75 log::trace!("Flash ROM read {address:08X}, current state {:?}", self.state); 76 77 let address = address & 0xFFFF; 78 79 if self.id_mode && address <= 0x0001 { 80 let id = if BANKED { SANYO_128K_ID } else { PANASONIC_64K_ID }; 81 return id.to_le_bytes()[address as usize]; 82 } 83 84 self.memory[(self.bank_offset | address) as usize] 85 } 86 87 pub fn write(&mut self, address: u32, value: u8) { 88 log::trace!("Flash ROM write {address:08X} {value:02X}, current state {:?}", self.state); 89 90 let address = address & 0xFFFF; 91 92 self.state = match self.state { 93 State::Ready => { 94 if address == 0x5555 && value == 0xAA { 95 State::ReceivingCommand1 96 } else { 97 State::Ready 98 } 99 } 100 State::ReceivingCommand1 => { 101 if address == 0x2AAA && value == 0x55 { 102 State::ReceivingCommand2 103 } else { 104 State::Ready 105 } 106 } 107 State::ReceivingCommand2 => { 108 if address == 0x5555 { 109 match Command::from_byte(value) { 110 Some(Command::EnterIdMode) => { 111 self.id_mode = true; 112 State::Ready 113 } 114 Some(Command::ExitIdMode) => { 115 self.id_mode = false; 116 State::Ready 117 } 118 Some(Command::PrepareErase) => State::EraseReady, 119 Some(Command::WriteByte) => State::WriteByteReady, 120 Some(Command::SwitchBank) => { 121 if BANKED { 122 State::BankSwitchReady 123 } else { 124 State::Ready 125 } 126 } 127 Some(Command::EraseChip | Command::EraseSector) | None => State::Ready, 128 } 129 } else { 130 State::Ready 131 } 132 } 133 State::EraseReady => { 134 if address == 0x5555 && value == 0xAA { 135 State::EraseReceivingCommand1 136 } else { 137 State::Ready 138 } 139 } 140 State::EraseReceivingCommand1 => { 141 if address == 0x2AAA && value == 0x55 { 142 State::EraseReceivingCommand2 143 } else { 144 State::Ready 145 } 146 } 147 State::EraseReceivingCommand2 => match Command::from_byte(value) { 148 Some(Command::EraseChip) if address == 0x5555 => { 149 self.memory.fill(0xFF); 150 State::Ready 151 } 152 Some(Command::EraseSector) => { 153 let sector_addr = (self.bank_offset | (address & 0xF000)) as usize; 154 self.memory[sector_addr..sector_addr + 0x1000].fill(0xFF); 155 State::Ready 156 } 157 _ => State::Ready, 158 }, 159 State::WriteByteReady => { 160 self.memory[(self.bank_offset | address) as usize] = value; 161 State::Ready 162 } 163 State::BankSwitchReady => { 164 self.bank_offset = u32::from(value & 1) << 16; 165 State::Ready 166 } 167 }; 168 169 log::trace!(" New state {:?}", self.state); 170 } 171 172 pub fn memory(&self) -> &[u8] { 173 &self.memory 174 } 175} 176 177#[cfg(test)] 178mod tests { 179 use super::*; 180 181 #[test] 182 fn write_then_read() { 183 let mut flash_rom = FlashRom64K::new(None); 184 185 assert_eq!(0xFF, flash_rom.read(0xE001357)); 186 187 flash_rom.write(0xE005555, 0xAA); 188 flash_rom.write(0xE002AAA, 0x55); 189 flash_rom.write(0xE005555, 0xA0); 190 flash_rom.write(0xE001357, 0x42); 191 192 assert_eq!(0x42, flash_rom.read(0xE001357)); 193 } 194 195 #[test] 196 fn erase_chip() { 197 let mut flash_rom = FlashRom64K::new(None); 198 flash_rom.memory.fill(0x12); 199 200 flash_rom.write(0xE005555, 0xAA); 201 flash_rom.write(0xE002AAA, 0x55); 202 flash_rom.write(0xE005555, 0x80); 203 flash_rom.write(0xE005555, 0xAA); 204 flash_rom.write(0xE002AAA, 0x55); 205 flash_rom.write(0xE005555, 0x10); 206 207 for address in 0xE000000..=0xE00FFFF { 208 assert_eq!(0xFF, flash_rom.read(address)); 209 } 210 } 211 212 #[test] 213 fn erase_sector() { 214 let mut flash_rom = FlashRom64K::new(None); 215 flash_rom.memory.fill(0x12); 216 217 flash_rom.write(0xE005555, 0xAA); 218 flash_rom.write(0xE002AAA, 0x55); 219 flash_rom.write(0xE005555, 0x80); 220 flash_rom.write(0xE005555, 0xAA); 221 flash_rom.write(0xE002AAA, 0x55); 222 flash_rom.write(0xE007000, 0x30); 223 224 for address in 0xE000000..=0xE006FFF { 225 assert_eq!(0x12, flash_rom.read(address)); 226 } 227 228 for address in 0xE007000..=0xE007FFF { 229 assert_eq!(0xFF, flash_rom.read(address)); 230 } 231 232 for address in 0xE008000..=0xE00FFFF { 233 assert_eq!(0x12, flash_rom.read(address)); 234 } 235 } 236 237 #[test] 238 fn bank_switch() { 239 let mut flash_rom = FlashRom128K::new(None); 240 241 // Write 0x42 to $1357 242 flash_rom.write(0xE005555, 0xAA); 243 flash_rom.write(0xE002AAA, 0x55); 244 flash_rom.write(0xE005555, 0xA0); 245 flash_rom.write(0xE001357, 0x42); 246 247 assert_eq!(0x42, flash_rom.read(0xE001357)); 248 249 // Switch to bank 1 250 flash_rom.write(0xE005555, 0xAA); 251 flash_rom.write(0xE002AAA, 0x55); 252 flash_rom.write(0xE005555, 0xB0); 253 flash_rom.write(0xE000000, 1); 254 255 assert_eq!(0xFF, flash_rom.read(0xE001357)); 256 257 // Write 0xBC to $1357 258 flash_rom.write(0xE005555, 0xAA); 259 flash_rom.write(0xE002AAA, 0x55); 260 flash_rom.write(0xE005555, 0xA0); 261 flash_rom.write(0xE001357, 0xBC); 262 263 assert_eq!(0xBC, flash_rom.read(0xE001357)); 264 265 // Switch to bank 0 266 flash_rom.write(0xE005555, 0xAA); 267 flash_rom.write(0xE002AAA, 0x55); 268 flash_rom.write(0xE005555, 0xB0); 269 flash_rom.write(0xE000000, 0); 270 271 assert_eq!(0x42, flash_rom.read(0xE001357)); 272 273 assert_eq!(0x42, flash_rom.memory[0x01357]); 274 assert_eq!(0xBC, flash_rom.memory[0x11357]); 275 } 276}