공통
1장 비트·바이트·16진수
비트 여덟 개가 바이트 하나입니다. 2진수와 16진수, 음수와 체크섬까지 숫자를 읽는 법을 익힙니다.
비트와 바이트 쉽게
컴퓨터 안의 모든 것은 "켜짐"과 "꺼짐" 두 가지로 적힙니다. 전구 하나를 떠올려 보세요. 켜지면 1, 꺼지면 0입니다. 이 0 또는 1 한 칸을 비트(bit)라고 합니다.
전구 하나로는 두 가지밖에 나타낼 수 없지만 여덟 개를 나란히 두면 켜고 끄는 조합이 2 × 2 × … × 2 = 256가지가 됩니다. 이 여덟 칸 묶음이 바이트(byte)이고, 0부터 255까지의 숫자 하나를 담습니다. 롬 파일은 이런 바이트가 수백만 개 늘어선 것입니다.
10진수에서 오른쪽부터 1, 10, 100의 자리가 있듯 2진수에는 1, 2, 4, 8, … 128의 자리가 있습니다. 켜진 칸의 자릿값을 더하면 10진수가 됩니다. 아래 「비트 켜고 끄기」에서 직접 켜 보세요. 처음 값 $30은 돌격편 롬 헤더에 실제로 있는 바이트입니다(실제 패치에서).
더 깊이음수, 비트 연산, 10진 모드
음수: 2의 보수
바이트에는 부호 칸이 따로 없습니다. 대신 "맨 왼쪽 비트(128의 자리)가 켜져 있으면 256을 빼서 읽는다"고 약속합니다. 이것을 2의 보수라고 합니다. $FF는 255이면서 −1이고, $80은 128이면서 −128입니다. 같은 바이트를 어느 쪽으로 읽을지는 그것을 쓰는 명령이 정합니다.
2의 보수에서 −x는 "모든 비트를 뒤집고 1을 더한 것"입니다. 「비트 켜고 끄기」에서 $06을 넣고 「모두 뒤집기」, 「+1」을 차례로 누르면 $FA, 곧 −6이 됩니다. 5장의 분기 명령 D0 FA가 "6바이트 되돌아가라"인 이유가 이것입니다. 이때 거리는 분기 명령 자신이 아니라 그다음 명령의 위치부터 셉니다. D0 FA가 $800A에 있으면 다음 명령은 $800C이고, 거기서 6바이트 되돌아간 $8006으로 갑니다.
비트 연산
롬 해킹 공식에는 자릿수 계산보다 비트 연산이 자주 나옵니다.
- AND(
&): 둘 다 1인 칸만 1. 필요한 칸만 남기는 "마스크"로 씁니다.$A8 & $7F = $28은 맨 왼쪽 비트만 지운 것입니다(4장의 LoROM 공식). - OR(
|): 하나라도 1이면 1. 특정 칸을 켤 때 씁니다. - XOR(
^): 서로 다르면 1.^ $FF는 모든 칸 뒤집기입니다(「비트 켜고 끄기」의 「모두 뒤집기」). - 시프트(
<<,>>): 칸을 왼쪽·오른쪽으로 미는 것으로, 한 칸에 ×2, ÷2입니다.n << 15는 n × $8000입니다.
10진 모드(BCD)
점수처럼 화면에 10진수로 보여 줄 값은 한 바이트의 두 16진수 자리에 10진수 두 자리를 따로 넣기도 합니다. $25를 "25"로 읽는 방식으로, BCD라고 합니다. 65c816은 P 레지스터의 D 플래그를 켜면 덧셈(ADC)·뺄셈(SBC)을 이 방식으로 계산합니다. 롬에서 $99 다음이 $00이 되는 값을 보면 BCD를 의심해 보세요.
16진수: 네 칸씩 묶어 읽기 쉽게
0011 0000처럼 0과 1을 여덟 개씩 적으면 읽기 힘듭니다. 그래서 네 칸씩 끊어 한 글자로 적습니다. 네 칸은 0부터 15까지 16가지라서, 0–9 다음에 A–F를 이어 붙여 16가지 글자를 씁니다. 이것이 16진수입니다.
| 10진수 | 2진수 | 16진수 | 10진수 | 2진수 | 16진수 |
|---|---|---|---|---|---|
| 0 | 0000 | 0 | 8 | 1000 | 8 |
| 1 | 0001 | 1 | 9 | 1001 | 9 |
| 2 | 0010 | 2 | 10 | 1010 | A |
| 3 | 0011 | 3 | 11 | 1011 | B |
| 4 | 0100 | 4 | 12 | 1100 | C |
| 5 | 0101 | 5 | 13 | 1101 | D |
| 6 | 0110 | 6 | 14 | 1110 | E |
| 7 | 0111 | 7 | 15 | 1111 | F |
16진수라는 것을 표시하는 방법이 여러 가지라 처음엔 헷갈립니다. $30, 0x30, 30h는 모두 같은 값(10진수 48)입니다. 게임기 쪽 문서와 어셈블리는 $를, 파일 오프셋과 프로그래밍 언어는 0x를 주로 씁니다. 이 가이드도 CPU 주소는 $, 파일 위치는 0x로 적습니다.
KB와 MB, 2의 거듭제곱 쉽게
롬 크기를 말할 때 1KB는 1000이 아니라 210 = 1,024바이트이고, 1MB는 1,024KB = 1,048,576바이트입니다. 1,000단위와 헷갈리지 않게 KiB·MiB라고 적기도 하지만, 롬 해킹 자료는 대부분 KB·MB를 1,024단위로 씁니다. 16진수로 적으면 깔끔합니다. 1KB = $400, 32KB = $8000, 64KB = $10000, 1MB = $100000입니다. 앞으로 자주 나오는 숫자이니 눈에 익혀 두세요.
실제 패치에서 실제 패치에서
헤더 바이트 하나에 정보 두 개
돌격편 롬의 파일 0x7FD5에는 $30이 있습니다. 2진수로 0011 0000입니다. 이 바이트는 칸마다 뜻이 따로 있어서, 오른쪽 네 칸 0000은 "LoROM 방식" (4장), 비트 4(16의 자리)는 "빠른 롬(FastROM)"을 뜻합니다. 같은 LoROM이지만 빠른 롬을 쓰지 않는 제절초는 $20입니다. 「비트 켜고 끄기」에서 비트 4만 꺼 보면 $30이 $20이 됩니다.
체크섬: 모든 바이트의 합
롬 헤더에는 롬의 모든 바이트를 더한 값의 아래 16비트(체크섬)와, 그 값의 모든 비트를 뒤집은 값(보수)이 들어 있습니다. 둘을 더하면 언제나 $FFFF입니다. 패치로 바이트를 바꾸면 합도 바뀌므로 다시 계산해 적어야 합니다.
| 롬 | 체크섬 | 보수 | 합 |
|---|---|---|---|
| 돌격편 일본판 | $10F0 | $EF0F | $FFFF |
| 돌격편 v1.1 | $2B67 | $D498 | $FFFF |
| 슈퍼 마리오 RPG 일본판 | $4575 | $BA8A | $FFFF |
실제 게임기는 이 값을 확인하지 않아서 틀려도 게임은 돌아갑니다. 하지만 에뮬레이터나 롬 관리 도구가 "손상된 롬"으로 표시하므로 한글패치들은 마지막 단계에서 다시 계산합니다.
더 깊이체크섬을 정확히 계산하는 법
- 더할 때 헤더의 보수·체크섬 네 바이트는
FF FF 00 00으로 놓고 셉니다. 보수와 체크섬은 서로 비트를 뒤집은 값이라 네 바이트의 합이 언제나$1FE로 같아서, 어떤 값이 들어 있든 미리 정해 둔 값으로 더해도 결과가 같습니다. - 롬 크기가 2의 거듭제곱이 아니면(예: 3MB) 카트리지가 보여 주는 모양대로 더합니다. 가장 큰 2의 거듭제곱 부분(2MB)을 한 번, 나머지(1MB)는 그 크기(2MB)가 될 때까지 되풀이해 더합니다. kr-patch-toolkit의
krpatch snes checksum이 이렇게 계산합니다. - 체크섬 자리는 헤더 위치에 따라 다릅니다. LoROM·SA-1은 파일 0x7FDC–0x7FDF, HiROM은 0xFFDC–0xFFDF, ExHiROM은 0x40FFDC–0x40FFDF입니다(보수 2바이트, 체크섬 2바이트, 각각 작은 자리 먼저). 카피어 헤더가 붙은 파일은 그 512바이트를 빼고 셉니다.
글자 코드의 맨 위 비트
돌격편 대사는 글자 하나를 16비트 코드로 적는데, 맨 위 비트($8000)가 켜진 코드는 작은 글자로 그립니다. 비트 하나를 "글자 크기 표시"로 빌려 쓴 것입니다. 이렇게 한 칸 한 칸에 뜻을 싣는 일이 롬 안에서는 아주 흔합니다.
직접 해 보기 직접 해 보기
헤더 바이트 세 개(맵 모드, 카트리지 종류, 롬 크기)를 헥스 뷰어로 봅니다. 돌격편 일본판이라면 30 02 0B가 나옵니다.
xxd -s 0x7FD5 -l 3 game.sfc체크섬은 툴킷이 계산해 줍니다. 틀리면 1로 끝나고, --fix를 주면 제자리에 다시 씁니다.
krpatch snes checksum game.sfc
krpatch snes checksum game.sfc --fix실제 패치에서 이 개념이 쓰인 롬 분석 노트
노트의 자세한 절은 「자세히 보기」를 펼친 안쪽에 있습니다.
직접 해 보기 kr-patch-toolkit 명령 설명
참고 자료
- WDC, W65C816S 데이터시트 — D 플래그(10진 모드)와 ADC·SBC의 BCD 동작
- fullsnes, SNES Cartridge ROM Header — 롬 크기 바이트(1 SHL n KB), 체크섬과 보수의 정의
- SNESdev Wiki, ROM header — 맵 모드 바이트의 FastROM 비트
댓글