+switch_to_pm
+ mov bx, 0B800h
+ mov es, bx ; set extra segment to starting address of video RAM
+ mov byte [es:0], 'L' ; print an L to screen, to let us know that we actually got here
+
+ cli ; disable interrupts
+ xor ax, ax ; clear accumulator
+ mov ds, ax ; clear data segment, this makes sure the next instruction reads from the right place
+ lgdt [gdt_desc] ; load the Global Descriptor Table
+
+ mov eax, cr0 ; move Control Register 0 to accumulator
+ or eax, 1 ; flip the bit which controls memory protection
+ mov cr0, eax ; move the accumulator back to CR0
+ jmp CODE_SEG:protected ; not quite there yet, need to do a far jump to clean the pipeline from any 16-bit instructions
+ ; note that the jump does not have to be physically far away, it just needs to use a segmented address
+
+ bits 32 ; we are finally in 32-bit mode
+protected
+ mov ax, DATA_SEG ; put the selector for the data segment in the accumulator
+ ; in real mode, segmentation works by taking one of these segment registers, shifting it right by 4 bits or 1 hex digit
+ ; and then adding a 16-bit offset to form a 20-bit address
+ ; example: 1234h:5678h
+ ; 1234h is shifted to 12340h, 12340h + 5678h is 179B8h
+ ; in 32-bit protected mode, these segment registers do not hold the segment address itself, but a selector in the GDT
+ ; so we have to update the segment registers accordingly
+ mov ds, ax
+ mov ss, ax
+ mov es, ax
+ mov fs, ax
+ mov gs, ax
+
+ ; reinitialize the stack at a safe location
+ mov ebp, 090000h
+ mov esp, ebp
+
+ ; print a string to let us know that we survived the switch
+ mov ebx, pm_success
+ call pmprint
+
+ ; transfer control to the kernel
+ call KERNEL_OFFSET
+
+ ; above call should not return in normal circumstances, but if it does hang forever
+ jmp $
+
+pmprint
+ pusha ; save registers to stack
+ mov edx, video_memory ; initialize dx with location of VRAM
+
+.loop
+ mov al, [ebx] ; read next char and put it in al
+ mov ah, 00000111b ; puts the VGA text mode color white on black into ah
+
+ cmp al, 0 ; if the next character is null, we reached end of string
+ je .done ; so return the instruction
+
+ mov [edx], al ; otherwise put the next character in the video memory
+ mov [edx+1], ah ; do the same for its color
+
+ inc ebx ; point to next character in string
+ add edx, 2 ; point to next character in VRAM
+
+ jmp .loop ; go back to the loop
+
+.done
+ popa ; restore registers from stack
+ ret ; return
+
+pm_success db "Now in protected mode", 0
+video_memory equ 0B8000h
+
+; the actual Global Descriptor Table
+; refer to Volume 3, Chapter 2, 2.1.1 of Intel's 64 and IA-32 Software Developer's Manual for more info
+gdt_start
+null_seg
+ dq 0
+code_seg
+ dw 0FFFFh
+ dw 0
+ db 0
+ db 10011010b
+ db 11001111b
+ db 0
+data_seg
+ dw 0FFFFh
+ dw 0
+ db 0
+ db 10010010b
+ db 11001111b
+ db 0
+gdt_end
+gdt_desc
+ dw gdt_end - gdt_start - 1
+ dd gdt_start
+CODE_SEG equ code_seg - gdt_start
+DATA_SEG equ data_seg - gdt_start