맞춤형 RISC‑V 에뮬레이터에서 DOOM 실행하기

DOOM을 거의 모든 컴퓨팅 장치에서 실행할 수 있다는 능력은 하드웨어와 소프트웨어 개발 분야에서 축하받는 벤치마크가 되었습니다. 스마트워치부터 임신 테스트기까지, 이 아이코닉한 게임은 개발자들의 독창성과 현대 컴퓨팅의 유연성을 입증하는 증거가 됩니다. 이 전통은 최근 맞춤형 RISC‑V 에뮬레이터에서 DOOM을 실행하는 프로젝트를 통해 계속되고 있으며, 오픈소스 명령어 집합 아키텍처의 힘과 접근성을 보여줍니다.

이 프로젝트는 인기 밈을 구현할 뿐만 아니라 에뮬레이터 개발, 시스템 콜 구현, 새로운 최소 환경으로 애플리케이션을 포팅하는 과정에서 마주치는 복잡한 문제들에 대한 귀중한 통찰을 제공합니다. "hello world" 단계에서 완전한 플레이 가능한 게임에 이르기까지, 처음부터 기능적인 컴퓨팅 플랫폼을 구축하는 데 필요한 기본 단계를 보여줍니다.

RISC-V 에뮬레이터 구축

이 작업의 핵심은 RV32IM 명령어 집합을 구현하도록 설계된 RISC‑V 에뮬레이터 rvcore입니다. 이 명령어 집합을 선택한 이유는 임베디드 시스템과 교육용 프로젝트에 적합한, 작지만 강력한 아키텍처에 초점을 맞추기 위함입니다. 에뮬레이터는 최소한의 시스템 콜 인터페이스를 제공하는데, 이는 프로그램이 가상 하드웨어와 상호 작용하기 위해 입력/출력 및 메모리 관리와 같은 작업을 수행하는 데 필수적입니다.

ELF 로딩 및 메모리 관리

컴파일된 프로그램을 실행하기 위해 에뮬레이터는 ELF(Executable and Linkable Format) 로딩 기능을 포함합니다. 현재는 단일 PT_LOAD 세그먼트만 지원하여 초기 구현을 단순화하면서도 많은 애플리케이션에 충분합니다. 커스텀 링커 스크립트를 사용해 C 프로그램의 진입점인 _start가 가상 주소 0에 일관되게 위치하도록 하여 ELF 로더 설계를 더욱 간소화했습니다.

메모리 조직은 모든 시스템에서 중요한 요소이며, rvcore 에뮬레이터는 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 +-------------------------------------+

이 레이아웃은 DOOM이 프레임을 렌더링하는 0x705FDD부터 시작되는 VRAM(1,024,000 바이트)에 큰 비중을 할당합니다. 메모리 상단의 작은 큐 영역은 입력을 처리하며, 읽기와 쓰기 인덱스가 별도로 지정됩니다.

DOOM 포팅: 단계별 접근법

DOOM을 실행하기 위한 여정은 더 단순한 작업부터 시작해 점차 필요한 인프라를 구축해 나가는 방식으로 진행되었습니다.

"Hello World"에서 C 프로그램까지

처음에는 기본 "hello world" 어셈블리 프로그램으로 에뮬레이터를 테스트했습니다. 다음 큰 과제는 C 프로그램을 실행할 수 있게 하는 것이었습니다. 이를 위해 임베디드 시스템에 최적화된 C 표준 라이브러리인 newlib을 통합했습니다. newlib을 사용하면 개발자가 시스템 콜 스텁을 하나씩 구현하면서 필요한 기능을 차례로 제공할 수 있어, 목표 프로그램이 성공적으로 실행될 때까지 환경을 점진적으로 완성할 수 있습니다.

doomgeneric 활용

포팅 과정을 단순화하기 위해 프로젝트는 doomgeneric을 활용했습니다. 이 프로젝트는 DOOM을 위한 최소하고 이식 가능한 레이어를 제공하여 플랫폼 특화 세부 사항을 추상화하고, 기본 시스템 콜과 렌더링 메커니즘이 갖춰지면 게임을 실행하기 쉽게 만들어 줍니다. doomgeneric을 사용하면 주요 작업은 에뮬레이터의 가상 하드웨어와 상호 작용하는 데 필요한 스텁을 구현하는 것이 됩니다.

입력 및 출력 처리

DOOM은 출력 화면을 메모리의 고정 영역, 즉 0x705FDD부터 시작되는 VRAM 영역에 렌더링합니다. 에뮬레이터는 이 메모리 영역을 디스플레이 출력으로 해석하는 역할을 담당합니다. 키보드 명령과 같은 입력은 메모리 내 지정된 큐에 기록되어 처리됩니다. 에뮬레이터 내부에서 실행되는 DOOM 인스턴스는 이러한 입력을 가로채어 처리함으로써 인터랙티브 루프를 완성합니다.

DOOM 포트의 지속적인 유산

맞춤형 RISC‑V 에뮬레이터에서 DOOM을 실행하는 프로젝트는 기술 커뮤니티 내에서 깊은 공감을 얻습니다. 이러한 시도는 탐구 정신과 기술적 숙련을 구현한다는 점에서 큰 호응을 얻으며, Hacker News 사용자 @patrickhener가 적절히 표현했듯이:

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

이 감상은 DOOM 포팅이 새로운 하드웨어나 소프트웨어 플랫폼을 탐험하는 개발자들에게 일종의 통과 의식이 되었음을 강조합니다. 또 다른 사용자 @ani_k47은 간단히 "Things like this are really cool"이라며, 이러한 창의적이고 도전적인 작업에 대한 폭넓은 찬사를 요약했습니다.

이 RISC‑V DOOM 에뮬레이터는 오픈소스 하드웨어 아키텍처와 전념하는 개발이 이루어낼 수 있는 가능성을 보여주는 설득력 있는 사례입니다. 고전 게임을 위한 기능적인 플랫폼을 제공할 뿐 아니라, 시스템 에뮬레이션과 애플리케이션 포팅의 저수준 세부 사항에 관심 있는 누구에게든 교육적 자원을 제공합니다.

SUMMARY: 이 글은 DOOM을 실행할 수 있는 RISC‑V 에뮬레이터 개발 과정을 탐구하며, 클래식 게임을 맞춤형 하드웨어 환경으로 옮기는 데 직면한 기술적 도전과 해결책을 강조합니다. 에뮬레이터의 아키텍처, 메모리 관리, 그리고 복잡한 애플리케이션인 DOOM을 포팅하는 과정을 상세히 다룹니다.

TITLE: 맞춤형 RISC‑V 에뮬레이터에서 DOOM 실행하기

Sources