/////////////////////////////////
INDICE ///////////////////////
DS CODE 0: "Los primeros pasos"
http://www.elotrolado.net/showthread.php?s=&threadid=560011
DS CODE 1: "Interrumpiendo"
http://www.elotrolado.net/showthread.php?s=&postid=1704954093
////////////////////////////////////////////////////////////////
¿Cuánto tiempo desde el primer tutorial introductorio eh? La verdad es que he andado bastante ocupado (hacerse ingeniero y esas cosas) y además ando liado en un par de proyectos gordos (cuak, cuak dicen los patos). A parte, que tratar de enseñar libnds y a la vez mostrar el hardware es más extenso de lo planeado. Pero bueno, aquí vuelvo al pie del cañón esperando que os guste y que el próximo capítulo lo termine en menos tiempo. Y sin más allá vamos.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
Bienvenidos al primer capítulo de "como dejar mi adicción por palib y entender lo que estoy haciendo". Si, primero. Desde aquí comenzaremos ya a tratar cosas más serias y entraremos de lleno a programar en DS utilizando libnds. Tenía previsto juntar más temas, pero visto la extensión de este, los voy a sacar por separado. Supongo que además será más amenos de leer.
INTERRUPCIONES
Menuda mieeeerda de tema has cogido para empezar, dices mientras miras asqueado. Es comprensible, no suena ameno. Sin embargo las interrupciones serán un elemento que estén presentes en cada uno de nuestros proyectos y que además mucha gente que empezó con versiones anteriores de libnds (dícese con ndslib) o aun antes con gba, todavía no hace un uso de la funcionalidad de libnds para este tema. Veréis la de cosas que podéis hacer sólo con interrupciones y un printf
Las interrupciones podemos clasificarlas en interrupciones hardware e interrupciones sofware (de la bios).
1. INTERRUPCIONES HARDWARE
¿Qué es una interrupción? Pues lo que el nombre dice aproximadamente. Una
parte del hardware emite una seña l indicando que sucede algo que puede ser interesante de conocer y por tanto de
interrumpir el flujo normal de ejecución de un programa. Cada vez que esto sucede se queda guardado en un registro de peticiones de interrupción y será
tarea del programador atenderlas o no. Bien, ¿pero como funciona?. Pues tenemos 5 partes: el
registro maestro de interrupciones (IME), el
registro de activación de interrupciones (IE) , el
registro de petición de interrupciones (IF) ,el
gestor de interrupción (Interrupt Handler). y el llamémoslo "
lanzador de interrupciones " (Interrupt dispatcher). En libnds encontraremos los registros con el nombre de
REG_IME, REG_IE y REG_IF
¿Ves? No son tantas cosas. Vamos a ver que hace cada uno entonces.
-Registro maestro de interrupciones (IME)
El más sencillito de todos. Su función es la de
permitir o no permitir que sucedan
interrupciones
-Registro de activación de interrupciones (IE)
Este es en el que se indica de que interrupciones queremos ser avisados. Para ello contamos con un registro de 32 bits en el que
cada bit activa o desactiva una tipo de interrupción (no todos se usan)
-Registro de petición de interrupciones (IF)
El último registro es igualmente 32 bits (evidentemente tiene que existir una correspondencia con el IE) y en el
se pone a 1 el bit del tipo de señal que está solicitando interrumpir la CPU. Volviendo a escribir un 1 en éste registro, el hardware sabrá que la petición ha sido atendida
-Interrupt Handler
El Interrupt Handler es una función que se puede definir en una posición especial de memoria y que
permite tratar la interrupción . Cuando se produce una interrupción se entra en un modo especial que llama a una rutina de la BIOS que salta a una dirección de memoria, en la que pondremos el Interrupt Handler.
-Interrupt dispatcher
Libnds utiliza un Interrupt dispatcher como interrupt handler. Es decir, es una función con la ventaja de que el
decide a que función de las registradas en una tabla de interrupciones llamar según la interrupción recibida. Para los curiosos y valientes pueden mirarlo en el código fuente de libnds en perfecto ensamblador
Por último comentar un pequeño detalle que se ha ido olvidando por el camino. Anteriormente he dicho que el hardware emite una señal que interesa recoger. Y ahi está la clave. Esas
señales deben ser activadas en los registros apropiados para poder ser recogidas (si no es como tirarse a parar un penalti antes de que pite el arbitro. Vamos, que estamos perdiendo el tiempo). Iré comentando cuáles son éstos registros y que bits hay que cambiar para las interrupciones de las que hable.
Buenoooo. ¿Y tengo que ir activando yo y mirando todo eso? Al comienzo se hacía todo de forma manual la mitad de las cosas: activar IME, poner a 1 los bits en el IE de las señales que queríamos leer, añadir un interrupt handler (configurándolo manualmente) y por último comprobar que interrupción se producía y gestionarla. Como vemos no es un proceso que podamos calificar de, digamos, entretenido. No voy a poner a poner un ejemplo de ésto porque considero que con saber un poco la teoría es más que suficiente, ya que teniendo todo el proceso simplificado gracias a libnds es practicamente absurdo realizar todo el proceso uno mismo. Vamos a ello.
-Gestionar interrupciones hardware
Bueno primero vamos a mirar que interrupciones tenemos a nuestra disposición y el parámetro correspondiente en libnds (son las máscaras para los bit del registro IE)
IRQ_VBLANK -> vblank. Se produce cuando se pinta toda la pantalla. Aproximadamente 60 veces por segundo
IRQ_HBLANK -> hblank. Ésta es para cuando pinta una línea horizontal (scanline)
IRQ_VCOUNT -> avisa cuando han pasado tantas scanlines como se ha indicado en el vcount (0 a 262. 0 a 191 está pintando y 192-262 es periodo VBLANK)
IRQ_TIMER0 -> interrupciones para los temporizadores
IRQ_TIMER1
IRQ_TIMER2
IRQ_TIMER3
IRQ_NETWORK -> no está muy claro que hace. Sólo en ARM7
IRQ_DMA0 -> para hacer accesos directos a memoria DMA
IRQ_DMA1
IRQ_DMA2
IRQ_DMA3
IRQ_KEYS -> podemos definirla para una combinación de teclas.
IRQ_CART -> se chiva de que estás sacando el cartucho de gba
IRQ_IPC_SYNC -> sirve para intercambiar información entre el ARM7 y el ARM9. Sólo 4 bits
IRQ_FIFO_EMPTY -> para comunicación entre procesadores también e indica que el FIFO está vacío.
IRQ_FIFO_NOT_EMPTY-> en este caso hace lo contrario, es decir, el FIFO no está vacío
IRQ_CARD -> indica el fin de una transmisión de datos desde la tarjeta de la DS
IRQ_CARD_LINE -> algo sobre la tarjeta de la DS pero ni idea de que exactamente
IRQ_GEOMETRY_FIFO -> indica si la FIFO de comandos 3D está vacía o medio llena. Sólo para ARM9
IRQ_LID -> para la tapa de la DS. Seguro que ya éstas pensando en los sellos de Another Code. Sólo ARM7
IRQ_SPI -> bus SPI. Sólo ARM7
IRQ_WIFI -> para wifi. Sólo ARM7
Esas son todas las interrupciones que podremos decidir recoger o no. Algunas no está muy claro su función (trataré de completar más en un futuro, pero no hay mucho donde rascar) y otras no las usaréis nunca o practicamente nunca, pero al menos ya sabéis las armas con las que contáis. Veamos ahora que funciones nos ofrece libnds para poder activar y desactivarlas:
*void irqInit()
Ésta función debe ser llamada siempre que se quieran usar interrupciones (una vez al principio del código o más si queremos resetear la tabla de interrupciones).
Inicializa la tabla de interrupciones y el Interrupt Handler .
*void irqSet(int mask, IntFn handler)
Una vez llamada la función de inicialización utilizaremos ésta otra función para
indicar que interrupciones queremos usar y si queremos asignar una función a esa interrupción . El primer parámetro es una de las máscaras de la lista de arriba y el segundo el nombre de la función. La función siempre devolverá void y no tendrá parámetro.
*void irqEnable(IRQ_MASK irq)
Tal y como está diseñada libnds, con el irqSet sólo, no se activa la interrupción porque, extrañamente, el IME, registro maestro de interrupciones como recordaréis, se activa sólo dentro de esta función (es decir que también podemos poner un REG_IME=1 después de los irqSet y estarían igualmente todas activadas). De este modo lo normal es que siempre que hagamos un
irqEnable para las interrupciones que hemos indicada en irqSet . Igualmente se pasa como parámetro las máscaras de arriba pudiendo pasar varias con | (or para el que no pille). También se usa el enable si se utiliza la siguiente que voy a comentar, irqDisable.
*void irqDisable(int irq)
Está función sirve para poder
deshabilitar una interrupción . La habilitaremos de nuevo con irqEnable. Recibe como parámetro una de las mñascaras de arriba
*void irqClear(IRQ_MASK irq)
Con ésta no sólo deshabilitamos, si no que tambíen la
quitamos de la tabla de interrupciones . ¿Adivinas que le pasamos cómo parámetro?
*void irqInitHandler(IntFn handler)
Esta función dudo que alguien la use.
Permite pasarle un interrupt handler propio en vez del dispatcher que usa libnds. Si alguien usa una propia, posiblemente ni use ésta función, así que para el caso...
Y ya está. Así de fácil y de sencillo, con esas pocas funciones podremos activar, desactivar, etc todas las interrupciones hardware de la DS. Bueno, no está mal, algo he pillado, pero vamos a ver ¿yo quiero ésto para algo? Sssss, calma, que nos quedan interrupciones por software. Después ejemplos.
2. INTERRUPCIONES SOFTWARE
La idea es básicamente la misma que en las interrupciones hardware. El flujo de ejecución normal se interrumpe y se pasa a otra tarea, sólo que en éste caso
nosotros estamos generando la interrupción (llamando a la función directamente) y además
la función a la que se llama no tenemos que definirla si no que ya está en la bios (de hecho lo que hacemos es llamar a una función de la bios). La DS tiene muchas funciones de éste tipo y en libnds son fácilmente reconocibles porque comienzan con swi. Debido a la cantidad de ellas y a que no son tan frecuentemente usadas (que poco profesionales somos...) sólo voy a poner la lista para que tengáis una referencia rápida de cuáles tenéis disponibles en cada procesador con sus códigos de llamada (en ensamblador se llamarían de la forma swi código) y una pequeña descripción. .
NDS7 NDS9 Function
00h 00h swiSoftReset -> resetea varios elementos del procesador
03h 03h swiDelay -> para pequeñas esperas
04h 04h swiIntrWait -> espera a que se produzca una interrupción indicada
05h 05h swiWaitForVBlank -> espera a que llegue una interrupción de vblank
06h 06h swiWaitForIRQ -> espera a que llegue cualquier interrupción
07h - swiSleep -> deja dormido el ARM7
08h - swiChangeSoundBias -> cambia el SOUND_BIAS (no preguntéis)
09h 09h swiDivide -> division
0Bh 0Bh swiCopy -> copia datos de una posición a otra. En unidades de 2 o 4 bytes
0Ch 0Ch swiFastCopy -> copia datos de una posición a otra más rápido, pero parece que sólo una parte. En unidades de 4 bytes
0Dh 0Dh swiSqrt -> raiz cuadrada
0Eh 0Eh swiCRC16 -> calcula un CRC de 16 bits
0Fh 0Fh swiIsDebugger -> comprueba si se ustando debugger
10h 10h swiUnpackBits -> para incrementar los bpp de las imágenes??
11h 11h swiDecompressLZSSWram -> descomprime datos con LZSS. Más rápida
12h 12h swiDecompressLZSSVram -> descomprime datos con LZSS. Usa funciones callback (puede leer de más sitios que de memoria)
13h 13h swiDecompressHuffman -> descomprime datos con Huffman. Usa funciones callback
14h 14h swiDecompressRLEWram -> descomprime datos con RLE. Más rápida
15h 15h swiDecompressRLEVram -> descomprime datos con RLE. Usa funciones callback
- 16h swiDecodeDelta8 -> descomprime datos guardados de forma incremental (10-12-15-=10-+1-+3)
- 18h swiDecodeDelta16 -> descomprime datos guardados de forma incremental (10-12-15-=10-+1-+3)
1Ah - swiGetSineTable ->
1Bh - swiGetPitchTable ->
1Ch - swiGetVolumeTable ->
1Dh - GetBootProcs (no definida en libnds) ->
1Fh 1Fh swiSetHaltCR ->
Algunas como véis no os puedo decir mucho o nada (si algún dia puedo completarlo, lo haré). Aquí tenéis un link a la sintaxis de las funciones con sus parámtros y retornos, así como más información sobre como tienen que estar alineados, etc., proveniente de la antigua ndslib (que se ha mantenido de forma invariable en libnds)
http://www.drunkencoders.com/documents/DS/ndslib.htm#_BIOS
Bueno vamos a empezar ya con los ejemplos.
3. EJEMPLOS
EJEMPLO INTERRUPCIONES: VBLANK, HBLANK y VCOUNT
-Elementos
Bien como he comentado ya, para poder recoger una interrupción hardware necesitamos tener algo que recoger. En el caso de
estas 3 interrupciones nos encontramos con el registros definido en libnds como REG_DISPSTAT/DISP_SR . Vamos a ver como es éste registro y como cambiar sus bits en libnds (despúes de la explicación pongo el define que pone a 1 el bit)
REG_DISPSTAT/DISP_SR
Bit Expl.
0 V-Blank flag -> indica si estas fuera(0) o dentro(1) del VBLANK . DISP_IN_VBLANK
1 H-Blank flag -> indica si estas fuera(0) o dentro(1) del HBLANK . DISP_IN_HBLANK
2 V-Counter flag -> indica si estas fuera(0) o dentro(1) del VCOUNT . DISP_YTRIGGERED
3 V-Blank IRQ -> activa la interrupción VBLANK. DISP_VBLANK_IRQ
4 H-Blank IRQ -> activa la interrupción HBLANK. DISP_HBLANK_IRQ
5 V-Counter IRQ -> activa la interrupción VCOUNT. DISP_YTRIGGER_IRQ
6 No usado
7-15 V-Count -> aquí indicamos que línea entre 0 y 262 activará la interrupción. Este campo es un poco cabrón puesto que los 8 primeros bits son 8-15 y el último es el 7 es decir 5 no es 101 sino 1010 y 256 sería 000000001
Bueno ya sabemos entonces que bits poner a 1 para poder tener ya nuestras interrupciones a punto.... Peeeero (siempre hay un pero) la gente de libnds piensa en nosotros y sabe que usamos tanto
VBLANK y HBLANK que cada vez que hagamos un
irqSet de estas 2 interrupciones, internamente libnds ya
activa los bits de REG_DISPSTAT/DISP_SR . Luego ésto lo podéis tener en cuenta para no volver a ponerlos a 1 (o si si os apetece), siendo sólo necesario cambiarlos para VCOUNT
Y hablando de VCOUNT resulta que también podemos leer que línea se está pintando (muy útil para cuando se produce un HBLANK saber donde estamos). Para ello está este registro de libnds (el nombre a mala leche)
REG_VCOUNT/DISP_Y
Bit Expl.
0-8 Línea actual (0-262) -> aquí si están los bits en su sitio (lo otro es herencia de gba, que como no les cabían por un lado, tiraron para el otro)
9-15 No usado
-Qué hace
En este ejemplo
activaremos las 3 interrupciones . Despues configuraremos como en el primer ejemplo un fondo para texto (ésta vez en la secundaria) y en la principal activaremos el
modo framebuffer (pintas los pixels directamente el framebuffer y éste se pinta en pantalla).
En cada hblank cambiaremos el color con el que se están pintando los pixels y en el
vcount cambiaremos el tono a morado. Este modo no es ni mucho menos recomendable para hacer un gradiente, pero lo importante es ver la funciones de interrupciones en las que se definen todas, asi como las funciones a las que se llama tras la interrupción. Además hay un
contador que vemos como se incrementa en cada
vblank .
-Código
#include "nds.h"
#include <nds/arm9/console.h>
#include <stdio.h>
int counter=0;
u8 color=0;
u8 color2=0;
void vBlank(){
++counter; //incrementamos el contador cada VBLANK, es decir, 60 veces por segundo
}
void hBlank(){
color=REG_VCOUNT/DISP_Y>>3; //cambiamos el color en cada HBLANK (linea horizonal)
}
void vCount(){
color2=10; //cuando lleguemos a la linea indicada en REG_DISPSTAT/DISP_SR cambiamos el color
}
void interrupciones(){
irqInit(); //inicializamos la tabla de interrupciones
irqSet(IRQ_VBLANK,vBlank); //decimos que queremos recibir la interrupción por VBLANK y además le asignamos
//la función vBlank que será llamada cada vez que se produzca
irqSet(IRQ_HBLANK,hBlank); //lo mismo para HBLANK
irqSet(IRQ_VCOUNT,vCount); //y también para VCOUNT
irqEnable(IRQ_MASK(IRQ_VBLANK|IRQ_HBLANK|IRQ_VCOUNT)); //activamos las interrupciones. Como ya he comentado, despúes de hacer el set
//el único motivo por el que aun no está habilitada es por IME, así que podríamos poner también REG_IME=1;
//irqEnable(IRQ_VBLANK|IRQ_HBLANK|IRQ_VCOUNT); para devkitpro r19
REG_DISPSTAT/DISP_SR|=DISP_YTRIGGER_IRQ|(128<<8); //activamos en este registro la señas de VCOUNT y establecemos un valor para comparar con VCOUNT
//como HBLANK y VBLANK ya están activados en el set, no ponemos nada más
};
int main(void) {
powerON(POWER_ALL); //a partir de la r19 creo que no es necesario
lcdMainOnBottom(); //vamos a coger la sana costumbre escoger al principio donde poner la pantalla principal
videoSetMode(MODE_FB0); //usamos el modo framebuffer para la pantalla principal
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE); //selecciona el modo de video 0 para la pantalla secundaria y activa el fondo 0
vramSetBankA(VRAM_A_LCD); //en el modo framebuffer mapeamos el banco de este modo
vramSetBankC(VRAM_C_SUB_BG); //reserva el banco C para fondos de la pantalla secundaria
SUB_BG0_CR = BG_MAP_BASE(31); //configura el registro del fondo 0 para que busque el mapa de tiles en el bloque 31.
BG_PALETTE_SUB[255] = RGB15(31,31,31); //ponemos el último color de la paleta de fondos de la pantalla principal a blanco
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16); //función básica proporcionada por libnds para poner texto. Sencilla pero con ciertas limitaciones
interrupciones(); //inicializamos las interrupciones
u16 *screen=VRAM_A; //empezamos a pintar el framebuffer con el color
for(int i = 0; i < 256 * 192; ++i) //el cambio del framebuffer coincide con los hblank y va cambiando de color en cada línea, y en cierto momento se produce el VCOUNT y se cambia de color
*screen++ = RGB15(color2,0,color);
while(1) {
iprintf("\x1b[1;1H%d", counter); //visualizamos el contador incrementado en los vblank
}
return 0;
}
EJEMPLO INTERRUPCIONES: IRQ_VBLANK,IRQ_KEYS Y SWIWAITFORVBLANK -Elementos Bueno en éste otro ejemplo vamos a ver una interrupcion hardware (IRQ_KEYS ) y la interrupción software más usada (swiWaitForVBlank
) [COLOR=sandybrown]que detiene el bucle hasta que llegue un vblank[/COLOR] .
IRQ_KEYS nos permite generar una
interrupción cuando se pulse una combinación de botones o un botón dentro de la combinación , según lo configuremos. Como véis no da mucho margen y se usa por ejemplo para salir de modo sleep, hacer algún truco en concreto, un salvado automático o porqué no, una captura de pantalla. El
registro para activar la interrupción es REG_KEYCNT REG_KEYCNT
0 A ->(1) añadir a la combinación de teclas
1 B ->
2 Select ->
3 Start ->
4 Right ->
5 Left ->
6 Up ->
7 Down ->
8 R ->
9 L ->
10-13 No usado ->
14 Key IRQ -> activa la interrupción IRQ_KEYS
15 Modo -> (0) cualquier tecla de la combinación es válida o (1) hay que pulsar todas -Qué hace En esta demo haremos uso del
vblank para controlar los fps de nuestra aplicación . ¿Cómo? Muy sencillo. Primero haremos uso de nuestra nueva interrupción software swiWaitForVBlank que detiene la ejecución del bucle hasta que haya una interrupción por vblank. De este modo incrementamos una variable en el bucle, siendo su valor en el mejor de los casos igual que el contador incrementado en vblank (es decir que se incrementa 60 veces por segundo). Lo que pasaría si el bucle es muy pesado es que la variable no se actualizaría tantas veces y por tanto al visualizarla tendría un menor valor. En definitiva estamos controlando los fps. Por otra parte aprovechando el contador del vblank, he creado un segundero a modo de temporizdor (ya sabéis otro uso del vblank!!).
La segunda parte corresponde a la interrupción de las teclas. Lo que vamos a hacer es configurar el registro REG_KEYCNT para que
cuando pulsemos A+B el temporizador se detenga y cuando volvamos a pulsarlas, se reactive (en emulador va un poco "durillo")
-Código
#include "nds.h"
#include <nds/arm9/console.h>
#include <stdio.h>
int vcounter=0;
int lcounter=0;
int tiempo=0;
bool vblankEnable=true;
void vBlank(){
++vcounter; //incrementamos el contador cada VBLANK, es decir, 60 veces por segundo
if (vcounter==60){ //cuando vcounter llega a 60 es que ha pasado un segundo
vcounter=0;
iprintf("\x1b[1;1HFPS %d", lcounter); //el valor que tenga lcounter son los frames a los que está llendo la lógica, nuestros adorados fps
++tiempo; //incrementamos nuestro temporizador. Esto sería equivalente a un segundo
lcounter=0;
}
}
void keys(){
if (vblankEnable){
irqDisable(IRQ_VBLANK); //desactivamos la interrupción de vblank
vblankEnable=false;
}
else{
irqEnable(IRQ_VBLANK); //la volamos a activar
vblankEnable=true;
}
}
void interrupciones(){
irqInit(); //inicializamos la tabla de interrupciones
irqSet(IRQ_VBLANK,vBlank); //decimos que queremos recibir la interrupción por VBLANK y además le asignamos
//la función vBlank que será llamada cada vez que se produzca
irqSet(IRQ_KEYS,keys);
irqEnable(IRQ_MASK(IRQ_VBLANK|IRQ_KEYS)); //activamos las interrupciones. Como ya he comentado, despúes de hacer el set
//el único motivo por el que aun no está habilitada es por IME, así que podríamos poner también REG_IME=1;
//irqEnable(IRQ_VBLANK|IRQ_HBLANK|IRQ_VCOUNT); para devkitpro r19
REG_KEYCNT|=(3<<14)|3; //con esto activamos la interrupción para las teclas (bit 14) A y B (bit 0 y 1) y decimos además que ambas tienen que
//estar presionadas (bit 15)
};
int main(void) {
powerON(POWER_ALL); //a partir de la r19 creo que no es necesario
lcdMainOnBottom(); //vamos a coger la sana costumbre escoger al principio donde poner la pantalla principal
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE); //selecciona el modo de video 0 para la pantalla secundaria y activa el fondo 0
vramSetBankC(VRAM_C_SUB_BG); //reserva el banco C para fondos de la pantalla secundaria
SUB_BG0_CR = BG_MAP_BASE(31); //configura el registro del fondo 0 para que busque el mapa de tiles en el bloque 31.
BG_PALETTE_SUB[255] = RGB15(31,31,31); //ponemos el último color de la paleta de fondos de la pantalla principal a blanco
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16); //función básica proporcionada por libnds para poner texto. Sencilla pero con ciertas limitaciones
interrupciones(); //inicializamos las interrupciones
while(1) {
++lcounter; //con esto contamos cuantas veces pasa por el bucle
iprintf("\x1b[2;1HTemporizador %d", tiempo);
swiWaitForVBlank(); //con esta función detendremos la ejecución del bucle hasta que se produzca una interrupción por vblank
//tiene varios fines como sincronizar el bucle con el dibujado, ya que el bucle si no está continuamente
//realizándose a la máxima velocidad posible
}
return 0;
}
4. COMENTARIOS Bueno ésta creo que es una buena toma de contacto con las interrupciones y creo también que tenéis todo lo suficiente para poder trastear con ellas. Interrupciones como termporizadore, dma o fifo merecen un capítulo a parte (que quien sabe si podré dedicar, pero espero que si). De todos modos si echáis de menos algun ejemplo en concreto lo pedís.
También aclarar que cuando ponga nombre/nombre2 en los registros o funciones, significa que según la versión de libnds incluida en devkitpro han cambiado el nombre. La primera es la versión más nueva y la siguiente la antigua
5. ANEXO. BOTONES Y TÁCTIL El anexo de éste capítulo se lo vamos a dedicar a la gestión de botones y de táctil. La verdad es que libnds nos lo pone francamente sencillo. Lo primero que debemos saber es que tanto la táctil como los botones X e Y los recoge el subprocesador, aunque libnds hace de ello un proceso transparente. Estos datos son recogidos por el IPC del que no hace falta que nos preocupemos en este caso.Las demás teclas se quedan guardadas en el registro REG_KEYINPUT en el que los bits correspondientes a las teclas están en el mismo orden que en REG_KEYCNT. Vamos a ver sin más que posibilidades nos oferta libnds para leer botones:
*void scanKeys() Recoge las
nuevas teclas pulsadas del IPC y de KEYINPUT
*uint32 keysHeld(void) Devuelve las teclas que está
pulsadas *uint32 keysDown(void) Devuelve las teclas que
han sido pulsadas desde este scanKey
pero que en el anterior
no lo estaban *uint32 keysDownRepeat(void) En este caso devuelve las teclas
pulsadas según un intervalo de repetición que definimos con la siguiente función
*void keysSetRepeat( u8 setDelay, u8 setRepeat ) Con esto definimos primero
cuanto se tarda en detectar una tecla como pulsada y con el segundo parámetro,
cada cuanto volver a detectarla como pulsada
*uint32 keysUp(void) Devuelve las teclas que han
dejado de estar presionadas *touchPosition touchReadXY() Devuelve la
posición de la táctil Tenemos todo ¿no? Teclas presionadas, teclas que pulsas una vez, con repetición al levantar y la táctil. Vamos a ver ahora con que tenemos que
comparar los que nos devuelven estas funciones para comprobar el botón que queramos
KEY_A
KEY_B
KEY_SELECT
KEY_START
KEY_RIGHT
KEY_LEFT
KEY_UP
KEY_DOWN
KEY_R
KEY_L
KEY_X
KEY_Y
KEY_TOUCH -> hay presión sobre la táctil
KEY_LID -> estado de la tapa EJEMPLO -Qué hace En el ejemplo vamos a ir mirando todas estas funciones y mirando cuantás pulsaciones se contabilizan según la función usada. Luego también sacaremos por pantalla la posición del puntero dentro de la táctil y contaremos cuantas veces abrimos y cerramos la tapa de la consola
-Código #include "nds.h"
#include <nds/arm9/console.h>
#include <nds/arm9/input.h>
#include <stdio.h>
int teclaA=0;
int teclaB=0;
int teclaL=0;
int pantallaC=0;
int pantallaA=0;
void interrupciones(){
irqInit(); //inicializamos la tabla de interrupciones
irqSet(IRQ_VBLANK,0); //esta vez no asigno función para que veáis que no siempre es necesario. La activamos para que podamos
//usar swiWaitForVBlank
irqEnable(IRQ_VBLANK); //activamos las interrupciones
};
void logica(){
scanKeys(); //actualizamos los botones pulsadas
if (keysHeld()) iprintf("\x1b[3;1HAlgun boton esta pulsado "); //miramos si alguna tecla está pulsada
else iprintf("\x1b[3;1HNingun boton esta pulsado");
if (KEY_A & keysDownRepeat()) teclaA++; //si la tecla A está presionada (mediante repeticiones) se incrementa
if (KEY_B & keysDown()) teclaB++; //cuenta las veces que se presiona B
if (KEY_LID & keysDown()) pantallaC++; //miramos si cerramos la táctil
if (KEY_LID & keysUp()) pantallaA++; //miramos si cerramos la táctil
if (KEY_L & keysUp()) teclaL++; //cuenta las veces que se presiona B
if (KEY_TOUCH & keysHeld()) { //si hay presión sobre la táctil entonces miramos las coordenadas. Para una selección quizás mejor keysDown
touchPosition touch=touchReadXY();
iprintf("\x1b[1;1HTactil X %d, Y %d ",touch.px, touch.py);
}
iprintf("\x1b[4;1HEl boton A se ha pulsado %d",teclaA);
iprintf("\x1b[5;1HEl boton B se ha pulsado %d",teclaB);
iprintf("\x1b[6;1HEl boton L se ha levantado %d",teclaL);
iprintf("\x1b[8;1HPantalla cerrada %d",pantallaC);
iprintf("\x1b[9;1HPantalla abierta %d",pantallaA);
};
int main(void) {
powerON(POWER_ALL); //a partir de la r19 creo que no es necesario
lcdMainOnBottom(); //vamos a coger la sana costumbre escoger al principio donde poner la pantalla principal
videoSetModeSub(MODE_0_2D | DISPLAY_BG0_ACTIVE); //selecciona el modo de video 0 para la pantalla secundaria y activa el fondo 0
vramSetBankC(VRAM_C_SUB_BG); //reserva el banco C para fondos de la pantalla secundaria
SUB_BG0_CR = BG_MAP_BASE(31); //configura el registro del fondo 0 para que busque el mapa de tiles en el bloque 31.
BG_PALETTE_SUB[255] = RGB15(31,31,31); //ponemos el último color de la paleta de fondos de la pantalla principal a blanco
consoleInitDefault((u16*)SCREEN_BASE_BLOCK_SUB(31), (u16*)CHAR_BASE_BLOCK_SUB(0), 16); //función básica proporcionada por libnds para poner texto. Sencilla pero con ciertas limitaciones
interrupciones(); //inicializamos las interrupciones
keysSetRepeat(20, 20); //ponemos la repeticiones de teclas con un retraso de comienzo de 20 y de 20 retrasos por repeticion
while(1) {
logica();
swiWaitForVBlank(); //esperamos el vblank
}
return 0;
}
////////////////////////////////////////////////////////
Bueno ya está terminado y maquetado (casi hora y media!!!). Me gustaría que me dijerais que preferís respecto al código si como está con interoducción y luego todo comentado (para tener mejor vista general) o sólo comentar las partas relevantes de forma intercalada y quitando comentarios (o dejando). En los pequeños no importa mucho porque practicamente todo el código es importante. En los más grandes pues no se. Ala ahora a leerlo que si no me enfado.
He subido los ejemplos para las versiones antiguas de libnds y para las nuevas. Si no hay 2 versiones es que es común todo a las 2