Squares

Source: src/demo_stage_squares.c · 160 lines · 7 functions.

Squares screen captures

Four moments from the running Squares demo. The PET characters build expanding nested outlines one cell at a time, with a new glyph and reverse-video choice for each square.

Squares demo displaying nested PET character outlines
Squares building outward from the fixed lower-right anchor.
Squares demo at a later stage of its nested pattern
A later frame with more completed character-cell outlines.
Squares demo showing another random PET glyph pattern
The same square path rendered with another randomized PET glyph.
Squares demo showing its expanding nested outlines
Another cycle showing the expanding nested-square effect.

Module entry points

These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.

How to read these diagrams

Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.

These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.

Functions in this file

setStageTenCell() · source line 50
static void setStageTenCell(int column, int row, uint8_t glyph)
  1. Decisioncolumn < 0 || column >= DEMO_STAGE_TEN_COLUMNS || row < 0 || row >= DEMO_STAGE_TEN_ROWS
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Executestage_ten_cells[(size_t)row * DEMO_STAGE_TEN_COLUMNS + (size_t)column] = glyph;
Read original function source
static void setStageTenCell(int column, int row, uint8_t glyph)
{
    if (column < 0 || column >= DEMO_STAGE_TEN_COLUMNS ||
        row < 0 || row >= DEMO_STAGE_TEN_ROWS) return;
    stage_ten_cells[(size_t)row * DEMO_STAGE_TEN_COLUMNS + (size_t)column] = glyph;
}
beginStageTenSquare() · source line 57
static void beginStageTenSquare(uint32_t now_ms)
  1. Executestage_ten_glyph = 65U + (uint8_t)(esp_random() % 63U);
  2. Decision(esp_random() & 1U) != 0U
    True
    1. Executestage_ten_glyph |= 0x80U;
    False

    Continue after this decision.

  3. Executestage_ten_edge_step = 0U;
  4. Executestage_ten_phase = SQUARES_DRAW_BOTTOM;
  5. Executestage_ten_next_update_at = now_ms;
Read original function source
static void beginStageTenSquare(uint32_t now_ms)
{
    // BASIC line 140 chooses a new display character and line 160 sometimes
    // enables reverse video. The high bit carries that reverse-video choice in
    // the existing PET renderer, as it does for KALEIDO.
    stage_ten_glyph = 65U + (uint8_t)(esp_random() % 63U);
    if ((esp_random() & 1U) != 0U) stage_ten_glyph |= 0x80U;
    stage_ten_edge_step = 0U;
    stage_ten_phase = SQUARES_DRAW_BOTTOM;
    stage_ten_next_update_at = now_ms;
}
demoStageSquaresStart() · source line 69
void demoStageSquaresStart(uint32_t now_ms)

Calls into this documentation:

  1. Executememset(stage_ten_cells, 0, sizeof(stage_ten_cells));
  2. Executestage_ten_draw_step_ms = DEMO_STAGE_TEN_DRAW_STEP_MIN_MS + (esp_random() % DEMO_STAGE_TEN_DRAW_STEP_RANGE_MS);
  3. Executestage_ten_size = 1U;
  4. ExecutebeginStageTenSquare(now_ms);
Read original function source
void demoStageSquaresStart(uint32_t now_ms)
{
    memset(stage_ten_cells, 0, sizeof(stage_ten_cells));
    stage_ten_draw_step_ms = DEMO_STAGE_TEN_DRAW_STEP_MIN_MS +
        (esp_random() % DEMO_STAGE_TEN_DRAW_STEP_RANGE_MS);
    stage_ten_size = 1U;
    beginStageTenSquare(now_ms);
}
drawNextStageTenSquareCell() · source line 78
static void drawNextStageTenSquareCell(void)

Calls into this documentation:

  1. Executeconst int right = DEMO_STAGE_TEN_ANCHOR_COLUMN;
  2. Executeconst int bottom = DEMO_STAGE_TEN_ANCHOR_ROW;
  3. Executeconst int left = right - (int)stage_ten_size + 1;
  4. Executeconst int top = bottom - (int)stage_ten_size + 1;
  5. Executeconst int step = (int)stage_ten_edge_step;
  6. Select a casestage_ten_phase

    Enter matching case; follow source fall-through until a break, return or end.

      1. Case / labelcase SQUARES_DRAW_BOTTOM:
      2. ExecutesetStageTenCell(left + step, bottom, stage_ten_glyph);
      3. Exit enclosing loop or switchbreak;
      4. Case / labelcase SQUARES_DRAW_RIGHT:
      5. ExecutesetStageTenCell(right, bottom - step, stage_ten_glyph);
      6. Exit enclosing loop or switchbreak;
      7. Case / labelcase SQUARES_DRAW_TOP:
      8. ExecutesetStageTenCell(right - step, top, stage_ten_glyph);
      9. Exit enclosing loop or switchbreak;
      10. Case / labelcase SQUARES_DRAW_LEFT:
      11. ExecutesetStageTenCell(left, top + step, stage_ten_glyph);
      12. Exit enclosing loop or switchbreak;
      13. Case / labelcase SQUARES_HOLD:
      14. Exit enclosing loop or switchbreak;
Read original function source
static void drawNextStageTenSquareCell(void)
{
    // This is the PET cursor choreography behind BASIC lines 170-220. The
    // original writes a character along the bottom, then travels up, left, and
    // down again to return to its anchor. Direct cell coordinates retain the
    // same expanding-square result without relying on terminal cursor wrapping.
    const int right = DEMO_STAGE_TEN_ANCHOR_COLUMN;
    const int bottom = DEMO_STAGE_TEN_ANCHOR_ROW;
    const int left = right - (int)stage_ten_size + 1;
    const int top = bottom - (int)stage_ten_size + 1;
    const int step = (int)stage_ten_edge_step;

    switch (stage_ten_phase) {
        case SQUARES_DRAW_BOTTOM:
            setStageTenCell(left + step, bottom, stage_ten_glyph);
            break;
        case SQUARES_DRAW_RIGHT:
            setStageTenCell(right, bottom - step, stage_ten_glyph);
            break;
        case SQUARES_DRAW_TOP:
            setStageTenCell(right - step, top, stage_ten_glyph);
            break;
        case SQUARES_DRAW_LEFT:
            setStageTenCell(left, top + step, stage_ten_glyph);
            break;
        case SQUARES_HOLD:
            break;
    }
}
advanceStageTenSquarePath() · source line 108
static void advanceStageTenSquarePath(uint32_t now_ms)

Calls into this documentation:

  1. Execute++stage_ten_edge_step;
  2. Decisionstage_ten_edge_step < stage_ten_size
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  3. Executestage_ten_edge_step = 0U;
  4. Select a casestage_ten_phase

    Enter matching case; follow source fall-through until a break, return or end.

      1. Case / labelcase SQUARES_DRAW_BOTTOM:
      2. Executestage_ten_phase = SQUARES_DRAW_RIGHT;
      3. Exit enclosing loop or switchbreak;
      4. Case / labelcase SQUARES_DRAW_RIGHT:
      5. Executestage_ten_phase = SQUARES_DRAW_TOP;
      6. Exit enclosing loop or switchbreak;
      7. Case / labelcase SQUARES_DRAW_TOP:
      8. Executestage_ten_phase = SQUARES_DRAW_LEFT;
      9. Exit enclosing loop or switchbreak;
      10. Case / labelcase SQUARES_DRAW_LEFT:
      11. Execute++stage_ten_size;
      12. Decisionstage_ten_size > DEMO_STAGE_TEN_MAX_SIZE
        True
          1. Executestage_ten_phase = SQUARES_HOLD;
          2. Executestage_ten_hold_until = now_ms + DEMO_STAGE_TEN_HOLD_MS;
          3. Executestage_ten_next_update_at = stage_ten_hold_until;
        False
          1. ExecutebeginStageTenSquare(now_ms);
      13. Exit enclosing loop or switchbreak;
      14. Case / labelcase SQUARES_HOLD:
      15. Exit enclosing loop or switchbreak;
Read original function source
static void advanceStageTenSquarePath(uint32_t now_ms)
{
    ++stage_ten_edge_step;
    if (stage_ten_edge_step < stage_ten_size) return;

    stage_ten_edge_step = 0U;
    switch (stage_ten_phase) {
        case SQUARES_DRAW_BOTTOM: stage_ten_phase = SQUARES_DRAW_RIGHT; break;
        case SQUARES_DRAW_RIGHT: stage_ten_phase = SQUARES_DRAW_TOP; break;
        case SQUARES_DRAW_TOP: stage_ten_phase = SQUARES_DRAW_LEFT; break;
        case SQUARES_DRAW_LEFT:
            ++stage_ten_size;
            if (stage_ten_size > DEMO_STAGE_TEN_MAX_SIZE) {
                stage_ten_phase = SQUARES_HOLD;
                stage_ten_hold_until = now_ms + DEMO_STAGE_TEN_HOLD_MS;
                stage_ten_next_update_at = stage_ten_hold_until;
            } else {
                beginStageTenSquare(now_ms);
            }
            break;
        case SQUARES_HOLD: break;
    }
}
demoStageSquaresUpdate() · source line 132
void demoStageSquaresUpdate(uint32_t now_ms)

Calls into this documentation:

  1. Decision(int32_t)(now_ms - stage_ten_next_update_at) < 0
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisionstage_ten_phase == SQUARES_HOLD
    True
      1. ExecutedemoStageSquaresStart(now_ms);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  3. ExecutedrawNextStageTenSquareCell();
  4. Executestage_ten_next_update_at += stage_ten_draw_step_ms;
  5. ExecuteadvanceStageTenSquarePath(now_ms);
Read original function source
void demoStageSquaresUpdate(uint32_t now_ms)
{
    if ((int32_t)(now_ms - stage_ten_next_update_at) < 0) return;
    if (stage_ten_phase == SQUARES_HOLD) {
        demoStageSquaresStart(now_ms);
        return;
    }

    drawNextStageTenSquareCell();
    stage_ten_next_update_at += stage_ten_draw_step_ms;
    advanceStageTenSquarePath(now_ms);
}
demoStageSquaresDraw() · source line 145
void demoStageSquaresDraw(uint16_t *frame)

Calls into this documentation:

  1. FOR loopint row = 0; row < DEMO_STAGE_TEN_ROWS; ++row

    Repeat body while the loop condition permits; then continue below.

      1. FOR loopint column = 0; column < DEMO_STAGE_TEN_COLUMNS; ++column

        Repeat body while the loop condition permits; then continue below.

          1. Executeconst uint8_t glyph = stage_ten_cells[(size_t)row * DEMO_STAGE_TEN_COLUMNS + (size_t)column];
          2. Decisionglyph == 0U
            True
            1. Next loop iterationcontinue;
            False

            Continue after this decision.

          3. ExecutedrawPet2001Char(frame, column * (int)PET2001_FONT_GLYPH_WIDTH, row * (int)PET2001_FONT_GLYPH_HEIGHT, glyph, DEMO_STAGE_TEN_FOREGROUND, COLOR_BLACK, 1);
Read original function source
void demoStageSquaresDraw(uint16_t *frame)
{
    // SQUARES is another PET 40 x 19 cell scene. The normal scale-two ticker
    // is drawn later and therefore remains entirely below this character map.
    for (int row = 0; row < DEMO_STAGE_TEN_ROWS; ++row) {
        for (int column = 0; column < DEMO_STAGE_TEN_COLUMNS; ++column) {
            const uint8_t glyph = stage_ten_cells[(size_t)row *
                                                   DEMO_STAGE_TEN_COLUMNS +
                                                   (size_t)column];
            if (glyph == 0U) continue;
            drawPet2001Char(frame, column * (int)PET2001_FONT_GLYPH_WIDTH,
                            row * (int)PET2001_FONT_GLYPH_HEIGHT, glyph,
                            DEMO_STAGE_TEN_FOREGROUND, COLOR_BLACK, 1);
        }
    }
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "demo_stage_squares.h"

#include "PET2001_font.h"
#include "c64_colors.h"
#include "esp_attr.h"
#include "esp_random.h"
#include "game_graphics.h"

#include <stddef.h>
#include <string.h>

// Segment 10 -- SQUARES, porting the PET cursor routine as expanding, nested outlines.
// The original 40 x 25 display could grow squares to 21 cells. The ticker leaves
// this port 19 rows high, so the fixed lower-right anchor and limit below keep
// every complete outline visible above it. Raise/lower both anchor coordinates
// with MAX_SIZE if you want to move or resize the whole nested-square field.
#define DEMO_STAGE_TEN_COLUMNS 40
#define DEMO_STAGE_TEN_ROWS 19
#define DEMO_STAGE_TEN_ANCHOR_COLUMN 28
#define DEMO_STAGE_TEN_ANCHOR_ROW 17
#define DEMO_STAGE_TEN_MAX_SIZE 17U
// BASIC line 130 picks one random delay for a whole set of nested squares.
// These bounds retain that variable speed; set RANGE to 1U for a fixed speed.
// Lower values draw faster.
#define DEMO_STAGE_TEN_DRAW_STEP_MIN_MS 6U
#define DEMO_STAGE_TEN_DRAW_STEP_RANGE_MS 20U
#define DEMO_STAGE_TEN_HOLD_MS 2600U
#define DEMO_STAGE_TEN_FOREGROUND COLOR_WHITE

typedef enum {
    SQUARES_DRAW_BOTTOM,
    SQUARES_DRAW_RIGHT,
    SQUARES_DRAW_TOP,
    SQUARES_DRAW_LEFT,
    SQUARES_HOLD,
} squares_phase_t;

// SQUARES stores its completed character cells in PSRAM. The small cursor-like
// state below advances one printed PET character at a time around each edge.
EXT_RAM_BSS_ATTR static uint8_t
    stage_ten_cells[DEMO_STAGE_TEN_COLUMNS * DEMO_STAGE_TEN_ROWS];
static squares_phase_t stage_ten_phase;
static uint8_t stage_ten_size;
static uint8_t stage_ten_edge_step;
static uint8_t stage_ten_glyph;
static uint32_t stage_ten_next_update_at;
static uint32_t stage_ten_hold_until;
static uint32_t stage_ten_draw_step_ms;

static void setStageTenCell(int column, int row, uint8_t glyph)
{
    if (column < 0 || column >= DEMO_STAGE_TEN_COLUMNS ||
        row < 0 || row >= DEMO_STAGE_TEN_ROWS) return;
    stage_ten_cells[(size_t)row * DEMO_STAGE_TEN_COLUMNS + (size_t)column] = glyph;
}

static void beginStageTenSquare(uint32_t now_ms)
{
    // BASIC line 140 chooses a new display character and line 160 sometimes
    // enables reverse video. The high bit carries that reverse-video choice in
    // the existing PET renderer, as it does for KALEIDO.
    stage_ten_glyph = 65U + (uint8_t)(esp_random() % 63U);
    if ((esp_random() & 1U) != 0U) stage_ten_glyph |= 0x80U;
    stage_ten_edge_step = 0U;
    stage_ten_phase = SQUARES_DRAW_BOTTOM;
    stage_ten_next_update_at = now_ms;
}

void demoStageSquaresStart(uint32_t now_ms)
{
    memset(stage_ten_cells, 0, sizeof(stage_ten_cells));
    stage_ten_draw_step_ms = DEMO_STAGE_TEN_DRAW_STEP_MIN_MS +
        (esp_random() % DEMO_STAGE_TEN_DRAW_STEP_RANGE_MS);
    stage_ten_size = 1U;
    beginStageTenSquare(now_ms);
}

static void drawNextStageTenSquareCell(void)
{
    // This is the PET cursor choreography behind BASIC lines 170-220. The
    // original writes a character along the bottom, then travels up, left, and
    // down again to return to its anchor. Direct cell coordinates retain the
    // same expanding-square result without relying on terminal cursor wrapping.
    const int right = DEMO_STAGE_TEN_ANCHOR_COLUMN;
    const int bottom = DEMO_STAGE_TEN_ANCHOR_ROW;
    const int left = right - (int)stage_ten_size + 1;
    const int top = bottom - (int)stage_ten_size + 1;
    const int step = (int)stage_ten_edge_step;

    switch (stage_ten_phase) {
        case SQUARES_DRAW_BOTTOM:
            setStageTenCell(left + step, bottom, stage_ten_glyph);
            break;
        case SQUARES_DRAW_RIGHT:
            setStageTenCell(right, bottom - step, stage_ten_glyph);
            break;
        case SQUARES_DRAW_TOP:
            setStageTenCell(right - step, top, stage_ten_glyph);
            break;
        case SQUARES_DRAW_LEFT:
            setStageTenCell(left, top + step, stage_ten_glyph);
            break;
        case SQUARES_HOLD:
            break;
    }
}

static void advanceStageTenSquarePath(uint32_t now_ms)
{
    ++stage_ten_edge_step;
    if (stage_ten_edge_step < stage_ten_size) return;

    stage_ten_edge_step = 0U;
    switch (stage_ten_phase) {
        case SQUARES_DRAW_BOTTOM: stage_ten_phase = SQUARES_DRAW_RIGHT; break;
        case SQUARES_DRAW_RIGHT: stage_ten_phase = SQUARES_DRAW_TOP; break;
        case SQUARES_DRAW_TOP: stage_ten_phase = SQUARES_DRAW_LEFT; break;
        case SQUARES_DRAW_LEFT:
            ++stage_ten_size;
            if (stage_ten_size > DEMO_STAGE_TEN_MAX_SIZE) {
                stage_ten_phase = SQUARES_HOLD;
                stage_ten_hold_until = now_ms + DEMO_STAGE_TEN_HOLD_MS;
                stage_ten_next_update_at = stage_ten_hold_until;
            } else {
                beginStageTenSquare(now_ms);
            }
            break;
        case SQUARES_HOLD: break;
    }
}

void demoStageSquaresUpdate(uint32_t now_ms)
{
    if ((int32_t)(now_ms - stage_ten_next_update_at) < 0) return;
    if (stage_ten_phase == SQUARES_HOLD) {
        demoStageSquaresStart(now_ms);
        return;
    }

    drawNextStageTenSquareCell();
    stage_ten_next_update_at += stage_ten_draw_step_ms;
    advanceStageTenSquarePath(now_ms);
}

void demoStageSquaresDraw(uint16_t *frame)
{
    // SQUARES is another PET 40 x 19 cell scene. The normal scale-two ticker
    // is drawn later and therefore remains entirely below this character map.
    for (int row = 0; row < DEMO_STAGE_TEN_ROWS; ++row) {
        for (int column = 0; column < DEMO_STAGE_TEN_COLUMNS; ++column) {
            const uint8_t glyph = stage_ten_cells[(size_t)row *
                                                   DEMO_STAGE_TEN_COLUMNS +
                                                   (size_t)column];
            if (glyph == 0U) continue;
            drawPet2001Char(frame, column * (int)PET2001_FONT_GLYPH_WIDTH,
                            row * (int)PET2001_FONT_GLYPH_HEIGHT, glyph,
                            DEMO_STAGE_TEN_FOREGROUND, COLOR_BLACK, 1);
        }
    }
}