Nuclear Invaders para JUPITER ACE
Publicado: 22 Sep 2013, 09:48
EL JUEGO
Vuelven los invasores del espacio!!!
En el año 1977 intentaron invadir las ciudades de la tierra, y fracasaron.
Ahora vuelven con un malvado plan: destruir las centrales nucleares para dejar el planeta inhabitable.
Con la ayuda de nuestro tanque debemos evitar que cumplan con su objetivo.
Cada nivel lo componen 30 invasores, y al superarlo aparecerá una central más grande y los invasores deberán recorrer menos distancia para llegar a ella.
Destruye el UFO que aparece por la parte superior para conseguir puntos extras.
Pulsa “RETURN” en la pantalla de presentación para empezar la partida.
Descargar el juego en formato ".ACE" (Snapshot para emuladores):
ATENCIÓN !!! El juego requiere de un equipo con ampliación de memoria (19 KB mínimo).
BLOQUES
En FORTH los programas se suelen hacer definiendo palabras que se encargan de hacer cosas muy especificas, y variables globales para todas las palabras.
Se han definido 10 variables:
X – Posición del tanque.
H – Posición horizontal del disparo.
V – Posición vertical del disparo. Si vale 0 el disparo no está activo.
U – Posición horizontal del UFO. Se inicializa a -200 y si es menor de 0 no aparece en pantalla.
L – Número de vidas.
N – Fase del juego.
S – Puntos.
R – Récord.
P – Puntero que apunta a los datos del invasor que se ha de mover.
D – Invasores que quedan activos.
Para el programa se han definido 16 palabras:
CLRSCR: Borra la parte de juego de la pantalla. Respeta los marcadores superiores e inferiores.
PAUSA: Hace una pausa. Coge un valor del stack.
USING: Imprime los puntos y el récord. Necesita tres valores en el stack: Valor, Posición vertical y posición horizontal.
RND: Genera un número aleatorio entre 0 y 31 y lo deja en el stack.
GRAFICOS: Redefine las letras minúsculas con los gráficos del juego.
INSTRU: Muestra la pantalla de instrucciones, hasta que se pulsa una tecla.
PRESENTA: Muestra la pantalla de presentación y la pantalla de inicio de partida.
INICIO: Posiciona y muestra en pantalla a los invasores, el tanque y los barriles nucleares del centro.
MOVER: Controla el movimiento de nuestro tanque.
INVASOR: Controla a los invasores.
UFO: Controla el UFO.
DISPARO: Controla todo lo relacionado con el disparo. Si detecta una colisión llama a IMPACTO.
IMPACTO: Se encarga de controlar lo que ha tocado el disparo. Nada, muro, UFO o Invasor.
VIDAS: Decrementa el contador de vidas y continúa o finaliza la partida según las que queden.
FASE: Al superar un nivel, lo incrementa y llama a INICIO.
RUN: Ejecuta el programa y se encarga de ir llamando a las palabras necesarias en cada momento.
COMO FUNCIONA
A continuación explico como funciona básicamente el programa:
Al ejecutar el programa con RUN:
- Se hace una pequeña PAUSA para que tengas tiempo a soltar la tecla ENTER.
- Se ejecuta la palabra GRAFICOS que se encarga de cargar los gráficos del juego.
- INSTRU nos presenta la pantalla de instrucciones. Solo se muestra una vez.
- Se inicia un primer bucle (BEGIN / UNTIL) que es infinito y se encarga de ejecutar toda una serie de palabras.
- Se muestra la pantalla de presentación y se inicializan los puntos, vidas y nivel.
- Se inicia un segundo bucle que se repite hasta que perdemos todas las vidas y finaliza la partida.
- Se inicializa los datos de los invasores, tanque y UFO.
- Se inicializa un tercer bucle que se repite hasta que perdemos una vida.
- Se hace una PAUSA porque sino el juego es demasiado rápido.
- Controlamos nuestro tanque con MOVER.
- Controlamos el disparo con DISPARO y consultando la RAM de video miramos si hemos tocado algo, ejecutando IMPACTO.
- Controlamos el UFO con UFO.
- Con FASE controlamos si hemos destruido a todos los invasores y si es cierto, incrementamos el nivel.
- Controlamos el movimiento de los invasores con INVASOR.
- Si ningún invasor ha llegado a la central nuclear se deja un 0 en el stack y el UNTL vuelve al tercer BEGIN. En caso contrario se deja un 1 en el stack y se rompe el UNTIL.
- Con vidas decrementamos el número de vidas, actualizamos su marcador y deja un 0 en el stack si todavía quedan y el UNTIL vuelve al segundo BEGIN. Si no hay más vidas, se deja un 1 en el stack y se rompe el UNTIL.
- Si se acaban las vidas se hace una PAUSA, se deja un 0 en el stack que hace que el UNTIL nos mande al primer BEGIN.
EL LISTADO
Código: Seleccionar todo
0 VARIABLE X 0 VARIABLE H 0 VARIABLE V 0 VARIABLE U 0 VARIABLE L
0 VARIABLE N 0 VARIABLE R 0 VARIABLE P 0 VARIABLE S 0 VARIABLE D
: CLRSCR 2 0 AT 608 SPACES ;
: PAUSA 0 DO 200 0 DO LOOP LOOP ;
: USING AT DUP DUP DUP 1000 < IF 48 EMIT THEN 100 < IF 48 EMIT THEN 10 < IF 48 EMIT THEN . ;
: RND 15403 C@ 31 AND ;
: GRAFICOS 1 3 7 13 15 5 8 4 128 192 224 176 240 160 16 32 1 3 7 13 15 2 10 128 192 224 176 240 64 160 80 8 36 47 59 63 31 8 16 32 72 232 184 248 240 32 16 8 4 15 27 63 63 40 6 32 64 224 176 248 248 40 192 3 31 63 57 63 14 25 12 192 248 252 156 252 112 152 48 3 31 63 57 63 6 13 48 192 248 252 156 252 96 176 12 1 3 3 127 255 255 255 255 0 128 128 252 254 254 254 254 0 7 31 63 109 255 57 16 0 224 248 252 182 255 156 8 4 34 16 8 96 4 18 36 64 136 16 32 12 32 144 72 0 1 1 1 1 1 0 0 66 32 71 15 30 76 135 35 2 100 240 250 217 240 196 18 251 251 251 0 223 223 223 0 0 0 0 0 0 0 255 0 3 28 35 32 33 35 33 32 224 28 226 2 194 226 194 130 37 47 46 38 32 28 3 0 82 122 58 50 2 28 224 0 12039 12255 DO I C! -1 +LOOP ;
: INSTRU CLS 1 8 AT ." NUCLEAR INVADERS" 6 4 AT ." CONTROLS: LEFT = Z" 7 14 AT ." RIGHT = X" 8 14 AT ." FIRE = ENTER" 14 6 AT ." *PRESS KEY TO START*" 21 6 AT ." C SCAINET SOFT, 2013" BEGIN INKEY UNTIL 9 PAUSA ;
: PRESENTA CLS ." SCORE<1> HI-SCORE SCORE<2>" S @ 1 3 USING R @ 1 14 USING 5 14 AT ." PLAY" 7 8 AT ." NUCLEAR INVADERS" 10 5 AT ." *SCORE ADVANCE TABLE*" 12 9 AT ." op =? MYSTERY" 14 9 AT ." cd =30 POINTS" 16 9 AT ." gh =20 POINTS" 18 9 AT ." kl=10 POINTS" 22 4 AT ." mnmn" 13 SPACES ." CREDIT 00" BEGIN INKEY UNTIL CLRSCR 21 1 AT ." wwwwwwwwwwwwwwwwwwwwwwwwwwwwww" 22 2 AT ." 3" 11 9 AT ." PLAY PLAYER<1>" 10 0 DO 1 3 AT 4 SPACES 3 PAUSA 1 3 AT ." 0000" 3 PAUSA LOOP 3 L ! 2 N ! 0 S ! ;
: INICIO CLRSCR 15 15 N @ - DUP U ! AT N @ 2 * 2 + DUP 0 DO ." v" LOOP 4 U @ AT 0 DO ." v" LOOP 3 13 DO U @ DUP I SWAP AT ." v" N @ 0 DO ." xy" LOOP ." v" I 1+ SWAP AT ." v" N @ 0 DO ." z{" LOOP ." v" -2 +LOOP 99 103 DUP 107 DUP 3 13 DO I 1 AT DUP EMIT DUP 1+ EMIT I 29 AT DUP EMIT 1+ EMIT -2 +LOOP 3 0 5 0 99 3 0 7 0 103 3 0 9 0 103 3 0 11 0 107 3 0 5 29 99 3 0 7 29 103 3 0 9 29 103 3 0 11 29 107 3 0 13 29 107 9999 10049 DO I C! -1 +LOOP -200 U ! 3 X ! 0 V ! 10000 P ! 30 D ! ;
: MOVER X @ DUP 27 < IF INKEY 120 = IF X @ 1+ X ! THEN THEN 3 > IF INKEY 122 = IF X @ 1- X ! THEN 20 X @ 1- AT ." mn " ;
: INVASOR P @ C@ O> IF P @ 1+ C@ 0= IF RND D @ 5 < IF 10 ELSE 26 THEN > IF 1 P @ 1+ C! THEN 0 ELSE P @ 10025 < IF 1 ELSE -1 THEN DUP DUP P @ 3 + C@ + P @ 3 + C! P @ 2 + C@ P @ 3 + C@ AT 0> IF SPACE THEN P @ 4 + C@ P @ 3 + C@ 2 MOD 0> IF 2 - THEN DUP EMIT 1+ EMIT 0< IF SPACE THEN P @ 3 + C@ DUP 14 N @ - = IF DROP 1 ELSE 15 N @ + = IF 1 ELSE 0 THEN THEN THEN ELSE 0 THEN P @ 10045 = IF 10000 ELSE P @ 5 + THEN P ! ;
: UFO U @ DUP 0< IF 1+ U ! ELSE DUP 27 > IF 3 SWAP AT 3 SPACES -200 U ! ELSE 1+ DUP U ! 3 SWAP AT ." op" THEN THEN ;
: DISPARO V @ 0= IF INKEY 13 = IF X @ H ! 19 V ! THEN ELSE V @ DUP H @ AT SPACE 5 < IF 0 V ! ELSE V @ 2- V ! IMPACTO 0= IF V @ H @ AT 115 EMIT THEN THEN THEN ;
: IMPACTO 9216 V @ 32 * + H @ + C@ 32 = IF 0 ELSE V @ 15 < IF V @ 3 = IF 3 U @ 1+ AT ." tu" 18 12 DO I 15 BEEP LOOP RND 12 / 1+ 50 * DUP 3 U @ 1+ AT . S @ + DUP S ! 1 3 USING 20 PAUSA 3 U @ AT 4 SPACES 200 U ! ELSE D @ 1- D ! V @ 3 - 2 / H @ 15 > IF 5 + THEN V @ DUP 5 = IF DROP 30 ELSE 10 > IF 10 ELSE 20 THEN THEN S @ + DUP S ! 1 3 USING 1- 5 * 10000 + V ! V @ 2 + C@ V @ 3 + C@ DUP 15 < IF 1+ THEN 2 PICK 2 PICK AT ." qr" 10 100 BEEP AT 2 SPACES V @ DUP C@ 1- SWAP C! V @ C@ 0> IF 0 V @ 1+ C! H @ 15 < IF 0 ELSE 29 THEN V @ 3 + C! V @ 2 + C@ V @ 3 + C@ AT H @ 15 < IF SPACE THEN V @ 4 + C@ DUP EMIT 1+ EMIT THEN THEN THEN 0 V ! 1 THEN ;
: VIDAS 100 200 DO I 20 BEEP -5 +LOOP 30 PAUSA L @ 1- DUP DUP 0> IF DUP 22 AT . DUP 2 * 2 + 22 SWAP AT 2 SPACES ELSE 17 11 AT ." GAME OVER" S @ R @ > IF S @ R ! THEN THEN L ! 0= ;
: FASE D @ 0= IF N @ DUP 5 < IF 1+ THEN N ! 100 PAUSA INICIO THEN ;
: RUN 9 PAUSA GRAFICOS INSTRU BEGIN PRESENTA BEGIN INICIO BEGIN 2 PAUSA MOVER DISPARO UFO FASE INVASOR UNTIL VIDAS UNTIL 200 PAUSA 0 UNTIL ;
APUNTES FINALES
El lenguaje FORTH siempre me ha fascinado desde los tiempos en que la Revista ZX publicó un curso. Me pareció un lenguaje muy original, simple y compacto. Y con esta experiencia he podido comprobar que también es muy rápido.
Un poco de historia
Cerca de mi casa había un local de SUSHIRO DATA que distribuía, allí por 1982, un curioso ordenador que se programaba en FORTH y se llamaba JUPITER ACE. Allí lo pude ver por primera y última vez. Decían que su lenguaje era cuatro veces más rápido que el BASIC y creo que se quedaron cortos.
Como no pude hacerme con el equipo, unos años más tarde y ya en posesión de un INVES ZX-SPECTRUM me compré el Abersoft FORTH. Era muy interesante, pero realmente tampoco me decidí a hacer nada con él.
Aparte, con los años programé en lenguaje BASIC múltiples intérpretes de FORTH para equipos como el ZX-81, SPECTRUM, MSX, CASIO PB y PC que me permitían hacer pequeños programas poco útiles. En el año 1993 programé para PC un intérprete de FORTH en ensamblador. Incluso mi juego MAINFRAME incorpora un sencillo intérprete que es necesario usar y programar para finalizarlo, y todos los programas del sistema están programados en FORTH.
Pero realmente nunca me había lanzado a hacer un programa usando FORTH, y esta es mi primera experiencia, que me ha dejado muy satisfecho.
El equipo
El equipo fue diseñado por técnicos que participaron en los diseños del SINCLAIR ZX-81 y ZX-SPECTRUM y que dejaron SINCLAIR para montar su propia empresa, la “Jupiter Cantab”. La empresa quebró en 1984.
El JUPITER ACE se lanzó en 1982 y tiene muchas cosas en común con los equipos de SINCLAIR: disposición de las teclas, juego de caracteres, resolución, “casi” compatibilidad de periféricos, etc.
Incorpora un Z80A a 3,25 MHz y lleva una memoria ROM de 8 KB con el intérprete FORTH y una RAM de 3 KB, ampliables. Funciona en blanco y negro en modo texto, con una resolución de 32x24 caracteres y tiene un “modo gráfico” igual que el del SINCLAIR ZX-81 de 64x48 puntos de 4x4 pixels. El sonido lo genera la propia CPU y se reproduce mediante un zumbador igual que en el SPECTRUM.
De los 3 KB de serie del equipo, 1 KB lo ocupa la memoria de video, 1 KB en el juego de caracteres (se copia en RAM) y el último 1 KB está a disposición del usuario.
Cosillas del JUPITER ACE
El FORTH es un lenguaje que en si no tiene sintaxis. Usa una pila o stack para almacenar números y una serie de palabras nos permiten hacer cosas usando en algunos casos los números del stack.
Programar en este equipo requiere ir creando palabras. Cada palabra es un pequeño programa que hace una cosa concreta, y con una o varias creas el programa completo. Vamos, que es bastante modular. Estas palabras se van añadiendo a las que vienen de serie con el sistema. Con “VLIST” podemos ver todas las palabras que hay disponibles.
Con “EDIT palabra” podemos editar una palabra definida por nosotros, pero no las fijas. Este editor es sencillo pero práctico y cómodo de usar. También nos muestra el listado “bastante” estructurado, lo que facilita su comprensión. Y atención, porque al acabar de editar una palabra, queda duplicada en el diccionario. Con “REDEFINE palabra” borramos la copia anterior para que no haya problemas durante la ejecución. Y si solo deseamos ver el código de una palabra podemos usar “LIST palabra”.
Este sistema tiene dos modos de funcionamiento, SLOW (por defecto) y FAST, aunque no tiene nada que ver con el ZX-81. En modo SLOW el intérprete controla los errores y en el modo FAST no. Así que si el programa está verificado que no falla, se puede activar el modo FAST para que sea algo más rápido. También, en modo FAST no se puede detener el programa con las teclas SHIFT+SPACE.
El proyecto
Para hacer mi primer programa me podría haber buscado algo más sencillo, pero me he armado de valor y me he propuesto hacer una versión de este juego, previamente programado en DRAGON, MSX y ORIC.
Por otro lado, tengo la placa base del clon del JUPITER ACE diseñada por jepalza y sergitron, y la ampliación de memoria diseñada por mcleod, pero no me he puesto, por ahora, a hacerla funcionar. Así que todo el desarrollo lo he hecho con un emulador: el EightyOne, aunque también he usado el SpudAce. El ACE32 de MS-DOS no graba snapshots y lo descarté, aunque los carga y el juego funciona perfectamente en él.
Gracias a un manual original que conseguí tiempo atrás, las cosas han sido más sencillas, aunque deja bastante que desear.
Así, la principal dificultad ha sido, aparte del programa en si, aprender a usar un JUPITER ACE, y la cosa es bastante más sencilla de lo que parece.
El desarrollo
A partir de aquí he hecho una lista de las distintas cosas que debe hacer el programa y he organizado las palabras. Y sobre la marcha he ido empezando por las partes más sencillas y a medida que aprendía me he liado con las partes más complejas. Así, he empezado con la pantalla de presentación, el movimiento de nuestro tanque, del UFO y el disparo, y he dejado para el final el movimiento de los invasores y el control de lo que toca el disparo.
Este equipo solo funciona en modo carácter, pero nos permite redefinirlos. La dirección empieza en 11264 y acaba en 12287. Contiene 128 caracteres, ya que el resto son iguales pero en video inverso. Con esta información y un bucle he redefinido las minúsculas a partir de los gráficos de 8x8 de la versión MSX.
Para detectar el impacto de nuestro disparo he consultado la memoria RAM de video. Está comprendida entre las direcciones 9216 y la 9983. Ocupa 768 bytes que corresponden a su resolución de 32x24 caracteres en pantalla.
Este equipo no tiene ningún generador de números aleatorios. Para generarlos he accedido a la variable del sistema FRAMES que ocupa 4 bytes y empieza en 15403. El valor de esta variable se incrementa 50 veces por segundo. Mediante un AND 31 limito el rango de valores entre 0 y 31, que ya me va bien para mis necesidades.
El sonido se reproduce mediante la palabra BEEP y funciona igual que en el SPECTRUM.
El tema más complicado ha sido donde guardar los datos de los 10 invasores. Necesitaba 50 bytes, 5 por invasor: Vidas del invasor (3 a 0), Activo (0/1), Posición Y, Posición X y gráfico principal. Este FORTH permite usar matrices, pero en el manual no queda muy claro, así que decidí usar el buffer de la línea de comandos de 255 bytes, y que va desde la dirección 9985 hasta la 10239. Como durante la ejecución del programa no se usan, quedan a mi disposición. Eso si, al volver al sistema se puede destruir su contenido si la línea es muy larga, así que la he usado a partir de la dirección 10000. He usado la variable P como puntero y la voy incrementando o decrementando según convenga.
Respecto al código, algunas partes las podría haber reducido aprovechando más el stack, pero programar en FORTH es bastante liado y he preferido hacerlo como lo he hecho para poder ver con más facilidad que hacen las distintas partes del código de una palabra. Y ni que decir que debugar en FORTH es bastante complicado y liado. Visto que funciona rápido y bien, he aplicado la máxima de “si algo funciona, no lo toques”.
Programar el juego entero me ha llevado 5 días a tiempo parcial… viajes en tren al trabajo, noches, etc.
Y una vez lo he finalizado considero que es mi primer juego en FORTH pero no el último. El próximo espero poderlo programar en mi placa clónica.
Os invito a probarlo.