Mostrando entradas con la etiqueta zx spectrum. Mostrar todas las entradas
Mostrando entradas con la etiqueta zx spectrum. Mostrar todas las entradas

lunes, 2 de diciembre de 2019

Herramienta de Desarrollo para ZX Spectrum SpectNet IDE

Revisando el estado actual de las herramientas de desarrollo para el ZX Spectrum, he encontrado una iniciativa llamada "SpectNet IDE" con una pinta fantástica. 




Sus características principales incluyen todo lo que uno puede desear y en un futuro seguro que será aún mas completo. 

Se sienta sobre el Visual Studio 2017 / 2019 Community Edition y tiene soporte para:

1) Programación en ASM para Z80.

2) Integración con Boriel Basic

3) Emulador incorporado y depuración punto a punto. 

Por el momento estoy valorando el uso de la versión 2.0 Preview 2




Creo que hay que darle las gracias al autor Istvan Novak y animarle a continuar descargando y usando esta magnífica extensión para desarrollar para el ZX Spectrum.



martes, 26 de marzo de 2019

viernes, 22 de julio de 2016

Colección de música en 8-BIT. CHIPTUNES

Os dejo una colección de música en 8-BIT o como le llaman de forma coloquial, ¡chiptunes !.

Hay chiptunes de Spectrum , Commodore 64, MSX, Amiga y otras cosas.

Lo podéis descargar de este enlace.

Abajo, una lista de lo que contiene la colección.

Que lo disfrutéis.
 

viernes, 1 de julio de 2016

Calibrar el color en un Spectrum 48k placa Ver 1 y 2.

En las dos primeras versiones del ZX Spectrum, las "Issue 1" e "Issue 2" el sincronismo del color y el balance de los colores dependen de unos pequeños reguladores. 

Ya me ha pasado más de una vez, que, al no usar el Spectrum en un largo período de tiempo, al encenderlo se ve mal. 

Pues bueno, no queda más remedio que abrirlo y "jugar" con estos pequeños reguladores, hasta que tengamos una imagen y color buena. 

Hay que tener cuidado porque hay que hacerlo con el Spectrum encendido y los movimientos tienen que ser mínimos. Si no hacemos los movimientos muy pequeños, nos podemos tirar horas sin dar con ello. 



El primer regulador "Clock Trimmer" regula la caída del punto en pantalla. Normalmente no hay que tocarlo.

El segundo regulador o "Colour sync trimmer" solo lo tocamos si no tenemos color en absoluto. 

Y ahora los más puñeteros. 

El tercero o "Red-yellow trimmer" y el cuarto o "Blue-yellow trimmer" regulan el balance de color y los debemos mover al tiempo para encontrar un buen color y un blanco lo mas "neutral" (es decir lo más blanco posible). Es aquí donde vamos a invertir más tiempo y debemos movernos lo más lento y suave posible.

La mejor manera de regular los colores, es mientras ejecutamos este programa BASIC , que nos ayuda a ver como va quedando todo. 

10 FOR N= 0 TO 7 
20 FOR M = 1 TO 3 
30 PAPER N : PRINT “ “ 
40 NEXT M 
50 NEXT N 
60 GOTO 10

Darle crédito a DataServe Retro Computing donde encuentro siempre información muy útil.

Un pequeño vídeo del programa BASIC para calibrar los colores: 





miércoles, 29 de junio de 2016

Ejemplo de juego en BASIC 3 en Raya.

Uno de los juegos más manidos, en los ratos muertos en el cole, el 3 en raya.

Implementar este juego en BASIC para el ZX Spectrum, nos da la posibilidad de practicar una de las cosas más importantes que hay en la programación, que es la inteligencia artificial (IA o en inglés AI).

Hay muchos algoritmos aplicables a este juego, que se pueden desarrollar en BASIC con más o menos esfuerzo, e incluso hacer que el juego sea "invencible".

La verdad que un juego en el que no podamos ganar, no tiene gracia y además, la IA debería parecerse a nosotros, los humanos, cometiendo errores y tomando decisiones inadecuadas.

Las premisas básicas para implementar la inteligencia del Spectrum a la hora de jugar contra nosotros han sido:

1) Se decide quien juega primero, humano o ordenador de forma aleatoria.

2) Si sale el Spectrum, posiciona su jugada de forma aleatoria.

3) Se comprueba siempre que nadie a ganado, o si  hay un empate.

4) Se comprueba si el jugador humano tiene una jugada para ganar, y se tapa por parte del Spectrum.

5) Si no hay una jugada por parte del jugador humano en la que pueda ganar, el Spectrum hace su siguiente jugada de forma aleatoria, en las casillas que hay libres.

Parece demasiado simple, ¿ verdad ?. Pues bueno, si que es sencillo de programar en el Spectrum  y además, el juego es divertido.

Habrá veces que nos gane otras que empatemos o que gane el Speccy. La aleatoriedad de la un valor añadido al juego, lo hace divertido, pero a la vez (como sabe comprobar si vamos a ganar y nos trata de parar) es lo suficientemente listo como para que el juego tenga dificultad.

El código fuente lo podéis bajar de este enlace.

Como siempre, lo he pasado a código máquina, usando el compilador a MC de HiSoft. La ganancia en velocidad de ejecución es asombrosa.

La versión en MC la podéis bajar de este enlace.

Por último, un vídeo de demostración del juego.






viernes, 29 de enero de 2016

Ejemplo de marquesina rotante en BASIC

Bueno, es simplemente un texto que va de derecha a izquierda en pantalla, aparece y desaparece en bucle. 

Como nosotros leemos de izquierda a derecha, lo conveniente es que el texto aparezca de derecha a izquierda. 

Puedes descargar el código fuente en formato TAP de este enlace.


Abajo el código BASIC.
Es muy sencillo 

10 CLS
20 PRINT AT 0,0;"Pulsa q para terminar"
30 LET M$="MARQUESINA"
40 LET C=1
50 FOR I=31 TO 0 STEP -1
60 PRINT AT 20,I;M$(1 TO C)
70 IF C<LEN (M$) THEN LET C=c+1
80 IF (31-i)>=LEN (m$) THEN PRINT AT 20,i+LEN (m$);" "
90 GO SUB 170
100 NEXT I
110 FOR i=1 TO LEN (m$) STEP 1
120 PRINT AT 20,0;m$(i TO LEN (m$))
130 PRINT AT 20,LEN (m$)-i;" "
140 GO SUB 170
150 NEXT i
160 GO TO 40
170 LET i$=INKEY$
180 IF i$="q" THEN STOP
190 RETURN
 


martes, 26 de enero de 2016

ZX Spectrum, pasando un "misil" por en medio de la pantalla en BASIC, parte 2

En el ejemplo de "ZX Spectrum, pasando un "misil" por en medio de la pantalla en BASIC" habíamos visto como acometer el pintado sobre lo que hubiese en pantalla, de una línea recta, sobre lo que hubiese en pantalla.

En este ejemplo, hemos ampliado para que, dado: 
  • Unas coordenadas de origen (X,Y) en modo carácter.
  • Número de celdas para el desplazamiento.
  • Una dirección (arriba, abajo y diagonales).


... nos pinte la línea de 8 bits, a modo de misil y atraviese lo que esté en el camino, respetando sus propiedades de dibujo, color y fondo. 

Dado que tenemos un límite de 2 colores, por celda, veremos el efecto de como va cambiando de color a medida que el misil va pasando por encima del contenido.

Los efectos más deseables, se logran con el fondo "blanco" y sin brillo ni intermitencia. En el ejemplo se usan estos modos, para que se vea como el programa es capaz de respetar las propiedades de cada cosa. 

Veamos el programa en acción: 


Con este ejemplo, podemos extrapolar subrutinas, para poder implementar el lanzamiento de misiles en el juego Retro8ogue

Además, para optimizar al máximo el paso a código máquina, usando el compilador HiBasic, he usado las directivas de compilación apropiadas para marcar (casi) todas las variables como enteros positivos, además de las longitudes máximas de cada variable cadena usada. 

El compilador de BASIC funciona de forma óptima, cuando trabaja con enteros, y más, si son enteros positivos. 

Todas estas optimizaciones permiten dejar el compilado con solo 278 bytes para variables, además de presentar el mejor rendimiento. 

Puedes descargar el código BASIC de este enlace.

El ejemplo en código máquina se encuentra en este enlace.



jueves, 21 de enero de 2016

ZX Spectrum, pasando un "misil" por en medio de la pantalla en BASIC

Como hemos visto en la entrada que ejemplifica como pasar de coordenadas de caracteres a coordenadas de alta resolución en pantalla del Spectrum, se pueden mezclar instrucciones que usan coordenadas de alta resolución como PLOT e instrucciones que usan las coordenadas de modo "caracter" como PRINT AT en BASIC.

¿Que pasa si queremos, por ejemplo, pasar un "misil" (linea recta de 8 bits) por en medio de caracteres o UDG's que ya están en pantalla ?. Es decir lo que queremos conseguir es una animación que se solape por encima de lo que hay en pantalla. 

Bueno, lo primero que tenemos que tener en cuenta son cuatro particularidades del Spectrum, que condicionarán nuestro trabajo:
  • Solo hay dos colores por "celda" de 8x8 en pantalla. Es decir, cuando "pisamos" una celda, solo podemos gestionar dos colores a la vez, el de fondo (PAPER) y el del carácter o gráfico (INK). 
  • La ordenación de las direcciones en memoria, no son "lineales". Es decir, hay que calcular posiciones en memoria sabiendo que hay "saltos raros" de memorias en función de la posición en pantalla. 
  • No hay una manera directa de pasar de decimal a binario. Hay que hacer una subrutina que nos lo haga, dado que necesitamos saber el "dibujo" en pantalla por donde pasamos. PEEK nos devuelve un decimal y para dibujar en pantalla, necesitamos saber que es lo que estamos "pisando". 
  • Los efectos FLASH y BRIGHT no afectan a los colores y se pueden usar libremente. Es decir, no nos complican la vida.


Acordémonos que lo que hay en cada celda, es un dibujo de 8x8 bits.

En el siguiente vídeo, vemos ejemplificado el paso de un misil, de izquierda a derecha, que además pasa por encima de letras, y cuando sale, las deja como estaban. 





Se observa el efecto de los colores, cuando pasamos por una letra. Dado que el misil es negro, cuando escribimos un punto con PLOT toda la celda se pasa a color negro. 

Para hacerlo funcionar correctamente tanto en las celdas "vacias" (que solo tienen un espacio en blanco y color de fondo) y recomponer las letras y los colores cuando el misil pasa por encima de ella usamos dos instrucciones importantes, OVER e INVERSE.

Cuando el misil pasa, imprimimos un punto usando OVER 1. Cuando vamos "borrando" el misil y recuperando lo que había, usamos INVERSE en función de lo que había en esa posición, un 0 o un 1. 

También tenemos en cuenta si es el último pixel, para devolverle el color a la celda. 

IF r$(x)="0" AND X<8 THEN PLOT OVER 1;(px-8)+(x-1),py+4: GO TO 390
IF r$(x)="1" AND X<8 THEN PLOT INVERSE 0; FLASH f; BRIGHT bold; FLASH f; INK co;(px-8)+(x-1),py+4: GO TO 390
IF R$(X)="0" AND X=8 THEN PLOT INVERSE 1; INK co; BRIGHT bold; FLASH f;(px-8)+(x-1),py+4: GO TO 390
IF R$(X)="1" AND X=8 THEN PLOT INVERSE 0; INK co; BRIGHT bold; FLASH f;(px-8)+(x-1),py+4

También usamos ATTR y un mecanismo para calcular la posición en memoria y el contenido.

Es todo un poco lioso, pero viendo el ejemplo en código se entiende mucho mejor. 

Como siempre el código BASIC es bastante lento, pero una vez que lo pasamos a código máquina usando el compilador HiSoft (y habiendo optimizado el código BASIC) el rendimiento es aceptable.

He dejado el código BASIC del ejemplo un este enlace

En este otro enlace que dejado el mismo ejemplo pasado a código máquina. 



ZX Spectrum. De decimal a binario y viceversa.

En todas las operaciones de lectura de memoria usando PEEK, el Spectrum nos devuelve un valor decimal.

Cuando lo que estamos leyendo de memoria, es un gráfico, por ejemplo una letra, si queremos usar el contenido necesitamos saber la composición en binario de dicho valor. 

Este pequeño programa BASIC, pide una letra para imprimirla en pantalla, localiza la dirección que ocupa en pantalla (es decir, su "celda") para luego interpretar su contenido en binario y pintarla en pantalla usando ceros y unos. 

Si queremos usar un gráfico que hay en pantalla , o manipularlo, debemos hacerlo usando su valor en binario.

Recordemos que cada carácter es una plantilla de 8x8 bits, dibujada usando ceros y unos, igual que cuando usamos los User Defined Graphics (UDG's)



















En este vídeo observamos como se introduce una letra, la imprimimos en pantalla, luego la analizamos y la volvemos a pintar en pantalla, pero usando ceros y unos. 

El código fuente lo puedes descargar en formato TAP de este enlace.









martes, 19 de enero de 2016

ZX Spectrum BASIC, pasar de coordenadas de baja a alta resolución.

Es muy cómodo trabajar con las coordenadas X e Y de baja resolución, para utilizar comandos como PRINT AT y SCREEN$ en pantalla. 

Las coordenadas de "baja resolución" son perfectas para manejarnos en modo "carácter"  y con UDG's de 8x8 básicos. 

El modo de "alta resolución" de nuestro Spectrum son 255 pixels horizontales por 175 pixels verticales.

Cuando queremos crear formas geométricas, o simplemente pintar una línea recta usamos comandos pomo DRAW y PLOT, que usan las coordenadas de alta resolución. 

Es una gran ventaja el poder combinar el uso de ambos modos gráficos ; simplemente usando el comando y las coordenadas que nos interesen a cada fin.

Por ejemplo, el juego Retro8ogue utiliza el modo carácter para casi todo, usando las coordenadas del modo de "baja resolución" (comandos PRINT AT y SCREEN$ básicamente).

Para poder pintar, por ejemplo, una pequeña línea, que viaje a través de la pantalla (es decir, una flecha) parece más apropiado usar el comando PLOT que crear varios UDG's; uno por cada uno de las 4 direcciones en las que la "flecha" se podría desplazar. 

Entonces, ¿como combinamos el uso de ambos modos gráficos, en función de lo que hay en pantalla?.

Pues lo más sencillo es , dado un "carácter" o UDG en pantalla (posiciones X e Y de modo "baja resolución") calcular cuales son sus coordenadas X e Y en modo "alta resolución". 

Para ello, usamos dos funciones BASIC en nuestro programa: 

Para calcular la coordenada X: 

DEF FN x(x)=x*8

El valor inicial de la posición X es la coordenada X por 8. Como cada "celda" en el modo de baja resolución es de 8x8, esta función nos devolverá la coordenada X donde se encuentra el primer pixel, en alta resolución. 

Para calcular la coordenada Y: 

DEF FN y(y)=ABS ((y*8)-168)

Dado que la coordenada Y en el modo de baja resolución, va "al revés" que en el modo de alta resolución (en baja se cuenta de arriba a abajo y en alta de abajo a arriba, en pantalla) sacamos el valor absoluto de la coordenada Y multiplicado por 8, y restando la última posición inicial de las celdas en vertical, que es 168.

Hay que recordar que estas fórmulas nos devuelven los valores iniciales de X e Y. 

La celda "completa" que representan las coordenadas de modo carácter que le hemos pasado a las funciones, son las coordenadas X e Y de alta resolución más 8 (bits).

En el código de ejemplo de abajo, calculamos las coordenadas de alta resolución, y pintamos una X en la "celda" a la que pertenecen. 

El código fuente en BASIC, del ejemplo también lo puedes descargar aquí

Después del código hay un vídeo ilustrativo.

10 CLS : LET lx=0: LET ly=0: LET hy=0: LET hx=0
20 DEF FN x(x)=x*8
30 DEF FN y(y)=ABS ((y*8)-168)
40 INPUT "Entra coor. Y :";ly
50 INPUT "Entra coor. X :";lx
60 IF lx<0 OR lx>31 OR ly<0 OR ly>21 THEN PRINT AT 2,2;"Val. no val. 0>X<32. 0>Y<22": GO TO 40
70 PRINT AT 2,2;"              ": PRINT AT 2,2;"Lo X: ";lx
80 PRINT AT 3,2;"              ": PRINT AT 3,2;"Lo Y: ";ly
90 LET hx=FN x(lx): LET hy=FN y(ly)
100 PRINT AT 4,2;"              ": PRINT AT 4,2;"Hi X: ";hx
110 PRINT AT 5,2;"              ": PRINT AT 5,2;"Hi Y: ";hy
120 INPUT "Pintar X ?";z$
130 IF z$="s" THEN GO SUB 200
140 INPUT "salir ? s/n ";z$
150 IF z$="s" THEN GO TO 180
160 PRINT AT 2,2;"                           "
170 GO TO 10
180 STOP
190 RETURN
200 FOR a=0 TO 7
210 PLOT hx+a,hy+a
220 PLOT hx+a,(hy+7)-a
230 NEXT a
240 RETURN 




martes, 22 de diciembre de 2015

Ejemplo de cursor en pantalla

He aquí un ejemplo de como podemos implementar un cursor en pantalla. 

Está pensado para trabajar, como un cursor en pantalla de modo texto, al uso de lo que estamos acostumbrados. 

Para realizar el cursor, tenemos que tener en cuenta tanto las características del BASIC como las limitaciones en pantalla a la hora de imprimir texto. 

Es decir, tenemos que gestionar dos cosas:

1) El texto que se encuentra en las coordenadas del cursor.

2) Los atributos de dicho texto (color del texto, color de fondo, brillo e intermitencia). 

El cursor debe desplazarse por la pantalla sin modificar el valor original de la celda donde se encuentre. Para ellos tenemos que tener en cuenta los puntos 1 y 2 cuando movamos dicho cursor. 

Para esto usaremos dos funciones del BASIC; 

1) SCREEN$ para obtener el carácter que está en las coordenadas X,Y.

2) ATTR para obtener sus atributos (color, fondo brillo e intermitencia). Usa el mismo mecanismo de coordenadas X e Y. 

La función SCREEN$ nos devuelve (solo) el carácter que está en la celda X,Y. No devuelve ni sus atributos (que se obtienen con ATTR) ni valor cuando el carácter en la celda es un UDG

Para obtener un valor si es un UDG hay una manera, que se describe en otro ejemplo

El valor que nos devuelve ATTR es un valor numérico que se descompone de la siguiente manera:
  • Si el valor es superior a 128, el carácter está en modo intermitente (FLASH 1).
  • Si el valor es superior a 68, el carácter tiene brillo (BRIGHT 1). 
  • El valor de ATTR (si toca restar porque es mayor de 68 y/o 128) dividido entre 8 nos da el color del fondo (PAPER). 
  • El valor de ATTR menos el valor del fondo (PAPER) nos da el color del carácter (INK). 

Extracto del programa de ejemplo: 
LET f=0
IF z>128 THEN LET f=1: LET z=z-128
LET b=0
IF z>64 THEN LET b=1: LET z=z-64
LET p=INT (z/8)
LET co=z-(p*8)
PRINT FLASH f; INK co; BRIGHT b; PAPER p;z$

La filosofía del cursor es sencilla:

1) Guardamos en dos variables el valor de la celda y sus astributos.

2) Imprimimos el cursor encima (OVER 1) ,intermitente (FLASH 1) y con brillo (BRIGHT 1) para que se vea fácil en el ejemplo.

3) Al movernos, restauramos el valor de la celda donde estábamos.

Llenamos la pantalla de caracteres generados de forma aleatoria y vemos como funciona el cursor. En la parte de abajo nos da una linea de ayuda con las teclas para mover dicho cursor.

He usado unos UDG de flechas en la linea de ayuda, para hacerlo más amigable.

El código BASIC lo puedes descargar de aquí.

Una versión del ejemplo pasada a código máquina la tienes aquí.

Debajo de la imagen está el listado completo por si lo quieres copiar y pegar.



















5 CLEAR
10 LET t$="Ejemplo de cursor"
20 CLS
30 BORDER 4
40 GO SUB 460
50 PRINT AT 0,INT ((32-LEN t$)/2); INK 2;t$
60 FOR y=1 TO 20
70 FOR x=0 TO 31
80 LET co=INT (RND*8)
90 IF co=2 THEN GO TO 80
100 LET ff=INT (RND*10)
110 LET f=0
120 IF ff>8 THEN LET f=1
130 LET p=INT (RND*8)
140 IF p=2 THEN GO TO 130
150 PRINT AT y,x; INK co; FLASH f; PAPER p;CHR$ (65+(1+(RND*60)))
160 NEXT x
170 NEXT y
180 LET x=0: LET y=1: LET xant=x: LET yant=y: LET z$= SCREEN$ (y,x): LET z=ATTR (y,x): PRINT AT y,x; OVER 1; FLASH 1; INK 2; BRIGHT 1;"\::"
190 PRINT AT 21,0; INK 0;"u(";CHR$ 144;"),d(";CHR$ 145;"),l(";CHR$ 146;"),r(";CHR$ 147;") o q"
200 LET i$=INKEY$
210 IF i$="q" THEN GO TO 420
220 IF i$<>"l" AND i$<>"r" AND i$<>"u" AND i$<>"d" THEN GO TO 200
230 LET xant=x: LET yant=y
240 IF i$="l" THEN IF x>=1 THEN LET x=x-1
250 IF i$="r" THEN IF x<31 THEN LET x=x+1
260 IF i$="u" THEN IF y>1 THEN LET y=y-1
270 IF i$="d" THEN IF y<20 THEN LET y=y+1
280 GO SUB 300
290 GO TO 200
300 LET f=0
310 IF z>128 THEN LET f=1: LET z=z-128
320 LET b=0
330 IF z>64 THEN LET b=1: LET z=z-64
340 LET p=INT (z/8)
350 LET co=z-(p*8)
360 PRINT AT yant,xant; OVER 0; FLASH f; INK co; BRIGHT b; PAPER p;z$
370 LET z$= SCREEN$ (y,x)
380 LET z=ATTR (y,x)
390 PRINT AT y,x; OVER 1; FLASH 1; INK 2; BRIGHT 1;"\::"
400 PRINT AT 0,26;"     ": PRINT AT 0,26;y;" ";x
410 RETURN
420 CLS : CLEAR : BORDER 7: PRINT AT 0,0; INK 0;"Adios"
430 STOP
440 DATA 0,24,60,126,126,24,24,0,0,24,24,126,126,60,24,0
450 DATA 0,16,48,126,126,48,16,0,0,8,12,126,126,12,8,0
460 RESTORE 440
470 FOR i=0 TO 15
480 READ a
490 IF i<=7 THEN POKE USR "a"+i,a
500 IF i>7 THEN POKE USR "b"+(i-8),a
510 NEXT i
520 RESTORE 450
530 FOR i=0 TO 15
540 READ a
550 IF i<=7 THEN POKE USR "c"+i,a
560 IF i>7 THEN POKE USR "d"+(i-8),a
570 NEXT i
580 RETURN



jueves, 17 de diciembre de 2015

¡ZX Retro8ogue actualizado !. Avances

¡Avances de este nuevo ZX Retro8ogue!

Ya tenemos un pequeño motor del juego capaz de: 
  • Generar un mapa aleatorio conteniendo de 1 a 4 edificios.
  • Rellenar los exteriores con árboles y rocas. 
  • Generar las salidas y entradas a otros mapas. 
  • Espacio para información de nuestro héroe. 
  • Sistema de texto emergente, para la ayuda / información en juego / inventario.
  • Depurado del motor hasta el momento.
  • Revisión del código para mejor rendimiento del compilador HiBasic a código máquina. 
  • Tamaño de código. Actualmente está en 10Kb de BASIC y 14Kb compilado a mcode. 
  • Pasados los UDG a decimal (¡espacio!).
  • Optimizaciones varias de rendimiento (bucles y re-utilización de variables).

Por el momento, el motor en su estado actual está probado tanto en BASIC como en código máquina.

Podéis descargar del código fuente de este enlace (formato tap) y la cinta con su cargador y su código máquina, en este otro enlace (formato tap).

Una vez ejecutado y cargado, puedes usar "h" para ver el sistema de texto emergente. con "q" salimos del programa. El efecto de vuelta al mapa desde la ventana de texto emergente, es muy chulo :D.

Puedes descargar una grabación hecha en el emulador Fuse desde aquí. Para visualizarla usa el  menú "File/recording/Play".


¡ Seguiré informando de los avances de este nuevo ZX Retro8ogue !.

lunes, 14 de diciembre de 2015

BASIC, no puedo localizar UDG's con SCREEN$ !!. He aqui una opción buena bonita y barata.

Una de las funciones más útiles que tiene el BASIC de nuestro ZX Spectrum es SCREEN$. 

Gracias a esta función podemos saber que carácter se encuentra presente en la pantalla, dadas las coordenadas X e Y.

En el modo de "texto" tenemos 22 líneas (Y) y 32 columnas (X). Es muy cómodo, utilizar SCREEN$ como mecanismo de detección de colisiones en un juego en el que hallamos usado caracteres (de 8x8) como gráficos. 

Como se puede ver en !Como me gustan los UDG! es muy sencillo usar los 20 caracteres de gráficos de usuario que nos facilita el Spectrum, para hacer nuestros propios sprites de 8x8. 

Los podemos usar en pantalla, y todo bien, hasta que se nos ocurre la brillante idea de usar SCREEN$ para obtenerlos de pantalla. SCREEN$ nos devolverá un vacío, dado que no es capaz de gestionar los UDG. Nuestro gozo en un pozo. 

He aquí unas cuantas líneas de código para tener nuestro propio "SCREEN$" y poder identificar los UDG en pantalla. 

La idea es la misma, le damos unas coordenadas de linea/columna, y el código nos devuelve el código UDG al cual pertenece. Si la variable de control (res) es un cero (0) es que el carácter que se encuentra en las coordenadas proporcionadas, no es un UDG. Si la variable de control res tiene un valor igual o superior a 144, nos está devolviendo el código del UDG que ha encontrado en esas coordenadas. 

  10 REM Localizacion de  UDG por coordenadas x,y
  20 REM igual que el SCREEN$(y,x) pero funciona con UDG
  30 REM vamos a cargar el UDG 149 (f)
  40 FOR i=0 TO 7
  50 POKE USR "f"+i,255
  60 NEXT i
  70 PRINT AT 7,13;CHR$ 149
  80 REM le damos las coordenadas y res nos devuelve el UDG
  90 LET x=13: LET y=7
 100 GO SUB 260
 110 PRINT AT 0,0;"El UDG es el ";res
 120 GO TO 310
 130 LET res=udg
 140 LET vY=y*8
 150 LET vX=x
 160 FOR S=0 TO 7
 170 LET iY=vY+S
 180 LET BLOCK=INT (iY/64)
 190 LET CROW=INT (iY/8)
 200 LET YR=iY-(CROW*8)
 210 LET CROW=CROW-(BLOCK*8)
 220 LET ADD=16384+BLOCK*2048+CROW*32+YR*256
 230 IF (PEEK ((USR (CHR$ udg))+s))<>(PEEK (add+vX)) THEN LET res=0: RETURN
 240 NEXT S
 250 RETURN
 260 FOR i=144 TO 164
 270 LET udg=i
 280 GO SUB 130
 290 IF res>=144 THEN RETURN
 300 NEXT i
 310 STOP
Hay varias cosas que tener en cuenta:


  • Las coornedas linea/columa las fijamos igual que en el SCREEN$. Ya se encarga el cógido de las "visicitudes" y "saltos" de las direcciones de memoria en pantalla. 
  • Está optimizado para UDG's de 8x8. Con un poco de paciencia y coco, se puede adaptar a 16x16, o 32x32 de forma sencilla. 
  • Puedes leer  "Jugando a los Sprites, episodio I" para ampliar la funcionalidad.
  • Si res vale cero(0), no es un UDG.
  • Si res es 144 o más , es un UDG y res es el código del mismo. 
  • Se compara byte a byte el caracter en pantalla, con la(s) definición(es) de(los) UDG.
  • Está optimizado para el mejor rendimiento (en caso de diferencia, aborta y al siguiente). 


Ahora ya no tienes excusa para usar los UDG's en esos juegos que puedes hacer y basar tu detección de colisiones de forma muy sencilla desde BASIC.



viernes, 27 de noviembre de 2015

De BASIC a código máquina sin dolor, parte II

He estado probando diversos compiladores a Mcode (código máquina) aparte del ya mencionado Softek "FP" Full Compiler en la entrada "De BASIC a código máquina sin dolor". 

De las varias opciones con las que me he peleado (literal), os propongo como otra opción muy válida para pasar vuestros desarrollos BASIC a código máquina, el compilador "HiSoft BASIC" de Cameron Hayne y publicado en 1986 por HiSoft.

Este compilador soporta los modelos ZX Spectrum, ZX Spectrum +, ZX Spectrum 128 y ZX Spectrum Plus 2. 

Para usarlo en los ZX Spectrum +2A/B/3 deberéis usar el modo 48k. El cargador de 128 utiliza comandos CAT y nomenclatura de archivos que los 2A/B no entienden. 

En las versiones de 128k soportadas, solo ocupa 500 bytes de RAM de usuario, por lo que nos deja hasta 40k para nuestro programa BASIC. 

Estoy trabajando en una versión que cargue en los modelos +2A/B, dado que no hay una versión en cinta para ellos. Si hay una versión en disco para la el ZX Spectrum +3. 

Hay otra versión depurada por Zeljko Juric también disponible.

En general es un compilador muy robusto. Tiene varias particularidades muy buenas, como la de poder "mezclar" código máquina de una compilación con otra (al estilo de librerías o módulos) además de soportar básicamente todos los comandos de BASIC, incluyendo matrices de hasta 2 niveles (A(x,y)). Puedes definir renglones / áreas de compilación a Mcode y dejar bloques de BASIC libres. Esto es muy útil para los comandos no soportados desde el compilado. También es posible compilar por separado los segmentos DATA grandes que tengamos y "pegarlos" al código máquina principal del programa.

Los comandos no soportados, los podemos usar directamente con BASIC, yendo y viniendo del código máquina.

La versión que he utilizado es la 1.1.

La cinta viene con dos caras. Cara A para el 48k y cara B para los 128k soportados.

Hay un montón de ejemplos en la cinta y el manual de uso está muy bien; explica todo con detalle, da ejemplos y tiene un capítulo dedicado a tips para mejorar el rendimiento del código máquina generado por el compilador.

Otra funcionalidad muy curiosa, es la posibilidad de investigar las variables de nuestro programa, con el comando *T.

El propio compilador propone fijar un tipo de datos para la(s) variable(s) (con una directiva especial en una linea REM) , y así obtener un compilado aún más rápido y pequeño.

Soporta funciones de usuario, GOTO/GOSUB calculados, operaciones de floating point y otra gran cantidad de cosas.

El compilado contiene todo lo necesario para que el programa sea independiente (todos los run-time necesarios).

En las pruebas realizadas hasta ahora, con proyectos que tengo en curso, el resultado  (y la estabilidad) ha sido mejor que el obtenido con el compilador Softket "FP" Full Compiler.


miércoles, 25 de noviembre de 2015

Jugando a los Sprites, III. ¡Laser Basic!

Tanto en Jugando a los Sprites episodio I como en Jugando a los Sprites episodio II vemos visto las carencias y dificultades de trabajar con los gráficos en sprites desde BASIC y hemos definido un par de trucos / mecanismos para mejorar tanto el rendimiento como el uso de dichos sprites. 

Las técnicas descritas en el episodio I y en el episodio II son válidas para hacer bastantes cosas. Todo dependerá de la complejidad del proyecto en el que nos embarquemos. 

Por suerte, la compañía Oasis Software en el año 1986 sacó al mercado una extensión del BASIC llamada Laser Basic enfocada completamente a la programación con gráficos y sprites desde BASIC.

 Resumiendo mucho las capacidades de esta extensión de BASIC, podemos decir que: 

  • Gestiona hasta 256 sprites de cualquier tamaño.
  • Se pueden definir ventanas de pantalla y operar con ellas.
  • Se pueden realizar movimientos en pantalla tanto de los sprites como de las zonas definidas como ventanas, junto con sus atributos de pixeles, de forma muy sencilla.
  • Animaciones 
  • Optimizaciones para que el rendimiento sea bueno. 
  • Editor de Sprites avanzado. 
  • Gestión de colisión de sprites en pantalla. 
  • Toolkit para el BASIC.
  • Definición de procedimientos / funciones complejas.


Es decir, que nos brinda todo lo necesario para desarrollar "en serio" aplicaciones y juegos, con una calidad gráfica alta. 

Claro, necesitamos cargado en memoria del Spectrum, el código máquina del Laser Basic para que todo funcione. 

¿Es esto un problema?. 

Pues no, dado que tenemos el Laser Compiler que nos permite pasar nuestros desarrollos a código máquina, añadiendo todo lo necesario del Laser Basic para que el aplicativo sea completamente independiente y distribuirle.. 

¿Conclusión?.

Porque reinventar la rueda, cuando Laser Basic y Laser Compiler nos da (casi) todo lo que necesitamos para embarcarnos, por ejemplo, en el desarrollo de juegos usando solo BASIC?. 

Y ya para los sibaritas y  puristas Oasis Software lo condimentó todo con Laser Genius, un editor ensamblador/Mcode. 

He creado un Zip con todo juntito, junto con los manuales,para facilitar las cosas. 
Lo puedes descargar aquí

Ser buenos/buenas. 





lunes, 23 de noviembre de 2015

Jugando a los Sprites, episodio II.

En Jugando a los Sprites Episodio I vimos como se pinta un OVNI de 16x16 en pantalla usando escrituras directas a la memoria de vídeo.

Para ello usamos un array con las direcciones de memoria, y una rutina de pintado haciendo POKE. Ambas en su versión "bucle" y en su versión "directa", para obtener un mejor rendimiento.

Aún así, el resultado es lento  (aunque siempre lo podemos mejorar compilando el código BASIC a código máquina).

Bueno, usando UDG's podemos hacer exactamente lo mismo, y el resultado es mucho más rápido al ejecutar. Otra ventaja, es que nos queda un código mucho más pequeño, que eso teniendo solo 48k, siempre se agradece.

Esta vez, pintamos el OVNI directamente en bits.

270 REM *El Ovni en Binario
280 DATA BIN 00000111,BIN 11100000
290 DATA BIN 00001000,BIN 00010000
300 DATA BIN 00010000,BIN 00001000
310 DATA BIN 00100011,BIN 11000100
320 DATA BIN 01000011,BIN 11000010
330 DATA BIN 10000000,BIN 00000001
340 DATA BIN 10000000,BIN 00000001
350 DATA BIN 11111111,BIN 11111111
360 DATA BIN 11111111,BIN 11111111
370 DATA BIN 11111111,BIN 11111111
380 DATA BIN 10000000,BIN 00000001
339 DATA BIN 01000011,BIN 11000010
400 DATA BIN 00100011,BIN 11000100
410 DATA BIN 00010000,BIN 00001000
420 DATA BIN 00001000,BIN 00010000
430 DATA BIN 00000111,BIN 11100000
He aquí los cuatro cuadros de 8x8, que vamos a usar en 4 UDG's, de la A a la D (UDG-A,UDG-B,UDG-C y UDG-D).

Si quitas las instrucciones (DATA y BIN) ves perfectamente el OVNI pintado en 0 y 1.

Lo podíamos haber organizado de otra forma, pero parece natural poner los 4 cuadrantes seguidos, de derecha a izquierda y de arriba a abajo. 

Ahora los cargamos en los UDG. Primero los cuadrantes de arriba (A y B) y luego los de abajo (C y D).

160 REM *Cargamos nuestro srpite 16x16
170 RESTORE 280
180 FOR I=0 TO 7
190 READ A: READ B
200 POKE USR "A"+I,A: POKE USR "B"+I,B
210 NEXT I
220 FOR I=0 TO 7
230 READ A: READ B
240 POKE USR "C"+I,A: POKE USR "D"+I,B
250 NEXT I
¡Ya los tenemos listos para usar !.

La rutina que los usa, dadas unas coordenadas X e Y y la posición anterior.

120  REM *Esta es nuestra rutina de dibujo
130  PRINT AT (PY),(PX+1);CHR$ 32:
        PRINT AT (PY+1),(Px+1);CHR$ 32:
PRINT AT POSY,POSX+1;CHR$ 145:
PRINT AT (POSY+1),(POSX+1);CHR$ 147:
PRINT AT PY,PX;CHR$ 32:
PRINT AT (PY+1),(PX);CHR$ 32:
PRINT AT POSY,POSX;CHR$ 144:
PRINT AT (POSY+1),POSX;CHR$ 146
140 LET PX=POSX: LET pY=POSY
150  RETURN 
Usamos CHR$ 32 para "borrar" (espacio en blanco) y los CHR$ 144,145,146 y 147 correspondientes a UDG-A,UDG-B,UDG-C y UDG-D.

Se borran los dos cuadrantes de la derecha primero (arriba y abajo), se pintan los nuevos en la nueva posición y luego se hace lo mismo con los otros dos cuadrantes. Lo he hecho así, pero se pueden borrar todos primero, o los de la izquierda, o como más nos guste.

El programa esta vez, lo mueve directamente 10 posiciones a la derecha. El rendimiento es bueno y el "parpadeo" lo podemos disimular / ajustar al gusto, cambiando el orden en el cual borramos y volvemos a pintar. 















Aunque solo tengamos del UDG-A al UDG-U, y para un sprite de 16x16 gastemos 4, hay maneras de tener muchos más, solo hay que tener imaginación ;). 

Podemos guardar en memoria tile-sets y asignarlos a los UDG cuando los queramos usar. Así, no tenemos limitación ninguna. 

Esta vez el programa es más corto. De todas formas, puedes descargarlo de aquí o descargarlo en formato TAP.

10 REM *Jugando a los Sprites 2*
15 CLS
20 LET POSX=1
30 LET POSY=1
40 LET PX=1
50 LET PY=1
60 GO SUB 160
70 GO SUB 130
80 INPUT "Vamos a moverlo o salir (q/Q)";z$
90 IF z$="q" OR z$="Q" THEN GO TO 440
100 FOR I=0 TO 10:
LET POSX=POSX+1:
GO SUB 120:
NEXT I
110 GO TO 440
120 REM *Esta es nuestra rutina de dibujo
130 PRINT AT (PY),(PX+1);CHR$ 32:
PRINT AT (PY+1),(Px+1);CHR$ 32:
PRINT AT POSY,POSX+1;CHR$ 145:
PRINT AT (POSY+1),(POSX+1);CHR$ 147:
PRINT AT PY,PX;CHR$ 32:
PRINT AT (PY+1),(PX);CHR$ 32:
PRINT AT POSY,POSX;CHR$ 144:
PRINT AT (POSY+1),POSX;CHR$ 146
140 LET PX=POSX: LET pY=POSY
150 RETURN
160 REM *Cargamos nuestro srpite 16x16
170 RESTORE 280
180 FOR I=0 TO 7
190 READ A: READ B
200 POKE USR "A"+I,A: POKE USR "B"+I,B
210 NEXT I
220 FOR I=0 TO 7
230 READ A: READ B
240 POKE USR "C"+I,A: POKE USR "D"+I,B
250 NEXT I
260 RETURN
270 REM *El Ovni en Binario
280 DATA BIN 00000111,BIN 11100000
290 DATA BIN 00001000,BIN 00010000
300 DATA BIN 00010000,BIN 00001000
310 DATA BIN 00100011,BIN 11000100
320 DATA BIN 01000011,BIN 11000010
330 DATA BIN 10000000,BIN 00000001
340 DATA BIN 10000000,BIN 00000001
350 DATA BIN 11111111,BIN 11111111
360 DATA BIN 11111111,BIN 11111111
370 DATA BIN 11111111,BIN 11111111
380 DATA BIN 10000000,BIN 00000001
390 DATA BIN 01000011,BIN 11000010
400 DATA BIN 00100011,BIN 11000100
410 DATA BIN 00010000,BIN 00001000
420 DATA BIN 00001000,BIN 00010000
430 DATA BIN 00000111,BIN 11100000
440 REM *Final del programa



Jugando a los Sprites, episodio I.

Es sabido que el ZX Spectrum no tiene soporte hardware para gestionar sprites. Sus homónimos como el Commodore 64 y el MSX sí que tienen soporte tanto a nivel de hardware como de BASIC, para los sprites. 

Después de haber leído una estupenda publicación de Tero Heikkinen en su blog Old Machinery sobre sprites en el ZX me he animado a aplicar varios de los conceptos que Tero explica, única y exclusivamente al BASIC y añadir un par de cosas mi propia cosecha.

La idea es ejemplificar tanto lo malo como lo bueno que tenemos al tratar de usar / crear sprites en el propio Spectrum, desde BASIC.

Para usar sprites de 8x8 , podemos usar directamente los UDG desde BASIC. Fácil, sencillo de usar y con un rendimiento bueno. 

Hay que tener en cuenta que el Spectrum está enfocado a usar caracteres de 8x8, por lo que la ordenación en memoria es un poco extraña. Para no entrar en muchos tecnicismos, vamos a decir que las posiciones en memoria de la pantalla, coinciden con determinadas columnas de pixeles, como se ve en la imagen a continuación: 

















Para no liarnos mucho, podemos usar un algoritmo que nos de, las 176 posiciones (valores) que nos interesan; lo vemos más adelante. 

Bueno, yo quería pintar un OVNI en pantalla, así que haciendo un poco de trampa, usando el editor del BasinC he pintado un OVNI de 16x16.



















Luego, lo he exportado en valores decimales al BASIC. 

1020 REM *Datos del sprite 16x16 en decimal(4 cuadriculas de 8)
1030 DATA 7,224,7,224,8,16,19,200
1040 DATA 35,196,64,2,159,249,255,255
1050 DATA 255,255,159,249,64,2,35,196
1060 DATA 19,200,8,16,7,224,7,224

Ahora, vamos a hacer el pintado en 2 pasos:

1) Las posiciones de memoria válidas en una variable, para calcularlas o cargarlas una única vez.

2) Asignamos coordenadas X e Y, donde queremos pintar y llamamos al pintado. 

Las posiciones de memoria.

En la publicación de Tero, se calcula las posiciones en función ya de unas determinadas coordenadas en pantalla que quiere usar. Yo, simplemente, y por tener luego los valores "fijos", he dejado el cálculo en el programa de ejemplo. 

Acordaros que la 1ra posición de memoria para el pintado en pantalla es 16384.

490 REM * Para calcular los valores del POKE
500 FOR Y=0 TO 175
510 LET BLOCK=INT (Y/64)
520 LET CROW=INT (Y/8)
530 LET YR=Y-(CROW*8)
540 LET CROW=CROW-(BLOCK*8)
550 LET ADD=(16384+(BLOCK*2048))+(CROW*32)+(YR*256)
560 LET A(Y+1)=ADD
570 NEXT Y

Este bucle es costoso para el Spectrum. Siendo un poco listos, incluimos en el programa un segmento DATA con los 176 valores, que siempre irá mucho más rápido hacer un READ que el cálculo completo. 

1080 REM *Datos de los POKE calcualdos, 176 valores posibles*
1090 DATA 16384,16640,16896,17152,17408,17664,17920,18176,16416,16672,16928,17184,17440,17696
1100 DATA .................
¿ Y por que hacemos esto?. Bueno, porque luego vamos a usar nuestra super rutina de pintado en pantalla, asignando las coordenadas X e Y que queremos y llamándola. 

370 PRINT AT 19,1;"Con bucle"
380 FOR I=0 TO 15
390 POKE A(I+(POSY))+(POSX),0: POKE A(I+(POSY))+(POSX)+1,0
400 NEXT I
410 LET PV=POSY+DVER
420 LET PH=POSX+DHOZ
430 FOR I=0 TO 15
440 READ L: READ R
450 POKE A(I+(PV))+(PH),L
460 POKE A(I+(PV))+((PH)+1),R
470 NEXT I

La primera parte de la rutina, "borra" de pantalla lo anterior (la posicion anterior del OVNI). La segunda parte, dadas unas nuevas coordenadas, pinta el OVNI.

Como los bucles son más lentos que las instrucciones directas, y como sabemos que solo son 16 instrucciones (nuestro sprite es de 16x16) podemos sustituir el bucle por instrucciones directas, tratando de ganarle algo al rendimiento. 

Es decir, que puedes hacer los 16 POKE en 16 instrucciones diferentes, y ganar algo.

READ L
READ R
POKE A(0+(PV))+(PH),L: POKE A(0+(PV))+((PH)+1),R
READ L
READ R
POKE A(1+(PV))+(PH),L: POKE A(1+(PV))+((PH)+1),R
Así, del 0 al 15.

En el código fuente más abajo, tienes todo. 

La verdad, es que el resultado es chulo, pero más lento que el caballo del malo. ¿Como solucionar este problema de rendimiento?. Bueno, yo me he liado la manta a la cabeza y he compilado el código BASIC a código máquina y ahora sí soy un panda FELIZ. 

En el episodio II, haremos lo mismo, usando UDG y veremos si nos hace falta código máquina o no ;). 

Ah, casi se me olvidaba. Aquí os dejo un fichero TAP con el programa.

Tambíen te puedes descargar el código BASIC.















jueves, 19 de noviembre de 2015

¡ Como me gustan los UDG !

No hay nada mas 8 bit, que usar gráficos de 8 bits :D

El Spectrum nos deja usar su concepto de User Defined Grahpics (UDG) para hacer cosas muy chulas.

Tenemos desde el UDG-A hasta el UDG-U para cargar nuestros diseños usando una plantilla de 8x8 bits.

Con el método CHR$ podemos imprimir en pantalla desde la "A" (144) hasta la "U" (154), conteniendo nuestros propios caracteres.

Vamos a pintar un reloj.

Para ello, como no podía ser de otra manera, lo vamos a diseñar usando 0 y 1.

1000 DATA BIN 00011000
1010 DATA BIN 00011000
1020 DATA BIN 00100100
1030 DATA BIN 01011010
1040 DATA BIN 00100100
1070 DATA BIN 00011000
1080 DATA BIN 00011000
1090 DATA BIN 00000000
Parece nada, ¿verdad?. Pues ya verás que chulo queda. 


















La instrucción POKE USR, nos permite cargar desde la UDG-A hasta la UDG-U nuestros diseños, para luego usarlos con CHR$.

Este programa, lee el diseño, lo asigna al carácter 144 (UDG-A) y luego lo usa con un PRINT. 

Algo muy interesante, es que además, podemos usar INK, PAPER, FLASH y todas las zarandajas que nos permite el BASIC.

10 REM *Usando UDG de 1 caracter (8x8)
20 CLS : PRINT "Dos relojes": PRINT : RESTORE 1000
30 FOR I=0 TO 7
40 READ B
50 POKE USR "A"+I,B
60 NEXT I
70 PRINT CHR$ 144;" "; INK 3; FLASH 1;CHR$ 144; INK 8
80 INPUT "Pulsa para terminar";z$: GO TO 9000
1000 DATA BIN 00011000
1010 DATA BIN 00011000
1020 DATA BIN 00100100
1030 DATA BIN 01011010
1040 DATA BIN 00100100
1070 DATA BIN 00011000
1080 DATA BIN 00011000
1090 DATA BIN 00000000
9000 FLASH 0: PRINT AT 19,1;"Termine"

El reloj de la derecha, está parpadeando aunque aquí no lo veamos (hemos usado FLASH 1). 

Para usar nuestro reloj, es tan fácil como escribir PRINT CHR$ 144 en nuestro Spectrum.

Si haces un NEW, para escribir un programa nuevo, el gráfico permanecerá en memoria.



miércoles, 18 de noviembre de 2015

Haciendo uso (correcto) del Joystick desde BASIC. ZX Interface II.

¿Que sería de nuestros Spectrum sin un Joystick? 

Pues una fuente de frustración en muchos juegos y situaciones. 

Vamos a ver como hacer un uso efectivo de la entrada del Joystick desde el código BASIC. 

La mágica función INKEY$ nos deja leer las entradas del teclado y del mando en "tiempo real", pero, tiene un problema: 

¿Como identificamos que la palanca se ha girado a la derecha y que le hemos dado a disparar, a la vez?. 

Pues no podemos. 

Para hacer las cosas bien, tenemos que leer de los puertos del mando, directamente. Para ello, asignamos el valor del puerto del mando, a una variable usando IN. 

LET a = IN 61438
¿Sencillo, verdad?. Bueno tiene truco. 

La variable contendrá un valor numérico que representa una secuencia de bits. 

Dicha secuencia, tiene la información real de lo que ha pasado.

  • bit 0 : disparo 
  • bit 1 : arriba 
  • bit 2 : abajo
  • bit 3 : derecha 
  • bit 4 : izquierda 

Ah, otra cosa : 

  • El mando "1", se lee con IN 61438. 
  • El mando "2", se lee con IN 63486.

Como un ejemplo vale más que mil palabras, he aquí un sencillo programa de dibujo, que hace uso de lo que hemos explicado arriba.

1 REM  * Programa de dibujo *
5 CLS
10 REM *Posicion inicial en pantalla *
15 LET x=0: LET y=80
17 PRINT AT 19,1;"Pulsa Q para salir"
20 GO SUB 1000: REM * nos vamos a leer el puerto del mando 1*
30 IF disp=1 THEN PLOT x,y
40 GO TO 20
1000 IF INKEY$="Q" OR INKEY$="q" THEN GO TO 2000
1005 LET a=IN 61438
1010 IF a>127 THEN LET a=a-128
1020 IF a>63 THEN LET a=a-64
1030 IF a>31 THEN LET a=a-32
1040 IF a>15 THEN LET a=a-16: IF x<255 THEN LET x=x+1
1050 IF a>7 THEN LET a=a-8:     IF x>0 THEN LET x=x-1
1060 IF a>3 THEN LET a=a-4:     IF y<175 THEN LET y=y+1
1070 IF a>1 THEN LET a=a-2:     IF y<>0 THEN LET y=y-1
1080 LET disp=a
1090 RETURN
2000 CLS : PRINT AT 1,1;"Bye"
Si pulsas disparar, dejará de pintar, aunque se seguirá moviendo el cursor (invisible). 

Las mates son fáciles, vamos comprobando el valor de a, y vamos restando para sacar los valores de los bits que nos interesan. 

En la imagen, se ven las lineas rectas, porque he emulado la entrada del mando usando las teclas del teclado. 

No hay nada mejor que el olor a código por la mañana ;).