bits 16
org 0x500

jmp main

; Global Descriptor Table
gdt:
	dd 0
	dd 0

	dw 0xffff
	dw 0
	db 0
	db 10011010b
	db 11001111b
	db 0

	dw 0xffff
	dw 0
	db 0
	db 10010010b
	db 11001111b
	db 0
gdt_descriptor:
	dw gdt_descriptor - gdt - 1
	dd gdt

; Data
BytesPerSector		dw 512
RootEntries			dw 224
SectorsPerTrack		dw 18
HeadsPerCylinder	dw 2
DriveNumber			db 0xFF

filename	db "KERNEL  BIN"
filesize	dd 0

err_notfound	db "ERROR: Kernel file not found", 13, 10, 0
err_floppy		db "ERROR: Floppy disk failure", 13, 10, 0

cluster			dw 0
pointer			dw 0

msg_ax	db "AX:", 0
msg_bx	db "BX:", 0
msg_cx	db "CX:", 0
msg_dx	db "DX:", 0
msg_sp	db "SP:", 0
msg_bp	db "BP:", 0
msg_si	db "SI:", 0
msg_di	db "DI:", 0
msg_ip	db "IP:", 0

msg_es	db "ES:", 0
msg_cs	db "CS:", 0
msg_ss	db "SS:", 0
msg_ds	db "DS:", 0
msg_fs	db "FS:", 0
msg_gs	db "GS:", 0

msg_tab	db "        ", 0
msg_nl	db 13, 10, 0


; Code
main:
	; Setting up registers
	cli
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov ax, 0x9000
	mov ss, ax
	mov sp, 0xffff
	sti

	; Loading the GDT
	lgdt [gdt_descriptor]

	; Enable A20
	in al, 0x92
	test al, 2
	jnz a20ok
	or al, 2
	and al, 0xfe
	out 0x92, al
	a20ok:

	cmp dl, [DriveNumber]
	je drive_number_ok
	mov [DriveNumber], dl
	mov ah, 8

	push ax
	push bx
	push cx
	push dx
	push es
	push di
	pushf

	int 0x13

	popf
	pop di
	pop es
	pop dx
	pop cx
	pop bx
	pop ax

	jmp ent_pmode ; SKIPPING EVERTHING BELOW

	jc fatal_disk_error
	and cx, 0x3f
	mov [SectorsPerTrack], cx
	movzx dx, dh
	add dx, 1
	mov [HeadsPerCylinder], dx
drive_number_ok:
	mov eax, 0

floppy_ok:
	mov ax, 19	; NumberOfFATs * SectorsPerFAT + ReservedSectors
	call l2hts

	mov si, buffer
	mov bx, ds
	mov es, bx
	mov bx, si

	mov ah, 0x2	; Param for int 0x13: read floppy sectors
	mov al, 14	; Read 14 of them

	pusha

read_root_dir:
	popa
	pusha

	stc
	int 0x13

	jnc search_dir		; If OK, skip ahead
	call reset_floppy	; Else, reset the floppy controller and try again
	jnc read_root_dir	; Floppy reset OK?
	jmp fatal_disk_error			; If not, fatal double error

search_dir:
	popa

	mov ax, ds	; Root dir is now in [0x2e00]
	mov es, ax	; Set DI to this info
	mov di, buffer

	mov cx, word [RootEntries]
	mov ax, 0

next_root_entry:
	xchg cx, dx	; We use 'cx' in the inner loop

	mov si, filename
	mov cx, 11
	rep cmpsb
	je found_file_to_load

	add ax, 32

	mov di, buffer
	add di, ax

	xchg dx, cx
;	loop next_root_entry
	dec cx
	jnz next_root_entry

	mov si, err_notfound
	call print_string16
	jmp reboot

found_file_to_load:
	mov ax, word [es:di + 0xF]	; Offset 11 + 15 = 26
	mov word [cluster], ax

	mov ax, 1	; Sector 1
	call l2hts

	mov di, buffer
	mov bx, di

	mov ah, 0x2	; int 0x13 parameters: read (FAT) sectors
	mov al, 9	; all 9 sectors of first FAT

	pusha

read_fat:
	popa
	pusha

	stc
	int 0x13

	jnc read_fat_ok		; If it went OK, skip ahead
	call reset_floppy	; Else, reset floppy controller and try again
	jnc read_fat		; Floppy reset OK?

fatal_disk_error:
	mov si, err_floppy	; If not, print message and reboot
	call print_string16
	jmp reboot			; FATAL DOUBLE ERROR

read_fat_ok:
	popa

	mov ax, 0x3000	; Where we'll load the boot1 stage 
	mov es, ax
	mov bx, 0x0000

	mov ah, 0x2	; int 0x13 parameters: read floppy
	mov al, 1

	xor ecx, ecx
	push ecx
	push ax

load_file_sector:
	mov ax, word [cluster]
	add ax, 31

	call l2hts

	mov ax, 0x3000
	mov es, ax
	mov bx, word [pointer]

	pop ax
	push ax

	stc
	int 0x13

	jnc calculate_next_cluster	; If there's no error
	call reset_floppy			; Else, reset floppy and try again
	jmp load_file_sector

calculate_next_cluster:
	pop ax
	pop ecx
	inc ecx
	push ecx
	push ax

	mov ax, [cluster]
	mov dx, 0
	mov bx, 3
	mul bx
	mov bx, 2
	div bx				; 'dx' = [cluster] mod 2
	mov si, buffer
	add si, ax			; 'ax' = word in FAT for the 12 bit entry
	mov ax, word[ds:si]

	or dx, dx
	jz even		; If [cluster] is even, drop last 4 bits of word
				; with next cluster; if odd, drop first 4 bits

odd:
	shr ax, 4		; Shift out first 4 bits
	jmp short next_cluster_cont
even:
	and ax, 0xfff	; Mask out last 4 bits

next_cluster_cont:
	mov word [cluster], ax
	
	cmp ax, 0xff8
	jae end

	add word [pointer], 512
	jmp load_file_sector

end:
	pop ax
	pop ecx
	mov dword [filesize], ecx

ent_pmode:
	; Clear the screen
	push ax
    mov ah, 0
	mov al, 3
    int 0x10
	pop ax

	; Print the segments
	push si

	mov si, msg_ax
	call print_string16
	push ax
	call print_hex_word
	pop ax

	mov si, msg_tab
	call print_string16

	mov si, msg_bx
	call print_string16
	push bx
	call print_hex_word
	pop bx
	
	mov si, msg_tab
	call print_string16

	mov si, msg_cx
	call print_string16
	push cx
	call print_hex_word
	pop cx

	mov si, msg_nl
	call print_string16

	mov si, msg_dx
	call print_string16
	push dx
	call print_hex_word
	pop dx

	mov si, msg_tab
	call print_string16

	mov si, msg_sp
	call print_string16
	push sp
	call print_hex_word
	pop sp

	mov si, msg_tab
	call print_string16

	mov si, msg_bp
	call print_string16
	push bp
	call print_hex_word
	pop bp

	mov si, msg_nl
	call print_string16

	mov si, msg_si
	call print_string16
	call print_hex_word
	pop si

	mov si, msg_tab
	call print_string16

	mov si, msg_di
	call print_string16
	push di
	call print_hex_word
	pop di

	mov si, msg_tab
	call print_string16

	mov si, msg_ip
	call print_string16
	push word buffer
	call print_hex_word
	pop si

	mov si, msg_nl
	call print_string16

	mov si, msg_es
	call print_string16
	push es
	call print_hex_word
	pop es

	mov si, msg_tab
	call print_string16

	mov si, msg_cs
	call print_string16
	push cs
	call print_hex_word
	pop ax

	mov si, msg_tab
	call print_string16

	mov si, msg_ss
	call print_string16
	push ss
	call print_hex_word
	pop ss

	mov si, msg_nl
	call print_string16

	mov si, msg_ds
	call print_string16
	push ds
	call print_hex_word
	pop ds

	mov si, msg_tab
	call print_string16

	mov si, msg_fs
	call print_string16
	push fs
	call print_hex_word
	pop fs

	mov si, msg_tab
	call print_string16

	mov si, msg_gs
	call print_string16
	push gs
	call print_hex_word
	pop gs

	; Wait for a keystroke
	mov ax, 0
	int 0x16

	; Entering protected mode
	cli
	mov eax, cr0
	or eax, 1
	mov cr0, eax
	jmp 0x8:pmain	; COMPUTER REBOOTS AFTER THIS INSTRUCTION

	cli
	hlt


; Reset floppy controller
reset_floppy:
	push ax
	push dx
	mov ax, 0
	mov dl, 0
	stc
	int 0x13
	pop dx
	pop ax
	ret

; Reboot
reboot:
	mov ah, 0
	int 0x16	; Wait for keystroke
	cli
	int 0x19	; Reboot

; Logical to head track sector
; IN: ax(logical sector); OUT: correct registers for int 13
l2hts:
	push bx
	push ax

	mov bx, ax

	mov dx, 0
	div word [SectorsPerTrack]
	add dl, 1
	mov cl, dl
	mov ax, bx

	mov dx, 0
	div word [SectorsPerTrack]
	mov dx, 0
	div word [HeadsPerCylinder]
	mov dh, dl
	mov ch, al

	mov dl, byte [DriveNumber]

	pop ax
	pop bx
	ret

; Print String
; IN: si(string location; OUT:
print_string16:
	mov ah, 0xe
.loop:
	lodsb
	or al, al
	jz .done
	int 0x10
	jmp .loop
.done:
	ret

; Print HEX value
print_hex_word:
    pusha           ; Save all registers, 16 bytes total
    mov bp, sp      ; BP=SP, on 8086 can't use sp in memory operand
    mov cx, 0x0404  ; CH = number of nibbles to process = 4 (4*4=16 bits)
                    ; CL = Number of bits to rotate each iteration = 4 (a nibble)
    mov dx, [bp+18] ; DX = word parameter on stack at [bp+18] to print
    mov bx, 13		; BX = page / foreground attr is at [bp+20]

.loop:
    rol dx, cl      ; Roll 4 bits left. Lower nibble is value to print
    mov ax, 0x0e0f  ; AH=0E (BIOS tty print),AL=mask to get lower nibble
    and al, dl      ; AL=copy of lower nibble
    add al, 0x90    ; Work as if we are packed BCD
    daa             ; Decimal adjust after add.
                    ;    If nibble in AL was between 0 and 9, then CF=0 and
                    ;    AL=0x90 to 0x99
                    ;    If nibble in AL was between A and F, then CF=1 and
                    ;    AL=0x00 to 0x05
    adc al, 0x40    ; AL=0xD0 to 0xD9
                    ; or AL=0x41 to 0x46
    daa             ; AL=0x30 to 0x39 (ASCII '0' to '9')
                    ; or AL=0x41 to 0x46 (ASCII 'A' to 'F')
    int 0x10        ; Print ASCII character in AL
    dec ch
    jnz .loop       ; Go back if more nibbles to process
    popa            ; Restore all the registers
    ret

; Protected mode code
bits 32

%include "vga.inc"

msg_p db "Hey! It works!", 10, 0

pmain:
	mov ax, 0x10
	mov ds, ax
	mov ss, ax
	mov es, ax
	mov esp, 0x90000

	call clear_screen
	mov ebx, msg_p
	call print_string

	cli
	hlt

	mov eax, dword [filesize]
	movzx ebx, word [BytesPerSector]
	mul ebx
	mov ebx, 4
	div ebx
	cld
	mov esi, 0x30000
	mov edi, 0x100000
	mov ecx, eax
	rep movsd

	call 0x8:0x100000

	cli
	hlt

buffer:
