+ bits 64
+ org 1000h
+
+%include "src/serial.inc"
+
+BOOT_SERVICES equ 96
+BOOT_GET_MEMORY_MAP equ 56
+BOOT_EXIT_BOOT_SERVICES equ 232
+
+start:
+ mov [efihandle], rcx
+ mov [systab], rdx
+
+ ; retrieve memory map
+ ; args: RCX, RDX, R8, and R9.
+ lea rcx, [mmap_size]
+ lea rdx, [mmapbuf]
+ lea r8, [mmap_key]
+ lea r9, [mmap_descsz]
+ lea rax, [mmap_descver]
+ push rax
+ sub rsp, 32
+ mov rax, [systab]
+ mov rbx, [rax + BOOT_SERVICES]
+ call [rbx + BOOT_GET_MEMORY_MAP]
+ add rsp, 40
+ ; exit boot services
+ mov rcx, [efihandle]
+ mov rdx, [mmap_key]
+ mov rax, [systab]
+ mov rbx, [rax + BOOT_SERVICES]
+ call [rbx + BOOT_EXIT_BOOT_SERVICES]
+
+ ; move code to absolute 1000h
+ call get_rip
+.after_call:
+ sub rax, .after_call - start
+ mov rsi, rax ; source address
+ mov rdi, start ; destination
+ mov rcx, end - start
+ shr rcx, 2 ; dwords
+ cld
+ rep movsd ; copy code to make it absolute
+
+ mov rax, .abs
+ jmp rax
+.abs:
+ call setup_serial
+
+ ; switch to 32-bit compatibility long mode
+ lgdt [gdtlim]
+ push word 8
+ push qword start32
+ retfq
+
+get_rip:
+ mov rax, [rsp]
+ ret
+
+ ; serial port setup
+setup_serial:
+ ; set clock divisor
+ mov al, LCTL_DLAB
+ mov dx, UART_LCTL
+ out dx, al
+ mov ax, DIV_9600
+ mov dx, UART_DIVLO
+ out dx, al
+ mov al, ah
+ mov dx, UART_DIVHI
+ out dx, al
+ ; set format 8n1
+ mov al, LCTL_8N1
+ mov dx, UART_LCTL
+ out dx, al
+ ; clear and enable fifo
+ mov al, FIFO_ENABLE_CLEAR
+ mov dx, UART_FIFO
+ out dx, al
+ ; assert RTS and DTR
+ mov al, MCTL_DTR_RTS_OUT2
+ mov dx, UART_MCTL
+ out dx, al
+ ret
+
+
+; ----------- 32bit code ----------
+
+ bits 32
+start32:
+ mov ax, 10h
+ mov ds, ax
+ mov ss, ax
+ mov es, ax
+ mov gs, ax
+ mov fs, ax
+
+ ; disable paging to deactivate long mode
+ mov eax, cr0
+ and eax, 7fffffffh
+ mov cr0, eax
+
+ ; disable long mode (EFER.LME = 0)
+ ; TODO: EFER is MSR c0000080, LME is bit 8
+ mov ecx, 0c0000080h
+ rdmsr
+ and eax, 0fffffeffh
+ wrmsr
+
+ ; halt for ever
+ sti
+halt: hlt
+ jmp halt
+
+ser_putchar:
+ mov ah, al
+ ; wait until transmit register is empty
+ mov dx, UART_LSTAT
+.wait: in al, dx
+ and al, LST_TREG_EMPTY
+ jz .wait
+ mov dx, UART_DATA
+ mov al, ah
+ out dx, al
+ ret
+
+ser_putstr:
+ mov al, [si]
+ test al, al
+ jz .done
+ call ser_putchar
+ inc si
+ jmp ser_putstr
+.done: ret
+
+
+; ---------- data ------------
+
+ align 8
+efihandle dq 0
+systab dq 0
+; memory map data
+mmap_size dq 4096
+mmap_key dq 0
+mmap_descsz dq 0
+mmap_descver dq 0
+
+ align 4096
+mmapbuf: times 4096 db 0
+
+str_hello db 'hello!',13,10,0
+
+ align 4
+gdtlim dw 31
+gdtbase dq gdt
+
+ align 8
+gdt: ; 0: null segment
+ dd 0, 0
+ ; 1: code - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:code/non-conf/rd
+ dd 0x0000ffff
+ dd 0x00cf9a00
+ ; 2: data - base:0, lim:4g, G:4k, 32bit, avl, pres|app, dpl:0, type:data/rw
+ dd 0x0000ffff
+ dd 0x00cf9200
+ ; 3: code16
+ dd 0x0000ffff
+ dd 0x00009a00
+
+ align 4
+end:
+; vi:ft=nasm: