TONC: Curso programacion GBA II

  • En este segundo tutorial se realiza una introducción a la pantalla de la GBA viendo los distintos periodos que tiene. Tambien se ve en que consiste la paleta de colores, las distintas representaciones gráficas que tenemos en la GBA y una primera forma de como realizar el sincronizamiento vertical.


Contenido

La pantalla de la GBA

  • La GBA tiene una pantalla LCD con las siguientes características:
  • 240 pixeles de ancho x 160 pixeles de alto.
  • Capaz de mostrar 32768 colores (15-bit)
  • La tasa de refresco es de 60 fps (59.73 Hz),
  • La GBA tiene 5 capas que pueden contener gráficos:
  • 4 capas de fondos.
  • 1 capa de sprite.
  • Capaz de realizar algunos efectos especiales que incluyen:
  • Mezclar dos capas.
  • Hacer mosaico.
  • Rotación y escalada.

Periodos de dibujo y en blanco

  • Como se ha comentado ya, toda la pantalla de la GBA es refrescada 60 veces por segundo. La siguiente imagen representa los diferentes periodos que se producen:


Periodos pantalla GBA TONC.png

nombre longitud ciclos
pixel 1 4
HDraw 240px 960
HBlank 68px 272
scanline HDraw + HBlank 1232
VDraw 160 scanlines 197120
VBlank 68 scanlines 83776
refresco VDraw + VBlank 280896



  • Para evitar el 'tearing' o ruptura de imagen los datos de posición de suelen actualizar en el VBlank. Esta es la razón por la que la mayoría de juegos corren a 60 o 30 fps. La sincronización en el VBlank conlleva que la regiones PAL a menudo tengan juegos más lentos ya que las televisiones PAL corren (corrían) a 50Hz, luego solo 50 fps en lugar de 60, luego juegos un 17% mas lentos.
  • Un refresco completo de la pantalla tarda 280896 ciclos, si los dividimos por la velocidad de reloj nos da 59.73 imágenes por segundo.

Colores y paletas

  • La GBA es capaz de mostrar colores de 16-bit en formato 5.5.5. Lo que significa 5 bits para el azul, 5 para el verde y 5 para el rojo. El bit que sobra no se usa.
X B B B B B G G G G G R R R R R


  • La GBA tiene dos paletas de 256 entradas de colores de 16-bit:
  • Una para los fondos. Empieza en la dirección de memoria 0500:0000h.
  • Una para los sprites (objetos). Empieza en la dirección de memoria 0500:0200h.
  • Los fondos y los sprites pueden usar las paletas de dos maneras dependiendo de como se configure el fondo o sprite en cuestión:
  • Como una sola paleta de 256 colores. 8-bit/px indicando que color de los 256 se utiliza.
  • Como 16 subpaletas individuales de 16 colores cada una. 4-bit/px indicando cual de las 16 subpaletas se utiliza.
  • Tanto los fondos como los sprites usan la entrada 0 de la paleta como color transparente. Los pixeles con ese color no se dibujarán, permitiendo a otras capas (fondos) y sprites mostrarse a través de el.

Representaciones gráficas

  • En la GBA hay tres tipos de representaciones gráficas:
  • Fondos 'bitmap'.
  • Fondos 'tiled'.
  • Sprites.
  • En los fondos 'bitmap' (imagen matricial) la memoria de vídeo funciona como un mapa de bits (rejilla rectangular de pixeles). Para dibujar un pixel en la localización (x,y), solo hay que ir a esa localización (y*w+x) y rellenarla con un color.
  • Los fondos 'tiled' trabajan totalmente de forma diferente. Primero se almacena tiles de 8x8 pixeles en una parte de la memoria de vídeo. Luego, en otra parte de la memoria, se construye el 'tile-map', el cual contiene los indices que le dicen a la GBA que 'tiles' forman la imagen que se ve en la pantalla. Para rellenar una pantalla solo es necesario un mapa de 30x20 de números (la GBA ya se preocupa en dibujar los 'tiles' a los que apuntan esos números).
  • Los sprites son objetos gráficos pequeños (desde 8x8 a 64x64 pixeles) que pueden ser transformados independientemente uno de los otros y pueden ser usados tanto con los fondos 'bitmap' como con los 'tiled'.

Los registros de pantalla

  • Existen 3 registros de I/O con los que tendrás que lidiar siempre que quieras hacer algo gráfico.

REG_DISPCNT

  • El registro REG_DISPCNT (0400:0000h) contiene los bits principales para el control de la pantalla.
F E D C B A 9 8 7 6 5 4 3 2 1 0
OW W1 W0 Obj BG3 BG2 BG1 BG0 FB OM HB PS GB Mode
bits nombre define descripción
0-2 Mode DCNT_MODEx. DCNT_MODE# Configura el modo de video. 0, 1, 2 son los modos 'tiled' y 3, 4, 5 son los modos 'bitmap'.
3 GB DCNT_GB Activado si el cartucho es un juego de GBC. Bit de solo lectura. 0=GBA, 1=GBC.
4 PS DCNT_PAGE Selección de página. Solo para los modos 4 y 5 donde se puede usar el cambio de página para una animación más suave. 0-1=Frame 0-1.
5 HB DCNT_OAM_HBL 1=Permite el acceso a OAM durante el HBlank. OAM esta normalmente bloqueada durante el VDraw. Previene el parpadeo de la pantalla cuando hay demasiados sprites en una 'scanline'.
6 OM DCNT_OBJ_1D Configura si los sprites almacenados en la VRAM usan una dimensión o dos. 1=1d, los tiles están almacenados secuencialmente. 2=2d, cada fila de tiles es almacenada 32x64 bytes desde el comienzo de la fila previa.
7 FB DCNT_BLANK 1=Fuerza un 'blank'.
8-C BG0-BG3, Obj DCNT_BGx, DCNT_OBJ. DCNT_LAYER# Activa el renderizado del fondo correspondiente y de los sprites. 0=Off, 1=On.
D-F W0, W1, OW DCNT_WINx, DCNT_WINOBJ Permite el uso de la ventana 0, 1 y la ventana de Objeto, respectivamente. Las ventanas pueden usarse para enmascarar/ocultar determinadas áreas (como hacia la lampara en el Zelda:LTTP). 0=Off, 1=On.

REG_DISPSTAT

  • El registro REG_DISPSTAT (0400:0004h) contiene los bits de estado de la pantalla y control de interrupción.
F E D C B A 9 8 7 6 5 4 3 2 1 0
VcT - VcI HbI VbI VcS HbS VbS
bits nombre define descripción
0 VbS DSTAT_IN_VBL Estado del VBlank (de solo lectura). 1 si esta dentro del VBlank, 0 si se encuentra en el VDraw.
1 HbS DSTAT_IN_HBL Estado del HBlank (de solo lectura). 1 si esta dentro del HBlank, 0 si se encuentra en el HDraw.
2 VcS DSTAT_IN_VCT Estado del 'trigger' del VCount (de solo lectura). 1 si la 'scanline' actual coincide con el valor del 'scanline trigger' (REG_VCOUNT == REG_DISPSTAT{8-F}).
3 VbI DSTAT_VBL_IRQ Solicitud de interrupción VBlank. Si está a 1 se dispara una interrupción en el VBlank.
4 HbI DSTAT_HBL_IRQ Solicitud de interrupción HBlank. Si está a 1 se dispara una interrupción en el HBlank.
5 VcI DSTAT_VCT_IRQ Solicitud de interrupción VCount.
6 7 - - No se usan.
8-F VcT DSTAT_VCT# Valor del trigger del VCount.
  • Cuando el valor del 'scanline' actual es idéntico al contenido del registro VCount (REG_VCOUNT == REG_DISPSTAT{8-F}) entonces el bit 2 del REG_DISPSTAT se pone a 1, y si el bit 5 del REG_DISPSTAT está a 1 se solicita la interrupción.

REG_VCOUNT

  • El registro REG_VCOUNT (0400:0006h) indica la 'scanline' actualmente dibujada. Cuenta desde 0 hasta 227.
F E D C B A 9 8 7 6 5 4 3 2 1 0
- Vc
bits nombre descripción
F E D C B A 9 8 - No se usan.
7 6 5 4 3 2 1 0 Vc Cuenta vertical ('scanline' actual). El rango es [0,227].


VSync

  • Usa el VBlank como un mecanismo de sincronización y para actualizar los datos del juego. A esto se le llama vsync (sincronización vertical). Hay varias maneras de hacer el vsync. Los dos métodos más comunes son:
  • Usar un bucle 'while' y comprobar REG_VCOUNT.
  • Usar un bucle 'while' y comprobar el último bit de REG_DISPSTAT{0}.
  • Por ejemplo, ya que el VBlank comienza en el 'scanline' 160, podriamos ver cuando el REG_VCOUNT supera este valor:
#define REG_VCOUNT *(u16*)0x04000006

void vid_vsync()
{    while(REG_VCOUNT < 160);	}
  • Esta forma de hacer el vsync presenta dos problemas:
  • El bucle no cambia el valor de la variable REG_VCOUNT. Para evitar que el compilador realice optimizaciones sobre dicha variable (metiéndola en un registro), debemos declararla volatile, ya que hay bastantes probabilidades de que el valor este por debajo de 160 por lo que tendríamos un bonito bucle infinito.
  • En demos simples esperar al VBlank no es suficiente, ya que quizas nos encontremos en el VBlank cuando llamemos a la subrutina vid_sync(), por lo que se consumirá inmediatamente y no sincronizaría a 60 fps. Para evitar esto primero hay que esperar al próximo VDraw para que cuando se produzca el próximo VBlank estemos el comienzo de este.
  • La versión corregida sería esta:
#define REG_VCOUNT *(vu16*)0x04000006

void vid_vsync()
{
    while(REG_VCOUNT >= 160);   // esperar al VDraw
    while(REG_VCOUNT < 160);    // esperar al VBlank
}
  • Esta es una manera sencilla de hacer el vsync, pero también es muy pobre, ya que mientras estamos en el bucle continuamos consumiendo ciclos de CPU (lo que implica un coste de batería) y debido a que no estamos haciendo absolutamente nada dentro del bucle 'while' estamos malgastando batería. La manera recomendada de hacer el vsync es poniendo la CPU en modo de baja potencia y cuando terminemos, usar una interrupción para volverla a la vida de nuevo (este método se verá en lecciones posteriores).

Enlaces Relacionados