Skip to content

Commit

Permalink
Added vitasdk example
Browse files Browse the repository at this point in the history
  • Loading branch information
nikarh committed Mar 4, 2024
1 parent dfe2140 commit 1281395
Show file tree
Hide file tree
Showing 13 changed files with 328 additions and 7 deletions.
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"rust-analyzer.cargo.target": "armv7-sony-vita-newlibeabihf"
"rust-analyzer.cargo.target": "armv7-sony-vita-newlibeabihf",
"rust-analyzer.cargo.extraEnv": {
"PKG_CONFIG_SYSROOT_DIR": "/opt/vitasdk",
"PKG_CONFIG_PATH": "/opt/vitasdk/arm-vita-eabi/lib/pkgconfig",
}
}
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;
197 changes: 197 additions & 0 deletions crates/4-vitasdk/src/debug/screen.rs
Original file line number Diff line number Diff line change
@@ -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::<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;
}
}
}
55 changes: 55 additions & 0 deletions crates/4-vitasdk/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<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" for some reason
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 1281395

Please sign in to comment.