Skip to content

Instantly share code, notes, and snippets.

@jondlove
Created February 9, 2022 13:09
Show Gist options
  • Save jondlove/3c048f1701bfba13bd802b3fc9d2a783 to your computer and use it in GitHub Desktop.
Save jondlove/3c048f1701bfba13bd802b3fc9d2a783 to your computer and use it in GitHub Desktop.
A simple bootloader for booting on non-EFI x86 systems
[bits 16]
; X86_BOOT.ASM
;
; Written by Jonathan Love, 2017
; In my third/fourth/more attempt at actually getting to Protected Mode
;
; Bootloader for x86 MBR booting
;
; For interrupt codes, refer http://www.ctyme.com/intr/int.htm
; For data ports, refer to Intel's PCH Documentation
; For general x86 details, refer to Intel's SDM Documentation
; Current State:
; Mode: Real
; A20 Line: No
; Stack: No
; Put memory into known configuration
; Data segment is within our loaded code, at 0x07c0
mov ax, 0x07c0
mov ds, ax
; Configure our stack to *end* right after our bootloader
mov ax, 0x07e0
mov ss, ax
; Gives us 8K of stack space
mov sp, 0x2000
; Current State:
; Mode: Real
; A20 Line: No
; Stack: 0x7e00 -> 0x9E00
; Print some words on the screen
call clear_screen
push 0x0000
call movecursor
push msg_boot
call print
push msg_newline
call print
; Current State:
; Mode: Real
; A20 Line: No
; Stack: 0x7e00 -> 0x9E00
; Enable the A20 line, so we can access >1MB RAM
; We use FAST A20, and assume A20 is disabled
; There are many caveats. We ignore these for now
; Ref. Intel PCH, "PORT92"
in al, 0x92
or al, 2 ; Bit 1 on PORT92 controls the ALT_A20_GATE
out 0x92, al ; Special Port
; Current State:
; Mode: Real
; A20 Line: Yes
; Stack: 0x7e00 -> 0x9E00
; Let's use some protection
push msg_protected
call print
push msg_newline
call print
; We'll use a protected, flat, unpaged model
; The kernel can change as necessary
; Ref: Intel SDM, Vol 3A. Ch 3.
cli ; Disable interrupts
lgdt [gdtr] ; Load GDT Register
mov eax, cr0 ; Load control register flags
or al, 1 ; Set (PE) Protection Enable
mov cr0, eax
; Current State:
; Mode: Protected
hlt
clear_screen:
; Clears the screen
;
; @param none
; @returns none
push bp
mov bp, sp
pusha
mov ah, 0x07 ; Int 10h: scroll window
mov al, 0x00 ; Scroll window far enough to clear
mov bh, 0x07 ; White on black
mov cx, 0x0000 ; Top left of screen
mov dh, 0x18 ; 18h = 24 rows
mov dl, 0x4f ; 4fh = 79 cols
int 0x10 ; Interrupt: video, scroll window down
popa
mov sp, bp
pop bp
ret
movecursor:
; Moves the cursor to a specified location
;
; @param word Location to move to
; @returns none
push bp
mov bp, sp
pusha
mov dx, [bp+4] ; Get the first parameter
mov ah, 0x02 ; Int 10h: Set cursor position
mov bh, 0x00 ; Screen page
int 0x10
popa
mov bp, sp
pop bp
ret
print:
; Print string to screen
;
; @param word Address of message to write
; @returns none
push bp
mov bp, sp
pusha
mov si, [bp+4] ; Get the first parameter, put in source index
mov bh, 0x00 ; Screen page
mov bl, 0x00 ; Foreground colour
mov ah, 0x0e ; Int 10h: write char
.char:
mov al, [si] ; Get current char from source index
add si, 1 ; Move to next char
or al, 0
je .return ; Finish printing, if we've reached null terminator
int 0x10 ; Else, print char
jmp .char
.return:
popa
mov sp, bp
pop bp
ret
msg_boot: db "Booting...", 0 ; Null terminated string
msg_newline: db 10, 13, 0
msg_protected: db "Loading Protected Mode", 0
gdtr:
; Global Descriptor Table
; Intel SDM Vol 3A. Ch 3.
; Contains a null segment, a code segment, and a data segment
; We have no task-state-segment (TSS) because we don't have
; multi-tasking. There is also no Thread Local Storage
; We have no userland segments, as there is no userland.
; Null Segment
dq 0
; Kernel Code Segment
dw 0xffff ; Segment limit 0:15
dw 0x0000 ; Base 0:15
db 0x00 ; Base 16:23
db 10011010b ; Access Byte:
; Present: 1
; Priv Lvl: 00
; Descriptor: 1 (code/data)
; Type:
; Executable: 1 (yes, code segment)
; Conforming: 0 (can only access in specified privlvl)
; Read/Write: 1 (Code sector is readable)
; Accessed: 0 (Segment has not been accessed)
db 11001111b ; Flags/Limit 16:19:
; Flags
; Granularity: 1 (4 KiB blocks)
; Size: 1 (32-bit protected mode)
; 64-bit segment: 0 (No)
; [Reserved]: 0
; Limit 16:19: 1111
db 0x00 ; Base 24:31
; Kernel Data Segment
dw 0xffff ; Segment limit 0:15
dw 0x0000 ; Base 0:15
db 0x00 ; Base 16:23
db 10010010b ; Access Byte:
; Present: 1
; Priv Lvl: 00
; Descriptor: 1 (code/data)
; Type:
; Executable: 0 (no, data segment)
; Conforming: 0 (can only access in specified privlvl)
; Read/Write: 1 (Data sector is read/writeable)
; Accessed: 0 (Segment has not been accessed)
db 11001111b ; Flags/Limit 16:19:
; Flags
; Granularity: 1 (4 KiB blocks)
; Size: 1 (32-bit protected mode)
; 64-bit segment: 0 (No)
; [Reserved]: 0
; Limit 16:19: 1111
db 0x00 ; Base 24:31
; Padding, to ensure this code, when compiled, is 512 bytes
times 510-($-$$) db 0
; Signature to indicate bootable MBR partition
dw 0xAA55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment