XTCFMODE Source

From Lo-tech Wiki
Jump to navigation Jump to search

Source code for the XTCFMODE utility, which can be compiled using Turbo Pascal 6. Compiled code can be downloaded here.

Code

Program XTCFMode;

{For XT-CF family of adapters, lists current mode and changes the mode.
 Dependent on XTIDE Universal BIOS, version 2 or newer.}

uses dos, crt;

type
  tSectorBuffer = Array[0..511] of BYTE;
  pSectorBuffer = ^tSectorBuffer;

const 
  Version                              = '0.32';
  Author                               = 'James Pearce';
  BuildDate                            = '05-May-13';
  HelpNameOnly                         = 1;
  HelpQuick                            = 2;
  HelpFull                             = 4;
  XTCF_8BIT_PIO_MODE                   = $00;
  XTCF_8BIT_PIO_MODE_WITH_BIU_OFFLOAD  = $01;
  XTCF_DMA_MODE                        = $02;
  INVALID_MODE                         = $04;
  DEVICE_ERROR                         = $08;

var
  VERBOSE : BOOLEAN;


Procedure PrintHelp(mode : byte);
begin
  WriteLn('XTCFMODE - transfer mode utility for XT-CF family of adapters.');
  WriteLn('See http://www.lo-tech.co.uk/XT-CF');
  WriteLn;
  If mode = HelpFull then
  begin
    WriteLn;
    WriteLn('To display current mode:   xtcfmode [BIOS_drive_number]');
    WriteLn('To change mode:            xtcfmode [BIOS_drive_number] set [mode]');
    WriteLn('   where [mode] is one of:');
    Write('     ',XTCF_8BIT_PIO_MODE);
    WriteLn(' - for PIO 8-bit (slowest & safest)');
    Write('     ',XTCF_8BIT_PIO_MODE_WITH_BIU_OFFLOAD);
    WriteLn(' - for PIO 16-bit (faster on most systems, but depends on proper BIU');
    WriteLn('         implementation on system board - won''t work with AT&T PC6300)');
    Write('     ',XTCF_DMA_MODE);
    WriteLn(' - for DMA mode (via channel 3, supported by XT-CFv3 adapter only);');
    WriteLn('         fastest on 8088 hardware');
    WriteLn;
    WriteLn('Specify [BIOS_drive_number] in hex, i.e. 80h, 81h etc');
  end;{if mode =}
end;{procedure PrintHelp}


Function GetMode( Device : Byte ) : byte;
var
  Regs : Registers;
begin
  with regs do begin
    ah := $1E; {XT-CF functions}
    al := 2;   {get XT-CF transfer mode}
    dl := Device;
  end;
  Intr($13,regs);
  if regs.flags and FCarry = FCarry then GetMode := DEVICE_ERROR
  else GetMode := regs.DH; {return mode}
  WriteLn('Read mode: ', regs.DH);
  WriteLn('Block mode sectors: ', regs.DL);
end;{function GetMode}


Function SetMode( Device, Mode : Byte ) : Byte;
var
  regs : registers;
begin
  with regs do begin
    ah := $1E;
    al := 1; {set mode}
    dl := Device;
    dh := Mode;
  end;{with}
  Intr($13,regs);
  if regs.flags and FCarry = FCarry then SetMode := DEVICE_ERROR
  else SetMode := Mode; {return mode set}
end;{function}



FUNCTION ReadSectors(drive, head, track, sector, numtoread : Byte; buff: Pointer): Byte; assembler;
{function courtesy of Trixter - http://trixter.oldskool.org/}
{ drive = 0 for drive A:, 1 = B:,   }
{ 80h = first hard drive.           }
ASM
  mov  ah,02h
  mov  al,numtoread
  les  bx,buff     { es:bx -> buffer }
  mov  ch,track
  mov  cl,sector
  mov  dh,head
  mov  dl,drive
  int  13h
  {numsectors read in al, status in ah, cf=0 if successful}
END;


function BuffersMatch(Buff1, Buff2 : pSectorBuffer) : Boolean;
var
  i,j,a,b,k   : word;
  Error : Boolean;
begin
  If VERBOSE then WriteLn('Comparing buffers...');
  Error := false;
  for i := 0 to Pred(SizeOf(tSectorBuffer)) do
    if Buff1^[i] <> Buff2^[i] then
    begin
      Error := true;
      k := i;
      i := Pred(SizeOf(tSectorBuffer));
    end;{if}
  BuffersMatch := Not Error;
  If VERBOSE then begin
    Write('Buffers ');
    if Error then begin
      WriteLn('did not match.  Trace:');
      WriteLn('Ofs     Buff1     Buff2');
      if k > 10 then a := k - 10 else a := 0;
      if k < 500 then b := k + 10 else b := 511;
      for j := a to b do
        WriteLn(j,'    ',Buff1^[j],'     ',Buff2^[j]);
    end else Write('matched.');
  end;
end;{function}


Function ParamSpecified(s : string) : boolean;
{returns true is s was specified in a command line parameter,
 either directly or via - or /, e.g. these are equivalent: h, -h, /h}
var
  i,x   : byte;
  found : boolean;
  comp  : string;
begin
  found := false;
  for x := 1 to length(s) do s[x] := UpCase(s[x]);
  for i := 1 to ParamCount do
  begin
    comp := ParamStr(i);
    for x := 1 to length(s) do comp[x] := UpCase(comp[x]);
    if (comp[1] = '/') or (comp[1] = '-') then
      if length(comp) > 1 then comp := copy( comp,2,pred(length(comp)) )
      else comp := '';
    if comp = s then found := true;
    if found then i := ParamCount;
  end;{for}
  ParamSpecified := found;
end;{function ParamSpecified}


var
  res, Device, mode : byte;
  valres            : integer;
  error             : Boolean;
  Buffer1, Buffer2  : pSectorBuffer;
  Ch                : Char;


begin
  If (ParamCount = 0) or (ParamSpecified('?')) or (ParamSpecified('h')) then
    PrintHelp(HelpFull)
  else begin
    Device := 0;
    {first check for verbose mode}
    if ParamSpecified('vv') then VERBOSE := TRUE else VERBOSE := FALSE;

    {user gave some parameters, so see what we're doing}
    if VERBOSE then WriteLn('Checking parameters...');
    if (ParamStr(1) = '80') or (ParamStr(1) = '80h') then Device := $80;
    if (ParamStr(1) = '81') or (ParamStr(1) = '81h') then Device := $81;
    if (ParamStr(1) = '82') or (ParamStr(1) = '82h') then Device := $82;
    if (ParamStr(1) = '83') or (ParamStr(1) = '83h') then Device := $83;
    
    if Device = 0 then
    begin
      if VERBOSE then WriteLn('Device still 0 - exiting');
      PrintHelp(HelpFull)
    end else begin
      if (ParamSpecified('set')) and (ParamCount >= 3) then
      begin
        Val(ParamStr(3),mode,valres);
        if valres <> 0 then PrintHelp(HelpFull)
        else begin
          {all looks OK}
          {if BIU_OFFLOAD was specified, also perform a disk read test to confirm if it's supported}
          If VERBOSE then WriteLn('Allocating buffers');
          New(Buffer1); New(Buffer2);

          error := false;
          if Mode = XTCF_8BIT_PIO_MODE_WITH_BIU_OFFLOAD then
          begin
            {first perform a read against sector 1, which hopefully should always have something on it}
            If VERBOSE then WriteLn('Attempting to read 1st sector of selected device');
            if ReadSectors(Device,0,0,1,1,Buffer1) AND $00FF <> 1 then Error := true;
          end;
          if not error then
          begin
            case SetMode(Device,Mode) of
              XTCF_8BIT_PIO_MODE                   :  WriteLn('8-bit PIO mode selected.');
              XTCF_8BIT_PIO_MODE_WITH_BIU_OFFLOAD  :  WriteLn('8-bit PIO with BIU offload mode selected.');
              XTCF_DMA_MODE                        :  WriteLn('DMA mode selected.');
              INVALID_MODE                         :  begin 
                                                        WriteLn('Invalid mode specified.');
                                                        Error := true;
                                                      end;
              DEVICE_ERROR                         :  begin 
                                                        WriteLn('Selected device not XT-CF.');
                                                        Error := true;
                                                      end;
            end;{case}
            If VERBOSE then
            begin
              if error then WriteLn('Error flag set - SetMode() failed.  Continuing anyhow.')
              else WriteLn('SetMode() succeeded.');
            end;{if VERBOSE}
            if (Mode = XTCF_8BIT_PIO_MODE_WITH_BIU_OFFLOAD) and (not error) then
            begin
              {read sector 1 again, and check it matched the first read}
              If VERBOSE then WriteLn('Attempting to read 1st sector of selected device again');
              if ReadSectors(Device,0,0,1,1,Buffer2) AND $00FF <> 1 then Error := true;
              if not error then
              begin
                If VERBOSE then WriteLn('Read second buffer OK');
                if not BuffersMatch(Buffer1,Buffer2) then
                begin
                  WriteLn('It appears that BIU offload mode is not supported on this machine.');
                  Write('Reset to 8-bit PIO <Y/N>? ');
                  Repeat Ch := UpCase(ReadKey) until Ch in ['Y','N'];
                  WriteLn(Ch);
                  if Ch = 'Y' then
                  begin
                    if SetMode(Device,XTCF_8BIT_PIO_MODE) <> XTCF_8BIT_PIO_MODE then
                    begin
                      WriteLn('WARNING: Unable to return device to 8-bit PIO mode.  Restart system to');
                      Write('         avoid system corruption.');
                      repeat Ch := ReadKey; until Ch = 'C'; {soft hang}
                    end else WriteLn('8-bit PIO mode selected.');
                  end;{if Ch='Y'}
                end;{if not BuffersMatch}
              end;{if not error}
            end;
          If VERBOSE then WriteLn('About to dispose of buffers');
          Dispose(Buffer1); Dispose(Buffer2);
          If VERBOSE then WriteLn('Buffer RAM freed');
          end {if not error}
          else WriteLn('Error encountered reading from specified device.');
        end;{if/else}
      end {if ParamSpecified('set')}
      else if ParamCount = 1 then
      begin
        PrintHelp(HelpNameOnly);
        Write('Device ',ParamStr(1),': ');
        case GetMode(Device) of
          XTCF_8BIT_PIO_MODE                   :  WriteLn('8-bit PIO');
          XTCF_8BIT_PIO_MODE_WITH_BIU_OFFLOAD  :  WriteLn('8-bit PIO with BIU offload');
          XTCF_DMA_MODE                        :  WriteLn('DMA');
          DEVICE_ERROR                         :  WriteLn('Device is not XT-CF.');
        end;{case}
      end {if ParamCount}
      else PrintHelp(HelpFull);
    end;{if Device = 0/Else}
  end;{if ParamCount = 0}
  If VERBOSE then WriteLn('Program terminating - returning control to OS.');
END.{Program}

See Also