x86 NASM 实模式下的间接远跳转

x86 NASM Indirect Far Jump In Real Mode

我一直在摆弄多阶段引导加载程序,我的所有代码都可以工作,除了最后一部分:The Jump。我之前已经得到了这段代码,但我想通过替换这一行使其更加模块化:

jmp 0x7E0:0

有了这个:

jmp far [Stage2Read + SectorReadParam.bufoff]

我不想在加载代码的地方进行硬编码,而是想间接跳转到它。这是我的其余代码:

; This is stage 1 of a multi-stage bootloader

bits 16                      
org 0x7C00            

jmp 0:boot_main   

%include "io16.inc"

boot_main:
    ; setup the new stack
    cli               
    mov ax, 0x100       
    mov ss, ax          
    mov bp, 0x4000       
    mov sp, bp          
    sti 

    ; Setup data segment
    xor ax, ax
    mov ds, ax

    ; Save which drive we booted from
    mov [Stage2Read + SectorReadParam.drive], dl    

    ; Home-made BIOS wrapper to read sectors into memory
    mov si, Stage2Read
    call ReadSectors                               

    ; Change to new data segment
    mov ax, [Stage2Read + SectorReadParam.bufseg]
    mov ds, ax             

    ;jmp 0x7E0:0                                    ; THIS WORKS
    jmp far [Stage2Read + SectorReadParam.bufoff]   ; BUT THIS DOES NOT

; Used as the parameters for ReadSectors
Stage2Read: ISTRUC SectorReadParam     
    AT SectorReadParam.bufoff,  dd 0
    AT SectorReadParam.bufseg,  dw 0x07E0
    AT SectorReadParam.numsecs, db 1
    AT SectorReadParam.track,   db 0
    AT SectorReadParam.sector,  db 2
    AT SectorReadParam.head,    db 0
    AT SectorReadParam.drive,   db 0        ; needs to be initialized!
IEND

; Ending
times 510-($-$$) db 0    
dw 0xAA55              

记住所有这些代码都已经过测试并且可以工作,除了间接远跳转可以工作。这就是我需要让它工作的全部。我想知道间接远跳转是否隐含地使用了例如 ds 以便地址 Stage2Read + SectorReadParam.bufoff 不正确。这真的让我很烦恼,因为它看起来很简单。我需要帮助!

您期望 jmp farStage2Read + SectorReadParam.bufoff 读取其目标地址,这实际上是 0x0000:Stage2Read + SectorReadParam.bufoff (ds = 0x0000)。

然而,就在跳转之前,ds 被设置为 0x07e0,所以在我看来,您的代码正在从 0x07e0:Stage2Read + SectorReadParam.bufoff.

读取其目标地址

您的原始代码中有几个错误。第一个事实是您使用 DD(32 位 DWORD)而不是 16 位 来获得偏移量字。这一行:

AT SectorReadParam.bufoff, dd 0

应该是:

AT SectorReadParam.bufoff, dw 0

默认情况下(在您的情况下)为 FAR JMP 指定内存操作数时,它是相对于 DS(数据分割)。在 FAR JMP 之前,您将 DS 设置为一个新值,因此 JMP 内存操作数将读取来自错误段的内存地址(0x07e0 而不是 0x0000)。

您可以在 JMP 之后设置 DS 或者您可以将内存操作数更改为相对于 CS (仍然是包含数据的段)使用覆盖。它可能看起来像这样:

jmp far [CS:Stage2Read + SectorReadParam.bufoff]