###   Projekte und Informationen rund um den KC85   ### 

Neue Speicherverwaltung - neuer Standard?

von Jörg Linder

Der ungenutzte Speicher im Grundgerät während der Arbeit unter MicroDOS bzw. CP/M hat schon so manchen Programmierer gereizt. Dabei gab es durchaus auch die eine oder andere Rangelei zwischen Programmteilen unterschiedlichen Ursprungs. Inzwischen hat sich zwar ZAS als Quasi-Standard behauptet und wird von vielen KC-Usern verwendet, aber grundsätzlich ist man nicht vor Komplikationen gefeit.

Eine für alle Programmierer / Programme einvernehmliche Lösung müßte also her. Dafür stehen mehrere Möglichkeiten zur Verfügung. So könnte z. B. der freie Speicherplatz durch die Anzahl der aktiven Programmierer geteilt werden. Als Ergebnis erhielte man die verfügbaren Bytes pro Kopf. Was daraus gemacht wird, wäre jedem selbst überlassen.

Etwas ernster hat Dirk Walther die Sache betrachtet und folgende Forderungen aufgestellt: Jeder Programmierer hat den Benutzer darüber zu informieren, welche(n) Speicherbereich(e) sein Programm verwendet. Über die Kommandozeile sollte die Basisadresse zu beeinflussen sein. Nach seinen Vorstellungen wären Programme / Treiber optimal, wenn sie frei verschiebbar (also relokatibel) wären.

Letzteres ist bei größeren Projekten kaum realisierbar. Wird das Programm bzw. der Treiber aber vom D004 in das Grundgerät "gepokt", so ließen sich erst beim Schreiben die Sprung- und Zeigeradressen eintragen. Erst zu diesem Zeitpunkt würde der Code also auf eine bestimmte (Basis)adresse gelinkt.

In der Entwicklungsphase des neuen Betriebssystems standen wir vor einem größeren Problem. Man konnte die drei Systembestandteile CCP, BDOS und BIOS nicht in einem Schritt linken, da die Basisadresse des Codes sich erst aus der Größe des gelinkten Codes ergab. Sowohl für Anwender als auch Linker eine schier unlösbare Aufgabe. Woher sollte man vorher wissen wie groß das Ganze wird? Welche Basisadresse könnte schätzungsweise die richtige sein?

Bei meiner (verschwindend geringen) Zuarbeit für Mario Leubner bin ich über ein besonderes Ausgabeformat des Linkers gestolpert. Hierbei werden zwar alle Segmente zusammengefügt, aber es steht noch keine feste Basisadresse fest. Es handelt sich also um eine Art "Zwischencode", der schon fast fertig ist. In diesem Zusammenhang sind mir wieder Dirk Walther's Ansätze eingefallen. Warum sollte man sich dieses Format nicht zunutze machen?

Das Zauberwort heißt "PRL". Diese Abkürzung steht für "Page ReLocatable" und ist gleichzeitig auch der Dateityp einer solchen Datei. Ein derartig gelinktes Programm hat einen 256 Byte langen Header, dem der Code mit einem originalen Offset von 0100h (also für ein normales CP/M-Programm) folgt. Anschließend ist eine Bit Map gespeichert, die ein Achtel der Codelänge umfaßt.

Diese Bit Map ist eine Kette von Bits, mit denen angezeigt wird, welches Byte des Codes noch reloziert werden muß. Für jedes Byte des Codes steht ein Bit zur Verfügung, also entspricht ein Byte der Bit Map 8 Codebytes. Das höchstwertigste Bit (Bit 7) des ersten Bytes der Bit Map bestimmt, ob das erste Byte des Codes reloziert werden muß. Ist das Bit gesetzt, so muß das Codebyte reloziert werden. Das nächste Bit (Bit 6) bezieht sich demzufolge auf das zweite Codebyte usw.

Eine PRL-Datei ist folgendermaßen aufgebaut:

0001 - 0002h Programmgröße, (low, high)
0004 - 0005h zusätzlich benötigter Speicher (im Normalfall leer)
0006 - 00FFh momentan unbenutzt, reserviert für zukünftige Erweiterungen
0100 + Programmgröße Beginn der Bit Map

Bei der Verwendung von PRL-Dateien sind zwei Dinge zu beachten. Zum einen können derartige Dateien immer nur auf eine volle Page reloziert werden, das untere Byte der Basisadresse ist also immer 00h. Zum anderen stehen die Informationen innerhalb der PRL-Datei nach dem Header nicht mehr Page-aligned, so daß die Bit Map z. B. bei 053Bh beginnen könnte.

Jetzt sind die Forderungen von Dirk Walther eigentlich ganz einfach zu erfüllen. Es werden nur noch ein paar Zeilen Assembler benötigt, um die PRL-Datei auf eine bestimmte Adresse zu linken. Auch größere Projekte lassen sich verwirklichen. Einige Routinen aus der SYSLIB mit den eigenen zu einer PRL-Datei verbinden - fertig! Doch bevor jetzt jeder wie wild drauflos programmiert und sich am Ende wieder alles im Grundgeräte-RAM bekriegt, hier der zweite Teil meiner Überlegungen.

Ein Programm oder Treiber darf sich nicht länger irgendwo im Speicher breit machen. Es muß sich korrekt einordnen. Dazu muß aber jemand (oder etwas) Regie führen. Grundgedanke des Systems ist die Zuteilung des/der freien Bereiche(s) durch ZAS. Diese Software-Erweiterung hat sich durchgesetzt und stellt meines Erachtens eine solide Basis für zukünftige Erweiterungen dar.

Soll ein Treiber oder eine Systemerweiterung in das Grundgerät übertragen werden, so muß eine dementsprechende PRL-Datei vorliegen. Ein Installationsprogramm beantragt die Anzahl der benötigten Pages (256 Byte Blöcke) bei ZAS. Dieses überprüft, ob noch genügend zusammenhängender Speicher verfügbar ist. Im positiven Fall gibt ZAS an das Installationsprogramm die Basisadresse zurück. Erst dann wird der Code gelinkt und übertragen.

Momentan "weiß" nur das Installationsprogramm, ob und wo sich das übertragene Programm im Speicher befindet. Ein Programm, welches davon Gebrauch machen will, hat aber überhaupt keine Ahnung davon. Weil sich der übertragene Programmcode überall und nirgends befinden könnte, muß noch ein Verfahren gefunden werden, das die Basisadresse zurückgibt. Daher ist die Einführung eines Identifizierungscodes (kurz: ID) für jeden Treiber notwendig.

Eigentlich sollte ein Byte dafür genügen. Verschiedene Versionen einer Software können stets gleiche IDs bekommen. Seitens eines Programmierers (ich will ja keine Namen nennen :-) kam jedoch die dringende Bitte, zwei Bytes vorzusehen. Eines als ID für die Software und das andere als ID für den Programmierer. (Type & Creator - schöne Grüße aus der Apple-Kiste) Welche Variante letztendlich Anwendung findet, ist aber zur Zeit unerheblich.

Doch nun weiter zur Funktionsweise. Möchte ein MicroDOS- oder CP/M-Programm einen speziellen Treiber im Grundgeräte-RAM nutzen, muß zunächst der ID an ZAS übermittelt werden, um von dort die Basisadresse zu erhalten. Einmal festgestellt, kann diese Adresse in einer Arbeitszelle hinterlegt und für weitere Operationen benutzt werden. Genaue Spezifikationen für die Treiber stehen augenblicklich noch nicht fest. Auf alle Fälle wird aber (analog zu den Drucker- und Koppeltreibern) am Anfang des Codes ein Programmverteiler stehen. Aus meiner Sicht sollten folgende Funktionen unterstützt werden: Initialisierungsroutine, Abfrage der Versionsnummer und Rückgabe einer kurzen Zeichenkette (mit Realname, Copyright u. ä.).

Wie man Sätzen mit den Worten "momentan", "zur Zeit" oder "augenblicklich" entnehmen kann, handelt es sich bei der hier vorgestellten Lösung eigentlich nur um einen Lösungsansatz. Derzeit enthält weder ZAS die notwendige Unterstützung, noch gibt es ordentliche Standards. Ein paar Ideen zur Nutzung sind allerdings schon vorhanden. Deshalb soll dieser Beitrag auch "nur" eine Diskussionsgrundlage bilden. Wer also etwas dazu zu sagen hat, der möge dies gern hier in den KC-News tun - jede Meinung zählt!

Besonders die folgenden Problemchen bedürfen noch einer Lösung:: Völlig ungeklärt ist zum Beispiel, wie man Programmcode aus einer Hochsprache installiert! Wichtiger ist jedoch, Kompatibilität zu bereits vorhandener Software zu erhalten. Eventuell sollten die Pages von ZAS in absteigender Folge vergeben werden, so daß der Bereich ab 4000h möglichst (lange) frei bleibt!? Ideen, wie ESC-Sequenzen derart installierter Treiber unterstützt werden können, sind ebenfalls herzlich willkommen!

Ich gehe davon aus, daß wir vielleicht schon beim nächsten Clubtreffen klüger sind und einen neuen Standard definieren können. Die großen Speicherkriege mit zahllosen verletzten Bytes sollten dann der Vergangenheit angehören. Übrigens, eine PRL-Datei auf eine bestimmte Adresse zu linken, ist ganz einfach - sogar ich hab's geschafft. Den Beweis in Assembler trete ich in der Programmierecke dieser Ausgabe an.

Zusammenfassung:

Treiber installieren

  • benötigte Pages an ZAS übermitteln
  • Rückmeldung: Startadresse oder Fehler
  • Treiber relozieren und in Grundgerät übertragen
  • Treiber initialisieren
  • ID-Code(s) an ZAS übergeben

Treiber deinstallieren

  • ID-Code(s) an ZAS übergeben
  • Löschbefehl erteilen
  • ZAS korrigiert Belegungsvektor und Tabelle

notwendige Routinen in ZAS

  • Feststellung freie Pages/Belegung
  • Belegungsvektor nach De-/Installation aktualisieren
  • ID-Code(s) eintragen/löschen

von ZAS verwaltete Tabellen

  • 8 Bytes Belegungsvektor (Bereich 4000 - 7FFFh = 64 Pages = 8 Bytes)
  • je Treiber 1/2 Byte(s) ID-Code(s), 1 Byte Startpage, 1 Byte Länge (Anzahl Pages), evtl. 1 Byte ESC-Code