Programiranje u Assembleru

Rasprava o AVR mikrokontrolerima, AVR projekti i drugo vezano za AVR...

Moderators: pedja089, stojke369, trax, InTheStillOfTheNight

Post Reply
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Programiranje u Assembleru

Post by buco-1 »

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.

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.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
User avatar
InTheStillOfTheNight
Odlično uznapredovao
Odlično uznapredovao
Posts: 938
Joined: 01-06-2006, 17:54
Location: Zagreb

Re: Ružno pače

Post by InTheStillOfTheNight »

Svaka chast @buco. Znaš da uvijek podržavam sve tekstove napisane u kojima se nešto novo može naučiti. Ja ću tartufe pisati vjerojatno dugo, no cilj tartufa je svakako C, a ne assembler. U svakom slučaju assembler je neizbježan za suštinsko razumjevanje računala, pa ću ga često morati spomenuti. Tartufe sam tek krenuo pisati i još nema nekih velikih nauka oko C-a, kao ni oko samog AVR-a. Mislim da bi bilo dobro da pišeš za MEGA88 jer svi moji primjeri C-a biti će za taj AVR. Mada je tebi apsolutno svejedno jer je set instrukcija AVR-a identičan...

Često sam čuo od početnika kako njima ne treba ništa profesionalno. Kao bitno je da oni mogu na jednostavan način nešto napraviti. Moje mišljenje je sigurno kako je to loš put i loš način. Postoje programeri, i oni koji to nisu. Za biti nekakav programer svakako je potrebno poznavati C i assembler. (što se tiče MCU-a). Meni je @kizo specifičan po tom pitanju. Razumije MCU, no ipak sve piše u Bascomu što je dokaz da se može znati dosta assemblera, a isto tako programirati u Bascomu... Tvoja ideja mi je fenomenalna, pogotovo da se napravi eep assemblera i sigurno da ću čitati i aktivno sudjelovati u pomoći...
InTheStillOfTheNight
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Ružno pače

Post by buco-1 »

InTheStillOfTheNight wrote:Mislim da bi bilo dobro da pišeš za MEGA88 jer svi moji primjeri C-a biti će za taj AVR. Mada je tebi apsolutno svejedno jer je set instrukcija AVR-a identičan...
Točno. Set instrukcija, za čitavu porodicu ATmega a jako malom razlikom i AVR, su identični. No nešto bi se dalo urediti da realni primjeri budu za megu88. Pokušat ću se u početku bazirati više na općenitom, zajedničkom porodici ATmega/AVR, s osvrtom na neka riješenja u seriji PIC16xxx (no to ovisi kako vrijeme bude dopuštalo).
InTheStillOfTheNight wrote:Često sam čuo od početnika kako njima ne treba ništa profesionalno. Kao bitno je da oni mogu na jednostavan način nešto napraviti. Moje mišljenje je sigurno kako je to loš put i loš način.
Apsolutno dijelim tvoje mišljenje. Posebno je assembler problematičan. Njega malo tko voli, zapravo najradije bi ga izbjegli da ikako mogu čak i profesionalci. Ipak, to je nemoguće, bar za sada jer nema toliko dobrog HPL-a. Zato sam priču o assembleru i nazvo po onoj poznatoj H.C.Andersena.
Naizgled je strava, užaš. Teško razumljiv, užasno zahtjevan jer traži puno raumijevanje mnogih stvari no kad ga jednom upiješ tad shvatiš koliko je moćan i koliko si na dobitku.
InTheStillOfTheNight wrote:Ja ću tartufe pisati vjerojatno dugo, no cilj tartufa je svakako C, a ne assembler.

Nadam se skorom nastavku.

P.S. Hvala na podršci!
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Ružno pače

Post by buco-1 »

Nastavak:
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 "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 (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.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Ružno pače

Post by buco-1 »

Nastavak:

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, 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 2
BSF  STATUS,RP1
MOVF 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 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 memoriji

Code: 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 0x0800
ldi 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.

Primjer razlike u nazivima istih naredbi (gledano u grubo):

Code: Select all

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

movlw 5       ; neposredno punjenje akumulatora PIC16xxx serije
movwf 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.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Ružno pače

Post by buco-1 »

Nastavak

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).
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.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

Nastavak

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 !!!
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)
;
;
;*****************************************************************************
; 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 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 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 treba
INIT:   ldi r17, 4                  ; početak programa
Za sad toliko.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

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-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 KOMENTARI
INIT:
ldi r17, 4 ; 4 ATm8 INIT STACK
out SPH, r17 ; može se koristiti i predefinicija iz m8def zvana RAMEND
clr r17 ;
out SPL, r17 ;
lds r17, SFIOR
ori r17, 8
sts SFIOR, r17 ; onemogucen ADC i ADC mux
sbi ACSR, 7
cbi 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
;andi r17, 0x7F ; brise bit 7
;out EMCUCR, r17
out TIFR, r17
ldi r17, 0xA8 ; ivsel=0 // omogucen SLEEP mode s Power down i padajucim sign
out MCUCR, r17 ; 38 ==8515 // m8==A8
ldi r17, 0x40 ; omoguci prekid preko INT0 tu ce bit OK taster kasnije onemoguciti
out GICR, r17
nop
clr r17 ; setiranje timera
out TCCR1A, r17 ; normal port operation / bez PWMa i OCNa
out TIMSK, r17 ; timer stopiran
ldi r17, 5
out TCCR1B, r17 ; preskaler postavljen na 1024
ldi r17, 0x5F ; EE // B7 --
ldi r16, 0xC8 ; D1 //18 A037 presk 1024 14MHz== 3S compl==5FC8
out TCNT1H, r17
out TCNT1L, r16 ; TIMSK ce kasnije bit setiran na OVFL mode
ldi r17, 4
out TCCR0, r17 ; postavi timer 0 u normal mod s preskalerom 256
;clr r17
; out GIFR, r17 ; prekidni flagovi u 00
ldi r17, 0xFF ; bio F8 za 5V
out OSCCAL, r17 ; 14MHz
nop
clr r17
out DDRC, r17
out PORTC, r17
ldi r17, 0xC0 ; svi ulazni osim PD6 i PD7
out DDRD, r17
nop
ldi r17, 0xFF
out DDRB, r17 ; izlazni portovi
out PORTD, r17
nop
cbi PORTD, 6 ; iskljuci TX / primjer nemaskiranog, direktnog, postavljanja pina u "0"
ldi r17, 0x3E ; bit 6==0 iskljuci TX i 7==0 iskljuci sound
out PORTB, r17
nop
ldi r17, 0x80
sts Anim1pos, r17 ; upisuje vrijednost iz r17 u ram određen pozicijom Anim1pos
sts Anim1old, r17
sei ; omoguci opci prekid generalno
rjmp 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 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 SFIOR
ori r17, 8 ; vršimo maskiranje bita postavljanjem logicke jedinice i u bit 3
sts SFIOR, r17 ; spremamo novo stanje u SFIOR

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

Može i ovaj primjer
in r17, SFIOR
andi r17, 0xFB ; omoguci PUD (pullup) bit 2 / 1=disable 0=eneable
out 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 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 !!!!
nop
in r17, GICR
andi r17, 0xBF ; brise bit 6 onemoguci prekid s I/O porta PD2 ==INT0
out GICR, r17 ; to je samo jedna od multipleksiranih funkcija porta MCU-a
nop

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 ZH
clr ZL ;
clr PNS ; postavi normal mode za znakove
rcall IEPRD ; cita interni eeprom
str1: mov Maxlen, byte , podatak iz eeproma sprema u registar Maxlen
rcall LCDini ; vrši inicijalizaciju LCD-a pozivom na rutinu/e u driveru
rcall SKCOPY
ldi YH, 1 ; pos srama sa podacima za svaki skuter / time i status
clr YL
sts Slika1, YL
clr inL
clr tmH
inc tmH
strX: rcall M1SLIKA ; postavljamo pocetnu sliku na LCD
mov tmL, Maxlen
ldi LINEX, 0x40
strs: inc LINEX
cpi LINEX, 0x46 ; po 5 skutera
breq MAIN
Naredba uvjetnog grananja breq njen doseg, dubina, je 256. Macroasembler će vas upozorit ako pređete maximalnu vrijednost.

rcall LINxy
rcall 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.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

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.

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 DF80
bbg:      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
          clt
avr00E1:  ld     r18, X+      ; 00E1 912D
          rcall  avr02FD      ; 00E2 D21A
          dec    r17          ; 00E3 951A
          brne  avr00E1       ; 00E4 F7E1
          set
          rjmp   avr018C      ; 00E5 C0A6
avr00E6:  ldi    r17, 0x69    ; 00E6 E619
          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 C09E
avr00EE:  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 flasa
vlki:     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 E110
avr010D:  sts    0x0067, r17  ; 010B 9310 0067 len
          rjmp   avr018B      ; 010F C07C
avr0110:  cpi    r17, 0x0B    ; 0110 301B   P1 0B (country code other way)
           brne  avr0114      ; 0111 F411
          ldi    ZL, 0x3B     ; 0112 E4E8 coco
          rjmp   avr010A      ; 0113 CFF6
avr0114:  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 eepromu
avr0125:  cpi    r17, 0x0E    ; 0125 301E  P1 0E (read card file)
           brne  avr013D      ; 0126 F429   staviti CF umjesto 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 018C
avr012C:  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 C058
avr0133:  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 C04E
avr0147:  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
          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 CF7D
avr0169:  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 9468
avr0180:  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 odgovora
avr018C:  ldi    r21, 0x00    ; 018C E050
avr018D:  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 all

MAIN:    rcall  TASTS         ; bivsi TASTE
         clr  TMARK           ; marker za operaciju umanji TIMER skutera
         clr  COUNT           ; brojac prolaska 6S [timer mode] * 10 == 60S // 1min
         ldi  r17, 4          ; bio 80
         out  TIMSK, r17      ; ukljuci timer1 16bitni na OVFL mode 
         clr  YL
TAS1:    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 Gore
sbis 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 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 Vpp
lds r17, PORTG
ori r17, 0x10
sts PORTG, r17
nop
.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 all

MAIN:    call KRENI         ;--- cita status slota -----
         cp r19, r17
          breq maia          ; 
         sts SLOTO, r17      
mair:    rcall AUTOMD         ; ide na auto detekciju stanja slota
maia:    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.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

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šnjenje
brne DEL14
ret
;
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 ;
nop
nop
nop
nop
nop
nop
nop
nop
nop
brne DELAY
ret

;
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, 0x02
out OCR1BH, r16 ; komparaciona vrijednost u H i L regostrima / 16 bitni
ldi r16, 0xE8 ;
out OCR1BL, r16
ldi r16, 0x01

out TCNT1H, r16 ; pocetna vrijednost brojaca u H i L registrima / 16 bitni
ldi r16, 0x00
out TCNT1L, r16

ldi r16, 0x20 ; postavljanjem bita 5 u registru i njegovim kopiranjem u TIMSK postavili smo
out 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).

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, 3
out 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, r17

out 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.

Prije odlaska u "zonu sumraka" mogli bi vidjeti što je to zapravo bootloader. Cemu on služi i što može.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

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. :shock:

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.

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 kljuca
glx:      clr    ZH
          clr    ZL
          clr    r5
gl0:      rcall  inst         ; CITA NAREDBU 
          lds   r24, CMD
          cpi   r24, 0xA0    ; 
          brne   gl3
          rjmp   wspmr        ; self prg routine
gl1:      cpi   r24, 0xA2    ; promijena kljuca dekrypcije!!  
          brne  gl3
          rjmp  akeyn         ; upisi poz novog kljuca
;gl2:      cpi   r24, 0xA4
          ;brne   gl3
          ;jmp   IEPRD        ; int eprom read
gl3:      cpi   r24, 0xA6
          brne   gl6
          rjmp  IEWR         ; int eeprom write
gl6:      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 all

pts3:    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. 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 all

iboot:   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
         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 ulaz
iboo1:   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.

etc...
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

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 all

spmr:      sbic  EECR, EEWE     
           rjmp  spmr
           out   SPMCR, r22        
           spm
           .dw  $FFFF
           nop
sp1:       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.

O tome više u nastavku.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

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 stranicu
rcall spmr
wsp1: ld r0, Y+ ; upiši u stranicu
ld r1, Y+
ldi r22, 1 ; puni temp registar 32Word (16bit)
rcall spmr
adiw ZH:ZL, 2 ; uvecaj adresu
cpi YL, 0x80
brne wsp1
ldi r22, 5 ; upiši stranicu u programsku memoriju
rcall 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, MENPO
lds ZL, MENLO
lpm
mov r18, r0

Čitamo podatak iz programske memorije na adresi koja je definirama na početku programa, 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 r16
ld r18, X+ ; množenik uzimamo iz srama
mul r18, r16 ; množimo ta dva registra
movw 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 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.
etc...
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

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 sbis).

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

ldi r25, 8 ;
test1: dec r25 ; umanji za 1
brne 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 readbyte
st X+, byte ; pocinju naredbe
cpi XL, 0x66
brne test2
Ovo 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, round
test3: tst r1
breq test3a
add r0, r3
dec r1
rjmp test3
test3a: 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):

test4: lsl r14
brcc test4
eor 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.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

Prije nego što krenemo naprijed, jedno izvinjenje. Naredbu SPM (Store Program Memory) sam krivo interpretirao kao Self Programming Mode. SPM je i pojam, igrom slučaja, koji se koristi ne samo kod ATMega jezgri već i kod drugih MCUa, IC-ova koji imaju mogućnost samoprogramiranja.
Pošto se najveći dio pojašnjenja kratice SPM-a odvijao kroz pojam, tj njenu funkciju, došlo je do previda kod "prevođejna" kratice SPM, u dijelu teksta, kad sam govorio o istoj kao naredbi, instrukciji samog procesora. Nema isprike za grešku no... ljudi smo.

Višestruke petlje, možda bolje reči tok/tokovi programa.

Sjećam se koliko je bilo problematično ljudima ispetljati se iz petlje u petlji u petlji u petlji... Ni dan danas mi nije jasno zašto su nekad na informatici davili ljude, već tad, potpuno zastarjelim programskin jezikom; Basic. Bilo je puno važnijih stvari koje su se mogli predavati ljudima: kao rad s računalom, operativnim sustavima, korisničkm aplikacijama itd...

Da se ne nađu uvrijeđeni korisnici Basic alata za programiranje MCU-a, to se najviše odnosi na programiranje klase IBM x86 mašina čije programiranje je drastično različito od programiranja računala specijalne namjene (ovog našeg MCU-a), kako je već i rečeno.
Iskreno, mislim da je njegova upotreba kod programiranja MCU-a ostatak te prošlosti koji će se još dugo povlačiti, bar tu kod nas, nažalost, jer je naš školski sustav prilagodljiv k'o dinosaur. Dok se mi prilagodimo novom razvijeni svijet je već dosta sg-a. daleko odmakao.

Pretpostavljam da će se netko sad nasmijati i reči paa... što će onda tu assembler? Pitanje bi bilo potpuno na mjestu da se radi o programiranju gore spomenute klase računala (IBM x86). Zamislite assembler u kojem imate, samo, desetine načina indeksnog adresiranja i hrpne naredbi vezanih na to. Još je stari Intelov procesor 8086 imao dvadesetak naredbi u klasi indeksnog adresiranja kao i: Motoraola 6800, ZX80, 6502... ili Cosmac 1802. Nije samo razlika u tome jeli Von Neuman (to obično krivo napišem) ili Harward tip, CISC ili RISC, razlika je puno dublja.

Asembler je najprije neka vrst filozofije a potom način programiranja. Tek na kraju je nešto što bi mogli nazvat programskim jezikom. Mislim da je to rečeno i u uvodu priče o njemu.
Za računala opće namjene, PC x86 standard, bilo bi veoma nezgodno pisati složene programe u assembleru dok je za računala specijalne namjene on jedan od namoćnijih alata. Nije ga teško svladati, zbog relativno malog skupa naredbi, kao i relativno jednostavne struktrure i kompleksnosti programa koji se pišu za njih. No, da nebi isplao kako je to mačiji kašalj govori i činjenica da i nije neka gužva oko asemblera čak i na tim malim računalima.

Neki bi mogli reči pa nije stotinjak naredbi baš malo. Znate, pogledajte bilo koji program pisan u asembleru, ili diasemblirani program pisan u HPL-u, i vidjeti ćete da se ne koristi ni trećina naredbi iz skupa.
Ima naredbi koji programeri u assembleru možda nikad nisu koristili, kao ni compileri, ili veoma rijetko. To je i zbog onog ranije pisanog, da je računalo u biti prost stroj koji "razumije" tri logičke opracije i dva posmaka bita. Sve ostalo je njihova kombinacija.

Petlje u petlji u... khm, se koriste u asembleru isto, gotovo isto, kao i u HPL-ima. Glavni program je najbolji primjer za to. Najčešće su to i pozivi iz složenijeg potprograma, kojima počesto prethodi uvjet, na jednako tako nešto složeniju rutinu, npr. komunikaciju ili driver, indentifikaciju nekog uređaja, dijela HW-a i sl... Znači tvore neku vrstu programske petlje, manje ili veće složenosti vraćajući se uvijek na početak, u glavni program.

Primjer:
Čitanje ID-a i svih drugih relevantnih podataka iz flash mem kod njene autoidentifikacije i pripreme za programiranje a pozvan iz glavnog programa:

( Dio potprograma)
;
;
swi5: ldi r17, 0x55 ; SST algo
mov alg1, r17
call AMIDRD ; poziv na rutinu za čitanje ID opcije
call PODACI ; potprogram koji radi s bazom podataka za tipove memorija
cpi r24, 0xFF
breq swi6
jmp devok ; memorija pronađena pa ide na potprogram za testiranje CFI moda itd...
swi6: ldi byte, 3 ; rutina koja šalje odgovor drugom računalu.
call sendbyte
ldi byte, 4
call sendbyte
ldi byte, 0xFF
call sendbyte
mov byte, mfd
call sendbyte
mov byte, devd
call sendbyte
jmp MAIN ; vraća se u glavni program

;

AMIDRD: ;rdy_bsy ; SW ID citanje AMD MODE ( To je djelić potprograma no... dovoljno)
;nop
clr mfd
cli_CE
clr hiadr
clr loadr
ldi shiadr, 0x20 ; Vpp
sts PORTF, shiadr
nop
clr r17
out PORTC, r17 ; Hidata==0
nop
;call DEVRES
U ovom, gornjem, dijelu se postavljaju realni parametri HW-a našeg MCU, portovi i kašnjenja, da bi program mogao odraditi traženu operaciju sa traženom flash memorijom (16 bitna nand flash).

ldi byte, 0xF0
call AMDAS ;----- poziv na rutinu koja drži algoritam upisa u tja tip mem
ldi r17, 100
call DELMCS ; kasnjenje (SW-sko)
ldi r19, 20 ; -----
amdi0: ldi byte, 0x90 ;
call AM16A ;------- rutina koja radi s relanim HW-om MCU-a/Mem , adr/podaci
clr r24
amdi1: out PORTB, hiadr
out PORTD, loadr
nop
ulaz16 ; primjer upotrebe macro naredbe
amdif: nop
cli_OE ; OE=0 ------još malo realnog HW pa tako i kašnjenja. synhronizacija MCU-a
nop ; --- i naše memorije.
nop
nop
nop
in r24, PINA
in r25, PINC
set_OE ; OE=1

Vidite na ovom gornjem primjeru sinhronizacije da u realnom vremenu, programu vašeg MCU-a, on, MCU, nigdje ne trči. Nema od od trčanja ništa.
Nažalost, iz HPL-a se počesto nevidi stvarno stanje stvari i programer počesto misli da njegov program negdje žuri. U gornjem primjeru 50nS cyklus ATm128 postavlja OE i čita podatak sa 16bitnog ulaza/izlaza nand flash memorije , koja je relano po brzini tu negdje, čak i brža (svi novi tipovi), no da bi ispravno odradili algoritam mora imat kašnjenje inače podatak neće biti isčitan ispravno. Kao što vidite prije samog čitanja odradi 4 NOP-a da bi 100% ispravno pročitala podatak sa portova.

Razumijem da ljudi koji linkaju drivere u svoj program, ne pišu ih sami, to nevide i,ili, misle da on nema realno kašnjenje upisano u driver pa to leti. To je još jedan od prednosti programiranja u assembleru, jer točno znate: što i tko i koliko vremena troši i nemate zabludu da vaš stroj ima vremena na bacanje. Nema, vjerujte.

Samo programi "internog" karaktera, koji su najčešće u MCU-u sami sebi svrha, ili su dijelovi složenih kalkuacija (npr. kripto/decripto rutina, koja se, usput budi rečeno, riješava drugim tipom MCU-a ili čak IC-a, ASIC npr), nemaju potrebu za sihronizacijom tj. imamo osjećaj kako "lete", jure.

Real time opeacije su tu veoma jasne. Nikakve time shareing i multitasking opcije neće tu pomoć. Realna operacija u stvarnom vremenu, kao ova gore, će uvjek tražit svoje vrijeme izvršenja u fomi kašnjenja. Kod jako dugih kašnjenja, kao kod: usart protokola, složenih kripto programa(recimo da je trebate) i sl, može se govoriti, nešto već i je, o nekoj vrsti vremenske preraspodjele (time shareing) poslova.

Mislim da je multitaskig (odvijanje dva ili više programa u isto vrijeme, virtualno naravno) malo neozbiljno spominjati na ovoj klasi mikroračunala. Za eksperiment je to moguće izvesti no u praksi, kod rada sa vanjskim sklopovima, za što je taj tip mikroračunala i napravljen, je to nepotrebno i ne lako izovodivo. Najčešće zbog prevelikih realnih kašnjenja i besmisleno. Principielno i stvarno, jedna jezgra može vuć u istom trenu samo jednu operaciju.

HW-ski timeri/brojači/pwm, komunikacijski sklopovi itd... pa čak i portovi, nisu dio same jezgre mikroračunala što se vidi i iz njegove arhitekture. Oni su, njihovi registri, spojeni na internu sabirnicu i ne utječu na sam rad računala, prosesor s njemu pripadajučom sabirnicom, memorijama.
Samim time rad tih sklopova se nemože ni nazivat time shareing-om ili multitasking-om. Oni su samo pridodani standardnoj jezgri kao i ram te programska memorija, no to dvoje s njime čini srce samog računala dok ostali sklopovi ne.

Nastavljamo dalje:

devok: call CFImode ; --- poziva jedan složeniji potprogram od njega samog.
mov r17, XL
subi r17, 0x20
sts 0x01E0, r17 ; cfi len
clr r17
sts incmdx, r17
call BLOKSTAT ;----- potprogram koji testira stanje blokova: zaključan/otključan itd..
devs: mov byte, inL
call sendbyte ;---- salje podatke iz gornjeg potprograma u sustav
mov byte, mfd
call sendbyte
mov byte, inH
call sendbyte
mov byte, devd
call sendbyte
sbrc cfm, 6 ;-- 1== nema CFI
jmp cfimo
ldi XL, 0x20
;
;
;ima još no nije nam važno za primjer
cfimX: jmp MAIN ;---pošto smo "skočili" u taj potprogram moramo se na isti način vratiti nazad, u ovom primjeru u glavni program!


Nadam se da se iz cijelog ovog primjera vidi koliko potprogram može biti složen i koliko ima poziva koji su vezani na uvjetno grananje a čine programsku petlju. Bezuvjetnom grananju najčešće, u potprogramima, prethodi uvjetna pa se i ona može smatrati petljom.

Funkcionalno gledano računalo stalno ispituje uvjet grananja, o određenoj točki, i vodi program u ciljanom smijeru no uvijek to mora završiti u glavnom programu. Tako je kod svih programskih jezika i svih razina petlji. Razumnjivo jer oni, njihovi prevodioci, ionako to lijepo spakiraju u objektni kod.
Operacije grananja kao: breq, brcc, sbrc, sbis itd... su bit svakog programa (naravno AVR/ATmega).

Toliko o granaju, petljama i slično.

Vjerojatno ćemo se još vraćati na njih kroz priču o asembleru kao što se često vraćamo pojamu vremena i kašnjenja u samom izvršenju realnih operacija nad sklopovima u okruženju našeg MCU-a.
Nikad nemojte zaboraviti! MCU je,najčešće, samo dio cjeline u stvarnoj primjeni. Zato ga i nazivaju tako, mikrokontroler.
Rijetki su primjeri, van igranja i zezanja, da je on jedinica sama za sebe. Ima, primjer su kartice za bankarsko poslovanje, pay-tv i sl. no njihovoj su sabirnici tad dodani tkzv. kriptoprosesori na uštrb nekih drugih HW-skih implemenata. Tako da oni nisu predmet ove priče.

E sad dolazimo i do pitanja emulacije HW, tj. implementacije sklopovskih riješenja pomoću programa. Veoma zanimljivo i zahtjevno područje za programere. Ne samo da štedi novac i pojednostavljuje izradu uređaja već je to, na neki način, poligon za testiranje vas i vašeg razumijevanja procesa, kako onih u HW-ru uređaja tako i onih u MCU-u kao i vještinama programiranja. Sposobnosti da se spoji, koji put, naizgled nespojivo. Asembler je daleko najbolji alat za ostvarenje jednog takvog cilja.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

Prije nego krenemo dalje možda nebi bilo loše pojasniti upotrebu radnih registara kao i ram-a u programu. To jedna od važnijih zadaća u assembleru. Kao što ste vidjeli u primjerima od ranije radne registre možemo predefinirati naredbom .def i na taj način im dodijeliti točno određenu funkciju kao globalnu u čitavom programu.
Kod HPL-a to baš i neide tako jer compiler ima neka svoja ograničenja iz veoma razumljivih razloga. Kao što varijable možete vezati na određeni registar iz skupa registara opće namjene to isto možete učiniti s ram-om naredbom .equ. Naravno i jedna i druga naredba (def i equ) nisu naredbe iz skupa naredbi računala već assemblera samog.

Ako pažljivo i dobro odradite taj posao vaš će program biti jednostavniji i razumljiviji kako vama tako i samom stroju jer će generirani objektni kod biti manji i jednostavniji. Usput će biti i brži za izvođenje jer neće za sobrom vući push i pop naredbe kojima compileri HPL-a pred svaku iole složeniju rutinu pohranjuju trenutne vrijednosti skupa registara (onih koje će program prevodioc koristiti za tu rutinu) u stog te ih po završetku ponovo vraća u njih. Čini to u za gotovo svaku rutinu.

U assembleru, pod uvjetom da ste pažljivo odradili predefinicije, tako nešto je potrebno zaista rijetko i to kod veoma kompleksih programa ali i tad jakograničenom broju rutina i s malim brojem registra. Kao što je puno ranije rečeno programer u assembleru se najčešće koristi upotrebom tkzv. "tmp" registara. To su registri kojima niste dodijelili ništa pa se koriste u svim rutinama za prijenos podataka iz rama, eeproma, portova, implemenata u sam ALU tj, oni djeluju kao neka vrsta accumulatora.

Obično nije potrebo više od 2 - 4 takva registra. Slično je s upotrebom rama-a. U njemu možete definirati gdje (na kojoj adresi) i koliko podataka (dubinu adresnog polja) ćete dodijeliti određenoj varijabli ili grupi podataka.

Primjeri:

--- REGISTRI ---
.def COUNT=r2
.def TIMER=r22 :

-- RAM ---
.equ SPLOW=0x015E ; TMP SPI LOW adr Čuvaju se adrese serijske memorije
.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

Naizgled ram može da služi kao pohrana raznim varijablama i to stoji no ako ste primjetili tu se uglavnom čuvaju vrijednosti koje ne sudjeluju da tako kažem u samom izvršenju programa tj one moraju (varijable iz ram-a) biti prenesene u registre jer samo oni, registri opće namjene su vezani na ALU (jezgru). To se čini naredbama lds i ld (load stog).

ld r17, X+
lds r17, 0x015E
Obje gornje naredbe čine isto, pune r17 (jedan od registara opće namjene) vrijednošću iz zadane adrese ram-a. Gornji to čini upotrebom indeksnog registrarskog para XH:XL a donji neposredo sa zadane adrese (0x015E). Čitanje grupe podataka iz rama kao i upis se najčešće se obavlja upotrebom indeknih registara dok se pojedinačno čitanje ili pohranjivanje varijable u ram čini neposredno, definiranjem addrese.

Mislim da je više nego vidljiv razlog zašto je najbolje koristiti registre za pohrane pojedinačnih varijabli od rama.Srećom imamo 32 registra pa se uvjek da složiti kombinacija koja će naš program učiniti jednostavnijim i u konačnici bržim.

Push i Pop naredbe nisu nepotrebne i nisu "naporene" kako bi se činilo nakon disasembliranja objektnog koda kojeg je generirao bilo koji HPL. Potrebne su, kako je već i rečeno, kod pisanja zaista složenih struktura programa i potprograma kad je čak i tako veliki broj radnih registra nedovoljan da svim varijablma dodjelimo njihov registar. Da ne zaboravim, stog operacije rade tako što prvi registar koji upišete u stog se posljedni iščitava. Njegova pohranjena vrijednost, naravno.



Znam, vjerojatno će se sad neki malo i isčuđavat misleći pri tome na ram. U pravu su na neki način, veličina rama je zaista dovoljna za mnoge stvari ali... Sve to vrijedi za operacije koje nisu u stisci s vremenom izvršenja i ne moraju se ordediti u relnom vremenu jer njega ipak nemožemo ignorirati. Takvi problemi se najćešće javljaju kod velike brzine razmjene informacija, emulaciji nekih vrsta HW-sa kao i kod složenih kalkulacija čiji se rezultat mora dobiti u točno određenom vremenu da bi nastavak programa mogao teći nesmetano i da bi kranji ishod imao ikakvog smisla.

Sad bi se mogli javiti oni ljudi koji će reći pa mogu kupiti "jači" MCU i sl. Uvijek možete kupiti nešto bolje ali zapamtite snaga računala nije samo u njegovom HW-ru već softveru zapravo svemu onome što će objektni kod učiniti jednosavnijim a time i bržim za izvođenje no ne samo to već s manje šanse da dođe do greške.

Pošto govorimo o programiranju u assembleru, koji ima neka svoja pravila, definicije registra ( dodjela varijabli) kao i polja rama su neizostavni dio uvoda u pisanje programa. Strkukturu kao i objektni kod to čini jednistvenim u odnosu na iste ili slične programe/potprograme/rutine u HPL-ima.
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
mojalovaa
Napredujem
Napredujem
Posts: 135
Joined: 29-03-2013, 19:28

Re: Programiranje u Assembleru

Post by mojalovaa »

Dali je velika razlika izmedju BASCOM i Assemblera , posto ja sada ucim tek bascom a vidim da ovdje postoji puno razlicitih sistema pisanja programa za mikrokontrolere.
Inace moja tema je "Bascom za totalne neznalice" , pa ako vas zanima mozete procitati a usput ako znate pisati u BASCOM-u a vjerovatno znate , pozivam vas da kada se tema malo zakomplicira da mi pomognete ako vam nije problem.
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

Razlika BASCOM assembler?

Nemala, kao i za ostale HPL-e.
Iako i assembler ima svoju sintaksu i svoja pravila kao i svi prg. jezici. Kod njega je to veznao u najdirektnijem obliku na strojne naredbe same mašine.
Mislim da je to u toj, iako nedovršenoj, priči o njemu i objašnjeno.

Ako nađem vremena, jer jako rijetko dođem na forum, pomoći ću ti ako budem mogao.

Pozdrav
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
mojalovaa
Napredujem
Napredujem
Posts: 135
Joined: 29-03-2013, 19:28

Re: Programiranje u Assembleru

Post by mojalovaa »

Slabo i ja sada stignem ali zanima me u praksi dali je jednostavniji od recimo bascoma za atmele , odnosno dali je primjenjiv na atmelima.
buco-1
Napredujem
Napredujem
Posts: 105
Joined: 24-10-2010, 21:22

Re: Programiranje u Assembleru

Post by buco-1 »

mojalovaa wrote:Slabo i ja sada stignem ali zanima me u praksi dali je jednostavniji od recimo bascoma za atmele , odnosno dali je primjenjiv na atmelima.
Znam da ovaj odgovor nema nekog praktičnog smisla ali kad sam već tu:

Jednostavniji je, kao i svi assembleri vezani na RISC mašine Harwardskog tipa, pogotovo ovi novi macro assembleri koji dopuštaju upotrebu predefinicija iz C++ kao i od samog macro assemblera.
Imaju i opcije uključivanja C++ progrrama u sam ne asseblirani kod, no to je za sad relativno slabo razvijeno, bar u Atmel studiju 6.0. Nezgrapno je.

Moderni macro assembler dozvoljava potpunu modularnost programa a uz pažljivo napisane predefinicije i njihovu jednostavnu upotrebu u kasnijim projektima. U principu se kod tih novih alata program postavlja i definira kao u C++ i sličnim C-like alatima. Osim što za razliku od svih njih omogućava potpunu kontrolu na sustavom, do najsitnijih detalja.

// Modul primjer //
#ifdef xxla
#define ulaz_a PORTA
#define pin_a 5
.
#ifndef xxer
#define xxer 1
#endif
#include "SPIflash_drv.inc"
#endif
Svakom programeru u HPL-u je poznat gornji primjer ( ne doslovno). To je primjer predefinicije koja se koristi u C++ ali i u Atmelovom macro assembleru, kao i u alatima koji se plaćaju.

Ali (to ali muči generacije programera) assembler koliko god moderniziran bio jednostavan je pod pretpostavkom da imaš određena predznanja: poznaješ mašinu, poznaš njenu tehničku dokumentaciju i detalje načina rada računala na razini HW-a. Kod MCU-a trebaš još i dodatnih "znanja", npr. HW -er na internoj sabirnici, konfiguracioni registri itd...

Preporučam ga onima koje istiski zanima računalo, ne samo programiranje, a poznaju puno više od osnova njegova funkcioniranja (tako i tehnologiju u kojem je rađen) i spremni su potrošiti vrijeme na edukaciju. Nagrada je potpuna kontrola nad sustavom, svim njegovim resursima i naravno veličina i efikasnost koda a time i brzina izvođenja programa. Mogli bi napisati roman a da nije sve rečeno.

Pozdrav
Today's scientists have substituted mathematics for experiments, and they wander off through equation after equation, and eventually build a structure which has no relation to reality.
- Nikola Tesla
Post Reply