← ST-Computer 03 / 1988

Vom Quelltext zum Programm

Grundlagen

NACH EINER KURZEN ERLÄUTERUNG DER FUNKTIONSWEISE EINES INTERPRETERS UND COMPILERS SOLL SCHWERPUNKT-MÄSSIG AUF DEN LINKER EINGEGANGEN WERDEN, DER LETZTENDLICH EIN ABLAUFFÄHIGES PROGRAMM ERZEUGT. EIN SOLCHER LINKER FÜR DAS GST-FORMAT, DER MIT DEM LATTICE C-COMPILER ENTSTAND. LIEGT ALS LISTING VOR.

Sicherlich weiß jeder, der in höheren Programmiersprachen programmiert, darĂŒber Bescheid, daß der Computer diese Sprache nicht so ohne weiteres versteht. Er akzeptiert nĂ€mlich nur Anweisungen in seiner Maschinensprache, Auch allgemein besannt dĂŒrfte sein, daß dies beim ATARI ST die Maschinensprache des 68000-Prozessors ist. Solche Maschinensprachen sind fĂŒr den Menschen beinahe unlesbar, denn die Maschinenbefehle sind allesamt durch Zahlen codiert, deren Bedeutung man in einer Tabelle nachschlagen kann. Dies sann man auch einem Assembler ĂŒberlassen, dadurch wird die Maschinensprache Lesbarer und leichter anwendbar. Der Assembler ĂŒbersetzt zum Beispiel Texte wie 'ADD # 1,D0’ in den codierten Maschinenbefehl $5240.

Aber auch in einer solchen Assemblersprache ist das Programmieren noch recht | mĂŒhselig. Man stelle sich etwa die Auswertung eines Ausdrucks, wie zum Beispiel z=3*(x+y), vor. In der 68000-Assemblersprache hĂ€tte man dazu etwa folgendes zu schreiben:

MOVE x,D0 ADD y,D0 MULS #3,D0 MOVE D0,z

Die Schreibweise z=3*(x+y) ist eben doch : um einiges verstĂ€ndlicher. Deshalb existieren verschiedene höhere Programmiersprachen, die es ermöglichen, Programme in bedeutend lesbarerer Form zu schreiben. Allerdings mĂŒssen diese Programme, bevor sie ausgefĂŒhrt werden, in die fĂŒr den I Computer verstĂ€ndliche Form gebracht werden. DafĂŒr gibt es grundsĂ€tzlich zwei Möglichkeiten, nĂ€mlich Interpreter und I Compiler.

Der Interpreter

Bekanntestes Beispiel dafĂŒr ist sicherlich der BASIC-Interpreter. Ein Interpreter durchlĂ€uft den Programm-Quelltext, analysiert dabei Befehl fĂŒr Befehl und fĂŒhrt entsprechende Aktionen durch. Bei einer Zuweisung ‘neu=alt’ passiert etwa folgendes:

Das Wort ‘neu’ wird Buchstabe fĂŒr Buchstabe gelesen, so lange, bis das '=’ Zeichen erreicht ist. Dann wird in einer Variablentabelle, die der Interpreter anlegt, nach der Variablen ‘neu’ gesucht. Ist diese gefunden, wird das Wort ‘alt’, wieder Buchstabe fĂŒr Buchstabe gelesen und in der Variablentabelle nach ‘alt’ gesucht. Nachdem auch diese Variable gefunden wurde, kann der Wert von ‘alt’ auf die Variable 'neu' kopiert werden.

Wie man sieht, verlangt selbst eine einfache Zuweisung eine Menge Arbeit vom Interpreter. Stellt man sich nun noch vor. daß diese Zuweisung innerhalb einer Schleife stattfindet, kann man erahnen, warum Interpreter oft so langsam sind.

Der Compiler

...geht einen anderen Weg. Er analysiert den Quelltext nur einmal und ĂŒbersetzt ihn vollstĂ€ndig in ein Maschinenprogramm. Dabei wird jeder Variablen eine Speicheradresse zugeordnet, und die Zuweisung ‘neu=alt’ wird beispielweise in den Maschinenbefehl ‘MOVE 1000,1002’ ĂŒbersetzt, wobei 1000 der Speicherplatz fĂŒr die Variable ‘alt’ und 1002 der Speicherplatz fĂŒr die Variable ‘neu’ ist. Man kann sich jetzt leicht vorstellen, wieviel schneller das Programm wird, wenn es compiliert ist.

GrundsĂ€tzlich teilt man den Compilationsprozeß in drei Schritte ein, nĂ€mlich in lexikalische Analyse (Scanning), syntaktische Analyse (Parsing) und die Codeerzeugung (Code Generation). HĂ€ufig, aber nicht immer, können diese drei Phasen in einem einzigen Durchlauf (Pass) parallel durchgefĂŒhrt werden. Beispiele dafĂŒr sind Sprachen wie PASCAL oder C. Obwohl bei PASCAL und C grundsĂ€tzlich möglich, wird der Compiler trotzdem oft in mehrere Phasen eingeteilt, um Speicher zu sparen. Die Kommunikation der Phasen untereinander erfolgt dann durch Zwischenfiles. Doch nun zur eigentlichen Arbeit des Compilers:

In der lexikalischen Analyse wird der Quelltext eingeteilt in Symbole wie Konstanten, Namen, SchlĂŒsselwörter, Begrenzungszeichen und Operatoren, Kommentare werden eliminiert. Gleichzeitig werden Namen stabeilen mit Variablen-, Konstanten-, Prozedumamen oder Ă€hnlichem aufgebaut.

Die syntaktische Analyse arbeitet nicht mehr mit einzelnen Zeichen, sondern mit den vom Scanner gelieferten Symbolen. Dabei wird die eigentliche SyntaxprĂŒfung vorgenommen, also die richtige Stellung der SchlĂŒsselwörter, Strichpunkte etc. Der Parser liefert auch die Basis fĂŒr die nachfolgende Codeerzeugung.

Die Codeerzeugung liefert im Prinzip ein lauffĂ€higes Maschinenprogramm, es heißt jetzt Objektcode und wird in einer Objektdatei abgelegt. Dieses Programm kann noch - möglicherweise vom Compiler selbst - optimiert und danach vom Linker zum endgĂŒltigen Programm verarbeitet werden. Doch wozu nun noch der Linker?

Aufgaben des Linkers

Wie gesagt, ist das vom Compiler erzeugte Programm fĂŒr sich alleine noch nicht lauffĂ€hig. Es befinden sich darin beispielsweise Unterprogrammaufrufe, die noch keine richtige Sprungadresse enthalten. Schreibt man in C etwa ‘printf(0)', so ĂŒbersetzt der Compiler dies sinngemĂ€ĂŸ in folgendes Maschinenprogramm:

MOVE #0,-(SP) JSR ... ADDQ #2,SP

Das heißt im Klartext: Bringe die ‘0’ als Parameter auf den Stack, rufe dann das Maschinenprogramm ‘printf’ auf und bringe anschließend den Stack wieder in Ordnung. Nun weiß der Compiler aber noch gar nicht, wo das ‘printf’-Programm sich im Speicher befinden wird, deshalb kann er den JSR-Befehl nicht mit einer vernĂŒnftigen Adresse versorgen. Statt-dessen setzt er an dieser Stelle eine Mitteilung an den Linker ein, so daß dieser die fehlende Adresse ergĂ€nzen kann.

Erst der Linker weiß ja, wo sich das ‘printf’-Programm letztendlich befindet, denn er verbindet das compilierte Programm mit den entsprechenden Libraries (Bibliotheken), wo unter anderem auch das ‘printf-Unterprogramm versteckt ist. Dasselbe passiert, wenn man in einem Programm externe Variablen verwendet, die in einem anderen Quelltext definiert sind. Auch hier sind die endgĂŒltigen Adressen nur dem Linker bekannt. Das wĂ€re dann auch schon die wichtigste Aufgabe des Linkers, aber er tut noch etwas mehr.

Beim TOS werden Programme in drei Bereiche aufgeteilt, TEXT, DATA und BSS. Der TEXT-Bereich (oder TEXT-Section) sollte immer Maschinenbefehle enthalten; der DATA-Bereich (oder DATA-Section) sollte Daten enthalten, darunter versteht man vordefinierte Felder, Stringkonstanten und so weiter; der BSS-Bereich (-Section) schließlich enthĂ€lt Undefinierte Daten (leere Variablen oder Felder). Da die Daten im BSS-Bereich Undefiniert sind, ist es nicht nötig, sie auf Diskette abzuspeichern. Das Betriebssystem reserviert beim Start des Programms den nötigen Speicherplatz und besetzt ihn einfach mit Nullen vor.

In den Objektdateien, dazu gehören natĂŒrlich auch die Bibliotheken, sind die EintrĂ€ge in die verschiedenen Sections bunt durcheinandergewĂŒrfelt und mĂŒssen vom Linker sortiert werden. Am Ende entsteht also eine große TEXT-Section, eine große DATA-Section und eine große BSS-Section. Nun aber ins Detail, und damit zum GST-Format:

Das GST-Obiectfile-Format

Das GST-Format entspricht gewissen Syntaxregeln, die vom Linker analysiert und ĂŒberprĂŒft werden. Der Linker arbeitet also genauso wie ein Compiler, nur die Syntaxregeln sind bedeutend einfacher. Diese Syntaxregeln sehen in Backus-Naur Form wie folgt aus:

Die geschweiften Klammern deuten eine beliebige Wiederholung eines Elements an, auch nullmal ist erlaubt. Die eckige Klammer bedeutet null- oder einfaches Auftreten eines Elements. Der senkrechte Strich heißt “oder”.

Man sieht, daß ein Objektfile aus beliebig vielen Modulen bestehen kann. Wenn man ein normales Programm compiliert oder assembliert, entsteht ein Objektfile, das genau ein Modul enthĂ€lt. Eine Library besteht im allgemeinen aus mehreren Modulen. Man kann sich Libraries selbst erzeugen, indem man mehrere Objektfiles aneinanderhĂ€ngt.

Ein Modul beginnt mit einem -Kommando, gefolgt von beliebig vielen < chunk >s und endet mit einem -Kommando. Zum < string > ist noch zu sagen, daß das erste Byte die LĂ€nge des Strings angibt und dann entsprechend viele Zeichen folgen. Innerhalb eines < chunk >s stehen neben Symboldefinitionen und Referenzen die eigentlichen Datenbytes fĂŒr eine Section. Im GST-Format sind beliebig viele Sectionnamen erlaubt, nicht nur TEXT, DATA und BSS. Ein ist ein Kommentar, der keine weitere Bedeutung hat. Mit < xdef > wird ein Symbolname(< string >) definiert, wobei den Wert des Symbols bezĂŒglich des Sectionstarts der Section angibt. Es wird hier nicht vom Namen der Section Gebrauch gemacht, sondern von einer , die mit dem -Kommando dem Namen zugeteilt wird. Diese Vorgehensweise soll das Auftauchen immer wieder gleicher Namen im Objektfile vermeiden und so Speicher sparen. Negative s gehören zu Sectionnamen, positive < id >s zu normalen Symbolnamen und die Null bedeutet im < xdef >-Kommando, daß ein absolutes Symbol definiert

<objectfile> ::= <module> { <module> } <module> ::= <source> { <chunk> } <end> <source> ::= $FB $01 <string> <end> ::= $FB $13 <string> ::= <byte> { <byte> } <chunk> ::= { cheader command> ( [ «section command> <body> ] ■ <header_command> ::= <comment> | <xdef> j <define> <comment> ::= $FB $02 <string> <xdef> ::= $FB $06 <string> <long> <id> <define> ::= $FB $10 <id> <string> <id> ::= <word> <word> ::= <byte> <byte> <long> ::= <word> <word> <section_command> ::= <section> | <org> | <coirunon> <section> ::= $FB $04 <id> <org> ::= $FB $03 <long> <common> ::= SFB $12 <id> <body> ::= { <data byte> | cbody command> } <body_command> ::= <offset> | <xdef> | <xref> 1 <define> j‘ <comment> <offset> ::= $FB $05 <long> <xref> ::= $FB $07 <long> <byte> { <op> <id> } $FB <op> ::= + | - <data byte> ::= $00 | ... I $FA | $FB $FB | $FC | . , ,, | $FF

wird. Das < section >-Kommando legt fest, welcher Section die nachfolgenden Datenbytes zugeordnet werden sollen, es muß deshalb immer vor dem Auftauchen irgendwelcher Datenbytes stehen. Das -Kommando bestimmt eine absolute Adresse, ab der die Datenbytes abgelegt werden. Das -Kommando entspricht ziemlich genau dem < section >-Kommando, allerdings werden die folgenden Datenbytes nicht an die bereits vorhandene Section angehĂ€ngt, sondern diese wird durch die neue Definition ĂŒberschrieben und, falls der neue Bereich lĂ€nger als der alte ist, verlĂ€ngert. Mit dem < offset >-Kommando kann man eine bestimmte Position innerhalb der momentanen Section anwĂ€hlen, von der ab dann die noch folgenden Datenbytes eingetragen werden. Das < xref >-Kommando trĂ€gt nun einen Symbolwert, beziehungsweise einen Wert der durch Additionen und Subtraktionen von Symbolwerten entsteht, an der augenblicklichen Position ein. Der Wert ergibt sich aus dem konstanten -Wert und die durch die folgenden s gegebenen Symbolwerte, die bei < op >='+' addiert und bei < op >=’-’ subtrahiert werden. Die spezielle Null hat hier die Bedeutung der augenblicklichen Position im Gesamtprogramm. Das Ergebnis kann nach der Berechnung als Byte, Wort oder Langwort verwendet werden, außerdem kann es vorzeichenbehaftet und sogar noch relativ zur augenblicklichen Position sein. Über diese Möglichkeiten gibt das im < xdef >-Kommando enthaltene Auskunft, die Bits dieses Bytes haben, falls sie gesetzt sind, folgende Bedeutung:

Bit 0 : Das Ergebnis ist ein Byte.
Bit 1 : Das Ergebnis ist ein Wort.
Bit 2 : Das Ergebnis ist ein Langwort .
Bit 3 : Das Ergebnis ist vorzeichenbehaftet .
Bit 4 : Das Ergebnis ist vorzeichenlos .
Bit 5 : Das Ergebnis ist PC-relativ zu interpretieren.
Bit 6 : Das Ergebnis benötigt zusĂ€tzliche Relozierung nach dem Laden, das heißt, es muß in eine Tabelle ein getragen werden, die sich am Ende des Programm-Files befindet, und die das Betriebssystem zur endgĂŒltigen Anpassung des Programms an die endgĂŒltige Startadresse benötigt.

Dazu jetzt ein kleines Beispiel in C :

void main() { printf(0); }

Der LATTICE C-Compiler erzeugt daraus folgendes Programm:

42A7 CLR.L -(SP) 4EB9 0000 0000 JSR PRINTF 588F ADDQ.L #4, SP 4E75 RTS

Das zugehörige Objektfile sieht so aus:

FB 01 08 42 45 49 53 50 49 45 4C SOURCE BEISPIEL FB 10 FF FF 04 74 65 78 74 DEFINE -1 TEXT FB 10 FF FE 04 64 61 74 61 DEFINE -2 DATA FB 10 FF FD 05 75 64 61 74 61 DEFINE -3 UDATA FB 10 FF FC 05 64 65 62 75 67 DEFINE -4 DEBUG FB 10 00 05 04 5F 33 32 6B DEFINE 5 _32K FB 04 FF FF SECTION -1 FB 06 04 6D 61 69 6E 00 00 00 00 FF FF XDEF MAIN 0 -1 42 A7 4E B9 Code fĂŒr CLR.L -(SP) und JSR FB 10 00 06 06 70 72 69 6E 74 66 DEFINE 6 PRINTF FB 07 00 00 00 00 54 2B 00 06 FB XREF 0 54 + 6 58 8F 4E 75 Code fĂŒr ADDQ.L #4,SP und RTS FB 04 FF FE SECTION -2 FB 04 FF FD SECTION -3 FB 13 END

Dazu folgende ErlĂ€uterungen: XDEF MAIN 0 -1 heißt, daß das Symbol MAIN den Wert 0 relativ zur Section -1 hat. Aus den vorangegangenen DEFINE-Kommandos erkennt man, daß -1 die TEXT-Section ist. Das Kommando XREF 0 54 + 6 heißt: Trage den Wert 0 plus den Wert des Symbols 6 als vorzeichenloses Langwort, mit Eintrag in die ‘Runtime-Relocation-Table", an der momentanen Programmposition ein. Symbol 6 ist, wie man am darĂŒberliegenden DEFINE erkennt, das PRINTF-Symbol. Damit ist der JSR-Be-fehl vollstĂ€ndig mit der richtigen Adresse versorgt. Die Sections DATA. UDATA und DEBUG, die zwar per DEFINE erwĂ€hnt sind, erhalten keine Daten. Doch nun zum Linker selbst:

Die Arbeitsweise des Linkers LINK

Der hier vorgestellte Linker verarbeitet alle Objektfiles in einem Durchlauf (Pass) und erstellt das lauffĂ€hige Programm im Speicher. Der dazu nötige Speicherbereich ist auf 100 Kbyte festgelegt, kann aber per Option (fast) beliebig vergrĂ¶ĂŸert oder verkleinert werden. Nach Fertigstellung des Programms im Speicher wird dieses auf Datei geschrieben. Der Aufbau des Programms aus den Objektfiles geschieht wie folgt:

FĂŒr jedes Sprachelement des Objektfiles steht im Programm genau eine Prozedur, die eben dieses Sprachelement analysiert, dies sind die Prozeduren:

'link_file' fĂŒr <object_file> 'module' fĂŒr <nrodule> 'chunk' fĂŒr <chunk> 'body' fĂŒr <body> 'section_command' fĂŒr <section_command> 'header_command' fĂŒr <header_command> 'xref_dir' fĂŒr <xref> 'xdef_dir' fĂŒr <xdef> 'offset_dir' fĂŒr <offset> 'data_dir' fĂŒr <data_byte> 'common_dir' fĂŒr <common> 'org_dir' fĂŒr <org> 'section_dir' fĂŒr <section> 'comment dir' fĂŒr <comment>

Diese Prozeduren rufen sich den Syntaxregeln entsprechend gegenseitig auf. Diese Methode findet sich auch in vielen Compilern wieder; ein Compiler, der nach diesem Prinzip arbeitet, heißt ‘Recursive-Descent-Compiler". Da wir nun schon bei der Analogie zum Compiler sind, sei noch folgendes erwĂ€hnt: Scanner, Parser und Codegenerator arbeiten ineinander verschrĂ€nkt, der Linker benötigt also nur einen einzigen Pass. Der Scanner ist realisiert in der Prozedur ‘nxsy’, die die Bytefolgen aus dem Objektfile in syntaktische Einheiten zerlegt. Diese Einheiten spiegeln sich in der Typdefinition von ‘DIRECT" wieder. Der Parser besteht genau aus den obengenannten Prozeduren, die nicht mehr mit den Datenbytes, sondern mit den vom Scanner gelieferten Einheiten arbeiten. Diese Prozeduren bewerkstelligen auch zum grĂ¶ĂŸten Teil die Codeerzeugung, das heißt, sie bringen die aus den Objektfiles ankommenden Sections in die endgĂŒltige Reihenfolge. Gleichzeitig bauen sie ein nicht unerhebliches Tabellenwerk auf, das das Auffinden Von Symbolen, Sections, Modulen und s ermöglicht.

Erst nach dem Einlesen sĂ€mtlicher Objektfiles sind (hoffentlich) alle Symbole definiert, und die aufgetretenen s können berechnet werden. Diese Berechnung geschieht in der Prozedur ‘all_xrefs\ die ihrerseits die Prozedure ‘calc_xref’ zur Bearbeitung eines s benutzt. In ‘all_xrefs’ entsteht auch die “Runtime-Relocation-Table”, die Tabelle also, die es dem Betriebssystem ermöglicht, das Programm an jede beliebige Position im Speicher zu verschieben. Die Prozedur ‘debug_table’ hĂ€ngt, falls gewĂŒnscht, an das Programmfile noch die gesamte Symboltabelle an, die von einem symbolischen Debugger weiterverwendet werden kann. Das fertige Programm wird von der Prozedur ‘write_prog’ auf Datei geschrieben.

Bei Libraries gibt es nun die Besonderheit, daß die in ihr enthaltenen Modulen nur hinzugelinkt werden, wenn dort Symbole definiert sind, die auch woanders gebraucht werden. Die Prozedur ltest_module' liest deshalb zunĂ€chst ein ganzes Modul in einen Pufferspeicher und stellt fest, ob eines der in diesem Modul definierten Symbole benötigt wird. Nur wenn das der Fall ist, wird der Pufferinhalt dem Programm in gewohnter Weise hinzugefĂŒgt. Die GrĂ¶ĂŸe des Puffers ist mit 32 Kbyte vordefiniert, kann aber per Option geĂ€ndert werden.

Achtung! Prozeduren innerhalb eines Moduls kann der Linker nicht einzeln hinzufĂŒgen oder weglassen, denn innerhalb eines Moduls können sich die Prozeduren gegenseitig aufrufen, ohne daß der Linker davon etwas merkt. Definiert man etwa in einem C-Quelltext zwei Prozeduren, von denen eine die andere aufruft, so kann der Aufruf meist bereits vom Compiler durch einen relativen Sprung compiliert werden, der Linker hat an dieser Stelle dann keine Arbeit mehr.

Zum Aufbau und Suchen von Tabellen existieren folgende Prozeduren:

'app_mod' : EinfĂŒgen eines Modulnamens in die Modulnamentabelle; dies ist eine lineare Liste. 'app_xsy' : EinfĂŒgen eines Symbols in einen binĂ€ren Baum. 'app_sec' : EinfĂŒgen einer Sectiondefinition in eine lineare Liste. 'src_xsy' : Suchen eines Symbols im BinĂ€rbauin. 'src sec' : Suchen einer Sectiondef inition in der linearen Liste.

Der VollstÀndigkeit halber seien noch die folgenden Prozeduren erwÀhnt:

'halt' : Abbruch des Programms nach einem fatalen Fehler. 'strnicmp' Vergleich zweier Strings ohne Unterscheidung von Klein- und Großbuchstaben mit LĂ€ngenbegrenzung. 'statistic' : Anzeige der Sections mit LĂ€ngenangabe 'list xsy' Ausdrucken aller oder nur Undefinierter Symbole. 'get drct' : Ein Byte aus dem Objektfile lesen. 'get byte' : Ein Byte entweder aus dem Modulpuffer oder dem Objektfile lesen 'move_up' Speicherbereich nach oben schieben, um so Platz fĂŒr die Erweiterung einer Section zu schaffen ’init mem' Speicherbereiche per 'malloc' reservieren 'make ext' Falls ein Dateiname keine Extension besitzt, so wird hier eine angehĂ€ngt. Dazu werden die Funktionen 'stcgfe' und 'strmfe' aus der Library des LATTICE-C gebraucht, 'stcgfe' isoliert die Extension aus einem Dateinamen, 'strmfe' fĂŒgt eine neue Extension an. 'command line' : Diese Prozedur analysiert die Kommandozeile und setzt entsprechende Flags.

Weshalb ĂŒberhaupt noch einen Linker fĂŒr das GST-Format ?

Besitzer des GST-Linkers werden sich sicherlich schon oft ĂŒber die mangelnde Geschwindigkeit desselben, aber auch ĂŒber die immer wiederkehrenden Bomben wĂ€hrend des Linkprozesses geĂ€rgert haben. Der hier vorgestellte Linker arbeitet bei Diskettenbetrieb mit der 4.5-fachen Geschwindigkeit und hat bisher auch bei grĂ¶ĂŸeren Programmen keine Bomben geworfen.

Ein weiterer Nachteil des GST-Linkers besteht darin, daß dieser nur die TEXT-Section des ablauffĂ€higen Programms benutzt. Das heißt, daß alle in den Objektfiles definierten Sections dort landen. Bei der DATA-Section (initialisierte Felder, Stringkonstanten usw.) wĂ€re das nicht weiter schlimm, wer in seinem Programm aber leere Felder von mehreren Kilobytes GrĂ¶ĂŸe vereinbart hat, wird sicher wenig VerstĂ€ndnis dafĂŒr haben, daß diese unnötigerweise Speicherplatz auf der Diskette belegen. Normalerweise sind nĂ€mlich nur TEXT- und DATA-Bereiche auf Datei abgelegt, der BSS-Bereich wird erst nach dem Laden des Programms vom Betriebssystem initialisiert. Dieser Linker verteilt die Bereiche richtig auf TEXT-, DATA- oder BSS-Section, so daß die ablauffĂ€higen Programme nicht selten gewaltig zusammenschrumpfen. Dies kann aus KompatibilitĂ€tsgrĂŒnden an- und abgeschaltet werden.

Welche Sections wo landen, ist in der Prozedur ‘def_section' festgelegt. Diese ordnet beliebigen Sectionnamen die Zahl -1 zu, so daß diese Sections in die TEXT-Section des TOS gelangen. Sections mit Namen ‘DATA’ wird die Kennung -2 zugeteilt, was zur Folge hat, daß diese auch in die DATA-Section des TOS gebracht werden. Sections mit Namen ‘ÜDATA’ und ‘BSS' bekommen die Kennung -3 und werden dadurch der BSS-Section des TOS zugeordnet. Diese wird, im Gegensatz zur Vorgehensweise des Original-GST-Linkers, nicht auf Diskette abgespeichert, falls man die -SEC Option eingeschaltet hat. Die Namen sind kompatibel zum LATTICE C, dieser nennt die BSS-Section nĂ€mlich ‘UDATA’, und zum Metacomco Assembler, der schon den richtigen Namen "BSS' verwendet. Wer noch andere Sections im BSS-Bereich untergebracht haben möchte, kann dies in ‘def_section’ in analoger Weise eintragen.

Aufruf des Linkers

Der Linker sollte als “TTP”-Programm benannt werden, denn er erwartet die Eingabe einer Kommandozeile, die der des GST-Linkers sehr Ă€hnlich ist, und folgendes Aussehen haben sollte:

[<module>] [<control> [<listing> [<program>]]] {<option>]

module : ist der Dateiname einer Objekt-Datei, die gelinkt werden soll. Dieser Name wird vom Linker immer mit ".BIN" ergÀnzt.

control : ist der Name einer Control-Datei, in der Linkeranweisungen stehen.

listing : ist der Name einer List-Datei,

program : ist der Dateiname des fertigen Programms .

Als Options sind zulÀssig:

-LIST [<listing>] : Der Linker erzeugt ein Listing in der Datei mit Namen <listing>. -PROG [<program>] : Der Linker legt das Programm in der Datei <program> ab. -MEM <size> : Damit wird die GrĂ¶ĂŸe des Speichers in KByte festgelegt in dem der Linker das fertige Programm abspeichert. Also mit "-MEM 200" können Programme bis zu 200 Kbyte GrĂ¶ĂŸe gelinkt werden. Der Defaultwert ist 100 Kbyte. -BUF <size> : Der Linker benutzt fĂŒr die Bearbeitung von Libraries einen Puffer, in dem er zunĂ€chst Module ablegt, und dann testet, ob diese dazugelinkt werden mĂŒssen oder nicht. Die GrĂ¶ĂŸe des Puffers kann mit dieser Option in Kbytes bestimmt werden. Der Defaultwert ist 32 Kbytes, und damit im allgemeinen ausreichend. -NOLIST : Der Linker produziert kein Listing. -NODEBUG : Der Linker erzeugt keine Symboltabelle im Programm. -NOPROG : Der Linker erzeugt kein Programm. -DEBUG : Das Programm erhĂ€lt eine Symboltabelle. -SYM : Eine Symboltabelle wird ans Listing angehĂ€ngt. -NOSYM : Keine Symboltabelle im Listing. -SEC : Sections mit Namen "TEXT" und andere werden in der TEXT-Section abgelegt. Sections mit Namen "DATA" werden in der DATA-Section abgelegt. Sections mit dem Namen "UDATA" oder "BSS" werden in der BSS-Section abgelegt, also nicht mit auf Diskette abgespeichert, sondern beim Start des Programms mit Null initialisiert. -NOSEC : Alle Sections werden in der TEXT-Section abgelegt, so wie das der GST-Linker tut. -WITH [<control>] : Der Linker arbeitet mit dem Controlfile <control>. im Controlfile können folgende Anweisungen stehen: INPUT <filename> : Die Datei <filename> wird in jedem Fall hinzugelinkt. LIBRARY <filename> : Nur benötigte Module aus der Datei <filename> werden hinzugelinkt.

In beiden FĂ€llen wird an die Dateinamen die Extension “.BIN” angehĂ€ngt, falls keine andere vorhanden ist. Die Defaultwerte fĂŒr die Options sind:

-NOLIST -NODEBUG -NOSYM -NOSEC -MEM 100 -BUF 32

Die Defaultfilenamen sind:

Input-file : <module>.BIN Listing-file : <module>.MAP Control-file : <module>.LNK Program-file : <module>.PRG

Der < module >-Dateiname ist also immer notwendig.

Schließlich und endlich nun noch die Fehlermeldungen, die erscheinen können:

  • Error 0: Out of memory Beim Aufruf der malloc-Funktion konnte der gewĂŒnschte Speicher nicht zur VerfĂŒgung gestellt werden. Die einzige Abhilfe wĂ€re hier, den Computer ohne Accessories zu starten, um mehr Speicher zu haben.
  • Error 1: Program memory too small Das Programm ist grĂ¶ĂŸer als 100 Kbyte, beziehungsweise als der mit der SEC Option vereinbarte Speicher. Abhilfe: Mit der -SEC Option mehr Speicher reservieren.
  • Error 2: Error in binary File. Das Objektfile entspricht nicht dem GST-Format. Leider hat der Metacomco-Assembler die Eigenart, unter gewissen UmstĂ€nden fehlerhafte Objektfiles zu produzieren. Im allge meinen hilft es, in bestimmten Datenbereichen (etwa bei Stringkonstanten) zusĂ€tzliche Bytes einzufĂŒgen. Genaueres lĂ€ĂŸt sich hier leider nicht sagen.
  • Error 3: Too many operands in XREF. In einem XREF-Kommando können maximal 10 Symbole in eine Berechnung eingehen, sind es einmal mehr, so erscheint diese Fehlermeldung. In der Praxis dĂŒrfte das allerdings kaum Vorkommen. Trotzdem Abhilfe: Konstante XMAX im Quelltext erhöhen und den Linker neu kompilieren.
  • Error 4: Illegal section id. Im einem Modul ist die Anzahl verschiedener Sections auf 20 begrenzt (sinnvoll sind sowieso nur drei). Sollten es dennoch mehr sein, so erscheint diese Meldung. Abhilfe: Ändern der Konstanten MAX NDEF und neucompilieren des Linkers.
  • Error 5: Illegal symbol id. In einem Modul (nicht im gesamten Programm!) ist die Anzahl verschiedener Symbole auf 500 begrenzt. Sollten es dennoch mehr sein, so erscheint diese Meldung. Abhilfe: Ändern der Konstanten MAXPDEF und neucompilieren des Linkers.
  • Error 6: ORG encountered. Das ORG-Kommando in einem Objektfile wird mangels Bedarf nicht vom Linker unterstĂŒtzt.
  • Error 7: Should not occur Taucht nie auf.
  • Error 8: Word or longword at odd address. Dies ist ein Fehler im Objektfile, fĂŒr den der Compiler oder Assembler verantwortlich ist.
  • Error 9: Buffer too small. Der Puffer, der zum Zwischenspeichern fĂŒr Module aus den Libraries gebraucht wird, ist zu klein. Abhilfe: mit der -BUF Option mehr Speicher reservieren.
  • Error 10: Cannot write file correctly. Das Programmfile kann nicht fehlerfrei geschrieben werden.

Im allgemeinen ist dann kein Platz mehr auf der Diskette. Außer diesen fatalen Fehlern werden natĂŒrlich Undefinierte oder mehrfach definierte Symbole gemeldet, dies fĂŒhrt aber nicht zum Abbruch des Programms.

Sollte das Linken nicht geklappt haben, meldet der Linker dem aufrufenden Programm einen Fehlercode 1, ansonsten Null. Manche Shell-Programme (wie z.B. das “MENU+” von Meta-comco) machen Gebrauch davon.

Ingo Eichenseher

ENDE

/* Linker Version vom 08.01.1988 04:15 */ /* Ingo Eichenseher, Am Leiterle 9, D-8901 Stadtbergen */ #include <stdio.h> #include <osbind.h> #include <string.h> #include <ctype.h> #define FMSIZE 64 #define MAX_LEN 32 #define MAX_PDEF 500 #define MAX_NDEF 20 #define BLEN 1024 #define XMAX 10 #define sgn(x) ((x)<0?-l:((x)==0?0:1)) typedef enum direct { data,source,comment,org,section, offset,xdef,xref, define,common,end,eofsy } DIRECT; typedef char ALFA[MAX_LEN]; typedef struct oper { short id; char op, } OPER; typedef struct Symbol { short length; DIRECT directive; char string[81]; long longword; short id; char trunc_rule; unsigned char data_byte; short n_xref; OPER xref_oper[XMAX]; } SYMBOL; typedef struct mod_item { struct mod_item *mod_next; char mod_name[2]; } MOD_ITEM; typedef struct section { struct section *sec_next; short sec_id; char *sec_start; long sec_length; long sec_oldlen; long sec_fxref; long sec_xptr; MOD_ITEM *sec_module; char sec_name[MAX_LEN]; } SECTION; typedef struct xsym { SECTION *xsy_sect; MOD_ITEM *xsy_mod; long xsy_value; short xsy_defd; struct xsym *xsy__left ; struct xsym *xsy_right; char xsy_name[MAX_LEN]; } XSYMBOL; typedef struct xoper { char xop_oper; char xop_optyp; union { SECTION *xop_sec; XSYMBOL *xop_sym; } xop_ptr; } XOPER; typedef struct xref { struct xref *xref_next; long xref_pos; long xref_abs; short xref_trunc; short xref_ops; struct xoper xref__oper [XMAX] ; } XREF; extern void app_xsy(XSYMBOL* *,XSYMBOL*); extern XSYMBOL* src_xsy(XSYMBOL*,char*); extern void app_sec(SECTION**,SECTION*); extern SECTION* src_sec(SECTION*,char*); extern int read_b(); extern void printsy(); extern void nxsy(); extern void move_up(SECTION*); extern SECTION* def_section(SECTION*,char*,short); extern long calc_xsy(XSYMBOL*); SYMBOL sy; char message[]="68000 GST-Format-Linker Version 2.4 /8.1.1988\n\n"; char module_name[80]; ALFA *pdef_name; ALFA *ndef_name; char input_name [FMSIZE] , file__name [FMSIZE] , control_name[FMSIZE]; char listing_name[FMSIZE],program_name[FMSIZE]; short control_flag,listing_flag,program_flag, debug_flag,symbol_flag; short spar_flag; char *membot,*memtop,*memstart,*memend,*altStart, *code_ptr,*neustart; char *altxref,*debug_start,*debug_end; long mem_size,buf_size; unsigned char *module_buffer,*module_end,*module_ptr, *module_top,*module_max; SECTION *curr_sec,*sec_liste,*moved_sec; SECTION **sec_lptr=&sec_liste; MOD_ITEM *mod_liste,*curr_mod; XSYMBOL *xsy_liste; XREF *xref_liste; FILE *list_file; int undefd_sym,double_sym,range_err; short header[14]={0x60la,0,0,0,0,0,0,0,0,0,0,0,0,0} char *errmsg[]= { "Out of memory", /* 0 */ "Program memory too small", /* 1 */ "Error in binary file", /* 2 */ "Too many operands in XREF", /* 3 */ "Illegal section id", /* 4 */ "Illegal symbol id", /* 5 */ "ORG encountered", /* 6 */ "Should not occur", /* 7 */ "Word or longword at odd address", /* 8 */ "Buffer too small", /* 9 */ "Cannot write file correctly", /* 10 */ }; void halt(n) int n; { printf("Error %2d: %s.\n",n,errmsg[n]); printf ("Press any key to continue\n"); gemdos(1); exit (1); } int strnicmp(x,y,n) register char *x,*y; register unsigned int n; { if (n<1) return(0); while(toupper(*x)==toupper(*y) && *x && *y && -n) { x++; y++; } return((int)(toupper(*x)-toupper(*y))); } void statistic() { SECTION *s; fprintf(list_file, "\n-----------------\n") ; fprintf(list_file,"SECTION START LENGTH\n"); fprintf(list_file, "\n-----------------\n") ; for (s=sec_liste; s!=NULL; s=s->sec_next) fprintf(list_file, "%-9s %8X %8X\n",s->sec_name, s->sec_start-membot,s->sec_length); fprintf (list_file, " \n") ; } MOD_ITEM *app_mod(mod_liste,name) MOD_ITEM **mod_liste; char *name; { MOD_ITEM *new; new=(MOD_ITEM*)malloc(sizeof(MOD_ITEM)+strlen(name)); if (new==NULL) halt(0); strcpy(new->mod_name,name); new->mod_next=*mod_liste; *mod_liste=new; return(new); } void app_xsy(xsy_liste,xsy_neu) XSYMBOL **xsy_liste,*xsy_neu; { int c; if (*xsy_liste==NULL) { *xsy_liste=xsy_neu; xsy_neu->xsy_left=xsy_neu->xsy_right=NULL; } else if ((c=stricmp(xsy_neu->xsy_name, (*xsy_liste)->xsy_name))<0) app_xsy(&(*xsy_liste)->xsy_left,xsy_neu); else app_xsy(&(*xsy_liste)->xsy_right,xsy_neu); } XSYMBOL *src_xsy(xsy_liste,name) XSYMBOL *xsy_liste; char *name; { int c; if (xsy_liste==NULL) return(NULL); if ((c=stricmp(name,xsy_liste->xsy_name))==0) return (xsy_liste); if (c<0) return(src_xsy(xsy_liste->xsy_left,name)); else return(src_xsy(xsy_liste->xsy_right,name)); return(NULL); } long calc_xsy(s) XSYMBOL* s; { long value; value=s->xsy_value; if (s->xsy_sect!=NULL) value+=s->xsy_sect ->sec_start-membot; return(value); } void debug_table(x) XSYMBOL *x; { register char *p; register short i; if (x!=NULL) { if (code_ptr+14>=memtop) halt(l); debug_table(x->xsy_left); p=x->xsy_name; for (i=8; i-;){ *code_ptr++=*p; if (*p) p++; } if (x->xsy_sect!=NULL) *((short*)code_ptr)=0xA200; else *((short*)code_ptr)=0xA000; code_ptr+=2; * ((long*)code_ptr)=calc_xsy(x); code_ptr+=4; debug_table(x->xsy_right); } } void list_xsy (xsy__liste, u_flag) XSYMBOL *xsy_liste; int u_flag; { if (xsy_liste!=NULL) { list_xsy(xsy_liste->xsy_left,u_flag); if (u_flag) { if (!(xsy_liste->xsy_defd&1)) { fprintf(list_file,"Undefined Symbol; '%sr\n", xsy_liste->xsy_name); undefd_sym++; } } else { if (xsy_liste->xsy_defd&l) { fprintf(list_file, "%-20s %08X%c",xsy__liste->xsy_name, calc_xsy(xsy_liste), xsy_liste->xsy_defd&2 ? '':'*'); if (xsy_liste->xsy_sect!=NULL) fprintf(list_file," %15s", xsy_liste->xsy_sect->sec_name); else fprintf(list_file," %20s"," "); if (xsy_liste->xsy_mod!=NULL) fprintf(list_file," %15s", xsy_liste->xsy_mod->mod_name); fprintf(list_file,"\n"); } else fprintf(list_file,"%-20s undefined\n", xsy_liste->xsy_name); } list_xsy(xsy_liste->xsy_right,u_flag); } } void app_sec(sec_liste,sec_neu) SECTION **sec_liste,*sec_neu; { if (*sec_liste==NULL) { *sec_liste=sec_neu; sec_neu->sec_next=NULL; } else if (sec_neu->sec__id<=(*sec__liste)->sec_id) app_sec(&(*sec_liste)->sec_next,sec_neu); else { sec_neu->sec_next=*sec_liste; *sec__liste=sec_neu; } } SECTION* src_sec(sec_liste,name) SECTION *sec_liste; char *name; { while(sec_liste!=NULL) { if (stricmp(sec_liste->sec_name,name)==0) return(sec_liste); sec_liste=sec_liste->sec_next; } return(NULL); } unsigned char inp_buf[BLEN],*buf_ptr,*buf_end; int inp_hnd; int read_b() { if (buf_end-inp_buf<BLEN) return(-1); buf_end=inp_buf+Fread(inp_hnd,BLEN,inp_buf); buf_ptr = inp__buf; return(buf_ptr<buf_end ? (int)*buf_ptr++ : -1); } int get_drct() { return( buf_ptr<buf_end ? (int)*buf_ptr++ : read_b()); } int get_byte() { return(module_ptr<module_end ? (int)*module_ptr++ : get_drct() ); } void nxsy() { int c; if ((c=get_byte())==0xFB) { register char *p; register int i; switch(get_byte()) { case -1 : sy.directive=eofsy; sy.length=0; break; case 0x01 : sy.directive=source; sy.length=0; p=sy.string; for(i=get_byte();i-;) *p++=get_byte(); *p='\0'; break; case 0x02 : sy.directive=comment; sy.length=0; p=sy.string; for(i=get_byte();i-;) *p++=get_byte(); *p='\0’; break; case 0x03 : sy.directive=org; sy.length=O; sy.longword=(get_byte()<<24) + (get_byte () <<16) + (get_byte()<<8)+(get_byte()); break; case 0x04 : sy.directive=section; sy.length=0; sy.id= (get_byte () <<8) + (get_byte ()) break; case 0x05 : sy.directive=offset; sy.length=0; sy.longword= (get_byte () <<24) + (get_byte () <<16) + (get_byte () <<8) + (get_byte () ); break; case 0x06 : sy.directive=xdef; sy.length=0; p=sy.string; for(i=get_byte();i-;) *p++=get_byte(); *p='\0'; sy.longword=(get_byte()<<24)+ (get_byte()<<16)+ (get_byte () <<8) + (get_byte () ) ; sy.id= (get_byte ()<<8) + (get_byte () ) break; case 0x07 : sy.directive=xref; sy.longword= (get_byte () «24) + (get_byte () <<16) + (get_byte () <<8) + (get_byte () ) ; sy.trunc_rule=get_byte(); sy.length=sy.trunc__rule&7; for (i=0; (c=get_byte())!= 0xFB && i<XMAX; i++) { if (c!='+' && c!='-') { printf("Illegal XREF Operator:%c\n",c); halt(2); } sy.xref_oper[i].op=c; sy.xref_oper[i].id= (get_byte()<<8)+(get_byte()); } if (c!=0xFB) halt (3); sy.n_xref=i; break; case 0x10 : sy.directive=define; sy.length=0; sy.id=(get_byte()<<8)+(get_byte()) p=sy.string; for(i=get_byte();i-;) *p++=get_byte(); *p='\0’; break; case 0x12 : sy.directive=common; sy.length=0; sy.id=(get_byte()<<8)+(get_byte()) break; case 0x13 : sy.directive=end; sy.length=0; break; case 0xFB : sy.directive=data; sy.length=1; sy.data_byte=0xFB; break; default : printf("Illegal Directive\n"); halt(2); break; } } else { if (c==-1) { sy.directive=eofsy; sy.length=0; } else { sy.directive=data; sy.data_byte=c; sy.length=1; } } } void move_up(s) SECTION *s; { if (memend!=memtop) halt(-1); moved_sec=s; if (s!=NULL) if (s->sec_start!=NULL) { altstart=memstart; memstart=s->sec_start; memend=memtop-(altstart-merastart); if (altstart>memstart) movmem(memstart,memend, altstart-memstart); } else moved_sec=NULL; } void comment_dir() { fprintf(list_file,"COMMENT: %s\n",sy.string); nxsy(); } void xdef_dir(body_flag) int body_flag; { XSYMBOL *s; if ( (s=src_xsy(xsy_liste,sy.String))==NULL) { if ( (s=(XSYMBOL*)malloc(sizeof(XSYMBOL)))== NULL) halt(0); strupr (sy.String); strncpy(s->xsy_name,sy.string,MAX_LEN-1); s->xsy_defd=0; s->xsy_mod=NULL; app_xsy(&xsy_liste,s); } if (s->xsy_defd&1) { fprintf(list_file,"Double defined Symbol: %s\n", sy.string); double_sym++; } else { if (sy.id) { if (sy.id>0 |f -sy.id>MAX_NDEF) halt(4); if ( (s->xsy_sect=src_sec(sec_liste, ndef_name[-sy.id]))==NULL) { if (body_flag) halt(2); s->xsy_sect=def_section(sec_liste, ndef_name[-sy.id],sy.id); } } else s->xsy_sect-NULL; s->xsy_value = sy.longword; if (s->xsy_sect!=NULL) s->xsy_value += s->xsy_sect->sec_oldlen; s->xsy_defd != 1; s->xsy_mod = curr_mod; } nxsy(); } void define_dir() { strupr(sy.string); if (sy.id>0) { if (sy.id>MAX_PDEF) halt (4); strncpy(pdef_name[sy.id],sy.string,MAX_LEN-1); } else { if (-sy.id>MAX_NDEF) halt(5); strncpy(ndef_name[-sy.id],sy.string,MAX_LEN-1); } nxsy(); } SECTION *def_section(sec_liste,name, id) SECTION *sec_liste; char *name; short id; { SECTION *sec; if (NULL==(sec=(SECTION*)malloc(sizeof(SECTION)))) halt (0); strupr(name); strncpy(sec->sec_name,name,MAX_LEN-1); sec->sec_start = NULL; sec->sec_length = 0; sec->sec_oldlen = 0; sec->sec_id = -1; sec->sec_module = curr_mod; if (!stricmp(sec->sec_name,"DATA"))sec->sec_id=-2; if (!stricmp(sec->sec_name,"BSS"))sec->sec_id=-3; if (!stricmp(sec->sec_name,"UDATA"))sec->sec_id=-3; sec->sec_fxref = NULL; sec->sec_xptr = (long)&sec->sec_fxref; app_sec(sec_lptr,sec); return(sec); } void sec_com_dir() { if (sy.id>=0 || -sy.id>=MAX_NDEF) halt(4); if (NULL==(curr_sec=src_sec(sec_liste, ndef_name[-sy.id]))) { curr_sec=def_section(sec_liste, ndef__name [-sy. id] , sy . id) ; move_up(curr_sec->sec_next); curr_sec->sec_start=memstart; } else { move_up(curr_sec->sec_next); if (curr_sec->sec_start—NULL) curr_sec->sec_start=memstart; } } void section_dir() { sec_com_dir(); code_ptr=neustart=memstart ; nxsy(); } void org_dir() { halt (6); nxsy (); } void common_dir() { sec_com_dir(); neustart=memstart; code_ptr=curr_sec->sec_start; nxsy (); } void data_dir() { if (code_ptr>=memend) halt(1); *code_ptr++=sy.data_byte; nxsy(); } void offset_dir() { if (code_ptr>neustart) neustart=code_ptr; code_ptr=memstart+sy.longword; if (code_ptr>neustart) neustart=code_ptr; nxsy(); } void xref_dir() { XREF *x; XSYMBOL *xsy; SECTION *sec; short i,xid; if ((x=(XREF*)malloc(sizeof(XREF)+(sy.n_xref-XMAX) *sizeof(XOPER)))==NULL) halt (0); if (curr_sec==NULL) halt (2); x->xref_pos = code_ptr-curr_sec->sec_start; x->xref_abs = sy.longword; x->xref_ops = sy.n_xref; x->xref_trunc = sy.trunc_rule; x->xref_next = NULL; for (i=0; i<sy.n_xref; i++) { x->xref_oper[i].xop_dper=sy.xref_oper[i].op; xid=sy.xref_oper[i].id; switch(x->xref_oper[i].xop_optyp=sgn(xid)) { case -1 : if (-xid>MAX_NDEF) halt(4); if ( (sec=src_sec(sec_liste, ndef_name[-xid]))==NULL) sec=def_section(sec_liste, ndef_name[-xid] , xid); x->xref_oper[i].xop_ptr.xop_sec=sec; if (x->xref_oper[i].xop_oper=='+') x->xref_abs += sec->sec_oldlen; else x->xref_abs -= sec->sec_oldlen; break; case 0 : break; case 1 : if ( (xsy=src_xsy(xsy_liste, pdef_name[xid]))==NULL) { if ( (xsy=(XSYMBOL*)malloc(sizeof (XSYMBOL)))==NULL ) halt (0); strncpy(xsy->xsy_name, pdef_name[xid],MAX_LEN-1); xsy->xsy_defd=0; xsy->xsy_mod=NULL; app_xsy(&xsy_liste,xsy); } xsy->xsy_defd |= 2; x->xref_oper[i].xop_ptr.xop_sym=xsy; break; } } *((XREF**)curr_sec->sec_xptr)=x; curr_sec->sec_xptr=(long)&x->xref_next; code_ptr +- sy.trunc_rule & 7; if (code_ptr>=memend) halt(l); nxsy () ; } void header_command() { int in_header_com=1; while(in_header_com) switch (sy.directive) { case comment : comment_dir(); break; case xdef : xdef_dir(0); break; case define : define_dir(); break; default : in_header_com=0; break; } } void section__command () { switch(sy.directive) { case section : section_dir(); break; case org : org_dir(); break; case common : common_dir(); break; default : halt(2); break; } } void body() { while(sy.directive==data || sy.directive==offset || sy.directive==xdef || sy.directive==xref || sy.directive==define || sy.directive==comment ) { switch(sy.directive) { case data : data_dir(); break; case offset : offset_dir(); break; case xdef : xdef_dir(l); break; case xref : xref_dir(); break; case define : define_dir(); break; case comment : comment_dir(); break; default : halt(2); break; } } } void chunk() { SECTION *s; while (sy.directive==xdef || sy.directive==comment || sy.directive==define) header_command(); if (sy.directive==section || sy.directive==org || sy.directive==common ) { section_command(); body(); if ( ((long)code_ptr&l) && code_ptr>=neustart) { if (code_ptr>=memend) halt(1); *code_ptr++='\0'; } if (code_ptr>neustart) neustart=code_ptr; curr_sec->sec_length+=neustart-memstart; if (altstart!=NULL) { if (altstart>memstart) movmem(memend,neustart, altstart-memstart); for(s=moved_sec; s!=NULL; s=s->sec_next) if (s->sec_start!=NULL) s-> sec_start += neustart-memstart; memend=memtop; neustart+=altstart-memstart; altStart=NULL; } memstart=neustart; } } void module() { SECTION *sec; short i; if (sy.directive!=source) halt(2); curr_mod=app_mod(&mod_liste,sy.string); strcpy(module_name,sy.string); nxsy(); while (sy.directive==xdef || sy.directive==comment || sy.directive==define || sy.directive==section || sy.directive==org || sy.directive== common) chunk(); if (sy.directive!=end) halt (2); if (listing_flag) fprintf(list_file,"%-12.12s:", module_name); i=0; for (sec=sec_liste; sec!=NULL; sec=sec->sec_next) { if (listing_flag) { if (i++>=3) { fprintf(list_file, "\n"); i=0; } fprintf(list_file," %8.8s=%08X", sec->sec_name,sec->sec_length-sec-> sec_oldlen); } sec->sec_oldlen=sec->sec_length; } if (listing_flag) fprintf(list_file,"\n"); strcpy(module_name,"NO MODULE"); nxsy(); } void calc_xref(x,c,modname) XREF *x; char *c,*modname; { short i; long value; value = x->xref_abs; c += x->xref_pos; /* printf("XREF at %8X %X",c-membot,value); */ for (i=0; i<x->xref_ops; i++) { /*printf("%c",x->xref_oper[i],xop_oper);*/ switch(x->xref_oper[i].xop_optyp) { case -1 : if (x->xref_oper [i] . xop__oper==' + ' ) value+=x->xref_oper[i].xop_ptr. xop_sec->sec_start -membot; else value-=x->xref_oper[i].xop_ptr. xop_sec->sec_start -membot; /*printf("%s/%x",x->xref_oper[i]. xop_ptr.xop_sec->sec_name, x->xref_oper[i].xop_ptr.xop_sec ->sec_start);*/ break; case 0 : if (x->xref_oper[i].xop_oper=='+') value+=c-membot; else value-=c-membot; printf ("&"); break; case 1 : if (x->xref_oper[i].xop_oper=='+ ') value+=calc_xsy(x->xref_oper[i] .xop_ptr.xop_sym); else value-=calc_xsy(x->xref_oper[i] .xop_ptr.xop_sym); /*printf("%s/%x",x->xref_oper[i] .xop_ptr.xop_syrri->xsy_name, calc_xsy(x->xref_oper[i].xop_ptr .xop_sym));*/ break; } /*printf("\n");*/ }. if (c<membot || c>memstart) halt(2); if (x->xref_trunc & 32) value-=c-membot; switch( x->xref_trunc & 7 ) { case 4 : if (((long)c)& 1) halt(IV; *((long*)c) = value; break; case 2 : if ( ( (long)c)&1) halt(l); *( (short*)c) = value; if (x->xref_trunc & 8) /* Wert hat Vorzeichen */ { If (value<-32768L |I value>32767L) { range_err++; printf("XREF.W-value out of rAnge in module '%s'\n", modname); } } else /* Value ist unsigned */ { if (value>65535L) { range_err++; printf("XREF.UW-value out of Range in module '%s'\n", modname); } } break; case 1 : *c=value; if (x->xref_trunc & 8) /* Wert hat Vorzeichen */ { if (value<-128L I| value>128L) { range_err++; printf("XREF.B-value out of Range in module '%s'\n", modname); } } else /* Value ist unsigned */ { if (value>255L) { range_err++; printf("XREF.UB-value out of Range in module '%s'\n", modname); } } break; default: halt (2); } if ( x->xref_trunc & 64 ) { if (altxref==NULL) { if (code_ptr>=memtop-4) halt(1); altxref=c; * ((long*)code_ptr)=c-membot; code_ptr+=4; } else { while(c-altxref>254) { altxref+=254; if (code_ptr > =memtop) halt(1); *code_ptr++='\001'; } if (code_ptr>=memtop) halt(1); *code_ptr++=c-altxref ; altxref=c; } } } void all_xrefs(sec_liste) SECTION *sec_liste; { XREF *x; while(sec_liste!=NULL) { x=(XREF*)sec__liste->sec_fxref; while(x!=NULL) { calc_xref(x,sec_liste->sec_start, sec_liste->sec_module->mod_name); x=x->xref_next; } sec_liste=sec_liste->sec_next; } if (altxref==NULL) { if (code_ptr>=memtop-8) halt(1); *((long*)code_ptr)=0; code_ptr+=4; *((long*)code_ptr)=0; code_ptr+=4; } else { if (code_ptr>=memtop) halt(1); *code_ptr++='\0’; } } void init_mem() { pdef_name=(ALFA*)raalloc(MAX_PDEF*sizeof(ALFA)); ndef_name=(ALFA*)malloc(MAX_NDEF*sizeof(ALFA)); membot=(char*)raalloc(mem_size); module_buffer= (char*) malloc (buf__size); if ( membot==NULL || module_buffer==NULL II ndef_name==NULL || pdef_name==NULL ) halt{0); memtop=membot +mem_size; memstart=membot; memend=memtop; altStart=NULL; module_top=module_buffer+buf_size; module_max=module_ptr=module_end=module_buffer; } void make_ext(make_name,name,ext) char *make_name,*name, *ext; { char oldext[FMSIZE]; stcgfe(oldext,name); if (!*oldext) strmfe(make_name,name,ext); else strcpy(make_name,name); } void comraand_line(a rgc,a rgv) int argc; char *argv[]; { int i=2; int x; if (argc<2) { printf("No Filename specified\n"); exit(1); } strcpy(file_name,argv[1]); strmfe(input_name,file_name,"BIN"); strmfe(listing_name,file_name,"MAP"); strmfe(control_name,file_name,"LNK"); strmfe (program_name, file_name, "PRG") ; listing_flag=0; control_flag=0; program_flag=1; debug_flag=0; symbol_flag=0; spar_flag=0; i=2; if (argc>i) if (*argv[i]!='-') { make_ext(control_name,argv[i++],"LNK"); control_flag=1; } if (argc>i) if (*argv[i]!='-') { make_ext(listing_name,argv[i++],"MAP"); listing_flag=1; } if (argc>i) if (*argv[i] ) make_ext (program_name,argv[i++],"PRG"); for (; i<argc; i++) { if (!stricmp(argv[i] , "-NOLIST")) { listing_flag=0; continue; } if (!stricmp(argv[i],"-NODEBUG")) { debug_flag=0;continue; } if (!stricmp(argv[i],"-NOPROG")) { program_flag=0; continue; } if (!stricmp(argv[i],"-DEBUG")) { debug_flag=1;continue; } if (!stricmp(argv[i],"-SYM")) { symbol_flag=1; continue; } if (!stricmp(argv[i],"-NOSYM")) { symbol_flag=0; continue; } if (!stricmp(argv[i] , "-SEC")) { spar_flag=1;continue; } if (!stricmp(argv[i], "-NOSEC")) { spar_flag=0;continue; } if (!stricmp(argv[i],"-WITH")) { if (i+l<argc) if (*argv[i+1]!='-') strcpy(control_name,argv[++i]); control_flag=1; continue; } if (!stricmp(argv[i],"-LIST")) { if (i+1<argc) if (*argv[i+1]!='-') strcpy(listing_name,argv[++i]); listing_flag=1; continue; } if (!stricmp(argv[i],"-PROG")) { if (i+1<argc) if (*argv[i+l] ! ='-') strcpy(program_name,argv[++i]); program_flag=1; continue; } if (!stricmp(argv[i],"-MEM")) { if (i + 1<argc) if (*argv[i+l]!='-') { x=atoi(argv[++i]); if (x>2 && x<800) mem_size=x*1024; } continue; } if (!stricmp(argv[i] , "-BUF") ) { if (i+1<argc) if (*argv[i+1]!='-') { x=atoi(argv[++i]); if (x>2 && x<800) buf_size=x*1024; } continue; } printf("Invalid Option:'%s'\n",argv[i]); } } int test_module() { register int c; int end_test=0; int result=0; register short i; char string[80]; register char *p; XSYMBOL *s; module_end=module_ptr=modu1e_buffer; do { c=get_drct(); if (c<0) halt(2); if (module_end+128>=module_top) halt (9); *module_end++=c; if (c==0xFB) { c=get_drct(); *module_end++=c; switch(c) { case -1 : halt(2); break; case 0xFB : break; case 0x01 : case 0x02 : i=get_drct(); *module_end++=i; while(i-) *module_end++= get_drct(); break; case 0x03 : case 0x05 : *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); break; case 0x12 : case 0x04 : *module_end++=get_drct(); *module_end++=get_drct(); break; case 0x06 : p=string; i=get_drct(); *module_end++=i; while(i—) {*p=get_drct(); *module_end++= *P++; } *p='\0’; *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); s=src_xsy (xsy_liste, string); if (s!=NULL) if (!(s->xsy_defd&l)) end_test=result=l; break; case 0x07 : *module_end++=get_drct() ; *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); *module_end++=get_drct(); while(1) { c=get_drct(); *module_end++=c; if (c==0xFB | | c==-1) break; *module_end++=get_drct(); *module_end++=get_drct () ; } break; case 0x10 : *module_end++=:get_drct () ; *module_end++=get_drct(); i=get_drct(); *module_end++=i; while(i—) *module_end++= get_drct(); break; case 0x13 : end__test=1; break; default : halt(2); } } /* if 0xFB */ } while(!end_test); if (module_end>module_max) module_max=module_end; return(result); } void link_file(name,lib_mode) char *name; int lib_mode; ( inp_hnd=Fopen(name,0); buf_ptr=buf_end= inp_buf+BLEN; strcpy(module_name,"*NO MODULE"); if (inp_hnd<0) { printf("Cannot open binary file: '%s'\n",name); exit(1); } nxsy(); /* SOURCE oder EOFSY */ while(sy.directive!=eofsy) { if (lib_mode) { if {!test_module()) { module_end=module_ptr=module_buffer; ' nxsy(); } else module(); } else module(); } Fclose (inp__hnd) ; } void write_prog() { int handle; int n,h; SECTION *sec; char *start,*endcode; if ( (handle=Fcreate(program_name, 0))<0 ) { printf("Cannot open %s for write\n"); halt(10); } *((long*)sheader[7])=debug_end-memstart; n=Fwrite(handle,28,header); if (n!=28) halt(10); if (spar_flag) { h=1; sec=sec_liste; start=membot; while(sec->sec_id>-2 && sec->sec_next!=NULL) sec= sec->sec_next; if (sec->sec__id==-2) /* TEXT-Section schreiben */ { endcode = sec->sec_start; *((long*)&header[h])=endcode-start; if (endcode-Start) { n=Fwrite(handle,endcode-start,Start); if (n!=endcode-start) halt(10); } h=3; /* next SECTION * / start=endcode; /* neuer Start fĂŒr den Rest */ } while(sec->sec_id>-3 && sec->sec_next!=NULL) sec= sec->sec_next; if (sec->sec_id==-3) { /* BSS gefunden */ endcode = sec->sec_start; { *((long*)&header[h])=endcode-start; if (endcode-start) { n=Fwrite(handle,endcode-start, Start); if (n!=endcode-start) halt (10); } } h=5; /* next SECTION */ start=endcode; /* neuer Start fĂŒr den Rest */ } /* Rest in (h)-Section schreiben */ endcode=debug_start; *((long*)&header[h])=endcode-start; if (h<5) /* BSS-Section wird nicht geschrieben */ { if (endcode-start) { n=Fwrite(handle,endcode-start,Start); if (n!=endcode-start) halt (10); } } } else /* also nicht sparen */ { start=membot; endcode=debug_start; *((long*)&header[1])=debug_start-membot; if (endcode-start) { n=Fwrite(handle,endcode-start,Start); if (n!=endcode-start) halt (10); } } /* Symboltabelle und Relocation Table schreiben */ * ( (long*)Sheader[7] )=debug_end-debug_start; start=debug_start; endcode=code_ptr; if (endcode-start) { n=Fwrite(handle,endcode-start, Start); if (n!=endcode-start) halt(10); } Fseek(0,handle,0); n=Fwrite(handle,28,header); if (n!=28) halt(10); if (Fclose(handle)<0) halt(10); } void main(argc,argv) int argc; char *argv[]; { char line[80],*name; FILE *fp; int lib_mode; int err_code=0; printf(message); mem_size=100*1024; buf_size=32*1024; double_sym=undefd_sym=range_err=0; command_line(argc,argv); mod_liste=NULL; list_file=stdout; if (listing_flag) { list_file=fopen(listing_name,"w"); if (list_file==NULL) { printf("Cannot open list-file '%s7 for write\n"); exit(1); } if (list_file!=stdout) fprintf(list_file,message) } init_mem(); sec_liste=curr_sec=moved_sec:=NULL; xsy_liste=NULL; xref_liste=NULL; altxref=NULL; if (control_flag) { fp=fopen(control_name,"r"); if (fp==NULL) { printf("Cannot open control-file:'%s'\n", control_name); exit (1); } while(!feof(fp)) ( if ((name=fgets(line,80,fp))==NULL) *line='\0' for (name=line; *name!='\n' && *name; name++); *name='\0'; if (!*line) continue; if (*line=='*') continue; name=NULL; lib_mode=0; if (!strnicmp(line,"INPUT",5)) name=line+5; else if (!strnicmp(line,"LIBRARY",7)) { name=line+7; lib_mode=1; } if (name==NULL) printf("Invalid control-line: %s\n",line); else { name=stpblk(name); if (*name=='*') strmfe(name,file_name, "BIN"); else make_ext(name,name,"BIN"); link_file(name,libjnode); } ) fclose(fp); } else link_file(input_name,0); code_ptr=memstart ; debug_start=code_ptr; if (debug_flag) debug_table (xsy_liste); debug_end=code_ptr; all_xrefs(sec_liste); statistic (); fprintf(list_file,"Program length - %8X\n", memstart-membot); fprintf(list_file,"Symbol Table = %8X\n", debug_end-memstart); fprintf(list_file,"Relocation Table = %8X\n", code_ptr-debug_end); fprintf (list_file, "----------------------- \n"); fprintf(list_file, "Memory Usage = %7d%%\n", (code_ptr-membot)*100/mem_size); fprintf(list_file, "Buffer Usage = %7d%%\n", (module max-module_buffer)*100/buf_size); fprintf(list_file, "------------------------ \n"); list__xsy (xsy_liste, 1) ; if (program_flag) write_prog(); if (symbol_ flag) { fprintf(list_file,"\nSymbol Table:\n"); fprintf {list file, "------------------- \n") ; list_xsy(xsy_liste,0); fprintf(list_file,"\n"); } if (undefd_ sym) { if (list_file ! = stdout) fprintf(list__file,"Undefined Symbols:%8d\n", undefd_sym); printf("Undefined Symbols:%8d\n",undefd_sym); err_code=1; } if (double_sym) ( if (list_file!=stdout) fprintf(list_file,"Multiply defined :%8d\n", double._sym) ; printf("Multiply defined :%8d\n",double_sym); err_code=1; } if (range_err) { if (list_file!=stdout) fprintf(list_file,"Range errors :%8d\n", range_err); printf("Range errors :%8d\n",range_err) ; err_code=1; } printf("\nLink completed\n"); if (list_file!=stdout) fprintf(list_file, "\nLink completed\n"); if (listing flag) fclose(fp); exit(err_code); }