Module 0 checkpoint
Goal
One small ROM that proves you can build, run, read input, and draw a ROM-packed sprite. If this works, foundations are solid.
What you will see
- Text: count value
- A increments (edge-triggered)
- B resets
- Up to 12 star sprites drawn for the current count
Skills exercised
| Skill | Lesson |
|---|---|
| Toolchain + ROM | L01 |
| Update / render + vsync-paced present | L02 |
| Joypad poll + pressed edges | L03 |
| DFS sprite | L04 |
| Mental model of the machine | L05 |
| Debugging loop (any time) | L37 · reference |
Build & run
bash
source scripts/env.sh
make -C lessons/m0-checkpoint
# → lessons/m0-checkpoint/m0_checkpoint.z64Open in Ares (Homebrew mode). Map controls if needed.
Self-check
- [ ] ROM builds without errors
- [ ] Ares shows the UI
- [ ] A increments once per press (not while held forever spamming — we use pressed)
- [ ] B resets to 0
- [ ] Stars appear as the count rises
Optional exercises
- Change the star art in
lessons/m0-checkpoint/assets/star.png. - Cap display at 8 stars but allow the counter to go higher (HUD vs icons).
- Require Start to begin counting (simple two-state: title → play).
Full lesson source
The blocks below are imported from the real repository files at build time (VitePress <<< snippets). They are not hand-copied into this markdown.
lessons/m0-checkpoint/Makefile · lessons/m0-checkpoint/src/main.c
lessons/m0-checkpoint/Makefile
make
# Module 0 checkpoint — press A to count (+ sprite)
ROMNAME := m0_checkpoint
ROM_TITLE := "M0 Checkpoint"
MKSPRITE_FLAGS := -f RGBA16
include ../../common/lesson.mklessons/m0-checkpoint/src/main.c
c
/**
* Module 0 checkpoint
* ============================================================================
* Combines L01–L04: loop, joypad edges, DFS sprite.
* A = +1 count (pressed, not held), B = reset, stars drawn for count.
* If this ROM works, your foundations are solid.
* DOCS: docs/guide/m0/checkpoint.md
*/
#include <libdragon.h>
#include <stdio.h>
int main(void)
{
display_init(RESOLUTION_320x240, DEPTH_16_BPP, 2, GAMMA_NONE,
FILTERS_RESAMPLE);
dfs_init(DFS_DEFAULT_LOCATION);
rdpq_init();
rdpq_text_register_font(1, rdpq_font_load_builtin(FONT_BUILTIN_DEBUG_VAR));
joypad_init();
sprite_t *star = sprite_load("rom:/star.sprite");
int count = 0;
char line[48];
while (1) {
joypad_poll();
joypad_buttons_t pressed = joypad_get_buttons_pressed(JOYPAD_PORT_1);
if (pressed.a) {
count++;
}
if (pressed.b) {
count = 0;
}
snprintf(line, sizeof(line), "Count: %d", count);
surface_t *disp = display_get();
rdpq_attach(disp, NULL);
rdpq_clear((color_t){ .r = 10, .g = 28, .b = 24, .a = 255 });
rdpq_text_print(NULL, 1, 24, 30, "Module 0 checkpoint");
rdpq_text_print(NULL, 1, 24, 56, "A = +1 B = reset");
rdpq_text_print(NULL, 1, 24, 90, line);
if (star) {
rdpq_set_mode_standard();
rdpq_mode_alphacompare(1);
/* Draw one star per count (capped so we do not fill the screen). */
int n = count < 12 ? count : 12;
for (int i = 0; i < n; i++) {
float x = 24.0f + (float)(i % 6) * 40.0f;
float y = 140.0f + (float)(i / 6) * 40.0f;
rdpq_sprite_blit(star, x, y, NULL);
}
}
rdpq_text_print(NULL, 1, 24, 220, "If this works, foundations are solid.");
rdpq_detach_show();
}
}