Cartridge GPIO port
Used to communicate with any cartridge peripherals (RTC, solar sensor, gyro sensor, etc.)
12define_bit_enum!(GpioPinDirection, [Input, Output]); 13define_bit_enum!(GpioMode, [WriteOnly, ReadWrite]); 14 15#[derive(Debug, Clone, Encode, Decode)] 16pub struct GpioPort { 17 pin_directions: [GpioPinDirection; 4], 18 mode: GpioMode, 19} 20 21impl GpioPort { 22 pub fn new() -> Self { 23 Self { pin_directions: [GpioPinDirection::default(); 4], mode: GpioMode::default() } 24 } 25 26 // $080000C4 27 pub fn write_data( 28 &mut self, 29 value: u16, 30 rtc: Option<&mut SeikoRealTimeClock>, 31 solar: Option<&mut SolarSensor>, 32 ) { 33 let pin = |i: u8| match self.pin_directions[i as usize] { 34 GpioPinDirection::Input => true, 35 GpioPinDirection::Output => value.bit(i), 36 }; 37 38 log::trace!("GPIO data write: {:04b}", value & 0b1111); 39 40 if let Some(rtc) = rtc { 41 rtc.write(RtcWrite { chip_select: pin(2), clock: pin(0), data: pin(1) }); 42 } 43 44 if let Some(solar) = solar { 45 solar.write(SolarSensorWrite { reset: pin(1), clock: pin(0) }); 46 } 47 } 48 49 // $080000C4 50 pub fn read_data( 51 &self, 52 rtc: Option<&SeikoRealTimeClock>, 53 solar: Option<&SolarSensor>, 54 ) -> Option<u16> { 55 if self.mode == GpioMode::WriteOnly { 56 return None; 57 } 58 59 let data1 = match self.pin_directions[1] { 60 GpioPinDirection::Input => rtc.is_none_or(SeikoRealTimeClock::read), 61 GpioPinDirection::Output => true, 62 }; 63 64 let data3 = match self.pin_directions[3] { 65 GpioPinDirection::Input => solar.is_none_or(SolarSensor::read), 66 GpioPinDirection::Output => true, 67 }; 68 69 Some(0b0101 | (u16::from(data1) << 1) | (u16::from(data3) << 3)) 70 } 71 72 // $080000C6 73 pub fn write_pin_directions(&mut self, value: u16) { 74 self.pin_directions = array::from_fn(|i| GpioPinDirection::from_bit(value.bit(i as u8))); 75 76 log::trace!("GPIO pin directions write ({value:04X}): {:?}", self.pin_directions); 77 } 78 79 // $080000C6 80 pub fn read_pin_directions(&self) -> Option<u16> { 81 if self.mode == GpioMode::WriteOnly { 82 return None; 83 } 84 85 let value = self 86 .pin_directions 87 .into_iter() 88 .enumerate() 89 .map(|(i, direction)| (direction as u16) << i) 90 .reduce(|a, b| a | b) 91 .unwrap(); 92 93 Some(value) 94 } 95 96 // $080000C8 97 pub fn write_mode(&mut self, value: u16) { 98 self.mode = GpioMode::from_bit(value.bit(0)); 99 100 log::debug!("GPIO mode write ({value:04X}): {:?}", self.mode); 101 } 102 103 // $080000C8 104 pub fn read_mode(&self) -> Option<u16> { 105 if self.mode == GpioMode::WriteOnly { 106 return None; 107 } 108 109 Some(self.mode as u16) 110 } 111}