*/
Want to see what people are talking about? See the latest forum posts.
*/

View \ACCESSPC.PAS

ACCESS.bus Controller (Pascal)

Submitted By: WEBMASTER
Rating: starstarstar (Rate It)


(****************** ACCESS.bus Controller (PC) ******************)
(* This listing is designed to to be compiled under             *)
(* Turbo Pascal version 6.0 or above                            *)
(* Author      :  Robert Clemens                                *)
(* Last update : 1-23-93                                        *)
(****************************************************************)

{$L-}            (* Link to disk    off  *)
{$B-}                          (* Boolean complete eval off   *)
{$I+}            (* I/O checking on  *)
{$N-}            (* No numeric coprocessor  *)
{$M 4096, 0, 4096}      (* Stack and heap size                *)
{$V-}            (* String checking Off          *)
{$S-}            (* Stack Checking Off            *)
{$R-}            (* Range checking off            *)

program AbCon;

Uses
  Crt,
  Dos;

const
  ECHOOFF       = 0;
  ECHOTX        = 1;
  ECHORX        = 2;
  ECHOFULL      = 3;
  EchoStr       : array[0..3] of string[9] =
                ('OFF      ',
                   'Txed     ',
                   'Rxed     ',
                   'Rxed+Txed');
  EchoMode      : byte = ECHOFULL;
  HostAvailible : boolean = false;
  CARDSPEED     : byte       = $18;  (* 8mhz card speed       *)
  TimerCount    : integer = 0;
  Leave  : boolean = false;


(*********************** Essential Basics ***********************)
type
  str2    = string[2];

procedure Beep;    (* Sounds terminal bell  *)
var tms : byte;
 begin
 for tms := 1 to 3 do
   begin
   sound(1200);
   delay(20);
   sound(900);
   delay(20);
   end;
 NoSound;
 end;

function hex (b : byte) : str2;  (* Convert byte to hex   *)
  const
    h : array [0..15] of char = '0123456789ABCDEF';
  begin
    Hex := h [ b shr 4 ] + h [ b and 15 ];
  end;

function ASCIIToHex(ch:char) : byte;
  begin
   case ch of
'a'..'f' : ASCIIToHex := ord(ch) - 87;
'A'..'F' : ASCIIToHex := ord(ch) - 55;
'0'..'9' : ASCIIToHex := ord(ch) - 48;
   end;
  end;

(***************** PCD 8584 Chip constants **********************)
const
  RW_ADDRESS    = $00;            (* Select Own Address Register        *)
  RW_DATA       = $40;  (* Select Data Register    *)
  RW_CLOCK      = $20;                (* Select I2C Speed Register    *)

  ACK      = $00;          (* Write ACK Bit          *)
  NOTACK        = $01;    (* Write NoACK Bit                *)
  STOP    = $02;        (* Write Stop Condition          *)
  START  = $04;      (* Write Start Condition   *)
  ENI      = $08;          (* Write Enable Chip Interrupt       *)

  PIN      = $80;          (* Read/Write PIN Bit                *)

  BB        = $01;            (* Read BUS BUSY Condition    *)
  ARL      = $02;          (* Read Lost Arbitration     *)
  AAS      = $04;          (* Read Slave Receiver Condition*)
  LRB      = $08;          (* Read Last Received Bit (ACK)      *)
  BER      = $10;          (* Read Bus ERROR Condition  *)
  STS      = $20;          (* Read STOP while Slave     *)

  DataPort      = $300;              (* These are port addresses for        *)
  StatPort      = $301;        (* the ISA card               *)
  ControlPort   = $302;

(******************* Ab Protocol command values *****************)
const
  AB_DEV_RESET  = $F0;
  AB_ID_REQUEST = $F1;
  AB_ASSIGN_ADD = $F2;
  AB_CAP_REQUEST  = $F3;
  AB_DEV_ATTEN  = $E0;
  AB_ID_REPORT  = $E1;
  AB_CAP_REPORT = $E3;
  AB_ERROR            = $E4;

  AB_APP_POLL   = $B0;
  AB_APP_TEST   = $B1;
  AB_HW_SIG     = $A0;
  AB_TEST_REPORT  = $A1;
  AB_APP_RESUME = $FC;
  AB_APP_HOLD   = $FD;
  AB_INP_ERR    = $02;
  AB_KEY_CLICK  = $01;

(*********************** Bus Conditions *************************)
const
  BUSCONFIGURED = $00;
  BUSASSIGNADD  = $04;
  BUSCONFIRM    = $08;
  BUSRESET            = $10;
  BUSERROR            = $20;
  BusStatus     : byte  = BUSCONFIGURED;

(*********************** I2C Conditions *************************)
const
  I2CIDLE       = 0;
  I2CRxING      = 1;
  I2CTxING      = 2;
  I2CSTOP       = 4;
  I2CERROR      = 5;
  I2CStatus     : byte       = I2CIDLE;

(********************** Device Conditions ***********************)
const
  DEVRESET      = $00;
  DEVWAIT       = $01;
  DEVCONFIRM    = $02;
  DEVGETCAP     = $04;
  DEVWAITCAP    = $08;
  DEVREADY      = $10;
  DEVERROR      = $20;

(***************** AB Data Constants and Types ******************)
const
  MAXABMSGLEN   = 127;
  MAXDEVICES    = 15;
  MAXCAPABILITIESLEN    = 127;
  BUSRESETDELAY = 5;

  HOST_ADDRESS  = $50;
  DEVICE_DEFAULT= $6E;
  PROTOCOL      = $80;

  MAXTXPAKS     = 16;
  MAXRXPAKS     = 32;

  ReportPtr     : byte = 0;
  InputPtr      : byte = 0;
  TxLoadPtr     : byte = 0;
  TxPtr  : byte = 0;

type
  AB_DEVICE = record
        Status  : integer;
        Address : integer;
        class   : integer;
        ID            : string[30];
        Capabilities    : string[MAXCAPABILITIESLEN];
        DisplayMe       : byte;
        CapOffset  : word;
        end;

  AB_DEVICES = record
        Device    : array[0..MAXDEVICES] of AB_Device;
        end;

  AbMessage          = array[0..MAXABMSGLEN] of byte;

var
  Devices              : array[0..MAXDEVICES] of AB_Device;
  AbMsg   : AbMessage;
  ABIntNum            : byte;
  IntEnableMask  : byte;
  OldIntMask        : byte;
  ReadData            : byte;
  WriteData          : byte;
  StatusData        : byte;
  OldABIntVector        : pointer;
  OldTimerIntVector     : pointer;
  RxMsgPaks          : array[0..MAXRXPAKS,0..130] of byte;
  TxMsgPaks          : array[0..MAXTXPAKS,0..130] of byte;
  RxMsgLen            : integer;
  TxCnt   : byte;
  RxCnt   : byte;
  RxChkSum            : byte;
  TxChkSum            : byte;
  chtr      : char;


(************* Build a pak and attempt to send it ***************)

procedure TxPak(dtn,src,lgth : byte; msg : AbMessage);
var c : byte;
  len : byte;

  begin     (* Assemble a message    *)
  len := lgth and $7F;      (* and calculate Check- *)
  TxChkSum := dtn;                  (* sum then put it into *)
  TxMsgPaks[TxLoadPtr,2] := dtn;        (* the transmit queue.  *)
  TxChkSum := TxChkSum xor src;
  TxMsgPaks[TxLoadPtr,3] := src;
  TxChkSum := TxChkSum xor lgth;
  TxMsgPaks[TxLoadPtr,4] := lgth;
  for c := 1 to len do
    begin
    TxChkSum := TxChkSum xor msg[c];
    TxMsgPaks[TxLoadPtr,c + 4] := msg[c];
    end;
  TxMsgPaks[TxLoadPtr,len + 5] := TxChkSum;
  TxMsgPaks[TxLoadPtr,1] := len + 5;
  TxMsgPaks[TxLoadPtr,0] := 2;

  if ((EchoMode and ECHOTX) = ECHOTX) then
    begin
    write('Tx.. ');
    for c := 0 to TxMsgPaks[TxLoadPtr,1] do
      write(hex(TxMsgPaks[TxLoadPtr,c]),' ');
    writeln;
    end;

  Inc(TxLoadPtr);
  if TxLoadPtr = MAXTXPAKS then TxLoadPtr := 0;

  if (port[StatPort] <> $81) then exit;
  port[DataPort] := TxMsgPaks[TxPtr,TxMsgPaks[TxPtr,0]];
  port[StatPort] := PIN + RW_DATA + ENI + START + NOTACK;
  I2CStatus := I2CTxING;

  end;


(********************** Timer Service ***************************)

(*$F+   *)
procedure Timer;
interrupt;
var
  ds            : byte;

  begin

        asm
        push    ss
        pushf         (* Setup for RETI instruction      *)
        call    OldTimerIntVector   (* Call old Timer ISR      *)
        pop     ss
        end;

  if TxPtr <> TxLoadPtr then
    begin
    if ((port[StatPort] and BB) = 0) then exit;
    port[DataPort] := TxMsgPaks[TxPtr,TxMsgPaks[TxPtr,0]];
    port[StatPort] := PIN + RW_DATA + ENI + START + NOTACK;
    I2CStatus := I2CTxING;
    exit;
    end;

  if BusStatus <> BUSCONFIGURED then
    begin
    if BusStatus = BUSCONFIRM then
      begin
      Dec(TimerCount);
      if TimerCount = 0 then
        begin
        for ds := 1 to MAXDEVICES do
          if (Devices[ds].Status = DEVCONFIRM) then
            Devices[ds].Status := DEVRESET;

        AbMsg[1] := AB_ID_REQUEST;
        TxPak(DEVICE_DEFAULT,
              HOST_ADDRESS,
              PROTOCOL + 1,
              AbMsg);
        BusStatus := BUSCONFIGURED;
        end
       else exit;
      end;
    if BusStatus = BUSRESET then
      begin
      Dec(TimerCount);
      if TimerCount = (BUSRESETDELAY - 3) then
        begin
        if not HOSTAVAILIBLE then
          begin
          AbMsg[1] := AB_DEV_RESET;
          for  ds := MAXDEVICES downto 1 do
            begin
            TxPak((ds * 2) + HOST_ADDRESS,
                HOST_ADDRESS,
                PROTOCOL + 1,
                AbMsg);
            end;
          end
         else
          begin
          AbMsg[1] := AB_ID_REQUEST;
          for  ds := MAXDEVICES downto 1 do
            begin
            TxPak((ds * 2) + HOST_ADDRESS,
                HOST_ADDRESS,
                PROTOCOL + 1,
                AbMsg);
            end;
          AbMsg[1] := AB_DEV_RESET;
          TxPak(DEVICE_DEFAULT,
                HOST_ADDRESS,
                PROTOCOL + 1,
                AbMsg);
          end;
        end;

      if TimerCount = 0 then
        begin
        AbMsg[1] := AB_ID_REQUEST;
        TxPak(DEVICE_DEFAULT,
              HOST_ADDRESS,
              PROTOCOL + 1,
              AbMsg);
        BusStatus := BUSASSIGNADD;
        TimerCount := 3;
        end
       else exit;
      end;
    if BusStatus = BUSASSIGNADD then
       begin
       Dec(TimerCount);

       for  ds := MAXDEVICES downto 1 do        (* Start Get Capability *)
         begin                                  (* phase for device     *)
         if Devices[ds].Status = DEVGETCAP then
           begin
           AbMsg[1] := AB_CAP_REQUEST;
           AbMsg[2] := 0;
           AbMsg[3] := 0;
           TxPak((ds * 2) + HOST_ADDRESS,
                HOST_ADDRESS,
                PROTOCOL + 3,
                AbMsg);
           Devices[ds].Status := DEVWAITCAP;
           end;
         end;

       if TimerCount = 0 then
         begin
         BusStatus := BUSCONFIGURED;
         end
        else exit;
      end;
    end;

  end;

(*$F-   *)

(*********************** AB Interupt handler ********************)

function DataReady:boolean;          (* Is data in input buf      *)
  begin
  DataReady := InputPtr <> ReportPtr;
  end;


(*$F+   *)
procedure DevInterrupt;  (* Main Interrupt Handler        *)
interrupt;
  begin

  if I2CStatus > I2CRxING then    (* Transmit Routine       *)
    begin
    Inc(TxMsgPaks[TxPtr,0]);
    if I2CStatus = I2CSTOP then
      begin
      port[StatPort] := PIN + RW_DATA + ENI + STOP + NOTACK;
      I2CStatus := I2CIDLE;
      Inc(TxPtr);
      if TxPtr = MAXTXPAKS then TxPtr := 0;
      end
     else
      begin
      port[dataport] := TxMsgPaks[TxPtr,TxMsgPaks[TxPtr,0]];
      if TxMsgPaks[TxPtr,0] = TxMsgPaks[TxPtr,1] then I2CStatus := I2CSTOP
       else I2CStatus := I2CTxING;
      end;

    end

 else

    begin                            (* Receive Routine     *)
    StatusData := port[StatPort];
    ReadData := port[DataPort];  (* Read DataPort *)

    if (StatusData and AAS) = AAS then
      begin
      RxMsgPaks[InputPtr,0] := 0;       (* Say Message OK      *)
      RxMsgPaks[InputPtr,1] := ReadData;(* Read Dest address    *)
      RxChkSum := ReadData;
      RxCnt := 2;
      I2CStatus := I2CRxing;
      end
     else
      if StatusData = $00 then    (* Read a good byte       *)
        begin
        RxMsgPaks[InputPtr,RxCnt] := ReadData;
        RxChkSum := RxChkSum xor ReadData;
        if RxCnt = 2 then RxMsglen := ReadData and $7F
         else Dec(RxMsgLen);        (* look for End of Msg      *)
        Inc(RxCnt);
        if RxMsgLen = -1 then         (* Do Checksum     *)
          if (RxChkSum xor ReadData) <> 0 then
            RxMsgPaks[InputPtr,0] := $FF;       (* Checksum bad        *)
        end
       else
        begin
        if (StatusData and STS) = STS then I2CStatus := I2CIDLE
         else
          begin
          I2CStatus := I2CERROR;
          RxMsgPaks[InputPtr,0] := StatusData;  (* Message bad    *)
          end;
        Inc(InputPtr);
        if InputPtr = MAXRXPAKS then InputPtr := 0;
        if InputPtr = ReportPtr then writeln('Rx Queue OVERRUN !!');
        end;

    end;

  port[$20] := $20;

  end;
(*$F-   *)


procedure InstallAbInterrupt;
  begin
  GetIntVec(AbIntNum,OldABIntVector);
  SetIntVec(AbIntNum,@DevInterrupt);
  port[$21] := port[$21] and IntEnableMask;
  end;


procedure UnInstallAbInterrupt;
  begin
  port[$21] := OldIntMask;
  SetIntVec(AbIntNum,OldABIntVector);
  end;


(*********************** Display Routines ***********************)

procedure DisplayMenu;
  begin
  writeln('READ "S"tatusPort,  "D"ataPort       "H"ex Value      "R"eset Devices');
  writeln('WRITE S"t"atusPort,  D"a"taPort      "E"cho Mode      "U"ser Input       "Q"uit');
  writeln('===============================================================================');
  window(1,4,79,Hi(WindMax));
  writeln('EchoMode = RXed+Txed');
  end;


procedure DisplayRxedMessage;
var  c : byte;
  begin
  if RxMsgPaks[ReportPtr,0] <> 0 then
    begin
    write('Rx ERROR ! ');
    end
   else write('Rx.. ');
  for c := 0 to (RxMsgPaks[ReportPtr,3] and $7F) + 4  do
   write(hex(RxMsgPaks[ReportPtr,c]),' ');
  writeln;
  end;


(************************ Ab Code Begins ************************)

procedure Reset8584;        (* Software reset of PCD8584        *)
  begin
  port[ControlPort] := 1;
  delay(50);
  port[ControlPort] := 0;
  end;


procedure SetStatusRegister(Rgst : byte; Dta : byte);
  begin    (* Set PCD8584 Control Register  *)
  port[StatPort] := PIN + Rgst;
  port[DataPort] := Dta;
  port[StatPort] := PIN + RW_DATA + ENI + ACK;
  if port[StatPort] <> $81 then
    begin
    writeln('ACCESS.bus Adapter Board not found..');
    halt;
    end;
  end;


procedure Init8584;
  begin
  SetStatusRegister(RW_ADDRESS,HOST_ADDRESS shr 1);
  SetStatusRegister(RW_CLOCK,CARDSPEED);
  end;


procedure ResetAbDevices;
var n : byte;
  begin
  if EchoMode > ECHOOFF then writeln('Resetting all Ab Devices...');
  BusStatus := BUSRESET;
  AbMsg[1] := AB_DEV_RESET;
  for  n := MAXDEVICES downto 1 do
    begin
    TxPak((n * 2) + HOST_ADDRESS,
          HOST_ADDRESS,
          PROTOCOL + 1,
          AbMsg);
    end;
  end;


procedure ResetAll;
  begin
  Reset8584;
  Init8584;
  FillChar(Devices,sizeof(Devices),$00);
  I2CStatus := I2CIDLE;
  ResetAbDevices;
  end;


(*********************** AB Message Parser **********************)

procedure CheckQueue;
label Escape;
var
 DevNumber      : byte;
 AddressAssigned: boolean;
 c            : byte;
 SlotScan       : byte;


  begin
 Repeat
  if (EchoMode > ECHOTX) then DisplayRxedMessage;
  DevNumber := (RxMsgPaks[ReportPtr,2] - HOST_ADDRESS) shr 1;
  if (RxMsgPaks[ReportPtr,3] and PROTOCOL) = PROTOCOL then
    begin

      case RxMsgPaks[ReportPtr,4] of

 AB_DEV_ATTEN : begin
                if (RxMsgPaks[ReportPtr,2] = DEVICE_DEFAULT) then
                 begin        (*HOT-PLUG?*)
                 if (RxMsgPaks[ReportPtr,5] <> 0) then
                   begin
                   if EchoMode > 0 then
                     writeln('DEVICE REPORTS SELF TEST ERROR !!');
                   goto Escape;
                   end;
                 if (BusStatus = BUSCONFIGURED) then
                   begin
                   TimerCount := 5;
                   BusStatus := BUSCONFIRM;
                   for c := 1 to MAXDEVICES do (* Devices Present    *)
                     begin
                     if Devices[c].Status = DEVREADY then
                       begin
                       Devices[c].Status := DEVCONFIRM;
                       AbMsg[1] := AB_ID_REQUEST;
                       TxPak((SlotScan * 2) + HOST_ADDRESS,
                              HOST_ADDRESS,
                              PROTOCOL + 1,
                              AbMsg);

                       end;
                     end;
                   end;
                 end;
                end;

 AB_ID_REPORT : begin
                if Devices[Devnumber].Status = DEVCONFIRM then
                  begin
                  Devices[Devnumber].Status := DEVREADY;
                  end
                 else
                  begin
                  SlotScan := 0;
                  AddressAssigned := false;
                 Repeat
                  Inc(SlotScan);
                  if (Devices[SlotScan].Status = DEVRESET) then
                    begin
                    Devices[SlotScan].Status := DEVREADY;
                    AbMsg[1] := AB_ASSIGN_ADD;
                    for c := 2 to 29 do
                      begin
                      Devices[SlotScan].ID[c - 1] := chr(RxMsgPaks[ReportPtr,c + 3]);
                      AbMsg[c] := RxMsgPaks[ReportPtr,c + 3];
                      end;
                    AbMsg[30] := (SlotScan * 2) + HOST_ADDRESS;
                    Devices[SlotScan].Address := (SlotScan * 2) + HOST_ADDRESS;
                    TxPak(DEVICE_DEFAULT,
                          HOST_ADDRESS,
                          PROTOCOL + 30,
                          AbMsg);
                    AddressAssigned := true;
                    Devices[SlotScan].Status := DEVREADY;
                    Delay(2);
                    AbMsg[1] := AB_CAP_REQUEST;
                    AbMsg[2] := hi(Devices[SlotScan].CapOffset);
                    AbMsg[3] := lo(Devices[SlotScan].CapOffset);
                    TxPak((SlotScan * 2) + HOST_ADDRESS,
                            HOST_ADDRESS,
                            PROTOCOL + 3,
                            AbMsg);
                    if EchoMode = ECHOFULL then
                      begin
                      write('Dev ',hex((SlotScan * 2) + HOST_ADDRESS),' ');
                      for c := 1 to 28 do
                        write(Devices[SlotScan].ID[c]);
                      writeln;
                      end;
                    end;
                 Until AddressAssigned or (SlotScan > MAXDEVICES);
                  end;
                end;

AB_CAP_REPORT : begin
                if RxMsgPaks[ReportPtr,3] > $83 then
                  begin
                  Devices[DevNumber].CapOffset := (RxMsgPaks[ReportPtr,5] shl 8) +
                                      RxMsgPaks[ReportPtr,6];
                  for c := 0 to (RxMsgPaks[ReportPtr,3] - $83) do
                    Devices[DevNumber].Capabilities[Devices[DevNumber].CapOffset + c + 1] := chr(RxMsgPaks[ReportPtr,c + 7]);
                  Devices[DevNumber].CapOffset :=
                     Devices[DevNumber].CapOffset + (RxMsgPaks[ReportPtr,3] - $83);
                  AbMsg[1] := AB_CAP_REQUEST;
                  AbMsg[2] := hi(Devices[DevNumber].CapOffset);
                  AbMsg[3] := lo(Devices[DevNumber].CapOffset);
                  TxPak((DevNumber * 2) + HOST_ADDRESS,
                          HOST_ADDRESS,
                          PROTOCOL + 3,
                          AbMsg);
                  end
                 else
                  begin
                  for c := 1 to 127 do write(Devices[DevNumber].Capabilities[c]);
                  writeln
                  end;
                end;

      end;

    end;

Escape:

  Inc(ReportPtr);
  if ReportPtr = MAXRXPAKS then ReportPtr := 0;
 Until not DataReady;
  end;


(************************ Get a Hex Value ***********************)

procedure GetHex(var n : byte);
var
  GetValue : byte;

  begin
  GetValue := 0;
 Repeat
  if DataReady then CheckQueue;
  if Keypressed then
    begin
    chtr := upcase(Readkey);
     case chtr of
   'A'..'F',
   '0'..'9': if GetValue < 2 then
             begin            (* Small Digit     *)
             if GetValue = 1 then
               begin
               n := n + ASCIIToHex(chtr);
               GetValue := 2;
               end
              else
               begin                (* Big Digit        *)
               n := ASCIIToHex(chtr) * 16;
               GetValue := 1;
               end;
             write(chtr);
             end;

        ^M : begin
             if GetValue = 1 then Beep;
             end;

        ^H : if GetValue > 0 then
             begin
             if GetValue = 2 then
               begin
               n := n - ASCIIToHex(chtr);       (* Small Digit *)
               GetValue := 1;
               end
              else
               begin
               n := 0;        (* Big Digit      *)
               GetValue := 0;
               end;
             gotoxy(WhereX - 1,WhereY);
             write(' ');
             gotoxy(WhereX - 1,WhereY);
             end;

        else Beep;

     end;

    end;
 Until ((chtr = ^M) and ((GetValue = 2) or (GetValue = 0))) or (chtr = ^[);
  writeln;
  end;


(*********************** Get a User Messge **********************)

procedure DoUserInput(x,y : integer);
const
  n     : integer = 0;
  dest  : byte = 0;
  source: byte = 0;
  length: byte = 0;
var
  len   : integer;
  Val   : byte;
  Msg   : AbMessage;

  begin
  write('Enter Message (without ChkSum): ');
  n := 0;
  FillChar(msg,sizeof(msg),0);
REPEAT
  if DataReady then CheckQueue;
  if Keypressed then
    begin
    chtr := upcase(ReadKey);

     case chtr of
'0'..'9','A'..'F'
      : begin            (* Get a hex number and    *)
        if n < 44 then                  (* put in the proper    *)
          begin    (* place  *)
          write(chtr);
          Inc(n);
          Inc(x);
          if (n and 1) = 1 then
            val := ASCIIToHex(chtr) * 16
           else
            begin
            write(' ');
            Inc(x);
            val := val + ASCIIToHex(chtr);
              case n div 2 of
         1 : dest := val;
         2 : source := val;
         3 : begin
             length := val;
             Len := length and $7F;
             end;
     4..22 : Msg[(n div 2) - 3] := val;
              end;
            end;
          end
         else Beep;
          end;

   ^M : if length <> 0