Leggere il codice sorgente
Dopo aver visto le basi della programmazione C, siamo finalmente pronti a compilare il nostro primo programma Homebrew, comunque vi consiglio di ricercare un po' in internet guide più dettagliate sulla programmazione C, questo perchè sia per motivi di spazio che di tempo vi ho solo riassunto la complessità del sistema di programmazione.
Prendo come esempio un modello di esempio del wii e non più del camecube.
Example-WiiAprendo con un editor il file "template.c" situato nella cartella "/(percorso)/DevkiPRO/wii-example/template/source/" vedrete la sorgente del codice.
Da notare subito
CODICE
#include <stdio.h>
#include <stdlib.h>
#include <gccore.h>
#include <wiiuse/wpad.h>
Questi non sono altro che file inclusi nella nostra sorgente. Una serie di funzioni precompilate che semplificano la stesura del nostro codice.
Esistono 2 modi di inclusione il primo come nell'esempio quando i file non sono nella cartella corrente e il seconto come da notare qui sotto quando sono nella stessa cartella.
CODICE
#include "esempio.h"
Ora analiziamo i varii file:
Contiene gli standar delle librerie Input e Output che ci permette di stampare il testo a schermo, leggere e scrivere file.
CODICE
#include <stdlib.h>
Dichiara funzioni e costanti di utilità generale: allocazione della memoria, controllo dei processi, conversione tra tipi e così via.
CODICE
#include <gccore.h>
#include <wiiuse/wpad.h>
Sono le librerie per il funzionamento del programma sullo standard del Wii, l'uso del pad ecc.
Queste quattro librerie sono il minimo indispensabile per il buon funzionamento delle applicazioni Homebrew.
CODICE
static void *xfb = NULL;
static GXRModeObj *rmode = NULL;
Le seguenti variabili al momento le tralasciamo perchè servono solamente a mostrare il video sullo schermo.
CODICE
int main(int argc, char **argv) {
Questa riga è la riga principale del nostro codice al di sotto di questa vi troviamo tutti i comandi e variabili che il nostro programma deve svolgere.
la maggior parte del codice sottostante sono funzioni video e quindi le tralasciamo perchè le tratteremo più in la, quindi saltiamo alla seguente riga:
CODICE
printf("Hello World!");
Se avete letto attentamente la guida precedente capirete subito che questa riga non fa altro che stampare a video la scritta "Hello Woprd!".
Poi vi è la creazione di un ciclo infinito.
Questa funzione racchiusa nella precedente libreria "wiiuse/wpad.h" dice al sistema che vogliamo utilizzare il controller Wii e quindi mette in ascolto i pulsanti premuti o rilasciati sul wiimote, questo è sempre necessario inserirlo in un ciclo quando abbiamo bisogno di un imput dal controller.
CODICE
u32 pressed = WPAD_ButtonsDown(0);
Questa riga darà un valore "True" alla variabile "pressed" quando verrà premuto un bottone sul controller.
CODICE
if ( pressed & WPAD_BUTTON_HOME ) exit(0);
Qui ci troviamo davanti ad un controllo della variabile "pressed" e se il bottone premuto sia l'"home", quindi se questi 2 parametri risultano veri ("True"), darà il comando di uscire dall'applicazione.
Questa funzione non fa altro che aspettare la sincronizzazione verticale dello schermo, la utilizziamo nel caso in cui ci sono state molte visualizzazioni grafiche altrimenti rischiamo di far sfarfallare lo schermo accelerando gli fps.
Chiusura del ciclo infinito.
Tutti i programmi C devono ritornare a 0 alla fine del programma. Se non ritorno a 0 allora il compilatore si lamentano perché non sanno dov'è la fine del programma.
Compilazione del codice sorgente
Sappiamo che il codice non fa altro Scrivere su schermo la scritta "Hello Word!" ed attendere finchè non viene premuto il tasto Home per uscire dall'applicazione.
Ora però prima di poter avviare il nostro esempio sulla consol dobbiamo compilare la sorgente, aprite un terminale ed entrate nella cartella dove è situato il "Makefile"
CODICE
cd /(percorso)/DevkitPRO/wii-example/template
e digitate il comando:
Adesso abbiamo compilato la nostra sorgente e se non avete commesso errori durante la creazione dell'ambiente il risultato sarà:
CODICE
template.c
linking ... template.elf
output ... template.dol
Da notare che il risultato sono 2 file, uno in formato ".dol" e l'altro ".elf", questi possono essere poi utilizzati nel canale Homebrew della wii, se in caso si rivoglia ricompilare la sorgente è necessario dare prima il comando "clean" che ripulisce le tracce della precedente compilazione, questo per evitare eroori se abbiamo cambiato parti della sorgente.
Errori di compilazione
Ora trattiamo gli errori del compilatore, di solito quando durante la compilazione (dopo il comando "make") ci da sempre il file e la riga dell'errore, nella maggior parte dei casi è la dimenticanza di ";" dopo ogni riga di comando.
CODICE
/source/template.c: In function 'main':
/source/template.c:21: error: expected ';' before 'rmode'
Il compilatore ci dice che nella funzione "main" (la funzione principale del codice) si è verificato l'errore e ci conduce in prossimità della linea in questione.
Il compilatore può anche dare avviso di cose che ancora lavorare, ma ci avverte cosa dobbiamo guardare Ad esempio:
CODICE
/source/template.c: In function 'main':
/source/template.c:52: warning: unused variable 'test'
Questo avviene quando viene dicharata una variabile intera e poi viene utilizzata come alfanumerica
Ora abbiamo un po' di fondamenti per la creazione e la compilazione di applicazioni per il nostro wii, comunque sto partendo dal presupposto che tu abbia già installato il canale Homebrew sulla tua consol Wii, questo rende più semplice provare i programmi.
In caso contrario di consiglio di installare il canale.
Per provare "template.elf" o "template.dol" bisogna creare una cartella nella nostra scheda SD, da non dimenticare che la nuova cartella deve essere sotto "apps" e che il file deve essere rinominato in "boot.elf" o "boot.dol" a seconda del caso e se volete creare una icona in formato ".png" di dimensioni standard 128x48 e un file "meta.xml" dove inserire le informazioni, qui sotto vi riporto il file ".xml" di esempio:
CODICE
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<app version="1">
<name>Nome del programma</name>
<coder>Nome o gruppo del creatore del programma.</coder>
<version>Versione del programma</version>
<release_date>data di rilascio ne formato: YYYYmmddHHMMSS</release_date>
<short_description>Questo viene visualizzato nel menu principale del Homebrew Channel,
spazio utilizzato per aggiungere 4 parole di spiegazione del
programma</short_description>
<long_description>Descrizione del programma con le eventuali funzioni o comandi di
controllo</long_description>
</app>
Per oggi è tutto alla prossima.
Se vuoi scaricare il tutorial clicca
quiController Pad
Benvenuti alla 4 parte riguardante la programmazione Homebrew per la consol Wii, spero che abbiate già letto le 3 parti precedenti riguardanti l'ambiente di lavoro e le basi di programmazione C, questo tutorial comprenderà principalmente le librerie riguardanti le funzioni di controllo dei comandi dei Pad Wii e GameCube.
Come esempio per semplificare la spiegazione prendo in considerazione una sorgente che abbiamo già visto in precedenza e che vi incollo qui sotto:
CODICE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ogcsys.h>
#include <gccore.h>
static u32 *xfb;
static GXRModeObj *rmode;
void Initialise() {
VIDEO_Init();
PAD_Init();
rmode = VIDEO_GetPreferredMode(NULL);
xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(xfb);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
}
int main() {
Initialise();
printf("Hello World!\n");
return 0;
}
Come già avevamo visto ci sono le librerie che includiamo per il funzionamento di input/output, ecc.
poi troviamo la funzione per l'inizializazione del video e poi nella funzione principale "int main()" troviamo il richiamo della funzione di inizializazione e poi la stampa di "Hello Word!", insomma tutte cose che già abbiamo trattato nelle lezioni precedenti.
Se volete compilare il codice avete bisogno del file "Makefile" riportato qui sotto, tenendo conto delle regole già trattato in precedenza:
CODICE
#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPPC)),)
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC)
endif
include $(DEVKITPPC)/gamecube_rules
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
SOURCES := source
DATA := data
INCLUDES := include
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
CFLAGS = -g -O2 -Wall $(MACHDEP) $(INCLUDE)
CXXFLAGS = $(CFLAGS)
LDFLAGS = -g $(MACHDEP) -Wl,-Map,$(notdir $@).map
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -logc -lm
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS :=
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export OUTPUT := $(CURDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
#---------------------------------------------------------------------------------
# automatically build a list of object files for our project
#---------------------------------------------------------------------------------
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
export LD := $(CC)
else
export LD := $(CXX)
endif
export OFILES := $(addsuffix .o,$(BINFILES)) \
$(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
$(sFILES:.s=.o) $(SFILES:.S=.o)
#---------------------------------------------------------------------------------
# build a list of include paths
#---------------------------------------------------------------------------------
export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
-I$(CURDIR)/$(BUILD) \
-I$(LIBOGC_INC)
#---------------------------------------------------------------------------------
# build a list of library paths
#---------------------------------------------------------------------------------
export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
-L$(LIBOGC_LIB)
export OUTPUT := $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@make --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).elf $(OUTPUT).dol
#---------------------------------------------------------------------------------
run:
psoload $(TARGET).dol
#---------------------------------------------------------------------------------
reload:
psoload -r $(TARGET).dol
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
#---------------------------------------------------------------------------------
# main targets
#---------------------------------------------------------------------------------
$(OUTPUT).dol: $(OUTPUT).elf
$(OUTPUT).elf: $(OFILES)
#---------------------------------------------------------------------------------
# This rule links in binary data with the .jpg extension
#---------------------------------------------------------------------------------
%.jpg.o : %.jpg
#---------------------------------------------------------------------------------
@echo $(notdir $<)
$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------
# This rule links in binary data
#---------------------------------------------------------------------------------
%.bin.o : %.bin
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
%.mod.o : %.mod
#---------------------------------------------------------------------------------
@echo $(notdir $<)
@$(bin2o)
-include $(DEPENDS)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------
La Differenza per richiamare il controller Wii o GameCube è solamente dovuta ad una "W", cioè nel caso si dichiari un richiamo del controller GameCube la parte iniziale della funzione è "PAD_", nel caso del controller WII "WPAD_".
Allora cominciamo meglio con esempii più pratici per capire meglio come funziona l'uso dei PAD, prendiamo al momento in considerazione quello del GameCube, per prima cosa abbiamo bisogno di una scansione della periferica tramite il comando:
Senza questo comando il controller sarà del tutto inutile, perchè non verrà rilevato dal sistema.
Innanzitutto bisogna tenere conto che ci sono tre diversi stati di rivelazione:
CODICE
PAD_ButtonsDown(0) //controllo se il pulsante è stato premuto
PAD_ButtonsHeld(0) //controllo se il pulsante è stato tenuto basso
PAD_ButtonsUp(0) //controllo se il pulsante è stato rilasciato
In questo esempio vogliamo verificare se un determinato pulsante è stato premuto, per questo utiliziamo la funzione "PAD_ButtonsDown()" come illustrato di seguito.
CODICE
u16 buttonsDown = PAD_ButtonsDown(0);
Assegnamo alla variabile "buttonsDown" la funzione "PAD_ButtonsDown(0)" per verificare se il pulsante è stato premuto, da notare lo "0" tra parentesi, sta ad indicare il controller che andiamo ad utilizzare, in questo caso il primo, se invece vogliamo controllare il secondo allora dobbiamo sostituire il valore con "1" e così via.
Ora dobbiamo verificare se il pulsante è stato premuto e se abbiamo premuto il pulsante "A" allora vogliamo che il sistema ce lo scriva:
CODICE
if( buttonsDown & PAD_BUTTON_A ) {
printf("Il pulsante A è stato premuto.\n");
}
Possiamo dire che la variabile "buttonsDown" sia una variabile boolean, cioè se il pulsante non è stato premuto assume il valore di "0" (falso), al contrario assume un valore (vero). Lo stesso principio vale per gli altri pulsanti, se vogliamo sapere quali siano gli altri pulsanti basta andare nel file:
CODICE
/(percorso)\devkitPro\libogc\include\ogc\pad.h
per vedere tutti i pulsanti e funzioni a disposizione riguardanti il controller GameCube.
Ora vogliamo verificare se il pulsante viene tenuto premuto e quando il pulsante viene rilasciato, il codice che riporto di seuito è simile a quello precedente con la differenza che sto utilizzando le rimanenti due funzioni elencate in precedenza:
CODICE
u16 buttonsHeld = PAD_ButtonsHeld(0);
if (buttonsHeld & PAD_BUTTON_A ) {
printf("Il pulsante A viene premuto.\n");
}
u16 buttonsUp = PAD_ButtonsUp(0);
if (buttonsUp & PAD_BUTTON_A ) {
printf("Il pulsante A è stato rilasciato.\n");
}
Ora passiamo ai 2 joysticks presenti sul controller del GameCube, questi li possiamo richiamare tramite le seguenti funzioni:
CODICE
PAD_SubStickX(0) //Joystick giallo asse orizontale
PAD_SubStickY(0) //Joystick giallo asse verticale
PAD_StickX(0) //Joystick asse orizontale
PAD_StickX(0) //Joystick asse verticale
Quando si usa il joystick non è necessario richiamare "buttonsDown" o qualsiasi altra cosa.
La funzione del joystick assume un valore che comprende da -18 a +18 sia sull'asse "X" (orizonatle) che sull'asse "Y" (verticale), qui sotto vi illustro un esmpio per capire meglio il funzionamento.
CODICE
if (PAD_StickY(0) > 18) {
printf(“Joystick mosso verso l'alto.\n”);
}
if (PAD_StickY(0) < -18) {
printf(“Joystick mosso verso il basso.\n”);
}
Bisogna tenere conto che la funzione per il joystick è come la funzione "PAD_ButtonsHeld" cioè si avrà un valore finche il joystick ha un movimento altrimenti torna automaticamente a "0" (falso).
Ora vediamo il nostro codice con tutte le nuove nozioni che abbiamo visto fino ad ora, il seguente codice va inserito dopo la riga del comando "printf" e quindi avremo:
CODICE
while(1) {
PAD_ScanPads();
u16 buttonsDown = PAD_ButtonsDown(0);
if( buttonsDown & PAD_BUTTON_A ) {
printf("Il pulsante A è stato premuto.\n");
}
u16 buttonsHeld = PAD_ButtonsHeld(0);
if (buttonsHeld & PAD_BUTTON_A ) {
printf("Il pulsante A viene premuto.\n");
}
u16 buttonsUp = PAD_ButtonsUp(0);
if (buttonsUp & PAD_BUTTON_A ) {
printf("Il pulsante A è stato rilasciato.\n");
}
if (PAD_StickY(0) > 18) {
printf("Joystick mosso verso l'alto.\n");
}
if (PAD_StickY(0) < -18) {
printf("Joystick mosso verso il basso.\n");
}
if (buttonsDown & PAD_BUTTON_START) {
exit(0);
}
}
Ora basterebbe compilare il nostro piccolo programmino per avere i risultati che abbiamo appena visto, questo però funzionano solo sul Pad del Gamecube, per avere gli stessi risultati sul PAD wii dovremo innanzitutto cambiare il file di compilazione "Makefile" andando a sostituire alla riga:
CODICE
include $(DEVKITPPC)/gamecube_rules
da vedere la scritta "gamecube_rules", a noi serve "wii_rules", e in più dobbiamo aggiungere le librerie Wii e Bluetooth:
CODICE
LIBS := -lwiiuse -lbte -logc -lm
Ora sul nostro programma dobbiamo includere le librerie del Pad aggiungendo in alto la riga
CODICE
#include <wiiuse/wpad.h>
Ora dobbiamo come detto in principio aggiungere la "W" alla funtione "Pad_" e visto che il Wiimote non ha il joystick elimineremo questi controlli e quindi il nostro codice sarà:
CODICE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <ogcsys.h>
#include <gccore.h>
#include <wiiuse/wpad.h>
static u32 *xfb;
static GXRModeObj *rmode;
void Initialise() {
VIDEO_Init();
WPAD_Init();
rmode = VIDEO_GetPreferredMode(NULL);
xfb = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode));
console_init(xfb,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ);
VIDEO_Configure(rmode);
VIDEO_SetNextFramebuffer(xfb);
VIDEO_SetBlack(FALSE);
VIDEO_Flush();
VIDEO_WaitVSync();
if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync();
}
int main() {
Initialise();
printf("Hello World!\n");
while(1) {
WPAD_ScanPads();
u16 buttonsDown = WPAD_ButtonsDown(0);
if( buttonsDown & WPAD_BUTTON_A ) {
printf("Il pulsante A viene premuto.\n");
}
u16 buttonsHeld = WPAD_ButtonsHeld(0);
if (buttonsHeld & WPAD_BUTTON_A ) {
printf("Button A is being held down.\n");
}
u16 buttonsUp = WPAD_ButtonsUp(0);
if (buttonsUp & WPAD_BUTTON_A ) {
printf("Il pulsante A è stato rilasciato.\n");
}
/*if (PAD_StickY(0) > 18) {
printf("Joystick mosso verso l'alto.\n");
}
if (PAD_StickY(0) < -18) {
printf("Joystick mosso verso il basso.\n");
}*/
if (buttonsDown & WPAD_BUTTON_HOME) {
exit(0);
}
}
return 0;
}
Ora se andiamo a compilare ed ad esegure il programma sulla nostra consol avremo lo stesso risultato prercedente solo con il Wiimote.
Vi aspetto alla prossima con la libreria per il puntatore del Wiimote.
Ciriciao a presto
Se vuoi scaricare il tutorial clicca
qui