* * * *
10 Visitatori, 0 Utenti
In questo articolo creeremo una prima semplice applicazione per Nintendo DS: mostreremo dei caratteri a schermo e interagiremo con la console attraverso il touch screen e i tasti.


Lo scheletro dell'applicazione

Qui di seguito è riportato il codice necessario per creare una qualsivoglia applicazione per Nintendo DS:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
program prova;

{$mode objfpc}

uses
  ctypes, nds9;

begin

  while true do
  begin
    // loop infinito!
  end;
end.

Il codice è molto semplice, quindi c'è davvero poco da dire, se non che la unit nds9 contiene gli header della libreria libndsfpc. Provando a compilare si otterrà una ROM perfettamente funzionante che mostrerà una bella schermata... nera! 

Già, compilare. Il mio consiglio è quello di prendere l'intera directory dell'esempio "hello_world", distribuito con la libreria libndsfpc,  copiarla in un'altra posizione (evitate spazi nel percorso!) e utilizzarla come base di partenza per i vostri progetti. Per far funzionare il tutto occorre modificare il file makefile.fpc come segue:

1
2
3
4
5
6
..
[default]
cpu=arm
target=nds
fpcdir=C:\fpc4nds\
..

indicando in fpcdir il percorso del compilatore.

L'ultimo passo è quello di creare uno script batch che servirà per compilare il codice:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
@echo off
set path=C:\fpc4nds\bin\i386-win32;%PATH%

fpcmake -r -w -Tnds

make clean distclean OS_TARGET=nds CPU_TARGET=arm BINUTILSPREFIX=arm-none-eabi- PP=ppcrossarm.exe 

make OS_TARGET=nds CPU_TARGET=arm BINUTILSPREFIX=arm-none-eabi- PP=ppcrossarm.exe OPT="@c:\fpc4nds\bin\i386-win32\fpc.nds.cfg -CX -XX -O2" 

pause

modificando, ovviamente, il path, se serve.


Scrivere caratteri sullo schermo

Le cose cominciano a farsi più interessanti con la possibilità di stampare dei caratteri sullo schermo. Anche questa operazione richiede poche righe di codice:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
program prova_console;
{$mode objfpc} 

uses
  ctypes, nds9; 

var
  i: integer;

begin
  consoleDemoInit();
  printf('Hello World!!!!'); 

  while true do
  begin
    swiWaitForVBlank();
  end;
end. 

La funzione consoleDemoInit() serve ad inizializzare la console con i caratteri predefiniti. E' possibile anche caricare dei font personalizzati, ma è un argomento che verrà trattato in seguito, quando si sarà affrontata la gestione dei banchi di memoria. Da notare l'uso di swiWaitForVBlank() nel loop infinito, che sincronizza il vertical blank. Per ora basti sapere che, grazie all'utilizzo di questa procedura, è possibile, ad esempio, sincronizzare le chiamate alle procedure di disegno con il refresh dello schermo, evitando così fastidiosi fenomeni di flickering.

La libreria libndsfpc è un binding di una libreria scritta in c, quindi le funzioni di stampa a schermo sono quelle proprie del c. Nel nostro caso stiamo utilizzando printf(), ma ce ne sono altre che vedremo man mano che ne avremo la necessità. Basti sapere che, caratteristica interessante, anteponendo dei codici di controllo alla stringa di testo da stampare, è possibile ad esempio cambiare colore ai caratteri:

1
2
printf(#27 + '[32m' + 'Verde!' + #10); 
printf(#27 + '[32;1m' + 'Verde acceso!');

Il codice numerico #27 attiva la sequenza di escape, necessaria per indicare che quello che segue è un codice di controllo; [32m indica che da quel punto in poi i caratteri vanno stampati a schermo in verde (codice di colore 32). Nel caso in cui volessimo una tonalità di verde più brillante sarà sufficiente digitare il codice [32;1m. Il codice #10 invece è un semplice "a capo".

I valori accettati per il colore sono riepilogati in tabella:

Codice Colore
30
nero
31 rosso
32 verde
33 giallo
34 blu
35 magenta
36 ciano
37 bianco

E' anche possibile indicare il punto dello schermo dove si vuole posizionare la stringa di testo:

1
printf(#27 + '[3;2H' + 'Riga 3, colonna 2');


Interazione con i tasti

Il Nintendo DS è equipaggiato da diverse periferiche di input. Oltre ai classici tasti presenti in tutte le console, dispone di uno schermo sensibile al tocco. Vedremo ora come gestire l'input dalla pulsantiera e dal touch screen.

Responsabile della gestione dell'input è un registro a 16 bit, chiamato REG_KEYINPUT, che si trova all'indirizzo di memoria $04000130, che è così dichiarato:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
const
  REG_KEYINPUT: pcuint16 = pointer($04000130);
...
type
  KEYPAD_BITS = cint;
const
  // Bit values for the keypad buttons.
  KEY_A : KEYPAD_BITS = (1 shl 0); // Keypad A button.
  KEY_B : KEYPAD_BITS = (1 shl 1); // Keypad B button.
  KEY_SELECT : KEYPAD_BITS = (1 shl 2); // Keypad SELECT button.
  KEY_START : KEYPAD_BITS = (1 shl 3); // Keypad START button.
  KEY_RIGHT : KEYPAD_BITS = (1 shl 4); // Keypad RIGHT button.
  KEY_LEFT : KEYPAD_BITS = (1 shl 5); // Keypad LEFT button.
  KEY_UP : KEYPAD_BITS = (1 shl 6); // Keypad UP button.
  KEY_DOWN : KEYPAD_BITS = (1 shl 7); // Keypad DOWN button.
  KEY_R : KEYPAD_BITS = (1 shl 8); // Right shoulder button.
  KEY_L : KEYPAD_BITS = (1 shl 9); // Left shoulder button.
  KEY_X : KEYPAD_BITS = (1 shl 10); // Keypad X button.
  KEY_Y : KEYPAD_BITS = (1 shl 11); // Keypad Y button.
  KEY_TOUCH : KEYPAD_BITS = (1 shl 12); // Touchscreen pendown.
  KEY_LID : KEYPAD_BITS = (1 shl 13); // Lid state.

Lo stato di ogni tasto è associato ad un bit nel registro (1: rilasciato; 0: premuto).

Vediamo un piccolo esempio sull'utilizzo diretto del registro REG_KEYINPUT:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
program keys;
{$mode objfpc}
uses
  ctypes, nds9;
begin
  consoleDemoInit();
  while true do
  begin
    if (REG_KEYINPUT^ and KEY_A) <> 0 then
      printf('A Key released')
    else
      printf('A Key pressed');
    swiWaitForVBlank();
    consoleClear();
  end;
end.

Vediamo il funzionamento dell'esempio linea per linea:

[6] Viene inizializzata la console standard

[7-15] Loop infinito per far girare il programma

[9] Viene controllato lo stato del bit KEY_A (cioè del bit nella posizione 0) nel registro REG_KEYINPUT. Il registro è un puntatore ad una zona di memoria, quindi andrà dereferenziato per accedere al suo contenuto (^). Loperatore logico and restituisce lo stato del bit KEY_A (0 o 1); se non è impostato a 0 (<> 0) il tasto non è premuto e stampiamo a schermo un messaggio ([10]), altrimenti è premuto e stampiamo a schermo un secondo messaggio ([12]).

[13] Aspettiamo il prossimo vertical blank

[14] Cancelliamo il contenuto della console sullo schermo, altrimenti i caratteri verrebbero sovrascritti di volta in volta, sovrapponendosi a quelli scritti in precedenza.

Si può ottenere lo stesso risultato utilizzando delle funzioni di livello più alto, senza andare a scomodare puntatori e bit. Espandiamo un po' l'esempio precedente:

 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
program keys_b;
{$mode objfpc}
uses
  ctypes, nds9;
var
  held: integer;
begin
  consoleDemoInit();
  while true do
  begin
    scanKeys();
    held := keysHeld();
    if (held and KEY_A) <> 0 then
      printf('A Key is pressed'#10)
    else
      printf('A Key is released'#10);
    if (held and KEY_X) <> 0 then
      printf('X Key is pressed'#10)
    else
      printf('X Key is released'#10);
    if (held and KEY_TOUCH) <> 0 then
      printf('Touch pad is touched'#10)
    else
      printf('Touch pad is not touched'#10);
    swiWaitForVBlank();
    consoleClear();
  end;
end.

[11] La procedura scanKeys() legge lo stato corrente del keypad. Effettuata la chiamata alla procedura una volta per ogni frame, vengono rese disponibili una serie di utili funzioni:

function keysCurrent(): cuint32;
function keysHeld(): cuint32;
function keysDown(): cuint32;
function keysDownRepeat(): cuint32;
procedure keysSetRepeat(setDelay: cuint8; setRepeat: cuint8);
function keysUp(): cuint32;

[12-24] Una volta ottenuto lo stato del keypad, possiamo leggere lo stato di un singolo tasto, effettuando un and tra il valore restituito dalla funzione e il bit occupato dal tasto. Come nell'esempio precedente, stampiamo a schermo dei messaggi indicanti lo stato dei tasti.


Interazione col touch screen

Fino a questo punto le cose sono tanto semplici quanto poco interessanti. Abbiamo visto come gestire i tasti e lo stato del touch screen (tocco/non tocco). Ma se volessimo indicare a schermo le coordinate del punto di tocco? Nel prossimo esempio vedremo appunto come fare. La procedura che si occupa della gestione del touch screen è la seguente:

type
  touchPosition = packed record
  rawx: cuint16;
  rawy: cuint16;
  px: cuint16;
  py: cuint16;
  z1: cuint16;
  z2: cuint16;
end;
PtouchPosition = ^touchPosition;
TtouchPosition = touchPosition;
...
procedure touchRead(var data: touchPosition);

La procedura touchRead(var data: touchPosition) si occupa di aggiornare il record touchPosition, che possiamo quindi leggere per ottenere le informazioni sul punto di tocco sul touchpad.  Le coordinate di un singolo punto su di un piano bidimensionale (come il touch screen) possono essere memorizzate in 2 variabili, ma il record touchPositon è composto da 6 valori. Per quale motivo? Perché memorizza i valori x e y del punto di tocco (px, py), i loro valori non calibrati (rawx, rawy) e il livello di pressione sul touch screen (z1, z2).

Come al solito vediamo un breve esempio sull'utilizzo del touch screen. In esso sono introdotti alcuni concetti più avanzati, che verranno meglio trattati nei prossimi tutorial; per il momento provate comunque seguirli:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
program touch_a;
{$mode objfpc}
uses
  ctypes, nds9;
var
  color: integer;
  touch: TTouchPosition;
  x, y: integer;
begin
  videoSetMode(MODE_FB0);
  vramSetBankA(VRAM_A_LCD);
  lcdMainOnBottom();
  while true do
  begin
    touchRead(touch);
    color := (touch.px + touch.py) shr 4;
    for y := 0 to 192 - 1 do
      for x := 0 to 256 - 1 do
        VRAM_A[y * 256 + x] := integer(RGB15(color, x shr 3, y shr 3));
    swiWaitForVBlank();
  end;
end.

[10-12] Nell'esempio utilizzeremo l'accesso diretto ai pixel dello schermo, quindi impostiamo la modalità framebuffer (MODE_FB0) per il video; riserviamo quindi il banco di memoria A per l'utilizzo con lo schermo LCD tramite la procedura vramSetBankA(); per ultimo impostiamo lo schermo inferiore come schermo principale per la nostra piccola applicazione.

[15] All'interno del loop infinito eseguiamo la lettura del touch screen e salviamone il contenuto nella variabile touch.

[16] La variabile color viene utilizzata per memorizzare il valore della componente rossa del colore, ricavandolo dalle coordinate del punto di tocco. Piccola spiegazione: "shr 4" equivale a scrivere "div 2^4". Per ogni componente di un colore RGB15 c'è bisogno di un valore intero nel range 0-31, ma la somma delle coordinate del punto di tocco rientra nel range 0-(191 + 255 = 446). Dividendo questo valore per 2^4=16, otteniamo un valore massimo di 27,875, che è molto vicino al valore massimo che ci serve (31).

[17-19] Ad ogni frame effettuiamo un loop attraverso tutti i pixel dello schermo e assegniamo un colore ad ognuno di essi, passando alla funzione RGB15() il valore che abbiamo memorizzato nella variabile color per la componente rossa; per le componenti verdi e blu del colore eseguiremo invece un calcolo, basandoci sui valori delle coordinate x e y. In questo caso abbiamo bisogno di un valore nel range 0-31, partendo da 0-191 (coordinata y) e 0-255 (coordinata x). Questa volta divideremo per 2^3=8, che è lo stesso che scrivere "shr 3". Anche in questo caso otterremo una buona approssimazione del valore massimo richiesto.


Conclusioni

Non vi rimane che provare a giocare con i comandi appena visti, in attesa del prossimo articolo, in cui proveremo a salvare dei dati su file e a ricaricarli in un secondo momento. 

Scarica i sorgenti

Per commenti e chiarimenti: http://www.lazaruspascal.it/index.php?topic=78.0


Share on Twitter! Digg this story! Del.icio.us Share on Facebook! Technorati Reddit StumbleUpon

Articles in « Free Pascal per DS »

Recenti

How To

Trascinare un file nel programma da xinyiman
DB concetti fondamentali e ZeosLib da xinyiman
Recuperare codice HTML da pagina web da xinyiman
Mandare mail con Lazarus da xinyiman
Stabilire il sistema operativo da xinyiman
Esempio lista in pascal da xinyiman
File INI da xinyiman
Codice di attivazione da xinyiman
Realizzare programmi multilingua da xinyiman
Lavorare con le directory da xinyiman
Utilizzare Unità esterne da Loryea
TTreeView da xinyiman
TTreeview e Menu da xinyiman
Generare controlli RUN-TIME da xinyiman
LazReport, PDF ed immagini da xinyiman
Intercettare tasti premuti da xinyiman
Ampliare Lazarus da xinyiman
Lazarus e la crittografia da xinyiman
System Tray con Lazarus da xinyiman
UIB: Unified Interbase da Microges2000
Il file: questo sconosciuto da Microges2000
Conferma di chiusura di un applicazione da xinyiman
Liste e puntatori da Microges2000
Overload di funzioni da Microges2000
Funzioni a parametri variabili da Microges2000
Proprietà da Microges2000
Conversione numerica da Microges2000
TImage su Form e Panel da Maverich
Indy gestiore server FTP lato Client da Maverich
PopUpMenu sotto Pulsante (TSpeedButton) da Maverich
Direttiva $macro da Microges2000
Toolbar da xinyiman
Evidenziare voci TreeView da Maverich
Visualizzare un file Html esterno da Maverich
StatusBar - aggirare l'errore variabile duplicata da Maverich
Da DataSource a Excel da xinyiman
Le permutazioni da xinyiman
Brute force da xinyiman
Indy 10 - Invio email con allegati da Maverich
La gestione degli errori in Lazarus da xinyiman
Pascal Script da xinyiman
Linux + Zeos + Firebird da xinyiman
Dataset virtuale da xinyiman
Overload di operatori da Microges2000
Lavorare con file in formato JSON con Lazarus da nomorelogic
Zeos ... dietro le quinte (prima parte) da Stilgar
Disporre le finestre in un blocco unico (come Delphi) da Maverich
Aspetto retrò (Cmd Line) da xinyiman
Come interfacciare periferica twain da Narciso
Ubuntu - aggiornare free pascal e lazarus da xinyiman
fpcup: installazioni parallele di lazarus e fpc da nomorelogic
Free Pascal e Lazarus sul Raspberry Pi da nomorelogic
Cifratura: breve guida all'uso dell'algoritmo BlowFish con lazarus e free pascal. da nomorelogic
Creare un server multithread da xinyiman
guida all'installazione di fpc trunk da subversion in linux gentoo da nomorelogic
Indice da nomorelogic
DB concetti fondamentali e connessioni standard da xinyiman
Advanced Record Syntax da nomorelogic
DB concetti fondamentali e DBGrid da xinyiman
DB concetti fondamentali e TDBEdit, TDBMemo e TDBText da xinyiman
Advanced Record Syntax: un esempio pratico da nomorelogic
Superclasse form base per programmi gestionali (e non) da nomorelogic
Superclasse form base per programmi gestionali (e non) #2 - log, exception call stack, application toolbox da nomorelogic
Superclasse form base per programmi gestionali (e non) #3 - traduzione delle form da nomorelogic
Superclasse form base per programmi gestionali (e non) #4 - wait animation da nomorelogic
Un dialog per la connessione al database:TfmSimpleDbConnectionDialog da nomorelogic
Installare lazarus su mac osx sierra da xinyiman
Utenti
Stats
  • Post in totale: 10424
  • Topic in totale: 1326
  • Online Today: 23
  • Online Ever: 74
  • (Luglio 09, 2012, 11:05:53 am)
Utenti Online
Users: 0
Guests: 10
Total: 10

Disclaimer:

Questo blog non rappresenta una testata giornalistica poiché viene aggiornato senza alcuna periodicità. Non può pertanto considerarsi un prodotto editoriale ai sensi della legge n. 62/2001.