Im Herzen des JOYCE

Teil 1: Restart-Vektoren und BIOS-Jumpblocks

Will ein Programmierer seine Programme besonders schnell machen, so wird er direkt mit dem Prozessor seines Rechners kommunizieren, sprich: sein Programm in Maschinensprache schreiben müssen. Will er jedoch nicht alles alleine machen, kann er für die Programmierung der Ein-/Ausgabesequenzen auf die Routinen des Betriebssystems zurückgreifen. CP/M Plus, das Betriebssystem der JOYCE-Rechner (PCW 8256/8512), bietet hier mit seinen BDOS-Funktionen schon viele Möglichkeiten.

Die Programmaufrufe der BDOS-Funktionen sind weitgehend standardisiert; Parameterübergabe und -rückgabe sind vorgegeben, und solange diese Vorgaben eingehalten werden, kann nicht viel passieren. Ist man damit jedoch noch nicht zufrieden, bietet der JOYCE weitere Möglichkeiten.

Der nächste Schritt, der Aufruf und die Verwendung der BIOS-Funktionen, ist dann allerdings schon etwas für erfahrene Programmierer. Hier werden ausführliche Kenntnisse der Hardware benötigt, denn viele BIOS-Funktionen können noch nicht einmal vom Anwenderprogramm aufgerufen werden, da sie in einer anderen Speicherbank "residieren". Steigt man noch weiter hinab, will man direkt mit der Peripherie kommunizieren, die Verwaltung des Speichers selbst übernehmen oder spezielle Anwendungen programmieren, sind die Experten gefragt, jene Programmierer, die auch vor der Benutzung der ROM-Routinen nicht zurückschrecken. Sie merken sicher schon, daß einiges an Grundwissen vorhanden sein muß, um "an der Maschine" zu programmieren.

Ich will mich jedoch bemühen, auch den noch nicht so versierten Programmierern den Einblick in das Betriebssystem des JOYCE zu ermöglichen. Wer die Besitzer der CPC-Computer bisher um ihre ROM-Routinen und ROM-Funktionen beneidet hat, wird feststellen, daß der JOYCE ähnliches zu bieten hat, hier jedoch nicht im ROM, sondern im BIOS des Systemprogramms.

Kerniges BIOS

Der BIOS-Kern des JOYCE ist in verschiedene funktionale Abschnitte unterteilt. Jeder dieser Abschnitte hat die Aufgabe, die Kommunikation mit bestimmten Ein-/Ausgabeeinheiten zu verwalten und zu unterstützen. Diese Abschnitte liegen fast ausschließlich im Speicherblock #0 und sind damit in allen Speicherbänken, mit Ausnahme der Bank 1 (TPA), für den Prozessor erreichbar. Die Zuordnung von Programmsegmenten zu bestimmten Geräten beziehungsweise Aufgaben erlaubt es, die Eigenschaften eines Peripheriegeräts optimal zu nutzen, so daß ein Anwenderprogramm ohne Schwierigkeiten mit der Peripherie kommunizieren kann. Die Adreßlage dieser Segmente können Sie der Tabelle 1 entnehmen.
1.RSTRestart-Vektoren und BIOS-Jumpblocks$0000
2.CDCommunications Driver$010A
3.TETerminal-Emulator$0530
4.DDDisc Driver$0BC0
5.KMKeyboard Manager$1100
6.SCRScreen Driver$14E0
7.DDLDisc Driver (FDC Interface)$1C20
8.KLKernel$1E40
Tabelle 1: Die Programmsegmente des JOYCE-BIOS und deren Startadressen im Speicherblock #0
Leider gibt es über diese Betriebssystemsegmente nur sehr wenig "offizielle" Dokumentation, so daß der interessierte Programmierer weitgehend auf die eigene Phantasie und Erfahrung angewiesen ist. Darum hier also ein erster Einblick in die Betriebssystemroutinen des JOYCE-BIOS. Bevor wir jedoch auf diese Routinen zugreifen können, müssen wir uns entweder in der Speicherbank 0 oder einer anderen CP/M-Bank (mit Ausnahme der TPA, Bank 1) befinden. Da die Aufteilung und Struktur des Speichers im JOYCE schon längst kein Geheimnis mehr darstellt, sei hier nur das Wichtigste kurz zusammengefaßt: Eine Speicherbank (64 kByte) besteht beim JOYCE aus vier Speicherblöcken zu je 16 kByte. Diese Speicherblöcke werden normalerweise vom BIOS verwaltet und konfiguriert.

Das CP/M-Betriebssystem selbst beansprucht die Blöcke 0,1,2,3,7 und 8 und stellt dem Anwender die Blöcke 4,5,6 und einen Teil von Block 1 zur Verfügung (auch TPA - Transient Program Area genannt). Der Block 7, die sogenannte Common Memory, ist normalerweise Teil einer jeden Bank. Über diesen Block wechselt das BIOS von einer Bank in die andere. Die restlichen Bänke, je nach Speicherausbau bis zur Bank 25, werden durch Ersetzen des Blocks #1 in der Systembank (Bank #0) gebildet (siehe Abbildung).
Abbildung: Die Speicherorganisation des JOYCE - die weiteren Bänke entstehen, indem der entsprechende Block in die Bank 0 eingeblendet wird (anstelle von Block 1)

Um eine gemeinsame Basis zu haben, gehen wir zukünftig davon aus, daß wir zunächst in die Systembank (Bank 0) umschalten, bevor wir die Systemroutinen aufrufen. Diese Umschaltung läßt sich relativ einfach über die BIOS-Funktion #30 (USERF) durchführen. Damit werden dann auch gleich Interrupt-Vektoren und Stack-Adressen in die dafür vorgesehenen Systembereiche gelegt. Der Aufruf dieser BIOS-Funktion erfolgt direkt vom Anwenderprogramm aus mit der Befehlssequenz
CALL 0FC5AH
DEFW ROUTINE
wobei ROUTINE die Adresse der gewünschten Systemroutine darstellt. Da während der Umschaltung der Speicherbänke die Registerinhalte gesichert werden, können eventuell benötigte Parameter direkt übergeben werden.

Beginnen wir nun mit dem ersten Abschnitt des Betriebssystems, den Restart-Vektoren und dem XBIOS-Jumpblock. Restart-(RST-)Befehle funktionieren im Prinzip wie CALL-Befehle; beim Aufruf wird die Rückkehradresse auf dem Prozessor-Stack gesichert, damit nach dem Ausführen einer Programmroutine die Rückkehr ins Hauptprogramm möglich wird. Der Vorteil dieser RST-Befehle ist jedoch, daß sie im Vergleich zum CALL-Befehl nur ein einziges Byte an Speicherplatz belegen.

Dieser Vorteil wird allerdings dadurch wieder relativiert, daß der Z80 Prozessor des JOYCE insgesamt nur acht Restart-Befehle kennt und damit wiederum nur feststehende Adressen aufrufen kann. Diese Adressen liegen allesamt in der sogenannten "Zeropage" der aktiven Speicherbank des Rechners, im Adreßraum $00-$FF. In der Befehlssyntax des Z80 werden die Adressen zugleich mit dem RST-Befehl festgelegt, also zum Beispiel RST 08H oder RST 30H. Auf diese Weise lassen sich die Adressen $00, $08, $10, §18, $20, §28, $30 und $31 direkt aufrufen. In den Betriebssystem-Routinen des JOYCE werden diese Befehle, ausgenommen die Befehle RST 30H und RST 38H, zur Umschaltung der Speicherbänke benutzt. Dabei gibt es vorgegebene Umschaltkonfigurationen (RST 00H und RST 08H) und "frei" definierbare Konfigurationen (RST 20H und RST 28H). Bei den frei definierbaren Aufrufen erfolgt die Festlegung der Speicherblöcke über einen RAM-Control-Block (RAMCB), in dem oberer und unterer Speicherblock und die Startadresse der Programmroutine, die nach der Speicherumschaltung aufgerufen werden soll, definiert sind. Die Begriffe "oberer" und "unterer" Speicherblock beziehen sich dabei auf die Adreßlage im Gesamtspeicher, das heißt, der obere Speicherblock belegt den Adreßbereich $8000 - $BFFF und der untere Speicherblock den Adreßbereich $4000 - $7FFF. Die Speicherblöcke #0 (Adreßbereich $0000 - $3FFF) und #7 (Common Memory, Adreßbereich $C000 - $FFFF) bleiben auch bei diesen Speicherkonfigurationen verfügbar. Für den Aufruf dieser Restart-Funktionen aus der TPA heraus müssen sowohl der RST-Befehl als auch der RAM-Control-Block in der Systembank oder in der Common Memory liegen.

Bei allen Restart-Aufrufen zur Speicherkonfiguration werden während der Speicherumschaltung die Prozessorregister gesichert und können für die Parameterübergabe beziehungsweise -rückgabe benutzt werden.

Die RST-Funktionen und deren Aufrufformate im einzelnen:

RST 00H: schaltet den Speicherblock #8 (Bank 2) in den Adreßbereich der CPU ein. Die Startadresse der auszuführenden Routine folgt dem RST-Befehl.
Beispielaufruf:

RST 00H
DEFW Start_Adresse

RST 08H: schaltet das Screen-Environment (Block 2) in den Adreßbereich der CPU ein. Die Startadresse der auszuführenden Routine folgt dem RST-Befehl.
Beispielaufruf:

RST 08H
DEFW Start_Adresse

RST 20H: schaltet eine vom Nutzer definierte Speicherkonfiguration in den Adreßbereich der CPU ein. Die Adresse des RAMCB folgt dem RST-Befehl.
Beispielaufruf:

      RST  20H
      DEFW RAMCB

RAMCB DEFW Start_Adresse
      DEFB Lo_Block
      DEFB Hi_Block

RST 28H: schaltet eine vom Nutzer definierte Speicherkonfiguration in den Adreßbereich der CPU ein. Der RAMCB folgt dem RST-Befehl.
Beispielaufruf:
      RST 28H
RAMCB DEFW Start_Adresse
DEFB  Lo_Block
DEFB  Hi_Block

Weiterhin kann über die Adresse $002B eine Speicherumschaltung ähnlich wie mit RST 20H / RST 28H erreicht werden. Hier muß vor dem Aufruf die Adresse des RAMCB in das HL-Registerpaar geladen werden. Der Aufruf erfolgt dann mit einem "normalen" CALL-Befehl.
Beispielaufruf:
      LD HL, RAMCB
      CALL 002BH

RAMCB DEFW Start_Adresse
      DEFB Lo_Block
      DEFB Hi_Block

Listing 1 zeigt einige Beispiele, wie diese Restart-Funktionen vom Anwenderprogramm aufgerufen werden können.
[Sample routines]

RST 30H: Dieser RST-Befehl stellt eine BREAK-Funktion dar und wird normalerweise bei Systemfehlern aufgerufen. Nach dem Aufruf befindet sich der Prozessor in einer Endlosschleife, die nur durch einen System-Reset (SHIFT-EXTRA-EXIT) wieder verlassen werden kann.

RST 38: wird vom Prozessor für die Bearbeitung von Interrupts (IM 1) aufgerufen und leitet das Programm auf die Interrupt-Service-Routine um.

Jumpblocks

Das BIOS des JOYCE bietet zwei Jumpblocks (Einsprungtabellen): den Standard-CP/M3-Jumpblock und einen erweiterten (extended) BIOS-Jumpblock. Der "Standard-Jumpblock" liegt in der Common Memory (Startadresse beim JOYCE: $FC00) und ist direkt von der TPA (Bank 1) zum Aufruf der BIOS-Funktionen erreichbar. Der "extended Jumpblock" dagegen liegt in der Systemspeicherbank (Bank 0) und kann nur nach vorheriger Speicherbankumschaltung erreicht werden.

Über diesen Jumpblock lassen sich einige der Systemroutinen aufrufen, die wir im weiteren Verlauf dieser Serie näher behandeln und erläutern wollen. Da im Prinzip nur die gewünschten Aufrufe auf die eigentlichen Routinen umgeleitet werden, sei der extended Jumpblock hier nur der Vollständigkeit halber aufgeführt.

Der extended BIOS-(XBIOS-)Jumpblock stellt eine Spezialität der CP/M-3-Implementationen der Amstrad-Rechner dar, daß heißt er wird sowohl von den JOYCE- als auch von den CPC-Rechnern unterstützt. Damit wird den Programmierern eine quasi standardisierte Schnittstelle zur Kommunikation mit der System-Software angeboten, die das "programmieren an der Hardware" erleichtern soll. Der Aufruf der einzelnen XBIOS-Funktionen erfolgt direkt aus dem Anwenderprogramm heraus über die schon beschriebene BIOS-Funktion #30.

Ein Beispiel:
Wollen Sie die aktuelle Cursorposition oder die Größe des gerade aktiven Darstellungsfensters ermitteln, so kann hierfür eine Funktion des Terminal-Emulators benutzt werden (TE ASK, Einsprungadresse $00BF). Der Aufruf geschieht dann mit:
CALL FC5AH
DEFW TE_ASK

Die BIOS Funktion schaltet die Speicherbank um, sichert Interrupt-Vektoren und Stack-Bereiche und ruft die Adresse $00BF im BIOS-Jumpblock auf. Hier steht nun ein Sprungbefehl zur eigentlichen Systemroutine, die die angeforderten Parameter zusammenstellt und an das Anwenderprogramm zurückgibt.

Tabelle 2 listet alle Funktionen, deren Bedeutung und die benutzten Einsprungadressen auf, die über diesen Jumpblock erreicht werden können.
1. Funktionen des Disk-Treibers
DD INIT
DD SETUP
DD READ SECTOR
DD WRITE SECTOR
DD CHECK SECTOR
DD FORMAT
DD LOGIN
DD SEL FORMAT
DD DRIVE STATUS
DD READ ID
DD L DPB
DPB DD L XDPB
DD L ON MOTOR
DD L T OFF MOTOR
DD L OFF MOTOR
DD L READ
DD L WRITE
DD L SEEK
$0080
$0083
$0086
$0089
$008C
$008F
$0092
$0095
$0098
$009B
$009E
$00A1
$00A4
$00A7
$00AA
$00AD
S00B0
$00B3
initialisiert den Disk-Treiber
legt die Laufwerksparameter neu fest
liest einen Sektor von der Diskette
schreibt einen Sektor auf die Diskette
überprüft einen Sektor der Diskette
formatiert eine Spur
meldet ein Laufwerk neu an
wählt ein Standardformat
liest den Status des spezifizierten Laufwerks
liest die Sektorkennung des nächsten Sektors
initialisiert einen DPB-Standard
initialisiert einen extended DPB (XDPB)
schaltet Floppy-Motor ein (Einschaltverzögerung)
startet die Ausschaltverzögerung für den Motor
schaltet den Motor aus (ohne Verzögerung)
Lesekommando direkt an FDC senden
Schreibkommando direkt an FDC senden
fährt eine bestimmte Spur auf der Diskette an
2. Funktionen des I/O-Treibers
CD VERSION
CD INFO
CD SA INIT
CD SA BAUD
CD SA PARAMS
$00E3
$00E6
$00B6
$00B9
$00BC
übergibt die BIOS-Versionsnummer
liefert Systeminfo (Hardware-Konfiguration)
initialisiert die SIO
setzt die Baud-Rate für die SIO
liefert die aktuellen Einstellungen der SIO
3. Funktionen des Terminal-Emulators
TE ASK
TE RESET
TE STL ASK
TE STL ON OFF
TE SET INK
TE SET BORDER
TE SET SPEED
$00BF
$00C2
$00C5
$00C8
$00CB
$00CE
$00D1
liest Cursorposition und Fenstergöße
reinitialisiert den Terminal-Emulator
ist die Statuszeile eingeschaltet?
schaltet die Statuszeile ein bzw. aus
erlaubt ein Umschalten der Ausgabefarben
erlaubt ein Umschalten der Ausgabefarben
legt Zeitperioden für den Farbwechsel fest
4. Funktionen des Tastaturtreibers
KM SET EXPAND
KM SET KEY
KM KT GET
KM KT PUT
KM SET SPEED
$00D4
$00D7
$00DA
$00DD
$00E0
speichert Expansions-Strings
ändert die Übersetzungstabelle für eine Taste
liest ein Zeichen aus dem Tastaturpuffer
schreibt ein Zeichen in den Tastaturpuffer
legt die Zeiten für die Tastaturabfrage fest
5. Funktionen des Bildschirmtreibers
SCR RUN ROUTINE $00E9 schaltet das Screen-Environment ein und ruft
eine spezifizierte Programmroutine auf
6. Funktionen des CP/M-BIOS
?BOOT
?CIST
?CI
?COST
?CO
?CINIT
?PDERR
?PDCHANGE
?PMSG
KL MOVE
$00EC
$00EF
$00F2
$00F5
$00F8
$00FB
$00FE
$0101
$0104
$0107
führt die System-Initialisierung durch
liest den Status eines Eingabegeräts
liest ein Zeichen vom Eingabegerät
liest den Status eines Ausgabegeräts
gibt ein Zeichen an ein Ausgabegerät aus
reinitialisiert die Baud-Rate der SIO
gibt eine Gerätefehlermeldung aus
gibt die Meldung für Laufwerkswechsel aus
Ausgabe einer beliebigen Statusmeldung
ermöglicht bankübergreifenden Datentransfer
Tabelle 2: Die Funktionen des BIOS, die über den extended BIOS-Jumpblock aufgerufen werden können, und ihre Einsprungadressen

Die in der Tabelle mit "?" gekennzeichneten Funktionen entsprechen den Kommunikationsmodulen des CP/M-BIOS. Diese Sprungvektoren werden vom BIOS zur Ein- und Ausgabe von Zeichen (Character I/O) und zur Ausgabe von Fehler- und Statusmeldungen benutzt.

Neben diesen Sprungvektoren enthält dieser "untere" Bereich des Systemspeichers aber auch noch weitere interessante Informationen. Hier sind neben den Interrupt-Vektoren für die Floppy auch die jeweils aktuelle Speicherkonfiguration und die Anzahl der im System vorhandenen Speicherblöcke gespeichert. Details zu diesen Speicherstellen finden Sie in Tabelle 3.
$0060
$0061
$0062
$0063
$0064
$0065
$007F
aktuelle Blocknummer, Adreßbereich $0000 - $3FFF
aktuelle Blocknummer, Adreßbereich $4000 - $7FFF
aktuelle Blocknummer, Adreßbereich $8000 - $BFFF
aktuelle Blocknummer, Adreßbereich $C000 - $FFFF
aktueller System-Ticker (Interrupt-Processing)
Frequenzteiler für Interrupt-Processing
Anzahl der vorhandenen Speicherblöcke
Tabelle 3: Wichtige Adressen in der sogenannten "Zeropage" der Bank 0 (Adreßbereich $0000 - $0100)

(Norbert Finke/rs)
[Overview] [Part 2]

Scanned by Werner Cirsovius
March 2003
© DMV Verlag