# `brawl:device/matrix`

## Overview

The **`brawl:device/matrix`** interface defines a portable, minimal **LED Matrix** API for WebAssembly (WASM) modules. It provides a consistent way to control LED Matrix arrays across diverse embedded targets - ranging from microcontrollers to emulators - while remaining fully compatible with the WASM MVP specification.

Each function follows a simple convention: it returns a signed 32-bit integer, where negative values represent standardized error codes and non-negative values represent valid results or success indicators. This design keeps the ABI deterministic, low-overhead, and portable between environments, enabling WASM applications to interact directly with LED Matrix devices without callbacks, threads, or host-specific extensions.

## Implementation Notes

This section outlines the expected behavior and conventions used by the `brawl:device/matrix` interface. It provides guidance for both host implementers and guest developers to ensure consistent behavior across WASM targets. The underlying implementation (LED Matrix driver) is abstracted from the caller and managed by the host driver.

#### LED Matrix Dimensions

`matrixDimensions()` returns the width and height of the LED Matrix array packed into an `i32`. The high word contains the `width` whilst the lower word contains the `height`. To extract the `width` and `height` can be done as shown below:

```
int32_t dims = matrixDimensions();
if (dims >= 0) {
    int32_t width  = (dims >> 16) & 0xFFFF;
    int32_t height =  dims        & 0xFFFF;
}
```

#### Operation

To initialize and begin using the LED Matrix, simply call the function `matrixInit()` - this will initialize the underlying LED Matrix controller and configure background services required for its operation on the host environment. This function should be called before utilizing any other functions, or an `MATRIX_ERROR_NOT_INITIALIZED` error will be returned.

#### Individual LED Control

To change the state of individual LED elements within the LED Matrix, use the functions `matrixOn()` and `matrixOff()` to toggle between on and off. To clear and to stop any animations on the LED Matrix, simply call the `matrixClear()` function.

#### Loading Pixels

The `matrixLoadPixels(mem_ptr, width, height)` function takes an array of `uint8_t` elements, where a non zero value represents a pixel being `ON` and a zero value being `OFF`. The `width` and `height` should match the dimensions of the LED Matrix, on success - the host renders the LED Matrix immediately and stops any current animation.

```
uint8_t pixels[] = {
    0, 0, 0, 1, 1, 0, 0, 0,
    0, 0, 1, 0, 0, 1, 0, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 1, 1, 1, 1, 1, 1, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 1, 0, 0, 0, 0, 1, 0,
    0, 0, 0, 0, 0, 0, 0, 0
};

matrixInit();
matrixClear();
matrixLoadPixels((int32_t)(uintptr_t)pixels, 8, 8);
```

The above example (pseudo code) will load the glyph `A` to an device that provides `8x8` LED Matrix.

#### Animation Frames

The `matrixLoadFrames(mem_ptr, count)` reads back-to-back frames from guest memory and transforms as required to the host memory requirements. Each frame consists of bitmap data (packed `uint32_t` words, row-major, padded to 32-bits), followed by a `uint32_t` value representing the duration in milliseconds between frames.

Lets consider the 'A' glyph shown earler, and how this would map into packed data  (pseudo code).

```
//  0, 0, 0, 1, 1, 0, 0, 0  == 0x18
//  0, 0, 1, 0, 0, 1, 0, 0  == 0x24
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42  // first uint32_t
//
//  0, 1, 1, 1, 1, 1, 1, 0  == 0x7E
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42
//  0, 1, 0, 0, 0, 0, 1, 0  == 0x42
//  0, 0, 0, 0, 0, 0, 0, 0  == 0x00  // second uint32_t

uint32_t frame_A[] = {
    0x18244242, 0x7E424200, 100      // 8x8 pixel data, 100 ms duration
};

matrixInit();
matrixLoadFrames((int32_t)(uintptr_t)frame_A, 1);
```

If more than one frame is defined, playback is initiated by calling `matrixPlay(loop)`, a non zero value will loop the playback of the frames. Individual frames can be loaded via `matrixRenderFrame()` or move between frames using `matrixNext()`. At any time, the status of the sequence can be checked via `matrixSequenceDone()` that will return a non zero value when idle or finished and zero if an animation sequence is still active.

#### Error Codes

All `brawl:device/matrix` functions return a signed 32-bit integer (`int32_t`), which encodes both result and error information:

| Value      |                                                                                       |
|------------|---------------------------------------------------------------------------------------|
| `ret < 0`  | an error occurred.                                                                    |
| `ret == 0` | operation completed successfully (or no value to return).                             |
| `ret > 0`  | operation succeeded and returned a value (e.g., packed dimensions or sequence state). |

All memory access operations are bounds-checked by the host; going outside will return a `MATRIX_ERROR_MEMORY_OOB` error. All coordinate or index operations are bounds-checked by the host; invalid coordinates return a `MATRIX_ERROR_INVALID_ARG` error.

## Constants

| Constant            |                                         |
|---------------------|-----------------------------------------|
| `MATRIX_MAX_FRAMES` | The maximum number of frames supported. |

#### Matrix Errors

All functions return a signed 32-bit integer (`int32_t`).  If the value is **negative**, it represents an error code from the table below:

| Constant                       |                                                                |
|--------------------------------|----------------------------------------------------------------|
| `MATRIX_ERROR_NOT_INITIALIZED` | The LED Matrix module is not initiaized.                       |
| `MATRIX_ERROR_INVALID_ARG`     | The argument provided was invalid for the operation requested. |
| `MATRIX_ERROR_MEMORY_OOB`      | The requested operation would result in a MEMORY_OOB Error.    |

## Application Programmer Interface (API)

```
//--------------------------------------------------------------------------
// brawl:device/matrix

#define MATRIX_MAX_FRAMES  16

// MATRIX errors
enum MATRIX_ERROR {
                           // >= 0           no error
  MATRIX_ERROR_NOT_INITIALIZED = -1,      // LED Matrix not initialized
  MATRIX_ERROR_INVALID_ARG     = -2,      // invalid argument
  MATRIX_ERROR_MEMORY_OOB      = -3       // memory out of bounds
};
```

#### LED Matrix Dimensions

```
int32_t // MATRIX_ERROR|int32_t
matrixDimensions(void);
```

#### Operation

```
int32_t // MATRIX_ERROR|void
matrixInit(void);

int32_t // MATRIX_ERROR|void
matrixOn(int32_t x, int32_t y);

int32_t // MATRIX_ERROR|void
matrixOff(int32_t x, int32_t y);

int32_t // MATRIX_ERROR|void
matrixClear(void);
```

#### Loading Pixels

```
int32_t // MATRIX_ERROR|void
matrixLoadPixels(int32_t mem_ptr, int32_t w, int32_t h);
```

#### Animation Frames

```
int32_t // MATRIX_ERROR|void
matrixLoadFrames(int32_t mem_ptr, int32_t count);

int32_t // MATRIX_ERROR|void
matrixRenderFrame(int32_t index);

int32_t // MATRIX_ERROR|void
matrixPlay(int32_t loop);

int32_t // MATRIX_ERROR|void
matrixNext(void);

int32_t // MATRIX_ERROR|int32_t
matrixSequenceDone(void);

//--------------------------------------------------------------------------
```

## Example - Light up Corners on LED Matrix (C)

```
#include <stdint.h>
#include <stdio.h>

#include "brawl-device-matrix.h"

static void matrix_err(int32_t rc, const char *what) {
    if (rc < 0) fprintf(stderr, "%s failed: %d\n", what, rc);
}

int main() {

    int32_t dims, width, height;
    int32_t rc;

    // get the matrix dimensions
    dims   = matrixDimensions();
    if (dims < 0) { matrix_err(dims, "matrixDimensions"); return 1; }
    width  = (dims >> 16) & 0xFFFF;
    height =  dims        & 0xFFFF;

    // initialize and clear the LED Matrix
    rc = matrixInit();
    if (rc < 0) { matrix_err(rc, "matrixInit"); return 1; }
    rc = matrixClear();
    if (rc < 0) { matrix_err(rc, "matrixClear"); return 1; }

    // highlight the corner LEDs (guard against 0-sized matrices)
    if ((width > 0) && (height > 0)) {
        rc = matrixOn(0,       0);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
        rc = matrixOn(width-1, 0);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
        rc = matrixOn(0,       height-1);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
        rc = matrixOn(width-1, height-1);
        if (rc < 0) { matrix_err(rc, "matrixOn"); return 1; }
    }

    return 0;
}
```

#### Revision

2025-10 (draft)
