I am developing a toy OS in assembly and I have a problem when switching from protected mode back to real mode. I have successfully switched to protected mode, called the kernel that writes text to [0xb8000] video memory, returned to the caller and (probably) switched back to real mode. I am trying to use bios interrupts from real mode so I don't have to write my own device drivers.
Interrupts however don't seem to be executed after switching back to real mode. They don't crash the system as they would in protected mode so I guess I am in real mode.
minrep.asm (bootloader)
boot.header:
[BITS 16]
[ORG 0x7c00]
mov ax, cs
mov ds, ax
xor ax, ax
mov ds, ax
cli
mov ss, ax
mov sp, 0x7c00
sti
call main
jmp $
disk.read_sectors:
reset:
xor ax,ax
int 0x13
jc reset
floppy:
xor bx,bx
mov ah,0x2
mov al, [esp+6]
mov cx,[esp+4]
mov dh,0x0
mov dl, 0x80
mov bx, ds
mov es, bx
mov bx, [esp+2]
int 0x13
jc error
mov ax, 0
ret
error:
mov ax, 1
ret
main:
push word 16 ; 16 sectors for stage2
push word 2 ; starts at sector 2
push word 0x1000 ; into 0x1000 memory buffer
call disk.read_sectors
pop bx
pop bx
pop bx
jmp 0000:0x1000
mov ax, 0
ret
footer:
times 510-($-boot.header) db 0
dw 0xAA55
minrep_st2.asm (stage2, enter prot and back to real)
[BITS 16]
[ORG 0x1000]
stage2.header:
mov ax, cs
mov ds, ax
jmp stage2.main
stage2.main:
mov al, 'B'
mov ah, 0x0E
mov bh, 0x00
mov bl, 0x07
int 0x10
call switch_to_protected
switch_to_protected:
[ bits 16 ]
cli
switch_to_pm:
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pm
[ bits 32 ]
init_pm:
mov ax, DATA_SEG
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp , 0x90000
mov esp , ebp
sti
[BITS 32]
switch_to_real:
mov [0xb8000], byte 'D'
[BITS 32]
cli
mov eax, cr0
and eax, 0x7FFFFFFF ; clear PG bit
mov cr0, eax
xor eax,eax ; A convenient zero
mov cr3,eax ; Flush the TLB
mov eax, cr0
and eax, 0xFFFFFFFE ; clear PE bit
mov cr0, eax
lidt [idtr]
jmp 0:continue
[BITS 16]
continue:
sti
mov al, 'C'
mov ah, 0x0E
mov bh, 0x00
mov bl, 0x07
int 0x10
hlt
GDT:
idtr:
dw 0x3ff
dd 0
gdt_start:
gdt_null:
dd 0x0 ;
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 0x0
db 10011010b ; 1 st flags , type flags
db 11001111b ; 2 nd flags , Limit ( bits 16 -19)
db 0x0
gdt_data:
dw 0xffff
dw 0x0
db 0x0
db 10010010b ; 1 st flags , type flags
db 11001111b ; 2 nd flags , Limit ( bits 16 -19)
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
stage2.footer:
times 8192-($-stage2.header) db 0
how to compile:
nasm.bat minrep.asm -o disk_p1.bin
nasm minrep_st2.asm -o disk_p2.bin
copy /b disk_pt1.bin + disk_pt2.bin disk.img
call qemu disk.img
EDIT:
if i only do this to go protected, then movin to 0xb800 in real mode does clear the first character but doesn't print it correctly (blank char instead of byte 'Z')
[ bits 16 ]
cli
switch_to_pm:
lgdt [ gdt_descriptor ]
mov eax , cr0
or eax , 0x1
mov cr0 , eax
jmp CODE_SEG:init_pm
[ bits 32 ]
init_pm:
the print char 'Z' after sti in continue:
mov ax, 0xb800;
mov es, ax;
mov [es:0000], byte 'Z'