'Quelque part, quelque chose a terriblement mal tourné ...'
deci nb,d ; Saisir un nombre décimal au clavier, et le stocker dans ; la variable "nb". ; Deci stocke le nombre sur 16 bits (1 word), alors la variable ; doit être de type ".WORD" lda nb,d ; On charge ce nombre dans le registre A, car le processeur ; peut seulement faire des opérations sur ce qui est contenu dans ; ses registres. adda 10,i ; On ajoute 10 au nombre qui est contenu dans A sta nb,d ; Finalement, on transfère le contenu de A dans la variable "nb" ; pour la mettre à jour avec la nouvelle valeur. deco nb,d ; "Deco" affiche en décimal le nombre contenu dans "nb" stop ; Arrêt de l'exécution. Sans cette instruction, le processeur ; continuerait à exécuter le reste de la mémoire, variables ; comprises! nb: .WORD 0 ; "nb" est une variable de type WORD, qui est initialisée à 0 au ; début de l'exécution. .END ; ".END" est une directive qui délimite la fin du programme. Remarquez que tout ce qui apparaît après la directive .END est considéré comme un commentaire par l'assembleur. Il n'est donc pas nécessaire de précéder ces lignes par des ";".
stro msg1,d deci nb1,d deci nb2,d lda nb1,d adda nb2,d sta res,d stro msg2,d deco res,d stop msg1: .ASCII "Veuillez entrer deux nombres \n\x00" msg2: .ASCII "\nLa somme de" ; On peut ecrire un message .ASCII " ces deux nombres est \x00" ; sur plusieurs lignes... nb1: .WORD 0 nb2: .WORD 0 res: .WORD 0 .END
ldx -5,i ;On se sert de X comme compteur de boucle boucle: stx temp,d ;Puisqu'on ne peut pas afficher le contenu ; de X directement, il faut le stocker dans une ; variable intermédiaire. deco temp,d ;Afficher le compteur de boucle charo 0x0A,i ;Retour à la ligne addx 1,i cpx 5,i brle boucle ;BRLE = BR si plus petit ou égal stop temp: .WORD 0 ;Variable bidon qui sert à l'affichage .END
deci I,d ;1er nombre (positif!) deci J,d ;2e nombre ldx I,d ;On fera I tours de boucle. X servira de compteur lda 0,i ;Initialiser l'accumulateur encore: adda J,d subx 1,i cpx 0x0000,i brne encore ; Faire un autre tour si non nul sta IJ,d deco IJ,d stop I: .WORD 0 J: .WORD 0 IJ: .WORD 0 .END
;Division entière, calcul du Quotient (Total / Nb) et du Reste ;Entrée des données deci Total,d deci Nb, d lda Total, d ;Registre A pour Total ldx Quot, d ;Registre X pour Quot encore: cpa Nb,d brlt fin ;Aller à fin si zéro suba Nb, d addx 0x0001,i br encore fin: stx Quot, d sta Reste, d deco Quot, d charo 0x0A,i ;Ligne suivante deco Reste, d stop Total: .WORD 0 Nb: .WORD 0 Quot: .WORD 0 Reste: .WORD 0 .END
stro msg1,d chari car,d stro msg2,d charo car,d stro msg3,d ldbytea car,d ;* Ne regardez pas ces quatre instructions de sta -2,s ;* trop près: elles servent à afficher le code subsp 2,i ;* ascii hexadécimal du caractère. call Hexoutb ;* charo '\"',i stop car: .BYTE 0 msg1: .ASCII "Veuillez entrer un caractère: \n\x00" msg2: .ASCII "\nLe caractère entre est: \"\x00" msg3: .ASCII "\"\nEt son code ascii est: \"\x00" ; LA FONCTION HEXOUTB EST INCLUSE DANS LE .TXT
chari car,d charo '\n',i lda 0,i ; Effacer la partie haute du registre A ldbytea car,d ; Et charger le caractere dans la partie basse cpa 0x30,i ;'0' On aurait aussi pu ecrire "cpa '0',i" brlt invalide cpa 0x39,i ;'9' brle chiffre cpa 0x41,i ;'A' brlt invalide cpa 0x5A,i ;'Z' brle lettre cpa 0x61,i ;'a' brlt invalide cpa 0x7A,i ;'z' brle lettre invalide: stro minval,d br fin lettre: stro mlettre,d br fin chiffre: stro mchiffre,d fin: stop car: .BYTE 0 mlettre: .ASCII "Lettre\x00" mchiffre: .ASCII "Chiffre\x00" minval: .ASCII "Invalide\x00" .END
ldx -1,i ;-1 parce que l'on rajoute 1 dès le début lda 0,i next: addx 1,i ldbytea chaine,x breq fin cpa 0x41,i ;'A' brlt next ;pas une lettre?, on branche cpa 0x5A,i ;'Z' brle lettre ; c'est une lettre -> on branche cpa 0x61,i ;'a' brlt next ;pas une lettre?, on branche cpa 0x7A,i ;'z' brle lettre ;c'est une lettre -> on branche br next ;arrivé ici, ça ne peut plus être une lettre... lettre: anda 0xDF,i ;A AND 11011111, pour faire une lettre majuscule ;lettre: ora 0x20,i ;A OR 00100000, pour faire une lettre minuscule stbytea chaine,x ;remplacer le caractère dans la string br next ;et recommencer la boucle... fin: stro chaine,d stop chaine: .ASCII "Bonjour TOUT le Monde 123\x00" .END
Essais intéressants à faire:
init: .EQUATE 0x0001 nbrep: .EQUATE 18 ldx nbrep,i ; Nombre de répétitions de la boucle lda init,i ; Valeur initiale dans A adda 0,i ; Reset les flags call Affres ;Choisir ASLA, ASRA, ROLA ou RORA rec: asla ; Décalage à gauche du registre A ;rec: asra ; Décalage à droite du registre A ;rec: rola ; Rotation à gauche du registre A ;rec: rora ; Rotation à droite du registre A call Affres subx 1,i ; Décrémenter notre compteur de 1 brne rec ; Est ce que X = 0 ? si non, aller à "rec" stop ; Fonction d'affichage d'une ligne de résultat ; Affiche les flags et le nombre contenu dans A. ; Format: Flags, bin, hex, déc. Affres: call PrntFlag charo ' ',i charo ' ',i sta -2,s subsp 2,i call Binout ; Afficher le nombre en base binaire charo ' ',i charo ' ',i sta -2,s subsp 2,i call Hexoutw ; Afficher le nombre en base hexadécimale charo ' ',i charo ' ',i sta -2,s deco -2,s ; Afficher le nombre en base décimale charo '\n',i ; CR/LF ret0 ; LES FONCTIONS SONT INCLUSES DANS LE .TXT
;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, s'il est a 0, ;on ne veut pas de piments. ;Une exception est faite pour les tomates; deux bits sont utilises pour ;donner le nombre 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 stro msgrep,d lda nb,d anda 1,i ;A AND 000001 =>A=000001 (bin) -> salade ; ; A=000000 (bin) -> pas de salade cpa 1,i ;est ce que A=1 ? brne psalade ;pas de salade? stro msgsal,d psalade: lda nb,d ;le nombre a ete modifie, alors on le recharge anda 2,i ;A AND 000010 =>A=000010 -> champignons, ; ; A=000000 -> pas de champignons cpa 2,i ;est ce que A=10 ? brne pchamp ;pas de champignons? stro msgcham,d pchamp: lda nb,d anda 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 ; ;cpa 0,i ;on veut des tomates? (instruction pas nécessaire) breq ptomates ;non? ->branchement, oui? -> on continue sta tom,d ;stocker pour imprimer deco tom,d stro msgtom,d ptomates: lda nb,d anda 16,i ;A AND 010000 =>A=010000 -> piments, ; ; A=000000 -> pas de piments ; ;PAS DE cpa RAISON: 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 cpa raison. ; ;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? stro msgpim,d ppiments: lda nb,d anda 32,i ;A AND 100000 =>A=100000 -> oignons, ; ; A=000000 -> pas d'oignons breq poignons ;pas d'oignons? stro msgoign,d poignons: stop nb: .WORD 0 tom: .WORD 0 msgrep: .ASCII "Le hamburger sera constitue de:\n\x00" msgsal: .ASCII "Salade \x00" msgcham: .ASCII "Champignons \x00" msgtom: .ASCII " Tomate(s) \x00" msgpim: .ASCII "Piments \x00" msgoign: .ASCII "Oignons\x00" .END
ldx 0,i ;X servira de compteur et d'index lda 0,i ;A servira à vérifier si on est à la fin ; de la string rep: ldbytea chaine,x breq fin charo chaine,x addx 1,i br rep fin: stop chaine: .ASCII "Bonjour\x00" .END
ldx 0,i lda 0,i nxtinput: chari chaine,x ;charger le caractère dans la string ldbytea chaine,x breq sortie ;A = 0000 ? (Pour compatibilité avec le mode Batch IO) cpa 0x0A,i ;A = 000A ? (0A est un "ENTER" en ASCII) breq sortie ;Oui -> on sort de la boucle addx 1,i ;+1 à notre compteur br nxtinput sortie: lda 0,i stbytea chaine,x ;Remplacer le caractère "0A" par "00" pour marquer la fin ; ;de la string stro str,d stro chaine,d charo '"',i stop str: .ASCII "La chaine saisie est: \"\x00" chaine: .BLOCK 50 ;50 caractères maximum .END
stro msgQuest,d chari car,d ldbytex car,d andx 0x00DF,i ;Majuscule + effacer partie haute de X cpx 'A',i brlt invalide cpx 'C',i brgt invalide ;'A' <= X <= 'C' subx 'A',i ;A=0, B=1, C=2 aslx ;A=0, B=2, C=4 (aligne selon WORDS) br tablebr,x ;A->casA, B->casB, C->casC casA: lda 10,i adda 10,i br fincase casB: lda -20,i asla br fincase casC: lda 5,i sta temp,d asla ; = *2 asla ; = *4 adda temp,d ; = *5 asla ; = *10 br fincase invalide: stro msgInval,d lda 0,i fincase: sta temp,d stro msgRepon,d deco temp,d stop ;-------------------------Variables----------------------------- car: .BYTE 0 temp: .WORD 0 tablebr: .ADDRSS casA ;Tableau des branchements pour le CASE .ADDRSS casB .ADDRSS casC msgQuest: .ASCII "Voulez vous que pep8 calcule:\n" .ASCII "A) 10 + 10 = ?\n" .ASCII "B) -20 * 2 = ?\n" .ASCII "C) 5 * 10 = ?\n" .ASCII "\nChoix: \x00" msgRepon: .ASCII "\n\nReponse: \x00" msgInval: .ASCII " *Choix invalide*\x00" .END
lda 0,i sta tabact,d ; On commence au 1er tableau deci nb,d nexttab: ldx tabact,d cpx MAXTAB,i ; est ce qu'on a dépassé le dernier tableau? brge pastrouv ldx tabadrs,x ; X = tabadrs[tabact] lda tabact,d adda 2,i ; On incrémente la variable pour le prochain tour de boucle sta tabact,d lda nb,d cpa 0,x breq trouve0 cpa 2,x breq trouve1 cpa 4,x breq trouve2 cpa 6,x breq trouve3 br nexttab pastrouv: stro mpastrou,d br fin trouve0: lda 1,i br trouve trouve1: lda 2,i br trouve trouve2: lda 3,i br trouve trouve3: lda 4,i br trouve trouve: stro mtrouve1,d sta tmp,d deco tmp,d stro mtrouve2,d lda tabact,d asra sta tmp,d deco tmp,d fin: stop tabact: .WORD 0 tmp: .WORD 0 nb: .WORD 0 mtrouve1: .ASCII "Le nombre a ete trouve a la case #\x00" mtrouve2: .ASCII " du tableau #\x00" mpastrou: .ASCII "Le nombre n'a pas ete trouve\x00" tbl1: .WORD 100 .WORD 200 .WORD 300 .WORD 400 tbl2: .WORD 10 .WORD 20 .WORD 30 .WORD 40 tbl3: .WORD -1 .WORD -2 .WORD -3 .WORD -4 tbl4: .WORD 10000 .WORD 20000 .WORD 45 .WORD 240 tbl5: .WORD 33 .WORD 44 .WORD 55 .WORD 66 MAXTAB: .EQUATE 10 ; Nombre de tableaux *2 tabadrs: .ADDRSS tbl1 .ADDRSS tbl2 .ADDRSS tbl3 .ADDRSS tbl4 .ADDRSS tbl5 .END
lda COTE1,s asla ;cote1 * 2 sta TEMP,s lda COTE2,s asla ;cote2 * 2 adda TEMP,s ;cote2 + cote1 sta PERIM,s ;stocker la réponse dans la variable qui sera retournée
À 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 deuxième côté |
-4 | COTE1 | 10 | Longueur du premier côté |
-2 | PERIM | 12 | Périmètre du Rectangle |
0 | ??? |
REGX: .EQUATE 0 ;Registre X original REGA: .EQUATE 2 ;Registre A original TEMP: .EQUATE 4 ;Variable temporaire RET: .EQUATE 6 ;Adresse de retour COTE2: .EQUATE 8 ;Longueur du 2e coté (Entrée) COTE1: .EQUATE 10 ;Longueur du 1er coté (Entrée) PERIM: .EQUATE 12 ;Périmètre du rectangle (Sortie)
;Début de la fonction: PerimRec: subsp 6,i ;réserver 6 octets pour les variables locales (REGX, REGA et TEMP) sta REGA,s ;sauver l'état du registre A stx REGX,s ;sauver l'état du registre X ;Fin de la fonction: lda RET,s ;récupérer l'adresse de retour sta COTE1,s ;et la stocker juste au dessus de PERIM lda REGA,s ;récupérer les registres A et X originaux ldx REGX,s addsp 10,i ;et nettoyer la pile (on veut que SP pointe le nouvel emplacement ; de RET (à COTE1, donc +10) ret0
addsp 10,i ret0
REGX: .EQUATE 0 ;Registre X original REGA: .EQUATE 2 ;Registre A original TEMP: .EQUATE 4 ;Variable temporaire RET: .EQUATE 6 ;Adresse de retour COTE2: .EQUATE 8 ;Longueur du 2e coté (Entrée) COTE1: .EQUATE 10 ;Longueur du 1er coté (Entrée) PERIM: .EQUATE 12 ;Périmètre du rectangle (Sortie) ;Début de la fonction: PerimRec: subsp 6,i ;réserver 6 octets pour les variables locales (REGX, REGA et TEMP) sta REGA,s ;sauver l'état du registre A stx REGX,s ;sauver l'état du registre X lda COTE1,s asla ;cote1 * 2 sta TEMP,s lda COTE2,s asla ;cote2 * 2 adda TEMP,s ;cote2 + cote1 sta PERIM,s ;stocker la réponse dans la variable qui sera retournée ;Fin de la fonction: lda RET,s ;récupérer l'adresse de retour sta COTE1,s ;et la stocker juste au dessus de PERIM lda REGA,s ;récupérer les registres A et X originaux ldx REGX,s addsp 10,i ;et nettoyer la pile (on veut que SP pointe le nouvel emplacement ; de RET (à COTE1, donc +10) ret0
lda lgcote1,d sta -4,s ;stocker le coté 1 à la case COTE1 lda lgcote2,d sta -6,s ;stocker le coté 2 à la case COTE2 subsp 6,i ;mettre à jour le pointeur de pile (3 variables -> 6 octets) call PerimRec lda 0,s ;A = Périmètre sta lgperim,d addsp 2,i ;nettoyer la pile
lda tab,i sta -4,s subsp 4,i call somme deco 0,s addsp 2,i stop tab: .WORD 10 .WORD 20 .WORD 30 ; Fonction somme: additionne les 3 nombres du tableau dont l'adresse est sur la pile ADRSRET: .EQUATE 0 ADRSTAB: .EQUATE 2 SOMME: .EQUATE 4 somme: ldx 0,i lda ADRSTAB,sxf ; "À la case ADRSTAB de la pile, il y a l'adresse d'un tableau. Va chercher le addx 2,i ; contenu de la case X de ce tableau." adda ADRSTAB,sxf ; case 2 addx 2,i adda ADRSTAB,sxf ; case 4 sta SOMME,s lda ADRSRET,s sta ADRSTAB,s ret2 .END
lda prix,i sta -2,s ; TXP_PRIX lda 4,i sta -4,s ; TX_NBITM lda total,i sta -6,s ; TXP_TOT subsp 6,i call TotTaxes deco total,d stop prix: .WORD 100 .WORD 200 .WORD 300 .WORD 400 ; 100+200+300+400 = 1000$ au total. total: .WORD 0 ; ************************************************************************** ; TotTaxes: Fonction qui calcule la taxe sur tous les items d'un tableau ; d'entiers (TXP_PRIX), et qui stocke la somme des prix avec taxe des items ; dans la variable demandée (TXP_TOT) ; Paramètres: ; TXP_PRIX: Pointeur sur un tableau d'entiers contenant les prix des items ; TX_NBITM: Nombre d'items dans ce tableau (TXP_PRIX) ; TXP_TOT: Pointeur sur une variable qui recevra le grand total. ; Fonctionnement de TotTaxes: ; 1) Pour chaque item de TXP_PRIX, on calcule la taxe et on stocke le résultat dans un ; tableau temporaire TX_TBTMP ; 2) On fait la somme des prix de tous les items de TXP_PRIX et toutes les taxes ; calculées de TX_TBTMP ; 3) On stocke cette somme à l'adresse demandée (TXP_TOT) TX_MXITM: .EQUATE 10 ; Nombre max d'items dans le tableau TX_TBTMP ; Pile de la fonction: TX_TBTMP: .EQUATE 0 ; Tableau temporaire stocké directement sur la pile TX_REGA: .EQUATE 20 ; Le tableau précédent fait 20 octets, alors cette ; ; variable est à l'adresse 20 TX_REGX: .EQUATE 22 TX_TMP: .EQUATE 24 TX_RET: .EQUATE 26 TXP_TOT: .EQUATE 28 ; Pointeur sur la variable qui doit contenir le total, ; ; toutes taxes incluses. TX_NBITM: .EQUATE 30 ; Nombre d'éléments dans le tableau TXP_PRIX: .EQUATE 32 ; Pointeur sur le tableau des prix TotTaxes: subsp TX_RET,i sta TX_REGA,s stx TX_REGX,s ldx TX_NBITM,s cpx 0,i brle tx_err cpx TX_MXITM,i brgt tx_err ; 0 < TX_NBITM <= TX_MXITM subx 1,i aslx ; X est positionné sur la dernière case du tableau pointé ; ; par TXP_PRIX tx_taxe: lda TXP_PRIX,sxf ; On se sert du pointeur qui est sur la pile pour accéder ; ; à la case X du tableau de prix asra ; = 50% asra ; = 25% asra ; = 12,5% sta TX_TMP,s asra ; = 6,25% asra ; = 3,125% adda TX_TMP,s ; 12,5% + 3,125% ~= 15% de taxe. La marge d'erreur est très ; ; grande! ;) sta TX_TBTMP,sx ; On stocke le résultat à la case X du tableau temporaire ; ; qu'on a sur la pile. subx 2,i brge tx_taxe ; Boucler tant que X est positif ldx TX_NBITM,s subx 1,i aslx lda 0,i ; On fait la somme de tous les prix et taxes pour calculer le grand total tx_tot: adda TXP_PRIX,sxf ; prix de l'item adda TX_TBTMP,sx ; + taxe sur cet item subx 2,i brge tx_tot sta TXP_TOT,sf ; Et on se sert du pointeur pour stocker le résultat ; ; directement dans la variable du programme appelant tx_err: lda TX_RET,s sta TXP_PRIX,s lda TX_REGA,s ldx TX_REGX,s addsp TXP_PRIX,i ret0 .END
deci -4,s subsp 4,i call Fibo deco 0,s addsp 2,i stop ;******************************************************************************************* ; Fonction Fibonacci récursive RET: .EQUATE 0 N: .EQUATE 2 FN: .EQUATE 4 Fibo: lda N,s ; Si N==0 ou N==1 -> br suite breq zero cpa 1,i breq un suba 1,i sta -4,s subsp 4,i call Fibo ; Fibo(N-1) ; ; On laisse la réponse sur la pile pour plus tard lda 4,s ; La pile est maintenant décalée de 2, alors on ne doit pas ; ; utiliser les .EQUATEs suba 2,i sta -4,s subsp 4,i call Fibo ; Fibo(N-2) lda 0,s ; A = Fibo(N-2) adda 2,s ; A = Fibo(N-2) + Fibo(N-1) addsp 4,i ; Dépiler les retours de Fibo(N-2) et Fibo(N-1) br suite ; 0 et 1: On retoure le paramêtre. Remarquez que le registre A contient déjà la bonne valeur ; alors on pourrait tout simplement brancher directement à "suite"... zero: lda 0,i ; Fibo(0) = 0 br suite un: lda 1,i ; Fibo(1) = 1 ; Ici, le registre A doit contenir la réponse. suite: sta FN,s lda RET,s sta N,s ; Déplacer l'adresse de retour ret2 ; = ADDSP 2,i suivi de RET0 .END
deci -4,s subsp 4,i call Facto deco 0,s addsp 2,i stop RET: .EQUATE 0 N: .EQUATE 2 FN: .EQUATE 4 Facto: lda N,s cpa 1,i breq un ; Facto(1) = 1 suba 1,i sta -4,s subsp 4,i call Facto ; Facto(n-1) lda 0,s addsp 2,i sta -8,s lda N,s sta -6,s subsp 8,i call Mulss ; n * Facto(n-1) lda 2,s addsp 4,i un: sta FN,s lda RET,s sta N,s ret2 ; LA FONCTION MULSS EST INCLUSE DANS LE .TXT
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 la partie entière de 1 à 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
Si on veut retrouver le nombre original à partir du nombre binaire:
10011,010101b = 24 + 21 + 20 + 2-2 + 2-4 + 2-6 = 16 + 2 + 1 + 0,25 + 0,0625 + 0.015625 = 19,328125
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
On trouve sa valeur binaire: 0011 1110 0011 0100 0000 0000 0000 0000
Signe = 0
Exposant = 0111 1100
Mantisse = 011 0100 0000 0000 0000 0000
On décode:
Signe = Positif
Exposant = 7Ch - 7Fh = -3h = -3d
Mantisse = 01101b
On reconstruit le nombre:
+ 1,01101b * 2-3 = + 0,00101101b
En décimal, 0,00101101b = 2-3 + 2-5 + 2-6 + 2-8 = 0,17578125
Donc, le nombre à virgule flottante sur 32 bits 3E320000h vaut 0,17578125 en décimal.
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 (au lieu de 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?
; Ce programme affiche les 16 premières puissances de 2 en décimal. ; Mais il ne fonctionne pas... Trouvez les bugs! ;Déclaration des variables nombre: .WORD 0x0001 ;Programme principal lda nombre,i ;Initialisation du nombre original ldx 0x16,i ;16 tours de boucle ;Boucle d'affichage des puissances ;À l'entrée, X contient le nb de tours de boucle à faire, ; et A le nombre à partir duquel on commence l'affichage. boucle: deco nombre,d ;Afficher le nombre actuel charo 0x0A,i ;Prochaine ligne asla ;A*2 sta nombre,d subx 1,i ;Prochain tour de boucle cpx 0,i breq boucle ;Continuer la boucle tant que X != 0 stop .END
ldx str,d ;X=???? ... str: .ASCII "ALLO\x00"
deco 0x100,i
.ASCII "xyz\x00" .END
.ASCII "AB" .WORD 0x4142 .BYTE 0x41 .BYTE 0x42 .BYTE 'A' .BYTE 'B' .WORD 16706
deci nb,d ;On récupère un nombre au clavier brlt point1 ;??? breq point2 ;??? point3: ... ;???
A: Accumulateur (16 bits)
Sert à effectuer les opérations logiques, arithmétiques ou d'accès mémoire.
X: Index (16 bits)
Comme pour le registre A, et peut aussi servir à indexer la mémoire quand on l'utilise avec le mode indexé.
SP: Stack Pointer (16 bits)
Pointe le sommet de la pile. (Attention: la pile grandit à l'envers!)
PC: Program Counter (16 bits)
Pointe la prochaine instruction à exécuter.
IR: Instruction Register (24 bits)
Contient l'instruction actuellement en cours d'exécution
NZVC: Status Bits (4 bits)
Registre d'état du processeur. Donne des renseignements sur le résultat des instructions exécutées par le processeur. Contient les flags Négatif, Zéro, oVerflow et Carry.
Les deux servent à indiquer un débordement. L'Overflow (V=1) indique un débordement en supposant que le nombre était SIGNÉ, et le Carry (C=1) en supposant que le nombre était NON-SIGNÉ.
Un débordement arrive quand l'opération fait sortir le nombre des bornes. Par exemple, sur 16 bits, un nombre signé doit être compris entre -32768 (8000 hex) et 32767 (7FFF hex). Un nombre non signé, (sur 16 bits aussi) doit être compris entre 0 (0000 hex) et 65535 (FFFF hex).
Exemple d'Overflow (V=1) avec la soustraction: (-32768) - 1
Exemple de Carry (C=1) avec la soustraction: 0 - 1
Exemple où V=1 et C=1 (Attention, dans cet exemple, les nombres n'ont pas la même valeur selon qu'on les considère comme signés ou non signés!): 9000 (hex) + 8000 (hex)
X = 414C
(414C correspond à "AL")
En décimal: -32768 à 32767
En hexadécimal: 8000 à 7FFF
Les registres A et X sont des registres "généraux", c'est à dire que la plupart des instructions vont en avoir besoin. Ils peuvent servir à effectuer des opérations logiques (NOTR, ORR, ANDR) et arithmétiques (ADDR, SUBR, ASLR, ASRR, RORR, ROLR, NEGR), ou des opérations de chargement/stockage (LDR, STR, LDBYTER, STBYTER). En plus de pouvoir faire les mêmes opérations que le registre A, le registre X peut aussi servir à indexer la mémoire à partir d'un pointeur de base qui est fourni en opérande: c'est le mode indexé (,x).
(Notez qu'on pourrait tout aussi bien décider de mettre RORR, ROLR et ASLR dans la catégorie des instructions logiques...)
"256" sera affiché (sans les guillemets)
L'instruction demandait d'afficher le nombre hexadécimal 100 en décimal.
Programme assemblé: 78 79 7A 00
Ce qui sera exécuté:
ADDX 0x797A,i STOPLe processeur ne peut pas faire la différence entre le code et les variables. Pour lui, ce ne sont que des octets en mémoire. Tout ce qui se retrouve sous son registre PC sera décodé et exécuté comme une instruction.
Il n'y a aucune différence. Toutes les déclarations réserveront 16 bits initialisés avec la valeur hexadécimale 4142.
Parce que c'est ce "caractère" qui marque la fin de la chaine. Sans lui, l'instruction d'affichage (STRO) imprimerait la mémoire au complet, ou au moins jusqu'à ce qu'elle rencontre un autre caractère nul.
deci nb,d ;On récupère un nombre au clavier brlt point1 ;nb est négatif? (BRLT = BR si N=1) breq point2 ;nb est nul? (BREQ = BR si Z=1) point3: ... ;nb est positif
; On veut créer une liste chaînée contenant 2 éléments. ; Chaque élément contient 2 entiers et un pointeur sur l'élément suivant ; ; Structure d'un élément: ; champ1: int ; champ2: int ; champ3: pointeur sur un élément ; ; ; On veut que la liste soit dans cet état: ; (tête de liste) ----> (123;456) ----> (789;987) ----| ; État initial de la liste: ; (tête de liste) ----| ; ------------- Créer le 1er élément lda 6,i sta -2,s ; Allouer 6 octets lda ptr,i sta -4,s ; Et stocker le pointeur sur ces 6 octets dans la variable ptr. subsp 4,i call new ; new(ADRESSE(ptr),6) ldx ptr,d stx tete,d ; Nouvelle tête de liste ; État de la liste: ; (tête de liste) ----> (?;?) ----? lda 123,i sta 0,x ; élément1,champ1 lda 456,i sta 2,x ; élément1,champ2 lda 0,i sta 4,x ; élément1,champ3 (on initialise avec un pointeur nul.) ; État de la liste: ; (tête de liste) ----> (123;456) ----| ; ------------- Créer le 2e élément lda 6,i sta -2,s ; Allouer 6 octets lda ptr,i sta -4,s ; Et stocker le pointeur sur ces 6 octets dans la variable ptr. subsp 4,i call new ; new(ADRESSE(ptr),6) ldx tete,d lda ptr,d sta 4,x ; élément1, champ3 (on enchaine l'élément2 après l'élément1) ; État de la liste: ; (tête de liste) ----> (123;456) ----> (?;?) ----? ldx 4,x ; On suit la liste lda 789,i sta 0,x ; élément2,champ1 lda 987,i sta 2,x ; élément2,champ2 lda 0,i sta 4,x ; élément2,champ3 ; État de la liste: ; (tête de liste) ----> (123;456) ----> (789;987) ----| ; ----------------- Affichage des 2 éléments ldx tete,d ; Élément1 deco 0,x charo ';',i deco 2,x charo '\n',i ldx 4,x ; Élément2 deco 0,x charo ';',i deco 2,x charo '\n',i stop ptr: .WORD 0 ; Variable temporaire recevant l'adresse de la mémoire allouée tete: .WORD 0 ; Tête de liste ; ;Sous-programme new ; Alloue la taille demandÈe et place l'adresse de la ; zone dans le pointeur dont l'adresse est passéÈe en paramËtre NvieuxX: .EQUATE 0 ; sauvegarde X NvieuxA: .EQUATE 2 ; sauvegarde A NadRet: .EQUATE 4 ; adresse retour Npoint: .EQUATE 6 ; adresse pointeur ? remplir Ntaille: .EQUATE 8 ; taille requise ; new: SUBSP 4,i ; espace local STA NvieuxA,s ; sauve STX NvieuxX,s ; sauve LDA heappnt,d ; STA Npoint,sf ; LDA Ntaille,s ; taille du noeud ADDA 1,i ; arrondir ? pair ANDA 0xFFFE,i ; ADDA heappnt,d ; CPA heaplmt,i ; BRGT new0 ; STA heappnt,d ; prÈparer heappnt pour appel suivant BR new1 ; new0: LDA 0,i ; STA Npoint,sf ; pointeur NULL new1: LDA NadRet,s ; dÈplace adresse retour STA Ntaille,s ; LDA NvieuxA,s ; restaure LDX NvieuxX,s ; ADDSP 8,i ; nettoyer pile RET0 heappnt: .ADDRSS heap ; initialiser heappnt heap: .BLOCK 255 ; espace heap; selon système .BLOCK 255 ; espace heap; selon système heaplmt: .BYTE 0 ; .END
Questions et commentaires:
Vincent Gournay - vincent2170@sympatico.ca