use std::fmt::{Debug, Display};
use crate::core::{cartridge::mapper::bank_addr, CartridgeMemory, Mapper, NametableArrangement};
use log::*;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]
pub struct SxRom {
shift: usize,
chr_bank_0: usize,
chr_bank_1: usize,
prg_bank: usize,
control: usize,
has_written: bool,
}
impl Default for SxRom {
fn default() -> SxRom {
SxRom {
shift: 0x10,
chr_bank_0: 0,
chr_bank_1: 0,
prg_bank: 0,
control: 0,
has_written: false,
}
}
}
#[typetag::serde]
impl Mapper for SxRom {
fn mapper_num(&self) -> u32 {
1
}
fn read_cpu(&self, cpu_addr: usize, mem: &CartridgeMemory) -> u8 {
if cpu_addr < 0x8000 {
if cpu_addr < 0x6000 {
warn!("Reading to {:X}", cpu_addr);
return 0;
}
mem.read_prg_ram(cpu_addr - 0x6000)
} else {
let mode = (self.control & 0x0C) >> 2;
let addr = match mode {
0 | 1 => {
let bank_num = (self.prg_bank & 0x0E) >> 1;
bank_addr(0x8000, bank_num, cpu_addr)
}
2 => {
let bank_num = self.prg_bank & 0x0F;
if cpu_addr < 0xC000 {
bank_addr(0x4000, 0, cpu_addr)
} else {
bank_addr(0x4000, bank_num, cpu_addr)
}
}
3 => {
let bank_num = self.prg_bank & 0x0F;
if cpu_addr < 0xC000 {
bank_addr(0x4000, bank_num, cpu_addr)
} else {
let last_bank_num = (mem.prg_rom.len() - 1) / 0x4000;
bank_addr(0x4000, last_bank_num, cpu_addr)
}
}
_ => panic!("Should never happen"),
};
mem.read_prg_rom(addr)
}
}
fn read_ppu_debug(&self, ppu_addr: usize, mem: &CartridgeMemory) -> u8 {
let mode = (self.control & 0x10) >> 4;
let addr = if mode == 0 {
bank_addr(0x2000, (self.chr_bank_0 & 0x1E) >> 1, ppu_addr)
} else if ppu_addr < 0x1000 {
bank_addr(0x1000, self.chr_bank_0, ppu_addr)
} else {
bank_addr(0x1000, self.chr_bank_1, ppu_addr)
};
mem.read_chr(addr)
}
fn write_cpu(&mut self, cpu_addr: usize, mem: &mut CartridgeMemory, value: u8) {
if cpu_addr < 0x8000 {
if cpu_addr < 0x6000 {
warn!("Writing to {:X}", cpu_addr);
} else {
mem.write_prg_ram(cpu_addr - 0x6000, value);
}
} else {
if value & 0x80 == 0 {
if !self.has_written {
self.has_written = true;
let new_shift = (self.shift >> 1) | ((value as usize & 0x01) << 4);
if (self.shift & 0x01) != 0 {
if cpu_addr < 0xA000 {
self.control = new_shift;
} else if cpu_addr < 0xC000 {
self.chr_bank_0 = new_shift;
} else if cpu_addr < 0xE000 {
self.chr_bank_1 = new_shift;
} else {
self.prg_bank = new_shift;
}
self.shift = 0x10;
} else {
self.shift = new_shift;
}
}
} else {
self.shift = 0x10;
self.control |= 0x0C;
}
}
}
fn write_ppu(&mut self, ppu_addr: usize, mem: &mut CartridgeMemory, value: u8) {
let mode = (self.control & 0x10) >> 4;
let addr = if mode == 0 {
bank_addr(0x2000, (self.chr_bank_0 & 0x1E) >> 1, ppu_addr)
} else if ppu_addr < 0x1000 {
bank_addr(0x1000, self.chr_bank_0, ppu_addr)
} else {
bank_addr(0x1000, self.chr_bank_1, ppu_addr)
};
mem.write_chr(addr, value);
}
fn nametable_arrangement(&self, _: &CartridgeMemory) -> NametableArrangement {
match self.control & 0x03 {
0 | 1 => NametableArrangement::OneScreen,
2 => NametableArrangement::Horizontal,
3 => NametableArrangement::Vertical,
_ => panic!("Should never happen"),
}
}
fn advance_cpu_cycles(&mut self, _cycles: u32) {
self.has_written = false;
}
}
impl Debug for SxRom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"SxROM, Mode: {:X}, Control: {:X}, shift: {:X}",
(self.control & 0x0C) >> 2,
self.control,
self.shift
)
}
}
impl Display for SxRom {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SxROM")
}
}