Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen angezeigt.
Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
cpm:write_a_bios [2012/03/07 10:51] – angelegt 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. Treibern 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. | ||
- | |||
- | 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, between " | ||
- | ; | ||
- | 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 brauch man folgende Angaben: | ||
- | |||
- | - Gesamtkapazität der Diskette in KByte | ||
- | - physischer Aufbau der Diskette | ||
- | - Anzahl der Spuren (Tracks) | ||
- | - Anzahl physischer Sektoren pro Spur | ||
- | - anzahl sd | ||
- | | ||
- | ... | ||
- | |||
- | ; | ||
- | DPBLK: | ||
- | DW 26 ; | ||
- | DB 3 ; | ||
- | DB 7 ; | ||
- | DB 0 ; | ||
- | DW 242 ; | ||
- | DW 63 ; | ||
- | DB 192 ; | ||
- | DB 0 ; | ||
- | DW 16 ; | ||
- | DW 2 ; | ||
- | ; | ||
- | ; end of fixed tables | ||