|
Mage Wars NEX - Dark Age Scavangers Seaside The Megagrip II Epic Marketing Crystal Software Dafel Bloodline Update: Napalm The World Foundry phase5 Settlers II Delsyd Software Update: Amiga JoyRide Amiga AMP v2.1 Starbirds The Strangers Tiny Troops IOBlix VGP Demo Delsyd Software 1000% Mala škola C-a www.xrage.com Amiga99 Show Mama, I wanna be a coder! Otvorena pisma (??) Nagradna igra DOSTA !!!! |
Evo još jedne programerski orijentirane serije članaka, pored male škole C-a. Za razliku od nje, ova će se baviti programiranjem u najnižem programskom jeziku, mašinskom jeziku tj. mašincu. Prvih nekoliko tekstova će biti namijenjeno totalnim početnicima. Također, htio bih da napomenem da će naglasak u nešto kasnijim tekstovima biti stavljen na programiranje raznih stvari primjenjivih u demoima, kao što su mod-player rutine, c2p, razni efekti, optimizacije, itd. Pa da počnemo... PROCESORAmiga u sebi sadrži Motorolin procesor iz serije MC680x0, a u novije vrijeme i PPC60x. Mi ćemo se baviti samo programiranjem 68k procesora i to MC68020 ili novijim, jer su prethodni procesori ipak zastarjeli. Motorole MC68k imaju šesnaest 32-bitnih registara, od toga je osam namijenjeno za smiještanje i manipulaciju podacima, dok je ostatak zadužen za rad s memorijskim adresama. Data registri, tj. registri za podatke su registri koji nose nazive od D0 do D7, dok su adresni od A0 do A7, pri tom treba imati na umu da se A7 koristi kao sistemski pokazivač steka, pa se najčešce ne koristi u druge svrhe. A7 se može označiti i kao SP (eng. steck pointer). Inače, stek je dio memorije koji je rezerviran za privremeno smiještanje podataka. Pored ovih šesnaest registara postoji i PC (eng. program counter) registar koji uvijek sadrži adresu instrukcije koja tek treba da se izvrši. I poslijednji SR (eng. status register) sadrži bitove koji označavaju "stanje" u kojem se procesor nalazi. To su takozvani flegovi (eng. flegs), uz pomoć kojih npr. procesor "zna" da jedan registar sadrži veći broj od drugog, da je broj u nekom registru negativan, itd. Zašto su registri uopće potrebni? Razlog leži u tome što kada procesor pristupa određenom podatku u memoriji, potrebno je neko vrijeme da bi se to dogodilo. To vrijeme je uslovljeno fizičkom distancom, što će reći registri se nalaze u samom procesoru tako da će on znatno brže raditi s njima nego s podacima iz memorije. BROJNI SISTEMI68k procesor ne mora da obrađuje uvijek svih 32 bita nekog registra. Može da radi i samo u prvih 16 bitova ili samo u privih 8. Inače, kao što znate, 8 bitova čini BAJT, dok 16 čini WORD, a 32 bita daju LONGWORD. Kod programiranja u mašincu, najčešce se koriste binarni i heksadecimalni brojni sistemi. Ako ih usporedimo s decimalnim sistemom, primjetiti ćemo da je praktično jedina razlika u osnovi, koja je kod decimalnog 10. Kod binarnog sistema osnova je 2, a kod heksadecimalnog 16. Da pojednostavimo, zamislite da imamo brojač posjećenosti nekog sajta na internetu i on izgleda ovako 0000. Ako za njega koristimo decimalni sistem, u kome imamo cifre od 0 do 9, nakon stanja brojača 0009 dolazi 0010. Sto se ustvari dogodilo? Prva znamenka (uvijek gledamo sa desne strane) je prešla svoj maksimum (9) i opet se vratila na svoju početnu vrijednost (0), a znamenka desno od nje se uvecala za jedan. Na isti način funkcionira i binarni sistem, s tom razlikom što postoje samo dvije znamenke 0 i 1. Znači početni brojač pokazuje 0000, zatim 0001, pa onda 0010, 0011, 0100, 0101, 0110, 0111, 1000, i tako dalje. Da bi ste broj iz binarnog sistema pretvorili u decimalni potrebno je da znate da setovani bitovi, odnosno bitovi čija je vrijednost 1, imaju vrijednost 2^n, gdjee je n redni broj bita, s tim da je prvi bit sa desne strane nulti bit tj. n=0. Dobijene vrijednosti setovanih bitova se zbroje i eto decimalnog broja. Na primjer %01010110 = 2^1+2^2+2^4+2^6 = 2+4+16+64 = 86. Ispred binarnog broja se nalazi znak za posto (%) kojim se u asembleru označava binarni broj. Analogno ovim sistemima, heksadecimalni brojni sistem ima 16 znamenki, to su brojevi od 0 do 9 i slova od A do F. Tako je A u heksadecimalnom sistemu isto što i 10 u decimalnom a F isto što i 15. Ovakvi brojevi se obilježavaju predznakom dolara ($). Npr. $73F26D36 = %110110100110110 = 1945267510. ASEMBLEROdavno je prošlo vrijeme, kada su se mašinski programi pisali unošenjem kodova instrukcija. Zatim su se pojavili monitori, programi u kojima se moglo pisati samo direktno na određenim memorijskim adresama. A nakon njih pojavili su se asembleri, programi koji služe za prevođenje tekstualnog sors koda (eng. source code) u procesoru razumljiv, mašinski, jezik. Ali i dan danas su se zadržali nazivi kodiranje i programiranje u mašincu, ali se oni ne odnose na gore pomenute situacije već na rad u asembleru. Jedna od rijetkih sfera u kojoj Amiga danas ne oskudijeva su asembleri. Neki od njih su PhxAss, StormASM, AsmOne, AsmPro,... Koji asembler ćete koristiti je vaša odluka, samo nemojte koristiti stare asemblere tipa MasterSeka i sl. Ja koristim AsmPro koji je nezvanični nasljednik AsmOne-a. On u sebi ima integriran veoma dobar editor, disasembler, debugger i još neke veoma fine stvarčice, ali nemojte pomisliti da želim da utičem na vašu odluku. Ni slučajno. =) (PhxAss i AsmPro mozete naci i na aminetu.) Najbolje bi bilo da dok pratite ove tekstove ipak koristite AsmPro jer on prikazuje sadržaje registara nakon izvršenja programa. Evo i kratkog opisa AsmPro-a (AsmOne). Sve opcije se nalaze u menijima ali svakoj možete pristupiti i sa tastature. Na samom startu, program vas pita koliko memorije želite da alocirate i kog tipa treba da bude memorija. Izaberite PUBLIC memoriju. Onda će asembler koristiti FAST memoriju ukoliko je imate, ako ne onda ČIP. Da ste odabrali ČIP i FAST tip onda bi asembler koristio isključivo tu vrstu memorije. ABSOLUTE će koristiti fiksne memorijske adrese, zato ovo izbjegavajte, bar za sada. Sa ESC ulazite i izlazite iz editora i komandne linije. U njoj možete zadavati naredbe npr. A (asembliranje), R (ucitavane sorsa), W (snimanje), itd. Znači, kada napišete program, pređite u komandnu liniju, otkucajte A (i enter naravno), ako je sve uredu, sa J startujete program s početka sorsa, ili sa J [ime] od labele. Nakon završetka vašeg programa pojaviti će se spisak svih registara i njihovih vrijednosti. INSTRUKCIJENaš prvi program neće biti HelloWorld jer bi takav primjer imao isuviše kompliciran source za početak. Zato krećemo s nečim jednostavnijim, npr. zbrajanje dva broja.
*********** amigastorm! ********************* start ;labela move.l #$10,d0 ;stavlja $10 u D0 move.l #$20,d1 ;stavlja $20 u D1 add.l d0,d1 ;zbraja sadržaj D0 sa D1, rezultat ide u D1 rts ;izlazi iz programa ********************************************* Asemblirajte i startujte program. Dobiti ćete:
D0: 00000010 00000030 .... A0: ......... ..... Rezultat zbrajanje je $30 i nalazi se u registru D1. Kao što ste mogli da primjetite komentari se pišu iza zvjezdice (*) i iza znaka točka-zarez (;). "start" je labela, s koje ste mogli startovati vaš program sa "J start" ali to nije potrebno jer se "start" nalazi na početku sourcea. Kao što sama riječ kaže, MOVE (eng. pomjeri) "pomjera" broj $10 u registar D0. Taraba (#) ispred $10 označava da treba staviti baš vrijednost koja se nalazi iza nje u D0. Isto tako u D1 se stavlja vrijednost $20. Zatim se one instrukcijom ADD.L D0,D1 zbrajaju i to tako da se sadržaj registra D0 dodaje sadržaju registra D1. U mašincu je uvijek destinacija, onaj registar koji se nalazi sa desne strane zareza! Znaci ADD.L D0,D1 bi u BASIC-u bilo D1=D1+D0. I na kraju instrukcija RTS (ReTurn from Subroutine) završava izvršavanje programa. Sufiks .L na instrukcijama označava da se radi o LONGWORD-ovima (32 bita), kada bismo pisali sasvim pravilno trebali bi da napišemo: move.l #$00000010,d0 move.l #$00000020,d1 Ali nule se ignoriraju kao i u decimalnom sistemu. Treba da znate da, iako ste napisali samo $20, asembler preostali dio desno popunjava nulama.
*************** još jedan primjer ********** start move.l #$ffffffff,d0 ;stavlja $ffffffff u D0 clr.l d1 ;briše D1 clr.l d2 ;briše D2 clr.l d3 ;briše D3 move.w d0,d1 ;premješta word iz D0 u D1 move.b d0,d2 ;kopira bajt iz D0 u D2 rts ;kraj ********************************************* A što se ovdje dogodilo? Stavljamo $ffffffff u D0 brišemo D1,D2 i D3 i premještamo samo WORD iz D0 u D1 i samo BAJT iz D0 u D2. Registri daju: D0: ffffffff 0000ffff 000000ff 00000000 ... Nije bilo teško zaključiti da sufiksi .W i .B označavaju WORD i BAJT. Ukoliko se sufiks ne navede, podrzumijeva se da su u pitanju WORDovi! CLR sluzi za brisanje data registara, i moze da brise cijeli registar(.l) ili samo word(.w) ili samo bajt(.b) u registru.
*********** another one ********************** start clr.l d0 ;briše D0 clr.l d1 ;D1 clr.l d2 ;D2 clr.l d3 ;D3 clr.l d4 ;i D4 lea text,a0 ;adresu "text" upisuje u A0 lea textend,a1 ;adresu "textend" upisuje u A1 move.b (a0),d0 ;sa adrese "text" kopira bajt u D0, slovo "s" add.l #1,a0 ;uvećava adresu iz A0 za 1 move.b (a0),d1 ;sa adrese "text+1" kopira bajt u D1, slovo "t" sub.l #1,a1 ;umanjuje adresu iz A1 za 1 move.b (a1),d4 ;sa adrese "textend-1" kopira bajt u D4, "m" sub.l #1,a1 ;umanjuje adresu u A1 za 1 move.b (a1),d3 ;kopira bajt, "r" sub.l #1,a1 ;-1 move.b (a1),d2 ;"o" rts ;kraj text dc.b 'storm' ;alocira 5 bajtova i puni ih textend ************************************************ Ovdje se pojavljaju dvije nove instrukcije: LEA i SUB. LEA je instrukcija koja smješta neku adresu u adresni registar, u ovom slučaju adresu "text" u A0, i "textend" u A1. SUB je suprotna instrukcija ADD-u, tj. oduzimanje. "text" i "textend" su labele koje označavaju adrese na kojim počinje i završava se riječ "storm". Sa DC.B direktno u memoriju upisujemo podatke i to bajtove. U ovom slučaju to je 5 bajtova koji čine riječ "storm". Oni su smješteni od adrese "text" do adrese "textend". Kao što vidite, adrese nisu apsolutne, tj. nisu izražene u brojevima. Zamislite da su adrese brojevi, kao na primjer:
$504342 dc.b 'storm' ;text $504347 ;textend Mozda će se nakon asembliranja "storm" baš naći na ovim adresama, ali ovakav source nikad nećete vidjeti. Zašto? Pa ima nekoliko razloga. Prvo, ne radimo u monitoru, u njemu biste morali da na adresu $504342 upišete "storm", a gore gdje piše LEA textend,A1 biste upisali LEA $504347,A1. I šta da promijenite riječ u "amiga storm". Onda biste morali da mijenjate i LEA $504347,A1 u LEA $50434D,A1 jer je riječ duža za 6 bajtova. I zamislite da treba isto uraditi na više od jednog mjesta. U slučaju da se još nešto nalazilo iza $504347, sve to biste morali ručno da pomijerate. Ali u asembleru, se nećete time opterećivati. Druga stvar je u tome što Amiga programe nikada ne učitava na fiksne adrese. Kako biste startali dva programa ako bi se jedan učitao u memoriji od adrese $100 do $500, a drugi od $200 do $500. Drugi bi skoro preklopio prvi što bi najvjerovatnije izazvalo pad sistema. Zbog toga koristimo relativno adresiranje. Sto ustvari ovaj program radi? Briše registre od D0 do D4 (CLR.L). Adresu "text" stavlja u A0, a "textend" u A1 (LEA). Kopira bajt sa adrese koja je smejštena u A0 u D0 (MOVE.B). Uvećava adresu iz A0 za 1 (ADD.L). Kopira ponovo bajt koji se nalazi na adresi, smještenoj u A0, u D1, tj. kopira bajt sa adrese "text+1" (MOVE.B). Oduzima 1 od adrese u A1 (SUB.l) i zatim kopira bajt sa adrese koja je smještena u A1 na D4 (MOVE.B). Ponovo oduzima 1, i kopira opet. I sve to još jednom. Instrukcija MOVE.B (A0),D0 prenosi bajt koji se nalazi na adresi koja je smještena u A0, na D0. Ako se u A0 nalazi adresa $3747 onda ćete u D0 dobiti bajt koji nalazi na toj adresi. Isto biste postigli i instrukcijom MOVE $3747,D0. Dobijeni registri nakon završetka programa izgledaju ovako:
D0: 00000073 00000074 0000006F 00000072 0000006D .... To su ascii kodovi riječi "storm", što možete provjeriti iz komandne linije u AsmPro-u sa "? $73" itd. Ako želite da vidite koliko je dugačak vaš asemblirani program otkucajte "? textend-start" . Do sada smo koristili nekoliko varijacija instrukcije MOVE, pa da vidimo još neke moguce. MOVEInstrukcija MOVE X,Y u suštini premješta sadržaj iz X u Y, a X i Y mogu biti: [legenda:] An - adresni registar (A0-A7) Dn - data registar (D0-D7) [ea] - adresa ili registar #n - apsolutna vrijednost Reg - registar (ili An ili Dn) Evo nekih mogućih načina adresiranja, ali se nećemo zamarati njihovim imenima:
Postoji dosta kombinacija, koje sam preskakao ali one su više nego logične. Ovo nisu sve mogućnosti MOVE komande ali je za ovaj broj sasvim dovoljno. Evo još nekoliko jednostavnijih instrukcija... NOP - instrukcija koja bukvalno ne radi NIšTA sem što čeka dva ciklusa.. JSR labela - Skok na podprogram iz kog se vraca instrukcijom RTS. JSR bi se mogla poistovjetiti s GOSUB naredbom iz BASIC-a. start JSR rutina ;skače na rutinu MOVE.L D0,D1 ;kopira D0 u D1 RTS ;završava program rutina ADD.L #$100,D0 ;uvećava D0 za $100 RTS ;vraća se u glavni dio programa JMP labela - Skok na određenu adresu označenu labelom.
start CLR.L D0 ;briše D0
ADD.L #1,D0 ;povećava D0 za 1
JMP start ;skače na start
Ovaj program stalno uvećava D0 i može se prekinuti samo "nasilno", ubijanjem taska ili resetom. CMP [ea],[ea] - instrukcija koja vrši poređenje i na osnovu njega postavlja neke flegove iz SR registra. CMP #5,D0 ;provjerava da li je D0=5 CMP.B (A0),D1 ;da li je bajt u D1 jednak bajtu na adresi iz A0 CMP.L start,end ;da li je longword na adresi start jednak onom na adresi end BEQ labela - skok na labelu ukoliko je rezultat jednak.
start CMP.B #$A,D4 ;provjerava da li je bajt u d4 jednak $a
BEQ yes ;ako jeste skače na labelu yes
RTS ;ako nije završava se
yes MOVE.l #$ffffffff,D7 ;puni D7
RTS ;kraj
BNE labela - skok na labelu ukoliko je rezultat različit.
run MOVE #50,D0 ;stvlja 50 u D0
LEA buffer,A0 ;adresu buffer u A0
loop MOVE.L #$01010101,(A0)+ ;puni longwordom adresu na A0 i uvećava je za 4
SUB #1,D0 ;oduzima 1 od D0
CMP #0,D0 ;upoređuje D0 sa nulom
BNE loop ;ako nije nula skače na loop
RTS ;ako jeste nula završava se
buffer dcb.l 0,50 ;alocira 50 longwordova i ispunjava ih nulama
Ovo je bio primjer petlje koja puni 50 longwordova. Oni koji već imaju iskustva s asemblerom će primjetiti da se mnogi sorsovi mogu napisati kraće i da mogu biti brži, ali još uvijek ne znamo sve instrukcije, a o optimiziranju koda će se govoriti u nešto kasnijim tekstovima. Za sada smo obradili CLR, ADD, SUB, RTS, MOVE, LEA, NOP, JSR, JMP, CMP, BEQ, BNE i komande asemblera za upisivanje u memoriju DC.x i DCB.x. Od ovih instrukcija se ne moze napraviti bog zna kakav program, ali igrajte se što više s ovim instrukcijama da slijedećeg broja Amiga STORMa. U njemu će biti detaljnije opisana pomenute instrukcije i još neke. Za kraj evo još jednog programčica koji mijenja boje na ekranu. *********************************** final_test lea boje,a0 ;stavlja adresu tabele sa bojama u a0 move #8,d1 ;stavlja 8 u d1 .l1 move (a0,d1.w*2),d0 ;u d0 kopira word sa adrese iz a0+d1*2 jsr color ;skače na rutinu za postavljanje boje sub #1,d1 ;oduzima 1 od d1 cmp #-1,d1 ;ukoliko je -1 u d1 beq kraj ;skače na labelu kraj nop ;ukoliko nije -1 nop ;3 puta ne radi ništa =) tj. čeka 6. ciklusa nop ;(nikad nemojte praviti ovakvo čekanje jer nisu ;svi procesori jednako brzi, ovo je samo primjer) jmp .l1 ;skače na .l1 (labela s točkom ispred je interna ;labela) kraj rts color lea $dff000,a6 ;stavlja adresu $dff000 u a6 move.l #$fffff,d7 ;$fffff na d7 .l1 move.w d0,$180(a6) ;boju iz d0 na $dff180 (boja pozadine) move.w d0,$182(a6) ;d0 na $dff182 (1. boja) move.w d0,$184(a6) ;$dff184... move.w d0,$186(a6) ;.... sub.l #1,d7 ;oduzima #1 od d7 cmp.l #0,d7 ;da li je 0 u d7? bne .l1 ;Ne, onda skok na .l1 (kao što vidite ista labela ;se pojavljuje i ovdje, ali ona pripada color rutini ;Između dvije globalne labele (final_test,kraj,color..) ;može da se nalazi više internih na koje možete ;skakati samo između te dvije globalne. rts ;Ako je 0 u d7, povratak iz rutine boje dc.w $f00 ;boje su zapisane u RGB formatu, bajt za svaku dc.w $0f0 ;komponentu dc.w $00f dc.w $000 dc.w $fff dc.w $0ff dc.w $f0f dc.w $ff0 ************************************* Sve komentare i pitanja i prijedloge, o čemu biste željeli da pišem, možete poslati na email: cyborg@army.net.
|
|||||