2600 - PROGRAMANDO JUEGOS SIN RECURSOS

o "como pudieron hacerlo!!!"

Introduccion


Un poquito de Historia


Hoy en dia existen un sin fin de consolas, computadores y demases aparatos dedicados al fin de la entretencion casera basados en los videojuegos. En los ultimos a得s el mercado en este rubro a crecido de forma desmesurada. Aparecen muchas compa張as de software dedicadas a la creacion de juegos, algunas con innovadoras ideas y otras no tan originales, contratando programadores brillantes para crear sus engines.
Es tanto el auge que pareciera que cualquiera podria programar un juego... bueno, de hechp asi es, pero solo algunos pueden hacer un buen juego.

Lo que mucha gente olvida es que no siempre fue tan sencillo programar un juego... talves ni siquiera saben que la idea, el argumento, el desarrollo y los toques finales muchas veces fueron la parte facil de todo eso... la verdadera proeza era implementarlo (casi una lucha) , y llevarlo a la realidad. Me refiero a cuando hacer un juego requeria de una persona con mucha experiencia, habilidad, imaginacion y MUY MUY MUY BUEN PROGRAMADOR.

El objetivo de este informe es mostrar de forma bastante detallada la arquitectura de una consola que marco una epoca, el Atari 2600, conocido como el prollecto STELLA dentro del ambiente de la programacion. Esta consola tuvo un auge muy grande, con decir que desde 1976, cuando hizo su aparicion, se crearon juegos initerrumpidamente hasta los años 1998-1990. Mucha gente creo su gusto por los juegos de video en esta consola y hasta marco su vida como futuros programadores, para tratar de imitar lo que veian.
Pero fue el mismo hecho de que se hicieron tantos juegos lo que provoco la caida de esta en los a得s 83 84 puesto que estos juegos eran de una calidad bastante deficiente y pobres en sus ideas.

Un aspecto importante de esta consola, que es lo que mostrare en todo el informe, es la dificultad de programar, literalmente, cualquier juego. Debido a la "sencilles" de la cosola, el programador debia de tener en mente "TODO" el juego a la hora de implentarlo pues un cambio de ultima hora podia ser desastroso o imposible en ultima instancia. Por ultimo, espero que una ves hallan leido esto, se den cuenta de todo el trabajo que hay detras de un simple juego y no se quejen de que la grafica era mala o el juego era muy simple.

Especificaciones Tecnicas

El Stella parece ser muy sencillo... de hecho lo es, y este es su hardware

El procesador

El 6502 es un procesador de uso comun muy difundido en la epoca de finales de los 70 y comienzo de los 80. Muchas maquinas lo usaron (atari, commodore, etc) y mucha gente se crio con este. Este procesador, a pesar de sus limitaciones, poseia gran poder.
Posee el 6502 solo 3 registros y con ellos debe hacer todo. Uno de estos es el registro A (acumulador) que es el registro donde se procesan los calculos. El set de instrucciones es muy amplio, debido a la gran cantidad de modos de direccionamiento de este, desde el modo inmediato hasta el indexado indirecto. Esto va totalmente en contra de la filosofia de RISC, pero su gran facilidad de programacion lo hizo favorito de mucha gente y la plataforma perfecta para muchos de los primero peque得s computadores personales.
Realmente el Stella no usaba un 6502, sino que una variante de este llamado 6507 que se diferenciaba del 6502 en poseer un bus de datos mas pequeño de solo 13 bits con lo cual podia direccionar hasta 8 Kb. Esto limitaba los juegos disponibles hasta catridges de 8 Kilos, pero luego la gente aprendio nuevas tecnicas de hardware, hasta conseguir catriges de 16 y 32 kilos que corrieran en la misma maquina.
Una de las cosas mas importantes de este procesador es el hecho de que las instrucciones que afectaban las primeras direcciones hasta la $FF son ejecutadas considerablemente mas rapido que el resto. Esto es muy importante en este caso, pues como se vera adelante el Stella manejaba todo el hardeware escribiendo en registros mapeados en estas primeras direcciones.

La Memoria en el Stella

El espacio de direcciones del Stella consiste en 8 Kb, pues el bus de datos del 6507 era de 13 bits solamente.
De estos 8 Kb, las primeras direcciones hasta el $08 tienen mapeados registros de hardware, que fisicamente estan en el TIA, y sirven para controlar casi todo desde la posicion horizontal de los jugadores hasta el ancho de los sprites. Despues vienen vienen 128 bytes de ram que van desde el $80 hasta el $FF, que dan al programador un lugar para sus variables y la pila. Y desde ahi en adelante el espacio direcciona directamente los catridges de memoria, que contienen el juego. Estos juegos podian ser tan chicos como 2kb o grandes como 8kb.

El protocolo de television


Para entender realmente el funcionamiento de esta consola hay que tambien conocer algo de como se formaran las imagenes en la television pues esto esta muy unido a la programacion en si... mas bien, en eso consiste la mayor parte de la programacion del juego.

Partamos primero definiendo un "frame". un frame consiste en 262 lineas horizontales y cada una de ellas consiste en 228 tics de reloj (3.58 Mhz). Esto quiere decir que cada linea tomara esa cantidad de tics en dibujarse. La imagen se redibuja de izquierda de arriba hacia abajo a 60 frames por segundo. El formato de un frame es el siguiente: 3 lineas de sincronia vertical y luego vienen 37 lineas de blanqueo vertical. Luego se dibujan las lineas que se veran de la television, estas consisten en 192 lineas propiamente tal y finalmente 30 lineas de Overscan (estas son virtuales, pues como lineas no existen, sino que son 30*228 tics de reloj que el rayo de electrones demora en volver hasta la esquina superior izquierda.
Cada Linea de television comienza con 68 tics de blanqueo horizontal que es el tiempo en que el rayo demora en volver al lado izquierdo de la pantalla.

El manejo de sincronizacion horizontal es tarea del hardware, pero todo la sincronizacion vertical corre por cuenta del programador que por cada frame debe: generar 3 lineas de VSYNC (sincronia vertical) 37 de VBLANK (blanqueo vertical), 192 lineas para el despliegue actual y las 30 de overscan.

La imagen de la pantalla se genera de la siguiente manera. El procesador debe ingresar los datos de la linea que esta siendo dibujada en el TIA (Television Interface Adapter). Este chip (donde residia todo el poder grafico de esta consola, verdadero abuelo de las tarjetas graficas de hoy) solo puede contener una linea, la actual. Por esa razon el procesador debe ir "un paso delante" generando la imagen de la linea. Como un ciclo de CPU es igual a tres tics del reloj del sistema, el programador solo tiene 76 ciclos de maquina por linea (228/3=76) para calcular y dibujar la imagen. La parte de todo juego que se encarga de toda esta tarea es conocida como el Kernel.
El resto de las lineas (3 de VSYNC, 37 de VBLANK y 30 de OVERSCAN) nos daran 5,320 ciclos de maquina (70 lineas *76 ciclos de maquina) para el resto de la programacion logica del juego.
Esa logica va desde calcular las nuevas posiciones de los jugadores hasta chequear las entradas de los joysticks y distintos calculos necesarios.

En el proximo capitulo, lo mas importante de este informe es una especificacion del chip TIA con sus caracteristicas completas.

TIA (Television Interface Adaptor)


Este chip fue dise帶do para servir de interface entre el 2600 y el televisor, tomando del bus de datos de 8bits mandadas por el procesador y convirtiendolas en se帶les analogas, interpretables para el televisor. La idea en mente durante su dise得 era la de proveer una manera eficiente de dibujar un playfield (campo de juego), y cinco objetos movibles, a saber 2 sprites, 2 misiles y una pelota. Todos estos son creados manipulando una serie de registros que podian ser direccionados y escritos por la CPU y cada uno de estos registros tiene sus propias capacidades y desventajas. Es el TIA el que se encarga de tomar la informacion de estos registros y desplegarla en la pantalla. Ademas los objetos movibles, como los sprites, poseen un conjunto de registros de posicion mediante los cuales el TIA "sabe" en que lugar de la pantalla deben ser desplegados.

SIncronizacion con el procesador.


COmo se menciono anteriormente, uno tiene 5,320 ciclos de maquina al final de cada frame para calcular la logica del juego y con esto uno debe arreglarselas, pero los ciclos de ejecucion tienen un largo indefinido. Antes esto la solucion que existe es la existencia del registro WSYNC, (wait sync) el cual pone al procesador en estado de halt hasta que el rayo de electrones a vuelto a la esquina superior derecha. Esta es una solucion sencilla pero el correcto uso de ella es de enorme dificultad.

Los Registros


Luminosidad y color: Para los 7 objetos de un juego (playfield, 2 sprites, 2 misiles y pelota) existen solo 4 registros de 8 bit para asignarles color y luminosidad, cuatro bit de color y 3 bits de luminosidad. La asignacion de colores para los objetos es la siguiente:

     Nombre de Registro       Objeto coloreado
     COLUMP0   	          	P0, M0 (Sprite 0, misil 0)
     COLUMP1	     		P1, M1 (Sprite 1, misil 1)
     COLUMPF            	PF, BL (Playfield, Pelota)
     COLUMBK             	BK (background)

Notese que Sprite y misil comparten color, esto es util para juegos como combat donde el disparo de cada jugador coincide con el de el tanque que este controla. :)

Playfield:

El registro PF es usado para crear la ambientacion del juego, el campo de batalla, los laberintos, bosques, etc. Este registro de "baja resolucion por asi decirlo" es el patron que usa el TIA para dibujar la mitad izquierda de la linea actual. Este registro de 20 bits se subdivide en 3 registro de 4, 8 y 8 bits correspondientemente que definen los 20 bits izquierdos del campo de juego. El lado derecho de la pantalla se dibuja automaticamente "clonado" del izquierdo o reflejado, segun se especifique por el programador escribiendo en el registro CTLPF (control playfield)

Los Objetos "Movibles"


El 2600 posee cinco objetos graficos, cada uno posisionable horizontalmente de manera independiente. Cada uno de estos registros guarda la forma horizontal que tiene el sprite y que sera desplegada en la linea actual.A cada uno se le puede asignar una posicion horizontal distinta y moverlos con respecto a esto con solo escribir en los registros asociados a los objetos. La posicion vertical de los objetos es tratada de una manera totalmente distinta a la horizontal. Por ejemplo, si queremos que la pelota aparesca en el centro del playfield (por ejemplo en un juego de Pong) y con un ancho de 2 lineas (pixels), debemos contar hasta llegar a la linea 96, luego escribir en el registro que activa la pelota, contar 2 lineas y luego desactivar el registro hasta el siguiente frame.

Misiles (M0, M1)


Estos dos objetos graficos se dibujaran en cualquier linea de la pantalla con solo escribir un 1 en los registros ENAM0 y ENAM1. Estos objetos se posisionaran desplazados desde el borde derecho segun el valor en sus registros de posicion, y el ancho de los misiles se controla con los registros NUSIZ0 y NUSIZ1 respectivamente. Estos registro guardan ademas otras propiedades de los misiles.

La Pelota


Este objeto se comporta como los misiles y se activa facilmente con el registro ENABL. El ancho de la pelota es facilmente cambiable a 1, 2, 4 u 8 tics con solo escribir en el registro CTLPF.

Una particularidad existe en este registro. Escribiendo 1 en el registro VDELBL, la pelota demorara una linea mas en dibujarse que cuando fue activada. La razon de esto es que para tener mas tiempo de calculo, muchos programas solo actualizabaan los registros del TIA cada 2 lineas. Asi, con ese registro, los programas lograban una resolucion de 1 tic para los movientos de la pelota, mientras que el escenario y los objetos seguian teniendo una baja resolucion.

Sprites (GP0 y GP1)


Los Sprites (los jugadores) poseen las mismas caracteristicas que los demas objetos, pero ademas poseen otras mas que los hacen especialmente utiles. Un sprite se obtiene escribiendo en un registro de 8 bits (GP0 y GP1) los datos de la forma del sprite en la linea deseada. Esto permite que el sprite tenga la forma deseada (menor igual a 8 tics) y puede ser hasta del tama得 de la pantalla. Basta con poner el sprite en ENABLED e ir cambiando la forma a medida que se van dibujando las lineas en la pantalla.

Otra caracteristica importante de los sprites es que pueden ser reflejados (imagen inversa en izquierda-derecha) con solo escribir en un registro de 1 bit (REFP0 y REFP1). Esta y la capacidad de producir diferentes copias de un mismo sprite con solo escribir en el registro NUSIZ0 o NUSIZ1 son las dos grandes distinciones con los otros objetos. Ademas, el sprite y sus copias podian cambiar el ancho, desde 1 a 8 tics de reloj escribiendo en los mismos registros.

Estos tambien cuentan con retraso vertical, al igual que la pelota, poniendo un uno en VDELP0 y VDELP1, registros de 1 bit que podian ser desactivados poniendo un 0 en ellos.

Posicionamiento Horizontal. (RESP0, RESP1, RESM0, RESM1, RESBL)

Cada objeto movible (sprite, misil o pelota) tiene asociado un STROBE REGISTER el cual al ser escrito, originara que el objeto sea dibujado en esa posicion horizontal durante la segunda linea. Si el seteo del registro ocurre durante el blanqueo vertical, entonces el objeto sera dibujado junto al borde izquierdo de la pantalla.

Existe un problema. COmo el reloj de la CPU corresponde a 3 tics del sistema, y una escritura a un regristro puede demorar hasta 5 ciclos de maquina, el movimiento horizontal queda confinado a intervalos de 15 tics... extremadamente baja resolucion de movimiento!!!! pero esto puede ser solucionado conc una tecnica que explicare mas adelante.

Los Misiles ademas tienen otro registro, RESMP0 y RESMP1 que tienen por mision desactivar el misil (esconderlo) y luego modificar su posicion a la de su sprite asociado para un nuevo disparo.

Movimiento horizontal.

Esto es una de las cosas mas dificiles (como todo lo demas). Cada objeto tiene asicoado un registro de desplazamiento (HMP0, HMP1, HMM0, HMM1, HMPL) de 4 bits, que puede ir desde -7 a 8 en notacion complemento de dos. Este desplazamiento puede ser sumado a la posicion horizontal tan solo escribiendo en el registro HMOVE. Aquellos objetos que no deseen ser movidos deben tener un 0 en el registro. Ademas, pueden efectuarse multiples desplazamientos con solo escribir en HMOVE varias veces.

Por restricciones de hardware, HMOVE debe ser invocado inmediatamente luego de un WSYNC (wait for sync) para obligar a que HMOVE ocurra durante el blanqueo horizontal. Esto se debe a que los registros usados eran muy viejos y por lo tanto bastante lentos.

Escribiendo en el registro HMCLR se borran todos los movimientos acumulados.

Colisiones


El TIA puede identificar 15 distintas colisiones entre los distintos objetos, durante cada scan-line. Estas colisiones seran guardadas durante el blanqueo horizontal en flip-flop latch (para que ocurran todas las colisiones que pueden ser posibles)y luego puedan ser leidas y decodificadas por el programados leyendo el valor de las colisiones en el registro de colisiones. Este registro puede ser limpiado accesando el registro CXCLR. Puertos de Input El TIA tiene 6 puertos de input, dividos en dos grupos, 4 Dumped (INPT0-INPT3) y Latched (INPT4-INPT5). Los dumped sirven para conectar hasta 4 paddles (controles de perilla). Cuando estos estas perillas se mueven, cargan un condensador dentro de la consola. Luego el procesador manda una se帶l al codensador para descargarlo. Midiendo cuanto tiempo se demora el condensador en descargarse se puede especificar una posicion de un objeto en la pantalla mediante unos simples calculos. Los latched estan conectados a los botones de los joystick y sirven para medir cuando estos son presionados.

El PIA

Este Chip Ofrece muchas funciones utiles para la consola. Entre las mas destacadas estan dos puertos de entrada de 8 bits, un "timer" programable, y la impresionante cantidad de 128 bytes de ram.

El timer

Este es un timer programable, el cual puede ser seteado para medir eventos de tiempo por el programador. La utilidad se puede encontrar al tratar de sicronizarse con el TIA o para la sincronizacion vertical con la pantalla.
Para utilizar este basta escribir un numero en uno de los cuatro registros de timer. Para activar un timer de 6400 tics, basta escribir un 100 en TIM64T
Intervalo  		Mnemonico
1 tics        	TIM1T
8 tics		TIM8T
64 tics		TIM64T
1024 tics		T1024T
Luego de que el timer llega a cero, se mantiene en cero en un ciclo y luego este vuelve a FF. Esto tiene por objeto saber cuanto tiempo hace que el timer llego a cero.

Memoria RAM

El PIA tiene 128 bytes que se ubican entre las direcciones $80 (hexadecimal) y $FF. Antes de $80 se encuentran mapeados los registros del TIA. Normalmente los datos se ubicaban desde $80 hacia arriba y la pila desde el $FF hacia abajo esperando que nunca se pisaran uno con el otro.

Puertos de entrada y salida

El stella cuenta con dos puertos de 8 bits cada uno para comunicarse con el exterior. El puerto B se encuentra mapeado en la memoria como SWTCHB. Cuando uno lo lee, se obtiene un vector con los estados de los switches de la consola. Esta es la configuracion del mapeo.
          Bit       Switch         Significado
          D7        P1 dificultad  0 = amateur (B), 1 = pro (A)
          D6        P0 dificultad  0 = amateur (B), 1 = pro(A)
          D5/D4     no usado
          D3        color - B/N    0 = B/N, 1 = color
          D2        no usado
          D1        boton select   0 = switch presionado.
          D0        boton reset    0 = switch presionado.

El segundo puerto es usado para leer el estado de los joysticks. Ambos son mapeados en el mismo puerto sin contar con los botnes que son mapeados en los registros INPT4 e INPT5. Esta es la configuracion del puerto:
               Bit       Direccion Joystick
               D7        derecha    D
               D6        izquierda  D
               D5        abajo      D
               D4        arriba     D
               D3        derecha    I
               D2        izquierda  I
               D1        abajo      I
               D0        arriba     I

Concluciones

Finalmente queria hacer notar lo dificil que era programar para estas maquinas. La dificultad estriba en que el programador debia disenarlo todo en conjunto pues no podia empezar con los graficos y al final agregar la musica, pues agregar algo podia destruir toda la sincronizacion con la pantalla, pues muchos calculos se hacian en el mismo momento en que se dibujaban las lineas. Si no les parece suficientemente dificil, adjunto un muy muy muy facil codigo en assembler que genera una pocas lineas de colores en la pantalla. Ni siquiera tiene musica pero es suficientemente complicado para tener una idea.

Click Aqui para un ejemplo en assembler de Nick Bensema