psp/
lib.rs

1#![allow(
2    stable_features,
3    internal_features,
4    clippy::bad_bit_mask,
5    clippy::missing_safety_doc
6)]
7#![feature(
8    asm_experimental_arch,
9    alloc_error_handler,
10    global_asm,
11    const_loop,
12    const_if_match,
13    core_intrinsics,
14    c_variadic,
15    lang_items,
16    rustc_attrs
17)]
18// For unwinding support
19#![feature(std_internals, panic_info_message, panic_internals, c_unwind)]
20#![cfg_attr(not(feature = "stub-only"), feature(panic_unwind))]
21#![cfg_attr(feature = "std", feature(psp_std))]
22// For the `const_generics` feature.
23#![allow(incomplete_features)]
24#![cfg_attr(not(feature = "std"), no_std)]
25
26#[macro_use]
27extern crate paste;
28#[cfg(not(feature = "stub-only"))]
29extern crate alloc;
30#[cfg(not(feature = "stub-only"))]
31extern crate panic_unwind;
32
33#[macro_use]
34#[doc(hidden)]
35#[cfg(not(feature = "stub-only"))]
36pub mod debug;
37
38#[macro_use]
39mod vfpu;
40mod eabi;
41pub mod math;
42pub mod sys;
43#[cfg(not(feature = "stub-only"))]
44pub mod test_runner;
45#[cfg(not(feature = "stub-only"))]
46pub mod vram_alloc;
47
48#[cfg(not(feature = "stub-only"))]
49mod alloc_impl;
50#[cfg(not(feature = "stub-only"))]
51pub mod panic;
52
53#[cfg(not(feature = "stub-only"))]
54mod screenshot;
55#[cfg(not(feature = "stub-only"))]
56pub use screenshot::*;
57
58#[cfg(not(feature = "stub-only"))]
59mod benchmark;
60#[cfg(not(feature = "stub-only"))]
61pub use benchmark::*;
62
63#[cfg(not(feature = "stub-only"))]
64mod constants;
65#[cfg(not(feature = "stub-only"))]
66pub use constants::*;
67
68#[doc(hidden)]
69pub use unstringify::unstringify;
70
71#[cfg(not(feature = "std"))]
72#[cfg(feature = "stub-only")]
73#[panic_handler]
74fn panic(_: &core::panic::PanicInfo) -> ! {
75    loop {
76        core::hint::spin_loop()
77    }
78}
79
80#[cfg(not(test))]
81#[cfg_attr(not(bootstrap), rustc_std_internal_symbol)]
82extern "C" fn __rust_foreign_exception() -> ! {
83    loop {
84        core::hint::spin_loop()
85    }
86}
87
88#[cfg(feature = "std")]
89pub use std::panic::catch_unwind;
90
91#[cfg(all(not(feature = "std"), not(feature = "stub-only")))]
92pub use panic::catch_unwind;
93
94#[cfg(feature = "embedded-graphics")]
95pub mod embedded_graphics;
96
97#[repr(align(16))]
98#[derive(Copy, Clone)]
99pub struct Align16<T>(pub T);
100
101#[cfg(all(target_os = "psp", not(feature = "stub-only")))]
102core::arch::global_asm!(
103    r#"
104        .section .lib.ent.top, "a", @progbits
105        .align 2
106        .word 0
107    .global __lib_ent_top
108    __lib_ent_top:
109        .section .lib.ent.btm, "a", @progbits
110        .align 2
111    .global __lib_ent_bottom
112    __lib_ent_bottom:
113        .word 0
114
115        .section .lib.stub.top, "a", @progbits
116        .align 2
117        .word 0
118    .global __lib_stub_top
119    __lib_stub_top:
120        .section .lib.stub.btm, "a", @progbits
121        .align 2
122    .global __lib_stub_bottom
123    __lib_stub_bottom:
124        .word 0
125    "#
126);
127
128#[cfg(feature = "std")]
129extern "C" {
130    #[link_name = "main"]
131    #[doc(hidden)]
132    pub fn c_main(argc: isize, argv: *const *const u8) -> isize;
133}
134
135// Because code generated by `module!()` lives in user's crate, cfg attribute cannot be used there.
136// Hence this macro.
137#[cfg(feature = "std")]
138#[doc(hidden)]
139#[macro_export]
140macro_rules! _start {
141    ($_:expr, $argc:expr, $argv:expr) => {
142        unsafe { $crate::c_main($argc as _, $argv as _) as _ }
143    };
144}
145#[cfg(not(feature = "std"))]
146#[doc(hidden)]
147#[macro_export]
148macro_rules! _start {
149    ($psp_main:expr, $argc:expr, $argv:expr) => {{
150        unsafe fn init_cwd(arg0: *mut u8) {
151            let mut len = 0;
152            while *arg0.add(len) != 0 {
153                len += 1;
154            }
155
156            // Truncate until last '/'
157            while len > 0 && *arg0.add(len - 1) != b'/' {
158                len -= 1;
159            }
160
161            if len > 0 {
162                let tmp = *arg0.add(len);
163                *arg0.add(len) = 0;
164                $crate::sys::sceIoChdir(arg0 as *const u8);
165                *arg0.add(len) = tmp;
166            }
167        }
168
169        if $argc > 0 {
170            unsafe { init_cwd($argv as *mut u8) };
171        }
172
173        // TODO: Maybe print any error to debug screen?
174        let _ = $crate::catch_unwind($psp_main);
175
176        0
177    }};
178}
179
180/// Declare a PSP module.
181///
182/// You must also define a `fn psp_main() { ... }` function in conjunction with
183/// this macro.
184#[macro_export]
185macro_rules! module {
186    ($name:expr, $version_major:expr, $version_minor: expr) => {
187        #[doc(hidden)]
188        mod __psp_module {
189            #[no_mangle]
190            #[link_section = ".rodata.sceModuleInfo"]
191            #[used]
192            static MODULE_INFO: $crate::Align16<$crate::sys::SceModuleInfo> =
193                $crate::Align16($crate::sys::SceModuleInfo {
194                    mod_attribute: 0,
195                    mod_version: [$version_major, $version_minor],
196                    mod_name: $crate::sys::SceModuleInfo::name($name),
197                    terminal: 0,
198                    gp_value: unsafe { &_gp },
199                    stub_top: unsafe { &__lib_stub_top },
200                    stub_end: unsafe { &__lib_stub_bottom },
201                    ent_top: unsafe { &__lib_ent_top },
202                    ent_end: unsafe { &__lib_ent_bottom },
203                });
204
205            extern "C" {
206                static _gp: u8;
207                static __lib_ent_bottom: u8;
208                static __lib_ent_top: u8;
209                static __lib_stub_bottom: u8;
210                static __lib_stub_top: u8;
211            }
212
213            #[no_mangle]
214            #[link_section = ".lib.ent"]
215            #[used]
216            static LIB_ENT: $crate::sys::SceLibraryEntry = $crate::sys::SceLibraryEntry {
217                // TODO: Fix this?
218                name: core::ptr::null(),
219                version: ($version_major, $version_minor),
220                attribute: $crate::sys::SceLibAttr::SCE_LIB_IS_SYSLIB,
221                entry_len: 4,
222                var_count: 1,
223                func_count: 1,
224                entry_table: &LIB_ENT_TABLE,
225            };
226
227            #[no_mangle]
228            #[link_section = ".rodata.sceResident"]
229            #[used]
230            static LIB_ENT_TABLE: $crate::sys::SceLibraryEntryTable =
231                $crate::sys::SceLibraryEntryTable {
232                    module_start_nid: 0xd632acdb, // module_start
233                    module_info_nid: 0xf01d73a7,  // SceModuleInfo
234                    module_start: module_start,
235                    module_info: &MODULE_INFO.0,
236                };
237
238            use core::ffi::c_void;
239
240            #[no_mangle]
241            extern "C" fn module_start(argc_bytes: usize, argv: *mut c_void) -> isize {
242                extern "C" fn main_thread(argc: usize, argv: *mut c_void) -> i32 {
243                    $crate::_start!(super::psp_main, argc, argv)
244                }
245
246                unsafe {
247                    let id = $crate::sys::sceKernelCreateThread(
248                        b"main_thread\0".as_ptr(),
249                        main_thread,
250                        // default priority of 32.
251                        32,
252                        // 256kb stack
253                        256 * 1024,
254                        $crate::sys::ThreadAttributes::USER | $crate::sys::ThreadAttributes::VFPU,
255                        core::ptr::null_mut(),
256                    );
257
258                    $crate::sys::sceKernelStartThread(id, argc_bytes, argv);
259                }
260
261                0
262            }
263        }
264    };
265}
266
267/// Enable the home button.
268///
269/// This API does not have destructor support yet. You can manually setup an
270/// exit callback if you need this, see the source code of this function.
271pub fn enable_home_button() {
272    use core::{ffi::c_void, ptr};
273    use sys::ThreadAttributes;
274
275    unsafe {
276        unsafe extern "C" fn exit_thread(_args: usize, _argp: *mut c_void) -> i32 {
277            unsafe extern "C" fn exit_callback(_arg1: i32, _arg2: i32, _arg: *mut c_void) -> i32 {
278                sys::sceKernelExitGame();
279                0
280            }
281
282            let id = sys::sceKernelCreateCallback(
283                &b"exit_callback\0"[0],
284                exit_callback,
285                ptr::null_mut(),
286            );
287
288            sys::sceKernelRegisterExitCallback(id);
289            sys::sceKernelSleepThreadCB();
290
291            0
292        }
293
294        // Enable the home button.
295        let id = sys::sceKernelCreateThread(
296            &b"exit_thread\0"[0],
297            exit_thread,
298            32,
299            0x1000,
300            ThreadAttributes::empty(),
301            ptr::null_mut(),
302        );
303
304        sys::sceKernelStartThread(id, 0, ptr::null_mut());
305    }
306}