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.orgscene.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

현재 단락 (1/324)

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

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