2016-08-08 48 views
0

Die folgenden Quelldateien werden separat (in rohen Binärdateien) zusammengestellt und auf Sektoren 1 und 2 einer virtuellen Diskette geladen. Diese Diskette dient dann als Boot-Medium für eine qemu-system-i386-VM.Position unabhängige Code in 16-Bit-Real-Modus, bootloading/Floppy lesen

Der "Bootloader" liest das "erste Programm" aus Sektor 2 der Diskette und springt dann in den Speicher mit dem gerade gelesenen Code. Der folgende Code funktioniert wie gewünscht (d. H. Die Willkommensnachricht "erstes Programm" wird gedruckt), aber ich musste ORG 0x001E in der Quelle des "ersten Programms" angeben (erhalten durch Untersuchung des Bootloader-Codes in einem Hex-Editor). 0x001E ist der Offset des temp Puffers, der den von der Diskette gelesenen Code enthält.

"Bootloader":

BITS 16 

bootloader_main: 
    mov bx, 0x07C0  ; Set data segment to bootloader's default segment 
    mov ds, bx 


    mov ah, 0x02  ; BIOS int13h "read sector" function 
    mov al, 1   ; Number of sectors to read 
    mov cl, 2   ; Sector to read 
    mov ch, 0   ; Cylinder/track 
    mov dh, 0   ; Head 
    mov dl, 0   ; Disk number (here, the floppy disk) 
    mov bx, 0x07C0  ; Segment containing the destination buffer 
    mov es, bx 
    mov bx, temp  ; Destination buffer offset 
    int 0x13 

    jmp temp 

    ret 
;end bootloader_main 




temp: times 60 db 17 

times 510-($-$$) db 0  ; Pad rest of sector and add bootloader 
dw 0xAA55      signature 

"erstes Programm":

BITS 16 
ORG 0x001E  ; Assume that this code will be located 0x001E bytes  
        after start of bootloader (in RAM) 

mov bx, string  ; Print a welcome string 
mov ah, 0x0E 
print_loop: 
    mov al, byte [bx] 
    int 0x10 
    inc bx 
    cmp byte [bx], 0 
    jne print_loop 
;end print_loop 


string: db "This is the first program.", 0 

Alternativ könnte ich ORG 0x200 und 0x200 für den Puffer anstelle von temp (dh das Programm in den Arbeitsspeicher geladen werden kurz nach Bootloader), aber keine dieser Hacks scheint nachhaltig zu sein, wenn es darum geht, nützliche Betriebssysteme zu erstellen. Wie vermeide ich diese Art von Hardcoding von Adressen?

+1

markieren Sie bitte die Prozessorarchitektur bei der Montage Fragen veröffentlichen. – rjp

+0

Ich würde mir vorstellen, dass die Basisadresse etwas ist, das mit dem Bootloader spezifiziert werden müsste. Die meiste meiner Arbeit in diesem Bereich war auf Embedded-Systemen, und ich weiß, dass das BIOS eine Menge Sachen für Sie erledigt. Wenn ich einen Bootloader benutze, hat er normalerweise eine Dokumentation, die mir sagt, wo der Einstiegspunkt erwartet wird. – rjp

+0

Wenn also jemand ein Programm zusammenbaute, das auf die Diskette geladen und vom Bootloader gelesen wurde, wurde ihm gesagt, er solle etwas wie 'ORG 512' (oder eine andere bestimmte Adresse) zu seinem Quellcode hinzufügen? Und der Bootloader würde das Programm immer auf den gleichen Teil des Speichers laden? Es ist schwer vorstellbar, wie sich moderne Betriebssysteme daraus entwickelt haben, aber ich denke, ich sollte mehr über die Geschichte lesen. – Vale132

Antwort

1

Sie können eine harte Codierung von Adressen vermeiden, indem Sie Segmente verwenden. Laden Sie das "erste Programm" an einer Adresse, die ein Vielfaches von 16 ist und laden Sie DS mit dem entsprechenden Segment (Adresse/16) und dann weit zu segment:0 wo ist wo Sie das Programm geladen haben. Verwenden Sie im geladenen Programm ORG 0.

Zum Beispiel:

BITS 16 

bootloader_main: 
    mov ax, 0x07C0  ; Set data segment to bootloader's default segment 
    mov ds, ax 

    mov ah, 0x02  ; BIOS int13h "read sector" function 
    mov al, 1   ; Number of sectors to read 
    mov cl, 2   ; Sector to read 
    mov ch, 0   ; Cylinder/track 
    mov dh, 0   ; Head 
    mov bx, program_seg ; load program at program_seg:0 
    mov es, bx 
    xor bx, bx 
    int 0x13 

    mov ax, program_seg 
    mov ds, ax 
    mov ss, ax   ; set stack to end of program_seg 
    mov sp, 0 
    jmp program_seg:0 

bootloader_end: 
program_seg equ (bootloader_end - bootloader_main + 0x7c00 + 15)/16 

times 510-($-$$) db 0  ; Pad rest of sector and add bootloader 
dw 0xAA55     ; signature 
BITS 16 
ORG 0 

mov bx, string  ; Print a welcome string 
mov ah, 0x0E 
print_loop: 
    mov al, byte [bx] 
    int 0x10 
    inc bx 
    cmp byte [bx], 0 
    jne print_loop 
;end print_loop 


string: db "This is the first program.", 0 

ich die mov dl, 0 Anweisung entfernt haben, weil Sie diesen Wert nicht schwer Code sollte. Das BIOS wird die Laufwerksnummer des Boot-Geräts in DL weitergeben, so dass Sie es nicht ändern müssen.

+0

Ein anderer üblicher Trick besteht darin, Ihren Bootloader-Code irgendwo anders zu kopieren (Sie kopieren, damit Sie wissen, wo) und den zweiten Sektor über den ursprünglichen Bootsektor zu laden, was Ihnen wiederum einen bekannten Speicherort gibt. Viele Ansätze. –

+0

@DavidHoelzer Es stellte sich die Frage, wie man ein Programm an eine nicht-hardcodierte "bekannte" Adresse lädt. Abgesehen davon, wie das Ladesegment ausgewählt wird, behandelt dieses Beispiel hauptsächlich das Laden von .COM-Dateien durch MS-DOS. –

0

In diesem Beispiel können Sie eine beliebige Menge Code zwischen jmp Load_Buffer & Load_Buffer eingeben, solange die letzten 4 Byte des Bootsektors nicht überschritten werden.

Bootloader

BOOTSEG  equ 0x7c0 
    LOW_MEM  equ 18 
    DISKIO  equ 19 

fest, wie viele Seiten 4k avaliable sind und wandeln diese zu einem Segment Adresse. In meinem Fall ist es 0x8FC0.

xor  cx, cx 
mov  cl, 64 
int  LOW_MEM    ; Get # of 4k segments to top of memory 
sub  ax, cx 
shl  ax, 6      ; 

Deaktivieren Sie den Interrupt immer, wenn Sie den Stapelzeiger ändern.Dies stellt Stapel in einem ziemlich sicheren Platz an der Spitze des Speichers weniger 64k

cli 
mov  ss, ax 
xor  sp, sp 
sti 

An diesem Punkt CS 0 sein könnte oder könnte es 0x7c0 je nach BIOS-Anbieter sein, aber das ist nicht wirklich wichtig, weil nur ES: BX an diesem Punkt wichtig ist

mov  ax, Load_Buffer 
shr  ax, 4 
add  ax, BOOTSEG 
mov  es, ax 
mov  ax, 0x201     ; Read one sector 
mov  cx, 2      ; Starting @ cylinder 0, sector 2 
xor  dh, dh     ; Head 0 and BIOS has already passed device. 

Eine Alternative wäre hier ES gewesen zu laden mit 0x7c0 und BX mit Load_Buffer wie in Ihrem Beispiel, aber in unserem Fall ES = BOOTSEG + Load_Buffer/16.

xor  bx, bx 
int  DISKIO     ; Read sector 2 
jmp  Load_Buffer 

Alignment on page borderry garantiert, dass loader immer funktioniert, egal wie viel über Load_Buffer hinzugefügt wird, es sei denn, es passiert, dass die letzten 4 Bytes des Bootsektors überschrieben werden. Die Ausrichtung muss nur auf der Seitengrenze erfolgen, also hätte 16 genauso gut funktioniert.

align 32 
Load_Buffer: 

Es ist wahrscheinlich eine gute Idee Sektor mit NOP einhüllen Code zu füllen wegläuft, wird es kein Segment Schuld.

times 508 - ($-$$) db 144  ; NOP's 0x90 
int  25      ; Incase codes runs away we'll reboot 
dw  0xAA55     ; Boot sig (no required by some emulators) 

erstes Programm

Gehen lodsb zu verwenden, nicht das ist richtiger als Weg, nutzt aber nur weniger Code.

mov  si, string 
push es 
pop  ds      ; DS:SI now points to string 
mov  ah, 14     ; TTY output 

Loop: 
lodsb       ; Read from DS:SI 
test al, 255    ; Test if these any of these bits are on. 
jz  .Done 
int  16 
jmp  Loop 

Die Zeit vor Etikett ist nur NASM Funktionalität für die Deklaration von lokalen Labels

.Done: 
hlt 
jmp  $ 

string db 'This is the first program', 0