%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:
        ; Disable interrupts, and clear direction flag
        CLI
        CLD
        JMP     0000h:SETUP

SETUP:
        XOR     AX, AX
        MOV     SS, AX
        MOV     SP, 7C00h
        ; 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

        STI

        XOR     DX, DX
        ;MOV    [drive], DL
        ;CALL    GET_DRIVEDATA
        ;PUSH    EAX
        MOV     EBX, 002015012h
        PUSH    EBX

        PUSH    BX
        CALL    PRINT
        DB      'L:FS', 0Dh, 0Ah, 0
        POP     BX

LOADFS:
        ; Load file system
        XOR     CX, CX
        MOV     AL, 24
        XOR     EDI, EDI
        ;MOV     CX, 1
        ;MOV     AL, 23
        ;MOV     EDI, 200h
        CALL    READ_DRIVE_SAFE
        JNC     CHKFSTYPE
        JMP     FAIL

CHKFSTYPE:
        PUSH    BX
        CALL    PRINT
        DB      'C:FST', 0Dh, 0Ah, 0
        POP     BX
        ; Check if file system is PrimFS
        CMP     [ES:200h + 3], DWORD 'BOOT'
        JE      FASZOM
        JMP     FAIL
FASZOM:

        ; Look for KERNEL.BIN
        CALL    PRINT
        DB      'F:KER', 0Dh, 0Ah, 0
        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

        CALL    PRINT
        DB      'L:KER', 0Dh, 0Ah, 0

        ; 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_SAFE
        JC      FAIL

        CALL    PRINT
        DB      'J:KER', 0Dh, 0Ah, 0

        ; 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!', 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 READ_DRIVE_SAFE
; Reads sectors one by one.
; INPUT:
;       same as READ_DRIVE
; OUTPUT:
;       same as READ_DRIVE
; -------------------------------------------------------------
READ_DRIVE_SAFE:
        PUSH    AX
        MOV     AL, 1
        CALL    READ_DRIVE
        INC     ECX
        PUSH    BX
        CALL    PRINT
        DB      '.', 0
        POP     BX
        POP     AX
        DEC     AL
        CMP     AL, 0
        JNZ     READ_DRIVE_SAFE
        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
_RD_FAILCOUNT           DB      10

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
_DO_READ:
        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
        JNC     _READ_SUCCESS
        XOR     AX, AX
        INT     13h
        DEC     BYTE [_RD_FAILCOUNT]
        CMP     [_RD_FAILCOUNT], BYTE 0
        JNZ     _DO_READ
        JMP     FAIL
_READ_SUCCESS:
        ADD     DI, 200h        ; Store new DI
        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
