Programar Cartuchos
1. Introducción
Aquí explicaremos de forma fácil cómo programar los cartuchos más comunes para nuestros Atari de 8 bits; partiremos de los más estándar a algunos un poco más complejos.
También es necesario saber ensamblador y conocer un poco del equipo a nivel de Hardware.
Cabe señalar que existieron cartuchos más pequeños de tamaño 2 Kb y 4 Kb en los primeros programas y juegos comerciales, pero nos enfocaremos en los más comunes.
2. Herramientas
Como ensamblador usaremos Mads por su actualización continua y por algunas macros propias que te ahorran líneas de código cuando necesitas instrucciones de ensamblador lda/sta; para editor, dejamos que los usuarios seleccionen el que les sea más cómodo, pero tenemos guías para Notepad++ y VS Code.
También recomendamos leer ciertos libros como Atari Root y el Mapeando el Atari versión actualizada del The Mapping ATARI.
3. Cartucho de 8 Kb
Partiremos con el más común de todos, el de 8 kb; esto quiere decir que la información se podrá acceder en el rango de las posiciones de memoria de $A000 al $BFFF, o sea, vamos a tener 8.192 bytes para nuestros programas. Es importante destacar que esto es de lectura; no podemos cambiar algún valor en este rango en tiempo de ejecución.
Ahora un ejemplo simple y explicaremos más abajo ciertos detalles importantes a tener en cuenta.
1 opt f+h-
2
3 org $A000
4
5dlist
6 .by $70,$70,$70,$70,$70,$70,$70,$47
7 .wo screen
8 .by $7,$70,$6,$41
9
10screen
11 .sb " cartridge "
12 .sb " 8 KB "
13 .sb " WWW.ATARIWARE.CL "*
14
15start
16 mwa #dlist $230
17 jmp *
18
19end
20 rts
21
22 org $BFFA
23 .wo start
24 .by 0,4
25 .wo end
Para generar el archivo ROM, se hace con la siguiente instrucción desde la consola, que es lo más básico :
mads cart_8kb.asm -o:cart_8kb.rom

Ahora explicaremos las líneas importantes:
- Línea 1 : Le indicamos al ensamblador Mads que rellene con $FF los bytes restantes para completar 8.192 bytes y además que elimine la cabecera que se agrega automáticamente para los ejecutables (XEX).
- Línea 3 : La dirección definida por el sistema operativo de donde comienza un cartucho de 8 KB.
- Líneas 5 a la 8 : Definición de una lista de despliegue (diseño de pantalla).
- Líneas 10 a la 13 : Mensaje que se colocará en el diseño de pantalla definido anteriormente.
- Líneas 15 a la 17 : Programa principal y donde se activa el diseño de pantalla definido anteriormente con un ciclo infinito para que se quede en el programa.
- Líneas 19 a la 20 : Fin del programa del cartucho y que vuelva al sistema operativo del ATARI.
- Líneas 22 a la 25 : Esto es la cabecera del cartucho; siempre debe estar en la posición $BFFA. Tiene dos bytes que indican dónde inicia el programa del cartucho; siguen los bytes 0, 4, que es un cartucho estándar, y los últimos dos bytes, que es donde termina el programa del cartucho.
El ensamblador usado tiene algunas macros propias para ahorrar tiempo y que sea menos largo el código ensamblador, por ejemplo, la instrucción mwa usada en la línea 16.
Al generar el archivo, el ensamblador lo convierte a las típicas instrucciones:
Al generar el archivo, el ensamblador lo convierte a las típicas instrucciones:
lda #$00
sta $230
lda #$A0
sta $231
sta $230
lda #$A0
sta $231
Ahora, lo último a tener en cuenta: como anteriormente se dijo, no vamos a tener acceso a modificar ningún valor desde el rango $A000 hasta $BFFF, ya que esto es ROM. Entonces, si necesitamos cambiar un diseño de pantalla o datos de los bytes, deberá usar la RAM; se recomienda usar el rango $1000 al $9FFF cuando sea para un cartucho de 8 kb y su Atari sea 64 kb de RAM.
Un ejemplo: si necesito definir una pantalla y cambiar una línea de tamaño gráfico, lo normal sería que el diseño esté en la ROM y copiemos los bytes a una nueva posición de memoria en la RAM, hagamos la modificación y, por último, actualicemos la pantalla usando la nueva dirección.
¿Cómo sería en ensamblador aplicando lo descrito anteriormente?
1SDLSTL = $230
2CONSOL = $D01F
3
4 opt f+h-
5
6 org $A000
7
8dlist
9 .by $70,$70,$70,$70,$70,$70,$70,$47
10 .wo screen
11 .by $7,$70,$6,$41
12
13screen
14 .sb " cartridge "
15 .sb " 8 KB "
16 .sb " WWW.ATARIWARE.CL "*
17
18start
19 ldx #0
20copy_dlist
21 lda dlist,x
22 sta $1000,x
23 inx
24 cpx #$E
25 bne copy_dlist
26 mwa #$1000 SDLSTL
27
28key
29 lda CONSOL
30 cmp #6
31 beq key_start
32 cmp #5
33 beq key_select
34 jmp key
35key_start
36 lda #7
37 sta $100C
38 jmp key
39key_select
40 lda #6
41 sta $100C
42 jmp key
43
44end
45 rts
46
47 org $BFFA
48 .wo start
49 .by 0,4
50 .wo end
Explicaremos lo que agregamos para cambiar una línea de la lista de despliegue :
- Líneas 1 y 2 : Etiquetas definidas con un valor del Hardware de Atari correspondiente a la lista de despliegue y a las teclas de consola.
- Líneas 19 a la 25 : Copiamos nuestra lista de despliegue desde la ROM a la RAM, ubicándola en la posición de $1000.
- Línea 26 : Le decimos al sistema operativo que aplique la lista de despliegue que está en la RAM.
- Líneas 28 a la 34 : Preguntamos al sistema operativo si se presiona una de las teclas START o SELECT.
- Líneas 35 a la 38 : Si es la tecla START, entonces cambiamos en la RAM la posición de memoria $100C con el valor de $7, para que tenga letras más grandes en la línea del texto www.atariware.cl.
- Líneas 39 a la 42 : Si es la tecla SELECT, entonces cambiamos en la RAM la posición de memoria $100C con el valor de $6, para que vuelva a tener letras pequeñas en la línea del texto www.atariware.cl.
Con esto concluimos con el tema importante de cómo colocar algunas cosas en la memoria RAM para poder manipularlas después en nuestro programa que se está ejecutando como un cartucho.
4. Cartucho de 16 Kb
Continuamos con el segundo tipo de cartucho de 16kb, esto quiere decir que la información se podrá acceder en el rango de las posiciones de memoria de $8000 al $BFFF, o sea, vamos a tener 16.384 bytes para nuestros programas.
Ahora vamos con el ejemplo; algunas cosas no se explicarán, ya que se aplica lo mismo que se usa en el cartucho de 8 kb.
1SDLSTL = $230
2
3 opt f+h-
4
5 org $8000
6
7dlist
8 .by $70,$70,$70,$70,$70,$70,$70,$47
9 .wo screen
10 .by $7,$70,$6,$41
11
12screen
13 .sb " cartridge "
14 .sb " 16 KB "
15 .sb " WWW.ATARIWARE.CL "*
16
17start
18 mwa #dlist SDLSTL
19 jmp *
20
21end
22 rts
23
24 org $BFFA
25 .wo start
26 .by 0,4
27 .wo end

Esta vez no hay mucho que explicar, ya que todo es igual que el cartucho anterior, solo que ahora la posición inicial cambió a $8000, que es la línea 5.
Solo mencionaremos que estos dos tipos de cartuchos de 8 Kb y 16 Kb son los que maneja el hardware de Atari. Ahora te preguntarás qué pasa con los 32 Kb, 64 Kb, etc. En resumen, debes respetar el mismo formato de 8 kb o 16 kb, solo que usarán bancos de memoria que se irán cambiando para extraer la información y colocarla en la RAM.
5. Cartucho XEGS de 32 Kb
Este sería el tercer tipo más famoso y último que hizo Atari para su familia XL/XE. Como adelantamos, posee bancos de memoria que debes ir cambiando para ir leyendo su contenido. Explicaremos la más pequeña, que es de 32 Kb. Esta posee 4 bancos de memoria de 8 kb, o sea, 8.192 bytes por banco, pero uno de sus bancos es fijo, el cual inicia el cartucho.
- El banco de inicio sería el que está en el rango de memoria de $A000 al $BFFF y este no cambiará nunca, ya que es fijo.
- Los siguientes bancos usarán el rango de memoria de $8000 a $9FFF y se irán cambiando cada vez que nosotros queramos usando un número 0, 1, 2 y escribiéndolo en la dirección de memoria $D500.

La imagen es una ilustración de cómo es la estructura de este formato de cartucho. También se puede aplicar a sus versiones de más memoria de 64 Kb a 1MB; la única diferencia es que aumentan los bancos de memoria disponibles.
Después de la explicación de la estructura, partiremos con el primer ejemplo, el cual está enfocado solo en aprender cómo desde el banco principal hacemos el cambio de los otros bancos en el rango $A000 al $BFFF. Para ello, en cada banco colocaremos un mensaje para saber qué banco se leyó y mostrarlo en pantalla.
1FLPTR = $FC
2CDTMF4 = $22C
3COLOR2 = $2C6
4CONSOL = $D01F
5
6CPUTREC = $9
7ICCOM = $342
8ICBAL = $344
9ICBAH = $345
10ICBLL = $348
11ICBLH = $349
12CIOV = $E456
13SETVBV = $E45C
14
15 opt h-
16 org $8000
17
18 dta c'Leyendo banco 0',$FD,$9B
19:8175 .byte $FF
20
21 org $8000
22
23 dta c'Leyendo banco 1',$FD,$9B
24:8175 .byte $FF
25
26 org $8000
27
28 dta c'Leyendo banco 2',$FD,$9B
29:8175 .byte $FF
30
31 opt f+
32 org $A000
33
34start_cart
35 lda #0
36 sta FLPTR
37 sta FLPTR+1
38 sta COLOR2
39 jsr print
40 lda #1
41 sta FLPTR+1
42read_consol
43 lda CONSOL
44 cmp #6
45 bne no_consol
46 lda FLPTR
47 cmp #3
48 bne next_bank
49 lda #0
50 sta FLPTR
51next_bank
52 inc FLPTR
53 sta $D500
54 jsr print
55 lda $800E
56 rol
57 sta COLOR2
58no_consol
59 jsr wait
60 jmp read_consol
61
62wait
63 ldx #0
64 ldy #5
65 lda #$FF
66 sta CDTMF4
67 lda #4
68 jsr SETVBV
69loop
70 lda CDTMF4
71 bne loop
72 rts
73
74print
75 ldx #0
76 mva #CPUTREC ICCOM,x
77 lda FLPTR+1
78 cmp #0
79 bne print_cart
80 mwa #text ICBAL,x
81 jmp print_exit
82print_cart
83 mwa #$8000 ICBAL,x
84print_exit
85 mwa #$A0 ICBLL,x
86 jsr CIOV
87 rts
88
89text
90 dta c'presiona ',' START '*,' para ir leyendo los bancos de memoria del cartucho'
91 dta $7F,$7F,$7F,$7F,c'XEGS 32 KB',$7F,$7F,$7F,$7F
92 dta c' www.atariware.cl',$1D,$9B
93
94end_cart
95 rts
96
97 org $BFFA
98 .word start_cart
99 .by $0,$4
100 .word end_cart

Ahora lo explicamos:
- Línea 1 : Posición de memoria de dos bytes; usamos los bytes por separado: el primero FLPTR para movernos de banco y FLPTR+1 para controlar el mensaje que imprimiremos por pantalla.
- Líneas 6 al 13 : Posiciones que necesitas para poder usar el SIO para imprimir un mensaje.
- Líneas 16 al 29 : Aquí tenemos los bancos con un mensaje y rellenamos con $FF hasta cumplir con los 8.192 bytes por banco.
- Líneas 34 al 41 : Seteamos en cero las posiciones de FLPTR y mandamos a imprimir el primer mensaje; después cambiamos el valor a FLPTR+1 para que muestre los mensajes de los bancos de memoria.
- Líneas 42 al 50 : Leemos las teclas de consola y preguntamos si es START para cambiar el valor consecutivo de FLPTR; también se validó que esté en el rango correspondiente.
- Líneas 51 al 57 : Aumentamos FLPTR y se lo pasamos a $D500 para decirle que cambiamos al siguiente banco, mandamos a imprimir en pantalla el banco y, por último, cambiamos el color de fondo.
- Líneas 58 al 60 : Llamamos a la rutina de pausa.
- Líneas 62 al 72 : Rutina de pausa usando el sistema operativo para que, al presionar la tecla START, espere un momento entre mensaje.
- Líneas 74 al 87 Rutina para imprimir un mensaje en la pantalla mediante el sistema operativo usando la unidad por defecto E:; mediante FLPTR+1 discriminamos cuál mensaje va a colocar en pantalla.
Como segundo ejercicio, explicaremos cómo pasar un archivo binario de un juego a nuestro cartucho de 32 KB. Esto tiene unas limitantes por el tipo de cartucho que estamos usando; la más importante es que el juego debe estar entre las zonas de memoria $1000 A $7FFF; es porque el cartucho desde $8000 al $BFFF está activo como ROM.
Tomaremos un clásico juego llamado GORF y lo analizaremos; si es candidato, para ello usaremos el mismo emulador Altirra para activar la consola y arrastrar el juego gorf.xex al emulador.

Si ves la consola, te mostrará los segmentos de memoria que usa al cargar este juego; por lo que se ve, es un segmento largo y continuo de memoria, $2000 al $48E3; entonces usaremos como un banco y un cuarto. También nos indica dónde se inicia el juego con el segmento $02E2 y $02E3, el cual tiene dos bytes, que sería $48D3, que es lo que nos importa.
Ahora viene el proceso de cómo sacar el segmento principal del juego que nos interesa, que sería el del $2000 al $48E3. Usamos la herramienta DIS6502, ya sea su versión antigua o la más nueva; en ambos es lo mismo.

Seleccionamos el segmento y vamos a guardar sin cabecera; con esto dejamos el segmento limpio sin la cabecera de los archivos .xex. Con este paso tenemos el segmento total.
Usaremos otra herramienta llamada HxD que es un editor hexadecimal que nos ayudará a cortar una parte de 8 Kb y otra más pequeña de 2275 bytes. Una vez listos y guardados los archivos, dejaré un zip con los archivos, el ejecutable, segmento total, segmento parte 1 y, por último, segmento parte 2. Ahora pasaremos al ejemplo.
1FLPTR = $FC
2
3 org $80
4mem_orig .ds 2
5mem_dest .ds 2
6
7 opt h-
8 org $8000
9 ins 'gorf_part1.bin'
10
11 org $8000
12 ins 'gorf_part2.bin'
13:5916 .byte $FF
14
15 org $8000
16:8192 .byte $FF
17
18 opt f+
19 org $A000
20
21start_cart
22 lda #0
23 sta $D500
24 mwa #$8000 mem_orig
25 mwa #$2000 mem_dest
26 lda #32
27 sta FLPTR
28 jsr copy8kb
29
30 lda #1
31 sta $D500
32 mwa #$8000 mem_orig
33 mwa #$4000 mem_dest
34 lda #9
35 sta FLPTR
36 jsr copy8kb
37
38 jmp $48D3
39
40copy8kb
41 lda FLPTR
42 tax
43 ldy #0
44copy_loop
45 lda (mem_orig),y
46 sta (mem_dest),y
47 iny
48 bne copy_loop
49 inc mem_orig+1
50 inc mem_dest+1
51 dex
52 bne copy_loop
53 rts
54
55end_cart
56 rts
57
58 org $BFFA
59 .word start_cart
60 .by $0,$4
61 .word end_cart

Ahora lo explicamos:
- Líneas 3 al 5 : Reservamos 2 bytes para la posición de origen de lectura de la memoria ROM y 2 bytes para la posición de destino en la memoria RAM.
- Líneas 3 al 5 : Incluimos la primera parte del juego, que debe ser de 8 Kb.
- Líneas 11 al 13 : Incluimos la segunda parte del juego y rellenamos con $FF lo restante para armar los 8 Kb.
- Líneas 15 al 16 : Debes llenar este banco con $FF para completar un tercer banco obligatorio para armar un cartucho de 32 Kb.
- Líneas 22 al 28 : Seleccionamos el banco 0 y seteamos las variables con los valores $8000, que es la memoria de origen ROM, y $2000 para la memoria de destino en RAM. También tenemos FLPTR con 32 para copiar todo $8000 a $9FFF y, por último, llamamos a la rutina de copia.
- Líneas 30 al 36 : Seleccionamos el banco 1 y seteamos las variables con los valores $8000, que es la memoria de origen ROM, y $4000 para la memoria de destino en RAM. También tenemos FLPTR con 9 para copiar un poco de bytes y, por último, llamamos a la rutina de copia.
- Línea 38 : Saltamos al inicio del juego, que anteriormente nos dijo el emulador Altirra.
- Líneas 40 al 53 : Rutina de copia de bytes, con la variable FLPTR; le damos el largo a copiar; siempre será múltiplo de 255 para copiar bloques largos.
6. Conclusiones
Como se vio, hemos mostrado cómo programar los diferentes cartuchos que ATARI vendía para su línea de 8 bits; siempre el movimiento de memoria entre el cartucho ROM y la RAM puede ser 8 Kb o 16 Kb, ya que el hardware fue diseñado así. Otra cosa, existe un cartucho conmutado XEGS, pero no estoy seguro si fue algo ideado por ATARI o idea de un tercero; por el momento no se explicará por ese motivo.
Bueno, se pueden hacer cosas entretenidas hoy en día. Si te estás preguntando por la parte electrónica, hay varios diseños para estos cartuchos en PCBWay en la sección proyectos que puedes mandar hacer y armar tu propio cartucho real.