Skip to main content

gb_pak/
eeprom.rs

1//! MBC7's save storage: 128 words on a serial EEPROM.
2//!
3//! MBC7 carries no SRAM. In its place is a 93LC56 chip reached one bit at a
4//! time through a single port, so this is a driver rather than a window: there is
5//! nothing to map and no [`sram`](mod@crate::sram) scope to open.
6//!
7//! Words are 16 bits and addressed 0 to 127, giving 256 bytes. A write is slow:
8//! the command goes out one bit at a time and the chip then takes time of its
9//! own, so write a save in one pass rather than a field at a time.
10//!
11//! The chip starts locked. [`unlock`] allows writes and [`lock`] refuses them
12//! again.
13
14use core::ptr::{read_volatile, write_volatile};
15
16use crate::{WINDOW, reg};
17
18const PORT: *mut u8 = (WINDOW + 0x80) as *mut u8;
19
20const CS: u8 = 0x80;
21const CLK: u8 = 0x40;
22const DI: u8 = 0x02;
23const DO: u8 = 0x01;
24
25/// Words the chip holds.
26pub const WORDS: u8 = 128;
27
28fn port(bits: u8) {
29    unsafe { write_volatile(PORT, bits) };
30}
31
32/// Clock one bit out, returning the bit the chip clocked back.
33fn exchange(state: u8, bit: bool) -> bool {
34    let level = state | if bit { DI } else { 0 };
35    port(level);
36    port(level | CLK);
37    let read = unsafe { read_volatile(PORT as *const u8) } & DO != 0;
38    port(level);
39    read
40}
41
42fn command(opcode: u8, address: u8) {
43    port(0);
44    port(CS);
45    exchange(CS, true);
46    for i in (0..2).rev() {
47        exchange(CS, opcode >> i & 1 != 0);
48    }
49    for i in (0..8).rev() {
50        exchange(CS, address >> i & 1 != 0);
51    }
52}
53
54fn finish() {
55    port(0);
56}
57
58/// Read one word.
59pub fn read(address: u8) -> u16 {
60    reg::enable();
61    reg::select_raw(0x40);
62
63    command(0b10, address);
64    let mut word = 0u16;
65    for _ in 0..16 {
66        word = word << 1 | exchange(CS, false) as u16;
67    }
68    finish();
69
70    reg::disable();
71    word
72}
73
74/// Write one word. Does nothing until [`unlock`].
75pub fn write(address: u8, word: u16) {
76    reg::enable();
77    reg::select_raw(0x40);
78
79    command(0b01, address);
80    for i in (0..16).rev() {
81        exchange(CS, word >> i & 1 != 0);
82    }
83    finish();
84
85    // The chip holds its data line low until the write has finished.
86    port(CS);
87    while unsafe { read_volatile(PORT as *const u8) } & DO == 0 {}
88    finish();
89
90    reg::disable();
91}
92
93/// Allow writes.
94pub fn unlock() {
95    reg::enable();
96    reg::select_raw(0x40);
97    command(0b00, 0b1100_0000);
98    finish();
99    reg::disable();
100}
101
102/// Refuse writes again.
103pub fn lock() {
104    reg::enable();
105    reg::select_raw(0x40);
106    command(0b00, 0b0000_0000);
107    finish();
108    reg::disable();
109}