lunes, 23 de noviembre de 2015

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.















No hay comentarios: