; ===========================================================================
; LOWLEVEL DISK I/O
; ===========================================================================

_BIOS_NumFIXEDDRIVES EQU 0075h

IF (MSDOS)
   RETRYCOUNT      EQU 5
ELSE
   RETRYCOUNT      EQU 3
ENDIF

BytesPerSector     EQU 512

AbsDisk_Rec:
      diskdrive         db 0
      munits            db 0
      ABSDSK_SPT        dw 18
      ABSDSK_HEADS      dw 2
      ABSDSK_SectSize   dw bytespersector
      FDC_STATE         db 0
      FDC_ERROR         db 0
AbsDisk_Info:
      fixeddisks        db ?
      floppydisks       db ?
      floppies          db ?
      drives            db ?  ; should be in filesys.asm
AbsDiskIORec:
      ABSDSK_lStartSect dd 0
      ABSDSK_wSectCnt   dw 1 ;
      ABSDSK_pBuffer    dd ?
AbsDiskIORec_SIZE EQU $ - AbsDiskIORec
AbsDisk_SIZE EQU $ - AbsDisk_Rec

DISK_INT macro
;      clc
      push bp
      int  13h
      pop  bp
      endm

DEBUGDOT  macro
IF (DEBUG)
          pushf
          push ax
          mov  al, 2eh
          call teletype
          pop  ax
          popf
ENDIF
          endm

; ===========================================================================
LBAtoCHS macro
; Input : AX = Sector
;         BX = Heads
;         CX = Sectors per track
; Output: AX = Sector
;         BX = Head
;         CX = Cylinder
; convert "ax=2; LBA addressing scheme to CHS addressing scheme
; absolute sector = (logical sector / sectors per track) + 1
; absolute head   = (logical sector / sectors per track) MOD number of heads
; absolute track  = logical sector / (sectors per track * number of heads)
; ===========================================================================
      push dx          ; ax = LBA sector
      ; calculate sector
      xor  dx, dx
      div  cx          ; Sectors Per Track
      inc  dx          ; sector++
      push dx          ; sector -> stack
      ; ax = num of tracks
      xor  dx, dx
      div  bx          ; calculate head = (LSN/SPT)%2
      ; and  al, 01h     ; clear bit 1 to 7  al = al % 2
      mov  cx, ax      ; cylinder
      mov  bx, dx      ; head
      ; calculate track
      ;pop  ax
      ;push ax
      ;mov  bx, ABSDSK_SPT
      ;add  bx, ABSDSK_SPT
      ;xor  dx, dx
      ;div  bx        ; track = LSN / (2 * SPT)
      ;mov  ch, al    ; track
      pop  ax         ; clean it up - sector
      pop  dx
      ret
      endm

; ===========================================================================
abs2chs macro
; Input : AX = ssector
;         BX = swSectspertrack
;         CX = swHeads
; Output: AL = abstrack
;         CH = cylinder
;         CL = sector
;         DH = head
;         DL = drive?
; Abs sector = (SPT / LOGICAL SECTOR) +1
; Abs head   = (SPT / LOGICAL SECTOR) MOD heads
; Abs track  = SPT / (LOGICAL SECTOR * heads)
; ===========================================================================
        xor  dx, dx
        div  bx              ; calculate track
        inc  dl
        push cx
        mov  cl, dl          ; cl = sector
        xor  dx, dx
        pop  bx
        div  bx              ; compute head
        mov  ch, al          ; ch = cylinder
        mov  dh, dl          ; dh = head
        ret
        endm

; ===========================================================================
LBACHS macro
; BX = SPT
; CX = Heads
; AX = LBASECTOR
; ===========================================================================
     push ax
     push bx
     push cx
     xor  dx, dx
     div  bx
     inc  dl
     pop  bx       ; heads
     push dx       ; sector on stack
     xor  dx, dx
     div  bx
     mov  dh, dl
     mov  cx, ax   ; AL = ABS track
     pop  dx       ; sector from stack
     or   cx, dx
     pop  bx
     pop  bx
     endm

packcylsect macro xcyl, ysect
          mov  cx, xcyl
          xchg ch, cl           ; ch=low 8 bits of cylinder number
          ror  cl, 1            ; bits 8-9 of cylinder number...
          ror  cl, 1            ; ...to bits 6-7 in CL
          or   cl, ysect      ; sector number (bits 0-5)
          endm

; ===========================================================================
; Bios dependant routines for high level disk support
; ===========================================================================

; ===========================================================================
is_lba proc far
; ===========================================================================
       mov  ax, 4100h
       mov  bx, 55aah
       int  13h
       jc   nolba
       cmp  bx, 0aa55h
       jne  nolba
       test cx, 1
       jz   nolba
       clc
       ret
nolba:
       stc
       ret
is_lba endp

; ===========================================================================
resetdisk proc far
; Reset the floppy drive
; Input : DL = Drive
; Output: CF=CY = ERROR else OKAY
; ===========================================================================
      mov  ah, 0     ; reset disk
      DISK_INT
      sbb  ax, ax    ; sub w/ CF AX=-CF=-1,0
      inc  ax	 
      ret
resetdisk endp

; =====================================================================================================
l2hts proc far
; Convert floppy sector from logical to physical
; Calculate head, track and sector settings for int 13h
; IN: AX = logical sector, OUT: correct regs for int 13h
; =====================================================================================================
	push bx
	push ax

	mov bx, ax			; Save logical sector

	xor dx, dx			; First the sector
        div word ptr cs:[ABSDSK_SPT]
	add dl, 01h			; Physical sectors start at 1
	mov cl, dl			; Sectors belong in CL for int 13h
	mov ax, bx

	xor dx, dx			; Now calculate the head
        div word ptr cs:[ABSDSK_SPT]
	xor dx, dx
        div word ptr cs:[ABSDSK_HEADS]
	mov dh, dl			; Head/side
	mov ch, al			; Track

	pop ax
	pop bx

        ; mov dl, 0                       ; Boot device = 0
      ret
l2hts endp

; ===========================================================================
IPL_FloppyChk proc far
; Out : AL = NZ = no. Floppy drives 
; ===========================================================================
    int  11h
    test al, 1
    jz   short no_IPL_Floppy
         mov   cl, 6
         shr   al, cl
         inc   al
         ret
no_IPL_Floppy:
    mov  al, 0
    ret
IPL_FloppyChk endp

; ===========================================================================
readsectors proc far
; AX    = Logical Sector
; CX    = Count
; ES:BX = BUFFER
; ===========================================================================
IF (DEBUG)
      push ax
      push cx
      call put_hex
      _echo ' '
      mov  ax, cx
      call putnum
      _echo 'R'
      _newline
      pop  cx
      pop  ax
ENDIF
      mov  byte ptr cs:diskdrive, dl   ; Reference
      push di
      mov  di, RETRYCOUNT
readsect:
      ; Basicly, 255 sectors limited at once = 131Kb
      ; Better to read in 1 sector at a time.
      ; pusha
      push cx
      call l2hts
      pop  ax
      mov  ah, 02h                ; Params for int 13h: read floppy sectors
      mov  dl, byte ptr cs:[diskdrive]
      DISK_INT
      ; popa
      ; add es:bx + 512 bytes
      ; inc sector
      ; add  bx, word ptr bytespersector
      ; inc  ax
      jnc  readsectors_OK
IF (DEBUG)
           _echo '!'
ENDIF
           mov  dl, byte ptr cs:[diskdrive]
           call resetdisk             ; Check we've read them OK
           dec  di
           jnz  readsect
readsectors_err:
      sbb  al, al           ; carry: al=ff, else al=0
      and  al, ah           ; carry: error code, else 0
      stc
      jmp  readsectors_X
readsectors_OK:
IF (DEBUG)
      _echo '.'
ENDIF
readsectors_X:
      pop  di
      ret
readsectors endp

; ===========================================================================
writesectors proc far
; AX    = Logical Sector
; CX    = Count
; ES:BX = BUFFER
; ===========================================================================
IF (DEBUG)
      push ax
      push cx
      call put_hex
      _echo ' '
      mov  ax, cx
      call putnum
      _echo 'W'
      _newline
      pop  cx
      pop  ax
ENDIF
      mov  byte ptr cs:diskdrive, dl   ; Reference
      push di
      mov  di, RETRYCOUNT
writesect:
      ; Basicly, 255 sectors limited at once = 131Kb
      ; Better to read in 1 sector at a time.
      ; pusha
      push cx
      call l2hts
      pop  ax
      mov  ah, 03h                ; Params for int 13h: read floppy sectors
      mov  dl, byte ptr cs:[diskdrive]
      DISK_INT
      ; popa
      ; add es:bx + 512 bytes
      ; inc sector
      ; add  bx, word ptr bytespersector
      ; inc  ax
      jnc  writesectors_OK
IF (DEBUG)
           _echo '!'
ENDIF
           mov  dl, byte ptr cs:[diskdrive]
           call resetdisk             ; Check we've read them OK
           dec  di
           jnz  writesect
writesectors_err:
      sbb  al, al           ; carry: al=ff, else al=0
      and  al, ah           ; carry: error code, else 0
      stc
      jmp  writesectors_X
writesectors_OK:
IF (DEBUG)
      _echo '.'
ENDIF
writesectors_X:
      pop  di
      ret
writesectors endp

; ===========================================================================
diskstatus proc far
; Obtains the error/status code of the most recent disk I/O operation.
; Input  : DL = Drive
; Output : AH = CODE at 0040:0041
; ===========================================================================
       mov  ah, 1
       DISK_INT
       ret
diskstatus endp

; ===========================================================================
diskio proc far
;          CX = cylinder number
;          DH = head number
;          DL = drive number
;          SI = sector number
;          DI = sectors
;          ES:BX = Buffer to read write to 
; ===========================================================================
       mov  al, 1            ; this should be an error code                     
       cmp  ch, 3            ; this code can't write above 3FFh=1023
       ja   dskioerr         ; as cylinder # is limited to 10 bits.

     ;  xchg ch, cl           ; ch=low 8 bits of cyl number
     ;  ror  cl, 1            ; bits 8-9 of cylinder number...
     ;  ror  cl, 1            ; ...to bits 6-7 in CL
     ;  or   cl, si           ; or in the sector number (bits 0-5)
     ;  mov  al, di           ; count of sectors to read/write/...

       ; les  bx, [bp+04h]   ; Load 32 bit buffer ptr into ES:BX
       DISK_INT              ; process sectors
       sbb   al, al          ; carry: al=ff, else al=0
       and   al, ah          ; carry: error code, else 0
       DEBUGDOT
dskioerr:
       mov  ah, 0            ; extend AL into AX without sign extension
       ret
diskio endp

; ===========================================================================
lba_readwrite proc far
; Input : AX    = MODE COMMAND
;         DL    = Drive
;         DS:SI = DAP pointer
; output: return 0 if successful, else error code
; ===========================================================================
      push ds
      push si
      ; mov  dl, [bp+10]      ; drive (if or'ed with 80h a hard drive)
      ; mov  ax, [bp+8]       ; get the command
      ; lds  si, [bp+4]       ; get far dap pointer
      DISK_INT
      pop  si
      pop  ds
      mov  al, ah           ; place any error code into al
      mov  ah, 0            ; zero out ah           
      ret
lba_readwrite endp

; ===========================================================================
verify proc far
; AL = Sector count
; ===========================================================================
       xor  ah, ah
       mov  di, ax
       mov  si, cx
       mov  ah, 4
       call cs:diskio
       ret
verify endp

; ===========================================================================
format proc far
; Input : CX    = cylinder (track)
;         DL    = Drive
;         DH    = Head
;         ES:BX = Addr of buffer containing formatting info
; Output: CF=CY on error and AX = code
; ===========================================================================
       xor  ah, ah
       mov  di, ax
       mov  si, cx
       mov  ah, 05h
       call cs:diskio
       ret
format endp

; ===========================================================================
diskinit proc far
; Output: ES:SI = AbsDiskInfo structure [hh,ff] 
; ===========================================================================
        mov  al, 0
        mov  cs:munits, al
        xor  dx, dx
        call resetdisk
        ;
        int  11
        mov  cl, 6
        shr  al, cl
        and  ax, 03h
        inc  al
        ; Check for phantom floppy drive B:
        cmp  al, 1
        jne  equip_chk_des
        inc  al                   ; fake B:
equip_chk_des:
        mov  cx, ax
        xor  dx, dx
equip_loop:
;        push cx
;        call new_unit
;        mov  es:[UDSC_RUNIT][di], di
;        call floppy_type
;        cmp

        mov  byte ptr cs:[floppies], al
        ;
        mov  dl, 80h
        mov  ah, 8
        DISK_INT            ; GET drive parameters (should be in filesys.asm)
        jc   hddgetp_no
        mov  byte ptr cs:drives, dl
        xor  ax, ax
        mov  byte ptr cs:fixeddisks, al
        mov  byte ptr cs:floppydisks, al

        mov  dl, 0ffh
enumdrives:
        mov  ah, 15h
        DISK_INT
        jc   hddgetp_no
        dec  dl
        cmp  ah, 0
        je   enumchk
        cmp  ah, 3
        je   enumhdd
        inc  cs:floppydisks
        jmp  short enumchk
enumhdd:
        inc  cs:fixeddisks
enumchk:
        cmp  dl, 0
        jne  enumdrives
        jmp  short diskinitx
hddgetp_no:
        xor  dl, dl
diskinitx:
        push cs
        pop  es
        lea  si, cs:[fixeddisks]
        clc
        ret
diskinit endp

; ===========================================================================
disk_change proc far
; DL = Drive 
; returns 1 if disk has changed, 0 if not, 0xFFFF if error.
; ===========================================================================
      push si
      xor  si, si    ; AT&T 6300 crashes if SI not clear
      mov  ah, 16h   ; read change status 
      DISK_INT
      pop  si
      sbb  al, al
      jnc  fl_dc
      ; change line inactive 
      cmp  ah, 6     ; =6 then disk was changed
      jne  fl_dc
      mov  al, 1     ; YES!
fl_dc:
      cbw
      ret
disk_change endp

; ===========================================================================
setdisktype proc far
; DL = Drive
; AL = Type
FL_320_360K    EQU 01h
FL_320_360K12M EQU 02h
FL_12M         EQU 03h
FL_720K        EQU 04h
; ===========================================================================
      mov  ah, 17h   ; floppy set disk type for format
      DISK_INT
ret_ah:
      mov  al, ah
      mov  ah, 0         
      ret
setdisktype endp

; ===========================================================================
setmedia proc far
; BX = Sectors/track
; CX = number of tracks
; DX = drive no
; ===========================================================================
      dec  cx       ; no of cylinders - 1 last cyl no
      xchg ch, cl   ; CH = low 8 buts of last cyl number
      ror  cl, 1    ; extrt bits 8-9 of cylinder into cl 6-7
      ror  cl, 1          
      or   cl, bl   ; 0-5 ored w/ 7-6
      push di
      mov  ah, 18h  ; disk set media type for format
      DISK_INT
      jc   skip_int1e
      push es
      xor  dx, dx
      mov  es, dx
      cli
      pop  word ptr es:[1eh*4+2]    ; set int 0x1e table to es:di
      mov  es:[1eh*4], di
      sti
skip_int1e:
      pop  di
      mov  al, ah
      mov  ah, 0 		
      ret 
;      mov  ah, 18h            ; set media type for format
;      dec  ch
;      xchg ch, cl
;      ror  cl, 1
;      ror  cl, 1
;      or   cl, bl
;      int  13h
;      jc   skip1eDPT
;      push es
;      xor  dx, dx
;      mov  es, dx
;      cli
;      mov     [es:1eh*4],di
;      pop     word [es:1eh*4+2] ; set int 0x1e table to es:di
;      sti
;skip1eDPT:

setmedia endp

; ===========================================================================
readDisk        proc far
;       readDisk:       Reads a number of sectors into memory.
;
;       Call with:      DX:AX = 32-bit DOS sector number
;                       DI = number of sectors to read
;                       ES:BX = destination buffer
;                       ES must be 64k aligned (1000h, 2000h etc).
;
;       Returns:        CF set on error
;                       ES:BX points one byte after the last byte read. 
; ===========================================================================
		push    si
read_next:      push    dx
		push    ax

                ;
		; translate sector number to BIOS parameters
		;

		;
		; abs = sector                          offset in track
		;     + head * sectPerTrack             offset in cylinder
		;     + track * sectPerTrack * nHeads   offset in platter
		; 
		; t1     = abs  /  sectPerTrack         (ax has t1)
		; sector = abs mod sectPerTrack         (cx has sector)
		;
                div     word ptr ABSDSK_SPT
		mov     cx, dx

		;
		; t1   = head + track * nHeads
		;
		; track = t1  /  nHeads                 (ax has track)
		; head  = t1 mod nHeads                 (dl has head)
		;
		xor     dx, dx
                div     word ptr ABSDSK_HEADS

		; the following manipulations are necessary in order to 
		; properly place parameters into registers.
		; ch = cylinder number low 8 bits
		; cl = 7-6: cylinder high two bits
		;      5-0: sector
		mov     dh, dl                  ; save head into dh for bios
		ror     ah, 1                   ; move track high bits into
		ror     ah, 1                   ; bits 7-6 (assumes top = 0)
		xchg    al, ah                  ; swap for later
                mov     dl, byte ptr ABSDSK_SPT
		sub     dl, cl
		inc     cl                      ; sector offset from 1
		or      cx, ax                  ; merge cylinder into sector
		mov     al, dl                  ; al has # of sectors left

		; Calculate how many sectors can be transfered in this read
		; due to dma boundary conditions.
		push    dx

		mov     si, di                  ; temp register save
		; this computes remaining bytes because of modulo 65536
		; nature of dma boundary condition
		mov     ax, bx                  ; get offset pointer
		neg     ax                      ; and convert to bytes
		jz      ax_min_1                ; started at seg:0, skip ahead

		xor     dx, dx                  ; convert to sectors
                div     word ptr ABSDSK_SectSize

		cmp     ax, di                  ; check remainder vs. asked
		jb      ax_min_1                ; less, skip ahead
		mov     si, ax                  ; transfer only what we can

ax_min_1:       pop     dx

		; Check that request sectors do not exceed track boundary
                mov     si, ABSDSK_SPT
		inc     si
		mov     ax, cx                  ; get the sector/cyl byte
		and     ax, 03fh                ; and mask out sector
		sub     si, ax                  ; si has how many we can read
		mov     ax, di
                cmp     si, di                  ; see if asked <= available
		jge     ax_min_2
		mov     ax, si                  ; get what can be xfered

ax_min_2:       mov     si, RETRYCOUNT
		mov     ah, 2
                mov     dl, diskdrive

retry:          push    ax
		int     13h
		pop     ax
                jnc     read_ok
		push    ax
		xor     ax, ax          ; reset the drive
		int     13h
		pop     ax
		dec     si
		jnz     retry
		stc
		pop     ax
		pop     dx
		pop     si
		ret

read_next_jmp:  jmp     short read_next
read_ok:        xor     ah, ah                          
		mov     si, ax                  ; AX = SI = number of sectors read      
                mul     word ptr ABSDSK_SectSize ; AX = number of bytes read
		add     bx, ax                  ; add number of bytes read to BX
		jnc     no_incr_es              ; if overflow...

		mov     ax, es       
		add     ah, 10h                 ; ...add 1000h to ES
		mov     es, ax
		
no_incr_es:     pop     ax
		pop     dx                      ; DX:AX = last sector number

		add     ax, si
		adc     dx, 0                   ; DX:AX = next sector to read
		sub     di, si                  ; if there is anything left to read,
                jg      read_next_jmp           ; continue

		clc
		pop     si
		ret
readDisk        endp

; ===========================================================================
writeDisk        proc far
;       writeDisk:       writes a number of sectors into memory.
;
;       Call with:      DX:AX = 32-bit DOS sector number
;                       DI = number of sectors to write
;                       ES:BX = destination buffer
;                       ES must be 64k aligned (1000h, 2000h etc).
;
;       Returns:        CF set on error
;                       ES:BX points one byte after the last byte write. 
; ===========================================================================
		push    si
write_next:      push    dx
		push    ax

		;
		; translate sector number to BIOS parameters
		;

		;
		; abs = sector                          offset in track
		;     + head * sectPerTrack             offset in cylinder
		;     + track * sectPerTrack * nHeads   offset in platter
		; 
		; t1     = abs  /  sectPerTrack         (ax has t1)
		; sector = abs mod sectPerTrack         (cx has sector)
		;
                div     word ptr ABSDSK_SPT
		mov     cx, dx

		;
		; t1   = head + track * nHeads
		;
		; track = t1  /  nHeads                 (ax has track)
		; head  = t1 mod nHeads                 (dl has head)
		;
		xor     dx, dx
                div     word ptr ABSDSK_HEADS

		; the following manipulations are necessary in order to 
		; properly place parameters into registers.
		; ch = cylinder number low 8 bits
		; cl = 7-6: cylinder high two bits
		;      5-0: sector
		mov     dh, dl                  ; save head into dh for bios
		ror     ah, 1                   ; move track high bits into
		ror     ah, 1                   ; bits 7-6 (assumes top = 0)
		xchg    al, ah                  ; swap for later
                mov     dl, byte ptr ABSDSK_SPT
		sub     dl, cl
		inc     cl                      ; sector offset from 1
		or      cx, ax                  ; merge cylinder into sector
		mov     al, dl                  ; al has # of sectors left

                ; Calculate how many sectors can be transfered in this write
		; due to dma boundary conditions.
		push    dx

		mov     si, di                  ; temp register save
		; this computes remaining bytes because of modulo 65536
		; nature of dma boundary condition
		mov     ax, bx                  ; get offset pointer
		neg     ax                      ; and convert to bytes
                jz      wr_ax_min_1                ; started at seg:0, skip ahead

		xor     dx, dx                  ; convert to sectors
                div     word ptr ABSDSK_SectSize
		
		cmp     ax, di                  ; check remainder vs. asked
                jb      wr_ax_min_1                ; less, skip ahead
		mov     si, ax                  ; transfer only what we can

wr_ax_min_1:       pop     dx

		; Check that request sectors do not exceed track boundary
                mov     si, ABSDSK_SPT
		inc     si
		mov     ax, cx                  ; get the sector/cyl byte
		and     ax, 03fh                ; and mask out sector
                sub     si, ax                  ; si has how many we can write
		mov     ax, di
		cmp     si, di                  ; see if asked <= available
                jge     wr_ax_min_2
		mov     ax, si                  ; get what can be xfered

wr_ax_min_2:       mov     si, RETRYCOUNT
                mov     ah, 3
                mov     dl, diskdrive

wr_retry:          push    ax
		int     13h
		pop     ax
                jnc     write_ok
		push    ax
		xor     ax, ax          ; reset the drive
		int     13h
		pop     ax
		dec     si
                jnz     wr_retry
		stc
		pop     ax
		pop     dx
		pop     si
		ret

write_next_jmp:  jmp     short write_next
write_ok:        xor     ah, ah                          
                mov     si, ax                  ; AX = SI = number of sectors write      
                mul     word ptr ABSDSK_SectSize ; AX = number of bytes write
                add     bx, ax                  ; add number of bytes write to BX
                jnc     wr_no_incr_es              ; if overflow...

		mov     ax, es       
		add     ah, 10h                 ; ...add 1000h to ES
		mov     es, ax
		
wr_no_incr_es:  pop     ax
		pop     dx                      ; DX:AX = last sector number

		add     ax, si
                adc     dx, 0                   ; DX:AX = next sector to write
                sub     di, si                  ; if there is anything left to write,
                jg      write_next_jmp           ; continue

		clc
		pop     si
		ret
writeDisk        endp

; ===========================================================================
; Primitives for interfacing directly with NEC micro PD765 chip controller
; ===========================================================================

WaitForFDC proc far
     mov  dx, 03F4h ; dx = port
w_FDC_l:
     in   al, dx                          ; al = main status register
     test al, 00010000b                 ; IF Bit 4 != 0...
     jnz  w_FDC_l                        ; ...THEN go on waiting
     mov  al, [FDC_STATE]                ; al = FDC state
     cmp  al, 0                          ; IF state != "ready"...
     jne  w_FDC_l                        ; ...THEN go on waiting
     ret
WaitForFDC endp

DisableFDD proc far
; Turn off floppy drive motors
     call waitforFDC
     mov  dx, 03f2h   ; port for DOR
     mov  al, 00000000b
     out  dx, al      ; disable all FDDS and FDC
     ret
DisableFDD endp

SendFDCCommand proc far
; bl = command
        mov  dx, 03f4h
wait_loop:
        in   al, dx     ; status register
        test al, 80h    ; bit 7=0 = FDC not ready
        jz   wait_loop
        mov  al, bl
        mov  dx, 03f5h  ; write command to FDC
        out  dx, al
        ret
SendFDCCommand endp

GetFDCResult proc far
     mov  dx, 03f4h
g_FDC_r_l:
     in   al, dx   ; status
     test al, 80h
     jz  g_FDC_r_l
     mov  dx, 03f5h
     in   al, dx
     ret
GetFDCResult endp

RecalibrateFDD proc far

  call WaitForFDC ; wait until FDC is ready
  mov  al, 1
  mov [FDC_STATE], al ; FDC state = "command without result phase"

  mov bl, 00000111b ; bl = command byte 1 (command)
  call SendFDCCommand ; send command byte 1

  mov bl, [bootdrive] ; bl = command byte 2 (drive)
  call SendFDCCommand ; send command byte 2

  ret
RecalibrateFDD endp

EnableFDD proc far
  sti
  mov al, 1
  mov [FDC_STATE], al ; FDC state = "command without result phase"

  ; enable boot drive
  mov dl, [bootdrive] ; dl = boot drive
  cmp dl, 0 ; IF drive != 0 (a: under DOS)...
  jne check_b ; ...THEN wether it is 1 (b: under DOS)...
  mov al, 00011100b ; ...ELSE select drive 0
  jmp check_end
check_b:
  mov al, 00101101b ; select drive 1
check_end:
  mov dx, 03F2h ; dx = port of DOR
  out dx, al ; enable drive

  ; set data rate
  mov dx, 03F7h ; dx = port
  mov al , 00000000b ; al = data rate (500 kbit/s, standard)
  out dx, al ; write al to configuration register

  ; set other drive parameters
  mov bl, 00000011b
  call SendFDCCommand ; command byte 0 (command)
  mov bl, 00h ; step time = 16 ms, head move up time = 256 ms
  call SendFDCCommand ; command byte 1
  mov bl, 00000000b ; head move down time = 256 ms, DMA mode enabled
  call SendFDCCommand ; command byte 2

  ; recalibrate drive
  call RecalibrateFDD
  ret
EnableFDD endp

ReadPhysicalSector proc far
; ReadPhysicalSector: read physical sector from floppy disk
; in:  dh = head
;      ch = cylinder
;      cl = sector
;      es:di = destination address
; out: al = error code (0 = "no error", 1 = "error")
;-----------------------------------------------------------------------------
  push es
  push di ; save destination address
  push cx ; save sector
  push cx ; save sector
  push dx ; save head
  push cx ; save cylinder
  push dx ; save head
  push cx ; save cylinder
  push dx ; save head

  ; set up DMA channel
  mov  bl, 2 ; bl = DMA channel (FDC)
  call UnmaskDMAChannel ; unmask channel 2
  call DisableDMA ; disable DMA
  mov  al, 01000110b ; al = mode
  out  0Bh, al ; send mode to slave
  out  0Ch, al ; reset flip flop (value of al is not important)
  mov  al, 00h
  out  04h, al ; send low byte (0000XXh) of (temporary) destination address (001000h)
  mov  al, 10h
  out  04h, al ; send high byte (00XX00h) of (temporary) destination address (001000h)
  mov  al, 00h ; al = 64k page (XX0000h)
  out  81h, al ; send 64k page
  out  0Ch, al ; reset flip flop (value of al is not important)
  mov  al, 0FFh
  out  05h, al ; send low byte of data size (512 - 1 = 01FFh)
  mov  al, 01h
  out  05h, al ; send high byte of data size (512 - 1 = 01FFh)
  call EnableDMA ; enable DMA

  sti ; enable IRQs (perhaps disabled by int instruction)

  ; go to cylinder
  call WaitForFDC ; wait until FDC is ready
  mov  al, 1
  mov  [FDC_STATE], al ; FDC state = "command without result phase"
  mov  bl, 00001111b
  call SendFDCCommand ; command byte 0 (command)
  mov  bl, [bootdrive] ; bl = boot drive (D)
  pop  dx ; dh = head
  shl  dh, 2 ; bit 2 of dh = head (H)
  or   bl, dh ; bl = 00000HDD
  call SendFDCCommand ; command byte 1 (head, drive)
  pop  cx ; ch = cylinder
  mov  bl, ch ; bl = cylinder
  call SendFDCCommand ; command byte 2 (cylinder)

  ; prepare reading sector
  call WaitForFDC ; wait until FDC is ready
  mov  al, 2
  mov [FDC_STATE], al ; FDC state = "reading sector"

  ; command byte 0 (single track operation, MFM, no deleted address marks)
  mov  bl, 01100110b ; bl = command byte
  call SendFDCCommand

  ; command byte 1 (head, cylinder)
  mov  bl, [bootdrive] ; bl = boot drive (D)
  pop  dx ; dh = head (H)
  shl  dh, 2 ; bit 2 of dh = head
  or   bl, dh ; bl = 00000HDD
  call SendFDCCommand

  ; command byte 2 (cylinder)
  pop  cx ; ch = cylinder
  mov  bl, ch ; bl = cylinder
  call SendFDCCommand

  ; command byte 3 (head)
  pop  dx ; dh = head
  mov  bl, dh ; bl = head
  call SendFDCCommand

  ; command byte 4 (first sector to read)
  pop  cx ; cl = sector
  mov  bl, cl ; bl = sector
  call SendFDCCommand

  ; command byte 5 (sector size)
  mov  bl, 2 ; bl = sector size (512 byte)
  call SendFDCCommand

  ; command byte 6 (last sector to read)
  pop  cx ; cl = sector
  mov  bl, cl ; bl = sector
  call SendFDCCommand

  ; command byte 7
  mov  bl, 27 ; bl = GAP3 length (standard)
  call SendFDCCommand

  ; command byte 8
  mov  bl, 0FFh ; bl = data length (standard)
  call SendFDCCommand

  ; wait for command execution and analyse results
  call WaitForFDC ; wait until FDC is ready
  mov  al, [FDC_ERROR] ; al = FDC error code
  cmp  al, 0 ; IF error code == 0 ("no error")...
  je   copy_data ; ...THEN copy read data to destination address
  pop  dx ; clear stack
  mov  al, 1 ; al = error code = "error"
  jmp  rps_end

  ; copy data to destination address
copy_data:
  pop  di
  pop  es ; es:di
  push ds
  mov  ax, 0050h
  mov  ds, ax
  mov  si, 1000h ; ds:si = source address
  mov  cx, 512 ; copy 512 byte
  cld
  rep  movsb ; copy data
  pop  ds
  mov  al, 0 ; al = error code (no error)

rps_end:
  ret
ReadPhysicalSector endp

;-----------------------------------------------------------------------------
Read_Sector proc far ; read sector from floppy disk
;-----------------------------------------------------------------------------
; in:  bx = LSN
;      es:di = destination address
; out: al = error code (0 = "no error", 1 = "error")
;-----------------------------------------------------------------------------
    mov cx, RETRYCOUNT
read_loop:
    push bx ; save LSN
    push cx ; save counter
    push di ; save destination offset

    ; read sector
    mov  ax, bx
    ;call cs:LBA_To_CHS ; cl = sector, ch = cylinder, dh = head
    call ReadPhysicalSector ; read sector
    cmp al, 0 ; IF error code == 0 ("no error")...
    je rs_success1 ; ...THEN terminate reading sector

    call RecalibrateFDD ; recalibrate drive

    pop di ; edi = destination offset
    pop cx ; load counter
    pop bx ; bx = LSN
    loop read_loop ; read again

    ; reading sector failed
    mov al, 1 ; al = error code (error)
    jmp readsect_end

    ; sector successfully read
rs_success1:
    pop ax ; clear stack
    pop ax ; clear stack
    pop ax ; clear stack
    mov al, 0 ; al = error code ("no error")
readsect_end:
    ret
Read_Sector endp

;/*
; * Read a sector from the disk drive
; */
;read_sector(drive, sector, buffer) asm
;{
;                PUSH    DS                      ; Save DS
;                POP             ES                      ; Set ES
;                MOV             BX,4[BP]        ; Get buffer
;                MOV             AX,6[BP]        ; Get sector
;
;
;                XOR             DX,DX           ; Zero high
;                DIV             WORD PTR _sectrk; Calculate track
;                MOV             CL,DL           ; CL = sector
;                INC             CL                      ; 1-
;                XOR             DX,DX           ; Zero high
;                DIV             WORD PTR _numhead; Compute head
;                MOV             CH,AL           ; CH = cylinder
;                MOV             DH,DL           ; DH = head
;
;
;                MOV             DL,8[BP]        ; DL = drive
;                MOV             DI,3            ; Try three times
;read1:  MOV             AX,0201h        ; Read 1 sector
;                INT             13h                     ; Call BIOS
;                JNC             read2           ; Success
;                DEC             DI                      ; Reduce count
;                JNZ             read1           ; Keep trying
;                PUSH    AX                      ; Pass parameter
;                CALL    _disk_error     ; Report an error
;                POP             AX                      ; Clean stack
;read2:  XOR             AX,AX           ; Zero return
;}

;.386p
;L6:
;    PUSHAD
;    DB    66h, 6Ah, 0    ; PUSH Long 0
;    PUSH    EAX
;    PUSH    ES
;    PUSH    BX
;    PUSH    CX
;    PUSH    16
;    MOV    SI, SP
;    PUSH    DX
;    call  is_lba
;    jc    L6
;
;    POP      DX
;    MOV    AX, 4200h
;  L8:
;    INT       13h
;  L7:
;    MOV    SP, SI
;    POPA
;    POPAD
;.286
;    RET

; ===========================================================================
fix_filename proc far
; SI : FILENAME.EXT
; ===========================================================================
        lea  di, dos_filespec
        mov  cx, 8
fixfile0:
        mov  al, byte ptr [si]
        cmp  al, '.'
        je   ff1
        mov  byte ptr [di], al
        cmp  al, 20h
        je   ff1
        inc  si
        inc  di
        loop fixfile0
ff1:
        inc  si
        mov  di, offset dos_filespec+8
        mov  cx, 3
fixfile1:
        mov  al, byte ptr [si]
        mov  byte ptr [di], al
        inc  si
        inc  di
        loop fixfile1
ff2:    lea  si, dos_filespec
        ret     
dos_filespec db 11 dup(20h),0
fix_filename endp

; ===========================================================================
remove_filedot proc far
; ===========================================================================
         mov  al, ' '
         mov  byte ptr [di+7], al    ; remove the dot
         ret
remove_filedot endp

; ===========================================================================
ExecProgram proc far
; ES:SI = Program to exec
; The following four lines set up a very basic Program Segment Prefix,
; aka PSP, which provides some information for DOS programs. For
; instance, CD 20 = 'int 20h', or 'return to DOS' -- a program can
; use this code to quit
; Portions from http://mikeos.berlios.de/
; ===========================================================================
        mov  byte ptr es:[si+0], 0CDh
        mov  byte ptr es:[si+1], 020h ; INT 20h
        mov  byte ptr es:[si+2], 0A0h
        mov  byte ptr es:[si+2], 000h ; a000h = ALL RAM AVAIL

	cli
        pusha                         ; Save all registers and stack pointer
	push ds
	push es
	push ss
	push sp
	sti

        xor  ax, ax                   ; Clear registers to be DOS compatible
        xor  bx, bx
        xor  cx, cx
        xor  dx, dx
	;xor si, si
        xor  di, di
        xor  bp, bp
        mov  byte ptr cs:[ProgRun], 1

	push  es
	mov   bx, 0100h
	push  bx
	; push  si
	retf
        ; End of the program run
        mov byte ptr cs:[ProgRun], 0
	cli
	pop  sp
	pop  ss
        pop  es                          ; common registers
        pop  ds
	popa
	sti
	clc
	ret
ProgRun db 0
ExecProgram endp

; ===========================================================================
LoadFile proc far
; IN : DI    = location of filename
;      ES:SI = location in RAM to load file
; OUT: BX    = file size, carry set if program not found on the disk
; NOTE: Based on free bootloader code by E Dehling.
; Portions from http://mikeos.berlios.de/
; ===========================================================================
        clc
        ;
        mov  word ptr cs:[load_buf+0], si
        mov  word ptr cs:[load_buf+2], es
        push si
        mov  si, di
        call fix_filename
        mov  di, si
        pop  si
	mov  word ptr cs:[filename_loc], di
	xor  ax, ax                         ; eax
	mov  dl, cs:[diskdrive]
	call resetdisk
        jc   loadflop
        call cs:loadimage
	jnc  loadok
loaderr:
        stc
        jmp  loadexit
loadflop:
        mov  ax, 3
        jmp  loaderr
	; _panic                     ; BUG!
loadok: clc
loadexit:
        ret
; ===========================================================================
NextCluster proc far
; AX = Cluster
; ===========================================================================
	xor  dx, dx
	mov  bx, 2
;        xor  dx, dx
	div  bx				; DX = [CLUSTER] mod 2
        or   dx, dx          ; If DX = 0 [CLUSTER] = even, if DX = 1 then odd
        jz   _even           ; If [CLUSTER] = even, drop last 4 bits of word
                             ; with next cluster; if odd, drop first 4 bits
_odd:
	mov  ax, word ptr cs:[cluster]
	mov  bx, 3
	mul  bx
	mov  bx, 2
;        xor  dx, dx
        div  bx                 ; AX = address of word in FAT for the 12 bits
        lea  si, cs:[os_buffer]
	add  si, ax
	mov  ax, word ptr ds:[si]
        shr  ax, 4         ; Shift out first 4 bits (belong to another entry)
	mov  word ptr cs:[cluster], ax
	cmp  ax, 0FF8h
        jae  _end
	add  word ptr cs:[loadpos], 512
        jmp  _end           ; Onto next sector!
_even:
        mov  ax, word ptr cs:[cluster]
	mov  bx, 3
	mul  bx
	mov  bx, 2
;        xor  dx, dx
	div  bx
	lea  si, cs:[os_buffer]		; AX = word in FAT for the 12 bits
	add  si, ax
	mov  ax, word ptr ds:[si]
	and  ax, 0FFFh			; Mask out last 4 bits
	mov  word ptr cs:[cluster], ax		; Store cluster
	cmp  ax, 0FF8h
        jae  _end
	add  word ptr cs:[loadpos], 512	; Increase buffer pointer
        jmp  _end
_end:
        clc
        ret
nextcluster endp

; ===========================================================================
loadimage proc far
; ===========================================================================
        ; Ready to read first block of data
        mov  ax, 19                 ; Root dir starts at logical sector 19
        mov  cl, 14                 ; And read 14 of them
        lea  si, cs:[os_buffer]     ; ES:BX should point to our buffer
        mov  bx, cs
	mov  es, bx
	mov  bx, si
        ; mov  dl, cs:[diskdrive]
        call readsectors
        jnc  scan_root
             stc
             ret
scan_root:
        ;
        mov  ax, ds                 ; ES:DI = root directory
	mov  es, ax
        mov  cx, 244                ; Search all entries in root dir
        mov  ax, 0                  ; Searching offset 0 in root dir
        lea  di, cs:[os_buffer]
        ;
next_root_entry:
        push cx
        xchg cx, dx                 ; We use CX in the inner loop...
        mov  si, cs:[filename_loc]  ; DS:SI = location of filename to load
        mov  cx, 11                 ; Length of filename, for comparison
	rep  cmpsb
	je   found_file_to_load
        add  ax, 32                 ; Bump searched entries by 1 (offset + 32 bytes)
        lea  di, cs:[os_buffer]     ; Point root-dir at next entry
        add  di, ax
        xchg dx, cx                 ; Swap, as we need the 'outer' CX in this loop
IF (DEBUG)
         push si
         push es
         push cx
         mov  cx, 32
         push ax
         push cs
         pop  es
         mov  si, di
         call debugdump
         pop  ax
         pop  cx
         pop  es
         pop  si
ENDIF
        pop  cx
	loop next_root_entry
             stc
             ret
found_file_to_load:                 ; Now fetch cluster and load FAT into RAM
        ;
        mov  ax, word ptr es:[di+0FH]
	mov  word ptr cs:[cluster], ax
        mov  ax, word ptr es:[di+10h]        ; Get file size and store it
        mov  word ptr cs:[file_size], ax
        ;
        mov  ax, 1                  ; Sector 1 = first sector of first FAT
        mov  cl, 09h                ; And read 9 of them :-)
        mov  bx, ds
	mov  es, bx
        lea  si, cs:[os_buffer]  
	mov  bx, si
        mov  dl, cs:[diskdrive]
        call readsectors
        jc   loadimageerr
read_fat_ok:
        xor  ax, ax
        mov  ax, word ptr cs:[load_buf+2]
	mov  es, ax
        mov  ax, word ptr cs:[load_buf+0]
        add  ax, 0100h
        mov  bx, ax
        ; mov  bx, 0100h 
	mov  word ptr cs:[loadpos], bx
	; mov  ax, 2000h
	; mov  es, ax
	; xor  bx, bx     ; si 
load_file_sector:
        ; push ax                    ; Save in case we (or int calls) lose it
	; mov  ax, [load_position]

        mov  ax, word ptr cs:[cluster]       ; Convert sector to logical
	add  ax, 31
        xor  cx, cx
        mov  cl, 01h
        ; Set buffer past what we've already read
        mov  bx, word ptr cs:[load_buf+2]
        mov  es, bx
	mov  bx, word ptr cs:[loadpos]  ; ,bx
        ; BUG - Clobbering the FAT data !!
        ; mov  bx, ds
        ; mov  es, bx
        ; lea  si, cs:[os_buffer]  
        ; mov  bx, si
        ; mov  dl, cs:[diskdrive]
        call readsectors
        jc   loadimageerr
calculate_next_cluster:
; If there's no error...
	mov  ax, word ptr cs:[cluster]
        call nextcluster
        ; jnc  load_file_sector 
        cmp  ax, 0fffh
        je   loadimage_ok
        cmp  ax, 0ff8h
        jb   load_file_sector
loadimage_ok:
        ; pop  ax
        ; Clear stack
        mov  bx, word ptr cs:[file_size]; Get file size to pass back in BX
        xchg bh, bl
        clc
        jmp  loadimage_x
loadimageerr:
	stc				; If file never found, return with carry set
loadimage_x:
	ret
loadimage endp
filename_loc   dw 0
load_buf       dd 0
loadpos        dw 0
cluster        dw 0         ; Cluster of the file we want to load
file_size      dw 0         ; Size of the file
os_buffer      db 8196 dup (0) ; +(512-1)) dup(0)
LoadFile endp
