Cite as
CelestialAmber/TetrisNESDisasm
Original
https://github.com/CelestialAmber/TetrisNESDisasm
Pinned
CelestialAmber/TetrisNESDisasm @ 94581c0306fa0009fe11fde48222875549d15b8b
Accessed
2026-10-20
Licence
none stated
Credit
Disassembly by Amber Brault (CelestialAmber) and contributors. Its README thanks Eric Anderson (ejona86) for the info file and other files from his taus project that generated much of the code, and qalle2's nes-util for the CHR tools.
Notes
Builds the US ROM (tetris.sha1) and, with PAL=1, the European one (tetris-pal.sha1); an NWC flag builds the Nintendo World Championships cartridge. Line numbers refer to main.asm, constants.asm, and tetris-ram.asm at the pinned commit. The labels (framesPerDropTable, pickRandomTetrimino, DAS_DELAY, …) are the disassembly's names, not Nintendo's.

Cited on

Claims (39)

After a lock, NES Tetris checks four rows, from two above the piece's row down; a row with none of its 10 cells empty is complete, and every row above it moves down one row whole while the top row empties.main.asm L3195–3222main.asm L3228–3244main.asm L3253–3256Code
Tetris (NES, US build) starts its demo when the title screen has waited for Start until the frame counter's high byte, cleared as the title appears, reaches 5: between 1,025 and 1,280 frames (computed), about 17 to 21 seconds. The demo stores 0 as the game type, which the disassembly's comment names A-type, and 0 as the start level.main.asm L603–633main.asm L263–269main.asm L3735–3743main.asm L132–133Code
The US build's demo plays a table of 256 pairs, a controller byte and a count; the demo code holds each byte for its count plus one frame, about 4,780 frames in all (computed), some 80 seconds. It writes the held buttons and the newly pressed ones into the same two bytes the controller routine fills, so the pieces move by the ordinary play code. The @recording branch of the same routine writes such pairs from the live controller when $D0 holds $FF, but the code's only store to $D0 writes 0.data/demo_data.asm L1–3data/demo_data.asm L69–71main.asm L3637–3676main.asm L5541–5549main.asm L282main.asm L3687–3720main.asm L563Code
The demo's pieces come from a fixed list, not the randomizer: in demo mode chooseNextTetrimino reads the next byte of the 256-byte demoTetriminoTypeTable, from 0 at each game start, and takes bits 4-6 as an index into the 7-entry spawn table. An index of 7 reads the byte after the table, $02, the T piece's spawn orientation, so 2 of the 8 index values give a T; 45 of the table's 256 bytes carry a 7 (computed).main.asm L2949–2962main.asm L3013–3020constants.asm L123main.asm L1229–1236data/demo_data.asm L136–137Code
The demo stores 1 as the game mode, which the disassembly's comment names the title screen, when the button table runs out, its read address reaching the piece table's page, or when Start is pressed. The game-over routine has no demo case: a one-player game over, in the demo as in play, stores $03, which the comment names the level menu.main.asm L412–424main.asm L3642–3644main.asm L3678–3684main.asm L3545–3591main.asm L132Code
The disassembly builds the No-Intro Tetris (USA) ROM exactly; with the iNES header tetris.asm writes in place of the ROM's NES 2.0 header, the file's SHA-1 is the one in tetris.sha1.tetris.sha1 L1tetris.asm L14–19Verified
Gravity comes from framesPerDropTable; a piece falls one row every 48 frames at level 0, never more slowly at a higher level, holding steady across levels 10–12, 13–15, and 16–18, down to 2 frames at levels 19–28, and 1 frame from level 29 on.main.asm L1607–1611main.asm L1584–1595Verified
From level 29 ($1D) on, the game skips the table and drops the piece a row every frame.main.asm L1584–1591Verified
fallTimer counts frames, and when it reaches the level's value the piece moves down a row; if that row is blocked, the piece locks where it is, so a resting piece locks after one gravity interval.main.asm L1441–1449main.asm L1570–1594Code
The first piece of a game hangs at the top for 96 frames before gravity starts, because autorepeatY starts at $A0 (−96) and gravity waits until it counts up to 0; pressing Down ends the wait.constants.asm L94main.asm L1239–1240main.asm L1530–1537Verified
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).constants.asm L89–90main.asm L1622–1638Verified
A blocked shift sets autorepeatX to 16, so a piece held against a wall or the stack keeps DAS charged and moves on the first frame the way is clear.main.asm L1661–1665Verified
Spawning a piece leaves autorepeatX alone, so a charge carries over; with the direction held through the entry delay, the new piece moves on its first frame and every 6 frames after.main.asm L2920–2945main.asm L489–492Verified
While Down is held the piece can't shift; shift_tetrimino returns before reading Left or Right.main.asm L1616–1621Verified
A soft drop can't start while Left or Right is held.main.asm L1540–1543Code
Holding Down drops the piece a row on its 3rd frame and then every 2nd frame, and each piece needs a fresh press, because spawning zeroes autorepeatY and only a new press starts the count.main.asm L1544–1569Code
Every new press of Left or Right zeroes the DAS count and moves the piece at once, so pressing on every other frame moves it every 2 frames.main.asm L1622–1624Verified
Pieces spawn centered on column 5 of 0–9; in spawn orientation T, J, Z, S, and L cover columns 4–6, O 4–5, and I 3–6, so most pieces need 3 shifts to reach the right wall and 4 to reach the left.main.asm L2927–2928main.asm L1760–1785Code
At level 29 on an empty board, a piece is under control for 19 frames; held from spawn it shifts 2 columns, with DAS charged 4, and tapped it shifts once every 2 frames.main.asm L1584–1591main.asm L1622–1665Verified
A rotation that would overlap a wall or the stack is undone; the game tries no other position.main.asm L1476–1501Code
Select shows or hides the next piece.main.asm L1453–1460Code
The random number generator is a 16-bit linear feedback shift register at $0017; each step shifts it right and feeds bit 1 XOR bit 9 into the top bit, and from its seed, $8988, it repeats after 32,767 steps.main.asm L5468–5483main.asm L347–351Verified
The NMI handler steps the generator every frame, so the time a player takes to place a piece changes which piece comes next.main.asm L270–274Code
pickRandomTetrimino adds a pick counter to the generator's high byte and keeps the low 3 bits; a result of 7, or the same piece as last time, makes it step the generator and take the high byte's low 3 bits plus the previous piece's orientation id, modulo 7, with no further check.main.asm L2974–3002main.asm L3013–3014Code
The reroll adds the previous piece's orientation id, not its index, so one of its eight results is doubled; after an O ($0A) or an S ($0B) the doubled result is the same piece again.constants.asm L121–139Code
Run over every state of the generator and every pick count, the routine repeats a T, J, Z, L, or I with probability 1/32 and an O or S with 1/16, so 4.0% of the pieces in a long game repeat the one before.main.asm L2974–3002Verified
In a long game the routine deals T and S most often (14.67% and 14.68%) and L and I least (13.89% each); J, Z, and O come 14.29–14.30% of the time.main.asm L2974–3002Verified
Over a long game, 4.03% of pieces repeat the one before, against 14.3% (1 in 7) for a uniform, memoryless pick.main.asm L2974–3002Verified
In 4,800 pieces dealt by the running game, 4.46% repeated the piece before, against 4.03% simulated and 14.3% for a uniform pick; 6.29% of O and S pieces repeated (simulated 6.25%) and 3.76% of the others (3.13%).main.asm L2974–3002Verified
Clearing 1, 2, 3, or 4 lines at once scores 40, 100, 300, or 1,200 points times one more than the level.main.asm L3529–3531main.asm L3460–3469Verified
The clear that raises the level scores at the new level, because the lines and the level go up before the points are added.main.asm L3422–3430main.asm L3460–3465Verified
The score stops at 999,999; when its top digit would pass 9, the code writes 99 99 99.main.asm L3508–3515Verified
The level first rises at 10 × (start level + 1) lines from start levels 0–9, at 100 lines from 10–15, and at 110, 120, 130, and 140 lines from 16, 17, 18, and 19; after that it rises every 10 lines.main.asm L3395–3423Verified
Each time the lines' ones digit turns 0, the level goes up by 1 if it is below the hundreds and tens digits read as one hexadecimal byte; 130 lines gives $13, which is 19.main.asm L3406–3420Code
Holding A while pressing Start on the level menu adds 10 to the chosen level, so games can start at levels 10–19.main.asm L867–876Verified
The next piece spawns 10 frames after a lock with the piece's center on row 18 or 19, and 2 frames later for every 4 rows higher, up to 18 frames.main.asm L2484–2492main.asm L3532–3542main.asm L3074–3077main.asm L3263–3264main.asm L2896–2899Verified
A line clear adds 17 to 20 frames to the entry delay, for the animation that blanks two columns of each cleared row every 4th frame, in 5 steps.main.asm L2757–2760main.asm L2821–2825Verified
The PAL build's DAS waits 12 frames and then repeats every 4.constants.asm L72–75Code
The PAL build's gravity table starts at 36 frames per row at level 0 and reaches 1 frame at level 19.main.asm L1601–1606Code