Assembleur 8088

Document créé par P. TRAU, en octobre 1988, et remis à jour en janvier 97. Eh oui, c'est bien vieux. Mais ce n'est pas complètement dépassé, je le garde. Pour toute remarque, ou proposition de mise à jour, envoyez moi un



MEMENTO ASSEMBLEUR 8088 - x86

Ce document date de fin 88 (eh oui, je vieillis). Je l'ai remis là presque tel quel (rtf2html est quand même passé par là) pour l'avoir disponible (je ne retrouve jamais mes documents papier).

Si vous êtes sous Linux, regardez le mini-howto assembleur


TRAU Patrick 30.10.88

1. registres du 8088

Ils ont tous une taille de 16 bits

1.1. Registres généraux :

1.2. Registres segment :

1.3. registre d'état :

  F   E   D   C   B   A   9   8   7   6   5   4   3   2   1   0
+---------------------------------------------------------------+
|   |   |   |   | O | D | I | T | S | Z |   | A |   | P |   | C |
+---------------------------------------------------------------+
Overflow (dépassement de capacité nb signé) / Decrémentation index/ Interruptions autorisées / Trap (pas à pas) / Signe négatif / Zéro / retenue Auxiliaire (entre le 4è et 5è bit) / nb Pair de 1 / Carry (retenue)

2. adressages

Souvent, on doit donner deux adresses (ex MOV). Ce sera toujours :
Destination,Source

Mais on ne peut pas faire de transfert direct de mémoire à mémoireni d'adressage immédiat à destination d'un registre segment (passer par un registre général). La destination ne peut être un adressage immédiat.

Attention, en mémoire on stocke toujours l'octet de poids faible en premier. De même on stocke un offset avant un segment.

3. instructions

3.1. transferts

notations :
d destrination - s source
PA pas d'adresse à donner (registres fixés d'avance)
TA tous adressages (immédiat registre direct indirect-indexé)
RM adressage registre ou mémoire (direct indirect-indexé)
AF ne modifie aucun Flag MF modifie Flags O S Z A P C
MOV d,s : copie de la source dans la destination          TA AF
XCHG d,s : échange d,s (impossible sur segment)           RM AF
PUSH s : empilage (toujours 16 bits)                      RM AF
POP d : dépilage                                          RM AF
XLAT : copie d=AL, s=[BX+AL]                              PA AF

IN d,s : lecture port d=AL ou AX, s=ndeg. de port si<256 ou
				    DX si entre 0 et 65535        AF
OUT d,s : écriture port à partir de l'accumulateur                AF

LEA d,s : chargement d'un offset LEA BX,[DI+5]=MOV BX,DI ADD BX,5
cette instruction est surtout utile en MASM (LEA BX,truc[DI]      TA AF

LDS d,s : chargement double mot dans DS et d (d=registre général,s=TA)
idem MOV d,s MOV AX,s[2] MOV DS,AX                                TA AF
LES d,s : idem LDS mais avec ES

PUSHF : empilage registre d'état                                   PA AF
POPF : dépilage flags (remet les flags dans l'état sauvé)          PA MF
LAHF : copie partie basse flags dans AH (compatible 8085)          PA AF
SAHF : copie AH dans partie basse du registre d'état               PA MF

3.2. opérations booléennes

flags (sauf NOT) : S,Z,P suivant le résultat; O,C=0; A=?

NOT d : complément à 1                                        RM AF
AND d,s : d := d ET s (ET bit à bit)                          TA MF
OR d,s : OU                                                   TA MF
XOR d,s : ou exclusif (un ou l'autre mais pas les deux)       TA MF
TEST d,s : nulle part := d ET s (sert aux tests)              TA MF

3.3. opérations arithmétiques

ADD d,s : d:=d+s                                              TA MF
ADC d,s : d:=d+s+C (retenue opération précédente)             TA MF
INC d : d:=d+1 modifie tous flags sauf C (utiliser Z)         TA MF
AAA : ajustement ascii addition précédente dans AL            PA MF
  masque les 4 bits hauts, si C=1 alors AH=01 sinon AH=00
  modifie flags A et C; O S Z P=?
DAA : ajustement DCB addition précédente (ne modifie pas AH)  PA MF
   flag O=?
SUB d,s : d:=d-s _                                            TA MF
SBB d,s : d:=d-s-C                                            TA MF
DEC d : d:=d-1 (MF sauf C)                                    TA MF
NEG d : d:=-d (MF, C=1)                                       TA MF
AAS : ajustement ascii soustraction précédente (AL)           TA MF
    MF mais seuls A et C significatifs
DAS : ajustement DCB soustraction précédente (AL) O=?         TA MF
les ajustements ne marchent bien que sur des résultats positifs
CMP d,s : nulle part:=d-s (sert aux tests)                    TA MF

MUL s : AX:=AL*s ou DX|AX:=AX*s C=O=1 si partie haute <>0     RM MF
	  les autres flags sont indéfinis
IMUL s : idem sur entiers signés                              RM MF
AAM ajustement ascii MUL précédent (AX uniquement)            PA MF
      flags : seuls P,Z et S sont significatifs

DIV s : (AL:=AX div s, AH:=reste) ou (AX:=DX|AX div s, DX:=reste)
    aucun flag n'est significatif                             RM MF
IDIV s : idem entiers signés                                  RM MF
AAD ajustement ascii à faire juste AVANT DIV. AH doit valoir 0
      flags : seuls P,Z et S sont significatifs               PA MF

3.4. conversions

CBW : convertit l'entier signé dans AL en mot dans AX         PA AF
CWD :    "          "      "     "  AX en double mot DX|AX    PA AF

Ces conversions sont souvent utilisées avant IDIV

3.5. décalages

Acceptent les adressages registres ou mémoire, le nombre de décalages est soit 1, soit contenu dans CL (notation SAR AX,1 ou SAR AX,CL).

Flags : O si changement de signe, C, les autres non modifiés sauf pour SAR

	    +----+     +------------------------------+
SHL/SAL     | CF |<----|                              |<----- 0
	    +----+     +------------------------------+

		       +------------------------------+     +----+
SHR            0 ----->|                              |---->| CF |
		       +------------------------------+     +----+

		       +------------------------------+     +----+
SAR                +-->|                              |---->| CF |
		   |   +------------------------------+     +----+
		   +-----+

	    +----+     +------------------------------+
ROL         | CF |<----|                              |<--+
	    +----+  |  +------------------------------+   |
		    +-------------------------------------+

		       +------------------------------+     +----+
ROR                +-->|                              |---->| CF |
		   |   +------------------------------+  |  +----+
		   +-------------------------------------+

	    +----+     +------------------------------+
RCL         | CF |<----|                              |<--+
	    +----+     +------------------------------+   |
	       +------------------------------------------+

		       +------------------------------+     +----+
RCR                +-->|                              |---->| CF |
		   |   +------------------------------+     +----+
		   +------------------------------------------+

3.6. instructions de chaînes (blocs)

La chaîne source se trouve dans DS:SI sauf si l'on précise un autre segment, la destination est toujours dans ES:DI. On incrémente (ou décrémente si le flag D vaut 1) automatiquement après l'instruction SI et DI de 1 ou 2 suivant le type de l'instruction (Byte ou Word).

Ces opérations sont généralement précédées d'un préfixe de type REP, CX contenant le nombre d'itérations (boucle FOR pour REP, REPEAT pour REPZ REPNZ).

MOVSB/MOVSW : déplacement                                           PA AF
STOSB/STOSW : copie de AL ou AX dans le bloc destination            PA AF
LODSB/LODSW : copie source dans AL ou AX (avec LOOP par ex)         PA AF
CMPSB/CMPSW : comparaison                                           PA MF
SCASB/SCASW : comparaison destination, AL ou AX (recherche)         PA MF
en MASM on peut écrire MOVS dest,source, il choisira B ou W suivant le type de variable et mettra si nécessaire un préfixe de segment pour la source.

3.7. structures de boucles (AF)

préfixes de répétition (boucles sur UNE instruction) :

3.8. boucles sur plusieurs instructions :

3.9. sauts (AF)

JMP adresse : saut inconditionnel.

L'adresse peut être donnée soit :

On peut (pour un saut indirect par variable) utiliser tous les adressages de variables.

Sauts conditionnels (uniquement saut court -128 +127 octets) :

3.10. sous-programmes - interruptions

CALL adresse : appel de sous-programme. Accepte l'adressage immédiat ou indirect (voir JMP).

RET n : retour de sous programme. N représente le nombre d'octets à dépiler après l'adresse de retour (nombre PAIR, 0 par défaut). Sous DEBUG, utiliser RETF pour un call far (MASM le fait automatiquement).

INT n : appel d'interruption logicielle (sous-programmes du DOS par ex). N entre 0 et 255. Cet appel sauve en plus de l'adresse de retour (FAR) le registre d'état. Les flags T et I sont mis à 0 (masque interruptions et debug).

INTO : appel de INT 4 uniquement si flag O=1

IRET : retour d'interruption (avec récupération registre d'état).

3.11. contrôle du processeur

modifications registre d'état (PA) :                  _
STC : C:=1            CLC : C:=0             CMC : C:=C
STD : D:=1 (décrémentation)       CLD : D:=0 (incrémentation)
STI : I:=1 (autorise les interruptions) CLI : I:=0 (masquage)

3.12. divers :

4. fonctions du DOS

On les obtient en mettant le numéro de fonction dans AH et appeler INT 21h.

Les autres fonctions servent surtout aux manipulations defichiers, de dates...

5. un assembleur simple : MASM

On ne donne ici que des informations de base. Les valeurs sont par défaut en décimal, terminées par h en hexa ou B en binaire. Une valeur (même hexa) doit commencer par un chiffre (0 par exemple).

Tout ce qui suit un ; est considéré comme commentaire

5.1. Déclaration d'un segment

nom SEGMENT options
    .........
nom ENDS
Les options peuvent être entre autre PUBLIC STACK ou AT valeur_segment. Un .COM ne peut avoir qu'un seul segment. Un .EXE doit avoir un segment STACK, il sera automatiquement mis dans SS à l'éxécution

5.2. Constantes

EQU ex: int_dos EQU 21h ou même param EQU [BP + debut_pile]

5.3. déclaration de variables

nom TYPE valeur_initiale

TYPE = DB (byte) DW (word) DD (double word). Le nombre de valeurs initiales permet de donner la place réservée à la variable (? pour ne pas initialiser, DUP ( ) pour initialiser une zone).
ex:  message DB 'ceci est un message pour la Fdeg. 09',10,13,'$'
     tableau DW 100h DUP(?)
MOV AX,variable sera assemblé en MOV AX,[adresse]. Pour utiliser l'adresse d'une variable utiliser OFFSET (MOV BX,OFFSET variable pour utiliser ensuite un adressage indexé)

MASM mettra automatiquement les préfixes de segments, à condition de lui dire ce qu'il y a dans les registres segments par ASSUME :

ASSUME CS:code, DS:donnees, ES:nothing, SS:pile
ASSUME est valable jusqu'au prochain assume sur le même registre segment.

Attention, cette directive ne met pas de valeurs dans les registres, il faut les mettre (MOV AX,donnees mov DS,AX). Si la bonne valeur n'est pas dans le registre, il se passera n'importe quoi. Un préfixe de segment ne sera mis automatiquement que si la variable a déjà été déclarée avant. Sinon il suppose que la variable sera dans le segment par défaut (ou celui précisé), et donnera une erreur si elle n'y est pas.

5.4. procédures

nom PROC type ; type = NEAR (défaut) ou FAR. Le RET en dépendra
......
nom ENDP

5.5. changement de type

nouveau_type PTR variable

Nouveau_type= BYTE WORD DWORD NEAR FAR...

ex : offset_v  DW 1234
     segment_v DW 5678
     LDS BX,DWORD PTR offset_v
autre solution :
   adresse_v LABEL DWORD
   offset_v  DW 1234
   segment_v DW 5678
   LDS BX,adresse_v
LABEL donne l'adresse actuelle et un type à une variable, sans prendre de place.

5.6. assemblages séparés

(pour des .EXE créés par MASM, MS PASCAL, MS FORTRAN ou MS C)

;fichier PROC.ASM                   ;fichier PRINC.ASM
code_proc SEGMENT                   code SEGMENT
PUBLIC add                          ASSUME CS:code
add PROC FAR                        EXTERN add:FAR
    ADD AX,BX                       debut:  mov ax,100h
    RET ;automatiquement FAR                mov bx,234h
add ENDP                                    call add
code_proc ENDS                      code ENDS
END ;ne rien rajouter, ce n'est     END debut ;1ère instruction du
    ;pas le programme principal               ;programme principal

on apelle MASM proc;
	  MASM princ;
	  LINK princ+proc,proc; 

5.7. macros

(comme un sous-programme, mais le code est recopié à chaque fois)
	nom MACRO parametres_formels
	    ......
	ENDM ;; ne pas redonner ici le nom, ça ferait
		récursivité
Les paramètres seront recopiés tels quels (idem EQU). On peut déclarer LOCAL label pour que le label soit local : un autre nom est donné à chaque appel.

ex : zero MACRO var,nb
     LOCAL boucle     ;;pas de commentaire entre MACRO et LOCAL!
	    mov DI,nb
	    inc DI               ;;REP STOS VAR serait mieux mais
     boucle:mov var[DI],0        ;;n'utilise pas LOCAL
	    dec DI
	    jz boucle
     ENDM
On peut l'appeler par ZERO tableau,AX ou ZERO tableau,16

Rq : on peut regrouper des macros courantes dans un fichier appelé par :
INCLUDE nom_de_fichier

5.8. cas particulier des .COM

ils prennent moins de place, se chargent plus rapidement mais sont limités à un segment de code de 64Ko. Ils doivent automatiquement commencer en 100h (utiliser ORG 100h), les 256 premiers octets servent au DOS. Ne déclarer qu'un seul segment. CREE_COM nomfic créera le fichier .COM
exemple : code SEGMENT
	  assume CS:code
	  ORG 100h
	  debut: ......
		 ......
	  code ENDS
	  END debut ; debut DOIT valoir 100h
à l'initialisation, CS est mis au premier endroit libre. DS, ES, SS sont normalement mis égaux à CS (suivant la version du DOS).


Contents table des matières

P. TRAU, ULP, janvier 97