Tachibana
Langue : français

Groupe 10 — Entrées-sorties UEXT Officiel

10,1 UExt Initialise · 10,2 Write GPIO · 10,3 Read GPIO · 10,4 Set Port Direction · 10,5 Write I2C · 10,6 Read I2C · 10,7 Read Analog · 10,8 I2C Status · 10,9 Read I2C Block · 10,10 Write I2C Block · 10,11 Read SPI Block · 10,12 Write SPI Block · 10,13 Read UART Block · 10,14 Write UART Block · 10,15 Set UART Speed and Protocol · 10,16 Write byte to UART · 10,17 Read byte from UART · 10,18 Check if Byte Available · 10,19 Route UART to USB CDC modem

Le connecteur UEXT a 10 broches, désignées par leur numéro : 1 le 3,3 V, 2 la masse, 3 TXD et 4 RXD (port série), 5 SCL et 6 SDA (I2C), 7 MISO, 8 MOSI, 9 SCK et 10 SSEL (SPI). Les broches 3 à 10 servent aussi d'entrées-sorties simples ; 3, 4, 8 et 9 peuvent lire une tension (entrée analogique).

Utiliser l'I2C, le SPI ou régler le port série réserve leurs broches : les lire ou les écrire comme broches simples donne alors l'erreur 1, jusqu'à 10,1. Sur la carte, Trinity démarre son port de débogage sur TXD et RXD : les broches 3 et 4 lui appartiennent, même après 10,1 (dans l'émulateur, elles sont libres).

Au banc, Phosphoneo n'a rien de branché sur le port UEXT : ses réponses (niveau d'une entrée, valeur analogique, octets I2C ou SPI) sont des valeurs factices. Les exemples de ces fonctions sont vérifiés (l'appel est fait, sans erreur), mais leur sortie n'est pas reproduite. Celles qui ne dépendent que du firmware (verrou de sortie, sens, broches réservées, erreurs, port série sans rien de branché) sont publiées comme ailleurs.

10,1 UExt Initialise Officiel

Remet les entrées-sorties UEXT dans leur état de départ : broches 3 à 10 en entrée, verrous à 0, broches réservées rendues.

Entrées : aucune.

Sorties : aucune.

L'exemple écrit en I2C, ce qui réserve les broches 5 et 6 : la broche 5 est refusée (erreur 1), puis acceptée après 10,1. Les broches du port de débogage (3 et 4 sur la carte) restent réservées. En NeoBASIC, clear (et donc run) fait cet appel ; comme clear efface aussi les variables, l'exemple NeoBASIC écrit la boîte aux lettres.

Mots-clés NeoBASIC qui font cet appel : clear

Texte de la documentation du firmware (anglais)

Initialise the UExt I/O system. This resets the IO system to its default state, where all UEXT pins are I/O pins, inputs and enabled.

C (llvm-mos)
/* Libérer les broches prises par l'I2C (groupe 10, 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)
        ;
}

static uint8_t ecrire(uint8_t broche, uint8_t v) {   /* 10,2 : rend l'indicateur d'erreur */
    libre();
    ControlPort.params[0] = broche;
    ControlPort.params[1] = v;
    api(10, 2);
    return ControlPort.error;
}

int main(void) {
    neo_uext_i2c_write(0x50, 0, 0);             /* 10,5 : l'I2C prend les broches 5 (SCL) et 6 (SDA) */
    uint8_t e1 = ecrire(5, 1);
    neo_uext_init();                            /* UExt Initialise : toutes les broches en entrée, libres */
    uint8_t e2 = ecrire(5, 1);
    printf("Apres 10,5 : broche 5, erreur %u\n", e1);
    printf("Apres 10,1 : broche 5, erreur %u\n", e2);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Apres 10,5 : broche 5, erreur 1
Apres 10,1 : broche 5, erreur 0
Mad Pascal
// Libérer les broches prises par l'I2C (groupe 10, fonction 1).
program uext1;
uses crt, neo6502, neo6502uext;
var e1, e2: byte;

function ecrire(broche, v: byte): byte;         // 10,2 : rend l'indicateur d'erreur
begin
  NeoWriteGPIO(broche, v);
  NeoWaitMessage;
  result := NeoMessage.error;
end;

begin
  NeoWriteI2C($50, 0, 0);                        // 10,5 : l'I2C prend les broches 5 (SCL) et 6 (SDA)
  e1 := ecrire(5, 1);
  NeoUExtInitialize;                            // UExt Initialise : toutes les broches en entrée, libres
  e2 := ecrire(5, 1);
  writeln('Apres 10,5 : broche 5, erreur ', e1);
  writeln('Apres 10,1 : broche 5, erreur ', e2);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Apres 10,5 : broche 5, erreur 1
Apres 10,1 : broche 5, erreur 0
NeoBASIC
10 ' Liberer les broches prises par l'I2C (groupe 10, fonction 1)
20 iwrite $50, 0, 0 : ' 10,5 : l'I2C prend les broches 5 (SCL) et 6 (SDA)
30 b = 5 : v = 1 : gosub 1000 : e1 = e
40 while peek($ff00) : wend : poke $ff01, 1 : poke $ff00, 10 : ' UExt Initialise (clear le fait aussi, mais efface les variables)
50 gosub 1000 : e2 = e
60 print "Apres 10,5 : broche 5, erreur ";e1
70 print "Apres 10,1 : broche 5, erreur ";e2
80 end
1000 ' 10,2 : ecrire v sur la broche b ; erreur dans e
1010 while peek($ff00) : wend : poke $ff04, b : poke $ff05, v : poke $ff01, 2 : poke $ff00, 10
1020 while peek($ff00) : wend : e = peek($ff02)
1030 return
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Apres 10,5 : broche 5, erreur 1
Apres 10,1 : broche 5, erreur 0
Assembleur 65C02 (64tass)
; Libérer les broches prises par l'I2C (groupe 10, 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

start: lda #<i2c
        ldx #>i2c
        jsr envoi
        lda #<ecr5
        ldx #>ecr5
        jsr envoi
        lda ControlPort+2
        sta e1
        lda #<init
        ldx #>init
        jsr envoi
        lda #<ecr5
        ldx #>ecr5
        jsr envoi
        lda ControlPort+2
        sta e2
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e1
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda e2
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

i2c:    .byte 10, 5, 3, $50, 0, 0  ; 10,5 : l'I2C prend les broches 5 (SCL) et 6 (SDA)
ecr5: .byte 10, 2, 2, 5, 1       ; 10,2 : broche 5, valeur 1
init:   .byte 10, 1, 0             ; 10,1 : UExt Initialise
t0:     .text "Apres 10,5 : broche 5, erreur ", 0
t1:     .text "Apres 10,1 : broche 5, erreur ", 0
e1:     .byte 0
e2:     .byte 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

; 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 :
Apres 10,5 : broche 5, erreur 1
Apres 10,1 : broche 5, erreur 0

10,2 Write GPIO Officiel

Écrit le verrou de sortie d'une broche ; il n'apparaît sur la broche que si elle est en sortie.

Entrées
OctetsTypeRôle
P0uint8broche (3 à 10)
P1uint80 bas, autre valeur haut
Sorties
OctetsTypeRôle
erreuruint81 si la broche n'existe pas ou est réservée

Le verrou garde la valeur même en entrée : repasser la broche en sortie (10,4) la remet sur la broche. En sortie, 10,3 relit le verrou : l'exemple relit donc 255 puis 0, sans rien brancher. La broche 2 (la masse) est refusée. En NeoBASIC, pin n, v fait cet appel (et pin(n) rend 1 ou 0, pas 255 ou 0).

Mots-clés NeoBASIC qui font cet appel : pin

Texte de la documentation du firmware (anglais)

This copies the value Parameter:1 to the output latch for UEXT pin Parameter:0. This will only display on the output pin if it is enabled, and its direction is set to "Output" direction.

C (llvm-mos)
/* Écrire le verrou de sortie d'une broche (groupe 10, 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)
        ;
}

static uint8_t ecrire(uint8_t broche, uint8_t v) {   /* Write GPIO : rend l'indicateur d'erreur */
    libre();
    ControlPort.params[0] = broche;
    ControlPort.params[1] = v;
    api(10, 2);
    return ControlPort.error;
}

int main(void) {
    neo_uext_init();                            /* 10,1 */
    neo_uext_gpio_set_direction(10, NEO_UEXT_OUTPUT);   /* 10,4 : broche 10 en sortie */
    ecrire(10, 1);
    uint8_t haut = neo_uext_gpio_read(10);      /* 10,3 : en sortie, on relit le verrou */
    ecrire(10, 0);
    uint8_t bas = neo_uext_gpio_read(10);
    uint8_t e = ecrire(2, 1);                   /* la broche 2 du connecteur est la masse */
    neo_uext_init();                            /* tout remettre en entrée */
    printf("Broche 10 : %u puis %u\n", haut, bas);
    printf("Broche 2 : erreur %u\n", e);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Broche 10 : 255 puis 0
Broche 2 : erreur 1
Mad Pascal
// Écrire le verrou de sortie d'une broche (groupe 10, fonction 2).
program uext2;
uses crt, neo6502, neo6502uext;
var haut, bas, e: byte;

function ecrire(broche, v: byte): byte;         // 10,2 : rend l'indicateur d'erreur
begin
  NeoWriteGPIO(broche, v);
  NeoWaitMessage;
  result := NeoMessage.error;
end;

begin
  NeoUExtInitialize;                            // 10,1
  NeoSetDirection(10, GPIO_PINMODE_OUTPUT);     // 10,4 : broche 10 en sortie
  ecrire(10, 1);                                // Write GPIO
  haut := NeoReadGPIO(10);                      // 10,3 : en sortie, on relit le verrou
  ecrire(10, 0);
  bas := NeoReadGPIO(10);
  e := ecrire(2, 1);                            // la broche 2 du connecteur est la masse
  NeoUExtInitialize;                            // tout remettre en entrée
  writeln('Broche 10 : ', haut, ' puis ', bas);
  writeln('Broche 2 : erreur ', e);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Broche 10 : 255 puis 0
Broche 2 : erreur 1
NeoBASIC
10 ' Ecrire le verrou de sortie d'une broche (groupe 10, fonction 2)
20 pin 10, output : ' 10,4 : broche 10 en sortie
30 pin 10, 1 : h = pin(10) : ' Write GPIO, puis 10,3 : en sortie, on relit le verrou
40 pin 10, 0 : l = pin(10)
50 b = 2 : v = 1 : gosub 1000 : ' la broche 2 du connecteur est la masse
60 pin 10, input : ' remettre en entree
70 print "Broche 10 : ";h;" puis ";l
80 print "Broche 2 : erreur ";e
90 end
1000 ' 10,2 : ecrire v sur la broche b ; erreur dans e
1010 while peek($ff00) : wend : poke $ff04, b : poke $ff05, v : poke $ff01, 2 : poke $ff00, 10
1020 while peek($ff00) : wend : e = peek($ff02)
1030 return
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Broche 10 : 1 puis 0
Broche 2 : erreur 1
Assembleur 65C02 (64tass)
; Écrire le verrou de sortie d'une broche (groupe 10, 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

start: lda #<init
        ldx #>init
        jsr envoi
        lda #<sortie
        ldx #>sortie
        jsr envoi
        lda #<haut
        ldx #>haut
        jsr envoi
        lda #10
        jsr lire
        sta h
        lda #<bas
        ldx #>bas
        jsr envoi
        lda #10
        jsr lire
        sta l
        lda #<masse
        ldx #>masse
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<init
        ldx #>init
        jsr envoi
                                   ; tout remettre en entrée
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda h
        jsr decimal
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda l
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

; 10,3 : niveau de la broche A (0 ou 255), rendu dans A.
lire:   pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #10
        ldx #3                     ; 10,3 : Read GPIO
        jsr appel
        lda ControlPort+4
        rts

init:   .byte 10, 1, 0             ; 10,1 : UExt Initialise
sortie: .byte 10, 4, 2, 10, 2      ; 10,4 : broche 10 en sortie
haut: .byte 10, 2, 2, 10, 1      ; 10,2 : broche 10, valeur 1 (Write GPIO)
bas: .byte 10, 2, 2, 10, 0      ; 10,2 : broche 10, valeur 0
masse: .byte 10, 2, 2, 2, 1       ; 10,2 : broche 2, valeur 1 : la broche 2 du connecteur est la masse
t0:     .text "Broche 10 : ", 0
t1:     .text " puis ", 0
t2:     .text "Broche 2 : erreur ", 0
h:      .byte 0
l:      .byte 0
e:      .byte 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

; 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 :
Broche 10 : 255 puis 0
Broche 2 : erreur 1

10,3 Read GPIO Officiel

Lit une broche : son niveau si elle est en entrée, son verrou si elle est en sortie.

Entrées
OctetsTypeRôle
P0uint8broche (3 à 10)
Sorties
OctetsTypeRôle
P0uint80 bas, 255 ($FF) haut
erreuruint81 si la broche n'existe pas ou est réservée

Une entrée en l'air (rien de branché) peut se lire haute ou basse. En NeoBASIC, pin(n) fait cet appel et rend 1 ou 0.

Mots-clés NeoBASIC qui font cet appel : pin()

Texte de la documentation du firmware (anglais)

If the pin is set to "Input" direction, reads the level on pin on UEXT port Parameter:0. If it is set to "Output" direction, reads the output latch for pin on UEXT port Parameter:0. If the read is successful, the result will be in Parameter:0.

C (llvm-mos)
/* Lire le niveau d'une broche en entrée (groupe 10, fonction 3). */
#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)
        ;
}

int main(void) {
    neo_uext_init();                            /* 10,1 : toutes les broches en entrée */
    uint8_t v = neo_uext_gpio_read(7);          /* Read GPIO : 0 (bas) ou 255 (haut) */
    printf("Broche 7 : %s\n", v ? "haut" : "bas");
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Lire le niveau d'une broche en entrée (groupe 10, fonction 3).
program uext3;
uses crt, neo6502, neo6502uext;
var v: byte;
begin
  NeoUExtInitialize;                            // 10,1 : toutes les broches en entrée
  v := NeoReadGPIO(7);                          // Read GPIO : 0 (bas) ou 255 (haut)
  if v <> 0 then writeln('Broche 7 : haut') else writeln('Broche 7 : bas');
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Lire le niveau d'une broche en entree (groupe 10, fonction 3)
20 v = pin(7) : ' Read GPIO (au lancement, run remet les broches en entree)
30 n$ = "bas" : if v then n$ = "haut"
40 print "Broche 7 : ";n$

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Lire le niveau d'une broche en entrée (groupe 10, fonction 3).
        .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 #<init
        ldx #>init
        jsr envoi
        lda #7
        jsr lire                   ; Read GPIO : 0 (bas) ou 255 (haut)
        bne _haut
        lda #<tb
        ldx #>tb
        jsr ecrire
        rts
_haut:   lda #<th
        ldx #>th
        jsr ecrire
        rts

; 10,3 : niveau de la broche A (0 ou 255), rendu dans A.
lire:   pha
        jsr WaitMessage
        pla
        sta ControlPort+4
        lda #10
        ldx #3                     ; 10,3 : Read GPIO
        jsr appel
        lda ControlPort+4
        rts

init:   .byte 10, 1, 0             ; 10,1 : UExt Initialise
tb:     .text "Broche 7 : bas", 13, 0
th:     .text "Broche 7 : haut", 13, 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,4 Set Port Direction Officiel

Choisit le sens d'une broche : entrée, sortie ou entrée analogique.

Entrées
OctetsTypeRôle
P0uint8broche (3 à 10)
P1uint81 entrée, 2 sortie, 3 entrée analogique
Sorties
OctetsTypeRôle
erreuruint81 si la broche n'existe pas, est réservée, ou n'a pas d'entrée analogique

Seules les broches 3, 4, 8 et 9 ont une entrée analogique : la broche 10 est refusée, la 8 acceptée. Passer en sortie met aussitôt le verrou (10,2) sur la broche. En NeoBASIC, pin n, input, pin n, output et pin n, analog font cet appel (avec une virgule) et lèvent une erreur de plage en cas d'échec.

Mots-clés NeoBASIC qui font cet appel : pin

Texte de la documentation du firmware (anglais)

Set the port direction for UEXT Port Parameter:0 to the value in Parameter:1. This can be $01 (Input), $02 (Output), or $03 (Analogue Input).

C (llvm-mos)
/* Choisir le sens d'une broche (groupe 10, fonction 4). */
#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)
        ;
}

static uint8_t sens(uint8_t broche, uint8_t s) {    /* Set Port Direction : rend l'indicateur d'erreur */
    libre();
    ControlPort.params[0] = broche;
    ControlPort.params[1] = s;                  /* 1 entrée, 2 sortie, 3 entrée analogique */
    api(10, 4);
    return ControlPort.error;
}

int main(void) {
    neo_uext_init();                            /* 10,1 */
    uint8_t a = sens(10, 3);                    /* broche 10 : pas d'entrée analogique */
    uint8_t b = sens(8, 3);                     /* broche 8 : analogique possible */
    uint8_t c = sens(10, 2);
    neo_uext_init();                            /* tout remettre en entrée */
    printf("Broche 10 en analogique : erreur %u\n", a);
    printf("Broche 8 en analogique : erreur %u\n", b);
    printf("Broche 10 en sortie : erreur %u\n", c);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Broche 10 en analogique : erreur 1
Broche 8 en analogique : erreur 0
Broche 10 en sortie : erreur 0
Mad Pascal
// Choisir le sens d'une broche (groupe 10, fonction 4).
program uext4;
uses crt, neo6502, neo6502uext;
var a, b, c: byte;

function sens(broche, s: byte): byte;           // Set Port Direction : rend l'indicateur d'erreur
begin
  NeoSetDirection(broche, s);                   // 1 entrée, 2 sortie, 3 entrée analogique
  NeoWaitMessage;
  result := NeoMessage.error;
end;

begin
  NeoUExtInitialize;                            // 10,1
  a := sens(10, GPIO_PINMODE_ANALOGUE);         // broche 10 : pas d'entrée analogique
  b := sens(8, GPIO_PINMODE_ANALOGUE);          // broche 8 : analogique possible
  c := sens(10, GPIO_PINMODE_OUTPUT);
  NeoUExtInitialize;                            // tout remettre en entrée
  writeln('Broche 10 en analogique : erreur ', a);
  writeln('Broche 8 en analogique : erreur ', b);
  writeln('Broche 10 en sortie : erreur ', c);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Broche 10 en analogique : erreur 1
Broche 8 en analogique : erreur 0
Broche 10 en sortie : erreur 0
NeoBASIC
10 ' Choisir le sens d'une broche (groupe 10, fonction 4)
20 b = 10 : s = 3 : gosub 1000 : a = e : ' broche 10 : pas d'entree analogique
30 b = 8 : s = 3 : gosub 1000 : c8 = e : ' broche 8 : analogique possible
40 b = 10 : s = 2 : gosub 1000 : c10 = e
50 pin 8, input : pin 10, input : ' remettre en entree
60 print "Broche 10 en analogique : erreur ";a
70 print "Broche 8 en analogique : erreur ";c8
80 print "Broche 10 en sortie : erreur ";c10
90 end
1000 ' Set Port Direction : broche b, sens s (1 entree, 2 sortie, 3 analogique) ; erreur dans e
1010 while peek($ff00) : wend : poke $ff04, b : poke $ff05, s : poke $ff01, 4 : poke $ff00, 10
1020 while peek($ff00) : wend : e = peek($ff02)
1030 return
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Broche 10 en analogique : erreur 1
Broche 8 en analogique : erreur 0
Broche 10 en sortie : erreur 0
Assembleur 65C02 (64tass)
; Choisir le sens d'une broche (groupe 10, fonction 4).
        .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 #<init
        ldx #>init
        jsr envoi
        lda #<s10a
        ldx #>s10a
        jsr envoi
        lda ControlPort+2
        sta a
        lda #<s8a
        ldx #>s8a
        jsr envoi
        lda ControlPort+2
        sta b
        lda #<s10s
        ldx #>s10s
        jsr envoi
        lda ControlPort+2
        sta c
        lda #<init
        ldx #>init
        jsr envoi
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda a
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda b
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda c
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

init:   .byte 10, 1, 0             ; 10,1 : UExt Initialise
s10a: .byte 10, 4, 2, 10, 3       ; 10,4 : broche 10, analogique : impossible
s8a: .byte 10, 4, 2, 8, 3        ; 10,4 : broche 8, analogique : possible
s10s: .byte 10, 4, 2, 10, 2       ; 10,4 : broche 10, sortie
t0:     .text "Broche 10 en analogique : erreur ", 0
t1:     .text "Broche 8 en analogique : erreur ", 0
t2:     .text "Broche 10 en sortie : erreur ", 0
a:      .byte 0
b:      .byte 0
c:      .byte 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

; 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 :
Broche 10 en analogique : erreur 1
Broche 8 en analogique : erreur 0
Broche 10 en sortie : erreur 0

10,5 Write I2C Officiel

Écrit un octet dans un registre d'un périphérique I2C.

Entrées
OctetsTypeRôle
P0uint8adresse du périphérique (7 bits)
P1uint8registre
P2uint8valeur
Sorties
OctetsTypeRôle
erreuruint81 si le transfert n'est pas complet

La documentation du firmware dit qu'aucune erreur n'est signalée si le périphérique est absent ; le code de la carte rend pourtant 1 quand l'écriture n'aboutit pas, ce qui est le cas sans périphérique (non vérifié sur la carte). Le premier appel I2C réserve les broches 5 et 6. En NeoBASIC, iwrite dev, reg, val fait cet appel.

Mots-clés NeoBASIC qui font cet appel : iwrite

Texte de la documentation du firmware (anglais)

Write to I2C Device Parameter:0, Register Parameter:1, value Parameter:2. No error is flagged if the device is not present.

C (llvm-mos)
/* Écrire un registre d'un périphérique I2C (groupe 10, fonction 5). */
#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)
        ;
}

int main(void) {
    libre();
    ControlPort.params[0] = 0x50;               /* P0 : adresse du périphérique (une EEPROM 24C02, par exemple) */
    ControlPort.params[1] = 0x00;               /* P1 : registre */
    ControlPort.params[2] = 0x42;               /* P2 : valeur */
    api(10, 5);                                 /* Write I2C */
    printf("Ecriture en $50 : erreur %u\n", ControlPort.error);
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Écrire un registre d'un périphérique I2C (groupe 10, fonction 5).
program uext5;
uses crt, neo6502, neo6502uext;
var e: byte;
begin
  NeoWriteI2C($50, $00, $42);                   // Write I2C : périphérique $50 (une EEPROM 24C02, par exemple), registre 0, valeur $42
  NeoWaitMessage;
  e := NeoMessage.error;
  writeln('Ecriture en $50 : erreur ', e);
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Ecrire un registre d'un peripherique I2C (groupe 10, fonction 5)
20 iwrite $50, 0, $42 : ' Write I2C : peripherique $50 (une EEPROM 24C02, par exemple), registre 0, valeur $42
30 e = peek($ff02)
40 print "Ecriture en $50 : erreur ";e

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Écrire un registre d'un périphérique I2C (groupe 10, fonction 5).
        .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 #<reg
        ldx #>reg
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

reg:    .byte 10, 5, 3, $50, 0, $42   ; Write I2C : périphérique $50 (une EEPROM 24C02, par exemple), registre 0, valeur $42
t0:     .text "Ecriture en $50 : erreur ", 0
e:      .byte 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,6 Read I2C Officiel

Lit un octet dans un registre d'un périphérique I2C.

Entrées
OctetsTypeRôle
P0uint8adresse du périphérique
P1uint8registre
Sorties
OctetsTypeRôle
P0uint8valeur lue
erreuruint81 si le périphérique ne répond pas

L'appel écrit le numéro de registre, puis lit un octet. L'exemple vérifie d'abord la présence du périphérique (10,8). En NeoBASIC, iread(dev, reg) fait cet appel.

Mots-clés NeoBASIC qui font cet appel : iread()

Texte de la documentation du firmware (anglais)

Read from I2C Device Parameter:0, Register Parameter:1. If the read is successful, the result will be in Parameter:0. If the device is not present, this will flag an error. Use FUNCTION 10,2 first, to check for its presence.

C (llvm-mos)
/* Lire un registre d'un périphérique I2C (groupe 10, fonction 6). */
#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)
        ;
}

int main(void) {
    if (!neo_uext_i2c_status(0x50)) {           /* 10,8 : périphérique présent ? */
        printf("Rien en $50\n");
        return 0;
    }
    uint8_t v = neo_uext_i2c_read(0x50, 0x00);  /* Read I2C : registre 0 */
    printf("Registre 0 de $50 : %u\n", v);
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Lire un registre d'un périphérique I2C (groupe 10, fonction 6).
program uext6;
uses crt, neo6502, neo6502uext;
var v: byte;
begin
  if NeoCheckI2C($50) = 0 then                  // 10,8 : périphérique présent ?
    writeln('Rien en $50')
  else begin
    v := NeoReadI2C($50, $00);                  // Read I2C : registre 0
    writeln('Registre 0 de $50 : ', v);
  end;
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Lire un registre d'un peripherique I2C (groupe 10, fonction 6)
20 if idevice($50) = 0 then print "Rien en $50" : end : ' 10,8 : peripherique present ?
30 v = iread($50, 0) : ' Read I2C : registre 0
40 print "Registre 0 de $50 : ";v

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Lire un registre d'un périphérique I2C (groupe 10, fonction 6).
        .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 #<etat
        ldx #>etat
        jsr envoi
        lda ControlPort+4          ; non nul : présent
        bne _la
        lda #<t0
        ldx #>t0
        jsr ecrire
        rts
_la:    lda #<reg
        ldx #>reg
        jsr envoi
        lda ControlPort+4
        sta v
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda v
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

etat:   .byte 10, 8, 1, $50        ; 10,8 : périphérique $50 présent ?
reg:    .byte 10, 6, 2, $50, 0     ; Read I2C : registre 0
t0:     .text "Rien en $50", 13, 0
t1:     .text "Registre 0 de $50 : ", 0
v:      .byte 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,7 Read Analog Officiel

Lit la tension d'une broche réglée en entrée analogique, de 0 à 4095 pour 0 à 3,3 V.

Entrées
OctetsTypeRôle
P0uint8broche (3, 4, 8 ou 9)
Sorties
OctetsTypeRôle
P0-P1uint16valeur, 0 à 4095
erreuruint81 broche sans entrée analogique, 2 broche pas réglée en analogique (10,4)

En millivolts : valeur × 3300 / 4095. L'exemple montre d'abord les deux erreurs (2 sur la broche 8 pas encore réglée, 1 sur la broche 10), vérifiées au banc ; la valeur lue, elle, dépend de ce qui est branché. En NeoBASIC, analog(n) fait cet appel.

Mots-clés NeoBASIC qui font cet appel : analog()

Texte de la documentation du firmware (anglais)

Read the analogue value on UEXT Pin Parameter:0. This has to be set to analogue type to work. Returns a value from 0..4095 stored in Parameters:0,1, which represents an input value of 0 to 3.3 volts.

C (llvm-mos)
/* Lire une entrée analogique (groupe 10, fonction 7). */
#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)
        ;
}

static uint16_t valeur;

static uint8_t analogique(uint8_t broche) {     /* Read Analog : valeur dans valeur, erreur rendue */
    libre();
    ControlPort.params[0] = broche;
    api(10, 7);
    valeur = ControlPort.params[0] | ControlPort.params[1] << 8;
    return ControlPort.error;
}

int main(void) {
    neo_uext_init();                            /* 10,1 : broches en entrée numérique */
    uint8_t e1 = analogique(8);                 /* pas encore réglée en analogique */
    uint8_t e2 = analogique(10);                /* pas d'entrée analogique sur la broche 10 */
    neo_uext_gpio_set_direction(8, NEO_UEXT_ANALOGUE_INPUT);   /* 10,4 */
    analogique(8);
    neo_uext_init();
    printf("Broche 8 non reglee : erreur %u\n", e1);
    printf("Broche 10 : erreur %u\n", e2);
    printf("Broche 8 : %u sur 4095\n", valeur);   /* 4095 : 3,3 V */
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Lire une entrée analogique (groupe 10, fonction 7).
program uext7;
uses crt, neo6502, neo6502uext;
var e1, e2: byte;
    valeur: word;

function analogique(broche: byte): byte;        // Read Analog : valeur dans valeur, erreur rendue
begin
  NeoReadAnalog(broche);
  NeoWaitMessage;                               // attendre la fin de l'appel, puis relire P0-P1
  valeur := NeoMessage.params[0] + NeoMessage.params[1] shl 8;
  result := NeoMessage.error;
end;

begin
  NeoUExtInitialize;                            // 10,1 : broches en entrée numérique
  e1 := analogique(8);                          // pas encore réglée en analogique
  e2 := analogique(10);                         // pas d'entrée analogique sur la broche 10
  NeoSetDirection(8, GPIO_PINMODE_ANALOGUE);    // 10,4
  analogique(8);
  NeoUExtInitialize;
  writeln('Broche 8 non reglee : erreur ', e1);
  writeln('Broche 10 : erreur ', e2);
  writeln('Broche 8 : ', valeur, ' sur 4095');   // 4095 : 3,3 V
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Lire une entree analogique (groupe 10, fonction 7)
20 b = 8 : gosub 1000 : e1 = e : ' pas encore reglee en analogique
30 b = 10 : gosub 1000 : e2 = e : ' pas d'entree analogique sur la broche 10
40 pin 8, analog : ' 10,4
50 v = analog(8) : ' Read Analog, 0-4095 pour 0 a 3,3 V
70 pin 8, input
80 print "Broche 8 non reglee : erreur ";e1
90 print "Broche 10 : erreur ";e2
100 print "Broche 8 : ";v;" sur 4095"
110 end
1000 ' 10,7 sur la broche b ; erreur dans e
1010 while peek($ff00) : wend : poke $ff04, b : poke $ff01, 7 : poke $ff00, 10
1020 while peek($ff00) : wend : e = peek($ff02)
1030 return

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Lire une entrée analogique (groupe 10, fonction 7).
        .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 #<init
        ldx #>init
        jsr envoi
        lda #<an8
        ldx #>an8
        jsr envoi
        lda ControlPort+2
        sta e1
        lda #<an10
        ldx #>an10
        jsr envoi
        lda ControlPort+2
        sta e2
        lda #<s8a
        ldx #>s8a
        jsr envoi
        lda #<an8
        ldx #>an8
        jsr envoi
        lda ControlPort+4          ; P0-P1 : 0-4095 pour 0 à 3,3 V
        sta v
        lda ControlPort+5
        sta v+1
        lda #<init
        ldx #>init
        jsr envoi
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e1
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda e2
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t2
        ldx #>t2
        jsr ecrire
        lda v
        ldx v+1
        jsr decimal16
        lda #<t3                   ; 4095 : 3,3 V
        ldx #>t3
        jsr ecrire
        lda #13
        jsr WriteCharacter
        rts

; Affiche en décimal le nombre de 16 bits A (poids faible) X (poids fort).
decimal16:
        sta n16
        stx n16+1
        ldy #0                     ; nombre de chiffres empilés
_div10: ldx #16                    ; n16 / 10 : quotient dans n16, reste dans A
        lda #0
_bit:   asl n16
        rol n16+1
        rol a
        cmp #10
        bcc _pas
        sbc #10
        inc n16
_pas:   dex
        bne _bit
        pha
        iny
        lda n16
        ora n16+1
        bne _div10
_sort:  pla
        ora #'0'
        jsr WriteCharacter
        dey
        bne _sort
        rts
n16:    .word 0

init:   .byte 10, 1, 0             ; 10,1 : UExt Initialise
an8:    .byte 10, 7, 1, 8          ; 10,7 : Read Analog, broche 8
an10:   .byte 10, 7, 1, 10         ; broche 10 : pas d'entrée analogique
s8a: .byte 10, 4, 2, 8, 3        ; 10,4 : broche 8, entrée analogique
t0:     .text "Broche 8 non reglee : erreur ", 0
t1:     .text "Broche 10 : erreur ", 0
t2:     .text "Broche 8 : ", 0
t3:     .text " sur 4095", 0
e1:     .byte 0
e2:     .byte 0
v:      .word 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,8 I2C Status Officiel

Dit si un périphérique répond à une adresse I2C.

Entrées
OctetsTypeRôle
P0uint8adresse du périphérique
Sorties
OctetsTypeRôle
P0uint81 si le périphérique répond, 0 sinon

L'appel essaie de lire un octet. L'exemple parcourt les adresses $08 à $77 et affiche celles qui répondent. En NeoBASIC, idevice(dev) fait cet appel.

Mots-clés NeoBASIC qui font cet appel : idevice()

Texte de la documentation du firmware (anglais)

Try to read from I2C Device Parameter:0. If present, then Parameter:0 will contain a non-zero value.

C (llvm-mos)
/* Chercher les périphériques I2C présents (groupe 10, fonction 8). */
#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)
        ;
}

int main(void) {
    printf("Presents :");
    for (uint8_t a = 0x08; a < 0x78; a++)       /* adresses I2C ordinaires */
        if (neo_uext_i2c_status(a))             /* I2C Status : non nul si présent */
            printf(" $%02X", a);
    printf("\n");
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Chercher les périphériques I2C présents (groupe 10, fonction 8).
program uext8;
uses crt, neo6502, neo6502uext;
const hex: string[16] = '0123456789ABCDEF';
var a: byte;
begin
  write('Presents :');
  for a := $08 to $77 do                        // adresses I2C ordinaires
    if NeoCheckI2C(a) <> 0 then                 // I2C Status : non nul si présent
      write(' $', hex[a shr 4 + 1], hex[a and 15 + 1]);
  writeln;
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Chercher les peripheriques I2C presents (groupe 10, fonction 8)
20 h$ = "0123456789ABCDEF"
30 print "Presents :";
40 for a = $08 to $77 : ' adresses I2C ordinaires
50 if idevice(a) then print " $";mid$(h$, (a >> 4) + 1, 1);mid$(h$, (a & 15) + 1, 1); : ' I2C Status
60 next a
70 print

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Chercher les périphériques I2C présents (groupe 10, fonction 8).
        .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 #<t0
        ldx #>t0
        jsr ecrire
        lda #$08                   ; adresses I2C ordinaires, $08 à $77
        sta adr
_suiv:  jsr WaitMessage
        lda adr
        sta ControlPort+4
        lda #10
        ldx #8                     ; 10,8 : I2C Status
        jsr appel
        lda ControlPort+4          ; non nul : présent
        beq _abs
        lda #' '
        jsr WriteCharacter
        lda #'$'
        jsr WriteCharacter
        lda adr
        lsr a
        lsr a
        lsr a
        lsr a
        jsr hex
        lda adr
        and #15
        jsr hex
_abs:   inc adr
        lda adr
        cmp #$78
        bne _suiv
        lda #13
        jsr WriteCharacter
        rts

; Affiche le chiffre hexadécimal A (0-15).
hex:    tax
        lda chiffres,x
        jmp WriteCharacter
chiffres: .text "0123456789ABCDEF"
t0:     .text "Presents :", 0
adr:    .byte 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,9 Read I2C Block Modifié par Trinity

Lit une suite d'octets d'un périphérique I2C dans la mémoire du 6502.

Entrées
OctetsTypeRôle
P0uint8adresse du périphérique
P1-P2addrdestination
P3-P4uint16nombre d'octets
Sorties
OctetsTypeRôle
erreuruint81 si le transfert n'est pas complet

Rien n'est écrit avant la lecture : pour lire à partir d'un registre, on l'envoie d'abord (ici, 10,5 place le pointeur d'une EEPROM). Trinity borne la longueur à la fin de la mémoire du 6502. NeoBASIC n'a pas de mot-clé pour cet appel.

Texte de la documentation du firmware (anglais)

Try to read a block of memory from I2C Device Parameter:0 into memory at Parameters:1,2, length Parameters:3,4.

C (llvm-mos)
/* Lire un bloc d'octets d'un périphérique I2C (groupe 10, fonction 9). */
#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)
        ;
}

static uint8_t tampon[4];

int main(void) {
    neo_uext_i2c_write(0x50, 0x00, 0x00);       /* 10,5 : sans valeur, place le pointeur d'une EEPROM... */
    libre();
    ControlPort.params[0] = 0x50;               /* P0 : périphérique */
    ControlPort.params[1] = (uint16_t)tampon & 0xFF;   /* P1-P2 : destination */
    ControlPort.params[2] = (uint16_t)tampon >> 8;
    ControlPort.params[3] = sizeof tampon;      /* P3-P4 : longueur */
    ControlPort.params[4] = 0;
    api(10, 9);                                 /* Read I2C Block */
    uint8_t e = ControlPort.error;
    printf("Lu en $50 (erreur %u) : %u %u %u %u\n", e, tampon[0], tampon[1], tampon[2], tampon[3]);
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Lire un bloc d'octets d'un périphérique I2C (groupe 10, fonction 9).
program uext9;
uses crt, neo6502, neo6502uext;
var tampon: array[0..3] of byte;
    e: byte;
begin
  NeoWriteI2C($50, $00, $00);                   // 10,5 : place le pointeur d'une EEPROM...
  NeoReadBlockI2C($50, word(@tampon), 4);       // Read I2C Block
  NeoWaitMessage;
  e := NeoMessage.error;
  writeln('Lu en $50 (erreur ', e, ') : ', tampon[0], ' ', tampon[1], ' ', tampon[2], ' ', tampon[3]);
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Lire un bloc d'octets d'un peripherique I2C (groupe 10, fonction 9)
20 t = alloc(4)
30 iwrite $50, 0, 0 : ' 10,5 : place le pointeur d'une EEPROM...
40 while peek($ff00) : wend
50 poke $ff04, $50 : doke $ff05, t : doke $ff07, 4 : poke $ff01, 9 : poke $ff00, 10 : ' Read I2C Block
60 while peek($ff00) : wend : e = peek($ff02)
70 print "Lu en $50 (erreur ";e;") : ";peek(t);" ";peek(t + 1);" ";peek(t + 2);" ";peek(t + 3)

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Lire un bloc d'octets d'un périphérique I2C (groupe 10, fonction 9).
        .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 #<ptr0
        ldx #>ptr0
        jsr envoi
        lda #<bloc
        ldx #>bloc
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #<t1
        ldx #>t1
        jsr ecrire
        ldx #0
_oct:   lda tampon,x
        jsr decimal
        cpx #3
        beq _fin
        lda #' '
        jsr WriteCharacter
        inx
        bra _oct
_fin:   lda #13
        jsr WriteCharacter
        rts

ptr0:   .byte 10, 5, 3, $50, 0, 0  ; 10,5 : place le pointeur d'une EEPROM...
bloc: .byte 10, 9, 5, $50     ; 10,9 : Read I2C Block, périphérique, destination, longueur
        .word tampon, 4
t0:     .text "Lu en $50 (erreur ", 0
t1:     .text ") : ", 0
e:      .byte 0
tampon: .fill 4

; 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,10 Write I2C Block Modifié par Trinity

Envoie une suite d'octets de la mémoire du 6502 à un périphérique I2C.

Entrées
OctetsTypeRôle
P0uint8adresse du périphérique
P1-P2addrsource
P3-P4uint16nombre d'octets
Sorties
OctetsTypeRôle
erreuruint81 si le transfert n'est pas complet

Le premier octet est souvent le registre visé, suivi des données. Trinity borne la longueur à la fin de la mémoire du 6502. En NeoBASIC, isend dev, liste fait cet appel ; la liste mêle nombres (un octet chacun) et chaînes.

Mots-clés NeoBASIC qui font cet appel : isend

Texte de la documentation du firmware (anglais)

Try to write a block of memory to I2C Device Parameter:0 from memory at Parameters:1,2, length Parameters:3,4.

C (llvm-mos)
/* Envoyer un bloc d'octets à un périphérique I2C (groupe 10, fonction 10). */
#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)
        ;
}

static const uint8_t bloc[] = { 0x00, 'N', 'E', 'O' };   /* registre 0, puis trois octets */

int main(void) {
    libre();
    ControlPort.params[0] = 0x50;               /* P0 : périphérique */
    ControlPort.params[1] = (uint16_t)bloc & 0xFF;     /* P1-P2 : source */
    ControlPort.params[2] = (uint16_t)bloc >> 8;
    ControlPort.params[3] = sizeof bloc;        /* P3-P4 : longueur */
    ControlPort.params[4] = 0;
    api(10, 10);                                /* Write I2C Block */
    printf("Envoye en $50 : %u octets, erreur %u\n", sizeof bloc, ControlPort.error);
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Envoyer un bloc d'octets à un périphérique I2C (groupe 10, fonction 10).
program uext10;
uses crt, neo6502, neo6502uext;
const bloc: array[0..3] of byte = ($00, ord('N'), ord('E'), ord('O'));   // registre 0, puis trois octets
var e: byte;
begin
  NeoWriteBlockI2C($50, word(@bloc), 4);        // Write I2C Block
  NeoWaitMessage;
  e := NeoMessage.error;
  writeln('Envoye en $50 : 4 octets, erreur ', e);
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Envoyer un bloc d'octets a un peripherique I2C (groupe 10, fonction 10)
20 isend $50, 0, "NEO" : ' Write I2C Block : registre 0, puis trois octets
30 e = peek($ff02)
40 print "Envoye en $50 : 4 octets, erreur ";e

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Envoyer un bloc d'octets à un périphérique I2C (groupe 10, fonction 10).
        .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 #<bloc
        ldx #>bloc
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

bloc: .byte 10, 10, 5, $50     ; 10,10 : Write I2C Block, périphérique, source, longueur
        .word donnees, 4
donnees: .byte 0                   ; registre 0, puis trois octets
        .text "NEO"
t0:     .text "Envoye en $50 : 4 octets, erreur ", 0
e:      .byte 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,11 Read SPI Block Modifié par Trinity

Lit une suite d'octets sur le bus SPI dans la mémoire du 6502.

Entrées
OctetsTypeRôle
P1-P2addrdestination
P3-P4uint16nombre d'octets
Sorties
OctetsTypeRôle
erreuruint81 si le transfert n'est pas complet

P0 n'est pas lu : il n'y a qu'un périphérique, choisi par SSEL (broche 10), mis à 0 pendant le transfert. Le premier appel SPI réserve les broches 7 à 10. Sur une carte dont le stockage est une carte SD, le SPI est pris et l'appel rend 1. NeoBASIC n'a pas de mot-clé pour cet appel.

Texte de la documentation du firmware (anglais)

Try to read a block of memory from SPI Device into memory at Parameters:1,2, length Parameters:3,4.

C (llvm-mos)
/* Lire un bloc d'octets sur le bus SPI (groupe 10, fonction 11). */
#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)
        ;
}

static uint8_t tampon[4];

int main(void) {
    libre();
    ControlPort.params[1] = (uint16_t)tampon & 0xFF;   /* P1-P2 : destination (P0 n'est pas lu) */
    ControlPort.params[2] = (uint16_t)tampon >> 8;
    ControlPort.params[3] = sizeof tampon;      /* P3-P4 : longueur */
    ControlPort.params[4] = 0;
    api(10, 11);                                /* Read SPI Block */
    uint8_t e = ControlPort.error;
    printf("Lu en SPI (erreur %u) : %u %u %u %u\n", e, tampon[0], tampon[1], tampon[2], tampon[3]);
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Lire un bloc d'octets sur le bus SPI (groupe 10, fonction 11).
program uext11;
uses crt, neo6502, neo6502uext;
var tampon: array[0..3] of byte;
    e: byte;
begin
  NeoReadBlockSPI(word(@tampon), 4);            // Read SPI Block
  NeoWaitMessage;
  e := NeoMessage.error;
  writeln('Lu en SPI (erreur ', e, ') : ', tampon[0], ' ', tampon[1], ' ', tampon[2], ' ', tampon[3]);
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Lire un bloc d'octets sur le bus SPI (groupe 10, fonction 11)
20 t = alloc(4)
30 while peek($ff00) : wend
40 doke $ff05, t : doke $ff07, 4 : poke $ff01, 11 : poke $ff00, 10 : ' Read SPI Block (P0 n'est pas lu)
50 while peek($ff00) : wend : e = peek($ff02)
60 print "Lu en SPI (erreur ";e;") : ";peek(t);" ";peek(t + 1);" ";peek(t + 2);" ";peek(t + 3)

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Lire un bloc d'octets sur le bus SPI (groupe 10, fonction 11).
        .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 #<bloc
        ldx #>bloc
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #<t1
        ldx #>t1
        jsr ecrire
        ldx #0
_oct:   lda tampon,x
        jsr decimal
        cpx #3
        beq _fin
        lda #' '
        jsr WriteCharacter
        inx
        bra _oct
_fin:   lda #13
        jsr WriteCharacter
        rts

bloc: .byte 10, 11, 5, 0       ; 10,11 : Read SPI Block (P0 n'est pas lu), destination, longueur
        .word tampon, 4
t0:     .text "Lu en SPI (erreur ", 0
t1:     .text ") : ", 0
e:      .byte 0
tampon: .fill 4

; 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,12 Write SPI Block Modifié par Trinity

Envoie une suite d'octets de la mémoire du 6502 sur le bus SPI.

Entrées
OctetsTypeRôle
P1-P2addrsource
P3-P4uint16nombre d'octets
Sorties
OctetsTypeRôle
erreuruint81 si le transfert n'est pas complet

Comme 10,11 : P0 n'est pas lu, SSEL (broche 10) passe à 0 pendant l'envoi. L'exemple montre que le SPI a pris les broches 7 à 10 : écrire la broche 10 donne ensuite l'erreur 1 (vérifié au banc). En NeoBASIC, ssend liste fait cet appel.

Mots-clés NeoBASIC qui font cet appel : ssend

Texte de la documentation du firmware (anglais)

Try to write a block of memory to SPI Device from memory at Parameters:1,2, length Parameters:3,4.

C (llvm-mos)
/* Envoyer un bloc d'octets sur le bus SPI (groupe 10, fonction 12). */
#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)
        ;
}

static const uint8_t bloc[] = { 0x9F, 0x00, 0x00 };     /* une commande et deux octets */

static uint8_t ecrire(uint8_t broche, uint8_t v) {   /* 10,2 : rend l'indicateur d'erreur */
    libre();
    ControlPort.params[0] = broche;
    ControlPort.params[1] = v;
    api(10, 2);
    return ControlPort.error;
}

int main(void) {
    neo_uext_init();                            /* 10,1 */
    libre();
    ControlPort.params[1] = (uint16_t)bloc & 0xFF;     /* P1-P2 : source (P0 n'est pas lu) */
    ControlPort.params[2] = (uint16_t)bloc >> 8;
    ControlPort.params[3] = sizeof bloc;        /* P3-P4 : longueur */
    ControlPort.params[4] = 0;
    api(10, 12);                                /* Write SPI Block */
    uint8_t e = ControlPort.error;
    uint8_t b = ecrire(10, 1);                  /* le SPI a pris les broches 7 à 10 */
    neo_uext_init();
    printf("Envoye en SPI : erreur %u\n", e);
    printf("Broche 10 ensuite : erreur %u\n", b);
    return 0;
}

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Mad Pascal
// Envoyer un bloc d'octets sur le bus SPI (groupe 10, fonction 12).
program uext12;
uses crt, neo6502, neo6502uext;
const bloc: array[0..2] of byte = ($9F, $00, $00);   // une commande et deux octets
var e, b: byte;

function ecrire(broche, v: byte): byte;         // 10,2 : rend l'indicateur d'erreur
begin
  NeoWriteGPIO(broche, v);
  NeoWaitMessage;
  result := NeoMessage.error;
end;

begin
  NeoUExtInitialize;                            // 10,1
  NeoWriteBlockSPI(word(@bloc), 3);             // Write SPI Block
  NeoWaitMessage;
  e := NeoMessage.error;
  b := ecrire(10, 1);                           // le SPI a pris les broches 7 à 10
  NeoUExtInitialize;
  writeln('Envoye en SPI : erreur ', e);
  writeln('Broche 10 ensuite : erreur ', b);
end.

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

NeoBASIC
10 ' Envoyer un bloc d'octets sur le bus SPI (groupe 10, fonction 12)
20 ssend $9f, 0, 0 : ' Write SPI Block : une commande et deux octets
30 e1 = peek($ff02)
40 b = 10 : v = 1 : gosub 1000 : ' le SPI a pris les broches 7 a 10
50 print "Envoye en SPI : erreur ";e1
60 print "Broche 10 ensuite : erreur ";e
70 end
1000 ' 10,2 : ecrire v sur la broche b ; erreur dans e
1010 while peek($ff00) : wend : poke $ff04, b : poke $ff05, v : poke $ff01, 2 : poke $ff00, 10
1020 while peek($ff00) : wend : e = peek($ff02)
1030 return

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

Assembleur 65C02 (64tass)
; Envoyer un bloc d'octets sur le bus SPI (groupe 10, fonction 12).
        .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 #<init
        ldx #>init
        jsr envoi
        lda #<bloc
        ldx #>bloc
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<ecr10
        ldx #>ecr10
        jsr envoi
        lda ControlPort+2
        sta b
        lda #<init
        ldx #>init
        jsr envoi
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda b
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

init:   .byte 10, 1, 0             ; 10,1 : UExt Initialise
bloc: .byte 10, 12, 5, 0       ; 10,12 : Write SPI Block (P0 n'est pas lu), source, longueur
        .word donnees, 3
donnees: .byte $9F, 0, 0           ; une commande et deux octets
ecr10: .byte 10, 2, 2, 10, 1      ; 10,2 : broche 10, valeur 1 : le SPI a pris les broches 7 à 10
t0:     .text "Envoye en SPI : erreur ", 0
t1:     .text "Broche 10 ensuite : erreur ", 0
e:      .byte 0
b:      .byte 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

; 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

Vérifié au banc (appel fait, sans erreur). La sortie dépend du matériel branché sur le port UEXT : celle de l'émulateur, factice, n'est pas reproduite.

10,13 Read UART Block Modifié par Trinity

Attend une suite d'octets sur le port série et la range dans la mémoire du 6502.

Entrées
OctetsTypeRôle
P1-P2addrdestination
P3-P4uint16nombre d'octets
Sorties
OctetsTypeRôle
erreuruint81 si un octet n'arrive pas dans les 5 secondes

Le délai de 5 secondes vaut pour chaque octet ; pendant l'attente, le 65C02 est arrêté. Sans rien de branché, l'exemple finit donc sur l'erreur 1 au bout de 5 secondes, sur la carte comme au banc. Si 10,19 route le port vers un modem USB branché, c'est lui qui est lu. NeoBASIC n'a pas de mot-clé pour cet appel.

Texte de la documentation du firmware (anglais)

Try to read a block of memory from UART into memory at Parameters:1,2, length Parameters:3,4. This can fail with a timeout. If UART routing (10,19) selects a USB CDC modem, the block is read from it instead.

C (llvm-mos)
/* Attendre un bloc d'octets sur le port série (groupe 10, fonction 13). */
#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)
        ;
}

static uint8_t tampon[4];

int main(void) {
    libre();
    ControlPort.params[1] = (uint16_t)tampon & 0xFF;   /* P1-P2 : destination */
    ControlPort.params[2] = (uint16_t)tampon >> 8;
    ControlPort.params[3] = sizeof tampon;      /* P3-P4 : longueur */
    ControlPort.params[4] = 0;
    api(10, 13);                                /* Read UART Block : attend jusqu'à 5 s par octet */
    printf("4 octets attendus : erreur %u\n", ControlPort.error);   /* 1 : délai dépassé */
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
4 octets attendus : erreur 1
Mad Pascal
// Attendre un bloc d'octets sur le port série (groupe 10, fonction 13).
program uext13;
uses crt, neo6502, neo6502uext;
var tampon: array[0..3] of byte;
    e: byte;
begin
  NeoReadBlockUART(word(@tampon), 4);           // Read UART Block : attend jusqu'à 5 s par octet
  NeoWaitMessage;
  e := NeoMessage.error;
  writeln('4 octets attendus : erreur ', e);    // 1 : délai dépassé
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
4 octets attendus : erreur 1
NeoBASIC
10 ' Attendre un bloc d'octets sur le port serie (groupe 10, fonction 13)
20 t = alloc(4)
30 while peek($ff00) : wend
40 doke $ff05, t : doke $ff07, 4 : poke $ff01, 13 : poke $ff00, 10 : ' Read UART Block : jusqu'a 5 s par octet
50 while peek($ff00) : wend : e = peek($ff02)
60 print "4 octets attendus : erreur ";e : ' 1 : delai depasse
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
4 octets attendus : erreur 1
Assembleur 65C02 (64tass)
; Attendre un bloc d'octets sur le port série (groupe 10, fonction 13).
        .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 #<bloc
        ldx #>bloc
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

bloc: .byte 10, 13, 5, 0       ; 10,13 : Read UART Block, jusqu'à 5 s par octet
        .word tampon, 4
t0:     .text "4 octets attendus : erreur ", 0   ; 1 : délai dépassé
e:      .byte 0
tampon: .fill 4

; 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

; 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 :
4 octets attendus : erreur 1

10,14 Write UART Block Modifié par Trinity

Envoie une suite d'octets de la mémoire du 6502 sur le port série.

Entrées
OctetsTypeRôle
P1-P2addrsource
P3-P4uint16nombre d'octets
Sorties
OctetsTypeRôle
erreuruint8toujours 0 sur le port série de la carte ; 1 si un modem USB routé (10,19) refuse

L'envoi ne vérifie pas qu'un appareil écoute. Le port série est partagé avec le port de débogage de Trinity : sa vitesse est celle du dernier 10,15. En NeoBASIC, usend liste fait cet appel.

Mots-clés NeoBASIC qui font cet appel : usend

Texte de la documentation du firmware (anglais)

Try to write a block of memory to UART from memory at Parameters:1,2, length Parameters:3,4. Routed to a USB CDC modem if 10,19 selects it.

C (llvm-mos)
/* Envoyer un bloc d'octets sur le port série (groupe 10, fonction 14). */
#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)
        ;
}

static const char texte[] = "NEO\r\n";

int main(void) {
    libre();
    ControlPort.params[1] = (uint16_t)texte & 0xFF;    /* P1-P2 : source */
    ControlPort.params[2] = (uint16_t)texte >> 8;
    ControlPort.params[3] = sizeof texte - 1;   /* P3-P4 : longueur */
    ControlPort.params[4] = 0;
    api(10, 14);                                /* Write UART Block */
    printf("Envoye : %u octets, erreur %u\n", sizeof texte - 1, ControlPort.error);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Envoye : 5 octets, erreur 0
Mad Pascal
// Envoyer un bloc d'octets sur le port série (groupe 10, fonction 14).
program uext14;
uses crt, neo6502, neo6502uext;
var texte: string[8];
    e: byte;
begin
  texte := 'NEO'#13#10;
  NeoWriteBlockUART(word(@texte) + 1, length(texte));   // Write UART Block
  NeoWaitMessage;
  e := NeoMessage.error;
  writeln('Envoye : ', length(texte), ' octets, erreur ', e);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Envoye : 5 octets, erreur 0
NeoBASIC
10 ' Envoyer un bloc d'octets sur le port serie (groupe 10, fonction 14)
20 usend "NEO", 13, 10 : ' Write UART Block
30 e = peek($ff02)
40 print "Envoye : 5 octets, erreur ";e
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Envoye : 5 octets, erreur 0
Assembleur 65C02 (64tass)
; Envoyer un bloc d'octets sur le port série (groupe 10, fonction 14).
        .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 #<bloc
        ldx #>bloc
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

bloc: .byte 10, 14, 5, 0       ; 10,14 : Write UART Block, source, longueur
        .word texte, 5
texte:  .text "NEO", 13, 10
t0:     .text "Envoye : 5 octets, erreur ", 0
e:      .byte 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

; 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 :
Envoye : 5 octets, erreur 0

10,15 Set UART Speed and Protocol Modifié par Trinity

Règle la vitesse (en bauds) et le format du port série.

Entrées
OctetsTypeRôle
P0-P3uint32vitesse en bauds
P4uint8protocole : 0 = 8N1, le seul pris en charge
Sorties
OctetsTypeRôle
erreuruint8toujours 0

L'appel réserve les broches 3 (TXD) et 4 (RXD) : les écrire comme broches simples donne l'erreur 1. Le port de débogage de Trinity suit la nouvelle vitesse (sa trace devient illisible à une autre vitesse que la sienne). Si 10,19 route vers un modem USB, l'appel règle sa ligne (la plupart l'ignorent). En NeoBASIC, uconfig bauds fait cet appel, en 8N1.

Mots-clés NeoBASIC qui font cet appel : uconfig

Texte de la documentation du firmware (anglais)

Set the Baud Rate and Serial Protocol for the UART interface. The baud rate is in Parameters:0..3 and the protocol number is Parameter:4. Currently only 8N1 is supported, this is protocol 0. If UART routing selects a USB CDC modem, this sets its line coding (most USB modems ignore it).

C (llvm-mos)
/* Régler la vitesse du port série (groupe 10, fonction 15). */
#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)
        ;
}

static uint8_t ecrire(uint8_t broche, uint8_t v) {   /* 10,2 : rend l'indicateur d'erreur */
    libre();
    ControlPort.params[0] = broche;
    ControlPort.params[1] = v;
    api(10, 2);
    return ControlPort.error;
}

int main(void) {
    neo_uext_uart_configure(115200, 0);         /* Set UART Speed and Protocol : 115200 bauds, 8N1 */
    uint8_t e = ControlPort.error;
    uint8_t apres = ecrire(3, 0);               /* TXD (3) et RXD (4) sont au port série */
    neo_uext_init();
    printf("Reglage : erreur %u\n", e);
    printf("Broche 3 ensuite : erreur %u\n", apres);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Reglage : erreur 0
Broche 3 ensuite : erreur 1
Mad Pascal
// Régler la vitesse du port série (groupe 10, fonction 15).
program uext15;
uses crt, neo6502, neo6502uext;
var e, apres: byte;

function ecrire(broche, v: byte): byte;         // 10,2 : rend l'indicateur d'erreur
begin
  NeoWriteGPIO(broche, v);
  NeoWaitMessage;
  result := NeoMessage.error;
end;

begin
  NeoSetupUART(115200, UART_PROTOCOL_8N1);      // Set UART Speed and Protocol : 115200 bauds, 8N1
  NeoWaitMessage;
  e := NeoMessage.error;
  apres := ecrire(3, 0);                        // TXD (3) et RXD (4) sont au port série
  NeoUExtInitialize;
  writeln('Reglage : erreur ', e);
  writeln('Broche 3 ensuite : erreur ', apres);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Reglage : erreur 0
Broche 3 ensuite : erreur 1
NeoBASIC
10 ' Regler la vitesse du port serie (groupe 10, fonction 15)
20 uconfig 115200 : ' Set UART Speed and Protocol : 115200 bauds, 8N1
30 e1 = peek($ff02)
40 b = 3 : v = 0 : gosub 1000 : ' TXD (3) et RXD (4) sont au port serie
50 print "Reglage : erreur ";e1
60 print "Broche 3 ensuite : erreur ";e
70 end
1000 ' 10,2 : ecrire v sur la broche b ; erreur dans e
1010 while peek($ff00) : wend : poke $ff04, b : poke $ff05, v : poke $ff01, 2 : poke $ff00, 10
1020 while peek($ff00) : wend : e = peek($ff02)
1030 return
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Reglage : erreur 0
Broche 3 ensuite : erreur 1
Assembleur 65C02 (64tass)
; Régler la vitesse du port série (groupe 10, fonction 15).
        .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 #<vitesse
        ldx #>vitesse
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<ecr3
        ldx #>ecr3
        jsr envoi
        lda ControlPort+2
        sta b
        lda #<init
        ldx #>init
        jsr envoi
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda b
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

init:   .byte 10, 1, 0             ; 10,1 : UExt Initialise
vitesse: .byte 10, 15, 5           ; 10,15 : Set UART Speed and Protocol
        .dword 115200              ; P0-P3 : bauds
        .byte 0                    ; P4 : protocole 0, 8N1
ecr3: .byte 10, 2, 2, 3, 0       ; 10,2 : broche 3, valeur 0 : TXD (3) et RXD (4) sont au port série
t0:     .text "Reglage : erreur ", 0
t1:     .text "Broche 3 ensuite : erreur ", 0
e:      .byte 0
b:      .byte 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

; 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 :
Reglage : erreur 0
Broche 3 ensuite : erreur 1

10,16 Write byte to UART Modifié par Trinity

Envoie un octet sur le port série.

Entrées
OctetsTypeRôle
P0uint8octet
Sorties
OctetsTypeRôle
erreuruint8toujours 0

Comme 10,14, sans vérifier qu'un appareil écoute ; routé vers un modem USB si 10,19 le choisit. NeoBASIC n'a pas de mot-clé pour cet appel (usend envoie un bloc).

Texte de la documentation du firmware (anglais)

Write byte Parameter:0 to the UART (or the routed USB CDC modem).

C (llvm-mos)
/* Envoyer un octet sur le port série (groupe 10, fonction 16). */
#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)
        ;
}

int main(void) {
    neo_uext_uart_write('A');                   /* Write byte to UART */
    printf("Octet envoye : erreur %u\n", ControlPort.error);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Octet envoye : erreur 0
Mad Pascal
// Envoyer un octet sur le port série (groupe 10, fonction 16).
program uext16;
uses crt, neo6502, neo6502uext;
var e: byte;
begin
  NeoWriteByteUART(ord('A'));                   // Write byte to UART
  NeoWaitMessage;
  e := NeoMessage.error;
  writeln('Octet envoye : erreur ', e);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Octet envoye : erreur 0
NeoBASIC
10 ' Envoyer un octet sur le port serie (groupe 10, fonction 16)
20 while peek($ff00) : wend : poke $ff04, asc("A") : poke $ff01, 16 : poke $ff00, 10 : ' Write byte to UART
30 while peek($ff00) : wend : e = peek($ff02)
40 print "Octet envoye : erreur ";e
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Octet envoye : erreur 0
Assembleur 65C02 (64tass)
; Envoyer un octet sur le port série (groupe 10, fonction 16).
        .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 #<octet
        ldx #>octet
        jsr envoi
        lda ControlPort+2
        sta e
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

octet:  .byte 10, 16, 1, 'A'       ; 10,16 : Write byte to UART
t0:     .text "Octet envoye : erreur ", 0
e:      .byte 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

; 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 :
Octet envoye : erreur 0

10,17 Read byte from UART Modifié par Trinity

Lit un octet reçu sur le port série, sans attendre.

Entrées : aucune.

Sorties
OctetsTypeRôle
P0uint8octet reçu
erreuruint81 si rien n'est arrivé

Contrairement à 10,13, l'appel n'attend pas. Sans rien de branché, l'exemple trouve l'erreur 1, sur la carte comme au banc. NeoBASIC n'a pas de mot-clé pour cet appel.

Texte de la documentation du firmware (anglais)

Read a byte from the UART (or the routed USB CDC modem). It is returned in Parameter:0. Error 1 if nothing is available.

C (llvm-mos)
/* Lire un octet du port série s'il y en a un (groupe 10, fonction 17). */
#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)
        ;
}

int main(void) {
    libre();
    api(10, 17);                                /* Read byte from UART */
    if (ControlPort.error)                      /* 1 : rien n'est arrivé */
        printf("Rien a lire : erreur %u\n", ControlPort.error);
    else
        printf("Recu : %u\n", ControlPort.params[0]);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Rien a lire : erreur 1
Mad Pascal
// Lire un octet du port série s'il y en a un (groupe 10, fonction 17).
program uext17;
uses crt, neo6502, neo6502uext;
var v, e: byte;
begin
  NeoReadByteUART;                              // Read byte from UART
  NeoWaitMessage;                               // attendre la fin de l'appel, puis relire P0
  v := NeoMessage.params[0];
  e := NeoMessage.error;                        // 1 : rien n'est arrivé
  if e <> 0 then writeln('Rien a lire : erreur ', e)
  else writeln('Recu : ', v);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Rien a lire : erreur 1
NeoBASIC
10 ' Lire un octet du port serie s'il y en a un (groupe 10, fonction 17)
20 while peek($ff00) : wend : poke $ff01, 17 : poke $ff00, 10 : ' Read byte from UART
30 while peek($ff00) : wend : e = peek($ff02) : v = peek($ff04)
40 if e then print "Rien a lire : erreur ";e : end : ' 1 : rien n'est arrive
50 print "Recu : ";v
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Rien a lire : erreur 1
Assembleur 65C02 (64tass)
; Lire un octet du port série s'il y en a un (groupe 10, fonction 17).
        .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 #<lire
        ldx #>lire
        jsr envoi
        lda ControlPort+4
        sta v
        lda ControlPort+2          ; 1 : rien n'est arrivé
        sta e
        beq _recu
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda e
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts
_recu:   lda #<t1
        ldx #>t1
        jsr ecrire
        lda v
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

lire:   .byte 10, 17, 0            ; 10,17 : Read byte from UART
t0:     .text "Rien a lire : erreur ", 0
t1:     .text "Recu : ", 0
e:      .byte 0
v:      .byte 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

; 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 :
Rien a lire : erreur 1

10,18 Check if Byte Available Modifié par Trinity

Dit si un octet reçu attend d'être lu sur le port série.

Entrées : aucune.

Sorties
OctetsTypeRôle
P0uint81 si un octet attend, 0 sinon

À appeler avant 10,17 pour ne lire que ce qui est arrivé. En NeoBASIC, uhasdata() fait cet appel.

Mots-clés NeoBASIC qui font cet appel : uhasdata()

Texte de la documentation du firmware (anglais)

See if a byte is available in the UART input buffer. If available Parameter:0 is non zero.

C (llvm-mos)
/* Savoir si un octet attend sur le port série (groupe 10, fonction 18). */
#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)
        ;
}

int main(void) {
    printf("Octet en attente : %s\n", neo_uext_uart_available() ? "oui" : "non");   /* Check if Byte Available */
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Octet en attente : non
Mad Pascal
// Savoir si un octet attend sur le port série (groupe 10, fonction 18).
program uext18;
uses crt, neo6502, neo6502uext;
var v: byte;
begin
  NeoCheckUART;                                 // Check if Byte Available
  NeoWaitMessage;                               // attendre la fin de l'appel, puis relire P0
  v := NeoMessage.params[0];
  if v <> 0 then writeln('Octet en attente : oui') else writeln('Octet en attente : non');
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Octet en attente : non
NeoBASIC
10 ' Savoir si un octet attend sur le port serie (groupe 10, fonction 18)
20 r$ = "non" : if uhasdata() then r$ = "oui" : ' Check if Byte Available
30 print "Octet en attente : ";r$
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Octet en attente : non
Assembleur 65C02 (64tass)
; Savoir si un octet attend sur le port série (groupe 10, fonction 18).
        .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 #<test
        ldx #>test
        jsr envoi
        lda ControlPort+4          ; non nul : un octet attend
        bne _oui
        lda #<tn
        ldx #>tn
        jsr ecrire
        rts
_oui:   lda #<to
        ldx #>to
        jsr ecrire
        rts

test:   .byte 10, 18, 0            ; 10,18 : Check if Byte Available
tn:     .text "Octet en attente : non", 13, 0
to:     .text "Octet en attente : oui", 13, 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

; 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 :
Octet en attente : non

10,19 Route UART to USB CDC modem Trinity

Choisit où vont les fonctions du port série (10,13 à 10,18) : le port série du UEXT, un modem USB, ou le modem s'il est branché.

Entrées
OctetsTypeRôle
P0uint80 port série du UEXT, 1 premier modem USB (CDC), 2 automatique ; au-delà : automatique
Sorties
OctetsTypeRôle
P0uint8mode retenu (0, 1 ou 2)

Automatique est le réglage de départ : un programme série (netconfig, prophet...) passe ainsi par un modem Wi-Fi USB s'il y en a un, sans changement. La documentation dit que P0 rend le mode « en vigueur » : c'est le réglage retenu (2 pour automatique), pas le port effectivement utilisé. L'exemple remet le mode automatique avant de finir. NeoBASIC n'a pas de mot-clé pour cet appel.

Texte de la documentation du firmware (anglais)

Select the source/target of the UART functions 10,13-10,18. Parameter:0 = 0 for the hardware UART (UEXT), 1 for the first USB CDC modem, 2 for AUTO (the CDC modem if one is plugged in, otherwise the hardware UART). AUTO is the default, so serial programs such as netconfig/prophet use a USB Wi-Fi modem unchanged when one is present. Parameter:0 returns the mode in effect.

C (llvm-mos)
/* Choisir où vont les fonctions du port série (groupe 10, fonction 19, 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)
        ;
}

static uint8_t router(uint8_t mode) {           /* Route UART to USB CDC modem : rend le mode retenu */
    libre();
    ControlPort.params[0] = mode;               /* 0 UART du UEXT, 1 modem USB, 2 automatique */
    api(10, 19);
    return ControlPort.params[0];
}

int main(void) {
    uint8_t a = router(0);
    uint8_t b = router(9);                      /* hors plage : automatique */
    router(2);                                  /* remettre le mode par défaut */
    printf("Demande 0 : %u, demande 9 : %u\n", a, b);
    return 0;
}
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Demande 0 : 0, demande 9 : 2
Mad Pascal
// Choisir où vont les fonctions du port série (groupe 10, fonction 19, Trinity).
program uext19;
uses crt, neo6502, neo6502uext;
var a, b: byte;

function router(mode: byte): byte;              // Route UART to USB CDC modem : rend le mode retenu
begin
  NeoMessage.params[0] := mode;                 // 0 UART du UEXT, 1 modem USB, 2 automatique
  NeoSendMessage(10, 19);
  NeoWaitMessage;
  result := NeoMessage.params[0];
end;

begin
  a := router(0);
  b := router(9);                               // hors plage : automatique
  router(2);                                    // remettre le mode par défaut
  writeln('Demande 0 : ', a, ', demande 9 : ', b);
end.
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Demande 0 : 0, demande 9 : 2
NeoBASIC
10 ' Choisir ou vont les fonctions du port serie (groupe 10, fonction 19, Trinity)
20 m = 0 : gosub 1000 : a = r
30 m = 9 : gosub 1000 : b = r : ' hors plage : automatique
40 m = 2 : gosub 1000 : ' remettre le mode par defaut
50 print "Demande 0 : ";a;", demande 9 : ";b
60 end
1000 ' Route UART to USB CDC modem : mode m (0 UART du UEXT, 1 modem USB, 2 automatique) ; mode retenu dans r
1010 while peek($ff00) : wend : poke $ff04, m : poke $ff01, 19 : poke $ff00, 10
1020 while peek($ff00) : wend : r = peek($ff04)
1030 return
Sortie observée dans l'émulateur Phosphoneo, firmware Trinity 0.16.69 :
Demande 0 : 0, demande 9 : 2
Assembleur 65C02 (64tass)
; Choisir où vont les fonctions du port série (groupe 10, fonction 19, 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 #<r0
        ldx #>r0
        jsr envoi
        lda ControlPort+4          ; mode retenu
        sta a
        lda #<r9
        ldx #>r9
        jsr envoi
        lda ControlPort+4
        sta b
        lda #<r2
        ldx #>r2
        jsr envoi
        lda #<t0
        ldx #>t0
        jsr ecrire
        lda a
        jsr decimal
        lda #<t1
        ldx #>t1
        jsr ecrire
        lda b
        jsr decimal
        lda #13
        jsr WriteCharacter
        rts

r0:     .byte 10, 19, 1, 0         ; 10,19 : 0 UART du UEXT
r9:     .byte 10, 19, 1, 9         ; hors plage : automatique
r2:     .byte 10, 19, 1, 2         ; remettre le mode par défaut, automatique
t0:     .text "Demande 0 : ", 0
t1:     .text ", demande 9 : ", 0
a:      .byte 0
b:      .byte 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

; 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 :
Demande 0 : 0, demande 9 : 2

← L'API du Neo6502