Sunteți pe pagina 1din 89

Introduccin

es un curso de programacin dedicado obviamente a la Wii. La idea es que una persona con conocimientos de C mas

nos vago, pueda programar para dicha consola, facilitando en lo posible la entrada a este mundillo, con un poco de

tad por vuestra parte.

bis entrado aqu pensando que se os va a ensear a programar para Wii, os equivocas. Sois vosotros los que tenis

acer el esfuerzo de aprender y este tutorial solo puede allanaros el camino en lo posible. Puede que os rindis a las

ras de cambio u os animes mucho al principio, pero luego dejis el tema apartado debido a que la programacin no camino de rosas o mejor dicho, es como una rosa con espinas, donde te vas pinchando todo el rato con el tallo acceder a la flor.

os programadores opinan que para hacer determinadas tareas, C no es el lenguaje mas conveniente y que no

n pegas en aprender diferentes lenguajes de programacin si hace falta, pero sin embargo ponen muchas pegas a

der cosas de una determinada maquina que es muy diferente a lo que ellos suelen estar acostumbrados.

e un punto de vista prctico, todo lo que se os pueda ofrecer aqu seguramente no lo podis utilizar

sionalmente de una forma directa, pero no os olvidis que la programacin es un proceso creativo y que requiere

uena capacidad de adaptacin. Por tanto qu mejor aprendizaje que tratar con maquinas como las consolas, que

nen de un hardware relativamente limitado y tan diferente con los ordenadores de hoy da?, divertirse mientras

demos y hacemos algo diferente?. No tiene precio e incluso si eres un programador profesional, a lo mejor te sirve

recuperar aquello que os motiv a elegir esta profesin y que habis perdido con la rutina.

ea del tutorial es proporcionaros una base para que podis entreteneros haciendo programas en Wii, pero el uso que

is hacer de ello, depende de vosotros.

Comenzando por el Principio

mero que debis hacer es descargaros DevkitPro. Descargaros la version 1.4.7 del instalador:

tp://sourceforge.net/project/showfiles.php?group_id=114505&package_id=160396

is utilizando otras plataformas, mirate esto:

tp://wiki.devkitpro.org/index.php/Getting_Started

talador est desactualizado (y el curso tambien, por culpa de libfat) y os crear conflictos. Mira ste hilo que te

ar a conseguir lo ficheros necesarios:

tp://www.elotrolado.net/viewtopic.php?f=165&t=1257837&p=1716884329#p1716884329

Linux marcan os puede echar una mano y seguramente otros usuarios del foro.

ez instalado DevkitPro, tendris un entorno basado en MinGW que os permitir programar para diversas consolas y

is usar el famoso "make" (luego se explica algo ms sobre este tema). Adems, se incluye una utilidad por si

itis un editor, llamada Programmer Notepad o algo similar, que tal vez os resulte til. necesitas los parches de Hermes:

tp://mods.elotrolado.net/~hermes/curso_wii/devkitPro_Wii_by_Hermes.rar

debeis copiarlo para sustituir las libreras y los ficheros de cabecera de libogc donde tengais instalado DevkitPro,

scribiendo todo lo que sobre escriba. Se tratan de las libreras ya compiladas con lo ultimo del git de hackmii y que

e cambios que no encontrars en ninguna parte.

ultimo, algunos ejemplos hechos por Hermes:

tp://mods.elotrolado.net/~hermes/curso_wii/hermes_examples.rar

o lo podis instalar en devkitPro/examples/wii/hermes por ejemplo, para que tengis una referencia.

os entenderis bastantes cosas vindolas, porque no hay mejor maestro que disponer de un cdigo que muestre se hacen las cosas. Ah se incluye los fuentes de la screenlib, que es la base grfica que vamos a utilizar en este

o, ejemplos que muestran el uso de las fuentes de letra o el ultimo ejemplo que muestra como simular una batera

o el Nunchuk y el Wiimotes como baquetas.

erto, en este ultimo ejemplo, quiz os preguntis cmo diablos se controlan los acelermetros?, si sacas los

es a pantalla, puedes ver que si pones el mando en Vertical hacia arriba, te da unos valores distintos, que si los y hay cosas que se sobreentienden como "gforce" (fuerza G). La experimentacin es algo que vais a tener que usar

orma.

emplo, preparando este ejemplo, Hermes se dio cuenta de que hay un fallo en la librera que hace que no se

an usar los valores gforce u orientacin (orient) del Nunchuk, si no nicamente, los valores de aceleracin en RAW.

timo y en el apartado de las descargas, quiz os interese contar con: SpriteGen: editor de Sprites y mapas de Sprites: es una utilidad de Windows pero funciona desde Wine. Creador de fuentes: convierte fuentes ttf tratando de adaptar el tamao para exportalas al formato que usa la screenlib. Audacity: se usa a menudo para exportar y preparar efectos de sonido a formato RAW 8 bit con signo, con la ASNDLIB.

o de los ejemplos que Hermes ha subido, ha incluido un par de utilidades: filetochar que es una potente utilidad

onvertir archivos binarios a C usando varios tipos de variables y aadiendo alineacin, etc. y Wiiload, la utilidad

lemento del HBC para poder subir los ejecutables mediante WIFI.

in tenis un directorio de recursos con sonidos y algn grfico (mirar resources). Hay un ejecutable .bat que os

tra el uso de filetochar de modo prctico.

Cosas que debes saber del entorno de programacin


LOS MAKEFILE

nes de plataformas MSDOS/Windows, sabrs lo que es un .bat. Pues el Makefile es como uno de esos viejos

vos de procesamiento por lotes, pero mas a lo bestia, por as decirlo. Lo que aqu tienes que saber, es que dispones

a serie de plantillas de ejemplo y no importa que no conozcas todos los detalles, solo importa que sepas lo

ente para poder aprovecharlo a tu favor.

efecto los Makefiles que vienen en DevkitPro, tratan de compilar todos los archivos C que estn dentro del

rectorio "source" y el ejecutable toma el nombre del directorio en el que se encuentra el Makefile.

es, si tenemos un directorio llamado examples y dentro de el, el Makefile y un subdirectorio source conteniendo

tos archivos C, si desde linea de comandos hicieramos "make" el resultado sera que se compilaran todos los

vos dentro de source y obtendriamos un archivo examples.dol otro llamado examples.elf (que no deberamos

r en la release) y nos creara un subdirectorio build conteniendo los objetos de dicha compilacin. objetos dentro de build se utilizan para compilar rpidamente, evitando recompilar los archivos que no hemos

ado. Por lo tanto, si eliminamos el subdirectorio build podremos recompilar limpiamente todo de nuevo. A veces es

niente hacerlo para evitar ciertos problemas curiosos.

mbargo, en los Makefile suele existir una orden especifica que nos permite limpiar de objetos de una forma rpida.

en linea de comandos ponemos "make clean" se ejecutar una seccin de cdigo (si existe) que tratar de borrar

esos objetos.

ues:

make make clean

-> se utiliza para compilar -> se utiliza para borrar el resultado de las compilaciones

orma de acelerar esto desde Windows, sera crear un fichero bat como los que se usa en los ejemplos:

keIt.bat

make pause

use" se utiliza para que la ventana no se cierre automticamente y quede a la espera de que se pulse una tecla.

general, es mejor idea utilizar un IDE que sea capaz de hacer "make" y capturar el resultado a una ventana del

ama. Esto suele tener la ventaja aadida de que si se producen muchos errores, bajo linea de comandos se suele

r parte del listado y que algunos IDE permiten ir directamente a la linea causante del problema pinchando en el

INTERIORIDADES DE UN MAKEFILE

dentramos en tierras Marcianas para aprender a controlar algunas cosillas de nuestrosMakefiles.

is algunos de los ejemplos, quiz habris notado que los Makefile compilan solo los ficheros que se necesitan y

ncluso se permite compilar ficheros que estn fuera del subdirectorio source tomndolos desde el

orio resources.

mo se consigue eso?. Pues, si miramos en las "tripas" del Makefile, vemos cosas como:

TARGET := $(notdir $(CURDIR)) BUILD := build SOURCES := source RESOURCES := ../../resources DATA := data INCLUDES := include ../resources

son como las variables de entorno que se pueden usar en Windows y en este caso estn indicando: TARGET: este es el nombre del fichero que queremos obtener, el codigo "raro" que le sigue, le est diciendo que pille el nombre de actual directorio pero sin ruta. Esa es la razn de que si se compila example1, obtengmos un example1.dol. BUILD y SOURCES: son la razn de que se compile lo que hay dentro del subdirectoriosource y que los objetos se almacenen en el subdirectorio build. RESOURCES: es una variable que se crea para poder compilar los fuentes externos. El .. implica subir un directorio y dado que al coger los fuentes, el compilador estar en el directorio source ../../ nos sita fuera del directorio donde estamos compilando y ../../resources nos mete dentro del directorio resources de los ejemplos. DATA: por lo general devkitPro utiliza este subdirectorio (que deberiais crear vosotros) para tratar con ciertos tipos de datos. INCLUDE: esta variable os permite especificar varios destinos donde encontrar ficheros .h necesarios para vuestra aplicacin. En este caso, sealan a un hipottico subdirectorio include y a ../resources, para que encuentre los ficheros .h de los datos que tenemos enresources.

bajando un poco vemos:

LIBS := ../../tremor/libtremor.a -lfat -lasnd -lmodplay -lwiiuse lscreen -lbte -lz -logc -lm

ara referencia a la libreria libtremor.a que se encuentra en el directorio "tremor" que es "hermano" del directorio de

icacin que estamos compilando.

talle importante: El orden de colocacin de las libreras, es importante. Hay libreras que usan funciones que

den de otras libreras y que si no estn correctamente ordenadas, se produce un error. La forma de ordenarla, es la

nte: aqu el ltimo elemento, es el primero.

decir, que para construir nuestro ejecutable, libm.a (-lm) se utiliza antes que libtremor.a.

o, aclarado esta parte, bajemos un poco ms:

#CFILES := $(dir)/*.c))) CFILES :=

$(foreach dir,$(SOURCES),$(notdir $(wildcard main.c $(RESOURCES)/econmy.c

hay varios detalles importantes. Dentro del Makefile, el smbolo # se utiliza para comentar, por lo que el resto de

ea es ignorado. Si echis un ojo a esa linea "rara" se ve que es la responsable de que se compilaran todos los

os .c presentes en el subdirectorio source.

gar de eso, se ha reemplazado por los ficheros a compilar. El primero es main.c que est dentro de source y el

ndo emplea el contenido de la variable RESOURCES para componer la ruta donde encontrar "econmy.c" que es el

o MOD que usa el primero de los ejemplos que se han pasado. veis, la forma de usar el contenido de la variable RESOURCES es $(RESOURCES).

se quiere aadir mas ficheros .c que es lo que se tiene que hacer?. Pues aadirlos a la lnea, como estis viendo.

orma de trabajar es conveniente, por que por ejemplo, si se usara la forma "automatica" que utilizan los ejemplos

vkitPro, aadira todos los ficheros C presente en RESOURCES, cuando solo necesitamos uno.

o, hemos visto cosas bastante importantes, pero como puedo hacer que el Makefile interprete otras ordenes?. Por

plo, en algunos de los programas se utiliza el Makefile para ejecutar el programa haciendo "Make run".

es bien imaginate que al principio aades una variable como esta:

export WIILOAD =tcp:192.168.2.14

oad necesita una variable de entorno llamada WIILOAD con la IP de la Wii para poder pasar el ejecutable al HBC, al

r la palabra "export" el make se ocupar de ello, haciendo visible esa variable para todos los programas.

nos vamos mas abajo en el Makefile y encontramos este cdigo:

run:

wiiload $(OUTPUT).dol

cir: que el Makefile tiene la posibilidad de ejecutar "make run" pero necesitis dos cosas: aadir la variable de

no WIILOAD tal y como se explica (cambiando la IP por la de vuestra Wii obviamente) y por otro lado, teneis que

r wiiload.exe en alguna ruta sealada por el PATH. Un buen sitio sera C:\devkitPro\msys\bin

in teniendo en cuenta que est en la carpeta "util" que es un directorio "hermano" a la aplicacin, podrais utilizar

rmula:

run: ../util/wiiload $(OUTPUT).dol

s ejemplos, se usa ejecutar.bat que directamente, crea la variable de entorno y pasa un parmetro para evitar que

licaciones, al no ver ningn parmetro, interpreten que no se puede volver al cargador y al hacer reset, se vuelva

n del sistema.

sotros quereis aadir otros usos. pues por ejemplo, podes aadir:

convertir: [tab]../util/filetochar <loquesea>

e que [tab] representa al pulsar el tabulador para separar el comando. Luego la cosa sera hacer "make convertir"

que hicera esa tarea en concreto

Cosas que debes saber de la programacin bajo Wii


EL ENDIAN

ramar en C, no es programar en BASIC". Suena de perogrullo, pero esto tiene sus implicaciones.

mero que debes saber sobre Wii, es que utiliza "Big Endian" para almacenar los datos. Los procesadores Intel usan

lo contrario "Litte Endian".

ene que ver con la forma de almacenar el dato. Cuando un dato ocupa mas de un byte, existen dos formas de

izarlo de mayor a menor peso, que eso se conoce como Big Endian y de menor a mayor peso, que se conoce como

Endian.

ataformas Intel se utiliza la ordenacin de menor a mayor (Little), asi por ejemplo, para almacenar en memoria el

256 en decimal, el primer byte sera 0 y el segundo 1 (1*256+0= 256). Sin embargo, en la Wii sera al reves,

ro el 1 y despues el 0.

n lo cual si por ejemplo, quieres importar un BMP, o un WAV, pues te toca cambiar el orden de los bytes de los

s una de las razones por las que es util exportar los datos a C en RAW, puesto que si importas una lista de numeros bits, no se ve afectada por la forma de almacenar el numero en memoria (es decir, 32767 es el mismo numero en

s sistemas, pero cambia la forma en que ese numero se almacena en memoria o dentro de un fichero, por

plo).

pre que importes datos desde una plataforma Intel, tendrs que tener en cuenta el tipo de Endian que usan los

convertir el Endian, puedes usar estas funciones:

unsigned short inline swap_16(unsigned short a) { // para numeros de 16 bits return( (a<<8) | (a>>8)); } unsigned inline swap_32(unsigned a) { // para numeros de 32 bits return( (a<<24) | (a>>24) | ((a<<8) & 0xff0000) | ((a>>8) & 0xff00)); }
LOS TIPOS DE DATOS

i se usan los tipos de siempre, pero ademas aade otros tipos que son abreviaciones, de forma usual:

char

s8

entero de 8 bits con signo: rango -128 a 127

unsigned char

u8

entero de 8 bits sin signo: rango 0 a 255

short

s16

entero de 16 bits con signo: rango -32768 a 32767

unsigned short

u16

entero de 16 bits sin signo: rango 0 a 65535

int

s32

entero de 32 bits con signo: rango -0x80000000 a 0x7fffffff

unsigned int

u32

entero de 32 bits sin signo: rango 0 a 0xffffffff

long

s3

lo mismo que int

unsigned long

u32

lo mismo que unsigned int

entero de 64 bits con signo: rango -0x8000000000000000 a long long s64 0x7fffffffffffffff

unsigned long long

u64

entero de 64 bits sin signo: rango 0 a 0xffffffffffffffff

float

f32

Numero con coma flotante de 32 bits

double

f64

Numero con coma flotante de 64 bits

ien podeis ver formas como vu16 que se refieren a un tipo "volatile" y otros tipos predefinidos en

tPro/libogc/include/gctypes.h.

LA ALINEACIN DE LOS DATOS

es algo que tenis que, necesariamente, tener en cuenta en maquinas como Wii y es que su procesador, adems de

jar los tipos que he listado arriba, necesita almacenarlos en memoria alineados con respecto a su tamao.

decir, si se esta manejando un int el procesador solo puede leer ints o almacenar ints en direcciones alineadas a 4

: 0, 4, 8, 12, 16.....

ataforma Intel estas acostumbrado a que un int, lo puedes guardar en direcciones desalineadas. y esto es algo que

s que tener, muy pero que muy en cuenta.

ejemplo, si defines un struct:

struct { char lie;

int repido; } manue;

structura no ser igual compilando en Windows que en Wii, por qu?. Pues porque en Windows la alineacin del

no es problema, por lo que "lie" ocupar un byte y "repido" 4 bytes. Sin embargo, en Wii el compilador adems de

aadir 3 bytes de padding despues de "lie" para que asegurar que "repido" est alineado a 4 bytes.

es algo que tendris que tener en cuenta y usar inteligentemente las estructuras y gestionar las variables de distinto

o adecuadamente: acostumbrarse a alinear los datos y paddearlos de forma adecuada, puede redundar en una

r velocidad e incluso menor espacio.

sucede si el procesador lee un dato de forma desalineada al tamao del dato?. Pues que os volveris locos para

trar el bug seguramente.

LA ALINEACIN DE DATOS QUE USEN DMA Y EL TRATAMIENTO DE LA MEMORIA

ma es que el procesador trabaja sobre memoria cacheada normalmente, mientras que las DMA (perifricos que uso) no. Por lo tanto, debemos tener en cuenta dos cosas: que los perifricos suelen acceder a memoria usando

eterminada alineacin, al igual que pasa con los datos normales y que existe un mecanismo que se encarga de

scribir de la memoria para refrescar la cach.

neacin utilizada es de 32 bytes. Para obtener memoria con esa alineacin, podemos recurrir a usar memalign(32,

lugar de malloc(x) para asignar memoria, pero tambien podemos declarar una variable global de forma que tenga

ineacin:

u8 buffer[1024] __attribute__ ((aligned (32))); u8 buffer[1024] __attribute__ ((aligned (32)))={0}; // si quereis fijar algun dato.

neas de cache usan 32 bytes, por lo tanto, es conveniente alnear el dato a 32 y asi mismo asegurar el padding

s de relleno):

#define MAX_BYTES 17 u8 buffer[MAX_BYTES+32] __attribute__ ((aligned (32))); // forma rapida de aplicar un padding de seguridad

emplo, si quierees subir una textura o un sonido con ASNDLIB, es preciso que la memoria pasada est alineada

amental) y con el correspondiente padding (conveniente). Si no se tienen en cuenta esas reglas, es posible que el

de la cache, MACHAQUE datos de la pila o de otras variables.

EL MECANISMO DE LA CACHE

en una serie de funciones encargadas del refresco de la cache y de su escritura en memoria. Prestad atencin

e no comprender el uso de estas funciones, suele acarrear muchos problemas. Dentro de screenlib o de la

LIB, no precisis utilizarlas, pues ya se usan de forma automtica, pero os ser til para otras cosas: DCFlushRange(void *startaddress,u32 len);: esta funcin recibe la direccin de memoria (debera estar alineada a 32) y la longitud en bytes (debera estar paddeada, pero ya lo hace ella) de la memoria que queremos "flushear". Es decir, lo que hace es escribir los datos desde la cach a la memoria, si en la cache hay cambios que no se han reflejado en la memoria en todo ese rango. Por eso es til llamarla antes de hacer una DMA. DCInvalidateRange(void *startaddress,u32 len);: esta funcin recibe la direccin de memoria (debera estar alineada a 32) y la longitud en bytes (debera estar paddeada, pero ya lo hace ella) de la memoria que queremos "invalidar" en la cach. Es decir, lo que hace es leer desde la memoria todo ese rango, perdiendo todos los datos almacenados en la cach. Resulta til si un perifrico nos suministra datos en memoria mediante DMA, para que la cach se refresque y al leer desde el procesador, obtengamos los datos correctos.

El truco del almendruco

xcepciones son un fastidio, cada vez que el programa la casca, de donde viene el problema?.

libreras se han pasado, tenis una modificacin particular de Hermes del cdigo encargado de gestionar

ciones. En lugar de pegar el pantallazo y quedarse muerto en espera de que pulses un boton del PAD de

cube, aqu a los 8 segundos tratar de volver al cargador.

ambin tiene otro truco, existe una variable de cadena que podeis cambiar de forma externa, para que os muestre

xto al ocurrir la excepcion:

extern char *debug_str;

una funcion aades por ejemplo:

void funcion() { debug_str="funcion()"; ...... ...... debug_str="Indefinido"; }

ntro de funcin se produce una excepcin, se mostrar la cadena "funcion()" y sabris que la excepcin ha ocurrido

e punto.

in existe un truco para conocer exactamente, en que funcin ha ocurrido la excepcin. Con debug_str tal vez

as saber en que funcin global haya ocurrido, pero de forma imprecisa. Mas bien os puede servir para acorralar el

ema, si es reiterativo, pero existe otro truco: cuando sale la pantalla de excepcin, aparece un listado de los

ros y justo abajo, aparece una serie de direcciones de memoria que son la traza de donde ocurri la excepcin.

es bien, existe una utilidad que permite a partir de la informacin de debug que proporciona el elf, conocer a que

n pertenece esa direccin de memoria:

set path=%path%;C:\devkitPro\devkitPPC\bin powerpc-gekko-addr2line.exe -e Guitarfun.elf -f 0x805d5400 pause

odeis ver un uso especfico.

La screenlib (prolegmenos)

o, toca meterse en materia directa, as que primero nos alejaremos un poco para ver las cosas en perspectiva.

rera grfica, como todas, se basa en las GX (podis descargar un interesante documento sobre el tema aqu).

ra consola utiliza un mecanismo para pasarle los datos al GX, llamado FIFO (First In, First Out) que tiene como una

s cualidades que permite pasar datos desalineados con su tamao (es decir, aqu es posible enviarle un int sin que

alineado a 4 con la memoria empleada en el FIFO) y que en definitiva, marca la capacidad total de instrucciones/

/ vrtices que podeis enviar al GX.

mao empleado por defecto en screenlib es fijado desde FIFO_SIZE a 1MB exacto. Evidentemente, es posible usar

tamaos mayores si es necesario, pero 1MB es un tamao mas que suficiente para muchos usos e incluso se puede

car un salto para que el GX utilice otra memoria fuera del FIFO para desarrollar sus tareas (mi programa Guitarfun esa tcnica).

bueno, no vamos a perdernos en divagaciones, basta con saber que en cada frame de vdeo, disponemos de un

de 1MB para enviar datos al GX, pero que el GX va machacando instrucciones al mismo tiempo, por lo que va

do espacio libre para nuevas instrucciones / datos (trabaja en anillo) y que posiblemente, es algo de lo que no te

s que preocupar.

ro lado, conviene que sepis que las GX utilizan por hardware dos matrices de transformacin de polgonos: una de

ccin y otra Mundial. En screenlib la Mundial no se utiliza (se le pasa una matriz unitaria, que no provoca cambios)

e Proyeccin es fijada para usar el mtodo Ortogrfico que es mas adecuado para usos 2D.

cha matriz se establece un rango X/Y ajustado a la resolucin de pantalla y con un ligero reajuste en el caso de

ucin NTSC. Para la Z, se utiliza un rango de 1000.

namente, podis conocer el ancho y el alto de la resolucin empleada gracias a las

bles SCR_WIDTH y SCR_HEIGHT. Si la resolucin es <=480 es obvio que se trata de una seal progresiva o que refrescan a 60Hz (aqui la resolucin sera de 640x480). En modo PAL la resolucin es de 640x528 y el refresco

Hz.

o de Z, como ya se ha comentado utiliza un rango de 1000. De forma interna, el rango sera de 0 a -999, siendo ese

el punto mas alejado. En la librera puesto que tratamos con 2D, se utiliza el trmino de layer (capa) puesto que en suelen utilizar varias capas de grficos y para hacer mas amigable el tema, se niega el valor para hacer que el sea de 0 a 999 y evitar el uso de nmeros negativos. Asi pues, podemos hacer uso de 1000 capas y la capa 999 se

a considerar como el fondo de pantalla, mientras que layer= 0 es poner los graficos en primer plano. Al utilizar una

ccin Ortogrfica, no se produce un escalado con la distancia para disminuir el tamao, tenedlo en cuenta.

TRANSPARENCIAS Y TRANSLUCIDEZ

enlib es inicializada para utilizar colores con Alfa por debajo de 8 (en un rango de 0 a 255) como colores

parentes, tanto en texturas como en color directo a los polgonos. Esto significa que no se debe hacer uso de los

s de color que no empleen Alpha (modo 16bits RGB565) dado que dichos colores no sern dibujados en pantalla.

as a esto podemos dibujar Sprites sin que su color de fondo, se superponga al fondo (evidentemente, siempre que

or usado de fondo, sea considerado transparente). Si utilizis el programa Spritegen y queris exportar los sprites

r directo de 16 bits, en "Export to C", desmarcar "Use Palette" y en "Direct Color" selecciona 15 bits. Si queris

rtir directamente esos sprites en tiles, podis fijar la alineacin a 32 bytes ah. De esta forma exportareis los sprites RGB5A1 y si el fondo est utilizando el color 0 (que por defecto tiene Alpha=0 en Spritegen) pues ya lo tenis listo

uncionar. Solo os faltar crear la textura a partir de los datos del sprite para tenerlo funcionando.

ejemplo 3, podis ver que se ha hecho una exportacin de datos desde Spritegen a 8 bits con paleta ("Export to

arcar "Alpha Enable" y "Use Palette". "Direct Color/ Palette Color" a 15 bits). En dicho ejemplo, se asigna memoria

da para alojar los tiles de los sprites y se conserva el mapa de color original. Eso os permite no tener que alinear

tos exportados, pero la ventaja principal, es que sabiendo que el indice de color transparente es 0, podis utilizar el original para detectar colisiones de forma mas precisa: un color en el mapa original distinto a 0 representa algo

o". podis observar, si exporto color directo o con paleta a 15 bits (+ 1 de Alpha! y Wii solo admite paletas de 16

) en realidad, ese Alpha solo va a servir para determinar si un color es transparente o no (si se dibuja o no se

a, vamos): pero Wii puede usar un rango mas amplio que 16 bits (y realmente, Wii trata de forma especial los

es con el bit Alpha=0, puesto que usa un modo de color denominado RGB5A3 y por eso a RGB5A1 es conveniente

l valor global sea 0 para Alpha=0 para evitar cosas raras).

alidad, vosotros podis especificar tambin un color en formato RGBA8 (8 bits por componente) para dibujar

ficies, o cajas desde screenlib y eso os permite hacer una especie de filtro para los usos con textura o sin textura.

ta forma, entramos en el terreno de la translucidez.

nslucidez es un fenmeno que consiste en poder ver objetos que estn detrs de otros objetos a travs de ellos.

or ejemplo, un cristal es un material translcido, pues permite ver lo que hay detrs aunque le cause una

erencia (por ejemplo, si ves a travs de un cristal verdoso, los objetos de detrs tomarn dicha tonalidad). La

ncia real con la transparencia es que esta ltima, realmente no dibuja los pxeles por lo que no ocupan espacio en

enacin Z (el layer). As pues, para dibujar objetos translcidos de forma correcta, debemos dibujar PRIMERO los

os que queden detrs, pues si el objeto translcido usa layer 0, la ordenacin Z no permitir que se dibuje nada

s (por ejemplo, en layer 100). Y las partes transparentes, en realidad son "agujeros" en el objeto

ma de interpretar esto, mas o menos es asi:

n un rango de 0 a 255: Alpha <8 -> Transparente, Alpha>=8 ->Nivel de translucidez, Alpha==255 -> Color Slido

blamos de color RGB5A1 seria:

pha==0 -> Transparente, Alpha==1 -> Color Solido

La screenlib
INICIALIZAR

nicializar la librera, se usa esta funcin:

InitScreen();

es todo lo que tienes que hacer en tu main() para que el video est operativo usando la configuracin que tengas en

cerlo, SCR_WIDTH y SCR_HEIGHT te informar de la resolucin que estas empleando y se explic en el anterior

ulo, puedes determinar si el refresco es a 60Hz porque el alto sera de 480 pxeles.

mbargo, conviene recordar que la lbrera trabajar pensando en 4:3 y no en 16:9. En ese caso, te tocar a ti

minar el formato de la pantalla, ya que la resolucin de salida seguira siendo la misma y aplicar los factores de

a correspondiente.

quieres conocer si estas usando una configuracin de 16:9 puedes inicializar as:

int is_16_9=0; CONF_Init(); is_16_9=CONF_GetAspectRatio(); InitScreen();

artir de ah, hacer los ajustes pertinentes.

CAMBIAR EL VIEWPORT (NIVEL: USUARIO AVANZADO)

usos especiales, puedes cambiar el Viewport. Por ejemplo, podras virtualizar la pantalla para tratar los 16:9 de proporcionada directamente, cambiando el ancho, pero no se aconseja porque por ejemplo, si ajustases para

ar a 854x480, realmente, estaras condensando 1,333 pxeles en 1. Para ciertos grficos quiz no sea mucho

ema, pero para letras o pequeos detalles, es mejor desproporcionar un poco a que te falte resolucin.

ejemplo 3 se muestra como utilizar un Viewport de 480x360 para "inventarme" una resolucin de la que carece Wii

ta forma:

ChangeProjection(0, -10*(SCR_HEIGHT<=480), 479, 360+10*(SCR_HEIGHT<=480));

mo se puede apreciar, se aplica una correccin para video NTSC (<=480) y espero que entiendas que esos valores

xperimentales: en una TV se puede ver perfecto, pero quizs en otra no.

alguna razn quisieras volver a los valores originales, es tan facil como usar sta funcin:

RestoreProjection();

ejemplo 3 se puede observar como se cambia el valor de una variable: xclip. Esta variable se utiliza para limitar derecha el texto visible (funcin s_printf, que veremos mas adelante)

FINALIZANDO EL FRAME

ncin Screen_flip(); se encarga de esperar a que se dibujen todos los grficos, sincronizar con el video vertical,

ambiar el buffer de la pantalla y borrar el nuevo, asi como el buffer Z.

ltima funcin que debes llamar para completar el frame de video y mientras no sea llamada, no podrs apreciar

mbios efectuados.

DIBUJANDO CARACTERES

rera dispone internamente, de dos sets de caracteres ANSI, los cuales son facilmente seleccionables con la

n SelectFontTexture:

SelectFontTexture(0); // -> selecciona el font por defecto SelectFontTexture(1); // -> selecciona el font extra/externo

vel Avanzado)

rera permite reemplazar el segundo juego de caracteres por otro que haya capturado el usuario utilizando la

ad ttf2raw (ver utilidades recomendadas al principio). Para ello se dispone de la funcin:

UploadFontTextureExt(unsigned char *punt);

e punt sera el lugar de la memoria donde tenemos alojado el nuevo font.

puede restablecer el font original usando UploadFontTextureExt(INTERNAL_EXTFONT); Si quieres cambiar el font

efecto, pues te tocara modificar screenlib a tu gusto

n de Nivel avanzado)

ariables PX y PY: son las encargadas de proporcionar las coordenadas de inicio del texto (en pxeles) que

miremos con la funcion s_printf

: se encarga de suministrar el color de las letras en formato RGBA8 (ten cuidado de no utilizar esta variable para

osa).

or: proporciona el color del fondo tambien en formato RGBA8. Por defecto es 0.

center: est pensada para centrar en horizontal el texto si la pones a 1. Por defecto est a 0 (desactivada).

etter: Ccontrola los diferentes tamaos de letra, pero para vosotros sera mejor usar la funcin letter_size().

r_size(tamx, tamy);: ajusta el tamao de las letras a tamx, tamy. El font original es a 16x32 pero podis

arlo a diferentes tamaos as.

(avanzado): esta variable controla el lmite derecho de las letras para su visualizacin. Si un caracter supera ese

, el resto del texto ser ignorado, pero nota que esto no equivale a hacer un recorte exacto en ese punto y si un

ter excede por ancho ese lmite, ser totalmente visible si es posible

ntf( char *format, ...);: funciona de forma similar a la funcin estndar printf, que como es de sobra conocida,

no necesita mucha explicacin. La nica diferencia, es que si un texto se sale de la pantalla, no se proceder a un

o de lnea automtico, cosa lgica si pensais que aqu el tamao de letra es variable. Por cierto, las letras desde

se escriben en layer 0.

_str(u32 x, u32 y, u32 z, u32 color, char *string,u16 tamx, u16 tamy);: si lo que queris es imprimir una

e cadena, sin formato, etc, podis utilizar sta funcin que como podis observar, necesita de coordenadas x,y,z (la si se aleja en sentido negativo), color (para el fondo usa bkcolor), la cadena de texto y el ancho y alto de los

teres.

La screenlib - Texturas

exturas son un asunto que merecen un captulo aparte dentro de sta documentacin. La screenlib ofrece soporte

exturas con paleta (soportando las 16 paletas que provee las GX) o sin paleta y tambin para modos de color que

n directamente soportados por las GX, pero con una pequea adaptacin son posibles. Antes de nada, tienes que que Wii aloja sus texturas en la memoria "normal", es decir, en otras mquinas tienes que subirlas a la VRAM,

ras que aqu bastar con que esa memoria est alineada con 32 (recuerda memalign, que lo mencionamos al

pio) y que hayamos flusheado los datos, puesto que la textura se trabaja fuera de cach por las GX. Como

eenlib no pretende ser un sustituto de las GX, si no un complemento de ellas, para trabajar, necesitars usar los

o objetos para manejar texturas:

GXTexObj text; // -> esta es la forma de definir un objeto de textura GXTlutObj palette; //-> esta es la forma de definir un objeto de paleta

exturas se organizan internamente en tiles, generalmente de 4x4, pero hay otras organizaciones de datos. Por

ucin, sera conveniente que tanto el ancho, como el alto de la textura, fuera divisible entre 8 para estar seguro de

uestra textura no parte tiles (que yo no recuerdo el lmite exacto y en todo caso, es un margen mucho mas

oso que usar texturas en base 2 que precisan otros sistemas)

mensiones mximas a usar en textura son de 1024x1024, lo que implica un tamao empleado de 1,2 o 4 MB (asi te parece pequeo, ya ves que no lo es tanto).

CREANDO PALETAS

namente, screenlib utiliza una funcin llamada ctlut() para crear una paleta partiendo de los datos proporcionados

ma externa.

mato de las paletas es de 16 bits y para trabajar con la screenlib deberias usar modos TLUT_RGB5A3,

_RGB5A1, TLUT_SRGB5A1:

#define Cambia el orden de los bytes del color (util para importaciones) TLUT_LITTLE_ENDIAN 128

Para RGB5A3 (si el bit Alpha de mas peso, es 1, el color es RGB5A1, en otro #define TLUT_RGB5A3 2 caso RGB4A3), Azul menor peso

Pseudo RGB5A1 (es RGB5A3, pero asumiendo que si Alpha==0,color==0) #define TLUT_RGB5A1 2 ,Azul menor peso

#define TLUT_RGB565 3

Este no lo deberias usar, al carecer de Alpha

#define TLUT_SRGB565 4

Este tampoco lo deberias usar

Para RGB5A1 con R y B intercambiado, Rojo menor peso (al estilo de #define TLUT_SRGB5A1 5 SpriteGen)

emoria de la paleta debe estar alineada a 32 bytes y ser de tipo u16 (unsigned short). Si quieres asignar un color

leta manualmente, partiendo desde un color RGBA8 (con Rojo como menor peso) puedes usar Color5551(x) para

er un color en formato TLUT_SRGB5A1. Ejemplo:

Color5551(0xffff0000); //-> convierte color Solido y Azul intenso al formato SRGB5A1

ncion para crear la paleta, es:

void CreatePalette(GXTlutObj *palette, int format, int pal_index, u16 *mem, int entries);

e: palette: puntero a objeto de paleta. format: formato de color (TLUT_RGB5A3, TLUT_RGB5A1, TLUT_SRGB5A1). pal_index ndice de paleta a usar dentro de las GX (0 a 15). Tambien fija una variable para relacionar paleta y textura. mem memoria conteniendo las entradas de paleta (alineada a 32, recuerda). Recuerda que los datos originales sern modificados. entries nmero de entradas de la paleta (hasta 256, aunque fsicamente, hay paletas que soportan 4096 colores).

LA FUNCIN SETPALETTE()

alette(int pal_index); se usa para fijar una variable interna con el ndice de paleta (de 0 a 15) a utilizar cuando

mos una textura que haga uso de paletas. Lo normal es que primero creemos paleta, lo cual nos ajusta esa variable

ma automtica y luego procediramos a crear nuestras texturas, pero qu sucede si quiero hacer una serie de

os en la textura en cada frame? Pues que nos tocar decirle de alguna forma que paleta debe usar al crearse la

ra y para eso tenemos sta funcin.

CREANDO TEXTURAS

eso tenemos sta funcin:

void CreateTexture(GXTexObj *texture, int format, void *mem, int width, int height, int repeat);
texture: puntero objeto de textura. format: formato de color: TILE_CI4, TILE_CI8, TILE_RGB5A1, TILE_RGB5A3, TILE_SRGB5A1, TILE_RGBA8, TILE_SRGBA8. mem: memoria que contiene los datos de textura y donde se formarn los tiles (es decir, que se modifican los datos). Alineada a 32 bytes. width: ancho de la textura. height: alto de la textura. repeat: flag que ajusta si la textura se repite en forma de mosaico si se exceden las coordenadas de textura (por defecto, usa 0).

scripcin del formato de color:

#define Cambia el orden de los bytes de color (util para las importaciones) TILE_LITTLE_ENDIAN 128

#define TILE_CI4 0

Para CI4 (color indirecto de 4 bits, o sea, con paleta)

#define TILE_CI8 1

For CI8 (color indirecto de 8 bits, o sea, con paleta)

Para RGB5A3 (si el bit Alpha de mas peso, es 1, el color es RGB5A1, en otro #define TILE_RGB5A3 2 caso RGB4A3), Azul menor peso

Pseudo RGB5A1 (es RGB5A3, pero asumiendo que si Alpha==0,color==0) #define TILE_RGB5A1 2 ,Azul menor peso

#define TILE_RGB565 3

No deberias usarlo

#define TILE_SRGB565 4

No deberias usarlo

#define TILE_SRGB5A1 5

Para RGB5A1 con R y B intercambiados. Rojo menor peso

#define TILE_RGBA8 6

Para RGBA8. Azul menor peso

#define TILE_SRGBA8 7

Para RGBA8 con R y B intercambiados. Rojo menor peso

uario avanzado)

efecto las texturas usan filtrado bilineal. Es posible cambiar a GX_NEAR (para que se vean cuadraditos si ampliamos

tura) si utilizamos antes de crear la textura la siguiente funcin:

UseTexLOD(GX_NEAR , GX_NEAR);

a restablecer:

UseTexLOD(GX_LINEAR , GX_LINEAR);

o que es un uso relativamente raro, no he querido aadir un nuevo parmetro a la funcin de crear textura que en

do, va a ser un lastre.

n usuario avanzado)

SELECCIONANDO TEXTURA

seleccionar la textura en las funciones de dibujo, se utiliza sta funcin:

SetTexture(GXTexObj *texture);

se puede apreciar, se le pasa un puntero al objeto de textura. Si se le pasa NULL (Valor 0), esas funciones no

n textura y simplemente, aplicarn el color que le pasemos.

en una serie de texturas predefinidas para poder usarlas como patrones de relleno:

#define #define #define #define #define

STRIPED_PATTERN &tex_pattern[0] CROSS_PATTERN &tex_pattern[1] SQUARE_PATTERN &tex_pattern[2] MOSAIC_PATTERN &tex_pattern[3] NULL_PATTERN NULL

n: SetTexture(MOSAIC_PATTERN); Se utilizara ese patrn de relleno con las funciones de dibujo.

La screenlib - Funciones de Dibujo

reenlib se dise principalmente, como un conjunto de herramientas que permitiesen disear mens de una forma

o menos sencilla, por lo que no es extrao que muchas de sus funciones se limiten a dibujar distintos tipos de cajas

bordes de dichas cajas, con el fin de embellecerlas y por otro lado, marcar la diferencia entre una caja seleccionada que no lo es.

o de la librera, no hay ninguna regla que impida hacer un uso 3D o 2D diferente y no hace falta trastear mucho en

entes de la librera para ver de que forma utiliza las GX y de que forma pueden convivir. Desde un punto de vista mas bien pseudo 2D) se ha tratado de facilitar ciertos usos especiales, pero adems estas funciones, suponen un

ejemplo de como se utilizan las cosas desde las GX y os da pistas sobre que es lo que deberais alterar si por

plo, queremos una proyeccin que supere el rango de 0 a 999 en Z o usar grficos en perspectiva y meternos de

en las 3D.

unciones para dibujar cajas rellenas (con un color o con una textura), son estas:

// dibuja una caja cuadrada rellena DrawFillBox(int x,int y, int width, int height, int layer, u32 color); // dibuja una caja con las esquinas redondeadas, rellena DrawRoundFillBox(int x,int y, int width, int height, int layer, u32 color); // dibuja una caja con los lados izq-der redondeados, rellena DrawRoundFillBox2(int x,int y, int width, int height, int layer, u32 color);
x, y: posicin de la caja en pxeles. width, height: dimensiones de la caja en pxeles. layer: capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z).

color: color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso).

unciones para dibujar el borde de las cajas (con un color o con una textura), son estas:

// dibuja el borde de una caja cuadrada DrawBox(int x,int y, int width, int height, int layer, int thickness, u32 color); // dibuja el borde de una caja con esquinas redondeadas DrawRoundBox(int x,int y, int width, int height, int layer, int thickness, u32 color); // dibuja el borde de una caja con los lados izq-der redondeados DrawRoundBox2(int x,int y, int width, int height, int layer, int thickness, u32 color);
x, y: posicin de la caja en pxeles. width, height: dimensiones de la caja en pxeles. layer: capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z). thickness: grosor de la linea de 1 a lo que admita. color: color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso).

se puede apreciar, son funciones muy sencillitas.

DIBUJANDO SUPERFICIES CURVAS

porte de la librera se limita a dibujar Elipses y grficos tipo tarta (DrawSlice).Con la funcin Elipse se pueden

er crculos y circunferenvias. Y es ms, teniendo en cuenta que las proporciones de la pantalla pueden hacer que un

" en el Viewport no sea completamente cuadrado, resulta conveniente hacer un ajuste de los radios para que

ros crculos o circunferencias, no se vean alargadas o achatadas, segn el caso.

ues para dibujar elipses tenis:

// dibuja una elipse con su superficie rellena DrawFillEllipse(int x,int y, int rx, int ry, int layer, u32 color); // dibuja el borde de la elipse DrawEllipse(int x,int y, int rx, int ry, int layer, int thickness, u32 color);
x, y: posicin en pxeles. rx, ry: radio en pxeles para las dimensiones X e Y. layer: capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z). thickness: grosor de la linea de 1 a lo que admita.

color: color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso).

a dibujar arcos de una elipse (grficos tipo tarta o el uso que le querais dar. El objeto se dibujara entre el arco que

n degrees_start y degrees_end):

// dibuja el arco de una elipse con su superficie rellena DrawFillSlice(int x,int y, int rx, int ry, int layer, int degrees_start, int degrees_end, u32 color); // dibuja el borde del arco de la elipse DrawSlice(int x,int y, int rx, int ry, int layer, int thickness, int degrees_start, int degrees_end, u32 color);
x, y: posicin en pxeles. rx, ry: radio en pxeles para las dimensiones X e Y. layer: capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z). thickness: grosor de la linea de 1 a lo que admita. degrees_start: angulo en grados (de 0 a 360) de inicio. degrees_end: angulo en grados (de 0 a 360) final. color: color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso).

LA FUNCIN DE LINEA

ncin DrawLine() se incluy pensando ms en ciertos usos especiales que pensando en ella como la funcin que

pre deberias utilizar". La funcin est preparada para dibujar lineas de mas de un pxel de grosor y en realidad dos tringulos de forma interna. Pero tiene un pequeo defectillo, para dibujar lineas horizontales o verticales,

apoyo para poder ligar con otras lineas y formar cajas, pero si esa lnea es mas gruesa de un pxel y digamos que "rampas", la unin no ser buena. Notese tambin que le influye si tenemos una textura o no en activo (con

xture()).

eris dibujar lneas u otros polgonos de forma rpida, deberais utilizar GX_Begin, el cual se describe luego, mas

// dibuja lineas con o sin textura y de diferentes grosores DrawLine(int x,int y, int x2, int y2, int layer, int thickness, u32 color);
x, y: posicin de inicio en pxeles. x2, y2: posicin final en pxeles. layer: capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z). thickness: grosor de la linea de 1 a lo que admita. color: color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso).

DIBUJANDO SUPERFICIES

e hace de sta librera que resulte til para hacer juegos 2D de forma directa, es sin duda, la

n DrawSurface().

uncin permite dibujar un objeto de textura directamente, proporcionndole unas dimensiones que no tienen

e coincidir con las de la textura utilizada. Es decir, si la textura tiene un ancho de 32x32, no hay nada que impida

arla como superficie mayor o menor, lo cual lleva implcito un aumento o reduccin de tamao (de ah que nos

e til utilizar GX_LINEAR en el LOD de la textura al crearla).

ues DrawSurface(), se puede utilizar tanto para dibujar una ventana en la cual utilizaremos una textura como

ficie donde dibujar pxeles (en el ejemplo 1 os incluyo una muestra de como utilizo una textura para dibujar) o para

ar sprites completamente acelerados por la consola.

// dibuja un objeto de textura como superficie DrawSurface(GXTexObj *surface,int x,int y, int width, int height, int layer, u32 color);
surface: puntero a objeto de textura que describe la superficie (creada conCreateTexture()). NO usar NULL aqu. x, y: posicin de la superficie en pxeles. width, height: dimensiones visuales de la superficie en pxeles. layer: capa de 0 a 999, siendo 999 la capa mas alejada y 0 la mas cercana (equivale a -Z). color: color en formato RGBA8 (8 bits por componente y Rojo byte de menor peso).

CONECTANDO CON LAS GX: GX_BEGIN() [USUARIO AVANZADO]

dibujar polgonos, resulta mas adecuado usar las GX directamente, puesto que as ganamos en velocidad y

ente es muy sencillo con ayuda de unas pocas funciones de apoyo que se han aadido. Un ejemplo de uso, lo

s ver en el ejemplo 2, donde se utiliza GX_Begin para dibujar las lineas remarcadas que forman las "montaas"

se hace un uso especial, partiendo de una coordenada de origen y usando esto para dibujar 4 lineas en una

guracin 2x2 para el remarcado y asi asegurar un grosor de 2 pxeles en la linea).

mero que tenemos que hacer, es preparar lo necesario para dibujar polgonos con o sin texturas:

ConfigureForColor();

a es la funcin necesaria para ajustar el uso de vrtices para admitir coordenadas de posicin y color:

ConfigureForTexture(int scale);

a es la funcin necesaria para ajustar el uso de vrtices con coordenadas de posicin, color y coordenadas de

a scale: este parmetro se utiliza para fijar el factor de escala para la textura, debido a que las coordenadas las especificamos como enteros de 16 bits. Para entender esto, tienes que saber que para el GX, los limites de una textura se especifican como nmeros flotantes siendo 0.0f uno de los extremos y 1.0f el otro, aunque tambin es posible utilizar magnitudes negativas (en caso de crear textura con el parmetro repeat=1).

ues, ste factor de scale es un desplazamiento cuyo valor real es 1<<scale para representar 1.0f.

cir, que si fijamos scale=10, como 1<<10==1024, un valor de 0 representara a 0.0f mientras que 1024

sentara a 1.0f.

amos un ejemplo: imaginaos que creamos una textura con unas dimensiones de 64x32 texels (el texel es como el pero referido a textura). A ojos del GX, las dimensiones irian desde 0.0f a 1.0f, de forma independiente a ese

o.

es bien, si especificamos un factor de scale= 10, eso significa que si damos una coordenada de textura tx=1024( en

ho), tx sera equivalente a 1.0f pero a efectos prcticos de textura sealara a 64, teniendo en cuenta el ancho de la

a en texels.

embargo si especificamos ty=1024 (el alto), a efectos prcticos sealara a 32 que es el alto de la textura y no a

e parecer lioso pero la idea es que las coordenadas de textura no dependan exactamente del tamao de la textura,

ue obviamente, hay una relacin directa. Vosotros lo que tenis que ver es que 1<<scale representa al lmite

al y horizontal de la textura (y el otro es 0, obviamente) y no caer en el error de pensar que como el ancho de la

ra es 64 y el alto 32, hay que pasarle esos valores numricos o 1.0f y 0.5f, porque la escala hace referencia al valor

0f, ya que independientemente del tamao real de la textura, 1.0f es el limite derecho, pero tambin el inferior.

ues, si la textura es un rectngulo, las coordenadas quedaran as, para un factor escala de 10:

q-arr = (0.0f,0.0f)= (0,0) der-arr=(1.0f,0.0f)=(1024,0)

q-abj =(0,1.0f)=(0,1024) der-abj=(1.0f,1.0f)=(1024,1024)

actamente igual que si fuera una textura cuadrada.

o que 1.0f en realidad, rebasa el lmite de la textura, lo mejor es quedarse un poco corto, de forma que sera mejor

1023 para ese borde.

ero que con este ejemplo, hayis captado mejor lo de las coordenadas de textura y sus escalas (se puede

ificar floats como coordenada de textura directamente, pero por cuestiones de velocidad y espacio, es mejor utilizar

os de 16 bits y mas para un uso 2D).

GX_BEGIN()

egin es la funcion que nos permite dibujar polgonos desde las GX. Para utilizarla desde screenlib debis hacerlo

GX_Begin(primitive,

GX_VTXFMT0, vertices);

primitive: puede ser GX_POINTS, GX_LINES, GX_LINESTRIP, GX_TRIANGLES, GX_TRIANGLESTRIP, GX_TRIANGLEFAN, GX_QUADS. Si quieres saber ms, en la documentacin sobre las GX, se describe este tema, pero se hace un resumen aqu:

GX_POINTS

Dibuja puntos, necesita un vrtice por cada punto a dibujar

GX_LINES

Dibuja lineas, necesita dos vrtices por cada linea a dibujar

Dibuja lineas, necesita dos vrtices para la primera linea, pero el resto de lineas utilizan el GX_LINESTRIP vrtice de la ltima linea dibujada, mas uno nuevo. Es decir, que para dibujar dos lineas se necesitaran 3 vrtices y las dos lineas compartiran uno de ellos

GX_TRIANGLES

Dibuja tringulos, necesita tres vrtices por tringulo

Dibuja tringulos enlazados de forma que el primero requiere tres vrtices, pero los siguientes comparten dos vrtices con el ltimo tringulo dibujado mas uno nuevo. Resulta GX_TRIANGLEST adecuado para crear objetos en forma de tiras o anillos (objetos torno, por ejemplo).Por RIP ejemplo, dos trianglestrips requieren 4 vrtices, mientras que tres trianglestrips requieren 5 vertices

GX_TRIANGLEFA N

Dibuja tringulos utilizando un vrtice como referencia central (el primer vrtice especificado), el ultimo del anterior tringulo mas uno nuevo. Obviamente, el primer tringulo

precisar de trs vertices. Por ejemplo, dos trianglefan requieren 4 vertices, mientras que tres trianglefan requieren 5 vertices. Resulta adecuado para hacer graficos de tarta, cerrar figuras, para modelar una sombrilla...

Los quads son figuras que requieren 4 vrtices. Los vrtices requiere usar un orden horario o antihorario (es decir izq-arr, der-arr, der-abj, izq-abj por ejemplo) y se debe evitar vrtices GX_QUADS cruzados o que puedan generar dos planos. Son tiles para dibujar cuadrados/rectangulos y desde un punto de vista 3D, paredes, techos, suelos...

GX_VTXFMT0: esto especifica que vamos a usar el formato de vrtices 0, que es el que programamos con ConfigureForColor() y ConfigureForTexture(). vertices: numero de vrtices a usar, hasta un lmite de 65535 (quiz el 0 represente 65536, pero vamos, que puedes usar un buen puado de una sola llamada). Y por supuesto, siempre que no superes el tamao del FIFO. Recuerda que debes darle ls vertices exactos (o se producir un cuelgue de la muerte)

PASANDO VRTICES A GX_BEGIN()

reen.h tienes dos funciones definidas para pasar los parmetros necesarios a los vrtices:

// para aadir vrtices con posicin y color AddColorVertex(s16 x, s16 y, s16 z, unsigned color); // para aadir vrtices con posicin, color y coordenada de textura AddTextureVertex(s16 x, s16 y, s16 z, unsigned color, s16 tx, s16 ty);

rad no equivocaros con la funcin utilizada si no quieres que se produzca un cuelgue.

acabar, utiliza GX_End(); que se utiliza de forma decorativa.

EJEMPLOS DE USO DE GX_BEGIN()

ConfigureForColor(); // usa color GX_Begin(GX_LINE, GX_VTXFMT0, 2); // dibuja una linea (por tanto pasamos 2 vertices) AddColorVertex(0,0,0,0xffffffff); // primer vertice AddColorVertex(SCR_WIDTH, SCR_HEIGHT,0,0xffffffff); //segundo vertice GX_End();

SetTexture(&texture); // fija una textura

ConfigureForTexture(10); // usa textura y fija escala a 10 ( 1<<10== 1024) (notese que usar siempre la textura especificada por SetTexture()) GX_Begin(GX_QUADS, GX_VTXFMT0, 4); // dibuja un QUAD (por tanto pasamos 4 vertices) AddTextureVertex(0,0,0,0xffffffff, 0, 0); // primer vertice AddTextureVertex(SCR_WIDTH, 0,0,0xffffffff, 1023, 0); // segundo vertice AddTextureVertex(SCR_WIDTH, SCR_HEIGHT,0,0xffffffff, 1023,1023); // tercer vertice AddTextureVertex(0, SCR_HEIGHT,0,0xffffffff,0, 1023); // cuarto vertice GX_End();

EL ULTIMO RECURSO: RESTAURAR EL ENTORNO 2D

sible que necesitis saltar a usar 3D o incluso que hagis un uso mas avanzado de las 2D, cambiando la matriz

ial, etc. En ese caso, tendris que restaurar el entorno para volver a trabajar con screenlib y para eso, se ha

do la funcion:

Recover2DContext();

uncin recupera en lo posible el contexto 2D de screenlib, pero si hacis usos avanzados (como cambiar la matriz

nsformacin de texturas y cosas asi), es posible que eso lo tengis que restablecer a mano. Obviamente, ser

ra responsabilidad cambiar a los distintos contextos y esta funcin no puede saber todo lo que habis cambiado y

urarlo vosotros.

do caso, dispones del cdigo fuente de la librera y algunas cosas se han organizado de forma que te sea sencillo

ue es lo que precisa screenlib para trabajar.

o es todo lo referente a la librera grfica de Hermes.

La ASNDLIB (Introduccin)

NDLIB es la sucesora de una librera anterior, llamada SNDLIB que fue una de las primeras aportaciones de Hermes

mebrew de Wii. Se diferencian en que la primera utiliza el DSP, lo cual permite acelerar las voces, mientras que la

nda usa el procesador de la Wii para calcular las distintas voces y adems soporta unas voces especiales pensadas

generar las voces de instrumentos, con efectos como ataque, sostenimiento y cada.

P es un procesador relativamente poco potente para la tarea encomendada. Mezclar 16 voces en estreo y con un de frecuencias que va desde un 1Hz a 144000Hz es algo que requiere bastantes multiplicaciones y operaciones

adaptar y mezclar las voces, no es tarea fcil para un procesador de 16 bits y que est pensado para trabajar de

manera (baste decir que Hermes fu el primero en darle utilidad, basndose en una informacin con 3 aos de

edad y resolviendo varias piezas del puzzle hasta hace bien poco desconocidas).

o es que por razones de capacidad de proceso, era imposible mantener 16 voces y aadir la posibilidad de que el

rabajara con los efectos de ataque, sostenimiento y cada. Se podran aadir desde fuera y probablemente,

ndo pocos recursos y algunos trucos, pero como aparte de Hermes, creo que nadie las ha usado, pues decidi

mirlas.

uestiones de proceso, no es conveniente usar muchas voces por encima de 48000Hz. La Wii trabaja a

0Hz a 16bits por sample y en estreo y la ASNDLIB permite reproducir voces Mono o Stereo, con samples de 8 o

s (con signo) y en el rango mencionado de 1 Hz (MIN_PITCH) a 144000Hz (MAX_PITCH) lo cual significa que la

a es capaz de adaptar la frecuencia de las voces para que acaben sonando a la frecuencia especificada en las voces

os 48000Hz. Sin embargo, es obvio que usar voces por encima de esa frecuencia, implica una prdida de calidad de

o y adems, multiplicar la carga de trabajo al DSP. Cuanto menor sea la frecuencia a la que se reproduce una voz,

desahogado ir el DSP, aunque es cierto que el cdigo se ha diseado para que el DSP cumpla bien con su trabajo

o soportando una carga de trabajo enorme.

P se coordina con la interrupcin de audio que es llamada cada 21,333 ms aproximadamente, una vez que se

umpe la DMA de Audio y entonces se inicia un proceso de llamadas en cascada a la DMA del DSP de hasta 17 pasos,

procesar las voces. ltimo sealar en ste apartado, que las voces de sonido a usar, deben estar alineadas a 32 bytes y usar

rentemente un padding de 32 bytes. De nuevo estamos en el mismo caso que con las texturas.

La ASNDLIB - Funciones
INICIANDO

en muchas otras libreras, lo primero que hay que hacer es iniciarla.

ASND_Init();

ciar la ASNDLIB, las voces estn pausadas mediante una pausa general.

PAUSA GENERAL

ASND_Pause(s32 paused);
paused: pon 1 para pausar y 0 para continuar.

ues, antes de trabajar con voces, necesitars hacer:

ASND_Init(); ASND_Pause(0);

d en cuenta que en el momento de quitar la pausa y aunque no se oiga sonido, la librera estar trabajando y el

dor interno general de samples, estar contando.

FINALIZANDO

inalizar la librera, podis usar:

ASND_End();

habis fijado en mis ejemplos, veris que sta funcin es llamada en fun_exit() que es fijada con atexit() para que

r del programa, salir de algunas libreras. Esta practica es conveniente debido a que pueden haber algunas DMAs

archa y as evitamos un posible cuelgue.

una vez tenemos la librera iniciada y funcionando (quitando la pausa), ahora podemos decidir que voces usar la 0 a la 15 (ya se ha comentado que dispones de 16).

voz 0, se suele reservar para usar con reproductores de msica.

BUSCANDO VOCES LIBRES

// devuelve la primera voz libre empezando por la voz 1 s32 ASND_GetFirstUnusedVoice();

ima voz que nos devolver como libre es la 0. Esta funcin se puede utilizar para conocer la primera voz sin uso,

no hay ninguna regla escrita que diga que no se pueden interrumpir voces en ejecucin en cualquier momento.

REPRODUCIENDO UNA VOZ

ejecutar una voz se utiliza estas funciones:

s32 ASND_SetVoice(s32 voice, s32 format, s32 pitch,s32 delay, void *snd, s32 size_snd, s32 volume_l, s32 volume_r, ASNDVoiceCallback callback); s32 ASND_SetInfiniteVoice(s32 voice, s32 format, s32 pitch,s32 delay, void *snd, s32 size_snd, s32 volume_l, s32 volume_r);

mera es el uso normal, mientras que la segunda se usa para ejecutar una voz de forma continua e ininterrumpida y

ueda por tanto, sometida al control del usuario (cambio de pitch, volumen o momento de pararla).

unciones devuelven SND_OK o SND_INVALID en caso de error.

metros: voice: una desde 0 a 15 (MAX_SND_VOICES-1).

format: formato PCM desde VOICE_MONO_8BIT a VOICE_STEREO_16BIT (ver asndlib.h). pitch: frecuencia del pitch (en Hz). delay: tiempo de retardo en milisegundos (ms). Tiempo de espera antes de que se inicie la voz. snd: buffer con los samples a reproducir (alineado y con padding a 32 bytes). size_snd: tamao del buffer en bytes. volume_l: volumen del canal izquierdo en el rango de 0 a 255. volume_r: volumen del canal derecho en el rango de 0 a 255. callback: puede ser NULL, lo que indicara que la voz se usa nicamente para reproducir los samples especificados y despus quedar libre o la direccin de una funcin callback para trabajar a doble buffer, que ser invocada desde la interrupcin (cuidado con el gasto de CPU aqu) cada vez que se detecte que al menos uno de los buffers de voz queda libre y as asignar un nuevo buffer con ayuda de la funcin ASND_AddVoice(). En el modo doble buffer (usando callback) la voz no se libera por si misma y devuelve SND_WAITING en caso de quedarse sin samples para seguir reproduciendo. Cmo se puede observar, el modo con callback resulta mas adecuado para reproductores de audio, mientras que sin callback es mas adecuado para efectos de sonido sueltos.

LA FUNCIN CALLBACK DE ASND_SETVOICE

tructura es la siguiente:

void my_callback(s32 voice){ }

e voice recibe el numero de voz por si sta callback es compartida con otras voces. En el callback estis en tiempo

errupcin y cuanto menos tiempo gastis aqu, mejor. Si necesitis hacer algo pesado, despertad un hilo desde

pero no sobrecarguis sta funcin porque comprometeris otras cosas.

LA FUNCIN ASND_ADDVOICE()

s32 ASND_AddVoice(s32 voice, void *snd, s32 size_snd);

unciones devuelven SND_OK, SND_BUSY si no es posible aadir la voz o SND_INVALID en caso de error.

metros: voice: una desde 0 a 15 (MAX_SND_VOICES-1). snd: buffer con los samples a aadir (alineado y con padding a 32 bytes). size_snd: tamao del buffer en bytes.

cir: se puede observar que la funcin solo devolver SND_OK en caso de tratarse de una voz correcta y encontrar

e los bufferes libres para ser asignado. En ese caso, la voz ser aadida y nosotros podremos comparar y proceder

rcambiar buffers en caso de usar la voz en un reproductor.

LGICA ADICIONAL PARA SISTEMA DE DOBLE BUFFER


sistema de doble buffer lo usual es que un buffer se est reproduciendo, mientras otro se est rellenado

mprimiendo formato Ogg, por ejemplo. Sin embargo la librera ASNDLIBtiene un funcionamiento asncrono y

ita de mecanismos de proteccin para evitar por ejemplo, que tengamos dos buffers aadidos en la cola de

duccin (uno en ejecucin y otro en espera) y que machaquemos esos datos al descomprimir nuevos samples y

rlos al buffer de ejecucin.

ues, se necesita algo que le diga al lector-descompresor de samples que se est quieto-parado esperando que el actual de lectura quede libre al cesar la reproduccin.

a ello contamos con dos funciones que tienen resultado similar:

s32 ASND_TestPointer(s32 voice, void *pointer);

ta funcin, debemos pasarle el inicio del buffer y ese puntero ser comprobado y en caso de coincidir con el buffer

produccin o el buffer en espera, se nos devolver el valor 1, lo que indicara que deberamos esperar antes de

nar" el buffer con nuevos datos y 0 en caso de no estar en uso ese puntero.

nto antes de refrescar los datos del buffer, deberamos comprobar que ASND_TestPointer(voice, puntero)==0 y

der a refrescar si se cumple.

s32 ASND_TestVoiceBufferReady(s32 voice);

uncin devuelve 1 si uno de los dos buffers est libre. Puesto que nosotros controlamos la secuencia de intercambio

SND_AddVoice(), es obvio que si por ejemplo, acabamos de aadir un nuevo buffer y nos queda uno libre, es que

mos leer/descomprimir sin problemas nuevos datos.

ues, deberamos comprobar que ASND_TestVoiceBufferReady(voice)==1 antes de refrescar los datos del buffer.

FUNCIONES DE TIEMPO GLOBAL

e un timer de sonido global asignado a todas las voces y cuya utilidad puede ser coordinar el manejo de las voces en

mpo. Para leerlo/controlarlo, dispones de las siguientes funciones:

// devuelve el tiempo transcurrido en milisegundos (ms) u32 ASND_GetTime(); // devuelve el numero de samples reproducidos hasta el momento en relacin a 48000Hz (48000 samples = 1 segundo)

u32 ASND_GetSampleCounter(); // devuelve el numero de samples reproducidos a 48000Hz por cada interrupcin u32 ASND_GetSamplesPerTick(); // fija el tiempo del contador global por el especificado en milisegundos (ms) void ASND_SetTime(u32 time);

FUNCIONES DE ESTADO Y TIEMPO DE LAS VOCES

conocer el estado de una voz, se utiliza sta funcin:

s32 ASND_StatusVoice(s32 voice);

os devuelve: SND_INVALID: voz no valida (fuera del rango 0 a 15). SND_UNUSED: voz sin uso. SND_WORKING: voz en uso y reproduciendo. SND_WAITING: voz en uso y esperando nuevos samples (doble buffer).

oces poseen una medida de tiempo particular que es til para determinados usos:

// devuelve el tiempo transcurrido en milisegundos, sin tener en cuenta el tiempo de delay especificado en la voz u32 ASND_GetTimerVoice(s32 voice); // devuelve el numero de samples reproducidos a 48000Hz (es independiente de la frecuencia de la voz) u32 ASND_GetTickCounterVoice(s32 voice);

te caso, no se puede fijar el tiempo de inicio de la voz, ya que de ello se encarga ASND_SetVoice().

La ASNDLIB - Funciones de control


CONTROL GLOBAL
funciones de control global contis con:

// pausa general (1=pausa, 0= continua) void ASND_Pause(s32 paused);

// retorna uno si la est activa la pausa general o 0 en otro caso s32 ASND_Is_Paused(); // aade una callback de propsito general que ser llamada cada 21,333 ms (p.e: void my_globalcallback() {}) void ASND_SetCallback(void (*callback)());
CONTROL DE VOCES

// para la voz (si est en ejecucin, obviamente) s32 ASND_StopVoice(s32 voice); // si pause=1, pausa la voz, si pause=0 continua. Est pausa se anula con ASND_SetVoice() tambin s32 ASND_PauseVoice(s32 voice, s32 pause); s32 ASND_StatusVoice(s32 voice);

os devuelve: SND_INVALID: voz no valida (fuera del rango 0 a 15). SND_UNUSED: voz sin uso. SND_WORKING: voz en uso y reproduciendo. SND_WAITING: voz en uso y esperando nuevos samples (doble buffer).

s32 ASND_TestPointer(s32 voice, void *pointer);

ta funcin, debemos pasarle el inicio del buffer y ese puntero ser comprobado y en caso de coincidir con el buffer

produccin o el buffer en espera, se nos devolver el valor 1, lo que indicara que deberamos esperar antes de

nar" el buffer con nuevos datos y 0 en caso de no estar en uso ese puntero.

nto antes de refrescar los datos del buffer, deberamos comprobar que ASND_TestPointer(voice, puntero)==0 y

der a refrescar si se cumple.

s32 ASND_TestVoiceBufferReady(s32 voice);

uncin devuelve 1 si uno de los dos buffers est libre. Puesto que nosotros controlamos la secuencia de intercambio

SND_AddVoice(), es obvio que si por ejemplo, acabamos de aadir un nuevo buffer y nos queda uno libre, es que

mos leer/descomprimir sin problemas nuevos datos.

pues, deberamos comprobar que ASND_TestVoiceBufferReady(voice)==1 antes de refrescar los datos del buffer.

CONTROL DINMICO DE VOCES

voces de tipo infinito o de doble buffer, puede ser til el uso de estas funciones:

// cambio dinmico de la frecuencia de reproduccin (pitch) (rango de 1Hz a 144Khz) s32 ASND_ChangePitchVoice(s32 voice, s32 pitch); // cambio dinmico del volumen (recuerda que el rango es de 0 a 255) s32 ASND_ChangeVolumeVoice(s32 voice, s32 volume_l, s32 volume_r);
A GOLPE DE NOTA

na funcin que nos permite utilizar voces para hacerlas sonar como si fueran notas de un instrumento,

rcionando la frecuencia a partir de los datos de nota y frecuencia base del sample:

int ANote2Freq(int note, int freq_base,int note_base);


note: nota codificada a reproducir. Por ejemplo: NOTE( NOTE_C,4) para nota C y octava 4. freq_base: frecuencia base de los samples. Por ejemplo 8000Hz. note_base: nota codificada de los samples. Por ejemplo: NOTE( NOTE_LA,3) para nota LA y octava 3 (LA 3). retorna: frecuencia (en Hz).

entender esto, en asndlib.h tenis un definido NOTE (note, octave) que compone la nota codificada a usar en esta

n, asi como la enumeracin de las distintas notas (NOTE_DO o NOTE_C).

cuencia base es la frecuencia a la que capturas un sonido y la nota base, es la nota que capturaste. Por ejemplo,

naos que capturis la nota Do3 de un rgano a 11025 Hz, pues obviamente, para que ASNDLIB pueda simular un

tiene que saber que relacin hay entre la frecuencia base y las distintas notas .

RENDIMIENTO DEL DSP

spone de una curiosa funcin para medir el uso del DSP:

u32 ASND_GetDSP_PercentUse();

elve el tanto por ciento usado en un determinado instante, por si queris saber el tiempo que gasta el DSP en

sar las voces (si supera el 100%, es que al DSP le falta tiempo para procesar las voces que le estis mandando).

mos que es una funcin de debug .

s consultar asndlib.h para mas detalles.

Lectura de Mandos: Wiimote

LA LIBRERA DEL WIIMOTE

use.a (-lwiiuse) contiene las funciones necesarias para leer el Wiimote y sus expansiones, tales como el Nunchuk,

o Clasico o la Guitarra de GHIII. podeis observar en los ejemplos de Hermes, necesitis incluir:

#include <wiiuse/wpad.h>

e es donde se definen las funciones necesarias, aunque en wiiuse.h podeis observar tambien, que se definen ciertas

turas necesarias.

INICIAR EL WIIMOTE

WPAD_Init();

todo lo necesario para iniciar la librera, pero tambin necesitareis configurar el modo o cosas como el tiempo que

andos aguardarn antes de apagarse por falta de uso:

// tiempo en segundos que aguardar el mando antes de apagarse por falta de uso (p. e: 5*60 para 5 minutos) void WPAD_SetIdleTimeout(u32 seconds);

do de operacin lo podis ajustar con:

// ajusta el modo de operacin s32 WPAD_SetDataFormat(s32 chan, s32 fmt);


chan: un nmero de 0 a 3 para cada uno de los wiimotes o mejor WPAD_CHAN_ALL para ajustar el modo de todos los mandos. fmt: WPAD_FMT_BTNS (por defecto) para leer solo los botones y palancas, WPAD_FMT_BTNS_ACC para tener en cuenta adems los acelermetros y WPAD_FMT_BTNS_ACC_IR para utilizar los acelermetros y la cmara infrarroja. Como es evidente, el gasto de bateras depender de la activacin de estos modos, as que es conveniente activar slo lo necesario.

// ajusta la resolucin de video para cada canal, cuando se usa la cmara IR

s32 WPAD_SetVRes(s32 chan,u32 xres,u32 yres);


chan: un nmero de 0 a 3 para cada uno de los wiimotes o mejor WPAD_CHAN_ALL para ajustar el modo de todos los mandos. xres: resolucin horizontal, por ejemplo SCR_WIDTH si usis screenlib. yres: resolucin vertical, por ejemplo SCR_HEIGHT si usas screenlib.

mente, sta funcion solo es necesaria si activas el modo WPAD_FMT_BTNS_ACC_IR.

LEYENDO LOS WIIMOTES

s32 WPAD_ScanPads();

uncin se encarga de leer todos los mandos mediante el mtodo polling y debe ser llamada en cada frame.

elve el nmero de PAD en activo (salvo que se produzca un error, por lo que no es del todo fiable).

LEYENDO LOS DATOS DE UN WIIMOTE EN PARTICULAR

s32 WPAD_Probe(s32 chan,u32 *type);

es la funcin clave que deberais llamar para conocer si un mando est conectado o no y que tipo de datos devuelve.

no <0 si se produce un error, otro caso, mando conectado y datos servidos. chan: un nmero de 0 a 3 para cada uno de los wiimotes. type: puntero a una variable u32 donde podremos leer el tipo de expansin: WPAD_EXP_NONE, si no hay ninguna, WPAD_EXP_NUNCHUK, si el Nunchuk est conectado, WPAD_EXP_CLASSIC y WPAD_EXP_GUITARHERO3.

una vez que verificamos que WPAD_Probe() devuelve un valor >=0 y que conocemos el tipo, podemos pasar a leer los datos:

u32 WPAD_ButtonsHeld(int chan);

elve todos los botones pulsados tantos del Wiimote, como de las expansiones. En wpad.h podis encontrar las

ciones que son del tipo WPAD_BUTTON_X para los botones del wiimote, WPAD_NUNCHUK_BUTTON_X para los del

huk y WPAD_CLASSIC_BUTTON_X o WPAD_GUITAR_HERO_3_BUTTON_X para el resto de las expansiones.

and (& ) de esta forma:

u32 buttons=WPAD_ButtonsHeld(0);

if(buttons & WPAD_BUTTON_A) { /* A pulsado */}

WPADData *WPAD_Data(int chan);

uncin nos devuelve un puntero a la estructura WPADData interna (mirad en wpad.h). De esta forma, se pueden

er a todos los datos procedentes de las expansiones, sticks analgicos o la cmara IR.

en tambin funciones separadas para leer cada elemento:

void void void void void

WPAD_IR(int chan, struct ir_t *ir); WPAD_Orientation(int chan, struct orient_t *orient); WPAD_GForce(int chan, struct gforce_t *gforce); WPAD_Accel(int chan, struct vec3w_t *accel); WPAD_Expansion(int chan, struct expansion_t *exp);

ferentes estructuras podis verlas definidas en wiiuse.h

emplo con:

WPADData * wmote_datas; wmote_datas=WPAD_Data(0);

amos los datos del Wiimote 0 (el del led 1 encendido) y si tuviramos el Nunchuk conectado, podramos leer los del stick con:

// datos izquierda-derecha del stick analogico del nunchuk devuelta como u8 (128== centro aprox) wmote_datas->exp.nunchuk.js.pos.x; // datos abajo-arriba del stick analogico del nunchuk devuelta como u8 (128== centro aprox) wmote_datas->exp.nunchuk.js.pos.y; // datos correspondientes a la posicin central del stick wmote_datas->exp.nunchuk.js.center.x; wmote_datas->exp.nunchuk.js.center.y;

y mas datos como min y max, pero eso ya deberais mirarlo vosotros).

ejemplo, en caso de activar la cmara IR y ajustar la resolucin con WPAD_SetVRes() podrais leer la posicin aqu:

// posicin X de pantalla devuelta como float wmote_datas->ir.x // posicin Y de pantalla devuelta como float wmote_datas->ir.y // devuelve 1 si los datos de x e y son vlidos o 0 si no lo son (apuntando fuera de la barra sensora) wmote_datas->valid

esta forma usar el wiimote como un puntero.

comiendo hacer vuestras propias pruebas y buscar en wpad.h y sobre todo, en wiiuse.h la definicin de las

ntes estructuras y visualizar los datos en pantalla con s_printf(), para comprender mejor que es lo que devuelven.

, parece ser que hay algunos fallos en relacin al nunchuk y por ejemplo, el uso de los acelermetros solo funciona

w (por eso en el ejemplo 4 uso el Nunchuk desde exp.nunchuk.accel.x y no de forma similar a como uso el

ote).

EL RUMBLE

mble se puede activar o desactivar con:

s32 WPAD_Rumble(s32 chan, int status);

emente haciendo status=1, pondremos el motor en marcha y lo pararemos con status=0. Si queremos simular

tas vibraciones, nos tocar ir activando y desactivando el rumble con una determinada cadencia.

APAGANDO MANDOS DE FORMA MANUAL

o se ocupa la funcin:

s32 WPAD_Disconnect(s32 chan);


SALIENDO DE LA LIBRERA

WPAD_Shutdown(); antes de salir del programa.

Lectura de Mandos: Gamecube Pad

tura de Mandos: Gamecube Pad

unciones se definen en ogc/pad.h y todo se asimila de forma interna a libogc y no necesitis incluir librera aparte.

INICIANDO LA LIBRERA DEL GAMECUBE PAD

PAD_Init();
LEYENDO LOS PADS DE GAMECUBE

u32 PAD_ScanPads();

sta funcin se leen todos los PADs conectados y devuelve una mscara que indica que mandos estn conectados o

n la versin original de pad.c, hay un fallo (que no se sabe ai se produce solo en Wii o no) que hace que si conectas

puerto 2, sin haber un mando en el puerto 1, a veces falle la conexin con ese mando 2 (Hermes se dio cuenta

rollando el emulador Wiiengine), por lo que vosotros disponis de una versin modificada que evita este fallo.

rdad la lectura usa mtodo polling y que por tanto, tendras que llamar sta funcin en cada frame,

s de leer los datos del pad.

plo de uso:

u32 npads; .... npads=PAD_ScanPads(); if(npads if(npads if(npads if(npads & & & & 1) 2) 4) 8) {} {} {} {} // // // // mando mando mando mando 1 2 3 4 conectado conectado conectado conectado

LEYENDO LOS DATOS DE UN PAD DE GAMECUBE EN PARTICULAR

ez que conocemos los mandos que tenemos conectados, podemos usar estas funciones (mirar pad.h).

u16 PAD_ButtonsHeld(int pad);

elve todos los botones pulsados. El rango va desde PAD_BUTTON_LEFT a PAD_BUTTON_START, como podeis

var en pad.h.

s8 PAD_StickX(int pad); s8 PAD_StickY(int pad);

conocer los valores del stick (observar que utiliza un rango de -128 a 127, siendo 0 la posicin central).

s8 PAD_SubStickX(int pad); s8 PAD_SubStickY(int pad);

conocer los valores del substick o como lo llamo yo, el pezn amarillo (observar que utiliza un rango de -128 a 127,

o 0 la posicin central).

u8 PAD_TriggerL(int pad); u8 PAD_TriggerR(int pad);

conocer los valores analgicos de los gatillos, si se estn pulsando. Dan un rango de 0 a 255.

dad que los valores analgicos son de referencia y que en el caso de los sticks, siempre es conveniente usar una

muerta (un rango de valores que se ignora, por ejemplo, entre -32 y +32, para evitar que la posicin centrada del

sea un cachondeo y haya cierta deriva). esto ya sabis todo lo imprescindible para usar ste tipo de pads

Punto de inflexin

hasta ahora, tenis acceso al vdeo, al audio y a los distintos mandos. Tambin se han proporcionado las libreras

iladas, herramientas para crear sprites e integrar las cosas en el propio programa, de forma que Hermes ha

lido su promesa de acercaros la programacin de Wii a los que aoris los viejos ordenadores como el Spectrum y

ilidad para realizar juegos.

ejemplo 3 podis observar la integracin con los sprites, incluso podis ver como leer el tiempo en milisegundos por

acin get_ms_clock() y as poder regular la velocidad de actualizacin de vuestros sprites de forma independiente

resco de pantalla (cuidando que ese tiempo sea siempre superior o igual 20ms por frame, claro). En los ejemplos se

tran como utilizar el Modplayer e incluso en el ejemplo 3, se muestra como controlar "manualmente" el modplayer

que cuando llegue al final del MOD, cambiarlo por otro.

odo lo que sa ha explicado aqu y con lo que veis en los ejemplos, tenis todo lo necesario para crear juegos y

der a programarlos.

o pensar: "Eh! No nos has contado nada sobre como acceder a la SD, ni al DVD ni a los dispositivos USB. Tampoco

as hablado de programacin multihilo y cosas asi".

algunas de esas cosas me ocupar a continuacin, pero realmente lo necesitis? Es decir, plantearos si de veras, que necesitis todo eso para realizar un juego de plataformas, un juego de marcianos y cosas as. Evidentemente,

ue queris es realizar un juego donde la programacin pasa a un segundo plano y los grficos ocuparn una

da, pues es evidente que necesitareis acceder a dispositivos para cargar vuestros grficos, pero es que una de dos:

O no necesitis ste cursillo y por tanto, ya sabis muchas de estas cosas o lo tenis muy fcil de averiguar por vuestra cuenta. Este curso es principalmente, para principiantes. O no tenis muchos pjaros en la cabeza, pues todava no habis empezado a gatear y ya queris correr.

e se quiere que te des cuenta es que con la informacin que tenis hasta ahora, los ejemplos y esas herramientas que disponis, ya podis incluso echar a correr y que si ste cursillo se parara aqu, con todo lo que tenis, sera

ente para hacer buenos trabajos.

LIBFAT: Acceso a los dispositivos de almacenamiento


es la librera que nos permite acceder a dispositivos del almacenamiento formateados en FAT12/FAT16 o FAT32, USB Gekko, el lector de SD interno y desde hace un tiempo, dispositivo USB (este ltimo limitado a las

A: Describe la antigua LIBFAT.

ificaciones USB 1.1)

a integrarla en tus programas, basta con aadir -lfat como librera en el Makefile (los ejemplos de Hermes, la

en) y #include <fat.h> en tus fuentes.

UTF-8 FRENTE A ANSI CHAR SET


de meternos en faena con la librera, conviene conocer un aspecto que puede daros alguna que otra sorpresa.

do los ordenadores iniciaron su andadura, se hizo necesario emplear un estndar para poder manejar texto e

ambiar la informacin y el estndar elegido fue el ASCII que se defini entre 1963-1966 y posteriormente, se

ni en 1986 dando lugar a la especificacin ANSI que conocemos actualmente. se compona de 128 caracteres, usando 7 bits por tanto, donde se le aada un ltimo bit como bit de paridad en

ansmisiones, dado que ste estndar se pens inicialmente para telegrafa.

os 128 caracteres, los primeros 33 se utilizaban para cdigos de control no imprimibles. As por ejemplo, chr 8

senta un espacio atrs, chr 9 el tabulador, chr 10 avance de lnea , chr 13 el retorno de carro y chr 32 el espacio.

s imprimibles, el caracter '0' es chr 48, la 'A' es chr 65, y la 'a' chr 97.

gunos sistemas (LINUX/UNIX) el carcter 13 se toma como avance de lnea y retorno de carro de forma simultanea

es la razn por las que algunas veces, si abrimos un README desde el Wordpad de Windows, el texto se apelotona

ue espera que las lneas terminen con los caracteres 10 y 13).

mbargo, pronto se hizo necesario ampliar el set de caracteres, dado que ASCII recoga los caracteres latinos USA

por ejemplo, no recoga la , ni los caracteres de acentuacin que nosotros conocemos. Esto se recoge cmo una

sin que ocupa los caracteres desde el 128 al 255 (usando lo que antes era un bit de paridad) conocido como

dar ISO-8859-1 y as en solo 8 bits, se recogan todos los caracteres especiales de origen latino.

nlib utiliza una captura de caracteres que contiene 224 caracteres (elimina los 32 primeros caracteres ASCII, pero

rva el carcter espacio) y que recogen esa especificacin ANSI con los caracteres extendidos ISO-8859-1.

o que estos estndares solo recogen caracteres latinos, se hizo necesario crear otros estndares que recogieran

tipos de caracteres para soportar otros idiomas, como por ejemplo, el ruso y as naci el Unicode. Dentro del

de existe una especificacin llamada Utf-8 (8-bit Unicode Transformation Format) que utiliza un sistema de longitud

ble para que partiendo de 8 bits, se puedan codificar todos los caracteres Unicode.

a especificacin es la que utiliza la librera libfat a la hora de trabajar con nombres de ficheros, pero omo nos

?.

bien, Utf-8 utiliza el bit de mayor peso, el que actualmente recoge los caracteres ISO-8859-1 para cambiar y

car la tabla de caracteres a utilizar. Y al ser de longitud variable, un carcter puede ocupar de 1 a 4 bytes.

icode, los primeros 256 caracteres son los mismos que los especificados en la ISO-8859, por lo que los caracteres

del 0 al 127, corresponderan directamente a lo que nosotros conocemos. Lo que cambia es la forma de tratar el bit

ayor peso y eso compromete los nombres de los ficheros que utilicen acentos, etc.

ue tenis dos opciones: O bien procuris utilizar nombres de ficheros/directorios que no usen caracteres fuera del rango de 128 caracteres definido por el estandar ANSI. O si no os queda ms remedio, tendris que convertir de Utf-8 a ISO-8859 para imprimir o de ISO-8859 a Utf-8 para manejar nombres de ficheros/directorios que usen esos caracteres especiales.

emplo en la aplicacin Wiireader, slo interesa ver el nombre de los ficheros correctamente al visualizarlos en

lla, dado que al listar ficheros desde libfat, se obtienen en Utf-8 y por tanto, al abrirlos se conservar ese Utf-8.

ues, como sabemos que los caracteres del 0 a 127 en Utf-8 son iguales y ocupan solo un byte, solo nos restara como se codifican los caracteres del 128 al 255 para poder imprimirlos correctamente desde la screenlib.

http://es.wikipedia.org/wiki/UTF-8 podemos ver que para cubrir el rango de caracteres 0x80 a 0x7ff (nosotros solo

itamos de 0x80 a 0xff), se utilizan dos bytes:

rango: 000080 - 0007FF Unicode (UTF-16) 00000xxx xxxxxxxx Utf-8: 110xxxxx 10xxxxxx

ues, podramos hacer una rutina de conversin de Utf-8 a ISO-8859 de esta forma:

void UTF8_To_ISO8859(u8 *src, *u8 dst) { while(1) { if(src[0]==0) {*dst=0;break;} // fin de cadena if((src[0] & 128)==0) { // rango de 0x0 a 0x7f *dst++=*src++; } else if((scr[0] & 224) ==192 && (scr[1] & 192)==128) { // rango 0x80 a 0x7ff

if(src[0] & 0x1c) { // error fuera del rango 0x80 a 0xff: caracteres no soportados *dst=0;break; } *dst++= (((src[0] & 3)<<6) | (src[1] & 63)); src+=2; } else { // error fuera del rango de 0x0 a 0x7ff o no usa especificacin UTF-8 *dst=0;break; } } }

sta rutina (que por cierto, no ha sido testeada) podris convertir cadena utf-8 como fuente, a otra ISO como

no, para visualizarla con s_printf por ejemplo (caso de que el nombre quepa en pantalla). Una sugerencia sera que

aseis esa funcin para que en caso de ser un carcter no soportado, visualizara un carcter comodn (? por

plo), y que trabajaseis con Utf-8 siempre y solo convirtierais para visualizar (pero all cada uno).

LIBFAT: Inicializando
se inicializa con sta funcin:

bool fatInit (u32 cacheSize, bool setAsDefaultDevice);

rmal es que la inicialiceis as:

fatInit(8, false);

8 establece una cach de 8 clusters para entradas de directorios y cosas as.

trata de montar una serie de dispositivos en unidades con nombres como "fat0:","fat1:",..., "fat4:"... pero permite

ar uno de esos dispoitivos cmo unidad por defecto "fat:".

rmal es que esa unidad la asignis a la SD, de sta forma:

fatSetDefaultInterface(PI_INTERNAL_SD);

.h podis ver una serie de dispositivos definidos como PI_algo, como PI_SDGECKO_A, o PI_USBSTORAGE.

mbargo, si vais a acceder a varios dispositivos fat, es preferible utilizar el nombre original de la "particin" a estar

ambiado el interface por defecto.

ues, imaginemos que tenemos esta cadena:

char name[]="fatX:/test.txt";

dramos especificar la unidad directamente, modificando la cadena de sta forma: name[3]=48+PI_INTERNAL_SD;

s el carcter ASCII '0')

DETECTANDO LAS PARTICIONES Y ASIGNANDO EL TAMAO DE LA CACH DE LECTURA

con lo que tenemos hasta ahora, podramos funcionar sin problemas, pero por cuestiones de velocidad de lectura,

e resultar conveniente asignar un tamao interno de cach para la lectura de ficheros. Al mismo tiempo, resulta

niente detectar si los dispositivos estn operativos, pues ciertas operaciones podran colgar directamente la

la.

asignar el tamao de la cache de lectura, se utiliza sta funcin:

bool fatEnableReadAhead(PARTITION_INTERFACE partitionNumber, u32 numPages, u32 pageSize);

no liaros mucho:

fatEnableReadAhead(PI_INTERNAL_SD, 12, 32);

fatEnableReadAhead(PI_USBSTORAGE, 12, 32);

unos buenos valores. El tamao de pgina y el nmero de paginas, requieren un uso de memoria que si no se

istra bien, se desperdicia intilmente, por lo que no es conveniente abusar.

ests pensando por que no se nos explica mas a fondo el uso de stas funciones?. Pues por que el autor original

librera, ha decidido desechar los cambios que tanto rodries cmo Hermes introdujeron y est inmerso en una tarea

spedazar la librera y migrar fuentes, no se sabe muy bien si con la idea de jugar al despiste o que. Solo se puede

que repite viejos errores y arrastra ciertos bugs que darn problemas, sobre todo en multithread. Y que Hermes no

a mover un dedo ni para reportarlos, ni para migrar sus cambios, ni nada de nada, puesto que para Hermes la libfat

samos funciona bien y no le da la gana volver atrs, sobre todo en el tema de la cach. Pero obviamente, sto es

cin y si tu "actualizas" la librera, stas funciones de cach no las encontrars. de asignar un tamao de cache, conviene saber si el dispositivo est en funcionamiento:

{ DIR_ITER *dir; char path[]="fatX:/"; int have_device=0; path[3]=48+PI_INTERNAL_SD; dir = diropen(path); if (dir) { dirclose(dir); have_device|=1; fatEnableReadAhead(PI_INTERNAL_SD, 12, 32); } }

esta forma, podemos saber si el dispostivo SD est operativo (intentando abrir el directorio raiz) y en caso

ativo, ajustamos un flag de presencia y activamos la cach de lectura.

spositivos USB suelen causar problemas: a veces tardan mucho en inicializarse, no se resetean bien o se tropiezan,

hay veces que les da la neura y eso explica cosas raras que Hermes suele hacer en sus inicializaciones.

emplo, en Wiireader esto es lo que Hermes usa yo para inicializar libfat tratando de pillar dispositivo SD y

sitivo USB:

fatInit(8, false); sleep(2); // espero dos segundos, no se muy bien si para darle tiempo a que el dispositivo negocie o est preparado para funcionar fatSetDefaultInterface(PI_INTERNAL_SD); // usa SD como fat: have_device=0; // bits que me indican la presencia de dispositivos { path_file[3]=48+PI_INTERNAL_SD; // path_file ="fatX:/"; for(n=0;n<5;n++) {// numero de reintentos por si la unidad no est preparada o est "enojada" dir = diropen(path_file);

if (dir) { // monta la cache dirclose(dir); have_device|=1; fatEnableReadAhead(PI_INTERNAL_SD, 12, 32); break; } usleep(200*1000); } path_file[3]=48+PI_USBSTORAGE; // path_file ="fatX:/"; for(n=0;n<5;n++) { // numero de reintentos por si la unidad no est preparada o est "enojada" dir = diropen(path_file); if (dir) { // monta la cache dirclose(dir); have_device|=2; fatEnableReadAhead(PI_USBSTORAGE, 12, 32); break; } usleep(200*1000); } }

alguno lo ve superfluo? Pues tal vez, pero cuando uno est harto de que entras desde un RESET y no te ve el

drive", sales y entras y ahora s y cosas as, acabas por ver demonios por todas partes y si hacindolo as.

LIBFAT: Trabajando con ficheros y directorios


LISTANDO DIRECTORIOS

a listar ficheros y directorios, podis utilizar el siguiente procedimiento:

#include "sys/dir.h" ..... DIR_ITER * dir; static char namefile[256*4]; // reserva espacio suficiente para UTF-8 static struct stat filestat; dir = diropen("fat:/"); // abre el directorio raiz de la unidad fat: while(1) {

if(dirnext(dir, namefile, &filestat)!=0) break; // si no hay mas entradas en el directorio, sal if(filestat.st_mode & S_IFDIR) { // es el nombre de un directorio // namefile contiene el nombre del directorio en formato UTF8,que puede ser "." o ".." tambien } else { // namefile contiene el nombre del fichero en formato UTF-8 } } dirclose(dir); // cierra el directorio
OPERACIONES CON FICHEROS

us de iniciar libfat y comprobar que los dispositivos estn operativos, podis utilizar las libreras estndar ANSI-C

nte a dispositivos para crear/borrar/renombrar/fijar directorios.

emplo con:

mkdir("fat3:/jamacuco", S_IREAD | S_IWRITE);

creara el directorio "jamacuco" en raiz de la SD, fijando los correspondientes permisos. Quiz no sea buena idea

at3 as directamente, pues si algn lumbreras cambia el orden de los dispositivos maana , fat3 quizs apunte a

osa (de ahi eso de usar puntero[3]=48+PI_torreo).

eer/escribir ficheros, podis utilizar fopen/fread/fwrite/fseek/ftell/fclose...

nes alguna duda sobre las libreras estndar, pasaos por aqu y lo miris ms a fondo.

EL DESMONTAJE DE DISPOSITIVOS

ta conveniente asegurarse antes de salir de un programa de que todos los ficheros abiertos, se han cerrado y de

as cachs de escritura se han "flusheado". Resulta conveniente desmontar los dispositivos.

e puede hacer aadiendo esto a la funcin de salida que definimos con at_exit():

if(have_device & 1) fatUnmount(PI_INTERNAL_SD); // desmonta la SD if(have_device & 2) fatUnmount(PI_USBSTORAGE); // desmonta dispostivo USB

funciones las modific Hermes para que flushearn los datos incluso en el caso de que se detectaran ficheros

os antes de salir y no se pudieran desmontar los dispositivos. Si cerris los ficheros, se flushearn los buffers

icados, pero existen ciertas operaciones como crear directorios, borrarlos, etc, que pueden dejar alguna operacin

ente de escritura desde la cach al dispositivo y por eso es aconsejable desmontarlo. esto , tienes todo lo necesario para trabajar con ficheros.

Gestin del Tiempo


FORMAS DE PERDER EL TIEMPO

nclude <unistd.h> podemos encontrar dos interesantes funciones para "perder" el tiempo:

unsigned sleep(unsigned int __seconds ); // espera X segundos durmiendo el hilo int usleep(useconds_t __useconds); // espera X microsegundos durmiendo el hilo

plos:

sleep(2); // espera dos segundos usleep(2*1000); // espera 2 milisegundos

plicacin correcta de sta funcin sera: "Suspende la ejecucin del hilo (del programa) permitiendo que otro hilo

a tomar el control y al cabo de X tiempo, trata de continuar con la ejecucin del hilo en cuanto sea posible".

cir, que al margen de la precisin que pueda tener el temporizador, el tiempo que tarda el hilo en volver a

rtar, depender de si hay un hilo con mayor prioridad que est funcionando en el momento de cumplirse el plazo o

e una funcin interna en la librera libogc, que no est incluida en ningn fichero de cabecera (de ah que se

mine interna) llamada udelay, que podeis declararla as:

void udelay(int us); // retarda X microsegundos

uncin pierde el tiempo de forma similar a usleep, pero no duerme el hilo. La importancia de este matiz la podris

render cuando se hable de la programacin multithread pero os basta con saber que si un hilo no suspende (o

me) su ejecucin, no puede ser interrumpido por otro hilo salvo que ese nuevo hilo tenga una prioridad mayor.

o queda patente que usleep() permite la ejecucin de hilos que estn a la espera de inferior o igual prioridad,

ras que udelay() no.

MEDIDA RELATIVA DEL TIEMPO

na funcin llamada gettick() a nivel interno que nos devuelve un contador de 32 bits de tiempo. Esta funcin no

presente en ningn fichero de cabecera (si os interesa curiosear el fuente de las funciones, est en

/timesupp.c) por lo que tendris que definirla as:

unsigned gettick();

tick() devuelve "ticks" que hay que convertir a medidas de tiempo humanas.

odemos encontrar en /ogc/lwp_watchdog.h una serie de definiciones para realizar esa conversin:

ticks_to_secs(ticks); ticks_to_millisecs(ticks); ticks_to_microsecs(ticks); ticks_to_nanosecs(ticks);

ejemplo 3 se incluyo un procedimiento para medir el tiempo en ms de sta forma:

#define ticks_to_msecs(ticks) extern unsigned gettick();

((ticks)/TB_TIMER_CLOCK)

unsigned get_ms_clock() { // retorna los milisegundos transcurridos return ticks_to_msecs(gettick()); }

odemos medir el intervalo de tiempo entre dos medidas, haciendo la diferencia y usarlo para hacer los ajustes

arios en el programa (por ejemplo, actualizar movimientos de nuestros sprites de forma independiente al refresco

pantalla y cosas asi)

mbargo, existe una funcin que hace ms o menos lo mismo que gettick(), pero trabajando con medidas de 64 bits

gar de los 32 bits que devuelve gettick() y que se define en /ogc/lwp_watchdog.h

long long gettime();

enciona aqu por si necesitais precisin extra. Parece que gettime() gasta un tiempo en hacer la lectura).

RELOJ DE TIEMPO REAL

ogc/timesupp.c hay una funcin:

int clock_gettime(struct timespec *tp);

a funcin devuelve 0 si todo fue bien y los datos en una estructura timespec que se define as:

struct timespec { time_t tv_sec; /* Seconds */ // unsigned long (u32) long tv_nsec; /* Nanoseconds */ // (s32) };

a le falta un parmetro con respecto a la definicin de la funcin en time.h y se supone que equivale a

_gettime(CLOCK_REALTIME, &time); en otros sistemas.

, se deja dicho para que vosotros lo sepis. Eso si, ste tipo de funciones no estn pensadas para ser llamadas en

frame, porque suelen ser lentas al acceder al reloj de tiempo real, as que haced un uso responsable de ella.

TIMERS PROGRAMABLES

ema te permite definir una serie de alarmas de tiempo que vienen muy bien para trabajar con pasos regulares. un ojo a ogc/system.h ahora hemos visto funciones que nos permiten perder el tiempo y otras que nos permiten medir un intervalo, pero

nguna se puede estar seguro de que el tiempo transcurrido es el esperado, (dependiendo de la precisin del

orizador, claro).

lo las alarmas son especialmente tiles ya que te permiten programar un tiempo que una vez transcurrido, produce

nterrupcin que nos lleva a una callback de tratamiento donde teniendo cuidado, eso s (recordemos que estamos

mpo de interrupcin), podemos ajustar lo que sea necesario.

e esa callback por ejemplo, podemos "despertar" hilos de una forma regular (Hermes por ejemplo emplea una

a en su juego Guitarfun para actualizar graficos/leer pad cada 20 ms, mientras el hilo de fondo, descodifica y

duce Ogg y de esa forma, el programa trabaja de forma independiente al refresco de frames (50/60 Hz) para cada

to de imagen).

crear una alarma, primero necesitamos dos estructuras, una para alojar la alarma:

syswd_t myalarm;

para alojar el tiempo de la alarma:

struct timespec alarm_time;

ces podemos proceder a crear una alarma con:

s32 SYS_CreateAlarm(syswd_t *thealarm);

ta forma:

SYS_CreateAlarm(&myalarm);

uncin devuelve 0 si la alarma se cre correctamente, pero ahora toca ponerla en funcionamiento con alguna de

dos funciones:

s32 SYS_SetAlarm(syswd_t thealarm,const struct timespec *tp,alarmcallback cb); // una sola vez s32 SYS_SetPeriodicAlarm(syswd_t thealarm,const struct timespec *tp_start,const struct timespec *tp_period,alarmcallback cb); // peridicamente

primera ajusta la alarma para un nico uso pero nada impide para que dentro de la callback programes de nuevo la

a usando SetAlarm con el mismo u otro tiempo y repetir (o en cualquier otro punto). La segunda usa un

orizador inicial y luego otro que se utilizar para llamar de forma peridica a la callback thealarm: estructura alarm inicializada con SYS_CreateAlarm(). tp, tp_start, tp_periodic: estructuras timespec con el tiempo a programar. cb: callback que ser llamada al transcurrir el tiempo.

elven 0 si se pudo ajustar la alarma.

os algunos ejemplos:

mplo de Alarma de una sola vez:

syswd_t myalarm; struct timespec alarm_time; void alarm_handler() { // aqui llega cuando se cumpla el tiempo: recuerda que esto es una callback de interrupcion }

...... alarm_time.tv_sec=0; alarm_time.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos) SYS_CreateAlarm(&myalarm); SYS_SetAlarm(myalarm,&alarm_time,alarm_handler);

plo de Alarma Autoprogramable:

syswd_t myalarm; struct timespec alarm_time; int flip=0; void alarm_handler() { // aqui llega cuando se cumpla el tiempo: recuerda que esto es una callback de interrupcion alarm_time.tv_sec=0; if(flip) alarm_time.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos) else alarm_time.tv_nsec=40*1000*1000; // 40 milisegundos (notese que tv_nsec es en nanosegundos) flip^=1; SYS_SetAlarm(myalarm,&alarm_time,alarm_handler); // reprogramamos la alarma desde la callback } ...... alarm_time.tv_sec=0; alarm_time.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos) SYS_CreateAlarm(&myalarm); SYS_SetAlarm(myalarm,&alarm_time,alarm_handler);

plo de Alarma Peridica:

syswd_t myalarm; struct timespec alarm_time; struct timespec alarm_time2; int flip=0; void alarm_handler() { // aqui llega cuando se cumpla el tiempo: recuerda que esto es una callback de interrupcion // aqui llega despues de dos segundos y luego, cada 20 milisegundos } ...... alarm_time.tv_sec=2; // 2 segundos alarm_time.tv_nsec=0; alarm_time2.tv_sec=0; alarm_time2.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos) SYS_CreateAlarm(&myalarm); SYS_SetPeriodicAlarm(myalarm,&alarm_time, &alarm_time2, alarm_handler); // aguarda 2 segundos y a apartir de ah, llama la callback cada 20 ms
LIBERANDO/CANCELANDO ALARMAS

armas que podemos programar, son finitas. No se sabe cual ser el nmero exacto pero obviamente, lo podis

robar creando alarmas hasta que se produzca un error.

ncin:

s32 SYS_CancelAlarm(syswd_t thealarm);

puedes usar para cancelar alarmas ya programadas y en curso, mientras que la funcin:

s32 SYS_RemoveAlarm(syswd_t thealarm);

permite liberar las alarmas creadas con SYS_CreateAlarm();

esto ya tienes lo necesario para gestionar el tiempo de forma apropiada

Programacin Multihilo

i la programacin multihilo consiste en la posibilidad de interrumpir la ejecucin de un hilo para tratar otro o en la

lidad de aprovechar los tiempos muertos de un hilo, para gestionar otras cosas desde otro hilo.

milagros no existen y la CPU slo puede ocuparse de uno de ellos en cada momento, por lo que no se trata de

tar varias tareas de forma simultnea, si no de aprovechar los tiempos muertos como digo, o tratar una serie de

os conectados a las interrupciones desde un hilo, de forma que ese hilo interrumpe a otro para realizar alguna tarea

mismo tiempo, permite el paso de las interrupciones.

los tienen asignados todos un nivel de prioridad. Esta prioridad se utiliza para determinar que hilo toma el control y

la consiste en que el hilo que tiene mayor prioridad y est en espera de ser activado, se hace con el control.

osa que tenis que tener en cuenta y manejar con sumo cuidado, es el tema de las reentradas en las funciones.

o el tema de utilizar funciones del mismo grupo dentro de una librera o que comparten recursos.

emplo, si desde un hilo se leen los pads con WPAD_ScanPads() y desde otro se comprueban los datos ledos del

ote, es posible que el hilo que lee el Wiimote tome el control en medio de una llamada a WPAD_ScanPads() y reciba errneos y en otros casos, se pueden obtener una serie de paradojas.

estra funcin utiliza nicamente registros del procesador y variables locales o globales que no se modifican, no

is problemas en utilizarla en multihilo, puesto que cada hilo tiene su propia pila y los registros se salvan

s de cambiar de hilo. Pero si vuestra funcin accede a variables globales que se modifican, acceden a registros del

ware o a dispositivos, en definitiva todo lo que sea compartir recursos, entonces no estar preparada para un acceso

hilo y deberis protegerla mediante uso de semforos (que veremos luego).

LA CREACIN DE UN HILO

ncin LWP_CreateThread() es la encargada de crear los hilos (ver ogc/lwp.h):

s32 LWP_CreateThread(lwp_t *thethread,void* (*entry)(void *),void *arg,void *stackbase,u32 stack_size,u8 prio);


thethread: puntero de tipo lwp_t que recibe el handle. Por ejemplo: static lwp_t h_my_thread=LWP_THREAD_NULL; entry: funcin de entrada del hilo. arg: puntero con el argumento que recibir la funcin de entrada del hilo (para asignar datos privados). Puede ser NULL. stackbase: puntero con la direccin base de la pila (alineada a 32). Si NULL, se asigna de forma interna. stack_size: tamao de la pila en bytes. Si 0 se asigna 8KB por defecto. prio: prioridad del hilo, de 0 a 127 (maxima prioridad). Como referencia el player ogg utiliza 80 y yo suelo asignar a 40 el main() (no se conoce la prioridad de main() pero tampoco es que importe mucho).

ncin devuelve 0 si no hubo problema alguno.

LA FUNCIN DE ENTRADA DEL HILO

ncin de entrada sera algo as:

void * thread_main(void *arg) { // arg recibe el puntero arg que asignamos al crear el hilo return NULL; // aqui se sale del hilo }

dentemente, sera un hilo muy fugaz y su utilidad sera bastante baja. La cosa mejorara aadindole un bucle de

ol y algo que permitiera oxigenar el resto de hilos:

int exit_thread_main=0; void * thread_main(void *arg) { // arg recibe el puntero arg que asignamos al crear el hilo while(1) { usleep(20000); // aguarda 20 ms if(exit_thread_main) break; /* aqui haces lo que te de la gana */ } return NULL; // aqui se sale del hilo }

observamos que este hilo ha sido pensado para tener un prioridad mayor que la de main(), para que mas o menos,

20 milisegundos se active (usleep() como ya se explic, duerme el hilo y luego trata de despertarlo) y haga lo que que hacer en un bucle infinito (luego la tarea sea hara cada 20 ms+ lo que tarde el resto, que puede ser una

da de tiempo), pero que tiene una variable, exit_thread_main la cual asignndola a un valor no cero, rompe el

y eso hara que finalizara el hilo.

variable de control es muy importante puesto que este hilo por si solo no va a poder salir y eso dara lugar a un

ue de la mquina cuando pretendas volver al HBC por ejemplo. As pues, antes de volver deberas asignar

hread_main=1; y asegurarte que el hilo est 'despierto' para que se produzca ese break.

CONTROLANDO LA EJECUCIN DEL HILO

funcin thread_main de antes, hemos observado un mtodo que supone que el hilo va a su bola desactivndose por

io de 20 ms y luego volvindose activar. Sin embargo, seguramente os resultar mas til activar el hilo de forma

precisa, desde una callback de una alarma o cualquier otro sitio.

eso nos ser muy til el uso de colas:

static lwpq_t thread_queue=LWP_TQUEUE_NULL; // thread_queue contendr el handler de la cola

izamos la cola antes de crear el hilo con:

LWP_InitQueue(&thread_queue); // inicializa el queue

us creamos el hilo y en la funcin de entrada del hilo ponemos:

int exit_thread_main=0; void * thread_main(void *arg) { // arg recibe el puntero arg que asignamos al crear el hilo while(1) { if(exit_thread_main) break; // si ste es nuevo LWP_ThreadSleep(thread_queue); cola para poder despertarlo if(exit_thread_main) break; /* aqui haces lo que te de la gana */ } return NULL; // aqui se sale del hilo }

:D

// duerme el hilo usando una

se puede apreciar, ahora el hilo estar controlado por esa cola y necesitaremos decirle desde fuera que puede

nuar.

ello, desde una callback podis usar sta funcin:

LWP_ThreadSignal(thread_queue);

cerlo, en el momento en que las reglas de prioridad lo permitan, nuestro hilo despertar, har su tarea y al

letar el bucle, volver a dormirse en espera de que llamemos a LWP_ThreadSignal() de nuevo.

oneros algunos ejemplos, en el programa Guitarfun se usa el mtodo de las colas para actualizar los grficos cada

s mediante una alarma, los reproductores de audio esperan la activacin del hilo de sta forma para rellenar con

es los buffers, etc.

cnica de usleep() que usbamos al principio, se usa en Guitarfun tambin, para el nuevo hilo que se encarga de

ar los buffers de lectura de los ficheros Ogg, en lecturas de 256KB, tarea que puede consumir varios segundos y

or tanto, es despreciable la prdida de ms del usleep y precisa un funcionamiento independiente.

inalizar una cola se usa:

void LWP_CloseQueue(lwpq_t thequeue);


SALIR DE LOS HILOS

mos visto que si retornamos desde la funcin de inicio del hilo, salimos de l, pero si queremos salir de un hilo el principal o vamos a salir al sistema, conviene seguir los siguientes pasos:

exit_thread_main=1; // asignamos a 1 la variable que rompe el bucle while() LWP_ThreadSignal(thread_queue); // si estamos usando colas, forzamos que el hilo despierte en caso de estar dormido en la cola LWP_JoinThread(h_my_thread, NULL); // pasamos el handle del hilo (lwp_t) del que esperaremos su finalizacin LWP_CloseQueue(thread_queue); // cerramos la cola

lgo fallara aqu, la aplicacin se colgara, as que conviene asegurarse de que ninguna otra cosa bloquear la salida

o.

go Guitarfun o la aplicacin Wiireader, muestran varios ejemplos que podis estudiar.

d mucho cuidado eso si, con usar funciones que se interfieran con el uso de varios hilos

CAMBIANDO LA PRIORIDAD
habis visto, en Wii los hilos obedecen a temas prioridad a la hora de activarse, siendo el hilo de mayor prioridad

s abusn de la clase, no permitiendo la ejecucin de otros hilos salvo que el mismo se libere.

as veces, resulta interesante cambiar la prioridad de un hilo para forzar que tome el control y luego bajrsela, para

tir que otro le pueda interrumpir o simplemente, fijar una prioridad muy alta para que otro hilo no pueda

umpir al hilo actual hasta que acabe de hacer algo, momento en el cual restableceremos la prioridad.

esgracia en Wii hay un problema raro que afecta a la programacin multihilo y parece ser que a los nmeros

tes. Dicho problema se corrigi parcialmente, pero no del todo, as que puede ser conveniente proteger ciertas

ones de sta manera.

ncin para cambiar la prioridad es:

void LWP_SetThreadPriority(lwp_t thethread,u32 prio);


thethread: handle del hilo. Si LWP_THREAD_NULL se trata del hilo actual (no confundir con NULL). Tambin puedes usar LWP_GetSelf(); para conocer el Handle del hilo actual. prio: nueva prioridad para el hilo.

or ejemplo, si dentro del main() ponemos LWP_SetThreadPriority(LWP_THREAD_NULL, 40); asignamos a main() la

dad 40

iste una funcin equivalente para conocer la prioridad.

PROTEGER SECCIONES CRTICAS

mos dicho que cambiando la prioridad, podramos proteger una determinada seccin de un hilo contra el cambio de

solicitado desde una callback (interrupciones), pero hay puntos donde por conveniencia es mejor deshabilitar las

as interrupciones.

ello podemos contar con dos funciones, presentes en ogc/irq.h:

u32 IRQ_Disable(); // deshabilita las interrupciones y devuelve el estado void IRQ_Restore(u32 level); // restaura las interrupciones con el estado devuelto por IRQ_Disable()

s que esto se usa as:

u32 stat; stat=IRQ_Disable(); // deshabilita /* hacer algo que no lleve mucho tiempo, ni requiera interrupciones habilitadas */ IRQ_Restore(stat); // restaura
EL USO DE SEMFOROS

emforos se utilizan para regular el paso de los hilos hacia una funcin o funciones que solo permiten el acceso a un

ada vez, debido a una serie de caractersticas propias de esa funcin o funciones. Por ejemplo, si se va leer desde

podis programar el dispositivo para que os lea un sector y el dispositivo tardar un tiempo en "servroslo", pero no

s leer sectores no consecutivos de forma simultanea, ni el dispositivo puede atender mas peticiones hasta que haya

ado con la primera (ya que solo tiene "dos manos" hablando virtualmente).

ues, se necesita un mecanismo que haga que si dos o mas hilos acceden a una funcin protegida, solo uno pase y el

queden a la espera hasta que finalice el hilo que pas primero. Y de eso se encargan los semforos (un ejemplo lo

s en LIBFAT, que bloquea el acceso a todas sus funciones mediante semforos).

crear uno, primero tenemos que crear un identificador tal que as (ver ogc/mutex.h):

mutex_t mi_semaforo=LWP_MUTEX_NULL;

ncin para inicializarlo es sta:

s32 LWP_MutexInit(mutex_t *mutex,boolean use_recursive);

cializamos as:

LWP_MutexInit(&mi_semaforo, false);

mforo creado devolver 0 si se inici correctamente y permitir el paso de forma inicial.

do queramos eliminarlo, de sta forma:

LWP_MutexDestroy(mi_semaforo);

ya tenemos nuestro semforo creado, y ahora, cmo se usa en la prctica?. Pues para eso tenemos stas dos

ones:

s32 LWP_MutexLock(mutex_t mutex); // bloquea mediante el semforo s32 LWP_MutexUnlock(mutex_t mutex); // desbloquea el semforo

amos un ejemplo:

int

my_funcion(int manolo) { int ret=0;

LWP_MutexLock(mi_semaforo); // bloquea el paso de hilos. De forma inicial al crear el semforo, permite el paso de un hilo /* codigo protegido por el semaforo */ ...... ...... LWP_MutexUnlock(mi_semaforo); // desbloquea el semaforo y permite el paso del hilo siguiente a la funcin return ret; }

se puede apreciar, es un mecanismo muy sencillo. Aplicndolo a todas las funciones de una librera que sean

ibles, podemos evitar el acceso de otros hilos a una librera mientras est en uso por parte de uno de ellos, pero

no se traduce en un error de forma que la librera diga algo as "lo siento, pero estoy en uso: vuelva usted mas

", si no que el semforo lo convierte en "en est momento la funcin est ocupada, aguarde en la sala de espera que el hilo pepito abandone la funcin".

claro, es responsabilidad del hilo "pepito" informar con MutexUnlock de que otros hilos pueden acceder a ella. y

n es importante que el hilo "pepito" evite volver a pasar por MutexLock, puesto que ya no dispone de tarjeta de y ser bloqueado tambin.

sto es todo lo que tenis que saber sobre los semforos, los hilos y el copn de vino para trabajar con hilos. El resto

a vuestra. Categoras:

con Hilo Oficial

Wii

Hemos
movido el contenido de este artculo a la wiki. Si deseas contribuir con el artculo o

consultarlo puedes hacerlo siguiendo el link situado en la parte de arriba. Si lo que

quieres es realizar consultas o ayudar a otros hazlo en este hilo.


Ultima edicin por comepiedras el 22 Dic 2011 15:33, editado 7 veces A r r

i b a

ThemAsiiveStyleAdicto

Mensajes: 137 Registrado: 06 Nov 2008

por ThemAsiiveStyle- 01 Jul 2009 10:11

Que maldita Currada de tuto tio, esta tarde me lo leoi haver si puedo hacer algo!!
A r r i b a

Hermes
MegaAdicto!!!

Mensajes: 11053 Registrado: 18 Ene 2003

por Hermes 01 Jul 2009 10:12

LIBFAT: Acceso a los dispositivos de almacenamiento


NOTA: Describe la antigua LIBFAT Libfat es la librera que nos permite acceder a dispositivos del almacenamiento formateados en FAT12/FAT16 o FAT32, desde USB Gekko, el lector de SD interno y desde hace un tiempo, dispositivo USB (este ltimo limitado a las especificaciones USB 1.1) Para integrarla en tus programas, basta con aadir -lfat como librera en el Makefile (mis ejemplos, la incluyen) y #include <fat.h> en tus fuentes. UTF-8 frente a ANSI char set Antes de meternos en faena con la librera, conviene conocer un aspecto que puede daros alguna que otra sorpresa. Cuando los ordenadores iniciaron su andadura, se hizo

necesario emplear un estndar para poder manejar texto e intercambiar la informacin y el estndar elegido fue el ASCII que se defini entre 1963-1966 y posteriormente, se redefini en 1986 dando lugar a la especificacin ANSI que conocemos actualmente. ASCII se compona de 128 caracteres, usando 7 bits por tanto, donde se le aada un ltimo bit como bit de paridad en las transmisiones, dado que ste estndar se pens inicialmente para telegrafa. De esos 128 caracteres, los primeros 33 se utilizaban para cdigos de control no imprimibles. As por ejemplo, chr 8 representa un espacio atrs, chr 9 el tabulador, chr 10 avance de lnea , chr 13 el retorno de carro y chr 32 el espacio. De los imprimibles, el caracter '0' es chr 48, la 'A' es chr 65, y la 'a' chr 97 En algunos sistemas (LINUX/UNIX) el carcter 13 se toma como avance de lnea y retorno de carro de forma simultanea y esa es la razn por las que algunas veces, si abrimos un README desde el Wordpad de Windows, el texto se apelotona espera que las lneas terminen con los caracteres 10 y 13) Sin embargo, pronto se hizo necesario ampliar el set de caracteres, dado que ASCII recoga los caracteres latinos USA pero por ejemplo, no recoga la , ni los caracteres de acentuacin que nosotros conocemos. Esto se recoge cmo una extensin que ocupa los caracteres desde el 128 al 255 (usando lo que antes era un bit de paridad) conocido como estndar ISO-8859-1 y as en solo 8 bits, se recogan todos los caracteres especiales de origen latino. screenlib utiliza una captura de caracteres que contiene 224 caracteres (elimina los 32 primeros caracteres ASCII, pero conserva el carcter espacio) y que recogen esa especificacin ANSI con los caracteres extendidos ISO-8859-1 Puesto que estos estndares solo recogen caracteres latinos, se hizo necesario crear otros estndares que recogieran otros tipos de caracteres para soportar otros idiomas, como por ejemplo, el ruso y as naci el Unicode. Dentro del Unicode existe una especificacin llamada Utf-8 (8-bit Unicode Transformation Format) que utiliza un sistema de longitud variable para que partiendo de 8 bits, se puedan codificar todos los caracteres Unicode. Esta especificacin es la que utiliza la librera libfat a la hora de trabajar con nombres de ficheros, pero como nos afecta? (porque

Pues bien, Utf-8 utiliza el bit de mayor peso, el que actualmente recoge los caracteres ISO-8859-1 para cambiar y codificar la tabla de caracteres a utilizar. Y al ser de longitud variable, un carcter puede ocupar de 1 a 4 bytes En Unicode, los primeros 256 caracteres son los mismos que los especificados en la ISO-8859, por lo que los caracteres Utf-8 del 0 al 127, corresponderan directamente a lo que nosotros conocemos. Lo que cambia es la forma de tratar el bit de mayor peso y eso compromete los nombres de los ficheros que utilicen acentos, etc. As que tenis dos opciones: 1) O bien procuris utilizar nombres de ficheros/directorios que no usen caracteres fuera del rango de 128 caracteres definido por el estandar ANSI 2) O si no os queda ms remedio, tendris que convertir de Utf-8 a ISO-8859 para imprimir o de ISO-8859 a Utf-8 para manejar nombres de ficheros/directorios que usen esos caracteres especiales. En mi caso en mi aplicacin Wiireader, a mi slo me interesa ver el nombre de los ficheros correctamente al visualizarlos en pantalla, dado que al listar ficheros desde libfat, los obtengo en Utf-8 y por tanto, al abrirlos conservar ese Utf-8 As pues, como sabemos que los caracteres del 0 a 127 en Utf-8 son iguales y ocupan solo un byte, solo nos restara saber como se codifican los caracteres del 128 al 255 para poder imprimirlos correctamente desde la screenlib En http://es.wikipedia.org/wiki/UTF-8 podemos ver que para cubrir el rango de caracteres 0x80 a 0x7ff (nosotros solo necesitamos de 0x80 a 0xff), se utilizan dos bytes:

rango: 000080 - 0007FF Unicode (UTF-16) 00000xxx xxxxxxxx Utf-8: 110xxxxx 10xxxxxx

As pues, podramos hacer una rutina de conversin de Utf-8 a ISO-8859 de esta forma:

CDIGO: SELECCIONAR TODO

void UTF8_To_ISO8859(u8 *src, *u8 dst) { while(1) { if(src[0]==0) {*dst=0;break;} // fin de cadena

if((src[0] & 128)==0) {*dst++=*src++;} // rango de 0x0 a 0x7f else if((scr[0] & 224) ==192 && (scr[1] & 192)==128) // rango 0x80 a 0x7ff { if(src[0] & 0x1c) {*dst=0;break;} // error fuera del rango 0x80 a 0xff: caracteres no soportados

*dst++= (((src[0] & 3)<<6) | (src[1] & 63)); src+=2; } else // error fuera del rango de 0x0 a 0x7ff o no usa especificacin UTF-8 { *dst=0;break; } } }

Con esta rutina (que por cierto, acabo de crear aqu y no ha sido testeada :twisted:) podris convertir cadena utf-8 como fuente, a otra ISO como destino, para visualizarla con s_printf por ejemplo (caso de que el nombre quepa en pantalla ). Mi sugerencia sera que ampliases esa funcin para que en caso de ser un carcter no soportado, visualizara un carcter comodn (? por ejemplo), y que trabajases con Utf-8 siempre y solo convirtieras para visualizar (pero all cada uno )

********************************************************************* ******************************

LIBFAT: Inicializando

Libfat se inicializa con sta funcin:

bool fatInit (u32 cacheSize, bool setAsDefaultDevice);

Lo normal es que la inicialiceis as:

fatInit(8, false);

El 8 establece una cach de 8 clusters para entradas de directorios y cosas as. Libfat trata de montar una serie de dispositivos en unidades con nombres como "fat0:","fat1:",..., "fat4:"... pero permite asignar uno de esos dispoitivos cmo unidad por defecto "fat:". Lo normal es que esa unidad la asignis a la SD, de sta forma:

fatSetDefaultInterface(PI_INTERNAL_SD);

En fat.h podis ver una serie de dispositivos definidos como PI_algo, como PI_SDGECKO_A, o PI_USBSTORAGE. Sin embargo, si vais a acceder a varios dispositivos fat, es preferible utilizar el nombre original de la "particin" a estar intercambiado el interface por defecto. Asi pues, imaginemos que tenemos esta cadena: char name[]="fatX:/test.txt"; podramos especificar la unidad directamente, modificando la cadena de sta forma: name[3]=48+PI_INTERNAL_SD; (48 es el carcter ASCII '0') Detectando las particiones y asignando el tamao de la cach de lectura

Bien, con lo que tenemos hasta ahora, podramos funcionar sin problemas, pero por cuestiones de velocidad de lectura, puede resultar conveniente asignar un tamao interno de cach para la lectura de ficheros. Al mismo tiempo, resulta conveniente detectar si los dispositivos estn operativos, pues ciertas operaciones podran colgar directamente la consola. Para asignar el tamao de la cache de lectura, se utiliza sta funcin:

bool fatEnableReadAhead(PARTITION_INTERFACE partitionNumber, u32 numPages, u32 pageSize);

Para no liaros mucho, os dir que:

fatEnableReadAhead(PI_INTERNAL_SD, 12, 32);

fatEnableReadAhead(PI_USBSTORAGE, 12, 32);

Son unos buenos valores. El tamao de pgina y el nmero de paginas, requieren un uso de memoria que si no se administra bien, se desperdicia intilmente, por lo que no es conveniente abusar. Quiz ests pensando por que no nos explica mas a fondo el uso de stas funciones? Pues por que el autor original de la librera, ha decidido desechar los cambios que tanto rodries cmo yo introdujimos y est inmerso en una tarea de despedazar la librera y migrar fuentes, no se muy bien si con la idea de jugar al despiste o que: solo puedo decir que repite viejos errores y arrastra ciertos bugs que darn problemas, sobre todo en multithread. Y que yo no pienso mover un dedo ni para reportarlos, ni para migrar mis cambios, ni nada de nada, puesto que para m la libfat que usamos funciona bien y no me da la gana volver atrs, sobre todo en el tema de la cach. Pero obviamente, sto es mi opcin y si tu "actualizas" la librera, stas funciones de cach no las encontrars.

Antes de asignar un tamao de cache, conviene saber si el dispositivo est en funcionamiento:

{ DIR_ITER *dir; char path[]="fatX:/"; int have_device=0;

path[3]=48+PI_INTERNAL_SD; dir = diropen(path); if (dir) {dirclose(dir); {have_device|=1;fatEnableReadAhead(PI_INTERNAL_SD, 12, 32);} }

De esta forma, podemos saber si el dispostivo SD est operativo (intentando abrir el directorio raiz) y en caso afirmativo, ajustamos un flag de presencia y activamos la cach de lectura. Los dispositivos USB suelen causar problemas: a veces tardan mucho en inicializarse, no se resetean bien o se tropiezan o yo que se, pero hay veces que les da la neura y eso explica cosas raras que suelo hacer en mis inicializaciones. Por ejemplo, en Wiireader esto es lo que uso yo para inicializar libfat tratando de pillar dispositivo SD y dispositivo USB:

fatInit(8, false);

sleep(2); // espero dos segundos, no se muy bien si para darle tiempo a que el dispositivo negocie o est preparado para funcionar

fatSetDefaultInterface(PI_INTERNAL_SD); // usa SD como fat:

have_device=0; // bits que me indican la presencia de dispositivos

{ path_file[3]=48+PI_INTERNAL_SD; // path_file ="fatX:/";

for(n=0;n<5;n++) // numero de reintentos por si la unidad no est preparada o est "enojada" { dir = diropen(path_file); if (dir) {dirclose(dir); have_device|=1;fatEnableReadAhead(PI_INTERNAL_SD, 12, 32);break;} // monta la cache usleep(200*1000); }

path_file[3]=48+PI_USBSTORAGE; // path_file ="fatX:/";

for(n=0;n<5;n++) // numero de reintentos por si la unidad no est preparada o est "enojada" { dir = diropen(path_file); if (dir) {dirclose(dir); have_device|=2;fatEnableReadAhead(PI_USBSTORAGE, 12, 32);break;} // monta la cache usleep(200*1000); } }

Que alguno lo ve superfluo? Pues tal vez, pero cuando uno est hasta los cojones de que entras desde un RESET y no te ve el "penedrive", sales y entras y ahora s y cosas as, acabas por ver demonios por todas partes y si hacindolo as, no te pasa que haras tu? ********************************************************************* ******************************

LIBFAT: Trabajando con ficheros y directorios


Listando directorios Para listar ficheros y directorios,puedes utilizar el siguiente procedimiento:
CDIGO: SELECCIONAR TODO

#include "sys/dir.h" .....

DIR_ITER * dir;

static char namefile[256*4]; // reserva espacio suficiente para UTF-8

static struct stat filestat;

dir = diropen("fat:/"); // abre el directorio raiz de la unidad fat:

while(1) { if(dirnext(dir, namefile, &filestat)!=0) break; // si no hay mas entradas en el directorio, sal

if(filestat.st_mode & S_IFDIR) // es el nombre de un directorio { // namefile contiene el nombre del directorio en formato UTF-8,que puede ser "." o ".." tambien } else { // namefile contiene el nombre del fichero en formato UTF-8 } }

dirclose(dir); // cierra el directorio

Operaciones con ficheros Despus de iniciar libfat y comprobar que los dispositivos estn operativos, podis utilizar las libreras estndar ANSI-C referente a dispositivos para crear/borrar/renombrar/fijar directorios. Por ejemplo con:

mkdir("fat3:/jamacuco", S_IREAD | S_IWRITE);

Se creara el directorio "jamacuco" en raiz de la SD, fijando los correspondientes permisos. Quiz no sea buena idea usar fat3 as directamente, pues si algn lumbreras cambia el orden de los dispositivos maana , fat3 quiz apunte a otra cosa (de ahi eso de usar puntero[3]=48+PI_torreo) Para leer/escribir ficheros, podis utilizar fopen/fread/fwrite/fseek/ftell/fclose... Vamos, que si tienes alguna duda sobre las libreras estndar, psate por aqu y te lo miras ms a fondo :

http://c.conclase.net/librerias/index.php El Desmontaje de dispositivos Resulta conveniente asegurarse antes de salir de un programa de que todos los ficheros abiertos, se han cerrado y de que las cachs de escritura se han "flusheado". Resulta conveniente desmontar los dispositivos: Eso se puede hacer aadiendo esto a la funcin de salida que definimos con at_exit():

if(have_device & 1) fatUnmount(PI_INTERNAL_SD); // desmonta la SD if(have_device & 2) fatUnmount(PI_USBSTORAGE); // desmonta dispostivo USB

Estas funciones las modifiqu para que flushearn los datos incluso en el caso de que se detectaran ficheros abiertos antes de salir y no se pudieran desmontar los dispositivos. Si cierras los ficheros, se flushearn los buffers modificados, pero existen ciertas operaciones como crear directorios, borrarlos, etc, que pueden dejar alguna operacin pendiente de escritura desde la cach al dispositivo y por eso es aconsejable desmontarlo. Y con esto y un bizcocho, tienes todo lo necesario para trabajar con ficheros

********************************************************************* ******************************

Gestin del Tiempo


Formas de perder el tiempo En: #include <unistd.h> Podemos encontrar dos interesantes funciones para "perder" el tiempo:

unsigned sleep(unsigned int __seconds ); // espera X segundos durmiendo el hilo int usleep(useconds_t __useconds); // espera X microsegundos durmiendo el hilo

Ejemplos:

sleep(2); // espera dos segundos usleep(2*1000); // espera 2 milisegundos

La explicacin correcta de sta funcin sera: "Suspende la ejecucin del hilo (del programa) permitiendo que otro hilo pueda tomar el control y al cabo de X tiempo, trata de continuar con la ejecucin del hilo en cuanto sea posible". Es decir, que al margen de la precisin que pueda tener el temporizador, el tiempo que tarda el hilo en volver a despertar, depender de si hay un hilo con mayor prioridad que est funcionando en el momento de cumplirse el plazo o no. Existe una funcin interna en la librera libogc, que no est incluida en ningn fichero de cabecera (de ah que la denomine interna) llamada udelay, que podeis declararla as:

void udelay(int us); // retarda X microsegundos

Esta funcin pierde el tiempo de forma similar a usleep, pero no duerme el hilo. La importancia de este matiz la podris comprender cuando hable de la programacin multithread pero os basta con saber que si un hilo no suspende (o duerme) su ejecucin, no puede ser interrumpido por otro hilo salvo que ese nuevo hilo tenga una prioridad mayor. Luego queda patente que usleep() permite la ejecucin de hilos que estn a la espera de inferior o igual prioridad, mientras que udelay() no. Medida Relativa del tiempo Hay una funcin llamada gettick() a nivel interno que nos devuelve un contador de 32 bits de tiempo. Esta funcin no est presente en ningn fichero de cabecera (si os interesa curiosear el fuente de las funciones, est en libogc/timesupp.c) por lo que tendris que definirla as:

unsigned gettick();

gettick() devuelve "ticks" que hay que convertir a medidas de tiempo humanas. As podemos encontrar en /ogc/lwp_watchdog.h una serie de definiciones para realizar esa conversin:

ticks_to_secs(ticks); ticks_to_millisecs(ticks); ticks_to_microsecs(ticks); ticks_to_nanosecs(ticks);

En el ejemplo 3 yo incluyo un procedimiento para medir el tiempo en ms de sta forma:


CDIGO: SELECCIONAR TODO

#define ticks_to_msecs(ticks) extern unsigned gettick();

((ticks)/TB_TIMER_CLOCK)

unsigned get_ms_clock() // retorna los milisegundos transcurridos { return ticks_to_msecs(gettick()); }

As podemos medir el intervalo de tiempo entre dos medidas, haciendo la diferencia y usarlo para hacer los ajustes necesarios en el programa (por ejemplo, actualizar movimientos de nuestros sprites de forma independiente al refresco de la pantalla y cosas asi) Sin embargo, existe una funcin que hace ms o menos lo mismo que gettick(), pero trabajando con medidas de 64 bits en lugar de los 32 bits que devuelve gettick() y que se define en /ogc/lwp_watchdog.h

long long gettime();

Te lo menciono aqu por si necesitas precisin extra, pero para mi uso particular, con gettick() me es suficiente (y parece que gettime() gasta un tiempo en hacer la lectura) Reloj de Tiempo Real En libogc/timesupp.c hay una funcin:

int clock_gettime(struct timespec *tp);

Esta funcin devuelve 0 si todo fue bien y los datos en una estructura timespec que se define as:

struct timespec { time_t tv_sec; /* Seconds */ // unsigned long (u32) long tv_nsec; /* Nanoseconds */ // (s32) };

Yo no he empleado sta funcin nunca, pero de entrada, le falta un parmetro con respecto a la definicin de la funcin en time.h y supongo que equivale a clock_gettime(CLOCK_REALTIME, &time); en otros sistemas. En fin, lo dejo dicho para que vosotros lo sepis: eso si, ste tipo de funciones no estn pensadas para ser llamadas en cada frame, porque suelen ser lentas al acceder al reloj de tiempo real, as que haced un uso responsable de ella. Timers programables El sistema te permite definir una serie de alarmas de tiempo que vienen muy bien para trabajar con pasos regulares. Echad un ojo a ogc/system.h Hasta ahora hemos visto funciones que nos permiten perder el tiempo y otras que nos permiten medir un intervalo, pero en ninguna se puede estar seguro de que el tiempo transcurrido es el esperado, (dependiendo de la precisin del temporizador, claro) Por ello las alarmas son especialmente tiles ya que te permiten programar un tiempo que una vez transcurrido, produce una interrupcin que nos lleva a una callback de tratamiento donde teniendo cuidado, eso s (recordemos que estamos en tiempo de interrupcin), podemos ajustar lo que sea necesario. Desde esa callback por ejemplo, podemos "despertar" hilos de una forma regular (yo por ejemplo, empleo una alarma en mi juego Guitarfun para actualizar graficos/leer pad cada 20 ms, mientras el hilo de fondo, descodifica y reproduce Ogg y de esa forma, el programa trabaja de forma independiente al refresco de frames (50/60 Hz) para cada formato de imagen) La creacin de una alarma Para crear una alarma, primero necesitamos dos estructuras, una para alojar la

alarma:

syswd_t myalarm;

Y otra para alojar el tiempo de la alarma:

struct timespec alarm_time;

Entonces podemos proceder a crear una alarma con:

s32 SYS_CreateAlarm(syswd_t *thealarm);

De esta forma:

SYS_CreateAlarm(&myalarm);

la funcin devuelve 0 si la alarma se cre correctamente, pero ahora toca ponerla en funcionamiento con alguna de estas dos funciones:

s32 SYS_SetAlarm(syswd_t thealarm,const struct timespec *tp,alarmcallback cb); // una sola vez

s32 SYS_SetPeriodicAlarm(syswd_t thealarm,const struct timespec *tp_start,const struct timespec *tp_period,alarmcallback cb); // peridicamente

La primera ajusta la alarma para un nico uso pero nada impide para que dentro de la callback programes de nuevo la alarma usando SetAlarm con el mismo u otro tiempo y repetir (o en cualquier otro punto). La segunda usa un temporizador inicial y luego otro que se utilizar para llamar de forma peridica a la callback

-thealarm: Estructura alarm inicializada con SYS_CreateAlarm() -tp, tp_start, tp_periodic: Estructuras timespec con el tiempo a programar. -cb: Callback que ser llamada al transcurrir el tiempo Devuelven 0 si se pudo ajustar la alarma. Veamos algunos ejemplos: Ejemplo de Alarma de una sola vez:
CDIGO: SELECCIONAR TODO

syswd_t myalarm; struct timespec alarm_time;

void alarm_handler() { // aqui llega cuando se cumpla el tiempo: recuerda que esto es una callback de interrupcion

......

alarm_time.tv_sec=0; alarm_time.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos)

SYS_CreateAlarm(&myalarm); SYS_SetAlarm(myalarm,&alarm_time,alarm_handler);

Ejemplo de Alarma Autoprogramable:

CDIGO: SELECCIONAR TODO

syswd_t myalarm; struct timespec alarm_time;

int flip=0;

void alarm_handler() { // aqui llega cuando se cumpla el tiempo: recuerda que esto es una callback de interrupcion

alarm_time.tv_sec=0; if(flip) alarm_time.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos) else alarm_time.tv_nsec=40*1000*1000; // 40 milisegundos (notese que tv_nsec es en nanosegundos)

flip^=1; SYS_SetAlarm(myalarm,&alarm_time,alarm_handler); // reprogramamos la alarma desde la callback }

......

alarm_time.tv_sec=0; alarm_time.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos)

SYS_CreateAlarm(&myalarm); SYS_SetAlarm(myalarm,&alarm_time,alarm_handler);

Ejemplo de Alarma Peridica:


CDIGO: SELECCIONAR TODO

syswd_t myalarm; struct timespec alarm_time; struct timespec alarm_time2; int flip=0;

void alarm_handler() { // aqui llega cuando se cumpla el tiempo: recuerda que esto es una callback de interrupcion

// aqui llega despues de dos segundos y luego, cada 20 milisegundos }

......

alarm_time.tv_sec=2; // 2 segundos alarm_time.tv_nsec=0;

alarm_time2.tv_sec=0; alarm_time2.tv_nsec=20*1000*1000; // 20 milisegundos (notese que tv_nsec es en nanosegundos)

SYS_CreateAlarm(&myalarm); SYS_SetPeriodicAlarm(myalarm,&alarm_time, &alarm_time2, alarm_handler); // aguarda 2 segundos y a apartir de ah, llama la callback cada 20 ms

Liberando/Cancelando alarmas Las alarmas que podemos programar, son finitas. No se cual ser el nmero exacto pero obviamente,lo puedes comprobar creando alarmas hasta que se produzca un error La funcin:

s32 SYS_CancelAlarm(syswd_t thealarm);

La puedes usar para cancelar alarmas ya programadas y en curso, mientras que la funcin:

s32 SYS_RemoveAlarm(syswd_t thealarm);

Te permite liberar las alarmas creadas con SYS_CreateAlarm(); Y con esto ya tienes lo necesario para gestionar el tiempo de forma apropiada

********************************************************************* ******************************

Programacin Multihilo
En Wii la programacin multihilo consiste en la posibilidad de interrumpir la ejecucin de un hilo para tratar otro o en la posibilidad de aprovechar los tiempos muertos de un hilo, para gestionar otras cosas desde otro hilo. Los milagros no existen y la CPU slo puede ocuparse de uno de ellos en cada momento, por lo que no se trata de ejecutar varias tareas de forma simultnea, si no de aprovechar los tiempos muertos como digo, o tratar una serie de eventos conectados a las interrupciones desde un hilo, de forma que ese hilo interrumpe a otro para realizar alguna tarea y al mismo tiempo, permite el paso de las interrupciones. Los hilos tienen asignados todos un nivel de prioridad. Esta prioridad se utiliza para determinar que hilo toma el control y la regla consiste en que el hilo que tiene mayor prioridad y est en espera de ser activado, se hace con el control. Una cosa que tenis que tener en cuenta y manejar con sumo cuidado, es el tema de las reentradas en las funciones. Incluso el tema de utilizar funciones del mismo grupo

dentro de una librera o que comparten recursos. Por ejemplo, si desde un hilo se leen los pads con WPAD_ScanPads() y desde otro se comprueban los datos ledos del Wiimote, es posible que el hilo que lee el Wiimote tome el control en medio de una llamada a WPAD_ScanPads() y reciba datos errneos y en otros casos, se pueden obtener una serie de paradojas. Si tu funcin utiliza nicamente registros del procesador y variables locales o globales que no se modifican, no tendrs problemas en utilizarla en multihilo, puesto que cada hilo tiene su propia pila y los registros se salvan antes de cambiar de hilo . Pero si tu funcin accede a variables globales que se modifican, acceden a registros del hardware o a dispositivos, en definitiva todo lo que sea compartir recursos, entonces no estar preparada para un acceso multihilo y deberas protegerla mediante uso de semforos (que veremos luego). La creacin de un hilo La funcin LWP_CreateThread() es la encargada de crear los hilos (ver ogc/lwp.h):

s32 LWP_CreateThread(lwp_t *thethread,void* (*entry)(void *),void *arg,void *stackbase,u32 stack_size,u8 prio);

-thethread: Puntero de tipo lwp_t que recibe el handle. Por ejemplo: static lwp_t h_my_thread=LWP_THREAD_NULL; -entry: Funcin de entrada del hilo -arg: Puntero con el argumento que recibir la funcin de entrada del hilo (para asignar datos privados). Puede ser NULL -stackbase: Puntero con la direccin base de la pila (alineada a 32). Si NULL, se asigna de forma interna. -stack_size: Tamao de la pila en bytes. Si 0 se asigna 8KB por defecto. -prio: Prioridad del hilo, de 0 a 127 (maxima prioridad). Como referencia el player ogg

utiliza 80 y yo suelo asignar a 40 el main() (no conozco la prioridad de main() pero tampoco es que importe mucho ).

La funcin devuelve 0 si no hubo problema alguno. La funcin de entrada del hilo La funcin de entrada sera algo as:
CDIGO: SELECCIONAR TODO

void * thread_main(void *arg) { // arg recibe el puntero arg que asignamos al crear el hilo

return NULL; // aqui se sale del hilo }

Evidentemente, sera un hilo muy fugaz y su utilidad sera bastante baja. La cosa mejorara aadindole un bucle de control y algo que permitiera oxigenar el resto de hilos:
CDIGO: SELECCIONAR TODO

int exit_thread_main=0;

void * thread_main(void *arg) { // arg recibe el puntero arg que asignamos al crear el hilo

while(1) { usleep(20000); // aguarda 20 ms if(exit_thread_main) break;

/* aqui haces lo que te de la gana */

return NULL; // aqui se sale del hilo }

Aqu observamos que este hilo ha sido pensado para tener un prioridad mayor que la de main(), para que mas o menos, cada 20 milisegundos se active (usleep() como ya expliqu, duerme el hilo y luego trata de despertarlo) y haga lo que tenga que hacer en un bucle infinito (luego la tarea sea hara cada 20 ms+ lo que tarde el resto, que puede ser una burrada de tiempo), pero que tiene una variable, exit_thread_main la cual asignndola a un valor no cero, rompe el bucle y eso hara que finalizara el hilo. sta variable de control es muy importante puesto que este hilo por si solo no va a poder salir y eso dara lugar a un cuelgue de la mquina cuando pretendas volver al HBC por ejemplo. As pues, antes de volver deberas asignar exit_thread_main=1; y asegurarte que el hilo est 'despierto' para que se produzca ese break.

Controlando la ejecucin del hilo En la funcin thread_main de antes, hemos observado un mtodo que supone que el hilo va a su bola desactivndose por espacio de 20 ms y luego volvindose activar. Sin embargo, seguramente te resultar mas til activar el hilo de forma mas precisa, desde una callback de una alarma o cualquier otro sitio. Para eso nos ser muy til el uso de colas:

static lwpq_t thread_queue=LWP_TQUEUE_NULL; // thread_queue contendr el handler de la cola

Inicializamos la cola antes de crear el hilo con:

LWP_InitQueue(&thread_queue); // inicializa el queue

Despus creamos el hilo y en la funcin de entrada del hilo ponemos:


CDIGO: SELECCIONAR TODO

int exit_thread_main=0;

void * thread_main(void *arg) { // arg recibe el puntero arg que asignamos al crear el hilo

while(1) { if(exit_thread_main) break; // si ste es nuevo :D

LWP_ThreadSleep(thread_queue); poder despertarlo

// duerme el hilo usando una cola para

if(exit_thread_main) break;

/* aqui haces lo que te de la gana */

return NULL; // aqui se sale del hilo }

Como se puede apreciar, ahora el hilo estar controlado por esa cola y necesitaremos decirle desde fuera que puede continuar. Para ello, desde una callback podis usar sta funcin:

LWP_ThreadSignal(thread_queue);

Al hacerlo, en el momento en que las reglas de prioridad lo permitan, nuestro hilo despertar, har su tarea y al completar el bucle, volver a dormirse en espera de que llamemos a LWP_ThreadSignal() de nuevo. Por poneros algunos ejemplos, en mi programa Guitarfun se usa el mtodo de las colas para actualizar los grficos cada 20 ms mediante una alarma, los reproductores de audio esperan la activacin del hilo de sta forma para rellenar con samples los buffers, etc. La tcnica de usleep() que usbamos al principio, la uso en Guitarfun tambin, para el nuevo hilo que se encarga de rellenar los buffers de lectura de los ficheros Ogg, en lecturas de 256KB, tarea que puede consumir varios segundos y que por tanto, es despreciable la prdida de ms del usleep y precisa un funcionamiento independiente. Para finalizar una cola se usa:

void LWP_CloseQueue(lwpq_t thequeue);

Salir de los Hilos Ya hemos visto que si retornamos desde la funcin de inicio del hilo, salimos de l, pero si queremos salir de un hilo desde el principal o vamos a salir al sistema, conviene seguir los siguientes pasos:

exit_thread_main=1; // asignamos a 1 la variable que rompe el bucle while()

LWP_ThreadSignal(thread_queue); // si estamos usando colas, forzamos que el hilo despierte en caso de estar dormido en la cola

LWP_JoinThread(h_my_thread, NULL); // pasamos el handle del hilo (lwp_t) del que esperaremos su finalizacin

LWP_CloseQueue(thread_queue); // cerramos la cola

Si algo fallara aqu, la aplicacin se colgara, as que conviene asegurarse de que ninguna otra cosa bloquear la salida del hilo. No he preparado ejemplos con uso de mltiples hilos, pero mi juego Guitarfun o mi aplicacin Wiireader, muestran varios ejemplos que podis estudiar. Tened mucho cuidado eso si, con usar funciones que se interfieran con el uso de varios hilos Cambiando la prioridad Cmo habis visto, en Wii los hilos obedecen a temas prioridad a la hora de activarse, siendo el hilo de mayor prioridad el ms abusn de la clase, no permitiendo la ejecucin de otros hilos salvo que el mismo se libere. Algunas veces, resulta interesante cambiar la prioridad de un hilo para forzar que tome el control y luego bajrsela, para permitir que otro le pueda interrumpir o simplemente, fijar una prioridad muy alta para que otro hilo no pueda interrumpir al hilo actual hasta que acabe de hacer algo, momento en el cual restableceremos la prioridad. Por desgracia en Wii hay un problema raro que afecta a la programacin multihilo y parece ser que a los nmeros flotantes. Dicho problema se corrigi parcialmente, pero no del todo, as que puede ser conveniente proteger ciertas secciones de sta manera. La funcin para cambiar la prioridad es:

void LWP_SetThreadPriority(lwp_t thethread,u32 prio);

-thethread: Handle del hilo. Si LWP_THREAD_NULL se trata del hilo actual (no confundir con NULL). Tambin puedes usar LWP_GetSelf(); para conocer el Handle del hilo actual. -prio: Nueva prioridad para el hilo

As por ejemplo, si dentro del main() ponemos LWP_SetThreadPriority(LWP_THREAD_NULL, 40); asignamos a main() la prioridad 40 Nota: no existe una funcin equivalente para conocer la prioridad Proteger secciones crticas Ya hemos dicho que cambiando la prioridad, podramos proteger una determinada seccin de un hilo contra el cambio de hilos solicitado desde una callback (interrupciones), pero hay puntos donde por conveniencia es mejor deshabilitar las propias interrupciones. Para ello podemos contar con dos funciones, presentes en ogc/irq.h:

u32 IRQ_Disable(); // deshabilita las interrupciones y devuelve el estado

void IRQ_Restore(u32 level); // restaura las interrupciones con el estado devuelto por IRQ_Disable()

Vamos que esto se usa as:


CDIGO: SELECCIONAR TODO

u32 stat;

stat=IRQ_Disable(); // deshabilita

/* hacer algo que no lleve mucho tiempo, ni requiera interrupciones habilitadas */

IRQ_Restore(stat); // restaura

El uso de semforos

Los semforos se utilizan para regular el paso de los hilos hacia una funcin o funciones que solo permiten el acceso a un hilo cada vez, debido a una serie de caractersticas propias de esa funcin o funciones. Por ejemplo, si voy a leer desde USB, puedes programar el dispositivo para que te lea un sector y el dispositivo tardar un tiempo en "servrtelo", pero no puedes leer sectores no consecutivos de forma simultanea, ni el dispositivo puede atender mas peticiones hasta que haya finalizado con la primera (ya que solo tiene "dos manos" hablando virtualmente ).

As pues, se necesita un mecanismo que haga que si dos o mas hilos acceden a una funcin protegida, solo uno pase y el resto queden a la espera hasta que finalice el hilo que pas primero. Y de eso se encargan los semforos (un ejemplo lo tenis en LIBFAT, que bloquea el acceso a todas sus funciones mediante semforos) Para crear uno, primero tenemos que crear un identificador tal que as (ver ogc/mutex.h):

mutex_t mi_semaforo=LWP_MUTEX_NULL;

La funcin para inicializarlo es sta:

s32 LWP_MutexInit(mutex_t *mutex,boolean use_recursive);

Lo inicializamos as:

LWP_MutexInit(&mi_semaforo, false);

El semforo creado devolver 0 si se inici correctamente y permitir el paso de forma inicial. Cuando queramos eliminarlo, de sta forma (lo explico ahora, que si n, luego me olvido )

LWP_MutexDestroy(mi_semaforo);

Bien, ya tenemos nuestro semforo creado Y ahora cmo se usa en la prctica? Pues para eso tenemos stas dos funciones:

s32 LWP_MutexLock(mutex_t mutex); // bloquea mediante el semforo

s32 LWP_MutexUnlock(mutex_t mutex); // desbloquea el semforo

Pongamos un ejemplo:
CDIGO: SELECCIONAR TODO

int {

my_funcion(int manolo)

int ret=0;

LWP_MutexLock(mi_semaforo); // bloquea el paso de hilos. De forma inicial al crear el semforo, permite el paso de un hilo

/* codigo protegido por el semaforo */ ...... ......

LWP_MutexUnlock(mi_semaforo); // desbloquea el semaforo y permite el paso del hilo siguiente a la funcin

return ret; }

Como se puede apreciar, es un mecanismo muy sencillo: aplicndolo a todas las funciones de una librera que sean accesibles, podemos evitar el acceso de otros hilos a una librera mientras est en uso por parte de uno de ellos, pero esto no se traduce en un error de forma que la librera diga algo as "lo siento, pero estoy en uso: vuelva usted mas tarde", si no que el semforo lo convierte en "en est momento la funcin est ocupada, aguarde en la sala de espera hasta que el hilo pepito abandone la funcin". Pero claro, es responsabilidad del hilo "pepito" informar con MutexUnlock de que otros hilos pueden acceder a ella. y tambin es importante que el hilo "pepito" evite volver a pasar por MutexLock, puesto que ya no dispone de tarjeta de visita y ser bloqueado tambin. Y sto es todo lo que tenis que saber sobre los semforos, los hilos y el copn de vino para trabajar con hilos. El resto es cosa vuestra. FIN

S-ar putea să vă placă și