Tutoriel – Introduction au langage machine et à l’ASM C64 – Partie 2


Facebooktwittergoogle_plusredditpinterestlinkedintumblrmail

Tutoriel Commodore 64 AssembleurDans la première partie nous avons vu, mine de rien, pas mal de choses essentielles : Ce qu’est le langage machine, les écritures et lectures en mémoire, les registres du MOS6510 (A, X et Y) et les branchements et l’appel de routine.

Nous allons poursuivre maintenant notre apprentissage de l’assembleur sur le Commodore 64 en présentant un Monitor.

(NDLR : Pour ceux qui ne l’ont pas encore suivi, vous devriez commencer par la première partie de ce tutoriel.)

 

PARTIE 2-1 : ASSEMBLER AVEC SUPERMON

C’est un programme simple et relativement court qui permets d’explorer, de modifier la mémoire, d’en extraire du code machine en désassemblant des zones de mémoire et bien évidement, d’assembler des programmes en langage assembleur ce qui sera plus pratique que d’écrire directement en langage machine avec le BASIC.

Vous trouverez ce programme dans les cardridges « Final ». Ne possédant pas ce cardridge j’utilise SUPERMON de Jim Butterfield qui est légèrement plus contraignant que le MON de Final. Il en existe bien entendu d’autres mais le gros avantage de SUPERMON est qu’il est très peu volumineux comparé aux autres existants.

Vous trouverez SUPERMON64 chez moi.

L’article original ici :
Sur atarimagazines.com
Sur Archives.org

Apres son chargement, SUPERMON occupe l’espace mémoires de $97ED à $9FFF.

 

HEXADECIMAL

Attention les monitors ne comprennent que l’hexadécimale !

Les nombres en représentation hexadécimale sont en base de 16 (au lieu de notre base courante de 10). Pour compter en hexadécimale on commence normalement par 0, 1, 2 etc jusqu’à 9. Ensuite A, B, C, D, E, F et enfin 10 qui représentera donc 16 en base décimale. Les caractères A, B, C, D, E et F représentent respectivement les nombres décimaux 10, 11, 12, 13, 14 et 15.

Lorsque l’on assemblera avec un Monitor, nous devrons toujours faire précéder nos valeurs ou adresses par le préfixe ‘$’ par exemple LDA #0 est impossible il faut écrire LDA #$00 de même STA 53280 n’est pas possible nous devons écrire STA $D020.

Sous SUPERMON, une autre contrainte est qu’il faut écrire l’adresse complète : STA $800 n’est pas possible, il faut écrire STA $0800

Pourquoi utiliser la représentation hexadécimale ? Tout simplement, un nombre hexadécimal permet de représenter un octet avec 2 caractères. En fait, un seul caractère hexadécimale permet de représenter 4 bits et, avec l’habitude, un nombre hexadécimale se converti facilement mentalement en binaire et vice-versa.

hexadecimal binaire

La représentation en hexadécimale est idéale lorsque l’on manipule des bits ou des adresses. De plus je vous garantis qu’il est beaucoup plus facile de retenir les registre ou des adresses en hexadécimale qu’en décimale.

Donc pour convertir un octet hexadécimale en décimal on procède ainsi : decimal=hex1*16+hex0

Tableau d’exemples :

Une adresse étant représentée sur 2 octets on utilisera 4 caractères hexadécimaux pour la représenter.
Pour convertir une adresse Hexadécimale en décimal on procède ainsi :
Decimal= hex3*4096+hex2*256+hex1*16+hex0

Tableau d’exemples :

Enfin lorsque l’on détaille les bits que nous manipulons dans un octet, nous parlerons de b7, b6, b5, b4, b3, b2, b1, et b0 ou ce dernier est le bit le moins significatif b7 étant le bit le plus significatif.

Exemples :

$00 :
B7    B6    B5    B4    B3    B2    B1    B0
0     0     0     0     0     0     0     0
$AA :
B7    B6    B5    B4    B3    B2    B1    B0
1     0     1     0     1     0     1     0
$FF :
B7    B6    B5    B4    B3    B2    B1    B0
1     1     1     1     1     1     1     1

Désolé de rabâcher, mais avec un Monitor, il nous faut vraiment apprendre à penser en hexadécimale.

 

Utilisation de SUPERMON

Revenons maintenant à notre monitor.
Au démarrage de SUPERMON apparait ceci :

Nous y voyons en fait la valeur actuelle (toujours en hexadécimale) des registres du 6510.
PC représente le registre PC ( J) sur 2 octets. AC représente le registre A.
XR, YR respectivement les registres X et Y.
Enfin SR est le registre de statuts.
Nous reviendrons sur les registres SP et SR plus loin.

Pour l’instant, pour se familiariser avec le Monitor, utilisons ce que nous avons déjà appris entrez A C000 LDA #$00. Nous indiquons ainsi que nous voulons assembler (A) en adresse $C000 l’instruction LDA #$00. Apres avoir ensuite appuyé sur « Enter », la ligne A C002 apparait automatiquement, l’instruction étant de longueur de 2 octets, le monitor à calculer pour nous l’adresse de la prochaine instruction.

Lorsque l’on saisit quelque chose de non conforme, Supermon affiche un point d’interrogation comme ci-dessous.

Il nous faut réutiliser la commande .A suivi de l’adresse et ressaisir l’instruction.

Entrons maintenant l’instruction BRK. Cette instruction est un peu particulière. On retiendra qu’elle va servir à rendre la main au Monitor ou bien plus simplement, pour terminer un programme. Une fois l’instruction entrée, apparait A C003 appuyons directement sur Entrée pour sortir du mode d’assemblage.

Avant d’exécuter le programme, entrez P C000 C002 pour visualiser le code assembleur ainsi le code machine généré par SUPERMON.

A9 00 représente le code machine en hexadécimal (169,0) de l’instruction LDA #$00
00 représente le code machine de l’instruction BRK.

Maintenant tapez G C000. Le Monitor va forcer le processeur à exécuter à partir de l’adresse $C000 (49152) et ainsi exécuter notre petit (tout petit) programme. Nous devrions obtenir ceci :

On remarque que les valeurs des registres ont changé. Le registre A vaut maintenant 0 et le registre de statuts SR vaut $33 (0011 0011) au lieu de $31 (0011 0001) car le bit1 c’est-à-dire le drapeau ZERO a été allumé.
Le registre PC vaut maintenant $C002 c’est tout simplement l’adresse où notre programme a rendu la main au monitor grâce à l’instruction BRK.
Avec le monitor, nous avons la possibilité d’exécuter toutes les instructions du processeur et d’en observer les effets.

Continuons notre familiarisation avec le monitor et saisissons notre boucle d’attente des 8 touches clavier de la colonne 0 (RUN/RESTORE, SPACE, Q, 1, 2, Back-Arrow, C= et Restaure) vue dans le dernier programme de la partie 1.
Maintenant, le paramètre de l’instruction BEQ sera l’adresse où nous voulons brancher et non plus le déplacement relatif : le Monitor calcul ce dernier à notre place.

Terminons par l’instruction BRK. Et visualisons le désassemblage avec la commande P C000 C0008. Enfin pour exécuter notre bout de code utiliser la commande G C000.

Apres avoir appuyer notre bout de code est exécuté. Le curseur disparait jusqu’à ce qu’une touche Break ou Espace (ou bien 1, 2, Q, C=, Back-arrow et Restaure) soit pressée.

Vidéo de démonstration :

 

VISUALISATION ET MANIPULATION DE LA MEMOIRE

La commande .M de SUPERMON permet de visualiser la mémoire.
Par exemple si nous saisissons .M $0400 $04FF nous allons voir le contenue de la mémoire écran située par défaut entre $0400 et $7EF (1024 à 2047) représenté en hexadécimal défiler où par exemple pour la première ligne :0440 représente l’adresse et les 8 valeurs suivante (20 32 etc) la valeurs en hexadécimal des 8 cases mémoire de $0440 a $0447.

Maintenant rentrer la commande .M 0400 0440

Ensuite faire monter le curseur jusqu’à la ligne 0400 et changer la valeur 2E par 00 et appuyer sur ENTER. Et constater que le caractère @ apparait en haut à gauche de l’écran.

En effet en plus de visualiser la mémoire, nous pouvons également la modifier.
Par exemple si nous remplaçons toutes les valeurs de 0400 à 0447 par la valeur de $20 on verra toute la partie haute de l’écran effacée.

 

L’utilisation de SUPERMON résumé en un programme

Pour terminer de résumé l’utilisation de SUPERMON, saisissons le programme suivant qui met juste l’écran hors-service et affiche une petite ligne blanche au centre de l’écran et attend qu’une touche de colonne 0 soit pressée pour rendre la main.

Ce programme utilise l’instruction CPY mem qui effectue une soustraction interne entre le registre Y et la valeur située à l’adresse mem affectant ainsi les drapeaux. Ici on utilise l’adresse $D012 qui est un registre du contrôleur vidéo du C-64 (Le VIC-II) et qui indique la position vertical courante du canon à électron à l’écran. Ainsi la boucle suivante :

      LDY #$60
      CPY $D012
      BNE -5

se termine lorsque le balayage horizontal atteints la ligne #$60 (ligne 96). On change, alors, rapidement la couleur de bord en blanc.
Ce petit bout de code sera par la suite très utile : il va permettre une synchronisation avec le rafraichissement de l’écran.
La boucle qui suit attend que la ligne horizontale #$60 se termine

      CPY $D012
      BEQ -5

On change rapidement l’écran de bord en noire et ainsi créer un « Raster » (une ligne horizontale) blanc.
Si vous ne comprenez pas complètement le programme ci-dessous, ne vous inquiétez pas, la synchronisation et les rasters seront aborder dans une future partie. Contentez-vous de le saisir, il s’agit juste d’illustrer l’utilisation de SUPERMON.

On peut désassembler notre programme avec P C000 C0022

Exécutons notre programme avec la commande G C000.
Nous devrions voir apparaitre une ligne blanche en haut de l’écran.

Sauvons maintenant notre programme en utilisant la commande suivante .S«test.prg»,08,C000,C027
SUPERMON crée un fichier test.prg où sont contenues notre code machine que nous venons d’assembler de l’adresse C000 a C027. Le paramètre 08 indique que nous sauvons sur disquette.

Effectuons une réinitialisation de notre C-64 et chargeons maintenant notre programme avec la commande LOAD «TEST.PRG»,8. Pour l’exécuter il nous faudra utiliser SYS49152.
On vérifie ainsi que notre programme fonctionne bien.
Nous aurions pu très bien charger depuis SUPERMON avec la commande .L« TEST.PRG »,08

Vidéo de démonstration :

 

Résumé des commandes d’un MON :

.A Assemble ; exemple .A LDX #$00
.D Désassemble ; exemple .D C000
.P Dessassemble plus precis ; exemple .P C000 C020
.M Afficher la mémoire ; exemple .M C000 C020
.R Affiche l’état des registres
.F Remplissage mémoire ; exemples:

  • .F C100 C1FF AA remplit la mémoire de $C100 a $C1FF avec la valeur hexadécimale AA.
  • .F C100 C100 FF écrit $FF dans la case mémoire $C100.

.G Exécuter ; exemple .G C000 exécuter à partir de $C000.
.X quitter
.L charger exemple .L « TEST.PRG »,08
.S sauver exemple .S«test.prg»,08,C000,C1FF
.T transfert exemple T C000 C0FF C100 transfert le contenue de $C000 a $C0FF en $C100
Attention lors de chargement ou de sauvegarde : pas d’espace.

Nous avons résumé très rapidement l’utilisation de SUPERMON, poursuivons maintenant avec lui notre apprentissage du MOS6510.

Dorénavant, les listings seront représentés de deux façons :

  • Fragments de code où nous omettrons d’indiquer l’adresse. Exemple :

      ASM               COMMENTAIRE
      LDA #$00          Charge 0 dans A
      STA $D020         Couleur noir pour l’écran de bord.

Le lecteur est encouragé à tester de lui-même ces fragments de codes.

  • Programme complet.

ADRESSE     ASM               COMMENTAIRE
C000        LDY $DC01         Lecture clavier dans Y
C002        INY               Incremente Y (si Y=#$FF, l’incremention produira 0)
C003        BEQ $C000         Si 0 aucune touche presse : on boucle
C005        BRK

Les programmes sont volontairement « minimalistes » pour que le lecteur puisse les tester, modifier et bien sur les embellir. :)

 

Lecture et écriture mémoire avec adressage indexée

Jusqu’à présent, nous n’avons utilisé que deux modes d’adressage : Direct comme LDA #$01 et absolue comme LDA $DC01 ou STA $D020.
Voyons maintenant un autre mode d’adressage très utile dit adressage indexé. Les instructions LDA mem,X ou bien STA mem,X utilisent ce mode d’adressage où le registres X joue le rôle index en ajoutant sa valeur à l’adresse absolue mem.
Par exemple, si on suppose que le registre X est égal à 1, dans ce case LDA $0400,X équivaut à LDA $0401. Si X= $09, LDA $0400, X équivaut à LDA $0409 etc.
A la place de X nous pouvons également utiliser le registre Y par exemple si Y= 1 LDA $0400,Y équivaut à LDA $0401.


      ASM               COMMENTAIRE
      LDX #$00          X=0
      LDA $0400,X       A = valeur dans $0400
      LDX #$02          X=0
      LDA $0400,X       A = valeur dans $0402
      LDX #$24          X=0
      LDA $0400,X       A = valeur dans $0424

L’adressage indexée est redoutable dans une boucle où l’index (X ou Y) est incrémenté
Illustrons cela avec ce petit programme qui remplit la mémoire écran avec un caractère de dessin et attend une touche Break pour quitter.

ADRESSE  ASM               COMMENTAIRE
C000     LDX #$00          X = 0
C002     LDA #$E5          A = $e5 caractère avec des lignes verticales
C004     STA $0400,X       Ecriture du caractère en mémoire écran
C007     STA $0500,X       Ecriture du caractère en mémoire écran
C00A     STA $0600,X       Ecriture du caractère en mémoire écran
C00D     STA $0700,X       Ecriture du caractère en mémoire écran
C010     INX               Index suivant
C011     BNE C002          Si X diffèrent de 0 on boucle
C013     LDA $DC01         Lecture clavier
C016     CMP #$FF          Touche presse ?
C018     BEQ $C013         Non on boucle
C00B     BRK               Sinon on quitte

Si nous exécutons le programme, l’écran ci-dessous devrait apparaitre.

Pour rappel, la mémoire écran est par default située entre $0400 et $07EF (1024 et 2023). Une écriture dans cette zone dans l’une des 1000 cases (40 colonnes sur 25 lignes) provoque l’apparition d’un caractère en fonction de ce que l’on écrit. Par exemple l’écriture en $0400 d’une valeur 1 provoque l’apparition d’un A en haut à gauche.
Dans le programme ci-dessus et grâce à l’adressage indexé dans une boucle, on remplit toute la mémoire écran.
Amusez-vous à remplacer #$e5 par un autre caractère. On peut par exemple effacer l’écran en utilisant la valeur #$20.

Grace à ce mode d’adressage on peut facilement copier des zones de mémoires.
Le fragment ci-dessous copie la zone $4000-$40FF dans $4100-$41FF

      ASM               COMMENTAIRE
      LDX #$00          X=0
*-9   LDA $4000,X       Lecture $40xx          
      STA $4100,X       Que l’on copie dans $41xx
      INX               Index suivant
      BNE *-9           Si diffèrent de 0 on boucle

La copie de blocs de mémoire vers d’autres zones est une opération élémentaire, dès-lors vous risquer de revoir très souvent le fragment de code ci-dessus qui trouve des applications dans diverses situations.

 

Apprendre de nouvelles instructions

Le monitor étant idéal pour l’apprentissage direct et pour tester les instructions du 6510.

Souvent nous disons que LDA est une instruction mais ce n’est pas tout à fait vrai car, comme nous l’avons vu, le processeur fait la distinction entre par exemple LDA #val ($A9) et LDA mem ($A5). Mais il est plus commode pour nous grouper les instructions par leur action qu’elles effectuent (LDA charge le registre A, STA écrit dans la mémoire avec le registre A, etc). Voilà pourquoi dans les références, les instructions sont groupées par mnémotechnique.

La liste complète du jeu d’instruction du MOS65xx se trouve par exemple sur le site de Idoc64.
Nous allons regarder comment lire cette référence.
Prenons par exemple le mnémotechnique LDA.

                 LDA Load accumulator with memory                
 
  Operation:  M -> A                                    N Z C I D V
                                                        / / _ _ _ _
  +----------------+-----------------------+---------+---------+----------+
  | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes|No. Cycles|
  +----------------+-----------------------+---------+---------+----------+
  |  Immediate     |   LDA #Oper           |    A9   |    2    |    2     |
  |  Zero Page     |   LDA Oper            |    A5   |    2    |    3     |
  |  Zero Page,X   |   LDA Oper,X          |    B5   |    2    |    4     |
  |  Absolute      |   LDA Oper            |    AD   |    3    |    4     |
  |  Absolute,X    |   LDA Oper,X          |    BD   |    3    |    4*    |
  |  Absolute,Y    |   LDA Oper,Y          |    B9   |    3    |    4*    |
  |  (Indirect,X)  |   LDA (Oper,X)        |    A1   |    2    |    6     |
  |  (Indirect),Y  |   LDA (Oper),Y        |    B1   |    2    |    5*    |
  +----------------+-----------------------+---------+---------+----------+
  * Add 1 if page boundary is crossed.

Dans ce tableau, l’action de l’instruction est indiquée par M->A qui signifie : charger dans le registre A en fonction de mode d’adressage.
L’action sur les bits du registre d’état SR est indiquée dans NZCIDV. Ici nous voyons que pour LDA, seuls les bits N et Z sont affectés.
Chaque entrée correspond à un mode d’adressage dont le nom est indiqué dans la colonne « Addressing Mode ».
L’instruction machine correspondante est indiquée dans la colonne OP CODE en hexadécimal.
La colonne « Assmbly Language Form » contiendra la syntaxe en assembleur.
La colonne « No. Bytes » la taille de l’instruction machine.
Enfin « No. Cycles » nous donne le nombre de cycles machine que l’instruction prend pour être exécuté (le processeur du C-64 exécute environ 1 millions de cycles par seconde).
Par exemple l’instruction LDA #Val s’exécute en 2 cycles, l’instruction LDA mem en 4 cycles. En revanche, LDA mem,X s’exécutera en 4 cycles sauf si l’adresse correspondante tombe sur une page différente de mem. Une page mémoire est en fait une section mémoire définit par le premier octet de l’adresse haute. $0400 et $0480 appartiennent à la même page : la page $04. Mais l’adresse $0840 et $0940 appartiennent à des pages différentes ; respectivement la page $08 et la page $09.
Concrètement si X vaut $10, alors l’instruction LDA $0400,X lira en adresse $0410 et s’exécutera donc en 4 cycles. Par contre, LDA $04F8,X lira en adresse $0508 c’est-à-dire en page $05 alors que mem vaut $04F8 donc en page $04 : l’instruction prendra 5 cycles pour être exécuter.
Nous examinerons un peu plus loin les autres modes d’adressage non encore abordé.

Prenons maintenant des instructions que nous n’avons pas encore vue TAX et TAY.

                  TAX Transfer accumulator to index X              
 
  Operation:  A -> X                                    N Z C I D V
                                                        / / _ _ _ _
                                 (Ref: 7.11)
  +----------------+-----------------------+---------+---------+----------+
  | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes|No. Cycles|
  +----------------+-----------------------+---------+---------+----------+
  |  Implied       |   TAX                 |    AA   |    1    |    2     |
  +----------------+-----------------------+---------+---------+----------+
 
 
                  TAY Transfer accumulator to index Y              
 
  Operation:  A -> Y                                    N Z C I D V
                                                        / / _ _ _ _
                                 (Ref: 7.13)
  +----------------+-----------------------+---------+---------+----------+
  | Addressing Mode| Assembly Language Form| OP CODE |No. Bytes|No. Cycles|
  +----------------+-----------------------+---------+---------+----------+
  |  Implied       |   TAY                 |    A8   |    1    |    2     |
  +----------------+-----------------------+---------+---------+----------+

Le label Opération nous indique que l’on charge dans X ou Y la valeur du registre A plus simplement, TAX et TAY transfère la valeur du registre A respectivement dans X et Y.
Ces instructions ne pèsent qu’un seul octet, n’ont pas de mode d’adressage et s’exécutent en 2 cycles.

Avec Supermon, nous pouvons directement tester ces instructions par exemple comme ceci :

       ADRESSE        ASM            COMMENTAIRE
       $C000          LDA #$91       Charge $91 dans A
       $C002          TAX            Transfère la valeur de A dans X
       $C003          TAY            Transfère la valeur de A dans Y
       $C004          BRK            Fin

Après avoir exécuté ce programme, nous voyons bien que les registre X et Y valent $91 !

Essayez par vous-même de tester maintenant les instructions TXA et TYA et constater qu’elles servent bien à transférer les registres X ou Y dans le registre A respectivement.

 

Page Zéro

La page zéro est un endroit de la mémoire localise en $0000-$00FF. Toutes les instructions utilisant l’adressage en Page zéro prennent 1 cycle de moins et 1 octets de moins. C’est donc une zone privilégiée.
Les deux instructions suivantes font exactement la même chose : écrire la valeur de A à l’adresse $0025. Sauf que la deuxième instruction utilise la page 0 et prend donc un octet de moins et s’exécute en 3 cycles au lieu de 4 pour la première.

  ASM            COMMENTAIRE
  STA $0025      écriture dans $25. Code machine : 141,37,0
  STA $25        écriture dans $25. Code machine : 133,37

Dans certains vieux assembleurs, on rajoutait Z à l’instruction mnémotechnique pour indiquer explicitement un adressage en Page 0 : STAZ $25 LDAZ $25

En page Zéro se trouve aussi quelque variables intéressante remplit par le système d’exploitation du C-64.

Par exemple:

$A0-$A2 Horloge interne
$22-$25 Espace temporaire pour le basic
$26-$29 Espace temporaire pour la multiplication et division
$2A Libre
$C5 Buffer clavier
$D3 Position curseur en X (Colonne ou COL) de 0 à 39.
$D5 Position curseur en Y (Ligne ou ROW) de 0 à 24.
$FB-$FE Espaces libres

 

Opérations logique booléennes

Les opérations de logique booléens seront abondamment utilisées donc autant se familiariser avec tout de suite.
Voici les tableaux des opérateurs AND (Et) OR (Ou) et EOR(Ou exclusif).

En colonne et en rangée nous avons la valeur des deux bits de paramètre et au milieu le résultat.
A quoi cela peut-il servir ? Principalement pour tester, allumer, inverser ou éteindre des bits (mais pas que !).

Par exemple, le b4 du registre $D011 indique si l’affichage est actif. En éteignant ce bit on met une partie du VIC hors service comme nous l’avons déjà fait dans quelques programmes précédents. Mais nous l’avions fait d’une façon un peu sauvage en écrivant directement 0.

       LDA #$00
       STA $D011

Alors que l’on devrait plutôt procéder ainsi si on veut conserver l’état des autres bits de ce registre.

       LDA $D011
       AND #$EF
       STA $D011

Détaillons : par default le registre $D011 contient la valeur de $1B c’est-à-dire en binaire 0001 1011. Le bit 4 est allumé et nous souhaitons l’éteindre sans toucher aux autres bits.
On applique donc un AND (ET) logique avec la valeur 1110 1111 c’est-à-dire $EF où tous les bits sont allumée excepté celui que nous voulons éteindre, le bit 4.

                         0001 1011              $1B
AND                      1110 1111              $EF
=                        0000 1011              $0B

De la même façon pour allumer un bit précis on effectuera un OR (OU) avec une valeur ou seul le(s) bit(s) que nous voulons allumer sera/ont allumé/s.
On rallume donc le bit 4 du registre $D011 de cette façon.

       LDA $D011
       ORA #$10
       STA $D011
                         0000 1011              $0B
OR                       0001 0000              $10
=                        0001 1011              $1B

Un EOR (OU exclusif) est très utilisé pour inverser des bits.
Un programme d’exemple ci-dessous pour faire varier la couleur du fond sur deux valeurs.

ADRESSE          ASM                    COMMENTAIRE
C000             LDA $D020              Lecture couleur de bord
C003             EOR #$01               on inverse le bit 1
C006             STA $D020              On applique la nouvelle couleur
C009             LDA $DC01              Lecture clavier
C00C             CMP #$FF               Touche presse ?
C00E             BEQ $C000              Non -> on boucle
C010             BRK                    Sinon on quitte

 

La Pile et le Registre SP

La pile est un espace qui se trouve en page 1 entre l’adresse $0100 et $01FF et qui est géré par le processeur en LIFO (dernier entré, premier sortie).

Le registre SP contient l’adresse de poids faible de la prochaine entrée valide de la pile.
Les instructions PHA et PLA empile A et dépilé dans A respectivement. Leur utilisation est idéale lorsque l’on veut sauvegarder des valeurs intermédiaires.

Le programme ci-dessous illustre une utilisation de la pile en changeant la couleur de bord et en la restaurant en fin de programme.

ADRESSE          ASM                    COMMENTAIRE
C000             LDA $D020              Lecture couleur de bord
C003             PHA                    Sauve cette valeur dans la pile
C004             EOR #$0F               on inverse les quarte derniers bits
C006             STA $D020              On applique la nouvelle couleur
C009             JSR $E4E0              Attend une touche clavier colonne 0 au moins 8 secondes
C00C             PLA                    Récupère la couleur d’origine
C00D             STA $D020              Applique la couleur d’origine
C010             BRK                    Quitte

Notez également l’utilisation de la routine $E4E0 qui est très pratique pour les petits tests : elle attend au moins 8 secondes avant de rendre la main à moins qu’une touche de la colonne 0 du clavier ait été pressée.

Fonctionnement de la Pile en détail.

Pour les lecteurs curieux, on peut analyser en détails le fonctionnement de la pile avec Supermon.
Regardez l’exemple ci-dessous :

        ADRESSE           ASM
        C000              LDA #$AA
        C002              PHA
        C003              LDA #$BB
        C005              PHA
        C006              BRK

Avant d’exécuter ce programme utilisons les commandes M 01F0 01FF pour vérifier l’état de la mémoire avant empilage.

Le programme ci-dessus va empiler les valeurs $AA ensuite $BB et rendre la main au monitor avec l’instruction BRK.
Avant l’exécution du programme le registre SP vaut F6 et les zones mémoires $01F6 et $01F5 valent respectivement $9D et $BA.
Apres exécution du programme, le registre SP vaut F4 et les zones mémoires $01F6 et $01F5 valent respectivement $AA et $BB.

Ensuite on initialise le registre A avec 0 pour observer l’effet du dépilage :
Un premier PLA, le registre A reprends la valeur de $BB et le registre SP est incrémenté et passe à la valeur $F5.
Un deuxième PLA, le registre A prend la valeur $AA et le registre SP est incrémenté et passe à la valeur $F6.
Remarquez que la valeur des zones mémoire ne sont plus changer $01F6 et $01F5 valent toujours respectivement $AA et $BB.

La pile est également utilisée lors d’un appel de routine par JSR ; l’adresse qui termine l’instruction JSR est placée dans la pile, l’instruction RTS ne fait que dépiler l’adresse pour l’appliquer au Registre PC (le processeur en interne rajoute ensuite 1).

Ci-dessus un exemple. L’adresse $C100 contient une routine qui initialise le registre X à la valeur 0 avant de « breaker » pour pouvoir examiner les registre.
L’adresse $C080 contient un JSR $C1000 qui va faire appel à la routine.
Avant l’appel, le registre SP vaut $F6 après exécution de JSR la pile vaut $F6 est l’adresse $0105 et $0106 valent $82 $C0 soit l’adresse $C082 vers laquelle l’instruction RTS de la routine doit rendre la main.

 

Retour sur les routines

Dans la première partie nous avions appris à appeler une routine grâce à l’instruction JSR add. Nous pouvons également créer nos propres routines qui devront se terminer par l’instruction RTS.
Lorsque nous appelions notre programme machine avec la commande Basic SYS, nous appelions en fait notre routine en langage machine. Et une routine peut également appelez d’autre routines.

Par exemple on veut créer une routine qui va émettre un son quelconque.

D’abord résumons rapidement comment emmètre un son en utilisant le fameux SID de notre Commodore 64.
Le SID contient trois voix programmable indépendamment. Chaque voix peut gérer quatre formes d’onde : Bruit blancs, Créneaux (Pulse avec contrôle du rapport cyclique), Dent de scie et Triangle. Nous n’utiliserons ici que la voix 0.

Pour émettre un son avec la voix 0 il faut :

  • Régler le volume général contrôler par les bits b3-b0 du registre $D418
  • Configurer l’enveloppe (ADSR) (Attaque, pré-maintien, maintien et relâchement) de la voix 0. Chaque paramètre est contrôlé par 4 bits des registres $D404/$D405 : les bits b7-b4 de $D404 pour l’attaque A, b3-b0 de $D404 pour le pré-maintien D, b7-b4 de $D405 pour le maintien S et enfin les bits b3-b0 de $D405 pour le relâchement R.
  • Régler la fréquence du son sur les registre $D400 et $D401.
  • Et on écrit, dans le registre de contrôle de la Voix $D404, la forme d’onde voulue. On y allume le bit b0 et les bits b7-b4 sélectionne respectivement : le bruit blanc, le créneau, dent de scie et triangulaire.
  • On fait démarrer la phase R en éteignant le bit 0 du registre $D404.
  • On éteint complètement la voix en écrivant 0 dans ce même registre de contrôle.

On remarquera comme il est plus facile de configurer le SID avec l’hexadécimal qu’en décimal avec le Basic.
La routine suivant émet un bruit blanc. On la placera en adresse $C050 :

        ADRESSE        ASM            COMMENTAIRE
        C050           LDX #$10       Fréquence haute        
        C052           STX $D401      Registre A dans fréquence haut
        C055           LDX #$09       Attaque = 0 Decay = 9
        C057           STX $D405      AD
        C05A           LDX #$F8       Sustain=$F release=$8
        C05C           STX $D406      SR
        C05F           LDX #$0F       VOLUME MAX
        C061           STX $D418
        C064           LDX #$81       Bruit blanc gate = 1          
        C065           STX $D404
        C068           LDX #$80       Bruit blanc gate = 0
        C06A           RTS

On peut utiliser maintenant cette routine très simplement par exemple dans le programme ci-dessous, chaque fois que la touche espace est pressée :

        ADRESSE        ASM            COMMENTAIRE
        C000           LDA $C5        Lecture clavier
        C002           CMP #$3F       Touche break ?
        C004           BEQ $C010      Oui on saute la boucle
        C006           CMP #$3E       Touche Espace ?
        C008           BNE $C000      Non on boucle
        C00A           JSR $C050      Sinon Appel de notre routine : on émet un son
        C00D           JMP $C000      On Boucle
        C010           LDA #$00       A=0
        C011           STA $D018      pour éteindre le SID
        C014           BRK            Et quitte

 

Utilisation principale de la pile dans des routines.

Très souvent dans une routine on ne veut pas affecter la valeur des registres qui arrivent en entrée. On peut utiliser alors la pile pour sauver la valeur des registres en les empilant puis à la fin de la fonction, on restaure les valeurs d’origine en les dépilant.

        ASM                    COMMENTAIRE                                  
        PHA                    Sauve A dans la pile                                         
        TXA                    Transfert X dans A                   
        PHA                    Pour le sauver dans la pile          
        TYA                    Transfert Y dans A                   
        PHA                    Pour le sauver dans la pile                  
        *********************  notre routine
        PLA                    Récupère la dernière Entrée dans la pile     
        TAY                    Retrouve Y                                           
        PLA                    Récupère l’entrée suivant dans la pile
        TAX                    Retrouve X                                                   
        PLA                    Retrouve A                                           
        RTS                    Quitte

Profitons-en pour voir quelques exemples de compromis entre la vitesse et la taille du code que l’on rencontre très souvent lorsque l’on programme en assembleur.

Si votre routine doit-être rapide il vaut mieux ne pas utiliser la pile et faire comme ci-dessous où nous nous servons des adresse $CFF0-$CFF2 pour sauver les valeurs en entrée.

        ASM                    Cycles         Taille
        STA $CFF0              4 - 4          3 - 3
        STX $CFF1              4 – 8          3 - 6
        STY $CFF2              4 – 12         3 - 9
        ;------------------------------------------
        LDA $CFF0              4 – 16         3 - 12
        LDX $CFF1              4 – 20         3 - 15
        LDY $CFF2              4 – 24         3 – 18

On peut faire encore mieux en utilisant une adresse de la page Zéro en utilisant cette fois ci les adresses $35-$36

        ASM                    Cycles         Taille
        STA $35                3 - 3          2 - 2
        STX $36                3 – 6          2 - 4
        STY $37                3 – 9          2 - 6
        ;------------------------------------------
        LDA $35                3 – 12         2 - 8
        LDX $36                3 – 15         2 - 10
        LDY $37                3 – 18         2 - 12

Tout sera une histoire de compromis :
L’utilisation de la pile rend le code plus petit avec 8 octets, mais elle prend 26 cycles.
La deuxième façon utilise 18 octets mais gagne 2 cycles.
Enfin la troisième est la plus rapide avec 18 cycles (8 cycles gagnée) et pèse 12 octets mais les espaces en Page zéro ne sont pas toujours disponible.

 

Instructions INC/DEC

Les instructions INC et DEC incrémentent et décrémentent une case mémoire respectivement et agissent sur les flags Z et N.

        ASM            COMMENTAIRE
        LDA #$00       A = $00
        STA $D020      $D020 = $00   
        INC $D020      $D020 = $01
        INC $D020      $D020 = $02
        DEC $D020      $D020 = $01

Ces instructions acceptent également l’adressage indexé.
Exemple : on incrémente chaque case de la zone $8000-$80FF.

        ASM            COMMENTAIRE
        LDX #$00       INDEX X = 0
*-5     INC $8000,X    INCREMENTE $80xx
        INX            INDEX SUIVANT
        BNE *-5        SI X <> 0 on boucle

Dans la plupart des tutoriaux d’assembleurs, on retrouve ce programme très simple qui utilise l’instruction INC pour incrémenter la couleur de bord.

        ADRESSE          ASM                    COMMENTAIRE
        C000             INC $D020              Couleur d’écran de bord suivant
        C003             LDA $DC01              Lecture clavier colonne 0
        C006             CMP #$FF               Touche presse ?
        C008             BEQ $C000              Non on boucle
        C00A             BRK                    Sinon Fin du programme

 

Programme de conclusion

Avant d’aborder la suite de cette partie sur le Monitor (qui sera un peu plus lourde), je vous propose ce petit programme qui va un peu résumer ce que nous avons vu.
Apres avoir dessiné des petits carres avec le caractère #$CF, nous allons utiliser un registre du VIC, le $D016, pour effectuer un scrolling horizontal.
Les 3 derniers bits de ce registre contrôlent le décalage horizontal de la mémoire écran pour un maximum de 7 pixels. Mais pour que cela marche, le bit 4 de ce registre doit être mis à 0. On remarquera qu’une fois fait, seul 38 caractères sont affichés au lieu de 40.
Seuls les 3 derniers bits de registre seront incrémentés et les 5 bits de poids fort (b7-b3) devront avoir une valeur de 0. Cette opération s’effectuera après rafraichissement d’écran provoquant ainsi une animation tout à fait sympathique (mais qui peut donner un petit mal de tête).

Le fragment ci-dessous fait scroller horizontalement la mémoire écran.

        ASM            COMMENTAIRE
        DEC $D016      Incrémentation de $D016
        LDA $D016      Lecture de $D016
        AND #$07       Pour éteindre les bits7-4
        STA $D016      Applique

On se sert de la pile pour sauver la valeur d’origine du registre $D016 que l’on restaurera à la fin du programme. On applique le scrolling à chaque rafraichissement mais vous pouvez vous amuser à faire sans. Enfin le programme utilise la routine $E544 qui efface la mémoire écran mais également la mémoire couleur (située en $D800-DFEF) voilà pourquoi elle est appelez au début de programme.

ADRESSE          ASM                    COMMENTAIRE
C000             JSR $E544              Efface l’écran et les couleurs
C003             LDA $D016              Lecture couleur de bord
C006             PHA                    Sauve la valeur de $D016 dans la pile
C007             LDA #$E2               Ligne horizontale
C009             LDX #$00               Index 0
C00B             STA $0400,X            écriture mémoire écran
C00E             STA $0500,X            écriture mémoire écran
C011             STA $0600,X            écriture mémoire écran
C014             STA $0700,X            écriture mémoire écran
C017             INX                    Index suivant
C018             BNE $C008              boucle écriture
C01A             DEC $D016              Scroll : Décrémentation de $D016
C01D             LDA $D016              dans A
C020             AND #$07               Seul les bits b2-b0 sont préservés
C022             STA $D016              Applique dans $D016
C025             LDY #$FF               Y=255 pour la synchronisation
C027             CPY $D012              On attend
C02A             BNE $C024              que le balayage ait atteint la ligne 255
C02C             LDA $DC01              Lecture clavier colonne 0
C02F             CMP #$FF               Touche presse ?
C031             BEQ $C017              Non on boucle sur le scroll
C033             PLA                    Sinon récupère la valeur d’origine de la pile
C034             STA $D016              Que l’on restaure
C037             JSR $E544              Efface l’écran
C03A             BRK                    Fin

J’encourage le lecteur à modifier le programme précédent. Voilà quelques suggestions mais je dois en oublier un bon paquet: Vous pouvez utiliser un autre caractère mais pour que ça marche il faut évidements éviter les caractères type « ligne horizontale », sinon vous devriez utiliser le registre $D011 pour le scrolling vertical, mais il faudra, dans ce cas, impérativement préserver les valeurs d’origine des b7-b5. Vous pouvez également dupliquer la partie synchronisation pour ralentir l’animation ou carrément la supprimer.

Enfin ceux qui préfèrent partir de rien et qui sont ambitieux peuvent par exemple réécrire le programme du manuel utilisateur qui faisait voler une montgolfière complètement en assembleur mais en utilisant la synchronisation avec l’écran pour supprimer les scintillements que l’on avait avec le programme Basic.

 

Pzawa

Tutoriel – Introduction au langage machine et à l’ASM C64 – Partie 2

Ce sujet a 3 réponses, 4 participants et a été mis à jour par Jim Jim, il y a 1 semaine et 4 jours.

3 sujets de 1 à 3 (sur un total de 3)

Partager sur vos réseaux sociaux préférés :
Facebooktwittergoogle_plusredditpinterestlinkedintumblrmail

  • Auteur
    Messages
  • #15531
    Jeeg
    Jeeg
    • 25 Messages

    Vraiment excellent, bravo ! :good:



    #15535
    lexomil
    lexomil
    • 5 Messages

    Sacré boulot !

    Pour ceux qui ne peuvent/veulent installer un émulateur et les outils pour faire de l’asm il existe un site http://www.6502asm.com/ qui propose un assembleur 6502 avec un écran minimaliste, c’est une solution très simple pour débuter.

    #15537
    Staff
    Jim
    Jim
    • 1 347 Messages

    Gros bravo à Pzawa oui qui nous a encore fait un tuto de très haut niveau.  :good:

    My beast : A500 Plus - 2Mo de Chip - 128Mo Fast - 8Gb CF - 68080@78Mhz (Vampire V2+ inside)

Partager sur vos réseaux sociaux préférés :
Facebooktwittergoogle_plusredditpinterestlinkedintumblrmail
3 sujets de 1 à 3 (sur un total de 3)

Vous devez être connecté pour répondre à ce sujet.