Eu fiz um emulador NES. Aqui é o que eu aprendi sobre a Nintendo original.

Criei recentemente o meu próprio emulador NES . Eu fiz isso principalmente por diversão e para aprender sobre como o NES funcionou. Aprendi algumas coisas interessantes, então escrevi este artigo para compartilhar. Há uma grande quantidade de documentação já existente, então isso é apenas para realçar algumas dicas interessantes. Aviso : isso será muito técnico!

Meu emulador pode gravar GIFs animados. Aqui estou jogando Donkey Kong.

A CPU

O NES usou o MOS 6502 (a 1,79 MHz) como sua CPU. O 6502 é um microprocessador de 8 bits que foi projetado em 1975. (há quarenta anos!) Este chip foi muito popular – também foi usado no Atari 2600 e 800, Apple I & II, Commodore 64, VIC-20, BBC Micro e mais. De fato, uma revisão do 6502 (o 65C02 ) ainda está em produção hoje.

Os 6502 tinham relativamente poucos registos (A, X & Y) e eram registos de propósito especial. No entanto, suas instruções apresentaram vários modos de endereçamento, incluindo um modo de "página zero" que poderia fazer referência às primeiras 256 palavras ($ 0000 – $ 00FF) na memória. Esses opcodes exigiram menos bytes na memória do programa e menos ciclos de CPU durante a execução. Uma maneira de ver isso é que um desenvolvedor pode tratar esses 256 slots como "registros".

O 6502 não apresentava instruções de multiplicação ou divisão. E, claro, não há ponto flutuante. Havia um modo BCD (decimal codificado binário), mas isso foi desativado na versão NES do chip – possivelmente devido a preocupações de patente.

O 6502 tinha uma pilha de 256 bytes sem detecção de transbordamento.

O 6502 tinha 151 opcodes (de um possível 256). Os restantes 105 valores são opcodes ilegais / indocumentados. Muitos deles quebram o processador. Mas alguns deles realizam resultados possivelmente úteis por coincidência. Como tal, muitos deles receberam nomes com base no que eles fazem.

O 6502 tinha pelo menos um erro de hardware, com saltos indiretos. JMP (<addr>) não funcionaria corretamente se <addr> fosse da forma $ xxFF. Ao ler dois bytes do endereço especificado, não levaria o estouro FF-> 00 para o xx. Por exemplo, iria ler $ 10FF e $ 1000 em vez de $ 10FF e $ 1100.

Mapa da Memória

O 6502 tinha um espaço de endereço de 16 bits, portanto, poderia referir até 64 KB de memória. Mas, o NES tinha apenas 2 KB de RAM, nos endereços de US $ 0000 a US $ 0800. O resto do espaço de endereços era para acessar o PPU, a APU, o cartucho do jogo, dispositivos de entrada, etc.

Algumas linhas de endereços foram desencadeadas, então grandes blocos do espaço de endereço realmente refletem outros endereços. Por exemplo, $ 1000 a $ 1800 espelha a RAM de US $ 0000 a US $ 0800. Escrever para US $ 1000 equivale a escrever para US $ 0000.

É PERIGOSO FAZER SEMPRE! PEGUE ISSO.

O PPU (Unidade de processamento de imagem)

O PPU gerou a saída de vídeo para o NES. Ao contrário da CPU, o chip PPU foi especialmente construído para o NES. O PPU correu em 3x a freqüência da CPU. Cada ciclo do PPU produz um pixel durante a renderização.

O PPU pode renderizar uma camada de fundo e até 64 sprites. Sprites pode ser 8×8 ou 8×16 pixels. O fundo pode ser rolado nos eixos X e Y. Ele suportou a rolagem "fina" (um pixel por vez). Esse era um grande problema naquela época.

Tanto o fundo como os sprites foram feitos de telhas 8×8. As tabelas de padrões na ROM do cartucho definiram essas telhas. Os padrões especificaram apenas dois bits da cor. Os outros dois bits vieram de uma tabela de atributos. Um nome de usuário especificou quais azulejos vão para onde em segundo plano. Em suma, parece complicado em comparação com os padrões de hoje. Eu tinha que explicar ao meu colega de trabalho que não era "apenas um bitmap".

O fundo foi composto de 32 x 30 = 960 destes azulejos 8×8. O deslocamento foi implementado ao renderizar mais de um desses 32 x 30 backgrounds, cada um com um deslocamento. Se deslocando nos eixos X e Y, até quatro desses fundos poderiam se tornar visíveis. No entanto, o NES só suportou dois, então diferentes modos de espelhamento foram usados ??para espelhamento horizontal ou vertical.

O PPU continha 256 bytes de OAM – Object Attribute Memory – que armazenava os atributos sprite para todos os 64 sprites. Os atributos incluem a coordenada X e Y do sprite, o número de telha para o sprite e um conjunto de sinalizadores que especificou dois bits da cor do sprite, especificou se o sprite aparece na frente ou atrás da camada de fundo e permite lançar o sprite verticalmente e / ou horizontalmente. O NES suportou uma cópia DMA da CPU para copiar rapidamente um pedaço de 256 bytes para o OAM inteiro. Este acesso direto foi cerca de quatro vezes mais rápido do que a cópia manual dos bytes.

Embora o PPU tenha suportado 64 sprites, apenas 8 podem ser exibidos em uma única linha de varredura. Uma bandeira de transbordamento seria configurada para que o programa possa lidar com uma situação com muitos sprites em uma linha. É por isso que os sprites cintilam quando há muitas coisas acontecendo no jogo. Além disso, houve um erro de hardware que causou a bandeira de transbordamento às vezes não funcionar corretamente.

Muitos jogos fariam mudanças no meio do quadro, de modo que o PPU faria uma coisa por uma parte da tela e outra coisa para a outra – geralmente usada para dividir a rolagem ou renderizar uma barra de pontuação. Isso precisava de tempo preciso e sabia exatamente quantos ciclos de CPU usavam cada instrução. Coisas assim dificultam a emulação.

O PPU tinha uma forma primitiva de detecção de colisão – se o primeiro sprite (zeroth) intersejasse no fundo, uma bandeira seria definida indicando um "hit zero do sprite". Apenas um desses ataques poderia ocorrer por quadro.

O NES tinha uma paleta interna de 54 cores distintas – estas eram as únicas cores disponíveis. Não era RGB; As cores na paleta, basicamente, cuspiram um determinado erro de croma e luminância para a TV.

A paleta de cores NES.

A APU (Unidade de processamento de áudio)

A APU suportou dois canais de onda quadrada, um canal de onda triangular, um canal de ruído e um canal de modulação delta.

Para reproduzir sons, o programa do jogo escreveria em registros específicos (endereços na memória) para configurar esses canais.

Os canais de ondas quadradas suportaram controle de freqüência e duração, varreduras de freqüência e envelopes de volume.

O canal de ruído usou um registro de deslocamento de retorno linear para gerar ruído pseudo-aleatório.

O canal de modulação delta pode reproduzir amostras da memória. A música SMB3 possui um som de metal que utiliza o DMC. A TMNT3 teve vozes como "cowabunga" que usavam o DMC.

Luta de balão

Mapeadores

O espaço de endereçamento reservado para o cartucho de jogos restritos para 32KB de memória do programa e 8KB de memória de caracteres (tabelas de padrões). Isso foi bastante limitante, então as pessoas criaram mapeadores criativos e implementados.

Um mapeador é hardware no próprio cartucho que pode executar a troca bancária para trocar o novo programa ou a memória do caractere para o espaço de memória endereçável. O programa poderia controlar essa mudança de banco, escrevendo para endereços específicos que apontaram para o hardware do mapeador.

Os diferentes cartuchos de jogos implementaram esse banco alternando de maneiras diferentes, então há dezenas de mapeadores diferentes. Assim como um emulador deve imitar o hardware NES, ele também deve emular os mapeadores de cartuchos. No entanto, cerca de 90% de todos os jogos NES usam um dos seis mapeadores mais comuns.

Ficheiros de ROM

Um arquivo ROM .nes contém os bancos de memória do programa e os bancos de memória de caracteres do cartucho. Tem um pequeno cabeçalho que especifica o que o mapeador usou e o modo de espelhamento de vídeo que ele usou. Ele também especifica se a RAM com respaldo de bateria estava presente no cartucho.

Conclusão

Foi divertido aprender sobre o NES. Estou impressionado com o que as pessoas conseguiram realizar com hardware tão limitado. Isso me faz querer escrever um jogo de estilo de 8 bits agora …

Eu escrevi meu emulador em Go usando OpenGL + GLFW para vídeo e PortAudio para áudio. O código está tudo no GitHub, então confira:

https://github.com/fogleman/nes

O meu favorito: Super Mario Bros. 3

Saber mais

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *