Matrix

Source: src/matrix.c · 158 lines · 8 functions.

Screen captures

Views of this part running on the Mini Arcade.

Matrix effect on the Mini Arcade
Matrix showing its falling character effect.

Screen / module overview

  1. Enter Matrix

    Seed the random generator. Initialise up to 40 falling columns and select speed X4.

  2. Handle controls

    E cycles speed. A counts secret presses: 15 presses, with no gap longer than 750 ms, calls addGold(666).

  3. Wait for next update

    updateMatrix() returns until 40 ms has passed.

  4. Advance every column

    Add column speed multiplied by the selected speed. Reset columns after their entire trails leave the display.

  5. Publish a frame

    Set frame_ready. matrixTakeFrame() returns the flag and clears it.

  6. Draw and repeat

    Clear black; draw white heads and fading green glyphs or binary digits. Draw speed text. Repeat until stopped.

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

next_random() · source line 49
static uint32_t next_random(void)
  1. Executerandom_state ^= random_state << 13;
  2. Executerandom_state ^= random_state >> 17;
  3. Executerandom_state ^= random_state << 5;
  4. Exit this functionreturn random_state;
Read original function source
static uint32_t next_random(void)
{
    random_state ^= random_state << 13;
    random_state ^= random_state >> 17;
    random_state ^= random_state << 5;
    return random_state;
}
reset_column() · source line 57
static void reset_column(matrix_column_t *column)

Calls into this documentation:

  1. Executecolumn->y = -(float)(next_random() % LCD_HEIGHT);
  2. Executecolumn->speed = (float)(8U + next_random() % 17U) / 10.0f;
  3. Executecolumn->length = 8U + next_random() % 15U;
Read original function source
static void reset_column(matrix_column_t *column)
{
    column->y = -(float)(next_random() % LCD_HEIGHT);
    column->speed = (float)(8U + next_random() % 17U) / 10.0f;
    column->length = 8U + next_random() % 15U;
}
startMatrix() · source line 64
void startMatrix(uint32_t now_ms)

Calls into this documentation:

  1. Executerandom_state = esp_random();
  2. Decisionrandom_state == 0U
    True
    1. Executerandom_state = 0x9E3779B9U;
    False

    Continue after this decision.

  3. Executecolumn_count = LCD_WIDTH / CHAR_WIDTH;
  4. Decisioncolumn_count > MAX_COLUMNS
    True
    1. Executecolumn_count = MAX_COLUMNS;
    False

    Continue after this decision.

  5. FOR loopunsigned index = 0; index < column_count; ++index

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

      1. Executecolumns[index].x = (int)index * CHAR_WIDTH;
      2. Executereset_column(&columns[index]);
  6. Executelast_frame = now_ms;
  7. Executelast_secret_press = 0;
  8. Executesecret_press_count = 0;
  9. Executespeed_index = AI_SPEED_DEFAULT_INDEX;
  10. Executeframe_ready = true;
  11. Executerunning = true;
Read original function source
void startMatrix(uint32_t now_ms)
{
    random_state = esp_random();
    if (random_state == 0U) random_state = 0x9E3779B9U;
    column_count = LCD_WIDTH / CHAR_WIDTH;
    if (column_count > MAX_COLUMNS) column_count = MAX_COLUMNS;
    for (unsigned index = 0; index < column_count; ++index) {
        columns[index].x = (int)index * CHAR_WIDTH;
        reset_column(&columns[index]);
    }
    last_frame = now_ms;
    last_secret_press = 0;
    secret_press_count = 0;
    speed_index = AI_SPEED_DEFAULT_INDEX;
    frame_ready = true;
    running = true;
}
stopMatrix() · source line 82
void stopMatrix(void)
  1. Executerunning = false;
  2. Executeframe_ready = false;
  3. Executesecret_press_count = 0;
Read original function source
void stopMatrix(void)
{
    running = false;
    frame_ready = false;
    secret_press_count = 0;
}
matrixAction() · source line 89
void matrixAction(bool secret_press, bool speed, uint32_t now_ms)

Calls into this documentation:

  1. Decision!running
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisionspeed
    True
      1. ExecutecycleAISpeed(&speed_index);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decision!secret_press
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  4. Decisionsecret_press_count != 0U && now_ms - last_secret_press > SECRET_PRESS_WINDOW_MS
    True
      1. Executesecret_press_count = 0;
    False

    Continue after this decision.

  5. Executelast_secret_press = now_ms;
  6. Execute++secret_press_count;
  7. Decisionsecret_press_count < SECRET_PRESS_COUNT
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  8. ExecuteaddGold(666U);
  9. Executelast_secret_press = 0;
  10. Executesecret_press_count = 0;
Read original function source
void matrixAction(bool secret_press, bool speed, uint32_t now_ms)
{
    if (!running) return;
    if (speed) {
        cycleAISpeed(&speed_index);
        return;
    }
    if (!secret_press) return;
    if (secret_press_count != 0U &&
        now_ms - last_secret_press > SECRET_PRESS_WINDOW_MS) {
        secret_press_count = 0;
    }
    last_secret_press = now_ms;
    ++secret_press_count;
    if (secret_press_count < SECRET_PRESS_COUNT) return;
    addGold(666U);
    last_secret_press = 0;
    secret_press_count = 0;
}
updateMatrix() · source line 109
void updateMatrix(uint32_t now_ms)

Calls into this documentation:

  1. Decision!running || now_ms - last_frame < FRAME_INTERVAL_MS
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Executelast_frame = now_ms;
  3. FOR loopunsigned index = 0; index < column_count; ++index

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

      1. Executematrix_column_t *column = &columns[index];
      2. Executecolumn->y += column->speed * (float)(1U << speed_index);
      3. Decisioncolumn->y - (float)(column->length * CHAR_HEIGHT) > LCD_HEIGHT
        True
          1. Executereset_column(column);
        False

        Continue after this decision.

  4. Executeframe_ready = true;
Read original function source
void updateMatrix(uint32_t now_ms)
{
    if (!running || now_ms - last_frame < FRAME_INTERVAL_MS) return;
    last_frame = now_ms;
    for (unsigned index = 0; index < column_count; ++index) {
        matrix_column_t *column = &columns[index];
        column->y += column->speed * (float)(1U << speed_index);
        if (column->y - (float)(column->length * CHAR_HEIGHT) > LCD_HEIGHT) {
            reset_column(column);
        }
    }
    frame_ready = true;
}
matrixTakeFrame() · source line 123
bool matrixTakeFrame(void)
  1. Executeconst bool ready = frame_ready;
  2. Executeframe_ready = false;
  3. Exit this functionreturn ready;
Read original function source
bool matrixTakeFrame(void)
{
    const bool ready = frame_ready;
    frame_ready = false;
    return ready;
}
drawMatrix() · source line 130
void drawMatrix(uint16_t *frame)

Calls into this documentation:

  1. Executeclear(frame, COLOR_BLACK);
  2. FOR loopunsigned column_index = 0; column_index < column_count; ++column_index

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

      1. Executeconst matrix_column_t *column = &columns[column_index];
      2. FOR loopunsigned character_index = 0; character_index < column->length; ++character_index

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

          1. Executeconst int y = (int)column->y - (int)(character_index * CHAR_HEIGHT);
          2. Decisiony < -CHAR_HEIGHT || y >= LCD_HEIGHT
            True
            1. Next loop iterationcontinue;
            False

            Continue after this decision.

          3. Executeconst uint16_t color = character_index == 0U ? COLOR_WHITE : RGB565(0, (220 - (int)character_index * 12) > 20 ? 220 - (int)character_index * 12 : 20, 0);
          4. Decisionnext_random() % 5U == 0U
            True
              1. Executeconst unsigned glyph = next_random() % (sizeof(matrix_glyphs) / sizeof(matrix_glyphs[0]));
              2. Executedraw_bitmap_scaled(frame, column->x, y, matrix_glyphs[glyph], 8, 8, 8, 8, color);
            False
              1. Executeconst char character[2] = { next_random() & 1U ? '1' : '0', '\0' };
              2. Executedraw_text(frame, column->x, y, character, 1, color);
  3. Executedraw_text(frame, 4, 156, "E SPEED", 1, COLOR_LIGHTGREEN);
  4. Executedraw_text(frame, 58, 156, aiSpeedShortText(speed_index), 1, COLOR_WHITE);
Read original function source
void drawMatrix(uint16_t *frame)
{
    clear(frame, COLOR_BLACK);
    for (unsigned column_index = 0; column_index < column_count; ++column_index) {
        const matrix_column_t *column = &columns[column_index];
        for (unsigned character_index = 0; character_index < column->length;
             ++character_index) {
            const int y = (int)column->y - (int)(character_index * CHAR_HEIGHT);
            if (y < -CHAR_HEIGHT || y >= LCD_HEIGHT) continue;
            const uint16_t color = character_index == 0U ? COLOR_WHITE :
                RGB565(0, (220 - (int)character_index * 12) > 20 ?
                           220 - (int)character_index * 12 : 20, 0);
            if (next_random() % 5U == 0U) {
                const unsigned glyph = next_random() %
                    (sizeof(matrix_glyphs) / sizeof(matrix_glyphs[0]));
                draw_bitmap_scaled(frame, column->x, y, matrix_glyphs[glyph],
                               8, 8, 8, 8, color);
            } else {
                const char character[2] = {
                    next_random() & 1U ? '1' : '0', '\0'
                };
                draw_text(frame, column->x, y, character, 1, color);
            }
        }
    }
    draw_text(frame, 4, 156, "E SPEED", 1, COLOR_LIGHTGREEN);
    draw_text(frame, 58, 156, aiSpeedShortText(speed_index), 1, COLOR_WHITE);
}
Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "matrix.h"

#include "game_graphics.h"
#include "PET2001_font.h"
#include "gold.h"
#include "ai_speed.h"
#include "esp_random.h"

#define MAX_COLUMNS 40U
#define CHAR_WIDTH 8
#define CHAR_HEIGHT 8
#define FRAME_INTERVAL_MS 40U
#define SECRET_PRESS_WINDOW_MS 750U
#define SECRET_PRESS_COUNT 15U

typedef struct {
    int x;
    float y;
    float speed;
    uint8_t length;
} matrix_column_t;

static const uint8_t matrix_glyphs[][8] = {
    {0x7E,0x42,0x5A,0x5A,0x5A,0x42,0x7E,0x00},
    {0x18,0x7E,0x18,0x24,0x42,0x81,0x00,0x00},
    {0x7E,0x08,0x3E,0x2A,0x3E,0x08,0x08,0x00},
    {0x42,0x24,0x18,0x24,0x42,0x00,0x00,0x00},
    {0x7E,0x10,0x3C,0x10,0x7E,0x00,0x00,0x00},
    {0x7E,0x08,0x18,0x08,0x7E,0x00,0x00,0x00},
    {0x7E,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
    {0x3C,0x42,0x02,0x3E,0x42,0x3C,0x00,0x00},
    {0x7E,0x08,0x3E,0x08,0x7E,0x00,0x00,0x00},
    {0x42,0x24,0x18,0x18,0x18,0x00,0x00,0x00},
    {0x00,0x3E,0x29,0x4A,0x08,0x18,0x70,0x00},
    {0x80,0xF8,0xCC,0xC6,0xC6,0xCC,0xF8,0x80},
    {0x60,0xC0,0xA0,0x10,0x2C,0x0A,0x0A,0x0C},
    {0x3C,0x7E,0xFF,0x5A,0x18,0x18,0x3C,0x7E},
};

static matrix_column_t columns[MAX_COLUMNS];
static unsigned column_count;
static uint32_t random_state;
static uint32_t last_frame;
static uint32_t last_secret_press;
static uint8_t secret_press_count;
static uint8_t speed_index;
static bool running;
static bool frame_ready;

static uint32_t next_random(void)
{
    random_state ^= random_state << 13;
    random_state ^= random_state >> 17;
    random_state ^= random_state << 5;
    return random_state;
}

static void reset_column(matrix_column_t *column)
{
    column->y = -(float)(next_random() % LCD_HEIGHT);
    column->speed = (float)(8U + next_random() % 17U) / 10.0f;
    column->length = 8U + next_random() % 15U;
}

void startMatrix(uint32_t now_ms)
{
    random_state = esp_random();
    if (random_state == 0U) random_state = 0x9E3779B9U;
    column_count = LCD_WIDTH / CHAR_WIDTH;
    if (column_count > MAX_COLUMNS) column_count = MAX_COLUMNS;
    for (unsigned index = 0; index < column_count; ++index) {
        columns[index].x = (int)index * CHAR_WIDTH;
        reset_column(&columns[index]);
    }
    last_frame = now_ms;
    last_secret_press = 0;
    secret_press_count = 0;
    speed_index = AI_SPEED_DEFAULT_INDEX;
    frame_ready = true;
    running = true;
}

void stopMatrix(void)
{
    running = false;
    frame_ready = false;
    secret_press_count = 0;
}

void matrixAction(bool secret_press, bool speed, uint32_t now_ms)
{
    if (!running) return;
    if (speed) {
        cycleAISpeed(&speed_index);
        return;
    }
    if (!secret_press) return;
    if (secret_press_count != 0U &&
        now_ms - last_secret_press > SECRET_PRESS_WINDOW_MS) {
        secret_press_count = 0;
    }
    last_secret_press = now_ms;
    ++secret_press_count;
    if (secret_press_count < SECRET_PRESS_COUNT) return;
    addGold(666U);
    last_secret_press = 0;
    secret_press_count = 0;
}

void updateMatrix(uint32_t now_ms)
{
    if (!running || now_ms - last_frame < FRAME_INTERVAL_MS) return;
    last_frame = now_ms;
    for (unsigned index = 0; index < column_count; ++index) {
        matrix_column_t *column = &columns[index];
        column->y += column->speed * (float)(1U << speed_index);
        if (column->y - (float)(column->length * CHAR_HEIGHT) > LCD_HEIGHT) {
            reset_column(column);
        }
    }
    frame_ready = true;
}

bool matrixTakeFrame(void)
{
    const bool ready = frame_ready;
    frame_ready = false;
    return ready;
}

void drawMatrix(uint16_t *frame)
{
    clear(frame, COLOR_BLACK);
    for (unsigned column_index = 0; column_index < column_count; ++column_index) {
        const matrix_column_t *column = &columns[column_index];
        for (unsigned character_index = 0; character_index < column->length;
             ++character_index) {
            const int y = (int)column->y - (int)(character_index * CHAR_HEIGHT);
            if (y < -CHAR_HEIGHT || y >= LCD_HEIGHT) continue;
            const uint16_t color = character_index == 0U ? COLOR_WHITE :
                RGB565(0, (220 - (int)character_index * 12) > 20 ?
                           220 - (int)character_index * 12 : 20, 0);
            if (next_random() % 5U == 0U) {
                const unsigned glyph = next_random() %
                    (sizeof(matrix_glyphs) / sizeof(matrix_glyphs[0]));
                draw_bitmap_scaled(frame, column->x, y, matrix_glyphs[glyph],
                               8, 8, 8, 8, color);
            } else {
                const char character[2] = {
                    next_random() & 1U ? '1' : '0', '\0'
                };
                drawPet2001TextTransparent(frame, column->x, y, character, color, 1);
            }
        }
    }
    drawPet2001TextTransparent(frame, 4, 156, "E SPEED", COLOR_LIGHTGREEN, 1);
    drawPet2001TextTransparent(frame, 58, 156, aiSpeedShortText(speed_index), COLOR_WHITE, 1);
}