• No results found

Verschillen tussen de Intel C en Turbo C++ compiler

5. Software debugging van het ISDN terminal board

5.1 Verschillen tussen de Intel C en Turbo C++ compiler

Zoals in de inleiding van dit hoofdstuk al werd gezegd, zijn door omstandigheden uiteindelijk twee compilers in gebruik: een voor het PC gebaseerd ISDN systeem en ren voor het ISDN terminal board. Het grootste verschil tussen de TURBO C++ compiler en de INTEL compiler is dat de TURBO C++ een geYntegreerde editor, compiler, linker en debugger is terwijl bij het INTEL systeem de editor, compiler, linker en debugger los van elkaar vanuit MS-DOS moet worden gebruikt. Het mag duidelijk zijn dat met een compleet geintegreerd systeem gemakkelijker en snellerkanworden gewerkt.

Het grootste nadeel van de TURBO C compiler is het ontbreken van speciale C startup en exit-codes voor een 'embedded system'. Het is dan zeker niet aan te raden om de normale gegenereerde C code voor de PC te gebruiken voor zo'n non-MS-DOS omgeving of 'embedded system' zoals het ISDN terminal board. Door het ontbreken van de PC ROM routines en door het ontbreken van de Operating System routines die voor de juiste communicatie met de randapparatuur zorgen, zal de software vastlopen zodra een van die functies wordt aangeroepen. De C-functies die uiteindelijk voor deze systeem afhankelijke codes zorgen zijn niet allemaal bekend. Voor enkele functie is dit weI duidelijk. Deze zijn namelijk speciaal voor de PC randapparatuur geschreven en bevinden zich vooral in de bijgeleverde standaard C files STDIO.H (STanDard Input/Output) en CONIO.H (CONsole Input/Output). Deze functies moeten we dus bij de PC-versie van de ISDN software, dus bij de software voor de MITEL kaarten zo weinig mogelijk gebruiken om later geen conversieproblemen te creeren bij het ISDN terminal board

Helaas is dit niet in acht genomen, de op de PC ontwikkelde ISDN software maakt gebruik van de standaard afdruk- en toetsenbordroutines die bij alle C compilers als bibliotheek, geoptimaliseerd en aangepast voor het specifieke systeem waarop de software draait, wordt meegeleverd. Vooral de Monitoring Dispatcher en het Call Control systeem maken gebruik van het speciale door Borland, leverancier van TURBO C, ontwikkelde 'window' systeem voor tekst.

Bij de conversie van de ISDN software naar het ISDN terminal moesten nu eerst alle afdruk-en toetsenbordfuncties vervangen worden door zelf geschreven routines die via het RS232 interface van de IDPC met behulp van ANSI-codes het beeld van een terminal kunnen aansturen en het toetsenbord van de terminal kunnen aflezen. Het window-systeem

waarop de Monitoring Dispatcher bij de MITEL kaarten is gebaseerd moest geheel herschreven worden tot een systeem zonder windows. Dit is uiteindelijk door E.J.E.T.

Boonnans [11] gedeeltelijk gedaan. Echter werken enkele van de al omgezette functies niet goed omdat deze niet afdoende getest konden worden door het ontbreken van een werkende S-bus.

Om deze conversieproblemen in de toekomst te vennijden is er nu een window systeem voor het terminal board ontwikkeld (zie hoofdstuk 6) dat alleen gebruik maakt van een 'standaard' afdrukroutine en een toetsenbordroutine.

Een ander probleem dat zich voordoet zijn de beschikbare compiler opties. De TURBO C-compiler kan 'case-sensitive' C-compileren en linken. En deze mogelijk is dus ook vele malen gebruikt bij het verzinnen van variabelen en type definities. Een goed voorbeeld hiervanis in het onderstaande stukje programma te zien.

*dp;

RAM; /*#MR comment, never used*/

Deze manier van benoemen leidt tot fouten en onduidelijkheden. Vooral als er voor de data structuur van de variabele 'state' ook nog eens de onderstaande type definitie wordt gebruikt.

typedefstruct state state;

De TURBO C-compiler compileert enlinktdit soort programma structuren zonder moeite ook als aIle fout-herkenningsroutines aan worden gezet. De INTEL C-compiler heeft

echter geen 'case-sensitive' optie. AIle namen, op welke manier ze ook geschreven zijn, zijn hetzelfde, dus: isdn = Isdn

=

ISDN, enz.. Indien dan bij de INTEL C-compiler het foutendetectieniveau op standaard blijft staan (diagnostic(1)) worden enkele fouten die op deze manier ontstaan niet opgemerkt, daarintegen ziet de optie 'diagnostic(O)' weI de nieuwe, vreemde variabelen toekenningen en vooral de dubbele variabele defmities. Beide compilers Iaten weI toe dat voor een variabele en een type definitie eenzelfde naam wordt gebruikt, dus:

struct ram ram;

is weI toegestaan maar Ievert een slecht te volgen en te debuggen programma op. Het Ieidt tot fouten en overbodige declaraties. Een oplossing voor het dubbele variabelen probleem wordt in de volgende paragraaf gegeven.

Ben tweede compiler optie die voor conversieproblemen zorgt is de ROM optie van de INTEL compiler. Deze optie hebben we nodig omdat we uiteindelijk willen dat de software in een (EP)ROM wordt gezet. Door de ROM optie zijn C constructies die automatisch de variabelen initialiseren niet toegestaan [12]. AIleen constanten mogen tijdens de declaratie worden gei"nitialiseerd. Dit moet dan extra worden aangegeven. Ook dit wordt in het volgende hoofdstuk belicht.

5.2 Conversiefouten

In de voorgaande paragraaf hebben we alopgemerkt dat de INTEL C-compiler niet 'case-sensitive' kan compileren en daardoor o.a. dubbele variabele declaraties ontstaan. De heer E.J.E.T. Boormans [11] heeft deze dubbele declaraties opgelost door een van de declaraties te verwijderen. Hij was van mening dat een dubbele verwijzing voor de structures overbodig was, dus :

struct bearercap

*

AudioCap;

struct bearercap AUDIOCAP;

AudioCap

=

&AUDIOCAP;

werd omgezet in:

struct bearercap

*

AudioCap,.

AudioCap->injo[Oj

=...

De dubbele verwijzing naar een structure is inderdaad niet nodig, maar de gebruikte oplossing is helemaal fout. De C-compilers kunnen het juiste gebruik van pointers ook niet opsporen of controleren zodat dit soort fouten dan ook moeilijk te vinden zijn.

Op de bovenstaande manier worden aile pointers die op deze manier zijn behandeld, dus alle dubbele pointer variabelen, niet geYnitialiseerd en kunnen dus overal naartoe verwijzen.

Een toekenning aan een variabele uit deze structuur, zoals het info[O] veld, kan dus overal in het geheugen terecht komen en zo het programma op onverklaarbare wijze plotseling laten vastlopen. Tevens wordt op deze manier nergens een stukje geheugen gereserveerd voor de data-structuur. Als we al van een enkele verwijzing gebruik willen maken, moet dit op de volgende rnanier gebeuren:

struct bearercap AUDIO CAP;

&AUDIOCAP->injo[Oj

=...

of:

AUDIOCAP.injo[Oj

=...

We moeten dan iedere pointer in het programma gaan veranderen. We kunnen dus beter een andere oplossing zoeken.

Een standaard of vaste manier voor de constructie van een variabele-naam biedt in dit geval een mogelijke oplossing. We kunnen bijvoorbeeld namen voor pointers laten eindigen op '..Ptr' of de namen voor structures laten eindigen op '...struct'. Bij de eerste methode moeten we echter weer in het gehele programma de pointers gaan veranderen.

Omdat de naam van de structure slechts tweemaal in het programma voorkomt, bij het definieren van het datagebied voor de structure en bij de initialisatie van de bijbehorende pointers, veranderen we de namen van de structures. De desbetreffende programmadelen

die hieronder staan venneld zijn op die rnanier aangepast

• GLOBAL.C (zie bijlage 4):

struct process

/* Pointer to structure */

/* Create structure space */

struct callentity

*cause, *callid, *caltstate,*chanid, *progind;

CAUSEstruct, CALLIDstruct, CAUSTATEstruct;

CHANIDstruct, PROGINDstruct;

*cldparnr, *cldparsa, *clgparnr, *clgparsa;

CWPARNRstruct, CWPARSAstruct;

CLGPARNRstruct,CLGPARSAstruct;

*NotPresent;

NOT_PRESENTstruct;

*SpeechCap; /*Bearer capability for speech coding*/

SPEECHCAPstruct;

*AudioCap; /*Bearer capability for3.1 kHz audio */

AUDIOCAPstruct;

*Urdat1Cap; /* Bearer capability for

unrestricted digital info type1 */

URDATI CAPstruct;

struct bearercap *Urdat2Cap; /*Bearer capability for unrestricted digital info type 2 */

struct bearercap URDAT2CAPslruct;

struct bearercap *bearcap;

struct bearercap BEARCAPstnu:t;

De initialisaties van de betreffende pointers worden dan weer in de initialisatieroutines teruggezet:

• INITPROC.C:

PointParser=&POINTPARSERstruct;/*#MR comment, Initialize pointers to declared data structures in memory*/

dp

=

&DPstruct;

Een iets ander probleem treedt bij de IDPC subroutines op. Dit is in het onderstaande stukje te zien (lNITIDPC.C):

extern struct idpc (far *IDPC);

void/ar idpc_init(byte idpc) (

i/(idpc==O) IDPC=IDPC_BASE1;

}

We zien dat drie maal dezelfde naam voor een andere soort variabele wordt gebruikt. De 'IDPC' pointer naar de 'idpc' structure kan worden gehandhaafd, maar de procedure-parameter moet worden verandetd. We maken van de procedure-parameter 'idpc' 'idpcnr' en passen dit toe op de volgende subroutines:

• idpc_initOinINITIDPC.C

• idpc_controlO in INmDPC.C

• idpc_ram_initOinINITRAM.C

• idpcintOinINT_IDPC.C

Een andere melding die door de INTEL C-compilerindiagnostic mode 0 wordt gegeven, is de 'Suspicious pointer conversion'. Deze melding hoeft geen problemen te geven bij de software maar het oplossen van deze foutmelding is toch verstandiger. Hij wordt veroorzaakt door de onderstaande constructie waarbij een vet gedrukte 'const' wordt vergeten:

const struct test ABeD ...;

const struct test *TestPtr;

TestPtr = &ABCD;

We kunnen dan door het gebruik van de fout-gedefinieerde pointer 'TestPtr' toch nog de constante structure veranderen. Omdat de INTEL C-compiler door de ROM optie alle

constanten in het ROM plaatst, zal in ons geval geprobeerd worden naar het ROM te schrijven. We veranderen dus voor de zekerheid alle voorkomende gevallen:

const struct statecodefar *Searchin L3_AUXn...C

const struct state far *CallStatein L3_INIT.C en L3_INTER.C

const struct state *TableStatein TERMDISP.C

const struct state *Auxstatein TERMFUNC.C

Om verdere conversiefouten te voorkomen, is de prototyping van functies en variabelen nit de vele 'include/header'-files gehaald. Ieder programmaonderdeel of file begint dan nu ook op de volgende manier:

• Omschrijving van het betreffende software-onderdeel

• Include en header files, waarbij de eigen ISDN files nooit verdere files inladen en gebruiken

• Prototyping van de functie(s)indit programmagedeelte

• Declaratie van de global variabelen

• Prototyping van de externe functies die in dit gedeelte gebruikt worden

• Declaratie vandeexteme variabelen die in dit gedeelte gebruikt worden

• De functie(s) van dit programma onderdeel