Pravo je mi je zadovoljstvo kada na forumu mogu da razmenim misljenje sa kompetentnim ljudima o nekoj temi, kao
sada u poslednjih par nedelja. Mislim da se u sustini svi slazemo, ali se izrazavamo na razlicite nacine.
Nedostaje nam konkretnost i cinjenice sto bi posluzilo i drugima koji prate ovu temu.
Odin D. blizu si kada si procenio da radim sam. Da, moja firmica ima samo jednog zaposljenog, to sam ja. Ali sa
jos petoricom kolega saradjujem jer imaju firme registrovane za pruzanje usluga, tako da ih ponekad angazujem za
usluge u razvoju i projektovanju i drugim poslovima. Radim po tenderima, i imam periodican posao, pa iz tog
razloga ne mogu da imam stalno zaposlene.
Ako bi sazeli nase diskusije, onda bi mogli da ih svedemo na nekoliko tacaka:
1. Asembler je najmocniji i najbolji programski jezik za razvoj softvera MCU-a i sa njime se mogu 100% iskoristiti
interni resursi MCU-a.
2. Priblizno isto vreme (algoritam, pisanje izvornog teksta i testiranje) se trosi po jednoj liniji ispisanog
izvornog koda. To znaci da ce se manje vremena utrositi na pisanje softvera na visem programskom jeziku nego na
asembleru.
3. Program napisan na visem programskom jeziku, moze se kompajlirati za razlicite MCU-ove sto dozvoljava da se u
jednom uredjaju lako jedan MCU moze zameniti drugim. Naravno, uz neznatna prilagodjavanja.
4. Nijedan visi programski jezik ne moze da bude efikasan kao asembler. Zato treba ukalkulisati gubitak u brzini i
visak generisanog koda prilikom izbora MCU-a.
5. Programski jezik C je toliko dominantan da je jedini programski jezik viseg nivoa koji se koristi u
profesionalnom programiranju MCU-a.
6. C je nastao kao pomocni jezik za razvoj UNIX-a u AT&T Belovim laboratorijama (Denis Rici 1972). U prvoj verziji
nije imao ni tipove, ali je po zavrsetku razvoja UNIX-a preradjen i postao sastavni deo isporuke UNIX-a. Ustvari,
C je programski jezik srednjeg nivoa, i zato je besmisleno uporedjivati ga sa jezicima viseg nivoa kao sto su
PASCAL i drugi.
7. C je daleko od savrsenog programskog jezika. Oslanja se na linkere razvijene kasnih 50-tih godina proslog veka,
i zbog kompatibilnosti se, u tom smislu, do danas nije nista promenilo. Linkeri su tada bili neophodni jer u
memoriji racunara nije mogao da stane ceo izvorni tekst, pa se sa diska ucitavao deo po deo, i prevodio u objektni
kod, a na kraju je linker povezivao sve objektne fajlove koji su sadrzali objektne kodove. Danas za to ne postoji
potreba. Memorije su velike da mogu da prime ceo tekst programa i da ga prevedu u jednom ili vise prolaza. Greske
koje detektuje linker se ne mogu pozicionirati u izvornom kodu, i samo iskusnom programeru one ne prave problem. C
ne zadovoljava ni jedan od tri kriterijuma koja je postavio 1977 Barron za dobar programski jezik. Dakle, C je
postao to sto jeste diktatom logike profita i uzasavam se pomisli da ce dominirati i u narednim decenijama (valjda
nece). Logika profita koci razvoj softvera, a podstice razvoj hardvera, svedoci smo toga.
8. Upoznao sam nekoliko firmi na zapadu u kojima se ozbiljno i profesionalno programira na C-u. Sve one imaju
grupu za dibagiranje. Na trziste se izbacuje nedovoljno testiran softver a posle toga popravlja i kroz lansiranje
raznih verzija uzimaju pare od korisnika. Po teoriji dokazivanja ispravnosti programa (to nisam nikada radio),
program pisan na C-u (ali isto i u asembleru) nije moguce podvrgnuti dokazivanju ispravnosti. Praksa pokazuje da
broj gresaka u programu pisanom na C-u, podeljeno sa brojem linija koda, skoro isti kao kod asemblera.
Zaklucak: koristite C jer nemate drugi izbor, a asemblerom se samo pomazite tamo gde je kriticna brzina. Postoji
referentna definicija C-a kao standard, sto garantuje njegovu prenosivost sa jenog MCU-a na drugi (uz neznatna
podesavanja nestandardnih prosirenja koja moraju da postoje za svaki MCU). Vecina profesionalnih razvojnih sistema
obezbedjuje automatizam kod ovih podesavanja, pa tako mozete da ih izbegnete samim izborom MCU-a.
Znajuci sve ovo, pre 10-tak godina lutao sam izmedju asemblera i C-a. Asembleri koji su bili dostupni od raznih
proizvodjaca pisani su tako da deluju traumaticno na programera. Koncepcijski svi onu su bili isti, razlikovali su
se samo u asemblerskim iskazima. Asemblerske direktive su uglavnom bile iste i prilicno nelogicne. Sa asemblerom
svi znate kakva je muka izmisljati nazive labelama, voditi racuna o varijablama u memoriji, upravljati sa stekom i
t.d. Zato sam seo i za oko 6 meseci napisao sopstveni kompajler za asembler. U jednoj recenici: to je kao PASCAL u
kojem su PASCAL iskazi zamenjeni asemblerskim iskazima.
Dalji tekst nije za uzrast ispod 16 god. Salim se, ako nekog ne interesuje kako treba da izgleda dobar asembler,
ili smatra da je asembler mrtav, onda ne treba da cita ono sto sam u nastavku napisao.
Da ilustrujem primerima:
Code:
const
MaxNiz = 55;
Ime = 'Pera i Laza';
type
tVreme = record
sat : 0..23;
minut,sekunda : 0..59; //podintervalni tip
end;
tNiz2Na : array[0..7] of byte;
const
Niz2Na : tNiz2Na = (1,2,4,8,16,32,64,128); //ovaj niz je tipska konstanta i smesta se u flash
var
Var1 : byte;
Var2 : longint;
PortD : set of (bit0,bit1,bit2,bit3,bit4,bit5,bit6,bit7) absolute 8; //deklarisanje skupa bitova
Var3 : record
Vreme : tVreme
Dogadjaj : (Ukljucen,iskljucen,stao); //nabrojiv tip
end;
NizA,NizB : array[0..MaxNiz] of tVreme;
Ime1,Ime2 : string[12];
Var4 : word;
Var5 : byte;
Umesto bit0..bit7 zgodno je upisati stvarna imena signala na portu D. Varijable se automatski rasporedjuju u
RAM-u, osim PortD koji sa absolute biva pozicioniran na adresi 8.
A evo kako se koristi:
Code:
ldaa [Var3.Vreme.minut];
ldaa [Var2.3]; //najnizi bajt od Var2
ldaa [Ime1[4]]; //cita 4-ti karakter
ldaa [NizA[9]]; //cita 10-ti element niza
bset bit1,[PortD]; //setuje bit porta D
if [PortD](bit6) =0 then inc [Var1]
else dec [Var1];
ldaa [PortD];
anda |bit0..bit3,bit5|; //vertikalne crte ogradjuju skupovnu vrednost
oraa |bit4|;
staa [PortD];
ldx [Var1];
clrh;
ldaa x[Niz2Na]; //ako je vrednost Var1 < 8, u akumulatoru je 2^Var1
ldaa [Var3.Vreme.sat];
case AccA of
9..16 : PrvaSmena;
17..23,0 : DrugaSmena;
1..8 : TrecaSmena;
end;
Poziv procedura (PrvaSmena i t. d.) se vrsi samo navodjenjem imena, a kompajler odlucuje da li je dugacak ili
kratak poziv sa jsr ili bsr.
Code:
while [PortD](bit3) <>0 do //testiranje bita u portu D
begin
inc [Var1];
end;
ldhx SizeOf(NizA); //kopiranje niza
repeat
ldaa x[NizA-1];
staa x[NizB-1];
until decr(IndX) =0; //dekrementira X registar i skace na pocetak petlje ako je on <>0
Var2 := MulLong(Var2,Var2); //u modulu MAT sam napisao procedure i funkcija za
//aritmetiku. Ovim se dobija kvadrat od Var2
Deo funkcije MulLong je:
Code:
function MulLong(a,b:longint):longint;
var dynamic
z : longint;
begin
ldaa s[a]; //a,b i z su lokalne varijable na steku pa se njima pristupa sa s[]
//kada se izracuna rezultat, a on je u z, sledi:
ldhx s[z.2];
sthx s[result.2];
ldhx s[z.0];
sthx s[result.0]; //result je varijabla koja ostaje na steku po izlasku iz funkcije
end; //skida se sa steka i smesta u Var2
Postoje i makroi:
Code:
macro IncBajtIliWord(a);
begin
_if not exist(a) then _ErrorMSG('Ne postoji parametar');
_if not VarOf(a) then _ErrorMSG('parametar nije varijabla');
_if SizeOf(a) > 2 then _ErrorMSG('parametar je predugacak');
_if SizeOf(a) = 1 then
begin
_if DynamicOf(a) then inc s[a]
else inc [a];
end
else
begin
_if DynamicOf(a) then
begin
inc s[a.1];
if =0 then inc s[a.0];
end
else
begin
inc [a.1];
if =0 then inc [a.0];
end;
end;
end;
_if je kompajlerski uslov za prevodjenje jednog ili drugog dela programa.
_ErrorMSG prekida kompajliranje i ispisuje gresku kao da ju je ispisao kompajler.
Labela u makrou ne pravi problem kao u C-u. Ko je koristio C makroe sa labelom zna o cemu pisem.
Makro moze i da vrati vrednost:
Code:
makro Mul(a,b);
begin
_if not Exist(result) then _ErrorMSG('Ne postoji leva vrednost');
ldaa [a];
ldx [b];
mul;
staa [result.1];
stx [result.0];
end;
Kad se ukljuci makro, formalni parametri (a i b i eventualni result) se zamenjuju stvarnim.
Tako:
Code:
Var4 = Mul(Var1,Var5);
result postaje Var4, a Var1, a b Var5. Kod poziva procedure i funkcija, vrsi se prenos vrednosti stvarnih
parametara vrednostima formalnih. Kod ukljucivanja makroa, stvarni parametri (kao objekti) zamenjuju formalne
parametre makroa, sto otvara siroku fleksibilnost pisanja softvera. Cak se mogu ostvariti i neke skromne osobine
objektno orijentisanih jezika.
Makro se moze ukljuciti i sa manje parametara, ali se sa exist ispituje koji postoji. Makro moze da ukljuci drugi
makro, a moze i da pozove bilo koju proceduru ili funkciju.
Vec imam modul sa velikim brojem makroa, i oni dominiraju u mojim programima. Kao da imam visi programski jezik
koji umesto operatora poziva funkcije za svaku operaciju. Naravno, tu su i moduli za komunikaciju, matematiku
(integer i float), rad sa stringovima i t. d.
Sta sam dobio ovim? Evo analiza na bazi modula (include fajla u C-u) napisanog za DES, TDES, MAC (sa DES i TDES)
koji su napisani za C i za ovakav asembler. Pisanje na C-u i testiranje je potrajalo nekoliko dana, a onda sam
odstampao C tekst i po njemu za manje od sata napisao asemblerski tekst. Ako ste obratili paznju, na ovakvom
asembleru to nije tesko, i zato je bilo tako brzo. Asemblerski program je odmah proradio ispravno (vec je bio
ispravan u C-u). Na C-u je fajl imao 626 linija izvornog koda, generisao je kod duzine 3986 bajtova (zajedno sa
tabelama), proizveo 71 bajt varijabli u RAM-u i izvrsavao je MAC izracunavanje za 77.6ms. Asemblerski modul je
imao 1490 linija izvornog koda, generisao je 3436 bajtova koda, potrosio 66 bajta RAM-a i izracunao MAC sumu za
35.7ms.
Vidi se da jedna linija C-a prosecno generise 6.4 bajta koda i izvrsava se za 124us. Asembler je u proseku
generisao 2.3 bajta koda po liniji sa prosecnim trajanjem od 24us. Podatak o vremenu izvrsenja je validan samo za
ovaj primer.
Moze se zakljuciti da sa mojim asemblerom pisem 2.3 puta duzi kod (i otprilike mi treba za to toliko vise remena),
ali kad se radi o algoritmu, strukturi programa i nizu drugih stvari, imajuci u vidu da moj asembler poseduje sve
strukture kao i C, a takodje i sve strukture podataka, za taj deo posla trosim priblizno isto vreme. Ako imate u
vidu da samo fizicko pisanje programa nema veliki udeo u razvoju softvera, onda efikasnost pisanja softvera za MCU
sa mojim asemblerom se priblizava efikasnosti programiranja na C-u. Sa druge strane imam dobitak u duzini koda i
brzini izvrsenja koda.
Naravno, C je nezamnljiv u slucaju kada bih sa MC908 presao na neki drugi MCU. Za taj drugi bi bez problema vazio
vec napisan fajl, dok bi na asembleru on morao da se ponovo pise. Dakle, kako za sada u svojim projektima koristim
uvek MCU iz iste familije, ja taj problem nemam.
Izvijavam se na preopsirnosti. Mozda nekoga ovo ne interesuje, ali sa ovakvim asemblerom priblizio sam se u
efikasnosti pisanja softvera u C-u. Nazalost ima jednu veliku manu, nije prenosiv na druge MCU-ove. Kompajler je
pisan u DELPHI-u i generisanje koda se vrsi u izdvojenom modulu, tako da zamenom tog modula mogu da imam ovakav
asembler i za druge MCU-ove. Ali ko ce to da radi? Uradio sam pre koju godinu ovakav asembler za AVR, ali nije
dovoljno testiran jer nisam nikad koristio taj MCU.
Naravno, nisam napisao samo samo asembler, on je integrisan sa editorom, simulatorom loaderom programa na MCU i
dibagerom.
Jos jednom izvinite na preopsirnosti, ali sam hteo da pokazem da i asembler moze da bude humaniji i efikasniji.
Srdacan pozdrav svima koji sa paznjom prate ovu temu.