Ejemplo Hello World
Vamos a provocar un poco de inquietud en ti para animarte a programar algo para la consola Neo Geo Pocket. En este ejemplo, te enseñaremos a realizar un Hello World y ver como se muestra posteriormente.
Antes de nada, no se va a explicar como aprender a programar, eso ya es una tarea personal, pero si te vas a meter con esto, necesitas saber como se programa en lenguaje C ( no nos sirve C++ o C# )
REQUISITOS
Todo lo que necesitas lo puedes encontrar clonando/descargando la plantilla creada por Adrián Melián en su GitHub, la cual tiene todas las herramientas necesarias. También puedes clonarla a través de este comando de Git
https://github.com/ameliandev/ngpc-project-template.git
Sigue el proceso de configuración y ejecución detallado en el README del proyecto.
Ficheros clave
carthdr.h
makefile
main.c
PASO 1
El fichero carthdr.h, es el fichero que creará una estructura de cabecera para el cartucho, es decir, este archivo lo que hace es crear una entrada en la rom para que la Pocket lo identifique y pueda leerlo. Al editarlo veremos el código siguiente:
const char Licensed[28] = " LICENSED BY SNK CORPORATION";
const FuncPtr ptr = main;
const short CartID = 0;const short System = 0x1000;
const char CartTitle[12] = "FRAMEWORK ";
const long Reserved[4] = {0,0,0,0};
De aquí, lo que nos interesa es la siguiente linea:
const char CartTitle[12] = "FRAMEWORK ";
Esta línea, lo que hace es indicar el nombre del juego. Podeis darle lo que querais pero no puede pasar de 12 caractéres (const char CartTitle[12]). Por ejemplo, podemos darle como nombre «RTYPE3 «, nótese los espacios para completar 12 carácteres.
Con esto ya tenemos nuestro fichero de cabecera creado, aho podemos guardarlo y ir al siguiente paso.
PASO 2
Ahora abrimos el fichero makefile. Este fichero se encargará de indicarle al compilador, que ficheros son los que tiene que seleccionar para compilar, ¿es simple no? Al abrirlo veremos lo siguiente:
.SUFFIXES: .c .asm .rel .abs
NAME = main
OBJS = \
main.rel \
library.rel \
core/rom/flash.rel \
OUTPUT_DIR = bin
$(NAME).ngp: makefile ngpc.lcf $(OBJS)
tulink -la -o $(NAME).abs ngpc.lcf system.lib $(OBJS)
tuconv -Fs24 $(NAME).abs
s242ngp $(NAME).s24
.c.rel:
cc900 -c -O3 $< -o $@
clean:
@rm -f $(OUTPUT_DIR)/*.abs
@rm -f $(OUTPUT_DIR)/*.map
@rm -f $(OUTPUT_DIR)/*.s24
@rm -f $(OUTPUT_DIR)/*.ngc
@rm -f $(OUTPUT_DIR)/*.ngp
@rm -f $(OUTPUT_DIR)/*.map
@rm -rf *.rel
move_files:
@if [ ! -d "$(OUTPUT_DIR)" ]; then mkdir $(OUTPUT_DIR); fi
@if [ -f "$(NAME).ngp" ]; then mv $(NAME).ngp $(OUTPUT_DIR)/$(NAME).ngc; fi
@if [ -f "$(NAME).abs" ]; then mv $(NAME).abs $(OUTPUT_DIR)/$(NAME).abs; fi
@if [ -f "$(NAME).rel" ]; then mv $(NAME).rel $(OUTPUT_DIR)/$(NAME).rel; fi
@if [ -f "$(NAME).s24" ]; then mv $(NAME).s24 $(OUTPUT_DIR)/$(NAME).s24; fi
@if [ -f "$(NAME).map" ]; then mv $(NAME).map $(OUTPUT_DIR)/$(NAME).map; fi
En este fichero, fijemonos en lo siguiente:
NAME = main => Aqui vamos a cambiar esto por el nombre de nuestro juego (no hace falta que sea de 12 caracteres como en el carthdr.h, simplemente podríamos RTYPE4, ejemplo:
NAME = RTYPE3
Lo siguiente:
OBJS =
main.rel
library.rel
Esto lo que va a hacer, es declarar un Array, que contendrá estos dos ficheros, para luego trabajar con ellos. Realmente al empezar por primera vez solo va a existir el library.rel, el main.rel aún no lo tenemos pero nos es irrelevante.
Aquí ahora mismo, no hacemos nada, pero, si en un futuro nuestro fichero de código lo renombramos y ponemos que se llama rtype4.c, aquí habrá que ponerse lo siguiente:
OBJS =
rtype4.rel
library.rel
Guardamos y seguimos
PASO 3
Ahora ya lo que queda es meter el código y empezar a programar, así que editamos el fichero main.c.
Se permite desarrollar para NGPC en C o en Ensamblador. En este ejemplo trabajaremos con C por preferencias personales. La estructura de un fichero de un juego suele tener la siguiente estructura básica:
#if 1 // header files
#include "ngpc.h" // required
#include "carthdr.h" // required
#include "library.h" // NGPC routines
#include "core/rom/flash.h"
// #include <stdlib.h> // std C routines
// #include <stdio.h>
#endif
#define P1_PAL1 0
#define MEM_POS 4
extern u32 gamedata[256];
void main()
{
InitNGPC();
SetBackgroundColour(RGB(68, 68, 68));
SysSetSystemFont();
InitFlash();
SetPalette(SCR_1_PLANE, P1_PAL1, 4, RGB(15, 15, 15), RGB(15, 15, 15), RGB(15, 15, 15));
/* #region No Escape! */
while (1)
{
WaitVsync();
}
/* endregion */
}
Explicación
Todos las lineas que pone #include, es porque se importan otros ficheros a este con el que trabajamos para poder usarlos. Estos ficheros puden ser, ficheros de sprites, de sonido, de tiles, una libreria de código, etc. Por ejemplo tenemos la línea siguiente:
#include "ngpc.h"
Esta linea, incluye en nuestro código el fichero ngpc.h, que tiene unas variables declaradas para poder usar directamente desde este archivo (puedes abrirlo y echarle un ojo) y así con el esto.
Otro por ejemplo es el #include «carthdr.h» que contiene la configurtación de las cabeceras del cartucho
Lo que hace que nuestra rom se inicie, es el método main() el cual es el que hace que nuestra rom haga algo. Es importante indicar, que NUNCA NUNCA, este proceso puede terminar, explico…
La rutina de ejecución del código es secuencial, si en el método main() no existe, dará error, y, si dicho método se finaliza el juego se cerrará al completo terminando su ejecución. Es por ello que el método main() debe incluir algo que siempre haga que este funcionando, para ello, como en el ejemplo, añadimos un bucle while(1). Esto hará que mientras (while en ingles) el valor sea 1, este bucle estará funcionando continuamente y no cerrará la instrucción Main() a no ser que la cerremos nosotros con otros proceso pero eso ya es otra cosa.
Otro método que debe estar cargado si o si en el Main() es el InitNGPC(); Este método inicializa la actividad con la NGPC y es necesario que esté, si no no va a hacer nada.
Entonces, hagamos nuestro fichero, pongamos lo siguiente:
#if 1 // header files
#include "ngpc.h" // required
#include "carthdr.h" // required
#include "library.h" // NGPC routines
#include "core/rom/flash.h"
// #include <stdlib.h> // std C routines
// #include <stdio.h>
#endif
#define P1_PAL1 0
#define MEM_POS 4
extern u32 gamedata[256];
void main()
{
InitNGPC();
SetBackgroundColour(RGB(68, 68, 68));
SysSetSystemFont();
InitFlash();
SetPalette(SCR_1_PLANE, P1_PAL1, 4, RGB(15, 15, 15), RGB(15, 15, 15), RGB(15, 15, 15));
/* #region No Escape! */
while (1)
{
PrintString(SCR_1_PLANE, 1, 1, 1, "Hello World");
WaitVsync();
}
/* endregion */
}
Guardamos nuestro fichero. Ahora mismo no hace nada, es simplemente un archivo con unas lineas sin funcionamiento visual. Vamos a añadir un Hello World. Para ellos, vamos a usar el método PrintString()
Este método es muy simple, inserta en el plano 1 (la NGPC tiene tres planos, SCR_1_PLANE, SCR_2_PLANE para backgrounds y el SPRITE_PLANE para sprites) un texto, y luego lo muestra en pantalla, así de simple.
¿Cómo he encontrado la funciñon PrintString? Fácil, si quieres investigar, fíjate en la cabecera del fichero main.c. En dicha cabecera hay un #include de un fichero llamado library.h. que a su vez, este fichero internamente hace un include del fichero library.c. Este fichero, pertenece al framework v4 de Ivan Mackintosh y contiene todas las rutinas que podemos utilizar para trabajar con la NGPC de forma cómoda.
Edita el fichero library.c ubicado en la reíz del proyecto y busca el método PrintString(). Su código es el siguiente
void PrintString(u8 Plane, u8 Palette, u8 XPos, u8 YPos, const char *theString)
{
//////////////////////////////////////////////////////////////////////////////
// PrintString
// Displays a string on the screen at the specified location
// Inputs:
// Plane - Scroll Plane to clear SCR_1_PLANE or SCR_2_PLANE
// PalleteNo - 0-15 the palette number to set
// XPos - X Position (0 to 19)
// YPos - Y Position (0 to 18)
// theString - The string to be displayed
//////////////////////////////////////////////////////////////////////////////
u16 *Screen;
switch (Plane)
{
case SCR_1_PLANE:
Screen = SCROLL_PLANE_1;
break;
case SCR_2_PLANE:
Screen = SCROLL_PLANE_2;
break;
default:
return;
}
while (*theString)
{
u16 Offset = ((u16)YPos * 32) + XPos;
u16 Value = *theString + ((u16)Palette << 9);
Screen[Offset] = Value;
theString++;
XPos++;
}
}
No voy a explicar todo su código, ya dije que no voy a enseñar a programar, pero fíjate en sus parámetros:
void PrintString(u8 Plane, u8 Palette, u8 XPos, u8 YPos, const char * theString)
- void = esto significa que el método no devuelve nada
- PrintString = nombre del método.
- u8 Plane = Variable de tipo u8 (es un entero por decirlo así que permite valores desde 0 a 255) Este parámetro, hace referencia a el Nº de Plano que vamos a usar.
- u8 Palete = Como el anterior, pero este indica el nº de paleta que vamos a usar.
- u8 Xpos = Posición en pantalla en eje X.
- u8 YPos = Posición en pantalla en eje Y.
- const cahr * theString = Variable de referencia (*) de tipo carácter, que será el texto que se va a mostrar.
Ahora explicado esto lo usaremos de la siguiente manera:
PrintString(SCR_1_PLANE, 1, 1, 1, "Hello World");
Es simple, indicamos en el parámetro 1, el plano. Luego, indicamos en nº de paleta, como no tenemos ninguna declarada y es la primera llamada que hacemos, ponemos la 1, aunque se podría poner cualqueir otro número. Luego posición X y Y (1,1) y al final, entre comillas, el texto ¿Es fácil no?
Ahora, para incluir esto en nuestro código, pues, dentro del método Main(), y dentro del While (recordeos para que sirvía) añadimos la línea como la he indicado.
void main(void) { // Inicio de ejcución del juego.
InitNGPC();
while (1) {
PrintString(SCR_1_PLANE, 1, 1, 1, "Hello World");
}
} // Fin de rutina Main
Ya tenemos nuestro programa hecho. La explicación de esto es:
A) inicializamos el método Main()
B) Inicializamos el sistema con InitNGPC();
C) Creamos una sentencia While(1) {} para evitar que se salga el programa del método Main()
D) Añadimos la linea para mostrar el texto; PrintString(SCR_1_PLANE, 1, 1, 1, «Hello World»);
Listo guardamos el fichero y ya tenemos el programa hecho. Ahora solo queda compilarlo para obtener la ROM.
PASO 4
Para compilar debemos abrir una consola/terminal/powershell. Accedemos al directorio donde tenemos el código y ejecutar el comando make o de forma más cómoda ya que incluye varias configuraciones, puedes ejecutar (si has seguido los pasos de configuración del README) el fichero build.bat
Debería aparecer si no hay errores u nresultado como este
Si el proceso se ha ejecutado correctamente, podrás ver en el directorio ./bin varios ficheros, entre ellos uno con extensión .ngc el cual es nuestra nueva rom compilada y lista para probar en emulador o cartucho flash.
Ejemplo Incorrecto. Aqui he provocado adrede un error para que el compilador nos indique el error y la línea. En mi caso (en el tuyo puede ser otro), se debe a que cuando añadí la linea PrintString(SCR_1_PLANE, 1, 1, 1, «Hello World»); no le puse el punto y coma al final. Pero siempre el compilador mostrará en pantalla mensajes de Error para identifica rel fallo, acompañado de la línea de error.
Aconsejo empezar a buscar desde la primera línea indicada por el compilador.
Y esto es lo básico para desarrollar, a partir de aquí ya depende de vosotros o de quien queira progresar en esto. Me podeis preguntar todas las dudas que tengais que os intentaré ayudar.
Saludos y espero ver vuestra capturas de vuestro emulador con la ROM cargada
Espero que os haya gustado.
Últimos comentarios