···11use core::str::Utf8Error;
2233+#[cfg(feature = "defmt")]
44+use defmt::{warn, debug};
55+36pub const XIP_BASE: *const u8 = 0x10000000 as *const u8;
47pub const PROGRAM_RAM_AREA_BASE: *mut u8 = 0x20020000 as *mut u8;
5869pub const SLOT_SIZE: usize = 0x80000;
7101111+pub const unsafe fn slot(id: u8) -> *const ProgramSlotHeader {
1212+ if id > 31 {
1313+ panic!("slot ID must be between 0 and 31");
1414+ }
1515+1616+ if id == 0 {
1717+ // "Slot 0" is used for the launcher, which is stored 128K into the flash
1818+ XIP_BASE.add(128 * 1024).cast()
1919+ } else {
2020+ XIP_BASE.add(SLOT_SIZE * id as usize).cast()
2121+ }
2222+}
2323+2424+2525+pub struct Programs {
2626+ id: u8,
2727+}
2828+2929+impl Programs {
3030+ pub fn new() -> Self {
3131+ Self { id: 0 }
3232+ }
3333+}
3434+3535+impl Iterator for Programs {
3636+ type Item = *const ProgramSlotHeader;
3737+3838+ fn next(&mut self) -> Option<Self::Item> {
3939+ loop {
4040+ self.id += 1;
4141+4242+ if self.id > 31 {
4343+ return None;
4444+ }
4545+4646+ let s = unsafe { slot(self.id) };
4747+4848+ unsafe {
4949+ if (*s).is_valid() {
5050+ #[cfg(feature = "defmt")]
5151+ debug!("Found program {} version {} in slot {}", (*s).name().unwrap(), (*s).version().unwrap(), self.id);
5252+ return Some(s);
5353+ } else {
5454+ #[cfg(feature = "defmt")]
5555+ debug!("No program found in slot {}", self.id);
5656+ }
5757+ }
5858+ }
5959+ }
6060+6161+ fn size_hint(&self) -> (usize, Option<usize>) {
6262+ (0, Some(32 - self.id as usize))
6363+ }
6464+}
6565+866#[repr(C)]
967pub struct ProgramSlotHeader {
1068 pub block_erase_cycles: usize,
···13711472 pub data_len: usize,
1573 pub data_lma: *const u8,
1616- pub data_vma: *const u8,
7474+ pub data_vma: *mut u8,
17751876 pub bss_len: usize,
1919- pub bss_vma: *const u8,
7777+ pub bss_vma: *mut u8,
20782179 pub name_len: usize,
2280 pub name_ptr: *const u8,
···3795 len: 0,
3896 data_len: 0,
3997 data_lma: core::ptr::null(),
4040- data_vma: core::ptr::null(),
9898+ data_vma: core::ptr::null_mut(),
4199 bss_len: 0,
4242- bss_vma: core::ptr::null(),
100100+ bss_vma: core::ptr::null_mut(),
43101 name_len: name.len(),
44102 name_ptr: name.as_ptr(),
45103 version_len: version.len(),
···48106 }
49107 }
50108109109+ pub fn is_valid(&self) -> bool {
110110+ // Erased flash contains all 1s
111111+ if self.len == 0 || self.len == usize::MAX {
112112+ return false;
113113+ }
114114+115115+ if self.len > SLOT_SIZE {
116116+ #[cfg(feature = "defmt")]
117117+ warn!("Program header has invalid size");
118118+ return false;
119119+ }
120120+121121+ if !self.check_crc() {
122122+ #[cfg(feature = "defmt")]
123123+ warn!("Program has invalid CRC");
124124+ return false;
125125+ }
126126+127127+ if self.name().is_err() {
128128+ #[cfg(feature = "defmt")]
129129+ warn!("Program name is not valid UTF-8");
130130+ return false;
131131+ }
132132+133133+ if self.version().is_err() {
134134+ #[cfg(feature = "defmt")]
135135+ warn!("Program version string is not valid UTF-8");
136136+ return false;
137137+ }
138138+139139+ let slot_min = (&raw const *self) as usize;
140140+ let slot_max = slot_min + SLOT_SIZE;
141141+ let slot_range = slot_min..slot_max;
142142+ let ram_min = PROGRAM_RAM_AREA_BASE as usize;
143143+ let ram_max = ram_min + 136 * 1024;
144144+ let ram_range = ram_min..ram_max;
145145+146146+ if self.data_len > 0 {
147147+ if !slot_range.contains(&(self.data_lma as usize)) || !slot_range.contains(&(self.data_lma as usize + self.data_len - 1)) {
148148+ #[cfg(feature = "defmt")]
149149+ warn!("Program has invalid data section addresses");
150150+ return false;
151151+ }
152152+153153+ if !ram_range.contains(&(self.data_vma as usize)) || !ram_range.contains(&(self.data_vma as usize + self.data_len - 1)) {
154154+ #[cfg(feature = "defmt")]
155155+ warn!("Program has invalid data section load addresses");
156156+ return false;
157157+ }
158158+ }
159159+160160+ if self.bss_len > 0 {
161161+ if !ram_range.contains(&(self.bss_vma as usize)) || !ram_range.contains(&(self.data_vma as usize + self.data_len - 1)) {
162162+ #[cfg(feature = "defmt")]
163163+ warn!("Program has invalid bss section addresses");
164164+ return false;
165165+ }
166166+ }
167167+168168+ true
169169+ }
170170+171171+ pub fn slot(&self) -> u8 {
172172+ (((&raw const *self as usize) - XIP_BASE as usize) / SLOT_SIZE) as u8
173173+ }
174174+51175 pub fn check_crc(&self) -> bool {
52176 if self.len >= SLOT_SIZE || self.len < size_of::<ProgramSlotHeader>() {
53177 return false;
···68192 pub fn version(&self) -> Result<&str, Utf8Error> {
69193 unsafe {
70194 core::str::from_utf8(core::slice::from_raw_parts(self.version_ptr, self.version_len))
195195+ }
196196+ }
197197+198198+ pub unsafe fn load(&self) {
199199+ if self.data_len > 0 {
200200+ core::ptr::copy_nonoverlapping(self.data_lma, self.data_vma, self.data_len);
201201+ }
202202+203203+ if self.bss_len > 0 {
204204+ self.bss_vma.write_bytes(0, self.bss_len);
71205 }
72206 }
73207}
+6-41
eepy-sys/src/input.rs
···11use core::fmt::{Display, Formatter};
22-use crate::syscall;
22+use core::mem::MaybeUninit;
33+use crate::{syscall, SafeOption};
34use crate::syscall::SyscallNumber;
4556#[repr(usize)]
···7677 }
7778}
78797979-#[repr(usize)]
8080-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
8181-#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8282-pub enum ScEventType {
8383- NoEvent = 0,
8484- TouchUp = 1,
8585- TouchDown = 2,
8686- TouchMove = 3,
8787- RefreshFinished = 4,
8888-}
8989-9090-impl TryFrom<usize> for ScEventType {
9191- type Error = ();
9292-9393- fn try_from(value: usize) -> Result<Self, Self::Error> {
9494- match value {
9595- x if x == ScEventType::NoEvent as usize => Ok(ScEventType::NoEvent),
9696- x if x == ScEventType::TouchUp as usize => Ok(ScEventType::TouchUp),
9797- x if x == ScEventType::TouchDown as usize => Ok(ScEventType::TouchDown),
9898- x if x == ScEventType::TouchMove as usize => Ok(ScEventType::TouchMove),
9999- x if x == ScEventType::RefreshFinished as usize => Ok(ScEventType::RefreshFinished),
100100- _ => Err(()),
101101- }
102102- }
103103-}
104104-10580pub fn next_event() -> Option<Event> {
106106- let mut ev_type: usize;
107107- let mut x: u16;
108108- let mut y: u16;
8181+ let mut event: MaybeUninit<SafeOption<Event>> = MaybeUninit::uninit();
1098211083 unsafe {
11184 syscall!(
11285 SyscallNumber::Input,
113113- out ev_type in InputSyscall::NextEvent,
114114- out x,
115115- out y,
8686+ in InputSyscall::NextEvent,
8787+ in event.as_mut_ptr(),
11688 );
117117- }
11889119119- match ScEventType::try_from(ev_type) {
120120- Ok(ScEventType::NoEvent) => None,
121121- Ok(ScEventType::TouchUp) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Up, x, y })),
122122- Ok(ScEventType::TouchDown) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Down, x, y })),
123123- Ok(ScEventType::TouchMove) => Some(Event::Touch(TouchEvent { ev_type: TouchEventType::Move, x, y })),
124124- Ok(ScEventType::RefreshFinished) => Some(Event::RefreshFinished),
125125- Err(_) => panic!("invalid touch event"),
9090+ event.assume_init().into()
12691 }
12792}
12893
+31-1
eepy-sys/src/lib.rs
···66pub mod image;
77pub mod input;
88pub mod usb;
99+pub mod exec;
9101010-pub use tp370pgh01::IMAGE_BYTES;1111+pub use tp370pgh01::IMAGE_BYTES;
1212+1313+/// FFI-safe version of the standard `Option` type.
1414+///
1515+/// Convert to/from a standard `Option` using `.into()`.
1616+#[repr(C)]
1717+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
1818+#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1919+pub enum SafeOption<T> {
2020+ None,
2121+ Some(T),
2222+}
2323+2424+impl<T> From<Option<T>> for SafeOption<T> {
2525+ fn from(value: Option<T>) -> Self {
2626+ match value {
2727+ None => SafeOption::None,
2828+ Some(v) => SafeOption::Some(v),
2929+ }
3030+ }
3131+}
3232+3333+impl<T> From<SafeOption<T>> for Option<T> {
3434+ fn from(value: SafeOption<T>) -> Self {
3535+ match value {
3636+ SafeOption::None => None,
3737+ SafeOption::Some(v) => Some(v),
3838+ }
3939+ }
4040+}
+2
eepy-sys/src/syscall.rs
···66 Image = 1,
77 Input = 2,
88 Usb = 3,
99+ Exec = 4,
910}
10111112impl TryFrom<u8> for SyscallNumber {
···1718 x if x == SyscallNumber::Image as u8 => Ok(SyscallNumber::Image),
1819 x if x == SyscallNumber::Input as u8 => Ok(SyscallNumber::Input),
1920 x if x == SyscallNumber::Usb as u8 => Ok(SyscallNumber::Usb),
2121+ x if x == SyscallNumber::Exec as u8 => Ok(SyscallNumber::Exec),
2022 _ => Err(()),
2123 }
2224 }