Executando verificação de segurança...
21

O que Aprendi Escrevendo um Emulador de Fliperama aos 17 anos

Era um daqueles dias em que eu me sentia estagnado — preso num ciclo de repetir as mesmas soluções com ferramentas diferentes. Eu queria começar algo novo, sair da mesmice. Foi então que me lembrei dos vídeos do Fábio Akita explicando como o NES funciona, e resolvi tentar escrever um emulador. Acabei caindo em um projeto do canal javidx9, que mostrava como criar um emulador de NES do zero.

No primeiro dia, tomei um baque: era informação demais pra quem nunca tinha estudado arquitetura de computadores a fundo. O vídeo era ótimo, mas eu me sentia completamente perdido. Não entendi quase nada e acabei largando antes mesmo de sair do chão. Mas aquilo me despertou algo. Em vez de desistir de vez, decidi entender como essas máquinas realmente funcionavam — por dentro.

Explorando alternativas

Continuei pesquisando e encontrei o subreddit r/emudev, onde muitos recomendavam começar pelo CHIP-8, uma espécie de sistema didático com uma arquitetura simples. Cheguei a considerar, mas queria algo mais concreto, com mais peso histórico. Eu não queria só emular um conceito — queria emular uma máquina real, com um jogo real.

Foi aí que encontrei o Intel 8080, um processador de 8 bits usado em vários computadores dos anos 70, incluindo uma máquina de fliperama icônica: Space Invaders. Aquilo me empolgou. Eu podia escrever um emulador de um processador real, rodando um jogo clássico, com tela, sprites e jogabilidade de verdade.

Primeiros passos

Comecei reunindo o máximo de informações sobre o 8080: lista de registradores, modos de endereçamento, tabela de instruções — e, claro, o manual técnico completo, que se provou indispensável. Não existia um guia definitivo de “como escrever um emulador”, então fui montando tudo na tentativa e erro.

Modelei primeiro os registradores, depois a memória, e então comecei a implementar as instruções uma a uma. Algumas eram simples, mas outras envolviam nuances que me fizeram revisar o código várias vezes. O começo foi turbulento: inconsistências, bugs difíceis de rastrear, lógica quebrada — mas fui persistindo.

Um dos primeiros desafios foi implementar a instrução JMP, que salta a execução para um endereço específico. Foi quando aprendi, na prática, o impacto do little endian: os dois bytes que formam o endereço vêm na ordem invertida — o byte menos significativo primeiro. Se você não estiver atento a isso, o programa simplesmente pula para o lugar errado.

Logo depois, enfrentei as instruções de chamada de função (CALL) e retorno (RET). Tive que entender como a pilha era usada para armazenar o endereço de retorno. Essa foi a primeira vez que lidei de fato com stack frames e manipulação manual da pilha. Hoje, depois de ler partes do Computer Systems: A Programmer’s Perspective (CS:APP), vejo com mais clareza como o fluxo de controle funciona em nível de instrução.

Quando o assembly começa a fazer sentido

Uma das partes mais fascinantes foi entender como estruturas de alto nível, como if e else, se traduzem em assembly. Aprendi que instruções como CPI 0 (Compare Immediate) comparam o registrador acumulador (A) com um valor imediato e ajustam flags internas, como a flag ZERO. Com isso, uma condição como if (val == 0) vira algo como:

CPI 0      ; compara A com 0
  JNZ skip   ; se for diferente, pula o bloco
  ; código executado se val == 0
  skip:
  

Essas flags se tornaram essenciais na minha cabeça, e o CS:APP ajudou a consolidar esse entendimento, mostrando como essas operações são base de quase toda lógica condicional em baixo nível.

A mágica do “PLAY SPACE INVADERS”

Depois de alguns meses longe do projeto, resolvi revisitar o código. Corrigi alguns bugs antigos e implementei a leitura da memória de vídeo — que no Space Invaders é um mapeamento direto de bits para pixels. Não há cores RGB: cada pixel é representado por 1 bit, indicando se ele está aceso ou apagado. Levei um bom tempo organizando o frontend do emulador para interpretar esse layout corretamente e desenhar na tela.

Com o tempo, fui modularizando o projeto: separei arquivos para a CPU, vídeo, depuração e utilitários. E então, pela primeira vez, vi na tela a mensagem "PLAY SPACE INVADERS". Foi mágico. Eu já não estava muito otimista sobre o futuro do projeto, mas ver aquelas palavras, fruto direto da execução do meu emulador, foi um divisor de águas.

Só que o jogo travava ali — os pontos e os alienígenas não apareciam. Descobri que o jogo depende de interrupções temporizadas para atualizar a tela e controlar a lógica. Por já ter estudado interrupções ao desenvolver meu mini-kernel Lizard OS, eu sabia o que procurar. Fiz um hack inicial: descobri o endereço da rotina de atualização e passei a decrementar manualmente uma variável em cada ciclo só pra confirmar que o problema era esse — e era.

Depois implementei o sistema de interrupções corretamente. Em seguida, corrigi instruções de rotação de bits, como RAR, que estavam com a lógica incorreta e causavam efeitos estranhos nos cálculos.

Estado atual

Demo do emulador rodando Space Invaders

Hoje, o emulador está quase jogável. Ele exibe a tela corretamente, processa a maioria das instruções do 8080 com fidelidade e já tem suporte ao bit shifter, essencial para o jogo. O sistema de I/O ainda é limitado — inputs de jogador ainda não estão implementados, e há um bug conhecido no cálculo da posição dos sprites dos tiros.

O que eu aprendi

Aprendi a ler documentação técnica com atenção. Aprendi que nem tudo vai estar explicado de forma clara, e que muitas vezes o melhor jeito de entender alguma coisa é abrir um emulador de referência, dar um step e ver o que acontece em cada ciclo.

Aprendi a confiar mais no processo de depuração do que na suposição. Debugger na mão virou essencial — entender cada registrador, cada mudança no estado. Vi de perto o que é uma emulador de hardware de verdade, e isso me deu uma noção muito mais concreta de como as linguagens de alto nível se apoiam em instruções simples no fundo.

Também me forçou a modularizar melhor meus projetos, separar lógica, criar utilitários, reaproveitar código. Além disso, tive que lidar com bugs que só aparecem depois de dezenas de milhares de ciclos — o tipo de erro que exige paciência e método pra isolar.

Acima de tudo, esse projeto me ensinou que entender o funcionamento interno de uma máquina te dá outra perspectiva sobre software. Saber o que tá acontecendo embaixo tornou minhas decisões mais consciente.

Codigo Fonte do Emulador


O que Aprendi Escrevendo um Emulador de Fliperama aos 17 anos

Carregando publicação patrocinada...