Triceraprog
La programmation depuis le Crétacé

Articles avec le tag « Affichage ».

  • VG5000µ, tracer une ligne en BASIC ()

    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 et x' ainsi que de y et y'
      • Calculer la pente \(a = \frac{(x' - x)}{(y' - y)}\)
      • Calculer \(b = \frac{y'x - yx'}{y'-y}\)
      • Pour tous les y'' de y à y', calculer \(x'' = ay'' + b\)
      • Tracer un pixel en (x'', y'')

    Il s'agit à présent de transformer ces instructions en instructions BASIC. Je me base sur l'implémentation de tracé de point publié précédemment, implémenté entre les lignes 100 à 250.

    C'est quelque chose de classique en BASIC de l'époque, les routines, c'est-à-dire des sous-programmes dédiés à une tâche, sont implémentées à des lignes précises. Dans le cas de mon tracé de point, la routine est re-logeable, il est possible de changer les numéros de ligne sans problème.

    Cette routine utilise les variables X et Y en entrée. Elle utilise aussi en interne les variables ZX, ZY, RX, RY, CH, DI, AT et OL. Cela signifie que ces variables, si elles sont utilisées ailleurs dans le programme, verront leur contenu changer à l'appel de la routine.

    C'est, j'en ai déjà parlé, une limitation gigantesque des BASIC de cet époque. Même si, ALGOL, un langage antérieur, avait introduit la portée lexicale des variables, la compartimentation des différentes parties d'un programme n'est pas un acquis lors de la création du BASIC et les versions disponibles sur les ordinateurs familiaux, par soucis d'accessibilité très certainement, ont gardé cette limitation.

    Veillons donc juste à être prudent avec le nom des variables utilisées.


    Préambule

    Je vais implémenter la routine de segment de droite à partir de la ligne 300, soit après l'implémentation du point. Il y a une raison pour cela que j'expliquerai sûrement plus tard.

    La routine commence par une petite documentation du fonctionnement. Ces commentaires sont complètement optionnels mais bien utiles pour les utilisateurs futurs. Voire pour moi, me rappeler plus tard ce que j'avais décidé.

    300 REM AFFICHE UN SEGMENT DE DROITE DANS L'ESPACE SEMI-GRAPHIQUE
    310 REM X1 ET Y1 CONTIENNENT LES COORDONNEES DE DEPART
    320 REM X2 ET Y2 CONTIENNENT LES COORDONNEES D'ARRIVEE
    

    Puis vient l'échange des coordonnées. Comme le BASIC du VG5000µ n'a pas de commande pour inverser le contenu de deux variables (c'est une instruction plutôt rare), il me faut passer par une variable intermédiaire que je nomme TT. On reconnaîtra bien là les pâtés caractéristique des programmes BASIC de l'époque. Plein de lettres et de signes collés.

    330 IF X2<X1 THEN TT=X1:X1=X2:X2=TT:TT=Y1:Y1=Y2:Y2=TT
    

    Il faut ensuite, pour décider de l'octant et donc du balayage, savoir si on est plus de la verticale que de l'horizontale. Pour cela, on peut calculer la valeur absolue de la différence des X (abscisses) et des Y (ordonnées).

    340 AX=ABS(X2-X1):AY=ABS(Y2-Y1)
    

    Vient ensuite la sélection de la sous-routine à utiliser, en fonction de balayage utilisé. L'instruction conditionnelle IF du BASIC du VG5000µ n'a pas de clause ELSE comme sur beaucoup d'autres BASIC. Une solution (il y en a d'autres) est d'écrire les deux choix explicitement.

    Si la condition indiquée après le IF est vérifiée, alors le programme continuera à la ligne indiquée après l'instruction GOSUB. Lorsque l'interpréteur BASIC rencontrera l'instruction RETURN, le déroulement reprendra après le GOSUB.

    350 IF AX >= AY THEN GOSUB 400
    360 IF AX < AY THEN GOSUB 500
    370 RETURN
    


    Balayages

    Reste à écrire les deux balayages différents. Commençons par les X.

    400 A=(Y2-Y1)/(X2-X1)
    410 FOR X=X1 TO X2
    420 Y=INT(0.5+Y1+A*(X-X1))
    430 GOSUB 100
    440 NEXT X
    450 RETURN
    

    Il n'y a pas de fonction d'arrondi disponible. La fonction est émulée en prenant la partie entière, c'est-à-dire sans les chiffres après la virgule, du nombre augmenté de 0.5. La fonction pour obtenir la partie entière est INT.

    La ligne 420 est une simplification du calcul de b dans l'équation de la droite, valide pour les valeurs de x du segment de droite. Je vous laisse faire le calcul.

    Pour le balayage des Y, il faut commencer par échanger les coordonnées si nécessaire.

    500 IF Y2<Y1 THEN TT=X1:X1=X2:X2=TT:TT=Y1:Y1=Y2:Y2=TT
    

    Puis le balayage en lui-même est effectué.

    510 A=(X2-X1)/(Y2-Y1)
    520 FOR Y=Y1 TO Y2
    530 X=INT(0.5+X1+A*(Y-Y1))
    540 GOSUB 100
    550 NEXT Y
    560 RETURN
    

    La ligne 530 comme la 420 est une simplification des calculs.

    Et voilà. Une routine de tracé de segment de droite avec les moyens du bord, en BASIC VG5000µ.

    Petite modification avant le récapitulatif, les lignes 500 et 330 font toutes les deux l'échange des coordonnées. Se répéter en programmation est mauvais signe dans une grande partie des cas. Déplaçons cette partie dans une sous-routine.

    330 IF X2<X1 THEN GOSUB 270
    
    500 IF Y2<Y1 THEN GOSUB 270
    
    270 TT=X1:X1=X2:X2=TT:TT=Y1:Y1=Y2:Y2=TT
    280 RETURN
    


    Récapitulatif

    300 REM AFFICHE UN SEGMENT DE DROITE DANS L'ESPACE SEMI-GRAPHIQUE
    310 REM X1 ET Y1 CONTIENNENT LES COORDONNEES DE DEPART
    320 REM X2 ET Y2 CONTIENNENT LES COORDONNEES D'ARRIVEE
    
    330 IF X2<X1 THEN GOSUB 380
    
    340 AX=ABS(X2-X1):AY=ABS(Y2-Y1)
    350 IF AX >= AY THEN GOSUB 400
    360 IF AX < AY THEN GOSUB 500
    370 RETURN
    
    380 TT=X1:X1=X2:X2=TT:TT=Y1:Y1=Y2:Y2=TT
    390 RETURN
    
    400 A=(Y2-Y1)/(X2-X1)
    410 FOR X=X1 TO X2
    420 Y=INT(0.5+Y1+A*(X-X1))
    430 GOSUB 100
    440 NEXT X
    450 RETURN
    
    500 IF Y2<Y1 THEN GOSUB 380
    510 A=(X2-X1)/(Y2-Y1)
    520 FOR Y=Y1 TO Y2
    530 X=INT(0.5+X1+A*(Y-Y1))
    540 GOSUB 100
    550 NEXT Y
    560 RETURN
    


    Utilisation

    Voici par exemple une utilisation du programme pour tracé un dessin. Sur chacune des lignes, les coordonnées des lignes sont mentionnées puis la sous-routine est appelée. La commande DISPLAY exécutée ensuite sert à forcer le rafraîchissement de l'écran après chaque ligne.

    Comme indiqué précédemment, c'est long, très long. Il faut environ 17 secondes sur la machine pour contempler le résultat. Le VG5000µ nous laisse peu d'options en restant dans le BASIC pour améliorer cela. Nous essaierons cependant une prochaine fois.

    10 GOTO 1000
    
    1000 INIT
    1010 X1=10:Y1=15:X2=35:Y2=5:GOSUB 300:DISPLAY
    1020 X1=60:Y1=15:X2=35:Y2=5:GOSUB 300:DISPLAY
    1030 X1=10:Y1=15:X2=35:Y2=60:GOSUB 300:DISPLAY
    1040 X1=60:Y1=15:X2=35:Y2=60:GOSUB 300:DISPLAY
    1050 X1=60:Y1=15:X2=10:Y2=15:GOSUB 300:DISPLAY
    1060 END
    

    Résultat de tracé de segments


  • Après le point, la ligne ()

    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 ?

    Plutôt que de l'imaginer, vous pouvez même faire une expérience.

    Prenez une feuille à petits carreaux, choisissez un point de départ que vous coloriez en noir, un point d'arrivée que vous coloriez en noir. L'exercice consiste alors à colorier les carreaux intermédiaires afin de tracer un segment de droite.

    Si la réalisation est très simple pour des segments de droite horizontaux, vertiaux ou diagonaux, les droites plus quelconques, elles, demandent un peu plus d'attention afin d'obtenir un beau tracé, équilibré.

    Lignes à carreaux

    Une stratégie peut être de prendre une règle, de tracer un trait fin entre les milieux des deux cases d'extrémités et de colorier ensuite les cases traversées par le trait.

    Lignes à problèmes

    Il y a de l'idée, mais la droite, avec certaines orientations, est épaisse par endroit. Avec des gros pixels, cela ne va pas bien rendre.

    Une autre idée est d'utiliser la forme mathématique d'une droite : \(y = ax + b\). En ajustant correctement a et b et faisant varier x sur les coordonnées de l'écran, on devrait trouver y.

    Coordonnées

    x0123456
    y11,522,533,544,5

    La solution fonctionne bien... jusqu'à ce que a soit supérieur à 1. Au-delà de 1, le tracé du segment à l'écran n'est plus continu, mais comporte des trous !

    Des trous

    Et c'est sans parler du cas où l'on voudrait tracer un segment vertical. En effet, avec l'idée de faire varier les x, pour un segment vertical, x ne prend qu'une valeur. Et donc y une seule valeure aussi. Ce qui donne... un beau point. C'est normal puisqu'à une valeur de x on associe une et une seule valeur de y.

    Cette manière de tracer des segments de droite semble ne pas fonctionner, et pourtant, c'est première idée n'est pas si mauvaise. Elle nécessite juste un petit ajustement.

    Puisque la méthode de balayage des x fonctionne sur les segments de droites allant d'une pente nulle, l'horizontale, à une pente de 1, la diagonale, peut-être faut-il changer un peu d'angle pour les segments de droite allant de la diagonale à la verticale ?

    Pour cette partie-là, est-ce qu'il ne suffirait pas de poser : \(x = a'y + b'\) ? C'est-à-dire balayer les y pour obtenir les x, avec des coefficients ajustés en conséquence.

    Coordonnées

    y0123456
    x22 + 1/32 + 2/333 + 1/33 + 2/34

    Plus de trous

    Ça fonctionne plutôt bien.

    Par un jeu de symétries, le problème peut être résolu en séparant l'espace autour du point de départ en huit espaces qui induiront l'utilisation d'un balayage des x ou des y. Ces secteurs étant au nombre de huit, on les appelle des octants.

    Pour simplifier le problème, on peut limiter les cas à deux d'entres eux en prenant pour extrémité de départ le point le plus à gauche sur l'axe des x, par exemple. Il reste alors à considérer dans lequel des quatre octants à la droite de ce point le segment va être tracé, et choisir entre le balayage des x ou le balayage des y.

    Octants

    Pour décider de la méthode à appliquer, on peut commencer par calculer la pente de la droite. Si les deux points sont (x, y) et (x', y'), alors la pente de la droite est \(\frac{(y' - y)}{(x' - x)}\), le a dans \(y = ax + b\).

    Cependant, en cas de droite verticale, on se retrouve avec une division par 0 (x' étant égal à x). Si la droite est presque verticale, on peut aussi se retrouver avec des valeurs assez grandes et des problèmes de capacités de calcul de la machine.

    En fait, il n'est pas nécessaire de calculer la pente, car ce que l'on cherche à savoir où se situe la droite portant le segment par rapport à une horizontale, une verticale et une diagonale.

    Et pour cela, il suffit d'une part de comparer les valeurs absolues de \((y' - y)\) et de \((x' - x)\). Si dans le cas d'un balayage des y, on part du point le plus bas (tout comme on part du point le plus à gauche pour le balayage des x), alors les secteurs sont utilisés de cette manière :

    Décision


    Un premier algorithme

    J'obtiens donc une première idée d'algorithme qui ressemble grossièrement à ça :

    • 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 et x' ainsi que de y et y'
      • Calculer la pente \(a = \frac{(x' - x)}{(y' - y)}\)
      • Calculer \(b = \frac{y'x - yx'}{y'-y}\)
      • Pour tous les y'' de y à y', calculer \(x'' = ay'' + b\)
      • Tracer un pixel en (x'', y'')


    Voici un algorithme de tracé de segment de droite qui fonctionne. Mais qui est aussi extrêmement lent. Dans un prochain article, nous verrons comment rendre ça sur le VG5000 en BASIC.


  • VG5000µ, SetPoint en BASIC, l'implémentation ()

    Ça y est, après avoir vu la structure des caractères semi-graphiques, l'arrangement en mémoire des valeurs d'affichage et fait un essai de manipulation de ces valeurs, nous voici prêts pour écrire un morceau de programme qui peut prendre en entrée des coordonnées et allumer ou éteindre un point à l'écran.

    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 désignent les mêmes variables. Cela limite fortement la possibilité d'utiliser des noms de variables intéressants.

    Deux caractères significatifs pour les variables

    Cette aparté terminée, revenons à la définition du problème.


    Modélisation du problème

    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.

    Dans un premier temps, 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
    • Récupérer les valeurs pour la paire d'adresse mémoire
    • Si le caractère présent n'était pas un caractère semi-graphique standard, considérer qu'il était complètement éteint (valeur 0 pour le caractère)
    • Modifier la valeur du caractère récupéré en fonction des coordonnées à l'intérieur du caractère semi-graphique
    • Modifier la mémoire écran avec les nouvelles valeurs


    Prenons un exemple avec les coordonnées (2, 7) que j'avais déjà utilisées dans l'article sur les changements de repère. Les coordonnées du caractères sont (0, 2) et les coordonnées dans le caractères sont (0, 1).

    À partir des coordonnées (0, 2), je trouve que l'adresse mémoire est &"4000"+160. Une paire de PEEK me donne les valeurs déjà présentes 128 et 230. Cela signifie qu'il y a un caractère graphique étendu à cet endroit. Je le considère donc comme un caractère éteint, de valeur 0.

    À partir des coordonnées (0, 1), j'obtiens pour valeur de caractère 64+4 = 68, qui sera donc la première valeur.

    La seconde valeur, en fonction des couleurs sera 224. Une paire de POKE avec ces valeurs allumera le bon point.

    Des peeks

    Maintenant, que se passe-t-il si un caractère graphique est déjà présent ? Pour vérifier cela, je vais allumer le point aux coordonnées (3, 7). Cela donne la paire de coordonnées (0, 2) et (1, 1). C'est-à-dire les mêmes emplacements mémoires mais avec un point différent dans le caractère semi-graphique.

    Les valeurs récupérées sont donc 68 et 224, celles que je viens de calculer et mettre en mémoire avec POKE. 224 reste identique, puisque les couleurs sont fixes. Le numéro de caractère, lui, doit être changé. Le nouveau gros pixel à allumer est celui de valeur 8. Je peux donc ajouter 8 à 68 pour donner 76. Et cela marcherait.

    Mais c'est faux dans le cas général.

    L'addition ne fonctionne pas. Si j'allume deux fois le même gros pixel, il ne faut pas ajouter le numéro de ce pixel, puisqu'il est déjà pris en compte.

    L'opération qu'il faut utiliser ici est une opération OU. Je ne m'étendrai pas dessus ici. Retenez juste que faire un OU entre le numéro du caractère actuel et le numéro associé au pixel revient à ajouter ce numéro associé uniquement s'il n'est déjà pas pris en compte par le caractère.

    En BASIC, le OU est écrit OR. 68 OR 8 donne 76. Parfait.

    Des pokes

    Voici le programme en BASIC, commenté, qui reprend ces opérations.

    100 REM AFFICHE UN POINT DANS L'ESPACE SEMI-GRAPHIQUE
    110 REM X CONTIENT L'ABSCISSE ENTRE 0 et 79
    120 REM Y CONTIENT L'ABSCISSE ENTRE 0 et 74
    130 ZX=INT(X/2):ZY=INT(Y/3)         : REM Calcul des coordonnées écran (ZX, ZY)
    140 RX=X-ZX*2:RY=Y-ZY*3             : REM Calcul des coordonnées dans le caractère (RX, RY)
    150 CH=2^(RY*2+RX)                  : REM Calcul du numéro de pixel associé
    160 DI=&"4000"+ZY*80+ZX*2           : REM Calcul de l'emplacement mémoire écran
    170 AT=PEEK(DI+1)                   : REM Récupération de la valeur d'attribut
    180 REM Si c'est un caractère graphique, la valeur du caractère est récupérée dans OL
    190 REM Sinon, OL reste à 64
    200 OL=64:IF (AT AND 128) = 128 THEN OL=PEEK(DI)
    210 REM Si le caractère était étendu, OL est remis à 64
    220 IF (OL AND 128) = 128 THEN OL=64
    230 POKE DI,OL OR CH                : REM Le caractère est augmenté de la nouvelle valeur
    240 POKE DI+1,224                   : REM Les couleurs sont fixes
    250 RETURN                          : REM Fin de la routine, retour à l'appelant
    

    Je ne m'étendrai pas non plus ici sur le AND en ligne 200. Retenez que cette opération vérifie si le bit de rang 7, qui décrit si le caractère est semi-graphique, est à 1. De même sur la ligne 200, où je vérifie si le bit de rang 7, qui décrit si le caractère est étendu, est à 1.

    Et voici comment peut-être utilisé ce programme. X et Y sont initialisés avec les coordonnées, puis la routine en ligne 100 est appelée avec GOSUB.

    10 INIT
    20 X=20:Y=10:GOSUB 100
    21 X=21:Y=10:GOSUB 100
    22 X=19:Y=11:GOSUB 100
    23 X=19:Y=12:GOSUB 100
    24 X=22:Y=11:GOSUB 100
    25 X=22:Y=12:GOSUB 100
    26 X=20:Y=13:GOSUB 100
    27 X=21:Y=13:GOSUB 100
    30 END
    

    Tracer des images, ou tout simplement des objets géométriques de cette façon serait très fastidieux. On pourra améliorer ça dans un futur article.

    Le résultat

    Le listing


  • VG5000µ, SetPoint en BASIC, manipulation de gros pixels ()

    Dans l'épisode précédent, j'avais décrit l'arrangement en mémoire vidéo des caractères semi-graphiques et un moyen d'accéder directement en mémoire à celui qui nous intéresse.

    Le but est toujours de pouvoir allumer un élément constitutif d'un caractère semi-graphique, que je nomme « gros pixel ». Et l'article précédent s'arrêtait au moment où je pouvais récupérer, via une paire d'appels à commande PEEK, les deux valeurs décrivant le caractère affiché à l'écran.

    Premier test, après avoir effacé l'écran avec INIT, j'affiche les deux valeurs correspondantes à la colonne 1, ligne 0.

    Peek écran

    Les deux valeurs obtenues et visibles sur la capture d'écran sont 79 et 0. Pour comprendre ce qu'ils signifient, je reprends ici un morceau de l'article sur l'arrangement de la mémoire vidéo qui décrit la structure de ces deux valeurs.

    On y lit que la première valeur a cette structure :

    Bit76543210
    ContenuN (0)/E (1)numéro du caractère

    Et que la seconde a celle-ci :

    Bit76543210
    ContenuTX (0)Inverse vidéoLargeur x2Hauteur x2ClignotementCouleur
    ContenuGR (1)Couleur de fondClignotementCouleur du caractère

    Compter jusqu'à deux

    Pour comprendre ce que ces tableaux signifient, il faut savoir et comprendre que les valeurs que j'ai obtenues et affichées le sont en base 10, c'est-à-dire sous la forme sous laquelle nous comptons usuellement dans la vie de tous les jours. En base 10, il y a 10 chiffres, et 0 à 9, et les nombres sont composés de ces chiffres. 79 est composé du chiffre 1 en position des dizaines et 6 en position des unités.

    C'est d'ailleurs au programme du primaire à l'école, où l'on apprends que 79, c'est 1 dizaine et 6 unités. Autrement dit, 1 × 10 + 6. De même, 0 est composé de 2 centaines, de 0 dizaines et de 5 unités, autrement dit 2 * 100 + 0 * 10 + 5.

    Ces 10 chiffres sont arbitraires. On aurait pu, et d'autres civilisations l'ont fait, avoir un système de comptage avec 20 chiffres. Pourquoi pas. En programmation, il est usuel de compter en base 10, bien entendu, mais aussi en base 8, donc en ne prenant que les chiffres de 0 à 7, en base 16, où nous avons besoins de chiffres supplémentaires au-delà du 9 (qui sont notés A, B, C, D, E et F) et... en base 2, où seuls les chiffres 0 et 1 sont utilisés.

    Le pourquoi de ces bases sort du cadre de cet article. J'y reviendrai probablement une autre fois. Je ne vais pas non plus expliquer ici les conversions d'écritures entre ces bases.

    Ces chiffres, 0 et 1 dont on va se servir se nomment chiffres binaires, en anglais Binary Digits, raccourcis en bit. C'est ce bit qui est mentionné dans les tableaux ci-dessus.

    Les valeurs que nous avons obtenues sont, de part l'organisation de l'ordinateur, des valeurs entre 0 et 255, et il se trouve que ces valeurs peuvent toutes s'écrire avec 8 chiffres binaires. Cette organisation en groupe de 8 bits s'appelle en octet.


    On reprend son souffle

    Cela fait beaucoup de vocabulaire d'un coup. Alors reprenons. Les valeurs que nous avons obtenues avec PEEK :

    • Sont toujours comprises entre 0 et 255,
    • Peuvent s'écrire en base 2 avec 8 bits (chiffres binaires) maximum,
    • Ces groupements de 8 bits, s'appellent des octets.

    À quoi ressemblent ces deux valeurs écrites en base 2 ? Les voici :

    • 79 (en base 10), s'écrit 01001111 (en base 2)
    • 0 (en base 10), s'écrit 00000000 (en base 2)


    Tout comme en base 10, le 0 en tête du 01001111 est optionnel, mais je les garde ici pour bien faire la différence entre les deux notations, et pour montrer que ces deux valeurs écrites en binaires s'associent parfaitement aux cases des tableaux ci-dessus.

    Dernier petit détail, dans les tableaux, le rang du chiffre binaire commence par le rang 0, à droite, puis va jusqu`au rang 7, à gauche. Cela donne bien 8 chiffres/colonnes.


    Décodage, enfin

    On a maintenant tous les éléments pour comprendre les deux valeurs récupérées au début de cet article. Le premier octet, de valeur 79, écrit en base 2 et remis en place dans le tableau de décodage, donne ceci :

    Bit76543210
    Contenu01001111

    Le second octet, de valeur 0, mis en base 2 et placé dans le tableau de décodage donne ceci :

    Bit76543210
    Contenu00000000

    Ce qui se lit :

    • Première valeur : il s'agit d'un caractère non étendu (bit 7 vaut 0) dont le numéro est 79
    • Seconde valeur : il s'agit d'un caractère textuel, de hauteur et largeur normale, sans clignotement, et de couleur 0 (noir)

    Un rapide coup d’œil sur la table de caractère nous renseigne que le caractère normal (non étendu) de rang 79 est le caractère O. Nous avons bien lu le O de Ok! en haut de l'écran.


    Et maintenant, l'encodage

    La commande pour modifier la valeur d'un emplacement mémoire, c'est-à-dire l'inverse de PEEK, est POKE. Cette commande prend un numéro d'emplacement mémoire et une valeur pour se charger de mettre cette valeur à cet emplacement mémoire.

    Exemple rapide en faisant POKE &"4000"+2,80 pour voir que le O de Ok! se transforme en P.

    Poke écran

    Ce que je voudrais maintenant, c'est utiliser des caractères semi-graphiques pour tracer une ligne horizontale au milieu de l'écran. En reprenant la méthode décrite dans l'article sur la constitution des caractères semi-graphiques, je calcule que les deux pixels du milieu allumés donnent le caractère numéro 76, ce que je peux vérifier sur le tableau des caractères semi-graphiques.

    D'après le calcul pour trouver l'emplacement mémoire du milieu de l'écran, ligne 12, colonne 0, j'obtiens 960, que je devrai ajouter à l'adresse de début de la mémoire vidéo &"4000".

    Dans les 20 paires de valeurs à partir de cet emplacement, je dois placer le caractère semi-graphique numéro 76 (1001100 en binaire sur 7 bits) non étendu, sans clignotement, avec comme couleur de fond la couleur 6 (110 en binaire sur 3 bits) et la couleur d'encre 0 (000 en binaire sur 3 bits), qui sont les couleurs par défaut à l'allumage du VG5000µ (INIT 6).

    Cela donne dans les tableaux d'encodage (qui sont toujours les mêmes) :

    Bit76543210
    Contenu01001100
    Bit76543210
    Contenu11100000

    Soit, une fois convertis en base 10, les valeurs 76 et 224.

    Et enfin, le programme

    10 INIT 6           : REM On efface l'écran
    20 P=&"4000"+960    : REM La variable P est positionnée en début de ligne 12
    30 FOR X=1 TO 40    : REM On effectue 40 fois les instructions suivantes
    40 POKE P,76        : REM La première valeur est 76
    50 POKE P+1,224     : REM La valeur suivante est 224
    60 P=P+2            : REM La variable P est avancée de 2, afin de traiter la paire suivante
    70 NEXT X           : REM Fin des instructions à répéter 40 fois
    

    Voici une belle ligne horizontale à base d'un caractère semi graphique choisi et positionné grâce à une série de calculs. Il n'y a plus long avant de pouvoir choisir directement quel point de l'écran afficher !

    Poke écran


  • VG5000µ, SetPoint en BASIC, changements de repères ()

    Après avoir fait un mini tour des commandes graphiques, puis étudié la structure des caractères semi-graphiques du VG5000µ, il est temps d'implémenter la possibilité d'allumer un point particulier de l'écran.

    Comme je vais utiliser les caractères semi-graphiques, ce « point » sera sur la grille de résolution de 80 par 75. En effet, il y a 40 caractères par ligne qui ont chacun une largeur de deux éléments, et 25 caractères par colonne qui ont chacun trois éléments en hauteur.

    L'idée pour allumer ou éteindre un point est donc d'abord de trouver quelle position de caractère semi-graphique est influencée, puis de modifier le caractère à cette position. Pour modifier le caractère, il faudra lire celui déjà présent, calculer le nouveau, puis écrire le nouveau.

    Transformation de coordonnées

    Pour visualiser la transformation de coordonnées, voici un tableau.

    |-------------|-------------|-------------|-------------|-------------|
    | (0,0) (1,0) | (1,0) (2,0) | (3,0) (4,0) | (5,0) (6,0) | ......
    | (0,1) (1,1) | (1,1) (2,1) | (3,1) (4,1) | (5,1) (6,1) | ..... etc.
    | (0,2) (1,2) | (1,2) (2,2) | (3,2) (4,2) | (5,2) (6,2) | ....
    |    [0,0]    |    [1,0]    |    [2,0]    |    [3,0]    | ....
    |-------------|-------------|-------------|-------------|------
    | (0,3) (1,3) | (1,3) (2,3) | (3,3) (4,3) | (5,3) (6,3) |
    | (0,4) (1,4) | (1,4) (2,4) | (3,4) (4,4) | (5,4) (6,4) |
    | (0,5) (1,5) | (1,5) (2,5) | (3,5) (4,5) | (5,5) (6,5) |
    |    [0,1]    |    [1,1]    |    [2,1]    |    [3,1]    |
    |-------------|-------------|-------------|-------------|---
    | (0,6) (1,6) | (1,6) (2,6) | (3,6) (4,6) | (5,6) (6,6) |
    | (0,7) (1,7) | (1,7) (2,7) | (3,7) (4,7) | (5,7) (6,7) |
    | (0,8) (1,8) | (1,8) (2,8) | (3,8) (4,8) | (5,8) (6,8) |
    |    [0,2]    |    [1,2]    |    [2,2]    |    [3,2]    |
    |-------------|-------------|-------------|-------------|-
    |   .     .   |   .     .   |   .     .   |   .     .   | 
    |   .     .   |   .     .   |   .     .   |   .     
    |   .     .   |   .     .   |   .     .
    

    Entre parenthèse, j'ai placé tous les couples de coordonnées qui nous intéressent. Entre crochet, j'ai placé les coordonnées du caractère correspondant.

    Pour passer du premier système de coordonnées à l'autre, nous utilisons un outil mathématique très simple : la division. Prenez une coordonnée « gros pixel », divisez par 2 sa coordonnées X, divisez par 3 sa coordonnée Y (en arrondissant à l'entier inférieur), et vous obtenez les coordonnées du caractère semi-graphique à modifier.

    Par exemple : (2, 7) devient (2÷2, 7÷3) ce qui fait (0, 2). Sept divisé par trois de manière entière faisant 2.

    À présent que l'on sait trouver les coordonnées du caractère semi-graphique, il nous faut trouver lequel des six éléments est à modifier. Et pour cela, nous utilisons encore la division entière, mais cette fois en prenant son reste.

    En continuant sur le même exemple : (2, 7) devient (reste de 2÷2, reste de 7÷3), ce qui fait (0, 1). C'est-à-dire l'élément de la colonne de gauche (0), sur la ligne du centre (1).

    Pour transformer l’élément semi-graphique aux coordonnées (2, 7), il faudra donc allumer l'élément semi graphique de la colonne de gauche, ligne du centre, du caractère positionné aux coordonnées (0, 2).

    Ce que nous venons de faire est un changement de repère.

    Voici le même tableau avec l'élément exemple mis en évidence :

    |-------------|-------------|-------------|-------------|-------------|
    | (0,0) (1,0) | (1,0) (2,0) | (3,0) (4,0) | (5,0) (6,0) | ......
    | (0,1) (1,1) | (1,1) (2,1) | (3,1) (4,1) | (5,1) (6,1) | ..... etc.
    | (0,2) (1,2) | (1,2) (2,2) | (3,2) (4,2) | (5,2) (6,2) | ....
    |    [0,0]    |    [1,0]    |    [2,0]    |    [3,0]    | ....
    |-------------|-------------|-------------|-------------|------
    | (0,3) (1,3) | (1,3) (2,3) | (3,3) (4,3) | (5,3) (6,3) |
    | (0,4) (1,4) | (1,4) (2,4) | (3,4) (4,4) | (5,4) (6,4) |
    | (0,5) (1,5) | (1,5) (2,5) | (3,5) (4,5) | (5,5) (6,5) |
    |    [0,1]    |    [1,1]    |    [2,1]    |    [3,1]    |
    |=============|-------------|-------------|-------------|---
    I             I (1,6) (2,6) | (3,6) (4,6) | (5,6) (6,6) |
    I       (1,7) I (1,7) (2,7) | (3,7) (4,7) | (5,7) (6,7) |
    I             I (1,8) (2,8) | (3,8) (4,8) | (5,8) (6,8) |
    I    [0,2]    I    [1,2]    |    [2,2]    |    [3,2]    |
    |=============|-------------|-------------|-------------|-
    |   .     .   |   .     .   |   .     .   |   .     .   | 
    |   .     .   |   .     .   |   .     .   |   .     
    |   .     .   |   .     .   |   .     .
    

    Transformation du caractère

    À présent que l'on connaît les coordonnées du caractère à modifier, il faut trouver un moyen de savoir quelle est sa valeur actuelle. Et là-dessus, le BASIC du VG5000µ ne nous aide pas. Il y bien une commande d'affichage à l'écran, PRINT, mais pas de commande spécifique pour connaître le caractère actuellement affiché à l'écran pour une position donnée.

    Heureusement, cet article décrit l'arrangement en mémoire des informations d'affichage telles que gérées par le BASIC. Il est donc possible, moyennant un autre changement de coordonnées et de l'instruction PEEK du BASIC, d'obtenir cette information.

    L'instruction PEEK prend en paramètre une adresse mémoire et renvoie son contenu sous la forme d'un octet, c'est-à-dire, d'un entier allant de 0 à 255. C'est une instruction, avec son pendant POKE qui permet de modifier le contenu d'une adresse mémoire, qui permet au BASIC d'interagir avec la machine au delà des instructions toutes faites. Nous y reviendrons dans d'autres articles.

    En lisant l'article sur l'arrangement mémoire des informations écran, il est facile de convertir les coordonnées d'un caractère à l'écran en adresse mémoire. Chaque ligne prend 80 octets, il faut donc multiplier le numéro de la ligne par 80. Et chaque caractère prend deux octets sur une ligne, il faut donc multiplier le numéro de la colonne par 2.

    Dans notre exemple, (0, 2) donne (0×2)+(2×80) = 160. L'emplacement est donc le 160ième depuis le début de la mémoire vidéo. Plus exactement 160ième et 161ième puisque chaque caractère prend deux octets. Encore un changement de repère.

    Il reste donc à décoder cette information, la modifier et la replacer en mémoire. Ceci sera pour la prochaine fois.

    Repère cartésien


« Page 2 / 3 »