Nahe Call / Jump-Tabellen funktionieren in einem Bootloader nicht immer

Allgemeines Problem

Ich habe einen einfachen Bootloader entwickelt und bin auf ein Problem in einigen Umgebungen gestoßen, in denen Anweisungen wie diese nicht funktionieren:

mov si, call_tbl      ; SI=Call table pointer
call [call_tbl]       ; Call print_char using near indirect absolute call
                      ; via memory operand
call [ds:call_tbl]    ; Call print_char using near indirect absolute call
                      ; via memory operand w/segment override
call near [si]        ; Call print_char using near indirect absolute call
                      ; via register

Jeder dieser Fälle betrifft indirekt naheANRU zu absoluten Speicheroffsets. Ich habe festgestellt, dass ich Probleme habe, wenn ich "similar @" verwend JMP Tabellen. Aufrufe und Sprünge, die relativ sind, scheinen nicht betroffen zu sein. Code wie dieser funktioniert:

call print_char 

Ich habe die auf Stackoverflow vorgestellten Ratschläge von Postern übernommen, in denen die Vor- und Nachteile des Schreibens eines Bootloaders besprochen wurden. Insbesondere sah ich diesesPaketüberflus antworte mitAllgemeine Bootloader-Tipps. Der erste Tipp war:

Wenn das BIOS zu Ihrem Code springt, können Sie sich nicht auf @ verlasse CS, DS, ES, SS, SP -Register mit gültigen oder erwarteten Werten. Sie sollten beim Start Ihres Bootloaders entsprechend eingerichtet werden. Es kann nur garantiert werden, dass Ihr Bootloader von der physischen Adresse 0x07c00 geladen und ausgeführt wird und dass die Bootlaufwerksnummer in das @ geladen wir DL registrieren

Nimmt man alle Ratschläge, habe ich mich nicht auf @ verlass CS, Ich richte einen Stack ein und setze DS für das @ geeignet se ORG (Ursprungsoffset) habe ich verwendet. Ich habe ein Minimal Complete Verifiable-Beispiel erstellt, das das Problem veranschaulicht. Ich habe das mit @ erstel NASM, aber es scheint kein spezifisches Problem für @ zu se NASM.

Minimal Example

Der zu testende Code lautet wie folgt:

[ORG 0x7c00]
[Bits 16]

section .text
main:
    xor ax, ax
    mov ds, ax            ; DS=0x0000 since OFFSET=0x7c00
    cli                   ; Turn off interrupts for potentially buggy 8088
    mov ss, ax
    mov sp, 0x7c00        ; SS:SP = Stack just below 0x7c00
    sti                   ; Turn interrupts back on

    mov si, call_tbl      ; SI=Call table pointer
    mov al, [char_arr]    ; First char to print 'B' (beginning)
    call print_char       ; Call print_char directly (relative jump)

    mov al, [char_arr+1]  ; Character to print 'M' (middle)
    call [call_tbl]       ; Call print_char using near indirect absolute call
                          ; via memory operand
    call [ds:call_tbl]    ; Call print_char using near indirect absolute call
                          ; via memory operand w/segment override
    call near [si]        ; Call print_char using near indirect absolute call
                          ; via register

    mov al, [char_arr+2]  ; Third char to print 'E' (end)
    call print_char       ; Call print_char directly (relative jump)

end:
    cli
.endloop:
    hlt                   ; Halt processor
    jmp .endloop

print_char:
    mov ah, 0x0e    ; Write CHAR/Attrib as TTY
    mov bx, 0x00    ; Page 0
    int 0x10
    retn

; Near call address table with one entry
call_tbl: dw print_char

; Simple array of characters
char_arr: db 'BME'

; Bootsector padding
times 510-($-$) db 0
dw 0xAA55

Ich baue beide ein ISO image und ein 1,44-MB-Disketten-Image für Testzwecke. Ich benutze eine Debian Jessie-Umgebung, aber die meisten Linux-Distributionen wären ähnlich:

nasm -f bin boot.asm -o boot.bin
dd if=/dev/zero of=floppy.img bs=1024 count=1440
dd if=boot.bin of=floppy.img conv=notrunc

mkdir iso    
cp floppy.img iso/
genisoimage -quiet -V 'MYBOOT' -input-charset iso8859-1 -o myos.iso -b floppy.img -hide floppy.img iso

Ich habe ein Disketten-Image namensfloppy.img und ein ISO Bild mit dem Namenmyos.iso.

Expectations vs Actual Results

Unter den meisten Bedingungen funktioniert dieser Code, in einigen Umgebungen jedoch nicht. Wenn es funktioniert, wird dies einfach auf dem Display gedruckt:

BMMME

Ich drucke ausB mit einem typischenANRU mit relativen Offset scheint es gut zu funktionieren. In einigen Umgebungen erhalte ich beim Ausführen des Codes Folgendes:

B

Und dann scheint es einfach aufzuhören, irgendetwas zu tun. Es scheint das @ auszudruckB richtig, aber dann passiert etwas Unerwartetes.

Umgebungen, die zu funktionieren scheinen:

QEMU mit Diskette und ISO gebootet VirtualBox mit Diskette und ISO gebootetVMWare 9 mit Diskette und ISO gebootet DosBox mit Diskette gebootetOffiziell verpackt Bochs (2.6) unter Debian Jessie mit Floppy-ImageBochs 2.6.6 (erstellt aus der Quellcodeverwaltung) unter Debian Jessie mit Diskettenimage und ISO BilAST Premmia SMP P90 System ab Mitte der 90er Jahre mit Diskette und ISO

Umgebungen, die nicht wie erwartet funktionieren:

Offiziell verpackt Bochs (2.6) auf Debian Jessie mit ISO Bil 486DX-basiertes System mit AMI-BIOS aus den frühen 90er Jahren, das ein Floppy-Image verwendet. CDs booten auf diesem System nicht, sodass die CD nicht getestet werden konnte.

as ich interessant finde ist, dass Bochs (Version 2.6) funktioniert unter Debian Jessie mit einem @ nicht wie erwart ISO. Wenn ich mit der gleichen Version von der Diskette boote, funktioniert es wie erwartet.

In allen Fällen das ISO und das Disketten-Image schien zu laden und zu starten, da inALL Fälle konnte es zumindest ausdruckenB auf dem Display.

Meine FrageWenn es fehlschlägt, warum wird nur ein @ ausgedruckB und nichts weiter Warum funktionieren einige Umgebungen und andere scheitern?Ist das ein Fehler in meinem Code oder der Hardware / dem BIOS?Wie kann ich das Problem beheben, sodass ich weiterhin nahezu indirekte Jump- und Call-Tabellen für absolute Speicher-Offsets verwenden kann? Mir ist bewusst, dass ich diese Anweisungen vollständig vermeiden kann, und das scheint mein Problem zu lösen, aber ich möchte verstehen, wie und ob ich sie in einem Bootloader richtig verwenden kann.

Antworten auf die Frage(2)

Ihre Antwort auf die Frage