|
| #define | DAS_INITIAL_DELAY 16 |
| | DAS initial delay before auto-repeat starts (~267ms at 60fps)
|
| |
| #define | DAS_REPEAT_DELAY 6 |
| | DAS repeat interval once auto-repeat is active (~100ms at 60fps)
|
| |
| #define | FLASH_FRAMES 10 |
| | Duration of the line clear flash animation in frames.
|
| |
| #define | MSG_COLOR_COUNT 8 |
| | Number of colors in the rainbow cycle array.
|
| |
| #define | MSG_COLOR_SPEED 8 |
| | Frames to display each color before advancing to the next.
|
| |
| #define | SPEED_TABLE_LEN 30 |
| | Number of entries in speed_table (levels 0-29)
|
| |
| #define | STATE_GAME_OVER 3 |
| | Game over: board frozen, "GAME OVER" rainbow cycle, await restart.
|
| |
| #define | STATE_LINE_CLEAR 2 |
| | Line clear animation: flashing rows + screen shake.
|
| |
| #define | STATE_PLAYING 1 |
| | Active gameplay: piece falling, player input, gravity.
|
| |
| #define | STATE_TITLE 0 |
| | Title screen: displaying "PRESS START" with rainbow cycle.
|
| |
|
| static void | addScore (u8 lines_cleared) |
| | Add points for cleared lines using NES scoring formula.
|
| |
| static u8 | getGravitySpeed (void) |
| | Look up the gravity speed (frames per drop) for the current level.
|
| |
| static void | handleInput (void) |
| | Process player input with DAS (Delayed Auto Shift) for piece movement.
|
| |
| int | main (void) |
| | Main entry point – Tetris initialization and state machine loop.
|
| |
| static u8 | spawnPiece (void) |
| | Spawn the next piece at the top of the board.
|
| |
| static void | startGame (void) |
| | Initialize a new game: clear board, reset state, start music.
|
| |
| static void | stateGameOver (void) |
| | Game over state: display frozen board with rainbow "GAME OVER" text.
|
| |
| static void | stateLineClear (void) |
| | Line clear animation state: flash rows, shake screen, then collapse.
|
| |
| static void | statePlaying (void) |
| | Main gameplay state: handle input, apply gravity, lock pieces.
|
| |
| static void | stateTitle (void) |
| | Title screen state: rainbow "PRESS START" text until player begins.
|
| |
|
| static LineClearResult | clear_result |
| | Result of the last boardFindFullLines() call (which rows are full)
|
| |
| static s8 | cur_col |
| |
| static u8 | cur_rot |
| |
| static s8 | cur_row |
| |
| static u8 | cur_type |
| |
| static u8 | das_down_timer |
| |
| static u8 | das_left_timer |
| |
| static u8 | das_right_timer |
| |
| static u8 | flash_timer |
| |
| static u8 | game_state |
| |
| static u8 | gravity_speed |
| |
| static u8 | gravity_timer |
| |
| static u16 | level |
| |
| static u16 | line_scores [] = { 0, 40, 100, 300, 1200 } |
| | NES scoring table: base points per number of simultaneous line clears.
|
| |
| static u8 | lines_until_levelup |
| |
| static u16 | msg_colors [] |
| | Rainbow color cycle palette for animated message text (BGR555).
|
| |
| static u8 | next_type |
| |
| u16 | pad_keys [] |
| | Raw joypad state buffer written by the NMI handler every frame.
|
| |
| static u8 | paused |
| |
| static u16 | prev_pad |
| | Previous frame's joypad state (for edge-triggered button detection)
|
| |
| static u16 | score |
| |
| static s8 | shake_dx [] = { 2, -2, 1, -1, 2, -1, 1, 0 } |
| | Screen shake X offsets during line clear animation.
|
| |
| static s8 | shake_dy [] = { -1, 1, -2, 1, 0, -1, 1, 0 } |
| | Screen shake Y offsets during line clear animation.
|
| |
| static u8 | speed_table [] |
| | NES NTSC gravity speed table (frames per automatic drop).
|
| |
| const char | str_gameover [] |
| | "GAME OVER" overlay message string
|
| |
| const char | str_paused [] |
| | Global frame counter incremented by the NMI handler.
|
| |
| const char | str_start [] |
| | "PRESS START" title screen message string
|
| |
| static u16 | total_lines |
| |
Tetris with 3-layer BG architecture, DAS input, and SNESMOD audio.
A full Tetris implementation demonstrating a 3-layer Mode 1 architecture where each BG layer serves a distinct purpose: BG1 (4bpp) renders the playfield with border, locked blocks, and falling piece; BG2 (4bpp) displays the HUD with score, level, lines, and next piece preview; BG3 (2bpp) provides a message overlay (PRESS START, PAUSED, GAME OVER) that floats above the playfield using MODE1_PRIORITY_HIGH.
Input uses NES-authentic Delayed Auto Shift (DAS): an initial delay of 16 frames before auto-repeat kicks in at 6-frame intervals. Gravity follows the NES NTSC speed table (48 frames/drop at level 0 down to 1 frame/drop at level 29+). Scoring uses NES multipliers.
The renderer uses dirty flags to minimize VBlank DMA – only changed layers are flushed each frame. Heavy DMA frames (line clear completion, game over) use force blank to guarantee all VRAM writes succeed. An HDMA gradient provides a color wash effect on the background.
- SNES Concepts
- Mode 1 three-layer architecture (playfield / HUD / message overlay)
- BG3 priority bit for floating overlay above 4bpp layers
- Dirty-flag DMA: selective per-layer VRAM updates each VBlank
- Force blank for heavy multi-layer DMA (BG1+BG2+BG3 > 4KB)
- HDMA gradient effect on background palette
- Delayed Auto Shift (DAS) for responsive piece movement
- NES gravity speed table and scoring system
- SNESMOD: SPC700 music playback (Tetris theme)
- State machine: TITLE, PLAYING, LINE_CLEAR, GAME_OVER
- Screen shake effect during line clear animation
- What to Observe
- Rainbow-cycling "PRESS START" on the title screen
- Pieces fall with increasing speed as level rises
- D-pad moves pieces (DAS auto-repeat after holding); A/B rotates
- Completed lines flash and shake the screen before clearing
- Score, level, and line count update in the right-side HUD
- Next piece preview shows the upcoming tetromino
- START pauses with a "PAUSED" overlay; GAME OVER cycles rainbow text
- Modules Used
- console, sprite, dma, background, input
- See also
- background.h, dma.h, input.h, snesmod.h
| static void stateLineClear |
( |
void |
| ) |
|
|
static |
Line clear animation state: flash rows, shake screen, then collapse.
Runs for FLASH_FRAMES (10) frames. Each frame applies screen shake via BG scroll offsets and toggles the clearing rows' visual appearance. After the animation completes: removes lines from the board, updates scoring/level, performs a force-blank flush (heavy DMA of BG1+BG2), and spawns the next piece.
| static void statePlaying |
( |
void |
| ) |
|
|
static |
Main gameplay state: handle input, apply gravity, lock pieces.
Uses delta rendering: erases the piece from the tilemap buffer, processes input and gravity, then redraws the piece at its new position. This way only the changed cells need updating rather than the entire board.
When a piece locks, checks for completed lines. If found, transitions to STATE_LINE_CLEAR. Otherwise, re-renders the board and spawns next piece.
Initial value:= {
48, 43, 38, 33, 28, 23, 18, 13, 8, 6,
5, 5, 5, 4, 4, 4, 3, 3, 3,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1
}
NES NTSC gravity speed table (frames per automatic drop).
Index = level number. Level 0 = 48 frames/drop (slowest), level 29+ = 1 frame/drop (fastest). Values match the original NES Tetris for authentic feel.
NOT const – const arrays go to SUPERFREE ROM which may land in bank $01+. The compiler generates lda.l $0000,x (always bank $00) for array access, so const data in bank $01 would read garbage. Mutable arrays go to WRAM (bank $00) via CopyInitData at startup.