Apprendre l'assembleur 6809 en 10 minutes
Copyrigh (c)1998 Dan Truong, ENSSAT, 6 rue de Kerampont, B.P. 447, 22305 Lannion Cedex.
Syntaxe
Les commentaires sont précédés d'un ";" ou d'une "*".
par exemple:
; un commentaire
* un commentaire
Les mnémoniques de l'assembleur et les pseudo-ops sont écrites sur 3 ou 4 caractères. Les noms choisis permettent de se rapeler facilement de l'opération exécutée
par exemple:
ADDA #$55 ; effectue l'addition A+$55->A
DB $12 ; réserve un octet en mémoire, initalisé à $12 (declare byte)
Les constantes peuvent être écrite en ASCII, en décimal ou en hexadécimal.
par exemple:
; Les 3 déclarations suivantes sont équivalentes:
db 'A' ; en ASCII
db 65 ; en décimal
db $41 ; en hexadécimal
Notez que le 6809 code tout sur des octets, les différentes façons d'écrire les valeurs ont donc juste pour but de faciliter la tache du programmeur (gerér du texte directement en ASCII, des entiers en décimal ou en hexa, etc.).
Par exemple, on pourrait écrire du code illisible, et très dur à rédiger. Les deux bouts de codes suivant sont équivalents:
lda #$65
db 72 ; ici on a remplacé la mnémonique par son code-op
db 'A' ; (notez qu'il faut déjà chercher pour le connaitre)
bne suite$
ldx Message$
jsr AFF_TXT
...
lda #$65
cmpa #$65 ; c'est quand même plus lisible comme ca!
bne suite$
ldx Message$
jsr AFF_TXT
...
Les labels font 32 caractères maximum, ils sont suivis de ":". On différencie les majuscules des minuscules. Les labels globaux sont définis dans tous le programme dans tout le programme. ils doivent donc être uniques. Les labels locaux ne ne sont définis qu'entre deux labels globaux. Ils peuvent donc être redéfinis dans le programme. On les déclare en les faisant suivre d'un "$".
par exemple:
ROUTINE1: ; Label global, il ne peut être défini qu'une fois
ldx #Message$
jsr AFF_TXT
Menu$: ; Label local
jsr LIRE_CAR
cmpa #0
bne Menu2$
rts ; fin de routine
Menu2$:
cmpa #1
bne $Menu
jsr MENU1
jmp Menu$
Message$:
db 'Menu de la routine 1',0
ROUTINE2: ; Label global, tous les anciens labels locaux sont oubliés
ldx #Message$
jsr AFF_TXT
Menu$: ; Label local, on réutilise le nom "Menu$"
jsr LIRE_CAR
;...
bne Menu$ ; quand on saute il n'y a pas d'ambiguité
Message$:
db 'Menu de la routine 2',0
Les pseudo-opérations et directives de compilation
Le compilateur comprends plusieurs pseudo-opérations. Une pseudo-op ne se traduit pas en une instruction machine, mais est comprise par le compilateur qui effectue l'action correspondante.
par exemple:
Douze EQ $12 ; Définit le nom "Douze"
lda #Douze ; Le compilateur remplacera "Douze" par $12 automatiquement
On peut réserver et initialiser une zone mémoire pour y déclarer du texte, des entier ou des flotants:
par exemple:
DB 'je peux afficher ce texte a l''écran' ; une chaine de texte
DB 0,$12 ; deux octets
DW $ffff ; un mot 16 bits
FLOAT 175.12 ; un flotant 32 bits
DOUBLE 175.2 ; un flotant 64 bits
On peut réserver et initaliser beaucoup d'espace en mémoire:
BLKB 32,0 ; on reserve 32 octets mis à zéro
BLKW 32,$FF ; on réserve 32 mots (64 octets), initialisés à 255.
Il existe d'autres pseudo-opérations et directives de compilation. Si vous en avez besoin, reportez vous à la documentation de l'assembleur 6809.
Modes d'adressage
Le mode d'adressage est spécifié par des caractères # ou []
par exemple:
si en mémoire on a la valeur $33 à l'adresse $12 et la valeur $ff à l'adresse $33 alors
lda #$12 ; immédiat: A <- $12 charge la constante dans le registre A
lda $12 ; direct: A <- $33 charge dans A le contenu à l'adresse $12
lda [$12] ; indirect: A <- $ff charge dans A le contenu à l'adresse
; pointée par le contenu à l'adresse $12
lda ,X ; indexé: charge dans A le contenu à l'adresse pointée par
; le registre X
lda ,X+ ; post-incrémentation: idem, ensuite X est incrémenté de 1
lda ,X++ ; idem, ensuite X est incrémenté de 2
lda , X- ; on peut aussi décrémenter X de 1 ou 2...
lda #5,X ; charge dans A le contenu de la RAM a l'adresse pointée
; par le registre X augmentée de 5
; L'offset doit tenir soit sur 5, 8 ou 16 bits
lda [$5],X ; on peut faire des combinaisons...
Les registres
Il y a 2 registres A et B 8 bits pour travailler, qui peuvent être combinés et utilisés comme un registre 16 bits appelé D.
Il y a deux registres spéciaux 8 bits CC et DP. CC contient les drapeaux d'exécution (résultats de l'UAL, et drapeaux d'interruption). DP (?).
Il y a aussi 2 registres d'index 16 bits, X et Y, et 2 registres de pile 16 bits U et S. On peut utiliser X et Y a volonté, mais il ne faut pas modifier U ou S directement, car ils servent à gérer les piles.
Registres 8 bits:
A accumulateur A
B accumulateur B
CC registre de codes conditions (drapeaux de l'UAL)
DP (?)
Registres 16 bits:
D = A (poids forts de D), B (poids faible de D)
X registre d'index (lda ,X)
Y registre d'index (lda ,Y)
U pointeur de pile utilisateur (pulu/pshu)
S pointeur de pile système (puls/pshs)
Les instructions de base
Voici une liste (non exhaustive) des instructions disponibles. Par convention on note R pour un registre a définir. Par exemple, si on parle de ldR où R peut être A, B ou D, alors il y a 3 instructions, lda, ldb et ldd. Lorsqu'on écrit NN, cela représente le paramètre de l'instruction utilisé pour faire l'adressage mémoire. Par exemple si on écrit "ldR NN", on peut en fait utiliser l'instruction "lda #$12".
ldR
NNstR NN
charge/stocke une valeur dans le registre R
(où R est A,B,D,X,Y,U,S au choix)
addR adcR andR aslR asrR bitR clrR
cmpR comR decR eorR incR lslR negR
orR rolR rorR sbcR tstR NN
effectuent une opération arithmetique ou logique entre le registre R et la RAM.
par exemple, eorR (exclusive or), lslR (logical shift left), rolR (rotate bits left)
(où R est soit A ou B)
addR subR cmpR NN
effectue une opération arithmetique ou logique entre le registre R et la RAM
(où R est A, B ou D)
abx
additionne le registre B (8 bits) à X (B+X->X)
mul
multiplie A par B, résultat dans D (AxB->D)
sex
extention des bits de signe du registre (?)
leasR
...
daa
decimal adjust A (?)
exg R1, R2
echange le contenu de deux registres de même taille
(où R1 et R2 sont soit 8 bits: A, B CC ou DP, soit 16 bits: X,Y,S,U ou PC)
tfr R1,R2
copie le contenu de R1 dans R2
(où R1 et R2 sont soit 8 bits: A, B CC ou DP, soit 16 bits: X,Y,S,U ou PC)
pshR1 R2
pulR1 R2
empile/dépile le contenu du registre R2 dans la pile R1
(où R1 est soit S soit U et R2 est un autre registre)
bcc bcs beq bne bge
bgt bhi bhs ble NN ...
branchements conditionnels
bra NN
branchement inconditionnel
jmp NN
saut inconditionnel
jsr NN
saut inconditionnel à une routine (qui finit par un "rts")
rts
saut de retour vers l'adresse de l'appel (empilée dans la pile S)
Pour une description plus détaillée du jeu d'instruction, voyez la documentation du processeur 6809 ou prenez un livre de programmation du 6809 à la bibliothèque.
Utilisation de la pile
Lorsqu'il n'y a pas assez de registres pour travailler, on peut sauvegarder temporairement des registres dans la pile (c.a.d. en mémoire) avant de les réutiliser avec pshs/puls.
On peut utiliser soit la pile système (S) soit la pile utilisateur (U).
Attention: il faut impérativement avoir dépiler tous les registres qu'on a empilé avant de faire un rts, sinon le système plante.
exemple:
TestAxB:
; on verifie que A*B est superieur a 255 mais on veut garder A et B intact
...
pshs A ; ... donc on les empile
pshs B
mult ; A*B -> D : la multiplication efface A et B !
cmpd #$00FF ; c'est pour ca qu'on a sauvés A et B
ble Erreur$
puls B ; on dépile A et B
puls A
...
rts
Erreur$:
ldx Message$
jsr AFF_TXT
rts
Message$:
db " A x B est trop petit"
Les instructions jsr/rts utilisent la pile pour faire des routines réutilisables. Jsr empile le PC puis saute à l'adresse de la routine demandée. Rts effectue automatiquement le saut de retour en dépilant le PC.
exemple:
Main:
jsr LIT_CAR ; lecture de B
jsr ASC_TO_BIN
tfr A, B
jsr LIT_CAR ; lecture de A
jsr ASC_TO_BIN
jsr Mult8Bit ; (1) on appele la routine AxB->A
cmpa #$55
ble AffA$ ; si AxB <= $55 on veut afficher A*B*3
tfr B, A ; sinon on affiche B*3 (en recopiant B dans A)
AffA$:
ldb #$3 ; on multiplie par 3
jsr Mult8Bit ; (2) on réutilise la routine AxB->B
jsr AFF_VAL
rts
;-----------------------------
; cette routine fait AxB->A sans effacer B.
Mult8Bit:
pshs B
mult
cmpb #$0
bne Erreur$
puls B
rts ; on retourne automatiquement en (1) ou en (2)
Erreur$:
ldx Message$
jsr AFF_TXT
rts
Message$:
db "Erreur: A x B -> A (dépassement de capacité)"
Remarques sur la chaine de compilation
Le compilateur détecte peu d'erreurs à la compilation. Par exemple "mult X, B" ne génère pas d'erreur. Le compilateur ignore le surplus de texte et se contente de générer un "mult" qui fait AxB->D.
La méthode de compilation est la suivante pour le projet:
- ecrire un fichier source
edit program.asm
- compiler le programme.
x6809 program
On obtient un fichier objet program.obj
- linker le programme. Il faut générer un fichier .hex (option h)
link
program
1000
(taper entrée jusqu'au choix des options)
h
On obtient un fichier texte program.hex contenant des nombres en hexa
- utiliser le terminal de windows pour envoyer le code sur la carte 6908
mettre la carte 6809 en mode transfert de programme (T)
lancer terminal
menu Transfert/Envoyer un fichier texte
selectionner program.hex
cliquer OK
Il faudra peut être configurer le mode de transfert du terminal:
Menu paramètre/Communications 1200 baud, 8 bits, parité aucune, xon/xoff, 1 bit d'arrêt, port COM2.
- il reste à exécuter le programme
lancer le programme sur la console WYSE (g1000)
Si l'affichage du WYSE ne marche pas il faut le configurer:
Touche setup (F3)
On se déplace dans le menu du bas avec les flèches, on sélectionne avec espace, on sort avec setup
choisir communication, et mettre emission et réception à 1200 bauds (même paramètres que ceux utilisés pour terminal)
Si il y a toujours un problème, vous pouvez vérifier que l'écran émule bien du VT100, que le clavier est bien US, etc.
Note: les cables VISU et PC ne sont pas équivalents, ne les confondez pas.
Fin.