我正在编写一个简单的NASM程序集引导扇区。该代码应在16位实模式下将文本打印到屏幕上,然后切换到32位保护模式并在屏幕上打印文本。
我使用QEMU作为我的CPU仿真器,它正在从16位模式打印文本。但是,在32位模式下应该打印的文本不会打印。
我认为这是我的代码存在的问题,但是我也运行了similar code,同样的问题是只能在16位模式下工作。
我没有正确使用QEMU,还是我搞砸了其他东西?
谢谢
编辑:添加的代码
boot_sector.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
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
gdt.asm
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
print_string_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
print_string.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
命令: 要构建:
nasm -f bin boot_sector.asm -o boot_sector.bin
Qemu:
qemu-system-x86_64 boot_sector.bin --nographic
答案 0 :(得分:2)
您没有正确设置实模式堆栈指针和段寄存器。实模式堆栈指针由SS:SP组成。您不知道堆栈在哪里,因为您只修改了SP。 Bootloader的开头应类似于:
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
您的代码不依赖 BP ,因此不必进行设置,尽管这样做不会造成任何伤害。
进入保护模式的主要问题是GDT中的错误。每个描述符条目为8个字节,layout of each descriptor如下:
在您的代码中,您似乎缺少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)
此条目只有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)
在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.
使用命令行:
qemu-system-x86_64 boot_sector.bin -curses
答案 1 :(得分:1)
好,我知道了。当仅在macO上运行qemu-system-x86_64 boot_sector.bin
时,它甚至不以16位实模式显示任何内容。我在网上某个地方发现可以添加-nographic
,并且可以在16位实模式下使用。在32位PM中,删除-nographic
标签并添加-curses
。这工作得很好。感谢Michael Petch也向我展示了我不好的GDT条目。
答案 2 :(得分:0)
像这样将这些东西放在 switch_to_pm.asm
的顶部:
;switch_to_pm.asm
[bits 16]
; Switch to protected mode
switch_to_pm:
mov ax, 0x2401
int 0x15 ; enable A20 bit
mov ax, 0x3
int 0x10 ; set vga text mode 3
cli ; 1. disable interrupts
lgdt [gdt_descriptor] ; 2. load the GDT descriptor