1#![allow(
43 clippy::doc_markdown,
44 clippy::similar_names,
45 clippy::cast_possible_truncation,
46 clippy::cast_lossless,
47 clippy::cast_sign_loss,
48 clippy::cast_possible_wrap,
49 clippy::struct_excessive_bools,
50 clippy::too_many_lines,
51 clippy::if_not_else,
52 clippy::branches_sharing_code,
53 clippy::missing_const_for_fn
54)]
55
56use alloc::boxed::Box;
57use alloc::vec;
58
59use rustysnes_savestate::{SaveReader, SaveStateError, SaveWriter};
60
61use crate::board::{Board, Coprocessor, MappedAddr};
62use crate::header::Region;
63
64const IRAM_SIZE: usize = 0x800;
66const BWRAM_MAX: usize = 0x4_0000;
69
70const fn mirror(mut address: u32, size: u32) -> u32 {
74 if size == 0 {
75 return 0;
76 }
77 let mut base = 0u32;
78 let mut mask = 1u32 << 23;
79 let mut size = size;
80 while address >= size {
81 while address & mask == 0 {
82 mask >>= 1;
83 }
84 address -= mask;
85 if size > mask {
86 size -= mask;
87 base += mask;
88 }
89 mask >>= 1;
90 }
91 base + address
92}
93
94#[derive(Debug, Clone)]
96struct Io {
97 sa1_irq: bool,
99 sa1_rdyb: bool,
100 sa1_resb: bool,
101 sa1_nmi: bool,
102 smeg: u8,
103 cpu_irqen: bool,
105 chdma_irqen: bool,
106 cpu_irqcl: bool,
107 chdma_irqcl: bool,
108 crv: u16,
110 cnv: u16,
111 civ: u16,
112 cpu_irq: bool,
114 cpu_ivsw: bool,
115 cpu_nvsw: bool,
116 cmeg: u8,
117 sa1_irqen: bool,
119 timer_irqen: bool,
120 dma_irqen: bool,
121 sa1_nmien: bool,
122 sa1_irqcl: bool,
123 timer_irqcl: bool,
124 dma_irqcl: bool,
125 sa1_nmicl: bool,
126 snv: u16,
128 siv: u16,
129 hvselb: bool,
131 ven: bool,
132 hen: bool,
133 hcnt: u16,
134 vcnt: u16,
135 cbmode: bool,
137 dbmode: bool,
138 ebmode: bool,
139 fbmode: bool,
140 cb: u32,
141 db: u32,
142 eb: u32,
143 fb: u32,
144 sbm: u8,
146 sw46: bool,
147 cbm: u8,
148 swen: bool,
150 cwen: bool,
151 bwp: u8,
152 siwp: u8,
154 ciwp: u8,
155 dmaen: bool,
157 dprio: bool,
158 cden: bool,
159 cdsel: bool,
160 dd: u8,
161 sd: u8,
162 chdend: bool,
163 dmasize: u8,
164 dmacb: u8,
165 dsa: u32,
167 dda: u32,
168 dtc: u16,
169 bbf: bool,
171 brf: [u8; 16],
172 acm: bool,
174 md: bool,
175 ma: u16,
176 mb: u16,
177 hl: bool,
179 vb: u8,
180 va: u32,
181 vbit: u8,
182 cpu_irqfl: bool,
184 chdma_irqfl: bool,
185 sa1_irqfl: bool,
186 timer_irqfl: bool,
187 dma_irqfl: bool,
188 sa1_nmifl: bool,
189 hcr: u16,
191 vcr: u16,
192 mr: u64,
194 overflow: bool,
195}
196
197impl Io {
198 fn power() -> Self {
199 Self {
200 sa1_irq: false,
201 sa1_rdyb: false,
202 sa1_resb: true,
203 sa1_nmi: false,
204 smeg: 0,
205 cpu_irqen: false,
206 chdma_irqen: false,
207 cpu_irqcl: false,
208 chdma_irqcl: false,
209 crv: 0,
210 cnv: 0,
211 civ: 0,
212 cpu_irq: false,
213 cpu_ivsw: false,
214 cpu_nvsw: false,
215 cmeg: 0,
216 sa1_irqen: false,
217 timer_irqen: false,
218 dma_irqen: false,
219 sa1_nmien: false,
220 sa1_irqcl: false,
221 timer_irqcl: false,
222 dma_irqcl: false,
223 sa1_nmicl: false,
224 snv: 0,
225 siv: 0,
226 hvselb: false,
227 ven: false,
228 hen: false,
229 hcnt: 0,
230 vcnt: 0,
231 cbmode: false,
232 dbmode: false,
233 ebmode: false,
234 fbmode: false,
235 cb: 0,
236 db: 1,
237 eb: 2,
238 fb: 3,
239 sbm: 0,
240 sw46: false,
241 cbm: 0,
242 swen: false,
243 cwen: false,
244 bwp: 0x0f,
245 siwp: 0,
246 ciwp: 0,
247 dmaen: false,
248 dprio: false,
249 cden: false,
250 cdsel: false,
251 dd: 0,
252 sd: 0,
253 chdend: false,
254 dmasize: 0,
255 dmacb: 0,
256 dsa: 0,
257 dda: 0,
258 dtc: 0,
259 bbf: false,
260 brf: [0; 16],
261 acm: false,
262 md: false,
263 ma: 0,
264 mb: 0,
265 hl: false,
266 vb: 16,
267 va: 0,
268 vbit: 0,
269 cpu_irqfl: false,
270 chdma_irqfl: false,
271 sa1_irqfl: false,
272 timer_irqfl: false,
273 dma_irqfl: false,
274 sa1_nmifl: false,
275 hcr: 0,
276 vcr: 0,
277 mr: 0,
278 overflow: false,
279 }
280 }
281
282 fn save_state(&self, s: &mut SaveWriter) {
284 s.write_bool(self.sa1_irq);
285 s.write_bool(self.sa1_rdyb);
286 s.write_bool(self.sa1_resb);
287 s.write_bool(self.sa1_nmi);
288 s.write_u8(self.smeg);
289 s.write_bool(self.cpu_irqen);
290 s.write_bool(self.chdma_irqen);
291 s.write_bool(self.cpu_irqcl);
292 s.write_bool(self.chdma_irqcl);
293 s.write_u16(self.crv);
294 s.write_u16(self.cnv);
295 s.write_u16(self.civ);
296 s.write_bool(self.cpu_irq);
297 s.write_bool(self.cpu_ivsw);
298 s.write_bool(self.cpu_nvsw);
299 s.write_u8(self.cmeg);
300 s.write_bool(self.sa1_irqen);
301 s.write_bool(self.timer_irqen);
302 s.write_bool(self.dma_irqen);
303 s.write_bool(self.sa1_nmien);
304 s.write_bool(self.sa1_irqcl);
305 s.write_bool(self.timer_irqcl);
306 s.write_bool(self.dma_irqcl);
307 s.write_bool(self.sa1_nmicl);
308 s.write_u16(self.snv);
309 s.write_u16(self.siv);
310 s.write_bool(self.hvselb);
311 s.write_bool(self.ven);
312 s.write_bool(self.hen);
313 s.write_u16(self.hcnt);
314 s.write_u16(self.vcnt);
315 s.write_bool(self.cbmode);
316 s.write_bool(self.dbmode);
317 s.write_bool(self.ebmode);
318 s.write_bool(self.fbmode);
319 s.write_u32(self.cb);
320 s.write_u32(self.db);
321 s.write_u32(self.eb);
322 s.write_u32(self.fb);
323 s.write_u8(self.sbm);
324 s.write_bool(self.sw46);
325 s.write_u8(self.cbm);
326 s.write_bool(self.swen);
327 s.write_bool(self.cwen);
328 s.write_u8(self.bwp);
329 s.write_u8(self.siwp);
330 s.write_u8(self.ciwp);
331 s.write_bool(self.dmaen);
332 s.write_bool(self.dprio);
333 s.write_bool(self.cden);
334 s.write_bool(self.cdsel);
335 s.write_u8(self.dd);
336 s.write_u8(self.sd);
337 s.write_bool(self.chdend);
338 s.write_u8(self.dmasize);
339 s.write_u8(self.dmacb);
340 s.write_u32(self.dsa);
341 s.write_u32(self.dda);
342 s.write_u16(self.dtc);
343 s.write_bool(self.bbf);
344 s.write_bytes(&self.brf);
345 s.write_bool(self.acm);
346 s.write_bool(self.md);
347 s.write_u16(self.ma);
348 s.write_u16(self.mb);
349 s.write_bool(self.hl);
350 s.write_u8(self.vb);
351 s.write_u32(self.va);
352 s.write_u8(self.vbit);
353 s.write_bool(self.cpu_irqfl);
354 s.write_bool(self.chdma_irqfl);
355 s.write_bool(self.sa1_irqfl);
356 s.write_bool(self.timer_irqfl);
357 s.write_bool(self.dma_irqfl);
358 s.write_bool(self.sa1_nmifl);
359 s.write_u16(self.hcr);
360 s.write_u16(self.vcr);
361 s.write_u64(self.mr);
362 s.write_bool(self.overflow);
363 }
364
365 fn load_state(&mut self, s: &mut SaveReader) -> Result<(), SaveStateError> {
374 self.sa1_irq = s.read_bool()?;
375 self.sa1_rdyb = s.read_bool()?;
376 self.sa1_resb = s.read_bool()?;
377 self.sa1_nmi = s.read_bool()?;
378 self.smeg = s.read_u8()? & 0x0F; self.cpu_irqen = s.read_bool()?;
380 self.chdma_irqen = s.read_bool()?;
381 self.cpu_irqcl = s.read_bool()?;
382 self.chdma_irqcl = s.read_bool()?;
383 self.crv = s.read_u16()?;
384 self.cnv = s.read_u16()?;
385 self.civ = s.read_u16()?;
386 self.cpu_irq = s.read_bool()?;
387 self.cpu_ivsw = s.read_bool()?;
388 self.cpu_nvsw = s.read_bool()?;
389 self.cmeg = s.read_u8()? & 0x0F; self.sa1_irqen = s.read_bool()?;
391 self.timer_irqen = s.read_bool()?;
392 self.dma_irqen = s.read_bool()?;
393 self.sa1_nmien = s.read_bool()?;
394 self.sa1_irqcl = s.read_bool()?;
395 self.timer_irqcl = s.read_bool()?;
396 self.dma_irqcl = s.read_bool()?;
397 self.sa1_nmicl = s.read_bool()?;
398 self.snv = s.read_u16()?;
399 self.siv = s.read_u16()?;
400 self.hvselb = s.read_bool()?;
401 self.ven = s.read_bool()?;
402 self.hen = s.read_bool()?;
403 self.hcnt = s.read_u16()?;
404 self.vcnt = s.read_u16()?;
405 self.cbmode = s.read_bool()?;
406 self.dbmode = s.read_bool()?;
407 self.ebmode = s.read_bool()?;
408 self.fbmode = s.read_bool()?;
409 self.cb = s.read_u32()? & 0x07;
410 self.db = s.read_u32()? & 0x07;
411 self.eb = s.read_u32()? & 0x07;
412 self.fb = s.read_u32()? & 0x07;
413 self.sbm = s.read_u8()? & 0x1F;
414 self.sw46 = s.read_bool()?;
415 self.cbm = s.read_u8()? & 0x7F;
416 self.swen = s.read_bool()?;
417 self.cwen = s.read_bool()?;
418 self.bwp = s.read_u8()? & 0x0F;
419 self.siwp = s.read_u8()?;
420 self.ciwp = s.read_u8()?;
421 self.dmaen = s.read_bool()?;
422 self.dprio = s.read_bool()?;
423 self.cden = s.read_bool()?;
424 self.cdsel = s.read_bool()?;
425 self.dd = s.read_u8()? & 0x01;
426 self.sd = s.read_u8()? & 0x03;
427 self.chdend = s.read_bool()?;
428 self.dmasize = (s.read_u8()? & 0x07).min(5);
433 self.dmacb = (s.read_u8()? & 0x03).min(2);
434 self.dsa = s.read_u32()? & 0xFF_FFFF;
435 self.dda = s.read_u32()? & 0xFF_FFFF;
436 self.dtc = s.read_u16()?;
437 self.bbf = s.read_bool()?;
438 self.brf.copy_from_slice(s.read_bytes(16)?);
439 self.acm = s.read_bool()?;
440 self.md = s.read_bool()?;
441 self.ma = s.read_u16()?;
442 self.mb = s.read_u16()?;
443 self.hl = s.read_bool()?;
444 let vb = s.read_u8()? & 0x0F;
446 self.vb = if vb == 0 { 16 } else { vb };
447 self.va = s.read_u32()? & 0xFF_FFFF;
448 self.vbit = s.read_u8()? & 0x07;
449 self.cpu_irqfl = s.read_bool()?;
450 self.chdma_irqfl = s.read_bool()?;
451 self.sa1_irqfl = s.read_bool()?;
452 self.timer_irqfl = s.read_bool()?;
453 self.dma_irqfl = s.read_bool()?;
454 self.sa1_nmifl = s.read_bool()?;
455 self.hcr = s.read_u16()?;
456 self.vcr = s.read_u16()?;
457 self.mr = s.read_u64()?;
458 self.overflow = s.read_bool()?;
459 Ok(())
460 }
461}
462
463#[derive(Debug, Clone)]
465struct Timer {
466 scanlines: u16,
467 vcounter: u16,
468 hcounter: u16,
469}
470
471pub struct Sa1Board {
474 rom: Box<[u8]>,
475 bwram: Box<[u8]>,
476 iram: Box<[u8; IRAM_SIZE]>,
477 io: Io,
478 timer: Timer,
479 reset_pending: bool,
481 bwram_dma: bool,
483 dma_line: u8,
485 host_accesses: u64,
487}
488
489impl core::fmt::Debug for Sa1Board {
490 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
491 f.debug_struct("Sa1Board")
492 .field("rom_len", &self.rom.len())
493 .field("bwram_len", &self.bwram.len())
494 .field("sa1_resb", &self.io.sa1_resb)
495 .field("host_accesses", &self.host_accesses)
496 .finish_non_exhaustive()
497 }
498}
499
500impl Sa1Board {
501 #[must_use]
505 pub fn new(rom: Box<[u8]>, sram_size: usize, region: Region) -> Self {
506 let bw_size = sram_size.max(BWRAM_MAX).next_power_of_two();
507 let scanlines = match region {
508 Region::Pal => 312,
509 Region::Ntsc => 262,
510 };
511 Self {
512 rom,
513 bwram: vec![0u8; bw_size].into_boxed_slice(),
514 iram: Box::new([0u8; IRAM_SIZE]),
515 io: Io::power(),
516 timer: Timer {
517 scanlines,
518 vcounter: 0,
519 hcounter: 0,
520 },
521 reset_pending: false,
522 bwram_dma: false,
523 dma_line: 0,
524 host_accesses: 0,
525 }
526 }
527
528 fn rom_byte(&self, off: u32) -> u8 {
531 let size = self.rom.len() as u32;
532 self.rom
533 .get(mirror(off, size) as usize)
534 .copied()
535 .unwrap_or(0)
536 }
537
538 fn rom_read_packed(&self, packed: u32) -> u8 {
542 if packed & 0xff_ffe0 == 0x00_7fe0 {
544 match packed {
545 0x7fea if self.io.cpu_nvsw => return self.io.snv as u8,
546 0x7feb if self.io.cpu_nvsw => return (self.io.snv >> 8) as u8,
547 0x7fee if self.io.cpu_ivsw => return self.io.siv as u8,
548 0x7fef if self.io.cpu_ivsw => return (self.io.siv >> 8) as u8,
549 _ => {}
550 }
551 }
552 let lo = packed < 0x40_0000;
553 let address = packed & 0x3f_ffff;
554 let block = address >> 20; let within = address & 0x0f_ffff;
556 let (mode, bank) = match block {
557 0 => (self.io.cbmode, self.io.cb),
558 1 => (self.io.dbmode, self.io.db),
559 2 => (self.io.ebmode, self.io.eb),
560 _ => (self.io.fbmode, self.io.fb),
561 };
562 if lo && !mode {
563 self.rom_byte(address)
564 } else {
565 self.rom_byte((bank << 20) | within)
566 }
567 }
568
569 fn rom_cpu_packed(bank: u32, addr: u32) -> u32 {
571 if bank >= 0xC0 {
572 let block = (bank - 0xC0) >> 4; let within = ((bank & 0x0F) << 16) | addr;
575 0x40_0000 | (block << 20) | within
576 } else {
577 let block = match bank {
579 0x00..=0x1F => 0,
580 0x20..=0x3F => 1,
581 0x80..=0x9F => 2,
582 _ => 3,
583 };
584 let within = ((bank & 0x1F) << 15) | (addr & 0x7FFF);
585 (block << 20) | within
586 }
587 }
588
589 fn rom_read_cpu(&self, bank: u32, addr: u32) -> u8 {
590 self.rom_read_packed(Self::rom_cpu_packed(bank, addr))
591 }
592
593 fn rom_read_sa1(&self, address: u32) -> u8 {
596 let packed = if address & 0x40_8000 == 0x00_8000 {
597 ((address & 0x80_0000) >> 2) | ((address & 0x3f_0000) >> 1) | (address & 0x00_7fff)
598 } else {
599 address
600 };
601 self.rom_read_packed(packed)
602 }
603
604 fn bwram_read(&self, off: u32) -> u8 {
607 if self.bwram.is_empty() {
608 return 0;
609 }
610 let size = self.bwram.len() as u32;
611 self.bwram
612 .get(mirror(off, size) as usize)
613 .copied()
614 .unwrap_or(0)
615 }
616
617 fn bwram_write(&mut self, off: u32, val: u8) {
618 if self.bwram.is_empty() {
619 return;
620 }
621 let size = self.bwram.len() as u32;
622 let idx = mirror(off, size) as usize;
623 if let Some(slot) = self.bwram.get_mut(idx) {
624 *slot = val;
625 }
626 }
627
628 fn bwram_read_bitmap(&self, address: u32) -> u8 {
630 if !self.io.bbf {
631 let shift = address & 1;
633 let byte = self.bwram_read(address >> 1);
634 if shift == 0 { byte & 0x0F } else { byte >> 4 }
635 } else {
636 let shift = address & 3;
638 let byte = self.bwram_read(address >> 2);
639 (byte >> (shift * 2)) & 0x03
640 }
641 }
642
643 fn bwram_write_bitmap(&mut self, address: u32, val: u8) {
645 if !self.io.bbf {
646 let shift = address & 1;
647 let a = address >> 1;
648 let cur = self.bwram_read(a);
649 let data = if shift == 0 {
650 (cur & 0xF0) | (val & 0x0F)
651 } else {
652 (cur & 0x0F) | ((val & 0x0F) << 4)
653 };
654 self.bwram_write(a, data);
655 } else {
656 let shift = address & 3;
657 let a = address >> 2;
658 let cur = self.bwram_read(a);
659 let mask = !(0x03u8 << (shift * 2));
660 let data = (cur & mask) | ((val & 0x03) << (shift * 2));
661 self.bwram_write(a, data);
662 }
663 }
664
665 fn bwram_write_linear(&mut self, off: u32, val: u8) {
667 if !self.io.swen && !self.io.cwen && (off & 0x3_ffff) < (0x100u32 << self.io.bwp) {
668 return;
669 }
670 self.bwram_write(off, val);
671 }
672
673 fn bwram_read_cpu(&mut self, bank: u32, addr: u32) -> u8 {
674 if (0x6000..=0x7FFF).contains(&addr) {
675 let off = self.io.sbm as u32 * 0x2000 + (addr & 0x1FFF);
676 if self.bwram_dma {
677 return self.dma_cc1_read(off);
678 }
679 return self.bwram_read(off);
680 }
681 let raw = (bank << 16) | addr;
683 self.bwram_read(raw)
684 }
685
686 fn bwram_write_cpu(&mut self, bank: u32, addr: u32, val: u8) {
687 let off = if (0x6000..=0x7FFF).contains(&addr) {
688 self.io.sbm as u32 * 0x2000 + (addr & 0x1FFF)
689 } else {
690 (bank << 16) | addr
691 };
692 if !self.io.swen && !self.io.cwen && (off & 0x3_ffff) < (0x100u32 << self.io.bwp) {
693 return;
694 }
695 self.bwram_write(off, val);
696 }
697
698 fn bwram_read_sa1(&self, address: u32) -> u8 {
700 if address & 0x40_0000 != 0 && address & 0x20_0000 != 0 {
701 return self.bwram_read_bitmap(address & 0x0f_ffff);
703 }
704 if address & 0x40_0000 != 0 {
705 return self.bwram_read(address);
707 }
708 if !self.io.sw46 {
710 self.bwram_read((self.io.cbm as u32 & 0x1f) * 0x2000 + (address & 0x1fff))
711 } else {
712 self.bwram_read_bitmap(self.io.cbm as u32 * 0x2000 + (address & 0x1fff))
713 }
714 }
715
716 fn bwram_write_sa1(&mut self, address: u32, val: u8) {
717 if address & 0x40_0000 != 0 && address & 0x20_0000 != 0 {
718 self.bwram_write_bitmap(address & 0x0f_ffff, val);
719 return;
720 }
721 if address & 0x40_0000 != 0 {
722 self.bwram_write_linear(address, val);
723 return;
724 }
725 if !self.io.sw46 {
726 self.bwram_write_linear(
727 (self.io.cbm as u32 & 0x1f) * 0x2000 + (address & 0x1fff),
728 val,
729 );
730 } else {
731 self.bwram_write_bitmap(self.io.cbm as u32 * 0x2000 + (address & 0x1fff), val);
732 }
733 }
734
735 fn iram_read(&self, off: u32) -> u8 {
738 self.iram[(off & 0x7FF) as usize]
739 }
740
741 fn iram_write(&mut self, off: u32, val: u8) {
742 self.iram[(off & 0x7FF) as usize] = val;
743 }
744
745 fn iram_write_cpu(&mut self, off: u32, val: u8) {
746 let block = (off >> 8) & 0x7;
747 if self.io.siwp & (1 << block) == 0 {
748 return;
749 }
750 self.iram_write(off, val);
751 }
752
753 fn iram_write_sa1(&mut self, off: u32, val: u8) {
754 let block = (off >> 8) & 0x7;
755 if self.io.ciwp & (1 << block) == 0 {
756 return;
757 }
758 self.iram_write(off, val);
759 }
760
761 fn read_vbr(&self, address: u32) -> u8 {
764 if address & 0x40_8000 == 0x00_8000 || address & 0xc0_0000 == 0xc0_0000 {
765 return self.rom_read_sa1(address);
766 }
767 if address & 0x40_e000 == 0x00_6000 || address & 0xf0_0000 == 0x40_0000 {
768 return self.bwram_read(address);
769 }
770 if address & 0x40_f800 == 0x00_0000 || address & 0x40_f800 == 0x00_3000 {
771 return self.iram_read(address);
772 }
773 0xff
774 }
775
776 fn arith_trigger(&mut self) {
779 if !self.io.acm {
780 if !self.io.md {
781 self.io.mr =
783 ((self.io.ma as i16 as i32) * (self.io.mb as i16 as i32)) as u32 as u64;
784 self.io.mb = 0;
785 } else {
786 if self.io.mb == 0 {
788 self.io.mr = 0;
789 } else {
790 let dividend = self.io.ma as i16 as i32;
791 let divisor = self.io.mb as i32;
792 let remainder = if dividend >= 0 {
793 (dividend % divisor) as u16
794 } else {
795 (((dividend % divisor) + divisor) % divisor) as u16
796 };
797 let quotient = ((dividend - remainder as i32) / divisor) as u16;
798 self.io.mr = ((remainder as u64) << 16) | quotient as u64;
799 }
800 self.io.ma = 0;
801 self.io.mb = 0;
802 }
803 } else {
804 let prod = (self.io.ma as i16 as i64) * (self.io.mb as i16 as i64);
806 self.io.mr = (self.io.mr as i64).wrapping_add(prod) as u64;
807 self.io.overflow = (self.io.mr >> 40) & 1 != 0;
808 self.io.mr &= 0xff_ffff_ffff;
809 self.io.mb = 0;
810 }
811 }
812
813 fn timer_trigger_irq(&mut self) {
816 self.io.timer_irqfl = true;
817 if self.io.timer_irqen {
818 self.io.timer_irqcl = false;
819 }
820 }
821
822 fn tick(&mut self, clocks: u32) {
825 let mut remaining = clocks;
826 while remaining >= 2 {
827 remaining -= 2;
828 if !self.io.hvselb {
829 self.timer.hcounter = self.timer.hcounter.wrapping_add(2);
830 if self.timer.hcounter >= 1364 {
831 self.timer.hcounter = 0;
832 self.timer.vcounter = self.timer.vcounter.wrapping_add(1);
833 if self.timer.vcounter >= self.timer.scanlines {
834 self.timer.vcounter = 0;
835 }
836 }
837 } else {
838 self.timer.hcounter = self.timer.hcounter.wrapping_add(2);
839 self.timer.vcounter = self.timer.vcounter.wrapping_add(self.timer.hcounter >> 11);
840 self.timer.hcounter &= 0x07ff;
841 self.timer.vcounter &= 0x01ff;
842 }
843 match (self.io.hen, self.io.ven) {
844 (false, false) => {}
845 (true, false) => {
846 if self.timer.hcounter == self.io.hcnt << 2 {
847 self.timer_trigger_irq();
848 }
849 }
850 (false, true) => {
851 if self.timer.vcounter == self.io.vcnt && self.timer.hcounter == 0 {
852 self.timer_trigger_irq();
853 }
854 }
855 (true, true) => {
856 if self.timer.vcounter == self.io.vcnt
857 && self.timer.hcounter == self.io.hcnt << 2
858 {
859 self.timer_trigger_irq();
860 }
861 }
862 }
863 }
864 }
865
866 fn dma_normal(&mut self) {
870 const SOURCE_ROM: u8 = 0;
871 const SOURCE_BWRAM: u8 = 1;
872 const SOURCE_IRAM: u8 = 2;
873 const DEST_IRAM: u8 = 0;
874 const DEST_BWRAM: u8 = 1;
875 while self.io.dtc != 0 {
876 self.io.dtc = self.io.dtc.wrapping_sub(1);
877 let source = self.io.dsa;
878 let target = self.io.dda;
879 self.io.dsa = self.io.dsa.wrapping_add(1) & 0xff_ffff;
880 self.io.dda = self.io.dda.wrapping_add(1) & 0xff_ffff;
881 match (self.io.sd, self.io.dd) {
882 (SOURCE_ROM, DEST_BWRAM) => {
883 let data = self.rom_read_sa1(source);
884 self.bwram_write(target, data);
885 }
886 (SOURCE_ROM, DEST_IRAM) => {
887 let data = self.rom_read_sa1(source);
888 self.iram_write(target, data);
889 }
890 (SOURCE_BWRAM, DEST_IRAM) => {
891 let data = self.bwram_read(source);
892 self.iram_write(target, data);
893 }
894 (SOURCE_IRAM, DEST_BWRAM) => {
895 let data = self.iram_read(source);
896 self.bwram_write(target, data);
897 }
898 _ => {}
899 }
900 }
901 self.io.dma_irqfl = true;
902 if self.io.dma_irqen {
903 self.io.dma_irqcl = false;
904 }
905 }
906
907 fn dma_cc1(&mut self) {
910 self.bwram_dma = true;
911 self.io.chdma_irqfl = true;
912 if self.io.chdma_irqen {
913 self.io.chdma_irqcl = false;
914 }
915 }
916
917 fn dma_cc1_read(&mut self, address: u32) -> u8 {
920 let charmask = (1u32 << (6 - self.io.dmacb)) - 1;
921 if address & charmask == 0 {
922 let bpp = 2u32 << (2 - self.io.dmacb);
923 let bpl = (8u32 << self.io.dmasize) >> self.io.dmacb;
924 let bwmask = self.bwram.len() as u32 - 1;
925 let tile = ((address.wrapping_sub(self.io.dsa)) & bwmask) >> (6 - self.io.dmacb);
926 let ty = tile >> self.io.dmasize;
927 let tx = tile & ((1 << self.io.dmasize) - 1);
928 let mut bwaddr = self.io.dsa + ty * 8 * bpl + tx * bpp;
929 for y in 0..8u32 {
930 let mut data: u64 = 0;
931 for byte in 0..bpp {
932 data |= (self.bwram_read((bwaddr + byte) & bwmask) as u64) << (byte << 3);
933 }
934 bwaddr += bpl;
935 let mut out = [0u8; 8];
936 for x in 0..8u32 {
937 out[0] |= ((data & 1) as u8) << (7 - x);
938 data >>= 1;
939 out[1] |= ((data & 1) as u8) << (7 - x);
940 data >>= 1;
941 if self.io.dmacb == 2 {
942 continue;
943 }
944 out[2] |= ((data & 1) as u8) << (7 - x);
945 data >>= 1;
946 out[3] |= ((data & 1) as u8) << (7 - x);
947 data >>= 1;
948 if self.io.dmacb == 1 {
949 continue;
950 }
951 out[4] |= ((data & 1) as u8) << (7 - x);
952 data >>= 1;
953 out[5] |= ((data & 1) as u8) << (7 - x);
954 data >>= 1;
955 out[6] |= ((data & 1) as u8) << (7 - x);
956 data >>= 1;
957 out[7] |= ((data & 1) as u8) << (7 - x);
958 data >>= 1;
959 }
960 for byte in 0..bpp {
961 let p = self.io.dda + (y << 1) + ((byte & 6) << 3) + (byte & 1);
962 self.iram_write(p & 0x07ff, out[byte as usize]);
963 }
964 }
965 }
966 self.iram_read((self.io.dda + (address & charmask)) & 0x07ff)
967 }
968
969 fn dma_cc2(&mut self) {
972 let base = ((self.dma_line & 1) << 3) as usize;
973 let bpp = 2u32 << (2 - self.io.dmacb);
974 let mut address = self.io.dda & 0x07ff;
975 address &= !((1u32 << (7 - self.io.dmacb)) - 1);
976 address += (self.dma_line as u32 & 8) * bpp;
977 address += (self.dma_line as u32 & 7) * 2;
978 for byte in 0..bpp {
979 let mut output = 0u8;
980 for bit in 0..8u32 {
981 output |= ((self.io.brf[base + bit as usize] >> byte) & 1) << (7 - bit);
982 }
983 self.iram_write(address + ((byte & 6) << 3) + (byte & 1), output);
984 }
985 self.dma_line = (self.dma_line + 1) & 15;
986 }
987
988 fn read_io_cpu(&self, address: u32) -> u8 {
991 match 0x2200 | (address & 0x1FF) {
992 0x2300 => {
994 (self.io.cmeg & 0x0f)
995 | (u8::from(self.io.cpu_nvsw) << 4)
996 | (u8::from(self.io.chdma_irqfl) << 5)
997 | (u8::from(self.io.cpu_ivsw) << 6)
998 | (u8::from(self.io.cpu_irqfl) << 7)
999 }
1000 _ => 0,
1001 }
1002 }
1003
1004 fn read_io_sa1(&mut self, address: u32) -> u8 {
1005 match 0x2200 | (address & 0x1FF) {
1006 0x2301 => {
1008 (self.io.smeg & 0x0f)
1009 | (u8::from(self.io.sa1_nmifl) << 4)
1010 | (u8::from(self.io.dma_irqfl) << 5)
1011 | (u8::from(self.io.timer_irqfl) << 6)
1012 | (u8::from(self.io.sa1_irqfl) << 7)
1013 }
1014 0x2302 => {
1016 self.io.hcr = self.timer.hcounter >> 2;
1017 self.io.vcr = self.timer.vcounter;
1018 self.io.hcr as u8
1019 }
1020 0x2303 => (self.io.hcr >> 8) as u8,
1021 0x2304 => self.io.vcr as u8,
1022 0x2305 => (self.io.vcr >> 8) as u8,
1023 0x2306 => self.io.mr as u8,
1025 0x2307 => (self.io.mr >> 8) as u8,
1026 0x2308 => (self.io.mr >> 16) as u8,
1027 0x2309 => (self.io.mr >> 24) as u8,
1028 0x230a => (self.io.mr >> 32) as u8,
1029 0x230b => u8::from(self.io.overflow) << 7,
1031 0x230c => {
1033 let data = self.varlen_word();
1034 data as u8
1035 }
1036 0x230d => {
1037 let data = self.varlen_word();
1038 if self.io.hl {
1039 self.io.vbit += self.io.vb;
1040 self.io.va = self.io.va.wrapping_add((self.io.vbit >> 3) as u32) & 0xff_ffff;
1041 self.io.vbit &= 7;
1042 }
1043 (data >> 8) as u8
1044 }
1045 _ => 0xff,
1046 }
1047 }
1048
1049 fn varlen_word(&self) -> u32 {
1050 let mut data = self.read_vbr(self.io.va) as u32;
1051 data |= (self.read_vbr(self.io.va.wrapping_add(1) & 0xff_ffff) as u32) << 8;
1052 data |= (self.read_vbr(self.io.va.wrapping_add(2) & 0xff_ffff) as u32) << 16;
1053 data >> self.io.vbit
1054 }
1055
1056 fn write_io_cpu(&mut self, address: u32, data: u8) {
1057 match 0x2200 | (address & 0x1FF) {
1058 0x2200 => {
1060 if self.io.sa1_resb && data & 0x20 == 0 {
1061 self.reset_pending = true;
1063 self.io.ciwp = 0x00;
1064 }
1065 self.io.smeg = data & 0x0f;
1066 self.io.sa1_nmi = data & 0x10 != 0;
1067 self.io.sa1_resb = data & 0x20 != 0;
1068 self.io.sa1_rdyb = data & 0x40 != 0;
1069 self.io.sa1_irq = data & 0x80 != 0;
1070 if self.io.sa1_irq {
1071 self.io.sa1_irqfl = true;
1072 if self.io.sa1_irqen {
1073 self.io.sa1_irqcl = false;
1074 }
1075 }
1076 if self.io.sa1_nmi {
1077 self.io.sa1_nmifl = true;
1078 if self.io.sa1_nmien {
1079 self.io.sa1_nmicl = false;
1080 }
1081 }
1082 }
1083 0x2201 => {
1085 self.io.chdma_irqen = data & 0x20 != 0;
1086 self.io.cpu_irqen = data & 0x80 != 0;
1087 }
1088 0x2202 => {
1090 self.io.chdma_irqcl = data & 0x20 != 0;
1091 self.io.cpu_irqcl = data & 0x80 != 0;
1092 if self.io.chdma_irqcl {
1093 self.io.chdma_irqfl = false;
1094 }
1095 if self.io.cpu_irqcl {
1096 self.io.cpu_irqfl = false;
1097 }
1098 }
1099 0x2203 => self.io.crv = (self.io.crv & 0xff00) | data as u16,
1101 0x2204 => self.io.crv = (self.io.crv & 0x00ff) | ((data as u16) << 8),
1102 0x2205 => self.io.cnv = (self.io.cnv & 0xff00) | data as u16,
1104 0x2206 => self.io.cnv = (self.io.cnv & 0x00ff) | ((data as u16) << 8),
1105 0x2207 => self.io.civ = (self.io.civ & 0xff00) | data as u16,
1107 0x2208 => self.io.civ = (self.io.civ & 0x00ff) | ((data as u16) << 8),
1108 0x2220 => {
1110 self.io.cb = (data & 0x07) as u32;
1111 self.io.cbmode = data & 0x80 != 0;
1112 }
1113 0x2221 => {
1114 self.io.db = (data & 0x07) as u32;
1115 self.io.dbmode = data & 0x80 != 0;
1116 }
1117 0x2222 => {
1118 self.io.eb = (data & 0x07) as u32;
1119 self.io.ebmode = data & 0x80 != 0;
1120 }
1121 0x2223 => {
1122 self.io.fb = (data & 0x07) as u32;
1123 self.io.fbmode = data & 0x80 != 0;
1124 }
1125 0x2224 => self.io.sbm = data & 0x1f,
1127 0x2226 => self.io.swen = data & 0x80 != 0,
1129 0x2228 => self.io.bwp = data & 0x0f,
1131 0x2229 => self.io.siwp = data,
1133 0x2231..=0x2237 => self.write_io_shared(address, data),
1134 _ => {}
1135 }
1136 }
1137
1138 fn write_io_sa1(&mut self, address: u32, data: u8) {
1139 match 0x2200 | (address & 0x1FF) {
1140 0x2209 => {
1142 self.io.cmeg = data & 0x0f;
1143 self.io.cpu_nvsw = data & 0x10 != 0;
1144 self.io.cpu_ivsw = data & 0x40 != 0;
1145 self.io.cpu_irq = data & 0x80 != 0;
1146 if self.io.cpu_irq {
1147 self.io.cpu_irqfl = true;
1148 if self.io.cpu_irqen {
1149 self.io.cpu_irqcl = false;
1150 }
1151 }
1152 }
1153 0x220a => {
1155 self.io.sa1_nmien = data & 0x10 != 0;
1156 self.io.dma_irqen = data & 0x20 != 0;
1157 self.io.timer_irqen = data & 0x40 != 0;
1158 self.io.sa1_irqen = data & 0x80 != 0;
1159 }
1160 0x220b => {
1162 self.io.sa1_nmicl = data & 0x10 != 0;
1163 self.io.dma_irqcl = data & 0x20 != 0;
1164 self.io.timer_irqcl = data & 0x40 != 0;
1165 self.io.sa1_irqcl = data & 0x80 != 0;
1166 if self.io.sa1_nmicl {
1167 self.io.sa1_nmifl = false;
1168 }
1169 if self.io.sa1_irqcl {
1170 self.io.sa1_irqfl = false;
1171 }
1172 if self.io.timer_irqcl {
1173 self.io.timer_irqfl = false;
1174 }
1175 if self.io.dma_irqcl {
1176 self.io.dma_irqfl = false;
1177 }
1178 }
1179 0x220c => self.io.snv = (self.io.snv & 0xff00) | data as u16,
1181 0x220d => self.io.snv = (self.io.snv & 0x00ff) | ((data as u16) << 8),
1182 0x220e => self.io.siv = (self.io.siv & 0xff00) | data as u16,
1184 0x220f => self.io.siv = (self.io.siv & 0x00ff) | ((data as u16) << 8),
1185 0x2210 => {
1187 self.io.hen = data & 0x01 != 0;
1188 self.io.ven = data & 0x02 != 0;
1189 self.io.hvselb = data & 0x80 != 0;
1190 }
1191 0x2211 => {
1193 self.timer.vcounter = 0;
1194 self.timer.hcounter = 0;
1195 }
1196 0x2212 => self.io.hcnt = (self.io.hcnt & 0xff00) | data as u16,
1198 0x2213 => self.io.hcnt = (self.io.hcnt & 0x00ff) | ((data as u16) << 8),
1199 0x2214 => self.io.vcnt = (self.io.vcnt & 0xff00) | data as u16,
1200 0x2215 => self.io.vcnt = (self.io.vcnt & 0x00ff) | ((data as u16) << 8),
1201 0x2225 => {
1203 self.io.cbm = data & 0x7f;
1204 self.io.sw46 = data & 0x80 != 0;
1205 }
1206 0x2227 => self.io.cwen = data & 0x80 != 0,
1208 0x222a => self.io.ciwp = data,
1210 0x2230 => {
1212 self.io.sd = data & 0x03;
1213 self.io.dd = (data >> 2) & 1;
1214 self.io.cdsel = data & 0x10 != 0;
1215 self.io.cden = data & 0x20 != 0;
1216 self.io.dprio = data & 0x40 != 0;
1217 self.io.dmaen = data & 0x80 != 0;
1218 if !self.io.dmaen {
1219 self.dma_line = 0;
1220 }
1221 }
1222 0x2231..=0x2237 => self.write_io_shared(address, data),
1223 0x2238 => self.io.dtc = (self.io.dtc & 0xff00) | data as u16,
1225 0x2239 => self.io.dtc = (self.io.dtc & 0x00ff) | ((data as u16) << 8),
1226 0x223f => self.io.bbf = data & 0x80 != 0,
1228 0x2240..=0x224f => {
1230 let idx = (address & 0x0f) as usize;
1231 self.io.brf[idx] = data;
1232 if (idx == 7 || idx == 15) && self.io.dmaen && self.io.cden && !self.io.cdsel {
1233 self.dma_cc2();
1234 }
1235 }
1236 0x2250 => {
1238 self.io.md = data & 0x01 != 0;
1239 self.io.acm = data & 0x02 != 0;
1240 if self.io.acm {
1241 self.io.mr = 0;
1242 }
1243 }
1244 0x2251 => self.io.ma = (self.io.ma & 0xff00) | data as u16,
1246 0x2252 => self.io.ma = (self.io.ma & 0x00ff) | ((data as u16) << 8),
1247 0x2253 => self.io.mb = (self.io.mb & 0xff00) | data as u16,
1248 0x2254 => {
1249 self.io.mb = (self.io.mb & 0x00ff) | ((data as u16) << 8);
1250 self.arith_trigger();
1251 }
1252 0x2258 => {
1254 self.io.vb = data & 0x0f;
1255 self.io.hl = data & 0x80 != 0;
1256 if self.io.vb == 0 {
1257 self.io.vb = 16;
1258 }
1259 if !self.io.hl {
1260 self.io.vbit += self.io.vb;
1261 self.io.va = self.io.va.wrapping_add((self.io.vbit >> 3) as u32) & 0xff_ffff;
1262 self.io.vbit &= 7;
1263 }
1264 }
1265 0x2259 => self.io.va = (self.io.va & 0xff_ff00) | data as u32,
1267 0x225a => self.io.va = (self.io.va & 0xff_00ff) | ((data as u32) << 8),
1268 0x225b => {
1269 self.io.va = (self.io.va & 0x00_ffff) | ((data as u32) << 16);
1270 self.io.vbit = 0;
1271 }
1272 _ => {}
1273 }
1274 }
1275
1276 fn write_io_shared(&mut self, address: u32, data: u8) {
1277 match 0x2200 | (address & 0x1FF) {
1278 0x2231 => {
1280 self.io.dmacb = data & 0x03;
1281 self.io.dmasize = (data >> 2) & 0x07;
1282 self.io.chdend = data & 0x80 != 0;
1283 if self.io.dmacb > 2 {
1284 self.io.dmacb = 2;
1285 }
1286 if self.io.dmasize > 5 {
1287 self.io.dmasize = 5;
1288 }
1289 if self.io.chdend {
1290 self.bwram_dma = false;
1291 }
1292 }
1293 0x2232 => self.io.dsa = (self.io.dsa & 0xff_ff00) | data as u32,
1295 0x2233 => self.io.dsa = (self.io.dsa & 0xff_00ff) | ((data as u32) << 8),
1296 0x2234 => self.io.dsa = (self.io.dsa & 0x00_ffff) | ((data as u32) << 16),
1297 0x2235 => self.io.dda = (self.io.dda & 0xff_ff00) | data as u32,
1299 0x2236 => {
1300 self.io.dda = (self.io.dda & 0xff_00ff) | ((data as u32) << 8);
1301 if self.io.dmaen {
1302 if !self.io.cden && self.io.dd == 0 {
1303 self.dma_normal();
1304 } else if self.io.cden && self.io.cdsel {
1305 self.dma_cc1();
1306 }
1307 }
1308 }
1309 0x2237 => {
1310 self.io.dda = (self.io.dda & 0x00_ffff) | ((data as u32) << 16);
1311 if self.io.dmaen && !self.io.cden && self.io.dd == 1 {
1312 self.dma_normal();
1313 }
1314 }
1315 _ => {}
1316 }
1317 }
1318
1319 fn classify_cpu(addr24: u32) -> CpuRegion {
1322 let bank = (addr24 >> 16) & 0xFF;
1323 let addr = addr24 & 0xFFFF;
1324 let lo = bank & 0x7F; if lo <= 0x3F {
1326 if (0x2200..=0x23FF).contains(&addr) {
1327 return CpuRegion::Io;
1328 }
1329 if (0x3000..=0x37FF).contains(&addr) {
1330 return CpuRegion::Iram(addr & 0x7FF);
1331 }
1332 if (0x6000..=0x7FFF).contains(&addr) {
1333 return CpuRegion::Bwram;
1334 }
1335 if addr >= 0x8000 {
1336 return CpuRegion::Rom;
1337 }
1338 }
1339 if (0x40..=0x4F).contains(&bank) {
1340 return CpuRegion::Bwram;
1341 }
1342 if bank >= 0xC0 {
1343 return CpuRegion::Rom;
1344 }
1345 CpuRegion::Open
1346 }
1347}
1348
1349enum CpuRegion {
1351 Io,
1352 Rom,
1353 Bwram,
1354 Iram(u32),
1355 Open,
1356}
1357
1358impl Board for Sa1Board {
1359 fn name(&self) -> &'static str {
1360 "SA-1"
1361 }
1362
1363 fn coprocessor(&self) -> Coprocessor {
1364 Coprocessor::Sa1
1365 }
1366
1367 fn map(&self, addr24: u32) -> MappedAddr {
1368 match Self::classify_cpu(addr24) {
1369 CpuRegion::Io | CpuRegion::Iram(_) => MappedAddr::Coprocessor,
1370 CpuRegion::Rom => MappedAddr::Rom(0),
1371 CpuRegion::Bwram => MappedAddr::Sram(0),
1372 CpuRegion::Open => MappedAddr::Open,
1373 }
1374 }
1375
1376 fn read24(&mut self, addr24: u32) -> u8 {
1377 let bank = (addr24 >> 16) & 0xFF;
1378 let addr = addr24 & 0xFFFF;
1379 match Self::classify_cpu(addr24) {
1380 CpuRegion::Io => {
1381 self.host_accesses = self.host_accesses.wrapping_add(1);
1382 self.read_io_cpu(addr24)
1383 }
1384 CpuRegion::Rom => {
1385 let b = if bank >= 0xC0 { bank } else { bank & 0x7F };
1386 self.rom_read_cpu(b, addr)
1387 }
1388 CpuRegion::Bwram => {
1389 let b = if (0x40..=0x4F).contains(&bank) {
1390 bank
1391 } else {
1392 bank & 0x7F
1393 };
1394 self.bwram_read_cpu(b, addr)
1395 }
1396 CpuRegion::Iram(off) => self.iram_read(off),
1397 CpuRegion::Open => 0,
1398 }
1399 }
1400
1401 fn write24(&mut self, addr24: u32, val: u8) {
1402 let bank = (addr24 >> 16) & 0xFF;
1403 let addr = addr24 & 0xFFFF;
1404 match Self::classify_cpu(addr24) {
1405 CpuRegion::Io => {
1406 self.host_accesses = self.host_accesses.wrapping_add(1);
1407 self.write_io_cpu(addr24, val);
1408 }
1409 CpuRegion::Bwram => {
1410 let b = if (0x40..=0x4F).contains(&bank) {
1411 bank
1412 } else {
1413 bank & 0x7F
1414 };
1415 self.bwram_write_cpu(b, addr, val);
1416 }
1417 CpuRegion::Iram(off) => self.iram_write_cpu(off, val),
1418 CpuRegion::Rom | CpuRegion::Open => {}
1419 }
1420 }
1421
1422 fn rom(&self) -> &[u8] {
1423 &self.rom
1424 }
1425
1426 fn sram(&self) -> &[u8] {
1427 &self.bwram
1428 }
1429
1430 fn sram_mut(&mut self) -> &mut [u8] {
1431 &mut self.bwram
1432 }
1433
1434 fn irq_pending(&self) -> bool {
1435 (self.io.cpu_irqen && self.io.cpu_irqfl) || (self.io.chdma_irqen && self.io.chdma_irqfl)
1436 }
1437
1438 fn coprocessor_host_accesses(&self) -> u64 {
1439 self.host_accesses
1440 }
1441
1442 fn save_state(&self, w: &mut SaveWriter) {
1447 w.section(*b"SA10", |s| {
1448 self.io.save_state(s);
1449 s.write_u16(self.timer.scanlines);
1450 s.write_u16(self.timer.vcounter);
1451 s.write_u16(self.timer.hcounter);
1452 s.write_bool(self.reset_pending);
1453 s.write_bool(self.bwram_dma);
1454 s.write_u8(self.dma_line);
1455 s.write_bytes(&*self.iram);
1456 });
1457 }
1458
1459 fn load_state(&mut self, r: &mut SaveReader) -> Result<(), SaveStateError> {
1460 let mut s = r.expect_section(*b"SA10")?;
1461 self.io.load_state(&mut s)?;
1462 self.timer.scanlines = s.read_u16()?;
1463 self.timer.vcounter = s.read_u16()?;
1464 self.timer.hcounter = s.read_u16()?;
1465 self.reset_pending = s.read_bool()?;
1466 self.bwram_dma = s.read_bool()?;
1467 self.dma_line = s.read_u8()? & 15; self.iram.copy_from_slice(s.read_bytes(IRAM_SIZE)?);
1469 if s.remaining() != 0 {
1470 return Err(SaveStateError::Invalid(alloc::format!(
1471 "SA10 section has {} trailing byte(s)",
1472 s.remaining()
1473 )));
1474 }
1475 Ok(())
1476 }
1477
1478 fn has_second_cpu(&self) -> bool {
1481 true
1482 }
1483
1484 fn second_cpu_running(&self) -> bool {
1485 !(self.io.sa1_rdyb || self.io.sa1_resb)
1486 }
1487
1488 fn second_cpu_take_reset(&mut self) -> bool {
1489 core::mem::take(&mut self.reset_pending)
1490 }
1491
1492 fn second_cpu_poll_nmi(&mut self) -> bool {
1493 if self.io.sa1_nmi && !self.io.sa1_nmicl {
1494 self.io.sa1_nmifl = true;
1495 self.io.sa1_nmicl = true;
1496 return true;
1497 }
1498 false
1499 }
1500
1501 fn second_cpu_poll_irq(&self) -> bool {
1502 (self.io.timer_irqen && !self.io.timer_irqcl)
1503 || (self.io.dma_irqen && !self.io.dma_irqcl)
1504 || (self.io.sa1_irq && !self.io.sa1_irqcl)
1505 }
1506
1507 fn second_cpu_tick(&mut self, clocks: u32) {
1508 self.tick(clocks);
1509 }
1510
1511 fn second_cpu_read(&mut self, addr24: u32) -> u8 {
1512 let bank = (addr24 >> 16) & 0xFF;
1513 let addr = addr24 & 0xFFFF;
1514 if bank == 0x00 {
1516 match addr {
1517 0xFFEA | 0xFFFA => return self.io.cnv as u8, 0xFFEB | 0xFFFB => return (self.io.cnv >> 8) as u8,
1519 0xFFEE | 0xFFFE => return self.io.civ as u8, 0xFFEF | 0xFFFF => return (self.io.civ >> 8) as u8,
1521 0xFFFC => return self.io.crv as u8, 0xFFFD => return (self.io.crv >> 8) as u8,
1523 _ => {}
1524 }
1525 }
1526 if addr24 & 0x40_fe00 == 0x00_2200 {
1528 return self.read_io_sa1(addr24);
1529 }
1530 if addr24 & 0x40_8000 == 0x00_8000 || addr24 & 0xc0_0000 == 0xc0_0000 {
1532 return self.rom_read_sa1(addr24);
1533 }
1534 if addr24 & 0x40_e000 == 0x00_6000
1536 || addr24 & 0xe0_0000 == 0x40_0000
1537 || addr24 & 0xf0_0000 == 0x60_0000
1538 {
1539 return self.bwram_read_sa1(addr24);
1540 }
1541 if addr24 & 0x40_f800 == 0x00_0000 || addr24 & 0x40_f800 == 0x00_3000 {
1543 return self.iram_read(addr24 & 0x7FF);
1544 }
1545 0
1546 }
1547
1548 fn second_cpu_write(&mut self, addr24: u32, val: u8) {
1549 if addr24 & 0x40_fe00 == 0x00_2200 {
1550 self.write_io_sa1(addr24, val);
1551 return;
1552 }
1553 if addr24 & 0x40_8000 == 0x00_8000 || addr24 & 0xc0_0000 == 0xc0_0000 {
1554 return; }
1556 if addr24 & 0x40_e000 == 0x00_6000
1557 || addr24 & 0xe0_0000 == 0x40_0000
1558 || addr24 & 0xf0_0000 == 0x60_0000
1559 {
1560 self.bwram_write_sa1(addr24, val);
1561 return;
1562 }
1563 if addr24 & 0x40_f800 == 0x00_0000 || addr24 & 0x40_f800 == 0x00_3000 {
1564 self.iram_write_sa1(addr24 & 0x7FF, val);
1565 }
1566 }
1567}
1568
1569#[must_use]
1571pub fn select(rom: Box<[u8]>, sram_size: usize, region: Region) -> Sa1Board {
1572 Sa1Board::new(rom, sram_size, region)
1573}
1574
1575#[cfg(test)]
1576mod tests {
1577 use super::*;
1578
1579 fn board() -> Sa1Board {
1580 Sa1Board::new(vec![0u8; 0x10_0000].into_boxed_slice(), 0, Region::Ntsc)
1582 }
1583
1584 #[test]
1585 fn detects_sa1_and_has_second_cpu() {
1586 let b = board();
1587 assert_eq!(b.coprocessor(), Coprocessor::Sa1);
1588 assert!(b.has_second_cpu());
1589 assert!(!b.second_cpu_running());
1591 }
1592
1593 #[test]
1594 fn cpu_decode_regions() {
1595 let b = board();
1596 assert!(matches!(b.map(0x00_2200), MappedAddr::Coprocessor)); assert!(matches!(b.map(0x00_3000), MappedAddr::Coprocessor)); assert!(matches!(b.map(0x00_6000), MappedAddr::Sram(_))); assert!(matches!(b.map(0x40_0000), MappedAddr::Sram(_))); assert!(matches!(b.map(0x00_8000), MappedAddr::Rom(_))); assert!(matches!(b.map(0xC0_0000), MappedAddr::Rom(_))); }
1603
1604 #[test]
1605 fn reset_handshake_and_vector_intercept() {
1606 let mut b = board();
1607 b.write24(0x00_2203, 0x34);
1609 b.write24(0x00_2204, 0x12);
1610 b.write24(0x00_2200, 0x00); assert!(b.second_cpu_take_reset());
1612 assert!(!b.second_cpu_take_reset()); assert!(b.second_cpu_running());
1614 assert_eq!(b.second_cpu_read(0x00_FFFC), 0x34);
1616 assert_eq!(b.second_cpu_read(0x00_FFFD), 0x12);
1617 }
1618
1619 #[test]
1620 fn arithmetic_unit_mul_div() {
1621 let mut b = board();
1622 b.second_cpu_write(0x00_2250, 0x00);
1625 b.second_cpu_write(0x00_2251, 0x10);
1626 b.second_cpu_write(0x00_2252, 0x00);
1627 b.second_cpu_write(0x00_2253, 0x10);
1628 b.second_cpu_write(0x00_2254, 0x00); assert_eq!(b.second_cpu_read(0x00_2306), 0x00);
1630 assert_eq!(b.second_cpu_read(0x00_2307), 0x01); b.second_cpu_write(0x00_2250, 0x01);
1633 b.second_cpu_write(0x00_2251, 100);
1634 b.second_cpu_write(0x00_2252, 0x00);
1635 b.second_cpu_write(0x00_2253, 7);
1636 b.second_cpu_write(0x00_2254, 0x00);
1637 assert_eq!(b.second_cpu_read(0x00_2306), 14); assert_eq!(b.second_cpu_read(0x00_2308), 2); }
1640
1641 #[test]
1642 fn iram_roundtrip_both_views() {
1643 let mut b = board();
1644 b.write24(0x00_2229, 0xFF);
1646 b.write24(0x00_3010, 0x5A);
1647 assert_eq!(b.read24(0x00_3010), 0x5A);
1648 assert_eq!(b.second_cpu_read(0x00_3010), 0x5A);
1650 }
1651
1652 #[test]
1653 fn bwram_roundtrip_main_view() {
1654 let mut b = board();
1655 b.write24(0x00_2226, 0x80);
1657 b.write24(0x40_0123, 0x77); assert_eq!(b.read24(0x40_0123), 0x77);
1659 }
1660
1661 #[test]
1662 fn rom_window_reads_image() {
1663 let mut rom = vec![0u8; 0x10_0000];
1664 rom[0x0000] = 0xAA;
1665 rom[0x8000] = 0xBB;
1666 let mut b = Sa1Board::new(rom.into_boxed_slice(), 0, Region::Ntsc);
1667 assert_eq!(b.read24(0x00_8000), 0xAA);
1669 assert_eq!(b.read24(0x01_8000), 0xBB);
1671 }
1672
1673 #[test]
1674 fn system_state_round_trips_through_save_state() {
1675 let mut b = board();
1676 b.write24(0x00_2203, 0x34); b.write24(0x00_2204, 0x12); b.write24(0x00_2200, 0x00); b.write24(0x00_2229, 0xFF); b.write24(0x00_3010, 0x5A); let mut w = SaveWriter::new();
1683 b.save_state(&mut w);
1684 let bytes = w.into_bytes();
1685
1686 let mut fresh = board();
1687 let mut r = SaveReader::new(&bytes);
1688 fresh.load_state(&mut r).unwrap();
1689
1690 assert_eq!(fresh.second_cpu_read(0x00_FFFC), 0x34);
1691 assert_eq!(fresh.second_cpu_read(0x00_FFFD), 0x12);
1692 assert!(fresh.second_cpu_take_reset());
1693 assert_eq!(fresh.read24(0x00_3010), 0x5A);
1694 assert_eq!(r.remaining(), 0);
1695 }
1696}