.code16
.intel_syntax noprefix
.section .boot, "ax", @progbits
// Disable interrupts.
cli
// Clear segment selectors.
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov fs, ax
mov gs, ax
// Set cs to 0x0000, as some BIOSes load the MBR to either 07c0:0000 or 0000:7c000.
jmp 0x0000:entry_rm16
entry_rm16:
// Set video mode 3, see [1].
// * 80x25 text mode
// * 640x200 pixel resolution (8x8 pixel per char)
// * 16 colors (4bit)
// * 4 pages
// * 0xB800 screen address
//
// [1] http://www.ctyme.com/intr/rb-0069.htm
mov ax, 0x3
int 0x10
// Move cursor to second row.
// http://www.ctyme.com/intr/rb-0087.htm
mov ah, 0x02
mov bh, 0 // page
mov dh, 1 // row
mov dl, 0 // col
int 0x10
// Clear direction flag for lodsb below.
cld
// Load pointer to msg_rm string (null terminated).
lea si, [msg_rm]
// Teletype output char at current cursor position.
// http://www.ctyme.com/intr/rb-0106.htm
mov ah, 0x0e
1:
lodsb // al <- ds:si ; si+=1 ; (al char to write)
test al,al // test for null terminator
jz 2f
int 0x10
jmp 1b
2:
// Enable A20 address line.
in al, 0x92
or al, 2
out 0x92, al
// Load GDT descriptor.
lgdt [gdt_desc]
// Enable protected mode (set CR0.PE bit).
mov eax, cr0
or eax, (1 << 0)
mov cr0, eax
// Far jump which loads segment selector (0x0008) into cs.
// 0x0008 -> RPL=0, TI=0(GDT), I=1
jmp 0x0008:entry_pm32
.code32
entry_pm32:
// Select data segment selector (0x0010) for ds.
mov ax, gdt_data - gdt
mov ds, ax
// Write through VGA interface (video memory).
// Each character is represented by 2 bytes.
// 4 bit bg | 4 bit fg | 8 bit ascii char
//
// Start writing at third line.
mov edi, 0xb8000 + (80 * 2 * 2) //
lea esi, [msg_pm]
1:
lodsb // al <- ds:esi ; esi+=1
test al, al // test for null terminator
jz 2f
or eax, 0x1f00 // blue bg, white fg
stosw // ds:[edi] <- ax; edi+=2
jmp 1b
2:
hlt
jmp 2b
// For simplicity keep data used by boot sector in the same section.
.balign 8
msg_rm:
.asciz "Hello from Real Mode!"
msg_pm:
.asciz "Hello from Protected Mode!"
.balign 8
gdt:
.8byte 0x0000000000000000 // 0x00 | null descriptor
.8byte 0x00cf9a000000ffff // 0x08 | 32 bit, code (rx), present, dpl=0, g=4K, base=0, limit=fffff
gdt_data:
.8byte 0x00cf92000000ffff // 0x10 | 32 bit, data (rw), present, dpl=0, g=4K, base=0, limit=fffff
gdt_desc:
.2byte .-gdt-1 // size
.4byte gdt // address
// Write MBR boot magic value.
.fill 510 - (. - .boot), 1, 0x00
.2byte 0xaa55