home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power-Programmierung
/
CD1.mdf
/
magazine
/
drdobbs
/
1988
/
02
/
lindley
/
lindley.ls5
< prev
Wrap
Text File
|
1987-08-16
|
38KB
|
1,624 lines
{************************************************}
{*** ***}
{*** Turbo Pascal ***}
{*** Serial Protocol Analyzer ***}
{*** written by ***}
{*** Craig A. Lindley ***}
{*** ***}
{*** Ver: 2.0 Last update: 08/15/87 ***}
{*** ***}
{************************************************}
{$K-,U-,C-,G30,D-}
{ ------- Notes on compiler directives --------- }
{ K- No stack checking otherwise multitasking}
{ kernal will not run. }
{ U-,C- Turn off user break checks to speed }
{ screen I/O. }
{ G30,D- Buffer standard input device (keyboard) }
{ and disable device checks. This makes }
{ keyboard respond much faster and be }
{ buffered. }
CONST
HexDigits: STRING[16] = '0123456789ABCDEF';
AsciiStrs: ARRAY[0..31] OF STRING[3] =
('Nul','Soh','Stx','Etx','Eot','Enq','Ack','Bel',
'Bs' ,'Ht' ,'Lf' ,'Vt' ,'Ff' ,'Cr' ,'So' ,'Si' ,
'Dle','Dc1','Dc2','Dc3','Dc4','Nak','Syn','Etb',
'Can','Em' ,'Sub','Esc','Fs' ,'Gs' ,'Rs' ,'Vs');
SerialDataFifoSize = 2000; {serial data fifo size}
DisplayFifoSize = 3000; {display fifo size}
{$I multi.pas} {include the}
{multitasking kernel}
TYPE
FullString = STRING[255];
Str80 = STRING[80];
Str3 = STRING[3];
DataRec = RECORD
Data,
Status: Byte;
END;
DisplayRec = RECORD
Tag: (FromCOM1,FromCOM2);
DR: DataRec;
END;
{This fifo overhead structure is the same for}
{all fifo types regardless of the items to be}
{stored in the fifo. Two types are fifos are }
{defined.}
OverHead = RECORD {fifo overhead data}
{structure}
Count, {# of items in fifo}
Inptr, {ptr to where items are}
{stored}
Outptr: Integer; {ptr to where items are}
{fetched}
NotEmpty, {ptrs to waiting tasks}
NotFull: tcbptr;
END;
{definition of a serial data fifo}
SerialDataFifo = RECORD
Ovd: OverHead; {fifo overhead}
Data: ARRAY[1..SerialDataFifoSize]
OF DataRec; {fifo data area}
END;
{definition of display fifo}
DisplayFifoType = RECORD
Ovd: OverHead;
Data: ARRAY[1..DisplayFifoSize]
OF DisplayRec;
END;
DisplayTriggerType = (Before,After);
VAR
regs: register_type;
{storage for the original IRQ3 & 4 code segment}
{and instruction pointer addresses}
OldIRQ3_CS,
OldIRQ3_IP,
OldIRQ4_CS,
OldIRQ4_IP: Integer;
{UART status storage variables}
OldCOM1_Status,
OldCOM2_Status,
COM1_Status,
COM2_Status: Byte;
{Display formatting boolean flags}
UpdateScreenStatus,
AsciiDisplay,
HandShakeDisplay,
AddSpace,
{Data display boolean flags}
{If true the data from the specified COM port}
{is tagged and then moved into the display fifo}
COM1_Display_Data,
COM2_Display_Data,
{Data Acquisition boolean flags}
{Controls acquisiton of data by the Interrupt}
{Serivce Routines. If true then serial data is}
{stored by the ISR.}
COM1_Data_Acquire,
COM2_Data_Acquire,
{Variables used for the triggering function}
TriggerEnabled,
COM1_Is_Triggered,
COM2_Is_Triggered: Boolean;
TriggerPattern: Integer;
TriggerMode: DisplayTriggerType;
{Fifo declarations}
COM1_Input_Fifo,
COM2_Input_Fifo,
COM1_Output_Fifo,
COM2_Output_Fifo: SerialDataFifo;
DisplayFifo: DisplayFifoType;
{Screen formatting strings built at run time to}
{format the screen.}
Line1, Line2,
Line3, Line4,
Line5, Line6,
Line24: Str80;
{Cursor storage for DisplayCOMData procedure}
OldXPos,
OldYPos: Integer;
{Lock for screen control}
ScreenAccess: Semaphore;
{************ Begin FIFO Procedures ************}
PROCEDURE Init_Fifos;
PROCEDURE Initialize_fifo(VAR o:OverHead);
{Initialize a fifo's overhead data structure.}
{This procedure will work with any type fifo.}
{This makes the fifo appear empty.}
BEGIN
o.Count := 0; {count is empty}
o.Inptr := 1; {ptrs to 1st entry}
o.Outptr := 1; {put in and take out at}
{entry 1}
o.NotEmpty :=NIL; {signals to nil}
o.NotFull :=NIL;
END;
BEGIN
Initialize_fifo(COM1_Input_fifo.Ovd);
Initialize_fifo(COM1_Output_fifo.Ovd);
Initialize_fifo(COM2_Input_fifo.Ovd);
Initialize_fifo(COM2_Output_fifo.Ovd);
Initialize_fifo(DisplayFifo.Ovd);
END;
PROCEDURE PutSerialData (d:DataRec;
VAR f:SerialDataFifo);
BEGIN
WITH f.Ovd DO
BEGIN {check if fifo full}
IF Count = SerialDataFifoSize THEN
BEGIN {if so go to sleep}
waitfor := addr (NotFull);
wait;
END; {when not full add}
Count:=Count+1; {one more to count}
f.data[Inptr]:=d; {store the data record}
Inptr:=Inptr+1; {bump input pointer}
IF Inptr > SerialDataFifoSize THEN
Inptr:=1; {wrap ptr if necessary}
{if waiters for this fifo wake them}
IF NotEmpty <> NIL THEN
send(NotEmpty);
END;
END;
PROCEDURE GetSerialData (VAR f:SerialDataFifo;
VAR d:DataRec);
BEGIN
WITH f.Ovd DO
BEGIN {check if fifo empty}
IF Count = 0 THEN
BEGIN {if so go to sleep}
waitfor := addr (NotEmpty);
wait;
END;
{when data is available}
Count:=Count-1; {one less to count}
d :=f.data[Outptr]; {get the data record}
Outptr:=Outptr+1;{bump output pointer}
IF Outptr > SerialDataFifoSize THEN
Outptr:=1; {wrap ptr if necessary}
{if waiters for this fifo wake them}
IF NotFull <> NIL THEN
send(NotFull);
END;
END;
PROCEDURE PutDisplayData (d:DisplayRec;
VAR f:DisplayFifoType);
BEGIN
WITH f.Ovd DO
BEGIN {check if fifo full}
IF Count = DisplayFifoSize THEN
BEGIN {if so go to sleep}
waitfor := addr (NotFull);
wait;
END; {when not full add}
Count:=Count+1; {one more to count}
f.data[Inptr]:=d; {store the data record}
Inptr:=Inptr+1; {bump input pointer}
IF Inptr > DisplayFifoSize THEN
Inptr:=1; {wrap ptr if necessary}
{if waiters for this fifo wake them}
IF NotEmpty <> NIL THEN
send(NotEmpty);
END;
END;
PROCEDURE GetDisplayData (VAR f:DisplayFifoType;
VAR d:DisplayRec);
BEGIN
WITH f.Ovd DO
BEGIN {check if fifo empty}
IF Count = 0 THEN
BEGIN {if so go to sleep}
waitfor := addr (NotEmpty);
wait;
END;
{when data is available}
Count:=Count-1; {one less to count}
d :=f.data[Outptr]; {get the data record}
Outptr:=Outptr+1;{bump output pointer}
IF Outptr > DisplayFifoSize THEN
Outptr:=1; {wrap ptr if necessary}
{if waiters for this fifo wake them}
IF NotFull <> NIL THEN
send(NotFull);
END;
END;
{Include the menuing system}
{$I menu.pas}
{Include the serial procedures}
{$I serial.pas}
{********* Additional Serial Procedures *********}
PROCEDURE SetBreak (PortAddr:Integer; State:Boolean);
{Controls the break generation for the specified}
{COM port.}
VAR
Temp: Byte;
BEGIN
{Read the LineControl reg of the 8250}
{either set or reset the break bit D6}
{as specified. Write the new reg value}
{back to the port}
Temp := port[PortAddr+LineControl];
IF State THEN
Temp := Temp OR $40
ELSE
Temp := Temp AND $BF;
port[PortAddr+LineControl] := Temp;
END;
PROCEDURE MakeHandShake (PortAddr:Integer;
Status:Byte);
VAR
Temp: Byte;
BEGIN
{Set the bits in the ModemControl reg of the}
{specified COM port according to the bits}
{of the variable Status. This procedure is}
{used to always force the handshake lines of}
{the COM ports to agree}
Temp := port[PortAddr+ModemControl];
IF (Status AND BrkBit) <> 0 THEN
SetBreak(PortAddr,True)
ELSE
SetBreak(PortAddr,False);
IF (Status AND CTSBit) <> 0 THEN
Temp := Temp OR $02
ELSE
Temp := Temp AND $FD;
IF (Status AND DSRBit) <> 0 THEN
Temp := Temp OR $01
ELSE
Temp := Temp AND $FE;
{Store the new value of the ModemControl}
{register back}
port[PortAddr+ModemControl] := Temp;
END;
{************** Display Procedures *************}
PROCEDURE BuildDisplay;
{Setup the main SPA display screen}
{The OldCOM?_Status variables are consciencely}
{clobbered to force the screen to be updated}
{after it is built or rebuilt after the menus}
{are displayed}
BEGIN
OldCOM1_Status := $FF;
OldCOM2_Status := $FF;
WriteStringAt(Line1,High,1,1);
WriteStringAt(Line2,Low,1,2);
WriteStringAt(Line3,Low,1,3);
WriteStringAt(Line4,Low,1,4);
WriteStringAt(Line5,Low,1,5);
WriteStringAt(Line6,Low,1,6);
WriteStringAt(Line24,Rev,1,24);
END;
PROCEDURE DisplayHandShakeStatus (PortAddr:Integer;
InStatus,
OutStatus:Byte);
CONST
StatLineNum = 3; {screen line of line status}
InLineNum = 4; {screen line of in handshake}
{lines}
OutLineNum = 5; {screen line of out handshake}
{lines}
CDOffset = 11; {offsets on a screen line for}
RIOffset = 16; {the individual items to be}
DSROffset = 22; {displayed}
CTSOffset = 28;
BRKOffset = 13;
FEOffset = 18;
PEOffset = 23;
OROffset = 28;
DTROffset = 12;
RTSOffset = 22;
VAR
DisplayOffset: Integer;
Ind: Char;
BEGIN
{Where an item is displayed is determined in}
{part by which COM status is being displayed}
{COM1's offset is 0 whereas COM2's offset is}
{51 character positions.}
IF PortAddr = COM1 THEN
DisplayOffset := 0
ELSE
DisplayOffset := 51;
IF (InStatus AND BrkBit) <> 0 THEN
Ind := 'B'
ELSE
Ind := '-';
WriteStringAt(Ind,High,
BRKOffset+DisplayOffset,
StatLineNum);
IF (InStatus AND FEBit) <> 0 THEN
Ind := 'E'
ELSE
Ind := '-';
WriteStringAt(Ind,High,
FEOffset+DisplayOffset,
StatLineNum);
IF (InStatus AND PEBit) <> 0 THEN
Ind := 'E'
ELSE
Ind := '-';
WriteStringAt(Ind,High,
PEOffset+DisplayOffset,
StatLineNum);
IF (InStatus AND ORBit) <> 0 THEN
Ind := 'E'
ELSE
Ind := '-';
WriteStringAt(Ind,High,
OROffset+DisplayOffset,
StatLineNum);
IF (InStatus AND CDBit) <> 0 THEN
Ind := 'M'
ELSE
Ind := 'S';
WriteStringAt(Ind,High,
CDOffset+DisplayOffset,
InLineNum);
IF (InStatus AND RIBit) <> 0 THEN
Ind := 'M'
ELSE
Ind := 'S';
WriteStringAt(Ind,High,
RIOffset+DisplayOffset,
InLineNum);
IF (InStatus AND DSRBit) <> 0 THEN
Ind := 'M'
ELSE
Ind := 'S';
WriteStringAt(Ind,High,
DSROffset+DisplayOffset,
InLineNum);
IF (InStatus AND CTSBit) <> 0 THEN
Ind := 'M'
ELSE
Ind := 'S';
WriteStringAt(Ind,High,
CTSOffset+DisplayOffset,
InLineNum);
{Note: The OTHER COM port is consulted about}
{the output status. DTR of this port should}
{equal DSR of other port. RTS of this port}
{should equal CTS of other port}
IF (OutStatus AND DSRBit) <> 0 THEN
Ind := 'M'
ELSE
Ind := 'S';
WriteStringAt(Ind,High,
DTROffset+DisplayOffset,
OutLineNum);
IF (OutStatus AND CTSBit) <> 0 THEN
Ind := 'M'
ELSE
Ind := 'S';
WriteStringAt(Ind,High,
RTSOffset+DisplayOffset,
OutLineNum);
END;
PROCEDURE DisplayBufferStatus;
VAR
PerCentage: Integer;
PerCentStr: Str80;
BEGIN
PerCentage := Round((COM1_Input_Fifo.Ovd.Count/
SerialDataFifoSize) * 100);
Str(PerCentage:2,PerCentStr);
WriteStringAt(PerCentStr + '%',High,12,2);
PerCentage := Round((COM1_Output_Fifo.Ovd.Count/
SerialDataFifoSize) * 100);
Str(PerCentage:2,PerCentStr);
WriteStringAt(PerCentStr + '%',High,26,2);
PerCentage := Round((COM2_Input_Fifo.Ovd.Count/
SerialDataFifoSize) * 100);
Str(PerCentage:2,PerCentStr);
WriteStringAt(PerCentStr + '%',High,63,2);
PerCentage := Round((COM2_Output_Fifo.Ovd.Count/
SerialDataFifoSize) * 100);
Str(PerCentage:2,PerCentStr);
WriteStringAt(PerCentStr + '%',High,77,2);
PerCentage := Round((DisplayFifo.Ovd.Count/
DisplayFifoSize) * 100);
Str(PerCentage:2,PerCentStr);
WriteStringAt(PerCentStr + '%',High,47,5);
END;
FUNCTION FormatCharAscii (Num:Integer) : Str3;
BEGIN
IF Num < ORD(' ') THEN
FormatCharAscii := AsciiStrs[Num]
ELSE
FormatCharAscii := chr(Num);
END;
FUNCTION FormatCharHex (Num:Integer) : Str3;
BEGIN
FormatCharHex :=
HexDigits[(Num SHR 4) AND $000F +1 ] +
HexDigits[(Num AND $000F) + 1];
END;
FUNCTION DisplayData (D:DisplayRec) : Integer;
VAR
VideoAttrib: AttribType;
Temp: STRING[7];
BEGIN
WITH D DO
BEGIN
{If data from either channel should be}
{displayed then...}
IF (((Tag = FromCOM1) AND COM1_Display_Data) OR
((Tag = FromCOM2) AND COM2_Display_Data)) THEN
BEGIN
{set video attribute depending upon}
{which COM channel it is from}
IF Tag = FromCOM1 THEN
VideoAttrib := Low
ELSE
VideoAttrib := Rev;
{choose ASCII or Hex format}
IF AsciiDisplay THEN
Temp := FormatCharAscii(DR.Data)
ELSE
Temp := FormatCharHex(DR.Data);
IF HandShakeDisplay THEN
Temp := Temp + ':$'+
FormatCharHex(DR.Status);
{write serial data string to display}
WriteString(Temp,VideoAttrib);
{if formatting with spaces}
IF AddSpace THEN
BEGIN
{output space to display and add}
{a space to string for length calc}
write(' ');
Temp := Temp + ' ';
END;
{ret length of formatted item}
DisplayData := length(Temp);
END
ELSE
{if no data ret 0 length}
DisplayData := 0;
END;
END;
{*********** Miscellaneous Procedures ***********}
PROCEDURE Init_Program;
{Perform default initialization for protocol}
{analyzer program}
BEGIN
Init_Fifos;
COM1_Data_Acquire := True;
COM2_Data_Acquire := True;
COM1_Display_Data := True;
COM2_Display_Data := True;
TriggerEnabled := False;
COM1_Is_Triggered := False;
COM2_Is_Triggered := False;
AsciiDisplay := True;
HandShakeDisplay := False;
AddSpace := True;
COM1_Status := 0;
COM2_Status := 0;
{set serial com defaults 1200 baud, 8 bit word,
{1 stopbit, no parity}
{both COM ports always set the same}
COM_Rate := 8; {1200 baud}
COM_StopBits := 1;
COM_DataBits := 8;
COM_Parity := none;
SetNewCOMParameter; {apply the parameters}
END;
PROCEDURE VerifyHardware;
CONST
TestByte = $5A; {try to store and retrieve}
{this value}
SafeByte = $03; {set default 8 bits 1 stop}
BEGIN
ClrScr;
{just in case of reentry}
Disable_Serial_Devices;
WriteString(' Serial Protocol Analyzer Hardware Verification Check '
,Rev);
WriteStringAt('Checking COM1',HighBlink,2,3);
port[COM1+LineControl] := TestByte;
delay(700);
IF port[COM1+LineControl] <> TestByte THEN
BEGIN
WriteStringAt('COM1 Hardware Bad or Missing',
Rev,2,3);
Halt;
END;
port[COM1+LineControl] := SafeByte;
WriteStringAt('COM1 Hardware Verified',
Low,2,3);
delay(700);
WriteStringAt('Checking COM2',HighBlink,
2,4);
port[COM2+LineControl] := TestByte;
delay(500);
IF port[COM2+LineControl] <> TestByte THEN
BEGIN
WriteStringAt('COM2 Hardware Bad or Missing',
Rev,2,4);
Halt;
END;
port[COM2+LineControl] := SafeByte;
WriteStringAt('COM2 Hardware Verified',Low,2,4);
delay(2000);
ClrScr;
END;
{****** Menu Command Processing Procedure *******}
{Declared forward previously}
PROCEDURE ProcessCmd;
VAR
HexStr: Str3;
HexChar,
ErrCode: Integer;
Ch: Char;
BEGIN
IF cmd_code <> 0 THEN
BEGIN
CASE cmd_code OF
1: ExitMenu := True;
2: BEGIN {300 baud}
COM_Rate := 6;
SetNewCOMParameter;
END;
3: BEGIN {600 baud}
COM_Rate := 7;
SetNewCOMParameter;
END;
4: BEGIN {1200 baud}
COM_Rate := 8;
SetNewCOMParameter;
END;
5: BEGIN {2400 baud}
COM_Rate := 11;
SetNewCOMParameter;
END;
6: BEGIN {4800 baud}
COM_Rate := 13;
SetNewCOMParameter;
END;
7: BEGIN {9600 baud}
COM_Rate := 15;
SetNewCOMParameter;
END;
8: BEGIN {1 stop bit}
COM_StopBits := 1;
SetNewCOMParameter;
END;
9: BEGIN {2 stop bit}
COM_StopBits := 2;
SetNewCOMParameter;
END;
10: BEGIN {5 bit word}
COM_DataBits := 5;
SetNewCOMParameter;
END;
11: BEGIN {6 bit word}
COM_DataBits := 6;
SetNewCOMParameter;
END;
12: BEGIN {7 bit word}
COM_DataBits := 7;
SetNewCOMParameter;
END;
13: BEGIN {8 bit word}
COM_DataBits := 8;
SetNewCOMParameter;
END;
14: BEGIN {Odd Parity}
COM_Parity := Odd;
SetNewCOMParameter;
END;
15: BEGIN {Even Parity}
COM_Parity := Even;
SetNewCOMParameter;
END;
16: BEGIN {No Parity}
COM_Parity := None;
SetNewCOMParameter;
END;
17: BEGIN {Display COM1 only}
COM1_Display_Data := True;
COM2_Display_Data := False;
END;
18: BEGIN {Display COM2 only}
COM1_Display_Data := False;
COM2_Display_Data := True;
END;
19: BEGIN {Display both COM1 and COM2}
COM1_Display_Data := True;
COM2_Display_Data := True;
END;
20: BEGIN {COM1 is triggered}
GoToXY(1,25);
ClrEol;
COM1_Is_Triggered := True;
COM2_Is_Triggered := False;
WriteStringAt('COM1 awaiting trigger',
HighBlink,3,25);
END;
21: BEGIN {COM2 is triggered}
GoToXY(1,25);
ClrEol;
COM1_Is_Triggered := False;
COM2_Is_Triggered := True;
WriteStringAt('COM2 awaiting trigger',
HighBlink,3,25);
END;
22: BEGIN {Input trigger pattern}
GoToXY(1,MenuLine4); {position cursor}
ClrEol; {clear line}
WriteString(
'Input trigger pattern as two hex digits: ',
High);
{initialize str with hex prefix}
HexStr := '$';
FOR HexChar := 1 TO 2 DO
BEGIN
REPEAT
{get a char}
Ch := upcase(Char(GetKey));
{loop until valid char is input}
UNTIL pos(Ch,HexDigits) <> 0;
{display to user}
write(Ch);
{add to hex string}
HexStr := HexStr + Ch;
END;
{convert hex to int}
Val(HexStr,TriggerPattern,ErrCode);
GoToXY(28,25);
Write('Trigger Pattern: ',
FormatCharHex(TriggerPattern));
END;
23: BEGIN {Display BEFORE Trigger mode}
TriggerMode := Before;
WriteStringAt('Mode: Display Before Trigger'
,Low,51,25);
IF COM1_Is_Triggered THEN
{start with displaying data}
COM1_Display_Data := True
ELSE
IF COM2_Is_Triggered THEN
COM2_Display_Data := True
ELSE
WriteStringAt('Mode Error -- Select channel'
,HighBlink,51,25);
END;
24: BEGIN {Display AFTER Trigger mode}
TriggerMode := After;
WriteStringAt('Mode: Display After Trigger'
,Low,51,25);
IF COM1_Is_Triggered THEN
{start without displaying data}
COM1_Display_Data := False
ELSE
IF COM2_Is_Triggered THEN
COM2_Display_Data := False
ELSE
WriteStringAt('Mode Error -- Select channel'
,HighBlink,51,25);
END;
25: TriggerEnabled := True; {enable trigger}
26: BEGIN {Stop Triggering}
TriggerEnabled := False;
{stop triggering for both channels}
COM1_Is_Triggered := False;
COM2_Is_Triggered := False;
{start data display for both channels}
COM1_Display_Data := True;
COM2_Display_Data := True;
GoToXY(3,25);
ClrEol;
WriteString('Triggering Disabled',Low);
END;
27: BEGIN {Ascii Normal Data Display}
AsciiDisplay := True;
HandShakeDisplay := False;
END;
28: BEGIN {Ascii with handshake Data Display}
AsciiDisplay := True;
HandShakeDisplay := True;
END;
29: BEGIN {Hex Normal Data Display}
AsciiDisplay := False;
HandShakeDisplay := False;
END;
30: BEGIN {Hex with handshake Data Display}
AsciiDisplay := False;
HandShakeDisplay := True;
END;
31: BEGIN {Toggle adding spaces to display}
AddSpace := NOT AddSpace;
END;
32: BEGIN {Start data acquire}
COM1_Data_Acquire := True;
COM2_Data_Acquire := True;
END;
33: BEGIN {Stop data acquire}
COM1_Data_Acquire := False;
COM2_Data_Acquire := False;
END;
34: BEGIN {Clear the display}
{claim the screen}
Alloc(ScreenAccess);
ClrScr;
DrawMenuFrame;
{set cursor postion to home}
OldXPos := 1;
OldYPos := 1;
Dealloc(ScreenAccess);
END;
35: Init_Program; {reset the analyzer}
36: BEGIN {End the analyzer program}
ExitMenu := True;
ExitProgram := True;
END;
END;
END;
END;
{************ Begin Task Procedures *************}
PROCEDURE ProcessKeysTask;
VAR
Ch: Char;
Done,
OldCOM1Flag,
OldCOM2Flag: Boolean;
BEGIN
Done := False;
REPEAT
{read key if available else yield}
Ch := Char(GetKey);
CASE Ch OF
Esc: BEGIN {if key is Esc}
{display and process the menu}
Done := DoMenu;
{rebuild display when finished}
BuildDisplay;
END;
Sp: BEGIN {if space bar}
{save old state}
OldCOM1Flag := COM1_Display_Data;
OldCOM2Flag := COM2_Display_Data;
{stop filling of the display fifo so}
{it does not overflow durin pause}
{turn off display data both channels}
COM1_Display_Data := False;
COM2_Display_Data := False;
GoToXY(1,24);
ClrEol;
GoToXY(23,24);
WriteString(
'Press <Enter> to restart the display'
,HighBlink);
REPEAT
{loop until Cr starts display again}
Ch := Char(GetKey);
UNTIL Ch = Cr;
{restore previous display status}
COM1_Display_Data := OldCOM1Flag;
COM2_Display_Data := OldCOM2Flag;
BuildDisplay; {rebuild display}
END;
END;
UNTIL Done;
END;
PROCEDURE MoveCOM1Data;
{Move data from the COM1 input buffer to the}
{COM2 output buffer and to the display buffer}
{if enabled}
VAR
Sd: DataRec;
Dd: DisplayRec;
BEGIN
REPEAT
{yield if no data to move}
WHILE COM1_Input_Fifo.Ovd.Count = 0 DO
yield;
{when data is available move it}
{turn ints off, read data, turn ints on}
INLINE($FA);
GetSerialData(COM1_Input_Fifo,Sd);
INLINE($FB);
{store in COM2 output fifo}
PutSerialData(Sd,COM2_Output_Fifo);
{if a trigger is enabled}
IF COM1_Is_Triggered AND
TriggerEnabled THEN
BEGIN
{and a match is found}
IF Sd.Data = TriggerPattern THEN
{indicate match & disable further matches}
{trigger is single shot event}
BEGIN
TriggerEnabled := False;
WriteStringAt(' COM1 Triggered '
,Low,3,25);
{If displaying before trigger}
{then stop data display}
IF TriggerMode = Before THEN
COM1_Display_Data := False
ELSE
{start the data display}
COM1_Display_Data := True;
END;
END;
{if we are displaying data}
IF COM1_Display_Data THEN
{move data to the display fifo also}
BEGIN
{tagged from this COM device}
Dd.DR := Sd;
Dd.Tag := FromCOM1;
PutDisplayData(Dd,DisplayFifo);
END;
UNTIL False;
END;
PROCEDURE MoveCOM2Data;
{Move data from the COM2 input buffer to the}
{COM1 output buffer and to the display buffer}
{if enabled}
VAR
Sd: DataRec;
Dd: DisplayRec;
BEGIN
REPEAT
{yield if no data to move}
WHILE COM2_Input_Fifo.Ovd.Count = 0 DO
yield;
{when data is available move it}
{ints off, read data, turn ints on}
INLINE($FA);
GetSerialData(COM2_Input_Fifo,Sd);
INLINE($FB);
{store data in COM1 output fifo}
PutSerialData(Sd,COM1_Output_Fifo);
{if a trigger is enabled}
IF COM2_Is_Triggered AND
TriggerEnabled THEN
BEGIN
{indicate match & disable further matches}
{trigger is single shot event}
IF Sd.Data = TriggerPattern THEN
BEGIN
TriggerEnabled := False;
WriteStringAt(' COM2 Triggered ',
Low,3,25);
{If displaying before trigger}
{stop data display}
IF TriggerMode = Before THEN
COM2_Display_Data := False
ELSE
{If displaying after trigger}
{start data display}
COM2_Display_Data := True;
END;
END;
{if we are displaying data}
IF COM2_Display_Data THEN
BEGIN
{move data to the display fifo also}
Dd.DR := Sd;
{tagged from this COM device}
Dd.Tag := FromCOM2;
PutDisplayData(Dd,DisplayFifo);
END;
UNTIL False;
END;
PROCEDURE OutputCOM1Data;
{Move serial data from the output fifo}
{to the COM port}
CONST
{max of 80 chars to serial port}
{between yields to other tasks}
OutputsPerYield = 80;
VAR
Outs: Integer;
Sd: DataRec;
Urgent: Boolean;
BEGIN
REPEAT
{do until data is available in fifo}
WHILE (COM1_Output_Fifo.Ovd.Count = 0) DO
BEGIN
{always make the handshake lines}
{between COM1 and COM2 agree then yield}
MakeHandshake(COM1,COM2_Status);
yield;
END;
{we have data to output}
{Its urgent if fifo is over half full}
Urgent := (COM1_Output_Fifo.Ovd.Count >=
(SerialDataFifoSize/2));
{Initialize output counter to max value}
Outs := OutputsPerYield;
{While there is data to output}
WHILE ((COM1_Output_Fifo.Ovd.Count <> 0) AND
(Outs <> 0)) DO
BEGIN
{test if UART is ready to transmit}
WHILE (Get_Serial_Status(COM1) AND
TxRdyBit) = 0 DO
BEGIN
{if we have time yield until ready}
IF NOT Urgent THEN
yield;
END;
{get data record to output}
{set the handshake lines and output}
{the data}
GetSerialData(COM1_Output_Fifo,Sd);
MakeHandshake(COM1,Sd.Status);
port[COM1] := Sd.Data;
{one less to output}
Outs := Outs - 1;
END;
yield;
UNTIL False;
END;
PROCEDURE OutputCOM2Data;
{Move serial data from the output fifo}
{to the COM port}
CONST
{max of 80 chars to serial port}
{between yields to other tasks}
OutputsPerYield = 80;
VAR
Outs: Integer;
Sd: DataRec;
Urgent: Boolean;
BEGIN
REPEAT
{do until data is available in fifo}
WHILE (COM2_Output_Fifo.Ovd.Count = 0) DO
BEGIN
{always make the handshake line}
{between COM1 and COM2 agree then yield}
MakeHandshake(COM2,COM1_Status);
yield;
END;
{we have data to output}
{Its urgent if fifo is over half full}
Urgent := (COM2_Output_Fifo.Ovd.Count >=
(SerialDataFifoSize/2));
{Initialize output counter to max value}
Outs := OutputsPerYield;
{While there is data to output}
WHILE ((COM2_Output_Fifo.Ovd.Count <> 0) AND
(Outs <> 0)) DO
BEGIN
{test if UART is ready to transmit}
WHILE (Get_Serial_Status(COM2) AND
TxRdyBit) = 0 DO
BEGIN
{if we have time yield until ready}
IF NOT Urgent THEN
yield;
END;
{get data record to output}
{set the handshake lines and output}
{the data}
GetSerialData(COM2_Output_Fifo,Sd);
MakeHandshake(COM2,Sd.Status);
port[COM2] := Sd.Data;
{one less to output}
Outs := Outs - 1;
END;
yield;
UNTIL False;
END;
PROCEDURE DisplayCOMData;
CONST
{max # of items displayed before yielding}
MaxDisplayItems = 20;
VAR
DisplayItems,
DataLen: Integer;
D: DisplayRec;
BEGIN
{initial cursor is homed}
{in data display window}
OldXPos := 1;
OldYPos := 1;
REPEAT
{if data in display fifo}
IF DisplayFifo.Ovd.Count <> 0 THEN
BEGIN
{claim the screen, define the window}
{position the cursor and initialize}
{items counter}
Alloc(ScreenAccess);
Window(1,7,80,23);
GoToXY(OldXPos,OldYPos);
DisplayItems := MaxDisplayItems;
WHILE ((DisplayFifo.Ovd.Count <> 0 ) AND
(DisplayItems <> 0)) DO
{while there is data to display}
BEGIN
{get the data, display it, dec}
{item counter. Save len of displayed}
{item.}
GetDisplayData(DisplayFifo,D);
DataLen := DisplayData(D);
DisplayItems := DisplayItems - 1;
{if not room to display for another}
{item of same length and we're on the}
{last display line in window, then}
{home cursor and start overwriting}
{screen so as to avoid the scroll of}
{the display. If not on last line of}
{window, advance to next line. Either}
{way, the next line must be cleared.}
IF WhereX + DataLen >= 80 THEN
IF WhereY = 17 THEN
GoToXY(1,1)
ELSE
WriteLn;
ClrEol;
END;
{save new cursor position}
OldXPos := WhereX;
OldYPos := WhereY;
{back to full screen}
{and release screen lock and yield}
Window(1,1,80,25);
Dealloc(ScreenAccess);
END
ELSE
{if no data to display just yield}
yield;
UNTIL False;
END;
PROCEDURE Timer;
VAR
Toggle: Boolean;
BEGIN
Toggle := True;
REPEAT
{every 1/2 second do the following}
pause(4);
{if ok to update the screen}
IF UpdateScreenStatus THEN
BEGIN
{lock the screen}
Alloc(ScreenAccess);
{if status of either port has changed}
IF ((COM1_Status <> OldCOM1_Status) OR
(COM2_Status <> OldCOM2_Status)) THEN
{then update the screen}
BEGIN
{display new COM1 status}
DisplayHandShakeStatus(COM1,COM1_Status
,COM2_Status);
{save new status}
OldCOM1_Status := COM1_Status;
{display new COM2 status}
DisplayHandShakeStatus(COM2,COM2_Status
,COM1_Status);
OldCOM2_Status := COM2_Status;
END;
{every full second update}
{buffer status on screen}
IF Toggle THEN
DisplayBufferStatus;
{unlock the screen}
Dealloc(ScreenAccess);
Toggle := NOT Toggle;
END;
UNTIL False;
END;
BEGIN {main}
{verify presence of COM1 and COM2}
VerifyHardware;
{initialize the multitasker}
Init_Kernel;
{initialize the menu structure}
Init_Menu;
{install the serial ISRs}
Install_Serial_Handlers;
{Initialize strings used to format the display}
Line1 := Char(201)+' COM1 '+repl(16,Char(205))+
' Serial Protocol Analyzer Ver: 1.0 '+
repl(15,Char(205))+ ' COM2 '+Char(187);
Line2 := Char(186)+' In Fifo:'+repl(5,' ')+
'Out Fifo: '+ Char(186)+
repl(10,' ')+'By'+repl(10,' ')+
Char(186)+' In Fifo:'+repl(5,' ')+
'Out Fifo: '+Char(186);
Line3 := Char(186)+' Stat- BRK: FE: PE: OR: '+
Char(186)+ ' Craig A. Lindley '+
Char(186)+ ' Stat- BRK: FE: PE: OR: '+
Char(186);
Line4 := Char(186)+' In - CD: RI: DSR: CTS: '+
Char(186)+repl(22,' ')+Char(186)+
' In - CD: RI: DSR: CTS: '+Char(186);
Line5 := Char(186)+ ' Out- DTR: RTS:'+
repl(7,' ')+Char(186)+
' Display Fifo: '+Char(186)+
' Out- DTR: RTS:'+repl(7,' ')+
Char(186);
Line6 := Char(200)+repl(27,Char(205))+
Char(202)+repl(22,Char(205))+
Char(202)+repl(27,Char(205))+
Char(188);
Line24 := ' <Esc> for Menu <Space Bar> to Pause'+
' COM1 - Normal : COM2 - Reverse Video';
BuildDisplay; {show main display}
Init_Program; {initialize all vars}
{initialize screen lock}
Initialize_Semaphore(ScreenAccess);
{enable status update}
UpdateScreenStatus:= True;
{Fork off all tasks}
Fork;
IF child_process THEN
MoveCOM1Data;
Fork;
IF child_process THEN
MoveCOM2Data;
Fork;
IF child_process THEN
OutputCOM1Data;
Fork;
IF child_process THEN
OutputCOM2Data;
Fork;
IF child_process THEN
DisplayCOMData;
Fork;
IF child_process THEN
Timer;
ProcessKeysTask;
ClrScr;
Disable_Serial_Devices;
Remove_Serial_Handlers;
END
.