diff --git a/Cargo.lock b/Cargo.lock index c08a1df..a6e9238 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -952,8 +952,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.33.0" -source = "git+https://github.com/vita-rust/tokio?branch=vita#44b0b05024b375298ea68437816a2533a875b355" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -967,8 +968,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.1.0" -source = "git+https://github.com/vita-rust/tokio?branch=vita#44b0b05024b375298ea68437816a2533a875b355" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", @@ -1130,6 +1132,14 @@ dependencies = [ "sdl2", ] +[[package]] +name = "vita-example-vitasdk" +version = "0.1.0" +dependencies = [ + "rand", + "vitasdk-sys", +] + [[package]] name = "vita-newlib-shims" version = "0.3.1" @@ -1150,6 +1160,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "vitasdk-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "665d8a491ad572e0ebc103846c4a51e66b641c942391b7241f91c413a3a301a9" + [[package]] name = "want" version = "0.3.1" diff --git a/Cargo.toml b/Cargo.toml index f31279a..89a8785 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,5 @@ lto = true opt-level = 2 [patch.crates-io] -tokio = { git = "https://github.com/vita-rust/tokio", branch = "vita" } - # Required for rustls ring = { git = "https://github.com/vita-rust/ring", branch = "v0.16.20-vita" } diff --git a/crates/4-vitasdk/Cargo.toml b/crates/4-vitasdk/Cargo.toml new file mode 100644 index 0000000..55bce98 --- /dev/null +++ b/crates/4-vitasdk/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "vita-example-vitasdk" +version = "0.1.0" +edition = "2021" + +license = "MIT OR Apache-2.0" +repository = "https://github.com/vita-rust/examples" +homepage = "https://github.com/vita-rust/examples/crates/4-vitasdk" + +description = "VITASDK example" + +[dependencies] +vitasdk-sys = { version = "0.3.3", features = ["SceDisplay_stub", "SceSysmem_stub"] } +rand = "0.8.5" + +[package.metadata.vita] +title_id = "RUSTTEST4" +title_name = "VITASDK test" +assets = "./static" diff --git a/crates/4-vitasdk/src/debug/font.bin b/crates/4-vitasdk/src/debug/font.bin new file mode 100644 index 0000000..9b332fc Binary files /dev/null and b/crates/4-vitasdk/src/debug/font.bin differ diff --git a/crates/4-vitasdk/src/debug/font.rs b/crates/4-vitasdk/src/debug/font.rs new file mode 100644 index 0000000..6ccba7b --- /dev/null +++ b/crates/4-vitasdk/src/debug/font.rs @@ -0,0 +1,19 @@ +pub struct DebugFont { + pub glyphs: &'static [u8], + pub width: usize, + pub height: usize, + pub first: u8, + pub last: u8, + pub size_w: usize, + pub size_h: usize, +} + +pub const DEBUG_FONT: DebugFont = DebugFont { + glyphs: include_bytes!("font.bin"), + width: 8, + height: 8, + first: 0, + last: 255, + size_w: 8, + size_h: 8, +}; diff --git a/crates/4-vitasdk/src/debug/mod.rs b/crates/4-vitasdk/src/debug/mod.rs new file mode 100644 index 0000000..f6b8fd3 --- /dev/null +++ b/crates/4-vitasdk/src/debug/mod.rs @@ -0,0 +1,2 @@ +pub mod font; +pub mod screen; diff --git a/crates/4-vitasdk/src/debug/screen.rs b/crates/4-vitasdk/src/debug/screen.rs new file mode 100644 index 0000000..034fea9 --- /dev/null +++ b/crates/4-vitasdk/src/debug/screen.rs @@ -0,0 +1,197 @@ +use core::ffi::c_void; +use core::fmt::{Result, Write}; +use core::mem::size_of; +use core::ptr; + +use vitasdk_sys::{ + sceDisplaySetFrameBuf, sceKernelAllocMemBlock, sceKernelFreeMemBlock, sceKernelGetMemBlockBase, + SceDisplayFrameBuf, SceUID, SCE_DISPLAY_SETBUF_NEXTFRAME, + SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, +}; + +use super::font::DEBUG_FONT; + +const SCREEN_WIDTH: usize = 960; +const SCREEN_HEIGHT: usize = 544; +const SCREEN_PIXEL_COUNT: usize = SCREEN_WIDTH * SCREEN_HEIGHT; +const SCREEN_FB_WIDTH: usize = 960; +const SCREEN_FB_SIZE: usize = 2 * 1024 * 1024; +const SCREEN_TAB_SIZE: usize = 4; // Tab size in number of characters +const SCREEN_TAB_W: usize = DEBUG_FONT.size_w * SCREEN_TAB_SIZE; + +const DEFAULT_FG: u32 = 0xFFFFFFFF; +const DEFAULT_BG: u32 = 0xFF000000; + +pub struct DebugScreen { + // TODO: rename to pixel array or something like that + framebuffer: Framebuffer, + coord_x: usize, + coord_y: usize, + color_fg: u32, + color_bg: u32, +} + +pub struct Framebuffer { + buf: *mut u32, + block_uid: SceUID, +} + +impl Framebuffer { + pub fn new() -> Framebuffer { + // Allocate memory to use as display buffer + let mut base: *mut c_void = ::core::ptr::null_mut(); + let block_uid = unsafe { + let block_uid: SceUID = sceKernelAllocMemBlock( + b"display\0".as_ptr() as *const i8, + SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, + SCREEN_FB_SIZE as u32, + ::core::ptr::null_mut(), + ); + sceKernelGetMemBlockBase(block_uid, &mut base); + block_uid + }; + Framebuffer { + buf: base as *mut u32, + block_uid, + } + } + + pub fn set_display(&mut self) { + // Sets buffer as current display frame + let frame = SceDisplayFrameBuf { + size: size_of::() as u32, + base: self.buf as *mut c_void, + pitch: SCREEN_FB_WIDTH as u32, + pixelformat: 0, + width: SCREEN_WIDTH as u32, + height: SCREEN_HEIGHT as u32, + }; + unsafe { + sceDisplaySetFrameBuf(&frame, SCE_DISPLAY_SETBUF_NEXTFRAME); + } + } + + #[allow(unused)] + pub fn get(&self, index: usize) -> u32 { + if index > SCREEN_PIXEL_COUNT { + panic!("Invalid framebuffer index"); + } + unsafe { ptr::read_volatile(self.buf.offset(index.try_into().unwrap())) } + } + + pub fn set(&mut self, index: usize, value: u32) { + if index > SCREEN_PIXEL_COUNT { + panic!("Invalid framebuffer index"); + } + unsafe { ptr::write_volatile(self.buf.offset(index.try_into().unwrap()), value) } + } +} + +impl Drop for Framebuffer { + fn drop(&mut self) { + let _error_code = unsafe { sceKernelFreeMemBlock(self.block_uid) }; + } +} + +impl Write for DebugScreen { + fn write_str(&mut self, s: &str) -> Result { + self.puts(s.as_bytes()); + Ok(()) + } +} + +impl DebugScreen { + pub fn new() -> Self { + let mut framebuffer = Framebuffer::new(); + framebuffer.set_display(); + Self { + framebuffer, + coord_x: 0, + coord_y: 0, + color_fg: DEFAULT_FG, + color_bg: DEFAULT_BG, + } + } + + #[allow(unused)] + fn clear(&mut self, from_h: usize, to_h: usize, from_w: usize, to_w: usize) { + for h in from_h..to_h { + for w in from_w..to_w { + self.framebuffer.set(h * SCREEN_FB_WIDTH + w, self.color_bg); + } + } + } + + fn puts(&mut self, text: &[u8]) { + let bytes_per_glyph = DEBUG_FONT.width * DEBUG_FONT.height / 8; + + for &chr in text.iter() { + if chr == b'\t' { + self.coord_x += SCREEN_TAB_W - (self.coord_x % SCREEN_TAB_W); + continue; + } + + // Go to next line at the end of the current line + if self.coord_x + DEBUG_FONT.width > SCREEN_WIDTH { + self.coord_y += DEBUG_FONT.size_h; + self.coord_x = 0; + } + + // Go to screen top when at the bottom of the screen + if self.coord_y + DEBUG_FONT.height > SCREEN_HEIGHT { + self.coord_x = 0; + self.coord_y = 0; + } + + if chr == b'\n' { + self.coord_x = 0; + self.coord_y += DEBUG_FONT.size_h; + continue; + } else if chr == b'\r' { + self.coord_x = 0; + continue; + } + + let current_offset = self.coord_x + self.coord_y * SCREEN_FB_WIDTH; + let mut font = + &DEBUG_FONT.glyphs[(chr - DEBUG_FONT.first) as usize * bytes_per_glyph..]; + let mut mask = 1 << 7; + + for row in 0..DEBUG_FONT.height { + for col in 0..DEBUG_FONT.width { + if mask == 0 { + font = &font[1..]; + mask = 1 << 7; + } + + self.framebuffer.set( + current_offset + row * SCREEN_FB_WIDTH + col, + if font[0] & mask == 0 { + self.color_bg + } else { + self.color_fg + }, + ); + + mask >>= 1; + } + + #[allow(clippy::reversed_empty_ranges)] + for col in DEBUG_FONT.width..DEBUG_FONT.size_w { + self.framebuffer + .set(current_offset + row * SCREEN_FB_WIDTH + col, self.color_bg) + } + } + + #[allow(clippy::reversed_empty_ranges)] + for row in DEBUG_FONT.height..DEBUG_FONT.size_h { + for col in 0..DEBUG_FONT.size_w { + self.framebuffer + .set(current_offset + row * SCREEN_FB_WIDTH + col, self.color_bg) + } + } + + self.coord_x += DEBUG_FONT.size_w; + } + } +} diff --git a/crates/4-vitasdk/src/main.rs b/crates/4-vitasdk/src/main.rs new file mode 100644 index 0000000..7aed85b --- /dev/null +++ b/crates/4-vitasdk/src/main.rs @@ -0,0 +1,55 @@ +use std::backtrace::Backtrace; +use std::fmt::Write; +use std::panic::PanicInfo; +use std::thread; +use std::time::Duration; + +mod debug; + +pub fn main() { + std::panic::set_hook(Box::new(custom_panic_hook)); + + let mut screen = debug::screen::DebugScreen::new(); + writeln!(screen, "This not-so-bare-metal is starting to rust!").ok(); + thread::sleep(Duration::from_secs(2)); + writeln!(screen, "See? Told ya!").ok(); + thread::sleep(Duration::from_secs(2)); + + let random_numbers: Vec = (0..8).map(|_i| rand::random::()).collect(); + writeln!(screen, "Some random numbers: {:?}", random_numbers).ok(); + + thread::sleep(Duration::from_secs(5)); +} + +fn custom_panic_hook(info: &PanicInfo<'_>) { + // The current implementation always returns `Some`. + let location = info.location().unwrap(); + + let msg = match info.payload().downcast_ref::<&'static str>() { + Some(s) => *s, + None => match info.payload().downcast_ref::() { + Some(s) => &s[..], + None => "Box", + }, + }; + let name = "unknown"; + + let mut screen = debug::screen::DebugScreen::new(); + + writeln!( + screen, + "thread '{}' panicked at '{}', {}", + name, msg, location + ) + .ok(); + + // Give 2 seconds to see the error in case capturing the stack trace fails + // (capturing the stack trace allocates memory) + thread::sleep(Duration::from_secs(2)); + + // The backtrace is full of "unknown" for some reason + let backtrace = Backtrace::force_capture(); + writeln!(screen, "{}", backtrace).ok(); + + thread::sleep(Duration::from_secs(10)); +} diff --git a/crates/4-vitasdk/static/sce_sys/icon0.png b/crates/4-vitasdk/static/sce_sys/icon0.png new file mode 100644 index 0000000..e78f766 Binary files /dev/null and b/crates/4-vitasdk/static/sce_sys/icon0.png differ diff --git a/crates/4-vitasdk/static/sce_sys/livearea/contents/bg.png b/crates/4-vitasdk/static/sce_sys/livearea/contents/bg.png new file mode 100644 index 0000000..ffa42be Binary files /dev/null and b/crates/4-vitasdk/static/sce_sys/livearea/contents/bg.png differ diff --git a/crates/4-vitasdk/static/sce_sys/livearea/contents/startup.png b/crates/4-vitasdk/static/sce_sys/livearea/contents/startup.png new file mode 100644 index 0000000..3c3a7d7 Binary files /dev/null and b/crates/4-vitasdk/static/sce_sys/livearea/contents/startup.png differ diff --git a/crates/4-vitasdk/static/sce_sys/livearea/contents/template.xml b/crates/4-vitasdk/static/sce_sys/livearea/contents/template.xml new file mode 100644 index 0000000..a4d43f0 --- /dev/null +++ b/crates/4-vitasdk/static/sce_sys/livearea/contents/template.xml @@ -0,0 +1,11 @@ + + + + + bg.png + + + + startup.png + +