use crate::memory_mapped::MemoryMapped;
use crate::save::{Error, MediaInfo, MediaType, RawSaveAccess};
use crate::save::utils::Timeout;
use core::cmp;
const PORT: MemoryMapped<u16> = unsafe { MemoryMapped::new(0x0DFFFF00) };
const SECTOR_SHIFT: usize = 3;
const SECTOR_LEN: usize = 1 << SECTOR_SHIFT;
const SECTOR_MASK: usize = SECTOR_LEN - 1;
fn dma_send(source: &[u32], ct: usize) {
crate::dma::dma3_exclusive(|| unsafe {
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
crate::dma::dma_copy16(source.as_ptr() as *mut u16, 0x0DFFFF00 as *mut u16, ct);
});
}
fn dma_receive(source: &mut [u32], ct: usize) {
crate::dma::dma3_exclusive(|| unsafe {
crate::dma::dma_copy16(0x0DFFFF00 as *mut u16, source.as_ptr() as *mut u16, ct);
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
});
}
struct BufferData {
idx: usize,
data: BufferContents,
}
#[repr(align(4))]
union BufferContents {
uninit: (),
bits: [u16; 82],
words: [u32; 41],
}
impl BufferData {
fn new() -> Self {
BufferData { idx: 0, data: BufferContents { uninit: () } }
}
fn write_bit(&mut self, val: u8) {
unsafe {
self.data.bits[self.idx] = val as u16;
self.idx += 1;
}
}
fn write_num(&mut self, count: usize, num: u32) {
for i in 0..count {
self.write_bit(((num >> (count - 1 - i)) & 1) as u8);
}
}
fn read_num(&mut self, off: usize, count: usize) -> u32 {
let mut accumulator = 0;
unsafe {
for i in 0..count {
accumulator <<= 1;
accumulator |= self.data.bits[off + i] as u32;
}
}
accumulator
}
fn receive(&mut self, count: usize) {
unsafe {
dma_receive(&mut self.data.words, count);
}
}
fn submit(&self) {
unsafe {
dma_send(&self.data.words, self.idx);
}
}
}
struct EepromProperties {
addr_bits: usize,
byte_len: usize,
}
impl EepromProperties {
#[allow(clippy::needless_range_loop)]
fn read_sector(&self, word: usize) -> [u8; 8] {
let mut buf = BufferData::new();
buf.write_bit(1);
buf.write_bit(1);
buf.write_num(self.addr_bits, word as u32);
buf.write_bit(0);
buf.submit();
buf.receive(68);
let mut out = [0; 8];
for i in 0..8 {
out[i] = buf.read_num(4 + i * 8, 8) as u8;
}
out
}
#[allow(clippy::needless_range_loop)]
fn write_sector_raw(
&self, word: usize, block: &[u8], timeout: &mut Timeout,
) -> Result<(), Error> {
let mut buf = BufferData::new();
buf.write_bit(1);
buf.write_bit(0);
buf.write_num(self.addr_bits, word as u32);
for i in 0..8 {
buf.write_num(8, block[i] as u32);
}
buf.write_bit(0);
buf.submit();
timeout.start();
while PORT.get() & 1 != 1 {
if timeout.check_timeout_met(10) {
return Err(Error::OperationTimedOut);
}
}
Ok(())
}
fn write_sector_safe(
&self, word: usize, data: &[u8], start: usize, timeout: &mut Timeout,
) -> Result<(), Error> {
let mut buf = self.read_sector(word);
buf[start..start + data.len()].copy_from_slice(data);
self.write_sector_raw(word, &buf, timeout)
}
fn write_sector(
&self, word: usize, data: &[u8], start: usize, timeout: &mut Timeout,
) -> Result<(), Error> {
if data.len() == 8 && start == 0 {
self.write_sector_raw(word, data, timeout)
} else {
self.write_sector_safe(word, data, start, timeout)
}
}
fn check_offset(&self, offset: usize, len: usize) -> Result<(), Error> {
if offset.checked_add(len).is_none() && (offset + len) > self.byte_len {
Err(Error::OutOfBounds)
} else {
Ok(())
}
}
fn read(&self, mut offset: usize, mut buf: &mut [u8]) -> Result<(), Error> {
self.check_offset(offset, buf.len())?;
while !buf.is_empty() {
let start = offset & SECTOR_MASK;
let end_len = cmp::min(SECTOR_LEN - start, buf.len());
let sector = self.read_sector(offset >> SECTOR_SHIFT);
buf[..end_len].copy_from_slice(§or[start..start + end_len]);
buf = &mut buf[end_len..];
offset += end_len;
}
Ok(())
}
fn verify(&self, mut offset: usize, mut buf: &[u8]) -> Result<bool, Error> {
self.check_offset(offset, buf.len())?;
while !buf.is_empty() {
let start = offset & SECTOR_MASK;
let end_len = cmp::min(SECTOR_LEN - start, buf.len());
if buf[..end_len] != self.read_sector(offset >> SECTOR_SHIFT) {
return Ok(false);
}
buf = &buf[end_len..];
offset += end_len;
}
Ok(true)
}
fn write(&self, mut offset: usize, mut buf: &[u8], timeout: &mut Timeout) -> Result<(), Error> {
self.check_offset(offset, buf.len())?;
while !buf.is_empty() {
let start = offset & SECTOR_MASK;
let end_len = cmp::min(SECTOR_LEN - start, buf.len());
self.write_sector(offset >> SECTOR_SHIFT, &buf[..end_len], start, timeout)?;
buf = &buf[end_len..];
offset += end_len;
}
Ok(())
}
}
const PROPS_512B: EepromProperties = EepromProperties { addr_bits: 6, byte_len: 512 };
const PROPS_8K: EepromProperties = EepromProperties { addr_bits: 14, byte_len: 8 * 1024 };
pub struct Eeprom512B;
impl RawSaveAccess for Eeprom512B {
fn info(&self) -> Result<&'static MediaInfo, Error> {
Ok(&MediaInfo {
media_type: MediaType::Eeprom512B,
sector_shift: 3,
sector_count: 64,
uses_prepare_write: false,
})
}
fn read(&self, offset: usize, buffer: &mut [u8], _: &mut Timeout) -> Result<(), Error> {
PROPS_512B.read(offset, buffer)
}
fn verify(&self, offset: usize, buffer: &[u8], _: &mut Timeout) -> Result<bool, Error> {
PROPS_512B.verify(offset, buffer)
}
fn prepare_write(&self, _: usize, _: usize, _: &mut Timeout) -> Result<(), Error> {
Ok(())
}
fn write(&self, offset: usize, buffer: &[u8], timeout: &mut Timeout) -> Result<(), Error> {
PROPS_512B.write(offset, buffer, timeout)
}
}
pub struct Eeprom8K;
impl RawSaveAccess for Eeprom8K {
fn info(&self) -> Result<&'static MediaInfo, Error> {
Ok(&MediaInfo {
media_type: MediaType::Eeprom8K,
sector_shift: 3,
sector_count: 1024,
uses_prepare_write: false,
})
}
fn read(&self, offset: usize, buffer: &mut [u8], _: &mut Timeout) -> Result<(), Error> {
PROPS_8K.read(offset, buffer)
}
fn verify(&self, offset: usize, buffer: &[u8], _: &mut Timeout) -> Result<bool, Error> {
PROPS_8K.verify(offset, buffer)
}
fn prepare_write(&self, _: usize, _: usize, _: &mut Timeout) -> Result<(), Error> {
Ok(())
}
fn write(&self, offset: usize, buffer: &[u8], timeout: &mut Timeout) -> Result<(), Error> {
PROPS_8K.write(offset, buffer, timeout)
}
}