I am trying to switch to protected mode after playing around a little bit with real mode, but something weird occurs. In real mode I have created a simple welcome screen asking the user to enter their name. After that, the switch to protected mode should occur, but this is what happens instead:
As you can see I press Enter and the background of a character starts changing, and then QEMU (the emulator I am using) restarts and re-displays the welcome screen.
Here is the code :
section startUp vstart=0x7f00
; clearing the screen
mov ah,0x00
mov al,0x03
int 0x10
; disabling the cursor
mov ah,0x01
mov ch,0x3f
int 0x10
; using Bios to print character
mov ah,0x0e
mov al,'C'
mov cx,0x01
int 0x10
; printing a string
mov ax,msg
mov bx,0x00
mov cl,0x0f
mov dx,0x00
call printMsg
; print warning
mov ax,warning
mov bx,0x00
mov cl,0x04
mov dx,0x3a
call printMsg
; print message
mov ax,msg2
mov bx,0x00
mov cl,0x0f
mov dx,0x0140
call printMsg
; input
mov si,keypress
mov bx,0x00
call input
; switching to protected mode
cli ; disabling interrupts
lgdt [GDT_Descriptor]
mov eax,cr0
or eax,0x01
mov cr0,eax ; here we are in 32 bit protected mode
jmp protectedMode
; printing a string function
printMsg:
pusha
; ax ==> offset , bx ==> segment , dl ==> Line a0*lineNumber - 1, cl ==> color
mov es,bx
mov bx,0xB000
mov ds,bx
mov si,ax
mov di,0x8000
add di,dx
mov byte[es:0x7e00],0x00
jmp loopThrough
loopThrough:
mov al,byte[es:si]
mov byte[ds:di], al
add di,0x01
mov byte[ds:di],cl
sub di,0x01
add si,0x01
mov bl,byte[es:si]
cmp bl,0x00
je quit
add di,0x02
jmp loopThrough
quit :
popa
ret
; input function kjjj
input:
pusha
; si ==> offset , bx ==> segment
mov di,si
mov es,bx
jmp loopinterupt
loopinterupt :
mov ah,0x00
int 0x16
cmp al,0x20
je quitInput
mov byte[es:si],al
add si,0x01
mov byte[es:si],0x00
mov ax,di
mov dx,0x172
mov cl,0x0f
call printMsg
jmp loopinterupt
quitInput :
popa
ret
; Data
; Setting Up the GDT
GDT :
times 8 db 0x00
; base : 0x100000 ; Limit : 0x00700
dw 0x0700 ; limit 1
dw 0x0000 ; base 1
db 0x10 ; base 2
db 0x9a ; access Byte
db 0xc0 ; limit + flags
db 0x00d ; base 3
; base : 0x800000 ; limit : 0x00700
dw 0x0700 ; limit 1
dw 0x0000 ; base 1
db 0x80 ; base 2
db 0x96 ; access Byte
db 0xc0 ; limit + flags
db 0x00 ; base 3
GDT_Descriptor :
dw GDT_Descriptor - GDT - 1
dd GDT
msg db 'Welcome to the OS fdffdfgd',0x00
msg2 db 'Please write your name : ',0x00
keypress db 'K',0x00
warning db ""
[bits 32]
protectedMode:
jmp $
times 1024-($-$$) db 0
So as you saw the code executes just fine until the jmp protectedMode
is executed.
Following the Protected Mode guide on OSDev, one can see there are 3 steps involved.
You are doing this correctly in your code by using the cli
instruction.
Due to horrible history, the hardware clears bit 20 of every address when accessing memory. You most likely don't want this, since it means that half of your address space can't be accessed. A full guide on how to disable this behavior is on OSDev, but just for a quick test on QEMU, this should work:
mov ax, 0x2401
int 0x15
If you want to switch to 32-bit Protected Mode, you most likely want two segment descriptors to start with: code and data. Since you probably want access to the full 4GB address space, you should specify a 0 base and a 4GB limit for both of them. Therefore, the only bit that will differ between them is the code/data bit:
; Setting Up the GDT
GDT :
times 8 db 0x00
; code segment: base=0; limit=0xFFFFFFFF
; present, non-system, executable, non-conforming, read+exec
dw 0xFFFF ; limit 1
dw 0x0000 ; base 1
db 0x00 ; base 2
db 0x9a ; access Byte
db 0xcf ; limit + flags
db 0x00 ; base 3
; data segment: base=0; limit=0xFFFFFFFF
; present, non-system, data segment, expand-up, read+write
dw 0xFFFF ; limit 1
dw 0x0000 ; base 1
db 0x00 ; base 2
db 0x92 ; access Byte
db 0xcf ; limit + flags
db 0x00 ; base 3
GDT_Descriptor :
dw GDT_Descriptor - GDT - 1
dd GDT
These segments will allow you to use 32-bit registers like eax
to address any memory location up to the 4GB mark.
Finally, make sure that you are actually using these segments (by loading them into the segment registers), or you can cause pure chaos.
Full mode-switching code:
; enable the A20 gate
mov ax, 0x2401
int 0x15
; disable interrupts
cli
; we do it after the BIOS call
; who knows what sort of buggy BIOS may execute STI and not restore our flags properly
; load the GDT
lgdt [GDT_Descriptor]
; enter protected mode (set bit 0 in cr0)
mov eax, cr0
or eax, 1
mov cr0, eax
; jump to protected mode entry point
jmp 0x08:protectedMode
; Here we use an absolute FAR JUMP, which reloads CS
; If we don't reload CS, the CPU will continue executing Protected Mode code using the old CS descriptor => chaos
; CS=0x08 because that is the offset of the code descriptor in the GDT
; ...
; protected mode entry point
[bits 32]
protectedMode:
; reload the other segment registers with the data descriptor
mov eax, 0x10
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
; VERY IMPORTANT: set up the stack
mov ss, eax
mov esp, you_decide_the_place