Document DM-PR-001, Revision A. November 1995.
For licensed Maestro developers. Distribute with cartridge dev kits only.
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.
A cart is a directory under public/carts/
containing two files:
manifest.json — metadata.main.lua — the program.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" }
]
id is what appears on the cart-select
screen. Keep it short; long titles wrap badly on the licensed Maestro
Vision monitor.
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.
gfx table
The Cadenza graphics unit. All draw calls write to the
software framebuffer; the framebuffer is presented at the end of
_draw().
| 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. |
| 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.
| 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. |
| 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. |
snd tableThe 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. |
inp tableInput 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. |
inp.btn for in-game purposes, but the
eject behaviour cannot be suppressed.
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. |
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
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.
|
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.