ESTRUCTURA DE COMPUTADORS I(EC-1)
Enginyeria Informàtica
Quadern de Laboratori
Carlos ÁlvarezCristina Barrado
Luis M. Díaz de CerioMontse Fernández
David LópezÀngel Olivé
Joan Manel ParcerisaJordi Tubella
Miguel Valero
Dept. d’Arquitectura de Computadors
ments
s bases
guatges,
s poden
a feta
es al
lloc a
haver
des a
n molt
una
rendreu
ern. A
nceptes
s de 20
que
ran de
estan
sor que
Els laboratoris de EC-1
L’assignatura d’estructura de computadors 1 (EC-1) és una assignatura de coneixe
bàsics pel que respecta al funcionament de l’ordinador. En aquesta assignatura trobareu le
sobre les que creixen alguns dels conceptes de sistemes operatius, xarxes, compilació, llen
etc. També és una assignatura eminentment pràctica, on els coneixements teòrics adquirit
ser fixats i ampliats al laboratori. És per això que les pràctiques són molt orientades a la fein
a classe.
Obligatorietat de les pràctiques
Les pràctiques de l’assignatura no són obligatòries. Això vol dir que no heu de lliurar r
professor al final de cada sessió. La nota de laboratori s’obté per mitjà d’un examen que té
l’última sessió. El pes d’aquesta nota i la manera de calcular la nota final ja us la deuen
explicat a classe (o bé la podeu trobar a la guia docent).
Que no siguin obligatòries no vol dir que no cal fer-les. Les pràctiques estan orienta
fixar els coneixements adquirits a classe, per tant serveixen per entendre l’assignatura i só
útils per aprovar. Hem eliminat la obligatorietat per eliminar l’estrès que produeix lliurar
pràctica cada quinze dies, però recomanem que les feu: no us portarà massa temps i n’ap
molt.
I jo, que haig de fer al laboratori?
Hi ha cinc sessions de laboratori. A cada sessió li correspon un capítol d’aquest quad
cada capítol hi ha una secció anomenada “lectura prèvia” on es repassen breument els co
relacionats amb el contingut de la sessió corresponent. Aquesta lectura no us portarà mé
minuts i aprofitareu millor les hores de laboratori.
A l’apartat anomenat “Enunciats de la pràctica” trobareu la descripció de les activitats
s’han d’anar desenvolupant durant la sessió. De tant en tant trobareu exercicis que s’hau
contestar sobre el mateix enunciat. Identificareu fàcilment aquest exercicis perquè
emmarcats en quadres de color gris. Quan trobeu un exercici que no us surt, crideu al profes
us ajudarà a trobar la solució.
1
quina
sala on
r
Per
usuari
na de
unciats
uesta
eu-vos
proper
i que,
t a les
com
I com es comença?
Les pràctiques són a les sales D6-003 i D6-003bis (el vostre professor us indicarà a
aula us toca). Per entrar als comptes de l’assignatura s’han de fer coses diferents segons la
sigueu. Un cop hagueu encès la màquina i aquesta hagi fet elboot, els de l’aula D6-003 heu d’entra
amb identificadorECIXX on XX és el número de PC al que esteu treballant (està indicat).
exemple, si esteu al PC número 6, el vostre nom d’usuari ésECI6, si és el número 13, doncs
ECI13. Els que estigueu a la sala D6-003bis (la més propera a l’entrada) teniu com a nom d’
ECI1XX . Per exemple, si esteu al PC número 6 teniu com a nom d’usuariECI106, mentre que si
esteu al 13, el vostre identificador ésECI113.
Un cop esteu al vostre compte, podeu accedir a 3 discos virtuals (a part del discA: ).
Al disc W: trobareu l’enunciat de les pràctiques als subdirectoris indicats. Aquesta zo
disc és comú per a tothom, i no hi podeu escriure. Per tant per treballar heu de copiar els en
a la vostra zona de treball.
El discU: es la vostra zona de treball. Hi ha una zona de treball per cada PC i en aq
podeu escriure, esborrar etc... El primer que heu de fer és copiar els enunciats al disc U:. Fix
que d’aquestes zones n’hi ha una per PC, però no per grup de pràctiques. Això vol dir que el
grup d’EC1 que treballi amb el mateix PC on esteu, farà servir la mateixa zona de treball (
per tant, esborrarà tot el que trobi). És molt important que, si voleu guardar-vos el que heu fe
pràctiques us feu una còpia en disquets.
El disc T: és per lliurar l’examen (el dia de l’examen, òbviament). Ja us explicarem
funciona al propi enunciat de l’examen.
2
a en
eureu la
ssaran
Sessió 1: Introducció
Objectiu: En aquesta sessió aprendreu l’entorn de treball. Veureu quina pinta fa un program
assemblador, veureu com funcionen el traductor, ellinker i el debugger(tot i que deldebugger
només veurem unes poques comandes, que ampliarem a les properes sessions). També v
representació dels naturals i dels enters, i el funcionament d’una sèrie d’instruccions. Es repa
també els conceptes de registres,flags i instruccions per a la manipulació de bits.
Lectura prèvia:
L’entorn
Els passos habituals per fer un programa (en qualsevol llenguatge) són els següents: primer
es crea el programa escrivint-lo en el llenguatge font per mitjà d’un editor de programes. El resultat
és un fitxer en un llenguatge que pot entendre l’usuari, però no la màquina. Per traduïr-lo a
llenguatge màquina es fa servir un programa traductor. Aquest genera un fitxer amb la traducció del
vostre programa, però encara no és un fitxer executable. Un fitxer executable conté el programa
traduit més una sèrie de codi que ha de tenir tot programa que es vulgui córrer a una màquina
determinada. Aquest codi comú es troba a les anomenades biblioteques del llenguatge (language
libraries). L’encarregat d’ajuntar el vostre codi amb el codi d’aquestes biblioteques és un programa
anomenat muntador (linker). Així doncs, el següent pas és invocar el muntador, que generarà el
programa executable (veure la figura 1.1).
Editor
Traductorbiblioteca
del llenguatge
Muntador
PROG.ASM
PROG.OBJ
PROG.EXE
figura 1.1: Entorn típic de programació
3
Durant el procés de creació d’un programa se solen produir errors. Hi ha dos tipus d’errors:
els sintàctics o detectables en temps de traducció, i els semàntics o detectables en temps
d’execució. Els errors sintàctics són, per exemple, escriure malament una instrucció o fer una
operació entre dos tipus de dades incompatibles. Aquests errors són detectats pel traductor i els
hem de solucionar abans de poder generar un executable.
Un cop tenim un programa sintàcticament correcte el podem executar, però això no implica
que el programa sigui correcte. Totes les instruccions poden ser correctes, però ens podem haver
oblidat de posar la condició de final d’un bucle (i que aquest no acabi mai) o que, senzillament, el
programa no faci el que nosaltres volem. Aquests errors només es poden detectar en temps
d’execució, i per tal d’eliminar-los fem servir un depurador de programes (debugger). Els
depuradors ens permeten executar el programa instrucció a instrucció i veure tots els valors que es
van calculant, de manera que podem trobar els errors.
En el laboratori farem servir l’editor edit de DOS. El traductor (que en el cas de traduir d’un
llenguatge assemblador a llenguatge màquina rep el nom d’assemblador), el linker i el debugger
seran els de Borland (TASM, TLINK y TD )
Quina pinta fa un programa en assemblador?
A la figura 1.2 teniu el codi de la primera pràctica que provarem.
.model large
.386
N EQU -3
.stack 100h
.dataA1 dd 1A2 dd -1A3 dd 77777777h.code.startupETIQ1: MOV EAX, A1 MOV EBX, A2 ADD EAX, EBX ADD EAX, N MOV A3, EAX.exitEND
Definició del model
Començamen del segment de dades
Començament del seg. de codi
Començament delprograma principal.
Final del prog. pral.Final del fitxer
Definició del segment de pila
Definició de constants
figura 1.2: Codi del programa INTRO1.ASM
4
El codi que està ressaltatha d’aparèixer en qualsevol programa. Les comandes .model large
i .386 indiquen l’entorn en que ens mourem (model de treball i tipus d’instruccions). Després de la
definició del model, podem posar les constants que ens interessi. Les constants es defineixen com
a nomEQU valor. A partir d’aquest punt totes les aparicions d’una constant són substituïdes pel seu
valor (compte: NO és una variable, NO es troba a memòria).
A continuació es defineix el segment de pila per mitjà de la directiva .stack i el número de
bytes que volem dedicar a la pila (en aquest cas 100h bytes).
La directiva .data indica el començament del segment de dades. A partir d’aquest punt
declararem totes les variables globals que faci servir el nostre programa.
La directiva .code indica el començament del segment de codi. Al segment de codi estaran
els procediments i funcions del programa (com veurem en properes pràctiques) i el programa
principal. El programa principal és el codi comprés entre les directives .startup i .exit. El vostre
fitxer ha d’acabar necessàriament amb la directiva END.
Al començament i final del programa principal han d’anar una sèrie d’instruccions
obligatòriament, i que sempre són les mateixes. Aquestes instruccions NO les heu de posar
vosaltres, sinó que les afegeix el traductor substituïnt el .startup i el .exit per les instruccions
pertinents.
Com es compila i es linka un programa?
La traducció es fa amb el programa Turbo Assembler, amb la següent comanda:
tasm /zi nomprograma
NOTA: tant la comanda tasm com el vostre nom de programa (no fa falta extensió, només el
nom) pot estar en majúscules o minúscules. La opció de compilació /zi ha d’estar
OBLIGATÒRIAMENT en minúscules (si no, la ignora). Aquesta opció de compilació genera
informació que serà utilitzada després a l’hora de depurar el programa. El tasm genera un fitxer
nomprograma.obj.
Per muntar s’ha de fer:
tlink /v /3 nomprograma
NOTA: Novament, tant tlink com el nom del programa (sense extensió) pot estar en
majúscules o minúscules, mentre que les opcions /3 (que linka amb la biblioteca d’instruccions del
386) com /v (que fa que es deixi informació pel debugger) han d’estar en minúscules.
Un cop fet això ja tenim un executable nomprograma.exe que podem executar o depurar.
A partir d’aquest punt, ja treballarem amb l’ordinador en marxa.
5
fitxer
ostre
rror de
alitzat
ostra
es i de
PU
b
feu un
ent de
a l’altre
eu de
e pila
ramat
zen tot
ostre
cutar-les
Enunciats de la sessió.
Activitat 1.A: Com començar.
Començarem amb el programa que heu vist a la figura 1.2, i que es troba al
INTRO1.ASM. Primer de tot, editeu-lo amb l’edit per veure’l (i practicar una mica):
edit intro1.asm
Un cop estigueu familiaritzats amb l’edit ja podeu fer servir el traductor:
tasm /zi intro1
Fixeu-vos que quan traduïu, sortiran una llista d’errors o bé una indicació de que el v
programa ha estat traduït correctament. No passeu al muntatge fins que no tingueu cap e
traducció (en aquest cas no n’haurien de sortir d’errors).
tlink /v /3 intro1
Igualment, el muntador us informarà de si tot el procés ha estat correcte. Un cop heu fin
amb èxit aquest procés, ja podeu entrar al Turbo Debugger:
td intro1
El td ofereix moltes possibilitats. Només explicarem les que ens interessen a la n
assignatura. Primer de tot, ens interessa veure la informació a nivell de contingut de registr
memòria. Per fer això anirem al menú principal (F10) escollirem la opció View (V) i la subopció C
(C). És a dir, premeu consecutivament les teclesF10-V-C. Ara podeu veure una nova pantalla am
informació per la depuració. El millor és veure aquesta pantalla el més gran possible, per tant
Zoom (premeu la teclaF5).
La pantalla que veureu està dividida en 5 zones, que contenen informació sobre el segm
codi, els registres, els flags, la pila i el segment de dades. Podeu passar de una subpantalla
prement la teclaTAB (figura 1.3).
Al començament, la pantalla de codi mostra el programa que voleu depurar. Si canvi
pantalla (per mitjà deTAB ) anireu (en aquest ordre) a la pantalla de registres, a la de flags, a la d
i a la de dades, tornant després a la de codi (proveu de fer-ho).
A la pantalla de codi, us trobareu que les primeres instruccions no són les que heu prog
vosaltres. Són les instruccions introduïdes per la directiva.startup, i les podeu veure a la figura 1.4.
Aquestes instruccions són sempre les mateixes, independentment del programa, i inicialit
l’entorn de treball (fixeu-vos que inicialitzen els registres ds, ss i sp). La primera instrucció del v
programa està després d’aquestes 7 instruccions, per tant, el primer que hauríeu de fer és exe
6
da per
ostra
ciats
estes
mateix
ança
antalla
l vostre
i posar-vos a sobre de la primera instrucció pròpia del vostre programa (que està indica
l’etiqueta ETIQ1 (podeu veure com la informació sobre etiquetes es manté: abans de la v
primera instrucció hi ha un indicador #intro1#etiq1 que marca el nom del fitxer i l’etiqueta asso
a la instrucció).
Per executar una instrucció anirem al menú principal (F10), opcióRun (R), subopcióTrace
intro (T). Això executa una (i només una) instrucció. Hi han accions que es fan força sovint; aqu
tenen el que s’anomena un curtcircuït al teclat: una tecla o una combinació de tecles que fan el
sense necessitat de passar pel menú cada vegada. Per exemple, al costat deTrace intro trobareu el
seu curtcircuït (F7). Això vol dir que és equivalent ferF10-R-T que ferF7.
Per treballar, us recomanem que feu sempre el següent: primer executeu ambF7 les 7
instruccions del.startup (observeu com, conforme les executeu, a la pantalla de codi el cursor av
i com els registres i els flags que es modifiquen durant l’execució es veuen ressaltats a la p
respectiva). Un cop heu executat aquestes instruccions, esteu a la primera instrucció pròpia de
programa (identificada per #intro1#etiq1).
Pantalla de codi
Pan
talla
de
regi
stre
s
Pan
talla
de
flags
Pantalla depila
Pantalla de dades
TAB TAB
TAB
TAB
TAB
figura 1.3: Divisió de la pantalla altd
figura 1.4: Instruccions introduïdes per la directiva.startup
mov dx, “segm” ; segm pot canviar d’un PC a un altremov ds, dxmov bx, sssub bx, dxshl bx, 04mov ss, dxadd sp, bx
7
a dada
(des de
A3, us
ent), la
stra,
es (per
e.
a
. El
ariables
ateixa
veu
cia
Tot i que per defecte els registres surten amb 16 bits, hauríeu de fer servir eltd amb els registres
de 32 bits. Per fer això, aneu a la pantalla de registres (amb elTAB ) i feu ALT-F10 (les dues tecles
simultàniament) i al submenú que surt escolliu la opció de registres amb 32 bits (R).
Per veure les dades del vostres programa teniu dues possibilitats: inspeccionar una únic
o controlar el segment de dades. Per a inspeccionar una dada podeu anar al menú principal
qualsevol subpantalla) per mitjà de la teclaF10, escollir la opcióData (D), subopcióInspect (I ). Una
finestra us preguntarà quina variable voleu veure. Per exemple, pregunteu per la variable
sortirà una finestra com la de la figura 1.5, que us indicarà la seva adreça (segment:desplaçam
longitud (byte, word, dword) i el seu valor decimal i hexadecimal (per sortir d’aquesta fine
premeuESC)
L’altre possibilitat és controlar el segment de dades. Si us aneu a la subpantalla de dad
mitjà deTAB ) podeu ferALT-F10 , subopcióGoto (G) i us preguntarà quina variable voleu veur
Si poseu A1 veureu com la pantalla de dades es situa a l’adreçaDS:0000 (que és on comença la dad
A1). Amb la opció d’inspecció descrita al paràgraf anterior podeu observar com A1 és aDS:0000 ,
A2 aDS:0004 i A3 a DS:0008 , per tant podeu controlar totes les variables a la mateixa finestra
problema és que la finestra de dades us mostra la pantalla byte a byte i, en aquest cas, les v
són de mida dword. Si voleu veure les dades del DS amb una mida diferent de byte, feu a la m
pantalla de dadesALT-F10 Display as (D) i escolliu mida entreByte,Word oLong (Nota: en aquest
punt, pel TD undouble word és unlong word); per tant en aquest cas s’ha d’escollir la opcióL .
Torneu a la pantalla de programa (TAB ) i anem a executar el programa pas a pas. Obser
que la primera instrucció mou aEAXla variable A1. Cada cop que hi ha una instrucció que referèn
Inspecting A3
@XXXX:0008
dword 2004318071 (77777777h)
adreça inicial desegment de dades(coincideix ambel registre DS)
desplaçamentde la dadarespecte DS
longitut de la dada
valor (decimal)de la dada
valor (hexadecimal)de la dada
figura 1.5: Finestra de inspecció d’una dada
8
(veure
1
Si ara
a dir,
a memòria, a dalt de la pantalla surt la adreça implicada i el valor abans d’executar la instrucció
figura 1.6).
Si executeu les dues primeres instruccions veureu comEAXi EBXes carregen amb els valors
(00000001h) i -1 (FFFFFFFFh) respectivament. El resultat de sumarEAXi EBXes mostra a la figura
1.7.
Podeu comprovar que tant el registre com els flags que es modifiquen canvien de color.
executeu la segona suma (sumaEAX amb la constant N, que valia -3) sortirà el de la figura 1.8.
Abans d’executar la instruccióMOV A3, EAX observeu que:
a) A dalt de la pantalla us surt l’adreça i el valor de la posició de memòria implicada, és
A3 (ds:0008 77777777 ).
figura 1.6: Adreça i valor de la posició implicada en una
instrucció de memòria
ds:0000 00000001
ETIQ1: MOV EAX, A1
0000 0000 0000 0000 0000 0000 0000 0001 =EAX 1111 1111 1111 1111 1111 1111 1111 1111 =EBX
1 0000 0000 0000 0000 0000 0000 0000 0000
+
CF=1 SF=0
(el resultatés positiu)
OF=0(positiu mésnegatiu iguala positiu: nooverflow)
ZF=1
(el resultatés zero)
figura 1.7: Resultat de ADD EAX, EBX
9
dreça i
iable
r,
jut
l que té
dada
plícit i
1 i A2
anc en
ue en
rt és al
b) A la pantalla del segment de dades podeu observar com, efectivament, aquesta és l’a
el valor implicats.
Si executeu la instrucció (F7) podeu veure com a la pantalla del segment de dades, la var
A3 ha canviat de valor.
Les dues últimes instruccions (mov ah, 4c i int 21) són afegides per la directiva.exit. Si les
executeu, el programa acaba. Llavors podeu sortir deltd (ALT-X ) o bé tornar a executar-lo (F7, us
dirà que el programa ha acabat, premeuRETURN, F7 i us preguntarà si voleu tornar-lo a carrega
premeuRETURN i ja torneu a estar al començament del programa).
Ara ja teniu una idea de les possibilitats deltd. Anem a repassar uns quants conceptes amb l’a
d’aquest programa.
Activitat 1.B: Enters i naturals.
Recordeu que quan es representa qualsevol dada a memòria, té un valor explícit (aquel
com a dada guardada en binari) i un valor implícit (aquell que té interpretada com un tipus de
determinat o com a instrucció). En aquest apartat volem que veieu la diferència entre valor ex
valor implícit interpretat com a natural o com a enter. Suposem dues variables de mida byte A
amb valors binaris 00110010b i 11000000b, respectivament. Completeu les caselles en bl
l’exercici 1.1.
Haureu observat que els valors són ben diferents segons la interpretació (valor implícit) q
feu. Calculeu ara la suma dels dos números i respongueu les preguntes de l’exercici 1.2.
Ara és el moment de comprovar si heu contestat correctament. L’enunciat d’aquesta pa
0000 0000 0000 0000 0000 0000 0000 0000 =EAX 1111 1111 1111 1111 1111 1111 1111 1101 =N
0 1111 1111 1111 1111 1111 1111 1111 1101
+
CF=0 SF=1
(el resultatés negatiu)
OF=0(positiu mésnegatiu iguala negatiu: nooverflow)
ZF=0
(el resultatno és zero)
figura 1.8: Resultat de ADD EAX, N
10
a
per
Anem
b zeros
t
fitxer INTRO2.ASM (figura 1.9).
Assembleu-lo, munteu-lo i seguiu-lo amb eltd tal i com s’ha explicat a la primera part de l
pràctica. Executeu només les 3 primeres instruccions (fixeu-vos en l’ús dels registresAH i AL, que
formen part del mateix registre de 32 bits, l’EAX). Si la vostra resposta no era correcta analitzeu el
què.
El número resultant és de mida 1 byte. Si l’interpretem com a enter, és de signe negatiu.
ara a extendre aquest resultat de mida byte a mida double word. Aquesta extensió es pot fer am
(instruccióMOVZX) o fent una extensió de signe (instruccióMOVSX). Suposem que tenim el resulta
de la suma anterior als registresAL i BL. Ompliu les caselles en blanc de l’exercici 1.3.
Valor explícit(binari)Variable
A1
A2
00110010
11000000
Valor explícit(hexadecimal)
Valor implícit(com a natural
Valor implícit(com a enter
exercici 1.1
en decimal) en decimal)
Valor explícit(binari)
0 0 1 1 0 0 1 0
1 1 0 0 0 0 0 0Valor explícit(hexadecimal)
Valor implícit(com a natural
Valor implícit(com a enter
+
= = =
Quin és el valor final delflags? CF= OF= ZF= SF=
És el resultat final correcte interpretat com a natural?
És el resultat final correcte interpretat com a enter?
exercici 1.2
en decimal) en decimal)
11
Comproveu executant el programa si ho heu fet correctament.
.model large
.386
.stack 100h
.dataA1 db 00110010bA2 db 11000000b.code.startupETIQ1: MOV AL, A1 MOV AH, A2 ADD AL, AH MOV BL, AL MOVZX EAX, AL MOVSX EBX, BL.exitEND
figura 1.9: Programa INTRO2.ASM
Contingut(binari)Registre
AL=BL
Contingut(hexadecimal)
S’executa la instruccióMOVZX EAX, AL
Contingut d’EAX?
binari hexa
S’executa la instruccióMOVSX EBX, BL
Contingut d’EBX?
binari hexa
exercici 1.3
Anota el valor d’AL i BL després d’executarMOV BL, AL
12
010b.
Activitat 1.C: Instruccions lògiques
Suposem que tenim dues variables de mida byte A1 i A2 amb valors 11110000b i 10101
Calculeu el resultat de fer una operacióAND i una operacióOR entre les dues variables.
Per comprovar el resultat tenim el programa INTRO3.ASM
Executeu les 6 primeres instruccions (sense contar les del.startup) i comproveu les vostres
respostes.
Valor(binari)Variable
A1
A2
1 1 1 1 0 0 0 0
1 0 1 0 1 0 1 0
Valor(hexadecimal)
Valor(binari)
1 1 1 1 0 0 0 0
1 0 1 0 1 0 1 0
A1 AND A2
binari hexa
A1 OR A2
hexabinari
exercici 1.4
.model large
.386
.stack 100h
.dataA1 db 11110000bA2 db 10101010b.code.startupETIQ1: MOV AL, A1 MOV BL, A2 AND AL, BL MOV AH, A1 MOV BH, A2 OR AH, BH NOT AX MOV BX, 1000000000000000b BT BX, 15 BT BX, 14.exitEND
figura 1.10: Programa INTRO3.ASM
13
és
pliu les
El resultat de la instruccióANDés aAL i el resultat de la instruccióORés aAH, quin serà el
resultat de fer unNOT AX?
Executeu amb eltd la instruccióNOT AX i comproveu la vostra resposta.
La instrucció Bit Test (BT) carrega alCF un bit del primer operand. El bit que es carrega
indicat pel segon operand (els bits es compten de dreta a esquerra i començant per 0). Om
caselles en blanc.
Un cop contestat, comproveu la vostra resposta amb ajut deltd.
AX
binari hexa
NOT AX
binari hexa
exercici 1.5
BX
binari hexa1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 0 0 0
BT BX, 15 CF ?
BT BX, 14 CF ?
exercici 1.6
14
ió. Les
e un).
om a
ra,
Activitat 1.D: Rotacions i desplaçaments.
En aquest apartat veurem el funcionament de les instruccions de desplaçament i rotac
instruccions de desplaçament poden ser lògiques o aritmètiques.
Els desplaçaments lògics desplacen els bits del valor font introduïnt zeros (un o més d
L’últim bit que surt del valor font és emmagatzemant alflag CF (figura 1.11).
Els desplaçaments aritmètics fan el mateix, però mantenint el signe (figura 1.12).
Les instruccions de rotació també desplacen, però el bit que surt del valor serveix c
realimentació. Les instruccions deROLi RORfan que el bit desplaçat afora sigui el mateix que ent
a més de deixar una còpia alCF (figura 1.13).
SHR
0
CF
SHLCF
0
figura 1.11: Instruccions de SHIFT
SARCF
SAL (Fa el mateix que elSHL)CF
0
figura 1.12: Instruccions de SHIFT aritmètic
ROR CF
ROLCF
figura 1.13: Instruccions de rotació
15
e
bans
Les instruccions de rotació amb carry (RCR, RCL) funcionen de manera similar, però el bit qu
entra és el que hi havia alCF i el que surt va a parar alCF (figura 1.14).
També hi ha instruccions per posar elCF a 1 (STC), a 0 (CLC) i invertir-lo (CMC).
Per provar aquestes instruccions farem servir el programa INTRO4.ASM (figura 1.15). A
d’executar-lo feu l’exercici 1.7 i comproveu els resultats amb eltd.
RCR
CF
RCL
CF
figura 1.14: Rotacions amb carry
.model large
.386
.stack 100h
.dataA1 dd 80000000h.code.startupETIQ1: MOV EAX, A1 SHR EAX, 1 SHR EAX, 3 MOV EBX, A1 SAR EBX, 1 SAR EBX, 3 MOV ECX, A1 ROL ECX, 1 ROL ECX, 1 ROL ECX, 8 MOV EDX, A1 STC CLC RCL EDX, 1 RCL EDX, 1 RCL EDX, 1 STC RCL EDX, 1.exitEND
figura 1.15: Programa INTRO4.ASM
16
gma
rands
ó a la
són
ts, 2 de
Activitat 1.E: Instruccions de multiplicació.
Les instruccions de multiplicació i divisió al processador intel 386 són el paradi
d’instruccions que admeten moltes possibilitats, tant a nivell de mida d’operands, número d’ope
i operands implícits. Primer farem una petita explicació del seu funcionament (més informaci
documentació “Manual del 386”).
La instruccióMULrealitza una multiplicació de naturals. Té un únic operand (els altres
implícits) i aquest operand pot ser de mida byte, word o double word (i pot estar a memòria o a un
registre). Recordeu que multiplicar dos números de 8 bits té com a resultat un número de 16 bi
A1 dd 80000000h
Instrucció EBX (binari) CF
MOV EBX, A1
SAR EBX, 1
SAR EBX, 3
Instrucció EAX (binari) CF
MOV EAX, A1
SHR EAX, 1
SHR EAX, 3
Instrucció ECX (binari) CF
MOV ECX, A1
ROL ECX, 1
ROL ECX, 1
ROL ECX, 8
Instrucció EDX (binari) CF
MOV EDX, A1
STC
CLC
RCL EDX, 1
RCL EDX, 1
RCL EDX, 1
STC
RCL EDX, 1
exercici 1.7
17
s és que
e 32
l, E
uadres
heu
e
ARI
no. La
aquest
rvir les
, per
un
;
.
e,
16 bits donen un de 32, i dos de 32 en donen un de 64 bits.
Per tant podem realitzar una multiplicació amb operands de 8 bits fent
MUL op8
Quin és l’altre operand? On es deixa el resultat? La resposta a aquestes dues pregunte
tant l’altre operand font com l’operand destí són implícits (i són el registreAL i AX, respectivament).
Per tant, la operació que es fa és
AX = AL * op8
Pel que respecta a la multiplicació amb operands de 16 bits, l’operand implícit ésAX i els 32
bits de resultat s’emmagatzemen de la següent manera: els 16 bits alts es guarden alDXi els 16 baixos
al registreAX
Amb la mateixa filosofia, els 64 bits resultants de fer una multiplicació de dos elements d
bits es guarden al registresEDX (els 32 de més pes) i aEAX (els de menys pes).
Per il.lustrar aquest problema, anem a multiplicar 7 per 2. El resultat (14 decima
hexadecimal) ocupa menys d’un byte, per tant podem provar els 3 casos. Completeu els q
blancs de l’exercici 1.8 (i ratlleu la part dels registres que no es modifiquen).
A la figura 1.16 teniu el codi del fitxer INTRO5.ASM, que conté el codi per provar si el que
fet és correcte. Executeu només fins a l’etiquetaETIQb1 . Abans de llistar el codi, noteu un parell d
coses: al llarg del programa, els registres s’inicialitzen amb certs valors. Això NO ÉS NECES
fer-lo. En aquest programa està fet per que noteu quina part del registre es modifica i quina
segona cosa que heu de observar és que només s’han definit dues variables A1 i A2 per
problema, ambdues de mida double word. Per fer les operacions sobre byte i word es fan se
directives BYTE PTR i WORD PTR. Si no es fessin servir, el compilador es queixaria doncs
exemple, la instruccióMOV AL, A1intentaria moure una variable definida com de 32 bits (A1) a
registres de 8 bits (AL).
La instruccióIMUL fa una multiplicaciótenint en compte el signe. Pot tenir 1, 2 o 3 operands
en el cas de tenir un operand, funciona exactament igual que laMUL(però tenint en compte el signe)
Per il.lustrar les diferències entreMULi IMUL multiplicarem dos enters codificats en 1 byt
MUL op16
32 bits
DX AX
16 bits 16 bits
= AX * op16
MUL op32
64 bits
EDX EAX
32 bits 32 bits
= EAX * op32
18
em els
blanc de
rand
xò vol
bits alts
amb valors 0A6h (-90 considerat com a enter, 166 considerat com a natural) i 04h. Si consider
números com a enters o com a naturals, els resultats seran diferents. Ompliu les caselles en
l’exercici 1.9.
Executeu el programa amb eltd fins la etiquetaETIQb2 i comproveu el resultat.
La instruccióIMUL pot tenir també 2 i 3 operands. En el cas de tenir-ne dos, el primer ope
fa de font i destí, i no està limitat a ser AX (pot ser qualsevol registre). Per exemple
IMUL BX, CX fa BX = BX * CX
Noteu que el resultat podria ocupar fins a 32 bits, però es guarda en un registre de 16. Ai
dir que només es guardaran els 16 bits baixos del resultat, i si el resultat és més gran, els 16
AL= 07h BL=02hMUL BL
EAX?
AH AL
AX
DH DL
DX
EDX?
AX= 0007h BX=0002hMUL BX
EAX?
AH AL
AX
DH DL
DX
EDX?
EAX= 00000007h EBX=00000002hMUL EBX
EAX?
AH AL
AX
DH DL
DX
EDX?
exercici 1.8
AL= 0A6h BL=04hMUL BL
AX?
AL= 0A6h BL=04hIMUL BL
AX?
Valor decimal com a natural? Valor decimal com a enter?
exercici 1.9
19
.model large
.386
.stack 100h
.dataA1 dd 00000007hA2 dd 00000002hB1 db 0A6hB2 db 04hB3 dw 0FFA6hC1 dw 0301hC2 dw 0003hC3 dd 00000301hC4 dd 00000003h.code.startupETIQa1: MOV EAX, -1 MOV EDX, -1 MOV AL, BYTE PTR A1 MUL BYTE PTR A2ETIQa2: MOV EAX, -1 MOV EDX, -1 MOV AX, WORD PTR A1 MUL WORD PTR A2ETIQa3: MOV EAX, -1 MOV EDX, -1 MOV EAX, A1 MUL A2ETIQb1: MOV AL, B1 MUL B2 MOV EBX, EAX MOV EAX, 0 MOV AL, B1 IMUL B2ETIQb2: MOV CX, B3 IMUL CX, 04hETIQb3: IMUL BX, B3, 04hETIQc1: MOV EAX, -1 MOV EDX, -1 MOV AX, C1 CWD DIV C2ETIQc2: MOV EAX, -1 MOV EDX, -1 MOV EAX, C3 CDQ IDIV C4.exitEND
figura 1.16: Programa INTRO5.ASM
20
perand
ria o un
tí ha
l’altre
teix
doble
r la
te (una
bits
es perden. El mateix passa amb multiplicacions amb operands de 32 bits. A més, el primer o
ha de ser per força un registre, mentre el segon pot ser un registre, una adreça de memò
immediat.
En el cas de l’IMUL amb 3 operands, el primer és el destí i els altres dos els fonts. El des
de ser per força un registre, el primer font pot ser un registre o una posició de memòria, mentre
font només pot ser un immediat. Per exemple:
IMUL BX, B3, 04h BX = B3 * 4
Comproveu el funcionament d’aquestes instruccions executant amb eltd fins a l’etiqueta
ETIQc1.
Activitat 1.F: Instruccions de divisió.
Les instruccionsDIV i IDIV realitzen una divisió (de naturals o enters) i segueixen el ma
patró:
DIV dada
IDIV dada
On dada és el divisor i pot ser de 8, 16 o 32 bits. En qualsevol cas, el dividend ocupa el
de bits que el divisor. La operació retorna la divisió entera i el residu.
Si l’operand és de 8 bits es fa
AL = AX div dada8 ; AH = AX mod dada8 .
Si l’operand és de 16 bits el dividend és de 32 bits, on el 16 alts són aDXi els 16 baixos són aAX:
AX = DX:AX div dada16; DX = DX:AX mod dada16
Si l’operand és de 32 bits, el dividend és de 64 i es guarda aEDX(els 32 alts) i aEAX(els 32
baixos):
EAX = EDX:EAX div dada32; EDX = EDX:EAX mod dada32
COMPTE: si el quocient ocupa més bits dels del registres destí (AL, AX, EAX) llavors es
produeix un errordivisió per zero). Ho notareu perquè el programa s’atura i treu un missatge pe
pantalla.
Per tal de poder fer aquestes operacions és necessari tenir el dividend en el format correc
part aEAXo AX i l’altre a EDXo DX). Existeixen dues instruccions per convertir enters de 16 a 32
en formatDX:AX i 32 a 64 en EDX:EAX .
21
u els
no es
CWD (Convert Word to Double word) no té operadors (són implícits) i fa
DX:AX <- Extensió de signe del registreAX
CDQ (Convert Double word to Quad word) funciona de la mateixa manera:
EDX:EAX <- Extensió de signe del registreEAX
Per provar-lo executeu la última part del programa INTRO5.ASM, però abans conteste
espais en blanc de l’exercici 1.10 i de l’exercici 1.11 (ombrejar la part dels registres que
modifica).
C1=0301h C2=0003h
EAX?
AX
AH AL
EDX?
DX
DH DLInstrucció
MOV AX, C1
EAX?
AX
AH AL
EDX?
DX
DH DL
CWD
EAX?
AX
AH AL
EDX?
DX
DH DL
DIV C2
exercici 1.10
C3=00000301h C4=00000003h
EAX?
AX
AH AL
EDX?
DX
DH DLInstrucció
MOV EAX, C3
EAX?
AX
AH AL
EDX?
DX
DH DL
CDQ
EAX?
AX
AH AL
EDX?
DX
DH DL
IDIV C4
exercici 1.11
22
nt
mputa-
ències
Sessió 2: Tipus de dades i modes d’adreçame
Objectiu: En aquesta sessió repassarem la representació de la informació a la memòria del co
dor: veurem la definició i ús de punters, vectors i matrius. També veurem com programar sent
complexes comif-else i els buclesfor i while.
Lectura prèvia
Tipus de dades
Punters: un punter ocupa 32 bits i conté una adreça de memòria en format
segment:desplaçament, com es mostra a la figura 2.1.
Per inicialitzar aquest punter, es pot fer a la pròpia declaració del punter (figura 2.2a) o bé fer
el codi amb ajut de la instrucció LEA, que deixa al destí el desplaçament de la variable font (figura
2.2b).
@PX
4A
E300
12
figura 2.1: Exemple de punter
Suposem que ds=4AE3hi que tenim una
variable X a ds:0012
Una variable PX que fos un
valor 4AE3:0012
punter a X tindria el
X db ?PX dd X
a)
X db ?PX dd ?
...LEA AX, X ; carrega en AX el
;el desplaçament d’XMOV WORD PTR PX, AXMOV WORD PTR PX+2, DS; sabem que
;X és al segment de dades
b)
figura 2.2: Inicialització d’un punter a) a la pròpia declaració
i b) via codi.
23
e
ent del
ent 1
t, l’accés
ix en
nt 4
questa
ments
emòria
a
oc
nts del
ytes
tenir
Vectors: tots els elements d’un vector s’emmagatzemen en un únic bloc de memòria a partir
d’una adreça determinada, els diferents elements s’emmagatzemen en posicions consecutives de
manera que l’element i està entre l’i-1 i l’i+1 (figura 2.3). Els vectors estan definits sempre a partir d
la posició 0. El propi índex indica quants elements ens hem de desplaçar des del començam
primer element (per accedir a l’element 0 hem de saltar-nos 0 elements, per accedir a l’elem
hem de saltar-nos 1 element, etc... en general, per accedir a l’element amb índexi ens hem de saltar
els i elements anteriors)
Si l’emmagatzematge és com el del llenguatge C (int v[N]; ), llavors a partir de l’adreça de
v ja es troben tots els elements (ocupant 4 bytes cada element, donat que són enters). Per tan
a l’elementv[i] és com segueix:
v[i] = M d[@v + i*4]
Si pel contrari estem parlant d’un vector tipus JAVA, llavors l’emmagatzematge es produe
dues parts de memòria: el punter a les dades i les dades en si. Si definim un vectorv com a int v[]
= new int[N]; la variablev apunta a una posició de memòria on hi ha un punter (ocupa
bytes) que, al seu temps, apunta a un únic bloc de memòria on hi ha la informació del vector. A
informació consta d’un enter (4 bytes sempre) en la primera posició, que indica el número d’ele
del vector, i a continuació tots els elements emmagatzemats en posicions consecutives de m
(figura 2.4). Si volem accedir av[ i ] (l’element del vector amb índexi), hem de fer dos accessos
memòria: el primer a la posicióv per tal de trobar el punter a la informació del vector. En el ll
apuntat per aquest punter, i amb desplaçament 0, trobaríem la longitud en nombre d’eleme
vector (v.length ). L’elementi està a l’adreça que indica el punter més 4 (per saltar-nos els 4 b
de la longitud) mési*B, essent B la grandària en bytes de cada element. En resum, per ob
...
v[0]
v[1]
v[2]
v[n-2]
v[n-1]
@v[0]
N elements (si cada element ocupa B bytes, N*B bytes)
figura 2.3: Representació d’un vector a memòria
24
A no
siderar
ltre
v.length hem de fer:
v.length = Md[Mref[@v]]
Al seu temps, per obtenir l’element v[i] es fa fent:
@v[0] = Mref[@v] + 4
v[i] = M d[@v[0] + i*B]
o bé
v[i] = M d[Mref[@v] + 4 + i*B]
Matrius bidimensionals: una matriu bidimensional de NxM elements s’emmagatzema en
un únic bloc a memòria. Interpretem una matriu de NxM com una matriu de N files de M elements
cadascuna. Si cada element de la matriu ocupa B bytes, llavors la matriu ocupa un bloc de MxNxB
bytes (veure figura 2.5a).
Dins d’aquest bloc, els elements s’emmagatzemen per files, primer es guarden tots els
elements de la fila 0, després els de la fila 1, etc... Dins de cada fila, els elements estan ordenats
per columnes (figura 2.5b).
Per tant, per accedir a l’element mat[i][j] haurem de saltar i files completes (de M elements
de B bytes), i després j elements de B bytes (suposem una matriu d’enters, B=4 bytes). És a dir, la
fórmula per obtenir un element mat[i][j] serà:
mat[i][j] = M d[@mat + ((i*M)+j)*B]
El sistema anteriorment descrit serveix per a matrius tipus llenguatge C. El llenguatge JAV
admet matrius bidimensionals com a tals, però admet vectors de vectors, per tant podem con
una matriu deNxM elements com un vector deN elements on cadascun és, al seu temps, un a
...
length
v[0]
v[1]
v[n-2]
v[n-1]
@v
figura 2.4: Representació d’un vector JAVA a memòria
int v[] = new int[N];
v[]
TV: mida vectorN elements,cada element de B bytesTV: N*B bytes T
V+
4 by
tes
@v[0]
25
M
N
fila i
columna j
int mat[N][M];
@mat
NxMelementsNxMxBbytes
figura 2.5: a) Format d’una matriu C de N files i M
columnes i b) organització per files
...
fila
0m
at[0
,*]
fila
1m
at[1
,*]
fila
N-1
mat
[N-1
,*]
mat[1,0]mat[1,1]
mat[1,M-1]
mat[1,2]
a)
b)
26
ça de
vector deM elements (int mat2[][]= new int[N][M] , figura 2.6).
La manera d’obtenir un element mat2[i][j] és una mica més complexa:
@mat2[0] = Mref[@mat2] + 4
@mat2[i][0] = Mref[@mat2[0] + 4*i] + 4
mat2[i][j] = Md[@mat2[i][0] + 4*j]
És a dir, un primer accés per trobar la llista de punters a les files, un altre per trobar l’adre
la fila i, i un altre per trobar l’elementj , desplaçant-nos per la filai.
Modes d’adreçament
Els modes d’adreçament ens indiquen on són les dades implicades en una instrucció en
llenguatge màquina. Les dades poden ser a la pròpia instrucció (mode immediat), en un registre
...
N
mat2[0][]
@mat2
figura 2.6: Representació d’una matriu JAVA a memòria
int mat2[][] = new int[N][M];
mat2[]
mat2[1][]
mat2[N-2][]
mat2[N-1][]
...
M
mat2[0][0]
mat2[0][1]
mat2[0][M-2]
mat2[0][M-1]
...
M
mat2[1][0]
mat2[1][1]
mat2[1][M-2]
mat2[1][M-1]
etc
@mat2[0]
27
(mode registre) o a la memòria, i en aquest cas el mode pot ser absolut, indexat, indirecte,
desplaçament o una combinació dels anteriors i, en general és de la forma:
RS:constant[RB+RI*E]
• RS o registre de segment: pot ser qualsevol registre de segment (cs, ss, ds, es, fs, gs). Això
força el segment on es buscarà la dada. La resta de l’expressió indica el desplaçament dintre del
segment. Si no es posa registre de segment es considera per defecte el de dades (ds), excepte
en el cas que un dels registres sigui l’EBP o l’ESP, en aquest cas es considera per defecte el de
pila (ss).
• constant: pot ser qualsevol valor constant (etiquetes, números, constants, operacions entre con-
stants etc..). Si s’inclou algun símbol referent a una variable, el TASM avaluarà l’expressió sub-
stituint aquest símbol pel seu desplaçament respecte al seu segment, i NO PEL SEU VALOR (ja
que el valor li és desconegut)
• RB o registre base: pot ser qualsevol registre de 32 bits
• RI o registre índex: pot ser qualsevol registre de 32 bits, tret de l’ESP
• E o factor d’escalat: ha de ser un valor 1, 2, 4 o 8.
Sentències estructurades
En aquest punt veurem les sentències que defineixen un bucle (for i while) i la sentència
condicional (if-else) i la seva traducció a assemblador.
Les estructures for i while es poden executar un mínim de 0 iteracions (si el valor inicial és
superior al final en el for, i si la primera vegada no es compleix la condició en el while). La traducció
de les sentències for i while es pot veure a la figura 2.7.
Pel que respecta a la sentència if-else, la seva traducció seria com la indicada a la figura 2.8.
En tots aquests exemples s’han suposat variables enteres de 32 bits. Si les dades són d’un
altre tipus o grandària (o bé la condició del si és diferent), llavors cal que les adapteu al problema
que esteu programant.
28
int vi,vf;for (int i=vi; i<=vf; i++)
{cos del bucle}
i = vi;while (i<=vf)
{cos del buclei++;}
Suposem que el valor inicial és a lavariable vi i el valor final a lavariable vf . Farem servir el registreesi com a variable d’inducció i .VI dd 0VF dd 99
MOV ESI, VIbucle: CMP ESI, VF
JG fibucle;cos del bucleINC ESIJMP bucle
fibucle:
figura 2.7: Traducció de les estructuresfor i while
Suposem que els valors inicials d’ a ib són als registres eax i ebx
CMP EAX, EBXJNE sino; codi_llavorsJMP final
sino:; codi sino
final: ...
figura 2.8: Traducció d’una estructuraif-else
if (a==b){codi_llavors}
else{codi_sino}
29
vector
ent la
mb
teu
poc
on
Enunciats de la sessió
Activitat 2.A: Vector de tipus C
En aquest primer apartat, estudiarem un bucle que fa la suma de tots els elements d’un
de tipus C. El vector es diu v_C, i té 8 elements de tipusshort (enter de 16 bits). L’algorisme que
realitza la suma dels elements el podeu trobar a la figura 2.9a.
Si analitzeu el codi en assemblador (figura 2.9b), veureu que es recorre tot el vector, f
suma sobre el registreax i omplint la variable suma amb el resultat al final del bucle.
El codi de la figura 2.9.b el teniu al fitxer TDMA1.ASM. Compileu i munteu el programa a
el tasm i el tlink . Ara anem a executar l’algorisme amb eltd. Per veure el funcionament, podem
executar un parell d’iteracions ambF7 i veure com els valors dels registres van variant. Si execu
un parell d’iteracions ambF7 veureu que el fet d’executar instrucció a instrucció de vegades és
útil. Per accelerar això podem fer servir els punts d’aturada obreakpoints.
Per instal-lar unbreakpointhem de fer servir el cursor per situar-nos a sobre de la instrucció
volem posar el punt d’aturada. Amb els cursors, situeu-vos a sobre de la instrucciójmp bucle1 i feu
F10, opcióBreakpoint, subopcióToggle (o utilitzeu el curtcircuïtF2). Veureu com la instrucció ha
short v_C[8];short suma;
suma=0;for (int i=0; i<8; i++)
{ suma += v_C[i];}
a)
.code
.startupmov esi, 0
mov ax, 0 mov ecx, 8bucle1: cmp esi, ecx je fi_bucle add ax, v_C[esi*2] inc esi jmp bucle1fi_bucle: mov suma,ax.exit
b)
figura 2.9: a) algorisme bàsic en alt nivell i b) la seva traducció a assemblador.
30
b un
nt
odeu
u
21)
Si voleu
u
com
xer
ts
ix un
eu
quedat marcada. Es pot executar el programa amb unrun (F10, opció Run, subopcióRun o el
curtcircuït F9). En aquest cas, el programa s’executarà fins que trobi una instrucció am
breakpointo fins que el programa finalitzi. Si executeu ambF9, veureu que sempre us atureu al pu
on vàreu posar elbreakpointi que cada vegada heu executat una iteració completa. Amb això p
fer una execució completa. Tres comentaris respecte delsbreakpoints i l’opció run:
1) Per eliminar elsbreakpointsésF10opcióBreakpoint, subopcióDelete all, que elimina TOTS
els punts d’aturada. Si voleu eliminar només unbreakpoint, situeu-vos a sobre de la instrucció i fe
F10, opcióBreakpoint, subopcióToggle (o utilitzeu el curtcircuïtF2). Veureu com la instrucció deixa
d’estar marcada.
2) En qualsevol punt del programa podeu tornar a començar fentF10, opció Run, subopció
Program reset (curtcircuïtCtrl-F2 ).curtcircuït
3) Si feu unrun i arribeu al final de tot, penseu que quan s’executa l’última instrucció (INT
les dades que hi ha al segment de dades o als registres poden no ser les del vostre programa.
estar segurs, poseu unbreakpointa la instrucció MOV AH, 4Ch (la d’abans del INT 21) i comprove
llavors les vostres dades.
Podem comprovar el resultat seguint les sumes parcials al registreax o bé mirant el resultat
final a la variablesuma. Per mirar el resultat de la variable podem veure què hi ha a memòria asuma
(un cop executada la instrucció etiquetada com a fi_bucle, que mou el resultat asuma), anant a la
pantalla de memòria (per mitjà delTAB ) i mostrant el resultat (ALT-F10 G escrivintsuma). Per
veure-la en formatword (és unshortde Java), hem de ferALT-F10 D i escollirword (el resultat és
329 = 0x0149).
Una altra manera de veure-ho és fer una inspecció del valor d’aquesta variable (F10, opcióData,
subopcióInspect i poseusuma). Recordeu que aquesta finestra us mostra tant el valor decimal
l’hexadecimal (per tant 0x149, 329).
Ara anem a modificar una mica el programa. Copieu TDMA1.ASM en un altre fit
TDMA2.ASM (feucopy TDMA1.ASM TDMA2.ASM). Volem que faci la suma de tots els elemen
del vector v_C2, que ja està definit amb tots els elements de midashort (16 bits).
Feu l’exercici 2.1 i acabeu-lo abans de continuar llegint.
Si heu fet l’exercici 2.1 haureu comprovat que la suma dels valors d’aquest vector produe
overflowsobre unshort . Per tant hem de deixar el resultat de la suma sobre unint suma2 (32
bits).
Implementeu sobre el codi del fitxer TDMA2.ASM l’algorisme de la figura 2.10. Comprov
executant amb el td que el resultat de suma2 és el correcte (36000 = 0x00008CA0)
31
exercici 2.1
Traduïu el número 36000 a binari:
Els elements del vector v_C2 són:
32000, 4333, 328, -123, 4, -555, 34, -21 suma= 36000
Interpreteu el resultat com a enter de 16 bits i traduïu-lo a decimal,
quant dóna?
es pot representar el número enter 36000 en 16 bits?
short v_C2[8];int suma2;
suma2=0;for (int i=0; i<8; i++)
{ suma2 += (int)v_C2[i];}
figura 2.10: Nou algorisme.
32
suma
1.
saber
Activitat 2.B: Vectors de tipus Java
Copieu el fitxer TDMA1.ASM a TDMA3.ASM (copy TDMA1.ASM TDMA3.ASM). Si
l’editeu, veureu que també hi ha definit un vector en tipus Java (v_JAVA), de tipusshort i de 8
elements, que coincideixen en valor amb els del vector v_C. Volem fer el mateix algorisme de
de shorts sobre una variablesuma_java de tipus short . Modifiqueu el necessari per
implementar l’algorisme de la figura 2.11.
Comproveu que el resultat obtingut és el mateix que en el primer apartat de l’exercici 2.
Un cop us funcioni, anem a introduir una sentènciaif-else. Copieu el fitxer TDMA3.ASM a
TDMA4.ASM i modifiqueu-lo per implementar el codi de la figura 2.12.
Comprovació: Els elements del vector són: 1, 2, 300, 123, -45, 23, -62, -13. Per tant heu de
quins valors tindran les variablessuma_pos i suma_neg al final de la execució.
short v_JAVA[]= new short[8];short suma_java;
suma_java=0;for (int i=0; i<8; i++)
{ suma_java += v_JAVA[i];}
figura 2.11: Sumatori de tots els elements d’un vector Java.
short v_JAVA[]= new short[8];short suma_pos;short suma_neg
for (int i=0; i<8; i++){ if (v_JAVA[i]<0){
suma_neg += v_JAVA[i];}
else {suma_pos += v_JAVA[i];}
}
figura 2.12: Codi amb una sentènciaif-else bàsica.
33
2.14).
Activitat 2.C: Accés a matrius tipus C
El fitxer TDMA5.ASM (que ja existeix) conté el que es pot veure a la figura 2.13.
En aquest fitxer està definida una matriu tipus C anomenadamat_c . És una matriu de 4 files
per 6 columnes (int mat_c[4][6]; ) que ja està inicialitzada. Resoleu l’exercici 2.2.
Volem fer un programa que sumi tots els elements de la columna 2 de la matriu (figura
.dataaux0 dd 6 dd 0,1,2,3,4,5aux3 dd 6 dd 30h,31h,32h,33h,34h,35haux2 dd 6 dd 20h,21h,22h,23h,24h,25haux1 dd 6 dd 10h,11h,12h,13h,14h,15haux_j dd 4 dd aux0 dd aux1 dd aux2 dd aux3mat_java dd aux_jmat_c dd 0,1,2,3,4,5 dd 10h,11h,12h,13h,14h,15h dd 20h,21h,22h,23h,24h,25h dd 30h,31h,32h,33h,34h,35hsuma_java dd 0suma_c dd 0.code.startup
.exitEND
figura 2.13: Fitxer TDMA5.ASM
exercici 2.2
Donada la definicióint mat_c[4][6]; com a matriu de tipus C, suposant quei és una variable int, quina és la fórmula per accedir a l’element mat_c[i][2]?
34
ercici
lement
la
(104
Implementeu aquest codi utilitzant per a l’accés de dins del bucle la fórmula trobada a l’ex
2.2 (accés aleatori). Per comprovar-ho, el resultat és 104 = 0x68.
Sabem que també es pot fer accés seqüencial (trobar un element en funció de l’e
anterior). Feu l’exercici 2.3.
Copieu el fitxer TDMA5.ASM a TDMA6.ASM i implementeu sobre aquest últim el codi de
figura 2.14 amb accés seqüencial. Utilitzeu el resultat de l’exercici 2.3. El resultat és el mateix
= 0x68).
suma_c=0;for (int i=0;i<4;i++)
{suma_c += mat_c[i][2];}
figura 2.14: Suma dels elements de la columna 2.
exercici 2.3
Calculeu les fórmules de l’accés a mat_c[i][2] i mat_c[i+1][2] i trobeu la diferència
35
da
Activitat 2.D: Accés a un vector de vectors (Java).
Copieu el fitxer TDMA5.ASM a TDMA7.ASM. Observareu (figura 2.13) que tenim defini
la mateixa matriu amb els mateixos valors com a vector de vectors de Java (mat_java ). Volem que
implementeu el codi de la figura 2.15.
Ho haureu d’implementar per mitjà d’accés aleatori. Feu l’exercici 2.4.
Implementeu l’algorisme i comproveu el resultat (novament dóna 104 = 0x68).
suma_java=0;for (int i=0;i<4;i++)
{suma_java += mat_java[i][2];}
figura 2.15: Suma dels elements de la columna 2.
exercici 2.4
Donada la definicióint mat_java[4][6]; com a matriu de tipus Java,quina és la fórmula que ens permet accedir a l’element mat_java[i][2]?
36
en les
ducció
Sessió 3: Subrutines
Objectius: En aquesta sessió experimentarem en l’ús de subrutines. Veurem com es realitz
diferents fases de la gestió de les subrutines i programarem subrutines a partir d’una tra
sistemàtica de codi en alt nivell que utilitza funcions i/o procediments.
Lectura prèvia
A continuació trobareu un resum amb els conceptes més importants que s’han introduït en
aquest tema de subrutines: definicions, fases de la seva gestió, contingut del bloc d’activació i
traducció de codi en alt nivell que utilitza funcions i/o procediments.
Definicions
Subrutina: Conjunt d’instruccions de llenguatge màquina (o assemblador) que realitza una
tasca parametritzable. Serveix per implementar les funcions, procediments i mètodes que
existeixen en els llenguatges d’alt nivell.
Vegeu en la figura 3.1 els diferents elements que apareixen en un codi en alt nivell on es
defineix una funció i es referencia aquesta funció.
figura 3.1: Definició i referència d’una funció
...r = f(pr1,pr2);...
int f (int pf1, int pf2){/* variables locals*/l1,l2:int;
(* cos de la funció *)return resultat;
}
❶ activació de la funció
❷ desactivació de la funció
pr1,pr2: paràmetres realspf1, pf2: paràmetres formalsr: resultat de la funciól1,l2: variables locals
❶
❷
37
Bloc d’activació: Correspon a tota la informació que es manega durant la gestió de les
subrutines: resultat de la subrutina, paràmetres de la subrutina, adreça de retorn, variables locals i
estat del processador. La major part del bloc d’activació es guarda a la pila (excepte el resultat i
aquelles variables locals que s’emmagatzemen en registres).
Paràmetres reals/formals:Els paràmetres reals són els que es passen en cadascuna de les
activacions que es fan a una subrutina. Els paràmetres formals són els que s’utilitzen dins la
definició d’una subrutina.
Paràmetres per valor/ per referència:Un paràmetre per valor és únicament d’entrada a una
subrutina. Un paràmetre per referència pot ser d’entrada i/o sortida.
Fases de la gestió de les subrutines
Passar els paràmetres:Consisteix en col.locar els paràmetres reals que es passen a un
subrutina en el lloc adequat perquè la subrutina pugui utilitzar-los quan necessiti accedir als seus
paràmetres formals.
Considerarem que els paràmetres sempre es passen en la pila. L’ordre en què s’empilen els
paràmetres sempre és el contrari a l’ordre en què estan escrits.
Un paràmetre per valor es passa col.locant una CÒPIA del paràmetre real en el cim de la pila.
Un paràmetre per referència es passa col.locant l’ ADREÇA del paràmetre real en el cim de la pila.
Cridar a la subrutina: Consisteix en guardar l’adreça de retorn a la pila i saltar a executar la
primera instrucció de la subrutina. Tot això es fa quan s’executa la instrucció CALL.
Establir el punter al bloc d’activació (o establir l’enllaç dinàmic):Consisteix en inicialitzar
un registre (s’utilitza el “Frame Pointer” o EBP) perquè apunti a una posició fixa dins el bloc
d’activació.
Crear les variables locals.Consisteix en reservar a la pila l’espai necessari per a les
variables locals a la subrutina i, si cal, inicialitzar-les. L’ordre en què queden les variables locals a la
pila és el mateix amb el que han estat declarades.
Salvar l’estat:Consisteix en guardar a la pila tots els registres que es modifiquen en el cos de
la subrutina.
Executar la subrutina:Els aspectes específics a nivell de subrutines que es poden produir
en el cos de la subrutina i que hem de saber realitzar correctament són: accés als paràmetres,
accés a les variables locals i accés al resultat.
A un paràmetre s’hi accedeix a través del registre EBP amb el desplaçament positiu
necessari segons el lloc que li correspon dins el bloc d’activació. A una variable local s’hi accedeix
a través del registre EBP amb el desplaçament negatiu adequat. El resultat d’una subrutina sempre
es col.loca en el registre EAX (o bé en l’AX o l’AL si la mida del resultat fos de 2 o d’1 byte).
38
Restaurar l’estat: Consisteix en recuperar el valor que tenien els registres que s’hagin
modificat al llarg de la subrutina.
Eliminar les variables locals:Consisteix en alliberar l’espai que ocupaven les variables locals
dins la pila.
Restaurar el punter al bloc d’activació:Consisteix en recuperar el valor del registre EBP que
hi havia a l’inici de la subrutina.
Retornar de la subrutina:Consisteix en saltar a la instrucció següent a la de la crida a la
subrutina. Això es fa quan s’executa la instrucció RET.
Eliminar els paràmetres:Consisteix en treure de la pila tots els paràmetres que s’hagin
passat.
Recollir el resultat:Consisteix en agafar el resultat que ha deixat la subrutina en el registre
EAX (o bé en l’AX o en l’AL, segons la seva mida).
Traducció literal de codi en alt nivell
A continuació repassarem quina és la traducció a llenguatge assemblador que resulta d’un
codi en alt nivell que defineix una funció i la referencia. Vegeu aquest codi en la figura 3.2.
En aquest exemple veiem que es defineix una funció f que té dos paràmetres d’entrada i
retorna un enter. El paràmetre formal pf2 és un enter i per tant es passa per valor. El paràmetre
formal pf1 és un vector i es passa per referència. En el programa principal es fa una referència a la
funció on es passen com a paràmetres reals les variables globals pr1 i pr2 . El resultat de la funció
es guarda a la variable global r .
figura 3.2: Exemple de codi en alt nivell que utilitza funcions
int r,pr1[10],pr2; // pr1 és un vector C
int f(int pf1[10], int pf2) { // pf1 és un vector Cint l1,l2;
l1 = pf1[0];l2 = pf2;return (l1+l2)/2;
}
void main() {r = f(pr1,pr2);
}
39
A l’hora de traduir aquest codi és recomanable que tinguem clarament identificat quin és el
contingut del bloc d’activació que es formarà a la pila. Adoneu-vos que el bloc d’activació està
totalment construït en el moment que s’ha acabat d’executar la fase anomenada “Salvar l’estat” i,
per tant, estem a punt de començar a executar el cos de la subrutina. La figura 3.3 mostra el
contingut del bloc de l’activació de la subrutina que estem considerant en aquest exemple.
La traducció literal (sense optimitzar) d’aquest codi a assemblador seria el que es mostra a la
figura 3.4. Observeu que les subrutines es col.loquen en el segment de codi abans que comenci el
programa principal (abans de la directiva .startup ).
Abans de començar a fer les diferents parts de la pràctica mireu com s’han implementat les
diferents fases de la gestió de subrutines i com s’ha traduït literalment cadascuna de les sentències
que hi ha en el cos de la subrutina exemple.
figura 3.3: Contingut del bloc d’activació abans de començar el cos de la subrutina
PF2@PF1
@retorn
EBPantSS:EBP
L2
L1
reg1
...
El punter al paràmetre PF1 és a l’adreça: SS:8[EBP]
El paràmetre PF2 és a l’adreça: SS:12[EBP]
La variable local L1 és a l’adreça: SS:-8[EBP]
La variable local L2 és a l’adreça: SS:-4[EBP]
regn
40
figura 3.4: Exemple de codi en assemblador que utilitza subrutines
.model large
.386
.stack 100h
.dataPR1 DD ?PR2 DD ?R DD ?
.code
F PROCPUSH EBP ; establir punter ...MOV EBP, ESP ; ... al bloc d’activacióSUB ESP, 8 ; crear variables localsPUSH ES ; salvar ...PUSH EBX ; ... l’estat
; COS DE LA SUBRUTINA (INICI)MOV ES, 10[EBP] ; traducció de ...MOVZX EBX, WORD PTR 8[EBP] ; ...MOV EBX, ES:[EBX] ; ...MOV -8[EBP], EBX ; ... l1 := pf1[0]
MOV EBX, 12[EBP] ; traducció de ...MOV -4[EBP], EBX ; ... l2 := pf2
MOV EBX, -8[EBP] ; traducció de ...ADD EBX, -4[EBP] ; ...SAR EBX, 1 ; ...MOV EAX, EBX ; ... retorna (l1+l2)/2
; COS DE LA SUBRUTINA (FI)
POP EBX ; restaurar ...POP ES ; ... l’estatMOV ESP, EBP ; eliminar variables localsPOP EBP ; restaurar punter al bloc d’act.RET ; retornar de la subrutina
F ENDP
.startupPUSH PR2 ; passar paràm. per valorPUSH DS ; passar ...LEA AX, PR1 ; ...PUSH AX ; ... paràm. per referènciaCALL F ; cridar la subrutinaADD ESP, 8 ; eliminar els paràmetresMOV R, EAX ; recollir el resultat.exit
end
41
eu el
ma els
ció per
es, que
passen
e
Enunciats de la sessió
Activitat 3.A: Veure el bloc d’activació
La primera activitat consisteix en veurem com és un bloc d’activació concret. Consider
codi en alt nivell que apareix a la figura 3.5. En aquest programa es defineix una funció que su
elements d’un vector (emmagatzemat per files com en C) i es fa una referència a aquesta fun
calcular la suma dels elements del vectort .
Tindrem en compte les consideracions per defecte que es fan durant la gestió de subrutin
són:
• Els paràmetres es passen en la pila de dreta a esquerra.
• El pas de paràmetres de tipus basics es fa sempre per valor mentre que els vectors es
per referència.
• El resultat es retorna en el registre EAX.
• Les variables locals s’emmagatzemen a la pila. En aquest cas concret, només s’ha d
guardar a la pila la variabletmp , ja que la variablei , que és de control d’un bucleper , es
guarda en un registre directament.
Feu l’exercici 3.1.
figura 3.5: Codi en alt nivell considerat en l’activitat 1
// constantsfinal int N=5;// variables globalsint res;int t[N]={10,11,12,13,14};
int suma_vector(int v[N], int num) {int tmp;
tmp = 0;for (int i=0; i<num; i++) {
tmp += v[i];}return tmp;
}
void main() {res = suma_vector(t,N);
}
42
. Per
figura
ar-lo.
pte la
tot el
rucció
ueix
b F8 i
de codi
s
c
de la
s
anera
servir
de la
ut de
Anem a comprovar amb el TurboDebugger quin és el contingut d’aquest bloc d’activació
a això, utilitzarem el programa SUBR1.ASM, que és la traducció a assemblador del codi de la
3.5.
Primer de tot, hauríeu d’entendre el codi de la subrutinasuma_vector . És un exercici de
traducció literal de subrutines, i es fa tal com s’ha resumit en la lectura prèvia.
Un cop analitzat el programa, l’assembleu i el munteu per tenir-lo preparat per a depur
Dins el TD, el primer que heu de fer és comprovar que l’execució és correcta. Tenint en com
inicialització del vectort , el contingut final de la variableres ha de ser0000003Ch .
És convenient que sapigueu una opció més que ens permet el TD. A part d’executar
programa sencer (F9), executar fins un punt d’aturada (F2 + F9) o executar instrucció a inst
(Trace , F7), hi ha la possibilitat d’executar tot el codi de la subrutina de cop. Això s’aconseg
amb l’opcióStep (la seva tecla de funció associada és F8). Proveu d’executar el programa am
amb F7 i observeu les diferències que hi ha. Mentre ho feu, adoneu-vos que a la subfinestra
de la CPU, quan hi ha una instrucció “CALL”, apareix també una instrucció “PUSH CS” prèviament.
Això és correcte. Quan programeu en assemblador una instruccióCALL es posen sempre aqueste
dues instruccions en llenguatge màquina.
Un cop vista la diferència entreStep i Trace anem a veure el bloc d’activació. El blo
d’activació està totalment constituït quan comencem a executar la primera instrucció del cos
subrutina (MOV ES, 10[EBP]). Polseu, doncs, la teclaF7 (Trace) cinc cops perquè s’executin le
instruccions anteriors a aquesta. Sabem que el bloc d’activació està a la pila, però la millor m
de visualitzar-lo no és a través de la subfinestra de la pila (dins la finestra CPU). El millor és fer
la subfinestra de memòria, ja que és molt més flexible d’utilitzar. Us situeu en aquesta part
finestra CPU i li indiqueu que voleu visualitzar a partir de l’adreça SS:EBP (premeuCTRL-G per
poder introduir l’expressió de l’adreça a visualitzar). Indiqueu també que voleu veure el conting
exercici 3.1
Escriviu els elements que constitueixen el bloc d’activació de la subrutina
suma_vector . A l’esquerra, indiqueu el desplaçament, relatiu al EBP, de cada element
SS: EBP+( )
SS: EBP+( )SS: EBP+( )SS: EBP+( )SS: EBP+( )
@lògica element del bloc d’activació
43
ó
dades.
BP+0 hi
, que
cimal.
on
na
splaceu
tingut
bloc
d’una
oincidir
l al que
funció
memòria en mida dword (premeuCTRL-D Long ). Llavors, veureu el contingut del bloc d’activaci
que hi ha a partir d’on apunta EBP.
Observeu que el registre de segment de la pila coincideix amb el registre de segment de
Això és degut al tipus de model de programació que considerem (.model large ). Per tant, la
subfinestra de dades us posarà DS:0110 i aquesta adreça és la mateixa que SS:EBP. A SS:E
ha d’haver el contingut anterior del registre EBP. A SS:EBP+4 hi ha d’haver l’adreça de retorn
ésCS:006A . A SS:EBP+8 hi ha d’haver el punter al paràmetrev , que ésDS:0004 . A SS:EBP+12
hi ha d’haver el paràmetrenum, que és00000005 .
És important que sapigueu que el TD interpreta tots els números que introduïu en hexade
Així, si volguéssiu anar a veure el paràmetrenum directament (que està 12 bytes més enllà d’
apunta EBP), hauríeu de prémerCTRL-G seguit de SS:EBP+0C dins la finestra on us dema
l’expressió.
Si voleu veure el contingut de les adreces anteriors a on apunta EBP només cal que us de
amb el cursor o li indiqueu que voleu veure a partir d’una altra adreça. Així, si voleu veure el con
de la variable localtmp , us posicioneu a SS:EBP-4.
La figura 3.6 us mostra el contingut sencer del bloc d’activació. El rang d’adreces del
d’activació és [SS:0102, SS:011F]. Tingueu en compte que el valor dels segments pot variar
execució a una altra. Per tant, els valors dels segments que surten a la figura no tenen per què c
amb els que obteniu vosaltres. Per exemple, en l’adreça de retorn, el segment ha de ser igua
valgui el registre CS. D’altra banda, el contingut de la variable localtmp és indefinit ja que encara
no s’ha inicialitzat. Tingueu en compte també que el contingut dels registres podria variar en
de com estiguin inicialitzats al principi del programa.
ds:0100 5BDA.... 00000000ds:0108 00000020 xxxxxxxxds:0110 00000000 5BEA006Ads:0118 5BF20004 00000005ds:0120 ........ ........
figura 3.6: Contingut del bloc d’activació
ES
CSDS
SS=DS
44
iment
conté
cució.
ioni
un
hi ha
er per
us
ut del
Activitat 3.B: Analitzar una subrutina (que conté errors)
Haureu d’analitzar el codi d’una subrutina que correspon a la traducció literal del proced
que es mostra a la figura 3.7. El codi de la subrutina és a la figura 3.8. Tanmateix, la subrutina
uns quants errors. Els errors es produeixen tant en temps d’assemblatge com en temps d’exe
L’objectiu és que localitzeu tots els errors i aneu corregint la subrutina fins que func
correctament.
El procés que hauríeu de seguir és el següent:
• Fer l’exercici 3.2. Es tracta de dibuixar el bloc d’activació corresponent asubr2 .
• Fer un seguiment visual del codi de la subrutinasubr2 . Mireu si hi ha algun error que ja el
pogueu localitzar directament. Aneu escrivint els errors en el lloc estipulat en l’exercici 3.3.
• Assemblar el programa. El codi de la subrutina és al fitxer SUBR2.ASM. Veureu que hi ha
error d’assemblatge en el codi original. Esbrineu què és el que es pretén fer en la línia que
l’error i programeu-ho adequadament.
• Depurar el programa. Aquí es tracta que utilitzeu les eines que us ofereix el Turbo Debugg
fer la depuració dels programes. Heu de considerar el programa principal (figura 3.7) que
donem fet i que NO CONTÉ CAP ERROR (no l’heu de modificar).
Per saber si teniu la subrutina programada correctament, heu de visualitzar el conting
vectorvs, situant-vos a la subfinestra de dades i prementCTRL-G seguit devs . La subrutina el
que fa és col.locar envs[i] el valor del primer paràmetre (ve[i] ) mes el segon (-3 ). Per tant, com
que el vectorve està inicialitzat a (-1,0,1,2,3,4) el contingut correcte del vectorvs al final del
programa hauria de ser: (-4, -3, -2, -1, 0, 1).
figura 3.7: Programa principal que referencia el procediment SUBR2
// variables globalsint ve[6]={-1, 0, 1, 2, 3, 4};int vs[6];
void subr2(int a, int b, int c, int v[6]) {}
void main() {int i;
for (i=0; i<6; i++) {subr2(ve[i], -3, i, vs);
}}
45
figura 3.8: Codi del procediment considerat en l’activitat 2.B
void subr2( int a, int b, int c,int v[6]) {
int loc;
loc = a + b;v[c] = loc;
}
exercici 3.2
Dibuixeu el bloc d’activaciód’aquest procediment
figura 3.9: Subrutina que conté errors
SUBR2 PROC
PUSH EBPMOV EBP, ESPSUB ESP, 4PUSH EAXPUSH EBXPUSH ES
MOV ECX, 4[EBP]ADD ECX, 6[EBP]MOV EBX, WORD PTR 20[EBP]MOV ES, 22[EBP]MOV EAX, 12[EBP]MOV ES:[EBX+4*EAX], ECX
POP EAXPOP EBXPOP ESMOV ESP, EBPPOP EBP
SUBR2 ENDP
exercici 3.3
Anoteu els errors que aneulocalitzant i com els heu corregit.
46
es reals.
la
a de
ments
r
, com
l byte de
r és
i ha
Activitat 3.C: Implementar diversos passos de paràmetres
En aquesta activitat farem el pas de paràmetres per diferents tipus de dades i de paràmetr
A la figura 3.10 es mostra l’estructura del programa i la definició de variables globals.
El programa principal crida a la funciósubr3 i li passa per paràmetre el valor 0. Aleshores
subrutinasubr3 fa una crida a la funciós3 i li passa:
M - variable global de tipus matriu C (per referència)
tam_fila - variable local de tipus enter (per valor)
param - paràmetre de tipus enter (per valor)
V - variable local de tipus vector C (per referència)
‘X’ - constant de tipus char (per valor)
La funciós3 es una funció externa, és a dir, no està definida al mateix fitxer font, pero s’h
muntar junt amb aquest programa per construir l’executable. Aquesta funció copia tots els ele
vectorv2 a la filanfila de la matriuv1 i retorna el número de vegades que ha trobat el caràctec.
Feu en primer lloc l’exercici 3.4. Tingueu en compte que els les dades que ocupen 1 byte
ara els caràcters, quan es passen com a paràmetre ocupen 2 bytes a la pila i la dada està a
menys pes (el de més pes no conté informació útil).
Després comproveu amb el TD que el codi sigui correcte. El fitxer que heu d’utilitza
SUBR3.ASM. La funciós3, ja assemblada, és al fitxer S3.OBJ. Comproveu com al programa h
figura 3.10: Estructura del programa
// definicio variables globalschar M[2][7]={‘a’, ‘b’, ‘c’, ‘d’, ‘e’, ‘f’, ‘g’,
‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’ };
int res;
// definicio subrutina externaint s3( char v1[2][7], int tam, int nfila, char v2[7], char c) {}
// subrutina a programarint subr3(int param){
char V[7] = {‘D’, ‘I’, ‘A’, ‘0’, ‘D’, ‘I’, ‘A’};int ret, tam_fila = 7;
ret = s3 (M, tam_fila, param, V, ‘D’);return ret;
}
// programa principalvoid main () {
res = subr3(0);}
47
i
ar la
una directiva .extrn s3:far per tal d’indicar que el símbols3 correspon a una etiqueta de cod
que es defineix externament. La comanda per muntar el programa ha de ser:
tlink /3/v subr3 s3
Al fitxer SUBR3.ASM només hi heu d’afagir les instruccions necessàries per implement
subrutinasubr3: salvar i restaurar els registres utilitzats, fer el pas de paràmetres abans delcall ,
alliberar l’espai dels paràmetres després delcall i retornar el valorret .
Els resultats correcte que hi hauria d’haver a les variables globals són:
res = 00000002h
M = ‘DIA0DIA’
exercici 3.4
SS: EBP+( )
SS: EBP+( )SS: EBP+( )SS: EBP+( )SS: EBP+( )
@lògica elements del bloc d’activació
SS: EBP+( )
SS: EBP+( )SS: EBP+( )SS: EBP+( )SS: EBP+( )
Dibuixeu el bloc d’activació desubr3 i calculeu l’adreça deV respecte elEBP
@V=
Programeu les instruccions necessàries per fer el pas de paràmetres enret = s3 (M, tam_fila, param, V, ‘D’);
48
ue és
n índex
que els
.
a la
Activitat 3.D: Programar una subrutina sencera
En aquest darrera activitat heu de programar una subrutina recursiva i comprovar q
correcte. És la subrutinasubr4 que hi ha dins el programa de la figura 3.11.
El que fa aquesta subrutina és calcular el producte dels elements d’un vector que tenen u
comprès dins el rang establert per [i..j] (i,j són paràmetres de la funció). Es considera sempre
vectors començen amb l’índex 0, estan emmagatzemats com en C i es passen per referència
El programa principal ja el teniu codificat dins el fitxer SUBR4.ASM. Fa dues crides
subrutinasubr4 . La primera serveix per calcular el producte de tots els elements del vectorA. El
resultat ha de ser 144 (en decimal). La segona calcula el producte dels elementsB[2] a B[5] . El
resultat ha de ser -4.
Feu l’exercici 3.5.
figura 3.11: Programa considerar en l’activitat 4
// programa recursiufinal int N = 10;
int A[N] = {-2, -2, -1, -1, 1, 1, 2, 2, 3, 3};int B[N] = {1, 1, 1, 2, -1, 2, 3, 3, 3, 3};int res1,res2;
int subr4(int v[N], int i, int j) {int loc;
if (i==j) {loc = v[i];
} else {loc = v[i] * subr4(v,i+1,j);
}return loc;
}
void main() { res1 = subr4(A,0,N-1); res2 = subr4(B,2,5);}
exercici 3.5
Passeu a fer la programació de la subrutina dins el fitxer SUBR4.ASM icomproveu el resultat
49
. Poseu
de
ent a
Activitat Opcional: Mida de la pila
Feu els següents exercicis:
Determineu quina és la mida en bytes que es necessita per a la pila en el vostre programa
un punt d’aturada dins el cos de la subrutinasubr4 i aneu executant. Fixeu-vos en la diferència
valors del registre ESP entre el principi del programa i l’última activació que es faci correspon
la primera crida asubr4 del programa principal.
exercici 3.6
Contesteu a les següents preguntes:
• On assignareu la variable localloc ? A la pila o a un registre?:• Quants bytes ocuparà un bloc d’activació desubr4 ?:
(no compteu l’estat; no sabeu el que ocupa abans de programar)• Quin és el nombre màxim de blocs d’activació que hi haurà a la
pila per la primera crida que es fa dins el programa principal?• I per la segona crida?• Quants bytes hauria de tenir la pila per assegurar que no
es produeixi sobreeiximent?(hauríeu de considerar un marge de seguretat per a l’estat)
exercici 3.7
Poseu en la definició de la pila la mida que hagueu considerat convenient en l’exercicianterior.
exercici 3.8
Contesteu a la següent pregunta:
• Quants bytes han ocupat tots els blocs d’activació que hi ha hagutsimultàniament a la pila durant l’execució d’aquest programa?
50
a
isme
tecle-
Sessió 4: E/S per enquest
Objectius: Aprendre a escriure directament a la pantalla de l’IBM PC. Entendre el mecan
bàsic de sincronització per enquesta i construir programes senzills per llegir els caràcters
jats al PC.
Lectura prèvia
Us expliquem a continuació els aspectes bàsics del funcionament de la pantalla i del
teclat a l’IBM PC. Tots aquests aspectes seran importants en el desenvolupament de la sessió.
Per tenir una descripció més detallada d’aquests dispositius podeu consultar el document
“Descripció dels dispositius d’E/S en l’IBM PC”, d’Eduard Ayguadé, Agustín Fernández i
Antonio González, publicat pel CPET.
La pantalla de l’IBM PC
La pantalla en mode text es pot veure com una matriu de tipus C, amb 25 files (de la fila
0 a la 24) x 80 columnes (de la columna 0 a la 79). El controlador que governa la pantalla té
dos registres de 8 bits per cadascuna de les posicions de la pantalla. El processador ha
d’escriure en aquests registres la informació que vol que es vegi a la pantalla, tal i com
s’explica més endavant. Podem considerar doncs que el controlador té 25x80x2 registres de
dades. No té cap registre de estat ni de control.
La figura 4.1 mostra la correspondència entre els registres de dades del controlador i les
posicions de la pantalla. Els registres 0 i 1 corresponen a la posició (0,0) de la pantalla. Si
volem escriure un caràcter en aquesta posició llavors escriurem el codi ASCII del caràcter al
registre 0 i l’atribut al registre 1. L’atribut és un codi de 8 bits que indica la forma com s’ha de
visualitzar el caràcter. Els atributs que utilitzarem són:
00000111b 07h normal:fons negre,caràcter blanc
01110000b 70h invers:fons blanc,caràcter negre
Després d’escriure el codi ASCII i l’atribut als registres 0 i 1, el caràcter es veurà a la
posició (0,0) de la pantalla mentre no modifiquem el contingut d’aquests registres.
Cadascuna de les posicions de pantalla restants tenen associats un parell de registres,
amb una correspondència per files. És a dir, els registres 2 i 3 estan associats a la posició (0,1),
51
els registres 4 i 5 a la posició (0,2) i, en general, el registre (80*i+j)*2 conté el codi ASCII del
caràcter que s’ha de veure a la posició (i,j) i el registre (80*i+j)*2 + 1 conté l’atribut.
Els registres del controlador de pantalla estan mapejats a l’espai de memòria, a partir
d’una adreça que anomenem adreça base. Aquesta adreça base depèn de la tarjeta gràfica
què disposi el PC. En alguns PCs l’adreça base física és B0000h (l’adreça base lògica és
0B000h:0000 ). En altres PCs l’adreça base física és B8000h (l’adreça base lògica és
0B800h:0000 ). Quan el sistema operatiu DOS fa el boot, inicialitza una variable amb el mode
de vídeo de la tarjeta. En concret, el valor del byte de l’adreça de memòria 0000:0449h conté
informació sobre la tarjeta. Si el valor d’aquest byte és 7, l’adreça base és 0B0000h . Si el valor
és 2 o 3, llavors l’adreça és 0B8000h .
Vosaltres disposeu d’una rutina que consulta el byte de l’adreça de memòria
0000:0449h . La rutina es diu ADPANT, i retorna pel registre AX el valor del número de
segment on està mapejada la pantalla (0B000h o 0B800h ). El següent fragment de codi, per
exemple, escriu el caràcter ‘A’ , en mode normal, a la posició (0,0) de la pantalla.
0,00
1
24
i
0 1 79j
0,1
1,0 1,1
0,2 0,79
i,j
24,7924,0
0,0
0,1
0,2
0,79
1,0
1,1
i,j
24,79
0
(80*i+j)*2
123
158159
(80*i+j)*2+1
Registres del Controlador de Pantalla Posicions de la Pantalla
Codi ASCII del caràcter de la posició (0,0)
Atribut del caràcter de la posició (0,0)
figura 4.1: Correspondència entre els registres del controlador de la pantalla i les
posicions de la pantalla
52
CALL ADPANT ; deixa en AX el número de segment; de l’adreça base de pantalla
MOV ES, AX ;MOV ES:0,BYTE PTR ‘A’ ; escrivim el codi ASCII de la ‘A’
; al registre 0(desplaçament 0;respecte del segment que hi ha a ES)
MOV ES:1,BYTE PTR 07h ; escrivim l’atribut al registre 1
Per poder utilitzar la rutina ADPANT, els vostres programes han de declarar-la, abans de
la directiva .stack , de la següent manera:
extrn adpant: far
A més a més, haureu de muntar el vostre programa amb la comanda:
tlink /v/3 nom_fitxer,,,w:ourlib\libio.lib
53
El teclat de l’IBM PC
Amb un teclat qualsevol es poden fer dos tipus d’accions: polsar una tecla o deixar-la
anar. Quan polsem una tecla diem que s’ha produït un event de tipus MAKE. Quan deixem anar
una tecla diem que s’ha produït un event de tipus BREAK.
El controlador del teclat de l’IBM PC té dos registres de 8 bits cadascun: registre de
dades i registre d’estat (no té registre de control).
El registre d’estat ens indica quan s’ha produït un event. Concretament, quan es
produeix un event (MAKE o BREAK) el bit 0 del registre d’estat es posa a 1.
El registre de dades conté la informació necessària per identificar l’event que s’ha
produït. Tal i com es mostra a la figura 4.2, el bit 7 del registre de dades indica el tipus d’event
(si és 0 llavors és un MAKEi si és 1 llavors és un BREAK). Els 7 bits baixos del registre de dades
contenen el codi de rastreig. Aquest codi és un número que ens permet identificar la tecla que
s’ha polsat o que s’ha deixat anar. Cada tecla té associada un codi de rastreig diferent, d’acord
amb l’esquema que es mostra a la figura 4.3.
Per saber quin és el codi ASCII corresponent a un codi de rastreig determinat teniu a la
biblioteca de programes LIBIO.LIB la taula TTECLAT. La manera d’accedir-hi és la següent:
MOV BL, TTECLAT[EAX]
on en EAXheu de tenir el codi de rastreig (el bit de MAKE/BREAKha d’estar a zero) extès
a 32 bits. Aquesta instrucció deixa al registre BL el codi ASCII corresponent al codi de rastreig
CODIM/B
0: MAKE1: BREAK
figura 4.2: Configuració del registre de dades del teclat
1 59 60 61 62 63 64 6665 67 68 87 88 70. .
82 71 73
83 79 81
80
72
75 77
69 53 55 74
71 72 73
75 76 7778
79 80 81
82 8328
41 2 3 4 5 6 7 8 9 10 11 12 13 14
15 16 17 18 19 20 21 22 23 24
58 30 31 32 33 34 35 36 37 38
42
29
86 44 45 46 47
56
48 49 50 51
57
25 26 27
39 40 43
52 53
56 29
54
28
figura 4.3: Codi de rastreig associat a cadascuna de les tecles del teclat
54
que hi hagi en EAX. En cas de ser un caràcter, el codi que queda és el del caràcter EN
MAJÚSCULES.
Per poder fer servir la taula TTECLAT, l’heu de declarar, abans de la directiva .stack ,
de la següent manera:
extrn tteclat: byte
A més a més, haureu de muntar el vostre programa amb la comanda:
tlink /v/3 nom_fitxer,,,w:ourlib\libio.lib
Els registres del controlador de teclat estan mapejats a l’espai d’entrada/sortida.
Concretament, el registre d’estat és a l’adreça 64h i el de dades és a l’adreça 60h . Per
tant, aquests registres s’han de llegir amb la instrucció IN . Quan es llegeix el registre de dades,
el bit baix del registre d’estat es posa a 0 automàticament.
En general, per llegir el teclat per enquesta cal seguir els següents passos:
1 Inhibir les interrupcions del teclat (veure més endavant).
2 Consultar de forma contínua el registre d’estat fins que el bit 0 es posi a 1,
indicant que s’ha produït un event (MAKE o BREAK).
3 Llegir el registre de dades per determinar l’event que s’ha produït (això farà que
baixi el bit 0 del registre d’estat).
4 Determinar si l’event és un MAKE o un BREAK.
5 Obtenir, a partir del codi de rastreig, el codi ASCII corresponent a la tecla. En
aquest pas es farà servir la taula TTECLAT.
6 Restaurar les interrupcions del teclat.
Com hem vist abans, el teclat no té registre de control, que seria el lloc natural per inhibir
les interrupcions del teclat. Aquesta inhibició s’ha de fer directament al controlador
d’interrupcions, que s’explicarà en detall a la sessió 5. De moment, és suficient saber que per
inhibir les interrupcions del teclat s’ha de posar a 1 el bit 1 del registre de màscara del
controlador d’interrupcions. Aquest registre està mapejat a l’adreça 21h de l’espai d’entrada/
sortida. Per tant, el codi necessari per inhibir les interrupcions del teclat és:
CLIIN AL,21hOR AL,00000010bOUT 21h,ALSTIPer restaurar les interrupcions del teclat, s’ha de tornar a deixar un 0 al bit 1 del registre
de màscara.
55
e cada
e diu
manda
’ en
Enunciats de la pràctica
Consideracions generals
Tots els exercicis d’aquesta sessió escriuen alguna cosa a la pantalla. La prova qu
exercici està resolt correctament es fa comprovant que a la pantalla s’escriu el qu
l’enunciat. Per tant, no s’ha de fer servir el TD.
Abans d’executar cadascun dels programes d’aquesta sessió, s’ha d’executar la co
CLS del DOS. Aquesta comanda posarà la pantalla en blanc.
No us oblideu que els programes s’han de muntar amb la comanda:
tlink /v/3 nom_fitxer,,,w:ourlib\libio.lib
Activitat 4.A: Escriptures a la Pantalla
El fitxer ENQ1.ASM només conté les següents instruccions:
CALL ADPANT
MOV ES,AX
MOV ES:0,’A’
MOV ES:2,07H
Aquestes instruccions pretenen escriure a la posició [0,0] de la pantalla la lletra ‘A
mode normal.
exercici 4.1
Poseu a punt aquest codi, fent que funcioni tal i com s’espera. Anoteu acontinuació tots els errors que tenia el codi original (tant d’assemblatge comd’execució).
56
exercici 4.2
Feu una segona versió del codi que faci exactament el mateix que el del’exercici 4.1, però que només tingui tres instruccions. Anoteu a continuacióles tres instruccions.
exercici 4.3
Escriviu a continuació les instruccions necessàries per escriure a la posició[5,7] de la pantalla la lletra ‘A’ en mode invers. Afegiu el codi al programa icomproveu si funciona.
exercici 4.4
Escriviu a continuació les instruccions necessàries per escriure la lletra ‘A’en mode normal en totes les posicions de la pantalla entre la [5,7] i la [6,7](ambdues incloses). Afegiu el codi al programa i comproveu que funciona. Ushauria de sortir un codi amb un bucle amb no més de 4 instruccions.
57
, s’han
es al
Activitat 4.B: Lectura del teclat per enquesta
En els apartats següents s’ha de treballar amb el teclat per enquesta. Per tant
d’inhibir les interrupcions del teclat, tal i com s’ha explicat a la lectura prèvia, i restaurar-l
final del programa.
Treballarem amb el fitxerENQ2.ASM.
exercici 4.5
Escriviu a continuació el codi necessari per escriure la lletra ‘A’ a la posició[5,7] just en el moment que es produeixi un event de teclat (un MAKE o unBREAK). Incorporeu el codi al programa.
Tanmateix, el programa acaba de seguida. Doneu una explicació d’aquestcomportament.
exercici 4.6
Modifiqueu el codi de l’exercici 4.5 per tal que el caràcter ‘A’ s’escrigui quanes produexi el primer MAKE.
58
Exercicis opcionals
exercici 4.7
Escriviu a continuació el codi necessari per escriure a la posició [5,7] de lapantalla el primer caràcter teclejat, just en el moment que es produeixi elMAKE. Incorporeu el codi al programa.
exercici 4.8
Feu un programa que escrigui, a partir de la posició [22,7] de la pantalla, totsels caràcters teclejats (en el moment del MAKE). El programa ha d’acabarquan es teclegi la lletra ‘F’.
exercici 4.9
Feu una versió del codi de l’exercici 4.8, en un altre fitxer anomenatENQ3.ASM, eliminant la part d’inhibir i desinhibir interrupcions de teclat.Executeu el programa resultant. Descriviu a continuació les diferències entreel comportament d’aquest codi i el de l’exercici 4.8. Si no observeudiferències, passeu al següent apartat.
59
exercici 4.10
Modifiqueu els codis dels exercicis 4.8 i 4.9 de forma que, abans de ferl’operació d’enquesta del teclat:
enquesta: IN AL, 64h. . .
s’executi un bucle que simplement doni N voltes. Afegiu entre l’etiquetad’enquesta i la lectura del registre d’estat el següent:
enquesta: MOV ECX,Nespera: DEC ECX
JNZ esperaIN AL, 64h...
Compareu el comportament dels dos codis quan N=1 i N=1000. Què passaquan N es fa gran ? Doneu una explicació d’aquest comportament.
exercici 4.11
Feu un programa que escrigui en pantalla el caràcter teclejat, per a cadaevent del teclat que es produeixi. Si és un MAKE, que l’escrigui a partir de laposició [22,7] en mode normal. Si és un BREAK, que l’escrigui a partir de laposició [23,7], en mode invers. El programa ha d’acabar quan es teclegi lalletra ‘F’.
60
rames
taura-
etc.
Sessió 5: E/S per interrupcions
Objectius: Entendre el mecanisme bàsic de sincronització per interrupcions, a través de prog
senzills que en mostrin el funcionament. Aprendre a escriure correctament la inicialització i res
ció del vector d’interrupcions, la programació d’una rsi, l’ús de les variables de sincronització,
Lectura prèvia
En aquesta sessió aprendrem a fer petits programes en llenguatge assemblador que se
sincronitzen per interrupcions amb els dispositius de rellotge i de teclat, i escriuen els seus resultats
a la pantalla. A continuació trobareu un resum dels punts clau a tenir en compte per a programar
per interrupcions els dispositius del PC. Per a una descripció més detallada d’aquests dispositius
podeu consultar el document “Descripció dels dispositius d’E/S en l’IBM PC” d’Eduard Ayguadé,
Agustín Fernández i Antonio González, publicat pel CPET.
Les interrupcions, i el controlador d’interrupcions
Gestionar la sincronització de l’E/S per interrupcions significa que quan un dispositiu
requereix l’atenció, envia un senyal de petició al processador, provocant que s’interrompi el
programa en execució i passi a executar-se el codi d’una rutina rsi, que és una subrutina específica
per atendre aquesta petició. Així és com funciona normalment el teclat i el rellotge en el PC.
L’IBM PC té un controlador d’interrupcions, l’i8259, encarregat de gestionar les interrupcions
produïdes pels perifèrics del sistema. Bàsicament, aquest controlador rep les peticions
d’interrupció dels perifèrics, decideix en cada instant quina d’elles ha de ser atesa, interromp al
processador, i li envia l’identificador que correspon al dispositiu que ha de ser atès.
Els dispositius generen les peticions d’interrupció activant els senyals IRQx (interruption
request) connectats a l’i8259. Per exemple, el rellotge activa IRQ0, mentre que el teclat activa
IRQ1. El controlador disposa d’un registre de màscara IMR (port 21H) de 8 bits, que permet inhibir
selectivament cada un dels senyals de petició: si el bit i del IMR val 1, les peticions que arribin per
la línia IRQi seran ignorades. Si arriba més d’una petició simultàniament, el controlador selecciona
la més prioritària (número de petició més petit indica prioritat més alta: IRQ0 > IRQ1). A més a més,
aquest controlador és multinivell, de manera que una rsi en curs pot ser interrompuda si arriba una
nova petició d’un altre dispositiu. No obstant, aquesta petició sols serà atesa pel controlador si el
corresponent dispositiu té major prioritat que el dispositiu servit per l’rsi.
Després de detectar el senyal d’interrupció, el processador notifica al controlador que està
disposat a atendre la petició, i el controlador respon enviant-li un identificador id del dispositiu (id=8
61
en el cas del rellotge, id=9 en el cas del teclat). Llavors, el processador esbrina l’adreça de la rsi
que li correspon consultant el id-èssim element del vector d’interrupcions, el qual es troba ubicat a
l’adreça física 0 de la memòria, i se suposa correctament inicialitzat. Seguidament, el processador
salva a la pila els registres FLAGS, CS i IP, i fa un salt a la rsi (CS:IP pren per valor l’adreça de la
rsi, i el flag IF es posa a 0).
Les rutines de servei d’interrupció (rsi)
Una rsi ha de seguir les normes elementals de programació de subrutines: salvar el valor
anterior de tots els registres que modifiqui, i restaurar-los abans de finalitzar, ja que poden contenir
informació vital per al programa interromput. Però a més, ha de tenir en compte alguns aspectes
addicionals:
• Una rsi no ha de ser invocada amb instruccions CALL, sinó que s’activa en un instant
impredecible, a instàncies d’un event extern. Per tant, tampoc no rep cap paràmetre, ni
per la pila ni per registre.
• En general, en un computador es pot executar més d’un programa al mateix temps, i
llavors una rsi pot interrompre qualsevol d’aquests programes. Per tant, la rsi no coneix
en absolut el significat dels valors dels registres del programa interromput (o almenys
no en té cap garantia). Per tant, les úniques dades que pot manipular són les variables
globals del programa principal que l’ha instal.lat: búffers, comptadors, variables
booleanes de sincronització, etc.
• Just abans de retornar, la rsi ha de notificar-ho al controlador d’interrupcions, per tal que
aquest gestioni correctament les prioritats de les interrupcions multinivell. Això es fa per
mitjà de la comanda eoi (end of interruption). Executar un eoi consisteix a escriure en el
registre de control del controlador d’interrupcions (adreça 20H) el valor 20H (figura 5.1).
• Finalment, per retornar de la rsi cal fer-ho amb una instrucció especial: IRET. Si hem
programat bé la rsi, en aquest punt el cim de la pila hauria de contenir els valors de IP,
CS i FLAGS salvats en el moment de la interrupció (comproveu que desapileu tants
bytes com hàgiu apilat!). La instrucció IRET els restaura, de forma que causarà un salt
al punt on s’havia produït la interrupció.
El teclat
Aquest dispositiu ja s’ha estudiat a la sessió anterior. Sols ens cal afegir que:
• interromp per a qualsevol event, sigui make (polsació) o break (alliberament).
mov al, 20hout 20h, al
figura 5.1: La comanda eoi
62
• sol.licita les interrupcions activant el senyal IRQ1
• identificador = 9
• la petició es manté activa fins que el processador llegeix el registre de dades del teclat
(adreça 60H). Per tant, convé assegurar-se que dins la rsi de teclat es faci sempre la
lectura d’aquest registre, per tal de desactivar la petició, encara que no ens interessi el
seu contingut, escrivint: in al,60h
El rellotge
L’IBM-PC té tres temporitzadors programables, integrats en el xip i8253, que serveixen per a
diversos propòsits. Estudiarem un d’aquests temporitzadors (TIMER0), que aquí anomenarem
simplement el rellotge (no el confongueu amb el senyal de rellotge que serveix per seqüenciar els
circuits del processador!). Aquest dispositiu no produeix entrades ni sortides de dades. Tan sols
produeix peticions d’interrupció a intervals regulars, per tal que el sistema tingui noció del temps
real transcorregut. Molts dels seus modes de funcionament, així com el període, són programables,
encara que en aquest curs sols considerarem el seu funcionament bàsic, establert per defecte
quan s’engega el sistema MS-DOS, de la següent manera:
• interromp 18.2 vegades per segon, (però podem considerar 18 interrupcions/s)
• sol.licita les interrupcions activant el senyal IRQ0 (és, per tant, el més prioritari).
• identificador = 8
El programa principal
A diferència de l’E/S per enquesta, no hem d’inhibir les interrupcions, com és obvi. Però el
que sí que hem de fer és modificar el vector d’interrupcions.
El vector d’interrupcions de l’IBM-PC és un vector de tipus C, de punters (2 bytes de
desplaçament i 2 de segment) que es troba sempre a partir de l’adreça física de memòria = 0
(adreça lògica 0000:0000). El i-èssim element del vector conté l’adreça (diem que “apunta”) a la rsi
del dispositiu amb identificador = i (ja hem vist que i=8 per al rellotge, i que i=9 per al teclat).
Si volem treballar amb un dispositiu per interrupcions, haurem ja escrit una subrutina rsi
específica per a aquest dispositiu, i llavors cal que modifiquem l’element corresponent del vector
d’interrupcions per tal que apunti a la nostra rutina. Abans de modificar aquest element, no obstant,
és important guardar el valor anterior per tal que el poguem restaurar abans d’acabar el programa.
Suposem, per exemple, que escrivim la rsi anomenada rellotge_meu per al rellotge, i que
hem declarat una variable adr_vella dd ? per a guardar el valor anterior. Llavors, al principi del
programa principal, haurem de modificar el vector com indica la figura 5.2.
Per restaurar el vector d’interrupcions, al final del programa principal, sols cal desfer les
accions del codi anterior (veure figura 5.3).
63
Fixeu-vos que en el fragment de la figura 5.2 inhibim les interrupcions quan modifiquem el
vector (CLI ), per impedir que es produeixi una interrupció entremig dels dos mov. Si succeís això, el
processador llegiria en el vector una adreça a mig actualitzar, incorrecta. En el fragment de la figura
5.3 en canvi, no cal inhibir les interrupcions ja que modifiquem el vector amb un sol mov.
Inicialització del registre DS dins una rsi
Cada cop que es fa un accés a memòria s’utilitza un registre de segment. Si s’accedeix a les
variables globals s’utilitza el registre DS per defecte. De la mateixa manera que s’ha dit
anteriorment que no es pot pressuposar cap valor dins una rsi dels registres que s’utilitzen en el
programa principal, tampoc s’ha de pressuposar que el registre DS està apuntant al segment de
dades.
Encara que en el programa hi hagi un únic segment de dades, la resta de rsi que té el sistema
operatiu sí que el poden variar per accedir a les seves variables globals. El comportament erroni es
produiria si la nostra rsi interromp una altra rsi del sistema operatiu que ha canviat el valor del
registre DS. Si això succeeix, quan la rsi accedeixi a la variable, estarà usant el desplaçament
correcte però el segment possiblement canviat, provocant el desastre. En el moment de la
interrupció, el processador inicialitza CS i IP amb l’adreça del segment de codi de la rsi, ja que
l’obté del vector d’interrupcions, però en canvi, no pot inicialitzar l’adreça del segment de dades de
la rsi. En general, serà necessari que el valor del segment de dades quedi inclòs en el propi codi,
en forma reubicable. TASM disposa de la directiva @DATA que permet fer això, com en el següent
exemple:
mov ax, @data ;el segment queda codificat com un immediatmov ds, ax ;inicialitzem DS per accedir a memòria
mov ax, 0mov es, ax ; el vector comença en 0000:0000mov eax, es:[8*4] ; salvo l’adreça vellamov adr_vella, eaxlea ax, rellotge_meu ; en ax tinc el desplaçamentcli ; inhibim les interrupcionsmov es:[8*4], ax ; en i*4 guardem el desplaçamentmov es:[8*4+2], cs ; en i*4+2 el segmentsti ; ja podem permetre interrupcions
figura 5.2: Instruccions per inicialitzar el vector d’interrupcions.
mov ax, 0mov es, axmov eax, adr_vellamov es:[8*4], eax
figura 5.3: Instruccions per restaurar el vector d’interrupcions.
64
cions
aurem
er saber
manda
codis
un
mar un
a difícil
essador
ús del
que el
és la
).
er,
Enunciats de la sessió
En aquesta sessió modificarem el vector d’interrupcions per tal de controlar les interrup
del rellotge i el teclat. Per aquest motiu, no usarem el TD per depurar els programes, sinó que h
d’executar el programa, observar atentament com respon, i interpretar el seu comportament p
on falla. Abans d’executar cadascun dels programes d’aquesta sessió, s’ha d’executar la co
CLS de DOS, per posar la pantalla en blanc.
Al igual que en la sessió anterior, en algunes activitats s’utilitza la taula de traducció de
de rastreigTTECLATi la subrutina per determinar el segment del búffer de pantallaADPANT, les quals
estan definides en un mòdul extern. Per tant, la comanda per muntar cada programa és:
tlink /v/3 nom_fitxer,,,w:ourlib\libio.lib
Activitat 5.A: Modificar el vector d’interrupcions
En aquesta primera Activitat aprendrem a programar el vector d’interrupcions. Volem
programa que s’esperi durant 5 segons i llavors acabi. Segurament podríem pensar a progra
bucle amb el nombre suficient d’iteracions perquè transcorreguessin els 5 segons, però result
calcular el nombre precís d’iteracions, a part que si més tard executem el programa en un proc
més ràpid o més lent, el retard variarà. La manera correcta de programar un retard fix és fer
dispositiu de rellotge, el qual proporciona una noció precisa i portable del temps real: cada cop
rellotge produeixi una interrupció, incrementarem un comptador, i el lloc apropiat per fer-ho
corresponent rsi, que hem anomenatrellotge (vegeu figura 5.4).
Aquesta rutina posa la variable globalfinal a CERTquan el comptador val 18*5 (5 segons
Trobareu el codi de la rsi al fitxerINTER1.ASM. Es demana que, aprofitant aquest mateix fitx
rellotge procmov ax, @datamov ds, axinc ticscmp tics, 18*5jne rel_sortirmov final, CERT
rel_sortir:mov al, 20hout 20h, aliret
rellotge endp
figura 5.4: Rutina rsi del rellotge
65
n ja us
escriviu el programa principal (a continuació de l’.startup ) seguint l’esquema de la figura 5.5.
Assembleu i munteu el programa. Executeu-lo directament des del prompt del DOS. Qua
funcioni el programa (ha d’esperar 5 segons i acabar), feu l’exercici 5.3.
Executeu el programa amb la nova rsi corregida. Verifiqueu que funciona bé.
MOV tics, 0;MOV final, FALS;... ; salvar_adreça_vella_vector... ; modificar_adreça_vectorbucle:
CMP final, FALSJE bucle
... ; restaurar_adreça_vella_vector
figura 5.5: Esquema incomplet del programa principal
exercici 5.1
Al codi de la rsi hi hem deixat anar un error, que hauríeu de corregir. Per descobrir-lo,re-escriviu el bucle d’espera del programa principalexactament com el següent:
bucle:MOV AL, finalCMP AL, FALSJE bucle
Executeu el programa. Veureu que ja no funciona. Per què? Escriviu la rsi correcta.
66
itiu ja
odueixi
l
Activitat 5.B: El teclat
Ara treballarem amb el dispositiu del teclat. Recordeu que la descripció d’aquest dispos
va ser descrita a la lectura prèvia de la sessió 4. Volem fer un programa que acabi quan es pr
la primera interrupció del teclat, de tipusmake. Completeu l’exercici 5.2.
Copieu la rsi en el mateix fitxerINTER1.ASM de l’activitat anterior. Després modifiqueu e
programa principal (recordeu que l’identificador del teclat és el 9).
exercici 5.2
Escriviu aquí el codi de la rsi de teclat
67
lotge,
de
sol
n
scriu el
àcters
cions.
Activitat 5.C: Rellotge i pantalla
A continuació escriurem un programa un xic més elaborat, que treballarà amb el rel
com en la primera activitat. Utilitzarem el fitxerINTER2.ASM, que conté, ja declarat en el segment
dades, el vector de caràctersstring , de tipus C. Els caràcters estan codificats en ASCII (ocupen 1
byte), i l’últim de tots és un byte 0 (zero).
Feu un programa que escrigui la cadenastring a partir de la posició (2,0) de la pantalla, e
vídeo invers, de forma que passin 0.5 segons (9 tics) des que s’escriu un caràcter fins que s’e
següent. Recordeu com es fa per determinar on està mapejada la pantalla:
call adpantmov es, ax
El programa el plantejarem segons indica la figura 5.6, és a dir, que volem que els car
s’escriguin des de la rsi. El programa principal ja està escrit en el fitxer, i no necessita modifica
Sols heu d’escriure la rsi del rellotge.
Executeu el programa i verifiqueu que funciona bé.
tics++;if (tics==9) {
if (!es_fi_cadena) {tics=0;... // escr_seguent_caracter
}else {
final=CERT;}
}
(rsi rellotge) (prog. principal)
final=FALS;tics=0;while (!final) {};
figura 5.6: Plantejament per resoldre l’activitat 5.C.
68
lejats
n hagi
cordeu
tejar
mbé en
criguin a
Activitat 5.D: Rellotge, pantalla i teclat
A continuació farem un refinament al programa anterior, en el mateix fitxerINTER2.ASM. Feu
les 2 modificacions a l’hora abans de provar qualsevol de les dues per separat:
Modificació 1) Volem que el programa escrigui a partir de la posició (7,0) els caràcters tec
(en el moment delmake).
Modificació 2) També volem que quan la rsi de rellotge trobi el final de la cadenastring , en
comptes de finalitzar el programa, torni a començar a escriure la cadena, a continuació d’o
acabat d’escriure a pantalla l’anterior cadena. El programa acabarà quan es polsi la tecla ‘F’. Re
com es fa per traduir un codi de rastreig al corresponent caràcter ASCII :
movzx eax, almov bl, tteclat[eax]
De forma similar a l’activitat anterior, l’algorisme que pinta les tecles polsades es pot plan
de dues maneres: o bé escrivint-les des del programa principal, o bé des de la rsi del teclat. I ta
aquest cas volem que se segueixi el segon plantejament, és a dir que les tecles polsades s’es
la pantallades de la rsi de teclat.
Executeu el programa i verifiqueu que funciona correctament.
69
erò no
gueix
crivint la
n
.3.
Activitat opcional: Accés al segment de dades des de la Rsi
Imagineu què passaria en el programa anterior si restaurem les interrupcions del teclat p
les del rellotge. La idea és la següent: si el programa acaba, i el vector d’interrupcions se
apuntant a la nostra rutina de rellotge, potser aquesta se seguirà executant i doncs, seguirà es
cadenastring a la pantalla (o no?). Sobre el mateix fitxerINTER2.ASM, poseu entre comentaris (u
’;’ a principi de línia) les instruccions que restauren la interrupció del rellotge, i feu l’exercici 5
exercici 5.3
Executeu el programa. Premeu la tecla ’F’ per acabar. Se segueix escrivint lafrase string en pantalla ?
Mentre el rellotge segueix escrivint la cadenastring en pantalla, executeucomandes com CLS o DIR. Executeu també TASM (sense arguments). Quèsucceeix ? Com expliques el diferent comportament en cada cas ?
70
grama
ercici
rior !)
uen els
grama
,79),
a ’F’.
Activitat opcional: Una tasca independent per al programa principal
En aquesta activitat farem algunes modificacions al programa anterior per tal que el pro
principal executi una tasca diferent de les del rellotge i el teclat. Abans de seguir, resoleu l’ex
5.4.
A continuació feu les següents modificacions, en el mateix fitxerINTER2.ASM: En primer lloc,
restaureu correctament les interrupcions (heu de treure els ’;’ que heu afegit a l’activitat ante
Mentre s’escriu un caràcter de la cadena cada 0.5 segons a partir de la posició (2,0), i s’escri
caràcters del teclat a la pantalla a partir de la posició (7,0), volem que cada 5 segons el pro
principal canvïi l’atribut de totes les posicions de la pantalla, des de la (0,0) fins a la (24
intercanviant l’atribut normal per invers i viceversa. El programa acabarà quan es polsi la tecl
Executeu el programa i verifiqueu que funciona.
exercici 5.4
Suposem que AL conté l’atribut d’una posició de la pantalla, i aquest val 07h(normal) o bé 70h (invers). Escriviu un codi que canvïi l’atribut de AL denormal a invers i viceversa, amb 1 sola instrucció.
71