Triceraprog
La programmation depuis le Crétacé

  • Automatisation : utilisation de Visual Studio Code ()

    Il y a presque trois ans (déjà !), j'avais mis en place un environnement de programmation pour me permettre de mettre au point un programme en assembleur Z80 et de l'envoyer vers MAME, afin de réduire le nombre d'opérations manuelles. Le tout à partir de Sublime Text 3.

    Peut-être parce que la documentation de cet éditeur n'est pas des plus détaillée, ou peut-être parce que c'est avant tout un éditeur de texte, les extensions autour de l'assembleur Z80 sont peu nombreuses. C'est plutôt du côté de Visual Studio Code que ces extensions sont apparues.

    Récemment, j'ai donc fait deux changements dans ma chaîne de mise au point pour VG5000µ. Tout d'abord, j'utilise à présent Visual Studio Code comme éditeur, et ensuite, j'ai changé d'assembleur.

    Visual Studio Code

    Tout comme Sublime Text 3, l'important pour moi est que l'éditeur puisse fonctionner sur diverses plateformes, et entre autre sur celle que j'utilise pour mes projets rétro : Ubuntu Linux. De ce côté, c'est ok.

    Pour la syntaxe colorée, j'utilise le plugin Z80 Macro-Assembleur. Le plugin apporte aussi un « problem matcher », qui est la façon pour l'éditeur de savoir si des erreurs ont été levées lors de phase de construction.

    Il offre aussi un support d'Intellisense, mais que je n'ai pas encore mis en fonctionnement. A priori, ça ne fonctionne pas tout seul.

    J'ai aussi ajouté Z80 Assembly meter qui permet d'évaluer le nombre de cycles pris par du code sélectionné, avec une option MSX qui allonge d'un cycle les instructions, ce qui est aussi valable pour le VG5000µ.

    L'assembleur

    Auparavant, j'utilisais l'assembleur z80asm livré avec l'environnement z88dk. C'était un choix historique venant de mes essais en C sur le Z80. Cet assembleur fonctionne bien, mais est très minimaliste. En effet, un assembleur derrière un compilateur n'a pas besoin de beaucoup d'aides, le compilateur pouvant générer le code in extenso.

    Lorsque l'on met au point du code à la main, rapidement, pouvoir écrire des macros, manipuler des adresses via des expressions, devient un outil nécessaire.

    C'est vers sjasmplus que je me suis tourné. Comme souvent, l'assembleur est fait avec une machine particulière en tête, ici la ligne des Spectrum. Mais ce n'est pas bien grave. L'assembleur a des macros, est assez souple sur la syntaxe, a pas mal d'instructions pour déclarer ce que l'on veut faire.

    La tuyauterie

    Voici le fichier tasks.json que j'ai écris. Il est probablement perfectible car je ne connais pas toutes les subtilités de Visual Studio Code, mais il fait l'affaire pour le moment.

    Pour le fonctionnement du script vgboot.lua, je vous renvois à cet article. Le script n'a pas bougé depuis.

        "version": "2.0.0",
        "tasks": [
            {
                "label": "sjasmplus - Build",
                "type": "shell",
                // Options: No fake instructions, Warning as Errors, Multi arg is ',,'
                "command": "sjasmplus --syntax=FLwa ${fileBasename} --raw=${fileBasenameNoExtension}.bin --lst",
                "problemMatcher": [
                    "$errmatcher-sjasmplus"
                ],
                "group": {
                    "kind": "build",
                    "isDefault": true
                },
                "options": {
                    "cwd": "${relativeFileDirname}"
                }
            },
            {
                "label": "sjasmplus - Run on Mame",
                "type": "shell",
                "command": "mame64 vg5k -ramsize 48k -nomax -window -autoboot_delay 0 -autoboot_script vgboot.lua -debug -debugger none -natural",
                "problemMatcher": [
                    "$errmatcher-sjasmplus"
                ],
                "dependsOn": [
                    "sjasmplus - Build"
                ],
                "options": {
                    "cwd": "${relativeFileDirname}"
                }
            },
            {
                "label": "sjasmplus - Debug on Mame",
                "type": "shell",
                "command": "mame64 vg5k -ramsize 48k -nomax -window -autoboot_delay 0 -autoboot_script vgboot.lua -debug -debugger qt -natural",
                "problemMatcher": [
                    "$errmatcher-sjasmplus"
                ],
                "dependsOn": [
                    "sjasmplus - Build"
                ],
                "options": {
                    "cwd": "${relativeFileDirname}"
                }
            }
        ]
    }
    

    La suite

    Les environnements pour des machines connues vont encore plus loin, avec par exemple de l'intégration de debugger directement dans Visual Studio Code. Il y a aussi un support de tests unitaires, qui me plaît bien. Reste qu'il faut faire quelques branchements pour que cela fonctionne pour un VG5000µ. Je ne sais pas encore si j'irai vers là.


  • VG5000µ, Set Point assembleur depuis le BASIC ()

    Cet article est la suite de deux précédents articles. Le premier, en plusieurs parties, était l'implémentation de l'affichage d'un « gros pixel » sur l'écran du VG5000µ. Le second était celui sur la possibilité (ou plutôt la difficulté) d'ajouter des commandes au BASIC du VG5000µ.

    Il existe cependant une façon d'ajouter des commandes au BASIC... ou presque. Cette possibilité est évoquée brièvement dans le livre « Clefs pour VG5000 » page 98. À charge au lecteur de se débrouiller.

    Cette méthode, indiquée dans le livre, est à vrai dire celle qui est utilisée par l'implémentation BASIC de la machine. Chaque routine d'instruction démarre avec HL qui pointe dans le buffer du texte BASIC sur l'octet suivant le token d'instruction, et donc sur ses éventuels paramètres.

    C'est donc le cas, comme les autres, de l'instruction CALL. La routine de l'instruction se charge de lire l'adresse puis de l'appeler. Par lecture de ce paramètre, HL a été positionné après celui-ci et c'est donc dans cet état que la routine utilisateur est appelée.

    Exemple

    Voici un exemple rapide d'une commande qui peut s'appeler par CALL &"7000",X,Y, X et Y étant les coordonnées du point à afficher. La routine setpoint est celle de l'article cité au début.

    L'idée derrière le premier jp est de commencer par une série de jp qui amène aux différentes routines qui seront donc CALL &"7000", CALL &"7003", CALL &"7006",...

    On peut même aller jusqu'à mettre l'adresse dans une variable pour obtenir quelque chose comme CALL SP,X,Y (les variables ont deux caractères maximum significatif sur VG5000µ).

        defc chkchr = $8
        defc getbyt = $0086
    
        org     $7000
    entry:
        jp      call_setpoint
    
    call_setpoint:
        push    bc          ; Sauvegarde des registres, sauf HL, dont on a besoin
        push    af
        push    de
    
        rst     chkchr      ; Il doit y avoir une virgule, ou c'est une erreur de syntaxe
        defb    ','
    
        call    getbyt      ; Puis une expression 8 bits entière pour la première coordonnée X dans A et (D)E
        push    de          ; Sauve cette valeur sur la pile
    
        rst     chkchr      ; Il doit y avoir une virgule, ou c'est une erreur de syntaxe
        defb    ','
    
        call    getbyt      ; Puis une expression 8 bits entière pour la seconde coordonnée Y dans A et (D)E
    
        ex      (sp), hl    ; Récupère la coordonnée X (dans L) en échange du pointeur sur le texte BASIC
        ld      h, a        ; Place la coordonnée Y dans H
    
        call    setpoint    ; Appel l'affichage du point avec les coordonnées dans HL
    
        pop     hl          ; Restaure les registres, dont HL
        pop     de
        pop     af
        pop     bc
    
        ret
    

    D'autres choses ?

    Le livre indique aussi la possibilité d'utiliser deint ($0083). Ce n'est cependant pas possible directement. Cette fonction prend ce qui est dans l'accumulateur flottant et le place en tant qu'entier dans DE. Mais il faut donc au préalable charger l'accumulateur flottant.

    C'est possible avec eval_num_ex ($284d), qui doit donc être appelé au préalable, car c'est cette routine qui analyse l'expression et vérifie qu'elle est numérique.

    Conclusion

    Grâce à cette méthode, il est donc possible de créer une petite bibliothèques de routines assembleurs appelable avec des paramètres depuis le BASIC.

    Affichage des résultats de tests dans MAME


  • VG5000µ, le commentaire de la ROM ()

    Le 8 avril 2017, un message sur le forum system-cfg posait la question de l'existence d'un commentaire extensif de la ROM du VG5000µ. J'y répondais que j'avais quelques notes.

    Depuis, sur ce site, j'ai décortiqué un certain nombre de parties, par curiosité personnelle, ou pour répondre à des questions qui se posaient sur le même forum. Au fur et à mesure, je me suis embarqué dans le commentaire exhaustif de la ROM.

    Le commentaire

    Ce fut long, plutôt long.

    Et je publie le résultat aujourd'hui. Ce résultat prend la forme principale de deux fichiers de commentaires, l'un pour la ROM 1.0 et l'autre pour la ROM 1.1. Le tout est disponible ici sur GitHub.

    Ce que je ne publie pas aujourd'hui est l'outil qui permet de prend la ROM d'un côté, le fichier de commentaires de l'autre et qui génère un listing assembleur commenté qui peut-être assemblé à nouveau. Cet outil, que j'ai développé en parallèle, nécessite un bon coup de nettoyage avant publication. Peut-être que je le publierai avant son nettoyage complet, mais je dois au moins effectuer une relecture de sécurité.

    Terminé ?

    Les commentaires ayant été écrits sur une durée de trois ans, et malgré une passe de relecture récente, il reste probablement des incohérences de nommage, et peut-être aussi quelques fautes de frappe ou d'orthographe par-ci par-là. Il est tout à fait possible de me les mentionner.

    Dans le futur, en fonction d'autres développement autour du VG5000µ, il est possible que je revienne dessus et que je corrige ou que j'augmente les informations.

    Il y a aussi parfois des noms d'articles entre crochets. Il s'agit d'articles que j'ai prévu de faire, s'ils ne le sont pas déjà, sur ce site. On verra...

    C'est en français !

    Oui. C'est en français. Comme la machine. J'ai hésite à taper les commentaires en anglais ou en français. Au final, je suis parti pour le français en essayant de ne pas utiliser trop d'anglicismes, à part les évidents du domaine.

    Si quelqu'un veut se lancer dans une traduction dans une autre langue, la licence est « Attribution - Partage dans les Mêmes Conditions 4.0 International » (CC BY-SA 4.0). Allez-y !


  • VG5000µ, Schémas de principe mis à jour ()

    Il y a deux ans, je publiais ici une remise au propre du schéma de principe de la documentation du VG5000µ.

    Alors que je finalise le commentaire de la ROM, je me suis apperçu qu'il y avait une erreur au niveau du décodage des entrées sorties. C'est à présent corrigé (ainsi que sur l'article original).

    Au niveau de 7807, j'avais inversé les entrées \RD et \WR, ce qui n'avait pas de sens pour l'entrée C du 74LS138.

    J'en ai profité pour améliorer la nomenclature des signaux au niveau de ce même composant, en cohérence avec le datasheet.


    La platine principale

    Image cliquable pour une version en haute définition. (mise à jour 17 nov. 2020)

    Platine principale


    La platine K7/Son

    Image cliquable pour une version en haute définition. (mise à jour 9 sept. 2018)

    Platine K7/Son


  • VG5000µ, deux mises à jour sur MAME ()

    Lors du commentaire systématique de la ROM du VG5000µ, je suis arrivé sur les routines de lecture et écriture sur cassette. Comme d'habitude, afin de vérifier le fonctionnement de la machine, je fais des tests. Si je fais parfois des tests sur le matériel réel, la plupart du temps, un test sur émulateur suffit, voire est beaucoup plus simple, permettant de dérouler une routine et la suivre avec des données bien choisies.

    J'utilise essentiellement MAME pour cela, qui est muni d'un debuggeur qui répond à mes attentes. J'utilise parfois dcvg5k, qui est plus orienté sur une utilisation de la machine simple et pratique.

    Cependant, pour la cassette, aucun des deux ne convenait. Les deux émulateurs ne savent lire que le format K7, qui a le mérite d'être extrêmement simple et facilement lisible, mais qui a l'inconvénient d'être inadéquat au bon déroulement des routines de lecture et d'écriture.

    La format K7 est une simple liste des octets décodés depuis un enregistrement réel. C'est un format pratique car très compact. L'émulateur dcvg5k s'en sert pour injecter ou extraire les valeurs en court-circuitant la routine de lecture et d'écriture de la ROM. Si j'ai bien compris, l'émulateur intercepte l'exécution normale à des endroits bien choisis et prend la main pour une lecture ou écriture très rapide.

    C'est une idée qui se défend pour une utilisation de la machine en évitant des temps d'attentes associés aux cassettes.

    Dans mon cas d'étude des routines, c'est par contre hors-jeu. J'ai besoin d'un signal audio. Je me suis donc tourné vers MAME pour voir si je pouvais ajouter le traitement d'un fichier .WAV avec ma ROM 1.1 non modifiée.

    Ajout de lecture/écriture audio

    La première opération a été d'ajouter le support du format .WAV sur MAME.

    Comme on peut l'imaginer, la plupart des services de base existent déjà quelque part dans MAME. C'est l'avantage d'un tel mastodonte. Mais être un mastodonte à aussi un inconvénient : il faut savoir ça se trouve et comment ça s'utilise.

    Une fois trouvé, c'est assez simple. Dans le fichier src/lib/formats/vg5k_cas.cpp, à côté de la déclaration du support du type de fichier K7, il suffit d'ajouter la déclaration du support .WAV. Toute la gestion de fichier et de magnétophone virtuel est alors pris en charge.

    CASSETTE_FORMATLIST_START(vg5k_cassette_formats)
        CASSETTE_FORMAT(vg5k_k7_format)
        CASSETTE_FORMAT(wavfile_format)
    CASSETTE_FORMATLIST_END
    

    Premiers problèmes

    Je me doutais que cela n'allait pas fonctionner directement, puisque la machine était annotée comme ne fonctionnant pas, à cause d'un problème de lecture à 1200 bauds. J'avais comme deuxième objectif de corriger cela, afin de pouvoir analyser les routines complètement.

    Avec la prise en charge du format .WAV, l'émulation réussi à lire un fichier audio sans soucis. Mais méfiance, la routine du VG5000µ se sert de l'amorce du fichier, une suite de signaux haut/bas, pour se calibrer.

    Le deuxième test est donc de sauver un fichier .WAV puis de le recharger. Et là, cela ne fonctionne pas. Le fichier est bien sauvé, mais impossible à relire.

    En examinant le fichier obtenu, je vois que les timings sont complètement farfelus. Ils ne correspondent pas à la théorie décrite dans le manuel technique, ils ne correspondent ni à 1200 bauds ni à 2400 bauds, et ils ne correspondent pas à la comparaison avec des fichiers écrits par du matériel réel.

    Le moteur démarre

    Au passage, les manipulations sous MAME sont pénibles, car la gestion du contrôle du moteur du magnétophone n'est pas là. Là encore, la plus grande partie du temps passé est de comprendre comment MAME gère le système. Au final, tout est là, il suffit de le brancher sur le bon signal envoyé sur le port I/O de la cassette.

    Chose faite en modifiant la fonction void vg5k_state::cassette_w(uint8_t data) de src/mame/drivers/vg5k.cpp et en y ajoutant cette ligne :

    m_cassette->change_state(BIT(data, 1) ? CASSETTE_MOTOR_ENABLED : CASSETTE_MOTOR_DISABLED , CASSETTE_MASK_MOTOR);
    

    J'en profite pour réécrire cette petite fonction pour faire ressortir un peu mieux la sémantique des données envoyées sur le port I/O.

    À présent, les fonctions d'accès à la cassette (CLOAD, CSAVE et les autres) contrôlent le moteur simulé du lecteur dans MAME, est c'est bien plus pratique à utiliser !

    Le Z80 doit attendre

    Retour sur les problèmes de timings. Je vous évite tout le cheminement et les tests quand soudain me vient un doute. Est-ce que l'émulation tourne à la bonne vitesse ? Oui, la fréquence déclarée pour le Z80 est la bonne (même si techniquement, elle devrait découler de celle du VDP), mais cela ne suffit pas !

    En effet, le VG5000µ insère un état WAIT supplémentaire pendant sa phase M1 (fetch). Et ça change tout.

    Un petit détour par le Z80 ici. Lors de la phase M1 (opcode fetch), qui s'opère en 4 cycles d'horloge, le 2 premiers cycles placent l'adresse du PC sur le bus d'adresse puis signalent une requête de lecture mémoire. Lors du cycle 3 (T3), la valeur de l'instruction est lue depuis le bus de données. Les cycles T3 et T4 conjointement servent pour le rafraîchissement des mémoires dynamiques, laissons ça de côté.

    Le Z80 prévoit que le matériel puisse ne pas être prêt à temps pour livrer sur le bus de données l'instruction lue en cycle T3. La ligne WAIT est donc vérifiée à la fin de T2. Si la ligne est validée, alors le Z80 passe en mode WAIT en ajoutant des cycles supplémentaires d'attente, jusqu'à ce que la ligne WAIT soit relâchée.

    Et le driver MAME ne respecte pas ça.

    Le VG5000µ tourne trop vite ! Les timings sont faux et l'écriture sur cassette ne fonctionne pas. Reste à savoir comment corriger ça.

    À la recherche du cycle en plus

    Une première piste est d'aller voir comment est gérée cette fonctionnalité dans l'émulation Z80 de MAME. Mauvaise nouvelle, elle ne l'est pas. Ou plutôt, la ligne WAIT est bien émulée, mais uniquement entre les instructions. Cela est bien assez nécessaire, il semblerait, pour l'émulation de son utilisation par des périphériques qui demandent au Z80 d'attendre.

    Mais la détection au cycle T2 de la phase M1 n'est pas là. C'est d'ailleurs indiqué dans les commentaires comme une amélioration possible... Que je ne me sens pas d'ajouter.

    Deuxième piste : allez voir ce que font les autres. Côté Amstrad CPC et MSX, les tables de timings d'opcode sont ajustées. Il semblerait qu'il y ait d'autres raisons que ce seul état d'attente, même si c'est une des raisons. Les réutiliser seraient une option... pourvu qu'elles soient utilisables, ce que je trouve un peu lourd à vérifier.

    Reste que cela m'ennuie car ce n'est pas vraiment ce qu'il se passe dans la machine. Je fouille encore. Je trouve un driver qui annonce que faute d'avoir trouvé comment faire, la machine est trop rapide...

    Et enfin, je trouve une solution qui me plaît, utilisée par un autre driver. Utiliser une fonction de rappel sur le rafraîchissement des mémoires dynamiques, qui, étonnamment, est émulée par MAME. Il suffit alors, dans cette fonction de rappel, de dire à l'émulateur du Z80 qu'il devra exécuter un cycle de plus.

    void vg5k_state::z80_m1_w(uint8_t data)
    {
        m_maincpu->adjust_icount(-1);
    }
    

    Ça fonctionne !

    Et tout à coup, tous les timings cassette que je surveillais sont corrects ! La sauvegarde génère un fichier audio qui ressemble à quelque chose (même s'il est bien trop carré pour être pris pour un vrai signal sorti d'une machine réelle, mais ce n'est pas bien grave), et surtout, ce fichier est relu sans problème par l'émulateur.

    Et ceci est à 2400 bauds comme à 1200 bauds.

    Les fichiers sont aussi compris par l'utilitaire de transformation en format K7 qui accompagne dcvg5k.

    Des fichiers audio écrits depuis un vrai VG5000µ sont lus aussi. Il me reste le dernier test : lire sur du vrai matériel un fichier audio écrit par MAME. J'ai bon espoir que cela fonctionne, mais faute d'avoir effectué de vrais tests, le driver reste en non fonctionnel.

    Support de la touche DELTA

    Le VG5000µ possède sur son clavier cette touche non nommée qui est juste désignée par un triangle dans la documentation, connue aussi sous le nom de touche DELTA.

    Puisque j'étais dans MAME, pourquoi pas ajouter cette fonctionnalité ? En effet, le soft reset qui consiste à remettre le PC à 0, ce que fait MAME par défaut, ne permet pas de simuler le soft reset tel qu'implémenté sur la machine à travers la touche DELTA.

    Or, ce soft reset peut-être routé vers une routine utilisateur pour d'autres usages, comme par exemple, relancer directement un programme.

    Une touche à part

    Cette touche est à part dans sa connexion au système. Chaque touche du clavier forme une matrice qui est lue à travers le bus d'entrée/sortie du Z80. Mais pas celle-ci.

    La touche DELTA est (presque) directement branchée à la ligne NMI du Z80. Autrement dit, appuyer sur la touche provoque une interruption non masquable dans le Z80. Par défaut, cette interruption appelle une routine qui vérifie si la touche CTRL est aussi appuyée et dans ce cas, provoque un soft reset. Tout ceci après avoir appelé une potentielle routine utilisateur qui, de base, ne fait rien.

    Comme d'habitude, tout cela est bien entendu pris en charge par MAME, reste à savoir comment. Je suis allé voir du côté d'ancien matériels, où les switchs étaient branchés directement sur des fonctions, sans vraiment constituer un clavier.

    La solution trouvée est la suivante. Tout d'abord, déclarer un nouveau port d'entrée/sortie qui associe au changement d'état d'une touche (j'ai choisi la touche End/Fin du clavier, mais c'est configurable par l'utilisateur) une fonction de rappel.

        PORT_START("direct")
            PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_KEYBOARD)        PORT_CODE(KEYCODE_END)                              PORT_NAME("DELTA")          PORT_CHANGED_MEMBER(DEVICE_SELF, vg5k_state, delta_button, 0)
    

    La fonction de rappel signale tout simplement la ligne NMI du Z80 pendant un bref instant :

    INPUT_CHANGED_MEMBER(vg5k_state::delta_button)
    {
        if (!newval) {
            m_maincpu->pulse_input_line(INPUT_LINE_NMI, attotime::zero);
        }
    }
    

    Et voilà. La touche DELTA est émulée. On peut vérifier le fonctionnement des routines utilisateur sur CTRL+DELTA.

    Attention, elle ne l'est que lorsque le clavier est en more réel dans MAME, et non en mode naturel, ce dernier mode cherchant une correspondance naturelle entre le clavier de l'hôte et la machine émulée.

    C'est où ?

    Les deux patchs ont été intégrés dans la branche principale de MAME, c'est donc dès à présent disponible sur le dépôt, ou bien dans une future version officielle de MAME.


Page 1 / 15 (suivant) »