On this page
  1. Dropping
  2. Moving
  3. Dealing pieces
  4. Related
  5. Mechanics
  6. References
  7. Sources

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, $02
alexsteb/tetris_disassembly · main.asm L4549–45594
ldh a, [rGRAVITY]
	and a
	jr z, l_20b5
	dec a
	ldh [rGRAVITY], a
…
	ldh a, [$ff00 + $9a]
	ldh [rGRAVITY], a
alexsteb/tetris_disassembly · main.asm L5519–55404

#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

MechanicKindHow
Delayed auto shiftDelay, then repeat23 frames, then every 9
Piece randomizerRerollup to three reads of the hardware divider, tested with an OR of type ids
Drop speedLevel tablea 21-entry table, shifted by 10 in hard mode
Every claim about this game, by concept (12)
generalThe Rev 1 ROM carries the header the disassembly writes, and its bytes sum to the global checksum the disassembly gives, $16BF.Verified
Delayed auto shiftPressing 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.Verified
A blocked shift leaves the counter at 1, so a piece held against a wall tries to move again on the next frame.Verified
With neither Left nor Right held, the counter goes back to 23, so a released direction keeps no charge.Code
The rotation and shift routine returns at once while no piece is falling, so the counter holds its value between pieces.Code
Holding Down doesn't stop a piece from shifting, and holding Left or Right with Down stops the soft drop.Verified
With 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.Verified
Drop speedGravity 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.Verified
Holding Down while pressing Start on the title screen turns on hard mode, which adds 10 to the level index, up to 20.Verified
Piece randomizerTo 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.Code
If 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.Verified
In 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

References

  1. ^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.

    In the claim ledger

  2. ^ 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.

    In the claim ledger

  3. ^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 at DAS_RESET (16) the piece moves and the count drops to DAS_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.

    In the claim ledger

  4. ^ 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.

    In the claim ledger

  5. ^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.

    In the claim ledger

  6. ^ 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.

    In the claim ledger

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

    In the claim ledger

  8. ^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.

    In the claim ledger

  9. ^ 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.

    In the claim ledger

  10. ^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.

    In the claim ledger

  11. ^ 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.

    In the claim ledger

  12. ^ 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.

    In the claim ledger

  13. ^ 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.

    In the claim ledger

Sources

Code

Secondary