Skip to content

GX on a 6502

The 6502 runs the game. It does not have to push every pixel, decode every asset, or pretend 64 KiB is the size of the world.

GX matters on a 6502 precisely because D/BASIC divides the job differently from a traditional machine-specific BASIC. The small CPU remains excellent at rules, state machines, collision decisions, counters, and characterful game logic. Large storage, paging, graphics composition, resource decoding, sound, and bulk operations sit behind governed runner services.

This is not a faster 1982 interpreter with a few new commands. It is one portable bytecode program using the whole D/OS machine.

Alpha qualification

The large-memory and Intent architecture is the D/BASIC contract. The complete 121,067-byte GX engine, all 17 upstream sample programs, and every physical 6502 product route remain explicit alpha qualification rungs. A successful host or browser run is not a substitute for a captured hardware receipt.

The old limits and the new contract

Classic BASICs often shared 16–64 KiB among source, interpreter, variables, strings, screen memory, and the operating system. A large game became a puzzle about overlays and bank registers before it became a puzzle about play.

D/BASIC moves those constraints below the language:

Old program concern D/BASIC responsibility
source and machine code must fit below one 64 KiB ceiling DBC regions use wide lengths and are admitted from the runner's pool
code must select banks explicitly the runner pages the current code/data working set
arrays need near/far pointer variants arrays are bounds-checked values behind handles
art is copied through CPU-visible RAM every frame validated resources remain with the composed head
the CPU paints a framebuffer the application emits GFX meaning; the head composes it
sound code writes chip registers in tight loops AUDIO Intents describe notes, voices, effects, and streams
each machine needs another source tree one DBC image meets a target's admitted routes

The first 6502 profile targets a nominal 512 KiB logical workspace, with larger profiles able to follow. That is not a claim that the processor suddenly addresses 512 KiB in one instruction. It means program code and governed values are no longer designed around the processor's current 16-bit window.

What “memory limits are erased” means

It means the 16–64 KiB language ceiling is gone:

  • DBC code, constants, and metadata are not encoded with a permanent 64 KiB image limit;
  • arrays, strings, objects, maps, and loaded resources use governed identities rather than exposed addresses;
  • the runtime keeps a bounded hot working set visible to the CPU and pages other regions as needed;
  • banking is private adapter work, never a D/BASIC type or statement; and
  • allocation failure is a named, atomic capacity refusal rather than corruption.

It does not mean physical capacity and bandwidth disappeared. A 512 KiB target still has a 512 KiB budget. A badly laid-out hot loop can still page too often. A thousand active entities can still consume more turns than ten. D/BASIC removes ceremony and target forks; it does not repeal arithmetic.

How a GX frame is divided

Stage 6502/application work Runner/head work
input read normalized actions or a coherent input snapshot sample keyboard, joystick, paddle, pointer, or controller
update advance GX carriers, rules, score, AI, and collision consequences resume bounded provider work and preserve ordering
prepare choose camera, visible objects, animation identities, and semantic effects resolve retained resources and admitted capabilities
present emit a small ordered Intent stream compose tiles, sprites, text, colour, and final presentation
audio emit note/effect/music changes synthesize, mix, stream, or lower to the available sound route

The application authors state. The composed head authors pixels. A richer accelerator can sit over the composed head; if it refuses or is unavailable, the system returns to that product's composed baseline, not to a hidden CPU software painter.

One rectangle instead of 61,440 pixel decisions

A 320 × 192 frame contains 61,440 pixels. Clearing it byte by byte is exactly the kind of work a 6502 should not repeat when the intent is already known.

INTENT GFX.BEGIN_FRAME()
INTENT GFX.FILL_RECT(0, 0, 320, 192, 2081)
INTENT GFX.DRAW_TEXT(12, 12, "READY", 3, -1, 2081)
INTENT GFX.PRESENT()

The game issues four semantic operations. The head chooses how to satisfy them and reports completion only when the final presentation boundary is real.

The same principle scales:

  • a sprite request names resource, position, frame, visibility, and layer;
  • a tilemap request names tileset, map, camera, and visible region;
  • a surface move names retained content and a new location;
  • a polyline names points once instead of issuing one operation per pixel; and
  • a presentation hint is advisory metadata, never permission to change game meaning.

Keep art where it is used

The expensive version of a sprite is “copy all its pixels from the 6502 on every frame.” The D/BASIC version is “load this validated logical resource, then refer to it.”

package asset → validate once → decode/select representation → retain near compositor
                                    later frames send identity + geometry

A browser may keep a high-density image. A colour head may keep RGB565 or indexed data. A one-bit head may keep a declared monochrome variant. All represent the same logical hero and preserve its game geometry.

This is also why large game packages do not consume the 6502's hot working set merely because they contain music, backgrounds, and sprite sheets. The resource provider owns those bytes after admission; the game retains a logical owner, not a raw pointer.

Sound is an Intent, not a polling loop

A collision can request an effect in one bounded operation:

IF GXEntitiesOverlap(Actors(), HERO, COIN) THEN
  Score = Score + 100
  INTENT AUDIO.NOTE_ON(0, 1047, 224, 1, 8)
END IF

The sound provider decides whether that semantic voice becomes a native chip tone, a mixed oscillator, or another admitted rendering. Recorded sound and music use bounded PCM writes with an explicit media clock. The 6502 does not busy-wait between samples.

Good game audio code sends changes:

  • start or replace a voice;
  • stop a voice;
  • play an identified effect;
  • change a music state;
  • offer the next bounded PCM chunk; or
  • silence everything owned by this application on close.

It does not resend an unchanged register plan every game tick.

Autonomous presentation removes needless turns

Some game motion is visually meaningful but logically uneventful. A coin can loop its animation, a background can scroll at a fixed parallax factor, and a projectile can travel until a boundary or collision.

The full GX direction lets an admitted head retain that presentation state and advance it without a round trip for every intermediate pixel. The 6502 receives meaningful events—collision, completion, boundary, input—and remains authoritative for the game consequence.

This division is safe only when the contract names:

  • the initial state and exact owner;
  • the clock or tick domain;
  • which motion is presentation-only;
  • what event returns authority to the application; and
  • how cancellation and route changes preserve state.

If a route cannot keep that contract, GX lowers to bounded application-authored steps or refuses the optional feature. It never invents invisible physics.

The 121 KB engine is a feature, not an embarrassment

The upstream declaration and implementation pair is 121,067 bytes before adaptation. That size is valuable evidence:

  • it crosses the old 64 KiB psychological and container boundary;
  • it contains real control flow, arrays, records, BYREF calls, maps, strings, and platform seams;
  • it forces the compiler to separate hot per-frame code from cold loading and tooling code; and
  • it proves installed library delivery is about real reuse rather than tiny examples.

The 6502 never needs all 121 KB resident at once. Startup, map I/O, device discovery, editor tooling, collision, and animation are not simultaneously hot. The compiler lays out coherent regions; the runtime pages cold code; shared delivery keeps one reviewed engine artifact available to many games.

The complete paging receipt remains a qualification requirement. “It should fit” is architecture. A bounded, repeatable run of the engine and game suite is evidence.

Optimize the meaning, not the instruction count

Write operations that expose useful units:

ARRAYFILL Scratch(), 0
ARRAYSWAP FrontMap(), BackMap()
Frame = GXAnimationFrame(FirstFrame, FrameCount, Tick, TicksPerFrame)

ARRAYFILL exposes bulk work. ARRAYSWAP exchanges bindings instead of copying a world. GXAnimationFrame replaces a sleep-driven controller with deterministic arithmetic. These choices help every runner and are especially valuable on a 6502.

For game loops:

  1. keep each event or frame turn bounded;
  2. batch whole display and bulk-memory ideas;
  3. keep hot entity state compact and contiguous;
  4. avoid rebuilding strings, objects, and adapters inside DRAW;
  5. retain loaded resources;
  6. invalidate only when state or presentation actually changed; and
  7. make optional richness capability-driven.

Route changes do not fork the game

A D/BASIC game never asks IF MAC or IF ATARI. The runner adapts beneath the same Intent:

Capability Upgrade Honest lowering
font high-density outline rasterization stable resident hinted face
colour true colour / rich RGB565 deterministic indexed or monochrome mapping
sprite retained high-resolution image declared compact representation
audio mixed PCM and semantic voices bounded native tone route or named refusal
input keyboard + controller + pointer available normalized local device set
animation retained/autonomous composition bounded application-authored ticks

Geometry, collision, score, save data, and game decisions remain unchanged. A feature essential to play is declared and preflighted. Optional sparkle can lower or refuse without turning a win into a loss.

A developer-friendly 6502 workflow

  1. Build and play the game in the browser with the same D/BASIC model.
  2. Keep GX, application rules, and assets target-neutral.
  3. Inspect the image's declared Service and Intent capabilities.
  4. Test the composed baseline and every advertised accelerator separately.
  5. Measure hot turns, page misses, provider latency, and final presentation receipts.
  6. Capture physical-hardware qualification separately from host and emulator evidence.

The reward is unusually pleasant: a game can begin as a tiny browser experiment, grow into a packaged desktop application, and arrive on a 6502 without gaining a bank-switching chapter in its source.


Next: Networking →