ASMtrad CPC

Apprenez l'assembleur Z80

Lire un texte:

Dans ce cour, nous allons voir comment lire un texte et ensuite trouver quelles lettres de notre fonte correspondent à celui-ci.
Le but de l'exercice est d'une part de vous apprendre à pointer sur des données, mais aussi d'apprendre quelques opérations utiles.

Stocker un texte:

Pour stocker un texte l'assembleur vous permet de dire que vous voulez stocker les valeurs ASCII.

Mais que sont les valeurs ASCII ?
Et bien c'est une norme. Norme pour laquelle une lettre correspond à une valeur.
Bien qu'il y ait des variantes, grosso-modo les lettres de l'alphabet ne changent pas de numéro et seuls les caractère spéciaux peuvent être différents.

Vous trouverez ICI la table des caractères ASCII.

En dessous de votre caractère vous avez des nombres. Par exemple 32 pour l'espace ou 65 pour le "A".
Ces nombres sont les numéros du caractère ASCII.

Nous allons nous en servir pour choisir notre caractère et afficher le sprite en fonction.
Pour cela il faut donc stocker votre texte en ASCII et il faut donc dire à l'assembleur de stocker le texte comme tel.

Nous allons donc utiliser l'instruction DEFM (où DM pour certains assembleur)

La syntaxe est simplement: DEFM "Message texte"

Essayons:

                            ORG         #8000
                            DEFM        "HELLO WORLD"

On assemble et on regarde en RAM comment ca a été stocké:

hello world

#48 si on regarde le numéro ASCII correspond bien au "H"; de même #45 correspond bien au "E"
C'est donc bon, notre texte est bien stocké en numéro ASCII.

Notez que certains assembleur acceptent aussi l'instruction DM "", voir même simplement DB ""...

2 - Lire le texte:

Il nous faut donc utiliser ce chiffre pour trouver dans notre fonte où est le sprite à afficher.
Puisque nous avons stocké les sprites dans le même ordre que les caractères ASCII ca va être facile.

Collons un label avant notre texte:

                            ORG         #8000
                LETEXTE     DEFM        "HELLO WORLD"

Maintenant il va nous falloir lire le texte. On va mettre dans HL l'adresse ou il se trouve:

                            ORG         #8000
                            LD          HL,LETEXTE      ;dans HL on a l'adresse du texte
                            RET                         ;on met dès maintenant notre RET de fin histoire de pas l'oublier

                LETEXTE     DEFM        "HELLO WORLD"

Maintenant, nous devons lire l'octet contenu à l'adresse contenu dans HL.
Pour cela nous avons l'instruction LD A,(HL)

C'est assez intuitif en fait, on comprend en lisant l'instruction, que A contiendra l'octet contenu à l'adresse HL (puisque HL est entre parenthèses)
Retenez bien ce principe: Si c'est entre parenthèse alors cela concerne l'octet contenu à l'adresse entre parenthèse.

                            ORG         #8000
                            LD          HL,LETEXTE      ;dans HL on a l'adresse du texte
                            LD          A,(HL)          ;on met dans A l'octet contenu à l'adresse HL
                            RET                         ;on met dès maintenant notre RET de fin histoire de pas l'oublier

                LETEXTE     DEFM        "HELLO WORLD"

A contient donc dans notre exemple le numéro ASCII de "H"

Maintenant, il faut déterminer la lettre dans notre fonte.
Afin de faire cela on va déjà soustraire 32 de notre numéro ASCII.

Pourquoi ? Regardez la fonte système...
Comme je l'ai dit tout à l'heure, le premier caractère de la fonte system a pour numéro 32 (en décimal sinon #20 en hexa).
Si on enlève 32, l'espace aura pour numéro 0... "!" aura pour numéro 1; '"' aura pour numéro 3; "#" aura pour numéro 4 et ainsi de suite.

C'est une bonne astuce,car si on prend l'adresse de notre fonte et qu'on ajoute 0, on sera bien sur le premier caractère, à savoir l'espace !!!

Pour passer au caractère suivant, il suffit de multiplier le numéro de la lettre (moins 32) par la longueur en octets d'une lettre de notre fonte.

Par exemple, imaginons que notre texte soit " !" soit un espace puis un point d'exclamation.
Quand on a soustrait 32 on obtient donc 0 et 1 comme numéro de nos caractères.

Admettons que dans notre fonte, un caractère fait 64 octets de longueur (4 octets de large*16 lignes) et que notre fonte soit en #4000.
Nous avons donc en #4000 l'espace et en #4000+64 le point d'exclamation.

Il faut donc multiplier le numéro de la lettre par la longueur d'une lettre dans la fonte (si les lettres font toutes la même largeur et hauteur bien entendu).

Pour soustraire 32 du numéro de notre lettre, nous allons utiliser l'instruction: SUB val
Val correspond à la valeur à soustraire.
Cette instruction fonctionne sur 8 bits (pour info elle prend 2 NOPS) et comme je l'ai déjà dit, toute opération sur 8 bits passe par A.
Ainsi, l'instruction SUB val enlève "val" à A.

                            ORG         #8000
                            LD          HL,LETEXTE      ;dans HL on a l'adresse du texte
                            LD          A,(HL)          ;on met dans A l'octet contenu à l'adresse HL
                            SUB         32              ;on soustrait 32 au contenu de A.
                            RET                         ;on met dès maintenant notre RET de fin histoire de pas l'oublier

                LETEXTE     DEFM        "HELLO WORLD"

Jusque la, c'est pas bien compliqué.

On obtient donc dans A notre numéro de lettre que l'on doit multiplier par la longueur d'une lettre dans la fonte.
Sauf que...

En assembleur Z80, nous n'avons aucune instruction de multiplication...

Vous vous dites certainement que ce n'était pas la peine de vous parler de tout cela si ce n'est pas faisable !!!
Mais bien entendu c'est faisable et vos cours de mathématique de classe de CP (vous voyez tout de même que je ne vous en demande pas trop) vont vous servir :)

En effet, qu'est ce qu'une multiplication sinon une addition ?

Multiplier un nombre par 2 revient à l'additionner avec lui même.
2*2=4 mais 2+2=4 aussi...

Pour multiplier par 4 il suffit de faire 2 additions:
2*4=8 c'est: 2+2=4 puis 4+4=8 !!!

Ca marche assez bien tant qu'on reste dans des multiples de 2.

On va utiliser l'instruction ADD HL,HL pour faire nos additions.

ADD HL,HL additionne HL avec lui même et le résultat est mis dans HL.

Exemple (ca sera plus clair):

                            LD          HL,2            ;on met 2 dans HL
                            ADD         HL,HL           ;HL=HL+HL ...  2+2=4 on a donc multiplié par 2
                            ADD         HL,HL           ;HL=HL+HL ...  4+4=8 on a donc multiplié par 4
                            ADD         HL,HL           ;HL=HL+HL ...  8+8=16 on a donc multiplié par 8
                            ADD         HL,HL           ;HL=HL+HL ...  16+16=32 on a donc multiplié par 16
                            ...

Et ainsi de suite.

Si notre lettre dans la fonte fait 4 octets de large pour 16 lignes, une lettre fait donc 4*16=64 octets de longueur.

Pour obtenir une multiplication par 64, il nous faudra donc faire 6 additions:

                            ADD         HL,HL           ;*2
                            ADD         HL,HL           ;*4
                            ADD         HL,HL           ;*8
                            ADD         HL,HL           ;*16
                            ADD         HL,HL           ;*32
                            ADD         HL,HL           ;*64

Mais bon, avant de faire notre addition, encore faudrait-il que notre numéro de lettre soit dans HL... Pour cela rien de plus simple.

Vu que notre numéro est dans A, il suffit de recopier A dans L !!!

L c'est le poids faible de la valeur 16 bits (valeur de 0 à 255). Ne vous trompez pas en recopiant A dans H car pour le coup H est une valeur supérieure à 255...).

Pour recopier: un LD r8bits,r8bits suffira...
LD L,A recopie A dans L. Rappelez vous que le premier registre après l'instruction est toujours celui qui reçoit quelque chose.

Mais, n'oublions pas de mettre 0 dans H, car il n'est peut être pas nul... Et pour nous, il faut qu'il le soit !!!

On reprend notre routine:

                            ORG         #8000
                            LD          HL,LETEXTE      ;dans HL on a l'adresse du texte
                            LD          A,(HL)          ;on met dans A l'octet contenu à l'adresse HL
                            SUB         32              ;on soustrait 32 au contenu de A.
                            LD          L,A             ;on met A dans L
                            LD          H,0             ;on met 0 dans H
                                                        ;HL contient le numéro de la lettre
                                                        ;on peut passer à la multiplication... Notre lettre dans l'exemple fait 64 octet de longueur

                            ADD         HL,HL           ;*2
                            ADD         HL,HL           ;*4
                            ADD         HL,HL           ;*8
                            ADD         HL,HL           ;*16
                            ADD         HL,HL           ;*32
                            ADD         HL,HL           ;*64

                                                        ;le numéro de la lettre a été multiplié par la longueur d'une lettre
                            RET                         ;on met dès maintenant notre RET de fin histoire de pas l'oublier

                LETEXTE     DEFM      "HELLO WORLD"

Nous reste plus qu'à ajouter notre valeur multipliée à l'adresse de la fonte, pour qu'enfin on pointe sur la lettre dans la fonte.

Pour cela nous allons additionner la valeur calculée à l'adresse de la fonte.

La encore, rien de plus simple, nous allons faire une addition sur 16 bits entre HL et un autre registre. Choisissons DE par exemple.

ADD HL,DE additionnera la valeur calculée avec l'adresse de la fonte. Le resultat sera dans HL.

                            ORG         #8000
                            LD          HL,LETEXTE      ;dans HL on a l'adresse du texte
                            LD          A,(HL)          ;on met dans A l'octet contenu à l'adresse HL
                            SUB         32              ;on soustrait 32 au contenu de A.
                            LD          L,A             ;on met A dans L
                            LD          H,0             ;on met 0 dans H
                                                        ;HL contient le numéro de la lettre
                                                        ;on peut passer à la multiplication... Notre lettre dans l'exemple fait 64 octet de longueur

                            ADD         HL,HL           ;*2
                            ADD         HL,HL           ;*4
                            ADD         HL,HL           ;*8
                            ADD         HL,HL           ;*16
                            ADD         HL,HL           ;*32
                            ADD         HL,HL           ;*64

                                                        ;le numéro de la lettre a été multiplié par la longueur d'une lettre

                            LD          DE,#4000        ;on met dans DE #4000 ce qui correspond à l'adresse de notre fonte
                            ADD         HL,DE           ;on additionne notre valeur calculée à l'adresse de notre fonte
                                                        ;HL contient l'adresse de notre lettre dans la fonte :)
                                                        ;il n'y a plus qu'à afficher !!!

                            RET                         ;on met dès maintenant notre RET de fin histoire de pas l'oublier

                LETEXTE     DEFM      "HELLO WORLD"

Voila, vous n'avez plus qu'à afficher votre lettre tout à droite de l'écran, puis à scroller celui-ci d'un octet à chaque frame (ou chaque VBL comme vous voulez, de toute façon ca veut dire la même chose).

On va donc passer à l'exercice, mais avant cela, il faut que l'on parle de la façon dont est stocké le code en RAM et que nous voyons les automodifications !!!