c64lua
v0.1.3
Published
Write PICO-8-flavored Lua, get a Commodore 64 .prg / .d64. The C64 member of the luacretro Lua-to-C console SDK family.
Downloads
396
Maintainers
Readme
c64lua
Write PICO-8-flavored Lua, get a Commodore 64 .prg / .d64.
c64lua is the Commodore 64 member of the luacretro family of Lua-to-C
console SDKs (alongside gtlua/GameTank, gbalua/Game Boy Advance, mdlua/Genesis).
You write a small PICO-8-shaped Lua game; c64lua ahead-of-time compiles it to
C, then to a 6502 .prg with the bundled cc65 toolchain, and wraps it into an
autostart .d64 disk image, the format the new Commodore 64 Ultimate hardware
and the homebrew/demo scene load. No interpreter, no VM: your Lua becomes native
6502 machine code. No native compiler or emulator to install, either.
Your first game
This is a complete C64 game. No assets, no boilerplate - one main.lua.
_draw() runs every frame and clears + redraws the whole scene - the normal
game-engine model. The runtime is double-buffered (you draw into a hidden
buffer that's shown only once it's complete), so a full-screen redraw every
frame is correct and tear-free. A full 160x200 cls+redraw runs below 60fps at
~1 MHz - that's honest C64 speed, not tearing (see the perf model in
docs/CHEATSHEET.md):
function _draw() -- runs every frame: clear + redraw
cls(0) -- black background
circfill(80, 96, 22, 10) -- head: a yellow circle
circfill(74, 90, 3, 0) -- left eye: black
circfill(86, 90, 3, 0) -- right eye
circ(80, 100, 12, 0) -- mouth: a black arc
print("hello c64", 58, 132, 7) -- white text
endBuild it and play it in a window:
npx c64lua run examples/hello/main.luaOr build the distributable - a .prg and an autostart .d64 disk image:
npx c64lua build examples/hello/main.lua -o hello.prg --d64 hello.d64That's the whole loop: write main.lua, run it, ship the .d64. Load the
disk on a real C64 (or the Ultimate) with LOAD"*",8,1 : RUN.
One C64 wrinkle: the multicolor pixel is 2:1 (double-wide), so a plain
circfillreads a touch wide on screen. The shippedexamples/hello/main.luaadds a tiny integer-onlydisc()helper (halve each span's x-extent) so the smiley in the screenshot above is perfectly round - see docs/DIFFERENCES.md for the fat-pixel details.
Why the C64
Unlike the NES, the C64 has a real framebuffer: video memory is plain RAM the
CPU can write at any time (VIC-II multicolor bitmap mode). So the full P8 draw
verb set works with no queue, no vblank budget, and no refusals:
cls/pset/line/rect/rectfill/circ/circfill/print/spr/pget. Its 16 fixed
hardware colors are the closest color-model kinship to PICO-8 of any console in
the family, and SID gives great-sounding sfx for very little driver code.
The honest cost: the C64 is ~1MHz with no blitter. It is the slowest pixel-pusher in the family. c64lua games look like PICO-8 games but budget like C64 games — see the perf model in docs/CHEATSHEET.md and the platform realities in docs/DIFFERENCES.md.
Native resolution
- Hardware: VIC-II multicolor bitmap mode, 160 x 200 native — each multicolor pixel is 2:1 (double-wide), so the displayed picture is 320 x 200 with 160 addressable columns, plus the hardware border.
- c64lua's canvas is 160 x 200, coordinates 0-159 x 0-199.
- Screenshots in this repo are integer-scaled (2x = 640 x 400) captures of the real emulator output, never resampled.
Build options & requirements
Start your own game by copying an example, then build with these flags:
npx c64lua build mygame/main.lua -o game.prg --d64 game.d64
# --dev attribute-clash border flash + counter diagnostics
# --num8 8.8 fixed point (smaller/faster math, less range)-o game.prgproduces the C64.prg;--d64 game.d64also wraps it into an autostart 1541 disk image (LOAD"*",8,1 : RUN, the format the C64 Ultimate and the demo scene load).npx c64lua run mygame/main.luabuilds and plays it in a window over the bundled VICE core, through the sharedromdev-core-runnerSDL host (the same one the whole SDK family and the romdev playtest tool use). The window needs@kmamal/sdl, an optional dependency of the runner.
Requirements: Node.js 24+, and nothing else -
npm install brings the cc65 toolchain (compiler + c64.lib + linker config)
as WebAssembly via romdev-toolchain-cc65. No native compiler, no VICE install.
The Lua dialect
c64lua speaks the same PICO-8-flavored subset as the rest of the luacretro
family (the shared front-end compiles it): 16.16 fixed-point numbers with
PICO-8 semantics, _init/_update/_update60/_draw callbacks, local-only
top-level declarations, structs via pool(), flat arrays via array(),
integer-typed locals inferred automatically. See
docs/CHEATSHEET.md for the full verb list and
docs/ASSETS.md for the color model + art pipeline.
Examples
| example | what it shows | | --- | --- | | hello | centered smiley + text (the "it works" cart) | | pad-square | joystick + a hardware MOB sprite | | mathcheck | fixed-point conformance (shared family goldens) | | plasma | the framebuffer flex — full-screen CPU rendering |
Docs
- CHEATSHEET.md — every verb, the color table, the perf model.
- DIFFERENCES.md — 2:1 fat pixels + per-cell attribute clash, honestly.
- ASSETS.md — the P8↔C64 color mapping + PNG import.
License
MIT. No AI attribution. See LICENSE.
