.code16
main:
	ljmp $0x0,$entry

entry:
	/* Set up initial segments */
	xor %ax, %ax
	mov %ax, %ds
	mov %ax, %ss

	/* Don't lose dl */
	mov %dl, boot_disk

	/* Initialize stack to just below us */
	mov $0x7c00, %ax
	mov %ax, %sp

	/* Prepare to switch to unreal mode */
	cli
	push %ds
	push %es

	/* Enable A20 */
	in $0x92, %al
	or $2, %al
	out %al, $0x92

	/* Switch to unreal mode */
	lgdtw gdtr
	mov %cr0, %eax
	or $1, %al
	mov %eax, %cr0
	jmp pmode
pmode:
	mov $0x10, %bx
	mov %bx, %ds
	mov %bx, %es
	and $0xfe, %al
	mov %eax, %cr0
	jmp unrealmode
unrealmode:
	pop %es
	pop %ds

	/* Clear the screen */
	mov  $0, %al
	movl $3840, %ecx
	movl $0xb8000, %edi
	addr32 rep stosb

	/* Check if we can actually go to long mode on this */
	mov $0x80000001, %eax
	cpuid
	and $0x20000000, %edx
	jnz can_long

	movl $str_Need_long, %esi
	call print_string
	jmp _oh_no

can_long:
	/* Spot check memory */
	movl $0x12345678, %eax
	movl $0x5000000, %ebx
	movl %eax, (%ebx)
	movl (%ebx), %edx
	cmp %edx, %eax
	jz good_memory

	movl $str_More_mem, %esi
	call print_string

_oh_no:
	jmp _oh_no

good_memory:
	/* Ask for drive params */
	mov $0x48, %ah
	mov boot_disk, %dl
	mov $drive_params, %si
	int $0x13

.extern _bss_start

boot_from_cd:
	/* Collect information on lower memory. */
	mov $0x500, %ax
	mov %ax, %es
	clc
	int $0x12
	mov %ax, lower_mem

	/* Collect information on upper memory. */
	mov $0x0, %di
	call do_e820
	jc hang

	/* Get video mode info */
	mov $0, %ax
	mov %ax, %es
	mov $vbe_cont_info, %di
	mov $0x4F00, %ax
	int $0x10

	/* Actually switch to protected mode. */
	mov %cr0, %eax
	or $1, %eax
	mov %eax, %cr0

	mov $0x10, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss

	cli

.global bios_main
	ljmp $0x08,$bios_main

hang:
	jmp hang

do_e820:
	xor %ebx, %ebx
	xor %bp, %bp
	mov $0x534D4150, %edx
	mov $0xe820, %eax
	movl $0x1,%es:20(%di)
	mov $24, %ecx
	int $0x15
	jb  do_e820.failed
	mov $0x534D4150, %edx
	cmp %edx, %eax
	jne do_e820.failed
	test %ebx, %ebx
	je  do_e820.failed
	jmp do_e820.jmpin
do_e820.e820lp:
	mov $0xe820, %eax
	movl $0x1,%es:20(%di)
	mov $24, %ecx
	int $0x15
	jb do_e820.e820f
	mov $0x534D4150, %edx
do_e820.jmpin:
	jcxz do_e820.skipent
	cmp $20, %cl
	jbe do_e820.notext
	testb $0x1, %es:20(%di)
	je do_e820.skipent
do_e820.notext:
	mov %es:8(%di), %ecx
	or %es:12(%di), %ecx
	jz do_e820.skipent
	inc %bp
	add $24, %di
do_e820.skipent:
	test %ebx, %ebx
	jne do_e820.e820lp
do_e820.e820f:
	mov %bp, mmap_ent
	clc
	ret
do_e820.failed:
	stc
	ret

print_string:
	movl $0xb8000, %edi
print_string.loop:
	movb (%esi), %ah
	cmp $0, %ah
	je print_string.exit
	movb %ah, (%edi)
	inc %edi
	movb $7, (%edi)
	inc %esi
	inc %edi
	jmp print_string.loop
print_string.exit:
	ret

pm_stack:
	.quad 0

.global do_bios_call
do_bios_call:
.code32
	/* Standard function entry point stuff */
	push %ebp
	mov  %esp, %ebp
	push %eax
	push %ebx
	push %ecx
	push %edx
	push %esi
	push %edi
	/* Save stack because bios might mess it up? */
	movl %esp, %eax
	movl %eax, (pm_stack)

	/* Prepare intermediary mode */
	mov $0x20, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss

	/* Enable intermediary mode */
	ljmp $0x18,$do_bios_call.0

do_bios_call.0:
.code16
	/* Disable protected mode */
	mov %cr0, %eax
	and $~1, %eax
	mov %eax, %cr0

	/* Jump to deactivate protected mode */
	ljmp $0x0,$do_bios_call.1

do_bios_call.1:
	/* Set up real mode segments */
	xor %ax, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss

	/* Enable interrupts while BIOS is active */
	sti

	/* Function switch */
	movl 32(%esp), %ebx

	/* 1: Read disk */
	mov $0x01, %ax
	cmp %bx, %ax
	je do_bios_call.read_disk

	/* 2: Query mode index */
	mov $0x02, %ax
	cmp %bx, %ax
	je do_bios_call.query_mode

	/* 3: Set mode */
	mov $0x03, %ax
	cmp %bx, %ax
	je do_bios_call.set_mode

	mov $0x04, %ax
	cmp %bx, %ax
	je do_bios_call.test_key

	/* Else: Bad call, jump to loop. */
	jmp do_bios_call.done

do_bios_call.read_disk:
	mov $0x42, %ah     /* Extended read */
	mov boot_disk, %dl /* Using our boot disk */
	mov $dap, %si      /* From the DAP below */
	int $0x13
	jmp do_bios_call.done

do_bios_call.query_mode:
	movl 36(%esp), %ecx
	mov $0x0, %ax
	mov %ax, %es
	mov $vbe_info, %edi
	mov $0x4F01, %ax
	int $0x10
	jmp do_bios_call.done

do_bios_call.set_mode:
	movl 36(%esp), %ebx
	mov $0x4F02, %ax
	int $0x10
	jmp do_bios_call.done

do_bios_call.test_key:
	movl 36(%esp), %ebx
	xor %ax, %ax
	mov %bl, %ah
	int $0x16
	movl %eax, 20(%esp)
	jmp do_bios_call.done

do_bios_call.done:
	/* Disable interrupts again */
	cli

	/* Restore data segment, gdt */
	xor %ax,%ax
	mov %ax, %ds
	lgdtw gdtr

	/* Enable protected mode */
	mov %cr0, %eax
	or $1, %eax
	mov %eax, %cr0

	/* Jump to activate protected mode */
	ljmp $0x08,$do_bios_call.2

do_bios_call.2:
.code32
	/* Restore protected mode data segments */
	mov $0x10, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %fs
	mov %ax, %gs
	mov %ax, %ss

	/* Restore stack */
	movl (pm_stack), %eax
	movl %eax, %esp

	/* Pop callee-saved registers we may messed with */
	pop %edi
	pop %esi
	pop %edx
	pop %ecx
	pop %ebx
	pop %eax
	pop %ebp

	/* Return */
	ret

.align 8
gdtr:
	.word gdt_end - gdt_base - 1
	.long gdt_base

gdt_base:
	.quad 0
	.word 0xFFFF
	.word 0
	.byte 0
	.byte 0x9a
	.byte 0xcf
	.byte 0

	.word 0xffff
	.word 0
	.byte 0
	.byte 0x92
	.byte 0xcf
	.byte 0

	.word 0xffff
	.word 0
	.byte 0
	.byte 0x9e
	.byte 0
	.byte 0

	.word 0xffff
	.word 0
	.byte 0
	.byte 0x92
	.byte 0
	.byte 0
gdt_end:

.global boot_disk
boot_disk:
	.byte 0

.global mmap_ent
mmap_ent:
	.byte 0
	.byte 0

.global lower_mem
lower_mem:
	.byte 0
	.byte 0

.align 4
.global dap
dap:
	.byte 16
	.byte 0 /* always 0 */
.global dap_sectors
dap_sectors:
	.word 1
.global dap_buffer
dap_buffer:
	.long 0x0
.global dap_lba_low
dap_lba_low:
	.long 0
.global dap_lba_high
dap_lba_high:
	.long 0

.align 4
drive_params:
	.word 0x1A
	.word 0 /* flags */
	.long 0 /* cylinders */
	.long 0 /* heads */
	.long 0 /* sectors */
	.quad 0 /* total sectors */
.global drive_params_bps
drive_params_bps:
	.word 0 /* bytes per sector */

.align 4
.global vbe_info
vbe_info:
	.word 0 /* attributes */
	.word 0 /* old shit (window a/b) */
	.word 0 /* Granulatory of banks, don't care. */
	.word 0 /* Window size, don't care. */
	.long 0 /* Segments... */
	.long 0 /* old bank switching thing */
.global vbe_info_pitch
vbe_info_pitch:
	.word 0 /* PITCH */
.global vbe_info_width
vbe_info_width:
	.word 0 /* WIDTH */
.global vbe_info_height
vbe_info_height:
	.word 0 /* HEIGHT */
	.word 0 /* w, y */
	.byte 0 /* planes */
.global vbe_info_bpp
vbe_info_bpp:
	.byte 0 /* bits per pixel */
	.byte 0 /* banks */
	.byte 0 /* Memory model */
	.byte 0 /* bank size */
	.byte 0 /* pages */
	.byte 0 /* reserved */
	.byte 0 /* RED mask */
	.byte 0 /* RED offset */
	.byte 0 /* GREEN mask */
	.byte 0 /* GREEN offset */
	.byte 0 /* BLUE maask */
	.byte 0 /* BLUE offset */
	.byte 0 /* ALPHA mask */
	.byte 0 /* ALPHA offset */
	.byte 0 /* Color attributes */
.global vbe_info_fbaddr
vbe_info_fbaddr:
	.long 0 /* Framebuffer address */
	.long 0 /* Extra memory offset */
	.word 0 /* Extra memory size */
	.zero 206 /* Other crap */

.align 4
vbe_cont_info:
	.ascii "VBE2"
	.word 0x200
	.long 0
	.long 0 /* caps */
.global vbe_cont_info_mode_off
vbe_cont_info_mode_off:
	.word 0 /* MODES */
vbe_cont_info_mode_seg:
	.word 0
	.zero 494

str_Need_long:
	.asciz "ToaruOS 2.0 requires a 64-bit processor."
str_More_mem:
	.asciz "ToaruOS 2.0 needs at least 128MiB of RAM, and 1GiB is recommended."

.global disk_space
disk_space:
	.zero 2048
