Tachibana
Langue : français

Groupe 13 — Éditeur Officiel

13,1 Initialise Editor · 13,2 Reenter the Editor

Le firmware contient un éditeur plein écran, une ligne de texte par ligne d'écran, celui que NeoBASIC emploie pour ses programmes. Il ne sait rien du document édité : il demande au programme ce qu'il lui faut par des rappels. 13,1 le démarre, puis le programme appelle 13,2 en boucle ; chaque appel rend en P0 un code de rappel, auquel le programme répond avant de rappeler 13,2.

Codes de rappel : 0 fin de l'édition (Échap) ; 1 donner le nombre de lignes en P0-P1 ; 2 donner la ligne dont le numéro est en P1-P2 (à partir de 1), en mettant en P0-P1 l'adresse d'un tampon qui la contient (octet de longueur, puis texte) ; 3 donner une touche en P0 (0 : aucune) ; 4 la ligne dont le numéro est en P1-P2 a changé, le tampon de la ligne éditée contient son nouveau texte ; 5 insérer une ligne vide et 6 supprimer la ligne, numéro en P1-P2 ; $FF rien à faire, rappeler 13,2.

Touches reconnues, en plus des caractères : gauche Ctrl+A, droite Ctrl+D, haut Ctrl+W, bas Ctrl+S, début Ctrl+T, fin Ctrl+G, tabulation, retour arrière, suppression Ctrl+Z, Entrée, page haut Ctrl+R et bas Ctrl+F, insérer une ligne Ctrl+P, supprimer la ligne Ctrl+Q, Échap pour sortir. Les exemples tirent leurs touches d'une liste ; un vrai programme lit le clavier (2,1).

13,1 Initialise Editor Officiel

Démarre une session de l'éditeur plein écran ; rend le premier rappel (1 : combien de lignes ?).

Entrées
OctetsTypeRôle
P0-P1addradresse d'une ligne d'état (octet de longueur, puis texte) affichée en haut de l'écran, ou 0
Sorties
OctetsTypeRôle
P0uint8premier rappel : 1

L'exemple mène une session complète sur un document de trois lignes et relève les rappels : 1 (nombre de lignes), 2 trois fois pour afficher les lignes, 2 encore pour la ligne à éditer, 3 pour une touche (Échap), puis 0. Les $FF (rien à faire) ne sont pas comptés. NeoBASIC n'a pas de mot-clé pour cet appel ; son éditeur de programmes s'en sert.

Texte de la documentation du firmware (anglais)

Initialises the editor

C (llvm-mos)
/* Suivre les rappels d'une session d'édition courte (groupe 13, fonction 1). */
#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)
        ;
}

/* Le document édité : trois lignes, chaînes précédées de leur longueur (le format de l'éditeur). */
static uint8_t doc[3][41] = {"\x02Un", "\x04" "Deux", "\x05Trois"};
static uint8_t tampon[256];                     /* ligne échangée avec l'éditeur */
static uint8_t touches[] = {0x1B};             /* Échap tout de suite (au lieu du clavier, 2,1) */
static uint8_t t;

/* Répond au rappel code, puis relance l'éditeur (13,2) ; rend le rappel suivant. */
static uint8_t repondre(uint8_t code) {
    uint16_t n = ControlPort.params[1] | ControlPort.params[2] << 8;   /* numéro de ligne (codes 2 et 4) */
    libre();
    if (code == 1) {                            /* combien de lignes ? */
        ControlPort.params[0] = 3;
        ControlPort.params[1] = 0;
    } else if (code == 2) {                     /* la ligne n (à partir de 1), dans un tampon */
        uint8_t *l = n <= 3 ? doc[n - 1] : (uint8_t *)"";   /* après la fin : ligne vide */
        for (uint8_t i = 0; i <= l[0]; i++)
            tampon[i] = l[i];
        ControlPort.params[0] = (uint16_t)tampon & 0xFF;
        ControlPort.params[1] = (uint16_t)tampon >> 8;
    } else if (code == 3) {                     /* une touche (0 : aucune) */
        ControlPort.params[0] = t < sizeof touches ? touches[t++] : 0x1B;
    } else if (code == 4 && n <= 3) {           /* la ligne n a changé : le tampon la contient */
        for (uint8_t i = 0; i <= tampon[0] && i < 41; i++)
            doc[n - 1][i] = tampon[i];
    }
    api(13, 2);                                 /* Reenter the Editor */
    return ControlPort.params[0];
}

static uint8_t demarrer(void) {                 /* 13,1 : sans ligne d'état (P0-P1 = 0) */
    libre();
    ControlPort.params[0] = 0;
    ControlPort.params[1] = 0;
    api(13, 1);                                 /* Initialise Editor */
    return ControlPort.params[0];
}

int main(void) {
    uint8_t vus[16], nv = 0;
    uint8_t code = demarrer();
    while (1) {
        if (code != 0xFF && nv < sizeof vus)
            vus[nv++] = code;                   /* $FF : pas de rappel, on relance seulement */
        if (code == 0)                          /* fin de l'édition */
            break;
        code = repondre(code);
    }
    printf("Rappels :");
    for (uint8_t i = 0; i < nv; i++)
        printf(" %u", vus[i]);
    printf("\n");
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Rappels : 1 2 2 2 2 3 0
Mad Pascal
// Suivre les rappels d'une session d'édition courte (groupe 13, fonction 1).
program editeur1;
uses crt, neo6502;
const NT = 1;
      touches: array[0..1] of byte = (27, 27);    // Échap tout de suite (au lieu du clavier, 2,1)
var doc: array[0..2, 0..40] of byte;            // le document : longueur, puis texte (le format de l'éditeur)
    tampon: array[0..255] of byte;              // ligne échangée avec l'éditeur
    t: byte;

// Répond au rappel code, puis relance l'éditeur (13,2) ; rend le rappel suivant.
function repondre(code: byte): byte;
var n: word;
    i, k: byte;
begin
  n := NeoMessage.params[1] + NeoMessage.params[2] shl 8;   // numéro de ligne (codes 2 et 4)
  NeoWaitMessage;
  if code = 1 then                              // combien de lignes ?
    wordParams[0] := 3
  else if code = 2 then begin                   // la ligne n (à partir de 1), dans un tampon
    tampon[0] := 0;                             // après la fin : ligne vide
    if n <= 3 then
      for i := 0 to doc[n - 1, 0] do tampon[i] := doc[n - 1, i];
    wordParams[0] := word(@tampon);
  end else if code = 3 then begin               // une touche (0 : aucune)
    if t < NT then begin NeoMessage.params[0] := touches[t]; inc(t); end
    else NeoMessage.params[0] := $1B;
  end else if (code = 4) and (n <= 3) then begin   // la ligne n a changé : le tampon la contient
    k := tampon[0];
    if k > 40 then k := 40;                     // copie bornée à la taille d'une ligne du document
    doc[n - 1, 0] := k;
    for i := 1 to k do doc[n - 1, i] := tampon[i];
  end;
  NeoReenterEditor;                             // Reenter the Editor
  NeoWaitMessage;
  result := NeoMessage.params[0];
end;

function demarrer: byte;                        // 13,1 : sans ligne d'état (P0-P1 = 0)
begin
  NeoWaitMessage;
  wordParams[0] := 0;
  NeoInitEditor;                                // Initialise Editor
  NeoWaitMessage;
  result := NeoMessage.params[0];
end;

procedure ligne(i: byte; s: string);            // range s dans la ligne i du document
var j: byte;
begin
  doc[i, 0] := length(s);
  for j := 1 to length(s) do doc[i, j] := ord(s[j]);
end;

procedure document;
begin
  ligne(0, 'Un');
  ligne(1, 'Deux');
  ligne(2, 'Trois');
  t := 0;
end;
var vus: array[0..15] of byte;
    nv, code, i: byte;
begin
  document;
  nv := 0;
  code := demarrer;
  while true do begin
    if (code <> $FF) and (nv < 16) then begin   // $FF : pas de rappel, on relance seulement
      vus[nv] := code;
      inc(nv);
    end;
    if code = 0 then break;                     // fin de l'édition
    code := repondre(code);
  end;
  write('Rappels :');
  for i := 0 to nv - 1 do write(' ', vus[i]);
  writeln;
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Rappels : 1 2 2 2 2 3 0
NeoBASIC
10 ' Suivre les rappels d'une session d'edition courte (groupe 13, fonction 1)
20 d = alloc(123) : t = alloc(256) : ' document (3 lignes : longueur, puis texte) et tampon d'echange
30 i = 0 : l$ = "Un" : gosub 1400 : i = 1 : l$ = "Deux" : gosub 1400 : i = 2 : l$ = "Trois" : gosub 1400
40 kk = alloc(1) : poke kk, 27 : nt = 1 : q = 0 : ' touches : Echap tout de suite (au lieu du clavier, 2,1)
50 while peek($ff00) : wend : doke $ff04, 0 : poke $ff01, 1 : poke $ff00, 13 : ' Initialise Editor, sans ligne d'etat
60 while peek($ff00) : wend : c = peek($ff04)
70 r$ = "Rappels :"
80 while c <> 0
90 if c <> 255 then r$ = r$ + " " + str$(c) : ' 255 : pas de rappel, on relance seulement
100 n = deek($ff05) : gosub 1000
110 wend
120 print r$;" 0"
130 end
1000 ' repond au rappel c, relance l'editeur (13,2) ; rappel suivant dans c
1010 while peek($ff00) : wend
1020 if c = 1 then doke $ff04, 3 : ' combien de lignes ?
1030 if c = 2 then gosub 1200 : doke $ff04, t : ' la ligne n, dans le tampon t
1040 if c = 3 then poke $ff04, 27 : if q < nt then poke $ff04, peek(kk + q) : q = q + 1 : ' une touche
1050 if c = 4 then gosub 1300 : ' la ligne n a change : le tampon la contient
1060 while peek($ff00) : wend : poke $ff01, 2 : poke $ff00, 13 : ' Reenter the Editor
1070 while peek($ff00) : wend : c = peek($ff04)
1080 return
1200 ' ligne n du document (41 octets par ligne en d) vers le tampon t ; apres la fin : ligne vide
1210 poke t, 0 : if n > 3 then return
1220 a = d + (n - 1) * 41 : for j = 0 to peek(a) : poke t + j, peek(a + j) : next j
1230 return
1300 ' tampon t vers la ligne n du document, borne a 40 caracteres
1310 if n > 3 then return
1320 m = peek(t) : if m > 40 then m = 40
1330 a = d + (n - 1) * 41 : poke a, m : for j = 1 to m : poke a + j, peek(t + j) : next j
1340 return
1400 ' range l$ dans la ligne i du document
1410 a = d + i * 41 : poke a, len(l$) : for j = 1 to len(l$) : poke a + j, asc(mid$(l$, j, 1)) : next j
1420 return
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Rappels : 1 2 2 2 2 3 0
Assembleur 65C02 (64tass)
; Suivre les rappels d'une session d'édition courte (groupe 13, fonction 1).
        .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

NT = 1
start:  jsr demarrer
_b:     cmp #$FF                   ; $FF : pas de rappel, on relance seulement
        beq _r
        ldx nv
        sta vus,x
        inc nv
        cmp #0                     ; fin de l'édition
        beq _fin
_r:     jsr repondre
        bra _b
_fin:   lda #<t0
        ldx #>t0
        jsr ecrire
        ldx #0
_a:     lda #' '
        jsr WriteCharacter
        lda vus,x
        jsr decimal
        inx
        cpx nv
        bne _a
        lda #13
        jmp WriteCharacter

touches: .byte $1B                 ; Échap tout de suite (au lieu du clavier, 2,1)
t0:     .text "Rappels :", 0
nv:     .byte 0
vus:    .fill 16

; Démarre l'éditeur (13,1) sans ligne d'état (P0-P1 = 0) ; premier rappel dans A.
demarrer:
        jsr WaitMessage
        stz ControlPort+4
        stz ControlPort+5
        lda #13
        ldx #1                     ; 13,1 : Initialise Editor
        jsr appel
        lda ControlPort+4
        rts

; Répond au rappel A, puis relance l'éditeur (13,2) ; rappel suivant dans A.
repondre:
        sta code
        lda ControlPort+5          ; numéro de ligne (codes 2 et 4), P1-P2
        sta nl
        lda ControlPort+6
        sta nh
        jsr WaitMessage
        lda code
        cmp #1
        bne _c2
        lda #3                     ; combien de lignes ? 3
        sta ControlPort+4
        stz ControlPort+5
        bra _relance
_c2:    cmp #2
        bne _c3
        stz tampon                 ; après la fin : ligne vide
        jsr adresse                ; ligne n : adresse dans ptr, C = 1 si n hors du document
        bcs _t
        lda (ptr)                  ; longueur + 1 octets à copier
        tay
_copie: lda (ptr),y
        sta tampon,y
        dey
        bpl _copie
_t:     lda #<tampon               ; P0-P1 : adresse du tampon
        sta ControlPort+4
        lda #>tampon
        sta ControlPort+5
        bra _relance
_c3:    cmp #3
        bne _c4
        lda #$1B                   ; au-delà de la liste : Échap
        ldx t
        cpx #NT
        bcs _k
        lda touches,x
        inc t
_k:     sta ControlPort+4          ; P0 : la touche
        bra _relance
_c4:    cmp #4
        bne _relance
        jsr adresse                ; la ligne n a changé : le tampon la contient
        bcs _relance
        lda tampon
        cmp #41
        bcc _lg
        lda #40                    ; copie bornée à 40 caractères
_lg:    tay
        sta (ptr)
_rg:    cpy #0
        beq _relance
        lda tampon,y
        sta (ptr),y
        dey
        bra _rg
_relance:
        lda #13
        ldx #2                     ; 13,2 : Reenter the Editor
        jsr appel
        lda ControlPort+4
        rts

; Adresse de la ligne n (nl, nh) du document dans ptr ; C = 1 si n n'est pas 1, 2 ou 3.
adresse:
        lda nh
        bne _hors
        ldx nl
        beq _hors
        cpx #4
        bcs _hors
        dex
        txa
        asl a
        tax
        lda lignes,x
        sta ptr
        lda lignes+1,x
        sta ptr+1
        clc
        rts
_hors:  sec
        rts

code:   .byte 0
nl:     .byte 0
nh:     .byte 0
t:      .byte 0
lignes: .word doc0, doc1, doc2
doc0:   .byte 2
        .text "Un"
        .fill 38
doc1:   .byte 4
        .text "Deux"
        .fill 36
doc2:   .byte 5
        .text "Trois"
        .fill 35
tampon: .fill 256                  ; ligne échangée avec l'éditeur (longueur, puis texte)

; 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

; 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
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Rappels : 1 2 2 2 2 3 0

13,2 Reenter the Editor Officiel

Relance l'éditeur après la réponse du programme à un rappel ; rend le rappel suivant.

Entrées
OctetsTypeRôle
P0-P1uint16réponse au rappel précédent : nombre de lignes (1), adresse du tampon (2), touche en P0 (3)
Sorties
OctetsTypeRôle
P0uint8rappel suivant (voir l'introduction)
P1-P2uint16numéro de ligne, pour les rappels 2, 4, 5 et 6

Le tampon donné pour la ligne éditée doit pouvoir recevoir 256 octets : l'éditeur y réécrit la ligne quand elle a changé (rappel 4), avant de passer à une autre ligne ou de sortir. Le curseur peut aller une ligne après la fin du document : le programme doit alors fournir une ligne vide et, si elle change, l'ajouter. À la sortie, l'éditeur efface l'écran. L'exemple édite « Un », « Deux », « Trois » avec les touches X, bas, Fin, !, Échap : deux lignes sont rangées. NeoBASIC n'a pas de mot-clé pour cet appel.

Texte de la documentation du firmware (anglais)

Re-enters the system editor. Returns the function required for call out, the editors sort of 'call backs' - see editor specification.

C (llvm-mos)
/* Éditer un petit document avec l'éditeur du firmware (groupe 13, fonction 2). */
#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)
        ;
}

/* Le document édité : trois lignes, chaînes précédées de leur longueur (le format de l'éditeur). */
static uint8_t doc[3][41] = {"\x02Un", "\x04" "Deux", "\x05Trois"};
static uint8_t tampon[256];                     /* ligne échangée avec l'éditeur */
static uint8_t touches[] = {'X', 0x13, 0x07, '!', 0x1B};   /* X, bas, Fin, !, Échap (au lieu du clavier, 2,1) */
static uint8_t t;

/* Répond au rappel code, puis relance l'éditeur (13,2) ; rend le rappel suivant. */
static uint8_t repondre(uint8_t code) {
    uint16_t n = ControlPort.params[1] | ControlPort.params[2] << 8;   /* numéro de ligne (codes 2 et 4) */
    libre();
    if (code == 1) {                            /* combien de lignes ? */
        ControlPort.params[0] = 3;
        ControlPort.params[1] = 0;
    } else if (code == 2) {                     /* la ligne n (à partir de 1), dans un tampon */
        uint8_t *l = n <= 3 ? doc[n - 1] : (uint8_t *)"";   /* après la fin : ligne vide */
        for (uint8_t i = 0; i <= l[0]; i++)
            tampon[i] = l[i];
        ControlPort.params[0] = (uint16_t)tampon & 0xFF;
        ControlPort.params[1] = (uint16_t)tampon >> 8;
    } else if (code == 3) {                     /* une touche (0 : aucune) */
        ControlPort.params[0] = t < sizeof touches ? touches[t++] : 0x1B;
    } else if (code == 4 && n <= 3) {           /* la ligne n a changé : le tampon la contient */
        for (uint8_t i = 0; i <= tampon[0] && i < 41; i++)
            doc[n - 1][i] = tampon[i];
    }
    api(13, 2);                                 /* Reenter the Editor */
    return ControlPort.params[0];
}

static uint8_t demarrer(void) {                 /* 13,1 : sans ligne d'état (P0-P1 = 0) */
    libre();
    ControlPort.params[0] = 0;
    ControlPort.params[1] = 0;
    api(13, 1);                                 /* Initialise Editor */
    return ControlPort.params[0];
}

int main(void) {
    uint8_t ranges = 0;
    uint8_t code = demarrer();                  /* 13,1 */
    while (code != 0) {                         /* 0 : l'utilisateur a quitté (Échap) */
        if (code == 4)
            ranges++;
        code = repondre(code);                  /* 13,2 */
    }
    for (uint8_t i = 0; i < 3; i++) {
        doc[i][doc[i][0] + 1] = 0;
        printf("Ligne %u : %s\n", i + 1, (char *)doc[i] + 1);
    }
    printf("Lignes rangees : %u\n", ranges);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Ligne 1 : XUn
Ligne 2 : Deux!
Ligne 3 : Trois
Lignes rangees : 2
Mad Pascal
// Éditer un petit document avec l'éditeur du firmware (groupe 13, fonction 2).
program editeur2;
uses crt, neo6502;
const NT = 5;
      touches: array[0..NT - 1] of byte = (ord('X'), $13, $07, ord('!'), 27);   // X, bas, Fin, !, Échap (au lieu du clavier, 2,1)
var doc: array[0..2, 0..40] of byte;            // le document : longueur, puis texte (le format de l'éditeur)
    tampon: array[0..255] of byte;              // ligne échangée avec l'éditeur
    t: byte;

// Répond au rappel code, puis relance l'éditeur (13,2) ; rend le rappel suivant.
function repondre(code: byte): byte;
var n: word;
    i, k: byte;
begin
  n := NeoMessage.params[1] + NeoMessage.params[2] shl 8;   // numéro de ligne (codes 2 et 4)
  NeoWaitMessage;
  if code = 1 then                              // combien de lignes ?
    wordParams[0] := 3
  else if code = 2 then begin                   // la ligne n (à partir de 1), dans un tampon
    tampon[0] := 0;                             // après la fin : ligne vide
    if n <= 3 then
      for i := 0 to doc[n - 1, 0] do tampon[i] := doc[n - 1, i];
    wordParams[0] := word(@tampon);
  end else if code = 3 then begin               // une touche (0 : aucune)
    if t < NT then begin NeoMessage.params[0] := touches[t]; inc(t); end
    else NeoMessage.params[0] := $1B;
  end else if (code = 4) and (n <= 3) then begin   // la ligne n a changé : le tampon la contient
    k := tampon[0];
    if k > 40 then k := 40;                     // copie bornée à la taille d'une ligne du document
    doc[n - 1, 0] := k;
    for i := 1 to k do doc[n - 1, i] := tampon[i];
  end;
  NeoReenterEditor;                             // Reenter the Editor
  NeoWaitMessage;
  result := NeoMessage.params[0];
end;

function demarrer: byte;                        // 13,1 : sans ligne d'état (P0-P1 = 0)
begin
  NeoWaitMessage;
  wordParams[0] := 0;
  NeoInitEditor;                                // Initialise Editor
  NeoWaitMessage;
  result := NeoMessage.params[0];
end;

procedure ligne(i: byte; s: string);            // range s dans la ligne i du document
var j: byte;
begin
  doc[i, 0] := length(s);
  for j := 1 to length(s) do doc[i, j] := ord(s[j]);
end;

procedure document;
begin
  ligne(0, 'Un');
  ligne(1, 'Deux');
  ligne(2, 'Trois');
  t := 0;
end;
var ranges, code, i, k: byte;
begin
  document;
  ranges := 0;
  code := demarrer;                             // 13,1
  while code <> 0 do begin                      // 0 : l'utilisateur a quitté (Échap)
    if code = 4 then inc(ranges);
    code := repondre(code);                     // 13,2
  end;
  for i := 0 to 2 do begin
    write('Ligne ', i + 1, ' : ');
    for k := 1 to doc[i, 0] do write(chr(doc[i, k]));
    writeln;
  end;
  writeln('Lignes rangees : ', ranges);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Ligne 1 : XUn
Ligne 2 : Deux!
Ligne 3 : Trois
Lignes rangees : 2
NeoBASIC
10 ' Editer un petit document avec l'editeur du firmware (groupe 13, fonction 2)
20 d = alloc(123) : t = alloc(256) : ' document (3 lignes : longueur, puis texte) et tampon d'echange
30 i = 0 : l$ = "Un" : gosub 1400 : i = 1 : l$ = "Deux" : gosub 1400 : i = 2 : l$ = "Trois" : gosub 1400
40 kk = alloc(5) : poke kk, asc("X") : poke kk + 1, $13 : poke kk + 2, $07 : poke kk + 3, asc("!") : poke kk + 4, 27
50 nt = 5 : q = 0 : ' touches : X, bas, Fin, !, Echap (au lieu du clavier, 2,1)
60 while peek($ff00) : wend : doke $ff04, 0 : poke $ff01, 1 : poke $ff00, 13 : ' 13,1
70 while peek($ff00) : wend : c = peek($ff04) : r = 0
80 while c <> 0 : ' 0 : l'utilisateur a quitte (Echap)
90 n = deek($ff05) : ' numero de ligne (codes 2 et 4), lu avant tout calcul
100 if c = 4 then r = r + 1
110 gosub 1000
120 wend
130 for i = 0 to 2 : s$ = "" : a = d + i * 41 : for j = 1 to peek(a) : s$ = s$ + chr$(peek(a + j)) : next j
140 print "Ligne ";i + 1;" : ";s$ : next i
150 print "Lignes rangees : ";r
160 end
1000 ' repond au rappel c, relance l'editeur (13,2) ; rappel suivant dans c
1010 while peek($ff00) : wend
1020 if c = 1 then doke $ff04, 3 : ' combien de lignes ?
1030 if c = 2 then gosub 1200 : doke $ff04, t : ' la ligne n, dans le tampon t
1040 if c = 3 then poke $ff04, 27 : if q < nt then poke $ff04, peek(kk + q) : q = q + 1 : ' une touche
1050 if c = 4 then gosub 1300 : ' la ligne n a change : le tampon la contient
1060 while peek($ff00) : wend : poke $ff01, 2 : poke $ff00, 13 : ' Reenter the Editor
1070 while peek($ff00) : wend : c = peek($ff04)
1080 return
1200 ' ligne n du document (41 octets par ligne en d) vers le tampon t ; apres la fin : ligne vide
1210 poke t, 0 : if n > 3 then return
1220 a = d + (n - 1) * 41 : for j = 0 to peek(a) : poke t + j, peek(a + j) : next j
1230 return
1300 ' tampon t vers la ligne n du document, borne a 40 caracteres
1310 if n > 3 then return
1320 m = peek(t) : if m > 40 then m = 40
1330 a = d + (n - 1) * 41 : poke a, m : for j = 1 to m : poke a + j, peek(t + j) : next j
1340 return
1400 ' range l$ dans la ligne i du document
1410 a = d + i * 41 : poke a, len(l$) : for j = 1 to len(l$) : poke a + j, asc(mid$(l$, j, 1)) : next j
1420 return
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Ligne 1 : XUn
Ligne 2 : Deux!
Ligne 3 : Trois
Lignes rangees : 2
Assembleur 65C02 (64tass)
; Éditer un petit document avec l'éditeur du firmware (groupe 13, fonction 2).
        .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

NT = 5
start:  jsr demarrer               ; 13,1
_b:     cmp #0                     ; 0 : l'utilisateur a quitté (Échap)
        beq _fin
        cmp #4
        bne _r
        inc ranges
_r:     jsr repondre               ; 13,2
        bra _b
_fin:   ldx #0
_l:     phx
        lda #<t0
        ldx #>t0
        jsr ecrire
        pla
        pha
        inc a
        jsr decimal                ; numéro de ligne
        lda #<t1
        ldx #>t1
        jsr ecrire
        pla
        pha
        asl a
        tax
        lda lignes,x
        sta ptr
        lda lignes+1,x
        sta ptr+1
        lda (ptr)                  ; longueur de la ligne
        sta nb
        ldy #1
_c:     cpy nb
        beq _d
        bcs _nl
_d:     lda (ptr),y
        jsr WriteCharacter
        iny
        bra _c
_nl:    lda #13
        jsr WriteCharacter
        plx
        inx
        cpx #3
        bne _l
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda ranges
        jsr decimal
        lda #13
        jmp WriteCharacter

touches: .byte 'X', $13, $07, '!', $1B   ; X, bas, Fin, !, Échap (au lieu du clavier, 2,1)
t0:     .text "Ligne ", 0
t1:     .text " : ", 0
t2:     .text "Lignes rangees : ", 0
ranges: .byte 0
nb:     .byte 0

; Démarre l'éditeur (13,1) sans ligne d'état (P0-P1 = 0) ; premier rappel dans A.
demarrer:
        jsr WaitMessage
        stz ControlPort+4
        stz ControlPort+5
        lda #13
        ldx #1                     ; 13,1 : Initialise Editor
        jsr appel
        lda ControlPort+4
        rts

; Répond au rappel A, puis relance l'éditeur (13,2) ; rappel suivant dans A.
repondre:
        sta code
        lda ControlPort+5          ; numéro de ligne (codes 2 et 4), P1-P2
        sta nl
        lda ControlPort+6
        sta nh
        jsr WaitMessage
        lda code
        cmp #1
        bne _c2
        lda #3                     ; combien de lignes ? 3
        sta ControlPort+4
        stz ControlPort+5
        bra _relance
_c2:    cmp #2
        bne _c3
        stz tampon                 ; après la fin : ligne vide
        jsr adresse                ; ligne n : adresse dans ptr, C = 1 si n hors du document
        bcs _t
        lda (ptr)                  ; longueur + 1 octets à copier
        tay
_copie: lda (ptr),y
        sta tampon,y
        dey
        bpl _copie
_t:     lda #<tampon               ; P0-P1 : adresse du tampon
        sta ControlPort+4
        lda #>tampon
        sta ControlPort+5
        bra _relance
_c3:    cmp #3
        bne _c4
        lda #$1B                   ; au-delà de la liste : Échap
        ldx t
        cpx #NT
        bcs _k
        lda touches,x
        inc t
_k:     sta ControlPort+4          ; P0 : la touche
        bra _relance
_c4:    cmp #4
        bne _relance
        jsr adresse                ; la ligne n a changé : le tampon la contient
        bcs _relance
        lda tampon
        cmp #41
        bcc _lg
        lda #40                    ; copie bornée à 40 caractères
_lg:    tay
        sta (ptr)
_rg:    cpy #0
        beq _relance
        lda tampon,y
        sta (ptr),y
        dey
        bra _rg
_relance:
        lda #13
        ldx #2                     ; 13,2 : Reenter the Editor
        jsr appel
        lda ControlPort+4
        rts

; Adresse de la ligne n (nl, nh) du document dans ptr ; C = 1 si n n'est pas 1, 2 ou 3.
adresse:
        lda nh
        bne _hors
        ldx nl
        beq _hors
        cpx #4
        bcs _hors
        dex
        txa
        asl a
        tax
        lda lignes,x
        sta ptr
        lda lignes+1,x
        sta ptr+1
        clc
        rts
_hors:  sec
        rts

code:   .byte 0
nl:     .byte 0
nh:     .byte 0
t:      .byte 0
lignes: .word doc0, doc1, doc2
doc0:   .byte 2
        .text "Un"
        .fill 38
doc1:   .byte 4
        .text "Deux"
        .fill 36
doc2:   .byte 5
        .text "Trois"
        .fill 35
tampon: .fill 256                  ; ligne échangée avec l'éditeur (longueur, puis texte)

; 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

; 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
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Ligne 1 : XUn
Ligne 2 : Deux!
Ligne 3 : Trois
Lignes rangees : 2

← L'API du Neo6502