|
| #define | FRAME_JUMP 1 |
| | Dynamic sprite frame index: jump pose.
|
| #define | FRAME_STAND 6 |
| | Dynamic sprite frame index: standing idle pose.
|
| #define | FRAME_WALK0 2 |
| | Dynamic sprite frame index: walk animation frame 0.
|
| #define | FRAME_WALK1 3 |
| | Dynamic sprite frame index: walk animation frame 1.
|
| #define | GRAVITY 48 |
| | Gravity acceleration in 8.8 fixed-point units per frame.
|
| #define | MARIO_ACCEL 0x0038 |
| | Horizontal acceleration per frame in 8.8 fixed-point (0.22 pixels/frame).
|
| #define | MARIO_ACT_FALL 3 |
| | Mario is falling (downward velocity or walked off an edge).
|
| #define | MARIO_ACT_JUMP 2 |
| | Mario is jumping (upward velocity, ascending phase).
|
| #define | MARIO_ACT_STAND 0 |
| | Mario is standing on solid ground, idle.
|
| #define | MARIO_ACT_WALK 1 |
| | Mario is walking (horizontal velocity nonzero, on ground).
|
| #define | MARIO_HIJUMPING 0x0594 |
| | High jump initial upward velocity (when holding UP during jump).
|
| #define | MARIO_JUMPING 0x0394 |
| | Normal jump initial upward velocity in 8.8 fixed-point.
|
| #define | MARIO_MAXACCEL 0x0140 |
| | Maximum horizontal velocity in 8.8 fixed-point (1.25 pixels/frame).
|
| #define | T_EMPTY 0x0000 |
| | Tile property: passable (air, background decoration).
|
| #define | T_SOLID 0xFF00 |
| | Tile property: solid (ground, walls, platforms).
|
| #define | VRAM_BG_MAP 0x6800 |
| | VRAM word address for BG1 tilemap (SC_64x32).
|
| #define | VRAM_BG_TILES 0x2000 |
| | VRAM word address for BG1 tile character data.
|
| #define | VRAM_SPR_LARGE 0x0000 |
| | Asset symbols from data.asm (.incbin sections in SUPERFREE banks). Post-A6+A7, C pointers carry the bank byte and dmaCopyVram/dmaCopyCGram read it directly — no ASM loader stub needed.
|
| #define | VRAM_SPR_SMALL 0x1000 |
| | VRAM word address for small (8x8) sprite tiles.
|
|
| static s16 | asr8 (s16 val) |
| | Arithmetic right shift by 8 bits (extract integer part of 8.8 fixed-point).
|
| int | main (void) |
| | Main entry point – platformer initialization and game loop.
|
| static void | map_flush_column (void) |
| | Flush the buffered column to VRAM via DMA (flush phase).
|
| static u16 | map_get_tile_prop (s16 px, s16 py) |
| | Look up the collision property of the tile at pixel coordinates.
|
| static void | map_load (void) |
| | Load the map from ROM and populate the initial VRAM tilemap.
|
| static void | map_prepare_column (u16 map_col, u16 vram_col) |
| | Buffer a map column for deferred VRAM write (prepare phase).
|
| static void | map_update (void) |
| | Check if the camera has scrolled to a new tile column and prepare it.
|
| static void | mario_animate (void) |
| | Update Mario's sprite animation frame based on current action.
|
| static void | mario_apply_physics (void) |
| | Apply gravity and integrate velocity into position.
|
| static void | mario_clamp_and_transition (void) |
| | Clamp Mario to world boundaries and handle action state transitions.
|
| static void | mario_collide_horizontal (void) |
| | Check horizontal tile collisions (wall sliding).
|
| static void | mario_collide_vertical (void) |
| | Check vertical tile collisions (ground landing and ceiling bonk).
|
| static void | mario_handle_input (void) |
| | Process D-pad and button input for Mario's movement.
|
| static void | mario_init (void) |
| | Initialize Mario's position, velocity, animation, and sprite configuration.
|
| static void | mario_update_camera (void) |
| | Update camera to follow Mario and position the sprite on screen.
|
| static void | write_vram_column (u16 map_col, u16 vram_col) |
| | Write a full tile column to VRAM via direct register writes.
|
|
| static s16 | cam_max_x |
| static s16 | camera_x |
| static AnimClip | clip_jump = { clip_jump_frames, 0, 1, 1, 0 , 0 } |
| static u16 | clip_jump_frames [] = { 1 } |
| static AnimClip | clip_stand = { clip_stand_frames, 0, 1, 1, 0 , 0 } |
| static u16 | clip_stand_frames [] = { 6 } |
| static AnimClip | clip_walk = { clip_walk_frames, 0, 2, 4, 0 , 0 } |
| static u16 | clip_walk_frames [] = { 2 , 3 } |
| static u16 | col_buffer [32] |
| | Column tile buffer for deferred VRAM streaming.
|
| static u8 | col_pending |
| static u16 | col_vram_base |
| static s16 | last_tile_x |
| static const u16 * | map_data |
| static u16 | map_height |
| static s16 | map_max_x |
| static const u16 * | map_row_ptrs [32] |
| | Precomputed row pointers into map_data for fast tile lookup.
|
| static u16 | map_width |
| const u8 | mapmario [] |
| | Mario sprite tile data (16x16, 4bpp) for the dynamic sprite engine.
|
| static u8 | mario_action |
| static AnimPlayer | mario_anim = {0, 0, 0, 0, 0} |
| static s16 | mario_x |
| static u8 | mario_xfrac |
| static s16 | mario_xvel |
| static s16 | mario_y |
| static u8 | mario_yfrac |
| static s16 | mario_yvel |
| static u8 | sfx_jump_slot |
| | SPC700 sound effect slot index for the jump sound.
|
| static const u16 * | tile_props |
| const u8 | tilesetatt [] |
| | Tile attribute table (T_SOLID/T_EMPTY per tile index, b16 format).
|
Side-scrolling platformer with scroll streaming and SNESMOD audio.
A Mario-style platformer demonstrating the core techniques of a scrolling SNES game: tile-based map streaming, subpixel physics, tile collision, camera tracking, animated sprites, and SPC700 music/sound effects via the SNESMOD audio driver.
The map engine streams one tile column per frame using a prepare/flush pattern: during active display, the next column is read from ROM into a RAM buffer (map_prepare_column); during VBlank, a 64-byte DMA writes it to VRAM (map_flush_column). This keeps VRAM writes within the VBlank budget. The column at +32 tiles ahead is streamed to cover the 33rd partial tile at the screen edge.
Physics use 8.8 fixed-point velocities with arithmetic right shift for signed values. Collision checks test two probe points per axis against the tile property table (T_SOLID / T_EMPTY).
Port of the PVSnesLib likemario example.
- SNES Concepts
- Scroll streaming: 1 column/frame prepare-during-display, DMA-in-VBlank
- SC_64x32 tilemap with 64-column wraparound for seamless scrolling
- Subpixel 8.8 fixed-point physics (acceleration, gravity, max velocity)
- Tile collision via property lookup table (tilesetatt)
- Dynamic sprite engine with frame-based animation
- SNESMOD: SPC700 module playback and sound effects
- Assembly DMA loader with bank bytes for SUPERFREE ROM data
- What to Observe
- Mario walks left/right with acceleration and deceleration
- Press A to jump (hold UP for a higher jump)
- Camera follows Mario; background scrolls smoothly
- Music plays continuously; a jump sound effect triggers on A press
- Falling off the bottom respawns Mario at the top
- Modules Used
- console, sprite, sprite_dynamic, sprite_lut, dma, input, background
- See also
- background.h, sprite.h, input.h, dma.h, snesmod.h
Main entry point – platformer initialization and game loop.
Initializes all subsystems during force blank: Mode 1 video, tile/palette DMA via assembly loader, dynamic sprite engine, map loading, Mario state, and SNESMOD audio (music + jump sound effect). Then enters the main loop with a strict pipeline:
Active display (CPU-only, no VRAM):
- mario_handle_input() - read pad, apply acceleration
- mario_apply_physics() - gravity, velocity integration
- mario_collide_vertical/horizontal() - tile collision response
- mario_clamp_and_transition() - boundary checks, state transitions
- mario_animate() - frame selection
- mario_update_camera() - camera + sprite positioning
- map_update() - prepare next column in RAM buffer
VBlank (VRAM writes allowed):
- map_flush_column() - DMA one column (64 bytes)
- bgSetScroll() - update hardware scroll registers
- oamVramQueueUpdate() - upload sprite tiles
- snesmodProcess() - SPC700 communication (no VBlank restriction)
- Returns
- 0 (never reached – infinite game loop)
Initialize Mario's position, velocity, animation, and sprite configuration.
Places Mario at a safe spawn point, sets the dynamic sprite engine's frame to the standing pose, and configures the sprite attribute byte (priority 3, H-flip for facing right). The tile data may sit in any ROM bank (SUPERFREE placement); OAM_SET_GFX() records the bank byte of the pointer it is given. Until 2026-09-20 it recorded bank 0, and this example carried a hand-written getSpriteTilBank() helper to work around it.