1#![allow(clippy::similar_names)]
17
18use rustysnes_savestate::{SaveReader, SaveStateError, SaveWriter};
19
20use crate::coproc::armv3::bus::ArmBus;
21use crate::coproc::armv3::primitives::{self, Flags};
22use crate::coproc::armv3::regs::{Cpsr, Mode, Pipeline, Regs, mode};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum Vector {
27 Undefined,
29 SoftwareIrq,
31 Irq,
33}
34
35impl Vector {
36 const fn address(self) -> u32 {
37 match self {
38 Self::Undefined => 0x04,
39 Self::SoftwareIrq => 0x08,
40 Self::Irq => 0x18,
41 }
42 }
43
44 const fn mode(self) -> Mode {
46 match self {
47 Self::Undefined => mode::UNDEFINED,
48 Self::SoftwareIrq => mode::SUPERVISOR,
49 Self::Irq => mode::IRQ,
50 }
51 }
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58enum AluOp {
59 And,
60 Eor,
61 Sub,
62 Rsb,
63 Add,
64 Adc,
65 Sbc,
66 Rsc,
67 Tst,
68 Teq,
69 Cmp,
70 Cmn,
71 Orr,
72 Mov,
73 Bic,
74 Mvn,
75}
76
77impl AluOp {
78 const fn from_bits(bits: u32) -> Self {
79 match bits & 0xF {
80 0x0 => Self::And,
81 0x1 => Self::Eor,
82 0x2 => Self::Sub,
83 0x3 => Self::Rsb,
84 0x4 => Self::Add,
85 0x5 => Self::Adc,
86 0x6 => Self::Sbc,
87 0x7 => Self::Rsc,
88 0x8 => Self::Tst,
89 0x9 => Self::Teq,
90 0xA => Self::Cmp,
91 0xB => Self::Cmn,
92 0xC => Self::Orr,
93 0xD => Self::Mov,
94 0xE => Self::Bic,
95 _ => Self::Mvn,
96 }
97 }
98
99 const fn is_comparison(self) -> bool {
101 matches!(self, Self::Tst | Self::Teq | Self::Cmp | Self::Cmn)
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
113enum Category {
114 DataProcessing,
115 Msr,
116 Mrs,
117 Branch,
118 SingleDataTransfer,
119 BlockDataTransfer,
120 Multiply,
121 MultiplyLong,
122 SingleDataSwap { byte: bool },
123 SoftwareInterrupt,
124 InvalidOp,
125}
126
127impl Category {
128 fn decode(index: u16) -> Self {
129 if (0xF00..=0xFFF).contains(&index) {
131 return Self::SoftwareInterrupt;
132 }
133 if index & !0xB0 == 0x109 {
136 return Self::SingleDataSwap { byte: false };
137 }
138 if index & !0xB0 == 0x149 {
139 return Self::SingleDataSwap { byte: true };
140 }
141 if index & !0x70 == 0x089 {
143 return Self::MultiplyLong;
144 }
145 if index & !0x70 == 0x009 {
147 return Self::Multiply;
148 }
149 if (0x800..=0x9FF).contains(&index) {
150 return Self::BlockDataTransfer;
151 }
152 if (0x400..=0x7FF).contains(&index) {
153 return Self::SingleDataTransfer;
154 }
155 if (0xA00..=0xBFF).contains(&index) {
156 return Self::Branch;
157 }
158 if index <= 0x3FF {
159 let operation = AluOp::from_bits(u32::from(index) >> 5);
160 let set_condition_codes = index & 0x10 != 0;
161 if !set_condition_codes && operation.is_comparison() {
162 return if index & 0x20 != 0 {
163 Self::Msr
164 } else {
165 Self::Mrs
166 };
167 }
168 return Self::DataProcessing;
169 }
170 Self::InvalidOp
171 }
172}
173
174#[derive(Debug, Clone, Copy, Default)]
176pub struct Cpu {
177 pub regs: Regs,
179 pub pipeline: Pipeline,
181}
182
183impl Cpu {
184 pub fn power_on(&mut self, bus: &mut impl ArmBus) {
189 self.regs = Regs::default();
190 self.pipeline = Pipeline::default();
191 self.regs.cpsr.mode = mode::SUPERVISOR;
192 self.regs.cpsr.irq_disable = true;
193 self.regs.cpsr.fiq_disable = true;
194 self.pipeline.request_reload();
195 self.advance_pipeline(bus);
196 }
197
198 fn advance_pipeline(&mut self, bus: &mut impl ArmBus) {
199 self.pipeline
200 .process(&mut self.regs.r[15], |addr| bus.read_code(addr));
201 }
202
203 pub fn step(&mut self, bus: &mut impl ArmBus) {
206 let opcode = self.pipeline.execute.opcode;
207 let cond = (opcode >> 28) as u8;
208 if primitives::check_condition(cond, self.regs.cpsr.flags) {
209 self.execute(opcode, bus);
210 }
211 self.advance_pipeline(bus);
212 }
213
214 #[allow(clippy::missing_const_for_fn)]
222 fn set_r(&mut self, reg: u8, value: u32) {
223 self.regs.r[reg as usize] = value;
224 if reg == 15 {
225 self.pipeline.request_reload();
226 }
227 }
228
229 fn execute(&mut self, opcode: u32, bus: &mut impl ArmBus) {
230 #[allow(clippy::cast_possible_truncation)]
233 let index = (((opcode & 0x0FF0_0000) >> 16) | ((opcode & 0xF0) >> 4)) as u16;
234 match Category::decode(index) {
235 Category::DataProcessing => self.exec_data_processing(opcode, bus),
236 Category::Msr => self.exec_msr(opcode),
237 Category::Mrs => self.exec_mrs(opcode),
238 Category::Branch => self.exec_branch(opcode),
239 Category::SoftwareInterrupt => self.enter_exception(Vector::SoftwareIrq),
240 Category::InvalidOp => self.enter_exception(Vector::Undefined),
241 Category::SingleDataTransfer => self.exec_single_data_transfer(opcode, bus),
242 Category::BlockDataTransfer => self.exec_block_data_transfer(opcode, bus),
243 Category::Multiply => self.exec_multiply(opcode, bus),
244 Category::MultiplyLong => self.exec_multiply_long(opcode, bus),
245 Category::SingleDataSwap { byte } => self.exec_single_data_swap(opcode, byte, bus),
246 }
247 }
248
249 fn maybe_restore_cpsr_from_spsr(&mut self, dst_reg: u8, update_flags: bool) {
255 if dst_reg == 15 && update_flags {
256 let spsr = self.regs.spsr();
257 self.regs.switch_mode(spsr.mode);
258 self.regs.cpsr = spsr;
259 }
260 }
261
262 #[allow(clippy::too_many_lines)]
268 fn exec_data_processing(&mut self, opcode: u32, bus: &mut impl ArmBus) {
269 let immediate = opcode & (1 << 25) != 0;
270 let rn = ((opcode >> 16) & 0xF) as u8;
271 let dst_reg = ((opcode >> 12) & 0xF) as u8;
272 let operation = AluOp::from_bits(opcode >> 21);
277 let update_flags = (opcode & (1 << 20) != 0) || operation.is_comparison();
278
279 let mut op1 = self.regs.r[rn as usize];
280 let mut carry = self.regs.cpsr.flags.c;
281 let op2 = if immediate {
282 let rotate = (opcode >> 8) & 0xF;
283 let imm = opcode & 0xFF;
284 if rotate == 0 {
285 imm
286 } else {
287 let (v, c) = primitives::rotate_right_carry(imm, rotate * 2, carry);
288 carry = c;
289 v
290 }
291 } else {
292 let shift_type = (opcode >> 5) & 0x3;
293 let rm = (opcode & 0xF) as u8;
294 let mut v = self.regs.r[rm as usize];
295 let use_reg_shift = opcode & (1 << 4) != 0;
296 let shift = if use_reg_shift {
297 bus.idle();
305 let rs = ((opcode >> 8) & 0xF) as u8;
306 #[allow(clippy::cast_possible_truncation)]
307 let s = (self.regs.r[rs as usize] as u8).wrapping_add(if rs == 15 { 4 } else { 0 });
308 if rm == 15 {
309 v = v.wrapping_add(4);
310 }
311 if rn == 15 {
312 op1 = op1.wrapping_add(4);
313 }
314 s
315 } else {
316 #[allow(clippy::cast_possible_truncation)]
317 let s = ((opcode >> 7) & 0x1F) as u8;
318 s
319 };
320 let (result, c) = match shift_type {
321 0 => primitives::shift_lsl(v, shift, carry),
322 1 => primitives::shift_lsr(
323 v,
324 if use_reg_shift || shift != 0 {
325 shift
326 } else {
327 32
328 },
329 carry,
330 ),
331 2 => primitives::shift_asr(
332 v,
333 if use_reg_shift || shift != 0 {
334 shift
335 } else {
336 32
337 },
338 carry,
339 ),
340 _ => {
341 if !use_reg_shift && shift == 0 {
342 primitives::shift_rrx(v, carry)
343 } else {
344 primitives::shift_ror(v, shift, carry)
345 }
346 }
347 };
348 carry = c;
349 result
350 };
351
352 let prior = self.regs.cpsr.flags;
353 let (result, flags): (Option<u32>, Flags) = match operation {
354 AluOp::And => {
355 let r = op1 & op2;
356 (Some(r), primitives::logical_flags(r, carry, prior))
357 }
358 AluOp::Eor => {
359 let r = op1 ^ op2;
360 (Some(r), primitives::logical_flags(r, carry, prior))
361 }
362 AluOp::Sub => {
363 let (r, f) = primitives::sub(op1, op2, true, prior);
364 (Some(r), f)
365 }
366 AluOp::Rsb => {
367 let (r, f) = primitives::sub(op2, op1, true, prior);
368 (Some(r), f)
369 }
370 AluOp::Add => {
371 let (r, f) = primitives::add(op1, op2, false, prior);
372 (Some(r), f)
373 }
374 AluOp::Adc => {
375 let (r, f) = primitives::add(op1, op2, prior.c, prior);
376 (Some(r), f)
377 }
378 AluOp::Sbc => {
379 let (r, f) = primitives::sub(op1, op2, prior.c, prior);
380 (Some(r), f)
381 }
382 AluOp::Rsc => {
383 let (r, f) = primitives::sub(op2, op1, prior.c, prior);
384 (Some(r), f)
385 }
386 AluOp::Tst => {
387 let r = op1 & op2;
388 (None, primitives::logical_flags(r, carry, prior))
389 }
390 AluOp::Teq => {
391 let r = op1 ^ op2;
392 (None, primitives::logical_flags(r, carry, prior))
393 }
394 AluOp::Cmp => {
395 let (_, f) = primitives::sub(op1, op2, true, prior);
396 (None, f)
397 }
398 AluOp::Cmn => {
399 let (_, f) = primitives::add(op1, op2, false, prior);
400 (None, f)
401 }
402 AluOp::Orr => {
403 let r = op1 | op2;
404 (Some(r), primitives::logical_flags(r, carry, prior))
405 }
406 AluOp::Mov => (Some(op2), primitives::logical_flags(op2, carry, prior)),
407 AluOp::Bic => {
408 let r = op1 & !op2;
409 (Some(r), primitives::logical_flags(r, carry, prior))
410 }
411 AluOp::Mvn => {
412 let r = !op2;
413 (Some(r), primitives::logical_flags(r, carry, prior))
414 }
415 };
416
417 if update_flags {
418 self.regs.cpsr.flags = flags;
419 }
420 if let Some(r) = result {
421 self.set_r(dst_reg, r);
422 }
423 self.maybe_restore_cpsr_from_spsr(dst_reg, update_flags);
424 }
425
426 #[allow(clippy::missing_const_for_fn)]
433 fn exec_branch(&mut self, opcode: u32) {
434 let with_link = opcode & (1 << 24) != 0;
435 #[allow(clippy::cast_possible_wrap)]
436 let offset = ((opcode as i32) << 8) >> 6; if with_link {
438 self.regs.r[14] = self.regs.r[15].wrapping_sub(4);
439 }
440 self.regs.r[15] = self.regs.r[15].wrapping_add(offset.cast_unsigned());
441 self.pipeline.request_reload();
442 }
443
444 fn exec_msr(&mut self, opcode: u32) {
448 let immediate = opcode & (1 << 25) != 0;
449 let write_to_spsr = opcode & (1 << 22) != 0;
450 let mask = (opcode >> 16) & 0xF;
451
452 if write_to_spsr && matches!(self.regs.cpsr.mode, mode::USER | mode::SYSTEM) {
453 return; }
455
456 let value = if immediate {
457 let imm = opcode & 0xFF;
458 let shift = (opcode >> 8) & 0xF;
459 if shift == 0 {
460 imm
461 } else {
462 primitives::rotate_right(imm, shift * 2)
463 }
464 } else {
465 self.regs.r[(opcode & 0xF) as usize]
466 };
467
468 let user_mode = self.regs.cpsr.mode == mode::USER;
469 let target: &mut Cpsr = if write_to_spsr {
470 self.regs.spsr_mut()
471 } else {
472 &mut self.regs.cpsr
473 };
474
475 if mask & 0x8 != 0 {
476 target.flags = Flags {
477 n: value & (1 << 31) != 0,
478 z: value & (1 << 30) != 0,
479 c: value & (1 << 29) != 0,
480 v: value & (1 << 28) != 0,
481 };
482 }
483 if mask & 0x1 != 0 && (write_to_spsr || !user_mode) {
484 let new_mode = (value & 0x1F) as u8;
485 let fiq_disable = value & (1 << 6) != 0;
486 let irq_disable = value & (1 << 7) != 0;
487 if write_to_spsr {
488 target.mode = new_mode | mode::BIT;
489 target.fiq_disable = fiq_disable;
490 target.irq_disable = irq_disable;
491 } else {
492 self.regs.switch_mode(new_mode);
495 self.regs.cpsr.fiq_disable = fiq_disable;
496 self.regs.cpsr.irq_disable = irq_disable;
497 }
498 }
499 }
500
501 #[allow(clippy::missing_const_for_fn)]
505 fn exec_mrs(&mut self, opcode: u32) {
506 let use_spsr = opcode & (1 << 22) != 0;
507 let rd = ((opcode >> 12) & 0xF) as u8;
508 let value = if use_spsr {
509 self.regs.spsr().to_u32()
510 } else {
511 self.regs.cpsr.to_u32()
512 };
513 self.set_r(rd, value);
514 }
515
516 fn enter_exception(&mut self, vector: Vector) {
521 let cpsr = self.regs.cpsr;
522 self.regs.switch_mode(vector.mode());
523 *self.regs.spsr_mut() = cpsr;
524 self.regs.cpsr.irq_disable = true;
525 self.regs.r[14] = self.pipeline.decode.address;
526 self.regs.r[15] = vector.address();
527 self.pipeline.request_reload();
528 }
529
530 fn exec_single_data_transfer(&mut self, opcode: u32, bus: &mut impl ArmBus) {
534 let immediate = opcode & (1 << 25) == 0;
535 let pre = opcode & (1 << 24) != 0;
536 let up = opcode & (1 << 23) != 0;
537 let byte = opcode & (1 << 22) != 0;
538 let write_back = opcode & (1 << 21) != 0;
539 let load = opcode & (1 << 20) != 0;
540 let rn = ((opcode >> 16) & 0xF) as u8;
541 let rd = ((opcode >> 12) & 0xF) as u8;
542
543 let mut addr = self.regs.r[rn as usize];
544 let offset = if immediate {
545 opcode & 0xFFF
546 } else {
547 let shift_type = (opcode >> 5) & 0x3;
548 #[allow(clippy::cast_possible_truncation)]
549 let shift = ((opcode >> 7) & 0x1F) as u8;
550 let rm = (opcode & 0xF) as u8;
551 let v = self.regs.r[rm as usize];
552 let carry = self.regs.cpsr.flags.c;
553 match shift_type {
554 0 => primitives::shift_lsl(v, shift, carry).0,
555 1 => primitives::shift_lsr(v, if shift == 0 { 32 } else { shift }, carry).0,
556 2 => primitives::shift_asr(v, if shift == 0 { 32 } else { shift }, carry).0,
557 _ => {
558 if shift == 0 {
559 primitives::shift_rrx(v, carry).0
560 } else {
561 primitives::shift_ror(v, shift, carry).0
562 }
563 }
564 }
565 };
566
567 if pre {
568 addr = if up {
569 addr.wrapping_add(offset)
570 } else {
571 addr.wrapping_sub(offset)
572 };
573 }
574
575 if load {
576 let value = bus.read(addr, byte);
577 self.set_r(rd, value);
578 bus.idle();
579 } else {
580 let value = self.regs.r[rd as usize].wrapping_add(if rd == 15 { 4 } else { 0 });
584 bus.write(addr, value, byte);
585 }
586
587 if !pre {
588 addr = if up {
589 addr.wrapping_add(offset)
590 } else {
591 addr.wrapping_sub(offset)
592 };
593 }
594
595 if (rd != rn || !load) && (write_back || !pre) {
598 self.set_r(rn, addr);
599 }
600 }
601
602 #[allow(clippy::too_many_lines)]
608 fn exec_block_data_transfer(&mut self, opcode: u32, bus: &mut impl ArmBus) {
609 let pre = opcode & (1 << 24) != 0;
610 let up = opcode & (1 << 23) != 0;
611 let psr_force_user = opcode & (1 << 22) != 0;
612 let write_back = opcode & (1 << 21) != 0;
613 let load = opcode & (1 << 20) != 0;
614 let rn = ((opcode >> 16) & 0xF) as u8;
615 #[allow(clippy::cast_possible_truncation)]
616 let mut reg_mask = opcode as u16;
617
618 let base = self.regs.r[rn as usize].wrapping_add(if rn == 15 { 4 } else { 0 });
619 let mut addr = base;
620
621 let mut reg_count = reg_mask.count_ones();
622 if reg_mask == 0 {
623 reg_count = 16;
626 reg_mask = 0x8000;
627 }
628
629 if !up {
630 addr = addr.wrapping_sub((reg_count - u32::from(!pre)) * 4);
631 } else if pre {
632 addr = addr.wrapping_add(4);
633 }
634
635 let write_back_addr = base.wrapping_add(if up {
636 reg_count * 4
637 } else {
638 (reg_count * 4).wrapping_neg()
639 });
640 if write_back && load {
641 self.set_r(rn, write_back_addr);
642 }
643
644 let org_mode = self.regs.cpsr.mode;
645 if psr_force_user && (!load || reg_mask & 0x8000 == 0) {
646 self.regs.switch_mode(mode::USER);
647 }
648
649 let mut first_reg = true;
650 for i in 0..16u8 {
651 if reg_mask & (1 << i) == 0 {
652 continue;
653 }
654 if !load {
655 let value = self.regs.r[i as usize].wrapping_add(if i == 15 { 4 } else { 0 });
656 bus.write(addr, value, false);
657 }
658 if first_reg && write_back {
659 self.set_r(rn, write_back_addr);
669 first_reg = false;
670 }
671 if load {
672 let value = bus.read(addr, false);
677 self.set_r(i, value);
678 }
679 addr = addr.wrapping_add(4);
680 }
681
682 if load {
683 bus.idle();
684 }
685
686 self.regs.switch_mode(org_mode);
687
688 if psr_force_user && load && reg_mask & 0x8000 != 0 {
689 let spsr = self.regs.spsr();
690 self.regs.switch_mode(spsr.mode);
691 self.regs.cpsr = spsr;
692 }
693 }
694
695 fn exec_multiply(&mut self, opcode: u32, bus: &mut impl ArmBus) {
704 let rd = ((opcode >> 16) & 0xF) as u8;
705 let rn = ((opcode >> 12) & 0xF) as u8;
706 let rs = ((opcode >> 8) & 0xF) as u8;
707 let rm = (opcode & 0xF) as u8;
708 let update_flags = opcode & (1 << 20) != 0;
709 let mult_and_acc = opcode & (1 << 21) != 0;
710
711 let rm_val = self.regs.r[rm as usize];
712 let rs_val = self.regs.r[rs as usize];
713 let mut result = rm_val.wrapping_mul(rs_val);
714 if mult_and_acc {
715 result = result.wrapping_add(self.regs.r[rn as usize]);
716 }
717
718 for _ in 0..multiply_cycles(rs_val) {
719 bus.idle();
720 }
721 if mult_and_acc {
722 bus.idle();
723 }
724
725 if rd != 15 {
726 self.set_r(rd, result);
727 }
728 if update_flags {
729 self.regs.cpsr.flags.z = result == 0;
734 self.regs.cpsr.flags.n = result & 0x8000_0000 != 0;
735 }
736 }
737
738 fn exec_multiply_long(&mut self, opcode: u32, bus: &mut impl ArmBus) {
742 let rh = ((opcode >> 16) & 0xF) as u8;
743 let rl = ((opcode >> 12) & 0xF) as u8;
744 let rs = ((opcode >> 8) & 0xF) as u8;
745 let rm = (opcode & 0xF) as u8;
746 let update_flags = opcode & (1 << 20) != 0;
747 let mult_and_acc = opcode & (1 << 21) != 0;
748 let signed = opcode & (1 << 22) != 0;
749
750 bus.idle();
751
752 let rm_val = self.regs.r[rm as usize];
753 let rs_val = self.regs.r[rs as usize];
754 let mut result: u64 = if signed {
755 (i64::from(rm_val.cast_signed()) * i64::from(rs_val.cast_signed())).cast_unsigned()
756 } else {
757 u64::from(rm_val) * u64::from(rs_val)
758 };
759 if mult_and_acc {
760 let acc =
761 (u64::from(self.regs.r[rh as usize]) << 32) | u64::from(self.regs.r[rl as usize]);
762 result = result.wrapping_add(acc);
763 }
764
765 for _ in 0..multiply_cycles(rs_val) {
766 bus.idle();
767 }
768 if mult_and_acc {
769 bus.idle();
770 }
771
772 #[allow(clippy::cast_possible_truncation)]
773 if rl != 15 {
774 self.set_r(rl, result as u32);
775 }
776 #[allow(clippy::cast_possible_truncation)]
777 if rh != 15 {
778 self.set_r(rh, (result >> 32) as u32);
779 }
780 if update_flags {
781 self.regs.cpsr.flags.z = result == 0;
783 self.regs.cpsr.flags.n = result & (1 << 63) != 0;
784 }
785 }
786
787 fn exec_single_data_swap(&mut self, opcode: u32, byte: bool, bus: &mut impl ArmBus) {
792 let rn = ((opcode >> 16) & 0xF) as u8;
793 let rd = ((opcode >> 12) & 0xF) as u8;
794 let rm = (opcode & 0xF) as u8;
795
796 let addr = self.regs.r[rn as usize];
797 let old = bus.read(addr, byte);
798 bus.idle();
799 let new_value = self.regs.r[rm as usize].wrapping_add(if rm == 15 { 4 } else { 0 });
800 bus.write(addr, new_value, byte);
801 self.set_r(rd, old);
802 }
803
804 pub(crate) fn save_state(&self, w: &mut SaveWriter) {
807 self.regs.save_state(w);
808 self.pipeline.save_state(w);
809 }
810
811 pub(crate) fn load_state(&mut self, r: &mut SaveReader) -> Result<(), SaveStateError> {
816 self.regs.load_state(r)?;
817 self.pipeline.load_state(r)
818 }
819}
820
821const fn multiply_cycles(rs: u32) -> u32 {
827 if rs & 0xFFFF_FF00 == 0 || rs & 0xFFFF_FF00 == 0xFFFF_FF00 {
828 1
829 } else if rs & 0xFFFF_0000 == 0 || rs & 0xFFFF_0000 == 0xFFFF_0000 {
830 2
831 } else if rs & 0xFF00_0000 == 0 || rs & 0xFF00_0000 == 0xFF00_0000 {
832 3
833 } else {
834 4
835 }
836}
837
838#[cfg(test)]
839#[allow(clippy::cast_possible_truncation)]
843mod tests {
844 use super::*;
845 use alloc::vec::Vec;
846
847 struct TestBus {
851 mem: Vec<u8>,
852 idle_count: u32,
853 }
854
855 impl TestBus {
856 fn new() -> Self {
857 Self {
858 mem: alloc::vec![0u8; 0x1_0000],
859 idle_count: 0,
860 }
861 }
862
863 fn word_at(&self, addr: u32) -> u32 {
864 let a = addr as usize;
865 u32::from(self.mem[a])
866 | (u32::from(self.mem[a + 1]) << 8)
867 | (u32::from(self.mem[a + 2]) << 16)
868 | (u32::from(self.mem[a + 3]) << 24)
869 }
870
871 fn set_word(&mut self, addr: u32, value: u32) {
872 let a = addr as usize;
873 self.mem[a] = value as u8;
874 self.mem[a + 1] = (value >> 8) as u8;
875 self.mem[a + 2] = (value >> 16) as u8;
876 self.mem[a + 3] = (value >> 24) as u8;
877 }
878 }
879
880 impl ArmBus for TestBus {
881 fn read_code(&mut self, addr: u32) -> u32 {
882 self.word_at(addr & 0xFFFF)
883 }
884 fn read(&mut self, addr: u32, byte: bool) -> u32 {
885 if byte {
886 u32::from(self.mem[(addr & 0xFFFF) as usize])
887 } else {
888 self.word_at(addr & 0xFFFF)
889 }
890 }
891 fn write(&mut self, addr: u32, value: u32, byte: bool) {
892 if byte {
893 self.mem[(addr & 0xFFFF) as usize] = value as u8;
894 } else {
895 self.set_word(addr & 0xFFFF, value);
896 }
897 }
898 fn idle(&mut self) {
899 self.idle_count += 1;
900 }
901 }
902
903 const fn add_r0_imm(imm: u32) -> u32 {
905 0xE280_0000 | (imm & 0xFF)
906 }
907
908 fn boot(program: &[u32]) -> (Cpu, TestBus) {
909 let mut bus = TestBus::new();
910 for (i, &w) in program.iter().enumerate() {
911 bus.set_word((i as u32) * 4, w);
912 }
913 let mut cpu = Cpu::default();
914 cpu.power_on(&mut bus);
915 (cpu, bus)
916 }
917
918 #[test]
919 fn power_on_enters_supervisor_with_both_interrupts_masked() {
920 let (cpu, _bus) = boot(&[]);
921 assert_eq!(cpu.regs.cpsr.mode, mode::SUPERVISOR);
922 assert!(cpu.regs.cpsr.irq_disable);
923 assert!(cpu.regs.cpsr.fiq_disable);
924 assert_eq!(cpu.pipeline.execute.address, 0);
925 }
926
927 #[test]
928 fn data_processing_add_immediate() {
929 let (mut cpu, mut bus) = boot(&[add_r0_imm(5), add_r0_imm(3)]);
931 cpu.step(&mut bus);
932 assert_eq!(cpu.regs.r[0], 5);
933 cpu.step(&mut bus);
934 assert_eq!(cpu.regs.r[0], 8);
935 }
936
937 #[test]
938 fn condition_code_gates_execution() {
939 let mov_r0_1 = 0xE3A0_0001u32; let addeq_r0_1 = 0x0280_0001u32; let add_r0_1 = add_r0_imm(1); let (mut cpu, mut bus) = boot(&[mov_r0_1, addeq_r0_1, add_r0_1]);
944 cpu.step(&mut bus);
945 assert_eq!(cpu.regs.r[0], 1);
946 cpu.step(&mut bus); assert_eq!(cpu.regs.r[0], 1, "ADDEQ must not execute when Z is clear");
948 cpu.step(&mut bus);
949 assert_eq!(cpu.regs.r[0], 2);
950 }
951
952 #[test]
953 fn subs_sets_flags_and_cmp_never_writes_a_destination() {
954 let movs_r0_0 = 0xE3B0_0000u32; let cmp_r0_0 = 0xE350_0000u32; let (mut cpu, mut bus) = boot(&[movs_r0_0, cmp_r0_0]);
958 cpu.step(&mut bus);
959 assert!(cpu.regs.cpsr.flags.z);
960 cpu.regs.r[0] = 0x1234;
963 cpu.step(&mut bus);
964 assert_eq!(
965 cpu.regs.r[0], 0x1234,
966 "CMP must never write a destination register"
967 );
968 assert!(
969 !cpu.regs.cpsr.flags.z,
970 "CMP r0,#0 with r0==0x1234 must clear Z"
971 );
972 }
973
974 #[test]
975 fn r15_reads_as_address_plus_8_inside_data_processing() {
976 let mov_r0_pc = 0xE1A0_000Fu32;
978 let (mut cpu, mut bus) = boot(&[mov_r0_pc]);
979 cpu.step(&mut bus);
980 assert_eq!(
981 cpu.regs.r[0], 8,
982 "PC read as an operand is address+8, not address"
983 );
984 }
985
986 #[test]
987 fn register_specified_shift_amount_also_adds_4_when_rs_is_r15() {
988 let add_r0_r1_lsr_r15 = 0xE080_0FB1u32;
995 let (mut cpu, mut bus) = boot(&[add_r0_r1_lsr_r15]);
996 cpu.regs.r[0] = 0;
997 cpu.regs.r[1] = 0x1000;
998 cpu.step(&mut bus);
1001 assert_eq!(
1002 cpu.regs.r[0], 1,
1003 "rs==15 must read as address+12 (the usual +8, plus the extra internal cycle), \
1004 exactly like rm/rn do -- the source applies +4 to all three independently"
1005 );
1006 }
1007
1008 #[test]
1009 fn branch_with_link_sets_lr_to_the_next_sequential_instruction() {
1010 let bl = 0xEB00_0002u32; let (mut cpu, mut bus) = boot(&[bl]);
1014 cpu.step(&mut bus);
1015 assert_eq!(
1016 cpu.regs.r[15],
1017 0x10 + 8,
1018 "branch target re-establishes the +8 pipeline offset"
1019 );
1020 assert_eq!(
1021 cpu.regs.r[14], 4,
1022 "LR = branch instruction's own address + 4"
1023 );
1024 }
1025
1026 #[test]
1027 fn msr_writes_only_the_flag_bits_when_masked_to_flags_only() {
1028 let msr_flags_only = 0xE128_F000u32; let (mut cpu, mut bus) = boot(&[msr_flags_only]);
1031 cpu.regs.r[0] = 0xF000_0000;
1032 let mode_before = cpu.regs.cpsr.mode;
1033 cpu.step(&mut bus);
1034 assert!(
1035 cpu.regs.cpsr.flags.n
1036 && cpu.regs.cpsr.flags.z
1037 && cpu.regs.cpsr.flags.c
1038 && cpu.regs.cpsr.flags.v
1039 );
1040 assert_eq!(
1041 cpu.regs.cpsr.mode, mode_before,
1042 "flags-only MSR must not touch mode"
1043 );
1044 }
1045
1046 #[test]
1047 fn mrs_reads_back_the_packed_cpsr() {
1048 let mrs_r0_cpsr = 0xE10F_0000u32;
1050 let (mut cpu, mut bus) = boot(&[mrs_r0_cpsr]);
1051 cpu.step(&mut bus);
1052 assert_eq!(cpu.regs.r[0], cpu.regs.cpsr.to_u32());
1053 }
1054
1055 #[test]
1056 fn software_interrupt_enters_supervisor_and_parks_the_return_address() {
1057 let swi = 0xEF00_0000u32;
1059 let (mut cpu, mut bus) = boot(&[swi]);
1060 let decode_addr_before = cpu.pipeline.decode.address;
1061 cpu.step(&mut bus);
1062 assert_eq!(
1063 cpu.pipeline.execute.address, 0x08,
1064 "jumped to the SoftwareIrq vector"
1065 );
1066 assert_eq!(
1067 cpu.regs.r[15],
1068 0x08 + 8,
1069 "pipeline re-establishes +8 at the new vector"
1070 );
1071 assert_eq!(cpu.regs.r[14], decode_addr_before);
1072 assert_eq!(cpu.regs.cpsr.mode, mode::SUPERVISOR);
1073 assert!(cpu.regs.cpsr.irq_disable);
1074 }
1075
1076 #[test]
1077 fn movs_pc_restores_cpsr_from_spsr_like_an_exception_return() {
1078 let swi = 0xEF00_0000u32;
1081 let movs_pc_lr = 0xE1B0_F00Eu32; let (mut cpu, mut bus) = boot(&[swi, 0, movs_pc_lr, 0]);
1083 cpu.regs.switch_mode(mode::USER);
1086 cpu.step(&mut bus); assert_eq!(cpu.regs.cpsr.mode, mode::SUPERVISOR);
1088 let lr = cpu.regs.r[14];
1089 cpu.step(&mut bus); assert_eq!(
1091 cpu.regs.cpsr.mode,
1092 mode::USER,
1093 "CPSR restored from SPSR_svc, back to the mode the SWI was made from"
1094 );
1095 assert_eq!(
1096 cpu.regs.r[15],
1097 lr + 8,
1098 "PC = LR, then the pipeline re-establishes +8"
1099 );
1100 }
1101
1102 #[test]
1103 fn undefined_opcode_traps_to_the_undefined_vector() {
1104 let undefined = 0xEC00_0000u32;
1112 let (mut cpu, mut bus) = boot(&[undefined]);
1113 cpu.step(&mut bus);
1114 assert_eq!(
1115 cpu.pipeline.execute.address, 0x04,
1116 "jumped to the Undefined vector"
1117 );
1118 assert_eq!(
1119 cpu.regs.r[15],
1120 0x04 + 8,
1121 "pipeline re-establishes +8 at the new vector"
1122 );
1123 assert_eq!(cpu.regs.cpsr.mode, mode::UNDEFINED);
1124 }
1125
1126 #[test]
1127 fn ldr_pre_indexed_with_no_writeback() {
1128 let ldr_r0_r1 = 0xE591_0000u32;
1130 let (mut cpu, mut bus) = boot(&[ldr_r0_r1]);
1131 cpu.regs.r[1] = 0x2000;
1132 bus.set_word(0x2000, 0xDEAD_BEEF);
1133 cpu.step(&mut bus);
1134 assert_eq!(cpu.regs.r[0], 0xDEAD_BEEF);
1135 assert_eq!(cpu.regs.r[1], 0x2000, "P=1,W=0 must not write back");
1136 }
1137
1138 #[test]
1139 fn ldr_post_indexed_always_writes_back_even_without_the_w_bit() {
1140 let ldr_r0_r1_post4 = 0xE491_0004u32;
1143 let (mut cpu, mut bus) = boot(&[ldr_r0_r1_post4]);
1144 cpu.regs.r[1] = 0x2000;
1145 bus.set_word(0x2000, 0x1234_5678);
1146 cpu.step(&mut bus);
1147 assert_eq!(
1148 cpu.regs.r[0], 0x1234_5678,
1149 "loaded from the ORIGINAL address"
1150 );
1151 assert_eq!(
1152 cpu.regs.r[1], 0x2004,
1153 "post-indexed writeback always happens"
1154 );
1155 }
1156
1157 #[test]
1158 fn str_r15_stores_address_plus_12_not_plus_8() {
1159 let str_r15_r1 = 0xE581_F000u32;
1162 let (mut cpu, mut bus) = boot(&[str_r15_r1]);
1163 cpu.regs.r[1] = 0x2000;
1164 cpu.step(&mut bus);
1165 assert_eq!(
1166 bus.word_at(0x2000),
1167 8 + 4,
1168 "stored value is address+12, not address+8"
1169 );
1170 }
1171
1172 #[test]
1173 fn ldm_ia_writeback_loads_registers_in_ascending_order() {
1174 let ldmia_r0_r1_r2 = 0xE8B0_0006u32;
1176 let (mut cpu, mut bus) = boot(&[ldmia_r0_r1_r2]);
1177 cpu.regs.r[0] = 0x3000;
1178 bus.set_word(0x3000, 0x1111_1111);
1179 bus.set_word(0x3004, 0x2222_2222);
1180 cpu.step(&mut bus);
1181 assert_eq!(cpu.regs.r[1], 0x1111_1111);
1182 assert_eq!(cpu.regs.r[2], 0x2222_2222);
1183 assert_eq!(
1184 cpu.regs.r[0], 0x3008,
1185 "writeback: base + register_count * 4"
1186 );
1187 }
1188
1189 #[test]
1190 fn stm_ia_writeback_stores_registers_in_ascending_order() {
1191 let stmia_r0_r1_r2 = 0xE8A0_0006u32;
1193 let (mut cpu, mut bus) = boot(&[stmia_r0_r1_r2]);
1194 cpu.regs.r[0] = 0x3000;
1195 cpu.regs.r[1] = 0xAAAA_AAAA;
1196 cpu.regs.r[2] = 0xBBBB_BBBB;
1197 cpu.step(&mut bus);
1198 assert_eq!(bus.word_at(0x3000), 0xAAAA_AAAA);
1199 assert_eq!(bus.word_at(0x3004), 0xBBBB_BBBB);
1200 assert_eq!(cpu.regs.r[0], 0x3008);
1201 }
1202
1203 #[test]
1204 fn ldm_with_an_empty_register_list_transfers_only_r15_but_advances_as_if_all_16_did() {
1205 let ldm_r0_empty = 0xE890_0000u32;
1208 let (mut cpu, mut bus) = boot(&[ldm_r0_empty]);
1209 cpu.regs.r[0] = 0x4000;
1210 bus.set_word(0x4000, 0x9000); cpu.step(&mut bus);
1212 assert_eq!(
1213 cpu.pipeline.execute.address, 0x9000,
1214 "R15 was loaded from address 0x4000 despite the empty encoded list"
1215 );
1216 }
1217
1218 #[test]
1219 fn ldm_with_s_bit_and_pc_in_the_list_restores_cpsr_from_spsr() {
1220 let ldm_r0_r1_pc_caret = 0xE8D0_8002u32;
1224 let (mut cpu, mut bus) = boot(&[ldm_r0_r1_pc_caret]);
1225 cpu.regs.switch_mode(mode::SUPERVISOR);
1226 cpu.regs.spsr_mut().mode = mode::USER; cpu.regs.r[0] = 0x5000;
1228 bus.set_word(0x5000, 0xCAFE_BABE); bus.set_word(0x5004, 0x2000); cpu.step(&mut bus);
1231 assert_eq!(cpu.regs.r[1], 0xCAFE_BABE);
1232 assert_eq!(
1233 cpu.regs.cpsr.mode,
1234 mode::USER,
1235 "CPSR restored from SPSR_svc"
1236 );
1237 assert_eq!(
1238 cpu.pipeline.execute.address, 0x2000,
1239 "R15 loaded from the list"
1240 );
1241 }
1242
1243 #[test]
1244 fn mul_computes_the_low_32_bits_and_sets_z_n() {
1245 let muls_r0_r1_r2 = 0xE010_0291u32;
1247 let (mut cpu, mut bus) = boot(&[muls_r0_r1_r2]);
1248 cpu.regs.r[1] = 6;
1249 cpu.regs.r[2] = 7;
1250 cpu.step(&mut bus);
1251 assert_eq!(cpu.regs.r[0], 42);
1252 assert!(!cpu.regs.cpsr.flags.z);
1253 assert!(!cpu.regs.cpsr.flags.n);
1254 }
1255
1256 #[test]
1257 fn mla_accumulates_into_the_multiply_result() {
1258 let mla_r0_r1_r2_r3 = 0xE020_3291u32;
1260 let (mut cpu, mut bus) = boot(&[mla_r0_r1_r2_r3]);
1261 cpu.regs.r[1] = 6;
1262 cpu.regs.r[2] = 7;
1263 cpu.regs.r[3] = 100;
1264 cpu.step(&mut bus);
1265 assert_eq!(cpu.regs.r[0], 142);
1266 }
1267
1268 #[test]
1269 fn umull_widens_to_64_bits_across_two_registers() {
1270 let umulls_r3_r2_r0_r1 = 0xE092_3190u32;
1272 let (mut cpu, mut bus) = boot(&[umulls_r3_r2_r0_r1]);
1273 cpu.regs.r[0] = 0xFFFF_FFFF;
1274 cpu.regs.r[1] = 2;
1275 cpu.step(&mut bus);
1276 let result = (u64::from(cpu.regs.r[2]) << 32) | u64::from(cpu.regs.r[3]);
1277 assert_eq!(result, u64::from(0xFFFF_FFFFu32) * 2);
1278 assert!(!cpu.regs.cpsr.flags.z);
1279 }
1280
1281 #[test]
1282 fn smull_sign_extends_negative_operands() {
1283 let smulls_r3_r2_r0_r1 = 0xE0D2_3190u32;
1285 let (mut cpu, mut bus) = boot(&[smulls_r3_r2_r0_r1]);
1286 cpu.regs.r[0] = (-5i32).cast_unsigned();
1287 cpu.regs.r[1] = 3;
1288 cpu.step(&mut bus);
1289 let result = ((u64::from(cpu.regs.r[2]) << 32) | u64::from(cpu.regs.r[3])).cast_signed();
1290 assert_eq!(result, -15);
1291 assert!(cpu.regs.cpsr.flags.n, "negative 64-bit result sets N");
1292 }
1293
1294 #[test]
1295 fn swp_reads_the_old_value_then_writes_the_new_one_atomically() {
1296 let swp_r0_r2_r1 = 0xE101_0092u32;
1298 let (mut cpu, mut bus) = boot(&[swp_r0_r2_r1]);
1299 cpu.regs.r[1] = 0x2000;
1300 cpu.regs.r[2] = 0xBEEF_CAFE;
1301 bus.set_word(0x2000, 0x1111_1111);
1302 cpu.step(&mut bus);
1303 assert_eq!(cpu.regs.r[0], 0x1111_1111, "old value read into rd");
1304 assert_eq!(
1305 bus.word_at(0x2000),
1306 0xBEEF_CAFE,
1307 "new value written to the same address"
1308 );
1309 }
1310
1311 #[test]
1312 fn swpb_swaps_a_single_byte() {
1313 let swpb_r0_r2_r1 = 0xE141_0092u32;
1315 let (mut cpu, mut bus) = boot(&[swpb_r0_r2_r1]);
1316 cpu.regs.r[1] = 0x2000;
1317 cpu.regs.r[2] = 0xAB;
1318 bus.set_word(0x2000, 0xFFFF_FF42); cpu.step(&mut bus);
1320 assert_eq!(cpu.regs.r[0], 0x42, "old byte read into rd");
1321 assert_eq!(
1322 bus.word_at(0x2000),
1323 0xFFFF_FFAB,
1324 "only the low byte was swapped"
1325 );
1326 }
1327
1328 #[test]
1329 fn multiply_cycles_matches_the_documented_early_termination_rule() {
1330 assert_eq!(multiply_cycles(0), 1);
1331 assert_eq!(multiply_cycles(0xFF), 1);
1332 assert_eq!(multiply_cycles(0xFFFF_FF00), 1);
1333 assert_eq!(multiply_cycles(0xFFFF), 2);
1334 assert_eq!(multiply_cycles(0x00FF_FFFF), 3);
1335 assert_eq!(multiply_cycles(0x7FFF_FFFF), 4);
1336 }
1337}