boot.asm (6022B)
1 ; Load code to 0x7c00 (Boot Sector address). 2 [org 0x7c00] 3 [bits 16] 4 5 MAGICOFF equ (0x7c00 + 510) 6 KERNOFF equ 0x1000 7 8 section .text 9 global _start 10 11 ; Entry point. 12 _start: 13 cli ; Disable interrupts. 14 xor ax, ax ; Clear segment registers. 15 mov ds, ax 16 mov es, ax 17 mov fs, ax 18 mov gs, ax 19 mov ss, ax 20 mov bp, 0x9000 ; Set the base and stack pointers. 21 mov sp, bp 22 cld ; Read strings from low to high. 23 sti ; Enable interrupts back. 24 25 call a20_test 26 call a20_enable 27 call kernel_load ; Read kernel from disk to memory. 28 jmp pm_enter ; Enter 32-bit Protected Mode. 29 jmp $ ; Safety hang. 30 31 ; Test to see if the A20 line is enabled. 32 ; As a reference we will use the magic number 0xaa55 since we know it's always 33 ; going to have the same value and be located at address 0x7dfe (0x7c00 + 510). 34 ; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled. 35 a20_test: 36 pusha 37 38 mov ax, [MAGICOFF] ; The magic number is there. 39 mov dx, ax 40 41 ; We'll try to advance 1MB in memory. If the end result hasn't wrapped up 42 ; around zero, it means that our processor is able to access more than 1MB 43 ; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected 44 ; address after the 1MB advance should be: 45 ; 46 ; 0x100000 + 0x7dfe = 0x107dfe 47 ; 48 ; The formula for memory segmentation is: 49 ; 50 ; address = segment * 16 + offset 51 ; 52 ; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`. 53 ; Applying the formula to calculate our offset, we have: 54 ; 55 ; 0xffff0 + offset = 0x107dfe => 56 ; offset = 0x107dfe - 0xffff0 => 57 ; offset = 0x7e0e 58 59 push bx 60 mov bx, 0xffff 61 mov es, bx ; Assign value to the segment register. 62 pop bx 63 64 mov bx, 0x7e0e 65 mov dx, [es:bx] ; If the A20 line is disabled we get 0xaa55. 66 67 cmp ax, dx 68 je cont ; If they're equal, the A20 line might be disabled. 69 popa 70 mov ax, 0x01 ; Success code 1. 71 ret 72 cont: 73 mov ax, [0x7dff] 74 mov dx, ax 75 76 push bx 77 mov bx, 0xffff ; Make another segment. 78 mov es, bx 79 pop bx 80 81 mov bx, 0x7e0f 82 mov dx, [es:bx] 83 84 cmp ax, dx ; Now it really is disabled if ax == [es:bx]. 85 je exit 86 popa 87 mov ax, 0x01 ; Success code 1. 88 ret 89 exit: 90 popa 91 xor ax, ax ; Error code 0. 92 ret 93 94 ; Enable the A20 line. We'll try enabling it using the following ways: 95 ; - BIOS interrupt. 96 ; - Keyboard controller. 97 ; - FastA20. 98 a20_enable: 99 pusha 100 101 ; BIOS interrupt. 102 mov ax, 0x2401 ; A20-Gate Active. 103 int 0x15 104 105 call a20_test 106 cmp ax, 0x01 107 je a20_done 108 jmp a20_fail 109 110 ; Keyboard controller. 111 sti ; Enable interrupts. 112 113 call a20_waitc 114 mov al, 0xad ; Disable controller. 115 out 0x64, al ; Send data to port 0x64. 116 call a20_waitc 117 mov al, 0xd0 ; We want to read from the controller. 118 out 0x64, al 119 120 call a20_waitd 121 in al, 0x60 ; Read data from port 0x60. 122 push ax ; Save data. 123 124 call a20_waitc 125 mov al, 0xd1 ; We want to send data. 126 out 0x64, al 127 128 call a20_waitc 129 pop ax 130 or al, 0x02 ; Write the second bit back. 131 out 0x60, al ; Send data to the data port. 132 133 call a20_waitc 134 mov al, 0xae ; Enable controller. 135 out 0x64, al 136 137 call a20_waitc 138 sti ; Enable interrupts again. 139 140 call a20_test 141 cmp ax, 0x01 142 je a20_done 143 jmp a20_fail 144 145 ; Wait for the keyboard controller to be available for receiving commands. 146 a20_waitc: 147 in al, 0x64 ; Read from port 0x64. 148 test al, 0x02 ; Test the second bit. 149 jnz a20_waitc ; If it's 1, it's busy. 150 ret 151 ; Wait for the keyboard controller to send data back. 152 a20_waitd: 153 in al, 0x64 ; Read from port 0x64 again. 154 test al, 0x01 ; Test the first bit. 155 jz a20_waitd ; If it's 1, the data is not ready to be sent. 156 ret 157 158 ; FastA20 159 in al, 0x92 ; FastA20 uses port 0x92. 160 or al, 0x02 ; Mask second bit. 161 out 0x92, al ; Send data back to port 0x92. 162 163 call a20_test 164 cmp ax, 0x01 165 je a20_done 166 jmp a20_fail 167 168 ; The A20 is enabled. Go on. 169 a20_done: 170 popa 171 ret 172 173 ; The A20 is disabled. 174 a20_fail: 175 mov si, str_a20_fail 176 call puts 177 jmp $ 178 179 ; Load kernel from disk to memory. 180 kernel_load: 181 pusha 182 mov ah, 0x42 ; BIOS read disk extended function. 183 mov dl, [BOOTDRV] ; Set boot drive. 184 mov si, disk_packet 185 int 0x13 ; BIOS read disk interrupt. 186 187 jc diskerr ; There's an error. Too bad. 188 popa 189 ret 190 diskerr: 191 mov si, str_diskerr 192 call puts 193 jmp $ ; There's nothing we can do at this point. 194 195 disk_packet: 196 .size db 0x10 197 .zero db 0x00 198 .count dw 0x0f 199 .off16 dw KERNOFF 200 .seg16 dw 0x0000 201 .lba dq 1 202 203 ; Set up the GDT (Global Descriptor Table). 204 gdt: 205 gdt_null: 206 dq 0x00000000 207 208 gdt_kernel_code: 209 dw 0xffff 210 dw 0x0000 211 db 0x00 212 db 10011010b 213 db 11001111b 214 db 0x00 215 216 gdt_kernel_data: 217 dw 0xffff 218 dw 0x0000 219 db 0x00 220 db 10010010b 221 db 11001111b 222 db 0x00 223 224 gdt_userland_code: 225 dw 0xffff 226 dw 0x0000 227 db 0x00 228 db 11111010b 229 db 11001111b 230 db 0x00 231 232 gdt_userland_data: 233 dw 0xffff 234 dw 0x0000 235 db 0x00 236 db 11110010b 237 db 11001111b 238 db 0x00 239 240 gdt_ptr: 241 dw $ - gdt - 1 242 dd gdt 243 244 GDT_CODESEG equ gdt_kernel_code - gdt 245 GDT_DATASEG equ gdt_kernel_data - gdt 246 247 ; Set up the GDT and go into Protected Mode. 248 pm_enter: 249 cli ; Disable BIOS interrupts. 250 lgdt [gdt_ptr] ; Load the GDT. 251 mov eax, cr0 252 or eax, 1 ; Protected Mode Enable. 253 mov cr0, eax ; There's no going back now. 254 jmp GDT_CODESEG:pm_init 255 256 ; We're in Protected Mode. 257 [bits 32] 258 pm_init: 259 mov ax, GDT_DATASEG 260 mov ds, ax 261 mov es, ax 262 mov fs, ax 263 mov gs, ax 264 mov ss, ax 265 266 mov ebp, 0x90000 267 mov esp, ebp 268 269 call kernel_exec 270 jmp $ 271 272 ; Hand control over to the C kernel. Godspeed! You Black Emperor. 273 kernel_exec: 274 call KERNOFF 275 jmp $ 276 277 ; Print a null-terminated string. 278 [bits 16] 279 puts: 280 pusha 281 mov ah, 0x0e ; Tell BIOS to display a character. 282 loop: 283 mov al, [si] ; Load character. 284 cmp al, 0 ; Check for \0. 285 jne putchar ; If it's not \0, print the character. 286 popa ; We're done, pop everything. 287 ret ; Return back to where we were. 288 putchar: 289 int 0x10 ; BIOS print interrupt. 290 inc si ; `str++` 291 jmp loop ; Go to the next character. 292 293 ; String declarations. 294 str_diskerr: db "Error loading disk.", 0x0a, 0x0d, 0x00 295 str_a20_fail: db "The A20 Line is disabled", 0x0a, 0x0d, 0x00 296 297 BOOTDRV: db 0x80 298 299 ; Padding to 512 bytes. The last 2 bytes will come from the magic number. 300 times 510 - ($ - $$) db 0 301 ; Magic number. 302 dw 0xaa55