INF2170
ORGANISATION DES ORDINATEURS ET ASSEMBLEUR
'Quelque part, quelque chose a terriblement mal tourné ...'
Index des problèmes et exemples:
DÉMO DU 20 JUILLET
Cette semaine:
Programmation de BINO (BINARY OUTPUT):
Pour modifier une interruption, vous allez avoir besoin du code
du système d'exploitation, qui est inclus dans le répertoire "/pep7/docu" du
zip. Vous pouvez aussi le trouver sur le site web du cours (Dans la section "Laboratoiressemaine
12").
Nous allons remplacer l'instruction HEXO par BINO:
1) Trouver le marqueur "Opc11111:" dans le code source, qui est le point
d'entrée de HEXO
2) Remplacer le code par celui de BINO
3) Dans le menu "Pep7Redefine
Mnemonics..." remplacer "HEXO" par "BINO", laisser la case "Unary" déseletionnée,
et sauver.
4) Activer la fenètre contenant le "nouveau" système d'exploitation et
selectionner dans le menu: "Pep7Assemble/install
New OS"
5) Pep7 est prêt à assembler notre nouvelle instruction!
;Code de la nouvelle interruption BINO. ;le code en majuscule vient de HEXO, et reste inchangé.. ;le reste vient du programme BINOUT Opc11111: ADDSP d#-2,i ;Allocate storage for number
JSR Get ;Get number from memory
LOADA number,s ;A := number
ADDSP d#2,i ;Deallocate storage for number
loadx d#16,i
rep: asla
brc retenue
charo c#/0/,i
br dec
retenue: charo c#/1/,i
dec: subx d#1,i
brne rep
RTS
|
Notes:
Si vous essayez de programmer une interruption unaire (c-à-d qui ne prend pas de paramètres, par exemple comme l'instruction ASRR), en plus de selectionner la case "Unary" du menu de redéfinition des mnémoniques, vous devez aussi placer le petit bout de code suivant à l'intérieur de l'interruption pour avertir l'OS que l'instruction prend seulement 1 byte:
LOADA TRUE,i ;TRUE=1. Égalité définie au début du code de l'OS STOREA isUnary,d ;Avertir la fonction qui gère les interruption que celle-ci est ;unaire. (par l'intermédiaire d'une variable globale) |
Addition de deux nombres réels (Question 3 de l'examen final Été 2003):
On veut additionner deux nombres réels (virgule flottante, 32 bits): C281E000 + 41858000 = ?
Méthode 1 (plus simple mais plus longue):
On va convertir les nombres en décimal, les additionner, et encoder la solution en nombre à virgule flottante.
C281E000 = 1100 0010 1000 0001 1110 0000 0000 0000
Signe: 1 Nombre négatif
Exposant: 10000101b = 85h
85-7F = 6
Mantisse: 000 0001 1110 0000 0000 0000
1,00 0000 1111
Nombre: - 1,00 0000 1111 * 26 = - 100 0000,1111 = -64,9375d
41858000 = 0100 0001 1000 0101 1000 0000 0000 0000
Signe: 0 Nombre Positif
Exposant: 1000 0011b = 83h
83-7F = 4
Mantisse: 000 0101 1000 0000 0000 0000
1,0000 1011
Nombre: 1,0000 1011 *24 = 1 0000,1011 = 16,6875d
Somme: -64,9375 + 16,6875 = -48,25
-48,25d = - 11 0000,01 = - 1,100 0001 * 25
Signe: Négatif 1
Exposant: 5 5+7F = 84h =
1000 0100b
Mantisse: 100 0001 100 0001 0000 0000
0000 0000
Nombre: 1100 0010 0100 0001 0000 0000 0000 0000b = C2410000h
Méthode 2 (plus rapide mais plus compliquée):
On va ramener les nombres au même exposant et les additionner directement en hexadécimal:
41858000 a comme exposant 83h. On va le faire passer à 85h:
41858000 = 0100 0001 1000 0101 1000
0000 0000 0000
Pour augmenter l'exposant de 2, on doit aussi ajuster la mantisse, c'est à dire
qu'on doit la décaler à droite de 2 cases. Attention, il ne faut pas oublier
qu'il y a un 1 caché à gauche de la virgule (puisque le nombre est normalisé),
qu'on doit faire entrer dans la mantisse en la décalant:
Mantisse originale: 00001011000000000000000
(000 0101 1000 0000 0000 0000)
Mantisse décalée: 01000010110000000000000
(010 0001 0110 0000 0000 0000)
Nombre original: 1,0000 1011 * 24
Nombre décalé: 0,01 0000 1011 * 26
Maintenant que les deux nombres ont le même exposant, on peut les additionner:
Mantisse de C281E000 : 000 0001 1110 0000 0000 0000
Nombre à additionner:
-1,000 0001 1110 0000 0000 0000
0,0100 0010 1100b + (-1,0000 0011 1100b) = 0,42Ch - 1,03Ch
= -0,C10h = -0,1100 0001 0000
Pour faire la soustraction, on oublie la virgule et comme on ne
veut pas avoir de débordement, on va soustraire le plus petit du plus gros:
103C - 042C = 0C10
On se rappelle qu'on a inversé les signes et qu'on a enlevé la
virgule:
Réponse: -0,C10
Nouveau nombre: - 0,1100 0001 0000 * 26 = - 1,100
0001 0000 *25
Signe: Négatif 1
Exposant: 5 5+7F = 84h =
1000 0100b
Mantisse: 100 0001 0000 0000 0000 0000
Nombre encodé: 1100 0010 0100 0001 0000 0000 0000 0000b = C2410000h
Je ne suis pas sûr que la 2e méthode vale la peine dans un examen, compte tenu qu'on vous demande quand même de donner les valeurs décimales des nombres... Mais c'est la méthode la plus rapide et la plus proche du fonctionnement du processeur.
Bonne chance à l'examen!!
DÉMO DU 13 JUILLET
Cette semaine:
Structure des nombres à virgule flottante:
Structure des nombres:
Exceptions aux nombres normalisés:
Convertir 19,328125 en binaire:
Pour convertir la partie du nombre qui est après la virgule, on fait des multiplications successives par 2:
0,328125 * 2 = 0,65625
0,65625 * 2 = 1,3125
0,3125 * 2 =
0,625
-- Attention, on change le 1 en 0 avant de continuer les multiplications
0,625 * 2 = 1,25
0,25
* 2 = 0,5
0,5 * 2 =
1,0
Il ne nous reste plus qu'à prendre les chiffres en vert : 010101
(On remplit de gauche à droite pour la partie décimale, de droite à gauche pour
la partie entière. Autrement dit, on remplit les bits à partir de la virgule.)
Vous savez comment convertir le 19, mais c'est interessant de voir l'analogie avec la méthode d'en haut:
19 / 2 = 9 Reste 1
9 / 2 = 4 Reste 1
4 / 2 = 2 Reste 0
2 / 2 = 1 Reste 0
1 / 2 = 0 Reste 1
On remplit de droite à gauche: 10011
Et on a la réponse: 19,328125d = 10011,010101b
Exemple 1: Convertir -18,75 en nombre binaire à virgule flottante à 32 bits:
18d = 10010b
0,75d = 2-1 + 2-2 = 1/2 + 1/4 = 0,11b
18,75d = 10010,11b
On décale la virgule jusqu'au premier bit (le plus à gauche) valant 1. On se rappelle que déplacer la virgule à gauche revient à diviser le
nombre par 2
(imaginez que les bits à droite de la virgule sont ceux qu'on perdrait en
faisant un shift à droite d'un registre):
10010,11 = 1,001011 * 24
On a maintenant tout ce qu'il nous faut:
Signe: Négatif
Exposant: 4
Mantisse: 001011
On encode pour le format des nombres à virgule flottante:
Signe = 1
Exposant = 4d + 7Fh = 83h = 10000011b
Mantisse = 001 0110 0000 0000 0000 0000
Donc, -18,75 en nombre binaire à virgule flottante à 32 bits est: 1100 0001 1001 0110 0000 0000 0000 0000b ou C196000h
Notes:
Exemple 2: Convertir le nombre à virgule flottante sur 32 bits 3E340000h en nombre décimal:
On trouve sa valeur binaire: 0011
1110 0011 0100 0000 0000 0000 0000
Signe = 0
Exposant = 01111100b = 7Ch
Mantisse = 011 0100 0000 0000 0000 0000
On décode:
Signe = positif
Exposant = 7Ch - 7Fh = -3h = -3d
Mantisse = 01101
On reconstruit le nombre : 1,01101
* 2-3 = 0,00101101
En décimal, 0,00101101b = 2-3 + 2-5 + 2-6
+ 2-8 = 0,125 + 0,03125 + 0,015625 + 0,00390625 = 0,17578125
Donc, le nombre à virgule flottante sur 32 bits 3E320000h vaut 0,17578125 en décimal.
Notes:
Une histoire vraie! :)
En 1994, on découvre un bug plutôt embêtant dans les processeur Pentium 60Mhz à
100Mhz. L'instruction FDIV (Floating point DIVision) retournait, dans certains
cas, des valeurs dont la perte de précision dépassait de loin les limites de
l'acceptable. L'ordinateur pouvait ainsi calculer:
4,195,835 / 3,145,727 * 3,145,727 - 4,195,835 = 256 (et non pas 0...)
Un bug dans un processeur n'est ni chose nouvelle, ni surprenant. Mais la plupart du temps, ils sont mineurs ou n'arrivent que dans des circonstances très rares. Intel décida sur le coup que le bug n'était pas assez important pour échanger les processeurs affectés, sans se douter de tout ce qui allait leur tomber sur la tête.
Jusqu'à la fin du mois de décembre 1994, où Intel ordonna un
rappel des processeurs défectueux, flèches et couteaux ont volé entre des
clients mécontents et Intel, qui même jusqu'à ce jour préfère appeler le
problème de l'instruction FDIV une simple "faille" plutôt qu'un "bug". :)
Voici quelques blagues qu'on retrouve sur internet à ce sujet (en anglais
seulement - désolé) :
Q: What algorithm did Intel use in the Pentium's floating point
divider?
A: "Life is like a box of chocolates." (Source: F. Gump of Intel)
Q : What do you call a series of FDIV instructions on a Pentium?
A1: Successive approximations.
A2: A random number generator.
New Intel slogans for the Pentium:
- Redefining the PC--and Mathematics As Well
- We give you the most megaflops.
- Pentium, the computer your kids can relate to: it can't do fractions either.
Reasons to buy a Pentium
- Math errors add zest to life
- You need an alibi for the I.R.S.
I wonder...
- Did Intel use a buggy Pentium to calculate the quarterly dividends for its
stockholders? An interesting question, to be sure.
- Did Intel use a Pentium to calculate the 27000 years it takes to give an
error?
DÉMO DU 6 JUILLET
Cette semaine:
Comment créer une fonction complète
1) Déterminer quelles sont les entrées et sorties
Entrées: Côté1 et Côté2 (longueurs des 2 côtés différents du
rectangle)
Sortie: Périmètre du rectangle
2) Écrire la fonction avec des "variables" en mode Stack sans s'occuper de rien
loada COTE1,s
asla ;cote1 * 2
storea TEMP,s ;Besoin d'une variable supplémentaire? Pas de problème, on l'invente, et
; on la définira plus tard.
loada COTE2,s
asla ;cote2 * 2
adda TEMP,s ;cote2 + cote1
storea PERIM,s ;stocker la réponse dans la variable qui sera retournée
|
Note:
3) Dessiner la pile
À gauche: index de la pile AVANT l'appel de la fonction
À droite: index de la pile à l'INTÉRIEUR de la fonction
??? | |||
REGX | 0 | Registre X original | |
REGA | 2 | Registre A original | |
TEMP | 4 | Variable temporaire | |
RET | 6 | Adresse de retour | |
-6 | COTE2 | 8 | Longueur du 2e côté |
-4 | COTE1 | 10 | Longueur du 1er côté |
-2 | PERIM | 12 | Périmètre du Rectangle |
0 | ??? |
Notes:
4) Écrire les EQUATEs
REGX: .EQUATE d#0 ;Registre X original
REGA: .EQUATE d#2 ;Registre A original
TEMP: .EQUATE d#4 ;Variable temporaire
RET: .EQUATE d#6 ;Adresse de retour
COTE2: .EQUATE d#8 ;Longueur du 2e coté (Entrée)
COTE1: .EQUATE d#10 ;Longueur du 1er coté (Entrée)
PERIM: .EQUATE d#12 ;Périmètre du rectangle (Sortie)
|
5) Écrire le début et la fin de la fonction
;Début de la fonction:
PerimRec: addsp d#-6,i ;réserver 6 octets pour les variables locales (REGX, REGA et TEMP)
storea REGA,s ;sauver l'état du registre A
storex REGX,s ;sauver l'état du registre X
;Fin de la fonction:
loada RET,s ;récupérer l'adresse de retour
storea COTE1,s ;et la stocker juste au dessus de PERIM
loada REGA,s ;récupérer les registres A et X originaux
loadx REGX,s
addsp d#10,i ;et nettoyer la pile (on veut que SP pointe le nouvel emplacement
;de RET (à COTE1, donc +10)
rts
|
Note:
6) Pour finir, tout rassembler (et placer une description de la fonction!)
REGX: .EQUATE d#0 ;Registre X original
REGA: .EQUATE d#2 ;Registre A original
TEMP: .EQUATE d#4 ;Variable temporaire
RET: .EQUATE d#6 ;Adresse de retour
COTE2: .EQUATE d#8 ;Longueur du 2e coté (Entrée)
COTE1: .EQUATE d#10 ;Longueur du 1er coté (Entrée)
PERIM: .EQUATE d#12 ;Périmètre du rectangle (Sortie)
;Début de la fonction:
PerimRec: addsp d#-6,i ;réserver 6 octets pour les variables locales (REGX, REGA et TEMP)
storea REGA,s ;sauver l'état du registre A
storex REGX,s ;sauver l'état du registre X
loada COTE1,s
asla ;cote1 * 2
storea TEMP,s
loada COTE2,s
asla ;cote2 * 2
adda TEMP,s ;cote2 + cote1
storea PERIM,s ;stocker la réponse dans la variable qui sera retournée
;Fin de la fonction:
loada RET,s ;récupérer l'adresse de retour
storea COTE1,s ;et la stocker juste au dessus de PERIM
loada REGA,s ;récupérer les registres A et X originaux
loadx REGX,s
addsp d#10,i ;et nettoyer la pile (on veut que SP pointe le nouvel emplacement
;de RET (à COTE1, donc +10)
rts
|
Notes:
7) Exemple d'appel de la fonction:
loada lgcote1,d
storea d#-4,s ;stocker le coté 1 à la case COTE1
loada lgcote2,d
storea d#-6,s ;stocker le coté 2 à la case COTE2
addsp d#-6,i ;mettre à jour le pointeur de pile (3 variables -> 6 octets)
jsr PerimRec
loada d#0,s ;A = Périmètre
storea lgperim,d
addsp d#2,i ;nettoyer la pile
|
Notes:
DÉMO DU 29 JUIN
Cette semaine:
DÉMO DU 22 JUIN
Cette semaine:
Changer des minuscules en majuscules, ou inversement:
Valeurs hexadécimales des caractères ASCII:
'0' = 30 ... '9' =
39
'A' = 41 ... 'Z' = 5A
'a' = 61 ... 'z' = 7A
Pour changer un "A" en "a", il suffit donc d'additionner
20 (hex) à sa valeur; pour l'inverse, on soustrait.
Notez qu'il y a une manière plus efficace de faire l'opération. Si on regarde la
valeur binaire des caractères:
'0' = 00110000 ...
'9' = 00111001
'A' = 01000001 ...
'Z' = 01011010
'a' = 01100001 ...
'z' = 01111010
On remarque qu'il y a un seul bit qui fait la différence
entre la lettre majuscule et minuscule.
On peut donc utiliser les instructions AND et OR pour donner la valeur 1 ou 0 au
bit en question, quelle que soit sa valeur précédente. Cela nous évite d'avoir à
tester si on doit faire l'opération ou pas: on la fait de toutes façons.
loadb chaine,i loadx d#-1,i ;-1 parce que l'on rajoute 1 dès le début de la boucle.. loada d#0,i next: addx d#1,i ldbyta ,x breq fin compa h#41,i ;'A' brlt next ;pas une lettre?, on branche compa h#5A,i ;'Z' brle lettre ; c'est une lettre -> on branche compa h#61,i ;'a' brlt next ;pas une lettre?, on branche compa h#7A,i ;'z' brle lettre ;c'est une lettre -> on branche br next ;arrivé ici, ça ne peut plus être une lettre... lettre: anda h#DF,i ;A AND 11011111, pour faire une lettre majuscule ;lettre: ora h#20,i ;A OR 00100000, pour faire une lettre minuscule stbyta ,x ;remplacer le caractère dans la string br next ;et recommencer la boucle... fin: loada chaine,i storea d#-2,s addsp d#-2,i jsr Afficher ;Rajouter la fonction afficher après le programme, ou mettre l'appel ;en commentaire stop chaine: .ASCII /Bonjour TOUT le Monde 123/ .BYTE d#0 ;une fois le programme chargé en mémoire, ce byte (qui est le ;caractère nul) suivra la string "Bonjour". Il ne faut donc déclarer ;AUCUNE variable entre .ASCII et .BYTE !! .END
|
loadb chaine,i loadx d#0,i loada d#0,i nxtinput: chari ,x ;charger le caractère dans la string ldbyta ,x addx d#1,i ;+1 à notre compteur compa h#0A,i ;A = 000A ? (0A est un "ENTER" en ASCII) brne nxtinput ;Non, on a pas appuyé sur ENTER -> on continue subx d#1,i ;X pointait le caractère après le "ENTER". Il faut donc ;le reculer pour qu'il pointe le "ENTER" (0A) loada d#0,i stbyta ,x ;Remplacer le caractère "0A" par "00" pour marquer la fin ;de la string stop chaine: .BLOCK d#50 ;50 caractères maximum .END
|
DÉMO DU 8 JUIN
Cette semaine:
Adressage et taille des instructions:
La plupart des instructions pep7 sont encodées sur 3 octets (1
octet pour l'instruction, 2 pour l'opérande). Il y a cependant quelques
exceptions à cette règle:
Toutes les instructions ne prenant pas d'opérande occuppent un seul octet en
mémoire:
0000 loada var1,d ;Cette instruction prend 3 octets 0003 ldbyta var2,d ; 3 octets 0006 charo h#0A,i ; 3 octets (même si l'opérande n'aurait en fait besoin que d'un octet) 0009 asra ; 1 octet 000A loadb pvar1,i ; 3 octets 000D loadx d#0,i ; 3 octets 0010 loadb ,x ; 1 octet 0011 br fin ; 3 octets 0014 fin: stop ; 1 octet 0015 var1: .WORD d#0 ;Cette variable prend 2 octets 0017 var2: .BYTE d#0 ;Cette variable prend 1 octet 0018 pvar1: .ADDRSS var1 ;Cette variable prend 2 octets 001A tbl: .BLOCK d#10 ;Cette variable prend 10 octets 0024 str: .ASCII /Bonjour/ ;Cette variable prend 7 octets 002B .BYTE d#0 ;Cette variable prend 1 octet (002C) (mémoire indéterminée à partir de cette adresse) .END
|
DÉMO DU 1er JUIN
Cette semaine:
DÉMO DU 25 MAI
Cette semaine:
Voici un petit programme qui imprime un nombre en base binaire.
deci nb,d loada nb,d loadx d#16,i ;le registre X servira de compteur pour la boucle. ;16 bits à imprimer dans le registre A, donc 16 tours de boucle à effectuer rep: asla ;décalage à gauche. le bit de gauche du registre est transféré dans ;le flag de retenue (C) brc retenue ;il y a eu un débordement (C=1)? si oui, on branche à "retenue" ;sinon, on continue. charo c#/0/,i br dec ;branchement inconditionnel à "dec" retenue: charo c#/1/,i dec: subx d#1,i ;décrémenter notre compteur brne rep ;compteur arrive à zéro (Z=1)? si oui, on continue ;sinon, on branche à "rep" stop nb: .WORD d#0 .END
|
Programme de test pour les instructions de shift. (ASRR et ASLR)
init: .EQUATE h#0001
nbrep: .EQUATE d#18
loada init,i ;valeur initiale dans A
loadx nbrep,i ;nombre de répétitions de la boucle
rec: storea nb,d
storea d#-2,s
addsp d#-2,i
jsr Binout ;afficher le nombre en base binaire
charo h#20,i ;espace
charo h#20,i ;espace
hexo nb,d ;afficher le nombre en base hexadécimale
charo h#20,i ;espace
charo h#20,i ;espace
deco nb,d ;afficher le nombre en base décimale
charo h#0A,i ;CR/LF
;Choisir ASLA ou ASRA
asla ;décalage à gauche du registre A
; asra ;décalage à droite du registre A
subx d#1,i ;décrémenter notre compteur de 1
brne rec ;est ce que X = 0 ? si non, aller à "rec"
stop
nb: .WORD d#0
;Inclure la fonction Binout ici ou mettre l'appel en commentaire.
.END
|
;Le probleme du hamburger :) ;Pour preparer un hamburger, on encode 6 bits de la maniere suivante: ;bit0: salade ;bit1: champignons ;bits2&3: nombre de tranches de tomates ;bit4: piments ;bit5: oignons ;note: les numeros de bits se lisent de droite a gauche. le bit0 est donc ;le bit le plus faible. ;Ces bits nous donnent la composition du hamburger, c'est a dire que si ;le bit4 est a 1, on veut des piments dans le hamburger, si il est a 0, ;on ne veut pas de piments. ;Une exception est faite pour les tomates; deux bits sont utilises pour ;donner la quantite de tranches de tomates a mettre (0,1,2,3) ;On veut faire un programme qui prend en entree un nombre et qui imprime ;a l'ecran la composition du hamburger. ;Exemples: ;Le nombre entre est 19 (010011). ;0 1 00 1 1 ;bit5 bit4 bit3&2 bit1 bit0 ;Le hamburger sera compose de: ;piments, champignons et salade. ;Ce que l'on verra a l'ecran: SALADE CHAMPIGNONS PIMENTS ;Le nombre entre est 24 (011000). Le hamburger sera compose de: ;piments et deux tranches de tomate. ;Ce que l'on verra a l'ecran: 2TOMATE(S) PIMENTS deci nb,d loada nb,d anda d#1,i ;A AND 000001 =>A=000001 (bin) -> salade ; A=000000 (bin) -> pas de salade compa d#1,i ;est ce que A=1 ? brne psalade ;pas de salade? charo c#/S/,i charo c#/A/,i charo c#/L/,i charo c#/A/,i charo c#/D/,i charo c#/E/,i charo c#/ /,i psalade: loada nb,d ;le nombre a ete modifie, alors on le recharge anda d#2,i ;A AND 000010 =>A=000010 -> champignons, ; A=000000 -> pas de champignons compa d#2,i ;est ce que A=10 ? brne pchamp ;pas de champignons? charo c#/C/,i charo c#/H/,i charo c#/A/,i charo c#/M/,i charo c#/P/,i charo c#/I/,i charo c#/G/,i charo c#/N/,i charo c#/O/,i charo c#/N/,i charo c#/S/,i charo c#/ /,i pchamp: loada nb,d anda d#12,i ;A AND 001100 =>A=000000 -> pas de tomates, ; A=000100 -> 1 tomate, ; A=001000 -> 2 tomates, ; A=001100 -> 3 tomates, asra ;decalage de A a droite deux fois, pour que asra ;le nombre de tomates arrive dans les ;premiers bits ; compa d#0,i ;on veut des tomates? (instruction pas nécessaire) breq ptomates ;non? ->branchement, oui? -> on continue storea tom,d ;stocker pour imprimer deco tom,d charo c#/T/,i charo c#/O/,i charo c#/M/,i charo c#/A/,i charo c#/T/,i charo c#/E/,i charo c#/(/,i charo c#/S/,i charo c#/)/,i charo c#/ /,i ptomates: loada nb,d anda d#16,i ;A AND 010000 =>A=010000 -> piments, ; A=000000 -> pas de piments ;PAS DE COMPARAISON. On aurait pu suivre le ;meme schema que pour la salade et les champignons ;mais il existe une maniere plus efficace de le ;faire: apres avoir fait le AND, si A vaut zero, ;le flag Z (zero) sera leve (c-a-d que Z vaudra 1). ;On peut donc determiner le resultat de ;l'operation sans faire de comparaison. ;Mais il faut utiliser l'instruction BREQ ;au lieu de BRNE. ;BRNE fait un branchement si Z=0 (nombre non-nul) ;BREQ fait un branchement si Z=1 (zero) breq ppiments ;pas de piments? charo c#/P/,i charo c#/I/,i charo c#/M/,i charo c#/E/,i charo c#/N/,i charo c#/T/,i charo c#/S/,i charo c#/ /,i ppiments: loada nb,d anda d#32,i ;A AND 100000 =>A=100000 -> oignons, ; A=000000 -> pas d'oignons breq poignons ;pas d'oignons? charo c#/O/,i charo c#/I/,i charo c#/G/,i charo c#/N/,i charo c#/O/,i charo c#/N/,i charo c#/S/,i charo c#/ /,i poignons: stop nb: .WORD d#0 tom: .WORD d#0 .END
|
DÉMO DU 18 MAI
Cette semaine:
Exemple de branchement et d'utilisation de l'instruction COMPR:
Programme: Affiche 'ZR' si le nombre entré est zéro, 'NZ' si le nombre entré n'est pas zéro
deci nb,d ;input d'un nombre en base décimale loada nb,d compa d#0,i ;Comparer A avec 0 => A-0 rec: breq zero ;si le flag Z est à 1, branchement à l'étiquette 'zero' charo c#/N/,i charo c#/Z/,i ;imprimer 'NZ' br fin ;branchement inconditionel sur l'étiquette 'fin' zero: charo c#/Z/,i charo c#/R/,i ;imprimer 'ZR' fin: stop nb: .WORD d#0 .END
|
loadb chaine,i ;charger l'adresse de la chaîne dans BP loadx d#0,i ;reset de l'index (X pointe le début de la chaîne) loada d#0,i ;reset de A (le but est surtout de mettre les 8 PREMIERS bits à 0) rep: ldbyta ,x ;charger le caractère à afficher dans les 8 derniers bits de A ;A = 0000 0000 ???? ???? breq finprog ;si on a chargé le caractère nul dans les 8 derniers bits de A, la ;valeur du registre au complet sera nulle (A = 0000 0000 0000 0000) charo ,x ;Afficher le caractère pointé par X + BP addx d#1,i ;+1 à notre compteur br rep ;et recommencer la boucle... finprog: stop chaine: .ASCII /Bonjour/ .BYTE d#0 ;une fois le programme chargé en mémoire, ce byte (qui est le ;caractère nul) suivra la string "Bonjour". Il ne faut donc déclarer ;AUCUNE variable entre .ASCII et .BYTE !! .END
|
Trace d'un BREQ ou BRNE utilisé sans instruction COMPR
Reprenons le bout de code où on charge un caractère dans
le registre A dans le but de savoir si on doit l'afficher ou s'il signale la fin
de la string:
(Voir: Affichage de strings)
...
loada d#0,i
rep: ldbyta ,x
breq
finprog
...
Étape par étape:
1) État des registres à l'origine:
Registres: A = ???? X = 0000 BP = pointe sur le début de la string Status bits (flags): Z (Zéro; 1 -> Le nombre est nul, 0 -> le nombre n'est pas nul) = ? N (Négatif; 1 -> le nombre est négatif, 0 -> le nombre est positif ou nul) = ? C (Carry ou Retenue; 1 -> la dernière opération a fait un débordement de nombre non signé) = ? V (oVerflow ou débordement; 1 -> la dernière opération a fait un débordement de nombre signé) = ? |
2) État des registres après l'exécution de: LOADA d#0,i
Registres: A = 0000 X = 0000 BP = pointe sur le début de la string Status bits (flags): Z = 1 N = 0 C = ? (inchangé) V = ? (inchangé) |
3) État des registres après l'exécution de: LDBYTA
,x
a) Si on a chargé un caractère normal, prenons par exemple le caractère "C"
(valeur ASCII : 43)
Registres: A = 0043 X = 0000 BP = pointe sur le début de la string Status bits (flags): Z = 0 N = 0 C = ? (inchangé) V = ? (inchangé) |
b) Si on a chargé le caractère nul (valeur ASCII: 00)
Registres: A = 0000 X = 0000 BP = pointe sur le début de la string Status bits (flags): Z = 1 N = 0 C = ? (inchangé) V = ? (inchangé) |
4) On arrive pour faire le BREQ
L'intruction BREQ effectue un branchement si le flag Z est à 1, sinon on continue avec l'instruction suivante.
Notes:
[le processeur effectue l'opération A - 0 et utilise le résultat (0) pour mettre les flags à jour] Registres: A = 0000 X = 0000 BP = pointe sur le début de la string Status bits (flags): Z = 1 N = 0 C = 0 V = 0 |
Le résultat du branchement sera donc le même de toute façon...
Registres: A = ??00 X = 0000 BP = pointe sur le début de la string Status bits (flags): Z = 0 si les ?? du registre A ont une valeur non-nulle Z = 1 si on est chanceux et que les ?? du registre A ont une valeur nulle N = 0 si le bit de signe (gauche) des ?? vaut 0 N = 1 si le bit de signe (gauche) des ?? vaut 1 C = ? (inchangé) V = ? (inchangé) |
Autrement dit, quand on fait une opération sur un registre, même si c'est une opération sur 8 bits, le processeur mettra les flags à jour en fonction du contenu du registre AU COMPLET, c-à-d en fonction des 16 bits du registre!
DÉMO DU 11 MAI
Cette semaine:
Vous voulez voir un exemple de programme en particulier? Comprendre une trace en détail? Des questions sur le TP ou la démonstration?
N'hésitez pas à écrire!
Vincent Gournay - vincent2170@sympatico.ca