vezba 9 - celobrojna aritmetika
Post on 15-Jan-2016
74 Views
Preview:
DESCRIPTION
TRANSCRIPT
1
9.
CELOBROJNA ARITMETIKA
9.1 Instrukcije pomeranja i rotacije
Instrukcije pomeranja su jedna od glavnih karakteristika asemblerskog jezika. Pomeranje
označava pomeranje bitova levo ili desno unutar operanda. U tabeli 1 su prikazane instrukcije
pomeranja, koje sve utiču na Overflow i Carry flegove.
Tabela 1. Instrukcije pomeranja i rotacije
Mnemonik Opis
SHL Shift left (pomeraj ulevo)
SHR Shift right (pomeraj udesno)
SAL Shift arithmetic left (aritmetičko pomeranje ulevo)
SAR Shift arithmetic right (aritmetičko pomeranje udesno)
ROL Rotate left (rotacija ulevo)
ROR Rotate right (rotacija udesno)
RCL Rotate carry left (rotacija sa prenosom ulevo)
RCR Rotate carry right (rotacija sa prenosom udesno)
SHLD Double-precision shift left
SHRD Double-precision shift right
9.1.1 Logičko i aritmetičko pomeranje
Postoje dva načina za pomeranje bitova unutar operanda. Logičko pomeranje upisuje nule
na novokreirane pozicije. Na sledećoj slici, bajt se logički pomera za jednu poziciju udesno.
Drugim rečima, svaki bit se pomera na sledeću nižu poziciju. Bitu 7 se dodeljuje vrednost 0.
Sledeća ilustracija prikazuje jedno logičko pomeranje udesno binarne vrednosti
11001111, čime se dobija 01100111. Bit najmanje težine se premešta u Carry fleg:
2
Drugi tip pomeranja je aritmetičko pomeranje. Novokreirane pozicije se popunjavaju sa
kopijom originalnog bita znaka:
Na primer, binarna vrednost 11001111, ima 1 kao bit znaka. Kada se aritmetički pomeri
za jedan bit udesno, postaće 11100111:
9.1.2 Instrukcija SHL
Instrukcija SHL vrši logičko pomeranje ulevo odredišnog operanda, popunjavajući
najniži bit sa nulom. Bit najveće težine se premešta u Carry fleg, a bit koji je bio u Carry flegu
se odbacuje:
Ako pomerite ulevo za jedno mesto broj 11001111, dobija se 10011110:
Sintaksa SHL instrukcije je:
SHL odredište, broj
gde broj označava za koliko mesta se vrednost pomera ulevo.
Instrukcija dozvoljava sledeće tipove operanada:
SHL reg,imm8
SHL mem,imm8
SHL reg,CL
SHL mem,CL
Procesori x86 dozvoljavaju da imm8 bude bilo koji ceo broj od 0 do 255. Alternativno,
registar CL može sadržati broj pomeranja. Ovi formati važe i za SHR, SAL, SAR, ROR, ROL.
Primer
U sledećim instrukcijama, BL se pomera jedanput ulevo. Najviši bit se kopira u Carry
fleg, a najniži postaje 0:
3
mov bl,8Fh ; BL = 10001111b
shl bl,1 ; CF = 1, BL = 00011110b
Višestruko pomeranje
Kada se vrednost pomera ulevo više puta, Carry fleg sadrži poslednji bit koji je pomeren
sa MSB pozicije. U sledećem primeru, bit 7 ne završava u Carry flegu, jer ga zamenjuje bit 6
(nula):
mov al,10000000b
shl al,2 ; CF = 0, AL = 00000000b
Na sličan način, kada se vrednost pomera udesno više puta, Carry fleg sadrži poslednji
bit koji je bio pomeren sa LSB pozicije.
Bitsko množenje
SHL može da vrši množenje sa stepenima dvojke. Pomeranje nekog operanda ulevo za n
bitova daje proizvod tog operanda sa 2n. Na primer, pomeranje broja 5 ulevo za 1 bit, daje 10:
mov dl,5
shl dl,1
Ako se binarni broj 00001010 (decimalno 10) pomeri ulevo za dva mesta, dobija se 40
(10 x 22):
mov dl,10 ; pre: 00001010
shl dl,2 ; posle: 00101000
9.1.3 Instrukcija SHR
Ova instrukcija vrši logičko pomeranje udesno odredišnog operanda, menjajući najviši
bit sa nulom. Najniži bit se kopira u Carry fleg, a bit koji je pre toga bio u Carry flegu je
izgubljen:
SHR koristi isti format instrukcija kao SHL. U sledećem primeru, 0 iz najnižeg bita u AL
se kopira u Carry fleg, a najviši bit u AL postaje 0:
mov al,0D0h ; AL = 11010000b
shr al,1 ; AL = 01101000b, CF = 0
Višestruko pomeranje
U ovom slučaju, poslednji bit koji je bio na LSB poziciji se premešta u Carry fleg:
4
mov al,00000010b
shr al,2 ; AL = 00000000b, CF = 1
Bitsko deljenje
Logičkim pomeranjem neoznačenog operanda udesno za n bitova, dobija se količnik tog
operanda sa 2n. U sledećim iskazima, delimo 32 sa 2 i dobija se 16:
mov dl,32
shr dl,1
U sledećem primeru, 64 se deli sa 23:
mov al,01000000b ; AL = 64
shr al,3 ; deljenje sa 8, AL = 00001000b
Deljenje označenih brojeva pomoću pomeranja se dobija koristeći SAR instrukciju, zato
što ona čuva bit znaka.
9.1.4 Instrukcije SAL i SAR
SAL instrukcija radi isto kao i SHL instrukcija. SAL pomera svaki bit u odredišnom
operandu za određeni broj mesta ulevo. Najnižem bitu se dodeljuje 0. Najviši bit se premešta u
Carry fleg, a bit koji je već bio u Carry flegu se odbacuje:
Ako se broj 11001111 pomeri ulevo za jedan bit, postaje 10011110:
Instrukcija SAR vrši aritmetičko pomeranje udesno odredišnog operanda:
Operandi instrukcija SAL i SAR su isti kao i za SHL i SHR. Pomeranje se može ponavljati
veći broj puta, na osnovu drugog operanda u instrukciji:
SAR odredište, broj
Sledeći primer pokazuje kako SAR duplicira bit znaka. AL je negativan pre i posle
pomeranja udesno:
5
mov al,0F0h ; AL = 11110000b (-16)
sar al,1 ; AL = 11111000b (-8), CF = 0
Označeno deljenje
Možete podeliti označeni operand sa stepenom broja 2 koristeći SAR instrukciju. U
sledećem primeru, -128 se deli sa 23. Rezultat je -16:
mov dl,-128 ; DL = 10000000b
sar dl,3 ; DL = 11110000b
Proširivanje znakom registra AX u EAX
Pretpostavimo da AX sadrži označeni ceo broj i da želite da ga proširite znakom i smestite
u EAX. Najpre se EAX pomera 16 bitova ulevo, a potom se aritmetički pomera za 16 bitova
udesno:
mov ax,-128 ; EAX = ????FF80h
shl eax,16 ; EAX = FF800000h
sar eax,16 ; EAX = FFFFFF80h
9.1.5 Instrukcija ROL
Instrukcija ROL pomera svaki bit ulevo. Najviši bit se kopira u Carry fleg i na poziciju
najnižeg bita. Format instrukcije je isti kao i za SHL.
Rotacijom se ne gube bitovi. Bit sa jedne strane se rotiranjem pojavljuje na drugoj strani.
Primetite u sledećem primeru kako se najviši bit kopira i u Carry fleg i na poziciju 0:
mov al,40h ; AL = 01000000b
rol al,1 ; AL = 10000000b, CF = 0
rol al,1 ; AL = 00000001b, CF = 1
rol al,1 ; AL = 00000010b, CF = 0
Višestruke rotacije
Kada se koristi broj rotacija veći od 1, Carry fleg sadrži poslednji bit koji je izrotiran sa
MSB pozicije:
mov al,00100000b
rol al,3 ; CF = 1, AL = 00000001b
Zamena grupe bitova
Instrukcija ROL se može koristiti za zamenu gornje (bitovi 4-7) i donje (bitovi 0-3)
polovine bajta. Na primer, rotiranjem broja 26h za četiri mesta u bilo kom smeru, dobija se 62h:
mov al,26h
6
rol al,4 ; AL = 62h
Kada se rotira ceo broj od više bajtova za 4 bita, dobija se pomeranje svake
heksadecimalne cifre za jednu poziciju ulevo ili udesno. Na primer, ovde je neprekidno rotiran
broj 6A4Bh ulevo za 4 bita, a na kraju je dobijena originalna vrednost:
mov ax,6A4Bh
rol ax,4 ; AX = A4B6h
rol ax,4 ; AX = 4B6Ah
rol ax,4 ; AX = B6A4h
rol ax,4 ; AX = 6A4Bh
9.1.6 Instrukcija ROR
Ova instrukcija pomera svaki bit udesno i kopira najniži bit u Carry fleg i bit najviše
pozicije. Format instrukcije je isti kao i za SHL:
U sledećim primerima, primetite kako se najniži bit kopira i u Carry fleg i u najviši bit
rezultata:
mov al,01h ; AL = 00000001b
ror al,1 ; AL = 10000000b, CF = 1
ror al,1 ; AL = 01000000b, CF = 0
Višestruka rotacija
Kada se koristi višestruka rotacija, Carry fleg sadrži poslednji bit koji je pomeren sa LSB
pozicije:
mov al,00000100b
ror al,3 ; AL = 10000000b, CF = 1
9.1.7 Instrukcije RCL i RCR
Instrukcija RCL pomera svaki bit ulevo, kopira Carry fleg u LSB i kopira MSB u Carry
fleg:
Ako posmatramo Carry fleg kao dodatni bit koji je dodat na krajnji bit operanda, RCL se
ponaša kao ROL instrukcija. U sledećem primeru, CLC instrukcija briše Carry fleg. Prva RCL
instrukcija premešta najviši bit iz BL u Carry fleg i pomera ostale bitove ulevo. Druga RCL
instrukcija premešta Carry fleg u najniži bit i pomera ostale bitove ulevo:
clc ; CF = 0
7
mov bl,88h ; CF,BL = 0 10001000b
rcl bl,1 ; CF,BL = 1 00010000b
rcl bl,1 ; CF,BL = 0 00100001b
Restauracija bita iz Carry flega
RCL može da vrati bit koji je prethodno bio premešten pomeranjem u Carry fleg. U
sledećem primeru se proverava najniži bit promenljive testval tako što se pomera njen najniži
bit u Carry fleg. Ako je najniži bit jednak 1, dolazi do skoka. U suprotnom, RCL vraća broj na
originalnu vrednost:
.data
testval BYTE 01101010b
.code
shr testval,1 ; pomeri LSB u Carry fleg
jc exit ; izađi ako je Carry fleg setovan
rcl testval,1 ; u suprotnom vrati broj na staro
Instrukcija RCR
Ova instrukcija pomera svaki bit udesno, kopira Carry fleg u MSB i kopira LSB u Carry
fleg:
Kao i u slučaju sa RCL, pomaže da se zamisli ceo broj na ovoj slici kao 9-bitna vrednost,
sa Carry flegom sa desne strane LSB-a.
U sledećem primeru se koristi STC kako bi se setovao Carry fleg. Potom se vrši RCR
operacija nad AH registrom:
stc ; CF = 1
mov ah,10h ; AH, CF = 00010000 1
rcr ah,1 ; AH, CF = 10001000 0
Prekoračenje označenih vrednosti
Overflow fleg se setuje kada se pomera ili rotira označeni ceo broj za jednu poziciju i
dobije se vrednost van opsega označenih brojeva odredišnog operanda. Drugim rečima, znak
broja biva promenjen. U sledećem primeru, pozitivan ceo broj (+127), smešten u 8-bitnom
registru, postaje negativan broj (-2) kada se rotira ulevo:
mov al,+127 ; AL = 01111111b
rol al,1 ; OF = 1, AL = 11111110b
Slično, kada se -128 rotira za jednu poziciju udesno, setuje se Overflow fleg. Rezultat u
AL (+64) ima suprotan znak:
8
mov al,-128 ; AL = 10000000b
shr al,1 ; OF = 1, AL = 01000000b
Vrednost Overflow flega je nedefinisana kada se pomera ili rotira za više od jednog mesta.
9.1.8 Instrukcije SHLD i SHRD
SHLD instrukcija pomera odredišni operand za određen broj mesta ulevo. Upražnjene
pozicije se popunjavaju sa najvišim bitovima izvorišnog operanda. Izvorišni operand se ne
menja, ali utiče se na Sign, Zero, Parity, Carry i Auxiliary Carry flegove:
SHLD odredište, izvorište, broj
Na sledećoj slici je prikazano izvršavanje SHLD instrukcije sa pomerajem od jednog
mesta. Najviši bit izvorišnog operanda se kopira u najniži bit odredišnog operanda. Svi bitovi
odredišnog operanda se pomeraju ulevo:
Instrukcija SHRD pomera odredišni operand za određeni broj pozicija udesno.
Upražnjene pozicije se popunjavaju najnižim bitovima izvorišnog operanda:
SHRD odredište, izvorište, broj
Na sledećoj slici je prikazao izvršavanje SHRD instrukcije sa pomerajem od jednog
mesta:
Sledeći formati instrukcija se mogu primeniti i na SHLD i SHRD. Odredišni operand
može biti registarski ili memorijski, a izvorišni operand mora biti registarski. Operand broj
može biti CL registar ili 8-bitni neposredni operand:
SHLD reg16,reg16,CL/imm8
SHLD mem16,reg16,CL/imm8
SHLD reg32,reg32,CL/imm8
9
SHLD mem32,reg32,CL/imm8
Primer 1
Sledeći iskazi pomeraju wval ulevo za 4 bita i ubacuju najviša 4 bita AX registra u najniže
4 pozicije wval promenljive:
.data
wval WORD 9BA6h
.code
mov ax,0AC36h
shld wval,ax,4 ; wval = BA6Ah
Kretanje podataka je prikazano na sledećoj slici:
Primer 2
U sledećem primeru, AX se pomera udesno za 4 bita, a najniža 4 bita registra DX se
pomeraju u najviše 4 pozicije registra AX:
mov ax,234Bh
mov dx,7654h
shrd ax,dx,4 ; AX = 4234h
SHLD i SHRD se mogu koristiti za manipulaciju bitmapiranim slikama, kada se grupa
bitova mora pomeriti ulevo ili udesno za pozicioniranje slike na ekranu. Druga moguća
upotreba je enkripcija podataka, u kojoj algoritam enkripcije uključuje pomeranje bitova. Na
kraju, dve instrukcije se mogu koristiti kada se vrši brzo deljenje i množenje sa veoma velikim
celim brojevima.
Sledeći primer demonstrira upotrebu SHRD tako što se niz dvostrukih reči pomera udesno
za 4 bita:
.data
array DWORD 648B2165h,8C943A29h,6DFA4B86h,91F76C04h,8BAF9857h
.code
mov bl,4 ; broj pomeranja
mov esi,OFFSET array ; ofset niza
mov ecx,(LENGTHOF array) – 1 ; broj elemenata niza
L1: push ecx ; čuvanje brojača petlje
mov eax,[esi + TYPE DWORD]
mov cl,bl ; broj pomeranja
10
shrd [esi],eax,cl ; pomeri EAX u gornje bitove [ESI]-a
add esi,TYPE DWORD ; ukaži na sledeći par elemenata
pop ecx ; vrati brojač petlje
loop L1
shr DWORD PTR [esi],COUNT ; pomeri poslednju dvostruku reč
9.2 Primena instrukcija pomeranja i rotiranja
9.2.1 Binarno množenje
U ranijim Intel-ovim procesorima, instrukcije binarnog množenja (MUL i IMUL) su se
smatrale sporim u odnosu na druge mašinske instrukcije. Kao rezultat toga, programeri su
pronašli da se množenje može efikasnije odraditi koristeći tehnike pomeranja bitova. Instrukcija
SHL vrši efikasno neoznačeno množenje kada je činilac stepen broja 2. Pomeranjem
neoznačenog broja za n bitova ulevo dobija se proizvod tog broja sa 2n. Bilo koji drugi činilac
se može izraziti kao suma stepena broja dva. Na primer, množenje neoznačenog EAX sa 36
možemo napisati kao množenje sa 25 + 22 i koristiti osobinu distributivnosti množenja:
EAX * 36 = EAX * (25 + 22)
= EAX * (32 + 4)
= (EAX * 32) + (EAX * 4)
Na sledećoj slici je prikazano množenje 123 sa 36, čime se dobija 4428:
Zanimljivo je primetiti da se u proizvodu 36 setuju bitovi 2 i 5, a da 2 i 5 istovremeno
označavaju koliko puta se pomeraju brojevi. Koristeći ovu informaciju, sledeći kod množi 123
sa 36 koristeći SHL i ADD instrukcije:
.code
mov eax,123
mov ebx,eax
shl eax,5 ; množenje sa 25
shl ebx,2 ; množenje sa 22
add eax,ebx ; sabiranje proizvoda
9.2.2 Prikaz binarnih bitova
Uobičajen programerski zadatak je konverzija binarnih celih brojeva u ASCII binarni
string koji se može prikazati. Instrukcija SHL je korisna zato što kopira najviši bit operanda u
Carry fleg svaki put kada se operand pomera ulevo. Sledeća procedura predstavlja jednostavnu
implementaciju ovog principa.
11
;---------------------------------------------------------
BinToAsc PROC
;
; Konvertuje 32-bitne binarne cele brojeve u ASCII binarne
brojeve.
; Prima: EAX = binarni ceo broj, ESI ukazuje na bafer
; Vraća: bafer popunjen sa ASCII binarnim ciframa
;---------------------------------------------------------
push ecx
push esi
mov ecx,32 ; broj bitova u EAX
L1: shl eax,1 ; pomeranje gornjeg bita u Carry fleg
mov BYTE PTR [esi],'0' ; odabir 0 kao podrazumevane
; cifre
jnc L2 ; ako nema prenosa, skoči na L2
mov BYTE PTR [esi],'1' ; u suprotnom, premesti 1 u
; bafer
L2: inc esi ; sledeća pozicija u baferu
loop L1 ; pomeri drugi bit ulevo
pop esi
pop ecx
ret
BinToAsc ENDP
9.2.3 Ekstrakcija MS-DOS polja sa datumom
Kada je memorija od velike važnosti, sistemski softveri često pakuju više polja podataka
u jedan broj. Da bi se dobili ovi podaci, aplikacije često treba da ekstrahuju sekvencu bitova
koje se nazivaju stringovi bitova. Na primer, u režimu sa realnim adresama, MS-DOS funkcija
57h vraća datum poslednje modifikacije fajla u DX. Bitovi 0-4 predstavljaju dan između 1 i 31,
bitovi 5-8 su za mesec, a bitovi 9-15 za godinu. Ako je fajl poslednji put modifikovan 10.3.1999,
datum u fajlu će imati sledeći oblik u DX registru (broj za godinu se računa kao razlika do 1980.
godine):
Da bi se dobio jedan string bitova, treba pomeriti bitove u niži deo registra i obrisati
nebitne pozicije. Sledeći kod ekstrahuje dan iz datuma u fajlu tako što kopira DL i maskira
bitove koji ne pripadaju tom polju:
mov al,dl ; kopiranje DL
and al,00011111b ; brisanje bitova 5-7
mov day,al ; sačuvaj u day
Da bi se dobio mesec, pomeramo bitove 5-8 u donji deo registra AL pre maskiranja ostalih
bitova. AL se potom kopira u promenljivu:
12
mov ax,dx ; kopiranje DX
shr ax,5 ; pomeri udesno za 5 bitova
and al,00001111b ; obriši bitove 4-7
mov month,al ; sačuvaj u month
Godina (bitovi 9-15) je kompletno u DH registru. Kopiramo ga u AL i pomeramo udesno
za jedan bit:
mov al,dh ; kopiranje DH
shr al,1 ; pomeri udesno za 1
mov ah,0 ; obriši AH
add ax,1980 ; godina je razlika od 1980. godine
mov year,ax ; sačuvaj u year
9.3 Instrukcije za množenje i deljenje
Množenje celih brojeva u x86 asembleru se može vršiti kao 32-bitne, 16-bitne ili 8-bitne
operacije. U većini slučajeva, radi se sa EAX ili jednim od njegovih delova (AX, AL).
Instrukcije MUL i IMUL vrše neoznačeno i označeno množenje celih brojeva, respektivno.
Instrukcija DIV vrši neoznačeno deljenje, a IDIV označeno deljenje celih brojeva.
9.3.1 Instrukcija MUL
Instrukcija MUL ima tri verzije:
— Množenje 8-bitnog operanda sa AL registrom,
— Množenje 16-bitnog operanda sa AX registrom,
— Množenje 32-bitnog operanda sa EAX registrom.
Dva činioca uvek moraju biti iste veličine, a proizvod je duplo veće veličine. Ova tri
formata prihvataju registarske i memorijske operande, ali ne i neposredne:
MUL reg/mem8
MUL reg/mem16
MUL reg/mem32
Jedini operand u MUL instrukciji je množilac. U tabeli 2 su prikazani podrazumevani
množioci i proizvodi, u zavisnosti od veličine množioca. Pošto je odredišni operand duplo veći
u veličini od množilaca, do prekoračenja ne može doći. MUL setuje Carry i Overflow flegove
ako gornja polovina proizvoda nije jednaka nuli. Carry fleg se obično koristi kod neoznačene
aritmetike, tako da ćemo o njemu ovde govoriti. Kada se AX pomnoži sa 16-bitnim operandom,
na primer, proizvod se smešta u kombinaciju DX i AX registara. Odnosno, gornjih 16 bitova
proizvoda se smešta u DX, a donjih 16 u AX. Carry fleg se setuje ako DX nije jednak nuli, što
označava da proizvod ne može stati u donju polovinu implicitnog odredišnog operanda.
13
Tabela 2. Operandi MUL instrukcije
Množilac 1 Množilac 2 Proizvod
AL reg/mem8 AX
AX reg/mem16 DX:AX
EAX reg/mem32 EDX:EAX
Primeri
Sledećim iskazima se množi AL sa BL, a rezultat se smešta u AX. Carry fleg se briše (CF
= 0) jer je AH (gornja polovina proizvoda) jednak nuli.
mov al,5h
mov bl,10h
mul bl ; AX = 0050h, CF = 0
Sledećim iskazima se množi 16-bitna vrednost 2000h sa 0100h. Carry fleg je setovan zato
što gornja polovina proizvoda (u DX) nije jednaka nuli:
.data
val1 WORD 2000h
val2 WORD 0100h
.code
mov ax,val1 ; AX = 2000h
mul val2 ; DX:AX = 00200000h, CF = 1
Sledećim iskazima se množi 12345h sa 1000h, čime se dobija 64-bitni proizvod u
kombinaciji EDX i EAX registara. Carry fleg je obrisan jer je gornja polovina proizvoda u EDX
jednaka nuli:
mov eax,12345h
mov ebx,1000h
mul ebx ; EDX:EAX = 0000000012345000h, CF = 0
9.3.2 Instrukcija IMUL
Instrukcija IMUL vrši množenje sa označenim brojevima. Za razliku od instrukcije MUL,
IMUL čuva znak proizvoda. To se radi tako što se najviši bit donje polovine proizvoda proširuje
u gornje bitove proizvoda. Postoje tri formata ove instrukcije: sa jednim, dva ili tri operanda. U
formatu sa jednim operandom, množioci su iste veličine, a proizvod je duplo veći u veličini.
14
Format sa jednim operandom
Proizvod se smešta u AX, DX:AX ili EDX:EAX:
IMUL reg/mem8 ; AX = AL * reg/mem8
IMUL reg/mem16 ; DX:AX = AX * reg/mem16
IMUL reg/mem32 ; EDX:EAX = EAX * reg/mem32
Kao i kod MUL, pošto je veličina proizvoda duplo veća od množilaca, ne može doći do
prekoračenja. Takođe, Carry i Overflow flegovi se setuju ako gornja polovina proizvoda ne
predstavlja proširenje znaka donje polovine. Ova informacija se može koristiti kako bi se
odlučilo treba li odbaciti gornju polovinu proizvoda.
Format sa dva operanda
Proizvod se smešta u prvom operandu, koji mora biti registar. Drugi operand može biti
registar, memorijski operand ili neposredna vrednost. Slede 16-bitni formati:
IMUL reg16,reg/mem16
IMUL reg16,imm8
IMUL reg16,imm16
Slede formati sa 32-bitnim operandima koji pokazuju da množilac može biti 32-bitni
registar, 32-bitni memorijski operand ili neposredna vrednost (8 ili 32 bita):
IMUL reg32,reg/mem32
IMUL reg32,imm8
IMUL reg32,imm32
Ovaj format odseca proizvod na dužinu odredišnog operanda. Ako se izgube značajne
cifre, setuju se Carry i Overflow flegovi. Treba proveriti jedan od ovih flegova nakon izvršenja
IMUL instrukcije sa dva operanda.
Format sa tri operanda
Proizvod se smešta u prvom operandu. Drugi operand može biti 16-bitni registar ili
memorijski operand, koji se množi sa trećim operandom, 8-bitnim ili 16-bitnim neposrednim
operandom:
IMUL reg16,reg/mem16,imm8
IMUL reg16,reg/mem16,imm16
Registar ili memorijski operand dužine 32 bita se može pomnožiti sa 8-bitnom ili 32-
bitnom neposrednom vrednošću:
IMUL reg32,reg/mem32,imm8
IMUL reg32,reg/mem32,imm32
Ako se izgube značajne cifre pri izvršavanju IMUL, setuju se Carry i Overflow flegovi.
Treba proveriti status ovih flegova nakon izvršavanja IMUL instrukcije sa tri operanda.
15
Neoznačeno množenje
IMUL formati sa dva ili tri operanda se mogu koristiti i za neoznačeno množenje, budući
da je donja polovina proizvoda ista i za označene i za neoznačene brojeve. Postoji jedan
nedostatak kada se ovo radi: Carry i Overflow flegovi neće označavati da li je gornja polovina
proizvoda jednaka nuli.
Primeri sa IMUL
Sledeće instrukcije množe 48 sa 4, dobijajući +192 u AX. Iako je proizvod korektan, AH
nije proširenje znaka od AL, tako da je setovan Overflow fleg:
mov al,48
mov bl,4
imul bl ; AX = 00C0h, OF = 1
Sledeće instrukcije množe -4 sa 4, čime se dobija -16 u AX. AH predstavlja proširenje
znaka od AL, tako da je Overflow fleg obrisan:
mov al,-4
mov bl,4
imul bl ; AX = FFF0h, OF = 0
Sledeće instrukcije množe 48 sa 4, čime se dobija +192 u DX:AX. Registar DX
predstavlja proširenje znaka od AX, tako da je Overflow fleg obrisan:
mov ax,48
mov bx,4
imul bx ; DX:AX = 000000C0h, OF = 0
Sledeće instrukcije množe 32-bitne vrednosti 4.823.424 sa -423, čime se dobija
-2.040.308.352 u EDX:EAX. Overflow fleg je obrisan jer je EDX proširenje znaka od EAX:
mov eax,+4823424
mov ebx,-423
imul ebx ; EDX:EAX = FFFFFFFF86635D80h, OF = 0
Sledeće instrukcije demonstriraju format sa dva operanda:
.data
word1 SWORD 4
dword1 SDWORD 4
.code
mov ax,-16 ; AX = -16
mov bx,2 ; BX = 2
imul bx,ax ; BX = -32
imul bx,2 ; BX = -64
imul bx,word1 ; BX = -256
mov eax,-16 ; EAX = -16
mov ebx,2 ; EBX = 2
imul ebx,eax ; EBX = -32
imul ebx,2 ; EBX = -64
imul ebx,dword1 ; EBX = -256
16
IMUL instrukcije sa dva i tri operanda koriste odredišni operand koji je iste veličine kao
i množilac. Samim tim, moguće je da dođe do označenog prekoračenja. Treba uvek proveriti
Overflow fleg nakon izvršavanja ovakvih IMUL instrukcija. Sledeće instrukcije sa dva
operanda pokazuju označeno prekoračenje jer -64.000 ne može da stane unutar 16-bitnog
odredišnog operanda:
mov ax,-32000
imul ax,2 ; OF = 1
Sledeće instrukcije pokazuju format sa tri operanda, uključujući primer sa označenim
prekoračenjem:
.data
word1 SWORD 4
dword1 SDWORD 4
.code
imul bx,word1,-16 ; BX = -64
imul ebx,dword1,-16 ; EBX = -64
imul ebx,dword1,-2000000000 ; OF = 1
Merenje vremena izvršavanja programa
Programeri često porede performanse jedne implementacije koda u odnosu na drugu tako
što mere njihova vremena izvršavanja. MS-Windows API biblioteka omogućava neophodne
alate za ovaj posao, što je još više olakšano sa GetMseconds procedurom u Irvine32 biblioteci.
Procedura daje broj sistemskih milisekundi koje su prošle od ponoći. U sledećem primeru,
najpre se poziva GetMseconds, tako da zapamtimo početno vreme. Potom se poziva procedura
čije vreme izvršavanja želimo da izmerimo (FirstProcedureToTest). Na kraju, ponovo se
poziva GetMseconds i računa se razlika dve vrednosti koje je procedura vratila:
.data
startTime DWORD ?
procTime1 DWORD ?
procTime2 DWORD ?
.code
call GetMseconds ; početno vreme
mov startTime,eax
.
call FirstProcedureToTest
.
call GetMseconds ; krajnje vreme
sub eax,startTime ; računanje proteklog vremena
mov procTime1,eax ; čuvanje proteklog vremena
Naravno, postoji malo vreme koje se troši na dva poziva procedure GetMseconds.
Međutim, ovo vreme je nevažno kada se mere performanse između dve implementacije koda.
17
Ovde, pozivamo drugu proceduru koju želimo da testiramo, i čuvamo njeno vreme izvršavanja
(procTest2):
call GetMseconds ; početno vreme
mov startTime,eax
.
call SecondProcedureToTest
.
call GetMseconds ; krajnje vreme
sub eax,startTime ; računanje proteklog vremena
mov procTime2,eax ; čuvanje proteklog vremena
Na kraju ovoga, odnos između procTime1 i procTime2 ukazuje na relativne performanse
dve procedure.
Poređenje MUL i IMUL sa pomeranjem bitova
U starijim x86 procesorima, postojala je značajna razlika u performansama između
množenja pomoću pomeranja bitova i množenja sa MUL i IMUL instrukcijama. Možemo
koristiti proceduru GetMseconds kako bismo uporedili vremena izvršavanja ova dva tipa
množenja. Sledeće dve procedure vrše neprekidno množenje koristeći konstantu
LOOP_COUNT kako bi se odredio broj ponavljanja:
mult_by_shifting PROC
;
; Množi EAX sa 36 koristeći SHL, LOOP_COUNT puta.
;
mov ecx,LOOP_COUNT
L1: push eax ; sačuvaj originalni EAX
mov ebx,eax
shl eax,5
shl ebx,2
add eax,ebx
pop eax ; vrati EAX
loop L1
ret
mult_by_shifting ENDP
mult_by_MUL PROC
;
; Množi EAX sa 36 koristeći MUL, LOOP_COUNT puta.
;
mov ecx,LOOP_COUNT
L1: push eax ; sačuvaj originalni EAX
mov ebx,36
mul ebx
pop eax ; vrati EAX
loop L1
ret
mult_by_MUL ENDP
18
Sledećim kodom se poziva mult_by_shifting i prikazuje vreme izvršavanja.
LOOP_COUNT = 0FFFFFFFFh
.data
intval DWORD 5
startTime DWORD ?
.code
call GetMseconds ; početno vreme
mov startTime,eax
mov eax,intval ; množenje počinje
call mult_by_shifting
call GetMseconds ; krajnje vreme
sub eax,startTime
call WriteDec ; prikaz proteklog vremena
Nakon poziva mult_by_MUL na isti način, rezultujuća vremena na 4 GHz Pentium 4
procesoru su pokazala da se procedura sa SHL izvršila za 6,078 sekundi, a sa MUL za 20,718
sekundi. Drugim rečima, korišćenje MUL instrukcije je bilo 241 puta sporije. Međutim, kada
se isti programi pokrenu na Intel Core2Duo procesoru, vremena obe funkcije su potpuno ista.
Ovaj primer pokazuje da je Intel uspeo da značajno optimizuje MUL i IMUL instrukcije u
skorijim procesorima.
9.3.3 Instrukcija DIV
Instrukcija DIV vrši 8-bitno, 16-bitno i 32-bitno neoznačeno celobrojno deljenje. Jedini
registarski ili memorijski operand je delilac. Formati su:
DIV reg/mem8
DIV reg/mem16
DIV reg/mem32
U sledećoj tabeli je prikazan odnos između deljenika, delioca, količnika i ostatka.
Deljenik Delilac Količnik Ostatak
AX reg/mem8 AL AH
DX:AX reg/mem16 AX DX
EDX:EAX reg/mem32 EAX EDX
Primeri
Sledeće instrukcije vrše 8-bitno neoznačeno deljenje 83h sa 2, čime se dobija 41h i ostatak
1:
mov ax,0083h ; deljenik
movbl,2 ; delilac
div bl ; AL = 41h, AH = 01h
19
Sledeće instrukcije vrše 16-bitno neoznačeno deljenje 8003h sa 100h, čime se dobija 80h
i ostatak 3. DX sadrži viši deo deljenika, tako da se mora obrisati pre izvršenja DIV instrukcije:
mov dx,0 ; obriši deljenik, gornji deo
mov ax,8003h ; deljenik, donji deo
mov cx,100h ; delilac
div cx ; AX = 0080h, DX = 0003h
Sledeće instrukcije vrše 32-bitno neoznačeno deljenje koristeći memorijski operand kao
delilac:
.data
dividend QWORD 0000000800300020h
divisor DWORD 00000100h
.code
mov edx,DWORD PTR dividend + 4 ; viša dvostruka reč
mov eax,DWORD PTR dividend ; niža dvostruka reč
div divisor ; EAX = 08003000h, EDX = 00000020h
9.3.4 Deljenje označenih brojeva
Deljenje označenih brojeva je skoro identično sa deljenjem neoznačenih brojeva, sa
jednom važnom razlikom: deljenik mora biti u potpunosti proširen znakom pre deljenja. Najpre
pogledajmo instrukcije za proširenje znaka. Potom ćemo ih primeniti na instrukciju za označeno
deljenje, IDIV.
Instrukcije za proširenje znaka (CBW, CWD, CDQ)
Instrukcija CBW (convert byte to word) proširuje bit znaka registra AL u AH, čuvajući
znak broja. U sledećem primeru, 9Bh (u AL) i FF9Bh (u AX) su oba jednaki -101 decimalno:
.data
byteVal SBYTE -101 ; 9Bh
.code
mov al,byteVal ; AL = 9Bh
cbw ; AX = FF9Bh
Instrukcija CWD (convert word to doubleword) proširuje znak od AX u DX:
.data
wordVal SWORD -101 ; FF9Bh
.code
20
mov ax,wordVal ; AX = FF9Bh
cwd ; DX:AX = FFFFFF9Bh
Instrukcija CDQ (convert doubleword to quadword) proširuje znak od EAX u EDX:
.data
dwordVal SDWORD -101 ; FFFFFF9Bh
.code
mov eax,dwordVal
cdq ; EDX:EAX = FFFFFFFFFFFFFF9Bh
9.3.5 Instrukcija IDIV
Ova instrukcija vrši označeno deljenje, koristeći iste operande kao i DIV. Pre izvršavanja
8-bitnog deljenja, deljenik (AX) mora biti proširen sa znakom. Ostatak uvek ima isti znak kao
i deljenik.
Primer 1
Sledeće instrukcije dele -48 sa 5. Nakon izvršenja IDIV, količnik u AL je -9, a ostatak u
AH je -3:
.data
byteVal SBYTE -48 ; D0 heksadecimalno
.code
mov al,byteVal ; donja polovina deljenika
cbw ; proširi AL u AH
mov bl,+5 ; delilac
idiv bl ; AL = -9, AH = -3
Sledeća slika prikazuje kako se AL proširuje znakom u AX sa instrukcijom CBW.
Da bismo razumeli zašto je proširenje znakom deljenika neophodno, ponovićemo prethodni
primer bez proširivanja znaka. Sledeći kod inicijalizuje AH sa nulom tako da ima poznatu
vrednost, a potom deli bez korišćenja CBW:
.data
byteVal SBYTE -48 ; D0 heksadecimalno
.code
mov ah,0 ; gornji deo deljenika
mov al,byteVal ; donji deo deljenika
mov bl,+5 ; delilac
idiv bl ; AL = 41, AH = 3
Pre deljenja, AX = 00D0h (208 decimalno). IDIV deli taj broj sa 5, čime se dobija 41 i
ostatak 3. Ovo sigurno nije korektno rešenje.
21
Primer 2
Za 16-bitno deljenje neophodno je da AX bude prošireno znakom u DX. U sledećem
primeru se deli -5000 sa 256:
.data
wordVal SWORD -5000
.code
mov ax,wordVal ; deljenjik, donji deo
cwd ; proširenje AX u DX
mov bx,+256 ; delilac
idiv bx ; količnik AX = -19, ostatak DX = -136
Primer 3
Za 32-bitno deljenje je neophodno da EAX bude prošireno znakom u EDX. U sledećem
primeru se deli 50.000 sa -256:
.data
dwordVal SDWORD +50000
.code
mov eax,dwordVal ; deljenik, donji deo
cdq ; proširenje EAX u EDX
mov ebx,-256 ; delilac
idiv ebx ; količnik EAX = -195, ostatak EDX = +80
Svi statusni flegovi su nedefinisani nakon izvršavanja DIV i IDIV.
Prekoračenje prilikom deljenja
Ako se prilikom deljenja dobije količnik koji ne može da stane u odredišni operand, dolazi
do prekoračenja. To čini da procesor dobije prekid, a trenutni program se zaustavlja. Na primer,
sledeće instrukcije generišu prekoračenje zato što je količnik (100h) suviše veliki za AL:
mov ax,1000h
mov bl,10h
div bl ; AL ne može da smesti 100h
Kada se ovaj kod izvršava na MS-Windows, dobija se poruka o grešci prikazana na slici
1. Slična poruka se dobija kada pokušate da delite sa nulom.
Slika 1. Poruka o prekoračenju prilikom deljenja
22
Kako bi se smanjila verovatnoća pojavljivanja prekoračenja, treba koristiti 32-bitne
delioce i 64-bitne deljenike. U sledećem kodu, delilac je u EBX, a deljenik je smešten u 64-
bitnu kombinaciju EDX i EAX:
mov eax,1000h
cdq
mov ebx,10h
div ebx ; EAX = 00000100h
Da bi se sprečilo deljenje sa nulom, testirajte delilac pre deljenja:
mov ax,dividend
mov bl,divisor
cmp bl,0 ; provera delioca
je NoDivideZero ; nula? Prikaži grešku
div bl ; nije nula: nastavi
.
.
NoDivideZero: ;(prikaži "Pokušaj deljenja sa nulom")
9.3.6 Implementacija aritmetičkih izraza
Implementacija aritmetičkih izraza na prvi pogled deluje kao radnja koju je najbolje
ostaviti za pisce kompajlera. Međutim, možete naučiti kako kompajleri optimizuju kod.
Takođe, možete implementirati bolju proveru na greške od kompajlera tako što ćete proveriti
veličinu proizvoda nakon operacija množenja. Većina kompajlera jezika visokog nivoa ignoriše
gornja 32 bita proizvoda kada se množe dva 32-bitna operanda. U asembleru, međutim, možete
koristiti Carry i Overflow flegove kako biste videli da li proizvod može da stane u 32 bita.
Primer 1
Implementirati sledeći C++ iskaz u asembleru, koristeći neoznačene 32-bitne cele
brojeve:
var4 = (var1 + var2) * var3;
U ovom slučaju možemo raditi s leva na desno (prvo sabiranje, pa množenje). Nakon
druge instrukcije, EAX sadrži sumu var1 i var2. U trećoj instrukciji, EAX se množi sa var3 i
proizvod se smešta u EAX:
mov eax,var1
add eax,var2
mul var3 ; EAX = EAX * var3
jc tooBig ; neoznačeno prekoračenje?
mov var4,eax
jmp next
tooBig: ; prikaz poruke o grešci
Ako instrukcija MUL generiše proizvod veći od 32 bita, instrukcija JC skače na labelu
koja prikazuje grešku.
23
Primer 2
Implementirati sledeći C++ iskaz, koristeći neoznačene 32-bitne cele brojeve:
var4 = (var1 * 5) / (var2 - 3);
U ovom primeru, postoje dva podizraza unutar zagrada. Leva strana se može dodeliti u
EDX:EAX, tako da nije neophodno proveravati na prekoračenje. Desna strana se dodeljuje u
EBX:
mov eax,var1 ; leva strana
mov ebx,5
mul ebx ; EDX:EAX = proizvod
mov ebx,var2 ; desna strana
sub ebx,3
div ebx ; konačno deljenje
mov var4,eax
Primer 3
Implementirati sledeći C++ iskaz, koristeći označene 32-bitne cele brojeve:
var4 = (var1 * -5) / (-var2 % var3);
Možemo početi sa izrazom na desnoj strani i smestiti njegovu vrednost u EBX. Pošto su
operandi označeni, važno je proširiti sa znakom deljenik u EDX i koristiti IDIV instrukciju:
mov eax,var2 ; početak desne strane
neg eax
cdq ; proširivanje znakom deljenika
idiv var3 ; EDX = ostatak
mov ebx,edx ; EBX = desna strana
Potom, računamo izraz na levoj strani, a proizvod smeštamo u EDX:EAX:
mov eax,-5 ; početak leve strane
imul var1 ; EDX:EAX = leva strana
Na kraju, leva strana (EDX:EAX) se deli sa desnom stranom (EBX):
idiv ebx ; krajnje deljenje
mov var4,eax ; količnik
9.4 Sabiranje i oduzimanje sa proširenom preciznošću
Sabiranje i oduzimanje sa proširenom preciznošću predstavlja sabiranje i oduzimanje
brojeva koji su skoro beskonačne veličine. U C++-u, pisanje programa kojim se sabiraju dva
1024-bitna cela broja ne bi bilo lako. Ali u asembleru, instrukcije ADC (add with carry) i SBB
(subtract with borrow) služe za ove operacije.
24
9.4.1 Instrukcija ADC
Instrukcija ADC sabira izvorišni operand sa sadržajem Carry flega i odredišnim
operandom. Formati instrukcije su isti kao i za ADD, a operandi moraju biti iste veličine:
ADC reg,reg
ADC mem,reg
ADC reg,mem
ADC mem,imm
ADC reg,imm
Na primer, sledeće instrukcije sabiraju dva 8-bitna cela broja (FFh + FFh), čime se dobija
16-bitna suma u DL:AL, koja je 01FEh:
mov dl,0
mov al,0FFh
add al,0FFh ; AL = FEh
adc dl,0 ; DL/AL = 01FEh
Na sledećoj slici je prikazano kretanje podataka tokom dva koraka sabiranja. Najpre, FFh
se sabira sa AL, čime se dobija FEh u AL registru i setuje se Carry fleg. Potom, i 0 i sadržaj
Carry flega se sabiraju sa DL registrom:
Na sličan način, sledeće instrukcije sabiraju dva 32-bitna cela broja (FFFFFFFFh +
FFFFFFFFh), čime se dobija 64-bitna suma u EDX:EAX: 00000001FFFFFFFFh:
mov edx,0
mov eax,0FFFFFFFFh
add eax,0FFFFFFFFh
adc edx,0
Primer sa proširenim sabiranjem
Sledeća Extended_Add procedura sabira dva proširena cela broja iste veličine. Koristeći
petlju, radi sa proširenim celim brojevima kao da su paralelni nizovi. Kako sabira svaki par
vrednosti u nizovima, uzima u obzir i vrednost prenosa iz sabiranja koje je izvršeno u
prethodnoj iteraciji petlje. Ova implementacija podrazumeva da su brojevi smešteni kao nizovi
bajtova, ali ovaj primer se lako može modifikovati za sabiranje nizova dvostrukih reči:
;--------------------------------------------------------
Extended_Add PROC
;
; Računa sumu dva proširena cela broja smeštena
; kao nizovi bajtova.
; Prima: ESI i EDI ukazuju na dva cela broja,
25
; EBX ukazuje na promenljivu koja će čuvati sumu
; ECX ukazuje koliko bajtova treba sabirati.
; Memorija za smeštaj sume mora biti za jedan bajt
; veća od ulaznih operanada.
; Vraća: ništa
;--------------------------------------------------------
pushad
clc ; brisanje Carry flega
L1: mov al,[esi] ; uzmi prvi broj
adc al,[edi] ; saberi sa drugim brojem
pushfd ; sačuvaj Carry fleg
mov [ebx],al ; smesti delimičnu sumu
add esi,1 ; ažuriraj sva tri pokazivača
add edi,1
add ebx,1
popfd ; vrati Carry fleg
loop L1 ; ponovi petlju
mov byte ptr [ebx],0 ; obriši viši bajt sume
adc byte ptr [ebx],0 ; saberi sa preostalim prenosom
popad
ret
Extended_Add ENDP
Sledeći kod poziva ovu proceduru, prosleđujući joj dva 8-bitna cela broja. Treba paziti da
se alocira dodatan bajt za sumu, u slučaju da se generiše prenos prilikom sabiranja dva gornja
bajta brojeva.
.data
op1 BYTE 34h,12h,98h,74h,06h,0A4h,0B2h,0A2h
op2 BYTE 02h,45h,23h,00h,00h,87h,10h,80h
sum BYTE 9 DUP(0)
.code
main PROC
mov esi,OFFSET op1 ; prvi operand
mov edi,OFFSET op2 ; drugi operand
mov ebx,OFFSET sum ; suma
mov ecx,LENGTHOF op1 ; broj bajtova
call Extended_Add
; Prikaz sume.
mov esi,OFFSET sum
mov ecx,LENGTHOF sum
call Display_Sum
call Crlf
Program daje sledeće na svom izlazu. U ovom slučaju se dobija prenos:
0122C32B0674BB5736
Procedura Display_Sum iz istog programa prikazuje sumu u pravilnom redosledu,
počevši sa najvišim bajtom, pa potom ide ka nižim bajtovima:
26
Display_Sum PROC
pushad
; ukaži na sledeći element niza
add esi,ecx
sub esi,TYPE BYTE
mov ebx,TYPE BYTE
L1: mov al,[esi] ; uzmi bajt iz niza
call WriteHexB ; prikaži ga
sub esi,TYPE BYTE ; ukaži na prethodni bajt
loop L1
popad
ret
Display_Sum ENDP
9.4.2 Instrukcija SBB
Instrukcija SBB oduzima i izvorišni operand i vrednost u Carry flegu od odredišnog
operanda. Mogući operandi su isti kao i za ADC instrukciju. Sledeći kod vrši 64-bitno
oduzimanje. Setuje EDX:EAX na 0000000700000001h i oduzima 2 od ove vrednosti. Donja
32 bita se prva oduzimaju, setujući Carry fleg. Potom se gornja 32 bita oduzimaju, uključujući
Carry fleg:
mov edx,7 ; gornja polovina
mov eax,1 ; donja polovina
sub eax,2 ; oduzmi 2
sbb edx,0 ; oduzmi gornju polovinu
Na slici 2 je prikazano kretanje podataka tokom dva koraka oduzimanja. Najpre, vrednost
2 se oduzima od EAX, čime se dobija FFFFFFFFh u EAX. Carry fleg se setuje jer je neophodna
pozajmica kada se oduzima veći broj od manjeg. Potom SBB instrukcija oduzima i 0 i sadržaj
Carry flega od EDX.
Slika 2. Oduzimanje od 64-bitnog celog broja koristeći SBB
top related