Contributor: ROLAND SKINNER           

unit RJScan;

{******************************}
{                              }
{            RJScan            }
{                              }
{             v1.1             }
{                              }
{                              }
{              by              }
{                              }
{        Roland Skinner        }
{                              }
{      Copyright (c) 1992      }
{                              }
{         RJS Software         }
{                              }
{    Released to the public    }
{         domain 1994.         }
{                              }
{******************************}


{ Implements scanning ability for the DFI HS-3000 PLUS HANDY SCANNER or }
{ other 100% compatible hand-scanners (including certain GeniScans).    }

{ NOTE - This unit may be overlayed.                                    }
{      - This unit requires Turbo Pascal 6 (or above).                  }

{$B-,D-,F+,G-,I-,L-,O+,R-,S-,V-,X-}

{=============================================================================}

interface

{-----------------------------------------------------------------------------}

  const
    AnyResolution = 0;

{-----------------------------------------------------------------------------}

  type
    ScanError = (scOK,scNoScanner,scInvalidResolution,scIncorrectResolution,
                 scInvalidImageWidth);

{-----------------------------------------------------------------------------}

  type
    ScanLineBufferProc = function(LineNumber : Integer) : Pointer;
                         { NOTE - This function should return  the  address }
                         {        of the scan-buffer for the "LineNumber"th }
                         {        line. First line is number 0.             }
    DisplayScannedLineProc = procedure(LineNumber : Integer);
                             { NOTE - This  procedure  should  display  (if }
                             {        necessary)  the  "LineNumber"th  line }
                             {        that was scanned in.  First  line  is }
                             {        number 0.                             }
    StopScanningProc = function : Boolean;
                       { NOTE - This function should return "False", unless }
                       {        some  event  has  occurred  which  requires }
                       {        scanning to stop.                           }

{-----------------------------------------------------------------------------}

  function  ScanImage(DesiredResolution,MaxLinesToScan,BytesPerLine : Integer;
                      ScanLineBuffer : ScanLineBufferProc;
                      DisplayScannedLine : DisplayScannedLineProc;
                      StopScanning : StopScanningProc) : ScanError;
    {- This function will scan an image  with  width  8*"BytesPerLine"  and }
    {  height "MaxLinesToScan". It is possible to specify the resolution at }
    {  which to scan the image  in  "DesiredResolution"  (100,200,300,400). }
    {  If the resolution set on the scanner is different to that specified, }
    {  then   the   "scIncorrectResolution"   error   will   be   returned. }
    {  If "DesiredResolution" is "AnyResolution", then any resolution  will }
    {  be allowed. "scInvalidResolution" will be returned if  a  resolution }
    {  other than 100,200,300,400 or "AnyResolution" is specified.          }
    {  "ScanLineBuffer",  "DisplayScannedLine"   and   "StopScanning"   are }
    {  procedures/functions whose functions are discussed above. These must }
    {  be FAR procedures/functions.                                         }
    {  If "BytesPerLine" is too  large  for  the  scanner-resolution,  then }
    {  "scInvalidImageWidth" will be returned.                              }
    {  If scanner is not installed then "scNoScanner" is returned.          }
    {  If successful, then "scOK" will be returned.                         }
    {  This function may not work with  certain hand-scanners (if  so,  use }
    {  "GenericScanImage").                                                 }
  function  GenericScanImage(MaxLinesToScan,BytesPerLine : Integer;
                             ScanLineBuffer : ScanLineBufferProc;
                             DisplayScannedLine : DisplayScannedLineProc;
                             StopScanning : StopScanningProc) : ScanError;
    {- This  function  will  scan  an  image  in  an  analogous  manner  as }
    {  "ScanImage". However, it does not do any checks for valid resolution }
    {  or image-width. This is to allow compatibility for scanners which do }
    {  not allow for scan-resolution selection.                             }
    {  "scOK", "scNoScanner" and "scInvalidImageWidth" may be  returned  by }
    {  this function. Refer to "ScanImage" for a discussion about these.    }
  function  ScannerIsInstalled : Boolean;
    {- Returns installed-status of scanner.                                 }
  function  ResolutionOfScanner : Integer;
    {- Returns the resolution  set  on  the  scanner.  If  scanner  is  not }
    {  installed, then -1 will be returned.                                 }
    {  This function may not work with certain hand-scanners.               }

{=============================================================================}

implementation

{-----------------------------------------------------------------------------}

  const
    MaxBytesPerLine : Array[1..4] of Byte = (50,102,154,205);

{-----------------------------------------------------------------------------}

  var
    ScannerInstalled        : Boolean;
    ScannerResolution       : Word;
    ScannerResolution100    : Byte;
    DMAChannel              : Byte;
    DMAPageRegister         : Word;
    DMACurAddrRegister      : Word;
    DMACurWordCountRegister : Word;
    DMAClearSingleMaskBit   : Byte;
    DMASetSingleMaskBit     : Byte;
    DMAModeRegisterSetting  : Byte;
    DMAWriteRequest         : Byte;
    DMATerminalCountReached : Byte;

{-----------------------------------------------------------------------------}

  procedure DetermineScannerResolution; assembler;
  var
    Data : Byte;
  asm
    xor     ax,ax
    jmp     @Start
  @ResSettings:
    db      21h,41h,51h,71h
  @Start:
    mov     dx,27Bh
    mov     cx,300
  @1:
    in      al,dx
    and     al,10000000b
    jnz     @1
  @2:
    in      al,dx
    and     al,10000000b
    jz      @2
    loop    @1
  @3:
    in      al,dx
    and     al,10000000b
    jnz     @3
  @4:
    in      al,dx
    and     al,00100100b
    shr     al,1
    shr     al,1
    or      ah,al
    shr     al,1
    shr     al,1
    or      ah,al
    and     ah,00000011b
    xor     al,al
    xchg    al,ah
    mov     bl,4
    sub     bl,al
    mov     al,bl
    push    ax
    mov     bx,OFFSET (@ResSettings-1)
    add     bx,ax
    mov     al,[cs:bx]
    mov     dx,27Ah
    out     dx,al
    mov     Data,al
    pop     ax
    mov     ScannerResolution100,al
    mov     cx,100
    mul     cx
    mov     ScannerResolution,ax
  end;

{-----------------------------------------------------------------------------}

  procedure DetermineScannerDMA; assembler;
  asm
    mov     dx,27Bh
    in      al,dx
    and     al,00001010b
    cmp     al,00001000b
    je      @UseDMA1
    cmp     al,00000010b
    je      @UseDMA3
    jmp     @NoDMA
  @UseDMA1:
    mov     DMAChannel,1
    mov     DMAPageRegister,        83h
    mov     DMACurAddrRegister,     02h
    mov     DMACurWordCountRegister,03h
    mov     DMAClearSingleMaskBit,  00000001b
    mov     DMASetSingleMaskBit,    00000101b
    mov     DMAModeRegisterSetting, 01000101b
    mov     DMAWriteRequest,        00000001b
    mov     DMATerminalCountReached,00000010b
    jmp     @Exit
  @UseDMA3:
    mov     DMAChannel,3
    mov     DMAPageRegister,        82h
    mov     DMACurAddrRegister,     06h
    mov     DMACurWordCountRegister,07h
    mov     DMAClearSingleMaskBit,  00000011b
    mov     DMASetSingleMaskBit,    00000111b
    mov     DMAModeRegisterSetting, 01000111b
    mov     DMAWriteRequest,        00000011b
    mov     DMATerminalCountReached,00001000b
    jmp     @Exit
  @NoDMA:
    mov     DMAChannel,0
  @Exit:
  end;

{-----------------------------------------------------------------------------}

  procedure TurnScannerOn; assembler;
  asm
    mov     dx,27Ah
    mov     al,01h
    out     dx,al
  end;

{-----------------------------------------------------------------------------}

  procedure TurnScannerOff; assembler;
  asm
    mov     dx,27Ah
    mov     al,00h
    out     dx,al
  end;

{-----------------------------------------------------------------------------}

  procedure DMADelay; assembler;
  asm
    nop
    nop
    nop
  end;

{-----------------------------------------------------------------------------}

  function  DoScan(MaxLinesToScan,BytesPerLine : Integer;
                   ScanLineBuffer : ScanLineBufferProc;
                   DisplayScannedLine : DisplayScannedLineProc;
                   StopScanning : StopScanningProc) : ScanError;
  var
    LinesScanned : Integer;
    ScanBuffer   : Pointer;
    WidthToScan  : Word absolute BytesPerLine;
    QuitScanning : Boolean;
  begin
    if (BytesPerLine>0) and (BytesPerLine<=MaxBytesPerLine[ScannerResolution100]) then
    begin
      LinesScanned := 0;
      QuitScanning := False;
      repeat
        ScanBuffer := ScanLineBuffer(LinesScanned);
        asm
        {-Disable DMA transfer }
          mov     al,DMASetSingleMaskBit
          out     0Ah,al
          call    DMADelay;
          mov     al,DMAModeRegisterSetting
          out     0Bh,al
          call    DMADelay
        {-Setup Buffer address }
          les     di,ScanBuffer
          mov     dx,es
          mov     al,dh
          mov     cl,4
          shl     dx,cl
          shr     al,cl
          add     dx,di
          adc     al,0
          mov     cx,dx
          mov     dx,DMAPageRegister
          out     dx,al
          call    DMADelay
          out     0Ch,al
          call    DMADelay
          mov     dx,DMACurAddrRegister
          mov     al,cl
          out     dx,al
          call    DMADelay
          mov     al,ch
          out     dx,al
          call    DMADelay
        {-Setup bytes to transfer }
          out     0Ch,al
          call    DMADelay
          mov     ax,WidthToScan
          dec     ax
          mov     dx,DMACurWordCountRegister
          out     dx,al
          call    DMADelay
          mov     al,ah
          out     dx,al
        {-Start DMA transfer }
          mov     dx,27Bh
          out     dx,al
          dec     dx
          in      al,dx                 { DX = 027Ah }
          mov     al,DMAWriteRequest
          out     09h,al
          call    DMADelay
          mov     al,DMAClearSingleMaskBit
          out     0Ah,al
        end;
      {-Scan line }
        asm
          mov     bl,DMATerminalCountReached
        @1:
          in      al,08h
          and     al,bl
          cmp     al,bl
          je      @2
          push    bx
          call    StopScanning
          pop     bx
          or      al,al
          jz      @1
          mov     QuitScanning,True
        @2:
        end;
        DisplayScannedLine(LinesScanned);
        Inc(LinesScanned);
      until (LinesScanned=MaxLinesToScan) or QuitScanning;
      DoScan := scOK;
    end
    else
      DoScan := scInvalidImageWidth;
  end;

{-----------------------------------------------------------------------------}

  function  ScanImage(DesiredResolution,MaxLinesToScan,BytesPerLine : Integer;
                      ScanLineBuffer : ScanLineBufferProc;
                      DisplayScannedLine : DisplayScannedLineProc;
                      StopScanning : StopScanningProc) : ScanError;
  begin
    if ScannerInstalled then
    begin
      if (DesiredResolution=AnyResolution) or ((DesiredResolution div 100) in [1..4]) then
      begin
        TurnScannerOn;
        DetermineScannerResolution;
        if (DesiredResolution=AnyResolution) or (DesiredResolution=ScannerResolution) then
          ScanImage := DoScan(MaxLinesToScan,BytesPerLine,
                              ScanLineBuffer,DisplayScannedLine,StopScanning)
        else
          ScanImage := scIncorrectResolution;
        TurnScannerOff;
      end
      else
        ScanImage := scInvalidResolution;
    end
    else
      ScanImage := scNoScanner;
  end;

{-----------------------------------------------------------------------------}

  function  GenericScanImage(MaxLinesToScan,BytesPerLine : Integer;
                             ScanLineBuffer : ScanLineBufferProc;
                             DisplayScannedLine : DisplayScannedLineProc;
                             StopScanning : StopScanningProc) : ScanError;
  begin
    if ScannerInstalled then
    begin
      TurnScannerOn;
      ScannerResolution100 := 4;
      GenericScanImage := DoScan(MaxLinesToScan,BytesPerLine,
                          ScanLineBuffer,DisplayScannedLine,StopScanning);
      TurnScannerOff;
    end
    else
      GenericScanImage := scNoScanner;
  end;

{-----------------------------------------------------------------------------}

  procedure DetermineScannerPresence;
  begin
    TurnScannerOn;
    DetermineScannerDMA;
    TurnScannerOff;
    ScannerInstalled := (DMAChannel<>0);
  end;

{-----------------------------------------------------------------------------}

  function  ScannerIsInstalled : Boolean;
  begin
    ScannerIsInstalled := ScannerInstalled;
  end;

{-----------------------------------------------------------------------------}

  function  ResolutionOfScanner : Integer;
  begin
    if ScannerInstalled then
    begin
      TurnScannerOn;
      DetermineScannerResolution;
      TurnScannerOff;
      ResolutionOfScanner := ScannerResolution;
    end
    else
      ResolutionOfScanner := -1;
  end;

{-----------------------------------------------------------------------------}

begin
  DetermineScannerPresence;
end.

{=============================================================================}