$R-,S-,I-,D+,T+,F-,V+,B-,N-,L+ UNIT ASYNC; INTERFACE Uses Delays; (**************************** ASYNC.PAS *********************************) (* *) (* Modul for bruk av 1,2,3 el. 4 COM-porter samtidig, med interrupt *) (* bde ved sending og mottak og uavhengige ring-buffere opptil *) (* 64k for hver retning og port. *) (* *) (* Oslo, November 1987 Terje Mathisen, Norsk Hydro *) (* *) (**************************** ASYNC.PRO *********************************) CONST RX_int = 1; TX_int = 2; RLS_int = 4; MODEM_int = 8; SumOf_int =15; TYPE ComPortType = 1..4; ParityType = (No_Parity, Even_Parity, Odd_Parity, Zero_Parity, One_Parity); RS_IntSet = 0..SumOf_int; RS_BufPtrType = ^RS_BufferType; RS_BufferType = RECORD ICadr, IntNr : WORD; oldModemContrReg : BYTE; oldLevel : BYTE; oldVector : Pointer; xin : Pointer; xout, SizeX, LimitX : WORD; Tin : WORD; Tout : Pointer; SizeT, SendFirst : WORD; ShowXoffPtr : Pointer; Toggle_Xoff, RLS_user, MODEM_user : Pointer; Ctrl_P : BYTE; {0 - > default, 1..4 -> NOTIS} UseTint, HostXoff : BOOLEAN; Bufferfilled : BYTE; AutoXoff, AltXoff : BOOLEAN; Xoff1C, Xoff2C, Xon1C, Xon2C : CHAR; Line_Status, MODEM_status : BYTE; WaitTX : BOOLEAN; Int_Mask : BYTE; oldIntEnableReg : BYTE; END; VAR RS_BufPtr : ARRAY [ComPortType] OF RS_BufPtrType; RS_TimeOut : WORD; RS_Buffer : ARRAY [ComPortType] OF RS_BufferType; { Must be in data-seg! } PROCEDURE RS_MakeBuffer(Rsize,Tsize,IOaddr,SWint:WORD; com : WORD); PROCEDURE RS_Init (baudRate : LongInt; NbrOfBits, { 5|6|7|8 } StopBits: WORD; { 1|2 } Parity: ParityType; { (No_Parity, Even_Parity, Odd_Parity, Zero_Parity, One_Parity) } VAR result: BOOLEAN; com: ComPortType); { 1..4 } PROCEDURE RS_Stop(com: ComPortType); PROCEDURE RS_Start(rs_int: RS_IntSet; com: ComPortType); PROCEDURE RS_BusyRead(VAR ch:CHAR;VAR done : BOOLEAN; com : WORD); PROCEDURE RS_ReadBlock(VAR buf;max:WORD;VAR bytes:WORD;com : WORD); PROCEDURE RS_Write(ch: CHAR;VAR done : BOOLEAN; com: WORD ); PROCEDURE RS_WriteBlock(VAR buf;len: WORD;VAR bytes:WORD; com: WORD); FUNCTION RS_GetChar(VAR ch : CHAR; com : WORD): BOOLEAN; FUNCTION RS_Avail(com : WORD): WORD; FUNCTION RS_Room(com : WORD): WORD; PROCEDURE RS_Enable(com : WORD); PROCEDURE RS_WriteFirst(ch:CHAR;com:WORD); PROCEDURE RS_ClrBuffer(com: WORD); PROCEDURE RS_Set_TX_Int(rs_int : RS_IntSet; com : WORD); FUNCTION RS_Empty(com : WORD) : BOOLEAN; PROCEDURE RS_Break(ms : WORD;com : WORD); PROCEDURE RS_StopLink(com : WORD); PROCEDURE RS_StartLink(com : WORD); PROCEDURE RS_StopAll; IMPLEMENTATION CONST LineContrReg = 3; { to specify format of transmitted data } LowBaudRateDiv = 0; { lower byte of divisor to select baud rate } HighBaudRateDiv = 1; { higher byte of divisor } LineStatusReg = 5; { holds status info on the data transfer } ReceiverReg = 0; { received CHAR is in this register } TransmitReg = 0; { CHAR to send is put in this reg } IntEnableReg = 1; { to enable the selected interrupt } IntIdentReg = 2; ModemContrReg = 4; { controls the interface to a modem } PROCEDURE GetAlignMem(VAR p : Pointer; size : WORD); VAR temp : ^BYTE; BEGIN REPEAT GetMem(p,size); IF Ofs(p^) = 0 THEN Exit; FreeMem(p,size); New(temp); UNTIL FALSE; END; PROCEDURE RS_MakeBuffer(Rsize, Tsize, IOaddr, SWint, com: WORD); CONST PortTab : ARRAY [ComPortType] OF WORD = ($3F8,$2F8,$3E8,$2E8); IntTab : ARRAY [ComPortType] OF BYTE = (12,11,12,11); VAR c, c0, c1 : WORD; BEGIN IF Rsize + Tsize > MemAvail - $100 THEN BEGIN Halt(1); END; IF com = 0 THEN BEGIN c0 := 1; c1 := 4; END ELSE BEGIN IF com > 4 THEN Halt(1); c0 := com; c1 := com; END; FOR c := c0 TO c1 DO WITH RS_Buffer[c] DO BEGIN IF (com = 0) AND (c > 1) THEN RS_Buffer[c] := RS_Buffer[1] ELSE BEGIN IF Rsize > 0 THEN BEGIN GetAlignMem(xin,Rsize); SizeX := Rsize; LimitX := Rsize DIV 8; END; IF Tsize > 0 THEN BEGIN GetAlignMem(Tout,Tsize); SizeT := Tsize; END; END; IF IOaddr = 0 THEN ICadr := PortTab[c] ELSE ICadr := IOaddr; IF SWint = 0 THEN IntNr := IntTab[c] ELSE IntNr := SWint; { Disse variablene er nullstilt allerede! xin := 0; xout := 0; SendFirst := 0; tin := 0; tout := 0; Ctrl_P := 0; UseTint := FALSE; Sending := FALSE; Receiving := FALSE; HostXoff := FALSE; BufferFilled := 0; AltXoff := FALSE; ShowXoffPtr := NIL; Toggle_Xoff := 0; RLS_user := 0; MODEM_user := 0; } {Default to use XON/XOFF!} AutoXoff := TRUE; Xoff1C := ^S; Xon1C := ^Q; END; END; PROCEDURE RS_Init (baudRate : LongInt; NbrOfBits, { 5|6|7|8 } StopBits: WORD; { 1|2 } Parity: ParityType; { (No_Parity, Even_Parity, Odd_Parity, Zero_Parity, One_Parity) } VAR result: BOOLEAN; com: ComPortType); { 1..4 } CONST ParityTab : ARRAY [ParityType] OF BYTE = (0,$18,$08,$38,$28); VAR divisor : WORD; parameters: BYTE; BEGIN (* Init *) result := FALSE; WITH RS_Buffer[com] DO BEGIN IF Xin = NIL THEN BEGIN {No buffer allocated!} Halt(1); END; (* load the divisor of the baud rate generator: *) IF baudrate < 1 THEN Exit; divisor := (115200 + (baudrate DIV 2)) DIV baudrate; Port[ICadr+LineContrReg] := $80; Port[ICadr+HighBaudRateDiv] := Hi(divisor); Port[ICadr+LowBaudRateDiv] := Lo(divisor); (* prepare the parameters: *) parameters := ParityTab[Parity]; IF stopBits = 2 THEN parameters := parameters + 4 ELSE IF stopBits <> 1 THEN Exit; IF (nbrOfBits < 5) OR (nbrOfBits > 8) THEN Exit; Port[ICadr+LineContrReg] := parameters + (nbrOfBits - 5); (* Disable Interrupts: *) Port[ICadr+IntEnableReg] := 0; result := TRUE; END; END { Init }; CONST I8259ContrWord1 = $21; (* Interrupt controller, Operation Control Word 1 *) (************************* ASSEMBLER ROUTINES FOR MAX SPEED ****************) PROCEDURE RS_Com1Int; EXTERNAL; PROCEDURE RS_Com2Int; EXTERNAL; PROCEDURE RS_Com3Int; EXTERNAL; PROCEDURE RS_Com4Int; EXTERNAL; PROCEDURE RS_BusyRead(VAR ch:CHAR;VAR done : BOOLEAN; com : WORD); EXTERNAL; PROCEDURE RS_ReadBlock(VAR buf;max:WORD; VAR bytes : WORD;com : WORD);EXTERNAL; PROCEDURE RS_Write(ch: CHAR;VAR done : BOOLEAN; com: WORD ); EXTERNAL; PROCEDURE RS_WriteBlock(VAR buf;len: WORD; VAR bytes : WORD; com: WORD);EXTERNAL; FUNCTION RS_GetChar(VAR ch : CHAR; com : WORD): BOOLEAN; EXTERNAL; FUNCTION RS_Avail(com : WORD): WORD; EXTERNAL; FUNCTION RS_Room(com : WORD): WORD; EXTERNAL; PROCEDURE RS_Enable(com : WORD); EXTERNAL; PROCEDURE RS_WriteFirst(ch:CHAR;com:WORD);EXTERNAL; {$L ASYNC.OBJ} (***************************************************************************) VAR vect_tab : ARRAY [0..255] OF Pointer ABSOLUTE 0:0; PROCEDURE Disable; Inline($FA); PROCEDURE Enable; Inline($FB); PROCEDURE GetVector(vnr : WORD; VAR vector : Pointer); BEGIN vector := vect_tab[vnr]; END; {GetVector} PROCEDURE SetVector(vnr : WORD; vector : Pointer); BEGIN Disable; vect_tab[vnr] := vector; Enable; END; {PutVector} PROCEDURE RS_Start(rs_int : RS_IntSet; com: ComPortType); VAR adr : Pointer; mask, tempSet : BYTE; dummy : WORD; ch : CHAR; ok : BOOLEAN; BEGIN WITH RS_Buffer[com] DO IF OldVector = NIL THEN BEGIN (* enable interrupts in the interrupt controller (8259): *) tempSet := Port[I8259ContrWord1]; (* set the interrupt vector *) GetVector(IntNr,OldVector); CASE com OF 1 : adr := @RS_Com1int; 2 : adr := @RS_Com2int; 3 : adr := @RS_Com3int; 4 : adr := @RS_Com4int; END; SetVector(IntNr,adr); mask := 1 Shl (IntNr - 8); oldLevel := tempSet AND mask; DISABLE; Port[I8259ContrWord1] := tempSet AND NOT mask; dummy := Port[ICadr+IntIdentReg] + Port[ICadr+LineStatusReg] + Port[ICadr+ModemContrReg] + Port[ICadr+ReceiverReg]; (* clear the controller *) WORD(xin) := 0; xout := 0; SendFirst := 0; tin := 0; WORD(tout) := 0; HostXoff := FALSE; WaitTX := FALSE; AutoXoff := TRUE; BufferFilled := 0; Line_Status := 0; MODEM_Status := 0; tempSet := Port[ICadr+ModemContrReg]; oldModemContrReg := tempSet AND 11; { DTR and RTS } Port[ICadr+ModemContrReg] := tempSet OR 11; Int_Mask := rs_int; oldIntEnableReg := Port[ICadr+IntEnableReg]; Port[ICadr+IntEnableReg] := rs_int; UseTint := (TX_int AND rs_int) <> 0; ENABLE; END; dummy := 50; REPEAT RS_BusyRead(ch,ok,com); { Remove pending int's } Dec(dummy); UNTIL NOT ok OR (dummy = 0); END {RS_Start}; PROCEDURE RS_Stop(com: ComPortType); BEGIN WITH RS_Buffer[com] DO IF OldVector <> NIL THEN BEGIN DISABLE; (* restore old mask in 8259: *) Port[I8259ContrWord1] := Port[I8259ContrWord1] OR oldLevel; (* disable interrupts in 8250: *) Port[ICadr+IntEnableReg] := oldIntEnableReg; (* restore modem control register in 8250: *) Port[ICadr+ModemContrReg] := (Port[ICadr+ModemContrReg] AND 244) OR oldModemContrReg; ENABLE; (* restore the old interrupt vector *) SetVector(IntNr,OldVector); OldVector := NIL; END; END {RS_Stop}; (* PROCEDURE RS_Read(VAR ch: CHAR;com: WORD ); VAR done : BOOLEAN; BEGIN REPEAT RS_BusyRead (ch, done, com); UNTIL done; END {RS_Read}; *) PROCEDURE RS_ClrBuffer(com: WORD); BEGIN WITH RS_Buffer[com] DO BEGIN Disable; WORD(xin) := 0; xout := 0; tin := 0; WORD(tout) := 0; SendFirst := 0; Enable; END; END; {ClrBuffer} PROCEDURE RS_Set_TX_Int(rs_int : RS_IntSet; com : WORD); BEGIN WITH RS_Buffer[com] DO BEGIN Disable; tin := 0; WORD(tout) := 0; SendFirst := 0; Int_Mask := rs_int; Port[ICadr+IntEnableReg] := rs_int; UseTint := (TX_int AND rs_int) <> 0; Enable; END; END; {RS_Set_TX_Int} FUNCTION RS_Empty(com : WORD) : BOOLEAN; VAR ch : CHAR; ok : BOOLEAN; BEGIN WITH RS_Buffer[com] DO RS_Empty := WORD(xin) = xout; END; {EmptyBuffer} PROCEDURE RS_Break(ms : WORD;com : WORD); VAR oldreg : BYTE; BEGIN WITH RS_Buffer[com] DO BEGIN WaitTX := TRUE; WHILE Port[ICadr+LineStatusReg] AND 32 = 0 DO ; { wait for no traffic } oldreg := Port[ICadr+LineContrReg]; Port[ICadr+LineContrReg]:= oldreg OR 64; Delay(ms); Port[ICadr+LineContrReg] := OldReg; Delay(250); WaitTX := FALSE; IF NOT HostXoff THEN RS_Enable(com); END; END; {RS_Break} PROCEDURE RS_StopLink(com : WORD); VAR bf : BYTE; BEGIN WITH RS_Buffer[com] DO IF AutoXoff THEN BEGIN Disable; bf := BufferFilled; BufferFilled := BufferFilled OR 2; Enable; IF bf = 0 THEN BEGIN RS_WriteFirst(Xoff1C,com); Delay(10); END; END; END; PROCEDURE RS_StartLink(com : WORD); VAR bf : BYTE; BEGIN WITH RS_Buffer[com] DO IF AutoXoff THEN BEGIN Disable; BufferFilled := BufferFilled AND 253; bf := BufferFilled; Enable; IF bf = 0 THEN BEGIN RS_WriteFirst(Xon1C,com); END; END; END; VAR SaveExit : Pointer; PROCEDURE RS_StopAll; BEGIN RS_Stop(1); RS_Stop(2); RS_Stop(3); RS_Stop(4); ExitProc := SaveExit; END; BEGIN FillChar(RS_Buffer,SizeOf(RS_Buffer),#0); RS_BufPtr[1] := Addr(RS_Buffer[1]); RS_BufPtr[2] := Addr(RS_Buffer[2]); RS_BufPtr[3] := Addr(RS_Buffer[3]); RS_BufPtr[4] := Addr(RS_Buffer[4]); RS_TimeOut := 0; SaveExit := ExitProc; ExitProc := @RS_StopAll; END.