Saw*_*bst 0 x86 assembly qemu nasm osdev
我正在编写一个简单的 NASM 汇编引导扇区。该代码应在 16 位实模式下将文本打印到屏幕上,然后切换到 32 位保护模式并将文本打印到屏幕上。
我使用 QEMU 作为我的 CPU 模拟器,它按照应有的方式从 16 位模式打印文本。但是,在 32 位模式下应该打印的文本不会打印。
我认为这是我的代码的问题,但我也运行了类似的代码,同样的问题是仅在 16 位模式下工作。
是我没有正确使用 QEMU,还是我弄乱了其他东西?我的代码是:
引导扇区.asm
; Boot sector that enters 32 bit protected mode
[org 0x7c00]
mov bp, 0x9000 ; Set stack
mov sp, bp
mov bx, MSG_REAL_MODE
call print_string
call switch_to_pm ; We will never return to here
jmp $
%include "print_string.asm"
%include "gdt.asm"
%include "print_string_pm.asm"
%include "switch_to_pm.asm"
[bits 32]
;Where we arrive after switching to PM
BEGIN_PM:
mov ebx, MSG_PROTECTED_MODE
call print_string_pm ; 32 bit routine to print string
jmp $ ; Hang
; Global variables
MSG_REAL_MODE: db "Started in 16-bit real mode.", 0
MSG_PROTECTED_MODE: db "Successfully landed in 32-bit protected mode.", 0
; Boot sector padding
times 510-($-$$) db 0
dw 0xaa55
Run Code Online (Sandbox Code Playgroud)
switch_to_pm.asm
[bits 16]
; Switch to protected mode
switch_to_pm:
mov bx, MSG_SWITCHING ; Log
call print_string
cli ; Clear interrupts
lgdt [gdt_descriptor] ; Load GDT
mov eax, cr0 ; Set the first bit of cr0 to move to protected mode, cr0 can't be set directly
or eax, 0x1 ; Set first bit only
mov cr0, eax
jmp CODE_SEG:init_pm ; Make far jump to to 32 bit code. Forces CPU to clear cache
[bits 32]
; Initialize registers and the stack once in PM
init_pm:
mov ax, DATA_SEG ; Now in PM, our old segments are meaningless
mov ds, ax ; so we point our segment registers to the data selector defined GDT
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x90000 ; Move stack
mov esp, ebp
call BEGIN_PM ; Call 32 bit PM code
; Global variables
MSG_SWITCHING: db "Switching to 32-bit protected mode...", 0
Run Code Online (Sandbox Code Playgroud)
汇编语言
gdt_start:
gdt_null: ; The mandatory null descriptor
dd 0x0 ; dd = define double word (4 bytes)
dd 0x0
gdt_code: ; Code segment descriptor
dw 0xffff ; Limit (bites 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10011010b ; 1st flags, type flags
db 11001111b ; 2nd flags, limit (bits 16-19)
gdt_data:
dw 0xffff ; Limit (bites 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10010010b ; 1st flags, type flags
db 11001111b ; 2nd flags, limit (bits 16-19)
db 0x0
gdt_end: ; necessary so assembler can calculate gdt size below
gdt_descriptor:
dw gdt_end - gdt_start - 1 ; GDT size
dd gdt_start ; Start adress of GDT
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
Run Code Online (Sandbox Code Playgroud)
打印字符串_pm.asm
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print_string_pm:
pusha
mov edx, VIDEO_MEMORY
print_str_pm_loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je print_str_pm_return
mov [edx], ax
add ebx, 1
add edx, 2
jmp print_str_pm_loop
print_str_pm_return:
popa
ret
Run Code Online (Sandbox Code Playgroud)
打印字符串.asm
print_string:
pusha
mov ah, 0x0e
_print_str_loop:
mov al, [bx]
cmp al, 0
je _print_str_return
int 0x10
inc bx
jmp _print_str_loop
_print_str_return:
popa
ret
Run Code Online (Sandbox Code Playgroud)
命令: 构建:
nasm -f bin boot_sector.asm -o boot_sector.bin
Run Code Online (Sandbox Code Playgroud)
奎姆:
qemu-system-x86_64 boot_sector.bin --nographic
Run Code Online (Sandbox Code Playgroud)
您没有正确设置实模式堆栈指针和段寄存器。实模式堆栈指针由 SS:SP 组成。您不知道堆栈在内存中的位置,因为您只修改了 SP。引导加载程序的开头应该类似于:
xor ax, ax ; Set ES=DS=0 since an ORG of 0x7c00 is used
mov es, ax ; 0x0000<<4+0x7c00 = physical address 0x07c00
mov ds, ax
mov bp, 0x9000
mov ss, ax ; Set stack to 0x0000:0x9000
mov sp, bp
Run Code Online (Sandbox Code Playgroud)
您的代码不依赖于BP,因此不必进行设置,尽管这样做不会造成任何损害。
进入保护模式的主要问题是 GDT 中的错误。每个描述符条目为 8 个字节,每个描述符的布局如下:
在您的代码中,您似乎在 32 位代码描述符中丢失了一个字节:
gdt_code: ; Code segment descriptor
dw 0xffff ; Limit (bites 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10011010b ; 1st flags, type flags
db 11001111b ; 2nd flags, limit (bits 16-19)
Run Code Online (Sandbox Code Playgroud)
该条目只有 7 个字节长。看来您缺少最后一个字节,该字节应该为 0 来完成 32 位基地址。它应该是:
gdt_code: ; Code segment descriptor
dw 0xffff ; Limit (bites 0-15)
dw 0x0 ; Base (bits 0-15)
db 0x0 ; Base (bits 16-23)
db 10011010b ; 1st flags, type flags
db 11001111b ; 2nd flags, limit (bits 16-19)
db 0x0 ; Base (bits 24-31)
Run Code Online (Sandbox Code Playgroud)
当使用以下命令在 QEMU 中运行时,qemu-system-x86_64 boot_sector.bin它应该显示为:
我用红色突出显示了在保护模式下打印的文本。
如果您希望在控制台模式下的图形显示之外运行代码,请告诉 QEMU 使用curses。
-curses
Normally, if QEMU is compiled with graphical window support, it
displays output such as guest graphics, guest console, and the QEMU
monitor in a window. With this option, QEMU can display the VGA
output when in text mode using a curses/ncurses interface. Nothing
is displayed in graphical mode.
Run Code Online (Sandbox Code Playgroud)
使用命令行:
qemu-system-x86_64 boot_sector.bin -curses
Run Code Online (Sandbox Code Playgroud)