1//! EEPROM save memory code
2//!
3//! Comes in 512-byte and 8KB variants; protocol is the same except for the number of address bits
4
5use bincode::{Decode, Encode};
6use jgenesis_common::num::GetBit;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Encode, Decode)]
9enum Request {
10    Read,
11    Write,
12}
13
14#[derive(Debug, Clone, Copy, Encode, Decode)]
15enum EepromState {
16    Idle,
17    ReceivingRequestType,
18    ReceivingAddress { address: u16, remaining: u8, request: Request },
19    ReceivingData { bits: u64, remaining: u8, address: u16 },
20    PreparingSend { bits: u64, wait_remaining: u8 },
21    SendingData { bits: u64, remaining: u8 },
22    AwaitingEndMarker { request: Request, address: u16 },
23}
24
25#[derive(Debug, Clone, Encode, Decode)]
26pub struct Eeprom<const LEN: usize, const ADDRESS_BITS: u8> {
27    memory: Box<[u8]>,
28    state: EepromState,
29}
30
31pub type Eeprom512 = Eeprom<512, 6>;
32pub type Eeprom8K = Eeprom<8192, 14>;
33
34impl<const LEN: usize, const ADDRESS_BITS: u8> Eeprom<LEN, ADDRESS_BITS> {
35    pub fn new(initial_save: Option<&Vec<u8>>) -> Self {
36        let mut memory = vec![0xFF; LEN].into_boxed_slice();
37
38        if let Some(initial_save) = initial_save
39            && initial_save.len() >= LEN
40        {
41            memory.copy_from_slice(&initial_save[..LEN]);
42        }
43
44        Self { memory, state: EepromState::Idle }
45    }
46
47    pub fn read(&mut self) -> bool {
48        log::trace!("EEPROM read, current state {:X?}", self.state);
49
50        match self.state {
51            EepromState::PreparingSend { bits, mut wait_remaining } => {
52                wait_remaining -= 1;
53                self.state = if wait_remaining == 0 {
54                    EepromState::SendingData { bits, remaining: 64 }
55                } else {
56                    EepromState::PreparingSend { bits, wait_remaining }
57                };
58                true
59            }
60            EepromState::SendingData { mut bits, mut remaining } => {
61                let bit = bits.bit(63);
62                bits <<= 1;
63                remaining -= 1;
64
65                self.state = if remaining == 0 {
66                    EepromState::Idle
67                } else {
68                    EepromState::SendingData { bits, remaining }
69                };
70
71                bit
72            }
73            _ => true,
74        }
75    }
76
77    pub fn write(&mut self, bit: bool) {
78        log::trace!("EEPROM write {}, current state {:X?}", u8::from(bit), self.state);
79
80        self.state = match self.state {
81            EepromState::Idle => {
82                if bit {
83                    EepromState::ReceivingRequestType
84                } else {
85                    EepromState::Idle
86                }
87            }
88            EepromState::ReceivingRequestType => EepromState::ReceivingAddress {
89                address: 0,
90                remaining: ADDRESS_BITS,
91                request: if bit { Request::Read } else { Request::Write },
92            },
93            EepromState::ReceivingAddress { mut address, mut remaining, request } => {
94                address = (address << 1) | u16::from(bit);
95                remaining -= 1;
96
97                if remaining == 0 {
98                    match request {
99                        Request::Read => EepromState::AwaitingEndMarker { request, address },
100                        Request::Write => {
101                            EepromState::ReceivingData { bits: 0, remaining: 64, address }
102                        }
103                    }
104                } else {
105                    EepromState::ReceivingAddress { address, remaining, request }
106                }
107            }
108            EepromState::ReceivingData { mut bits, mut remaining, address } => {
109                bits = (bits << 1) | u64::from(bit);
110                remaining -= 1;
111
112                if remaining == 0 {
113                    let byte_addr = ((address << 3) as usize) & (LEN - 1);
114                    self.memory[byte_addr..byte_addr + 8].copy_from_slice(&bits.to_be_bytes());
115                    EepromState::AwaitingEndMarker { request: Request::Write, address }
116                } else {
117                    EepromState::ReceivingData { bits, remaining, address }
118                }
119            }
120            EepromState::PreparingSend { bits, wait_remaining } => {
121                EepromState::PreparingSend { bits, wait_remaining }
122            }
123            EepromState::SendingData { bits, remaining } => {
124                EepromState::SendingData { bits, remaining }
125            }
126            EepromState::AwaitingEndMarker { request, address } => match request {
127                Request::Read => {
128                    let byte_addr = ((address << 3) as usize) & (LEN - 1);
129                    let bits = u64::from_be_bytes(
130                        self.memory[byte_addr..byte_addr + 8].try_into().unwrap(),
131                    );
132                    EepromState::PreparingSend { bits, wait_remaining: 4 }
133                }
134                Request::Write => EepromState::Idle,
135            },
136        };
137
138        log::trace!("  New state {:X?}", self.state);
139    }
140
141    pub fn memory(&self) -> &[u8] {
142        &self.memory
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    fn eeprom_write<const LEN: usize, const ADDRESS_BITS: u8>(
151        eeprom: &mut Eeprom<LEN, ADDRESS_BITS>,
152        address: u16,
153        value: u64,
154    ) {
155        // Write: 10, then address (MSB first), then data, then 0
156        eeprom.write(true);
157        eeprom.write(false);
158
159        let mut address_shift = address;
160        for _ in 0..ADDRESS_BITS {
161            eeprom.write(address_shift.bit(ADDRESS_BITS - 1));
162            address_shift <<= 1;
163        }
164
165        let mut value_shift = value;
166        for _ in 0..64 {
167            eeprom.write(value_shift.bit(63));
168            value_shift <<= 1;
169        }
170
171        eeprom.write(false);
172    }
173
174    fn eeprom_read<const LEN: usize, const ADDRESS_BITS: u8>(
175        eeprom: &mut Eeprom<LEN, ADDRESS_BITS>,
176        address: u16,
177    ) -> u64 {
178        // Read: 10, then address (MSB first), then 0, then read data (ignore first 4 bits)
179        eeprom.write(true);
180        eeprom.write(true);
181
182        let mut address_shift = address;
183        for _ in 0..ADDRESS_BITS {
184            eeprom.write(address_shift.bit(ADDRESS_BITS - 1));
185            address_shift <<= 1;
186        }
187
188        eeprom.write(false);
189
190        // Skip first 4 bits
191        for _ in 0..4 {
192            eeprom.read();
193        }
194
195        let mut value: u64 = 0;
196        for _ in 0..64 {
197            value = (value << 1) | u64::from(eeprom.read());
198        }
199
200        value
201    }
202
203    fn write_then_read<const LEN: usize, const ADDRESS_BITS: u8>(
204        mut eeprom: Eeprom<LEN, ADDRESS_BITS>,
205    ) {
206        for _ in 0..100 {
207            eeprom.memory.fill(0);
208
209            let address: u16 = rand::random();
210
211            let mut value: u64 = 0;
212            while value == 0 {
213                value = rand::random();
214            }
215
216            eeprom_write(&mut eeprom, address, value);
217            assert_eq!(value, eeprom_read(&mut eeprom, address));
218            assert_eq!(0, eeprom_read(&mut eeprom, address.wrapping_add(1)));
219            assert_eq!(0, eeprom_read(&mut eeprom, address.wrapping_sub(1)));
220        }
221    }
222
223    #[test]
224    fn write_then_read_512() {
225        write_then_read(Eeprom512::new(None));
226    }
227
228    #[test]
229    fn write_then_read_8k() {
230        write_then_read(Eeprom8K::new(None));
231
232        // Test that highest 4 address bits are ignored
233        let mut eeprom = Eeprom8K::new(None);
234        eeprom.memory.fill(0);
235
236        let value = 0x0123456789ABCDEF;
237        eeprom_write(&mut eeprom, 0x001F, value);
238        assert_eq!(value, eeprom_read(&mut eeprom, 0x001F));
239        assert_eq!(value, eeprom_read(&mut eeprom, 0xFC1F));
240        assert_eq!(0, eeprom_read(&mut eeprom, 0xFE1F));
241    }
242}