Article View: be.comp.programming
Article #14335C met assembly access
From: GE
Date: Fri, 23 Feb 2001 17:32
Date: Fri, 23 Feb 2001 17:32
154 lines
9387 bytes
9387 bytes
Ik zit met volgende 2 problemen, waarvoor ik nergens een oplossing
vind:
1. Offset van leden in een union/structure bepalen in assembly.
Als je het absolute address van een structure hebt, kan je dan - op
een universele manier - een parameter laten genereren door de compiler
die de offset van het lid meegeeft. In dat geval moet je zelf geen
2de lijst in assembly maken, en blijft met 1 lijst alles up-to-date.
Een voorbeeld
typedef union
{
INT16U word;
struct
{
INT16U ClkCnt :8;
INT16U Res0 :1;
INT16U Res1 :1;
INT16U Res2 :1;
INT16U Res3 :1;
INT16U Res4 :1;
INT16U MisPCB :1;
INT16U IPort :1;
INT16U Error :1;
}bit;
}RETPARAM;
In dit geval moet bijvoorbeeld de locatie van de beide 8-bit
parameters van de structure (de 8-bits mogen nog samen genomen worden
tot 1 byte offset) aangegeven worden door de compiler.
2. Return size.
Ik zit met een 16-bit embedded processor (geen x86 of 68000 adept).
Handig aan dat beest is het feit dat er voor elke call / jump twee
opcode-varianten zijn. Er is dus een call met 16-bit return address
(bankswitched addressing), terwijl een andere opcode voorzien is voor
een call met een absoluut 32-bit return address.
Naar analogie hiermee zijn er dus ook 2 verschillende return from
subroutine instructies: RET voor 16-bit return parameter, RETP voor
een 32-bit return.
Daar komen echter enige beperkingen bij: aangezien het onbegonnen werk
is om een parameter mee te geven die de RET of RETP instructie kiest
om terug te keren (kost me te een register, of ik moet de parameter op
de stack gaan zoeken), wordt volgende systeem gehanteerd:
- indien alle callers binnen hetzelfde 64K page geloceerd zijn, wordt
RET gebruikt: een short-call.
- analoog hieraan: indien er een (of meer) caller is die buiten de
64Kbyte page gelegen is, moet iedere caller (ook die van dezelfde
page) een long-call uitvoeren en wordt RETP gebruikt. Dit is dus
trager en kost meer geheugen.
Een aantal functies zijn compiler-gegenereerd om van te huilen:
bijvoorbeel de flash-update kost in C net geen 500 bytes constant
gealloceerde RAM. In directe assembly maximaal 50 bytes dynamisch
gealloceerde RAM en 230 bytes (incl de ram-copied routines) Flash.
Probleem van die handgeschreven assembly routines: hoe kan ik uitmaken
of de compiler een long-call of een short-call uitvoert naar mijn
assembly functies (die als externe C functies gedeclareerd zijn).
Ik vind echter geen manier om in de source te beschrijven - als het al
kan - dat er een RET of RETP gebruikt moet worden. Nu doe ik dat mbhv
een constante, die geset of gereset wordt op basis van de resultaten
van de eerste compile-ronde.
Volgende situatie doet zich bij mij immers voor in een 9-sector
256Kbyte grote single-bank Flash-EPROM gebaseerd ontwerp: ik wil user
log-data in de Flash stoppen. Daarom is het nodig dat de software die
de Flash EPROM beschrijft uitgevoerd wordt in RAM. De boot-up Flash
sector is hardware write protected, en bevat o.a. functies die niet
veranderen (vb de Flash update subroutine). Ik doe het volgende:
- er is een assembly routine op een absolute gedeclareerd address in
de write-protected Flash sector. Die ontvangt register-parameters.
Via een software interrupt vector wordt de functie aangeroepen, zodat
ik geen absolute, vaste call uitvoer naar een address.
- een C-functie wordt als extern gedeclareerd in 1 van de niet-write
protected Flash sectors en is in werkelijkheid een assembly source.
- Die assembly subroutine haalt de parameters van de juiste stack
(user of system stack) en laad die in de juiste MCU-registers. Ze
voert de gewenste software interrupt uit.
{in geval de write-protected functie vervangen moet worden, kan dat
door de C of assembly functie te veranderen}
- Nu komt het tricky part: binnen de write-protected
software-interrupt Flash stored subroutine, wordt ruimte gealloceerd
op de system stack, gevolgd door een copy van een deel van de
Flash-update subroutine naar de system stack. Dan worden 32-bit
return addressen gepushed: de eerste return (vanuit Flash) wordt naar
RAM gesprongen (de gecopieerde functie op de system stack). Die
schakelt de Flash in write mode en veranderd de inhoud. Op het einde
zet de RAM functie de Flash terug in read mode en voert een RETP uit,
waarbij teruggekeerd wordt naar de write-protected Flash zone. Die
kuist de system stack op en keert terug naar de assembly source, die
op haar beurt de return parameter in het juiste register zet en zo
overdraagt naar de C compiler functies.
Na 15 jaar assembly programmatie kan ik alleen concluderen dat C bij
gebruik in embedded applicaties (zonder RTOS of gebruik van libraries)
eigenlijk zeer beperkte rotzooi is.
Als C gebruikt wordt om pointers naar vanalles en nog wat te
beschrijven, wordt het haast even cryptisch als assembly. En het
fabeltje dat C betere code genereert dan handgeschreven assembly is
alleen waar indien men dit vergelijkt met onervaren assembly
programmeurs. Het gunstigste resultaat dat de (overigens zeer goed
bevonden) compiler haalde met maximale optimalisatie was 2* langer en
stukken trager dan mijn assembly code. Zelfs factoren tot 10* komen
frequent voor (en dan vergelijk ik met de meegeleverde demo C sources,
want zelf krijg ik die cryptische omschrijvingen (nog) niet eens in
elkaar geflanst). C programmeurs vergeten bij hun "optimizing"
compilers 2 dingen:
- De compiler moet al achterhalen wat wil. Net zoals bij
spraakherkenning, autorouters,.. is dat geen eenvoudige klus en vaak
naar verhouding minderwaardig aan rechtstreekse "taal".
- Het gebruik van diverse libraries maakt dat je code al groot wordt
zonder dat er iets gebeurt. Om die reden kunnen veel embedded
ontwerpers geen libs gebruiken (ik spreek van systemen waar PC/104
niet eens in aanmerking komt wegens te duur & te weinig analoog
georienteerd).
- Als je een microcontroller "van de grond" moet liften in C, zit je
met het probleem dat je toch al een assembly startsource moet hebben.
Combineer dat met vb write-protected Flash-EPROM zones, en vergeet de
portabiliteit maar. Wie dat nog kan halen is een een kei, of heeft
code die in het geheel niet optimaal is.
In tegenstelling tot assembly heb je bij C geen "gevoel" meer waar
iets in 't geheugen zit. Je kan ook niet van die fratsen uitvoeren
zoals subroutines copieren en uitvoeren op de stack (en als 't al in C
kan, is het nog even portable als assembly). Hoe kan je in C
bijvoorbeeld aangeven dat je het absolute startaddress van een
instructie moet hebben - zoals bijvoorbeeld in het geval van een Flash
routine die je naar RAM wil copieren?
- De fabel dat assembly niet foutloos te krijgen is vind ik op z'n
zachtst amusant. De code van het laatst door mij ontworpen toestel
bevat 8000 regels assembly statements. Tot heden werd er 1 bug
gerapporteerd: de toestand van een DIP schakelaar werkte averechts met
de manual, iets wat in C even makkelijk fout omschreven kan worden.
De totale ontwikkeling (1 persoon) van schema, print en software,
inclusief het testen besloeg 7 maanden. O.a. 1 van de specs was:
temperaturen meten -op 1 graad nauwkeurig- bij 300 graden C (met een
8-bit A/D converter).
Ik ontwerp ook schema's en printplaten. Daarvoor heb je "prachtige"
autorouters en autoplacement software. De onze kostte net geen
300,000Bfr exclusief BTW (zo'n aanbieding waar alles samen maar een
tikkeltje duurder was dan 2 losse componenten). Dus zogezegd geen
rommel. Lees echter maar eens op
http://www.malotaux.nl/nrm/Lectures/MicroPCB/MCB97B.html waarom
uiteindelijk die hele autorouter/autoplacement software (zeker bij
mixed mode PCBs of hoogfrequent) in bijna alle gevallen is wat ik al
lang ondervonden heb: waardeloze kitch. Hetzelfde met embedded
applicaties in C: voor je de compiler goed en wel kan wijsmaken waar
wat zit en hoe hij er mee om moet, heb je vaak al een aardig stuk
-foutloos- in assembly voor elkaar.
P.S. vb GCC ondersteund de gebruikte processor niet. De gebruikte
compiler is trouwens door diverse collega embedded designers al
bestempeld als van het extreem betere soort. Vb Functies die vb niet
regulier aangeroepen worden, worden uitgesloten tijdens het linken
(dus geen code ruimte ingepalmd). Idem voor lussen waarin niets
gebeurt: die worden niet gecompileerd.
Technical Details
Message-ID:
<3a965314.19920514@news.freegates.be>Path:
arc1-ca.pugleaf.net!supernews.google.com!sn-xit-03!supernews.com!newsfeed.wirehub.nl!fr.usenet-edu.net!usenet-edu.net!newsfeeds.belnet.be!news.belnet.be!news1.carrier1.net!news-in2.freegates.be!not-for-mailShow Full Headers
Complete Article Headers
X-Google-Language: DUTCH,ASCII-7-bit X-Google-Thread: fcea4,f586b9e7b88ddcf0,start X-Google-Attributes: gidfcea4,public X-Google-ArrivalTime: 2001-02-23 09:34:13 PST Path: arc1-ca.pugleaf.net!supernews.google.com!sn-xit-03!supernews.com!newsfeed.wirehub.nl!fr.usenet-edu.net!usenet-edu.net!newsfeeds.belnet.be!news.belnet.be!news1.carrier1.net!news-in2.freegates.be!not-for-mail From: Erno@freegates.be (GE) Newsgroups: be.comp.programming Subject: C met assembly access Date: Fri, 23 Feb 2001 17:32:37 GMT Organization: Tiscali Belgium / FreeGates (Full Access / No Bill !) Lines: 154 Message-ID: <3a965314.19920514@news.freegates.be> NNTP-Posting-Host: news.tiscalinet.be X-Trace: onew2.freegates.net 982949508 8085 212.35.2.81 (23 Feb 2001 17:31:48 GMT) X-Complaints-To: abuse@freegates.be NNTP-Posting-Date: 23 Feb 2001 17:31:48 GMT X-Newsreader: Forte Free Agent 1.11/32.235 X-NNTP-Posting-Host: ppp-151-35-224.tiscalinet.be