gba_save/flash/
writer.rs

1use crate::{
2    flash::{
3        Bank, Command, Error, FLASH_MEMORY, Reader64K, SIZE_64KB, send_command, switch_bank,
4        verify_byte, verify_bytes,
5    },
6    log,
7    mmio::IME,
8};
9use core::{cmp::min, marker::PhantomData, ptr, time::Duration};
10use embedded_io::{ErrorType, Read, Write};
11
12/// A writer on a 64KiB flash device.
13///
14/// This type allows writing data on the range specified upon creation.
15///
16/// If the memory being written to has been written to previously without being erased, the writes
17/// will not succeed.
18#[derive(Debug)]
19pub struct Writer64K<'a> {
20    address: *mut u8,
21    len: usize,
22    lifetime: PhantomData<&'a ()>,
23}
24
25impl Writer64K<'_> {
26    pub(crate) unsafe fn new_unchecked(address: *mut u8, len: usize) -> Self {
27        log::info!(
28            "Creating Flash 64KiB writer at address 0x{:08x?} with length {len}",
29            address as usize
30        );
31        Self {
32            address,
33            len,
34            lifetime: PhantomData,
35        }
36    }
37}
38
39impl ErrorType for Writer64K<'_> {
40    type Error = Error;
41}
42
43impl Write for Writer64K<'_> {
44    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
45        let mut write_count = 0;
46        loop {
47            if write_count >= min(buf.len(), self.len) {
48                if self.len == 0 {
49                    return Err(Error::EndOfWriter);
50                }
51                self.address = unsafe { self.address.add(write_count) };
52                self.len -= write_count;
53                return Ok(write_count);
54            }
55
56            let address = unsafe { self.address.add(write_count) };
57            let byte = unsafe { *buf.get_unchecked(write_count) };
58            send_command(Command::Write);
59            unsafe {
60                address.write_volatile(byte);
61            }
62            verify_byte(address, byte, Duration::from_millis(20))?;
63
64            write_count += 1;
65        }
66    }
67
68    fn flush(&mut self) -> Result<(), Self::Error> {
69        Ok(())
70    }
71}
72
73/// A writer on a 128KiB flash device.
74///
75/// This type allows writing data on the range specified upon creation.
76///
77/// If the memory being written to has been written to previously without being erased, the writes
78/// will not succeed.
79#[derive(Debug)]
80pub struct Writer128K<'a> {
81    address: *mut u8,
82    len: usize,
83    bank: Bank,
84    lifetime: PhantomData<&'a ()>,
85}
86
87impl Writer128K<'_> {
88    pub(crate) unsafe fn new_unchecked(address: *mut u8, len: usize) -> Self {
89        log::info!(
90            "Creating Flash 128KiB writer at address 0x{:08x?} with length {len}",
91            address as usize
92        );
93        let bank = if address < unsafe { FLASH_MEMORY.add(SIZE_64KB) } {
94            Bank::_0
95        } else {
96            Bank::_1
97        };
98        switch_bank(bank);
99
100        Self {
101            address,
102            len,
103            bank,
104            lifetime: PhantomData,
105        }
106    }
107}
108
109impl ErrorType for Writer128K<'_> {
110    type Error = Error;
111}
112
113impl Write for Writer128K<'_> {
114    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
115        let mut write_count = 0;
116        loop {
117            if write_count >= min(buf.len(), self.len) {
118                if self.len == 0 {
119                    return Err(Error::EndOfWriter);
120                }
121                self.address = unsafe { self.address.add(write_count) };
122                self.len -= write_count;
123                return Ok(write_count);
124            }
125
126            let mut address = unsafe { self.address.add(write_count) };
127            if matches!(self.bank, Bank::_0) {
128                if ptr::eq(address, unsafe { FLASH_MEMORY.add(SIZE_64KB) }) {
129                    self.bank = Bank::_1;
130                    switch_bank(self.bank);
131                }
132            }
133            if matches!(self.bank, Bank::_1) {
134                address = unsafe { address.sub(SIZE_64KB) };
135            }
136
137            let byte = unsafe { *buf.get_unchecked(write_count) };
138            send_command(Command::Write);
139            unsafe {
140                address.write_volatile(byte);
141            }
142            verify_byte(address, byte, Duration::from_millis(20))?;
143
144            write_count += 1;
145        }
146    }
147
148    fn flush(&mut self) -> Result<(), Self::Error> {
149        Ok(())
150    }
151}
152
153/// A writer on a 64KiB Atmel flash device.
154///
155/// This type allows writing data on the range specified upon creation.
156#[derive(Debug)]
157pub struct Writer64KAtmel<'a> {
158    address: *mut u8,
159    len: usize,
160    buf: [u8; 128],
161    flushed: bool,
162    lifetime: PhantomData<&'a ()>,
163}
164
165impl Writer64KAtmel<'_> {
166    pub(crate) unsafe fn new_unchecked(address: *mut u8, len: usize) -> Self {
167        log::info!(
168            "Creating Flash 64KiB Atmel writer at address 0x{:08x?} with length {len}",
169            address as usize
170        );
171        let mut buf = [0xff; 128];
172        let mut flushed = true;
173
174        // Read data in case of unalignment.
175        let offset = address.align_offset(128);
176        if offset != 0 {
177            let mut reader = unsafe { Reader64K::new_unchecked(address.sub(offset), offset) };
178            unsafe {
179                reader
180                    .read_exact(buf.get_unchecked_mut(..offset))
181                    .unwrap_unchecked()
182            };
183            flushed = false;
184        }
185
186        Self {
187            address,
188            len,
189            buf,
190            flushed,
191            lifetime: PhantomData,
192        }
193    }
194}
195
196impl ErrorType for Writer64KAtmel<'_> {
197    type Error = Error;
198}
199
200impl Write for Writer64KAtmel<'_> {
201    fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
202        let mut write_count = 0;
203        loop {
204            if write_count >= min(buf.len(), self.len) {
205                if self.len == 0 {
206                    return Err(Error::EndOfWriter);
207                }
208                self.len -= write_count;
209                return Ok(write_count);
210            }
211
212            unsafe {
213                *self.buf.get_unchecked_mut(self.address as usize % 128) =
214                    *buf.get_unchecked(write_count);
215            }
216            self.flushed = false;
217
218            unsafe { self.address = self.address.add(1) };
219
220            if self.address as usize % 128 == 0 {
221                self.flush()?;
222            }
223
224            write_count += 1;
225        }
226    }
227
228    fn flush(&mut self) -> Result<(), Self::Error> {
229        if self.flushed {
230            return Ok(());
231        }
232
233        self.flushed = true;
234
235        // Read any remaining bytes at the back of the buffer.
236        let offset = self.address as usize % 128;
237        if offset != 0 {
238            let mut reader = unsafe { Reader64K::new_unchecked(self.address, 128 - offset) };
239            unsafe {
240                reader
241                    .read_exact(self.buf.get_unchecked_mut(offset..))
242                    .unwrap_unchecked()
243            };
244        }
245
246        let offset_address = unsafe { self.address.sub(if offset == 0 { 128 } else { offset }) };
247
248        // Disable interrupts, storing the previous value.
249        //
250        // This prevents anything from interrupting during writes to memory. GBATEK recommends
251        // disabling interrupts on writes to Atmel devices.
252        let previous_ime = unsafe { IME.read_volatile() };
253        // SAFETY: This is guaranteed to be a valid write.
254        unsafe { IME.write_volatile(false) };
255
256        send_command(Command::Write);
257        for (i, &byte) in self.buf.iter().enumerate() {
258            unsafe { offset_address.add(i).write_volatile(byte) };
259        }
260
261        // Restore previous interrupt enable value.
262        // SAFETY: This is guaranteed to be a valid write.
263        unsafe {
264            IME.write_volatile(previous_ime);
265        }
266
267        verify_bytes(offset_address, &self.buf, Duration::from_millis(20))?;
268
269        Ok(())
270    }
271}
272
273impl Drop for Writer64KAtmel<'_> {
274    fn drop(&mut self) {
275        if !self.flushed {
276            log::warn!(
277                "Dropped Flash Atmel 64KiB writer without flushing remaining {} bytes. They will be flushed automatically, but any errors will not be handled.",
278                self.address as usize % 128
279            );
280        }
281        // This will swallow any errors.
282        let _ignored_result = self.flush();
283    }
284}