Curso de Programación

1, 2, 3
Imagen Hemos

movido el contenido de este artículo a la wiki. Si deseas contribuir con el artículo 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.
Que maldita Currada de tuto tio, esta tarde me lo leoi haver si puedo hacer algo!!
LIBFAT: Acceso a los dispositivos de almacenamiento

NOTA: Describe la antigua LIBFAT

Libfat es la librería 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 añadir -lfat como librería 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 librería, conviene conocer un aspecto que puede daros alguna que otra sorpresa. Cuando los ordenadores iniciaron su andadura, se hizo necesario emplear un estándar para poder manejar texto e intercambiar la información y el estándar elegido fue el ASCII que se definió entre 1963-1966 y posteriormente, se redefinió en 1986 dando lugar a la especificación ANSI que conocemos actualmente.

ASCII se componía de 128 caracteres, usando 7 bits por tanto, donde se le añadía un último bit como bit de paridad en las transmisiones, dado que éste estándar se pensó inicialmente para telegrafía.

De esos 128 caracteres, los primeros 33 se utilizaban para códigos de control no imprimibles. Así por ejemplo, chr 8 representa un espacio atrás, chr 9 el tabulador, chr 10 avance de línea , 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 carácter 13 se toma como avance de línea y retorno de carro de forma simultanea y esa es la razón por las que algunas veces, si abrimos un README desde el Wordpad de Windows, el texto se apelotona XD (porque espera que las líneas terminen con los caracteres 10 y 13)

Sin embargo, pronto se hizo necesario ampliar el set de caracteres, dado que ASCII recogía los caracteres latinos USA pero por ejemplo, no recogía la ñ, ni los caracteres de acentuación que nosotros conocemos. Esto se recoge cómo una extensión que ocupa los caracteres desde el 128 al 255 (usando lo que antes era un bit de paridad) conocido como estándar ISO-8859-1 y así en solo 8 bits, se recogían 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 carácter espacio) y que recogen esa especificación ANSI con los caracteres extendidos ISO-8859-1

Puesto que estos estándares solo recogen caracteres latinos, se hizo necesario crear otros estándares 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 especificación 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 especificación es la que utiliza la librería libfat a la hora de trabajar con nombres de ficheros, pero ¿como nos afecta?

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 carácter 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, corresponderían 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 tenéis dos opciones:

1) O bien procuráis 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 más remedio, tendréis 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 aplicación Wiireader, a mi sólo 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 restaría 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, podríamos hacer una rutina de conversión 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) {*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 especificación UTF-8
           {
           *dst=0;break;
           }
       }
}


Con esta rutina (que por cierto, acabo de crear aquí y no ha sido testeada :twisted:) podréis 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 XD). Mi sugerencia sería que ampliases esa función para que en caso de ser un carácter no soportado, visualizara un carácter comodín (? por ejemplo), y que trabajases con Utf-8 siempre y solo convirtieras para visualizar (pero allá cada uno XD )

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

LIBFAT: Inicializando

Libfat se inicializa con ésta función:

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 cómo unidad por defecto "fat:".

Lo normal es que esa unidad la asignéis a la SD, de ésta forma:

fatSetDefaultInterface(PI_INTERNAL_SD);


En fat.h podéis 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 "partición" a estar intercambiado el interface por defecto.

Asi pues, imaginemos que tenemos esta cadena:

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

podríamos especificar la unidad directamente, modificando la cadena de ésta forma: name[3]=48+PI_INTERNAL_SD; (48 es el carácter ASCII '0')

Detectando las particiones y asignando el tamaño de la caché de lectura

Bien, con lo que tenemos hasta ahora, podríamos funcionar sin problemas, pero por cuestiones de velocidad de lectura, puede resultar conveniente asignar un tamaño interno de caché para la lectura de ficheros. Al mismo tiempo, resulta conveniente detectar si los dispositivos están operativos, pues ciertas operaciones podrían colgar directamente la consola.

Para asignar el tamaño de la cache de lectura, se utiliza ésta función:

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


Para no liaros mucho, os diré que:

fatEnableReadAhead(PI_INTERNAL_SD, 12, 32);

o

fatEnableReadAhead(PI_USBSTORAGE, 12, 32);


Son unos buenos valores. El tamaño de página y el número de paginas, requieren un uso de memoria que si no se administra bien, se desperdicia inútilmente, por lo que no es conveniente abusar.

Quizá estés pensando ¿por que no nos explica mas a fondo el uso de éstas funciones? Pues por que el autor original de la librería, ha decidido desechar los cambios que tanto rodries cómo yo introdujimos y está inmerso en una tarea de despedazar la librería 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 darán 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 atrás, sobre todo en el tema de la caché. Pero obviamente, ésto es mi opción y si tu "actualizas" la librería, éstas funciones de caché no las encontrarás.

Antes de asignar un tamaño 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" XD
{
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" XD
{
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 haciéndolo así, no te pasa ¿que harías tu? XD

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

LIBFAT: Trabajando con ficheros y directorios

Listando directorios

Para listar ficheros y directorios,puedes 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 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

Después de iniciar libfat y comprobar que los dispositivos están operativos, podéis utilizar las librerías estándar ANSI-C referente a dispositivos para crear/borrar/renombrar/fijar directorios.

Por ejemplo con:

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


Se crearía el directorio "jamacuco" en raiz de la SD, fijando los correspondientes permisos. Quizá no sea buena idea usar fat3 así directamente, pues si algún lumbreras cambia el orden de los dispositivos mañana , fat3 quizá apunte a otra cosa (de ahi eso de usar puntero[3]=48+PI_torreo)

Para leer/escribir ficheros, podéis utilizar fopen/fread/fwrite/fseek/ftell/fclose...

Vamos, que si tienes alguna duda sobre las librerías estándar, pásate por aquí y te lo miras más a fondo :D:

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 cachés de escritura se han "flusheado". Resulta conveniente desmontar los dispositivos:

Eso se puede hacer añadiendo esto a la función 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 flushearán 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 flushearán los buffers modificados, pero existen ciertas operaciones como crear directorios, borrarlos, etc, que pueden dejar alguna operación 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 ;)

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

Gestión 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 explicación correcta de ésta función sería: "Suspende la ejecución del hilo (del programa) permitiendo que otro hilo pueda tomar el control y al cabo de X tiempo, trata de continuar con la ejecución del hilo en cuanto sea posible".

Es decir, que al margen de la precisión 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 función interna en la librería libogc, que no está incluida en ningún fichero de cabecera (de ahí que la denomine interna) llamada udelay, que podeis declararla así:

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


Esta función pierde el tiempo de forma similar a usleep, pero no duerme el hilo. La importancia de este matiz la podréis comprender cuando hable de la programación multithread pero os basta con saber que si un hilo no suspende (o duerme) su ejecución, no puede ser interrumpido por otro hilo salvo que ese nuevo hilo tenga una prioridad mayor. Luego queda patente que usleep() permite la ejecución de hilos que estén a la espera de inferior o igual prioridad, mientras que udelay() no.

Medida Relativa del tiempo

Hay una función llamada gettick() a nivel interno que nos devuelve un contador de 32 bits de tiempo. Esta función no está presente en ningún fichero de cabecera (si os interesa curiosear el fuente de las funciones, está en libogc/timesupp.c) por lo que tendréis 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 conversión:

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:

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

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 función que hace más 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 precisión 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 función:

int clock_gettime(struct timespec *tp);


Esta función 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 función nunca, pero de entrada, le falta un parámetro con respecto a la definición de la función en time.h XD y supongo que equivale a clock_gettime(CLOCK_REALTIME, &time); en otros sistemas.

En fin, lo dejo dicho para que vosotros lo sepáis: eso si, éste tipo de funciones no están 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 precisión del temporizador, claro)

Por ello las alarmas son especialmente útiles ya que te permiten programar un tiempo que una vez transcurrido, produce una interrupción que nos lleva a una callback de tratamiento donde teniendo cuidado, eso sí (recordemos que estamos en tiempo de interrupción), 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 creación 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 función 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); // periódicamente


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 periódica 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:

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:

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 Periódica:

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 número exacto pero obviamente,lo puedes comprobar creando alarmas hasta que se produzca un error XD

La función:

s32 SYS_CancelAlarm(syswd_t thealarm);


La puedes usar para cancelar alarmas ya programadas y en curso, mientras que la función:

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 ;)


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

Programación Multihilo

En Wii la programación multihilo consiste en la posibilidad de interrumpir la ejecución 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 sólo puede ocuparse de uno de ellos en cada momento, por lo que no se trata de ejecutar varias tareas de forma simultánea, 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 tenéis 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 librería o que comparten recursos.

Por ejemplo, si desde un hilo se leen los pads con WPAD_ScanPads() y desde otro se comprueban los datos leídos 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 erróneos y en otros casos, se pueden obtener una serie de paradojas.

Si tu función utiliza únicamente registros del procesador y variables locales o globales que no se modifican, no tendrás 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 función 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 deberías protegerla mediante uso de semáforos (que veremos luego).

La creación de un hilo

La función 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: Función de entrada del hilo

-arg: Puntero con el argumento que recibirá la función de entrada del hilo (para asignar datos privados). Puede ser NULL

-stackbase: Puntero con la dirección base de la pila (alineada a 32). Si NULL, se asigna de forma interna.

-stack_size: Tamaño 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 XD).

La función devuelve 0 si no hubo problema alguno.

La función de entrada del hilo

La función de entrada sería 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
}


Evidentemente, sería un hilo muy fugaz y su utilidad sería bastante baja. La cosa mejoraría añadiéndole un bucle de control 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
}


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 haría 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 asignándola a un valor no cero, rompe el bucle y eso haría 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 daría lugar a un cuelgue de la máquina cuando pretendas volver al HBC por ejemplo. Así pues, antes de volver deberías asignar exit_thread_main=1; y asegurarte que el hilo está 'despierto' para que se produzca ese break.


Controlando la ejecución del hilo

En la función thread_main de antes, hemos observado un método que supone que el hilo va a su bola desactivándose por espacio de 20 ms y luego volviéndose 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


Después creamos el hilo y en la función 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  :D

    LWP_ThreadSleep(thread_queue);  // duerme el hilo usando una cola para poder despertarlo

    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 podéis usar ésta función:

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 método de las colas para actualizar los gráficos cada 20 ms mediante una alarma, los reproductores de audio esperan la activación del hilo de ésta forma para rellenar con samples los buffers, etc.

La técnica de usleep() que usábamos al principio, la uso en Guitarfun también, 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 pérdida 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 función 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 finalización

LWP_CloseQueue(thread_queue); // cerramos la cola



Si algo fallara aquí, la aplicación se colgaría, así que conviene asegurarse de que ninguna otra cosa bloqueará la salida del hilo.

No he preparado ejemplos con uso de múltiples hilos, pero mi juego Guitarfun o mi aplicación Wiireader, muestran varios ejemplos que podéis estudiar.

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

Cambiando la prioridad

Cómo habéis visto, en Wii los hilos obedecen a temas prioridad a la hora de activarse, siendo el hilo de mayor prioridad el más abusón de la clase, no permitiendo la ejecución 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 bajársela, 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 programación multihilo y parece ser que a los números flotantes. Dicho problema se corrigió parcialmente, pero no del todo, así que puede ser conveniente proteger ciertas secciones de ésta manera.

La función 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). También 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 función equivalente para conocer la prioridad

Proteger secciones críticas

Ya hemos dicho que cambiando la prioridad, podríamos proteger una determinada sección 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í:

u32 stat;

stat=IRQ_Disable(); // deshabilita

/* hacer algo que no lleve mucho tiempo, ni requiera interrupciones habilitadas */

IRQ_Restore(stat); // restaura



El uso de semáforos

Los semáforos se utilizan para regular el paso de los hilos hacia una función o funciones que solo permiten el acceso a un hilo cada vez, debido a una serie de características propias de esa función 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 "servírtelo", 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 XD ).

Así pues, se necesita un mecanismo que haga que si dos o mas hilos acceden a una función 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 semáforos (un ejemplo lo tenéis en LIBFAT, que bloquea el acceso a todas sus funciones mediante semáforos)

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

mutex_t mi_semaforo=LWP_MUTEX_NULL;


La función para inicializarlo es ésta:

s32 LWP_MutexInit(mutex_t *mutex,boolean use_recursive);


Lo inicializamos así:

LWP_MutexInit(&mi_semaforo, false);


El semáforo 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 XD )

LWP_MutexDestroy(mi_semaforo);



Bien, ya tenemos nuestro semáforo creado ¿Y ahora cómo se usa en la práctica?

Pues para eso tenemos éstas dos funciones:

s32 LWP_MutexLock(mutex_t mutex); // bloquea mediante el semáforo

s32 LWP_MutexUnlock(mutex_t mutex); // desbloquea el semáforo


Pongamos 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 semáforo, 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 función

return ret;
}



Como se puede apreciar, es un mecanismo muy sencillo: aplicándolo a todas las funciones de una librería que sean accesibles, podemos evitar el acceso de otros hilos a una librería mientras esté en uso por parte de uno de ellos, pero esto no se traduce en un error de forma que la librería diga algo así "lo siento, pero estoy en uso: vuelva usted mas tarde", si no que el semáforo lo convierte en "en esté momento la función está ocupada, aguarde en la sala de espera hasta que el hilo pepito abandone la función".

Pero claro, es responsabilidad del hilo "pepito" informar con MutexUnlock de que otros hilos pueden acceder a ella. y también es importante que el hilo "pepito" evite volver a pasar por MutexLock, puesto que ya no dispone de tarjeta de visita y será bloqueado también.

Y ésto es todo lo que tenéis que saber sobre los semáforos, los hilos y el copón de vino para trabajar con hilos. El resto es cosa vuestra.

FIN
Increíble. Mil gracias por el aporte.
Hermes genial!!! [inlove]

Yo trabajo como programador senior en Java y PHP y tengo bastantes conocimientos de C, pero siempre me ha dado perrería aventurarme al mundo de wii... ahora con el megamanual que te has currado y mi tiempo libre, no tengo excusa.

1000 GRACIAS!!!!


Por cierto... esto se merece una CHINCHETA enorme... por mi parte ya tengo este post en mis marcadores [oki]
Uff, ha costado subirlo XD

Me pasaba de los 100 Mil caracteres en el primer post [+risas] (en entuwii.net, tenía privilegios y podía doble-postear, weno, exactamente, esto eran 14 post seguidos XD )


Tambien os puede interesar ésto:
Imagen

[Wii] Graficos 3D para Wii (PDF + Ejemplos)

Pero aviso que estoy desactualizado y hay cosas que lo mismo no recuerdo ahora (en su momento me metí mucho en la materia, pero el tiempo no pasa en balde y las cosas se olvidan XD)
Como siempre un muy buen trabajo. (y que por suerte no se ha perdido)

¡Venga esa xinxeta!

Saludos
Si señor, algo así no se podía perder en el olvido. [plas] [plas]
Espectacular [plas]

De todos modos ahora mismo entuwii "vuelve a la vida" (solo para lectura), que también se agradece.

Se puede bajar en PDF o similar completo al 100%? Imagino que tu tendrás algo asi no?

Perdona si estaba ya posteado el PDF, no me lo he leido todo aún :)
Hermes, te he enviado un PM.. Si fueras tan amable de leerlo, te lo agradeceria..
Por cierto, pedazo de tutorial. Muchisimas gracias!!
Absolutamente increíble, magnifico.

Te garantizo que se va a convertir en mi biblia. Queria empezar con la programación en Wii, pero al no tener ni zorra me echaba para atras tirar de miles de preguntas en los foros. Con esto todo queda hiper claro y concentrado en un solo lugar.

GRACIAS!!!
Este Hermes no para de sorprenderme.
+100000
wow muchas gracias tio, pense que nunca mas lo tendria a mano ^^
Y la primera frase esa que dice que no importa si sabes mucho o poco, ¿en poco se incluye nada? [+risas]

Ya que tengo el verano por delante quiero haceralgo para entretenerme, pero no se si podre, que espero que si
Esto si que es un regalo de vacaiones.
Gracias Maquina.
Gracias por ponerlo, lo echaba de menos ^^
Gracias Hermes por devolvernos tu confianza, Intetaremos sacarle fruto...

saludos...
5 estrellas. Si no anduviese liado con el PFC, hacia algo
WOW Hermes muchas gracias, tremendo tutorial para aprender... ya te voy a estar molestando con preguntas xDDDDDDD
Buen aporte!
Gracias por el tutorial!
Cosas como estas hacen que más y más gente nos interesemos
en estos asuntos y halla una mejor producción a nivel mundial.
Para Hermes:

- crees que la scene de wii esta preparada para lo que podian hacer los hack-scenes de amiga-pc antaño Intros & DemoScene?....
- se podria organizar algo con la informacion que tenemos hasta el momento en programacion wii?...

Aqui enlace de este verano 2009

http://www.assembly.org/summer09/
Bueno, ya que este post ha salido a la luz, que por cierto me parece genial que en Scene se hable de mas cosas que de CIOS y Reproductores, quería comentar una pequeña duda que tengo, a ver si alguien tuviese la respuesta.

Durante mi programación para Wii, me he hecho un lio tremendisimo con el tema de los tamaños de los buffers de video.
¿Que diferencia hay entre el buffer embedido de video y el buffer externo?¿Y si quiero dar soporte a 16:9 como lo hago?

En fin, si alguien sabe la respuesta pues genial :)
La diferencia te la dice el nombre. El bufer empotrau esta dentro de la grafica y es RAPIDO aunque justisimo. El otro es uno cocinado en la RAM.
Lupi escribió:Bueno, ya que este post ha salido a la luz, que por cierto me parece genial que en Scene se hable de mas cosas que de CIOS y Reproductores, quería comentar una pequeña duda que tengo, a ver si alguien tuviese la respuesta.

Si, y eso que sacamos el foro de softmods de aqui, sino... aun así sigo viendo que los cios y demás deberían estar fuera de scene.

De hecho creo que este tema se merece más una chincheta que "Baneos por hacer trampas en Mario Kart"...
Quien apoya la moción? [oki] [oki] [oki] [oki]

Durante mi programación para Wii, me he hecho un lio tremendisimo con el tema de los tamaños de los buffers de video.
¿Que diferencia hay entre el buffer embedido de video y el buffer externo?

Básicamente a esto te ha respondido Vrsquid, pero te añado un apunte más recuerda que el tamaño de tus texturas no debe superar los 1024 pixels y que deben ser tamaño par.

Yo te recomiendo usar pngs de 24bits para tu artwork y cargarlos con el upng o la libreria que uses :)

¿Y si quiero dar soporte a 16:9 como lo hago?

En principio si usas una libreria como grrlib el soporte viene dado automatico, es decir si el usuario activa la opción tu programa aparecerá en formato panorámico. Si lo quieres hacer tu a mano hay una función que te devuelve el formato de la pantalla: CONF_GetAspectRatio()

En Wiituka parchee grrlib para que soportara correctamente los 16:9 a pantalla completa (sin bordes laterales).
Aquí tienes el enlace al code: http://code.google.com/p/wiituka/source ... RLIB.c#542

Un Saludo granaino! (XD)
Pd: Gracias hermes por el rescate :)
D_Skywalk escribió:De hecho creo que este tema se merece más una chincheta que "Baneos por hacer trampas en Mario Kart"...
Quien apoya la moción? [oki] [oki] [oki] [oki]


Bueno, creo que eso corresponde a los mods hacerlo y si no lo han hecho hasta ahora, no creo que vayan a cambiar de opinión.

A mí me da exactamente igual: lo he colgado aquí para que pudierais hacer un quote y pudierais extraer fácilmente el bbcode (cosa que no se puede hacer con el hilo cerrado en entuwii.net) y por que al fin y al cabo, es algo que debería haber hecho hace tiempo (solo que colgué el teclado poco después...).

Pero vamos, no es algo que me preocupe si se le pone una chincheta o no a un hilo mío a estas alturas del partido (total, no me toca a mí decidirlo y no soy más que un invitado).

Saludos
(mensaje borrado)
De hecho creo que este tema se merece más una chincheta que "Baneos por hacer trampas en Mario Kart"...
Quien apoya la moción? [oki] [oki] [oki] [oki]

+1.
El tema de las trampas en mario kart creo que esta algo "pasado".
Yo no voy a intentar programar por que no tengo ni idea y todo esto se me hace un mundo, pero creo que este hilo es mas importante o ayuda mas que el de mario kart por ejemplo.
Un saludo.
Bueno, mi intención tampoco era crear ningún conflicto, solo que véia más interesante para un foro de scene, un hilo de programación como este que el viejo tema de las trampas del Mario Kart.

Un Saludo compas :)
Mil gracias, grandisimo aporte y trabajo.

Un saludo [oki]
Bueno en primer lugar muchisimas gracias HERMES por este pedazo de tutorial.
Aun no he comenzado a programar nada para Wii, pero tengo un proyecto por ahi que me recome la cabeza.
Antes de ponerme mas en serio quisiera saber si existe alguna libreria para utilizar los microfonos usb de sing-it (creo que son logitech) en nuestras aplicaciones. Si no existe ¿que necesito para poder utilizar los microfono? crear un driver? no se supone que dicho driver ya esta incluido en el ISO?
SuperBerny escribió:Bueno en primer lugar muchisimas gracias HERMES por este pedazo de tutorial.
Aun no he comenzado a programar nada para Wii, pero tengo un proyecto por ahi que me recome la cabeza.
Antes de ponerme mas en serio quisiera saber si existe alguna libreria para utilizar los microfonos usb de sing-it (creo que son logitech) en nuestras aplicaciones. Si no existe ¿que necesito para poder utilizar los microfono? crear un driver? no se supone que dicho driver ya esta incluido en el ISO?


No creo que exista ninguna librería para usar los microfonos que mencionas, pues lo que requiere es un driver. El driver USB está incluido en el IOS, pero el driver de micrófono puede que esté incluido en ciertos IOS (y en otros no), pero de ahí a que sepas como se enlaza ese driver con tu aplicación va un salto muy grande (habría que tener los SDK que hagan uso de microfonos y reversarlos)

Por lo general, lo mejor sería tratar con el driver USB directamente y hacer el manejo del micrófono desde el PPC, pero claro ¿sabes tu como hacer un driver para micrófono que soporte los dispositivos Logitech? Porque yo en principio, no tengo ni la menor idea.

----------------------------------------------------------------------------------------------------------------------------

Por otro lado, mola ver el hilo del curso de programación en la tercera página y tener a gente preguntando sobre un curso o tutorial que les ayude a empezar a programar en la Wii, por que éste hilo está en un sitio idóneo para ser localizado [+risas].

Total, si como dijo alguno, debe ser más importante en un foro de Scene un hilo relacionado con el baneo de los que usan cheats en el Mario Kart, que un curso de programación tan completo como éste [poraki] [qmparto] [qmparto] [qmparto]
Le he hechado un vistazo... y muy curradoestoy por imprimirmelo todo y hecharle un buen vistazo cuando me vaya de vacaciones.
Muy currado la verdad

¿porque no está en un hilo oficial? lo veo más interesante como has dicho a que te baneen por usar trucos o pedir prestado el twilight princess (porque ya se puede instalar homebrew con bannerbomb).
HILO OFICIAL YA

Por cierto los que quieran programar algo apra wii y no se les ocurra que podrian ahcer... pueden hechar un ojo al hilo de aplicaciones que nos gustaria en wii, seguro qeu alguna de las peticiones es factible
dantemugiwara escribió:¿porque no está en un hilo oficial? lo veo más interesante como has dicho a que te baneen por usar trucos o pedir prestado el twilight princess (porque ya se puede instalar homebrew con bannerbomb).
HILO OFICIAL YA


A mi no me preguntes [+risas]. Es lo que tiene EOL que funciona al revés del resto de las páginas y aquí lo que se produce dentro, es completamente ignorado (además, tengo la sospecha de que si es de mi procedencia, más [+risas])

Es triste, pero es así.
Pues es una lastima que un hilo tan currado caiga a la tercera pagina.

Yo no tengo ni pajorera de programación pero me lo voy a empollar e intentaré en un futuro tocar aplicaciones de wii, supongo que no lograré anda pero probaré.

Hermes me recomiendas algo para empezar... queiro decir primero se aprende a gatear y luego a andar... en esto que seria gatear?
Gracias Hermes por la respuesta.

Hermes escribió:Por lo general, lo mejor sería tratar con el driver USB directamente y hacer el manejo del micrófono desde el PPC


Perdona mi ignorancia pero ¿que es PPC?

Hermes escribió:¿sabes tu como hacer un driver para micrófono que soporte los dispositivos Logitech?

Pues evidentemente no. Pero he visto usuarios de linux hacer un driver para un lanza cohetes...(de juguete) Asi que continuare buscando.

Por cierto, si el hilo no es ya fijo tal vez la solucion sea irlo subiendo cada pocos dias.
dantemugiwara escribió:Pues es una lastima que un hilo tan currado caiga a la tercera pagina.

Yo no tengo ni pajorera de programación pero me lo voy a empollar e intentaré en un futuro tocar aplicaciones de wii, supongo que no lograré anda pero probaré.

Hermes me recomiendas algo para empezar... queiro decir primero se aprende a gatear y luego a andar... en esto que seria gatear?


Hombre, yo empezaría primero aprendiendo a trabajar en C, los tipos de datos, estructuras, punteros, el if, do/while/, switch/case, como se usan las operaciones lógicas, etc.

Luego miraría algoritmos y cosas así, por ejemplo ¿como ordenar de mayor a menor un conjunto de elementos? ¿como se traza una trayectoria punto a punto? (por ejemplo, para pintar una recta/curva o mover un objeto a lo largo de ella) ¿como detectar si dos bitmaps están colisionando? ¿o en que punto colisionarían siguiendo sus trayectorias y velocidades? ¿como hacer que un objeto se aproxime a otro buscando la ruta mas corta, ya sea siguiendo un camino directo o en un laberinto?

Algunas las podrías estudiar usando el modo consola en el PC, compilando mismamente con gcc y aunque puedes encontrar ejemplo hechos por otra gente, tambien puedes pensar por tu cuenta la forma de hacerlo.

Perdona mi ignorancia pero ¿que es PPC?


El procesador principal de la Wii. Tambien tiene un ARM al que nos referimos como Starlet, que es donde se alojan los drivers de los IOS.

Hasta hace relativamente poco no había manera de poder situar un driver en el Starlet desde el PPC, pero hace un tiempo yo añadí esa posibilidad en los cIOS 202/222/223 al crear un módulo especial que habilita un espacio de 512KB de RAM en el Starlet para poder subir módulos y proporcionar herramientas para la creación de hilos, escribir y leer en el memoria del Starlet desde el PPC, y proporcionar la forma de utilizar una serie de syscalls para gestionar timers, etc. (el Starle se encarga de la seguridad de sistema, asi que es un poco pijotero para ciertas cosas)

Pero de todas formas, lo lógico sería tratar de hacer un driver en el PPC y luego tratar de enlazarlo en el Starlet mediante los cIOS que menciono, dentro de lo posible, pero claro, si tu idea es utilizar un driver procedente de Linux, te va a costar lo suyo adaptarlo.
Por votacion popular chincheta.
Buen trabajo Hermes
Gran aporte Hermes, enhorabuena por la chincheta.
Llevo con ganas de coger esto de la programación en Wii desde que vi algo en EnTuWii.Net.
Espero que sea más facil de lo que parece. [ginyo]
Acabo de empezar a leer el tutorial y quise probar lo de compilar con el bat de los ejemplos de hermes pero no me compila, me sale lo de la imagen del adjunto. Creo que tal vez se instalo mal devkitpro porque tuve que hacer mucho intentos para instalarlo porque en la parte de la descarga muchas veces no quizo, y no tengo ese directorio que dice ay de devkitppc.

Adjuntos

Hermes tienes alguna copia en PDF o similar? Es para poder imprimirlo y que quede bien.
Gracias por el curso y felicidades por la chincheta.
comepiedras escribió:Hermes tienes alguna copia en PDF o similar? Es para poder imprimirlo y que quede bien.
Gracias por el curso y felicidades por la chincheta.


No, no tengo ninguna copia en PDF, porque éste curso se hizo de forma directa en un hilo donde fuí escribiendo cada apartado durante días (ventajas de poder cerrar hilos y doblepostear [+risas]). A mi este estilo de trabajo me gusta bastante, el de abrir un hilo y volcar el contenido de mi cerebro, corrigiendo y ampliando si es necesario. No fue el mismo caso de la documentación sobre las GX que por cierto, fue publicada aquí tambien en EOL y está perdida en otro abismo XD

Sobre el tema de la chincheta, no creo que haya que felicitarme a mí, si no a vosotros: Yo no pinto nada y al publicar esto aquí, simplemente cumplo una vieja promesa que tenía apartada desde hace unos meses, cuando por motivos personales dejé la scene y no programé ni una sola línea durante meses. El trato que se le de, ya no depende de mí, pero vamos, que estoy acostumbrado a que este tipo de cosas se pierdan en los abismos XD
Ok, gracias por responder.
Ahora no creo que pueda pero si algún dia lo pongo para imprimir y queda en condiciones si quieres te paso el PDF por si quieres colgarlo o tenerlo :)
hermes, no se si recordaras aquel juego de amiga que luego paso a otras plataformas llamado IK trataba de tres luchadores de artes marciales (white ,red ,blue)
ahora tengo los fuentes de pc y me gustaria compilarlo para wii...
Hola,kiero aprender sobre programacion,he descargado todas las herramientas,pero antes de empezar,,,,¿podrias mostrarme alguna actividad muy,pero muy facil que pueda hacer como para darme cuenta de lo ke puedo hacer con toda esta data?...
Ahun sin saber nada sobre programcion,puedo darme cuenta de lo valiosa ke es toda esta info.
Mil gracias!!....estuve buscando hace mucho y no podia encontrar nada ke pudiera enseñarme algo sobre programacion....gracias
zamborr escribió:Hola,kiero aprender sobre programacion,he descargado todas las herramientas,pero antes de empezar,,,,¿podrias mostrarme alguna actividad muy,pero muy facil que pueda hacer como para darme cuenta de lo ke puedo hacer con toda esta data?...
Ahun sin saber nada sobre programcion,puedo darme cuenta de lo valiosa ke es toda esta info.
Mil gracias!!....estuve buscando hace mucho y no podia encontrar nada ke pudiera enseñarme algo sobre programacion....gracias

Solo para aclararte que en este tutorial se hace de cuenta que el usuario ya sabe programar en C para PC, pero no conoce bien como es el cuento para Wii.
jajaj,,,gracias,,,por descalificarme de entrada!!.......tu respuesta me hace tomar mas impulso para alcanzar mis objetivos...de todas formas me hubiese gustado que me responda alguien que sepa de programacion....jjajaa...o ke lo haga hermes....
Bueno,voy a seguir investigando..... [sonrisa]
Pues yo se de programación jeje xD, pero de PC, no de Wii..
Por cierto, de ninguna manera mi intención era descalificarte, pero como bien sabes, todo lleva un orden, y por eso te digo que primero aprende a programar para estar listo y seguir este tutorial..
ok...gracias por tus concejos!!
Ya aprendi el primer paso!!!.....
Nunca es tarde para aprender.....yo tengo 73 años.. [ayay]
zamborr escribió:ok...gracias por tus concejos!!
Ya aprendi el primer paso!!!.....
Nunca es tarde para aprender.....yo tengo 73 años.. [ayay]

Offtopic: En serio tienes 73 años? Es raro ver gente de tal edad por estos foros jeje. Nos alegra tenerte acá..
---
Y como asi? Ya aprendiste a programar??? Jaja, eso si no te lo creoo, para programar tardas por lo menos 2 meses jaja!!
jajaj...caiste...jajaj...tu sabes programar,pero yo se engañar...jajaja...tengo 30,pero espero llegar a los 73 años siendo un verdadero hacker.
Bueno,no kiero desvirtuar mas este tema,pero de verdad me muero por aprender lo minimo,podrias tirarme algo como para darme una idea de como programar??..supongo ke alguien habra estudiado y puede explicarme de como se empieza a programar....o ahunque sea una definicion...jaja
No mueran con lo ke saben,sino se van a morir sin gloria...
141 respuestas
1, 2, 3