C64 a Fondo – 6502 vs 6510 Parte 7 –VIA interfaz con periféricos

Hola, en esta entrega vamos a estudiar que usan los procesadores para hablar con los periféricos (datasette, disquetera) sin tener que estar constantemente congelados esperando una respuesta de ellos.

¿Por qué necesito algo entre el 6502/6510 y un periférico?

Los procesadores tienen como características ser muy rápidos, estamos hablando de 1 millón de operaciones o ciclos de reloj por segundo en el caso del 6510. Un periférico como el datasette o la disquetera está en el orden de 300 bytes por segundo de transferencia. 

Si el procesador tuviera que esperar cada acceso de información disponible sería toda la computadora extremadamente lenta, por eso ponemos un chip en el medio para que hable con el procesador y los periféricos.

Este chip tiene como función recibir la información que el 6510 le envía, mantenerla y pasarla al periférico y también recibir la información desde el periférico, mantenerla y avisar al procesador que tiene información disponible para él. De esta forma el bus de datos se mantiene libre hasta que el procesador tenga que comunicarse con el VIA.

El VIA – Versatile Interface Adapter

El MOS 6522 conocido también como VIA es un chip que nos permite tener comunicación con dos puertos para periféricos (A y B) que manejen hasta 8 bits de información en el canal de datos cada uno, la misma cantidad que manejan el 6502 y el 6510 en su bus de datos. 

Cada puerto tiene capacidad de mantener la información recibida por el periférico (input latching) y cada bit de los puertos puede ser seleccionado como input o output, lo que permite una comunicación directa y bidireccional entre el procesador y el periférico.

Este chip posee 16 registros internos que permiten entre otras funciones:

  • Seleccionar si los pines del canal de datos son de input o output
  • Almacenar la información que entrega o recibe el procesador de modo de mantener el canal de datos vacío pero la información preservada.
  • Elegir si el VIA puede o no interrumpir al procesador cuando tiene información para entregar.

Pinout del VIA

El VIA posee 40 pines que permiten desde realizar direccionamiento, seleccionar o no el chip, hacer un reset, interrumpir al procesador y comunicarse con los periféricos. Cuando algún puerto tenga como última letra una B larga, en este diagrama significa BARRA, leer como si tuviera una barra encima o sea que el pin es Active Low, se activa cuando el voltaje va a cero (<0,4v en este caso)

VSS y VDD/VCC son los pines de Ground y 5 Volts respectivamente

PA0 a PA7 son pines de datos bidireccionales (input y output) para el primer puerto de periféricos el A, el periférico se comunica con el 6522 por esos pines.

PB0 a PB7 son pines de datos bidireccionales (input y output) para el segundo puerto de periféricos el B, el periférico se comunica con el 6522 por esos pines.

CB1 y CB2 son pines de control del puerto B permitiendo cosas como decidir en base a sus input si interrumpe o no al procesador

CA1 y CA2 son pines de control del puerto A permitiendo cosas como decidir en base a sus input si interrumpe o no al procesador

RS0 a RS3 son pines que permiten seleccionar que registro interno del VIA se accede cuándo el procesador lee o escribe los puertos de datos D0 a D7

RESB puerto de reset, el 6522 necesita de un reset cuando el 6502 es prendido por primera vez o pasar por un reset en este pin recibe la señal. (Active Low).

D0 a D7 en estos pines el procesador 6502/6510 se comunica con el 6522 para leer o escribir datos y acceder a cualquiera de los registros internos del VIA.

PHI2 en este pin se recibe la señal de reloj que es la misma que usa el 6502, al estar diseñado el 6522 para trabajar con estos procesadores todo el timing interno funciona coordinadamente con esta línea de procesadores.

CS1 y CS2B ambos pines son usados para seleccionar al chip (pines de chip enabled) cuando el CS1 es high y el CS2 es low el chip sabe que se están comunicando con él y lee o escribe en el bus de datos respectivamente.

RWB este pin específico se está realizando una lectura (high) o una escritura (low) a los pines D0 a D7.

IRQB este pin cuando está en estado low crea una interrupción al procesador para que este tome información del puerto de periférico que corresponde.

Función de input/output

La función de input output del VIA esta programada a través de los registros internos del chip.

Si queremos usar por ejemplo los puertos del PB0 al PB7 como output para darle información al periférico debemos primero seleccionar el Data Direction Register B. El mismo se selecciona a través de los puertos RS0 a RS3 en este caso poniendo los pines con los valores de 0v o Ground (<0.4 volts) en caso de tener un 0 en la tabla siguiente o 5v en caso de tener un valor 1:

RS3= 0 RS2= 0 RS1= 1 RS0= 0

Esto corresponde con el valor 2 del Register Number (DDR B). Luego de esta selección el procesador debe escribir el valor deseado a través de los pines D0 a D7. Para poner un pin en OUTPUT el valor deberá ser 1 y en INPUT deberá ser cero. Si quisiera poner todos los pines como output el procesador escribirá 1111 1111.

Luego el procesador debe enviar la información que quiere mandar de output al periférico para esto se utiliza el registro Output Register. En nuestro caso usaremos el Output Register B para seleccionarlo en los pines RS3 a RS0 poner los siguientes valores:

RS3= 0 RS2= 0 RS1= 0 RS0= 0

Que corresponden al valor de registro cero. Si quisiera tener como valores de Salida en PB7 a PB0 los valores 10101010 debería escribir estos mismos en el registros enviando esta información por los pines D7 a D0.

Estos puertos soportan input latching, que es una característica por la cual el puerto conserva el valor de input que recibió desde el periférico aunque el mismo no refresque la señal. Los datos se mantienen (aunque cambien los valores en los pines PB0 a PB7) mientras el flag CB1 de interrupción este set y se borran cuando este flag es desactivado. Para controlar si usamos o no input latching o si los pines solo reflejan el valor eléctrico que poseen en el instante usamos el registro C, Peripheral Control Register. Lo mismo aplica para el Port A.

Funciones adicionales

El VIA también provee dos timers y un Shift Register de paralelo a serial / serial a paralelo.

Timers

El Timer 1 es un contador de cuenta regresiva de 16 bits que se carga usando los registros 4 y 5 donde primero se carga la información en dos registros de tipo latch, recordando el chip la misma y luego se pasa la información a los registros del contador. Cuando este llega a cero se produce una interrupción y se pone a ground el pin de IRQB.

El Timer 2 es un contador por intervalo en el cual desde que se carga su valor inicial comienza a contar o puede también contar los pulsos recibidos en el pin PB6 del Puerto B.

Los modos de ambos Timers se setean con el registro B el Auxiliar Control Register.

Shift Register

El shift register es un registro que va a rotar los 8 bits que posee a través del pin CB2 o puede cargarse (input) a través del pin CB1 o también vaciarse (output) a través del mismo pin CB1. Muy útil para hacer conversiones serial/paralelo paralelo/serial.

El timing del shift register puede ser controlado por el Timer 2, el reloj del sistema PHI2 o un reloj externo.

Cómo funciona el de la Commodore 64 el VIA

¿Dónde usaba Commodore al VIA?

Commodore usó el VIA en diferentes productos no así en la Commodore 64 donde uso el CIA o MOS 6526 que revisaremos en un próximo artículo. Podemos ver a continuación los motherboard del VIC 20 y la disquetera 1541 donde sí fue utilizado.

El VIA en funcionamiento

Para ver visualmente como conectar un VIA al 6502 y al 6510 les dejo como siempre un video en detalle de la serie. En este vamos a ver cómo se conectan los pines, que código máquina escribir para comunicarnos con los registros internos y un ejemplo de output utilizando Leds. Todo programado directamente en código máquina.

6522 VIA Versatile Adaptive Interface – 6502 vs 6510 parte 7

Artículos en la serie C64 a Fondo

A continuación les dejo los links a los artículos anteriores de la serie

Introducción

Parte 1 – El módulo de reloj

Parte 2 – Pinout 6510 y 6502

Parte 3 – Codeando a Mano la Primera Instrucción de Código Máquina

Parte 4 – Primer Programa desde EEPROM

Parte 5 – I/O Pins del Procesador

Parte 6 – MOS 6503 Una Rareza

Referencias

A continuación les dejo algunos links donde profundizar el tema

Video de la serie 6502 vs 6510 Parte 7 – El VIA

MOS 6522 Versatile Adaptive Interface VIA – 6502 vs 6510 parte 7

Libros y artículos 

Libro Machine Language for the Commodore 64,128,  y otras Computadoras Commodore Anexo I

MCS6522 VERSATILE INTERFACE ADAPTER

W65C22 (W65C22N and W65C22S) Versatile Interface Adapter (VIA) Datasheet 

Commodore 64 clock using the CIA TOD clock (from “Compute!”) | Retro64 

Referencias de velocidades de transferencias de periféricos:

C64/C128 I/O performances 

Y como siempre la serie de Ben Eater del 6502

Build a 6502 computer | Ben Eater 

Todos los ejemplos de código de los videos los pueden encontrar en:

https://github.com/carlinhocr/6502_vs_6510

C64 a Fondo – 6502 vs 6510 Parte 5 – I/O Pins del procesador

Continuamos este estudio comparativo del 6502 vs el 6510 en este caso descubriendo la principal diferencia entre el 6510 y el 6502 los pines de Input/Output que posee el 6510 y no posee el 6502.

Les dejo el link al articulo anterior en la serie, y al final como siempre los links a todos los artículos de la misma.

Parte 4 – Primer Programa desde EEPROM

Qué son los I/O Ports

La I/O ports son 6 puertos/pines/registros que pueden funcionar como Input o como Output en el procesador 6510. 

P0 a P5 Pines de I/O Port. Este procesador en su más marcada diferencia presenta en estos pines 6 conexiones bidireccionales con periféricos como si fuera un pequeño 6522 VIA o un 6526 CIA. 

Cómo se utilizan

Para poder utilizarlos se debe especificar si los vamos a usar como Input o Output. Esto se hace de una forma muy particular. El procesador 6510 a diferencia de todo el resto de la línea 6500 utiliza para sí dos direcciones de memoria que nunca pueden ser parte de la RAM o ROM y son las primeras dos direcciones la $0000 y la $0001.

La dirección $0000 es llamado DATA DIRECTION Register. En este vamos a especificar para cada uno de los seis pines si lo queremos utilizar como Input o como Output. Si el valor es 0 queda como Input y si es 1 queda como output. Si lo vamos utilizar como Input conectarlo con una resistencia a 0 Volts o +5 Volts mientras no se utilice para no tener inputs flotando. Esta especificación se realiza escribiendo un byte a la dirección $0000.

BitPinInput/Output
000/1
110/1
220/1
330/1
440/1
550/1
6No UtilizadoNo Utilizado
7No UtilizadoNo Utilizado

Por ejemplo si quisiéramos habilitar el pin 2 y el 4 como Output y el resto como Inputs deberíamos escribir el número binario %00010100 correspondiente al hexadecimal $18. Podríamos configurar al 6510 con la siguiente instrucción en assembler

lda #%00010100 ; cargamos al acumulador  el número en binario

  ; antecedido por % para indicar que está em binário

; y # para indicar que es un número y no una posición

; de memoria

sta $00 ;guardamos el contenido del acumulador en el 

;registro interno de memoria DATA DIRECTION REGISTER

Si ahora queremos leer la información de los I/O registers necesitamos utilizar otra dirección de memoria la $0001 también llamado de I/O port register.

BitPinInput/Output
000/1
110/1
220/1
330/1
440/1
550/1
6No UtilizadoNo Utilizado
7No UtilizadoNo Utilizado

Podemos cargar los valores de este registro al acumulador con el siguiente comando para leer por ejemplo:

Lda $01 ; lee el I/O port register

Sta $0400 ; escribe el contenido del acumulador en la posición

; de memoria $0400

Para poder escribir en este registro y por consecuencia cambiar el valor de los pines con valor 1 a +5 Volts o High usamos el siguiente programa assembler.

lda #%00111111 ;va a escribir todos los bits del I/O port para que 

;sus pines sean +5 Volts

sta $01 ;escribir el registro

Cómo funciona en la Commodore 64

La Commodore 64 y su procesador 6510 pueden direccionar hasta 65536 direcciones de memoria, los famosos 64KB, pero ¿cuántos kb tenemos en la Commodore?

KbytesFunción
64RAM
8BASIC ROM
8KERNAL ROM
4Character Generator
4I/O (Vic II, SID, CIA 1 y 2, Puerto de Cartucho)

Esto nos da 64+8+8+4+4 = 88Kb para direccionar, lo cual es un problema ya que tendríamos 24Kb que no podrían ser direccionados, ahí entran los I/O Ports para ayudarnos.

Estos cumplen dos funciones: manejar parte del datasette y direccionar esta memoria extra para que puedan caber las 65536 direcciones.

Los puertos en cuanto a su configuración de Input o Output así se encuentran configurados en la Commodore.

BitPinInput/OutputNombre en C64
001 outputLORAM
111 outputHIRAM
221 outputCHAREN
331 outputCassette Write
440 inputCassette Sense
551 outputCassette Motor
6No UtilizadoNo UtilizadoNo Utilizado
7No UtilizadoNo UtilizadoNo Utilizado

Manejo de Memoria

Memorias que se superponen, como vimos anteriormente veamos cómo es que estas memorias se están superponiendo y en qué direcciones.

NombreDireccionesKbytesFunción 1Función 2Función 3
Low Memory$0000-$9FFF40kbRAM
BASIC$A000-$BFFF8kbRAMBASIC ROM
High Memory$C000-$CFFF4kbRAM
I/O$D000-$DFFF4kbRAMI/OCHAR ROM
KERNAL$E000-$FFFF8kbRAMKERNAL ROM

Como se superponen estas memorias se controla principalmente con los I/O ports de P0 a P2.

P0 – Bit 0 del registro $0001, LOW RAM. Si este bit es 0 tenemos acceso a la RAM si es 1 a la BASIC ROM. ¿Cuándo lo apago? ,cuando creo un programa en código máquina con lo que aprovecho 8kb extras de ram.

P1 – Bit 1 del registro $0001, HIGH RAM. Si este bit es 0 tenemos acceso a la RAM si es 1 al KERNAL ROM. ¿Cuándo lo apago? ,cuando creo un programa en código máquina y no necesito ninguna rutina del KERNAL con lo que aprovecho 8kb extras de ram

P2 – Bit 2 del registro $0001, LOW RAM. Si este bit es 0 tenemos acceso a los I/O devices (VIC II, SID, CIA, etc)  si es 1 a la ROM de character generation donde se almacenan los 2 juegos de caracteres de la commodore. Generalmente nunca debemos acceder a la ROM del CHAR GEN ya que el VIC II puede acceder a esta directamente para dibujar los caracteres en pantalla. Un motivo válido para acceder es leerla para copiarla en memoria y modificar el juego de caracteres.

Aquí tenemos otro gráfico que muestra mejor la configuración de los 3 registros y que efectos tendrían en memoria.

A continuación les incluyo un programa en BASIC que les va a permitir apagar momentáneamente el basic y el kernel escribir y leer en la RAM oculta y volver a activar el basic y el kernel sacado del libro PEEKS & POKES para Commodore 64.

Para leer la RAM escondida usar:

PRINT USR(ADDRESS).

Para escribir haciendo un POKE usar:

SYS 715,ADDRESS,BYTE

Y este es el programa en código máquina con su loader en basic:

10 DATA 165,20,72,165,21,72,32,247,183,120,165,141,252,133

20 DATA 1,160,0,177,20,168,165,1,9,3,133,1,88,104,133,21

30 DATA 104,133,20,76,162,179,32,253,174,32,138,173,32,247

40 DATA 183,32,253,174,32,158,183,165,1,41,252,133,1,138

50 DATA 160,0,145,20,165,1,9,3,133,1,96

60 FOR 1=678 TO 747: READ A: POKE I, A: NEXT I

70 POKE 785,166:POKE 786,2

Manejo de Datasette

El datasette es manejado por 3 puertos del 6510 y la CIA. Los I/O Ports P3 a P5 están conectados a las funciones de CASSETTE Write, CASSETTE Sense, y CASSETTE Motor.

P3 – Bit 3 del registro $0001, Cassette Write. Este puerto es el que se utiliza cuando vamos a grabar información al cassette enviando pulsos con la información a grabar a través de este pin.

P4 – Bit 4 del registro $0001, Cassette Sense. Este puerto mide si alguna tecla fue presionada en el datasette. Cuando vemos el famoso mensaje “PRESS PLAY ON TAPE” el procesador está esperando que presionemos la tecla play en el datasette. Al presionar esta tecla y como el puerto es un input se lee un 0 Volts y el datasette sabe que una tecla fue presionada. Como podemos presionar 3 teclas en el datasette (play, rewind, forward) y sólo tenemos una línea de Casette Sense se produce el efecto curioso de que si presionamos rewind o forward la Commodore piensa que presionamos play y trata de cargar el programa de casette.

P5 – Bit 5 del registro $0001, Cassette Motor. Este puerto activa el motor del datasette cuando se presiona una tecla en el datasette, si alguna vez hicieron la prueba de presionar el botón de play y nada funciona si no está conectado a la Commodore no es sólo por una falta de power sino por que la tecla play no hace nada sólo el pin P5 activa el motor del datasette. 

Si estudiamos el circuito exacto conectado a este puerto vemos que en realidad el procesador no prende el motor si no que activa un transistor que switchea 9 Volts para que estos sí vayan al conector del datasette y alimenten el motor

Para completar la discusión del datasette nos falta sólo el puerto de cassette read pero este está conectado al puerto /flag de la CIA1 y está fuera del contexto de los puerto I/O del 6510 y será analizado en un próximo artículo.

Estudiando los I/O Ports visualmente

Para poder estudiar visualmente cómo se conectan y funcionan los I/O ports del 6510 les dejo este video que complementa al artículo.

6502 vs 6510 IO Ports / Processor Ports – Parte 5

Artículos en la serie C64 a Fondo

A continuación les dejo los links a los artículos anteriores de la serie

Introducción

Parte 1 – El módulo de reloj

Parte 2 – Pinout 6510 y 6502

Parte 3 – Codeando a Mano la Primera Instrucción de Código Máquina

Parte 4 – Primer Programa desde EEPROM

Referencias

A continuación les dejo algunos links donde profundizar el tema:

VIDEOS

Video de la serie 6502 vs 6510 Parte 5 – IO Ports / Processor Ports

6502 vs 6510 IO Ports / Processor Ports – Parte 5

Aquí tiene acceso a toda la serie:

6502 vs 6510 estudio detallado y comparación 

PAPERS

The 6510 Processor Port | C64 OS 

6510 MICROPROCESSOR WITH I/O 

Mapping the Commodore 64 & 64C : Leemon, Sheldon : Free Download, Borrow, and Streaming : Internet Archive 

Peeks & pokes for the Commodore-64 : Liesert, H. J. (Hans Joachim), 1961- : Free Download, Borrow, and Streaming : Internet Archive 

Y como siempre la serie de Ben Eater del 6502

Build a 6502 computer | Ben Eater 

Todos los ejemplos de código de los videos los pueden encontrar en:

https://github.com/carlinhocr/6502_vs_6510

BASIC – 8 – Animaciones (3)

Con todo lo aprendido en los post anteriores vamos a realizar una pequeña animación. Para ello definiremos 2 sprites, que serán los frames del famosisimo Space Invaders:

El programa que estoy utilizando aquí es C64 Studio, un IDE de programación que entre otras cosas trae un sencillo editor de sprites que me permite exportar los DATA a lineas de BASIC, y lo pueden descargar desde aquí: https://www.georg-rottensteiner.de/en/index.html

Luego cargaremos estos valores a partir de la memoria 12288, que corresponden a los punteros 192 y 193. Para hacer una animación solamente tenemos que implementar un loop FOR, y alternamos estos valores en cada iteración.
La parte interesante que la podemos ver a continuación:

100 for sx = 24 to 255
110 poke v,sx
120 poke 2040,192 + ((sx and 8) / 8)
130 next 

El loop se encuentra entre las lineas 100-130, en la linea 110 se va actualizando la posición y en la linea 120 se especifica que frame mostrar segun esa posición.
El truco aquí esta en “+ ((sx and 8) / 8)”: este código toma el valor 1 o 0 segun el estado del bit 3 en la variable sx.
De esta forma las primeras 8 posiciones tomaran el valor 0, lo que hará que el puntero quede seteado en la posición 192, en las siguientes 8 posiciones quedará en 1, por lo que el puntero sera 193, en las siguientes 8 volverá a 0 … y así indefinidamente.
Si quisiéramos que la animacion se reproduzca mas rápido podemos checkear el estado de los bits de menor peso y si queremos que se reproduzca mas lento chequeamos los bits de mayor peso.
Aqui podemos ver como se ve nuestro alien animado:

y el listado completo a continuación:

10 v=53248 
20 poke v+21,1
30 poke 2040,192
40 for t=12288 to 12414
50 read n
60 poke t,n
70 next
80 poke v+39,1
90 poke v+1,75

100 for sx = 24 to 255
110 poke v,sx
120 poke 2040,192 + ((sx and 8) / 8)
130 next 

150 for sx = 255 to 24 step -1
160 poke v,sx
170 poke 2040,192 + ((sx and 8) / 8)
180 next 

200 goto 100

1000 data 14,7,0,14,7,0,3,12
1010 data 0,3,12,0,15,255,0,15
1020 data 255,0,60,243,192,60,243,192
1030 data 255,255,240,255,255,240,207,255
1040 data 48,207,255,48,204,3,48,204
1050 data 3,48,7,158,0,7,158,0
1060 data 0,0,0,0,0,0,0,0
1070 data 0,0,0,0,0,0,0,1
1080 data 14,7,0,14,7,0,195,12
1090 data 48,195,12,48,207,255,48,207
1100 data 255,48,252,243,240,252,243,240
1110 data 255,255,240,255,255,240,63,255
1120 data 192,63,255,192,28,3,128,28
1130 data 3,128,48,0,192,48,0,192
1140 data 0,0,0,0,0,0,0,0
1150 data 0,0,0,0,0,0,0,1

Y esto es todo por hoy, nos vemos en la próxima entrega!

BASIC – 7 – Sprites (2)

En el post anterior vimos como activar un sprite, como seleccionar el area de memoria que dara la forma a nuestro sprite, y lo posicionamos en la pantalla.
Hoy vamos a darle la forma definitiva, para ello tenemos 2 vias:

  1. Usar un editor de sprites, que nos genere los numeros necesarios
  2. Hacerlo a mano

Hoy vamos a optar por la segunda opción, mas que nada para aprender como se hacía a la antigüa usanza, despues podremos utilizar multitud de programas que nos permiten dibujar comodamente y exportar los datos al formato que deseemos

Los sprites en la C64 poseen un tamaño de 24 x 21 pixels, lo que nos da 3 bytes (24px) por 21 = 63 bytes.
Para definir un sprite “a la antigüa” dibujaremos una cuadricula de 24×21, agrupando en columnas de 8 pixels. Luego pintaremos las casillas con los puntos que componen nuestro gráfico, para finalmente convertir cada grupo de 8 pixels en un byte.
Por ejemplo, aqui tenemos un circulo solido relleno:

Cada grupo de 8 bits los convertimos primero a binario, y luego a decimal (podemos utilizar la calculadora de windows, seteandola en modo programador), de forma que el primer byte es 00000000, que en decimal es “0”, el segundo 01111110 es “126” en decimal, y asi con el resto.
A continuación tenemos el programa completo, con los 63 bytes que definen nuestra “pelota”.

10 v=53248
20 pokev+21,1: rem activamos sprite numero 1
30 poke2040,192: rem direccion de inicio 12288
40 fort=12288to12350 : rem leemos los 63 valores
45 read n
50 poke t,n
60 next
70 pokev+39,1: rem elegimos color blanco
80 pokev,100: rem posicion horizontal 100
90 pokev+1,110: rem posicion vertical 110
100 data 0, 126, 0
110 data 3,255,192
120 data 7,255,224
130 data 31,255,248
140 data 31,255,248
150 data 63,255,252
160 data 127,255,254
170 data 127,255,254
180 data 255,255,255
190 data 255,255,255
210 data 255,255,255
220 data 255,255,255
230 data 255,255,255
240 data 127,255,254
250 data 127,255,254
260 data 63,255,252
270 data 31,255,248
280 data 31,255,248
290 data 7,255,224
300 data 3,255,192
310 data 0, 126, 0

Ademas de poder posicionar nuestro sprite en cualquier parte de la pantalla, tambien podemos elegirle un color, expandirlo al doble a lo ancho, o alto, o ambos.
Para darle un color utilizaremo el registro 39 (v + 39 seria) y un número entre 0 y 15 para especificar que color queremos asignarle

Para expandir a lo ancho tenemos el registro 29, y para expandir a lo alto el registro 23. Cada uno de estos registros son de 8 bits, y cada bit se corresponde con el sprite que queremos expandir, así que por ejemplo, si queremos expandir el sprite 1 a lo ancho deberiamos tipear POKE V+29,1
y si queremos expandir el sprite 8 a lo alto lo hacemos con POKE V+23,128
Aqui podemos ver como jugamos un poco con estos registros:
Y con esto tenemos suficiente por hoy. Nos vemos en el proximo capitulo!

BASIC – 6 – SPRITES (1)

Los sprites en la C64 son objetos que podemos definir como nosotros deseemos, y que se pueden posicionar en cualquier parte de la pantalla. Al ser manejados por hardware no tenemos que preocuparnos por preservar el fondo de pantalla, ni mover bytes, pintarlos ni nada que en otras computadoras es tarea corriente.
La C64 puede manejar hasta 8 sprites, y cada uno de ellos se les puede asignar un color independiente, expandirlos en el eje X o Y, posicionarlos en cualquier parte de la pantalla, y un monton de cosas mas.
Cada sprite tiene un tamaño de 24×21 pixels, y ocupa 63 bytes en memoria

En este pequeño ejemplo vamos a definir un sprite cuadrado (todo con el valor 255, que en binario es 11111111), y vamos a ver como lo podemos mover, expandir y cambiar el color.
Primero vamos a ingresar este programa en la c64, con el que vamos a poder ejemplificar varios conceptos 😀

10 v=53248 
20 poke v+21,1
30 poke 2040,16
40 for t=1024 to 1087
50 poke t,255
60 next
70 poke v,100
80 poke v+1,110

En la primera linea seteamos la variable v con el valor 53248. Este valor corresponde al primer registro de los 46 que posee el chip de video de la C64 (el VIC2). De esta manera solamente tenemos que recordar una posición de memoria, y ciertos registros claves.
El primer registro aparece en la linea 20, el número 21, y corresponde al numero de sprite activo, en este caso el número 1. Luego aparece una dirección de memoria (2040) con el que podemos especificar que segmento de la memoria del VIC2 queremos utilizar para definir la forma de nuestro sprite.

NOTA acerca del direccionamiento de memoria del VIC2:
Si bien el 6510 (el microprocesador de la C64) puede direccionar hasta 64kb de memoria, el VIC2 solo puede direccionar 16kb. Por razones que escapan a esta introducción, el VIC2 se puede configurar para que utilize cualquiera de los 4 bancos de 16Kb, y por defecto cuando encendemos la C64 esta configurado para trabajar en el banco 0 (el bloque de memoria que va de 0 a 16384)

En la dirección de memoria 2040, entonces, podemos especificar el lugar donde se almacenarán los valores que daran forma a nuestro Sprite, en segmentos de 64 bytes. Para nuestro ejemplo utilizamos el valor 16, que multiplicado por 64 nos da … 1024… mhmmm..
Eso lo vimos en un post anterior, en el que imprimíamos en pantalla utilizando POKEs directamente en la dirección de video del VIC2.
La idea de utilizar la memoria de video es mostrar como se va llenando la memoria con los valores que necesita el sprite (lineas 40 – 60), y como podemos modificarlos y ver como se reflejan esas modificaciones en la figura del sprite. Por supuesto que en un juego real no vamos a utilizar esa direccion, porque donde borramos la pantalla o actualizamos cualquier valor se nos rompe el sprite.
Finalmente en las lineas 70 y 80 posicionamos el sprite en pantalla.
Si ejecutamos el programa obtendremos el siguiente resultado (observen como se modifica el sprite cuando altero las 2 primeras lineas de texto en pantalla)

Observese tambien que se ve un puntito que representa el cursor.
En la siguiente entrega ya vamos a darle una forma mejor, y vamos a mostrar el uso de los diferentes registros.

BASIC – 5 – Leer el joystick para mover objetos

En el post anterior vimos como mover un caracter por la pantalla, pero habia algunas limitaciones, como por ejemplo que no podiamos realizar diagonales, y que cada vez que queríamos avanzar debíamos pulsar repetidamente las teclas.

Hoy vamos a hacer el mismo programa, pero controlando la pelota con el joystick conectado en el port 2, pero primero… un poco de teoria.

Para leer las posiciones del joystick tenemos las posiciones de memoria 56321 (port 1) y 56320 (port 2).
los bits 0,1,2,3 corresponden a las direcciones (arriba, abajo, izquierda, derecha) y el bit 4 corresponde al boton de disparo.
Estando el joystick en posicion central, sin pulsar disparo estas direcciones nos devuelven el valor 127, que corresponde a %01111111, y a medida que vamos moviendo el joystick y pulsando el disparo se ponen en 0 los bits correspondientes.
Podemos ver rapidamente como se comportan los valores con un sencillo programita de 3 lineas

20 j=peek(56320)
30 print j
40 goto 20

Copia este programa y cuando lo ejecutes comenzara a imprimir el valor 127. Conecta un joystick al port 2 y veras como varia este valor segun las direcciones que muevas.

Vamos ahora si con el programa de la bola modificado para joystick

10 x = 10: y = 10
20 poke 1024 + (40 * y)+ x, 81
30 j = not(peek(56320)) and 127
40 if j = 0 then 30
50 poke 1024 + (40 * y)+ x, 32
60 dx = -((j and 4) / 4) + ((j and 8) / 8)
70 dy = -(j and 1) + ((j and 2) / 2)
80 x = x + dx: y = y + dy
90 poke 1024 + (40 * y)+ x, 81
100 goto 30

Como es usual, primero definimos en la linea 10 la posicion inicial en pantalla, y a continuacion imprimimos la bola. A continuacion leemos el valor del port 2 y, para poder hacer unas operaciones despues, vamos a invertir los bits, y poner el bit 7 en 0 (con esto lo que logramos es que cuando pulsemos el bit se ponga en 1, en vez de 0)

Las otras lineas interesantes son la 60 / 70, que obtienen el valor dx / dy haciendo unos calculos. La ventaja de hacerlo de esta manera sobre el tradicional chequeo de IFs es que ocupa menos lugar (importantisimo) y que ya nos resuelve la deteccion de diagonales (con estas 2 lineas evitamos 8 lineas de IFs)

En la linea 80 actualizamos las posiciones, luego imprimimos la bola, y vuelta a chequear el valor del port 2.
A continuacion podemos ver la diferencia con el programa anterior

El proximo post vamos a comenzar a utilizar sprites. Hasta la próxima!

BASIC – 4 – Leer el teclado para mover cosas

El BASIC de la C64 nos provee 2 instrucciones para ingresar información desde el teclado:

  1. INPUT : Para casos en los que queremos ingresar un nombre o un valor numérico
  2. GET : para cuando necesitamos detectar solo la pulsación de una tecla

Para lo que queremos hacer ahora vamos a utilizar el comando GET. El modo de uso es muy simple, solo toma como parámetro la variable en la que queremos que coloque el valor de la tecla pulsada.
Con este pequeño ejemplo vemos como funciona:

10 get a$
20 print a$;
30 goto 10

Si ejecutamos aparentemente no realiza nada, pero si pulsamos una tecla aparecera su correspondiente letra en la pantalla. Lo que esta haciendo aqui es un loop infinito en el que lee el valor de la tecla pulsada, lo imprime, y vuelve a leer, asi indefinidamente.
Con todo esto podemos hacer nuestro programita en el que controlamos una bola

10 px% = 10: py% = 10
20 a$ = ""
30 get a$ : if a$="" then 100
40 poke 1024+(py%*40)+px%,32
50 if a$="d" then px%=px%+1
60 if a$="a" then px%=px%-1
70 if a$="s" then py%=py%+1
80 if a$="w" then py%=py%-1
90 poke 1024+(py%*40)+px%,81
100 goto 30

Nuevamente definimos nuestras variables de posición, luego leemos el valor de la tecla pulsada, y si no se detecto ninguna salteamos la parte de código que mueve nuestro personaje.
Esto tal vez no tenga demasiado sentido en este ejemplo, pero en juegos mas complejos esta bueno que si no se pulsa ninguna tecla se puedan hacer otras cosas (no tiene sentido volver a imprimir el personaje en la misma posición)

En las siguientes lineas borramos la bola de la posición anterior (el caracter 32 es el Espacio), comprobamos si se pulsó alguna de las teclas W, A, S, D, e imprimimos la bola en la posición actualizada.
NOTA: se podria optimizar mucho más este ejemplo, pero a efectos didácticos vamos a mantenerlo simple.

Y así es como queda:

Agregando una cola que vaya guardando las posiciones anteriores podemos implementar un juego tipo Nibbles, o cualquier cosa que necesitemos mover por la pantalla

BASIC – 3 – Imprimiendo En Pantalla (2)

Vimos en el post anterior que tenemos 2 formas de imprimir en pantalla: con el comando PRINT tradicional o directamente cargando valores en la memoria de video.

La memoria de video de la C64 se compone (simplificando mucho, para lo que queremos explicar) de una matriz de 40 columnas por 25 filas de caracteres, lo que nos da un total de 1000 caracteres. En cualquiera de estas posiciones podemos imprimir lo que deseemos, simplemente colocando el valor correcto.

La posición de memoria 1024 es el inicio de nuestra memoria de video. En ella se encuentra el caracter que corresponde a la esquina superior izquierda de la pantalla, y a medida que vamos incrementando esta posición los caracteres van llenando la pantalla.


Para muestra, vale este pequeño ejemplo:

10 for x = 0 to 1000
20 poke 1024 + x, 81
30 next

Aqui podemos ver como se va llenando la pantalla, de izquierda a derecha, las 1000 posiciones de la misma.


¿Y si queremos posicionar algo? Es muy sencillo, simplemente con esta formula:

m = 1024 + (40 * py) + px

Donde m es la posición de memoria, px es la columna y py es la fila donde queremos posicionar nuestro gráfico. Con toda esta información podemos escribir un pequeño programita que nos dibuja una pelota rebotando por la pantalla:

10 px% = 10: py% = 10: dx% = 1: dy% = 1
20 poke 1024+(py%*40)+px%,32
30 px% = px% + dx%
40 py% = py% + dy%
50 if px%=39 then dx% = -dx%
60 if px%=0 then dx% = -dx%
70 if py%=24 then dy% = -dy%
80 if py%=0 then dy% = -dy%
90 poke 1024+(py%*40)+px%,81
100 goto 20

px y py son la posición de la pelota; dx y dy su dirección.


Inmediatamente pintamos un espacio, porque si no lo hacemos la pelota nos dejará una estela de pelotas, luego incrementamos la posicion según la dirección en la que está yendo, hacemos las comprobaciones para que no salga de la pantalla, y finalmente imprimimos la pelota… y reinicia el ciclo.

Y el resultado es el siguiente… bueno, mas o menos, la captura de pantallas esta hecha a 15 cuadros (frames) por segundo, lo que hace que la pelota desaparezca un poco.

Partiendo de este código se puede realizar un clon de breakout o similar

En el próximo post vamos a ver como leer el teclado, ycontrolar con WASD nuestra bola