TheMrIron2 The PSP was Sony's first and biggest foray into the handheld console scene. While it was remembered for being a powerful system with a plethora of good games of console quality, it was also hacked rapidly and the homebrew scene grew at an absurd rate. The PSP scene reached incredible highs and remains one of the consoles with the biggest and proudest homebrew catalogs, being hackable in minutes with a USB cable and with software from a near-fullspeed N64 emulator, all of the standard homebrew goodies and some more interesting things such as a universal remote program, a screen mirroring program so you can play PC games on PSP and even a broken, simple port of nullDC - the Dreamcast emulator.

So as such a massively famous system in the hacking scene, I think it would be good if I introduced you to the PSP as a coding companion. It will certainly serve you well, because it's a perfect programming system; unlike the GBA, it supports easier languages like Python more easily while also allowing for C code to be easily and effectively compiled. It doesn't give you all the overhead you would like, but it gives you enough for 3D experiments. It allows you to go down to the metal as far as C will allow you, and has its own set of nice APIs, but they are also largely avoidable although you won't quite have the same bare-metal experience as PSP.

Hardware
Without further ado, a reminder of the PSP's specs so you know what you're working with. Unlike the GBA, the PSP has a GPU. This allows us to enter a whole new world of programming; once you get comfortable, you can try your hand at accelerating your programs by using the GPU and doing hardware 3D rendering for maximum efficiency, but thankfully the PSP has enough horsepower to brute-force some 3D rendering without the need for a GPU. The PSP's CPU is a MIPS based CPU, similar to the PS2's, and runs at any desired clockspeed from 1 to 333MHz. Generally, these are preset to 222, 266, 300 and 333MHz. The GPU and memory bus are affected by the chosen CPU speed, however, as they operate at exactly half the speed at all times. So at 333MHz, the bus and GPU are operating at 166MHz. The PSP also has a generous 32MB of RAM available, and 4MB of VRAM. This is the same as the PS2, in fact. However, PSP models after the original PSP-1000 have 64MB of RAM. So while commercial games could only utilise 32MB, homebrew games can access practically all of this memory, though some is still reserved for system tasks.

[​IMG]
So we have lots of generous hardware overhead if we ever want to venture into 3D; that's great! But for now, we'll be sticking with simple demos to ease us into this.

Setting Up

Setting everything up is a little less straightforward. The PSP SDK on GitHub is still receiving regular updates, however I have heard that it's currently not as stable as it could be - for example, a friend of mine was baffled when trying to compile DaedalusX64 for PSP for a solid week until he realised the current toolchain on GitHub was the problem. So I recommend checking this version out instead as the last stable version to be safe.

To use this on Windows, I recommend setting up a Linux virtual machine or Cygwin because currently the resources for Windows are scarce and just inferior to Linux's toolset, unfortunately. So it's your best bet. There is an excellent tutorial on setting this up with the PSP toolchain on Wikibooks here, and I strongly advise reading those instructions to set it up. If you are already running Linux, simply download the above toolchain.

Makefile

A makefile is simply a file we use to tell the compiler some information about our program and allowing it to build the program on command. Here is a template for a makefile:


Code:
TARGET = my_program
OBJS   = main.o myLibrary.o

INCDIR   =
CFLAGS   = -G0 -Wall -O2
CXXFLAGS = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS  = $(CFLAGS)

LIBDIR  =
LDFLAGS =
LIBS    = -lm

BUILD_PRX = 1

EXTRA_TARGETS   = EBOOT.PBP
PSP_EBOOT_TITLE = My Program
PSP_EBOOT_ICON= ICON0.png
PSP_EBOOT_PIC1= PIC1.png
PSP_EBOOT_SND0= SND0.at3

PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
It's not too bad, you don't actually have to touch most of this at all. You can change names, such as "My Program", and if you're really looking for a performance gain you can change up flags such as changing -O2 to -O3 for a more aggressive compiler to optimise your program. However, we won't need to change much at all. The "target" tells the compiler the name of our source file, and "objs" to create a "main.o" and "myLibrary.o" as we are using "main.c" as the file where we program the code and “myLibrary.c” as a helper class.

Next we tell it to build a PRX binary rather than a static ELF binary. Static ELFs are generally depreciated, and only PRX-based homebrew can be signed to run on any PSP firmware. Afterwards we also create an "EBOOT.PBP" which is the executable, like an EXE on Windows. Then you give it the title and have an option of an icon (144x80), a background picture (480x272), and a PSP sound file (at3). If you don't want one of these, then simply delete the lines.

Callbacks

Callbacks are common files that will be used frequently in our programs and will make our lives easier in future so we don't have to re-write the same code. Create a folder called "common" in your programming directory so we can store some callbacks here.

Let's create a callback called "callback.h". This is a header file, and it will define and declare a few things so our program can run.

Code:
#ifndef COMMON_CALLBACK_H
#define COMMON_CALLBACK_H

int isRunning();
int setupExitCallback();

#endif
The 'ifndef' is used to make sure that this file only gets imported once, otherwise an error will occur. Then it should be pretty self-explanatory; we will have two functions which we will use: "isRunning()" to check if the user has requested to quit the program, and "setupCallbacks()" which will setup all the necessary things for your program to run on the PSP.

That's all for "callback.h". You can save and close that now. Now that we have the header definitions we can also create the source file: name it "callback.c". We'll start by including the file "pspkernel.h" which gives us access to several kernel methods.

Code:
#include <pspkernel.h>
Next, we create a boolean. A boolean is a simple true or false statement. Executing the method "isRunning()" will tell us whether a request to exit the application was created by the user. We will use this function in our program so that we can clean up any leftover memory and exit the program without crashing it.

Code:
static int exitRequest  = 0;

int isRunning()
{
    return !exitRequest;
}
Simple so far, as you can see. Well, the next part is a bit more complicated. But don't worry, you don't need to understand it. This basically creates a new thread, which creates a callback for exiting our program. The callback will then make "exitRequest" = true if the user presses the "home" and "exit" button on their PSP, and I'm sure that's self explanatory. Here's the complete code for "callback.c":

Code:
[SPOILER]#include <pspkernel.h>

static int exitRequest = 0;

int isRunning()
{
    return !exitRequest;
}

int exitCallback(int arg1, int arg2, void *common)
{
    exitRequest = 1;
    return 0;
}

int callbackThread(SceSize args, void *argp)
{
    int callbackID;

    callbackID = sceKernelCreateCallback("Exit Callback", exitCallback, NULL);
    sceKernelRegisterExitCallback(callbackID);

    sceKernelSleepThreadCB();

    return 0;
}

int setupExitCallback()
{
    int threadID = 0;

    threadID = sceKernelCreateThread("Callback Update Thread", callbackThread, 0x11, 0xFA0, THREAD_ATTR_USER, 0);
    
    if(threadID >= 0)
    {
        sceKernelStartThread(threadID, 0, 0);
    }

    return threadID;
}
[/SPOILER]

Again, understanding it isn't too important right now, since this won't change and it will simply be included in our programs in future for easy exiting.

Hello World

We have already gone through a lot, and we have set down some foundations for our future PSP projects! That's actually a lot of the hard part. The next part is simple; we will make a simple template for a program and throw a "Hello World" into it.

We'll start by including “pspkernel.h” which will allow us to exit the application, "pspdebug.h" so that we can get a simple debug screen started, "pspdisplay.h" for "sceDisplayWaitVblankStart" function - so we can synchronise the screen timing with V-Blanks, as we covered in the GBA programming post - and "callback.h" of course so that the user can quit at any time by pressing 'home' and then 'exit'.

Code:
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspdisplay.h>

#include "../common/callback.h"
Next part is also quite simple; we simply give the PSP some details about our program.

Code:
#define VERS    1 //Talk about this
#define REVS    0

PSP_MODULE_INFO("Hello World", PSP_MODULE_USER, VERS, REVS);
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
PSP_HEAP_SIZE_MAX();

#define printf pspDebugScreenPrintf
In PSP_MODULE_INFO we tell the PSP the name of our program, as well as any attributes and the version of our program. We don't need to get too deep into this part. I have also defined "printf" as "pspDebugScreenPrintf". "printf" is a simple function in C to print text to the screen, and this is just simplifying the longer version of this code from the PSP APIs. So that way, when the program is being compiled, "printf" will simply act as an alias for "pspDebugScreenPrintf". Simple! That's definitions for you. Next part:

Code:
int main()
{       
    pspDebugScreenInit();
    setupExitCallback();

    while(isRunning())
    {
        pspDebugScreenSetXY(0, 0);
        printf("Hello World!");
        sceDisplayWaitVblankStart();
    }

    sceKernelExitGame();   
    return 0;
}
This is the main "loop" of our program, as the "int main()" suggests. This is the heart of the code in a C program. First, we will initialize the debug screen, and setup our callbacks. Then inside the loop we place the position to write to at (0,0) so that printing doesn't go to the next line and print out our message. Then to prevent screen tearing (ie. ensuring that the screen delivers frames with perfect consistency to avoid a "tearing" effect) we call “sceDisplayWaitVblankStart”. Once the user quits and the loop is broken (remember we are using the "isRunning()" method), we do a last call to "sceKernelExitGame()" which will exit our application and return zero to close the program.

Now if you are using an IDE that can also compile your PSP programs, you can hit compile and put the "EBOOT.PBP" in a folder on your PSP, and then run it. If on the other hand you choose to do things manually, then we will have to create the Makefile before compiling. I'll be making the Makefile for the sake of covering it in this tutorial. Remember we talked about one of these earlier? Well, let's flesh it out a bit!

Code:
TARGET        = hello_world
OBJS        = main.o ../common/callback.o

INCDIR        =
CFLAGS        = -G0 -Wall -O2
CXXFLAGS    = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS    = $(CFLAGS)

LIBDIR        =
LDFLAGS    =
LIBS        = -lm

BUILD_PRX = 1

EXTRA_TARGETS    = EBOOT.PBP
PSP_EBOOT_TITLE= Hello World

PSPSDK    = $(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
So we've added the name of our program and the path we want to compile it to in our Makefile. Now we simply call the "make" command on our toolchain and we have compiled our first program! This EBOOT file will boot on a real PSP. Place it in PSP:/PSP/GAME/helloworld/ and it will appear in the games section of your PSP! (You can also boot it using an emulator.)

That was cool! It's great to see our work pay off like this in such a nice way. As you can see, the PSP is quite a standardised platform and things have to be named and set up. But it's worth it, because the final result looks proudly official and complete.

Button Input

So in the second tutorial we will be learning how to work with button inputs. Let's start with our headers again. It's the same deal as before, except this time we're including "pspctrl.h" so we can accept button inputs.

Code:
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>

The code here is the same, aside from changing the name and version number. 


[CODE]#define VERS    2
#define REVS    1

PSP_MODULE_INFO("Button Input", PSP_MODULE_USER, VERS, REVS);
PSP_MAIN_THREAD_ATTR(PSP_THREAD_ATTR_USER);
PSP_HEAP_SIZE_MAX();

#define printf pspDebugScreenPrintf
Now for the main part of our program again.

Code:
int main()
{       
    pspDebugScreenInit();
    setupExitCallback();

    int running = isRunning();

    SceCtrlData buttonInput;

    sceCtrlSetSamplingCycle(0);
    sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);
In our main loop we do the usual, but this time create a variable called running so that we can update it if the exit callback occurs or if the user presses the button 'start'. Then we create the button input object and set the sampling rate to 0, and allow analog mode so that we can read where the analog pad is.

Code:
    while(running)
    {
        running = isRunning();

        pspDebugScreenSetXY(0, 0);
        printf("Analog X = %d ", buttonInput.Lx);
        printf("Analog Y = %d \n", buttonInput.Ly);
Now we start a while loop and update our variable from the exit callback telling us if we should quit or not. We reset the position to print, and print out the analog pad position. Printing the variable is done by passing it in the second parameter and using '%d' as a placeholder for it.

One thing to remember, when you get the position of the analog stick using the 'Lx' and 'Ly' properties, you get a value from 0 to 255. You can subtract 128 from that so that the center would be 0. However, there is a chance that the program will pick up an analog position because of the stick's "dead zone". This is when the analog stick is ever so slightly off or imperfectly positioned, and our program is picking it up.

Code:
        sceCtrlPeekBufferPositive(&buttonInput, 1);

        if(buttonInput.Buttons != 0)
        {
            if(buttonInput.Buttons & PSP_CTRL_START){
                                    printf("Start");
                                    running = 0;
            } 
Okay, so this is the idea for our program. As you can infer from the code above, if we are looking for button inputs and the start button is pressed, we print "Start". We also set running to 0; by changing the state of "running" from true to false (or 1 to 0 in this case), we can stop our program smoothly. Let's continue with the other buttons.

Code:
if(buttonInput.Buttons & PSP_CTRL_SELECT)    printf("Select");

if(buttonInput.Buttons & PSP_CTRL_UP)        printf("Up");
            if(buttonInput.Buttons & PSP_CTRL_DOWN)        printf("Down");
            if(buttonInput.Buttons & PSP_CTRL_RIGHT)    printf("Right");
            if(buttonInput.Buttons & PSP_CTRL_LEFT)        printf("Left");

            if(buttonInput.Buttons & PSP_CTRL_CROSS)    printf("Cross");
            if(buttonInput.Buttons & PSP_CTRL_CIRCLE)    printf("Circle");
            if(buttonInput.Buttons & PSP_CTRL_SQUARE)    printf("Square");
            if(buttonInput.Buttons & PSP_CTRL_TRIANGLE)    printf("Triangle");
    
            if(buttonInput.Buttons & PSP_CTRL_RTRIGGER)    printf("R-Trigger");
            if(buttonInput.Buttons & PSP_CTRL_LTRIGGER)    printf("L-Trigger");
        }
    }
At the end we close our program. You may notice that we don't check for all buttons, and this is because we need kernel control to check for those buttons. That may be covered in a future tutorial, but for now that's it. Here's a final overview of the code:

Warning: Spoilers inside!

And that's it! Let's set up a makefile for this so we can compile it.

Code:
TARGET        = ButtonInput
OBJS        = main.o ../common/callback.o

INCDIR        =
CFLAGS        = -O2 -G0 -Wall
CXXFLAGS    = $(CFLAGS) -fno-exceptions -fno-rtti
ASFLAGS    = $(CFLAGS)

BUILD_PRX    = 1

LIBDIR        = ./
LIBS        = -lm
LDFLAGS    =

EXTRA_TARGETS        = EBOOT.PBP
PSP_EBOOT_TITLE    = ButtonInput

PSPSDK    = $(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak
Now we run "make" and we should have our second PSP program!

That wraps up this tutorial. Since a lot of new concepts were introduced, such as makefiles, callbacks and such, I thought we would leave it at this for now. (It's also almost 1AM for me)
I hope you guys enjoyed this tutorial as well and if people like this, I'll keep making them! Though admittedly my knowledge is pretty rusty and I'm pretty reliant on pre-existing resources at this point. :P
If you enjoyed it, please like it, tell me what you think in the comments and follow me to get notified of further posts. Thanks for reading!

6 Comments

  • jimmyj
  • TheMrIron2
  • crimsonwolf8439
  • TheMrIron2
  • DBlaze
  • TheMrIron2
You need to be logged in to comment