1use crate::{
7 cart::Cart,
8 common::{Clock, ResetKind, Regional, Reset},
9 mapper::{Mapped, MappedRead, MappedWrite, Mapper, MemMap},
10 mem::MemBanks,
11 ppu::Mirroring,
12};
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
28#[must_use]
29pub enum Mmc3Revision {
30 A,
32 BC,
34 Acc,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[must_use]
40struct TxRegs {
41 bank_select: u8,
42 bank_values: [u8; 8],
43 irq_latch: u8,
44 irq_counter: u8,
45 irq_enabled: bool,
46 irq_reload: bool,
47 last_clock: u16,
48}
49
50impl TxRegs {
51 const fn new() -> Self {
52 Self {
53 bank_select: 0x00,
54 bank_values: [0x00; 8],
55 irq_latch: 0x00,
56 irq_counter: 0x00,
57 irq_enabled: false,
58 irq_reload: false,
59 last_clock: 0x0000,
60 }
61 }
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
65#[must_use]
66pub struct Txrom {
67 regs: TxRegs,
68 mirroring: Mirroring,
69 irq_pending: bool,
70 revision: Mmc3Revision,
71 chr_banks: MemBanks,
72 prg_ram_banks: MemBanks,
73 prg_rom_banks: MemBanks,
74}
75
76impl Txrom {
77 const PRG_WINDOW: usize = 8 * 1024;
78 const CHR_WINDOW: usize = 1024;
79
80 const FOUR_SCREEN_RAM_SIZE: usize = 4 * 1024;
81 const PRG_RAM_SIZE: usize = 8 * 1024;
82 const CHR_RAM_SIZE: usize = 8 * 1024;
83
84 const PRG_MODE_MASK: u8 = 0x40; const CHR_INVERSION_MASK: u8 = 0x80; pub fn load(cart: &mut Cart) -> Mapper {
88 cart.add_prg_ram(Self::PRG_RAM_SIZE);
89 if cart.mirroring() == Mirroring::FourScreen {
90 cart.add_ex_ram(Self::FOUR_SCREEN_RAM_SIZE);
91 }
92 if !cart.has_chr() {
93 cart.add_chr_ram(Self::CHR_RAM_SIZE);
94 };
95 let mut txrom = Self {
96 regs: TxRegs::new(),
97 mirroring: cart.mirroring(),
98 irq_pending: false,
99 revision: Mmc3Revision::BC, chr_banks: MemBanks::new(0x0000, 0x1FFF, cart.chr_len(), Self::CHR_WINDOW),
101 prg_ram_banks: MemBanks::new(0x6000, 0x7FFF, cart.prg_ram.len(), Self::PRG_WINDOW),
102 prg_rom_banks: MemBanks::new(0x8000, 0xFFFF, cart.prg_rom.len(), Self::PRG_WINDOW),
103 };
104 let last_bank = txrom.prg_rom_banks.last();
105 txrom.prg_rom_banks.set(2, last_bank - 1);
106 txrom.prg_rom_banks.set(3, last_bank);
107 txrom.into()
108 }
109
110 #[inline]
111 pub fn set_revision(&mut self, revision: Mmc3Revision) {
112 self.revision = revision;
113 }
114
115 fn update_banks(&mut self) {
116 let prg_last = self.prg_rom_banks.last();
117 let prg_lo = self.regs.bank_values[6] as usize;
118 let prg_hi = self.regs.bank_values[7] as usize;
119 if self.regs.bank_select & Self::PRG_MODE_MASK == Self::PRG_MODE_MASK {
120 self.prg_rom_banks.set(0, prg_last - 1);
121 self.prg_rom_banks.set(1, prg_hi);
122 self.prg_rom_banks.set(2, prg_lo);
123 } else {
124 self.prg_rom_banks.set(0, prg_lo);
125 self.prg_rom_banks.set(1, prg_hi);
126 self.prg_rom_banks.set(2, prg_last - 1);
127 }
128 self.prg_rom_banks.set(3, prg_last);
129
130 let chr = self.regs.bank_values;
133 if self.regs.bank_select & Self::CHR_INVERSION_MASK == Self::CHR_INVERSION_MASK {
134 self.chr_banks.set(0, chr[2] as usize);
135 self.chr_banks.set(1, chr[3] as usize);
136 self.chr_banks.set(2, chr[4] as usize);
137 self.chr_banks.set(3, chr[5] as usize);
138 self.chr_banks.set_range(4, 5, (chr[0] & 0xFE) as usize);
139 self.chr_banks.set_range(6, 7, (chr[1] & 0xFE) as usize);
140 } else {
141 self.chr_banks.set_range(0, 1, (chr[0] & 0xFE) as usize);
142 self.chr_banks.set_range(2, 3, (chr[1] & 0xFE) as usize);
143 self.chr_banks.set(4, chr[2] as usize);
144 self.chr_banks.set(5, chr[3] as usize);
145 self.chr_banks.set(6, chr[4] as usize);
146 self.chr_banks.set(7, chr[5] as usize);
147 }
148 }
149
150 fn clock_irq(&mut self, addr: u16) {
151 if addr < 0x2000 {
152 let next_clock = (addr >> 12) & 1;
153 let (last, next) = if self.revision == Mmc3Revision::Acc {
154 (1, 0)
155 } else {
156 (0, 1)
157 };
158 if self.regs.last_clock == last && next_clock == next {
159 let counter = self.regs.irq_counter;
160 if counter == 0 || self.regs.irq_reload {
161 self.regs.irq_counter = self.regs.irq_latch;
162 } else {
163 self.regs.irq_counter -= 1;
164 }
165 if (counter & 0x01 == 0x01
166 || self.revision == Mmc3Revision::BC
167 || self.regs.irq_reload)
168 && self.regs.irq_counter == 0
169 && self.regs.irq_enabled
170 {
171 self.irq_pending = true;
172 }
173 self.regs.irq_reload = false;
174 }
175 self.regs.last_clock = next_clock;
176 }
177 }
178}
179
180impl Mapped for Txrom {
181 #[inline]
182 fn irq_pending(&self) -> bool {
183 self.irq_pending
184 }
185
186 #[inline]
187 fn mirroring(&self) -> Mirroring {
188 self.mirroring
189 }
190
191 #[inline]
192 fn set_mirroring(&mut self, mirroring: Mirroring) {
193 self.mirroring = mirroring;
194 }
195
196 #[inline]
197 fn ppu_bus_read(&mut self, addr: u16) {
198 self.clock_irq(addr);
199 }
200
201 #[inline]
202 fn ppu_bus_write(&mut self, addr: u16, _val: u8) {
203 self.clock_irq(addr);
204 }
205}
206
207impl MemMap for Txrom {
208 #[inline]
223 fn map_read(&mut self, addr: u16) -> MappedRead {
224 self.clock_irq(addr);
225 self.map_peek(addr)
226 }
227
228 fn map_peek(&self, addr: u16) -> MappedRead {
229 match addr {
230 0x0000..=0x1FFF => MappedRead::Chr(self.chr_banks.translate(addr)),
231 0x2000..=0x3EFF if self.mirroring == Mirroring::FourScreen => {
232 MappedRead::ExRam((addr & 0x1FFF) as usize)
233 }
234 0x6000..=0x7FFF => MappedRead::PrgRam(self.prg_ram_banks.translate(addr)),
235 0x8000..=0xFFFF => MappedRead::PrgRom(self.prg_rom_banks.translate(addr)),
236 _ => MappedRead::None,
237 }
238 }
239
240 fn map_write(&mut self, addr: u16, val: u8) -> MappedWrite {
241 match addr {
242 0x0000..=0x1FFF => MappedWrite::Chr(self.chr_banks.translate(addr), val),
243 0x2000..=0x3EFF if self.mirroring == Mirroring::FourScreen => {
244 MappedWrite::ExRam((addr & 0x1FFF) as usize, val)
245 }
246 0x6000..=0x7FFF => MappedWrite::PrgRam(self.prg_ram_banks.translate(addr), val),
247 0x8000..=0xFFFF => {
248 match addr & 0xE001 {
271 0x8000 => {
272 self.regs.bank_select = val;
273 self.update_banks();
274 }
275 0x8001 => {
276 let bank = self.regs.bank_select & 0x07;
277 self.regs.bank_values[bank as usize] = val;
278 self.update_banks();
279 }
280 0xA000 => {
281 if self.mirroring != Mirroring::FourScreen {
282 self.mirroring = match val & 0x01 {
283 0 => Mirroring::Vertical,
284 1 => Mirroring::Horizontal,
285 _ => unreachable!("impossible mirroring"),
286 };
287 self.update_banks();
288 }
289 }
290 0xA001 => {
291 }
293 0xC000 => self.regs.irq_latch = val,
295 0xC001 => self.regs.irq_reload = true,
296 0xE000 => {
297 self.irq_pending = false;
298 self.regs.irq_enabled = false;
299 }
300 0xE001 => self.regs.irq_enabled = true,
301 _ => unreachable!("impossible address"),
302 }
303 MappedWrite::None
304 }
305 _ => MappedWrite::None,
306 }
307 }
308}
309
310impl Reset for Txrom {
311 fn reset(&mut self, _kind: ResetKind) {
312 self.irq_pending = false;
313 self.regs = TxRegs::new();
314 self.update_banks();
315 }
316}
317
318impl Clock for Txrom {}
319impl Regional for Txrom {}