| Tutorials: Mama, I wanna be a coder! II | |||
|---|---|---|---|
| by Cyborg/INDUS3 | cyborg@fonoc.co.yu | ||
Nakon kraće pauze nastavljamo sa školicom asemblera za dobru dečicu i njihove nevaljale roditelje.
Na samom početku ću ispraviti grešku koja se potkrala u prošlom broju. Primer za JMP instrukciju ne uvećava registar D0 do beskonačnosti (tačnije do vrednosti $ffffffff =), već briše D0, a zatim ga uveca za jedan i ove dve instrukcije ponavlja beskonačno mnogo puta. Problem je u pogrešnom mestu na kome se nalazi labela start, kao što ste verovatno i sami primetili. Tačan primer će izgledati ovako:
CLR.L D0 ;briše D0
start ADD.L #1,D0 ;povecava D0 za 1
JMP start ;skače na start
JMP sadrži tačnu adresu na koju program treba da "skoči", tako da ova instrukcija zauzima 6 bajtova; 2 bajta označavaju kod same instrukcije dok je u preostalom longwordu smeštena adresa na koju program skače. Instrukcija koja je vrši istu funkciju kao i JMP ali sa odredjenim ograničenjima je instrukcija BRA. Njenim korištenjem naš primer postaje:
CLR.L D0
start ADD.L #1,D0
BRA.S start
BRA.S start zauzima samo 2 bajta i funkcioniše na taj način što pamti "udaljenost" od ili do adrese na koju program treba da skoči ali ne pamti i samu adresu. Pošto je ADD.L #1,D0 dužine 6 bajtova a BRA.S start dužine 2 bajta program će skočiti na adresu za 8 bajtova manju od stanja PC-a (Program Counter). BRA.S je ekvivalentna instrukciji BRA.B i one omogućavaju skokove na udaljenosti od +/- $7f (127) bajtova, a postoje i BRA.W koja je dugačka 4 bajta i može da skače u zoni od +/- $7fff (32768) bajtova i BRA.L od 6 bajtova s opsegom od +/- $7fffffff (2147483647) bajtova. Ograničenje BRA instrukcije je i u tome što ovom instrukcijom nije moguć skok na druge sekcije programa. No o tome nešto kasnije.
Postoji veliki broj tzv. Branch instrukcija; jedna od njih je i
BRA koja vrši bezuslovno grananje. BSR je instrukcija koja ima istu
funkciju kao i JSR stim što BSR.S ima 2 bajta, BSR.W 4 bajta a BSR.L
poseduje 6 bajtova.
Sve ostale Branch instrukcije vrše grananje u zavisnosti od stanja
Statusnog Registra (SR). SR je 16-bitni registar, u čijem se nižem bajtu
nalazi 5 bitova, tzv. flegova. To su C (Carry), V (Overflow), Z (Zero), N
(Negative) i X (Extended) fleg. Nakon svake instrukcije određeni flegovi
se postavljaju na jedinice ili nule u zavisnosti od operacije. Na osnovu
njihovih imena može se zaključiti koju funkciju imaju. C fleg će biti
postavljen ako je došlo do prenosa jednog bita pri računanju ili
šiftovanju, V će biti postavljen ukoliko je došlo do prekoračenja, Z ako
je rezultat operacije nula, N fleg ako je rezultat operacije negativan, a X
fleg ima također funkciju prenosa i bita, sličo kao i C fleg. X fleg nema
uticaja na grananje, tako da grananje odredjuju N,Z,V i C flegovi.
BHI
Do grananja će doći ukoliko su i C i Z flegovi nule, tj. ako je rezultat veći od operanda sa kojim se poredio.
BLO
Do grananja dolazi ako je C fleg nula, tj. ako je rezultat manji.
BLS
Grananje će se dogoditi ako su ili C ili Z flegovi postavljeni, tj. ako je rezultat manji ili jednak.
BHS
Ukoliko je C nula, doći će do grananja, tj. ako je rezultat veći ili jednak.
BLT
Ako je N=0 i V=1, ili N=1 i V=0, tj. ako je rezultat manji dolazi do grananja.
BLE
Ako je N=0 i V=1, ili N=1 i V=0, ili ako je Z=1, tj. ako je rezultat manji ili jednak.
BGT
Ako je N=1 i V=1 i Z=0, ili N=0 i V=0 i Z=0, tj. ako je rezultat veći.
BGE
Ako je N=1 i V=1, ili N=0 i V=0, tj. ako je rezultat veći ili jednak.
BPL
BMI
N=1, tj. rezultat negativan.
BVC
V=0, tj. nema prekoračenja.
BVS
V=1, tj. došlo je do prekoračenja.
BNE
Z=0, tj. rezultat nije jednak nuli.
BEQ
Z=1, tj. rezultat je nula.
Za razliku od BRA i BSR instrukcija sve ostale Bcc instrukcije mogu imati samo sufikse .S(.B) i .W .
;- primer grananja -------------
move.w #$20,d1 ;postaljamo vrednost sa kojom poredimo
tst.w d0 ;da li je d0=0?
beq.s .nula ;ako jeste, skok na adresu .nula
;ako nije,
cmp.w d1,d0 ;poredimo d0 i d1
bgt.s .vece ;ako je d0>d1, skok na adresu .vece
.manje ;rezultat je manji od $20 i nije nula
...
...
rts
.nula
...
rts
.vece
...
rts
;--------------------------------
Ovde se pojavljuje instrukcija TST koja testira dati operand i postavlja N i Z flegove dok flegove V i C briše. Nakon ove instrukcije moguće je videti da li je operand koji je testiran jednak nuli ili je negativan, odnosno pozitivan.
QUICK instrukcije
Postoje tri quick instrukcije: MOVEQ, ADDQ i SUBQ. Sve tri imaju iste funkcije kao i njihove slow =) varijante, s tom razlikom što ove instrukcije služe da bi se konstanta stavila u registar ili da se izvrši sabiranje/oduzimanje konstantom, i to relativno malom konstantom.
MOVEQ uvek stavlja longword i to samo u data registar. Konstanta može da bude u opsegu od -128 ($ffffff80) do 127 ($7f). Ako u AsmPro-u napišete MOVEQ #128,D0, asembler će vas upozoriti, a u D0 ćete dobiti -128 =) Ova instrukcija je dugačka 2 bajta za razliku od MOVE.L #-128,D0 koja ima 6 bajtova. Naravno ovo se odražava i na brzinu izvršavanja instrukcije.
ADDQ/SUBQ služe za dodavanje konstante u opsegu od 1 do 8, ali za razliku od MOVEQ nisu ograničeni samo na longwordove i na data registre već se mogu koristiti u sve tri dužine podataka i u različitim načinima adresiranja.
Ubuduće ću na svim mestima na kojima je moguće koristiti quick instrukcije to i raditi jer se time dobija na brzini koda i njegovoj veličini.
Tako bi prvi primer trebalo da glasi:
moveq #0,d0
start addq.l #1,d0
bra.s start
Ako se pitate zašto sam stavio MOVEQ #0,D0 umesto CLR.L D0, pa zato što je brže.
Operacije nad bitovima
BSET n,xxx postavlja n-ti bit u operandu xxx. n može biti zadato
direktno ili iz data registra dok je xxx ili data registar ili adresa.
BSET #4,D0 postalja četvrti bit u D0, a BSET D0,(A0) postavlja bit
koji ima vrednost iz D0 na adresi smeštenoj u A0.
BCLR briše odredjeni bit poput prethodne instrukcije.
BCHG invertuje određeni bit. Ako bit ima vrednost 1 nakon ove instrukcije imat će vrednost 0 i obratno.
BTST testira određeni bit i na osnovu testiranja postavlja Z fleg. Na taj način se može saznati da li je određeni bit 0 ili 1.
Matematičke operacije
Već sam pomenuo ADD i SUB koji služe za sabiranje i oduzimanje.
MULU i MULS instrukcije služe za množenje dva operanda, bez znaka i
sa znakom. Postoje po tri varijante ovih instrukcija.
Prva mogućnost je da se množe dva 16-bitna broja (wordovi),
dobijeni rezultat je 32-bitan (longword).
move.w #-2,d0
move.w #$5000,d1
MULS.W D0,D1
Druga varijanta je da se pomnože dva 32-bitna broja i da rezultat bude isto tako 32-bitan.
MULU.L D0,D1
I na kraju postoji mogućcnost množenja dva 32-bitna broja tako da dobijeni rezultat bude 64-bitan (quadword).
MULS.L D0,D0:D1
U ovom slučaju D0 množi D1, a rezultat ide u D0 i D1 tako da se u D0 nalazi viši longword dobijenog rezultata, a u D1 niži longword.
Primer u kome se koristi množenje bila bi rutina za ispisivanje jednog pixela na chunky ekranu.
;- put pixel ---------------
lea chunky,a0 ;adresa chunky bafera
moveq #30,d0 ;x koordinata pixela
moveq #70,d1 ;y koordinata
MULU.W #320,D1 ;y koord. množimo sa 320 da bi dobili adresu
;reda u kome će biti upisan pixel
add.w d0,d1 ;na nju dodajemo x koordinatu
move.b #$1,(a0,d1.l) ;stavljamo chunky pixel sa 1. bojom na
;adresu chunky+320*y+x
rts
;----------------------------
DIVU i DIVS služe za deljenje, kao i kod množenja, znak može učestvovati u deljenju. Za instrukcije deljenja postoje po četri varijante koje se tiču dužine operanada.
DIVS.W (A0),D0
U ovom slučaju se 32-bitni broj iz D0 deli sa 16-bitnim brojem koji je smešten na adresi iz A0. Količnik se smešta u nizih 16 bitova u registru D0, a u viših 16 bitova smešta se ostatak deljenja.
DIVU.L D1,D7
32-bitni broj iz D1 deli 32-bitni broj iz D7 i smešta 32-bitni količnik u D7. Ostatak se nigde ne upisuje!
DIVS.L D0,D1:D2
32-bitni broj iz D0 deli 64-bitni broj iz D1D2 (u D1 je viši longword, u D2 niži). Količnik je 32-bitan i smešta se u D2, dok se ostatak smešta u D1 i takođe je 32-bitan.
DIVUL.L (A6),D7:D3
32-bitni broj sa adrese iz A6 deli 32-bitni broj iz D3. Količnik ide u D3 a ostatak u D7. I količnik i ostatak su 32-bitni brojevi.
NEG
move.l #$7f7f7f,d0
moveq #0,d1
sub.l d0,d1
Prethodni primer je ekvivalentan sledećem:
move.l #$7f7f7f,d1
NEG.L D1
EXT je instrukcija koja služi da "raširi" znak na word ili longword u određenom data registru . Pozitivni i negativni brojevi se razlikuju po najvišem bitu. Ako se radi o bajtu to je 7. bit, kod worda u pitanju je 15. a kod longworda 31. bit, tj. krajnji levi bit. Ukoliko ovaj bit ima vrednost 1 takav broj je negativan. Na ovaj način je jednim bajtom moguće napisati brojeve od -128 do +127. -128 je u heksadecimalnom sistemu $80, sledeći -127 je $81, -126 je $82,....a -1 je ustvari $ff.
moveq #0,d0
move.b #-2,d0
muls.w #5,d0
rts
Ovaj primer prvo briše kompletan registar D0 a zatim u bajt istog postavlja broj -2 tj. $fe. Zatim se vrši množenje (16bit x 16bit = 32 bit) brojem 5, ali dobijeni rezultat će biti broj $4f6, što nije "tačno" jer bi rezultat trebalo da bude -10. U čemu je problem? Napisaću ono što se dogodilo malo drugačije, onako kako je izgledao registar D0 nakon svake instrukcije:
d0=$00000000 ;moveq #0,d0
d0=$000000fe ;move.b #-2,d0
d0=$000004f6 ;muls.w #5,d0
Kada bi registri izgledali ovako, rezultat bi bio tačan:
d0=$00000000
d0=$0000fffe
d0=$fffffff6 ;decimalno -10
Pošto instrukcija muls.w množi dva 16-bitna broja, broj $fe
odnosno 8-bitno -2 je potrebno proširiti na 16-bitno -2, tj. $fffe. To
je ono što i radi instrukcija EXT.
EXT ima svoje tri varijacije. Njenim korišćenjem je moguće
proširivanje 8-bitne vrednosti na 16-bitnu, instrukcijom EXT.W Dn,
proširivanje 16-bitne vrednosti na 32 bita uz pomoc EXT.L Dn i
proširivanje 8-bitnog broja na 32 bita, EXTB.L Dn.
;-- ext primer ------------
moveq #0,d0
move.l d0,d1
move.l d0,d2
move.l d0,d3
move.b #127,d0
move.b d0,d1
ext.w d1
move.b #-128,d2
move.b d2,d3
extb.l d3
rts
;--------------------------
Logičke operacije
Logičko (Bulovo) I, ILI, Eksluzivno ILI i logičko NE su operacije koje mc680x0 procesori mogu da izvrše po jednom instrukcijom.
AND obavlja operaciju logičko I. Svaki par bitova daje kao rezultat bit po sledećoj tabeli:
0 1 0 1
and 0 0 1 1
-------
0 0 0 1
move.b #%10011011,d0
and.b #%01010101,d0
;rezultat je %00010001
OR, logičko ILI radi po sledećoj šemi:
0 1 0 1
or 0 0 1 1
-------
0 1 1 1
move.b #%10011011,d0
or.b #%01010101,d0
;rezultat je %11011111
EOR, ekskluzivno ILI daje:
0 1 0 1
eor 0 0 1 1
-------
0 1 1 0
move.b #%10011011,d0
eor.b #%01010101,d0
;rezultat je %11001110
NOT, logičko NE invertuje svaki bit:
not 0 1
---
1 0
move.b #%10011011,d0
not.b d0
;rezultat je %01100100
Ove instrukcije rade na sve tri dužine (.b, .w, .l).
Ponekad će se javiti potreba za zamenom sadržaja dva registra. Za ovakvu operaciju vam neće biti potreban dodatni registar jer ovu funkciju poseduje instrukcija EXG. EXG uvek razmenjuje cele registre (32-bita) bilo da su u pitanju data ili adresni registri.
moveq #-1,d0
move.w #$1234,a0
EXG D0,A0
rts
To bi bilo sve za ovaj broj. Do sledećeg, samo naporno vežbajte =)