Tachibana
Language: English

Group 36 — Control Manager Trinity

36,1 New Control · 36,2 Dispose Control · 36,3 Draw Controls · 36,4 Draw Control · 36,5 Set Control Value · 36,6 Get Control Value · 36,7 Set Control Flags · 36,8 Find Control · 36,9 Track Control · 36,10 Key Control

The control manager of the Trinity toolbox puts buttons, check boxes, radio buttons, scroll bars and text fields in windows (group 34): 24 at most.

The examples open a window with frame 20, 20, 220, 140 (content from (21, 31) to (219, 139)) and put their controls in it: a control Rect is relative to the content, so the button put at (10, 10) is at (31, 41) on screen. The mouse is simulated by calls to 36,9 at the wanted coordinates. The toolbox is reset with 32,19 at the start and at the end.

36,1 New Control Trinity

Creates a control in a window and draws it at once.

Inputs
BytesTypeMeaning
P0uint8kind: 1 button, 2 check box, 3 radio button, 4 scroll bar, 5 text field
P1uint8window
P2-P3addraddress of the Rect, relative to the window content
P4-P5addraddress of the length-prefixed title, or of the buffer of a text field (read in place)
P6-P7uint16maximum of a scroll bar, or maximum length of a text field
Outputs
BytesTypeMeaning
P0uint8control number (1 to 24)
erreuruint81 bad parameters, 2 none left

A scroll bar is vertical when it is higher than wide. Radio buttons are not grouped: the program clears the others. When handling the update event of a window, 36,3 redraws all its controls. On the bench, the 25th control is refused (error 2). NeoBASIC has no keyword for this call.

Firmware documentation text

Toolbox (Trinity, T-12, ADR-01 of the fork). Creates a control of kind Parameter:0 (1 button, 2 check box, 3 radio button, 4 scroll bar - vertical when higher than wide -, 5 text field) in window Parameter:1, with the Rect at Parameters:2,3 relative to the content of the window, the length-prefixed title (or, for a text field, the text buffer) at Parameters:4,5 read in place, and Parameters:6,7 = maximum value of a scroll bar or maximum length of a text field ; returns the control number (1-24) in Parameter:0 (error 1 bad parameters, 2 none left). The control is drawn at once ; redraw all the controls of a window with Draw Controls when handling its update event. Radio buttons are not grouped : the program clears the others.

C (llvm-mos)
/* Créer un contrôle (groupe 36, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static Rect rb = {10, 10, 70, 24}, rc = {10, 30, 110, 42}, rs = {180, 0, 192, 100};
static const char ok[] = "\002OK", option[] = "\006Option";

int main(void) {
    uint8_t b, c, s, e;
    ouvrir();
    b = controle(1, &rb, ok, 0);                /* bouton */
    c = controle(2, &rc, option, 0);            /* case à cocher */
    s = controle(4, &rs, titre, 50);            /* barre de défilement verticale (plus haute que large), maximum 50 */
    controle(6, &rb, ok, 0);                    /* type inconnu : refusé */
    e = ControlPort.error;
    reinit();
    printf("Bouton %u, case %u, barre %u ; type 6 : erreur %u\n", b, c, s, e);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton 1, case 2, barre 3 ; type 6 : erreur 1
Mad Pascal
// Créer un contrôle (groupe 36, fonction 1, Trinity).
program ct1;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

const ok: array[0..2] of byte = (2, ord('O'), ord('K'));
      option: array[0..6] of byte = (6, ord('O'), ord('p'), ord('t'), ord('i'), ord('o'), ord('n'));
var rb, rc, rs: Rect;
    b, c, s, e: byte;
begin
  poser(rb, 10, 10, 70, 24); poser(rc, 10, 30, 110, 42); poser(rs, 180, 0, 192, 100);
  ouvrir;
  b := controle(1, @rb, @ok, 0);                // bouton
  c := controle(2, @rc, @option, 0);            // case à cocher
  s := controle(4, @rs, @titre, 50);            // barre de défilement verticale (plus haute que large), maximum 50
  controle(6, @rb, @ok, 0);                     // type inconnu : refusé
  e := NeoMessage.error;
  reinit;
  writeln('Bouton ', b, ', case ', c, ', barre ', s, ' ; type 6 : erreur ', e);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton 1, case 2, barre 3 ; type 6 : erreur 1
NeoBASIC
10 ' Creer un controle (groupe 36, fonction 1, Trinity)
20 gosub 7000
30 g = 10 : h = 10 : d = 70 : b = 24 : gosub 8000 : rb = q : s$ = "OK" : gosub 7900 : ok = ch
40 ty = 1 : mx = 0 : gosub 7100 : bt = cn : ' bouton
50 g = 10 : h = 30 : d = 110 : b = 42 : gosub 8000 : s$ = "Option" : gosub 7900
60 ty = 2 : gosub 7100 : ca = cn : ' case a cocher
70 g = 180 : h = 0 : d = 192 : b = 100 : gosub 8000 : ch = tf
80 ty = 4 : mx = 50 : gosub 7100 : ba = cn : ' barre de defilement verticale (plus haute que large), maximum 50
90 q = rb : ch = ok : ty = 6 : mx = 0 : gosub 7100 : e6 = e : ' type inconnu : refuse
100 gosub 8300
110 print "Bouton ";bt;", case ";ca;", barre ";ba;" ; type 6 : erreur ";e6
120 end
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton 1, case 2, barre 3 ; type 6 : erreur 1
65C02 assembly (64tass)
; Créer un contrôle (groupe 36, 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:  jsr ouvrir
        lda #<cb
        ldx #>cb
        jsr controle
        sta b
        lda #<cc
        ldx #>cc
        jsr controle
        sta c
        lda #<cs
        ldx #>cs
        jsr controle
        sta s
        lda #<c6
        ldx #>c6
        jsr controle
        lda ControlPort+2
        sta e
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda b
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda c
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda s
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

rb:     .word 10, 10, 70, 24
rc:     .word 10, 30, 110, 42
rs:     .word 180, 0, 192, 100
ok:     .byte 2
        .text "OK"
option: .byte 6
        .text "Option"
cb:     .byte 1                  ; bouton
        .word rb, ok, 0
b:      .byte 0
cc:     .byte 2                  ; case à cocher
        .word rc, option, 0
c:      .byte 0
cs:     .byte 4                  ; barre verticale (plus haute que large), maximum 50
        .word rs, titre, 50
s:      .byte 0
c6:     .byte 6                  ; type inconnu : refusé
        .word rb, ok, 0
e:      .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "Bouton ", 0
t2:     .text ", case ", 0
t3:     .text ", barre ", 0
t4:     .text " ; type 6 : erreur ", 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Bouton 1, case 2, barre 3 ; type 6 : erreur 1

36,2 Dispose Control Trinity

Erases a control and frees its number.

Inputs
BytesTypeMeaning
P0uint8control number

Outputs: none.

Disposing a window frees its controls as well. The freed number is taken by the next control. NeoBASIC has no keyword for this call.

Firmware documentation text

Erases and frees control Parameter:0. Disposing a window frees its controls.

C (llvm-mos)
/* Supprimer un contrôle (groupe 36, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static Rect rb = {10, 10, 70, 24}, rc = {10, 30, 110, 42};
static const char ok[] = "\002OK", option[] = "\006Option";

int main(void) {
    uint8_t b, t, n;
    ouvrir();
    b = controle(1, &rb, ok, 0);
    controle(2, &rc, option, 0);
    libre(); ControlPort.params[0] = b; api(36, 2);   /* Dispose Control : effacé, numéro libéré */
    libre(); p16(0, 40); p16(2, 45); api(36, 8);      /* 36,8 : plus rien à la place du bouton */
    t = ControlPort.params[4];
    n = controle(1, &rb, ok, 0);
    reinit();
    printf("Apres suppression : en (40, 45) : %u, nouveau : %u\n", t, n);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Apres suppression : en (40, 45) : 0, nouveau : 1
Mad Pascal
// Supprimer un contrôle (groupe 36, fonction 2, Trinity).
program ct2;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

const ok: array[0..2] of byte = (2, ord('O'), ord('K'));
      option: array[0..6] of byte = (6, ord('O'), ord('p'), ord('t'), ord('i'), ord('o'), ord('n'));
var rb, rc: Rect;
    b, t, n: byte;
begin
  poser(rb, 10, 10, 70, 24); poser(rc, 10, 30, 110, 42);
  ouvrir;
  b := controle(1, @rb, @ok, 0);
  controle(2, @rc, @option, 0);
  NeoWaitMessage; NeoMessage.params[0] := b; appel(36, 2);   // Dispose Control : effacé, numéro libéré
  wordParams[0] := 40; wordParams[1] := 45; appel(36, 8);   // 36,8 : plus rien à la place du bouton
  t := NeoMessage.params[4];
  n := controle(1, @rb, @ok, 0);
  reinit;
  writeln('Apres suppression : en (40, 45) : ', t, ', nouveau : ', n);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Apres suppression : en (40, 45) : 0, nouveau : 1
NeoBASIC
10 ' Supprimer un controle (groupe 36, fonction 2, Trinity)
20 gosub 7000
30 g = 10 : h = 10 : d = 70 : b = 24 : gosub 8000 : rb = q : s$ = "OK" : gosub 7900 : ok = ch
40 ty = 1 : mx = 0 : gosub 7100 : bt = cn
50 g = 10 : h = 30 : d = 110 : b = 42 : gosub 8000 : s$ = "Option" : gosub 7900 : ty = 2 : gosub 7100
60 while peek($ff00) : wend : poke $ff04, bt : f = 2 : gosub 8110 : ' Dispose Control : efface, numero libere
70 while peek($ff00) : wend : doke $ff04, 40 : doke $ff06, 45 : f = 8 : gosub 8110 : t = peek($ff08) : ' 36,8 : plus rien
80 q = rb : ch = ok : ty = 1 : gosub 7100 : n = cn
90 gosub 8300
100 print "Apres suppression : en (40, 45) : ";t;", nouveau : ";n
110 end
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Apres suppression : en (40, 45) : 0, nouveau : 1
65C02 assembly (64tass)
; Supprimer un contrôle (groupe 36, 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:  jsr ouvrir
        lda #<cb
        ldx #>cb
        jsr controle
        sta b
        lda #<cc
        ldx #>cc
        jsr controle
        jsr WaitMessage
        lda b
        sta ControlPort+4
        lda #36
        ldx #2                  ; Dispose Control : effacé, numéro libéré
        jsr appel
        lda #<b0
        ldx #>b0
        jsr envoi
        lda ControlPort+8
        sta t
        lda #<cb
        ldx #>cb
        jsr controle
        sta n
        lda #<b1
        ldx #>b1
        jsr envoi
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda t
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda n
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

rb:     .word 10, 10, 70, 24
rc:     .word 10, 30, 110, 42
ok:     .byte 2
        .text "OK"
option: .byte 6
        .text "Option"
cb:     .byte 1                  ; bouton
        .word rb, ok, 0
b:      .byte 0
cc:     .byte 2                  ; case à cocher
        .word rc, option, 0
b0:     .byte 36, 8, 4             ; 36,8 : plus rien à la place du bouton
        .word 40, 45
t:      .byte 0
n:      .byte 0
b1:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t2:     .text "Apres suppression : en (40, 45) : ", 0
t3:     .text ", nouveau : ", 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Apres suppression : en (40, 45) : 0, nouveau : 1

36,3 Draw Controls Trinity

Draws every control of a window.

Inputs
BytesTypeMeaning
P0uint8window

Outputs: none.

To call after 34,12 in the update handler. The example paints the content in colour 4: the frames of both buttons (white, 15) come back. NeoBASIC has no keyword for this call.

Firmware documentation text

Draws every control of window Parameter:0 (to call after Set Port in the update handler).

C (llvm-mos)
/* Redessiner les contrôles d'une fenêtre (groupe 36, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static uint8_t pixel(int16_t x, int16_t y) {   /* 5,33 : couleur du fond en (x, y) */
    libre();
    p16(0, x);
    p16(2, y);
    api(5, 33);
    return ControlPort.params[0];
}
static Rect ecran = {0, 0, 320, 240};
static void barbouiller(void) {                 /* le programme peint tout le contenu en couleur 4 */
    libre(); ControlPort.params[0] = fenetre; api(34, 12);   /* 34,12 Set Port */
    libre(); ControlPort.params[0] = 4; api(32, 4);
    libre(); p16(0, (uint16_t)&ecran); api(32, 8);
}

static Rect r1 = {10, 10, 70, 24}, r2 = {80, 10, 140, 24};
static const char ok[] = "\002OK", non[] = "\003Non";

int main(void) {
    uint8_t a1, a2, b1, b2;
    putchar(12);                                /* écran effacé : on relit des pixels */
    ouvrir();
    controle(1, &r1, ok, 0);                    /* deux boutons, cadres en (31, 41) et (101, 41) */
    controle(1, &r2, non, 0);
    barbouiller();
    a1 = pixel(31, 41); a2 = pixel(101, 41);
    libre(); ControlPort.params[0] = fenetre; api(36, 3);   /* Draw Controls */
    b1 = pixel(31, 41); b2 = pixel(101, 41);
    reinit();
    printf("Cadres peints : %u %u, redessines : %u %u\n", a1, a2, b1, b2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Cadres peints : 4 4, redessines : 15 15
Mad Pascal
// Redessiner les contrôles d'une fenêtre (groupe 36, fonction 3, Trinity).
program ct3;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

var ecran: Rect;

procedure barbouiller;                          // le programme peint tout le contenu en couleur 4
begin
  poser(ecran, 0, 0, 320, 240);
  NeoWaitMessage; NeoMessage.params[0] := fenetre; appel(34, 12);   // 34,12 Set Port
  NeoMessage.params[0] := 4; appel(32, 4);
  wordParams[0] := word(@ecran); appel(32, 8);
end;

const ok: array[0..2] of byte = (2, ord('O'), ord('K'));
      non: array[0..3] of byte = (3, ord('N'), ord('o'), ord('n'));
var r1, r2: Rect;
    a1, a2, b1, b2: byte;
begin
  write(chr(12));                               // écran effacé : on relit des pixels
  poser(r1, 10, 10, 70, 24); poser(r2, 80, 10, 140, 24);
  ouvrir;
  controle(1, @r1, @ok, 0);                     // deux boutons, cadres en (31, 41) et (101, 41)
  controle(1, @r2, @non, 0);
  barbouiller;
  a1 := NeoGetPixel(31, 41); a2 := NeoGetPixel(101, 41);
  NeoWaitMessage; NeoMessage.params[0] := fenetre; appel(36, 3);   // Draw Controls
  b1 := NeoGetPixel(31, 41); b2 := NeoGetPixel(101, 41);
  reinit;
  writeln('Cadres peints : ', a1, ' ', a2, ', redessines : ', b1, ' ', b2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Cadres peints : 4 4, redessines : 15 15
NeoBASIC
10 ' Redessiner les controles d'une fenetre (groupe 36, fonction 3, Trinity)
20 cls : ' ecran efface : on relit des pixels
30 gosub 7000
40 g = 10 : h = 10 : d = 70 : b = 24 : gosub 8000 : s$ = "OK" : gosub 7900 : ty = 1 : mx = 0 : gosub 7100
50 g = 80 : d = 140 : gosub 8000 : s$ = "Non" : gosub 7900 : gosub 7100 : ' cadres en (31, 41) et (101, 41)
60 gosub 2000
70 a1 = point(31, 41) : a2 = point(101, 41)
80 while peek($ff00) : wend : poke $ff04, fw : f = 3 : gosub 8110 : ' Draw Controls
90 b1 = point(31, 41) : b2 = point(101, 41)
100 gosub 8300
110 print "Cadres peints : ";a1;" ";a2;", redessines : ";b1;" ";b2
120 end
2000 ' le programme peint tout le contenu en couleur 4
2010 ec = alloc(8) : doke ec, 0 : doke ec + 2, 0 : doke ec + 4, 320 : doke ec + 6, 240
2020 while peek($ff00) : wend : poke $ff04, fw : poke $ff01, 12 : poke $ff00, 34 : ' 34,12 Set Port
2030 while peek($ff00) : wend : poke $ff04, 4 : poke $ff01, 4 : poke $ff00, 32
2040 while peek($ff00) : wend : doke $ff04, ec : poke $ff01, 8 : poke $ff00, 32
2050 while peek($ff00) : wend : return
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Cadres peints : 4 4, redessines : 15 15
65C02 assembly (64tass)
; Redessiner les contrôles d'une fenêtre (groupe 36, 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 #12                    ; écran effacé : on relit des pixels
        jsr WriteCharacter
        jsr ouvrir
        lda #<c1
        ldx #>c1
        jsr controle
        lda #<c2
        ldx #>c2
        jsr controle
        jsr barbouiller
        lda #31
        ldy #41
        jsr pixel
        sta a1
        lda #101
        ldy #41
        jsr pixel
        sta a2
        jsr WaitMessage
        lda fen
        sta ControlPort+4
        lda #36
        ldx #3                     ; Draw Controls
        jsr appel
        lda #31
        ldy #41
        jsr pixel
        sta b1
        lda #101
        ldy #41
        jsr pixel
        sta b2
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda a1
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda a2
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda b1
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda b2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

; Couleur du fond (5,33) en (A, Y), X et Y inférieurs à 256, rendue dans A.
pixel:  pha
        jsr WaitMessage
        pla
        sta ControlPort+4          ; P0-P1 : X
        stz ControlPort+5
        sty ControlPort+6          ; P2-P3 : Y
        stz ControlPort+7
        lda #5
        ldx #33                    ; 5,33 : Read Pixel
        jsr appel
        lda ControlPort+4
        rts

r1:     .word 10, 10, 70, 24
r2:     .word 80, 10, 140, 24
ok:     .byte 2
        .text "OK"
non:    .byte 3
        .text "Non"
c1:     .byte 1                  ; deux boutons, cadres en (31, 41)...
        .word r1, ok, 0
c2:     .byte 1                  ; ... et (101, 41)
        .word r2, non, 0
a1:     .byte 0
a2:     .byte 0
b1:     .byte 0
b2:     .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "Cadres peints : ", 0
t2:     .text " ", 0
t3:     .text ", redessines : ", 0
t4:     .text " ", 0

; Le programme peint tout le contenu en couleur 4.
barbouiller:
        jsr WaitMessage
        lda fen
        sta ControlPort+4
        lda #34
        ldx #12                    ; 34,12 Set Port
        jsr appel
        lda #<coul
        ldx #>coul
        jsr envoi
        lda #<peint
        ldx #>peint
        jsr envoi
        rts
coul:   .byte 32, 4, 1, 4
peint:  .byte 32, 8, 2
        .word ecran
ecran:  .word 0, 0, 320, 240

; 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Cadres peints : 4 4, redessines : 15 15

36,4 Draw Control Trinity

Redraws a control.

Inputs
BytesTypeMeaning
P0uint8control number

Outputs: none.

The button is redrawn entirely: white frame (15), black inside (0). NeoBASIC has no keyword for this call.

Firmware documentation text

Redraws control Parameter:0.

C (llvm-mos)
/* Redessiner un contrôle (groupe 36, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static uint8_t pixel(int16_t x, int16_t y) {   /* 5,33 : couleur du fond en (x, y) */
    libre();
    p16(0, x);
    p16(2, y);
    api(5, 33);
    return ControlPort.params[0];
}
static Rect ecran = {0, 0, 320, 240};
static void barbouiller(void) {                 /* le programme peint tout le contenu en couleur 4 */
    libre(); ControlPort.params[0] = fenetre; api(34, 12);   /* 34,12 Set Port */
    libre(); ControlPort.params[0] = 4; api(32, 4);
    libre(); p16(0, (uint16_t)&ecran); api(32, 8);
}

static Rect rb = {10, 10, 70, 24};
static const char ok[] = "\002OK";

int main(void) {
    uint8_t b, c1, i1, c2, i2;
    putchar(12);
    ouvrir();
    b = controle(1, &rb, ok, 0);
    barbouiller();
    c1 = pixel(31, 41); i1 = pixel(35, 43);     /* cadre et intérieur du bouton */
    libre(); ControlPort.params[0] = b; api(36, 4);   /* Draw Control */
    c2 = pixel(31, 41); i2 = pixel(35, 43);
    reinit();
    printf("Peint : %u, %u ; redessine : cadre %u, dedans %u\n", c1, i1, c2, i2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Peint : 4, 4 ; redessine : cadre 15, dedans 0
Mad Pascal
// Redessiner un contrôle (groupe 36, fonction 4, Trinity).
program ct4;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

var ecran: Rect;

procedure barbouiller;                          // le programme peint tout le contenu en couleur 4
begin
  poser(ecran, 0, 0, 320, 240);
  NeoWaitMessage; NeoMessage.params[0] := fenetre; appel(34, 12);   // 34,12 Set Port
  NeoMessage.params[0] := 4; appel(32, 4);
  wordParams[0] := word(@ecran); appel(32, 8);
end;

const ok: array[0..2] of byte = (2, ord('O'), ord('K'));
var rb: Rect;
    b, c1, i1, c2, i2: byte;
begin
  write(chr(12));
  poser(rb, 10, 10, 70, 24);
  ouvrir;
  b := controle(1, @rb, @ok, 0);
  barbouiller;
  c1 := NeoGetPixel(31, 41); i1 := NeoGetPixel(35, 43);   // cadre et intérieur du bouton
  NeoWaitMessage; NeoMessage.params[0] := b; appel(36, 4);   // Draw Control
  c2 := NeoGetPixel(31, 41); i2 := NeoGetPixel(35, 43);
  reinit;
  writeln('Peint : ', c1, ', ', i1, ' ; redessine : cadre ', c2, ', dedans ', i2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Peint : 4, 4 ; redessine : cadre 15, dedans 0
NeoBASIC
10 ' Redessiner un controle (groupe 36, fonction 4, Trinity)
20 cls
30 gosub 7000
40 g = 10 : h = 10 : d = 70 : b = 24 : gosub 8000 : s$ = "OK" : gosub 7900 : ty = 1 : mx = 0 : gosub 7100 : bt = cn
50 gosub 2000
60 c1 = point(31, 41) : i1 = point(35, 43) : ' cadre et interieur du bouton
70 while peek($ff00) : wend : poke $ff04, bt : f = 4 : gosub 8110 : ' Draw Control
80 c2 = point(31, 41) : i2 = point(35, 43)
90 gosub 8300
100 print "Peint : ";c1;", ";i1;" ; redessine : cadre ";c2;", dedans ";i2
110 end
2000 ' le programme peint tout le contenu en couleur 4
2010 ec = alloc(8) : doke ec, 0 : doke ec + 2, 0 : doke ec + 4, 320 : doke ec + 6, 240
2020 while peek($ff00) : wend : poke $ff04, fw : poke $ff01, 12 : poke $ff00, 34 : ' 34,12 Set Port
2030 while peek($ff00) : wend : poke $ff04, 4 : poke $ff01, 4 : poke $ff00, 32
2040 while peek($ff00) : wend : doke $ff04, ec : poke $ff01, 8 : poke $ff00, 32
2050 while peek($ff00) : wend : return
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Peint : 4, 4 ; redessine : cadre 15, dedans 0
65C02 assembly (64tass)
; Redessiner un contrôle (groupe 36, 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 #12                    ; écran effacé : on relit des pixels
        jsr WriteCharacter
        jsr ouvrir
        lda #<cb
        ldx #>cb
        jsr controle
        sta b
        jsr barbouiller
        lda #31
        ldy #41
        jsr pixel
        sta c1
        lda #35
        ldy #43
        jsr pixel
        sta i1
                                   ; cadre et intérieur du bouton
        jsr WaitMessage
        lda b
        sta ControlPort+4
        lda #36
        ldx #4                  ; Draw Control
        jsr appel
        lda #31
        ldy #41
        jsr pixel
        sta c2
        lda #35
        ldy #43
        jsr pixel
        sta i2
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda c1
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda i1
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda c2
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda i2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

; Couleur du fond (5,33) en (A, Y), X et Y inférieurs à 256, rendue dans A.
pixel:  pha
        jsr WaitMessage
        pla
        sta ControlPort+4          ; P0-P1 : X
        stz ControlPort+5
        sty ControlPort+6          ; P2-P3 : Y
        stz ControlPort+7
        lda #5
        ldx #33                    ; 5,33 : Read Pixel
        jsr appel
        lda ControlPort+4
        rts

rb:     .word 10, 10, 70, 24
ok:     .byte 2
        .text "OK"
cb:     .byte 1                  ; bouton
        .word rb, ok, 0
b:      .byte 0
c1:     .byte 0
i1:     .byte 0
c2:     .byte 0
i2:     .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "Peint : ", 0
t2:     .text ", ", 0
t3:     .text " ; redessine : cadre ", 0
t4:     .text ", dedans ", 0

; Le programme peint tout le contenu en couleur 4.
barbouiller:
        jsr WaitMessage
        lda fen
        sta ControlPort+4
        lda #34
        ldx #12                    ; 34,12 Set Port
        jsr appel
        lda #<coul
        ldx #>coul
        jsr envoi
        lda #<peint
        ldx #>peint
        jsr envoi
        rts
coul:   .byte 32, 4, 1, 4
peint:  .byte 32, 8, 2
        .word ecran
ecran:  .word 0, 0, 320, 240

; 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Peint : 4, 4 ; redessine : cadre 15, dedans 0

36,5 Set Control Value Trinity

Sets the value of a control and redraws it.

Inputs
BytesTypeMeaning
P0uint8control number
P1-P2uint16value: 0 or 1 for boxes, 0 to the maximum for scroll bars

Outputs: none.

On the bench, a value past the maximum of a scroll bar is brought back to the maximum, without error. NeoBASIC has no keyword for this call.

Firmware documentation text

Sets the value of control Parameter:0 to Parameters:1,2 (0/1 for boxes, 0..max for scroll bars) and redraws it.

C (llvm-mos)
/* Donner une valeur à un contrôle (groupe 36, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static Rect rc = {10, 30, 110, 42}, rs = {180, 0, 192, 100};
static const char option[] = "\006Option";

static uint8_t donner(uint8_t c, uint16_t v) {  /* Set Control Value */
    libre();
    ControlPort.params[0] = c;
    p16(1, v);
    api(36, 5);
    return ControlPort.error;
}

int main(void) {
    uint8_t c, s, e;
    uint16_t vc, v1, v2;
    ouvrir();
    c = controle(2, &rc, option, 0);
    s = controle(4, &rs, titre, 50);
    donner(c, 1);
    vc = valeur(c);
    donner(s, 25);
    v1 = valeur(s);
    e = donner(s, 99);                          /* au-delà du maximum : ramené à 50 */
    v2 = valeur(s);
    reinit();
    printf("Case : %u ; barre : %u, puis 99 -> %u (erreur %u)\n", vc, v1, v2, e);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Case : 1 ; barre : 25, puis 99 -> 50 (erreur 0)
Mad Pascal
// Donner une valeur à un contrôle (groupe 36, fonction 5, Trinity).
program ct5;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

const option: array[0..6] of byte = (6, ord('O'), ord('p'), ord('t'), ord('i'), ord('o'), ord('n'));

function donner(c: byte; v: word): byte;        // Set Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  p16(1, v);
  appel(36, 5);
  result := NeoMessage.error;
end;

var rc, rs: Rect;
    c, s, e: byte;
    vc, v1, v2: word;
begin
  poser(rc, 10, 30, 110, 42); poser(rs, 180, 0, 192, 100);
  ouvrir;
  c := controle(2, @rc, @option, 0);
  s := controle(4, @rs, @titre, 50);
  donner(c, 1);
  vc := valeur(c);
  donner(s, 25);
  v1 := valeur(s);
  e := donner(s, 99);                           // au-delà du maximum : ramené à 50
  v2 := valeur(s);
  reinit;
  writeln('Case : ', vc, ' ; barre : ', v1, ', puis 99 -> ', v2, ' (erreur ', e, ')');
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Case : 1 ; barre : 25, puis 99 -> 50 (erreur 0)
NeoBASIC
10 ' Donner une valeur a un controle (groupe 36, fonction 5, Trinity)
20 gosub 7000
30 g = 10 : h = 30 : d = 110 : b = 42 : gosub 8000 : s$ = "Option" : gosub 7900 : ty = 2 : mx = 0 : gosub 7100 : ca = cn
40 g = 180 : h = 0 : d = 192 : b = 100 : gosub 8000 : ch = tf : ty = 4 : mx = 50 : gosub 7100 : ba = cn
50 c = ca : vv = 1 : gosub 2000 : c = ca : gosub 7200 : vc = v
60 c = ba : vv = 25 : gosub 2000 : gosub 7200 : v1 = v
70 vv = 99 : gosub 2000 : e9 = e : gosub 7200 : v2 = v : ' au-dela du maximum : ramene a 50
80 gosub 8300
90 print "Case : ";vc;" ; barre : ";v1;", puis 99 -> ";v2;" (erreur ";e9;")"
100 end
2000 ' Set Control Value : controle c, valeur vv
2010 while peek($ff00) : wend : poke $ff04, c : doke $ff05, vv : f = 5 : goto 8110
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Case : 1 ; barre : 25, puis 99 -> 50 (erreur 0)
65C02 assembly (64tass)
; Donner une valeur à un contrôle (groupe 36, 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:  jsr ouvrir
        lda #<cc
        ldx #>cc
        jsr controle
        sta c
        lda #<cs
        ldx #>cs
        jsr controle
        sta s
        jsr WaitMessage
        lda c
        sta ControlPort+4
        lda #1
        sta ControlPort+5
        stz ControlPort+6
        lda #36
        ldx #5                  ; Set Control Value
        jsr appel
        lda c
        jsr valeur
        sta vc
        jsr WaitMessage
        lda s
        sta ControlPort+4
        lda #25
        sta ControlPort+5
        stz ControlPort+6
        lda #36
        ldx #5                  ; Set Control Value
        jsr appel
        lda s
        jsr valeur
        sta v1
        jsr WaitMessage
        lda s
        sta ControlPort+4
        lda #99
        sta ControlPort+5
        stz ControlPort+6
        lda #36
        ldx #5                  ; au-delà du maximum : ramené à 50
        jsr appel
        lda ControlPort+2
        sta e
        lda s
        jsr valeur
        sta v2
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda vc
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda v1
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda v2
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda e
        jsr decimal
        lda #<t5
        ldx #>t5
        jsr ecrire
        lda #13
        jsr WriteCharacter
        rts

rc:     .word 10, 30, 110, 42
rs:     .word 180, 0, 192, 100
option: .byte 6
        .text "Option"
cc:     .byte 2                  ; case à cocher
        .word rc, option, 0
c:      .byte 0
cs:     .byte 4                  ; barre, maximum 50
        .word rs, titre, 50
s:      .byte 0
vc:     .byte 0
v1:     .byte 0
e:      .byte 0
v2:     .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "Case : ", 0
t2:     .text " ; barre : ", 0
t3:     .text ", puis 99 -> ", 0
t4:     .text " (erreur ", 0
t5:     .text ")", 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Case : 1 ; barre : 25, puis 99 -> 50 (erreur 0)

36,6 Get Control Value Trinity

Returns the value of a control.

Inputs
BytesTypeMeaning
P0uint8control number
Outputs
BytesTypeMeaning
P1-P2uint16value

A click (36,9) toggles the check box each time; the radio button goes to 1 and stays there. NeoBASIC has no keyword for this call.

Firmware documentation text

Returns the value of control Parameter:0 in Parameters:1,2.

C (llvm-mos)
/* Lire la valeur d'un contrôle (groupe 36, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static uint8_t suivre(uint8_t c, int16_t x, int16_t y, uint8_t phase) {   /* 36,9 Track Control : 1 si le contrôle a agi */
    libre();
    ControlPort.params[0] = c;
    p16(1, x);
    p16(3, y);
    ControlPort.params[5] = phase;              /* 0 bouton enfoncé, 1 mouvement, 2 bouton relâché */
    api(36, 9);
    return ControlPort.params[6];
}
static uint8_t clic(uint8_t c, int16_t x, int16_t y) {   /* enfoncé puis relâché au même endroit */
    suivre(c, x, y, 0);
    return suivre(c, x, y, 2);
}

static Rect rc = {10, 30, 110, 42}, rr = {10, 48, 110, 60};
static const char option[] = "\006Option", radio[] = "\005Radio";

int main(void) {
    uint8_t c, r;
    uint16_t c0, c1, c2, r1, r2;
    ouvrir();
    c = controle(2, &rc, option, 0);            /* case à cocher, en (31, 61) à l'écran */
    r = controle(3, &rr, radio, 0);             /* bouton radio, en (31, 79) */
    c0 = valeur(c);
    clic(c, 40, 65); c1 = valeur(c);            /* la case bascule à chaque clic */
    clic(c, 40, 65); c2 = valeur(c);
    clic(r, 40, 82); r1 = valeur(r);            /* le bouton radio reste à 1 */
    clic(r, 40, 82); r2 = valeur(r);
    reinit();
    printf("Case : %u, %u, %u ; radio : %u, %u\n", c0, c1, c2, r1, r2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Case : 0, 1, 0 ; radio : 1, 1
Mad Pascal
// Lire la valeur d'un contrôle (groupe 36, fonction 6, Trinity).
program ct6;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

function suivre(c: byte; x, y: smallint; phase: byte): byte;   // 36,9 Track Control : 1 si le contrôle a agi
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  p16(1, word(x));
  p16(3, word(y));
  NeoMessage.params[5] := phase;                // 0 bouton enfoncé, 1 mouvement, 2 bouton relâché
  appel(36, 9);
  result := NeoMessage.params[6];
end;

function clic(c: byte; x, y: smallint): byte;   // enfoncé puis relâché au même endroit
begin
  suivre(c, x, y, 0);
  result := suivre(c, x, y, 2);
end;

const option: array[0..6] of byte = (6, ord('O'), ord('p'), ord('t'), ord('i'), ord('o'), ord('n'));
      radio: array[0..5] of byte = (5, ord('R'), ord('a'), ord('d'), ord('i'), ord('o'));
var rc, rr: Rect;
    c, r: byte;
    c0, c1, c2, r1, r2: word;
begin
  poser(rc, 10, 30, 110, 42); poser(rr, 10, 48, 110, 60);
  ouvrir;
  c := controle(2, @rc, @option, 0);            // case à cocher, en (31, 61) à l'écran
  r := controle(3, @rr, @radio, 0);             // bouton radio, en (31, 79)
  c0 := valeur(c);
  clic(c, 40, 65); c1 := valeur(c);             // la case bascule à chaque clic
  clic(c, 40, 65); c2 := valeur(c);
  clic(r, 40, 82); r1 := valeur(r);             // le bouton radio reste à 1
  clic(r, 40, 82); r2 := valeur(r);
  reinit;
  writeln('Case : ', c0, ', ', c1, ', ', c2, ' ; radio : ', r1, ', ', r2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Case : 0, 1, 0 ; radio : 1, 1
NeoBASIC
10 ' Lire la valeur d'un controle (groupe 36, fonction 6, Trinity)
20 gosub 7000
30 g = 10 : h = 30 : d = 110 : b = 42 : gosub 8000 : s$ = "Option" : gosub 7900 : ty = 2 : mx = 0 : gosub 7100 : ca = cn : ' case, en (31, 61)
40 h = 48 : b = 60 : gosub 8000 : s$ = "Radio" : gosub 7900 : ty = 3 : gosub 7100 : ra = cn : ' bouton radio, en (31, 79)
50 c = ca : gosub 7200 : c0 = v
60 x = 40 : y = 65 : gosub 7400 : gosub 7200 : c1 = v : ' la case bascule a chaque clic
70 gosub 7400 : gosub 7200 : c2 = v
80 c = ra : y = 82 : gosub 7400 : gosub 7200 : r1 = v : ' le bouton radio reste a 1
90 gosub 7400 : gosub 7200 : r2 = v
100 gosub 8300
110 print "Case : ";c0;", ";c1;", ";c2;" ; radio : ";r1;", ";r2
120 end
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Case : 0, 1, 0 ; radio : 1, 1
65C02 assembly (64tass)
; Lire la valeur d'un contrôle (groupe 36, 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:  jsr ouvrir
        lda #<cc
        ldx #>cc
        jsr controle
        sta c
        lda #<cr
        ldx #>cr
        jsr controle
        sta r
        lda c
        jsr valeur
        sta c0
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #0                  ; la case bascule à chaque clic
        sta pt+4
        lda c
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda c
        jsr suivre
        lda c
        jsr valeur
        sta c1
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #0                  ; enfoncé
        sta pt+4
        lda c
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda c
        jsr suivre
        lda c
        jsr valeur
        sta c2
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #82
        sta pt+2
        stz pt+3
        lda #0                  ; le bouton radio reste à 1
        sta pt+4
        lda r
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #82
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda r
        jsr suivre
        lda r
        jsr valeur
        sta r1
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #82
        sta pt+2
        stz pt+3
        lda #0                  ; enfoncé
        sta pt+4
        lda r
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #82
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda r
        jsr suivre
        lda r
        jsr valeur
        sta r2
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda c0
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda c1
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda c2
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda r1
        jsr decimal
        lda #<t5
        ldx #>t5
        jsr ecrire
        lda r2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

rc:     .word 10, 30, 110, 42
rr:     .word 10, 48, 110, 60
option: .byte 6
        .text "Option"
radio:  .byte 5
        .text "Radio"
cc:     .byte 2                  ; case à cocher, en (31, 61) à l'écran
        .word rc, option, 0
c:      .byte 0
cr:     .byte 3                  ; bouton radio, en (31, 79)
        .word rr, radio, 0
r:      .byte 0
c0:     .byte 0
c1:     .byte 0
c2:     .byte 0
r1:     .byte 0
r2:     .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "Case : ", 0
t2:     .text ", ", 0
t3:     .text ", ", 0
t4:     .text " ; radio : ", 0
t5:     .text ", ", 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Case : 0, 1, 0 ; radio : 1, 1

36,7 Set Control Flags Trinity

Sets the flags of a control and redraws it.

Inputs
BytesTypeMeaning
P0uint8control number
P1uint8flags: bit 0 disabled

Outputs: none.

A disabled control is drawn grey and ignores the mouse (36,9) and keys (36,10). NeoBASIC has no keyword for this call.

Firmware documentation text

Sets the flags of control Parameter:0 (bit 0 disabled : drawn grey, ignores tracking and keys) and redraws it.

C (llvm-mos)
/* Rendre un contrôle inactif (groupe 36, fonction 7, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static uint8_t suivre(uint8_t c, int16_t x, int16_t y, uint8_t phase) {   /* 36,9 Track Control : 1 si le contrôle a agi */
    libre();
    ControlPort.params[0] = c;
    p16(1, x);
    p16(3, y);
    ControlPort.params[5] = phase;              /* 0 bouton enfoncé, 1 mouvement, 2 bouton relâché */
    api(36, 9);
    return ControlPort.params[6];
}
static uint8_t clic(uint8_t c, int16_t x, int16_t y) {   /* enfoncé puis relâché au même endroit */
    suivre(c, x, y, 0);
    return suivre(c, x, y, 2);
}

static Rect rc = {10, 30, 110, 42};
static const char option[] = "\006Option";

static void options(uint8_t c, uint8_t o) {     /* Set Control Flags : bit 0 inactif */
    libre();
    ControlPort.params[0] = c;
    ControlPort.params[1] = o;
    api(36, 7);
}

int main(void) {
    uint8_t c, a1, a2;
    uint16_t v1, v2;
    ouvrir();
    c = controle(2, &rc, option, 0);
    options(c, 1);                              /* inactif : dessiné en gris, ignore la souris */
    a1 = clic(c, 40, 65); v1 = valeur(c);
    options(c, 0);
    a2 = clic(c, 40, 65); v2 = valeur(c);
    reinit();
    printf("Inactive : %u, valeur %u ; active : %u, valeur %u\n", a1, v1, a2, v2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Inactive : 0, valeur 0 ; active : 1, valeur 1
Mad Pascal
// Rendre un contrôle inactif (groupe 36, fonction 7, Trinity).
program ct7;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

function suivre(c: byte; x, y: smallint; phase: byte): byte;   // 36,9 Track Control : 1 si le contrôle a agi
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  p16(1, word(x));
  p16(3, word(y));
  NeoMessage.params[5] := phase;                // 0 bouton enfoncé, 1 mouvement, 2 bouton relâché
  appel(36, 9);
  result := NeoMessage.params[6];
end;

function clic(c: byte; x, y: smallint): byte;   // enfoncé puis relâché au même endroit
begin
  suivre(c, x, y, 0);
  result := suivre(c, x, y, 2);
end;

const option: array[0..6] of byte = (6, ord('O'), ord('p'), ord('t'), ord('i'), ord('o'), ord('n'));

procedure options(c, o: byte);                  // Set Control Flags : bit 0 inactif
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  NeoMessage.params[1] := o;
  appel(36, 7);
end;

var rc: Rect;
    c, a1, a2: byte;
    v1, v2: word;
begin
  poser(rc, 10, 30, 110, 42);
  ouvrir;
  c := controle(2, @rc, @option, 0);
  options(c, 1);                                // inactif : dessiné en gris, ignore la souris
  a1 := clic(c, 40, 65); v1 := valeur(c);
  options(c, 0);
  a2 := clic(c, 40, 65); v2 := valeur(c);
  reinit;
  writeln('Inactive : ', a1, ', valeur ', v1, ' ; active : ', a2, ', valeur ', v2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Inactive : 0, valeur 0 ; active : 1, valeur 1
NeoBASIC
10 ' Rendre un controle inactif (groupe 36, fonction 7, Trinity)
20 gosub 7000
30 g = 10 : h = 30 : d = 110 : b = 42 : gosub 8000 : s$ = "Option" : gosub 7900 : ty = 2 : mx = 0 : gosub 7100 : c = cn
40 o = 1 : gosub 2000 : ' inactif : dessine en gris, ignore la souris
50 x = 40 : y = 65 : gosub 7400 : a1 = ag : gosub 7200 : v1 = v
60 o = 0 : gosub 2000
70 gosub 7400 : a2 = ag : gosub 7200 : v2 = v
80 gosub 8300
90 print "Inactive : ";a1;", valeur ";v1;" ; active : ";a2;", valeur ";v2
100 end
2000 ' Set Control Flags : controle c, options o (bit 0 inactif)
2010 while peek($ff00) : wend : poke $ff04, c : poke $ff05, o : f = 7 : goto 8110
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Inactive : 0, valeur 0 ; active : 1, valeur 1
65C02 assembly (64tass)
; Rendre un contrôle inactif (groupe 36, fonction 7, 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:  jsr ouvrir
        lda #<cc
        ldx #>cc
        jsr controle
        sta c
        jsr WaitMessage
        lda c
        sta ControlPort+4
        lda #1
        sta ControlPort+5
        lda #36
        ldx #7                  ; Set Control Flags, bit 0 : inactif, ignore la souris
        jsr appel
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #0                  ; enfoncé
        sta pt+4
        lda c
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda c
        jsr suivre
        sta a1
        lda c
        jsr valeur
        sta v1
        jsr WaitMessage
        lda c
        sta ControlPort+4
        lda #0
        sta ControlPort+5
        lda #36
        ldx #7                  ; Set Control Flags : actif
        jsr appel
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #0                  ; enfoncé
        sta pt+4
        lda c
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #65
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda c
        jsr suivre
        sta a2
        lda c
        jsr valeur
        sta v2
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda a1
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda v1
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda a2
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda v2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

rc:     .word 10, 30, 110, 42
option: .byte 6
        .text "Option"
cc:     .byte 2                  ; case à cocher
        .word rc, option, 0
c:      .byte 0
a1:     .byte 0
v1:     .byte 0
a2:     .byte 0
v2:     .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "Inactive : ", 0
t2:     .text ", valeur ", 0
t3:     .text " ; active : ", 0
t4:     .text ", valeur ", 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Inactive : 0, valeur 0 ; active : 1, valeur 1

36,8 Find Control Trinity

Returns the control of the front window under a point, and the part hit.

Inputs
BytesTypeMeaning
P0-P1int16point X
P2-P3int16point Y
Outputs
BytesTypeMeaning
P4uint8control (0 if none)
P5uint8part: 1 body; scroll bars: 2 first arrow, 3 second arrow, 4 page before the thumb, 5 page after, 6 thumb

Measured on the bench for the 12 × 100 pixel scroll bar: 8-line arrows at the top and bottom, 4-line thumb (y 79 to 82 for the value 25 out of 50). NeoBASIC has no keyword for this call.

Firmware documentation text

Returns in Parameter:4 the control of the frontmost window under Parameters:0,1 (X), Parameters:2,3 (Y) (0 if none) and in Parameter:5 the part : 1 body ; scroll bars : 2 first arrow, 3 second arrow, 4 page before the thumb, 5 page after, 6 thumb.

C (llvm-mos)
/* Trouver le contrôle sous un point (groupe 36, fonction 8, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static Rect rb = {10, 10, 70, 24}, rs = {180, 0, 192, 100};
static const char ok[] = "\002OK";
static const int16_t py[6] = {45, 33, 125, 50, 80, 110};
static const int16_t px[6] = {40, 205, 205, 205, 205, 205};
static const char *nom[6] = {"Bouton", "Fleche du haut", "Fleche du bas", "Page avant", "Curseur", "Page apres"};

int main(void) {
    uint8_t s, i, c[6], z[6];
    ouvrir();
    controle(1, &rb, ok, 0);
    s = controle(4, &rs, titre, 50);            /* barre en x 201 à 212, y 31 à 130 */
    libre(); ControlPort.params[0] = s; p16(1, 25); api(36, 5);   /* curseur au milieu : y 79 à 82 */
    for (i = 0; i < 6; i++) {
        libre(); p16(0, px[i]); p16(2, py[i]); api(36, 8);   /* Find Control */
        c[i] = ControlPort.params[4];
        z[i] = ControlPort.params[5];
    }
    reinit();
    for (i = 0; i < 6; i++)
        printf("%s : %u, %u\n", nom[i], c[i], z[i]);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton : 1, 1
Fleche du haut : 2, 2
Fleche du bas : 2, 3
Page avant : 2, 4
Curseur : 2, 6
Page apres : 2, 5
Mad Pascal
// Trouver le contrôle sous un point (groupe 36, fonction 8, Trinity).
program ct8;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

const ok: array[0..2] of byte = (2, ord('O'), ord('K'));
      px: array[0..5] of word = (40, 205, 205, 205, 205, 205);
      py: array[0..5] of word = (45, 33, 125, 50, 80, 110);
var rb, rs: Rect;
    s, i: byte;
    c, z: array[0..5] of byte;
begin
  poser(rb, 10, 10, 70, 24); poser(rs, 180, 0, 192, 100);
  ouvrir;
  controle(1, @rb, @ok, 0);
  s := controle(4, @rs, @titre, 50);            // barre en x 201 à 212, y 31 à 130
  NeoWaitMessage; NeoMessage.params[0] := s; p16(1, 25); appel(36, 5);   // curseur au milieu : y 79 à 82
  for i := 0 to 5 do begin
    NeoWaitMessage;
    wordParams[0] := px[i];
    wordParams[1] := py[i];
    appel(36, 8);                               // Find Control
    c[i] := NeoMessage.params[4];
    z[i] := NeoMessage.params[5];
  end;
  reinit;
  writeln('Bouton : ', c[0], ', ', z[0]);
  writeln('Fleche du haut : ', c[1], ', ', z[1]);
  writeln('Fleche du bas : ', c[2], ', ', z[2]);
  writeln('Page avant : ', c[3], ', ', z[3]);
  writeln('Curseur : ', c[4], ', ', z[4]);
  writeln('Page apres : ', c[5], ', ', z[5]);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton : 1, 1
Fleche du haut : 2, 2
Fleche du bas : 2, 3
Page avant : 2, 4
Curseur : 2, 6
Page apres : 2, 5
NeoBASIC
10 ' Trouver le controle sous un point (groupe 36, fonction 8, Trinity)
20 gosub 7000
30 g = 10 : h = 10 : d = 70 : b = 24 : gosub 8000 : s$ = "OK" : gosub 7900 : ty = 1 : mx = 0 : gosub 7100
40 g = 180 : h = 0 : d = 192 : b = 100 : gosub 8000 : ch = tf : ty = 4 : mx = 50 : gosub 7100 : ba = cn : ' x 201 a 212, y 31 a 130
50 while peek($ff00) : wend : poke $ff04, ba : doke $ff05, 25 : f = 5 : gosub 8110 : ' curseur au milieu : y 79 a 82
60 dim c(5), z(5)
70 for k = 0 to 5 : read x, y
80 while peek($ff00) : wend : doke $ff04, x : doke $ff06, y : f = 8 : gosub 8110 : ' Find Control
90 c(k) = peek($ff08) : z(k) = peek($ff09) : next k
100 data 40, 45, 205, 33, 205, 125, 205, 50, 205, 80, 205, 110
110 gosub 8300
120 for k = 0 to 5 : read n$ : print n$;" : ";c(k);", ";z(k) : next k
130 data "Bouton", "Fleche du haut", "Fleche du bas", "Page avant", "Curseur", "Page apres"
140 end
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton : 1, 1
Fleche du haut : 2, 2
Fleche du bas : 2, 3
Page avant : 2, 4
Curseur : 2, 6
Page apres : 2, 5
65C02 assembly (64tass)
; Trouver le contrôle sous un point (groupe 36, fonction 8, 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:  jsr ouvrir
        lda #<cb
        ldx #>cb
        jsr controle
        lda #<cs
        ldx #>cs
        jsr controle
        sta s
        jsr WaitMessage
        lda s
        sta ControlPort+4
        lda #25
        sta ControlPort+5
        stz ControlPort+6
        lda #36
        ldx #5                  ; Set Control Value 25 : curseur en y 79 à 82
        jsr appel
        lda #<b0
        ldx #>b0
        jsr envoi
        lda ControlPort+8          ; P4 : contrôle
        sta c0
        lda ControlPort+9          ; P5 : partie
        sta z0
        lda #<b1
        ldx #>b1
        jsr envoi
        lda ControlPort+8          ; P4 : contrôle
        sta c1
        lda ControlPort+9          ; P5 : partie
        sta z1
        lda #<b2
        ldx #>b2
        jsr envoi
        lda ControlPort+8          ; P4 : contrôle
        sta c2
        lda ControlPort+9          ; P5 : partie
        sta z2
        lda #<b3
        ldx #>b3
        jsr envoi
        lda ControlPort+8          ; P4 : contrôle
        sta c3
        lda ControlPort+9          ; P5 : partie
        sta z3
        lda #<b4
        ldx #>b4
        jsr envoi
        lda ControlPort+8          ; P4 : contrôle
        sta c4
        lda ControlPort+9          ; P5 : partie
        sta z4
        lda #<b5
        ldx #>b5
        jsr envoi
        lda ControlPort+8          ; P4 : contrôle
        sta c5
        lda ControlPort+9          ; P5 : partie
        sta z5
        lda #<b6
        ldx #>b6
        jsr envoi
        lda #<t7
        ldx #>t7
        jsr ecrire
        lda c0
        jsr decimal
        lda #<t8
        ldx #>t8
        jsr ecrire
        lda z0
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t9
        ldx #>t9
        jsr ecrire
        lda c1
        jsr decimal
        lda #<t10
        ldx #>t10
        jsr ecrire
        lda z1
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t11
        ldx #>t11
        jsr ecrire
        lda c2
        jsr decimal
        lda #<t12
        ldx #>t12
        jsr ecrire
        lda z2
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t13
        ldx #>t13
        jsr ecrire
        lda c3
        jsr decimal
        lda #<t14
        ldx #>t14
        jsr ecrire
        lda z3
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t15
        ldx #>t15
        jsr ecrire
        lda c4
        jsr decimal
        lda #<t16
        ldx #>t16
        jsr ecrire
        lda z4
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t17
        ldx #>t17
        jsr ecrire
        lda c5
        jsr decimal
        lda #<t18
        ldx #>t18
        jsr ecrire
        lda z5
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

rb:     .word 10, 10, 70, 24
rs:     .word 180, 0, 192, 100
ok:     .byte 2
        .text "OK"
cb:     .byte 1                  ; bouton
        .word rb, ok, 0
cs:     .byte 4                  ; barre en x 201 à 212, y 31 à 130
        .word rs, titre, 50
s:      .byte 0
b0:     .byte 36, 8, 4             ; 36,8 : Find Control
        .word 40, 45
c0:     .byte 0
z0:     .byte 0
b1:     .byte 36, 8, 4             ; 36,8 : Find Control
        .word 205, 33
c1:     .byte 0
z1:     .byte 0
b2:     .byte 36, 8, 4             ; 36,8 : Find Control
        .word 205, 125
c2:     .byte 0
z2:     .byte 0
b3:     .byte 36, 8, 4             ; 36,8 : Find Control
        .word 205, 50
c3:     .byte 0
z3:     .byte 0
b4:     .byte 36, 8, 4             ; 36,8 : Find Control
        .word 205, 80
c4:     .byte 0
z4:     .byte 0
b5:     .byte 36, 8, 4             ; 36,8 : Find Control
        .word 205, 110
c5:     .byte 0
z5:     .byte 0
b6:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t7:     .text "Bouton : ", 0
t8:     .text ", ", 0
t9:     .text "Fleche du haut : ", 0
t10:    .text ", ", 0
t11:    .text "Fleche du bas : ", 0
t12:    .text ", ", 0
t13:    .text "Page avant : ", 0
t14:    .text ", ", 0
t15:    .text "Curseur : ", 0
t16:    .text ", ", 0
t17:    .text "Page apres : ", 0
t18:    .text ", ", 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Bouton : 1, 1
Fleche du haut : 2, 2
Fleche du bas : 2, 3
Page avant : 2, 4
Curseur : 2, 6
Page apres : 2, 5

36,9 Track Control Trinity

Has a control track the mouse: to call on mouse down, on each move and on mouse up.

Inputs
BytesTypeMeaning
P0uint8control number
P1-P2int16mouse X
P3-P4int16mouse Y
P5uint8phase: 0 mouse down, 1 move, 2 mouse up
Outputs
BytesTypeMeaning
P6uint81 if the control acted

A button acts when released inside it, even after leaving it; a box or radio button on release. A scroll bar acts when its value changes: arrows by 1 on mouse down, pages by the maximum divided by 10, thumb on move (dragged by 20 lines, it goes from 1 to 13). A clicked text field then receives the keys of 36,10. NeoBASIC has no keyword for this call.

Firmware documentation text

Tracks control Parameter:0 from the mouse : Parameters:1,2 (X), Parameters:3,4 (Y), Parameter:5 = phase (0 mouse down, 1 move, 2 mouse up). Parameter:6 is 1 when the control acted : button released inside it (highlighted while the pointer is inside), box toggled or radio set on mouse up, scroll bar value changed (arrows by 1, pages by max/10, thumb dragged), text field clicked (give it the keys with Key Control).

C (llvm-mos)
/* Suivre la souris sur un contrôle (groupe 36, fonction 9, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static uint8_t suivre(uint8_t c, int16_t x, int16_t y, uint8_t phase) {   /* 36,9 Track Control : 1 si le contrôle a agi */
    libre();
    ControlPort.params[0] = c;
    p16(1, x);
    p16(3, y);
    ControlPort.params[5] = phase;              /* 0 bouton enfoncé, 1 mouvement, 2 bouton relâché */
    api(36, 9);
    return ControlPort.params[6];
}
static uint8_t clic(uint8_t c, int16_t x, int16_t y) {   /* enfoncé puis relâché au même endroit */
    suivre(c, x, y, 0);
    return suivre(c, x, y, 2);
}

static Rect rb = {10, 10, 70, 24}, rs = {180, 0, 192, 100};
static const char ok[] = "\002OK";

int main(void) {
    uint8_t b, s, a1, a2, a3, a4;
    uint16_t v1, v2;
    ouvrir();
    b = controle(1, &rb, ok, 0);
    s = controle(4, &rs, titre, 50);
    suivre(b, 40, 45, 0);                       /* bouton : enfoncé dedans, sorti, relâché dedans */
    suivre(b, 100, 100, 1);
    a1 = suivre(b, 40, 45, 2);
    a2 = suivre(s, 205, 125, 0);                /* flèche du bas : agit dès l'enfoncement, +1 */
    suivre(s, 205, 125, 2);
    v1 = valeur(s);
    suivre(s, 205, 42, 0);                      /* curseur (y 40 à 43 pour la valeur 1), glissé de 20 lignes */
    a3 = suivre(s, 205, 62, 1);
    a4 = suivre(s, 205, 62, 2);
    v2 = valeur(s);
    reinit();
    printf("Bouton : %u ; fleche : %u, valeur %u\n", a1, a2, v1);
    printf("Curseur glisse : %u puis %u, valeur %u\n", a3, a4, v2);
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton : 1 ; fleche : 1, valeur 1
Curseur glisse : 1 puis 0, valeur 13
Mad Pascal
// Suivre la souris sur un contrôle (groupe 36, fonction 9, Trinity).
program ct9;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

function suivre(c: byte; x, y: smallint; phase: byte): byte;   // 36,9 Track Control : 1 si le contrôle a agi
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  p16(1, word(x));
  p16(3, word(y));
  NeoMessage.params[5] := phase;                // 0 bouton enfoncé, 1 mouvement, 2 bouton relâché
  appel(36, 9);
  result := NeoMessage.params[6];
end;

function clic(c: byte; x, y: smallint): byte;   // enfoncé puis relâché au même endroit
begin
  suivre(c, x, y, 0);
  result := suivre(c, x, y, 2);
end;

const ok: array[0..2] of byte = (2, ord('O'), ord('K'));
var rb, rs: Rect;
    b, s, a1, a2, a3, a4: byte;
    v1, v2: word;
begin
  poser(rb, 10, 10, 70, 24); poser(rs, 180, 0, 192, 100);
  ouvrir;
  b := controle(1, @rb, @ok, 0);
  s := controle(4, @rs, @titre, 50);
  suivre(b, 40, 45, 0);                         // bouton : enfoncé dedans, sorti, relâché dedans
  suivre(b, 100, 100, 1);
  a1 := suivre(b, 40, 45, 2);
  a2 := suivre(s, 205, 125, 0);                 // flèche du bas : agit dès l'enfoncement, +1
  suivre(s, 205, 125, 2);
  v1 := valeur(s);
  suivre(s, 205, 42, 0);                        // curseur (y 40 à 43 pour la valeur 1), glissé de 20 lignes
  a3 := suivre(s, 205, 62, 1);
  a4 := suivre(s, 205, 62, 2);
  v2 := valeur(s);
  reinit;
  writeln('Bouton : ', a1, ' ; fleche : ', a2, ', valeur ', v1);
  writeln('Curseur glisse : ', a3, ' puis ', a4, ', valeur ', v2);
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton : 1 ; fleche : 1, valeur 1
Curseur glisse : 1 puis 0, valeur 13
NeoBASIC
10 ' Suivre la souris sur un controle (groupe 36, fonction 9, Trinity)
20 gosub 7000
30 g = 10 : h = 10 : d = 70 : b = 24 : gosub 8000 : s$ = "OK" : gosub 7900 : ty = 1 : mx = 0 : gosub 7100 : bt = cn
40 g = 180 : h = 0 : d = 192 : b = 100 : gosub 8000 : ch = tf : ty = 4 : mx = 50 : gosub 7100 : ba = cn
50 c = bt : x = 40 : y = 45 : ph = 0 : gosub 7300 : ' bouton : enfonce dedans, sorti, relache dedans
60 x = 100 : y = 100 : ph = 1 : gosub 7300
70 x = 40 : y = 45 : ph = 2 : gosub 7300 : a1 = ag
80 c = ba : x = 205 : y = 125 : ph = 0 : gosub 7300 : a2 = ag : ' fleche du bas : agit des l'enfoncement, +1
90 ph = 2 : gosub 7300 : gosub 7200 : v1 = v
100 y = 42 : ph = 0 : gosub 7300 : ' curseur (y 40 a 43 pour la valeur 1), glisse de 20 lignes
110 y = 62 : ph = 1 : gosub 7300 : a3 = ag
120 ph = 2 : gosub 7300 : a4 = ag : gosub 7200 : v2 = v
130 gosub 8300
140 print "Bouton : ";a1;" ; fleche : ";a2;", valeur ";v1
150 print "Curseur glisse : ";a3;" puis ";a4;", valeur ";v2
160 end
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
Bouton : 1 ; fleche : 1, valeur 1
Curseur glisse : 1 puis 0, valeur 13
65C02 assembly (64tass)
; Suivre la souris sur un contrôle (groupe 36, fonction 9, 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:  jsr ouvrir
        lda #<cb
        ldx #>cb
        jsr controle
        sta b
        lda #<cs
        ldx #>cs
        jsr controle
        sta s
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #45
        sta pt+2
        stz pt+3
        lda #0                  ; bouton : enfoncé dedans...
        sta pt+4
        lda b
        jsr suivre
        lda #<100
        sta pt
        lda #>100
        sta pt+1
        lda #100
        sta pt+2
        stz pt+3
        lda #1                  ; ... sorti...
        sta pt+4
        lda b
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #45
        sta pt+2
        stz pt+3
        lda #2                  ; ... relâché dedans
        sta pt+4
        lda b
        jsr suivre
        sta a1
        lda #<205
        sta pt
        lda #>205
        sta pt+1
        lda #125
        sta pt+2
        stz pt+3
        lda #0                  ; flèche du bas : agit dès l'enfoncement, +1
        sta pt+4
        lda s
        jsr suivre
        sta a2
        lda #<205
        sta pt
        lda #>205
        sta pt+1
        lda #125
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda s
        jsr suivre
        lda s
        jsr valeur
        sta v1
        lda #<205
        sta pt
        lda #>205
        sta pt+1
        lda #42
        sta pt+2
        stz pt+3
        lda #0                  ; curseur (y 40 à 43 pour la valeur 1)...
        sta pt+4
        lda s
        jsr suivre
        lda #<205
        sta pt
        lda #>205
        sta pt+1
        lda #62
        sta pt+2
        stz pt+3
        lda #1                  ; ... glissé de 20 lignes
        sta pt+4
        lda s
        jsr suivre
        sta a3
        lda #<205
        sta pt
        lda #>205
        sta pt+1
        lda #62
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda s
        jsr suivre
        sta a4
        lda s
        jsr valeur
        sta v2
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda a1
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda a2
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda v1
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t4
        ldx #>t4
        jsr ecrire
        lda a3
        jsr decimal
        lda #<t5
        ldx #>t5
        jsr ecrire
        lda a4
        jsr decimal
        lda #<t6
        ldx #>t6
        jsr ecrire
        lda v2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

rb:     .word 10, 10, 70, 24
rs:     .word 180, 0, 192, 100
ok:     .byte 2
        .text "OK"
cb:     .byte 1                  ; bouton
        .word rb, ok, 0
b:      .byte 0
cs:     .byte 4                  ; barre, maximum 50
        .word rs, titre, 50
s:      .byte 0
a1:     .byte 0
a2:     .byte 0
v1:     .byte 0
a3:     .byte 0
a4:     .byte 0
v2:     .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "Bouton : ", 0
t2:     .text " ; fleche : ", 0
t3:     .text ", valeur ", 0
t4:     .text "Curseur glisse : ", 0
t5:     .text " puis ", 0
t6:     .text ", valeur ", 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
Bouton : 1 ; fleche : 1, valeur 1
Curseur glisse : 1 puis 0, valeur 13

36,10 Key Control Trinity

Sends a key to a text field.

Inputs
BytesTypeMeaning
P0uint8control number
P1uint8character; 8 or 127 deletes the last one
Outputs
BytesTypeMeaning
P2uint81 if the buffer changed

The buffer is the length-prefixed string given to 36,1, in 6502 RAM: the program reads the text there. A character past the maximum length is refused. NeoBASIC has no keyword for this call.

Firmware documentation text

Sends the key Parameter:1 to text field Parameter:0 : printable characters are appended to its buffer (up to the maximum length), 8 or 127 deletes the last one ; Parameter:2 is 1 if the buffer changed. The buffer is the length-prefixed string given to New Control, in 6502 RAM.

C (llvm-mos)
/* Envoyer une touche à un champ de texte (groupe 36, fonction 10, 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)
        ;
}

/* Rect de QuickDraw : gauche, haut, droite, bas (16 bits signés ; droite et bas exclus). */
typedef struct { int16_t g, h, d, b; } Rect;

/* La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu. */
static Rect cadre = {20, 20, 220, 140};
static const char titre[] = "\001A";             /* chaînes précédées de leur longueur, lues sur place */
static uint8_t fenetre;

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 void reinit(void) {                      /* 32,19 Toolbox Reset : plus de fenêtre ni de contrôle */
    libre();
    api(32, 19);
}
static void ouvrir(void) {                      /* 34,1 : une fenêtre avec barre de titre */
    reinit();
    libre();
    p16(0, (uint16_t)&cadre);
    p16(2, (uint16_t)titre);
    ControlPort.params[4] = 1;
    api(34, 1);
    fenetre = ControlPort.params[5];
}
/* 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
   au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle. */
static uint8_t controle(uint8_t type, Rect *r, const char *t, uint16_t max) {
    libre();
    ControlPort.params[0] = type;
    ControlPort.params[1] = fenetre;
    p16(2, (uint16_t)r);
    p16(4, (uint16_t)t);
    p16(6, max);
    api(36, 1);
    return ControlPort.params[0];
}
static uint16_t valeur(uint8_t c) {             /* 36,6 Get Control Value */
    libre();
    ControlPort.params[0] = c;
    api(36, 6);
    return ControlPort.params[1] | ControlPort.params[2] << 8;
}

static uint8_t suivre(uint8_t c, int16_t x, int16_t y, uint8_t phase) {   /* 36,9 Track Control : 1 si le contrôle a agi */
    libre();
    ControlPort.params[0] = c;
    p16(1, x);
    p16(3, y);
    ControlPort.params[5] = phase;              /* 0 bouton enfoncé, 1 mouvement, 2 bouton relâché */
    api(36, 9);
    return ControlPort.params[6];
}
static uint8_t clic(uint8_t c, int16_t x, int16_t y) {   /* enfoncé puis relâché au même endroit */
    suivre(c, x, y, 0);
    return suivre(c, x, y, 2);
}

static Rect rt = {10, 70, 150, 84};
static char tampon[5] = "\002ab";               /* longueur, puis le texte ; maximum 4 caractères */

static uint8_t touche(uint8_t c, uint8_t k) {   /* Key Control : 1 si le tampon a changé */
    libre();
    ControlPort.params[0] = c;
    ControlPort.params[1] = k;
    api(36, 10);
    return ControlPort.params[2];
}
static void afficher(void) {
    uint8_t i;
    for (i = 1; i <= tampon[0]; i++)
        putchar(tampon[i]);
    putchar('\n');
}

int main(void) {
    uint8_t t, x, y, z;
    ouvrir();
    t = controle(5, &rt, tampon, 4);
    clic(t, 40, 105);                           /* le champ est cliqué */
    x = touche(t, 'X');
    y = touche(t, 'Y');
    z = touche(t, 'Z');                         /* plein : refusé */
    reinit();
    printf("X : %u, Y : %u, Z : %u -> ", x, y, z);
    afficher();
    ouvrir();
    t = controle(5, &rt, tampon, 4);
    touche(t, 8);                               /* 8 (ou 127) efface le dernier caractère */
    reinit();
    printf("Effacement -> ");
    afficher();
    return 0;
}
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
X : 1, Y : 1, Z : 0 -> abXY
Effacement -> abX
Mad Pascal
// Envoyer une touche à un champ de texte (groupe 36, fonction 10, Trinity).
program ct10;
uses crt, neo6502;
type Rect = record g, h, d, b: smallint; end; // gauche, haut, droite, bas (16 bits signés ; droite et bas exclus)

// La fenêtre : contenu de (21, 31) à (219, 139). Les contrôles sont placés dans le contenu.
const titre: array[0..1] of byte = (1, ord('A'));   // chaînes précédées de leur longueur, lues sur place
var cadre: Rect;
    fenetre: byte;

procedure poser(var r: Rect; g, h, d, b: smallint);
begin
  r.g := g; r.h := h; r.d := d; r.b := b;
end;

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

procedure reinit;                               // 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
begin
  NeoWaitMessage;
  appel(32, 19);
end;

procedure ouvrir;                               // 34,1 : une fenêtre avec barre de titre
begin
  reinit;
  poser(cadre, 20, 20, 220, 140);
  wordParams[0] := word(@cadre);
  wordParams[1] := word(@titre);
  NeoMessage.params[4] := 1;
  appel(34, 1);
  fenetre := NeoMessage.params[5];
end;

// 36,1 New Control : type (1 bouton, 2 case, 3 radio, 4 barre de défilement, 5 champ de texte), Rect relatif
// au contenu, titre (ou tampon du champ), maximum ; rend le numéro du contrôle.
function controle(t: byte; r, s: pointer; max: word): byte;
begin
  NeoWaitMessage;
  NeoMessage.params[0] := t;
  NeoMessage.params[1] := fenetre;
  wordParams[1] := word(r);
  wordParams[2] := word(s);
  wordParams[3] := max;
  appel(36, 1);
  result := NeoMessage.params[0];
end;

function valeur(c: byte): word;                 // 36,6 Get Control Value
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  appel(36, 6);
  result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;

procedure p16(i: byte; v: word);                // paramètre 16 bits en Pi, Pi+1
begin
  NeoMessage.params[i] := lo(v);
  NeoMessage.params[i + 1] := hi(v);
end;

function suivre(c: byte; x, y: smallint; phase: byte): byte;   // 36,9 Track Control : 1 si le contrôle a agi
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  p16(1, word(x));
  p16(3, word(y));
  NeoMessage.params[5] := phase;                // 0 bouton enfoncé, 1 mouvement, 2 bouton relâché
  appel(36, 9);
  result := NeoMessage.params[6];
end;

function clic(c: byte; x, y: smallint): byte;   // enfoncé puis relâché au même endroit
begin
  suivre(c, x, y, 0);
  result := suivre(c, x, y, 2);
end;

var tampon: array[0..4] of byte = (2, ord('a'), ord('b'), 0, 0);   // longueur, puis le texte ; maximum 4 caractères
    rt: Rect;
    t, x, y, z: byte;

function touche(c, k: byte): byte;              // Key Control : 1 si le tampon a changé
begin
  NeoWaitMessage;
  NeoMessage.params[0] := c;
  NeoMessage.params[1] := k;
  appel(36, 10);
  result := NeoMessage.params[2];
end;

procedure afficher;
var i: byte;
begin
  for i := 1 to tampon[0] do write(chr(tampon[i]));
  writeln;
end;

begin
  poser(rt, 10, 70, 150, 84);
  ouvrir;
  t := controle(5, @rt, @tampon, 4);
  clic(t, 40, 105);                             // le champ est cliqué
  x := touche(t, ord('X'));
  y := touche(t, ord('Y'));
  z := touche(t, ord('Z'));                     // plein : refusé
  reinit;
  write('X : ', x, ', Y : ', y, ', Z : ', z, ' -> ');
  afficher;
  ouvrir;
  t := controle(5, @rt, @tampon, 4);
  touche(t, 8);                                 // 8 (ou 127) efface le dernier caractère
  reinit;
  write('Effacement -> ');
  afficher;
end.
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
X : 1, Y : 1, Z : 0 -> abXY
Effacement -> abX
NeoBASIC
10 ' Envoyer une touche a un champ de texte (groupe 36, fonction 10, Trinity)
20 tp = alloc(5) : poke tp, 2 : poke tp + 1, asc("a") : poke tp + 2, asc("b") : ' longueur, puis le texte ; maximum 4
30 gosub 7000
40 g = 10 : h = 70 : d = 150 : b = 84 : gosub 8000 : rt = q : ch = tp : ty = 5 : mx = 4 : gosub 7100 : c = cn
50 x = 40 : y = 105 : gosub 7400 : ' le champ est clique
60 k = asc("X") : gosub 2000 : kx = kc
70 k = asc("Y") : gosub 2000 : ky = kc
80 k = asc("Z") : gosub 2000 : kz = kc : ' plein : refuse
90 gosub 8300
100 print "X : ";kx;", Y : ";ky;", Z : ";kz;" -> ";
110 gosub 3000
120 gosub 7000 : q = rt : ch = tp : ty = 5 : mx = 4 : gosub 7100 : c = cn
130 k = 8 : gosub 2000 : ' 8 (ou 127) efface le dernier caractere
140 gosub 8300
150 print "Effacement -> "; : gosub 3000
160 end
2000 ' Key Control : touche k au champ c ; kc = 1 si le tampon a change
2010 while peek($ff00) : wend : poke $ff04, c : poke $ff05, k : f = 10 : gosub 8110 : kc = peek($ff06) : return
3000 for i = 1 to peek(tp) : print chr$(peek(tp + i)); : next i : print : return
7000 ' une fenetre avec barre de titre (34,1) : contenu de (21, 31) a (219, 139) ; numero dans fw
7010 gosub 8300 : cf = alloc(8) : doke cf, 20 : doke cf + 2, 20 : doke cf + 4, 220 : doke cf + 6, 140
7020 s$ = "A" : gosub 7900 : tf = ch
7030 while peek($ff00) : wend : doke $ff04, cf : doke $ff06, tf : poke $ff08, 1 : poke $ff01, 1 : poke $ff00, 34
7040 while peek($ff00) : wend : fw = peek($ff09) : return
7100 ' 36,1 New Control : type ty (1 bouton, 2 case, 3 radio, 4 barre, 5 champ), Rect q relatif au contenu,
7110 ' titre (ou tampon) ch, maximum mx ; numero dans cn, erreur dans e
7120 while peek($ff00) : wend : poke $ff04, ty : poke $ff05, fw : doke $ff06, q : doke $ff08, ch : doke $ff0a, mx
7130 f = 1 : gosub 8110 : cn = peek($ff04) : return
7200 ' 36,6 Get Control Value du controle c, dans v
7210 while peek($ff00) : wend : poke $ff04, c : f = 6 : gosub 8110 : v = deek($ff05) : return
7300 ' 36,9 Track Control : controle c en (x, y), phase ph (0 enfonce, 1 mouvement, 2 relache) ; ag = 1 s'il a agi
7310 while peek($ff00) : wend : poke $ff04, c : doke $ff05, x : doke $ff07, y : poke $ff09, ph
7320 f = 9 : gosub 8110 : ag = peek($ff0a) : return
7400 ' clic : enfonce puis relache en (x, y)
7410 ph = 0 : gosub 7300 : ph = 2 : goto 7300
7900 ' chaine s$ precedee de sa longueur, dans un bloc neuf ch (lue sur place)
7910 ch = alloc(len(s$) + 1) : poke ch, len(s$) : for i = 1 to len(s$) : poke ch + i, asc(mid$(s$, i, 1)) : next i : return
8000 ' Rect en q : gauche g, haut h, droite d, bas b (droite et bas exclus)
8010 q = alloc(8) : doke q, g : doke q + 2, h : doke q + 4, d : doke q + 6, b : return
8100 ' appel 36,f, parametres deja poses ; erreur dans e
8110 poke $ff01, f : poke $ff00, 36
8120 while peek($ff00) : wend : e = peek($ff02) : return
8300 ' 32,19 Toolbox Reset : plus de fenetre ni de controle
8310 while peek($ff00) : wend : poke $ff01, 19 : poke $ff00, 32
8320 while peek($ff00) : wend : return
Output observed in the Phosphoneo emulator, Trinity firmware 0.16.69:
X : 1, Y : 1, Z : 0 -> abXY
Effacement -> abX
65C02 assembly (64tass)
; Envoyer une touche à un champ de texte (groupe 36, fonction 10, 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:  jsr ouvrir
        lda #<ct
        ldx #>ct
        jsr controle
        sta t
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #105
        sta pt+2
        stz pt+3
        lda #0                  ; le champ est cliqué
        sta pt+4
        lda t
        jsr suivre
        lda #<40
        sta pt
        lda #>40
        sta pt+1
        lda #105
        sta pt+2
        stz pt+3
        lda #2                  ; relâché
        sta pt+4
        lda t
        jsr suivre
        jsr WaitMessage
        lda t
        sta ControlPort+4
        lda #'X'
        sta ControlPort+5
        lda #36
        ldx #10                 ; Key Control
        jsr appel
        lda ControlPort+6          ; P2 : 1 si le tampon a changé
        sta x
        jsr WaitMessage
        lda t
        sta ControlPort+4
        lda #'Y'
        sta ControlPort+5
        lda #36
        ldx #10                 ; Key Control
        jsr appel
        lda ControlPort+6          ; P2 : 1 si le tampon a changé
        sta y
        jsr WaitMessage
        lda t
        sta ControlPort+4
        lda #'Z'
        sta ControlPort+5
        lda #36
        ldx #10                 ; plein : refusé
        jsr appel
        lda ControlPort+6          ; P2 : 1 si le tampon a changé
        sta z
        lda #<b0
        ldx #>b0
        jsr envoi
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda x
        jsr decimal
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda y
        jsr decimal
        lda #<t3
        ldx #>t3
        jsr ecrire
        lda z
        jsr decimal
        lda #<t4
        ldx #>t4
        jsr ecrire
        jsr afficher
        jsr ouvrir
        lda #<ct
        ldx #>ct
        jsr controle
        sta t
        jsr WaitMessage
        lda t
        sta ControlPort+4
        lda #8
        sta ControlPort+5
        lda #36
        ldx #10                 ; 8 (ou 127) efface le dernier caractère
        jsr appel
        lda #<b5
        ldx #>b5
        jsr envoi
        lda #<t6
        ldx #>t6
        jsr ecrire
        jsr afficher
        rts

rt:     .word 10, 70, 150, 84
tampon: .byte 2                    ; longueur, puis le texte ; maximum 4 caractères
        .text "ab"
        .fill 2
ct:     .byte 5                  ; champ de texte, maximum 4
        .word rt, tampon, 4
t:      .byte 0
x:      .byte 0
y:      .byte 0
z:      .byte 0
b0:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t1:     .text "X : ", 0
t2:     .text ", Y : ", 0
t3:     .text ", Z : ", 0
t4:     .text " -> ", 0
b5:     .byte 32, 19, 0            ; 32,19 Toolbox Reset
t6:     .text "Effacement -> ", 0

; Affiche le tampon (chaîne précédée de sa longueur), puis passe à la ligne.
afficher:
        ldx #0
_af:    cpx tampon
        beq _fa
        lda tampon+1,x
        jsr WriteCharacter
        inx
        bra _af
_fa:    lda #13
        jmp WriteCharacter

; 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

; Une fenêtre avec barre de titre (34,1) : contenu de (21, 31) à (219, 139) ; numéro dans fen.
ouvrir: lda #<raz
        ldx #>raz
        jsr envoi
        lda #<nfen
        ldx #>nfen
        jsr envoi
        lda ControlPort+9
        sta fen
        rts
raz:    .byte 32, 19, 0            ; 32,19 Toolbox Reset : plus de fenêtre ni de contrôle
nfen:   .byte 34, 1, 5
        .word cadre, titre
        .byte 1
cadre:  .word 20, 20, 220, 140
titre:  .byte 1
        .text "A"                  ; chaînes précédées de leur longueur, lues sur place
fen:    .byte 0

; 36,1 New Control : bloc en A/X (type, Rect relatif au contenu, titre ou tampon, maximum) ; numéro dans A.
controle:
        sta blk
        stx blk+1
        jsr WaitMessage
        ldy #0
        lda (blk),y
        sta ControlPort+4          ; P0 : type (1 bouton, 2 case, 3 radio, 4 barre, 5 champ)
        lda fen
        sta ControlPort+5          ; P1 : fenêtre
        ldy #1
_ctl:   lda (blk),y
        sta ControlPort+5,y        ; P2-P7 : Rect, titre, maximum
        iny
        cpy #7
        bne _ctl
        lda #36
        ldx #1
        jsr appel
        lda ControlPort+4
        rts

; 36,6 Get Control Value du contrôle A : valeur (8 bits bas) dans A.
valeur: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #36
        ldx #6
        jsr appel
        lda ControlPort+5
        rts

; 36,9 Track Control : contrôle A, point et phase dans le bloc pt (X, Y, phase) ; A = 1 si le contrôle a agi.
suivre: pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        ldx #4
_sv:    lda pt,x
        sta ControlPort+5,x        ; P1-P2 : X, P3-P4 : Y, P5 : 0 enfoncé, 1 mouvement, 2 relâché
        dex
        bpl _sv
        lda #36
        ldx #9
        jsr appel
        lda ControlPort+10
        rts
pt:     .word 0, 0
        .byte 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:
X : 1, Y : 1, Z : 0 -> abXY
Effacement -> abX

← The Neo6502 API