ROM retuning — patching a tracker's pitch table
Overwriting the block of precomputed pitch values inside a tracker's ROM, so that every note in the sequencer plays at a frequency you chose. It is how alternative-tunings are done on the game-boy, and it is also the repair for an underclocked machine. The whole job is: find the table, compute a new one, paste, flash.
Why it works#
A game-boy-apu channel is pitched by an 11-bit period value v, where the pulse frequency is 131072 ÷ (2048 − v) Hz. Nothing in the chip knows about notes. lsdj and nanoloop each carry a table of those integers, one per playable note, and index it from the note column — so the table alone decides what "C-4" means. Replace it and the sequencer, the instruments, the tables and the effect commands all carry on unchanged, playing something else (sources: s-little-scale-lsdj-tuning-tables, s-little-scale-nanoloop-18-tet).
The arithmetic#
Forward (frequency → register value), and back again to see what you actually got — little-scale's own spreadsheet columns (source: s-little-scale-nanoloop-18-tet):
Data = 2048 - (131072 / Frequency) round to an integer
Resultant = 131072 / (2048 - Data) what the chip will really play
Write each value little-endian, low byte first: 44 → 2C 00, 156 → 9C 00, 262 → 06 01.
Building the frequency column:
- n-TET:
f = tonic × 2^(k/n)for step k. In a sheet indexed in semitones, an n-EDO step is12/nrows — 0.667 for 18-TET, 0.5 for 24 (source: s-little-scale-nanoloop-18-tet). - Just / ratio scales:
f = tonic × numerator/denominator, then × 2 per octave. The koto sheet is literally two columns of numerator and denominator (source: s-little-scale-koto-tuning). - Check the ends. Below 64 Hz
Datagoes negative and the note does not exist; near the top consecutive entries collapse onto the same value (sources: s-little-scale-koto-tuning, s-little-scale-lsdj-tuning-tables).
Mapping fewer than 12 degrees onto 12 keys#
A pentatonic ROM still has twelve keys per octave, so some of them must repeat. little-scale's hirajoshi sheet pairs each chromatic key with a scale degree explicitly — degrees 0,0,1,1,1,2,2,3,3,3,4,4 across the octave, a 2-3-2-3-2 spread that keeps the tonic on the same key every octave (source: s-little-scale-koto-tuning).
How to do it#
LSDj — by hand#
Offsets verified on 3.8.9 only (source: s-little-scale-lsdj-tuning-tables):
| what | offset | size | notes |
|---|---|---|---|
| GB / GBC / GBA pitch table | $6D3A (27,962) | 216 bytes = 108 × 2 | starts with the bytes 2C 00 9C 00 06 01 |
| Super Game Boy pitch table | $6E12 (28,178) | 216 bytes | immediately after the first, recomputed for the SGB1's ~2.4 % fast clock |
- Open the ROM in a hex editor in overwrite mode (little-scale uses Hex Fiend on OS X). Do not insert — the ROM must stay 1,048,576 bytes.
- Find the table: "starts just after location
$6D30with the bytes2C 00 9C 00 06 01. Select 216 bytes starting with2Cand00" (source: s-little-scale-underclocking-and-retuning). - Paste your 108 little-endian values over it.
- Patch the second block too. KiGB reads only the SGB table "regardless of emulator settings", so a ROM patched in one place will test wrong there and play wrong under a Super Game Boy (source: s-little-scale-lsdj-tuning-tables).
- Save, flash, play.
LSDj — with lsdj_tune#
abrasive's Perl script does all of the above from the command line and, uniquely, rewrites the note names as well, so the display matches the tuning. Built for LSDJ 3.9.9 (source: s-little-scale-abrasive-lsdj-tune):
lsdj_tune --et 24 --base A5 440 --rom lsdj.gb --out lsdj_et24.gb
lsdj_tune --cstep 78 --base 0 70 --rom lsdj.gb --out lsdj_alpha.gb # Wendy Carlos alpha
lsdj_tune --ratio 1,256/243,9/8,32/27,81/64,4/3,729/512,1024/729,3/2,128/81,27/16,16/9,243/128,2 \
--names D,Eb,E,F,F#,G,G#,Ab,A,Bb,B,C,C# --base 0 73.42 --rom lsdj.gb --out lsdj_pyth.gb
Modes: --et n, --cents, --cstep, --ratio; all 108 notes may also be given explicitly. Its stated limitation is that the base-frequency option "won't take custom note names, and sequences always start on the base note".
Nanoloop#
For 1.3d (nl13d.gb, 32,768 bytes): the pitch table is 144 bytes at $730A (29,450), and its stock contents are the same 12-TET-from-C2 series as LSDj's (source: s-little-scale-nanoloop-18-tet). Same procedure, same formula.
Changing the clock instead — underclocking a DMG#
Swapping the DMG's crystal moves pitch and tempo together by one ratio, which is the only way to get a sub-octave Game Boy that still runs a stock ROM (source: s-little-scale-underclocking-and-retuning; the mod itself documented by Gieskes):
- Stock crystal: 4.194304 MHz.
- 2.097152 MHz — exactly half. One octave down, everything still in 12-TET, no patch needed. nitro2k01 called it "the elusive 2.097152mhz crystal oscillator" in June 2009; it is the harder part to buy.
- 2.000000 MHz — the one shops actually stock. The ratio is 0.4768, an octave and 82 cents, so the machine lands nearly a semitone below the octave: "some disgustingly out of tune B or something similar".
- The fix, from Tom Gilmore (10k): build the pitch table for twice the fitted crystal — 4.000000 MHz for a 2 MHz machine — and paste it in. The ROM is then an exact octave below written pitch and in tune with everything else. little-scale's 108-value replacement table is printed in full in
raw/little-scale/retuning-underclocked-game-boy.txt; decoded against 4 MHz its first octave is 65.41 · 69.29 · 73.40 · 77.79 · 82.40 · 87.29 · 92.52 · 97.96 · 103.82 · 110.04 · 116.50 · 123.52 Hz. - Physically: the replacement crystal is taller than the original, so it is "bent over and put on its side, with some tape that insulates it from the rest of the PCB".
- Overclocking is the same problem mirrored. He had built an 8 MHz DMG a year earlier — "almost twice the speed, almost twice the pitch" — and 8.000000 ÷ 4.194304 = 1.9073 is an octave minus 82 cents, the same error as the 2 MHz crystal in the other direction. Asked in 2009 whether his replacement table would also serve at 8 MHz he said "in theory… because I haven't actually tried it out with an overclocked GB"; by the rule above the table for an 8 MHz machine should be computed for 4 MHz, half the fitted crystal (sources: s-little-scale-overclocked-game-boy, s-little-scale-underclocking-and-retuning).
An underclocked machine still syncs with a stock one over the link port (sync): make the normal Game Boy the slave, or run the underclocked one as master at double tempo (254 against 127) (sources: s-little-scale-underclocking-and-retuning, s-chipmusic-lsdj-faq).
Tips & pitfalls#
- Offsets are version-specific. A commenter who applied the 3.8.9 table to 4.0.5 got a cartridge that "would not boot up" (source: s-little-scale-underclocking-and-retuning). Always confirm the signature bytes
2C 00 9C 00 06 01are at the address before overwriting, and keep the original ROM. - Overwrite, never insert. Any length change breaks the ROM.
- Two tables, not one, on LSDj — and KiGB reads the wrong one.
- Test on hardware, or in an emulator that reads the table you patched. Emulator and hardware disagree here more than usual.
- Note names will lie after a hand patch; only
lsdj_tune --namesfixes the display. - The bottom octave and a half is lost below 64 Hz, and the top octave is quantised into unisons — plan the tonic so the useful range sits in the middle (game-boy-apu).
- Retuning and reclocking are independent and compose: an underclocked machine can also be microtuned, by computing the table for both the new clock and the new scale.
Related#
alternative-tunings · game-boy-apu · lsdj · nanoloop · game-boy · sync · semitone-math · scales-and-modes · harmonic-series · abrasive · little-scale · nitro2k01
Sources#
s-little-scale-lsdj-tuning-tables · s-little-scale-underclocking-and-retuning · s-little-scale-koto-tuning · s-little-scale-nanoloop-18-tet · s-little-scale-abrasive-lsdj-tune · s-little-scale-overclocked-game-boy · s-chipmusic-lsdj-faq