O processador 6502, com sua arquitetura simples e eficiente, é um chip icônico que alimentou uma miríade de sistemas, desde o Apple II e o Commodore 64 até o Nintendo Entertainment System (NES). Aprender a programar em 6502 não é apenas um exercício de nostalgia, mas também uma forma valiosa de entender os fundamentos da computação, a arquitetura de hardware e a otimização de código em um ambiente com recursos limitados. Este guia é projetado para iniciantes, mas também oferece insights para aqueles que desejam se aprofundar no mundo da programação 6502.
Introdução à Arquitetura 6502
O 6502 é um processador de 8 bits com uma arquitetura RISC (Reduced Instruction Set Computing), embora o termo “RISC” seja um tanto anacrônico para a época. Possui um conjunto de instruções relativamente pequeno, mas poderoso, e depende fortemente de registradores para realizar operações. Os principais componentes da arquitetura 6502 são:
- Acumulador (A): O principal registrador para operações aritméticas e lógicas.
- Registros de Índice (X e Y): Usados para indexar memória e realizar loops.
- Ponteiro de Pilha (SP): Aponta para a pilha, uma área de memória usada para armazenar temporariamente dados e endereços de retorno de subrotinas.
- Contador de Programa (PC): Aponta para a próxima instrução a ser executada.
- Registrador de Status (SR ou P): Contém flags que indicam o resultado das operações, como carry, zero, negativo, overflow e interrupt.
Entender esses registradores e suas funções é crucial para escrever código 6502 eficiente.
Conjunto de Instruções 6502: O Básico
O conjunto de instruções 6502 pode parecer intimidador no início, mas é relativamente fácil de aprender com a prática. Aqui estão algumas das instruções mais comuns:
- LDA (Load Accumulator): Carrega um valor na acumulador. Exemplo:
LDA #$05(carrega o valor hexadecimal 05 no acumulador). - STA (Store Accumulator): Armazena o valor do acumulador em um endereço de memória. Exemplo:
STA $2000(armazena o valor do acumulador no endereço de memória $2000). - ADC (Add with Carry): Adiciona um valor ao acumulador, juntamente com o flag de carry. Exemplo:
ADC #$10(adiciona o valor hexadecimal 10 ao acumulador). - SBC (Subtract with Carry): Subtrai um valor do acumulador, levando em conta o flag de carry (na verdade, adiciona o complemento de dois). Exemplo:
SBC #$05(subtrai o valor hexadecimal 05 do acumulador). - INC (Increment Memory): Incrementa o valor em um endereço de memória. Exemplo:
INC $2000(incrementa o valor no endereço de memória $2000). - DEC (Decrement Memory): Decrementa o valor em um endereço de memória. Exemplo:
DEC $2000(decrementa o valor no endereço de memória $2000). - INX/INY (Increment X/Y): Incrementa o valor dos registros de índice X ou Y. Exemplo:
INX(incrementa o registro X). - DEX/DEY (Decrement X/Y): Decrementa o valor dos registros de índice X ou Y. Exemplo:
DEY(decrementa o registro Y). - CMP (Compare): Compara o valor do acumulador com outro valor, definindo os flags de status apropriadamente. Exemplo:
CMP #$0A(compara o acumulador com o valor hexadecimal 0A). - BEQ (Branch if Equal): Desvia a execução para outro endereço se o flag Zero estiver setado. Exemplo:
BEQ LOOP(desvia para a label LOOP se o flag Zero estiver setado). - BNE (Branch if Not Equal): Desvia a execução para outro endereço se o flag Zero não estiver setado. Exemplo:
BNE LOOP(desvia para a label LOOP se o flag Zero não estiver setado). - JMP (Jump): Desvia a execução para outro endereço. Exemplo:
JMP START(desvia para a label START). - JSR (Jump to Subroutine): Desvia a execução para uma subrotina, empurrando o endereço de retorno na pilha. Exemplo:
JSR MY_SUBROUTINE(desvia para a subrotina MY_SUBROUTINE). - RTS (Return from Subroutine): Retorna de uma subrotina, retirando o endereço de retorno da pilha. Exemplo:
RTS.
Esta é apenas uma pequena amostra do conjunto de instruções 6502. Existem também instruções para operações lógicas (AND, ORA, EOR), deslocamento de bits (ASL, LSR, ROL, ROR), manipulação da pilha (PHA, PLA, PHP, PLP) e interrupções (BRK, RTI).
Modos de Endereçamento
O 6502 oferece uma variedade de modos de endereçamento que permitem acessar a memória de diferentes maneiras. Compreender esses modos é fundamental para otimizar o código.
- Imediato: O operando é o próprio valor a ser usado. Exemplo:
LDA #$10(carrega o valor 16 no acumulador). - Absoluto: O operando é um endereço de memória de 16 bits. Exemplo:
LDA $2000(carrega o valor do endereço de memória $2000 no acumulador). - Zero Page: O operando é um endereço de memória de 8 bits na página zero (endereços $0000-$00FF). Este modo é mais rápido do que o absoluto. Exemplo:
LDA $10(carrega o valor do endereço de memória $0010 no acumulador). - Zero Page, X/Y: O operando é um endereço de memória de 8 bits na página zero, indexado pelo registro X ou Y. Exemplo:
LDA $10,X(carrega o valor do endereço de memória $0010 + X no acumulador). - Absoluto, X/Y: O operando é um endereço de memória de 16 bits, indexado pelo registro X ou Y. Exemplo:
LDA $2000,X(carrega o valor do endereço de memória $2000 + X no acumulador). - (Indireto, X): O operando é um endereço de memória de 8 bits na página zero. O endereço resultante é encontrado nos dois bytes localizados no endereço zero page + X e no endereço zero page + X + 1. Exemplo:
LDA ($10,X). - (Indireto), Y: O operando é um endereço de memória de 8 bits na página zero. O endereço resultante é encontrado nos dois bytes localizados no endereço zero page e no endereço zero page + 1. O valor do registrador Y é então adicionado a esse endereço. Exemplo:
LDA ($10),Y.
Um Exemplo Prático: Somando Dois Números
Vamos criar um pequeno programa para somar dois números armazenados em memória e armazenar o resultado em outro local.
; Endereços de memória
NUM1 = $2000
NUM2 = $2001
RESULT = $2002
; Código do programa
LDA NUM1 ; Carrega o primeiro número no acumulador
ADC NUM2 ; Adiciona o segundo número ao acumulador
STA RESULT ; Armazena o resultado no endereço de memória
BRK ; Finaliza o programa
Este código carrega o valor no endereço NUM1 no acumulador, adiciona o valor no endereço NUM2 ao acumulador e armazena o resultado no endereço RESULT. A instrução BRK interrompe a execução do programa.
Ferramentas para Desenvolvimento 6502
Existem várias ferramentas disponíveis para o desenvolvimento de programas 6502, incluindo:
- Assemblers: Ferramentas que traduzem código assembly em código de máquina. Exemplos: ca65, Kick Assembler.
- Emuladores: Programas que simulam o comportamento do hardware 6502. Exemplos: VICE (para Commodore), FCEUX (para NES), Mesen (para NES).
- Debuggers: Ferramentas que permitem rastrear a execução do código, inspecionar registradores e memória, e depurar erros. Muitos emuladores incluem um debugger integrado.
Para começar, você pode usar um emulador com um assembler integrado. Isso permite escrever, compilar e executar seu código em um único ambiente.
Tópicos Avançados
Depois de dominar os conceitos básicos, você pode explorar tópicos mais avançados:
- Interrupções: Aprenda a lidar com interrupções (IRQs e NMIs) para responder a eventos externos.
- Gráficos e Som: Explore as capacidades gráficas e sonoras dos sistemas 6502, como o VIC-II do Commodore 64 e o PPU do NES.
- Otimização: Aprenda técnicas para otimizar seu código para tamanho e velocidade, aproveitando os modos de endereçamento e as características da arquitetura 6502.
- Programação Bare-Metal: Desenvolva programas que rodam diretamente no hardware, sem um sistema operacional.
Conclusão
A programação 6502 pode ser um desafio, mas também é incrivelmente gratificante. A simplicidade da arquitetura força você a pensar cuidadosamente sobre cada instrução e a otimizar seu código. Além disso, aprender a programar em 6502 oferece uma compreensão mais profunda dos fundamentos da computação e da arquitetura de hardware. Com este guia e um pouco de prática, você estará escrevendo seus próprios programas 6502 em pouco tempo!
Perguntas Frequentes (FAQs)
Qual a melhor maneira de começar a programar em 6502?
Comece com um emulador que inclua um assembler e um debugger. Experimente pequenos programas simples, como somar dois números ou exibir um caractere na tela. Explore os exemplos e tutoriais disponíveis online.
Quais são as principais diferenças entre os modelos de 6502?
Existem várias variantes do 6502, cada uma com suas próprias características e recursos. Alguns modelos têm instruções adicionais ou diferentes modos de endereçamento. A maioria dos conceitos básicos é a mesma em todos os modelos.
O que é a página zero e por que é importante?
A página zero é os primeiros 256 bytes da memória (endereços $0000-$00FF). O acesso à página zero é mais rápido do que o acesso à memória absoluta, então é frequentemente usado para armazenar variáveis e dados acessados com frequência.
Como faço para depurar meu código 6502?
Use um debugger para rastrear a execução do seu código, inspecionar os registradores e a memória, e identificar erros. Defina breakpoints para interromper a execução em pontos específicos do código.
Onde posso encontrar mais recursos e exemplos de código 6502?
Existem muitos recursos online, incluindo tutoriais, fóruns e documentação. Procure por exemplos de código para o sistema específico que você está programando (por exemplo, Commodore 64, Apple II, NES).
