1use crate::{
6 cart::Cart,
7 common::{Clock, Regional, Reset, ResetKind, Sram},
8 cpu::{Cpu, Irq},
9 mapper::{
10 self, MapRead, MapWrite, MappedRead, MappedWrite, Mapper, Mirrored, OnBusRead, OnBusWrite,
11 },
12 mem::{BankAccess, Banks},
13 ppu::Mirroring,
14};
15use serde::{Deserialize, Serialize};
16
17#[derive(Debug)]
19#[must_use]
20enum PageBit {
21 Low,
22 High,
23}
24
25impl PageBit {
26 const fn page(&self, page: usize, val: u8) -> usize {
27 let val = (val as usize) & 0x0F;
28 match self {
29 PageBit::Low => (page & 0xF0) | val,
30 PageBit::High => (val << 4) | (page & 0x0F),
31 }
32 }
33}
34
35impl From<u16> for PageBit {
36 fn from(addr: u16) -> Self {
37 if addr & 0x01 == 0x01 {
38 Self::High
39 } else {
40 Self::Low
41 }
42 }
43}
44
45#[derive(Default, Debug, Clone, Serialize, Deserialize)]
47#[must_use]
48pub struct Regs {
49 pub irq_enabled: bool,
50 pub irq_reload: [u8; 4],
51 pub irq_counter_size: u8,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
56#[must_use]
57pub struct JalecoSs88006 {
58 pub regs: Regs,
59 pub irq_counter: u16,
60 pub mirroring: Mirroring,
61 pub chr_banks: Banks,
62 pub prg_ram_banks: Banks,
63 pub prg_rom_banks: Banks,
64}
65
66impl JalecoSs88006 {
67 const PRG_WINDOW: usize = 8 * 1024;
68 const PRG_RAM_SIZE: usize = 8 * 1024;
69 const CHR_WINDOW: usize = 1024;
70
71 const IRQ_MASKS: [u16; 4] = [0xFFFF, 0x0FFF, 0x00FF, 0x000F];
72
73 pub fn load(cart: &mut Cart) -> Result<Mapper, mapper::Error> {
74 if !cart.has_prg_ram() {
75 cart.add_prg_ram(Self::PRG_RAM_SIZE);
76 }
77 let mut jalecoss88006 = Self {
78 regs: Regs::default(),
79 irq_counter: 0,
80 mirroring: cart.mirroring(),
81 chr_banks: Banks::new(0x0000, 0x1FFF, cart.chr_rom.len(), Self::CHR_WINDOW)?,
82 prg_ram_banks: Banks::new(0x6000, 0x7FFF, cart.prg_ram.len(), Self::PRG_WINDOW)?,
83 prg_rom_banks: Banks::new(0x8000, 0xFFFF, cart.prg_rom.len(), Self::PRG_WINDOW)?,
84 };
85 jalecoss88006
86 .prg_rom_banks
87 .set(3, jalecoss88006.prg_rom_banks.last());
88 Ok(jalecoss88006.into())
89 }
90
91 fn update_prg_bank(&mut self, bank: usize, val: u8, bits: PageBit) {
92 self.prg_rom_banks
93 .set(bank, bits.page(self.prg_rom_banks.page(bank), val));
94 }
95
96 fn update_chr_bank(&mut self, bank: usize, val: u8, bits: PageBit) {
97 self.chr_banks
98 .set(bank, bits.page(self.chr_banks.page(bank), val));
99 }
100}
101
102impl Mirrored for JalecoSs88006 {
103 fn mirroring(&self) -> Mirroring {
104 self.mirroring
105 }
106
107 fn set_mirroring(&mut self, mirroring: Mirroring) {
108 self.mirroring = mirroring;
109 }
110}
111
112impl MapRead for JalecoSs88006 {
113 fn map_peek(&self, addr: u16) -> MappedRead {
129 match addr {
130 0x0000..=0x1FFF => MappedRead::Chr(self.chr_banks.translate(addr)),
131 0x6000..=0x7FFF if self.prg_ram_banks.readable(addr) => {
132 MappedRead::PrgRam(self.prg_ram_banks.translate(addr))
133 }
134 0x8000..=0xFFFF => MappedRead::PrgRom(self.prg_rom_banks.translate(addr)),
135 _ => MappedRead::Bus,
136 }
137 }
138}
139
140impl MapWrite for JalecoSs88006 {
141 fn map_write(&mut self, addr: u16, val: u8) -> MappedWrite {
142 match addr {
143 0x6000..=0x7FFF => {
144 if self.prg_ram_banks.writable(addr) {
145 return MappedWrite::PrgRam(self.prg_ram_banks.translate(addr), val);
146 }
147 }
148 _ => match addr & 0xF003 {
149 0x8000 | 0x8001 => self.update_prg_bank(0, val, PageBit::from(addr)),
150 0x8002 | 0x8003 => self.update_prg_bank(1, val, PageBit::from(addr)),
151 0x9000 | 0x9001 => self.update_prg_bank(2, val, PageBit::from(addr)),
152 0x9002 => {
153 let prg_ram_access = if val & 0x01 == 0x01 {
154 if val & 0x02 == 0x02 {
155 BankAccess::ReadWrite
156 } else {
157 BankAccess::Read
158 }
159 } else {
160 BankAccess::None
161 };
162 self.prg_ram_banks.set_access(0, prg_ram_access);
163 }
164 0xA000 | 0xA001 => self.update_chr_bank(0, val, PageBit::from(addr)),
165 0xA002 | 0xA003 => self.update_chr_bank(1, val, PageBit::from(addr)),
166 0xB000 | 0xB001 => self.update_chr_bank(2, val, PageBit::from(addr)),
167 0xB002 | 0xB003 => self.update_chr_bank(3, val, PageBit::from(addr)),
168 0xC000 | 0xC001 => self.update_chr_bank(4, val, PageBit::from(addr)),
169 0xC002 | 0xC003 => self.update_chr_bank(5, val, PageBit::from(addr)),
170 0xD000 | 0xD001 => self.update_chr_bank(6, val, PageBit::from(addr)),
171 0xD002 | 0xD003 => self.update_chr_bank(7, val, PageBit::from(addr)),
172 0xE000..=0xE003 => self.regs.irq_reload[(addr & 0x03) as usize] = val,
173 0xF000 => {
174 Cpu::clear_irq(Irq::MAPPER);
175 self.irq_counter = u16::from(self.regs.irq_reload[0])
176 | (u16::from(self.regs.irq_reload[1]) << 4)
177 | (u16::from(self.regs.irq_reload[2]) << 8)
178 | (u16::from(self.regs.irq_reload[3]) << 12);
179 }
180 0xF001 => {
181 Cpu::clear_irq(Irq::MAPPER);
182 self.regs.irq_enabled = val & 0x01 == 0x01;
183 if val & 0x08 == 0x08 {
184 self.regs.irq_counter_size = 3;
185 } else if val & 0x04 == 0x04 {
186 self.regs.irq_counter_size = 2;
187 } else if val & 0x02 == 0x02 {
188 self.regs.irq_counter_size = 1;
189 } else {
190 self.regs.irq_counter_size = 0;
191 }
192 }
193 0xF002 => self.set_mirroring(match val & 0x03 {
194 0 => Mirroring::Horizontal,
195 1 => Mirroring::Vertical,
196 2 => Mirroring::SingleScreenA,
197 3 => Mirroring::SingleScreenB,
198 _ => unreachable!("invalid mirroring mode: ${val:02X}"),
199 }),
200 0xF003 => {
201 }
203 _ => (),
204 },
205 }
206 MappedWrite::Bus
207 }
208}
209
210impl Reset for JalecoSs88006 {
211 fn reset(&mut self, kind: ResetKind) {
212 self.regs = Regs::default();
213 if kind == ResetKind::Hard {
214 self.prg_rom_banks.set(3, self.prg_rom_banks.last());
215 }
216 }
217}
218
219impl Clock for JalecoSs88006 {
220 fn clock(&mut self) -> u64 {
221 if self.regs.irq_enabled {
222 let irq_mask = Self::IRQ_MASKS[self.regs.irq_counter_size as usize];
223 let counter = self.irq_counter & irq_mask;
224 if counter == 0 {
225 Cpu::set_irq(Irq::MAPPER);
226 }
227 self.irq_counter =
228 (self.irq_counter & !irq_mask) | (counter.wrapping_sub(1) & irq_mask);
229 1
230 } else {
231 0
232 }
233 }
234}
235
236impl OnBusRead for JalecoSs88006 {}
237impl OnBusWrite for JalecoSs88006 {}
238impl Regional for JalecoSs88006 {}
239impl Sram for JalecoSs88006 {}