Tetris (Game Boy)
Nintendo's 1989 Tetris for the Game Boy, read through alexsteb's disassembly and measured in the running ROM.
The code read here is alexsteb’s tetris_disassembly of the Rev 1 ROM. The ROM carries the header the disassembly writes, and its bytes sum to the disassembly’s global checksum1. The annotation is unfinished, so many routines go by their addresses (func_2007, l_2509). Every timing below was also measured in the ROM, running headless in Mesen (probes/tetris/tetris-game-boy.lua).
Its timings differ from the NES version’s throughout. Its DAS, for one, waits 23 frames, then every 92, where the NES game waits 16 frames, then every 63.
#Dropping
Gravity reloads a countdown from a table indexed by level, and the piece falls a row when the countdown passes 0: 53 frames per row at level 0, down to 3 at the top index4,4. Holding Down while pressing Start on the title screen selects hard mode, which adds 10 to the index5.
l_1afa:
ld hl, $1b06
ld d, $00
add hl, de
ld a, [hl]
ldh [rGRAVITY], a
ldh [$ff00 + $9a], a
ret
…
db $34, $30, $2C, $28, $24, $20, $1B, $15, $10, $0A, $09, $08
db $07, $06, $05, $05, $04, $04, $03, $03, $02ldh a, [rGRAVITY]
and a
jr z, l_20b5
dec a
ldh [rGRAVITY], a
…
ldh a, [$ff00 + $9a]
ldh [rGRAVITY], a#Moving
A press of Left or Right moves the piece at once. Held, it moves again after 23 frames, then every 92,2. The counter counts down rather than up:
every frame while a piece falls: # l_2509
if Right was just pressed: count = 23; move
else if Right is held:
count -= 1
if count == 0: count = 9; move
else: (the same for Left; with neither, count = 23)
move: column + 1; if blocked: undo, count = 1
- A blocked shift leaves the count at 16, so a piece against a wall tries again on the next frame6.
- Releasing the direction resets the count to 23, so no charge survives a release7.
- The routine doesn’t run while no piece is falling, so the count holds still between pieces8. With Left held through a lock against the wall, the next piece first moved 8 frames after it appeared, then every 99,9.
- Down doesn’t block shifting; holding a direction with Down blocks the soft drop instead10.
#Dealing pieces
The piece randomizer has no generator of its own: it reads the hardware divider register DIV and reduces it modulo 7 with a counting loop11. The preview shows the pick made one spawn earlier, so each new pick lands two pieces ahead. The routine checks each pick against the piece now spawning and the one shown next by ORing their ids:
on each spawn: # func_2007
current = next; next = hidden
for try in 1..3:
pick = 4 × ((DIV − 1) mod 7) # type ids $00 (L) to $18 (T)
if try == 3 or (pick | next | current) != current: break
hidden = pick
The test rerolls three of a kind, but also any pick whose bits fall inside the spawning piece’s id11. If the three reads were independent, the routine would deal 10.86% (L) to 16.22% (O), with 10.71% repeats12,12. L comes least because its id, $00, has no bits to fail the test12.
The running game deals 10.84% L, 16.74% O, and 11.55% repeats in 9,000 pieces13,13. The shares match the model, but repeats come more often than independent reads would give13.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Delayed auto shift | Delay, then repeat | 23 frames, then every 9 |
| Piece randomizer | Reroll | up to three reads of the hardware divider, tested with an OR of type ids |
| Drop speed | Level table | a 21-entry table, shifted by 10 in hard mode |
Every claim about this game, by concept (12)
References
- ^The Rev 1 ROM carries the header the disassembly writes, and its bytes sum to the global checksum the disassembly gives,
$16BF.Verifiedalexsteb/tetris_disassembly · “Tetris (World) (Rev 1)”How we know
181 db $01 ;mask rom version number 182 db $0a ;header check [ok] 183 db $16, $bf ;global check [ok]
main.asm lines 181–183 at b4bbceb · upstream ↗ In the cartridge (“Tetris (World) (Rev 1)”): the same four header bytes. We don't reproduce the bytes.
Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Tetris (World) (Rev 1) (SHA-1
74591cc9501a…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcPressing Left or Right moves the piece at once and sets a counter to 23; each held frame counts it down, and at 0 the piece moves and the counter restarts at 9, so a held piece moves again 23 frames after the press and then every 9.Verifiedalexsteb/tetris_disassembly · “Tetris (World) (Rev 1)”
How we know
6342l_2509: 6343 ld hl, rBLOCK_X 6344 ldh a, [rBUTTON_HIT] 6345 ld b, a 6346 ldh a, [rBUTTON_DOWN] 6347 ld c, a 6348 ld a, [hl] ⋮ 6350 bit 4, b 6351 ld a, $17 6352 jr nz, l_2527 6353 bit 4, c 6354 jr z, l_254c ⋮
main.asm lines 6342–6365 at b4bbceb · upstream ↗ In the cartridge (“Tetris (World) (Rev 1)”): the shift routine, l_2509. We don't reproduce the bytes.
Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Tetris (World) (Rev 1) (SHA-1
74591cc9501a…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^Holding Left or Right moves the piece at once, again 16 frames later, and then every 6 frames; the press zeroes
autorepeatX, each held frame adds 1, and atDAS_RESET(16) the piece moves and the count drops toDAS_DELAY(10).VerifiedCelestialAmber/TetrisNESDisasm · Birken, "Applying AI to Nintendo Tetris" · “Tetris (USA)”How we know
89DAS_DELAY := $0A 90DAS_RESET := $10
constants.asm lines 89–90 at 94581c0 · upstream ↗ 1622 lda newlyPressedButtons 1623 and #BUTTON_LEFT+BUTTON_RIGHT 1624 bne @resetAutorepeatX 1625 lda heldButtons 1626 and #BUTTON_LEFT+BUTTON_RIGHT 1627 beq @ret 1628 inc autorepeatX 1629 lda autorepeatX 1630 cmp #DAS_RESET 1631 bmi @ret 1632 lda #DAS_DELAY 1633 sta autorepeatX ⋮
main.asm lines 1622–1638 at 94581c0 · upstream ↗ holding down one of those direction buttons causes the game to automatically shift the piece every 6 frames after an initial delay of 16 frames.
Shifting Tetriminos · archived copy ↗ In the cartridge (“Tetris (USA)”): shift_tetrimino, CPU $89AE. We don't reproduce the bytes.
Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Tetris (USA) (SHA-1
3026d28b63d9…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcdGravity reloads a countdown from a table of 21 values, one per level index from 52 at index 0 down to 2 at index 20, and the piece falls a row when the countdown passes 0, so it falls every 53 frames at index 0 and every 3 at index 20.Verifiedalexsteb/tetris_disassembly · “Tetris (World) (Rev 1)”
How we know
4549l_1afa: 4550 ld hl, $1b06 4551 ld d, $00 4552 add hl, de 4553 ld a, [hl] 4554 ldh [rGRAVITY], a 4555 ldh [$ff00 + $9a], a 4556 ret ⋮ 4558 db $34, $30, $2C, $28, $24, $20, $1B, $15, $10, $0A, $09, $08 4559 db $07, $06, $05, $05, $04, $04, $03, $03, $02
main.asm lines 4549–4559 at b4bbceb · upstream ↗ 5522 ldh a, [rGRAVITY] 5523 and a 5524 jr z, l_20b5 5525 dec a 5526 ldh [rGRAVITY], a ⋮ 5537 and a ⋮ 5539 ldh a, [$ff00 + $9a] 5540 ldh [rGRAVITY], a
main.asm lines 5519–5540 at b4bbceb · upstream ↗ In the cartridge (“Tetris (World) (Rev 1)”): the level lookup and its table, func_1ae8. We don't reproduce the bytes.
Measured by a script in MesenCE 2.2.1 (build 4b8a196), on Tetris (World) (Rev 1) (SHA-1
74591cc9501a…). It fails if the result disagrees with the claim.The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^Holding Down while pressing Start on the title screen turns on hard mode, which adds 10 to the level index, up to 20.Verifiedalexsteb/tetris_disassembly
How we know
4534func_1ae8: 4535 ldh a, [$ff00 + $a9] 4536 ld e, a 4537 ldh a, [rHARD_MODE] 4538 and a 4539 jr z, l_1afa 4540 ld a, $0a 4541 add a, e 4542 cp $15 4543 jr c, l_1af9 4544 ld a, $14 ⋮ 4547 ld e, a
main.asm lines 4534–4547 at b4bbceb · upstream ↗ 823first_player_selected: 824 push af 825 ldh a, [rBUTTON_DOWN] 826 bit BTN_DOWN, a 827 jr z, .skip_hard_mode ; set hard mode only if (Start + Down) are pressed. 828 ldh [rHARD_MODE], a
main.asm lines 823–828 at b4bbceb · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abA blocked shift leaves the counter at 1, so a piece held against a wall tries to move again on the next frame.Verifiedalexsteb/tetris_disassembly
How we know
6373l_253a: 6374 ld hl, rBLOCK_X 6375 xor a 6376 ld [$dfe0], a 6377 ldh a, [$ff00 + $a0] 6378 ld [hl], a 6379 call func_2683 6380 ld a, $01 ⋮ 6383 ldh [$ff00 + $aa], a
main.asm lines 6373–6383 at b4bbceb · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^With neither Left nor Right held, the counter goes back to 23, so a released direction keeps no charge.Codealexsteb/tetris_disassembly
How we know
6387l_254c: 6388 bit 5, b 6389 ld a, $17 6390 jr nz, l_255e 6391 bit 5, c 6392 jr z, l_2549
main.asm lines 6387–6392 at b4bbceb · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^The rotation and shift routine returns at once while no piece is falling, so the counter holds its value between pieces.Codealexsteb/tetris_disassembly
How we know
6273func_24bb: 6274 ld hl, rBLOCK_VISIBILITY 6275 ld a, [hl] 6276 cp $80 6277 ret z ; return if there is no falling block (visible)
main.asm lines 6273–6277 at b4bbceb · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcWith Left held through a lock against the wall and on into the next piece, that piece first moved 8 frames after it appeared and then every 9.Verifiedalexsteb/tetris_disassembly
How we know
6274 ld hl, rBLOCK_VISIBILITY 6275 ld a, [hl] 6276 cp $80
main.asm lines 6273–6277 at b4bbceb · upstream ↗ 6391 bit 5, c 6392 jr z, l_2549 6393 ldh a, [$ff00 + $aa] 6394 dec a 6395 ldh [$ff00 + $aa], a 6396 ret nz 6397 ld a, $09 ⋮ 6400 ldh [$ff00 + $aa], a
main.asm lines 6387–6400 at b4bbceb · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^Holding Down doesn't stop a piece from shifting, and holding Left or Right with Down stops the soft drop.Verifiedalexsteb/tetris_disassembly
How we know
5513func_209c: 5514 ldh a, [rBUTTON_DOWN] 5515 and $b0 5516 cp $80 5517 jr z, l_2071
main.asm lines 5513–5517 at b4bbceb · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abTo pick a piece, the game reads the hardware divider
DIV, reduces it modulo 7, and checks the result against the piece now spawning and the one after it; it reads again when the three type ids OR together to the spawning piece's, and takes the third read regardless.Codealexsteb/tetris_disassembly · “Tetris (World) (Rev 1)”How we know
5414 ld a, [rNEXT_BLOCK_TYPE] 5415 ld [hl], a 5416 and $fc 5417 ld c, a
main.asm lines 5414–5417 at b4bbceb · upstream ↗ 5444 ld h, $03 5445l_2043: 5446 ldh a, [rDIV] 5447 ld b, a 5448l_2046: 5449 xor a 5450l_2047: 5451 dec b 5452 jr z, l_2054 5453 inc a 5454 inc a 5455 inc a ⋮
main.asm lines 5444–5479 at b4bbceb · upstream ↗ In the cartridge (“Tetris (World) (Rev 1)”): the spawn routine, func_2007. We don't reproduce the bytes.
Checked against the evidence by its author, an AI model, 2026-10-20.
Checked by a second AI model, 2026-10-21.
- ^ abcIf its three reads were independent and uniform, the routine would deal L 10.86% of the time and O 16.22%, with the others between, and 10.71% of pieces would repeat the one before.Verifiedalexsteb/tetris_disassembly
How we know
5446 ldh a, [rDIV] 5447 ld b, a ⋮ 5457 cp $1c ⋮ 5465 dec h 5466 jr z, l_2062 5467 or d 5468 or c 5469 and $fc 5470 cp c 5471 jr z, l_2043
main.asm lines 5444–5479 at b4bbceb · upstream ↗ Measured by a script in MesenCE 2.2.1 (build 4b8a196), on . It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
- ^ abcIn 9,000 pieces dealt over 474 games on the original Game Boy, L came 10.84% of the time and O 16.74%, and 11.55% of pieces repeated the one before.Verified“Tetris (World) (Rev 1)” · alexsteb/tetris_disassembly
How we know
In the cartridge (“Tetris (World) (Rev 1)”): the spawn routine, func_2007. We don't reproduce the bytes.
5446 ldh a, [rDIV] 5447 ld b, a ⋮ 5457 cp $1c ⋮ 5465 dec h 5466 jr z, l_2062 5467 or d 5468 or c 5469 and $fc 5470 cp c 5471 jr z, l_2043
main.asm lines 5444–5479 at b4bbceb · upstream ↗ Measured by a script. It fails if the result disagrees with the claim.
The numbers come from the run; its author, an AI model, checked the script measures what the claim says, 2026-10-25.
Checked by a second AI model, 2026-10-21.
Checked by a second AI model, 2026-10-25.
Sources
Code
- alexsteb. 2021. “tetris_disassembly: a disassembly of Tetris (Game Boy).” Commit b4bbceb. alexsteb/tetris_disassembly @ b4bbceb · licence, all uses
- 1989. “Tetris (World) (Rev 1).” · licence, all uses
- (CelestialAmber), Amber Brault and contributors. 2024. “TetrisNESDisasm: a disassembly of Tetris (NES).” Commit 94581c0. CelestialAmber/TetrisNESDisasm @ 94581c0 · licence, all uses
- 1989. “Tetris (USA).” · licence, all uses
Secondary
- Birken, Michael. 2014. “Applying Artificial Intelligence to Nintendo Tetris.” original · archived · licence, all uses