1use std::{sync::Arc, time::Duration};
4
5use probe_rs_target::{Architecture, CoreType, InstructionSet};
6use zerocopy::IntoBytes;
7
8use crate::{
9 CoreInformation, CoreInterface, CoreRegister, CoreStatus, Error, HaltReason, MemoryInterface,
10 architecture::xtensa::{
11 arch::{
12 CpuRegister, Register, SpecialRegister,
13 instruction::{Instruction, InstructionEncoding},
14 },
15 communication_interface::{
16 DebugCause, IBreakEn, ProgramStatus, WindowProperties, XtensaCommunicationInterface,
17 },
18 registers::{FP, PC, RA, SP, XTENSA_CORE_REGISTERS},
19 sequences::XtensaDebugSequence,
20 xdm::PowerStatus,
21 },
22 core::{
23 BreakpointCause,
24 registers::{CoreRegisters, RegisterId, RegisterValue},
25 },
26 semihosting::{SemihostingCommand, decode_semihosting_syscall},
27};
28
29pub(crate) mod arch;
30pub(crate) mod xdm;
31
32pub mod communication_interface;
33pub(crate) mod register_cache;
34pub mod registers;
35pub(crate) mod sequences;
36
37#[derive(Debug)]
39pub struct XtensaCoreState {
40 enabled: bool,
42
43 breakpoints_enabled: bool,
45
46 breakpoint_set: [bool; 2],
50
51 pc_written: bool,
54
55 semihosting_command: Option<SemihostingCommand>,
57
58 spilled: bool,
60}
61
62impl XtensaCoreState {
63 pub(crate) fn new() -> Self {
65 Self {
66 enabled: false,
67 breakpoints_enabled: false,
68 breakpoint_set: [false; 2],
69 pc_written: false,
70 semihosting_command: None,
71 spilled: false,
72 }
73 }
74
75 fn breakpoint_mask(&self) -> u32 {
77 self.breakpoint_set
78 .iter()
79 .enumerate()
80 .fold(0, |acc, (i, &set)| if set { acc | (1 << i) } else { acc })
81 }
82}
83
84pub struct Xtensa<'probe> {
86 interface: XtensaCommunicationInterface<'probe>,
87 state: &'probe mut XtensaCoreState,
88 sequence: Arc<dyn XtensaDebugSequence>,
89}
90
91impl<'probe> Xtensa<'probe> {
92 const IBREAKA_REGS: [SpecialRegister; 2] =
93 [SpecialRegister::IBreakA0, SpecialRegister::IBreakA1];
94
95 pub fn new(
97 interface: XtensaCommunicationInterface<'probe>,
98 state: &'probe mut XtensaCoreState,
99 sequence: Arc<dyn XtensaDebugSequence>,
100 ) -> Result<Self, Error> {
101 let mut this = Self {
102 interface,
103 state,
104 sequence,
105 };
106
107 this.on_attach()?;
108
109 Ok(this)
110 }
111
112 fn clear_cache(&mut self) {
113 self.state.spilled = false;
114 self.interface.clear_register_cache();
115 }
116
117 fn on_attach(&mut self) -> Result<(), Error> {
118 let core_reset;
120 if self.state.enabled {
121 let status = self.interface.xdm.power_status({
122 let mut clear_value = PowerStatus(0);
123 clear_value.set_core_was_reset(true);
124 clear_value.set_debug_was_reset(true);
125 clear_value
126 })?;
127 core_reset = status.core_was_reset() || !status.core_domain_on();
128 let debug_reset = status.debug_was_reset() || !status.debug_domain_on();
129
130 if core_reset {
131 tracing::debug!("Core was reset");
132 *self.state = XtensaCoreState::new();
133 }
134 if debug_reset {
135 tracing::debug!("Debug was reset");
136 self.state.enabled = false;
137 }
138 } else {
139 core_reset = true;
140 }
141
142 if !self.state.enabled {
144 self.interface.enter_debug_mode()?;
146 self.state.enabled = true;
147
148 if core_reset {
149 let was_running = self
151 .interface
152 .halt_with_previous(Duration::from_millis(500))?;
153
154 self.sequence.on_connect(&mut self.interface)?;
155
156 if was_running {
157 self.run()?;
158 }
159 }
160 }
161
162 Ok(())
163 }
164
165 fn core_info(&mut self) -> Result<CoreInformation, Error> {
166 let pc = self.read_core_reg(self.program_counter().id)?;
167
168 Ok(CoreInformation { pc: pc.try_into()? })
169 }
170
171 fn skip_breakpoint(&mut self) -> Result<(), Error> {
172 self.state.semihosting_command = None;
173 if !self.state.pc_written {
174 let debug_cause = self.debug_cause()?;
175
176 let pc_increment = if debug_cause.break_instruction() {
177 3
178 } else if debug_cause.break_n_instruction() {
179 2
180 } else {
181 0
182 };
183
184 if pc_increment > 0 {
185 let mut pc_value = self.interface.read_register_untyped(Register::CurrentPc)?;
187 pc_value += pc_increment;
188 self.interface
189 .write_register_untyped(Register::CurrentPc, pc_value)?;
190 } else if debug_cause.ibreak_exception() {
191 let pc_value = self.interface.read_register_untyped(Register::CurrentPc)?;
192 let bps = self.hw_breakpoints()?;
193 if let Some(bp_unit) = bps.iter().position(|bp| *bp == Some(pc_value as u64)) {
194 self.clear_hw_breakpoint(bp_unit)?;
196 let ps = self.current_ps()?;
198 self.interface.step(1, ps.intlevel())?;
199 self.set_hw_breakpoint(bp_unit, pc_value as u64)?;
201 }
202 }
203 }
204
205 Ok(())
206 }
207
208 fn check_for_semihosting(&mut self) -> Result<Option<SemihostingCommand>, Error> {
211 const SEMI_BREAK: u32 = const {
212 let InstructionEncoding::Narrow(bytes) = Instruction::Break(1, 14).encode();
213 bytes
214 };
215
216 if let Some(command) = self.state.semihosting_command {
218 return Ok(Some(command));
219 }
220
221 let pc: u64 = self.read_core_reg(self.program_counter().id)?.try_into()?;
222
223 let mut actual_instruction = [0u8; 3];
224 self.read_8(pc, &mut actual_instruction)?;
225 let actual_instruction = u32::from_le_bytes([
226 actual_instruction[0],
227 actual_instruction[1],
228 actual_instruction[2],
229 0,
230 ]);
231
232 tracing::debug!("Semihosting check pc={pc:#x} instruction={actual_instruction:#010x}");
233
234 let command = if actual_instruction == SEMI_BREAK {
235 let syscall = decode_semihosting_syscall(self)?;
236 if let SemihostingCommand::Unknown(details) = syscall {
237 self.sequence
238 .clone()
239 .on_unknown_semihosting_command(self, details)?
240 } else {
241 Some(syscall)
242 }
243 } else {
244 None
245 };
246 self.state.semihosting_command = command;
247
248 Ok(command)
249 }
250
251 fn on_halted(&mut self) -> Result<(), Error> {
252 self.state.pc_written = false;
253 self.clear_cache();
254
255 let status = self.status()?;
256 tracing::debug!("Core halted: {:#?}", status);
257
258 if status.is_halted() {
259 self.sequence.on_halt(&mut self.interface)?;
260 }
261
262 Ok(())
263 }
264
265 fn halt_with_previous(&mut self, timeout: Duration) -> Result<bool, Error> {
266 let was_running = self.interface.halt_with_previous(timeout)?;
267 if was_running {
268 self.on_halted()?;
269 }
270
271 Ok(was_running)
272 }
273
274 fn halted_access<F, T>(&mut self, op: F) -> Result<T, Error>
275 where
276 F: FnOnce(&mut Self) -> Result<T, Error>,
277 {
278 let was_running = self.halt_with_previous(Duration::from_millis(100))?;
279
280 let result = op(self);
281
282 if was_running {
283 self.run()?;
284 }
285
286 result
287 }
288
289 fn current_ps(&mut self) -> Result<ProgramStatus, Error> {
290 Ok(self
293 .interface
294 .read_register_untyped(Register::CurrentPs)
295 .map(ProgramStatus)?)
296 }
297
298 fn debug_cause(&mut self) -> Result<DebugCause, Error> {
299 Ok(self.interface.read_register::<DebugCause>()?)
300 }
301
302 fn spill_registers(&mut self) -> Result<(), Error> {
303 if self.state.spilled {
304 return Ok(());
305 }
306 self.state.spilled = true;
307
308 let register_file = RegisterFile::read(
309 self.interface.core_properties().window_option_properties,
310 &mut self.interface,
311 )?;
312
313 let window_reg_count = register_file.core.window_regs;
314 for reg in 0..window_reg_count {
315 let reg =
316 CpuRegister::try_from(reg).expect("Could not convert register to CpuRegister");
317 let value = register_file.read_register(reg);
318 self.interface
319 .state
320 .register_cache
321 .store(Register::Cpu(reg), value);
322 }
323
324 if self.current_ps()?.excm() {
325 return Ok(());
328 }
329 if self.current_ps()?.woe() {
330 register_file.spill(&mut self.interface)?;
333 }
334
335 Ok(())
336 }
337}
338
339impl MemoryInterface for Xtensa<'_> {
342 fn supports_native_64bit_access(&mut self) -> bool {
343 self.interface.supports_native_64bit_access()
344 }
345
346 fn read_word_64(&mut self, address: u64) -> Result<u64, Error> {
347 self.halted_access(|this| {
348 this.spill_registers()?;
349
350 this.interface.read_word_64(address)
351 })
352 }
353
354 fn read_word_32(&mut self, address: u64) -> Result<u32, Error> {
355 self.halted_access(|this| {
356 this.spill_registers()?;
357
358 this.interface.read_word_32(address)
359 })
360 }
361
362 fn read_word_16(&mut self, address: u64) -> Result<u16, Error> {
363 self.halted_access(|this| {
364 this.spill_registers()?;
365
366 this.interface.read_word_16(address)
367 })
368 }
369
370 fn read_word_8(&mut self, address: u64) -> Result<u8, Error> {
371 self.halted_access(|this| {
372 this.spill_registers()?;
373
374 this.interface.read_word_8(address)
375 })
376 }
377
378 fn read_64(&mut self, address: u64, data: &mut [u64]) -> Result<(), Error> {
379 self.read_8(address, data.as_mut_bytes())
380 }
381
382 fn read_32(&mut self, address: u64, data: &mut [u32]) -> Result<(), Error> {
383 self.read_8(address, data.as_mut_bytes())
384 }
385
386 fn read_16(&mut self, address: u64, data: &mut [u16]) -> Result<(), Error> {
387 self.read_8(address, data.as_mut_bytes())
388 }
389
390 fn read_8(&mut self, address: u64, data: &mut [u8]) -> Result<(), Error> {
391 self.halted_access(|this| {
392 this.spill_registers()?;
393
394 this.interface.read_8(address, data)
395 })
396 }
397
398 fn write_word_64(&mut self, address: u64, data: u64) -> Result<(), Error> {
399 self.interface.write_word_64(address, data)
400 }
401
402 fn write_word_32(&mut self, address: u64, data: u32) -> Result<(), Error> {
403 self.interface.write_word_32(address, data)
404 }
405
406 fn write_word_16(&mut self, address: u64, data: u16) -> Result<(), Error> {
407 self.interface.write_word_16(address, data)
408 }
409
410 fn write_word_8(&mut self, address: u64, data: u8) -> Result<(), Error> {
411 self.interface.write_word_8(address, data)
412 }
413
414 fn write_64(&mut self, address: u64, data: &[u64]) -> Result<(), Error> {
415 self.interface.write_64(address, data)
416 }
417
418 fn write_32(&mut self, address: u64, data: &[u32]) -> Result<(), Error> {
419 self.interface.write_32(address, data)
420 }
421
422 fn write_16(&mut self, address: u64, data: &[u16]) -> Result<(), Error> {
423 self.interface.write_16(address, data)
424 }
425
426 fn write_8(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
427 self.interface.write_8(address, data)
428 }
429
430 fn write(&mut self, address: u64, data: &[u8]) -> Result<(), Error> {
431 self.interface.write(address, data)
432 }
433
434 fn supports_8bit_transfers(&self) -> Result<bool, Error> {
435 self.interface.supports_8bit_transfers()
436 }
437
438 fn flush(&mut self) -> Result<(), Error> {
439 self.interface.flush()
440 }
441}
442
443impl CoreInterface for Xtensa<'_> {
444 fn wait_for_core_halted(&mut self, timeout: Duration) -> Result<(), Error> {
445 self.interface.wait_for_core_halted(timeout)?;
446 self.on_halted()?;
447
448 Ok(())
449 }
450
451 fn core_halted(&mut self) -> Result<bool, Error> {
452 let was_halted = self.interface.state.is_halted;
453 let is_halted = self.interface.core_halted()?;
454
455 if !was_halted && is_halted {
456 self.on_halted()?;
457 }
458
459 Ok(is_halted)
460 }
461
462 fn status(&mut self) -> Result<CoreStatus, Error> {
463 let status = if self.core_halted()? {
464 let debug_cause = self.debug_cause()?;
465
466 let mut reason = debug_cause.halt_reason();
467 if reason == HaltReason::Breakpoint(BreakpointCause::Software) {
468 self.state.pc_written = false;
470 if let Some(cmd) = self.check_for_semihosting()? {
472 reason = HaltReason::Breakpoint(BreakpointCause::Semihosting(cmd));
473 }
474 }
475
476 CoreStatus::Halted(reason)
477 } else {
478 CoreStatus::Running
479 };
480
481 Ok(status)
482 }
483
484 fn halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
485 self.halt_with_previous(timeout)?;
486
487 self.core_info()
488 }
489
490 fn run(&mut self) -> Result<(), Error> {
491 self.skip_breakpoint()?;
492 Ok(self.interface.resume_core()?)
493 }
494
495 fn reset(&mut self) -> Result<(), Error> {
496 self.reset_and_halt(Duration::from_millis(500))?;
497
498 self.run()
499 }
500
501 fn reset_and_halt(&mut self, timeout: Duration) -> Result<CoreInformation, Error> {
502 self.state.semihosting_command = None;
503 self.sequence
504 .reset_system_and_halt(&mut self.interface, timeout)?;
505 self.on_halted()?;
506
507 self.on_attach()?;
509
510 self.core_info()
511 }
512
513 fn step(&mut self) -> Result<CoreInformation, Error> {
514 self.skip_breakpoint()?;
515
516 let ps = self.current_ps()?;
518 self.interface.step(1, ps.intlevel())?;
519
520 self.on_halted()?;
521
522 self.core_info()
523 }
524
525 fn read_core_reg(&mut self, address: RegisterId) -> Result<RegisterValue, Error> {
526 self.halted_access(|this| {
527 let register = Register::try_from(address)?;
528 let value = this.interface.read_register_untyped(register)?;
529
530 Ok(RegisterValue::U32(value))
531 })
532 }
533
534 fn write_core_reg(&mut self, address: RegisterId, value: RegisterValue) -> Result<(), Error> {
535 self.halted_access(|this| {
536 let value: u32 = value.try_into()?;
537
538 if address == this.program_counter().id {
539 this.state.pc_written = true;
540 }
541
542 let register = Register::try_from(address)?;
543 this.interface.write_register_untyped(register, value)?;
544
545 Ok(())
546 })
547 }
548
549 fn available_breakpoint_units(&mut self) -> Result<u32, Error> {
550 Ok(self.interface.available_breakpoint_units())
551 }
552
553 fn hw_breakpoints(&mut self) -> Result<Vec<Option<u64>>, Error> {
554 self.halted_access(|this| {
555 let mut breakpoints = Vec::with_capacity(this.available_breakpoint_units()? as usize);
556
557 let enabled_breakpoints = this.interface.read_register::<IBreakEn>()?;
558
559 for i in 0..this.available_breakpoint_units()? as usize {
560 let is_enabled = enabled_breakpoints.0 & (1 << i) != 0;
561 let breakpoint = if is_enabled {
562 let address = this
563 .interface
564 .read_register_untyped(Self::IBREAKA_REGS[i])?;
565
566 Some(address as u64)
567 } else {
568 None
569 };
570
571 breakpoints.push(breakpoint);
572 }
573
574 Ok(breakpoints)
575 })
576 }
577
578 fn enable_breakpoints(&mut self, state: bool) -> Result<(), Error> {
579 self.halted_access(|this| {
580 this.state.breakpoints_enabled = state;
581 let mask = if state {
582 this.state.breakpoint_mask()
583 } else {
584 0
585 };
586
587 this.interface.write_register(IBreakEn(mask))?;
588
589 Ok(())
590 })
591 }
592
593 fn set_hw_breakpoint(&mut self, unit_index: usize, addr: u64) -> Result<(), Error> {
594 self.halted_access(|this| {
595 this.state.breakpoint_set[unit_index] = true;
596 this.interface
597 .write_register_untyped(Self::IBREAKA_REGS[unit_index], addr as u32)?;
598
599 if this.state.breakpoints_enabled {
600 let mask = this.state.breakpoint_mask();
601 this.interface.write_register(IBreakEn(mask))?;
602 }
603
604 Ok(())
605 })
606 }
607
608 fn clear_hw_breakpoint(&mut self, unit_index: usize) -> Result<(), Error> {
609 self.halted_access(|this| {
610 this.state.breakpoint_set[unit_index] = false;
611
612 if this.state.breakpoints_enabled {
613 let mask = this.state.breakpoint_mask();
614 this.interface.write_register(IBreakEn(mask))?;
615 }
616
617 Ok(())
618 })
619 }
620
621 fn registers(&self) -> &'static CoreRegisters {
622 &XTENSA_CORE_REGISTERS
623 }
624
625 fn program_counter(&self) -> &'static CoreRegister {
626 &PC
627 }
628
629 fn frame_pointer(&self) -> &'static CoreRegister {
630 &FP
631 }
632
633 fn stack_pointer(&self) -> &'static CoreRegister {
634 &SP
635 }
636
637 fn return_address(&self) -> &'static CoreRegister {
638 &RA
639 }
640
641 fn hw_breakpoints_enabled(&self) -> bool {
642 self.state.breakpoints_enabled
643 }
644
645 fn architecture(&self) -> Architecture {
646 Architecture::Xtensa
647 }
648
649 fn core_type(&self) -> CoreType {
650 CoreType::Xtensa
651 }
652
653 fn instruction_set(&mut self) -> Result<InstructionSet, Error> {
654 Ok(InstructionSet::Xtensa)
656 }
657
658 fn fpu_support(&mut self) -> Result<bool, Error> {
659 Ok(false)
661 }
662
663 fn floating_point_register_count(&mut self) -> Result<usize, Error> {
664 Ok(0)
666 }
667
668 fn reset_catch_set(&mut self) -> Result<(), Error> {
669 Err(Error::NotImplemented("reset_catch_set"))
670 }
671
672 fn reset_catch_clear(&mut self) -> Result<(), Error> {
673 Err(Error::NotImplemented("reset_catch_clear"))
674 }
675
676 fn debug_core_stop(&mut self) -> Result<(), Error> {
677 self.interface.leave_debug_mode()?;
678 Ok(())
679 }
680}
681
682struct RegisterFile {
683 core: WindowProperties,
684 registers: Vec<u32>,
685 window_base: u8,
686 window_start: u32,
687}
688
689impl RegisterFile {
690 fn read(
691 xtensa: WindowProperties,
692 interface: &mut XtensaCommunicationInterface<'_>,
693 ) -> Result<Self, Error> {
694 let window_base_result = interface.schedule_read_register(SpecialRegister::Windowbase)?;
695 let window_start_result = interface.schedule_read_register(SpecialRegister::Windowstart)?;
696
697 let mut register_results = Vec::with_capacity(xtensa.num_aregs as usize);
698
699 let ar0 = arch::CpuRegister::A0 as u8;
700
701 interface.restore_registers()?;
704 for ar in ar0..ar0 + xtensa.window_regs {
707 let reg = CpuRegister::try_from(ar)?;
708 interface.state.register_cache.remove(reg.into());
709 }
710
711 for _ in 0..xtensa.num_aregs / xtensa.window_regs {
712 for ar in ar0..ar0 + xtensa.window_regs {
714 let reg = CpuRegister::try_from(ar)?;
715 let result = interface.schedule_read_register(reg)?;
716
717 register_results.push(result);
718 }
719
720 let rotw_arg = xtensa.window_regs / xtensa.rotw_rotates;
722 interface
723 .xdm
724 .schedule_execute_instruction(Instruction::Rotw(rotw_arg));
725 }
726
727 interface
729 .xdm
730 .execute()
731 .expect("Failed to execute read. This shouldn't happen.");
732
733 let mut register_values = register_results
734 .into_iter()
735 .map(|result| interface.read_deferred_result(result))
736 .collect::<Result<Vec<_>, _>>()?;
737
738 let window_base = interface.read_deferred_result(window_base_result)?;
741
742 let window_start = interface.read_deferred_result(window_start_result)?;
758
759 register_values.rotate_right((window_base * xtensa.rotw_rotates as u32) as usize);
762
763 Ok(Self {
764 core: xtensa,
765 registers: register_values,
766 window_base: window_base as u8,
767 window_start,
768 })
769 }
770
771 fn spill(&self, xtensa: &mut XtensaCommunicationInterface<'_>) -> Result<(), Error> {
772 if !self.core.has_windowed_registers {
773 return Ok(());
774 }
775
776 if self.window_start == 0 {
777 return Ok(());
779 }
780
781 let mut window_base = self.next_window_base(self.window_base);
801
802 while let Some(window) = RegisterWindow::at_windowbase(self, window_base) {
803 window.spill(xtensa)?;
804 window_base = self.next_window_base(window_base);
805
806 if window_base == self.window_base {
807 break;
812 }
813 }
814
815 Ok(())
816 }
817
818 fn is_window_start(&self, windowbase: u8) -> bool {
819 self.window_start & 1 << (windowbase % self.core.windowbase_size()) != 0
820 }
821
822 fn next_window_base(&self, window_base: u8) -> u8 {
823 let mut wb = (window_base + 1) % self.core.windowbase_size();
824 while wb != window_base {
825 if self.is_window_start(wb) {
826 break;
827 }
828 wb = (wb + 1) % self.core.windowbase_size();
829 }
830
831 wb
832 }
833
834 fn wb_offset_to_canonical(&self, idx: u8) -> u8 {
835 (idx + self.window_base * self.core.rotw_rotates) % self.core.num_aregs
836 }
837
838 fn read_register(&self, reg: CpuRegister) -> u32 {
839 let index = self.wb_offset_to_canonical(reg as u8);
840 self.registers[index as usize]
841 }
842}
843
844struct RegisterWindow<'a> {
845 window_base: u8,
846
847 window_size: u8,
849
850 file: &'a RegisterFile,
851}
852
853impl<'a> RegisterWindow<'a> {
854 fn at_windowbase(file: &'a RegisterFile, window_base: u8) -> Option<Self> {
855 if !file.is_window_start(window_base) {
856 return None;
857 }
858
859 let next_window_base = file.next_window_base(window_base);
860 let window_size = (next_window_base + file.core.windowbase_size() - window_base)
861 % file.core.windowbase_size();
862
863 Some(Self {
864 window_base,
865 file,
866 window_size: window_size.min(3),
867 })
868 }
869
870 fn read_register(&self, reg: CpuRegister) -> u32 {
872 let index = self.wb_offset_to_canonical(reg as u8);
873 self.file.registers[index as usize]
874 }
875
876 fn wb_offset_to_canonical(&self, idx: u8) -> u8 {
877 (idx + self.window_base * self.file.core.rotw_rotates) % self.file.core.num_aregs
878 }
879
880 fn spill(&self, interface: &mut XtensaCommunicationInterface<'_>) -> Result<(), Error> {
881 let a0_a3 = [
883 self.read_register(CpuRegister::A0),
884 self.read_register(CpuRegister::A1),
885 self.read_register(CpuRegister::A2),
886 self.read_register(CpuRegister::A3),
887 ];
888
889 match self.window_size {
890 0 => {} 1 => interface.write_32(self.read_register(CpuRegister::A5) as u64 - 16, &a0_a3)?,
893
894 2 => {
896 let sp = interface.read_word_32(self.read_register(CpuRegister::A1) as u64 - 12)?;
897
898 interface.write_32(self.read_register(CpuRegister::A9) as u64 - 16, &a0_a3)?;
899
900 if tracing::enabled!(tracing::Level::INFO) {
902 let written =
906 interface.read_word_32(self.read_register(CpuRegister::A9) as u64 - 12)?;
907 assert!(
908 written == self.read_register(CpuRegister::A1),
909 "Failed to spill A1. Expected {:#x}, got {:#x}",
910 self.read_register(CpuRegister::A1),
911 written
912 );
913 }
914
915 let regs = [
916 self.read_register(CpuRegister::A4),
917 self.read_register(CpuRegister::A5),
918 self.read_register(CpuRegister::A6),
919 self.read_register(CpuRegister::A7),
920 ];
921 interface.write_32(sp as u64 - 32, ®s)?;
922 }
923
924 3 => {
926 let sp = interface.read_word_32(self.read_register(CpuRegister::A1) as u64 - 12)?;
927 interface.write_32(self.read_register(CpuRegister::A13) as u64 - 16, &a0_a3)?;
928
929 let regs = [
930 self.read_register(CpuRegister::A4),
931 self.read_register(CpuRegister::A5),
932 self.read_register(CpuRegister::A6),
933 self.read_register(CpuRegister::A7),
934 self.read_register(CpuRegister::A8),
935 self.read_register(CpuRegister::A9),
936 self.read_register(CpuRegister::A10),
937 self.read_register(CpuRegister::A11),
938 ];
939 interface.write_32(sp as u64 - 48, ®s)?;
940 }
941
942 _ => unreachable!(),
945 }
946
947 Ok(())
948 }
949}