1; This code runs in 16-bit Real Mode when the computer starts.
2; It is responsible for loading the kernel and switching to 32-bit Protected Mode.
3
4[org 0x7c00] ; BIOS loads the bootloader at this memory address
5KERNEL_OFFSET equ 0x1000 ; Memory address where we will load our kernel
6
7; Save the boot drive index provided by BIOS in DL register
8mov [BOOT_DRIVE], dl
9
10; Initialize the stack pointer to a safe area in memory
11mov bp, 0x9000
12mov sp, bp
13
14; Load the kernel from the disk into memory
15call load_kernel
16
17; Switch to VGA Mode 13h: 320x200 resolution with 256 colors
18; This is a graphical mode where each byte in video memory is a pixel color
19mov ax, 0x0013
20int 0x10
21
22; Enter 32-bit Protected Mode
23call switch_to_pm
24
25; Infinite loop in case we ever return (we shouldn't)
26jmp $
27
28; Global Descriptor Table (GDT)
29; The GDT defines memory segments and their access rights for Protected Mode.
30gdt_start:
31 dd 0x0 ; The null descriptor is required at the beginning
32 dd 0x0
33
34gdt_code:
35 ; Code segment descriptor
36 dw 0xffff ; Limit (bits 0-15)
37 dw 0x0 ; Base (bits 0-15)
38 db 0x0 ; Base (bits 16-23)
39 db 10011010b ; Access byte: Present, Privilege 0, Code, Executable, Readable
40 db 11001111b ; Flags: 4KB granularity, 32-bit mode, Limit (bits 16-19)
41 db 0x0 ; Base (bits 24-31)
42
43gdt_data:
44 ; Data segment descriptor
45 dw 0xffff ; Limit (bits 0-15)
46 dw 0x0 ; Base (bits 0-15)
47 db 0x0 ; Base (bits 16-23)
48 db 10010010b ; Access byte: Present, Privilege 0, Data, Writable
49 db 11001111b ; Flags: 4KB granularity, 32-bit mode, Limit (bits 16-19)
50 db 0x0 ; Base (bits 24-31)
51
52gdt_end:
53
54; The GDT descriptor is passed to the LGDT instruction
55gdt_descriptor:
56 dw gdt_end - gdt_start - 1 ; Size of GDT (minus 1)
57 dd gdt_start ; Starting address of GDT
58
59; Constants for segment selectors (offsets within the GDT)
60CODE_SEG equ gdt_code - gdt_start
61DATA_SEG equ gdt_data - gdt_start
62
63; Switch to 32-bit Protected Mode
64[bits 16]
65switch_to_pm:
66 cli ; Disable interrupts while switching modes
67 lgdt [gdt_descriptor] ; Load the GDT descriptor
68 mov eax, cr0 ; Enable Protected Mode bit in Control Register 0
69 or eax, 0x1
70 mov cr0, eax
71 jmp CODE_SEG:init_pm ; Far jump to flush the CPU pipeline and switch to 32-bit mode
72
73[bits 32]
74init_pm:
75 ; Update all segment registers to use the new Data Segment selector
76 mov ax, DATA_SEG
77 mov ds, ax
78 mov ss, ax
79 mov es, ax
80 mov fs, ax
81 mov gs, ax
82
83 ; Re-initialize the stack for 32-bit mode at a higher memory address
84 mov ebp, 0x90000
85 mov esp, ebp
86
87 ; Call the entry point that leads to our kernel
88 call BEGIN_PM
89
90; Routine to load sectors from disk using BIOS interrupts
91[bits 16]
92load_kernel:
93 mov bx, KERNEL_OFFSET ; Destination address in memory
94 mov dh, 15 ; Number of sectors to load (adjust if kernel grows)
95 mov dl, [BOOT_DRIVE] ; Drive index to read from
96 call disk_load
97 ret
98
99disk_load:
100 push dx
101 mov ah, 0x02 ; BIOS Read Sector function
102 mov al, dh ; Number of sectors
103 mov ch, 0x00 ; Cylinder 0
104 mov dh, 0x00 ; Head 0
105 mov cl, 0x02 ; Sector 2 (Sector 1 is the bootloader)
106 int 0x13 ; Call BIOS disk interrupt
107 jc disk_error ; Jump if Carry Flag is set (indicates error)
108 pop dx
109 cmp dh, al ; Verify that we read the expected number of sectors
110 jne disk_error
111 ret
112
113disk_error:
114 ; Halt execution if disk read fails
115 jmp $
116
117; 32-bit entry point to jump into the compiled C kernel
118[bits 32]
119BEGIN_PM:
120 call KERNEL_OFFSET ; Jump to the memory location where kernel was loaded
121 jmp $ ; Safety hang
122
123; Boot sector padding and signature
124BOOT_DRIVE db 0
125times 510-($-$$) db 0 ; Pad the rest of the 512 bytes with zeros
126dw 0xaa55 ; The standard boot signature required by BIOS