Il y a bien quelque chose avec le Z80 qui me ralenti à chaque fois, ce sont les comparaisons. L'égalité, c'est facile. Mais dès qu'il s'agit de comparer deux octets pour savoir si l'un est plus grand que l'autre, strictement ou pas, et encore pire, si ce sont des octets signés, j'y passe du temps... pour souvent me tromper.
Et donc, voici un petit tableau récapitulatif pour m'aider à m'y retrouver, et peut-être que ça pourra aussi vous aider.
Un octet non signé est positif et dans l'intervalle [0, 255]. Après cp b (qui calcule A−B sans stocker le résultat), on a : Z = 1 si A = B et C = 1 si A < B.
C'est à l'été 2017 qu'après avoir commencé à étudier en profondeur le VG5000µ, je me mets en tête de commenter l'intégralité de sa ROM. Il y a bien quelques morceaux de ROM désassemblée qui existent, mais c'est incomplet et surtout, à cause des astuces d'instructions partielles du Z80, pas ré-assemblable à l'identique.
Or, en plus de l'étude, j'aimerais pouvoir modifier la ROM pour faire des essais, voire ajouter des commandes. Je cherche alors un désassembleur Z80 qui puisse aussi ajouter des commentaires et produire un code ré-assemblable. Je n'en trouve pas. Et puis, il y a cette page qui me fait dire que ça ne serait pas si compliqué que ça à faire.
C'est ainsi que je commence à développer un désassembleur Z80, que je nomme alors z80tools, car il n'est alors qu'une partie d'un répertoire d'outils variés permettant d'analyser la ROM. Une partie de ces outils seront extraits …
Si vous aussi vous jetez régulièrement un œil sur les adressages possibles et valides pour le 6502,
voici un tableau récapitulatif qui pourra vous servir. En tout cas, il me servira.
Il y a quelques années, j'avais commencé une suite de tests pour le VG5000µ sous la forme d'un programme en mélange de C et assembleur.
L'idée était double : écrire une référence pour s'inspirer dans la programmation du VG5000µ (et particulièrement de l'EF9345), ainsi que
tester la fidélité des émulateurs.
Je me suis servi de cette base de code pour la première raison et mes besoins personnels. J'ai aussi pu corriger une paire de bugs dans
MAME grâce à la seconde utilisation.
Cependant, cette base de code traîne depuis un moment sur mon disque et je n'y passe pas beaucoup de temps. Alors autant que ça serve
à d'autres, et je mets donc aujourd'hui cette base à disposition.
Je ne sais plus trop dans quel état il est. Ça doit globalement fonctionner. J'imagine.
Ce site est, en tout cas a été jusqu'à maintenant, très machines 8 bits. Du fait des participations aux Retro Programmers United for Obscure Systems, mais aussi aux machines sur lesquelles je m'amuse le plus. L'avantage des machines 8 bits, c'est qu'elles la plupart du temps assez simple à comprendre, à programmer, avec des designs techniques parfois originaux mais qui restent abordables.
Cependant, il y a peu, Zisquier a lancé un site pour aborder le 68000 à travers la programmation en assembleur sur Atari ST. Je m'étais lancé dans l'idée d'essayer d'enseigner l'assembleur de manière ludique, mais comme on dit, « life happens » et je n'ai pas poursuit sur la lancée.
Comme j'étais en pause d'été, que l'Atari ST (STE en réalité) fait partie de mon historique de machines personnelles, et que j'avais quitté cette machine sans en avoir fait le tour je pense, même si j'y avais fait un …
Le scan du clavier, sur Mattel Aquarius, à lieu dans la ROM à l'adresse $1e80. Cette fonction traite les minuscules, les majuscules mais aussi les raccourcis BASIC, en injectant au fur et à mesure les touches nécessaires comme si elles avaient été tapées au clavier.
C'est beaucoup trop pour un scan de clavier dans un jeu, et peut même poser quelques soucis. Mais c'est une bonne base pour écrire une routine de lecture de clavier, car la lecture des valeurs des touches n'est pas forcément très simples.
Ce que nous apprends la lecture de la routine en ROM est qu'il semble falloir attendre une stabilité dans les valeurs avant d'accepter la touche. En effet, la routine fait plusieurs lectures et ne considère la touche appuyée que si cette lecture est stable.
Voici une version de la routine, où j'ai enlever ce qui était traitement de raccourcis BASIC, ainsi que le …
Commençant à étudier le Mattel Aquarius afin de participer à la nouvelle session de « Retro Programmers United for Obscure Systems », et devant le manque de documentation, j'ai regardé ce qu'il y avait dans la ROM. Et c'est un BASIC Microsoft qui y est implémenté, ce qui est bien pratique puisque c'est un BASIC que j'ai bien étudié à travers le VG5000µ.
J'ai donc ressorti ma trousse à outils et voici la liste des points d'entrées des commandes et fonctions du BASIC.
La première colonne est le token BASIC, la seconde l'adresse du point d'entrée en ROM et la troisième le nom.
Les commandes
128 $0c21 end
129 $05bc for
130 $0d13 next
131 $071c data
132 $0893 input
133 $10cc dim
134 $08be read
135 $0731 let
136 $06dc goto
137 $06be run
138 $079c if
139 $0c05 restore
140 $06cb gosub
141 $06f8 return
142 $071e rem
143 …
Encore ?! Oui... encore. Une nouvelle manière de gérer la construction d'un programme VG5000µ. Après la version Sublime Text et z80asm en 2018, puis la version Visual Studio Code et sjasmplus en 2020, je voulais essayer autre chose.
J'avais laissé de côté Sublime Text et z80asm pour deux raisons : le changement de license de Sublime Text que je n'avais pas apprécié, et le côté très simpliste de z80asm, dont je touchais des limites.
Pour un nouveau projet, je voulais utiliser z88dk, un kit de développement pour machines Z80, avec du support C et ASM, ainsi que des bibliothèques standards. Je voulais aussi approfondir ma connaissance du support de toolchains avec CMake.
Alors oui, cmake pour un tout petit projet pour des machines des années 80, ça fait un peu surdimensionné... Je le concède. Et ça n'enlève en rien mon envie de fouiller de ce côté.
Il y a peu, j'ai eu une discussion à propos de l'apprentissage de l'assembleur. La discussion était partie de l'envie d'une personne de créer un jeu sur MSX, mais directement au niveau de la machine, plutôt que de passer par un langage de haut niveau, comme le BASIC natif. Et pourquoi pas. Une donnée importante : la personne en question connaît déjà la programmation, c'est donc un abord de nouveau langage dont on parle, et non des concepts généraux du développement d'un programme.
Lorsque l'on aborde un langage de plus haut niveau, que ce soit BASIC ou Pascal, on va se concentrer sur la manière d'exprimer des concepts dans ce langage en particulier. Lorsque l'on connaît déjà un autre langage de même famille (large), il s'agit même souvent de comprendre quelle sont les particularité du langage appris.
Lorsque l'on aborde une machine en particulier dans un langage de haut niveau, il …
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 …
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 …
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é à …
Lors d'une discussion sur le forum system-cfg à propos de la fonction RND une question a été posée sur le format des nombres dans le VG5000µ. C'est une question qui revient et que je voulais documenter pour mémoire, me posant régulièrement la question et oubliant juste après...
Différents formats
Distinguons déjà deux choses : les nombres manipulés par le système, et les nombres manipulés par le BASIC. Les premiers sont de diverses formes en fonction des besoins, de type entier, signé ou pas, sur 8 ou 16 bits la plupart du temps. Il n'y a pas grand chose à dire sur eux.
Les seconds sont ceux manipulés par le BASIC, qui est un BASIC Microsoft sur VG5000µ, et ce qui sera valide dans cet article le sera pour d'autres machines avec BASIC Microsoft et un processeur Z80. Au moins dans les grandes lignes, mais pour ce que j'ai vu en comparant …
Pour ces quatre nouveaux hooks, je ne suis pas très inspirés. Il s'agit de hook destiné au traitement des entrées sorties. Trois d'entre eux sont appelés lors d'une impression de caractères, le quatrième pour de l'acquisition.
Routines en sorties
Voici les trois premières :
$47DF, prthk : début de commande PRINT.
Est appelé en tant que première instruction de l'exécution de l'instruction PRINT.
À ce moment là, HL pointe vers les arguments de PRINT et le flag Z est à 1 s'il n'y a rien dans ces arguments.
Si vous rendez la main à la routine, elle déroulement l'affichage.
$47E2, outhk : début d'impression de caractère -> pour rerouter vers de nouvelles sorties
Est appelé pour chaque caractère envoyé sur un périphérique de sortie (en $3bd0)
La variable système (prtflg) désigne le périphérique.
Le caractère à afficher est dans le registre A.
Attention, ce caractère est à comprendre par rapport aux modes d'affichage : est-ce …
En premier, l'argument passé à CALL est évalué, puis est ensuite transformé en entier sur 16 bits.
Cette adresse, précédée par l'opcode pour JP à une adresse absolue 16 bits est placée dans le hook calhk. La routine saute enfin vers cette adresse, qui agit comme un tremplin vers l'adresse indiquée à l'instruction CALL.
Comme c'est le RET de la routine appelée qui fera office de RET pour l'ensemble de l'instruction CALL, la préservation de l'environnement est à la charge de la routine …
L'article précédent présentait un accrochage sur un hook d'interruption. Dans cet article, je vais regarder du côté des hooks de commandes de périphériques.
Ces hooks sont initialisés différements des 10 premiers, selon le code qui suit :
L'instruction placée au début de chaque vecteur de redirection est un JP et l'adresse par défaut est celle d'une routine indiquant que le périphérique n'est pas géré.
Il s'agit d'une extension de commandes mise à disposition par le VG5000µ, permettant de traiter les commandes LPEN, MODEM et DISK. Il est d'ailleurs amusant de voir que dans le manuel d'utilisation, ces trois commandes sont mentionnées dans le rappel des instructions reconnues, mais qu'elles ne sont pas décrites.
Dans la table des instructions, le décodage de ces tokens …
Pour cet article, nous allons laisser de côté la partie BASIC-80 pour regarder du côté d'un fonctionnement spécifique au VG5000µ. Pas que le principe soit original, il est présent dans de nombreuses machines, mais que les capacités sont diverses suivant les différentes machines.
Les « hooks » (la traduction de « crochet » me semble un peu hasardeuse, une « accroche » me semble un peu meilleur) est un moyen qu'offre le système pour intervenir lors de certaines opérations en augmentant le fonctionnement de la ROM. En y mettant son grain de sel en quelque sorte.
Plus prosaïquement, les « hooks » sont des appels à des adresses précises, en RAM, à des routines qui peuvent être modifiées. Il peut s'agir aussi sur d'autres machines de récupérer dans une variable système une adresse d'indirection. Sur le VG5000µ, toutes les adresses de « hooks » sont fixes.
Les « hooks » sont parfois aussi appelés vecteurs d'indirection. Ou bien tout simplement vecteurs.
Il y a quelques temps, un message sur le forumConfig.cfg demandait s'il était simple ou même possible d'ajouter des instructions supplémentaires à la ROM d'un VG5000µ. C'est une question que je me posais aussi, avec dans l'idée d'ajouter des instructions graphiques utilisant l'implémentation des articles précédents].
J'ai donc continué à étudier la ROM (que je commence à bien connaître maintenant) à la recherche d'une méthode. Et on va voir que ça n'est pas gagné.
Le parseur
Lorsque la touche RET du clavier est appuyée, il se passe plusieurs choses. Tout d'abord, un caractère NUL (valeur 0) est placé dans le buffer d'entrée à l'emplacement du dernier caractère qui n'est pas un espace (adresses $3c4a à $3c56). Le RET à la fin de cette fonction ramène hors de la boucle principal du traitement interactif.
Après un traitement de la protection de programmes qui n'est pas le sujet ici, une …
À présent que l'on sait diviser par 3, reprenons l'affichage d'un point à l'écran. Pour rappel.
En entrée, nous avons : des coordonnées X et Y, comprises entre 0 et 79 pour X et 0 et 74 pour Y.
En effet de bord, c'est-à-dire en modification de l'état de la machine, nous voulons : le point correspondant à l'écran qui prend la couleur d'encre définie.
Pour cette version, la procédure ne prendra pas d'information de couleur, je me contenterai d'utiliser la couleur d'encre 0 (noir) sur fond 6 (bleu), qui est la combinaison à l'initialisation de la machine.
Les étapes, d'après les articles précédents, sont donc :
À partir de X et Y, trouver les coordonnées du caractère à modifier à l'écran
À partir de X et Y, trouver les coordonnées à l’intérieur du caractère semi-graphique
À partir de coordonnées du caractère, calculer l'adresse mémoire écran correspondante
La méthode de cet article, qui sera le dernier avant de revenir à l'affichage d'un point, va diviser grâce à, globalement, une seule addition. Oui ! Une seule addition.
L'idée
Au tout début de la série d'articles sur la division, j'ai mis en place un système de tests pour m'assurer que mes bouts d'assembleurs faisaient ce qu'il étaient censés faire. Et pour cela, je comparais une série de divisions avec un tableau de résultats.
Mais alors, pourquoi ne pas utiliser un tableau de résultats directement ? On stock quelque part le résultat de toutes les divisions par 3 des nombres entiers de 0 à 255, et on …
Dans le dernier article, j'avais cherché une version plus rapide pour effectuer une division par 3, toujours dans l'optique d'afficher un point à l'écran en transcrivant la routine écrite en BASIC vers de l'assembleur Z80.
Le résultat était mitigé : une routine en moyenne plus rapide et plus stable, mais sur le domaine de définition considéré (le nombre de lignes graphiques du VG5000µ), pas entièrement gagnante.
Dans cet article, je vais donc étudier une autre manière de faire, un peu différente. Alors que les deux premières implémentations permettait de diviser par n'importe quel nombre de 1 à 255, cette nouvelle version est spécialisée dans la division par 3.
Est-ce que cette spécialisation permettra de gagner en performance et/ou en taille ?
L'idée
Le Z80 n'a pas d'instruction pour diviser de manière générale, cela a déjà été mentionné dans les articles précédents. Par contre, il est tout à fait possible de …
Dans le dernier article, j'avais écrit une première manière d'effectuer une division, en spécialisant la routine pour une division par 3 puisque c'est ce qui m'intéresse pour afficher un point à l'écran du VG5000µ.
Pour référence, cette routine nécessitait 15 octets en mémoire et son exécution pouvait prendre jusqu'à 695 cycles, ce qui est plutôt grand.
Micro optimisation
La première optimisation est une micro optimisation. On appelle micro optimisation une amélioration de la routine par un détail de fonctionnement, plutôt que par une réflexion sur l'ensemble de la méthode.
Ici, l'idée est de remplacer le couple push bc / pop bc en début et fin de la routine par un couple exx / exx. L'instruction exx du Z80 effectue un renommage des registresBC, DE et HL. Ces registres (ainsi que d'autres, mais qui ne sont pas touchés par cette instruction) existent en deux exemplaires dans le Z80. Un exemplaire de chaque …
À présent que j'ai un garde fou pour vérifier que je ne fais pas d'erreur d'inattention, me voilà près à diviser des nombres. Pour rappel j'ai besoin de diviser des nombres afin de faire les calculs permettant d'affiche le bon pixel à l'écran.
Pour second rappel, le Z80, au cœur du VG5000µ (et de beaucoup d'autres ordinateurs de l'époque) n'a pas d'instruction de division.
La division
Lorsque je divise de manière entièrea par b, je veux trouver le nombre c tel quel $c * b = a$. Comme la division ne tombe pas toujours juste, j'ai aussi un rester tel que $c * b + r = a$.
Autrement dit, combien de fois dois-je additionner b pour obtenir a (au reste près). Une manière de trouver le résultat est de soustraireb à a autant de fois que l'on peut sans passer sous 0.
Par exemple, $\frac{21}{7}$ se trouve comme ceci …
Après avoir mis en place une vérification (légère) de l'intégrité de la pile, je passe à la vérification de la validité de l'appel d'une fonction.
Le fonctionnement du test est assez simple : je prends une suite de nombres, j'appelle une fonction avec en paramètre chacun de ces nombres, je vérifie que le résultat est conforme à ce que j'attendais.
Par exemple, si je veux tester une fonction diviser par 2 (division entière), je peux utiliser la suite de nombre 0, 10, 32, 255 et comparer les résultats respectifs avec 0, 5, 16, 127 (255 étant impair, le résultat de la division entière est 127, avec un reste égal à 1).
Encore plus simple qu'une division par 2, il y a la fonction identité : celle qui renvoie le paramètre sans le toucher. Tester cette fonction permet de se concentrer sur le développement du test.
Il y a maintenant pas mal de temps, j'avais implémenté, en BASIC, une routine pour afficher un point à l'écran. Puis de là, une routine pour tracer une ligne, puis un cercle. Le constat était que c'était très lent. Le BASIC interprété est déjà plutôt lent de manière générale, et celui du VG5000µ n'est pas particulièrement rapide.
Il y a plusieurs raisons à cela, et ce sera peut-être le contenu d'articles futurs.
Mais en attendant, et après tous les efforts pour se confectionner un environnement de travail pour développer en assembleur, je repars sur l'implémentation du tracé d'un point à l'écran, et cette fois-ci, en assembleur.
L'avantage d'un programme écrit dans un langage de « haut niveau », comme le BASIC, est de simplifier bien des choses. Faire une division d'une variable A par 3 par exemple, peut s'écrire B = A/3. C'est simple, concis, lisible.