INF2170

ORGANISATION DES ORDINATEURS ET ASSEMBLEUR


'Quelque part, quelque chose a terriblement mal tourné ...'

 


Index des problèmes et exemples:

[Aller à la semaine actuelle]

 


DÉMOS DU 15 ET 16 DÉCEMBRE

 

Cette semaine:

 

Bonne chance à l'examen et passez de joyeuses fêtes!!!!

 

 


DÉMOS DU 7, 8, 9 ET 10 DÉCEMBRE

 

Cette semaine:

 

 

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.

 

 


DÉMOS DU 30 NOVEMBRE, 1er, 2 ET 3 DÉCEMBRE

 

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:

		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)

 

 


DÉMOS DU 22, 23, 24 ET 25 NOVEMBRE

 

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:

 

 

L'histoire du bug du pentium:

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ÉMOS DU 16, 17, 18 ET 19 NOVEMBRE

 

Cette semaine:

 

 

La fonction Factorielle:

 
		deci    d#-4,s      	;deci directement dans le paramètre
		addsp   d#-4,i
		jsr     Facto
        	deco    d#0,s       	;deco direct de la solution
        	addsp   d#2,i       	;nettoyer la pile
        	stop



;Fonction Factorielle:
;---------------------

REGX:   	.equate d#0         	;vieux registre X
REGA:   	.equate d#2         	;vieux registre A
RET:    	.equate d#4         	;adresse de retour de la fonction
N:      	.equate d#6         	;Le paramètre N
FN:     	.equate d#8         	;N!

Facto:  	addsp   d#-4,i      	;>
        	storea  REGA,s      	;> "Entrée de la fonction"
        	storex  REGX,s      	;>

        	loada   N,s
        	brne    cont        	;N=0?
        	loada   d#1,i       	;oui, retourner 1
        	br      fin

cont:   	suba    d#1,i       	;A = N-1
        	storea  d#-4,s      	;store le nouveau N
        	addsp   d#-4,i      	;on pointe sur N
        	jsr     Facto       	;Appeler Factorielle(N-1)
        	loada   d#0,s       	;récupérer la réponse
        	addsp   d#2,i       	;nettoyer la pile

        	storea  d#-6,s      	;(N-1)!
        	loada   N,s
        	storea  d#-8,s      	;N
        	addsp   d#-8,i
        	jsr     Mulss       	;N * (N-1)!
        	loada   d#2,s       	;A = N!
        	addsp   d#4,i       	;nettoyer la pile

fin:    	storea  FN,s

        	loada   RET,s       	;>
        	storea  N,s         	;>
        	loada   REGA,s      	;> "Sortie de la fonction"
        	loadx   REGX,s      	;>
        	addsp   N,i         	;>
        	rts                 	;>

.END

 

 

La suite de Fibonacci:

 
Fibo:		loada	d#2,s		;charger le nombre passé en paramètre (n)
		breq	zero
		compa	d#1,i
		breq	un

		suba	d#1,i		;n-1
		addsp	d#-4,i		;préparer l'appel de la fonction Fibo()
		storea	d#0,s		;paramètre = n-1
		jsr	Fibo
					;On laisse la réponse sur la pile pour plus tard
		loada	d#4,s		;charger n
		suba	d#2,i		;n-2
		addsp	d#-4,i		;préparer l'appel de la fonction Fibo()
		storea	d#0,s		;paramètre = n-2
		jsr	Fibo

		loada	d#0,s		;récupérer la solution de Fibo(n-2)
		adda	d#2,s		;additionner la solution de Fibo(n-1)
		addsp	d#4,i		;et nettoyer la pile...
		br	suite

zero:		loada	d#0,i		;Fibo(0) = 0
		br	suite
un:		loada	d#1,i		;Fibo(1) = 1

suite:		storea	d#4,s		;stocker la réponse
		loada	d#0,s
		storea	d#2,s		;déplacer le pointeur de retour
		addsp	d#2,i		;et encore une fois, nettoyer la pile
		rts

.END

 

 

 


DÉMOS DU 9, 10, 11 ET 12 NOVEMBRE

 

Cette semaine:

 

 

Routine d'affichage de strings:

 
afficher:	loadb	d#2,s		;charger l'adresse de la chaîne dans BP
		loadx	d#0,i
		loada	d#0,i

rep:		ldbyta	,x		;boucle d'affichage, rien de nouveau...
		breq	finaff
		charo	,x
		addx	d#1,i
		br	rep

finaff:		loada	d#0,s		;charger l'adresse de retour
		storea	d#2,s		;la placer en haut
		addsp	d#2,i		;et nettoyer la pile
		rts

 

 

 

Moyenne de deux nombres:

 
		addsp	d#-4,i		;réserver 2 words sur la pile
		deci	d#0,s		;prendre le premier nombre et le placer à la case 0 de la pile
		deci	d#2,s		;prendre le deuxième nombre et le placer à la case 2 de la pile

		jsr 	moyenne

		addsp	d#4,i		;Redonner les 4 octets qu'on a pris à la pile au début.
					;Cette méthode de nettoyage est simple mais n'est pas très
					;propre. Regardez l'exemple suivant pour voir la bonne manière
					;de faire...
		stop


moyenne:	loada	chaine1,i	;charger dans A l'adresse de la chaîne à afficher
		storea	d#-2,s		;la placer sur la pile
		addsp	d#-2,i		;ajuster le pointeur de pile pour l'adresse de la chaîne qu'on vient
					;d'y mettre
		jsr 	Afficher	;afficher cette chaîne ( "La moyenne de " )
		deco	d#2,s		;afficher le 1er nombre
		loada	chaine2,i
		addsp	d#-2,i
		storea	d#0,s
		jsr	Afficher	;" et "
		deco	d#4,s		;afficher le 2e nombre
		charo	c#/ /,i
		charo	c#/=/,i
		charo	c#/ /,i		;" = "

		loada	d#2,s
		adda	d#4,s		;A = n1+n2
		asra			;A = A/2 (c-à-d, moyenne). Le flag C nous dit si le nombre était impair
		storea	d#2,s		;on utilise l'espace du premier nombre pour afficher
		deco	d#2,s		;afficher le résultat
		brc	retenue		;le nombre était impair -> afficher ",5"
					;Dans ce cas, on peut faire l'instruction de branchement après
					;STOREA et DECO car ces deux instructions ne modifient pas les
					;flags mis à jour par ASRA
		br	finmoy		;pas de retenue? routine terminée. On pourrait aussi
					;faire un RTS directement...
retenue:	charo	c#/,/,i
		charo	c#/5/,i		;",5"
finmoy:		rts


chaine1:	.ASCII	/La moyenne de /
		.BYTE	d#0
chaine2:	.ASCII	/ et /
		.BYTE	d#0

		
.END

 

 

		;[Enlever l'instruction "addsp	d#4,i" à la fin du programme]

finmoy:		loada	d#0,s	;Charger l'adresse de retour de la routine dans A
		storea	d#4,s	;La placer en haut de la pile, à la place du premier nombre qu'on avait donné
				;en paramètre
		addsp	d#4,i	;Après cette instruction, la pile pointe sur l'adresse de retour,
				;et est nettoyée des deux nombres donnés en paramètres.
		rts

 

 

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ÉMOS DU 3 ET 5 NOVEMBRE

 

Cette semaine:

 

 


DÉMOS DU 27, 28 ET 29 OCTOBRE

 

Cette semaine:

 

 


DÉMOS DU 19, 20, 21 ET 22 OCTOBRE

 

Cette semaine:

 

 


DÉMOS DU 12, 13, 14 ET 15 OCTOBRE

 

Cette semaine:

 

 

Exemple d'utilisation des pointeurs et indirections:

Les nombres en rouge à gauche sont les adresses mémoire (en hexadécimal) calculées par l'assembleur.

0000		loadb	ptr,i	;B=0011
0003		loadx	d#0,i	;X=0000

0006		loada	ptr,i	;Charge ptr dans A			-> A = 0011
0009		loada	ptr,d	;Charge le mot pointé par ptr dans A	-> A = 1234
000C		loada	,x	;Charge le mot pointé par B+X dans A	-> A = 1234
000D		ldbyta	ptr,d	;Charge l'octet pointé par ptr dans A	-> A = ..12
				;les ".." signifient que cette partie de A reste inchangée. Donc,
				;dans ce cas, la valeur de A serait "1212" pcq l'instruction précédente
				;a déjà chargé "1234" dans A. Les deux derniers chiffres de A seront
				;écrasés par "12".

0010		stop

0011	ptr:	.WORD	h#1234	;À l'adresse (hexadécimale) "0011", on retrouve le mot "1234"
				;Si on regarde seulement les octets:
				;Adresse 0011 -> Octet "12"
				;Adresse 0012 -> Octet "34"
		 

 

Notes:

 

 

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

 

 

 

Recherche d'un nombre à l'intérieur de plusieurs tableaux

boucle1: loadb	tableau,i	;tableau de pointeurs
	 loadx	index,d		;X=tableau actuel
	 compx	MAXLISTE,i	;arrivé au bout de la liste?
	 breq	pastrouv	;si oui -> on a cherché partout sans le trouver..
	 loadb	,x		;sinon, on charge un ptr sur le prochain tableau à parcourir
	 addx	d#2,i		;ajuster X pour le prochain tour de boucle
	 storex	index,d		;Et le stocker.

	 loadx	d#0,i		;X pointe le 1er nombre du tableau
boucle2: compx	MAXNBS,i	;arrivé au bout du tableau?
	 breq	boucle1

	 loada	,x		;Récupérer un nombre
	 addx	d#2,i		;X pointe la prochaine case du tbl
	 compa	ATROUVER,i	;C'est le nombre qu'on cherchait?
	 brne	boucle2		;Non? alors on recommence

	 loada	msgtrv,i	;Oui -> imprimer l'enplacement du nombre
	 storea	d#-2,s
	 addsp	d#-2,i
	 jsr	Afficher
	 loada	index,d		;Numéro du tableau actuel *2
	 asra			;Numéro du tableau actuel
	 storea	index,d		;On n'a plus besoin de cette variable alors on s'en sert pour afficher
	 deco	index,d
	 charo	c#/,/,i
	 asrx			;diviser par 2 pour avoir l'index actuel
	 storex	index,d
	 deco	index,d		;et afficher aussi
	 br	fin

pastrouv:loada	msgpastr,i
	 storea	d#-2,s
	 addsp	d#-2,i
	 jsr	Afficher
	 fin:	stop


index:   .WORD	d#0

msgtrv:  .ASCII	/Trouve: /
	 .BYTE	d#0
msgpastr:.ASCII	/Pas trouve.../
	 .BYTE	d#0

				;Le tableau de pointeurs :)
tableau: .ADDRSS L1		;Pointeur sur la liste 1 (adresse de la liste 1)
	 .ADDRSS L2		;Pointeur sur la liste 2
	 .ADDRSS L3		;Pointeur sur la liste 3
	 .ADDRSS L4		;Pointeur sur la liste 4

L1:	 .WORD	d#73		;Liste 1
	 .WORD	d#40
	 .WORD	d#456
	 .WORD	d#54
	 .WORD	d#387

L2:	 .WORD	d#75		;Liste 2
	 .WORD	d#15238
	 .WORD	d#53
	 .WORD	d#50
	 .WORD	d#123

L3:	 .WORD	d#81		;Liste 3
	 .WORD	d#788
	 .WORD	d#24
	 .WORD	d#35
	 .WORD	d#12

L4:	 .WORD	d#55		;Liste 4
	 .WORD	d#25
	 .WORD	d#86
	 .WORD	d#5
	 .WORD	d#4


ATROUVER:.EQUATE d#123		;nombre à trouver dans les listes
MAXLISTE:.EQUATE d#8		;nombre de listes de nombres *2 (pour WORDS)
MAXNBS:  .EQUATE d#10		;nombre de nombres par liste *2 (pour WORDS)


;Ajouter la fonction Afficher à la fin

.END

 

 


DÉMOS DU 5, 6, 7 ET 8 OCTOBRE

 

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

 

 

 

Saisie de strings:

 
		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

 

 

 

L'interruption CPUDUMP - Imprimer l'état des registres du processeur

Pour faire une trace du programme sans se servir du debugger:

Modification permanente de l'OS (plus pratique):

  1. Renommez le fichier "Pep7/Rsrc/OsObj.odc" en "Pep7/Rsrc/OsObj.odc.bak"
  2. Récupérez le fichier OsObj.odc et copiez le dans le répertoire "Pep7/Rsrc/", pour remplacer celui qu'on vient de renommer
  3. L'interruption est maintenant chargée dans l'OS automatiquement chaque fois qu'on démarre pep7. Pour vous en servir, déclarez un .BYTE initialisé à h#FF à l'endroit où vous voulez avoir l'état du processeur.

 

Modification temporaire de l'OS:

  1. Récupérez le fichier OsSource3.txt
  2. Ouvrez le dans pep7 et assurez vous que la fenêtre dans laquelle il est ouvert est active
  3. Dans le menu: Pep7 Assemble/Install new OS
  4. Et voila, l'interruption est chargée dans l'OS. Pour vous en servir, déclarez un .BYTE initialisé à h#FF à l'endroit où vous voulez avoir l'état du processeur.

 

Comment ça fonctionne?

Les 3 interruptions de pep7 (DECI, DECO et HEXO) ne se servent pas du 6e bit du code d'instruction (celui qui identifie le registre A ou X). L'assembleur lui donne la valeur 0 par défaut. En lui donnant la valeur 1, on a la possibilité de programmer 3 interruptions supplémentaires.
Le processeur reconnait FF comme une interruption (HEXO ,x). Avant que l'OS commence le traitement de l'interruption, on compare si l'instruction qui a causé l'interruption est exactement FF (contrairement à FB, qui serait la même interruption mais assemblée par l'assembleur.)
L'inconvénient est qu'on ne peut pas nommer cette interruption sans en remplacer une autre...

CPUDUMP donne l'état des registres A, X, B, PC, SP et des status bits. (Le registre IR sera toujours à FF, puisqu'il contient l'instruction exécutée).
Les flags qui sont imprimés en majuscules sont à 1, et ceux en minuscules à 0.

 

Attention:

 

Exemple d'utilisation:

        ...
        loada    d#0,i
        ldbyta
   ,x
        storex   var
        .BYTE    h#FF   ;Appel de l'interruption - Imprimer l'état des registres du processeur
        asrx
        adda     d#123,i
        ...

 

 

Le problème du Hamburger:

 
;Le problème du hamburger :)
;Pour préparer un hamburger, on encode 6 bits de la manière suivante:

;bit0:		salade
;bit1:		champignons
;bits2&3:	nombre de tranches de tomates
;bit4:		piments
;bit5:		oignons

;note: les numéros de bits se lisent de droite à gauche. le bit0 est donc
;le bit le plus faible.

;Ces bits nous donnent la composition du hamburger, c'est à dire que si
;le bit4 est a 1, on veut des piments dans le hamburger, s'il est à 0,
;on ne veut pas de piments.
;Une exception est faite pour les tomates; deux bits sont utilisés pour
;donner le nombre de tranches de tomates à mettre (0,1,2,3)

;On veut faire un programme qui prend en entrée un nombre et qui imprime
;à l'écran la composition du hamburger.

;Exemples:

;Le nombre entré est 19 (010011).

;0	1	00	1	1
;bit5	bit4	bit3&2	bit1	bit0

;Le hamburger sera composé de:
;piments, champignons et salade.
;Ce que l'on verra à l'écran:  Salade  Champignons  Piments

;Le nombre entré est 24 (011000). Le hamburger sera composé de:
;piments et deux tranches de tomate.
;Ce que l'on verra à l'ecran:  2 Tomate(s)  Piments


		deci		nb,d

		loada		msgrep,i
		storea		d#-2,s
		addsp		d#-2,i
		jsr		Afficher

		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?
		loada		msgsal,i
		storea		d#-2,s
		addsp		d#-2,i
		jsr		Afficher

psalade:	loada		nb,d		;le nombre a été modifié, 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?
		loada		msgcham,i
		storea		d#-2,s
		addsp		d#-2,i
		jsr		Afficher

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				;décalage de 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
		loada		msgtom,i
		storea		d#-2,s
		addsp		d#-2,i
		jsr		Afficher
		
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
						;même schéma que pour la salade et les champignons
						;mais il existe une manière plus efficace de le
						;faire: après avoir fait le AND, si A vaut zéro,
						;le flag Z (zéro) sera levé (c-a-d que Z vaudra 1).
						;On peut donc déterminer le résultat de
						;l'opération 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 (zéro)
		breq		ppiments	;pas de piments?
		loada		msgpim,i
		storea		d#-2,s
		addsp		d#-2,i
		jsr		Afficher

ppiments:	loada		nb,d
		anda		d#32,i		;A AND 100000 =>A=100000 -> oignons,
						;		A=000000 -> pas d'oignons
		breq		poignons	;pas d'oignons?
		loada		msgoign,i
		storea		d#-2,s
		addsp		d#-2,i
		jsr		Afficher

poignons:	stop
					
					

nb:		.WORD		d#0
tom:		.WORD		d#0

msgrep:		.ASCII		/Le hamburger sera constitue de:/
		.BYTE		h#0A		;Passer une ligne
		.BYTE		h#00		;Fin de la string
msgsal:		.ASCII		/Salade  /
		.BYTE		h#00
msgcham:	.ASCII		/Champignons  /
		.BYTE		h#00
msgtom:		.ASCII		/ Tomate(s)  /
		.BYTE		h#00
msgpim:		.ASCII		/Piments  /
		.BYTE		h#00
msgoign:	.ASCII		/Oignons/
		.BYTE		h#00


;Ajouter la fonction Afficher à la fin

.END

 

 

 


DÉMOS DU 28, 29, 30 SEPTEMBRE ET 1er OCTOBRE

 

Cette semaine:

 

 


DÉMOS DU 21, 22, 23 ET 24 SEPTEMBRE

 

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

 

 

 

Trace d'un BREQ ou BRNE utilisé sans instruction COMPR

        ...
        loada    d#0,i
EncorA:
 ldbyta   ,x
        breq     FiniA
        ...
 

Étape par étape:

1) État des registres à l'origine:

	Registres:
	A = ????
	X = 0000
	B = 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
	B = 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
	B = 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
	B = pointe sur le début de la string

	Status bits (flags):
	Z = 1
	N = 0
	C = ? (inchangé)
	V = ? (inchangé)

 

4) On arrive au 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
	B = 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
	B = 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!

 

 

Le programme BINOUT:

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

 

 

 


DÉMOS DU 14, 15, 16 ET 17 SEPTEMBRE

 

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