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