🖥️ TP2 - Approfondissement - Assembleur 6811

Corrigé - Arithmétique avancée et ruptures de séquence

📊 Partie A - Arithmétique Avancée

A.1 - Dépassement de capacité et flags N, V, C

🧪 Exercice A.1.1 - Dépassement lors d'une addition (nombres non signés)

Q1.a) Prédictions

Résultat de 200₁₀ + 100₁₀ sur 9 bits : 300₁₀ = 100101100₂

Dans le registre A (8 bits) : 00101100₂ (on garde seulement les 8 bits de poids faible)

En hexadécimal : 2C₁₆

En décimal : 44₁₀

Q1.b) Code machine

0000: 86 C8 8B 64 B7 00 20

86 C8 : ldaa #200 (C8₁₆ = 200₁₀)

8B 64 : adda #100 (64₁₆ = 100₁₀)

B7 00 20 : staa sum

Q1.c) Exécution pas à pas

Instruction A (hexa) A (décimal) Flag N Flag V Flag C
ldaa #200 C8 200 1 0 0
adda #100 2C 44 0 0 1

Q1.d) Analyse

Le registre A contient : 2C (hexa) = 44 (décimal)

Le flag C vaut : 1

Conclusion : Le résultat est incorrect car il y a eu un dépassement. Le flag C=1 indique qu'une retenue a été générée.
Pour obtenir le résultat correct, il faut calculer : C × 256 + sum = 1 × 256 + 44 = 300

💡 Point clé : Quand C=1, cela signifie qu'il y a eu un dépassement de capacité pour des nombres non signés. Le résultat réel est sur 9 bits.

🧪 Exercice A.1.2 - Dépassement avec nombres signés (flag V)

Q2.a) Test avec 127 + 1

Instruction A (hexa) A (décimal signé) N V C
adda #1 80 -128 1 1 0

Q2.b) Interprétation

Le registre A contient : 80₁₆ = -128 (décimal signé)

Flag V = 1 : Indique un dépassement en arithmétique signée

Flag N = 1 : Indique un nombre négatif

Explication : 127 + 1 = 128, mais en complément à 2 sur 8 bits, 128 n'est pas représentable (max = 127). Le résultat "déborde" et devient -128.

Q2.c) Test avec v1=-128 et v2=-1

Instruction A (hexa) A (signé) N V C
adda v2 7F 127 0 1 1

Q2.d) Interprétation

Le registre A contient : 7F₁₆ = 127 en décimal signé

Flag V = 1 : Indique un dépassement en arithmétique signée

Flag N = 0 : Indique un nombre positif

Explication : -128 + (-1) = -129, qui n'est pas représentable sur 8 bits signés (min = -128). Le résultat déborde et devient 127.

⚠️ Important : Le flag V détecte les dépassements pour les nombres SIGNÉS, tandis que le flag C détecte les dépassements pour les nombres NON SIGNÉS.

A.2 - Le résultat de l'opération est nul et flag Z

🧪 Exercice A.2.1 - Résultat nul et flag Z

Q3.a) Test avec v1=-1 et v2=1

Instruction A (hexa) A (décimal signé) N Z V C
staa sum 00 0 0 1 0 1

Q3.b) Interprétation

La case mémoire contient : 00₁₆

Flag Z = 1 : Indique que le résultat est exactement zéro

Explication : -1 + 1 = 0, le flag Z est positionné à 1 pour indiquer ce résultat nul.

✅ Points clés à retenir :
  • Z=1 : Le résultat est exactement zéro (très utile pour les boucles !)
  • N=1 : Le bit 7 est à 1, nombre négatif en complément à 2
  • V=1 : Dépassement de capacité pour opération signée (résultat < -128 ou> 127)
  • C=1 : Dépassement de capacité pour opération non signée (résultat < 0 ou> 255)

A.3 - Multiplication en machine

🧪 Exercice A.3.1 - Test avec dépassement 8 bits

Q4.a) Exécution pas à pas

Instruction A (hexa) B (hexa) D (hexa) Commentaire
ldaa v1 0A 00 0A00 A = 10₁₀
ldab v2 0A 28 0A28 B = 40₁₀
mul 01 90 0190 10₁₀ × 40₁₀ = 400₁₀
staa v3H 01 90 0190 V3H = 01₁₆
stab v3L 01 90 0190 V3L = 90₁₆

Q4.b) Analyse

v3H contient : 01 (hexa)

v3L contient : 90 (hexa)

Le résultat complet v3 : v3H|v3L = 0190 (hexa)

Conversion en décimal : 0190₁₆ = 1×256 + 144 = 400₁₀

Conclusion : Oui, le résultat est correct !

Q4.c) Code source et code machine (programme exécuté)

Code machine Code source assembleur
Address Opcode Operand [Label] operation [operand] [comment]
.org $0000 ; origine du programme en mémoire
0000 B6 00 20 ldaa v1 ; [A] ← [v1]
0003 F6 00 21 ldab v2 ; [B] ← [v2]
0006 3D - mul ; [D] ← [A] × [B] (D = A|B sur 16 bits)
0007 B7 00 22 staa v3H ; [v3H] ← [A]
000A F7 00 23 stab v3L ; [v3L] ← [B]
.org $0020 ; origine des données en mémoire (variables)
0020 0A v1 .byte 10 ; Premier facteur
0021 28 v2 byte 40 ; Deuxième facteur
0022 01 v3H .byte 0 ; Premier octet du résultat
0023 90 v3L .byte 0 ; Deuxième octet du résultat
💡 Point important : L'instruction MUL n'existe que sur le MC6811, pas sur le MC6800 ! Le résultat 16 bits est stocké dans le registre double D (D = A|B).

🔄 Partie B - Instructions de rupture de séquence

B.1 - Introduction aux sauts

🧪 Exercice B.1.1 - Saut inconditionnel (BRA : BRanch Always)

Q5. Exécution pas à pas

Instruction PC avant PC après A Commentaire
ldaa #5 0000 0002 05 A = 5
bra suite 0002 0006 05 PC saute à l'étiquette suite !
ldaa #10 ⚠️ Non exécutée
suite staa resultat 0006 0009 05 Suite du prog.

Conclusion : L'instruction BRA provoque un branchement inconditionnel. Elle saute toujours, sans tester de condition.

🧪 Exercice B.1.2 - Branchement conditionnel (BEQ : Banch if EQqual to zero)

Test 1 : data = 0

Q6. Complétez le tableau

Instruction A Flag Z Branchement ? Chemin pris
ldaa data 00 1 - -
beq estnul 00 1 OUI estnul

Valeur finale dans resultat : 00

Test 2 : data = 5

Q7. Complétez le tableau

Instruction A Flag Z Branchement ?
ldaa data 05 0 -
beq estnul 05 0 NON

Valeur finale dans resultat : 01

Conclusion :

  • BEQ branche si et seulement si Z = 1
  • Cela correspond à un résultat égal à zéro

B.2 - Premières boucles simples

🧪 Exercice B.2.1 - Compte à rebours (BNE : Banch if Not Equal to zero)

Prédictions

Nombre de fois que la boucle s'exécute : 5 fois

Valeur finale de cmpt : 00

Q8. Tableau d'exécution de la boucle

Passage A avant DECA A après DECA Flag Z Branchement ? Commentaire
1 05 04 0 OUI Continue
2 04 03 0 OUI Continue
3 03 02 0 OUI Continue
4 02 01 0 OUI Continue
5 01 00 1 NON Sort de la boucle

Conclusion : Pour sortir de la boucle, il faut que Z = 1 (condition de BNE).

BNE signifie "Branch if Not Equal to zero", donc on continue tant que Z=0 et on sort quand Z=1.

Algorigramme de la boucle

Début [A] ← 5 loop [A] ← [A] - 1 [A] ≠ 0 (Z = 0) ? OUI NON [cmpt] ← [A] Fin
📊 Explication de l'algorigramme :
  • Terminateur (ellipse) : Début et Fin du programme
  • Traitement (rectangle) : Opérations d'affectation ([A] ← 5, [A] ← [A] - 1)
  • Test (losange) : Condition de boucle ([A] ≠ 0 ?)
  • Étiquette (rectangle arrondi) : Point de retour de la boucle (loop)
  • Flèches : Sens d'exécution (verte = OUI, rouge = NON)

B.3 - Boucles avec adressage indexé

🧪 Exercice B.3.1 - Lire trois octets consécutifs dans la mémoire

Q9. Analyse du programme et synthèse dans un tableau

Instruction X (hexa) A (hexa) B (hexa) Donnée pointée Adresse
ldx #data 0020 - - 0A (10) 0020
ldaa 0,X 0020 0A - 0A (10) 0020
inx 0021 0A - 14 (20) 0021
ldab 0,X 0021 0A 14 14 (20) 0021
inx 0022 0A 14 1E (30) 0022
ldaa 0,X 0022 1E 14 1E (30) 0022

Conclusion :

  • Le registre X contient une adresse
  • L'instruction LDAA 0,X signifie : lire la donnée à l'adresse 0 + [X]
  • INX permet de passer à l'octet suivant
💡 Adressage indexé vs adressage étendu :
; Mode étendu (adresse fixe)
ldaa $0020 ; [A] ← [0020] : Affecter le contenu de la mémoire située à l'adresse fixe 0020₁₆ au registre A
; Mode indexé (adresse variable)
ldx #$0020 ; [X] ← 0020₁₆ : Affecter la valeur 0020₁₆ au registre X
ldaa 0,X ; [A] ← [0+[X]] : Affecter le contenu de la mémoire située à l'adresse 0 + [X] = 0020₁₆ au registre A
inx ; [X] ← [X] + 1
ldaa 0,X ; [A] ← [0+[X]] : Affecter le contenu de la mémoire située à l'adresse 0 + [X] = 0021₁₆ au registre A

Avantage : L'adresse change dynamiquement → parfait pour les boucles !

🧪 Exercice B.3.2 - Afficher une chaînes de caractères

Q10. Parcours avec test de fin (version avec compteur)

Passage X (hexa) A (hexa) B (hexa) Caractère
1 0020 48 5 'H'
2 0021 65 4 'e'
3 0022 6C 3 'l'
4 0023 6C 2 'l'
5 0024 6F 1 'o'
⚠️ Problème : Cette méthode nécessite de connaître la longueur de la chaîne à l'avance !
💡 Solution : Détecter le caractère NUL présent dans la chaîne !

Q11. Détection du caractère NUL

Q11.a) Algorigramme complété

L'algorigramme montre :

  1. [X] ← msg (initialisation du pointeur X avec l'adresse du premier caractère du message)
  2. Début de la boucle (loop)
  3. [A] ← [0 + [X]] (lire le caractère pointé par X)
  4. Test : [A] = 0 ? (Z=1 ?)
  5. Si OUI → brancher à suite (fin de chaîne détectée)
  6. Si NON → [X] ← [X] + 1 (passer au caractère suivant)
  7. Retour à loop

Q11.b) Code assembleur complété

ldx #msg ; X ← adresse de msg
loop ldaa 0,X ; A ← [0+[X]]
cmpa #0 ; (A = 0 (Z=1)?)
beq suite ; si oui alors branchement à suite
inx ; sinon X ← X + 1
bra loop ; branchement à loop
suite ... ; suite du programme

Q11.c) Fichier finchaine.asm complété

; SDK6811 - finchaine.asm
; Détection de fin de chaîne
.org $0000
ldx #msg ; X pointe sur le premier caractère
loop ldaa 0,X ; Lire le caractère
cmpa #0 ; Comparer avec NUL
beq suite ; Si NUL, sortir de la boucle
inx ; Sinon, passer au suivant
bra loop ; Continuer
suite ... ; Fin du parcours

.org $0020
msg .str "Hello" ; Chaîne avec NUL automatique

Q11.d) Affichage dans le simulateur

Pour afficher "Hello" dans l'onglet du simulateur, il faut utiliser transférer chaque caractère de la zone mémoire $0020:$0024 à la zone mémoire $FB00:$FB04.

; SDK6811 - hello.asm
; Affichage de la chaîne "Hello" dans le simulateur
ecran .equ $fb00 ; Déclaration d'une constante
.org $0000
ldx #msg ; X pointe sur le premier caractère dans la mémoire
ldy #ecran ; Y pointe sur la position (0,0) sur l'écran du simulateur
loop ldaa 0,X ; Lire le caractère
cmpa #0 ; Comparer avec NUL
beq suite ; Si NUL, sortir de la boucle
inx ; Passer au caractère suivant
iny ; Passer à la position suivante
staa 0,y ; Afficher le caractère à l'écran
bra loop ; Continuer
suite nop ; Fin du parcours

.org $0020
msg .str "Hello"
✅ Points clés à retenir sur les chaînes :
  • Une chaîne de caractères est terminée par le caractère NUL (00₁₆)
  • La directive .str ajoute automatiquement le NUL
  • On utilise le registre X comme pointeur pour parcourir la chaîne
  • On teste chaque caractère pour détecter la fin (cmpa #0)
  • Cette technique évite de devoir connaître la longueur à l'avance

B.4 - Sous-programme

🧪 Exercice B.4.1 - Effacer l'écran du simulateur

Q12.a) Identification du sous-programme

Le sous-programme à entourer s'étend de l'étiquette cls jusqu'à l'instruction rts :

; Sous-programme CLS (Clear Screen) - Efface l'écran en le remplissant d'espaces
cls ldaa #space ; Charge le caractère espace dans l'accumulateur A
ldx #bscreen ; Charge l'adresse du début de l'écran dans le registre X
loop staa 0,x ; Stocke le caractère espace à l'adresse pointée par X
inx ; Incrémente X pour pointer vers la position suivante
cpx #escreen ; Compare X avec l'adresse de fin d'écran
bne loop ; Si X ≠ escreen, continue la boucle
rts ; Fin du sous-programme, retourne à l'appelant

; Programme principal
main bsr cls ; Appel du sous-programme cls
...

Fonction : Efface une zone mémoire en la remplissant d'espaces (caractère ASCII $20).

Q12.b) Instruction d'appel du sous-programme

L'instruction utilisée est : bsr (cls pour le retour)

Signification : BSR = Branch to SubRoutine (Sauter vers un sous-programme)

Fonctionnement :

  • Sauvegarde l'adresse de retour sur la pile
  • Saute à l'étiquette cls
  • Le rts (ReTurn from Subroutine) restaure l'adresse et continue après le bsr

Q12.c) Analyse de la zone effacée

Zone effacée : [$FB00 ; $FB0F] soit 16 octets

Constante Valeur Description
bscreen $FB00 Adresse de début (incluse)
escreen $FB10 Adresse de fin (exclue)
Taille 16 octets $FB10 - $FB00 = 16

Explication : La boucle s'arrête quand X = escreen ($FB10), donc le dernier octet effacé est à $FB0F. Cela représente seulement 16 caractères sur une ligne qui en contient 50.

Q12.d) Modification pour effacer complètement la première ligne

Modification à effectuer : Changer la valeur de escreen

;=== AVANT (efface seulement 16 caractères) ===
bscreen .equ $FB00
escreen .equ $FB10 ❌ Trop petit !
space .equ $20
; === APRÈS (efface les 50 caractères de la ligne) ===
bscreen .equ $FB00
escreen .equ $FB32 ✅ Pour une ligne de 50 caractères
space .equ $20
Une ligne fait 50 caractères. Le calcul est : $FB00 + 50 (décimal) = $FB00 + $32 (hexadécimal) = $FB32

Aucune autre modification n'est nécessaire ! Le sous-programme utilisera automatiquement la nouvelle valeur.

Q12.e) Algorigramme du programme principal

main cls [x] ← adresse_du_message [y] ← adresse_pos(0,0)écran [A] ← [[X]] [A] = 0 (Z = 1)? Oui Non [[y]] ← [A] X ← X + 1, Y ← Y + 1 ploop end
Symboles normalisés utilisés (ISO 5807) :
Ovale : Terminateur (début/fin de programme)
Rectangle arrondi : Traitement (opération, instruction)
Rectangle avec bordures verticales vides : Sous-programme prédéfini
Losange : Décision (test conditionnel)

Note : Les crochets [ ] représentent le contenu d'une adresse mémoire ou d'un registre. Par exemple, [A] signifie le contenu de l'accumulateur A, et [[X]] signifie le contenu de la mémoire à l'adresse pointée par X.

📝 Récapitulatif des flags et instructions

Les 4 flags principaux

Flag Nom Signification Utilisé pour
Z Zero Résultat = 0 Tester l'égalité, détecter la fin de boucle
N Negative Bit 7 = 1 Détecter un nombre négatif (complément à 2)
V oVerflow Dépassement signé Détecter dépassement en arithmétique signée
C Carry Retenue Détecter dépassement en arithmétique non signée

Instructions de branchement

Instruction Signification Condition Utilisation
BRA Branch Always Toujours Saut inconditionnel
BEQ Branch if Equal Z = 1 Brancher si résultat = 0
BNE Branch if Not Equal Z = 0 Brancher si résultat ≠ 0 (boucles)
BSR Branch to SubRoutine - -

Modes d'adressage vus

Mode Syntaxe Exemple Description
Immédiat #valeur ldaa #200 Charger une valeur directe
Étendu adresse ldaa $0020 Lire à une adresse fixe
Indexé offset,X ldaa 0,X Lire à l'adresse offset + [X]
Relatif offset bsr sp Exécuter le sous-programme à partir de [PC] + offset (offset en complément à 2)
💡 Conseil pour les boucles :

Pour créer une boucle efficace :

  1. Initialiser un compteur ou un pointeur
  2. Créer une étiquette pour le début de la boucle
  3. Effectuer les opérations souhaitées
  4. Modifier le compteur/pointeur (DECA, INX...)
  5. Tester la condition de sortie
  6. Brancher conditionnellement au début (BNE, BEQ...)