
Bootsektor von Disketten
« | 21 Jan 2024 | »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:
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.