%define	FILESYS_SEG	07F0h		; File system will start at 07F0:0000
%define KERNEL_SEG	0AF0h		; Kernel will start at 0AF0:0000

%define bsOemName       bp+0x03		; OEM label
%define bsBytesPerSec   bp+0x0b		; bytes/sector
%define bsSecPerClust   bp+0x0d		; sectors/allocation unit
%define bsResSectors    bp+0x0e		; # reserved sectors
%define bsFATs          bp+0x10		; # of fats
%define bsRootDirEnts   bp+0x11		; # of root dir entries
%define bsSectors       bp+0x13		; # sectors total in image
%define bsMedia         bp+0x15		; media descrip: fd=2side9sec, etc...
%define sectPerFat      bp+0x16		; # sectors in a fat
%define sectPerTrack    bp+0x18		; # sectors/track
%define nHeads          bp+0x1a		; # heads
%define nHidden         bp+0x1c		; # hidden sectors
%define nSectorHuge     bp+0x20		; # sectors if > 65536
%define drive           bp+0x24		; drive number
%define extBoot         bp+0x26		; extended boot signature
%define volid           bp+0x27
%define vollabel        bp+0x2b
%define filesys         bp+0x36


	ORG	7C00h

	JMP	SHORT BOOT_CODE
	NOP

	TIMES	0x3E-$+$$ DB 0


BOOT_CODE:
	; Set up segment registers
	MOV	AX, CS
	MOV	DS, AX
	MOV	AX, FILESYS_SEG
	MOV	ES, AX

	; Check drive.
	TEST	DL, 80h
	JZ	WELCOME
	JMP	FAIL_INCORRECTDRIVE

WELCOME:
	CALL	PRINT
	DB	'Loading ShitOS... ', 0

	;MOV	[drive], DL
	CALL	GET_DRIVEDATA
	MOV	EBX, EAX
	PUSH	EBX
	JNC	LOADFS
	JMP	FAIL_INCORRECTDRIVE

LOADFS:
	; Load file system
	XOR	CX, CX
	MOV	AL, 24
	;XOR	EDI, EDI
	CALL	READ_DRIVE
	JNC	CHKFSTYPE
	JMP	FAIL

CHKFSTYPE:
	; Check if file system is PrimFS
	CMP	[ES:200h + 3], DWORD 'BOOT'
	JNE	FAIL

	; Look for KERNEL.BIN
	MOV	CX, 119
	MOV	BX, 200h + 2
FIND_KERNEL:
	ADD	BX, 32
	MOV	DI, BX
	PUSH	ECX
	MOV	CX, 3
	MOV	SI, KERNELFILE
	REPNE	CMPSD
	POP	ECX
	LOOPNE	FIND_KERNEL
	CMP	CX, 0
	JZ	FAIL_NOKERNEL

	; Load kernel
	; Calculate position
	MOV	AX, 119
	SUB	AX, CX
	;MOV	AX, 1
	PUSH	DX
	XOR	DX, DX
	MOV	BX, 24
	MUL	BX
	POP	DX

	MOV	CX, AX
	POP	EBX
	XOR	DI, DI
	MOV	AX, KERNEL_SEG
	MOV	ES, AX
	MOV	AL, 24
	CALL	READ_DRIVE
	JC	FAIL

	; Call the kernel
	CALL	KERNEL_SEG:0000h

	;JMP	FAIL



FAIL_INCORRECTDRIVE:
	CALL	PRINT
	DB	0Dh, 0Ah, 'Unsupported drive!', 0
	JMP	REBOOT

FAIL_NOKERNEL:
	CALL	PRINT
	DB	0Dh, 0Ah, 'No kernel.', 0
	JMP	REBOOT

FAIL:
	CALL	PRINT
	DB	'Operation failed... Press any key to retry!', 0

REBOOT:
	XOR	AH, AH
	INT	16h
	INT	19h


PRINT_0:
	XOR	BX, BX
	MOV	AH, 0Eh
	INT	10h
PRINT:
	POP	SI
	LODSB
	PUSH	SI
	CMP	AL, 0
	JNE	PRINT_0
	RET


; -------------------------------------------------------------
; PROC GET_DRIVEDATA
; Determines drive parameters.
; INPUT:
;	DL	- drive number
; OUTPUT:
;	EAX	- drive data, as returned from BIOS
;	CY	- set on error (INT 13h/08h)
; -------------------------------------------------------------
GET_DRIVEDATA:
	PUSH	ES
	PUSH	DI
	PUSH	BX
	PUSH	CX
	PUSH	DX
	PUSH	DX
	MOV	AH, 08h
	POP	DX
	PUSH	WORD 0000h
	POP	ES
	XOR	DI, DI
	INT	13h
	JC	_GET_DRIVEDATA_EXIT

	; Store result in EAX
	INC	DH		; To get the number of heads, instead of maximum head number
	MOV	AX, DX
	SHL	EAX, 16		; Shift DX (head number and drive number) to high word of EAX
	INC	CH		; To get the number of cylinders
	MOV	AX, CX
	CLC

_GET_DRIVEDATA_EXIT:
	POP	DX
	POP	CX
	POP	BX
	POP	DI
	POP	ES
	RET


; -------------------------------------------------------------
; PROC READ_DRIVE
; Reads a number of blocks from the specified drive.
; INPUT:
;	AL	- number of sectors to read
;	EBX	- drive data (returned from GET_DRIVEDATA)
;	ECX	- sector number (LBA)
;	DL	- drive number
;	DI	- ES:DI -> target buffer
; OUTPUT:
;	CY	- set on error (by INT 13h)
;	AH	- error code (set by INT 13h)
; -------------------------------------------------------------

_RD_NUM_CYLINDERS	DW	0
_RD_NUM_HEADS		DB	0
_RD_NUM_SECTORS		DB	0
;_RD_NUMHS_MUL_NUMSS	DW	0

_RD_C			DW	0
_RD_H			DB	0
_RD_S			DB	0

READ_DRIVE:
	; Calculate CHS address (Yuck!)
	; This is the formula we use:
	; lba = (c*num_heads + h)*num_sectors + s - 1
	; Express variables:
	; c = lba / (num_heads*num_sectors)
	; h = (lba % (num_heads*num_sectors)) / num_sectors
	; s = (lba % (num_heads*num_sectors)) % num_sectors + 1

	; Store registers we use during the calculation
	PUSH	EAX
	PUSH	EBX
	PUSH	EDX

	; Extract num_cylinders, num_heads, num_sectors of drive data
	; and store them in variables

	MOV	[_RD_NUM_SECTORS], BL
	AND	[_RD_NUM_SECTORS], BYTE 03Fh
	MOV	[_RD_NUM_CYLINDERS], BH
	;AND	BL, 0C0h
	SHR	BL, 6
	MOV	[_RD_NUM_CYLINDERS + 1], BL
	SHR	EBX, 16
	MOV	[_RD_NUM_HEADS], BH

	; Calculate num_heads*num_sectors
	XOR	EAX, EAX
	XOR	EBX, EBX
	XOR	EDX, EDX
	MOV	AL, [_RD_NUM_HEADS]
	MOV	BL, [_RD_NUM_SECTORS]
	MUL	BX
	;MOV	[_RD_NUMHS_MUL_NUMSS], AX

	; Calculate C
	MOV	BX, AX
	MOV	EAX, ECX
	DIV	EBX
	MOV	[_RD_C], AX

	; Calculate H
	; lba % (num_heads*num_sectors) is in EDX, since the last DIV
	MOV	EAX, EDX
	XOR	EBX, EBX
	MOV	BL, [_RD_NUM_SECTORS]
	XOR	EDX, EDX
	DIV	EBX
	MOV	[_RD_H], AL

	; Calculate S :)
	; (lba % (num_heads*num_sectors)) % num_sectors is in EDX
	INC	DX
	MOV	[_RD_S], DL

	; Huh! Over. At last.
	; Now call that yucky interrupt
	; But first, restore our registers from the stack
	POP	EDX
	POP	EBX
	POP	EAX

	; Number of sectors to read is already in AL
	PUSH	BX
	PUSH	CX
	PUSH	DX
	MOV	CH, [_RD_C]	; low 8 bits of cylinder number
	MOV	CL, [_RD_C + 1]	; high 2 bits of cylinder number
	SHL	CL, 6		; shift up
	ADD	CL, [_RD_S]	; sector number
	MOV	DH, [_RD_H]	; head number
	; Drive number is already in DL
	MOV	BX, DI		; target buffer
	MOV	AH, 02h
	INT	13h
	POP	DX
	POP	CX
	POP	BX

	; Flags & stuffs are stored by INT 13h
	RET


KERNELFILE:
	DB	'KERNEL.BIN  '

	TIMES	01FEh-$+$$ DB 0

SIGN	DW	0AA55h
