Professional Documents
Culture Documents
Assemblador
Assemblador
Assemblador
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
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
Resum .......................................................................................................... 94
Bibliografia ................................................................................................ 95
© FUOC • P06/05096/00295 • Mòdul 4 5 Assemblador
Introducció
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ó.
− Breu recorregut per les instruccions principals del llenguatge, les estructu-
res de dades que es poden definir i el control del flux del programa.
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:
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
• 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.
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.
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.
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
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ó.
Podeu treure l’opció que indica When done unzipping run: ..., ja que el fitxer que es vi-
sualitza no dóna gaire informació.
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
Do you wish to create program groups and icons for the Borland product you
recently installed?
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:
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ó.
.code
inici: STARTUPCODE
mov ax, @DATA ; Valor de segment per .DATA
© FUOC • P06/05096/00295 • Mòdul 4 13 Assemblador
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.
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 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
C:\TASM> hola (el sistema operatiu pren l’extensió .exe per defecte).
• 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ó.
• 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.
• 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.
• F9: Run. S’executa el programa de cop fins que troba un punt de ruptura o
bé finalitza.
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.
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:
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.
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
C:\TASM> tasm /zi hola (el tasm pren l’extensió .asm per defecte).
Turbo Assembler Version 4.1 Copyright ©1988, 1996 Borland International
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:
.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
El caràcter $ està fora de les cometes i l’assemblador no pot identificar què és.
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.
S’ha escrit move per mov. move no és una ordre de Turbo Assembler.
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.
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.
2.1. Instruccions
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
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ó.
.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
EXITCODE 0
end inici
cas1:mov dl, 65
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.
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.
mov dl, bh
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]
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]
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.
Si interessa executar la instrucció anterior, caldrà recórrer a executar una assignació in-
termèdia:
.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
EXITCODE 0
end inici
var1 db 1
var2 dw 1
var3 dd 1
• 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).
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:
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”.
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
EXITCODE 0
end inici
Els dos accessos esmentats s’han dut a terme de dues maneres diferents:
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).
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.
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.
.data
matriu dw 100 dup (?)
.code
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.
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.
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
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
El primer bucle posa en la pila els elements del vector mitjançant la instrucció
push.
Com que s’han posar els elements del vector ordenadament des del primer ele-
ment a l’últim.
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.
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
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.
Per a poder identificar la instrucció a la qual es vol saltar, cal posar-hi una eti-
queta.
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
jmp final
final:
EXITCODE 0
end inici
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
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.
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.
Si els valors comparats codifiquen nombres amb signe (enters), les instruccions
de salt condicional que es poden utilitzar són les següents:
• 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.
• 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
• 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.
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
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:
Les dues versions del programa que es mostra a continuació escriuen un mis-
satge per pantalla un nombre determinat de vegades.
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
inc cx
jmp avaluacio
final:
EXITCODE 0
end inici
avaluacio:
cmp cx,10
jge final
...
jmp avaluacio
final:
mov cx,0
...
inc cx
© FUOC • P06/05096/00295 • Mòdul 4 38 Assemblador
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.
• 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
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
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.
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
• 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.
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.
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
mov cx, 10
bucle: call subrutina
loop bucle
escriure:
push dx
push ax
lea dx, [missatge]
© FUOC • P06/05096/00295 • Mòdul 4 43 Assemblador
En aquest cas, es guarden els registres ax i dx, que són els que s’utilitzen per a
executar el codi corresponent.
push adreca_de_retorn
jmp subrutina
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.
nats d’entrada. En aquest cas, cal definir un mecanisme per a poder-li passar
un o diversos paràmetres. Exemple
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.
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
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.
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.
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
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.
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.
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
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.
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
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.
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
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
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.
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:
É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:
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.
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
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
EXITCODE 0
end inici
mov cx,10
enquesta:
• 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.
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.
es_mak:
lea dx, [mak]
jmp escriu
es_brk:
lea dx, [brk]
escriu:
mov ah, 09h
int 21h
loop enquesta
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.
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’...
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.
Una rutina d’atenció a la interrupció és una rutina qualsevol, com les vistes
fins ara, però amb dues particularitats:
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
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
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
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.
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
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.
* 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
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.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
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.
• 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.
En algunes d’aquestes crides cal passar com a paràmetre una cadena de text
definida en assemblador.
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
Per a fer una crida a una funció del sistema operatiu cal fer servir la instrucció Nota
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:
.code
inici: STARTUPCODE
EXITCODE 0
end inici
© FUOC • P06/05096/00295 • Mòdul 4 65 Assemblador
.code
inici: STARTUPCODE
EXITCODE 0
end inici
• 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 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
.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
EXITCODE 0
end inici
.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
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.
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
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:
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:
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:
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.
• Sense echo, el caràcter que s’introdueix pel teclat no s’escriu per pantalla.
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.
6. Referència ràpida
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
EXITCODE 0
end inici
.model small
.data
missatge db 'Bon dia!$'
.code
© FUOC • P06/05096/00295 • Mòdul 4 72 Assemblador
• 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).
.code
inici: STARTUPCODE
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
EXITCODE 0
end inici
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
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
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.
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.
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.
• Bits d’estat
• Bits de control
• Bits de sistema
Bits d’estat
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
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.
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.
Suposem que tractem amb bytes i que volem sumar dos enters, A i B. La taula següent pot
il·lustrar les diferències:
Bits de control
Bits de sistema
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ó.
Abans d’acabar aquest apartat, recordarem les regles fonamentals per a com-
prendre la dinàmica de les instruccions x86:
• 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.
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.
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
Exemple
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
Cada una d’aquestes operacions es fa de la manera següent: op és una suma, una resta,
una multiplicació o una divisió.
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
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:
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):
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:
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
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:
Incrementar i decrementar
• inc operand
• dec operand
Complementar a 2
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.
– shl operand, 1
– shl operand, cl
© FUOC • P06/05096/00295 • Mòdul 4 84 Assemblador
– 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.
– shr operand, 1
– shr operand, cl
– sar operand, 1
– sar operand, cl
Per acabar aquest apartat, presentem una taula amb les operacions aritmètiques:
add cx,
add d , s Sumar s a d d <- d + s of, sf, zf, af, cf, pf
bx
Comparar d i s sense
cmp d,s cmp ax,bx (en – s) of, sf, zf, af, pf, cf
modificar-los
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ó.
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
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:
inici: STARTUPCODE
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.
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
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.
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
Per acabar aquest apartat, presentem una taula amb les instruccions lògiques
de l’i8086:
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)
Cx <- cx – 1 ?
loop d Decrementar. cx i salt si cx <> 0 loop 27
ip <- d
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:
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.
Les instruccions de l’i8086 per a treballar amb l’espai d’adreçament d’E/S no-
més són dues:
• 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.
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.
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.
pop ip,(sp<-sp+n)
ret
ret n Tornada de la crida o
retf
pop ip,pop cs,(sp<-sp+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 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.
Resum
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.