Assembly 32位程序集引导加载程序可以在VM/qemu中工作,但不能在真正的PC上工作

Assembly 32位程序集引导加载程序可以在VM/qemu中工作,但不能在真正的PC上工作,assembly,x86,bootloader,memory-segmentation,protected-mode,Assembly,X86,Bootloader,Memory Segmentation,Protected Mode,我编写了这个小引导加载程序,它以32位保护模式将单个字符打印到屏幕上: bits 16 org 0x7c00 jmp boot times 3-($-$$) db 0x90 ; Support 2 or 3 byte encoded JMPs before BPB. ; Dos 4.0 EBPB 1.44MB floppy OEMname: db "mkfs.fat" ; mkfs.fat is what OEMname mkdosfs u

我编写了这个小引导加载程序,它以32位保护模式将单个字符打印到屏幕上:

bits 16
org 0x7c00

jmp boot
times 3-($-$$) db 0x90   ; Support 2 or 3 byte encoded JMPs before BPB.

; Dos 4.0 EBPB 1.44MB floppy
OEMname:           db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
bytesPerSector:    dw    512
sectPerCluster:    db    1
reservedSectors:   dw    1
numFAT:            db    2
numRootDirEntries: dw    224
numSectors:        dw    2880
mediaType:         db    0xf0
numFATsectors:     dw    9
sectorsPerTrack:   dw    18
numHeads:          dw    2
numHiddenSectors:  dd    0
numSectorsHuge:    dd    0
driveNum:          db    0
reserved:          db    0
signature:         db    0x29
volumeID:          dd    0x2d7e5a1a
volumeLabel:       db    "NO NAME    "
fileSysType:       db    "FAT12   "

boot:
mov ax, 0x2401
int 0x15 ; enable A20 bit
mov ax, 0x0003 ; change the video mode to 0x03
int 0x10

lgdt [gdt_pointer] ; load the gdt table
mov eax, cr0 
or eax, 0x1 ; set the protected mode bit on special CPU reg cr0
mov cr0, eax
jmp code_seg:main ; long jump to the code segment code_seg

gdt_start:
dq 0x0

gdt_code:
dw 0xFFFF
dw 0
db 0
db 0b10011010
db 0b11001111
db 0

gdt_data:
dw 0xFFFF
dw 0
db 0
db 0b10010010
db 0b11001111
db 0

gdt_end:

gdt_pointer:
dw gdt_end - gdt_start
dd gdt_start

code_seg equ gdt_code - gdt_start
data_seg equ gdt_data - gdt_start

bits 32

main:
mov ax, data_seg
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x90000
mov esp, ebp

mov ebx, 0xb8000
mov al, '!'
mov ah, 0b00001111
mov word[ebx], ax
cli
jmp $

times 510 - ($-$$) db 0
dw 0xaa55

这段代码在虚拟机上运行良好,但它会导致屏幕闪烁和在真实机器上重新启动。我在两台PC上对此进行了测试。更改几行删除位32,或eax,0x1,并将jmp code_seg:main替换为jmp main以使用16位模式而不是32位保护,这会使代码工作正常。但是,当我使用32位模式时,它失败了。为什么会这样?提前感谢。

问题是您没有将ds初始化为零。您正在使用以下lgdt指令:

    lgdt [gdt_pointer] ; load the gdt table
    xor ax, ax
    mov ds, ax
此内存访问隐式使用默认段,即没有bp的地址的ds。您使用的是org 7C00h,所以希望ds为零。但是,当ROM-BIOS加载程序将控制转移到加载程序时,在ds中保留任何可能的值是有效的。解决方案是在lgdt指令之前添加以下内容:

    lgdt [gdt_pointer] ; load the gdt table
    xor ax, ax
    mov ds, ax
然后使用它将ds初始化为零


Michael Petch也提供了此解决方案。

问题在于您没有将ds初始化为零。您正在使用以下lgdt指令:

    lgdt [gdt_pointer] ; load the gdt table
    xor ax, ax
    mov ds, ax
此内存访问隐式使用默认段,即没有bp的地址的ds。您使用的是org 7C00h,所以希望ds为零。但是,当ROM-BIOS加载程序将控制转移到加载程序时,在ds中保留任何可能的值是有效的。解决方案是在lgdt指令之前添加以下内容:

    lgdt [gdt_pointer] ; load the gdt table
    xor ax, ax
    mov ds, ax
然后使用它将ds初始化为零


Michael Petch也提供了此解决方案。

在lgdt中隐式使用ds之前,应该添加xor ax、ax\mov ds、ax。这似乎不太可能是主要或唯一的问题,但应该这样做,以使代码更加健壮。你给我的期望很低,但这是100%的。非常感谢。在lgdt中隐式使用ds之前,应该添加xor ax、ax\mov ds、ax。这似乎不太可能是主要或唯一的问题,但应该这样做,以使代码更加健壮。你给我的期望很低,但这是100%的。非常感谢。