Triceraprog
La programmation depuis le Crétacé

  • 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.


  • VG5000µ, deux routines (quasi) identiques ()

    Hier, en continuant le commentaire systématique de la ROM du VG5000µ, j'ai eu une impression de déjà-vu. Il me semblait bien avoir déjà commenté la partie « redéfinir un caractère étendu » (commandes SETE et SETG en BASIC).

    L'impression persiste, voire s’accroît, au fur et à mesure et je me décide à aller voir dans ce que j'ai déjà commenté. J'ai commencé, lentement, ce commentaire en août 2017, avec moult pauses, mais aussi du « butinage ». Il y a donc des parties que je ne me rappelle plus avoir déjà traitées.

    Et effectivement, un peu plus loin, je vois des commentaires similaires à ceux que je suis en train d'écrire. Les termes sont un peu différents, car au fil des commentaires, j'ai fait évoluer certains termes ou la manière de commenter, mais en regardant de plus près, oui, c'est évident, il y a deux fois la même routine dans la ROM.

    Je ne sais pas si c'est quelque chose de connu, alors voici mes commentaires.

    Le VDP

    Le Video Display Processor du VG5000µ, un EF9345, est utilisé par le BASIC de manière généralement indirecte. Les commandes comme les changement de couleur, de type de caractères ou encore de position du curseur, modifient des registres internes en RAM. Ces registres sont juste des emplacements réservés dans la RAM principale, celle accessible au Z80, ils n'ont rien de spécial.

    Ces registres se trouvent à partir de $47fa et pointés en tout temps par le registre IX.

    Le BASIC maintient aussi une représentation de l'écran, en $4000, et afficher quelque chose en BASIC revient à modifier cette représentation d'écran, en se basant sur les registres BASIC.

    À chaque rafraîchissement demandé par le VDP, via l'IRQ du Z80 et si le BASIC considère qu'il est temps de mettre à jour l'affichage, un transfert des données est fait vers le VDP.

    C'est le fonctionnement de base.

    Cependant, la ROM contient aussi des routines qui s'adressent directement au VDP. On peut envoyer un caractère dans la mémoire écran du VDP sans passer par la représentation maintenue par le BASIC. Bien évidemment, si on laisse faire le rafraîchissement du BASIC, cette donnée sera écrasée rapidement. On peut aussi lire un caractère depuis la mémoire écran du VDP.

    Ces quatre routines, putahl, putici, getahl et getici ne sont jamais utilisées par la ROM. Ce sont des routines mises à disposition pour l'utilisateur à une adresse fixe, dont le seul code est un branchement à l'implémentation.

    Une autre routine mise à disposition est setext, qui s'occupe de la redéfinition d'un caractère texte ou graphique dans le VDP. Le VDP est en effet configuré par défaut pour offrir 4 « polices de caractères », dont 2 en ROM, et 2 en RAM (celle accessible directement par le VDP). Les deux en RAM peuvent être modifiées via cette routine setext en fournissant le numéro du caractère à changer et 10 octets qui représentent les 10 lignes d'affichage du caractère.

    setext se trouve en $001b et branche immédiatement sur son implémentation en $0d85.

    setext vs. SETE(T/G)

    Le BASIC offre deux commandes pour redéfinir les caractères : SETET, pour redéfinir un caractère texte, et SETEG, pour redéfinir un caractère graphique. La différence entre caractère texte et graphique sort du périmètre de cet article.

    Du point de vue de l'interpréteur BASIC, il n'y a qu'une seule commande SETE, qui vérifie si le caractère suivant est T ou G. On pourrait dire que la dernière lettre de la commande est vue par l'interpréteur comme son premier paramètre.

    L'exécution de SETE se trouve en $0ced dans la ROM BASIC.

    On pourrait s'attendre à ce que SETE utilise la routine setext. Ou bien que les deux routines aient une partie commune. Ce n'est pas le cas.

    Des 141 octets de l'implémentation de setext, environ 100 (à la louche) sont strictement identiques dans SETE, routine qui elle pèse 160 octets (en incluant sa routine annexe, qu'elle est la seule à appeler).

    Différences

    Les deux routines font la même chose, dans le même ordre :

    • Couper le rafraîchissement de l'écran depuis sa représentation RAM,
    • Déterminer si on veut un caractère graphique ou texte,
    • Récupérer le numéro du caractère,
    • Envoyer les commandes au VDP pour préparer la définition,
    • Envoyer les 10 lignes au VDP,
    • Rétablir le rafraîchissement de l'écran.

    Pour setext, c'est très simple, les informations sont dans le registre A pour le caractère et son type, et HL pointe vers les données.

    Pour SETE, c'est un peu plus complexe. Le type de caractère est déterminé par la présence de T ou G, il y a une vérification de la validité du premier paramètre de la commande (numéro de caractère), puis la chaîne de caractères de description, suite de nombres hexadécimaux en ASCII, doit être décodée. C'est la raison pour laquelle la routine est plus longue.

    Cependant, le corps de la routine qui envoie toutes les commandes est identique, à la récupération de la donnée prêt. Et bien que les deux routines utilisent bien les mêmes appels pour envoyer une commande au VDP ou attendre que celui-ci soit prêt à recevoir une commande, il doit bien y avoir une centaine d'octets pouvant être mis en commun.

    Pourquoi ?

    Je n'ai pas d'explication à la duplication de cette routine. Il reste un peu de place inutilisé dans la ROM, et peut être qu'il n'était plus nécessaire d'optimiser la place prise et que c'est juste resté « comme ça ».

    Ce n'est pas, à mon avis, une question de performances avec setext qui serait plus rapide. Elle l'est, en effet, mais pourrait l'être encore plus. En effet, setext garde de SETE le fait de « retourner » chaque octet des descriptions de ligne pour les passer d'une visualisation humaine au format demandé par le VDP. La suite de commandes à envoyer pourraient aussi être rendue plus rapide à l'aide d'un buffer de commandes et l'utilisation de la routine regst.

    Amélioration ?

    Une amélioration possible de la ROM serait de réécrire ces deux routines. Il y aurait peut-être même moyen de profiter de la place gagnée pour caser deux commandes PSET et PRESET, qui font cruellement défaut sur le VG5000µ.


  • Considérations sur le langage LOGO ()

    Quand je vois passer des mentions du langage LOGO, elles sont généralement peu flatteuses. C'est compréhensible, la plupart des personnes qui se souviennent de ce langage de programmation y ont été exposées pendant leurs jeunes années d'études, lors du Plan Informatique pour Tous. Peu sont ceux qui ont creusé plus tard ce qu'ils avaient découverts au moyen d'ordinateurs poussifs et de cours pas toujours maîtrisés par des enseignants pas toujours bien convaincus.

    Mes quelques souvenirs de séances en salle informatiques sont plutôt sur des activités de manipulation du crayon optique sur « Colorpaint ». Je ne sais plus si j'ai fait du LOGO en classe, mais c'est probable. Je me souviens cependant avec netteté la rencontre avec un professeur, lors de la fréquentation d'un « club informatique » pendant des vacances, qui pour une raison ou une autre (mon intérêt enthousiaste à la programmation ?) m'a donné quelques cours de LOGO. Et lors d'une conversation dans laquelle j'avais du mentionner que je programmais en BASIC, il m'a dit cette phrase qui est restée gravée dans ma mémoire « le LOGO est bien plus puissant que le BASIC ».

    Je me souviens que cette phrase m'avait intriguée, mais pas forcément convaincue. Et de retour chez moi, j'ai continué mes aventures en BASIC. J'ai croisé LOGO quelques autres fois, de loin, sans m'y attarder.

    Ce n'est que beaucoup plus tard, quand j'ai commencé à faire de la programmation mon métier, que j'ai repensé à cette phrase. J'ai regardé ce qu'était LOGO de manière plus expérimentée et j'ai compris. J'ai compris la justesse de l'assertion. LOGO était bien autrement plus puissant que le BASIC. Mais d'une manière que je ne pouvais pas comprendre auparavant.

    C'est que la notion de « puissance » appliquée à un langage, ainsi qu'à son environnement, revêt de nombreux aspects. Cela peut être la vitesse d'exécution du programme, la facilité d'écriture, les possibilités d'interactivité avec la machine, la diversité des structures de code et de données.

    Sur des ordinateurs 8 bits, la vitesse d'exécution du BASIC l'emporte haut la main, même sur des BASIC interprétés comme ils l'étaient en majorité.

    Sur la facilité d'écriture, le BASIC est aussi très simple. C'était son but initial. LOGO n'est pas particulièrement complexe à écrire, mais comporte quelques étrangetés syntaxiques et pièges dont je parlerai dans un autre article. Et LOGO est aussi un peu plus verbeux.

    En possibilité d'interaction avec la machine, BASIC l'emporte probablement. Tous les LOGO ne se valent pas, mais l'orientation pédagogique sur les machines 8 bits poussaient les concepteurs à offrir essentiellement des manipulations de l'écran via la tortue, et quelques sons. BASIC, en tant que porte d'entrée de la machine, était souvent dotés des instructions nécessaires pour en démontrer ses capacités. Avec de notables exceptions cependant, mais ceci est une autre histoire.

    Vient ensuite la structure de code et de données. Et c'est là que LOGO renverse BASIC d'une pichenette. Le BASIC de l'époque est très linéaire, son édition se fait encore par numéro de lignes, sans labels. La structuration est possible bien entendu, mais les outils sont maigres. Niveau structure de donnée, le BASIC connaît le tableau multidimensionnel de taille fixe... et c'est tout.

    LOGO arrive avec ses fonctions qui supportent la récursivité, avec des contextes locaux, un nommage des procédures et une édition sans numéro de lignes, qui facilite les mises au point.

    Côté données, LOGO connaît la liste. Et avec une liste dynamique, de nouveaux horizons s'étendent. Des listes de nombres, des listes de mots, des listes de listes, des listes de commandes à exécuter...

    L'expressivité du langage est beaucoup plus intéressante, et les programmes plus concis, plus clairs. C'est en ce sens que LOGO était bien plus puissant.

    Mais tout ceci nécessite de la puissance de calcul et de la mémoire que les machines familiales de l'époque n'ont pas. Et cette malheureuse empreinte qui va rester principalement : on ne fait rien de bien sérieux avec LOGO.

    Pour donner un petit aperçu de LOGO et de son évolution, j'ai réalisé une petite vidéo (moins de 10 minutes) que vous pouvez voir ici.

    (billet posté aussi sur mon blog perso).


  • Récréation 3D, Cartouche Atari 800 ()

    J'avais une cartouche « Atari Writer » qui traînait sur le bureau depuis quelques temps, et je voulais faire un exercice rapide de modélisation avec Blender.

    Cela donne cette image, assez simple, mais qui a été un bon exercice.

    Tortue Jeulin T2


Page 1 / 15 (suivant) »