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 i BCS

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 i BCC

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

Ukoliko je N=0, tj. ako je rezultat pozitivan.

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 je instrukcija koja za funckiju ima negaciju određenog operanda. Negativ datog broja dobija se tako što se taj broj oduzme od nule.

    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 =)