Group 14 — USB Serial (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
Trinity only: a USB serial device (CDC-ACM class) plugged into a USB-A port of the board, usually a modem, such as the Neo6502 Wi-Fi modem, or a USB-serial adapter. P7 selects the device (0 the first, 1 the second); a larger number falls back to device 0, without an error. The serial functions of group 10 (10,13 to 10,18) also go through this device when 10,19 selects it, which is the default as soon as a modem is plugged in.
On the bench, Phosphoneo plugs in a simulated Hayes modem (tools/fake_modem.py): the examples send it AT and wait for "OK", as a real modem would do. They only print what does not depend on the modem (plugged in or not, errors, bytes written, presence of "OK").
14,1 Serial Status Trinity
Tells whether the USB serial device is plugged in and how many received bytes are waiting to be read.
| Bytes | Type | Meaning |
|---|---|---|
P7 | uint8 | device: 0 or 1 (above: 0) |
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | 1 if the device is plugged in |
P1-P2 | uint16 | received bytes waiting (0 if the device is absent) |
The call never flags an error. The example shows that device 5 (out of range) is seen as device 0, then that bytes are waiting after sending AT. Without a modem, both devices are absent (checked on the bench). NeoBASIC has no keyword for this group.
Firmware documentation text
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 octets65C02 assembly (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
Reads one byte received from the USB serial device, without waiting.
| Bytes | Type | Meaning |
|---|---|---|
P7 | uint8 | device: 0 or 1 |
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | byte read |
erreur | uint8 | 1 if nothing has arrived or the device is absent |
Before anything is sent, nothing has arrived: error 1. The example sends AT, then reads byte by byte until "OK".
Firmware documentation text
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 : OK65C02 assembly (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
Sends one byte to the USB serial device.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | byte to send |
P7 | uint8 | device: 0 or 1 |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the device is absent or did not take the byte |
For more than one byte, 14,5 does it all in one call. The example sends A, T and a carriage return, one at a time, then waits for "OK".
Firmware documentation text
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 : OK65C02 assembly (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
Reads the bytes received from the USB serial device in one go, up to a maximum, without waiting.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | addr | destination |
P2-P3 | uint16 | maximum number of bytes |
P7 | uint8 | device: 0 or 1 |
| Bytes | Type | Meaning |
|---|---|---|
P2-P3 | uint16 | number of bytes read (may be 0) |
erreur | uint8 | 1 if the device is absent |
Returning 0 bytes is not an error: call again later. The length is limited to the end of 6502 memory. The example sends ATI (identification) and reads blocks until "OK"; the identification text depends on the modem.
Firmware documentation text
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 OK65C02 assembly (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
Sends a run of bytes to the USB serial device in one go.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | addr | source |
P2-P3 | uint16 | number of bytes |
P7 | uint8 | device: 0 or 1 |
| Bytes | Type | Meaning |
|---|---|---|
P2-P3 | uint16 | number of bytes written |
erreur | uint8 | 1 if the device is absent |
Compare the number written with the number asked: the device may take fewer. The length is limited to the end of 6502 memory.
Firmware documentation text
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 : OK65C02 assembly (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
Sets the speed and format of the USB device serial line.
| Bytes | Type | Meaning |
|---|---|---|
P0-P3 | uint32 | speed in bauds |
P4 | uint8 | data bits (0: 8) |
P5 | uint8 | parity: 0 none, 1 odd, 2 even |
P6 | uint8 | stop bits, 1 or 2 (0: 1) |
P7 | uint8 | device: 0 or 1 |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 1 if the device is absent or refuses the setting |
Many USB modems ignore this setting: they have no real serial line. A USB-serial adapter applies it. The example sets 115200 bauds, 8N1, on device 0 (accepted) then on device 1, which is absent (error 1).
Firmware documentation text
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 165C02 assembly (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