+ mov ax, LOADER_PT_BASE / 16
+ mov es, ax
+ mov eax, 0x7
+ mov cx, 0x400
+1: stosd
+ add eax, 0x1000
+ loop 1b
+
+# Set page directory base register.
+
+ mov eax, LOADER_PD_BASE
+ mov cr3, eax
+
+#### Switch to protected mode.
+
+# Then we point the GDTR to our GDT. Protected mode requires a GDT.
+# We need a data32 prefix to ensure that all 32 bits of the GDT
+# descriptor are loaded (default is to load only 24 bits).
+
+ data32 lgdt gdtdesc
+
+# Then we turn on the following bits in CR0:
+# PE (Protect Enable): this turns on protected mode.
+# PG (Paging): turns on paging.
+# WP (Write Protect): if unset, ring 0 code ignores
+# write-protect bits in page tables (!).
+# EM (Emulation): forces floating-point instructions to trap.
+# We don't support floating point.
+
+#define CR0_PE 0x00000001
+#define CR0_EM 0x00000004
+#define CR0_PG 0x80000000
+#define CR0_WP 0x00010000
+
+ mov eax, cr0
+ or eax, CR0_PE + CR0_PG + CR0_WP + CR0_EM
+ mov cr0, eax
+
+# We're now in protected mode in a 16-bit segment. The CPU still has
+# the real-mode code segment cached in cs's segment descriptor. We
+# need to reload cs, and the easiest way is to use a far jump.
+# Because we're not in a 32-bit segment the data32 prefix is needed to
+# jump to a 32-bit offset.
+
+ data32 ljmp SEL_KCSEG, 1f + LOADER_PHYS_BASE
+
+# We're now in protected mode in a 32-bit segment.
+
+ .code32
+
+# Reload all the other segment registers and the stack pointer to
+# point into our new GDT.
+
+1: mov ax, SEL_KDSEG
+ mov ds, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+ mov ss, ax
+ add esp, LOADER_PHYS_BASE
+
+#### Jump to kernel entry point.
+
+ mov eax, main
+ call eax