Skip to content

Instantly share code, notes, and snippets.

@mukaschultze
Created December 10, 2019 06:29
Show Gist options
  • Save mukaschultze/555e74a126340356aa16a8154625da31 to your computer and use it in GitHub Desktop.
Save mukaschultze/555e74a126340356aa16a8154625da31 to your computer and use it in GitHub Desktop.
NES

Artigo Metodologia de Pesquisa em SI

Título

Relato de desenvolvimento de emulador NES usando Rust Relato do uso da linguagem Rust no desenvolvimento de um emulador de NES

Autores

Samuel M. Schultze samuelschultze@gmail.com

Manual de estilo

Manual de estilo SESI-SP Editora e SENAI-SP Editora

Abstract / Resumo

A grande documentação sobre consoles de videogames permite desenvolver emuladores para executar em computadores atuais jogos e programas antigos, desenvolver um sistema desses pode ser uma experiência muito gratificantes e dar muita experiência prática sobre conteúdos que são usados em sistemas de informações nos dias atuais.

Este artigo relata a experiência de desenvolver um emulador de NES (Nintendo Entertainment System) usando Rust, uma linguagem recente e muito performática, que visa mitigar problemas encontrados com desenvolvimento em C++ sem perder performance. Também são descritos uma visão geral sobre hardware emulado e demais tecnologias utilizadas no desenvolvimento.

O projeto final teve um resultado bastante satisfatório em relação à performance e executa jogos com bastante fidelidade, além de ter servido para validar a linguagem Rust, que se mostrou muito promissora e inovadora.

Introdução

Existem diversas comunidades grandes de desenvolvedores de jogos para plataformas já obsoletas e de emuladores, tais desenvolvedores buscam atingir com o hardware atual a execução de jogos que eram disponíveis nos anos 80 e 90. Estas comunidades foram responsáveis por documentaram aos mínimos detalhes o funcionamento de diversos consoles antigos, desde os mais antigos como o NES e o GameBoy, até alguns mais complexos como o Playstation 2.

Com a vasta documentação existente é possível que qualquer pessoa com alguns meses de estudo possam desenvolver seus próprios emuladores. Existem emuladores de NES em diversas linguagens de programação, desde linguagens estáticas como C, até linguagens mais dinâmicas e modernas como JavaScript/ECMAScript.

O objetivo deste artigo é mostrar os pontos fortes e fracos do desenvolvimento de um emulador NES em Rust, e sua performance em relação ao hardware original.

Revisão da Literatura

Trabalhos Similares

Proposta / Descrição da Experiência

O desenvolvimento de emuladores é muito comum entre entusiastas da programação, uma vez que são desafiadores de serem criados e o resultado pode ser divertido, além de trazer uma bagagem de conhecimento acerca de otimização, programação de baixo nível, hardware e de como funcionavam os consoles antigos.

Por ser complicado para os processadores atuais simularem componentes de hardware a nível de software, emuladores são bons candidatos à testes de benchmarks de linguagens, a proposta deste projeto é verificar o quão rápidos os processadores atuais conseguem simular instruções com alta entropia, além de verificar se a linguagem Rust é eficiente para efetuar otimizações neste quesito.

Tecnologias usadas

Para desenvolvimento do emulador de NES (Nintendo Entertainment System) e do MOS6502 foram usados a linguagem de programação Rust, o gerenciador de pacotes Cargo e um motor gráfico e de entrada baseado em SDL, visando alta performance e facilidade de manutenção.

Rust

Rust é uma linguagem de programação de sistemas, desenvolvida pela fundação Mozilla, visando performance equivalente à C e C++, porém reinventando alguns paradigmas usados até então para impedir problemas de alocação de memória, de processamento paralelo e concorrente, e demais erros associados à programação de mais baixo nível. A versão usada foi a stable 1.38.0.

Cargo

Cargo é o principal gerenciador de pacotes e aplicações principal de Rust, de fácil uso e contendo mais de 32.000 pacotes de código aberto em seu registro. O projeto foi executado com a versão stable 1.38.0

Simple DirectMedia Layer (SDL)

SDL é uma biblioteca de desenvolvimento que abstrai componentes de hardware como mouses, teclados, VGAs, gamepads, entre outros e permite a criação de aplicações otimizadas em múltiplos sistemas. Apesar de ser uma biblioteca escrita inteiramente em C, o SDL possui bindings para diversas outras linguagens, neste projeto foram usados os bindings "sdl2" na versão 0.32.2.

Tecnologias emuladas

Um NES (Nintendo Entertainment System) foi um console de 8 bits, desenvolvido pela Nintendo de 1983 até 1995 (com variantes legais até 2003), no Brasil a chegada oficial foi em 1993, quando os clones ilegais já haviam dominado o mercado, mercado este que já estava tomando rumo aos consoles de 16bits.

O console é dividido em 3 partes principais, uma responsável pela execução do programa, quem na maior parte das vezes é um jogo, outra encarregada por desenhar na tela a saída do programa de forma visual, a última parte fica com a tarefa de produzir as saídas sonoras como músicas e efeitos visuais.

A parte responsável pela execução do programa era a CPU, em conjunto com a memória de acesso aleatório (RAM, random access memory) de 2KiB (podendo ser expandida por hardware externo). O programa é lido da uma memória estática (ROM, read only memory), que era vendido separadamente em hardwares específicos, os Game Paks (popularmente conhecidos como "cartuchos").

Para desenhar nos antigos televisores analógicos era usados hardware específico, chamada de PPU (Picture processing unit), esta lia e cruzava informações do cartucho e da memória de vídeo (VRAM), como não era possível guardar informações de pixels individuais com as tecnologias da época a PPU produzia pixels em perfeita harmonia com o canhão de elétrons do televisor.

O áudio também era produzido por hardware específico e embutidos, conhecido como APU (Audio processing unit), porém um emulador desta parte não foi desenvolvida no projeto pois não é relevante para medir o desempenho da linguagem utilizada.

CPU MOS6502/RP2A03/RP2A07

Ricoh 2A03 e 2A07 foram processadores MOS6502 de segunda mão e modificados desenvolvidos especificamente para o uso no NES, trabalhavam respectivamente em 1.79MHz e 1.66MHz, sendo que o 2A03 era usado no mercado de televisores NTSC e o 2A07 no mercado de televisores PAL.

PPU RP2C02/RP2C07

As Picture processing units RP2C02 e RP2C07 também foram desenvolvidas pela Ricoh especificamente para o NES, para televisores NTSC e PAL respectivamente, possuíam 2KiB de memória VRAM, suporte para até 32 sprites (objetos móveis) simultâneas, mais de 25 cores simultâneas e contavam com diversas features que permitiam aos jogos da época uma grande gama de gráficos usando poucos recursos. Possuia tecnologias avançadas para a época, como scrolling do background tanto horizontal quanto vertical e renderizava em uma resolução de 256x240, apesar de nem todos televisores da época mostrarem a tela por completo.

Game Pak

Os Game Paks, ou cartuchos, possuíam todo o código do jogo, as sprites e os blocos que formavam o background. Podiam também conter hardware extra como memórias RAM independentes, memórias para o salvamento do progresso do jogo (por meio de baterias elétricas), maior capacidade de armazenamento ROM, expansão de sons, como músicas com mais de 8bits e mais canais de áudio, circuitos que aumentavam a qualidade gráfica dos jogos e permitiam animações que não seriam possíveis apenas com o hardware básico do console. Estas expansões de hardware receberam um nome popular de mappers na comunidade de desenvolvedores, pois mapeavam endereços do CPU para endereços físicos no cartucho.

Resultados

Performance

Quando compilado com todas as otimizações ativadas foram alcançados resultados satisfatórios referentes à performance, com uma emulação de CPU 79x mais veloz que o hardware original (559ns por ciclo de CPU no hardware original, 7ns por ciclo de CPU no hardware emulado) em um processador i5 7200U @ 2.50GHz.

A emulação de vídeo também se mostrou satisfatória, com o uso de aceleração de hardware foram alcançados mais de 230 quadros por segundo, em contraste com os 60 dos televisores da época. Vale ressaltar que grande parte dos jogos não foram desenvolvidos para serem executados em velocidades altas, e acabam acontecendo artefatos visuais caso não haja um limite de quadros por segundo.

Manutenibilidade

Rust é limpo e contém padrões claros de qualidade de código e de formatação, isso permite que possa ser facilmente entendida por qualquer outra pessoa que entenda da linguagem, e permite que alterações sejam feitas sem que haja preocupação com efeitos colaterais externos ao que foi modificado. Pode ser um pouco complexa para pessoas que não entendam sobre o funcionamento de uma CPU e da memória RAM de um computador.

Também é incluso na linguagem um framework de testes unitários nativo, que apesar de simples atende às necessidades de grande parte dos projetos open source disponíveis pelo cargo, e possui integração com a linha de comando, permitindo a fácil criação de sistemas de integração contínua e deploy contínuo, este framework foi usado para a execução de testes automatizados e se mostrou eficiente em seu dever.

A linguagem não possui orientação à objetos, mas conta com traits, que garantem a existência de implementações de funções para tipos específicos, esta feature foi especialmente útil na implementação dos mappers, uma vez que todos eles possuem as mesmas entradas e saídas e funcionam como caixa-preta para os demais componentes do sistema.

Ownership

A linguagem Rust utiliza de um paradigma de domínio de ponteiros, chamado de ownership, isso permite que toda a memória seja alocada e liberada com alta performance sem a necessidade de recorrer à garbage collectors, comuns em linguagens orientadas à objetos. Porém, tal paradigma pode dificultar projetos que necessitem de referências cruzadas, como grafos e afins, neste projeto foi notado uma dificuldade em fazer as referências entre a PPU, RAM e CPU, visto que apenas um componente pode ter domínio sobre outro, o que não é suficiente para o hardware do NES, esta dificuldade foi superada por meio do uso de contadores de referência e células de referências, features da linguagem que permitem ao borrow checker uma verificação em tempo de execução se um ponteiro está sendo usado por algum outro bloco de código.

Conclusões / Considerações Finais

Nota-se que a linguagem Rust, apesar de estar dando seus passos iniciais, possui muito potencial, uma comunidade muito ativa e educada, é de fácil compilação para diversas plataformas. Em contraste com C possui diversas vantagens, como a alocação e liberação automática de memória sem garbage collector, impossibilidades de cometer erros de sintaxe durante a programação que geram exceções em tempo de execução, e várias outras qualidades, porém pode ser demasiadamente complexa para iniciantes na programação e sua garantia de segurança limita o programador em algumas situações, isso pode ser visto como uma vantagem, contudo é estressante em alguns momentos.

A performance geral do projeto foi alta, alcançando até 7900% de aumento em throughput de instruções no emulador comparado com o hardware original do NES em processadores modernos.

Desenvolver emuladores pode se ser uma experiência bastante desafiadora e recompensante, recomendado para aprender novas linguagens de programação, aprender sobre otimização ou sobre hardware em geral.

Referências Bibliográficas

José Rafael F. P. de Carvalho. Emulador de Nintendo implementado em Python. Universidade Federal de Mato Grosso do Sul, 2016

Patrick Diskin. Nintendo Entertainment System Documentation. 2004

Brad Taylor. 2A03 technical reference. 2004

bokuweb. Writing An NES Emulator with Rust and WebAssembly. https://medium.com/@bokuweb17/writing-an-nes-emulator-with-rust-and-webassembly-d64de101c49d [Acesso em 28/11/2019]

The NES archive. O NES no Brasil. https://web.archive.org/web/20180922154639/http://www.nesarchive.net/v1/brasil.htm [Acesso em 28/11/2019]

Crates IO. https://crates.io/ [Acesso em 02/12/2019]

Steve Klabnik, Carol Nichols. The Rust Programming Language. 2018

Rust Language. https://www.rust-lang.org/ [Acesso em 03/12/2019]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment