88702155 programiranje u assembleru

37
Programiranje u Assembleru Uvod: Pokušati ću ispričati priču o tom zanimljivom načinu programiranja iz kuta kako sam ga ja doživio i kako ja gledam na njega. Ovo neće biti prepisivanje iz neke knjige ili sa raznih stranica na internetu. Tko želi može uvjek pročitati veoma zanimljivu knjigu "Art of programming". Sve one koji poznaju taj jezik, možda i nije pravi izraz, neka se uključe svojim komentarima i primjedbama te pomognu da se dobije što kvalitetniji uvod u taj način programiranja. Obično, kad se pogleda čak i najjednostavniji niz naredbi koji čine neku smislenu cjelinu u assembleru, većina tad pomisli kako je to strašno kompicirano i potpuno nerazumljivo u usporedbi sa naredbama u višim programskim jezicima. Tu obično i prestaje bilo kakvo zanimanje za taj,računalu najbliži, način programiranja. Code: Select all avr0014: ldi r17, 0xCB rcall avr002A lsr r18 dec r16 brne avr000F ret Code: Select all int uart_getc(void) { if (bit_is_set(UCSRA, RXC) == 0) return -1; return UDR; } Gornji primjeri možda nisu najbolji no pozlužit će da se vidi ta razlika. Vjerujem da je i laiku jasno koja je rutina pisana u asembleru. Krenimo na upoznavanje s assemblerom, zapravo onim što bi bilo dobro da razumijemo prije nego se upustimo u izradu programa tim jezikom (kako nevolim taj izraz). To je najjednostavniji jezik. Zapravo i nije jezik, više je jednostavna matrica koja text prevodi u narebe razumljive računalu, objektni kod. Njegova sintaksa je jednostavna i može se pisati u bilo kojem programu za obradu teksta. Assembler (sastavljač) je u principu jednostavno i napisat, pod uvjetom da imamo naredbe objektnog koda za dati stroj. Moderni asembleri su kompleksniji i omogućuju upotrebu makro naredbi kao i uključivanje koda drugih programa pisanih u asembleru, objektnom kodu, itd... Najčešće ih nazivju Macroassemblerima. Za rad u assembleru potrebno je poznavati računalo, procesor... ,njegovu internu arhitekturu, za kojeg pišemo program. To je ključno za čak i za jednostavne programe.

Upload: petar-ursic

Post on 09-Aug-2015

67 views

Category:

Documents


7 download

TRANSCRIPT

Page 1: 88702155 Programiranje u Assembleru

Programiranje u Assembleru

Uvod:

Pokušati ću ispričati priču o tom zanimljivom načinu programiranja iz kuta kako sam ga ja doživio i kako ja gledam na njega. Ovo neće biti prepisivanje iz neke knjige ili sa raznih stranica na internetu. Tko želi može uvjek pročitati veoma zanimljivu knjigu "Art of programming".

Sve one koji poznaju taj jezik, možda i nije pravi izraz, neka se uključe svojim komentarima i primjedbama te pomognu da se dobije što kvalitetniji uvod u taj način programiranja.

Obično, kad se pogleda čak i najjednostavniji niz naredbi koji čine neku smislenu cjelinu u assembleru, većina tad pomisli kako je to strašno kompicirano i potpuno nerazumljivo u usporedbi sa naredbama u višim programskim jezicima. Tu obično i prestaje bilo kakvo zanimanje za taj,računalu najbliži, način programiranja.Code: Select all

avr0014: ldi r17, 0xCB rcall avr002A lsr r18 dec r16 brne avr000F ret

Code: Select allint uart_getc(void){ if (bit_is_set(UCSRA, RXC) == 0) return -1; return UDR;}

Gornji primjeri možda nisu najbolji no pozlužit će da se vidi ta razlika. Vjerujem da je i laiku jasno koja je rutina pisana u asembleru.

Krenimo na upoznavanje s assemblerom, zapravo onim što bi bilo dobro da razumijemo prije nego se upustimo u izradu programa tim jezikom (kako nevolim taj izraz). To je najjednostavniji jezik. Zapravo i nije jezik, više je jednostavna matrica koja text prevodi u narebe razumljive računalu, objektni kod. Njegova sintaksa je jednostavna i može se pisati u bilo kojem programu za obradu teksta.

Assembler (sastavljač) je u principu jednostavno i napisat, pod uvjetom da imamo naredbe objektnog koda za dati stroj.Moderni asembleri su kompleksniji i omogućuju upotrebu makro naredbi kao i uključivanje koda drugih programa pisanih u asembleru, objektnom kodu, itd... Najčešće ih nazivju Macroassemblerima.

Za rad u assembleru potrebno je poznavati računalo, procesor... ,njegovu internu arhitekturu, za kojeg pišemo program. To je ključno za čak i za jednostavne programe.

Page 2: 88702155 Programiranje u Assembleru

Malo o računalu: Vjerujem da mnogi od vas poznaju osnove računala. Postoji i vrlo kvalitetna literatura. Računalo je u svojoj osnovi prost stroj. Iako mnogi nemisle tako, no to je zbog nepoznavanja načina njegova rada.

Za programera u asembleru je važno da zna broj radnih registara, veličinu, dubinu i tip stoga, adresni prostor prg. mem., načine adresiranja, veličinu rama, strukturu naredbi,broj ciklusa izvršenja naredbe, trajanje uzmi/izvrši ciklusa, broj prekida, prekidni vektor, internu sabirnicu, vanjsku sabirnicu... Ima toga puno.

Mikroračunalo:Pošto ćemo obradit programiranje u assembleru za 8 bitno mikroračunalo Harvardskog tipa i to MCU (mikrokontroler) serije AVR/Mega moramo znati da je dubina programske memorije, memorije podataka i rama zadana samim HW-om. Kod rama postoji mogućnost ograničenog proširenja na uštrb nekih drugih funkcija, no otom -potom.

Veličina polja registara najčešće je proširena na ram. No skup radnih regstara koji neposredno sudjeluju u operaciji a ALU-om (arihmetičo-logička jedinica) je fiksan i on ovisi o tipu jezgre (processoru). Npr PIC 16xxx serija ima klasičnu akumulatorski orijentiranu jezgru. Sve operacije s ALU, ali ne s samo s njom, se odvijaju preko jednog registra (akumulatora - W). AVR/Mega jezgre imaju moderniju arhitekturu i rade s 32 registra no 16 rade sve operacije s tim da tri para registara mogu radit u 16 bitnom modu. Tu je i polje tkzv. registara specijane namjene koje je dio RAM-a. O detaljima kasnije.

Obje serije imaju tkzv. RISC klasu procesora, reducirani skup naredbi. Bez brige, čak i tako možete odratit sve što poželite. Dali RISC ili CISC - kompleksni skup naredbi,nije na kraju toliko bitno.

Pošto je to MCU on u svojoj arhitekturi ima na internu sabirnicu spojene i HW sklopove: ADC, komunikacione sklopove (hardverski implemet njihovih protokola i registre za njihovu kontrolu i razmjenu podataka) itd... Širok je spektar onoga što je postavljeno u ta mala računala. Tu su još i nezaobilazni konfiguracijski registri koji predefiniraju modalitete rada i ponašanja MCU-a.

Mislim da je to dovoljno za uvod. Početak upoznavanja sa programiranjem u asembleru.

P.S. Bude li zainteresiranih za tu temu nastavit ću započetu priču. Molim vas da za komentare i kritike pišete u nekom drugom topic-u.

Prije nego odemo pojašnjavat vezu arhitekture mikroračunala i asemblera nebi bilo loše da raščistimo jednu veoma važnu stvar a to je pojam vremena. Čovijek, kao živo biće a ne kronometar, vrijeme doživljava i proživljava pa mu je ono, koliko god satovi bili točni, promjenjivo. Ne traje nam sekunda, minuta, sat... uvijek isto.

Vjerujem da se većina zapitala koliko zaista traje milisekunda. Dok nisam počeo pisat programe u assembleru osjećaj vremena je stalno "klizio iz ruku". Što hoću reći, učeći assembler osjećaj vremena postaje stvarniji. Ne samo da razumijete

Page 3: 88702155 Programiranje u Assembleru

"užasno spore" mili sekunde već i prilično duge delaye od 100 microS. Sekunde da ni ne spominjem. A sa mikrosekunadama i nano sekundama ste na ti i sve bolje razumijete vrijeme. Bez toga bi rad u assembleru bio gotovo nemoguć. Imali bi velikih problema.

Kompjuteru je vremenska baza jako važna stvar. Vidjeli ste često, u raznim alatima za pisanje programa u višim programskim jezicima, kako vas uredno pitaju da im navedete frekvenciju oscilatora. Bez toga bi izračunati delayi bili potpuno krivi i vaš program bi u najbolju ruku "štekao".

U assembleru nema tko računati delaye za vas (ima nekih programa kao AVR kalkulator no bilo bi dobro da ga ne koristite jer on ionako računa samo vrijednosti za timere, dali obični dali PWM mod). Znači najprije treba vidjeti koja je vremenska baza takt oscilatora processora.

Oscilatoru od 1 MHz takt je 1 microS (to znamo). No to je tek početak. Da bi nam taj podatak ičemu služio moramo znati koliko takt impulsa koristi bazična operacija jezgre računala a to je uzmi/izvrši, koja se još naziva i ciklus. Jezgre AVR/Mega imaju jednotaktni uzmi/izvrši ili ti ciklus. Jezgre PIC16xxx serije) imaju 4 taktni ciklus. Znači da će AVR/Mega jezgra 4 x brže odraditi ciklus od PIC-a za istu brzinu takt oscilatora.

Nije to naravno kraj priče o vremenu. Sa gore navedenim podacima ćete se susretati kod izračuna vrijednosti za HW-ski implementirane timere (bilo koji mod), no za emulirane delaye, kad nemate dovoljno timera ili nesmijete iz nekog razloga postavljati prekid u aktivni mod, ili vam, kad zbrojite kašnjenje rutine, ispadne da je brže emulirati nego setirati timere, itd... tada ćete kao podlogu za izračun vrijednosti delaya morati koristiti nešto drugo. To nešto je vrijeme izvršenja instrukcije. Zvuči komplicirano? Videjti ćete da nije ako se samo malo potrudite razumjeti.

U svim dokumentima o processorima, mikroračunalima itd... jedna od važnih stvari jest i tablica naredbi. Uredno poredane po funkciji ili abecednom redu čekaju da ih koristite. Jedna stvar vam nikako nebi smjela promaknuti, vrijeme izvršenja naredbe. Uz svaku naredbu, osim njenog opisa, stoji i koliki broj ciklusa traje. Sad vam je, nadam se, jasno čemu je gornji dio teksta služio.

Većina naredbi AVR/Mega jezgre traje između 1 najviše 2 ciklusa. Opracije dugog granjana, indirektnog skoka u rutinu ili poziva na istu 3-4 ciklusa, rijetko koja duže. Najveći broj naredbi traje jedan ciklus kao i kod PIC16xxx serije.

E sad smo napokon došli do korisnog podatka za izračun utrošenog vremena mikroračunala za određenu rutinu. Jel vas to podsjeća na delay? Nije tu u pitanju samo kašnjenje već puno važnije stvari, a to su vremenski kritične operacije. Programi, češće rutine, koji se moraju izvršiti u točno određeneom vremenu.

Mikroračunalo je ograničeno, arhitekturom, maximalnom freq -> taktom, vremenom trajanja ciklusa... da bi te ograničenosti izbjegli, ako je ikako moguće, poznavanje vremena izvrešenja naredbe pomoći će vam da vidite što će proći a što neće i da koristite one naredbe koje će maksimalno uštedjeti na vremenu, ili što je puno bolje razmislite o načinu postavljanja problema na drugi način.

Primjer naredbi i ciklusa: AVR/Mega jezgre: ldi r17, 1 (neposredno punjenje registra) --> ldi = 1 ciklus ==> 1 x

Page 4: 88702155 Programiranje u Assembleru

(uzmi/izvrši x 1 takt).PIC 16Fxx jezgra: movlw 8 (neposredno punjenje acc) --> movlw = 1 ciklus ==> 1 x (uzmi/izvrši x 4 takta).

AVR/Mega je 4x brža za isti generator takta. Vrijeme trajanja ciklusa je na 1MHz = 1 microS kod PIC16xxx je 4 mikroS. Eto, na kraju se vraćamo na početak, frekvenciju, no, nadam se, s jasnijom predodžbom kako funkcionira vrijeme u našem računalu.

Već kad smo kod vremena da spomenemo taj pojam: MIPS s kojim ćete se susretati u dokumentacijama računala (nemislim na poznatog proizvođača prosessora). Nastao je puno ranije i označava broj miliona instrukcija u sekundi. AVR/Mega na freq 1 MHz može maksimalno da "vozi" 1 MIPS, PIC16xxx seiji za istu brzinu izvođenja operacija treba 4MHz. To su idealne veličine i u praksi su nešto manje.

Nebih želio da me ljubitelji PIC16xxx serije krivo razumiju, jer nemislim da je zbog gore navedenih podataka ta serija MCU-a manje vrijedna. Svako mikroračunalo, ako je pravilno korišteno za ono što realno može njegov HW, je dobro. Najčešće problem predstavljaju programeri. Možda ova priča o assembleru pomogne da se pojasni važnost poznavanja detalja rada mikroračunala i okoline za koju program pišemo.

Arhitektura mikroracunala.Ni u ovom dijelu price necemo obradivati detalje kompletne arhitekture pojedinih serija mikroracunala. Nije cilj ovog dijela da C/P-a bilo ciju tehnicku dokumentaciju vec da pokuša proniknuti u tajnu veze izmedu svijeta hardwera i softwera, a jedna od najneposrednijih veza jest asembler.

Taj nacin programiranja je zaista specifican a to se ogleda i na vezi imedu naredbi i strukture samog mikroracunala za kojeg pišemo program. Pošto obradujemo mikroracunalo a ne procesor kao poseban sklop tako je i assembler za mikroracunalo kompleksniji i osebujniji proporcionalno kompleksnosti samog hardwera mirkoracunala od onoga što je sama jezgra. Naredbe asemblera prilagodene su samoj arhitekturi tog malog "cuda".

Primjer:Primjetili ste kako ja odvajam seriju AVR od ATMega serije iako u tehnickoj dokumentaciji i deklaraciji proizvodaca jasno trpaju ATmega mikroracunalo kao AVR strukturu. U cemu je problem? Razlika.

Naizgled je nema, vodi se Mega jezgra kao AVR, jasno i nedvosmisleno. No... Ima jedna naredba koja je dodana AVR jezgri kakvu imaju AT90Sxxxx... ta serija. Ta naredba se zove SPM. Netko ce reci pa što je tu toliko strašno. Jedna naredba pa što?

U raznih proizvodjatja u istoj seriji znaju varirati pokoje naredbe. Za programera u asembleru, ali i drugim jezicima SPM naredba nije samo promjena u mikrpoprogramskom slijedniku, memoriji, a djelomicno i u samoj arhitekturi jezgre, vec i u nacinu na koji možete postavti vaš program globalno. Ta je naredba donijela potpuno nove momente u tu seriju. Pitate se zašto? Ne samo da ona omogucava samoprogramiranje (SPM --> Self Programming Mode) vec je, zbog promjene na arhitektuiri jezgre mirkroracunala omoguceno da napišete bootloader, premjestite prekidne vektore u njega,

Page 5: 88702155 Programiranje u Assembleru

a tu su promjene i na konfiguracijskim registrima,itd...

PIC16Fxxx seija zanimljive je arhitekture i zanimljivih riješenja. Ona nema naredbu slicnu SPM no omogucava primjenom odredenog algoritma, prisanjem posebne rutine, samoprogramiranje. No za razliku od ATMega serije ne prati je sva ona podrška u HW-skoj strukturi koja omogucuje kreiranje bootloader sektora, promjenu pozicije (pocetnu adresu) prekidnog vektora, nacin pristupa zašticenoj zoni bootloadea, itd... Naravno, možemo se poslužiti "virtualnom" kreiranju zone bootloadera u PIC16Fxxx seriji.

Nadam se da vam je ovaj primjer pomogao da vidite kako naizgled "nevažna" promjena, jedna dodana naredba, zapravo znaci i promjenu u strukturi jezgre, vecu ili manju, a to omogucava važnu promjenu u samom programiranju mikroracunala tj. strukture i riješenja samog programa.

Da se vratimo arhitekturi. Na internu sabirnicu mikroracunala vezana je sama jezgra: akumulator( kod PIC16xxx), zona radnih registara (AVR/Mega), SRAM (Staticki RAM; koji je, najcešce, i polje radnih registara, stog i polje registara spec. namijene), PC (programski brojac), adresni brojac (srama, stoga..) Mikroprogramska memorija.

Programska memorija kao i memorija podataka naizgled pripadaju samoj jezgri no nisu baš dio nje. Znam, neki ce prigovoriti da se slicno može reci i za SRAM no pošto je dio SRAM-a predefiniran kao zona radnih registara(AVR/Mega serija) postavio sam ga kao dio jezgre. Opet da kažem o tome se uvijek može popričati.PIC16xxxx serija ima akumulator kao radni registar i poseban stog (dubina je 8 x 12-14bita). Ovo nam govori kako je osnova njegove arhitekture nešto starija, tipski. U SRAMU je smještena zona registara speijalne namijene. Serije se razlikuju, zbog specificnosti arhitekture, i u nacinu sada s adresni brojacem (kako programskim tako i rama). AVR/Mega serija, njen adresni brojac rama, kao i indeksni brojaci, imaju neposredan pristup cijeloj velicini SRAM-a. Kod PIC16xxxx serije brojac srama je 7 bitni + bitovi RP0 i RP1 koji su dio SFR-a (specijalnog registra funkcija) i cine tkzv. adresu bloka. Ima tu još detalja za izracum pozicija srama po blokovima. No to su zaista detalji. U indeksno adresiranje nećemo ulaziti. Bar ne za sad.

Primjer citanja podatka s adrese 0x0110: Code: Select all

- u PIC16F877:BCF STATUS,RP0 ; definiramo blok RP0=0 RP1=2 ==> blok 2BSF STATUS,RP1MOVF 0x10, W ; procitani podatak s adrese rama 0x0110 je u akumulatoru

- u ATm8:lds r17, 0x0110 ; pročitani podatak s adrese 0x0110 je u radnom registru (jednom od 16)

Ta razlicitost u arhitekturi jezgri ima svoju neposredu refleksiju na naredbe u asembleru i nacin upravljanja adresnim brojačem rama. Kod AVR/Mega on je "lineran" dok je kod PIC16xxx serije, kao što vidite, predodreden adresom bloka (ne ulazeci u detalje).

Slično je i kod programske memorije. AVR/Mega imaju za večinu čipova neblokovsko

Page 6: 88702155 Programiranje u Assembleru

adresiranje (linearno) do 64KB (tj. 32KW) dok PIC16xxx serija ima podijeljenu programsku memoriju u manje blokove (stranice od 2KB, npr. 16F877). U Mega seriji na MCU-u ATMega128 (ili ATmega104) gornjih 64kB se definira statusom RAMPZ bita.

Primjer za PIC16F877 iščitavanje podatka iz programske memorije u zoni --> org 018fc:

-- rutina za čitanje ID-a memorije (podatka) iz baze podataka u programskoj memorijiCode: Select all

RDID MOVF 0x38,W ; u registru 0x38 nalazi se vrijednost adrese za PCL ( niži byte adrese). BSF PCLATH,3h ; postavljamo adresu zadnjeg bloka (page-a) programske memorije BSF PCLATH,4h CALL IDVAL ; pozivamo rutinu koja isčitava podatak iz baza ID-a memorija programatora BCF PCLATH,4h ; vraćamo se nazad u prvi blok programske memorije BCF PCLATH,3h . .

ORG 018FC ;IDVAL BCF PCLATH,2h ; postavljamo početne parametre 11 bitnog adresnog brojača --> 2K blok BCF PCLATH,1h BSF PCLATH,0h ; niža 3 od 5 PCLATH bita MOVWF PCL ; adresa u bloku je ==> 3 niža PCLATH bita + 8 bita PCL-a. RETLW 0xC2 ; podatak, jedan od, iz tabele. RETLW 0x00 RETLW 0x00 RETLW 0x00 .

Primjer za ATMega8:Code: Select all

ldi ZH, 0x08 ; čitamo podatak iz zone programske memorije na adresi 0x0800ldi ZL, 0x00 ; postavljamo u indeksne brojače ZH, ZL adresu lpm ; čitamo podatak iz programske memorije sa željene adrese / rezultat je u r0

I na ovom primjeru vidite koliko arhitekura mikroračunala utječe na strukturu i tip naredbi asemblera. A to znači i samo dizaniranje programskih rutina.

Ostaje nam još ono zbog cega je mikrokontroler (MCU), to malo mikroracunalo, specificno. HW-ski I/O (Ulazno/izlani) sklopovi dodani na samu sabirnicu cine ga posebnim. Taj dio cine I/O portovi (vrata) a pošto je to MCU-a tu su i I/O spec namjene. Razni komunikacioni inteface-i(sucelja), ADC-i, Timeri, PWM, Analogni komparatori, itd... Zbog tih sklopova asembler je zaista specifican svakoj seriji. Svakako da nije to jedini razlog posebnosti asemblera od serije do serije, vec je to i zbog same arhitekture jezgre, a koji put i zbog samog naziva naredbe/i.

Page 7: 88702155 Programiranje u Assembleru

Primjer razlike u nazivima istih naredbi (gledano u grubo):Code: Select all

ldi r16, 5 ; neposredno punjenje radnog registra AVR/Megasts 0x61, r16 ; upiši registar u SRAM, lokacija 61h

movlw 5 ; neposredno punjenje akumulatora PIC16xxx serijemovwf 0x61 ; upiši sadržaj registra u sram lokacija 61h

U oba slucaja se radi o funkcionalno istoj rutini. Istina je da te dvije naredbe (sts i movwf) nisu baš iste u potpunosti.

Važno je podsjetiti da se osim HW-skih implemenata za pojedine operacije emulacijom određenih protokola, pisanjem drivera i sl. mogu dodavati nove mogućnosti mikroračunalu. Jedan od primjera za to može biti i emulacija USART protokola a ne korištenje njegovog HW-skog implementa koji često zna biti ograničavajući faktor u upotrebi oscilatora takta kojeg želite ili s istim nemožete ostvariti sve brzine prijenosa standardne, i nestandardne, za taj komunikacioni protokol.

U nastavku ćemo se okrenuti i tom dijelu priče.

Vjerojatno ste se pitali držeći u rukama mikroračunalo kako napisati program za njega, koji način programiranja je najbolji i slična pitanja.Ljudi mnoge stvari pokušavaju napraviti tako da one budu njima što razumljivije, jednostavnije za korištenje pa tako i pisanje programa za računalo. Od samih početaka smišljao se način kako premostiti jaz između onoga što računalo zaista "razumije" i željom za stvaranjem jezika koji će biti blizak ljudima. Vrlo brzo je stvoren simbolički jezik, najjednostavniji način programiranja koji nije objektni kod (pisanje naredbi u hexadecimalnom sustavu) a opet je razumljiv čovijeku, assembler.

Nije se tu stalo pa su "izmišljeni" (mislim da je to jedini pravi izraz) brojni viši programski jezici (HPL). Jezici koji su daleko bliži čovijeku no imaju jednu manu, programer drži kontrolu nad programom dok radi sa samim alatom a čim stisne "tipku" : compile, make... kontrolu preuzima program prevodilac.

Assembler nema taj problem zbog same prirode tog načina programiranja. Mnogi programeri u HPL-u će reći kako je njihov kod prenosiv. Istina, no najveći broj njih ili nezna ili, najčešće, ne želi znati koja je cijena "prenosivosti". Ako se tome pridoda da se većina programa prenosi unutar porodice mikroračunala za koju je kod pisan tad stvar postaje još ozbiljnija.

Česta priča, koju sam slušao, kako je program pisan u assembleru teško prenosiv je samo polovično točna a u praksi veoma rijetka. Mislim da netreba previše pojašnjavati. No, za one koji misle da treba, često se programi za mikroračunala ne prenose s jedne potpuno različite porodice na drugu; PIC16xxx na ATmegu, ST710 ili obrnuto. Pogotovo je to jasno kod mikroračunala specijalne namjene. Pošto je ova priča o assembleru vezana na mala osam bitna mikroračunala, MCU-e to je razlog da popričamo o programiranju u asembleru veći.

Nećemo se bavit paljenjem "lampica", to programeru u assembleru nebi trebao predstavljat problem već iz same filozofije tog načina programiranja. Prvi zadatak je upoznati se sa tehničkom dokumentacijom mikroračunala s kojim želimo raditi (i njegovom porodicom).

Page 8: 88702155 Programiranje u Assembleru

Znam i vidim da su mnogi nestrpljivi kad kupe ili dobiju mikrokontroler i prvo što naprave nije čitanje tehničke dokumentacije već šetanje po razno-raznim boardima i pokušaj da nešto naprave a da pritom uopće neznaju što je to računalo, kako to radi itd... No to je njihov problem.

Pošto smo odlučili da ćemo se upoznati s assemblerom za porodicu AVR/Mega držati ćemo se njenog hardwera. Znači prvi korak je upoznavanje s arhitekturom mikroračunala iz te porodice. U ranijim tekstovima upoznali smo se djelomično s temeljem te arhitekture no kroz kasniji tekst i primjere upoznati čemo se sa realnim strojem.

Iz dokumentacije je jasno da je jezgra računalo Harwardskog tipa, što je rečeno još na početku, a to znači da imamo jasno odvojenu programsku memoriju od memorije podataka. Dodaci u macroassembleru kao .db (niz jednobaytnih podataka) .dw (niz 16bitnih podataka u intel hex formatu --> reversni) koji označavaju podatke kao i naredba lpm (Load Program Memeory --> isčitavanje iz programske memorije ) samo potvrđuju strogu odijeljenost podataka od naredbi. Takav tip računala nam dosta olakšava posao oko programiranja.

Primjeri:Code: Select all

.dw 0x773F, 0x2518, 0x8000, 0x0014, 0x6854

.db 0x84,"SLOT ",0x85,"FREQ ",0x86,"U MODE "

Polje rama kao i EEPROM-a, su isključivo vezani na čuvanje podataka. Pisanjem algoritama za samoprogramiranje i te se memorije mogu pretvoriti u neku vrst memorije za trajno ili privremeno čuvanje programa.

Možda se pitate može li assembler da bude modularni kao i viši programski jezici. Odgovor je može! To potvrđuje najčešće prva nareba, ne samo macroassemblera,: .include "Xxxxdef.inc". U module mogu biti uključene i definicije kao i asm. programi,itd... No poanta pisanja programa u assembleru nije modularnost kao prioritet već efikasnost. Na kraju krajeva sve te definicije ili module možete uključiti u svoj program običnim kopiranjem, C/P.

Najveća vrijednost assemblera je u tome (po meni) što možete veoma precizno i jednostavno upravljati strukturom programa, rutinama, registrima, poljem rama, kompletnim HW-rom mikroračunala kao i njegovim okruženjem. Svim onim što će omogučiti da vaš program bude jednostavniji, brži, efikacniji... U svakom trenutku točno znate kako bi trebao sustav reagirati na naredbu, u kom pravcu će se odvijati vaš program u svakoj točki. U HPL-ima to je apsolutno nemoguće bez disasembliranja i analize programa (reinžinjeringa). Nikakvi simulatori neće dati točnu sliku stanja onoga što se zaista odvija u kompiliranom objektnom kodu. Da ne spominjemo prekomjernu upotrebu stoga i već znanu priču oko korištenja radnih registara (push / pop naredbe). Tu su i tkzv. duh rutine koja jedna drugu pozivaju potpuno nepotrebno itd.. Taj problem programer u assembleru nema, nepostoji. Primjetiti ćete vrlo brzo kako je rukovanje i upotreba skupom radnih registara u assembleru daleko kvalitetni a time odvijanje programa. Da ne ulazimo u strukturu programa i programskih rutina. Jasno je da programer u višem programskom jeziku, ni uz najbolju volju, nemože znati što će compiler uraditi od njegova koda. Nadam se da vam je jasno kako se ni jedan jedini znani viši proramski jezik nemože mjeriti s assemblerom.

U daljnjim tekstu pokušati ćemo pojasniti zašto je to tako.

Page 9: 88702155 Programiranje u Assembleru

Krenimo na posao. Spomenuti ćemo, iako mislim da je to jasno od ranije, kako je važno da odaberemo MCU koji će zadovoljavati kriterije potrebne da bi naš sustav radio kako treba i da bi našli najbolji balans između HW i SW. Nije to baš tako jednostavno, bar ne uvijek. Pretpostavljam što će neki reći; mi smo programeri. Mnogi moji znanci ponosno su isticali kako oni nemaju veze s HW, sve to drugi riješe a oni rade isključivo SW. Pogotovo kod nas je to poprimilo maha poodavno, valjda zato što nemamo vlastitu elektroničku industriju. Zapadnije od nas je malo drugačija precepcija odnosa HW/SW.

Pošto smatram programiranje u assembleru najboljim što se može naći u svijetu programiranja za određenu mašinu a ono pretpostavlja poznavanje HW kompletnog uređaja, ne samo mikroračunala, za kojeg se program piše. Često takvi ljudi određuju balans između HW-skih i SW-skih riješenja samog uređaja. Što će biti emulirano, što neće i na koji način. Znam, reći čete da smo otišli u dizajn samog sustava. Točno! Iz mnogih upita po ovom boardu, i ne samo ovom, vidi se da neki ljudi i pored znanja iz programiranja ne poznaju HW, tehnologiju u kojoj je MCU rađen pa to zna dovest do problema čak i kad je rutina dobro dizajnirana.

Vratimo se našem mikroračunalu. Recimo da našem uređaju odgovara ATmega8, ili nemamo ništa bolje pri ruci. Naravno, nemamo uvijek sve MCU-e na raspolaganu ili nam je koji put mikroračunalo kojeg imamo, uz manje preinake u dizajnu HW uređaja, sasvim zadovoljavajuće.

Odabrali smo MCU, dizajnirali HW, odredili što će biti čime riješeno i sad treba započeti pisati program da odradi trženi posao. Vjerojatno ste se prvi put pitali kako početi. Iako mnogi alati (macroassembleri) omogućuju kod otvaranja projekta da za vas kreiraju mali uvod na početku. Pođimo nekim redom. Mali primjer kako početi s dokumentiranjem.

Code: Select all;//////////////////////////////////// KOMANDER ///////////////////////////////////////;/// THIS SOURCE CODE IS PRIVATE PROPERTY OF ime prezime AND CAN'T BE USED BY ENYONE WHITHOUT SPETIAL;/// PREMISSION / CODE IS MADED 12.09.2005;/// ; OVAJ IZVORNI KOD NIJE DOZVOLJENO KORISTITI BEZ AUTOROVOG PRISTANKA ! ; SAMO ZA PRIVATNU UPOTREBU !!!;/////////////////////////////////////////////////////////////////////////////////////;/// IZVRSNI PROGRAM KORISTI LCD IZ NOKIJE 3410 KAO PRIKAZNU JEDINICU , KORISTI 4 TASTERA ZA UPRAVLJANJE;/// KORISTI TIMER ZA MINUTE I TIMER ZA LCD PAUZE , BUZZER ZA GENERIRANJE ZVUKA , U EPROMU POHRANJUJE PODATKE O ;/// JEDINICAMA KOJIMA UPRAVLJA / ADR=00 TRENUTNI MAX BROJ ADR01=JED1 ADR02=ACTIVE/TIME [BIT7 0=NEAKTIVAN] 5 BITOVA TIME;/// FuseL==E4 intosc / DE Q FuseH==CC-512W..CC-256W sa bootl /CF bez bootl

ili ovaj:Code: Select all

; un-assembled & TPScrypt code by Threat; private source code for authorised peoples only !!!

Page 10: 88702155 Programiranje u Assembleru

Dokumentacija? Ako su mi išta prigovarali moji prijatelji to je jako loša dokumentiranost programa. Mislim na one beskonačno duge opise što koja rutina radi (čak i one s tri-četiri linije) kojih se grozim. Za mene je to gubljenje vremena. Iz jednostane logike, programeru u assembleru netrebaju opisi svake, i najmanje rutine, da bi razumio čemu ona služi. Ali, vi ipak dokumentirajte rutine da nebi ispalo da vas loše savijetujem. Dijagrami toka? Sad, ako vam je on zaista potreban da bi lakše odradili posao pišite ga.

Da nebi stvari preskakali, već sagledali kompletan kontekst opcija našeg malog računala prije početka pisanja prvih redova naredbi imamo za obaviti jako važan zadatak. Izvršiti definiranje početnih parametara rada našeg MCU-a. Vjerujem da znate na što mislim? Koji tip oscilatora, wdt ili ne, pullup ili ne, bootloader da/ne i koje veličine, zaključavanje ili ne i koji mod, itd... Nadam se da vam je jasno da je naš MCU ( u ovom slučaju ATmega8) ne fiksne strukture. Njegov se HW može podesiti vašim potrebama (naravno ograničeno). Spoj glavnih konfiguracionih bytova, koji se češto upisuju programatarom, iako mogu i iz bootloadera (za Mega seriju, o tome kasnije), i registara specialne namjene konfiguriraju vaš mali stroj po željenim parametrima (ograničeni onim što je proizvođatč ponudio). Upisom konfiguracionih bytova Hifuse, Lowfuse ili kao što zna biti prezentirano u programatorima kao odabir bitova preko maske određujemo početno ponašanje MCU-a.

Recimo da ne želimo trošiti novac na externi oscilator (kristal ili nešto drugo) i želimo da MCU sadi što brže može. Primjer: Code: Select all

FuseLow ==> E4 odabiremo, između ostalog, interniosc (RC) Postavljanjem vrijednosti OSCALL registra na 0xFF, ako je MCU na radnom naponu od 5V, vaš interni oscilator će raditi na 16MHz. Ako je radni napon između 3,1-->3,3V tad će frekvencija biti 14MHz.

Pravilnim odabiramom vrijednosti OSCALL registra možete sasim dobro podesiti takt frekvenciju.

Da, jedna napomena za one koji to možda nisu znali ili misle da nemože, MCU-i u Cmos tehnologijama koji su deklarirani na 5V bez problema će raditi i na 3V3 i na 2V7 a mikrochipovi MCU-i (testirano na seriji 16F) rade dobro na naponu od 2V2 pa čak i na 1V8 i to spuštajući napon napajanja u samom radu MCU-a. Važno je jedno da znate i toga se držite ako sami dizajnirate HW, to je prag logičke "0". Na to je cmos osjetljiv. Ima jako malu toleranciju prema "0". Malo mu treba da prebaci s "0" na "1". To je i razlog one "nevolje" oko reseta za onu Megu168. Eto još jedan od razloga zašto je potrebno ne samo poznavati mirkoračunalo (strukturu) već i tehnologiju u kojoj je rađen, naravno za one koji planiraju, jednom, dizajnirati svoj vlastiti uređaj.

Što dalje? AVR- macroassembler (AVR Studio) počinje kopijom tkzv. startne definicije prije početka oznake za početak programa .cseg a to je u našem slučaju ova naredba assembleru .include "m8def.inc" Ta naredba nije ni ništa drugo već skup definicija imena registara kao i imenovanje MCU-a:

Code: Select all.device ATmega128. (Bila mi je pri ruci);;;********************************************************************

Page 11: 88702155 Programiranje u Assembleru

*********; CPU Register Declarations;*****************************************************************************

.def XL = r26 ; X pointer low

.def XH = r27 ; X pointer high

.def YL = r28 ; Y pointer low

.def YH = r29 ; Y pointer high

.def ZL = r30 ; Z pointer low

.def ZH = r31 ; Z pointer high;;;**** Interrupt Vectors ****.equ INT0addr = $002 ; External Interrupt0 Vector Address.equ INT1addr = $004 ; External Interrupt1 Vector Address.equ INT2addr = $006 ; External Interrupt2 Vector Address.equ INT3addr = $008 ; External Interrupt3 Vector Address.equ INT4addr = $00a ; External Interrupt4 Vector Address.equ INT5addr = $00c ; External Interrupt5 Vector Address.equ INT6addr = $00e ; External Interrupt6 Vector Address.equ INT7addr = $010 ; External Interrupt7 Vector Address.equ OC2addr = $012 ; Output Compare2 Interrupt Vector Address.equ OVF2addr = $014 ; Overflow2 Interrupt Vector Address.equ ICP1addr = $016 ; Input Capture1 Interrupt Vector Address.equ OC1Aaddr = $018 ; Output Compare1A Interrupt Vector Address.equ OC1Baddr = $01a ; Output Compare1B Interrupt Vector Address.equ OVF1addr = $01c ; Overflow1 Interrupt Vector Address.equ OC0addr = $01e ; Output Compare0 Interrupt Vector Address.equ OVF0addr = $020 ; Overflow0 Interrupt Vector Address;itd...

Jasno je da taj dodatak uopće nije potreban osim deklaracije MCU-a (.device ATmega8). Nemojte se isčuđavat jer naš MCU ionako nerazumije imena, no radi nas samih ipak je korisno. Da nebude nikakve zabune vi sami u svom assembleru možete te registre preminovati ili koristiti neki svoj vlastiti "m8def.inc" file ili kako god ga želite nazvati. To je običan text!

Iz ovoga je jasno da imate mogućnost da sami definirate nazive registara, zone rama, zone programske memorije itd,itd... Prije nego prijeđemo na definiranje naziva radnih registara (ako to želimo) moramo učiniti jednu važnu stvar, ono što je često bolna točka viših programskih jezika, dodjeljivanje funkcija tj. imenovanje varijabli i pridruživanje skupu ili pojedinom registru iz skupa radnih registra ili polja rama. Pošto u rutinama stalno trebaju neki od radnih registara (imamo ih 16 potpuno operativnih) za neposredni upis ili prijenos s jednog mjesta na drugo u ramu, eepromu, prg memoriji ( to oko prg memorije kad dođemo na bootloader i samoprogramiranje). Korisno je i definirati kojima od registara nećemo dodijeliti nikakvu funkciju tj. koji će biti tkzv (tmp) slobodni registri (iako mnogi programeri u asembleru imaju svoje "favorite" pa ih znaju napamet i koriste u gotovo svim programima).

Mega jezgre, kao i AVR imaju skup od 32 registara od toga 16 opće namjene no u tih 16 tri su indeksna para registara a od njih je jedan par ZH:ZL, par kojemu je dodijeljena specijala

Page 12: 88702155 Programiranje u Assembleru

funkcija. Samo oni gu biti pokazivači adrese u naredbi indirektnog poziva i indirektnog skoka (icall i ijmp) kao i adresni brojači naredbi lpm, spm... Primjer jedne jednostavne definicije registara na samaom početku programa:Code: Select all

.cseg .def LINEX=r24 .def LINEY=r23 .def TMARK=r21 ; .def inL=r4 .def inH=r5 .def tmL=r6 .def tmH=r7 .def Maxlen=r8 .def COUNT=r2 .def PNS=r10 .def TIMER=r22

.equ Slika1=0x0081 ; pozicije u ramu .equ OldKurs=0x0080 .equ PosBlink=0x0082 ; trenutna pos blinka .equ Anim1pos=0x0085 .equ Anim1old=0x0086

Svakako pokazati ćemo i primjer složenije predefinicije:

Code: Select all.equ FRACT=0x0156 ; trenutna freq .equ SLF0=0x0157 ; .equ SLF5=0x0158 ; .equ YDAT=0x0159 ; podatak za PLL registar // SwB .equ STRLEN=0x015A ; broj znakova u stringu [hexstr pretvorbom] .equ SLC4=0x015B ; komplement slota SLF4 .equ SLOTA=0x015C ; slot za identifikaciju / samo ako ima novi .equ SLOTX=0x015D ; upravo pobrisani slot .equ SPLOW=0x015E ; TMP SPI LOW adr .equ SPHIG=0x015F ; TMP SPI HI adr .equ SPSHA=0x0160 ; TMP SPI SHI adr .equ ROSWN=0x0161 ; nova pozicija rotary switch .equ ROSWO=0x0162 ; stara pozicija rotary switcha .equ MENOD=0x0163 ; odgovori poz hi byte SPI // start 00 eng cita iz SPI .equ MENPO=0x0164 ; postavke menija hi byte SPI .equ ROVAL=0x0165 ; vrijednost dobivena rotary SW algoritmom .equ MEOLD=0x0166 ; stara zadnja pozicija menija u SPI //ZH val .equ MENLO=0x0167 ; postavke menija lo byte SPI .equ METMP=0x0168 ; privremena pos ZLa za sub menije .equ MELNG=0x0169 ; aktivni jezik u meniju .equ RDMST=0x016A ; readme i o uredjaju pocetna najvisa adresa

.equ TIMERX=0xFC17 ; inv timera za 64 miliS prescaler 1024 //03E8 .equ SPIODE=0x0000 ; ADRESA BAZA ODGOVORA ENG shi i

Page 13: 88702155 Programiranje u Assembleru

hi low =00 // SPI .equ SPIRDM=0x0100 ; ADRESA README DOC shi i hi low ==00 // SPI .equ SETBYT=0x07F0 ; adresa set byte u INTEEP za jezik // max 30 jezika ; itd....

Kad smo definirali varijable i dodjelili im registre ili polja rama itd... definirali sve funkcije koje mislimo da trebamo postavljmo assembleru početak naredbom .org Iza te naredbe slijedi polje prekidnog vektora tj. adresa prekida (prekidnih rutina). Njegova dubina ovisi o MCU-u kojeg koristimo. Prva adresa nije i je prekid (zaprvo je reset), naravno, i ako ništa nije upisano u to polje program će ići dalje do svog "prirodnog" starta tj. kraja polja prekidnog vektora. Ako su korištene naredbe reti ili kombinacija reti i nop što god da se dogodilo u programu, a uzrok je greška, i dovede do skoka u zonu prekidnog vektora (a nisu reset) vratiti će se nazad bez većih posljedica po odvijanje programa osim gubljenja koje mikrosekunde. Pošto mikroračunala nisu savršena takve se stvari događaju.

Evo kako to izgleda:Code: Select all

.org 0

rjmp INIT ; odmah skačemo na inicijalizacijsku rutinu // Naziv te rutine nije važan reti reti reti reti reti reti reti ; reti nop reti nop ; ; reti .org 0x0016 ; povratna adresa iz bootloadera i prva adresa programa poslije polja prekidnog vektora. Ako ne koristimo bootloader ova .org definicija nam ne trebaINIT: ldi r17, 4 ; početak programa

Kao što smo vidjeli u ranijem tekstu s reset adrese skacemo u rutinu kojoj smo dali ime INIT. To je prva rutina koju cemo napisati, bar bi bilo dobro. U njoj cemo konfigurirati registre specialne namijene, definirati velicinu stoga u ramu, postaviti pocetne parametre portova, preskalera itd... Obicno se ta rutina još naziva i inicijalizacijska rutina.

Odmah da neke stvari rasčistimo, ovdije nećemo pojašnjavati imena i funkcije pojedinih registara specijalne namijene ili naredbi ako one nisu u službi pojašnjenja problema ili lakšeg raumijevanja toka programa i same upotrebe assemblera. Na početku je rečeno da se prije bilo kakvog upuštanja u pisanje programa u asembleru mora proučiti tehnička dokumentacije MCU-

Page 14: 88702155 Programiranje u Assembleru

a kojeg koristimo kao i okruženja u kojem on djeluje, HW-a uređaja.

Pisanje programa u asembleru nije isto što i pisanje programa u C-u, VB-u s korištenjem pojedinih programskih odsječaka pisanih u asembleru. Radi se o potpuno drugačijem pristupu postavljanju i vođenju programa. To su dva različita svijeta. Možda ovaj dio, uvod u program, i ne izgleda nešto zanimljiv i naizgled je idetičan onome što rade alati u C-u ali to je sve. Pogotovo kod jednostavnijih programa i definicija.

Nebi bilo loše da pročitate komentare sa strane u primjerima jer oni opisuju ono što se događa, što pojedina veličina unosi u registar, ram, eeprom... Komentari sa strane su veoma česti kod assemblerskih programa. Netko ih koristi u većoj netko u manjoj mjeri. Oni vam ga više izađu kao prilično dobra dokumentacija i uputa za neka buduća riješenja.

Polje registara specijalne namijene smješteno je u ramu na relativnoj adresi 0x00 pa naviše, ovisi o MCU-u. U našem slučaju ono ide do relativne adrese 0x3F. Zašto smo spominjali relativnu adresu rama? Ipak je MCU realan stroj s realnim tj. apsolutnim veličinama i jedino njih on razumije, pa tako i naš ATm8.

32 registra opće namjene (16/16) relano nisu nikakvo zasebno polje skupa registara, oni su samo prikazani programeru kao takvi a dio su realnog HW-a polja rama i to od apsolutne adrese 0x00 do 0x1F, što znači da je stvarno polje naših registara specijalne namijene od vrijednosti 0x20 pa do 0x5F. To je navedeno i u tehničkoj dokumentaciji (doduše sitnijim slovima) pa rekoh da vam bar malo olakšam snalaženje.

Zašto je to bitno? Programer u assembleru mora poznavati te detalje (kako je često naglašavano) jer za njega nema tko o tome voditi računa kao što to rade compileri u alatima HPL-a; C, VB... Znači da naš realni sektor, tj. stvarno polje rama počinje od adrese 0x60 pa do kraja rama. Pravilnom upotrebom indeksnih registara možete, što većina compilera nemože jer im brani upravo ta zaštita od slučajnog ulaska u polje registara specijalne namijene, direkto unositi podatke u radne registre predstavljajući ih mašini kao ram. To se npr. pokazalo kao vrlo korisno u pisanju složenih algoritama enkripcije i dekripcije. No najvažnije je da znamo pravo stanje stvari u našem stroju. Koliko čega imamo na raspolaganju kao programeri.

Primjer inicijalizacijske rutine:IME RUTINE NAREDBE KOMENTARIINIT: ldi r17, 4 ; 4 ATm8 INIT STACKout SPH, r17 ; može se koristiti i predefinicija iz m8def zvana RAMENDclr r17 ;out SPL, r17 ; lds r17, SFIORori r17, 8sts SFIOR, r17 ; onemogucen ADC i ADC muxsbi ACSR, 7cbi ADCSR, ADEN ; onemoguci ADC konvertor;sts ADCSR, r17;in r17, MCUCSR ; konfigutacija sleep moda opcija power-down int0 aktive low;andi r17, 0xDF ; brise bit 5 ;out MCUCSR, r17;in r17, EMCUCR

Page 15: 88702155 Programiranje u Assembleru

;andi r17, 0x7F ; brise bit 7;out EMCUCR, r17out TIFR, r17ldi r17, 0xA8 ; ivsel=0 // omogucen SLEEP mode s Power down i padajucim sign out MCUCR, r17 ; 38 ==8515 // m8==A8ldi r17, 0x40 ; omoguci prekid preko INT0 tu ce bit OK taster kasnije onemogucitiout GICR, r17nopclr r17 ; setiranje timera out TCCR1A, r17 ; normal port operation / bez PWMa i OCNaout TIMSK, r17 ; timer stopiranldi r17, 5out TCCR1B, r17 ; preskaler postavljen na 1024ldi r17, 0x5F ; EE // B7 -- ldi r16, 0xC8 ; D1 //18 A037 presk 1024 14MHz== 3S compl==5FC8out TCNT1H, r17out TCNT1L, r16 ; TIMSK ce kasnije bit setiran na OVFL mode ldi r17, 4out TCCR0, r17 ; postavi timer 0 u normal mod s preskalerom 256 ;clr r17; out GIFR, r17 ; prekidni flagovi u 00ldi r17, 0xFF ; bio F8 za 5Vout OSCCAL, r17 ; 14MHznopclr r17out DDRC, r17out PORTC, r17ldi r17, 0xC0 ; svi ulazni osim PD6 i PD7out DDRD, r17nopldi r17, 0xFF out DDRB, r17 ; izlazni portoviout PORTD, r17nopcbi PORTD, 6 ; iskljuci TX / primjer nemaskiranog, direktnog, postavljanja pina u "0"ldi r17, 0x3E ; bit 6==0 iskljuci TX i 7==0 iskljuci soundout PORTB, r17nopldi r17, 0x80sts Anim1pos, r17 ; upisuje vrijednost iz r17 u ram određen pozicijom Anim1possts Anim1old, r17sei ; omoguci opci prekid generalnorjmp START;Primjetiti cete da za definicije velicine stoga (SPH: SPL) nisu korištene predefinicije iz m8def.inc. U asembleru to nije važno, važno je da znate što cinite. Dali cete za to koristiti predefiniciju, decimalni, oktalni, hexadecimalni ili binarni broj nije bitno.Neke od naredbi imaju pocetak sa ; Taj znak, tocka-zarez, kazuje asembleru da tu naredbu ne uzima u obzir. Nakon definicije smijera podataka porta primjetiti cete da obicno koristim naredbu NOP (no

Page 16: 88702155 Programiranje u Assembleru

operation) . Ta naredba služi da bi se dalo vremena racunalu da se port sinkronizira i postavi kako treba prije slijedece operativne naredbe. NOP naredba uglavnom služi za "kupovanje" vremena tj. za kratka kašnjenja potrebna stroju da postavi vrijednost u realnom vremenu. O realnom vremenu smo nešto već i govorili.

Ako programer ne razumije vrijeme i način realnog ponašanja stroja i misli kako se njegove naredbe izvršavaju u nekom "warp" polju imati 'e ne malih proglema u stvarnosti i interakciji s HW-om uređaja. Svaka tehnologija ima svoje probleme, svaki stroj svoje realno kašnjenje. Ti se problemi najviše ogledaju kad se pišu rutine za rad s vanjskim sklopovima. Znate, čitanje i iščitavanje podataka iz rama u flash, iz flasha u ram, iz jednog u drugi registar se odvije unutar sustava čije kašnjenje je dio njega samog no to nije slučaj sa radom s vanjskim sklopovima. Možete uzeti primjer I2C eeproma koji su užasno spori. Ili pisanje inicijalizaciske rutine za driver MMC/SD kartice čija brzina u inicijalizaciji komunikacije nesmije prijeći 400kHz, emuliranje USART protokola (HW-ski i SW-ski layer) itd... Upravo radi tih stvari slobodno mogu reći da je NOP bogom dana naredba. Koliko god se to nekome činilo neobično. No kad razmislite o tome vidjeti ćete da sam u pravu.

U registre iz skupa registara specijalne namijene koje vidite u gornjoj rutini nemože se direktno upisivati vrijednost vec samo preko radnih registara naredbama in, out, lds, sts. Ovo je primjer gdje to nije slucaj: cbi PORTD, 6 vec se direktno postavlja (setira) ili briše (resetira) bit.

Ovako, jedno je bitno, nemorate vi definirati vrijednosti svih registara iz tog skupa vec samo onih koji ce neposredno utjecati na odvijanje vašeg programa i postavku HW-ra uredaja vama bitnu. Npr kao definicija SFIOR registra. Ako ne želite promijeniti ostale bitove tj, opcije registra specijalne namijene, u ovom slucaju nek to bude SFIOR, tada se poslužimo tkzv. maskiranjem bita.Primjer:lds r17, SFIOR ; iscitavamo trenutno stanje registra SFIORori r17, 8 ; vršimo maskiranje bita postavljanjem logicke jedinice i u bit 3sts SFIOR, r17 ; spremamo novo stanje u SFIOR

Ili ovaj primjer in r17, SFIORori r17, 4 ; onemoguci PULLUP otpornike u MCU-u.out SFIOR, r17 ;

Može i ovaj primjerin r17, SFIORandi r17, 0xFB ; omoguci PUD (pullup) bit 2 / 1=disable 0=eneableout SFIOR, r17 ;

Kao što vidite u sva tri primjera radi se o maskiranju bita operacijama koje za to i služe u racunalu, kojem god; OR i AND. Naredba andi se koristi kad se odabrani bit u maski želi pobrisati, tj. postaviti u logicku nulu a ori kad se odredeni bit želi postaviti u logicku jedinicu.Da nebude krivo shvaceno, bilo kad i bilo gdje u programu možete podešavati registre specijalne namjene a ova rutina se postavlja kao inicijalna, u njoj su pocetni uvjeti starta našeg MCU-a.

Sad kad smo našem MCU-u zadali pocetne parametre, kreirali tip i velicinu oscilatora takta, boot zonu (o njoj kasnije), definirali portove (smijer i startnu vrijednost), odabrali još neke od

Page 17: 88702155 Programiranje u Assembleru

specijalnih funkcija kao sleep s aktivacijom na prekidnom vektoru int0 u padajucoj ivici signala (postaje aktivan na logickoj nuli), definirali timere, ako nam trebaju, možemo krenuti dalje.

rjmp START voditi ce nas prema glavnom programu. To su naredbe i pozivi na rutine koje postavljaju pocetne parametre okolnog HW-ra uredaja, testiraju ga, testiraju komunikacije, provjeravaju ID-e pojedinih chipova ili modula u sustavu i ako je sve uredu idu pravo u glavni program ako nije obavještavaju nas o grešci, pod uvjetom da je taj dio obraden.

Kod jednostavnih programa, kao oni za paljenje lampica, raznih releja i slično netreba vam takva rutina. Netreba vam ni inicijalizacija... Pošto su oni prejednostavni, bez uvrede, nemogu biti uzeti kao primjer u priči o asembleru jer im ne treba dobar dio strukture programa. Uglavnom se svode na postavljanje ili brisanje bita odabranog porta i kašnjenja (dali timerom ili emuliranih). Nebi ih čak ni preporučio za učenje rada u assembleru. Jer se nema što naučit. No idemo dalje!

Primjer rutine koja vrši inicijalizaciju HW-a uređaja, test pojedinih rutina ili baza podataka itd:START: sleep ; MCU ceka budjenje s tasterom OK !!!!nopin r17, GICR andi r17, 0xBF ; brise bit 6 onemoguci prekid s I/O porta PD2 ==INT0out GICR, r17 ; to je samo jedna od multipleksiranih funkcija porta MCU-anop

Kao što vidite ovdje smo redefinirali status specijalnog registra jer nam više nece trebati sleep mod pa da nam nebi MCU slučajno "zaspao".

rcall ZVUK ; SUSTAV JE UKLJUCEN - poziv na rutinu koja generira zvuk na buzzeru.rcall TESTCOM ;-----testiranje comunikacije sa USB /rs232 VCPa

Ove dvije gornje naredbe rcall su poziv na rutinu koja je smještena unutar bloka. Dubina je najcešce oko 2KB. Zbog krivog izračuna stvarne razlike od naredbe do stvarne rutine može vam MCU upast u reset vektor ili negdje drugdje zaglibit. Ako niste sigurni uvijek se možete poslužiti naredbana za apsolutni poziv call ili skok jmp.

str0: clr ZHclr ZL ; clr PNS ; postavi normal mode za znakovercall IEPRD ; cita interni eepromstr1: mov Maxlen, byte , podatak iz eeproma sprema u registar Maxlenrcall LCDini ; vrši inicijalizaciju LCD-a pozivom na rutinu/e u driverurcall SKCOPYldi YH, 1 ; pos srama sa podacima za svaki skuter / time i statusclr YLsts Slika1, YLclr inLclr tmHinc tmHstrX: rcall M1SLIKA ; postavljamo pocetnu sliku na LCDmov tmL, Maxlenldi LINEX, 0x40

Page 18: 88702155 Programiranje u Assembleru

strs: inc LINEXcpi LINEX, 0x46 ; po 5 skuterabreq MAIN Naredba uvjetnog grananja breq njen doseg, dubina, je 256. Macroasembler će vas upozorit ako pređete maximalnu vrijednost.

rcall LINxyrcall SKUNM ; prikaz reda // S01 00 ISKLJ dec tmL ; maxlen brne strs ; još jedna naredba uvjetnog grananja

Tu bi mogli reci da nedostaje uvjet kojeg testira naredba uvjetnog grananja kao u gornjem primjeru naredba cpi , no u vecini mašina status "0" se ne postavalja u uvjet direktno, iako može. Ionako se nalazi u status registru (jednom od registara u polju registara specijalne namjene) u formi zastavice (flag). Tako da cim registar tmL dostigne nulu biti ce zadovoljen uvjet za preskok grananja i program ce nastaviti u slijedecoj naredbi. U ovom slucaju u glavnom programu. Tu smo uštedjeli jednu nadbu, najmanje jedan ciklus.

MAIN: rcall TASTS ; //// POČINJE GLAVNI PROGRAM ////

Vidjeli smo kako su u dijelu programa nazvanog START postavljeni pocetni parametri funkcija u registre ili stog, neki su iscitani iz interne memorije podataka. S ovim dijelom programa završeno je konfiguriranje sustava kao i postavljanje svih pocetnih vrijednosti u registre, ram, portove... Da nebi bilo zabune, mnoge od tih rutina koje su pozvane, kao i driveri koji se pozivanju u toj fazi izrade programa, vjerojatno nisu napisani (ili kopirani) no to nije bitno. Važno je da smo postavili sve kako bi trebalo biti ili mislimo da treba. Svakako i ovo, fizički ta rutina (START) nemora biti na početku, to je jasno ja se nadam, može biti bilo gdje važno je da se poziva prije početka izvršenja glavnog programa.

Netko će možda reći kako je nepotrebna. Možda, kod programa koji su neprofesionalno rađeni, ili postoji, iskreno neznam kakav, razlog za ne testiranje sustava prije početka odvijanja glavnog programa. Čak i za relativno jednostavne uređaje ona je korisna. Može se dodat glavnom programu ako dođe do neke od grešaka da ponovo krene od takve rutine. O upravljanju error kodom (greškom) ćemo se pozabaviti kasnije. Nažalost neki programeni to smatraju gubljenjem vremena, a neki kao da ni neznaju što je to obrada greške i koliko je ona bitna programu.

I tako, malo po malo mi dođosmo do glavnog programa. U asembleru je on principielno postavljen gotovo na isti način kao i kod viših programskih jezika. Uglavnom se pozivaju funkcionalne cjeline (potprogrami) jedan za drugim do kraja glavnog programa. Većina glavnih programa kao i potprograma i rutina nema kraj u klasičnom smislu te riječi. Glavni program se uvijek vraća na svoj početak zatvoren u bezuvjetnu petlju ili uvjetnu s grananjem na njegov početak kao jednom od opcija. Ja se ne sjećam ni jednog programa koji je imao klasičan kraj, osim nekih koji su služili testiranju.To nam govori jednu veoma važnu stvar, da se glavna bitka kod izrade programa pa tako i srukturiranje vrši u potprogramima, rutinama, driverima itd... No ipak, on nam govori, struktura glavnog programa, koliko smo dobro segmentirali program i jesmo li dobro odvagali funkcionalne cjeline. Kao što nevalja previše rascjepkat glavni program tako nevalja ni nagurat hrpu stvari u dva-tri funkcionalana bloka.

Page 19: 88702155 Programiranje u Assembleru

Glavni program je naš prvi korak, ulaz, u strukturiranje programa bez obzira što će najveći teret podnijeti potprogrami a počesto male rutine. Najvažnije je da se ne upustimo u beskrajno cjepaknje potprograma kao ni pretjerano sabijanje u jednu nefunkcionalnu, bolje reć teško funkcionalnu, cjelinu.

Na primjerima koje slijede pokušati ću vam pokazati kako gore rečeno i nemora uvijek biti ispravno.

Naravno nemora uvijek glavni program biti samo niz poziva na veće potprograme. Dobar primjer za to jesu emulatori pay-tv kartica. Njihovi glavni programi su prilično dugački i nerjetko kompleksni. Ali to je samo radi specifičnosti problema koji se njima rješavaju a ne nepoznavanja programiranja u asembleru.

Primjer jednog glavnog programa takvog emulatora:

Code: Select all; main:avr00BF: clr r17 ; 00BF 2711 clr XH sts 0x0062, r17 ; 00C0 9310 0062 upisi 00 sts 0x0063, r17 ; 00C2 9310 0063 clr r8 ; 00C4 2488 clr r7 clr r21 rcall avr0046 ; 00C5 DF80bbg: ldi ZH, 0x02 ; 00C6 E0F2 Load a config in @ 0x02FE (0xFF80) clr r17 ldi XL, 0x61 ; 00CE E6A1 ld r17, X ; 00CF 911C cpi r17, 0x05 ; 00D0 3015 brne avr00D3 ; 00D1 F409 rjmp avr01C1 ; 00D2 C0EE Handle ECM, interesting part!!!!

avr00D3: cpi r17, 0x01 ; 00D4 3011 01 EMM brne avr00EE rjmp EMM ; 00D6 CFBF rutina EMM bio 0096 0062 sig wrong!avr00D7: ld r17, X+ ; 00D7 911D cpi r17, 0x0A ; 00D8 301A brne avr00E6 ; 00D9 F461 ld r17, X+ ; 00DA 911D cpi r17, 0x05 ; 00DB 3015 brne avr00E6 ; 00DC F449 ld r17, X+ ; 00DD 911D subi r17, 0x02 ; 00DE 5012 12-2=10h ld ZH, X+ ; 00DF 91FD ld ZL, X+ ; 00E0 91ED cltavr00E1: ld r18, X+ ; 00E1 912D rcall avr02FD ; 00E2 D21A dec r17 ; 00E3 951A brne avr00E1 ; 00E4 F7E1 set rjmp avr018C ; 00E5 C0A6avr00E6: ldi r17, 0x69 ; 00E6 E619

Page 20: 88702155 Programiranje u Assembleru

ldi XL, 0x02 ; 00E7 E6A2 st X+, r17 ; 00E8 931D ldi r17, 0x00 ; 00E9 E010 st X+, r17 ; 00EA 931D ldi XL, 0x67 ; 00EB E6A7 st X+, r17 ; 00EC 931D rjmp avr018C ; 00ED C09Eavr00EE: ldi XL, 0x64 ; 00F2 E6A4 --------- pocinje klasa 02 prijavna rutina ------------------------ ld r17, X+ ; 00F3 911D ext eeprom write cpi r17, 0xFF ; 00F4 3F1F breq avr00D7 ; 00F5 F309 set ; 00F6 9468 cpi r17, 0x00 ; 00F7 3010 brne avr00FE ; 00F8 F429 ldi ZL, 0x15 ; 00F9 E1E5 serial num ldi r17, 0x14 ; 00FA E114 sts 0x0067, r17 ; 00FB 9310 0067 rjmp avr018B ; 00FD C08D avr00FE: cpi r17, 0x01 ; 00FE 3011 P1 01 (card serial num HEX) brne avr0107 ; 00FF F439 ldi r17, 0x10 ; 0101 E110 sts 0x0067, r17 ; 0102 9310 0067 ldi ZL, 0xF0 rcall avr02F2 mov r18, r0 cpi r18, 0x01 brne vlki ldi ZL,0x8A rjmp avr018B ; -- bio wfc hex ser iz flasavlki: ldi ZL, 0x2A ; 0100 E2EA trebalo biti od providera tekuceg !:)bio A2 rjmp avr010A ; 0106 C085 salje u cam avr0107: cpi r17, 0x02 ; 0107 3012 P1 02 (country code + COCO string) brne avr0110 ; 0108 F439 ldi ZL, 0x3B ; 0109 E3EB treba bit od tekuceg providera (a moze i ALL)avr010A: ldi r17, 0x10 ; 010A E110avr010D: sts 0x0067, r17 ; 010B 9310 0067 len rjmp avr018B ; 010F C07Cavr0110: cpi r17, 0x0B ; 0110 301B P1 0B (country code other way) brne avr0114 ; 0111 F411 ldi ZL, 0x3B ; 0112 E4E8 coco rjmp avr010A ; 0113 CFF6avr0114: cpi r17, 0x03 ; 0114 3013 P1 03 (get PID ) brne avr0125 ; 0115 F479 ldi r17, 0x18 ; 0116 E118 14 + 4 =18 ldi ZL, 0x4C ; 0119 E4EC PW 0315 za P00 lds r25, 0x0066 ; 011B 919D uzima provider num iz srama ! cpi r25, 0x00 ; 011C 3090 P2 00 (provider 00) ako ne 10 !! :) breq avr010D ; 011D F021 ldi ZL, 0x65 ; 011E E6E5 za P 10 0318 (PW) rjmp avr010D ; 0121 C06A uzima config u obzir i trazi u eepromuavr0125: cpi r17, 0x0E ; 0125 301E P1 0E (read card file) brne avr013D ; 0126 F429 staviti CF umjesto

Page 21: 88702155 Programiranje u Assembleru

kljuceva za secu u eeprom 00 ldi XL, 0x65 ; ldi ZH, 0x00 ; ld r22, X+ ; cpi r22, 0x02 ; breq avr0126 ; cpi r22, 0x03 ; breq avr0127 ;avr0126: ldi ZL, 0x00 ; CF 1 begin rjmp avr0128 ; avr0127: ldi ZL, 0x40 ; CF 2 begin avr0128: ldi r17, 0x40 ; 0128 E410 sts 0x0067, r17 ; 0129 9310 0067 sve sto izbacuje prema camu pocinje od 60 rjmp avr018B ; 012B C05F sa rutinom avr018B :)avr013D: cpi r17, 0xEF ; 013D 3E1F brne avr012C ; 013E F441 ldi XL, 0x68 ; 013F E6A8 ld ZH, X+ ; 0140 91FD ld ZL, X+ ; 0141 91ED ldi r17, 0x12 ; 0142 E112 sts 0x0067, r17 ; 0143 9310 0067 rcall avr02E1 ; 0145 D19B rjmp avr018C ; 0146 C045 bio 018Cavr012C: cpi r17, 0x08 ; 012C 3018 P1 08 (20h baytes from buffer ??) brne avr0133 ; 012D F429 ldi ZL, 0xA0 ; 012E E8E0 bio 80 ldi r17, 0x20 ; 012F E210 FF00000000000000000000000000000000000000 32 bayt sts 0x0067, r17 ; 0130 9310 0067 rjmp avr018B ; 0132 C058avr0133: cpi r17, 0x11 ; 0133 3111 P1 11 (????) nije mi trebao

brne avr0147 ; 0134 F441 ldi ZL, 0x00 ; 0135 EAE0 never use it ldi ZH, 0x00 ldi r17, 0x58 ; 0136 E518 sts 0x0062, r17 ; 0137 9310 0062 ldi r17, 0x40 ; 0139 E410 sts 0x0067, r17 ; 013A 9310 0067 rjmp avr018B ; 013C C04Eavr0147: cpi r17, 0x09 ; 0147 3019 P1 09 (cam key) brne avr0166 ; 0148 F4E9 ld r17, X+ ; 0149 911D cpi r17, 0x03 ; 014A 3013 brne avr0165 ; 014B F4C9 ldi r17, 0x55 ; 014C E515 ldi XL, 0x62 ; 014D E6A2 st X+, r17 ; 014E 931D ldi r17, 0x00 ; 014F E010 st X+, r17 ; 0150 931D sts 0x0067, r17 ; 0151 E6A7 lds r17, 0x0066 ; 0153 E6A6 lsl r17 ; 0155 0F11 lsl r17 ; 0156 0F11 lsl r17 ; 0157 0F11 ldi r25, 0x68 ; 0158 E698 08 add r17, r25 ; 0159 0F19 mov XL, r17 ; 015A 2FA1 pozicija cam keya ldi r17, 0x08 ; 015B E017 bio 07

Page 22: 88702155 Programiranje u Assembleru

ldi YH, 0x01 ; 015C E0F1 stog 01c5--- 8 bayta za cam key c6----last byt c5 ldi YL, 0xCD avr015E: ld r18, X+ ; 015E 912D st -Y, r18 ; 015F 9321 reversno spremanje dec r17 ; 0160 951A brne avr015E ; 0161 F7E1 rcall CKP avr0165: rjmp avr018C avr0166: cpi r17, 0x0A ; 0166 301A breq avr0169 ; 0167 F009 rjmp avr00E6 ; 0168 CF7Davr0169: ld r17, X+ ; 0169 911D cpi r17, 0x01 ; 016A 3011 brne avr0180 ; 016B F4A1 ld r17, X+ ; 016C 911D cpi r17, 0x02 ; 016D 3012 brne avr0180 ; 016E F489 ld r17, X+ ; 016F 911D cpi r17, 0x04 ; 0170 3014 brne avr0180 ; 0171 F471 ld r17, X+ ; 0172 911D cpi r17, 0x47 ; 0173 3417 brne avr0180 ; 0174 F459 ld r17, X+ ; 0175 911D cpi r17, 0x11 ; 0176 3111 brne avr0180 ; 0177 F441 ldi ZH, 0x02 ; 0178 E0F2 ldi ZL, 0x40 ; 0179 E4E0 ld r18, X+ ; 017A 912D clt ; 017B 94E8 rcall avr02FD ; 017C D180 ld r18, X+ ; 017D 912D rcall avr02FD ; 017E D17E set ; 017F 9468avr0180: ldi r17, 0x00 ; 0180 E010 sts 0x0067, r17 ; 0181 9310 0067 lds r17, 0x0065 ; 0183 9110 0065 ldi r25, 0x5E ; 0185 E59E sbrs r17, 1 ; 0186 FF11 pin answer ldi r25, 0x50 ; 0187 E590 sts 0x0062, r25 ; 0188 9390 0062 rjmp avr018C ; 018A C001

avr018B: rcall avr002D ; 018B DEA1 citanje eeproma i slanje u cam odgovoraavr018C: ldi r21, 0x00 ; 018C E050avr018D: rcall avr007B ; 018D DEED upis u cam rjmp avr00BF ; 018E CF30 Kao što vidite ide u bezuvjetnu petlju na svoj početak

Primjer jednog kasičnog glavnog programa:

Code: Select allMAIN: rcall TASTS ; bivsi TASTE clr TMARK ; marker za operaciju umanji TIMER skutera clr COUNT ; brojac prolaska 6S [timer mode] * 10 == 60S // 1min

Page 23: 88702155 Programiranje u Assembleru

ldi r17, 4 ; bio 80 out TIMSK, r17 ; ukljuci timer1 16bitni na OVFL mode clr YLTAS1: sbrc TMARK, 0 rcall TMDOW ; umanji vrijeme skuterima za jedan sbrc TMARK, 7 rcall BLINK ; rutina za upozorenje da je vrijeme isteklo rcall ANIM1 ; poziva potprogram za animaciju, generira pokretnu sliku na LCD rcall DEL87mS ; ovi dely-i od 87mS su dodani radi usporavanja zbog animacije. Gore ; UP taster rcall UPMD Dolje ; Ova naredba je jedan od primjera macro naredbe // DOWN taster rcall DOWMD rcall DEL87mS OK ; OK taster rjmp TAS2 Stop ; STOP taster rjmp TAS4 rjmp TAS1 ; kao što vidimo ovaj glavni program vrti od starta TAS1 to je stvarni glavni program

Donji glavni program je veoma jednostavan. Poziva direktno tri veća potprograma i uvjetno, ovisi o stisnutom tasteru, četiri jednostavnija potprograma. I ovdje je kao i u gornjem primjeru izvšeno, na neki način, uvjetno grananje na početak iako je naredba za direktno grananje rjmp korištena no njoj prethodi macro naredba Stop u kojoj se testira bit i određuje gdje će program ići. To je jedan od najčešćih načina vraćanja na početak.

U primjerima, pogotovo donjem, vidjeli smo da možemo definirati i svoje vlastite naredbe, macro naredbe. Naravno assembler će ih uredno prevesi u jedini skup naredbi kojeg mikroračunalo razumije, objektni kod. Za razliku od C-a, VB-a... mi ćemo točno znati strukturu te virtualne naredbe jer ćemo je morati napisati s naredbama iz skupa naredbi s liste MCU-a.

Primjer one naše macro naredbe:

.macro Goresbis PIND, 1.endm

Iz ovog primjera je jasno da ona ne skraćuje postupak niti išta pojednostavljuje već nam samo omogučava da preimenujemo naredbu sbis PIND, 1 u nama razumljiviji oblik. Takve se vrste macro definicija koriste kad imate puno toga za definirati pa da vam bude lakše pamtiti. Pogotovo na primjerima kompleksnijih upotreba pinova MCU-a. Kad radite s hrpom pinova kojima su dodjeljene određene funkcije na HW-skom layeru. HW-ski layer je maska (nešto složenije) koja se koristi kod spajanja nekog sklopa, IC-a, komunikacije, itd... na MCU. Npr. CS, WE, RD, Vpp... signali koji se koriste kod programiranja recimo UV-eeproma a realno su pinovi našeg MCU-a. Njih je tad dobro nazvati funkcionalnim imenima.

Na računalu, u stvarnosti, nema varanja. Kako god dubili na glavi nećete uštedit vrijeme nikakvim macro, i sličnim virtualnim, naredbama. To je jedino moguće pažljivim postavljanjem

Page 24: 88702155 Programiranje u Assembleru

strukture programa i samih načina riješavanja problema. Pisanjem što jednostavnijeg algoritma. Ja u početku gotovo da uopće nisam koristio macro naredbe upravo zbog navedenih razloga. Kasnije sam ipak, kako su dolazili složeniji programi, počeo i njih koristiti. Zato one i postoje.

Primjer macro naredbe, malo složeniji:

.macro set_Vpp1 ; PG 4 // 12V6 pin 13 TSOP48 pin Vpplds r17, PORTGori r17, 0x10sts PORTG, r17nop.endm

Ovaj primjer nam štedi "papir" tj. pisanje programa čini nešto jednostavnijim i lakše razumljivim. U primjeru vidite da je PORTG pozvan naredbom lds. U ATm128, ATm104 i sličnim taj port je dio polja skupa registara specijalne namijene i to čiji bitovi nemogu biti direktno pročitani ni upisani. Pa je korištenje macro naredbe tu zaista dobro došlo. Kao što vidimo definiranje macro naredbe uvijek počine s .macro IME NAREDBE po našoj želji, slijedi neka naredba ili skup naredbi i obezno završava s .endm

Primjer jednostavnog no nestandardnog glavnog programa:

Code: Select allMAIN: call KRENI ;--- cita status slota ----- cp r19, r17 breq maia ; sts SLOTO, r17 mair: rcall AUTOMD ; ide na auto detekciju stanja slotamaia: sbis PIND, 5 ; --- citamo taster rotary SW jmp SBMENU ;tu je info menu , settingsi i manual mode mali: jmp DBXCOM ;--!! TESTP za standardni multicam DBXCOM ---- s impulsom rjmp MAIN

Ovaj primjer govori da su dva potprograma funkcionalno i stukturno prilično složena (jmp SBMENU i jmp DBXCOM) pa se i vraćanje iz njih obavlja naredbom direktnog skoka ( jmp MAIN). Takvi potprogrami se obično ne pozivaju naredbama call, icall, rcall već jmp ili ijmp. Iako je i potprogram rcall AUTOMD dosta kompleksan ipak njegova struktura dozvoljava standardni poziv i vraćanje. Iz same strukture glavnog programa se vidi da se na neki načnim modus glavnog programa preselio u potprogram DBXCOM. Ovo je primjer kad potprogram preuzima funkciju glavnog programa (u većini), a za koje naravno postoje realni razlozi.

Mislim da su ova tri primjera glavnih programa pokazala kako situacija, bolje reći problem, ponekad od nas traže drugačije pristupe postavljanju strukture programa. Sva tri primjera su drastično različita. Najtipični i najčešći je onaj srednji primjer postavljanja glavnog programa.

Uskoro ulazimo u "zonu sumraka", potprograme i rutine, drivere itd.... Tamo gdje se zaista odvija pravi posao.

Page 25: 88702155 Programiranje u Assembleru

Kašnjenje? Možda ce nekome izgledati vec videna tema no iz kuta programera u asembleru ta tema je gotovo vjecna. Moc asemblerskog programiranja, izmedu ostalog, ogleda se i u opcijama koje nam se pružaju kod kreiranja moda kašnjenja (famoznog delay-a). U HPL-ima kašnjenje možete obaviti na dva nacina, uglavnom, klasicnom delay(xxxx) naredbom ili postavljanjem HW-skog timera sa ili bez prescalera, 8 ili 16 bitnog. No ovo oko postavljanja timera u prekidnom vektoru sa internim HW-om se ionako u HPL-ima mora uraditi kreiranjem assemblerske rutine.

Dok naredbom delay(100) u HPL-u najcešce postavljamo kašnjenje od npr. 100 miliS a compiler to "lijepo" prevede u rutinu koja davi mikroracunalo kako je to lijepo pojasnio naš kolega u "Tartufima" u assembleru uopce ne postoji tako što. Jednostavno toga nema! Naredbe delay u asembleru nema iz prostog razloga jer nije na listi naredbi MCU-a. Znaci moramo sami izracunati kašnjenje i napraviti petlju.

Naizgled bi nas to moglo navesti na krivi zakljucak kako je bolje imati HPL pa ti napišeš delay a compiler nek se muci. No ima jedna sitna razlika, u assembleru vi tocno znate što, kako i na koji nacin cete izvesti kašnjenje. To najcešce ovisi o vašoj kreativnosti.Najjednostavniji oblici kašnjena jesu oni koji su i najnemaštovitiji.

Primjer:ldi r17, 0x25 ; 14MHz==> 25h za 115kbit // kašnjenje za USART emulirani 8n1.rcall DEL14 ; poziv na kašnjenje;;DEL14: dec r17 ; rutina za kašnjenjebrne DEL14ret;Iz ovoga se vidi kako nam dobro dode poznavanje vremena izvršenja naredbe. Tj. ona vremenska baza, temelj bilo kakvog ozbiljnijeg rada oko mikroracunala, racunala uopce. Osnova izracuna je ===> ukupan broj ciklusa u petlji * vrijeme izvršenja ciklusa. U gornjem primjeru su dvije naredbe jedna ima 1 ciklus a druga 2, treca ( ret) je vracanje i uzima se u izracun kod jako kratkih vremena kašnjenja ( ispod 2 microS) na trajanju cyklusa od 50-->80nS kao i naredba za poziv rutine za kašnjenje rcall DEL14. U stvarnosti one se unose kao korektivni faktor u samu vrijednost varijable. Pošto smo objasnili sve oko izracuna ciklusa u ranijem tekstu necemo ga ponavljati.

Primjer 2:ldi r17, 0xF2 ; rcall DELAY ; 14MHz==> 0xF2 4800 bita // kašnjenje za emulirani USART 8in1;;;DELAY: dec r17 ; nopnopnopnopnopnop

Page 26: 88702155 Programiranje u Assembleru

nopnopnopbrne DELAYret;Pošto je kašnjenje daleko vece nego je moguce to dobiti sa rutinom DEL14 i maximalnom vrijednosti r17 registra napisana je ova. Iz nje se vidi kako se naredbama nop "kupuje" vrijeme mjesto da se koristi više registara. Ovo je više prilika da se vidi za što se sve ta naredba može koristiti.

Nebi to bio asembler kad nam nebi pružio mogucnost da smislimo još koju opciju. Npr. kao delayi mogu se koristiti neke druge rutine ili dijelovi potprograma. Npr. rutine za animaciju slike, sortiranje podataka i sl.

Svakako tu su i klasicni timeri. Definicijom u Inicijalizacijskoj rutini, ili bilo gdje u programu, te postavljanjem adrese prekidnog vektora za odabrani mod timera kojeg želimo možemo aktivirati HW-ski brojac. I on je kao i sve u racunalu vezan na takt oscilatora. Od preskalera pa na dalje. Funkcionalno gledano to je dobri stari brojac (T -FlipFlop) impulsa. Može da radi kao brojac nagore ili nadolje, s prescalerom ili bez njega... Pridodane su im, najcešce, opcije za komparaciju. Bojac TCNT1x (x= H,L) broji do vrijednosti postavljene u komparacionom registru OCR1BHx (x=H,L) i postavlja zastavicu prekida kad je ispunjen uvjet.

Primjer brojaca s komparacijom:ldi r16, 0x02out OCR1BH, r16 ; komparaciona vrijednost u H i L regostrima / 16 bitnildi r16, 0xE8 ; out OCR1BL, r16ldi r16, 0x01out TCNT1H, r16 ; pocetna vrijednost brojaca u H i L registrima / 16 bitnildi r16, 0x00out TCNT1L, r16 ldi r16, 0x20 ; postavljanjem bita 5 u registru i njegovim kopiranjem u TIMSK postavili smoout TIMSK, r16 ; gornje timere u aktivni mod / ovo može biti uradeno bilo gdje u programu.out TIFR, r16

Postavljanjem prekidne maske u TIMSK registar i zastavice u TIFR pod uvjetom da je postavljen opci uvjet prekida sei pocinje odbrojavanje. Ako u bilo kom dijelu programa poželite zaustavtit brojanje to možete uciniti najednostavnije na dva nacina; resetiranjem bita TIMSK registra za odabrani timer i mod korištenjem opcije maskiranja (zapamtite to je registar specijalne namjene) ili brisanjem registra za omogucavanje opceg prekida naredbom cli. Ako nemate ni jednu drugu prekidnu opciju aktivnu cli je najjednostavnija opcija.

Pošto smo koristili samo taj timer nije bilo potrebno izvršiti operaciju maskiranja vec je to ucinjeno naredbom ldi r16, 0x20. Kao što se da vidjeti najcešce su bit u TIMSK registru i njegova zasatavica u TIFR registru na istoj poziciji u registru, u našem primjeru bitu 5 (0x20 = bit 5).

Page 27: 88702155 Programiranje u Assembleru

Da malo pojasnimo to sa HEX kodom. Osam (8) bitni registar je podjeljen funkcionalno u nibble-e, gornji i donji. Bitovi od 0-->3 čine donji nibble a 4-->7 gornji nibble. Tu osnovu HEX coda bi svaki programer trebao poznavati bez obzira jeli koristi HPL ili asembler. Prvi bit donjeg nibble-a (0) je 1, drugi (1)je 2, treći (2)je 4 četvrti(3) je 8 kad to zbrojimo dobijemo 0xF kao maximalnu vrijednost. Prvi bit gornjeg nibblea bit(4) je 0x10, drugi(5) je 0x20, treći(6) je 0x40, četvrti(7) je 0x80. Kad ih zbrojimo dobijemo 0xF0. Zbrajanjem oba nibble-a u 8 bitni broj dobijemo 0xFF kao maximalnu vrijednost. U našem skupu naredbi (ATMega ali i AVR, PIC...) postoji naredba swap (zamijeni) kojom mijenjamo mjesta nibleu, gornji --> donji i obratno. Računanje u HEX kodu nije toliko strašno koliko se to na prvi pogled čini.

Pravilnim odabiraom vrijednosti za registar koji vrši kotrolu nad odabranim timerom možemo odabrati izmedu osatalog i preskaler opciju. U donjem primjeru je odabran preskaler koji broji 32 takta (u našem slucaju cyklusa) za 8 bitni brojac.Primjer:ldi r17, 3out TCCR0, r17 ; normal mode prescaler 32 cykl ==> 2 microS za freq. takta od 16 Mhz.

ldi r17, 0x5F ; "obicno" postavljanje 16 bitnog brojaca ldi r16, 0xC8 ; out TCNT1H, r17out TCNT1L, r16 ;

U svakom slucaju odabiraom bilo koje od opcija s brojacima možete imati širok spektar kašnjenja po osnovi prekidnog vektora i HW-ra specijalne namjene. Koristite li taj mod kašnjenja vaše racunalo ce normalno raditi svoj posao dok HW ne dode na zadane parametre. Tada ce sustav postaviti prekidnu zastavicu i skociti na adresu prekidnog vektora za taj brojac i mod u kojem je korišten. Kad dode na tu adresu uciniti ce ono što ste mu vi rekli, reti --> vratiti se nazad u program na prvu slijedecu naredbu od one gdje je prekid izvršen, ili rjmp, jmp --> skociti u prekidnu rutinu.

Emulacijom USART-a vrijeme kašnjenjenja na cekanje izmedu dva bita može se koristiti na razne nacine, jedan od njih je i pokretanje potprograma koji nisu vremenski kriticni ili pak jesu dio vremenski kriticne operacije, kao dekripcija DW-a koje znaju biti duže od 60 - 80 000 cyklusa pa i mnogo više.Kako god, asembler vam pruža široke mogucnosti manipulacijom kašnjenja, tj korištenja tog vremena u korisne svrhe. Kao što rekoh na uvodu ogranicenje smo mi sami i naša mašta. Sposobnost da nadmudrimo sustav.

Experimentirajte malo s raznim opcijama pa vidite kako sve to izgleda u stvarnosti. Najgore je kad se zatvorimo u neke obrasce i iz njih se ne micemo tješeci se kako smo zadovoljani onim što znamo. Zapamtite, racunalo je užasno prost stroj, gotovo za smijat se jednostavan a jedino što vrijedi ste vi, njegov programer. Sva njegova pamet i glupost je iskljucivo u vašim rukama. Isto tako ne postoji toliko loš kompjuter koliko može bit loš programer i zato nikad opravdanje za pogreške ne tražite u tom malom jednostavnom stroju, mikroracunalu. One se tamo sigurno ne nalaze. Asembler vam pruža mogucnost da se direktno suocite sa samim sobom, najprije, jer vam daje kompletnu mašinu na raspolagnje, sve njene registre, memorije, mogucnosti... a sve ostalo je na vašoj kreativnosti, sposobnosti kao i poznavanju samog HW-ra uređaja i tehnologije kojom je rađen.

Page 28: 88702155 Programiranje u Assembleru

Prije odlaska u "zonu sumraka" mogli bi vidjeti što je to zapravo bootloader. Cemu on služi i što može.

Bootloader? Hm. Malo sam razmišljao o načinu na koji bih pokušao pojasniti što bi to bilo. Strukturno gledano i HW-ski a i SW-ski u ATmega MCU-u je to ne samo, manje ili više kompleksan, program već i posebna zona u programskoj memoriji. Ne samo to, ima posebne registre u skupu konfiguracionih registara kojima se predefinira njegovo ponašanje i odnos prema dijelu programske memorije gdje je smještena aplikacija, radni dio vašeg programa.

On nemora biti vezan za aplikaciju koja se piše a smještena je u dijelu programske memorije koja nije boot zona. Postavlja se pravilnim konfiguriranjem u fuseH registru gdje mu se odmah određuje i veličina, tj. dubina.

Konfiguracioni bitovi koji to definiraju jesu u donjem nibbleu tog registra. To su bitovi BOTSZ0 i BOTSZ1. Postavljanjem bita BOOTRST u "0" rekli smo HW-ru MCU-a da da starta od adrese definirane bootsz bitovima. Uvijek je postavljen tako da su mu zadnje adrese dubine ujedno i najviše apsolutne adrese programske memorije. Iz toga je jasno da dubinu računa od kraja prema najnižoj adresi koju određuje veličina bloka tj njegova fizička veličina u bytovima.Jesam zakomplicirao?

Detalje konfiguracionih bitova kao i maski bootlock bitova ( BLBx )imate odlično definirane u tehničkoj dokumentaciji tog processora i to u tabelama u zoni koja opisuje programiranje chipa. Pa da ne kopiram tabele. Za funkcionalni opis bootloadera nije toliko bitno.

Što bi to zapravo uopće bio bootloader i čemu uopće na njega "gubit vrijeme". Tehnološki uređaji današnjice gotovo da su preplavljeni razno-raznim računalima, uglavnom specijalne namjene. Tako da su mnogi ljudi, čak i oni koji nemaju ama baš nikakve veze s računalima, čuli za nešto što se zove bootanje (dizanje sustava). Ono "boot" na displayu u raznoraznim uređajima.

Usporedit ću ga, jer on to načelno a i funkcionalno je, s BIOS-om na matičnim pločama vaših kućnih ljubimaca IBM standarda, oli ti PC-a. No bit ću toliko zločest pa ću ga usporediti s MBR sektorom ( Master Boot Record ) vaših "tvrdih" diskova.

Jel vam sad bar malo jasnije koliko ste dobili ako mjesto primjera radi 0xCF stavite recimo 0xCC u fuseH registar ATm8? ... Nije! Uh.

Proizvođač vam je omogučio da kreirate pravi pravcati bootloader sa svim onim opcijama podržanim u konfiguraciji a koje možete po uzoru na, npr. BIOS, pretvoriti u veoma kvalitetan alat. Ne samo za samoprogramiranje ili čitanje podataka ili dijelova podataka iz bilo koje od memorija već možete, unošenjem algoritama za komunikacije, ili korištenjem HW-skih implemenata, stvoriti program kojim ćete moći testirati i kontrolirati vaše računalo, mijenjati temeljne konfiguracione postavke sustava, sve konfiguracione bitove kao i lock bitove. Nemate straha da ćete slučajno zaključati MCU jer izradom rutina dodanih na komunikacije, tkzv. SW leyerom, ili ti specifičnim protokolom možete napraviti da vaš bootlodar za vas radi sve ono, samo puno brže i kvalitetnije, što radi bilo koji programator. Čak i paralelni.

Page 29: 88702155 Programiranje u Assembleru

Primjer:Code: Select all

MAINB: rcall iboot ;-- testira dal ima zahtijev za bootloaderom s usb ili rs232 rcall AECCPY ; kopira tabele AES 128b decrypto rutinu i kljuc u sram rcall Testaek ; izracun AES prosirenog kljucaglx: clr ZH clr ZL clr r5gl0: rcall inst ; CITA NAREDBU lds r24, CMD cpi r24, 0xA0 ; brne gl3 rjmp wspmr ; self prg routinegl1: cpi r24, 0xA2 ; promijena kljuca dekrypcije!! brne gl3 rjmp akeyn ; upisi poz novog kljuca;gl2: cpi r24, 0xA4 ;brne gl3 ;jmp IEPRD ; int eprom readgl3: cpi r24, 0xA6 brne gl6 rjmp IEWR ; int eeprom writegl6: cpi r24, 0xA8 ; ---****** Kasnije izbrisati tj ostaviti automatski **** brne glA rjmp LOCKB ; upis blb i lock bitova ! glA: cpi r24, 0x9F ; izadji brne glB rjmp pts3 ; izađi iz bootloadera glB: rjmp gl0 ;

Na gornjem primjeru se vidi jedan običan protokol kojim upravljamo bootloaderm. Slanjem jedne od naredbi u SRAM iz PC-a

možemo odabrati ponuđenu operaciju ili izaći iz bootloadera.

U donjoj rutini vidite tipičan izlazak iz bootloadera. Primjer možete nači i u dkumentaviji proizvođaća.

Code: Select allpts3: ldi r22, 0x11 ; povratak u aplikaciju !! r22==spmval rcall spmr ; u kontrolni registar postavlja vrijednost za izlazak iz bootloadera. rjmp 0x0014 ; prva adresa programa aplikacije a u ovom slučaju i prva adresa nakon kraja prekidnog vektora.

Dodavanjem grafike i LCD-a dobijate veoma moćan alat i to popuno neovisam. Protokole upravljačkih naredbi emulirate u bilo koji od script jezika (java, php, VB, pyton... ili, jednostavnije, koristite hyperterminal ili telnet) i sa bilo kojeg PC-a možete kontrolirati rad vašeg mikroračunala. Dodavanjem algoritama enkripcije možete vaš kod aplikacije za upload dodatno zaštiti.

Page 30: 88702155 Programiranje u Assembleru

Mogućnosti su velike i ovise isključivo o vašoj maštovitosti i potrebama.

Od prve ATmege na dalje nikad nisam nijednu koristio bez bootloadera. Jednostavnijeg ili složenijeg, ovisilo je o potrebama.To nesmijemo nikako zaboraviti, SPM naredba ( Self Programming Mode) radi isključivo u bootloader zoni i mora biti aktivirana na konfiguracionoj maski. Ona je, ako se sjećam zadnja adresa prekidnog vektora. Kontrolom registara specijalne namjene vezne na tu naredbu koristite razne modalitete njene primjene. Tu je i prekidni vektor. Postavljanjem bita IVSEL premještamo prekidni vektor sa početka programske memorije na početak bootzone ako to želimo.

Ovako izgleda početak bootloaer zone u programskoj memoriji kad prvi put pišemo program a želimo odmah ubaciti bootloader.

.org 0x0C00 ; 1B00 2KB byte dubina / 1KW /// Primjer samo...

Da bude jasno, bootloader možemo napisati kao zaseban program i uprogramirati ga u prg. memoriju. Nemora biti pisan kao dio aplikacije. Važno!! Ako ste jednom upisali bootloader u vaš MCU netreba vam nikakva pomoć programatora. Jedino ako želite mijenjati sam bootloader (iako to može učiniti sam bootloader primjenom određenog algoritma) ili ste zaboravili postaviti algoritam za promjenu konfiguracionih bytova a iz nekog razloga to morate redefinirati.

Nadam se da vam je jasmo da ste s bootloaderaom dobili mašinu koju možete pragramirati, emulirate li protokol, koju možete veoma efikasno kotrolirati na daljinu i uploadati vaš program veoma jednostavno na veoma udaljenim točkama. Definiranjem konfiguracije BootLockBit-ova bootloader se može koristi da dozvoljava korištenje svojih rutina iz aplikacije u potpunosti, djelomično ili je potpuno zatvoren.

Ovako, o bootlodaru bi mogli danima pričat i teško da bi sve rekli. No nadam se da je nešto jasnije čemu on služi i koje vam mogućnosti pruža. Eto, ako se sjetitite onog s početaka, tako nešto, kad sam vam pričao o onoj neobičnoj naredbi (SPM) koja je izmjenila strukturu i ponašanje HW ali i mogućnosti postavljanja programa, same aplikacije. Bez nje bi sam bootloader bio prilično osakaćen.

Što ako ne želite u bootloader, netreba vam trenutno a postavljen je BOOTRST bit u "0" i imate upisam bootloader?

Evo jedna jednostavna rutina:

Code: Select alliboot: clr XH ;-------- scan ulaza u bootloader sa max211 i ft232r --- clr XL ldi r25, 20 iboo0: sbis PINB, 1 ; maximalno ceka oko 32 mS x 20 ==640mS

rjmp iboo1 clc adiw XH:XL, 1

Page 31: 88702155 Programiranje u Assembleru

brcc iboo0 ; ovdje je delay riješen emulacijom jer ionako mora čekati. dec r25 ; ovako duga kašnjenja se mogu koristit za autotest ili nešto slično. brne iboo0 rjmp pts3 ; izadji iz bootloader nema zahtijeva za ulaziboo1: ldi byte, 0x46 ; FBQ == 46h 42h 51h šalje potvrdu PC-u ili nekom drugom računalu da je ušao u boot rcall sendbyte ; na njegov zahtjev // to je neka vrsta identifikacije ili ti ID-a uređaja ldi byte, 0x42 ; rcall sendbyte ldi byte, 0x51 rcall sendbyte ret ;

Da ne zaboravimo spomenuti i ovo, ako koristite SPM naredbu i želite njome programirati vašu prg. memoriju ne zaboravite da sve ATmege korite pageing, oli ti u sram upiše stranicu (page) određene dužine , ovisi o MCU-u koji imate, i tad kad ste upisali sve baytove po zadanoj veličini stranice (len) pozivate rutinu za samoprogramiranje. U dokumentaciji je to dobro objašnjeno i primjerima u C-u i asembleru. EEprom podataka se ne upisuje u page modu već byte na byte.Želite li promijeniti samo jedan byte u programskoj memoriji iščitate čitavu stranicu gdje se taj byte nalazi u sram po page obraascu, postavljanjem početne adrese u indeksne registare, npr. XH:XL, i promijenite ga u sramu. Postavite početne adrese stranice prg. memorije,gdje je bio onaj bzte, u registre ZH:ZL i adrese srama u indeksne registre XH:XL i tad pozovete rutinu za samoprogramiranje. Na taj način možete mijenjati jedan byte ili više u prg. mem.

Prije nego što nastavimo dalje nebi bilo loše da se malo zadržimo oko Samoprogramiranja. Nije to samo pitanje rutine u bootloader zoni vezane na naredbu SPM već i mogoćnosti da utjećemo na program. Na njegovu strukturu, mjenjamo pojedine rutine ili kreiramo potpuno nove. Na taj način računalu možemo omogučiti samoučnje, izradu jednostavnih compilera koji su dio bootloadera ili aplikacije. Možemo kreirati svoj vlastiti operativni sustav podešen našim potrebama itd.

Pođimo najprije od te famozne SPM naredbe. Uz tu naredbu vezan je registar u polju registara specijalne namjene SPMCR (SPM Control Register) oli ti kontrolni registar SPM naredbe. To je više funkcija nego klasična naredba. Pravilnim postavljanjem bitova u SPMCR određujemo što će SPM naredba uraditi. Kao adresni brojači koriste se registri r31 i r30 znani kao ZH i ZL a kao registri za prijenos podataka u privremeni buffer (veličine stranice) koriste se registri r1 i r0 pošto su naredbe AVR-ATmega 16bitne.

Primjer jezgre rutine za samoprogramiranje: Samo da dodam, prije SPM naredbe onemogućite sve prekide naredbom cli !! Može i postavljanjem vektor u zonu bootloadera ali ovo je sigurnije.

Code: Select allspmr: sbic EECR, EEWE rjmp spmr out SPMCR, r22 spm

Page 32: 88702155 Programiranje u Assembleru

.dw $FFFF nopsp1: in r19, SPMCR sbrc r19, SPMEN rjmp sp1 ret

Prva naredba sbic provjerava status EECR bita u EEWE registru i ako je zadovoljen uvjet grananja program preskače prvu bezuvjetnu petlju rjmp i ide na slijedeću naredbu. Tom naredbom upisujemo željenu vrijednost u SPM kontrolni registar i njome određujemo njeno ponašanje, tj. koju će opraciju napraviti. Sad slijedi sama SPM naredba.

Nakon te naredbe se nalazi nešto što bi močlo neke i začuditi .dw 0xFFFF ( $ => još jedan od markera hex broja). Nalog računalu da odmah po SPM naredbi postavi dvobaytno "prazno polje". Neobično, jeli tako. No pošto je naše računalo malo "bedast stroj" ono će uredno pokušat izvršit naredbu. Naravno pošto ta naredba ne postoji u skupu naredbi samo će gubit vrijeme na putu da dođe do prve naredbe iz skupa.

Da ponovimo, računalo ne razumije simboličke naredbe assemblera već samo binarne, u našem slučaju 16bitne. Dobili smo ono što smo trebali, gubljenje vremena. Pretpostavljam da se pitate zašto moramo gubiti vrijeme. Ako vašem računalu postavite frekvencije iznad 12MHz treba mu malo vremena da pravilno izvrši SPM naredbu tj. dovrši proces upisa stranice u programsku memoriju. Mislim da se pitate zašto to nije moglo s NOP naredbom. Moglo je, no ovo je još jedan od primjera kojim sam želio pokazati kako je računalu relativan pojam čak i upotreba same naredbe. Pokazao sam vam još jedan od načina kako se može ostvariti malo kašnjenje. U pravilu to se radi NOP opracijama. Ova NOP naredba ispod .dw je dodana jer računalo radi na 16MHz. Ako bi koristili oscilator na 20MHz tada ubacite još jednu liniju .dw 0xFFFF.

Mislim da vam je razlog jasan. Pošto mašina brže radi a vrijeme programiranja stranice je konstanta treba mu i veće kašnjenje. Na feq. od 28MHz stavite najmanje tri .dw 0xFFFF linije ili adekvatan broj NOP naredbi.

U slijedećoj naredbi in r19, SPMCR čita kontrolni registar i sprema njegov radržaj u jedan od registara opće namjene (r19) jer se na registrima i skupa registara specijalne namjene nemogu izvršiti operacije testiranja bita kao što je slijedeća naredba. Naredba sbrc tesira bit SPMEN u registru r19 i ako je uvjet grananja zadovoljen ("0") odlazi na naredbu ret tj. vraća se u potprogram iz kojeg je pozvan.

Znam što se pitate, kako se SPMEN (SPM eneable = omogući) našao u r19 kad je on bit registra specijalne namijene SPMCR. Jednostavno. Sjećate se našeg uvoda u pisanje programa i onog početka pozivom na m8def.inc? Tad sam rekao da su svi ti SPMCR, TIME0, PORTB itd.. samo običan tekst koji zamjenjuje broj ili adresu registra, ili bit u registru...

Upravo to, SPMEN je običan broj a u našem slučaju to je bit 0. Znači da smo istu stvar mogli dobit da smo napisali naredbu na slijedeći način sbrc r19, 0.

Vjerojatno ste primjetili kako se registri ZH:ZL koriste i kod naredbe LPM kao adresni brojači no oni se koriste i kod veoma zanimljivih no rijetko korištenih naredbi indirektnog skoka ijmp i indirektnog poziva icall.

Page 33: 88702155 Programiranje u Assembleru

Od svih registarskih parova samo se ZH:ZL koristi u naredbama koje zahtjevaju privremenu predefiniciju adresnog brojača, njegove vrijednosti: SPM, LPM ili trajnu: icall i ijmp. Kao što se ta dva indeksna registra korite za posebne operacije, tj operacije s adresnim brojačem tako se i dva registra iz skupa registara opeće namjene također koriste za unos ili prihvat podataka iz naredbi koje rade s čitanje ili upisom u programsku memoriju ili naredbama množenja a to su registri r0 i r1.Isključivo preko njih možemo upisivati podatke u privremenu zomu rama prije upisa u programsku memoriju.

Primjer:

ldi r22, 3 ; pobriši stranicurcall spmr wsp1: ld r0, Y+ ; upiši u straniculd r1, Y+ldi r22, 1 ; puni temp registar 32Word (16bit)rcall spmradiw ZH:ZL, 2 ; uvecaj adresucpi YL, 0x80brne wsp1ldi r22, 5 ; upiši stranicu u programsku memorijurcall spmr

U gornjem primjeru vidimo kako izgleda postupak programiranja jedne stranice (page) u programsku memoriju. Kao i kod programatora moramo najprije pobrisati stranicu koju u mislimo upisati nove podatke. Brisanje znači postavljanje svih bitova u stanje logičke "1" ( 0xff). Kao i svi EEPROM-i (elektrikčki izbrisive programibilne memorije) tako i naša u ATm8 može programirati samo logičke "0" i zato mora biti posemnim postupkom koji je dio HW-a dovedena u gore opisano stanje.

Slijedi upis u privremenu zonu srama (page) i to isključivo preko registara r1:r0. Iz primjera se vidi kako se pomoću indeksnih brojača ( registraskog para XH:XL) iz srama naredbom ld (load) uzimaju podaci i upisuju u navedene registre r1:r0 ( r1 drži viši byte 16 bitnog broja a r0 niži). Nakon svakog postavljanja podataka moramo postaviti SPMCR registar u opciju "puni temp. registar" i pozvati rutinu izvršenja SPM naredbe. Kontrolom vrijednosti indeksnog brojača pratimo jesmo li upisali cijelu stranicu u privremeni dio rama. Ako jesno postavljanjem opcije za upis u programsku memoriju pozivamo rutinu za izvršenje naredbe SPM (rutina u prošlom članku). To je dio potprograma, zapravo samo rutina, koji služi za programiranje naše ATm8.

Naredba LMP isključivo u r0 upisuje pročitani podatak iz programske memorije.

Primjer:lds ZH, MENPOlds ZL, MENLOlpmmov r18, r0

Čitamo podatak iz programske memorije na adresi koja je definirama na početku programa,

Page 34: 88702155 Programiranje u Assembleru

može i bio gdje drugdje u programu, definicijom .equ MENLO=0x0200 i .equ MENPO=0x0201. Naredba lds čita podatak iz srama i stavlja ga u indeksni registar (ZH ili ZL). Slijedi čitanje naredbom LPM (Load Program Memory) a zadnjom naredbom rezultat koji se nalazi (isključivo) u r0 premještamo u r18. To je opciono, nemoramo rezultat premještati nigdje ako na to nije potrebno. Funkcionalno naredba mov kopira sadržaj jednog registra u drugi.

Naredba mul (množenje 8 bitnog broja) pohranjuje 16 bitni rezultat u registre r1:r0.

Primjer:ldi r16, 5 ; upisujemo množitelj u registar r16ld r18, X+ ; množenik uzimamo iz sramamul r18, r16 ; množimo ta dva registramovw ZH:ZL, r1:r0 ; rezultat množenja iz para r1:r0 premještamo u reg. par ZH:ZL

U ovom primjeru množitelj je fiksan broj (5) u registru r16 dok smo moženik uzeli iz srama čije je ukazivač adrese indeksni registarski par XH:XL i smjestili ga u registar r18. X+ znači da će se nakon čitanja podatka iz srama indeksni registarski par uvećati za 1. Slijedi sama naredba množenja mul dva registra a rezultat iz r1:r0 je u ovom slučaju pohranjen u indeksne registre ZH:ZL.

Naredbe icall i ijmp omugučavaju ne samo direktan skok bilo gdje u program ijmp ili poziv potprograma/rutine icall već nam ostavljaju prostor redefiniranju programa, izradu operativnog sustava, jednostavnog compilera itd. Omogućuju modularnost sustavu, jednostavnu nadogradnju i sl.

Kod upotrebi tih naredbi morate znati točno što radite jer one pozivaju icall tj. skaču ijmp na apsolutnu adresu programske memorije navedenu u registrima ZH:ZL (a ako imate prg. mem. veću od 64k i vrijednošći bita rampz). Njih je dobro koristiti sa nekim jednostavnim file sustavom, virtualnim, ili markerima apsolutnih adresa svih potprograma i rutina u programskoj memoriji spremljenih u samu prg memoriju ili memoriju podataka.

U kombinaciji s njima i bootloader zonom te SPM naredbom možete dobiti veoma dinamične strukture programa gdje se rutine i potpogrami mogu čuvati bilo gdje, npr. u vanjskoj serijskoj memoriji, MMC/SD kartici i sl. te pozivati po potrebi i upisivati u programsku memoriju i na taj način davati neke nove sadržaje vašoj aplikaciji. Učiniti je modularnom bez potrebe da se nanovo reprogramira čijela prog memorija.

Primjer postavljanaj tih naredbi:

ldi ZH, high(SETBYT)ldi ZL, low(SETBYT) ;ijmp

Bezuvjetni skok na zadanu adresu u programskoj memoriji postavljene u ZH:ZL.

ldi ZH, high(SETBYT)ldi ZL, low(SETBYT) ;icall

Poziv potprograma ili rutine po osnovi apsolutne adrese programske memorije postavljene u

Page 35: 88702155 Programiranje u Assembleru

ZH:ZL.

Vjerujem da vam je jasno koliko i najmanja greška može zeznuti stvar. No uz malo pažnje nebi trebalo imati straha.Bude li vremena možda se viže pozabavimo tim opcijama.

Došlo je vrijeme da se okrenemo samoj izradi programa tamo gdje se on zaista i odvija u potprogramima i malim programskim odsjećcima; rutinama, koje često obavljaju jednostavne zadaće. No prije svega toga moramo znati da za nas nitko neće voditi brigu o ničemu, jer asembler je običan sastavljač s minimumom uplitanja njegova autora u kod koji ste napisali. Zapravo uplitanja kao kod HPL-a nema. Uglavnom se radi o prostim predefinicaja opisanima u ranijem tekstu. I što sad? Nebi bilo loše da se pozabavimo samim naredbama asemblera, odnosno skupon naredbi koje razumije naš ATm8 mCU.

Naznakovitije naredbe bilo kojeg programskog jezika jesu naredbe uvjetnih grananja. One stvaraju u nama osjećaj kako je računalo neki zaista pametan stroj. Kod viših programskih jezika obično se nazivaju petlje kao: for-next, do-loop, while-do... itd. Bez naredbi uvjetnih grananja (i bezuvjetnih) praktički nebi bilo moguće napisati ni jedan iole smisleniji program.

Pitate se kako neke od gore navedenih naredbi HPL-a izgledaju u asembleru? Asembler ne poznaje tako što i u njemu to ne postoji, bar ne na taj način no postoji veoma moćan podskup naredbi za uvjetna i bezuvjetna grananja. Sve naredbe uvjetnih grananja su u principu, a i funkcionalno, povezne za jedan registar, počesto izgubljen u masi drugih "važnijih" registara i problema, a to je STATUS-ni registar. Bitovi tog registara, ne svi (npr. bit općeg prekida ne), označavaju staus opracije nad podacima, oli ti rezultata, u samoj ALU (arihmetičko-logičkoj jedinici). Zato je taj registar direktno vezan na nju. To su gotovo u pravilu, stanje nule (svi bitovi rezultata = 0x00), Negativnog rezultata, preljeva, prijenosa, poluprijenosa itd... Bitovi tog registra se mogu testirati svaki posebno, može se cijeli isčitati a mogu se i upisivati.

Primjer za prekid bit7 I :

sei

Korištenjem gore navedene naredbe sei (set interrupt = omogući opći prekid) u bit7 I STATUS registra upisuje se logička jedinica i na taj način omogučava opći prekid bez kojed bi nam korištenje ISR-a, prekidnog vektora, bilo besmisleno, kao i sve naše definicije HW-skih timera, brojača, USARTA, I2C komunikacija, SPM-a itd...bez obizira na naše postavljanje bitova za omogućavnje prekida u registar niže razine xxxxCR. O tome je već pisano kod konfiguracije timera.

cli

Tom naredbom (clear interrupt = onemogući opći prekid) u bit7 (I) STATUS registra postavljamo logičku nulu i, kao što sam naziv kaže, onemogućavamo sve prekide na svim razinama. Prekidni vekor je tad neupotrebljiv.

Petlje. Programska grananja se u asembleru rade na najednostavniji način, tesiranjem bita STATUS registra ili upotrebom naredbi za testiranje statusa bita I/O porta (npr. sbic ili

Page 36: 88702155 Programiranje u Assembleru

sbis).

Primjeri grananja, tj. petlji, testiranjem bita Z ( rezultat =="0") STATUS registra:

ldi r25, 8 ; test1: dec r25 ; umanji za 1brne test1 ;

Naredba brne (granaj ako nije jednak na test1) se najčešće koristi nakon naredbe usporedbe dva registra no pošto je to računalo, a ne ljudski mozak, rekli smo da nakon svake opracije ALU u status registar upisuje trenutno stanje. Kad r25 dođe do nule u tom registru će zatavica Z biti postavljena i testiranjem bita Z naredbom brne, koja naizgled nema nikakve veze s tim, program će izići iz petlje.

Stvar možda zvuči čudno jer nismo rekli s čim naš r25 mora biti jednak. U asembleru, bar svima u kojima sam radio, nedefinirani uvijet kompracije ALU uzima kao nulu i to je sva tajna. Tako da naš brne u ovom slučaju znači ako je r25 =0x00 izađi iz petlje. Ona, ta naredba, stvarno znači suprotno, kao što je i napisano na početku, no pošto to računalo ne razumije, ono jednostavno tako ne radi, u stvarnosti testira bit Z i ako nije ili je postavljen odrađuje operaciju (naredbu) grananja. Slično vam rade i uvjetna grananja na veći ili manji operand. Mislim da slutitite i kako. Većina toga se vrti oko bita Z, N i C. Z je apsolutni favorit.

test2: rcall readbytest X+, byte ; pocinju naredbecpi XL, 0x66brne test2Ovo je primjer najčešćeg oblika korištenja uvjetnog grananja naredbom brne iako se funkcionalno opet svodi na testiranje bita Z STATUS registra jer ALU tj programski slijednik daje nalog ALU-u da oduzme vrijednost u registru XL i, u našem slučaju, vrijednosti 0x66 s tim da se vrijednost u registru ne mijenja. Ako je rezultat različit od nule neće biti zadovoljen uvjet izlaska i naš će programa biti u petlji (ide na grananje test2) dok se on ne zadovolji.

mov r1, roundtest3: tst r1breq test3aadd r0, r3dec r1rjmp test3test3a: ret

Nareba tst testira jeli vrijednost u našem registru r1, mora biti jedan od registra iz skupa registra opće namijene, jednaka nuli i, ovisno o rezultatu, mijenja status Z bita koji kasnije možete testirati nekom od naredbi uvjernog grananja. U našem slučaju breq (granaj ako je jednak). Pošto testiramo nulu nije potrebno navoditi kome je jednak iz ranije opisanog razloga. No kako rekoh u najvećem broju slučajeva sve se svodi na testiranje bita Z.

Mogli bi redati gomile primjera različitih testiranja na nulu. Mnoge naredbe uvjetnog grananja jesu logičke operacije, najčešće u formi ekskluzivno OR (eor,xor...) i testiraju najćešće bit Z.

Primjer za grananje na bit C STATUS registra (zastavica preljeva):

Page 37: 88702155 Programiranje u Assembleru

test4: lsl r14brcc test4eor r14, r19

Naredba brcc (Granaj ako je zastavica grananja, bit C, jednaka nuli). Obično se prije upotrebe ove naredbe bit C briše naredbom clc (briši zastavicu prijenosa). Naravno, nemora uvijek biti tako. Važno je zapamtiti da se nakon najvećeg broja naredbi mijenja i stanje zastavica STATUS registra shodno rezultatu u ALU-u. Asembler vam dozoljava da na zaista mnoge načine odradite i najsloženjija grananja. Ne postavlja nikakve posebne uvjete oko toga. Sve ovisi o vama.

Pošto je programska petlja (uvjetna i bezuvjetna grananja) veoma važna stvar o njima ćemo još pričati.