Group 1 — System Official
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 Changed by Trinity
Resets the messaging system (the mailbox) and the components that depend on it. Only for exceptional needs.
Inputs: none.
Outputs: none.
The official documentation says it "should normally not be used". An ordinary program does not need it. With Trinity, the console is cleared by the call: the example prints its message afterwards.
Difference in Trinity — Trinity makes it a soft reset: the API states go back to those of power on (console cleared, mode 0, palette, sprites, fonts, toolbox, no memory bank mapped, sound and stream stopped, tick and frame interrupts off). The 6502 memory is kept, USB devices stay mounted; no logo, no beep.
Firmware documentation text
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 reinitialisee65C02 assembly (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 Official
Reads the system counter, which advances by 100 units per second (one unit = one hundredth of a second).
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0-P3 | uint32 | counter value (32 bits, low byte in P0) |
Handy for timing, pacing an animation or waiting without blocking. The counter starts at zero at power-on.
NeoBASIC keywords that make this call: event(), time(), wait
Firmware documentation text
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 centiemes65C02 assembly (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 Official
Tells whether a key is held down, and the state of the modifier keys (Shift, Ctrl, Alt, Meta).
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | key code (USB HID usage: 4 = A, 44 = space, 80 = left arrow) |
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | key state: $FF down, 0 up |
P1 | uint8 | modifier keys held (one bit each) |
Reads the instantaneous keyboard state, without going through the queue of typed characters — ideal for a game. In NeoBASIC, key(n) makes the same call.
NeoBASIC keywords that make this call: key()
Firmware documentation text
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 : relachee65C02 assembly (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 Changed by Trinity
Loads the resident environment and hands over to it with an indirect jump through address 0: NeoBASIC with the official firmware, NeoDOS with Trinity.
Inputs: none.
Outputs: none.
Used to end a machine code program by handing control back. The calling program is overwritten; nothing runs after the jump. In Mad Pascal, NeoExecuteBasic only makes the call: the jump through address 0 still has to be written (see the example).
Difference in Trinity — Trinity keeps the function number and name, but loads NeoDOS (resident environment since Trinity 0.4.0) instead of NeoBASIC. NeoBASIC remains available through boot/neobasic.bin or NEOBASIC.NEO. Since Trinity 0.16.60, 1,3 always loads NeoDOS: the boot menu choice is applied by 1,27, which the kernel calls once at reset.
Firmware documentation text
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
Not applicable in NeoBASIC: A BASIC program does not need to reload the resident environment: it would erase the running program.
65C02 assembly (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 Official
Prints the list of Neo6502 project contributors, kept in flash memory.
Inputs: none.
Outputs: none.
Firmware documentation text
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 Usunov65C02 assembly (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 Official
Checks the serial port and, if it works, enters transfer mode: the Neo6502 receives data blocks sent by a computer, until Escape is pressed.
Inputs: none.
Outputs: none.
On the board, the call prints "Serial link enabled." and only returns when Escape is pressed. Without a usable serial port (emulators), it prints "Serial not functioning." and returns at once.
Firmware documentation text
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.65C02 assembly (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 Official
Selects the language and keyboard layout, with a two upper-case letter code (FR, US, DE…).
| Bytes | Type | Meaning |
|---|---|---|
P0 | char | first letter of the code (e.g. $46 for F) |
P1 | char | second letter of the code (e.g. $52 for R) |
Outputs: none.
In NeoBASIC, locale "FR" makes the same call.
NeoBASIC keywords that make this call: locale
Firmware documentation text
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 : FR65C02 assembly (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 Official
Restarts the whole machine, like a power cycle: the RP2040 is reset by its watchdog, and the 65C02 with it.
Inputs: none.
Outputs: none.
Nothing runs after the call; the 65C02 memory is not kept.
Firmware documentation text
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...65C02 assembly (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 Official
Runs a MOS "star" command, such as *cat. Available commands: cat (or .), del, file, copy, ren, md, cd.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | addr | address of the command: length-prefixed string, the leading star is optional |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 0 if the command succeeded, 255 if it is unknown |
Arguments are separated by spaces; an argument containing spaces goes between quotes.
Firmware documentation text
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>65C02 assembly (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 Official
Sends a character to the debug port (the Pico UART on the board, stderr in the emulators), without touching the screen.
| Bytes | Type | Meaning |
|---|---|---|
P0 | char | the character to send |
Outputs: none.
To trace a program without disturbing the display.
Firmware documentation text
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 envoyee65C02 assembly (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 Official
Gives the firmware version, as major.minor.patch.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | major version |
P1 | uint8 | minor version |
P2 | uint8 | patch |
Lets a program check that it runs on a recent enough firmware.
Firmware documentation text
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.6965C02 assembly (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
Triggers a periodic 65C02 interrupt (IRQB pin) at the chosen rate, from 1 to 1000 Hz; 0 stops it.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | uint16 | rate in Hz (1 to 1000), 0 to stop |
Outputs: none.
Install the interrupt handler first: its address goes to $FFFE-$FFFF (in RAM), then CLI. IRQB stays low until the vector is fetched: the handler has nothing to acknowledge and cannot be re-entered. If the main program may also be preparing a call, a handler that calls the API must save and restore $FF01-$FF0B. DSP Reset (1,0) stops the tick.
Firmware documentation text
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
Not applicable in NeoBASIC: An interrupt handler is machine code; NeoBASIC cannot provide one.
65C02 assembly (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
Gives the current rate of the interrupt tick set by function 1,12 (0 if it is stopped).
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | uint16 | rate in Hz, 0 if stopped |
Firmware documentation text
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 Hz65C02 assembly (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
Triggers a 65C02 interrupt at the start of every displayed frame (60 Hz, vertical sync of the DVI output); 0 stops it.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | non zero: enable, 0: stop |
Outputs: none.
Same delivery as the tick (1,12): handler at $FFFE-$FFFF, then CLI. Tick and frame interrupt may both be on: the handler cannot tell them apart (use 5,37 Frame Count or 1,1 Timer). Ideal for flicker-free animation.
Firmware documentation text
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
Not applicable in NeoBASIC: An interrupt handler is machine code; NeoBASIC cannot provide one.
65C02 assembly (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
Tells whether the frame interrupt (function 1,16) is on.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | non zero if on |
Firmware documentation text
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 : inactive65C02 assembly (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
Copies one of the 32 banks of 8 KB kept in the RP2040 flash into an 8 KB window of 65C02 RAM.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | bank number (0 to 31); $FF forgets the mapping without touching the window |
P1-P2 | addr | window address: page aligned, ending below $FF00 |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | non zero if the bank or the address is invalid |
The window is a copy: changing it does not change the bank (see 1,22). It must not hold the code making the call, the stack or the zero page. A bank never written reads $FF. The banks are also blitter pages $A0 to $BF (12,2), read only.
Firmware documentation text
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 25565C02 assembly (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
Gives the last selected bank, its window address, the number of banks and their size.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | last selected bank ($FF if none since reset) |
P1-P2 | addr | its window address |
P3 | uint8 | number of banks |
P4-P5 | uint16 | bank size in bytes |
Firmware documentation text
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 octets65C02 assembly (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
Gives the local date and time (time zone of function 1,24), and where they come from.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | uint16 | year (1970 to 2099) |
P2 | uint8 | month (1 to 12) |
P3 | uint8 | day (1 to 31) |
P4 | uint8 | hour (0 to 23) |
P5 | uint8 | minute |
P6 | uint8 | second |
P7 | uint8 | source: 0 not set (1970-01-01 + time since power on), 1 software clock (1,21), 2 PCF8563 clock on UEXT, 3 SNTP time of the USB modem (1,23) |
Firmware documentation text
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)65C02 assembly (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
Sets the local date and time; the PCF8563 clock is set too when present.
| Bytes | Type | Meaning |
|---|---|---|
P0-P6 | date | same layout as 1,20: year (2 bytes), month, day, hour, minute, second |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | non zero if a field is out of range (1970-2099, valid day of month, 24 hour time) |
Firmware documentation text
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:3065C02 assembly (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
Writes 8 KB of 65C02 RAM into a flash bank; the bank survives resets and firmware updates.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | bank number (0 to 31) |
P1-P2 | addr | address of the 8 KB to write (same constraints as 1,18) |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | non zero if the bank or the address is invalid, or the verification fails |
On the board, the flash sectors are erased then written, the display pauses for about 150 ms, and the content is verified. The emulators keep the banks in the file banks.flash of the storage. Flash wears: write a bank once, not in a loop (1,26 helps rewrite only what changed).
Firmware documentation text
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 : Bonjour65C02 assembly (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
Sets the date and time from the USB Wi-Fi modem (Pico W, Neo6502 Wi-Fi modem), which gets them through SNTP.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 0 success, 1 no modem connected, 2 modem without SNTP time (answer 1970) or silent |
Sends AT+CIPSNTPTIME? on the USB link and applies the answer, taking out the modem offset: the clock holds UTC, and 1,20 shows the local time of the zone of 1,24. Waits 2 seconds at most; pending input on the modem link is discarded. Nothing is automatic: the program must ask for it.
Firmware documentation text
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 USB65C02 assembly (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
Selects the time zone, by name (Europe/Paris, America/Montreal…) or as a fixed offset (UTC+2, UTC-3:30, +0530).
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | addr | address of the name: length-prefixed string |
| Bytes | Type | Meaning |
|---|---|---|
erreur | uint8 | 0 success, 1 unknown name, 2 flash write failed |
The clock keeps UTC; 1,20, 1,21 and FAT file dates 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 flash and applied again at every start up.
Firmware documentation text
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/Paris65C02 assembly (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
Gives the name of the current time zone, its offset from UTC and whether daylight saving time applies.
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | addr | address of a buffer: its first byte gives, before the call, the maximum length accepted; it then receives the name length, followed by the name |
| Bytes | Type | Meaning |
|---|---|---|
tampon | string | zone name ("UTC" by default) |
P2-P3 | int16 | offset from UTC in minutes (signed, daylight saving included) |
P4 | uint8 | non zero if daylight saving time applies |
Firmware documentation text
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 min65C02 assembly (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
Computes the Fletcher-16 checksum of a bank directly in flash, without copying it to RAM.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | bank number (0 to 31) |
| Bytes | Type | Meaning |
|---|---|---|
P0-P1 | uint16 | s1 = sum of the 8192 bytes modulo 65536 |
P2-P3 | uint16 | s2 = sum of the successive values of s1 modulo 65536 |
erreur | uint8 | non zero if the bank is invalid (the parameters are then zero) |
A bank never written reads $FF: s1 = $E000. Tells which banks of a cache changed before rewriting them with 1,22, without copying everything.
Firmware documentation text
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 = 6144065C02 assembly (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
Applies the choice of the boot menu (or of boot/auto.txt): loads the chosen program and returns 1, or returns 0 when there is nothing to start.
Inputs: none.
| Bytes | Type | Meaning |
|---|---|---|
P0 | uint8 | 1: program loaded, jump through address 0 ready; 0: NeoDOS chosen, nothing to start, or failure (a message says so) |
The kernel calls it once at reset, then 1,3 (NeoDOS) if it returned 0. The choice is forgotten once applied: called by a program, it therefore returns 0 and does nothing, as the example shows. A .bin is loaded at $0800 like NeoBASIC; a .neo must have an execution address.
Firmware documentation text
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 : 065C02 assembly (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