Small tutorials

01 / BASIC + MACHINE LANGUAGE

A small event loop.
Built with WAIT.

Vessels arrive. The mouse moves. The clock ticks. How does a BASIC interface know when it has something to do? In AIS4CBM, the bridge is a single byte.

Commodore 128 · BASIC 7.0 + 8502 assembly · Code from the graphics viewer

01 / THE BUILDING BLOCK

Wait for a bit to change.

BASIC provides a compact way to watch a memory location:

BASIC syntax
WAIT address,mask[,invert]

address selects the byte to read. mask selects the bits we care about. The optional invert flips selected bits before the test; its default is zero. Execution continues as soon as this result is nonzero:

(memory byte XOR invert) AND mask

WAIT WF,32 waits for bit 5 to be set. WAIT WF,32,32 waits for it to be clear. With a mask of 34 (32 OR 2), either selected bit can satisfy the test.

02 / A BYTE BETWEEN TWO WORLDS

Machine language publishes. BASIC reacts.

The runtime owns a byte called notifyFlag. Each bit has a meaning. Here are three used by the graphics viewer:

Three flags from AIS4CBM
ValueBinaryMeaningProtocol
200000010Mouse notificationA pending flag to consume
400000100Clock tickA bit that toggles
3200100000Vessel update readyA pending flag to consume

Once the vessel-update fields are ready, the manager publishes the notification by setting its bit. ORA preserves the other bits:

Assembly excerpt · publish a vessel update
lda notifyFlag
ora #MESSAGE_FLAG
sta notifyFlag
ML prepares vessel data, sets bit 5 in notifyFlag, and BASIC's WAIT returns. The BASIC handler consumes and acknowledges the pending event.
The vessel-update handshake. Follow the solid arrows to BASIC, then the dashed acknowledgement back. Open full size ↗

The IRQ services time, mouse sampling and serial buffering. Message decoding and vessel-update publication happen in the main runtime path, reached through the BASIC statement hook. A separate wake-up bit lets the IRQ release WAIT when buffered serial data needs draining; clock ticks also bring BASIC back into the loop. BASIC dispatches the resulting notifications to its UI routines.

03 / THE ACTUAL LOOP

Choose. Wait. Handle.

These are the first three statements of BASIC line 113, separated here for readability:

AIS4CBM excerpt · BASIC line 113
MK=PEEK(MP)
WAIT WF,MK,MS
WM=PEEK(WF) AND MK
WF
Address of the runtime's notification byte.
MP / MK
The shared interest-mask address, and the mask copied for this wait.
MS
The last handled values of state bits, including the toggling tick.
WM
The selected bits sampled after WAIT returns.

The runtime supplies the addresses through its API; they are not arbitrary spare RAM. These fragments assume AIS4CBM is already initialized. They are extracts to study, not a standalone program.

Pending flags and changing state

A pending mouse flag leads to the mouse handler:

AIS4CBM excerpt · BASIC line 114
114 IF (WM AND 2) THEN GOSUB 600:GOTO 113

The handler consumes the click notification, then BASIC returns to WAIT. The full loop gives serial draining and interactive work priority before handling vessel updates.

The tick uses a different handshake. The IRQ toggles bit 2 with EOR; BASIC remembers the value it has handled in MS:

Assembly excerpt · publish a tick
lda #TICK
eor notifyFlag
sta notifyFlag
AIS4CBM excerpt · BASIC line 119
119 IF (XOR(WM,MS AND MK) AND 4) THEN MS=XOR(MS,4):GOSUB 200:GOTO 113

After handling a tick, the remembered bit matches the current bit. WAIT blocks again. The next toggle makes them differ, whether the transition is 0 → 1 or 1 → 0.

A timeline of the current tick bit and BASIC's last-handled bit. Highlighted intervals show a mismatch after both the rising and falling tick edges; each mismatch makes WAIT return.
WAIT watches the difference, so both edges work. The shaded intervals end when BASIC records the new state. Open full size ↗

A dialog can choose its events

BASIC changes the shared mask as the UI changes. For example, the following isolates the operation used to turn off interest in vessel updates:

Simplified teaching snippet · mask only the vessel-update bit
POKE MP,(PEEK(MP) AND 223)

223 = 255 - 32. The actual dialog temporarily excludes several bits and restores vessel interest once its drawing transition is complete. The ML producer also checks the mask and defers eligible deliveries. A mask alone would only change what wakes WAIT.

04 / TRY THE BITS

Make WAIT return.

Signal a mouse click or vessel update, then handle it. Try a tick, handle it, and toggle it again. Both tick edges wake BASIC. Uncheck an event to see how the mask changes the test.

05 / TAKE IT INTO YOUR OWN PROGRAM

Keep the handshake explicit.

  • Publish data before the flag. Once the consumer sees “ready”, its associated data must be usable.
  • A flag is not an event queue. Repeated sets can merge, and two toggles can cancel before they are observed. Use a counter or queue when every occurrence matters.
  • Define who acknowledges each bit. A BASIC PEEK/POKE read-and-write is not atomic against interrupts. Use an interrupt-safe ML acknowledgement when concurrent writers require it.
  • Keep the producer running. WAIT cannot advance if the selected condition never occurs. IRQ notifications require the relevant interrupt to remain enabled.

The result is a small interface between fast machine-language work and readable BASIC handlers: a byte of notifications, a mask of interests, and a clear agreement about what each bit means.

Once WAIT releases the handler, how does BASIC read the vessel’s data? The companion tutorial, Ask ML, get a BASIC value: USR, follows a field request through the numeric and string return paths.

Read the code

Links point to main in the public GitLab repository. Source line numbers may move as the project develops.