Groupe 14 — Série USB (CDC) Trinity
14,1 Serial Status · 14,2 Serial Read Byte · 14,3 Serial Write Byte · 14,4 Serial Read Block · 14,5 Serial Write Block · 14,6 Serial Set Line Coding
Propre à Trinity : un appareil série USB (classe CDC-ACM) branché sur un port USB-A de la carte, en général un modem, comme le Modem Wi-Fi Neo6502, ou un adaptateur USB-série. P7 choisit l'appareil (0 le premier, 1 le second) ; un numéro plus grand revient à l'appareil 0, sans erreur. Les fonctions série du groupe 10 (10,13 à 10,18) passent aussi par cet appareil quand 10,19 le choisit, ce qui est le cas par défaut dès qu'un modem est branché.
Au banc, Phosphoneo branche un modem Hayes simulé (tools/fake_modem.py) : les exemples lui envoient AT et attendent « OK », comme le ferait un vrai modem. Ils n'affichent que ce qui ne dépend pas du modem (branché ou non, erreurs, nombre d'octets écrits, présence de « OK »).
14,1 Serial Status Trinity
Dit si l'appareil série USB est branché et combien d'octets reçus attendent d'être lus.
| Octets | Type | Rôle |
|---|---|---|
P7 | uint8 | appareil : 0 ou 1 (au-delà : 0) |
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | 1 si l'appareil est branché |
P1-P2 | uint16 | octets reçus en attente (0 si l'appareil est absent) |
L'appel ne signale jamais d'erreur. L'exemple montre que l'appareil 5 (hors plage) est vu comme l'appareil 0, puis qu'après l'envoi de AT des octets attendent. Sans modem, les deux appareils sont absents (vérifié au banc). NeoBASIC n'a pas de mot-clé pour ce groupe.
Texte de la documentation du firmware (anglais)
USB CDC-ACM serial device (modem, USB-serial adapter) on the Neo6502 host port. Parameter:7 selects the device (0 = first). Returns Parameter:0 = 1 if the device is connected, Parameters:1,2 = number of bytes waiting to be read.
C (llvm-mos)
/* Savoir si un modem USB est branché et combien d'octets il a envoyés (groupe 14, 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 uint16_t en_attente(void) { /* 14,1 : octets reçus en attente (appareil 0) ; 0xFFFF si absent */
libre();
ControlPort.params[7] = 0; /* P7 : appareil 0, le premier */
api(14, 1);
if (!ControlPort.params[0])
return 0xFFFF;
return ControlPort.params[1] | ControlPort.params[2] << 8;
}
static uint8_t envoyer(const char *s, uint8_t n) { /* 14,5 : écrit n octets ; rend le nombre écrit */
libre();
ControlPort.params[0] = (uint16_t)s & 0xFF; /* P0-P1 : adresse */
ControlPort.params[1] = (uint16_t)s >> 8;
ControlPort.params[2] = n; /* P2-P3 : nombre d'octets */
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 5);
return ControlPort.params[2];
}
static char recu[128];
static uint8_t nrecu;
static void lire(void) { /* 14,4 : ajoute à recu ce qui est arrivé */
libre();
ControlPort.params[0] = (uint16_t)(recu + nrecu) & 0xFF;
ControlPort.params[1] = (uint16_t)(recu + nrecu) >> 8;
ControlPort.params[2] = sizeof recu - 1 - nrecu;
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 4);
nrecu += ControlPort.params[2];
recu[nrecu] = 0;
}
static uint8_t attendre_ok(void) { /* lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs) */
uint32_t fin = neo_system_timer() + 300;
while (neo_system_timer() < fin) {
lire();
for (uint8_t i = 0; i + 1 < nrecu; i++)
if (recu[i] == 'O' && recu[i + 1] == 'K')
return 1;
}
return 0;
}
static uint8_t branche(uint8_t appareil) { /* Serial Status : P0 = 1 si branché */
libre();
ControlPort.params[7] = appareil; /* P7 : numéro de l'appareil */
api(14, 1);
return ControlPort.params[0];
}
int main(void) {
uint8_t b0 = branche(0), b5 = branche(5); /* 5 : hors plage (2 appareils, 0 et 1) */
uint16_t avant = en_attente(), apres = 0;
envoyer("AT\r", 3);
uint32_t fin = neo_system_timer() + 300;
while (apres == 0 && neo_system_timer() < fin)
apres = en_attente(); /* Serial Status : P1-P2, octets reçus pas encore lus */
attendre_ok(); /* puis on lit la réponse */
printf("Appareil 0 : %s, appareil 5 : %s\n", b0 ? "branche" : "absent", b5 ? "branche" : "absent");
printf("En attente avant AT : %u, apres : %s\n", avant, apres > 0 && apres != 0xFFFF ? "des octets" : "rien");
return 0;
}Appareil 0 : branche, appareil 5 : branche
En attente avant AT : 0, apres : des octetsMad Pascal
// Savoir si un modem USB est branché et combien d'octets il a envoyés (groupe 14, fonction 1).
program cdc1;
uses crt, neo6502;
var recu: array[0..127] of byte; // réponse du modem
nrecu: byte;
function en_attente: word; // 14,1 : octets reçus en attente (appareil 0) ; $FFFF si absent
begin
NeoWaitMessage;
NeoMessage.params[7] := 0; // P7 : appareil 0, le premier
NeoSendMessage(14, 1);
NeoWaitMessage;
if NeoMessage.params[0] = 0 then result := $FFFF
else result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
function envoyer(adresse: word; n: byte): byte; // 14,5 : écrit n octets ; rend le nombre écrit
begin
NeoWaitMessage;
wordParams[0] := adresse; // P0-P1 : adresse
wordParams[1] := n; // P2-P3 : nombre d'octets
NeoMessage.params[7] := 0;
NeoSendMessage(14, 5);
NeoWaitMessage;
result := NeoMessage.params[2];
end;
procedure lire; // 14,4 : ajoute à recu ce qui est arrivé
begin
NeoWaitMessage;
wordParams[0] := word(@recu) + nrecu;
wordParams[1] := 127 - nrecu;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 4);
NeoWaitMessage;
nrecu := nrecu + NeoMessage.params[2];
end;
function attendre_ok: boolean; // lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs)
var fin: cardinal;
i: byte;
begin
result := false;
fin := NeoGetTimer + 300;
while (not result) and (NeoGetTimer < fin) do begin
lire;
if nrecu > 1 then
for i := 0 to nrecu - 2 do
if (recu[i] = ord('O')) and (recu[i + 1] = ord('K')) then result := true;
end;
end;
const at: array[0..2] of byte = (ord('A'), ord('T'), 13);
var b0, b5: byte;
avant, apres: word;
fin: cardinal;
function branche(appareil: byte): byte; // Serial Status : P0 = 1 si branché
begin
NeoWaitMessage;
NeoMessage.params[7] := appareil; // P7 : numéro de l'appareil
NeoSendMessage(14, 1);
NeoWaitMessage;
result := NeoMessage.params[0];
end;
begin
b0 := branche(0);
b5 := branche(5); // 5 : hors plage (2 appareils, 0 et 1)
avant := en_attente;
apres := 0;
envoyer(word(@at), 3);
fin := NeoGetTimer + 300;
while (apres = 0) and (NeoGetTimer < fin) do
apres := en_attente; // Serial Status : P1-P2, octets reçus pas encore lus
attendre_ok; // puis on lit la réponse
write('Appareil 0 : ');
if b0 <> 0 then write('branche') else write('absent');
write(', appareil 5 : ');
if b5 <> 0 then writeln('branche') else writeln('absent');
write('En attente avant AT : ', avant, ', apres : ');
if (apres > 0) and (apres <> $FFFF) then writeln('des octets') else writeln('rien');
end.Appareil 0 : branche, appareil 5 : branche
En attente avant AT : 0, apres : des octetsNeoBASIC
10 ' Savoir si un modem USB est branche et combien d'octets il a envoyes (groupe 14, fonction 1)
20 r = alloc(128) : nr = 0 : ' reponse du modem
30 a = 0 : gosub 800 : b0 = c : a = 5 : gosub 800 : b5 = c : ' 5 : hors plage (2 appareils, 0 et 1)
40 gosub 900 : avant = w
50 s$ = "AT" : gosub 2000
60 apres = 0 : f = time() + 300
70 repeat : gosub 900 : apres = w : until apres <> 0 | time() > f : ' Serial Status : P1-P2, octets recus pas encore lus
80 gosub 2200 : ' puis on lit la reponse
90 a$ = "absent" : if b0 then a$ = "branche"
100 b$ = "absent" : if b5 then b$ = "branche"
110 print "Appareil 0 : ";a$;", appareil 5 : ";b$
120 e$ = "rien" : if apres > 0 & apres <> 65535 then e$ = "des octets"
130 print "En attente avant AT : ";avant;", apres : ";e$
140 end
800 ' Serial Status : c = 1 si l'appareil a est branche
810 while peek($ff00) : wend : poke $ff0b, a : poke $ff01, 1 : poke $ff00, 14
820 while peek($ff00) : wend : c = peek($ff04)
830 return
900 ' 14,1 : octets en attente sur l'appareil 0 dans w (65535 si absent)
910 while peek($ff00) : wend : poke $ff0b, 0 : poke $ff01, 1 : poke $ff00, 14
920 while peek($ff00) : wend : w = 65535 : if peek($ff04) then w = deek($ff05)
930 return
2000 ' 14,5 : envoie s$ (plus un retour chariot) ; nombre d'octets ecrits dans n
2010 b = alloc(40) : for i = 1 to len(s$) : poke b + i - 1, asc(mid$(s$, i, 1)) : next i : poke b + len(s$), 13
2020 while peek($ff00) : wend
2030 doke $ff04, b : doke $ff06, len(s$) + 1 : poke $ff0b, 0 : poke $ff01, 5 : poke $ff00, 14
2040 while peek($ff00) : wend : n = peek($ff06)
2050 return
2100 ' 14,4 : ajoute a r (nr octets deja lus) ce qui est arrive
2110 while peek($ff00) : wend
2120 doke $ff04, r + nr : doke $ff06, 127 - nr : poke $ff0b, 0 : poke $ff01, 4 : poke $ff00, 14
2130 while peek($ff00) : wend : nr = nr + peek($ff06)
2140 return
2200 ' lit jusqu'a OK ou 3 secondes (time() : horloge en cs) ; ok = 1 si OK recu
2210 ok = 0 : f = time() + 300
2220 repeat : gosub 2100
2230 for i = 0 to nr - 2 : if peek(r + i) = 79 & peek(r + i + 1) = 75 then ok = 1
2240 next i
2250 until ok | time() > f
2260 returnAppareil 0 : branche, appareil 5 : branche
En attente avant AT : 0, apres : des octetsAssembleur 65C02 (64tass)
; Savoir si un modem USB est branché et combien d'octets il a envoyés (groupe 14, 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 #0
jsr branche
sta b0
lda #5 ; 5 : hors plage (2 appareils, 0 et 1)
jsr branche
sta b5
jsr attente
lda w
sta avant
lda #<cmd_at
ldx #>cmd_at
ldy #3
jsr envoyer
stz apres
jsr depart
_att: jsr attente ; Serial Status : P1-P2, octets reçus pas encore lus
bcs _v
lda w
ora w+1
bne _des
_v: jsr ecoule
bcc _att
bra _suite
_des: lda #1
sta apres
_suite: jsr attendre_ok ; puis on lit la réponse
lda #<t0
ldx #>t0
jsr ecrire
lda b0
bne _ob0
lda #<ta
ldx #>ta
jsr ecrire
bra _eb0
_ob0: lda #<tb
ldx #>tb
jsr ecrire
_eb0:
lda #<t1
ldx #>t1
jsr ecrire
lda b5
bne _ob5
lda #<ta
ldx #>ta
jsr ecrire
bra _eb5
_ob5: lda #<tb
ldx #>tb
jsr ecrire
_eb5:
lda #13
jsr WriteCharacter
lda #<t2
ldx #>t2
jsr ecrire
lda avant
jsr decimal
lda #<t3
ldx #>t3
jsr ecrire
lda apres
bne _oapres
lda #<tr
ldx #>tr
jsr ecrire
bra _eapres
_oapres: lda #<td
ldx #>td
jsr ecrire
_eapres:
lda #13
jsr WriteCharacter
rts
; Serial Status de l'appareil A : A = 1 si branché.
branche:
pha
jsr WaitMessage
pla
sta ControlPort+11 ; P7 : numéro de l'appareil
lda #14
ldx #1 ; 14,1 : Serial Status
jsr appel
lda ControlPort+4
rts
; 14,1 : octets en attente sur l'appareil 0 dans w (16 bits) ; C = 1 si l'appareil est absent.
attente:
jsr WaitMessage
stz ControlPort+11 ; P7 : appareil 0, le premier
lda #14
ldx #1 ; 14,1 : Serial Status
jsr appel
lda ControlPort+5 ; P1-P2 : octets reçus pas encore lus
sta w
lda ControlPort+6
sta w+1
lda ControlPort+4 ; P0 : 1 si branché
cmp #1 ; C = 0 si absent...
eor #1
lsr a ; ... d'où C = 1 si absent
rts
w: .word 0
; 14,5 : envoie n octets depuis l'adresse A (poids faible), X (poids fort), n dans Y ; nombre écrit dans A.
envoyer:
pha
jsr WaitMessage
pla
sta ControlPort+4 ; P0-P1 : adresse
stx ControlPort+5
sty ControlPort+6 ; P2-P3 : nombre d'octets
stz ControlPort+7
stz ControlPort+11 ; P7 : appareil 0
lda #14
ldx #5 ; 14,5 : Serial Write Block
jsr appel
lda ControlPort+6
rts
; 14,4 : ajoute à recu ce qui est arrivé (nrecu octets déjà lus).
lire: jsr WaitMessage
clc
lda #<recu
adc nrecu
sta ControlPort+4 ; P0-P1 : recu + nrecu
lda #>recu
adc #0
sta ControlPort+5
sec
lda #127
sbc nrecu
sta ControlPort+6 ; P2-P3 : place restante
stz ControlPort+7
stz ControlPort+11
lda #14
ldx #4 ; 14,4 : Serial Read Block
jsr appel
clc
lda nrecu
adc ControlPort+6 ; P2-P3 : octets lus
sta nrecu
rts
; Horloge (1,1) en centièmes, 16 bits bas dans A (poids faible) et X (poids fort).
horloge:
jsr WaitMessage
lda #1
ldx #1 ; 1,1 : Timer
jsr appel
lda ControlPort+4
ldx ControlPort+5
rts
; Note le départ d'une attente de 3 secondes.
depart: jsr horloge
sta debut
stx debut+1
rts
; C = 1 si 3 secondes (300 cs) sont passées depuis depart.
ecoule: jsr horloge
sec
sbc debut
tay
txa
sbc debut+1
cmp #>300 ; (maintenant - départ) >= 300 ?
bne _f
cpy #<300
_f: rts
debut: .word 0
; Lit jusqu'à « OK » ou 3 secondes ; A = 1 si OK reçu.
attendre_ok:
jsr depart
_b: jsr lire
ldx #0
_c: inx
cpx nrecu
bcs _pas
lda recu-1,x
cmp #'O'
bne _c
lda recu,x
cmp #'K'
bne _c
lda #1
rts
_pas: jsr ecoule
bcc _b
lda #0
rts
recu: .fill 128 ; réponse du modem
nrecu: .byte 0
cmd_at: .text "AT", 13
t0: .text "Appareil 0 : ", 0
t1: .text ", appareil 5 : ", 0
t2: .text "En attente avant AT : ", 0
t3: .text ", apres : ", 0
ta: .text "absent", 0
tb: .text "branche", 0
td: .text "des octets", 0
tr: .text "rien", 0
b0: .byte 0
b5: .byte 0
avant: .byte 0
apres: .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 0Appareil 0 : branche, appareil 5 : branche
En attente avant AT : 0, apres : des octets14,2 Serial Read Byte Trinity
Lit un octet reçu de l'appareil série USB, sans attendre.
| Octets | Type | Rôle |
|---|---|---|
P7 | uint8 | appareil : 0 ou 1 |
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | octet lu |
erreur | uint8 | 1 si rien n'est arrivé ou si l'appareil est absent |
Avant tout envoi, rien n'est arrivé : erreur 1. L'exemple envoie AT, puis lit octet par octet jusqu'à « OK ».
Texte de la documentation du firmware (anglais)
Read one byte from the CDC device (Parameter:7) into Parameter:0. Sets the error flag if no byte is available or no device is connected.
C (llvm-mos)
/* Lire la réponse du modem octet par octet (groupe 14, 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 uint16_t en_attente(void) { /* 14,1 : octets reçus en attente (appareil 0) ; 0xFFFF si absent */
libre();
ControlPort.params[7] = 0; /* P7 : appareil 0, le premier */
api(14, 1);
if (!ControlPort.params[0])
return 0xFFFF;
return ControlPort.params[1] | ControlPort.params[2] << 8;
}
static uint8_t envoyer(const char *s, uint8_t n) { /* 14,5 : écrit n octets ; rend le nombre écrit */
libre();
ControlPort.params[0] = (uint16_t)s & 0xFF; /* P0-P1 : adresse */
ControlPort.params[1] = (uint16_t)s >> 8;
ControlPort.params[2] = n; /* P2-P3 : nombre d'octets */
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 5);
return ControlPort.params[2];
}
static char recu[128];
static uint8_t nrecu;
static void lire(void) { /* 14,4 : ajoute à recu ce qui est arrivé */
libre();
ControlPort.params[0] = (uint16_t)(recu + nrecu) & 0xFF;
ControlPort.params[1] = (uint16_t)(recu + nrecu) >> 8;
ControlPort.params[2] = sizeof recu - 1 - nrecu;
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 4);
nrecu += ControlPort.params[2];
recu[nrecu] = 0;
}
static uint8_t attendre_ok(void) { /* lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs) */
uint32_t fin = neo_system_timer() + 300;
while (neo_system_timer() < fin) {
lire();
for (uint8_t i = 0; i + 1 < nrecu; i++)
if (recu[i] == 'O' && recu[i + 1] == 'K')
return 1;
}
return 0;
}
static uint8_t octet;
static uint8_t lire_octet(void) { /* Serial Read Byte : rend l'indicateur d'erreur */
libre();
ControlPort.params[7] = 0;
api(14, 2);
octet = ControlPort.params[0];
return ControlPort.error;
}
int main(void) {
uint8_t e = lire_octet(); /* rien n'est encore arrivé */
envoyer("AT\r", 3);
uint32_t fin = neo_system_timer() + 300;
uint8_t prec = 0, ok = 0;
while (!ok && neo_system_timer() < fin)
if (lire_octet() == 0) {
ok = prec == 'O' && octet == 'K';
prec = octet;
}
printf("Avant l'envoi : erreur %u\n", e);
printf("Reponse a AT : %s\n", ok ? "OK" : "rien");
return 0;
}Avant l'envoi : erreur 1
Reponse a AT : OKMad Pascal
// Lire la réponse du modem octet par octet (groupe 14, fonction 2).
program cdc2;
uses crt, neo6502;
var recu: array[0..127] of byte; // réponse du modem
nrecu: byte;
function en_attente: word; // 14,1 : octets reçus en attente (appareil 0) ; $FFFF si absent
begin
NeoWaitMessage;
NeoMessage.params[7] := 0; // P7 : appareil 0, le premier
NeoSendMessage(14, 1);
NeoWaitMessage;
if NeoMessage.params[0] = 0 then result := $FFFF
else result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
function envoyer(adresse: word; n: byte): byte; // 14,5 : écrit n octets ; rend le nombre écrit
begin
NeoWaitMessage;
wordParams[0] := adresse; // P0-P1 : adresse
wordParams[1] := n; // P2-P3 : nombre d'octets
NeoMessage.params[7] := 0;
NeoSendMessage(14, 5);
NeoWaitMessage;
result := NeoMessage.params[2];
end;
procedure lire; // 14,4 : ajoute à recu ce qui est arrivé
begin
NeoWaitMessage;
wordParams[0] := word(@recu) + nrecu;
wordParams[1] := 127 - nrecu;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 4);
NeoWaitMessage;
nrecu := nrecu + NeoMessage.params[2];
end;
function attendre_ok: boolean; // lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs)
var fin: cardinal;
i: byte;
begin
result := false;
fin := NeoGetTimer + 300;
while (not result) and (NeoGetTimer < fin) do begin
lire;
if nrecu > 1 then
for i := 0 to nrecu - 2 do
if (recu[i] = ord('O')) and (recu[i + 1] = ord('K')) then result := true;
end;
end;
const at: array[0..2] of byte = (ord('A'), ord('T'), 13);
var octet, e, prec: byte;
ok: boolean;
fin: cardinal;
function lire_octet: byte; // Serial Read Byte : rend l'indicateur d'erreur
begin
NeoWaitMessage;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 2);
NeoWaitMessage;
octet := NeoMessage.params[0];
result := NeoMessage.error;
end;
begin
e := lire_octet; // rien n'est encore arrivé
envoyer(word(@at), 3);
fin := NeoGetTimer + 300;
prec := 0;
ok := false;
while (not ok) and (NeoGetTimer < fin) do
if lire_octet = 0 then begin
ok := (prec = ord('O')) and (octet = ord('K'));
prec := octet;
end;
writeln('Avant l''envoi : erreur ', e);
if ok then writeln('Reponse a AT : OK') else writeln('Reponse a AT : rien');
end.Avant l'envoi : erreur 1
Reponse a AT : OKNeoBASIC
10 ' Lire la reponse du modem octet par octet (groupe 14, fonction 2)
20 r = alloc(128) : nr = 0 : ' reponse du modem
30 gosub 800 : e1 = e : ' rien n'est encore arrive
40 s$ = "AT" : gosub 2000
50 f = time() + 300 : p = 0 : ok = 0
60 repeat : gosub 800
70 if e = 0 then if p = 79 & o = 75 then ok = 1
80 if e = 0 then p = o
90 until ok | time() > f
100 print "Avant l'envoi : erreur ";e1
110 r$ = "rien" : if ok then r$ = "OK"
120 print "Reponse a AT : ";r$
130 end
800 ' Serial Read Byte : octet dans o, erreur dans e
810 while peek($ff00) : wend : poke $ff0b, 0 : poke $ff01, 2 : poke $ff00, 14
820 while peek($ff00) : wend : e = peek($ff02) : o = peek($ff04)
830 return
900 ' 14,1 : octets en attente sur l'appareil 0 dans w (65535 si absent)
910 while peek($ff00) : wend : poke $ff0b, 0 : poke $ff01, 1 : poke $ff00, 14
920 while peek($ff00) : wend : w = 65535 : if peek($ff04) then w = deek($ff05)
930 return
2000 ' 14,5 : envoie s$ (plus un retour chariot) ; nombre d'octets ecrits dans n
2010 b = alloc(40) : for i = 1 to len(s$) : poke b + i - 1, asc(mid$(s$, i, 1)) : next i : poke b + len(s$), 13
2020 while peek($ff00) : wend
2030 doke $ff04, b : doke $ff06, len(s$) + 1 : poke $ff0b, 0 : poke $ff01, 5 : poke $ff00, 14
2040 while peek($ff00) : wend : n = peek($ff06)
2050 return
2100 ' 14,4 : ajoute a r (nr octets deja lus) ce qui est arrive
2110 while peek($ff00) : wend
2120 doke $ff04, r + nr : doke $ff06, 127 - nr : poke $ff0b, 0 : poke $ff01, 4 : poke $ff00, 14
2130 while peek($ff00) : wend : nr = nr + peek($ff06)
2140 return
2200 ' lit jusqu'a OK ou 3 secondes (time() : horloge en cs) ; ok = 1 si OK recu
2210 ok = 0 : f = time() + 300
2220 repeat : gosub 2100
2230 for i = 0 to nr - 2 : if peek(r + i) = 79 & peek(r + i + 1) = 75 then ok = 1
2240 next i
2250 until ok | time() > f
2260 returnAvant l'envoi : erreur 1
Reponse a AT : OKAssembleur 65C02 (64tass)
; Lire la réponse du modem octet par octet (groupe 14, 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: jsr lire_octet ; rien n'est encore arrivé : C = 1
lda #0
rol a
sta e
lda #<cmd_at
ldx #>cmd_at
ldy #3
jsr envoyer
stz prec
stz ok
jsr depart
_b: jsr lire_octet
bcs _t ; rien pour l'instant
ldx prec ; octet précédent
sta prec
cpx #'O'
bne _t
cmp #'K'
bne _t
lda #1 ; « OK » reçu
sta ok
bra _fin
_t: jsr ecoule
bcc _b
_fin: lda #<t0
ldx #>t0
jsr ecrire
lda e
jsr decimal
lda #13
jsr WriteCharacter
lda #<t1
ldx #>t1
jsr ecrire
lda ok
bne _ook
lda #<tr
ldx #>tr
jsr ecrire
bra _eok
_ook: lda #<tok
ldx #>tok
jsr ecrire
_eok:
lda #13
jsr WriteCharacter
rts
; Serial Read Byte : C = 1 si rien n'est arrivé (erreur), sinon l'octet dans A.
lire_octet:
jsr WaitMessage
stz ControlPort+11 ; P7 : appareil 0
lda #14
ldx #2 ; 14,2 : Serial Read Byte
jsr appel
lda ControlPort+2 ; erreur ?
cmp #1 ; C = 1 si erreur
lda ControlPort+4 ; l'octet
rts
; 14,1 : octets en attente sur l'appareil 0 dans w (16 bits) ; C = 1 si l'appareil est absent.
attente:
jsr WaitMessage
stz ControlPort+11 ; P7 : appareil 0, le premier
lda #14
ldx #1 ; 14,1 : Serial Status
jsr appel
lda ControlPort+5 ; P1-P2 : octets reçus pas encore lus
sta w
lda ControlPort+6
sta w+1
lda ControlPort+4 ; P0 : 1 si branché
cmp #1 ; C = 0 si absent...
eor #1
lsr a ; ... d'où C = 1 si absent
rts
w: .word 0
; 14,5 : envoie n octets depuis l'adresse A (poids faible), X (poids fort), n dans Y ; nombre écrit dans A.
envoyer:
pha
jsr WaitMessage
pla
sta ControlPort+4 ; P0-P1 : adresse
stx ControlPort+5
sty ControlPort+6 ; P2-P3 : nombre d'octets
stz ControlPort+7
stz ControlPort+11 ; P7 : appareil 0
lda #14
ldx #5 ; 14,5 : Serial Write Block
jsr appel
lda ControlPort+6
rts
; 14,4 : ajoute à recu ce qui est arrivé (nrecu octets déjà lus).
lire: jsr WaitMessage
clc
lda #<recu
adc nrecu
sta ControlPort+4 ; P0-P1 : recu + nrecu
lda #>recu
adc #0
sta ControlPort+5
sec
lda #127
sbc nrecu
sta ControlPort+6 ; P2-P3 : place restante
stz ControlPort+7
stz ControlPort+11
lda #14
ldx #4 ; 14,4 : Serial Read Block
jsr appel
clc
lda nrecu
adc ControlPort+6 ; P2-P3 : octets lus
sta nrecu
rts
; Horloge (1,1) en centièmes, 16 bits bas dans A (poids faible) et X (poids fort).
horloge:
jsr WaitMessage
lda #1
ldx #1 ; 1,1 : Timer
jsr appel
lda ControlPort+4
ldx ControlPort+5
rts
; Note le départ d'une attente de 3 secondes.
depart: jsr horloge
sta debut
stx debut+1
rts
; C = 1 si 3 secondes (300 cs) sont passées depuis depart.
ecoule: jsr horloge
sec
sbc debut
tay
txa
sbc debut+1
cmp #>300 ; (maintenant - départ) >= 300 ?
bne _f
cpy #<300
_f: rts
debut: .word 0
; Lit jusqu'à « OK » ou 3 secondes ; A = 1 si OK reçu.
attendre_ok:
jsr depart
_b: jsr lire
ldx #0
_c: inx
cpx nrecu
bcs _pas
lda recu-1,x
cmp #'O'
bne _c
lda recu,x
cmp #'K'
bne _c
lda #1
rts
_pas: jsr ecoule
bcc _b
lda #0
rts
recu: .fill 128 ; réponse du modem
nrecu: .byte 0
cmd_at: .text "AT", 13
t0: .text "Avant l'envoi : erreur ", 0
t1: .text "Reponse a AT : ", 0
tok: .text "OK", 0
tr: .text "rien", 0
e: .byte 0
ok: .byte 0
prec: .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 0Avant l'envoi : erreur 1
Reponse a AT : OK14,3 Serial Write Byte Trinity
Envoie un octet à l'appareil série USB.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | octet à envoyer |
P7 | uint8 | appareil : 0 ou 1 |
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | 1 si l'appareil est absent ou n'a pas pris l'octet |
Pour plus d'un octet, 14,5 fait tout en un appel. L'exemple envoie A, T et un retour chariot, un par un, puis attend « OK ».
Texte de la documentation du firmware (anglais)
Write the byte in Parameter:0 to the CDC device (Parameter:7). Sets the error flag if no device is connected.
C (llvm-mos)
/* Envoyer une commande au modem octet par octet (groupe 14, 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)
;
}
static uint16_t en_attente(void) { /* 14,1 : octets reçus en attente (appareil 0) ; 0xFFFF si absent */
libre();
ControlPort.params[7] = 0; /* P7 : appareil 0, le premier */
api(14, 1);
if (!ControlPort.params[0])
return 0xFFFF;
return ControlPort.params[1] | ControlPort.params[2] << 8;
}
static uint8_t envoyer(const char *s, uint8_t n) { /* 14,5 : écrit n octets ; rend le nombre écrit */
libre();
ControlPort.params[0] = (uint16_t)s & 0xFF; /* P0-P1 : adresse */
ControlPort.params[1] = (uint16_t)s >> 8;
ControlPort.params[2] = n; /* P2-P3 : nombre d'octets */
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 5);
return ControlPort.params[2];
}
static char recu[128];
static uint8_t nrecu;
static void lire(void) { /* 14,4 : ajoute à recu ce qui est arrivé */
libre();
ControlPort.params[0] = (uint16_t)(recu + nrecu) & 0xFF;
ControlPort.params[1] = (uint16_t)(recu + nrecu) >> 8;
ControlPort.params[2] = sizeof recu - 1 - nrecu;
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 4);
nrecu += ControlPort.params[2];
recu[nrecu] = 0;
}
static uint8_t attendre_ok(void) { /* lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs) */
uint32_t fin = neo_system_timer() + 300;
while (neo_system_timer() < fin) {
lire();
for (uint8_t i = 0; i + 1 < nrecu; i++)
if (recu[i] == 'O' && recu[i + 1] == 'K')
return 1;
}
return 0;
}
static uint8_t ecrire(uint8_t c) { /* Serial Write Byte : rend l'indicateur d'erreur */
libre();
ControlPort.params[0] = c;
ControlPort.params[7] = 0;
api(14, 3);
return ControlPort.error;
}
int main(void) {
uint8_t e = ecrire('A') | ecrire('T') | ecrire('\r');
uint8_t ok = attendre_ok();
printf("Envoi de AT : erreur %u\n", e);
printf("Reponse : %s\n", ok ? "OK" : "rien");
return 0;
}Envoi de AT : erreur 0
Reponse : OKMad Pascal
// Envoyer une commande au modem octet par octet (groupe 14, fonction 3).
program cdc3;
uses crt, neo6502;
var recu: array[0..127] of byte; // réponse du modem
nrecu: byte;
function en_attente: word; // 14,1 : octets reçus en attente (appareil 0) ; $FFFF si absent
begin
NeoWaitMessage;
NeoMessage.params[7] := 0; // P7 : appareil 0, le premier
NeoSendMessage(14, 1);
NeoWaitMessage;
if NeoMessage.params[0] = 0 then result := $FFFF
else result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
function envoyer(adresse: word; n: byte): byte; // 14,5 : écrit n octets ; rend le nombre écrit
begin
NeoWaitMessage;
wordParams[0] := adresse; // P0-P1 : adresse
wordParams[1] := n; // P2-P3 : nombre d'octets
NeoMessage.params[7] := 0;
NeoSendMessage(14, 5);
NeoWaitMessage;
result := NeoMessage.params[2];
end;
procedure lire; // 14,4 : ajoute à recu ce qui est arrivé
begin
NeoWaitMessage;
wordParams[0] := word(@recu) + nrecu;
wordParams[1] := 127 - nrecu;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 4);
NeoWaitMessage;
nrecu := nrecu + NeoMessage.params[2];
end;
function attendre_ok: boolean; // lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs)
var fin: cardinal;
i: byte;
begin
result := false;
fin := NeoGetTimer + 300;
while (not result) and (NeoGetTimer < fin) do begin
lire;
if nrecu > 1 then
for i := 0 to nrecu - 2 do
if (recu[i] = ord('O')) and (recu[i + 1] = ord('K')) then result := true;
end;
end;
var e: byte;
ok: boolean;
function ecrire(c: byte): byte; // Serial Write Byte : rend l'indicateur d'erreur
begin
NeoWaitMessage;
NeoMessage.params[0] := c;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 3);
NeoWaitMessage;
result := NeoMessage.error;
end;
begin
e := ecrire(ord('A')) or ecrire(ord('T')) or ecrire(13);
ok := attendre_ok;
writeln('Envoi de AT : erreur ', e);
if ok then writeln('Reponse : OK') else writeln('Reponse : rien');
end.Envoi de AT : erreur 0
Reponse : OKNeoBASIC
10 ' Envoyer une commande au modem octet par octet (groupe 14, fonction 3)
20 r = alloc(128) : nr = 0 : ' reponse du modem
30 c = asc("A") : gosub 800 : e1 = e : c = asc("T") : gosub 800 : e1 = e1 | e : c = 13 : gosub 800 : e1 = e1 | e
40 gosub 2200
50 print "Envoi de AT : erreur ";e1
60 r$ = "rien" : if ok then r$ = "OK"
70 print "Reponse : ";r$
80 end
800 ' Serial Write Byte : octet c, erreur dans e
810 while peek($ff00) : wend : poke $ff04, c : poke $ff0b, 0 : poke $ff01, 3 : poke $ff00, 14
820 while peek($ff00) : wend : e = peek($ff02)
830 return
900 ' 14,1 : octets en attente sur l'appareil 0 dans w (65535 si absent)
910 while peek($ff00) : wend : poke $ff0b, 0 : poke $ff01, 1 : poke $ff00, 14
920 while peek($ff00) : wend : w = 65535 : if peek($ff04) then w = deek($ff05)
930 return
2000 ' 14,5 : envoie s$ (plus un retour chariot) ; nombre d'octets ecrits dans n
2010 b = alloc(40) : for i = 1 to len(s$) : poke b + i - 1, asc(mid$(s$, i, 1)) : next i : poke b + len(s$), 13
2020 while peek($ff00) : wend
2030 doke $ff04, b : doke $ff06, len(s$) + 1 : poke $ff0b, 0 : poke $ff01, 5 : poke $ff00, 14
2040 while peek($ff00) : wend : n = peek($ff06)
2050 return
2100 ' 14,4 : ajoute a r (nr octets deja lus) ce qui est arrive
2110 while peek($ff00) : wend
2120 doke $ff04, r + nr : doke $ff06, 127 - nr : poke $ff0b, 0 : poke $ff01, 4 : poke $ff00, 14
2130 while peek($ff00) : wend : nr = nr + peek($ff06)
2140 return
2200 ' lit jusqu'a OK ou 3 secondes (time() : horloge en cs) ; ok = 1 si OK recu
2210 ok = 0 : f = time() + 300
2220 repeat : gosub 2100
2230 for i = 0 to nr - 2 : if peek(r + i) = 79 & peek(r + i + 1) = 75 then ok = 1
2240 next i
2250 until ok | time() > f
2260 returnEnvoi de AT : erreur 0
Reponse : OKAssembleur 65C02 (64tass)
; Envoyer une commande au modem octet par octet (groupe 14, 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 #'A'
jsr envoi1
sta e
lda #'T'
jsr envoi1
ora e
sta e
lda #13
jsr envoi1
ora e
sta e
jsr attendre_ok
sta ok
lda #<t0
ldx #>t0
jsr ecrire
lda e
jsr decimal
lda #13
jsr WriteCharacter
lda #<t1
ldx #>t1
jsr ecrire
lda ok
bne _ook
lda #<tr
ldx #>tr
jsr ecrire
bra _eok
_ook: lda #<tok
ldx #>tok
jsr ecrire
_eok:
lda #13
jsr WriteCharacter
rts
; Serial Write Byte : envoie A ; erreur rendue dans A.
envoi1: pha
jsr WaitMessage
pla
sta ControlPort+4
stz ControlPort+11 ; P7 : appareil 0
lda #14
ldx #3 ; 14,3 : Serial Write Byte
jsr appel
lda ControlPort+2
rts
; 14,1 : octets en attente sur l'appareil 0 dans w (16 bits) ; C = 1 si l'appareil est absent.
attente:
jsr WaitMessage
stz ControlPort+11 ; P7 : appareil 0, le premier
lda #14
ldx #1 ; 14,1 : Serial Status
jsr appel
lda ControlPort+5 ; P1-P2 : octets reçus pas encore lus
sta w
lda ControlPort+6
sta w+1
lda ControlPort+4 ; P0 : 1 si branché
cmp #1 ; C = 0 si absent...
eor #1
lsr a ; ... d'où C = 1 si absent
rts
w: .word 0
; 14,5 : envoie n octets depuis l'adresse A (poids faible), X (poids fort), n dans Y ; nombre écrit dans A.
envoyer:
pha
jsr WaitMessage
pla
sta ControlPort+4 ; P0-P1 : adresse
stx ControlPort+5
sty ControlPort+6 ; P2-P3 : nombre d'octets
stz ControlPort+7
stz ControlPort+11 ; P7 : appareil 0
lda #14
ldx #5 ; 14,5 : Serial Write Block
jsr appel
lda ControlPort+6
rts
; 14,4 : ajoute à recu ce qui est arrivé (nrecu octets déjà lus).
lire: jsr WaitMessage
clc
lda #<recu
adc nrecu
sta ControlPort+4 ; P0-P1 : recu + nrecu
lda #>recu
adc #0
sta ControlPort+5
sec
lda #127
sbc nrecu
sta ControlPort+6 ; P2-P3 : place restante
stz ControlPort+7
stz ControlPort+11
lda #14
ldx #4 ; 14,4 : Serial Read Block
jsr appel
clc
lda nrecu
adc ControlPort+6 ; P2-P3 : octets lus
sta nrecu
rts
; Horloge (1,1) en centièmes, 16 bits bas dans A (poids faible) et X (poids fort).
horloge:
jsr WaitMessage
lda #1
ldx #1 ; 1,1 : Timer
jsr appel
lda ControlPort+4
ldx ControlPort+5
rts
; Note le départ d'une attente de 3 secondes.
depart: jsr horloge
sta debut
stx debut+1
rts
; C = 1 si 3 secondes (300 cs) sont passées depuis depart.
ecoule: jsr horloge
sec
sbc debut
tay
txa
sbc debut+1
cmp #>300 ; (maintenant - départ) >= 300 ?
bne _f
cpy #<300
_f: rts
debut: .word 0
; Lit jusqu'à « OK » ou 3 secondes ; A = 1 si OK reçu.
attendre_ok:
jsr depart
_b: jsr lire
ldx #0
_c: inx
cpx nrecu
bcs _pas
lda recu-1,x
cmp #'O'
bne _c
lda recu,x
cmp #'K'
bne _c
lda #1
rts
_pas: jsr ecoule
bcc _b
lda #0
rts
recu: .fill 128 ; réponse du modem
nrecu: .byte 0
t0: .text "Envoi de AT : erreur ", 0
t1: .text "Reponse : ", 0
tok: .text "OK", 0
tr: .text "rien", 0
e: .byte 0
ok: .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 0Envoi de AT : erreur 0
Reponse : OK14,4 Serial Read Block Trinity
Lit d'un coup les octets reçus de l'appareil série USB, jusqu'à un maximum, sans attendre.
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | addr | destination |
P2-P3 | uint16 | nombre maximal d'octets |
P7 | uint8 | appareil : 0 ou 1 |
| Octets | Type | Rôle |
|---|---|---|
P2-P3 | uint16 | nombre d'octets lus (peut être 0) |
erreur | uint8 | 1 si l'appareil est absent |
Rendre 0 octet n'est pas une erreur : on rappelle plus tard. La longueur est bornée à la fin de la mémoire du 6502. L'exemple envoie ATI (identification) et lit par blocs jusqu'à « OK » ; le texte de l'identification dépend du modem.
Texte de la documentation du firmware (anglais)
Read up to Parameters:2,3 bytes into memory at Parameters:0,1 from the CDC device (Parameter:7) ; Parameters:2,3 return the count actually read (may be 0).
C (llvm-mos)
/* Envoyer une commande au modem et lire sa réponse par blocs (groupe 14, 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 uint16_t en_attente(void) { /* 14,1 : octets reçus en attente (appareil 0) ; 0xFFFF si absent */
libre();
ControlPort.params[7] = 0; /* P7 : appareil 0, le premier */
api(14, 1);
if (!ControlPort.params[0])
return 0xFFFF;
return ControlPort.params[1] | ControlPort.params[2] << 8;
}
static uint8_t envoyer(const char *s, uint8_t n) { /* 14,5 : écrit n octets ; rend le nombre écrit */
libre();
ControlPort.params[0] = (uint16_t)s & 0xFF; /* P0-P1 : adresse */
ControlPort.params[1] = (uint16_t)s >> 8;
ControlPort.params[2] = n; /* P2-P3 : nombre d'octets */
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 5);
return ControlPort.params[2];
}
static char recu[128];
static uint8_t nrecu;
static void lire(void) { /* 14,4 : ajoute à recu ce qui est arrivé */
libre();
ControlPort.params[0] = (uint16_t)(recu + nrecu) & 0xFF;
ControlPort.params[1] = (uint16_t)(recu + nrecu) >> 8;
ControlPort.params[2] = sizeof recu - 1 - nrecu;
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 4);
nrecu += ControlPort.params[2];
recu[nrecu] = 0;
}
static uint8_t attendre_ok(void) { /* lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs) */
uint32_t fin = neo_system_timer() + 300;
while (neo_system_timer() < fin) {
lire();
for (uint8_t i = 0; i + 1 < nrecu; i++)
if (recu[i] == 'O' && recu[i + 1] == 'K')
return 1;
}
return 0;
}
int main(void) {
envoyer("ATI\r", 4); /* 14,5 : identification du modem */
uint8_t ok = attendre_ok(); /* Serial Read Block, en boucle jusqu'à « OK » */
printf("Reponse a ATI : %s\n", ok && nrecu > 4 ? "texte, puis OK" : "incomplete");
return 0;
}Reponse a ATI : texte, puis OKMad Pascal
// Envoyer une commande au modem et lire sa réponse par blocs (groupe 14, fonction 4).
program cdc4;
uses crt, neo6502;
var recu: array[0..127] of byte; // réponse du modem
nrecu: byte;
function en_attente: word; // 14,1 : octets reçus en attente (appareil 0) ; $FFFF si absent
begin
NeoWaitMessage;
NeoMessage.params[7] := 0; // P7 : appareil 0, le premier
NeoSendMessage(14, 1);
NeoWaitMessage;
if NeoMessage.params[0] = 0 then result := $FFFF
else result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
function envoyer(adresse: word; n: byte): byte; // 14,5 : écrit n octets ; rend le nombre écrit
begin
NeoWaitMessage;
wordParams[0] := adresse; // P0-P1 : adresse
wordParams[1] := n; // P2-P3 : nombre d'octets
NeoMessage.params[7] := 0;
NeoSendMessage(14, 5);
NeoWaitMessage;
result := NeoMessage.params[2];
end;
procedure lire; // 14,4 : ajoute à recu ce qui est arrivé
begin
NeoWaitMessage;
wordParams[0] := word(@recu) + nrecu;
wordParams[1] := 127 - nrecu;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 4);
NeoWaitMessage;
nrecu := nrecu + NeoMessage.params[2];
end;
function attendre_ok: boolean; // lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs)
var fin: cardinal;
i: byte;
begin
result := false;
fin := NeoGetTimer + 300;
while (not result) and (NeoGetTimer < fin) do begin
lire;
if nrecu > 1 then
for i := 0 to nrecu - 2 do
if (recu[i] = ord('O')) and (recu[i + 1] = ord('K')) then result := true;
end;
end;
const ati: array[0..3] of byte = (ord('A'), ord('T'), ord('I'), 13);
var ok: boolean;
begin
envoyer(word(@ati), 4); // 14,5 : identification du modem
ok := attendre_ok; // Serial Read Block, en boucle jusqu'à « OK »
if ok and (nrecu > 4) then writeln('Reponse a ATI : texte, puis OK') else writeln('Reponse a ATI : incomplete');
end.Reponse a ATI : texte, puis OKNeoBASIC
10 ' Envoyer une commande au modem et lire sa reponse par blocs (groupe 14, fonction 4)
20 r = alloc(128) : nr = 0 : ' reponse du modem
30 s$ = "ATI" : gosub 2000 : ' 14,5 : identification du modem
40 gosub 2200 : ' Serial Read Block, en boucle jusqu'a OK
50 r$ = "incomplete" : if ok & nr > 4 then r$ = "texte, puis OK"
60 print "Reponse a ATI : ";r$
70 end
900 ' 14,1 : octets en attente sur l'appareil 0 dans w (65535 si absent)
910 while peek($ff00) : wend : poke $ff0b, 0 : poke $ff01, 1 : poke $ff00, 14
920 while peek($ff00) : wend : w = 65535 : if peek($ff04) then w = deek($ff05)
930 return
2000 ' 14,5 : envoie s$ (plus un retour chariot) ; nombre d'octets ecrits dans n
2010 b = alloc(40) : for i = 1 to len(s$) : poke b + i - 1, asc(mid$(s$, i, 1)) : next i : poke b + len(s$), 13
2020 while peek($ff00) : wend
2030 doke $ff04, b : doke $ff06, len(s$) + 1 : poke $ff0b, 0 : poke $ff01, 5 : poke $ff00, 14
2040 while peek($ff00) : wend : n = peek($ff06)
2050 return
2100 ' 14,4 : ajoute a r (nr octets deja lus) ce qui est arrive
2110 while peek($ff00) : wend
2120 doke $ff04, r + nr : doke $ff06, 127 - nr : poke $ff0b, 0 : poke $ff01, 4 : poke $ff00, 14
2130 while peek($ff00) : wend : nr = nr + peek($ff06)
2140 return
2200 ' lit jusqu'a OK ou 3 secondes (time() : horloge en cs) ; ok = 1 si OK recu
2210 ok = 0 : f = time() + 300
2220 repeat : gosub 2100
2230 for i = 0 to nr - 2 : if peek(r + i) = 79 & peek(r + i + 1) = 75 then ok = 1
2240 next i
2250 until ok | time() > f
2260 returnReponse a ATI : texte, puis OKAssembleur 65C02 (64tass)
; Envoyer une commande au modem et lire sa réponse par blocs (groupe 14, 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 #<cmd_ati ; 14,5 : identification du modem
ldx #>cmd_ati
ldy #4
jsr envoyer
jsr attendre_ok ; Serial Read Block, en boucle jusqu'à « OK »
beq _inc
lda nrecu
cmp #5
bcc _inc
lda #<tok
ldx #>tok
jsr ecrire
rts
_inc: lda #<tinc
ldx #>tinc
jsr ecrire
rts
; 14,1 : octets en attente sur l'appareil 0 dans w (16 bits) ; C = 1 si l'appareil est absent.
attente:
jsr WaitMessage
stz ControlPort+11 ; P7 : appareil 0, le premier
lda #14
ldx #1 ; 14,1 : Serial Status
jsr appel
lda ControlPort+5 ; P1-P2 : octets reçus pas encore lus
sta w
lda ControlPort+6
sta w+1
lda ControlPort+4 ; P0 : 1 si branché
cmp #1 ; C = 0 si absent...
eor #1
lsr a ; ... d'où C = 1 si absent
rts
w: .word 0
; 14,5 : envoie n octets depuis l'adresse A (poids faible), X (poids fort), n dans Y ; nombre écrit dans A.
envoyer:
pha
jsr WaitMessage
pla
sta ControlPort+4 ; P0-P1 : adresse
stx ControlPort+5
sty ControlPort+6 ; P2-P3 : nombre d'octets
stz ControlPort+7
stz ControlPort+11 ; P7 : appareil 0
lda #14
ldx #5 ; 14,5 : Serial Write Block
jsr appel
lda ControlPort+6
rts
; 14,4 : ajoute à recu ce qui est arrivé (nrecu octets déjà lus).
lire: jsr WaitMessage
clc
lda #<recu
adc nrecu
sta ControlPort+4 ; P0-P1 : recu + nrecu
lda #>recu
adc #0
sta ControlPort+5
sec
lda #127
sbc nrecu
sta ControlPort+6 ; P2-P3 : place restante
stz ControlPort+7
stz ControlPort+11
lda #14
ldx #4 ; 14,4 : Serial Read Block
jsr appel
clc
lda nrecu
adc ControlPort+6 ; P2-P3 : octets lus
sta nrecu
rts
; Horloge (1,1) en centièmes, 16 bits bas dans A (poids faible) et X (poids fort).
horloge:
jsr WaitMessage
lda #1
ldx #1 ; 1,1 : Timer
jsr appel
lda ControlPort+4
ldx ControlPort+5
rts
; Note le départ d'une attente de 3 secondes.
depart: jsr horloge
sta debut
stx debut+1
rts
; C = 1 si 3 secondes (300 cs) sont passées depuis depart.
ecoule: jsr horloge
sec
sbc debut
tay
txa
sbc debut+1
cmp #>300 ; (maintenant - départ) >= 300 ?
bne _f
cpy #<300
_f: rts
debut: .word 0
; Lit jusqu'à « OK » ou 3 secondes ; A = 1 si OK reçu.
attendre_ok:
jsr depart
_b: jsr lire
ldx #0
_c: inx
cpx nrecu
bcs _pas
lda recu-1,x
cmp #'O'
bne _c
lda recu,x
cmp #'K'
bne _c
lda #1
rts
_pas: jsr ecoule
bcc _b
lda #0
rts
recu: .fill 128 ; réponse du modem
nrecu: .byte 0
cmd_ati: .text "ATI", 13
tok: .text "Reponse a ATI : texte, puis OK", 13, 0
tinc: .text "Reponse a ATI : incomplete", 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 0Reponse a ATI : texte, puis OK14,5 Serial Write Block Trinity
Envoie d'un coup une suite d'octets à l'appareil série USB.
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | addr | source |
P2-P3 | uint16 | nombre d'octets |
P7 | uint8 | appareil : 0 ou 1 |
| Octets | Type | Rôle |
|---|---|---|
P2-P3 | uint16 | nombre d'octets écrits |
erreur | uint8 | 1 si l'appareil est absent |
Comparer le nombre écrit au nombre demandé : l'appareil peut en prendre moins. La longueur est bornée à la fin de la mémoire du 6502.
Texte de la documentation du firmware (anglais)
Write Parameters:2,3 bytes from memory at Parameters:0,1 to the CDC device (Parameter:7) ; Parameters:2,3 return the count actually written.
C (llvm-mos)
/* Envoyer un bloc d'octets au modem (groupe 14, 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)
;
}
static uint16_t en_attente(void) { /* 14,1 : octets reçus en attente (appareil 0) ; 0xFFFF si absent */
libre();
ControlPort.params[7] = 0; /* P7 : appareil 0, le premier */
api(14, 1);
if (!ControlPort.params[0])
return 0xFFFF;
return ControlPort.params[1] | ControlPort.params[2] << 8;
}
static uint8_t envoyer(const char *s, uint8_t n) { /* 14,5 : écrit n octets ; rend le nombre écrit */
libre();
ControlPort.params[0] = (uint16_t)s & 0xFF; /* P0-P1 : adresse */
ControlPort.params[1] = (uint16_t)s >> 8;
ControlPort.params[2] = n; /* P2-P3 : nombre d'octets */
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 5);
return ControlPort.params[2];
}
static char recu[128];
static uint8_t nrecu;
static void lire(void) { /* 14,4 : ajoute à recu ce qui est arrivé */
libre();
ControlPort.params[0] = (uint16_t)(recu + nrecu) & 0xFF;
ControlPort.params[1] = (uint16_t)(recu + nrecu) >> 8;
ControlPort.params[2] = sizeof recu - 1 - nrecu;
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 4);
nrecu += ControlPort.params[2];
recu[nrecu] = 0;
}
static uint8_t attendre_ok(void) { /* lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs) */
uint32_t fin = neo_system_timer() + 300;
while (neo_system_timer() < fin) {
lire();
for (uint8_t i = 0; i + 1 < nrecu; i++)
if (recu[i] == 'O' && recu[i + 1] == 'K')
return 1;
}
return 0;
}
int main(void) {
uint8_t n = envoyer("AT\r", 3); /* Serial Write Block : rend le nombre d'octets écrits */
uint8_t ok = attendre_ok();
printf("Ecrits : %u octets, reponse : %s\n", n, ok ? "OK" : "rien");
return 0;
}Ecrits : 3 octets, reponse : OKMad Pascal
// Envoyer un bloc d'octets au modem (groupe 14, fonction 5).
program cdc5;
uses crt, neo6502;
var recu: array[0..127] of byte; // réponse du modem
nrecu: byte;
function en_attente: word; // 14,1 : octets reçus en attente (appareil 0) ; $FFFF si absent
begin
NeoWaitMessage;
NeoMessage.params[7] := 0; // P7 : appareil 0, le premier
NeoSendMessage(14, 1);
NeoWaitMessage;
if NeoMessage.params[0] = 0 then result := $FFFF
else result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
function envoyer(adresse: word; n: byte): byte; // 14,5 : écrit n octets ; rend le nombre écrit
begin
NeoWaitMessage;
wordParams[0] := adresse; // P0-P1 : adresse
wordParams[1] := n; // P2-P3 : nombre d'octets
NeoMessage.params[7] := 0;
NeoSendMessage(14, 5);
NeoWaitMessage;
result := NeoMessage.params[2];
end;
procedure lire; // 14,4 : ajoute à recu ce qui est arrivé
begin
NeoWaitMessage;
wordParams[0] := word(@recu) + nrecu;
wordParams[1] := 127 - nrecu;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 4);
NeoWaitMessage;
nrecu := nrecu + NeoMessage.params[2];
end;
function attendre_ok: boolean; // lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs)
var fin: cardinal;
i: byte;
begin
result := false;
fin := NeoGetTimer + 300;
while (not result) and (NeoGetTimer < fin) do begin
lire;
if nrecu > 1 then
for i := 0 to nrecu - 2 do
if (recu[i] = ord('O')) and (recu[i + 1] = ord('K')) then result := true;
end;
end;
const at: array[0..2] of byte = (ord('A'), ord('T'), 13);
var n: byte;
ok: boolean;
begin
n := envoyer(word(@at), 3); // Serial Write Block : rend le nombre d'octets écrits
ok := attendre_ok;
write('Ecrits : ', n, ' octets, reponse : ');
if ok then writeln('OK') else writeln('rien');
end.Ecrits : 3 octets, reponse : OKNeoBASIC
10 ' Envoyer un bloc d'octets au modem (groupe 14, fonction 5)
20 r = alloc(128) : nr = 0 : ' reponse du modem
30 s$ = "AT" : gosub 2000 : ' Serial Write Block : nombre d'octets ecrits dans n
40 gosub 2200
50 r$ = "rien" : if ok then r$ = "OK"
60 print "Ecrits : ";n;" octets, reponse : ";r$
70 end
900 ' 14,1 : octets en attente sur l'appareil 0 dans w (65535 si absent)
910 while peek($ff00) : wend : poke $ff0b, 0 : poke $ff01, 1 : poke $ff00, 14
920 while peek($ff00) : wend : w = 65535 : if peek($ff04) then w = deek($ff05)
930 return
2000 ' 14,5 : envoie s$ (plus un retour chariot) ; nombre d'octets ecrits dans n
2010 b = alloc(40) : for i = 1 to len(s$) : poke b + i - 1, asc(mid$(s$, i, 1)) : next i : poke b + len(s$), 13
2020 while peek($ff00) : wend
2030 doke $ff04, b : doke $ff06, len(s$) + 1 : poke $ff0b, 0 : poke $ff01, 5 : poke $ff00, 14
2040 while peek($ff00) : wend : n = peek($ff06)
2050 return
2100 ' 14,4 : ajoute a r (nr octets deja lus) ce qui est arrive
2110 while peek($ff00) : wend
2120 doke $ff04, r + nr : doke $ff06, 127 - nr : poke $ff0b, 0 : poke $ff01, 4 : poke $ff00, 14
2130 while peek($ff00) : wend : nr = nr + peek($ff06)
2140 return
2200 ' lit jusqu'a OK ou 3 secondes (time() : horloge en cs) ; ok = 1 si OK recu
2210 ok = 0 : f = time() + 300
2220 repeat : gosub 2100
2230 for i = 0 to nr - 2 : if peek(r + i) = 79 & peek(r + i + 1) = 75 then ok = 1
2240 next i
2250 until ok | time() > f
2260 returnEcrits : 3 octets, reponse : OKAssembleur 65C02 (64tass)
; Envoyer un bloc d'octets au modem (groupe 14, 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 #<cmd_at
ldx #>cmd_at
ldy #3
jsr envoyer ; Serial Write Block : nombre d'octets écrits dans A
sta n
jsr attendre_ok
sta ok
lda #<t0
ldx #>t0
jsr ecrire
lda n
jsr decimal
lda #<t1
ldx #>t1
jsr ecrire
lda ok
bne _ook
lda #<tr
ldx #>tr
jsr ecrire
bra _eok
_ook: lda #<tok
ldx #>tok
jsr ecrire
_eok:
lda #13
jsr WriteCharacter
rts
; 14,1 : octets en attente sur l'appareil 0 dans w (16 bits) ; C = 1 si l'appareil est absent.
attente:
jsr WaitMessage
stz ControlPort+11 ; P7 : appareil 0, le premier
lda #14
ldx #1 ; 14,1 : Serial Status
jsr appel
lda ControlPort+5 ; P1-P2 : octets reçus pas encore lus
sta w
lda ControlPort+6
sta w+1
lda ControlPort+4 ; P0 : 1 si branché
cmp #1 ; C = 0 si absent...
eor #1
lsr a ; ... d'où C = 1 si absent
rts
w: .word 0
; 14,5 : envoie n octets depuis l'adresse A (poids faible), X (poids fort), n dans Y ; nombre écrit dans A.
envoyer:
pha
jsr WaitMessage
pla
sta ControlPort+4 ; P0-P1 : adresse
stx ControlPort+5
sty ControlPort+6 ; P2-P3 : nombre d'octets
stz ControlPort+7
stz ControlPort+11 ; P7 : appareil 0
lda #14
ldx #5 ; 14,5 : Serial Write Block
jsr appel
lda ControlPort+6
rts
; 14,4 : ajoute à recu ce qui est arrivé (nrecu octets déjà lus).
lire: jsr WaitMessage
clc
lda #<recu
adc nrecu
sta ControlPort+4 ; P0-P1 : recu + nrecu
lda #>recu
adc #0
sta ControlPort+5
sec
lda #127
sbc nrecu
sta ControlPort+6 ; P2-P3 : place restante
stz ControlPort+7
stz ControlPort+11
lda #14
ldx #4 ; 14,4 : Serial Read Block
jsr appel
clc
lda nrecu
adc ControlPort+6 ; P2-P3 : octets lus
sta nrecu
rts
; Horloge (1,1) en centièmes, 16 bits bas dans A (poids faible) et X (poids fort).
horloge:
jsr WaitMessage
lda #1
ldx #1 ; 1,1 : Timer
jsr appel
lda ControlPort+4
ldx ControlPort+5
rts
; Note le départ d'une attente de 3 secondes.
depart: jsr horloge
sta debut
stx debut+1
rts
; C = 1 si 3 secondes (300 cs) sont passées depuis depart.
ecoule: jsr horloge
sec
sbc debut
tay
txa
sbc debut+1
cmp #>300 ; (maintenant - départ) >= 300 ?
bne _f
cpy #<300
_f: rts
debut: .word 0
; Lit jusqu'à « OK » ou 3 secondes ; A = 1 si OK reçu.
attendre_ok:
jsr depart
_b: jsr lire
ldx #0
_c: inx
cpx nrecu
bcs _pas
lda recu-1,x
cmp #'O'
bne _c
lda recu,x
cmp #'K'
bne _c
lda #1
rts
_pas: jsr ecoule
bcc _b
lda #0
rts
recu: .fill 128 ; réponse du modem
nrecu: .byte 0
cmd_at: .text "AT", 13
t0: .text "Ecrits : ", 0
t1: .text " octets, reponse : ", 0
tok: .text "OK", 0
tr: .text "rien", 0
n: .byte 0
ok: .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 0Ecrits : 3 octets, reponse : OK14,6 Serial Set Line Coding Trinity
Règle la vitesse et le format de la ligne série de l'appareil USB.
| Octets | Type | Rôle |
|---|---|---|
P0-P3 | uint32 | vitesse en bauds |
P4 | uint8 | bits de données (0 : 8) |
P5 | uint8 | parité : 0 aucune, 1 impaire, 2 paire |
P6 | uint8 | bits de stop, 1 ou 2 (0 : 1) |
P7 | uint8 | appareil : 0 ou 1 |
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | 1 si l'appareil est absent ou refuse le réglage |
Beaucoup de modems USB ignorent ce réglage : ils n'ont pas de vraie ligne série. Un adaptateur USB-série, lui, l'applique. L'exemple règle 115200 bauds, 8N1, sur l'appareil 0 (accepté) puis sur l'appareil 1, absent (erreur 1).
Texte de la documentation du firmware (anglais)
Set the line coding of the CDC device (Parameter:7) : Parameters:0..3 = baud rate (32 bits), Parameter:4 = data bits (default 8), Parameter:5 = parity (0 none, 1 odd, 2 even), Parameter:6 = stop bits (1 or 2). Many USB modems ignore it.
C (llvm-mos)
/* Régler la vitesse et le format de la ligne série du modem (groupe 14, 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)
;
}
static uint16_t en_attente(void) { /* 14,1 : octets reçus en attente (appareil 0) ; 0xFFFF si absent */
libre();
ControlPort.params[7] = 0; /* P7 : appareil 0, le premier */
api(14, 1);
if (!ControlPort.params[0])
return 0xFFFF;
return ControlPort.params[1] | ControlPort.params[2] << 8;
}
static uint8_t envoyer(const char *s, uint8_t n) { /* 14,5 : écrit n octets ; rend le nombre écrit */
libre();
ControlPort.params[0] = (uint16_t)s & 0xFF; /* P0-P1 : adresse */
ControlPort.params[1] = (uint16_t)s >> 8;
ControlPort.params[2] = n; /* P2-P3 : nombre d'octets */
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 5);
return ControlPort.params[2];
}
static char recu[128];
static uint8_t nrecu;
static void lire(void) { /* 14,4 : ajoute à recu ce qui est arrivé */
libre();
ControlPort.params[0] = (uint16_t)(recu + nrecu) & 0xFF;
ControlPort.params[1] = (uint16_t)(recu + nrecu) >> 8;
ControlPort.params[2] = sizeof recu - 1 - nrecu;
ControlPort.params[3] = 0;
ControlPort.params[7] = 0;
api(14, 4);
nrecu += ControlPort.params[2];
recu[nrecu] = 0;
}
static uint8_t attendre_ok(void) { /* lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs) */
uint32_t fin = neo_system_timer() + 300;
while (neo_system_timer() < fin) {
lire();
for (uint8_t i = 0; i + 1 < nrecu; i++)
if (recu[i] == 'O' && recu[i + 1] == 'K')
return 1;
}
return 0;
}
static uint8_t regler(uint8_t appareil) { /* Serial Set Line Coding : 115200 bauds, 8N1 */
uint32_t baud = 115200;
libre();
ControlPort.params[0] = baud & 0xFF; /* P0-P3 : vitesse */
ControlPort.params[1] = baud >> 8;
ControlPort.params[2] = baud >> 16;
ControlPort.params[3] = baud >> 24;
ControlPort.params[4] = 8; /* P4 : bits de données */
ControlPort.params[5] = 0; /* P5 : pas de parité */
ControlPort.params[6] = 1; /* P6 : 1 bit de stop */
ControlPort.params[7] = appareil; /* P7 : numéro de l'appareil */
api(14, 6);
return ControlPort.error;
}
int main(void) {
uint8_t a = regler(0), b = regler(1); /* l'appareil 1 n'est pas branché */
printf("Appareil 0 : erreur %u, appareil 1 : erreur %u\n", a, b);
return 0;
}Appareil 0 : erreur 0, appareil 1 : erreur 1Mad Pascal
// Régler la vitesse et le format de la ligne série du modem (groupe 14, fonction 6).
program cdc6;
uses crt, neo6502;
var recu: array[0..127] of byte; // réponse du modem
nrecu: byte;
function en_attente: word; // 14,1 : octets reçus en attente (appareil 0) ; $FFFF si absent
begin
NeoWaitMessage;
NeoMessage.params[7] := 0; // P7 : appareil 0, le premier
NeoSendMessage(14, 1);
NeoWaitMessage;
if NeoMessage.params[0] = 0 then result := $FFFF
else result := NeoMessage.params[1] + NeoMessage.params[2] shl 8;
end;
function envoyer(adresse: word; n: byte): byte; // 14,5 : écrit n octets ; rend le nombre écrit
begin
NeoWaitMessage;
wordParams[0] := adresse; // P0-P1 : adresse
wordParams[1] := n; // P2-P3 : nombre d'octets
NeoMessage.params[7] := 0;
NeoSendMessage(14, 5);
NeoWaitMessage;
result := NeoMessage.params[2];
end;
procedure lire; // 14,4 : ajoute à recu ce qui est arrivé
begin
NeoWaitMessage;
wordParams[0] := word(@recu) + nrecu;
wordParams[1] := 127 - nrecu;
NeoMessage.params[7] := 0;
NeoSendMessage(14, 4);
NeoWaitMessage;
nrecu := nrecu + NeoMessage.params[2];
end;
function attendre_ok: boolean; // lit jusqu'à « OK » ou 3 secondes (1,1 : horloge en cs)
var fin: cardinal;
i: byte;
begin
result := false;
fin := NeoGetTimer + 300;
while (not result) and (NeoGetTimer < fin) do begin
lire;
if nrecu > 1 then
for i := 0 to nrecu - 2 do
if (recu[i] = ord('O')) and (recu[i + 1] = ord('K')) then result := true;
end;
end;
var a, b: byte;
function regler(appareil: byte): byte; // Serial Set Line Coding : 115200 bauds, 8N1
begin
NeoWaitMessage;
dwordParams[0] := 115200; // P0-P3 : vitesse
NeoMessage.params[4] := 8; // P4 : bits de données
NeoMessage.params[5] := 0; // P5 : pas de parité
NeoMessage.params[6] := 1; // P6 : 1 bit de stop
NeoMessage.params[7] := appareil; // P7 : numéro de l'appareil
NeoSendMessage(14, 6);
NeoWaitMessage;
result := NeoMessage.error;
end;
begin
a := regler(0);
b := regler(1); // l'appareil 1 n'est pas branché
writeln('Appareil 0 : erreur ', a, ', appareil 1 : erreur ', b);
end.Appareil 0 : erreur 0, appareil 1 : erreur 1NeoBASIC
10 ' Regler la vitesse et le format de la ligne serie du modem (groupe 14, fonction 6)
20 a = 0 : gosub 1000 : e0 = e
30 a = 1 : gosub 1000 : e1 = e : ' l'appareil 1 n'est pas branche
40 print "Appareil 0 : erreur ";e0;", appareil 1 : erreur ";e1
50 end
1000 ' Serial Set Line Coding sur l'appareil a : 115200 bauds, 8N1 ; erreur dans e
1010 while peek($ff00) : wend
1020 doke $ff04, 115200 & $ffff : doke $ff06, 115200 >> 16 : ' P0-P3 : vitesse
1030 poke $ff08, 8 : poke $ff09, 0 : poke $ff0a, 1 : poke $ff0b, a : ' 8 bits, sans parite, 1 stop ; P7 : appareil
1040 poke $ff01, 6 : poke $ff00, 14
1050 while peek($ff00) : wend : e = peek($ff02)
1060 returnAppareil 0 : erreur 0, appareil 1 : erreur 1Assembleur 65C02 (64tass)
; Régler la vitesse et le format de la ligne série du modem (groupe 14, 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 #0
jsr regler
sta a
lda #1 ; l'appareil 1 n'est pas branché
jsr regler
sta b
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
; Serial Set Line Coding de l'appareil A : 115200 bauds, 8N1 ; erreur rendue dans A.
regler: pha
jsr WaitMessage
pla
sta ControlPort+11 ; P7 : numéro de l'appareil
ldx #6
_c: lda ligne,x ; P0-P6 : vitesse (32 bits), bits de données, parité, bits de stop
sta ControlPort+4,x
dex
bpl _c
lda #14
ldx #6 ; 14,6 : Serial Set Line Coding
jsr appel
lda ControlPort+2
rts
ligne: .dword 115200
.byte 8, 0, 1
t0: .text "Appareil 0 : erreur ", 0
t1: .text ", appareil 1 : erreur ", 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 0Appareil 0 : erreur 0, appareil 1 : erreur 1