我按照Nick Blundell的一本书来做这件事 . 我编写了一个MBR程序,它首先在实模式下运行,程序中的一些指令会将cpu切换到保护模式 . 首先,我将GDT设置为:
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 10011010b
db 11001111b
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b
db 11001111b
db 0x0
gdt_end:
gdt_descriptor :
dw gdt_end - gdt_start - 1
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
然后cpu运行以下指令:
cli
lgdt [gdt_descriptor]
mov eax,cr0
or eax,0x1
mov cr0,eax ;this will set the cpu to protected-mode
;jmp $ ;I use this instrction to find where is wrong
jmp CODE_SEG:init_pm
jmp $
[bits 32]
init_pm:
jmp $
mov ax,10
jmp $
mov ds,eax
mov ss,eax
jmp $
mov es,ax
mov fs,ax
mov gs,ax
mov ebp,0x90000
mov esp,ebp
call BEGIN_PM
指令 jmp CODE_SEG:init_pm
将导致cpu崩溃并重新启动 . 如果我将其更改为 jmp init_pm
,则以下指令 mov ax,10
将导致cpu崩溃并重新启动 . 书中说切换操作需要长时间跳转 .
你能帮我做一下切换操作吗?
2 回答
您的代码中存在几个问题:
您在
gdt_code
描述符中缺少基本高位字的低位字节 . 只需在dw 0x0
之后添加db 0x0
即可 .Frank Kotler写道
gdt_descriptor
必须包含线性地址 . 是的,'s true, but it'不是唯一需要线性地址的地方 . 您可以在任何代码之前使用ORG
指令,或手动将原点添加到此字段 .指令
lgdt
仍然使用ds
寄存器来计算seg:off
地址 . 在org
下编写代码时,应将其设置为零 .远程跳转到保护模式也需要用作偏移的线性地址 . 请记住,此跳转是在兼容模式下执行的(因为它是保护模式切换后执行的第一条指令) . 它使用两个字节用于段选择器(冒号前)和 only two bytes 用于偏移量(冒号后) . 这意味着您不应该尝试跳转到高于
0xFFFF
的地址 . 再次,检查代码的来源 .在你的
gdt_descriptor
中,你've got the limit and the address. Unlike most other addresses, this is NOT a segment:offset address. It needs to be a linear address. As an MBR, you' ve可能会将它从最初加载的位置移到0x7C00 .gdt_descriptor
中的地址(通常称为gdtr
)需要是您现在所处位置的线性地址 . 您没有显示足够的代码以确定,但我怀疑您的问题是正确的 .