Tachibana
Language: English

Group 33 — Event Manager Trinity

33,1 Init Events · 33,2 Get Next Event · 33,3 Event Available · 33,4 Flush Events · 33,5 Set Timer · 33,6 Event Status

The event manager of the Trinity toolbox stores keyboard, mouse and timers in a single queue, polled by 33,2. Types: 1 key down, 2 key up, 3 auto key, 4 mouse down, 5 mouse up, 6 mouse move (consecutive moves are merged), 7 wheel, 8 timer, 9 window update (group 34), 10 window activation.

An EventRecord is 8 bytes in 6502 RAM: type, message, message2, modifiers, X and Y (16 bits). Masks have bit n-1 for type n: 1 for key presses, $10 for button releases, $80 for timers; 0 means all types.

The examples receive keys typed or clicks of the mouse emulated by the bench, wait a little with 1,1, then read the queue.

33,1 Init Events Trinity

Starts the event manager with an empty queue: keyboard, mouse and timer events are stored in it.

Inputs
BytesTypeMeaning
P0-P1uint16types to collect (bit n-1 for type n, 0 = all)

Outputs: none.

Only the types in the mask enter the queue: with mask 1, the example only gets the two key presses of "ab", not their releases. Calling 33,1 again empties the queue. The keyboard queue of group 2 still receives the keys: the examples empty it before returning. 1,0 (DSP Reset) and 32,19 (Toolbox Reset) stop the manager. NeoBASIC has no keyword for this call.

Firmware documentation text

Toolbox (Trinity, T-12, ADR-01 of the fork). Starts the event manager with an empty queue : keyboard, mouse and timer events are then collected in one queue of 32 records, polled by Get Next Event. Parameters:0,1 is the mask of event types to collect (bit n-1 for type n, 0 = all). Event types : 1 key down, 2 key up, 3 auto key (repeat), 4 mouse down, 5 mouse up, 6 mouse move (consecutive moves are merged), 7 wheel, 8 timer, 9 update (message = window to redraw, see group 34), 10 activate (message = window, message2 = 1 front / 0). An EventRecord is 8 bytes in 6502 RAM : what, message, message2, modifiers, X (2), Y (2) ; message is the ASCII code for keys (0 if none), the buttons after the change for mouse down/up, the signed delta for the wheel, the timer number for timers ; message2 is the key code for keys, the button that changed for mouse down/up with bit 7 set for a double click ; modifiers are the keyboard modifiers for keys and the buttons held for mouse events ; X,Y is the mouse position when the event was posted. The keyboard queue of group 2 is unaffected. There is no blocking wait : API calls are synchronous and the hosts read their input between calls ; poll, or sleep with the interrupt tick (1,12) and WAI. The manager is stopped by DSP Reset.

C (llvm-mos)
/* Démarrer la file d'événements (groupe 33, fonction 1, Trinity). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

/* Event Manager : un EventRecord = type, message, message2, modificateurs, X, Y (8 octets). */
typedef struct { uint8_t what, message, message2, modifiers; int16_t x, y; } EventRecord;

static void p16(uint8_t i, uint16_t v) {        /* paramètre 16 bits en Pi, Pi+1 */
    ControlPort.params[i] = v & 0xFF;
    ControlPort.params[i + 1] = v >> 8;
}
static uint16_t temps(void) {                   /* 1,1 : temps en centièmes de seconde (16 bits bas) */
    libre();
    api(1, 1);
    return ControlPort.params[0] | ControlPort.params[1] << 8;
}
static void attendre(uint16_t cs) {             /* laisse le temps aux touches tapées d'arriver */
    uint16_t t0 = temps();
    while (temps() - t0 < cs)
        ;
}
static void init_events(uint16_t masque) {      /* 33,1 Init Events */
    libre(); p16(0, masque); api(33, 1);
}
static uint8_t disponibles(uint16_t masque) {   /* 33,3 Event Available */
    libre(); p16(0, masque); api(33, 3);
    return ControlPort.params[2];
}
static uint8_t suivant(EventRecord *e, uint16_t masque) {   /* 33,2 Get Next Event : 1 si un événement est rendu */
    libre(); p16(0, (uint16_t)e); p16(2, masque); api(33, 2);
    return ControlPort.params[4];
}
static void vider_clavier(void) {               /* la file du clavier (groupe 2) a reçu les mêmes touches : on la vide */
    do { libre(); api(2, 1); } while (ControlPort.params[0]);
}

int main(void) {
    uint8_t n1, n2;
    init_events(1);                     /* masque : bit 0 = type 1, touche enfoncée seulement */
    attendre(100);                      /* le banc tape « ab » */
    n1 = disponibles(0);
    init_events(0);                     /* une nouvelle initialisation vide la file */
    n2 = disponibles(0);
    vider_clavier();
    printf("Touches enfoncees recues : %u\nApres une nouvelle initialisation : %u\n", n1, n2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Touches enfoncees recues : 2
Apres une nouvelle initialisation : 0
Mad Pascal
// Démarrer la file d'événements (groupe 33, fonction 1, Trinity).
program ev1;
uses crt, neo6502;
type EventRecord = record what, message, message2, modifiers: byte; x, y: smallint; end;   // 8 octets

procedure appel(f: byte);                       // appel 33,f (paramètres déjà posés)
begin
  NeoSendMessage(33, f);
  NeoWaitMessage;
end;

procedure attendre(cs: cardinal);               // laisse le temps aux touches tapées d'arriver
var t0: cardinal;
begin
  t0 := NeoGetTimer;
  while NeoGetTimer - t0 < cs do ;
end;

procedure init_events(masque: word);            // 33,1 Init Events
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(1);
end;

function disponibles(masque: word): byte;       // 33,3 Event Available
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(3);
  result := NeoMessage.params[2];
end;

function suivant(e: pointer; masque: word): byte;   // 33,2 Get Next Event : 1 si un événement est rendu
begin
  NeoWaitMessage;
  wordParams[0] := word(e);
  wordParams[1] := masque;
  appel(2);
  result := NeoMessage.params[4];
end;

procedure vider_clavier;                        // la file du clavier (groupe 2) a reçu les mêmes touches : on la vide
begin
  repeat
    NeoSendMessage(2, 1);
    NeoWaitMessage;
  until NeoMessage.params[0] = 0;
end;

var n1, n2: byte;
begin
  init_events(1);                               // masque : bit 0 = type 1, touche enfoncée seulement
  attendre(100);                                // le banc tape « ab »
  n1 := disponibles(0);
  init_events(0);                               // une nouvelle initialisation vide la file
  n2 := disponibles(0);
  vider_clavier;
  writeln('Touches enfoncees recues : ', n1);
  writeln('Apres une nouvelle initialisation : ', n2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Touches enfoncees recues : 2
Apres une nouvelle initialisation : 0
NeoBASIC
10 ' Demarrer la file d'evenements (groupe 33, fonction 1, Trinity)
20 m = 1 : f = 1 : gosub 8000 : ' Init Events, masque : bit 0 = type 1, touche enfoncee seulement
30 cs = 100 : gosub 8200 : ' le banc tape « ab »
40 m = 0 : f = 3 : gosub 8000 : n1 = r2 : ' 33,3
50 m = 0 : f = 1 : gosub 8000 : ' une nouvelle initialisation vide la file
60 f = 3 : gosub 8000 : n2 = r2
70 gosub 8300
80 print "Touches enfoncees recues : ";n1
90 print "Apres une nouvelle initialisation : ";n2
100 end
8000 ' 33,f : P0-P1 = m ; resultat de P0 a P4 dans r0 a r4
8010 while peek($ff00) : wend : doke $ff04, m
8020 poke $ff01, f : poke $ff00, 33
8030 while peek($ff00) : wend
8040 r0 = peek($ff04) : r1 = peek($ff05) : r2 = peek($ff06) : r4 = peek($ff08)
8050 return
8100 ' 33,2 Get Next Event dans l'EventRecord e, masque m ; r4 = 1 si un evenement est rendu
8110 while peek($ff00) : wend : doke $ff04, e : doke $ff06, m : f = 2 : goto 8020
8200 ' laisse le temps aux touches tapees d'arriver : cs centiemes
8210 t0 = time()
8220 while time() - t0 < cs : wend
8230 return
8300 ' la file du clavier (groupe 2) a recu les memes touches : on la vide
8310 while inkey$() <> "" : wend
8320 return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Touches enfoncees recues : 2
Apres une nouvelle initialisation : 0
65C02 assembly (64tass)
; Démarrer la file d'événements (groupe 33, fonction 1, Trinity).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  lda #<b0
        ldx #>b0
        jsr envoi
        lda #<100
        ldx #>100
        jsr attendre               ; le banc tape « ab »
        lda #<b1
        ldx #>b1
        jsr envoi
        lda ControlPort+6
        sta n1
        lda #<b2
        ldx #>b2
        jsr envoi
        lda #<b3
        ldx #>b3
        jsr envoi
        lda ControlPort+6
        sta n2
        jsr vider
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda n1
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t5
        ldx #>t5
        jsr ecrire
        lda n2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

b0:     .byte 33, 1, 2             ; Init Events, masque : bit 0 = type 1, touche enfoncée seulement
        .word 1
b1:     .byte 33, 3, 2             ; 33,3 : Event Available
        .word 0
n1:     .byte 0
b2:     .byte 33, 1, 2             ; une nouvelle initialisation vide la file
        .word 0
b3:     .byte 33, 3, 2             ; 33,3
        .word 0
n2:     .byte 0
t4:     .text "Touches enfoncees recues : ", 0
t5:     .text "Apres une nouvelle initialisation : ", 0

; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi:  sta blk
        stx blk+1
        jsr WaitMessage
        ldy #2
        lda (blk),y                ; nombre de paramètres
        clc
        adc #3
        sta fin
        ldy #3
_copie: cpy fin
        beq _lance
        lda (blk),y
        sta ControlPort+1,y        ; octet 3 du bloc -> P0 ($FF04)...
        iny
        bra _copie
_lance: ldy #1
        lda (blk),y
        tax
        lda (blk)
        jmp appel

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Affiche le mot de 16 bits A (poids faible), X (poids fort) en décimal, sans zéros de tête.
dec16:  sta n16
        stx n16+1
        stz vu
        ldy #0                     ; puissance de 10 en cours : 10000, 1000, 100, 10
_p:     ldx #0                     ; chiffre
_s:     lda n16
        sec
        sbc p10l,y
        pha
        lda n16+1
        sbc p10h,y
        bcc _neg                   ; plus petit : chiffre trouvé
        sta n16+1
        pla
        sta n16
        inx
        bra _s
_neg:   pla
        txa
        ora vu
        beq _suiv                  ; pas de zéro de tête
        txa
        ora #'0'
        phy
        jsr WriteCharacter
        ply
        lda #1
        sta vu
_suiv:  iny
        cpy #4
        bne _p
        lda n16                    ; les unités, toujours
        ora #'0'
        jmp WriteCharacter
p10l:   .byte <10000, <1000, <100, <10
p10h:   .byte >10000, >1000, >100, >10
n16:    .word 0
vu:     .byte 0

; Note l'instant de départ (1,1 : temps en centièmes de seconde, 16 bits bas).
depart: jsr temps
        lda tl
        sta t0
        lda th
        sta t0+1
        rts

; C = 1 si au moins A (poids faible), X (poids fort) centièmes se sont écoulés depuis depart.
ecoule: sta duree
        stx duree+1
        jsr temps
        sec
        lda tl
        sbc t0
        sta tl
        lda th
        sbc t0+1
        sta th
        lda tl
        cmp duree
        lda th
        sbc duree+1
        rts

; Attend A (poids faible), X (poids fort) centièmes : le temps aux touches tapées d'arriver.
attendre:
        pha
        phx
        jsr depart
        plx
        pla
_att:   pha
        phx
        jsr ecoule
        plx
        pla
        bcc _att
        rts

temps:  jsr WaitMessage
        lda #1
        ldx #1                     ; 1,1 : temps
        jsr appel
        lda ControlPort+4
        sta tl
        lda ControlPort+5
        sta th
        rts

; La file du clavier (groupe 2) a reçu les mêmes touches : on la vide.
vider:  jsr WaitMessage
        lda #2
        ldx #1                     ; 2,1 : lire un caractère, 0 si aucun
        jsr appel
        lda ControlPort+4
        bne vider
        rts

t0:     .word 0
tl:     .byte 0
th:     .byte 0
duree:  .word 0

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
blk      = $82
diviseur: .byte 0
fin:     .byte 0
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Touches enfoncees recues : 2
Apres une nouvelle initialisation : 0

33,2 Get Next Event Trinity

Removes the oldest queued event of a requested type and writes it into an EventRecord.

Inputs
BytesTypeMeaning
P0-P1addraddress of the EventRecord (8 bytes)
P2-P3uint16type mask (0 = all)
Outputs
BytesTypeMeaning
P4uint81 if an event is returned, 0 for a null event (type 0, with the mouse position)
erreuruint8non zero if the address is bad

For a key, message is the ASCII code and message2 the key code: "a" gives 97 and 4, "B" gives 66, 5 and modifiers 2 (Shift). Due timers come first. There is no blocking wait: the queue is polled in a loop. NeoBASIC has no keyword for this call.

Firmware documentation text

Removes the oldest queued event whose type is in the mask Parameters:2,3 (0 = all) and writes it in the EventRecord at Parameters:0,1 ; Parameter:4 is 1 if an event was returned, 0 if a null event (type 0, with the mouse position) was written instead. Due timers post their event first. Error if the address is bad.

C (llvm-mos)
/* Lire l'événement suivant (groupe 33, fonction 2, Trinity). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

/* Event Manager : un EventRecord = type, message, message2, modificateurs, X, Y (8 octets). */
typedef struct { uint8_t what, message, message2, modifiers; int16_t x, y; } EventRecord;

static void p16(uint8_t i, uint16_t v) {        /* paramètre 16 bits en Pi, Pi+1 */
    ControlPort.params[i] = v & 0xFF;
    ControlPort.params[i + 1] = v >> 8;
}
static uint16_t temps(void) {                   /* 1,1 : temps en centièmes de seconde (16 bits bas) */
    libre();
    api(1, 1);
    return ControlPort.params[0] | ControlPort.params[1] << 8;
}
static void attendre(uint16_t cs) {             /* laisse le temps aux touches tapées d'arriver */
    uint16_t t0 = temps();
    while (temps() - t0 < cs)
        ;
}
static void init_events(uint16_t masque) {      /* 33,1 Init Events */
    libre(); p16(0, masque); api(33, 1);
}
static uint8_t disponibles(uint16_t masque) {   /* 33,3 Event Available */
    libre(); p16(0, masque); api(33, 3);
    return ControlPort.params[2];
}
static uint8_t suivant(EventRecord *e, uint16_t masque) {   /* 33,2 Get Next Event : 1 si un événement est rendu */
    libre(); p16(0, (uint16_t)e); p16(2, masque); api(33, 2);
    return ControlPort.params[4];
}
static void vider_clavier(void) {               /* la file du clavier (groupe 2) a reçu les mêmes touches : on la vide */
    do { libre(); api(2, 1); } while (ControlPort.params[0]);
}

static EventRecord ev;

int main(void) {
    uint8_t i, r;
    init_events(0);
    attendre(100);                      /* le banc tape « aB » */
    for (i = 0; i < 2; i++) {
        suivant(&ev, 1);                /* masque 1 : touches enfoncées seulement */
        printf("Type %u, caractere %u, code %u, modificateurs %u\n", ev.what, ev.message, ev.message2, ev.modifiers);
    }
    r = suivant(&ev, 1);                /* plus rien : événement nul */
    vider_clavier();
    printf("Plus de touche : rendu %u, type %u\n", r, ev.what);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Type 1, caractere 97, code 4, modificateurs 0
Type 1, caractere 66, code 5, modificateurs 2
Plus de touche : rendu 0, type 0
Mad Pascal
// Lire l'événement suivant (groupe 33, fonction 2, Trinity).
program ev2;
uses crt, neo6502;
type EventRecord = record what, message, message2, modifiers: byte; x, y: smallint; end;   // 8 octets

procedure appel(f: byte);                       // appel 33,f (paramètres déjà posés)
begin
  NeoSendMessage(33, f);
  NeoWaitMessage;
end;

procedure attendre(cs: cardinal);               // laisse le temps aux touches tapées d'arriver
var t0: cardinal;
begin
  t0 := NeoGetTimer;
  while NeoGetTimer - t0 < cs do ;
end;

procedure init_events(masque: word);            // 33,1 Init Events
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(1);
end;

function disponibles(masque: word): byte;       // 33,3 Event Available
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(3);
  result := NeoMessage.params[2];
end;

function suivant(e: pointer; masque: word): byte;   // 33,2 Get Next Event : 1 si un événement est rendu
begin
  NeoWaitMessage;
  wordParams[0] := word(e);
  wordParams[1] := masque;
  appel(2);
  result := NeoMessage.params[4];
end;

procedure vider_clavier;                        // la file du clavier (groupe 2) a reçu les mêmes touches : on la vide
begin
  repeat
    NeoSendMessage(2, 1);
    NeoWaitMessage;
  until NeoMessage.params[0] = 0;
end;

var ev: EventRecord;
    i, r: byte;
begin
  init_events(0);
  attendre(100);                                // le banc tape « aB »
  for i := 1 to 2 do begin
    suivant(@ev, 1);                            // masque 1 : touches enfoncées seulement
    writeln('Type ', ev.what, ', caractere ', ev.message, ', code ', ev.message2, ', modificateurs ', ev.modifiers);
  end;
  r := suivant(@ev, 1);                         // plus rien : événement nul
  vider_clavier;
  writeln('Plus de touche : rendu ', r, ', type ', ev.what);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Type 1, caractere 97, code 4, modificateurs 0
Type 1, caractere 66, code 5, modificateurs 2
Plus de touche : rendu 0, type 0
NeoBASIC
10 ' Lire l'evenement suivant (groupe 33, fonction 2, Trinity)
20 e = alloc(8) : ' EventRecord : type, message, message2, modificateurs, X, Y
30 m = 0 : f = 1 : gosub 8000 : ' 33,1
40 cs = 100 : gosub 8200 : ' le banc tape « aB »
50 for i = 1 to 2
60 m = 1 : gosub 8100 : ' Get Next Event, masque 1 : touches enfoncees seulement
70 print "Type ";peek(e);", caractere ";peek(e + 1);", code ";peek(e + 2);", modificateurs ";peek(e + 3)
80 next i
90 m = 1 : gosub 8100 : r = r4 : ' plus rien : evenement nul
100 gosub 8300
110 print "Plus de touche : rendu ";r;", type ";peek(e)
120 end
8000 ' 33,f : P0-P1 = m ; resultat de P0 a P4 dans r0 a r4
8010 while peek($ff00) : wend : doke $ff04, m
8020 poke $ff01, f : poke $ff00, 33
8030 while peek($ff00) : wend
8040 r0 = peek($ff04) : r1 = peek($ff05) : r2 = peek($ff06) : r4 = peek($ff08)
8050 return
8100 ' 33,2 Get Next Event dans l'EventRecord e, masque m ; r4 = 1 si un evenement est rendu
8110 while peek($ff00) : wend : doke $ff04, e : doke $ff06, m : f = 2 : goto 8020
8200 ' laisse le temps aux touches tapees d'arriver : cs centiemes
8210 t0 = time()
8220 while time() - t0 < cs : wend
8230 return
8300 ' la file du clavier (groupe 2) a recu les memes touches : on la vide
8310 while inkey$() <> "" : wend
8320 return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Type 1, caractere 97, code 4, modificateurs 0
Type 1, caractere 66, code 5, modificateurs 2
Plus de touche : rendu 0, type 0
65C02 assembly (64tass)
; Lire l'événement suivant (groupe 33, fonction 2, Trinity).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  lda #<b0
        ldx #>b0
        jsr envoi
        lda #<100
        ldx #>100
        jsr attendre               ; le banc tape « aB »
        lda #<b1
        ldx #>b1
        jsr envoi
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda ev
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda ev+1
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda ev+2
        jsr decimal
        lda #<t5
        ldx #>t5
        jsr ecrire
        lda ev+3
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<b6
        ldx #>b6
        jsr envoi
        lda #<t7
        ldx #>t7
        jsr ecrire
        lda ev
        jsr decimal
        lda #<t8
        ldx #>t8
        jsr ecrire
        lda ev+1
        jsr decimal
        lda #<t9
        ldx #>t9
        jsr ecrire
        lda ev+2
        jsr decimal
        lda #<t10
        ldx #>t10
        jsr ecrire
        lda ev+3
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<b11
        ldx #>b11
        jsr envoi
        lda ControlPort+8          ; P4 : 1 si un événement est rendu
        sta r
        jsr vider
        lda #<t12
        ldx #>t12
        jsr ecrire
        lda r
        jsr decimal
        lda #<t13
        ldx #>t13
        jsr ecrire
        lda ev
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

ev:     .fill 8                    ; EventRecord : type, message, message2, modificateurs, X, Y
b0:     .byte 33, 1, 2             ; 33,1 : Init Events
        .word 0
b1:     .byte 33, 2, 4             ; Get Next Event, masque 1 : touches enfoncées seulement
        .word ev, 1
t2:     .text "Type ", 0
t3:     .text ", caractere ", 0
t4:     .text ", code ", 0
t5:     .text ", modificateurs ", 0
b6:     .byte 33, 2, 4             ; Get Next Event, masque 1 : touches enfoncées seulement
        .word ev, 1
t7:     .text "Type ", 0
t8:     .text ", caractere ", 0
t9:     .text ", code ", 0
t10:    .text ", modificateurs ", 0
b11:    .byte 33, 2, 4             ; plus rien : événement nul
        .word ev, 1
r:      .byte 0
t12:    .text "Plus de touche : rendu ", 0
t13:    .text ", type ", 0

; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi:  sta blk
        stx blk+1
        jsr WaitMessage
        ldy #2
        lda (blk),y                ; nombre de paramètres
        clc
        adc #3
        sta fin
        ldy #3
_copie: cpy fin
        beq _lance
        lda (blk),y
        sta ControlPort+1,y        ; octet 3 du bloc -> P0 ($FF04)...
        iny
        bra _copie
_lance: ldy #1
        lda (blk),y
        tax
        lda (blk)
        jmp appel

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Affiche le mot de 16 bits A (poids faible), X (poids fort) en décimal, sans zéros de tête.
dec16:  sta n16
        stx n16+1
        stz vu
        ldy #0                     ; puissance de 10 en cours : 10000, 1000, 100, 10
_p:     ldx #0                     ; chiffre
_s:     lda n16
        sec
        sbc p10l,y
        pha
        lda n16+1
        sbc p10h,y
        bcc _neg                   ; plus petit : chiffre trouvé
        sta n16+1
        pla
        sta n16
        inx
        bra _s
_neg:   pla
        txa
        ora vu
        beq _suiv                  ; pas de zéro de tête
        txa
        ora #'0'
        phy
        jsr WriteCharacter
        ply
        lda #1
        sta vu
_suiv:  iny
        cpy #4
        bne _p
        lda n16                    ; les unités, toujours
        ora #'0'
        jmp WriteCharacter
p10l:   .byte <10000, <1000, <100, <10
p10h:   .byte >10000, >1000, >100, >10
n16:    .word 0
vu:     .byte 0

; Note l'instant de départ (1,1 : temps en centièmes de seconde, 16 bits bas).
depart: jsr temps
        lda tl
        sta t0
        lda th
        sta t0+1
        rts

; C = 1 si au moins A (poids faible), X (poids fort) centièmes se sont écoulés depuis depart.
ecoule: sta duree
        stx duree+1
        jsr temps
        sec
        lda tl
        sbc t0
        sta tl
        lda th
        sbc t0+1
        sta th
        lda tl
        cmp duree
        lda th
        sbc duree+1
        rts

; Attend A (poids faible), X (poids fort) centièmes : le temps aux touches tapées d'arriver.
attendre:
        pha
        phx
        jsr depart
        plx
        pla
_att:   pha
        phx
        jsr ecoule
        plx
        pla
        bcc _att
        rts

temps:  jsr WaitMessage
        lda #1
        ldx #1                     ; 1,1 : temps
        jsr appel
        lda ControlPort+4
        sta tl
        lda ControlPort+5
        sta th
        rts

; La file du clavier (groupe 2) a reçu les mêmes touches : on la vide.
vider:  jsr WaitMessage
        lda #2
        ldx #1                     ; 2,1 : lire un caractère, 0 si aucun
        jsr appel
        lda ControlPort+4
        bne vider
        rts

t0:     .word 0
tl:     .byte 0
th:     .byte 0
duree:  .word 0

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
blk      = $82
diviseur: .byte 0
fin:     .byte 0
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Type 1, caractere 97, code 4, modificateurs 0
Type 1, caractere 66, code 5, modificateurs 2
Plus de touche : rendu 0, type 0

33,3 Event Available Trinity

Counts the queued events of a requested type, without removing them.

Inputs
BytesTypeMeaning
P0-P1uint16type mask (bit n-1 for type n, 0 = all)
Outputs
BytesTypeMeaning
P2uint8number of events

The example waits for two button releases (type 5), then counts. For a button, message2 is the button that changed, with bit 7 set for a double click: the second, quick click gives 129. On the bench, each button change also comes with a move (type 6): the example therefore counts with masks. NeoBASIC has no keyword for this call.

Firmware documentation text

Returns in Parameter:2 the number of queued events whose type is in the mask Parameters:0,1 (0 = all).

C (llvm-mos)
/* Compter les événements en attente (groupe 33, fonction 3, Trinity). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

/* Event Manager : un EventRecord = type, message, message2, modificateurs, X, Y (8 octets). */
typedef struct { uint8_t what, message, message2, modifiers; int16_t x, y; } EventRecord;

static void p16(uint8_t i, uint16_t v) {        /* paramètre 16 bits en Pi, Pi+1 */
    ControlPort.params[i] = v & 0xFF;
    ControlPort.params[i + 1] = v >> 8;
}
static uint16_t temps(void) {                   /* 1,1 : temps en centièmes de seconde (16 bits bas) */
    libre();
    api(1, 1);
    return ControlPort.params[0] | ControlPort.params[1] << 8;
}
static void attendre(uint16_t cs) {             /* laisse le temps aux touches tapées d'arriver */
    uint16_t t0 = temps();
    while (temps() - t0 < cs)
        ;
}
static void init_events(uint16_t masque) {      /* 33,1 Init Events */
    libre(); p16(0, masque); api(33, 1);
}
static uint8_t disponibles(uint16_t masque) {   /* 33,3 Event Available */
    libre(); p16(0, masque); api(33, 3);
    return ControlPort.params[2];
}
static uint8_t suivant(EventRecord *e, uint16_t masque) {   /* 33,2 Get Next Event : 1 si un événement est rendu */
    libre(); p16(0, (uint16_t)e); p16(2, masque); api(33, 2);
    return ControlPort.params[4];
}
static void vider_clavier(void) {               /* la file du clavier (groupe 2) a reçu les mêmes touches : on la vide */
    do { libre(); api(2, 1); } while (ControlPort.params[0]);
}

static EventRecord ev;

int main(void) {
    uint8_t enf, rel, b1, b2;
    uint16_t t0;
    init_events(0);
    t0 = temps();                       /* le banc clique deux fois, vite : un double clic */
    while (disponibles(0x10) < 2 && temps() - t0 < 600)   /* bit 4 : type 5, bouton relâché */
        ;
    enf = disponibles(0x08);            /* bit 3 : type 4, bouton enfoncé */
    rel = disponibles(0x10);
    suivant(&ev, 0x08);
    b1 = ev.message2;                   /* bouton qui a changé */
    suivant(&ev, 0x08);
    b2 = ev.message2;                   /* bit 7 : double clic */
    printf("Enfonces : %u, relaches : %u\nPremier clic : %u, second : %u\n", enf, rel, b1, b2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Enfonces : 2, relaches : 2
Premier clic : 1, second : 129
Mad Pascal
// Compter les événements en attente (groupe 33, fonction 3, Trinity).
program ev3;
uses crt, neo6502;
type EventRecord = record what, message, message2, modifiers: byte; x, y: smallint; end;   // 8 octets

procedure appel(f: byte);                       // appel 33,f (paramètres déjà posés)
begin
  NeoSendMessage(33, f);
  NeoWaitMessage;
end;

procedure attendre(cs: cardinal);               // laisse le temps aux touches tapées d'arriver
var t0: cardinal;
begin
  t0 := NeoGetTimer;
  while NeoGetTimer - t0 < cs do ;
end;

procedure init_events(masque: word);            // 33,1 Init Events
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(1);
end;

function disponibles(masque: word): byte;       // 33,3 Event Available
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(3);
  result := NeoMessage.params[2];
end;

function suivant(e: pointer; masque: word): byte;   // 33,2 Get Next Event : 1 si un événement est rendu
begin
  NeoWaitMessage;
  wordParams[0] := word(e);
  wordParams[1] := masque;
  appel(2);
  result := NeoMessage.params[4];
end;

procedure vider_clavier;                        // la file du clavier (groupe 2) a reçu les mêmes touches : on la vide
begin
  repeat
    NeoSendMessage(2, 1);
    NeoWaitMessage;
  until NeoMessage.params[0] = 0;
end;

var ev: EventRecord;
    enf, rel, b1, b2: byte;
    t0: cardinal;
begin
  init_events(0);
  t0 := NeoGetTimer;                            // le banc clique deux fois, vite : un double clic
  while (disponibles($10) < 2) and (NeoGetTimer - t0 < 600) do ;   // bit 4 : type 5, bouton relâché
  enf := disponibles($08);                      // bit 3 : type 4, bouton enfoncé
  rel := disponibles($10);
  suivant(@ev, $08);
  b1 := ev.message2;                            // bouton qui a changé
  suivant(@ev, $08);
  b2 := ev.message2;                            // bit 7 : double clic
  writeln('Enfonces : ', enf, ', relaches : ', rel);
  writeln('Premier clic : ', b1, ', second : ', b2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Enfonces : 2, relaches : 2
Premier clic : 1, second : 129
NeoBASIC
10 ' Compter les evenements en attente (groupe 33, fonction 3, Trinity)
20 e = alloc(8)
30 m = 0 : f = 1 : gosub 8000 : ' 33,1
40 t1 = time() : ' le banc clique deux fois, vite : un double clic
50 m = $10 : f = 3 : gosub 8000 : ' Event Available, bit 4 : type 5, bouton relache
60 if r2 >= 2 then 70
65 if time() - t1 < 600 then 50
70 m = $08 : f = 3 : gosub 8000 : en = r2 : ' bit 3 : type 4, bouton enfonce
80 m = $10 : f = 3 : gosub 8000 : re = r2
90 m = $08 : gosub 8100 : b1 = peek(e + 2) : ' bouton qui a change
100 m = $08 : gosub 8100 : b2 = peek(e + 2) : ' bit 7 : double clic
110 print "Enfonces : ";en;", relaches : ";re
120 print "Premier clic : ";b1;", second : ";b2
130 end
8000 ' 33,f : P0-P1 = m ; resultat de P0 a P4 dans r0 a r4
8010 while peek($ff00) : wend : doke $ff04, m
8020 poke $ff01, f : poke $ff00, 33
8030 while peek($ff00) : wend
8040 r0 = peek($ff04) : r1 = peek($ff05) : r2 = peek($ff06) : r4 = peek($ff08)
8050 return
8100 ' 33,2 Get Next Event dans l'EventRecord e, masque m ; r4 = 1 si un evenement est rendu
8110 while peek($ff00) : wend : doke $ff04, e : doke $ff06, m : f = 2 : goto 8020
8200 ' laisse le temps aux touches tapees d'arriver : cs centiemes
8210 t0 = time()
8220 while time() - t0 < cs : wend
8230 return
8300 ' la file du clavier (groupe 2) a recu les memes touches : on la vide
8310 while inkey$() <> "" : wend
8320 return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Enfonces : 2, relaches : 2
Premier clic : 1, second : 129
65C02 assembly (64tass)
; Compter les événements en attente (groupe 33, fonction 3, Trinity).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  lda #<b0
        ldx #>b0
        jsr envoi
        jsr depart                 ; le banc clique deux fois, vite : un double clic
_att:   lda #<b1
        ldx #>b1
        jsr envoi
        lda ControlPort+6
        cmp #2
        bcs _fin
        lda #<600
        ldx #>600
        jsr ecoule
        bcc _att
_fin:
        lda #<b2
        ldx #>b2
        jsr envoi
        lda ControlPort+6
        sta en
        lda #<b3
        ldx #>b3
        jsr envoi
        lda ControlPort+6
        sta re
        lda #<b4
        ldx #>b4
        jsr envoi
        lda ev+2                   ; bouton qui a changé
        sta b1v
        lda #<b5
        ldx #>b5
        jsr envoi
        lda ev+2                   ; bit 7 : double clic
        sta b2v
        lda #<t6
        ldx #>t6
        jsr ecrire
        lda en
        jsr decimal
        lda #<t7
        ldx #>t7
        jsr ecrire
        lda re
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t8
        ldx #>t8
        jsr ecrire
        lda b1v
        jsr decimal
        lda #<t9
        ldx #>t9
        jsr ecrire
        lda b2v
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

ev:     .fill 8                    ; EventRecord : type, message, message2, modificateurs, X, Y
b0:     .byte 33, 1, 2             ; 33,1 : Init Events
        .word 0
b1:     .byte 33, 3, 2             ; Event Available, bit 4 : type 5, bouton relâché
        .word $10
b2:     .byte 33, 3, 2             ; bit 3 : type 4, bouton enfoncé
        .word $08
en:     .byte 0
b3:     .byte 33, 3, 2             ; 33,3
        .word $10
re:     .byte 0
b4:     .byte 33, 2, 4             ; 33,2 : Get Next Event
        .word ev, $08
b1v:    .byte 0
b2v:    .byte 0
b5:     .byte 33, 2, 4             ; 33,2 : Get Next Event
        .word ev, $08
t6:     .text "Enfonces : ", 0
t7:     .text ", relaches : ", 0
t8:     .text "Premier clic : ", 0
t9:     .text ", second : ", 0

; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi:  sta blk
        stx blk+1
        jsr WaitMessage
        ldy #2
        lda (blk),y                ; nombre de paramètres
        clc
        adc #3
        sta fin
        ldy #3
_copie: cpy fin
        beq _lance
        lda (blk),y
        sta ControlPort+1,y        ; octet 3 du bloc -> P0 ($FF04)...
        iny
        bra _copie
_lance: ldy #1
        lda (blk),y
        tax
        lda (blk)
        jmp appel

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Affiche le mot de 16 bits A (poids faible), X (poids fort) en décimal, sans zéros de tête.
dec16:  sta n16
        stx n16+1
        stz vu
        ldy #0                     ; puissance de 10 en cours : 10000, 1000, 100, 10
_p:     ldx #0                     ; chiffre
_s:     lda n16
        sec
        sbc p10l,y
        pha
        lda n16+1
        sbc p10h,y
        bcc _neg                   ; plus petit : chiffre trouvé
        sta n16+1
        pla
        sta n16
        inx
        bra _s
_neg:   pla
        txa
        ora vu
        beq _suiv                  ; pas de zéro de tête
        txa
        ora #'0'
        phy
        jsr WriteCharacter
        ply
        lda #1
        sta vu
_suiv:  iny
        cpy #4
        bne _p
        lda n16                    ; les unités, toujours
        ora #'0'
        jmp WriteCharacter
p10l:   .byte <10000, <1000, <100, <10
p10h:   .byte >10000, >1000, >100, >10
n16:    .word 0
vu:     .byte 0

; Note l'instant de départ (1,1 : temps en centièmes de seconde, 16 bits bas).
depart: jsr temps
        lda tl
        sta t0
        lda th
        sta t0+1
        rts

; C = 1 si au moins A (poids faible), X (poids fort) centièmes se sont écoulés depuis depart.
ecoule: sta duree
        stx duree+1
        jsr temps
        sec
        lda tl
        sbc t0
        sta tl
        lda th
        sbc t0+1
        sta th
        lda tl
        cmp duree
        lda th
        sbc duree+1
        rts

; Attend A (poids faible), X (poids fort) centièmes : le temps aux touches tapées d'arriver.
attendre:
        pha
        phx
        jsr depart
        plx
        pla
_att:   pha
        phx
        jsr ecoule
        plx
        pla
        bcc _att
        rts

temps:  jsr WaitMessage
        lda #1
        ldx #1                     ; 1,1 : temps
        jsr appel
        lda ControlPort+4
        sta tl
        lda ControlPort+5
        sta th
        rts

; La file du clavier (groupe 2) a reçu les mêmes touches : on la vide.
vider:  jsr WaitMessage
        lda #2
        ldx #1                     ; 2,1 : lire un caractère, 0 si aucun
        jsr appel
        lda ControlPort+4
        bne vider
        rts

t0:     .word 0
tl:     .byte 0
th:     .byte 0
duree:  .word 0

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
blk      = $82
diviseur: .byte 0
fin:     .byte 0
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Enfonces : 2, relaches : 2
Premier clic : 1, second : 129

33,4 Flush Events Trinity

Discards the queued events of a requested type.

Inputs
BytesTypeMeaning
P0-P1uint16type mask (bit n-1 for type n, 0 = all)

Outputs: none.

"abc" gives 6 events: 3 key presses, 3 releases. Mask 2 discards the releases, mask 0 everything else. In NeoBASIC, the queue also gets the release of the Enter key that started `run`: 7 events at the start. NeoBASIC has no keyword for this call.

Firmware documentation text

Discards the queued events whose type is in the mask Parameters:0,1 (0 = all).

C (llvm-mos)
/* Vider la file d'événements (groupe 33, fonction 4, Trinity). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

/* Event Manager : un EventRecord = type, message, message2, modificateurs, X, Y (8 octets). */
typedef struct { uint8_t what, message, message2, modifiers; int16_t x, y; } EventRecord;

static void p16(uint8_t i, uint16_t v) {        /* paramètre 16 bits en Pi, Pi+1 */
    ControlPort.params[i] = v & 0xFF;
    ControlPort.params[i + 1] = v >> 8;
}
static uint16_t temps(void) {                   /* 1,1 : temps en centièmes de seconde (16 bits bas) */
    libre();
    api(1, 1);
    return ControlPort.params[0] | ControlPort.params[1] << 8;
}
static void attendre(uint16_t cs) {             /* laisse le temps aux touches tapées d'arriver */
    uint16_t t0 = temps();
    while (temps() - t0 < cs)
        ;
}
static void init_events(uint16_t masque) {      /* 33,1 Init Events */
    libre(); p16(0, masque); api(33, 1);
}
static uint8_t disponibles(uint16_t masque) {   /* 33,3 Event Available */
    libre(); p16(0, masque); api(33, 3);
    return ControlPort.params[2];
}
static uint8_t suivant(EventRecord *e, uint16_t masque) {   /* 33,2 Get Next Event : 1 si un événement est rendu */
    libre(); p16(0, (uint16_t)e); p16(2, masque); api(33, 2);
    return ControlPort.params[4];
}
static void vider_clavier(void) {               /* la file du clavier (groupe 2) a reçu les mêmes touches : on la vide */
    do { libre(); api(2, 1); } while (ControlPort.params[0]);
}

int main(void) {
    uint8_t a, b, c;
    init_events(0);
    attendre(100);                      /* le banc tape « abc » : 3 enfoncées, 3 relâchées */
    a = disponibles(0);
    libre(); p16(0, 2); api(33, 4);     /* Flush Events, masque 2 : bit 1 = type 2, touche relâchée */
    b = disponibles(0);
    libre(); p16(0, 0); api(33, 4);     /* masque 0 : tout */
    c = disponibles(0);
    vider_clavier();
    printf("Avant : %u, sans relachees : %u, tout vide : %u\n", a, b, c);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Avant : 6, sans relachees : 3, tout vide : 0
Mad Pascal
// Vider la file d'événements (groupe 33, fonction 4, Trinity).
program ev4;
uses crt, neo6502;
type EventRecord = record what, message, message2, modifiers: byte; x, y: smallint; end;   // 8 octets

procedure appel(f: byte);                       // appel 33,f (paramètres déjà posés)
begin
  NeoSendMessage(33, f);
  NeoWaitMessage;
end;

procedure attendre(cs: cardinal);               // laisse le temps aux touches tapées d'arriver
var t0: cardinal;
begin
  t0 := NeoGetTimer;
  while NeoGetTimer - t0 < cs do ;
end;

procedure init_events(masque: word);            // 33,1 Init Events
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(1);
end;

function disponibles(masque: word): byte;       // 33,3 Event Available
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(3);
  result := NeoMessage.params[2];
end;

function suivant(e: pointer; masque: word): byte;   // 33,2 Get Next Event : 1 si un événement est rendu
begin
  NeoWaitMessage;
  wordParams[0] := word(e);
  wordParams[1] := masque;
  appel(2);
  result := NeoMessage.params[4];
end;

procedure vider_clavier;                        // la file du clavier (groupe 2) a reçu les mêmes touches : on la vide
begin
  repeat
    NeoSendMessage(2, 1);
    NeoWaitMessage;
  until NeoMessage.params[0] = 0;
end;

var a, b, c: byte;
begin
  init_events(0);
  attendre(100);                                // le banc tape « abc » : 3 enfoncées, 3 relâchées
  a := disponibles(0);
  NeoWaitMessage;
  wordParams[0] := 2;                           // Flush Events, masque 2 : bit 1 = type 2, touche relâchée
  appel(4);
  b := disponibles(0);
  NeoWaitMessage;
  wordParams[0] := 0;                           // masque 0 : tout
  appel(4);
  c := disponibles(0);
  vider_clavier;
  writeln('Avant : ', a, ', sans relachees : ', b, ', tout vide : ', c);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Avant : 6, sans relachees : 3, tout vide : 0
NeoBASIC
10 ' Vider la file d'evenements (groupe 33, fonction 4, Trinity)
20 m = 0 : f = 1 : gosub 8000 : ' 33,1
30 cs = 100 : gosub 8200 : ' le banc tape « abc » : 3 enfoncees, 3 relachees
40 m = 0 : f = 3 : gosub 8000 : a = r2
50 m = 2 : f = 4 : gosub 8000 : ' Flush Events, masque 2 : bit 1 = type 2, touche relachee
60 m = 0 : f = 3 : gosub 8000 : b = r2
70 m = 0 : f = 4 : gosub 8000 : ' masque 0 : tout
80 f = 3 : gosub 8000 : c = r2
90 gosub 8300
100 print "Avant : ";a;", sans relachees : ";b;", tout vide : ";c
110 end
8000 ' 33,f : P0-P1 = m ; resultat de P0 a P4 dans r0 a r4
8010 while peek($ff00) : wend : doke $ff04, m
8020 poke $ff01, f : poke $ff00, 33
8030 while peek($ff00) : wend
8040 r0 = peek($ff04) : r1 = peek($ff05) : r2 = peek($ff06) : r4 = peek($ff08)
8050 return
8100 ' 33,2 Get Next Event dans l'EventRecord e, masque m ; r4 = 1 si un evenement est rendu
8110 while peek($ff00) : wend : doke $ff04, e : doke $ff06, m : f = 2 : goto 8020
8200 ' laisse le temps aux touches tapees d'arriver : cs centiemes
8210 t0 = time()
8220 while time() - t0 < cs : wend
8230 return
8300 ' la file du clavier (groupe 2) a recu les memes touches : on la vide
8310 while inkey$() <> "" : wend
8320 return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Avant : 7, sans relachees : 3, tout vide : 0
65C02 assembly (64tass)
; Vider la file d'événements (groupe 33, fonction 4, Trinity).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  lda #<b0
        ldx #>b0
        jsr envoi
        lda #<100
        ldx #>100
        jsr attendre               ; le banc tape « abc » : 3 enfoncées, 3 relâchées
        lda #<b1
        ldx #>b1
        jsr envoi
        lda ControlPort+6
        sta a
        lda #<b2
        ldx #>b2
        jsr envoi
        lda #<b3
        ldx #>b3
        jsr envoi
        lda ControlPort+6
        sta b
        lda #<b4
        ldx #>b4
        jsr envoi
        lda #<b5
        ldx #>b5
        jsr envoi
        lda ControlPort+6
        sta c
        jsr vider
        lda #<t6
        ldx #>t6
        jsr ecrire
        lda a
        jsr decimal
        lda #<t7
        ldx #>t7
        jsr ecrire
        lda b
        jsr decimal
        lda #<t8
        ldx #>t8
        jsr ecrire
        lda c
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

b0:     .byte 33, 1, 2             ; 33,1 : Init Events
        .word 0
b1:     .byte 33, 3, 2             ; 33,3 : Event Available
        .word 0
a:      .byte 0
b2:     .byte 33, 4, 2             ; Flush Events, masque 2 : bit 1 = type 2, touche relâchée
        .word 2
b3:     .byte 33, 3, 2             ; 33,3
        .word 0
b:      .byte 0
b4:     .byte 33, 4, 2             ; masque 0 : tout
        .word 0
b5:     .byte 33, 3, 2             ; 33,3
        .word 0
c:      .byte 0
t6:     .text "Avant : ", 0
t7:     .text ", sans relachees : ", 0
t8:     .text ", tout vide : ", 0

; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi:  sta blk
        stx blk+1
        jsr WaitMessage
        ldy #2
        lda (blk),y                ; nombre de paramètres
        clc
        adc #3
        sta fin
        ldy #3
_copie: cpy fin
        beq _lance
        lda (blk),y
        sta ControlPort+1,y        ; octet 3 du bloc -> P0 ($FF04)...
        iny
        bra _copie
_lance: ldy #1
        lda (blk),y
        tax
        lda (blk)
        jmp appel

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Affiche le mot de 16 bits A (poids faible), X (poids fort) en décimal, sans zéros de tête.
dec16:  sta n16
        stx n16+1
        stz vu
        ldy #0                     ; puissance de 10 en cours : 10000, 1000, 100, 10
_p:     ldx #0                     ; chiffre
_s:     lda n16
        sec
        sbc p10l,y
        pha
        lda n16+1
        sbc p10h,y
        bcc _neg                   ; plus petit : chiffre trouvé
        sta n16+1
        pla
        sta n16
        inx
        bra _s
_neg:   pla
        txa
        ora vu
        beq _suiv                  ; pas de zéro de tête
        txa
        ora #'0'
        phy
        jsr WriteCharacter
        ply
        lda #1
        sta vu
_suiv:  iny
        cpy #4
        bne _p
        lda n16                    ; les unités, toujours
        ora #'0'
        jmp WriteCharacter
p10l:   .byte <10000, <1000, <100, <10
p10h:   .byte >10000, >1000, >100, >10
n16:    .word 0
vu:     .byte 0

; Note l'instant de départ (1,1 : temps en centièmes de seconde, 16 bits bas).
depart: jsr temps
        lda tl
        sta t0
        lda th
        sta t0+1
        rts

; C = 1 si au moins A (poids faible), X (poids fort) centièmes se sont écoulés depuis depart.
ecoule: sta duree
        stx duree+1
        jsr temps
        sec
        lda tl
        sbc t0
        sta tl
        lda th
        sbc t0+1
        sta th
        lda tl
        cmp duree
        lda th
        sbc duree+1
        rts

; Attend A (poids faible), X (poids fort) centièmes : le temps aux touches tapées d'arriver.
attendre:
        pha
        phx
        jsr depart
        plx
        pla
_att:   pha
        phx
        jsr ecoule
        plx
        pla
        bcc _att
        rts

temps:  jsr WaitMessage
        lda #1
        ldx #1                     ; 1,1 : temps
        jsr appel
        lda ControlPort+4
        sta tl
        lda ControlPort+5
        sta th
        rts

; La file du clavier (groupe 2) a reçu les mêmes touches : on la vide.
vider:  jsr WaitMessage
        lda #2
        ldx #1                     ; 2,1 : lire un caractère, 0 si aucun
        jsr appel
        lda ControlPort+4
        bne vider
        rts

t0:     .word 0
tl:     .byte 0
th:     .byte 0
duree:  .word 0

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
blk      = $82
diviseur: .byte 0
fin:     .byte 0
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Avant : 6, sans relachees : 3, tout vide : 0

33,5 Set Timer Trinity

Programs a timer that posts an event at regular intervals.

Inputs
BytesTypeMeaning
P0uint8timer number (0 to 3)
P1-P2uint16period in 10 ms ticks (0 stops it)

Outputs: none.

The timer event (type 8) carries its number in message. Events are only created when the queue is polled, at most one per timer per poll. The example waits for three events of timer 2, stops it, then checks that no more come. NeoBASIC has no keyword for this call.

Firmware documentation text

Programs timer Parameter:0 (0-3) to post a timer event every Parameters:1,2 ticks of 10 ms (0 stops it). Timer events are generated when the queue is polled, at most one per timer per poll.

C (llvm-mos)
/* Programmer un minuteur (groupe 33, fonction 5, Trinity). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

/* Event Manager : un EventRecord = type, message, message2, modificateurs, X, Y (8 octets). */
typedef struct { uint8_t what, message, message2, modifiers; int16_t x, y; } EventRecord;

static void p16(uint8_t i, uint16_t v) {        /* paramètre 16 bits en Pi, Pi+1 */
    ControlPort.params[i] = v & 0xFF;
    ControlPort.params[i + 1] = v >> 8;
}
static uint16_t temps(void) {                   /* 1,1 : temps en centièmes de seconde (16 bits bas) */
    libre();
    api(1, 1);
    return ControlPort.params[0] | ControlPort.params[1] << 8;
}
static void attendre(uint16_t cs) {             /* laisse le temps aux touches tapées d'arriver */
    uint16_t t0 = temps();
    while (temps() - t0 < cs)
        ;
}
static void init_events(uint16_t masque) {      /* 33,1 Init Events */
    libre(); p16(0, masque); api(33, 1);
}
static uint8_t disponibles(uint16_t masque) {   /* 33,3 Event Available */
    libre(); p16(0, masque); api(33, 3);
    return ControlPort.params[2];
}
static uint8_t suivant(EventRecord *e, uint16_t masque) {   /* 33,2 Get Next Event : 1 si un événement est rendu */
    libre(); p16(0, (uint16_t)e); p16(2, masque); api(33, 2);
    return ControlPort.params[4];
}
static void vider_clavier(void) {               /* la file du clavier (groupe 2) a reçu les mêmes touches : on la vide */
    do { libre(); api(2, 1); } while (ControlPort.params[0]);
}

static EventRecord ev;

static void minuteur(uint8_t n, uint16_t ticks) {   /* 33,5 Set Timer : ticks de 10 ms, 0 l'arrête */
    libre(); ControlPort.params[0] = n; p16(1, ticks); api(33, 5);
}

int main(void) {
    uint8_t n = 0, m = 0, apres;
    init_events(0);
    minuteur(2, 10);                    /* minuteur 2 : un événement toutes les 100 ms */
    while (n < 3)
        if (suivant(&ev, 0x80)) {       /* bit 7 : type 8, minuteur */
            n++;
            m = ev.message;             /* numéro du minuteur */
        }
    minuteur(2, 0);
    attendre(50);
    apres = disponibles(0x80);
    printf("Minuteur %u : %u evenements ; arrete : %u\n", m, n, apres);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Minuteur 2 : 3 evenements ; arrete : 0
Mad Pascal
// Programmer un minuteur (groupe 33, fonction 5, Trinity).
program ev5;
uses crt, neo6502;
type EventRecord = record what, message, message2, modifiers: byte; x, y: smallint; end;   // 8 octets

procedure appel(f: byte);                       // appel 33,f (paramètres déjà posés)
begin
  NeoSendMessage(33, f);
  NeoWaitMessage;
end;

procedure attendre(cs: cardinal);               // laisse le temps aux touches tapées d'arriver
var t0: cardinal;
begin
  t0 := NeoGetTimer;
  while NeoGetTimer - t0 < cs do ;
end;

procedure init_events(masque: word);            // 33,1 Init Events
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(1);
end;

function disponibles(masque: word): byte;       // 33,3 Event Available
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(3);
  result := NeoMessage.params[2];
end;

function suivant(e: pointer; masque: word): byte;   // 33,2 Get Next Event : 1 si un événement est rendu
begin
  NeoWaitMessage;
  wordParams[0] := word(e);
  wordParams[1] := masque;
  appel(2);
  result := NeoMessage.params[4];
end;

procedure vider_clavier;                        // la file du clavier (groupe 2) a reçu les mêmes touches : on la vide
begin
  repeat
    NeoSendMessage(2, 1);
    NeoWaitMessage;
  until NeoMessage.params[0] = 0;
end;

procedure minuteur(n: byte; ticks: word);      // 33,5 Set Timer : ticks de 10 ms, 0 l'arrête
begin
  NeoWaitMessage;
  NeoMessage.params[0] := n;
  NeoMessage.params[1] := lo(ticks);
  NeoMessage.params[2] := hi(ticks);
  appel(5);
end;

var ev: EventRecord;
    n, m, apres: byte;
begin
  n := 0;
  m := 0;
  init_events(0);
  minuteur(2, 10);                              // minuteur 2 : un événement toutes les 100 ms
  while n < 3 do
    if suivant(@ev, $80) = 1 then begin         // bit 7 : type 8, minuteur
      inc(n);
      m := ev.message;                          // numéro du minuteur
    end;
  minuteur(2, 0);
  attendre(50);
  apres := disponibles($80);
  writeln('Minuteur ', m, ' : ', n, ' evenements ; arrete : ', apres);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Minuteur 2 : 3 evenements ; arrete : 0
NeoBASIC
10 ' Programmer un minuteur (groupe 33, fonction 5, Trinity)
20 e = alloc(8) : n = 0
30 m = 0 : f = 1 : gosub 8000 : ' 33,1
40 tk = 10 : gosub 2000 : ' minuteur 2 : un evenement toutes les 100 ms
50 m = $80 : gosub 8100 : ' bit 7 : type 8, minuteur
60 if r4 = 1 then n = n + 1 : mn = peek(e + 1) : ' message : numero du minuteur
70 if n < 3 then 50
80 tk = 0 : gosub 2000
90 cs = 50 : gosub 8200
100 m = $80 : f = 3 : gosub 8000 : ap = r2
110 print "Minuteur ";mn;" : ";n;" evenements ; arrete : ";ap
120 end
2000 ' 33,5 Set Timer : minuteur 2, tk ticks de 10 ms, 0 l'arrete
2010 while peek($ff00) : wend : poke $ff04, 2 : doke $ff05, tk : f = 5 : goto 8020
8000 ' 33,f : P0-P1 = m ; resultat de P0 a P4 dans r0 a r4
8010 while peek($ff00) : wend : doke $ff04, m
8020 poke $ff01, f : poke $ff00, 33
8030 while peek($ff00) : wend
8040 r0 = peek($ff04) : r1 = peek($ff05) : r2 = peek($ff06) : r4 = peek($ff08)
8050 return
8100 ' 33,2 Get Next Event dans l'EventRecord e, masque m ; r4 = 1 si un evenement est rendu
8110 while peek($ff00) : wend : doke $ff04, e : doke $ff06, m : f = 2 : goto 8020
8200 ' laisse le temps aux touches tapees d'arriver : cs centiemes
8210 t0 = time()
8220 while time() - t0 < cs : wend
8230 return
8300 ' la file du clavier (groupe 2) a recu les memes touches : on la vide
8310 while inkey$() <> "" : wend
8320 return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Minuteur 2 : 3 evenements ; arrete : 0
65C02 assembly (64tass)
; Programmer un minuteur (groupe 33, fonction 5, Trinity).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  lda #<b0
        ldx #>b0
        jsr envoi
        lda #<b1
        ldx #>b1
        jsr envoi
_att:   lda #<b2
        ldx #>b2
        jsr envoi
        lda ControlPort+8
        beq _att
        inc n
        lda ev+1                   ; message : numéro du minuteur
        sta mn
        lda n
        cmp #3
        bne _att
        lda #<b3
        ldx #>b3
        jsr envoi
        lda #<50
        ldx #>50
        jsr attendre
        lda #<b4
        ldx #>b4
        jsr envoi
        lda ControlPort+6
        sta ap
        lda #<t5
        ldx #>t5
        jsr ecrire
        lda mn
        jsr decimal
        lda #<t6
        ldx #>t6
        jsr ecrire
        lda n
        jsr decimal
        lda #<t7
        ldx #>t7
        jsr ecrire
        lda ap
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

ev:     .fill 8                    ; EventRecord : type, message, message2, modificateurs, X, Y
n:      .byte 0
mn:     .byte 0
b0:     .byte 33, 1, 2             ; 33,1 : Init Events
        .word 0
b1:     .byte 33, 5, 3, 2          ; Set Timer : minuteur 2, toutes les 10 × 10 ms
        .word 10
b2:     .byte 33, 2, 4             ; bit 7 : type 8, minuteur
        .word ev, $80
b3:     .byte 33, 5, 3, 2          ; 0 : arrêt
        .word 0
b4:     .byte 33, 3, 2             ; 33,3 : Event Available
        .word $80
ap:     .byte 0
t5:     .text "Minuteur ", 0
t6:     .text " : ", 0
t7:     .text " evenements ; arrete : ", 0

; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi:  sta blk
        stx blk+1
        jsr WaitMessage
        ldy #2
        lda (blk),y                ; nombre de paramètres
        clc
        adc #3
        sta fin
        ldy #3
_copie: cpy fin
        beq _lance
        lda (blk),y
        sta ControlPort+1,y        ; octet 3 du bloc -> P0 ($FF04)...
        iny
        bra _copie
_lance: ldy #1
        lda (blk),y
        tax
        lda (blk)
        jmp appel

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Affiche le mot de 16 bits A (poids faible), X (poids fort) en décimal, sans zéros de tête.
dec16:  sta n16
        stx n16+1
        stz vu
        ldy #0                     ; puissance de 10 en cours : 10000, 1000, 100, 10
_p:     ldx #0                     ; chiffre
_s:     lda n16
        sec
        sbc p10l,y
        pha
        lda n16+1
        sbc p10h,y
        bcc _neg                   ; plus petit : chiffre trouvé
        sta n16+1
        pla
        sta n16
        inx
        bra _s
_neg:   pla
        txa
        ora vu
        beq _suiv                  ; pas de zéro de tête
        txa
        ora #'0'
        phy
        jsr WriteCharacter
        ply
        lda #1
        sta vu
_suiv:  iny
        cpy #4
        bne _p
        lda n16                    ; les unités, toujours
        ora #'0'
        jmp WriteCharacter
p10l:   .byte <10000, <1000, <100, <10
p10h:   .byte >10000, >1000, >100, >10
n16:    .word 0
vu:     .byte 0

; Note l'instant de départ (1,1 : temps en centièmes de seconde, 16 bits bas).
depart: jsr temps
        lda tl
        sta t0
        lda th
        sta t0+1
        rts

; C = 1 si au moins A (poids faible), X (poids fort) centièmes se sont écoulés depuis depart.
ecoule: sta duree
        stx duree+1
        jsr temps
        sec
        lda tl
        sbc t0
        sta tl
        lda th
        sbc t0+1
        sta th
        lda tl
        cmp duree
        lda th
        sbc duree+1
        rts

; Attend A (poids faible), X (poids fort) centièmes : le temps aux touches tapées d'arriver.
attendre:
        pha
        phx
        jsr depart
        plx
        pla
_att:   pha
        phx
        jsr ecoule
        plx
        pla
        bcc _att
        rts

temps:  jsr WaitMessage
        lda #1
        ldx #1                     ; 1,1 : temps
        jsr appel
        lda ControlPort+4
        sta tl
        lda ControlPort+5
        sta th
        rts

; La file du clavier (groupe 2) a reçu les mêmes touches : on la vide.
vider:  jsr WaitMessage
        lda #2
        ldx #1                     ; 2,1 : lire un caractère, 0 si aucun
        jsr appel
        lda ControlPort+4
        bne vider
        rts

t0:     .word 0
tl:     .byte 0
th:     .byte 0
duree:  .word 0

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
blk      = $82
diviseur: .byte 0
fin:     .byte 0
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Minuteur 2 : 3 evenements ; arrete : 0

33,6 Event Status Trinity

Returns the number of queued events and tells whether events were lost because the queue was full.

Inputs: none.

Outputs
BytesTypeMeaning
P0uint8number of queued events
P1uint81 if events were lost since the last call (cleared by the call)

The bench types 16 keys, that is 32 events, without the program reading the queue. It keeps 31 and reports the loss; the Trinity documentation announces a queue of 32 records. The second read shows the flag cleared. NeoBASIC has no keyword for this call.

Firmware documentation text

Returns the number of queued events in Parameter:0 and, in Parameter:1, 1 if events were lost because the queue was full since the last call (the flag is then cleared).

C (llvm-mos)
/* Savoir si des événements ont été perdus (groupe 33, fonction 6, Trinity). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>

/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
    ControlPort.function = fonction;
    ControlPort.command = groupe;
    while (ControlPort.command)        /* attendre la fin */
        ;
}
static void libre(void) {              /* attendre que la boîte aux lettres soit libre */
    while (ControlPort.command)
        ;
}

/* Event Manager : un EventRecord = type, message, message2, modificateurs, X, Y (8 octets). */
typedef struct { uint8_t what, message, message2, modifiers; int16_t x, y; } EventRecord;

static void p16(uint8_t i, uint16_t v) {        /* paramètre 16 bits en Pi, Pi+1 */
    ControlPort.params[i] = v & 0xFF;
    ControlPort.params[i + 1] = v >> 8;
}
static uint16_t temps(void) {                   /* 1,1 : temps en centièmes de seconde (16 bits bas) */
    libre();
    api(1, 1);
    return ControlPort.params[0] | ControlPort.params[1] << 8;
}
static void attendre(uint16_t cs) {             /* laisse le temps aux touches tapées d'arriver */
    uint16_t t0 = temps();
    while (temps() - t0 < cs)
        ;
}
static void init_events(uint16_t masque) {      /* 33,1 Init Events */
    libre(); p16(0, masque); api(33, 1);
}
static uint8_t disponibles(uint16_t masque) {   /* 33,3 Event Available */
    libre(); p16(0, masque); api(33, 3);
    return ControlPort.params[2];
}
static uint8_t suivant(EventRecord *e, uint16_t masque) {   /* 33,2 Get Next Event : 1 si un événement est rendu */
    libre(); p16(0, (uint16_t)e); p16(2, masque); api(33, 2);
    return ControlPort.params[4];
}
static void vider_clavier(void) {               /* la file du clavier (groupe 2) a reçu les mêmes touches : on la vide */
    do { libre(); api(2, 1); } while (ControlPort.params[0]);
}

int main(void) {
    uint8_t n1, p1, n2, p2;
    init_events(0);
    attendre(300);                      /* le banc tape 16 touches : 32 événements */
    libre(); api(33, 6);                /* Event Status */
    n1 = ControlPort.params[0];
    p1 = ControlPort.params[1];
    libre(); api(33, 6);                /* l'indicateur de perte est remis à 0 */
    n2 = ControlPort.params[0];
    p2 = ControlPort.params[1];
    init_events(0);
    vider_clavier();
    printf("En file : %u, perdus : %u\nDeuxieme lecture : %u, perdus : %u\n", n1, p1, n2, p2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
En file : 31, perdus : 1
Deuxieme lecture : 31, perdus : 0
Mad Pascal
// Savoir si des événements ont été perdus (groupe 33, fonction 6, Trinity).
program ev6;
uses crt, neo6502;
type EventRecord = record what, message, message2, modifiers: byte; x, y: smallint; end;   // 8 octets

procedure appel(f: byte);                       // appel 33,f (paramètres déjà posés)
begin
  NeoSendMessage(33, f);
  NeoWaitMessage;
end;

procedure attendre(cs: cardinal);               // laisse le temps aux touches tapées d'arriver
var t0: cardinal;
begin
  t0 := NeoGetTimer;
  while NeoGetTimer - t0 < cs do ;
end;

procedure init_events(masque: word);            // 33,1 Init Events
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(1);
end;

function disponibles(masque: word): byte;       // 33,3 Event Available
begin
  NeoWaitMessage;
  wordParams[0] := masque;
  appel(3);
  result := NeoMessage.params[2];
end;

function suivant(e: pointer; masque: word): byte;   // 33,2 Get Next Event : 1 si un événement est rendu
begin
  NeoWaitMessage;
  wordParams[0] := word(e);
  wordParams[1] := masque;
  appel(2);
  result := NeoMessage.params[4];
end;

procedure vider_clavier;                        // la file du clavier (groupe 2) a reçu les mêmes touches : on la vide
begin
  repeat
    NeoSendMessage(2, 1);
    NeoWaitMessage;
  until NeoMessage.params[0] = 0;
end;

var n1, p1, n2, p2: byte;
begin
  init_events(0);
  attendre(300);                                // le banc tape 16 touches : 32 événements
  NeoWaitMessage;
  appel(6);                                     // Event Status
  n1 := NeoMessage.params[0];
  p1 := NeoMessage.params[1];
  appel(6);                                     // l'indicateur de perte est remis à 0
  n2 := NeoMessage.params[0];
  p2 := NeoMessage.params[1];
  init_events(0);
  vider_clavier;
  writeln('En file : ', n1, ', perdus : ', p1);
  writeln('Deuxieme lecture : ', n2, ', perdus : ', p2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
En file : 31, perdus : 1
Deuxieme lecture : 31, perdus : 0
NeoBASIC
10 ' Savoir si des evenements ont ete perdus (groupe 33, fonction 6, Trinity)
20 m = 0 : f = 1 : gosub 8000 : ' 33,1
30 cs = 300 : gosub 8200 : ' le banc tape 16 touches : 32 evenements
40 f = 6 : gosub 2000 : n1 = r0 : p1 = r1 : ' Event Status
50 gosub 2000 : n2 = r0 : p2 = r1 : ' l'indicateur de perte est remis a 0
60 m = 0 : f = 1 : gosub 8000
70 gosub 8300
80 print "En file : ";n1;", perdus : ";p1
90 print "Deuxieme lecture : ";n2;", perdus : ";p2
100 end
2000 ' 33,6 sans parametre
2010 while peek($ff00) : wend : f = 6 : goto 8020
8000 ' 33,f : P0-P1 = m ; resultat de P0 a P4 dans r0 a r4
8010 while peek($ff00) : wend : doke $ff04, m
8020 poke $ff01, f : poke $ff00, 33
8030 while peek($ff00) : wend
8040 r0 = peek($ff04) : r1 = peek($ff05) : r2 = peek($ff06) : r4 = peek($ff08)
8050 return
8100 ' 33,2 Get Next Event dans l'EventRecord e, masque m ; r4 = 1 si un evenement est rendu
8110 while peek($ff00) : wend : doke $ff04, e : doke $ff06, m : f = 2 : goto 8020
8200 ' laisse le temps aux touches tapees d'arriver : cs centiemes
8210 t0 = time()
8220 while time() - t0 < cs : wend
8230 return
8300 ' la file du clavier (groupe 2) a recu les memes touches : on la vide
8310 while inkey$() <> "" : wend
8320 return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
En file : 31, perdus : 1
Deuxieme lecture : 31, perdus : 0
65C02 assembly (64tass)
; Savoir si des événements ont été perdus (groupe 33, fonction 6, Trinity).
        .cpu "65c02"
        * = $0800
ControlPort    = $FF00             ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage    = $FFF4             ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1             ; affiche le caractère de A

start:  lda #<b0
        ldx #>b0
        jsr envoi
        lda #<300
        ldx #>300
        jsr attendre               ; le banc tape 16 touches : 32 événements
        lda #<b1
        ldx #>b1
        jsr envoi
        lda ControlPort+4
        sta n1
        lda ControlPort+5
        sta p1
        lda #<b2
        ldx #>b2
        jsr envoi
        lda ControlPort+4
        sta n2
        lda ControlPort+5
        sta p2
        lda #<b3
        ldx #>b3
        jsr envoi
        jsr vider
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda n1
        jsr decimal
        lda #<t5
        ldx #>t5
        jsr ecrire
        lda p1
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t6
        ldx #>t6
        jsr ecrire
        lda n2
        jsr decimal
        lda #<t7
        ldx #>t7
        jsr ecrire
        lda p2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

n1:     .byte 0
p1:     .byte 0
n2:     .byte 0
p2:     .byte 0
b0:     .byte 33, 1, 2             ; 33,1 : Init Events
        .word 0
b1:     .byte 33, 6, 0             ; Event Status
b2:     .byte 33, 6, 0             ; l'indicateur de perte est remis à 0
b3:     .byte 33, 1, 2             ; 33,1
        .word 0
t4:     .text "En file : ", 0
t5:     .text ", perdus : ", 0
t6:     .text "Deuxieme lecture : ", 0
t7:     .text ", perdus : ", 0

; Appel décrit par un bloc dont l'adresse est dans A (poids faible) et X (poids fort) :
; groupe, fonction, nombre de paramètres, puis les paramètres (P0, P1...).
envoi:  sta blk
        stx blk+1
        jsr WaitMessage
        ldy #2
        lda (blk),y                ; nombre de paramètres
        clc
        adc #3
        sta fin
        ldy #3
_copie: cpy fin
        beq _lance
        lda (blk),y
        sta ControlPort+1,y        ; octet 3 du bloc -> P0 ($FF04)...
        iny
        bra _copie
_lance: ldy #1
        lda (blk),y
        tax
        lda (blk)
        jmp appel

; Affiche la chaîne terminée par 0 dont l'adresse est dans A (poids faible) et X (poids fort).
ecrire: sta ptr
        stx ptr+1
        ldy #0
_e:     lda (ptr),y
        beq _f
        jsr WriteCharacter
        iny
        bne _e
_f:     rts

; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
        phx
        phy
        ldx #0
        ldy #100
        jsr _chiffre
        ldy #10
        jsr _chiffre
        ora #'0'
        jsr WriteCharacter
        ply
        plx
        rts
_chiffre:
        sty diviseur
        ldy #0
_div:   cmp diviseur
        bcc _fini
        sbc diviseur
        iny
        bne _div
_fini:  pha
        tya
        bne _ecrit
        cpx #0
        beq _saute
_ecrit: ora #'0'
        jsr WriteCharacter
        ldx #1
_saute: pla
        rts

; Affiche le mot de 16 bits A (poids faible), X (poids fort) en décimal, sans zéros de tête.
dec16:  sta n16
        stx n16+1
        stz vu
        ldy #0                     ; puissance de 10 en cours : 10000, 1000, 100, 10
_p:     ldx #0                     ; chiffre
_s:     lda n16
        sec
        sbc p10l,y
        pha
        lda n16+1
        sbc p10h,y
        bcc _neg                   ; plus petit : chiffre trouvé
        sta n16+1
        pla
        sta n16
        inx
        bra _s
_neg:   pla
        txa
        ora vu
        beq _suiv                  ; pas de zéro de tête
        txa
        ora #'0'
        phy
        jsr WriteCharacter
        ply
        lda #1
        sta vu
_suiv:  iny
        cpy #4
        bne _p
        lda n16                    ; les unités, toujours
        ora #'0'
        jmp WriteCharacter
p10l:   .byte <10000, <1000, <100, <10
p10h:   .byte >10000, >1000, >100, >10
n16:    .word 0
vu:     .byte 0

; Note l'instant de départ (1,1 : temps en centièmes de seconde, 16 bits bas).
depart: jsr temps
        lda tl
        sta t0
        lda th
        sta t0+1
        rts

; C = 1 si au moins A (poids faible), X (poids fort) centièmes se sont écoulés depuis depart.
ecoule: sta duree
        stx duree+1
        jsr temps
        sec
        lda tl
        sbc t0
        sta tl
        lda th
        sbc t0+1
        sta th
        lda tl
        cmp duree
        lda th
        sbc duree+1
        rts

; Attend A (poids faible), X (poids fort) centièmes : le temps aux touches tapées d'arriver.
attendre:
        pha
        phx
        jsr depart
        plx
        pla
_att:   pha
        phx
        jsr ecoule
        plx
        pla
        bcc _att
        rts

temps:  jsr WaitMessage
        lda #1
        ldx #1                     ; 1,1 : temps
        jsr appel
        lda ControlPort+4
        sta tl
        lda ControlPort+5
        sta th
        rts

; La file du clavier (groupe 2) a reçu les mêmes touches : on la vide.
vider:  jsr WaitMessage
        lda #2
        ldx #1                     ; 2,1 : lire un caractère, 0 si aucun
        jsr appel
        lda ControlPort+4
        bne vider
        rts

t0:     .word 0
tl:     .byte 0
th:     .byte 0
duree:  .word 0

; Appel : A = groupe, X = fonction (les paramètres sont déjà en place).
appel:  stx ControlPort+1
        sta ControlPort            ; écrit en dernier : lance l'appel
        jmp WaitMessage

ptr      = $80
blk      = $82
diviseur: .byte 0
fin:     .byte 0
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
En file : 31, perdus : 1
Deuxieme lecture : 31, perdus : 0

← The Neo6502 API