Groupe 1 — Système Officiel
1,0 DSP Reset · 1,1 Timer · 1,2 Key Status · 1,3 Basic · 1,4 Credits · 1,5 Serial Status · 1,6 Locale · 1,7 System Reset · 1,8 MOS · 1,10 Write character to debug · 1,11 Return Version Information · 1,12 Set Interrupt Tick · 1,13 Get Interrupt Tick · 1,16 Set Frame Interrupt · 1,17 Get Frame Interrupt · 1,18 Select Bank · 1,19 Get Bank Info · 1,20 Get Date Time · 1,21 Set Date Time · 1,22 Write Bank · 1,23 Sync Clock From Modem · 1,24 Set Time Zone · 1,25 Get Time Zone · 1,26 Bank Checksum · 1,27 Apply Boot Choice
1,0 DSP Reset Modifié par Trinity
Réinitialise le système de messagerie (la boîte aux lettres) et les composants qui en dépendent. À n'utiliser qu'en cas de besoin exceptionnel.
Entrées : aucune.
Sorties : aucune.
La documentation officielle précise « normalement, ne devrait pas être utilisé ». Un programme ordinaire n'en a pas besoin. Avec Trinity, la console est effacée par l'appel : l'exemple affiche son message après.
Différence dans Trinity — Trinity en fait un reset logiciel : les états de l'API reviennent à ceux de la mise sous tension (console effacée, mode 0, palette, sprites, polices, toolbox, aucune banque de mémoire projetée, son et flux arrêtés, interruptions de tick et de trame coupées). La mémoire du 6502 est gardée, les périphériques USB restent montés ; ni logo ni bip.
Texte de la documentation du firmware (anglais)
Soft reset (Trinity T-36, ADR-0001) : puts the API states back as after power on — console cleared, mode 0, palette, sprites, fonts (2,21 built in, Latin-1 letters), toolbox (as 32,19), no memory bank mapped, sound channels and stream stopped, interrupt tick and frame interrupt off. The 6502 memory is kept, the USB devices stay mounted and the display is not restarted (unless another mode was on). No logo, no beep.
C (llvm-mos)
/* Réinitialiser le système de messagerie (groupe 1, fonction 0). */
#include <stdio.h>
#include <neo6502.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 */
;
}
int main(void) {
while (ControlPort.command) /* boîte aux lettres libre */
;
api(1, 0); /* DSP Reset */
printf("Messagerie reinitialisee\n");
return 0;
}Messagerie reinitialiseeMad Pascal
// Réinitialiser le système de messagerie (groupe 1, fonction 0).
program dspreset;
uses crt, neo6502;
begin
NeoSubReset; // DSP Reset
NeoWaitMessage; // attendre la fin de l'appel
writeln('Messagerie reinitialisee');
end.Messagerie reinitialiseeNeoBASIC
10 ' Reinitialiser le systeme de messagerie (groupe 1, fonction 0)
20 while peek($ff00) : wend
30 poke $ff01, 0 : poke $ff00, 1
40 while peek($ff00) : wend
50 print "Messagerie reinitialisee"Messagerie reinitialiseeAssembleur 65C02 (64tass)
; Réinitialiser le système de messagerie (groupe 1, fonction 0).
.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 WaitMessage ; boîte aux lettres libre
lda #0
sta ControlPort+1 ; fonction 0 : DSP Reset
lda #1
sta ControlPort ; groupe 1 : lance l'appel
jsr WaitMessage
lda #<message
sta ptr
lda #>message
sta ptr+1
jmp ecrire
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
message: .text "Messagerie reinitialisee", 13, 0Messagerie reinitialisee1,1 Timer Officiel
Lit le compteur système, qui avance de 100 unités par seconde (une unité = un centième de seconde).
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
P0-P3 | uint32 | valeur du compteur (32 bits, octet de poids faible en P0) |
Pratique pour chronométrer, cadencer une animation ou attendre sans bloquer. Le compteur démarre à zéro à la mise sous tension.
Mots-clés NeoBASIC qui font cet appel : event(), time(), wait
Texte de la documentation du firmware (anglais)
Deposit the value (32-bits) of the 100Hz system timer into Parameters:0..3.
C (llvm-mos)
/* Chronométrer une boucle avec le compteur à 100 Hz (groupe 1, fonction 1). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
long debut = neo_system_timer(); /* centièmes de seconde */
for (volatile unsigned i = 0; i < 30000; i++)
;
long duree = neo_system_timer() - debut;
printf("Boucle : %ld centiemes\n", duree);
return 0;
}Boucle : 14 centiemesMad Pascal
// Chronométrer une boucle avec le compteur à 100 Hz (groupe 1, fonction 1).
program chrono;
uses crt, neo6502;
var debut: cardinal;
i: word;
begin
debut := NeoGetTimer; // centièmes de seconde
for i := 0 to 29999 do ;
writeln('Boucle : ', NeoGetTimer - debut, ' centiemes');
end.Boucle : 8 centiemesNeoBASIC
10 ' Chronometrer une boucle avec le compteur a 100 Hz (groupe 1, fonction 1)
20 d = time()
30 for i = 1 to 3000 : next i
40 print "Boucle : ";time() - d;" centiemes"Boucle : 24 centiemesAssembleur 65C02 (64tass)
; Chronométrer une boucle avec le compteur à 100 Hz (groupe 1, fonction 1).
; Affiche la durée (octet de poids faible, en décimal).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +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_compteur
sta debut
ldx #0 ; boucle d'attente : 256 x 256 tours
ldy #0
boucle: dey
bne boucle
dex
bne boucle
jsr lire_compteur
sec
sbc debut ; durée, en centièmes
pha
ldx #0
texte: lda message,x ; "Boucle : "
beq nombre
jsr WriteCharacter
inx
bne texte
nombre: pla
jsr decimal
ldx #0
fin: lda suffixe,x ; " centiemes"
beq retour
jsr WriteCharacter
inx
bne fin
retour: rts
; Groupe 1, fonction 1 : le compteur revient dans les paramètres 0 à 3.
lire_compteur:
jsr WaitMessage ; boîte aux lettres libre
lda #1
sta ControlPort+1 ; fonction 1 : Timer
sta ControlPort ; groupe 1 : écrit en dernier, lance l'appel
jsr WaitMessage ; attendre la fin
lda ControlPort+4 ; octet de poids faible
rts
; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
ldx #0 ; x = centaines déjà affichées ?
ldy #100
jsr chiffre
ldy #10
jsr chiffre
ora #'0'
jmp WriteCharacter
chiffre:
sty diviseur
ldy #0
div: cmp diviseur
bcc fini
sbc diviseur
iny
bne div
fini: pha
tya
bne ecrire
cpx #0
beq saute
ecrire: ora #'0'
jsr WriteCharacter
ldx #1
saute: pla
rts
debut: .byte 0
diviseur: .byte 0
message: .text "Boucle : ", 0
suffixe: .text " centiemes", 13, 0Boucle : 5 centiemes1,2 Key Status Officiel
Indique si une touche est enfoncée, et l'état des touches de modification (Maj, Ctrl, Alt, Méta).
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | code de la touche (code USB HID : 4 = A, 44 = espace, 80 = flèche gauche) |
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | état de la touche : $FF enfoncée, 0 relâchée |
P1 | uint8 | touches de modification enfoncées (un bit chacune) |
Lit l'état instantané du clavier, sans passer par la file des caractères tapés — idéal pour un jeu. En NeoBASIC, key(n) fait le même appel.
Mots-clés NeoBASIC qui font cet appel : key()
Texte de la documentation du firmware (anglais)
Deposits the state of the specified keyboard key into Parameter:0. State of keyboard modifiers (Shift/Ctrl/Alt/Meta) is returned in Parameter:1. The key which to query is specified in Parameter:0.
C (llvm-mos)
/* Savoir si la barre d'espace est enfoncée (groupe 1, fonction 2). */
#include <stdio.h>
#include <neo/api.h>
#define TOUCHE_ESPACE 44 /* code USB HID de la barre d'espace */
int main(void) {
uint8_t etat = neo_system_key_status(TOUCHE_ESPACE);
printf("Espace : %s\n", etat ? "enfoncee" : "relachee");
return 0;
}Espace : relacheeMad Pascal
// Savoir si la barre d'espace est enfoncée (groupe 1, fonction 2).
program touche;
uses crt, neo6502;
const TOUCHE_ESPACE = 44; // code USB HID de la barre d'espace
begin
if NeoIsKeyPressed(TOUCHE_ESPACE) <> 0 then
writeln('Espace : enfoncee')
else
writeln('Espace : relachee');
end.Espace : relacheeNeoBASIC
10 ' Savoir si la barre d'espace est enfoncee (groupe 1, fonction 2)
20 ' 44 est le code USB HID de la barre d'espace
30 if key(44)
40 print "Espace : enfoncee"
50 else
60 print "Espace : relachee"
70 endifEspace : relacheeAssembleur 65C02 (64tass)
; Savoir si la barre d'espace est enfoncée (groupe 1, 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
TOUCHE_ESPACE = 44 ; code USB HID de la barre d'espace
start: jsr WaitMessage
lda #TOUCHE_ESPACE
sta ControlPort+4 ; P0 : la touche à interroger
lda #2
sta ControlPort+1 ; fonction 2 : Key Status
lda #1
sta ControlPort ; groupe 1 : lance l'appel
jsr WaitMessage
lda ControlPort+4 ; P0 : $FF enfoncée, 0 relâchée
beq relachee
lda #<enfoncee
ldx #>enfoncee
bne afficher
relachee:
lda #<txt_relachee
ldx #>txt_relachee
afficher:
sta ptr
stx ptr+1
jmp ecrire
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
enfoncee: .text "Espace : enfoncee", 13, 0
txt_relachee: .text "Espace : relachee", 13, 0Espace : relachee1,3 Basic Modifié par Trinity
Charge l'environnement résident et lui passe la main par un saut indirect via l'adresse 0 : NeoBASIC avec le firmware officiel, NeoDOS avec Trinity.
Entrées : aucune.
Sorties : aucune.
Sert à terminer un programme machine en rendant la main. Le programme appelant est écrasé ; rien ne s'exécute après le saut. En Mad Pascal, NeoExecuteBasic fait seulement l'appel : le saut via l'adresse 0 reste à écrire (voir l'exemple).
Différence dans Trinity — Trinity garde le numéro et le nom de la fonction, mais charge NeoDOS (environnement résident depuis Trinity 0.4.0) au lieu de NeoBASIC. NeoBASIC reste disponible par boot/neobasic.bin ou NEOBASIC.NEO. Depuis Trinity 0.16.60, 1,3 charge toujours NeoDOS : le choix du menu de démarrage est appliqué par 1,27, que le noyau appelle une fois au reset.
Texte de la documentation du firmware (anglais)
Loads the resident environment (NeoDOS on Trinity) and sets the indirect jump through address zero to start it. Since T-36 it always loads NeoDOS : the boot menu choice is applied by Function 1,27, which the kernel calls once at reset.
C (llvm-mos)
/* Rendre la main à l'environnement résident (NeoBASIC ou NeoDOS) (groupe 1, fonction 3). */
#include <stdio.h>
#include <neo6502.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 */
;
}
int main(void) {
printf("Retour a l'environnement resident\n");
while (ControlPort.command)
;
api(1, 3); /* charge l'environnement résident */
__asm__ volatile ("jmp ($0000)"); /* et lui passe la main */
return 0; /* jamais atteint */
}Retour a l'environnement resident
NeoDOS version 0.32.1
(C) 2026 bmarty - Neo6502 disk operating systemMad Pascal
// Rendre la main à l'environnement résident (NeoBASIC ou NeoDOS) (groupe 1, fonction 3).
program versresident;
uses crt, neo6502;
begin
writeln('Retour a l''environnement resident');
NeoExecuteBasic; // charge l'environnement résident (appel 1,3 seulement)
NeoWaitMessage;
asm
jmp ($0000) // et lui passe la main : NeoExecuteBasic ne le fait pas
end;
end.Retour a l'environnement resident
NeoDOS version 0.32.1
(C) 2026 bmarty - Neo6502 disk operating systemNeoBASIC
Sans objet en NeoBASIC : Un programme BASIC n'a pas à recharger l'environnement résident : il effacerait le programme en cours.
Assembleur 65C02 (64tass)
; Rendre la main à l'environnement résident (NeoBASIC ou NeoDOS) (groupe 1, 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 #<message
sta ptr
lda #>message
sta ptr+1
jsr ecrire
jsr WaitMessage
lda #3
sta ControlPort+1 ; fonction 3 : Basic
lda #1
sta ControlPort ; groupe 1 : charge l'environnement résident
jsr WaitMessage
jmp ($0000) ; et lui passe la main
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
message: .text "Retour a l'environnement resident", 13, 0Retour a l'environnement resident
NeoDOS version 0.32.1
(C) 2026 bmarty - Neo6502 disk operating system1,4 Credits Officiel
Affiche la liste des contributeurs du projet Neo6502, conservée dans la mémoire flash.
Entrées : aucune.
Sorties : aucune.
Texte de la documentation du firmware (anglais)
Print the Neo6502 project contributors (stored in flash memory).
C (llvm-mos)
/* Afficher les contributeurs du Neo6502 (groupe 1, fonction 4). */
#include <neo/api.h>
int main(void) {
neo_system_credits();
return 0;
}Thanks to : Bill Auger,Wojciech Bocianski,Chris Garre
tt,David Given,Pete Gollan,Rien Matthijse,Andy McCall
,Paul Robson,Oliver Schmidt,Sascha Schneider,Veselin
Sladkov,Tsvetan UsunovMad Pascal
// Afficher les contributeurs du Neo6502 (groupe 1, fonction 4).
program credits;
uses crt, neo6502;
begin
NeoCredits;
NeoWaitMessage;
end.Thanks to : Bill Auger,Wojciech Bocianski,Chris Garre
tt,David Given,Pete Gollan,Rien Matthijse,Andy McCall
,Paul Robson,Oliver Schmidt,Sascha Schneider,Veselin
Sladkov,Tsvetan Usunov
A:\>NeoBASIC
10 ' Afficher les contributeurs du Neo6502 (groupe 1, fonction 4)
20 while peek($ff00) : wend
30 poke $ff01, 4 : poke $ff00, 1
40 while peek($ff00) : wendThanks to : Bill Auger,Wojciech Bocianski,Chris Garre
tt,David Given,Pete Gollan,Rien Matthijse,Andy McCall
,Paul Robson,Oliver Schmidt,Sascha Schneider,Veselin
Sladkov,Tsvetan UsunovAssembleur 65C02 (64tass)
; Afficher les contributeurs du Neo6502 (groupe 1, 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: jsr WaitMessage
lda #4
sta ControlPort+1 ; fonction 4 : Credits
lda #1
sta ControlPort ; groupe 1 : lance l'appel
jmp WaitMessageThanks to : Bill Auger,Wojciech Bocianski,Chris Garre
tt,David Given,Pete Gollan,Rien Matthijse,Andy McCall
,Paul Robson,Oliver Schmidt,Sascha Schneider,Veselin
Sladkov,Tsvetan Usunov
A:\>1,5 Serial Status Officiel
Vérifie le port série et, s'il fonctionne, passe en mode transfert : le Neo6502 reçoit des blocs de données envoyés par un ordinateur, jusqu'à un appui sur Échap.
Entrées : aucune.
Sorties : aucune.
Sur la carte, l'appel affiche « Serial link enabled. » et ne rend la main qu'à l'appui sur Échap. Sans port série utilisable (émulateurs), il affiche « Serial not functioning. » et rend la main aussitôt.
Texte de la documentation du firmware (anglais)
Check the serial port to see if there is a data transmission.
C (llvm-mos)
/* Passer en mode transfert série (groupe 1, fonction 5). */
#include <neo/api.h>
int main(void) {
neo_system_serial_update(); /* sur la carte : jusqu'à l'appui sur Échap */
return 0;
}Serial not functioning.Mad Pascal
// Passer en mode transfert série (groupe 1, fonction 5).
program serie;
uses crt, neo6502;
var etat: byte;
begin
etat := NeoCheckSerial; // sur la carte : jusqu'à l'appui sur Échap
NeoWaitMessage;
end.Serial not functioning.NeoBASIC
10 ' Passer en mode transfert serie (groupe 1, fonction 5)
20 while peek($ff00) : wend
30 poke $ff01, 5 : poke $ff00, 1
40 while peek($ff00) : wendSerial not functioning.Assembleur 65C02 (64tass)
; Passer en mode transfert série (groupe 1, 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: jsr WaitMessage
lda #1
ldx #5 ; fonction 5 : Serial Status
jmp appel ; sur la carte : jusqu'à l'appui sur Échap
; 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
diviseur: .byte 0Serial not functioning.1,6 Locale Officiel
Choisit la langue et la disposition du clavier, par un code de deux lettres majuscules (FR, US, DE…).
| Octets | Type | Rôle |
|---|---|---|
P0 | char | première lettre du code (ex. $46 pour F) |
P1 | char | seconde lettre du code (ex. $52 pour R) |
Sorties : aucune.
En NeoBASIC, locale "FR" fait le même appel.
Mots-clés NeoBASIC qui font cet appel : locale
Texte de la documentation du firmware (anglais)
Set the locale code specified in Parameters:0,1 as upper-case ASCII letters. Parameter:0 takes the first letter and Parameter:1 takes the second letter.
For example: French (FR) would require Parametr 0 being $46 and Parameter 1 being $52
C (llvm-mos)
/* Passer le clavier en disposition française (groupe 1, fonction 6). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_system_locale("FR");
printf("Clavier : FR\n");
return 0;
}Locale is now 'fr'
Clavier : FRMad Pascal
// Passer le clavier en disposition française (groupe 1, fonction 6).
program locale;
uses crt, neo6502;
begin
NeoSetLocale('FR');
NeoWaitMessage;
writeln('Clavier : FR');
end.Locale is now 'fr'
Clavier : FRNeoBASIC
10 ' Passer le clavier en disposition francaise (groupe 1, fonction 6)
20 locale "FR"
30 print "Clavier : FR"Locale is now 'fr'
Clavier : FRAssembleur 65C02 (64tass)
; Passer le clavier en disposition française (groupe 1, 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: jsr WaitMessage
lda #'F'
sta ControlPort+4 ; P0 : première lettre
lda #'R'
sta ControlPort+5 ; P1 : seconde lettre
lda #6
sta ControlPort+1 ; fonction 6 : Locale
lda #1
sta ControlPort ; groupe 1 : lance l'appel
jsr WaitMessage
lda #<message
sta ptr
lda #>message
sta ptr+1
jmp ecrire
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
message: .text "Clavier : FR", 13, 0Locale is now 'fr'
Clavier : FR1,7 System Reset Officiel
Redémarre toute la machine, comme une remise sous tension : le RP2040 est réinitialisé par son chien de garde, et le 65C02 avec lui.
Entrées : aucune.
Sorties : aucune.
Rien ne s'exécute après l'appel ; la mémoire du 65C02 n'est pas conservée.
Texte de la documentation du firmware (anglais)
System Reset. This is a full hardware reset. It resets the RP2040 using the Watchdog timer, and this also resets the 65C02.
C (llvm-mos)
/* Redémarrer la machine (groupe 1, fonction 7). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
printf("Redemarrage...\n");
neo_system_reset(); /* ne revient pas */
return 0;
}Redemarrage...Mad Pascal
// Redémarrer la machine (groupe 1, fonction 7).
program redemarre;
uses crt, neo6502;
begin
writeln('Redemarrage...');
NeoReset; // ne revient pas
end.Redemarrage...NeoBASIC
10 ' Redemarrer la machine (groupe 1, fonction 7)
20 print "Redemarrage..."
30 while peek($ff00) : wend
40 poke $ff01, 7 : poke $ff00, 1Redemarrage...Assembleur 65C02 (64tass)
; Redémarrer la machine (groupe 1, fonction 7).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: lda #<message
sta ptr
lda #>message
sta ptr+1
jsr ecrire
jsr WaitMessage
lda #7
sta ControlPort+1 ; fonction 7 : System Reset
lda #1
sta ControlPort ; groupe 1 : la machine redémarre
boucle: bra boucle
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
message: .text "Redemarrage...", 13, 0Redemarrage...1,8 MOS Officiel
Exécute une commande « étoile » du MOS, comme *cat. Commandes disponibles : cat (ou .), del, file, copy, ren, md, cd.
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | addr | adresse de la commande : chaîne précédée de sa longueur, l'étoile initiale est facultative |
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | 0 si la commande a réussi, 255 si elle est inconnue |
Les arguments sont séparés par des espaces ; un argument contenant des espaces se met entre guillemets.
Texte de la documentation du firmware (anglais)
Do a MOS command (a '* command') these are specified in the Wiki as they will be steadily expanded.
C (llvm-mos)
/* Lister le répertoire courant avec la commande MOS *cat (groupe 1, fonction 8). */
#include <stdio.h>
#include <neo6502.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 const char commande[] = "\x04*cat"; /* longueur, puis le texte */
int main(void) {
printf("Catalogue :\n");
while (ControlPort.command)
;
ControlPort.params[0] = (uint16_t)commande & 0xFF;
ControlPort.params[1] = (uint16_t)commande >> 8;
api(1, 8); /* MOS */
if (ControlPort.error)
printf("Commande inconnue\n");
return 0;
}Catalogue :
PROG.NEO 173 bytes.Mad Pascal
// Lister le répertoire courant avec la commande MOS *cat (groupe 1, fonction 8).
program cat;
uses crt, neo6502;
var commande: string[16];
begin
writeln('Catalogue :');
commande := '*cat'; // chaîne Pascal : longueur puis texte
NeoMOS(@commande);
NeoWaitMessage;
end.Catalogue :
PROG.NEO 273 bytes.NeoBASIC
10 ' Lister le repertoire courant avec la commande MOS *cat (groupe 1, fonction 8)
20 c$ = "*cat" : a = alloc(8) : poke a, len(c$)
30 for i = 1 to len(c$) : poke a + i, asc(mid$(c$, i, 1)) : next i
40 print "Catalogue :"
50 while peek($ff00) : wend
60 poke $ff04, a & 255 : poke $ff05, a >> 8 : poke $ff01, 8 : poke $ff00, 1
70 while peek($ff00) : wendCatalogue :
prog.bas 493 bytes.
boot <Dir>Assembleur 65C02 (64tass)
; Lister le répertoire courant avec la commande MOS *cat (groupe 1, fonction 8).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: lda #<titre
sta ptr
lda #>titre
sta ptr+1
jsr ecrire
jsr WaitMessage
lda #<commande
sta ControlPort+4 ; P0-P1 : adresse de la commande
lda #>commande
sta ControlPort+5
lda #8
sta ControlPort+1 ; fonction 8 : MOS
lda #1
sta ControlPort ; groupe 1 : lance l'appel
jmp WaitMessage
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
titre: .text "Catalogue :", 13, 0
commande: .byte 4 ; longueur
.text "*cat"Catalogue :
PROG.NEO 82 bytes.1,10 Write character to debug Officiel
Envoie un caractère sur le port de débogage (l'UART du Pico sur la carte, la sortie d'erreur dans les émulateurs), sans toucher à l'écran.
| Octets | Type | Rôle |
|---|---|---|
P0 | char | le caractère à envoyer |
Sorties : aucune.
Pour tracer un programme sans perturber l'affichage.
Texte de la documentation du firmware (anglais)
Writes a single character to the debug port (the UART on the Pico, or stderr on the emulator). This allows maximum flexibility.
C (llvm-mos)
/* Écrire une trace sur le port de débogage (groupe 1, fonction 10). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
const char *trace = "Bonjour\n";
while (*trace)
neo_system_debug_putc(*trace++);
printf("Trace envoyee\n");
return 0;
}Trace envoyeeMad Pascal
// Écrire une trace sur le port de débogage (groupe 1, fonction 10).
program trace;
uses crt, neo6502;
var trace: string[16];
i: byte;
begin
trace := 'Bonjour';
for i := 1 to length(trace) do NeoWriteDebug(ord(trace[i]));
NeoWriteDebug(10);
NeoWaitMessage;
writeln('Trace envoyee');
end.Trace envoyeeNeoBASIC
10 ' Ecrire une trace sur le port de debogage (groupe 1, fonction 10)
20 t$ = "Bonjour"
30 for i = 1 to len(t$)
40 while peek($ff00) : wend
50 poke $ff04, asc(mid$(t$, i, 1)) : poke $ff01, 10 : poke $ff00, 1
60 next i
70 while peek($ff00) : wend
80 print "Trace envoyee"Trace envoyeeAssembleur 65C02 (64tass)
; Écrire une trace sur le port de débogage (groupe 1, fonction 10).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: ldx #0
suivant:
lda trace,x
beq fini
jsr WaitMessage
sta ControlPort+4 ; P0 : le caractère
lda #10
sta ControlPort+1 ; fonction 10 : Write character to debug
lda #1
sta ControlPort ; groupe 1 : lance l'appel
inx
bne suivant
fini: jsr WaitMessage
lda #<message
sta ptr
lda #>message
sta ptr+1
jmp ecrire
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
trace: .text "Bonjour", 10, 0
message: .text "Trace envoyee", 13, 0Trace envoyee1,11 Return Version Information Officiel
Donne la version du firmware, sous la forme majeure.mineure.correctif.
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | version majeure |
P1 | uint8 | version mineure |
P2 | uint8 | correctif |
Permet à un programme de vérifier qu'il tourne sur un firmware assez récent.
Texte de la documentation du firmware (anglais)
Reads the current version Major.Minor.Patch into Parameters:0..2 These values are guaranteed to be in the range 0.255
C (llvm-mos)
/* Afficher la version du firmware (groupe 1, fonction 11). */
#include <stdio.h>
#include <neo/api.h>
int main(void) {
neo_version_t v;
neo_system_version(&v);
printf("Version : %d.%d.%d\n", v.major, v.minor, v.patch);
return 0;
}Version : 0.16.69Mad Pascal
// Afficher la version du firmware (groupe 1, fonction 11).
program version;
uses crt, neo6502;
var majeure, mineure, correctif: byte;
begin
NeoSendMessage(1, 11); // Return Version Information
NeoWaitMessage;
majeure := NeoMessage.params[0];
mineure := NeoMessage.params[1];
correctif := NeoMessage.params[2];
writeln('Version : ', majeure, '.', mineure, '.', correctif);
end.Version : 0.16.69NeoBASIC
10 ' Afficher la version du firmware (groupe 1, fonction 11)
20 ' lire les resultats dans des variables avant tout print : print utilise aussi la boite aux lettres
30 while peek($ff00) : wend
40 poke $ff01, 11 : poke $ff00, 1
50 while peek($ff00) : wend : a = peek($ff04) : b = peek($ff05) : c = peek($ff06)
60 print "Version : ";a;".";b;".";cVersion : 0.16.69Assembleur 65C02 (64tass)
; Afficher la version du firmware (groupe 1, fonction 11).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: jsr WaitMessage
lda #11
sta ControlPort+1 ; fonction 11 : Return Version Information
lda #1
sta ControlPort ; groupe 1 : lance l'appel
jsr WaitMessage
lda ControlPort+4 ; recopier P0-P2 : l'affichage réutilise la boîte aux lettres
sta version
lda ControlPort+5
sta version+1
lda ControlPort+6
sta version+2
lda #<message
sta ptr
lda #>message
sta ptr+1
jsr ecrire
lda version
jsr decimal
lda #'.'
jsr WriteCharacter
lda version+1
jsr decimal
lda #'.'
jsr WriteCharacter
lda version+2
jsr decimal
lda #13
jmp WriteCharacter
; Affiche A (0-255) en décimal, sans zéros de tête.
decimal:
ldx #0
ldy #100
jsr chiffre
ldy #10
jsr chiffre
ora #'0'
jmp WriteCharacter
chiffre:
sty diviseur
ldy #0
_div: cmp diviseur
bcc _fini
sbc diviseur
iny
bne _div
_fini: pha
tya
bne _ecrit
cpx #0
beq _saute
_ecrit: ora #'0'
jsr WriteCharacter
ldx #1
_saute: pla
rts
; Affiche la chaîne terminée par 0 pointée par (ptr).
ecrire: ldy #0
_e: lda (ptr),y
beq _f
jsr WriteCharacter
iny
bne _e
_f: rts
ptr = $80
version: .byte 0, 0, 0
diviseur: .byte 0
message: .text "Version : ", 0Version : 0.16.691,12 Set Interrupt Tick Trinity
Déclenche une interruption périodique du 65C02 (broche IRQB) à la fréquence voulue, de 1 à 1000 Hz ; 0 l'arrête.
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | uint16 | fréquence en Hz (1 à 1000), 0 pour arrêter |
Sorties : aucune.
Installer d'abord la routine d'interruption : son adresse va en $FFFE-$FFFF (en RAM), puis CLI. La ligne IRQB reste basse jusqu'à la lecture du vecteur : la routine n'a rien à acquitter et ne peut pas être réentrée. Si le programme principal prépare lui aussi un appel, la routine qui appelle l'API doit sauver puis restaurer $FF01-$FF0B. DSP Reset (1,0) arrête le tick.
Texte de la documentation du firmware (anglais)
Starts (or stops with 0) a periodic interrupt to the 65C02 at Parameters:0,1 Hz (1..1000) on IRQB (Trinity T-14, F-60 of the fork). The interrupt vector $FFFE/$FFFF is in RAM : install a handler there first, then CLI. IRQB is held low until the 65C02 fetches the vector at $FFFF, so the handler needs no acknowledge and cannot re-enter ; with I=1 the line stays low until CLI (ticks coalesce, none is lost). An interrupt handler may call the API but must save and restore $FF01-$FF0B if the main program could be preparing a call. DSP Reset (1,0) stops the tick. First delivery on a board : to be validated (bus loop timing, see docs/BACKLOG.md T-14).
C (llvm-mos)
/* Compter les interruptions d'un tick à 100 Hz pendant une seconde (groupe 1, fonction 12). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
static volatile uint16_t compteur;
__attribute__((interrupt)) void tick(void) { /* routine d'interruption (llvm-mos) */
compteur++;
}
static void regler_tick(uint16_t hz) {
libre();
ControlPort.params[0] = hz & 0xFF;
ControlPort.params[1] = hz >> 8;
api(1, 12); /* Set Interrupt Tick */
}
int main(void) {
*(void (**)(void))0xFFFE = tick; /* vecteur d'interruption, en RAM */
regler_tick(100);
__asm__ volatile ("cli");
long debut = neo_system_timer();
while (neo_system_timer() - debut < 100) /* une seconde */
;
__asm__ volatile ("sei");
regler_tick(0); /* arrêter le tick */
printf("Interruptions en 1 s : %u\n", compteur);
return 0;
}Interruptions en 1 s : 99Mad Pascal
// Compter les interruptions d'un tick à 100 Hz pendant une seconde (groupe 1, fonction 12).
program tick;
uses crt, neo6502;
var compteur: word;
vecteur: word absolute $FFFE; // vecteur d'interruption, en RAM
debut: cardinal;
procedure recevoir; assembler; // routine d'interruption : se termine par rti
asm
inc compteur
bne @+
inc compteur+1
@:
rti
end;
procedure regler_tick(hz: word);
begin
NeoWaitMessage;
wordParams[0] := hz; // P0-P1 : fréquence en Hz
NeoSendMessage(1, 12); // Set Interrupt Tick
NeoWaitMessage;
end;
begin
compteur := 0;
vecteur := word(@recevoir);
regler_tick(100);
asm
cli
end;
debut := NeoGetTimer;
repeat until NeoGetTimer - debut >= 100; // une seconde
asm
sei
end;
regler_tick(0); // arrêter le tick
writeln('Interruptions en 1 s : ', compteur);
end.Interruptions en 1 s : 99NeoBASIC
Sans objet en NeoBASIC : Une routine d'interruption s'écrit en code machine ; NeoBASIC ne peut pas en fournir une.
Assembleur 65C02 (64tass)
; Compter les interruptions d'un tick à 100 Hz pendant une seconde (groupe 1, fonction 12).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: lda #<tick ; installer la routine d'interruption
sta $FFFE
lda #>tick
sta $FFFF
jsr WaitMessage
lda #100
sta ControlPort+4 ; P0-P1 : 100 Hz
stz ControlPort+5
lda #1
ldx #12 ; fonction 12 : Set Interrupt Tick
jsr appel
cli
jsr compteur_100hz ; attendre une seconde (100 centièmes)
clc
adc #100
sta fin
_att: jsr compteur_100hz
cmp fin
bne _att
sei
jsr WaitMessage
stz ControlPort+4 ; 0 : arrêter le tick
stz ControlPort+5
lda #1
ldx #12
jsr appel
lda #<message
ldx #>message
jsr ecrire
lda nombre ; octet de poids faible suffit ici (< 256)
jsr decimal
lda #13
jmp WriteCharacter
tick: inc nombre ; routine d'interruption : rien à acquitter
rti
; Octet de poids faible du compteur à 100 Hz (groupe 1, fonction 1).
compteur_100hz:
jsr WaitMessage
lda #1
tax ; fonction 1 : Timer
jsr appel
lda ControlPort+4
rts
; 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
diviseur: .byte 0
nombre: .byte 0
fin: .byte 0
message: .text "Interruptions en 1 s : ", 0Interruptions en 1 s : 991,13 Get Interrupt Tick Trinity
Donne la fréquence actuelle du tick d'interruption réglé par la fonction 1,12 (0 s'il est arrêté).
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | uint16 | fréquence en Hz, 0 si arrêté |
Texte de la documentation du firmware (anglais)
Returns the current interrupt tick rate in Parameters:0,1 (0 = off).
C (llvm-mos)
/* Lire la fréquence du tick d'interruption (groupe 1, fonction 13). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
libre();
api(1, 13); /* Get Interrupt Tick */
uint16_t hz = ControlPort.params[0] | (ControlPort.params[1] << 8);
printf("Tick : %u Hz\n", hz); /* 0 : aucun tick en cours */
return 0;
}Tick : 0 HzMad Pascal
// Lire la fréquence du tick d'interruption (groupe 1, fonction 13).
program gettick;
uses crt, neo6502;
var hz: word;
begin
NeoSendMessage(1, 13); // Get Interrupt Tick
NeoWaitMessage;
hz := wordParams[0]; // 0 : aucun tick en cours
writeln('Tick : ', hz, ' Hz');
end.Tick : 0 HzNeoBASIC
10 ' Lire la frequence du tick d'interruption (groupe 1, fonction 13)
20 ' lire les resultats dans des variables juste apres l'appel : print reutilise la boite aux lettres
30 while peek($ff00) : wend
40 poke $ff01, 13 : poke $ff00, 1
50 while peek($ff00) : wend : hz = peek($ff04) + 256 * peek($ff05)
60 print "Tick : ";hz;" Hz"Tick : 0 HzAssembleur 65C02 (64tass)
; Lire la fréquence du tick d'interruption (groupe 1, fonction 13).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: jsr WaitMessage
lda #1
ldx #13 ; fonction 13 : Get Interrupt Tick
jsr appel
lda ControlPort+4 ; P0 : poids faible (le tick est ici arrêté : 0)
sta hz
lda #<message
ldx #>message
jsr ecrire
lda hz
jsr decimal
lda #<unite
ldx #>unite
jmp ecrire
; 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
diviseur: .byte 0
hz: .byte 0
message: .text "Tick : ", 0
unite: .text " Hz", 13, 0Tick : 0 Hz1,16 Set Frame Interrupt Trinity
Déclenche une interruption du 65C02 au début de chaque image affichée (60 Hz, synchronisation verticale de la sortie DVI) ; 0 l'arrête.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | non nul : activer, 0 : arrêter |
Sorties : aucune.
Même livraison que le tick (1,12) : routine en $FFFE-$FFFF, puis CLI. Tick et interruption d'image peuvent être actifs ensemble : la routine ne sait pas les distinguer (utiliser 5,37 Frame Count ou 1,1 Timer). Idéal pour animer sans scintillement.
Texte de la documentation du firmware (anglais)
Parameter:0 non zero : an interrupt is delivered to the 65C02 at the start of every displayed frame (60 Hz vertical sync of the DVI output), 0 stops it (Trinity T-14, F-10 of the fork). Same delivery as Function 1,12 : IRQB is held low until the vector fetch at $FFFF ; a frame arriving while interrupts are disabled coalesces with the next. The tick and the frame interrupt may both be on : the handler cannot tell them apart (use 5,37 Frame Count or 1,1 Timer). Stopped by DSP Reset (1,0).
C (llvm-mos)
/* Compter les images affichées en une seconde, par interruption (groupe 1, fonction 16). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
static volatile uint16_t images;
__attribute__((interrupt)) void image(void) { /* une interruption par image */
images++;
}
static void interruption_image(uint8_t active) {
libre();
ControlPort.params[0] = active;
api(1, 16); /* Set Frame Interrupt */
}
int main(void) {
*(void (**)(void))0xFFFE = image;
interruption_image(1);
__asm__ volatile ("cli");
long debut = neo_system_timer();
while (neo_system_timer() - debut < 100)
;
__asm__ volatile ("sei");
interruption_image(0);
printf("Images en 1 s : %u\n", images);
return 0;
}Images en 1 s : 60Mad Pascal
// Compter les images affichées en une seconde, par interruption (groupe 1, fonction 16).
program images;
uses crt, neo6502;
var nombre: word;
vecteur: word absolute $FFFE;
debut: cardinal;
procedure image; assembler; // une interruption par image
asm
inc nombre
bne @+
inc nombre+1
@:
rti
end;
procedure interruption_image(active: byte);
begin
NeoWaitMessage;
NeoMessage.params[0] := active;
NeoSendMessage(1, 16); // Set Frame Interrupt
NeoWaitMessage;
end;
begin
nombre := 0;
vecteur := word(@image);
interruption_image(1);
asm
cli
end;
debut := NeoGetTimer;
repeat until NeoGetTimer - debut >= 100;
asm
sei
end;
interruption_image(0);
writeln('Images en 1 s : ', nombre);
end.Images en 1 s : 60NeoBASIC
Sans objet en NeoBASIC : Une routine d'interruption s'écrit en code machine ; NeoBASIC ne peut pas en fournir une.
Assembleur 65C02 (64tass)
; Compter les images affichées en une seconde, par interruption (groupe 1, fonction 16).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: lda #<image
sta $FFFE
lda #>image
sta $FFFF
jsr WaitMessage
lda #1
sta ControlPort+4 ; P0 : activer
ldx #16 ; fonction 16 : Set Frame Interrupt
jsr appel
cli
jsr compteur_100hz
clc
adc #100
sta fin
_att: jsr compteur_100hz
cmp fin
bne _att
sei
jsr WaitMessage
stz ControlPort+4 ; 0 : arrêter
lda #1
ldx #16
jsr appel
lda #<message
ldx #>message
jsr ecrire
lda images
jsr decimal
lda #13
jmp WriteCharacter
image: inc images ; une interruption par image
rti
compteur_100hz:
jsr WaitMessage
lda #1
tax ; fonction 1 : Timer
jsr appel
lda ControlPort+4
rts
; 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
diviseur: .byte 0
images: .byte 0
fin: .byte 0
message: .text "Images en 1 s : ", 0Images en 1 s : 601,17 Get Frame Interrupt Trinity
Indique si l'interruption d'image (fonction 1,16) est active.
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | non nul si active |
Texte de la documentation du firmware (anglais)
Returns in Parameter:0 whether the frame interrupt (Function 1,16) is on.
C (llvm-mos)
/* Savoir si l'interruption d'image est active (groupe 1, fonction 17). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
libre();
api(1, 17); /* Get Frame Interrupt */
printf("Interruption d'image : %s\n", ControlPort.params[0] ? "active" : "inactive");
return 0;
}Interruption d'image : inactiveMad Pascal
// Savoir si l'interruption d'image est active (groupe 1, fonction 17).
program getframe;
uses crt, neo6502;
begin
NeoSendMessage(1, 17); // Get Frame Interrupt
NeoWaitMessage;
if NeoMessage.params[0] <> 0 then
writeln('Interruption d''image : active')
else
writeln('Interruption d''image : inactive');
end.Interruption d'image : inactiveNeoBASIC
10 ' Savoir si l'interruption d'image est active (groupe 1, fonction 17)
20 while peek($ff00) : wend
30 poke $ff01, 17 : poke $ff00, 1
40 while peek($ff00) : wend : a = peek($ff04)
50 if a
60 print "Interruption d'image : active"
70 else
80 print "Interruption d'image : inactive"
90 endifInterruption d'image : inactiveAssembleur 65C02 (64tass)
; Savoir si l'interruption d'image est active (groupe 1, fonction 17).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: jsr WaitMessage
lda #1
ldx #17 ; fonction 17 : Get Frame Interrupt
jsr appel
lda ControlPort+4 ; P0 : non nul si active
beq _non
lda #<active
ldx #>active
jmp ecrire
_non: lda #<inactive
ldx #>inactive
jmp ecrire
; 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
diviseur: .byte 0
active: .text "Interruption d'image : active", 13, 0
inactive: .text "Interruption d'image : inactive", 13, 0Interruption d'image : inactive1,18 Select Bank Trinity
Recopie l'une des 32 banques de 8 Ko conservées dans la flash du RP2040 dans une fenêtre de 8 Ko de la RAM du 65C02.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | numéro de banque (0 à 31) ; $FF oublie la correspondance sans toucher la fenêtre |
P1-P2 | addr | adresse de la fenêtre : alignée sur une page, fin avant $FF00 |
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | non nul si la banque ou l'adresse est invalide |
La fenêtre est une copie : la modifier ne modifie pas la banque (voir 1,22). Elle ne doit contenir ni le code qui fait l'appel, ni la pile, ni la page zéro. Une banque jamais écrite se lit $FF. Les banques sont aussi les pages $A0 à $BF du blitter (12,2), en lecture seule.
Texte de la documentation du firmware (anglais)
Memory banks in flash (Trinity T-17, from F-23 of the fork). Copies bank Parameter:0 (0-31, 8k each, kept in the RP2040 flash and read in place) into the 8k window at Parameters:1,2 of 6502 RAM : the window address is page aligned and the window ends below $FF00. The copy takes place while the 65C02 waits for the call ; the window must not hold the code executing the call, its stack or its zero page. The window is a copy : changing it does not change the bank (see Function 1,22). Bank $FF forgets the mapping and leaves the window as it is. Sets the error flag if the bank or the address is invalid. A bank never written reads as $FF. The banks are also blitter pages $A0 (bank 0) to $BF, read only, for Function 12,2 (bank to video RAM for instance).
C (llvm-mos)
/* Recopier la banque 31 dans une fenêtre de 8 Ko (groupe 1, fonction 18). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
static uint8_t fenetre[8192] __attribute__((aligned(256))); /* alignée sur une page */
int main(void) {
libre();
ControlPort.params[0] = 31; /* banque */
ControlPort.params[1] = (uint16_t)fenetre & 0xFF;
ControlPort.params[2] = (uint16_t)fenetre >> 8;
api(1, 18); /* Select Bank */
if (ControlPort.error)
printf("Banque ou fenetre invalide\n");
else
printf("Banque 31 : premier octet $%02X\n", fenetre[0]); /* $FF si jamais écrite */
return 0;
}Banque 31 : premier octet $FFMad Pascal
// Recopier la banque 31 dans une fenêtre de 8 Ko en $4000 (groupe 1, fonction 18).
program banque;
uses crt, neo6502;
var fenetre: array[0..8191] of byte absolute $4000; // alignée sur une page
begin
NeoWaitMessage;
NeoMessage.params[0] := 31; // banque
NeoMessage.params[1] := lo(word(@fenetre)); // P1-P2 : fenêtre
NeoMessage.params[2] := hi(word(@fenetre));
NeoSendMessage(1, 18); // Select Bank
NeoWaitMessage;
if NeoMessage.error <> 0 then
writeln('Banque ou fenetre invalide')
else
writeln('Banque 31 : premier octet $', HexStr(fenetre[0], 2)); // $FF si jamais écrite
end.Banque 31 : premier octet $FFNeoBASIC
10 ' Recopier la banque 31 dans une fenetre de 8 Ko (groupe 1, fonction 18)
20 m = alloc(8448) : w = (m + 255) & $ff00 : ' fenetre alignee sur une page
30 while peek($ff00) : wend
40 poke $ff04, 31 : poke $ff05, w & 255 : poke $ff06, w >> 8 : poke $ff01, 18 : poke $ff00, 1
50 while peek($ff00) : wend : e = peek($ff02)
60 if e
70 print "Banque ou fenetre invalide"
80 else
90 print "Banque 31 : premier octet ";peek(w)
100 endifBanque 31 : premier octet 255Assembleur 65C02 (64tass)
; Recopier la banque 31 dans une fenêtre de 8 Ko en $4000 (groupe 1, fonction 18).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
FENETRE = $4000 ; alignée sur une page, loin du programme
start: jsr WaitMessage
lda #31
sta ControlPort+4 ; P0 : banque 31
lda #<FENETRE
sta ControlPort+5 ; P1-P2 : fenêtre
lda #>FENETRE
sta ControlPort+6
lda #1
ldx #18 ; fonction 18 : Select Bank
jsr appel
lda ControlPort+2 ; erreur ?
bne _err
lda #<message
ldx #>message
jsr ecrire
lda FENETRE ; premier octet ($FF = 255 si jamais écrite)
jsr decimal
lda #13
jmp WriteCharacter
_err: lda #<erreur
ldx #>erreur
jmp ecrire
; 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
diviseur: .byte 0
message: .text "Banque 31 : premier octet ", 0
erreur: .text "Banque ou fenetre invalide", 13, 0Banque 31 : premier octet 2551,19 Get Bank Info Trinity
Donne la dernière banque sélectionnée, l'adresse de sa fenêtre, le nombre de banques et leur taille.
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | dernière banque sélectionnée ($FF si aucune depuis le démarrage) |
P1-P2 | addr | adresse de sa fenêtre |
P3 | uint8 | nombre de banques |
P4-P5 | uint16 | taille d'une banque en octets |
Texte de la documentation du firmware (anglais)
Returns the last bank selected in Parameter:0 ($FF if none since reset) and its window address in Parameters:1,2, the number of banks in Parameter:3 and the bank size in Parameters:4,5 (Trinity T-17).
C (llvm-mos)
/* Connaître le nombre et la taille des banques (groupe 1, fonction 19). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
libre();
api(1, 19); /* Get Bank Info */
uint8_t nombre = ControlPort.params[3];
uint16_t taille = ControlPort.params[4] | (ControlPort.params[5] << 8);
printf("%u banques de %u octets\n", nombre, taille);
return 0;
}32 banques de 8192 octetsMad Pascal
// Connaître le nombre et la taille des banques (groupe 1, fonction 19).
program bankinfo;
uses crt, neo6502;
var nombre: byte;
taille: word;
begin
NeoSendMessage(1, 19); // Get Bank Info
NeoWaitMessage;
nombre := NeoMessage.params[3];
taille := wordParams[2]; // P4-P5
writeln(nombre, ' banques de ', taille, ' octets');
end.32 banques de 8192 octetsNeoBASIC
10 ' Connaitre le nombre et la taille des banques (groupe 1, fonction 19)
20 ' lire les resultats dans des variables juste apres l'appel : print reutilise la boite aux lettres
30 while peek($ff00) : wend
40 poke $ff01, 19 : poke $ff00, 1
50 while peek($ff00) : wend : n = peek($ff07) : t = peek($ff08) + 256 * peek($ff09)
60 print n;" banques de ";t;" octets"32 banques de 8192 octetsAssembleur 65C02 (64tass)
; Connaître le nombre de banques (groupe 1, fonction 19).
.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 WaitMessage
lda #1
ldx #19 ; fonction 19 : Get Bank Info
jsr appel
lda ControlPort+7 ; P3 : nombre de banques
jsr decimal
lda #<message
ldx #>message
jmp ecrire
; 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
diviseur: .byte 0
message: .text " banques de 8192 octets", 13, 032 banques de 8192 octets1,20 Get Date Time Trinity
Donne la date et l'heure locales (fuseau de la fonction 1,24), et d'où elles viennent.
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | uint16 | année (1970 à 2099) |
P2 | uint8 | mois (1 à 12) |
P3 | uint8 | jour (1 à 31) |
P4 | uint8 | heure (0 à 23) |
P5 | uint8 | minute |
P6 | uint8 | seconde |
P7 | uint8 | source : 0 non réglée (1970-01-01 + temps depuis la mise sous tension), 1 horloge logicielle (1,21), 2 horloge PCF8563 sur l'UEXT, 3 heure SNTP du modem USB (1,23) |
Texte de la documentation du firmware (anglais)
Returns the date and time (Trinity T-18, F-14 of the fork), local time of the zone of Function 1,24 : Parameters:0,1 year (1970-2099), Parameter:2 month (1-12), Parameter:3 day (1-31), Parameter:4 hour (0-23), Parameter:5 minute, Parameter:6 second and Parameter:7 the source : 0 = not set (the value is then 1970-01-01 plus the time since power on), 1 = software clock set by Function 1,21 and advanced by the 100 Hz timer, 2 = PCF8563 real time clock found on the UEXT I2C bus (address $51), 3 = set from the SNTP time of the USB modem (Function 1,23 : the program asks for it, nothing is automatic, T-25).
C (llvm-mos)
/* Afficher la date et l'heure (groupe 1, fonction 20). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
libre();
api(1, 20); /* Get Date Time */
volatile uint8_t *p = ControlPort.params;
printf("%04u-%02u-%02u %02u:%02u:%02u (source %u)\n",
p[0] | (p[1] << 8), p[2], p[3], p[4], p[5], p[6], p[7]);
return 0;
}1970-01-01 00:00:06 (source 0)Mad Pascal
// Afficher la date et l'heure (groupe 1, fonction 20).
program dateheure;
uses crt, neo6502;
var p: array[0..7] of byte;
i: byte;
procedure deux(n: byte); // deux chiffres, avec zéro de tête
begin
if n < 10 then write('0');
write(n);
end;
begin
NeoSendMessage(1, 20); // Get Date Time
NeoWaitMessage;
for i := 0 to 7 do p[i] := NeoMessage.params[i]; // recopier : l'affichage réutilise la boîte aux lettres
write(p[0] + p[1] * 256, '-');
deux(p[2]); write('-'); deux(p[3]); write(' ');
deux(p[4]); write(':'); deux(p[5]); write(':'); deux(p[6]);
writeln(' (source ', p[7], ')');
end.1970-01-01 00:00:06 (source 0)NeoBASIC
10 ' Afficher la date et l'heure (groupe 1, fonction 20)
20 ' lire les resultats dans des variables juste apres l'appel, avant tout calcul : print et * reutilisent la boite aux lettres
30 while peek($ff00) : wend
40 poke $ff01, 20 : poke $ff00, 1
50 while peek($ff00) : wend : a0 = peek($ff04) : a1 = peek($ff05) : mo = peek($ff06) : j = peek($ff07) : h = peek($ff08) : mi = peek($ff09) : s = peek($ff0a) : so = peek($ff0b) : a = a0 + 256 * a1
60 print a;"-";right$("0"+str$(mo),2);"-";right$("0"+str$(j),2);" ";right$("0"+str$(h),2);":";right$("0"+str$(mi),2);":";right$("0"+str$(s),2);" (source ";so;")"1970-01-01 00:00:09 (source 0)Assembleur 65C02 (64tass)
; Afficher la date et l'heure (groupe 1, fonction 20) : jj/mm hh:mm:ss.
.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 WaitMessage
lda #1
ldx #20 ; fonction 20 : Get Date Time
jsr appel
ldx #7 ; recopier P0-P7 : l'affichage réutilise la boîte aux lettres
_copie: lda ControlPort+4,x
sta date,x
dex
bpl _copie
lda date+3 ; jour
jsr deux
lda #'/'
jsr WriteCharacter
lda date+2 ; mois
jsr deux
lda #' '
jsr WriteCharacter
lda date+4 ; heure
jsr deux
lda #':'
jsr WriteCharacter
lda date+5 ; minute
jsr deux
lda #':'
jsr WriteCharacter
lda date+6 ; seconde
jsr deux
lda #13
jmp WriteCharacter
deux: cmp #10 ; deux chiffres, avec zéro de tête
bcs _d
pha
lda #'0'
jsr WriteCharacter
pla
_d: jmp decimal
; 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
diviseur: .byte 0
date: .fill 801/01 00:00:061,21 Set Date Time Trinity
Règle la date et l'heure locales ; l'horloge PCF8563 est réglée aussi quand elle est présente.
| Octets | Type | Rôle |
|---|---|---|
P0-P6 | date | même disposition que 1,20 : année (2 octets), mois, jour, heure, minute, seconde |
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | non nul si un champ est hors limites (1970-2099, jour valide du mois, heure sur 24 h) |
Texte de la documentation du firmware (anglais)
Sets the date and time (Trinity T-18, F-14 of the fork), same layout as Function 1,20 (Parameter:7 ignored) : the software clock is set, and the PCF8563 too when present. Sets the error flag if a field is out of range (1970-2099, valid day of month, 24 hour time).
C (llvm-mos)
/* Régler la date et l'heure au 27 septembre 2026, 14:30:00 (groupe 1, fonction 21). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
static const uint8_t date[7] = { 2026 & 0xFF, 2026 >> 8, 9, 27, 14, 30, 0 };
libre();
for (uint8_t i = 0; i < 7; i++)
ControlPort.params[i] = date[i];
api(1, 21); /* Set Date Time */
if (ControlPort.error)
printf("Date refusee\n");
else
printf("Horloge reglee : 2026-09-27 14:30\n");
return 0;
}Horloge reglee : 2026-09-27 14:30Mad Pascal
// Régler la date et l'heure au 27 septembre 2026, 14:30:00 (groupe 1, fonction 21).
program setdate;
uses crt, neo6502;
begin
NeoWaitMessage;
wordParams[0] := 2026; // P0-P1 : année
NeoMessage.params[2] := 9; // mois
NeoMessage.params[3] := 27; // jour
NeoMessage.params[4] := 14; // heure
NeoMessage.params[5] := 30; // minute
NeoMessage.params[6] := 0; // seconde
NeoSendMessage(1, 21); // Set Date Time
NeoWaitMessage;
if NeoMessage.error <> 0 then
writeln('Date refusee')
else
writeln('Horloge reglee : 2026-09-27 14:30');
end.Horloge reglee : 2026-09-27 14:30NeoBASIC
10 ' Regler la date et l'heure au 27 septembre 2026, 14:30:00 (groupe 1, fonction 21)
20 while peek($ff00) : wend
30 poke $ff04, 2026 & 255 : poke $ff05, 2026 >> 8 : poke $ff06, 9 : poke $ff07, 27 : poke $ff08, 14 : poke $ff09, 30 : poke $ff0a, 0 : poke $ff01, 21 : poke $ff00, 1
40 while peek($ff00) : wend : e = peek($ff02)
50 if e
60 print "Date refusee"
70 else
80 print "Horloge reglee : 2026-09-27 14:30"
90 endifHorloge reglee : 2026-09-27 14:30Assembleur 65C02 (64tass)
; Régler la date et l'heure au 27 septembre 2026, 14:30:00 (groupe 1, fonction 21).
.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 WaitMessage
ldx #6
_copie: lda date,x ; P0-P6 : année, mois, jour, heure, minute, seconde
sta ControlPort+4,x
dex
bpl _copie
lda #1
ldx #21 ; fonction 21 : Set Date Time
jsr appel
lda ControlPort+2 ; erreur ?
bne _err
lda #<message
ldx #>message
jmp ecrire
_err: lda #<refus
ldx #>refus
jmp ecrire
; 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
diviseur: .byte 0
date: .word 2026
.byte 9, 27, 14, 30, 0
message: .text "Horloge reglee : 2026-09-27 14:30", 13, 0
refus: .text "Date refusee", 13, 0Horloge reglee : 2026-09-27 14:301,22 Write Bank Trinity
Écrit 8 Ko de la RAM du 65C02 dans une banque de la flash ; la banque survit aux redémarrages et aux mises à jour du firmware.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | numéro de banque (0 à 31) |
P1-P2 | addr | adresse des 8 Ko à écrire (mêmes contraintes que 1,18) |
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | non nul si la banque ou l'adresse est invalide, ou si la vérification échoue |
Sur la carte, les secteurs de flash sont effacés puis écrits, l'affichage se fige environ 150 ms, et le contenu est vérifié. Les émulateurs gardent les banques dans le fichier banks.flash du stockage. La flash s'use : écrire une banque une fois, pas dans une boucle (1,26 aide à ne réécrire que ce qui a changé).
Texte de la documentation du firmware (anglais)
Programs bank Parameter:0 with the 8k at Parameters:1,2 of 6502 RAM (same constraints as Function 1,18) : on the board the flash sectors are erased and written, the display pauses for about 150 ms, and the content is verified ; a written bank survives resets and reflashing of the firmware (it lives in the top 256k of the flash). The emulators keep the banks in the file "banks.flash" of the storage directory. Sets the error flag if the bank or the address is invalid or the verification fails (Trinity T-17). Flash wears : write banks once, not in loops.
C (llvm-mos)
/* Écrire un texte dans la banque 31, puis le relire (groupe 1, fonctions 22 et 18). */
#include <stdio.h>
#include <string.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
static uint8_t tampon[8192] __attribute__((aligned(256)));
static void banque(uint8_t fonction, uint8_t numero) {
libre();
ControlPort.params[0] = numero;
ControlPort.params[1] = (uint16_t)tampon & 0xFF;
ControlPort.params[2] = (uint16_t)tampon >> 8;
api(1, fonction);
}
int main(void) {
memset(tampon, 0, sizeof tampon);
strcpy((char *)tampon, "Bonjour");
banque(22, 31); /* Write Bank : tampon -> banque 31 */
if (ControlPort.error) {
printf("Ecriture refusee\n");
return 0;
}
memset(tampon, 0, sizeof tampon); /* effacer la copie en RAM */
banque(18, 31); /* Select Bank : banque 31 -> tampon */
printf("Banque 31 relue : %s\n", (char *)tampon);
return 0;
}Banque 31 relue : BonjourMad Pascal
// Écrire un texte dans la banque 31, puis le relire (groupe 1, fonctions 22 et 18).
program writebank;
uses crt, neo6502;
var tampon: array[0..8191] of byte absolute $4000; // 8 Ko alignés sur une page
texte: string[16];
i: byte;
procedure banque(fonction: byte);
begin
NeoWaitMessage;
NeoMessage.params[0] := 31;
NeoMessage.params[1] := lo(word(@tampon));
NeoMessage.params[2] := hi(word(@tampon));
NeoSendMessage(1, fonction);
NeoWaitMessage;
end;
begin
texte := 'Bonjour';
for i := 1 to length(texte) do tampon[i - 1] := ord(texte[i]);
tampon[length(texte)] := 0;
banque(22); // Write Bank : tampon -> banque 31
if NeoMessage.error <> 0 then
begin
writeln('Ecriture refusee');
exit;
end;
tampon[0] := 0; // effacer la copie en RAM
banque(18); // Select Bank : banque 31 -> tampon
write('Banque 31 relue : ');
i := 0;
while tampon[i] <> 0 do
begin
write(chr(tampon[i]));
inc(i);
end;
writeln;
end.Banque 31 relue : BonjourNeoBASIC
10 ' Ecrire un texte dans la banque 31, puis le relire (groupe 1, fonctions 22 et 18)
20 m = alloc(8448) : w = (m + 255) & $ff00
30 t$ = "Bonjour" : for i = 1 to len(t$) : poke w + i - 1, asc(mid$(t$, i, 1)) : next i : poke w + len(t$), 0
40 while peek($ff00) : wend
50 poke $ff04, 31 : poke $ff05, w & 255 : poke $ff06, w >> 8 : poke $ff01, 22 : poke $ff00, 1
60 while peek($ff00) : wend : e = peek($ff02)
70 if e then print "Ecriture refusee" : end
80 for i = 0 to 7 : poke w + i, 0 : next i
90 while peek($ff00) : wend
100 poke $ff04, 31 : poke $ff05, w & 255 : poke $ff06, w >> 8 : poke $ff01, 18 : poke $ff00, 1
110 while peek($ff00) : wend
120 r$ = "" : i = 0
130 while peek(w + i) : r$ = r$ + chr$(peek(w + i)) : i = i + 1 : wend
140 print "Banque 31 relue : ";r$Banque 31 relue : BonjourAssembleur 65C02 (64tass)
; Écrire un texte dans la banque 31, puis le relire (groupe 1, fonctions 22 et 18).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
TAMPON = $4000 ; 8 Ko alignés sur une page
start: ldx #0 ; copier le texte dans le tampon
_copie: lda texte,x
sta TAMPON,x
beq _ecrit
inx
bne _copie
_ecrit: lda #22 ; fonction 22 : Write Bank
jsr banque
lda ControlPort+2
bne _err
stz TAMPON ; effacer la copie en RAM
lda #18 ; fonction 18 : Select Bank
jsr banque
lda #<message
ldx #>message
jsr ecrire
lda #<TAMPON ; le texte relu depuis la banque
ldx #>TAMPON
jsr ecrire
lda #13
jmp WriteCharacter
_err: lda #<refus
ldx #>refus
jmp ecrire
; A = fonction (18 ou 22), sur la banque 31 et le tampon.
banque: pha
jsr WaitMessage
lda #31
sta ControlPort+4
lda #<TAMPON
sta ControlPort+5
lda #>TAMPON
sta ControlPort+6
plx
lda #1
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
diviseur: .byte 0
texte: .text "Bonjour", 0
message: .text "Banque 31 relue : ", 0
refus: .text "Ecriture refusee", 13, 0Banque 31 relue : Bonjour1,23 Sync Clock From Modem Trinity
Règle la date et l'heure d'après le modem Wi-Fi USB (Pico W, Modem Wi-Fi Neo6502), qui les obtient par SNTP.
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | 0 réussi, 1 aucun modem branché, 2 modem sans heure SNTP (réponse 1970) ou muet |
Envoie AT+CIPSNTPTIME? sur le lien USB et applique la réponse, en retirant le décalage du modem : l'horloge tient l'heure UTC, et 1,20 montre l'heure locale du fuseau de 1,24. Attend 2 secondes au plus ; ce qui attendait sur le lien du modem est jeté. Rien n'est automatique : c'est au programme de le demander.
Texte de la documentation du firmware (anglais)
Sets the date and time from the USB modem (Pico W, Neo6502picowifi) : sends AT+CIPSNTPTIME? on the CDC link and applies the answer ; the tz offset of the modem (AT+CIPSNTPCFG?, whole hours) is taken out so the clock holds UTC and Function 1,20 shows the local time of the zone selected by Function 1,24. Waits 2 seconds at most. Error 1 if no modem is connected, 2 if the modem has no SNTP time yet (answer 1970) or does not answer. Pending input on the modem link is discarded (Trinity T-25).
C (llvm-mos)
/* Mettre l'horloge à l'heure du modem Wi-Fi USB (groupe 1, fonction 23). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
libre();
api(1, 23); /* Sync Clock From Modem */
switch (ControlPort.error) {
case 0: printf("Horloge synchronisee\n"); break;
case 1: printf("Pas de modem USB\n"); break;
default: printf("Modem sans heure SNTP\n"); break;
}
return 0;
}Pas de modem USBMad Pascal
// Mettre l'horloge à l'heure du modem Wi-Fi USB (groupe 1, fonction 23).
program syncmodem;
uses crt, neo6502;
begin
NeoSendMessage(1, 23); // Sync Clock From Modem
NeoWaitMessage;
case NeoMessage.error of
0: writeln('Horloge synchronisee');
1: writeln('Pas de modem USB');
else
writeln('Modem sans heure SNTP');
end;
end.Pas de modem USBNeoBASIC
10 ' Mettre l'horloge a l'heure du modem Wi-Fi USB (groupe 1, fonction 23)
20 while peek($ff00) : wend
30 poke $ff01, 23 : poke $ff00, 1
40 while peek($ff00) : wend : e = peek($ff02)
50 if e = 0 then print "Horloge synchronisee"
60 if e = 1 then print "Pas de modem USB"
70 if e > 1 then print "Modem sans heure SNTP"Pas de modem USBAssembleur 65C02 (64tass)
; Mettre l'horloge à l'heure du modem Wi-Fi USB (groupe 1, fonction 23).
.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 WaitMessage
lda #1
ldx #23 ; fonction 23 : Sync Clock From Modem
jsr appel
lda ControlPort+2 ; 0 réussi, 1 pas de modem, 2 pas d'heure
bne _pas0
lda #<ok
ldx #>ok
jmp ecrire
_pas0: cmp #1
bne _pas1
lda #<sans
ldx #>sans
jmp ecrire
_pas1: lda #<muet
ldx #>muet
jmp ecrire
; 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
diviseur: .byte 0
ok: .text "Horloge synchronisee", 13, 0
sans: .text "Pas de modem USB", 13, 0
muet: .text "Modem sans heure SNTP", 13, 0Pas de modem USB1,24 Set Time Zone Trinity
Choisit le fuseau horaire, par son nom (Europe/Paris, America/Montreal…) ou par un décalage fixe (UTC+2, UTC-3:30, +0530).
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | addr | adresse du nom : chaîne précédée de sa longueur |
| Octets | Type | Rôle |
|---|---|---|
erreur | uint8 | 0 réussi, 1 nom inconnu, 2 écriture en flash échouée |
L'horloge garde l'heure UTC ; 1,20, 1,21 et les dates des fichiers FAT utilisent l'heure locale du fuseau, heure d'été comprise (règles de l'Union européenne, d'Amérique du Nord, d'Australie et de Nouvelle-Zélande). Le fuseau est enregistré en flash et réappliqué à chaque démarrage.
Texte de la documentation du firmware (anglais)
Selects the time zone (Trinity T-26) from the length-prefixed name at Parameters:0,1 : an IANA style name of the built in table ("Europe/Paris", "Europe/Belgrade", "America/Montreal", "Asia/Tokyo"...) or a fixed offset ("UTC+2", "UTC-3:30", "+0530"). The clock keeps UTC ; Functions 1,20 and 1,21 and the FAT timestamps use the local time of the zone, daylight saving included (rules of the European Union, North America, Australia and New Zealand). The zone is saved in the settings sector of the flash and applied at every start up. Error 1 if the name is unknown, 2 if the flash write failed.
C (llvm-mos)
/* Choisir le fuseau horaire de Paris (groupe 1, fonction 24). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
static const char fuseau[] = "\x0C" "Europe/Paris"; /* longueur (12), puis le nom */
int main(void) {
libre();
ControlPort.params[0] = (uint16_t)fuseau & 0xFF;
ControlPort.params[1] = (uint16_t)fuseau >> 8;
api(1, 24); /* Set Time Zone */
if (ControlPort.error == 1)
printf("Fuseau inconnu\n");
else if (ControlPort.error)
printf("Ecriture en flash echouee\n");
else
printf("Fuseau : Europe/Paris\n");
return 0;
}Fuseau : Europe/ParisMad Pascal
// Choisir le fuseau horaire de Paris (groupe 1, fonction 24).
program setzone;
uses crt, neo6502;
var fuseau: string[32]; // chaîne Pascal : longueur puis texte
begin
fuseau := 'Europe/Paris';
NeoWaitMessage;
wordParams[0] := word(@fuseau); // P0-P1 : adresse du nom
NeoSendMessage(1, 24); // Set Time Zone
NeoWaitMessage;
case NeoMessage.error of
0: writeln('Fuseau : ', fuseau);
1: writeln('Fuseau inconnu');
else
writeln('Ecriture en flash echouee');
end;
end.Fuseau : Europe/ParisNeoBASIC
10 ' Choisir le fuseau horaire de Paris (groupe 1, fonction 24)
20 z$ = "Europe/Paris" : a = alloc(16) : poke a, len(z$)
30 for i = 1 to len(z$) : poke a + i, asc(mid$(z$, i, 1)) : next i
40 while peek($ff00) : wend
50 poke $ff04, a & 255 : poke $ff05, a >> 8 : poke $ff01, 24 : poke $ff00, 1
60 while peek($ff00) : wend : e = peek($ff02)
70 if e = 0 then print "Fuseau : ";z$
80 if e = 1 then print "Fuseau inconnu"
90 if e = 2 then print "Ecriture en flash echouee"Fuseau : Europe/ParisAssembleur 65C02 (64tass)
; Choisir le fuseau horaire de Paris (groupe 1, fonction 24).
.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 WaitMessage
lda #<fuseau
sta ControlPort+4 ; P0-P1 : adresse du nom
lda #>fuseau
sta ControlPort+5
lda #1
ldx #24 ; fonction 24 : Set Time Zone
jsr appel
lda ControlPort+2
bne _err
lda #<ok
ldx #>ok
jmp ecrire
_err: lda #<refus
ldx #>refus
jmp ecrire
; 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
diviseur: .byte 0
fuseau: .byte 12 ; longueur
.text "Europe/Paris"
ok: .text "Fuseau : Europe/Paris", 13, 0
refus: .text "Fuseau refuse", 13, 0Fuseau : Europe/Paris1,25 Get Time Zone Trinity
Donne le nom du fuseau horaire en cours, son décalage par rapport à UTC et si l'heure d'été s'applique.
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | addr | adresse d'un tampon : son premier octet donne, avant l'appel, la longueur maximale acceptée ; il reçoit ensuite la longueur du nom, suivie du nom |
| Octets | Type | Rôle |
|---|---|---|
tampon | string | nom du fuseau (« UTC » par défaut) |
P2-P3 | int16 | décalage par rapport à UTC en minutes (signé, heure d'été comprise) |
P4 | uint8 | non nul si l'heure d'été s'applique |
Texte de la documentation du firmware (anglais)
Writes the current time zone name into the length-prefixed buffer at Parameters:0,1 ("UTC" by default), the current offset from UTC in minutes (signed, daylight saving included) in Parameters:2,3 and whether daylight saving time is in force in Parameter:4 (Trinity T-26).
C (llvm-mos)
/* Afficher le fuseau horaire en cours (groupe 1, fonction 25). */
#include <stdio.h>
#include <neo6502.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 char nom[64]; /* longueur, puis le nom */
int main(void) {
nom[0] = sizeof nom - 1; /* capacité du tampon, lue par le firmware */
libre();
ControlPort.params[0] = (uint16_t)nom & 0xFF;
ControlPort.params[1] = (uint16_t)nom >> 8;
api(1, 25); /* Get Time Zone */
int16_t decalage = ControlPort.params[2] | (ControlPort.params[3] << 8);
uint8_t ete = ControlPort.params[4];
printf("Fuseau : %.*s, decalage %d min%s\n", nom[0], nom + 1, decalage, ete ? " (heure d'ete)" : "");
return 0;
}Fuseau : UTC, decalage 0 minMad Pascal
// Afficher le fuseau horaire en cours (groupe 1, fonction 25).
program getzone;
uses crt, neo6502;
var nom: string[63]; // longueur, puis le nom
decalage: smallint;
ete: byte;
begin
nom[0] := chr(63); // capacité du tampon, lue par le firmware
NeoWaitMessage;
wordParams[0] := word(@nom); // P0-P1 : tampon qui reçoit le nom
NeoSendMessage(1, 25); // Get Time Zone
NeoWaitMessage;
decalage := smallint(wordParams[1]); // P2-P3 : minutes, signé
ete := NeoMessage.params[4];
write('Fuseau : ', nom, ', decalage ', decalage, ' min');
if ete <> 0 then write(' (heure d''ete)');
writeln;
end.Fuseau : UTC, decalage 0 minNeoBASIC
10 ' Afficher le fuseau horaire en cours (groupe 1, fonction 25)
20 ' lire les resultats dans des variables juste apres l'appel : print reutilise la boite aux lettres
30 a = alloc(64) : poke a, 63 : ' premier octet : capacite du tampon
40 while peek($ff00) : wend
50 poke $ff04, a & 255 : poke $ff05, a >> 8 : poke $ff01, 25 : poke $ff00, 1
60 while peek($ff00) : wend : d = peek($ff06) + 256 * peek($ff07) : e = peek($ff08)
70 if d > 32767 then d = d - 65536
80 n$ = "" : for i = 1 to peek(a) : n$ = n$ + chr$(peek(a + i)) : next i
90 print "Fuseau : ";n$;", decalage ";d;" min"Fuseau : UTC, decalage 0 minAssembleur 65C02 (64tass)
; Afficher le nom du fuseau horaire en cours (groupe 1, fonction 25).
.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 #63
sta nom ; premier octet : capacité du tampon, lue par le firmware
jsr WaitMessage
lda #<nom
sta ControlPort+4 ; P0-P1 : tampon qui reçoit le nom
lda #>nom
sta ControlPort+5
lda #1
ldx #25 ; fonction 25 : Get Time Zone
jsr appel
lda ControlPort+6 ; P2 : décalage en minutes (poids faible)
sta decalage
lda #<message
ldx #>message
jsr ecrire
ldx #0 ; le nom : longueur puis caractères
_nom: cpx nom
beq _fin
lda nom+1,x
jsr WriteCharacter
inx
bne _nom
_fin: lda #<suite
ldx #>suite
jsr ecrire
lda decalage ; 0 à 255 : suffit pour les fuseaux positifs de l'exemple
jsr decimal
lda #<unite
ldx #>unite
jmp ecrire
; 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
diviseur: .byte 0
nom: .fill 64
decalage: .byte 0
message: .text "Fuseau : ", 0
suite: .text ", decalage ", 0
unite: .text " min", 13, 0Fuseau : UTC, decalage 0 min1,26 Bank Checksum Trinity
Calcule la somme de contrôle Fletcher-16 d'une banque directement dans la flash, sans la recopier en RAM.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | numéro de banque (0 à 31) |
| Octets | Type | Rôle |
|---|---|---|
P0-P1 | uint16 | s1 = somme des 8192 octets modulo 65536 |
P2-P3 | uint16 | s2 = somme des valeurs successives de s1 modulo 65536 |
erreur | uint8 | non nul si la banque est invalide (les paramètres valent alors 0) |
Une banque jamais écrite se lit $FF : s1 = $E000. Permet de savoir quelles banques d'un cache ont changé avant de les réécrire avec 1,22, sans tout recopier.
Texte de la documentation du firmware (anglais)
Returns the Fletcher 16 checksum of bank Parameter:0 (0-31) as stored in flash, without copying it to 6502 RAM (Trinity T-81) : s1 = sum of the 8192 bytes modulo 65536 in Parameters:0,1, s2 = sum of the successive values of s1 modulo 65536 in Parameters:2,3. A bank never written reads as $FF (s1 = $E000). Sets the error flag if the bank is invalid (the parameters are then zero). The 65C02 waits for the call ; it avoids copying each bank with Function 1,18 and summing it in 6502 code to find which banks of a cache need writing with Function 1,22.
C (llvm-mos)
/* Somme de contrôle de la banque 30 (groupe 1, fonction 26). */
#include <stdio.h>
#include <neo6502.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
libre();
ControlPort.params[0] = 30; /* banque */
api(1, 26); /* Bank Checksum */
uint16_t s1 = ControlPort.params[0] | (ControlPort.params[1] << 8);
uint16_t s2 = ControlPort.params[2] | (ControlPort.params[3] << 8);
printf("Banque 30 : s1 = $%04X, s2 = $%04X%s\n", s1, s2, s1 == 0xE000 ? " (jamais ecrite)" : "");
return 0;
}Banque 30 : s1 = $E000, s2 = $F000 (jamais ecrite)Mad Pascal
// Somme de contrôle de la banque 30 (groupe 1, fonction 26).
program checksum;
uses crt, neo6502;
var s1, s2: word;
begin
NeoWaitMessage;
NeoMessage.params[0] := 30; // banque
NeoSendMessage(1, 26); // Bank Checksum
NeoWaitMessage;
s1 := wordParams[0];
s2 := wordParams[1];
write('Banque 30 : s1 = $', HexStr(s1, 4), ', s2 = $', HexStr(s2, 4));
if s1 = $E000 then write(' (jamais ecrite)');
writeln;
end.Banque 30 : s1 = $E000, s2 = $F000 (jamais ecrite)NeoBASIC
10 ' Somme de controle de la banque 30 (groupe 1, fonction 26)
20 ' lire les resultats dans des variables juste apres l'appel, avant tout calcul : print et * reutilisent la boite aux lettres
30 while peek($ff00) : wend
40 poke $ff04, 30 : poke $ff01, 26 : poke $ff00, 1
50 while peek($ff00) : wend : b0 = peek($ff04) : b1 = peek($ff05) : b2 = peek($ff06) : b3 = peek($ff07) : s1 = b0 + 256 * b1 : s2 = b2 + 256 * b3
60 print "Banque 30 : s1 = ";s1;", s2 = ";s2Banque 30 : s1 = 57344, s2 = 61440Assembleur 65C02 (64tass)
; Somme de contrôle de la banque 30 (groupe 1, fonction 26).
.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 WaitMessage
lda #30
sta ControlPort+4 ; P0 : banque
lda #1
ldx #26 ; fonction 26 : Bank Checksum
jsr appel
lda ControlPort+5 ; s1, poids fort puis poids faible
sta s1+1
lda ControlPort+4
sta s1
lda #<message
ldx #>message
jsr ecrire
lda s1+1
jsr hexa
lda s1
jsr hexa
lda #13
jmp WriteCharacter
hexa: pha ; A en deux chiffres hexadécimaux
lsr a
lsr a
lsr a
lsr a
jsr _h
pla
and #$0F
_h: cmp #10
bcc _c
adc #6
_c: adc #'0'
jmp WriteCharacter
; 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
diviseur: .byte 0
s1: .word 0
message: .text "Banque 30 : s1 = $", 0Banque 30 : s1 = $E0001,27 Apply Boot Choice Trinity
Applique le choix du menu de démarrage (ou de boot/auto.txt) : charge le programme choisi et rend 1, ou rend 0 s'il n'y a rien à lancer.
Entrées : aucune.
| Octets | Type | Rôle |
|---|---|---|
P0 | uint8 | 1 : programme chargé, saut via l'adresse 0 prêt ; 0 : NeoDOS choisi, rien à lancer, ou échec (un message le dit) |
Le noyau l'appelle une fois au reset, puis 1,3 (NeoDOS) si elle a rendu 0. Le choix est oublié une fois appliqué : appelée par un programme, elle rend donc 0 et ne fait rien, comme le montre l'exemple. Un .bin est chargé en $0800 comme NeoBASIC ; un .neo doit avoir une adresse d'exécution.
Texte de la documentation du firmware (anglais)
Applies the choice made in the boot menu or by boot/auto.txt (Trinity T-36, ADR-0001) : loads the chosen program and sets the jump through address zero, then returns 1 in Parameter:0 ; returns 0 when NeoDOS was chosen or the program could not be started (a message says so). The choice is forgotten once applied. The kernel calls it at reset, then Function 1,3 if it returned 0.
C (llvm-mos)
/* Demander le choix du menu de démarrage, déjà appliqué (groupe 1, fonction 27, Trinity). */
#include <stdio.h>
#include <neo6502.h>
#include <neo/api.h>
/* Appel brut de la boîte aux lettres : fonction puis groupe (écrit en dernier, il lance l'appel). */
static void api(uint8_t groupe, uint8_t fonction) {
ControlPort.function = fonction;
ControlPort.command = groupe;
while (ControlPort.command) /* attendre la fin */
;
}
static void libre(void) { /* attendre que la boîte aux lettres soit libre */
while (ControlPort.command)
;
}
int main(void) {
libre();
api(1, 27); /* Apply Boot Choice */
printf("Choix de demarrage : %u\n", ControlPort.params[0]); /* 0 : rien à lancer */
return 0;
}Choix de demarrage : 0Mad Pascal
// Demander le choix du menu de démarrage, déjà appliqué (groupe 1, fonction 27, Trinity).
program bootchoice;
uses crt, neo6502;
var c: byte;
begin
NeoSendMessage(1, 27); // Apply Boot Choice
NeoWaitMessage;
c := NeoMessage.params[0]; // lu avant writeln, qui réutilise la boîte aux lettres
writeln('Choix de demarrage : ', c); // 0 : rien à lancer
end.Choix de demarrage : 0NeoBASIC
10 ' Demander le choix du menu de demarrage, deja applique (groupe 1, fonction 27, Trinity)
20 while peek($ff00) : wend : poke $ff01, 27 : poke $ff00, 1 : ' Apply Boot Choice
30 while peek($ff00) : wend : c = peek($ff04)
40 print "Choix de demarrage : ";c : ' 0 : rien a lancerChoix de demarrage : 0Assembleur 65C02 (64tass)
; Demander le choix du menu de démarrage, déjà appliqué (groupe 1, fonction 27, Trinity).
.cpu "65c02"
* = $0800
ControlPort = $FF00 ; +0 groupe, +1 fonction, +2 erreur, +4.. paramètres
WaitMessage = $FFF4 ; attend que la boîte aux lettres soit libre
WriteCharacter = $FFF1 ; affiche le caractère de A
start: jsr WaitMessage
lda #1
ldx #27 ; 1,27 : Apply Boot Choice
jsr appel
lda ControlPort+4 ; 0 : rien à lancer
sta choix
lda #<t0
ldx #>t0
jsr ecrire
lda choix
jsr decimal
lda #13
jmp WriteCharacter
t0: .text "Choix de demarrage : ", 0
choix: .byte 0
; 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
diviseur: .byte 0Choix de demarrage : 0