"U potrazi za tartufima AVR-a" - Juriš na assembler, bitka kod HEX-a
Ovaj naslov je i tako ispred svog vremena i davno sam odlučio da će takav biti. Prije ovoga mislio sam napisati post o "upucavanju" Hex file-a u ATMEGA88, no to ću napraviti kasnije.
Razlog tome je naravno bucin topic o assembleru i drago mi je da se odlučio pisati takvo nešto.
Cilj posta je svakako prezentacija odnosa snaga assemblera i C-a. Program palenja LED-a u C-u sam već napisao, no sa strane assemblera ovo izgleda potpuno drugačije. Bitka kod hex-a svodi se na najnižu razinu na koju se u programiranju možemo spustiti... "Dno-Dna" -> Sve ispod ovoga bi bilo dizajniranje vlastitiog računala...
Što je HEX file i kako izgleda?
Samo ime HEX govori da je datoteka zapisana u Hexadecimalnom brojevnom sustavu:
(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F)Evo jedan red HEX file-a koji ću rastaviti i objasniti značenje HEX file-a:
(Ovo je prvi red iz HEX file-a za palenje LED-a i programa kojeg sam napisao u C-u)
:1000000019C020C01FC01EC01DC01CC01BC01AC00COvako je to zapisano u HEX datoteci, no ubaciti ću nekoliko razmaka u taj red jer moram red drugačije formirati da bi razlikovali stvari koje se nalaze u jednom redu...
: 10 0000 00 19C0 20C0 1FC0 1EC0 1DC0 1CC0 1BC0 1AC0 0CHex rastavljen prema redosljedu logičkih cjelina:: - označava početak reda u HEX datoteci. (Početak HEX zapisa)
10 - označava broj data byteova u tom redu. Ne radi se o 10 byteova nego o 16. Hex je pisan
u Hexadecimalnom sustavu, pa probajte kalkulatorom prebaciti HEX 10 u DEC sustav i to je 16
Tih 16 znači da u jednom redu postoji 16 "data" byteova.
0000 - Adresa na koju se upisuje data zapis HEX filea. Kao što vidite prvi red u HEX datoteci za
palenje LED-a ima adresu 0000 jer to je prva naredba koju će izvršiti AVR jezgra.
00 - ovo označava tip zapisa hex filea. 00 označava "DATA" zapis. U INTEL HEX fileu postoji 6
različitih tipova zapisa, no kad piše "00" sigurno se radi u DATA zapisu koji u sebi sadrži
16 bitnu adresu.
Nakon descriptora jednog reda idu instrukcije:
19C0 20C0 1FC0 1EC0 1DC0 1CC0 1BC0 1AC0.
Veličina svake instrukcije AVR jezgre je 2 byte-a. Postoje assemblerske instrukcije koje su 4 byte-a jer u 2 byte-a jednostavno je nemoguće napraviti sve instrukcije AVR-a. No to nam nije toliko bitno jer kada bi analizirali svaku naredbu skužili bi koja je 4 byte-a a koja 2 byte-a. Ovaj dio HEX filea sam izvukao iz palenja LED-a, pa također mogu zaključiti kako će AVR jezgra za prvu instrukciju sigurno izvršiti
19C0(HEX).
Bez obzira što ovo nitko nikad ne mora koristiti bitno je zapamtiti da svaka assemblerska instrukcija za AVR jezgru može biti 2 ili 4 byte-a.
Ako znamo koliko je velika jedna instrukcija, jednostavno je izračunati koliko instrukcija assemblera može stati u ATMEGA88.
Ako imao 8192 Bytea FLASH-a, onda sigurno u to može stati duplo manje assemblerskih instrukcija jer svaka instrukcija treba minimalno 2 byte-a.
8192/2 = 4096 -> ATMEGA88 može pohraniti u FLASH 4096 assemblerskih instrukcija
Ovo su instrukcije koje se nalaze u jednom redu HEX file-a.
Prvi byte u HEX fileu označava broj data byte-ova ->
10(HEX), 16(DEC).Dakle idam zbrojiti koliko ima tih byteova assemblerskih instrukcija...
19C0 - 2 Bytea
20C0 - 2 Bytea
1FC0 - 2 Bytea
1EC0 - 2 Bytea
1DC0 - 2 Bytea
1CC0 - 2 Bytea
1BC0 - 2 Bytea
1AC0 - 2 Bytea________________
Total = 16 bytes(DEC) (10HEX)
Moramo izvuči i zaključak ovoga -> U jednom redu HEX filea postoji max 8 assemblerskih naredbi, ili 16 data byteova za AVR jezgru.
Osatao je i famozni kraj HEX filea koji uvijek završava sa jednim byteom. U ovom slučaju piše ->
"0C"Kontrolni byte jednog reda reda HEX file-a zove se "checksum"
Dakle taj byte potvrđuje vjerodostojnost svih byteova u tom HEX redu i računa se na sljedeći način:
Ponovno mi treba čitav red HEX-a ->
:1000000019C020C01FC01EC01DC01CC01BC01AC00CZadnji byte ću obrisati kako bi ga mogao ponovno izračunati.
:1000000019C020C01FC01EC01DC01CC01BC01AC0Prvo što radim je to da razdvojim to na byteove ->
10 00 00 00 19 C0 20 C0 1F C0 1E C0 1D C0 1C C0 1B C0 1A C0Pokrenem windows calculator, prebacim ga u HEX i računam ovo:
10 +
00 +
00 +
00 +
19 +
CO.... -> Zbrojim sve byteove u Hex redu. Osim zadnjeg kojeg smo obrisali jer njega računamo.
Rezultat koji dobivan je
06F4. Broj koji sam dobio veći je od jednog byte-a pa uvijek uzimam niži byte ->
"F4". U calculatoru računam
"F4 XOR FF" i za relultat dobivam
"0B".
Dodam ovom
"0B" rezultatu
"01" i dobivam
"OC". Dakle,
OC mora biti na kraju ovog HEX reda. Evo univerzalna formula:
Checksum HEX reda računam tako da od zbroja svih byteova uzimam onaj niži (manje važeči), te računam XOR sa 0xFF i uvećavam rezultat za 1. Bitno je primjetiti da sam izbrisao checksum prilikom njegovog računanja i taj zadnji byte ne ulazi u formulu za proračun checksuma.
Dakle ovaj zadnji byte je izračunat prema svim ostalim byteovima u istom redu HEX filea samo kako bi mogao dokazati vjerodostojnost podataka u istom redu. Ako promjenimo samo jedan BIT u čitavom HEX redu checksum neće odgovarati rezultatu checksuma i samim time naš HEX file nije dobro zapisan.
Koja je u tome najnižem nivou moć assemblera? Ako možemo sami pisati HEX te izračunati sve što je potrebno za FLASH MCU-a što mi onda radimo? Programiramo ili pišemo brojeve?
Hex file je u principu strojni assembler, ovi brojevi su assemblerske naredbe koje su nazvane imenima čisto da bi komande imale smisla. U notepadu to izgleda kao broj, no radi se o čistoj, od majke rođenoj assemblerskoj instrukciji.
Tu nastaje ta prva rupa između C-a i assemblera. U c-u mi ne možemo utjecati na HEX file jer ga generira compiler, i on odlučuje kako će napraviti HEX file, dok u assembleru nas zapravo boli "k" za sve, jer mi smo ti koji odlučujemo o svakoj instrukciji. Mi smo oni koji slažu instukciju za instrukcijom i nitko drugi osim nas ne može utjecati na redosljed instrukcija AVR jezgre.
Žalosna istina na kraju ovog dijela napada na HEX je sljedeća:
Da bi mi upalili LED potebne su 3 assemblerske instrukcije (@buco vjerojatno zna i kraći način, no trenutno mi se ne kopa po assemblerskim instrukcijama da bih ovo napravio kraće)
Možda se pitate zašto HEX file koji je napravio compiler WINAVR iz C-a izgleda ovako:
- Code: Select all
:1000000019C020C01FC01EC01DC01CC01BC01AC00C
:1000100019C018C017C016C015C014C013C012C034
:1000200011C010C00FC00EC00DC00CC00BC00AC064
:1000300009C008C011241FBECFEFD4E0DEBFCDBF82
:1000400002D004C0DDCF81E087B9FFCFF894FFCFA5
:00000001FF
dok Hex file koji sam kasnije napisao u assembleru izgleda ovako:
- Code: Select all
:0600000081E087B9FFCF8B
:00000001FF
Vidite da je zadnji red HEX file-a identičan, i ako njega rastavimo po pravilima HEX file-a dobijemo ovo
00 0000 01 FF. 00 -> Broj byte-a je 0
0000 -> Adresa zapisa je 0
01 -> Tip zapisa više nije 00 nego je 01. Ovo u HEX-u znači kraj file-a.
FF -> Onaj dosadni checksum -> Zbroj byteova je 01 -> 01 XOR FF = FE, FE + 1 = FF -> Dakle checksum mora biti FF
U svakoj HEX datoteci može postojati samo jedan kraj file-a i uvijek se označava sa tipom zapisa
"01". Orginalna ideja (iz 1970) tog kraja je da u njemu postoji i adresa prve instrukcije koju izvršava jezgra, pa je u ovom našem HEX fileu dobro upisana adresa na 0000. No ovo u stvarnosti baš ne drži vodu jer ne bira HEX file odakle će jezgra startati, nego to bira FUSE AVR MCU-a o kojemu ćemo naravno kasnije.
Ovaj tekst oko HEX file-a nije potrebno znati, ali onaj tko hoće može si bez problema otvoriti HEX file i analizirati sve instrukcije AVR jezgre.
Zašto ja pišem ove gluposti o HEX-u? Postoji samo jedna razlika između pravih programera, i dakako onih koji to nisu.
Što napraviti kad nastane problem??? Bascom, C i svi jezici koji nisu assembler u sebi imaju nešto što možemo nazvati RANDOM BUG, pa tu i tamo znaju popizditi i napraviti glupost. Te gluposti mogu biti ili neznanje programera da kaže compileru što želi, ili compiler jednostavno ni sam ne zna kako da napravi to što programer želi, pa napravi sranje od HEX file-a.
Snaga programera je samo u daljini koliko seže njegovo znanje te oružja s kojima raspolaže.
Ako ja napadam "problem", koji ne želi odustati, sigurno da ću otići u najgoru moguću krajnost assembera i HEX-a, dok programer C-a ili BASCOM-a mora ostati na razini višeg jezika i problem ne može riješiti narednih 200 godina.
Povući ću jednu zanimljivost o HEX file-u. Dok ne znate što je to svi mislite da se upisuje u FLASH MCU-a, no ti nije istina.
Kada pogledamo što sve postoji u HEX redu vidimo da postoje gluposti kao adresa, broj byte-ova u jednom redu, tip zapisa i checksum reda.
Koji bi to konj napravio MCU i upisao u FLASH čitav hex? Ono što se stvarno upisuje u FLASH MCU-a su samo instrukcije, a sve ostalo što se nalazi u samom HEX-u potrebno je da bi se znalo kamo što treba upisati.
Dakle u FLASH MCU-a ne ide HEX, nego samo instrukcije za jezgru. HEX je takav kakav je samo da bi protokol upisivanja FLASH-a znao kamo što upisati, pa mogu zaključiti kako za prvi red HEX-a u MCU FLASHA biti će upisane samo instrukcije:
19C0 20C0 1FC0 1EC0 1DC0 1CC0 1BC0 1AC0Istinski FLASH u AVR-u nije HEX, nego samo instrukcije poslagane upravo onako kao HEX file opisuje.
To Be Continued...