1#![allow(
31 clippy::cast_possible_truncation,
32 clippy::cast_possible_wrap,
33 clippy::cast_sign_loss,
34 clippy::struct_excessive_bools,
35 clippy::similar_names,
36 clippy::verbose_bit_mask,
37 clippy::doc_markdown,
38 clippy::missing_const_for_fn
39)]
40
41use alloc::boxed::Box;
42use alloc::vec;
43
44use rustysnes_savestate::{SaveReader, SaveStateError, SaveWriter};
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum Revision {
49 Upd7725,
51 Upd96050,
53}
54
55impl Revision {
56 const fn pc_mask(self) -> u16 {
58 match self {
59 Self::Upd7725 => 0x07FF, Self::Upd96050 => 0x3FFF, }
62 }
63
64 const fn rp_mask(self) -> u16 {
66 match self {
67 Self::Upd7725 => 0x03FF, Self::Upd96050 => 0x07FF, }
70 }
71
72 const fn dp_mask(self) -> u16 {
74 match self {
75 Self::Upd7725 => 0x00FF, Self::Upd96050 => 0x07FF, }
78 }
79
80 const fn program_words(self) -> usize {
82 match self {
83 Self::Upd7725 => 2048,
84 Self::Upd96050 => 16384,
85 }
86 }
87
88 const fn data_words(self) -> usize {
90 match self {
91 Self::Upd7725 => 1024,
92 Self::Upd96050 => 2048,
93 }
94 }
95}
96
97#[derive(Debug, Clone, Copy, Default)]
99struct Flag {
100 ov0: bool, ov1: bool, z: bool, c: bool, s0: bool, s1: bool, }
107
108impl Flag {
109 fn to_bits(self) -> u16 {
110 u16::from(self.ov0)
111 | u16::from(self.ov1) << 1
112 | u16::from(self.z) << 2
113 | u16::from(self.c) << 3
114 | u16::from(self.s0) << 4
115 | u16::from(self.s1) << 5
116 }
117}
118
119#[derive(Debug, Clone, Copy, Default)]
121struct Status {
122 p0: bool, p1: bool, ei: bool, sic: bool, soc: bool, drc: bool, dma: bool, drs: bool, usf0: bool, usf1: bool, rqm: bool, siack: bool,
134 soack: bool,
135}
136
137impl Status {
138 fn to_bits(self) -> u16 {
139 let drs = self.drs && !self.drc; u16::from(self.p0)
141 | u16::from(self.p1) << 1
142 | u16::from(self.ei) << 7
143 | u16::from(self.sic) << 8
144 | u16::from(self.soc) << 9
145 | u16::from(self.drc) << 10
146 | u16::from(self.dma) << 11
147 | u16::from(drs) << 12
148 | u16::from(self.usf0) << 13
149 | u16::from(self.usf1) << 14
150 | u16::from(self.rqm) << 15
151 }
152
153 fn set_bits(&mut self, data: u16) {
154 self.p0 = data & 1 != 0;
155 self.p1 = data & 1 << 1 != 0;
156 self.ei = data & 1 << 7 != 0;
157 self.sic = data & 1 << 8 != 0;
158 self.soc = data & 1 << 9 != 0;
159 self.drc = data & 1 << 10 != 0;
160 self.dma = data & 1 << 11 != 0;
161 self.drs = data & 1 << 12 != 0;
162 self.usf0 = data & 1 << 13 != 0;
163 self.usf1 = data & 1 << 14 != 0;
164 self.rqm = data & 1 << 15 != 0;
165 }
166}
167
168#[derive(Debug, Clone)]
176pub struct Upd77c25 {
177 revision: Revision,
178 firmware_loaded: bool,
179 host_accesses: u64,
181
182 program_rom: Box<[u32]>,
183 data_rom: Box<[u16]>,
184 data_ram: Box<[u16]>,
185
186 stack: [u16; 16],
187 pc: u16,
188 rp: u16,
189 dp: u16,
190 sp: u8,
191 si: u16,
192 so: u16,
193 k: i16,
194 l: i16,
195 m: i16,
196 n: i16,
197 a: i16,
198 b: i16,
199 tr: u16,
200 trb: u16,
201 dr: u16,
202 sr: Status,
203 flag_a: Flag,
204 flag_b: Flag,
205}
206
207impl Upd77c25 {
208 const RUN_CAP: u32 = 0x10_0000;
211
212 #[must_use]
214 pub fn new(revision: Revision) -> Self {
215 let program_rom = vec![0u32; revision.program_words()].into_boxed_slice();
216 let data_rom = vec![0u16; revision.data_words()].into_boxed_slice();
217 let data_ram = vec![0u16; 2048].into_boxed_slice();
220 let mut me = Self {
221 revision,
222 firmware_loaded: false,
223 host_accesses: 0,
224 program_rom,
225 data_rom,
226 data_ram,
227 stack: [0; 16],
228 pc: 0,
229 rp: 0,
230 dp: 0,
231 sp: 0,
232 si: 0,
233 so: 0,
234 k: 0,
235 l: 0,
236 m: 0,
237 n: 0,
238 a: 0,
239 b: 0,
240 tr: 0,
241 trb: 0,
242 dr: 0,
243 sr: Status::default(),
244 flag_a: Flag::default(),
245 flag_b: Flag::default(),
246 };
247 me.power();
248 me
249 }
250
251 pub fn power(&mut self) {
253 self.stack = [0; 16];
254 self.pc = 0;
255 self.rp = 0;
256 self.dp = 0;
257 self.sp = 0;
258 self.si = 0;
259 self.so = 0;
260 self.k = 0;
261 self.l = 0;
262 self.m = 0;
263 self.n = 0;
264 self.a = 0;
265 self.b = 0;
266 self.tr = 0;
267 self.trb = 0;
268 self.dr = 0;
269 self.sr = Status::default();
270 self.flag_a = Flag::default();
271 self.flag_b = Flag::default();
272 }
273
274 #[must_use]
276 pub const fn firmware_loaded(&self) -> bool {
277 self.firmware_loaded
278 }
279
280 pub fn load_firmware(&mut self, bytes: &[u8]) -> bool {
291 let prog_bytes = self.program_rom.len() * 3;
292 let data_bytes = self.data_rom.len() * 2;
293 if bytes.len() < prog_bytes + data_bytes {
294 return false;
295 }
296 for (i, word) in self.program_rom.iter_mut().enumerate() {
297 let o = i * 3;
298 *word =
299 u32::from(bytes[o]) | u32::from(bytes[o + 1]) << 8 | u32::from(bytes[o + 2]) << 16;
300 }
301 for (i, word) in self.data_rom.iter_mut().enumerate() {
302 let o = prog_bytes + i * 2;
303 *word = u16::from(bytes[o]) | u16::from(bytes[o + 1]) << 8;
304 }
305 self.firmware_loaded = true;
306 self.power();
307 self.run_until_rqm();
309 true
310 }
311
312 #[must_use]
316 pub fn read_sr(&self) -> u8 {
317 if !self.firmware_loaded {
318 return 0;
319 }
320 (self.sr.to_bits() >> 8) as u8
321 }
322
323 pub fn write_sr(&mut self, _data: u8) {}
326
327 #[must_use]
329 pub fn read_dr(&mut self) -> u8 {
330 if !self.firmware_loaded {
331 return 0;
332 }
333 self.host_accesses += 1;
334 let value = if self.sr.drc {
335 self.sr.rqm = false;
337 self.dr as u8
338 } else if self.sr.drs {
339 self.sr.rqm = false;
341 self.sr.drs = false;
342 (self.dr >> 8) as u8
343 } else {
344 self.sr.drs = true;
346 self.dr as u8
347 };
348 self.run_until_rqm();
349 value
350 }
351
352 pub fn write_dr(&mut self, data: u8) {
354 if !self.firmware_loaded {
355 return;
356 }
357 self.host_accesses += 1;
358 if self.sr.drc {
359 self.sr.rqm = false;
361 self.dr = (self.dr & 0xFF00) | u16::from(data);
362 } else if self.sr.drs {
363 self.sr.rqm = false;
365 self.sr.drs = false;
366 self.dr = u16::from(data) << 8 | (self.dr & 0x00FF);
367 } else {
368 self.sr.drs = true;
370 self.dr = (self.dr & 0xFF00) | u16::from(data);
371 }
372 self.run_until_rqm();
373 }
374
375 #[must_use]
377 pub fn read_dp(&self, address: u16) -> u8 {
378 if !self.firmware_loaded {
379 return 0;
380 }
381 let hi = address & 1 != 0;
382 let word = self.data_ram[usize::from((address >> 1) & 2047)];
383 if hi { (word >> 8) as u8 } else { word as u8 }
384 }
385
386 pub fn write_dp(&mut self, address: u16, data: u8) {
388 if !self.firmware_loaded {
389 return;
390 }
391 let hi = address & 1 != 0;
392 let slot = &mut self.data_ram[usize::from((address >> 1) & 2047)];
393 if hi {
394 *slot = (*slot & 0x00FF) | u16::from(data) << 8;
395 } else {
396 *slot = (*slot & 0xFF00) | u16::from(data);
397 }
398 }
399
400 pub fn run_until_rqm(&mut self) {
403 if !self.firmware_loaded {
404 return;
405 }
406 let mut budget = Self::RUN_CAP;
407 while !self.sr.rqm && budget > 0 {
408 self.exec();
409 budget -= 1;
410 }
411 }
412
413 fn set_pc(&mut self, value: u16) {
416 self.pc = value & self.revision.pc_mask();
417 }
418
419 pub fn exec(&mut self) {
422 let opcode = self.program_rom[usize::from(self.pc)];
423 self.set_pc(self.pc.wrapping_add(1));
424 match opcode >> 22 {
425 0 => self.exec_op(opcode),
426 1 => self.exec_rt(opcode),
427 2 => self.exec_jp(opcode),
428 _ => self.exec_ld(opcode),
429 }
430
431 let result = i32::from(self.k) * i32::from(self.l); self.m = (result >> 15) as i16; self.n = (result << 1) as i16; }
435
436 #[allow(clippy::too_many_lines)]
437 fn exec_op(&mut self, opcode: u32) {
438 let pselect = (opcode >> 20) & 0x3;
439 let alu = (opcode >> 16) & 0xF;
440 let asl = (opcode >> 15) & 0x1;
441 let dpl = (opcode >> 13) & 0x3;
442 let dphm = ((opcode >> 9) & 0xF) as u16;
443 let rpdcr = (opcode >> 8) & 0x1;
444 let src = (opcode >> 4) & 0xF;
445 let dst = opcode & 0xF;
446
447 let idb: u16 = match src {
448 0 => self.trb,
449 1 => self.a as u16,
450 2 => self.b as u16,
451 3 => self.tr,
452 4 => self.dp,
453 5 => self.rp,
454 6 => self.data_rom[usize::from(self.rp)],
455 7 => 0x8000 - u16::from(self.flag_a.s1), 8 => {
457 self.sr.rqm = true;
458 self.dr
459 }
460 9 => self.dr,
461 10 => self.sr.to_bits(),
462 11 | 12 => self.si,
463 13 => self.k as u16,
464 14 => self.l as u16,
465 _ => self.data_ram[usize::from(self.dp & self.revision.dp_mask())],
466 };
467
468 if alu != 0 {
469 let mut p: u16 = match pselect {
470 0 => self.data_ram[usize::from(self.dp & self.revision.dp_mask())],
471 1 => idb,
472 2 => self.m as u16,
473 _ => self.n as u16,
474 };
475
476 let (q, mut flag, c) = if asl == 0 {
477 (self.a as u16, self.flag_a, self.flag_b.c)
478 } else {
479 (self.b as u16, self.flag_b, self.flag_a.c)
480 };
481 let cu = u16::from(c);
482
483 let r: u16 = match alu {
484 1 => q | p,
485 2 => q & p,
486 3 => q ^ p,
487 4 => q.wrapping_sub(p),
488 5 => q.wrapping_add(p),
489 6 => q.wrapping_sub(p).wrapping_sub(cu),
490 7 => q.wrapping_add(p).wrapping_add(cu),
491 8 => {
492 p = 1;
493 q.wrapping_sub(1)
494 }
495 9 => {
496 p = 1;
497 q.wrapping_add(1)
498 }
499 10 => !q,
500 11 => (q >> 1) | (q & 0x8000), 12 => (q << 1) | cu, 13 => (q << 2) | 3, 14 => (q << 4) | 15, _ => q.rotate_left(8), };
506
507 flag.z = r == 0;
508 flag.s0 = r & 0x8000 != 0;
509 if !flag.ov1 {
510 flag.s1 = flag.s0;
511 }
512
513 match alu {
514 1 | 2 | 3 | 10 | 13 | 14 | 15 => {
515 flag.ov0 = false;
516 flag.ov1 = false;
517 flag.c = false;
518 }
519 11 => {
520 flag.ov0 = false;
521 flag.ov1 = false;
522 flag.c = q & 1 != 0;
523 }
524 12 => {
525 flag.ov0 = false;
526 flag.ov1 = false;
527 flag.c = q >> 15 != 0;
528 }
529 _ => {
531 let carries = q ^ p ^ r;
532 let second = if alu & 1 != 0 { r } else { q };
533 let overflow = (q ^ r) & (p ^ second);
534 let ov0 = overflow & 0x8000 != 0;
535 flag.ov1 = if ov0 && flag.ov1 {
536 flag.s0 == flag.s1
537 } else {
538 ov0 || flag.ov1
539 };
540 flag.ov0 = ov0;
541 flag.c = (carries ^ overflow) & 0x8000 != 0;
542 }
543 }
544
545 if asl == 0 {
546 self.a = r as i16;
547 self.flag_a = flag;
548 } else {
549 self.b = r as i16;
550 self.flag_b = flag;
551 }
552 }
553
554 self.exec_ld(u32::from(idb) << 6 | dst);
556
557 if dst != 4 {
558 match dpl {
560 1 => self.dp = (self.dp & 0xF0) + (self.dp.wrapping_add(1) & 0x0F), 2 => self.dp = (self.dp & 0xF0) + (self.dp.wrapping_sub(1) & 0x0F), 3 => self.dp &= 0xF0, _ => {}
564 }
565 self.dp ^= dphm << 4;
566 self.dp &= self.revision.dp_mask();
567 }
568
569 if dst != 5 {
570 if rpdcr != 0 {
572 self.rp = self.rp.wrapping_sub(1) & self.revision.rp_mask();
573 }
574 }
575 }
576
577 fn exec_rt(&mut self, opcode: u32) {
578 self.exec_op(opcode);
579 self.sp = self.sp.wrapping_sub(1) & 0xF;
580 self.pc = self.stack[usize::from(self.sp)] & self.revision.pc_mask();
581 }
582
583 fn exec_jp(&mut self, opcode: u32) {
584 let brch = (opcode >> 13) & 0x1FF;
585 let na = ((opcode >> 2) & 0x7FF) as u16;
586 let bank = (opcode & 0x3) as u16;
587 let jp = (self.pc & 0x2000) | bank << 11 | na;
588
589 let taken = match brch {
590 0x000 => {
591 self.set_pc(self.so);
592 return;
593 }
594 0x080 => !self.flag_a.c,
595 0x082 => self.flag_a.c,
596 0x084 => !self.flag_b.c,
597 0x086 => self.flag_b.c,
598 0x088 => !self.flag_a.z,
599 0x08a => self.flag_a.z,
600 0x08c => !self.flag_b.z,
601 0x08e => self.flag_b.z,
602 0x090 => !self.flag_a.ov0,
603 0x092 => self.flag_a.ov0,
604 0x094 => !self.flag_b.ov0,
605 0x096 => self.flag_b.ov0,
606 0x098 => !self.flag_a.ov1,
607 0x09a => self.flag_a.ov1,
608 0x09c => !self.flag_b.ov1,
609 0x09e => self.flag_b.ov1,
610 0x0a0 => !self.flag_a.s0,
611 0x0a2 => self.flag_a.s0,
612 0x0a4 => !self.flag_b.s0,
613 0x0a6 => self.flag_b.s0,
614 0x0a8 => !self.flag_a.s1,
615 0x0aa => self.flag_a.s1,
616 0x0ac => !self.flag_b.s1,
617 0x0ae => self.flag_b.s1,
618 0x0b0 => self.dp & 0x0F == 0x00,
619 0x0b1 => self.dp & 0x0F != 0x00,
620 0x0b2 => self.dp & 0x0F == 0x0F,
621 0x0b3 => self.dp & 0x0F != 0x0F,
622 0x0b4 => !self.sr.siack,
623 0x0b6 => self.sr.siack,
624 0x0b8 => !self.sr.soack,
625 0x0ba => self.sr.soack,
626 0x0bc => !self.sr.rqm,
627 0x0be => self.sr.rqm,
628 0x100 => {
629 self.set_pc(jp & !0x2000); return;
631 }
632 0x101 => {
633 self.set_pc(jp | 0x2000); return;
635 }
636 0x140 => {
637 self.stack[usize::from(self.sp)] = self.pc;
638 self.sp = self.sp.wrapping_add(1) & 0xF;
639 self.set_pc(jp & !0x2000); return;
641 }
642 0x141 => {
643 self.stack[usize::from(self.sp)] = self.pc;
644 self.sp = self.sp.wrapping_add(1) & 0xF;
645 self.set_pc(jp | 0x2000); return;
647 }
648 _ => return,
649 };
650 if taken {
651 self.set_pc(jp);
652 }
653 }
654
655 fn exec_ld(&mut self, opcode: u32) {
656 let id = (opcode >> 6) as u16;
657 let dst = opcode & 0xF;
658 match dst {
659 0 => {}
660 1 => self.a = id as i16,
661 2 => self.b = id as i16,
662 3 => self.tr = id,
663 4 => self.dp = id & self.revision.dp_mask(),
664 5 => self.rp = id & self.revision.rp_mask(),
665 6 => {
666 self.dr = id;
667 self.sr.rqm = true;
668 }
669 7 => {
670 let v = (self.sr.to_bits() & 0x907C) | (id & !0x907C);
671 self.sr.set_bits(v);
672 }
673 8 | 9 => self.so = id,
674 10 => self.k = id as i16,
675 11 => {
676 self.k = id as i16;
677 self.l = self.data_rom[usize::from(self.rp)] as i16;
678 }
679 12 => {
680 self.l = id as i16;
681 self.k =
682 self.data_ram[usize::from((self.dp | 0x40) & self.revision.dp_mask())] as i16;
683 }
684 13 => self.l = id as i16,
685 14 => self.trb = id,
686 _ => self.data_ram[usize::from(self.dp & self.revision.dp_mask())] = id,
687 }
688 }
689
690 #[must_use]
694 pub const fn host_accesses(&self) -> u64 {
695 self.host_accesses
696 }
697
698 #[must_use]
700 pub const fn pc(&self) -> u16 {
701 self.pc
702 }
703
704 #[must_use]
706 pub const fn rqm(&self) -> bool {
707 self.sr.rqm
708 }
709
710 #[must_use]
712 pub fn data_ram_word(&self, index: usize) -> u16 {
713 self.data_ram[index & 2047]
714 }
715
716 #[must_use]
719 pub fn flags_packed(&self) -> u16 {
720 self.flag_a.to_bits() | self.flag_b.to_bits() << 6
721 }
722
723 pub fn save_state(&self, w: &mut SaveWriter) {
731 w.section(*b"NDSP", |s| {
732 for &word in &self.stack {
733 s.write_u16(word);
734 }
735 s.write_u16(self.pc);
736 s.write_u16(self.rp);
737 s.write_u16(self.dp);
738 s.write_u8(self.sp);
739 s.write_u16(self.si);
740 s.write_u16(self.so);
741 s.write_u16(self.k.cast_unsigned());
742 s.write_u16(self.l.cast_unsigned());
743 s.write_u16(self.m.cast_unsigned());
744 s.write_u16(self.n.cast_unsigned());
745 s.write_u16(self.a.cast_unsigned());
746 s.write_u16(self.b.cast_unsigned());
747 s.write_u16(self.tr);
748 s.write_u16(self.trb);
749 s.write_u16(self.dr);
750 write_status(s, self.sr);
751 write_flag(s, self.flag_a);
752 write_flag(s, self.flag_b);
753 for &word in &self.data_ram {
754 s.write_u16(word);
755 }
756 });
757 }
758
759 pub fn load_state(&mut self, r: &mut SaveReader) -> Result<(), SaveStateError> {
765 let mut s = r.expect_section(*b"NDSP")?;
766 for slot in &mut self.stack {
767 *slot = s.read_u16()?;
768 }
769 self.pc = s.read_u16()? & self.revision.pc_mask();
780 self.rp = s.read_u16()? & self.revision.rp_mask();
781 self.dp = s.read_u16()? & self.revision.dp_mask();
782 self.sp = s.read_u8()? & 0xF;
783 self.si = s.read_u16()?;
784 self.so = s.read_u16()?;
785 self.k = s.read_u16()?.cast_signed();
786 self.l = s.read_u16()?.cast_signed();
787 self.m = s.read_u16()?.cast_signed();
788 self.n = s.read_u16()?.cast_signed();
789 self.a = s.read_u16()?.cast_signed();
790 self.b = s.read_u16()?.cast_signed();
791 self.tr = s.read_u16()?;
792 self.trb = s.read_u16()?;
793 self.dr = s.read_u16()?;
794 self.sr = read_status(&mut s)?;
795 self.flag_a = read_flag(&mut s)?;
796 self.flag_b = read_flag(&mut s)?;
797 for slot in &mut self.data_ram {
798 *slot = s.read_u16()?;
799 }
800 if s.remaining() != 0 {
801 return Err(SaveStateError::Invalid(alloc::format!(
802 "NDSP section has {} trailing byte(s)",
803 s.remaining()
804 )));
805 }
806 Ok(())
807 }
808}
809
810fn write_flag(s: &mut SaveWriter, f: Flag) {
811 s.write_bool(f.ov0);
812 s.write_bool(f.ov1);
813 s.write_bool(f.z);
814 s.write_bool(f.c);
815 s.write_bool(f.s0);
816 s.write_bool(f.s1);
817}
818
819fn read_flag(s: &mut SaveReader) -> Result<Flag, SaveStateError> {
820 Ok(Flag {
821 ov0: s.read_bool()?,
822 ov1: s.read_bool()?,
823 z: s.read_bool()?,
824 c: s.read_bool()?,
825 s0: s.read_bool()?,
826 s1: s.read_bool()?,
827 })
828}
829
830fn write_status(s: &mut SaveWriter, st: Status) {
831 s.write_bool(st.p0);
832 s.write_bool(st.p1);
833 s.write_bool(st.ei);
834 s.write_bool(st.sic);
835 s.write_bool(st.soc);
836 s.write_bool(st.drc);
837 s.write_bool(st.dma);
838 s.write_bool(st.drs);
839 s.write_bool(st.usf0);
840 s.write_bool(st.usf1);
841 s.write_bool(st.rqm);
842 s.write_bool(st.siack);
843 s.write_bool(st.soack);
844}
845
846fn read_status(s: &mut SaveReader) -> Result<Status, SaveStateError> {
847 Ok(Status {
848 p0: s.read_bool()?,
849 p1: s.read_bool()?,
850 ei: s.read_bool()?,
851 sic: s.read_bool()?,
852 soc: s.read_bool()?,
853 drc: s.read_bool()?,
854 dma: s.read_bool()?,
855 drs: s.read_bool()?,
856 usf0: s.read_bool()?,
857 usf1: s.read_bool()?,
858 rqm: s.read_bool()?,
859 siack: s.read_bool()?,
860 soack: s.read_bool()?,
861 })
862}
863
864#[cfg(test)]
865mod tests {
866 use super::*;
867 use alloc::vec;
868 use alloc::vec::Vec;
869
870 fn synth_firmware(program: &[u32]) -> Vec<u8> {
872 let mut prog = vec![0u32; 2048];
874 prog[..program.len()].copy_from_slice(program);
875 let mut bytes = Vec::with_capacity(8192);
876 for w in prog {
877 bytes.push((w & 0xFF) as u8);
878 bytes.push((w >> 8 & 0xFF) as u8);
879 bytes.push((w >> 16 & 0xFF) as u8);
880 }
881 bytes.resize(8192, 0); bytes
883 }
884
885 const fn ld(id: u16, dst: u32) -> u32 {
889 0b11 << 22 | (id as u32) << 6 | dst
890 }
891 const fn op(pselect: u32, alu: u32, src: u32, dst: u32, dpl: u32) -> u32 {
893 pselect << 20 | alu << 16 | dpl << 13 | src << 4 | dst
894 }
895 const fn ljmp(target: u32) -> u32 {
897 0b10 << 22 | 0x100 << 13 | (target & 0x7FF) << 2
898 }
899
900 #[test]
901 fn alu_add_sub_multiply_and_dataram_moves() {
902 let program = [
906 ld(5, 1), ld(3, 3), op(1, 5, 3, 0, 0), op(0, 0, 1, 15, 1), ld(2, 1), op(1, 4, 3, 0, 0), op(0, 0, 1, 15, 1), ld(7, 10), ld(6, 13), ld(0, 1), op(3, 5, 0, 0, 0), op(0, 0, 1, 15, 1), ld(0xBEEF, 6), ];
920 let mut eng = Upd77c25::new(Revision::Upd7725);
921 let fw = synth_firmware(&program);
923 let prog_bytes = eng.program_rom.len() * 3;
924 for (i, word) in eng.program_rom.iter_mut().enumerate() {
925 let o = i * 3;
926 *word = u32::from(fw[o]) | u32::from(fw[o + 1]) << 8 | u32::from(fw[o + 2]) << 16;
927 }
928 eng.firmware_loaded = true;
929 let _ = prog_bytes;
930
931 for _ in 0..13 {
932 eng.exec();
933 }
934
935 assert_eq!(eng.data_ram_word(0), 8, "5+3");
936 assert_eq!(eng.data_ram_word(1), 0xFFFF, "2-3 borrow");
937 assert_eq!(eng.data_ram_word(2), 84, "(7*6)<<1 via the K*L multiplier");
938
939 assert!(eng.rqm(), "DR load raised RQM");
941 assert_eq!(eng.dr & 0xFF, 0xEF);
942 assert_eq!(eng.dr >> 8, 0xBE);
943 }
944
945 #[test]
946 fn dr_handshake_and_run_until_rqm() {
947 let program = [ld(0x1234, 6), ljmp(0)];
950 let mut eng = Upd77c25::new(Revision::Upd7725);
951 assert!(eng.load_firmware(&synth_firmware(&program)));
952 assert!(eng.firmware_loaded());
953 assert!(eng.rqm());
955 assert_eq!(eng.read_sr() & 0x80, 0x80, "SR high bit = RQM");
956
957 assert_eq!(eng.read_dr(), 0x34); assert_eq!(eng.read_dr(), 0x12); assert!(eng.rqm(), "loop re-loaded DR and re-raised RQM");
960 }
961
962 #[test]
963 fn inert_until_firmware() {
964 let mut eng = Upd77c25::new(Revision::Upd7725);
965 assert!(!eng.firmware_loaded());
966 assert_eq!(eng.read_sr(), 0);
967 assert_eq!(eng.read_dr(), 0);
968 eng.run_until_rqm(); assert!(!eng.load_firmware(&[0u8; 100])); }
971
972 #[test]
973 fn out_of_range_pointer_registers_are_masked_not_panicked_on() {
974 let mut w = SaveWriter::new();
978 w.section(*b"NDSP", |s| {
979 for _ in 0..16 {
980 s.write_u16(0);
981 } s.write_u16(0xFFFF); s.write_u16(0xFFFF); s.write_u16(0xFFFF); s.write_u8(0xFF); for _ in 0..10 {
987 s.write_u16(0);
988 } s.write_u16(0); for _ in 0..13 {
991 s.write_bool(false);
992 } for _ in 0..12 {
994 s.write_bool(false);
995 } for _ in 0..2048 {
997 s.write_u16(0);
998 } });
1000 let bytes = w.into_bytes();
1001
1002 let mut eng = Upd77c25::new(Revision::Upd7725);
1003 let mut r = SaveReader::new(&bytes);
1004 eng.load_state(&mut r).unwrap();
1005
1006 assert!(eng.pc <= Revision::Upd7725.pc_mask());
1007 assert!(eng.rp <= Revision::Upd7725.rp_mask());
1008 assert!(eng.dp <= Revision::Upd7725.dp_mask());
1009 assert!(eng.sp <= 0xF);
1010 let _ = eng.program_rom[usize::from(eng.pc)];
1012 let _ = eng.data_rom[usize::from(eng.rp)];
1013 let _ = eng.stack[usize::from(eng.sp)];
1014 }
1015}