├── .gitignore
├── .gitmodules
├── .rgignore
├── Cargo.toml
├── LICENSE
├── README.md
├── bindgen.sh
├── cargo-psp
├── Cargo.toml
└── src
│ ├── bin
│ ├── mksfo.rs
│ ├── pack-pbp.rs
│ └── prxgen.rs
│ └── main.rs
├── ci
├── Dockerfile-ppsspp
├── concourse
│ ├── build-rust.sh
│ ├── build-rust.yml
│ ├── concourse.yml
│ ├── run-tests.sh
│ └── run-tests.yml
├── std_verification
│ ├── Cargo.toml
│ └── src
│ │ ├── main.rs
│ │ └── time_test.rs
└── tests
│ ├── Cargo.toml
│ ├── assets
│ ├── blank_screenshot.bmp
│ └── embedded_graphics_triangle.bmp
│ └── src
│ ├── bmp_screenshot_test.rs
│ ├── main.rs
│ ├── math_test.rs
│ └── vram_test.rs
├── demo.gif
├── examples
├── clock-speed
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── cube
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── ferris.bin
│ ├── ferris.png
│ └── src
│ │ └── main.rs
├── embedded-graphics
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── assets
│ │ └── ferris.bmp
│ └── src
│ │ └── main.rs
├── gu-background
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── gu-debug-print
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── hello-world
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── msg-dialog
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── paint-mode
│ ├── Cargo.toml
│ ├── Psp.toml
│ └── src
│ │ ├── analog_stick_to_delta.rs
│ │ ├── background.rs
│ │ ├── debug_textbox.rs
│ │ ├── drawobject.rs
│ │ ├── lib.rs
│ │ └── main.rs
├── rainbow
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── rust-std-hello-world
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── time
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── vfpu-addition
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
├── vfpu-context-switching
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ │ └── main.rs
└── wlan
│ ├── .gitignore
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── libunwind
├── .gitignore
├── Makefile
└── no-sdc1.patch
├── prx.md
└── psp
├── Cargo.toml
├── build.rs
├── libunwind.a
└── src
├── alloc_impl.rs
├── benchmark.rs
├── constants.rs
├── debug.rs
├── eabi.rs
├── embedded_graphics.rs
├── lib.rs
├── math
├── mod.rs
└── trig.rs
├── msxfont.bin
├── panic.rs
├── screenshot.rs
├── sys
├── atrac.rs
├── audio.rs
├── codec.rs
├── ctrl.rs
├── debugfont.bin
├── display.rs
├── font.rs
├── ge.rs
├── gu.rs
├── gum.rs
├── hprm.rs
├── io.rs
├── jpeg.rs
├── kernel
│ ├── mod.rs
│ └── thread.rs
├── macros.rs
├── mod.rs
├── mp3.rs
├── mpeg.rs
├── nand.rs
├── net.rs
├── openpsid.rs
├── power.rs
├── registry.rs
├── rtc.rs
├── sircs.rs
├── types.rs
├── umd.rs
├── usb.rs
├── utility.rs
├── vfpu_context.rs
└── wlan.rs
├── test_runner.rs
├── vfpu.rs
└── vram_alloc.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | Cargo.lock
3 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "libunwind/rustc"]
2 | path = libunwind/rustc
3 | url = https://github.com/rust-lang/rust
4 |
--------------------------------------------------------------------------------
/.rgignore:
--------------------------------------------------------------------------------
1 | libunwind/rustc
2 |
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [workspace]
2 |
3 | members = [ "psp", "cargo-psp" ]
4 | exclude = [ "examples", "ci" ]
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright © 2020 Marko Mijalkovic
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
9 |
10 |
11 | This project also uses the PSPSDK as a reference:
12 |
13 | Copyright (c) 2005 adresd
14 | Copyright (c) 2005 Marcus R. Brown
15 | Copyright (c) 2005 James Forshaw
16 | Copyright (c) 2005 John Kelley
17 | Copyright (c) 2005 Jesper Svennevid
18 | All rights reserved.
19 |
20 | Redistribution and use in source and binary forms, with or without
21 | modification, are permitted provided that the following conditions
22 | are met:
23 | 1. Redistributions of source code must retain the above copyright
24 | notice, this list of conditions and the following disclaimer.
25 | 2. Redistributions in binary form must reproduce the above copyright
26 | notice, this list of conditions and the following disclaimer in the
27 | documentation and/or other materials provided with the distribution.
28 | 3. The names of the authors may not be used to endorse or promote products
29 | derived from this software without specific prior written permission.
30 |
31 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
32 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
33 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
34 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
35 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
36 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
37 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
38 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
39 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
40 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
rust-psp
2 |
3 | 
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | A library for building full PSP modules, including both PRX plugins and regular
17 | homebrew apps.
18 |
19 |
20 | ```rust
21 | #![no_std]
22 | #![no_main]
23 |
24 | psp::module!("sample_module", 1, 1);
25 |
26 | fn psp_main() {
27 | psp::enable_home_button();
28 | psp::dprintln!("Hello PSP from rust!");
29 | }
30 | ```
31 |
32 | See `examples` directory for sample programs.
33 |
34 | ## What about PSPSDK?
35 |
36 | This project is a completely new SDK, with no dependency on the original C/C++
37 | PSPSDK. It aims to be a **complete** replacement, with more efficient
38 | implementations of graphics functions, and the addition of missing libraries.
39 |
40 | ## Features / Roadmap
41 |
42 | - [x] `core` support
43 | - [x] PSP system library support
44 | - [x] `alloc` support
45 | - [x] `panic = "unwind"` support
46 | - [x] Macro-based VFPU assembler
47 | - [x] Full 3D graphics support (faster than PSPSDK in some cases!)
48 | - [x] No dependency on PSPSDK / PSPToolchain
49 | - [x] Reach full parity with user mode support in PSPSDK
50 | - [x] Port definitions to `libc` crate
51 | - [ ] Add support for creating kernel mode modules
52 | - [ ] Add `std` support
53 | - [ ] Automatically sign EBOOT.PBP files to run on unmodified PSPs
54 | - [ ] Implement / reverse undiscovered libraries
55 |
56 | ## Dependencies
57 |
58 | To compile for the PSP, you will need a Rust **nightly** version equal to or
59 | later than `2020-06-05` and the `rust-src` component. Please install Rust using
60 | https://rustup.rs/
61 |
62 | Use the following if you are new to Rust. (Feel free to set an override manually
63 | per-project instead).
64 |
65 | ```sh
66 | $ rustup default nightly && rustup component add rust-src
67 | ```
68 |
69 | You also need `cargo-psp` installed:
70 |
71 | ```sh
72 | $ cargo install cargo-psp
73 | ```
74 |
75 | ## Running Examples
76 |
77 | Enter one of the example directories, `examples/hello-world` for instance, and
78 | run `cargo psp`.
79 |
80 | This will create an `EBOOT.PBP` file under `target/mipsel-sony-psp/debug/`
81 |
82 | Assuming you have a PSP with custom firmware installed, you can simply copy this
83 | file into a new directory under `PSP/GAME` on your memory stick, and it will
84 | show up in your XMB menu.
85 |
86 | ```
87 | .
88 | └── PSP
89 | └── GAME
90 | └── hello-world
91 | └── EBOOT.PBP
92 | ```
93 |
94 | If you do not have a PSP, we recommend using the [PPSSPP emulator](http://ppsspp.org).
95 | Note that graphics code is very sensitive so if you're writing graphics code we
96 | recommend developing on real hardware. PPSSPP is more relaxed in some aspects.
97 |
98 | ### Advanced usage: `PRXEncrypter`
99 |
100 | If you don't have a PSP with CFW installed, you can manually sign the PRX using
101 | `PRXEncrypter`, and then re-package it using `pack-pbp`.
102 |
103 | ### Advanced usage: PSPLink
104 |
105 | If you have the PSPSDK installed and have built a working copy PSPLink manually,
106 | you can also use `psplink` and `pspsh` to run the `.prx` under
107 | `target/mipsel-sony-psp/debug/` if you prefer. Refer to the installation and
108 | usage guides for those programs.
109 |
110 | ### Debugging
111 |
112 | `psp-gdb` is currently too old to support printing Rust types. `rust-lldb` may
113 | be possible but it has not be experimented with yet.
114 |
115 | ## Usage
116 |
117 | To use the `psp` crate in your own Rust programs, add it to `Cargo.toml` like
118 | any other dependency:
119 |
120 | ```toml
121 | [dependencies]
122 | psp = "x.y.z"
123 | ```
124 |
125 | In your `main.rs` file, you need to setup a basic skeleton like so:
126 |
127 | ```rust
128 | #![no_std]
129 | #![no_main]
130 |
131 | // Create a module named "sample_module" with version 1.0
132 | psp::module!("sample_module", 1, 0);
133 |
134 | fn psp_main() {
135 | psp::enable_home_button();
136 | psp::dprintln!("Hello PSP from rust!");
137 | }
138 | ```
139 |
140 | Now you can simply run `cargo psp` to build your `EBOOT.PBP` file. You can also
141 | invoke `cargo psp --release` to create a release build.
142 |
143 | If you would like to customize your EBOOT with e.g. an icon or new title, you
144 | can create a `Psp.toml` file in the root of your project. Note that all keys are
145 | optional:
146 |
147 | ```toml
148 | title = "XMB title"
149 | xmb_icon_png = "path/to/24bit_144x80_image.png"
150 | xmb_background_png = "path/to/24bit_480x272_background.png"
151 | xmb_music_at3 = "path/to/ATRAC3_audio.at3"
152 | ```
153 |
154 | More options can be found in the schema defintion [here](/cargo-psp/src/main.rs#L11-L91).
155 |
156 | ## Known Bugs
157 |
158 | This crate **breaks** on builds with `opt-level=0`. Likely due to a bug in EABI
159 | interop. `cargo-psp` patches over this by passing `-C opt-level=3`.
160 |
161 | ## `error[E0460]: found possibly newer version of crate ...`
162 |
163 | If you get an error like this:
164 |
165 | ```
166 | error[E0460]: found possibly newer version of crate `panic_unwind` which `psp` depends on
167 | --> src/main.rs:4:5
168 | |
169 | 4 | use psp::dprintln;
170 | | ^^^
171 | |
172 | = note: perhaps that crate needs to be recompiled?
173 | ```
174 |
175 | Simply clean your target directory and it will be fixed:
176 |
177 | ```sh
178 | $ cargo clean
179 | ```
180 |
--------------------------------------------------------------------------------
/bindgen.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Quick and dirty script to generate psp_extern! bindings from a PSPSDK header.
4 |
5 | header() {
6 | bindgen $1 --no-layout-tests --whitelist-function 'sce.*' -- \
7 | -I /usr/local/pspdev/lib/gcc/psp/9.3.0/include \
8 | -I /usr/local/pspdev/psp/sdk/include \
9 | -I /usr/local/pspdev/psp/include \
10 | -include sys/types.h -include pspkerneltypes.h \
11 | | sed -re '
12 | # Format doc comments as triple slashes
13 | s/#\[doc = "([^"]*)"\]/\/\/\/\1/g
14 |
15 | # Remove redundant raw types
16 | s/::std::os::raw::c_int/i32/g
17 | s/::std::os::raw::c_uint/u32/g
18 | s/::std::os::raw::c_void/c_void/g
19 | s/::std::os::raw::c_char/u8/g
20 | s/::std::os::raw::c_uchar/u8/g
21 | s/::std::os::raw::c_ulong/u32/g
22 | s/::std::os::raw::c_long/i32/g
23 | s/::std::os::raw::c_ushort/u16/g
24 | s/::std::os::raw::c_short/i16/g
25 | s/::std::option::Option/Option/g
26 |
27 | s/uint/u32/g
28 |
29 | s/SceSize/usize/g
30 | s/SceSSize/isize/g
31 | s/SceVoid/c_void/g
32 |
33 | s/SceInt32/i32/g
34 | s/SceShort16/i16/g
35 |
36 | s/SceUInt32/u32/g
37 | s/SceUChar8/u8/g
38 |
39 | s/@param (\w*)\s+- /- `\1`: /
40 | s/@return (.*)/# Return Value\n \/\/\/\n \/\/\/ \1/
41 |
42 | # Delete redundant types resulting from previous replacements
43 | /pub type (\w+) = (\1);/d
44 | /pub type (usize|isize) =/d
45 | ' \
46 | | awk '
47 | # Bindgen generates multiple extern blocks so we want to merge them.
48 | /^}$/ { if (found_extern) { printf "\n"; next } }
49 | /^extern/ {
50 | if (!found_extern) {
51 | found_extern = 1;
52 | printf "\npsp_extern! {\n";
53 | }
54 |
55 | next;
56 | }
57 |
58 | # Add parameters header
59 | /\/\/\/ - `/ { if (!params) printf " /// # Parameters\n ///\n" }
60 | { if ($0 ~ /\s*\/\/\/ - `/) params = 1; else params = 0 }
61 |
62 | { print }
63 |
64 | END { print "}" }
65 | ' \
66 | | rustfmt
67 | }
68 |
69 | pspModule() {
70 | grep 'IMPORT_' < $1 \
71 | | sed '
72 | s/.*IMPORT_START\s*//
73 | s/.*IMPORT_FUNC.*",//
74 | ' \
75 | | cat - <(echo) # Add newline
76 | }
77 |
78 | # Utility function for awk
79 | AWK_CAMEL='
80 | function camelToSnake(s, last, i) {
81 | out = ""
82 |
83 | for (i = 0; i < length(s); i++) {
84 | char = substr(s, i + 1, 1)
85 |
86 | if (tolower(char) != char) {
87 | if (last != i - 1) out = out "_"
88 | last = i
89 | out = out tolower(char)
90 | } else {
91 | out = out char
92 | }
93 | }
94 |
95 | return out
96 | }
97 | '
98 |
99 | cat <(pspModule $2) <(header $1) \
100 | | awk "$AWK_CAMEL"'
101 | /^$/ { header_done = 1 }
102 |
103 | {
104 | if (!name) {
105 | name = substr($0, 1, match($0, ",") - 1)
106 | flags = substr($0, match($0, ",") + 1 + 2, 4)
107 | maj_ver = substr($0, match($0, ",") + 1 + 6, 2)
108 | min_ver = substr($0, match($0, ",") + 1 + 8, 2)
109 |
110 | next
111 | } else if (!header_done) {
112 | nid_name = substr($0, match($0, ",") + 1)
113 | nid_hex = substr($0, 1, match($0, ",") - 1)
114 |
115 | # Remove carriage returns
116 | gsub("\r", "", nid_name)
117 |
118 | nid_map[nid_name] = nid_hex
119 |
120 | next
121 | }
122 | }
123 |
124 | # Fix camel case struct fields
125 | /\s*pub \w*:/ {
126 | # field name start and end index
127 | start = match($0, "pub ") + 4
128 | end = match($0, ":")
129 | camel = substr($0, start, end - start)
130 |
131 | printf "%s%s%s\n", substr($0, 1, start - 1), camelToSnake(camel), substr($0, end)
132 |
133 | next
134 | }
135 |
136 | /psp_extern!/ {
137 | print
138 | print " #![name = " name "]"
139 | print " #![flags = 0x" flags "]"
140 | print " #![version = (0x" maj_ver ", 0x" min_ver ")]"
141 | printf "\n"
142 |
143 | RS = "\n\n"
144 |
145 | next
146 | }
147 |
148 | /pub fn/ {
149 | # Function name start and end index
150 | fn_start = match($0, "fn ") + 3
151 | fn_end = match(substr($0, fn_start), "\\(") + fn_start
152 | fn_name = substr($0, fn_start, fn_end - fn_start - 1)
153 |
154 | printf " #[psp(" nid_map[fn_name] ")]\n"
155 | printf substr($0, 1, fn_start - 1)
156 | printf fn_name "("
157 |
158 | # Substring of the argument list
159 | args = substr($0, fn_end, match(substr($0, fn_end), ")") - 1)
160 |
161 | args_multiline = match(args, "\n")
162 | fn_return = fn_end + length(args)
163 |
164 | if (args_multiline) printf "\n"
165 |
166 | # Fix the argument names
167 | while (1) {
168 | # These are indices
169 | arg_start = match(args, "[^[:space:]]")
170 | arg_colon = match(args, ":")
171 | arg_comma = match(args, ",")
172 |
173 | # Exit if no more arguments
174 | if (!arg_start) break
175 |
176 | arg_len = arg_comma - arg_start
177 |
178 | # Get the camelCase and snake_case variants of the argument name
179 | arg_camel = substr(args, arg_start, arg_colon - arg_start)
180 | arg_snake = camelToSnake(arg_camel)
181 |
182 | # Recreate the argument definition with the correct case
183 | if (arg_comma) arg = substr(args, arg_start, arg_len)
184 | else arg = substr(args, arg_start)
185 | sub(arg_camel, arg_snake, arg)
186 |
187 | if (args_multiline) printf " " arg ",\n"
188 | else if (arg_comma) printf arg ", "
189 | else printf arg
190 |
191 | # Advance the argument substring onto the next one
192 | if (arg_comma) args = substr(args, arg_comma + 1)
193 | else break
194 | }
195 |
196 | if (args_multiline) printf " "
197 |
198 | print substr($0, fn_return)
199 | printf "\n"
200 |
201 | next
202 | }
203 |
204 | { print }
205 | ' \
206 | | awk "$AWK_CAMEL"'
207 | # Fix camel case doc comment parameter names
208 | /\s*\/\/\/ - `/ {
209 | start = index($0, "`") + 1
210 | end = index(substr($0, start), "`") + start
211 | camel = substr($0, start, end - start)
212 |
213 | sub(camel, camelToSnake(camel), $0)
214 |
215 | print $0
216 | next
217 | }
218 |
219 | { print }
220 | '
221 |
--------------------------------------------------------------------------------
/cargo-psp/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "cargo-psp"
3 | version = "0.1.1"
4 | description = "`cargo build` wrapper for creating Sony PSP executables"
5 | repository = "https://github.com/overdrivenpotato/rust-psp"
6 | license = "MIT"
7 | authors = [
8 | "Marko Mijalkovic ",
9 | "Paul Sajna "
10 | ]
11 | edition = "2018"
12 | default-run = "cargo-psp"
13 |
14 | [[bin]]
15 | name = "prxgen"
16 |
17 | [[bin]]
18 | name = "pack-pbp"
19 |
20 | [[bin]]
21 | name = "mksfo"
22 |
23 | [dependencies]
24 | clap = "2.33.1"
25 | goblin = "0.2.3"
26 | scroll = "0.10.1"
27 | cargo_metadata = "0.10.0"
28 | rustc_version = "0.2.3"
29 |
30 | serde = "1.0.111"
31 | serde_derive = "1.0.111"
32 | toml = "0.5.6"
33 |
--------------------------------------------------------------------------------
/cargo-psp/src/bin/pack-pbp.rs:
--------------------------------------------------------------------------------
1 | use clap::{App, Arg, AppSettings};
2 | use std::{fs, mem};
3 |
4 | const SIGNATURE: [u8; 4] = *b"\0PBP";
5 | const VERSION: u32 = 0x1_0000;
6 |
7 | struct PbpHeader {
8 | signature: [u8; 4],
9 | version: u32,
10 | offsets: [u32; 8],
11 | }
12 |
13 | impl PbpHeader {
14 | fn to_bytes(self) -> [u8; mem::size_of::()] {
15 | let mut bytes = [0; mem::size_of::()];
16 |
17 | bytes[0..4].copy_from_slice(&self.signature);
18 | bytes[4..8].copy_from_slice(&self.version.to_le_bytes());
19 |
20 | for (i, offset) in self.offsets.iter().enumerate() {
21 | let idx = i * 4 + 8;
22 | bytes[idx..idx + 4].copy_from_slice(&offset.to_le_bytes());
23 | }
24 |
25 | bytes
26 | }
27 | }
28 |
29 | fn main() {
30 | let matches = App::new("pack-pbp")
31 | .version("0.1")
32 | .author("Marko Mijalkovic ")
33 | .about("Create Sony PSP packages")
34 | .setting(AppSettings::ColoredHelp)
35 | .arg(
36 | Arg::with_name("output.pbp")
37 | .takes_value(true)
38 | .help("Output PBP file")
39 | .required(true)
40 | )
41 | .arg(
42 | Arg::with_name("param.sfo")
43 | .takes_value(true)
44 | .help("Input PARAM.SFO file created with mksfo")
45 | .required(true)
46 | )
47 | .arg(
48 | Arg::with_name("icon0.png")
49 | .takes_value(true)
50 | .help("Optional XMB icon")
51 | .required(true)
52 | )
53 | .arg(
54 | Arg::with_name("icon1.pmf")
55 | .takes_value(true)
56 | .help("Optional animated XMB icon")
57 | .required(true)
58 | )
59 | .arg(
60 | Arg::with_name("pic0.png")
61 | .takes_value(true)
62 | .help("Optional XMB background (overlayed on top of PIC1.PNG)")
63 | .required(true)
64 | )
65 | .arg(
66 | Arg::with_name("pic1.png")
67 | .takes_value(true)
68 | .help("Optional XMB background")
69 | .required(true)
70 | )
71 | .arg(
72 | Arg::with_name("snd0.at3")
73 | .takes_value(true)
74 | .help("Optional XMB music (when present, sound from ICON1.PMF is ignored)")
75 | .required(true)
76 | )
77 | .arg(
78 | Arg::with_name("data.psp")
79 | .takes_value(true)
80 | .help("Executable file")
81 | .required(true)
82 | )
83 | .arg(
84 | Arg::with_name("data.psar")
85 | .takes_value(true)
86 | .help("Optional archive data")
87 | .required(true)
88 | )
89 | .get_matches();
90 |
91 | let read = |name: &str| {
92 | let value = matches.value_of(name).unwrap();
93 |
94 | if value == "NULL" {
95 | return None;
96 | }
97 |
98 | match fs::read(value) {
99 | Ok(b) => Some(b),
100 | Err(e) => panic!("failed to read {}: {}", value, e),
101 | }
102 | };
103 |
104 | let output_path = matches.value_of("output.pbp").unwrap();
105 |
106 | let files = vec![
107 | read("param.sfo"),
108 | read("icon0.png"),
109 | read("icon1.pmf"),
110 | read("pic0.png"),
111 | read("pic1.png"),
112 | read("snd0.at3"),
113 | read("data.psp"),
114 | read("data.psar"),
115 | ];
116 |
117 | let mut payload = Vec::new();
118 | let mut offsets = [0; 8];
119 | let mut current_offset = mem::size_of::() as u32;
120 |
121 | for (i, bytes) in files.into_iter().enumerate() {
122 | offsets[i] = current_offset;
123 |
124 | if let Some(bytes) = bytes {
125 | let len = bytes.len();
126 | payload.extend(bytes);
127 | current_offset += len as u32;
128 | }
129 | }
130 |
131 | let header = PbpHeader {
132 | signature: SIGNATURE,
133 | version: VERSION,
134 | offsets,
135 | };
136 |
137 | let mut output = Vec::new();
138 | output.extend(&header.to_bytes()[..]);
139 | output.extend(payload);
140 |
141 | if let Err(e) = fs::write(output_path, output) {
142 | panic!("couldn't write to {}: {}", output_path, e);
143 | }
144 |
145 | println!("Saved to {}", output_path);
146 | }
147 |
--------------------------------------------------------------------------------
/ci/Dockerfile-ppsspp:
--------------------------------------------------------------------------------
1 | FROM ubuntu:latest as ppsspp
2 |
3 | ENV DEBIAN_FRONTEND="noninteractive"
4 |
5 | RUN apt-get -y update
6 | RUN apt-get install -y build-essential cmake git libsdl2-dev python libglew-dev
7 | RUN git clone https://github.com/hrydgard/ppsspp --recursive
8 | WORKDIR /ppsspp/build-sdl
9 | RUN cmake .. \
10 | -DCMAKE_BUILD_TYPE=Release \
11 | -DCMAKE_SKIP_RPATH=ON \
12 | -DHEADLESS=ON \
13 | -DUSE_SYSTEM_LIBZIP=ON
14 | RUN make
15 | RUN make install
16 |
--------------------------------------------------------------------------------
/ci/concourse/build-rust.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -euo pipefail
4 |
5 | # If NO_CACHE is *not* set, then setup the cache directories
6 | if [ -z "${NO_CACHE:-}" ]; then
7 | # Cache only for normal builds
8 | export CARGO_HOME="$(pwd)"/.cargo
9 | export XARGO_HOME="$(pwd)"/.xargo
10 | export RUSTUP_HOME="$(pwd)"/.rustup
11 | fi
12 |
13 | rustup set profile minimal
14 | rustup update --no-self-update $RUSTUP_TOOLCHAIN
15 |
16 | # Install rust-src if needed.
17 | if ! rustup component list --installed | grep -q rust-src; then
18 | rustup component add rust-src
19 | fi
20 |
21 | pushd repo/cargo-psp/
22 | cargo build
23 | popd
24 |
25 | PATH="$(realpath repo)/target/debug:$PATH"
26 |
27 | pushd repo/ci/tests
28 | cargo psp
29 | popd
30 |
31 | cp -r repo/ci/tests/target/mipsel-sony-psp/debug/* rust-build-dir
32 |
--------------------------------------------------------------------------------
/ci/concourse/build-rust.yml:
--------------------------------------------------------------------------------
1 | platform: linux
2 |
3 | image_resource:
4 | type: docker-image
5 | source:
6 | repository: rust
7 |
8 | # The version is irrelevant here. This is a small base image that contains
9 | # `rustup`. The actual toolchain is selected with `RUSTUP_TOOLCHAIN` below,
10 | # and should be cached with `RUSTUP_HOME`.
11 | tag: 1.44-slim
12 |
13 | params:
14 | RUSTFLAGS: "-C link-dead-code"
15 | RUSTUP_TOOLCHAIN: nightly-2020-06-05
16 |
17 | inputs:
18 | - name: repo
19 |
20 | outputs:
21 | - name: rust-build-dir
22 |
23 | caches:
24 | - path: .cargo
25 | - path: .xargo
26 | - path: .rustup
27 | - path: repo/ci/tests/target/
28 | - path: repo/target/
29 |
30 | run:
31 | path: repo/ci/concourse/build-rust.sh
32 |
--------------------------------------------------------------------------------
/ci/concourse/concourse.yml:
--------------------------------------------------------------------------------
1 | resource_types:
2 | - name: pull-request
3 | type: docker-image
4 | source:
5 | repository: teliaoss/github-pr-resource
6 | - name: cron-resource
7 | type: docker-image
8 | source:
9 | repository: cftoolsmiths/cron-resource
10 | - name: slack-alert
11 | type: docker-image
12 | source:
13 | repository: arbourd/concourse-slack-alert-resource
14 |
15 | resources:
16 | - name: master-branch
17 | type: git
18 | source:
19 | uri: https://github.com/overdrivenpotato/rust-psp
20 | branch: master
21 | - name: nightly-trigger
22 | type: cron-resource
23 | source:
24 | # Trigger at 9AM New York time
25 | expression: "0 9 * * *"
26 | location: "America/New_York"
27 | - name: discord-channel
28 | type: slack-alert
29 | source:
30 | url: ((webhook-url))
31 | - name: pull-request
32 | type: pull-request
33 | source:
34 | repository: overdrivenpotato/rust-psp
35 | access_token: ((gh-access-token))
36 |
37 | jobs:
38 | - name: run-tests-for-master
39 | public: true
40 | plan:
41 | - do:
42 | - get: repo
43 | resource: master-branch
44 | params: { submodules: none }
45 | trigger: true
46 | - task: build-rust
47 | file: repo/ci/concourse/build-rust.yml
48 | - task: run-tests
49 | file: repo/ci/concourse/run-tests.yml
50 |
51 | - name: run-tests-for-pr
52 | public: true
53 | plan:
54 | - do:
55 | - get: repo
56 | resource: pull-request
57 | version: every
58 | trigger: true
59 | - put: repo
60 | resource: pull-request
61 | params:
62 | path: repo
63 | status: pending
64 | - task: build-rust
65 | file: repo/ci/concourse/build-rust.yml
66 | - task: run-tests
67 | file: repo/ci/concourse/run-tests.yml
68 | on_failure:
69 | put: repo
70 | resource: pull-request
71 | params:
72 | path: repo
73 | status: failure
74 | on_success:
75 | put: repo
76 | resource: pull-request
77 | params:
78 | path: repo
79 | status: success
80 |
81 | - name: test-rustc-nightly
82 | public: true
83 | plan:
84 | - do:
85 | - get: nightly-trigger
86 | trigger: true
87 | - get: repo
88 | resource: master-branch
89 | params: { submodules: none }
90 | - task: build-rust
91 | file: repo/ci/concourse/build-rust.yml
92 | params:
93 | RUSTUP_TOOLCHAIN: nightly
94 | NO_CACHE: true
95 | - task: run-tests
96 | file: repo/ci/concourse/run-tests.yml
97 | on_failure:
98 | put: discord-channel
99 | params:
100 | message: Rust-PSP failed with latest nightly
101 | type: failed
102 | color: '#ff0000'
103 |
--------------------------------------------------------------------------------
/ci/concourse/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -euo pipefail
4 |
5 | /ppsspp/build-sdl/PPSSPPHeadless rust-build-dir/EBOOT.PBP --timeout=10 -r .
6 |
7 | cat psp_output_file.log
8 |
9 | if [ "$(tail -n 1 psp_output_file.log)" == FINAL_SUCCESS ]; then
10 | echo Test passed
11 | else
12 | echo Test failed
13 | exit 1
14 | fi
15 |
--------------------------------------------------------------------------------
/ci/concourse/run-tests.yml:
--------------------------------------------------------------------------------
1 | platform: linux
2 |
3 | image_resource:
4 | type: docker-image
5 | source:
6 | repository: rustpsp/ppsspp-headless
7 |
8 | inputs:
9 | - name: repo
10 | - name: rust-build-dir
11 |
12 | outputs:
13 | - name: rust-build-dir
14 |
15 | run:
16 | path: repo/ci/concourse/run-tests.sh
17 |
--------------------------------------------------------------------------------
/ci/std_verification/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "std_test_cases"
3 | version = "0.1.0"
4 | authors = ["Glenn Hope "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp", features = ["std"] }
9 |
--------------------------------------------------------------------------------
/ci/std_verification/src/main.rs:
--------------------------------------------------------------------------------
1 | #![feature(restricted_std)]
2 | #![no_main]
3 |
4 | use psp::test_runner::TestRunner;
5 |
6 | mod time_test;
7 |
8 | psp::module!("std_verification", 1, 1);
9 |
10 | fn psp_main() {
11 | let tests = &[
12 | time_test::test_main,
13 | ];
14 |
15 | let mut runner = TestRunner::new_dprintln_runner();
16 | runner.start_run();
17 |
18 | for test in tests {
19 | runner.run(test);
20 | }
21 |
22 | runner.finish_run();
23 | }
24 |
--------------------------------------------------------------------------------
/ci/std_verification/src/time_test.rs:
--------------------------------------------------------------------------------
1 | use std::time::{Instant, SystemTime, Duration, UNIX_EPOCH};
2 | use psp::test_runner::TestRunner;
3 |
4 | const BEFORE_PSP: Duration = Duration::from_secs(1000212400);
5 |
6 | pub fn test_main(test_runner: &mut TestRunner) {
7 | let now = SystemTime::now();
8 | test_runner.check_list(&[
9 | ("system_time_sane", (now - BEFORE_PSP) > UNIX_EPOCH, true),
10 | ("instant_increments", Instant::now() < Instant::now(), true),
11 | ]);
12 | }
13 |
--------------------------------------------------------------------------------
/ci/tests/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "test_cases"
3 | version = "0.1.0"
4 | authors = ["Glenn Hope "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp", features = ["embedded-graphics"] }
9 | embedded-graphics = "0.6.2"
10 |
--------------------------------------------------------------------------------
/ci/tests/assets/blank_screenshot.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/ci/tests/assets/blank_screenshot.bmp
--------------------------------------------------------------------------------
/ci/tests/assets/embedded_graphics_triangle.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/ci/tests/assets/embedded_graphics_triangle.bmp
--------------------------------------------------------------------------------
/ci/tests/src/bmp_screenshot_test.rs:
--------------------------------------------------------------------------------
1 | use alloc::vec::Vec;
2 | use core::ffi::c_void;
3 |
4 | use embedded_graphics::prelude::*;
5 | use embedded_graphics::pixelcolor::Rgb888;
6 | use embedded_graphics::primitives::{rectangle::Rectangle, triangle::Triangle};
7 | use embedded_graphics::style::PrimitiveStyleBuilder;
8 |
9 | use psp::embedded_graphics::Framebuffer;
10 | use psp::test_runner::TestRunner;
11 |
12 | const BLANK_SCREENSHOT: &[u8] = include_bytes!("../assets/blank_screenshot.bmp");
13 | const EG_TRIANGLE_SCREENSHOT: &[u8] = include_bytes!("../assets/embedded_graphics_triangle.bmp");
14 |
15 | pub fn test_main(test_runner: &mut TestRunner) {
16 | test_runner.check_large_collection("blank_screenshot", BLANK_SCREENSHOT, &blank_screenshot());
17 |
18 | test_runner.check_large_collection(
19 | "embedded_graphics_triangle",
20 | EG_TRIANGLE_SCREENSHOT,
21 | &eg_triangle_screenshot(),
22 | );
23 | }
24 |
25 | // NOTE: This does not clear the screen, so running it
26 | // after other tests will most likely fail until that is added.
27 | fn blank_screenshot() -> Vec {
28 | psp::screenshot_bmp()
29 | }
30 |
31 | fn eg_triangle_screenshot() -> Vec {
32 | let mut disp = Framebuffer::new();
33 |
34 | let style = PrimitiveStyleBuilder::new()
35 | .fill_color(Rgb888::BLACK)
36 | .build();
37 | let black_backdrop = Rectangle::new(Point::new(0, 0), Point::new(160, 80)).into_styled(style);
38 | black_backdrop.draw(&mut disp).unwrap();
39 | Triangle::new(
40 | Point::new(8, 66 + 16),
41 | Point::new(8 + 16, 66 + 16),
42 | Point::new(8 + 8, 66),
43 | )
44 | .into_styled(
45 | PrimitiveStyleBuilder::new()
46 | .stroke_color(Rgb888::RED)
47 | .stroke_width(1)
48 | .build(),
49 | )
50 | .draw(&mut disp)
51 | .unwrap();
52 |
53 | psp::screenshot_bmp()
54 | }
55 |
56 | // Useful for generating bmp files for comparison.
57 | fn _write_bmp_helper(screenshot: &[u8]) {
58 | unsafe {
59 | let fd = psp::sys::sceIoOpen(
60 | b"host0:/TEST_OUTPUT.bmp\0" as *const u8,
61 | psp::sys::IoOpenFlags::CREAT | psp::sys::IoOpenFlags::RD_WR,
62 | 0o777,
63 | );
64 | psp::sys::sceIoWrite(
65 | fd,
66 | screenshot as *const _ as *const c_void,
67 | screenshot.len(),
68 | );
69 | psp::sys::sceIoClose(fd);
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/ci/tests/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 |
4 | #[cfg(not(feature = "stub-only"))] extern crate alloc;
5 |
6 | use psp::test_runner::TestRunner;
7 |
8 | mod bmp_screenshot_test;
9 | mod math_test;
10 | mod vram_test;
11 |
12 | psp::module!("ci_tests", 1, 1);
13 |
14 | fn psp_main() {
15 | let tests = &[
16 | bmp_screenshot_test::test_main,
17 | vram_test::test_main,
18 | math_test::test_main,
19 | ];
20 |
21 | let mut runner = TestRunner::new_file_runner();
22 | runner.start_run();
23 |
24 | for test in tests {
25 | runner.run(test);
26 | }
27 |
28 | runner.finish_run();
29 | }
30 |
--------------------------------------------------------------------------------
/ci/tests/src/math_test.rs:
--------------------------------------------------------------------------------
1 | use psp::math;
2 | use psp::test_runner::TestRunner;
3 |
4 | pub fn test_main(test_runner: &mut TestRunner) {
5 | test_runner.check_list(&[
6 | ("cos_2.5", test_cos(2.5), -0.8011436),
7 | ("cos_0", test_cos(0.0), 1.0),
8 | ("cos_pi", test_cos(psp::sys::GU_PI), -1.0),
9 | ]);
10 | }
11 |
12 | fn test_cos(num: f32) -> f32 {
13 | unsafe { math::cosf32(num) }
14 | }
15 |
--------------------------------------------------------------------------------
/ci/tests/src/vram_test.rs:
--------------------------------------------------------------------------------
1 | use core::ptr::null_mut;
2 | use psp::test_runner::TestRunner;
3 | use psp::vram_alloc::get_vram_allocator;
4 |
5 | pub fn test_main(test_runner: &mut TestRunner) {
6 | let mut alloc = get_vram_allocator().unwrap();
7 | test_runner.pass("allocator_initialization", "Received VRAM allocator.");
8 |
9 | let fake_alloc = get_vram_allocator();
10 | match fake_alloc {
11 | Ok(_) => test_runner.fail(
12 | "allocator_doubling_prevention",
13 | "Received second VRAM allocator! Singleton is not working.",
14 | ),
15 | Err(_) => test_runner.pass(
16 | "allocator_doubling_prevention",
17 | "VRAM allocator singleton functional.",
18 | ),
19 | }
20 |
21 | unsafe {
22 | let zero_ptr = null_mut();
23 |
24 | let chunk1 = alloc.alloc_sized::<[u8; 4]>(1);
25 | let chunk2 = alloc.alloc_sized::<[u8; 4]>(1);
26 |
27 | test_runner.check_list(&[
28 | (
29 | "first_chunk_addr_zero",
30 | chunk1.as_mut_ptr_direct_to_vram(),
31 | psp::sys::sceGeEdramGetAddr(),
32 | ),
33 | (
34 | "second_chunk_addr_zero",
35 | chunk2.as_mut_ptr_direct_to_vram(),
36 | psp::sys::sceGeEdramGetAddr().offset(4),
37 | ),
38 | (
39 | "first_chunk_addr_direct",
40 | chunk1.as_mut_ptr_from_zero(),
41 | zero_ptr,
42 | ),
43 | (
44 | "second_chunk_addr_direct",
45 | chunk2.as_mut_ptr_from_zero(),
46 | zero_ptr.offset(4),
47 | ),
48 | ]);
49 |
50 | let muh_item = alloc.move_to_vram([69u8; 16]);
51 |
52 | test_runner.check(
53 | "vram_moved_addr",
54 | muh_item.as_mut_ptr(),
55 | 0x4000008 as *const u8 as _,
56 | );
57 |
58 | test_runner.check("vram_storage_len", muh_item.len(), 16);
59 | test_runner.check("vram_storage_integrity1", muh_item[4], 69);
60 | muh_item[15] = 42;
61 | test_runner.check("vram_storage_integrity2", muh_item[15], 42);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/demo.gif
--------------------------------------------------------------------------------
/examples/clock-speed/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/clock-speed/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-clock-speed-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/clock-speed/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 |
4 | psp::module!("sample_clock_speed", 1, 1);
5 |
6 | fn psp_main() {
7 | psp::enable_home_button();
8 |
9 | unsafe {
10 | let cpu = psp::sys::scePowerGetCpuClockFrequency();
11 | let bus = psp::sys::scePowerGetBusClockFrequency();
12 |
13 | psp::dprintln!("PSP is operating at {}/{}MHz", cpu, bus);
14 | psp::dprintln!("Setting clock speed to maximum...");
15 |
16 | psp::sys::scePowerSetClockFrequency(333, 333, 166);
17 |
18 | let cpu = psp::sys::scePowerGetCpuClockFrequency();
19 | let bus = psp::sys::scePowerGetBusClockFrequency();
20 |
21 | psp::dprintln!("PSP is now operating at {}/{}MHz", cpu, bus);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/cube/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/cube/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-cube-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/cube/ferris.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/examples/cube/ferris.bin
--------------------------------------------------------------------------------
/examples/cube/ferris.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/examples/cube/ferris.png
--------------------------------------------------------------------------------
/examples/cube/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 |
4 | use core::{ptr, f32::consts::PI};
5 | use psp::Align16;
6 | use psp::sys::{
7 | self, ScePspFVector3, DisplayPixelFormat, GuContextType, GuSyncMode, GuSyncBehavior,
8 | GuPrimitive, TextureFilter, TextureEffect, TextureColorComponent,
9 | FrontFaceDirection, ShadingModel, GuState, TexturePixelFormat, DepthFunc,
10 | VertexType, ClearBuffer, MipmapLevel,
11 | };
12 | use psp::vram_alloc::get_vram_allocator;
13 | use psp::{BUF_WIDTH, SCREEN_WIDTH, SCREEN_HEIGHT};
14 |
15 | psp::module!("sample_cube", 1, 1);
16 |
17 | // Both width and height, this is a square image.
18 | const IMAGE_SIZE: usize = 128;
19 |
20 | // The image data *must* be aligned to a 16 byte boundary.
21 | static FERRIS: Align16<[u8; IMAGE_SIZE * IMAGE_SIZE * 4]> = Align16(*include_bytes!("../ferris.bin"));
22 |
23 | static mut LIST: Align16<[u32; 0x40000]> = Align16([0; 0x40000]);
24 |
25 | #[repr(C, align(4))]
26 | struct Vertex {
27 | u: f32,
28 | v: f32,
29 | x: f32,
30 | y: f32,
31 | z: f32,
32 | }
33 |
34 | static VERTICES: Align16<[Vertex; 12 * 3]> = Align16([
35 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: 1.0}, // 0
36 | Vertex { u: 1.0, v: 0.0, x: -1.0, y: 1.0, z: 1.0}, // 4
37 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: 1.0}, // 5
38 |
39 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: 1.0}, // 0
40 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: 1.0}, // 5
41 | Vertex { u: 0.0, v: 1.0, x: 1.0, y: -1.0, z: 1.0}, // 1
42 |
43 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: -1.0}, // 3
44 | Vertex { u: 1.0, v: 0.0, x: 1.0, y: -1.0, z: -1.0}, // 2
45 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: -1.0}, // 6
46 |
47 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: -1.0}, // 3
48 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: -1.0}, // 6
49 | Vertex { u: 0.0, v: 1.0, x: -1.0, y: 1.0, z: -1.0}, // 7
50 |
51 | Vertex { u: 0.0, v: 0.0, x: 1.0, y: -1.0, z: -1.0}, // 0
52 | Vertex { u: 1.0, v: 0.0, x: 1.0, y: -1.0, z: 1.0}, // 3
53 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: 1.0}, // 7
54 |
55 | Vertex { u: 0.0, v: 0.0, x: 1.0, y: -1.0, z: -1.0}, // 0
56 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: 1.0}, // 7
57 | Vertex { u: 0.0, v: 1.0, x: 1.0, y: 1.0, z: -1.0}, // 4
58 |
59 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: -1.0}, // 0
60 | Vertex { u: 1.0, v: 0.0, x: -1.0, y: 1.0, z: -1.0}, // 3
61 | Vertex { u: 1.0, v: 1.0, x: -1.0, y: 1.0, z: 1.0}, // 7
62 |
63 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: -1.0}, // 0
64 | Vertex { u: 1.0, v: 1.0, x: -1.0, y: 1.0, z: 1.0}, // 7
65 | Vertex { u: 0.0, v: 1.0, x: -1.0, y: -1.0, z: 1.0}, // 4
66 |
67 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: 1.0, z: -1.0}, // 0
68 | Vertex { u: 1.0, v: 0.0, x: 1.0, y: 1.0, z: -1.0}, // 1
69 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: 1.0}, // 2
70 |
71 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: 1.0, z: -1.0}, // 0
72 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: 1.0, z: 1.0}, // 2
73 | Vertex { u: 0.0, v: 1.0, x: -1.0, y: 1.0, z: 1.0}, // 3
74 |
75 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: -1.0}, // 4
76 | Vertex { u: 1.0, v: 0.0, x: -1.0, y: -1.0, z: 1.0}, // 7
77 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: -1.0, z: 1.0}, // 6
78 |
79 | Vertex { u: 0.0, v: 0.0, x: -1.0, y: -1.0, z: -1.0}, // 4
80 | Vertex { u: 1.0, v: 1.0, x: 1.0, y: -1.0, z: 1.0}, // 6
81 | Vertex { u: 0.0, v: 1.0, x: 1.0, y: -1.0, z: -1.0}, // 5
82 | ]);
83 |
84 | fn psp_main() {
85 | unsafe { psp_main_inner() }
86 | }
87 |
88 | unsafe fn psp_main_inner() {
89 | psp::enable_home_button();
90 |
91 | let mut allocator = get_vram_allocator().unwrap();
92 | let fbp0 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888).as_mut_ptr_from_zero();
93 | let fbp1 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888).as_mut_ptr_from_zero();
94 | let zbp = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm4444).as_mut_ptr_from_zero();
95 |
96 | sys::sceGumLoadIdentity();
97 |
98 | sys::sceGuInit();
99 |
100 | sys::sceGuStart(GuContextType::Direct, &mut LIST.0 as *mut [u32; 0x40000] as *mut _);
101 | sys::sceGuDrawBuffer(DisplayPixelFormat::Psm8888, fbp0 as _, BUF_WIDTH as i32);
102 | sys::sceGuDispBuffer(SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32, fbp1 as _, BUF_WIDTH as i32);
103 | sys::sceGuDepthBuffer(zbp as _, BUF_WIDTH as i32);
104 | sys::sceGuOffset(2048 - (SCREEN_WIDTH / 2), 2048 - (SCREEN_HEIGHT / 2));
105 | sys::sceGuViewport(2048, 2048, SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32);
106 | sys::sceGuDepthRange(65535, 0);
107 | sys::sceGuScissor(0, 0, SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32);
108 | sys::sceGuEnable(GuState::ScissorTest);
109 | sys::sceGuDepthFunc(DepthFunc::GreaterOrEqual);
110 | sys::sceGuEnable(GuState::DepthTest);
111 | sys::sceGuFrontFace(FrontFaceDirection::Clockwise);
112 | sys::sceGuShadeModel(ShadingModel::Smooth);
113 | sys::sceGuEnable(GuState::CullFace);
114 | sys::sceGuEnable(GuState::Texture2D);
115 | sys::sceGuEnable(GuState::ClipPlanes);
116 | sys::sceGuFinish();
117 | sys::sceGuSync(GuSyncMode::Finish, GuSyncBehavior::Wait);
118 |
119 | psp::sys::sceDisplayWaitVblankStart();
120 |
121 | sys::sceGuDisplay(true);
122 |
123 | // run sample
124 |
125 | let mut val = 0.0;
126 |
127 | loop {
128 | sys::sceGuStart(GuContextType::Direct, &mut LIST.0 as *mut [u32; 0x40000] as *mut _);
129 |
130 | // clear screen
131 | sys::sceGuClearColor(0xff554433);
132 | sys::sceGuClearDepth(0);
133 | sys::sceGuClear(ClearBuffer::COLOR_BUFFER_BIT | ClearBuffer::DEPTH_BUFFER_BIT);
134 |
135 | // setup matrices for cube
136 |
137 | sys::sceGumMatrixMode(sys::MatrixMode::Projection);
138 | sys::sceGumLoadIdentity();
139 | sys::sceGumPerspective(75.0, 16.0 / 9.0, 0.5, 1000.0);
140 |
141 | sys::sceGumMatrixMode(sys::MatrixMode::View);
142 | sys::sceGumLoadIdentity();
143 |
144 | sys::sceGumMatrixMode(sys::MatrixMode::Model);
145 | sys::sceGumLoadIdentity();
146 |
147 | {
148 | let pos = ScePspFVector3 { x: 0.0, y: 0.0, z: -2.5 };
149 | let rot = ScePspFVector3 {
150 | x: val * 0.79 * (PI / 180.0),
151 | y: val * 0.98 * (PI / 180.0),
152 | z: val * 1.32 * (PI / 180.0),
153 | };
154 |
155 | sys::sceGumTranslate(&pos);
156 | sys::sceGumRotateXYZ(&rot);
157 | }
158 |
159 | // setup texture
160 |
161 | sys::sceGuTexMode(TexturePixelFormat::Psm8888, 0, 0, 0);
162 | sys::sceGuTexImage(MipmapLevel::None, 128, 128, 128, &FERRIS as *const _ as *const _);
163 | sys::sceGuTexFunc(TextureEffect::Replace, TextureColorComponent::Rgb);
164 | sys::sceGuTexFilter(TextureFilter::Linear, TextureFilter::Linear);
165 | sys::sceGuTexScale(1.0, 1.0);
166 | sys::sceGuTexOffset(0.0, 0.0);
167 |
168 | // draw cube
169 |
170 | sys::sceGumDrawArray(
171 | GuPrimitive::Triangles,
172 | VertexType::TEXTURE_32BITF | VertexType::VERTEX_32BITF | VertexType::TRANSFORM_3D,
173 | 12 * 3,
174 | ptr::null_mut(),
175 | &VERTICES as *const Align16<_> as *const _,
176 | );
177 |
178 | sys::sceGuFinish();
179 | sys::sceGuSync(GuSyncMode::Finish, GuSyncBehavior::Wait);
180 |
181 | sys::sceDisplayWaitVblankStart();
182 | sys::sceGuSwapBuffers();
183 |
184 | val += 1.0;
185 | }
186 |
187 | // sys::sceGuTerm();
188 | // psp::sys::sceKernelExitGame();
189 | }
190 |
--------------------------------------------------------------------------------
/examples/embedded-graphics/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/embedded-graphics/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-embedded-graphics-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp", features = ["embedded-graphics"] }
9 | embedded-graphics = "0.6.2"
10 | tinybmp = { version = "0.2.2", features = ["graphics"] }
11 |
--------------------------------------------------------------------------------
/examples/embedded-graphics/assets/ferris.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/examples/embedded-graphics/assets/ferris.bmp
--------------------------------------------------------------------------------
/examples/embedded-graphics/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 |
4 | use embedded_graphics::fonts::{Font6x8, Text};
5 | use embedded_graphics::image::Image;
6 | use embedded_graphics::pixelcolor::Rgb888;
7 | use embedded_graphics::prelude::*;
8 | use embedded_graphics::primitives::{circle::Circle, rectangle::Rectangle, triangle::Triangle};
9 | use embedded_graphics::style::PrimitiveStyleBuilder;
10 | use embedded_graphics::style::TextStyleBuilder;
11 | use tinybmp::Bmp;
12 |
13 | use psp::embedded_graphics::Framebuffer;
14 |
15 | psp::module!("sample_emb_gfx", 1, 1);
16 |
17 | fn psp_main() {
18 | psp::enable_home_button();
19 | let mut disp = Framebuffer::new();
20 |
21 | let style = PrimitiveStyleBuilder::new()
22 | .fill_color(Rgb888::BLACK)
23 | .build();
24 | let black_backdrop = Rectangle::new(Point::new(0, 0), Point::new(160, 80)).into_styled(style);
25 | black_backdrop.draw(&mut disp).unwrap();
26 |
27 | // draw ferris
28 | let bmp = Bmp::from_slice(include_bytes!("../assets/ferris.bmp")).unwrap();
29 | let image: Image = Image::new(&bmp, Point::zero());
30 | image.draw(&mut disp).unwrap();
31 |
32 | Triangle::new(
33 | Point::new(8, 66 + 16),
34 | Point::new(8 + 16, 66 + 16),
35 | Point::new(8 + 8, 66),
36 | )
37 | .into_styled(
38 | PrimitiveStyleBuilder::new()
39 | .stroke_color(Rgb888::RED)
40 | .stroke_width(1)
41 | .build(),
42 | )
43 | .draw(&mut disp)
44 | .unwrap();
45 |
46 | Rectangle::new(Point::new(36, 66), Point::new(36 + 16, 66 + 16))
47 | .into_styled(
48 | PrimitiveStyleBuilder::new()
49 | .stroke_color(Rgb888::GREEN)
50 | .stroke_width(1)
51 | .build(),
52 | )
53 | .draw(&mut disp)
54 | .unwrap();
55 |
56 | Circle::new(Point::new(72, 66 + 8), 8)
57 | .into_styled(
58 | PrimitiveStyleBuilder::new()
59 | .stroke_color(Rgb888::BLUE)
60 | .stroke_width(1)
61 | .build(),
62 | )
63 | .draw(&mut disp)
64 | .unwrap();
65 |
66 | let rust = Rgb888::new(0xff, 0x07, 0x00);
67 | Text::new("Hello Rust!", Point::new(0, 86))
68 | .into_styled(TextStyleBuilder::new(Font6x8).text_color(rust).build())
69 | .draw(&mut disp)
70 | .unwrap();
71 | }
72 |
--------------------------------------------------------------------------------
/examples/gu-background/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/gu-background/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-gu-background-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/gu-background/src/main.rs:
--------------------------------------------------------------------------------
1 | //! A basic graphics example that only clears the screen.
2 |
3 | #![no_std]
4 | #![no_main]
5 |
6 | use core::ffi::c_void;
7 | use psp::sys::{self, GuState, TexturePixelFormat, DisplayPixelFormat};
8 | use psp::vram_alloc::get_vram_allocator;
9 | use psp::{BUF_WIDTH, SCREEN_WIDTH, SCREEN_HEIGHT};
10 |
11 | psp::module!("sample_gu_background", 1, 1);
12 |
13 | static mut LIST: psp::Align16<[u32; 0x40000]> = psp::Align16([0; 0x40000]);
14 |
15 | fn psp_main() {
16 | psp::enable_home_button();
17 |
18 | let mut allocator = get_vram_allocator().unwrap();
19 | let fbp0 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888).as_mut_ptr_from_zero();
20 | let fbp1 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888).as_mut_ptr_from_zero();
21 | let zbp = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm4444).as_mut_ptr_from_zero();
22 |
23 | unsafe {
24 |
25 | sys::sceGuInit();
26 | sys::sceGuStart(
27 | sys::GuContextType::Direct,
28 | &mut LIST as *mut _ as *mut c_void,
29 | );
30 | sys::sceGuDrawBuffer(DisplayPixelFormat::Psm8888, fbp0 as _, BUF_WIDTH as i32);
31 | sys::sceGuDispBuffer(SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32, fbp1 as _, BUF_WIDTH as i32);
32 | sys::sceGuDepthBuffer(zbp as _, BUF_WIDTH as i32);
33 | sys::sceGuOffset(2048 - (SCREEN_WIDTH/2), 2048 - (SCREEN_HEIGHT/2));
34 | sys::sceGuViewport(2048, 2048, SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32);
35 | sys::sceGuDepthRange(65535, 0);
36 | sys::sceGuScissor(0, 0, SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32);
37 | sys::sceGuEnable(GuState::ScissorTest);
38 | sys::sceGuFinish();
39 | sys::sceGuSync(sys::GuSyncMode::Finish, sys::GuSyncBehavior::Wait);
40 | sys::sceDisplayWaitVblankStart();
41 | sys::sceGuDisplay(true);
42 |
43 | loop {
44 | sys::sceGuStart(
45 | sys::GuContextType::Direct,
46 | &mut LIST as *mut _ as *mut c_void
47 | );
48 | sys::sceGuClearColor(0xff554433);
49 | sys::sceGuClearDepth(0);
50 | sys::sceGuClear(
51 | sys::ClearBuffer::COLOR_BUFFER_BIT |
52 | sys::ClearBuffer::DEPTH_BUFFER_BIT
53 | );
54 | sys::sceGuFinish();
55 | sys::sceGuSync(sys::GuSyncMode::Finish, sys::GuSyncBehavior::Wait);
56 | sys::sceDisplayWaitVblankStart();
57 | sys::sceGuSwapBuffers();
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/examples/gu-debug-print/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/gu-debug-print/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-gu-debug-example"
3 | version = "0.1.0"
4 | authors = ["Paul Sajna "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/gu-debug-print/src/main.rs:
--------------------------------------------------------------------------------
1 | //! A basic example of sceGuDebugPrint functionality
2 | //! Prints "Hello World" in red at 100, 100
3 |
4 | #![no_std]
5 | #![no_main]
6 |
7 | use core::ffi::c_void;
8 | use psp::sys::{self, TexturePixelFormat, DisplayPixelFormat};
9 | use psp::vram_alloc::get_vram_allocator;
10 | use psp::{BUF_WIDTH, SCREEN_HEIGHT};
11 |
12 | psp::module!("sample_gu_debug", 1, 1);
13 |
14 | static mut LIST: psp::Align16<[u32; 0x40000]> = psp::Align16([0; 0x40000]);
15 |
16 | fn psp_main() {
17 | psp::enable_home_button();
18 |
19 | let mut allocator = get_vram_allocator().unwrap();
20 | let fbp0 = allocator.alloc_texture_pixels(BUF_WIDTH, SCREEN_HEIGHT, TexturePixelFormat::Psm8888).as_mut_ptr_from_zero();
21 |
22 | unsafe {
23 | sys::sceGuInit();
24 | sys::sceGuStart(
25 | sys::GuContextType::Direct,
26 | &mut LIST as *mut _ as *mut c_void,
27 | );
28 | sys::sceGuDrawBuffer(DisplayPixelFormat::Psm8888, fbp0 as _, BUF_WIDTH as i32);
29 | sys::sceGuDebugPrint(100, 100, 0xff0000ff, b"Hello World\0" as *const u8);
30 | sys::sceGuDebugFlush();
31 |
32 | sys::sceGuFinish();
33 | sys::sceGuSync(sys::GuSyncMode::Finish, sys::GuSyncBehavior::Wait);
34 | sys::sceDisplayWaitVblankStart();
35 | sys::sceGuDisplay(true);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/examples/hello-world/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/hello-world/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-hello-world-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/hello-world/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 |
4 | psp::module!("sample_module", 1, 1);
5 |
6 | fn psp_main() {
7 | psp::enable_home_button();
8 | psp::dprint!("Hello PSP from rust!");
9 | }
10 |
--------------------------------------------------------------------------------
/examples/msg-dialog/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/msg-dialog/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-msg-dialog"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/msg-dialog/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 |
4 | use psp::sys::{
5 | UtilityDialogCommon, UtilityMsgDialogParams, UtilityMsgDialogMode,
6 | UtilityMsgDialogPressed, SystemParamLanguage, UtilityDialogButtonAccept,
7 | UtilityMsgDialogOption, self,
8 | DisplayPixelFormat, GuContextType, GuState, DepthFunc, FrontFaceDirection,
9 | ShadingModel, GuSyncMode, GuSyncBehavior
10 | };
11 |
12 | use core::ffi::c_void;
13 |
14 | psp::module!("sample_module", 1, 1);
15 |
16 | static mut LIST: psp::Align16<[u32; 262144]> = psp::Align16([0;262144]);
17 | const SCR_WIDTH: i32 = 480;
18 | const SCR_HEIGHT: i32 = 272;
19 | const BUF_WIDTH: i32 = 512;
20 |
21 | unsafe fn setup_gu() {
22 | sys::sceGuInit();
23 | sys::sceGuStart(GuContextType::Direct, &mut LIST as *mut _ as *mut c_void);
24 | sys::sceGuDrawBuffer(DisplayPixelFormat::Psm8888, core::ptr::null_mut(), BUF_WIDTH);
25 | sys::sceGuDispBuffer(SCR_WIDTH, SCR_HEIGHT, 0x88000 as *mut c_void, BUF_WIDTH);
26 | sys::sceGuDepthBuffer(0x110000 as *mut c_void, BUF_WIDTH);
27 | sys::sceGuOffset(2048 - (SCR_WIDTH as u32 /2), 2048 - (SCR_HEIGHT as u32 /2));
28 | sys::sceGuViewport(2048, 2048, SCR_WIDTH, SCR_HEIGHT);
29 | sys::sceGuDepthRange(0xc350, 0x2710);
30 | sys::sceGuScissor(0, 0, SCR_WIDTH, SCR_HEIGHT);
31 | sys::sceGuEnable(GuState::ScissorTest);
32 | sys::sceGuDepthFunc(DepthFunc::GreaterOrEqual);
33 | sys::sceGuEnable(GuState::DepthTest);
34 | sys::sceGuFrontFace(FrontFaceDirection::Clockwise);
35 | sys::sceGuShadeModel(ShadingModel::Smooth);
36 | sys::sceGuEnable(GuState::CullFace);
37 | sys::sceGuEnable(GuState::ClipPlanes);
38 | sys::sceGuFinish();
39 | sys::sceGuSync(GuSyncMode::Finish, GuSyncBehavior::Wait);
40 |
41 | sys::sceDisplayWaitVblankStart();
42 | sys::sceGuDisplay(true);
43 | }
44 |
45 | fn psp_main() {
46 | psp::enable_home_button();
47 |
48 | unsafe {
49 | setup_gu();
50 | }
51 |
52 | let dialog_size = core::mem::size_of::();
53 | let base = UtilityDialogCommon {
54 | size: dialog_size as u32,
55 | language: SystemParamLanguage::English,
56 | button_accept: UtilityDialogButtonAccept::Cross, // X to accept
57 | graphics_thread: 0x11, // magic number stolen from pspsdk example
58 | access_thread: 0x13,
59 | font_thread: 0x12,
60 | sound_thread: 0x10,
61 | result: 0,
62 | reserved: [0i32; 4],
63 | };
64 |
65 | let mut msg: [u8; 512] = [0u8; 512];
66 | msg[..40].copy_from_slice(b"Hello from a Rust-created PSP Msg Dialog");
67 |
68 | let mut msg_dialog = UtilityMsgDialogParams {
69 | base,
70 | unknown: 0,
71 | mode: UtilityMsgDialogMode::Text,
72 | error_value: 0,
73 | message: msg,
74 | options: UtilityMsgDialogOption::TEXT,
75 | button_pressed: UtilityMsgDialogPressed::Unknown1,
76 | };
77 |
78 | unsafe {
79 | sys::sceUtilityMsgDialogInitStart(
80 | &mut msg_dialog as *mut UtilityMsgDialogParams
81 | );
82 | }
83 |
84 | loop {
85 | let status = unsafe {sys::sceUtilityMsgDialogGetStatus()};
86 | match status {
87 | 2 => unsafe{sys::sceUtilityMsgDialogUpdate(1)},
88 | 3 => unsafe{sys::sceUtilityMsgDialogShutdownStart()},
89 | 0 => {break},
90 | _ => (),
91 | }
92 | unsafe {
93 | sys::sceGuStart(GuContextType::Direct, &mut LIST as *mut _ as *mut c_void);
94 | sys::sceGuFinish();
95 | sys::sceGuSync(GuSyncMode::Finish, sys::GuSyncBehavior::Wait);
96 | sys::sceDisplayWaitVblankStart();
97 | sys::sceGuSwapBuffers();
98 | }
99 | }
100 | unsafe { sys::sceKernelExitGame(); }
101 | }
102 |
--------------------------------------------------------------------------------
/examples/paint-mode/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-paint-mode"
3 | version = "0.1.0"
4 | authors = ["Glenn Hope "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp", features = ["embedded-graphics"] }
9 | embedded-graphics = "0.6.2"
10 | numtoa = "0.2"
11 |
--------------------------------------------------------------------------------
/examples/paint-mode/Psp.toml:
--------------------------------------------------------------------------------
1 | title = "Paint Mode"
2 |
--------------------------------------------------------------------------------
/examples/paint-mode/src/analog_stick_to_delta.rs:
--------------------------------------------------------------------------------
1 | // Number of "pixels" (out of 127 in each direction)
2 | // to ignore in the center of the analog stick, because
3 | // it's very common to have the stick rest slighly off-center.
4 | const DEADZONE: i32 = 10;
5 |
6 | // The maximum number of pixels to move in a single tick,
7 | // essentially the "mouse sensitivity" of the analog stick.
8 | const MAX_SPEED: i32 = 4;
9 |
10 | // Carve a number of rings around our deadzone equal to MAX_SPEED.
11 | const SPEED_MODIFIER: i32 = 127 / MAX_SPEED;
12 |
13 | // Convert the analog stick position to a number of pixels to move
14 | // in the direction it is being held.
15 | //
16 | // First, we need to treat 127, 127 as 0,0 on a Cartesian plane.
17 | // We receive coordinates from the PSP control stick like this:
18 | //
19 | // +--------------------+
20 | // |0,0 255,0|
21 | // | |
22 | // | |
23 | // | |
24 | // | 127,127 |
25 | // | |
26 | // | |
27 | // | |
28 | // |0,255 255,255|
29 | // +--------------------+
30 | //
31 | // So we subtract 127 to make our values look like this:
32 | //
33 | // +--------------------+
34 | // |-127,-127 127,-127|
35 | // | |
36 | // | |
37 | // | |
38 | // | 0,0 |
39 | // | |
40 | // | |
41 | // | |
42 | // |-127,127 127,127|
43 | // +--------------------+
44 | //
45 | // Then, we carve out a "deadzone" around 0,0 where inputs are equal to zero.
46 | // So, for a DEADZONE of 8, we would have:
47 | //
48 | // +--------------------+
49 | // | |
50 | // | |
51 | // | -8,-8 8,-8 |
52 | // | +------+ |
53 | // | | | |
54 | // | | | |
55 | // | +------+ |
56 | // | -8,8 8,8 |
57 | // | |
58 | // | |
59 | // +--------------------+
60 | //
61 | // Now, we use MAX_SPEED as the number of "rings" around 0,0.
62 | // So for a MAX_SPEED value of 3, this would look like:
63 | //
64 | // For Y: - For X: +
65 | // +--------------------+ +--------------------+
66 | // |33333333333333333333| |33221100000000112233|
67 | // -|22222222222222222222| |33221100000000112233|
68 | // |11111111111111111111| |33221100000000112233|
69 | // |000000+------+000000| |332211+------+112233|
70 | // |000000| |000000| |332211| |112233|
71 | // |000000| |000000| |332211| |112233|
72 | // |000000+------+000000| |332211+------+112233|
73 | // |11111111111111111111| |33221100000000112233|
74 | // +|22222222222222222222| |33221100000000112233|
75 | // |33333333333333333333| |33221100000000112233|
76 | // +--------------------+ +--------------------+
77 | //
78 | // And for a MAX_SPEED of 6, something like this:
79 | //
80 | // For Y: - For X: +
81 | // +--------------------+ +--------------------+
82 | // |66666666666666666666| |65432100000000123456|
83 | // -|44444444444444444444| |65432100000000123456|
84 | // |22222222222222222222| |65432100000000123456|
85 | // |000000+------+000000| |654321+------+123456|
86 | // |000000| |000000| |654321| |123456|
87 | // |000000| |000000| |654321| |123456|
88 | // |000000+------+000000| |654321+------+123456|
89 | // |22222222222222222222| |65432100000000123456|
90 | // +|44444444444444444444| |65432100000000123456|
91 | // |66666666666666666666| |65432100000000123456|
92 | // +--------------------+ +--------------------+
93 | //
94 | // Or, visualized differently for a MAX_SPEED of 8:
95 | //
96 | // +--------------------+
97 | // | |
98 | // | |
99 | // | 0,-1 |
100 | // | +------+ |
101 | // | | 0,0 |1,0 |
102 | // | | | |
103 | // | +------+ |
104 | // | |
105 | // | -4,4 |
106 | // | 8,8|
107 | // +--------------------+
108 | //
109 | pub fn convert_analog_to_delta_with_sensitivity_deadzone(raw_val: u8) -> i32 {
110 | let delta_val = (raw_val as i32) - 127;
111 |
112 | // Zero out a "deadzone" around 0,0 to adjust for joysticks that sit off-center.
113 | let distance_without_deadzone = if delta_val > -DEADZONE && delta_val < DEADZONE {
114 | 0
115 | } else if delta_val < -DEADZONE {
116 | delta_val + DEADZONE
117 | } else {
118 | delta_val - DEADZONE
119 | };
120 |
121 | distance_without_deadzone / SPEED_MODIFIER
122 | }
123 |
--------------------------------------------------------------------------------
/examples/paint-mode/src/background.rs:
--------------------------------------------------------------------------------
1 | use embedded_graphics::{
2 | pixelcolor::Rgb888,
3 | prelude::*,
4 | primitives::rectangle::Rectangle,
5 | style::{PrimitiveStyle, PrimitiveStyleBuilder, Styled},
6 | };
7 |
8 | use psp::{SCREEN_HEIGHT, SCREEN_WIDTH};
9 |
10 | pub fn get_background() -> Styled> {
11 | let style = PrimitiveStyleBuilder::new()
12 | .fill_color(Rgb888::BLACK)
13 | .build();
14 | Rectangle::new(
15 | Point::new(0, 0),
16 | Point::new(SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32),
17 | )
18 | .into_styled(style)
19 | }
20 |
--------------------------------------------------------------------------------
/examples/paint-mode/src/debug_textbox.rs:
--------------------------------------------------------------------------------
1 | use core::str;
2 | use numtoa::NumToA;
3 |
4 | use embedded_graphics::{
5 | fonts::{Font6x8, Text},
6 | pixelcolor::Rgb888,
7 | prelude::*,
8 | primitives::rectangle::Rectangle,
9 | style::{PrimitiveStyle, PrimitiveStyleBuilder, Styled, TextStyle},
10 | };
11 |
12 | use psp::embedded_graphics::Framebuffer;
13 | use psp::sys::SceCtrlData;
14 | use psp::{SCREEN_HEIGHT, SCREEN_WIDTH};
15 | pub fn get_textbox<'a>() -> Styled, TextStyle> {
16 | Text::new("", get_textbox_top_left()).into_styled(TextStyle::new(Font6x8, Rgb888::WHITE))
17 | }
18 |
19 | fn get_textbox_top_left() -> Point {
20 | Point::new(SCREEN_WIDTH as i32 - 42, SCREEN_HEIGHT as i32 - 8)
21 | }
22 |
23 | fn get_textbox_wipe_rect() -> Styled> {
24 | let style = PrimitiveStyleBuilder::new()
25 | .fill_color(Rgb888::BLACK)
26 | .build();
27 | Rectangle::new(
28 | get_textbox_top_left(),
29 | Point::new(SCREEN_WIDTH as i32, SCREEN_HEIGHT as i32),
30 | )
31 | .into_styled(style)
32 | }
33 |
34 | pub fn draw_debug_textbox(disp: &mut Framebuffer, pad_data: &SceCtrlData) {
35 | // Create a str holding our analog pad X and Y values
36 | let mut holder = [' ' as u8; 7];
37 | holder[3] = ':' as u8;
38 | pad_data.lx.numtoa(10, &mut holder[..3]);
39 | pad_data.ly.numtoa(10, &mut holder[4..]);
40 |
41 | let pad_debug_data_str = unsafe {
42 | // We can be extremely sure that our array holds nothing
43 | // but ASCII values, so we can safely skip UTF-8 checks.
44 | str::from_utf8_unchecked(&holder)
45 | };
46 |
47 | // Instantiate our textboxes
48 | let textbox_wipe = get_textbox_wipe_rect();
49 | let mut textbox = get_textbox();
50 | textbox.primitive.text = pad_debug_data_str;
51 |
52 | // Actually clear and redraw the textbox on screen
53 | textbox_wipe.draw(disp).unwrap();
54 | textbox.draw(disp).unwrap();
55 | }
56 |
--------------------------------------------------------------------------------
/examples/paint-mode/src/drawobject.rs:
--------------------------------------------------------------------------------
1 | use embedded_graphics::{
2 | pixelcolor::Rgb888,
3 | prelude::*,
4 | primitives::{circle::Circle, line::Line, rectangle::Rectangle, triangle::Triangle},
5 | style::{PrimitiveStyle, PrimitiveStyleBuilder, Styled},
6 | };
7 |
8 | use psp::embedded_graphics::Framebuffer;
9 |
10 | type StyledRect = Styled>;
11 | type StyledCirc = Styled>;
12 | type StyledTri = Styled>;
13 | type StyledX = [Styled>; 2];
14 |
15 | pub enum DrawObject {
16 | Circ(StyledCirc),
17 | Rect(StyledRect),
18 | Tri(StyledTri),
19 | X(StyledX),
20 | }
21 |
22 | impl DrawObject {
23 | pub fn size(&self) -> u32 {
24 | match self {
25 | Self::Circ(circ) => circ.primitive.radius,
26 | Self::Tri(tri) => tri.primitive.size().height / 2,
27 | Self::Rect(rect) => rect.primitive.size().height / 2,
28 | Self::X(x) => x[0].primitive.size().height / 2,
29 | }
30 | }
31 |
32 | pub fn draw(&self, disp: &mut Framebuffer) {
33 | match self {
34 | Self::Circ(circ) => circ.draw(disp).unwrap(),
35 | Self::Tri(tri) => tri.draw(disp).unwrap(),
36 | Self::Rect(rect) => rect.draw(disp).unwrap(),
37 | Self::X(x) => {
38 | for line in x {
39 | line.draw(disp).unwrap();
40 | }
41 | }
42 | };
43 | }
44 |
45 | pub fn translate_mut(&mut self, point: Point) {
46 | match self {
47 | Self::Circ(circ) => {
48 | circ.translate_mut(point);
49 | }
50 | Self::Tri(tri) => {
51 | tri.translate_mut(point);
52 | }
53 | Self::Rect(rect) => {
54 | rect.translate_mut(point);
55 | }
56 | Self::X(x) => {
57 | for line in x {
58 | line.translate_mut(point);
59 | }
60 | }
61 | };
62 | }
63 |
64 | pub fn grow(&mut self) {
65 | let size = self.size();
66 | if size < 31 {
67 | match self {
68 | Self::Circ(circ) => circ.primitive.radius += 1,
69 | Self::Tri(tri) => grow_triangle(tri),
70 | Self::Rect(rect) => grow_rectangle(rect),
71 | Self::X(x) => grow_x(x),
72 | }
73 | }
74 | }
75 |
76 | pub fn shrink(&mut self) {
77 | let size = self.size();
78 | if size > 2 {
79 | match self {
80 | Self::Circ(circ) => circ.primitive.radius -= 1,
81 | Self::Tri(tri) => shrink_triangle(tri),
82 | Self::Rect(rect) => shrink_rectangle(rect),
83 | Self::X(x) => shrink_x(x),
84 | }
85 | }
86 | }
87 |
88 | pub fn center(&self) -> Point {
89 | match self {
90 | Self::Circ(circ) => circ.primitive.center,
91 | Self::Tri(tri) => Point::new(
92 | tri.primitive.p1.x,
93 | (tri.primitive.p1.y + tri.primitive.p2.y) / 2,
94 | ),
95 | Self::Rect(rect) => Point::new(
96 | (rect.primitive.top_left.x + rect.primitive.bottom_right.x) / 2,
97 | (rect.primitive.top_left.y + rect.primitive.bottom_right.y) / 2,
98 | ),
99 |
100 | Self::X(x) => Point::new(
101 | (x[0].primitive.start.x + x[0].primitive.end.x) / 2,
102 | (x[0].primitive.start.y + x[0].primitive.end.y) / 2,
103 | ),
104 | }
105 | }
106 |
107 | pub fn move_by(&mut self, delta_x_pixels: i32, delta_y_pixels: i32, max_x: i32, max_y: i32) {
108 | let existing_center = self.center();
109 | let requested_delta = Point::new(delta_x_pixels, delta_y_pixels);
110 | let mut target_location: Point = existing_center + requested_delta;
111 | target_location.x = target_location.x.clamp(0, max_x);
112 | target_location.y = target_location.y.clamp(0, max_y);
113 | let actual_delta = target_location - existing_center;
114 | self.translate_mut(actual_delta);
115 | }
116 |
117 | pub fn new_circle(point: Point, size: u32) -> Self {
118 | Self::Circ(
119 | Circle::new(point, size).into_styled(
120 | PrimitiveStyleBuilder::new()
121 | .stroke_color(Rgb888::RED)
122 | .stroke_width(1)
123 | .build(),
124 | ),
125 | )
126 | }
127 |
128 | pub fn new_triangle(point: Point, size: u32) -> Self {
129 | let mut tri = Triangle::new(point, point, point).into_styled(
130 | PrimitiveStyleBuilder::new()
131 | .stroke_color(Rgb888::GREEN)
132 | .stroke_width(1)
133 | .build(),
134 | );
135 | grow_triangle_by(&mut tri, size as _);
136 | Self::Tri(tri)
137 | }
138 |
139 | pub fn new_rectangle(point: Point, size: u32) -> Self {
140 | let mut rect = Rectangle::new(point, point).into_styled(
141 | PrimitiveStyleBuilder::new()
142 | .stroke_color(Rgb888::MAGENTA)
143 | .stroke_width(1)
144 | .build(),
145 | );
146 | grow_rectangle_by(&mut rect, size as _);
147 | Self::Rect(rect)
148 | }
149 |
150 | pub fn new_x(point: Point, size: u32) -> Self {
151 | let line_1 = Line::new(point, point).into_styled(
152 | PrimitiveStyleBuilder::new()
153 | .stroke_color(Rgb888::BLUE)
154 | .stroke_width(1)
155 | .build(),
156 | );
157 | let line_2 = line_1.clone();
158 | let mut x = [line_1, line_2];
159 | grow_x_by(&mut x, size as _);
160 | Self::X(x)
161 | }
162 | }
163 |
164 | fn grow_triangle(styled_tri: &mut StyledTri) {
165 | grow_triangle_by(styled_tri, 1)
166 | }
167 |
168 | fn shrink_triangle(styled_tri: &mut StyledTri) {
169 | grow_triangle_by(styled_tri, -1)
170 | }
171 |
172 | fn grow_triangle_by(styled_tri: &mut StyledTri, grow_by: i32) {
173 | let mut tri = &mut styled_tri.primitive;
174 | tri.p1.y -= grow_by;
175 |
176 | tri.p2.x -= grow_by;
177 | tri.p2.y += grow_by;
178 |
179 | tri.p3.x += grow_by;
180 | tri.p3.y += grow_by;
181 | }
182 |
183 | fn grow_rectangle(styled_rect: &mut StyledRect) {
184 | grow_rectangle_by(styled_rect, 1)
185 | }
186 |
187 | fn shrink_rectangle(styled_rect: &mut StyledRect) {
188 | grow_rectangle_by(styled_rect, -1)
189 | }
190 |
191 | fn grow_rectangle_by(styled_rect: &mut StyledRect, grow_by: i32) {
192 | let mut rect = &mut styled_rect.primitive;
193 | rect.top_left.x -= grow_by;
194 | rect.top_left.y -= grow_by;
195 |
196 | rect.bottom_right.x += grow_by;
197 | rect.bottom_right.y += grow_by;
198 | }
199 |
200 | fn grow_x(styled_x: &mut StyledX) {
201 | grow_x_by(styled_x, 1)
202 | }
203 |
204 | fn shrink_x(styled_x: &mut StyledX) {
205 | grow_x_by(styled_x, -1)
206 | }
207 |
208 | fn grow_x_by(styled_x: &mut StyledX, grow_by: i32) {
209 | let mut line_1 = &mut styled_x[0].primitive;
210 | line_1.start.x -= grow_by;
211 | line_1.start.y -= grow_by;
212 | line_1.end.x += grow_by;
213 | line_1.end.y += grow_by;
214 |
215 | let mut line_2 = &mut styled_x[1].primitive;
216 | line_2.start.x -= grow_by;
217 | line_2.start.y += grow_by;
218 | line_2.end.x += grow_by;
219 | line_2.end.y -= grow_by;
220 | }
221 |
--------------------------------------------------------------------------------
/examples/paint-mode/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![feature(clamp)]
3 |
4 | pub mod drawobject;
5 | pub use drawobject::DrawObject;
6 |
7 | pub mod debug_textbox;
8 | pub use debug_textbox::draw_debug_textbox;
9 |
10 | pub mod background;
11 | pub use background::get_background;
12 |
13 | pub mod analog_stick_to_delta;
14 | pub use analog_stick_to_delta::convert_analog_to_delta_with_sensitivity_deadzone;
15 |
--------------------------------------------------------------------------------
/examples/paint-mode/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 | #![feature(slice_fill)]
4 | #![feature(exclusive_range_pattern)]
5 | #![feature(half_open_range_patterns)]
6 |
7 | use psp_paint_mode::{
8 | convert_analog_to_delta_with_sensitivity_deadzone, draw_debug_textbox, get_background,
9 | DrawObject,
10 | };
11 |
12 | use embedded_graphics::prelude::*;
13 |
14 | use psp::embedded_graphics::Framebuffer;
15 | use psp::sys::{CtrlButtons, CtrlMode, SceCtrlData};
16 | use psp::{SCREEN_HEIGHT, SCREEN_WIDTH};
17 |
18 | psp::module!("Paint Mode Example", 0, 1);
19 |
20 | fn psp_main() {
21 | psp::enable_home_button();
22 |
23 | let disp = &mut Framebuffer::new();
24 | let background = get_background();
25 | let mut cur_size = 1;
26 | let mut draw_obj = DrawObject::new_circle(get_midpoint(), cur_size);
27 | let mut cur_location = draw_obj.center();
28 |
29 | let mut i = 0;
30 |
31 | background.draw(disp).unwrap();
32 |
33 | unsafe {
34 | psp::sys::sceCtrlSetSamplingCycle(0);
35 | psp::sys::sceCtrlSetSamplingMode(CtrlMode::Analog);
36 | };
37 |
38 | let pad_data = &mut SceCtrlData::default();
39 | loop {
40 | unsafe {
41 | // Read button/analog input
42 | psp::sys::sceCtrlReadBufferPositive(pad_data, 1);
43 | }
44 |
45 | if pad_data.buttons.contains(CtrlButtons::START) {
46 | // Wipe the screen
47 | background.draw(disp).unwrap();
48 | }
49 |
50 | if pad_data.buttons.contains(CtrlButtons::RTRIGGER) {
51 | draw_obj.grow();
52 | }
53 |
54 | if pad_data.buttons.contains(CtrlButtons::LTRIGGER) {
55 | draw_obj.shrink();
56 | }
57 |
58 | if pad_data.buttons.contains(CtrlButtons::CIRCLE) {
59 | draw_obj = DrawObject::new_circle(cur_location, cur_size);
60 | }
61 |
62 | if pad_data.buttons.contains(CtrlButtons::TRIANGLE) {
63 | draw_obj = DrawObject::new_triangle(cur_location, cur_size);
64 | }
65 |
66 | if pad_data.buttons.contains(CtrlButtons::SQUARE) {
67 | draw_obj = DrawObject::new_rectangle(cur_location, cur_size);
68 | }
69 |
70 | if pad_data.buttons.contains(CtrlButtons::CROSS) {
71 | draw_obj = DrawObject::new_x(cur_location, cur_size);
72 | }
73 |
74 | let delta_x_pixels = convert_analog_to_delta_with_sensitivity_deadzone(pad_data.lx);
75 | let delta_y_pixels = convert_analog_to_delta_with_sensitivity_deadzone(pad_data.ly);
76 | draw_obj.move_by(
77 | delta_x_pixels,
78 | delta_y_pixels,
79 | SCREEN_WIDTH as i32,
80 | SCREEN_HEIGHT as i32,
81 | );
82 | draw_obj.draw(disp);
83 |
84 | if i < 10 {
85 | i += 1;
86 | } else {
87 | draw_debug_textbox(disp, pad_data);
88 | i = 0;
89 | }
90 |
91 | cur_location = draw_obj.center();
92 | cur_size = draw_obj.size();
93 | }
94 | }
95 |
96 | fn get_midpoint() -> Point {
97 | Point::new(SCREEN_WIDTH as i32 / 2, SCREEN_HEIGHT as i32 / 2)
98 | }
99 |
--------------------------------------------------------------------------------
/examples/rainbow/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/rainbow/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-rainbow-example"
3 | version = "0.1.0"
4 | authors = [
5 | "Paul Sajna ",
6 | "Marko Mijalkovic "
7 | ]
8 | edition = "2018"
9 |
10 | [dependencies]
11 | psp = { path = "../../psp" }
12 |
--------------------------------------------------------------------------------
/examples/rainbow/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 |
4 | use psp::sys;
5 | use psp::{SCREEN_WIDTH, SCREEN_HEIGHT, BUF_WIDTH};
6 |
7 | psp::module!("sample_module", 1, 1);
8 |
9 |
10 | static mut VRAM: *mut u32 = 0x4000_0000 as *mut u32;
11 |
12 | fn psp_main() {
13 | psp::enable_home_button();
14 | unsafe {
15 | sys::sceDisplaySetMode(sys::DisplayMode::Lcd, SCREEN_WIDTH as usize, SCREEN_HEIGHT as usize);
16 |
17 | // Cache-through address
18 | VRAM = (0x4000_0000u32 | sys::sceGeEdramGetAddr() as u32) as *mut u32;
19 |
20 | sys::sceDisplaySetFrameBuf(
21 | VRAM as *const u8,
22 | BUF_WIDTH as usize,
23 | sys::DisplayPixelFormat::Psm8888,
24 | sys::DisplaySetBufSync::NextFrame,
25 | );
26 |
27 | loop {
28 | sys::sceDisplayWaitVblankStart();
29 | for pos in 0..255 {
30 | let color = wheel(pos);
31 |
32 | for i in 0..(BUF_WIDTH * SCREEN_HEIGHT) {
33 | *VRAM.add(i as usize) = color;
34 | }
35 | }
36 | }
37 | }
38 | }
39 |
40 | fn wheel(mut pos: u8) -> u32 {
41 | pos = 255 - pos;
42 | if pos < 85 {
43 | u32::from_be_bytes([255 - pos * 3, 0, pos * 3, 255])
44 | } else if pos < 170 {
45 | pos -= 85;
46 | u32::from_be_bytes([0, pos * 3, 255 - pos * 3, 255])
47 | } else {
48 | pos -= 170;
49 | u32::from_be_bytes([pos * 3, 255 - pos * 3, 0, 255])
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/examples/rust-std-hello-world/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "rust-std-hello-world"
3 | version = "0.1.0"
4 | authors = ["Glenn Hope "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp", features = ["std"] }
9 |
--------------------------------------------------------------------------------
/examples/rust-std-hello-world/src/main.rs:
--------------------------------------------------------------------------------
1 | #![feature(restricted_std)]
2 | #![no_main]
3 | use std::string::String;
4 |
5 | psp::module!("rust_std_hello_world", 1, 1);
6 |
7 | fn psp_main() {
8 | psp::enable_home_button();
9 |
10 | let yeet = String::from("Yeeteth! I am inside a String!");
11 | psp::dprintln!("{}", yeet);
12 |
13 | let people = vec!["sajattack", "overdrivenpotato", "iridescence"];
14 | for person in people {
15 | let x = format!(
16 | "Hello, {}! I'm coming to you live from the standard library!\n",
17 | person
18 | );
19 | psp::dprint!("{}", x);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/time/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/time/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-time-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/time/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_main]
2 | #![no_std]
3 |
4 | use core::mem::MaybeUninit;
5 |
6 | psp::module!("sample_time", 1, 1);
7 |
8 | fn psp_main() {
9 | psp::enable_home_button();
10 |
11 | unsafe {
12 | let mut tick = 0;
13 | psp::sys::sceRtcGetCurrentTick(&mut tick);
14 |
15 | // Convert the tick to an instance of `ScePspDateTime`
16 | let mut date = MaybeUninit::uninit();
17 | psp::sys::sceRtcSetTick(date.as_mut_ptr(), &tick);
18 | let date = date.assume_init();
19 |
20 | psp::dprintln!(
21 | "Current time is {:02}:{:02}:{:02} UTC",
22 | date.hour,
23 | date.minutes,
24 | date.seconds
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/examples/vfpu-addition/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/vfpu-addition/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-vfpu-addition-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/vfpu-addition/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 | #![feature(llvm_asm)]
4 |
5 | psp::module!("vfpu_test", 1, 1);
6 |
7 | fn vfpu_add(a: i32, b: i32) -> i32 {
8 | let out;
9 |
10 | unsafe {
11 | psp::vfpu_asm! (
12 | // Convert `a` to float
13 | .mips "mtc1 $$a0, $3";
14 | .mips "cvt.s.w $3, $3";
15 | .mips "mfc1 $$a0, $3";
16 |
17 | // Convert `b` to float
18 | .mips "mtc1 $$a1, $3";
19 | .mips "cvt.s.w $3, $3";
20 | .mips "mfc1 $$a1, $3";
21 |
22 | // Perform addition
23 | mtv a0, S000;
24 | mtv a1, S001;
25 | vadd_s S000, S000, S001;
26 | mfv v0, S000;
27 |
28 | // Convert result to `i32`
29 | .mips "mtc1 $$v0, $3";
30 | .mips "cvt.w.s $3, $3";
31 | .mips "mfc1 $$v0, $3";
32 |
33 | : "={2}"(out)
34 | : "{4}"(a), "{5}"(b)
35 | : "f"
36 | );
37 | }
38 |
39 | out
40 | }
41 |
42 | fn psp_main() {
43 | psp::enable_home_button();
44 |
45 | // Enable the VFPU
46 | unsafe {
47 | use psp::sys::{self, ThreadAttributes};
48 | sys::sceKernelChangeCurrentThreadAttr(0, ThreadAttributes::VFPU);
49 | }
50 |
51 | psp::dprintln!("Testing VFPU...");
52 | psp::dprintln!("VFPU 123 + 4 = {}", vfpu_add(123, 4));
53 | }
54 |
--------------------------------------------------------------------------------
/examples/vfpu-context-switching/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/vfpu-context-switching/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-vfpu-context-switching-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/vfpu-context-switching/src/main.rs:
--------------------------------------------------------------------------------
1 | #![no_std]
2 | #![no_main]
3 | #![feature(llvm_asm)]
4 |
5 | use psp::sys::vfpu_context::{Context, MatrixSet};
6 |
7 | psp::module!("vfpu_context_test", 1, 1);
8 |
9 | #[no_mangle]
10 | #[inline(never)]
11 | extern fn psp_main() {
12 | psp::enable_home_button();
13 | psp::dprintln!("Testing VFPU context switcher...");
14 |
15 | let mut context = Context::new();
16 |
17 | unsafe {
18 | context.prepare(MatrixSet::VMAT3, MatrixSet::VMAT0);
19 | psp::vfpu_asm! {
20 | vmzero_q M000;
21 |
22 | viim_s S000, 1;
23 | viim_s S001, 2;
24 | viim_s S002, 3;
25 | viim_s S003, 4;
26 |
27 | vmmov_q M300, M000;
28 |
29 | : : : : "volatile"
30 | }
31 |
32 | // Clobber M300 and M000
33 | context.prepare(MatrixSet::empty(), MatrixSet::VMAT0 | MatrixSet::VMAT3);
34 | psp::vfpu_asm! {
35 | vmzero_q M000;
36 | vmzero_q M300;
37 |
38 | : : : : "volatile"
39 | }
40 |
41 | // Read M300 back from the context, and clobber M000.
42 | context.prepare(MatrixSet::VMAT3, MatrixSet::VMAT0);
43 | let mut out: i32;
44 | psp::vfpu_asm! {
45 | vmmov_q M000, M300;
46 |
47 | mfv t0, S000;
48 | .mips "mtc1 $$t0, $$f0";
49 | .mips "cvt.w.s $$f0, $$f0";
50 | .mips "mfc1 $$t0, $$f0";
51 | .mips "addu $0, $$zero, $$t0";
52 |
53 | mfv t0, S001;
54 | .mips "mtc1 $$t0, $$f0";
55 | .mips "cvt.w.s $$f0, $$f0";
56 | .mips "mfc1 $$t0, $$f0";
57 | .mips "addu $0, $0, $$t0";
58 |
59 | mfv t0, S002;
60 | .mips "mtc1 $$t0, $$f0";
61 | .mips "cvt.w.s $$f0, $$f0";
62 | .mips "mfc1 $$t0, $$f0";
63 | .mips "addu $0, $0, $$t0";
64 |
65 | mfv t0, S003;
66 | .mips "mtc1 $$t0, $$f0";
67 | .mips "cvt.w.s $$f0, $$f0";
68 | .mips "mfc1 $$t0, $$f0";
69 | .mips "addu $0, $0, $$t0";
70 |
71 | : "=r"(out) : : "t0", "f0" : "volatile"
72 | }
73 |
74 | psp::dprintln!("1 + 2 + 3 + 4 = {}", out);
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/examples/wlan/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 |
--------------------------------------------------------------------------------
/examples/wlan/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp-wlan-example"
3 | version = "0.1.0"
4 | authors = ["Marko Mijalkovic "]
5 | edition = "2018"
6 |
7 | [dependencies]
8 | psp = { path = "../../psp" }
9 |
--------------------------------------------------------------------------------
/examples/wlan/src/main.rs:
--------------------------------------------------------------------------------
1 | //! This example only demonstrates functionality regarding the WLAN chip. It is
2 | //! not a networking example. You might want to look into `sceNet*` functions
3 | //! for actual network access.
4 |
5 | #![no_std]
6 | #![no_main]
7 |
8 | psp::module!("sample_wlan", 1, 1);
9 |
10 | fn psp_main() {
11 | psp::enable_home_button();
12 |
13 | unsafe {
14 | let wlan_power = psp::sys::sceWlanDevIsPowerOn() == 1;
15 | let wlan_switch = psp::sys::sceWlanGetSwitchState() == 1;
16 |
17 | let mut buf = [0; 8];
18 | psp::sys::sceWlanGetEtherAddr(&mut buf[0]);
19 |
20 | psp::dprintln!(
21 | "WLAN switch enabled: {}, WLAN active: {}, \
22 | MAC address: {:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
23 | wlan_power, wlan_switch,
24 | buf[0], buf[1], buf[2], buf[3], buf[4], buf[5],
25 | );
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/libunwind/.gitignore:
--------------------------------------------------------------------------------
1 | src
2 | include
3 | libunwind.a
4 | *.o
5 |
--------------------------------------------------------------------------------
/libunwind/Makefile:
--------------------------------------------------------------------------------
1 | CC := clang
2 | CXX := clang++
3 | AR := llvm-ar
4 | ARFLAGS := rcs
5 |
6 | CPP_OBJLIST := libunwind.o
7 | C_OBJLIST := UnwindLevel1.o UnwindLevel1-gcc-ext.o
8 | S_OBJLIST := UnwindRegistersRestore.o UnwindRegistersSave.o
9 |
10 | CFLAGS := -std=c99
11 | CXXFLAGS := -std=c++11 -nostdinc++ -fno-exceptions -fno-rtti
12 | CPPFLAGS := -target mipsel-unknown-unknown -mcpu=mips2 -msingle-float \
13 | -fstrict-aliasing -funwind-tables -O3 \
14 | -D __LITTLE_ENDIAN__ -D __ELF__ -D _LIBUNWIND_IS_BAREMETAL \
15 | -D _LIBUNWIND_HAS_NO_THREADS -D _LIBUNWIND_IS_NATIVE_ONLY \
16 | -DNDEBUG \
17 | -I /usr/local/pspdev/psp/include/ \
18 | -I include
19 |
20 | ../psp/libunwind.a: libunwind.a
21 | cp libunwind.a ../psp/
22 | touch ../psp/build.rs
23 |
24 | libunwind.a: $(CPP_OBJLIST) $(C_OBJLIST) $(S_OBJLIST)
25 | $(AR) $(ARFLAGS) libunwind.a $^
26 |
27 | $(CPP_OBJLIST): %.o: src/%.cpp
28 | $(COMPILE.cc) $^
29 |
30 | $(C_OBJLIST): %.o: src/%.c
31 | $(S_OBJLIST): %.o: src/%.S
32 | $(C_OBJLIST) $(S_OBJLIST):
33 | $(COMPILE.c) $^
34 |
35 | .PHONY: clean patch
36 |
37 | patch:
38 | git submodule update --init --depth 1 -- ./rustc
39 | (cd rustc; git submodule update --init --depth 1 -- src/llvm-project)
40 | cp -r rustc/src/llvm-project/libunwind/{src,include} .
41 | patch -p0 < ./no-sdc1.patch
42 |
43 | clean:
44 | rm -r *.o libunwind.a
45 |
--------------------------------------------------------------------------------
/libunwind/no-sdc1.patch:
--------------------------------------------------------------------------------
1 | diff -u ../../rustc/src/llvm-project/libunwind/src/UnwindRegistersRestore.S src/UnwindRegistersRestore.S
2 | --- ../../rustc/src/llvm-project/libunwind/src/UnwindRegistersRestore.S 2020-04-27 15:40:17.000000000 -0400
3 | +++ src/UnwindRegistersRestore.S 2020-05-04 14:29:35.000000000 -0400
4 | @@ -823,22 +823,38 @@
5 | .set nomacro
6 | #ifdef __mips_hard_float
7 | #if __mips_fpr != 64
8 | - ldc1 $f0, (4 * 36 + 8 * 0)($4)
9 | - ldc1 $f2, (4 * 36 + 8 * 2)($4)
10 | - ldc1 $f4, (4 * 36 + 8 * 4)($4)
11 | - ldc1 $f6, (4 * 36 + 8 * 6)($4)
12 | - ldc1 $f8, (4 * 36 + 8 * 8)($4)
13 | - ldc1 $f10, (4 * 36 + 8 * 10)($4)
14 | - ldc1 $f12, (4 * 36 + 8 * 12)($4)
15 | - ldc1 $f14, (4 * 36 + 8 * 14)($4)
16 | - ldc1 $f16, (4 * 36 + 8 * 16)($4)
17 | - ldc1 $f18, (4 * 36 + 8 * 18)($4)
18 | - ldc1 $f20, (4 * 36 + 8 * 20)($4)
19 | - ldc1 $f22, (4 * 36 + 8 * 22)($4)
20 | - ldc1 $f24, (4 * 36 + 8 * 24)($4)
21 | - ldc1 $f26, (4 * 36 + 8 * 26)($4)
22 | - ldc1 $f28, (4 * 36 + 8 * 28)($4)
23 | - ldc1 $f30, (4 * 36 + 8 * 30)($4)
24 | + l.s $f0, (4 * 36 + 4 * 0)($4)
25 | + l.s $f1, (4 * 36 + 4 * 1)($4)
26 | + l.s $f2, (4 * 36 + 4 * 2)($4)
27 | + l.s $f3, (4 * 36 + 4 * 3)($4)
28 | + l.s $f4, (4 * 36 + 4 * 4)($4)
29 | + l.s $f5, (4 * 36 + 4 * 5)($4)
30 | + l.s $f6, (4 * 36 + 4 * 6)($4)
31 | + l.s $f7, (4 * 36 + 4 * 7)($4)
32 | + l.s $f8, (4 * 36 + 4 * 8)($4)
33 | + l.s $f9, (4 * 36 + 4 * 9)($4)
34 | + l.s $f10, (4 * 36 + 4 * 10)($4)
35 | + l.s $f11, (4 * 36 + 4 * 11)($4)
36 | + l.s $f12, (4 * 36 + 4 * 12)($4)
37 | + l.s $f13, (4 * 36 + 4 * 13)($4)
38 | + l.s $f14, (4 * 36 + 4 * 14)($4)
39 | + l.s $f15, (4 * 36 + 4 * 15)($4)
40 | + l.s $f16, (4 * 36 + 4 * 16)($4)
41 | + l.s $f17, (4 * 36 + 4 * 17)($4)
42 | + l.s $f18, (4 * 36 + 4 * 18)($4)
43 | + l.s $f19, (4 * 36 + 4 * 19)($4)
44 | + l.s $f20, (4 * 36 + 4 * 20)($4)
45 | + l.s $f21, (4 * 36 + 4 * 21)($4)
46 | + l.s $f22, (4 * 36 + 4 * 22)($4)
47 | + l.s $f23, (4 * 36 + 4 * 23)($4)
48 | + l.s $f24, (4 * 36 + 4 * 24)($4)
49 | + l.s $f25, (4 * 36 + 4 * 25)($4)
50 | + l.s $f26, (4 * 36 + 4 * 26)($4)
51 | + l.s $f27, (4 * 36 + 4 * 27)($4)
52 | + l.s $f28, (4 * 36 + 4 * 28)($4)
53 | + l.s $f29, (4 * 36 + 4 * 29)($4)
54 | + l.s $f30, (4 * 36 + 4 * 30)($4)
55 | + l.s $f31, (4 * 36 + 4 * 31)($4)
56 | #else
57 | ldc1 $f0, (4 * 36 + 8 * 0)($4)
58 | ldc1 $f1, (4 * 36 + 8 * 1)($4)
59 | diff -u ../../rustc/src/llvm-project/libunwind/src/UnwindRegistersSave.S src/UnwindRegistersSave.S
60 | --- ../../rustc/src/llvm-project/libunwind/src/UnwindRegistersSave.S 2020-04-27 15:40:17.000000000 -0400
61 | +++ src/UnwindRegistersSave.S 2020-05-04 14:29:35.000000000 -0400
62 | @@ -168,22 +168,38 @@
63 | sw $8, (4 * 34)($4)
64 | #ifdef __mips_hard_float
65 | #if __mips_fpr != 64
66 | - sdc1 $f0, (4 * 36 + 8 * 0)($4)
67 | - sdc1 $f2, (4 * 36 + 8 * 2)($4)
68 | - sdc1 $f4, (4 * 36 + 8 * 4)($4)
69 | - sdc1 $f6, (4 * 36 + 8 * 6)($4)
70 | - sdc1 $f8, (4 * 36 + 8 * 8)($4)
71 | - sdc1 $f10, (4 * 36 + 8 * 10)($4)
72 | - sdc1 $f12, (4 * 36 + 8 * 12)($4)
73 | - sdc1 $f14, (4 * 36 + 8 * 14)($4)
74 | - sdc1 $f16, (4 * 36 + 8 * 16)($4)
75 | - sdc1 $f18, (4 * 36 + 8 * 18)($4)
76 | - sdc1 $f20, (4 * 36 + 8 * 20)($4)
77 | - sdc1 $f22, (4 * 36 + 8 * 22)($4)
78 | - sdc1 $f24, (4 * 36 + 8 * 24)($4)
79 | - sdc1 $f26, (4 * 36 + 8 * 26)($4)
80 | - sdc1 $f28, (4 * 36 + 8 * 28)($4)
81 | - sdc1 $f30, (4 * 36 + 8 * 30)($4)
82 | + s.s $f0, (4 * 36 + 4 * 0)($4)
83 | + s.s $f1, (4 * 36 + 4 * 1)($4)
84 | + s.s $f2, (4 * 36 + 4 * 2)($4)
85 | + s.s $f3, (4 * 36 + 4 * 3)($4)
86 | + s.s $f4, (4 * 36 + 4 * 4)($4)
87 | + s.s $f5, (4 * 36 + 4 * 5)($4)
88 | + s.s $f6, (4 * 36 + 4 * 6)($4)
89 | + s.s $f7, (4 * 36 + 4 * 7)($4)
90 | + s.s $f8, (4 * 36 + 4 * 8)($4)
91 | + s.s $f9, (4 * 36 + 4 * 9)($4)
92 | + s.s $f10, (4 * 36 + 4 * 10)($4)
93 | + s.s $f11, (4 * 36 + 4 * 11)($4)
94 | + s.s $f12, (4 * 36 + 4 * 12)($4)
95 | + s.s $f13, (4 * 36 + 4 * 13)($4)
96 | + s.s $f14, (4 * 36 + 4 * 14)($4)
97 | + s.s $f15, (4 * 36 + 4 * 15)($4)
98 | + s.s $f16, (4 * 36 + 4 * 16)($4)
99 | + s.s $f17, (4 * 36 + 4 * 17)($4)
100 | + s.s $f18, (4 * 36 + 4 * 18)($4)
101 | + s.s $f19, (4 * 36 + 4 * 19)($4)
102 | + s.s $f20, (4 * 36 + 4 * 20)($4)
103 | + s.s $f21, (4 * 36 + 4 * 21)($4)
104 | + s.s $f22, (4 * 36 + 4 * 22)($4)
105 | + s.s $f23, (4 * 36 + 4 * 23)($4)
106 | + s.s $f24, (4 * 36 + 4 * 24)($4)
107 | + s.s $f25, (4 * 36 + 4 * 25)($4)
108 | + s.s $f26, (4 * 36 + 4 * 26)($4)
109 | + s.s $f27, (4 * 36 + 4 * 27)($4)
110 | + s.s $f28, (4 * 36 + 4 * 28)($4)
111 | + s.s $f29, (4 * 36 + 4 * 29)($4)
112 | + s.s $f30, (4 * 36 + 4 * 30)($4)
113 | + s.s $f31, (4 * 36 + 4 * 31)($4)
114 | #else
115 | sdc1 $f0, (4 * 36 + 8 * 0)($4)
116 | sdc1 $f1, (4 * 36 + 8 * 1)($4)
117 |
--------------------------------------------------------------------------------
/prx.md:
--------------------------------------------------------------------------------
1 | # PRX section structure
2 |
3 | ## .rodata.sceModuleInfo
4 |
5 | * Most important section
6 | * Details module information, and points to other sections
7 |
8 | ## .lib.ent
9 |
10 | * Details module exports
11 | * `module_start`
12 | * `SceModuleInfo`
13 | * `module_stop`
14 | * etc...
15 |
16 | ## .rodata.sceResident
17 |
18 | * Split into 2 uses:
19 | * The actual export table referenced in .lib.ent. The number of entries
20 | here is specified in the .lib.ent variable and function count fields.
21 | * Storing the names of imported resident libraries
22 |
23 | ## .lib.stub
24 |
25 | * Details modules to be imported
26 | * Unknown ATM if this is system imports only or can be additional user modules.
27 |
28 | ## .sceStub.text
29 |
30 | * Contains jump code for imported modules
31 | * 2 instructions per function, usually `jr $ra` followed by `nop`.
32 |
33 | ## .rodata.sceNid
34 |
35 | * Contains lists of NIDs, to be referenced in .lib.stub
36 |
--------------------------------------------------------------------------------
/psp/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "psp"
3 | version = "0.1.3"
4 | description = "A library for building full PSP modules, including both PRX plugins and regular homebrew apps."
5 | readme = "../README.md"
6 | repository = "https://github.com/overdrivenpotato/rust-psp"
7 | license = "MIT"
8 | authors = [
9 | "Marko Mijalkovic ",
10 | "Paul Sajna "
11 | ]
12 | edition = "2018"
13 | build = "build.rs"
14 |
15 | [lib]
16 | crate-type = ["lib", "staticlib"]
17 |
18 | [features]
19 | default = []
20 | std = []
21 | # Compile this library as a stub provider. Useful to compile this as a static
22 | # library for other projects.
23 | stub-only = []
24 |
25 | [dependencies]
26 | paste = "0.1.12"
27 | bitflags = "1.2.1"
28 | embedded-graphics = { version = "0.6.2", optional = true }
29 |
30 | [dependencies.num_enum]
31 | version = "0.5.0"
32 | default-features = false
33 |
34 | [dependencies.num_enum_derive]
35 | version = "0.5.0"
36 | default-features = false
37 |
--------------------------------------------------------------------------------
/psp/build.rs:
--------------------------------------------------------------------------------
1 | use std::{env, path::Path};
2 |
3 | fn main() {
4 | println!("cargo:rerun-if-changed=build.rs");
5 | println!("cargo:rerun-if-changed=libunwind.a");
6 |
7 | if env::var("CARGO_FEATURE_STUB_ONLY").is_ok() {
8 | return;
9 | }
10 |
11 | // TODO: Do we even need to copy the library over? Maybe we can just link
12 | // directly from the current directory.
13 | let out_dir = env::var("OUT_DIR").unwrap();
14 | let out_file = Path::new(&out_dir).join("libunwind.a");
15 | std::fs::copy("./libunwind.a", out_file).unwrap();
16 |
17 | println!("cargo:rustc-link-lib=static=unwind");
18 | println!("cargo:rustc-link-search=native={}", out_dir);
19 | }
20 |
--------------------------------------------------------------------------------
/psp/libunwind.a:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/psp/libunwind.a
--------------------------------------------------------------------------------
/psp/src/alloc_impl.rs:
--------------------------------------------------------------------------------
1 | use alloc::alloc::{Layout, GlobalAlloc};
2 | use core::{ptr, mem};
3 | use crate::sys::{self, SceUid, SceSysMemPartitionId, SceSysMemBlockTypes};
4 |
5 | /// An allocator that hooks directly into the PSP OS memory allocator.
6 | struct SystemAlloc;
7 |
8 | unsafe impl GlobalAlloc for SystemAlloc {
9 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
10 | let size = layout.size()
11 | // We need to store the memory block ID.
12 | + mem::size_of::()
13 |
14 | // We also store padding bytes, in case the block returned from the
15 | // system is not aligned. The count of padding bytes is also stored
16 | // here, in the last byte.
17 | + layout.align();
18 |
19 | // crate::debug::print_num(size);
20 |
21 | let id = sys::sceKernelAllocPartitionMemory(
22 | SceSysMemPartitionId::SceKernelPrimaryUserPartition,
23 | &b"block\0"[0],
24 | SceSysMemBlockTypes::Low,
25 | size as u32,
26 | ptr::null_mut(),
27 | );
28 |
29 | // TODO: Error handling.
30 | let mut ptr: *mut u8 = sys::sceKernelGetBlockHeadAddr(id).cast();
31 | *ptr.cast() = id;
32 |
33 | ptr = ptr.add(mem::size_of::());
34 |
35 | // We must add at least one, to store this value.
36 | let align_padding = 1 + ptr.add(1).align_offset(layout.align());
37 | *ptr.add(align_padding - 1) = align_padding as u8;
38 | ptr.add(align_padding)
39 | }
40 |
41 | #[inline(never)]
42 | unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
43 | let align_padding = *ptr.sub(1);
44 |
45 | let id = *ptr
46 | .sub(align_padding as usize)
47 | .cast::().offset(-1);
48 |
49 | // TODO: Error handling.
50 | sys::sceKernelFreePartitionMemory(id);
51 | }
52 | }
53 |
54 | #[global_allocator]
55 | static ALLOC: SystemAlloc = SystemAlloc;
56 |
57 | #[cfg(not(feature = "std"))]
58 | #[alloc_error_handler]
59 | fn aeh(_: Layout) -> ! { loop {} }
60 |
61 | #[no_mangle]
62 | #[cfg(not(feature = "stub-only"))]
63 | unsafe extern fn memset(ptr: *mut u8, value: u32, num: usize) -> *mut u8 {
64 | for i in 0..num {
65 | *ptr.add(i) = value as u8;
66 | }
67 |
68 | ptr
69 | }
70 |
71 | #[no_mangle]
72 | #[cfg(not(feature = "stub-only"))]
73 | unsafe extern fn memcpy(dst: *mut u8, src: *const u8, num: isize) -> *mut u8 {
74 | for i in 0..num {
75 | *dst.offset(i) = *src.offset(i);
76 | }
77 |
78 | dst
79 | }
80 |
81 | #[no_mangle]
82 | #[cfg(not(feature = "stub-only"))]
83 | unsafe extern fn memcmp(ptr1: *mut u8, ptr2: *mut u8, num: isize) -> i32 {
84 | for i in 0..num {
85 | let diff = ptr1.offset(i) as i32 - ptr2.offset(i) as i32;
86 |
87 | if diff != 0 {
88 | return diff;
89 | }
90 | }
91 |
92 | 0
93 | }
94 |
95 | #[no_mangle]
96 | #[cfg(not(feature = "stub-only"))]
97 | unsafe extern fn memmove(dst: *mut u8, src: *mut u8, num: isize) -> *mut u8 {
98 | if dst < src {
99 | for i in 0..num {
100 | *dst.offset(i) = *src.offset(i);
101 | }
102 | } else {
103 | for i in num-1..=0 {
104 | *dst.offset(i) = *src.offset(i);
105 | }
106 | }
107 |
108 | dst
109 | }
110 |
--------------------------------------------------------------------------------
/psp/src/benchmark.rs:
--------------------------------------------------------------------------------
1 | /// Execute `f` `iterations` times and return average duration per iteration
2 | pub fn benchmark(mut f: F, iterations: usize) -> core::time::Duration {
3 | let mut loop_start: u64 = 0;
4 | let mut loop_end: u64 = 0;
5 | let avg_micros: u64;
6 |
7 | unsafe {
8 | crate::sys::sceRtcGetCurrentTick(&mut loop_start as *mut u64);
9 |
10 | for _ in 0..iterations {
11 | f();
12 | }
13 |
14 | crate::sys::sceRtcGetCurrentTick(&mut loop_end as *mut u64);
15 | let avg_iter_ticks = (loop_end - loop_start) / iterations as u64;
16 | let ticks_per_sec = crate::sys::sceRtcGetTickResolution();
17 | avg_micros = ((avg_iter_ticks as f64 / ticks_per_sec as f64) * 1_000_000.0) as u64;
18 | }
19 |
20 | core::time::Duration::from_micros(avg_micros)
21 | }
22 |
--------------------------------------------------------------------------------
/psp/src/constants.rs:
--------------------------------------------------------------------------------
1 | /// PSP screen width in pixels
2 | pub const SCREEN_WIDTH: u32 = 480;
3 | /// PSP screen height in pixels
4 | pub const SCREEN_HEIGHT: u32 = 272;
5 | /// The screen buffer width is padded from 480 pixels to a power of 2 (512)
6 | pub const BUF_WIDTH: u32 = 512;
7 |
--------------------------------------------------------------------------------
/psp/src/debug.rs:
--------------------------------------------------------------------------------
1 | //! Debug support.
2 | //!
3 | //! You should use the `dprintln!` and `dprint!` macros.
4 |
5 | use crate::sys;
6 | use core::fmt;
7 |
8 | /// Like `println!`, but prints to the PSP screen.
9 | #[macro_export]
10 | macro_rules! dprintln {
11 | ($($arg:tt)*) => {
12 | $crate::debug::print_args(core::format_args!($($arg)*));
13 | $crate::debug::print_args(core::format_args!("\n"));
14 | }
15 | }
16 |
17 | /// Like `print!`, but prints to the PSP screen.
18 | #[macro_export]
19 | macro_rules! dprint {
20 | ($($arg:tt)*) => {
21 | $crate::debug::print_args(core::format_args!($($arg)*))
22 | }
23 | }
24 |
25 | // TODO: Wrap this in some kind of a mutex.
26 | static mut CHARS: CharBuffer = CharBuffer::new();
27 |
28 | /// Update the screen.
29 | fn update() {
30 | unsafe {
31 | init();
32 | clear_screen(0);
33 |
34 | for (i, line) in CHARS.lines().enumerate() {
35 | put_str::(
36 | &line.chars[0..line.len],
37 | 0,
38 | i * MsxFont::CHAR_HEIGHT,
39 | 0xffff_ffff,
40 | )
41 | }
42 | }
43 | }
44 |
45 | trait Font {
46 | const CHAR_WIDTH: usize;
47 | const CHAR_HEIGHT: usize;
48 |
49 | fn put_char(x: usize, y: usize, color: u32, c: u8);
50 | }
51 |
52 | struct MsxFont;
53 |
54 | impl Font for MsxFont {
55 | const CHAR_HEIGHT: usize = 10;
56 | const CHAR_WIDTH: usize = 6;
57 |
58 | fn put_char(x: usize, y: usize, color: u32, c: u8) {
59 | unsafe {
60 | let mut ptr = VRAM_BASE
61 | .offset(x as isize)
62 | .offset((y * BUFFER_WIDTH) as isize);
63 |
64 | for i in 0..8 {
65 | for j in 0..8 {
66 | if MSX_FONT[c as usize * 8 + i] & (0b1000_0000 >> j) != 0 {
67 | *ptr = color;
68 | }
69 |
70 | ptr = ptr.offset(1);
71 | }
72 |
73 | ptr = ptr.offset(-8).offset(BUFFER_WIDTH as isize);
74 | }
75 | }
76 | }
77 | }
78 |
79 | const BUFFER_WIDTH: usize = 512;
80 | const DISPLAY_HEIGHT: usize = 272;
81 | const DISPLAY_WIDTH: usize = 480;
82 | static mut VRAM_BASE: *mut u32 = 0 as *mut u32;
83 |
84 | unsafe fn clear_screen(color: u32) {
85 | let mut ptr = VRAM_BASE;
86 |
87 | for _ in 0..(BUFFER_WIDTH * DISPLAY_HEIGHT) {
88 | *ptr = color;
89 | ptr = ptr.offset(1);
90 | }
91 | }
92 |
93 | unsafe fn put_str(s: &[u8], x: usize, y: usize, color: u32) {
94 | if y > DISPLAY_HEIGHT {
95 | return;
96 | }
97 |
98 | for (i, c) in s.iter().enumerate() {
99 | if i >= (DISPLAY_WIDTH / T::CHAR_WIDTH) as usize {
100 | break;
101 | }
102 |
103 | if *c as u32 <= 255 && *c != b'\0' {
104 | T::put_char(T::CHAR_WIDTH * i + x, y, color, *c);
105 | }
106 | }
107 | }
108 |
109 | unsafe fn init() {
110 | // The OR operation here specifies the address bypasses cache.
111 | VRAM_BASE = (0x4000_0000u32 | sys::sceGeEdramGetAddr() as u32) as *mut u32;
112 |
113 | // TODO: Change sys types to usize.
114 | sys::sceDisplaySetMode(sys::DisplayMode::Lcd, DISPLAY_WIDTH, DISPLAY_HEIGHT);
115 | sys::sceDisplaySetFrameBuf(
116 | VRAM_BASE as *const u8,
117 | BUFFER_WIDTH,
118 | sys::DisplayPixelFormat::Psm8888,
119 | sys::DisplaySetBufSync::NextFrame,
120 | );
121 | }
122 |
123 | #[doc(hidden)]
124 | pub fn print_args(arguments: core::fmt::Arguments<'_>) {
125 | use fmt::Write;
126 |
127 | unsafe {
128 | let _ = write!(CHARS, "{}", arguments);
129 | }
130 |
131 | update();
132 | }
133 |
134 | // TODO: Move to font.
135 | const ROWS: usize = DISPLAY_HEIGHT / MsxFont::CHAR_HEIGHT;
136 | const COLS: usize = DISPLAY_WIDTH / MsxFont::CHAR_WIDTH;
137 |
138 | #[derive(Copy, Clone)]
139 | struct Line {
140 | chars: [u8; COLS],
141 | len: usize,
142 | }
143 |
144 | impl Line {
145 | const fn new() -> Self {
146 | Self {
147 | chars: [0; COLS],
148 | len: 0,
149 | }
150 | }
151 | }
152 |
153 | struct CharBuffer {
154 | lines: [Line; ROWS],
155 | written: usize,
156 | advance_next: bool,
157 | }
158 |
159 | impl CharBuffer {
160 | const fn new() -> Self {
161 | Self {
162 | lines: [Line::new(); ROWS],
163 | written: 0,
164 | advance_next: false,
165 | }
166 | }
167 |
168 | fn advance(&mut self) {
169 | self.written += 1;
170 | if self.written >= ROWS {
171 | *self.current_line() = Line::new();
172 | }
173 | }
174 |
175 | fn current_line(&mut self) -> &mut Line {
176 | &mut self.lines[self.written % ROWS]
177 | }
178 |
179 | fn add(&mut self, c: u8) {
180 | if self.advance_next {
181 | self.advance_next = false;
182 | self.advance();
183 | }
184 |
185 | match c {
186 | b'\n' => self.advance_next = true,
187 | b'\t' => {
188 | self.add(b' ');
189 | self.add(b' ');
190 | self.add(b' ');
191 | self.add(b' ');
192 | }
193 |
194 | _ => {
195 | if self.current_line().len == COLS {
196 | self.advance();
197 | }
198 |
199 | let line = self.current_line();
200 | line.chars[line.len] = c;
201 | line.len += 1;
202 | }
203 | }
204 | }
205 |
206 | fn lines(&self) -> LineIter<'_> {
207 | LineIter {
208 | buf: self,
209 | pos: 0,
210 | }
211 | }
212 | }
213 |
214 | impl fmt::Write for CharBuffer {
215 | fn write_str(&mut self, s: &str) -> fmt::Result {
216 | unsafe {
217 | for c in s.chars() {
218 | match c as u32 {
219 | 0..=255 => CHARS.add(c as u8),
220 | _ => CHARS.add(0),
221 | }
222 | }
223 | }
224 |
225 | Ok(())
226 | }
227 | }
228 |
229 | struct LineIter<'a> {
230 | buf: &'a CharBuffer,
231 | pos: usize,
232 | }
233 |
234 | impl<'a> Iterator for LineIter<'a> {
235 | type Item = Line;
236 |
237 | fn next(&mut self) -> Option {
238 | if self.pos < core::cmp::min(self.buf.written + 1, ROWS) {
239 | let idx = if self.buf.written > ROWS {
240 | (self.buf.written + 1 + self.pos) % ROWS
241 | } else {
242 | self.pos
243 | };
244 |
245 | let line = self.buf.lines[idx];
246 | self.pos += 1;
247 | Some(line)
248 | } else {
249 | None
250 | }
251 | }
252 | }
253 |
254 | /// Raw MSX font.
255 | ///
256 | /// This is an 8bit x 256 black and white image.
257 | const MSX_FONT: [u8; 2048] = *include_bytes!("msxfont.bin");
258 |
--------------------------------------------------------------------------------
/psp/src/eabi.rs:
--------------------------------------------------------------------------------
1 | extern {
2 | /// Call a function accepting 5 32-bit integer arguments via the MIPS-EABI ABI.
3 | ///
4 | /// This is not safe to call with a function that expects any other ABI.
5 | pub fn i5(a: u32, b: u32, c: u32, d: u32, e: u32, ptr: extern fn(u32, u32, u32, u32, u32) -> u32) -> u32;
6 |
7 | /// Call a function accepting 6 32-bit integer arguments via the MIPS-EABI ABI.
8 | ///
9 | /// This is not safe to call with a function that expects any other ABI.
10 | pub fn i6(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, ptr: extern fn(u32, u32, u32, u32, u32, u32) -> u32) -> u32;
11 |
12 | /// Call a function accepting 7 32-bit integer arguments via the MIPS-EABI ABI.
13 | ///
14 | /// This is not safe to call with a function that expects any other ABI.
15 | pub fn i7(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, ptr: extern fn(u32, u32, u32, u32, u32, u32, u32) -> u32) -> u32;
16 |
17 | /// Call a function with the signature `fn(i32, i64, i32) -> i64` via the MIPS-EABI ABI.
18 | ///
19 | /// This is not safe to call with a function that expects any other ABI.
20 | pub fn i_ii_i_rii(a: u32, b: u64, c: u32, ptr: extern fn(u32, u64, u32) -> u64) -> u64;
21 |
22 | /// Call a function with the signature `fn(i32, i64, i32) -> i32` via the MIPS-EABI ABI.
23 | ///
24 | /// This is not safe to call with a function that expects any other ABI.
25 | pub fn i_ii_i_ri(a: u32, b: u64, c: u32, ptr: extern fn(u32, u64, u32) -> u32) -> u32;
26 | }
27 |
28 | // Potential resource:
29 | // (scroll down to table) https://www.linux-mips.org/wiki/P32_Linux_ABI
30 | //
31 | // Page 3-18: http://web.archive.org/web/20040930224745/http://www.caldera.com/developers/devspecs/mipsabi.pdf
32 | // Copied from PDF:
33 | // Despite the fact that some or all of the arguments to a function are passed
34 | // in registers, always allocate space on the stack for all arguments. This
35 | // stack space should be a structure large enough to contain all the arguments,
36 | // aligned according to normal structure rules (after promotion and structure
37 | // return pointer insertion). The locations within the stack frame used for
38 | // arguments are called the home locations.
39 | #[cfg(target_os = "psp")]
40 | global_asm!(
41 | r#"
42 | .section .text
43 | .global i5
44 | i5:
45 | // Store the return register as we are calling a function manually.
46 | addiu $sp, -32
47 | sw $ra, 8($sp)
48 |
49 | // Load argument 5 into register t0. In MIPS-EABI, t0 is actually a4.
50 | lw $t0, 48($sp)
51 |
52 | // Load and call the bridged function.
53 | lw $t1, 52($sp)
54 | jalr $t1
55 |
56 | // Restore the stack and return.
57 | lw $ra, 8($sp)
58 | addiu $sp, 32
59 | jr $ra
60 |
61 | .global i6
62 | i6:
63 | addiu $sp, -32
64 | sw $ra, 8($sp)
65 |
66 | lw $t0, 48($sp)
67 | lw $t1, 52($sp)
68 |
69 | lw $t2, 56($sp)
70 | jalr $t2
71 |
72 | lw $ra, 8($sp)
73 | addiu $sp, 32
74 | jr $ra
75 |
76 | .global i7
77 | i7:
78 | addiu $sp, -32
79 | sw $ra, 8($sp)
80 |
81 | lw $t0, 48($sp)
82 | lw $t1, 52($sp)
83 | lw $t2, 56($sp)
84 |
85 | lw $t3, 60($sp)
86 | jalr $t3
87 |
88 | lw $ra, 8($sp)
89 | addiu $sp, 32
90 | jr $ra
91 |
92 | .global i_ii_i_rii
93 | .global i_ii_i_ri
94 | i_ii_i_rii:
95 | i_ii_i_ri:
96 | addiu $sp, -32
97 | sw $ra, 8($sp)
98 |
99 | lw $t0, 48($sp)
100 | lw $t1, 52($sp)
101 | jalr $t1
102 |
103 | lw $ra, 8($sp)
104 | addiu $sp, 32
105 | jr $ra
106 | "#
107 | );
108 |
--------------------------------------------------------------------------------
/psp/src/embedded_graphics.rs:
--------------------------------------------------------------------------------
1 | //! Interop between the `psp` crate and the 2D `embedded-graphics` crate.
2 |
3 | use crate::sys;
4 | use crate::{SCREEN_WIDTH, SCREEN_HEIGHT, BUF_WIDTH};
5 | use core::convert::TryInto;
6 | use embedded_graphics::{
7 | drawable::Pixel,
8 | geometry::Size,
9 | pixelcolor::{Rgb888, RgbColor},
10 | DrawTarget,
11 | };
12 |
13 | pub struct Framebuffer {
14 | vram_base: *mut u16,
15 | }
16 |
17 | impl Framebuffer {
18 | pub fn new() -> Self {
19 | unsafe {
20 | sys::sceDisplaySetMode(sys::DisplayMode::Lcd, 480, 272);
21 | let vram_base = (0x4000_0000u32 | sys::sceGeEdramGetAddr() as u32) as *mut u16;
22 | sys::sceDisplaySetFrameBuf(
23 | vram_base as *const u8,
24 | BUF_WIDTH as usize,
25 | sys::DisplayPixelFormat::Psm8888,
26 | sys::DisplaySetBufSync::NextFrame,
27 | );
28 | Framebuffer { vram_base }
29 | }
30 | }
31 | }
32 |
33 | impl DrawTarget for Framebuffer {
34 | type Error = core::convert::Infallible;
35 |
36 | fn draw_pixel(&mut self, pixel: Pixel) -> Result<(), Self::Error> {
37 | let Pixel(coord, color) = pixel;
38 |
39 | if let Ok((x @ 0..=SCREEN_WIDTH, y @ 0..=SCREEN_HEIGHT)) = coord.try_into() {
40 | unsafe {
41 | let ptr = (self.vram_base as *mut u32)
42 | .offset(x as isize)
43 | .offset((y * BUF_WIDTH) as isize);
44 |
45 | *ptr = (color.r() as u32)
46 | | ((color.g() as u32) << 8)
47 | | ((color.b() as u32) << 16);
48 | }
49 | }
50 |
51 | Ok(())
52 | }
53 |
54 | fn size(&self) -> Size {
55 | Size::new(SCREEN_WIDTH, SCREEN_HEIGHT)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/psp/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![allow(stable_features)]
2 | #![feature(
3 | alloc_error_handler,
4 | llvm_asm,
5 | global_asm,
6 | untagged_unions,
7 | core_intrinsics,
8 | const_loop,
9 | const_if_match,
10 | const_generics,
11 | c_variadic,
12 | lang_items,
13 | )]
14 |
15 | // For unwinding support
16 | #![feature(std_internals, panic_info_message, panic_internals, unwind_attributes)]
17 | #![cfg_attr(not(feature = "stub-only"), feature(panic_unwind))]
18 |
19 | // For the `const_generics` feature.
20 | #![allow(incomplete_features)]
21 |
22 | #![cfg_attr(not(feature = "std"), no_std)]
23 |
24 | #[macro_use] extern crate paste;
25 | #[cfg(not(feature = "stub-only"))] extern crate alloc;
26 | #[cfg(not(feature = "stub-only"))] extern crate panic_unwind;
27 |
28 | #[macro_use]
29 | #[doc(hidden)]
30 | #[cfg(not(feature = "stub-only"))]
31 | pub mod debug;
32 |
33 | #[macro_use] mod vfpu;
34 | mod eabi;
35 | pub mod math;
36 | pub mod sys;
37 | #[cfg(not(feature = "stub-only"))] pub mod test_runner;
38 | #[cfg(not(feature = "stub-only"))] pub mod vram_alloc;
39 |
40 | #[cfg(not(feature = "stub-only"))] mod alloc_impl;
41 | #[cfg(not(feature = "stub-only"))] pub mod panic;
42 |
43 | #[cfg(not(feature = "stub-only"))] mod screenshot;
44 | #[cfg(not(feature = "stub-only"))] pub use screenshot::*;
45 |
46 | #[cfg(not(feature = "stub-only"))] mod benchmark;
47 | #[cfg(not(feature = "stub-only"))] pub use benchmark::*;
48 |
49 | #[cfg(not(feature = "stub-only"))] mod constants;
50 | #[cfg(not(feature = "stub-only"))] pub use constants::*;
51 |
52 | #[cfg(not(feature = "std"))]
53 | #[cfg(feature = "stub-only")]
54 | #[panic_handler]
55 | fn panic(_: &core::panic::PanicInfo) -> ! { loop {} }
56 |
57 | #[cfg(not(feature = "std"))]
58 | #[no_mangle]
59 | extern "C" fn __rust_foreign_exception() -> ! { loop {} }
60 |
61 | #[cfg(feature = "std")]
62 | pub use std::panic::catch_unwind;
63 |
64 | #[cfg(all(not(feature = "std"), not(feature = "stub-only")))]
65 | pub use panic::catch_unwind;
66 |
67 | #[cfg(feature="embedded-graphics")]
68 | pub mod embedded_graphics;
69 |
70 | #[repr(align(16))]
71 | #[derive(Copy, Clone)]
72 | pub struct Align16(pub T);
73 |
74 | #[cfg(all(target_os = "psp", not(feature = "stub-only")))]
75 | global_asm!(
76 | r#"
77 | .section .lib.ent.top, "a", @progbits
78 | .align 2
79 | .word 0
80 | .global __lib_ent_top
81 | __lib_ent_top:
82 | .section .lib.ent.btm, "a", @progbits
83 | .align 2
84 | .global __lib_ent_bottom
85 | __lib_ent_bottom:
86 | .word 0
87 |
88 | .section .lib.stub.top, "a", @progbits
89 | .align 2
90 | .word 0
91 | .global __lib_stub_top
92 | __lib_stub_top:
93 | .section .lib.stub.btm, "a", @progbits
94 | .align 2
95 | .global __lib_stub_bottom
96 | __lib_stub_bottom:
97 | .word 0
98 | "#
99 | );
100 |
101 | /// Declare a PSP module.
102 | ///
103 | /// You must also define a `fn psp_main() { ... }` function in conjunction with
104 | /// this macro.
105 | #[macro_export]
106 | macro_rules! module {
107 | ($name:expr, $version_major:expr, $version_minor: expr) => {
108 | #[doc(hidden)]
109 | mod __psp_module {
110 | #[no_mangle]
111 | #[link_section = ".rodata.sceModuleInfo"]
112 | #[used]
113 | static MODULE_INFO: $crate::Align16<$crate::sys::SceModuleInfo> = $crate::Align16(
114 | $crate::sys::SceModuleInfo {
115 | mod_attribute: 0,
116 | mod_version: [$version_major, $version_minor],
117 | mod_name: $crate::sys::SceModuleInfo::name($name),
118 | terminal: 0,
119 | gp_value: unsafe { &_gp },
120 | stub_top: unsafe { &__lib_stub_top },
121 | stub_end: unsafe { &__lib_stub_bottom },
122 | ent_top: unsafe { &__lib_ent_top },
123 | ent_end: unsafe { &__lib_ent_bottom },
124 | }
125 | );
126 |
127 | extern {
128 | static _gp: u8;
129 | static __lib_ent_bottom: u8;
130 | static __lib_ent_top: u8;
131 | static __lib_stub_bottom: u8;
132 | static __lib_stub_top: u8;
133 | }
134 |
135 | #[no_mangle]
136 | #[link_section = ".lib.ent"]
137 | #[used]
138 | static LIB_ENT: $crate::sys::SceLibraryEntry = $crate::sys::SceLibraryEntry {
139 | // TODO: Fix this?
140 | name: core::ptr::null(),
141 | version: ($version_major, $version_minor),
142 | attribute: $crate::sys::SceLibAttr::SCE_LIB_IS_SYSLIB,
143 | entry_len: 4,
144 | var_count: 1,
145 | func_count: 1,
146 | entry_table: &LIB_ENT_TABLE,
147 | };
148 |
149 | #[no_mangle]
150 | #[link_section = ".rodata.sceResident"]
151 | #[used]
152 | static LIB_ENT_TABLE: $crate::sys::SceLibraryEntryTable = $crate::sys::SceLibraryEntryTable {
153 | module_start_nid: 0xd632acdb, // module_start
154 | module_info_nid: 0xf01d73a7, // SceModuleInfo
155 | module_start: module_start,
156 | module_info: &MODULE_INFO.0,
157 | };
158 |
159 | #[no_mangle]
160 | extern "C" fn module_start(_argc: isize, _argv: *const *const u8) -> isize {
161 | use $crate::sys::ThreadAttributes;
162 | use core::ffi::c_void;
163 |
164 | unsafe {
165 | extern fn main_thread(_argc: usize, _argv: *mut c_void) -> i32 {
166 | // TODO: Maybe print any error to debug screen?
167 | let _ = $crate::catch_unwind(|| {
168 | super::psp_main();
169 | });
170 |
171 | 0
172 | }
173 |
174 | let id = $crate::sys::sceKernelCreateThread(
175 | &b"main_thread\0"[0],
176 | main_thread,
177 | // default priority of 32.
178 | 32,
179 | // 256kb stack
180 | 256 * 1024,
181 | ThreadAttributes::USER,
182 | core::ptr::null_mut(),
183 | );
184 |
185 | $crate::sys::sceKernelStartThread(id, 0, core::ptr::null_mut());
186 | }
187 |
188 | 0
189 | }
190 | }
191 | }
192 | }
193 |
194 | /// Enable the home button.
195 | ///
196 | /// This API does not have destructor support yet. You can manually setup an
197 | /// exit callback if you need this, see the source code of this function.
198 | pub fn enable_home_button() {
199 | use core::{ptr, ffi::c_void};
200 | use sys::ThreadAttributes;
201 |
202 | unsafe {
203 | unsafe extern fn exit_thread(_args: usize, _argp: *mut c_void) -> i32 {
204 | unsafe extern fn exit_callback(_arg1: i32, _arg2: i32, _arg: *mut c_void) -> i32 {
205 | sys::sceKernelExitGame();
206 | 0
207 | }
208 |
209 | let id = sys::sceKernelCreateCallback(
210 | &b"exit_callback\0"[0],
211 | exit_callback,
212 | ptr::null_mut(),
213 | );
214 |
215 | sys::sceKernelRegisterExitCallback(id);
216 | sys::sceKernelSleepThreadCB();
217 |
218 | 0
219 | }
220 |
221 | // Enable the home button.
222 | let id = sys::sceKernelCreateThread(
223 | &b"exit_thread\0"[0],
224 | exit_thread,
225 | 32,
226 | 0x1000,
227 | ThreadAttributes::empty(),
228 | ptr::null_mut(),
229 | );
230 |
231 | sys::sceKernelStartThread(id, 0, ptr::null_mut());
232 | }
233 | }
234 |
--------------------------------------------------------------------------------
/psp/src/math/mod.rs:
--------------------------------------------------------------------------------
1 | mod trig;
2 | pub use trig::*;
3 |
--------------------------------------------------------------------------------
/psp/src/math/trig.rs:
--------------------------------------------------------------------------------
1 | // TODO: cosf vs cosf32? which makes intrinsics::cosf32 work?
2 | #[allow(non_snake_case)]
3 | #[no_mangle]
4 | pub unsafe extern "C" fn cosf32(rad: f32) -> f32 {
5 | let out;
6 |
7 | vfpu_asm!(
8 | .mips "mfc1 $$t0, $1";
9 | mtv t0, S000;
10 | vcst_s S001, VFPU_2_PI;
11 | vmul_s S000, S000, S001;
12 | vcos_s S000, S000;
13 | mfv t0, S000;
14 | .mips "mtc1 $$t0, $0";
15 |
16 | : "=f"(out) : "f"(rad) : "$8", "memory" : "volatile"
17 | );
18 |
19 | out
20 | }
21 |
--------------------------------------------------------------------------------
/psp/src/msxfont.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sajattack/rust-psp/0152d8bee8f8c21a6515c65d12cb82f9bc07f592/psp/src/msxfont.bin
--------------------------------------------------------------------------------
/psp/src/panic.rs:
--------------------------------------------------------------------------------
1 | //! Panic support for the PSP.
2 |
3 | // Most of the code here is lifted from `rustc/src/libstd/panicking.rs`. It has
4 | // been adapted to run on the PSP.
5 |
6 | #[cfg(not(feature = "std"))]
7 | use crate::sys;
8 |
9 | #[cfg(feature = "std")]
10 | use core::{mem::ManuallyDrop, any::Any};
11 | #[cfg(not(feature = "std"))]
12 | use core::{mem::{self, ManuallyDrop}, any::Any, panic::{PanicInfo, BoxMeUp, Location}};
13 |
14 | #[cfg(not(feature = "std"))]
15 | use core::fmt;
16 |
17 | #[cfg(not(feature = "std"))]
18 | use alloc::{boxed::Box, string::{String, ToString}};
19 |
20 | #[link(name = "unwind", kind = "static")]
21 | extern {}
22 |
23 | #[cfg(not(feature = "std"))]
24 | fn print_and_die(s: String) -> ! {
25 | dprintln!("{}", s);
26 |
27 | unsafe {
28 | sys::sceKernelExitDeleteThread(1);
29 | core::intrinsics::unreachable()
30 | }
31 | }
32 |
33 | #[cfg(not(feature = "std"))]
34 | #[panic_handler]
35 | #[inline(never)]
36 | fn panic(info: &PanicInfo) -> ! {
37 | panic_impl(info)
38 | }
39 |
40 | #[inline(always)]
41 | #[cfg_attr(not(target_os = "psp"), allow(unused))]
42 | #[cfg(not(feature = "std"))]
43 | fn panic_impl(info: &PanicInfo) -> ! {
44 | struct PanicPayload<'a> {
45 | inner: &'a fmt::Arguments<'a>,
46 | string: Option,
47 | }
48 |
49 | impl<'a> PanicPayload<'a> {
50 | fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> {
51 | PanicPayload { inner, string: None }
52 | }
53 |
54 | fn fill(&mut self) -> &mut String {
55 | use fmt::Write;
56 | let inner = self.inner;
57 | self.string.get_or_insert_with(|| {
58 | let mut s = String::new();
59 | drop(s.write_fmt(*inner));
60 | s
61 | })
62 | }
63 | }
64 |
65 | unsafe impl<'a> BoxMeUp for PanicPayload<'a> {
66 | fn take_box(&mut self) -> *mut (dyn Any + Send) {
67 | let contents = mem::take(self.fill());
68 | Box::into_raw(Box::new(contents))
69 | }
70 |
71 | fn get(&mut self) -> &(dyn Any + Send) {
72 | self.fill()
73 | }
74 | }
75 |
76 | let loc = info.location().unwrap();
77 | let msg = info.message().unwrap();
78 | rust_panic_with_hook(&mut PanicPayload::new(msg), info.message(), loc);
79 | }
80 |
81 | /// Central point for dispatching panics.
82 | ///
83 | /// Executes the primary logic for a panic, including checking for recursive
84 | /// panics, panic hooks, and finally dispatching to the panic runtime to either
85 | /// abort or unwind.
86 | #[cfg(not(feature = "std"))]
87 | fn rust_panic_with_hook(
88 | payload: &mut dyn BoxMeUp,
89 | message: Option<&fmt::Arguments<'_>>,
90 | location: &Location<'_>,
91 | ) -> ! {
92 | let panics = update_panic_count(1);
93 |
94 | fn die_nested() -> ! {
95 | print_and_die("thread panicked while processing panic. aborting.".into());
96 | }
97 |
98 | let mut info = PanicInfo::internal_constructor(message, location);
99 | info.set_payload(payload.get());
100 |
101 | dprintln!("{}", info.to_string());
102 |
103 | if panics > 1 {
104 | // If a thread panics while it's already unwinding then we
105 | // have limited options. Currently our preference is to
106 | // just abort. In the future we may consider resuming
107 | // unwinding or otherwise exiting the thread cleanly.
108 | die_nested();
109 | }
110 |
111 | rust_panic(payload)
112 | }
113 |
114 | fn update_panic_count(amt: isize) -> usize {
115 | // TODO: Make this thread local
116 | static mut PANIC_COUNT: usize = 0;
117 |
118 | unsafe {
119 | PANIC_COUNT = (PANIC_COUNT as isize + amt) as usize;
120 | PANIC_COUNT
121 | }
122 | }
123 |
124 | #[allow(improper_ctypes)]
125 | extern "C" {
126 | fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
127 | #[unwind(allowed)]
128 | fn __rust_start_panic(payload: usize) -> u32;
129 | }
130 |
131 | #[inline(never)]
132 | #[no_mangle]
133 | #[cfg(not(feature = "std"))]
134 | fn rust_panic(mut msg: &mut dyn BoxMeUp) -> ! {
135 | let code = unsafe {
136 | let obj = &mut msg as *mut &mut dyn BoxMeUp;
137 | panic_unwind::__rust_start_panic(obj as _)
138 | };
139 |
140 | print_and_die(alloc::format!("failed to initiate panic, error {}", code))
141 | }
142 |
143 | #[cfg(not(test))]
144 | #[no_mangle]
145 | #[cfg(not(feature = "std"))]
146 | extern "C" fn __rust_drop_panic() -> ! {
147 | print_and_die("Rust panics must be rethrown".into());
148 | }
149 |
150 | /// Invoke a closure, capturing the cause of an unwinding panic if one occurs.
151 | #[inline(never)]
152 | pub fn catch_unwind R>(f: F) -> Result> {
153 | // This whole function is directly lifted out of rustc. See comments there
154 | // for an explanation of how this actually works.
155 |
156 | union Data {
157 | f: ManuallyDrop,
158 | r: ManuallyDrop,
159 | p: ManuallyDrop>,
160 | }
161 |
162 | let mut data = Data { f: ManuallyDrop::new(f) };
163 |
164 | let data_ptr = &mut data as *mut _ as *mut u8;
165 |
166 | return unsafe {
167 | if core::intrinsics::r#try(do_call::, data_ptr, do_catch::) == 0 {
168 | Ok(ManuallyDrop::into_inner(data.r))
169 | } else {
170 | Err(ManuallyDrop::into_inner(data.p))
171 | }
172 | };
173 |
174 | #[cold]
175 | unsafe fn cleanup(payload: *mut u8) -> Box {
176 | let obj = Box::from_raw(__rust_panic_cleanup(payload));
177 | update_panic_count(-1);
178 | obj
179 | }
180 |
181 | #[inline]
182 | fn do_call R, R>(data: *mut u8) {
183 | unsafe {
184 | let data = data as *mut Data;
185 | let data = &mut (*data);
186 | let f = ManuallyDrop::take(&mut data.f);
187 | data.r = ManuallyDrop::new(f());
188 | }
189 | }
190 |
191 | #[inline]
192 | fn do_catch R, R>(data: *mut u8, payload: *mut u8) {
193 | unsafe {
194 | let data = data as *mut Data;
195 | let data = &mut (*data);
196 | let obj = cleanup(payload);
197 | data.p = ManuallyDrop::new(obj);
198 | }
199 | }
200 | }
201 |
202 | /// These symbols and functions should not actually be used. `libunwind`,
203 | /// however, requires them to be present so that it can link.
204 | // TODO: Patch these out of libunwind instead.
205 | #[cfg(all(target_os = "psp", not(feature = "stub-only")))]
206 | mod libunwind_shims {
207 | #[no_mangle]
208 | unsafe extern "C" fn fprintf(_stream: *const u8, _format: *const u8, ...) -> isize {
209 | -1
210 | }
211 |
212 | #[no_mangle]
213 | unsafe extern "C" fn fflush(_stream: *const u8) -> i32 {
214 | -1
215 | }
216 |
217 | #[no_mangle]
218 | unsafe extern "C" fn abort() {
219 | loop { llvm_asm!("" :::: "volatile"); }
220 | }
221 |
222 | #[no_mangle]
223 | unsafe extern "C" fn malloc(size: usize) -> *mut u8 {
224 | use alloc::alloc::{alloc, Layout};
225 |
226 | let size = size + 4;
227 |
228 | let data = alloc(Layout::from_size_align_unchecked(size, 4));
229 | *(data as *mut usize) = size;
230 |
231 | data.offset(4)
232 | }
233 |
234 | #[no_mangle]
235 | unsafe extern "C" fn free(data: *mut u8) {
236 | use alloc::alloc::{dealloc, Layout};
237 |
238 | let base = data.sub(4);
239 | let size = *(base as *mut usize);
240 |
241 | dealloc(base, Layout::from_size_align_unchecked(size, 4));
242 | }
243 |
244 | #[no_mangle]
245 | unsafe extern "C" fn getenv(_name: *const u8) -> *const u8 {
246 | core::ptr::null()
247 | }
248 |
249 | #[no_mangle]
250 | unsafe extern "C" fn __assert_func(_: *const u8, _: i32, _: *const u8, _: *const u8) {}
251 |
252 | #[no_mangle]
253 | static _impure_ptr: [usize; 0] = [];
254 | }
255 |
--------------------------------------------------------------------------------
/psp/src/screenshot.rs:
--------------------------------------------------------------------------------
1 | use core::{ptr, ffi::c_void};
2 | use crate::sys::{self, DisplayPixelFormat};
3 | use crate::{SCREEN_WIDTH, SCREEN_HEIGHT};
4 |
5 | // RGBA
6 | const BYTES_PER_PIXEL: usize = 4;
7 |
8 | const NUM_PIXELS: usize = (SCREEN_WIDTH * SCREEN_HEIGHT) as usize;
9 |
10 | #[repr(C, packed)]
11 | struct BmpHeader {
12 | pub file_type: [u8; 2],
13 | pub file_size: u32,
14 | pub reserved_1: u16,
15 | pub reserved_2: u16,
16 | pub image_data_start: u32,
17 | pub dib_header_size: u32,
18 | pub image_width: u32,
19 | pub image_height: u32,
20 | pub color_planes: u16,
21 | pub bpp: u16,
22 | pub compression: u32,
23 | pub image_data_len: u32,
24 | pub print_resolution_x: u32,
25 | pub print_resolution_y: u32,
26 | pub palette_color_count: u32,
27 | pub important_colors: u32,
28 | }
29 |
30 | impl BmpHeader {
31 | const BYTES: usize = core::mem::size_of::();
32 |
33 | fn to_bytes(self) -> [u8; Self::BYTES] {
34 | unsafe {
35 | core::mem::transmute(self)
36 | }
37 | }
38 | }
39 |
40 | fn rgba_to_bgra(rgba: u32) -> u32 {
41 | // 0xAABBGGRR -> 0xAARRGGBB
42 |
43 | core::intrinsics::bswap(rgba << 8 | rgba >> 24)
44 | }
45 |
46 | fn rgb565_to_bgra(rgb565: u16) -> u32 {
47 | let rgb565 = rgb565 as u32;
48 |
49 | // bbbb bggg gggr rrrr -> 0xffRRGGBB
50 | ((rgb565 & 0x1f) << 16) * 0x100 / 0x20
51 | | ((rgb565 & 0x7e0) << 3) * 0x100 / 0x40
52 | | ((rgb565 & 0xf800) >> 11) * 0x100 / 0x20
53 | | 0xff00_0000
54 | }
55 |
56 | fn rgba5551_to_bgra(rgba5551: u16) -> u32 {
57 | let rgba5551 = rgba5551 as u32;
58 |
59 | // abbb bbgg gggr rrrr -> 0xAARRGGBB
60 | ((rgba5551 & 0x1f) << 16) * 0x100 / 0x20
61 | | ((rgba5551 & 0x3e0) << 3) * 0x100 / 0x20
62 | | ((rgba5551 & 0x7c00) >> 10) * 0x100 / 0x20
63 | | ((rgba5551 & 0x8000) >> 15) * 0xff00_0000
64 | }
65 |
66 | fn rgba4444_to_bgra(rgba4444: u16) -> u32 {
67 | let rgba4444 = rgba4444 as u32;
68 |
69 | // aaaa bbbb gggg rrrr -> 0xAARRGGBB
70 | ((rgba4444 & 0x000f) << 16) * 0x100 / 0x10
71 | | ((rgba4444 & 0x00f0) << 4) * 0x100 / 0x10
72 | | ((rgba4444 & 0x0f00) >> 8) * 0x100 / 0x10
73 | | ((rgba4444 & 0xf000) << 12) * 0x100 / 0x10
74 | }
75 |
76 | /// Take a screenshot, returning a raw ARGB (big-endian) array.
77 | pub fn screenshot_argb_be() -> alloc::vec::Vec {
78 | let mut screenshot_buffer = alloc::vec![0; NUM_PIXELS];
79 | let mut buffer_width: usize = 0;
80 | let mut pixel_format = DisplayPixelFormat::Psm5650;
81 | let mut top_addr: *mut c_void = ptr::null_mut();
82 |
83 | unsafe {
84 | sys::sceDisplayGetFrameBuf(
85 | &mut top_addr,
86 | &mut buffer_width,
87 | &mut pixel_format,
88 | sys::DisplaySetBufSync::Immediate,
89 | );
90 | }
91 |
92 | // http://uofw.github.io/upspd/docs/hardware/PSPTEK.htm#memmap
93 |
94 | // If this is a kernel address...
95 | if top_addr as u32 & 0x80000000 != 0 {
96 | // Set the kernel cache-through bit.
97 | top_addr = (top_addr as u32 | 0xA0000000) as _;
98 | } else {
99 | // Else set the regular cache-through bit.
100 | top_addr = (top_addr as u32 | 0x40000000) as _;
101 | }
102 |
103 | for x in 0..SCREEN_WIDTH {
104 | for y in 0..SCREEN_HEIGHT {
105 | // BGRA is reversed ARGB. We do this for little-endian based copying.
106 | let bgra = match pixel_format {
107 | sys::DisplayPixelFormat::Psm8888 => {
108 | let rgba = unsafe {
109 | *(top_addr as *mut u32).add(x as usize + y as usize * buffer_width)
110 | };
111 |
112 | rgba_to_bgra(rgba)
113 | },
114 |
115 | sys::DisplayPixelFormat::Psm5650 => {
116 | let rgb565 = unsafe {
117 | *(top_addr as *mut u16).add(x as usize + y as usize * buffer_width)
118 | };
119 |
120 | rgb565_to_bgra(rgb565)
121 | }
122 |
123 | sys::DisplayPixelFormat::Psm5551 => {
124 | let rgba5551 = unsafe {
125 | *(top_addr as *mut u16).add(x as usize + y as usize * buffer_width)
126 | };
127 |
128 | rgba5551_to_bgra(rgba5551)
129 | }
130 |
131 | sys::DisplayPixelFormat::Psm4444 => {
132 | let rgba4444 = unsafe {
133 | *(top_addr as *mut u16).add(x as usize + y as usize * buffer_width)
134 | };
135 |
136 | rgba4444_to_bgra(rgba4444)
137 | }
138 | };
139 |
140 | // Display buffer is flipped upside down.
141 | let y_inv = SCREEN_HEIGHT - y - 1;
142 | screenshot_buffer[x as usize + y_inv as usize * SCREEN_WIDTH as usize] = bgra;
143 | }
144 | }
145 |
146 | screenshot_buffer
147 | }
148 |
149 | /// Take a screenshot, returning a valid bitmap file.
150 | pub fn screenshot_bmp() -> alloc::vec::Vec {
151 | let mut screenshot_buffer = alloc::vec![0; BmpHeader::BYTES + NUM_PIXELS * BYTES_PER_PIXEL];
152 |
153 | let payload = screenshot_argb_be();
154 |
155 | let bmp_header = BmpHeader {
156 | file_type: *b"BM",
157 | file_size: BmpHeader::BYTES as u32 + payload.len() as u32 * 4,
158 | reserved_1: 0,
159 | reserved_2: 0,
160 | image_data_start: BmpHeader::BYTES as u32,
161 | dib_header_size: 40,
162 | image_width: SCREEN_WIDTH as u32,
163 | image_height: SCREEN_HEIGHT as u32,
164 | color_planes: 1,
165 | bpp: 32,
166 | compression: 0,
167 | image_data_len: payload.len() as u32 * 4,
168 | print_resolution_x: 2835, // 72 DPI
169 | print_resolution_y: 2835, // 72 DPI
170 | palette_color_count: 0,
171 | important_colors: 0
172 | };
173 |
174 | screenshot_buffer[0..BmpHeader::BYTES].copy_from_slice(&bmp_header.to_bytes());
175 |
176 | unsafe {
177 | core::ptr::copy_nonoverlapping(
178 | &payload[0] as *const _ as _,
179 | &mut screenshot_buffer[BmpHeader::BYTES] as *mut u8,
180 | NUM_PIXELS * BYTES_PER_PIXEL,
181 | );
182 | }
183 |
184 | screenshot_buffer
185 | }
186 |
--------------------------------------------------------------------------------
/psp/src/sys/atrac.rs:
--------------------------------------------------------------------------------
1 | use core::ffi::c_void;
2 | use crate::eabi::i5;
3 |
4 | #[repr(C)]
5 | #[derive(Debug, Copy, Clone)]
6 | pub struct Atrac3BufferInfo {
7 | pub puc_write_position_first_buf: *mut u8,
8 | pub ui_writable_byte_first_buf: u32,
9 | pub ui_min_write_byte_first_buf: u32,
10 | pub ui_read_position_first_buf: u32,
11 | pub puc_write_position_second_buf: *mut u8,
12 | pub ui_writable_byte_second_buf: u32,
13 | pub ui_min_write_byte_second_buf: u32,
14 | pub ui_read_position_second_buf: u32,
15 | }
16 |
17 | psp_extern! {
18 | #![name = "sceAtrac3plus"]
19 | #![flags = 0x0009]
20 | #![version = (0x00, 0x00)]
21 |
22 | #[psp(0x780F88D1)]
23 | pub fn sceAtracGetAtracID(ui_codec_type: u32) -> i32;
24 |
25 | #[psp(0x7A20E7AF)]
26 | /// Creates a new Atrac ID from the specified data
27 | ///
28 | /// # Parameters
29 | ///
30 | /// - `buf`: the buffer holding the atrac3 data, including the RIFF/WAVE header.
31 | /// - `bufsize`: the size of the buffer pointed by buf
32 | ///
33 | /// # Return Value
34 | ///
35 | /// the new atrac ID, or < 0 on error
36 | pub fn sceAtracSetDataAndGetID(
37 | buf: *mut c_void,
38 | bufsize: usize,
39 | ) -> i32;
40 |
41 | #[psp(0x6A8C3CD5, i5)]
42 | /// Decode a frame of data.
43 | ///
44 | /// # Parameters
45 | ///
46 | /// - `atrac_id`: the atrac ID
47 | /// - `out_samples`: pointer to a buffer that receives the decoded data of the current frame
48 | /// - `out_n`: pointer to a integer that receives the number of audio samples of the decoded frame
49 | /// - `out_end`: pointer to a integer that receives a boolean value indicating if the decoded frame is the last one
50 | /// - `out_remain_frame`: pointer to a integer that receives either -1 if all at3 data is already on memory,
51 | /// or the remaining (not decoded yet) frames at memory if not all at3 data is on memory
52 | ///
53 | ///
54 | /// # Return Value
55 | ///
56 | /// < 0 on error, otherwise 0
57 | pub fn sceAtracDecodeData(
58 | atrac_id: i32,
59 | out_samples: *mut u16,
60 | out_n: *mut i32,
61 | out_end: *mut i32,
62 | out_remain_frame: *mut i32,
63 | ) -> i32;
64 |
65 | #[psp(0x9AE849A7)]
66 | /// Gets the remaining (not decoded) number of frames
67 | ///
68 | /// # Parameters
69 | ///
70 | /// - `atrac_id`: the atrac ID
71 | /// - `out_remain_frame`: pointer to a integer that receives either -1 if all at3 data is already on memory,
72 | /// or the remaining (not decoded yet) frames at memory if not all at3 data is on memory
73 | ///
74 | /// # Return Value
75 | ///
76 | /// < 0 on error, otherwise 0
77 | pub fn sceAtracGetRemainFrame(
78 | atrac_id: i32,
79 | out_remain_frame: *mut i32,
80 | ) -> i32;
81 |
82 | #[psp(0x5D268707)]
83 | /// # Parameters
84 | ///
85 | /// - `atrac_id`: the atrac ID
86 | /// - `write_pointer`: Pointer to where to read the atrac data
87 | /// - `available_bytes`: Number of bytes available at the writePointer location
88 | /// - `read_offset`: Offset where to seek into the atrac file before reading
89 | ///
90 | /// # Return Value
91 | ///
92 | /// < 0 on error, otherwise 0
93 | pub fn sceAtracGetStreamDataInfo(
94 | atrac_id: i32,
95 | write_pointer: *mut *mut u8,
96 | available_bytes: *mut u32,
97 | read_offset: *mut u32,
98 | ) -> i32;
99 |
100 | #[psp(0x7DB31251)]
101 | /// # Parameters
102 | ///
103 | /// - `atrac_id`: the atrac ID
104 | /// - `bytes_to_add`: Number of bytes read into location given by sceAtracGetStreamDataInfo().
105 | ///
106 | /// # Return Value
107 | ///
108 | /// < 0 on error, otherwise 0
109 | pub fn sceAtracAddStreamData(
110 | atrac_id: i32,
111 | bytes_to_add: u32,
112 | ) -> i32;
113 |
114 | #[psp(0xA554A158)]
115 | /// Gets the bitrate.
116 | ///
117 | /// # Parameters
118 | ///
119 | /// - `atrac_id`: the atracID
120 | /// - `out_bitrate`: pointer to a integer that receives the bitrate in kbps
121 | ///
122 | /// # Return Value
123 | ///
124 | /// < 0 on error, otherwise 0
125 | pub fn sceAtracGetBitrate(
126 | atrac_id: i32,
127 | out_bitrate: *mut i32,
128 | ) -> i32;
129 |
130 | #[psp(0x868120B5)]
131 | /// Sets the number of loops for this atrac ID
132 | ///
133 | /// # Parameters
134 | ///
135 | /// - `atrac_id`: the atracID
136 | /// - `nloops`: the number of loops to set
137 | ///
138 | /// # Return Value
139 | ///
140 | /// < 0 on error, otherwise 0
141 | pub fn sceAtracSetLoopNum(
142 | atrac_id: i32,
143 | nloops: i32,
144 | ) -> i32;
145 |
146 | #[psp(0x61EB33F5)]
147 | /// It releases an atrac ID
148 | ///
149 | /// # Parameters
150 | ///
151 | /// - `atrac_id`: the atrac ID to release
152 | ///
153 | /// # Return Value
154 | ///
155 | /// < 0 on error
156 | pub fn sceAtracReleaseAtracID(atrac_id: i32) -> i32;
157 |
158 | #[psp(0x36FAABFB)]
159 | /// Gets the number of samples of the next frame to be decoded.
160 | ///
161 | /// # Parameters
162 | ///
163 | /// - `atrac_id`: the atrac ID
164 | /// - `out_n`: pointer to receives the number of samples of the next frame.
165 | ///
166 | /// # Return Value
167 | ///
168 | /// < 0 on error, otherwise 0
169 | ///
170 | pub fn sceAtracGetNextSample(
171 | atrac_id: i32,
172 | out_n: *mut i32,
173 | ) -> i32;
174 |
175 | #[psp(0xD6A5F2F7)]
176 | /// Gets the maximum number of samples of the atrac3 stream.
177 | ///
178 | /// # Parameters
179 | ///
180 | /// - `atrac_id`: the atrac ID
181 | /// - `out_max`: pointer to a integer that receives the maximum number of samples.
182 | ///
183 | /// # Return Value
184 | ///
185 | /// < 0 on error, otherwise 0
186 | ///
187 | pub fn sceAtracGetMaxSample(
188 | atrac_id: i32,
189 | out_max: *mut i32,
190 | ) -> i32;
191 |
192 | #[psp(0xCA3CA3D2)]
193 | pub fn sceAtracGetBufferInfoForReseting(
194 | atrac_id: i32,
195 | ui_sample: u32,
196 | pbuffer_info: *mut Atrac3BufferInfo,
197 | ) -> i32;
198 |
199 | #[psp(0x31668BAA)]
200 | pub fn sceAtracGetChannel(
201 | atrac_id: i32,
202 | pui_channel: *mut u32,
203 | ) -> i32;
204 |
205 | #[psp(0xE88F759B)]
206 | pub fn sceAtracGetInternalErrorInfo(
207 | atrac_id: i32,
208 | pi_result: *mut i32,
209 | ) -> i32;
210 |
211 | #[psp(0xFAA4F89B)]
212 | pub fn sceAtracGetLoopStatus(
213 | atrac_id: i32,
214 | pi_loop_num: *mut i32,
215 | pui_loop_status: *mut u32,
216 | ) -> i32;
217 |
218 | #[psp(0xE23E3A35)]
219 | pub fn sceAtracGetNextDecodePosition(
220 | atrac_id: i32,
221 | pui_sample_position: *mut u32,
222 | ) -> i32;
223 |
224 | #[psp(0x83E85EA0)]
225 | pub fn sceAtracGetSecondBufferInfo(
226 | atrac_id: i32,
227 | pui_position: *mut u32,
228 | pui_data_byte: *mut u32,
229 | ) -> i32;
230 |
231 | #[psp(0xA2BBA8BE)]
232 | pub fn sceAtracGetSoundSample(
233 | atrac_id: i32,
234 | pi_end_sample: *mut i32,
235 | pi_loop_start_sample: *mut i32,
236 | pi_loop_end_sample: *mut i32,
237 | ) -> i32;
238 |
239 | #[psp(0x644E5607)]
240 | pub fn sceAtracResetPlayPosition(
241 | atrac_id: i32,
242 | ui_sample: u32,
243 | ui_write_byte_first_buf: u32,
244 | ui_write_byte_second_buf: u32,
245 | ) -> i32;
246 |
247 | #[psp(0x0E2A73AB)]
248 | pub fn sceAtracSetData(
249 | atrac_id: i32,
250 | puc_buffer_addr: *mut u8,
251 | ui_buffer_byte: u32,
252 | ) -> i32;
253 |
254 | #[psp(0x3F6E26B5)]
255 | pub fn sceAtracSetHalfwayBuffer(
256 | atrac_id: i32,
257 | puc_buffer_addr: *mut u8,
258 | ui_read_byte: u32,
259 | ui_buffer_byte: u32,
260 | ) -> i32;
261 |
262 | #[psp(0x0FAE370E)]
263 | pub fn sceAtracSetHalfwayBufferAndGetID(
264 | puc_buffer_addr: *mut u8,
265 | ui_read_byte: u32,
266 | ui_buffer_byte: u32,
267 | ) -> i32;
268 |
269 | #[psp(0x83BF7AFD)]
270 | pub fn sceAtracSetSecondBuffer(
271 | atrac_id: i32,
272 | puc_second_buffer_addr: *mut u8,
273 | ui_second_buffer_byte: u32,
274 | ) -> i32;
275 | }
276 |
--------------------------------------------------------------------------------
/psp/src/sys/codec.rs:
--------------------------------------------------------------------------------
1 | psp_extern! {
2 | #![name = "sceVideocodec"]
3 | #![flags = 0x4001]
4 | #![version = (0x00, 0x11)]
5 |
6 | #[psp(0xC01EC829)]
7 | pub fn sceVideocodecOpen(
8 | buffer: *mut u32,
9 | type_: i32,
10 | ) -> i32;
11 |
12 | #[psp(0x2D31F5B1)]
13 | pub fn sceVideocodecGetEDRAM(
14 | buffer: *mut u32,
15 | type_: i32,
16 | ) -> i32;
17 |
18 | #[psp(0x17099F0A)]
19 | pub fn sceVideocodecInit(
20 | buffer: *mut u32,
21 | type_: i32,
22 | ) -> i32;
23 |
24 | #[psp(0xDBA273FA)]
25 | pub fn sceVideocodecDecode(
26 | buffer: *mut u32,
27 | type_: i32,
28 | ) -> i32;
29 |
30 | #[psp(0x4F160BF4)]
31 | pub fn sceVideocodecReleaseEDRAM(buffer: *mut u32) -> i32;
32 | }
33 |
34 | pub enum AudioCodec {
35 | At3Plus = 0x00001000,
36 | At3 = 0x00001001,
37 | Mp3 = 0x00001002,
38 | Aac = 0x00001003,
39 | }
40 |
41 | psp_extern! {
42 | #![name = "sceAudiocodec"]
43 | #![flags = 0x4009]
44 | #![version = (0x00, 0x00)]
45 |
46 | #[psp(0x9D3F790C)]
47 | pub fn sceAudiocodecCheckNeedMem(
48 | buffer: *mut u32,
49 | type_: i32,
50 | ) -> i32;
51 |
52 | #[psp(0x5B37EB1D)]
53 | pub fn sceAudiocodecInit(
54 | buffer: *mut u32,
55 | type_: i32,
56 | ) -> i32;
57 |
58 | #[psp(0x70A703F8)]
59 | pub fn sceAudiocodecDecode(
60 | buffer: *mut u32,
61 | type_: i32,
62 | ) -> i32;
63 |
64 | #[psp(0x3A20A200)]
65 | pub fn sceAudiocodecGetEDRAM(
66 | buffer: *mut u32,
67 | type_: i32,
68 | ) -> i32;
69 |
70 | #[psp(0x29681260)]
71 | pub fn sceAudiocodecReleaseEDRAM(buffer: *mut u32) -> i32;
72 | }
73 |
--------------------------------------------------------------------------------
/psp/src/sys/ctrl.rs:
--------------------------------------------------------------------------------
1 | bitflags::bitflags! {
2 | /// Enumeration for the digital controller buttons.
3 | ///
4 | /// # Note
5 | ///
6 | /// Home, Note, Screen, VolUp, VolDown, Disc, WlanUp, Remote, and MS can only be
7 | /// read in kernel mode.
8 | #[derive(Default)]
9 | #[repr(transparent)]
10 | pub struct CtrlButtons: u32 {
11 | /// Select button.
12 | const SELECT = 0x000001;
13 | /// Start button.
14 | const START = 0x000008;
15 | /// Up D-Pad button.
16 | const UP = 0x000010;
17 | /// Right D-Pad button.
18 | const RIGHT = 0x000020;
19 | /// Down D-Pad button.
20 | const DOWN = 0x000040;
21 | /// Left D-Pad button.
22 | const LEFT = 0x000080;
23 | /// Left trigger.
24 | const LTRIGGER = 0x000100;
25 | /// Right trigger.
26 | const RTRIGGER = 0x000200;
27 | /// Triangle button.
28 | const TRIANGLE = 0x001000;
29 | /// Circle button.
30 | const CIRCLE = 0x002000;
31 | /// Cross button.
32 | const CROSS = 0x004000;
33 | /// Square button.
34 | const SQUARE = 0x008000;
35 | /// Home button. In user mode this bit is set if the exit dialog is visible.
36 | const HOME = 0x010000;
37 | /// Hold button.
38 | const HOLD = 0x020000;
39 | /// Music Note button.
40 | const NOTE = 0x800000;
41 | /// Screen button.
42 | const SCREEN = 0x400000;
43 | /// Volume up button.
44 | const VOL_UP = 0x100000;
45 | /// Volume down button.
46 | const VOL_DOWN = 0x200000;
47 | /// Wlan switch up.
48 | const WLAN_UP = 0x040000;
49 | /// Remote hold position.
50 | const REMOTE = 0x080000;
51 | /// Disc present.
52 | const DISC = 0x1000000;
53 | /// Memory stick present.
54 | const MEM_STICK = 0x2000000;
55 | }
56 | }
57 |
58 | /// Controller mode.
59 | #[repr(u32)]
60 | pub enum CtrlMode {
61 | /// Digital.
62 | Digital = 0,
63 | /// Analog.
64 | Analog
65 | }
66 |
67 | #[repr(C)]
68 | #[derive(Debug, Clone, Copy, Default)]
69 | /// Returned controller data
70 | pub struct SceCtrlData {
71 | /// The current read frame.
72 | pub timestamp: u32,
73 | /// Bit mask containing zero or more of `CtrlButtons`.
74 | pub buttons: CtrlButtons,
75 | /// Analogue stick, X axis.
76 | pub lx: u8,
77 | /// Analogue stick, Y axis.
78 | pub ly: u8,
79 | /// Reserved.
80 | pub rsrv: [u8; 6],
81 | }
82 |
83 | #[repr(C)]
84 | #[derive(Debug, Clone, Copy, Default)]
85 | pub struct SceCtrlLatch {
86 | pub ui_make: u32,
87 | pub ui_break: u32,
88 | pub ui_press: u32,
89 | pub ui_release: u32,
90 | }
91 |
92 | psp_extern! {
93 | #![name = "sceCtrl"]
94 | #![flags = 0x4001]
95 | #![version = (0, 0)]
96 |
97 | #[psp(0x6A2774F3)]
98 | /// Set the controller cycle setting.
99 | ///
100 | /// # Parameters
101 | ///
102 | /// - `cycle`: Cycle. Normally set to 0.
103 | ///
104 | /// # Return value
105 | ///
106 | /// The previous cycle setting.
107 | pub fn sceCtrlSetSamplingCycle(cycle: i32) -> i32;
108 |
109 | #[psp(0x02BAAD91)]
110 | /// Get the controller current cycle setting.
111 | ///
112 | /// # Parameters
113 | ///
114 | /// - `pcycle`: Return value.
115 | ///
116 | /// # Return value
117 | ///
118 | /// 0
119 | pub fn sceCtrlGetSamplingCycle(pcycle: *mut i32) -> i32;
120 |
121 | #[psp(0x1F4011E6)]
122 | /// Set the controller mode.
123 | ///
124 | /// # Parameters
125 | ///
126 | /// - `mode`: One of `CtrlMode`.
127 | ///
128 | /// # Return Value
129 | ///
130 | /// The previous mode.
131 | pub fn sceCtrlSetSamplingMode(mode: CtrlMode) -> i32;
132 |
133 | #[psp(0xDA6B76A1)]
134 | /// Get the current controller mode.
135 | ///
136 | /// # Parameters
137 | ///
138 | /// - `pmode`: Return value.
139 | ///
140 | /// # Return value
141 | ///
142 | /// 0
143 | pub fn sceCtrlGetSamplingMode(pmode: *mut i32) -> i32;
144 |
145 | #[psp(0x3A622550)]
146 | pub fn sceCtrlPeekBufferPositive(pad_data: *mut SceCtrlData, count: i32) -> i32;
147 |
148 | #[psp(0xC152080A)]
149 | pub fn sceCtrlPeekBufferNegative(pad_data: *mut SceCtrlData, count: i32) -> i32;
150 |
151 | #[psp(0x1F803938)]
152 | /// Read buffer positive
153 | ///
154 | /// # Parameters
155 | ///
156 | /// - `pad_data`: Pointer to a `SceCtrlData` structure used to hold the returned pad data.
157 | /// - `count`: Number of `SceCtrlData` buffers to read.
158 | pub fn sceCtrlReadBufferPositive(pad_data: *mut SceCtrlData, count: i32) -> i32;
159 |
160 | #[psp(0x60B81F86)]
161 | pub fn sceCtrlReadBufferNegative(pad_data: *mut SceCtrlData, count: i32) -> i32;
162 |
163 | #[psp(0xB1D0E5CD)]
164 | pub fn sceCtrlPeekLatch(latch_data: *mut SceCtrlLatch) -> i32;
165 |
166 | #[psp(0x0B588501)]
167 | pub fn sceCtrlReadLatch(latch_data: *mut SceCtrlLatch) -> i32;
168 |
169 | #[psp(0xA7144800)]
170 | /// Set analog threshold relating to the idle timer.
171 | ///
172 | /// # Parameters
173 | ///
174 | /// - `idlereset`: Movement needed by the analog to reset the idle timer.
175 | /// - `idleback`: Movement needed by the analog to bring the PSP back from
176 | /// an idle state.
177 | ///
178 | /// Set to -1 for analog to not cancel idle timer.
179 | ///
180 | /// Set to 0 for idle timer to be cancelled even if the analog is not moved.
181 | ///
182 | /// Set between 1-128 to specify the movement on either axis needed by the analog
183 | /// to fire the event.
184 | ///
185 | /// # Return value
186 | ///
187 | /// < 0 on error.
188 | pub fn sceCtrlSetIdleCancelThreshold(idlereset: i32, idleback: i32) -> i32;
189 |
190 | #[psp(0x687660FA)]
191 | /// Get the idle threshold values.
192 | ///
193 | /// # Parameters
194 | ///
195 | /// - `idlereset`: Movement needed by the analog to reset the idle timer.
196 | /// - `idleback`: Movement needed by the analog to bring the PSP back from
197 | /// an idle state.
198 | ///
199 | /// # Return value
200 | ///
201 | /// < 0 on error.
202 | pub fn sceCtrlGetIdleCancelThreshold(idlereset: *mut i32, idleback: *mut i32) -> i32;
203 | }
204 |
--------------------------------------------------------------------------------
/psp/src/sys/debugfont.bin:
--------------------------------------------------------------------------------
1 | ((( ((|(|(( x8P< L d` T$X @ @ @ T88T | | @ 8DdTLD8 | 8D@ | 8D@0@D8 0($| |<@@D8 0