← Back to Blogs
HN Story

Running DOOM on a Custom RISC-V Emulator

May 6, 2026

Running DOOM on a Custom RISC-V Emulator

The ability to run DOOM on virtually any computing device has become a celebrated benchmark in the world of hardware and software development. From smartwatches to pregnancy tests, the iconic game serves as a testament to the ingenuity of developers and the flexibility of modern computing. This tradition continues with a recent project demonstrating DOOM running on a custom-built RISC-V emulator, showcasing the power and accessibility of the open-source instruction set architecture.

This project not only delivers on the popular meme but also provides valuable insights into the intricacies of emulator development, syscall implementation, and the challenges of porting applications to new, minimal environments. It's a journey from a simple "hello world" to a fully playable game, illustrating the foundational steps required to build a functional computing platform from the ground up.

Building the RISC-V Emulator

The core of this endeavor is a RISC-V emulator, rvcore, designed to implement the RV32IM instruction set. This choice of instruction set signifies a focus on a compact yet capable architecture, suitable for embedded systems and educational projects. The emulator provides a minimal syscall interface, which is crucial for any program to interact with the underlying virtual hardware, handling operations like input/output and memory management.

ELF Loading and Memory Management

To execute compiled programs, the emulator incorporates ELF (Executable and Linkable Format) loading capabilities. Currently, it supports a single PT_LOAD segment, simplifying the initial implementation while still being sufficient for many applications. A custom linker script was employed to ensure that the entry point of C programs, _start, is consistently located at virtual address 0, further streamlining the ELF loader's design.

Memory organization is a critical aspect of any system, and the rvcore emulator defines a specific memory map to accommodate DOOM:

0x7FFFFF +-------------------------------------+
         |                                     |
         | QUEUE_SIZE (32 bytes)               |
         |                                     |
0x7FFFDF +-------------------------------------+ <-- QUEUE_START
0x7FFFDE | QUEUE_READ_IDX                      |
0x7FFFDD | QUEUE_WRITE_IDX                     |
         +-------------------------------------+
         |                                     |
         |                                     |
         | VRAM (1,024,000 bytes)              |
         |                                     |
         |                                     |
0x705FDD +-------------------------------------+ <-- STACK_START
         | Stack                               |
         |                                     |
         | v                                   |
         |                                     |
         | ^                                   |
         |                                     |
         | Program data + Heap                 |
         |                                     |
0x000000 +-------------------------------------+

This layout dedicates a significant portion to VRAM (1,024,000 bytes) starting at 0x705FDD, where DOOM renders its frames. A small queue area at the top of memory handles inputs, with dedicated indices for read and write operations.

Porting DOOM: A Step-by-Step Approach

The journey to running DOOM began with simpler tasks, gradually building up the necessary infrastructure.

From "Hello World" to C Programs

Initially, the emulator was tested with basic "hello world" assembly programs. The next significant hurdle was enabling the execution of C programs. This was achieved by integrating newlib, a C standard library optimized for embedded systems. newlib allows developers to implement syscall stubs incrementally, providing the underlying environment with the necessary functions one by one until the target program can run successfully.

Leveraging doomgeneric

To simplify the porting process, the project utilized doomgeneric. This project provides a minimal, portable layer for DOOM, abstracting away platform-specific details and making it easier to get the game running once the fundamental syscalls and rendering mechanisms are in place. With doomgeneric, the primary task became implementing the required stubs that interact with the emulator's virtual hardware.

Input and Output Handling

DOOM renders its output to a fixed area in memory, specifically the VRAM region starting at 0x705FDD. The emulator is responsible for interpreting this memory region as the display output. Inputs, such as keyboard commands, are handled by writing them into the designated queue in memory. The DOOM instance running within the emulator then intercepts and processes these inputs, completing the interactive loop.

The Enduring Legacy of DOOM Ports

Projects like this RISC-V emulator running DOOM resonate deeply within the technical community. They are often met with enthusiasm, embodying a spirit of exploration and technical mastery. As one Hacker News commenter, @patrickhener, aptly put it:

There is nothing, that can not run DOOM. I love those type of projects. Thanks for showing.

This sentiment highlights the cultural significance of porting DOOM, which has evolved into a rite of passage for many developers exploring new hardware or software platforms. It's a powerful demonstration of a system's capabilities and a testament to the enduring design of the game itself. Another user, @ani_k47, simply stated, "Things like this are really cool," encapsulating the widespread appreciation for such creative and challenging endeavors.

This RISC-V DOOM emulator stands as a compelling example of what can be achieved with open-source hardware architectures and dedicated development. It not only provides a functional platform for a classic game but also serves as an educational resource for anyone interested in the low-level details of system emulation and application porting.

References

HN Stories