L!no
GBA Developer
hey!
Hoy les traigo un tutorial sobre el desarrollo de programas de gba desde 0, para eso vamos a necesitar:
Les presento a OpenEngine-GBA, un proyecto de codigo libre que se viene cociendo desde hace bastante y permite la implementacion de un codigo mucho mas limpio, y esta escrito, hasta el dia de la fecha, 19/3/18, enteramente por mi. esta dividido en 3 partes, las cuales son:
pueden encontrarlo aquí
en poco tiempo, explicare como utilizar cada componente por separado y explicare las nuevas funciones que sean añadidas
eso es todo, hasta el proximo capitulo.
agradecimientos
a @Kaiser de Emperana por ayudarme con mis miles de dudas
Hoy les traigo un tutorial sobre el desarrollo de programas de gba desde 0, para eso vamos a necesitar:
- Cygwin
- DevkitARM
- Notepad++
Para compilar tu primer programa deberás copiar el directorio "template" dentro de C:\devkitPro\examples\gba\ a otra ubicación en tu disco duro. Es conveniente que esa ubicación sea cercana a la raíz de tu disco duro y que los nombres de directorio no tengan espacios o caracteres especiales. Un ejemplo apropiado sería: d:\gba\.
La carpeta template contiene lo siguiente:
+ Carpeta source. Contiene el código fuente de tu programa.
+ Archivo makefile. Archivo de texto que contiene las instrucciones necesarias para integrar tu ejecutable GBA.
Abre el archivo template.c en tu Notepad++, verás el siguiente código:
La primera sección son las inclusiones de librerías para controlar el hardware del GBA. Posteriormente se inicializan las interrupciones hardware del GBA con irqInit() y en este caso se prepara la interrupción de refresco de video, pero no se usará.
Después se inicializa la pantalla del GBA para que opere como consola de texto ASCII con la función consoleDemoInit(). Al haber inicializado la pantalla ya se puede usar la función iprintf() para enviar cadenas de texto a pantalla. Esta función opera con secuencias de escape ANSI para mover el cursor o colocar texto en lugares específicos.
Por último el programa entra a un ciclo infinito.
Compilando el hello world.
Esta parte es facil, solo tienen que abrir Cygwin y escribir lo siguiente:
(recuerden usar las barras invertidas(/) o no funcionara)
y luego escribir lo siguiente:
si todo salio bien, se habra creado una rom .gba, que cuando lo habramos mostrara lo siguiente:
http://prntscr.com/h6bv8v
felicidades, compilaron su primer programa de gba!
fuentes consultadas:
devkitpro.org
La carpeta template contiene lo siguiente:
+ Carpeta source. Contiene el código fuente de tu programa.
+ Archivo makefile. Archivo de texto que contiene las instrucciones necesarias para integrar tu ejecutable GBA.
Abre el archivo template.c en tu Notepad++, verás el siguiente código:
Código:
#include <gba_console.h>
#include <gba_video.h>
#include <gba_interrupt.h>
#include <gba_systemcalls.h>
#include <gba_input.h>
#include <stdio.h>
#include <stdlib.h>
//---------------------------------------------------------------------------------
// Program entry point
//---------------------------------------------------------------------------------
int main(void) {
//---------------------------------------------------------------------------------
// the vblank interrupt must be enabled for VBlankIntrWait() to work
// since the default dispatcher handles the bios flags no vblank handler
// is required
irqInit();
irqEnable(IRQ_VBLANK);
consoleDemoInit();
// ansi escape sequence to set print co-ordinates
// /x1b[line;columnH
iprintf("\x1b[10;10HHello World!\n");
while (1) {
VBlankIntrWait();
}
}
Después se inicializa la pantalla del GBA para que opere como consola de texto ASCII con la función consoleDemoInit(). Al haber inicializado la pantalla ya se puede usar la función iprintf() para enviar cadenas de texto a pantalla. Esta función opera con secuencias de escape ANSI para mover el cursor o colocar texto en lugares específicos.
Por último el programa entra a un ciclo infinito.
Compilando el hello world.
Esta parte es facil, solo tienen que abrir Cygwin y escribir lo siguiente:
Código:
cd ruta/a/tu/proyecto
y luego escribir lo siguiente:
Código:
make
http://prntscr.com/h6bv8v
felicidades, compilaron su primer programa de gba!
fuentes consultadas:
devkitpro.org
ahora vamos a tomar el archivo template.c, y borrar su contenido para que contenga esto:
no te preocupes si no lo entiendes, es informacion muy cruda y directa, y no esta hecho para ser muy comprensible. algo mejor es esto:
es lo mismo que el otro, pero mas comprensible y mas facil de modificar y entender.
conceptos:
http://prntscr.com/h6c5yl
y esto en el caso del segundo:
http://prntscr.com/h6c6a8
fuentes consultadas:
http://www.coranac.com/tonc
Código:
//codigo de ejemplo sacado de los tutoriales de tonc
int main() {
*(unsigned int*)0x04000000 = 0x0403;
((unsigned short*)0x06000000)[120+80*240] = 0x001F;
((unsigned short*)0x06000000)[136+80*240] = 0x03E0;
((unsigned short*)0x06000000)[120+96*240] = 0x7C00;
while(1);
return 0;
}
Código:
//codigo de tonc, adaptdo por L!no
#define VRAM 0x06000000 //definimos VRAM como 0x06000000, la seccion de la memoria correspondiente a la VRAM
#define IO 0x04000000 //definimos IO como 0x04000000, la seccion de la memoria correspondiente a la IO
#define CLR_RED 0x001F //definimos los colores rojo, lima,amarillo y azul
#define CLR_LIME 0x03E0
#define CLR_YELLOW 0x03FF
#define CLR_BLUE 0x7C00
void setmode3() { //definimos la funcion setmode3
*(unsigned int*)IO = 0x0403; //iniciamos la pantalla en modo 3, y activamos el background 2
}
void writepoint(int x, int y, int color) { //definimos la funcion writepoint
((unsigned short*)VRAM)[x+y*240] = color; //escribimos un punto en la VRAM con las cordenadas y el color indicados
}
int main()
{
setmode3(); //llamamos a setmode3
writepoint(120, 80, CLR_RED); //llamamos a writepoint, indicando la posicion x, la posicion y, y el color
writepoint(136, 80, CLR_LIME);
writepoint(136, 96, CLR_YELLOW);
writepoint(120, 96, CLR_BLUE);
while(1); //bucle infinito para evitar errores extraños
return 0;
}
conceptos:
- Si nosotros escribimos 0x0403 en la direccion 0x04000000 se activa el mode 3 y el background 2
- para la posicion x e y se usa esto: [x+y*240]
http://prntscr.com/h6c5yl
y esto en el caso del segundo:
http://prntscr.com/h6c6a8
fuentes consultadas:
http://www.coranac.com/tonc
Hoy les traigo un capitulo muy importante: la deteccion de botones. La GBA tiene 10 botones, los cuales son:
¿como leemos esto?
fuentes consultadas:
http://kylehalladay.com/blog/tutorial/gba/
- A
- B
- SELECT
- START
- DERECHA
- IZQUIERDA
- ARRIBA
- ABAJO
- R
- L
¿como leemos esto?
Primero importamos las librerias:
No se preocupen por esto, son solo definciones para numeros.
Luego definimos la región de la IO correspondiente a REG_KEYINPUT:
lo definimos como volatile ya que es una región cambiante y como u16 (un tipo de numero importado en la primera linea) porque es un registro de 16 bits.
Despues definimos las mascaras:
aunque no concuerden con los numeros dichos mas arriba, es una mascara cuy bit activado concuerda con el numero de boton.
Aunque suene antiintuitivo, cuando una tecla esta siendo presionada, su registro esta en 0, mientras que cuando no lo esta, su registro esta en 1. eso quiere decir que cuando la tecla START esta siendo presionada, el registro se parece a esto:
Le aplicamos una operacion OR con la mascara general para poner los bits que no conocemos en 1.
Luego le aplicamos un AND con la mascara de start:
Eso nos da 0, si le aplicamos un NOT nos da 1: ¡La tecla fue pulsada!
ahora pongamos eso en un codigo:
Primero iniciamos una variable de 16 bits key_cur:
Despues creamos una función key_poll:
Luego metemos dentro la operacion OR:
y creamos una funcion getKeyState:
ahora solo falta llamar a estas funciones desde main. Creamos un bucle infinito:
llamamos a la funcion key_poll:
luego añadimos un if:
y le añadimos el getkeystate
Código:
#include <gba_types.h>
Luego definimos la región de la IO correspondiente a REG_KEYINPUT:
Código:
#define REG_KEYINPUT (* (volatile u16*) 0x4000130)
Despues definimos las mascaras:
Código:
#define KEY_A 0x0001
#define KEY_B 0x0002
#define KEY_SELECT 0x0004
#define KEY_START 0x0008
#define KEY_RIGHT 0x0010
#define KEY_LEFT 0x0020
#define KEY_UP 0x0040
#define KEY_DOWN 0x0080
#define KEY_R 0x0100
#define KEY_L 0x0200
Aunque suene antiintuitivo, cuando una tecla esta siendo presionada, su registro esta en 0, mientras que cuando no lo esta, su registro esta en 1. eso quiere decir que cuando la tecla START esta siendo presionada, el registro se parece a esto:
Código:
REGISTRO: ???? ??11 1111 0111
Código:
INPUT: ???? ??11 1111 0111
FLAG : 1111 1100 0000 0000
--------------------------
1111 1111 1111 0111
Código:
INPUT: 1111 1111 1111 0111
START: 0000 0000 0000 1000
--------------------------
val= 0x 0000 0000 0000 0000
ahora pongamos eso en un codigo:
Primero iniciamos una variable de 16 bits key_cur:
Código:
u16 key_cur;
Código:
inline void key_poll()
{
}
Código:
inline void key_poll()
{
key_cur = REG_KEYINPUT | KEY_MASK;
}
Código:
u32 getKeyState(u16 key_code)
{
return !(key_cur & key_code);
}
Código:
void main()
{
while(1)
{
}
}
Código:
void main()
{
while(1)
{
key_poll();
}
}
Código:
void main()
{
while(1)
{
key_poll();
if () {
//hacer algo
}
}
}
Código:
void main()
{
while(1)
{
key_poll();
if ( getKeyState(KEY_A) ) {
//tecla A pulsada
}
}
}
http://kylehalladay.com/blog/tutorial/gba/
hoy continuaremos con el tema de los botones. que pasa si queremos saber si una tecla esta siendo pulsada? ahí les va:
fuentes consultadas:
http://kylehalladay.com/blog/tutorial/gba/
si nosotros comparamos el estado anterior de la tecla con el estado actual, podemos saber varias cosas. entre ellas, saber si una tecla acaba de ser presionada o si fue soltada en es mismo momento. eso se logra con un algoritmo tal que asi:
ahora pasemos esas ecuaciones al C:
primero definimos una variable key_prev:
luego actualizamos la funcion key_poll, dejandola así:
ahora definimos una funcion waskeypressed:
y dentrole añadimos el algoritmo de mas arriba:
luego creamos una funcion waskeyeleased:
y le añadimos el otro algoritmo:
ahora ya podemos controlar con toda precision el estado de las teclas.
para saber si la tecla acaba de ser pulsada:
eso nos da 1 si la tecla acaba de ser pulsada y 0 en caso contrario.
para saber si la tecla acaba de ser soltada:
eso nos da 1 si la tecla acaba de ser soltada y 0 en caso contrario.
Código:
(NOT(tecla_anterior) OR tecla_actual) AND mascara_de_la_tecla
para saber si la tecla acaba de ser soltada:
Código:
(NOT(tecla_actual) OR tecla_anterior) AND mascara_de_la_tecla
primero definimos una variable key_prev:
Código:
u16 key_prev;
Código:
void key_poll()
{
input_prev = input_cur;
input_cur = REG_KEYINPUT | KEY_MASK;
}
Código:
u16 wasKeyPressed(u16 key_code)
{
}
Código:
uint16 wasKeyPressed(uint16 key_code)
{
return (input_cur & ~input_prev) & key_code;
}
Código:
u16 wasKeyReleased(u16 key_code)
{
}
Código:
uint16 wasKeyReleased(uint16 key_code)
{
return (~input_cur & input_prev) & key_code;
}
http://kylehalladay.com/blog/tutorial/gba/
Tomemos por un segundo el codigo del captulo 4, y guardemoslo como input.h . vayamos de vuelta an nuestro archivo principal, borremosle el contenido y empecemos con el tema de hoy: dibujar y mover cuadrados en pantalla.
Primero definimos la VRAM, la IO, la funcion setmode3 e importamos la libreria gba_types:
despues definimos una funcion main:
y definimos una variable i y un bucle infinito:
y dentro le ponemos esto:
con esto deberamos tener un hermoso fondo blanco
Código:
#define VRAM 0x06000000
#define IO 0x04000000
void setmode3() {
*(unsigned int*)IO = 0x0403;
}
Código:
void main() {
}
Código:
int i = 0;
void main() {
while(1) {
}
}
Código:
int i = 0;
void main() {
while(1) {
((volatile uint16*)VRAM)[i] = 0xFFFF;
i += 1;
}
}
pero a quien le gustan los fondos blancos? creemos una funcion makecol:
no voy a profundizar en esto, eso ya lo haremos en otro capitulo.
luego definimos una funcion drawline:
le insertamos un bucle for:
y le añadimos otro bucle for dentro:
y le añadimos la funcion principal:
con eso escribimos nuestro cuadrado/rectangulo/paraboloide en la VRAM.
para regular los fps de la consola y hacer que no mueva mas de los que soporta la pantalla, creamos esta funcion:
no voy a explicarla, no es dificil de comprender.
ahora actualizemos la funcion main:
primero borramos su contenido y llamamos a setmode3:
luego abrimos un bucle for:
y le añadimos las condiciones:
y la rama condicional:
es basicamente el while(1) de antes, solo que llena la pantalla de negro y cuando termina para.
luego definimos la variable x:
y abrimos un bucle infinito:
dentro llamamos a la funcion vsync:
llamamos a la funcion drawrect:
y aumentamos el valor de x:
por ultimo, añadimos un return 0 luego del bucle:
ahora tenemos una pantalla negra que se va llenando de blanco 10 pixels a la vez.
Código:
inline u16 MakeCol(u8 red, u8 green, u8 blue)
{
return (red & 0x1F) | (green & 0x1F) << 5 | (blue & 0x1F) << 10;
}
luego definimos una funcion drawline:
Código:
void drawRect(int left, int top, int width, int height, u16 clr)
{
}
Código:
void drawRect(int left, int top, int width, int height, u16 clr)
{
for (int y = 0; y < height; ++y)
{
//lo que este aqui se repetira hasta que se alcanze el limite heigth
}
}
Código:
void drawRect(int left, int top, int width, int height, u16 clr)
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
//esto se repetira hasta que alcanzemos el limite width
}
}
}
Código:
void drawRect(int left, int top, int width, int height, u16 clr)
{
for (int y = 0; y < height; ++y)
{
for (int x = 0; x < width; ++x)
{
VRAM[(top + y) * 240 + left + x] = clr;
}
}
}
para regular los fps de la consola y hacer que no mueva mas de los que soporta la pantalla, creamos esta funcion:
Código:
#define REG_VCOUNT (* (volatile u16*) 0x04000006)
inline void vsync()
{
while (REG_VCOUNT >= 160);
while (REG_VCOUNT < 160);
}
ahora actualizemos la funcion main:
primero borramos su contenido y llamamos a setmode3:
Código:
setmode3();
Código:
for ()
{
}
Código:
for (int i = 0; i < 240 * 160; ++i)
{
}
Código:
for (int i = 0; i < 240 * SCREEN_H; ++i)
{
SCREENBUFFER[i] = MakeCol(0,0,0);
}
luego definimos la variable x:
Código:
int x = 0;
Código:
while(1)
{
}
Código:
while(1)
{
vsync();
}
Código:
while(1)
{
vsync();
drawRect(x % 240, (x / 240) * 10, 10, 10,MakeCol(31,31,31));
}
Código:
while(1)
{
vsync();
drawRect(x % 240, (x / 240) * 10, 10, 10,MakeCol(31,31,31));
x += 10;
}
Código:
return 0;
pero eso termina y se queda ahí. vamos a cambiar eso:
primero vamos a main y creamos un if despues de la llamada a vsync:
colocamos la condicion:
como tiene una rama condicional de una sola linea, lo ponemos seguido:
luego abrimos una variable last:
y llamamos a la funcion drawrect:
ahora tenemos un cuadrado de 10 por 10 viajando por la pantalla de nuestra GBA (o nuestro emulador).
primero vamos a main y creamos un if despues de la llamada a vsync:
Código:
if ()
Código:
if ( x > 240 * (160/10))
Código:
if ( x > 240 * (SCREEN_H/10)) x = 0;
Código:
int last = x - 10;
Código:
drawRect(last % 240, (last / 240 * 10, 10, 10,MakeCol(0,0,0));
hoy vamos a tomar nuestro cuadrado de 10 x 10 y lo convertiremos en un juego. o al menos un boceto de uno.
Fuentes:
todo esto fue programado por mi.
primero incluimos la libreria input.h de antes:
despues definimos SCREEN_W y SCREEN_H:
luego eliminaremos esto:
e iremos arriba para definir last:
vamos de bajo de vsync() y llamamos a key_poll:
borramos esto:
ahora abran un multibucle if/else if (no me molesten, me gusta decirles asi):
eso significa que si lo primero da falso, controla lo segundo, si el segundo da falso, controla el tercero, etc.
le añadimos condiciones getkeypressed:
y le añadimos las teclas:
ya podemos empezar.
Derecha:
esta es simple. le dcimos que dibuje al cuadrado en x:
aumentamos x:
y restamos last:
ahora el cuadrado va para la derecha al tocar dicha tecla.
Izquierda:
la izquierda es lo mismo pero al reves:
dibujamos el cuadrado:
restamos x:
y sumamos last:
ahora el cuadrado va a la izquierda.
Abajo:
ese es mas dificil. Primero escribimos esto:
esto hace que volvamos arriba cundo lleguemos abajo.
abrimos un bucle else:
dibujamos el cuadrado:
definimos el valor de last:
y aumentamos x:
ahora nos movemos hacia abajo.
Arriba
escribimos esto:
dibujamos el cuadrado:
definimos el valor de last:
y restamos x:
solo falta solucionar bugs:
añadimos esto en el bucle de main:
y listo!
Código:
#include "input.h"
Código:
#define SCREEN_W 240
#define SCREEN_H 160
Código:
int last = x - 10;
Código:
int last;
Código:
key_poll();
Código:
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10,MakeCol(31,31,31));
x += 10;
Código:
if () {
} else if () {
} else if () {
} else if () {
}
le añadimos condiciones getkeypressed:
Código:
if (getKeyState()) {
} else if (getKeyState()) {
} else if (getKeyState()) {
} else if (getKeyState()) {
}
Código:
if (getKeyState(KEY_RIGHT)) {
} else if (getKeyState(KEY_LEFT)) {
} else if (getKeyState(KEY_DOWN)) {
} else if (getKeyState(KEY_UP)) {
}
Derecha:
esta es simple. le dcimos que dibuje al cuadrado en x:
Código:
if (getKeyState(KEY_RIGHT)) {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
}
Código:
if (getKeyState(KEY_RIGHT)) {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
x += 10;
}
Código:
if (getKeyState(KEY_RIGHT)) {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
x += 10;
last = x - 10;
}
Izquierda:
la izquierda es lo mismo pero al reves:
dibujamos el cuadrado:
Código:
} else if (getKeyState(KEY_LEFT)) {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
}
Código:
} else if (getKeyState(KEY_LEFT)) {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
x -= 10;
}
Código:
} else if (getKeyState(KEY_LEFT)) {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
x -= 10;
last = x + 10;
}
Abajo:
ese es mas dificil. Primero escribimos esto:
Código:
} else if (getKeyState(KEY_DOWN)) {
if (x > SCREEN_W * 15) {
last = x;
x = x - SCREEN_W * (15);
}
}
abrimos un bucle else:
Código:
} else if (getKeyState(KEY_DOWN)) {
if (x > SCREEN_W * 15) {
last = x;
x = x - SCREEN_W * (15);
} else {
}
}
Código:
} else if (getKeyState(KEY_DOWN)) {
if (x > SCREEN_W * 15) {
last = x;
x = x - SCREEN_W * (15);
} else {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
}
}
Código:
} else if (getKeyState(KEY_DOWN)) {
if (x > SCREEN_W * 15) {
last = x;
x = x - SCREEN_W * (15);
} else {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
last = x;
}
}
Código:
} else if (getKeyState(KEY_DOWN)) {
if (x > SCREEN_W * 15) {
last = x;
x = x - SCREEN_W * (15);
} else {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
last = x;
x += 240;
}
}
Arriba
escribimos esto:
Código:
} else if (getKeyState(KEY_UP)) {
if (x < 160) {
last = x;
x = x + SCREEN_W * (15);
}
}
Código:
} else if (getKeyState(KEY_UP)) {
if (x < 160) {
last = x;
x = x + SCREEN_W * (15);
} else {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
}
}
Código:
} else if (getKeyState(KEY_UP)) {
if (x < 160) {
last = x;
x = x + SCREEN_W * (15);
} else {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
last = x;
}
}
Código:
} else if (getKeyState(KEY_UP)) {
if (x < 160) {
last = x;
x = x + SCREEN_W * (15);
} else {
drawRect(x % SCREEN_W, (x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,31));
last = x;
x -= 240;
}
}
añadimos esto en el bucle de main:
Código:
if (x < 0) {
x = SCREEN_W * (SCREEN_H/10);
drawRect(10 % SCREEN_W, (last / SCREEN_W) * 10, 10, 10,MakeCol (0,0,0));
drawRect(last % SCREEN_W, (0 / SCREEN_W) * 10, 10, 10,MakeCol (0,0,0));
}
lo primero que haremos sera crear y definir el valor de una variable puntos:
una variable foodinscreen:
y una variable food_x:
ahora definimos una funcion drawfood:
le añadimos un if:
su condicion:
y escribimos en la rama condicional lo siguiente:
la definicion del valor de food_x:
dentro le añadimos un rand:
un limite para el rand:
y una multiplicacion por 10:
acabamos de settear el valor de food_x en un numero al azar en los limites de la pantalla multiplo de 10.
llamamos a drawrect:
ahora dibujara un cuadrado amarillo en food_x.
por ultimo activamos foodinscreen:
ahora definimos una funcion checkfood:
le añadimos un if:
y le añadimos la condicion:
dentro borramos la comida:
sumamos 1 a la variable points:
y desactivamos foodinscreen:
ahora solo añadan esto debajo de key_poll():
Listo! ahora ya hay puntos.
Código:
int points;
Código:
int foodinscreen = 0;
Código:
int food_x;
Código:
void drawfood() {
}
Código:
void drawfood() {
if () {
}
}
Código:
void drawfood() {
if (!(foodinscreen)) {
}
}
la definicion del valor de food_x:
Código:
void drawfood() {
if (!(foodinscreen)) {
food_x = ;
}
}
Código:
void drawfood() {
if (!(foodinscreen)) {
food_x = (rand());
}
}
Código:
void drawfood() {
if (!(foodinscreen)) {
food_x = (rand()%(25*16));
}
}
Código:
void drawfood() {
if (!(foodinscreen)) {
food_x = (rand()%(25*16)) * 10;
}
}
llamamos a drawrect:
Código:
void drawfood() {
if (!(foodinscreen)) {
food_x = (rand()%(25*16)) * 10;
drawRect(food_x % SCREEN_W, (food_x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,0));
}
}
por ultimo activamos foodinscreen:
Código:
void drawfood() {
if (!(foodinscreen)) {
food_x = (rand()%(25*16)) * 10;
drawRect(food_x % SCREEN_W, (food_x / SCREEN_W) * 10, 10, 10, MakeCol(31,31,0));
foodinscreen = 1;
}
}
Código:
void checkfood() {
}
Código:
void checkfood() {
if () {
}
}
Código:
void checkfood() {
if (food_x == x) {
//lo que este aqui se ejecutara si el cuadrado esta sobre la comida
}
}
Código:
void checkfood() {
if (food_x == x) {
drawRect(food_x % SCREEN_W, (food_x / SCREEN_W) * 10, 10, 10, MakeCol(0,0,0));
}
}
Código:
void checkfood() {
if (food_x == x) {
drawRect(food_x % SCREEN_W, (food_x / SCREEN_W) * 10, 10, 10, MakeCol(0,0,0));
points += 1;
}
}
Código:
void checkfood() {
if (food_x == x) {
drawRect(food_x % SCREEN_W, (food_x / SCREEN_W) * 10, 10, 10, MakeCol(0,0,0));
points += 1;
foodinscreen = 0;
}
}
Código:
drawfood();
checkfood();
todo esto fue programado por mi.
La GBA, ademas de las interrupciones por hardware tiene interrupciones por software, que son llamadas mediante el comando de assembly (a.k.a ASM) SWI, que significa software interrupt, y usan como parametos de Entrada/salida los registros del r0 al r3. y me diran cosas como:
¡Lino, no puedes usar ASM en un archivo .c!
y yo les respondere algo como:
claro que se puede, mira lo que sigue:
ahora vamos a lo bueno:
¡Lino, no puedes usar ASM en un archivo .c!
y yo les respondere algo como:
claro que se puede, mira lo que sigue:
si nosotros escribimos esto:
ejecutara la instruccion ASM que pongas dentro de las comillas
Código:
int ASM()
{ asm(""); }
si saben assembly avanzado, seguro se saben lo que sigue. pero como no hay mucha gente que conozca ASM, ni que hablar de avanzado, explico esto. la funcion SWI recibe 1 parametro:
el cual puede ser cualquiera de esta lista:
Definiciones de algunas:
0x06: Div
entrada:
r0: numerador
r1: denominador
salida:
r0: numerador / denominador
r1: numerador % denominador
r3: abs(numerador / denominador)
Nota: NO dividas por 0!
0x08: Sqrt
Entrada:
r0: num, un entero sin signo de 32 bits
salida:
r1: sqrt(num)
0x0a: ArcTan2
entrada:
r0: x, un numero con signo de 16 bits (s16)
r1: y, un numero con signo de 16 bits (s16)
salida:
r0: x≥0 : θ= arctan(y/x) ∨ x<0 : θ= sign*(π − arctan(|y/x|).
esto da la inversa total de y = x*tan(θ). El problema con la tangente es que el dominio es un semicírculo, como lo es el rango de arctan. para conseguir el rango del circulo completo, los valores x e y son necesarios no solo para su cociente, sino también para sus signos. el rango matematico de θ es [−π, π⟩, que corresponde a [−0x8000, 0x8000⟩ (o [0, 2π⟩ y [0, 0xFFFF] si prefieres)
para llamar a cualquiera de estas funciones, usamos lo siguiente:
Código:
SWI 0x[codigo de interrupcion]
Código:
0x00 SoftReset 0x08 Sqrt
0x01 RegisterRamReset 0x09 ArcTan
0x02 Halt 0x0A ArcTan2
0x03 Stop 0x0B CPUSet
0x04 IntrWait 0x0C CPUFastSet
0x05 VBlankIntrWait 0x0D BiosChecksum
0x06 Div 0x0E BgAffineSet
0x07 DivArm 0x0F ObjAffineSet
0x10 BitUnPack 0x18 Diff16bitUnFilter
0x11 LZ77UnCompWRAM 0x19 SoundBiasChange
0x12 LZ77UnCompVRAM 0x1A SoundDriverInit
0x13 HuffUnComp 0x1B SoundDriverMode
0x14 RLUnCompWRAM 0x1C SoundDriverMain
0x15 RLUnCompVRAM 0x1D SoundDriverVSync
0x16 Diff8bitUnFilterWRAM 0x1E SoundChannelClear
0x17 Diff8bitUnFilterVRAM 0x1F MIDIKey2Freq
0x20 MusicPlayerOpen 0x28 SoundDriverVSyncOff
0x21 MusicPlayerStart 0x29 SoundDriverVSyncOn
0x22 MusicPlayerStop 0x2A GetJumpList
0x23 MusicPlayerContinue
0x24 MusicPlayerFadeOut
0x25 MultiBoot
0x26 HardReset
0x27 CustomHalt
0x06: Div
entrada:
r0: numerador
r1: denominador
salida:
r0: numerador / denominador
r1: numerador % denominador
r3: abs(numerador / denominador)
Nota: NO dividas por 0!
0x08: Sqrt
Entrada:
r0: num, un entero sin signo de 32 bits
salida:
r1: sqrt(num)
0x0a: ArcTan2
entrada:
r0: x, un numero con signo de 16 bits (s16)
r1: y, un numero con signo de 16 bits (s16)
salida:
r0: x≥0 : θ= arctan(y/x) ∨ x<0 : θ= sign*(π − arctan(|y/x|).
esto da la inversa total de y = x*tan(θ). El problema con la tangente es que el dominio es un semicírculo, como lo es el rango de arctan. para conseguir el rango del circulo completo, los valores x e y son necesarios no solo para su cociente, sino también para sus signos. el rango matematico de θ es [−π, π⟩, que corresponde a [−0x8000, 0x8000⟩ (o [0, 2π⟩ y [0, 0xFFFF] si prefieres)
para llamar a cualquiera de estas funciones, usamos lo siguiente:
Código:
int funcion(int parametros) {
asm("swi 0x");
}
guardemos el codigo anterior como game.c y borremos el contenido de template.c . luego haremos esto:
definimos una variable x:
y creamos una funcion swi_sqrt:
le añadimos el parametro num:
y la instruccion:
ahora definimos main:
dentro llamamos a sqrt con 4 como parametro:
abrmos un if:
le añadimos la condicion:
en la rama condicional abrimos un while:
escribimos blanco en la VRAM:
y aumentamos x:
ahora tenemos una pantalla blanca.
definimos una variable x:
Código:
int x;
Código:
int swi_sqrt() {
}
Código:
int swi_sqrt(int num) {
}
Código:
int swi_sqrt(int num) {
asm("swi 0x08");
}
Código:
void main() {
}
Código:
void main() {
int y = swi_sqrt1(4);
}
Código:
void main() {
int y = swi_sqrt1(4);
if() {
}
}
Código:
void main() {
int y = swi_sqrt1(4);
if(y == 2) {
}
}
Código:
void main() {
int y = swi_sqrt1(4);
if(y == 2) {
while(1) {
}
}
}
Código:
void main() {
int y = swi_sqrt1(4);
if(y == 2) {
while(1) {
((unsigned short*)0x06000000)[x] = 0xFFFF;
}
}
}
Código:
void main() {
int y = swi_sqrt1(4);
if(y == 2) {
while(1) {
((unsigned short*)0x06000000)[x] = 0xFFFF;
x += 1;
}
}
}
Les presento a OpenEngine-GBA, un proyecto de codigo libre que se viene cociendo desde hace bastante y permite la implementacion de un codigo mucho mas limpio, y esta escrito, hasta el dia de la fecha, 19/3/18, enteramente por mi. esta dividido en 3 partes, las cuales son:
- OpenEngine.h : se encarga de gestionar variables de sistema, funciones fisicas y regiones de control. todas las variables, regiones y demas, que sean usadas por esta o alguna de las otras 2 partes se encuentran aqui.
- OpenInput.h : gestiona las funciones de entrada de la GBA. las funciones aqui definidas son usadas para conocer la respuesta del jugador.
- OpenGraphics.h : gestiona la VRAM, y escribe sobre ella. tambien gestiona los modos de la pantalla
pueden encontrarlo aquí
en poco tiempo, explicare como utilizar cada componente por separado y explicare las nuevas funciones que sean añadidas
volví!
les mostrare brevemente como usar cada función:
les mostrare brevemente como usar cada función:
carga las variables para que las demas funciones anden.
uso:
uso:
Código:
key_poll();
TRUE si una tecla acaba de empezar a pulsarse y FALSE en caso contrario.
uso:
para saber que puede ser KEY_CODE, ver CODIGOS
uso:
Código:
if (wasKeyPressed(KEY_CODE)) {
//codigo
}
TRUE si una tecla acaba de ser soltada y FALSE en caso contrario.
uso:
para saber que puede ser KEY_CODE, ver CODIGOS
uso:
Código:
if (wasKeyReleased(KEY_CODE)) {
//codigo
}
TRUE si una tecla esta siendo pulsada y FALSE en caso contrario.
uso:
para saber que puede ser KEY_CODE, ver CODIGOS.
uso:
Código:
if (getKeyState(KEY_CODE)) {
//codigo
}
devuelve 1 si es pulsada la tecla DERECHA y -1 si es pulsada la tecla IZQUIERDA
uso:
uso:
Código:
x += key_tri_h();
devuelve 1 si es pulsada la tecla ARRIBA y -1 si es pulsada la tecla ABAJO
uso:
uso:
Código:
y += key_tri_v();
devuelve 1 si es pulsada la tecla R y -1 si es pulsada la tecla L
uso:
uso:
Código:
s += key_tri_s();
#define KEY_A 0x0001
#define KEY_B 0x0002
#define KEY_SELECT 0x0004
#define KEY_START 0x0008
#define KEY_RIGHT 0x0010
#define KEY_LEFT 0x0020
#define KEY_UP 0x0040
#define KEY_DOWN 0x0080
#define KEY_R 0x0100
#define KEY_L 0x0200
#define KEY_B 0x0002
#define KEY_SELECT 0x0004
#define KEY_START 0x0008
#define KEY_RIGHT 0x0010
#define KEY_LEFT 0x0020
#define KEY_UP 0x0040
#define KEY_DOWN 0x0080
#define KEY_R 0x0100
#define KEY_L 0x0200
eso es todo, hasta el proximo capitulo.
agradecimientos
a @Kaiser de Emperana por ayudarme con mis miles de dudas
Última edición: