program scanscsi(input,output);

{*** Copyright Notice: This source Code belongs to the book
     "The SCSI Bus and IDE Interface" from Addison-Wesley.

     It may be used, ported and modified for non-commercial
     purposes when this copyright notice is included.
     Authorisation from the publisher is necessary for
     commercial purposes.
}

uses CRT, DOS;

const
  PNAM         : string='SCSI Scanner V1.0 rev 003e 18.7.94 (fs)';

{ASPI Specific Constants}

  ASPI_SRB_LENGHT  = $7F;

  SRB_COMMAND_CODE = $00;
  SRB_STATUS       = $01;
  SRB_TARGET_ID    = $08;
  SRB_LUN          = $09;
  SRB_DATA_LENGTH  = $0A;
  SRB_BUFFER_OFS   = $0F;
  SRB_BUFFER_SEG   = $11;
  SRB_SCSI_LEN     = $17;
  SRB_HA_STATUS    = $18;
  SRB_TARGET_STATUS= $19;
  SRB_SCSI_CMD     = $40;

  SRB_X_SCSICMD    = $02;

{SCSI Specific Constants}

SCSI_CMD_LENGTH        = 11;

{Program specific constants}

DATA_LENGTH        = $FF;

{Messages}

  ASPI_CONNECTED            ='ASPI loaded';
  ASPI_OPEN_ERROR           ='Error opening ASPI';

type

{Generic Types}
   MemAdress = record
                  Offset:  integer;
                  Segment: integer;
                 end;

{ASPI-Types}

   SRBsize= 0..ASPI_SRB_LENGHT;
   SRBarray = array[SRBsize] of byte;

{SCSI-Types}

   SCSICmdSize = 0..SCSI_CMD_LENGTH;
   SCSICmd = record
               Command: array[SCSICmdSize] of byte;
               Status: byte;
               ID: byte;
               LUN: byte;
               Len: byte;
               TimeOut: integer;
             end;


   BufferLength = 0..DATA_LENGTH;
   DataBufferType = array[BufferLength] of byte;

var
  CommandBuffer: SCSICmd;
  DataBuffer   : DataBufferType;
  ID,LUN       : byte;

  AspiEntryPoint: MemAdress;
  SRB: SRBarray;
  SCSIConnected: string;

{**** Low Level Functions}

function FileOpen(FileName:string):integer;

const DOS_OPEN_FILE = $3D;

var register: registers;

begin
  FileName:=FileName+chr(0);
  with register
  do
    begin
      ax := DOS_OPEN_FILE shl 8;
      bx:=0;
      cx:=0;
      ds := seg(FileName);
      dx := ofs(FileName)+1; { because Pascal strings
                               carry their length in byte 0 }
    end;
    MSDOS(register);
    if (register.flags and FCarry) > 0
    then FileOpen:=-1
    else FileOpen:=register.ax;
end;

function FileClose(FileHandle:integer):integer;

const DOS_CLOSE_FILE = $3E;

var register: registers;

begin
  with register
  do
    begin
      ax := DOS_CLOSE_FILE shl 8;
      bx:=FileHandle;
    end;
    MSDOS(register);
    if (register.flags and FCarry) = 0
    then FileClose:=0
    else FileClose:=register.ax;
end;

{**** Miscellanous Generic Functions}

{**** SCSI generic functions}

function SCSICmdLen(Opcode: byte):byte;
begin
 SCSICmdLen:=0;
 if Opcode and $E0 = $00 then SCSICmdLen:=6;
 if Opcode and $E0 = $20 then SCSICmdLen:=10;
 if Opcode and $E0 = $40 then SCSICmdLen:=10;
 if Opcode and $E0 = $A0 then SCSICmdLen:=12;
end;

{**** ASPI-specific functions}

procedure GetASPIEntry(FileHandle:integer; var AspiEntry:MemAdress);

const  ASPI_ENTRY_LENGTH = 4;
       DOS_IOCTL_READ    = $4402;

var register: registers;

begin
  with register
  do
    begin
      ax := DOS_IOCTL_READ;
      bx:=FileHandle;
      cx:=ASPI_ENTRY_LENGTH;
      ds := seg(AspiEntry);
      dx := ofs(AspiEntry);
    end;
    MSDOS(register);
end;

procedure SCSI2SRB(var SRB: SRBarray; Command: SCSICmd; var DataBuffer: DataBufferType);

var k:integer;

begin
  for k:=0 to High(SRB) do SRB[k]:=0;
  SRB[SRB_COMMAND_CODE]:=SRB_X_SCSICMD;

  with Command do
    begin
      SRB[SRB_TARGET_ID]:=ID;
      SRB[SRB_LUN]:=LUN;
      SRB[SRB_SCSI_LEN]:=SCSICmdLen(Command[1]);
      for k:=0 to SRB[SRB_SCSI_LEN]-1 do SRB[SRB_SCSI_CMD+k]:=Command[k];
    end;

  SRB[SRB_DATA_LENGTH]:=lo(DATA_LENGTH);
  SRB[SRB_DATA_LENGTH+1]:=hi(DATA_LENGTH);
  SRB[SRB_BUFFER_SEG]:=lo(seg(DataBuffer));
  SRB[SRB_BUFFER_SEG+1]:=hi(seg(DataBuffer));
  SRB[SRB_BUFFER_OFS]:=lo(ofs(DataBuffer));
  SRB[SRB_BUFFER_OFS+1]:=hi(ofs(DataBuffer));

end;

procedure SRBexecute(var SRB: SRBarray);
var SRBsegment, SRBoffset: integer;

begin

  SRBsegment:=seg(SRB);
  SRBoffset:=ofs(SRB);

  asm
  mov ax, SRBsegment
  push ax
  mov ax, SRBoffset
  push ax
  LEA BX, AspiEntryPoint
  call DWORD PTR [bx]
  add sp,4
  end;
end;

function InitializeASPI(var AspiEntrypoint:MemAdress): boolean;

const  ASPI_NAME = 'SCSIMGR$';

var result: integer;
    AspiFileHandle: integer;
begin
  AspiFileHandle:=FileOpen(ASPI_NAME);
  if AspiFileHandle>-1
  then
    begin
      GetASPIEntry(AspiFileHandle,AspiEntryPoint);
      FileClose(AspiFileHandle);
      InitializeASPI:=true;
    end
  else   InitializeASPI:=false;
end;

procedure initialize;

var ByteNbr : integer;

begin
    with CommandBuffer do
    begin
      for ByteNbr:=0 to SCSI_CMD_LENGTH do
        Command[ByteNbr]:=0;
      ID:=0;
      LUN:=0;
      Status:=$FF;
    end;
    for ByteNbr:=0 to DATA_LENGTH do DataBuffer[ByteNbr]:=0;
end;

Procedure Inquire(ID,LUN:byte);
const INQUIRY : array [SCSICmdSize] of byte = ($12,$0,$0,$0,$ff,$0,$0,$0,$0,$0,$0,$0);

var k: integer;
    Status: byte;
begin
  for k:=0 to SCSI_CMD_LENGTH do CommandBuffer.command[k]:=INQUIRY[k];
  CommandBuffer.ID:=ID;
  CommandBuffer.LUN:=LUN;
  If LUN=0 then writeln('SCSI ID ',ID,': ');
  SCSI2SRB(SRB,CommandBuffer,DataBuffer);
  SRBexecute(SRB);
  repeat until SRB[SRB_STATUS]<>0;
  if SRB[SRB_STATUS] = 1 then
    if SRB[SRB_HA_STATUS]= 0 then
      begin
        Status:=DataBuffer[0] and $E0;
        if Status=0 then
          begin
            write('   LUN ',LUN,': ');
            for k:=8 to 35 do write(chr(DataBuffer[k]));
            writeln;
          end;
      end
     else if LUN=0 then writeln;
end;


begin
  writeln(PNAM);
  initialize;
  if InitializeASPI(AspiEntryPoint)
  then
    begin
      writeln(ASPI_CONNECTED);
      for ID:=0 to 7 do
      for LUN:=0 to 7 do Inquire(ID,LUN);
    end
  else writeln(ASPI_OPEN_ERROR);


end.