Sunday, March 22, 2015

Programando pra Atari 2600 no Fedora

Como todo cara old school que começou nos 8 bits, eu sempre gostei de estudar e mexer em máquinas antigas ou emuladores. Eu curto a nostalgia de ter instalados no meu Fedora todos os computadores ou sistemas antigos que marcaram a minha vida digital, como o Atari 2600 (através do stella, via RPMFusion Free), o MSX Expert (através do openmsx), o Amiga 500 (através do e-uae, infelizmente via RPMFusion Non-Free) e, claro, um DOSBox pra não perder o costume. Eu costumava ter uma VM rodando FreeDOS, mas acabei me rendendo à comodidade do DOSBox prontinho. Só cuido pra instalar no DOSBox também o NASM e um bom debugger, como o debug do FreeDOS. O Insight também é bem interessante pra demonstrar a execução passo a passo de programas x86 16 bits (que eu insisto em "infligir" aos meus alunos).

Há mais de 10 anos eu tenho vontade de estudar um pouco mais sobre o processador 6502 e sobre programação pro Atari 2600, já que o 6502 era o "outro" processador de 8 bits que "estragou" a infância de muita gente na minha geração. Eu cresci com o MSX e o processador Z80, e nunca tive muito contato com o 6502, enquanto muita gente (principalmente na América do Norte e Europa) cresceu com o 6502 (Apple IIe, Commodore 64, e até o TK 3000 IIe aqui no Brasil) e não conheceu o Z80. A idéia era matar a vontade de conhecer o 6502 e já brincar de programar pro Atari 2600 como um desafio divertido.

Na época em que bateu essa vontade, eu baixei tudo o que achei de referências, datasheets, tutoriais, listas de opcodes e etc sobre 6502 e Atari 2600, imprimi tudo e... guardei. :-( Nunca tive tempo de voltar nesse material, nem de instalar assemblers pra 6502 e realmente meter a mão na massa. Passados uns dez anos, há alguns meses, estávamos conversando sobre realizar oficinas no Hackerspace de Chapecó, e de repente todo aquele contexto acima me veio à cabeça e pensei: é agora que eu me afundo no Atari! :-) Nada melhor do que me jogar de cabeça na responsabilidade de ter que preparar uma oficina pra finalmente "fazer tempo" e colocar a mão na massa. Pois bem, aqui está o primeiro passo.

O ferramental é simples: precisa do emulador stella, um assembler pra família 6502 (o Atari 2600 usa o 6507, uma versão capada do 6502) e um bom editor de textos (vim, claro). O Fedora tem alguns assemblers de 6502, dentre os quais eu escolhi o atasm por ser o que me pareceu mais simples e direto (pra não dizer tosco). Ajuda também baixar o arquivo vcs.h, do tutorial "2600 101" do site AtariAge, com as constantes usadas para identificar mais facilmente os registradores e endereços mágicos do hardware. 20 anos de uso de Software Livre me ensinaram a sempre verificar a licença dos códigos encontrados por aí, e esse vcs.h não tem licença nenhuma, o que indicaria que não poderíamos usá-lo. Mas na boa, é só um monte de constantes que qualquer pessoa poderia digitar por si mesma lendo os manuais da arquitetura, então não tem stress. Na minha interpretação não constitui um trabalho relevante, então é meramente uma comodidade.

O que eu gostei no atasm é que ele vem com um PDF (eu sou da Geração Manual, não da Geração Tutorial) que explica bem as opções de linha de comando, a sintaxe de assembly e diretivas que ele usa, dá exemplos e não é totalmente estranho (alguns outros eram). :-) Usando a opção "-r" o atasm gera uma imagem "raw" (ao invés de um arquivo objeto pra linkar depois), que é exatamente o que precisamos pra criar um "cartucho" do Atari. Uma linha típica pra compilar (tecnicamente é "montar", mas esse termo nunca me soou bem) um arquivo chamado "oficina-1.asm" e gerar um binário "oficina-1.bin" seria "atasm -r -ooficina-1.bin oficina-1.asm". Pra testar dá pra chamar o stella direto, com "stella oficina-1.bin". Mais barbada que isso não tem! :-) Usando a opção "-v" o atasm também gera uma listagem do código compilado na saída padrão. As demais mensagens do atasm vão pra saída de erro padrão, então dá pra capturar a listagem e ainda ver as mensagens no terminal com uma linha tipo "atasm -v -r -ooficina-1.bin oficina-1.asm > oficina-1.lst".

Dei uma refrescada rápida em alguns tutoriais, e rabisquei o meu primeiro código pra Atari 2600, que só faz um scroll contínuo de todas as cores que ele pode gerar, usando uma cor diferente para cada linha na tela (notem que tem copyright e licença, que chique!):

; Primeiro teste de kernel simples pra Atari 2600
; Copyright © 2015 Fábio Olivé Leite
; Licença: Faça o que quiser com este código fonte.

.INCLUDE "vcs.h"

*= $f000

reset    sei          ; Inicializa flags.
         cld
         ldx #255     ; Inicializa pilha.
         txs


         lda #0
zero     sta $00, x   ; Zera a RAM e o TIA.
         dex
         bne zero

start    lda #0       ; Inicia o frame zerando VBLANK ...
         sta VBLANK
         lda #2
         sta VSYNC    ; ... e ligando VSYNC.

         sta WSYNC    ; 3 linhas de VSYNC.
         sta WSYNC
         sta WSYNC

         lda #0       ; Desliga VSYNC.
         sta VSYNC

         ldx #34      ; Espera mais 34 linhas de VBLANK.
vblank0  sta WSYNC    ; Muitos lugares falam em 37 linhas
         dex          ; mas no Stella o 34 alinhou melhor
         bne vblank0  ; com o topo da janela.

         ldx #210     ; Desenha 210 linhas visíveis.
         tya          ; Era pra ser 192, mas 210 fecha até
draw     sta COLUBK   ; embaixo da janela do Stella.
         sta WSYNC    ; Um dia eu testo em um Atari real.
         clc
         adc #1       ; Este "desenho" só vai variando a cor
         dex          ; de fundo nas linhas. :-)
         bne draw

         lda #2       ; Liga VBLANK porque acabou o frame.
         sta VBLANK
         ldx #30      ; São mais 30 linhas de overscan.
ovscan   sta WSYNC    ; Acabou ficando com 277 linhas no frame.
         dex
         bne ovscan

         iny          ; Incrementa a próxima cor inicial.
         jmp start    ; Volta pro início pra começar a próxima tela.

*= $fffa
.WORD    reset        ; NMI
.WORD    reset        ; Reset
.WORD    reset        ; IRQ


Não cabe aqui explicar os detalhes de como se programa pra Atari 2600, ou o porquê do código precisar ser da forma que é. Isso fica pra um próximo artigo, mas adiantando só um detalhe pra assustar: o Atari 2600 só tem "memória de vídeo" pra uma linha da imagem (da TV) de cada vez, então precisamos montar o "frame de vídeo" uma linha por vez durante o retraço horizontal, e processar a lógica do jogo durante o retraço vertical. Seja bem-vindo à programação pra Atari! ;-)

Os cartuchos simples pra Atari têm 4KiB que são mapeados na parte superior da memória do 6507, que na verdade só tem 13 pinos de endereços, embora o seu núcleo 6502 interno tenha todos os 16 pinos, o que faz com que o 6507 enxergue 8 blocos de 8KiB repetidos (que maravilha). O código então geralmente é gerado para começar no endereço 0xF000, e segundo o datasheet do 6502 as três últimas palavras de 16 bits (little-endian) na memória indicam os endereços para os quais o processador vai saltar em caso de NMI (interrupção não mascarável, cujo pino está faltando no 6507), em caso de Reset (ou quando é ligado) e em caso de IRQ (que também não tem no 6507, ô lasquera). Basicamente o costume é repetir nos seis últimos bytes da memória o endereço onde inicia o código do cartucho.

Compilando (tá, montando) o código acima com o atasm, ele nos conta que gerou 71 bytes de código de máquina. São 65 bytes de código mais os 6 dos endereços no final, e o binário gerado fica com exatos 4096 bytes, como deveria ser.

$ atasm -v -r -ooficina-1.bin oficina-1.asm > oficina-1.lst
ATasm 1.07 (A mostly Mac65 compatible 6502 cross-assembler)
Pass 1: Success. (0 warnings)
Pass 2:

Assembly successful
  Compiled 71 bytes (~0k)
  Writing raw binary image:
    f000-ffff

Compiled to binary file 'oficina-1.bin'


É legal olhar a listagem gerada pelo atasm, pra ver os opcodes e os endereços.

00:F0  78     reset    sei         ; Inicializa flags.
01:F0  D8              cld
02:F0  A2 FF           ldx #255    ; Inicializa pilha.
04:F0  9A              txs
05:F0  A9 00           lda #0
07:F0  95 00  zero     sta $00, x  ; Zera a RAM e TIA.
09:F0  CA              dex
0A:F0  D0 FB           bne zero
0C:F0  A9 00  start    lda #0      ; Inicia o frame zerando VBLANK ...
0E:F0  85 01           sta VBLANK
10:F0  A9 02           lda #2
12:F0  85 00           sta VSYNC   ; ... e ligando VSYNC.
14:F0  85 02           sta WSYNC   ; 3 linhas de VSYNC.
16:F0  85 02           sta WSYNC
18:F0  85 02           sta WSYNC
1A:F0  A9 00           lda #0      ; Desliga VSYNC.
1C:F0  85 00           sta VSYNC
1E:F0  A2 22           ldx #34     ; Espera mais 34 linhas de VBLANK.
20:F0  85 02  vblank0  sta WSYNC   ; Muitos lugares falam em 37 linhas
22:F0  CA              dex         ; mas no Stella o 34 alinhou melhor
23:F0  D0 FB           bne vblank0 ; com o topo da janela.
25:F0  A2 D2           ldx #210    ; Desenha 210 linhas visíveis.
27:F0  98              tya         ; Era pra ser 192, mas 210 fecha até
28:F0  85 09  draw     sta COLUBK  ; embaixo da janela do Stella.
2A:F0  85 02           sta WSYNC   ; Um dia eu testo em um Atari real.
2C:F0  18              clc
2D:F0  69 01           adc #1      ; Este "desenho" só vai variando a cor
2F:F0  CA              dex         ; de fundo nas linhas. :-)
30:F0  D0 F6           bne draw
32:F0  A9 02           lda #2      ; Liga VBLANK porque acabou o frame.
34:F0  85 01           sta VBLANK
36:F0  A2 1E           ldx #30     ; São mais 30 linhas de overscan.
38:F0  85 02  ovscan   sta WSYNC   ; Acabou ficando com 277 linhas no frame.
3A:F0  CA              dex
3B:F0  D0 FB           bne ovscan
3D:F0  C8              iny         ; Incrementa a próxima cor inicial.
3E:F0  4C 0C F0        jmp start   ; Volta pro início do frame.

FA:FF  00 F0  .WORD    reset       ; NMI
FC:FF  00 F0  .WORD    reset       ; Reset
FE:FF  00 F0  .WORD    reset       ; IRQ


Confirmando que temos um "cartucho" corretamente montado, agora é só chamar o stella e testar (basta rodar "stella oficina-1.bin"). A emocionante :-D captura de tela abaixo mostra que deu certo!


Agora que meu primeiro teste funcionou, estou fadado a passar o resto dos meus minutos livres tentando fazer algo que preste, ou melhor, tentando programar um excitante jogo onde um quadrado verde tenta pegar uma bola azul enquanto foge de um borrão amarelo, ou algo do tipo. ;-) Talvez ao invés de continuar postando no blog eu comece a escrever um material legal sobre 6502, sobre Atari 2600 e contendo exemplos cada vez mais elaborados lá no xap-hacks.org. Por mais que o Inglês seja uma obrigação básica pra nós que trabalhamos com tecnologia, eu sinto que criar material original e de qualidade em Português Brasileiro cobrindo conceitos fundamentais como Arquitetura de Computadores, especialmente usando processadores que existem de verdade ao invés de processadores "didáticos" que só existem no papel, é um esforço que vale a pena.

Vamos ver no que dá! :-)