PROGRAM PortMonitor; { General purpose I/O Port monitor Written by Jesse Bob Overholt, Carrollton, Texas Copyright (c) 1985, but you can have a copy if you want one! DDT and its various relatives does't provide much for examining and changing I/O ports under CP/M. When I found myself in need of that capability I turned (naturally) to Turbo Pascal and cranked out this little program. It should work without pain on most CP/M systems, and with a little effort maybe even on 16 bitters. You'll want to change the constants to set the control codes used for cursor movement. These are for Montezuma Micro CP/M 2.2 on a TRS-80 Model 4/4P. Operation is simple. Select the port by moving the cursor to it. Then specify changes by entering the first letter of the item to change. For example, type 'P' to set the port number, then enter the number in hex. Port numbers are always entered in hex, but data entry follows the Type set for the port. Commands: P = Set Port address (in hex) M = Set port Mode (I for Input, O for Output) D = Set port Data (value entered according to type) T = Set port Type (D for Decimal, H for Hex, B for Binary) ENTER or RETURN = Do the actual I/O and display data if input S = Scan port to perform continous I/O until 'space' entered BREAK or ^C = Exit back to CP/M The real problem with quick and dirty software tools is that they are always under-documented. This program is no exception, and I'll leave it for you to decide how self-documenting Pascal really is. Having spent many years in software support for pay the author is not all that excited about phone calls with gripes, enhancements, etc. If you really need to contact me you can try CompuServe 70130,101 or send a SASE to 3139 Oak Hill Road, Carrollton, TX 75007. Modifications: 14 July 85 by Gail Graham W5MLY Continous scan until keypressed added. } CONST Up = ^K; {Kimtron KT-7 and/or (Televideo 925)} Down = ^J; Left = ^H; Right = ^L; Break = ^Q; Enter = ^M; Beep = ^G; TYPE PortSet = SET OF 0..15; Modes = (Input, Output); DisplayTypes = (Binary, Hex, Decimal); KeyList = SET OF CHAR; InputLine = STRING[8]; DispLine = STRING[8]; VAR CurPort, CurVal, CurX, CurY: BYTE; DefinedPorts: PortSet; PortAddress, PortData: ARRAY [0..15] OF BYTE; PortMode: ARRAY [0..15] OF Modes; PortType: ARRAY [0..15] OF DisplayTypes; KeyBuf: CHAR; BreakRequested: BOOLEAN; {-------------------------------------------------------------------------} (* Following Keypressed function replaces the KeyPressed Function in Turbo Pascal, and is for use only with the NEC-APC. The Turbo Pascal keypressed function does not work on the NEC-APC. If your keypressed function does not work and you do not have a NEC, then you will have to write a similar replacement function to match your machine. If your keypressed function does work then this entire function may be deleted. The function is included as a remark so it will not compile normally. *) (* Function KEYPRESSED:BOOLEAN; VAR KEYSTAT:byte; BEGIN KeyStat := Port[$4C] and $10; If KeyStat <> 0 then keypressed := true else keypressed := false; end; *) {-------------------------------------------------------------------------} PROCEDURE SetXY (DescNum: BYTE); BEGIN CurY := (CurPort DIV 4) * 5 + 5; CurX := (CurPort MOD 4) * 20 + 1; END; {-------------------------------------------------------------------------} FUNCTION GetKey (LegalKeys: KeyList): CHAR; VAR InKey: CHAR; CONST Printable: SET OF CHAR = [#$20..#$7E]; BEGIN REPEAT Read(Kbd,InKey); InKey := UpCase(InKey); UNTIL InKey IN LegalKeys; IF InKey IN Printable THEN Write(InKey); GetKey := InKey; END; {-------------------------------------------------------------------------} FUNCTION GetLine (Length: BYTE; ValidKeys: KeyList): InputLine; VAR Buffer: InputLine; CurLen: BYTE; InKey: CHAR; { Local } PROCEDURE BackSpace; BEGIN Write(^H,'_',^H); Delete (Buffer, CurLen, 1); CurLen := CurLen - 1; END; BEGIN CurLen := 0; WHILE CurLen < Length DO BEGIN Write('*'); CurLen := CurLen + 1; END; WHILE CurLen > 0 DO BackSpace; Buffer := ''; REPEAT InKey := GetKey (ValidKeys + [^H, ^X]); CASE InKey OF ^H: IF CurLen > 0 THEN BackSpace; ^X: WHILE CurLen > 0 DO BackSpace; ELSE BEGIN Buffer := Buffer + InKey; CurLen := CurLen + 1; END; END; UNTIL CurLen = Length; GetLine := Buffer; END; {-------------------------------------------------------------------------} FUNCTION GetHex: BYTE; VAR InBuf: InputLine; { Local } FUNCTION HexVal (Digit: CHAR): BYTE; VAR DigitVal: BYTE; BEGIN DigitVal := Ord(Digit) - Ord('0'); IF DigitVal > 15 THEN DigitVal := DigitVal - 7; HexVal := DigitVal; END; BEGIN InBuf := GetLine (2, ['0'..'9']+['A'..'F']); GetHex := HexVal(InBuf[1]) SHL 4 + HexVal(InBuf[2]); END; {-------------------------------------------------------------------------} FUNCTION GetDec: BYTE; VAR InBuf: InputLine; I, Value: INTEGER; BEGIN REPEAT InBuf := GetLine (3, ['0'..'9']); Val(InBuf, Value, I); IF Value > 255 THEN I := 1; IF I > 0 THEN Write(^H,^H,^H,Beep); UNTIL I = 0; GetDec := Value; END; {-------------------------------------------------------------------------} FUNCTION GetBin: BYTE; VAR Value: BYTE; InBuf: InputLine; BEGIN Value := 0; InBuf := GetLine (8, ['0'..'1']); WHILE Length(InBuf) > 0 DO BEGIN Value := Value SHL 1; IF InBuf[1] = '1' THEN Value := Value + 1; Delete (InBuf, 1, 1); END; GetBin := Value; END; {-------------------------------------------------------------------------} FUNCTION CvtBin: DispLine; VAR Buffer: DispLine; Value: BYTE; BEGIN Buffer := ''; Value := PortData[CurPort]; REPEAT IF Odd(Value) THEN Buffer := '1' + Buffer ELSE Buffer := '0' + Buffer; Value := Value SHR 1; UNTIL Length(Buffer) = 8; CvtBin := Buffer; END; {-------------------------------------------------------------------------} FUNCTION CvtHex: DispLine; VAR Buffer: DispLine; { Local } FUNCTION HexDig (Value: BYTE): CHAR; BEGIN Value := Value + Ord('0'); IF Value > Ord('9') THEN Value := Value + 7; HexDig := Chr(Value); END; BEGIN CvtHex := HexDig(PortData[CurPort] SHR 4) + HexDig(PortData[CurPort] AND $0F); END; {-------------------------------------------------------------------------} PROCEDURE DisplayData; BEGIN GotoXY(CurX+10,CurY+2); Write(' ',^H^H^H^H^H^H^H^H); CASE PortType[CurPort] OF Binary: Write(CvtBin); Hex: Write(CvtHex); Decimal: Write(PortData[CurPort]:3); END; END; {-------------------------------------------------------------------------} BEGIN { Set up opening banner } ClrScr; LowVideo; ClrEol; GotoXY(10,1); Writeln('CCP/M Port Monitor Version 0.02'); Writeln(' (c) (p) 1985 by Jessie Overholt and modified by others'); NormVideo; Write(' Arrows to select, to set up, '); Writeln(' for I/O, to quit'); Writeln(' to scan assigned ports, to stop scan'); { Build Port desciptors } FOR CurPort := 0 TO 15 DO BEGIN SetXY (CurPort); GotoXY(CurX, CurY); Write('[ ] '); LowVideo; Write('Port:'); GotoXY(CurX+4,CurY+1); Write('Mode:'); GotoXY(CurX+4,CurY+2); Write('Data:'); GotoXY(CurX+4,CurY+3); Write('Type:'); NormVideo; GotoXY(CurX+10,CurY); Write('??'); GotoXY(CurX+10,CurY+1); Write('Input'); GotoXY(CurX+10,CurY+2); Write('00'); GotoXY(CurX+10,CurY+3); Write('Hex'); PortAddress[CurPort] := $00; PortMode[CurPort] := Input; PortData[CurPort] := $00; PortType[CurPort] := Hex; END; DefinedPorts := []; CurPort := 0; BreakRequested := FALSE; { Begin main command loop } REPEAT SetXY (CurPort); GotoXY(CurX+1, CurY); Read (Kbd,KeyBuf); CASE UpCase(KeyBuf) OF Up: CurPort := (CurPort - 4) AND $0F; Down: CurPort := (CurPort + 4) AND $0F; Left: CurPort := (CurPort - 1) AND $0F; Right: CurPort := (CurPort + 1) AND $0F; Break: BreakRequested := TRUE; 'P': BEGIN GotoXY(CurX+10,CurY); PortAddress[CurPort] := GetHex; DefinedPorts := DefinedPorts + [CurPort]; END; 'M': BEGIN GotoXY(CurX+10,CurY+1); Write('? I/O ',^H^H^H^H^H^H); IF GetKey(['I','O']) = 'I' THEN BEGIN Write('nput '); PortMode[CurPort] := Input; END ELSE BEGIN Write('utput'); PortMode[CurPort] := Output; END; END; 'D': BEGIN GotoXY(CurX+10,CurY+2); Write(' ',^H^H^H^H^H^H^H^H); CASE PortType[CurPort] OF Binary: PortData[CurPort] := GetBin; Hex: PortData[CurPort] := GetHex; Decimal: PortData[CurPort] := GetDec; END; END; 'T': BEGIN GotoXY(CurX+10,CurY+3); Write('? B/H/D',^H^H^H^H^H^H^H); CASE GetKey(['B','H','D']) OF 'B': BEGIN Write('inary '); PortType[CurPort] := Binary; END; 'H': BEGIN Write('ex '); PortType[CurPort] := Hex; END; 'D': BEGIN Write('ecimal '); PortType[CurPort] := Decimal; END; END; DisplayData; END; 'S':BEGIN Delay(300); (* allow keypressed time to clear *) WHILE not keypressed DO BEGIN DELAY(500); FOR CurPort := 0 to 15 do BEGIN IF CurPort IN DefinedPorts THEN BEGIN SetXY(CurPort); GotoXY(CurX,CurY); CASE PortMode[CurPort] OF Input: BEGIN PortData[CurPort] := Port[PortAddress[CurPort]]; DisplayData; END; Output: Port[PortAddress[CurPort]] := PortData[CurPort]; END; END; END; END; END; Enter: BEGIN IF CurPort IN DefinedPorts THEN BEGIN CASE PortMode[CurPort] OF Input: BEGIN PortData[CurPort] := Port[PortAddress[CurPort]]; DisplayData; END; Output: Port[PortAddress[CurPort]] := PortData[CurPort]; END; END ELSE Write(Beep); END; END; UNTIL BreakRequested; ClrScr; END.