1use procmacros::ram;
4use xtensa_lx::interrupt;
5#[cfg(esp32)]
6pub(crate) use xtensa_lx::interrupt::free;
7#[cfg(feature = "rt")]
8use xtensa_lx_rt::exception::Context;
9
10pub use self::vectored::*;
11use super::InterruptStatus;
12use crate::{pac, peripherals::Interrupt, system::Cpu};
13
14#[derive(Copy, Clone, Debug, PartialEq, Eq)]
16#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub enum Error {
18 InvalidInterrupt,
20 CpuInterruptReserved,
22}
23
24#[derive(Debug, Copy, Clone)]
29#[cfg_attr(feature = "defmt", derive(defmt::Format))]
30#[repr(u32)]
31pub enum CpuInterrupt {
32 Interrupt0LevelPriority1 = 0,
34 Interrupt1LevelPriority1,
36 Interrupt2LevelPriority1,
38 Interrupt3LevelPriority1,
40 Interrupt4LevelPriority1,
42 Interrupt5LevelPriority1,
44 Interrupt6Timer0Priority1,
46 Interrupt7SoftwarePriority1,
48 Interrupt8LevelPriority1,
50 Interrupt9LevelPriority1,
52 Interrupt10EdgePriority1,
54 Interrupt11ProfilingPriority3,
56 Interrupt12LevelPriority1,
58 Interrupt13LevelPriority1,
60 Interrupt14NmiPriority7,
62 Interrupt15Timer1Priority3,
64 Interrupt16Timer2Priority5,
66 Interrupt17LevelPriority1,
68 Interrupt18LevelPriority1,
70 Interrupt19LevelPriority2,
72 Interrupt20LevelPriority2,
74 Interrupt21LevelPriority2,
76 Interrupt22EdgePriority3,
78 Interrupt23LevelPriority3,
80 Interrupt24LevelPriority4,
82 Interrupt25LevelPriority4,
84 Interrupt26LevelPriority5,
86 Interrupt27LevelPriority3,
88 Interrupt28EdgePriority4,
90 Interrupt29SoftwarePriority3,
92 Interrupt30EdgePriority4,
94 Interrupt31EdgePriority5,
96}
97
98impl CpuInterrupt {
99 fn from_u32(n: u32) -> Option<Self> {
100 if n < 32 {
101 Some(unsafe { core::mem::transmute::<u32, Self>(n) })
102 } else {
103 None
104 }
105 }
106
107 fn is_internal(self) -> bool {
108 matches!(
109 self,
110 Self::Interrupt6Timer0Priority1
111 | Self::Interrupt7SoftwarePriority1
112 | Self::Interrupt11ProfilingPriority3
113 | Self::Interrupt15Timer1Priority3
114 | Self::Interrupt16Timer2Priority5
115 | Self::Interrupt29SoftwarePriority3
116 )
117 }
118
119 fn is_peripheral(self) -> bool {
120 !self.is_internal()
121 }
122}
123
124#[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
126pub static RESERVED_INTERRUPTS: &[u32] = &[
127 CpuInterrupt::Interrupt1LevelPriority1 as _,
128 CpuInterrupt::Interrupt19LevelPriority2 as _,
129 CpuInterrupt::Interrupt23LevelPriority3 as _,
130 CpuInterrupt::Interrupt10EdgePriority1 as _,
131 CpuInterrupt::Interrupt22EdgePriority3 as _,
132];
133
134pub(crate) fn setup_interrupts() {
135 for peripheral_interrupt in 0..255 {
139 Interrupt::try_from(peripheral_interrupt)
140 .map(|intr| {
141 #[cfg(multi_core)]
142 disable(Cpu::AppCpu, intr);
143 disable(Cpu::ProCpu, intr);
144 })
145 .ok();
146 }
147}
148
149pub fn enable_direct(interrupt: Interrupt, cpu_interrupt: CpuInterrupt) -> Result<(), Error> {
156 if RESERVED_INTERRUPTS.contains(&(cpu_interrupt as _)) {
157 return Err(Error::CpuInterruptReserved);
158 }
159 unsafe {
160 map(Cpu::current(), interrupt, cpu_interrupt);
161
162 xtensa_lx::interrupt::enable_mask(
163 xtensa_lx::interrupt::get_mask() | (1 << cpu_interrupt as u32),
164 );
165 }
166 Ok(())
167}
168
169pub unsafe fn map(cpu: Cpu, interrupt: Interrupt, which: CpuInterrupt) {
178 let interrupt_number = interrupt as usize;
179 let cpu_interrupt_number = which as u32;
180 match cpu {
181 Cpu::ProCpu => unsafe {
182 (*core0_interrupt_peripheral())
183 .core_0_intr_map(interrupt_number)
184 .write(|w| w.bits(cpu_interrupt_number));
185 },
186 #[cfg(multi_core)]
187 Cpu::AppCpu => unsafe {
188 (*core1_interrupt_peripheral())
189 .core_1_intr_map(interrupt_number)
190 .write(|w| w.bits(cpu_interrupt_number));
191 },
192 }
193}
194
195pub(crate) fn bound_cpu_interrupt_for(cpu: Cpu, interrupt: Interrupt) -> Option<CpuInterrupt> {
197 let cpu_intr = match cpu {
198 Cpu::ProCpu => unsafe {
199 (*core0_interrupt_peripheral())
200 .core_0_intr_map(interrupt as usize)
201 .read()
202 .bits()
203 },
204 #[cfg(multi_core)]
205 Cpu::AppCpu => unsafe {
206 (*core1_interrupt_peripheral())
207 .core_1_intr_map(interrupt as usize)
208 .read()
209 .bits()
210 },
211 };
212 let cpu_intr = CpuInterrupt::from_u32(cpu_intr)?;
213
214 if cpu_intr.is_peripheral() {
215 Some(cpu_intr)
216 } else {
217 None
218 }
219}
220
221pub fn disable(core: Cpu, interrupt: Interrupt) {
223 unsafe { map(core, interrupt, CpuInterrupt::Interrupt16Timer2Priority5) }
224}
225
226pub fn clear(_core: Cpu, which: CpuInterrupt) {
228 unsafe {
229 xtensa_lx::interrupt::clear(1 << which as u32);
230 }
231}
232
233#[cfg(interrupts_status_registers = "3")]
235pub fn status(core: Cpu) -> InterruptStatus {
236 unsafe {
237 match core {
238 Cpu::ProCpu => InterruptStatus::from(
239 (*core0_interrupt_peripheral())
240 .core_0_intr_status(0)
241 .read()
242 .bits(),
243 (*core0_interrupt_peripheral())
244 .core_0_intr_status(1)
245 .read()
246 .bits(),
247 (*core0_interrupt_peripheral())
248 .core_0_intr_status(2)
249 .read()
250 .bits(),
251 ),
252 #[cfg(multi_core)]
253 Cpu::AppCpu => InterruptStatus::from(
254 (*core1_interrupt_peripheral())
255 .core_1_intr_status(0)
256 .read()
257 .bits(),
258 (*core1_interrupt_peripheral())
259 .core_1_intr_status(1)
260 .read()
261 .bits(),
262 (*core1_interrupt_peripheral())
263 .core_1_intr_status(2)
264 .read()
265 .bits(),
266 ),
267 }
268 }
269}
270
271#[cfg(interrupts_status_registers = "4")]
273pub fn status(core: Cpu) -> InterruptStatus {
274 unsafe {
275 match core {
276 Cpu::ProCpu => InterruptStatus::from(
277 (*core0_interrupt_peripheral())
278 .core_0_intr_status(0)
279 .read()
280 .bits(),
281 (*core0_interrupt_peripheral())
282 .core_0_intr_status(1)
283 .read()
284 .bits(),
285 (*core0_interrupt_peripheral())
286 .core_0_intr_status(2)
287 .read()
288 .bits(),
289 (*core0_interrupt_peripheral())
290 .core_0_intr_status(3)
291 .read()
292 .bits(),
293 ),
294 #[cfg(multi_core)]
295 Cpu::AppCpu => InterruptStatus::from(
296 (*core1_interrupt_peripheral())
297 .core_1_intr_status(0)
298 .read()
299 .bits(),
300 (*core1_interrupt_peripheral())
301 .core_1_intr_status(1)
302 .read()
303 .bits(),
304 (*core1_interrupt_peripheral())
305 .core_1_intr_status(2)
306 .read()
307 .bits(),
308 (*core1_interrupt_peripheral())
309 .core_1_intr_status(3)
310 .read()
311 .bits(),
312 ),
313 }
314 }
315}
316
317#[cfg(esp32)]
318unsafe fn core0_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
319 pac::DPORT::PTR
320}
321
322#[cfg(esp32)]
323unsafe fn core1_interrupt_peripheral() -> *const crate::pac::dport::RegisterBlock {
324 pac::DPORT::PTR
325}
326
327#[cfg(any(esp32s2, esp32s3))]
328unsafe fn core0_interrupt_peripheral() -> *const crate::pac::interrupt_core0::RegisterBlock {
329 pac::INTERRUPT_CORE0::PTR
330}
331
332#[cfg(esp32s3)]
333unsafe fn core1_interrupt_peripheral() -> *const crate::pac::interrupt_core1::RegisterBlock {
334 pac::INTERRUPT_CORE1::PTR
335}
336
337pub fn current_runlevel() -> Priority {
339 let ps: u32;
340 unsafe { core::arch::asm!("rsr.ps {0}", out(reg) ps) };
341
342 let prev_interrupt_priority = ps as u8 & 0x0F;
343
344 unwrap!(Priority::try_from(prev_interrupt_priority))
345}
346
347pub(crate) unsafe fn change_current_runlevel(level: Priority) -> Priority {
356 let token: u32;
357 unsafe {
358 match level {
359 Priority::None => core::arch::asm!("rsil {0}, 0", out(reg) token),
360 Priority::Priority1 => core::arch::asm!("rsil {0}, 1", out(reg) token),
361 Priority::Priority2 => core::arch::asm!("rsil {0}, 2", out(reg) token),
362 Priority::Priority3 => core::arch::asm!("rsil {0}, 3", out(reg) token),
363 };
364 }
365
366 let prev_interrupt_priority = token as u8 & 0x0F;
367
368 unwrap!(Priority::try_from(prev_interrupt_priority))
369}
370
371mod vectored {
372 use super::*;
373
374 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
376 #[cfg_attr(feature = "defmt", derive(defmt::Format))]
377 #[repr(u8)]
378 pub enum Priority {
379 None = 0,
381 Priority1,
383 Priority2,
385 Priority3,
387 }
388
389 impl Priority {
390 pub const fn max() -> Priority {
392 Priority::Priority3
393 }
394
395 pub const fn min() -> Priority {
397 Priority::Priority1
398 }
399 }
400
401 impl TryFrom<u32> for Priority {
402 type Error = Error;
403
404 fn try_from(value: u32) -> Result<Self, Self::Error> {
405 match value {
406 0 => Ok(Priority::None),
407 1 => Ok(Priority::Priority1),
408 2 => Ok(Priority::Priority2),
409 3 => Ok(Priority::Priority3),
410 _ => Err(Error::InvalidInterrupt),
411 }
412 }
413 }
414
415 impl TryFrom<u8> for Priority {
416 type Error = Error;
417
418 fn try_from(value: u8) -> Result<Self, Self::Error> {
419 Self::try_from(value as u32)
420 }
421 }
422
423 impl CpuInterrupt {
424 #[inline]
425 fn level(&self) -> Priority {
426 match self {
427 CpuInterrupt::Interrupt0LevelPriority1
428 | CpuInterrupt::Interrupt1LevelPriority1
429 | CpuInterrupt::Interrupt2LevelPriority1
430 | CpuInterrupt::Interrupt3LevelPriority1
431 | CpuInterrupt::Interrupt4LevelPriority1
432 | CpuInterrupt::Interrupt5LevelPriority1
433 | CpuInterrupt::Interrupt6Timer0Priority1
434 | CpuInterrupt::Interrupt7SoftwarePriority1
435 | CpuInterrupt::Interrupt8LevelPriority1
436 | CpuInterrupt::Interrupt9LevelPriority1
437 | CpuInterrupt::Interrupt10EdgePriority1
438 | CpuInterrupt::Interrupt12LevelPriority1
439 | CpuInterrupt::Interrupt13LevelPriority1
440 | CpuInterrupt::Interrupt17LevelPriority1
441 | CpuInterrupt::Interrupt18LevelPriority1 => Priority::Priority1,
442
443 CpuInterrupt::Interrupt19LevelPriority2
444 | CpuInterrupt::Interrupt20LevelPriority2
445 | CpuInterrupt::Interrupt21LevelPriority2 => Priority::Priority2,
446
447 CpuInterrupt::Interrupt11ProfilingPriority3
448 | CpuInterrupt::Interrupt15Timer1Priority3
449 | CpuInterrupt::Interrupt22EdgePriority3
450 | CpuInterrupt::Interrupt27LevelPriority3
451 | CpuInterrupt::Interrupt29SoftwarePriority3
452 | CpuInterrupt::Interrupt23LevelPriority3 => Priority::Priority3,
453
454 CpuInterrupt::Interrupt24LevelPriority4
457 | CpuInterrupt::Interrupt25LevelPriority4
458 | CpuInterrupt::Interrupt28EdgePriority4
459 | CpuInterrupt::Interrupt30EdgePriority4
460 | CpuInterrupt::Interrupt31EdgePriority5
461 | CpuInterrupt::Interrupt16Timer2Priority5
462 | CpuInterrupt::Interrupt26LevelPriority5
463 | CpuInterrupt::Interrupt14NmiPriority7 => Priority::None,
464 }
465 }
466 }
467
468 #[inline(always)]
470 pub(crate) fn configured_interrupts(
471 core: Cpu,
472 status: InterruptStatus,
473 level: u32,
474 ) -> InterruptStatus {
475 unsafe {
476 let intr_map_base = match core {
477 Cpu::ProCpu => (*core0_interrupt_peripheral()).core_0_intr_map(0).as_ptr(),
478 #[cfg(multi_core)]
479 Cpu::AppCpu => (*core1_interrupt_peripheral()).core_1_intr_map(0).as_ptr(),
480 };
481
482 let mut res = InterruptStatus::empty();
483
484 for interrupt_nr in status.iterator() {
485 let i = interrupt_nr as isize;
486 let cpu_interrupt = intr_map_base.offset(i).read_volatile();
487 let cpu_interrupt = core::mem::transmute::<u32, CpuInterrupt>(cpu_interrupt);
489 let int_level = cpu_interrupt.level() as u32;
490
491 if int_level == level {
492 res.set(interrupt_nr);
493 }
494 }
495
496 res
497 }
498 }
499
500 pub fn enable(interrupt: Interrupt, level: Priority) -> Result<(), Error> {
502 enable_on_cpu(Cpu::current(), interrupt, level)
503 }
504
505 pub(crate) fn enable_on_cpu(
506 cpu: Cpu,
507 interrupt: Interrupt,
508 level: Priority,
509 ) -> Result<(), Error> {
510 let cpu_interrupt =
511 interrupt_level_to_cpu_interrupt(level, chip_specific::interrupt_is_edge(interrupt))?;
512
513 unsafe {
514 map(cpu, interrupt, cpu_interrupt);
515
516 xtensa_lx::interrupt::enable_mask(
517 xtensa_lx::interrupt::get_mask() | (1 << cpu_interrupt as u32),
518 );
519 }
520 Ok(())
521 }
522
523 pub unsafe fn bind_interrupt(interrupt: Interrupt, handler: unsafe extern "C" fn()) {
529 let ptr = unsafe {
530 &pac::__INTERRUPTS[interrupt as usize]._handler as *const _
531 as *mut unsafe extern "C" fn()
532 };
533 unsafe {
534 ptr.write_volatile(handler);
535 }
536 }
537
538 pub fn bound_handler(interrupt: Interrupt) -> Option<unsafe extern "C" fn()> {
540 unsafe {
541 let addr = pac::__INTERRUPTS[interrupt as usize]._handler;
542 if addr as usize == 0 {
543 return None;
544 }
545 Some(addr)
546 }
547 }
548
549 fn interrupt_level_to_cpu_interrupt(
550 level: Priority,
551 is_edge: bool,
552 ) -> Result<CpuInterrupt, Error> {
553 Ok(if is_edge {
554 match level {
555 Priority::None => return Err(Error::InvalidInterrupt),
556 Priority::Priority1 => CpuInterrupt::Interrupt10EdgePriority1,
557 Priority::Priority2 => return Err(Error::InvalidInterrupt),
558 Priority::Priority3 => CpuInterrupt::Interrupt22EdgePriority3,
559 }
560 } else {
561 match level {
562 Priority::None => return Err(Error::InvalidInterrupt),
563 Priority::Priority1 => CpuInterrupt::Interrupt1LevelPriority1,
564 Priority::Priority2 => CpuInterrupt::Interrupt19LevelPriority2,
565 Priority::Priority3 => CpuInterrupt::Interrupt23LevelPriority3,
566 }
567 })
568 }
569
570 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
572 pub(crate) static CPU_INTERRUPT_LEVELS: [u32; 8] = [
573 0b_0000_0000_0000_0000_0000_0000_0000_0000, 0b_0000_0000_0000_0110_0011_0111_1111_1111, 0b_0000_0000_0011_1000_0000_0000_0000_0000, 0b_0010_1000_1100_0000_1000_1000_0000_0000, 0b_0101_0011_0000_0000_0000_0000_0000_0000, 0b_1000_0100_0000_0001_0000_0000_0000_0000, 0b_0000_0000_0000_0000_0000_0000_0000_0000, 0b_0000_0000_0000_0000_0100_0000_0000_0000, ];
582 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
583 pub(crate) static CPU_INTERRUPT_INTERNAL: u32 = 0b_0010_0000_0000_0001_1000_1000_1100_0000;
584 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
585 pub(crate) static CPU_INTERRUPT_EDGE: u32 = 0b_0111_0000_0100_0000_0000_1100_1000_0000;
586
587 #[cfg(esp32)]
588 pub(crate) mod chip_specific {
589 use super::*;
590 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
591 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
592 0b0000_0000_0000_0000_0000_0000_0000_0000,
593 0b1111_1100_0000_0000_0000_0000_0000_0000,
594 0b0000_0000_0000_0000_0000_0000_0000_0011,
595 );
596 #[inline]
597 pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
598 [
599 Interrupt::TG0_T0_EDGE,
600 Interrupt::TG0_T1_EDGE,
601 Interrupt::TG0_WDT_EDGE,
602 Interrupt::TG0_LACT_EDGE,
603 Interrupt::TG1_T0_EDGE,
604 Interrupt::TG1_T1_EDGE,
605 Interrupt::TG1_WDT_EDGE,
606 Interrupt::TG1_LACT_EDGE,
607 ]
608 .contains(&interrupt)
609 }
610 }
611
612 #[cfg(esp32s2)]
613 pub(crate) mod chip_specific {
614 use super::*;
615 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
616 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::from(
617 0b0000_0000_0000_0000_0000_0000_0000_0000,
618 0b1100_0000_0000_0000_0000_0000_0000_0000,
619 0b0000_0000_0000_0000_0000_0011_1011_1111,
620 );
621 #[inline]
622 pub fn interrupt_is_edge(interrupt: Interrupt) -> bool {
623 [
624 Interrupt::TG0_T0_EDGE,
625 Interrupt::TG0_T1_EDGE,
626 Interrupt::TG0_WDT_EDGE,
627 Interrupt::TG0_LACT_EDGE,
628 Interrupt::TG1_T0_EDGE,
629 Interrupt::TG1_T1_EDGE,
630 Interrupt::TG1_WDT_EDGE,
631 Interrupt::TG1_LACT_EDGE,
632 Interrupt::SYSTIMER_TARGET0,
633 Interrupt::SYSTIMER_TARGET1,
634 Interrupt::SYSTIMER_TARGET2,
635 ]
636 .contains(&interrupt)
637 }
638 }
639
640 #[cfg(esp32s3)]
641 pub(crate) mod chip_specific {
642 use super::*;
643 #[cfg_attr(place_switch_tables_in_ram, unsafe(link_section = ".rwtext"))]
644 pub static INTERRUPT_EDGE: InterruptStatus = InterruptStatus::empty();
645 #[inline]
646 pub fn interrupt_is_edge(_interrupt: Interrupt) -> bool {
647 false
648 }
649 }
650}
651
652#[cfg(feature = "rt")]
653mod rt {
654 use super::{vectored::*, *};
655
656 #[unsafe(no_mangle)]
657 #[ram]
658 unsafe fn __level_1_interrupt(save_frame: &mut Context) {
659 unsafe {
660 handle_interrupts::<1>(save_frame);
661 }
662 }
663
664 #[unsafe(no_mangle)]
665 #[ram]
666 unsafe fn __level_2_interrupt(save_frame: &mut Context) {
667 unsafe {
668 handle_interrupts::<2>(save_frame);
669 }
670 }
671
672 #[unsafe(no_mangle)]
673 #[ram]
674 unsafe fn __level_3_interrupt(save_frame: &mut Context) {
675 unsafe {
676 handle_interrupts::<3>(save_frame);
677 }
678 }
679
680 #[inline(always)]
681 unsafe fn handle_interrupts<const LEVEL: u32>(save_frame: &mut Context) {
682 let core = Cpu::current();
683
684 let cpu_interrupt_mask =
685 interrupt::get() & interrupt::get_mask() & CPU_INTERRUPT_LEVELS[LEVEL as usize];
686
687 if cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL != 0 {
688 let cpu_interrupt_mask = cpu_interrupt_mask & CPU_INTERRUPT_INTERNAL;
693
694 let cpu_interrupt_nr = cpu_interrupt_mask.trailing_zeros();
696
697 if ((1 << cpu_interrupt_nr) & CPU_INTERRUPT_EDGE) != 0 {
700 unsafe {
701 interrupt::clear(1 << cpu_interrupt_nr);
702 }
703 }
704
705 if let Some(handler) = cpu_interrupt_nr_to_cpu_interrupt_handler(cpu_interrupt_nr) {
706 unsafe { handler(save_frame) };
707 }
708 } else {
709 let status = if !cfg!(esp32s3) && (cpu_interrupt_mask & CPU_INTERRUPT_EDGE) != 0 {
710 unsafe { interrupt::clear(cpu_interrupt_mask & CPU_INTERRUPT_EDGE) };
716
717 chip_specific::INTERRUPT_EDGE
720 } else {
721 status(core)
724 };
725
726 let configured_interrupts = configured_interrupts(core, status, LEVEL);
727 for interrupt_nr in configured_interrupts.iterator() {
728 let handler = unsafe { pac::__INTERRUPTS[interrupt_nr as usize]._handler };
729 let handler: fn(&mut Context) = unsafe {
730 core::mem::transmute::<unsafe extern "C" fn(), fn(&mut Context)>(handler)
731 };
732 handler(save_frame);
733 }
734 }
735 }
736
737 #[inline]
738 pub(crate) fn cpu_interrupt_nr_to_cpu_interrupt_handler(
739 number: u32,
740 ) -> Option<unsafe extern "C" fn(save_frame: &mut Context)> {
741 use xtensa_lx_rt::*;
742 Some(match number {
744 6 => Timer0,
745 7 => Software0,
746 11 => Profiling,
747 14 => NMI,
748 15 => Timer1,
749 16 => Timer2,
750 29 => Software1,
751 _ => return None,
752 })
753 }
754
755 unsafe extern "C" {
757 fn level4_interrupt(save_frame: &mut Context);
758 fn level5_interrupt(save_frame: &mut Context);
759 fn level6_interrupt(save_frame: &mut Context);
760 fn level7_interrupt(save_frame: &mut Context);
761 }
762
763 #[unsafe(no_mangle)]
764 #[unsafe(link_section = ".rwtext")]
765 unsafe fn __level_4_interrupt(save_frame: &mut Context) {
766 unsafe { level4_interrupt(save_frame) }
767 }
768
769 #[unsafe(no_mangle)]
770 #[unsafe(link_section = ".rwtext")]
771 unsafe fn __level_5_interrupt(save_frame: &mut Context) {
772 unsafe { level5_interrupt(save_frame) }
773 }
774
775 #[unsafe(no_mangle)]
776 #[unsafe(link_section = ".rwtext")]
777 unsafe fn __level_6_interrupt(save_frame: &mut Context) {
778 unsafe { level6_interrupt(save_frame) }
779 }
780
781 #[unsafe(no_mangle)]
782 #[unsafe(link_section = ".rwtext")]
783 unsafe fn __level_7_interrupt(save_frame: &mut Context) {
784 unsafe { level7_interrupt(save_frame) }
785 }
786}