Vom Quelltext zum Programm

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:

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 OxFB */
	} 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);
}


Aus: ST-Computer 03 / 1988, Seite 35

Links

Copyright-Bestimmungen: siehe Über diese Seite