| 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 dječicu i njihove nevaljale roditelje.
Na samom početku ću ispraviti grešku koja se potkrala u prošlom broju. Primjer za JMP instrukciju ne uvećava registar D0 do beskonačnosti (točnije do vrijednosti $ffffffff =), već briše D0, a zatim ga uveća za jedan i ove dvije instrukcije ponavlja beskonačno mnogo puta. Problem je u pogrešnom mjestu na kome se nalazi labela "start", kao što ste vjerojatno i sami primjetili. Točan primjer će izgledati ovako:
CLR.L D0 ;briše D0
start ADD.L #1,D0 ;povećava D0 za 1
JMP start ;skače na start
JMP sadrži toč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 smještena adresa na koju program skače.
Instrukcija koja vrši istu funkciju kao i JMP, ali sa određenim
ograničenjima je instrukcija BRA. Njenim korištenjem naš primjer postaje:
CLR.L D0
start ADD.L #1,D0
BRA.S start
BRA.S start zauzima samo 2 bajta i funkcionira 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 prijenosa 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 određuju 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 .
;- primjer grananja -------------
move.w #$20,d1 ;postaljamo vrijednost 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 vidjeti 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 zbrajanje/oduzimanje konstantom, i to relativno malom konstantom.
MOVEQ uvijek 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 mjestima 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 primjer trebao 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 vrijednost iz D0 na adresi smještenoj u A0.
BCLR briše određeni bit poput prethodne instrukcije.
BCHG invertira određeni bit. Ako bit ima vrijednost 1 nakon ove instrukcije imat će vrijednost 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 zbrajanje 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.
Primjer 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 dijeljenje, kao i kod množenja, znak može učestvovati u dijeljenju. Za instrukcije dijeljenja 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 dijeli sa 16-bitnim brojem koji je smješten na adresi iz A0. Količnik se smješta u nizih 16 bitova u registru D0, a u viših 16 bitova smješta se ostatak deljenja.
DIVU.L D1,D7
32-bitni broj iz D1 dijeli 32-bitni broj iz D7 i smešta 32-bitni količnik u D7. Ostatak se nigdje ne upisuje!
DIVS.L D0,D1:D2
32-bitni broj iz D0 dijeli 64-bitni broj iz D1D2 (u D1 je viši longword, u D2 niži). Količnik je 32-bitan i smješta se u D2, dok se ostatak smješta u D1 i također je 32-bitan.
DIVUL.L (A6),D7:D3
32-bitni broj sa adrese iz A6 dijeli 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 primjer je ekvivalentan slijedeć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 lijevi bit. Ukoliko ovaj bit ima vrijednost 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 primjer 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 "točno" jer bi rezultat trebao da bude -10. U čemu je problem? Napisat ć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 toč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štenjem je moguće
proširivanje 8-bitne vrijednosti na 16-bitnu, instrukcijom EXT.W Dn,
proširivanje 16-bitne vrijednosti na 32 bita uz pomoć EXT.L Dn i
proširivanje 8-bitnog broja na 32 bita, EXTB.L Dn.
;-- ext primjer ------------
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 slijedećoj tablici:
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 shemi:
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 invertira 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 zamjenom sadržaja dva registra. Za ovakvu operaciju vam neće biti potreban dodatni registar jer ovu funkciju posjeeduje instrukcija EXG. EXG uvijek razmjenjuje cijele 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 slijedećeg, samo naporno vježbajte =)