Skip to content

Commit

Permalink
Added vitasdk example (#4)
Browse files Browse the repository at this point in the history
Closes #3

---------

Co-authored-by: Aphek <[email protected]>
  • Loading branch information
nikarh and pheki authored Mar 6, 2024
1 parent 39674ca commit 7341fbe
Show file tree
Hide file tree
Showing 12 changed files with 326 additions and 6 deletions.
24 changes: 20 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 0 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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" }
19 changes: 19 additions & 0 deletions crates/4-vitasdk/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Binary file added crates/4-vitasdk/src/debug/font.bin
Binary file not shown.
19 changes: 19 additions & 0 deletions crates/4-vitasdk/src/debug/font.rs
Original file line number Diff line number Diff line change
@@ -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,
};
2 changes: 2 additions & 0 deletions crates/4-vitasdk/src/debug/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod font;
pub mod screen;
196 changes: 196 additions & 0 deletions crates/4-vitasdk/src/debug/screen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
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 {
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::<SceDisplayFrameBuf>() 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;
}
}
}
59 changes: 59 additions & 0 deletions crates/4-vitasdk/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::backtrace::Backtrace;
use std::fmt::Write;
use std::panic::{self, PanicInfo};
use std::thread;
use std::time::Duration;

mod debug;

pub fn main() {
let default_hook = panic::take_hook();
panic::set_hook(Box::new(move |info| {
custom_panic_hook(info);
default_hook(info);
}));

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<u8> = (0..8).map(|_i| rand::random::<u8>()).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::<String>() {
Some(s) => &s[..],
None => "Box<Any>",
},
};
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" as there's no elf to parse on the vita
let backtrace = Backtrace::force_capture();
writeln!(screen, "{}", backtrace).ok();

thread::sleep(Duration::from_secs(10));
}
Binary file added crates/4-vitasdk/static/sce_sys/icon0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions crates/4-vitasdk/static/sce_sys/livearea/contents/template.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>

<livearea style="a1" format-ver="01.00" content-rev="1">
<livearea-background>
<image>bg.png</image>
</livearea-background>

<gate>
<startup-image>startup.png</startup-image>
</gate>
</livearea>

0 comments on commit 7341fbe

Please sign in to comment.