tic80

MoonBit bindings for TIC-80.

wasm
tic80
fantasy console
moon add Milky2018/tic80@0.1.0
Download zip
Author
Version
0.1.0
License
Apache-2.0
Last updated
4 days ago
Downloads
2
README

#Milky2018/tic80-mbt

#Installation

Run the following command from the root of the cartridge module:

moon add Milky2018/tic80

This adds Milky2018/tic80 to the module's dependencies. Each cartridge package must also import it and use the linker configuration below.

#TIC-80 runtime requirement

Use a TIC-80 build based on the main branch of Milky2018/TIC-80. This branch updates the bundled wasm3 runtime and its TIC-80 integration to support the imported linear memory emitted by the current MoonBit toolchain.

TIC-80 builds without these changes may reject a MoonBit cartridge with only one memory per module is supported or unallocated linear memory, even when the cartridge package uses the required linker configuration below.

#Configuring a cartridge package

Every package that builds a TIC-80 WebAssembly cartridge must import this package, use the foreign_library package kind, and configure the WebAssembly linker to use TIC-80's imported linear memory. Add the following declarations to the cartridge package's moon.pkg file:

import { "Milky2018/tic80", } pkgtype(kind: "foreign_library") options( link: { "wasm": { "import-memory": { "module": "env", "name": "memory" }, "memory-limits": { "min": 4, "max": 4 }, "heap-start-address": 98304, }, }, )

Important: Do not omit pkgtype(kind: "foreign_library") from a cartridge package. It tells MoonBit to link the package as a foreign library whose exported callbacks can be loaded by TIC-80, rather than as a standalone program with a MoonBit main entry point.

This configuration has five responsibilities:

  • import { "Milky2018/tic80" } makes the TIC-80 API available through the @tic80 package qualifier.
  • pkgtype(kind: "foreign_library") builds a library-style WebAssembly module whose #export_name callbacks can be discovered by TIC-80. The cartridge is not a standalone WASI executable and does not define a MoonBit main entry point.
  • "import-memory": { "module": "env", "name": "memory" } makes the module import the env.memory linear memory supplied by TIC-80 instead of defining its own memory.
  • "memory-limits": { "min": 4, "max": 4 } declares that the imported memory is exactly four WebAssembly pages (256 KiB), matching TIC-80's limit. The first 96 KiB belongs to TIC-80 RAM, leaving at most 160 KiB for MoonBit static data, allocator metadata, and heap allocations.
  • "heap-start-address": 98304 reserves the first 96 KiB (0x18000) of that memory for TIC-80's RAM layout and starts MoonBit heap allocation after it. TIC-80 copies VRAM, tiles, sprites, map data, input state, audio state, and the rest of its runtime RAM into this region before calling the cartridge.

These settings belong to each cartridge package rather than to the reusable Milky2018/tic80 API package itself.

#Building and running a cartridge

Run moon build from the cartridge module's root directory. If the cartridge is the root package, no package path is needed:

moon build --target wasm --release

If the cartridge is a subpackage, pass the directory containing its moon.pkg file. For example, a package in game/ can be built with:

moon build --target wasm --release game

MoonBit places foreign-library output under _build/wasm/release/build/. In the game/ example, the cartridge is normally written to _build/wasm/release/build/game/game.wasm.

For a quick run that does not preserve project-specific sprites, maps, audio, or other resources, create a fresh WASM cartridge, import the compiled binary, and run it:

tic80 --skip --fs . --cmd 'new wasm & import binary _build/wasm/release/build/game/game.wasm & run'

--fs . makes the current project directory visible to TIC-80. new wasm creates a temporary WASM cartridge, import binary replaces its executable code with the MoonBit build output, and run starts the cartridge. Omit --cli and exit for interactive play so that the TIC-80 window remains open.

#Using a .wasmp project

A .wasmp file is TIC-80's text project format for a WASM cartridge. It stores cartridge metadata and editable resource sections such as tiles, sprites, maps, flags, palettes, SFX, and music. It does not embed the compiled WASM binary, so the current MoonBit output must be imported before each run.

Loading and saving text project files is a TIC-80 Pro feature. With a Pro build, start TIC-80 in the game project's directory:

tic80 --fs .

Then create and save a WASM project from the TIC-80 console:

new wasm save game.wasmp

Open the TIC-80 editors with Escape or F1, edit the cartridge resources, and use Ctrl+S to write the changes back to game.wasmp. Keep this file in version control alongside the MoonBit source; keep generated _build/ output ignored.

The normal development loop loads those resources, imports the newly compiled WASM, and runs the combined cartridge:

moon build --target wasm --release game tic80 --skip --fs . --cmd 'load game.wasmp & import binary _build/wasm/release/build/game/game.wasm & run'

The same two commands can be placed in the game project's own run.sh for one-command development. The binding library does not provide a fixed script because the package path, output filename, and project filename belong to each game.

To create a distributable binary cartridge that embeds both the .wasmp resources and the compiled WASM, run:

tic80 --cli --fs . --cmd 'load game.wasmp & import binary _build/wasm/release/build/game/game.wasm & save game.tic & exit'

The free edition cannot load or save .wasmp text projects. It can still use .tic cartridges: create one with new wasm, import the compiled binary, save it as game.tic, and subsequently load that .tic file before importing newer builds.

#Video banks

TIC-80 provides two 16 KiB video-memory banks, represented by VideoBank. Drawing commands and direct access to VRAM operate on the currently selected bank. Bank1 is composited over Bank0; pixels matching Bank1's clear color are transparent and reveal Bank0 underneath.

Use vbank() to query the current drawing target without changing it. Use vbank_set() to select a target; it returns the previously selected bank so a temporary switch can be restored without assuming which bank was active:

let previous = @tic80.vbank_set(Bank1)
// Drawing here targets Bank1.
@tic80.cls(0)
@tic80.print("overlay", x=8, y=8)
ignore(@tic80.vbank_set(previous))

Video banks are available in both the free and Pro editions. They are separate from the cartridge resource banks addressed by the bank argument of sync(): vbank_set() changes the active VRAM drawing target, while sync() copies tiles, sprites, maps, audio, palettes, flags, or screen data between a cartridge resource bank and runtime memory.

#Cartridge callbacks

TIC-80 cartridge callbacks are functions exported by the WebAssembly module and called by TIC-80. They are the reverse of the functions in the raw API: the cartridge calls imported TIC-80 APIs such as cls, map, and spr, while TIC-80 calls these exported lifecycle functions.

The WebAssembly ABI signatures are:

CallbackWebAssembly signatureRequiredPurpose
BOOT() -> voidNoInitialize the cartridge once.
TIC() -> voidYesUpdate and draw one frame at 60 FPS.
SCN(i32) -> voidNoApply per-scanline effects to the 240x136 game area.
BDR(i32) -> voidNoApply per-scanline effects to the full 256x144 output, including the border.
MENU(i32) -> voidNoHandle a custom game-menu selection.

Export names are case-sensitive. A MoonBit function may use an idiomatic lowercase name internally, but its #export_name must use the uppercase name expected by TIC-80.

#Lifecycle

The first frame follows this sequence:

load cartridge -> initialize the WebAssembly VM -> BOOT() -> TIC() -> BDR(row) and SCN(row) while the frame is presented

Subsequent frames call TIC() and then the scanline callbacks. MENU(index) is only called when the player selects a custom game-menu item.

#BOOT()

BOOT() is called once after the WebAssembly VM has been initialized and before the first call to TIC(). It is suitable for one-time state, resource bank, and palette initialization.

///|
#export_name("BOOT")
pub fn boot() -> Unit {
// One-time initialization.
}

#TIC()

TIC() is the required main callback. TIC-80 calls it once per frame at 60 FPS. Input handling, game-state updates, and ordinary drawing normally belong here.

///|
#export_name("TIC")
pub fn tic() -> Unit {
@tic80.cls(0)
@tic80.map()
@tic80.spr(1, 100, 60)
}

The cartridge fails to load if its WebAssembly module does not export TIC.

#SCN(row)

SCN(row) is called for each scanline in the 240x136 game area. Its row argument ranges from 0 through 135.

Use it for raster effects that only affect the game area, such as per-line palette changes, gradients, water distortion, or horizontal scrolling. The frame should normally be drawn in TIC() first; SCN() then adjusts display state while that frame is presented.

///|
#export_name("SCN")
pub fn scn(row : Int) -> Unit {
// Adjust palette or screen-offset state for this game-area row.
}

#BDR(row)

BDR(row) is called for every scanline in the complete 256x144 output. Its row argument ranges from 0 through 143. The complete output consists of the 240x136 game area plus 8-pixel left and right borders and 4-pixel top and bottom borders.

Use SCN() when an effect should be limited to the game area. Use BDR() when the effect must also control the border or otherwise cover the complete output. TIC-80 processes BDR() before SCN() on scanlines where both callbacks participate, and applies palette changes before presenting the line.

///|
#export_name("BDR")
pub fn bdr(row : Int) -> Unit {
// Adjust palette or border state for this full-output row.
}

MENU(index) handles selections from the cartridge's custom game menu. Menu items are declared by the menu cartridge metadata, for example in a .wasmp project:

-- menu: RESTART MUSIC DIFFICULTY

The selected item is passed as a zero-based index:

SelectionCallback
RESTARTMENU(0)
MUSICMENU(1)
DIFFICULTYMENU(2)

///|
#export_name("MENU")
pub fn menu(index : Int) -> Unit {
match index {
0 => restart_game()
1 => toggle_music()
_ => ()
}
}

If the cartridge does not declare custom menu items, MENU() is not called.

#Minimal callback set

Most cartridges only need BOOT() and TIC() initially. Add SCN(), BDR(), or MENU() only when the cartridge uses their corresponding features.

#
Button

pub(all) enum Button {
P1Up
P1Down
P1Left
P1Right
P1A
P1B
P1X
P1Y
P2Up
P2Down
P2Left
P2Right
P2A
P2B
P2X
P2Y
P3Up
P3Down
P3Left
P3Right
P3A
P3B
P3X
P3Y
P4Up
P4Down
P4Left
P4Right
P4A
P4B
P4X
P4Y
} derive(Eq,
Debug
)

A button on one of the four TIC-80 gamepads.

The explicit tags are the button IDs expected by the TIC-80 host ABI.

#
ButtonState

pub struct ButtonState {
// private fields
} derive(Eq,
Debug
)

An immutable snapshot of all 32 TIC-80 gamepad buttons.

#
ButtonState::any

fn ButtonState::any(self : ButtonState) -> Bool

Returns whether this snapshot contains any held button.

#
ButtonState::held

fn ButtonState::held(self : ButtonState, button : Button) -> Bool

Returns whether the selected button was held in this snapshot.

#
Flip

pub(all) enum Flip {
NoFlip
Horizontal
Vertical
Both
} derive(Eq,
Debug
)

A sprite or map tile flip applied by TIC-80.

#
Key

pub(all) enum Key {
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z
Digit0
Digit1
Digit2
Digit3
Digit4
Digit5
Digit6
Digit7
Digit8
Digit9
Minus
Equals
LeftBracket
RightBracket
Backslash
Semicolon
Apostrophe
Grave
Comma
Period
Slash
Space
Tab
Return
Backspace
Delete
Insert
PageUp
PageDown
Home
End
Up
Down
Left
Right
CapsLock
Ctrl
Shift
Alt
Escape
F1
F2
F3
F4
F5
F6
F7
F8
F9
F10
F11
F12
Numpad0
Numpad1
Numpad2
Numpad3
Numpad4
Numpad5
Numpad6
Numpad7
Numpad8
Numpad9
NumpadPlus
NumpadMinus
NumpadMultiply
NumpadDivide
NumpadEnter
NumpadPeriod
} derive(Eq,
Debug
)

A TIC-80 keyboard key.

The explicit tags are the key codes expected by the TIC-80 host ABI.

#
MapRemapResult

pub(all) struct MapRemapResult {
tile_id : Int
flip : Flip
rotate : Rotation
} derive(Eq,
Debug
)

The current tile state passed to a map remap callback, and the replacement state returned by it.

#
MouseState

pub(all) struct MouseState {
x : Int
y : Int
scroll_x : Int
scroll_y : Int
left : Bool
middle : Bool
right : Bool
} derive(Eq,
Debug
)

A snapshot of the mouse cursor, wheel, and button state.

x and y are screen coordinates. scroll_x and scroll_y are signed wheel movements for the current frame.

#
Rotation

pub(all) enum Rotation {
NoRotation
Clockwise90
Clockwise180
Clockwise270
} derive(Eq,
Debug
)

A clockwise sprite or map tile rotation applied by TIC-80.

#
SfxChannel

pub(all) enum SfxChannel {
Channel0
Channel1
Channel2
Channel3
} derive(Eq,
Debug
)

One of TIC-80's four sound-effect channels.

The explicit tags are the channel IDs expected by the TIC-80 host ABI.

#
SyncSection

pub(all) enum SyncSection {
Tiles
Sprites
Map
Sfx
Music
Palette
Flags
Screen
} derive(Eq,
Debug
)

A cartridge resource section that can be transferred by sync.

Each variant corresponds to one bit in TIC-80's resource synchronization mask.

#
TextureSource

pub(all) enum TextureSource {
TileSheet
TileMap
OtherVideoBank
} derive(Eq,
Debug
)

The texture sampled by ttri.

TileSheet samples image RAM through the active tile or sprite-sheet segment. TileMap samples map RAM and resolves its tile IDs through image RAM. OtherVideoBank samples the framebuffer of the video bank that is not currently selected by vbank_set.

#
VideoBank

pub(all) enum VideoBank {
Bank0
Bank1
} derive(Eq,
Debug
)

One of TIC-80's two 16 KiB video-memory banks.

Drawing commands and direct VRAM access operate on the selected bank. Bank1 is composited over Bank0; pixels matching Bank1's clear color are transparent and reveal Bank0. Video banks are always available and are unrelated to the cartridge resource banks selected by sync.

#
any_btn

fn any_btn() -> Bool

Returns whether any button on any gamepad is currently held.

#
any_btnp

fn any_btnp() -> Bool

Returns whether any button was newly pressed.

#
any_key

fn any_key() -> Bool

Returns whether any keyboard key is currently held.

#
any_keyp

fn any_keyp() -> Bool

Returns whether any keyboard key was newly pressed.

#
btn

fn btn(button : Button) -> Bool

Returns whether the selected gamepad button is currently held.

#
btnp

fn btnp(button : Button, hold? : Int, period? : Int) -> Bool

Returns whether a button was newly pressed.

#
button_state

fn button_state() -> ButtonState

Captures all four gamepads in one host call.

#
circ

fn circ(x : Int, y : Int, radius : Int, color : Int) -> Unit

Draws a filled circle centered at (x, y).

#
circb

fn circb(x : Int, y : Int, radius : Int, color : Int) -> Unit

Draws a one-pixel circle outline centered at (x, y).

#
clip

fn clip(x : Int, y : Int, w : Int, h : Int) -> Unit

Sets the screen clipping rectangle.

#
clip_reset

fn clip_reset() -> Unit

Restores the clipping rectangle to the full screen.

#
cls

fn cls(color? : Int) -> Unit

Clears the screen with a color.

#
elli

fn elli(x : Int, y : Int, a : Int, b : Int, color : Int) -> Unit

Draws a filled ellipse centered at (x, y) with the given semi-axes.

#
ellib

fn ellib(x : Int, y : Int, a : Int, b : Int, color : Int) -> Unit

Draws a one-pixel ellipse outline centered at (x, y) with the given semi-axes.

#
exit

fn exit() -> Unit

Stops the current cartridge and returns to the TIC-80 console.

#
fget

fn fget(sprite_id : Int, flag : Int) -> Bool

Returns whether one of a sprite's eight user-defined flags is set.

sprite_id ranges from 0 through 511 and flag ranges from 0 through 7. Flag meanings are defined by the cartridge. Panics when either argument is outside its valid range.

#
font

fn font(text : StringView, x : Int, y : Int, transparent_colors? : Bytes, char_width? : Int, char_height? : Int, fixed? : Bool, scale? : Int, alt? : Bool) -> Int

Draws ASCII text with a custom raster font. Panics if text contains a non-ASCII code unit. Transparent colors are passed directly from transparent_colors.

#
fset

fn fset(sprite_id : Int, flag : Int, value : Bool) -> Unit

Sets or clears one of a sprite's eight user-defined flags.

sprite_id ranges from 0 through 511 and flag ranges from 0 through 7. The change affects runtime RAM; use sync with the Flags section and to_cart=true to copy runtime flags back to a cartridge resource bank. Panics when either argument is outside its valid range.

#
key

fn key(key : Key) -> Bool

Returns whether the selected keyboard key is currently held.

#
keyp

fn keyp(key : Key, hold? : Int, period? : Int) -> Bool

Returns whether a key was newly pressed or repeated.

#
line

fn line(x0 : Float, y0 : Float, x1 : Float, y1 : Float, color : Int) -> Unit

Draws a straight line from (x0, y0) to (x1, y1).

#
map

fn map(x? : Int, y? : Int, w? : Int, h? : Int, sx? : Int, sy? : Int, transparent_colors? : Bytes, scale? : Int, remap? : FuncRef[(MapRemapResult, Int, Int) -> MapRemapResult]) -> Unit

Draws a map region. When present, remap receives the current tile state and its map coordinates, and returns the state to draw. The callback must be capture-free because it is stored as a WebAssembly function reference.

#
memcpy

fn memcpy(dest : Int, src : Int, length : Int) -> Unit

Copies length bytes within TIC-80's 96 KiB RAM.

dest and src are byte addresses. The source and destination ranges may overlap. A negative length or a range outside TIC-80 RAM is ignored. This function does not copy MoonBit heap objects or Bytes values.

#
memset

fn memset(address : Int, value : Byte, length : Int) -> Unit

Fills length bytes of TIC-80 RAM with value.

address is a byte address. A negative length or a range outside TIC-80 RAM is ignored. This function does not operate on MoonBit heap objects or Bytes values.

#
mget

fn mget(x : Int, y : Int) -> Int

Returns the tile ID at map coordinates (x, y).

#
mouse

fn mouse() -> MouseState

Returns an independent snapshot of the current mouse state.

#
mset

fn mset(x : Int, y : Int, tile_id : Int) -> Unit

Sets the tile ID at map coordinates (x, y).

#
music

fn music(track : Int, frame? : Int, row? : Int, loop_? : Bool, sustain? : Bool, tempo? : Int, speed? : Int) -> Unit

Starts playing one of the eight tracks created in TIC-80's Music Editor.

track ranges from 0 through 7. By default playback starts at the beginning of the track, loops after its final populated frame, does not sustain notes across frame boundaries, and uses the tempo and speed stored in the track. Use frame and row to start at a specific position, or tempo and speed to override the track settings. Call music_stop to stop playback. Panics if track is outside the range 0 through 7.

#
music_stop

fn music_stop() -> Unit

Stops the currently playing music and resets its channels.

This does not stop sound effects started independently with sfx.

#
peek

fn peek(address : Int) -> Byte

Reads one byte from TIC-80 RAM at the byte address address.

Returns zero when address is outside TIC-80 RAM. Use peek4, peek2, or peek1 when the address is expressed in narrower units.

#
peek1

fn peek1(address : Int) -> Byte

Reads one bit from TIC-80 RAM.

address is a bit index. Addresses 8 * n through 8 * n + 7 select the bits of byte n from least to most significant. Returns zero when the address is outside TIC-80 RAM.

#
peek2

fn peek2(address : Int) -> Byte

Reads one two-bit value from TIC-80 RAM.

address is a two-bit-field index. Addresses 4 * n through 4 * n + 3 select the four fields of byte n from least to most significant. Returns zero when the address is outside TIC-80 RAM.

#
peek4

fn peek4(address : Int) -> Byte

Reads one four-bit nibble from TIC-80 RAM.

address is a nibble index rather than a byte address. Addresses 2 * n and 2 * n + 1 select the low and high nibbles of byte n, respectively. Returns zero when the address is outside TIC-80 RAM.

#
pix

fn pix(x : Int, y : Int) -> Byte

Returns the color of a pixel.

#
pix_set

fn pix_set(x : Int, y : Int, color : Int) -> Unit

Draws a pixel.

#
pmem

fn pmem(index : Int) -> UInt

Reads a value from a persistent-memory slot.

index ranges from 0 through 255. Panics when it is outside that range.

#
pmem_set

fn pmem_set(index : Int, value : UInt) -> UInt

Writes a persistent-memory slot and returns its previous value.

index ranges from 0 through 255. Panics when it is outside that range.

#
poke

fn poke(address : Int, value : Byte) -> Unit

Writes one byte to TIC-80 RAM at the byte address address.

Writes outside TIC-80 RAM are ignored. Use poke4, poke2, or poke1 when the address is expressed in narrower units.

#
poke1

fn poke1(address : Int, value : Byte) -> Unit

Writes one bit to TIC-80 RAM.

address uses the bit indexing described by peek1; only the lowest bit of value is stored. Writes outside TIC-80 RAM are ignored.

#
poke2

fn poke2(address : Int, value : Byte) -> Unit

Writes one two-bit value to TIC-80 RAM.

address uses the two-bit-field indexing described by peek2; only the low two bits of value are stored. Writes outside TIC-80 RAM are ignored.

#
poke4

fn poke4(address : Int, value : Byte) -> Unit

Writes one four-bit nibble to TIC-80 RAM.

address uses the nibble indexing described by peek4; only the low four bits of value are stored. Writes outside TIC-80 RAM are ignored.

#
print

fn print(text : StringView, x? : Int, y? : Int, color? : Int, fixed? : Bool, scale? : Int, small_font? : Bool) -> Int

Draws ASCII text and returns its width. Panics if text contains a non-ASCII code unit.

#
rect

fn rect(x : Int, y : Int, w : Int, h : Int, color : Int) -> Unit

Draws a filled rectangle at (x, y) with the given size.

#
rectb

fn rectb(x : Int, y : Int, w : Int, h : Int, color : Int) -> Unit

Draws a one-pixel rectangle outline at (x, y) with the given size.

#
sfx

fn sfx(id : Int, note? : Int, duration? : Int, channel? : SfxChannel, volume_left? : Int, volume_right? : Int, speed? : Int) -> Unit

Plays one of the 64 effects created in TIC-80's SFX Editor.

note is a combined note number from 0 through 95, with twelve notes per octave. When omitted, the note stored in the selected effect is used. duration is measured in 60 Hz ticks; -1 plays continuously. The left and right volumes range from 0 through 15. When speed is omitted, the speed stored in the selected effect is used; explicit speeds range from -4 through 3. Call sfx_stop to stop an effect on a channel.

Panics if id is outside the range 0 through 63.

#
sfx_stop

fn sfx_stop(channel? : SfxChannel) -> Unit

Stops the sound effect playing on channel.

This does not stop music playback or effects on the other channels.

#
spr

fn spr(id : Int, x : Int, y : Int, transparent_colors? : Bytes, scale? : Int, flip? : Flip, rotate? : Rotation, w? : Int, h? : Int) -> Unit

Transparent colors are passed directly from transparent_colors.

#
sync

fn sync(sections? : ArrayView[SyncSection], bank? : Int, to_cart? : Bool) -> Unit

Synchronizes cartridge resources with runtime memory. An empty sections list selects every section. By default, data is copied from bank 0 into runtime memory; set to_cart to copy runtime data back to the bank. Each section can only be synchronized once per frame.

#
time

fn time() -> Float

Returns the elapsed time in milliseconds since the cartridge started.

#
trace

fn trace(text : StringView, color? : Int) -> Unit

Writes ASCII text to the console. Panics if text contains a non-ASCII code unit.

#
tri

fn tri(x1 : Float, y1 : Float, x2 : Float, y2 : Float, x3 : Float, y3 : Float, color : Int) -> Unit

Draws a filled triangle with the given vertices.

#
trib

fn trib(x1 : Float, y1 : Float, x2 : Float, y2 : Float, x3 : Float, y3 : Float, color : Int) -> Unit

Draws a one-pixel triangle outline with the given vertices.

#
tstamp

fn tstamp() -> UInt

Returns the current Unix timestamp in seconds.

#
ttri

fn ttri(x1 : Float, y1 : Float, x2 : Float, y2 : Float, x3 : Float, y3 : Float, u1 : Float, v1 : Float, u2 : Float, v2 : Float, u3 : Float, v3 : Float, texture_source? : TextureSource, transparent_colors? : Bytes, z1? : Float, z2? : Float, z3? : Float, depth? : Bool) -> Unit

Draws a triangle textured from texture_source.

The u and v coordinates are interpreted in the coordinate space of the selected source. When OtherVideoBank is selected, ttri samples whichever video bank is not currently selected: it normally samples Bank1 while drawing to Bank0, and samples Bank0 while drawing to Bank1. Transparent colors are passed directly from transparent_colors.

#
vbank

fn vbank() -> VideoBank

Returns the currently selected video bank without changing it.

Use vbank_set when subsequent drawing or direct VRAM operations should target a different bank.

#
vbank_set

fn vbank_set(bank : VideoBank) -> VideoBank

Selects the video bank used by subsequent drawing and direct VRAM operations, and returns the previously selected bank.

The return value makes temporary switching straightforward: save it, draw into another bank, then pass it back to vbank_set to restore the previous target. This only switches VRAM; it does not select a cartridge resource bank for sync.

Source Files

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io