This tutorial covers retrieving button and analog inputs from the PSP's controls which is the primary form of input in any application running on the PSP! This tutorial will hopefully be useful for anybody wanting to do more PSP programming in the future!

To detect input we first need to set up the sceCtrl module's sampling cycle (when it checks for input) via sceCtrlSetSamplingCycle(0) where 0 is the default argument. Then we need to make sure to set the sampling mode to analog for ease of use with the measurement of the Analog stick. If this was digital, it would result in a lack of granularity - either the analog stick is moved or not - rather than the given output. In our main program loop we then must read the control data via sceCtrlReadBufferPositive() into an sceCtrlData structure to get the actual button data. Finally, we compare to see if the bitflag is set for the control we're interested in and perform an action.

#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
PSP_MODULE_INFO("Tutorial", 0, 1, 0);
 * Callback code omitted - check the last tutorial
int main() { //In C++ `auto main() -> int` is also valid.
        struct SceCtrlData padData;
             if(padData.Buttons & PSP_CTRL_CROSS){
                   pspDebugScreenPrintf("X Pressed!\n");
        return 0;

There's nothing much to comment about in C & C++ because the code is rather simple. The one difference from Rust and Zig is that the comparison via & results in a number which is by default checked to 0. If it is not 0, then it is true.

psp::module!("Tutorial", 1, 0);
use psp::{SceCtrlData, CtrlButtons, CtrlMode};
pub fn psp_main() {
         let ctrl_data = &mut SceCtrlData::default();
         loop {
              psp::sys::sceCtrlReadBufferPositive(ctrl_data, 1);
              if ctrl_data.buttons.contains(CtrlButtons::Cross) {
                  psp::dprintln!("X Pressed!");

Using Rust, you'll notice that the code uses a proper enumeration (CtrlButtons, CtrlMode) which makes the code much cleaner looking. The other big thing you should notice is that the code is unsafe - and this is true about almost all of the Rust PSPSDK - the code is unsafe due to the fact that it calls into unsafe code within the PSP OS and driver modules.

const psp = @import("psp/utils/psp.zig");
usingnamespace @import("psp/include/pspctrl.zig");
comptime {
    asm(psp.module_info("Tutorial", 0, 1, 0));
pub fn main() !void {
    _ = sceCtrlSetSamplingCycle(0);
    _ = ctrlSetSamplingMode(PspCtrlMode.Analog);
    var ctrlData: SceCtrlData = undefined;
    while(true) {
         _ = sceCtrlReadBufferPositive(&ctrlData, 1);
         if( (ctrlData.buttons & @enumToInt(PspCtrlButtons.Cross)) != 0) {
              psp.debug.print("X Pressed!\n");

The first thing that should probably be talked about with the Zig example here is the use of _ = which is called discard - meaning that we explicitly discard the return value of the sce functions. These functions actually do have return values that do have meaning - and can be useful! In this context, we don't care about this, therefore we will discard. Zig forces you to do this to make sure return values are either useful and used, or errors are checked. The other thing you may notice is the ctrlSetSamplingMode() call, which is a wrapped version of the sceCtrlSetSamplingMode which allows you to pass in enums instead of casting enums to integers. Finally, the @enumToInt is used in the if statement to cast to an integer so that it can be ANDed to ctrlData.buttons, and the check against zero is explicitly written in Zig.

  • tutorial/input.txt
  • Last modified: 2021/10/12 13:50
  • by korigamik