'*****************************************************************************
'*
'* INT 13H (BIOS) - FDD and HDD BIOS service emulation
'*
'*****************************************************************************

//DEBUG.ON 'uncomment to enable debug messages (can be slow for hi-freq events)

//Parameters
public dim EMULATE_FDD_ONLY as boolean=false 'HDD-controller already emulated

dim disk as object, drive as object, FDD as boolean, HDD as boolean
dim status as byte, longmode as boolean, i1 as integer, i2 as integer
dim track as word, head as word, sector as word, seg as word, offs as word
dim pos as dword, sectsize as dword, workmode as byte, dw as dword, b as byte

//interrupt handler
public function EINT_13H as boolean

result=false 'if emulation used to capture real "int 13", need result

'/////////////////////////////// STARTUP //////////////////////////////////////

//variables
dim i as integer, j as integer, k as integer

//detect disk type (FDD and HDD) and get it by index
FDD=cpu.DL<0x80 : HDD=not(FDD)
if cpu.DL<0x80 then //Floppy Drive 
   if cpu.DL>=emuDrives.FDDCount then 'no more then floppy drives 
      cpu.AH=1 : cpu.FLAGS.CF=true : exit : end
   disk=emuDrives.FDD(cpu.DL) : FDD=true : HDD=false
else //Hard Drive
   if (cpu.DL-0x80)>=emuDrives.HDDCount then 'no more then hard drives 
      cpu.AH=1 : cpu.FLAGS.CF=true : exit : end
   disk=emuDrives.HDD(cpu.DL-0x80) :  FDD=false : HDD=true
end if

disk.Accessed=true

result=true 'inform emulator, that real "int 13" processed (no need to call)

//return error if disk empty or not latched
if (not disk.Ready) or (not disk.Latched) or (disk.Size=0) then 
   cpu.AH=1 : cpu.FLAGS.CF=true : exit : end 
drive=disk

//detect disk format if needed
if not(disk.Detected) then
   if ((FDD) and (not DetectFloppyDisk(disk))) or _
      ((HDD) and (not DetectHardDisk(disk))) then 
      cpu.AH=1 : cpu.FLAGS.CF=true : exit : end if 
 //update disk param table by detected data
// ?? "DETECTED"
 if FDD then //floppy disk table at vector INT 1Eh
    mem.Pos=shl(mem.Word(0x1E*4+2),4)+mem.Word(0x1E*4) 'table starting address
    if mem.Pos>512 then : mem.Pos=mem.Pos+4
       mem.Byte=disk.SectorCount-1                     'sectors per track
    end if 
 else //HDD disk tables at vectors INT 41H, 46H
   if cpu.DL=0x80 then : mem.Pos=shl(mem.Word(0x41*4+2),4)+mem.Word(0x41*4)
   elseif cpu.DL=0x81 then : mem.Pos=shl(mem.Word(0x46*4+2),4)+mem.Word(0x46*4)
   else : mem.Pos=0 : end if
   if mem.Pos>512 then 
      mem.Word=disk.CylinderCount                 'cylinders/tracks count
      mem.Byte=disk.HeadCount                     'heads/sides count
      mem.Byte=disk.SectorCount                   'sectors on track 
   end if
 end if
 //cpu.AH=0x06 : cpu.FLAGS.CF=true : exit 'disk changed???
end if

select case true

'*****************************************************************************
'* INT 13H AH=00h (BIOS) RESET DISK SYSTEM
'* Params: DL - drive index
'* Result: AH - state (0 - success, <>0 - failure)
'*         CF - error flag (0 - no error, 1 - error)
'*****************************************************************************

case (cpu.AH=0) : cpu.AH=0 : cpu.FLAGS.CF=false : status=cpu.AH 'success
 ?? "[INT 13h] Reset disk system (AH=0, DL=";Hex(cpu.DL,2);"h)"

pc.SetIRQ(6,true)
//dbg.Break

'*****************************************************************************
'* INT 13H AH=01h (BIOS) STATUS OF LAST OPERATION
'* Params: DL - drive index
'* Result: AH - last operation state
'*         CF - error flag (0 - no error, 1 - error)
'*****************************************************************************

case (cpu.AH=1) : cpu.AH=status :  cpu.FLAGS.CF=(cpu.AH<>0)
 ?? "[INT 13h] Status of last operation (AH=0, DL=";Hex(cpu.DL,2);"h)"

'*****************************************************************************
'* INT 13H AH=02h,0Ah (BIOS) READ SECTORS 
'*         AH=03h,0Bh (BIOS) WRITE SECTORS
'*         AH=04h     (BIOS) VERIFY SECTORS
'* Params: DL - drive index
'*         AL - number of sectors
'*         CH - track (cylinder) index (bits 0..7)
'*         CL - sector index (bits 0..5), HDD track index bits 8..9 (bits 6..7)  
'*         DH - head index
'*         ES:BX - target memory address
'* Result: AH - state (0 - success, <>0 - failure)
'*         CF - error flag (0 - no error, 1 - error)
'*         AL - number of actually processed sectors
'*****************************************************************************

case ((cpu.AH>=2) and (cpu.AH<=4)) or (cpu.AH=0xA) or (cpu.AH=0xB) 

 //debug messagess
 ?? "[INT 13h] "; : b=cpu.AH and 7:  
 ?? iif(b=2,"Read",iif(b=3,"Write","Verify"));" sectors";
 ?? "(mem=";Hex(cpu.ES,4);":";Hex(cpu.BX,4);"h,";
 ??

 //check params
 longmode=(cpu.AH=0xA) or (cpu.AH=0xB)
 select case cpu.AH
   case 0x2,0xA : workmode=0 'READ
   case 0x3,0xB : workmode=1 'WRITE
   case 0x4     : workmode=2 'VERIFY
 end select

 cpu.AH=1 : cpu.FLAGS.CF=true : status=cpu.AH 'error by default
 if (longmode) and (FDD) then : ?? "no longmode on FDD" : exit: end if
 if cpu.AL=0 then : ?? "zero sector illegal" : exit : end if
 //calc params
 track=iif(FDD,cpu.CH,cpu.CH or shl(cpu.CL and 0xC0,2)) : head=cpu.DH
 i1=cpu.CL and 0x3F : i2=i1+cpu.AL-1 : sector=i1 : seg=cpu.ES : offs=cpu.BX
 sectsize=disk.SectorSize
 ?? "h=";head;",t=";track;",s=";sector;",sc=";cpu.AL;",res=";
 cpu.AL=0 //count of transferred sectors
 for i=i1 to i2  //transfering sectors
   //if longmode then correct params
   if sector>disk.SectorCount then  //no more sectors on track
      if HDD then 
         sector=1 : head=head+1 
         if head>=disk.HeadCount then : head=0 : track=track+1 : end if 
      else : exit : end if 'error if FDD - some OS-es used it to detect floppy format
   end if
   //calc sector position on disk
   pos=track*disk.HeadCount+head 'absolute track index
   pos=pos*disk.SectorCount+sector-1 'absolute sector index
   pos=disk.StartOffset+pos*sectsize 'sector offset from disk start
   if (pos+sectsize)>disk.Size then exit 'non-complete sector
   //copy sector //TODO - protected mode seg.selectors???
   mem.pos=cpu.ESPhisBase/*shl(seg,4)*/+offs : disk.pos=pos 
//   if shl(seg,4)<>cpu.ESPhisBase then 
  //   ? "[INT 13] UNSUPPORTED ES BASE: ";shl(seg,4);"<>";cpu.ESPhisBase
  //   dbg.Break
 //  end if
  
   select case workmode
     case 0 : k=mem.Copy(disk.Object,sectsize) 'READ
     case 1 : k=disk.Copy(mem.Object,sectsize) 'WRITE
     case 2 : k=sectsize                       'VERIFY
   end select
   if k<>sectsize then : ?? cpu.AL;")" : exit : end if 'non-complete sector
   offs=offs+sectsize : sector=sector+1 : cpu.AL=cpu.AL+1 
 next i
 cpu.AH=0 : cpu.FLAGS.CF=false : status=cpu.AH : ?? cpu.AL;")" //success

'*****************************************************************************
'* INT 13H AH=05h (BIOS) FORMAT TRACK 
'* Params: RTFM :)
'* Result: AH - last operation state
'*         CF - error flag (0 - no error, 1 - error)
'*****************************************************************************

case (cpu.AH=5) : cpu.AH=0 : cpu.FLAGS.CF=false : status=cpu.AH 'success
 ?? "[INT 13h] Format track (AH=5, DL=";Hex(cpu.DL,2);"h)"

'*****************************************************************************
'* INT 13H AH=08h (BIOS) GET DRIVE PARAMS
'* Params: DL - drive index
'* Result: AH - last operation state
'*         CF - error flag (0 - no error, 1 - error)
'*	   BL - drive type
'*         CH - maximum track (cylinder) index (bits 0..7)
'*         CL - sector index (bits 0..5),max HDD track idx bits 8..9(bits 6..7)  
'*         DH - maximum head index
'*         DL - number of drives
'*	   ES:DI - address of floppy parameter table
'*****************************************************************************

case (cpu.AH=8) 
 cpu.AH=0 : cpu.FLAGS.CF=false : status=cpu.AH 'success
 cpu.BL=0 : if FDD then : i=disk.Size //FDD type //TOCHECK - similar to CMOS???
        if i>1500000 then : cpu.BL=6 '2.88 Mb
    elseif i>1300000 then : cpu.BL=4 '1.44 Mb
    elseif i>1000000 then : cpu.BL=2 '1.2 Mb
    elseif i>500000  then : cpu.BL=3 '720 Kb
    elseif i>300000  then : cpu.BL=1 '360 Kb
    else : cpu.BL=0 'unknown
 end if : end if
 i=disk.CylinderCount-1 'max. track index (0-based)
 cpu.CH=i : cpu.CL=shr(i and 0x300,2) or (disk.SectorCount and 0x3F)
 cpu.DH=disk.HeadCount-1 'max. head index (0-based)
 cpu.DL=2 'count of answered drives (2 FDD or HDD)
 if FDD then 'read vector of 1Eh interrupt - FDD params table
    cpu.DI=mem.Word(0x1E*4) :  cpu.ES=mem.Word(0x1E*4+2) : end

 ?? "[INT 13h] Get drive params (AH=8, DL=";Hex(cpu.DL,2);"h)"

'*****************************************************************************
'* INT 13H AH=09h (BIOS) PROGRAM HDD CONTROLLER BY TABLE PARAMS
'*         AH=0Dh (BIOS) RESET HDD
'*         AH=10h (BIOS) HDD READY
'*         AH=11h (BIOS) RECALIBRATE HDD
'*         AH=14h (BIOS) HDC DIAGNOSTIC
'* Params: DL - drive index
'* Result: AH - last operation state
'*         CF - error flag (0 - no error, 1 - error)
'*****************************************************************************

case (cpu.AH=9) or (cpu.AH=0xD) or (cpu.AH=0x10) or (cpu.AH=0x11) or _
     (cpu.AH=0x14) 
 cpu.AH=iif(HDD,0,1) 'supported by HDDs only (success)
 cpu.FLAGS.CF=cpu.AH<>0 : status=cpu.AH
 ?? "[BIOS INT13] Program HDD"

'*****************************************************************************
'* INT 13H AH=0Ch (BIOS) HDD SEEK TO TRACK
'* Params: DL - drive index
'*         CH - track (cylinder) index (bits 0..7)
'*         CL - sector index (bits 0..5), HDD track index bits 8..9 (bits 6..7)  
'*         DH - head index
'* Result: AH - state (0 - success, <>0 - failure)
'*         CF - error flag (0 - no error, 1 - error)
'*****************************************************************************

case (cpu.AH=0xC) 
 cpu.AH=1 : cpu.FLAGS.CF=true : status=cpu.AH 'error by default
 if FDD then exit 'HDD only
 track=cpu.CH or shl(cpu.CL and 0xC0,2) : head=cpu.DH : sector=cpu.CL and 0x3F
 if (track>=disk.CylinderCount) or (sector<1) or (sector>disk.SectorCount) or _
    (head>disk.HeadCount) then exit
 cpu.AH=0 : cpu.FLAGS.CF=false : status=cpu.AH 'success
 ?? "[BIOS INT13] Hdd seek"

'*****************************************************************************
'* INT 13H AH=10h (BIOS) HDD DRIVE READY
'* Params: DL - drive index
'* Result: AH - state (0 - success, <>0 - failure)
'*         CF - error flag (0 - no error, 1 - error)
'*****************************************************************************

case (cpu.AH=0x10)
 cpu.AH=1 : cpu.FLAGS.CF=true : status=cpu.AH 'error by default
 if FDD then exit 'HDD only
 if disk.Ready then : cpu.AH=0 : cpu.FLAGS.CF=false  'success
 else : cpu.AH=0xAA : cpu.FLAGS.CF=true : end if 'failure
 status=cpu.AH
 ?? "[BIOS INT13] Check hdd ready"

'*****************************************************************************
'* INT 13H AH=15h (BIOS) GET DISK/DRIVE TYPE/EXISTANCE
'* Params: DL - drive index
'* Result: AH - type code (0 - no drive, 1-2 - FDD wo/w changeline, 3 - HDD )
'*         CF - error flag (0 - no error, 1 - error)
'*         CX:DX - number of sectors
'*****************************************************************************

case (cpu.AH=0x15) 
 cpu.FLAGS.CF=false : status=0 'no error
 if FDD then : cpu.AH=1 : else 
 cpu.AH=3 : dw=disk.HeadCount*disk.CylinderCount*disk.SectorCount
 cpu.DX=dw : cpu.CX=shr(dw,16) : end if 
 ?? "[BIOS INT13] Get disk type/existance"

'//////////////////////////////// FINAL //////////////////////////////////////

case else : //cpu.AH=1 : cpu.FLAGS.CF=true : status=cpu.AH 'error
 result=false 'inform emulator, that real "int 13" not processed (need to call)
 //result=true
 ?? "[BIOS INT13] Unsupported call with AH=";Hex(cpu.AH,2) 
end select : end function
