feat(eeprom): Start working on Eeprom emulation
Former-commit-id: b600daa044e1bdf85fb022017517585a9aca4082
This commit is contained in:
parent
eda3d3e3e7
commit
51a79d68da
444
src/core/backup/eeprom.rs
Normal file
444
src/core/backup/eeprom.rs
Normal file
|
@ -0,0 +1,444 @@
|
||||||
|
use super::{BackupMemoryInterface};
|
||||||
|
|
||||||
|
use num::FromPrimitive;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum EepromSize {
|
||||||
|
Eeprom512,
|
||||||
|
Eeprom8k,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<usize> for EepromSize {
|
||||||
|
fn into(self) -> usize {
|
||||||
|
match self {
|
||||||
|
EepromSize::Eeprom512 => 0x0200,
|
||||||
|
EepromSize::Eeprom8k => 0x2000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Primitive, PartialEq, Copy, Clone)]
|
||||||
|
enum SpiInstruction {
|
||||||
|
Read = 0b011,
|
||||||
|
Write = 0b010,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Copy, Clone)]
|
||||||
|
enum SpiState {
|
||||||
|
RxInstruction,
|
||||||
|
RxAddress(SpiInstruction),
|
||||||
|
StopBit(SpiInstruction),
|
||||||
|
TxDummy(SpiInstruction),
|
||||||
|
TxData(usize),
|
||||||
|
RxData(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SpiState {
|
||||||
|
fn default() -> SpiState {
|
||||||
|
SpiState::RxInstruction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
struct EepromChip<M>
|
||||||
|
where
|
||||||
|
M: BackupMemoryInterface,
|
||||||
|
{
|
||||||
|
memory: M,
|
||||||
|
|
||||||
|
state: SpiState,
|
||||||
|
rx_count: usize,
|
||||||
|
rx_buffer: u64,
|
||||||
|
|
||||||
|
tx_count: usize,
|
||||||
|
tx_buffer: u8,
|
||||||
|
|
||||||
|
address: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M> EepromChip<M>
|
||||||
|
where
|
||||||
|
M: BackupMemoryInterface,
|
||||||
|
{
|
||||||
|
fn new(memory: M) -> EepromChip<M> {
|
||||||
|
EepromChip {
|
||||||
|
memory: memory,
|
||||||
|
state: SpiState::RxInstruction,
|
||||||
|
|
||||||
|
rx_count: 0,
|
||||||
|
rx_buffer: 0,
|
||||||
|
|
||||||
|
tx_count: 0,
|
||||||
|
tx_buffer: 0,
|
||||||
|
|
||||||
|
address: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rx(&mut self, bit: u8) {
|
||||||
|
// data is receieved MSB first
|
||||||
|
let bit = bit & 1;
|
||||||
|
self.rx_buffer = (self.rx_buffer << 1) | (if bit & 1 != 0 { 1 } else { 0 });
|
||||||
|
self.rx_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tx(&mut self) -> u8 {
|
||||||
|
// data is transmitted MSB first
|
||||||
|
let bit = self.tx_buffer >> 7;
|
||||||
|
self.tx_buffer = self.tx_buffer << 1;
|
||||||
|
self.tx_count += 1;
|
||||||
|
|
||||||
|
bit
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_rx_buffer(&mut self) {
|
||||||
|
self.rx_buffer = 0;
|
||||||
|
self.rx_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset_tx_buffer(&mut self) {
|
||||||
|
self.tx_buffer = 0;
|
||||||
|
self.tx_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_data_in(&mut self, si: u8) {
|
||||||
|
use SpiInstruction::*;
|
||||||
|
use SpiState::*;
|
||||||
|
|
||||||
|
// Read the si signal into the rx_buffer
|
||||||
|
self.rx(si);
|
||||||
|
|
||||||
|
match self.state {
|
||||||
|
RxInstruction => {
|
||||||
|
// If instruction was recvd, proceed to recv the address
|
||||||
|
if self.rx_count >= 2 {
|
||||||
|
let insn = SpiInstruction::from_u64(self.rx_buffer).expect(&format!(
|
||||||
|
"invalid spi command {:#010b}",
|
||||||
|
self.rx_buffer as u8
|
||||||
|
));
|
||||||
|
self.state = RxAddress(insn);
|
||||||
|
self.reset_rx_buffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RxAddress(insn) => {
|
||||||
|
if self.rx_count == 6 {
|
||||||
|
self.address = (self.rx_buffer as usize) * 8;
|
||||||
|
self.state = match insn {
|
||||||
|
Read => StopBit(insn),
|
||||||
|
Write => RxData(0),
|
||||||
|
};
|
||||||
|
self.reset_rx_buffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StopBit(Read) => {
|
||||||
|
if si != 0 {
|
||||||
|
panic!(
|
||||||
|
"SPI Read - bit 0 was expected for command termination (debug={:?})",
|
||||||
|
*self
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.state = TxDummy(Read);
|
||||||
|
self.reset_rx_buffer();
|
||||||
|
self.reset_tx_buffer();
|
||||||
|
}
|
||||||
|
RxData(rx_bytes) => {
|
||||||
|
if rx_bytes < 8 {
|
||||||
|
if self.rx_count % 8 == 0 {
|
||||||
|
if rx_bytes + 1 == 8 {
|
||||||
|
self.state = StopBit(Write);
|
||||||
|
self.reset_rx_buffer();
|
||||||
|
} else {
|
||||||
|
let byte = (self.rx_buffer & 0xff) as u8;
|
||||||
|
self.memory.write(self.address, byte);
|
||||||
|
self.reset_rx_buffer();
|
||||||
|
self.address += 1;
|
||||||
|
self.state = RxData(rx_bytes + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StopBit(Write) => {
|
||||||
|
if si != 0 {
|
||||||
|
panic!(
|
||||||
|
"SPI Write - bit 0 was expected for command termination (debug={:?})",
|
||||||
|
*self
|
||||||
|
);
|
||||||
|
}
|
||||||
|
self.state = RxInstruction;
|
||||||
|
self.reset_rx_buffer();
|
||||||
|
self.reset_tx_buffer();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clock_data_out(&mut self) -> u8 {
|
||||||
|
use SpiState::*;
|
||||||
|
|
||||||
|
match self.state {
|
||||||
|
TxDummy(insn) => {
|
||||||
|
let bit = self.tx();
|
||||||
|
if self.tx_count == 4 {
|
||||||
|
self.state = TxData(0);
|
||||||
|
self.reset_tx_buffer();
|
||||||
|
}
|
||||||
|
bit
|
||||||
|
}
|
||||||
|
TxData(tx_bytes) => {
|
||||||
|
if tx_bytes < 8 {
|
||||||
|
if self.tx_count % 8 == 0 {
|
||||||
|
let byte = self.memory.read(self.address);
|
||||||
|
self.tx_buffer = byte;
|
||||||
|
self.address += 1;
|
||||||
|
self.state = TxData(tx_bytes + 1);
|
||||||
|
}
|
||||||
|
self.tx()
|
||||||
|
} else {
|
||||||
|
self.state = RxInstruction;
|
||||||
|
self.reset_rx_buffer();
|
||||||
|
self.reset_tx_buffer();
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => self.tx(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_available(&self) -> bool {
|
||||||
|
if let SpiState::TxData(_) = self.state {
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Eeprom controller is usually mapped to the top 256 bytes of the cartridge memory
|
||||||
|
/// Eeprom controller can programmed with DMA accesses in 16bit mode
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct SpiController<M>
|
||||||
|
where
|
||||||
|
M: BackupMemoryInterface,
|
||||||
|
{
|
||||||
|
chip: RefCell<EepromChip<M>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<M> SpiController<M>
|
||||||
|
where
|
||||||
|
M: BackupMemoryInterface,
|
||||||
|
{
|
||||||
|
pub fn new(m: M) -> SpiController<M> {
|
||||||
|
SpiController {
|
||||||
|
chip: RefCell::new(EepromChip::new(m)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_half(&mut self, value: u16) {
|
||||||
|
self.chip.borrow_mut().clock_data_in(value as u8);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_half(&self) -> u16 {
|
||||||
|
let mut chip = self.chip.borrow_mut();
|
||||||
|
let bit = chip.clock_data_out() as u16;
|
||||||
|
if chip.data_available() {
|
||||||
|
bit
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn consume_dummy_cycles(&self) {
|
||||||
|
// ignore the dummy bits
|
||||||
|
self.read_half();
|
||||||
|
self.read_half();
|
||||||
|
self.read_half();
|
||||||
|
self.read_half();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
fn rx_data(&self) -> [u8; 8] {
|
||||||
|
let mut bytes = [0; 8];
|
||||||
|
for byte_index in 0..8 {
|
||||||
|
let mut byte = 0;
|
||||||
|
for _ in 0..8 {
|
||||||
|
let bit = self.read_half() as u8;
|
||||||
|
byte = (byte << 1) | bit;
|
||||||
|
}
|
||||||
|
bytes[byte_index] = byte;
|
||||||
|
}
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::super::BackupMemoryInterface;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use bit::BitIndex;
|
||||||
|
use hexdump;
|
||||||
|
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MockMemory {
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BackupMemoryInterface for MockMemory {
|
||||||
|
fn write(&mut self, offset: usize, value: u8) {
|
||||||
|
self.buffer[offset] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&self, offset: usize) -> u8 {
|
||||||
|
self.buffer[offset]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_mock_memory() -> MockMemory {
|
||||||
|
let mut buffer = vec![0; 512];
|
||||||
|
buffer[16] = 'T' as u8;
|
||||||
|
buffer[17] = 'E' as u8;
|
||||||
|
buffer[18] = 'S' as u8;
|
||||||
|
buffer[19] = 'T' as u8;
|
||||||
|
buffer[20] = '!' as u8;
|
||||||
|
|
||||||
|
MockMemory { buffer }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_spi_read_request(address: usize) -> Vec<u16> {
|
||||||
|
let address = (address & 0x3f) as u16;
|
||||||
|
let mut bit_stream = vec![0; 2 + 6 + 1];
|
||||||
|
|
||||||
|
// 2 bits "11" (Read Request)
|
||||||
|
bit_stream[0] = 1;
|
||||||
|
bit_stream[1] = 1;
|
||||||
|
// 6 bits eeprom address
|
||||||
|
bit_stream[2] = if address.bit(5) { 1 } else { 0 };
|
||||||
|
bit_stream[3] = if address.bit(4) { 1 } else { 0 };
|
||||||
|
bit_stream[4] = if address.bit(3) { 1 } else { 0 };
|
||||||
|
bit_stream[5] = if address.bit(2) { 1 } else { 0 };
|
||||||
|
bit_stream[6] = if address.bit(1) { 1 } else { 0 };
|
||||||
|
bit_stream[7] = if address.bit(0) { 1 } else { 0 };
|
||||||
|
// 1 stop bit
|
||||||
|
bit_stream[8] = 0;
|
||||||
|
|
||||||
|
bit_stream
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_spi_write_request(address: usize, value: [u8; 8]) -> Vec<u16> {
|
||||||
|
let address = (address & 0x3f) as u16;
|
||||||
|
let mut bit_stream = vec![0; 2 + 6 + 64 + 1];
|
||||||
|
|
||||||
|
// 2 bits "10" (Write Request)
|
||||||
|
bit_stream[0] = 1;
|
||||||
|
bit_stream[1] = 0;
|
||||||
|
|
||||||
|
// 6 bits eeprom address
|
||||||
|
bit_stream[2] = if address.bit(5) { 1 } else { 0 };
|
||||||
|
bit_stream[3] = if address.bit(4) { 1 } else { 0 };
|
||||||
|
bit_stream[4] = if address.bit(3) { 1 } else { 0 };
|
||||||
|
bit_stream[5] = if address.bit(2) { 1 } else { 0 };
|
||||||
|
bit_stream[6] = if address.bit(1) { 1 } else { 0 };
|
||||||
|
bit_stream[7] = if address.bit(0) { 1 } else { 0 };
|
||||||
|
|
||||||
|
// encode the 64bit value
|
||||||
|
for i in 0..8 {
|
||||||
|
let mut byte = value[i];
|
||||||
|
for j in 0..8 {
|
||||||
|
let bit = byte >> 7;
|
||||||
|
byte = byte << 1;
|
||||||
|
bit_stream[8 + i * 8 + j] = bit as u16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 stop bit
|
||||||
|
bit_stream[2 + 6 + 64] = 0;
|
||||||
|
|
||||||
|
bit_stream
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spi_read() {
|
||||||
|
let memory = make_mock_memory();
|
||||||
|
let mut spi = SpiController::<MockMemory>::new(memory);
|
||||||
|
|
||||||
|
// 1 bit "0" - stop bit
|
||||||
|
let stream = make_spi_read_request(2);
|
||||||
|
for half in stream.into_iter() {
|
||||||
|
spi.write_half(half);
|
||||||
|
}
|
||||||
|
|
||||||
|
spi.consume_dummy_cycles();
|
||||||
|
|
||||||
|
assert!(spi.chip.borrow().data_available());
|
||||||
|
|
||||||
|
let data = spi.rx_data();
|
||||||
|
|
||||||
|
assert_eq!(data[0], 'T' as u8);
|
||||||
|
assert_eq!(data[1], 'E' as u8);
|
||||||
|
assert_eq!(data[2], 'S' as u8);
|
||||||
|
assert_eq!(data[3], 'T' as u8);
|
||||||
|
assert_eq!(data[4], '!' as u8);
|
||||||
|
|
||||||
|
assert_eq!(spi.chip.borrow().state, SpiState::RxInstruction);
|
||||||
|
assert_eq!(spi.chip.borrow().rx_count, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_spi_read_write() {
|
||||||
|
let memory = make_mock_memory();
|
||||||
|
let mut spi = SpiController::<MockMemory>::new(memory);
|
||||||
|
|
||||||
|
let expected = "Work.".as_bytes();
|
||||||
|
|
||||||
|
// First, lets test a read request
|
||||||
|
let stream = make_spi_read_request(2);
|
||||||
|
for half in stream.into_iter() {
|
||||||
|
spi.write_half(half);
|
||||||
|
}
|
||||||
|
spi.consume_dummy_cycles();
|
||||||
|
let data = spi.rx_data();
|
||||||
|
|
||||||
|
assert_eq!("TEST!".as_bytes(), &data[0..5]);
|
||||||
|
{
|
||||||
|
let chip = spi.chip.borrow();
|
||||||
|
assert_eq!(SpiState::RxInstruction, chip.state);
|
||||||
|
assert_eq!(0, chip.rx_count);
|
||||||
|
assert_eq!(0, chip.rx_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, modify the eeprom data with a write request
|
||||||
|
let mut bytes: [u8; 8] = [0; 8];
|
||||||
|
bytes[0] = expected[0];
|
||||||
|
bytes[1] = expected[1];
|
||||||
|
bytes[2] = expected[2];
|
||||||
|
bytes[3] = expected[3];
|
||||||
|
bytes[4] = expected[4];
|
||||||
|
let stream = make_spi_write_request(2, bytes);
|
||||||
|
for half in stream.into_iter() {
|
||||||
|
spi.write_half(half);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let chip = spi.chip.borrow();
|
||||||
|
assert_eq!(expected, &chip.memory.buffer[0x10..0x15]);
|
||||||
|
assert_eq!(SpiState::RxInstruction, chip.state);
|
||||||
|
assert_eq!(0, chip.rx_count);
|
||||||
|
assert_eq!(0, chip.tx_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Also lets again read the result
|
||||||
|
let stream = make_spi_read_request(2);
|
||||||
|
for half in stream.into_iter() {
|
||||||
|
spi.write_half(half);
|
||||||
|
}
|
||||||
|
spi.consume_dummy_cycles();
|
||||||
|
let data = spi.rx_data();
|
||||||
|
assert_eq!(expected, &data[0..5]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
use super::BackupMemory;
|
use super::{BackupMemory, BackupMemoryInterface};
|
||||||
|
|
||||||
use num::FromPrimitive;
|
use num::FromPrimitive;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
|
@ -9,6 +9,7 @@ use serde::ser::{Serialize, SerializeStruct, Serializer};
|
||||||
|
|
||||||
use crate::util::write_bin_file;
|
use crate::util::write_bin_file;
|
||||||
|
|
||||||
|
pub mod eeprom;
|
||||||
pub mod flash;
|
pub mod flash;
|
||||||
|
|
||||||
pub const BACKUP_FILE_EXT: &'static str = "sav";
|
pub const BACKUP_FILE_EXT: &'static str = "sav";
|
||||||
|
@ -22,6 +23,11 @@ pub enum BackupType {
|
||||||
Flash1M = 4,
|
Flash1M = 4,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait BackupMemoryInterface: Sized + fmt::Debug {
|
||||||
|
fn write(&mut self, offset: usize, value: u8);
|
||||||
|
fn read(&self, offset: usize) -> u8;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct BackupMemory {
|
pub struct BackupMemory {
|
||||||
size: usize,
|
size: usize,
|
||||||
|
@ -105,14 +111,16 @@ impl BackupMemory {
|
||||||
buffer: buffer,
|
buffer: buffer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, offset: usize, value: u8) {
|
impl BackupMemoryInterface for BackupMemory {
|
||||||
|
fn write(&mut self, offset: usize, value: u8) {
|
||||||
self.buffer[offset] = value;
|
self.buffer[offset] = value;
|
||||||
self.file.seek(SeekFrom::Start(offset as u64)).unwrap();
|
self.file.seek(SeekFrom::Start(offset as u64)).unwrap();
|
||||||
self.file.write_all(&[value]).unwrap();
|
self.file.write_all(&[value]).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read(&self, offset: usize) -> u8 {
|
fn read(&self, offset: usize) -> u8 {
|
||||||
self.buffer[offset]
|
self.buffer[offset]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@ pub trait Bus {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_16(&self, addr: Addr) -> u16 {
|
fn read_16(&self, addr: Addr) -> u16 {
|
||||||
|
self.default_read_16(addr)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_read_16(&self, addr: Addr) -> u16 {
|
||||||
self.read_8(addr) as u16 | (self.read_8(addr + 1) as u16) << 8
|
self.read_8(addr) as u16 | (self.read_8(addr + 1) as u16) << 8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +22,11 @@ pub trait Bus {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_16(&mut self, addr: Addr, value: u16) {
|
fn write_16(&mut self, addr: Addr, value: u16) {
|
||||||
|
self.default_write_16(addr, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_write_16(&mut self, addr: Addr, value: u16) {
|
||||||
self.write_8(addr, (value & 0xff) as u8);
|
self.write_8(addr, (value & 0xff) as u8);
|
||||||
self.write_8(addr + 1, ((value >> 8) & 0xff) as u8);
|
self.write_8(addr + 1, ((value >> 8) & 0xff) as u8);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,8 +11,9 @@ use zip::ZipArchive;
|
||||||
use super::super::util::read_bin_file;
|
use super::super::util::read_bin_file;
|
||||||
use super::{Addr, Bus, GBAResult};
|
use super::{Addr, Bus, GBAResult};
|
||||||
|
|
||||||
|
use super::backup::eeprom::*;
|
||||||
use super::backup::flash::*;
|
use super::backup::flash::*;
|
||||||
use super::backup::{BackupMemory, BackupType, BACKUP_FILE_EXT};
|
use super::backup::{BackupMemory, BackupMemoryInterface, BackupType, BACKUP_FILE_EXT};
|
||||||
|
|
||||||
/// From GBATEK
|
/// From GBATEK
|
||||||
///
|
///
|
||||||
|
@ -77,7 +78,7 @@ impl CartridgeHeader {
|
||||||
pub enum BackupMedia {
|
pub enum BackupMedia {
|
||||||
Sram(BackupMemory),
|
Sram(BackupMemory),
|
||||||
Flash(Flash),
|
Flash(Flash),
|
||||||
Eeprom,
|
Eeprom(SpiController<BackupMemory>),
|
||||||
Undetected,
|
Undetected,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +88,7 @@ impl BackupMedia {
|
||||||
match self {
|
match self {
|
||||||
Sram(..) => "SRAM",
|
Sram(..) => "SRAM",
|
||||||
Flash(..) => "FLASH",
|
Flash(..) => "FLASH",
|
||||||
Eeprom => "EEPROM",
|
Eeprom(..) => "EEPROM",
|
||||||
Undetected => "Undetected",
|
Undetected => "Undetected",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,6 +148,8 @@ impl Cartridge {
|
||||||
} else {
|
} else {
|
||||||
BackupMedia::Undetected
|
BackupMedia::Undetected
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("Header: {:?}", header);
|
||||||
println!("Backup: {}", backup.type_string());
|
println!("Backup: {}", backup.type_string());
|
||||||
|
|
||||||
Cartridge {
|
Cartridge {
|
||||||
|
@ -169,7 +172,9 @@ fn create_backup(bytes: &[u8], rom_path: &Path) -> BackupMedia {
|
||||||
BackupMedia::Flash(Flash::new(backup_path, FlashSize::Flash128k))
|
BackupMedia::Flash(Flash::new(backup_path, FlashSize::Flash128k))
|
||||||
}
|
}
|
||||||
BackupType::Sram => BackupMedia::Sram(BackupMemory::new(0x8000, backup_path)),
|
BackupType::Sram => BackupMedia::Sram(BackupMemory::new(0x8000, backup_path)),
|
||||||
BackupType::Eeprom => BackupMedia::Eeprom,
|
BackupType::Eeprom => {
|
||||||
|
BackupMedia::Eeprom(SpiController::new(BackupMemory::new(0x200, backup_path)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
BackupMedia::Undetected
|
BackupMedia::Undetected
|
||||||
|
@ -191,7 +196,9 @@ fn detect_backup_type(bytes: &[u8]) -> Option<BackupType> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
use super::sysbus::{SRAM_HI, SRAM_LO};
|
use super::sysbus::consts::*;
|
||||||
|
|
||||||
|
const EEPROM_BASE_ADDR: u32 = 0x0DFF_FF00;
|
||||||
|
|
||||||
impl Bus for Cartridge {
|
impl Bus for Cartridge {
|
||||||
fn read_8(&self, addr: Addr) -> u8 {
|
fn read_8(&self, addr: Addr) -> u8 {
|
||||||
|
@ -212,15 +219,32 @@ impl Bus for Cartridge {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_16(&self, addr: u32) -> u16 {
|
||||||
|
if addr & 0xff000000 == GAMEPAK_WS2_HI && (self.bytes.len() <= 16*1024*1024 || addr >= EEPROM_BASE_ADDR) {
|
||||||
|
if let BackupMedia::Eeprom(spi) = &self.backup {
|
||||||
|
return spi.read_half();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.default_read_16(addr)
|
||||||
|
}
|
||||||
|
|
||||||
fn write_8(&mut self, addr: u32, value: u8) {
|
fn write_8(&mut self, addr: u32, value: u8) {
|
||||||
let offset = (addr & 0x01ff_ffff) as usize;
|
|
||||||
match addr & 0xff000000 {
|
match addr & 0xff000000 {
|
||||||
SRAM_LO | SRAM_HI => match &mut self.backup {
|
SRAM_LO | SRAM_HI => match &mut self.backup {
|
||||||
BackupMedia::Flash(flash) => flash.write(addr, value),
|
BackupMedia::Flash(flash) => flash.write(addr, value),
|
||||||
BackupMedia::Sram(memory) => memory.write((addr & 0x7FFF) as usize, value),
|
BackupMedia::Sram(memory) => memory.write((addr & 0x7FFF) as usize, value),
|
||||||
_ => {}
|
_ => {}
|
||||||
},
|
},
|
||||||
_ => self.bytes[offset] = value,
|
_ => {}, // TODO allow the debugger to write
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_16(&mut self, addr: u32, value: u16) {
|
||||||
|
if addr & 0xff000000 == GAMEPAK_WS2_HI && (self.bytes.len() <= 16*1024*1024 || addr >= EEPROM_BASE_ADDR) {
|
||||||
|
if let BackupMedia::Eeprom(spi) = &mut self.backup {
|
||||||
|
return spi.write_half(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.default_write_16(addr, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,8 +8,9 @@ use super::gpu::{GpuState, VIDEO_RAM_SIZE};
|
||||||
use super::iodev::IoDevices;
|
use super::iodev::IoDevices;
|
||||||
use super::{Addr, Bus};
|
use super::{Addr, Bus};
|
||||||
|
|
||||||
const WORK_RAM_SIZE: usize = 256 * 1024;
|
pub mod consts {
|
||||||
const INTERNAL_RAM_SIZE: usize = 32 * 1024;
|
pub const WORK_RAM_SIZE: usize = 256 * 1024;
|
||||||
|
pub const INTERNAL_RAM_SIZE: usize = 32 * 1024;
|
||||||
|
|
||||||
pub const BIOS_ADDR: u32 = 0x0000_0000;
|
pub const BIOS_ADDR: u32 = 0x0000_0000;
|
||||||
pub const EWRAM_ADDR: u32 = 0x0200_0000;
|
pub const EWRAM_ADDR: u32 = 0x0200_0000;
|
||||||
|
@ -26,6 +27,9 @@ pub const GAMEPAK_WS2_LO: u32 = 0x0C00_0000;
|
||||||
pub const GAMEPAK_WS2_HI: u32 = 0x0D00_0000;
|
pub const GAMEPAK_WS2_HI: u32 = 0x0D00_0000;
|
||||||
pub const SRAM_LO: u32 = 0x0E00_0000;
|
pub const SRAM_LO: u32 = 0x0E00_0000;
|
||||||
pub const SRAM_HI: u32 = 0x0F00_0000;
|
pub const SRAM_HI: u32 = 0x0F00_0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
use consts::*;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub enum MemoryAccessType {
|
pub enum MemoryAccessType {
|
||||||
|
@ -211,7 +215,7 @@ impl SysBus {
|
||||||
// "[{}] Possible write to EEPROM",
|
// "[{}] Possible write to EEPROM",
|
||||||
// Colour::Yellow.bold().paint("warn")
|
// Colour::Yellow.bold().paint("warn")
|
||||||
// );
|
// );
|
||||||
(&mut self.dummy, addr)
|
(&mut self.cartridge, addr)
|
||||||
}
|
}
|
||||||
SRAM_LO | SRAM_HI => (&mut self.cartridge, addr),
|
SRAM_LO | SRAM_HI => (&mut self.cartridge, addr),
|
||||||
_ => {
|
_ => {
|
||||||
|
|
Reference in a new issue