
%include "bootdef.inc"

%define VESA_MODEINFO_SEG 0x0120
%define VESA_MODELIST_SEG 0x0140
%define GOOD_MODELIST_SEG 0x0160

[bits 16]

org BOOT_STAGE2_ADD


;k now read root directory
	mov	bx, BOOT_ROOT_SEG
	mov	ds, bx
	xor	si, si			;k now ds:si points to beginning of root directory
	mov	es, si
	mov	cx, 224			;max root entries
loop_compare:
	mov	di, kernel
	push	cx
	push	si			;save pointer to root dir entry
	mov	cx, 11
loop_name:
	cmpsb
	loopz	loop_name
	jnz	goon			;cx didn't get to zero, bad file
	pop	si
	pop	cx
	jmp	found_file		;good file, ds:si points to start of root directory entry
goon:
	pop	si
	add	si, 32
	pop	cx
	loop	loop_compare

error:
	jmp	$			;halt! no kernel file found!

found_file:				;ds:si points to root dir entry of kernel file
	xor	ax, ax
	mov	gs, ax
	mov	ax, [ds:si+26]
	mov	bx, BOOT_FAT_SEG
	mov	ds, bx			;ds points to beginning of FAT
	mov	edi, BOOT_KERNEL_ADD

readkernel_loop:
	cmp	ax, 0xff7
	jg	readkernel_done
	push	ax
	call	getCHSfromCluster
	mov	ax, 0x0201
	mov	dl, [gs:BOOT_DRIVE]
	mov	bx, BOOT_KERNEL_SEG
	mov	es, bx
	xor	bx, bx
	int	0x13
	mov	cx, 256
	xor	ax, ax
	mov	es, ax
	mov	esi, BOOT_KERNEL_SEG*16
copykernel_loop:
	mov	ax, [es:esi]
	mov	[es:edi], ax
	inc	esi
	inc	esi
	inc	edi
	inc	edi
	loop	copykernel_loop

	pop	ax			;current logical cluster #

	mov	cx, ax			;cx=logical cluster
	mov	dx, 3
	mul	dx
	shr	ax, 1			;ax=logical cluster * 3 / 2
	mov	si, ax
	test	cl, 1			;is bit0 set?
	jnz	odd_cluster
even_cluster:
	lodsw
	and	ax, 0x0fff
	jmp	got_cluster
odd_cluster:
	lodsw
	shr	ax, 4
got_cluster:
	jmp	readkernel_loop

;------------------------------------------------------
readkernel_done:			;-------------put more real mode init stuff here!
					;----ask to load RD from floppy
	mov	ax, 0xb800
	mov	es, ax
	xor	ax, ax
	mov	ds, ax
	xor	di, di
	mov	cx, 2000
	mov	ax, 0x0700
cls_loop:
	stosw
	loop	cls_loop

	mov	dx, 0x3d4		;move cursor off screen...
	mov	al, 0x0e
	out	dx, al
	inc	dx
	mov	al, 0xff
	out	dx, al
	dec	dx
	mov	al, 0x0f
	out	dx, al
	inc	dx
	out	dx, al

	xor	di, di
	mov	si, txt_welcome
	mov	ah, 0x1f
	call	puts

	mov	di, 160
	mov	si, txt_rd1
	mov	ah, 7
	call	puts

	mov	si, txt_rd2
	mov	di, 160*2
	call	puts

	mov	di, 160*3
	mov	si, txt_input
	call	puts

get_rd:
	xor	ax, ax
	int	0x16
	cmp	al, '1'
	jz	got_rd
	cmp	al, '2'
	jnz	get_rd
got_rd:	
	stosb
	sub	al, '1'
	push	ds
	mov	bx, BOOT_DATA_SEG		;segment for data to send kernel
	mov	ds, bx
	mov	[ds:BOOT_HASRD], al
	pop	ds				;ds=0
	cmp	al, 0				;dont load rd
	jz	no_rd

	mov	cx, 80
	mov	edi, 0xb8000+160*4
filler_loop:
	mov	word [ds:edi], 0x0400+177
	inc	edi
	inc	edi
	loop	filler_loop
	mov	cx, 80				;80 cylinders to read
	xor	si, si
	mov	edi, BOOT_RD_ADD		;ram disk address
read_cylinder:
	push	cx
	mov	bx, 0x0100
	mov	es, bx
	xor	bx, bx
	mov	ax, 0x0224
	mov	cx, si
	mov	ch, cl
	mov	cl, 1
	xor	dx, dx
	mov	dl, [gs:BOOT_DRIVE]
	int	0x13

	mov	ebx, 0xb8000
	add	bx, si
	shl	bl, 1
	mov	word [ds:ebx+160*4], 0x0200+219

	push	si
	mov	esi, 0x1000
	mov	cx, 0x2400
copydisk_loop:
	mov	ax, [ds:esi]
	inc	esi
	inc	esi
	mov	[ds:edi], ax
	inc	edi
	inc	edi
	loop	copydisk_loop

	pop	si				;what cylinder# we are on...
	inc	si
	pop	cx
	loop	read_cylinder

;------------------------------------------------------
no_rd:
	mov	ax, BOOT_DATA_SEG
	mov	es, ax
	mov	di, BOOT_MEMMAP			;store memmap info in es:di for kernel
	xor	edx, edx
	mov	ax, 0x0100
	mov	ds, ax
	xor	ebx, ebx
getmemmap_loop:
	push	es
	push	di
	push	edx
	mov	ax, 0x0100			;get memory map
	mov	es, ax
	xor	di, di
	mov	eax, 0x0000E820
	mov	ecx, 0x00000014
	;mov	ebx, 0x00000000
	mov	edx, 0x534D4150			;'SMAP'
	int	0x15
	jc	getmemmap_carry
	cmp	eax, 0x534D4150			;eax should be 'SMAP' on return...
	jnz	getmemmap_error
	cmp	ebx, 0
	jz	getmemmap_done

	pop	edx				;now, copy memmap entry to es:di on the stack
	pop	di
	pop	es
	xor	si, si
	mov	cx, 20
getmemmap_copymem_loop:
	lodsb
	stosb
	loop	getmemmap_copymem_loop
	inc	edx
	jmp	getmemmap_loop

getmemmap_error:
	mov	ax, 0xb800
	mov	es, ax
	mov	di, 160*24
	xor	ax, ax
	mov	ds, ax
	mov	si, txt_memerror
	mov	ah, 0x04
	call	puts
	hlt
	jmp	$

getmemmap_carry:
	dec	edx
getmemmap_done:
	pop	edx
	pop	di
	pop	es

	xor	si, si
	mov	cx, 20
getmemmap_done_copymem_loop:
	lodsb
	stosb
	loop	getmemmap_done_copymem_loop
	inc	edx

	mov	di, BOOT_MEMENTRIES
	mov	[es:di], edx			;save # of memmap entries for kernel

						;on to vesa info...
	xor	ax, ax
	mov	gs, ax
	mov	ds, ax
	mov	ax, 0xb800
	mov	es, ax
	mov	di, 160
	mov	cx, 2000-80
	mov	ax, 0x0700
cls_vesa_loop:
	stosw
	loop	cls_vesa_loop
	mov	si, txt_vesa
	mov	di, 160*1
	mov	ah, 7
	call	puts

	push	di
	mov	ax, 0x0100
	mov	es, ax
	xor	di, di
	mov	dword [es:di], "2EBV"
	mov	ax, 0x4F00

	int	0x10
	pop	di
	cmp	ax, 0x004F
	jz	vesa_good
	mov	si, txt_novesa
	mov	ax, 0xb800
	mov	es, ax
	mov	ah, 7
	call	puts
	mov	ax, BOOT_DATA_SEG
	mov	ds, ax
	mov	word [ds:BOOT_VESA], 0
	jmp	vesa_done
vesa_good:
	mov	ax, 0xb800
	mov	es, ax
	mov	ax, 0x0100
	mov	ds, ax
	xor	si, si
	mov	bx, [4]
	mov	al, bh
	call	puthex
	mov	al, '.'
	stosb
	mov	al, 7
	stosb
	mov	al, bl
	call	puthex
	add	di, 4
	cmp	bh, 2
	jge	vesa_good2
	xor	ax, ax
	mov	ds, ax
	mov	si, txt_vesaold
	mov	ah, 7
	call	puts
	mov	ax, BOOT_DATA_SEG
	mov	ds, ax
	mov	word [ds:BOOT_VESA], 0
	jmp	vesa_done
vesa_good2:
	mov	ebx, [6]			;something like 0x00000E60
	mov	edx, [14]
	mov	si, bx
	shr	ebx, 16
	mov	ds, bx				;ds:si points to null-terminated OEM identification string
	mov	ah, 2
	push	si
	call	puts
	pop	si
	mov	ax, BOOT_DATA_SEG
	mov	es, ax
	mov	di, BOOT_VESA_OEM
vesa_copyoem:
	lodsb
	stosb
	or	al, al
	jnz	vesa_copyoem
	mov	ax, 0x0100
	mov	ds, ax
	xor	si, si
	mov	di, BOOT_VESA_VBE
	mov	cx, 512
vesa_copyvbe:
	lodsb
	stosb
	loop	vesa_copyvbe

	mov	si, dx
	shr	edx, 16
	mov	ds, dx				;ds:si points to video mode list
	mov	ax, VESA_MODELIST_SEG
	mov	es, ax
	xor	di, di
vesa_copymodes:
	lodsw
	stosw
	cmp	ax, 0xffff
	jnz	vesa_copymodes

	mov	ax, GOOD_MODELIST_SEG
	mov	es, ax
	xor	di, di
	mov	cx, 256
	mov	ax, 0xffff
clear_good_mode_list_loop:
	stosw
	loop	clear_good_mode_list_loop

	mov	ax, VESA_MODELIST_SEG
	mov	ds, ax
	xor	si, si				;ds:si points to video mode list where we can edit it :)
	mov	ax, GOOD_MODELIST_SEG
	mov	es, ax
	xor	di, di
	xor	dx, dx				;dx=what good mode # we are on
vesa_readmodeinfo_loop:
	lodsw
	cmp	ax, 0xffff
	jz	vesa_endofmodes
	push	ax				;save mode#
	call	checkmode
	cmp	ax, 0
	jz	vesa_readmodeinfo_good
	pop	ax
	jmp	vesa_readmodeinfo_loop
vesa_readmodeinfo_good:
	pop	ax				;restore mode#
	stosw
	call	vesa_showmodeinfo
	inc	dx
	jmp	vesa_readmodeinfo_loop

vesa_endofmodes:				;here we have a list of good modes at GOOD_MODELIST_SEG:0
	xor	ax, ax
	mov	ds, ax
	mov	si, txt_consolemode
	mov	ax, 0xb800
	mov	es, ax
	mov	di, 160*2
	mov	ah, 7
	call	puts
	mov	di, 160*3
	mov	cx, dx
	mov	al, 'b'
vesa_displaylabels:
	stosb
	push	ax
	mov	al, 7
	stosb
	mov	al, '.'
	stosb
	mov	al, 7
	stosb
	pop	ax
	inc	al
	add	di, 160-4
	loop	vesa_displaylabels		;done drawing screen of VESA choices, now ask for one
						;valid options are 'a' through (al-1)
	mov	bl, al
	xor	ax, ax
	mov	ds, ax
	mov	di, 160*24
	mov	si, txt_input
	mov	ah, 14
	call	puts
vesa_getchoice:
	xor	ax, ax
	int	0x16
	cmp	al, 'a'
	jl	vesa_getchoice
	cmp	al, bl
	jge	vesa_getchoice
	stosb
	push	ax
	mov	al, 14
	stosb
	pop	ax
	xor	ah, ah
	sub	ax, 'a'
	cmp	ax, 0
	jz	vesa_consolemode_only
	mov	cx, ax				;cx holds good mode# (1=first good vesa mode)
	dec	cx
	mov	ax, GOOD_MODELIST_SEG
	mov	ds, ax
	shl	cx, 1
	mov	si, cx				;ds:si points to word containing selected mode#
	lodsw
	mov	cx, ax
	mov	dx, ax				;cx and dx hold mode#

	mov	ax, VESA_MODEINFO_SEG
	mov	es, ax
	xor	di, di
	mov	ax, 0x4F01
	int	0x10
	call	checkvesa

	mov	ax, VESA_MODEINFO_SEG
	mov	ds, ax
	xor	si, si
	mov	ax, BOOT_DATA_SEG
	mov	es, ax
	mov	di, BOOT_VESA_INFO
	mov	cx, 256
vesa_copymodeinfo_loop:
	lodsb
	stosb
	loop	vesa_copymodeinfo_loop		;store ModeInfoBlock for current

	mov	[es:BOOT_VESA], dx		;store mode# for kernel

;	mov	ax, 0xb800
;	mov	es, ax
;	xor	di, di
;	mov	al, dh
;	call	puthex2
;	mov	al, dl
;	call	puthex2

	mov	bx, dx
	or	bx, 0x4000			;set "use LFB" bit of mode#
	mov	ax, 0x4F02
	int	0x10				;switch to graphics mode!!!
	call	checkvesa

	jmp	vesa_done

vesa_consolemode_only:
	mov	ax, BOOT_DATA_SEG
	mov	es, ax
	mov	word [es:BOOT_VESA], 0

	jmp	vesa_done

;------------------------------------------------------
vesa_showmodeinfo:
	pusha
	push	es
	push	ds
	mov	ax, VESA_MODEINFO_SEG
	mov	ds, ax
	xor	si, si
	mov	ax, 0xb800
	mov	es, ax

	mov	cx, dx

	mov	ax, 160
	mul	dx				;ax=160*mod#
	add	ax, 160*3+6			;offset first line of modes and room on left for label
	mov	di, ax

	mov	ax, [ds:18]
	call	console_putDec
	add	di, 10
	mov	al, 'x'
	call	console_putChar
	inc	di
	inc	di
	mov	ax, [ds:20]
	call	console_putDec
	add	di, 10
	mov	al, 'x'
	call	console_putChar
	inc	di
	inc	di
	xor	ah, ah
	mov	al, [ds:25]
	call	console_putDec
	add	di, 8
	mov	al, [ds:0]
	test	al, 0x80
	jz	vesa_showmodeinfo_done
	mov	al, 'L'
	call	console_putChar
	mov	al, 'F'
	call	console_putChar
	mov	al, 'B'
	call	console_putChar
	inc	di
	inc	di
	mov	ebx, [ds:40]
	mov	eax, ebx
	shr	eax, 24
	call	puthex2
	mov	eax, ebx
	shr	eax, 16
	call	puthex2
	mov	al, bh
	call	puthex2
	mov	al, bl
	call	puthex2
vesa_showmodeinfo_done:
	pop	ds
	pop	es
	popa
	ret

;------------------------------------------------------
checkmode:
	push	bx
	push	cx
	push	dx
	push	es
	push	ds
	push	di
	push	si
	mov	cx, ax				;cx=modenumber
	mov	ax, VESA_MODEINFO_SEG
	mov	es, ax
	xor	di, di
	mov	ax, 0x4F01
	int	0x10
	call	checkvesa
	xor	di, di				;es:di -> ModeInfoBlock struc
	mov	ax, [es:di]			;ModeAttributes
	test	al, 1				;mode supported
	jz	vesa_modenogood
	test	al, 8				;color mode
	jz	vesa_modenogood
	test	al, 0x10			;graphics mode
	jz	vesa_modenogood
	test	al, 0x80			;Linear Frame Buffer supported
	jz	vesa_modenogood
	mov	al, [es:di+25]			;BitsPerPixel
	cmp	al, 16
	jz	vesa_bppok
	cmp	al, 24
	jz	vesa_bppok
	cmp	al, 32
	jnz	vesa_modenogood
vesa_bppok:
	mov	ax, [es:di+18]			;XResolution
	mov	bx, [es:di+20]			;YResolution
	cmp	ax, 640				;640x480
	jnz	res_goon1
	cmp	bx, 480
	jnz	vesa_modenogood
	jmp	vesa_modegood
res_goon1:
	cmp	ax, 800
	jnz	res_goon2
	cmp	bx, 600
	jnz	vesa_modenogood
	jmp	vesa_modegood
res_goon2:
	cmp	ax, 1024
	jnz	res_goon3
	cmp	bx, 768
	jnz	vesa_modenogood
	jmp	vesa_modegood
res_goon3:
	cmp	ax, 1280
	jnz	res_goon4
	cmp	bx, 1024
	jz	vesa_modegood
	cmp	bx, 960
	jz	vesa_modegood
	jmp	vesa_modenogood
res_goon4:
	cmp	ax, 1600
	jnz	vesa_modenogood
	cmp	bx, 1200
	jnz	vesa_modenogood
vesa_modegood:
	pop	si
	pop	di
	pop	ds
	pop	es
	pop	dx
	pop	cx
	pop	bx
	xor	ax, ax
	ret
vesa_modenogood:
	pop	si
	pop	di
	pop	ds
	pop	es
	pop	dx
	pop	cx
	pop	bx
	mov	ax, 0xffff
	ret

;------------------------------------------------------
vesa_done:

;	xor	ax, ax				;wait for keypress...
;	int	0x16

	jmp	go_pm

;------------------------------------------------------
puts:
	lodsb
	or	al, al
	jz	puts_done
	stosb
	mov	al, ah
	stosb
	jmp	puts
puts_done:
	ret

;------------------------------------------------------
checkvesa:
	cmp	ax, 0x004F
	jnz	vesaerror
	ret
vesaerror:
	mov	ax, 0xb800
	mov	es, ax
	xor	ax, ax
	mov	ds, ax
	mov	si, txt_vesaerror
	mov	di, 160*24
	mov	ah, 4
	call	puts
	cli
	hlt

;-------Function console_putDec
;input:
; AX = number to display
;output:
; number written in decimal to es:di
console_putDec:
	pusha
	xor	dx, dx
	xor	bh, bh			;no characters written yet
	mov	cx, 10000
	div	cx			;ax=quotiont, dx=remainder
	add	ax, '0'
	cmp	ax, '0'
	je	.goon1
	call	console_putChar
	mov	bh, 1

.goon1:
	mov	ax, dx			;load remainder to ax
	xor	dx, dx
	mov	cx, 1000
	div	cx			;ax=quotiont, dx=remainder
	add	ax, '0'
	cmp	ax, '0'
	je	.goon11
	call	console_putChar
	mov	bh, 1
	jmp	.goon2
.goon11:
	cmp	bh, 0
	je	.goon2
	call	console_putChar

.goon2:
	mov	ax, dx			;load remainder to ax
	xor	dx, dx
	mov	cx, 100
	div	cx			;ax=quotiont, dx=remainder
	add	ax, '0'
	cmp	ax, '0'
	je	.goon21
	call	console_putChar
	mov	bh, 1
	jmp	.goon3
.goon21:
	cmp	bh, 0
	je	.goon3
	call	console_putChar

.goon3:
	mov	ax, dx			;load remainder to ax
	xor	dx, dx
	mov	cx, 10
	div	cx			;ax=quotiont, dx=remainder
	add	ax, '0'
	cmp	ax, '0'
	je	.goon31
	call	console_putChar
	mov	bh, 1
	jmp	.goon4
.goon31:
	cmp	bh, 0
	je	.goon4
	call	console_putChar
.goon4:					;here dx contains last remainder
	mov	ax, dx
	add	ax, '0'
	call	console_putChar

	popa
	ret

;------------------------------------------------------
console_putChar:
	stosb
	mov	al, 7
	stosb
	ret

;------------------------------------------------------
puthex:
	;es:di points to video memory
	;al holds hex value

	push	ax
	mov	ah, al
	shr	ax, 4
	and	al, 0x0F
	add	al, '0'
	cmp	al, '9'
	jle	puthex_goon1
	add	al, 'A'-'9'-1
puthex_goon1:
	cmp	al, '0'
	jz	puthex_skipzero
	stosb
	mov	al, 7
	stosb
puthex_skipzero:
	pop	ax
	push	ax
	and	al, 0x0F
	add	al, '0'
	cmp	al, '9'
	jle	puthex_goon2
	add	al, 'A'-'9'-1
puthex_goon2:
	stosb
	mov	al, 7
	stosb
	pop	ax
	ret

;------------------------------------------------------
puthex2:
	;es:di points to video memory, always displays 2 characters!
	;al holds hex value

	push	ax
	mov	ah, al
	shr	ax, 4
	and	al, 0x0F
	add	al, '0'
	cmp	al, '9'
	jle	puthex2_goon1
	add	al, 'A'-'9'-1
puthex2_goon1:
	stosb
	mov	al, 7
	stosb
	pop	ax
	push	ax
	and	al, 0x0F
	add	al, '0'
	cmp	al, '9'
	jle	puthex2_goon2
	add	al, 'A'-'9'-1
puthex2_goon2:
	stosb
	mov	al, 7
	stosb
	pop	ax
	ret

;------------------------------------------------------
txt_welcome: db	"                             Welcome to HOS v", VERSION, "!                             ", 0
txt_rd1: db	"1. Do not load an initial ram disk", 0
txt_rd2: db	"2. Load initial ram disk from floppy", 0
txt_input: db	"Enter your selection: ", 0
txt_vesa: db	"VESA version: ", 0
txt_vesaerror: db "VESA function call error! Halting system!", 0
txt_novesa: db	"VESA not found. Starting in console mode...", 0
txt_vesaold: db	"VESA version 2.0 required. Starting in console mode...", 0
txt_consolemode: db "a. Console mode only.", 0
txt_memerror: db "Extended Memory Map information unavailable! Halting system...", 0

;------------------------------------------------------
getCHSfromCluster:
	;input: ax=lba of sector on floppy (0-2879)
	add	ax, 31			;convert logical cluster# to lba#
	xor	dx, dx			;lba->chs
	mov	bx, 18
	div	bx
	inc	dx
	mov	cl, dl			;sector# (1-18)
	xor	dx, dx
	mov	bx, 2
	div	bx
	mov	ch, al			;cylinder# (0-79)
	mov	dh, dl			;head# (0-1)
	ret


;-------------------------------------------------------
gdtr:
	dw	gdt_end-gdt-1
	dd	gdt
gdt:
	dd	0
	dd	0

KERNEL_CODE equ $-gdt
	db	0xff		;limit	7:0
	db	0xff		;limit	15:8
	db	0x00		;base	7:0
	db	0x00		;base	15:8
	db	0x00		;base	23:16
	db	0x9a		;access
	db	0xcf		;flags / limit 19:16
	db	0x00		;base	31:24

KERNEL_DATA equ $-gdt
	db	0xff		;segment 16 = 4gb data
	db	0xff
	db	0x00
	db	0x00
	db	0x00
	db	0x92
	db	0xcf		;cf
	db	0x00

gdt_end:

;------------------------------------------------------
go_pm:
	xor	ax, ax
	mov	ds, ax
	lgdt	[gdtr]
	cli
	mov	eax, cr0
	inc	eax
	mov	cr0, eax

	jmp	KERNEL_CODE:pmode

bits 32
pmode:
	mov	ax, KERNEL_DATA
	mov	es, ax
	mov	ds, ax
	mov	fs, ax
	mov	gs, ax
	jmp	KERNEL_CODE:BOOT_KERNEL_ADD

kernel:	db	"KERNEL  BIN", 0


