Assemblador

You might also like

You are on page 1of 96

Assemblador

Programació en llenguatge assemblador


Jordi García Almiñana
Albert Riego Pérez

P06/05096/00295
Mòdul 4
© FUOC • P06/05096/00295 • Mòdul 4 2 Assemblador
© FUOC • P06/05096/00295 • Mòdul 4 Assemblador

Índex

Introducció ................................................................................................ 5

Objectius ..................................................................................................... 6

1. Conceptes previs .................................................................................. 7


1.1. Elements del computador ................................................................. 7
1.2. Llenguatges de programació ............................................................. 7
1.3. El llenguatge d’assemblador de l’i8086 ............................................ 8
1.3.1. Registres principals de l’i8086 ............................................... 8
1.3.2. Entorn de programació ......................................................... 9
1.3.3. Instal·lació del programari .................................................... 10
1.3.4. Treballar amb l’entorn de programació ................................ 12
1.3.5. Fer un programa amb l’entorn de programació ................... 12

2. Elements bàsics d’un programa en assemblador ........................ 20


2.1. Instruccions ...................................................................................... 20
2.1.1. Tipus d’instruccions .............................................................. 20
2.1.2. Operands de les instruccions ................................................ 20
2.2. Variables de memòria ....................................................................... 24
2.2.1. Declaració i us de variables ................................................... 24
2.2.2. Modes d’adreçament ............................................................. 26
2.3. Pila .................................................................................................... 29

3. Control de flux d’un programa en assemblador ........................ 32


3.1. Estructura condicional ...................................................................... 32
3.1.1. Comparacions de valors amb signe ...................................... 35
3.1.2. Comparacions de valors sense signe ..................................... 35
3.1.3. Altres instruccions de salt ..................................................... 36
3.2. Estructura iterativa ............................................................................ 36

4. Subrutines ............................................................................................. 40
4.1. Crida i retorn a una subrutina .......................................................... 40
4.2. Pas de paràmetres i retorn de resultats ............................................. 43
4.3. Pas de paràmetres per valor i per referència ..................................... 48

5. Programació dels dispositius d’E/S ................................................ 53


5.1. Instruccions de suport d’E/S ............................................................. 53
5.1.1. Les instruccions in i out ...................................................... 56
5.1.2. El registre de dades del controlador de teclat ....................... 57
5.2. Rutines d’atenció a perifèrics ............................................................ 57
© FUOC • P06/05096/00295 • Mòdul 4 Assemblador

5.3. Programació de dispositius del PC ................................................... 62


5.3.1. Rellotge programable (Timer) ............................................... 62
5.3.2. Teclat ..................................................................................... 62
5.3.3. Pantalla ................................................................................. 63
5.4. Funcions del sistema operatiu i serveis de la BIOS relacionats
amb el teclat i la pantalla ................................................................. 64

6. Referència ràpida ................................................................................ 71


6.1. Blocs d’un programa en assemblador ............................................... 71
6.2. Registres del processador i8086 ........................................................ 73
6.2.1. Registres d’ús general ............................................................ 73
6.2.2. Registres de segment ............................................................. 74
6.2.3. Registre d’estat (FLAGS) ........................................................ 75
6.3. Jocs d’instruccions ............................................................................ 77
6.3.1. Instruccions de moviments de dades .................................... 78
6.3.2. Instruccions aritmètiques ...................................................... 79
6.3.3. Instruccions lògiques ............................................................ 84
6.3.4. Instrucions de rotació i desplaçament de l’i8086 ................. 89
6.3.5. Instruccions de salt jxx ........................................................ 89
6.3.6. Instruccions d’E/S .................................................................. 91
6.3.7. Instruccions d’interrupció ..................................................... 92
6.4. PUSH i POP ........................................................................................ 93

Resum .......................................................................................................... 94

Bibliografia ................................................................................................ 95
© FUOC • P06/05096/00295 • Mòdul 4 5 Assemblador

Introducció

La importància del llenguatge assemblador rau en el fet que és el llenguatge


simbòlic que treballa més a prop del processador. Pràcticament totes les ins-
truccions d’assemblador tenen una correspondència directa amb les instruccions
binàries del codi màquina que utilitza directament el processador. Això fa que el
llenguatge sigui relativament senzill, però que tingui un gran nombre d’excep-
cions i regles definides per la mateixa arquitectura del processador.

Així doncs, aquest llenguatge permet escriure programes que poden aprofitar
totes les característiques de la màquina, cosa que facilita la comprensió de l’es-
tructura interna del processador. També pot aclarir algunes de les característi-
ques dels llenguatges d’alt nivell que queden amagades en la seva compilació.

El mòdul es divideix en quatre parts principals:

− Definició de l’entorn de programació amb què es treballarà, tant en l’edició


del programa com en el seguiment de l’execució.

− Breu recorregut per les instruccions principals del llenguatge, les estructu-
res de dades que es poden definir i el control del flux del programa.

− Explicació de la manera de definir subrutines i els diferents mètodes de pas


de paràmetres.

− Explicació de la manera de programar l’intercanvi de dades entre el proces-


sador i els diferents dispositius d’E/S.

També s’inclou una referència ràpida, un recull sistemàtic de l’ús de les ins-
truccions i d’algunes característiques específiques del llenguatge.

Aquest mòdul no pretén ser un manual amb totes les característiques del llen-
guatge assemblador, sinó una petita guia que permeti de fer-hi programes.
© FUOC • P06/05096/00295 • Mòdul 4 6 Assemblador

Objectius

Amb els materials d’aquest mòdul didàctic es pretén que l’estudiant assoleixi
els objectius següents:

1. Entendre l’entorn de programació del llenguatge assemblador.

2. Conèixer el repertori d’instruccions bàsiques del processador Intel, les di-


ferents formes d’adreçament per a tractar les dades i el control de flux dins
d’un programa.

3. Saber estructurar en subrutines un programa escrit en assemblador.

4. Saber accedir a les dades dels diferents dispositius i com controlar-los.

5. Tenir una idea global de l’arquitectura interna del processador segons les
característiques del llenguatge assemblador.
© FUOC • P06/05096/00295 • Mòdul 4 7 Assemblador

1. Conceptes previs

Abans de començar a programar en assemblador, és convenient aclarir una sè-


rie de conceptes. A continuació es presenten unes definicions que cal tenir en
compte.

1.1. Elements del computador

Els elements bàsics del computador són els següents:

• El processador. La part central del computador, el mòdul encarregat d’exe-


cutar les instruccions.

• La memòria. La part del computador on s’emmagatzema la informació (el


programa i les dades).

• Els registres. Petita memòria interna dins del processador. L’accés als regis-
tres és molt més ràpid que a la memòria, però el nombre de registres és molt
petit. Així doncs, els registres s’utilitzen temporalment com a suport a la
Exemple
memòria.
La pantalla permet a l’usuari
veure què fa el computador i el
• Els perifèrics o dispositius d’entrada/sortida. Dispositius que permeten teclat permet a l’usuari indicar
al computador què ha de fer.
la comunicació del computador amb el món exterior.

1.2. Llenguatges de programació

Un programa és un conjunt d’instruccions que realitzen una tasca determina-


da sobre un conjunt de dades. Els programes, per tant, estan formats per codi
i dades. Per a executar un programa, tant el codi com les dades han de ser en
la memòria.

Per a construir un programa, cal utilitzar un llenguatge de programació que


permeti especificar el funcionament que es vol que tingui el programa. Hi ha
molts llenguatges de programació i molts nivells de programació:

• Llenguatge de màquina. Llenguatge que entén un processador determi-


nat. Aquest llenguatge està format per instruccions codificades en binari i,
per tant, és molt difícil d’entendre per al programador.

• Llenguatge d’alt nivell. Llenguatge que entén el programador, ja que està


format per sentències (generalment en anglès) amb una estructuració lògi-
© FUOC • P06/05096/00295 • Mòdul 4 8 Assemblador

ca. El llenguatge d’alt nivell no té relació amb cap llenguatge de màquina,


està orientat a ser utilitzat pel programador i el computador no l’entén di-
rectament. Per a poder-lo executar, cal un procés previ de traducció de llen-
guatge d’alt nivell a llenguatge de màquina.

• Llenguatge d’assemblador. Representació en text del llenguatge de màquina.


Serveix perquè el programador pugui programar directament en llenguatge de
màquina, però sense haver d’escriure en codi binari. És un llenguatge de baix
nivell ja que no està format per sentències amb una estructuració lògica
fàcilment comprensibles pel programador, sinó que són una llista d’ins-
truccions molt bàsiques. Per a ser executat, també requereix un procés de
traducció, però més senzill que el llenguatge d’alt nivell.

1.3. El llenguatge d’assemblador de l’i8086

Els computadors personals (PC – personal computer) més comuns utilitzen pro-
cessadors de la família Intel x86 (o compatibles). El processador i8086 és un
dels predecessors del Pentium actual. Tot i que els processadors actuals són
d’una arquitectura i tecnologia superiors, segueixen mantenint la compatibi-
litat amb el llenguatge d’assemblador de l’i8086.

L’objectiu d’aquest curs és conèixer l’estructura del computador i del llenguatge


d’assemblador. Per a aquest fi, es pot utilitzar tant el llenguatge d’assemblador de
l’i8086 com el dels processadors més avançats. Però la programació en llenguatge
d’assemblador de l’i8086 és més senzilla i, per tant, aquest és el llenguatge selec-
cionat per a il·lustrar els conceptes d’aquest curs.

1.3.1. Registres principals de l’i8086

Com a primer pas per a conèixer el llenguatge d’assemblador de l’i8086, pre-


sentarem alguns dels registres de propòsit general de què disposa i l’ús que se
n’acostuma a fer.
© FUOC • P06/05096/00295 • Mòdul 4 9 Assemblador

Els registres ax, bx, cx, i dx són de 16 bits, però només es pot accedir als 8 bits
Exemple
de més pes o de menys pes. Tot i que aquests registres són de propòsit general,
El registre ah conté els 8 bits
el registre ax i el dx s’utilitzen sovint per a tasques predeterminades, el registre de més pes del registre ax i el
bx es fa servir per a guardar-hi adreces de memòria i el cx, per a fer de comp- registre al conté els 8 bits de
menys pes del registre ax.
tador.

Altres registres que s’utilitzen sovint són el registre si i el registre di. Aquests
registres, també de 16 bits, es poden utilitzar com a registres de propòsit gene-
ral, tot i que normalment s’utilitzaran com a registres índex.

També farem servir registres amb un ús més depenent, el registre bp i el registre sp, que
serveixen per a gestionar un bloc de memòria anomenat pila. De tota manera, no els uti-
litzarem fins que arribem al capítol que explica el concepte de pila.

Finalment, hi ha altres quatre registres de segment (cs, ds, es, ss), que serveixen per a
gestionar les adreces de memòria. En aquest curs no veurem l’ús d’aquests registres, ex-
cepte el registre que s’utilitzarà per a realitzar una tasca molt concreta al final del curs.

Quan es vol escriure un programa que pugui ser executat en qualsevol


computador (després de ser traduït), normalment es programa en un
llenguatge d’alt nivell, i es programa en assemblador quan es vol un
control més gran sobre les operacions que executi el computador.

1.3.2. Entorn de programació

Per a poder escriure un programa, tant en llenguatge d’alt nivell com en as-
semblador, cal un editor de text sense format, ja que el contingut del fitxer ha
de ser únicament les instruccions del programa, sense codis de control sobre
l’estil de la lletra, mida, colors, marges del document, etc. El fitxer, que con-
tindrà el programa font (en format text), s’anomena fitxer font.

Si el fitxer font conté un programa escrit amb un llenguatge d’alt nivell, el pro-
cés de traducció es realitza mitjançant un compilador. El compilador analitza
el significat del programa escrit en lleguatge d’alt nivell i genera instruccions
pertinents en llenguatge de màquina. Alternativament, si el fitxer font conté
un programa escrit en llenguatge d’assemblador, el procés de traducció es
realitza mitjançant un assemblador. L’assemblatge és un procés més senzill,
ja que la traducció d’assemblador a llenguatge de màquina és directa: cada ins-
trucció d’assemblador representa una instrucció en llenguatge de màquina.
© FUOC • P06/05096/00295 • Mòdul 4 10 Assemblador

Com a resultat de la compilació o de l’assemblatge, es genera un fitxer


objecte que conté instruccions en llenguatge de màquina però que en-
cara no es pot executar.

Diversos fitxers objecte es poden combinar mitjançant un muntador (linker)


per a generar un fitxer executable. Aquest procés també és necessari en cas de
tenir un sol fitxer objecte. El fitxer executable ja és el fitxer binari en llenguat-
ge de màquina amb el format adequat per a poder ser executat.

En executar un programa, aquest realitzarà les tasques que se li hagin especi-


ficat. Pot passar que, per errors en la programació, un programa no es compor-
ti com s’esperava. Per a aquests casos hi ha una eina, el depurador (debugger),
que permet executar de manera controlada un fitxer executable i, per tant, en
qualsevol moment es pot consultar el valor de les variables de memòria o dels
registres. D’aquesta manera es pot seguir l’execució del programa pas a pas fins
a trobar la instrucció, o les instruccions, que han causat els problemes.

1.3.3. Instal·lació del programari

Per a instal·lar el programari que trobareu en l’espai de l’assignatura, heu de


seguir aquests passos:

1) Crear un directori (ho podeu fer en l’arrel del disc dur, per exemple,
C:\TASM) que contindrà el programari un cop s’hagi fet la instal·lació.

2) Baixar el fitxer amb el programari que trobareu en l’espai de l’assignatura.


Nota
Es tracta del fitxer tad.zip. També podeu baixar des d’aquest espai l’arxiu
Trobareu aquest programari
ConTEXTsetup.exe, que conté l’editor de programari lliure ConTEXT. concretament en la ruta se-
güent:
Recursos / EINES I ELEMENTS
3) Descomprimir el fitxer. Dins del fitxer tad.zip trobareu els fitxers tad1.exe, DE SUPORT / Assembler /
Programari Assembler.
tad2.exe i tad3.exe. Els heu de posar en el mateix directori i executar-los l’un
darrere l’altre. Cada cop que executeu un d’aquests fitxers, us pregunta on vo-
leu deixar els fitxers descomprimits. Indiqueu un directori temporal (per
exemple, C:\TASM_TMP).

Podeu treure l’opció que indica When done unzipping run: ..., ja que el fitxer que es vi-
sualitza no dóna gaire informació.

4) Instal·lar el programari. Dins d’una finestra del programa Símbolo de siste-


ma (dins d’Accesorios del menú Todos los programas), aneu al directori on te-
niu els arxius que heu descomprimit en el punt 3 i executeu el fitxer
install.exe. Quan aparegui en pantalla la presentació, heu de prémer la tecla
Retorn per a continuar. En la pantalla següent us demanarà la unitat des d’on
es farà la instal·lació, heu de posar la lletra del vostre disc dur i prémer Retorn.
Amb la tecla Esc podeu sortir de la instal·lació.
© FUOC • P06/05096/00295 • Mòdul 4 11 Assemblador

Enter the SOURCE drive to use: C

A continuació, apareixerà una pantalla amb el directori des d’on es començarà


a fer la instal·lació i, en prémer Retorn, veureu la pantalla següent:

Per a continuar amb la instal·lació premeu F9 o seleccioneu Start Installation,


i es començaran a instal·lar els fitxers en el vostre disc. Quan s’acabin de copiar
els fitxers, apareixerà una pantalla amb informació sobre el Turbo Assembler,
que podreu llegir desplaçant-vos amb el cursor. En acabar de llegir aquesta in-
formació, premeu Esc per donar per finalitzat el procés d’instal·lació.

5) Afegir una línia a la variable PATH que indiqui el directori on són els exe-
cutables que fareu servir (TASM.EXE, TLINK.EXE i TD.EXE). Per a fer-ho en un
sistema operatiu Windows XP, cal anar a Mi PC i amb el botó dret accedir al
menú Propiedades i, en l’opció Opciones avanzadas, triar Variables de entorno
i modificar, o crear, la variable PATH.

Exemple

Si la variable PATH conté la línia següent:


PATH C:\WINDOWS;C:\WINDOWS\COMMAND

Cal afegir-hi C:\TASM\BIN, de manera que obtindríeu el PATH següent:


PATH C:\WINDOWS;C:\WINDOWS\COMMAND;C:\TASM\BIN

6) Esborrar el directori temporal on estan descomprimits els fitxers amb els


quals heu fet la instal·lació i els fitxers baixats del Web.

7) Reinicialitzar (si creieu oportú fer-ho en aquest moment) el sistema opera-


tiu. Quan torni a arrencar apareixerà una finestra amb el missatge següent:

Do you wish to create program groups and icons for the Borland product you
recently installed?

Responeu afirmativament la pregunta fent clic sobre Aceptar. Es crearà un nou


grup de programes. Ja podeu tancar la finestra i donar per acabat el procés
d’instal·lació.
© FUOC • P06/05096/00295 • Mòdul 4 12 Assemblador

1.3.4. Treballar amb l’entorn de programació

El programari s’ha de fer servir des de la línia d’ordres obrint una finestra amb
l’accés a Símbolo del sistema dins d’Accesorios del menú Todos los programas
que dóna el sistema operatiu. Dins d’aquesta finestra, situeu-vos en el directori
de treball on teniu els fitxers en assemblador (per exemple, C:\PGM) amb
l’ordre:

CD C:\PGM

Podeu editar el programa amb qualsevol editor del sistema operatiu que pugui
tractar arxius de tipus text (no utilitzeu Word o WordPad, que poden afegir
caràcters estranys al text). En Windows teniu un parell de programes senzills
per a editar: Edit i Bloc de Notas (Notepad). També podeu fer servir l’editor
ConTEXT, que trobareu en l’espai de l’assignatura. Aquest editor incorpora ca-
racterístiques que faciliten l’edició de programes en llenguatge d’assemblador.

Els tres processos bàsics d’un programa són assemblar, muntar i depurar. Els
heu de fer en la línia d’ordres amb les ordres següents:

• Per a assemblar: TASM/zi nom


• Per a muntar: TLINK/3/v nom
• Per a depurar: TD nom

També podeu cridar el Turbo Debugger des del menú Inicio. Trieu la icona
amb el nom Turbo Debugger for DOS dins la carpeta de Borland Tasm 5.0 que
s’ha creat durant la instal·lació.

1.3.5. Fer un programa amb l’entorn de programació

Per començar a conèixer la programació amb Turbo Assembler dintre d’en-


torns i8086, editarem un petit programa que mostrarà en blau l’esquelet bàsic
de qualsevol programa en assemblador i en negre les ordres pròpies del pro-
grama.

Editeu el programa següent amb el nom hola.asm:


Recordeu

Per a editar un programa


en assemblador, heu d’utilitzar
.model small ; Model de memòria SMALL un editor de text ASCII sense
format.
.data ; Obre el segment de dades
msg DB "Hola, món! $" ; Missatge que es vol imprimir

.code
inici: STARTUPCODE
mov ax, @DATA ; Valor de segment per .DATA
© FUOC • P06/05096/00295 • Mòdul 4 13 Assemblador

mov ds,ax ; Per a poder accedir a la variable msg


mov dx, OFFSET msg ; Prepara la int 21h servei 9
mov ah, 9
int 21h ; invoca el servei 9 que imprimeix una cadena
fins a trobar '$'
mov ah, 07h ; llegir una tecla (es queda en espera)
; posa el caràcter llegir en el registre al
int 21h
EXITCODE 0
end inici
Recordeu

El fitxer s’ha de guardar amb


Per a assemblar i muntar un programa mitjançant Turbo Assembler, un cop l’extensió .asm. En aquest cas:
guardat el fitxer, cal seguir aquests passos: hola.asm.

1) Obrir una finestra de Símbolo del sistema. Per defecte el directori on s’exe-
Exemple
cuten els programes de la línia d’ordres és el de l’usuari, per la qual cosa caldrà
Si el directori on hi ha l’arxiu
anar al directori on hi hagi l’arxiu de text per a poder assemblar el programa: de text és C:\TASM, escriurem
l’ordre cd c:\TASM.

C:\TASM> tasm/zi hola (tasm pren l’extensió .asm per defecte).

L’opció /zi fa que el tasm generi informació simbòlica per a la depuració. Un cop el pro-
grama estigui lliure d’errors de comportament, es pot deixar d’utilitzar aquesta opció.

Si no s’han trobat errors sintàctics, es generarà el fitxer hola.obj.

2) Muntar el programa i convertir-lo en un fitxer executable:

C:\TASM> tlink /v hola (tlink pren l’extensió .obj per defecte).

Aquest darrer pas generarà dos fitxers:

• El fitxer hola.map es genera per haver triat l’opció de compilació /zi.

• El fitxer hola.exe. Amb l’opció /v s’afegeix informació de depuració al final


d’aquest fitxer.

Si en el procés d’assemblar o muntar es genera algun error, cal revisar bé el co- Més endavant veurem com
s’analitzen els errors en el procés
d’assemblar i muntar.
di, ja que segurament s’ha canviat alguna cosa. Si no es localitza l’error, caldrà
recuperar el fitxer original i tornar a repetir el procés.

Els programes per a assemblar (TASM) i muntar (TLINK) no són un entorn des
d’on es treballa, sinó que l’única cosa que fan és llegir informació d’un fitxer
com a informació d’entrada i generar altres fitxers com a informació de sorti-
da. És a dir, tenen la funció de traduir la informació d’un format (llenguatge)
a un altre.
© FUOC • P06/05096/00295 • Mòdul 4 14 Assemblador

3) Ja es pot executar el programa directament mitjançant la línia següent:

C:\TASM> hola (el sistema operatiu pren l’extensió .exe per defecte).

Si s’executa el programa directament des de la línia d’ordres del sistema, s’exe-


cutarà tot de cop i només es podran veure les accions que es visualitzin per
pantalla, però no es podrà saber què passa amb les operacions internes (amb
l’execució de cada instrucció).

Si es vol supervisar l’execució del programa, caldrà utilitzar un depurador


(debugger). Per a executar Turbo Debugger, escrivim la línia següent:

C:\TASM> td hola (el td pren l’extensió .exe per defecte).

La pantalla del depurador té quatre parts:

• En la part superior hi ha una barra de menú, a la qual s’accedeix prement la tecla


Alt + la primera lletra de l’opció triada. Es desplega un menú amb les diferents opcions
associades a la tria.

• La finestra central mostra el codi del programa que s’està depurant. Aquesta finestra
no és un editor; és a dir, en cas de detectar algun error no es podrà modificar des
d’aquí. En aquesta zona es mostrarà la instrucció que s’està executant identificada amb
un triangle (en la pantalla anterior el triangle marca la instrucció mov ax, @DATA).

• En tercer lloc hi ha la finestra Watches, que presenta els valors de les variables o regis-
tres durant la depuració.

• En la part inferior, una barra que informa de l’acció que realitza cada una de les tecles
de funció més utilitzades durant el procés de depuració.

A diferència de l’assemblador i el muntador, el depurador és un entorn des


d’on es pot treballar. Per a aprofitar al màxim les seves utilitats, caldrà conèi-
xer tant com sigui possible cada una de les opcions de la barra de menús.

A continuació farem una breu descripció d’algunes de les funcionalitats més


importants del Turbo Debugger i quina és la tecla o combinació de tecles per
a executar-les:

• F2: Toggle breakpoint. Els punts de ruptura (breakpoints) permeten definir


punts on interessa que l’execució es pari. D’aquesta manera, és possible
consultar els valors de les variables importants del programa sense haver de
fer una execució pas a pas de tot el programa. Es pot col·locar un punt de
ruptura en qualsevol línia de codi simplement prement F2 sobre la línia.
© FUOC • P06/05096/00295 • Mòdul 4 15 Assemblador

• F4: Go to line. Aquesta opció permet executar totes les instruccions prèvies
a la línia on s’hagi situat el cursor. Després de prémer F4, l’execució avan-
çarà de cop fins a la línia del cursor. En conseqüència, el triangle que asse-
nyala la instrucció següent que s’ha d’executar coincideix amb la línia del
cursor. El cursor fa de punt de ruptura.

• F5: Zoom. Aquesta opció fa que la finestra activa ocupi tota la pantalla,
però si es torna a prémer F5 la pantalla torna a les dimensions originals.

• F6: Canviar finestra. Sota la finestra on es mostra el codi, hi la finestra


Watches (permet al programa comprovar el valor de les variables impor-
tants). Amb aquesta opció es pot saltar d’una pantalla a l’altra. Per a afegir
variables a la finestra Watches, només cal escriure’n el nom quan la finestra
està activa. Les variables que es visualitzen en la finestra Watches actualit-
zen el seu valor cada cop que s’executa una instrucció que les modifica.

• F7: Trace. Execució pas a pas. Aquesta opció permet executar la instrucció
marcada amb un triangle.

• F8: Step. Execució pas a pas. Permet executar la instrucció marcada amb un
triangle.

Diferència entre trace i step

Si la instrucció que s’ha d’executar és un CALL (crida a un procediment) amb trace, la


següent instrucció que s’executa és la primera del procediment que es crida (s’entra a de-
purar el procediment), mentre que amb step s’executa tot el procediment de cop i la ins-
trucció següent que s’executa és la que hi ha després del CALL.

• F9: Run. S’executa el programa de cop fins que troba un punt de ruptura o
bé finalitza.

• F10. Aquesta opció permet accedir a la barra de menú.

Hi ha altres funcions a les quals s’accedeix mitjançant combinacions de tecles:


Nota
Maj + [tecla], Alt + [tecla] o Control + [tecla]. Les funcions més útils són les
Si es manté polsat Alt o Control
següents: durant uns instants, en el
menú informatiu de la part in-
ferior de la pantalla apareixe-
• Alt + X: sortir. ran les funcions accessibles
mitjançant combinacions de
• Alt + F3: tancar la finestra activa. tecles.
• Alt + F5: obrir la finestra d’usuari.

Aquesta funció permet veure el que s’escriu per pantalla. En el cas del programa que es-
tem elaborant, permetria veure quan s’escriu “Hola, món!” i si s’ha escrit correctament.

• Alt + F6: obre la darrera finestra que s’ha tancat.


• Alt + F9: demana el nom d’una etiqueta o el nom d’un procediment i el
programa s’executa fins aquell punt.
© FUOC • P06/05096/00295 • Mòdul 4 16 Assemblador

Les funcions associades a la tecla Control varien segons quina sigui la finestra
activa en cada moment. Quan la finestra principal està activa, les més impor-
tants són les següents:

• Control + L: demana un número de línia del programa i mou el cursor a


l’inici de la línia indicada.

Aquesta opció és molt útil quan el programa ha donat un warning i es vol veure què passa
quan s’executa.

• Control + S: demana una paraula i la busca dins del fitxer. S’atura en trobar
la primera coincidència.

• Control + N: repeteix la darrera cerca realitzada; és a dir, permet continuar


una cerca i s’atura en trobar una nova coincidència.

• Maj + F3: copia en el portapapers el text seleccionat.

• Maj + F4: copia el que hi ha en el portapapers al lloc on hi hagi el cursor,


si s’hi pot escriure.

La resta de funcions de Turbo Debugger són accessibles des del menú (F10).
Una de les informacions més importants es troba sota el menú View: la possi-
bilitat de mostrar els registres mitjançant les opcions Registers o CPU.

Exercici

Feu algunes proves amb Turbo Debugger per veure’n el funcionament. A mesura que els
programes es vagin complicant, és molt important tenir un mínim d’agilitat per a loca-
litzar els errors de programació.

Per a fer un programa i verificar que funcioni correctament, cal fer els passos
següents:

• Editar
• Assemblar
• Muntar
• Depurar i/o executar

Aquest passos s’han de fer sempre en aquest ordre i, en cas de detectar un error
en algun d’ells, per a poder-lo corregir caldrà fer-ho des de l’editor i tornar a
fer tot el procés sense saltar-se cap pas. Si s’omet algun del passos, el programa
que s’executarà no serà la darrera versió modificada que es vol generar.

Exemple

Si es fa una modificació amb l’editor però no es guarden els canvis, si no fem el procés
d’assemblar i passem directament a muntar, estarem muntant la versió anterior, ja que
no s’haurà generat el nou .obj o ens saltem el procés de muntar i, per tant, executarem
la versió que tenia l’error, ja que no haurem generat el nou .exe.
© FUOC • P06/05096/00295 • Mòdul 4 17 Assemblador

Si en el procés d’assemblar o muntar es detecta un error o un warning, aparei-


xerà una informació com la següent:

C:\TASM> tasm /zi hola (el tasm pren l’extensió .asm per defecte).
Turbo Assembler Version 4.1 Copyright ©1988, 1996 Borland International

Assembling file: hola.asm

En detectar l’error, caldrà fer les modificacions pertinents en el fitxer, guardar-


lo i seguir els passos descrits anteriorment per a generar una nova versió del
programa. Fins que no s’hagin corregit tots els error en cada una de les etapes,
no es pot passar a l’etapa següent i, per tant, no es pot generar el programa exe-
cutable per a verificar-ne el funcionament.

S’ha d’anar molt alerta perquè moltes vegades, tot i que l’error es detecta en
una línia determinada del programa, el problema no és en la mateixa línia,
sinó en una altra part del programa.

S’han remarcat els errors del programa que exemplifiquen les tipologies d’er-
ror vistes:

.model small ; Model de memòria SMALL

msg1 EQU “Hola $”


.data ; Obre el segment de dades
msg DB "Hola, món! $" ; Missatge que es vol imprimir
msg2 DB "Adéu, món! $" ; Missatge que es vol imprimir

.code
inici: STARTUPCODE
mov ax, @DATA ; Valor de segment per .DATA
mov ds,al ; Per a poder accedir a la variable msg
© FUOC • P06/05096/00295 • Mòdul 4 18 Assemblador

mov dx, OFFSET msg1 ; Prepara la int 21h servei 9


move ah, 9
int 21h ; invoca el servei 9 que imprimeix una cadena
fins a trobar ‘$’
mov ah, 07h ; llegir una tecla (es queda en espera)
; posa el caràcter llegir en el registre al
int 21h
EXITCODE 0
end ini

En compilar el programa, apareixen els errors següents:

• **Error** hola.asm(5) Extra characters on line

El caràcter $ està fora de les cometes i l’assemblador no pot identificar què és.

• **Error** hola.asm(11) Argument to operation or instruction


has illegal size

S’ha escrit ax per al i en voler passar la informació del registre ds –que és de 16 bits– al
que és de 8 bits es produeix un error. ax si és de 16 bits.

• **Error** hola.asm(12) Value out of range


Nota

Aquest error és un cas en el


L’error detectat és que es vol calcular l’OFFSET (desplaçament) dins del segment de dades
qual es detecta error en una lí-
d’un element msg que no és una variable (no està declarada dins del segment de dades), nia però, en realitat, l’error és
sinó una constant que no emmagatzema un valor en una posició de memòria de la qual en un altre lloc.
sí ques es podría obtenir el desplaçament. Per a corregir aquest error, caldria declarar msg
dins del segment de dades de la mateixa manera que msg1 i msg2 o esborrar aquesta línia
i utilitzar l’identificador d’una variable.

• **Error** hola.asm(13) Illegal instruction

S’ha escrit move per mov. move no és una ordre de Turbo Assembler.

• **Error** hola.asm(18) Undefined symbol: INI

Es vol utilitzar l’element ini com a primer procediment per a ser executat en el programa
i l’assemblador ha detectat que en tot el programa no hi ha cap procediment o etiqueta
amb aquest nom. Per a solucionar aquest error, cal canviar ini per inici, que és el pro-
cediment principal que s’ha definit, o fer un nou procediment principal amb aquest
nom.

Si en executar el programa no fa el que s’havia previst que fes i s’ha executat


des del Símbol del sistema, només es veurà el que surt per pantalla. En l’exem-
ple que estem treballant, i recuperant-ne la versió bona del programa ho-
la.asm, el que apareixerà per pantalla serà el següent:

Hola, món!
© FUOC • P06/05096/00295 • Mòdul 4 19 Assemblador

Per a resoldre molts dels error que pot donar un programa en assemblador, és
molt util afegir comentaris en el codi, ja que el baix nivell d’aquest llenguatge
fa que els programes siguin difícils d’entendre.

Els comentaris són una informació descriptiva que ajuden a entendre


més fàcilment el codi d’un programa, tot i que no tenen cap efecte sobre
el comportament del programa. L’assemblador entén com a comentari
tot aquell text que hi ha darrere un punt i coma i fins al final de la línia.
© FUOC • P06/05096/00295 • Mòdul 4 20 Assemblador

2. Elements bàsics d’un programa en assemblador

2.1. Instruccions

L’i8086 és un exemple de processador CISC. Algunes de les seves instruccions


són, a vegades, summament complexes. No obstant això, com que està gairebé
a nivell de codi de màquina, per complicada que sigui una instrucció, no re-
sulta difícil comprendre’n la finalitat operativa.

2.1.1. Tipus d’instruccions

El joc d’instruccions dels processadors i8086 es pot classificar segons grups


d’instruccions (només en mostrem les més importants):

• Instruccions de moviment de dades (mov, xchg, lxx, push, pop)

• Instruccions aritmètiques (add, adc, inc, dec, sub, sbb, neg,


mul, div, etc.)

• Instruccions lògiques (and, not, or, test, xor)

• Instruccions de salt i crida (call, jmp, Jx, ret, loop, enter,


leave)

• Instruccions de control del processador (cli, sti, stc, clc)

• Instruccions d’entrada/sortida (I/O) (in, out)

• Instruccions d’interrupció (int, into, iretI)


En aquesta assignatura utilitzarem
un subconjunt d’aquestes
instruccions.
• Instruccions de rotació i desplaçament (rcl, rcr, rol, ror, sal, En l’apartat “Referència ràpida” trobareu
un recull detallat d’aquestes instruccions.
sar, shl)

2.1.2. Operands de les instruccions

Cada instrucció de llenguatge d’assemblador de l’i8086 utilitza un nombre fix


Exemple
d’operands, que depèn del tipus d’instrucció i està fixat per l’especificació del
La instrucció mov utilitza dos
llenguatge d’assemblador. Hi ha instruccions que utilitzen un únic operand, operands (separats per una co-
ma) i la instrucció int n'utilitza
instruccions que n’utilitzen dos, i algunes (poques) instruccions que no n’uti- només un.
litzen cap.

A partir d’ara distingirem entre els operands que es consulten (es llegeixen) i
els que es modifiquen (s’escriuen).
© FUOC • P06/05096/00295 • Mòdul 4 21 Assemblador

Una instrucció en assemblador executa una acció determinada, utilit-


zant un o diversos operands d’origen, sobre (generalment) un operand
de destinació. A més, cal tenir en compte que tant l’operand d’origen
com l’operand de destinació han de ser de la mateixa grandària.

Els operands d’origen –és a dir, els operands que es consulten per a executar la
instrucció– poden ser de tres tipus:

• Constant
• Registre
• Variable de memòria

De manera similar, els operands de destinació –és a dir, els operands que s’ac-
Nota
tualitzen com a conseqüència d’haver executat una instrucció– poden ser de
Si definim operand de destina-
dos tipus: ció com l’operand que s’actua-
litza quan s’executa una
instrucció, no té sentit dir que
• Registre l’operand de destinació és una
constant: una constant no es
• Variable de memòria pot modificar.

Tot i que tant l’operand d’origen com l’operand de destinació poden ser de
tipus variable de memòria, hi ha una restricció en l’assemblador de l’i8086:
els dos operands no poden ser de tipus variable de memòria en la mateixa
instrucció.

A continuació es mostra un programa que escriu quatre vegades la lletra A per


pantalla. Tot i que el codi d’aquest programa no afegeix cap complexitat res-
pecte dels exemples vistos fins ara, aquest programa il·lustra els tipus d’ope-
rands que es poden tenir en assemblador:

; Escriptura d’un caràcter per pantalla:


; llocs on pot ser el caràcter

.model small
.data
caracter1 db 65
caracter2 dw 65
.code

inici: STARTUPCODE

cas1:mov dl, 65
mov ah, 02h
int 21h

cas2:mov bh, 65
© FUOC • P06/05096/00295 • Mòdul 4 22 Assemblador

mov dl, bh
mov ah, 02h
int 21h

cas3:mov dl, [caracter1]


mov ah, 02h
int 21h

cas4:mov dx, [caracter2]


mov ah, 02h
int 21h

EXITCODE 0
end inici

cas1:mov dl, 65

La instrucció mov (que ja coneixem) té dos operands i copia el valor de l’ope-


rand d’origen (el llegeix) sobre l’operand de destinació (l’escriu):

mov destinació, origen

En aquest cas, mov utilitza com a operand d’origen un valor constant (el nom-
Alerta
bre 65). La constant especificada pot estar expressada en diferents formats,
En especificar una constant en
tots ells equivalents i que s’utilitzen segons prefereixi el programador: hexadecimal, ha de començar
obligatòriament per un dígit
[0..9]. És a dir, en lloc de posar
• En decimal: mov dl, 65 ffh s’ha de posar 0ffh.

• En hexadecimal: mov dl, 41h


• En ASCII: mov dl, ‘A’

Fixeu-vos que dins del mateix cas1, les altres dues instruccions també acce-
Exemple
deixen a un operand constant, en aquest cas expressat en hexadecimal.
Si es vol que el programa es-
crigui sempre una A, s’utilitza-
rà un operand constant.

S’utilitzen valors constants quan es vol que l’operand sigui sempre el


mateix, independentment de com i quan s’executi el programa.

mov dl, bh

En el cas2 podeu observar que l’operand d’origen de la instrucció mov és un


registre. De fet, en aquesta instrucció l’operand de destinació també ho és.
© FUOC • P06/05096/00295 • Mòdul 4 23 Assemblador

Normalment per a utilitzar un operand d’origen de tipus registre, aquest ha


d’haver estat prèviament inicialitzat. En l’exemple podeu observar que la ins-
trucció anterior inicialitza el registre amb la constant 65, de manera que el re-
sultat de l’escriptura serà el mateix que en el cas anterior.

L’accés a un registre és molt ràpid, però hi ha pocs registres disponibles. Així


doncs, normalment s’utilitzaran operands de tipus registre sempre que sigui
possible.

Hi ha operacions que forcen l’ús d’algun registre concret. Per exemple, si es-
crivim un caràcter per pantalla, s’ha de posar prèviament el caràcter en el re-
gistre dl i el codi 09h en el registre al.

caracter1 db 65
...
cas3:mov dl, [caracter1]

En el cas3, la instrucció mov utilitza un operand d’origen de tipus variable de


memòria. En aquest cas s’especifica entre claudàtors una variable de memòria
declarada en el segment de dades.

Com en el cas anterior, normalment per a utilitzar un operand d’origen de ti-


pus variable de memòria, aquesta ha d’haver estat declarada i inicialitzada prè-
viament. Fixeu-vos que la variable caracter1 ha estat declarada en el
segment de dades i inicialitzada en el valor 65 durant la seva definició.

Com que normalment no n’hi ha prou amb els registres de què es disposa,
s’utilitza un operand de tipus variable de memòria per a guardar temporal-
ment les dades del programa.

caracter2 dw 65
...
cas4:mov dx, [caracter2]

La variable de memòria caracter2 s’ha declarat en el segment de dades amb


dw en lloc de db. Declarar la mida d’una variable amb db significa que ocupa
8 bits (els necessaris per a codificar un caràcter en codi ASCII), mentre que dw
significa que la variable de memòria ocupa 16 bits. Per tant, en aquest segon
cas la constant 65 es codifica amb 16 bits (els 8 bits de més pes seran zero).

Dins del cas4, l’operand d’origen és una variable de memòria de 16 bits. Com
ja s’ha comentat, tant l’operand d’origen com l’operand de destinació han de
tenir la mateixa grandària. Per tant, ara s’utilitza un registre de 16 bits (dx).
© FUOC • P06/05096/00295 • Mòdul 4 24 Assemblador

dx és un registre de 16 bits, del qual dh fa referència als 8 bits de més pes i dl fa referència
als 8 bits de menys pes. En posar el valor 65 en dx, de fet aquest valor hi cap íntegrament
en dl, i en dh queden únicament zeros. Per tant, el resultat és el mateix que en el cas3.

Per a assignar un valor constant a una variable de memòria, es pot utilitzar la


Alerta
instrucció següent:
En tots els exemples s’han
il·lustrat diferents alternatives
mov [var], 4 d’accés a operands d’origen,
en els quals l’operand de desti-
nació era un registre. Però això
no és sempre així: l’operand de
Recordeu destinació pot ser tant un re-
gistre com una variable de me-
El llenguatge d’assemblador té una restricció que impedeix que ambdós operands, d’ori- mòria.
gen i de destinació, siguin variables de memòria.

No es permet la instrucció següent:

mov [var1], [var2]

Si interessa executar la instrucció anterior, caldrà recórrer a executar una assignació in-
termèdia:

mov ax, [var2]


mov [var1], ax

2.2. Variables de memòria

Les variables de memòria serveixen per a emmagatzemar temporalment en la


memòria informació que pot anar canviant durant l’execució del programa.

Una variable de memòria es representa mitjançant un nom declarat dins del


segment de dades, per al qual es defineix una grandària i, opcionalment, un
valor inicial. El fet d’identificar cada variable de memòria amb un nom serveix
per a especificar a quina part de la informació es vol accedir.

Quan s’executa el programa assemblador, el sistema reserva en la memòria


l’espai corresponent a la grandària de les variables. Aquestes posicions de me-
mòria estaran inicialitzades o no segons com s’hagi declarat la variable. Des
del programa d’assemblador es pot accedir al valor de la variable especificant-
ne el nom entre claudàtors.

2.2.1. Declaració i us de variables

Les variables de memòria s’han de declarar en el segment de dades. Hi ha di-


ferents maneres de declarar-les, en funció de la grandària i del nombre d’ele-
ments que contingui. A continuació presentem exemples amb les diferents
possibilitats que hi ha per a declarar-les i accedir-hi.

; Declaració de variables de memòria:


; diferents possibilitats
© FUOC • P06/05096/00295 • Mòdul 4 25 Assemblador

.model small
.data
var1 db 1
var2 dw 1
var3 dd 1
var4 db 'bon dia',10,13,'$'
var5 dw 1, 3, 5, 7, 9
var6 dw 10 dup (0)

.code
inici: STARTUPCODE

lea dx, [var4]


mov ah, 09h
int 21h

mov [var4], 'B'


mov al, 'D'
mov [var4 + 4], al

lea dx, [var4]


mov ah, 09h
int 21h

mov ax, [var5 + 8]


mov [var5], ax

EXITCODE 0
end inici

var1 db 1
var2 dw 1
var3 dd 1

Les variables de memòria s’han declarat en el segment de dades:

• var1 s’ha declarat com a db; per tant, té una grandària d’un byte (8 bits).

• var2 s’ha declarat com a dw; per tant, té una grandària d’un word (2 bytes,
16 bits).

• var3 s’ha declarat com a dd; per tant, té una grandària d’un double (4
bytes, 32 bits).

Després de la grandària, s’especifica el valor inicial de les variables. Això es pot


fer en qualsevol format: decimal, hexadecimal, o en codi ASCII. En l’exemple
les hem inicialitzades totes en 1.
© FUOC • P06/05096/00295 • Mòdul 4 26 Assemblador

Si la variable no ha de tenir cap valor inicial, es pot especificar de la manera


següent:
var dw ?

var4 db 'bon dia',10,13,'$'


var5 dw 1, 3, 5, 7, 9
var6 dw 10 dup (0)

Quan es vol declarar una llista de variables del mateix tipus (un vector), un cop
indicada la grandària que volem que tingui cada element (db, dw, dd), es
col·loquen els valors inicials consecutivament:

• var4 és una llista de 10 elements de grandària byte inicialitzats amb codis


ASCII. Els codis ASCII es poden especificar escrivint els caràcters entre co-
metes, o bé escrivint els codis separats per comes.

• var5 és una llista de 5 elements de grandària word inicialitzats en els valors


1, 3, 5, 7 i 9 respectivament. Quan els valors d’inicialització siguin nombres
naturals o enters, es posen directament separats per comes.

• var6 és una llista de 10 elements de grandària word inicialitzats en el valor


zero.

2.2.2. Modes d’adreçament

L’accés a una variable de memòria es fa posant-ne el nom entre claudàtors. Podeu consultar la definició dels
diferents modes d’adreçament
Quan es tracta d’una llista d’elements, el nom de la variable fa referència al pri- en el mòdul “Joc d’instruccions i modes
d’adreçament”.

mer valor de la llista.

En l’exemple següent posem el mode d’adreçament dels operands de les in-


truccions marcades en negreta.

Procediment
; Modes d’adreçament dels operands
A continuació explicarem
les instruccions remarcades
.model small en negreta.
.data
var1 db 1
var2 dw 1
var3 dd 1
var4 db 'bon dia',10,13,'$'
var5 dw 1, 3, 5, 7, 9
var6 dw 10 dup (0)
© FUOC • P06/05096/00295 • Mòdul 4 27 Assemblador

.code
inici: STARTUPCODE

lea dx, [var4]


mov ah, 09h
int 21h

mov [var4], 'B' ; indirecte, immediat


mov al, 'D' ; registre, immediat
mov [var4 + 4], al ; indexat, registre

lea dx, [var4]


mov ah, 09h
int 21h

mov ax, [var5 + 8] ; registre, indexat


mov [var5], ax ; indirecte, registre

EXITCODE 0
end inici

mov [var4], 'B' ; indirecte, immediat


mov al, 'D' ; registre, immediat
mov [var4 + 4], al ; indexat, registre

Aquest codi escriu inicialment la cadena de caràcters var4. A continuació,


escriu la primera lletra de les dues paraules en majúscula. Finalment, torna
a escriure la cadena de caràcters començant cada paraula per majúscula.

Es mou una B sobre el primer element de la llista de caràcters. S’accedeix a la


resta d’elements de la llista sumant un desplaçament respecte de l’element ini-
cial. Així doncs, per a accedir al cinquè element de la llista de caràcters, cal su-
mar un desplaçament de 4 respecte de l’element inicial, com mostra la tercera
instrucció en negreta.

Els dos accessos esmentats s’han dut a terme de dues maneres diferents:

• En el primer cas s’ha mogut una constant.


• En el segon cas s’ha mogut un registre.

En qualsevol cas, és important adonar-se que la grandària dels dos operands


en cada instrucció és el mateix, 8 bits.

mov ax, [var5 + 8]


mov [var5], ax
© FUOC • P06/05096/00295 • Mòdul 4 28 Assemblador

Per a accedir als elements d’una llista de words, cal tenir en compte que cada
element ocupa dos bytes. Per tant, l’accés es fa sumant un desplaçament de 2
per cada element.

Les instruccions marcades amb negreta no tenen cap efecte sobre el que s’es-
criu per pantalla, però podeu observar en l’accés a la llista de words un despla-
çament de 8 respecte de l’inici de la variable de memòria var5. És a dir, que
s’està accedint al cinquè element d’aquesta llista (l’últim element, el que conté
el valor 9). A més, fixeu-vos que com que s’accedeix a un element de 16 bits,
cal utilitzar un registre de 16 bits (en aquest cas, el registre ax).

Aquestes instruccions copien l’últim element de la llista sobre l’element ini-


Recordeu
cial. Aquesta acció teòricament es podria fer també de la manera següent:
Els dos operands d’una matei-
xa instrucció no poden ser va-
riables de memòria. Per això
mov [var5], [var5 + 8] s’ha hagut de fer el moviment
en dues instruccions.

Com a resultat de l’operació, el contingut del vector serà: 9, 3, 5, 7 i 9.

La definició de la grandària i la declaració del nombre d’elements de la variable


(amb la grandària definida) són les úniques eines de què disposem per a decla-
rar variables de memòria en llenguatge d’assemblador.

Amb aquestes opcions n’hi ha prou per a declarar variables simples, o vectors
de variables simples, però no per a declarar de manera senzilla vectors multi-
dimensionals (matrius) ni altres tipus estructurats.

Aquest problema, en assemblador, es resol declarant una variable amb la gran-


dària necessària per a emmagatzemar tots els elements d’aquest tipus estruc-
turat. Aleshores, per a accedir a cada element caldrà generar el codi adequat “a
mà” tenint en compte l’estructura que es vulgui donar a aquesta variable.

Exemple

Es vol declarar una matriu de 10 x 10 elements de grandària word, i es vol accedir a l’ele-
ment matriu[3][5]. Per a guardar 10 x 10 elements, cal una llista de 100 elements. Si s’as-
sumeix que els elements consecutius estan ordenats per files i que s’indexa la matriu a
partir del zero, l’element [3][5] és el sisè element de la quarta fila. Per tant, en una llista
lineal, és l’element posterior al: 10 x 3 + 5 = 35; és a dir, l’element que es troba a un des-
plaçament de 70 (35 x mida de cada element) a partir de l’inici de la llista.

Aquest accés es faria de la manera següent:

.data
matriu dw 100 dup (?)
.code

mov ax, [matriu + 70]

Si es volgués accedir a un element variable, aquesta multiplicació d’índexs per la grandà-


ria de la fila s’hauria de fer mitjançant les instruccions d’assemblador.
© FUOC • P06/05096/00295 • Mòdul 4 29 Assemblador

2.3. Pila

L’objectiu d’aquest apartat és conèixer el funcionament de la pila i alguna de La necessitat de les piles s’explicarà
després de conèixer el concepte
i funcionament de les subrutines.
les seves utilitats. Per tant, ens centrarem en alguns casos en què pot resultar
útil tenir una pila.

La pila és una zona de memòria auxiliar que serveix per a guardar-hi in-
formació temporalment a la qual s’accedeix mitjançant unes instrucci-
ons específiques.

El funcionament d’una pila és similar al d’un conjunt d’elements col·locats


l’un damunt de l’altre en forma de pila (d’aquí ve el nom). Es pot posar un ele-
ment al damunt d’aquesta pila, o se li pot treure l’element del damunt:

En representar una pila és important marcar-ne sempre el cim, ja que d’aques-


ta manera sempre se sabrà on és la part de dalt.

Per a posar una dada al cim de la pila, s’utilitza la instrucció push. Per a treure
la dada que hi ha en el cim de la pila, s’utilitza la instrucció pop.

De moment, veurem que la finalitat d’una pila és guardar temporalment el


contingut d’un registre necessari per a realitzar una altra tasca i poder-ne re-
cuperar posteriorment el valor. També pot servir per a guardar temporalment
qualsevol conjunt de dades en un ordre determinat.

Procediment
; Inversió d'un vector d'enters
A continuació explicarem
les instruccions remarcades
.model small en negreta.
.stack 1024
.data
vector dw 1,3,5,7,9

.code
inici: STARTUPCODE
© FUOC • P06/05096/00295 • Mòdul 4 30 Assemblador

xor si,si
buc1: cmp si,10
jge fi1
push [vector + si]
add si, 2
jmp buc1

fi1:
xor si,si
buc2: cmp si, 10
jge fi2
pop [vector + si]
add si,2
jmp buc2

fi2:
EXITCODE 0
end inici

Per a compendre aquest exemple,


pot ser interessant consultar
.stack 1024 l’apartat “Control de flux en un
programa d’assemblador”.

Les piles es declaren en el segment corresponent mitjançant la directiva


.stack seguida de la grandària, en bytes, que es vol que tingui la pila. Com
que és difícil saber amb exactitud la grandària que ha de tenir una pila, s’acos-
tuma a declarar-les prou grans perquè no hi hagi problemes.

En els nostres programes, una grandària de pila de 1.024 bytes acostuma a ser
suficient.

xor si,si
buc1: cmp si,10
jge fi1
push [vector + si]
add si, 2
jmp buc1

Aquest codi està format per dos bucles, el primer dels quals serveix per a recór-
Recordeu
rer els elements del vector utilitzant el registre si com a registre d’índex.
Si s’ha de recórrer un vector
d’elements de grandària word,
s’ha d’incrementar l’índex en
Per a inicialitzar el registre si a zero, s’ha utilitzat l’operació lògica xor. En ge- dues unitats.
neral sempre és millor utilitzar operands de registre que operands constants.
© FUOC • P06/05096/00295 • Mòdul 4 31 Assemblador

push [vector + si]

El primer bucle posa en la pila els elements del vector mitjançant la instrucció
push.

La instrucció push posa una dada de 16 bits al cim de la pila. L’operand


d’aquesta instrucció ha de ser de tipus registre o variable de memòria.

Com que s’han posar els elements del vector ordenadament des del primer ele-
ment a l’últim.

pop [vector + si]

De fet, la pila és una zona de memòria que es gestiona implícitament amb dos
registres:

• El registre ss, que apunta estàticament a l’inici de la zona de memòria que En aquest curs no tractarem el
registre ss.
representa la pila.

• El registre sp, que apunta constantment al cim de la pila. En aquest curs no tractarem
directament el registre sp, tot i que
les instruccions push i pop el
modifiquen implícitament.
La instrucció push equivaldria al codi següent:

sub sp, 2
mov [sp], operand

Aquest codi no es pot executar directament, ja que el registre sp no es pot utilitzar en cap
cas entre claudàtors. A més, és més eficient utilitzar la instrucció push que aquestes dues
instruccions.

La instrucció pop equivaldria al codi següent:

mov operand, [sp]


add sp, 2

Aquest codi no es pot executar directament, ja que el registre sp no es pot utilitzar en cap
cas entre claudàtors. A més, és més eficient utilitzar la instrucció pop que aquestes dues
instruccions.

Per acabar, volem esmentar el registre bp que s’utilitza per a tasques de suport
a la pila.
© FUOC • P06/05096/00295 • Mòdul 4 32 Assemblador

3. Control de flux d’un programa en assemblador

Controlar el flux dins d’un programa vol dir determinar l’ordre en què s’exe-
cutaran les intruccions. En assemblador, com en altres llenguatges d’alt nivell,
cal desenvolupar uns mecanismes que permetin implementar el control del
flux de les instruccions. En els apartats següents s’explica com es desenvolu-
pen les instruccions que implementen algorismes condicionals i iteratius.

3.1. Estructura condicional

Presentarem algunes instruccions que permeten donar una estructura condi-


cional a un programa en assemblador; és a dir, instruccions que permeten sal-
tar d’una part del programa a una altra en funció d’una condició. Tot i que
l’aparença del programa seguirà essent lineal, l’ordre d’execució de les instruc-
cions podrà ser no seqüencial gràcies a instruccions d’avaluació de la condició
i a instruccions de salt.

Una estructura condicional es construeix mitjançant una instrucció de


comparació i una instrucció de salt que salta, o no, segons la compara-
ció realitzada prèviament.

Per a poder identificar la instrucció a la qual es vol saltar, cal posar-hi una eti-
queta.

Una etiqueta és qualsevol nom (exceptuant aquelles paraules que ja te-


nen un significat propi en llenguatge d’assemblador) seguit de dos
punts, i serveix per a marcar una posició de memòria en la qual hi ha
una instrucció determinada.

A continuació es mostra el codi d’un programa que escriu un missatge per


pantalla en funció del valor de la tecla introduïda: si es prem D escriu “Bon
dia”, si es prem T escriu “Bona tarda” i altrament, no escriu res.

Procediment
; Escriptura d'un missatge per pantalla
A continuació explicarem
; en funció de la tecla introduïda les instruccions remarcades
en negreta.
.model small
© FUOC • P06/05096/00295 • Mòdul 4 33 Assemblador

.data
missatge1 db 'Entra una tecla (D/T): $'
missatge2 db 'Bon dia',10,13,'$'
missatge3 db 'Bona tarda',10,13,'$'

.code
inici: STARTUPCODE

lea dx, [missatge1]


mov ah, 09h
int 21h

mov ah, 01h


int 21h

cmp al, 'D'


jne tarda

dia: lea dx, [missatge2]


mov ah, 09h
int 21h

jmp final

tarda: cmp al,'T'


jne final

lea dx, [missatge3]


mov ah,09h
int 21h

final:
EXITCODE 0
end inici

cmp al, 'D'


jne tarda
...
tarda:

La primera part del codi és igual que en la resta de codis presentats en l’apartat
anterior. El canvi arriba un cop introduïda la tecla, ja que en aquest moment
es fa una avaluació del valor de la tecla mitjançant la instrucció de comparació
seguida d’una instrucció de salt. Aquesta instrucció provoca un salt de línia si
la comparació indica que els valors comparats són diferents (jne = jump if
not equal).
© FUOC • P06/05096/00295 • Mòdul 4 34 Assemblador

En concret, es compara la tecla premuda amb la lletra D. Si el resultat de la


comparació és positiu, no hi ha salt i segueix l’execució seqüencial del progra-
ma; és a dir, s’escriu el missatge2. Altrament, salta cap a la instrucció identi-
ficada amb l’etiqueta tarda.

tarda: cmp al,'T'


jne final
...
final:

Si la tecla introduïda no és una D i se salta a la instrucció identificada amb l’eti-


queta tarda, es realitza una nova comparació.

En concret, es compara la tecla premuda amb la lletra T. Si el resultat de la


comparació és positiu, no hi ha salt i segueix l’execució seqüencial del progra-
ma; és a dir, s’escriu el missatge3. Altrament, salta cap a la instrucció identi-
ficada amb l’etiqueta final.

jmp final
...
final:

Quan es tenen dues condicions excloents, cal afegir una instrucció de salt in-
condicional (jmp) al final del primer bloc d’instruccions per a evitar que s’exe-
cuti el segon bloc a continuació del primer.

Nota
cmp al, 'D'
L'etiqueta dia no s'utilitza per
... a res. En general, però, les eti-
tarda: cmp al,'T' quetes (com els comentaris)
serveixen per a fer més entene-
dor el programa. En aquest
cas, les etiquetes identifiquen
quatre blocs en el programa:
Cal remarcar que les comparacions es fan amb els caràcters D i T, que corres- inici, dia, tarda i final.

ponen a lletres en majúscula. En realitat el que s’està comparant és el codi


ASCII d’aquests caràcters amb el codi ASCII introduït pel teclat.

Com que els caràcters en majúscula i els caràcters en minúscula tenen dife-
rent codi ASCII, els missatges s’escriuran només si les tecles s’introdueixen en
majúscula.

En l’exemple heu vist dues instruccions de salt, però n’hi ha algunes més que
permeten saltar si es compleixen altres condicions: saltar si els valors són
iguals, saltar si els valors són diferents, saltar si un valor és més gran/petit que
l’altre, o bé saltar incondicionalment.
© FUOC • P06/05096/00295 • Mòdul 4 35 Assemblador

En general, però, en comparar dos valors s’ha de tenir en compte si aquests va-
lors codifiquen nombres amb signe (enters) o bé valors sense signe (naturals o
caràcters). Recordeu que una cadena de bits interpretada com a nombre natu-
ral representa un cert valor, però interpretada com a nombre enter pot repre-
sentar un valor molt diferent.

Així doncs, les instruccions de salt es poden agrupar en el següent:

• Comparacions de valors amb signe


• Comparacions de valors sense signe
• Altres instruccions de salt

3.1.1. Comparacions de valors amb signe

Si els valors comparats codifiquen nombres amb signe (enters), les instruccions
de salt condicional que es poden utilitzar són les següents:

• jg etiqueta (jump if greater) salta si el primer valor (operand de l’esquer-


ra) és més gran que el segon valor (operand de la dreta).

• jge etiqueta (jump if greater or equal) salta si el primer valor és més gran
o igual que el segon valor.

• jl etiqueta (jump if lower) salta si el primer valor és més petit que el se-
gon valor.

• jle etiqueta (jump if lower or equal) salta si el primer valor és més petit
o igual que el segon valor.

3.1.2. Comparacions de valors sense signe

Si els valors comparats codifiquen nombres sense signe (naturals o caracters),


les instruccions de salt condicional que es poden utilitzar són les següents:

• ja etiqueta (jump if above) salta si el primer valor és més gran que el se-
gon valor.

• jae etiqueta (jump if above or equal) salta si el primer valor és més gran
o igual que el segon valor.

• jb etiqueta (jump if below) salta si el primer valor és més petit que el se-
gon valor.

• Jbe etiqueta (jump if below or equal) salta si el primer valor és més petit
o igual que el segon valor.
© FUOC • P06/05096/00295 • Mòdul 4 36 Assemblador

3.1.3. Altres instruccions de salt

Altres instruccions de salt que es poden utilitzar, i que no depenen de la codi-


ficació dels nombres comparats, són les següents:

• je etiqueta, jz etiqueta (jump if equal or jump if zero) salta si els dos


valors comparats són iguals.

• jne etiqueta, jnz etiqueta (jump if not equal or jump if not zero) salta
si els dos valors comparats són diferents.

Les instruccions de salt condicional permeten saltar a una adreça de memòria que es trobi
relativament a prop. De fet, la representació interna de les instruccions disposen de 8 bits
per a codificar el desplaçament relatiu al que es vol saltar. Això vol dir que es poden fer
salts a instruccions que es troben a –128 bytes (enrere) o bé a +127 bytes (endavant) de
la instrucció actual.

• jmp etiqueta (jump) salta incondicionalment, sense necesstat d’haver


fet cap comparació prèvia.

La instrucció jmp permet efectuar salts a qualsevol instrucció del segment de codi. Aques-
ta instrucció és especialment útil, ja que en combinació amb les instruccions de salt con- Teniu més detalls sobre les
intruccions de salt en la “Referència
dicional permet fer salts a condicionals a qualsevol instrucció del segment de codi. Per ràpida”.
exemple, la instrucció jg etiqueta es pot reemplaçar per aquestes instruccions:

jle nosalta
jmp etiqueta

3.2. Estructura iterativa

Una estructura iterativa es construeix a partir d’una estructura condicional,


que avalua la finalització de les iteracions, i una instrucció de salt enrere, que
provoca les repeticions.

Tot i que en alt nivell hi ha diferents sentències que es poden utilitzar per a
implementar una estructura condicional (for, while, etc.), en assemblador
totes aquestes estructures es poden implementar de la mateixa manera. La di-
ferència entre l’una i l’altra és el tipus de condició que s’avalua:

• En un bucle for la condició és iterar fins a arribar a un nombre determinat


de vegades.

• En el bucle while la condició és una expressió lògica.

Les dues versions del programa que es mostra a continuació escriuen un mis-
satge per pantalla un nombre determinat de vegades.

• Primera versió, s’escriu el missatge 10 vegades.


© FUOC • P06/05096/00295 • Mòdul 4 37 Assemblador

Procediment
; Escriptura d'un missatge per pantalla
; un nombre determinat de vegades (10) A continuació explicarem les
instruccions remarcades en
negreta.
.model small
.data
missatge db 'Bon Dia',10,13,'$'

.code
inici: STARTUPCODE

mov cx,0

avaluacio:
cmp cx,10
jge final

lea dx, [missatge]


mov ah, 09h
int 21h

inc cx
jmp avaluacio

final:
EXITCODE 0
end inici

avaluacio:
cmp cx,10
jge final
...
jmp avaluacio

final:

Aquesta estructura iterativa està formada per una estructura condicional i un


salt enrere.

L’estructura condicional avalua el nombre de vegades que s’ha executat el co-


di, sabent que cx compta el nombre d’iteracions. Quan cx és més gran o igual
que 10, es produeix un salt cap al final. En cas contrari, s’executen les instruc-
cions de dins l’estructura iterativa i se salta enrere, cap a les instruccions que
avaluen de nou la condició.

mov cx,0
...
inc cx
© FUOC • P06/05096/00295 • Mòdul 4 38 Assemblador

Generalment, perquè aquest tipus d’estructura iterativa funcioni correcta-


ment, cal inicialitzar el comptador (en aquest cas el registre cx), i incrementar-
lo al final de cada iteració.

Si no haguéssim inicialitzat cx, aquest registre podria tenir qualsevol valor, per exemple
10, i en aquest cas el codi de dins l’estructura iterativa no s’executaria mai.

Igualment, si no s’hagués incrementat cx al final de cada iteració, el compta-


dor valdria sempre 0 i, per tant, no acabaria mai l’estructura iterativa i el pro-
grama es quedaria iterant indefinidament.

• Segona versió. S’escriu el missatge cada cop que es prem una tecla, fins que
la tecla premuda sigui la X.

Procediment
; Escriptura d'un missatge per pantalla un nombre
A continuació explicarem
; indeterminat de vegades (fins a llegir la tecla 'X') les instruccions remarcades
en negreta.
.model small
.data
missatge db 'Bon Dia',10,13,'$'

.code
inici: STARTUPCODE

avaluacio:
mov ah, 01h
int 21h
cmp al, 'X'
je final

lea dx, [missatge]


mov ah, 09h
int 21h

jmp avaluacio

final:
EXITCODE 0
end inici

avaluacio:
mov ah, 01h
int 21h
cmp al, 'X'
je final
...
jmp avaluacio
© FUOC • P06/05096/00295 • Mòdul 4 39 Assemblador

En aquesta segona versió del programes compara la tecla premuda amb el ca-
ràcter X. Si són iguals se salta cap al final del programa; altrament, s’escriu el
missatge i se salta enrere novament cap a la introducció de la tecla.

avaluacio:
mov ah, 01h
int 21h
cmp al, 'X'
je final

Igual que en la versió anterior, és important inicialitzar la condició d’avaluació


i actualitzar-la en cada iteració.

En aquest cas, la condició d’avaluació és el valor de la tecla introduïda pel te-


clat. Abans de fer la primera comparació, es llegeix sempre una tecla, i abans
de cada nova iteració es llegeix una nova tecla.

Per a la primera versió del programa, en què es pretén comptar un nombre de-
terminat d’iteracions, el llenguatge d’assemblador ofereix una altra manera de
resoldre-ho més eficient, la instrucció loop.

La instrucció loop compta iteracions utilitzant de manera implícita el registre


cx. Quan s’executa la instrucció loop, el registre cx es decrementa en una uni-
tat i, si el seu valor és diferent de 0, salta a l’etiqueta especificada en la mateixa
instrucció.

mov cx,10
bucle:
...
loop bucle

El registre cx s’inicialitza en 10. Cada cop que s’executa la instrucció loop, es decrementa
el valor del registre cx i mentre aquest sigui diferent de zero salta cap a l’etiqueta bucle.
En aquest cas ho farà 10 vegades, fins que cx valdrà finalment zero.
© FUOC • P06/05096/00295 • Mòdul 4 40 Assemblador

4. Subrutines

Dins d’un programa, generalment hi ha tasques comuns (per exemple, escriu-


re un missatge per pantalla) que s’han d’executar des de diferents parts del co-
di. Per a fer això, bàsicament hi ha dues possibilitats:

• Replicar el codi corresponent (escriure el missatge per pantalla) en cada


part del programa on es vol executar la tasca.

• Definir una subrutina que escriu el missatge i cridar-la des de cada part del
programa on es vol executar la tasca.

L’objectiu d’aquest capítol és conèixer els mecanismes per a declarar una su-
brutina en assemblador, i les instruccions de crida i retorn a la subrutina.

Una subrutina és un bloc de codi que realitza una tasca determinada.


Aquest bloc de codi pot ser cridat des de diferents parts del programa i,
en finalitzar, retorna al lloc des d’on s’ha cridat.

Les subrutines són preferibles a la replicació de codi per diferents motius. Per
una banda, replicar codi implica fer el programa més gran. Per l’altra, si es pre-
tén modificar el comportament d’aquest bloc de codi (perquè hi ha un error o
perquè es vol millorar) i el codi està replicat, aleshores s’ha de modificar cada
una de les còpies d’aquest codi.

L’avantatge principal de les subrutines és que permeten un disseny modular


del programa, amb la qual cosa en faciliten el disseny i la comprensió.

4.1. Crida i retorn a una subrutina

Veurem un programa que crida tres vegades una subrutina que escriu un mis-
satge per pantalla; per tant, aquest programa escriurà el missatge tres vegades
per pantalla.

Procediment
; Declaració i crida a una subrutina
A continuació explicarem
; que escriu un missatge per pantalla les instruccions remarcades
en negreta.
.model small
.stack 1024
© FUOC • P06/05096/00295 • Mòdul 4 41 Assemblador

.data
missatge db 'Bon dia',10,13,'$'

.code
inici: STARTUPCODE

call escriure
call escriure
call escriure

EXITCODE 0

escriure:
push dx
push ax
lea dx, [missatge]
mov ah, 09h
int 21h
pop ax
pop dx
ret

end inici

.stack 1024
...
call escriure
call escriure
call escriure

Per a poder cridar una subrutina cal, en primer lloc, tenir una pila declarada
Recordeu
perquè les subrutines la utilitzen de manera implícita, tal com veurem més en-
Per als programes que farem
davant. Com sempre, la pila haurà de ser prou gran. aquest curs, ni hi ha prou amb
declarar una pila de 1.024 bytes.

Per a cridar una subrutina s’utilitza la instrucció call. Una crida a una su-
brutina és semblant a una instrucció de salt, amb la diferència que, després
d’executar el codi corresponent, es retorna al punt en què s’ha cridat la
subrutina.

.stack 1024
...
escriure:
...
ret
© FUOC • P06/05096/00295 • Mòdul 4 42 Assemblador

Les subrutines s’han d’escriure fora dels límits marcats per les macros
STARTUPCODE i EXITCODE.

Les subrutines s’identifiquen amb una etiqueta, que en marca el punt d’entra-
da, i acaben amb la instrucció ret, que provoca un salt de retorn a la instruc-
ció posterior que ha cridat la subrutina. I això ho pot fer perquè guarda
l’adreça d’aquest punt, que s’anomena adreça de retorn.

.stack 1024
...
escriure:
push dx
push ax
lea dx, [missatge]
mov ah, 09h
int 21h
pop ax
pop dx
ret

En programar una subrutina, una tasca molt important és guardar els registres
que s’hi utilitzen.

Exemple

Tenim el codi següent:

mov cx, 10
bucle: call subrutina
loop bucle

Quantes vegades s’executarà la subrutina? Aparentment 10 vegades, però en realitat de-


pèn de si dins de la subrutina es modifica o no el registre cx.

Per a evitar l’escriptura de subrutines el funcionament de les quals depengui


de com és el codi que les crida, cal guardar el valor dels registres a l’inici de la
subrutina, i restaurar-lo al final.

Els registres es guarden en la pila amb la instrucció push, i per a restaurar-los,


utilitzarem la instrucció pop.

escriure:
push dx
push ax
lea dx, [missatge]
© FUOC • P06/05096/00295 • Mòdul 4 43 Assemblador

mov ah, 09h


int 21h
pop ax
pop dx
ret

En aquest cas, es guarden els registres ax i dx, que són els que s’utilitzen per a
executar el codi corresponent.

Com ja hem comentat, la instrucció call guarda l’adreça de retorn en la pila


(per això és important tenir definida una pila). Així doncs, la instrucció call
en realitat equivaldria al codi següent:

push adreca_de_retorn
jmp subrutina

De manera anàloga, la instrucció ret equivaldria al codi següent:

pop aux
jmp aux

aux és algun registre o variable auxiliar on guardaríem temporalment l’adreça que se sal-
ta de retorn de la subrutina.

Un cop vist que l’adreça de retorn es guarda en la pila, és més fàcil entendre
que quan una subrutina crida una altra subrutina, el codi s’executarà correc-
tament, perquè l’última adreça que es posa en la pila serà la primera adreça
que es traurà de la pila.

La primera instrucció de retorn torna al punt (2) i la segona, al punt (1).

4.2. Pas de paràmetres i retorn de resultats Exemple

Una subrutina que escrigui per


Pot interessar que el codi de la subrutina s’executi sobre uns valors determi- pantalla un nombre donat.

nats d’entrada. En aquest cas, cal definir un mecanisme per a poder-li passar
un o diversos paràmetres. Exemple

Una subrutina que sumi dos


També pot interessar que la subrutina, després de ser executada, retorni un re- nombres donats i que retorni
el resultat.
sultat. En aquest cas cal definir un mecanisme per al retorn del resultat.
© FUOC • P06/05096/00295 • Mòdul 4 44 Assemblador

En aquest apartat coneixerem el mecanisme de pas de paràmetres i de retorn


de resultat en els programes en assemblador.

Un dels avantatges del disseny modular és que permet dividir un programa en


tasques independents (les subrutines), de manera que diferents programadors
se’n puguin repartir la implementació. En aquest cas, és habitual que un pro-
gramador escrigui un codi que cridi subrutines escrites per altres programadors.

Hi ha moltes maneres diferents de fer el pas de paràmetres: per mitjà d’uns re-
gistres determinats, per mitjà de posicions concretes del segment de dades, per
mitjà de la pila, etc. Però cal fixar un mecanisme concret perquè tant qui crida
la subrutina com qui la programa es puguin comunicar correctament.

A una subrutina se li pot passar qualsevol nombre de paràmetres. El mecanis-


me de pas de paràmetres més utilitzat és el pas de paràmetres per la pila, ja que
permet passar un nombre inicialment il·limitat de paràmetres.

En l’exemple següent es mostra un programa que crida una subrutina que, do-
nats dos nombres passats com a paràmetres, retorna com a resultat la suma
d’aquests nombres.

Procediment
; Subrutina que retorna la suma de dos
; nombres enters passats com a paràmetre A continuació explicarem
les instruccions remarcades
en negreta.
.model small
.stack 1024
.data
num1 dw 3
num2 dw 4
res dw ?
missatge db 'La suma és $'

.code
inici: STARTUPCODE

push [num1]
push [num2]
call suma
mov [res], ax
add sp, 4

lea dx, [missatge]


mov ah, 09h
int 21h
mov dx, [res]
add dl, '0'
© FUOC • P06/05096/00295 • Mòdul 4 45 Assemblador

mov ah, 02h


int 21h

EXITCODE 0

suma:
push bp
mov bp, sp
push bx
push cx
mov bx, [bp+4]
mov cx, [bp+6]
add bx,cx
mov ax, bx
pop cx
pop bx
pop bp
ret

end inici

push [num1]
push [num2]
call suma

Com que el pas de paràmetres a la subrutina el fem per la pila, abans de cridar
la subrutina, cal deixar els paràmetres corresponents a la pila. D’aquesta ma-
nera, en executar-se la subrutina, s’ha de tenir en compte que els valors deter-
minats d’entrada sobre els quals s’executa el codi de la subrutina són en la pila.
Altrament, es cridaria la subrutina sense haver posat cap valor en la pila i, en
executar-se, no els trobaria.

En l’exemple que ens ocupa, es passen com a paràmetre els valors de les vari-
ables de memòria num1 i num2. Recordeu que quan cridem una subrutina,
l’adreça de retorn queda guardada en la pila.

És important tenir en compte l’ordre en què es posen els paràmetres en la pila,


ja que la subrutina ha de saber a quina posició de la pila són. En aquest exem-
ple, però, l’ordre no és important, ja que la suma és commutativa.

suma:
push bp
mov bp, sp
© FUOC • P06/05096/00295 • Mòdul 4 46 Assemblador

push bx
push cx
mov bx, [bp+4]
mov cx, [bp+6]
add bx,cx
mov ax, bx
pop cx
pop bx
pop bp
ret

Un cop s’han passat els paràmetres i s’ha cridat la subrutina, aquesta ha de po-
der accedir al valor dels paràmetres per tal d’executar el codi corresponent.
Això s’aconsegueix mitjançant el registre bp.

El registre bp
El registre bp és un registre base que s’utilitza com a registre auxiliar per
En aquest exemple, utilitzarem
a la manipulació de la pila. En ser un registre base, es pot fer servir per aquest registre per a accedir als
a accedir a una variable de memòria. paràmetres de la pila.

El primer que es fa en una subrutina és l’enllaç dinàmic: guarda el registre bp i fa


que apunti a una posició fixa de la pila, per exemple, a la posició actual del cim
(les dues primeres instruccions de la subrutina). Per a desfer l’enllaç dinàmic, úni-
cament cal restaurar el registre bp que s’ha guardat a l’inici de la subrutina.

Quan el bp apunta a una posició fixa de la subrutina, ja es poden guardar els


registres (tants com calguin, ja que això no afecta on apunta el bp). En l’exem-
ple que ens ocupa, els paràmetres es troben a partir de la posició [bp+4] en
endavant.

suma:
push bp
mov bp, sp
push bx
push cx
mov bx, [bp + 4]
mov cx, [bp + 6]
add bx,cx
mov ax, bx
pop cx
pop bx
pop bp
ret
© FUOC • P06/05096/00295 • Mòdul 4 47 Assemblador

Sabent la posició inicial a partir de la qual es troben els paràmetres, l’accés es


fa utilitzant el registre bp, tal com mostren les instruccions destacades en ne-
greta: [bp + 4] correspon al segon paràmetre (el valor 4) i [bp + 6] corres-
pon al primer paràmetre (el valor 3).

La subrutina accedeix als dos operands, els guarda en bx i cx, els suma i retor-
na el resultat per mitjà d’un registre, convencionalment, el registre ax.

Si s’ha de retornar el resultat per mitjà del registre ax, no cal guardar aquest registre dins
la subrutina, ni restaurar-lo. Això implica que el programador, en cridar una subrutina
que retorna un resultat, ja no tindrà cap valor important en el registre ax.

Sabent que el resultat ha de quedar finalment en el registre ax, aquest mateix


codi de l’exemple s’hauria pogut escriure més eficientment amb les instruccions
següents:
mov ax, [bp + 4]
add ax, [bp + 6}

inici: STARTUPCODE

push [num1]
push [num2]
call suma
mov [res], ax
add sp, 4

En retornar la subrutina (un cop s’ha executat la instrucció ret que retorna
l’adreça de retorn de la pila), caldrà tractar el resultat i treure els paràmtres de
la pila.

Molts llenguatges de programació d’alt nivell permeten tenir variables locals


dins una subrutina. És a dir, variables que es creen quan s’activa la subrutina
i que es destrueixen en acabar aquesta.

Les variables locals no són més que variables de memòria que ocupen temporalment un
espai en la pila, com mostra la figura següent:
© FUOC • P06/05096/00295 • Mòdul 4 48 Assemblador

La declaració d’una subrutina que contingui variables locals serà un codi sem-
blant al següent:

push bp
mov bp, sp
sub sp, N N és una constant igual
a la grandària que necessiten
les variables locals.
push ...
push ...

L’accés a les variables locals es farà utilitzant [bp - x], mentre que l’accés als
paràmetres es fa utilitzant [bp + x].

En acabar la subrutina, s’ha d’alliberar l’espai reservat per a les variables locals. x és el desplaçament necessari per
a accedir a la variable desitjada.
Això es pot fer ràpidament de la manera següent:

pop ...
pop ...
mov sp, bp
pop bp
ret

4.3. Pas de paràmetres per valor i per referència

Un recurs comú utilitzat pels llenguatges de programació d’alt nivell en pro-


gramar subrutines és el pas de paràmetres per referència: en lloc de passar com
a paràmetre una còpia d’un valor, es passa una referència* a una variable que * L'adreça de memòria
de la variable
conté el valor. Aquest mètode permet modificar el valor original de la variable.

Parlem de pas de paràmetres per valor quan es posa en la pila una còpia
del seu valor. En aquest cas, si es modifica el paràmetre, se’n modifica
només la còpia, el valor original queda inalterat.

Parlem de pas de paràmetres per referència quan es posa en la pila l’adreça


de memòria de la variable que el conté. En aquest cas, si es modifica el
contingut de l’adreça de memòria que s’ha passat com a paràmetre, es
modifica la variable original.

En general, es passarà un paràmetre per valor quan no interessi que el valor


original canviï a causa de l’execució de la subrutina. I es passarà un paràmetre
per referència quan es vulgui que el valor original es modifiqui per l’execució
de la subrutina.
© FUOC • P06/05096/00295 • Mòdul 4 49 Assemblador

L’exemple següent fa la suma de dos nombres passats com a paràmetre i retor-


na el resultat mitjançant un tercer paràmetre que es passa per referència.

Procediment
; Subrutina que suma dos nombres enters
; passats per paràmetre i deixa el resultat A continuació explicarem
les instruccions remarcades
; en un tercer paràmetre passat per referència en negreta.

.model small
.stack 1024
.data
num1 dw 3
num2 dw 4
res dw ?
missatge db 'La suma és $'

.code
inici: STARTUPCODE

push [num1]
push [num2]
lea bx, [res]
push bx
call suma
add sp, 6

lea dx, [missatge]


mov ah, 09h
int 21h
mov dx, [res]
add dl, '0'
mov ah, 02h
int 21h

EXITCODE 0

suma:
push bp
mov bp, sp
push ax
push bx
mov ax, [bp + 6]
add ax, [bp + 8]
mov bx, [bp + 4]
mov [bx], ax
pop bx
pop ax
pop bp
© FUOC • P06/05096/00295 • Mòdul 4 50 Assemblador

ret

end inici

push [num1]
push [num2]
lea bx, [res]
push bx
call suma
add sp, 6

Les dues primeres instruccions marcades en negreta mostren com s’han passat
els paràmetres per valor mitjançant la instrucció push: s’ha posat en la pila
una còpia del valor del paràmetre.

Per a passar un paràmetre per referència, cal posar en la pila l’adreça de la


variable de memòria corresponent utilitzant igualment la instrucció push. Per
a posar a la pila l’adreça d’una variable de memòria, cal accedir prèviament a
l’adreça utilitzant la instrucció lea.

Després de passar els tres paràmetres, en la pila hi haurà la còpia dels dos nom-
bres, num1 i num2, l’adreça de la variable de memòria res i, després d’executar
el call, també l’adreça de retorn.

suma:
push bp
mov bp, sp
push ax
push bx
mov ax, [bp + 6]
add ax, [bp + 8]
mov bx, [bp + 4]
mov [bx], ax
pop bx
pop ax
pop bp
ret

S’accedeix als paràmetres passats per valor mitjançant el registre bp. Cal
anar amb compte en calcular el desplaçament dels paràmetres, ja que ara
hi ha un tercer paràmetre (l’adreça de la variable res) entre num2 i l’adreça
de retorn.
© FUOC • P06/05096/00295 • Mòdul 4 51 Assemblador

Després de calcular la suma, la subrutina de l’exemple guarda el resultat en el


tercer paràmetre, passat per referència. És a dir, en la pila hi ha l’adreça de me-
mòria on es vol escriure. Per a escriure aquest resultat, s’utilitza el registre base
bx, que permet escriure en una variable de memòria. En aquest cas la subruti-
na no retorna cap resultat per al registre ax. Així doncs, si es fa servir aquest
registre dins la subrutina, cal guardar-lo inicialment i restaurar-lo al final.

suma:
push bp
mov bp, sp
push ax
push bx
mov ax, [bp + 6]
add ax, [bp + 8]
mov bx, [bp + 4]
mov [bx], ax
pop bx
pop ax
pop bp
ret

El fet de si la subrutina retorna o no un resultat no depèn de si hi ha algun


paràmetre passat per referència.

push [num1]
push [num2]
lea bx, [res]
push bx
call suma
add sp, 6

En acabar la subrutina, s’han de treure de la pila els paràmetres que s’hi havien
posat. Una manera fàcil de fer-ho consisteix a sumar a sp el que ocupen els
paràmetres; en aquest cas, 6 bytes.

En retornar de la subrutina no cal recuperar el resultat perquè ja hi és en la va-


riable de memòria res.

Ja hem comentat que un paràmetre passat per referència normalment serveix


per a passar l’adreça d’una variable de memòria que es vol que pugui ser mo-
dificada dins la subrutina, però també serveix per a passar un vector com a pa-
ràmetre.
© FUOC • P06/05096/00295 • Mòdul 4 52 Assemblador

Per conveni, els vectors es passen sempre per referència, encara que el seu con-
tingut no s’hagi de modificar; és a dir, es posarà en la pila l’adreça del primer
element del vector. Per a accedir als elements del vector, es pot utilitzar el re-
gistre bx per a apuntar a l’inici del vector i el registre si per a especificar el
desplaçament corresponent.

Cal tenir present que quan s’utilitzen registres o variables de memòria decla-
rades en el segment de dades se’n sap la mida, però quan s’accedeix a l’adreça
d’una variable que no es coneix a priori, pot ser que no sapiguem la mida dels
operands.

Exemple

Si s’especifica la instrucció mov [bx + si], al, ja sabem que la mida dels operands és
8 bits. Però si s’especifica la instrucció mov [bx + si], 0, no se sap si l’adreça [bx+si]
fa referència a una variable de 8 bits o a una de 16 bits. En assemblar el codi, el mateix
assemblador s’adona de la possible confusió i retorna el missatge següent:

*Warning* fitxer.asm(...) Argument needs type override

És a dir, que l’argument (l’operand) necessita especificar el seu tipus. Es poden utilitzar els
modificadors següents per a informar que la variable de memòria és de 8 bits o de 16 bits:

mov byte ptr [bx + si], 0


mov word ptr [bx + si], 0
© FUOC • P06/05096/00295 • Mòdul 4 53 Assemblador

5. Programació dels dispositius d’E/S

5.1. Instruccions de suport d’E/S

Els dispositius perifèrics es manipulen mitjançant lectures i/o escriptures en


els registres del controlador corresponent. Aquests registres poden estar ma-
pats en la memòria, com, per exemple, la pantalla, o bé en l’espai d’adreces
d’E/S. Per a accedir a registres mapats en la memòria, s’utilitzen les instruc-
cions normals d’accés a la memòria que hem vist fins ara. Però per a accedir
a registres mapats en l’espai d’adreces d’E/S, el llenguatge d’assemblador de
l’i8086 proporciona unes instruccions específiques:

• IN per a llegir d’un registre de l’espai d’adreces d’E/S.

• OUT per a escriure en un registre de l’espai d’adreces d’E/S.

Per a gestionar el funcionament d’un dispositiu perifèric, cal accedir als regis-
tres del controlador d’aquest dispositiu perifèric. Cada registre de cada contro-
lador té un paper particular en el funcionament del dispositiu perifèric. Per tant,
és necessari conèixer l’adreça d’aquests registres i el significat que té cada bit de
cada registre.

Exemple

Les dades del controlador del teclat són en un registre de 8 bits que es troba en l’adre-
ça 60h de l’espai d’E/S. Aquest registre conté el codi de l’última tecla premuda. A més, el bit
de més pes d’aquest registre indica si la tecla ha estat premuda (make) o alliberada (break). Els
altres set bits codifiquen la tecla que ha estat premuda.

En l’exemple següent es presenta un programa en el qual es capturen 10 mo-


viments de les tecles (prémer o alliberar). En cada cas, s’escriu per pantalla un
missatge que indica l’esdeveniment ocorregut.

Procediment
; Accés directament als registres del
A continuació explicarem
; controlador de teclat (per enquesta) les instruccions remarcades
en negreta.
.model small
.data
missatge1 db 'Prem les tecles',10,13,'$'
missatge2 db 'Fi del programa',10,13,'$'
mak db '<make>',10,13,'$'
brk db '<break>',10,13,'$'
© FUOC • P06/05096/00295 • Mòdul 4 54 Assemblador

.code
inici: STARTUPCODE

lea dx, [missatge1]


mov ah, 09h
int 21h

mov cx,10
enquesta:
in al, [64h]
test al, 01h
je enquesta
in al, [60h]
test al,80h
jne es_brk ;make/break
es_mak:
lea dx, [mak]
jmp escriu
es_brk:
lea dx, [brk]
escriu:
mov ah, 09h
int 21h
loop enquesta

fi: lea dx, [missatge2]


mov ah, 09h
int 21h

EXITCODE 0
end inici

mov cx,10
enquesta:

Després d’escriure el missatge en què es demana prémer les tecles, hi ha un bu-


cle principal (amb un comptador de 10 iteracions). Com que no hi ha cap su-
brutina, no es declara la pila.

Abans de seguir amb l’anàlisi del codi, és important conèixer el funcionament


dels registres del controlador del teclat. En concret, interessa gestionar dos re-
gistres de 8 bits, el d’estat i el de dades, que es troben, respectivament, en les
adreces 64h i 60h de l’espai d’adreçament d’E/S.
© FUOC • P06/05096/00295 • Mòdul 4 55 Assemblador

Els bits que interessen d’aquests registres són els següents:

• Quan el bit 0 del registre d’estat val 1, indica que s’ha tocat alguna tecla.
En llegir del registre de dades aquest bit, es posa automàticament a zero.

• Quan s’ha tocat una tecla, el bit 7 del registre de dades val 0 si s’ha premut
la tecla o 1 si ha estat alliberada.

enquesta:
in al, [64h]
test al, 01h
je enquesta
in al, [60h]
test al,80h
jne es_brk ;make/break
es_mak:

Una vegada estudiat el funcionament dels registres del controlador del teclat,
es pot implementar l’accés al teclat mitjançant l’enquesta.

L’accés mitjançant l’enquesta consisteix a llegir constantment el regis-


tre d’estat del controlador del dispositiu, fins que es detecta algun esde-
veniment.

En l’exemple podeu veure com es llegeix el registre de l’adreça 64h de l’espai


L'enquesta, en aquest cas, es fa per
d’adreçament d’E/S mitjançant la instrucció in. A continuació es compara el a detectar si s'ha tocat una tecla.

bit zero del valor llegit utilitzant la màscara de bits 01h per a aïllar el bit zero.
Mentre aquest bit sigui zero, el codi salta novament a la instrucció que llegeix
el registre d’estat.

Una màscara de bits és una llista de bits en la qual només els bits que
interessa tractar valen 1. Els altres valen 0.

L’etiqueta enquesta la utilitza tant el bucle que compta les iteracions com el
que implementa l’enquesta. Això no representa cap problema des del punt de
vista del llenguatge d’assemblador.
© FUOC • P06/05096/00295 • Mòdul 4 56 Assemblador

enquesta:
in al, [64h]
test al, 01h
je enquesta
in al, [60h]
test al,80h
jne es_brk ;make/break
es_mak:

Si es toca una tecla, el bit zero del registre d’estat del controlador de teclat es
posa a 1 i, per tant, finalitza l’enquesta. En aquest moment, l’acció següent
consistirà a comprovar el motiu de l’esdeveniment: saber si la tecla ha estat
premuda o alliberada.

Recordeu

El bit 7 del registre de dades del controlador de teclat indica que la tecla ha estat premuda
(bit7 = 0) o alliberada (bit7 = 1). Els 7 bits restants (del 0 al 6) codifiquen la tecla tocada.

En aquest cas, es llegeix el registre de dades (adreça 60h) mitjançant la instruc-


ció in, i s’aïlla el valor del bit 7 mitjançant la màscara 80h.

es_mak:
lea dx, [mak]
jmp escriu
es_brk:
lea dx, [brk]
escriu:
mov ah, 09h
int 21h
loop enquesta

Segons l’esdeveniment detectat, s’inicialitza el registre dx amb l’adreça del


missatge que es vol escriure: <make> o <break>. A continuació, i abans d’aca-
bar el bucle, s’escriu el missatge corresponent.

Quan es manté una tecla premuda uns quants segons, genera repetidament ac-
cions corresponents a la tecla premuda. D’aquesta manera es pot escriure mol-
tes vegades un caràcter determinat únicament mantenint premuda la tecla
corresponent.

5.1.1. Les instruccions in i out

Com ja hem comentat, en assemblador, l’accés als registres mapats en l’espai


d’E/S es fa mitjançant les instruccions in i out.
© FUOC • P06/05096/00295 • Mòdul 4 57 Assemblador

Exemple d’us de la instrucció in

Per a adreces entre la 000h i la 0ffh:

• Per a llegir un valor de 8 bits: in al, [xxh] ;


• Per a llegir un valor de 16 bits: in ax, [xxh] ;

Per a adreces superiors a 255, s’ha d’utilitzar el registre dx per a especificar-les:

• Per a llegir un valor de 8 bits: in al, dx ;


• Per a llegir un valor de 16 bits: in ax, dx ;

Per a escriure un registre de l’espai d’adreçament d’E/S, utilitzarem la instruc-


ció out d’una manera similar.

Per a adreces entre la 000h i la 0ffh:

• Per a escriure un valor de 8 bits: out [xxh], al ;


• Per a escriure un valor de 16 bits: out [xxh], ax ;

Per a adreces superiors a 255, s’ha d’utilitzar el registre dx per a especificar-les:

• Per a escriure un valor de 8 bits: out dx, al ;


• Per a escriure un valor de 16 bits: out dx, ax ;

5.1.2. El registre de dades del controlador de teclat

Com ja hem comentat, el registre de dades del controlador de teclat codifica,


Exemple
en els set bits de menys pes, la tecla premuda. La codificació d’aquest registre
Les tecles F1, F2, Maj, Retorn,
no es correspon amb el codi ASCII de la tecla, ja que s’han de codificar tecles Control, Alt, etc.
especials que no apareixen en el codi ASCII.

El controlador de teclat defineix el codi de rastreig, que codifica la posició que


ocupa la tecla en el teclat.

Exemples de codis de reastreig

• codi 1: tecla <Esc>


• codi 2: tecla <1>
• codi 3: tecla <2>
• codi 16: tecla <q>
• codi 17: tecla <w>
• codi 18: tecla <e>
• codi 19: tecla <r>
• codi 20: tecla <t>

Per a passar del codi de rastreig al codi ASCII, s’acostuma a definir un vector
que contingui en cada posició corresponent al codi de rastreig el codi ASCII de
la tecla associada.

traduccio db –1,–1,’1234567890’,–1,–1,–1,–1,’qwertyuiop’...

5.2. Rutines d’atenció a perifèrics

Els dispositius perifèrics llegeixen informació o l’escriuen de manera asíncro-


na respecte de l’execució de les instruccions per part del processador. És a dir,
© FUOC • P06/05096/00295 • Mòdul 4 58 Assemblador

un usuari prem les tecles sense haver-les de sincronitzar amb el rellotge del
processador. Per a accedir a aquesta informació, el processador s’ha de sincro-
nitzar amb el controlador dels dispositius perifèrics.

Hi ha dues tècniques bàsiques que permeten la sincronització entre el pro-ces-


sador i els controladors dels dispositius d’E/S:

• Sincronització per enquesta. Tècnica que consulta activament el registre


Sincronització
d’estat del controlador (normalment mapat en l’espai d’adreçament d’E/S) per enquesta
fins que detecta que s’ha produït un esdeveniment (per exemple, que s’ha Aquesta tècnica es basa
en els conceptes que heu vist
premut una tecla). en l’apartat anterior.

• Sincronització per interrupció. Mecanisme suportat pel processador que


permet al processador anar executant qualsevol codi i, en produir-se un es-
deveniment, per exemple, quan es prem una tecla, s’executa una rutina
d’atenció a la interrupció que l’atén.

En la sincronització per interrupció, per a associar una rutina a la interrupció


d’un dispositiu perifèric, s’utilitza el vector d’interrupcions, que es troba al
principi de la memòria (a partir de l’adreça 0) i conté l’adreça d’una rutina as-
sociada a cada dispositiu. Quan es produeix una interrupció, s’executa la ruti-
na associada al dispositiu corresponent. Per tant, si es vol executar una certa
rutina en produir-se la interrupció d’un dispositiu, només cal posar en la po-
sició corresponent del vector d’interrupcions l’adreça de la rutina en qüestió.

Una rutina d’atenció a la interrupció és una rutina qualsevol, com les vistes
fins ara, però amb dues particularitats:

• Quan finalitza la rutina ha d’informar el controlador d’interrupcions que


* End of interruption,
ja ha finalitzat. Això s’ha de fer escrivint el codi EOI* en el registre de con- final d’interrupció

trol del controlador d’interrupcions.

• S’ha de fer servir IRET (retorn d’una interrupció) en lloc de la instrucció


habitual de retorn.

La rutina d’atenció a la interrupció es pot executar en qualsevol moment, in-


dependentment de l’execució del programa. Per tant, és difícil que des del
programa se li puguin passar paràmetres, ja que no se sap en quin moment
s’executarà.

Procediment
; Accés directament als registres del
; controlador de tecla (per interrupció) A continuació explicarem
les instruccions remarcades
en negreta.
.model small
.data
© FUOC • P06/05096/00295 • Mòdul 4 59 Assemblador

missatge1 db 'Prem les tecles',10,13,'$'


missatge2 db 'Fi del programa',10,13,'$'
mak db '<make>',10,13,'$'
brk db '<break>',10,13,'$'
numTecles dw 0
.code
inici: STARTUPCODE

xor ax,ax
mov es,ax
push es:[36]
push es:[38]
lea ax, [RAIteclat]
cli
mov es:[36], ax
mov es:[38], cs
sti

lea dx,[missatge1]
mov ah, 09h
int 21h

wait: cmp [numTecles], 10


jl wait

lea dx, [missatge2]


mov ah, 09h
int 21h

cli
pop es:[38]
pop es:[36]
sti
EXITCODE 0

RAIteclat:
push ax
push dx
in al, [60h]
test al, 80h
jne esBk
esMk: lea dx, [mak]
jmp escr
esBk: lea dx, [brk]
escr: mov ah, 09h
int 21h
inc [numTecles]
© FUOC • P06/05096/00295 • Mòdul 4 60 Assemblador

mov al, 20h


out [20h], al
pop dx
pop ax
iret
end inici

El vector d’interrupcions està situat en la memòria a partir de l’adreça física 0,


i cada adreça d’una rutina d’atenció a la interrupció ocupa 4 bytes.

Per tant, la rutina d’atenció a la interrupció del teclat estarà situada a partir de
l’adreça 36. Perquè s’executi una certa rutina quan hi hagi interrupció del te-
clat, únicament cal posar l’adreça d’aquesta rutina a partir de l’adreça 36 de
memòria. Prèviament, però, caldrà guardar l’adreça de la rutina que hi hagi en
aquella posició i restaurar-la abans d’acabar el programa.

Per a accedir a les adreces del programa, com ja hem vist, es posa l’adreça entre
claudàtors. Per a accedir a adreces fora de l’espai d’adreçament del programa,
s’utilitzen registres de propòsit específic, els registres de segment.

Hi ha quatre registres de segment, anomenats cs, ds, es i ss. D’aquests,


únicament utilitzarem el registre es per a accedir a adreces fora dels lí-
mits del programa. Els altres registres tenen un ús molt determinat i no
els hem de manipular explícitament.

xor ax,ax
mov es,ax ; posem 0 en el registre es
push es:[36] ; guardem els dos primers bytes
push es:[38] ; guardem els altres dos bytes

S’utilitza el registre es per a generar l’adreça 0 i guardar l’adreça de la rutina


que hi ha a partir de l’adreça 36 de memòria.

lea ax, [RAIteclat] ; agafem l’adreça de la rutina


cli
mov es:[36], ax ; posem l’adreça
mov es:[38], cs ; posem el segment
sti

També s’utilitza el registre es per a posar l’adreça de la nostra rutina a partir


de l’adreça 36 de memòria.
© FUOC • P06/05096/00295 • Mòdul 4 61 Assemblador

Primer es posa l’adreça de la rutina en el registre ax i després, en la posició 36


En aquest curs no entrarem en
de memòria. A continuació, mitjançant el registre cs es completa l’adreça detalls sobre l’ús dels registres de
segment, sinó que només explicarem
que s’ha de modificar el vector
(de 4 bytes) perquè es pugui executar la rutina des de fora del programa. d’interrupcions i com es fa.

Les instruccions cli i sti serveixen, respectivament, per a inhibir i desinhibir


interrupcions, i cal posar-les abans i després de modificar el vector d’interrup-
cions. Altrament, si casualment es rep una interrupció de teclat quan només
s’ha modificat la meitat de l’adreça (els dos primers bytes), se saltaria a una po-
sició aleatòria de memòria i segurament el programa es quedaria penjat.

wait: cmp [numTecles], 10


jl wait

Si el programa només contingués aquest codi, es quedaria permanentment


Rutina d'atenció
bloquejat. Però en realitat hi ha un altre fragment de codi que s’hi executa a la interrupció

concurrentment: la rutina d’atenció a la interrupció del teclat RAIteclat. En aquesta rutina, es pot de-
manar qualsevol operació, per
Cada cop que es prem una tecla, l’execució del bucle d’espera activa es detin- exemple, incrementar el nom-
drà i s’executarà la rutina d’atenció a la interrupció. En general, però, no cal bre d’esdeveniments de teclat
numTecles.
fer una espera activa mentre no es prem una tecla.

Exemple

Mentre no es prem cap tecla, els programes amb jocs segueixen movent els objectes per
la pantalla i en detectar que es prem una tecla modifiquen el comportament del joc en
funció de la tecla premuda.

mov al, 20h


out [20h], al
pop dx
pop ax
iret
end inici

En aquest programa, la rutina d’atenció a la interrupció guarda (i restaura) els


registres, escriu un missatge en funció de la tecla premuda i incrementa el
nombre d’esdeveniments detectats relacionats amb el teclat.

Hi ha dues particularitats, però, en les rutines d’atenció a la interrupció, tal


com es pot veure en el codi marcat en negreta:

• Abans de restaurar els registres (o com a màxim abans de restaurar el regis-


tre ax), s’ha de generar el senyal de final d’interrupció (EOI) posant el valor
20h en el registre de l’espai d’E/S 20h.

* Interruption return.
• En retornar de la subrutina, cal fer servir la instrucció iret* en lloc de ret.
© FUOC • P06/05096/00295 • Mòdul 4 62 Assemblador

cli
pop es:[38]
pop es:[36]
sti
EXITCODE 0

Abans d’acabar el programa, si es vol que el teclat funcioni com funcionava


abans d’executar-lo, s’han de restaurar els valors originals de la rutina d’aten-
ció a la interrupció en el teclat (que s’havien guardat prèviament) en el vector
d’interrupcions.

Si el registre es no ha estat modificat i encara manté el valor zero, es restauren


els valor guardats en la pila i es posen en el vector d’interrupcions en les posi-
cions corresponents.

Tal com passava en modificar el vector d’interrupcions a l’inici del programa,


cal restaurar-lo inhibint prèviament les interrupcions i desinhibint-les poste-
riorment mitjançant les instruccions cli i sti.

5.3. Programació de dispositius del PC

En aquest apartat farem un breu resum del que heu de saber d’alguns disposi-
tius que us poden ser útils per poder fer la implementació de la pràctica.

5.3.1. Rellotge programable (Timer)

Els ordinadors tenen rellotges programables que permeten implementar retards


de temps fix, independent de la velocitat del processador. El senyal generat per un
dels rellotges del PC es connecta a la línia d’interrupció IRQ0, la més prioritària.
Aquest senyal es genera unes 18 vegades per segon.

El rellotge treballa sempre per interrupció i la rutina d’atenció a la interrupció


(RAI) IRQ0 s’associa al vector 08h. És a dir, l’adreça de la RAI es troba a la po-
sició 8 de la taula de vectors d’interrupcions.

5.3.2. Teclat

El teclat és el dispositiu d’E/S més bàsic. La pulsació d’una tecla genera una
interrupció de maquinari que es pot reprogramar per a identificar el codi de
la tecla premuda. En més alt nivell també es poden fer servir les funcions del
sistema operatiu 08h (sense echo) i 01h (amb echo) per llegir la tecla.
© FUOC • P06/05096/00295 • Mòdul 4 63 Assemblador

5.3.3. Pantalla

La pantalla en mode text es pot considerar com una matriu de caràcters de 25 files
per 80 columnes. La posició (0,0) correspon a la cantonada superior esquerra de
la pantalla.

Els caràcters que s’hi han de representar es guarden codificats en ASCII (valor
entre 0 i 255) i, per tant, ocupen 1 byte d’informació.

Els atributs de color també es codifiquen amb 1 byte. Això permet 256 combi-
nacions de color en cada posició de pantalla. Cada bit de l’atribut té un signi-
ficat ben definit:

Exemple

Blink Color del fons Color del caràcter

1 0 0 0 0 1 1 1

• El bit de més pes és el bit de blink (parpelleig): quan està a 1, el caràcter parpelleja; quan
està a 0, es representa normalment.

• Els bits 6, 5 y 4 corresponen al color de fons. Per tant, podem tenir 7 combinacions de
color de fons.

• Els bits 3 a 0 corresponen al color del text. El bit 3 indica la "intensitat" (0 = intensitat
baixa). Així, tenim 7 colors de text amb 2 intensitats.

Per a modificar un text a pantalla, es poden fer servir diferents mètodes:

• Accedir a l’adreça de memòria que conté els dos bytes que codifiquen el ca-
ràcter i l’atribut visualitzat a pantalla.

• Fer una crida a les funcions del sistema operatiu.

• Utilitzar els serveis de la BIOS .

En algunes d’aquestes crides cal passar com a paràmetre una cadena de text
definida en assemblador.

Una cadena de text en assemblador és una llista de caràcters, especifi-


cats mitjançant el codi ASCII, finalitzada amb el caràcter '$'.

El codi ASCII dels caràcters es pot especificar, o bé posant directament els ca-
ràcters entre cometes, o bé (en cas de caràcters de control) posant el codi ASCII
separat per comes.
© FUOC • P06/05096/00295 • Mòdul 4 64 Assemblador

5.4. Funcions del sistema operatiu i serveis de la BIOS relacionats


amb el teclat i la pantalla

Per a fer una crida a una funció del sistema operatiu cal fer servir la instrucció Nota

següent: En aquest tema ens centrarem


únicament en algunes crides
bàsiques d’accés a la pantalla
i al teclat encara que n’hi ha
moltes més.
int 21h

Abans cal especificar la funció del sistema que es vol utilitzar, posant el seu va-
lor en el registre ah i passar els paràmetres necessaris en els registres que calgui.

Per aclarir l’ús d’aquestes crides farem un programa que escriu un missatge inicial
per pantalla, llegeix una tecla, i escriu un segon missatge abans de finalitzar:

;Escriptura i lectura de pantalla/teclat amb crides a


rutines del sistema operatiu
.model small
.data
missatge1 db 'Prem qualsevol tecla',10,13,'$'
missatge2 db 'Programa finalitzat',10,13,'$'
saltlinia db 10,13,'$'
tecla db ?

.code
inici: STARTUPCODE

lea dx, [missatge1]


mov ah, 09h
int 21h

mov ah, 01h


int 21h
mov [tecla], al

lea dx, [saltlinia]


mov ah,, 09h
int 21h

lea dx, [missatge2]


mov ah, 09h
int 21h

EXITCODE 0
end inici
© FUOC • P06/05096/00295 • Mòdul 4 65 Assemblador

Hem remarcat en negreta quatre grups de tres instruccions:

• El primer grup d’instruccions escriu per pantalla el missatge1.


• El segon grup d’instruccions llegeix una tecla
• Els altres dos grups escriuen altres missatges per pantalla.

;Escriptura i lectura de pantalla/teclat amb crides a


funcions del sistema operatiu
.model small
.data
missatge1 db 'Prem qualsevol tecla',10,13,'$'
missatge2 db 'Programa finalitzat',10,13,'$'
saltlinia db 10,13,'$'
tecla db ?

.code
inici: STARTUPCODE

lea dx, [missatge1]


mov ah, 09h
int 21h

mov ah, 01h


int 21h
mov [tecla], al

lea dx, [saltlinia]


mov ah,, 09h
int 21h

lea dx, [missatge2]


mov ah, 09h
int 21h

EXITCODE 0
end inici

Com ja sabem, per a poder escriure un text, ha d’estar inicialment emmagat-


zemat a memòria. En el nostre cas tenim les variables de memòria missatge1,
missatge2, i saltlinia, cada una de les quals conté una cadena de text, i la
variable de memòria tecla que no està inicialitzada.

• Les dues primeres cadenes de text contenen un missatge i acaben amb els
caràcters 10 i 13, que codifiquen el salt de línia en codi ASCII.

La variable saltlinia conté únicament un salt de línia.

La variable tecla, que està sense inicialitzar (especificat amb un ?), servirà
per a guardar-hi la tecla llegida pel teclat.
© FUOC • P06/05096/00295 • Mòdul 4 66 Assemblador

;Escriptura i lectura de pantalla/teclat amb crides a


funcions del sistema operatiu

.model small
.data
missatge1 db 'Prem qualsevol tecla',10,13,'$'
missatge2 db 'Programa finalitzat',10,13,'$'
saltlinia db 10,13,'$'
tecla db ?

.code
inici: STARTUPCODE

lea dx, [missatge1]


mov ah, 09h
int 21h

mov ah, 01h


int 21h
mov [tecla], al

lea dx, [saltlinia]


mov ah,, 09h
int 21h

lea dx, [missatge2]


mov ah, 09h
int 21h

EXITCODE 0
end inici

Per a escriure un text per pantalla, el registre dx ha de contenir l’adreça de memò-


ria a partir de la qual (i fins a trobar el caràcter '$') hi ha el text que es vol escriure.

Per a obtenir l’adreça de memòria d’una variable, s’utilitza la instrucció lea


tal i com es mostra a l’exemple.

A continuació s’ha d’activar la interrupció 21h, posant prèviament un 09h al


registre ah, que indica que es vol escriure una cadena de caràcters per pantalla.

;Escriptura i lectura de pantalla/teclat amb crides a


funcions del sistema operatiu

.model small
.data
missatge1 db 'Prem qualsevol tecla',10,13,'$'
missatge2 db 'Programa finalitzat',10,13,'$'
© FUOC • P06/05096/00295 • Mòdul 4 67 Assemblador

saltlinia db 10,13,'$'
tecla db ?

.code
inici: STARTUPCODE

lea dx, [missatge1]


mov ah, 09h
int 21h

mov ah, 01h


int 21h
mov [tecla], al

lea dx, [saltlinia]


mov ah,, 09h
int 21h

lea dx, [missatge2]


mov ah, 09h
int 21h

EXITCODE 0
end inici

De manera similar, per a llegir un caràcter del teclat s’activa la interrupció 21h,
però posant prèviament un 01h al registre ah. D’aquesta manera el programa
s’espera fins que l’usuari prem una tecla.

La tecla premuda es veu per pantalla i queda emmagatzemada al registre al.


Normalment és important guardar el valor de la tecla premuda sobre alguna
variable de memòria (la variable tecla a l’exemple), ja que el registre al es
pot anar modificant en les properes interrupcions.

A l’exemple heu pogut veure una forma d’accés a la pantalla i una forma d’ac-
cés al teclat, però hi han altres variants que poden ser interessants durant
aquest curs. A continuació es descriuen algunes rutines associades a funcions
del sistema operatiu

Funció 09h del sistema operatiu: escriptura d' un text per pantalla

La manera d’escriure un text per pantalla és la descrita en l’exemple anterior:

text és la variable que conté el text


lea dx, [text] (acabat amb '$') que
es desitja escriure
mov ah, 09h
int 21h
© FUOC • P06/05096/00295 • Mòdul 4 68 Assemblador

Funció 02h del sistema operatiu: escriptura d’un caràcter per pantalla

Si es desitja escriure un sol caràcter per pantalla, no cal crear una cadena de
dos caràcters acabada amb el '$'. Hi ha una altre interrupció que permet fer-ho
més fàcilment:

car és el codi ASCII del caràcter


mov dl, car que volem escriure, o una variable,
o un registre que conté aquest codi.
mov ah, 02h
int 21h

Exemple

Qualsevol de les tres instruccions següents posa el codi ASCII de la lletra 'A' al registre dl,
ja que el codi ASCII de la lletra 'A' és el 65, o bé 41 si s’expressa en hexadecimal:

mov dl, 'A'


mov dl, 65
mov dl, 41h

No hi ha cap rutina de sistema que escrigui directament nombres per pantalla.


Com que l’escriptura per pantalla es fa sempre utilitzant el codi ASCII del caràcter
que es desitja escriure, l’escriptura de nombres és una tasca més complicada.

Si el nombre a escriure és d’un sól dígit, i sabent que el codi ASCII dels dígits
0..9 són valors consecutius, es pot utilitzar la interrupció descrita al cas ante-
rior de la manera següent:

num és el valor del nombre


mov dl, '0' (entre 0 i 9) que es vol escriure.

add dl, num


mov ah, 02h
int 21h

S’ha de sumar el valor del codi ASCII del dígit '0' al valor del nombre que es
vol escriure. El resultat d’aquesta suma és el valor del codi ASCII del dígit que
es vol escriure.

Si es vol escriure un nombre amb diversos dígits, l’escriptura s’ha de fer dígit
a dígit. Per això s’han de fer successives divisions per 10 i s’ha d’anar escrivint
cada un dels dígits.

Funció 01h i 08h del sistema operatiu: lectura d’un caràcter per teclat

Hi ha dues variants per a fer la lectura del teclat utilitzant la interrupció 21h
caràcter a caràcter:
© FUOC • P06/05096/00295 • Mòdul 4 69 Assemblador

• Amb echo, el caràcter que s’introdueix pel teclat s’escriu alhora per la pantalla.

car és una variable de memòria en


mov ah, 01h la que guardem el valor introduït
pel teclat.
int 21h
mov [car], al

• Sense echo, el caràcter que s’introdueix pel teclat no s’escriu per pantalla.

mov ah, 08h


int 21h
mov [car], al

En tots dos casos, i de manera similar a l’exemple, el caràcter introduït queda


emmagatzemat en el registre al.

Per fer una crida a un dels serveis de la BIOS cal fer servir La instrucció següent:

int 10h

De la mateixa manera que en les funcions del sistema operatiu, cal especificar
la crida del sistema que es vol utilitzar, posant el seu valor en el registre ah i
afegir els paràmetres necessaris en els altres registres indicats.

Us presentem una llista de crides que poden ser útils per a accedir a pantalla.
Aquestes crides no depenen del sistema operatiu, estan implementades en la
BIOS del PC.

Servei 02h de la BIOS: posar el cursor en una posició determinada de pantalla

Els paràmetres són els següents:

ah 02h (servei 02h)


bh pàgina (en el nostre cas, sempre un 0)
dh fila (coordenada y)
dl columna (coordenada x)

Servei 03h de la BIOS: llegir la posició del cursor en pantalla

Els paràmetres són els següents:

ah 03h (servei 03h)


bh pàgina (en el nostre cas, sempre un 0)
dh fila (coordenada y)
dl columna (coordenada x)
© FUOC • P06/05096/00295 • Mòdul 4 70 Assemblador

Servei 08h de la BIOS: llegir caràcter i atribut de la posició del cursor


en pantalla

Els paràmetres són els següents:

ah 08h (servei 08h)


bh pàgina (en el nostre cas, sempre un 0)
al caràcter (el caràcter llegit)
ah atribut (característiques de color)

Servei 09h de la BIOS: escriure un caràcter i atribut a la posició del cursor


en pantalla

Els paràmetres són els següents:

ah 09h (servei 09h)


bh pàgina (en el nostre cas, sempre un 0)
al caràcter (el caràcter que volem escriure)
bl atribut (característiques de color)
cx comptador (nombre de vegades que el volem escriure)
© FUOC • P06/05096/00295 • Mòdul 4 71 Assemblador

6. Referència ràpida

6.1. Blocs d’un programa en assemblador

Els programes en assemblador estan formats per blocs, anomenats segments.


En un programa cal definir els segments següents:

• El segment de codi, que conté les instruccions que formen el programa.

• El segment de pila, que conté els valors que es guarden en la pila.

• El segment de dades, que conté les variables de memòria que s’utilitzen en


el programa.

Tot i que hi ha altres maneres de declarar un programa en assemblador, nosal-


tres utilitzarem sempre el model que es presenta en l’exemple següent. Aquest
exemple mostra el codi d’un programa simple en assemblador que escriu per
pantalla el missatge “Bon dia!”.

Procediment
;Escriptura d'un missatge per pantalla
A continuació explicarem
les instruccions remarcades
.model small en negreta.
.data
missatge db 'Bon dia!$'

.code
inici: STARTUPCODE

lea dx, [missatge]


mov ah, 09h
int 21h

EXITCODE 0
end inici

.model small
.data
missatge db 'Bon dia!$'

.code
© FUOC • P06/05096/00295 • Mòdul 4 72 Assemblador

Aquest programa està format per dos blocs:

• El bloc identificat per la directiva .data, que conté les dades del programa
(el segment de dades).

• El bloc identificat per la directiva .code, que conté les instruccions del
programa (el segment de codi).

La directiva .model small fa referència al model de memòria que es vol uti-


litzar; en aquest cas, un model petit en què tot el codi és en el mateix segment.

missatge db 'Bon dia!$'

.code
inici: STARTUPCODE

lea dx, [missatge]


mov ah, 09h
int 21h

El segment de dades conté una única variable anomenada missatge, que inicial-
ment conté el text que es vol escriure per pantalla.

El segment de codi, a més d’unes macros que indiquen l’inici i el final del pro-
grama, conté tres instruccions.

inici: STARTUPCODE

lea dx, [missatge]


mov ah, 09h
int 21h

EXITCODE 0
end inici

El segment de codi, segons el model de programa que seguirem durant el


curs, comença amb una etiqueta (en aquest cas, inici) seguida de la macro
STARTUPCODE, que inicialitza alguns registres del processador.

Per a finalitzar el programa s’utilitza la macro EXITCODE 0, i per a indicar la Podeu veure més detalls en l’apartat
“Lectures i escriptures de cadenes
de caràcters” en aquest mateix mòdul
finalització del segment de codi s’ha de posar end seguit del nom de l’etiqueta didàctic

de començament de codi: inici.

Les tres instruccions restants són les que fan que s’escrigui el missatge per pan-
talla.
© FUOC • P06/05096/00295 • Mòdul 4 73 Assemblador

6.2. Registres del processador i8086

6.2.1. Registres d’ús general

Els registres d’ús general són aquells amb els quals treballa normalment el
codi d’aplicació d’una manera explícita. Tots tenen 16 bits i es poden utilitzar
els 8 bits menys significatius separadament per a realitzar operacions de 8 bits.

ax: registre acumulador

El registre ax s’utilitza normalment per a emmagatzemar resultats intermedis


Resgistres d'ús general
en càlculs i les dades utilitzades amb més freqüència. La major part de les ins-
ax, bx, cx i dx tenen la parti-
truccions poden treballar amb aquest registre i, fins i tot, algunes s’executen cularitat que es poden utilitzar
com dues unitats de 8 bits, i
més ràpidament si ho fan sobre aquest registre. Només alguns tipus d’adreça- llavors se'ls anomena x h
ment tenen prohibit l’ús d’aquest registre. (high) i x l (low), on x és el
mnemotècnic d'un sol caràcter
del registre.

bx: registre base

El registre bx, tot i ser també de propòsit general, s’utilitza sobretot amb adre-
çament indirecte i directe relatiu al registre. És a dir, si es vol accedir a una tau-
la de dades, s’hi emmagatzemaria l’adreça on comença la taula, de manera
que, amb les instruccions apropiades, n’hi hauria prou de fer referència al des-
plaçament de l’element al qual es vol accedir dins de la taula per a llegir o es-
criure una dada en la posició desitjada.

cx: registre comptador

El registre cx s’utilitza com a comptador per a efectuar tasques repetitives. Al-


gunes instruccions permeten executar bucles repetitius d’una manera molt efi-
cient només inicialitzant el registre amb el nombre de vegades que es vol
repetir el procés.

dx: registre de dades

El registre dx s’ha de veure gairebé com un segon acumulador que serveix bà-
sicament per a augmentar el rendiment del processador, en disposar de 32 bits
addicionals de dades que fan innecessari molt sovint haver de portar a la me-
mòria principal una dada que s’utilitzarà immediatament.

bp: registre base de pila

El registre bp se sol utilitzar per a emmagatzemar l’adreça del bloc de paràme-


tres següent dins de la pila. S’ha de dir també que aquest registre es pot enten-
dre com un segon registre base.
© FUOC • P06/05096/00295 • Mòdul 4 74 Assemblador

si: registre d’índex origen (source)


si i si

Aquests són els dos registres ín-


El registre si se sol utilitzar amb l’adreçament indirecte, directe relatiu al re- dex principals.
gistre i indexat; és a dir, per a referir-se a la posició de memòria que indica o a
aquesta adreça més un increment donat. En diem registre d’origen perquè algu-
nes instruccions complexes de tractament de cadenes de caràcters l’utilitzen
per a localitzar les dades amb què es treballarà i són capaces d’autoincremen-
tar-lo o de fer-lo decréixer durant el procés de la instrucció.

di: registre d'índex de destinació

El registre di se sol utilitzar també amb l’adreçament indirecte, directe relatiu


a registre i indexat. En diem registre de destinació perquè algunes instruccions
complexes de tractament de cadenes de caràcters n’utilitzen el contingut per
a localitzar la posició de memòria en què poden emmagatzemar els resultats i
són capaces d’incrementar-lo o de fer-lo decréixer durant el procés de la ins-
trucció.

sp: registre de punter de pila (stack)

El registre sp és l’apuntador principal a l’estructura de pila. Quan una instruc-


ció crida una subrutina, s’emmagatzema l’adreça de programa actual en la po-
sició de memòria apuntada pel registre, de manera que després es pugui Més endavant veurem amb detall el
funcionament de la pila.
“tornar” al punt original de la crida.

6.2.2. Registres de segment

Els registres de segment s’utilitzen per a formar l’adreça efectiva de qualsevol


accés a la memòria com si fos una segona adreça base, cosa que serveix per a
independitzar els programes de l’adreça física del seu codi o dades, i així aïllar-
ne uns altres. Segons el mode actual de treball del processador, el seu contin-
gut s’interpreta d’una manera o d’una altra. Per defecte, totes les instruccions
associades a registres tenen assignat un o diversos registre de segment. Aquesta
assignació per defecte moltes vegades es pot canviar mitjançant prefixos de
segment.

cs :registre de segment de codi

El registre cs s’utilitza per a calcular l’adreça efectiva de qualsevol instrucció


que el processador hagi d’executar.

ds: registre de segment de dades

El registre ds s’utilitza per defecte per a calcular l’adreça efectiva en memò-ria


de qualsevol accés l’origen o la destinació del qual siguin registres d’ús general
(excepte di, sp i bp).
© FUOC • P06/05096/00295 • Mòdul 4 75 Assemblador

es: registre de segment extra

El registre es s’utilitza en les instruccions complexes de tractament de cadenes


de caràcters, i en altres construccions, per a calcular l’adreça efectiva de la me-
mòria de destinació, juntament amb el di.

fs i gs: registres de segment addicionals

Els registres fs i gs es poden utilitzar per a adreçar altres àrees de memòria


(segments), de manera que alleugereixin la utilització dels registres de seg-
ment principals.

ip : registre comptador d’instrucció

Aquest és el registre que el processador manté actualitzat amb l’adreça en la


memòria de la instrucció en curs i que només es pot modificar per mitjà de les
instruccions de salt o crida, com veurem més endavant.

6.2.3. Registre d’estat (FLAGS)

FLAGS és un registre de 16 bits que conté els diferents indicadors o banderes


referents a l’estat del procés. Les diverses banderes sempre s’utilitzen separa-
dament i cada una té un propòsit específic; gairebé totes guarden informació
sobre l’última operació aritmètica que s’ha executat. Podem identificar tres ti-
pus de banderes:

• Bits d’estat
• Bits de control
• Bits de sistema

Bits d’estat

CF: bit de transport (carry flag)

El bit CF pren el valor 1 quan, després d’una operació aritmètica, s’ha generat
transport, i valor 0 en cas contrari. Tots dos valors els pren com a enters sense
signe.
© FUOC • P06/05096/00295 • Mòdul 4 76 Assemblador

PF: bit de paritat (parity flag)

El bit PF pren el valor 1 per a indicar paritat en els vuit bits menys significatius
del resultat d’algunes instruccions (nombre parell d’uns en la codificació binà-
ria d’aquests vuit bits). S’utilitza sobretot en comunicacions.

AF: bit de transport auxiliar (auxiliary carry flag)

El bit PF indica el transport de les operacions aritmètiques en el quart bit. És


important per a treballar amb aritmètica BCD (binary coded decimals), en la
qual cada dígit decimal es codifica amb 4 bits.

ZF: bit indicador de zero (zero flag)

El bit ZF pren el valor 1 quan el resultat d’una operació aritmètica és 0.

SF: bit indicador de signe (sign flag)

El bit SF pren el valor 1 quan el resultat d’una operació, pres com un enter
amb signe en complement a 2, és negatiu.

TF: bit indicador de trap (trap flag)

El bit TF l’utilitzen els programes depuradors de programes. Quan està a 1, es


genera una crida de trap a una rutina determinada després d’executar gairebé
qualsevol instrucció. D’aquesta manera es poden seguir els programes pas a pas.

OF: bit de sobreeiximent (overflow flag)

El bit OF serveix per a indicar sobreeiximent després d’una operació aritmètica


en la qual s’interpreten els operands i el resultat com a enters amb signe.

Exemple d’ús dels bits CF, OF i SF

Suposem que tractem amb bytes i que volem sumar dos enters, A i B. La taula següent pot
il·lustrar les diferències:

A 16 A 10 A 10(s) B 16 B 10 B 10(s) A + B 16 A + B 10 A + B 10(s) CF OF SF

10h 16 16 20h 32 32 30h 48 48 0 0 0

10h 16 16 72h 114 114 82h 130 −126 0 1 1

85h 133 −123 95h 149 −107 1 Ah 26 26 1 1 0

10h 16 16 95h 149 −107 A 5h 165 −91 0 0 1

FEh 254 −2 02h 2 2 00h 0 0 1 0 0

X 16: valor en hexadecimal (com a enter sense signe) de X


X 10: valor de X expressat com a decimal sense signe
X 10 (s): valor de X expressat com a decimal amb signe
of: valor del bit de desbordament del resultat A + B
sf: valor del bit indicador de signe del resultat A + B
cf: valor del bit de transport del resultat A + B
© FUOC • P06/05096/00295 • Mòdul 4 77 Assemblador

Bits de control

DF: bit de l’adreça (direction flag)

El bit DF permet controlar l’adreça de procés de les instruccions complexes de


tractament de cadenes de caràcters; és a dir, permet saber si les instruccions es-
mentades han de fer incrementar o decrementar els registres índex involucrats.

Bits de sistema

El processador utilitza els bits de sistema per a mantenir la informació referent


al mode de treball en què processa i normalment no són manipulats per les
aplicacions.

IF: bit indicador d’interrupcions habilitades (interrupt flag)


I/O PL: bits de privilegi d’E/S
NT : bit de tasca imbricada (nested task flag)
RF: bit de continuació (resume flag)
VM : bit de mode virtual V 86 (virtual mode flag)

No us heu de preocupar gaire si no enteneu la funció específica d’alguns bits


en aquest punt. N’hi ha prou de saber que els bits utilitzats normalment pels
programes d’aplicació són: CF, AF, ZF, SF, OF i DF.

6.3. Jocs d’instruccions

Una instrucció en assemblador i8086 té el format general següent:

[símbol:] [prefixos] OPCODE [operands]

OPCODE és el mnemotècnic de la instrucció desitjada.

Símbol serveix per a assignar un símbol a aquesta instrucció, de manera que es pugui
referenciar com a adreça de salt o crida.

Prefixos són instruccions modificadores opcionals amb codi d’operació assignat que
serveixen per a alterar el funcionament de la instrucció que els segueix. Normalment no
els farem servir. Els prefixos següents són vàlids:

SEG (seguit de, per exemple) cs indica que l’operand de la instrucció que segueix s’ha
d’obtenir utilitzant com a base el segment indicat, en lloc de l’assignat per defecte per a
la instrucció.

REP es fa servir per a forçar la iteració de la instrucció següent en algunes instruccions


d’ús de cadenes de caràcters.

De la mateixa manera que hi ha registres privilegiats, també hi ha instruccions


privilegiades que no es poden executar en mode aplicació.
© FUOC • P06/05096/00295 • Mòdul 4 78 Assemblador

El joc d’instruccions d’aquests processadors es pot classificar segons grups


d’instruccions:

• Instruccions de moviment de dades (mov, xchg, lxx, push, pop)


• Instruccions aritmètiques (add, adc, inc, dec, sub, sbb, neg, mul, div, etc.)
• Instruccions lògiques (and, not, or, test, xor)
• Instruccions de rotació i desplaçament (rcl, rcr, rol, ror, sal, sar, shl)
• Instruccions de salt i crida (call, jmp, jx, ret, loop, enter, leave)
• Instruccions d’E/S (I/O) (in, out)
• Instruccions d’interrupció (int, into, iret)
• Instruccions de control del processador (cli, sti, stc, clc)

Abans d’acabar aquest apartat, recordarem les regles fonamentals per a com-
prendre la dinàmica de les instruccions x86:

• El comportament de les instruccions no depèn exclusivament del seu codi


d’operació i paràmetres, ja que els prefixos d’operació i l’estat del registre
de control el poden alterar.

• Els operands en la memòria es busquen sempre per mitjà del registre de seg-
ment per defecte segons el tipus d’operació.

• Per a les instruccions que accepten dos operands, tan sols un pot ser d’accés
a la memòria, l’altre ha de ser una referència al registre.

6.3.1. Instruccions de moviments de dades

La taula següent mostra totes les instruccions de transferència disponibles en


la família x86, excepte les de gestió de pila, que s’estudiaran més endavant.

Instrucció Descripció Exemple Operació Nota

mov d,s Copiar el valor s en d mov ax ,[27] d <- s Tret de les instruccions de
transferència dels senyaladors
xchg d,s Intercanviar d i s xchg ax,[27] d <--> s (o cap als senyaladors), la resta
d’operacions d’aquesta família
Cercar en la taula [bx] l’element no modifica l’estat d’aquests
xlatb xlatb al<-[al + bx]
número al senyaladors.

Carregar en d l’adreça relativa al


lea d,s lea bx,[27] d<-offset(s)
segment (offset) de s

Nota

La columna Operació mostra d’una manera més o menys formal el que fa la instrucció Aquesta nomenclatura és es-
(descrit en la columna Descripció). Per a les operacions que es comporten d’una manera tàndard i quadra amb la des-
sensiblement diferent quan operen amb operands de 16 o 32 bits, s’ha procurat donar un cripció de modes d’adreça-
ment que hem vist
exemple i descriure ambdós modes.
anteriorment i també amb la
sintaxi esperada pel programa
Les adreces o operands entre claudàtors indiquen el contingut de la memòria en la posi- d'assemblador.
ció indicada per l’adreça o operand.
© FUOC • P06/05096/00295 • Mòdul 4 79 Assemblador

6.3.2. Instruccions aritmètiques

Per a realitzar càlculs, el llenguatge d’assemblador ens ofereix un conjunt


Instruccions aritmètiques
d’instruccions aritmètiques que operen amb nombres naturals o enters, però
Alguns llenguatges de progra-
no amb nombres codificats en coma flotant. mació implementen les opera-
cions amb funcions de
biblioteques que realitzen les
L’i8086 no té instruccions de suport a l’aritmètica amb nombres en coma operacions pas a pas -igualar
exponents, operar amb les
flotant. mantisses-, però això queda
fora de l’abast d’aquesta assig-
natura.
Les instruccions aritmètiques del llenguatge d’assemblador permeten realitzar
una operació aritmètica utilitzant un o dos operands d’origen i guardar el re-
sultat sobre un operand de destinació.

Les instruccions en assemblador de l’i8086 no permeten especificar més de dos


operands. Així doncs, un dels operands d’origen és sempre, a més, l’operand
de destinació.

Exemple

Suma de dos nombres:

add num1, num2

En aquest cas l’operació que es realitza és: num1 = num1 + num2

Altres operacions que requereixen un sol operand d’origen, com l’increment (sumar +1),
s’especifiquen amb un sol operand. És a dir:

inc num

En aquest cas l’operació que es realitza és: num = num + 1

Hi ha instruccions en què per a executar l’operació es pren sempre un operand


Exemple
(generalment un registre) de manera implícita: un dels operands no s’especi-
La multiplicació i la divisió
fica i es pren sempre un registre determinat.

Presentem a continuació un exemple que il·lustrarà el funcionament de les


operacions aritmètiques bàsiques: la suma, la resta, la multiplicació i la divisió.

Cada una d’aquestes operacions es fa de la manera següent: op és una suma, una resta,
una multiplicació o una divisió.

resultat = num1 op num2

Nota
; Realització d'operacions aritmètiques bàsiques:
L’objectiu d’aquest exemple és
; suma, resta, multiplicació i divisió únicament mostrar com s’uti-
litzen aquestes instruccions i,
per tant, aquest programa en
.model small realitat no fa res útil.
.data
num1 dw 365
num2 dw 12
© FUOC • P06/05096/00295 • Mòdul 4 80 Assemblador

resultat1 dw 0
resultat2 dw 0

.code
inici: STARTUPCODE

suma:
mov ax, [num1]
add ax, [num2]
mov [resultat1], ax

resta:
mov ax, [num1]
sub ax, [num2]
mov [resultat1], ax

multi:
mov ax, [num1]
imul [num2]
mov [resultat1], ax
mov [resultat2], dx

divis:
mov ax, [num1]
mov dx, '
idiv [num2]
mov [resultat1], ax
mov [resultat2], dx

EXITCODE 0
end inici

suma:
mov ax, [num1]
add ax, [num2]
mov [resultat1], ax

Quan es volen sumar dos nombres i guardar el resultat en un tercer nombre,


necessitem forçosament fer el càlcul amb un registre auxiliar (en l’exemple
fem servir el registre ax) i guardar posteriorment el resultat en la variable de
memòria corresponent.

Una altra possibilitat és sumar dos nombres i guardar el resultat sobre un


d’aquests dos nombres. En aquest cas, teòricament es podria fer el següent:

add [resultat], [nombre]


© FUOC • P06/05096/00295 • Mòdul 4 81 Assemblador

Hem de tenir present la limitació que els dos operands no poden ser varia-
bles de memòria. Per tant, en aquest cas també s’ha de fer el càlcul amb el
registre ax, però una mica més eficientment que en l’exemple:

mov ax, [nombre]


add [resultat], ax

resta:
mov ax, [num1]
sub ax, [num2]
mov [resultat1], ax

L’operació resta s’executa de manera similar a l’operació suma; només cal te-
nir en compte l’ordre en què s’especifiquen els operands, ja que l’operació
resta l’operand d’origen a l’operand de destinació (sobre el qual guarda el re-
sultat):

sub op1, op1 realitza l’operació següent: op1 = op1 – op2.

Si es volgués fer l’operació op2 – op1 (en què op1 i op2 són variables de me-
mòria), caldria operar de manera més subtil:

mov ax, [op1]


mov bx, [op2]
sub bx, ax
mov [op1], bx

Les operacions de suma i resta s’executen igual si es realitzen sobre


nombres naturals o nombres enters, ja que l’algorisme de suma (o resta)
per a nombres naturals i per a nombres enters (codificats en comple-
ment a 2) és el mateix.

multi:
mov ax, [num1]
imul [num2]
mov [resultat1], ax
mov [resultat2], dx

El funcionament de l’operació multiplicació depèn de la mida de l’operand


que s’especifiqui:

• Si l’operand és de 8 bits, s’executa: ax = operand x al


© FUOC • P06/05096/00295 • Mòdul 4 82 Assemblador

Es multiplica l’operand per al i es guarda el resultat en el registre ax, ja que


el producte de dos nombres de 8 bits podria ocupar fins a 16 bits. És a dir,
s’utilitza el registre ax de manera implícita.

• Si l’operand és de 16 bits, s’executa: dx_ax = operand x ax

S’utilitza el registre ax de manera implícita com a segon operand i es guar-


da el resultat en els registres dx (els 16 bits de més pes) i ax (els 16 bits de
menys pes), ja que el resultat pot ocupar fins a 32 bits.

L’operació multiplicació diferencia si es treballa amb nombres naturals o


enters:

• La instrucció imul multiplica nombres enters.


• La instrucció mul multiplica nombres naturals.

divis:
mov ax, [num1]
mov dx, '
idiv [num2]
mov [resultat1], ax
mov [resultat2], dx

L’operació divisió també depèn de la mida de l’operand que s’especifiqui:

• Si l’operand és de 8 bits, s’executa: al = ax / operand ah = ax % / representa el quocient,


i % representa el residu.
operand

• Si l’operand és de 16 bits, s’executa: ax = dx_ax / operand dx = dx_ax


% operand

L’operand especificat en la instrucció representa el divisor, el dividend es pren


implícitament del registre ax i del dx per a divisors de 16 bits. En aquest segon
cas, el dividend serà un nombre de 32 bits, format per dx (els 16 bits de més
pes) i ax (els 16 bits de menys pes).

L’operació divisió diferencia si es treballa amb nombres naturals o enters:

• La instrucció idiv multiplica nombres enters.


• La instrucció div multiplica nombres naturals.

Si es vol dividir dos nombres de 16 bits, l’operació divisió utilitza igualment


el registre dx de manera implícita. Per tant, en dx hi ha d’haver l’extensió
de signe si els nombres són enters, o simplement zero si els nombres són
naturals.
© FUOC • P06/05096/00295 • Mòdul 4 83 Assemblador

Si es vol dividir dos nombres de 8 bits, en la part alta del registre al (ah) hi
haurà d’haver l’extensió de signe corresponent:

• La instrucció cbw fa una extensió de signe del valor de al sobre ah.


En l'exemple...
• La instrucció cwd fa una extensió de signe del valor de ax sobre dx.
... s’ha d’utilitzar la instrucció
cwd per a fer l’extensió de sig-
Les operacions aritmètiques mostrades en l’exemple són les operacions bàsi- ne corresponent sobre el regis-
tre dx.
ques i suficients per a fer qualsevol càlcul. A continuació es descriuen altres
operacions aritmètiques que també poden ser interessants.

Incrementar i decrementar

Per a sumar o restar +1 a un operand, hi ha unes instruccions més eficients que


les instruccions bàsiques de suma i resta:

• inc operand
• dec operand

L’operand ha de ser un registre o una variable de memòria, ja que no té cap


sentit incrementar o decrementar una constant.

Complementar a 2

Per a canviar de signe un nombre enter, disposem de la instrucció següent:

neg operand

Com que l’i8086 representa els nombres enters amb complement a 2, de fet,
el que fa aquesta instrucció és fer el complement a 2 de l’operand especificat.
Com sempre, aquest operand ha de ser un registre o una variable de memòria.

Multiplicar i dividir per nombres potència de 2

En binari, una multiplicació per un nombre potència de 2 (per exemple, 2^N)


equival a desplaçar aquest nombre N posicions a l’esquerra. És a dir, a posar-li
N zeros per la dreta. És evident que desplaçar un nombre a l’esquerra és més
fàcil que fer una multiplicació. Per tant, en assemblador s’acostumen a utilit-
zar les instruccions de desplaçament per a multiplicar per un nombre potència
de 2.

Les instruccions de desplaçament són les següents:

• Instruccions de desplaçament per a nombres naturals

– shl operand, 1
– shl operand, cl
© FUOC • P06/05096/00295 • Mòdul 4 84 Assemblador

• Instruccions de desplaçament per a nombres enters

– sal operand, 1
– sal operand, cl

Quan es vol desplaçar una sola posició a l’esquerra (multiplicació per 2), s’utilitza la cons-
tant 1 com a segon operand.

Quan es vol fer un desplaçament diferent de l’anterior, s’ha de posar en el registre cl el


nombre de posicions que es vol desplaçar a l’esquerra.

La divisió en binari per un nombre potència de 2 equival a desplaçar aquest


nombre les posicions corresponents a la dreta. Les instruccions en assembla-
dor per a realitzar desplaçaments a la dreta són les següents, amb les mateixes
precisions que per a la multiplicació:

– shr operand, 1
– shr operand, cl
– sar operand, 1
– sar operand, cl

Per acabar aquest apartat, presentem una taula amb les operacions aritmètiques:

Instrucció Descripció Exemple Operació Senyaladors modificats

Sumar amb transport adc ax,


adc d , s d <- d + s + cf of, sf, zf, af, cf, pf
sad [bx]

add cx,
add d , s Sumar s a d d <- d + s of, sf, zf, af, cf, pf
bx

inc d Sumar 1 a d inc cx d <- d + 1 of, sf, zf, af, pf

dec d Restar 1 a d dec [27] d <- d - 1 of, sf, zf, af, pf

sb d , s Restar s de d sub cx,bx d <- d - s of, sf, zf, af, cf, pf

Cf = 1 excepte si d = 0 of, sf, zf,


neg d Complement a 2 de d neg ax d <- not(d) + 1
pf

Multiplicar s i mul bl ax <- AL * s (8 bits) cf i of = 1 si la meitat superior del


mul s
acumulador mul bx dx:ax <- ax * s (16 bits) resultat <> 0 sf, zf, pf

Multiplicar amb imul bl ax <- AL * s (8 bits) cf i of = 1 si la meitat superior del


imul s
signe s i acumulador imul bx dx:ax <- ax * s (16 bits) resultat <> 0 sf, zf, pf

Dividir acumulador div cl


div s Al <- ax / S, ah<-ax mod S sf, af, of, pf, cf indeterminats
entre s div cx

Al <- ax / s, ah <- ax mod s


Dividir amb signe Idiv cl
Idiv s ax <- dx:ax / s sf, af, of, pf, cf indeterminats
acumulador entre s Idiv cx
dx <- dx:ax mod s

Comparar d i s sense
cmp d,s cmp ax,bx (en – s) of, sf, zf, af, pf, cf
modificar-los

6.3.3. Instruccions lògiques

Ja sabem que les instruccions aritmètiques realitzen càlculs sobre valors de 8,


16 o 32 bits, interpretant tots els bits de manera unificada (segons una codifi-
© FUOC • P06/05096/00295 • Mòdul 4 85 Assemblador

cació). En canvi, les instruccions lògiques, que tenen molta importància en


certes àrees específiques com ara la programació lògica, treballen bit a bit.

Les instruccions lògiques del llenguatge d’assemblador permeten realit-


zar una operació lògica bit a bit utilitzant un o dos operands d’origen, i
guardar el resultat sobre un operand de destinació.

Les operacions lògiques bàsiques són and, or i not, tot i que el llenguatge d’as-
semblador també ofereix l’operació xor. De manera similar a les instruccions
aritmètiques, un dels operands d’origen és sempre, a més, l’operand de desti-
nació.

Presentem un programa d’exemple que copia una variable de memòria bit a


bit sobre una altra variable de memòria.

Evidentment, això es pot fer molt més fàcil mitjançant un mov, però l’objectiu és il·lustrar
el funcionament de les operacions lògiques.

Procediment
; Ús d’operacions lògiques bàsiques:
A continuació explicarem
; còpia d’un valor bit a bit les instruccions remarcades
en negreta.
.model small
.data
valor db 75
copia db 0f0h

.code
inici: STARTUPCODE

mov al, 00000001b


mov cx,8
bucle:
mov bl, [valor]
and bl, al
je bit0
bit1:
or [copia], al
jmp seguim
bit0:
mov ah, al
not ah
and [copia], ah

seguim:
shl al,1
© FUOC • P06/05096/00295 • Mòdul 4 86 Assemblador

loop bucle

EXITCODE 0
end inici

.model small
.data
valor db 75
copia db 0f0h

S’han declarat dues variables de memòria de 8 bits: el valor original (amb valor
inicial igual a 75 especificat en decimal) i la còpia (amb un valor inicial qual-
sevol especificat en aquest cas en hexadecimal, f0h). El que es pretén fer és mi-
rar bit a bit el valor original i, en funció d’aquest bit, posar un 1 o un 0 sobre
la còpia:

Hi haurà casos en què es voldrà forçar un 1 sobre un 0 (sense modificar la resta


de bits), o bé forçar un 0 sobre un 1 (també sense modificar la resta de bits), o
bé mantenir el bit que hi havia.

inici: STARTUPCODE

mov al, 00000001b


mov cx,8
bucle:
...
shl al,1
loop bucle

Per a aïllar el valor d’un sol bit, s’utilitza una màscara de bits. Com que es pre-
tén tractar els bits un a un, s’inicialitza el registre al amb una màscara de bits
en la qual tots els bits valen 0 excepte un.

En l’exemple, s’ha inicialitzat el registre al en la constant 1, expressada en bi-


nari. Aquest és un recurs comú quan es manipulen bits, ja que queda més clar
què és el que es pretén fer si s’especifica el valor 00000001b.
© FUOC • P06/05096/00295 • Mòdul 4 87 Assemblador

Al final del bucle s’ha fet un desplaçament a l’esquerra del registre al; és a dir,
es desplaça aquest 1 a la posició següent de l’esquerra. Aquest desplaçament es
fa 8 vegades per a tractar tots els bits del valor original.

bucle:
mov bl, [valor]
and bl, al
je bit0
bit1:
or [copia], al
jmp seguim
bit0:

Amb la màscara que hi ha en el registre al, es pot aïllar un bit del valor origi-
nal fent una and lògica del valor original amb la màscara.

La and de qualsevol valor amb zero és sempre 0. Per tant, la and de qualsevol
valor amb la màscara deixarà tots els bits a 0, excepte el bit que es troba en la
mateixa posició que l’1 que hi ha en la màscara. Aquest bit es mantindrà en 1
si el valor original era 1, o bé quedarà 0 si el valor original era 0.

Abans de fer la and s’ha copiat el valor original en el registre bl, ja que la and
modifica l’operand de destinació, i en aquest programa interessa mantenir el
valor original. Així doncs, després de fer la and, el registre bl valdrà 0 si el bit
corresponent era un 0, o 1 altrament.

bit1:
or [copia], al
jmp seguim
bit0:
mov ah, al
not ah
and [copia], ah

• S’executará el codi que hi ha en l’etiqueta bit1 si el bit analitzat és un 1.

Quan el bit és un 1, es força l’escriptura d’un 1 al valor còpia, respectant el


valor que tenen els altres bits. Per a forçar l’escriptura d’un bit en 1 (el bit
corresponent a la màscara), s’executarà una operació or de la màscara so-
bre l’operand de destinació.

• S’executarà el codi que hi ha en l’etiqueta bit0 si el bit analitzat és un 0.

Quan el bit és un 0, es força l’escriptura d’un 0 en el valor còpia, respectant


el valor que tenen els altres bits. Per a forçar l’escriptura d’un bit a 0 (el bit
© FUOC • P06/05096/00295 • Mòdul 4 88 Assemblador

corresponent a la màscara), s’executarà una operació and de l’invers de la


màscara sobre l’operand de destinació.

Per tant, per a forçar l’escriptura d’un 0 cal l’invers de la màscara; és a dir,
una altra màscara amb els bits a 0 si els bits de la màscara original eren 1, i
els bits a 1 si els bits de la màscara original eren 0. Això s’aconsegueix mit-
jançant la instrucció not.

En fer l’operació and amb 1, es mantindran els valors dels bits originals.
En fer-la amb 0, es força l’escriptura d’un 0.

En fer l’operació or amb 0, mantindrà els valors originals. En fer-la amb


un 1, es força que el bit corresponent valgui 1.

En l’exemple es veu el mecanisme de consulta d’un bit a partir de l’operació


lògica and, que modifica l’operand de destinació. L’inconvenient d’aquest
mecanisme és que si únicament es vol consultar el valor (sense modificar-lo),
primer s’ha de fer una còpia perquè la modificació es faci sobre la còpia.

La instrucció test permet fer la mateixa consulta però sense modificar cap ope-
rand. S’hauria pogut fer més eficient aquest programa amb aquesta operació.

La instrucció xor realitza la or-exclusiva bit a bit entre els dos operands es-
pecificats. Aquesta instrucció, a part d’utilitzar-se per a realitzar l’operació lò-
gica corresponent, es fa servir sovint per a inicialitzar un registre a 0 de la
manera següent:
xor reg, reg

La instrucció or-exclusiva de qualsevol valor amb si mateix és sempre 0.

Aquest mecanisme és més eficient que moure directament un 0 al registre, ja


que l’accés a un operand de registre és més eficient que l’accés a un operand
constant.

Per acabar aquest apartat, presentem una taula amb les instruccions lògiques
de l’i8086:

Instrucció Descripció Exemple Operació

and d, s And lògic d i s and ax, 27 H d <- d and s

not d Invertir d not ax d <- not d

or d, s Or lògic d i s or ax, bx d <- d or s

test d, s And d i s sense modificar-los Test ax, 001B d and s

xor d, s Xor lògic d i s xor ax, cx d <- d xor s


© FUOC • P06/05096/00295 • Mòdul 4 89 Assemblador

Les instruccions lògiques modifiquen els senyaladors sf, zf i pf segons el resultat de


l’operació, netejant (assigneu 0) els senyaladors of i cf. L’única excepció és la instrucció
not, que no afecta cap senyalador.

6.3.4. Instrucions de rotació i desplaçament de l’i8086

Instrucció Descripció Exemple Operació

Rodar amb carry d, n bits a


rcl d, n rcl ax, 1 rodar d <-c- < d (*n)
l’esquerra

rcr d, n Rodar amb carry d, n bits dreta rcr dx, cl rodar d >-c- > d (*n)

rol d, n Rodar d, n bits a l’esquerra rol dx, cl rodar d <-- < d (*n)

ror d, n Rodar d, n bits a la dreta ror bx, 1 rodar d >-- > d (*n)

sal d, n Desplaçar a l’esquerra d, n bits sal ax, 1 (shl ax, 1) desplaçar d <-- < d (*n)

Desplaçar a la dreta d, n bits


sar d, n sar bx, 1 desplaçar d >-s- > d (*n)
propagant bit de signe

shr d, n Desplaçar a la dreta d, n bits shr dx, cl desplaçar d >--> d (*n)

Desplaçar a la dreta d, n bits, on R


shrd d, R, n shrd dx, ax, cl desplaçar R >-d-> d (*n)
conté els bits a anar inserint

Desplaçar a l’esquerra d, n bits, on R


shld d, R, n shld dx, ax, cl desplaçar d <-d-< R (*n)
conté els bits que s’han d’anar inserint

En general, les instruccions de rotació i desplaçament afecten els senyala-dors


cf, zf, pf i sf segons el resultat i normalment només accepten el registre cl
com a compte de bits que s’han de rodar/desplaçar, o un compte fix inferior
a 256.

6.3.5. Instruccions de salt jxx

El joc d’instruccions x86 proporciona les instruccions de salt i bifurcació se-


güents:

Instrucció Descripció Exemple Operació

jmp d Salt incondicional a d jmp 27 Ip <- d

ja d Salt si més gran a d ja 27 Si cond, ip <- d

jae d Salt si més gran o igual a d jae 27 Si cond, ip <- d

jb d Salt si més petit a d jb 27 Si cond, ip <- d

jbe d Salt si més petit o igual a d jbe 27 Si cond, ip <- d

je d Salt si igual a l’adreça d je 27 Si cond, ip <- d

jg d Salt si més gran (amb) a d jg 27 Si cond, ip <- d

jge d Salt si més gran o igual (amb) a d jge 27 Si cond, ip <- d

jl d Salt si més petit (amb) a d jl 27 Si cond, ip <- d

jle d Salt si més petit o igual (amb) a d jle 27 Si cond, ip <- d

jne d Salt si diferent a adreça d jne 27 Si cond, ip <- d


© FUOC • P06/05096/00295 • Mòdul 4 90 Assemblador

Instrucció Descripció Exemple Operació

jno d Salt si no-sobreeiximent a d jno 27 Si cond, ip <- d

jnp d Salt si no-paritat a d jnp 27 Si cond, ip<-d

jns d Salt si no signe a d jns 27 Si cond, ip <- d

jo d Salt si sobreeiximent a d jo 27 Si cond, ip <- d

jp d Salt si paritat a d jp 27 Si cond, ip <- d

js d Salt si signe a d js 27 Si cond, ip <- d

jcxz d Salt si cx = 0 a d jcxz 27 Si cond, ip <- d

Cx <- cx – 1 ?
loop d Decrementar. cx i salt si cx <> 0 loop 27
ip <- d

Decrementar. cx, salt si cx <> 0 Cx <- cx – 1 ?


loope d loope 27
i zf = 1 ip <- d

Decrementar. cx, salt si cx <> 0 Cx <- cx – 1 ?


loopne d loopne 27
i zf = 0 ip <- d

En totes aquestes instruccions, l’operand és una adreça de codi; és a dir, el lloc


on s’espera que hi hagi altres instruccions executables. Aquesta adreça es pot
especificar directament, o per mitjà d’un símbol que hi estigui associat.

En general, les instruccions de salt no modifiquen el registre d’estat. L’excep-


ció són les instruccions de tipus loop, que porten implícita una ins-trucció
aritmètica de decrement associada.

Pel que fa als modes d’adreçament, només la instrucció de salt incondicional


(jmp) permet salts directes, indirectes o indexats. Totes les altres instruccions
de salt esperen adreçament immediat.

jmp [bx+si]

En general tots els salts incondicionals es codifiquen com a salts absoluts i els
condicionals, com a salts relatius. Per aquest motiu, es pot donar que un de-
terminat salt condicional no es pugui assemblar, perquè l’adreça destinació és
més lluny del que permet el seu operador. Aquest problema, el detecta el pro-
grama assemblador i la solució és ben senzilla: es canvia la instrucció per la
condicional inversa seguida d’un salt incondicional.

Exemple

ja molt lluny

Se substituiria per:

jbe contínua
jmp molt lluny
contínua:
© FUOC • P06/05096/00295 • Mòdul 4 91 Assemblador

Com ja hem vist, ens referim a les adreces x86 com a desplaçaments dins d’un
segment. En el cas d’adreces de codi, el desplaçament és normalment el valor
actual del registre de segment de codi cs. Això planteja el problema de com es
pot saltar o bifurcar a un altre segment de codi. La instrucció jmp té variants
que permeten fer-ho. El qualificador FAR permet indicar un salt intersegment:

jmp FAR SEGMENT:ETIQUETA

Els salts en el segment de codi actual s’anomenen salts intrasegment i són els que
s’utilitzen normalment. El qualificador apropiat per a ells és NEAR, però no cal
posar-lo, ja que és el tipus de salt que l’assemblador assumeix per defecte.

6.3.6. Instruccions d’E/S

Les instruccions de l’i8086 per a treballar amb l’espai d’adreçament d’E/S no-
més són dues:

Instrucció Descripció Exemple Operació

in s, p Entrada de port p a s in al,dx d <- port(p)

out p, s Sortida de s a port p out dx,al port(p) <- s

Aquestes instruccions no modifiquen les banderes. Tanmateix, s’han de tenir


certes precaucions:

• No és convenient efectuar dues d’aquestes instruccions de manera seqüen-


cial sense cap altra instrucció entre elles, ja que la majoria de dispositius ne-
cessiten un temps per a “reconèixer” l’adreça en el bus d’E/S. Per això, se
solen incorporar instruccions de retard entre dues instruccions d’E/S.

• L’amplada del bus d’E/S és de 16 bits; per això només es pot utilitzar una
adreça de port (P) de 16 bits, que ha de ser especificada per adreçament im-
mediat (quan és en el rang [0..0FFH]), o indirecte via el registre dx.

• La mida de les dades que s’han de llegir pot ser indistintament de 8, 16 o


32 bits, però sempre s’han de transferir des del registre acumulador (al, ax)
o cap a aquest.

S’ha de tenir molta cura de llegir/escriure exactament tants bits com calgui, ja que si, per
exemple, es fa out dx, ax llavors el port [dx] rebrà el contingut del registre al, i el
port [dx + 1], el contingut del registe ah.

• La major part dels dispositius PC treballen amb protocols de dos ports, el


primer dels quals s’utilitza per a mapar sobre el port següent un registre o
una adreça determinada de les disponibles en el dispositiu. En aquests ca-
© FUOC • P06/05096/00295 • Mòdul 4 92 Assemblador

sos, pot ser necessari treballar amb el bit indicador d’interrupcions inhibi-
des a 1, ja que s’ha de garantir l’atomicitat d’ambdues operacions; és a dir,
que entre l’OUT corresponent a la selecció del registre d’E/S del dispositiu i
l’IN/OUT de transferència de la dada, s’ha de garantir que no entri cap in-
terrupció amb una rutina de gestió que pugui, potencialment, desestablir
el registre del dispositiu preseleccionat.

6.3.7. Instruccions d’interrupció

La família i8086 implementa les funcions de crida i tornada d’una subrutina


següents:

Instrucció Descripció Exemple Operació

push ipip <- d


Crida la rutina a
call d call 27 H o
l’adreça d
push cs, push ip, cs<-d IP<-d

pop ip,(sp<-sp+n)
ret
ret n Tornada de la crida o
retf
pop ip,pop cs,(sp<-sp+n)

Executa interrupció pushf,push cs,push


int n int 21 H
de programari n ip,cli,cs:ip<-vector(n)

Tornada
Iret iret pop ip, pop cs, popf
d’interrupció

Les instruccions del tipus int executen les anomenades interrupcions de pro-
gramari, subrutines en les quals l’adreça d’inici està fixada en una taula de vec-
tors d’interrupció del sistema (els programes no la controlen en condicions
normals) i, a més, es guarda el registre d’estat (pushf) de manera automàtica
en la pila, posant a 0 el bit (flag) indicador d’interrupcions habilitades en el
moment de la crida. Aquest mecanisme serveix perquè els sistemes operatius
defineixin fàcilment una sèrie de rutines de propòsit general, amb els serveis
que proporcionen als programes.

El paràmetre n de la instrucció ret és opcional, i permet que aquesta instruc-


ció elimini automàticament de la pila els paràmetres d’entrada de la crida a la
subrutina.

Si aquesta implementació de les instruccions de crida té alguna dificultat, és


probablement la d’entendre quin tipus de crida es fa en cada moment, i com
es guarda l’adreça de tornada a la pila. Això depèn bàsicament de dos factors:

• El tipus de crida (intrasegment o extrasegment), que depèn de la localitza-


ció de la subrutina destinació (dins o fora del nostre segment de codi).

• La definició del segment de codi cridat, que determina si es guarda el registre


comptador d’instrucció complet o només els 16 bits menys significatius.
© FUOC • P06/05096/00295 • Mòdul 4 93 Assemblador

6.4. PUSH i POP

Les instruccions genèriques relacionades amb l’ús de la pila de programa en el


processador i8086 són les següents:

Instrucció Descripció Exemple Operació

push s Inserir en la pila l’operand s push ax SP <- SP - 2,[SP] <- s

pop d Treure de la pila i copiar en d pop ax d <- [SP],SP <- SP + 2

Inserir en la pila el registre SP <- SP - 2,[SP] <-


pushf pushf
d’estat Flags

Treure de la pila i copiar en el Registre d'estat <-


popf popf
registre d’estat [SP],SP <- SP + 2

Excepte la instrucció popf, aquestes instruccions no modifiquen el registre


d’estat.

La pila pot emmagatzemar dades de 2 o 4 bytes, segons si es treballa amb re-


gistres o variables en la memòria de 16 o 32 bits. Com a registre punter de pila
s’utilitza el registre sp.

El registre bp se sol utilitzar per a adreçar estructures o variables sense alterar * De moment utilitzarem la pila
com un recurs del sistema
el punter de pila, com a registre base de pila.* per a guardar variables o registres
temporals.

Finalment, convé recordar que s’ha de tenir molta cura a treballar amb la pila
en els aspectes següents:

La pila és un recurs limitat, no disposa d’un espai il·limitat. Per tant, és molt
eficaç per a guardar variables petites, però potser no és el lloc més adequat per
a emmagatzemar grans estructures de dades.

Quan s’emmagatzemen dades en la pila, s’ha de garantir que s’extreguin en un


moment o un altre, altrament es corre el risc d’omplir-la.

Encara que es vegin en la memòria els continguts anteriors de la pila després


d’extreure’ls, no s’han d’utilitzar, ja que la zona de pila “lliure” no ens pertany
i pot ser utilitzada, sense cap avís previ, per les rutines de gestió d’interrupcions
asíncrones.
© FUOC • P06/05096/00295 • Mòdul 4 94 Assemblador

Resum

La programació en llenguatge assemblador permet controlar totes les caracte-


rístiques d’un processador. Per a poder programar en aquest llenguatge cal co-
nèixer no només la funcionalitat de les instruccions, sinó també els operadors
que poden fer servir. També és important ser conscient de les limitacions do-
nades per l’arquitectura del processador.

Estructurar el programa en subrutines facilita la implementació de tasques repeti-


tives i l’estructuració del codi, i fa que els programes siguin més fàcils d’entendre.

Per poder programar els dispositius d’E/S, cal conèixer-ne les característiques
particulars. En el mòdul, s’han plantejat programes que permeten accedir a
dispositius com ara el teclat, la pantalla i el rellotge del sistema.
© FUOC • P06/05096/00295 • Mòdul 4 95 Assemblador

Bibliografia
Beltran de Heredia, J. (1994). Lenguaje ensamblador de los 80x86. Madrid: Anaya Multimedia.

Miguel Anasagasti, Pedro de (1999). Fundamentos de los computadores. Madrid: Paraninfo.

Rodríguez Roselló, Miguel Ángel (1987). 8088-8086/8087: programación ensamblador en


entorno MS DOS. Madrid: Anaya Multimedia.

Tischer, M. (1995). PC Interno 2.0. Madrid: Marcombo.

You might also like