I do this according a book by Nick Blundell. I write a MBR program, which runs in real-mode firstly, and some instructions in the program will switch the cpu to protected-mode. First I set the GDT like this:
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
Then the cpu runs the following instructions:
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
The instruction jmp CODE_SEG:init_pm
will cause the cpu to crash and restart. If I change this to jmp init_pm
, the following instruction mov ax,10
will cause the cpu to crash and restart. And the book said the switching operation needs a long jump.
Could you please help me do the switching operation?
There are several problems in your code:
gdt_code
descriptor. Simply add db 0x0
after dw 0x0
.gdt_descriptor
must contain linear address. Yes, that's true, but it's not the only place where linear address is required. You can use ORG
directive before any code, or manually add origin to this field.lgdt
still uses ds
register for seg:off
address calculation. You should have it set to zero when writing code under org
.0xFFFF
. Again, check origin of your code.mov ax, 10
instruction about which you say that it will cause the cpu to crash and restart, uses the wrong value! The selector that we need is at offset 16, that is 0x10 in hexadecimal. And since you defined DATA_SEG equ gdt_data - gdt_start
, you really should be writing mov ax, DATA_SEG
.