Aller au contenu
Eliott65wiki
Sommaire

Les appels système : une porte de deux octets

Un programme qui veut afficher, lire un fichier ou connaître l’heure frappe à une porte de deux octets. Le noyau, de l’autre côté, fait le travail. Simple, et sans rien réserver dans la mémoire du programme.

Dans cette page

Le principe

Le programme prépare un bloc de paramètres dans sa propre mémoire, à l’endroit de son choix (sauf en page zéro). Puis il écrit l’adresse de ce bloc dans la porte :

  • l’octet bas en $FFF8 ;
  • l’octet haut en $FFF9, ce qui déclenche l’appel.

Il attend ensuite que le noyau remette $FFF9 à zéro. Les résultats et le code d’erreur sont alors dans son bloc.

Un appel système, pas à pasLe programme range ses paramètres dans un bloc de sa mémoire, écrit l'adresse du bloc en $FFF8–$FFF9 et attend en relisant $FFF9. Le noyau, sur le cœur 0, lit le bloc, fait le travail, écrit les résultats et remet $FFF9 à zéro. Le programme repart. Environ 5 microsecondes pour un appel simple.Programme 6502Noyau (cœur 0 du RP2040)① prépare un bloc de paramètres② écrit l'adresse du bloc en $FFF8–$FFF9③ attend en relisant $FFF9LDA $FFF9 / BNElit le bloc, fait le travail,écrit les résultats dans le bloc④ remet $FFF9 à 0⑤ repart avec ses résultats≈ 5 µs pour un appel simple, soit ≈ 36 cycles de la 6502
Un appel système vu des deux côtés. La 6502 continue de tourner pendant l’appel : elle boucle sur $FFF9.

Le bloc a toujours la même forme :

OctetContenu
+0numéro du service (SYSTEME, ECRAN, FICHIERS…)
+1numéro de la fonction dans ce service
+2code d’erreur, écrit par le noyau (0 = succès)
+3 et suivantsles paramètres, puis les résultats

En assembleur (64tass), avec la bibliothèque libe65 fournie :

        lda #<bloc
        ldx #>bloc
        jsr e65_porte          ; ou la macro #E65_APPEL bloc
        lda bloc+E65_ERREUR    ; 0 = succès

bloc:   .byte E65_SVC_ECRAN, E65_ECRAN_TEXTE, 0
        .word $0400            ; adresse de la page de texte
        .byte $1F              ; blanc sur bleu

Pourquoi c’est différent

Une façon courante de dialoguer avec un système est une zone fixe : une « boîte aux lettres » à une adresse convenue, où l’on dépose les paramètres. Morpheus, par exemple, utilise 8 octets en $FF00. Eliott65 fait autrement :

  • aucune zone réservée : le bloc est où le programme veut, il peut en avoir plusieurs ;
  • aucun code résident : pas de routine du système à appeler en mémoire 6502 ;
  • l’appel est synchrone : quand la 6502 sort de sa boucle, le travail est fait.

Comment le noyau voit la porte

Avec le bus PIO + DMA, aucun cœur ARM ne voit passer les écritures de la 6502. Le noyau, sur le cœur 0, surveille donc l’octet $FFF9 dans la mémoire de la 6502. Dès qu’il n’est plus nul, il lit le bloc, traite l’appel, écrit les résultats, puis remet $FFF9 à zéro.

C’est pour cela que l’octet haut doit être non nul : le bloc ne peut pas être en page zéro.

Combien ça coûte

Le programme LATENCE.E65 a mesuré le coût d’un appel depuis la 6502 elle-même, avec l’horloge du système : 1 000 tours d’une boucle vide, puis 1 000 tours avec un appel SYSTEME PING.

Durée
un tour de boucle vide2,77 µs
un appel système complet4,99 µs, soit ≈ 36 cycles

Sur ces 36 cycles, une trentaine sont les instructions de la 6502 elle-même (préparer, écrire la porte, boucler, revenir). Le noyau ajoute environ 1 µs.

Le pire cas n’est pas mesuré : un appel qui tombe pendant que le pilote USB scrute le hub (une fois par seconde, quelques millisecondes) attend d’autant.

Les règles à connaître

  • Pas d’appel dans un gestionnaire d’interruption. Entre l’écriture de $FFF8 et celle de $FFF9, une interruption qui ferait son propre appel écraserait l’octet bas. Si c’est indispensable, le programme principal encadre ses appels par PHP / SEI … PLP.
  • La 6502 n’est pas gelée pendant l’appel : son horloge continue, ses interruptions sont servies, même pendant un long accès à la clé.
  • FICHIERS EXECUTER ne revient pas en cas de succès : le nouveau programme a pris la place.

Les services

N°ServicePour quoi faire
0SYSTEMEtemps, informations, hibernation, processus (lancer, céder, dormir, terminer, tuer)
1ECRANmire, texte simple, texte en couleur, graphisme
2CLAVIERfile de touches dans la mémoire du programme, lecture, disposition
3STOCKAGElire et écrire des blocs de 512 octets sur la clé
4FICHIERSlister, lire, écrire, supprimer, exécuter
5GRAPHIQUEmémoire vidéo, palette, plans, lutins, trame
6SONnotes et mélodies sur le haut-parleur de la carte

La liste complète, avec chaque bloc de paramètres et chaque code d’erreur, est dans la référence de l’API.

Une seule source pour l’API

L’API est décrite une seule fois, dans un fichier Python du dépôt. Deux choses en sont générées : la documentation de référence et le fichier d’inclusion e65.inc pour l’assembleur. Un test vérifie que les deux sont à jour. Le principe est le même que celui de Morpheus, qui génère aussi son code de répartition et sa documentation depuis une source unique.

Sources dans le dépôt : docs/adr/ADR-0002-noyau-hors-bande.md, docs/adr/ADR-0011-api-source-unique.md, docs/api/API.md, docs/journal/03-bus-demarrage.md, docs/journal/04-performances.md.