I just came back from the Open House event at my school. We had an in-house developed arcade cabinet complete with some games made at the Global Game Jam, and some first year students were showing off their bullet hell shooters. One of the teachers asked me to do a live programming session in exchange for soda & pizza. I just couldn't say no to that :p.
Normally, I would need to show how we would make games with Unity or another game developement tool, but I today I decided to do something more interesting: I made a small game for the Playstation Portable (PSP) in ISO C (rather than in C++) without prior knowledge of PSP programming.
The original SDK is made for Linux, and I happen to run Windows (with no virtual machine). Luckily somebody on the internet ported it, so it is as simple as running an installer. However, the tutorials online on how to create a basic "Hello World!" are a bit lacking (they often explain how to use the code, not what it does nor why to use it).
In this tutorial, I'll explain how to setup the SDK, an in-depth look at how to create an "Hello World!" example, and how to compile + test the application!.
Things you'll need
NOTE: If you use a PSP, I advice you to have multiple memory sticks and no important data on them. It isn't uncommon to screw up the memory stick when messing around.
Installing the SDK
Installing the SDK is as easy as running any other installer. Accept the license agreement, select the stuff you need, select the location for the installation, and press install!.
As for the options you can choose from: the only required one for this tutorial is the PSPSDK. I however, do recommend you to install the HTML documentation, sample projects and devpacks, and do not recommend to install the visual studio support option, as it is made for VS2008.
The offline documentation can be a real lifesaver in many situations, and the samples are a good starting point to learn more about the particular function(s) the PSPSDK has to offer. Some of the devpacks contain some useful functions as well to make your life a lot easier, but I won't cover them in this tutorial.
The samples are located in "\psp\sdk\samples\", and the documentation in "\doc\pspsdk\". The devpacks are located in "\psp\lib\".
Now that you installed everything you need, it is time to open up your favorite text / code editor and start writing a "hello world" application to see if everything works correctly.
First of all, you need some form of callback from the PSP to know when the application should be closed (example: when we press the home button). Let's make a callback.c & callback.h to setup the exit callback.
We include the pspkernel.h so we can get access to kernel functions (callbacks, threads).
The static int exitRequest is used to check whenever the application should be terminated.
isRunning() checks whenever the application needs to keep running. If exitRequest equals 1, isRunning() tells the application to stop running.
The exitCallback(int arg1, int arg2, void *common) function will be executed when it's triggered by the exit callback thread. If that happens, it will tell the application to stop running by setting the exitRequest to 1.
callbackThread(SceSize args, void *argp) function creates the callback named "Exit Callback", with the exitCallback assigned to it, registers the callback and is put to sleep until it's triggered to wake.
at last, setupExitCallback() creates a thread called "Callback Update Thread" with the callbackThead function assigned to it. The attribute type is user mode (0x80000000, THREAD_ATTR_USER in the samples). At last we start the thread and returns the threadID.
You'll also need a header file that exposes the functions you can use.
Now, let's move on and create a main.c file where the main function of the application will be located. For those unfamiliar with basic PSP programming, a (relative) detailed description about what's going on can be found below.
First of all, it includes a bunch of headers containing functions we're using.
pspkernel.h is included because we want to call sceKernelExitGame() in the main function.
pspdebug.h has functions for lots of useful debugging things (pspDebug...)
pspdisplay.h contains functions for the display (sceDisplay...)
PSP_MODULE_INFO(name, module, ver, rev) sets important data for initializing the application.
One of them is the application name, another is the application module, and the application version (major & minor) . This info will be displayed in the PSP information menu, and cannot be dynamically changed in runtime. All this information has to be known at forehand.
In this case, the name of our application is "Hello World". We set the module to user mode (0, PSP_MODULE_USER) as we don't need to mess with the kernel, the version to 1 and the revision to 0.
sce_newlib_attribute (or PSP_MAIN_THREAD_ATTR(attr) as used in the samples) sets the main thread attribute. We set this to user mode (0x80000000, PSP_THREAD_ATTR_USER in the samples), as set the application module is also in user mode.
sce_newlib_heap_kb_size (or PSP_HEAP_SIZE_KB(size) in the samples) gives us access to a certain amount of RAM heap we can use. If we set sce_newlib_heap_kb_size equal to -1 means that we reserve the maximum amount of heap available. Other ways to archieve the same thing are PSP_HEAP_SIZE_KB(-1), and PSP_HEAP_SIZE_MAX(). I highly recommend to set a fixed size for the memory we reserve, so that you always know the total amount of heap we can use.
Now that we set the basic attributes, we can write the main function. both int main(void) and int main(int argc, char *argv) are valid signatures. In this tutorial, we'll use the latter one as it allows us to get some debug information such as the application bootpath. Setting up the debug screen and the exit callback is as simple as calling pspDebugScreenInit(). and setupExitCallback().
We check in the while loop if the application recieved an exit request (if exitRequest in callback.c equals 1), and run the code inside the while loop every frame per second.
For now, the code we run every frame is not much: we set the debug info position coordinates with pspDebugScreenSetXY(int x, int y), and then display "Hello World" with pspDebugScreenPrint(char*). We use VSYNC to avoid screen tearing with sceDisplayWaitVblankStart().
At the end of our application, we exit the application and stop the running processes. To stop the kernel processes (such as the exit callback), we call sceKernelExitGame().
Phew!, that was a lot to explain. Luckily, you can use the code as a template for future projects, as this is code really easy to enhance to suit your needs.
Compiling the code
Before you can compile our code, you'll need a makefile. A makefile contains the instructions for the compiler, so it knows what to compile and how to compile it.
The basic stuff you need to know, is that:
TARGET is the name of the source file.
OBJS tells the compiler to make source objects of the main.c and the callback.c files.
PSP_EBOOT_TITLE is the name of the application appearing on the PSP start screen.
PSP_EBOOT_ICON is the menu icon.
PSP_EBOOT_PIC1 is the menu background.
A PSP menu icon is 144x80 pixels.
A PSP menu background is 480x272 pixels.
The color depth of the PSP display is TrueType (24-bits, R8G8B8).
It doesn't matter what the extention of the makefile is, as long as it's named "makefile" (without quotes).
The more advanced stuff:
BUILD_PRX tells the compiler to make a PRX binary rather than a static ELF library, as ELF's cannot be signed to run on any PSP frimware version. Note that PRX binaries in EBOOT files are different than PRX binaries in kernel mods.
EXTRA_TARGETS = EBOOT.PBP tells the compiler to make an EBOOT.PBP of the PRX.
include tells the compiler to include the PSPSDK's build.mak file which the compiler needs.
For example: c:/pspsdk/lib/build.mak.
CFLAGS contains c compiler flags .
CXXFLAGS contains c++ compiler flags.
ASFLAGS contains MIPS VI assambly flags.
LIBS this is where you link all the libraries.
C COMPILER FLAGS
-G(Level) enables debugging. G0 negates the G compiler flag .
-Wall enables all relevant error messages.
-Wextra enables even more error messages (optional).
-O(level) enables optimization flags. Requires -G flag to function.
C++ COMPILER FLAGS
-fno-exceptions disables exception handling.
-fno-rtti disables RTTI.
-lm the c math library.
There are more options you can use (INCDIR, LIBDIR, LDFLAGS, PSP_EBOOT_SND0), but I won't cover them here. Check the PSPSDK for more information on them.
For more information on the compiler flags, check the GCC documentation. It also has additional compiler flags not covered here, so I highly recommend you to check it out.
After you created the makefile, you want to compile the code using the makefile. Open up command prompt with administrator rights, and enter the commands provided below. Replace "LocationOfTheMakefile" to the directory containing the makefile.
Command Prompt (Administrator rights)
What these comands do, is first preventing command prompt from displaying where command prompt execute the commands, then it navigate to the folder where your makefile is located, compiles the code using the makefile, and clean up all the garbage made by the compiler.
If there is a compile error, it will be shown in the console as well. Try to fix warning messages as much as possible.
Automating the compiling process
As you probably already noticed, compiling the code that way above leaves a lot to be desired. The need to enter the command manually is a waste of your time. Luckily you can write a batch script that does all the work for you.
The batch script navigates to the directory you're running the batch file from, compiles the code using the makefile, cleans up all the garbage the compiler, creates a /build/<timestamp>/ folder and moves the EBOOT.PBP to it.
Testing the application
Now that we've compiled the application, we want to check if it actually runs and/or doesn't contain any errors in runtime. This is the part where we'll need the PSP / emulator. I highly recommend you to use a real PSP instead of an emulator, as the emulator won't be able to detect errors that you might run into with a real PSP.
To get the application running on the PSP itself is quite easy: Connect the PSP (with a memory stick inside) with the computer using the mini-usb to usb. Then, copy the <timestamp> folder to /PSP/GAME/. The application can be found under "Game".
The PPSSPP emulator however, requires a little bit more work. You obviously want to render the game as how it will look on a real PSP. In order to achieve this, we go to "Game-Settings" > "Window size" and set it to "1x". you also set "Render resolution" to "1x"
To load the EBOOT.PHP we compiled, press "File" > "Open" and open the EBOOT.PHP inside the <timestamp> folder.
If you followed the tutorial without any issues, your PSP / emulator will display the same thing as the image shown above. That's a lot of code for such a simple thing, I know. But heck, seeing those tiny white letters with a black background appear on my PSP makes me feel damn satisfied.
With this tutorial, you've learned how to install the PSPSDK on Windows, how to write a simple "Hello world!" application, how to edit a typical makefile, how to compile the code, and how to test your application!.
I hope you where able to follow my tutorial without (many) complications, and most of all.... that you enjoyed programming for the PSP :).
[rev2]: added more information on what's happening in the makefile, fixed compiler warnings
[rev1]: corrected grammar mistakes, updated screenshots