Site logo

Triceraprog
La programmation depuis le Crétacé

  • PHC-25, et Z80 en IM 2 ()

    Ça y est, la nouvelle saison des Retro Programmers United for Obscure System a démarré. Et cette fois, c'est le PHC-25 qui est à l'honneur. Un ordinateur de Sanyo à base de Z80 (d'un clone plus précisément) et d'un MC6845 (... un clone aussi) et plutôt bien fourni en mémoire vive pour cette classe de machines : 16 ko de RAM et 6 ko pour la VRAM, accessible directement par le Z80 (avec des contraintes de timing si on veut éviter des parasites graphiques).

    Bien entendu, comme toutes les machines concernées par cette game jam, le PHC-25 est un ordinateur qui n'a pas une grande logithèque, et pas non plus beaucoup d'activité ni de documentation. C'est le principe.

    Pour cette Jam, je suis parti sur l'idée que le programme sera en assembleur. Certains vont certainement encore faire des miracles en BASIC, mais ce n'est pas ma tasse de thé, même si je l'ai enté par le passé. Avec la communauté des participants, nous nous sommes donc penchés sur la compréhension de la machine. Pas facile là encore car nous ne sommes pas nombreux à avoir un PHC-25, il faut donc continuellement passer par les bonnes âmes qui peuvent lancer nos tests.

    Après quelques jours, j'ai commencé à mettre mes notes, un programme d'exemple et une manière de packager un programme assembleur sur gitlab. C'est principalement le résultat d'un travail de recherche collectif.

    J'ai pu aussi corriger deux petits trucs dans MAME, qui reste loin d'être parfait pour le PHC-25, mais qui est un tout petit peu mieux qu'il y a deux semaines. La machine reste assez simple et peu exotique par rapport à d'autres que l'on a traitées auparavant.

    IM 2

    Lors de nos discussions et analyse, il est apparu que la ROM du PHC-25 lançait une ISR (Interrupt Service Routine) à chaque synchronisation verticale. C'est classique. Pendant cette ISR, la ROM fait les modifications en VRAM en fonction des modifications d'affichages demandées par le BASIC, cela pour éviter les parasites graphiques. Il y a aussi une lecture du clavier, la gestion du son,...

    Mais aucune possibilité de reroutage. Là où l'on trouve dans d'autres machines un « hook » sur lequel on peut attacher son propre code, ici, il n'y a rien.

    La proposition a été faite de passer le Z80 en IM 2, afin de gérer entièrement l'interruption. Cela fonctionne, et ce que je vais expliquer ici. À noter que l'IM 2 est une fonctionnalité du Z80 et que la grande partie de ce qui suit est valable pour d'autres machines similaires à base de Z80.

    Interrupt Mode ?

    Le Z80 peut fonctionner selon trois modes d'interruption. Le mode 0 est le mode par défaut. C'est un mode reliquat du 8080. Dans ce mode, lorsque l'interruption est reçue, le Z80 va lire l'instruction suivante qui va lui être présentée depuis un mécanisme externe. Généralement, c'est une instruction RST qui est utilisée, afin de brancher à l'une des routines RESTART du Z80.

    Mais la plupart, si ce n'est toutes, des machines familiales à base de Z80 utilisent le mode 1, qui est beaucoup plus simple. Dans ce mode, lorsque l'interruption est reçue, le Z80 branche tout simplement à l'adresse fixe 0x0038, à partir de laquelle on place l'ISR. C'est ce que fait le PHC-25. Mais comme son ISR n'offre pas de moyen d'extension, on est vite limité si on veut prendre les choses en main.

    Le mode 2, ou IM 2, est un mode qui est normalement fait pour être utilisé avec un contrôleur d'interruption externe. Dans ce mode, l'interruption est suivie d'une valeur paire sur 8 bits. Cette valeur est utilisée comme index dans une table d'adresses d'ISRs. Le Z80 va lire l'adresse de l'ISR à partir de cette table, puis il va brancher à cette adresse.

    Cependant, le PHC-25 n'a pas de contrôleur d'interruption externe. Ainsi, la valeur reçue peut être un peu n'importe quoi (et même pas forcément paire). L'astuce consiste donc à utiliser une table dont toutes les entrées pointent vers la même adresse. Comme l'index peut-être paire ou impaire, cela implique une contrainte supplémentaire : l'adresse de l'ISR doit être composée de deux octets identiques ($FEFE par exemple). Autre petit piège, comme la valeur 255 peut-être reçue, la table doit contenir 257 octets, et non pas 256 uniquement.

    En pratique

    Voici le code commenté pour mettre en place l'IM 2 sur le PHC-25 :

        ; Code pour l'assembleur SjASMPlus
    
        org $c009   ; Adresse de départ du programme.
                    ; On commence à $C009, car c'est l'adresse de départ
                    ; avec le chargeur BASIC utilisé. Mais vous pouvez
                    ; adapter.
    
    start:
        call install_im2    ; Le programme démarre par l'installation de l'IM 2
    
    infinite_loop:
        halt                ; Attente de l'interruption
        jp infinite_loop    ; Puis boucle
    
    
        ; Installation de l'IM 2
    install_im2:
        di      ; On commence par désactiver les interruptions
                ; Comme on va modifier la configuration,
                ; on ne veut pas qu'elles se déclenchent pendant ce temps
    
    
        ; Déplace la pile
        ; Peut-être optionnel, mais comme ça, on sait où elle est
        ; On ne pourra pas retourner dans la ROM, mais de toute façon
        ; elle ne fonctionnerait pas en IM2.
        pop hl                  ; sauve l'adresse de retour de la routine
        ld  sp,$fffe            ; place la pile à $FFFE
        push hl                 ; Pousse l'adresse de retour sur la pile
                                ; À présent, le RET peut retourner à l'endroit de l'appel
    
    
        ; Remplit la table des vecteurs d'interruption
        ld      hl,$fe00        ; La table démarre en $FE00
        ld      e,l
        ld      d,h
        inc     de              ; Idiome de remplissage de mémoire
        ld      bc,257          ; De longueur 257 octets
        ld      a,$fd           ; Contenant l'adresse $fdfd
        ld      (hl),a
        ldir                    ; Remplissage de mémoire
    
        ; On place un saut vers l'ISR dans le vecteur d'interruption
        ld      hl,$fdfd        ; L'adresse du vecteur
        ld      a,$c3           ; Instruction de saut (JP) vers xxxx
        ld      (hl),a
        inc     hl
        ld      de,isr          ; L'adresse de l'ISR
        ld      (hl),e
        inc     hl
        ld      (hl),d          ; Met l'adresse de l'ISR après l'instruction JP
    
        ; Place la table des vecteurs d'interruption ($FExx)
        ld      a,$fe
        ld      i,a             ; Met l'adresse haute à $FE
        im      2               ; Commute en Mode d'Interruption 2
    
        ; All set
        ei                      ; Réactive les interruptions
        ret
    
    char:
        db      32
    
        ; Routine de service d'interruption
        ; Appelée au début de la synchronisation verticale
        ; Affiche un caractère en haut à gauche de l'écran
        ; (en mode SCREEN 1)
    isr:
        ld      hl,char
        ld      a,(hl)
        inc     (hl)
        ld      hl,$6000
        ld      (hl),a
        ei
        reti
    

  • Récréation Famicom ()

    Et puisque ces temps-ci, je m'intéresse à la Famicom, en ayant commencé par le Family Basic , j'ai voulu faire une petite image 3D de la console.

    Un recréation da la Famicom


  • Family BASIC, le BASIC sur Famicom ()

    L'hiver dernier, j'ai découvert le Family Basic, ou Famibe, un BASIC pour la Famicom (le petit nom de la Family Computer de Nintendo, dont la version occidentale donnera la Nintendo Entertainment System, ou NES).

    Le Family Basic, qui vient dans une boite bien visible sur une étagère, comprend une cartouche, un clavier et un manuel. Le clavier est un vrai clavier et est assez agréable à utiliser. Il se branche sur le port d'extension en façade de la console, là où se branchent les périphériques d'entrées supplémentaires, les deux manettes étant connectées à la console « en dur ».

    La cartouche est plus haute que les cartouches Famicom classiques, presque la taille d'une cartouche NES. Dedans, il y a de la rom PRG (le programme), de la rom CHR (les données graphiques) et de la RAM (la mémoire vive) supplémentaire. Cette RAM peut d'ailleurs être alimentée par piles lorsque la console est éteinte, ce qui permet de garder des programmes en mémoire.

    Quant au manuel, en japonais, il est assez fourni et explique comment utiliser le BASIC, comment utiliser les particularités de celui-ci et contient des listings de programmes à taper, avec des explications.

    Ce BASIC est plutôt intéressant, et même si la mémoire vive est très limitées (2ko sur les version 1 et 2 de la cartouche, 4ko sur la version 3), il est possible de faire des choses intéressantes.

    Il y a eu plusieurs versions du Family Basic. La première, sortie en 1984, semble ne pas avoir été très fiable et a pu être échangée contre une version 2 (il semble exister une version 2.0 et une version 2.1). Ces deux versions ont une cartouche de couleur noire. La version 3, dans une cartouche rouge et sortie en 1985, est beaucoup plus intéressante : plus de commandes et plus de mémoire vive.

    J'ai résumé quelques-unes de mes expériences dans une vidéos que vous pouvez voir ci-dessous.

    Un article en anglais, qui donne plus d'informations que ce présent petit article est disponible sur le site Nerdly Pleasures.


  • Instance Peertube pour Triceraprog ()

    Un projet que j'avais depuis un moment maintenant était de mettre en place une instance Peertube pour ce site. J'ai profité d'un changement de version majeur de Peertube pour réactiver ce projet et c'est maintenant chose faite.

    J'ai ai donc mis les deux vidéos sur le BASIC et celle sur le LOGO.


  • Environnement de développement pour Picthorix ()

    Forth est naturellement un langage qui est souvent utilisé dans un environnement interactif qui est à la fois un interpréteur et un compilateur. Il est aussi parfois accompagné d'un éditeur qui permet d'enregistrer des pages de code, afin de les sauvegarder, de les recharger et de les exécuter.

    Le Hector HRX a bien tout ça, et cela était probablement très agréable dans les conditions d'époque. Enfin, peut-être pas lorsque l'on n'avait que le lecteur de cassette intégré. En effet, le système puissant des écrans charge et sauve les pages automatiquement au besoin. Pratique lorsque la mémoire de masse est à accès direct, comme une disquette. Mais avec une cassette, il faut souvent avancer, rembobiner avec de bonnes chances d'écraser des données.

    Pour le développement de Picthorix, j'ai souvent utilisé le mode interactif pour bien comprendre le fonctionnement de certains mots, pour tester des idées, pour vérifier le fonctionnement de certaines parties de mon code. Mais hors de question d'utiliser en continu le mode natif, j'aurais perdu trop de temps en manipulations et en erreurs.

    Donc, comme souvent, j'ai réfléchi à un petit environnement de développement à base d'éditeur et d'émulateur sur PC.

    Formatage des écrans

    Le Hector HRX peut avoir en mémoire 10 écrans simultanés. Chaque écran contient 798 caractères. Chaque caractère est donc précieux. Mais formatter son code pour le rendre lisible et ajouter des commentaire est très précieux aussi, surtout dans un langage que l'on ne maîtrise pas.

    J'ai donc fait le choix d'avoir en source un fichier texte de forme libre et d'avoir une moulinette qui compacte le code pour mettre le maximum de code dans un écran. Il existe aussi le mot Forth --> qui permet de lancer l'exécution de l'écran suivant numériquement, la moulinette peut donc ajouter ce mot de transition à la fin de chaque écran, sauf le dernier.

    En pratique, il y a quelques pièges dans lesquels je suis tomber. Le premier est que la définition d'un mot ne peut pas commencer sur un écran et continuer dans le suivant. Il aurait fallu avoir un peu d'analyse du code Forth pour faire ce découpage correctement. C'est un piège que j'ai contourné de manière tout à fait manuelle : je place les --> à la main dans le code source pour séparer les screens. Pas le plus agréable, je me suis souvent retrouvé avec des screens trop grands qui ne chargeaient pas correctement, mais pas assez par rapport au temps de développement que cela m'aurait pris.

    Second piège, que j'ai mis un moment à comprendre. Parfois, mes affichages de chaînes de caractères étaient décalées, comme si des espaces étaient ajoutées en début de chaîne. J'ai mis un moment à comprendre que c'était parce que la mot de début de chaîne ." se trouvait en fin de ligne, mais pas en dernière caractère, et que le premier mot affiché se trouvait à la ligne suivante. Comme les écrans ont une largeur fixe, des espaces étaient ajoutés mécaniquement en début de chaîne.

    J'ai corrigé ça rapidement en revenant à la ligne lors de la détection d'un mot .". Cela fait perdre de l'espace, mais pas trop. Ça passe...

    Dernier piège... un développement un peu fait rapidement. La moulinette n'est pas très solide et se plante parfois lorsque la ligne source est « trop » longue. Je n'ai pas analysé pourquoi. J'ai contourné le problème en coupant les lignes trop longues dans le source lorsque le bug apparaissait. Celui-ci provoque un saut au prochain screen pour une raison qui m'échappe, mais qui serait probablement simple à comprendre avec du temps pour le débugger. Je n'ai pas pris ce temps.

    L'éditeur

    Le code source est édité dans Visual Code, mais techniquement, il n'y a rien de spécial. Je ne l'ai pas spécifiquement customisé, à part une petite colorisation syntaxique.

    L'émulateur

    Comme d'habitude, j'utilise MAME pour son scripting Lua qui a un accès au debugger. J'ai fais évoluer le script déjà utilisé dans des précédents projets (description initiale ici). Il a fallu bien entendu adapter les points d'arrêt pour l'automatisation et injecter les écrans préalablement converti en format binaire en mémoire.

    L'injection des écrans directement en mémoire est un peu fragile. En effet, Forth ajuste des variables lorsque l'on créé et on manipule des écrans. J'ai tenter de forcer ces variables à des valeurs connues en fonction des écrans injectés, mais sans vraiment de succès. Je me retrouvais avec un environnement extrêmement instable. J'ai cependant repéré qu'en évitant d'utiliser le premier écran, le système se comportait bien. Tant pis pour la perte d'un écran.

    Une fois les écrans injectés, je lance la compilation en passant l'émulateur en mode rapide, et j'obtiens ainsi un compilateur. La nature du Forth fait qu'à la fin du processus, j'ai une image du jeu compilé en mémoire à un emplacement tout à fait déterminé.

    À partir de là, soit je peux lancer le jeu pour les tests, soit tester certains mots. Je peux aussi sauvegarder la mémoire pour la recharger plus tard.

    Comme au bout d'un moment, j'ai rempli tous les screens, j'ai eu besoin d'une deuxième série d'écran à compiler à la suite. Pour scripter cela, le dernier écran de la première série termine en écrivant à un endroit précis de la mémoire et très éloigné de l'espace utilisé. Le script Lua détecte cette écriture et injecte la nouvelle série d'écran, puis lance une nouvelle compilation.

    Cela aurait pu être étendu à plus de séries d'écrans, il reste encore beaucoup de place en mémoire avec le jeu au complet. Cependant, comme il s'agit de le mettre sur cassette à la fin, il faut aussi veiller à ce que le temps de chargement soit de durée acceptable.

    Toujours automatiser !

    C'est mon conseil récurrent, et je le répète à nouveau. La dernière étape pour créer un jeu distribuable est donc de dumper la mémoire contenant tous les mots compilés qui forment le jeu. À partir du debuggeur, il faut donc spécifier une plage mémoire à sauvegarder et une adresse du mot de démarrage. Pour cela, en fin de compilation de la dernière série de screen, j'affichais les informations (taille, adresse de début) à l'écran. Restait à utiliser ces informations dans le debugger pour sauvegarder la mémoire.

    Le jour où je voulais publier le jeu, impossible d'avoir un dump qui fonctionne. J'ai passé la soirée à analyser un bug. Forcément, un vendredi soir, la fatigue de fin de semaine n'aide pas. Je suis allé me coucher sans trouver. Le lendemain, je relance et je trouve immédiatement (une bonne nuit de sommeil, ça aide). La taille que j'avais recopié à la main pour le dump n'était pas la bonne, de quatre octets...

    J'ai donc ajouté au script Lua le dump automatique, en allant chercher l'information sur la taille laissée par la fin de la compilation en mémoire.

    Conclusion

    Chaque operation manuelle répétée est une occasion de perdre son temps...

    À part ça, pas grand chose de neuf. Les outils sont disponibles dans les sources du jeu. Cependant, à la date d'écriture de cet article, avoir un Hector HRX qui fonctionne bien sur MAME nécessite quelques modifications que je n'ai pas encore proposé pour intégration. Mais cela sera fait quand j'aurais nettoyé les changements et vérifié que je n'ai pas cassé d'autres machines.


Page 1 / 23 (suivant) »

Tous les tags

3d (15), 6809 (1), 8bits (1), Affichage (24), AgonLight (2), Altaïr (1), Amstrad CPC (1), Apple (1), Aquarius (2), ASM (30), Atari (1), Atari 800 (1), Atari ST (2), Automatisation (4), BASIC (31), BASIC-80 (4), C (3), Calculs (1), CDC (1), Clion (1), cmake (1), Commodore (1), Commodore PET (1), CPU (1), Debug (5), Dithering (2), Divers (1), EF9345 (1), Émulation (7), Famicom (2), Forth (3), Game Jam (1), Hector (3), Histoire (1), Hooks (4), i8008 (1), Image (17), Jeu (14), Jeu Vidéo (4), Livre (1), Logo (2), Machine virtuelle (2), Magazine (1), MAME (1), Matra Alice (3), MDLC (7), Micral (2), Motorola (1), MSX (1), Musée (2), Nintendo Switch (1), Nombres (3), Optimisation (1), Outils (3), Pascaline (1), Peertube (1), PHC-25 (1), Photo (2), Programmation (5), Python (1), ROM (15), RPUfOS (5), Salon (1), SC-3000 (1), Schéma (5), Synthèse (15), Tortue (1), Triceraprog (1), VG5000 (62), VIC-20 (1), Vidéo (1), Z80 (21), z88dk (1)

Les derniers articles

PHC-25, et Z80 en IM 2
Récréation Famicom
Family BASIC, le BASIC sur Famicom
Instance Peertube pour Triceraprog
Environnement de développement pour Picthorix
Un jeu en Forth pour Hector HRX : Picthorix
Yeno SC-3000 et condensateurs
Suite de tests pour VG5000µ
Un peu d'Atari ST
Le Forth sur Hector HRX

Atom Feed

Réseaux