I'm learning x86 assembly, and I'm trying to make a toy operating system in NASM, but I don't understand some things.
I made a bootloader that is successfully boots my kernel:
- Loads 14 sectors from the diskette which contains the kernel file;
- Search a file in these sectors labeled
kernel.feo
; - Loads that file into the memory to the offset
0x2000
; - Executes the kernel using a far jump
jmp 0x2000:0x0000
.
So I have the kernel code located at 0x2000:0
in the memory. CS
might be properly set because the using of a far jump. In this kernel code, I want to enter 32-bit protected mode, but I'm not sure how GDTs are working. When I run the code below on a virtual machine (QEMU)
, it is don't do anything.
I want to please you to help me entering 32-bit protected mode!
That said, you have the following problems:
- You assume the code is loaded at
0x7c00:0
due to theorg 0
, but that might not be the case. The only thing guaranteed is the physical address. You should use a far jump to your entry point so thatCS
is properly set.- You are for some reason setting
DS
to0x2000
so your code won't find any data at all. You should setDS
to matchCS
, or use aCS
override everywhere (not recommended).- The protected mode code assumes zero-based segment, which in turn means it expects
org 0x7c00
which of course conflicts with your setup. You should switch toorg 0x7c00
and segments0
.- The VGA text mode segment is at
0xb8000
not0xb80000
(one less zero).- You don't have the boot signature bytes
0x55 0xaa
at the end of the boot sector.
I have corrected these things in my code:
[org 0x0]
is corrected to[org 0x2000]
and segments are set to0
;DS
is corrected to0
instead of0x2000
, so now it matches withCS
;- VGA Text Mode segment is corrected to
0xb8000
;
But the code won't work with these corrections, it should print two strings but it don't do anything!
Note that this kernel code should not end with a boot signature 0x55 0xAA
, because it isn't a boot sector.
Here is the corrected kernel code (that not working):
[bits 16]
[org 0x2000]
jmp 0:kernel_start
gdt_start:
gdt_null:
dd 0x0
dd 0x0
gdt_code:
dw 0xffff
dw 0x0
db 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
dd gdt_start
CODE_SEG equ gdt_code - gdt_start
DATA_SEG equ gdt_data - gdt_start
print:
mov ah, 14
mov bh, 0
lodsb
cmp al, 0
je .done
int 0x10
jmp print
.done:
ret
uzenet_real db 'uzenet16', 0
uzenet_prot db 'uzenet32', 0
kernel_start:
mov ax, 0
mov ss, ax
mov sp, 0xFFFC
mov ax, 0
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov si, uzenet_real
call print
cli
lgdt[gdt_descriptor]
mov eax, cr0
or eax, 0x1
mov cr0, eax
jmp CODE_SEG:b32
[bits 32]
VIDEO_MEMORY equ 0xb8000
WHITE_ON_BLACK equ 0x0f
print32:
pusha
mov edx, VIDEO_MEMORY
.loop:
mov al, [ebx]
mov ah, WHITE_ON_BLACK
cmp al, 0
je .done
mov [edx], ax
add ebx, 1
add edx, 2
jmp .loop
.done:
popa
ret
b32:
mov ax, DATA_SEG
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov ebp, 0x90000
mov esp, ebp
mov ebx, uzenet_prot
call print32
jmp $