Maestro(TM) Programmer's Reference

Document DM-PR-001, Revision A. November 1995.
For licensed Maestro developers. Distribute with cartridge dev kits only.


Contents

  1. Overview
  2. Cart layout
  3. Program structure
  4. The gfx table
  5. The snd table
  6. The inp table
  7. The sys table
  8. A minimal cart
  9. Reading the sample carts

1. Overview

Maestro cartridges run a single Lua program on the Toccata CPU. At boot, the BIOS exposes four global tables — gfx, snd, inp, and sys — which together form the entire programmer interface to the machine. There is no other way to talk to the hardware. There is no operating system underneath.

The frame rate is 60 Hz. The display is 384 by 224 pixels at 8 bits per pixel, indexed into a 256-entry palette drawn from the 18-bit master colour space. All coordinates are in screen pixels; the origin is the top-left.

This document is the four-page reference card that ships with every cartridge dev kit, expanded for the on-line edition.

2. Cart layout

A cart is a directory under public/carts/ containing two files:

The manifest fields are:

{
  "id":          "MY GAME",
  "title":       "MY GAME",
  "author":      "YOUR NAME",
  "year":        1995,
  "main":        "main.lua",
  "description": "One sentence. The BIOS shows this on the cart screen."
}

Once the cart directory exists, register it in public/carts/index.json so the BIOS lists it:

[
  { "id": "SWORDFALL",  "path": "swordfall"  },
  { "id": "MY GAME",    "path": "my-game"    }
]
Note. The id is what appears on the cart-select screen. Keep it short; long titles wrap badly on the licensed Maestro Vision monitor.

3. Program structure

The BIOS calls three global functions, in order, for every cart it loads:

_init() Called once, after the cart is loaded into RAM. Use it to define sprite sheets and tilemaps, seed the random number generator, and set initial state.
_update() Called once per frame, before _draw. Read input here. Step the simulation.
_draw() Called once per frame, after _update. Issue draw calls here. The framebuffer is presented when this function returns.

All three are optional, but a cart that does not define _draw will display whatever was on screen when the BIOS handed off, which is rarely what the author intended.

Errors. If any of the three functions raises a Lua error, the cart halts and the BIOS displays a CART ERROR screen. Press SELECT to eject. The error message is also written to the host console.

4. The gfx table

The Cadenza graphics unit. All draw calls write to the software framebuffer; the framebuffer is presented at the end of _draw().

Constants and primitives

gfx.W, gfx.H Screen dimensions. 384 and 224.
gfx.cls(c) Clear the framebuffer to palette index c.
gfx.pset(x, y, c) Plot one pixel.
gfx.line(x0, y0, x1, y1, c) Draw a line. Bresenham, clipped.
gfx.rect(x, y, w, h, c) Filled rectangle.
gfx.tri(x0, y0, x1, y1, x2, y2, c) Filled flat-shaded triangle. Counts against the polygon budget (~1500 triangles per frame on real hardware).
gfx.text(s, x, y, c) Draw a string in the BIOS font. The font is 8 by 8, uppercase, fixed-width. Lower case is folded to upper.

Palette

gfx.pal(i, r, g, b) Set palette entry i (0..255). Each component is 0..63 (the native 18-bit master palette). Takes effect on the next draw call.

The boot palette is pre-populated. Read the source of the sample carts (section 9) to see which indices give which colours; in the meantime, treat the first 16 entries as the EGA-ish primaries and experiment with the rest.

Sprites

gfx.spr_sheet(id, w, h, tw, th, hex) Define sprite sheet id. The sheet is w by h pixels, divided into tiles of tw by th. hex is a string of two-character hexadecimal palette indices, in row-major order, one entry per pixel.
gfx.spr(id, col, row, x, y, flipX, flipY, scale) Draw the tile at column col, row row of sheet id at screen position x,y. flipX, flipY default to false. scale is an integer multiplier and defaults to 1. Palette index 0 is treated as transparent.

Tilemaps

gfx.map_def(mapId, sheetId, cols, rows, tiles) Define a tilemap. tiles is a Lua table of length cols * rows; each entry is a 1-based tile index into the sheet, or 0 for empty.
gfx.map_draw(mapId, scrollX, scrollY, dx, dy) Draw a tilemap to the framebuffer with integer scrolling.
gfx.map_aff(mapId, a, b, c, d, tx, ty, horizonY) Draw a tilemap through a 2×2 affine matrix with translation. The pixels above horizonY are skipped. This is the Maestro's Mode-7-equivalent path; use it for ground planes and rotating overhead views. See SWORDFALL for a worked example.

5. The snd table

The Aria sound chip. Sixteen PCM channels, four FM operators, one noise channel. Channels are addressed by integer index; addressing the same channel twice cuts off the previous voice.

snd.fm(ch, freq, durSec, modRatio, modIndex, gain) Trigger an FM voice on channel ch at freq Hz for durSec seconds. modRatio and modIndex control the modulator. gain is 0..1. Defaults: ratio 2.0, index 3.0, gain 0.25.
snd.noise(ch, durSec, gain, cutoffHz) Trigger a band-limited noise burst. Defaults: gain 0.2, cutoff 1000.
snd.sample_def(id, hex, rate) Define PCM sample id. hex is signed 8-bit samples encoded as two-character hex; rate is the base sample rate in Hz.
snd.play(ch, sampleId, pitch, gain, loop) Play a defined sample. pitch is a multiplier on the base rate (1.0 = original). Defaults: pitch 1.0, gain 0.5, loop false.
snd.stop(ch) Silence one channel.
snd.mute_all() Silence every channel. Polite carts call this on pause.

6. The inp table

Input from the Scepter controller. Buttons are referenced by their lowercase name as a string:

"up", "down", "left", "right", "a", "b", "c", "d", "l", "r", "start", "select".

inp.btn(name) True while the button is held.
inp.btnp(name) True only on the frame the button was pressed. Use this for menu navigation.
inp.btnr(name) True only on the frame the button was released.
Reserved. SELECT is wired into the BIOS as the eject button and will return control to the cart-select screen at any time. Carts may read it with inp.btn for in-game purposes, but the eject behaviour cannot be suppressed.

7. The sys table

sys.time() Seconds since the cart was loaded. Floating point.
sys.cart() Returns a table with title, author, year, copied from the manifest.
sys.print(...) Print to the host console. For debugging only; not visible to players.
sys.screen_w, sys.screen_h Identical to gfx.W and gfx.H. Provided for convenience.

8. A minimal cart

The cart below moves a sixteen-pixel block left and right with the D-pad and prints a label. Save it as public/carts/hello/main.lua, write a manifest, and add an entry to index.json.

-- HELLO -- the smallest useful Maestro cart.

local x = gfx.W / 2

function _init()  end

function _update()
  if inp.btn("right") then x = x + 2 end
  if inp.btn("left")  then x = x - 2 end
  if inp.btnp("a")    then snd.fm(0, 440, 0.08) end
end

function _draw()
  gfx.cls(0)
  gfx.text("HELLO MAESTRO", 8, 8, 15)
  gfx.rect(x, 100, 16, 16, 40)
end

9. Reading the sample carts

Three carts are bundled with this kit. Read them in this order:

For questions not covered here, write to devsupport@damocles-interactive.com. Replies are typically within five business days.


UNDER CONSTRUCTION   THIS DOCUMENT IS REVISION A
Webmaster: webmaster@damocles-interactive.com
Last updated: November 17, 1995
You are visitor no. 00000218
Best viewed with Netscape Navigator 2.0 at 800 x 600 or higher.
This document supersedes DM-PR-000.

Copyright © 1995 Damocles Interactive Ltd. All rights reserved. Maestro(TM), S.W.O.R.D.(TM), Scepter(TM), Cadenza(TM), Aria(TM) and Toccata(TM) are trademarks of Damocles Interactive Ltd.