I. Introduction : 

Dans toutes les applications que l'on peut créer pour la PSP, il faut penser que l'utilisateur doit pouvoir quitter l'application. Pour cela, il faut paramétrer la manière dont ce fait cette fermeture de programme.

Nous allons donc voir de manière succinte le mécanisme des callbacks et des threads adaptés à notre besoin.

 

II. Convention de développement : 

Pour commencer, on va créer 2 fichiers : smartCallbacks.h et smartCallbacks.c.

Le premier contiendra les déclaration de types et les déclarations de fonctions, tandis que le second contiendra le code de toutes les fonctions déclarées dans le header (fichier .h).

Le fichier source (.c) incluera le fichier header (.h) ainsi que tous les headers nécessaires au bon développement de notre librairie.

Chaque type de données que nous déclarons sera préfixé par le mot-clé smart et les fonctions par le nom de la librairie ici smartCallbacks.

Les nom de variables seront toujours explicites afin de ne pas commettre d'erreurs lors de l'écriture du code.

 

III. Comment initialiser correctement une application PSP ?

Afin de pouvoir quitter correctement notre homebrew, il nous faut quelque chose qui puisse le faire à notre demande. On va donc utiliser une callback : c'est une fonction qui se déclenche lors d'un évènement. Dans notre cas, l'évènement est l'appui sur la touche HOME.

Cependant, ce n'est pas à nous de détecter cet évènement mais au kernel de la PSP. En effet, il existe une fonction permettant de définir qu'elle est la callback à appeler pour la fermeture du programme : sceKernelRegisterExitCallback.

Enfin, pour ne pas avoir de problème avec l'éxecution du reste de notre code, on lance un thread en parallèle pour s'occuper de toute cette configuration.

Notre système a donc besoin de 3 fonctions : la première va créer et lancer le thread d'initialisation, la seconde est le thread d'initialisation qui va créer et enregistrer la callback d'arrêt et la troisième c'est notre callback d'arrêt.

 

a) smartCallbacks_exitCallback :

Pour commencer, nous allons déclarer une fonction permettant d'ajouter une ligne dans un fichier : smartCallbacks_exitCallback.

Il suffit de mettre cette ligne dans smartCallbacks.h :

1
int smartCallbacks_exitCallback(void);

 

Le contenu de cette callback est simple, il suffit d'appeler la fonction sceKernelExitGame pour en faire une callback d'arrêt.

Voici donc le code de notre callback, à placer dans smartCallbacks.c :

1
2
sceKernelExitGame();
return 0;

 

b) smartCallbacks_thread :

Pour que cette callback puisse être appelée, nous allons écrire une fonction qui va servir de thread et qui va configurer notre callback pour que le kernel de la PSP l'appelle au bon moment : smartCallbacks_thread.

On rajoute la déclaration de notre fonction dans smartCallbacks.h :

1
int smartCallbacks_thread(SceSize args, void *argp); 

Le premier paramètre est le nombre d'arguments passés au thread et le second paramètre contient tous les paramètres qui lui sont passés. Tous les threads que vous voudrez créer doivent impérativement avoir ces 2 paramètres.

 

Maintenant, on peut s'attaquer au code de cette fonction.

D'abord, il nous faut déclarer une variable de type int pour stocker l'identifiant de la callback que nous allons créer.

1
int callback_id;

 

Pour créer une callback et obtenir un identifiant, il faut faire appel à la fonction sceKernelCreateCallback. Le premier paramètre est un nom que l'on choisit et peut donc être n'importe quoi : ici nous l'appellerons "Exit Callback". Le second paramètre est le nom de la fonction qui va être défini comme étant une callback : dans notre cas, il s'agit de smartCallbacks_exitCallback. Enfin, le dernier paramètre contient les arguments que l'on veut passer à notre callback : ici rien donc on passera la valeur NULL.

1
callback_id=sceKernelCreateCallback("Exit Callback", smartCallbacks_exitCallback, NULL);

 

Ensuite, il nous faut "enregistrer" la callback comme étant une callback d'arrêt en appelant la fonction sceKernelRegisterExitCallback. Cela a pour effet de notifier au kernel que notre callback doit être appelée lorsque l'utilisateur désire quitter le programme. D'ailleurs, sans l'enregistrement de cette callback, il est impossible de quitter (en appelant sceKernelExitGame) un homebrew sans faire crasher le système.

1
sceKernelRegisterExitCallback(callback_id);

 

Pour ne pas que le thread se termine et donc empêche l'arrêt de notre homebrew, nous devons l'endormir sans que cela affecte l'éxecution de callbacks : on utilise la fonction sceKernelSleepThreadCB.

1
sceKernelSleepThreadCB();

 

c) smartCallbacks_setup :

Pour terminer, il nous faut écrire une fonction qui va créer le thread à partir de la fonction que l'on a vu précedement et qui va le démarrer : smartCallbacks_setup.

On rajoute donc cette ligne dans notre fichier smartCallbacks.h :

1
int smartCallbacks_setup(void);

 

Lorsque l'on programme avec des threads, il nous faut toujours au moins une variable qui va contenir l'identifiant d'un thread. Nous déclarons donc un entier que l'on nomme thread_id (ou ce que vous voulez) :

1
int thread_id=0;

 

Maintenant, il nous faut créer le thread au moyen de la fonction sceKernelCreateThread. Cette fonction prend en paramètre un nom que l'on choisit, le nom de la fonction faisant office de thread, le niveau de priorité, la taille de la pile (stackSize), des attributs (ou données) à passer au thread et des paramètres additionnels.

1
thread_id=sceKernelCreateThread("init_thread", smartCallbacks_thread, 0x11, 0xFA0, 0, 0);

 

Il faut vérifier si le thread a été créé correctement : thread_id doit être supérieur ou égal à 0. Dans ce cas nous pouvons démarrer le thread à l'aide de sceKernelStartThread :

1
2
3
4
if(thread_id>=0)
{
	sceKernelStartThread(thread_id, 0, 0);
}

 

Enfin, il nous faut retourner thread_id afin que l'on puisse effectuer les traitements adéquats an cas de problème :

1
return thread_id;

 

IV. Exemple d'utilisation : 

Voici le code source d'un programme de type Hello World qui utilise notre librairie :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <pspkernel.h>
#include <pspdebug.h>
#include <pspctrl.h>
#include "smartCallbacks.h"

PSP_MODULE_INFO("Hello World", 0, 0, 1);

#define printf pspDebugScreenPrintf

int main(void)
{
	SceCtrlData pad;

	pspDebugScreenInit();
	smartCallbacks_setup();

	printf("Hello World !\n");
	while(1)
	{
		sceCtrlReadBufferPositive(&pad, 1);
		if (pad.Buttons & PSP_CTRL_CROSS)
		{
			sceKernelExitGame();
		} 
		sceKernelSleepThread();
	}

	return 0;
}

 

Et voici le fichier Makefile permettant la compilation. A noter, que pour utiliser la librairie, il faut bien penser à ajouter smartCallbacks.o dans le champ TARGET :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TARGET = hello-world
OBJS = hello-world.o smartCallbacks.o

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

LIBDIR = 
LDFLAGS = 
LIBS = -lpspgum -lpspgu

EXTRA_TARGETS = EBOOT.PBP
PSP_EBOOT_TITLE = Hello World
PSP_FW_VERSION = 372

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

 

V. Code source de la librairie :

 

Dans ce dossier, nous verrons de quelle manière nous pouvons nous simplifier le développement d'applications sur PSP en écrivant des petites librairies : les smartLibs. 

Nous aborderons des thèmes plus ou moins complexes comme un système de fichiers de logs pour les plus simples ou comment on peut lire un fichier image et l'afficher à l'écran pour les plus compliqués.

Pour chacune des librairies, constituant les smartLibs, nous expliquerons les concepts se cachant derrière : le fonctionnement de la mémoire pour l'accès à la VRAM par exemple. Nous détaillerons chaque ligne de code et nous tenterons de donner plusieurs pistes concernant un même traitement : les différents algorithmes permettant de tracer une ligne à l'écran par exemple. Nous fournirons un programme exemple permettant l'utilisation de la librairie étudiée. Enfin, nous finirons, chaque dossier par la mise à disposition du code source de la librairie en téléchargement.

I. Introduction : 

D'une manière générale, lorque l'on développe une application, on emploie la fonction printf pour débugguer ou pour afficher des informations.

Dans le développement sur PSP, nous n'aurons pas toujours l'occasion de pouvoir afficher du texte à l'écran.

En effet, lorsque nous aborderons la partie dédiée aux graphismes sur la PSP, il sera bien difficile de concilier "mode graphique" et "mode texte".

Pour cela, nous allons voir comment mettre en place un système simple nous permettant d'écrire un fichier de log à la place d'un printf.


II. Convention de développement :

Pour commencer, on va créer 2 fichiers : smartLog.h et smartLog.c.

Le premier contiendra les déclaration de types et les déclarations de fonctions, tandis que le second contiendra le code de toutes les fonctions déclarées dans le header (fichier .h). 

Le fichier source (.c) incluera le fichier header (.h) ainsi que tous les headers nécessaires au bon développement de notre librairie.

Chaque type de données que nous déclarons sera préfixé par la mot-clé smart et les fonctions par le nom de la librairie ici smartLog.

Les nom de variables seront toujours explicites afin de ne pas commettre d'erreurs lors de l'écriture du code.

 

III. Comment débugguer une application sans affichage à l'écran ?

Pour commencer, nous allons déclarer une fonction permettant d'ajouter une ligne dans un fichier : smartLog_addLine.

Il suffit de mettre cette ligne dans smartLog.h :

1
void smartLog_addLine(char *line);

 

Maintenant, nous allons nous occuper de son code.

Pour faire simple, notre fonction va écrire le contenu de la variable line dans un fichier que l'on va nommé smartlog.txt.

Pour cela, il nous faut ouvrir un fichier en mode a (append) afin de pouvoir ajouter une ligne à chaque fois et ne pas écraser ce que l'on a mis de dans précedemment.

Ensuite, on écrit le contenu de line dans le fichier ouvert puis on ferme le fichier.

Ce qui nous donne la fonction suivante :

1
2
3
4
5
6
7
8
void smartLog_addLine(char *line)
{
	FILE *fdesc;

	fdesc=fopen("smartlog.txt", "a");
	fwrite(line, 1, strlen(line), fdesc);
	fclose(fdesc);
}

 

Pour que cela fonctionne, il nous faut inclure stdio.h et smartLog.h:

1
2
#include <stdio.h>
#include "smartLog.h"

 

IV. Améliorations possibles :

Comme nous venons de le voir, notre librairie est très simple. C'est un avantage indéniable lorsque l'on ne veut seulement que quelques informations.

Par contre, cela devient un peu difficile lorsque l'on se met à programmer des applications plus complexes et que l'on a besoin de plusieurs fichiers de logs : applications multi-threadées par exemple.

Là, il faudra penser ou repenser la librairie afin de permettre la gestion multi-fichiers.

De même, il peut-être envisager de ré-écrire la fonction afin que le message qu'on passe puisse être formaté de la même manière que printf.

 

V. Exemple d'utilisation : 

Maintenant, nous allons voir comment utiliser notre librairie dans un programme simple.

Le programme suivant est dérivé de celui expliquant l'utilisation des touches de la PSP : Exemples SDK - controller - basic.

Pour commencer, on va supposer que savez comment on utilise le controleur de touches de la PSP.

Afin de pouvoir utiliser notre librairie, il nous faut l'inclure dans notre code source :

1
#include "smartLog.h"

 

Au début du programme principal main, nous allons déclarer une chaine de 500 caractères (ce qui est suffisant) qui contiendra le message que l'on veut envoyer dans le fichier de log.

1
char msg[500];

 

Enfin, dans notre exemple, à chaque appui sur une des touches TRIANGLE, CROSS, SQUARE ou CIRCLE nous allons envoyer le nom de la touche dans le log.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
switch(pad.Buttons)
{
	case PSP_CTRL_SQUARE:
		sprintf(msg, "Bouton : CARRE (num: %d)\n", pad.Buttons);
		smartLog_addLine(msg);
		break;
	case PSP_CTRL_TRIANGLE:
		sprintf(msg, "Bouton : CARRE (num: %d)\n", pad.Buttons);
		smartLog_addLine(msg);
		break;
	case PSP_CTRL_CIRCLE:
		sprintf(msg, "Bouton : CARRE (num: %d)\n", pad.Buttons);
		smartLog_addLine(msg);
		break;
	case PSP_CTRL_CROSS:
		sprintf(msg, "Bouton : CARRE (num: %d)\n", pad.Buttons);
		smartLog_addLine(msg);
		break;
	default:
		break;
}

 

ATTENTION, si vous voulez ouvrir correctement le fichier sous WINDOWS, il suffit de remplacer à la fin de chaque ligne '\n' par '\r\n'. 

Enfin, il nous faut faire référence à smartLog dans notre Makefile sous peine de voir des erreurs de compilation du type "Undefined Reference...".