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 …
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 …
« Petit Computer » est un environnement de programmation qui est apparu initialement sur la Nintendo DSi. Cet environnement se programme dans un dialecte de BASIC du nom de « Smile BASIC ». Et c'est sous le nom « Smile BASIC v4 » qu'il est disponible en version numérique sur l'eShop de la Nintendo Switch.
Après un lancement un peu chaotique en Europe au printemps 2020, à cause d'une erreur dans la classification d'âge il semblerait, il a été à nouveau disponible. Une fois téléchargé et après avoir branché un clavier et une souris en USB sur la Switch, la console se transforme en ordinateur programmable en BASIC. Et c'est amusant !
Un environnement presque à l'ancienne...
Au démarrage, on est pris en main avec des explications sur l'environnement. Les explications ne sont pas très poussées, et même après avoir parcouru le très bavard tutoriel, je pense que des apprentis programmeurs resteront un peu sur leur faim …
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 …
Comment donc les nombres aléatoires sont-ils générés sur un VG5000µ. C'est ce que je vous propose de suivre aujourd'hui en décortiquant le code.
Afin de suivre, il est important de comprendre comment les nombres sont stockés sur VG5000µ, et je vous propose pour cela un petit détour par cet article.
Petit rappel avant de commencer : un générateur de nombres aléatoires est une procédure qui émet une suite de nombres sur un intervalle, cette suite tentant d'avoir des propriétés intéressantes qui donnent l'illusion de l'aléatoire. La suite est cependant parfaitement définie, même si pas toujours simple à suivre, et c'est ce que nous allons voir par la suite.
L'initialisation
Tout commence très tôt pour le générateur de nombres aléatoires. Dès l’initialisation de la machine, une série de valeurs est copiée depuis la ROM vers les variables systèmes. Cela se passe en $1071, juste après l'initialisation de l'affichage.
Suite à l'article précédent, j'ai mis sur le dépôt GitHub un petit utilitaire Python qui reproduit les conversions entre la valeur du nombre et son codage en 4 octets.
Parfois, voir du code est plus simple qu'un long discours.
Et parce qu'on ne sait jamais trop quel sera la vie future du dépôt, voici le code des deux principales fonctions de l'outil.
importmathdefget_byte(number):"""Takes the current number, and returns the next byte encoding it with the reminder of the number to encode. """number*=256result=int(number)returnresult,(number-result)defencode(number):"""Gets a number, returns it's encoded four bytes (memory layout, so exponent at the end)."""# If the number is zero, the encoding is immediate.# In fact, only the exponent has to be 0.ifnumber==0:return[0,0,0,0]# Gets the sign from the number for later encoding …
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.
Dans l'article précédent, on avait vu la création d'une variable dans la zone principale de la mémoire. Cette variable a par défaut un contenu nul, et ne s'occupe pas de savoir si ce contenu est un nombre ou une chaîne de caractères. Les quatre octets de contenus qui suivent les deux octets du nom sont donc tous les quatre à $00.
Pour qu'une valeur soit associée à une variable, il faut une instruction d'assignation, directement via LET (éventuellement de manière implicite), plus indirectement avec une instruction FOR, ou encore plus indirectement par un couple READ/DATA.
Dans tous les cas, la valeur à assigner à la variable est le résultat de l'évaluation d'une expression, c'est-à-dire le résultat d'un calcul numérique ou d'une opération à partir de chaînes.
Afin de comprendre comment sont créées et stockées les chaînes de caractères, c'est donc du côté de l'évaluation d'expression qu'il faut …
Pour terminer cette série sur la gestion de la mémoire par le BASIC sur VG5000µ, j'aborde la manière dont sont stockées les variables en mémoire.
Les variables systèmes
On l'a vu dans l'article sur la cartographie de la mémoire, il y a six variables systèmes intéressantes sur ce sujet :
(vartab) est la première adresse d'une variable. C'est ici que sont stockés les noms des variables numériques (avec leur valeur) ou chaînes (avec un pointeur vers leur contenu),
(arytab) est la première adresse de stockage du contenu des tableaux dimensionnés par DIM (ou bien les tableaux crées par défaut par le BASIC avec un DIM implicite), avec leur nom, leur taille, et leur contenu (ou pointeurs),
(strend) est la première adresse de la zone libre de stockage, tout ce qu'il y a à partir de cette adresse et « au-dessus » jusqu'à la pile (pointée par le registre SP) est la mémoire BASIC …
Peut-être l'avez vous remarqué, certains programmes en BASIC sont construits avec leur programme principal avec des numéros de ligne « hauts » et ont leurs traitements souvent appelés dans les lignes « basses ».
Ainsi, dans l'article précédent sur le listing, le programme commence par un GOTO 10000 et le décodage de ligne est en 1300. À vrai dire, avant que je ne stocke les adresses des tokens dans un tableau, le décodage systématique de leurs noms étaient dans des lignes encore plus basses.
La raison en est toute simple : lorsque le BASIC sur VG5000µ cherche un numéro de ligne, il le cherche systématiquement depuis le début du programme.
C'est où que ça cherche ?
La routine de recherche de ligne est en $2347 et est appelée par GOTO, RESTORE, RENUM (beaucoup !), LIST, AUTO (à chaque nouvelle ligne), NEW, mais aussi lorsqu'il s'agit d'insérer une nouvelle ligne dans le listing au bon endroit …
Un programme en BASIC qui ferait un listing de lui-même. Reprogrammer l'instruction LIST. C'est un peu inutile, mais cela est un prétexte pour expliquer comment le programme est stocké en mémoire dans la machine.
Le VG5000µ utilise une version du BASIC-80 de Microsoft. Ce BASIC se retrouve sur d'autres machines dans des versions variées, mais dont on retrouve les grands principes.
Structure générale d'un programme en mémoire
Comme on l'a vu précédemment, le BASIC respecte un pointeur vers le début du programme qui se nomme (txttab), situé en $488E. Par défaut avec la ROM interne du VG5000µ, si aucune extension ne l'a modifié, (txttab) vaut $49FC au démarrage.
Chaque ligne est composée par les éléments suivants :
Une adresse mémoire (sur 2 octets) vers l'emplacement de la ligne suivante, ou $0000 pour marquer la fin de la liste de lignes,
Un numéro de ligne (sur 2 octets) qui correspond au numéro …
Quand j'ai commencé à étudier les documentations sur le VG5000µ, des incohérences sur certains points me sont apparues. Les différentes sources ne donnaient pas les même renseignements. Ou alors je lisais mal. Mais un truc ne collait pas.
En étudiant la ROM, là encore, ça ne collait pas avec les documentations. Mais le code ne ment pas, j'ai donc débuté une quête de la vérité : que se passe-t-il vraiment ?
... ce n'était peut-être pas si épique, mais bon...
L'anomalie qui m'intéresse aujourd'hui est celle de la gestion de la mémoire par le BASIC. Et voici les pièces qui ont provoqué mon étonnement.
Première pièce
La manuel de l'utilisateur, page 46, indique que le premier paramètre de la commande définit la totalité de l'espace occupé par les chaînes de caractères et doit être compris entre -32768 et 32767. Spécifier une mémoire à réserver négative est un peu étrange, et un test rapide …
Bonjour à tous. Cette année, j'espère pouvoir faire aboutir un projet autour du VG5000µ que j'ai commencé il y a longtemps et que j'avance petit pas par petit pas.
À essayer sur votre ancienne machine sous BASIC préférée. Ça devrait être portable sur à peu près toutes les machines avec interpréteur BASIC et des capactités par trop limitées sur les chaînes de caractères (adieu ZX81...). Sur certains, il faudra ajouter LET aux lignes 10, 20 et 40 pour les assignations de variable.
Après la description de l'utilisation de l'affichage haute-définition du VG5000µ, voici le fichier au format K7 et format WAV qui vous permettra de lancer le programme sur votre propre VG5000µ ou un émulateur.
Au passage, le programme contient la version avec la maison sans l'inversion vidéo qui avait lieu dans l'article précédent.
Avec ce mode d'affichage, on obtient une résolution de 80 pixels horizontalement par 75 pixels verticalement. Ce qui donne un total de 6000 pixels. Cependant, le manuel d'utilisation de la machine indique une définition d'image de 80 000 points. Plus de 13 fois plus. Existe-t-il une façon d'afficher des pixels plus petits et d'atteindre une haute (toute proportion gardée) définition ?
Petit rappel : je ne m'occupe pour le moment que des capacités du VG5000µ offertes par le BASIC, et se mettant à la place d'une utilisation à l'époque, avec juste le manuel de base.
Plus fin
Pour atteindre une définition plus fine depuis le BASIC, il faut …
Maintenant que nous avons un algorithme pour tracer un cercle ainsi que le moyen d'afficher un gros pixel à l'écran, nous voilà prêts pour traduire tout cela en BASIC, comme cela a été fait auparavant avec le segment de droite.
Pour rappel, voici l'implémentation en BASIC d'affichage d'un point à l'écran.
100REM AFFICHE UN POINT DANS L'ESPACE SEMI-GRAPHIQUE110REM X CONTIENT L'ABSCISSE ENTRE 0 et 79120REM Y CONTIENT L'ABSCISSE ENTRE 0 et 74130ZX=INT(X/2):ZY=INT(Y/3)140RX=X-ZX*2:RY=Y-ZY*3150CH=2^(RY*2+RX)160DI=&"4000"+ZY*80+ZX*2170AT=PEEK(DI+1)200OL=64:IF(ATAND128)=128THENOL=PEEK(DI)220IF(OLAND128)=128THENOL=64230POKEDI,OLORCH240POKEDI+1,224250RETURN …
Si vous avez fait un peu de trigonométrie à l'école, vous devez savoir que, pour un angle α allant de 0 à 2π, tracer des points aux coordonnées (cos(α), sin(α)). Ces coordonnées sont multipliées par le rayon et déplacées au centre du cercle. Et c'est comme cela qu'il peut sembler de prime abord intéressant de tracer un cercle.
Cela ressemble à quelque chose comme ça.
initialiser cx et cy avec les coordonnées du centre du cercle, r avec son rayon
initialiser a à 0
tant que \(a < 2\pi\)
\(x = cx + r.cos(a)\)
\(y = cy + r.sin(a)\)
tracer un point en (x, y)
augmenter a
fin
Super simple.
Mais trop simple.
Dans l'algorithme ci-dessus, la ligne qui indique augmenter a n'indique pas de combien il faudrait augmenter a.
Après avoir décrit un algorithme de tracé de segment de droite de manière générique, voyons un peu comment traduire ça en BASIC sur le VG5000µ. Pour ce premier article, il s'agira d'une implémentation simpliste, qui servira de base.
Pour rappel, voici l'algorithme générique :
En entrée, on a deux points de coordonnées (x, y) et (x', y')
Si x' est plus petit que x, échanger les valeurs de x et x' ainsi que de y et y'
Choix de l'octant en fonction de |y' - y| et de |x' - x|
Si on fait un balayage des x, alors
Calculer la pente \(a = \frac{(y' - y)}{(x' - x)}\)
Calculer \(b = \frac{x'y - xy'}{x'-x}\)
Pour tous les x'' de x à x', calculer \(y'' = ax'' + b\).
Tracer un pixel en (x'', y'').
Si on fait un balayage des y, alors
Si y' est plus petit que y, échanger les valeurs de x …
Après avoir vu le point, si nous passions à la ligne ? Ou plus exactement, au segment de droite.
Sur une feuille de papier, tracer un segment de droite entre deux points est une chose
simple : un crayon, un règle, on positionne la règle sur les deux points et l'on trace un
trait en tenant fermement cette dernière.
Le trait a alors une allure continue ; du moment que l'on n'utilise pas un microscope pour
en détailler la composition. Et c'est une représentation suffisante,
dans la plupart des cas, de l'objet mathématique sous-jacent.
Sur un écran d'ordinateur, c'est un tout autre problème. En effet, un écran traditionnel
est constitué d'une matrice de petits carreaux, les pixels. Les pixels des écrans récents
sont très petits et malgré cela, tracer un beau segment de droite n'est pas si simple.
Alors imaginez avec les gros carreaux formant les affichages d'anciens ordinateurs ?
Le BASIC est un langage, dans les versions de l'époque de cette machine, assez limité. Il n'y a entre autre pas de notion de portée lexicale des variables. La portée lexicale d'une variable, c'est l'ensemble des parties d'un code source depuis lesquelles une variable, ou plus généralement un symbole, est accessible. En BASIC sur VG5000µ, comme sur les BASIC d'autres machines de cette période, c'est simple, les variables simples sont accessibles de partout à partir du moment où elles ont été définies.
À cela s'ajoute le fait que seuls les deux premiers caractères d'une variable sont significatifs. ABC et ABD …