From 3839b8eb02ccc8b1a7b36bfb51b7da149302712f Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Thu, 21 May 2020 22:10:18 +0300 Subject: [PATCH] core: Finish up RTC! Pokemon Emerald no longer has the "Battery ran dry" message, and RTC functions work. Former-commit-id: b05115af9797b9d754e20d98b1dbd5dac5389518 Former-commit-id: b10a70ec02d0b8775e1e39e9d64b41860d3b1274 --- rustboyadvance-core/src/cartridge/builder.rs | 8 +- rustboyadvance-core/src/cartridge/gpio.rs | 37 +--- rustboyadvance-core/src/cartridge/mod.rs | 3 + rustboyadvance-core/src/cartridge/rtc.rs | 218 +++++++++++-------- rustboyadvance-core/src/debugger/command.rs | 5 + 5 files changed, 142 insertions(+), 129 deletions(-) diff --git a/rustboyadvance-core/src/cartridge/builder.rs b/rustboyadvance-core/src/cartridge/builder.rs index b203512..38aacf8 100644 --- a/rustboyadvance-core/src/cartridge/builder.rs +++ b/rustboyadvance-core/src/cartridge/builder.rs @@ -16,6 +16,7 @@ use super::Cartridge; use super::loader::{load_from_bytes, load_from_file, LoadRom}; #[derive(Debug)] +#[allow(dead_code)] pub enum GpioDeviceType { Rtc, SolarSensor, @@ -136,7 +137,10 @@ impl GamepakBuilder { let mut gpio_device = self.gpio_device; if let Some(overrides) = overrides::get_game_overrides(&header.game_code) { - info!("Found game overrides for {}: {:#?}", header.game_code, overrides); + info!( + "Found game overrides for {}: {:#?}", + header.game_code, overrides + ); if let Some(override_save_type) = overrides.save_type() { if override_save_type != save_type && save_type != BackupType::AutoDetect { warn!( @@ -150,7 +154,7 @@ impl GamepakBuilder { if overrides.force_rtc() { match gpio_device { GpioDeviceType::None => gpio_device = GpioDeviceType::Rtc, - GpioDeviceType::Rtc => {}, + GpioDeviceType::Rtc => {} _ => { warn!( "Can't use RTC due to forced gpio device type {:?}", diff --git a/rustboyadvance-core/src/cartridge/gpio.rs b/rustboyadvance-core/src/cartridge/gpio.rs index 96b2ee4..724da2a 100644 --- a/rustboyadvance-core/src/cartridge/gpio.rs +++ b/rustboyadvance-core/src/cartridge/gpio.rs @@ -4,32 +4,6 @@ use super::{GPIO_PORT_CONTROL, GPIO_PORT_DATA, GPIO_PORT_DIRECTION}; use bit::BitIndex; use serde::{Deserialize, Serialize}; -/// Struct holding the logical state of a serial port -#[repr(transparent)] -#[derive(Serialize, Deserialize, Copy, Clone, Debug)] -pub struct GpioPort(u16); - -impl GpioPort { - pub fn get(&self) -> u16 { - self.0 - } - - pub fn set(&mut self, value: u16) { - self.0 = value & 1; - } - - pub fn high(&self) -> bool { - self.0 != 0 - } - - pub fn low(&self) -> bool { - self.0 == 0 - } -} - -pub const GPIO_LOW: GpioPort = GpioPort(0); -pub const GPIO_HIGH: GpioPort = GpioPort(1); - #[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)] pub enum GpioDirection { /// GPIO to GBA @@ -53,7 +27,7 @@ pub trait GpioDevice: Sized { #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Gpio { - rtc: Option, + pub(in crate) rtc: Option, direction: GpioState, control: GpioPortControl, } @@ -117,7 +91,6 @@ impl Gpio { } } GPIO_PORT_CONTROL => { - info!("GPIO port control: {:?}", self.control); self.control = if value != 0 { GpioPortControl::ReadWrite } else { @@ -128,11 +101,3 @@ impl Gpio { } } } - -#[cfg(test)] -mod tests { - #[test] - fn test_gpio() { - unimplemented!(); - } -} diff --git a/rustboyadvance-core/src/cartridge/mod.rs b/rustboyadvance-core/src/cartridge/mod.rs index 9178864..ab3ecd0 100644 --- a/rustboyadvance-core/src/cartridge/mod.rs +++ b/rustboyadvance-core/src/cartridge/mod.rs @@ -49,6 +49,9 @@ impl Cartridge { pub fn get_symbols(&self) -> &Option { &self.symbols } + pub fn get_gpio(&self) -> &Gpio { + &self.gpio + } } use super::sysbus::consts::*; diff --git a/rustboyadvance-core/src/cartridge/rtc.rs b/rustboyadvance-core/src/cartridge/rtc.rs index 6dc0b39..bae422d 100644 --- a/rustboyadvance-core/src/cartridge/rtc.rs +++ b/rustboyadvance-core/src/cartridge/rtc.rs @@ -7,7 +7,7 @@ use num::FromPrimitive; use std::cmp; -use super::gpio::{GpioDevice, GpioDirection, GpioPort, GpioState, GPIO_LOW}; +use super::gpio::{GpioDevice, GpioDirection, GpioState}; fn num2bcd(mut num: u8) -> u8 { num = cmp::min(num, 99); @@ -41,6 +41,30 @@ impl Port { } } +/// Struct holding the logical state of a serial port +#[repr(transparent)] +#[derive(Serialize, Deserialize, Copy, Clone, Debug)] +struct PortValue(u16); + +#[allow(dead_code)] +impl PortValue { + fn get(&self) -> u16 { + self.0 + } + + fn set(&mut self, value: u16) { + self.0 = value & 1; + } + + fn high(&self) -> bool { + self.0 != 0 + } + + fn low(&self) -> bool { + self.0 == 0 + } +} + /// RTC Registers codes in the GBA #[derive(Primitive, Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] enum RegisterKind { @@ -67,9 +91,9 @@ impl RegisterKind { } #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)] -enum State { +enum RtcState { Idle, - WaitForChipSelect, + WaitForChipSelectHigh, GetCommandByte, RxFromMaster { reg: RegisterKind, @@ -107,14 +131,14 @@ impl SerialBuffer { } #[inline] - fn pop_bit(&mut self) -> bool { + fn pop_bit(&mut self) -> Option { if self.counter > 0 { let result = self.byte.bit(0); self.byte = self.byte.wrapping_shr(1); self.counter -= 1; - result + Some(result) } else { - false + None } } @@ -163,10 +187,10 @@ impl SerialBuffer { /// Model of the S3511 8pin RTC #[derive(Serialize, Deserialize, Clone, Debug)] pub struct Rtc { - state: State, - sck: GpioPort, - sio: GpioPort, - cs: GpioPort, + state: RtcState, + sck: PortValue, + sio: PortValue, + cs: PortValue, status: registers::StatusRegister, serial_buffer: SerialBuffer, internal_buffer: [u8; 8], @@ -175,14 +199,11 @@ pub struct Rtc { impl Rtc { pub fn new() -> Self { Rtc { - state: State::Idle, - sck: GPIO_LOW, - sio: GPIO_LOW, - cs: GPIO_LOW, - status: registers::StatusRegister { - mode_24h: true, - ..Default::default() - }, + state: RtcState::Idle, + sck: PortValue(0), + sio: PortValue(0), + cs: PortValue(0), + status: registers::StatusRegister(0x82), serial_buffer: SerialBuffer::new(), internal_buffer: [0; 8], } @@ -194,9 +215,18 @@ impl Rtc { fn force_reset(&mut self) { self.serial_buffer.reset(); - self.state = State::Idle; + self.state = RtcState::Idle; self.status.write(0); - // TODO according to the datasheet, the date time registers should reset to 0-0-0-0.. + // TODO according to the S3511 datasheet, + // the date time registers should be reset to 0-0-0-0.. + } + + fn serial_transfer_in_progress(&self) -> bool { + use RtcState::*; + match self.state { + Idle | WaitForChipSelectHigh => false, + _ => true, + } } /// Loads a register contents into an internal buffer @@ -206,9 +236,9 @@ impl Rtc { RegisterKind::DateTime => { let local: DateTime = Local::now(); let year = local.year(); - assert!(year >= 2000 && year <= 2099); // Wonder if I will leave to see this assert fail + assert!(year >= 2000 && year <= 2099); // Wonder if I will live to see this one fail - let hour = if self.status.mode_24h { + let hour = if self.status.mode_24h() { local.hour() } else { let (_, hour12) = local.hour12(); @@ -222,15 +252,10 @@ impl Rtc { self.internal_buffer[4] = num2bcd(hour as u8); self.internal_buffer[5] = num2bcd(local.minute() as u8); self.internal_buffer[6] = num2bcd(local.second() as u8); - - println!( - "DateTime result: {:x?} dt={:?}", - self.internal_buffer, local - ); } RegisterKind::Time => { let local: DateTime = Local::now(); - let hour = if self.status.mode_24h { + let hour = if self.status.mode_24h() { local.hour() } else { let (_, hour12) = local.hour12(); @@ -246,7 +271,6 @@ impl Rtc { fn store_register(&mut self, r: RegisterKind) { use RegisterKind::*; - info!("write register {:?} {:?}", r, self.internal_buffer); match r { Status => self.status.write(self.internal_buffer[0]), ForceReset => self.force_reset(), @@ -266,7 +290,6 @@ impl GpioDevice for Rtc { self.sck.set(data.bit(Port::Sck.index()) as u16); self.cs.set(data.bit(Port::Cs.index()) as u16); - let sck_rising_edge = old_sck.low() && self.sck.high(); let sck_falling_edge = old_sck.high() && self.sck.low(); if sck_falling_edge && gpio_state[Port::Sio.index()] == GpioDirection::Out { @@ -274,17 +297,32 @@ impl GpioDevice for Rtc { } if self.cs.high() && old_cs.low() { - trace!("RTC CS went from low to high!"); + trace!("RTC: CS went from low to high!"); + } + + use RtcState::*; + + if self.cs.low() && self.serial_transfer_in_progress() { + debug!( + "RTC: CS set low from state {:?}, resetting state", + self.state + ); + self.serial_buffer.reset(); + self.state = Idle; + return; } - use State::*; match self.state { Idle => { if self.sck.high() && self.cs.low() { - self.state = WaitForChipSelect; + if self.cs.low() { + self.state = WaitForChipSelectHigh; + } else { + self.state = GetCommandByte; + } } } - WaitForChipSelect => { + WaitForChipSelectHigh => { if self.sck.high() && self.cs.high() { self.state = GetCommandByte; self.serial_buffer.reset(); @@ -319,10 +357,11 @@ impl GpioDevice for Rtc { let is_read_operation = command.bit(7); - info!( - "got command: {} {:?}", + debug!( + "RTC: got command: {} {:?} args len: {}", if is_read_operation { "READ" } else { "WRITE" }, - reg + reg, + byte_count ); if byte_count != 0 { @@ -332,20 +371,21 @@ impl GpioDevice for Rtc { byte_count, byte_index: 0, }; + self.serial_buffer.reset(); } else { self.state = RxFromMaster { reg, byte_count, byte_index: 0, }; + self.serial_buffer.reset(); } } else { assert!(!is_read_operation); self.store_register(reg); self.state = Idle; + self.serial_buffer.reset(); } - - self.serial_buffer.reset(); } TxToMaster { byte_count, @@ -355,24 +395,29 @@ impl GpioDevice for Rtc { return; } - let new_byte_index = if self.serial_buffer.is_empty() { - byte_index + 1 - } else { - byte_index - }; - - if byte_count == new_byte_index { - self.state = Idle; - return; - } - - if byte_index != new_byte_index { + let mut new_byte_index = byte_index; + let bit = if let Some(bit) = self.serial_buffer.pop_bit() { + bit + } else if byte_index < byte_count { self.serial_buffer .load_byte(self.internal_buffer[byte_index as usize]); - } + new_byte_index += 1; + self.serial_buffer.pop_bit().unwrap() + } else { + self.state = Idle; + self.serial_buffer.reset(); + return; + }; + trace!("RTC TX BIT {}", bit); assert_eq!(gpio_state[Port::Sio.index()], GpioDirection::In); - self.sio.set(self.serial_buffer.pop_bit() as u16); + self.sio.set(bit as u16); + + if self.serial_buffer.is_empty() && new_byte_index == byte_count { + self.state = Idle; + self.serial_buffer.reset(); + return; + } self.state = TxToMaster { byte_count, @@ -423,28 +468,27 @@ impl GpioDevice for Rtc { } mod registers { - use super::*; - #[derive(Serialize, Deserialize, Copy, Clone, Debug, Default)] - pub(super) struct StatusRegister { - pub(super) per_minute_irq: bool, - pub(super) mode_24h: bool, - pub(super) power_off: bool, + bitfield! { + #[derive(Serialize, Deserialize, Clone)] + pub struct StatusRegister(u8); + impl Debug; + u8; + pub intfe, set_intfe : 1; // unimplemented + pub intme, set_intme : 3; // unimplemented + pub intae, set_intae : 5; // unimplemented + pub mode_24h, set_mode_24h : 6; + pub power_fail, set_power_fail : 7; } impl StatusRegister { + const IGNORED_MASK: u8 = 0b1001_0101; pub(super) fn read(&self) -> u8 { - let mut result = 0; - result.set_bit(3, self.per_minute_irq); - result.set_bit(6, self.mode_24h); - result.set_bit(7, self.power_off); - result + self.0 } pub(super) fn write(&mut self, value: u8) { - self.per_minute_irq = value.bit(3); - self.mode_24h = value.bit(6); - self.power_off = value.bit(7); + self.0 = !Self::IGNORED_MASK & value; } } } @@ -478,15 +522,15 @@ mod tests { } fn start_serial_transfer(rtc: &mut Rtc, gpio_state: &GpioState) { - assert_eq!(rtc.state, State::Idle); + assert_eq!(rtc.state, RtcState::Idle); // set CS low, rtc.write(&gpio_state, 0b0001); - assert_eq!(rtc.state, State::WaitForChipSelect); + assert_eq!(rtc.state, RtcState::WaitForChipSelectHigh); // set CS high, SCK rising edge rtc.write(&gpio_state, 0b0101); - assert_eq!(rtc.state, State::GetCommandByte); + assert_eq!(rtc.state, RtcState::GetCommandByte); } #[test] @@ -514,7 +558,7 @@ mod tests { macro_rules! setup_rtc { ($rtc:ident, $gpio_state:ident) => { - let mut $rtc = Rtc::new(Rc::new(Cell::new(false))); + let mut $rtc = Rtc::new(); #[allow(unused_mut)] let mut $gpio_state = [ GpioDirection::Out, /* SCK */ @@ -529,37 +573,31 @@ mod tests { fn test_rtc_status() { setup_rtc!(rtc, gpio_state); - rtc.status.mode_24h = false; + rtc.status.set_mode_24h(false); start_serial_transfer(&mut rtc, &mut gpio_state); // write Status register command transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 0, 1, 0]); assert_eq!( rtc.state, - State::RxFromMaster { + RtcState::RxFromMaster { reg: RegisterKind::Status, byte_count: 1, byte_index: 0, } ); - assert_eq!(rtc.status.mode_24h, false); + assert_eq!(rtc.status.mode_24h(), false); let mut serial_buffer = SerialBuffer::new(); - serial_buffer.load_byte( - registers::StatusRegister { - mode_24h: true, - ..Default::default() - } - .read(), - ); + serial_buffer.load_byte(1 << 6); - while !serial_buffer.is_empty() { - transmit(&mut rtc, &gpio_state, serial_buffer.pop_bit() as u8); + while let Some(bit) = serial_buffer.pop_bit() { + transmit(&mut rtc, &gpio_state, bit as u8); } assert!(rtc.serial_buffer.is_empty()); - assert_eq!(rtc.status.mode_24h, true); + assert_eq!(rtc.status.mode_24h(), true); start_serial_transfer(&mut rtc, &mut gpio_state); @@ -567,7 +605,7 @@ mod tests { transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 0, 1, 1]); assert_eq!( rtc.state, - State::TxToMaster { + RtcState::TxToMaster { byte_count: 1, byte_index: 0, } @@ -579,10 +617,8 @@ mod tests { let mut bytes = [0]; receive_bytes(&mut rtc, &gpio_state, &mut bytes); - let mut status = registers::StatusRegister::default(); - status.write(bytes[0]); - - assert_eq!(status.mode_24h, true); + let mut read_status = registers::StatusRegister(bytes[0]); + assert_eq!(read_status.mode_24h(), true); } #[test] @@ -593,7 +629,7 @@ mod tests { transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 1, 0, 1]); assert_eq!( rtc.state, - State::TxToMaster { + RtcState::TxToMaster { byte_count: 7, byte_index: 0 } @@ -602,7 +638,7 @@ mod tests { gpio_state[Port::Sio.index()] = GpioDirection::In; let mut bytes = [0; 7]; receive_bytes(&mut rtc, &gpio_state, &mut bytes); - assert_eq!(rtc.state, State::Idle); + assert_eq!(rtc.state, RtcState::Idle); println!("{:x?}", bytes); let local: DateTime = Local::now(); diff --git a/rustboyadvance-core/src/debugger/command.rs b/rustboyadvance-core/src/debugger/command.rs index 07459ba..22e3e43 100644 --- a/rustboyadvance-core/src/debugger/command.rs +++ b/rustboyadvance-core/src/debugger/command.rs @@ -46,6 +46,7 @@ bitflags! { pub enum Command { Info, GpuInfo, + GpioInfo, Step(usize), Continue, Frame(usize), @@ -78,6 +79,9 @@ impl Debugger { println!("IF={:#?}", self.gba.sysbus.io.intc.interrupt_flags); } GpuInfo => println!("GPU: {:#?}", self.gba.sysbus.io.gpu), + GpioInfo => { + println!("GPIO: {:#?}", self.gba.sysbus.cartridge.get_gpio()); + } Step(count) => { for _ in 0..count { self.gba.cpu.step(&mut self.gba.sysbus); @@ -288,6 +292,7 @@ impl Debugger { match command.as_ref() { "i" | "info" => Ok(Command::Info), "gpuinfo" => Ok(Command::GpuInfo), + "gpio" => Ok(Command::GpioInfo), "s" | "step" => { let count = match args.len() { 0 => 1,