{===========================================================================
   Program Name     : n/a
   System Name      : Atari ST and TT
   Program Author   : Copyright (c) 1991 ICD Inc.
                        All rights reserved
   Language         : Personal Pascal (Pascal Plus)
   Module Name      : DMA.I
   Purpose          : This is the header file for DMA.O.  It contains
                        routines for direct hard drive access as well
                        as an interface to tables within the ICD hard
                        disk driver (ICDBOOT.PRG).
===========================================================================}

{
   This revision posted 6-6-1991.  A number of significant changes have
   been made.  The FSCSI and SFSCSI commands are gone.  Full scsi commands
   are now indicated by a bit in a FLAGS word.  SCSI ID is no longer
   embedded in the command frame.  SCSI ID's can now range from 0 to 15.
   0 to 7 will reference devices connected to the DMA port on ST and TT.
   8 through 15 reference devices connected to the TT internal SCSI bus.
}


{       Structures kept within the ICDBOOT hard disk driver
----------------------------------------------------------------}

const
   read_dir   = 0;
   write_dir  = 1;
   fullscsi   = 2;
   one_second = 150000;

type
   bios_block = record
                recsiz: integer;        { # bytes/sector }
                clsiz:  integer;        { # sectors/cluster }
                clsizb: integer;        { # bytes/cluster }
                rdlen:  integer;        { # sectors/dir }
                fsiz:   integer;        { # sectors/FAT }
                fatrec: integer;        { sector # of 2nd FAT }
                datrec: integer;        { sector # of first data cluster }
                numcl:  integer;        { # data clusters }
                bflags: integer;        { bits/FAT entry flag: 1=16 bit? }
           end;

   drive_parameters = record
                units: integer;         { # SCSI IDs online }
                id:    packed array [0..15] of byte;
                lun:   packed array [0..15] of byte;
                start: array [0..15] of long_integer;
                bpb:   array [0..15] of bios_block;
                change:packed array [0..15] of byte;
                sizes: array [0..15] of long_integer;
            end;

   p_drive_parameters = ^drive_parameters;

   barray   = packed array[0..0] of byte;
   bptr     = ^barray;                    { generic byte pointer }

   Com_Rec  = record case integer of
                1: (data: packed array[1..16] of byte);
                2: (buf:  barray);
              end;

   Dat_rec  = record case integer of
                1: (bytes: Packed Array [0..511] of Byte);
                2: (words: Packed Array [0..255] of Integer);
                3: (longs: Packed Array [0..127] of Long_Integer);
                4: (buf:   barray);
              end;



var
   ID,LUN:  integer;            { global SCSI ID/LUN                    }
   Command: Com_Rec;            { global SCSI command buffer            }
   SecBuf:  Dat_Rec;            { global sector buffer                  }



{==========================================================================
               Interface to ICDBOOT.PRG
               ------------------------

ICDBOOT: return ptr to drive parameter block

    return:
        NIL     = Driver is not present
        !NIL    = ptr to parameter block

ICDHEAD: return ptr to beginning of ICDBOOT image. This is used by internal
        ICD utilities and the exact structure is undocumented.

    return:
        NIL     = Driver is not present
        !NIL    = ptr to beginning of driver

RELOG:  Rescan all partitions and reset all internal tables

    return:
        TRUE:   = success
        FALSE:  = driver not present or bad version of ICDBOOT

COLDBOOT: reboot system
}

Function ICDBOOT: p_drive_parameters; external;

Function ICDHEAD: bptr; external;

Function RELOG: boolean; external;      { rescan all partitions         }

Procedure COLDBOOT; external;





{==========================================================================
               Calls to perform SCSI commands
               ------------------------------

DMA:    Perform SCSI command (no retries, no REQUEST on error).

    in:
      ID        = SCSI ID of device (LUN is embedded in COM_FRAME; see below)
      COM_FRAME = command bytes (packed byte array)
      DATA_FRAME= data buffer (packed byte array)
      FLAGS     = 16 bits as follows:
                     bit[0]=DMA direction.
                     bit[1]=FULL SCSI. 
                     bits[2]-[15] are RESERVED at this time.

                  DMA transfer direction bit:
                     0 = read data from drive,
                     1 = write data to drive.

                  FULL SCSI bit:
                     0 = ACSI format
                            COM_FRAME is always 6 bytes long.
                            COMMAND BYTE is byte[1]
                            LUN is top 3 bits of byte[2]
                     1 = SCSI format
                            COM_FRAME length is variable; length in byte[1]
                            COMMAND BYTE is byte[2]
                            LUN is top 3 bits of byte[3]
      BLOCKS    = number 512 byte blocks to transfer (round up
                    for non-512 byte multiples)
      TIMEOUT   = timeout value used for command completion (not
                    SCSI selection timeout - selection is fixed at
                    100 ms). 150000 is one second and is NOT dependent
                    on cpu or speed

    return:
      0         = success
      2         = indicates an error (do a REQUEST to get actual
                    error sense code)
      -1        = timeout after sending command (drive is not responding)
      -2        = timeout while sending command
      -3        = this ID skipped by ICDBOOT

XDMA:   Performs DMA with retries. No retries are performed if the
        drive timed out. Retries are performed only when DMA returns
        a code 2, in which case XDMA does a REQUEST and tries DMA again.

    in:
            same as DMA

    return:
      0         = success
      -1        = timeout after sending command (drive is not responding)
      -2        = timeout while sending command
      -3        = this ID skipped by ICDBOOT
      127       = REQUEST fails (parity error?)
      1-126     = sense code from drive
      128-252   = more sense codes (not used by most SCSI devices)

SXDMA:  Performs XDMA but is used when a non-multiple of 16 bytes needs
        to be returned from the SCSI device. (For example doing a MODESENSE).
        The ST DMA chip has a bug where the FIFO will not flush its data
        when not full in receive mode. This function compensates for this by
        performing the command 4 times without resetting the DMA chip.
        USE ON READ COMMANDS ONLY.

    in:
            same as DMA

    return:
            same as XDMA

REQUEST: Performs a REQUEST SENSE on the SCSI device.

    in:
      id        = SCSI ID of device
      lun       = LUN of device

    return:
      0         = success
      -1        = timeout after sending command (drive is not responding)
      -2        = timeout while sending command
      -3        = this ID skipped by ICDBOOT
      127       = REQUEST fails (parity error?)
      1-126     = sense code from drive
      128-252   = more sense codes (not used by most SCSI devices)

}

Function DMA    (id:integer;var com_frame,data_frame: barray;
          flags,blocks: integer; timeout: long_integer): Integer; External;

Function XDMA   (id:integer;var com_frame,data_frame: barray;
          flags,blocks: integer; timeout: long_integer): Integer; External;
 
Function SXDMA  (id:integer;var com_frame,data_frame: barray;
          flags,blocks: integer; timeout: long_integer): Integer; External;

Function REQUEST (id,lun:integer): Integer; External;




{===========================================================================
                Some examples of DMA type calls
                -------------------------------
}


{examples of ACSI format commands}
Function Read_Sector (var buffer: barray; secno: Long_integer;
                           blocks: integer): integer;
begin
  Command.Data [1] := $08;
  Command.Data [2] := (secno & $FF0000) DIV $10000 + $20*LUN;
  Command.Data [3] := (secno & $00FF00) DIV $100;
  Command.Data [4] := secno & $0000FF;
  Command.Data [5] := blocks;
  Command.Data [6] := 0;
  Read_sector := XDMA (ID,command.buf,buffer,Read_Dir,blocks,30*one_second);
end;



Function Write_Sector (var buffer: barray; secno : Long_integer;
                            blocks: integer): integer;
begin
  Command.Data [1] := $0A;
  Command.Data [2] := (secno & $FF0000) DIV $10000 + $20*LUN;
  Command.Data [3] := (secno & $00FF00) DIV $100;
  Command.Data [4] := secno & $0000FF;
  Command.Data [5] := blocks;
  Command.Data [6] := 0;
  Write_sector := XDMA (ID,command.buf,buffer,Write_Dir,blocks,30*one_second);
end;


{ example of a FULL SCSI command}
Function Read_Capacity: Long_Integer;
var
  i: integer;

begin
  Command.Data [1] := 10;               { 10 bytes in command frame     }
  Command.Data [2] := $25;              { read capacity command         }
  Command.Data [3] := $20*LUN;
  for i:=4 to 11 do
    Command.Data [i] := 0;

  if SXDMA(ID,command.buf,SecBuf.buf,Read_Dir+fullscsi,1,3*one_second)=0 then
     Read_Capacity := SecBuf.longs[0]
  else
     Read_Capacity := 0;
end;