This static site is built entirely with HTML and CSS, without any Javascript. It's also built to last. Check out this manifesto on preserving web content for more details.
Built by Jon Choukroun
When I decided to learn C, a friend suggested I build a Chip-8 emulator. In addition to learning the language with a small, but non-trivial project, I learned quite a bit about how CPUs work - opcodes, memory read/writes, buses, framebuffers, etc. So when I needed a project to learn Rust, I turned to Chip-8 again. I was able to focus on the language details, since I was already familiar with the emulator architecture.
The Chip-8 is an old interpreted language, often picked for a programmer's first emulator project. It's reasonably simple, with a handful of simple instructions. Input, audio, and display functionality are also limited, giving the programmer an introduction to these systems, without overwhelming them.
The design of my Chip-8R emulator is quite simple. It's broken up into the following components:
This struct owns the bus, registers, and frame buffer. The Cpu::run()
function starts the game loop, handling keyboard input; fetching, decoding, and excecuting the next instruction; updating the display; decrementing the sound and delay timers; and playing audio.
To ensure the emulator runs at 700Hz, the run loop also waits. This isn't a cycle-accurate emulator.
Similar to a real bus, this component is how the CPU communicates with the rest of the system - RAM, the display, the audio system, and the keyboard. Rather than calling those modules directly the CPU calls bus functions, like Bus::render()
and Bus::play_audio()
.
For example, the bus allows the CPU to query the current keyboard state. Two instructions are conditional skips that check if a certain key is pressed. Another halts the loop (handled by the CPU) until a key is pressed. This state is all exposed by the bus.
This means the bus module owns the SDL context used to handle input events, draw to screen, and play audio. This context is then passed as a reference to those modules.
The bus's other critical functions are ROM loading, and the introduction prompts. These are rendered using the native dialog crate. I also used this crate to access the file system. Many emulators are run from the command line, using arguments for ROM loading and other system configs. Instead I wanted to explore Rust's native windowing and file system access.
Coming soon...
Coming soon...
This static site is built entirely with HTML and CSS, without any Javascript. It's also built to last. Check out this manifesto on preserving web content for more details.
Built by Jon Choukroun