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
This commit is contained in:
Michel Heily 2020-05-21 22:10:18 +03:00 committed by MishMish
parent 58c9d10360
commit 3839b8eb02
5 changed files with 142 additions and 129 deletions

View file

@ -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 {:?}",

View file

@ -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<Rtc>,
pub(in crate) rtc: Option<Rtc>,
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!();
}
}

View file

@ -49,6 +49,9 @@ impl Cartridge {
pub fn get_symbols(&self) -> &Option<SymbolTable> {
&self.symbols
}
pub fn get_gpio(&self) -> &Gpio {
&self.gpio
}
}
use super::sysbus::consts::*;

View file

@ -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<bool> {
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> = 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> = 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,21 +371,22 @@ 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();
}
}
TxToMaster {
byte_count,
byte_index,
@ -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> = Local::now();

View file

@ -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,