Skip to content

Read a button on Tatung Einstein

Reuse the actual machine-specific reader, with its caller, state, ownership and a diagnosed input mistake.

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.

Take the reader

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.

The reusable reader

sample.inc
    ld a,14
    out ($02),a               ; AY port A: row select
    ld a,$fe
    out ($03),a               ; select keyboard row 0
    ld a,15
    out ($02),a               ; AY port B: column return
    in a,($02)
    cpl
    and $40                   ; Space, row 0 bit 6
    ld b,a

Select AY register 14 and write $FE for keyboard row 0, then select register 15 and read port $02. Space is active-low bit 6. B receives 0/$40; AF/B and the AY register selection change.

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.

Caller storage: previous=$8001; turn=$8000, field clock=$8002/$8003; stack below $FFF0. The sample’s display initialiser and any interrupts have their own ownership requirements; keep them with this complete example.

Change it and recognise a mistake

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.

Deliberately broken input/update path

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 deliberately broken input caller.
Observed fault; use the working caller to repair it.

The complete caller

Working setup and integration

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

For more responsive controls, compare polling, buffering and press edges in input choices.

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.