PSP Development/User Input

Tutorial AnalysisEdit

This article builds off of the previous article, Hello World Application. Is is advised that you start with and understand the code from that article. This specific section's goal is to go over how the user input (UI) system works with the PSP.

UI System OverviewEdit

The PSPSDK libraries provide an interface between the Hardware and Program. This interface queries the hardware and gives you the result. This result is then checked using the AND operator (&) and a number which represents the button. Because of how the bits are aligned, the result returned can be thought of boolean bits, and thus a purpose of the AND operator. An example of this logic is 1010 could be button1 pressed, button2 not pressed, button 3 pressed, button4 not pressed. The PSP system has two ways to check states: a latch method, and a state method.

The typical flow for UI checking is

  1. initialize sampling
  2. query hardware for joystick information
  3. query hardware for key information
  4. utilize key and joystick information

Getting SetupEdit

This section details additions to the file structure needed will. These changes are mandatory and should NOT be skipped. Although renaming things is possible, it may cause headache.

FoldersEdit

Before starting, copying the hello_world project folder from the previous article and renaming it user_input will save time. Discarding the object files, EBOOT.PBP, and elf file is safe to do - they are of no at the moment. Discarding the object files in projects/common is not necessary.

FilesEdit

Updating the Makefile is the first task. Changing the project to suit our new environment is a good practice.

  1. Change TARGET to user_input
  2. Change PSP_EBOOT_TITLE to UserInput
  3. Under OBJS have main.o and ../common/callback.o
  4. Inside of main.c, switch the string in the call PSP_MODULE_INFO() to UserInput
  5. Empty the main.c of everything but a basic stencil as shown below

main.c

int main(int argc, char** argv)
{
	// basic init
	setupExitCallback();
	pspDebugScreenInit();

	while (isRunning()) {
		pspDebugScreenSetXY(0, 0);
		// empty
		sceDisplayWaitVblankStart();
	}
	// close out
	sceKernelExitGame();	
	return 0;
}

User input is a perfect example of a common module, as the other article facilitated. These two files are to be used in all applications where user input via the controller is a must.

  1. Navigate to the common folder
  2. Create a compilation unit called ui.c and ui.h
  3. Include ui.h in ui.c
  4. Include ui.h in main.c
  5. Head over to the project Makefile and append ../common/ui.o to the OBJS variable

Below is what the files should have.

ui.c

#include "ui.h"

ui.h

#ifndef UI_H
#define UI_H

#endif

Querying the HardwareEdit

Th next task is to implement methods. Anything controller related requires the include pspctrl.h. Once the information is acquired from the hardware, handling it, utilizing the ui compilation units is the next step.

Terminology

  • key hold - key press/release fires once per state switch, ie key down - key up
  • key latch - holding down a key fires constant actions per frame, ie key hold down - key hold up

Two sets of information are attainable - hold or latch. The latch method has the key information the hold method information has. The only thing adding the hold method will bring you is the joystick and timestamp information. This article will demonstrate both methods.

Key HoldEdit

  1. Include pspctrl.h in pspctrl.c
  2. Add a variable (struct) SceCtrlData data
  3. Add the function int getJX() for joystick X
  4. Add the function int getJY() for joystick Y
  5. Add the function void pollPad() for querying hardware

ui.c

#include <pspctrl.h>

static SceCtrlData data;

int getJX() {

}

int getJY() {

}

void pollPad() {

}

ui.h

int getJX();
int getJY();

void pollPad();

pollPad()'s purpose is to query the hardware once per frame when called. The hardware must be queried before any attempt to check for user input, and to ensure old data is not being used. The function used to read data into the SceCtrlData from hardware is sceCtrlReadBufferPositive(), which takes in the memory address of the SceCtrlData and the amount of buffer reading desired. It is only necessary to read one. There are two different types of hardware poll functions. Read and Peek. The sceCtrlPeekBufferPositive() does not pop the user input information. Using sceCtrlPeekBufferPositive(), one could check user input in multiple compilation units separately, polling the hardware each time. A use of the peek and read function is implementing an undo feature, or in game moves that are button-order dependent.

ui.c

void pollPad() {
	sceCtrlReadBufferPositive(&data, 1);
}

The SceCtrlData structure has values: int TimeStamp, int Buttons, int Lx, and int Ly. The TimeStamp is for telling the current read frame, which isn't utilized by this article. The Buttons is an integer with bit-booleans of any current pressed keys, which isnt utilized by this article, for utilization of the latch method's. The Lx and Ly variables pair with getJX() and getJY() functions respectively, for the functions expose the joystick information. LX and Ly are signed char-length numbers which aren't guaranteed to be perfectly in the middle. The middle is 128 X and 128 Y.

ui.c

int getJX() {
	return data.Lx;
}

int getJY() {
	return data.Ly;
}

Key LatchEdit

Depending on the program, key latch is a vital component to use. This method uses a different struct called SceCtrlLatch. SceCtrlData will be another static member to the ui compilation unit, with pollLatch() reading from the hardware. SceCtrlLatch stores information about the buttons cumulatively as the bit-boolean number in SceCtrlData - ie two buttons down could look like 1100.

ui.c

#include <pspctrl.h>

static SceCtrlLatch latch;

void pollLatch() {

}

ui.h

void pollLatch();

While sceCtrlReadBufferPositive() is to hold, sceCtrlReadLatch() is to latch. Latch only requires one argument - a reference to the latch data structure. Latch has only a few members: uiMake, uiBreak, uiPress, and uiRelease.

void pollLatch() {
	sceCtrlReadLatch(&latch);
}

In order to check for a key being press, the bit-booleans need AND'd with the key being checked. If the result comes out 1, the key being checking is down, else it is 0 and up - colloquially, 'is key down' would be true and 'is key up' false.

Observe the sceCtrlReadBufferPositive() (binary) pattern

0100 // bit-booleans
0001 & // AND key-0001
0000 // key-0001 is not down
0101 // bit-booleans
0001 & // AND key-0001
0001 // key-0001 is down

A helper function is necessary to check for hold method input, for the struct is not visible in main.c. The SceCtrlLatch members need to be accessed. In C, 0 is false and 1 is true for boolean checking. Latch has fields uiPress and uiRelease. uiPress is the bit-booleans, which represent the current keys down. uiRelease is -1 - uiPress. Only the uiPress is currently relevant. The function requests a key, and ANDs it with the current keys held to generate the key hold method. A proper function would be isKeyHold().

ui.c

int isKeyHold(int key) {
	return latch.uiPress & key;
}

ui.h

int isKeyHold(int key); // returns 1(true) if key is down

For the latch method, uiMake and uiBreak fields are used. When any number of keys have been pressed at once, uiMake will update from 0 to the bit-booleans of all the keys pressed for only that frame. When a any number of keys have been released at once, uiBreak will update from 0 to the bit-booleans of all the keys released for only that frame. If the keys 1011 were held at once, uiMake will be 1011 and in the next frame, if no other keys were pressed, be 0000. If only the key 0010 was released in a following frame, uiBreak will be 0010 and the following frame, if no other keys were released, will be 0000. If the keys 1001 were released together in a following frame, uiBreak will be 1001. Proper functions would be isKeyDown() and isKeyUp().

ui.c

int isKeyDown(int key) {
	return (latch.uiMake & key);
}

int isKeyUp(int key) {
	return (latch.uiBreak & key);
}

ui.h

int isKeyDown(int key);
int isKeyUp(int key);

Putting it All TogetherEdit

A simple example task is when a key is pressed, it will print a message - saying the held key is down, up for a frame, or down for a frame. Below is a list of possible keys that can be checked. While the list isn't fully comprehensive, certain keys can only be checked in Kernel mode. It is strongly recommended to stay away from Kernel mode.

  • PSP_CTRL_SELECT
  • PSP_CTRL_START
  • PSP_CTRL_UP
  • PSP_CTRL_RIGHT
  • PSP_CTRL_DOWN
  • PSP_CTRL_LEFT
  • PSP_CTRL_LTRIGGER
  • PSP_CTRL_RTRIGGER
  • PSP_CTRL_TRIANGLE
  • PSP_CTRL_CIRCLE
  • PSP_CTRL_CROSS
  • PSP_CTRL_SQUARE

There are two function that need to be called, which edit the sampling cycle and sampling mode. The two functions are sceCtrlSetSamplingCycle() and sceCtrlSetSamplingMode(). These need to be called before any user input checking, early in the program before the main loop. sceCtrlSetSamplingCycle() takes in an int, which is commonly 0. sceCtrlSetSamplingMode() takes in an enum, which is commonly PSP_CTRL_MODE_ANALOG.

main.c

	sceCtrlSetSamplingCycle(0);
	sceCtrlSetSamplingMode(PSP_CTRL_MODE_ANALOG);

To demonstrate the functions, poll the current key status from the hardware. Print if the cross key is up and down, if the circle key being held, and the current joystick axis positions.

main.c

	while (isRunning()) {
		sceDisplayWaitVblankStart();
		pspDebugScreenClear();
		pspDebugScreenSetXY(0, 0);
		pollPad();
		pollLatch();
		if(isKeyDown(PSP_CTRL_CROSS))
			printf("Cross is down!\n");
		if(isKeyUp(PSP_CTRL_CROSS))
			printf("Cross is up!\n");
		if(isKeyHold(PSP_CTRL_CIRCLE))
			printf("Circle is down!\n");
		printf("%d,%d", getJX(), getJY());
	}

The MakefileEdit

TARGET = user_input
OBJS = main.o ../common/callback.o ../common/ui.o

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

LIBDIR =
LIBS = 
LDFLAGS =

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = UserInput

PSPSDK=$(shell psp-config --pspsdk-path)
include $(PSPSDK)/lib/build.mak

Build by opening Windows Command Line or Terminal to the project directory and executing make EBOOT.PBP. Launch the EBOOT.PBP in PPSSPP. PPSSPP may require controller mapping configuration to the keyboard before input is possible.

Working ExampleEdit