tutorial:hello_world

Hello World!

While the Hello World! Application is usually the first stepping stone for any beginner programmer, it is also a great tool for verifying that everything is set up correctly and that you're ready to get started with developing your application - whatever that may be. This tutorial is the bare minimum and shows us the steps for setting up a PSP program.

To run a PSP application, your project binary will call into the module_start function which is meant as a general purpose starting point. In all three of our language toolchains, this is obscured by a wrapper that calls your main function. In order for the program loader to load the program, you need to include module information, which is typically generated as a macro at compile time. This will set up the necessary sections in the PRX to be parsed by the program loader. Then, our application is responsible for setting up a home button callback and then doing its mission. In this case, the mission is to print to the framebuffer, which all three toolchains have wrapper functions to use. Knowing this bit of background information, let's see how these programs are implemented.

#include <pspkernel.h>
#include <pspdebug.h>
 
PSP_MODULE_INFO("Tutorial", 0, 1, 0);
 
int exit_callback(int arg1, int arg2, void* common){
	sceKernelExitGame();
	return 0;
}
 
int CallbackThread(SceSize args, void* argp) {
	int cbid = sceKernelCreateCallback("Exit Callback", exit_callback, NULL);
	sceKernelRegisterExitCallback(cbid);
	sceKernelSleepThreadCB();
 
	return 0;
}
 
int SetupCallbacks(void) {
	int thid = sceKernelCreateThread("update_thread", CallbackThread, 0x11, 0xFA0, 0, 0);
	if (thid >= 0) {
		sceKernelStartThread(thid, 0, 0);
	}
	return thid;
}
 
int main() { //In C++ `auto main() -> int` is also valid.
	SetupCallbacks();
	pspDebugScreenInit();
	pspDebugScreenPrintf("Hello World!");
	return 0;
}

As you'll notice, the code for C and C++ is much longer than Rust or Zig due to the amount of extra boilerplate to setup callbacks. The other toolchains provide a simple utility to handle this default use-case. Going over the general ideas: after the initial include of headers, there is a macro call to PSP_MODULE_INFO which pastes in a module_info structure for our program with the specified information in the order of name, mode, major version, and minor version. This is followed by our main function, which sets up the callbacks, initializes the screen, and draws the text to it through the pspDebug API.

Build: You will need a makefile to use the PSP makefile system.

A generic makefile for PSP development is:

TARGET = Tutorial
OBJS = main.o
 
CFLAGS = 
CXXFLAGS = $(CFLAGS) -std=c++14 -fno-rtti
ASFLAGS = $(CFLAGS)
 
# PSP Stuff
BUILD_PRX = 1
PSP_FW_VERSION = 500
PSP_LARGE_MEMORY = 1
 
EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = Tutorial
#PSP_EBOOT_ICON = ICON0.PNG 
 
PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak

Then, to build the EBOOT:

make
#![no_std]
#![no_main]
 
psp::module!("Tutorial", 1, 0);
 
pub fn psp_main() {
    psp::enable_home_button();
 
    psp::dprintln!("Hello World!");
}

Rust's code is a lot simpler and cleaner than C/C++ thanks to the enable_home_button call, and starts by marking to the compiler that there is no standard library and no main. It uses the PSP crate (the psp:: namespace/module) to set up module info through a Rust macro, with name, major version, and minor version. It then continues to the alternatively named psp_main where it makes a simple call to enable the home button and then calls another macro, dprintln!, to print to the framebuffer.

Build:

cargo psp
const psp = @import("psp/utils/psp.zig");
 
comptime {
    asm(psp.module_info("Tutorial", 0, 1, 0));
}
 
pub fn main() !void {
    psp.utils.enableHBCB();
    psp.debug.screenInit();
 
    psp.debug.print("Hello World!");
}

Zig's code is also much cleaner than C/C++ due to the enableHBCB call. The application starts by importing the psp library and then uses a comptime block to call the module_info macro which returns the proper assembly code to an asm() statement. It takes in the name, mode, major and minor version in the same way that C/C++ does. Next, it declares a main function and enables the home button with a psp.utils.enableHBCB() and then initializes the screen with psp.debug.screenInit(). Finally, it prints using psp.debug.print to print to the screen. It's important to note that the psp.debug API is similar to that of the C version.

Build:

zig build
  • tutorial/hello_world.txt
  • Last modified: 2020/10/21 17:04
  • by iridescence