|
| static void | check_bricks (void) |
| | Check collision with bricks.
|
| static void | check_paddle (void) |
| | Check collision with paddle.
|
| static void | die (void) |
| | Handle player losing a life.
|
| static void | draw_screen (void) |
| | Update all sprite positions.
|
| static void | handle_pause (void) |
| | Handle pause functionality.
|
| int | main (void) |
| | Game initialization and main loop.
|
| static void | move_ball (void) |
| | Update ball position and handle wall collision.
|
| static void | move_paddle (void) |
| | Handle paddle movement from joypad input.
|
| static void | mycopy (u8 FAR *dest, const u8 *src, u16 len) |
| | Simple byte-by-byte memory copy.
|
| static void | new_level (void) |
| | Initialize a new level.
|
| static void | remove_brick (void) |
| | Remove a brick and update score.
|
| static void | run_frame (void) |
| | Run one frame of gameplay.
|
| static void | start_game (void) |
| | Set up a new game: fresh maps, bricks, palette, score, lives, level.
|
| static void | writenum (u16 num, u8 len, u16 FAR *tilemap, u16 pos, u16 offset) |
| | Write a right-aligned number to a tilemap buffer.
|
| static void | writestring (const char *st, u16 FAR *tilemap, u16 pos, u16 offset) |
| | Write a null-terminated string to a tilemap buffer.
|
|
| static u16 | a |
| static u16 | b |
| FAR u16 | backmap [] |
| | BG3 tilemap RAM copy (0x400 entries = 2KB), far RAM.
|
| u8 | backpal [] |
| | 7 level color sets for palette cycling (7 x 16 bytes = 112 bytes)
|
| u8 | backpal_end [] |
| u8 | bg1map [] |
| | BG1 tilemap: playfield border frame and HUD layout (2KB).
|
| u8 | bg1map_end [] |
| u8 | bg2map [] |
| | BG3 tilemap: all 4 background fill patterns concatenated (2KB).
|
| u8 | bg2map0 [] |
| | Individual BG3 patterns (pointers into bg2map for per-level selection).
|
| u8 | bg2map1 [] |
| u8 | bg2map2 [] |
| u8 | bg2map3 [] |
| u8 | bg2map_end [] |
| static u16 | blockcount |
| FAR u16 | blockmap [] |
| | BG1 tilemap RAM copy (0x400 entries = 2KB), far RAM.
|
| FAR u8 | blocks [] |
| | Mutable brick state array (100 entries, one per grid cell).
|
| const u8 | brick_map [] |
| | Default brick layout (10x10 grid, 100 bytes).
|
| static u16 | bx |
| static u16 | by |
| static u16 | c |
| static u16 | color |
| static u16 | hiscore |
| static u8 | i |
| static u8 | j |
| static u8 | k |
| static u16 | level |
| static u16 | level2 |
| static u16 | lives |
| static u16 | obx |
| static u16 | oby |
| static u16 | pad0 |
| u16 | pad_keys [] |
| | Raw joypad state buffer written by the NMI handler every frame.
|
| FAR u16 | pal [] |
| | Palette RAM copy (0x100 entries = 512 bytes), far RAM.
|
| u8 | palette [] |
| | Full 256-color palette for BG and sprite layers (512 bytes).
|
| u8 | palette_end [] |
| static s16 | pos_x |
| static s16 | pos_y |
| static u16 | px |
| static u16 | score |
| const char | str_blank [] |
| | Blank string (spaces) for clearing message areas.
|
| const char | str_gameover [] |
| | "GAME OVER" message string
|
| const char | str_paused [] |
| | "PAUSED" message string
|
| const char | str_ready [] |
| | "READY" message string (in data.asm for guaranteed bank $00 placement)
|
| u8 | tiles1 [] |
| | BG tiles: border, bricks, and font glyphs (4bpp, ~3840 bytes).
|
| u8 | tiles1_end [] |
| u8 | tiles2 [] |
| | Sprite tiles: ball, paddle, and shadow shapes (4bpp, ~592 bytes).
|
| u8 | tiles2_end [] |
| static s16 | vel_x |
| | Ball X velocity (-2 to +2 pixels/frame).
|
| static s16 | vel_y |
Breakout game with brick collision, paddle physics, and level cycling.
A complete Breakout game demonstrating how multiple SNES subsystems work together in a real game. The playfield uses background tiles (not sprites) for bricks, enabling 100 on-screen bricks without hitting the 128-sprite hardware limit. The paddle and ball are hardware sprites with shadow sprites for depth effect.
Key technical patterns: RAM tilemap buffers for runtime brick destruction, overlapping BG1/BG3 tilemaps in VRAM to save memory (requiring atomic dual-DMA updates), palette cycling for per-level color variation, and direct oamMemory[] writes (the escape hatch next to oamSet(); oamSet has been an asm routine since 2026-03-03, so either is fine at 10 sprites).
Ball bounce angle varies by paddle hit zone (4 zones), creating the classic Breakout aiming mechanic. Scoring uses level multipliers.
Based on SNES SDK game by Ulrich Hecht, PVSnesLib port by alekmaul.
- SNES Concepts
- Mode 1: BG1 (4bpp playfield), BG3 (2bpp background pattern), OBJ sprites
- Overlapping VRAM tilemaps (BG1 at $0000, BG3 at $0400 share $0400-$07FF)
- Atomic dual-DMA: both tilemaps uploaded in same VBlank to prevent flicker
- RAM tilemap buffers for runtime modification (brick destruction, HUD)
- Palette cycling: 7 color sets rotated per level via CGRAM DMA
- Direct oamMemory[] writes for 10 sprites (ball + paddle + shadows)
- Secondary sprite name table access (tile | 256 for VRAM $2000+)
- Force blank for bulk VRAM uploads exceeding VBlank DMA budget
- What to Observe
- Press START to begin; paddle moves with D-pad (hold A for speed boost)
- Ball bounces off walls, paddle, and bricks with angled deflection
- Bricks disappear on hit; score and high score update in the HUD
- Background color changes each level (palette cycling)
- Shadow sprites create a subtle depth effect under ball and paddle
- START pauses/unpauses the game
- Modules Used
- console, sprite, dma, background, input
- See also
- sprite.h, dma.h, background.h, input.h
| void check_paddle |
( |
void | | ) |
|
|
static |
Check collision with paddle.
BOUNCE PHYSICS: Ball bounce angle depends on where it hits the paddle: Left edge: Sharp left angle (vel_x = -2, vel_y = -1) Left-mid: Slight left (vel_x = -1, vel_y = -2) Right-mid: Slight right (vel_x = +1, vel_y = -2) Right edge: Sharp right angle (vel_x = +2, vel_y = -1)
This creates the classic breakout gameplay where paddle position matters for aiming the ball.
Handle player losing a life.
< Alias for the GAME OVER message
< Alias for the blank clearing string
< Alias for the blank clearing string
< Alias for the READY message
< Alias for the blank clearing string
< Alias for the blank clearing string
| void draw_screen |
( |
void | | ) |
|
|
static |
Update all sprite positions.
SPRITE ORGANIZATION: This game uses 10 hardware sprites for the ball and paddle. Sprite 0 is left unused: an old comment reported a corruption there that was never reproduced nor explained; nothing in the SDK reserves it.
Sprite Assignment: Sprite 1: Ball Sprites 2-5: Paddle (left cap, 2x middle mirrored, right cap) Sprite 6: Ball shadow (offset +3,+3 pixels, lower priority) Sprites 7-10: Paddle shadow (offset +4,+4 pixels, lower priority)
TILE NUMBERING: Sprite tiles are at VRAM $2000 (secondary name table). To access secondary table, set bit 8 of tile number: tile | 256
Tile Layout in tiles2: Tile 15: Paddle left cap Tile 16: Paddle middle (used twice, second time H-flipped) Tile 17: Paddle right cap Tile 18: Paddle shadow left Tile 19: Paddle shadow middle Tile 20: Ball Tile 21: Ball shadow
SHADOW TECHNIQUE: Shadows are separate sprites with:
- Same shape but darker color (shadow palette)
- Offset position (+3 or +4 pixels down-right)
- Lower priority (1 vs 3) so they appear behind main sprites
Initialize a new level.
This function handles level transitions:
- Reset ball/paddle positions
- Reset background tilemap and brick array from ROM
- Cycle background color
- Rebuild brick wall in existing BG1 tilemap (preserves HUD)
- Upload everything to VRAM
- Wait for player to press START
CRITICAL DMA LESSON: BG1 tilemap (0x0000-0x07FF) and BG3 tilemap (0x0400-0x0BFF) OVERLAP in VRAM at addresses 0x0400-0x07FF. Both tilemaps must be uploaded in the SAME VBlank, or the intermediate frame shows corruption.
This was a hard-learned lesson: splitting "to be safe" actually caused a visible pink flash. PVSnesLib does 4.5KB+ in one VBlank.
< Alias for the READY message
< Alias for the blank clearing string
< Alias for the blank clearing string
Set up a new game: fresh maps, bricks, palette, score, lives, level.
Called once at boot and again after GAME OVER, so the game loops title → play → game over → play (until 2026-09-26 GAME OVER froze the console in a while (1)). The fresh map carries the HUD's starting values; the caller keeps a beaten high score. Works on the RAM buffers only; the caller uploads them.
< Alias for the READY message