32 位程序集引导加载程序在 VM/qemu 中工作,但在真实 PC 上不工作
32-bit assembly bootloader works in VM/qemu but not on a real PC
我编写了这个在 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
此代码在 VM 上运行良好,但会导致屏幕闪烁几次并在真机上重新启动(我已在两台 PC 上测试过)。更改几行(删除“bits 32”、“or eax, 0x1”,并将“jmp code_seg:main”替换为“jmp main”)以使用 16 位模式而不是 32 位保护模式会导致代码正常工作正好。但是,当我使用 32 位模式时,它失败了。为什么是这样?提前致谢。
问题是您没有将 ds
初始化为零。您正在使用以下 lgdt
指令:
lgdt [gdt_pointer] ; load the gdt table
此内存访问隐式使用默认段,即 ds
用于没有 bp
的地址。您正在使用 org 7C00h
所以您希望 ds
为零。但是,当 ROM-BIOS 加载程序将控制权转移给您的加载程序时,它可以在 ds
中保留任何可能的值。解决方法是在 lgdt
指令之前添加以下内容:
xor ax, ax
mov ds, ax
然后 使用它来将 ds
初始化为零。
Michael Petch 的 .
中也提供了此解决方案
我编写了这个在 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
此代码在 VM 上运行良好,但会导致屏幕闪烁几次并在真机上重新启动(我已在两台 PC 上测试过)。更改几行(删除“bits 32”、“or eax, 0x1”,并将“jmp code_seg:main”替换为“jmp main”)以使用 16 位模式而不是 32 位保护模式会导致代码正常工作正好。但是,当我使用 32 位模式时,它失败了。为什么是这样?提前致谢。
问题是您没有将 ds
初始化为零。您正在使用以下 lgdt
指令:
lgdt [gdt_pointer] ; load the gdt table
此内存访问隐式使用默认段,即 ds
用于没有 bp
的地址。您正在使用 org 7C00h
所以您希望 ds
为零。但是,当 ROM-BIOS 加载程序将控制权转移给您的加载程序时,它可以在 ds
中保留任何可能的值。解决方法是在 lgdt
指令之前添加以下内容:
xor ax, ax
mov ds, ax
然后 ds
初始化为零。
Michael Petch 的