Skip to content

필사 모드: 현대 개발자를 위한 레트로 컴퓨팅 — 6502·Z80·에뮬레이터, 그리고 잃어버린 디버깅의 즐거움 (2026 심층 가이드)

한국어
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

프롤로그 — 왜 2026년에 6502인가

당신은 오늘 React Server Component를 디버깅하다 짜증이 났을 것이다. 어디서 어떤 코드가 어떤 런타임에서 도는지 추적하다, "이게 서버에서 도나? 클라이언트에서 도나? 둘 다인가? Edge인가?"라는 질문에 답을 못 찾았을 것이다. 또는 Kubernetes Pod이 OOMKilled 되었는데, 정확히 어떤 메모리 페이지가 어디로 갔는지 알 수 없어 그냥 메모리 limit을 두 배로 늘리고 퇴근했을 것이다.

레트로 컴퓨팅은 그 반대의 세계다.

- **64KB의 주소 공간.** 모든 바이트의 행방을 알 수 있다. 사실 알아야만 한다.

- **1.79MHz의 CPU.** 한 명령이 몇 사이클인지 외운다. 사이클 카운팅이 디버깅의 기본기다.

- **인터럽트는 두 개.** NMI와 IRQ. 그게 전부다. 비동기는 두 종류뿐이다.

- **그래픽 메모리는 별도 칩에 산다.** PPU(Picture Processing Unit). 우리가 직접 통신 프로토콜을 작성한다.

- **컴파일이 1초.** 빌드 캐시 따위 필요 없다.

이건 학습 도구로서, 또 주말의 즐거움으로서, 2026년의 개발자에게 가장 좋은 취미다. 이유는 단순하다 — **현대 추상의 모든 누수는 결국 레트로 컴퓨팅이 매일 마주하던 문제로 환원된다.** 메모리 정렬, 캐시 라인, DMA, 인터럽트 우선순위, 자원 경합. 이걸 한 번이라도 8비트 기계로 직접 다뤄본 사람과 안 다뤄본 사람의 디버깅은 다르다.

그리고 솔직히 — **재미있다.** 폴리곤 한 장이 화면에 뜨는 데 5초 걸리던 시절은 이미 지나갔지만, 스프라이트 한 장이 정확한 스캔라인에서 뜨도록 NMI 핸들러를 다듬는 그 감각은 여전히 살아 있다. 작은 화면, 큰 제약, 모든 바이트를 본인이 결정한다는 그 통제감. 이건 SaaS 시대에 잃어버린 종류의 즐거움이다.

이 글에서 다루는 것:

1. 레트로 하드웨어 — 6502·Z80·8086의 아키텍처와 그것이 왜 학습에 좋은지

2. 현대 레트로 툴체인 — cc65·GBDK·CA65·SDCC·z88dk

3. 디버거 — Mesen·bgb·bsnes-plus·FCEUX의 시간 여행과 메모리 뷰

4. 판타지 콘솔 — PICO-8·TIC-80·LIKO-12. 왜 "판타지"이며, 왜 르네상스인가

5. 커뮤니티 — NESDev·AtariAge·GBADev·Pouet·Demoscene

6. 첫 주말 프로젝트 — "내 첫 6502 ROM 굽기" 실전 코스

7. 어른의 변명 — 이게 왜 일에 도움이 되는가 (그리고 사실은 도움이 안 돼도 괜찮다)

1장 · 8비트 기계의 해부학 — 6502와 Z80과 8086

먼저 그림 한 장.

┌──────────────────────────────────────────┐

│ 6502 CPU (NES, 1.79 MHz) │

│ │

│ A (8b) X (8b) Y (8b) │

│ PC (16b) SP (8b, 0x0100~0x01FF) │

│ P (8b: NV-BDIZC flags) │

└────────────┬─────────────────────────────┘

│ 16-bit address bus → 64KB

┌────────────┴─────────────────────────────┐

│ 0x0000-0x07FF : 2KB internal RAM │

│ 0x0800-0x1FFF : RAM mirrors │

│ 0x2000-0x2007 : PPU registers │

│ 0x2008-0x3FFF : PPU mirrors │

│ 0x4000-0x4017 : APU & I/O │

│ 0x4020-0x5FFF : cartridge expansion │

│ 0x6000-0x7FFF : cartridge SRAM (옵션) │

│ 0x8000-0xFFFF : 32KB cartridge PRG-ROM │

└──────────────────────────────────────────┘

이 한 장의 메모리 맵이 NES의 모든 것이다. 더 깊은 추상도, 더 얕은 추상도 없다. 카트리지를 꽂으면 `0x8000`부터 게임 코드가 메모리에 올라간다.

6502 — 단순함의 거장

6502는 8비트 CPU이지만, 그 단순함은 거의 시(詩)에 가깝다.

- **레지스터 세 개.** Accumulator(A), Index X, Index Y. 그게 전부다.

- **스택은 256바이트.** `0x0100`부터 `0x01FF`까지 고정.

- **명령어 56개.** 하루면 외운다.

- **어드레싱 모드 13개.** zero-page, indirect-indexed, indexed-indirect — 이 단어들이 평생 머리에 남는다.

가장 작은 6502 프로그램. NES에서 화면을 파란색으로 채운다.

; init.s — 6502 assembly for NES

; 화면 전체를 파란색으로 채우는 가장 작은 프로그램

.segment "HEADER"

.byte "NES", $1A ; iNES 매직 넘버

.byte 2 ; PRG ROM 2 * 16KB = 32KB

.byte 1 ; CHR ROM 1 * 8KB

.byte $01, $00 ; 매퍼 0, mirroring vertical

.byte 0, 0, 0, 0 ; 패딩

.byte 0, 0, 0, 0, 0

.segment "STARTUP"

reset:

sei ; 인터럽트 비활성

cld ; decimal mode 끔

ldx #$ff

txs ; 스택 포인터 = $FF

; PPU 초기화 대기 (warm-up 두 번)

bit $2002

vblankwait1:

bit $2002

bpl vblankwait1

vblankwait2:

bit $2002

bpl vblankwait2

; 배경 팔레트 설정: $3F00 = universal background

lda #$3f

sta $2006

lda #$00

sta $2006 ; PPU 주소 = $3F00

lda #$01 ; color $01 = blue

sta $2007 ; PPU에 쓰기

; PPU 켜기

lda #%00001000 ; show background

sta $2001

forever:

jmp forever

.segment "VECTORS"

.word 0 ; NMI

.word reset ; Reset

.word 0 ; IRQ

당신이 React를 짜는 동안 본 적이 없을 코드일 것이다. 하지만 보라 — **이 30줄 안에 게임기 전체가 들어 있다.** 매핑된 메모리 주소에 직접 쓰는 것으로 PPU와 통신한다. 추상이 없다. 매뉴얼이 곧 API다.

Z80 — 더 따뜻한 8비트

Z80은 6502보다 약간 더 화려하다. Game Boy, MSX, ZX Spectrum이 다 이 칩이다.

- **레지스터 더 많음.** A, B, C, D, E, H, L. 페어로 BC, DE, HL.

- **두 개의 인덱스 레지스터** IX, IY.

- **섀도우 레지스터 셋.** 인터럽트 핸들러에서 `EXX` 한 번으로 컨텍스트 스위치.

- **명령어 700+ 개.** 6502보다 외울 게 많다.

Z80은 "어셈블리도 사람이 쓸 만하다"는 철학에 가깝다. 6502는 "최소한만 둔다"는 철학에 가깝다. 어느 쪽이 좋은지는 종교 전쟁이고, 두 쪽 다 해보면 양쪽 다 이해가 간다.

8086 — IBM PC의 시작

8086은 16비트이지만, **세그먼트 레지스터**라는 악명 높은 개념을 갖고 있다.

- **세그먼트 + 오프셋 = 물리 주소.** `CS:IP`, `DS:SI`, `SS:SP`, `ES:DI`.

- **각 세그먼트 64KB.** 그래서 1MB 주소 공간을 16비트 레지스터로 본다.

- **MS-DOS의 INT 21h.** OS 호출이 인터럽트 하나로 통일된다.

8086은 6502보다 풍부하고, Z80보다 체계적이지만, 세그먼트가 머리를 아프게 한다. **현대 x86-64 어셈블리가 왜 이렇게 생겼는지** 알고 싶다면 8086부터 보는 게 빠르다.

왜 이게 학습에 좋은가

세 칩의 공통점은 **머리에 다 들어간다**는 것이다.

- 모든 레지스터, 모든 명령어, 모든 메모리 영역을 외울 수 있다.

- 한 명령이 몇 사이클인지 안다. 명령어 표가 단 한 페이지다.

- 인터럽트가 두세 개뿐이다. 비동기 모델이 완전히 가시적이다.

- 컴파일러 없이 작성한다. 어셈블러 출력이 곧 메모리에 올라가는 바이트다.

이걸 한 번 거치고 나면, **"기계가 진짜로 무엇을 하고 있는가"**가 안다. 현대 CPU는 더 복잡하지만, 원리는 같다 — 레지스터·메모리·인터럽트. 64KB 안에서 익힌 그 직관이 64GB의 서버에서도 그대로 작동한다.

2장 · 현대 레트로 툴체인 — C로도 ROM을 굽는다

레트로라고 어셈블리만 쓰는 시대는 끝났다. 2026년의 NES 개발자는 **C로 짠다.** 그래도 어셈블리는 알아야 한다 — 컴파일러 출력을 읽어야 하니까. 하지만 일상의 90%는 C다.

cc65 — 6502의 C 컴파일러

`cc65`는 6502 계열을 위한 C 컴파일러이자 어셈블러 모음이다. NES, Atari 2600/8-bit, Commodore 64, Apple II, Atari Lynx 등을 한 도구로 다룬다.

cc65 설치 (macOS)

brew install cc65

Hello World 컴파일

cc65 -O -t nes hello.c

ca65 hello.s

ca65 -t nes crt0.s

ld65 -C nes.cfg -o hello.nes crt0.o hello.o nes.lib

에뮬레이터에서 실행

mesen hello.nes

다섯 줄로 NES ROM이 나온다. 이게 2026년이다.

`cc65`가 만드는 C는 **현대 C가 아니다.** 함수 호출이 비싸기 때문에(스택을 소프트웨어로 흉내 내야 한다), 전역 변수를 많이 쓰고, 인라인을 적극 활용한다. 컴파일러가 친절하지 않다 — 옵티마이저는 단순하고, 인라인 어셈블리(`__asm__("lda #$00")`)를 자주 섞어 쓴다.

그리고 그게 좋다. **C가 진짜로 어떤 코드를 만드는지 보인다.** 현대 LLVM이 만드는 마법 같은 결과물이 아니라, "이 C 한 줄이 어떤 어셈블리 다섯 줄로 변하는가"가 빤히 보인다.

CA65 — 어셈블러

순수 어셈블리로 짤 거라면 `ca65`(cc65 패키지 안에 들어 있음)를 쓴다. 강력한 매크로, 세그먼트 정의, 조건부 어셈블, include 파일까지 — 모던하다.

; 매크로로 함수 호출 흉내내기

.macro CALL routine

jsr routine

.endmacro

; 조건부 어셈블 (다른 매퍼별로 다른 코드)

.if MAPPER = 1

; MMC1

.elseif MAPPER = 4

; MMC3

.endif

GBDK — Game Boy의 C 컴파일러

Game Boy는 Z80의 사촌(Sharp LR35902)을 쓴다. GBDK는 그 위에 C를 얹는 툴체인이다.

brew install gbdk

lcc -Wa-l -Wl-m -Wl-j -o hello.gb hello.c

hello.gb 가 Game Boy ROM이다

GBDK는 sprite·tile·sound·input 헬퍼를 제공해서 cc65보다 처음 진입이 쉽다. Game Boy의 PPU(여기선 PPU 대신 LCD라 부른다)에 타일을 올리는 API가 한 줄이다.

// hello_gb.c

#include <gb/gb.h>

#include <stdio.h>

void main() {

printf("Hello, Game Boy!\n");

while (1) {

wait_vbl_done(); // VBlank 대기 = 한 프레임

}

}

이게 게임 보이에서 진짜로 돈다. 16비트 PC에서 컴파일한 ROM이 1989년 하드웨어에서 실행되는 것이다.

SDCC와 z88dk — Z80 일반

`SDCC`(Small Device C Compiler)는 Z80·MCS-51·HC08 등을 위한 C 컴파일러다. `z88dk`는 ZX Spectrum·MSX·CP/M 같은 Z80 시스템 전반을 위한 툴체인이다.

어셈블러 vs C — 어디서 어느 쪽?

| 작업 | 어셈블리 | C |

| --- | --- | --- |

| 게임 로직 | 가능하지만 고통 | 적합 |

| NMI 핸들러 | 어셈블리 거의 필수 | 인라인 asm |

| 사이클 정확한 IRQ 분기 | 어셈블리만 | 불가능 |

| 데이터 테이블 | 어셈블리 매크로 | 배열 |

| 사운드 엔진 | 보통 어셈블리 | 가능 |

규칙은 단순하다 — **시간 정확도가 핵심인 곳은 어셈블리, 그 외는 C.** 그리고 처음 시작은 **둘 다 조금씩** 해보고 자기에게 맞는 쪽으로 정착하면 된다.

3장 · 디버거 — 시간 여행과 메모리 뷰의 부활

여기서부터가 진짜다. 현대 디버깅에서 잃어버린 것이 레트로 디버거에 다 있다.

Mesen — NES 디버깅의 황금 표준

`Mesen`은 NES·Famicom·Game Boy·SNES·PC Engine까지 한 에뮬레이터에서 다 돌리는 정밀 에뮬레이터다. 그리고 디버거가 **압도적이다.**

Mesen이 갖춘 것:

- **시간 여행 디버거(rewind).** 마지막 몇 분의 실행을 되감아 한 사이클씩 앞뒤로 본다.

- **메모리 뷰어.** RAM, PRG-ROM, CHR-ROM, OAM, 팔레트, nametable을 실시간 패널로 본다.

- **이벤트 뷰어.** 한 프레임 동안 모든 PPU·CPU 이벤트를 한 장의 격자로 시각화한다. 어느 스캔라인에서 어떤 명령이 도는지 픽셀 단위로 본다.

- **트레이스 로거.** 모든 실행된 명령을 디스어셈블리로 기록한다.

- **CDL(Code/Data Log).** ROM의 어느 바이트가 코드이고 어느 바이트가 데이터인지 자동 라벨링.

- **Lua 스크립팅.** 디버거 안에서 Lua로 사용자 후크를 짠다. "이 변수가 바뀔 때마다 화면에 표시" 같은 게 다섯 줄이다.

이 도구가 **2026년의 Chrome DevTools보다 강력한 어떤 의미에서**라는 사실이, 우리가 잃어버린 게 무엇인지 보여준다.

bgb — Game Boy의 최강 디버거

Game Boy 디버깅의 표준은 `bgb`다. Windows에서 가장 좋고, macOS/Linux에서는 Wine으로 돈다(또는 BGB의 동의어 같은 `SameBoy`).

- 메모리, 레지스터, 스택, I/O 뷰

- 브레이크포인트(주소·조건)

- 스프라이트 뷰어, 타일 뷰어

- 디스어셈블리

bsnes-plus — SNES의 정밀 디버거

`bsnes-plus`는 SNES용 정밀 에뮬레이터로, byuu의 `bsnes`에 디버거를 얹은 포크다. 메모리·트레이스·브레이크포인트 다 갖췄다.

FCEUX — NES의 고전

`FCEUX`는 NES 에뮬레이터 중 가장 오래된 디버거다. Mesen이 등장하기 전까지는 표준이었다. 지금도 가볍게 돌리기 좋고, NES 영상 녹화(`fm2`)·TAS(Tool-Assisted Speedrun) 커뮤니티에서 여전히 표준이다.

SameBoy — macOS/Linux Game Boy 디버거

bgb가 Windows 중심이라면, `SameBoy`는 macOS와 Linux에서 네이티브로 돈다. Mesen만큼 좋은 디버거를 갖췄고, 정밀도도 최상위급이다.

잃어버린 것들

현대 디버거가 못 하는 것들이 여기엔 있다.

- **시간 여행이 기본이다.** Chrome DevTools의 "Time Travel"은 아직도 베타지만, Mesen은 10년 전부터 사이클 단위 되감기를 한다.

- **하드웨어 이벤트 시각화.** 어느 스캔라인에서 어떤 IRQ가 떴는가, 한 프레임 동안 메모리의 어느 영역에 몇 번 접근했는가 — 한 장의 격자에 다 나온다.

- **모든 상태가 보인다.** 64KB는 한 화면에 다 나온다. Pod 안의 16GB 힙을 사람의 눈으로 보는 일은 절대 없다.

레트로 디버깅을 한 번 거치면, **"디버거가 이런 것까지 할 수 있구나"**가 머리에 박힌다. 그리고 현대 디버거의 한계가 보인다.

4장 · 판타지 콘솔 — PICO-8, TIC-80, LIKO-12

여기서 르네상스 이야기를 해야 한다.

**판타지 콘솔(fantasy console)**은 "실제로는 존재한 적 없는 가상의 8비트/16비트 콘솔"을 흉내 내는 개발 환경이다. 진짜 6502는 아니지만, 그것의 정신을 따른다 — 작은 화면, 제한된 팔레트, 제한된 메모리, 단순한 API.

PICO-8 — 그 모든 것의 시작

Lexaloffle의 `PICO-8`이 이 운동의 출발이다. 사양을 보자.

- 화면: 128 x 128, 16색 고정 팔레트

- 코드: Lua, **8192 토큰까지**(라인 수가 아니라 토큰 수)

- 스프라이트: 128장, 8 x 8 픽셀

- 맵: 128 x 32 타일

- 사운드: 64 SFX, 64 패턴, 4 채널

- 컨트롤러: 6버튼(상하좌우, O, X)

- 카트리지: 32KB, 친구에게 공유하면 그 사람도 한 번에 편집 가능

이 제약이 핵심이다. **PICO-8 카트리지는 한 사람이 일주일에 완성할 수 있는 크기다.** 그게 의도된 설계다.

PICO-8의 Lua는 평범한 Lua의 부분집합 + 게임용 API다.

-- pico-8 cart: hello.p8 - 가장 작은 게임

-- 화살표키로 움직이는 점

x=64 y=64

function _update()

if btn(0) then x-=1 end

if btn(1) then x+=1 end

if btn(2) then y-=1 end

if btn(3) then y+=1 end

end

function _draw()

cls()

pset(x,y,11)

print("pico-8!",40,60,7)

end

15줄이다. **15줄에 게임 한 편의 핵심 구조가 들어 있다.** 입력, 업데이트, 렌더. 모든 게임 엔진이 결국 이 세 단계다. PICO-8이 가르치는 게 바로 그것이다.

PICO-8 콘솔 자체는 BBS(`splore`)로 카트리지를 검색·다운로드할 수 있다. 매주 새 카트리지가 수십 개씩 올라온다. 누구나 즉시 소스 코드를 볼 수 있다 — 카트리지가 곧 소스다.

TIC-80 — 오픈소스 대안

`TIC-80`은 PICO-8의 오픈소스 사촌이다.

- 화면: 240 x 136, 16색 팔레트(편집 가능)

- 코드: Lua·Moonscript·JS·Wren·Fennel·Squirrel·Python — 일곱 가지 언어 지원

- 스프라이트: 256장

- 사운드: 4 채널

- 무료, 오픈소스, 브라우저에서도 돈다

PICO-8이 유료(약 USD 15)인 게 부담이라면 TIC-80이 답이다. 사양이 PICO-8보다 약간 너그럽고, 언어 선택지가 넓다. 커뮤니티는 PICO-8보다 작지만 활발하다.

LIKO-12 — 더 친근한 LÖVE 위의 판타지 콘솔

`LIKO-12`는 LÖVE2D 위에서 도는 또 다른 판타지 콘솔이다. PICO-8과 유사한 디자인이지만 더 큰 카트리지와 더 너그러운 한계를 둔다. 학습용으로 좋다.

그 외 — Pixel Vision 8, Pyxel, GB Studio

- **Pixel Vision 8** — C# 기반, 카트리지 단위 개발

- **Pyxel** — Python 기반의 판타지 콘솔. ML 엔지니어에게 친숙

- **GB Studio** — 진짜 Game Boy ROM을 시각 노드 에디터로 만든다. 코드 없이 ROM이 나온다

왜 "판타지" 콘솔인가

이름이 묘하다. **존재한 적 없는 콘솔**을 흉내 내는 것이 왜 의미가 있는가?

답은 두 가지다.

1. **제약이 창의를 낳는다.** PICO-8의 8192 토큰 한계, 128 x 128 화면, 16색 팔레트는 자의적인 것 같지만, 이것이 사람을 **완성 가능한 작은 작품**으로 밀어 넣는다. AAA 게임을 시작한 인디 개발자는 99% 끝내지 못한다. PICO-8 카트리지를 시작한 사람은 일주일에 한 편 끝낸다.

2. **저작권 없는 향수.** 실제 NES·Game Boy 개발도 가능하지만, 닌텐도 IP나 폐기된 SDK 같은 법적 회색지대가 있다. 판타지 콘솔은 처음부터 자기 것이라 자유롭다.

판타지 콘솔은 **8비트 시대의 정신을 2026년에 되살린 것**이다. 그리고 한국·일본·미국·동유럽의 작은 인디 씬이 이걸 중심으로 모여 있다.

5장 · 커뮤니티 — 50년이 지나도 살아 있는 사람들

레트로 컴퓨팅은 사람 없이는 못 한다. 다행히 사람은 많다.

NESDev Wiki — NES 개발의 성서

`nesdev.org`의 위키는 인터넷에서 가장 잘 정리된 하드웨어 문서 중 하나다. 모든 매퍼, 모든 PPU 비트, 모든 APU 채널의 동작이 사이클 단위로 적혀 있다. 새 게임을 만든 사람들이 30년 동안 모은 결과물이다.

함께 보면 좋은 곳:

- **NESDev BBS** — 실시간 질문과 답이 오간다

- **NESDev Discord** — 활발한 채팅, 매주 새 카트리지가 공유된다

- **wiki.nesdev.org/w/index.php/Programming_guide** — 처음 시작하는 사람을 위한 가이드

AtariAge — Atari 컴퓨터·콘솔의 메카

Atari 2600·5200·7800·8-bit 컴퓨터·Lynx·Jaguar — 전 모델을 다루는 거대한 포럼. 새 카트리지가 매년 수십 개씩 정식 발매된다. **그렇다, 2026년에 Atari 2600 카트리지가 새로 나온다.** 그게 AtariAge다.

GBADev / Game Boy Dev — Game Boy 와 GBA

`gbadev.org`(Game Boy Advance)와 `gbdev.io`(Game Boy classic + Color)는 Game Boy 개발자의 허브다. GBDK·rgbds 같은 툴체인, 매뉴얼, 데모, 새 게임이 다 여기 모인다.

6502.org — 6502 전반

NES만이 아니라 Commodore 64, Apple II, Atari 8-bit, BBC Micro까지 — 6502 가족 전반을 다루는 사이트. 어셈블리 튜토리얼이 30년치 쌓여 있다.

Pouet — 데모씬의 보물창고

`pouet.net`은 데모씬(demoscene)의 중앙 데이터베이스다. **데모씬**은 1980년대 유럽에서 시작된 컴퓨터 예술 운동으로, 제한된 하드웨어 위에서 음악·그래픽·코드를 결합한 짧은 프로그램(demo·intro·tracker)을 경연하는 문화다.

- **64KB 인트로** — 64KB 안에 3D 그래픽, 음악, 효과까지 다 담는다

- **4KB 인트로** — 4KB. 0.004메가바이트. 그 안에 한 편의 영상이 들어간다

- **256바이트 인트로** — 256바이트. 1980년대 트윗 한 줄 크기의 ROM에 화면이 움직인다

데모씬은 디지털 시대의 최고 수준의 craft다. Revision, Assembly, Demosplash 같은 연례 파티가 지금도 열린다. **2026년에도 64KB 인트로 경연이 열린다.** 이게 사람들이 잃어버리지 않은 craft다.

Demozoo, Scene.org

`demozoo.org`와 `scene.org`는 데모·인트로의 아카이브다. 30년치 모든 데모를 다운로드 받아 자기 PC에서 돌릴 수 있다. 시간 캡슐이다.

r/EmulationDev, r/PICO8, r/GameBoyDevelopment

Reddit의 작은 서브 커뮤니티들도 활발하다. PICO-8 서브레딧은 매주 PICO-8 카트리지 공유와 코드 리뷰가 오간다.

6장 · 주말 프로젝트 — 첫 6502 ROM을 굽는 12시간 코스

이론은 끝났다. 이제 실제로 만들어보자.

토요일 오전 — 환경 구축 (1시간)

macOS

brew install cc65 mesen

또는 Linux

sudo apt install cc65

Mesen은 .NET 6 필요, github.com/SourMesen/Mesen2 릴리스에서 다운

토요일 오전 — Hello World ROM (2시간)

GitHub의 `bbbradsmith/NES-ca65-example`을 클론한다. 이게 cc65 기반 NES 개발의 표준 출발점이다.

git clone https://github.com/bbbradsmith/NES-ca65-example

cd NES-ca65-example

make

mesen example.nes

화면에 "Hello World"가 뜬다. **첫 NES 프로그램**이다. `Makefile`을 열어 빌드 과정을 본다. `.s` 파일들을 열어 어셈블리를 본다. **Mesen의 디버거를 켠다.** F10으로 한 명령씩 실행한다.

토요일 오후 — 컨트롤러 입력 받기 (3시간)

NES 컨트롤러는 8비트 시프트 레지스터다. `$4016`에 1을 쓰면 입력을 래치(latch)하고, 0을 쓰면 시프트가 시작된다. 그 다음 `$4016`을 8번 읽으면 A·B·Select·Start·Up·Down·Left·Right가 차례로 나온다.

read_controller:

lda #$01

sta $4016 ; 래치 시작

lda #$00

sta $4016 ; 시프트 시작

ldx #8

loop:

lda $4016 ; 한 비트씩 읽기

lsr ; 비트를 carry로

rol controller ; carry를 controller로

dex

bne loop

rts

`controller` 변수의 각 비트가 각 버튼이다. **이 8줄이 NES 입력 시스템의 전부다.** Chrome에서 `addEventListener("keydown", ...)`을 쓰던 당신에게는 이게 신선할 것이다 — 인터럽트 없이, 폴링만으로, 8번 읽고 끝.

토요일 저녁 — 스프라이트 움직이기 (4시간)

NES의 OAM(Object Attribute Memory)은 64개 스프라이트 슬롯이다. 각 스프라이트는 4바이트(Y·tile·attribute·X). 한 프레임에 한 번, `$2003` `$2004`를 통해 OAM을 갱신한다(또는 DMA로 `$4014`에 페이지 한 장을 통째로 던진다).

PICO-8에서는 `spr(0, x, y)` 한 줄로 끝나던 것이 NES에서는 12줄짜리 OAM 갱신 루틴이다. **그 차이가 학습이다.**

일요일 오전 — 사운드 (2시간)

NES의 APU는 채널 5개 — 펄스 두 개(`$4000-$4007`), 삼각파(`$4008-$400B`), 노이즈(`$400C-$400F`), DMC(`$4010-$4013`). 각 채널의 레지스터에 직접 쓰면 음이 난다.

; 펄스 1 채널로 A4(440Hz) 내기

lda #%10111111 ; duty 50%, 무한 길이, 볼륨 15

sta $4000

lda #%00001000 ; sweep 끄기

sta $4001

lda #$fd ; period low byte

sta $4002

lda #$00 ; period high byte

sta $4003

4바이트의 레지스터에 직접 쓰면, NES가 노래한다. **이게 사운드 카드 추상 없이 직접 합성 칩을 조작하는 경험이다.**

결과 — 12시간이면 무엇이 나오는가

토요일과 일요일 둘이 끝나면, 당신은 다음을 갖고 있을 것이다.

- "Hello World" ROM

- 컨트롤러로 스프라이트가 움직이는 ROM

- 충돌 판정과 점수 표시가 들어간 작은 게임의 원형

- 단순한 BGM 한 곡

- **그리고 그 모든 것이 어떻게 도는지에 대한 머릿속 모델**

그 모델이 가장 큰 수확이다. **64KB 안에서 한 프레임이 60Hz로 도는 것을 직접 작성해본 사람의 디버깅은 다르다.**

7장 · 어른의 변명 — 일에 도움이 되는가

레트로를 시작한 친구에게 우리는 종종 묻는다 — "그래서 그게 일에 도움이 돼?" 답은 둘이다.

도움이 된다, 진짜로

- **메모리 정렬과 캐시 라인.** 6502는 zero-page(`0x0000-0x00FF`)가 다른 영역보다 빠르다. 한 사이클 차이다. **현대 CPU도 똑같다.** L1·L2·L3·메인 메모리. 이 직관이 똑같이 작동한다.

- **인터럽트 지연.** NMI가 한 프레임 안에 끝나야 한다는 제약이, "큐가 차면 어떻게 되는가"라는 현대 분산 시스템의 질문과 같은 패턴이다.

- **명령 사이클 카운팅.** "이 함수가 200 사이클 걸리는가"를 자연스럽게 생각하게 된다. **CPU bound 분석이 직관이 된다.**

- **DMA의 본질.** NES의 OAM DMA는 한 프레임에 한 번, 페이지 한 장을 통째로 PPU로 옮긴다. 이걸 보면 PCIe DMA, GPU 텍스처 업로드, NVMe 큐의 동작이 한 줄로 이해된다.

- **에러 코드 없는 세계.** 함수는 결과를 carry 플래그로 알린다. Result/Option 타입이 왜 발명되었는지, carry 플래그를 한 번 쓰면 안다.

도움이 안 돼도 괜찮다

그리고 솔직히 — **도움이 안 돼도 괜찮다.** 모든 취미가 일에 도움이 되어야 하는 건 아니다. 음악가가 클래식 기타를 친다고 일렉트릭 기타가 더 늘지는 않는다. 그래도 즐겁다.

레트로 컴퓨팅의 가장 큰 가치는 **즐거움**이다. 작은 화면, 큰 제약, 매주 끝낼 수 있는 프로젝트, 50년이 지나도 살아 있는 커뮤니티, 디버거의 시간 여행, 메모리의 모든 바이트를 본인이 결정한다는 통제감 — 이게 SaaS 시대에 잃어버린 종류의 즐거움이다.

당신이 일주일에 React로 짠 코드 100%가 6개월 안에 사라질 수 있다. PICO-8 카트리지 한 편은 30년 후에도 그대로 돈다. **그 차이가 craft다.**

에필로그 — 8KB ROM 안의 영혼

이 글이 길어졌지만, 한 줄로 줄이면 이렇다.

> "64KB 안에서 우주를 만들 수 있다. 그리고 그 우주는 30년이 지나도 그대로 돈다."

시작 체크리스트

이번 주말에 시작한다면:

- [ ] `brew install cc65 mesen` (또는 운영체제별 동등 명령)

- [ ] PICO-8 또는 TIC-80 설치 (TIC-80은 무료)

- [ ] `bbbradsmith/NES-ca65-example` 클론, `make`

- [ ] Mesen에서 ROM 열고 F10으로 한 명령씩 실행

- [ ] `nesdev.org` 위키 페이지 30분 읽기

- [ ] Pouet에서 64KB 인트로 한 편 보기

- [ ] 첫 번째 카트리지를 PICO-8 BBS에 올리기 (또는 itch.io에)

안티 패턴

- **AAA 게임을 시작하지 말 것.** 일주일에 끝낼 수 있는 크기로 시작한다. 첫 카트리지는 "공이 화면을 튕긴다" 정도가 적당하다.

- **어셈블리 먼저 다 외우려 들지 말 것.** 필요한 명령만 그때그때 본다. 명령어 표를 책상 옆에 둔다.

- **에뮬레이터만 쓰지 말 것.** 한 번은 실제 하드웨어에 ROM을 올려본다. `EverDrive` 같은 플래시 카트리지를 쓰면 자기가 만든 ROM을 진짜 NES에서 돌릴 수 있다.

- **혼자 하지 말 것.** NESDev Discord, PICO-8 BBS, AtariAge — 사람이 있는 곳에 합류한다.

- **완성하지 않은 카트리지를 너무 오래 끌지 말 것.** 80% 완성하고 1년 묵히느니, 50% 완성을 일주일에 올리는 게 낫다.

다음 글 예고

다음 글은 **"PICO-8로 7일에 게임 한 편 완성하기 — 8192 토큰의 시"**(가제)로, 이 글의 4장에서 다룬 PICO-8을 본격 다룬다. 토큰 절약 기법, 0번부터 시작하는 게임 디자인, BBS에 카트리지 올리기까지 — 한 주 단위 일정으로 정리한다.

그 다음 글은 **"데모씬, 64KB 안의 우주 — 인트로 한 편이 어떻게 만들어지는가"**(가제)로, 데모씬의 역사와 현재, 그리고 첫 256바이트 인트로를 짜는 가이드를 다룬다.

> "추상이 너무 많이 쌓이면, 한 번씩 바닥으로 내려가야 한다. 그래야 그 위에 무엇이 쌓였는지 보인다."

>

> — 레트로 컴퓨팅을 다시 시작한 어느 주말, 끝.

참고 / References

- [NESDev Wiki](https://www.nesdev.org/wiki/)

- [NESDev BBS](https://forums.nesdev.org/)

- [cc65 — 6502 C 컴파일러](https://cc65.github.io/)

- [GBDK — Game Boy Development Kit](https://github.com/gbdk-2020/gbdk-2020)

- [Mesen — NES/SNES/GB 멀티 플랫폼 정밀 에뮬레이터](https://www.mesen.ca/)

- [bgb — Game Boy 디버거](https://bgb.bircd.org/)

- [SameBoy — macOS/Linux Game Boy 에뮬레이터](https://sameboy.github.io/)

- [bsnes-plus — SNES 디버거 포크](https://github.com/devinacker/bsnes-plus)

- [FCEUX — NES 에뮬레이터](https://fceux.com/web/home.html)

- [6502.org](http://www.6502.org/)

- [PICO-8 공식](https://www.lexaloffle.com/pico-8.php)

- [TIC-80 공식](https://tic80.com/)

- [LIKO-12 GitHub](https://github.com/RamiLego4Game/LIKO-12)

- [Pyxel — Python 판타지 콘솔](https://github.com/kitao/pyxel)

- [GB Studio — Game Boy 비주얼 IDE](https://www.gbstudio.dev/)

- [AtariAge — Atari 커뮤니티](https://atariage.com/)

- [GBADev — Game Boy Advance 개발](https://gbadev.net/)

- [gbdev.io — Game Boy classic/Color](https://gbdev.io/)

- [Pouet — 데모씬 데이터베이스](https://www.pouet.net/)

- [Demozoo — 데모씬 아카이브](https://demozoo.org/)

- [Scene.org — 데모 파일 아카이브](https://www.scene.org/)

- [SDCC — Small Device C Compiler](https://sdcc.sourceforge.net/)

- [z88dk — Z80 툴체인](https://z88dk.org/)

- [Easy 6502 — 브라우저에서 6502 배우기](https://skilldrick.github.io/easy6502/)

- [NES-ca65-example — cc65 기반 예제](https://github.com/bbbradsmith/NES-ca65-example)

- [Famitracker — NES 음악 트래커](http://famitracker.com/)

- [EverDrive — 실기 플래시 카트리지](https://krikzz.com/)

- [Revision Demoparty — 연례 데모씬 파티](https://2024.revision-party.net/)

현재 단락 (1/324)

당신은 오늘 React Server Component를 디버깅하다 짜증이 났을 것이다. 어디서 어떤 코드가 어떤 런타임에서 도는지 추적하다, "이게 서버에서 도나? 클라이언트에서 ...

작성 글자: 0원문 글자: 13,817작성 단락: 0/324