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:
parent
58c9d10360
commit
3839b8eb02
|
@ -16,6 +16,7 @@ use super::Cartridge;
|
||||||
use super::loader::{load_from_bytes, load_from_file, LoadRom};
|
use super::loader::{load_from_bytes, load_from_file, LoadRom};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
#[allow(dead_code)]
|
||||||
pub enum GpioDeviceType {
|
pub enum GpioDeviceType {
|
||||||
Rtc,
|
Rtc,
|
||||||
SolarSensor,
|
SolarSensor,
|
||||||
|
@ -136,7 +137,10 @@ impl GamepakBuilder {
|
||||||
let mut gpio_device = self.gpio_device;
|
let mut gpio_device = self.gpio_device;
|
||||||
|
|
||||||
if let Some(overrides) = overrides::get_game_overrides(&header.game_code) {
|
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 let Some(override_save_type) = overrides.save_type() {
|
||||||
if override_save_type != save_type && save_type != BackupType::AutoDetect {
|
if override_save_type != save_type && save_type != BackupType::AutoDetect {
|
||||||
warn!(
|
warn!(
|
||||||
|
@ -150,7 +154,7 @@ impl GamepakBuilder {
|
||||||
if overrides.force_rtc() {
|
if overrides.force_rtc() {
|
||||||
match gpio_device {
|
match gpio_device {
|
||||||
GpioDeviceType::None => gpio_device = GpioDeviceType::Rtc,
|
GpioDeviceType::None => gpio_device = GpioDeviceType::Rtc,
|
||||||
GpioDeviceType::Rtc => {},
|
GpioDeviceType::Rtc => {}
|
||||||
_ => {
|
_ => {
|
||||||
warn!(
|
warn!(
|
||||||
"Can't use RTC due to forced gpio device type {:?}",
|
"Can't use RTC due to forced gpio device type {:?}",
|
||||||
|
|
|
@ -4,32 +4,6 @@ use super::{GPIO_PORT_CONTROL, GPIO_PORT_DATA, GPIO_PORT_DIRECTION};
|
||||||
use bit::BitIndex;
|
use bit::BitIndex;
|
||||||
use serde::{Deserialize, Serialize};
|
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)]
|
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum GpioDirection {
|
pub enum GpioDirection {
|
||||||
/// GPIO to GBA
|
/// GPIO to GBA
|
||||||
|
@ -53,7 +27,7 @@ pub trait GpioDevice: Sized {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct Gpio {
|
pub struct Gpio {
|
||||||
rtc: Option<Rtc>,
|
pub(in crate) rtc: Option<Rtc>,
|
||||||
direction: GpioState,
|
direction: GpioState,
|
||||||
control: GpioPortControl,
|
control: GpioPortControl,
|
||||||
}
|
}
|
||||||
|
@ -117,7 +91,6 @@ impl Gpio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GPIO_PORT_CONTROL => {
|
GPIO_PORT_CONTROL => {
|
||||||
info!("GPIO port control: {:?}", self.control);
|
|
||||||
self.control = if value != 0 {
|
self.control = if value != 0 {
|
||||||
GpioPortControl::ReadWrite
|
GpioPortControl::ReadWrite
|
||||||
} else {
|
} else {
|
||||||
|
@ -128,11 +101,3 @@ impl Gpio {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn test_gpio() {
|
|
||||||
unimplemented!();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -49,6 +49,9 @@ impl Cartridge {
|
||||||
pub fn get_symbols(&self) -> &Option<SymbolTable> {
|
pub fn get_symbols(&self) -> &Option<SymbolTable> {
|
||||||
&self.symbols
|
&self.symbols
|
||||||
}
|
}
|
||||||
|
pub fn get_gpio(&self) -> &Gpio {
|
||||||
|
&self.gpio
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::sysbus::consts::*;
|
use super::sysbus::consts::*;
|
||||||
|
|
|
@ -7,7 +7,7 @@ use num::FromPrimitive;
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
|
||||||
use super::gpio::{GpioDevice, GpioDirection, GpioPort, GpioState, GPIO_LOW};
|
use super::gpio::{GpioDevice, GpioDirection, GpioState};
|
||||||
|
|
||||||
fn num2bcd(mut num: u8) -> u8 {
|
fn num2bcd(mut num: u8) -> u8 {
|
||||||
num = cmp::min(num, 99);
|
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
|
/// RTC Registers codes in the GBA
|
||||||
#[derive(Primitive, Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
#[derive(Primitive, Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||||
enum RegisterKind {
|
enum RegisterKind {
|
||||||
|
@ -67,9 +91,9 @@ impl RegisterKind {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq)]
|
||||||
enum State {
|
enum RtcState {
|
||||||
Idle,
|
Idle,
|
||||||
WaitForChipSelect,
|
WaitForChipSelectHigh,
|
||||||
GetCommandByte,
|
GetCommandByte,
|
||||||
RxFromMaster {
|
RxFromMaster {
|
||||||
reg: RegisterKind,
|
reg: RegisterKind,
|
||||||
|
@ -107,14 +131,14 @@ impl SerialBuffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pop_bit(&mut self) -> bool {
|
fn pop_bit(&mut self) -> Option<bool> {
|
||||||
if self.counter > 0 {
|
if self.counter > 0 {
|
||||||
let result = self.byte.bit(0);
|
let result = self.byte.bit(0);
|
||||||
self.byte = self.byte.wrapping_shr(1);
|
self.byte = self.byte.wrapping_shr(1);
|
||||||
self.counter -= 1;
|
self.counter -= 1;
|
||||||
result
|
Some(result)
|
||||||
} else {
|
} else {
|
||||||
false
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,10 +187,10 @@ impl SerialBuffer {
|
||||||
/// Model of the S3511 8pin RTC
|
/// Model of the S3511 8pin RTC
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct Rtc {
|
pub struct Rtc {
|
||||||
state: State,
|
state: RtcState,
|
||||||
sck: GpioPort,
|
sck: PortValue,
|
||||||
sio: GpioPort,
|
sio: PortValue,
|
||||||
cs: GpioPort,
|
cs: PortValue,
|
||||||
status: registers::StatusRegister,
|
status: registers::StatusRegister,
|
||||||
serial_buffer: SerialBuffer,
|
serial_buffer: SerialBuffer,
|
||||||
internal_buffer: [u8; 8],
|
internal_buffer: [u8; 8],
|
||||||
|
@ -175,14 +199,11 @@ pub struct Rtc {
|
||||||
impl Rtc {
|
impl Rtc {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Rtc {
|
Rtc {
|
||||||
state: State::Idle,
|
state: RtcState::Idle,
|
||||||
sck: GPIO_LOW,
|
sck: PortValue(0),
|
||||||
sio: GPIO_LOW,
|
sio: PortValue(0),
|
||||||
cs: GPIO_LOW,
|
cs: PortValue(0),
|
||||||
status: registers::StatusRegister {
|
status: registers::StatusRegister(0x82),
|
||||||
mode_24h: true,
|
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
serial_buffer: SerialBuffer::new(),
|
serial_buffer: SerialBuffer::new(),
|
||||||
internal_buffer: [0; 8],
|
internal_buffer: [0; 8],
|
||||||
}
|
}
|
||||||
|
@ -194,9 +215,18 @@ impl Rtc {
|
||||||
|
|
||||||
fn force_reset(&mut self) {
|
fn force_reset(&mut self) {
|
||||||
self.serial_buffer.reset();
|
self.serial_buffer.reset();
|
||||||
self.state = State::Idle;
|
self.state = RtcState::Idle;
|
||||||
self.status.write(0);
|
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
|
/// Loads a register contents into an internal buffer
|
||||||
|
@ -206,9 +236,9 @@ impl Rtc {
|
||||||
RegisterKind::DateTime => {
|
RegisterKind::DateTime => {
|
||||||
let local: DateTime<Local> = Local::now();
|
let local: DateTime<Local> = Local::now();
|
||||||
let year = local.year();
|
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()
|
local.hour()
|
||||||
} else {
|
} else {
|
||||||
let (_, hour12) = local.hour12();
|
let (_, hour12) = local.hour12();
|
||||||
|
@ -222,15 +252,10 @@ impl Rtc {
|
||||||
self.internal_buffer[4] = num2bcd(hour as u8);
|
self.internal_buffer[4] = num2bcd(hour as u8);
|
||||||
self.internal_buffer[5] = num2bcd(local.minute() as u8);
|
self.internal_buffer[5] = num2bcd(local.minute() as u8);
|
||||||
self.internal_buffer[6] = num2bcd(local.second() as u8);
|
self.internal_buffer[6] = num2bcd(local.second() as u8);
|
||||||
|
|
||||||
println!(
|
|
||||||
"DateTime result: {:x?} dt={:?}",
|
|
||||||
self.internal_buffer, local
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
RegisterKind::Time => {
|
RegisterKind::Time => {
|
||||||
let local: DateTime<Local> = Local::now();
|
let local: DateTime<Local> = Local::now();
|
||||||
let hour = if self.status.mode_24h {
|
let hour = if self.status.mode_24h() {
|
||||||
local.hour()
|
local.hour()
|
||||||
} else {
|
} else {
|
||||||
let (_, hour12) = local.hour12();
|
let (_, hour12) = local.hour12();
|
||||||
|
@ -246,7 +271,6 @@ impl Rtc {
|
||||||
|
|
||||||
fn store_register(&mut self, r: RegisterKind) {
|
fn store_register(&mut self, r: RegisterKind) {
|
||||||
use RegisterKind::*;
|
use RegisterKind::*;
|
||||||
info!("write register {:?} {:?}", r, self.internal_buffer);
|
|
||||||
match r {
|
match r {
|
||||||
Status => self.status.write(self.internal_buffer[0]),
|
Status => self.status.write(self.internal_buffer[0]),
|
||||||
ForceReset => self.force_reset(),
|
ForceReset => self.force_reset(),
|
||||||
|
@ -266,7 +290,6 @@ impl GpioDevice for Rtc {
|
||||||
self.sck.set(data.bit(Port::Sck.index()) as u16);
|
self.sck.set(data.bit(Port::Sck.index()) as u16);
|
||||||
self.cs.set(data.bit(Port::Cs.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();
|
let sck_falling_edge = old_sck.high() && self.sck.low();
|
||||||
|
|
||||||
if sck_falling_edge && gpio_state[Port::Sio.index()] == GpioDirection::Out {
|
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() {
|
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 {
|
match self.state {
|
||||||
Idle => {
|
Idle => {
|
||||||
if self.sck.high() && self.cs.low() {
|
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() {
|
if self.sck.high() && self.cs.high() {
|
||||||
self.state = GetCommandByte;
|
self.state = GetCommandByte;
|
||||||
self.serial_buffer.reset();
|
self.serial_buffer.reset();
|
||||||
|
@ -319,10 +357,11 @@ impl GpioDevice for Rtc {
|
||||||
|
|
||||||
let is_read_operation = command.bit(7);
|
let is_read_operation = command.bit(7);
|
||||||
|
|
||||||
info!(
|
debug!(
|
||||||
"got command: {} {:?}",
|
"RTC: got command: {} {:?} args len: {}",
|
||||||
if is_read_operation { "READ" } else { "WRITE" },
|
if is_read_operation { "READ" } else { "WRITE" },
|
||||||
reg
|
reg,
|
||||||
|
byte_count
|
||||||
);
|
);
|
||||||
|
|
||||||
if byte_count != 0 {
|
if byte_count != 0 {
|
||||||
|
@ -332,21 +371,22 @@ impl GpioDevice for Rtc {
|
||||||
byte_count,
|
byte_count,
|
||||||
byte_index: 0,
|
byte_index: 0,
|
||||||
};
|
};
|
||||||
|
self.serial_buffer.reset();
|
||||||
} else {
|
} else {
|
||||||
self.state = RxFromMaster {
|
self.state = RxFromMaster {
|
||||||
reg,
|
reg,
|
||||||
byte_count,
|
byte_count,
|
||||||
byte_index: 0,
|
byte_index: 0,
|
||||||
};
|
};
|
||||||
|
self.serial_buffer.reset();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
assert!(!is_read_operation);
|
assert!(!is_read_operation);
|
||||||
self.store_register(reg);
|
self.store_register(reg);
|
||||||
self.state = Idle;
|
self.state = Idle;
|
||||||
}
|
|
||||||
|
|
||||||
self.serial_buffer.reset();
|
self.serial_buffer.reset();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
TxToMaster {
|
TxToMaster {
|
||||||
byte_count,
|
byte_count,
|
||||||
byte_index,
|
byte_index,
|
||||||
|
@ -355,24 +395,29 @@ impl GpioDevice for Rtc {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_byte_index = if self.serial_buffer.is_empty() {
|
let mut new_byte_index = byte_index;
|
||||||
byte_index + 1
|
let bit = if let Some(bit) = self.serial_buffer.pop_bit() {
|
||||||
} else {
|
bit
|
||||||
byte_index
|
} else if byte_index < byte_count {
|
||||||
};
|
|
||||||
|
|
||||||
if byte_count == new_byte_index {
|
|
||||||
self.state = Idle;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if byte_index != new_byte_index {
|
|
||||||
self.serial_buffer
|
self.serial_buffer
|
||||||
.load_byte(self.internal_buffer[byte_index as usize]);
|
.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);
|
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 {
|
self.state = TxToMaster {
|
||||||
byte_count,
|
byte_count,
|
||||||
|
@ -423,28 +468,27 @@ impl GpioDevice for Rtc {
|
||||||
}
|
}
|
||||||
|
|
||||||
mod registers {
|
mod registers {
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Copy, Clone, Debug, Default)]
|
bitfield! {
|
||||||
pub(super) struct StatusRegister {
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub(super) per_minute_irq: bool,
|
pub struct StatusRegister(u8);
|
||||||
pub(super) mode_24h: bool,
|
impl Debug;
|
||||||
pub(super) power_off: bool,
|
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 {
|
impl StatusRegister {
|
||||||
|
const IGNORED_MASK: u8 = 0b1001_0101;
|
||||||
pub(super) fn read(&self) -> u8 {
|
pub(super) fn read(&self) -> u8 {
|
||||||
let mut result = 0;
|
self.0
|
||||||
result.set_bit(3, self.per_minute_irq);
|
|
||||||
result.set_bit(6, self.mode_24h);
|
|
||||||
result.set_bit(7, self.power_off);
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn write(&mut self, value: u8) {
|
pub(super) fn write(&mut self, value: u8) {
|
||||||
self.per_minute_irq = value.bit(3);
|
self.0 = !Self::IGNORED_MASK & value;
|
||||||
self.mode_24h = value.bit(6);
|
|
||||||
self.power_off = value.bit(7);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -478,15 +522,15 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_serial_transfer(rtc: &mut Rtc, gpio_state: &GpioState) {
|
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,
|
// set CS low,
|
||||||
rtc.write(&gpio_state, 0b0001);
|
rtc.write(&gpio_state, 0b0001);
|
||||||
assert_eq!(rtc.state, State::WaitForChipSelect);
|
assert_eq!(rtc.state, RtcState::WaitForChipSelectHigh);
|
||||||
|
|
||||||
// set CS high, SCK rising edge
|
// set CS high, SCK rising edge
|
||||||
rtc.write(&gpio_state, 0b0101);
|
rtc.write(&gpio_state, 0b0101);
|
||||||
assert_eq!(rtc.state, State::GetCommandByte);
|
assert_eq!(rtc.state, RtcState::GetCommandByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -514,7 +558,7 @@ mod tests {
|
||||||
|
|
||||||
macro_rules! setup_rtc {
|
macro_rules! setup_rtc {
|
||||||
($rtc:ident, $gpio_state:ident) => {
|
($rtc:ident, $gpio_state:ident) => {
|
||||||
let mut $rtc = Rtc::new(Rc::new(Cell::new(false)));
|
let mut $rtc = Rtc::new();
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut $gpio_state = [
|
let mut $gpio_state = [
|
||||||
GpioDirection::Out, /* SCK */
|
GpioDirection::Out, /* SCK */
|
||||||
|
@ -529,37 +573,31 @@ mod tests {
|
||||||
fn test_rtc_status() {
|
fn test_rtc_status() {
|
||||||
setup_rtc!(rtc, gpio_state);
|
setup_rtc!(rtc, gpio_state);
|
||||||
|
|
||||||
rtc.status.mode_24h = false;
|
rtc.status.set_mode_24h(false);
|
||||||
start_serial_transfer(&mut rtc, &mut gpio_state);
|
start_serial_transfer(&mut rtc, &mut gpio_state);
|
||||||
|
|
||||||
// write Status register command
|
// write Status register command
|
||||||
transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 0, 1, 0]);
|
transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 0, 1, 0]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
rtc.state,
|
rtc.state,
|
||||||
State::RxFromMaster {
|
RtcState::RxFromMaster {
|
||||||
reg: RegisterKind::Status,
|
reg: RegisterKind::Status,
|
||||||
byte_count: 1,
|
byte_count: 1,
|
||||||
byte_index: 0,
|
byte_index: 0,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(rtc.status.mode_24h, false);
|
assert_eq!(rtc.status.mode_24h(), false);
|
||||||
|
|
||||||
let mut serial_buffer = SerialBuffer::new();
|
let mut serial_buffer = SerialBuffer::new();
|
||||||
serial_buffer.load_byte(
|
serial_buffer.load_byte(1 << 6);
|
||||||
registers::StatusRegister {
|
|
||||||
mode_24h: true,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
.read(),
|
|
||||||
);
|
|
||||||
|
|
||||||
while !serial_buffer.is_empty() {
|
while let Some(bit) = serial_buffer.pop_bit() {
|
||||||
transmit(&mut rtc, &gpio_state, serial_buffer.pop_bit() as u8);
|
transmit(&mut rtc, &gpio_state, bit as u8);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert!(rtc.serial_buffer.is_empty());
|
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);
|
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]);
|
transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 0, 1, 1]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
rtc.state,
|
rtc.state,
|
||||||
State::TxToMaster {
|
RtcState::TxToMaster {
|
||||||
byte_count: 1,
|
byte_count: 1,
|
||||||
byte_index: 0,
|
byte_index: 0,
|
||||||
}
|
}
|
||||||
|
@ -579,10 +617,8 @@ mod tests {
|
||||||
let mut bytes = [0];
|
let mut bytes = [0];
|
||||||
receive_bytes(&mut rtc, &gpio_state, &mut bytes);
|
receive_bytes(&mut rtc, &gpio_state, &mut bytes);
|
||||||
|
|
||||||
let mut status = registers::StatusRegister::default();
|
let mut read_status = registers::StatusRegister(bytes[0]);
|
||||||
status.write(bytes[0]);
|
assert_eq!(read_status.mode_24h(), true);
|
||||||
|
|
||||||
assert_eq!(status.mode_24h, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -593,7 +629,7 @@ mod tests {
|
||||||
transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 1, 0, 1]);
|
transmit_bits(&mut rtc, &gpio_state, &[0, 1, 1, 0, 0, 1, 0, 1]);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
rtc.state,
|
rtc.state,
|
||||||
State::TxToMaster {
|
RtcState::TxToMaster {
|
||||||
byte_count: 7,
|
byte_count: 7,
|
||||||
byte_index: 0
|
byte_index: 0
|
||||||
}
|
}
|
||||||
|
@ -602,7 +638,7 @@ mod tests {
|
||||||
gpio_state[Port::Sio.index()] = GpioDirection::In;
|
gpio_state[Port::Sio.index()] = GpioDirection::In;
|
||||||
let mut bytes = [0; 7];
|
let mut bytes = [0; 7];
|
||||||
receive_bytes(&mut rtc, &gpio_state, &mut bytes);
|
receive_bytes(&mut rtc, &gpio_state, &mut bytes);
|
||||||
assert_eq!(rtc.state, State::Idle);
|
assert_eq!(rtc.state, RtcState::Idle);
|
||||||
|
|
||||||
println!("{:x?}", bytes);
|
println!("{:x?}", bytes);
|
||||||
let local: DateTime<Local> = Local::now();
|
let local: DateTime<Local> = Local::now();
|
||||||
|
|
|
@ -46,6 +46,7 @@ bitflags! {
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
Info,
|
Info,
|
||||||
GpuInfo,
|
GpuInfo,
|
||||||
|
GpioInfo,
|
||||||
Step(usize),
|
Step(usize),
|
||||||
Continue,
|
Continue,
|
||||||
Frame(usize),
|
Frame(usize),
|
||||||
|
@ -78,6 +79,9 @@ impl Debugger {
|
||||||
println!("IF={:#?}", self.gba.sysbus.io.intc.interrupt_flags);
|
println!("IF={:#?}", self.gba.sysbus.io.intc.interrupt_flags);
|
||||||
}
|
}
|
||||||
GpuInfo => println!("GPU: {:#?}", self.gba.sysbus.io.gpu),
|
GpuInfo => println!("GPU: {:#?}", self.gba.sysbus.io.gpu),
|
||||||
|
GpioInfo => {
|
||||||
|
println!("GPIO: {:#?}", self.gba.sysbus.cartridge.get_gpio());
|
||||||
|
}
|
||||||
Step(count) => {
|
Step(count) => {
|
||||||
for _ in 0..count {
|
for _ in 0..count {
|
||||||
self.gba.cpu.step(&mut self.gba.sysbus);
|
self.gba.cpu.step(&mut self.gba.sysbus);
|
||||||
|
@ -288,6 +292,7 @@ impl Debugger {
|
||||||
match command.as_ref() {
|
match command.as_ref() {
|
||||||
"i" | "info" => Ok(Command::Info),
|
"i" | "info" => Ok(Command::Info),
|
||||||
"gpuinfo" => Ok(Command::GpuInfo),
|
"gpuinfo" => Ok(Command::GpuInfo),
|
||||||
|
"gpio" => Ok(Command::GpioInfo),
|
||||||
"s" | "step" => {
|
"s" | "step" => {
|
||||||
let count = match args.len() {
|
let count = match args.len() {
|
||||||
0 => 1,
|
0 => 1,
|
||||||
|
|
Reference in a new issue