Bootsektor von Disketten

Ja damals, als wir noch unsere eigenen Betriebssystem schreiben wollten …

Da hat alles mit einer Diskette angefangen. Es wird Zeit diese Epoche wieder etwas aufleben zu lassen. Also schreiben wir mal ein paar Bytes in den Bootsektor.


512 Bytes ist ein üblicher Datensektor (also die kleinste als Block lesbare Einheit) einer Diskette groß. Und bis heute ist dieser Standard geblieben. Zwar haben Festplatten um 2010 herum auf 4096 Bytes umgesattelt, doch Schnittstellen von Betriebssystemen legen diese Größe bis heute als virtuellen Grenzwert fest, auch wenn die Hardware dahinter dann was anderes macht.

Und wenn ein PC startete, dann las er früher (vor dem UEFI Standard) den ersten Sektor des Bootmediums und führte den Code darin aus.

BIOS Bootvorgang

Nachdem das BIOS geladen ist und dieses sich ein Bootmedium auserkoren hat, lädt es dessen ersten Sektor in den 16-bit Realmode Speicherbereich 0000:7c00 und springt an diese Adresse.

Dass heißt, dass man den Bootsektor einfach mit Instruktionen füllen kann, und diese werden dann sofort ausgeführt.

Doch für eine ordentliche Plattform benötigt man Standards und Dateisysteme. Damals war das ein FAT Dateisystem bzw. FAT12 für Disketten und der Bootsektor sollte anzeigen, welches Dateisystem genutzt wird, und wie dessen Daten verteilt sind.

Deshalb ist der Bootsektor so aufgebaut, dass die ersten 3 Bytes für eine X86 Jump-Anweisung reserviert sind und danach sofort Datenfelder kommen, die das FAT Dateisystem (oder andere) beschreiben.
Der eigentliche Bootcode wird erst danach gespeichert, und genau dorthin muss die erste JUMP-Anweisung hinspringen.

Will man jetzt seinen eigenen kleinen Bootcode schreiben, sollte man die FAT-Info Daten bestehen lassen und nur den restlichen Codebereich nutzen.

Was ich früher oft vergessen habe: Ein Bootsektor endet mit einer Signatur mit den Bytes 0x55 und 0xaa. Fehlen diese beiden Bytes am Ende, überspringt das BIOS das Medium beim Bootvorgang.

Netwide Assembler

Der nasm Assembler lässt uns einen solchen Bootsektor ganz leicht zubereiten. Und mit ein paar “magischen” Zeichen (siehe emtpy_space) lässt sich auch ein Speicherbereich bis zu einer bestimmten Marke mit Nullen füllen, damit am Ende eine 512 Byte Ausgabedatei entsteht, die man in den Bootsektor übertragen kann.

Das sieht dann als Skelett wie folgt aus kann mit nasm bootsect.asm -f bin -o bootsect.bin kompiliert werden:

 1    ; bootsect.asm
 2    org 7c00h       ; boot sector is loaded and executed in [0000:7c00]
 3
 4                    ; Offset |Description
 5                    ;-------------------------
 6main:               ; 0000: startup code (jump over data fields)
 7    jmp short boot_code
 8    nop
 9
10    ; OEM system name
11    db 'MSDOS5.0'   ; 0003: format name (8 chars)
12
13    ; BPB (BIOS Parameter Block)
14    dw     0200h    ; 000b: bytes per sector
15    db       01h    ; 000d: sectors per cluster
16    dw     0001h    ; 000e: reserved sectors (starting at 0)
17    db       02h    ; 0010: number of FATs on disk
18    dw     00e0h    ; 0011: reserved root directory entries
19    dw     0b40h    ; 0013: total sectors on disk
20    db      0f0h    ; 0015: media descriptor byte
21                    ;       f0: 3.5" 1.44M -or- 3.5" 2.88M
22                    ;       f8: hard-disk
23                    ;       f9: 3.5" 720K -or- 5.25" 1.2M
24                    ;       fc: 5.25" 180K
25                    ;       fd: 5.25" 360K -or- 8" 500K
26                    ;       fe: 5.25" 160K -or- 8" 250K -or- 8" 1.2M
27                    ;       ff: 5.25" 320K
28    dw     0007h    ; 0016: sectors per FAT
29    dw     000fh    ; 0018: sectors per track
30    dw     0002h    ; 001a: heads
31    dd 00000000h    ; 001c: reserved
32    dd 00000000h    ; 0020: reserved
33    dw     0000h    ; 0024: reserved
34    db       29h    ; 0026: extended BPB enabled with 0x29 
35
36    dd 00000000h    ; 0027: volume serial
37    db '0123456789a'; 002b: volume label (11 chars)
38    db 'FAT12   '   ; 0036: file system ID (8 chars)
39
40boot_code:          ; 003e: boot code
41    ...
42empty_space:
43    times 0200h - 2 - ($ - $$)  db 0    ; fill with '0' until position 01fe
44 
45 boot_sector_signature:
46    dw    0aa55h    ; 01fe: boot sector signature

Man kann mit der times N - ($ - $$) db 0 Anweisung aus dem 512 Byte-Bootsektor auch ein ganzes leeres Diskettenimage machen, und das würde dann so enden:

 1   ...
 2empty_space:
 3    times 0200h - 2 - ($ - $$)  db 0    ; fill with '0' until position 01fe
 4 
 5 boot_sector_signature:
 6    dw    0aa55h    ; 01fe: boot sector signature
 7
 8floppy_space:
 9    times 1474560 - ($ - $$)  db 0

Die 1 474 560 Bytes große Datei umspannt eine ganze 1.44MB 3.5” Diskette und kann sofort in einer VM mit BIOS Support gebootet werden (sogar in MS Hyper-V).

Im boot_code kann man sich dann austoben und bis zu 400 Bytes große Programme schreiben. Wer mehr will, muss alles auf weitere Sektoren verteilen und diese mit dem int 13h zusätzlich in den Speicher laden.

Fazit

Vor 25 Jahren war es mein großer Traum per Assembler ein kleines OS bzw. OS-unabhängiges Programm zu schreiben. Und bis heute kann ich von dem damals gelernten Wissen profitieren.

Es ist Schade, dass wir heute auf unseren komplexen Geräten weder Lust noch Laune verspüren oder überhaupt die Möglichkeit haben, ein eigenes System hochzuziehen. UEFI macht es uns zwar möglich, aber Smartphones und Mikrokontroller sind oft so standardfern, dass es wenig bring, sich damit zu befassen und Google versucht überhaupt uns per Codesignaturen davon abzuhalten, auf ihren Geräten etwas eigenes ausführen zu lassen.

Also bleiben uns nur VMs und vielleicht das eine oder andere Altgerät, das immer noch startfähig ist, um “den Kern” unserer Systeme zu verstehen.

Ich freue mich jedenfalls, wenn ich ein paar Boot-Assemblerzeilen sehe und merke dabei, dass die OS-Hersteller damals wie heute auch nur mit Wasser kochen.

📧 📋 🐘 | 🔔
 

Meine Dokus über:
 
Weitere externe Links zu:
Alle extern verlinkten Webseiten stehen nicht in Zusammenhang mit opengate.at.
Für deren Inhalt wird keine Haftung übernommen.



Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!