Um das Betriebssystem tOSh herunterzuladen, kannst du dies über den Bereich Resources dieser Website tun.
Teile
Diese Artikelserie wird aus folgenden Teilen bestehen:
- Teil I - Bootloader <— Du bist hier
- Teil II - Stage 1
- Teil III - Sprung zu C
- Teil IV - Interrupts
- Teil V - Dateisystem und Diskettenlaufwerk-Treiber
- Teil VI - Booten auf verschiedenen Maschinentypen
ISA POST Tester Card
Vorwort
Noch ein Betriebssystem? Ja. Warum? Weil ich kann. Unglaublicherweise habe ich freie Zeit! Und da dies Teil eines größeren Projekts ist, werde ich in ein paar Monaten, wenn ich wieder genug Zeit habe, es zu überprüfen, eine Zusammenfassung und Dokumentation brauchen, und diesen Artikel zu schreiben wird mir irgendwie dabei helfen, alles an einem Ort zu haben, um später darauf zurückgreifen zu können.
Was ist dieses Projekt? Willst du mit dem Kernel von Linux konkurrieren? Willst du versuchen,
Windows durch dein eigenes zu ersetzen? Nein. Überhaupt nicht.
Tatsächlich glaube ich, dass es heutzutage ziemlich schwierig ist, so etwas alleine zu machen, und alles, was man allein erreichen könnte, würde wohl nicht viel weiter kommen als das, was Terry A. Davis mit Temple OS erreicht hat, was an sich, gelinde gesagt, ziemlich beeindruckend ist.
Ehrlich gesagt besteht das Ziel dieses Betriebssystems darin, meine Fähigkeiten als Programmierer und mein Verständnis von Betriebssystemen, Architekturen und Sprachen (ASM, C) zu zeigen, Werkzeuge "on the fly" zu erstellen und noch eine ganze Reihe anderer Dinge, teils um vor meinen Freunden ein bisschen anzugeben und teils um bei Recruitern und zukünftigen Arbeitgebern Eindruck zu schinden, falls sie Zweifel an meinen Fähigkeiten haben.
Falls du Recruiter bist: Hallo, freut mich! Schick diese Artikelserie bitte an den zuständigen technischen Leiter, er wird sie lieben.
Falls du der "zuständige technische Leiter" bist: Hallo, wie geht's? Freut mich: Schick mir bitte eine Mail und lass uns quatschen!
Falls du in keine dieser beiden Kategorien fällst, ist diese Artikelserie vielleicht aus irgendeinem Grund auch für dich nützlich. Universität? Neugier? Masochismus? Egal, willkommen und viel Spaß damit, so wie ich beim Schreiben darüber, wie man ein Betriebssystem für die Intel-Architektur erstellt, Spaß hatte und gelitten habe.
tOSh herunterladen (img / Quellcode)
Um das Betriebssystem tOSh herunterzuladen, kannst du dies über den Bereich Resources dieser Website tun. In der Ressourcentabelle gibt es mehrere Bereiche, und unter Software findest du das bereits kompilierte Image des Betriebssystems und unter Sourcecode die neueste Version des Quellcodes. LAD ES JETZT HERUNTER, VERPASS ES NICHT!!! (?)
Warum heißt es tOSh?
Tosh, tsh oder Toshi sind einige meiner Handles. Ich wollte etwas Ähnliches wie das rekursive Akronym GNU is Not Unix machen, und dabei kam Folgendes heraus:
"tOSh Operating System, Huh?"
Irgendwann fand ich das lustig. Heute nicht mehr so sehr, aber ich war zu faul, den Namen des Repos zu ändern ;D Gut, genug der Einleitung. Fangen wir direkt mit dem Allerallerersten an, was man beim Entwickeln eines Betriebssystems für Intel im Hinterkopf haben muss: die Bootsequenz zu verstehen.
Intel-Bootsequenz
Die Boot Sequence bzw. Boot_Sequence besteht aus einer Reihe von Schritten, die in einer bestimmten Reihenfolge ausgeführt werden müssen, um am Ende einen Kernel in den Speicher zu laden und schließlich zu seinem Code zu springen, um ihm die Kontrolle über die Geräte des Computers, die Benutzeroberfläche, die Programmausführung usw. zu übergeben…
Schauen wir uns diese Schritte ganz kurz an und achten dabei besonders auf Dinge, die wir darüber wissen müssen, wie der Rechner konfiguriert wird – vom Moment, in dem du bei deinem PC power-on drückst, bis zu dem Punkt, an dem du schließlich ein paar eigene Instruktionen einfügen kannst, sobald du deinen eigenen Code ausführen darfst.
P.O.S.T.
Wenn ein Computer eingeschaltet wird, haben die meisten CPU-Register sehr genau definierte Werte, darunter auch der Instruction Pointer (IP), den andere Architekturen oder Lehrbücher Program Counter nennen. Der Wert von IP enthält direkt nach dem Einschalten des Computers die Speicheradresse, die die CPU ausführt. Diese Speicheradresse nennt man reset vector und sie ist in der CPU hardcodiert: Die Speicheradresse ist konkret 0xFFFFFFF0. Das Motherboard des Computers stellt sicher, dass die am reset vector ausgeführte Instruktion eine jump-Instruktion zu der Speicheradresse ist, auf die der BIOS-Entry Point abgebildet ist. Dieses ganze Durcheinander von BIOS-Speicheradressen wird vom Chipsatz des Motherboards organisiert, weil der RAM zu diesem Zeitpunkt noch irgendeinen zufälligen Müll enthält.
Schließlich führt die CPU den Firmware-Code aus. Auf einem Legacy-BIOS-System initialisiert das BIOS einen Teil der Hardware des Computers und führt den POST - Power-On Self-Test durch. Dabei handelt es sich um eine Reihe von Diagnosen einiger essenzieller Komponenten, wie zum Beispiel der Grafikkarte. Wenn beispielsweise die Grafikkarte in diesem Stadium ausfällt, sorgt der POST-Code normalerweise dafür, dass das BIOS über den speaker des Computers ein paar Pieptöne ausgibt. Dann kannst du das Handbuch zur Hand nehmen und anhand der Anzahl der Pieptöne herausfinden, warum die Initialisierung fehlschlägt. Diese Informationen stehen normalerweise im Handbuch des Motherboards, wo du nachschauen kannst, was beispielsweise 3 lange und ein kurzer Piepton, 2 kurze und ein langer Piepton usw. bedeuten.
Nach dem P.O.S.T.: Sprung ins Ungewisse
Sobald die essenziellen Komponenten überprüft wurden, will das BIOS ein Betriebssystem booten. Und dieses Betriebssystem muss sich ja irgendwo befinden. Also beginnt es, auf Festplatten, USB-Sticks, Disketten oder CD-ROMs danach zu suchen. Wenn das BIOS kein verfügbares Gerät findet, stoppt es die Ausführung der CPU, zeigt aber vorher noch eine entsprechende Meldung an:
Non-System Disk or Disk Error.
Wenn es jedoch ein Gerät findet, versucht das BIOS, den ersten sector mit 512 bytes von dem zuerst gefundenen Speichermedium zu lesen (oder von dem im BIOS konfigurierten, versteht sich).
Dieser erste Sektor wird umgangssprachlich MBR (Master Boot Record) genannt. Das BIOS _lädt einfach alles, was sich in diesem ersten Sektor befindet,_ an die Speicheradresse 0x0000:0x7c00 (Segment 0, Adresse 0x7c00) und springt zu dieser Adresse, egal welcher Code dort ausgeführt werden soll.
Ein Sprung ins Ungewisse. (!)
Nur 512 Bytes verfügbar
Na gut, du hast nicht wirklich genau 512 Bytes zur Verfügung, um deinen Bootloader-Code zu schreiben, sondern ein bisschen weniger.
Das BIOS sucht auf den "bootfähigen Geräten" nach einer Markierung oder einem magic number (ja), die exakt aus der Bytefolge 0x55 und 0xAA an den Offsets 510 bzw. 511 bestehen muss.
Und das wohlgemerkt, wenn du von einer Diskette bootest. Wenn du von einer Festplatte booten musst, reduziert sich dein Platz auf 446 bytes.
Und als ob das nicht schon schlimm genug wäre, musst du auf diesem winzigen Platz Folgendes erledigen, um einen halbwegs vernünftigen Bootloader zu haben:
- Feststellen, von welcher Partition gebootet werden soll
- Herausfinden, wo sich das
Kernel-Image auf der Bootpartition befindet - Das
Kernel-Image in den Speicher laden (dasBIOShat Funktionen, um Sektoren zu lesen und in den Speicher zu kopieren) - Von
Real Modein denProtected Modewechseln (wir befinden uns imReal Modemit maximal1 MBverfügbarem Speicher, obwohl unser Computer über 9000 GB hat)Protected Modebedeutet unter anderem, von16-Bit-Instruktionen und Adressierung auf32-Bitumzuschalten. Dadurch fällt die Begrenzung des Speicherzugriffs auf1 Megabyteweg und der Adressraum wird auf bis zu4 Gigabyteserweitert. - Den
Stackeinrichten (ja, wir müssen den Stack selbst konfigurieren) - Fehlermeldungen! (findet der Bootloader das Image nicht? Konnte der Kernel nicht in den Speicher kopiert werden? Was zum Teufel passiert hier?)
Es gibt Wege, diese Probleme zu lösen, denn sonst würde es die heutigen Betriebssysteme nicht geben. Diese Probleme wurden in den letzten 40 Jahren der Geschichte der PCs immer wieder gelöst und diskutiert, genau wie wir es hier ebenfalls tun. Und sie werden auch weiterhin diskutiert und in weiteren Artikeln behandelt werden.
Einen Bootloader schreiben
Die Informationen oben sind das absolute Minimum, das man wissen muss, um zu verstehen, warum manche Werte, die völlig willkürlich erscheinen, es tatsächlich nicht sind. Als Nächstes schreiben wir einen Bootloader mit 512 bytes, der es uns zumindest ermöglicht, Sektoren von einem Gerät in den Speicher zu kopieren und zu dieser Adresse zu springen, um dort weiterzumachen.
Das nennt man One-Stage loading: Unser Bootloader lädt ein Kernel-Image (das kleiner als 1 Megabyte sein muss), danach springen wir zu einem Stub, in dem wir von Real Mode in den Protected Mode wechseln müssen, bevor wir schließlich korrekt zu unserem Kernel springen.
HALT! Bevor wir anfangen…
Wir werden ein paar Dinge installieren und die folgenden Schritte durchführen:
- Eine beliebige
Linux-Distribution verwenden -
nasminstallieren (Assembly-Compiler) -
qemuinstallieren (ein Emulator für mehrere Architekturen; wir werden die Intel-Architektur 386+ emulieren) -
PCeminstallieren (ein Emulator für PC-Komponenten; er emuliert verschiedene BIOS-Typen, Motherboards, CPUs, Grafikkarten usw…) -
bochsinstallieren, einen Emulator, der dich während der gesamten Entwicklung des Betriebssystems begleiten wird, da du damit spezielle Register wie GDT, IDT und CRn auslesen kannst. Es ist ein Emulator, mit dem du debuggen kannst, ziemlich ähnlich wie mit gdb.
…machen wir einen kleinen Geschichtstest
Bevor wir weitermachen, noch eine wichtige Klarstellung: Alles, was wir hier machen, beschreibt den klassischen Legacy-BIOS-Bootvorgang. Moderne Rechner können auch über UEFI booten, dessen Ladevorgang anders funktioniert. In dieser Serie bleiben wir bewusst bei BIOS, weil wir einen klassischen x86-Bootloader bauen.
Wir erstellen 3 Dateien:
bootsector.asmbuild_and_run.shstage1.asm
Alle drei leer, außer build_and_run.sh, das der Einfachheit halber ungefähr folgenden Inhalt haben sollte:
#!/bin/sh
nasm -f bin bootsector.asm -o boot.bin
nasm -f bin stage1.asm -o stage1.bin
cat boot.bin stage1.bin > tOSh.bin
qemu-system-x86_64 -fda tOSh.bin
Als Erstes konfigurieren wir nasm so, dass es unseren Assembly-Code genau so assembliert, wie wir es wollen.
; ------------------------------------------------
; tOSh Operating System, Huh? (c) 2019
; bootsector.asm by toshi
; $ nasm -f bin bootsector.asm -o boot.bin
; ------------------------------------------------
;
; docs
; https://wiki.osdev.org/Boot_Sequence
; https://wiki.osdev.org/Rolling_Your_Own_Bootloader
; https://stackoverflow.com/questions/34178717/load-segment-from-floppy-with-int13h
bits 16 ; setup nasm to 16 bit code
cpu 186 ; assemble with 80186 instruction set (ie.: push word label)
org 0x7c00 ; all offsets will be at 0x7c00
; after here we will be able to execute code
; in x86 real mode, so these are the first instructions
section .text
global _start ; make this symbol global so we can reference
; it later in a custom linker script
_start:
jmp boot
Die allererste Instruktion ist jmp boot, die zu einem label weiter unten springt.
Damit nasm genau weiß, wohin es springen muss (jmp boot), müssen wir den Assembler dazu zwingen, keine eigenen Entscheidungen zu treffen und zu versuchen, das Neueste vom Neuesten zu verwenden, das ihm zur Verfügung steht.
Deshalb konfigurieren wir nasm so, dass es 16-Bit-Code schreibt und mit der Direktive cpu maximal das
80186-Instruktionsset verwendet. Und schließlich sagen wir mit org 0x7c00, dass alle "Nullen" der Offsets von
Jumps, Labels und allem anderen bei 0x7c00 beginnen: Unser relativer "neuer Nullpunkt" ist also das, was wir mit org konfigurieren.
Das ist extrem wichtig und ein Konzept, das du besser jetzt als später verstehen solltest, wenn es bereits zu spät ist und du manchmal Assemblercode lesen musst, bei dem die Instruktionen überhaupt keinen Sinn ergeben.
Wenn du springst (jmp, jx, jxx), können die Sprünge far oder near sein. Bei einem far-Sprung auf 16-Bit-x86 werden
Segment und Offset der 16-Bit-Speicheradressierungsmodi von Intel bzw. Real Mode berücksichtigt.
Eines unserer wichtigsten Ziele, nachdem wir die CPU gebootet und den Kernel in den Speicher kopiert haben, wird sein,
den Real Mode zu verlassen und in den [Protected Mode] zu springen. Es ist unendlich viel einfacher, mit den Speicheradressierungsmodi
des Protected Mode zu arbeiten als mit denen des Real Mode, weil die Berechnung des physischen Speichers zwischen Segment und Offset
verwirrend und nervig ist. Im Protected Mode gehst du dagegen einfach direkt zu einer 32-Bit-Speicherposition und fertig.
Drücke ENTER, um fortzufahren…
Wir machen mit ein paar nützlichen Funktionen weiter, um unsere Bootsequenz zur schönsten im Viertel zu machen:
; http://www.ctyme.com/intr/rb-0097.htm
; ralf brown scroll down window int10h
clrscr:
push bp
mov bp, sp
mov ah, 0x07 ; set "scroll down window" for int 10h
mov al, 0 ; clear entire window with null char
; https://en.wikipedia.org/wiki/BIOS_color_attributes
mov bh, 0x4f ; colors (nibbles background/foreground)
; we choose red background, white characters
mov cx, 0 ; top left screen (0,0) (cx register)
mov dh, 24 ; 25 rows (from 0 to n-1) (dx register)
mov dl, 79 ; 80 cols (from 0 to n-1)
int 0x10
mov sp, bp
pop bp
ret
sleep:
push ax
push cx
push dx
mov ah, 0x86 ; int 15h, function 86h, CX:DX interval in microseconds
mov cx, 0x20 ; just a few seconds
mov dx, 0x0000
int 0x15
pop dx
pop cx
pop ax
ret
; print(char *msg_string)
print:
push bp
mov bp, sp
; move the cursor
; Set cursor position AH=02h BH = Page Number, DH = Row, DL = Column
mov dh, 12 ; row
mov dl, 16 ; col
mov bh, 0 ; page 0
mov ah, 0x2 ; move cursor service
int 0x10
; as we have stack, we grab the parameter
; of print
mov si, [bp+4] ; move the ptr of the first
; argument
mov ah, 0x0e ; write char teletype style
; http://www.ctyme.com/intr/rb-0106.htm
mov bh, 0 ; page number 0
mov bl, 0 ; foreground (only graphics mode)
.loop:
lodsb ; load a byte from ds:si into al
; glad we setup ds register before,
cmp al, 0 ; is the null caracter?
je .print_end
int 0x10 ; BIOS Video services
jmp .loop
.print_end:
mov sp, bp
pop bp
ret
load_disk:
push dx
mov ah, 0x02 ; int 13h, read
mov al, dh ; number of sectors to read
mov cl, 0x02 ; cl, sector, 0x01 is the boot sector
; 0x02 is where we will put our
; 'stage1' data
mov ch, 0x00 ; ch, cylinder
; boot_drive_numbers -->
; drive number, 0 = floppy1, 1 = floppy2
; 0x80 = hdd1, 0x81 = hdd2
mov dl, [boot_drive_number] ; the BIOS should have told us what's the
; drive number that we grabbed in the very first
; instructions
mov dh, 0x00 ; dh, head number (0 to F)
int 0x13 ; BIOS interruption
jc load_error ; if carry bit set, then error
pop dx
cmp al, dh ; al is now num of sectors read
jne sectors_error
ret
load_error:
push word load_error_msg
call print
add sp, 2
jmp error_hang
sectors_error:
push word sectors_error_msg
call print
add sp, 2
jmp error_hang
error_hang:
jmp $
In allen folgenden Funktionen gilt:
Da ich absolut nichts habe, außer dem unteren Teil des Speichers, Real Mode und der Möglichkeit, Interrupt-Aufrufe an das BIOS zu machen,
ist die einzige Schnittstelle, mit der ich überhaupt etwas machen kann, das BIOS. Um eine vollständige Liste der BIOS-Interrupts und aller
Funktionen zu bekommen, die du verwenden kannst, um mit anderen Geräten wie dem Diskettenlaufwerk, der Tastatur oder dem Bildschirm zu interagieren, gibt es die alte und geliebte Ralf Brown’s Interrupt List, die von unschätzbarem Wert ist.
clrscr: macht genau das, was ihr Akronym verspricht: den Bildschirm löschen.
sleep: die Ausführung für ein paar Sekunden anhalten.
print: diese Funktion gibt einen Text aus, auf den der als Parameter übergebene Pointer auf dem stack zeigt
load_disk: genau (head) Kopf 0x00, (cylinder) Zylinder 0x00 und Sektor 0x02 laden, wo sich später der Code von stage1 befinden wird.
load_error, sectors_error und error_hang: Funktionen, die den Fehlertyp auf dem Bildschirm ausgeben und die CPU anschließend absichtlich mit einer Endlosschleife mittels jmp $ aufhängen
Jetzt kommt der darunterstehende Bootcode mit boot::
boot:
nop ; stylish nop is stylish
; save DL register setup by BIOS for getting the drive number
mov [boot_drive_number], dl
Im Grunde war das Erste, was wir gemacht haben, wenn du den Code aufmerksam verfolgst, zum Label boot: zu springen,
und nach dem sympathischen und eleganten nop, den ich dort einfach nur hingeschrieben habe, weil ich Bock darauf hatte,
speichere ich als allererstes den Wert des Registers dl in einem anderen Label boot_drive_number.
Beim Booten füllt das BIOS einige Register mit Daten, und in dl hat uns das BIOS die Nummer
des Laufwerks gespeichert, von dem wir booten werden.
mov ax, 0x0000 ; if ds is 0x7c0 the ORG directive must be disabled
; but OFC ds is 0, then ORG directive must be enabled!
; TODO: Why? --
; BECAUSE: every memory position, or
; memory reference implicitely has
; ds inside -> [ptr] is [ds:ptr]
; that's why
; ALSO: segmentation essentialy is
; << 4, so 0x7c0 << 4 is 0x7c00
mov ds, ax ; set the data segment register
; we set ds as 0x0 because we use ORG 0x7c00
; for our offsets assembled in the binary
mov es, ax ; and 'extended?' register too
; setup the stack
mov ax, 0x7c00 + 512 ; 512 is the total size of the boot sector
mov ss, ax ; so we set the stack segment
; of this code, TODO: explain segment addresses
; arithmetics
mov sp, 0x1000 ; let's reserve 4k of stack
Hier richten wir einen rudimentären Stack ein, damit wir unsere eigenen Funktionen im Bootloader verwenden können.
Brauchen wir ihn? Und ehrlich gesagt: nein, aber wenn ich schon boote, möchte ich auch gerne Nachrichten auf dem Bildschirm anzeigen –zum Debuggen oder einfach nur aus Style-Gründen– und die einfachste Möglichkeit, das zu tun, ist, "Funktionen" zu erstellen, die einen Stack benötigen, um Parameter übergeben zu können.
Also setzen wir unsere Segmentregister ds (data segment) und es (extended segment) auf 0x0000 und konfigurieren das Segmentregister ss (stack segment) mit einer Speicheradresse, die am Ende unseres Codes liegt.
Schließlich reservieren wir mit der Instruktion mov sp, 0x1000 4 Kilobyte des Stacks, indem wir 0x1000 dem stack pointer-Register sp zuweisen.
call clrscr
push word loading_msg
call print
add sp, 2
call sleep
Nachdem wir den Stack eingerichtet haben, können wir jetzt die call-Instruktion verwenden und unsere ziemlich eigenartige calling convention benutzen.
Um einen Parameter an unsere Funktion zu übergeben, pushen wir den Parameter-Pointer, führen direkt danach ein call aus und addieren bei der Rückkehr ein word (wenn der push ein word war) zum stack pointer sp.
; start "stage1"
; TODO: make this a function,
; https://github.com/cfenollosa/os-tutorial/blob/master/07-bootsector-disk/boot_sect_main.asm
mov bx, 0x9000 ; es:bx = 0:0x9000 ; where in memory are we
; going to put the stage1 code
mov dh, 4 ; read 4 sectors
call load_disk
Hier konfigurieren wir einfach die Parameter unserer Funktion load_disk und sagen ihr, dass sie die 4 gelesenen Sektoren nach 0:0x9000 kopieren soll.
; uncomment the following lines from
; ------------ here ----------------
;push word 0x9000
; call print
;add sp, 2
;cli
;hlt
; ------------till here------------
; for seeing that the sector data
; is displayed
jmp 0x9000 ; execute stage1
hlt
Und sobald sie kopiert sind, springen wir ins Ungewisse zu einer Speicheradresse, an die wir noch nichts geschrieben haben.
loading_msg: db "Loading tOSh ver -3.1416a | tw/github: @0x705h ...", 0x07, 0
load_error_msg: db "Load error", 0
sectors_error_msg: db "Sectors error", 0
boot_drive_number: db 0
times 510 - ($-$$) db 'T' ; pad 510 bytes with 't', last two bytes
; in the 512 sector will be the bootloader mark
dw 0xaa55 ; bootloader mark. These bytes make this sector bootable
; here ends the first 512 bytes sector
Am Ende unseres bootsector.asm füllen wir den restlichen Platz mit Ts auf, damit wir sehen können, wie sich unser Binary verhält und ob uns noch Platz bleibt. Das Zeichen T verwende ich als Padding bis auf 512 Bytes, um zu sehen, wie viel Platz mir noch bleibt.
Weniger Ts bedeutet weniger Bytes zum Programmieren des Bootloaders.
Schließlich ist das dw word 0xaa55 eine Markierung oder Magic Number, die das BIOS an diesem exakten Offset erwartet (511b bzw. 512b), um den Hinweis zu bekommen, dass dieser Sektor tatsächlich bootfähig ist.
Memory Map & Sector Layout
In jedem Teil dieser Artikelserie gibt es einen Pflichtstopp: unseren Memory Map aktuell zu halten.
Wir müssen immer im Hinterkopf behalten, wo wir was im Speicher ablegen werden, und wenn wir dabei ordentlich arbeiten, ersparen wir uns verdammt viele Kopfschmerzen.
Floppy
| Sector (CHS) | Logischer Bereich | Inhalt/Code | Zweck |
|---|---|---|---|
| C:0 H:0 S:1 | boot sector | bootloader.asm | Liest den Inhalt der Diskette, kopiert ihn in den Speicher und springt zu stage1.asm |
Speicher (Real Mode)
| Physische Adresse | Seg:Offset-Notation | Größe | Zweck |
|---|---|---|---|
| 0x00000 - 0x003FF | 0000:0000 | 1 KB | IVT (Interrupt Vector Table des BIOS) |
| 0x00400 - 0x004FF | 0000:0400 | 256 B | BDA (BIOS Data Area) |
| 0x00500 - 0x07BFF | 0000:0500 | ~29.7 KB | Freier Bereich / Unser Stack (von SP=0x7C00 adressiert und nach unten wachsend) |
| 0x07C00 - 0x07DF_ | 0000:7C00 | 512 B | Bootsektor (bootsector.asm, vom BIOS geladen) |
| 0x07E00 - 0x8FFFF | 0000:7E00 | ~544 KB | Freier konventioneller Speicher |
| 0x09000 - 0x097FF | 0000:9000 | 2 KB | Stage 1 (hierhin kopiert INT 13h die 4 Sektoren von der Diskette) |
| 0xA0000 - 0xFFFFF | A000:0000 | 384 KB | "Videospeicher, VRAM und BIOS-ROM" |