CP/M Basic Input/Output System

Das BIOS ist der maschineabhängige Teil von CP/M. Theoretisch muss nur das BIOS angepasst werden und CP/M läuft auf einer anderen Maschine.

Für den Aufruf der BIOS-Funktionen bieten sich zwei Möglichkeiten an:
Direkter Aufruf Aufruf über BDOS

OS equ 0000h

   ld  hl,(OS+1)     ; BIOS Basisadresse laden
   ld  de,3*(BIOS-Nr.-1)
   add hl,de         ; Adresse fuer Vektor berechnen
   jp  (hl)          ; Und ausfuehren

BDOS equ  0000h

     ld   de,BIOSPB     ; BIOS Parameterblock laden
     ld   c,50          ; BDOS Funktion laden
     call BDOS          ; Adresse fuer USERF berechnen
     ret

BIOSPB:
     db   BIOS-Funktion ;0-32
     db   BIOS-A        ;Bytewert fuer Akku
     dw   BIOS-BC       ;Wortwert fuer Registerpaar BC
     dw   BIOS-DE       ;Wortwert fuer Registerpaar DE
     dw   BIOS-HL       ;Wortwert fuer Registerpaar HL
Der direkte Aufruf sollte bei einem Banksystem in der Regel nur für die Zeichenfunktionen verwendet werden. Im anderen Falle werden Adressen angesprungen, die sich nicht in der eingestellten Bank befinden. Dadurch kann es zu Abstürzen kommen. Der Aufruf über das BDOS stellt sicher, dass alle Funktionen richtig verarbeitet werden. Die BDOS Funktion 50 steht allerdings nur für CP/M ab Version 3 zur Verfügung,

Das BIOS beginnt mit den folgenden Sprunganweisungen zu den Dienstroutinen:
BIOS-Nr.Offset
[=3*(BIOS-Nr.-1)]
Beschreibung
0-JMP BOOT;Kaltstart Routine
10000HJMP WBOOT;Warmstart - Laden des Command Prozessors
20003HJMP CONST;Konsolstatus
30006HJMP CONIN;Konsoleingabe
40009HJMP CONOUT;Konsolausgabe
5000CHJMP LIST;Druckerausgabe
6000FHJMP PUNCH;Lochstreifenausgabe
70012HJMP READER;Lochstreifeneingabe
80015HJMP HOME;Disklesekopf auf Spur 0
90018HJMP SELDSK;Laufwerk wählen
10001BHJMP SETTRK;Spur wählen
11001EHJMP SETSEC;Sektor wählen
120021HJMP SETDMA;DMA-Adresse festlegen
130024HJMP READ;Sektor lesen
140027HJMP WRITE;Sektor schreiben
In CP/M 2 und höher sind noch die weiteren Sprunganweisungen vorhanden:
15002AHJMP LISTST;Druckerstatus
16002DHJMP SECTRAN;Übersetzung des Sektorversatzes (Skewing)
In CP/M 3 ist ein weiterer Satz von Sprunganweisungen vorhanden:
170030HJMP CONOST;Status der Konsolausgabe
180033HJMP AUXIST;Status der Hilfsgeräteleingabe
190036HJMP AUXOST;Status der Hilfsgerätelausgabe
200039HJMP DEVTBL;Adresse der Gerätetabelle
21003CHJMP DEVINI;Gerät initialisieren
22003FHJMP DRVTBL;Adresse der Laufwerkstabelle
230042HJMP MULTIO;Lesen/Schreiben von Mehrfachsektoren
240045HJMP FLUSH;Leeren der Dateipuffer
250048HJMP MOVE;Verschieben eines Speicherblocks
26004BHJMP TIME;Echtzeituhr
27004EHJMP SELMEM;Speicherbank wählen
280051HJMP SETBNK;Bank für DMA-Operation wählen
290054HJMP XMOVE;Bänke für MOVE laden
300057HJMP USERF;Systemabhängige Funktionen
31005AHJMP RESERV1;Reserviert
32005DHJMP RESERV2;Reserviert

BOOT

Diese Funktion ist völlig abhängig von der Implementierung und sollte niemals von einem Benutzerprogramm aufgerufen werden.

WBOOT

Lädt sowohl den Command Prozessor als auch (an einigen Systemen) das BDOS in den Speicher. Wie das durchgeführt wird, ist abhängig von der Implementierung; es könnem reservierte Disksektoren oder Extraspeicher Verwendung finden.

CONST

Gibt den Status im Akku zurück; 0 falls kein Zeichen verfügbar ist 0FFh im anderen Fall.

CONIN

Wartet, bis ein Zeichen von der Tastatur zur Verfügung steht und gibt es im Akku zurück.

CONOUT

Schreibt das Zeichen im Register C auf den Bildschirm.

LIST

Schreibt das Zeichen im Register C auf den Drucker. Die Routine wartet, bis der Drucker bereit ist.

PUNCH

Schreibt das Zeichen im Register C auf den "Lochstreifenstanzer" - oder was immer sich hinter dem Hilfsgerät verbirgt. Die Routine wartet, bis das Gerät bereit ist.

READER

Liest ein Zeichen vom "Lochstreifenleser" - oder was immer sich hinter dem Hilfsgerät verbirgt. Die Routine wartet, bis das Gerät bereit ist. Das Zeichen wird im Akku zurückgegeben. Wenn das Gerät nicht ausgeführt ist, wird das Zeichen 26 (^Z) zurückgegeben.

HOME

Das aktuelle Laufwerk wird auf Spur 0 gesetzt.

SELDSK

Wahl des Laufwerkes im Register C (0=A:, 1=B: ...). Aufruf mit Register DE=0 oder FFFFh. Ist der Wert FFFFh, wird nicht vom Laufwerk gelesen.
SELDSK gibt die Adresse eines Disc Parameter Headers im Register HL zurück.

SETTRK

Anwahl der Spur in Register BC - 0 relativ.

SETSEC

Anwahl des Sektors in Register BC. Bei CP/M 1 und 2 lag die Sektorgröße bei 128 Bytes. Bei CP/M 3 wird die Sektorgröße im Disk Parameter Block festgelegt.
In comp.os.cpm wurde diskutiert, ob der Wert des Parameter dieser Funktion ein Byte oder Wort ist. Das Ergebnis (basierend auf der Untersuchung der BDOS Quellen) war, dass es ein Wort ist.

SETDMA

Die Adresse in Register BC bestimmt, woher Daten bei der nächsten Laufwerksoperation gelesen (oder wohin geschrieben) werden.

READ

Lesen der aktuell eingestellten Spur und des Sektors in die aktuelle DMA Adresse. Gibt Akku=0 (Erfolg), 1 (nicht behebbarer Fehler) oder 0FFh (Medium gewechselt) zurück.

WRITE

Schreiben der aktuell eingestellten Spur und des Sektors. Register C enthält den Freigabe Kode:
C=0 - Schreiben kann später erfolgen
C=1 - Schreiben muss sofort erfolgen
C=2 - Schreiben kann später erfolgen, ein Pre-read ist nicht notwendig.
Gibt Akku=0 (Erfolg), 1 (nicht behebbarer Fehler), 2 (Laufwerk ist schreibgeschützt) oder 0FFh (Medium gewechselt) zurück.

LISTST

Status des aktuellen Druckers holen.
Gibt Akku=0 (nicht bereit) oder Akku=0FFh (bereit) zurück.

SECTRAN

Sektornummern umsetzen unter Berücksichtigung des Sektorversatzes (Skewing).
Bei Aufruf hält Register BC die logische Sektornummer (Null basierend) und DE die Adresse der Umsetztabelle. Zurückgegeben wird die physikalische Sektornummer im Register HL.

CONOST

Status des aktuellen Bildschirmausgabegerätes holen.
Gibt Akku=0 (nicht bereit) oder Akku=0FFh (bereit) zurück.

AUXIST

Status des aktuellen Hilfseingabegerätes holen.
Gibt Akku=0 (nicht bereit) oder Akku=0FFh (bereit) zurück.

AUXOST

Status des aktuellen Hilfsausgabegerätes holen.
Gibt Akku=0 (nicht bereit) oder Akku=0FFh (bereit) zurück.

DEVTBL

Gibt im Register HL die Adresse der Gerätetabelle zurück oder 0, wenn die Gerätetabelle nicht vorhanden ist.
Auf die Gerätetabelle kann von Programmen ohne Bankumschaltung zugegriffen werden, d.h. sie liegt im Common Memory.
Die Gerätetabelle enthält einen Eintrag für jedes Gerät. Jeder Eintrag ist wie folgt aufgebaut:
        DEFB    'NAME  '        ;Name aus 6 Bytes. Ist das erste Byte Null,
                                ;so kennzeichent diese das Ende der Tabelle.
        DEFB    Modus           ;Bitweise Darstellung:
                                ;Bit 0 gesetzt => Eingabe von diesem Gerät möglich
                                ;Bit 1 gesetzt => Ausgabe auf dieses Gerät möglich
                                ;Bit 2 gesetzt => Einstellung der Baudrate möglich
                                ;Bit 3 gesetzt => unterstützt XON/XOFF
                                ;Bit 4 gesetzt => benutzt XON/XOFF
                                ;Bits 5,6,7 gesetzt auf 0.
                                ; Amstrad Erweiterung: Ist Bit 7 gesetzt, erfolgt
                                ;kein Time Out bei diesem Gerät.
        DEFB    Baudrate        ;Kodierte Geschwindigkeit, 1-15 oder 0, wenn
                                ;Geschwindigkeit nicht veränderbar ist.
                                ;Die verfügbaren Raten sind:
                                ; 50,75,110,134.5,150,300,600,1200,
                                ; 1800,2400,3600,4800,7200,9600,19200.
Die maximale Anzahl von Geräten, die CP/M Plus zulässt, ist unklar. Die Dokumentation sagt abwechselnd 12 oder 13, während der Quellkode von DEVICE.COM auf 15 hinweist.

DEVINI

Initialisiert erneut das Gerät mit der Nummer in Register C - Aufruf bei Veränderung der Geräteeinstellungen (Baudrate, Modus, usw.)

DRVTBL

Gibt in Register HL die Adresse der Laufwerkstabelle zurück oder 0 (oder 0FFFFh oder 0FFFEh), wenn die Laufwerkstabelle nicht vorhanden ist. Die Laufwerkstabelle besteht aus 16 Zeigern zu den Disc Parameter Headern der 16 Laufwerke A-P; ist ein Zeiger 0, so existiert das entsprechende Laufwerk nicht.
Die Laufwerkstabelle liegt üblicherweise (aber nicht immer) im Common Memory (d.h. auf sie ist über Nutzerprogramme zugreifbar).

MULTIO

Dem BIOS anzeigen, dass das BDOS eine Anzahl von aufeinanderfolgender Disksektoren mit READ oder WRITE übertragen will. Übergabe mit Register C = Anzahl von auszuführenden Aufrufen; bis zu 16kByte können übertragen werden.
Die Idee hinter dem MULTIO Aufruf ist, dass das BIOS wählen kann, ob alle Daten mit der ersten READ/WRITE Operation übertragen werden, und danach bei den folgenden (n-1) Operationen keine Aktivitäten durchzuführen.

FLUSH

Schreibt anhängige Daten auf das Laufwerk.
Gib Resultat im Akku zurück: A=0 bei Erfolg, 1 bei physikalischem Laufwerksfehler und 2, wenn das Laufwerk schreibgeschützt ist.
Diese Funktion ist nur sinnvoll, wenn das BIOS das „Deblocking" überninmmt - d.h. die physikalische Sektorgröße ist nicht die Größe, die das BIOS dem BDOS übermittelt.

MOVE

Verschiebt BC Bytes im Speicher, die Startadresse steht im Register HL und die Endadresse im Register DE. Als Ergebnis sollten in den Registern HL und DE die Adressen stehen, die nicht mehr kopiert wurden. Wenn XMOVE vorher aufgerufen wurde, werden Daten zwischen zwei Bänken kopiert.

TIME

Laden des aktuellen Datums und der Uhrzeit in den SCB (nach Kaltstart -0Ch). Register HL und DE müssen gerettet werden. Wenn Register C=0FFh ist, dann wird die Uhrzeit stattdessen aus dem SCB gesetzt.
Das Format des 5 Byte Puffers ist definiert als:
        DW      Tag     ;Der Tag 1 ist der 1.Januar 1978
        DB      Stunde  ;gepacktes BCD
        DB      Minute  ;gepacktes BCD
        DB      Sekunde ;gepacktes BCD

SELMEM

Setzt die aktuelle Bank auf die Nummer im Akku. Bank 1 ist die Bank, in der Anwenderprogramme ablaufen (die TPA); Bank 0 und weitere Bänke werden genutzt von CP/M als Diskettenpuffer oder als Speicherlaufwerk (RAM Disk).

SETBNK

Setzt die Bank für die nächste Lese-/Schreiboperation. Die Bank wird im Akku übergeben.

XMOVE

Nach dem Aufruf von XMOVE kopiert der nächste Aufruf von MOVE Daten zwischen verschiedenen Bänken. Bei Aufruf von XMOVE steht im Register C die Quellbank und und in B die Zielbank.

USERF

Diese Funktion ist reserviert für den Entwickler des BIOS, um einige Besonderheiten hinzuzufügen. So greift der Aufruf z.B. bei Amstrad Rechnern auf die erweiterten BIOS Funktionen zu.

RESERV1 und RESERV2

Diese Aufrufe sind reserviert und enthalten jeweils die Instruktion JMP 0.
Letzte Änderung: 4.Juni 2004