home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / communic / async4_2 / async4.pas next >
Encoding:
Pascal/Delphi Source File  |  1987-11-14  |  33.9 KB  |  1,082 lines

  1. { Async4: Combined IBM, DG/1 serial interrupt handler unit  4.0b, 11 Nov 87 }
  2. {****************************************************************************
  3.  
  4.                                  Async4 Unit
  5.  
  6.                Async4 Communication Routines by Michael Quinlan
  7.                 Extended for use with DG1 by N. Arley Dealey
  8.                 Customized for ZAPCIS use by N. Arley Dealey
  9.          Converted to Turbo Pascal version 4 unit by N. Arley Dealey
  10.  
  11.                                      ----
  12.  
  13. NOTES:
  14.   1.  This is a quick and dirty adaptation of a quick and dirty hack of
  15.       Michael Quinlan's original work... don't expect beauty.  It does,
  16.       however, seem to work well enough within its basic design limitations.
  17.       The adaptation is also incomplete.  DTR control is not implemented for
  18.       the IBM machine type, Async_Change and Async_GetParam are not
  19.       implemented for the DG1 and nothing at all is implemented for the T2K.              
  20.   2.  This combined version extracts a few penalties in size and overhead.
  21.       The execution overhead is not generally a problem except in the
  22.       procedure Async_Send.  Eventually xmit ISRs should be installed and
  23.       Async_Send should just put the char into a buffer and insure that the
  24.       transmitter interrupt is enabled.  This would avoid the character by
  25.       character overhead currently experienced.
  26.   3.  I'm rather disturbed by Async_Open and Async_Close installing/removing
  27.       the interrupt handler.  The installation really should be done in
  28.       Async_Init and a companion procedure Async_Quit should be added to
  29.       perform the removal and other clean-up activities but I'm going to leave
  30.       it as is for now.  (nad  15 Jun 86)
  31.   4.  An Async_Flush procedure should probably also be added.
  32.   5.  LogicalPortNum values start at 1 and parallel the logical comm device
  33.       names (eg, LogicalPortNum 1 corresponds to the COM1: device).
  34.  
  35.  
  36. Revision History
  37. ----------------
  38. 14 Nov 87  4.1a  nad  Async_AvailPorts replaced with Async_AvailablePorts
  39.                       to allow non-seqential ports
  40. 11 Nov 87  4.0b  nad  Finally woke up & eliminated need for special TAP ver    
  41. 10 Nov 87  4.0b  nad  Added Async_GetParams
  42. 09 Nov 87  3.0a  ---  *** Uploaded to CompuServe BorPro forum DL2 ***********
  43.            3.0a  nad  Removed second param to Async_MapBpsRate
  44.            2.0a  nad  Fixed bps rate error, added: aBpsRate, Async_MapBpsRate
  45.            1.2a  nad  Uses DelayTick & DelaySecond now instead of Delay
  46.            1.1b  nad  Fixed bogus Divisor in Async_Change
  47.            1.1a  nad  Async_Buffer_Check renamed to Async_Get_Char for TapCIS
  48.            1.0a  nad  Reincorporated Async_Change from TapCIS sources
  49.            0.0a  nad  Exceedingly crude conversion to version 4 unit
  50. ---------------- OLD (include file version) REVISION HISTORY ----------------
  51.            4.1b  nad  Brute force hack to insure line is dropped on close
  52.            4.1a  nad  DTR control reinstated (conditionally)
  53.            4.0b  nad  _async_IsOpen changed to "typed constant"
  54.            4.0a  nad  Support for WRITE[LN]( Usr ... )
  55.            3.1b  nad  Back to single overlay -- double actually _cost_ space!
  56.            3.1a  nad  Double overlay version
  57.            3.0b  nad  squashed initialization bug in IBM_Async_Open
  58.                       experimental (rom call) Async_Send
  59.            3.0a  nad  Overlayed & reconsolidated version
  60.            2.0a  nad  Split into Async.0 and Async.1
  61.            1.0c  nad  AsyncDG1_SetDTR removed for space considerations
  62.            1.0b  nad  SetMachineInfo added, param removed from Async_Init
  63.            1.0a  nad  First combined version, new param to Async_Init
  64.            0.2b  nad  Saves & restores int vector (as it should have all along)
  65.            0.2a  nad  Various changes & extensions for ZapCIS compatibility
  66.            0.1a  nad  Added AsyncDG1_SetDTR procedure
  67. 16 Apr 86  0.0a  nad  First cut at conversion to work with DG/1
  68. {===========================================================================}
  69.  
  70. {---  Conditionals ---------------------------------------------------------}
  71. { The following conditionals may be set to control compilation as specified }
  72. { DEFINE Test           }{ Enables various trace reports                    }
  73. { DEFINE ForceDG1       }{ Forces machine type to DG1 for testing purposes  }
  74. { DEFINE DTR_Control    }{ Adds procedure to set/clear DTR                  }
  75. {---------------------------------------------------------------------------}
  76.  
  77. {$B-} { Short circuit boolean evaluation }
  78. {$I-} { I/O checking    OFF   }
  79. {$R-} { Range checking  OFF   }
  80. {$S-} { Stack checking  OFF   }
  81. {$V-} { Var-str check   OFF   }
  82.  
  83. UNIT Async4 ;
  84.  
  85. INTERFACE
  86.  
  87. USES
  88.   Timers,
  89.   Dos ;
  90.  
  91.  
  92. CONST
  93.   UnitVersion           = '4.0b' ;
  94.   UnitVerDate           = '11 Nov 87' ;
  95.   MaxPort               = 8 ;
  96.  
  97.  
  98. TYPE
  99.   aComputerType         = (IBM, DG1, T2K) ;
  100.   aSharedPort           = (ExtPort, IntModem) ;
  101.   aParitySetting        = (NoParity, OddParity, EvenParity) ;
  102.   aBpsRate              = (bps110,  bps150,  bps300,  bps600,
  103.                            bps1200, bps2400, bps4800, bps9600) ;
  104.   aSetOfPorts           = SET OF 1..MaxPort ;
  105.  
  106. {$IFDEF Test}
  107.   VAR
  108.     Async_BufferUsed    : word ;
  109.     Async_MaxBufferUsed : word ;
  110.   {$ENDIF}
  111.  
  112.  
  113. FUNCTION Async_DefinePort
  114.   (     LogicalPortNum  : byte ;
  115.         Base            : word ;
  116.         IRQ             : byte )
  117.   : BOOLEAN ;
  118.  
  119.  
  120. FUNCTION Async_Open
  121.   (     LogicalPortNum  : byte ;
  122.         Rate            : aBpsRate ;
  123.         Parity          : aParitySetting ;
  124.         WordSize        : byte ;
  125.         StopBits        : byte )
  126.   : BOOLEAN ;
  127.   { Sets up interrupt vector, initializes the com port for processing, sets }
  128.   { pointers to the buffer.  Returns false if com port not installed.       }
  129.  
  130. PROCEDURE Async_Close ;
  131.   { Turn off the com port interrupts & restores original vector.  }
  132.  
  133. PROCEDURE Async_Change 
  134.   (     Rate            : aBpsRate ;
  135.         Parity          : aParitySetting ;
  136.         WordSize        : byte  ;
  137.         StopBits        : byte  ) ;
  138.   { Change current comm parameters }
  139.  
  140. PROCEDURE Async_GetParams
  141.   ( VAR Rate            : aBpsRate ;
  142.     VAR Parity          : aParitySetting ;
  143.     VAR WordSize        : byte  ;
  144.     VAR StopBits        : byte  ) ;
  145.   { Get values of current comm parameters }
  146.  
  147. FUNCTION Async_MapBpsRate
  148.   (     Rate    : word )
  149.   : aBpsRate ;
  150.  
  151. FUNCTION Async_PortName
  152.   (     LogicalPortNum : byte )
  153.   : string ;
  154.   { Returns name of specified port. }
  155.  
  156. PROCEDURE Async_AvailablePorts
  157.   ( VAR CurrentPorts : aSetOfPorts ) ;
  158.   { Returns currently available comm ports. }
  159.  
  160. FUNCTION ASync_ComputerType
  161.   : aComputerType ;
  162.   { Returns type of computer program is executing on. }
  163.  
  164. FUNCTION Async_BufferOverflow
  165.   : BOOLEAN ;
  166.   { Returns true if input buffer has overflowed, otherwise false. }
  167.  
  168. FUNCTION Async_Buffer_Check
  169.   ( VAR c : CHAR )
  170.   : BOOLEAN ;
  171.   { If a character is available, returns true and moves the character from  }
  172.   { the buffer to the parameter.  Otherwise, returns false and parameter is } 
  173.   { undefined.                                                                }
  174.  
  175. PROCEDURE Async_Send
  176.   (     c : CHAR ) ;
  177.   { Transmits the character. }
  178.  
  179. PROCEDURE Async_Send_String
  180.   (     s : string ) ;
  181.   { Calls Async_Send to send each character of S, then sends <CR>. }
  182.  
  183. {$IFDEF Dtr_Control}
  184.   PROCEDURE Async_SetDTR
  185.     (     LogicalPortNum : byte ;
  186.           DTR_True       : BOOLEAN ) ;
  187.   {$ENDIF}
  188.  
  189. IMPLEMENTATION {============================================================}
  190.  
  191. CONST
  192.   MaxPhysPort    = 7 ;
  193.   BufferSize     = 4096 ;
  194.   BufferMax      = 4095 ;
  195.  
  196.   CommInterrupt  = $14 ;
  197.   I8088_IMR      = $21 ; { port address of the Interrupt Mask Register }
  198.  
  199.   { register offsets from base of IBM 8250 UART }
  200.   IBM_UART_THR         = $00 ;
  201.   IBM_UART_RBR         = $00 ;
  202.   IBM_UART_IER         = $01 ;
  203.   IBM_UART_IIR         = $02 ;
  204.   IBM_UART_LCR         = $03 ;
  205.   IBM_UART_MCR         = $04 ;
  206.   IBM_UART_LSR         = $05 ;
  207.   IBM_UART_MSR         = $06 ;
  208.  
  209.    { register offsets from base of DG/1 82C51A USART }
  210.   DG1_USART_Data       = $00 ;
  211.   DG1_USART_Status     = $01 ;
  212.   DG1_USART_Control    = $01 ;
  213.  
  214.   { misc }
  215.   DG1_CommOnCmd        = $8000 ;
  216.   DG1_CommOffCmd       = $8100 ;
  217.   DG1_Success          = $0000 ;
  218.   DG1_Failure          = $8000 ;
  219.  
  220.  
  221. VAR
  222.   ExitSave       : pointer ;
  223.   OriginalVector : pointer ;
  224.   IsOpen         : BOOLEAN ;
  225.   ComputerType   : aComputerType ;
  226.   Overflow       : BOOLEAN ;
  227.   PortsAvail     : aSetOfPorts ;
  228.   PhysicalPort   : byte ;       { currently open PHYSICAL port number }
  229.   { PhysicalPort is necessary because, although the mapping of logical to }
  230.   { physical port numbers is a straightforward arithmetic function on IBM }
  231.   { machines, the mapping is not straightforward on the DG/1 where both   }
  232.   { logical port number 2 and logical port number 3 share a physical port }
  233.   Base           : word ;       { base for open port }
  234.   IRQ            : byte ;       { irq  for open port }
  235.  
  236.   Buffer         : ARRAY [0..BufferMax] OF CHAR ;
  237.   BufferHead     : word ;       { Location in Buffer to put next char }
  238.   BufferTail     : word ;       { Location in Buffer to get next char }
  239.   BufferNewTail  : word ;
  240.  
  241.   DG1_IntOrExt   : aSharedPort ;
  242.  
  243. const { why, oh why, does Turbo _still_ insist on callings these constants? }
  244.   PortTable      : ARRAY [0..MaxPhysPort] OF RECORD
  245.     Base : word ;
  246.     IRQ  : byte
  247.     END  { PortTable record } = ( (Base : 0 ;  IRQ : 0),
  248.                                   (Base : 0 ;  IRQ : 0),
  249.                                   (Base : 0 ;  IRQ : 0),
  250.                                   (Base : 0 ;  IRQ : 0),
  251.                                   (Base : 0 ;  IRQ : 0),
  252.                                   (Base : 0 ;  IRQ : 0),
  253.                                   (Base : 0 ;  IRQ : 0),
  254.                                   (Base : 0 ;  IRQ : 0) ) ;
  255.  
  256.  
  257. {---------------------------------------------------------------------------}
  258. {                    M A C R O     D E F I N I T I O N S                    }
  259. {---------------------------------------------------------------------------}
  260.  
  261. PROCEDURE DisableInterrupts ;   inline( $FA {cli} ) ;
  262. PROCEDURE EnableInterrupts ;    inline( $FB {sti} ) ;
  263.  
  264.  
  265. {$IFDEF Test}
  266.   {$I Hex.inc}
  267.   {$ENDIF}
  268.  
  269.  
  270. {---------------------------------------------------------------------------}
  271. {                     L O C A L     P R O C E D U R E S                     }
  272. {---------------------------------------------------------------------------}
  273.  
  274.  
  275. {---------------------------------------------------------------------------}
  276. {                      ISR - Interrupt Service Routine                      }
  277. {---------------------------------------------------------------------------}
  278.  
  279. PROCEDURE ISR ; INTERRUPT ;
  280. { Interrupt Service Routine }
  281. { Invoked when the USART has received a byte of data from the comm line }
  282. { re-written 9/10/84 in machine language ; original source left as comments }
  283.  
  284. BEGIN { ISR }
  285.   inline(
  286.     $FB/                                { STI }
  287.  
  288.     { get the incoming character }
  289.     { Buffer[BufferHead] := CHR( port[Base + DG1_USART_Data] ) ; }
  290.     $8B/$16/Base/                       { MOV DX,Base }
  291.     $EC/                                { IN AL,DX }
  292.     $8B/$1E/BufferHead/                 { MOV BX,BufferHead }
  293.     $88/$87/Buffer/                     { MOV Buffer[BX],AL }
  294.  
  295.     { BufferNewHead := SUCC( BufferHead ) ;               }
  296.     $43/                                { INC BX }
  297.  
  298.     { IF BufferNewHead > BufferMax THEN BufferNewHead := 0 ; }
  299.     $81/$FB/BufferMax/                  { CMP BX,BufferMax }
  300.     $7E/$02/                            { JLE L001 }
  301.     $33/$DB/                            { XOR BX,BX }
  302.  
  303.     { IF BufferNewHead = BufferTail THEN Overflow := TRUE }
  304.     {L001:}
  305.     $3B/$1E/BufferTail/                 { CMP BX,BufferTail }
  306.     $75/$08/                            { JNE L002 }
  307.     $C6/$06/Overflow/$01/               { MOV Overflow,1 }
  308.     $90/                                { NOP generated by assembler for some reason }
  309.     $EB/$16/                            { JMP SHORT L003 }
  310.     { ELSE BEGIN                                                        }
  311.     {   BufferHead := BufferNewHead ;                     }
  312.     {   Async_BufferUsed  := SUCC( Async_BufferUsed ) ;                 }
  313.     {   IF Async_BufferUsed > Async_MaxBufferUsed THEN                  }
  314.     {     Async_MaxBufferUsed := Async_BufferUsed                       }
  315.     {   END ;                                                           }
  316.     {L002:}
  317.     $89/$1E/BufferHead/                 { MOV BufferHead,BX }
  318.     {$IFDEF Test}
  319.       $FF/$06/Async_BufferUsed/         { INC Async_BufferUsed }
  320.       $8B/$1E/Async_BufferUsed/         { MOV BX,Async_BufferUsed }
  321.       $3B/$1E/Async_MaxBufferUsed/      { CMP BX,Async_MaxBufferUsed }
  322.       $7E/$04/                          { JLE L003 }
  323.       $89/$1E/Async_MaxBufferUsed/      { MOV Async_MaxBufferUsed,BX }
  324.       {$ENDIF}
  325.     {L003:}
  326.  
  327.     { disable interrupts } {?????????????????????}
  328.     $FA/                                { CLI }
  329.  
  330.     { issue non-specific EOI }
  331.     { port[$20] := $20 ;                                                }
  332.     $B0/$20/                            { MOV AL,20h }
  333.     $E6/$20                             { OUT 20h,AL }
  334.     )
  335.   END { ISR } ;
  336.  
  337.  
  338. PROCEDURE DG1_Send
  339.   (     c : CHAR ) ;
  340.  
  341. { DG/1 character output procedure for USR device driver }
  342.  
  343. VAR
  344.   Counter : word ;
  345.  
  346. BEGIN { DG1_Send }
  347.   { wait for DSR & TxRdy }
  348.   Counter := MAXINT ;
  349.   WHILE (Counter <> 0) and ((port[Base + DG1_USART_Status] and $81) <> $81) DO
  350.     dec( Counter ) ;
  351.   { send the char IFF dsr & txrdy are true }
  352.   IF Counter <> 0 THEN BEGIN { send the character }
  353.     DisableInterrupts ; { critical region? why? }
  354.     port[Base + DG1_USART_Control] := $37 ; { err reset, rxe, dtr, rts, txe }
  355.     port[Base + DG1_USART_Data]    := ORD( c ) ;
  356.     port[Base + DG1_USART_Control] := $36 ; { err reset, rxe, dtr, rts }
  357.     EnableInterrupts    { critical region? why? }
  358.     END
  359.   END { DG1_Send } ;
  360.  
  361.  
  362. PROCEDURE IBM_Send
  363.   (     c : CHAR ) ;
  364.  
  365. { IBM character output procedure for USR device driver }
  366.  
  367. VAR
  368.   Counter : word ;
  369.  
  370. BEGIN { IBM_Send }
  371.   port[IBM_UART_MCR + Base] := $0B ; { turn on OUT2, DTR, and RTS }
  372.   Counter := MAXINT ;
  373.   WHILE (Counter <> 0) AND ((port[IBM_UART_MSR + Base] AND $10) = 0) DO
  374.     dec( Counter ) ;
  375.   IF Counter <> 0 THEN
  376.     Counter := MAXINT ;
  377.   WHILE (Counter <> 0) AND ((port[IBM_UART_LSR + Base] AND $20) = 0) DO
  378.     dec( Counter ) ;
  379.   DisableInterrupts ;
  380.   port[IBM_UART_THR + Base] := ORD( c ) ;
  381.   EnableInterrupts
  382.   END { IBM_Send } ;
  383.  
  384.  
  385. PROCEDURE DG1_DisablePort
  386.   (     PhysicalPortNum  : byte ;
  387.         Which            : aSharedPort ) ;
  388.  
  389. { Power down line drivers for desired port }
  390.  
  391. VAR
  392.   Regs : Registers ;
  393.  
  394. BEGIN { DG1_DisablePort }
  395.   WITH Regs DO BEGIN
  396.     ax := DG1_CommOffCmd ;
  397.     cx := ORD( Which ) ;
  398.     dx := PhysicalPortNum ;
  399.     intr( CommInterrupt, Regs )
  400.     END
  401.   END { DG1_DisablePort } ;
  402.  
  403.  
  404. FUNCTION DG1_EnablePort
  405.   (     PhysicalPortNum  : byte ;
  406.         Which            : aSharedPort )
  407.   : BOOLEAN ;
  408.  
  409. { Power up line drivers for desired port and check for presence }
  410.  
  411. VAR
  412.   Regs : Registers ;
  413.  
  414. BEGIN { DG1_EnablePort }
  415.   IF (PhysicalPortNum = 0) AND (Which = ExtPort) THEN
  416.     DG1_DisablePort( PhysicalPortNum, IntModem ) ; { make sure internal is off }
  417.   DG1_EnablePort := FALSE ; { assume the worst }
  418.   WITH Regs DO BEGIN
  419.     ax := DG1_CommOnCmd ;
  420.     cx := ORD( Which ) ;
  421.     dx := PhysicalPortNum ;
  422.     intr( CommInterrupt, Regs ) ;
  423.     IF ax = DG1_Success THEN
  424.       DG1_EnablePort := TRUE
  425.     ELSE
  426.       DG1_DisablePort( PhysicalPortNum, Which ) { make sure it is all off }
  427.     END
  428.   END { DG1_EnablePort } ;
  429.  
  430.  
  431. {$F+}
  432. PROCEDURE TerminateUnit ; {$F-}
  433.  
  434. BEGIN { TerminateUnit }
  435.   Async_Close ;
  436.   ExitProc := ExitSave
  437.   END { TerminateUnit } ;
  438.  
  439.  
  440. PROCEDURE InitializeUnit ;
  441.  
  442. { initialize variables }
  443.  
  444. CONST
  445.   NumPortsMask       = $0E00 ;
  446.   NumPortsShift      = 9 ;
  447.  
  448. VAR
  449.   j                  : byte ;
  450.   BIOS_Ports         : byte ;
  451.   BIOS_PortBaseTable : ARRAY [0..3] OF word absolute $0040:$0000 ;
  452.   BIOS_EquipFlag     : word                 absolute $0040:$0010 ;
  453.  
  454.   FUNCTION DetermineMachineType
  455.     : aComputerType ;
  456.  
  457.   { Determine which machine we are currently running on }
  458.  
  459.   CONST
  460.     ROM_Seg      = $F000 ;
  461.     ROM_MaxIndex = $7FFF ;
  462.     SearchStart  = $2000 ;
  463.     SearchEnd    = $27FF ;
  464.  
  465.   TYPE
  466.     SearchState  = (Searching, Checking, Found, NotFound) ;
  467.  
  468.   VAR
  469.     ROM          : ARRAY [0..ROM_MaxIndex] OF CHAR absolute ROM_Seg:$0000 ;
  470.     State        : SearchState ;
  471.     Index        : word ;
  472.     k            : byte ;
  473.     ID_Var       : string[11] ;
  474.  
  475.   BEGIN { DetermineMachineType }
  476.     {$IFDEF ForceDG1}
  477.       DetermineMachineType := DG1
  478.     {$ELSE}
  479.       ID_Var := 'GENERAL/One' ;
  480.       Index  := SearchStart;
  481.       State  := Searching ;
  482.       REPEAT
  483.         IF ROM[Index] = ID_Var[1] THEN BEGIN
  484.           k     := 1 ;
  485.           State := Checking ;
  486.           WHILE State = Checking DO BEGIN
  487.             inc( k ) ;
  488.             inc( Index ) ;
  489.             IF ROM[Index] <> ID_Var[k] THEN
  490.               State := Searching
  491.             ELSE IF k = length( ID_Var ) THEN
  492.               State := Found
  493.             END { while }
  494.           END ; { if }
  495.         inc( Index ) ;
  496.         IF Index > SearchEnd THEN
  497.           State := NotFound
  498.         UNTIL State IN [Found, NotFound] ;
  499.       IF State = Found THEN
  500.         DetermineMachineType := DG1
  501.       ELSE
  502.         DetermineMachineType := IBM
  503.       {$ENDIF}
  504.     END { DetermineMachineType } ;
  505.  
  506. BEGIN { InitializeUnit }
  507.   ExitSave := ExitProc ;
  508.   ExitProc := @TerminateUnit ;
  509.   IsOpen   := FALSE ;
  510.   Overflow := FALSE ;
  511.   {$IFDEF Test}
  512.     Async_BufferUsed      := 0 ;
  513.     Async_MaxBufferUsed   := 0 ;
  514.     {$ENDIF}
  515.   BIOS_Ports := ((BIOS_EquipFlag and NumPortsMask) shr NumPortsShift) ;
  516.   FOR j := 0 TO PRED( BIOS_Ports ) DO WITH PortTable[j] DO BEGIN
  517.     Base := BIOS_PortBaseTable[j] ;
  518.     IRQ  := SUCC( hi(Base) ) ;
  519.     PortsAvail := PortsAvail + [SUCC(j)]
  520.     END ; { for }
  521.   IF DetermineMachineType = IBM THEN BEGIN
  522.     ComputerType := IBM ;
  523.     END
  524.   ELSE { machinetype = DG1 } BEGIN
  525.     ComputerType := DG1 ;
  526.     { Note: This really needs to check for presence of internal modem but  }
  527.     {       for right now we'll just assume it is installed & hard code it }
  528.     PortsAvail   := PortsAvail + [3] ;
  529.     PortTable[3] := PortTable[1] { HACK! }
  530.     END
  531.   END { InitializeUnit } ;
  532.  
  533.  
  534. {---------------------------------------------------------------------------}
  535. {                  E X P O R T E D     P R O C E D U R E S                  }
  536. {---------------------------------------------------------------------------}
  537.  
  538.  
  539. FUNCTION Async_DefinePort
  540.   (     LogicalPortNum : byte ;
  541.         Base           : word ;
  542.         IRQ            : byte )
  543.   : BOOLEAN ;
  544.  
  545. VAR
  546.   PhysPortNum : byte ;
  547.  
  548. BEGIN { Async_DefinePort }
  549.   PhysPortNum := PRED( LogicalPortNum ) ;
  550.   IF (PhysPortNum IN [0..MaxPhysPort]) THEN BEGIN
  551.     PortTable[PhysPortNum].Base := Base ;
  552.     PortTable[PhysPortNum].IRQ  := IRQ ;
  553.     PortsAvail := PortsAvail + [LogicalPortNum] ;
  554.     Async_DefinePort := TRUE
  555.     END
  556.   ELSE
  557.     Async_DefinePort := FALSE
  558.   END { Async_DefinePort } ;
  559.  
  560.  
  561. {$IFDEF DTR_Control}
  562.   PROCEDURE Async_SetDTR
  563.     (     LogicalPortNum : byte ;
  564.           DTR_True       : BOOLEAN ) ;
  565.   
  566.   VAR
  567.     Regs : Registers ;
  568.   
  569.   BEGIN { Async_SetDTR }
  570.     IF Async_CurrentMachine = DG1 THEN WITH Regs DO BEGIN
  571.       IF DTR_True THEN
  572.         ax := $8200
  573.       ELSE
  574.         ax := $8300 ;
  575.       dx := PRED( LogicalPortNum ) ;
  576.       intr( CommInterrupt, Regs )
  577.       END
  578.     ELSE { Async_CurrentMachine = IBM } BEGIN
  579.       {*** NOT IMPLEMENTED YET ***}
  580.       END
  581.     END { Async_SetDTR } ;
  582.   {$ENDIF}
  583.  
  584.  
  585. FUNCTION Async_Buffer_Check
  586.   ( VAR c : CHAR )
  587.   : BOOLEAN ;
  588.  
  589. { see if a character has been received ; return it if yes }
  590.  
  591. BEGIN { Async_Buffer_Check/Async_Get_Char }
  592.   IF BufferHead = BufferTail THEN
  593.     Async_Buffer_Check := FALSE
  594.   ELSE BEGIN
  595.     c          := Buffer[BufferTail] ;
  596.     BufferTail := (SUCC( BufferTail ) MOD BufferSize) ;
  597.     {$IFDEF Test}
  598.       dec( Async_BufferUsed ) ;
  599.       {$ENDIF}
  600.     Async_Buffer_Check := TRUE
  601.     END
  602.   END { Async_Buffer_Check/Async_Get_Char } ;
  603.  
  604.  
  605. PROCEDURE Async_Send
  606.   (     c : CHAR ) ;
  607.  
  608. { transmit a character }
  609.  
  610. BEGIN { Async_Send }
  611.   CASE ComputerType OF
  612.     DG1 : DG1_Send( c ) ;
  613.     IBM : IBM_Send( c ) ;
  614.     T2K : begin writeln( '*** Async Error ***' ) ; halt end
  615.     END { case }
  616.   END { Async_Send } ;
  617.  
  618.  
  619. PROCEDURE Async_Send_String
  620.   (     s : string ) ;
  621.  
  622. { transmit a string }
  623.  
  624. VAR
  625.   i : byte ;
  626.  
  627. BEGIN { Async_Send_String }
  628.   FOR i := 1 to length( s ) DO
  629.     Async_Send( s[i] )
  630.   END ; { Async_Send_String }
  631.  
  632.  
  633. FUNCTION Async_MapBpsRate
  634.   (     Rate    : word )
  635.   : aBpsRate ;
  636.  
  637. BEGIN { Async_MapBpsRate }
  638.   IF Rate <= 110 THEN
  639.     Async_MapBpsRate := bps110
  640.   ELSE IF Rate <= 150  THEN
  641.     Async_MapBpsRate := bps150
  642.   ELSE IF Rate <= 300  THEN
  643.     Async_MapBpsRate := bps300
  644.   ELSE IF Rate <= 600  THEN
  645.     Async_MapBpsRate := bps600
  646.   ELSE IF Rate <= 1200 THEN
  647.     Async_MapBpsRate := bps1200
  648.   ELSE IF Rate <= 2400 THEN
  649.     Async_MapBpsRate := bps2400
  650.   ELSE IF Rate <= 4800 THEN
  651.     Async_MapBpsRate := bps4800
  652.   ELSE
  653.     Async_MapBpsRate := bps9600
  654.   END { Async_MapBpsRate } ;
  655.  
  656.  
  657. FUNCTION Async_PortName
  658.   (     LogicalPortNum : byte )
  659.   : string ;
  660.  
  661. { Returns name of specified port. }
  662.  
  663. VAR
  664.   s : string[1] ;
  665.  
  666. BEGIN { Async_PortName }
  667.   CASE ComputerType OF
  668.     DG1 : BEGIN
  669.       CASE LogicalPortNum OF
  670.         1 : Async_PortName := 'External' ;
  671.         2 : Async_PortName := 'Com2:   ' ;
  672.         3 : Async_PortName := 'Internal'
  673.         END { case }
  674.       END ;
  675.     IBM : BEGIN
  676.       str( LogicalPortNum, s ) ;
  677.       Async_PortName := concat( 'COM', s, ':' )
  678.       END ;
  679.     T2K : begin writeln( '*** Async Error ***' ) ; halt end
  680.     END { case }
  681.   END { Async_PortName } ;
  682.  
  683.  
  684. PROCEDURE Async_AvailablePorts
  685.   ( VAR CurrentPorts : aSetOfPorts ) ;
  686.  
  687. BEGIN { Async_AvailablePorts }
  688.   CurrentPorts := PortsAvail
  689.   END { Async_AvailablePorts } ;
  690.  
  691.  
  692. FUNCTION ASync_ComputerType
  693.   : aComputerType ;
  694.  
  695. BEGIN { Async_ComputerType }
  696.   Async_ComputerType := ComputerType
  697.   END { Async_ComputerType } ;
  698.  
  699.  
  700. FUNCTION Async_BufferOverflow
  701.   : BOOLEAN ;
  702.  
  703. BEGIN { Async_BufferOverflow }
  704.   Async_BufferOverflow := Overflow
  705.   END { Async_BufferOverflow } ;
  706.  
  707.  
  708. PROCEDURE Async_Close ;
  709.  
  710. { reset the interrupt system when USART interrupts no longer needed }
  711.  
  712.   {$IFDEF Test}
  713.     procedure reportstatus
  714.       (     header : boolean ) ;
  715.     var
  716.       mask : byte ;
  717.       j    : byte ;
  718.     begin
  719.       if header then
  720.         writeln( '*** DSR    BrkDet FrmErr OvrRun ParErr TxEmp  RxRdy  TxRdy ' ) ;
  721.       write( '*** ' ) ;
  722.       mask := $80 ;
  723.       for j := 7 downto 0 do begin
  724.         if ((port[Base + DG1_USART_Status] and mask) = 0) then
  725.           write( 'false  ' )
  726.         else
  727.           write( 'TRUE   ' ) ;
  728.         mask := mask shr 1
  729.         end ;
  730.       writeln
  731.       end ;
  732.     {$ENDIF}
  733.  
  734. BEGIN { Async_Close }
  735.   {$IFDEF Test}
  736.     WRITELN( '*** Async_Close' ) ;
  737.     {$ENDIF}
  738.   IF IsOpen THEN BEGIN
  739.     { disable the IRQ on the 8259 }
  740.     { --- ENTER CRITICAL AREA (?) ------------------------------------------ }
  741.     DisableInterrupts ;
  742.     port[I8088_IMR] := (port[I8088_IMR] or (1 shl IRQ)) ;
  743.     EnableInterrupts ;
  744.     { --- EXIT CRITICAL AREA (?) ------------------------------------------- }
  745.     { shut down the UART/USART }
  746.     CASE ComputerType OF
  747.       DG1 : BEGIN
  748.         {$IFDEF Test}
  749.           reportstatus( true ) ;
  750.           {$ENDIF}
  751.         { Check whether we're using the internal modem or something connected }
  752.         { to the external port and act accordingly.  We have to do this       }
  753.         { because of DG's brain-damaged decision that the internal modem      }
  754.         { should default to autoanswering (!) and do it on the very _first_   }
  755.         { ring (!!!) plus the fact that dropping DTR on the internal causes   }
  756.         { all the modem registers to be reset to their default values.  A pox }
  757.         { on DG's house for this travesty.                             -nacd  }
  758.         IF DG1_IntOrExt = ExtPort THEN { err reset & disable everything }
  759.           port[Base + DG1_USART_Control] := $10
  760.         ELSE { send onhook command then deal with usart } BEGIN
  761.           DelaySeconds( 1 ) ;
  762.           Async_Send( '+' ) ;  Async_Send( '+' ) ;  Async_Send( '+' ) ;
  763.           DelaySeconds( 1 ) ;
  764.           Async_Send_String( 'ATH' ) ;
  765.           DelayTicks( 6 ) ;
  766.           {err reset, rx & tx disabled, RTS false but leave DTR true }
  767.           port[Base + DG1_USART_Control] := $12
  768.           END ;
  769.         DelayTicks( 2 ) ;
  770.         {$IFDEF Test}
  771.           reportstatus( false ) ;
  772.           {$ENDIF}
  773.         DG1_DisablePort( PhysicalPort, DG1_IntOrExt )
  774.         END ; { dg1 }
  775.       IBM : BEGIN
  776.         port[Base + IBM_UART_IER] := 0 ;
  777.         port[Base + IBM_UART_MCR] := 0
  778.         END ; { ibm }
  779.       T2K : BEGIN
  780.         writeln( '*** Async Error ***' ) ; halt
  781.         END
  782.       END ; { case }
  783.     SetIntVec( IRQ + 8, OriginalVector ) ;
  784.     IsOpen := FALSE
  785.     END
  786.   END { Async_Close } ;
  787.  
  788.  
  789. FUNCTION Async_Open
  790.   (     LogicalPortNum     : byte ;
  791.         Rate               : aBpsRate ;
  792.         Parity             : aParitySetting ;
  793.         WordSize           : byte ;
  794.         StopBits           : byte )
  795.   : BOOLEAN ;
  796.  
  797. { open a communications port }
  798.  
  799.   PROCEDURE BIOS_RS232_Init
  800.     (     ThePort : byte ;
  801.           ComParm : word ) ;
  802.  
  803.   { Issue Interrupt $14 to initialize the UART/USART }
  804.   { See the IBM PC Technical Reference Manual for the format of ComParm }
  805.   { Valid for both IBM and DG1 }
  806.  
  807.   VAR
  808.     Regs : Registers ;
  809.  
  810.   BEGIN { BIOS_RS232_Init }
  811.     WITH Regs DO BEGIN
  812.       ah := $00 ;
  813.       al := ComParm ;
  814.       dx := ThePort ;
  815.       intr( CommInterrupt, Regs )
  816.       END
  817.     END { BIOS_RS232_Init } ;
  818.  
  819.   PROCEDURE BuildComParm
  820.     (     Rate      : aBpsRate ;
  821.           Parity    : aParitySetting ;
  822.           WordSize  : byte ;
  823.           StopBits  : byte ;
  824.       VAR ComParm   : word ) ;
  825.  
  826.   { Build the ComParm for RS232_Init }
  827.   { See DG/1 Programmer's Reference Manual for description }
  828.  
  829.   CONST
  830.     RateTable : ARRAY [aBpsRate] OF byte
  831.       = ( $00, $20, $40, $60, $80, $A0, $C0, $E0 ) ;
  832.  
  833.   VAR
  834.     i : byte ;
  835.  
  836.   BEGIN { BuildComParm }
  837.     ComParm := RateTable[Rate] ;
  838.  
  839.     CASE Parity OF
  840.       NoParity   : ComParm := (ComParm or $0000) ;
  841.       OddParity  : ComParm := (ComParm or $0008) ;
  842.       EvenParity : ComParm := (ComParm or $0018)
  843.       END ; { case }
  844.  
  845.     IF WordSize = 7 THEN
  846.       ComParm := (ComParm or $0002)
  847.     ELSE
  848.       ComParm := (ComParm or $0003) ;   { default to 8 data bits }
  849.  
  850.     IF StopBits = 2 THEN
  851.       ComParm := (ComParm or $0004)
  852.     ELSE
  853.       ComParm := (ComParm or $0000)    { default to 1 stop bit }
  854.     END { BuildComParm } ;
  855.  
  856.   PROCEDURE CommonInit
  857.     (     PhysicalPort : byte ;
  858.           Rate         : aBpsRate ;
  859.           Parity       : aParitySetting ;
  860.           WordSize     : byte ;
  861.           StopBits     : byte ) ;
  862.  
  863.   VAR
  864.     ComParm   : word ; { YUCK! TP3 won't pack multiple fields in a word }
  865.  
  866.   BEGIN { CommonInit }
  867.     Base := PortTable[PhysicalPort].Base ;
  868.     IRQ  := PortTable[PhysicalPort].IRQ ;
  869.  
  870.     { Build the ComParm for RS232_Init }
  871.     BuildComParm( Rate, Parity, WordSize, StopBits, ComParm ) ;
  872.  
  873.     { use the BIOS com port initialization routine to save code }
  874.     BIOS_RS232_Init( PhysicalPort, ComParm ) ;
  875.  
  876.     GetIntVec( IRQ + 8, OriginalVector ) ;
  877.     SetIntVec( IRQ + 8, @ISR )
  878.     END { CommonInit } ;
  879.  
  880.   FUNCTION DG1_Open
  881.     (     LogicalPortNum  : byte ;
  882.           Rate            : aBpsRate ;
  883.           Parity          : aParitySetting ;
  884.           WordSize        : byte ;
  885.           StopBits        : byte )
  886.     : BOOLEAN ;
  887.  
  888.   VAR
  889.     i : byte ;
  890.  
  891.   BEGIN { DG1_Open }
  892.     CASE LogicalPortNum of
  893.       1 : BEGIN PhysicalPort := 0 ;  DG1_IntOrExt := ExtPort  END ;
  894.       2 : BEGIN PhysicalPort := 1 ;  DG1_IntOrExt := ExtPort  END ;
  895.       3 : BEGIN PhysicalPort := 0 ;  DG1_IntOrExt := IntModem END ;
  896.       ELSE BEGIN
  897.         DG1_Open := FALSE ;
  898.         exit
  899.         END
  900.       END ; { case }
  901.     IF DG1_EnablePort( PhysicalPort, DG1_IntOrExt ) THEN BEGIN
  902.       CommonInit( PhysicalPort, Rate, Parity, WordSize, StopBits ) ;
  903.       DisableInterrupts ; { --- ENTER CRITICAL REGION ---------------------- }
  904.       { since we issued an interrupt $14 to initialize the 8251 we }
  905.       { can safely assume that it is now awaiting Control commands }
  906.       { reset any errors, enable RxRdy, assert DTR & RTS }
  907.       port[Base + DG1_USART_Control] := $26 ;
  908.  
  909.       { read and discard any chars which may be in the buffers }
  910.       i := port[Base + DG1_USART_Data] ;
  911.       i := port[Base + DG1_USART_Data] ;
  912.  
  913.       { enable the irq on the 8259 controller }
  914.       port[I8088_IMR] := (port[I8088_IMR] and ((1 shl IRQ) xor $FF)) ;
  915.       EnableInterrupts ; { --- EXIT CRITICAL REGION ------------------------ }
  916.       DG1_Open := TRUE
  917.       END
  918.     ELSE
  919.       DG1_Open := FALSE
  920.     END ; { DG1_Open }
  921.  
  922.   FUNCTION IBM_Open
  923.     (     LogicalPortNum  : byte ;
  924.           Rate            : aBpsRate ;
  925.           Parity          : aParitySetting ;
  926.           WordSize        : byte ;
  927.           StopBits        : byte )
  928.     : BOOLEAN ;
  929.  
  930.   VAR
  931.     i : byte ;
  932.  
  933.   BEGIN { IBM_Open }
  934.     PhysicalPort   := PRED( LogicalPortNum ) ;
  935.     IF PortTable[PhysicalPort].Base = 0 THEN
  936.       IBM_Open := FALSE
  937.     ELSE BEGIN
  938.       CommonInit( PhysicalPort, Rate, Parity, WordSize, StopBits ) ;
  939.       IF (port[IBM_UART_IIR + Base] and $00F8) <> 0 THEN
  940.         IBM_Open := FALSE
  941.       ELSE BEGIN
  942.         DisableInterrupts ; { --- ENTER CRITICAL REGION -------------------- }
  943.         port[IBM_UART_LCR + Base] :=
  944.           port[IBM_UART_LCR + Base] and $7F ;
  945.         i := port[IBM_UART_LSR + Base] ;
  946.         i := port[IBM_UART_RBR + Base] ;
  947.         port[I8088_IMR] := (port[I8088_IMR] and ((1 shl IRQ) xor $FF)) ;
  948.         port[IBM_UART_IER + Base] := $01 ; { enable data ready interrupt }
  949.         port[IBM_UART_MCR + Base] := (port[IBM_UART_MCR + Base] or $08) ;
  950.         EnableInterrupts ;  { --- EXIT CRITICAL REGION --------------------- }
  951.         IBM_Open := TRUE
  952.         END
  953.       END
  954.     END { IBM_Open } ;
  955.  
  956. BEGIN { Async_Open }
  957.   IF NOT IsOpen THEN BEGIN
  958.     BufferHead       := 0 ;
  959.     BufferTail       := 0 ;
  960.     Overflow         := FALSE ;
  961.     {$IFDEF Test}
  962.       Async_BufferUsed := 0 ;
  963.       {$ENDIF}
  964.     CASE ComputerType OF
  965.       DG1 :
  966.         IsOpen := DG1_Open( LogicalPortNum, Rate, Parity, WordSize, StopBits ) ;
  967.       IBM :
  968.         IsOpen := IBM_Open( LogicalPortNum, Rate, Parity, WordSize, StopBits ) ;
  969.       T2K :
  970.         IsOpen := FALSE
  971.       END ; { case }
  972.     Async_Open := IsOpen
  973.     END
  974.   END { Async_Open } ;
  975.  
  976.  
  977. PROCEDURE Async_Change
  978.   (     Rate          : aBpsRate ;
  979.         Parity        : aParitySetting ;
  980.         WordSize      : byte  ;
  981.         StopBits      : byte  ) ;
  982.   { Change current comm parameters }
  983.  
  984. VAR
  985.   Divisor           : word ;
  986.   LCR               : byte ;
  987.   const
  988.   DivisorTable      : ARRAY [aBpsRate] OF word
  989.     = ( $0417, $0300, $0180, $00C0, $0060, $0030, $0018, $000C ) ;
  990.  
  991.  
  992. BEGIN { Async_Change }
  993.  
  994.   Divisor := DivisorTable[Rate] ;
  995.  
  996.   CASE Parity OF
  997.     NoParity   : LCR := $00 ;
  998.     OddParity  : LCR := $08 ;
  999.     EvenParity : LCR := $18
  1000.     END { case } ;
  1001.  
  1002.   CASE WordSize OF
  1003.     5 :  LCR := (LCR or $00) ;
  1004.     6 :  LCR := (LCR or $01) ;
  1005.     7 :  LCR := (LCR or $02) ;
  1006.     8 :  LCR := (LCR or $03) ;
  1007.     else LCR := (LCR or $02)    { default - 7 data bits }
  1008.     END { case } ;
  1009.  
  1010.   CASE StopBits OF
  1011.     1 :  LCR := (LCR or $00) ;
  1012.     2 :  LCR := (LCR or $04) ;
  1013.     else LCR := (LCR or $00)    { default - 1 stop bit }
  1014.     END { case } ;
  1015.  
  1016.   {$IFDEF Test}
  1017.     writeln( '*** Async_Change ***' ) ;
  1018.     writeln( '  new LCR     : $', hex(lcr,2) ) ;
  1019.     writeln( '  new Divisor : $', hex(divisor,4) ) ;
  1020.     {$ENDIF}
  1021.   DisableInterrupts ; { --- ENTER CRITICAL REGION -------------------------- }
  1022.   port[ IBM_UART_LCR + Base] := (port[IBM_UART_LCR + Base] or $80) ;
  1023.   portw[               Base] := Divisor ;
  1024.   port[ IBM_UART_LCR + Base] := LCR ;
  1025.   EnableInterrupts    { --- EXIT CRITICAL AREA  ---------------------------- }
  1026.   END { Async_Change } ;
  1027.  
  1028.  
  1029. PROCEDURE Async_GetParams
  1030.   ( VAR Rate          : aBpsRate ;
  1031.     VAR Parity        : aParitySetting ;
  1032.     VAR WordSize      : byte  ;
  1033.     VAR StopBits      : byte  ) ;
  1034.   { Get current values of comm parameters }
  1035.  
  1036. VAR
  1037.   Divisor           : word ;
  1038.   LCR               : byte ;
  1039.  
  1040. BEGIN { Async_GetParams }
  1041.   IF ComputerType = IBM THEN BEGIN
  1042.     DisableInterrupts ; { --- ENTER CRITICAL REGION -------------------------- }
  1043.     LCR := port[IBM_UART_LCR + Base] ;
  1044.     port[IBM_UART_LCR + Base] := (port[IBM_UART_LCR + Base] or  $80) ;
  1045.     Divisor := portw[Base] ;
  1046.     port[IBM_UART_LCR + Base] := (port[IBM_UART_LCR + Base] and $7F) ;
  1047.     EnableInterrupts ;  { --- EXIT CRITICAL AREA  ---------------------------- }
  1048.     WordSize := ( LCR and $03) + 5 ;
  1049.     StopBits := ((LCR and $04) shr 2) + 1 ;
  1050.     CASE ((LCR and $18) shr 3) OF
  1051.       0, 2 : Parity := NoParity ;
  1052.       1    : Parity := OddParity ;
  1053.       3    : Parity := EvenParity
  1054.       END ; { case }
  1055.     IF Divisor = $000C THEN
  1056.       Rate := bps9600
  1057.     ELSE IF Divisor = $0018 THEN
  1058.       Rate := bps4800
  1059.     ELSE IF Divisor = $0030 THEN
  1060.       Rate := bps2400
  1061.     ELSE IF Divisor = $0060 THEN
  1062.       Rate := bps1200
  1063.     ELSE IF Divisor = $00C0 THEN
  1064.       Rate := bps600
  1065.     ELSE IF Divisor = $0180 THEN
  1066.       Rate := bps300
  1067.     ELSE IF Divisor = $0300 THEN
  1068.       Rate := bps150
  1069.     ELSE { Divisor had better = $0417 }
  1070.       Rate := bps110
  1071.     END { IBM }
  1072.   ELSE BEGIN
  1073.     WRITELN( '*** Critical Error: Async_GetParam not implemented ***' ) ;
  1074.     halt
  1075.     END 
  1076.   END { Async_GetParams } ;
  1077.  
  1078.   
  1079. BEGIN { Async4 unit body }
  1080.   InitializeUnit
  1081.   END { Async4 unit body }.
  1082.