Tutorial Player/Missile

1. Introducción

A continuación se presenta una traducción al español del artículo "Player/Missile Tutorial" escrito por Christopher Chabris y publicado en septiembre de 1983 en la revista Antic N°6 Volumen 2. Las modificaciones al texto original son mínimas, en particular cada dirección de memoria a la que se hace referencia en el artículo va precedida de la etiqueta que se les otorga en el Sistema Operativo de Atari (que son las que se usan en el Mapping the Atari también), y se agrega su valor equivalente en notación hexadecimal.

2. Intensifica tu acción

La mayoría de los propietarios de computadoras ATARI han oído hablar de los gráficos Player/Missile. Los gráficos P/M se utilizan en la mayoría de los juegos populares programados en lenguaje de máquina, como Star Raiders, Jawbreaker y Pac-Man. También se utilizan en muchos programas de utilidad, como Program-Text Editor y Home Filing Manager de Atari. La capacidad de los gráficos Player/Missile está integrada en todas las computadora ATARI y no está vinculada a ningún lenguaje de programación. Los Players y los Missiles se pueden mover por la pantalla (lo que se conoce como "playfield" (campo de juego)) sin alterar su contenido. Son fáciles de crear y animar.


Los gráficos P/M están controlados por dos de los chips creados por ATARI: el GTIA y el ANTIC. El CTIA, la versión antigua del chip GTIA, es idéntico desde el punto de vista de los gráficos Player/Missile. Estos chips manejan automáticamente la operación de los gráficos P/M mientras se ejecuta un programa, por lo que un programador no tiene que escribir una rutina de manejo de pantalla compleja y detallada. Sólo necesita simples comandos POKE para controlar los gráficos P/M.
Para comprenderlos, primero debemos definir algunos términos simples:


  1. Gráficos del Playfield (Campo de juego) : El campo de juego consta de todos los gráficos "normales"; por ejemplo, gráficos producidos en varios modos gráficos usando comandos como PLOT, DRAWTO y PRINT. Tenga en cuenta que el término "campo de juego" no tiene nada que ver con si el programa es o no un juego. Tanto VisiCalc como Shamus usan gráficos de campo de juego.
  2. Registros de Hardware: Estas son direcciones de memoria especiales asignadas a los chips GTIA y ANTIC. Los valores se introducen en estos registros de la misma manera que en la memoria convencional, pero cada POKE suele tener un efecto específico e inmediato sobre un player o un missile (Como por ejemplo, cambiar instantáneamente su posición horizontal).
  3. Páginas de Memoria: Una página de memoria consiste en 256 bytes de memoria contiguos y cuya dirección inicial es divisible por 256. Las ubicaciones de memoria 1536 a 1791, por ejemplo, comprenden la Página Seis porque 1536/256 = 6.

Teniendo en cuenta estas definiciones fundamentales, en este artículo experimentaremos con un solo Player.


Dependiendo de la resolución que desee, un Player es simplemente una lista de 128 ó 256 bytes de RAM. Cada byte de la lista consta de ocho bits y cada bit corresponde a un píxel del Player. Para ayudar a visualizar esto, piense en una franja que se extiende desde la parte superior de su pantalla hasta la parte inferior, y que está compuesta por ocho franjas contiguas más pequeñas. Si el Player tiene 256 bytes de largo, la franja consta de 256 líneas horizontales. Cada línea de ocho cuadrados pequeños representa un byte de ocho bits en la memoria. La figura 1 le ayudará a visualizar esta imagen.

Puede "encender" cualquier bit de los 256 x 8 = 2048 bits que representan al Player asignándole el valor "1". Y puede “apagarlo” asignándole "0". Sin embargo, para poder hacer esto, primero debe proporcionarle información a ANTIC acerca de cómo se deben mostrar estos bits en la pantalla. Lo mejor es aprender todo esto en un proceso paso a paso. Este proceso se describe a continuación y se ilustra con un programa de muestra que muestra el Player 0 en el centro de la pantalla (los cuatro Players están numerados del 0 al 3).

3. PASO 1 Seleccione el tamaño de su Player.

Elija ya sea resolución de una o dos líneas. La resolución de una sola línea brinda a los Players la altura de un píxel del Modo Gráfico 8 (resolución alta), mientras que en la de línea doble corresponde al tamaño de un píxel del Modo Gráfico 7 (la resolución más baja). Luego elija el ancho del Player: "0" para que tenga el ancho de un píxel del Modo Gráfico 7; "1" para el doble de ese ancho; o "3" para cuadruplicar ese ancho. Para que el programa de ejemplo sea más comprensible, ponemos el valor del ancho en una variable para su uso posterior. La primera línea del programa de ejemplo asigna cero la variable "WIDTH" para que cada píxel del Player sea igual al ancho de un píxel del Modo Gráfico 7:

1000 WIDTH=0	

4. PASO 2 Reserve memoria para su Player.

Los datos que representan las imágenes del Player se almacenan en la memoria RAM. Debe estar seguro de que el área de memoria elegida para estos datos no será alterada por nada (Como por ejemplo un programa en BASIC, variables o strings, o gráficos del playfield). Por lo tanto, debe reducir el límite superior de la memoria RAM para que BASIC y el sistema operativo (SO) piensen que hay menos RAM de la que realmente está disponible (El SO o el BASIC no pueden perturbar esta parte de la memoria si se encuentra más allá del límite superior). Establezca este límite superior examinando la ubicación de memoria RAMTOP (106, $6A), que siempre contiene el número de páginas de memoria disponible, y cambiándolo a un valor más bajo. Reste 8 páginas de memoria en el caso de usar resolución de una sola línea, o 4 páginas para resolución de línea doble. Esto reserva suficiente memoria para el número máximo de Players y Missiles permitido, aunque solo usaremos un Player en el programa de ejemplo. Después de restar este bloque de memoria del límite superior de la RAM disponible, debe hacer POKE del nuevo valor del límite superior en la ubicación de memoria RAMTOP, tal como lo hacemos en la segunda línea del programa de ejemplo:

1100 A=PEEK(106):A=A-4:POKE 106,A	

5. PASO 3 Configure el Modo Gráfico del Playfield.

Ahora que ha reservado espacio de memoria para sus gráficos P/M, puede ejecutar el primer comando GRAPHICS del programa con la certeza de que la pantalla no interferirá con nuestro Player. En el programa de ejemplo, usamos el Modo Gráfico 0 del BASIC, lo que nos permite usar 24 líneas de 40 caracteres cada una. También apagamos el cursor y limpiamos la pantalla:

1200 GRAPHICS 0:POKE 752,1:PRINT CHR$(125)	

6. PASO 4 Establezca los colores del Playfield y del Player.

Elija el color de fondo y cualquier otro color que desee utilizar. El programa de ejemplo utiliza el Modo Gráfico 0, lo que permite usar simultáneamente solo tres colores (uno para los caracteres, otro para el fondo y otro para el borde). Establecemos el color del fondo del Playfield en negro por medio del comando SETCOLOR. Establecer el color de un Player es más difícil. Aunque cada Player puede tener su propio color, en BASIC este color no se puede configurar por medio del comando SETCOLOR. Debe hacer POKE usando el valor del color en las ubicaciones de memoria PCOLR0 (704, $2C0) a PCOLR3 (707, $2C3) (Colores para los Players del 0 al 3, en ese orden). El valor con el que se debe hacer POKE a estos "registros de color" viene dado por la siguiente fórmula:

COLOR = TONO * 16 + LUMINANCIA	

donde TONO varía de 0 a 15 (Corresponde al segundo parámetro en la instrucción SETCOLOR) y LUMINANCIA varía de 0 a 14 (Solo números pares). Por cierto, en lugar de usar el comando SETCOLOR, también puede hacer POKE usando los valores de los colores en los registros del Playfield, direcciones COLOR0 (708, $2C4) a COLOR4 (712, $2C8). Para el programa de muestra, elegimos TONO 3 (rojo-naranja) y LUMINANCIA 6, y el valor que debemos usar para hacer POKE es 3 * 16 + 6, o sea, 54. Las siguientes dos líneas de nuestro programa de ejemplo establecen el color tanto para el Playfield como para el Player:

1300 SETCOLOR 2,0,0
1400 POKE 704,54	

7. PASO 5 Indique a GTIA qué tan anchos deben ser sus Players.

Esto se puede lograr haciendo un solo POKE en las ubicaciones de la SIZEP0 (53256, $D008) a la SIZEP3 (53259, $D00B) para los Players del 0 al 3. Debería familiarizarse con el comando POKE, ya que es necesario para cada operación de P/Ms cuando se programa en BASIC:

1500 POKE 53256,WIDTH	

8. PASO 6 Indique a ANTIC dónde encontrar sus Players.

Se debe hacer POKE en el registro de hardware denominado PMBASE (54279, $D407) usando el número de página del inicio del área de memoria reservada para los gráficos P/M. Debe multiplicar ese número de página por 256 para encontrar la dirección de inicio real en la RAM (Recuerde que una página de memoria consta de 256 bytes). En el programa de ejemplo almacenamos el valor de PMBASE en la variable A. Luego le decimos a ANTIC dónde encontrar el comienzo de nuestra memoria reservada para gráficos P/M:

1600 POKE 54279,A
1700 PMMEM=A*256	

9. PASO 7 Determine la posición horizontal y vertical de su Player.

Los valores para el posicionamiento horizontal y vertical de un Player son independientes del Modo Gráfico actual. La posición horizontal puede variar de 0 a 255. Sin embargo, los valores del 0 al 46 harán que el Player "aparezca" más allá del borde izquierdo de la pantalla y no se verá. Del mismo modo, los valores del 209 al 255 colocarán al Player más allá del borde derecho y tampoco se verá. Las posiciones verticales van del 0 al 127 para resolución de línea doble y del 0 al 255 para resolución de una sola línea (Este rango es el número de bytes en la lista de valores que conforman al Player). Para resolución de una sola línea, las posiciones de la 0 a la 31 y de la 224 a la 255 están fuera de la parte superior e inferior de la pantalla respectivamente. Estos números se reducen a la mitad en el caso de la resolución de doble línea. Tenga en cuenta que estos valores "fuera de pantalla" variarán ligeramente según los ajustes de su televisor o monitor.

La posición vertical de un Player en realidad está determinada por el primer byte del Player que es distinto de cero (Es decir, que no está en blanco en la pantalla). Por ejemplo, si la posición vertical de un Player fuera cero y cada byte de su lista fuera igual a 255 (Es decir, los ocho bits están "encendidos"), el Player aparecería como una franja sólida de color que se extiende desde la parte superior de la pantalla hasta abajo. Si la posición vertical fuera ocho, los bytes del 0 al 79 del Player serían iguales a cero (Ocho bits "apagados"). La Figura 2 explica esto. En nuestro programa de ejemplo, para indicar una posición en el centro aproximado de la pantalla en resolución de doble línea, inicializamos las variables P0X en 128 (la mitad de 255) y P0Y en 64 (la mitad de 127) para representar las coordenadas horizontales y verticales del Player 0:

1800 P0X=128:P0Y=64	

10. PASO 8 Borre la memoria basura del área de gráficos de sus P/Ms.

Esto se logra con un bucle FOR-NEXT. Debe almacenarse un cero en cada uno de los 1024 bytes (Cuatro páginas, como determinamos en el paso 2) del área que reservamos para los gráficos P/M, así:

1900 FOR L=PMMEM TO PMMEM+1023:POKE L,0: NEXT L	

11. PASO 9 Dibuje su Player en papel cuadriculado.

Este paso requiere papel cuadriculado. O puede dibujar una cuadrícula de ocho píxeles de ancho y tantos bytes de alto como desee. Recordando que el player tiene ocho píxeles de ancho, dibuje su Player haciendo que cada bit que desee "encender" sea igual a un número uno. Si bien es posible que desee un player largo y angosto, la mayoría no tiene más de 24 píxeles de alto. Para nuestro programa de ejemplo, dibujamos un Player que es un cubo pequeño de ocho píxeles de alto y ocho píxeles de ancho (Consulte la Figura 3).

12. PASO 10 Calcule los valores decimales para cada línea de su Player.

Después de dibujar su Player, escriba los siguientes números sobre cada una de las ocho columnas de píxeles del Player (¡Debería haber solo ocho!), comenzando desde la izquierda: 128,64,32,16,8,4,2,1. Estos números corresponden al valor binario de los bits del 7 al 0 de un byte de memoria. El valor de cada fila o byte del Player se obtiene sumando los números sobre las columnas cuyas casillas en esa fila contiene un número uno. Vea la Figura 4 para ver un ejemplo usando nuestro cubo.

13. PASO 11 Ponga los valores de los bytes que dan la forma de su Player en una línea DATA.

Para el ejemplo del cubo, la instrucción DATA es:

2000 DATA 31,35,69,249,137,138,140,248	

14. PASO 12 Coloque esta línea DATA en el área de memoria del Player 0.

La Tabla 1 enumera las ubicaciones en el área de gráficos P/Ms que están reservadas para cada Player. Encuentre la resolución adecuada y baje por la columna hasta que encuentre el número que se debe sumar a PMMEM (vea el paso 6) para obtener la dirección de inicio para la lista de bytes de un Player en particular. Sume la posición vertical de este Player y obtendrá la dirección de memoria donde debe comenzar a hacer POKE usando los números almacenados en la línea DATA del paso 11. En el programa de ejemplo, se usa un bucle FOR-NEXT para hacer POKE de estos datos:

2100 FOR J=PMMEM+512+P0Y TO PMMEM+512+P0Y+7:READ BYTE:POKE J,BYTE:NEXT J	

15. PASO 13 Indique a ANTIC la resolución de sus Players.

Asigne 46 (para resolución doble) a la dirección de memoria SDMCTL (559, $22F) ó 62 para resolución de una sola línea.

2200 POKE 559,46	

16. PASO 14 Dígale a ANTIC que active los gráficos P/M.

Ahora todo está listo para "habilitar" los gráficos Player/Missile en su computadora. Haga POKE en la dirección de memoria GRACTL (53277, $D01D) con el valor 3 (Siempre debe usar este valor).

2300 POKE 53277,3	

17. PASO 15 Coloque las coordenadas horizontales de su Player en los registros adecuados

Sin este último paso, los Players estarían encendidos pero invisibles porque ANTIC podría ubicarlos en la posición horizontal 0 (Más allá del borde izquierdo de la pantalla, como vimos en el paso 7). La posición horizontal del Player se controla introduciendo esa coordenada en las direcciones de la HPOSP0 (53248, $D000) a la HPOSP3 (53251, $D003) para los Players del 0 al 3. Las últimas líneas necesarias en nuestro programa son las siguientes:

2400 POKE 53248,P0X
9999 END	

Cuando haya ingresado las 16 líneas en su computadora, escriba RUN y presione [RETURN]. Después de unos segundos, debería ver un pequeño cubo naranja aproximadamente en el centro de una pantalla negra y el mensaje "READY" en la esquina superior izquierda. Si esto no sucede, compruebe si hay errores tipográficos. Debe depurar este programa y hacerlo funcionar antes de continuar, porque lo usaremos para ilustrar los principios del movimiento de los P/Ms.
Primero, agregue a su programa una línea que leerá un Joystick conectado al Puerto Uno:

2500 J=STICK(0)	

Consulte las páginas 59 y 60 de su Manual de Referencia BASIC para encontrar los valores de la función STICK para la manipulación horizontal y vertical del Joystick. Están:


14 para Arriba, 13 para Abajo, 11 para Izquierda y 7 para Derecha


Como determinamos en el paso 15 del procedimiento de configuración, la posición horizontal inicial del Player 0 se controla introduciendo un valor en el registro de hardware HPOSP0 (53248, $D000). De hecho, esto se puede hacer en cualquier momento durante la ejecución del programa. Armados con este conocimiento, escribimos las siguientes dos líneas del programa de ejemplo:

2600 IF J=7 THEN P0X=P0X+1:POKE 53248,P0X
2700 IF J=11 THEN P0X=P0X-1:POKE 53248,P0X	

Este código bastante sencillo determina si la palanca se empuja hacia la derecha o hacia la izquierda. Luego incrementa o decrementa la variable P0X y hace POKE del nuevo valor en el registro de posición horizontal del Player 0 (HPOSP0).
El movimiento vertical, sin embargo, no es tan simple. No hay registro de posición vertical. Para mover el Player 0 verticalmente, debemos mover los bytes que lo representan a través del área reservada de memoria.
Recordando que cada Player en resolución de doble línea requiere 128 bytes de la memoria reservada, mire la Tabla 1 nuevamente. La memoria del Player 0 comienza 512 bytes contando desde la dirección almacenada en PMMEM (O 1024 bytes en resolución de una sola línea). Usaremos un bucle FOR-NEXT para mover hacia adelante en la memoria solo los bytes que contienen datos para ese Player. Como esta rutina es demasiado grande para una línea, usamos subrutinas:

2800 IF J=14 THEN GOSUB 3100
2900 IF J=13 THEN GOSUB 3200
3000 GOTO 2500	

Cuando se empuja el Joystick hacia arriba (la función STICK devuelve un valor de 14), queremos mover al Player hacia la parte superior de la pantalla. Esto significa moverlo "hacia atrás" en la memoria (recuerde que los 128 bytes del Player comienzan con el byte 0 en la parte superior de la pantalla y terminan con el byte 127 en la parte inferior). Del mismo modo, movemos los bytes "hacia adelante" en la memoria cuando se empuja la palanca hacia abajo (STICK(0) = 13).
Primero, las líneas de código para el movimiento ascendente:

3100 FOR L=0 TO 9
3110 POKE PMMEM+511+P0Y+L,PEEK(PMMEM+512+P0Y+L)
3120 NEXT L
3130 P0Y=P0Y-1
3140 RETURN	

Hacemos un bucle de 0 a 9 porque nuestro Player tiene ocho bytes de largo: 0 a 9 permite mover un byte adicional en cualquier lado. Si no se incluyeran estos bytes, el Player se copiaría un byte hacia atrás, pero el último byte aparecería dos veces, tanto en la ubicación antigua como en la nueva. El cero al final borra el valor antiguo del último byte. La línea 3110 hace este trabajo. Solo hay una diferencia de un byte entre la dirección a la que se le hace PEEK en la segunda parte y aquella en la que se hace POKE con su valor en la primera parte de la expresión. La constante 512 se reduce a 511, por lo que la dirección a la que se le está haciendo POKE está una posición más abajo que la que se le está haciendo PEEK (las variables PMMEM, P0Y y L permanecen constantes en toda la línea). Cuando esto se hace diez veces, el Player se ha movido hacia atrás una byte en la memoria, lo que resulta en un ligero movimiento hacia arriba en la pantalla.

3200 FOR L=9 TO 0 STEP-1
3210 POKE PMMEM+512+P0Y+L,PEEK(PMMEM+511+P0Y+L)
3220 NEXT L
3230 P0Y=P0Y+1
3240 RETURN	

En la línea 3200, el ciclo se invierte utilizando la cláusula decreciente STEP-1. Los valores 512 y 511 se intercambiaron en la línea 3210 para que el valor de la dirección inferior se copie en la dirección superior. Intente visualizar cómo fallaría la rutina si la línea 3200 leyera lo mismo que la línea 3100. Esto haría que el primer byte, el cero que agregamos, se copiara en todo el Player, borrándolo por completo.
Si tiene una grabadora de cassettes o una unidad de disco, GUARDE su programa. Ahora ejecútelo de nuevo con el comando RUN y mueva el cubo por la pantalla con un joystick conectado al Puerto Uno. Dado que el programa no atrapa errores (TRAP) ni establece márgenes para los Players, el mover su Player demasiado hacia cualquier lado causará un ERROR #3 (ERROR DE VALOR) porque la variable P0X será mayor que 255 o menor que 0. Solo los números en el rango del 0 al 255 se pueden almacenar en direcciones o registros de RAM. Si mueve el Player demasiado lejos de la pantalla hacia arriba o hacia abajo, éste navegará a través de la RAM y puede destruir temporalmente partes del Sistema Operativo, lo que provocará que su sistema se "bloquee". Esto no causa ningún daño a la computadora, pero es posible que tenga que apagarla y volver a encenderla si ésta se "bloquea". Así que tenga cuidado.
Estos son los fundamentos de los gráficos Player/Missile. La experimentación mejorará aún más su capacidad para utilizar esta poderosa función de las computadoras ATARI.
Listado: PMDEMO.BAS Descargar

1 REM ** PM DEMO ** ANTIC MAGAZINE **
1000 WIDTH=0
1100 A=PEEK(106):A=A-4:POKE 106,A
1200 GRAPHICS 0:POKE 752,1:? CHR$(125)
1300 SETCOLOR 2,0,0
1400 POKE 704,54
1500 POKE 53256,WIDTH
1600 POKE 54279,A
1700 PMMEM=A*256
1800 P0X=128:P0Y=64
1900 FOR L=PMMEM TO PMMEM+1023:POKE L,0:NEXT L
2000 DATA 31,35,69,249,137,138,140,248
2100 FOR L=PMMEM+512+P0Y TO PMMEM+512+P0Y+7:READ BYTE:POKE L,BYTE:NEXT L
2200 POKE 559,46
2300 POKE 53277,3
2400 POKE 53248,P0X
2500 J=STICK(0)
2600 IF J=7 THEN P0X=P0X+1:POKE 53248,P0X
2700 IF J=11 THEN P0X=P0X-1:POKE 53248,P0X
2800 IF J=14 THEN GOSUB 3100
2900 IF J=13 THEN GOSUB 3200
3000 GOTO 2500
3100 FOR L=0 TO 9
3110 POKE PMMEM+511+P0Y+L,PEEK(PMMEM+512+P0Y+L)
3120 NEXT L
3130 P0Y=P0Y-1
3140 RETURN 
3200 FOR L=9 TO 0 STEP -1
3210 POKE PMMEM+512+P0Y+L,PEEK(PMMEM+511+P0Y+L)
3220 NEXT L
3230 P0Y=P0Y+1
3240 RETURN 
9999 END	

18. Tabla 1 Valores de desplazamiento de PMMEM

Resolución de una líneaResolución de línea doble
Área sin uso* +000 -> +767+000 -> +383
Missiles+768 -> +1023+384 -> +511
Player 0+1024 -> +1279+512 -> +639
Player 1+1280 -> +1535+640 -> +767
Player 2+1536 -> +1791+768 -> +895
Player 3+1792 -> +2047+896 -> +1023

* Esta área no es utilizada por los gráficos P/Ms, por lo que puede almacenar en ella lo que se desee. Estará protegida de la misma forma que la memoria reservada para los P/Ms.

19. Tabla 2 Direcciones de memoria de los gráficos P/M

EtiquetaDirección de memoriaFunción
RAMTOP106, $6AContiene el número de páginas de RAM libres en la computadora
PCOLR0-PCOLR3 704-707, $2C0-$2C3Registros de colores para los Players del 0 al 3.
SIZEP0-SIZEP353256-53259, $D008-$D00B(H) Registros de ancho para los Players del 0 al 3.
PMBASE54279, $D407(H) Número de página de inicio del área reservada de memoria.
SDMCTL559, $22FHaga POKE acá con 62 para resolución de una sola línea o 46 para resolución de doble línea.
GRACTL53277, $D01D(H) Haga POKE acá con 3 para activar los gráficos P/M.
HPOSP0-HPOSP353248-53251, $D000-$D003(H) Registros de la posición horizontal de los Players del 0 al 3.
GPRIOR623, $26FControla las prioridades de los Players y los campos de juego.

Una "(H)" antes de la descripción de una función indica que la ubicación es un registro de hardware.