Después de adquirir un par de libros maravillosos y ponerme a devorarlos, he comenzado a desarrollar un pequeño "entorno" de herramientas para facilitar el desarrollo usando Assembler en el Commodore 64.
Este desarrollo lo estoy realizando usando la herramienta CBM .prg Studio. Dicha herramienta es muy completa y me ha facilitado mucho la vida tanto para aprender como para terminar de comprender conceptos básicos del C64.
La idea detrás de "C64DevToolKit" (así lo he bautizado) no es estrictamente tener un set de librerías que facilite la programación, sino aprovechar para aprender implementando los conceptos básicos del Assembler.
Seguro que hay muchas formas de hacer dichas librerías mucho más eficientes, pero como he detallado, la idea principal es aprender en el camino, y , sobre todo, disfrutar de ello.
En esta primera versión, se usan muchos de los conceptos básicos como son:
1) Las posibilidades del propio lenguaje ASM.
2) Uso de subrutinas y macros.
3) Definición de direcciones de memoria y variables de uso común.
4) Uso de alguna que otra facilidad del Kernal del C64.
5) ScreenCodes y caracteres PETSCII.
6) Facilidades de la herramienta CBM Prog Studio.
7) Direccionamiento y uso de memoria 16 Bits usando modo indexado y la Página 0 de memoria.
8) Y alguna otra cosa más.
La funcionalidad de esta primera versión es muy básica, pero he ordenado el proyecto al estilo "moderno", tratando de compactar y reutilizar funcionalidad; como lo haríamos con librerías "modernas".
Seguiré completando el desarrollo, con muchas más cosas, a medida que tenga la posibilidad.
Los próximos pasos serán cosas como suma, multiplicación, el uso del reloj interno, salida de valores numéricos por pantalla y lo que se me valla cruzando, que sea básico, como lo que he desarrollado hasta ahora.
Este libro, aunque tiene una pequeña introducción al Assembly y al C64, va directo al desarrollo de un juego en ASM. Este lo dejo para más adelante, dado que quiero repasar bien lo básico con respecto al C64.
Aquí voy escribiendo mis cositas y es también para referencia rápida.
El plan es ir publicando cosas sueltas, que me parecen interesantes mientras voy construyendo una librería propia en ASM, para mis desarrollos en el C64.
También iré haciendo cositas del ZX Spectrum, en medio, para no perder la costumbre.
La prioridad es muy sencilla de entender. Los Sprites van del 0 al 7 ,así que el 0 es el que mayor prioridad tiene y el 7 el de menos. ¿ En que se traduce esto ?. Pues bien, si los solapamos, el cero siempre estará completamente visible, por debajo el 1 y así hasta el 7.
En la imagen siguiente lo vemos. Al moverse los 3 y solaparse, el fantasma quede arriba del todo (Sprite 0), el Pac - Man en medio (Sprite 1), pero tapando la cruz (Sprite 2).
La gestión de colisiones, es el conjunto de instrucciones BASIC que nos permiten detectar cuando nuestros Sprites se tocan entre ellos. Esto es una parte básica de cualquier juego, y es muy de agradecer que esté soportado tanto por hardware como por el BASIC (no como otros, si te miro a ti, Spectrum).
Básicamente existen dos instrucciones para detectar las colisiones, que usaremos en algún bucle donde tengamos la lógica de movimiento.
Para detectar la colisión de un Sprite con otro Sprite:
IF PEEK(V+30)AND X = [1,2,4,8,16,32,64 o 128] THEN [acción]
Sprite 0 1 2 3 4 5 6 7
Los valores de son 1,2,4,8,16,32,64 y 128 para cada uno de los Sprites del 0 al 7. Tenemos que usar el mismo valor en ambos lados.
Es decir, si queremos controlar las colisiones del primer Sprite en nuestro ejemplo (el fantasma), debemos usar la siguiente instrucción:
IFPEEK(V+30)AND1=1THENPOKE V+1,PEEK(V+1)-1
Lo que conseguimos con esto, es parar el movimiento del primer Sprite.
Hay que tener en cuenta que el registro de colisiones queda "sucio" siempre después de usado. Si no queremos sorpresas raras, lo debemos "limpiar" antes de usarlo.
Esto se hace con la instrucción: POKE V+30,0
Puedes descargar el ejemplo para CBM .prog Studio de este enlace.
En esta publicación vamos a cubrir los aspectos básicos de los movimientos de los Sprites en pantalla. Nos basaremos en el ejemplo de la anteriorpublicación y en el proyecto de la misma.
Añadimos una pequeña ayuda en el diseño de pantalla (directamente sobre el código) y movemos las posiciones iniciales de los Sprites un poco para acoplarlos a estas nuevas líneas de texto.
250PRINT"{113} m para mover los sprites {113}"
260PRINT"{113} s para parar el movimeinto {113}"
270PRINT" {113} q para salir {113}"
Ya que tenemos la pantalla montada, ahora vamos a implementar el movimiento de los 3 Sprites.
Los límites de coordenadas de la pantalla, para el uso de los Sprites y que sean completamente visibles son:
Esquina superior izquierda :
Coordenada X : 24
Coordenada Y : 50
Esquina inferior derecha :
Coordenada X : 343
Coordenada Y : 208
Esquina inferior izquierda :
Coordenada X : 24
Coordenada Y : 229
Esquina superior derecha :
Coordenada X : 343
Coordenada Y : 50
Estas son las coordenadas máximas de las esquinas, donde el Sprite es completamente visible. Es decir, podemos usar valores inferiores o superiores, pero entonces el Sprite no será completamente visible en pantalla.
Si no hemos usado toda la cuadríacula del Sprite (los 63 bytes) no importa, la cuenta aplica. Es decir que tenemos que tener en cuenta el tamaño completo del Sprite aunque no lo hallamos usado el lienzo completamente.
Lo primero será mover los 3 Sprites de izquierda a derecha.
Una cosa que tenemos que tener en cuenta, es que, si queremos mover hacia la derecha un valor superior a 255, tenemos que hacer un pequeño truco.
Para mover los Sprites, usamos POKE tal y como se indica en esta tabla:
660REM Movimiento eje X de los Sprites
665poke v+16,0 : GOSUB590
670FOR M=0TO347
680 hx=int(M/256):lx=M-256*hx
690if hx=1thenpoke v+16,7 : rem Esto es para hacer que pueda pasar a mas de x = 256
700poke v+0,lx
720poke v+2,lx
735poke v+4,lx
740NEXT
745poke v+16,0: GOSUB590 : rem reseteamos el registro a cero y volvemos a posicionar
750RETURN
La instrucción poke v+16,7 activa los 3 Sprites para que puedan pasar a la "derecha" del valor 256. Si no lo hacemos, se moverán hasta un determinado punto (255) y volverán desde la izquierda (24).
Es por esto que sumamos y usamos 7, para activarlo sobre los 3 a la vez.
Este lío no lo tenemos para el movimiento vertical.
Las posiciones donde el Sprite es completamente visible van de la 50 a la 229.
760REM Movimiento en el eje Y de 1 Sprite
770POKE V+0,100 : remPosicion X
780POKE V+1,50 : remPosicion Y
790for M=50to229
800POKE V+1,M : remPosicion Y
810next
815for M=229to50STEP -1
818POKE V+1,M : remPosicion Y
819NEXT
820RETURN
La tabla completa de las instrucciones POKE para manejar los Sprites, está en este documentotécnico o puedes verla aquí.
En la próxima publicación veremos como manejar las colisiones de Sprites y las prioridades.
Fue un primer paso muy básico, para ponerlo en marcha en lo que respecta a un proyecto BASIC para Commodore 64.
También usamos muy por encima el diseñador de pantallas, que nos permite, dado un set de caracteres "pintar" la pantalla y luego exportarla a nuestro código BASIC.
En esta publicación cubriremos el diseño de Sprites (por encima) usando el CBM .prog Studio y los conceptos básicos de uso de los Sprites desde el BASIC.
La herramienta de diseños de Sprites del BCM .prog Studio es muy completa y facilita mucho la tarea. A medida que avancemos en las subsiguientes publicaciones iremos usando algo más de su funcionalidad.
Usaremos como base el proyecto ya comenzado en la anterior publicación que se puede descargar de este enlace.
Una vez abierto el proyecto, hacemos doble click en la hoja de Sprites bajo el arbol "Spride Data". Esto abrirá el editor de Sprites.
Vamos a dibujar 3 Sprites distintos, para luego usarlos en nuestro programa BASIC.
En el editor, podemos usar color, color de fondo, multicolor y otras posibilidades como ver como quedaría una animación.
Hay que tener en cuenta, que lo que se exporta al listado BASIC es solo la definición de datos de los Sprites, por lo que lo que hagamos con las propiedades de color y demás cosas, lo tendremos que hacer luego por código.
Estos son los 3 Sprites que hemos pintado:
En la segunda solapa, "Scatch Pad" podemos posicionarlos en una pantalla artificial para ver que tal quedarían.
En resumen, el diseñador de Sprites tiene un montón de posibilidades para el diseño gráfico.
Hay un menú de exportar , donde pasaremos la definición de los Sprites al listado BASIC:
Una vez que tenemos el código en nuestro listado, empecemos con la parte esencial del uso de Sprites.
La idea es dejar los tecnicismos aparte e ir a lo que realmente nos va a ayudar a comenzar a conseguir cosas en pantalla.
Si te interesa la parte muy técnica, en este enlace puedes descargar la guía del programador en inglés.
Cosas que tenemos que saber:
El Commodore 64 tiene soporte por hardware para Sprites, en su afamado chip gráfico VIC-II.
Este chip se controla mediante registros en memoria (POKE) a partir de una determinada dirección. Esta dirección es 53248.
Lo primero que hacemos es definir una variable (V) con valor 53248. Esto nos simplifica la vida ( V = 53248). A partir de ahí, usamos esta variable más otros valores para acceder al control del chip de vídeo y por ende, hacer cosas en pantalla con los Sprites.
Dicho esto, el primer paso es cargar en memoria los datos de definición de los Sprites.
Cada Sprite son 63 bytes. Necesitamos un puntero para decirle a la máquina de donde coger los Sprites y luego inicializarlos en su posición. Las posiciones comienzan en el valor 2040 hasta el 2047. Es decir, tenemos 8 definiciones de Sprites.
Solo 8 no es problema, porque, como veremos en las siguientes publicaciones , podemos cambiar el contenido de la definición dentro de cada uno de los 8 , a nuestra conveniencia.
En nuestro proyecto de ejemplo, cargamos los 3 Sprites con la siguiente rutina.
320REM Cargamos los Sprites
330 V = 53248 : REMDefinimos acceso al chip grafico
340 M = 832 + ((64*3)-1) : REM vamos a cargalos en mem.
350REM Necesitamos 63 bytes por3 Sprites de espacio
355RESTORE
360FOR s=832to M
370READ a : REM vamos leyendo los DATA
380POKE s,a : REM los vamos cargando en memoria
390NEXT
395RETURN
400REM Asignamos los punteros a partir de la dir. 2040
410POKE2040,13 : REM Sprite 1
420POKE2041,14 : REM Sprite 2
425POKE2042,15 : REM Sprite 3
440RETURN
El siguiente paso es activarlos.
Para ello, hay una sola instrucción para los 8 Sprites.
Los vamos a activar :
450REM Activamos los Sprites
460POKE V+21,7 : REM Activacion de los Sprites 0,1 y 2
490RETURN
Y para desactivarlos todos usamos :
500REM Desactivamos los Sprites (todos)510POKE V+21,0520RETURN
Estos valores son los que se usan para manejar la activación / desactivación de los Sprites.
Es decir que usamos siempre POKE V+21, y el valor o suma de valores que necesitamos.
Para activar los 3 Sprites usamos:
POKE V+21,(1+2+4)
En resumen : POKE V+21,7.
Esto activaría los Sprites 0, 1 y 2.
Ahora asignamos los colores a cada Sprite.
530REM Le ponemos color a los Sprites 540POKE V+39,13 : REM Sprite 1 a azul550POKE V+40,7 : REM Sprite 2 a amarillo560POKE V+41,4 : REM Sprite 3 a violeta570RETURN
Del V+39 al V+46, se asignan los colores para cada uno de los 8 Sprites.
Por último en esta publicación , los posicionaremos en pantalla, para que se vean.
580REM Ponemos los Sprites en pantalla 590POKE V+0,54 : REMPosicion X del Sprite 1600POKE V+1,100 : REMPosicion Y del Sprite 1610POKE V+2,54 : REMPosicion X del Sprite 2620POKE V+3,150 : REMPosicion Y del Sprite 2630POKE V+4,54 : REMPosicion X del Sprite 3640POKE V+5,200 : REMPosicion Y del Sprite 3650RETURN
Otra cosa interesante, cambiar el color de fondo de la pantalla.
Se realiza con la(s) siguiente instrucción(es).
20POKE53281,1 : REM Color de fondo a blanco
300POKE53281,0 : REM Color de fondo a negro
Puedes descargar el proyecto para CBM .prog Studio, de este enlace.
Un pequeño vídeo de como va quedando nuestro tutorial.
Me puse como objetivo encontrar un entorno de programación gráfico al estilo de BasinC para ZX Spectrum.
La idea es tener una GUI (Graphical User Inerface) con todas las herramientas necesarias para la programación BASIC del Commodore 64 en nuestro PC. No solamente código fuente, sino herramientas gráficas y de sprites, manuales y otro tipo de complementos que nos permitan hacer todo desde un mismo sitio.
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 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.