C++로 게임보이 에뮬레이터 만들기: 저수준 컴퓨팅과 C++ 학습의 여정!

by DD
3개월 전
조회수 34

C++를 활용한 게임보이 에뮬레이터(GameBoy Emulator) 제작 과정을 상세히 설명하며, 저수준 컴퓨팅 및 C++ 학습 경험 공유

MBC1 지원(MBC1 Support), PPU 구현, 테스트 롬 활용 등 에뮬레이터 개발 과정에서 겪은 기술적 난관과 해결 과정 제시

닌텐도(Nintendo)의 법적 문제로 인해 부트롬(Boot ROM)을 구현하지 않음

2025년 6월 시작, 2026년 1월 본격적인 개발 시작, 테트리스(Tetris) 실행 성공

에뮬레이터 개발 동기 및 목표

저자는 C#, Golang, JavaScript/TypeScript 등 다양한 언어를 사용했지만, 저수준 컴퓨팅과 C/C++에 대한 지식이 부족했음을 인지하고, 게임보이 에뮬레이터 제작을 통해 학습 목표를 설정했다. 특히, 오픈소스 프로젝트에 대한 동경과 게임 개발에 대한 열정을 바탕으로, 실제 프로젝트를 통한 학습(Learning by Doing) 방식을 선택했다. 이 과정에서 에뮬레이터 개발은 저수준 컴퓨팅 기술 습득과 C++ 학습을 위한 효과적인 수단으로 작용했다.

초기 설계 및 CPU/MMU 클래스 구현

에뮬레이터 개발은 2025년 6월에 시작되었으며, Pan Docs와 CTurt의 블로그를 참고하여 초기 설계를 진행했다. CPU와 MMU 클래스를 분리하여 헤더 파일과 소스 파일을 구성하는 C++ 표준을 따랐다. CPU 클래스에는 CPU 레지스터(CPU Registers)16비트 레지스터(16-bit Registers)를 정의하고, MMU 클래스에서는 메모리 맵(Memory Map)을 구현하여 ROM, VRAM, I/O 레지스터 등을 할당했다. 이러한 구조는 코드의 가독성을 높이는 데 기여했다.

MBC1 지원 및 롬 뱅킹(ROM Banking) 구현

저자는 MBC1 기반의 카트리지를 지원하기 위해, MMU 클래스 내에서 롬 뱅킹 로직을 구현했다. MBC1은 512KiB에서 2MiB의 ROM과 8KiB에서 32KiB의 RAM을 지원하며, 특정 메모리 주소에 쓰기를 통해 ROM 및 RAM 뱅크를 전환한다. MMU::read_byte() 함수를 수정하여 롬 뱅킹을 처리하고, Super Mario Land와 같은 게임의 실행을 가능하게 했다. 또한, 배터리 백업 RAM 기능을 구현하여 Link's Awakening과 같은 게임의 저장을 지원했다.

PPU 구현 및 렌더링 문제 해결

PPU(Picture Processing Unit) 구현 과정에서 8비트 정수형 변수로 인해 PPU 사이클(PPU Cycles)이 제대로 계산되지 않는 문제를 겪었다. 이를 16비트 정수형으로 변경하여 해결했으며, VRAM/PPU I/O 레지스터 초기화 문제를 해결하여 렌더링 문제를 해결했다. SDL의 SDL_SetTextureScaleMode(SDL_SCALEMODE_NEAREST) 함수를 사용하여 2배 확대한 화면의 선명도를 개선했다.

테스트 롬(Test ROM) 활용 및 정확도 개선

테트리스(Tetris) 실행 이후, c-sp의 Game Boy Test Roms 저장소를 활용하여 에뮬레이터의 정확도를 개선했다. dmg-acid2 테스트 롬을 사용하여 PPU 에뮬레이션 문제를 디버깅하고, Object to Background Priority 비트(bit 7) 관련 문제를 해결했다. 이러한 테스트 롬을 통해 에뮬레이터의 정확도를 높이고, 다양한 게임의 호환성을 확보했다.

Learning Low-Level Computing and C++ by Making a Game Boy Emulator