USE16

stage1: ; dl comes with disk
    ; initialize segment registers
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax

    ; initialize stack
    mov sp, 0x7C00

    ; initialize CS
    ; far jump sets both CS and IP to a known-good state,
    ; we don't know where the BIOS put us at startup.
    ; (could be 0x00:0x7C00, could be 0x7C00:0x00.
    ; Not everybody follows spec.)
    push ax
    push word .set_cs
    retf

.set_cs:
    ; save disk number
    mov [disk], dl

    mov si, stage_msg
    call print
    mov al, '1'
    call print_char
    call print_line



    ; read CHS gemotry
    ;  CL (bits 0-5) = maximum sector number
    ;  CL (bits 6-7) = high bits of max cylinder number
    ;  CH = low bits of maximum cylinder number
    ;  DH = maximum head number
    mov ah, 0x08
    mov dl, [disk]
    xor di, di
    int 0x13
    jc error ; carry flag set on error
    mov bl, ch
    mov bh, cl
    shr bh, 6
    mov [chs.c], bx
    shr dx, 8
    inc dx ; returns heads - 1
    mov [chs.h], dx
    and cl, 0x3f
    mov [chs.s], cl

    ; disk address of stage 2
    ; (start sector)
    mov eax, STAGE2_SECTOR

    ; where to load stage 2
    mov bx, stage2

    ; length of stage2 + stage3
    ; (on disk, in sectors)
    mov cx, (stage3.end - stage2) / 512
    mov dx, 0

    call load

    jmp stage2.entry

; load some sectors from disk to a buffer in memory
; buffer has to be below 1MiB
; IN
;   ax: start sector
;   bx: offset of buffer
;   cx: number of sectors (512 Bytes each)
;   dx: segment of buffer
; CLOBBER
;   ax, bx, cx, dx, si
; TODO rewrite to (eventually) move larger parts at once
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)
load:
    ; replaced 127 with 1.
    ; see https://stackoverflow.com/questions/58564895/problem-with-bios-int-13h-read-sectors-from-drive
    ; TODO: fix later

    cmp cx, 1 ;127
    jbe .good_size

    pusha
    mov cx, 1; 127
    call load
    popa
    add eax, 1; 127
    add dx, 1 * 512 / 16 ; 127
    sub cx, 1;127

    jmp load
.good_size:
    mov [DAPACK.addr], eax
    mov [DAPACK.buf], bx
    mov [DAPACK.count], cx
    mov [DAPACK.seg], dx

; This should be a subroutine, 
; but we don't call/ret to save a few bytes.
; (we only use this once)
;
;call print_dapack
;print_dapack:
    mov bx, [DAPACK.addr + 2]
    call print_hex

    mov bx, [DAPACK.addr]
    call print_hex

    mov al, '#'
    call print_char

    mov bx, [DAPACK.count]
    call print_hex

    mov al, ' '
    call print_char

    mov bx, [DAPACK.seg]
    call print_hex

    mov al, ':'
    call print_char

    mov bx, [DAPACK.buf]
    call print_hex

    call print_line
    ;ret
    ; End of print_dapack


    cmp byte [chs.s], 0
    jne .chs
    ;INT 0x13 extended read does not work on CDROM!
    mov dl, [disk]
    mov si, DAPACK
    mov ah, 0x42
    int 0x13
    jc error ; carry flag set on error
    ret

.chs:
    ; calculate CHS
    xor edx, edx
    mov eax, [DAPACK.addr]
    div dword [chs.s] ; divide by sectors
    mov ecx, edx ; move sector remainder to ecx
    xor edx, edx
    div dword [chs.h] ; divide by heads
    ; eax has cylinders, edx has heads, ecx has sectors

    ; Sector cannot be greater than 63
    inc ecx ; Sector is base 1
    cmp ecx, 63
    ja error_chs

    ; Head cannot be greater than 255
    cmp edx, 255
    ja error_chs

    ; Cylinder cannot be greater than 1023
    cmp eax, 1023
    ja error_chs

    ; Move CHS values to parameters
    mov ch, al
    shl ah, 6
    and cl, 0x3f
    or cl, ah
    shl dx, 8

    ; read from disk using CHS
    mov al, [DAPACK.count]
    mov ah, 0x02 ; disk read (CHS)
    mov bx, [DAPACK.buf]
    mov dl, [disk]
    push es ; save ES
    mov es, [DAPACK.seg]
    int 0x13
    pop es ; restore EC
    jc error ; carry flag set on error
    ret

error_chs:
    mov ah, 0

error:
    call print_line

    mov bh, 0
    mov bl, ah
    call print_hex

    mov si, stage1_error_msg
    call print
    call print_line

.halt:
    cli
    hlt
    jmp .halt

%include "print.asm"

stage_msg: db "Stage ",0
stage1_error_msg: db " ERROR",0

disk: db 0

chs:
.c: dd 0
.h: dd 0
.s: dd 0

DAPACK:
        db 0x10
        db 0
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
.buf:   dw 0 ; memory buffer destination address (0:7c00)
.seg:   dw 0 ; in memory page zero
.addr:  dq 0 ; put the lba to read in this spot

db 0xff