Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Beide Seiten der vorigen Revision Vorhergehende Überarbeitung Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
cpm:write_a_bios [2012/03/07 18:45] – [Diskettenfunktionen] volkerp | cpm:write_a_bios [2014/05/08 10:19] (aktuell) – volkerp | ||
---|---|---|---|
Zeile 1: | Zeile 1: | ||
- | ====== | + | ====== |
**howto write a bios for cp/m 2.2\\ | **howto write a bios for cp/m 2.2\\ | ||
V. Pohlers, 2012** | V. Pohlers, 2012** | ||
- | Zu CP/M 2.2 gibt es eine Datei cbios.asm mit, die als Ausgang für ein BIOS dienen kann. | + | Ich will versuchen, die Grundlagen zum Schreiben eines eigenen kleinen BIOS für CP/M 2.2 praxisgerecht darzustellen. Grundlagenwissen sollte man schon haben, z.B. was ein BIOS ist und welche Funktionen drin sind, wie eine Diskette aufgebaut ist u.a.m.\\ |
- | Diesen Code habe ich hier genutzt, an Z80 angepasst | + | Detailwissen liefert [[cpm: |
- | Das BIOS folgt unmittelbar auf CCP und BDOS. Am Anfang des BIOS steht ein | ||
- | Sprungverteiler zu den 17 BIOS-Funktionen. | ||
- | Die BIOS-Funktionen müssen keine Register retten. | + | * [[cpm: |
+ | * [[cpm: | ||
- | ===== Kalt- und Warmstart ===== | ||
- | < | ||
- | .Z80 | ||
- | ; Skeletal CBIOS for first level of CP/M 2.0 alteration | ||
- | ; | ||
- | CCP EQU xx00H ; | ||
- | BDOS EQU CCP+806H ; | ||
- | BIOS EQU CCP+1600H ; | ||
- | |||
- | CDISK EQU 0004H ; | ||
- | IOBYTE EQU 0003H ; | ||
- | ; | ||
- | ORG BIOS ; | ||
- | |||
- | ; jump vector for individual subroutines | ||
- | JP BOOT ; | ||
- | WBOOTE: | ||
- | JP CONST ; | ||
- | JP CONIN ; | ||
- | JP CONOUT ; | ||
- | JP LIST ; | ||
- | JP PUNCH ; | ||
- | JP READER ; | ||
- | JP HOME ; | ||
- | JP SELDSK ; | ||
- | JP SETTRK ; | ||
- | JP SETSEC ; | ||
- | JP SETDMA ; | ||
- | JP READ ; | ||
- | JP WRITE ; | ||
- | JP LISTST ; | ||
- | JP SECTRAN ; | ||
- | </ | ||
- | |||
- | **BOOT** ist der Eintrittspunkt, | ||
- | werden sollte. Vom CP/M aus wird niemals BOOT aufgerufen. Im einfachsten Fall | ||
- | kann BOOT im BIOS-Bereich komplett leer bleiben, wenn die Systeminitialisierung | ||
- | im BOOT-Loader erfolgt. | ||
- | |||
- | Der Kaltstart wird nur nach dem erstmaligen Laden des Betriebsystems benötigt. | ||
- | Der Aufruf an den Kaltstart erfolgt meist von einem speziellen Ladeprogramm, | ||
- | nach dem Einschalten des Rechners das BIOS von der Systemdiskette geladen hat. | ||
- | |||
- | Aufgabe des Kaltstart ist es, die einzelnen Systemkomponenten zu initialisieren | ||
- | und eine Meldung über den erfolgten Systemstart auf der Console auszugeben. | ||
- | |||
- | Normalerweise wird nach einem Kaltstart ein Warmstart ausgeführt, | ||
- | und das BDOS von der Diskette lädt und die Sprungbefehle einsetzt. Dann braucht | ||
- | nur die zuerst anzuwählende Disk- und Usernummer in die Speicheradresse | ||
- | 0004h eingetragen zu werden. | ||
- | |||
- | **WBOOT** wird beim Warmstart (z.b. ^C oder JP 0000) aufgerufen. Die BDOS- | ||
- | Funktion 0 geht direkt zur BIOS-Funktion WBOOT weiter. | ||
- | |||
- | WBOOT muss CCP und BDOS im Speicher restaurieren. Für ein erstes BIOS kann dies | ||
- | übergangen werden, solange keine Programe gestartet werden, die das CCP oder | ||
- | sogar CCP und BDOS überschreiben. Zum Neuladen von CCP und BDOS sind folgende Verfahren üblich: | ||
- | |||
- | * Laden aus Systemspuren der BOOT-Diskette (meist A:) | ||
- | * Laden aus einer Kopie im Speicher (z.B. bei Vorhandensein von Schattenspeicher) | ||
- | * Erstellen einer Kopie während des BOOT-Vorgangs in eine RAM-Disk und Laden von dieser (üblicherweise auch hier aus Systemspuren) | ||
- | * Laden aus einer @OS-Datei von der Diskette. Dazu muss aber ein Mini-BDOS verfügbar sein, was das logische Lesen von Diskette unterstützt. Die Records dieser @OS-Datei könnten ja beliebig auf der Diskette verstreut liegen. | ||
- | |||
- | Nach dem Neuladen von CCP und BDOS werden die Systemsprünge für JP 0000 und CALL 5 eingerichtet, | ||
- | das aktuelle Laufwerk wieder selektiert und die Steuerung ans CCP übergeben. | ||
- | |||
- | < | ||
- | ; | ||
- | BOOT: | ||
- | XOR A ; | ||
- | LD (IOBYTE), | ||
- | LD (CDISK), | ||
- | JP GOCPM ; | ||
- | ; | ||
- | WBOOT: | ||
- | LD SP, | ||
- | ;... | ||
- | ..reread ccp+bdos into memory | ||
- | ;... | ||
- | ; | ||
- | ; end of load operation, set parameters and go to cp/m | ||
- | GOCPM: | ||
- | LD A, | ||
- | LD (0), | ||
- | LD HL, | ||
- | LD (1), | ||
- | ; | ||
- | LD (5), | ||
- | LD HL, | ||
- | LD (6), | ||
- | ; | ||
- | LD BC, | ||
- | CALL SETDMA | ||
- | ; | ||
- | EI ; | ||
- | LD A, | ||
- | LD C, | ||
- | JP CCP ; | ||
- | |||
- | </ | ||
- | |||
- | ===== Zeichen-I/O ===== | ||
- | |||
- | Im BIOS sind die grundlegenden I/ | ||
- | für Konsole, Drucker, Reader, Punch enthalten. | ||
- | |||
- | Ein einfaches BIOS braucht nur Ein- und Ausgabe für Konsole, die restl. | ||
- | Funktionen sind Leerfunktionen. | ||
- | |||
- | Das **I/ | ||
- | lesen bzw. beschreiben direkt die Speicherzelle IOBYTE. Das Systemprogramm STAT | ||
- | nutzt wiederum nur diese BDOS-Funktionen. | ||
- | |||
- | Ein I/ | ||
- | problemlos darauf verzichten; wenn man nicht verschiedene Geräte für die 4 I/O- | ||
- | Kanäle Konsole, Drucker, Reader, Punch unterstützen muss/will. | ||
- | |||
- | Die BIOS-Funktion **CONIN** darf das eingetippte Zeichen nicht auf dem Bildschirm | ||
- | ausgeben. | ||
- | |||
- | < | ||
- | ; | ||
- | ; | ||
- | ; simple i/o handlers (must be filled in by user) | ||
- | ; in each case, the entry point is provided, with space reserved | ||
- | ; to insert your own code | ||
- | ; | ||
- | CONST: | ||
- | ..status subroutine | ||
- | LD A,00H | ||
- | RET | ||
- | ; | ||
- | CONIN: | ||
- | ..input routine | ||
- | AND 7FH ; | ||
- | RET | ||
- | ; | ||
- | CONOUT: | ||
- | LD A, | ||
- | ..output routine | ||
- | RET | ||
- | ; | ||
- | LIST: | ||
- | LD A, | ||
- | RET ; | ||
- | ; | ||
- | LISTST: | ||
- | XOR A ;0 is always ok to return | ||
- | RET | ||
- | ; | ||
- | PUNCH: | ||
- | LD A, | ||
- | RET ;null subroutine | ||
- | ; | ||
- | ; | ||
- | READER: | ||
- | LD A, | ||
- | AND 7FH ; | ||
- | RET | ||
- | </ | ||
- | |||
- | ===== Diskettenfunktionen ===== | ||
- | |||
- | Der größte Teil des BIOS besteht in Funktionen zur Diskettenarbeit. | ||
- | |||
- | Eine Diskette besteht physikalisch aus **Spuren (Tracks)**. Diese sind in | ||
- | **physische Sektoren** unterteilt. Das BDOS greift immer über Track- und | ||
- | logische Sektornummer auf Laufwerke zu. Ein **logischer Sektor (sector, auch | ||
- | record)** ist immer 128 Byte lang. | ||
- | |||
- | Eine Spur kann Werte von 0..FFFFh annehmen, ein logischer Sektor von 0..FFFFh. | ||
- | Üblich sind bei Disketten aber Werte von 0..80 für die Spur und 0..255 für den logischen Sektor. | ||
- | Viele BIOSe arbeiten deshalb auch nur mit 8-Bit-Registern für diese Werte. Damit sind immerhin | ||
- | 256 Tracks * 256 logische Sektoren * 128 Byte = 8 MByte adressierbar. | ||
- | |||
- | Es ist Aufgabe des BIOS, die beiden Werte (Tracks und logischer Sektor) in | ||
- | |||
- | * Diskettenseite | ||
- | * Spur | ||
- | * und physische Sektornummer | ||
- | |||
- | umzusetzen. Wie das erfolgt, ist dem BIOS-Schreiber überlassen (und hängt von der Hardware ab). | ||
- | Beispiele folgen später. | ||
- | |||
- | Die physischen Sektoren sind beispielsweise 1 KByte groß. Das BIOS muss einen | ||
- | Puffer bereitstellen, | ||
- | diesen in logische Sektoren aufzuteilen. Dieser Mechanismuss heiß | ||
- | Blocking/ | ||
- | deblock.asm als Vorlage. | ||
- | |||
- | Die Routinen SETDMA, SETTRK, SETSEC speichern einfach die übergebenen Werte. | ||
- | Wenn die Laufwerke nur mit max 256 Spuren zu max. 256 log. Sektoren arbeiten, | ||
- | kann man TRACK und SECTOR als Byte belassen und nur Register C übernehmen. Das | ||
- | BDOS übergibt nur gültige Werte, so dass Register B in diesem Fall immer 0 ist | ||
- | und nicht beachtet werden muss. | ||
- | |||
- | **HOME** selektiert Spur 0. Ein phys. Zugriff aufs Laufwerk ist nicht erforderlich. | ||
- | Mam spart auch CALL SETTRK und RET, wenn HOME direkt vor SETTRK steht. | ||
- | Diese Funktion war bei älteren Laufwerken zur exakten | ||
- | Positionierung des Schreib/ | ||
- | Da das BDOS vor jedem Diskzugriff die Spurnummer über SETTRK | ||
- | anwählt, ist HOME bei neueren Laufwerken überflüssig. | ||
- | |||
- | **SETTRK** bezieht sich auf die im Registerpaar BC übergebene Spur. Diese Spurnummer | ||
- | errechnet sich immer aus der BDOS-internen (logischen) Spurnummer plus dem OFF- | ||
- | Wert im DPB. Wie auch beim SELDSK-Aufruf ist ein tatsächlicher Diskzugriff nicht | ||
- | garantiert. | ||
- | |||
- | **SETSEC** bezieht sich auf den im Registerpaar | ||
- | BC übergebenen Sektor. Die so gesetzte Sektornummer ist immer | ||
- | das Ergebnis der SECTRAN-Funktion (s.u.). Auch hier ist ein | ||
- | tatsächlicher Diskzugriff auf diesen Sektor nicht garantiert. | ||
- | |||
- | **SETDMA**: Alle nachfolgenden Diskzugriffe müssen die DMA-Adresse als | ||
- | Quell- (bei Schreibzugriffen) bzw. Zieladresse (bei Lesezugriffen) | ||
- | benutzen. Die DMA-Adresse zeigt immer auf einen 128-Byte großen Buffer, | ||
- | weshalb Diskzugriffe immer in Recordgröße erfolgen. | ||
- | |||
- | < | ||
- | ; | ||
- | ; i/o drivers for the disk | ||
- | ; | ||
- | HOME: | ||
- | LD BC, | ||
- | ; | ||
- | ;RET | ||
- | ; | ||
- | SETTRK ; | ||
- | LD (TRACK), | ||
- | RET | ||
- | ; | ||
- | SETSEC ; | ||
- | LD (SECTOR), | ||
- | RET | ||
- | |||
- | SETDMA ; | ||
- | LD (DMAAD), | ||
- | RET | ||
- | |||
- | ; | ||
- | </ | ||
- | |||
- | **SECTRAN** übernahm ursprünglich eine Sektornummertransformation bei | ||
- | hardsektorierten Disketten (8") anhand einer Sektorverschränkungstabelle (X- | ||
- | lation table XLT). Dies ist bei moderneren Laufwerken nicht mehr üblich bzw. | ||
- | wegen größerer physischer Sektorlänge als 128 Byte auch nicht möglich. Eine | ||
- | Sektorverschränkung physischer Sektoren erfolgt deshalb meist im phys. | ||
- | Diskettentreiber (s. unten). | ||
- | |||
- | Im allgemeinen genügt es, einfach die im Registerpaar BC | ||
- | übergebene Sektornummer ins Registerpaar HL zu kopieren. | ||
- | |||
- | im CP/A wird einfach der übergebene Wert genommen und um 1 erhöht (die Sektoren | ||
- | zählen in CP/A bzw. auf Diskette ab 1). Nutzt man eine allgemeine SECTRAN- | ||
- | Routine für alle Laufwerke, muss dies bei den phys. Laufwerkstreibern f. Read und Write beachtet werden. | ||
- | |||
- | < | ||
- | SECTRAN: (allg) | ||
- | ;translate the sector given by BC using the | ||
- | ;translate table given by DE | ||
- | EX DE, | ||
- | ADD HL, | ||
- | LD L, | ||
- | LD H, | ||
- | RET ;with value in HL | ||
- | ; | ||
- | SECTRAN: (CP/A) | ||
- | ;translate the sector given by BC without translate table | ||
- | LD L, | ||
- | LD H, | ||
- | inc HL | ||
- | RET ; | ||
- | ; | ||
- | </ | ||
- | |||
- | **SELDSK**: Das BIOS muß die im C-Register übergebene Laufwerksnummer | ||
- | überprüfen und, falls ein Laufwerk mit dieser Nummer existiert, | ||
- | in HL die Adresse des zugehörigen disk parameter header DPH zurückgeben, | ||
- | Im CP/M ist nicht garantiert, daß nach einem SELDSK-Aufruf | ||
- | auch tatsächlich auf dieses Laufwerk zugegriffen wird. | ||
- | Vielmehr hat der SELDSK-Aufruf nur eine ' | ||
- | damit sich das BDOS auf das Laufwerk einstellen kann. | ||
- | Das BIOS muß die Laufwerksnummer aber intern speichern, da | ||
- | sich nachfolgende Diskzugriffe immer auf das zuletzt selektierte | ||
- | Laufwerk beziehen. | ||
- | |||
- | In einem aufwendigen BIOS kann bei SELDSK eine Analyse der Diskette erfolgen, um | ||
- | das konkrete **Diskettenformat automatisch zu ermitteln**. Im CP/A-BIOS gibt es eine Liste von Formaten, die hier getestet werden (z.B. 624k, 780k, 800k). Als Resultat dieser | ||
- | Analyse wird ein passender DPB ausgewählt (oder dynamisch zusammengestellt) | ||
- | und im DPH eingetragen. | ||
- | |||
- | Die Laufwerke müssen nicht in alphabetischer Reihenfolge und durchlaufend angelegt sein. | ||
- | Man kann die Laufwerksbuchstaben A..P willkürlich den Laufwerken zuordnen. SELDSK muss | ||
- | für ein existierenes Laufwerk den passenden DPH und andernfalls 0000 zurückgeben. | ||
- | |||
- | Für jeden Laufwerksbuchstabe, | ||
- | einen eigenen disk parameter header DPH geben. | ||
- | |||
- | Ein disk parameter header DPH ist 16 Byte lang und muss im RAM stehen. BDOS | ||
- | beschreibt die freien Felder des DPH mit eigenen Werten. | ||
- | |||
- | Ein DPH umfasst 8 Einträge zu je 16 Bit und hat folgende Struktur: | ||
- | < | ||
- | +-----------------------------------------------------+ | ||
- | | XLT | NHDE | CLTK | FSCT | DIRBUF | DPB | CSV | ALV | | ||
- | +-----------------------------------------------------+ | ||
- | Byte 0/1 | ||
- | </ | ||
- | |||
- | XLT (s.o.), NHDE, CLTK, FSCT sind mit 0 vorbelegt, DIRBUF ist ein 128 Byte | ||
- | großer Puffer (für alle Laufwerke derselbe), DPB ist die Adresse des Disk | ||
- | Parameter Blocks, CSV die Adresse des Prüfsummenvektors (Check Sum Vector) und | ||
- | ALV die Adresse des Belegungsvektors (Allocation Vector). | ||
- | |||
- | Der DPB enthält die Laufwerkseigenschaften und wird weiter unten beschrieben. | ||
- | Ein DPB kann für mehrere DPH genommen werden, wenn die Laufwerkseigenschaften | ||
- | gleich sind (z.B. für 2 gleiche Diskettenlaufwerke). | ||
- | |||
- | CSV und ALV sind Speicherbereiche im RAM, | ||
- | deren Größe von den Laufwerkseigenschaften abhängen (s. DPB). | ||
- | |||
- | < | ||
- | ; fixed data tables for four drives | ||
- | |||
- | DPBASE: | ||
- | ; disk parameter header for disk 00 | ||
- | DPH0: | ||
- | DW 0000H, | ||
- | DW DIRBF, | ||
- | DW CHK00, | ||
- | ; disk parameter header for disk 01 | ||
- | DPH1: | ||
- | DW 0000H, | ||
- | DW DIRBF, | ||
- | DW CHK01, | ||
- | ; disk parameter header for disk 02 | ||
- | DPH2: | ||
- | DW 0000H, | ||
- | DW DIRBF, | ||
- | DW CHK02, | ||
- | ; disk parameter header for disk 03 | ||
- | DPH3: | ||
- | DW 0000H, | ||
- | DW DIRBF, | ||
- | DW CHK03, | ||
- | ; | ||
- | |||
- | ; werden die Laufwerksbuchstaben durchgehend vergeben (A:..D:) und | ||
- | ; folgen die DPH direkt aufeinander, | ||
- | SELDSK: | ||
- | LD HL, | ||
- | LD A,C | ||
- | LD (DISKNO), | ||
- | CP 4 ; | ||
- | RET NC ; | ||
- | ; compute proper disk parameter header address | ||
- | LD A, | ||
- | LD L, | ||
- | LD H, | ||
- | ADD HL, | ||
- | ADD HL, | ||
- | ADD HL, | ||
- | ADD HL, | ||
- | LD DE, | ||
- | ADD HL, | ||
- | RET | ||
- | |||
- | ; Alternativ: sind die Laufwerksbuchstaben nicht durchgehend vergeben, kann die Ermittlung auch direkt | ||
- | ; erfolgen, hier Laufwerke A:, B:, F:, P: | ||
- | SELDSK: | ||
- | LD A,C | ||
- | LD (DISKNO), | ||
- | |||
- | LD HL, DPH0 | ||
- | CP ' | ||
- | RET Z | ||
- | LD HL, DPH1 | ||
- | CP ' | ||
- | RET Z | ||
- | LD HL, DPH2 | ||
- | CP ' | ||
- | RET Z | ||
- | LD HL, DPH3 | ||
- | CP ' | ||
- | RET Z | ||
- | ; | ||
- | LD HL, | ||
- | RET | ||
- | </ | ||
- | |||
- | |||
- | Die **READ**-Funktion liest einen (logischen) Sektor von der Diskette in den | ||
- | DMA-Buffer. Die Disknummer, Spurnummer und Sektornummer sind jeweils durch die | ||
- | letzten SELDSK-, SETTRK- und SETSEC-Aufrufe festgelegt. | ||
- | |||
- | Bei physikalischen Sektorlängen vom mehr als 128 Bytes muß das BIOS einen | ||
- | Sektorbuffer entsprechender Große selbst bereitstellen und aus diesem Buffer 128 | ||
- | Bytes zum zuletzt definierten DMA-Buffer kopieren. Falls ein Lesefehler | ||
- | auftritt, sollte das BIOS den Diskzugriff ein paar Mal wiederholen und, falls | ||
- | der Fehler bestehen bleibt, den Fehlercode 1 im A-Register zurückgeben. | ||
- | |||
- | Die **WRITE**-Funktion schreibt einen (logischen) Sektor vom DMA-Buffer auf die | ||
- | Diskette. Die Disknummer, Spurnummer und Sektornummer sind jeweils durch die | ||
- | letzten SELDSK-, SETTRK- und SETSEC-Aufrufe festgelegt. | ||
- | |||
- | Bei physikalischen Sektorlängen von mehr als 128 Bytes kann das Record-Flag zur | ||
- | Realisierung eines ' | ||
- | Schreibzugriff reicht es, den logischen Sektor nur in den BIOS-internen | ||
- | Sektorbuffer zu übernehmen. Dies hat den Vorteil, daß nachfolgende | ||
- | Schreibzugriffe auf den selben physikalischen Sektor keinen Diskettenzugriff | ||
- | verlangen. Erst wenn der neue logische Sektor in einem anderen physikalsichen | ||
- | Sektor liegt, muß der Sektorbuffer auf die Diskette geschrieben werden. | ||
- | Directory-Schreibzugriffe sollten immer direkt auf die Diskette geleitet werden. | ||
- | |||
- | Je nach Laufwerkstyp (Diskette, RAM-Floppy etc.) können Read und Write völlig unterschiedlich implementiert sein. Die BIOS-Routinen READ und WRITE müssen in diesem Fall je nach Laufwerk DISKNO auf spezielle Routinen READx und WRITEx verzweigen. | ||
- | |||
- | Man spricht von physischen Laufwerkstreibern für Read und Write, wenn diese den physischen Transfer eines (physischen) Sektors von/zum Laufwerk übernehmen. Die logischen Laufwerkstreiber übernehmen das Blocking/ | ||
- | |||
- | Ganz einfache Routinen: | ||
- | < | ||
- | ; | ||
- | READ: | ||
- | ..read log. Sektor nach (DMAAD) | ||
- | ld a,0 ; keine Fehler | ||
- | ret | ||
- | ; | ||
- | WRITE: | ||
- | ..schreibe log. Sektor von (DMAAD) | ||
- | ld a,0 ; keine Fehler | ||
- | ret | ||
- | ; | ||
- | calcadr: | ||
- | die physikalische Position berechnen | ||
- | ret | ||
- | </ | ||
- | |||
- | Es verbleiben die RAM-Speicherbereiche, | ||
- | Ende des BIOS stehen sollten, damit das BIOS in den Systemspuren nicht zu groß wird. | ||
- | |||
- | < | ||
- | ; the remainder of the CBIOS is reserved uninitialized | ||
- | ; data area, and does not need to be a part of the | ||
- | ; system memory image (the space must be available, | ||
- | ; however) | ||
- | ; | ||
- | TRACK: | ||
- | SECTOR: | ||
- | DMAAD: | ||
- | DISKNO: | ||
- | ; | ||
- | ; scratch ram area for BDOS use | ||
- | DIRBF: | ||
- | ALL00: | ||
- | ALL01: | ||
- | ALL02: | ||
- | ALL03: | ||
- | CHK00: | ||
- | CHK01: | ||
- | CHK02: | ||
- | CHK03: | ||
- | |||
- | BUFFER: | ||
- | ; | ||
- | END | ||
- | </ | ||
- | |||
- | |||
- | ===== Der Disk Parameter Block ===== | ||
- | |||
- | |||
- | Ein DPB umfasst 15 Bytes in folgender Aufteilung: | ||
- | < | ||
- | +----------------------------------------------------------+ | ||
- | | SPT | BSH | BLM | EXM | DSM | DRM| AL0 | AL1 | CKS | OFF | | ||
- | +----------------------------------------------------------+ | ||
- | 0/1 | ||
- | | ||
- | </ | ||
- | |||
- | Beschreibung s.a. [[: | ||
- | |||
- | Zur Erstellung eines DPB braucht man folgende Angaben: | ||
- | |||
- | - Gesamtkapazität der Diskette in KByte | ||
- | - physischer Aufbau der Diskette | ||
- | - Anzahl der Spuren (Tracks) | ||
- | - Anzahl physischer Sektoren pro Spur | ||
- | - Anzahl der beschreibbaren Diskettenseiten | ||
- | - max. Anzahl der Direktory-Einträge | ||
- | - Anzahl der Systemspuren | ||
- | - gewünschte Blockgröße | ||
- | | ||
- | CP/M kennt keine Diskettenseiten, | ||
- | die Seite aus TRACK oder SECTOR ermitteln. Möglich ist z.B. bei einer Diskette | ||
- | mit 2 Seiten, 80 Spuren, 5 phys. Sektoren pro Spur: | ||
- | |||
- | * gerader Track Vorderseite (0,2,4,..), ungerader Track Rückseite (1,3,5,..) | ||
- | * Track 0..79 Vorderseite, | ||
- | * Sektor 1..5 Vorderseite, | ||
- | * ... | ||
- | | ||
- | CP/A nutzt die erste Variante. | ||
- | |||
- | Unabhängig von der physischen Sektorlänge gibt es im CP/M noch die **Blockgröße**. | ||
- | Das BDOS teilt jede Diskette in Blöcke (engl. Blocks) auf, um damit den | ||
- | Verwaltungs- und Speicheraufwand für die Belegungstabelle zu verkleinern. | ||
- | Die Länge eines Blocks ist 1, 2, 4, 8 oder 16 kByte. | ||
- | |||
- | Die Disketten-Belegungstabelle wird in Blöcken geführt, somit kann das BDOS | ||
- | Diskettenplatz auch nur blockweise vergeben. Nachteil dieser Aufteilung ist, das | ||
- | ein File immer ganze Blöcke belegt, auch wenn die tatsächliche Filelänge kleiner | ||
- | ist. | ||
- | |||
- | __Beispiel 1__: eine 800K Diskette mit 2 Seiten, 80 Spuren, 5 phys. Sektoren pro Spur | ||
- | |||
- | 2 Seiten * 80 Spuren -> 160 TRACKs\\ | ||
- | 800 KByte / 160 -> 5 KByte / Spur \\ | ||
- | 5 phys. Sektoren pro Spur -> 1 KByte großer phys. Sektor\\ | ||
- | SPT = 5 KByte / Spur -> 5K/128 = 40 logische Sektoren (records) pro Spur | ||
- | |||
- | Man muss sich für eine Blockgröße entscheiden. | ||
- | Nimmt man 2 KByte große Blöcke, gibt es insgesamt 800k/2k = 400 Blöcke. | ||
- | Damit braucht man 16 bit große Blocknummern. | ||
- | Nimmt man 4 KByte große Blöcke, gibt es insgesamt 800k/4k = 200 Blöcke. Damit | ||
- | braucht man nur 8 bit große Blocknummern. Aber man bekommt weniger kleine | ||
- | Dateien auf der Diskette unter. | ||
- | |||
- | Allgemein sollte die Blockgröße mit der Diskettengröße wachsen. | ||
- | |||
- | Aus Blockgröße und Blocknummeranzahl ergeben sich die Felder BSH, BLM und EXM | ||
- | des disk parameter blocks: | ||
- | |||
- | **OFS** := Anzahl der Systemspuren (logische TRACKs!)\\ | ||
- | **SPT** := Anz.Phys.Sektoren*Größe.Phys.Sektor / 128 | ||
- | |||
- | block size BLS in Byte (1024, 2048, .. ,16384) | ||
- | |||
- | **DSM** := Gesamtkapazität - 1 = Anz.Blöcke * (block size) - 1 | ||
- | |||
- | **BSH** := log2 (block size / 128)\\ | ||
- | **BLM** := (block size / 128) - 1\\ | ||
- | **EXM** := (block size / 1024) - 1 bei 8 bit-Blocknummern ( DSM/block size <= 255) bzw.\\ | ||
- | **EXM** := (block size / 2048) - 1 bei 16 bit-Blocknummern ( DSM/block size > 255)\\ | ||
- | |||
- | Die Anzahl der Directory-Einträge ist frei wählbar. Es werden immer ganze Blöcke vergeben, | ||
- | dies gilt auch für das Directory. Maximal können 16 Blöcke genutzt werden. | ||
- | Ein Directory-Eintrag ist 32 Byte lang, damit sind BLS/32 Directory-Einträge pro Block möglich.\\ | ||
- | Die Maximal-Zahl ergibt sich zu | ||
- | |||
- | **DRM** := Anz.Dir.Blöcke * (block size / 32) - 1 | ||
- | |||
- | Das Verhältnis zwischen Anzahl der Directory-Einträge und Anzahl der Blöcke | ||
- | sollte gewahrt bleiben. Es ist wenig sinnvoll, mehr Directory-Einträge als Blöcke zu haben. | ||
- | DSM/ | ||
- | |||
- | Im Beispiel ergibt sich für drei 2K-Blöcke 3 * 2048/32 = maximal mögliche 192 Einträge, | ||
- | das sollte man auch nutzen und DRM auf 191 setzen. | ||
- | |||
- | CP/M erkennt Diskettenwechsel, | ||
- | |||
- | **CKS** := (DRM + 1) / (128 / 32) = (DRM + 1) / 4 | ||
- | |||
- | Diese Maximalgröße muss nicht immer genommen werden; gerade bei großen Disketten | ||
- | oder Festplatten würde eine Prüfung zu lange dauern. Bei nicht wechselbaren | ||
- | Laufwerken wie Festplatten oder RAM-Disketten kann CKS auch auf 0 gesetzt werden. | ||
- | Damit wird nicht auf Diskettenwechsel geprüft. | ||
- | |||
- | Im DPH (s.o.) wird für den Prüfsummenvektor Speicherplatz definiert (CHKxx). | ||
- | Dieser muss CKS Byte groß sein: | ||
- | |||
- | CHK00: | ||
- | |||
- | Der Allocation Vektor (ALV) bildet die Belegungstabelle (besser: | ||
- | Belegungsvektor) der Diskette. Für jeden Block der Diskette ist im ALV ein Bit | ||
- | vorhanden, das entsprechend auf 0 (Block frei) oder 1 (Block belegt) gesetzt | ||
- | wird. Die Zuordnung der Blöcke zu den Bits geschieht in absteigender | ||
- | Bitnummernfolge (höchstes Bit eines Bytes zuerst) und aufsteigender Bytefolge | ||
- | (erstes Byte des ALV zuerst). Für ALLxx muss man deshalb (DSM+1)/8 Byte groß sein: | ||
- | |||
- | ALL00: | ||
- | |||
- | Achtung: Bei automatischer Formaterkennung müssen die Speicherplätze CHKxx und | ||
- | ALLxx für die größtmöglichen Werte ausgelegt sein! | ||
- | |||
- | Im disk parameter block sind die ersten beiden Byte des Allocation Vektors einzutragen. | ||
- | Je ein Bit ist für einen genutzten Directory-Block zu setzen: | ||
- | |||
- | AL0,AL1 = 11..100..00b | ||
- | |||
- | Im Beispiel gilt für 3 Directory-Blöcke | ||
- | |||
- | AL0 = 1110000b = E0h, AL1 = 00000000b = 00h | ||
- | |||
- | insgesamt ergibt sich für Beispiel 1 | ||
- | |||
- | < | ||
- | DPB00: | ||
- | DB 4 ;BSF block shift factor | ||
- | DB 15 ; | ||
- | DB 0 ;EXM null mask | ||
- | DW 224 ; | ||
- | DW 191 ; | ||
- | DB 0E0h ; | ||
- | DB 00h ; | ||
- | DW 48 ; | ||
- | DW 0 ; | ||
- | |||
- | CHK00: | ||
- | ALL00: | ||
- | </ |