Build your OS – Der Regnatix-Code – Seite 3

5. Der Loader

Was haben wir jetzt, machen wir eine kurze Bestandsaufnahme:

  • Wir haben ein Busprotokoll, mit welchem die drei Propellerchips miteinander kommunizieren können; dazu passend Routinen in Bellatrix und Regnatix.
  • Wir haben ein BIOS für Bellatrix, welches erweiterbar Funktionen der Benutzerschnittstelle über den Bus zur Verfügung gestellt. Implementiert sind momentan Texteingabe und Ausgabefunktionen.
  • Wir haben ein Bibliothek um alle Systemfunktionen in Regnatix-Anwendungen übersichtlich und einfach nutzen zu können.
  • Wir haben einen einfachen und erweiterbaren CLI, um Funktionen interaktiv aufzurufen und mit Parametern zu steuern.

Und was fehlt uns? Richtig, wir wollen endlich auch externe Kommandos und Programme von unserem Laufwerk starten. Dafür brauchen wir eine flexiblere Datenträgerverwaltung, ein Dateisystem und eine universellere Möglichkeit, um unseren compilierten SPIN-Code zu starten. Bis jetzt war unser „Datenträger“ der EEProm am Propeller. Nur von dort wurde beim Systemstart genau ein Programm geladen und gestartet. Es gibt zwar prinzipiell auch die Möglichkeit, bei Verwendung von EEProms die größer als 32 KByte sind, quasi mehrere Programme zu speichern und auch zu aktivieren, aber das ist eine ziemlich fummlige Angelegenheit. Mit SD-Card können wir momentan mit dem Hive auf ein Datenvolumen von 2GByte zugreifen, was bei Programmgrößen die sich im Bereich von KBytes bewegen, schon beachtlich ist. Auch die Möglichkeit Daten mit anderen Computern auszutauschen, ist durch die Möglichkeit der Verwendung von Fat16  und einem universellen Datenträger schon fast traumhaft, wenn man bedenkt das der Hive ja „Retro Style“ ist – schließlich hätte ich auch der Idee verfallen können ein Kasetteninterface zu integrieren… 🙂 Aber diesen Wünschen stehen auch einige Hindernisse im Weg, die es erst zu überwinden gilt:

  • SD-Laufwerk & Administra: Das ist unser kleinstes Problem. 😉 Durch unsere Erfahrungen mit Bellatrix können wir nun ganz nebenbei ein BIOS für Administra entwickeln, mit SD-Laufwerksfunktionen, die über den Bus zur Verfügung gestellt werden. Ein Softwareobjekt (fsrw) zur Ansteuerung einer SD-Card mit dem Dateisystem FAT16 steht fertig im OBEX dafür zur Verfügung. Ein wenig Spincode, einige neue Routinen im SIOS und wir haben Datei- und Laufwerksfunktionen zur Verfügung. Das Administra-BIOS ist also mehr eine Fleißaufgabe.
  • Komplexes Objektformat: Momentan wird unser Programm automatisch aus dem EEProm in den hRam geladen und gestartet. Das ist natürlich für die ersten Experimente absolut easy und auch eine wunderbare Lösung um mit einem einzelnen Propeller umzugehen. Aber wie laden wir eine compilierte SPIN-Binary-Datei in unseren hRAM und starten eine COG mit dem darin enthaltenen SPIN-Code? Ein Problem mit dem wir uns beschäftigen müssen um externe Kommandos von einem Datenträger zu starten. Ist die BIN-Datei relokatibel, also läuft sie auf jeder beliebigen Adresse im Ram, oder muß sie erst zur Laufzeit an diese Adresse „gelinkt“ werden?
  • Autostart: Unser Kommandozeilenprogramm ist auch nur eine normale SPIN-Binary – also wollen wir diese doch, wie bei vielen Betriebssystemen, beim booten des Systems laden und starten.

Wir brauchen also einen Loader! Und da wir ja genug Prozessoren haben, wollen wir einen Prozessor mit dieser Aufgabe betrauen und den Code für den Loader packen wir ganz normal in den EEProm – so wird er ganz komfortabel beim Systemstart aktiviert. Dieser Loader muß natürlich so klein wie möglich sein, da wir ja von unserem hRam nicht mehr als nötig verschwenden wollen. Die ideale Lösung wäre ein PASM-Loader, aber in unserem System wollen wir ja außdrücklich bei SPIN bleiben. Dennoch soll diese Möglichkeit nicht unerwähnt bleiben. Wie würde eine Lösung in Assembler aussehen? Aus dem EEProm wird dabei ein SPIN-Code geladen und gestartet, welcher wieder den Start einer neuen COG mit dem PASM-Maschinencode – dem eigentlichen Loader – initialisiert. Danach beendet sich der SPIN-Code und auch die COG mit dem SPIN-Tokeninterpreter wird schlafen gelegt. Das einzige was nun noch läuft ist unsere COG mit dem Loader in Maschinencode, welcher keinen Platz im hRAM belegt! Dieses Subsystem hat jetzt die volle Kontrolle über den ganzen Regnatixchip – und das ohne selbst mehr Ressourcen als nur diese eine COG zu belegen. Unsere SPIN-Lösung wird dabei nicht ganz so sparsam, aber dafür sehr einfach zu verstehen, da komplett in SPIN programmiert. Real belegt der SPIN-Loader im TriOS 173 Longs, der freie Bereich für die eigenen Anwendungen im hRAM beträgt 31900 Bytes, also über 31 KByte – ich denke das ist trotz der Verwendung von reinem SPIN-Code nicht sehr verschwenderisch.

Was muss unser Loader können?

  1. Der Loader muß eine BIN-Datei vom Datenträger in den hRam laden und einen SPIN-Tokenprozessor in einer weiteren COG starten, welche den geladenen SPIN-Code abarbeitet.
  2. Der Loader muß seine Funktion dem geladenen SPIN-Programm zur Verfügung stellen, damit diese Anwendung andere Programme starten kann.

Wir wollen den Loader in zwei Schritten realisieren. Als erstes soll der Loader eine beliebige SPIN-BIN laden und starten. Dafür bietet sich gleich der automatische Startvorgang an, den wir eh integrieren wollen. Lassen wie einfach eine Datei „start.bin“ laden, speichern unsere CLI unter diesem Namen auf SD-Card und unsere Mini-CLI sollte automatisch vom Datenträger starten.

Im zweiten Schritt bauen wir die Kommunikation zwischen der Anwendung und dem Loader ein – so kann dann jedes Programm ein beliebiges anderes Programm vom Laufwerk starten. Nun, genug der Pläne, gönnen wir uns einfach diesen Spaß.

Unser Bootloader:

[004-bel-bios.spin] —> Bellatrix-BIOS
[004-adm-bios.spin] —> Administra-BIOS
[004-reg-loader.spin] —> Regnatix-Loader

[start.bin] —> Unsere CLI [004-reg-cli.spin], compiliert und auf SD-Card gespeichert

Die Datei „start.bin“ compilieren wir aus unserem SPIN-Quelltext „004-reg-cli.spin“. Dazu benutzen wir das erste mal bei unseren Experimenten die Funktion „View Info“ – F8, um unseren compilierten Code als Binärdatei („Save Binary File“) abzuspeichern. Da wir auf unserem Laufwerk mit dem fsrw-Treiber Fat16 als Dateisystem haben, müssen wir die Datei auch im 8+3 Dateiformat speichern – in unsrem Fall als „start.bin“.

Link Wikipedia: „File Allocation Table“

Der Startvorgang läuft in zwei Phasen ab: In Phase 1 starten alle Propellerchips ihr BIOS, im Fall von Regnatix ist das BIOS unser Loader. In Phase 2 stellen Administra und Bellatrix ihre BIOS-Funktionen zur Verfügung. Der Loader aber öffnet die Datei „start.bin“ und kopiert diese, nach einer Analyse des Headers, in den Heap – eine Bytefeldvariable mit maximaler Größe. Ist die Datei geladen, wird eine neue COG mit dem Tokeninterpreter aus dem ROM gestartet, um das SPIN-Objekt in unserem Heap zu interpretieren.

6. Dateiverwaltung

Unser neues BIOS in Administra stellt dabei folgende Funktionen zur Verfügung:

PUB sd_open  | err,modus,len,i                          'sd: datei öffnen
PUB sd_close | err                                      'sd: datei schließen
PUB sd_getc | n                                         'sd: zeichen aus datei lesen
PUB sd_eof                                              'sd: abfragen ob eof erreicht ist
PUB sd_putc                                             'sd: zeichen in datei schreiben
PUB sd_getblk | len,i                                   'sd: block aus datei lesen
PUB sd_putblk | len,i                                   'sd: block in datei schreiben
PUB sd_nextfile | flag,i,len                            'sd: nächsten eintrag aus verzeichnis holen
PUB sd_opendir                                          'sd: verzeichnis öffnen
PUB sd_mount | err                                      'sd: sd-card mounten

Mit diesen IOS-Funktionen können wir ersten Kommandos zur Dateiverwaltung in unseren Kommandozeileninterpreter einbauen.  An diesen Kommandos (dir, type), ist weiter nichts besonderes – jeder hat sicher schon mit ähnlichen Funktionen in den verschiedensten Programmiersprachen gearbeitet. Deshalb werde ich diesen Part nicht weiter erläutern, der Quelltext ist relativ gut kommentiert.

[005-bel-bios.spin] —> Bellatrix-BIOS
[005-adm-bios.spin] —> Administra-BIOS
[005-reg-loader.spin] —> Regnatix-Loader

[start.bin] —> Unsere CLI (005-reg-cli.spin), compiliert und auf SD-Card gespeichert

7. Anwendungen

Wir nähern uns dem Ende unseres Tutorials, denn es fehlt nur noch eine Funktion. Erinnern wir uns und schauen, welchen Plan wir im ersten Teil „Bellatrix-Code“ hatten:

Letztlich muss unser einfaches Betriebssystem eine minimal Schnittstelle zu folgenden Funktionen realisieren:
	1. Textanzeige auf einem VGA-Monitor oder einem TV.
	2. Texteingabe über Tastatur.
	3. Zugriff auf Dateien auf SD-Card als Massenspeicher.
	4. Starten von Programmen.

Die ersten drei Punkte haben wir prinzipiell realisiert. Was uns noch fehlt ist eine Möglichkeit um Anwendungen zu starten. Aber so fremd ist uns das ja gar nicht mehr, denn wir starten während des Bootvorgangs schon eine Anwendung – unsere CLI in der Datei „start.bin“! Im Kern haben wir also schon was wir wollen, wir können es nur noch nicht aus unserer Anwendung selbst heraus steuern, was bedeutet, dass wir eine Kommunikation zwischen unserer Anwendung und dem Loader brauchen.

Für diesen Mechanismus nutzt unser neuer Loader die Variablen „lflag“ und „lname“. Nachdem der Loader die Datei „start.bin“ gestartet hat, geht er in einen Kommandomodus. In diesem Modus wartet er auf ein gesetztes „lflag“, um dannach alle anderen COG’s zu beenden und das Programm mit dem Namen in der Variable „lname“ zu starten. Unsere Anwendung braucht also nur den Namen der zu startenden Datei in die Variable zu speichern und das Loaderflag setzen – schon wird unser Programm vom Loader beendet und die neue Anwendung wird gestartet.

Das klingt ganz gut, aber wir haben dabei ein Problem: Der Loader und die Anwendung sind zwei völlig getrennte Objekte und können nicht einfach gegenseitig auf ihre Variablen zugreifen, denn unsere Anwendung ist ja ein SPIN-Objekt in der Variable (Heap) eines anderen SPIN-Objektes (Loader)… 🙂 Jaja, ist schon klar – total hirnverdreht, aber es funktioniert. Man könnte natürlich auch vom Loader aus die entsprechenden Zeiger bis zum Variablenbereich in dem geladenen Objekt im Heap „entflechten“, aber ich habe eine einfachere Variante gewählt, weil ich einfache Lösungen mag: Wir legen uns einfach eine globale Variable im externen RAM an, in welcher wir die Adresse unseres Loaderflags speichern. Die globalen Variablen legen wir in die oberste Adresse im eRAM, weitere Variablen können dann nach unten wachsend folgen. Unsere Anwendung nun kann über die Adresse in der globalen Variable auf das Loaderflag und den Stringpuffer dem Loader ein Signal zum Laden neuer Programme geben.

Die Ansteuerung der externen Rambänke wollen wir auf einer gesonderten Seite betrachten: „Externer RAM“

Also auf zum fröhlichen experimentieren:

[006-bel-bios.spin] —> Bellatrix-BIOS
[006-adm-bios.spin] —> Administra-BIOS
[006-reg-loader.spin] —> Regnatix-Loader

[start.bin] —> Unsere CLI [006-reg-cli.spin], compiliert und auf SD-Card gespeichert

Als Test brauchen wir noch zwei Anwendungen. Dafür verwende wir unser obligatorisches „Hallo Welt!“-Programm und ein kleinen Test um die Parameterübergabe zu prüfen. Diese Parameterübergabe ist nicht mit dem Kommando „para“ aus Abschnitt 4 zu verwechseln! Externe und interne Kommandos verwenden verschiedene Mechanismen um ihre Parameter zu übergeben. Wer sich den Quelltext anschaut wird feststellen, dass in unserer IOS einige neue Routinen hinzugekommen sind, unter anderem auch die Routinen um Parameter zu übergeben. Für die Übergabe werden diese der Einfachheit wegen ebenfalls im externen RAM gespeichert.

[hallo.bin] –> [006-reg-hallo.spin], compiliert und auf SD-Card gespeichert
[para.bin] -> [006-reg-para.spin], compiliert und auf SD-Card gespeichert

OBJ
        sios    : "007-reg-sios"                       

PUB main | key

  sios.start
  sios.print(string("Hallo Welt!"))
  sios.stop

Also ich finde unser kleines Hallo-Demo sieht doch ganz symphatisch und übersichtlich aus! Und mehr brauchen wir auch nicht um umfangreichere Programme für unser Mini-OS zu schreiben – IOS einbinden, IOS initialisieren und wenn fertig per IOS die Anwendung beenden. Kurz und schmerzlos.

Wie man aber auf dem Screen sieht gibt es noch ein kleines Problem: Wenn die gestartete Anwendung beendet wird und unsere CLI wieder startet, wird jedesmal die Systemmeldung neu ausgegeben. Wir brauchen also noch ein Warmstartflag, welches anzeigt, dass die CLI wieder neu gestartet wird und das System schon initialisiert ist. Als globale Systemvariable findet auch dieses Flag seinen Platz im oberen Variablenbereich des eRAM. Hier nochmal unser neues System mit der kleinen Änderung:

[007-bel-bios.spin] —> Bellatrix-BIOS
[007-adm-bios.spin] —> Administra-BIOS
[007-reg-loader.spin] —> Regnatix-Loader

[start.bin] —> Unsere CLI [007-reg-cli.spin], compiliert und auf SD-Card gespeichert
[hallo.bin] –> [007-reg-hallo.spin], compiliert und auf SD-Card gespeichert
[para.bin] -> [007-reg-para.spin], compiliert und auf SD-Card gespeichert

So sieht das ganze doch schon ganz vernünftig aus und damit möchte ich diesen Teil des Tutorials auch beenden, denn wir haben unser Ziel erreicht – wir haben ein kleines Betriebssystem für den Hive geschrieben. Zusatzfunktion wie Sound und Grafik einzubinden ist nun nur noch eine Fleißarbeit und im TriOS auch schon Realität. Im dritten und letzten Teil beschäftigen wir uns mit dem TriOS selbst. Für alle Interessierte die bis zu diesem Punkt der „Evolution“ des Systems folgten, wird schnell klar werden, dass TriOS genau auf jenen Mechanismen basiert, welche wir bis jetzt entwickelt haben. Einiges ist noch etwas verfeinert und komfortabler gestaltet, aber prinzipiell hat es die gleiche Basis. Und es ist, obwohl vollständig in SPIN geschrieben, ausreichend schnell, wobei es die Möglichkeit offen läßt, das volle Leistungsspektrum durch Einbindung von PASM-Modulen zu nutzen.

Viel Spaß beim Experimentieren!

drohne235

P.S. Informationen zur Anbindung des eRAM hab ich der Übersichtlichkeit wegen auf eine
gesonderten Seite ausgelagert: „Fortsetzung – Externer RAM“