Aller au contenu
Eliott65wiki
Sommaire

Plusieurs programmes à la fois

Une 6502 n’a qu’une mémoire de 64 Kio et aucune protection. Eliott65 y fait pourtant tourner jusqu’à huit programmes, même ceux qui ne rendent jamais la main. Voici comment, étape par étape.

Dans cette page

Étape 1 : deux programmes, deux mémoires (v0.13)

Le bus PIO + DMA calcule chaque adresse à partir d’une base. Changer de programme, c’est donc simplement changer de base : la 6502 se retrouve dans un autre bloc de 64 Kio du RP2040. Rien n’est copié, aucun octet n’est perdu. La mémoire du RP2040 permettait deux blocs de ce genre.

Pour passer la main, un programme appelle e65_ceder : comme pour l’hibernation, la routine range ses registres dans ses propres octets, puis le noyau arrête la 6502, change la base et la redémarre dans l’autre programme.

Un seul programme a l’écran et le clavier : on dit qu’il est au premier plan. Les autres tournent en arrière-plan.

Le bogue des lettres fantômes

Pendant les essais, deux lettres, « PR », réapparaissaient sans cesse sur l’écran du premier programme, à un endroit où il n’écrivait jamais. L’analyse a montré qu’il s’agissait exactement des octets qu’écrivait l’autre programme.

La cause : la W65C02S exécute encore quelques cycles après qu’on lui a demandé de se réinitialiser. Au moment de la commutation, elle était déjà dans la mémoire du nouveau programme ; elle y lisait que l’appel était fini, sortait de sa boucle et écrivait dans la mauvaise mémoire. La correction : mettre la 6502 en RESET et lui donner environ 70 cycles dans la mémoire de l’ancien programme, pendant qu’elle ne fait que lire, puis seulement changer de mémoire.

Résultat : 168 000 commutations sans un octet altéré dans les deux programmes.

Étape 2 : interrompre un programme égoïste (v0.14)

Le multitâche « coopératif » suppose que chaque programme cède la main de temps en temps. Et s’il ne le fait jamais ? Il faut pouvoir l’interrompre : c’est le multitâche préemptif.

La 6502 a une interruption non masquable, la NMI. Mais pour la gérer, il faut du code dans la mémoire de la 6502, et Eliott65 ne veut rien y laisser. L’astuce : le noyau emprunte 16 octets ($FFE8–$FFF7) le temps de l’interruption. Il y dépose un mini-gestionnaire qui empile les registres A, X et Y (la 6502 a déjà empilé PC et P), note le pointeur de pile, puis rend les octets avant de changer de programme. Tout l’état du programme interrompu est sur sa propre pile.

Sur la carte, du premier coup : un programme qui vérifie ses registres à chaque tour a subi 6 129 préemptions et environ 2,5 millions de vérifications, sans une erreur.

Étape 3 : jusqu’à 8 programmes (v0.17)

Deux blocs de 64 Kio, c’est deux programmes. Pour aller plus loin, il aurait fallu traduire les adresses à chaque cycle (une MMU dans le bus) : mesurée, cette piste coûtait 34 % de la vitesse. Elle a été écartée au profit d’une autre idée : échanger les pages au moment de la commutation.

Plusieurs programmes dans la même mémoireLa 6502 voit toujours une seule fenêtre de 64 Kio, découpée en 16 pages de 4 Kio. À chaque changement de programme, les pages non nulles du programme actif sortent vers une réserve de 31 cadres de 4 Kio, et les pages du suivant entrent. Une page entièrement à zéro n'occupe aucun cadre.Fenêtre de la 650264 Kio = 16 pages de 4 Kio010000000000000Fsortent : ses pages non nullesentrent : celles du suivantRéserve du RP204031 cadres de 4 KioAAABBBBCCCtableprogramme actif (A)programme B en attenteprogramme C en attentecadre libreUne page restée à zéro ne prend aucun cadre : un programme typique n'en remplit que 3.
La 6502 voit toujours la même fenêtre de 64 Kio. Les programmes en attente gardent leurs pages dans une réserve du RP2040 ; seules les pages non nulles font l’aller-retour.
  • La mémoire de la 6502 est découpée en 16 pages de 4 Kio.
  • Les programmes en attente gardent leurs pages dans une réserve de 30 cadres de 4 Kio (31 jusqu’à la v0.54 : le dernier porte désormais la page de zéros partagée, ce qui a rendu 4 Kio au firmware).
  • À la commutation, les pages non nulles du programme actif sortent, celles du suivant entrent.
  • Une page restée à zéro ne coûte rien : elle n’occupe aucun cadre.

Un programme typique n’utilise que 3 pages (page zéro et écran, code, vecteurs) : il en reste pour beaucoup d’autres. La limite est fixée à 8 processus.

Mesure, 5 programmes en même tempsValeur
échange des pages à la commutation307 µs
vitesse de la 65026,89 MHz (−5 % par rapport à un seul programme)
erreurs de registres après 12 308 commutations0

Ce que l’utilisateur a vu

  • Une barre horizontale qui descendait sur l’écran : pendant l’échange, la vidéo lisait la mémoire en cours de réécriture. Désormais la vidéo lit l’écran du premier plan par sa propre table de pages, basculée avant la réécriture.
  • Des touches perdues : elles n’étaient versées au programme que dans un cas qui ne se présentait jamais pour le shell. Elles le sont maintenant à l’entrée du programme dans la fenêtre, et dans l’ordre.
  • Un programme de test qui se recopiait « tant qu’il n’était pas deux » a rempli la machine de copies de lui-même : une preuve involontaire qu’on en loge bien huit.

Étape 4 : dormir au lieu de céder (v0.18)

Un shell qui attend une touche n’a rien à faire, mais il cédait la main des centaines de fois par seconde, et chaque fois coûtait un échange de pages. Il dort désormais : il n’est plus choisi jusqu’à ce qu’une touche arrive pour lui ou qu’un délai expire. Une touche pour le programme au premier plan endormi le réveille immédiatement.

Effet mesuré : en attente, 4 commutations en 10 secondes au lieu d’environ 130 par seconde.

Le shell

SHELL.E65 démarre tout seul s’il est sur la clé. C’est un terminal qui défile, en 40 × 30 couleur ou, depuis la v0.57, en 80 × 25 avec le texte du mode Hercules :

CommandeEffet
NOMlance le programme NOM.E65 au premier plan
NOM &le lance en arrière-plan
PSliste les processus
FG nmet le processus n au premier plan
KILL ntermine le processus n
DIRliste la clé, page par page
MODE 40, MODE 80passe en 40 × 30 couleur ou en 80 × 25
CLS, HELPefface l’écran, affiche l’aide

Les commandes sont en anglais depuis la v0.57.1, à la demande de l’utilisateur (HELP, FG et KILL remplacent AIDE, PP et TUER) ; les messages restent en français.

Ctrl+Tab, géré par le noyau, fait passer l’écran et le clavier au programme suivant. On peut aussi relancer une machine hibernée en arrière-plan : ETAT.HIB &.

Shell d'Eliott65 : REGISTRE & et MULTI & lancés en arrière-plan, puis PS qui liste REGISTRE.E65, SHELL.E65 au premier plan et MULTI.E65
Trois processus : REGISTRE et MULTI en arrière-plan, le shell au premier plan. Capture de l’émulateur d’Eliott65.

Un défaut trouvé plus tard

En mesurant le graphisme (v0.40), un programme qui enchaînait les appels système rendait le noyau incapable de le préempter : à chaque tentative, le noyau trouvait un appel en cours, abandonnait, et recommençait. L’ordre des opérations a été corrigé (geler, vérifier la porte, puis compter les pages). Voir Tuiles et lutins.

Sources dans le dépôt : docs/journal/11-multitache.md, docs/adr/ADR-0015-multitache.md, docs/adr/ADR-0016-preemption-nmi.md, docs/adr/ADR-0017-shell.md, docs/adr/ADR-0018-pages-a-la-commutation.md, docs/adr/ADR-0019-sommeil.md.