Skip to content

Wait for a turn on Tatung Einstein

Reuse an untimed command or press loop while retaining the machine’s actual display responsibilities.

Build the complete caller with make -C tatung-einstein/patterns/assembly/input/read-action. Press Space once, keep it held, then release and press again. The piece moves on each new press and returns to its starting position after two; holding does not repeat the action. Attach build/demo.dsk and use Control-BREAK to let MOS boot it. This is a disk-boot program, not a CP/M or BASIC loader.

Tatung Einstein native caller after accepted input.
Captured from the running caller in the configuration below.

Keep the world waiting

Display servicing continues while the turn count waits for a new Space press. The VDP keeps scanning its picture; wait_frame polls a fresh status announcement and the caller performs its bounded input, picture and sound work. It does not advance the rule merely because a field elapsed.

The actual loop and its machine setup

demo.asm
; Original capability caller: owns VDP, one audio channel and declared RAM.
VDP_DATA equ $08
VDP_CTRL equ $09
TURNS equ $8000
SOUND_TICKS equ $8005
; Stock MOS disk boot header: destination, end, entry, disk mode.
    org $9000
    dw $9000,$9200,start
    db 0
start:
    di
    ld sp,$fff0
    xor a
    ld ($8000),a
    ld ($8001),a
    ld ($8002),a
    ld ($8003),a
    ld a,1
    out ($20),a               ; mask dedicated keyboard interrupt
    ld a,7
    out ($02),a
    ld a,$7f
    out ($03),a               ; mute tone/noise gates, port A output / B input
    in a,($09)
    xor a
    out ($09),a
    ld a,$80
    out ($09),a          ; R0 legacy graphics
    xor a
    out ($09),a
    ld a,$81
    out ($09),a          ; R1 display and VDP IRQ disabled
    call init_picture
main:
    call wait_frame
    ld hl,($8002)
    inc hl
    ld ($8002),hl
    include "sample.inc"
    ld a,($8001)
    cpl
    and b
    jr z,no_action
    ld a,($8000)
    inc a
    ld ($8000),a
    call trigger_sound
no_action:
    ld a,b
    ld ($8001),a
    call draw_picture
    call service_sound
    jp main

; AF clobbered; caller exclusively owns status/control-port latch.
wait_frame:
    in a,($09)
wait_new:
    in a,($09)
    and $80
    jr z,wait_new
    ret
    include "picture.inc"
    include "sound.inc"
    defs $9200-$,0

Fit and adapt it

sample.inc is an inline hardware sampler: include it where the caller samples input. It has no RET/RTS and must not be called as a subroutine. It supplies the held state described below; the caller supplies previous-state storage and decides what a new press does.

Calculate a new-press mask as current AND NOT previous, then store current as previous on every update, including release. Test held state for continuous movement and the new-press mask for an action that should happen once. A key already held at the caller’s first sample counts as a press because previous starts at zero.

Keep the dedicated keyboard interrupt masked as in startup, or synchronise all AY register selections. An interrupt between address and data accesses can redirect a write.

The broken caller feeds held state into the action decision. Keeping the button down advances repeatedly; the native fault run reaches 32 instead of remaining at one. Restore the previous-state comparison and update previous on release as well.

A repeated-path mistake to recognise

no-edge.asm
; Original capability caller: owns VDP, one audio channel and declared RAM.
VDP_DATA equ $08
VDP_CTRL equ $09
TURNS equ $8000
SOUND_TICKS equ $8005
; Stock MOS disk boot header: destination, end, entry, disk mode.
    org $9000
    dw $9000,$9200,start
    db 0
start:
    di
    ld sp,$fff0
    xor a
    ld ($8000),a
    ld ($8001),a
    ld ($8002),a
    ld ($8003),a
    ld a,1
    out ($20),a               ; mask dedicated keyboard interrupt
    ld a,7
    out ($02),a
    ld a,$7f
    out ($03),a               ; mute tone/noise gates, port A output / B input
    in a,($09)
    xor a
    out ($09),a
    ld a,$80
    out ($09),a          ; R0 legacy graphics
    xor a
    out ($09),a
    ld a,$81
    out ($09),a          ; R1 display and VDP IRQ disabled
    call init_picture
main:
    call wait_frame
    ld hl,($8002)
    inc hl
    ld ($8002),hl
    include "../sample.inc"
    ld a,($8001)
    ld a,b
    nop
    jr z,no_action
    ld a,($8000)
    inc a
    ld ($8000),a
    call trigger_sound
no_action:
    ld a,b
    ld ($8001),a
    call draw_picture
    call service_sound
    jp main

; AF clobbered; caller exclusively owns status/control-port latch.
wait_frame:
    in a,($09)
wait_new:
    in a,($09)
    and $80
    jr z,wait_new
    ret
    include "../picture.inc"
    include "../sound.inc"
    defs $9200-$,0
Tatung Einstein faulty repeated path.
The fault fails the same rule-state guard as the working caller.

A timed turn is another policy: keep a deadline separately, decide how expiry competes with an arriving action, and service that clock while waiting. A blocking reader cannot provide that service from the same main path. A faster renderer or sound routine does not settle the rule.

See loop choices and the machine’s input entry for the reader’s complete contract.

Verification and sources

Target: TC-01 PAL, MOS 1.2, Ctrl-BREAK disk boot. These callers were executed with released Emu198x 0.44.1; assembly uses Asm198x 0.0.71. The sample folder contains reproduction instructions and retained observations. The independent checker binds source, native captures and actual output; its rejection tests exercise wrong state, missing files and false audio claims.

This qualifies the named emulator/firmware path. Pitch accuracy, original hardware and other peripherals require their own observations.

Tatung, Einstein Hardware Manual: programmable sound generator/keyboard interface and port decoding; MOS disk boot header; General Instrument AY-3-8910 data manual.