home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 3 Comm / 03-Comm.zip / DDJKRMIT.ZIP / ANDRSON2.ASC < prev   
Text File  |  1990-08-08  |  135KB  |  4,220 lines

  1. _KERMIT FOR OS/2_
  2. by Brian R. Anderson
  3.  
  4. [LISTING ONE]
  5.  
  6.  
  7. MODULE PCKermit;
  8. (**************************************************************************)
  9. (*                                                                        *)
  10. (*                  PCKermit  --  by Brian R. Anderson                    *)
  11. (*                         Copyright (c) 1990                             *)
  12. (*                                                                        *)
  13. (*  PCKermit is an implementation of the Kermit file transfer protocol    *)
  14. (*  developed at Columbia University.  This (OS/2 PM) version is a        *) 
  15. (*  port from the DOS version of Kermit that I wrote two years ago.       *)
  16. (*  My original DOS version appeared in the May 1989 issue of DDJ.        *)
  17. (*                                                                        *)
  18. (*  The current version includes emulation of the TVI950 Video Display    *)
  19. (*  Terminal for interaction with IBM mainframes (through the IBM 7171).  *)
  20. (*                                                                        *)
  21. (**************************************************************************)
  22.  
  23.    FROM SYSTEM IMPORT
  24.       ADR;
  25.     
  26.    FROM OS2DEF IMPORT
  27.       HAB, HWND, HPS, NULL, ULONG;
  28.  
  29.    FROM PMWIN IMPORT
  30.       MPARAM, HMQ, QMSG, CS_SIZEREDRAW,  WS_VISIBLE, FS_ICON,      
  31.       FCF_TITLEBAR, FCF_SYSMENU, FCF_SIZEBORDER, FCF_MINMAX, FCF_ACCELTABLE,
  32.       FCF_SHELLPOSITION, FCF_TASKLIST, FCF_MENU, FCF_ICON, 
  33.       SWP_MOVE, SWP_SIZE, SWP_MAXIMIZE, 
  34.       HWND_DESKTOP, FID_SYSMENU, SC_CLOSE, MIA_DISABLED, MM_SETITEMATTR,
  35.       WinInitialize, WinCreateMsgQueue, WinGetMsg, WinDispatchMsg, WinSendMsg,
  36.       WinRegisterClass, WinCreateStdWindow, WinDestroyWindow, WinWindowFromID,
  37.       WinDestroyMsgQueue, WinTerminate, WinSetWindowText, 
  38.       WinSetWindowPos, WinQueryWindowPos;
  39.  
  40.    FROM KH IMPORT
  41.       IDM_KERMIT;
  42.  
  43.    FROM Shell IMPORT
  44.       Class, Title, Child, WindowProc, ChildWindowProc, 
  45.       FrameWindow, ClientWindow, SetPort, Pos;
  46.  
  47.    
  48.    CONST
  49.       QUEUE_SIZE = 1024;   (* Large message queue for async events *)
  50.  
  51.    VAR
  52.       AnchorBlock : HAB;
  53.       MessageQueue : HMQ;
  54.       Message : QMSG;
  55.       FrameFlags : ULONG;
  56.       hsys : HWND;
  57.       MP1, MP2 : MPARAM;
  58.          
  59.  
  60. BEGIN   (* main *)
  61.    AnchorBlock := WinInitialize(0);
  62.     
  63.    IF AnchorBlock # 0 THEN
  64.       MessageQueue := WinCreateMsgQueue (AnchorBlock, QUEUE_SIZE);
  65.     
  66.       IF MessageQueue # 0 THEN
  67.          (* Register the parent window class *)
  68.          WinRegisterClass (
  69.              AnchorBlock,
  70.              ADR (Class),
  71.              WindowProc,
  72.              CS_SIZEREDRAW, 0);
  73.          
  74.          (* Register a child window class *)
  75.          WinRegisterClass (
  76.              AnchorBlock,
  77.              ADR (Child),
  78.              ChildWindowProc,
  79.              CS_SIZEREDRAW, 0);
  80.          
  81.          (* Create a standard window *)
  82.          FrameFlags := FCF_TITLEBAR + FCF_MENU + FCF_MINMAX + 
  83.                        FCF_SYSMENU + FCF_SIZEBORDER + FCF_TASKLIST + 
  84.                        FCF_ICON + FCF_SHELLPOSITION + FCF_ACCELTABLE;
  85.          
  86.          FrameWindow := WinCreateStdWindow (
  87.                   HWND_DESKTOP,           (* handle of the parent window *)
  88.                   WS_VISIBLE + FS_ICON,   (* the window style *)
  89.                   FrameFlags,             (* the window flags *)
  90.                   ADR(Class),             (* the window class *)
  91.                   NULL,                   (* the title bar text *)
  92.                   WS_VISIBLE,             (* client window style *)
  93.                   NULL,                   (* handle of resource module *)
  94.                   IDM_KERMIT,             (* resource id *)
  95.                   ClientWindow            (* returned client window handle *)
  96.          );
  97.           
  98.          IF FrameWindow # 0 THEN
  99.             (* Disable the CLOSE item on the system menu *)
  100.             hsys := WinWindowFromID (FrameWindow, FID_SYSMENU);
  101.             MP1.W1 := SC_CLOSE;   MP1.W2 := 1;
  102.             MP2.W1 := MIA_DISABLED;   MP2.W2 := MIA_DISABLED;
  103.             WinSendMsg (hsys, MM_SETITEMATTR, MP1, MP2);
  104.  
  105.             (* Expand Window to Nearly Full Size, And Display the Title *)
  106.             WinQueryWindowPos (HWND_DESKTOP, Pos);
  107.             WinSetWindowPos (FrameWindow, 0, 
  108.                Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6, 
  109.                SWP_MOVE + SWP_SIZE);
  110.             WinSetWindowText (FrameWindow, ADR (Title));
  111.             
  112.             SetPort;   (* Try to initialize communications port *)
  113.          
  114.             WHILE WinGetMsg(AnchorBlock, Message, NULL, 0, 0) # 0 DO
  115.                WinDispatchMsg(AnchorBlock, Message);
  116.             END;
  117.           
  118.             WinDestroyWindow(FrameWindow);
  119.          END;
  120.          WinDestroyMsgQueue(MessageQueue);
  121.       END;
  122.       WinTerminate(AnchorBlock);
  123.    END;
  124. END PCKermit.
  125.  
  126. [LISTING TWO]
  127.  
  128. DEFINITION MODULE Shell;
  129.  
  130.    FROM OS2DEF IMPORT
  131.       USHORT, HWND;
  132.  
  133.    FROM PMWIN IMPORT
  134.       MPARAM, MRESULT, SWP;
  135.  
  136.    EXPORT QUALIFIED
  137.       Class, Child, Title, FrameWindow, ClientWindow,
  138.       ChildFrameWindow, ChildClientWindow, Pos, SetPort, 
  139.       WindowProc, ChildWindowProc;
  140.          
  141.    CONST
  142.       Class = "PCKermit";
  143.       Child ="Child";
  144.       Title = "PCKermit -- Microcomputer to Mainframe Communications";
  145.  
  146.    
  147.    VAR
  148.       FrameWindow : HWND;
  149.       ClientWindow : HWND;   
  150.       ChildFrameWindow : HWND;
  151.       ChildClientWindow : HWND;
  152.       Pos : SWP;   (* Screen Dimensions: position & size *)
  153.       comport : CARDINAL;
  154.  
  155.  
  156.    PROCEDURE SetPort;
  157.    
  158.    PROCEDURE WindowProc ['WindowProc'] (
  159.       hwnd : HWND;
  160.       msg  : USHORT;   
  161.       mp1  [VALUE] : MPARAM; 
  162.       mp2  [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  163.  
  164.    PROCEDURE ChildWindowProc ['ChildWindowProc'] (
  165.       hwnd : HWND;
  166.       msg  : USHORT;   
  167.       mp1  [VALUE] : MPARAM; 
  168.       mp2  [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  169.  
  170. END Shell.
  171.  
  172. [LISTING THREE]
  173.  
  174. DEFINITION MODULE Term;   (* TVI950 Terminal Emulation For Kermit *)
  175.  
  176.    EXPORT QUALIFIED
  177.       WM_TERM, WM_TERMQUIT, 
  178.       Dir, TermThrProc, InitTerm, PutKbdChar, PutPortChar;
  179.  
  180.    CONST
  181.       WM_TERM = 4000H;
  182.       WM_TERMQUIT = 4001H;
  183.    
  184.       
  185.    PROCEDURE Dir (path : ARRAY OF CHAR);
  186.    (* Displays a directory *)
  187.    
  188.    PROCEDURE TermThrProc;
  189.    (* Thread to get characters from port, put into buffer, send message *)
  190.    
  191.    PROCEDURE InitTerm;
  192.    (* Clear Screen, Home Cursor, Get Ready For Terminal Emulation *)
  193.    
  194.    PROCEDURE PutKbdChar (ch1, ch2 : CHAR);
  195.    (* Process a character received from the keyboard *)
  196.  
  197.    PROCEDURE PutPortChar (ch : CHAR);
  198.    (* Process a character received from the port *)
  199.    
  200. END Term.
  201.  
  202. [LISTING FOUR]
  203.  
  204. DEFINITION MODULE Screen;
  205. (* Module to perform "low level" screen functions (via AVIO) *)
  206.  
  207.    FROM PMAVIO IMPORT
  208.       HVPS;
  209.  
  210.    EXPORT QUALIFIED
  211.       NORMAL, HIGHLIGHT, REVERSE, attribute, ColorSet, hvps,
  212.       White, Green, Amber, Color1, Color2,
  213.       ClrScr, ClrEol, GotoXY, GetXY,    
  214.       Right, Left, Up, Down, Write, WriteLn, WriteString,
  215.       WriteInt, WriteHex, WriteAtt;
  216.  
  217.    
  218.    VAR      
  219.       NORMAL : CARDINAL;
  220.       HIGHLIGHT : CARDINAL;    
  221.       REVERSE : CARDINAL;
  222.       attribute : CARDINAL;    
  223.       ColorSet : CARDINAL;
  224.       hvps : HVPS;   (* presentation space used by screen module *)
  225.          
  226.  
  227.    PROCEDURE White;
  228.    (* Sets up colors: Monochrome White *)
  229.       
  230.    PROCEDURE Green;
  231.    (* Sets up colors: Monochrome Green *)
  232.       
  233.    PROCEDURE Amber;
  234.    (* Sets up colors: Monochrome Amber *)
  235.       
  236.    PROCEDURE Color1;
  237.    (* Sets up colors: Blue, Red, Green *)
  238.       
  239.    PROCEDURE Color2;
  240.    (* Sets up colors: Green, Magenta, Cyan *)
  241.    
  242.    PROCEDURE ClrScr;      
  243.    (* Clear the screen, and home the cursor *)     
  244.    
  245.    PROCEDURE ClrEol;      
  246.    (* clear from the current cursor position to the end of the line *)     
  247.    
  248.    PROCEDURE Right;     
  249.    (* move cursor to the right *)    
  250.    
  251.    PROCEDURE Left;    
  252.    (* move cursor to the left *)      
  253.    
  254.    PROCEDURE Up;     
  255.    (* move cursor up *)      
  256.    
  257.    PROCEDURE Down;    
  258.    (* move cursor down *)     
  259.    
  260.    PROCEDURE GotoXY (col, row : CARDINAL);    
  261.    (* position cursor at column, row *)    
  262.    
  263.    PROCEDURE GetXY (VAR col, row : CARDINAL);    
  264.    (* determine current cursor position *)    
  265.  
  266.    PROCEDURE Write (c : CHAR);
  267.    (* Write a Character, Teletype Mode *)
  268.  
  269.    PROCEDURE WriteString (str : ARRAY OF CHAR);
  270.    (* Write String, Teletype Mode *)
  271.  
  272.    PROCEDURE WriteInt (n : INTEGER; s : CARDINAL);
  273.    (* Write Integer, Teletype Mode *)
  274.    
  275.    PROCEDURE WriteHex (n, s : CARDINAL);
  276.    (* Write a Hexadecimal Number, Teletype Mode *)
  277.    
  278.    PROCEDURE WriteLn;
  279.    (* Write <cr> <lf>, Teletype Mode *)
  280.    
  281.    PROCEDURE WriteAtt (c : CHAR);    
  282.    (* write character and attribute at cursor position *)    
  283.    
  284. END Screen.
  285.  
  286. [LISTING FIVE]
  287.  
  288. DEFINITION MODULE PAD;   (* Packet Assembler/Disassembler for Kermit *)
  289.  
  290.    FROM PMWIN IMPORT
  291.       MPARAM;
  292.       
  293.    EXPORT QUALIFIED
  294.       WM_PAD, PAD_Quit, PAD_Error, PacketType, yourNPAD, yourPADC, yourEOL, 
  295.       Aborted, sFname, Send, Receive, DoPADMsg;
  296.  
  297.    CONST
  298.       WM_PAD = 5000H;
  299.       PAD_Quit = 0;
  300.       PAD_Error = 20;
  301.               
  302.    TYPE
  303.       (* PacketType used in both PAD and DataLink modules *)
  304.       PacketType = ARRAY [1..100] OF CHAR;
  305.       
  306.    VAR
  307.       (* yourNPAD, yourPADC, and yourEOL used in both PAD and DataLink *)
  308.       yourNPAD : CARDINAL;   (* number of padding characters *)
  309.       yourPADC : CHAR;       (* padding characters *)
  310.       yourEOL  : CHAR;       (* End Of Line -- terminator *)
  311.       sFname : ARRAY [0..20] OF CHAR;
  312.       Aborted : BOOLEAN;
  313.  
  314.    PROCEDURE Send;
  315.    (* Sends a file after prompting for filename *)
  316.    
  317.    PROCEDURE Receive;
  318.    (* Receives a file (or files) *)
  319.  
  320.    PROCEDURE DoPADMsg (mp1, mp2 [VALUE] : MPARAM);
  321.    (* Output messages for Packet Assembler/Disassembler *)
  322.             
  323. END PAD.
  324.  
  325. [LISTING SIX]
  326.  
  327. DEFINITION MODULE DataLink;   (* Sends and Receives Packets for PCKermit *)
  328.  
  329.    FROM PMWIN IMPORT
  330.       MPARAM;
  331.       
  332.    FROM PAD IMPORT
  333.       PacketType;
  334.       
  335.    EXPORT QUALIFIED
  336.       WM_DL, FlushUART, SendPacket, ReceivePacket, DoDLMsg;
  337.  
  338.    CONST
  339.       WM_DL = 6000H;
  340.       
  341.    PROCEDURE FlushUART;
  342.    (* ensure no characters left in UART holding registers *)
  343.     
  344.    PROCEDURE SendPacket (s : PacketType);
  345.    (* Adds SOH and CheckSum to packet *)
  346.    
  347.    PROCEDURE ReceivePacket (VAR r : PacketType) : BOOLEAN;
  348.    (* strips SOH and checksum -- returns status: TRUE= good packet       *)
  349.    (* received;  FALSE = timed out waiting for packet or checksum error  *)
  350.    
  351.    PROCEDURE DoDLMsg (mp1, mp2 [VALUE] : MPARAM);
  352.    (* Process DataLink Messages *)
  353.    
  354. END DataLink.
  355.  
  356. [LISTING SEVEN]
  357.  
  358. (*************************************************************)
  359. (*                                                           *)
  360. (*                Copyright (C) 1988, 1989                   *)
  361. (*                 by Stony Brook Software                   *)
  362. (*                                                           *)
  363. (*                   All rights reserved.                    *)
  364. (*                                                           *)
  365. (*************************************************************)
  366.  
  367. DEFINITION MODULE CommPort;
  368.  
  369.    TYPE
  370.       CommStatus = (                
  371.                Success,   
  372.                InvalidPort,  
  373.                InvalidParameter,    
  374.                AlreadyReceiving,    
  375.                NotReceiving,  
  376.                NoCharacter,  
  377.                FramingError,  
  378.                OverrunError,  
  379.                ParityError,  
  380.                BufferOverflow,  
  381.                TimeOut   
  382.       );   
  383.  
  384.       BaudRate = (  
  385.                Baud110,   
  386.                Baud150,   
  387.                Baud300,   
  388.                Baud600,   
  389.                Baud1200,  
  390.                Baud2400,  
  391.                Baud4800,  
  392.                Baud9600,  
  393.                Baud19200  
  394.       );   
  395.       
  396.       DataBits = [7..8];  
  397.       StopBits = [1..2];  
  398.       Parity = (Even, Odd, None);  
  399.  
  400.  
  401.    PROCEDURE InitPort(port : CARDINAL; speed : BaudRate; data : DataBits;
  402.                           stop : StopBits; check : Parity) : CommStatus;
  403.  
  404.    PROCEDURE StartReceiving(port, bufsize : CARDINAL) : CommStatus;
  405.  
  406.    PROCEDURE StopReceiving(port : CARDINAL) : CommStatus;
  407.  
  408.    PROCEDURE GetChar(port : CARDINAL; VAR ch : CHAR) : CommStatus;
  409.  
  410.    PROCEDURE SendChar(port : CARDINAL; ch : CHAR; modem : BOOLEAN) : CommStatus;
  411.  
  412. END CommPort.
  413.  
  414. [LISTING EIGHT]
  415.  
  416. DEFINITION MODULE Files;   (* File I/O for Kermit *)
  417.  
  418.    FROM FileSystem IMPORT
  419.       File;
  420.       
  421.    EXPORT QUALIFIED
  422.       Status, FileType, Open, Create, CloseFile, Get, Put, DoWrite;
  423.          
  424.    TYPE
  425.       Status = (Done, Error, EOF);
  426.       FileType = (Input, Output);
  427.    
  428.    PROCEDURE Open (VAR f : File; name : ARRAY OF CHAR) : Status;
  429.    (* opens an existing file for reading, returns status *)
  430.    
  431.    PROCEDURE Create (VAR f : File; name : ARRAY OF CHAR) : Status;
  432.    (* creates a new file for writing, returns status *)
  433.    
  434.    PROCEDURE CloseFile (VAR f : File; Which : FileType) : Status;
  435.    (* closes a file after reading or writing *)
  436.    
  437.    PROCEDURE Get (VAR f : File; VAR ch : CHAR) : Status;
  438.    (* Reads one character from the file, returns status *)
  439.    
  440.    PROCEDURE Put (ch : CHAR);
  441.    (* Writes one character to the file buffer *)
  442.    
  443.    PROCEDURE DoWrite (VAR f : File) : Status;
  444.    (* Writes buffer to disk only if nearly full *)
  445.    
  446. END Files.
  447.  
  448. [LISTING NINE]
  449.  
  450. IMPLEMENTATION MODULE Shell;
  451.  
  452.    FROM SYSTEM IMPORT
  453.       ADDRESS, ADR;
  454.     
  455.    IMPORT ASCII;
  456.    
  457.    FROM OS2DEF IMPORT
  458.       HWND, HDC, HPS, RECTL, USHORT, NULL, ULONG;
  459.  
  460.    FROM Term IMPORT
  461.       WM_TERM, WM_TERMQUIT, 
  462.       Dir, TermThrProc, InitTerm, PutKbdChar, PutPortChar;
  463.  
  464.    FROM PAD IMPORT
  465.       WM_PAD, PAD_Quit, PAD_Error, DoPADMsg, Aborted, sFname, Send, Receive;
  466.  
  467.    FROM DataLink IMPORT
  468.       WM_DL, DoDLMsg;
  469.             
  470.    FROM Screen IMPORT
  471.       hvps, ColorSet, White, Green, Amber, Color1, Color2, ClrScr, WriteLn;
  472.       
  473.    FROM DosCalls IMPORT
  474.       DosCreateThread, DosSuspendThread, DosResumeThread, DosSleep;
  475.  
  476.    FROM PMAVIO IMPORT
  477.       VioCreatePS, VioAssociate, VioDestroyPS, VioShowPS, WinDefAVioWindowProc,
  478.       FORMAT_CGA, HVPS;
  479.       
  480.    FROM PMWIN IMPORT
  481.       MPARAM, MRESULT, SWP, PSWP, 
  482.       WS_VISIBLE, FCF_TITLEBAR, FCF_SIZEBORDER, FCF_SHELLPOSITION,
  483.       WM_SYSCOMMAND, WM_MINMAXFRAME, SWP_MINIMIZE, HWND_DESKTOP, 
  484.       WM_PAINT, WM_QUIT, WM_COMMAND, WM_INITDLG, WM_CONTROL, WM_HELP,
  485.       WM_INITMENU, WM_SIZE, WM_DESTROY, WM_CREATE, WM_CHAR, 
  486.       BM_SETCHECK, MBID_OK, MB_OK, MB_OKCANCEL, 
  487.       KC_CHAR, KC_CTRL, KC_VIRTUALKEY, KC_KEYUP,
  488.       SWP_SIZE, SWP_MOVE, SWP_MAXIMIZE, SWP_RESTORE,
  489.       MB_ICONQUESTION, MB_ICONASTERISK, MB_ICONEXCLAMATION,
  490.       FID_MENU, MM_SETITEMATTR, MM_QUERYITEMATTR, MIA_DISABLED, MIA_CHECKED, 
  491.       WinCreateStdWindow, WinDestroyWindow,
  492.       WinOpenWindowDC, WinSendMsg, WinQueryDlgItemText, WinInvalidateRect,
  493.       WinDefWindowProc, WinBeginPaint, WinEndPaint, WinQueryWindowRect,
  494.       WinSetWindowText, WinSetFocus, WinDlgBox, WinDefDlgProc, WinDismissDlg, 
  495.       WinMessageBox, WinPostMsg, WinWindowFromID, WinSendDlgItemMsg,
  496.       WinSetWindowPos, WinSetActiveWindow;
  497.  
  498.    FROM PMGPI IMPORT
  499.       GpiErase;
  500.  
  501.    FROM KH IMPORT
  502.       IDM_KERMIT, IDM_FILE, IDM_OPTIONS, IDM_SENDFN, ID_SENDFN,
  503.       IDM_DIR, IDM_CONNECT, IDM_SEND, IDM_REC, IDM_DIRPATH, ID_DIRPATH, 
  504.       IDM_DIREND, IDM_QUIT, IDM_ABOUT, IDM_HELPMENU, IDM_TERMHELP, 
  505.       IDM_COMPORT, IDM_BAUDRATE, IDM_DATABITS, IDM_STOPBITS, IDM_PARITY, 
  506.       COM_OFF, ID_COM1, ID_COM2, PARITY_OFF, ID_EVEN, ID_ODD, ID_NONE, 
  507.       DATA_OFF, ID_DATA7, ID_DATA8, STOP_OFF, ID_STOP1, ID_STOP2,
  508.       BAUD_OFF, ID_B110, ID_B150, ID_B300, ID_B600, ID_B1200, ID_B2400, 
  509.       ID_B4800, ID_B9600, ID_B19K2,
  510.       IDM_COLORS, IDM_WHITE, IDM_GREEN, IDM_AMBER, IDM_C1, IDM_C2;
  511.  
  512.    FROM CommPort IMPORT
  513.       CommStatus, BaudRate, DataBits, StopBits, Parity, InitPort,
  514.       StartReceiving, StopReceiving;
  515.    
  516.    FROM Strings IMPORT
  517.       Assign, Append, AppendChar;
  518.    
  519.    
  520.    CONST
  521.       WM_SETMAX = 7000H;
  522.       WM_SETFULL = 7001H;
  523.       WM_SETRESTORE = 7002H;
  524.       NONE = 0;   (* no port yet initialized *)
  525.       STKSIZE = 4096;
  526.       BUFSIZE = 4096;   (* Port receive buffers: room for two full screens *)
  527.       PortError = "Port Is Already In Use -- EXIT? (Cancel Trys Another Port)";
  528.       ESC = 33C;
  529.       
  530.    
  531.    VAR
  532.       FrameFlags : ULONG;
  533.       TermStack : ARRAY [1..STKSIZE] OF CHAR;
  534.       Stack : ARRAY [1..STKSIZE] OF CHAR;
  535.       TermThr : CARDINAL;
  536.       Thr : CARDINAL;
  537.       hdc : HDC;
  538.       frame_hvps, child_hvps : HVPS;
  539.       TermMode : BOOLEAN;
  540.       Path : ARRAY [0..60] OF CHAR;
  541.       Banner : ARRAY [0..40] OF CHAR;
  542.       PrevComPort : CARDINAL;
  543.       Settings : ARRAY [0..1] OF RECORD
  544.                                     baudrate : CARDINAL;
  545.                                     databits : CARDINAL;
  546.                                     parity : CARDINAL;
  547.                                     stopbits : CARDINAL;
  548.                                  END;    
  549.       MP1, MP2 : MPARAM;
  550.                                        
  551.  
  552.    PROCEDURE SetFull;
  553.    (* Changes window to full size *)
  554.       BEGIN
  555.          WinSetWindowPos (FrameWindow, 0,        
  556.             Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6,
  557.             SWP_MOVE + SWP_SIZE);
  558.       END SetFull;
  559.       
  560.               
  561.    PROCEDURE SetRestore;
  562.    (* Changes window to full size FROM maximized *)
  563.       BEGIN
  564.          WinSetWindowPos (FrameWindow, 0,
  565.             Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6,        
  566.             SWP_MOVE + SWP_SIZE + SWP_RESTORE);          
  567.       END SetRestore;
  568.       
  569.                                   
  570.    PROCEDURE SetMax;
  571.    (* Changes window to maximized *)
  572.       BEGIN
  573.          WinSetWindowPos (FrameWindow, 0,                           
  574.             Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6,        
  575.             SWP_MOVE + SWP_SIZE + SWP_MAXIMIZE);    
  576.       END SetMax;
  577.       
  578.                                                                          
  579.    PROCEDURE SetBanner;
  580.    (* Displays Abbreviated Program Title + Port Settings in Title Bar *)
  581.  
  582.       CONST
  583.          PortName : ARRAY [0..1] OF ARRAY [0..5] OF CHAR =
  584.             [["COM1:", 0C], ["COM2:", 0C]]; 
  585.          BaudName : ARRAY [0..8] OF ARRAY [0..5] OF CHAR =
  586.             [["110", 0C], ["150", 0C], ["300", 0C], 
  587.              ["600", 0C], ["1200", 0C], ["2400", 0C], 
  588.              ["4800", 0C], ["9600", 0C], ["19200", 0C]];  
  589.          ParityName : ARRAY [0..2] OF CHAR = ['E', 'O', 'N'];
  590.    
  591.       BEGIN
  592.          WITH Settings[comport - COM_OFF] DO
  593.             Assign (Class, Banner);
  594.             Append (Banner, " -- ");
  595.             Append (Banner, PortName[comport - COM_OFF]);
  596.             Append (Banner, BaudName[baudrate - BAUD_OFF]);
  597.             AppendChar (Banner, ',');
  598.             AppendChar (Banner, ParityName[parity - PARITY_OFF]);
  599.             AppendChar (Banner, ',');
  600.             AppendChar (Banner, CHR ((databits - DATA_OFF) + 30H));
  601.             AppendChar (Banner, ',');
  602.             AppendChar (Banner, CHR ((stopbits - STOP_OFF) + 30H)); 
  603.             WinSetWindowText (FrameWindow, ADR (Banner));
  604.          END;
  605.       END SetBanner;
  606.    
  607.    
  608.    PROCEDURE SetPort;
  609.    (* Sets The Communications Parameters Chosen By User *)
  610.  
  611.       VAR
  612.          status : CommStatus;
  613.          rc : USHORT;
  614.       
  615.       BEGIN
  616.          IF PrevComPort # NONE THEN
  617.             StopReceiving (PrevComPort - COM_OFF);
  618.          END;
  619.          
  620.          WITH Settings[comport - COM_OFF] DO
  621.             status := InitPort (
  622.                comport - COM_OFF,
  623.                BaudRate (baudrate - BAUD_OFF),
  624.                DataBits (databits - DATA_OFF),
  625.                StopBits (stopbits - STOP_OFF),
  626.                Parity (parity - PARITY_OFF),
  627.             );
  628.          END;
  629.      
  630.          IF status = Success THEN
  631.             StartReceiving (comport - COM_OFF, BUFSIZE);
  632.             PrevComPort := comport;
  633.          ELSE
  634.             rc := WinMessageBox (HWND_DESKTOP, FrameWindow, ADR (PortError),
  635.                                  0, 0, MB_OKCANCEL + MB_ICONEXCLAMATION);
  636.             IF rc = MBID_OK THEN
  637.                WinPostMsg (FrameWindow, WM_QUIT, MPARAM (0), MPARAM (0));
  638.             ELSE   (* try the other port *)
  639.                IF comport = ID_COM1 THEN
  640.                   comport := ID_COM2;
  641.                ELSE
  642.                   comport := ID_COM1;
  643.                END;
  644.                SetPort;   (* recursive call for retry *)
  645.             END;
  646.          END;      
  647.          SetBanner;
  648.       END SetPort;
  649.  
  650.  
  651.    PROCEDURE MakeChild (msg : ARRAY OF CHAR);
  652.    (* Creates a child window for use by send or receive threads *)
  653.       
  654.       VAR
  655.          c_hdc : HDC;
  656.          
  657.       BEGIN
  658.          WinPostMsg (FrameWindow, WM_SETFULL, MPARAM (0), MPARAM (0));
  659.             
  660.          Disable (IDM_CONNECT);
  661.          Disable (IDM_SEND);
  662.          Disable (IDM_REC);
  663.          Disable (IDM_DIR);
  664.          Disable (IDM_OPTIONS);
  665.          Disable (IDM_COLORS);
  666.          
  667.          (* Create a client window *)     
  668.          FrameFlags := FCF_TITLEBAR + FCF_SIZEBORDER;
  669.          
  670.          ChildFrameWindow := WinCreateStdWindow (
  671.                 ClientWindow,        (* handle of the parent window *)
  672.                 WS_VISIBLE,          (* the window style *)
  673.                 FrameFlags,          (* the window flags *)
  674.                 ADR(Child),          (* the window class *)
  675.                 NULL,                (* the title bar text *)
  676.                 WS_VISIBLE,          (* client window style *)
  677.                 NULL,                (* handle of resource module *)
  678.                 IDM_KERMIT,          (* resource id *)
  679.                 ChildClientWindow    (* returned client window handle *)
  680.          );
  681.          
  682.          WinSetWindowPos (ChildFrameWindow, 0,
  683.             Pos.cx DIV 4, Pos.cy DIV 4, 
  684.             Pos.cx DIV 2, Pos.cy DIV 2 - 3,
  685.             SWP_MOVE + SWP_SIZE);
  686.          
  687.          WinSetWindowText (ChildFrameWindow, ADR (msg));
  688.  
  689.          WinSetActiveWindow (HWND_DESKTOP, ChildFrameWindow);
  690.                   
  691.          c_hdc := WinOpenWindowDC (ChildClientWindow);
  692.          hvps := child_hvps;
  693.          VioAssociate (c_hdc, hvps);
  694.          ClrScr;     (* clear the hvio window *)
  695.       END MakeChild;
  696.       
  697.  
  698.    PROCEDURE Disable (item : USHORT);
  699.    (* Disables and "GREYS" a menu item *)   
  700.    
  701.       VAR
  702.          h : HWND;
  703.          
  704.       BEGIN
  705.          h := WinWindowFromID (FrameWindow, FID_MENU);
  706.          MP1.W1 := item;   MP1.W2 := 1;
  707.          MP2.W1 := MIA_DISABLED;   MP2.W2 := MIA_DISABLED;
  708.          WinSendMsg (h, MM_SETITEMATTR, MP1, MP2);
  709.       END Disable;
  710.       
  711.       
  712.    PROCEDURE Enable (item : USHORT);
  713.    (* Enables a menu item *)
  714.    
  715.       VAR
  716.          h : HWND;
  717.          atr : USHORT;
  718.          
  719.       BEGIN
  720.          h := WinWindowFromID (FrameWindow, FID_MENU);
  721.          MP1.W1 := item;   MP1.W2 := 1;
  722.          MP2.W1 := MIA_DISABLED;   MP2.W2 := MIA_DISABLED;
  723.          atr := USHORT (WinSendMsg (h, MM_QUERYITEMATTR, MP1, MP2));
  724.          atr := USHORT (BITSET (atr) * (BITSET (MIA_DISABLED) / BITSET (-1)));                  
  725.          MP1.W1 := item;   MP1.W2 := 1;
  726.          MP2.W1 := MIA_DISABLED;   MP2.W2 := atr;
  727.          WinSendMsg (h, MM_SETITEMATTR, MP1, MP2);
  728.       END Enable;
  729.       
  730.                
  731.    PROCEDURE Check (item : USHORT);
  732.    (* Checks a menu item -- indicates that it is selected *)   
  733.    
  734.       VAR
  735.          h : HWND;
  736.          
  737.       BEGIN
  738.          h := WinWindowFromID (FrameWindow, FID_MENU);
  739.          MP1.W1 := item;   MP1.W2 := 1;
  740.          MP2.W1 := MIA_CHECKED;   MP2.W2 := MIA_CHECKED;
  741.          WinSendMsg (h, MM_SETITEMATTR, MP1, MP2);
  742.       END Check;
  743.       
  744.       
  745.    PROCEDURE UnCheck (item : USHORT);
  746.    (* Remove check from a menu item *)
  747.    
  748.       VAR
  749.          h : HWND;
  750.          atr : USHORT;
  751.          
  752.       BEGIN
  753.          h := WinWindowFromID (FrameWindow, FID_MENU);
  754.          MP1.W1 := item;   MP1.W2 := 1;
  755.          MP2.W1 := MIA_CHECKED;   MP2.W2 := MIA_CHECKED;
  756.          atr := USHORT (WinSendMsg (h, MM_QUERYITEMATTR, MP1, MP2));
  757.          atr := USHORT (BITSET (atr) * (BITSET (MIA_CHECKED) / BITSET (-1)));                  
  758.          MP1.W1 := item;   MP1.W2 := 1;
  759.          MP2.W1 := MIA_CHECKED;   MP2.W2 := atr;
  760.          WinSendMsg (h, MM_SETITEMATTR, MP1, MP2);
  761.       END UnCheck;
  762.       
  763.                
  764.    PROCEDURE DoMenu (hwnd : HWND; item [VALUE] : MPARAM);
  765.    (* Processes Most Menu Interactions *)
  766.    
  767.       VAR
  768.          rcl : RECTL;
  769.          rc : USHORT;
  770.          
  771.       BEGIN
  772.          CASE CARDINAL (item.W1) OF
  773.             IDM_DIR:
  774.                SetFull;
  775.                WinQueryWindowRect (hwnd, rcl);
  776.                WinDlgBox (HWND_DESKTOP, hwnd, PathDlgProc, 0, IDM_DIRPATH, 0);
  777.                hvps := frame_hvps;
  778.                VioAssociate (hdc, hvps);
  779.                Dir (Path);
  780.                WinDlgBox (HWND_DESKTOP, hwnd, DirEndDlgProc, 0, IDM_DIREND, 0);
  781.                VioAssociate (0, hvps);
  782.                WinInvalidateRect (hwnd, rcl, 0);
  783.          |  IDM_CONNECT:
  784.                TermMode := TRUE;
  785.                Disable (IDM_CONNECT);
  786.                Disable (IDM_SEND);
  787.                Disable (IDM_REC);
  788.                Disable (IDM_DIR);
  789.                Disable (IDM_OPTIONS);
  790.                Disable (IDM_COLORS);
  791.                (* MAXIMIZE Window -- Required for Terminal Emulation *)
  792.                SetMax;
  793.                hvps := frame_hvps;
  794.                VioAssociate (hdc, hvps);
  795.                DosResumeThread (TermThr);
  796.                InitTerm;
  797.          |  IDM_SEND:
  798.                WinDlgBox (HWND_DESKTOP, hwnd, SendFNDlgProc, 0, IDM_SENDFN, 0);
  799.                MakeChild ("Send a File");
  800.                DosCreateThread (Send, Thr, ADR (Stack[STKSIZE]));
  801.          |  IDM_REC:
  802.                MakeChild ("Receive a File"); 
  803.                DosCreateThread (Receive, Thr, ADR (Stack[STKSIZE]));
  804.          |  IDM_QUIT:
  805.                rc := WinMessageBox (HWND_DESKTOP, ClientWindow,
  806.                         ADR ("Do You Really Want To EXIT PCKermit?"),
  807.                         ADR ("End Session"), 0, MB_OKCANCEL + MB_ICONQUESTION);
  808.                IF rc = MBID_OK THEN
  809.                   StopReceiving (comport - COM_OFF);
  810.                   WinPostMsg (hwnd, WM_QUIT, MPARAM (0), MPARAM (0));
  811.                END;
  812.          |  IDM_COMPORT:
  813.                WinDlgBox (HWND_DESKTOP, hwnd, ComDlgProc, 0, IDM_COMPORT, 0);
  814.                SetPort;
  815.          |  IDM_BAUDRATE:
  816.                WinDlgBox (HWND_DESKTOP, hwnd, BaudDlgProc, 0, IDM_BAUDRATE, 0);
  817.                SetPort;
  818.          |  IDM_DATABITS:
  819.                WinDlgBox (HWND_DESKTOP, hwnd, DataDlgProc, 0, IDM_DATABITS, 0);
  820.                SetPort;
  821.          |  IDM_STOPBITS:
  822.                WinDlgBox (HWND_DESKTOP, hwnd, StopDlgProc, 0, IDM_STOPBITS, 0);
  823.                SetPort;
  824.          |  IDM_PARITY:
  825.                WinDlgBox (HWND_DESKTOP, hwnd, ParityDlgProc, 0, IDM_PARITY, 0);
  826.                SetPort;
  827.          |  IDM_WHITE:
  828.                UnCheck (ColorSet);
  829.                ColorSet := IDM_WHITE;
  830.                Check (ColorSet);
  831.                White;
  832.          |  IDM_GREEN:
  833.                UnCheck (ColorSet);
  834.                ColorSet := IDM_GREEN;
  835.                Check (ColorSet);
  836.                Green;
  837.          |  IDM_AMBER:
  838.                UnCheck (ColorSet);
  839.                ColorSet := IDM_AMBER;
  840.                Check (ColorSet);
  841.                Amber;
  842.          |  IDM_C1:
  843.                UnCheck (ColorSet);
  844.                ColorSet := IDM_C1;
  845.                Check (ColorSet);
  846.                Color1;
  847.          |  IDM_C2:   
  848.                UnCheck (ColorSet);
  849.                ColorSet := IDM_C2;
  850.                Check (ColorSet);
  851.                Color2;           
  852.          |  IDM_ABOUT:
  853.                WinDlgBox (HWND_DESKTOP, hwnd, AboutDlgProc, 0, IDM_ABOUT, 0);
  854.          ELSE
  855.             (* Don't do anything... *)
  856.          END;
  857.       END DoMenu;   
  858.  
  859.  
  860.    PROCEDURE ComDlgProc ['ComDlgProc'] (
  861.    (* Process Dialog Box for choosing COM1/COM2 *)
  862.          hwnd  : HWND;
  863.          msg   : USHORT;   
  864.          mp1   [VALUE] : MPARAM; 
  865.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  866.       BEGIN
  867.          CASE msg OF
  868.             WM_INITDLG:
  869.                WinSendDlgItemMsg (hwnd, comport, BM_SETCHECK, 
  870.                MPARAM (1), MPARAM (0));
  871.                WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, comport));
  872.                RETURN 1;
  873.          |  WM_CONTROL:
  874.                comport := mp1.W1;
  875.                RETURN 0;
  876.          |  WM_COMMAND:
  877.                WinDismissDlg (hwnd, 1);
  878.                RETURN 0;
  879.          ELSE
  880.             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  881.          END;
  882.       END ComDlgProc;
  883.    
  884.     
  885.    PROCEDURE BaudDlgProc ['BaudDlgProc'] (
  886.    (* Process Dialog Box for choosing Baud Rate *)
  887.          hwnd  : HWND;
  888.          msg   : USHORT;   
  889.          mp1   [VALUE] : MPARAM; 
  890.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  891.       BEGIN
  892.          WITH Settings[comport - COM_OFF] DO
  893.             CASE msg OF
  894.                WM_INITDLG:
  895.                   WinSendDlgItemMsg (hwnd, baudrate, BM_SETCHECK, 
  896.                                        MPARAM (1), MPARAM (0));
  897.                   WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, baudrate));
  898.                   RETURN 1;
  899.             |  WM_CONTROL:
  900.                   baudrate := mp1.W1;
  901.                   RETURN 0;
  902.             |  WM_COMMAND:
  903.                   WinDismissDlg (hwnd, 1);
  904.                   RETURN 0;
  905.             ELSE
  906.                RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  907.             END;
  908.          END;
  909.       END BaudDlgProc;
  910.    
  911.     
  912.    PROCEDURE DataDlgProc ['DataDlgProc'] (
  913.    (* Process Dialog Box for choosing 7 or 8 data bits *)
  914.          hwnd  : HWND;
  915.          msg   : USHORT;   
  916.          mp1   [VALUE] : MPARAM; 
  917.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  918.       BEGIN
  919.          WITH Settings[comport - COM_OFF] DO
  920.             CASE msg OF
  921.                WM_INITDLG:
  922.                   WinSendDlgItemMsg (hwnd, databits, BM_SETCHECK, 
  923.                                        MPARAM (1), MPARAM (0));
  924.                   WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, databits));
  925.                   RETURN 1;
  926.             |  WM_CONTROL:
  927.                   databits := mp1.W1;
  928.                   RETURN 0;
  929.             |  WM_COMMAND:
  930.                   WinDismissDlg (hwnd, 1);
  931.                   RETURN 0;
  932.             ELSE
  933.                RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  934.             END;
  935.          END;
  936.       END DataDlgProc;
  937.    
  938.     
  939.    PROCEDURE StopDlgProc ['StopDlgProc'] (
  940.    (* Process Dialog Box for choosing 1 or 2 stop bits *)
  941.          hwnd  : HWND;
  942.          msg   : USHORT;   
  943.          mp1   [VALUE] : MPARAM; 
  944.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  945.       BEGIN
  946.          WITH Settings[comport - COM_OFF] DO
  947.             CASE msg OF
  948.                WM_INITDLG:
  949.                   WinSendDlgItemMsg (hwnd, stopbits, BM_SETCHECK, 
  950.                                  MPARAM (1), MPARAM (0));
  951.                   WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, stopbits));
  952.                   RETURN 1;
  953.             |  WM_CONTROL:
  954.                   stopbits := mp1.W1;
  955.                   RETURN 0;
  956.             |  WM_COMMAND:
  957.                   WinDismissDlg (hwnd, 1);
  958.                   RETURN 0;
  959.             ELSE
  960.                RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  961.             END;
  962.          END;
  963.       END StopDlgProc;
  964.    
  965.     
  966.    PROCEDURE ParityDlgProc ['ParityDlgProc'] (
  967.    (* Process Dialog Box for choosing odd, even, or no parity *)
  968.          hwnd  : HWND;
  969.          msg   : USHORT;   
  970.          mp1   [VALUE] : MPARAM; 
  971.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  972.       BEGIN
  973.          WITH Settings[comport - COM_OFF] DO
  974.             CASE msg OF
  975.                WM_INITDLG:
  976.                   WinSendDlgItemMsg (hwnd, parity, BM_SETCHECK, 
  977.                                        MPARAM (1), MPARAM (0));
  978.                   WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, parity));
  979.                   RETURN 1;
  980.             |  WM_CONTROL:
  981.                   parity := mp1.W1;
  982.                   RETURN 0;
  983.             |  WM_COMMAND:
  984.                   WinDismissDlg (hwnd, 1);
  985.                   RETURN 0;
  986.             ELSE
  987.                RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  988.             END;
  989.          END;
  990.       END ParityDlgProc;
  991.    
  992.     
  993.    PROCEDURE AboutDlgProc ['AboutDlgProc'] (
  994.    (* Process "About" Dialog Box *)
  995.          hwnd  : HWND;
  996.          msg   : USHORT;   
  997.          mp1   [VALUE] : MPARAM; 
  998.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  999.       BEGIN
  1000.          IF msg = WM_COMMAND THEN
  1001.             WinDismissDlg (hwnd, 1);
  1002.             RETURN 0;
  1003.          ELSE
  1004.             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  1005.          END;
  1006.       END AboutDlgProc;
  1007.  
  1008.  
  1009.    PROCEDURE SendFNDlgProc ['SendFNDlgProc'] (
  1010.    (* Process Dialog Box that obtains send filename from user *)
  1011.          hwnd  : HWND;
  1012.          msg   : USHORT;   
  1013.          mp1   [VALUE] : MPARAM; 
  1014.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  1015.       BEGIN
  1016.          CASE msg OF
  1017.             WM_INITDLG:
  1018.                WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, ID_SENDFN));
  1019.                RETURN 1;
  1020.          |  WM_COMMAND:
  1021.                WinQueryDlgItemText (hwnd, ID_SENDFN, 20, ADR (sFname));
  1022.                WinDismissDlg (hwnd, 1);
  1023.                RETURN 0;
  1024.          ELSE
  1025.             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  1026.          END;
  1027.       END SendFNDlgProc;
  1028.       
  1029.  
  1030.    PROCEDURE PathDlgProc ['PathDlgProc'] (
  1031.    (* Process Dialog Box that obtains directory path from user *)
  1032.          hwnd  : HWND;
  1033.          msg   : USHORT;   
  1034.          mp1   [VALUE] : MPARAM; 
  1035.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  1036.       BEGIN
  1037.          CASE msg OF
  1038.             WM_INITDLG:
  1039.                WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, ID_DIRPATH));
  1040.                RETURN 1;
  1041.          |  WM_COMMAND:
  1042.                WinQueryDlgItemText (hwnd, ID_DIRPATH, 60, ADR (Path));
  1043.                WinDismissDlg (hwnd, 1);
  1044.                RETURN 0;
  1045.          ELSE
  1046.             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  1047.          END;
  1048.       END PathDlgProc;
  1049.  
  1050.  
  1051.    PROCEDURE DirEndDlgProc ['DirEndDlgProc'] (
  1052.    (* Process Dialog Box to allow user to cancel directory *)
  1053.          hwnd  : HWND;
  1054.          msg   : USHORT;   
  1055.          mp1   [VALUE] : MPARAM; 
  1056.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  1057.       BEGIN
  1058.          IF msg = WM_COMMAND THEN
  1059.             WinDismissDlg (hwnd, 1);
  1060.             RETURN 0;
  1061.          ELSE
  1062.             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  1063.          END;
  1064.       END DirEndDlgProc;
  1065.       
  1066.    
  1067.    PROCEDURE HelpDlgProc ['HelpDlgProc'] (
  1068.    (* Process Dialog Boxes for the HELP *)
  1069.          hwnd  : HWND;
  1070.          msg   : USHORT;   
  1071.          mp1   [VALUE] : MPARAM; 
  1072.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  1073.       BEGIN
  1074.          IF msg = WM_COMMAND THEN
  1075.             WinDismissDlg (hwnd, 1);
  1076.             RETURN 0;
  1077.          ELSE
  1078.             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
  1079.          END;
  1080.       END HelpDlgProc;
  1081.  
  1082.  
  1083.    PROCEDURE KeyTranslate (mp1, mp2 [VALUE] : MPARAM; VAR c1, c2 : CHAR) : BOOLEAN;
  1084.    (* Translates WM_CHAR message into ascii keystroke *)
  1085.    
  1086.       VAR
  1087.             code : CARDINAL;     
  1088.             fs : BITSET;    
  1089.             VK, KU, CH, CT : BOOLEAN;     
  1090.    
  1091.       BEGIN
  1092.          fs := BITSET (mp1.W1);     (* flags *)                
  1093.          VK := (fs * BITSET (KC_VIRTUALKEY)) # {};              
  1094.          KU := (fs * BITSET (KC_KEYUP)) # {};            
  1095.          CH := (fs * BITSET (KC_CHAR)) # {};              
  1096.          CT := (fs * BITSET (KC_CTRL)) # {};              
  1097.          IF (NOT KU) THEN             
  1098.             code := mp2.W1;    (* character code *)              
  1099.             c1 := CHR (code);              
  1100.             c2 := CHR (code DIV 256);             
  1101.             IF ORD (c1) = 0E0H THEN      (* function *)             
  1102.                c1 := 0C;               
  1103.             END;             
  1104.             IF CT AND (NOT CH) AND (NOT VK) AND (code # 0) THEN            
  1105.                c1 := CHR (CARDINAL ((BITSET (ORD (c1)) * BITSET (1FH))));
  1106.             END;             
  1107.             RETURN TRUE;
  1108.          ELSE
  1109.             RETURN FALSE;
  1110.          END;
  1111.       END KeyTranslate;
  1112.       
  1113.          
  1114.    PROCEDURE WindowProc ['WindowProc'] (
  1115.    (* Main Window Procedure -- Handles message from PM and elsewhere *)
  1116.          hwnd  : HWND;
  1117.          msg   : USHORT;   
  1118.          mp1   [VALUE] : MPARAM; 
  1119.          mp2   [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  1120.  
  1121.       VAR
  1122.          ch : CHAR;
  1123.          hps       : HPS;
  1124.          pswp      : PSWP;
  1125.          c1, c2    : CHAR;
  1126.          NullRectl [0:0] : RECTL;
  1127.          
  1128.       BEGIN
  1129.          CASE msg OF 
  1130.             WM_HELP:
  1131.                IF TermMode THEN
  1132.                   WinDlgBox (HWND_DESKTOP, hwnd, HelpDlgProc, 
  1133.                              0, IDM_TERMHELP, 0);
  1134.                ELSE
  1135.                   WinDlgBox (HWND_DESKTOP, hwnd, HelpDlgProc, 
  1136.                              0, IDM_HELPMENU, 0);
  1137.                END;
  1138.                RETURN 0;
  1139.          |  WM_SETFULL:
  1140.                SetFull;
  1141.                RETURN 0;
  1142.          |  WM_SETRESTORE:
  1143.                SetRestore;
  1144.                RETURN 0;
  1145.          |  WM_SETMAX:
  1146.                SetMax;
  1147.                RETURN 0;
  1148.          |  WM_MINMAXFRAME:
  1149.                pswp := PSWP (mp1);
  1150.                IF BITSET (pswp^.fs) * BITSET (SWP_MINIMIZE) # {} THEN
  1151.                   (* Don't Display Port Settings While Minimized *)
  1152.                   WinSetWindowText (FrameWindow, ADR (Title));
  1153.                ELSE
  1154.                   WinSetWindowText (FrameWindow, ADR (Banner));
  1155.                   IF TermMode AND
  1156.                    (BITSET (pswp^.fs) * BITSET (SWP_RESTORE) # {}) THEN
  1157.                      (* Force window to be maximized in terminal mode *)
  1158.                      
  1159.                      WinPostMsg (FrameWindow, WM_SETMAX, 
  1160.                                  MPARAM (0), MPARAM (0));
  1161.                   ELSIF (NOT TermMode) AND
  1162.                    (BITSET (pswp^.fs) * BITSET (SWP_MAXIMIZE) # {}) THEN
  1163.                      (* Prevent maximized window EXCEPT in terminal mode *)
  1164.                      WinPostMsg (FrameWindow, WM_SETRESTORE, 
  1165.                                  MPARAM (0), MPARAM (0));
  1166.                   ELSE
  1167.                      (* Do Nothing *)
  1168.                   END;
  1169.                END;
  1170.                RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
  1171.          |  WM_CREATE:
  1172.                hdc := WinOpenWindowDC (hwnd);
  1173.                VioCreatePS (frame_hvps, 25, 80, 0, FORMAT_CGA, 0);
  1174.                VioCreatePS (child_hvps, 16, 40, 0, FORMAT_CGA, 0);
  1175.                DosCreateThread (TermThrProc, TermThr, ADR (TermStack[STKSIZE]));
  1176.                DosSuspendThread (TermThr);
  1177.                RETURN 0;
  1178.          |  WM_INITMENU:
  1179.                Check (ColorSet);
  1180.                RETURN 0;
  1181.          |  WM_COMMAND: 
  1182.                DoMenu (hwnd, mp1);
  1183.                RETURN 0;
  1184.          |  WM_TERMQUIT:
  1185.                TermMode := FALSE;
  1186.                DosSuspendThread (TermThr);
  1187.                VioAssociate (0, hvps);
  1188.                (* Restore The Window *)
  1189.                SetRestore;
  1190.                Enable (IDM_CONNECT);
  1191.                Enable (IDM_SEND);
  1192.                Enable (IDM_REC);
  1193.                Enable (IDM_DIR);
  1194.                Enable (IDM_OPTIONS);
  1195.                Enable (IDM_COLORS);
  1196.                RETURN 0;
  1197.          |  WM_TERM:
  1198.                PutPortChar (CHR (mp1.W1));   (* To Screen *)
  1199.                RETURN 0;
  1200.          |  WM_CHAR:
  1201.                IF TermMode THEN
  1202.                   IF KeyTranslate (mp1, mp2, c1, c2) THEN
  1203.                      PutKbdChar (c1, c2);   (* To Port *)
  1204.                      RETURN 0;
  1205.                   ELSE
  1206.                      RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2);
  1207.                   END;
  1208.                ELSE
  1209.                   RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
  1210.                END;
  1211.          |  WM_PAINT:
  1212.                hps := WinBeginPaint (hwnd, NULL, NullRectl);
  1213.                GpiErase (hps);
  1214.                VioShowPS (25, 80, 0, hvps); 
  1215.                WinEndPaint (hps);
  1216.                RETURN 0;
  1217.          |  WM_SIZE:
  1218.                IF TermMode THEN
  1219.                   RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2);
  1220.                ELSE
  1221.                   RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
  1222.                END;
  1223.          |  WM_DESTROY:
  1224.                VioDestroyPS (frame_hvps);
  1225.                VioDestroyPS (child_hvps);
  1226.                RETURN 0;
  1227.          ELSE
  1228.             RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
  1229.          END;
  1230.       END WindowProc;
  1231.       
  1232.  
  1233.    PROCEDURE ChildWindowProc ['ChildWindowProc'] (
  1234.    (* Window Procedure for Send/Receive child windows *)
  1235.       hwnd : HWND;
  1236.       msg  : USHORT;   
  1237.       mp1  [VALUE] : MPARAM; 
  1238.       mp2  [VALUE] : MPARAM) : MRESULT [LONG, WINDOWS];
  1239.       
  1240.       VAR
  1241.          mp : USHORT;
  1242.          hps : HPS;
  1243.          c1, c2 : CHAR;
  1244.          NullRectl [0:0] : RECTL;
  1245.       
  1246.       BEGIN
  1247.          CASE msg OF
  1248.             WM_PAINT:
  1249.                hps := WinBeginPaint (hwnd, NULL, NullRectl);
  1250.                GpiErase (hps);
  1251.                VioShowPS (16, 40, 0, hvps); 
  1252.                WinEndPaint (hps);
  1253.                RETURN 0;
  1254.          |  WM_CHAR:
  1255.                IF KeyTranslate (mp1, mp2, c1, c2) AND (c1 = ESC) THEN
  1256.                   Aborted := TRUE;
  1257.                   RETURN 0;
  1258.                ELSE
  1259.                   RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
  1260.                END;
  1261.          |  WM_PAD:
  1262.                mp := mp1.W1;
  1263.                IF (mp = PAD_Error) OR (mp = PAD_Quit) THEN
  1264.                   WriteLn;
  1265.                   IF mp = PAD_Error THEN
  1266.                      WinMessageBox (HWND_DESKTOP, hwnd, 
  1267.                                     ADR ("File Transfer Aborted"),
  1268.                                     ADR (Class), 0, MB_OK + MB_ICONEXCLAMATION);
  1269.                   ELSE
  1270.                      WinMessageBox (HWND_DESKTOP, hwnd, 
  1271.                                        ADR ("File Transfer Completed"),
  1272.                                        ADR (Class), 0, MB_OK + MB_ICONASTERISK);
  1273.                   END;
  1274.                   DosSleep (2000);
  1275.                   VioAssociate (0, hvps);
  1276.                   WinDestroyWindow(ChildFrameWindow);
  1277.                   Enable (IDM_CONNECT);
  1278.                   Enable (IDM_SEND);
  1279.                   Enable (IDM_REC);
  1280.                   Enable (IDM_DIR);
  1281.                   Enable (IDM_OPTIONS);
  1282.                   Enable (IDM_COLORS);
  1283.                ELSE
  1284.                   DoPADMsg (mp1, mp2);
  1285.                END;
  1286.                RETURN 0;
  1287.          |  WM_DL:
  1288.                DoDLMsg (mp1, mp2);
  1289.                RETURN 0;
  1290.          |  WM_SIZE:
  1291.                WinSetWindowPos (ChildFrameWindow, 0,
  1292.                   Pos.cx DIV 4, Pos.cy DIV 4, 
  1293.                   Pos.cx DIV 2, Pos.cy DIV 2 - 3,
  1294.                   SWP_MOVE + SWP_SIZE);
  1295.                RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2);
  1296.          ELSE
  1297.             RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
  1298.          END;
  1299.       END ChildWindowProc;
  1300.  
  1301.  
  1302. BEGIN   (* Module Initialization *)
  1303.     WITH Settings[ID_COM1 - COM_OFF] DO
  1304.        baudrate := ID_B1200;
  1305.        parity := ID_EVEN;
  1306.        databits := ID_DATA7;
  1307.        stopbits := ID_STOP1;
  1308.     END;
  1309.     
  1310.     WITH Settings[ID_COM2 - COM_OFF] DO
  1311.        baudrate := ID_B19K2;
  1312.        parity := ID_EVEN;
  1313.        databits := ID_DATA7;
  1314.        stopbits := ID_STOP1;
  1315.     END;
  1316.     PrevComPort := NONE;
  1317.     comport := ID_COM1;
  1318.     TermMode := FALSE;   (* Not Initially in Terminal Emulation Mode *)
  1319. END Shell.
  1320.  
  1321. [LISTING 10 - PART II]
  1322.  
  1323. IMPLEMENTATION MODULE Term;   (* TVI950 Terminal Emulation for Kermit *)
  1324.  
  1325.    FROM Drives IMPORT
  1326.       SetDrive;
  1327.       
  1328.    FROM Directories IMPORT
  1329.       FileAttributes, AttributeSet, DirectoryEntry, FindFirst, FindNext;
  1330.       
  1331.    FROM SYSTEM IMPORT
  1332.       ADR;
  1333.  
  1334.    FROM DosCalls IMPORT
  1335.       DosChDir, DosSleep;
  1336.             
  1337.    FROM Screen IMPORT
  1338.       ClrScr, ClrEol, GotoXY, GetXY,
  1339.       Right, Left, Up, Down, WriteAtt, WriteString, WriteLn, Write,
  1340.       attribute, NORMAL, HIGHLIGHT, REVERSE;        
  1341.       
  1342.    FROM PMWIN IMPORT
  1343.       MPARAM, WinPostMsg;
  1344.  
  1345.    FROM Shell IMPORT
  1346.       comport, FrameWindow;
  1347.       
  1348.    FROM KH IMPORT
  1349.       COM_OFF;
  1350.             
  1351.    FROM CommPort IMPORT
  1352.       CommStatus, GetChar, SendChar;
  1353.             
  1354.    FROM Strings IMPORT
  1355.       Length, Concat;
  1356.    
  1357.    IMPORT ASCII;
  1358.  
  1359.  
  1360.    CONST
  1361.       (* Key codes:  Note: F1 -- F12 are actually Shift-F1 -- Shift-F12 *)
  1362.       F1 = 124C;
  1363.       F2 = 125C;
  1364.       F3 = 126C;
  1365.       F4 = 127C;
  1366.       F5 = 130C;
  1367.       F6 = 131C;
  1368.       F7 = 132C;
  1369.       F8 = 133C;
  1370.       F9 = 134C;
  1371.       F10 = 135C;
  1372.       F11 = 207C;
  1373.       F12 = 210C;
  1374.       AF1 = 213C;   (* Alt-F1 *)
  1375.       AF2 = 214C;   (* Alt-F2 *)
  1376.       INS = 122C;
  1377.       DEL = 123C;
  1378.       HOME = 107C;
  1379.       PGDN = 121C;   (* synonym for PF10 *)
  1380.       PGUP = 111C;   (* synonym for PF11 *)
  1381.       ENDD = 117C;   (* synonym for PF12 *)
  1382.       UPARROW = 110C;
  1383.       DOWNARROW = 120C;
  1384.       LEFTARROW = 113C;
  1385.       RIGHTARROW = 115C;
  1386.       CtrlX = 30C;
  1387.       CtrlCaret = 36C;
  1388.       CtrlZ = 32C;
  1389.       CtrlL = 14C;
  1390.       CtrlH = 10C;
  1391.       CtrlK = 13C;
  1392.       CtrlJ = 12C;
  1393.       CtrlV = 26C;
  1394.       ESC = 33C;
  1395.       BUFSIZE = 4096;   (* character buffer used by term thread *)
  1396.  
  1397.    
  1398.    VAR
  1399.       commStat : CommStatus;
  1400.       echo : (Off, Local, On);      
  1401.       newline: BOOLEAN;   (* translate <cr> to <cr><lf> *)
  1402.       Insert : BOOLEAN;
  1403.       MP1, MP2 : MPARAM;
  1404.                   
  1405.  
  1406.    PROCEDURE Dir (path : ARRAY OF CHAR);
  1407.    (* Change drive and/or directory; display a directory (in wide format) *)
  1408.    
  1409.       VAR
  1410.          gotFN : BOOLEAN;
  1411.          filename : ARRAY [0..20] OF CHAR;
  1412.          attr : AttributeSet;
  1413.          ent : DirectoryEntry;
  1414.          i, j, k : INTEGER;
  1415.          
  1416.       BEGIN
  1417.          filename := "";   (* in case no directory change *)
  1418.          i := Length (path);
  1419.          IF (i > 2) AND (path[1] = ':') THEN   (* drive specifier *)
  1420.             DEC (i, 2);
  1421.             SetDrive (ORD (CAP (path[0])) - ORD ('A')); 
  1422.             FOR j := 0 TO i DO   (* strip off the drive specifier *)
  1423.                path[j] := path[j + 2];
  1424.             END;
  1425.          END;
  1426.          IF i # 0 THEN
  1427.             gotFN := FALSE;
  1428.             WHILE (i >= 0) AND (path[i] # '\') DO
  1429.                IF path[i] = '.' THEN
  1430.                   gotFN := TRUE;
  1431.                END;
  1432.                DEC (i);
  1433.             END;
  1434.             IF gotFN THEN
  1435.                j := i + 1;
  1436.                k := 0;
  1437.                WHILE path[j] # 0C DO
  1438.                   filename[k] := path[j];
  1439.                   INC (k);       INC (j);
  1440.                END;
  1441.                filename[k] := 0C;
  1442.                IF (i = -1) OR ((i = 0) AND (path[0] = '\')) THEN
  1443.                   INC (i);
  1444.                END;
  1445.                path[i] := 0C;
  1446.             END;
  1447.          END;
  1448.          IF Length (path) # 0 THEN
  1449.             DosChDir (ADR (path), 0);
  1450.          END;
  1451.          IF Length (filename) = 0 THEN
  1452.             filename := "*.*";
  1453.          END;
  1454.          attr := AttributeSet {ReadOnly, Directory, Archive};
  1455.          i := 1;   (* keep track of position on line *)
  1456.  
  1457.          ClrScr;         
  1458.          gotFN := FindFirst (filename, attr, ent);
  1459.          WHILE gotFN DO
  1460.             WriteString (ent.name);
  1461.             j := Length (ent.name);
  1462.             WHILE j < 12 DO   (* 12 is maximum length for "filename.typ" *)
  1463.                Write (' ');
  1464.                INC (j);
  1465.             END;
  1466.             INC (i);   (* next position on this line *)
  1467.             IF i > 5 THEN
  1468.                i := 1;   (* start again on new line *)
  1469.                WriteLn;
  1470.             ELSE
  1471.                WriteString (" | ");
  1472.             END;
  1473.             gotFN := FindNext (ent);
  1474.          END;
  1475.          WriteLn;
  1476.       END Dir;
  1477.   
  1478.  
  1479.    PROCEDURE InitTerm;
  1480.    (* Clear Screen, Home Cursor, Get Ready For Terminal Emulation *)
  1481.       BEGIN
  1482.          ClrScr;
  1483.          Insert := FALSE;
  1484.          attribute := NORMAL;
  1485.       END InitTerm;   
  1486.  
  1487.  
  1488.    PROCEDURE PutKbdChar (ch1, ch2 : CHAR);
  1489.    (* Process a character received from the keyboard *)
  1490.       BEGIN
  1491.          IF ch1 = ASCII.enq THEN   (* Control-E *)
  1492.             echo := On;
  1493.          ELSIF ch1 = ASCII.ff THEN   (* Control-L *)
  1494.             echo := Local;
  1495.          ELSIF ch1 = ASCII.dc4 THEN   (* Control-T *)
  1496.             echo := Off;
  1497.          ELSIF ch1 = ASCII.so THEN   (* Control-N *)
  1498.             newline := TRUE;
  1499.          ELSIF ch1 = ASCII.si THEN   (* Control-O *)
  1500.             newline := FALSE;
  1501.          ELSIF (ch1 = ASCII.can) OR (ch1 = ESC) THEN
  1502.             attribute := NORMAL;
  1503.             WinPostMsg (FrameWindow, WM_TERMQUIT, MPARAM (0), MPARAM (0));
  1504.          ELSIF ch1 = 0C THEN
  1505.             Function (ch2);
  1506.          ELSE
  1507.             commStat := SendChar (comport - COM_OFF, ch1, FALSE);
  1508.             IF (echo = On) OR (echo = Local) THEN
  1509.                WriteAtt (ch1);
  1510.             END;
  1511.          END;
  1512.       END PutKbdChar;
  1513.  
  1514.  
  1515.    PROCEDURE Function (ch : CHAR);
  1516.    (* handles the function keys -- including PF1 - PF12, etc. *)
  1517.       BEGIN
  1518.          CASE ch OF
  1519.             F1 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1520.                   commStat := SendChar (comport - COM_OFF, '@', FALSE);   
  1521.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1522.          |  F2 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1523.                   commStat := SendChar (comport - COM_OFF, 'A', FALSE);   
  1524.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1525.          |  F3 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1526.                   commStat := SendChar (comport - COM_OFF, 'B', FALSE);   
  1527.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1528.          |  F4 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1529.                   commStat := SendChar (comport - COM_OFF, 'C', FALSE);   
  1530.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1531.          |  F5 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1532.                   commStat := SendChar (comport - COM_OFF, 'D', FALSE);   
  1533.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1534.          |  F6 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1535.                   commStat := SendChar (comport - COM_OFF, 'E', FALSE);   
  1536.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1537.          |  F7 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1538.                   commStat := SendChar (comport - COM_OFF, 'F', FALSE);   
  1539.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1540.          |  F8 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1541.                   commStat := SendChar (comport - COM_OFF, 'G', FALSE);   
  1542.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1543.          |  F9 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1544.                   commStat := SendChar (comport - COM_OFF, 'H', FALSE);   
  1545.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1546.          |  F10, 
  1547.             PGDN: commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1548.                   commStat := SendChar (comport - COM_OFF, 'I', FALSE);   
  1549.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1550.          |  F11,
  1551.             AF1,
  1552.             PGUP: commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
  1553.                   commStat := SendChar (comport - COM_OFF, 'J', FALSE);   
  1554.                   commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
  1555.          |  F12,
  1556.             AF2,
  1557.             ENDD: commStat := SendChar (comport - COM_OFF, ESC, FALSE);
  1558.                   commStat := SendChar (comport - COM_OFF, 'Q', FALSE);
  1559.          |  INS : IF NOT Insert THEN
  1560.                      commStat := SendChar (comport - COM_OFF, ESC, FALSE);
  1561.                      commStat := SendChar (comport - COM_OFF, 'E', FALSE);
  1562.                   END;
  1563.          |  DEL : commStat := SendChar (comport - COM_OFF, ESC, FALSE);
  1564.                   commStat := SendChar (comport - COM_OFF, 'R', FALSE);
  1565.          |  HOME       : commStat := SendChar (comport - COM_OFF, CtrlZ, FALSE);
  1566.          |  UPARROW    : commStat := SendChar (comport - COM_OFF, CtrlK, FALSE);
  1567.          |  DOWNARROW  : commStat := SendChar (comport - COM_OFF, CtrlV, FALSE);
  1568.          |  LEFTARROW  : commStat := SendChar (comport - COM_OFF, CtrlH, FALSE);
  1569.          |  RIGHTARROW : commStat := SendChar (comport - COM_OFF, CtrlL, FALSE);
  1570.          ELSE
  1571.             (* do nothing *)
  1572.          END;
  1573.       END Function;
  1574.  
  1575.       
  1576.    PROCEDURE TermThrProc;
  1577.    (* Thread to get characters from port, put into buffer *)
  1578.    
  1579.       VAR
  1580.          ch : CHAR;
  1581.          
  1582.       BEGIN
  1583.          LOOP
  1584.             IF GetChar (comport - COM_OFF, ch) = Success THEN
  1585.                MP1.W1 := ORD (ch);   MP1.W2 := 0;
  1586.                MP2.L := 0;
  1587.                WinPostMsg (FrameWindow, WM_TERM, MP1, MP2);
  1588.             ELSE
  1589.                DosSleep (0);
  1590.             END
  1591.          END;
  1592.       END TermThrProc;
  1593.  
  1594.  
  1595.    VAR
  1596.       EscState, CurState1, CurState2 : BOOLEAN;
  1597.       CurChar1 : CHAR;
  1598.       
  1599.    PROCEDURE PutPortChar (ch : CHAR);
  1600.    (* Process a character received from the port *)
  1601.       BEGIN
  1602.          IF EscState THEN
  1603.             EscState := FALSE;
  1604.             IF ch = '=' THEN
  1605.                CurState1 := TRUE;
  1606.             ELSE
  1607.                Escape (ch);
  1608.             END;
  1609.          ELSIF CurState1 THEN
  1610.             CurState1 := FALSE;
  1611.             CurChar1 := ch;
  1612.             CurState2 := TRUE;
  1613.          ELSIF CurState2 THEN
  1614.             CurState2 := FALSE;
  1615.             Cursor (ch);
  1616.          ELSE
  1617.             CASE ch OF
  1618.                CtrlCaret, CtrlZ : ClrScr;
  1619.             |  CtrlL : Right;
  1620.             |  CtrlH : Left;
  1621.             |  CtrlK : Up;
  1622.             |  CtrlJ : Down;
  1623.             |  ESC   : EscState := TRUE;
  1624.             ELSE
  1625.                WriteAtt (ch);
  1626.                IF newline AND (ch = ASCII.cr) THEN
  1627.                   WriteLn;
  1628.                END;
  1629.             END;
  1630.          END;
  1631.          IF echo = On THEN
  1632.             commStat := SendChar (comport - COM_OFF, ch, FALSE);
  1633.          END;
  1634.       END PutPortChar;
  1635.       
  1636.       
  1637.    PROCEDURE Escape (ch : CHAR);
  1638.    (* handles escape sequences *)
  1639.       BEGIN
  1640.          CASE ch OF
  1641.             '*' : ClrScr;
  1642.          |  'T', 'R' : ClrEol;
  1643.          |  ')' : attribute := NORMAL;
  1644.          |  '(' : attribute := HIGHLIGHT;   
  1645.          |  'f' : InsertMsg;
  1646.          |  'g' : InsertOn;
  1647.          ELSE
  1648.             (* ignore *)
  1649.          END;
  1650.       END Escape;
  1651.       
  1652.       
  1653.    PROCEDURE Cursor (ch : CHAR);
  1654.    (* handles cursor positioning *)
  1655.    
  1656.       VAR
  1657.          x, y : CARDINAL;
  1658.          
  1659.       BEGIN
  1660.          y := ORD (CurChar1) - 20H;
  1661.          x := ORD (ch) - 20H;
  1662.          GotoXY (x, y);   (* adjust for HOME = (1, 1) *)
  1663.       END Cursor;
  1664.       
  1665.       
  1666.    VAR
  1667.       cx, cy : CARDINAL;
  1668.       
  1669.    PROCEDURE InsertMsg;
  1670.    (* get ready insert mode -- place a message at the bottom of the screen *)
  1671.       BEGIN
  1672.          IF NOT Insert THEN
  1673.             GetXY (cx, cy);   (* record current position *)
  1674.             GotoXY (1, 24);
  1675.             ClrEol;
  1676.             attribute := REVERSE;
  1677.          ELSE   (* exit Insert mode *)
  1678.             GetXY (cx, cy);
  1679.             GotoXY (1, 24);
  1680.             ClrEol;
  1681.             GotoXY (cx, cy);
  1682.             Insert := FALSE;
  1683.          END;
  1684.       END InsertMsg;   
  1685.       
  1686.       
  1687.    PROCEDURE InsertOn;
  1688.    (* enter insert mode -- after INSERT MODE message is printed *)
  1689.       BEGIN
  1690.          attribute := NORMAL;
  1691.          GotoXY (cx, cy);
  1692.          Insert := TRUE;
  1693.       END InsertOn;   
  1694.       
  1695.  
  1696. BEGIN   (* module initialization *)
  1697.    echo := Off;
  1698.    newline := FALSE;
  1699.    Insert := FALSE;
  1700.    EscState := FALSE;
  1701.    CurState1 := FALSE;
  1702.    CurState2 := FALSE;
  1703. END Term.
  1704.  
  1705. [LISTING ELEVEN]
  1706.  
  1707. IMPLEMENTATION MODULE Screen;
  1708. (* module to perform "low level" screen functions (via AVIO) *)
  1709.  
  1710.    IMPORT ASCII;
  1711.    
  1712.    FROM SYSTEM IMPORT
  1713.       ADR;
  1714.  
  1715.    FROM Strings IMPORT
  1716.       Length;
  1717.       
  1718.    FROM Conversions IMPORT
  1719.       IntToString;
  1720.  
  1721.    FROM KH IMPORT
  1722.       IDM_GREEN;
  1723.                   
  1724.    FROM Vio IMPORT
  1725.       VioSetCurPos, VioGetCurPos, VioScrollUp, 
  1726.       VioWrtNCell, VioWrtTTY, VioCell;
  1727.  
  1728.  
  1729.    CONST
  1730.       GREY = 07H;
  1731.       WHITE = 0FH;
  1732.       REV_GY = 70H;
  1733.       GREEN = 02H;
  1734.       LITE_GRN = 0AH;
  1735.       REV_GRN = 20H;
  1736.       AMBER = 06H;
  1737.       LITE_AMB = 0EH;
  1738.       REV_AMB = 60H;
  1739.       RED = 0CH;
  1740.       CY_BK = 0B0H;
  1741.       CY_BL = 0B9H;
  1742.       REV_RD = 0CFH;
  1743.       REV_BL = 9FH;
  1744.       MAGENTA = 05H;
  1745.       
  1746.             
  1747.    VAR    
  1748.       (* From Definition Module
  1749.       NORMAL : CARDINAL;
  1750.       HIGHLIGHT : CARDINAL;
  1751.       REVERSE : CARDINAL;
  1752.         attribute : CARDINAL;    
  1753.       hvps : HVPS;
  1754.       *)
  1755.        x, y : CARDINAL;     
  1756.        bCell : VioCell;     
  1757.       
  1758.  
  1759.    PROCEDURE White;
  1760.    (* Sets up colors: Monochrome White *)
  1761.       BEGIN
  1762.          NORMAL := GREY;
  1763.          HIGHLIGHT := WHITE;
  1764.          REVERSE := REV_GY;
  1765.          attribute := NORMAL;
  1766.       END White;
  1767.       
  1768.       
  1769.    PROCEDURE Green;
  1770.    (* Sets up colors: Monochrome Green *)
  1771.       BEGIN
  1772.          NORMAL := GREEN;
  1773.          HIGHLIGHT := LITE_GRN;
  1774.          REVERSE := REV_GRN;
  1775.          attribute := NORMAL;
  1776.       END Green;
  1777.       
  1778.       
  1779.    PROCEDURE Amber;
  1780.    (* Sets up colors: Monochrome Amber *)
  1781.       BEGIN
  1782.          NORMAL := AMBER;
  1783.          HIGHLIGHT := LITE_AMB;
  1784.          REVERSE := REV_AMB;
  1785.          attribute := NORMAL;
  1786.       END Amber;
  1787.       
  1788.       
  1789.    PROCEDURE Color1;
  1790.    (* Sets up colors: Blue, Red, Green *)
  1791.       BEGIN
  1792.          NORMAL := GREEN;
  1793.          HIGHLIGHT := RED;
  1794.          REVERSE := REV_BL;
  1795.          attribute := NORMAL;
  1796.       END Color1;
  1797.       
  1798.       
  1799.    PROCEDURE Color2;
  1800.    (* Sets up colors: Cyan Background; Black, Blue, White-on-Red *)
  1801.       BEGIN
  1802.          NORMAL := CY_BK;
  1803.          HIGHLIGHT := CY_BL;
  1804.          REVERSE := REV_RD;
  1805.          attribute := NORMAL;
  1806.       END Color2;
  1807.       
  1808.       
  1809.    PROCEDURE HexToString (num : INTEGER;
  1810.                           size : CARDINAL;
  1811.                           VAR buf : ARRAY OF CHAR;
  1812.                           VAR I : CARDINAL;
  1813.                           VAR Done : BOOLEAN);
  1814.    (* Local Procedure to convert a number to a string, represented in HEX *)   
  1815.    
  1816.       CONST
  1817.          ZERO = 30H;   (* ASCII code *)
  1818.          A = 41H; 
  1819.          
  1820.       VAR
  1821.          i : CARDINAL;
  1822.          h : CARDINAL;
  1823.          t : ARRAY [0..10] OF CHAR;
  1824.                                 
  1825.       BEGIN
  1826.          i := 0;
  1827.          REPEAT
  1828.             h := num MOD 16;
  1829.             IF h <= 9 THEN
  1830.                t[i] := CHR (h + ZERO);
  1831.             ELSE
  1832.                t[i] := CHR (h - 10 + A);
  1833.             END;
  1834.             INC (i);
  1835.             num := num DIV 16;
  1836.          UNTIL num = 0;
  1837.          
  1838.          IF (size > HIGH (buf)) OR (i > HIGH (buf)) THEN
  1839.             Done := FALSE;
  1840.             RETURN;
  1841.          ELSE
  1842.             Done := TRUE;
  1843.          END;
  1844.          
  1845.          WHILE size > i DO
  1846.             buf[I] := '0';   (* pad with zeros *)
  1847.             DEC (size);
  1848.             INC (I);
  1849.          END;
  1850.          
  1851.          WHILE i > 0 DO
  1852.             DEC (i);
  1853.             buf[I] := t[i];
  1854.             INC (I);
  1855.          END;
  1856.          
  1857.          buf[I] := 0C;
  1858.       END HexToString;
  1859.                                 
  1860.    
  1861.    PROCEDURE ClrScr;      
  1862.    (* Clear the screen, and home the cursor *)     
  1863.       BEGIN      
  1864.          bCell.ch := ' ';     (* space = blank screen *)    
  1865.          bCell.attr := CHR (NORMAL);    (* Normal Video Attribute *)     
  1866.          VioScrollUp (0, 0, 24, 79, 25, bCell, hvps);      
  1867.          GotoXY (0, 0);      
  1868.       END ClrScr;     
  1869.  
  1870.  
  1871.  
  1872.    PROCEDURE ClrEol;     
  1873.    (* clear from the current cursor position to the end of the line *)    
  1874.       BEGIN     
  1875.          GetXY (x, y);     (* current cursor position *)    
  1876.          bCell.ch := ' ';    (* space = blank *)     
  1877.          bCell.attr := CHR (NORMAL);   (* Normal Video Attribute *)    
  1878.          VioScrollUp (y, x, y, 79, 1, bCell, hvps);   
  1879.       END ClrEol;     
  1880.    
  1881.    
  1882.    PROCEDURE Right;    
  1883.    (* move cursor to the right *)   
  1884.       BEGIN     
  1885.          GetXY (x, y);    
  1886.          INC (x);     
  1887.          GotoXY (x, y);     
  1888.       END Right;    
  1889.    
  1890.    
  1891.    PROCEDURE Left;   
  1892.    (* move cursor to the left *)     
  1893.       BEGIN     
  1894.          GetXY (x, y);    
  1895.          DEC (x);     
  1896.          GotoXY (x, y);     
  1897.       END Left;   
  1898.    
  1899.    
  1900.    PROCEDURE Up;    
  1901.    (* move cursor up *)     
  1902.       BEGIN     
  1903.          GetXY (x, y);    
  1904.          DEC (y);     
  1905.          GotoXY (x, y);     
  1906.       END Up;    
  1907.    
  1908.    
  1909.    PROCEDURE Down;   
  1910.    (* move cursor down *)    
  1911.       BEGIN     
  1912.          GetXY (x, y);    
  1913.          INC (y);     
  1914.          GotoXY (x, y);     
  1915.       END Down;   
  1916.    
  1917.    
  1918.    PROCEDURE GotoXY (col, row : CARDINAL);   
  1919.    (* position cursor at column, row *)   
  1920.       BEGIN     
  1921.          IF (col <= 79) AND (row <= 24) THEN     
  1922.             VioSetCurPos (row, col, hvps);   
  1923.          END;    
  1924.       END GotoXY;     
  1925.    
  1926.    
  1927.    PROCEDURE GetXY (VAR col, row : CARDINAL);   
  1928.    (* determine current cursor position *)   
  1929.       BEGIN     
  1930.          VioGetCurPos (row, col, hvps);   
  1931.       END GetXY;    
  1932.    
  1933.  
  1934.    PROCEDURE Write (c : CHAR);
  1935.    (* Write a Character *)
  1936.       BEGIN
  1937.          WriteAtt (c);
  1938.       END Write;
  1939.       
  1940.       
  1941.    PROCEDURE WriteString (str : ARRAY OF CHAR);
  1942.    (* Write String *)
  1943.    
  1944.       VAR
  1945.          i : CARDINAL;
  1946.          c : CHAR;
  1947.          
  1948.       BEGIN
  1949.          i := 0;
  1950.          c := str[i];
  1951.          WHILE c # 0C DO
  1952.             Write (c);
  1953.             INC (i);
  1954.             c := str[i];
  1955.          END;
  1956.       END WriteString;
  1957.  
  1958.       
  1959.    PROCEDURE WriteInt (n : INTEGER; s : CARDINAL);
  1960.    (* Write Integer *)
  1961.    
  1962.       VAR
  1963.          i : CARDINAL;
  1964.          b : BOOLEAN;
  1965.          str : ARRAY [0..6] OF CHAR;
  1966.          
  1967.       BEGIN
  1968.          i := 0;
  1969.          IntToString (n, s, str, i, b);
  1970.          WriteString (str);
  1971.       END WriteInt;
  1972.       
  1973.    
  1974.    PROCEDURE WriteHex (n, s : CARDINAL);
  1975.    (* Write a Hexadecimal Number *)
  1976.    
  1977.       VAR
  1978.          i : CARDINAL;
  1979.          b : BOOLEAN;
  1980.          str : ARRAY [0..6] OF CHAR;
  1981.          
  1982.       BEGIN
  1983.          i := 0;
  1984.          HexToString (n, s, str, i, b);
  1985.          WriteString (str);
  1986.       END WriteHex;
  1987.       
  1988.    
  1989.    PROCEDURE WriteLn;
  1990.    (* Write <cr> <lf> *)
  1991.       BEGIN
  1992.          Write (ASCII.cr);   Write (ASCII.lf); 
  1993.       END WriteLn;
  1994.    
  1995.    
  1996.    PROCEDURE WriteAtt (c : CHAR);   
  1997.    (* write character and attribute at cursor position *)   
  1998.    
  1999.       VAR   
  2000.          s : ARRAY [0..1] OF CHAR;    
  2001.  
  2002.       BEGIN     
  2003.          GetXY (x, y);
  2004.          IF (c = ASCII.ht) THEN
  2005.             bCell.ch := ' ';
  2006.             bCell.attr := CHR (attribute);   
  2007.             REPEAT
  2008.                VioWrtNCell (bCell, 1, y, x, hvps);     
  2009.                Right;
  2010.             UNTIL (x MOD 8) = 0; 
  2011.          ELSIF (c = ASCII.cr) OR (c = ASCII.lf)
  2012.           OR (c = ASCII.bel) OR (c = ASCII.bs) THEN   
  2013.             s[0] := c;    s[1] := 0C;   
  2014.             VioWrtTTY (ADR (s), 1, hvps);     
  2015.             IF c = ASCII.lf THEN
  2016.                ClrEol;
  2017.             END;
  2018.          ELSE    
  2019.             bCell.ch := c;     
  2020.             bCell.attr := CHR (attribute);   
  2021.             VioWrtNCell (bCell, 1, y, x, hvps);     
  2022.             Right;   
  2023.          END;    
  2024.       END WriteAtt;    
  2025.    
  2026. BEGIN     (* module initialization *)     
  2027.    ColorSet := IDM_GREEN;
  2028.    NORMAL := GREEN;
  2029.    HIGHLIGHT := LITE_GRN;
  2030.    REVERSE := REV_GRN;
  2031.    attribute := NORMAL;     
  2032. END Screen.
  2033. bCell.ch := ' ';    (* space = blank *)     
  2034.          bCell.attr := CHR (NORMAL);   (* Normal Video Attribute *)    
  2035.          VioScrollUp (y, x, y, 79, 1, bCell, hvps);   
  2036.       END ClrEol;     
  2037.    
  2038.    
  2039.    PROCEDURE Right;    
  2040.    (* move cursor to the right *)   
  2041.       BEGIN     
  2042.          GetXY (x, y);    
  2043.          INC (x);     
  2044.          GotoXY (x, y);     
  2045.       END Right;    
  2046.    
  2047.    
  2048.    PROCEDURE Left;   
  2049.    (* move cursor to the left *)     
  2050.       BEGIN     
  2051.          GetXY (x, y);    
  2052.          DEC (x);     
  2053.          GotoXY (x, y);     
  2054.       END Left;   
  2055.    
  2056.    
  2057.    PROCEDURE Up;    
  2058.    (* move cursor up *)     
  2059.       BEGIN     
  2060.          GetXY (x, y);    
  2061.          DEC (y);     
  2062.          GotoXY (x, y);     
  2063.       END Up;    
  2064.    
  2065.    
  2066.    PROCEDURE Down;   
  2067.    (* move cursor down *)    
  2068.       BEGIN     
  2069.          GetXY (x, y);    
  2070.          INC (y);     
  2071.          GotoXY (x, y);     
  2072.       END Down;   
  2073.    
  2074.    
  2075.    PROCEDURE GotoXY (col, row : CARDINAL);   
  2076.    (* position cursor at column, row *)   
  2077.       BEGIN     
  2078.          IF (col <= 79) AND (row <= 24) THEN     
  2079.             VioSetCurPos (row, col, hvps);   
  2080.          END;    
  2081.       END GotoXY;     
  2082.    
  2083.    
  2084.    PROCEDURE GetXY (VAR col, row : CARDINAL);   
  2085.    (* determine current cursor position *)   
  2086.       BEGIN     
  2087.          VioGetCurPos (row, col, hvps);   
  2088.       END GetXY;    
  2089.    
  2090.  
  2091.    PROCEDURE Write (c : CHAR);
  2092.    (* Write a Character *)
  2093.       BEGIN
  2094.          WriteAtt (c);
  2095.       END Write;
  2096.       
  2097.       
  2098.    PROCEDURE WriteString (str : ARRAY OF CHAR);
  2099.    (* Write String *)
  2100.    
  2101.       VAR
  2102.          i : CARDINAL;
  2103.          c : CHAR;
  2104.          
  2105.       BEGIN
  2106.          i := 0;
  2107.          c := str[i];
  2108.          WHILE c # 0C DO
  2109.             Write (c);
  2110.             INC (i);
  2111.             c := str[i];
  2112.          END;
  2113.       END WriteString;
  2114.  
  2115.       
  2116.    PROCEDURE WriteInt (n : INTEGER; s : CARDINAL);
  2117.    (* Write Integer *)
  2118.    
  2119.       VAR
  2120.          i : CARDINAL;
  2121.          b : BOOLEAN;
  2122.          str : ARRAY [0..6] OF CHAR;
  2123.          
  2124.       BEGIN
  2125.          i := 0;
  2126.          IntToString (n, s, str, i, b);
  2127.          WriteString (str);
  2128.       END WriteInt;
  2129.       
  2130.    
  2131.    PROCEDURE WriteHex (n, s : CARDINAL);
  2132.    (* Write a Hexadecimal Number *)
  2133.    
  2134.       VAR
  2135.          i : CARDINAL;
  2136.          b : BOOLEAN;
  2137.          str : ARRAY [0..6] OF CHAR;
  2138.          
  2139.       BEGIN
  2140.          i := 0;
  2141.          HexToString (n, s, str, i, b);
  2142.          WriteString (str);
  2143.       END WriteHex;
  2144.       
  2145.    
  2146.    PROCEDURE WriteLn;
  2147.    (* Write <cr> <lf> *)
  2148.       BEGIN
  2149.          Write (ASCII.cr);   Write (ASCII.lf); 
  2150.       END WriteLn;
  2151.    
  2152.    
  2153.    PROCEDURE WriteAtt (c : CHAR);   
  2154.    (* write character and attribute at cursor position *)   
  2155.    
  2156.       VAR   
  2157.          s : ARRAY [0..1] OF CHAR;    
  2158.  
  2159.       BEGIN     
  2160.          GetXY (x, y);
  2161.          IF (c = ASCII.ht) THEN
  2162.             bCell.ch := ' ';
  2163.             bCell.attr := CHR (attribute);   
  2164.             REPEAT
  2165.                VioWrtNCell (bCell, 1, y, x, hvps);     
  2166.                Right;
  2167.             UNTIL (x MOD 8) = 0; 
  2168.          ELSIF (c = ASCII.cr) OR (c = ASCII.lf)
  2169.           OR (c = ASCII.bel) OR (c = ASCII.bs) THEN   
  2170.             s[0] := c;    s[1] := 0C;   
  2171.             VioWrtTTY (ADR (s), 1, hvps);     
  2172.             IF c = ASCII.lf THEN
  2173.                ClrEol;
  2174.             END;
  2175.          ELSE    
  2176.             bCell.ch := c;     
  2177.             bCell.attr := CHR (attribute);   
  2178.             VioWrtNCell (bCell, 1, y, x, hvps);     
  2179.             Right;   
  2180.          END;    
  2181.       END WriteAtt;    
  2182.    
  2183. BEGIN     (* module initialization *)     
  2184.    ColorSet := IDM_GREEN;
  2185.    NORMAL := GREEN;
  2186.    HIGHLIGHT := LITE_GRN;
  2187.    REVERSE := REV_GRN;
  2188.    attribute := NORMAL;     
  2189. END Screen.
  2190.  
  2191. [LISTING TWELVE]
  2192.  
  2193. (**************************************************************************)
  2194. (*                                                                        *)
  2195. (*                     Copyright (c) 1988, 1989                           *)
  2196. (*                      by Stony Brook Software                           *)
  2197. (*                               and                                      *)
  2198. (*                        Copyright (c) 1990                              *)
  2199. (*                       by Brian R. Anderson                             *)
  2200. (*                        All rights reserved.                            *)
  2201. (*                                                                        *)
  2202. (**************************************************************************)
  2203.  
  2204. IMPLEMENTATION MODULE CommPort [7];
  2205.  
  2206.    FROM SYSTEM IMPORT
  2207.       ADR, BYTE, WORD, ADDRESS;
  2208.  
  2209.    FROM Storage IMPORT
  2210.       ALLOCATE, DEALLOCATE;
  2211.       
  2212.    FROM DosCalls IMPORT
  2213.       DosOpen, AttributeSet, DosDevIOCtl, DosClose, DosRead, DosWrite;
  2214.  
  2215.  
  2216.    TYPE
  2217.       CP = POINTER TO CHAR;
  2218.       
  2219.    VAR
  2220.       pn : CARDINAL;
  2221.       Handle : ARRAY [0..3] OF CARDINAL;
  2222.       BufIn : ARRAY [0..3] OF CP;
  2223.       BufOut : ARRAY [0..3] OF CP;
  2224.       BufStart : ARRAY [0..3] OF CP;
  2225.       BufLimit : ARRAY [0..3] OF CP;
  2226.       BufSize : ARRAY [0..3] OF CARDINAL;
  2227.       Temp : ARRAY [1..1024] OF CHAR;   (* size of OS/2's serial queue *)
  2228.       
  2229.  
  2230.    PROCEDURE CheckPort (portnum : CARDINAL) : BOOLEAN;
  2231.    (* Check for a valid port number and open the port if it not alredy open *)
  2232.    
  2233.       CONST
  2234.          PortName : ARRAY [0..3] OF ARRAY [0..4] OF CHAR =
  2235.             [['COM1', 0C], ['COM2', 0C], ['COM3', 0C], ['COM4', 0C]];
  2236.  
  2237.       VAR
  2238.          Action : CARDINAL;
  2239.          
  2240.       BEGIN
  2241.          (* check the port number *)
  2242.          IF portnum > 3 THEN
  2243.             RETURN FALSE;
  2244.          END;
  2245.  
  2246.          (* attempt to open the port if it is not already open *)
  2247.          IF Handle[portnum] = 0 THEN
  2248.             IF DosOpen(ADR(PortName[portnum]), Handle[portnum], Action, 0,
  2249.              AttributeSet{}, 1, 12H, 0) # 0 THEN
  2250.                RETURN FALSE;
  2251.             END;
  2252.          END;
  2253.          RETURN TRUE;
  2254.       END CheckPort;
  2255.  
  2256.  
  2257.    
  2258.    PROCEDURE InitPort (portnum : CARDINAL; speed : BaudRate; data : DataBits;
  2259.                          stop : StopBits; check : Parity) : CommStatus;
  2260.    (* Initialize a port *)
  2261.       
  2262.       CONST
  2263.          Rate : ARRAY BaudRate OF CARDINAL =
  2264.                    [110, 150, 300, 600, 1200, 2400, 4800, 9600, 19200];
  2265.          TransParity : ARRAY Parity OF BYTE = [2, 1, 0];
  2266.  
  2267.       TYPE
  2268.          LineChar =  RECORD
  2269.                         bDataBits : BYTE;
  2270.                         bParity : BYTE;
  2271.                         bStopBits : BYTE;
  2272.                      END;
  2273.  
  2274.       VAR
  2275.          LC : LineChar;
  2276.                
  2277.       BEGIN
  2278.          (* Check the port number *)
  2279.          IF NOT CheckPort(portnum) THEN
  2280.             RETURN InvalidPort;
  2281.          END;
  2282.  
  2283.          (* Set the baud rate *)
  2284.          IF DosDevIOCtl(0, ADR(Rate[speed]), 41H, 1, Handle[portnum]) # 0 THEN
  2285.             RETURN InvalidParameter;
  2286.          END;
  2287.  
  2288.          (* set the characteristics *)
  2289.          LC.bDataBits := BYTE(data);
  2290.          IF stop = 1 THEN
  2291.             DEC (stop);    (* 0x00 = 1 stop bits;    0x02 = 2 stop bits *)
  2292.          END;
  2293.          LC.bStopBits := BYTE(stop);
  2294.          LC.bParity := TransParity[check];
  2295.  
  2296.          IF DosDevIOCtl(0, ADR(LC), 42H, 1, Handle[portnum]) # 0 THEN
  2297.             RETURN InvalidParameter;
  2298.          END;
  2299.  
  2300.          RETURN Success;
  2301.       END InitPort;
  2302.  
  2303.  
  2304.    PROCEDURE StartReceiving (portnum, bufsize : CARDINAL) : CommStatus;
  2305.    (* Start receiving characters on a port *)
  2306.       BEGIN
  2307.          IF NOT CheckPort(portnum) THEN
  2308.             RETURN InvalidPort;
  2309.          END;
  2310.          IF BufStart[portnum] # NIL THEN
  2311.             RETURN AlreadyReceiving;
  2312.          END;
  2313.          ALLOCATE (BufStart[portnum], bufsize);
  2314.          BufIn[portnum] := BufStart[portnum];
  2315.          BufOut[portnum] := BufStart[portnum];
  2316.          BufLimit[portnum] := BufStart[portnum];
  2317.          INC (BufLimit[portnum]:ADDRESS, bufsize - 1);
  2318.          BufSize[portnum] := bufsize;
  2319.          RETURN Success;
  2320.       END StartReceiving;
  2321.  
  2322.  
  2323.    PROCEDURE StopReceiving (portnum : CARDINAL) : CommStatus;
  2324.    (* Stop receiving characters on a port *)
  2325.       BEGIN
  2326.          IF NOT CheckPort(portnum) THEN
  2327.             RETURN InvalidPort;
  2328.          END;
  2329.          IF BufStart[portnum] # NIL THEN
  2330.             DEALLOCATE (BufStart[portnum], BufSize[portnum]);
  2331.             BufLimit[portnum] := NIL;
  2332.             BufIn[portnum] := NIL;
  2333.             BufOut[portnum] := NIL;
  2334.             BufSize[portnum] := 0;
  2335.          END;
  2336.          DosClose(Handle[portnum]);
  2337.          Handle[portnum] := 0;
  2338.          RETURN Success;
  2339.       END StopReceiving;
  2340.  
  2341.  
  2342.    PROCEDURE GetChar (portnum : CARDINAL; VAR ch : CHAR) : CommStatus;
  2343.    (* Get a character from the comm port *)
  2344.    
  2345.       VAR
  2346.          status : CARDINAL;
  2347.          read : CARDINAL;
  2348.          que : RECORD
  2349.                   ct : CARDINAL;
  2350.                   sz : CARDINAL;
  2351.                END;
  2352.          i : CARDINAL;
  2353.                
  2354.       BEGIN
  2355.          IF BufStart[portnum] = NIL THEN
  2356.             RETURN NotReceiving;
  2357.          END;
  2358.          IF NOT CheckPort(portnum) THEN
  2359.             RETURN InvalidPort;
  2360.          END;
  2361.          status := DosDevIOCtl (ADR (que), 0, 68H, 1, Handle[portnum]);
  2362.          IF (status = 0) AND (que.ct # 0) THEN
  2363.             status := DosRead (Handle[portnum], ADR (Temp), que.ct, read);
  2364.             IF (status # 0) OR (read = 0) THEN
  2365.                RETURN NotReceiving;
  2366.             END;
  2367.             FOR i := 1 TO read DO
  2368.                BufIn[portnum]^ := Temp[i];
  2369.                IF BufIn[portnum] = BufLimit[portnum] THEN
  2370.                   BufIn[portnum] := BufStart[portnum];
  2371.                ELSE
  2372.                   INC (BufIn[portnum]:ADDRESS);
  2373.                END;
  2374.                IF BufIn[portnum] = BufOut[portnum] THEN
  2375.                   RETURN BufferOverflow;
  2376.                END;
  2377.             END;
  2378.          END;
  2379.          
  2380.          IF BufIn[portnum] = BufOut[portnum] THEN
  2381.             RETURN NoCharacter;
  2382.          END;
  2383.          ch := BufOut[portnum]^;
  2384.          IF BufOut[portnum] = BufLimit[portnum] THEN
  2385.             BufOut[portnum] := BufStart[portnum];
  2386.          ELSE
  2387.             INC (BufOut[portnum]:ADDRESS);
  2388.          END;
  2389.          RETURN Success;
  2390.       END GetChar;
  2391.  
  2392.  
  2393.    PROCEDURE SendChar (portnum : CARDINAL; ch : CHAR; 
  2394.                          modem : BOOLEAN) : CommStatus;
  2395.    (* send a character to the comm port *)
  2396.       
  2397.       VAR
  2398.          wrote : CARDINAL;
  2399.          status : CARDINAL;
  2400.          commSt : CHAR;
  2401.          
  2402.       BEGIN
  2403.          IF NOT CheckPort(portnum) THEN
  2404.             RETURN InvalidPort;
  2405.          END;
  2406.          status := DosDevIOCtl (ADR (commSt), 0, 64H, 1, Handle[portnum]);
  2407.          IF (status # 0) OR (commSt # 0C) THEN
  2408.             RETURN TimeOut;
  2409.          ELSE
  2410.             status := DosWrite(Handle[portnum], ADR(ch), 1, wrote);
  2411.             IF (status # 0) OR (wrote # 1) THEN
  2412.                RETURN TimeOut;
  2413.             ELSE
  2414.                RETURN Success;
  2415.             END;
  2416.          END;
  2417.       END SendChar;
  2418.  
  2419.  
  2420. BEGIN   (* module initialization *)
  2421.    (* nothing open yet *)
  2422.    FOR pn := 0 TO 3 DO
  2423.       Handle[pn] := 0;
  2424.       BufStart[pn] := NIL;
  2425.       BufLimit[pn] := NIL;
  2426.       BufIn[pn] := NIL;
  2427.       BufOut[pn] := NIL;
  2428.       BufSize[pn] := 0;
  2429.    END;
  2430. END CommPort.
  2431.  
  2432. [LISTING THIRTEEN]
  2433.  
  2434. IMPLEMENTATION MODULE Files;   (* File I/O for Kermit *)
  2435.  
  2436.    FROM FileSystem IMPORT
  2437.       File, Response, Delete, Lookup, Close, ReadNBytes, WriteNBytes;
  2438.  
  2439.    FROM Strings IMPORT
  2440.       Append;
  2441.       
  2442.    FROM Conversions IMPORT
  2443.       CardToString;
  2444.       
  2445.    FROM SYSTEM IMPORT
  2446.       ADR, SIZE;
  2447.  
  2448.       
  2449.    TYPE
  2450.       buffer = ARRAY [1..512] OF CHAR;
  2451.  
  2452.       
  2453.    VAR
  2454.       ext : CARDINAL;  (* new file extensions to avoid name conflict *)
  2455.       inBuf, outBuf : buffer;
  2456.       inP, outP : CARDINAL;   (* buffer pointers *)
  2457.       read, written : CARDINAL;   (* number of bytes read or written *)
  2458.                                   (* by ReadNBytes or WriteNBytes    *)
  2459.        
  2460.       
  2461.    PROCEDURE Open (VAR f : File; name : ARRAY OF CHAR) : Status;
  2462.    (* opens an existing file for reading, returns status *)
  2463.       BEGIN
  2464.          Lookup (f, name, FALSE);
  2465.          IF f.res = done THEN
  2466.             inP := 0;   read := 0;
  2467.             RETURN Done;
  2468.          ELSE
  2469.             RETURN Error;
  2470.          END;
  2471.       END Open;
  2472.       
  2473.       
  2474.    PROCEDURE Create (VAR f : File; name : ARRAY OF CHAR) : Status;
  2475.    (* creates a new file for writing, returns status *)
  2476.    
  2477.       VAR
  2478.          ch : CHAR;
  2479.          str : ARRAY [0..3] OF CHAR;
  2480.          i : CARDINAL;
  2481.          b : BOOLEAN;
  2482.          
  2483.       BEGIN
  2484.          LOOP
  2485.             Lookup (f, name, FALSE);   (* check to see if file exists *)
  2486.             IF f.res = done THEN
  2487.                Close (f);
  2488.                (* Filename Clash: Change file name *)
  2489.                IF ext > 99 THEN   (* out of new names... *)
  2490.                   RETURN Error;
  2491.                END;
  2492.                i := 0;
  2493.                WHILE (name[i] # 0C) AND (name[i] # '.') DO
  2494.                   INC (i);   (* scan for end of filename *)
  2495.                END;
  2496.                name[i] := '.';   name[i + 1] := 'K';   name[i + 2] := 0C;
  2497.                i := 0;
  2498.                CardToString (ext, 1, str, i, b); 
  2499.                Append (name, str);   (* append new extension *)
  2500.                INC (ext);
  2501.             ELSE
  2502.                EXIT;
  2503.             END;
  2504.          END;
  2505.          Lookup (f, name, TRUE);
  2506.          IF f.res = done THEN
  2507.             outP := 0;
  2508.             RETURN Done;
  2509.          ELSE
  2510.             RETURN Error;
  2511.          END;
  2512.       END Create;
  2513.       
  2514.       
  2515.    PROCEDURE CloseFile (VAR f : File; Which : FileType) : Status;
  2516.    (* closes a file after reading or writing *)
  2517.       BEGIN
  2518.          written := outP;
  2519.          IF (Which = Output) AND (outP > 0) THEN
  2520.             WriteNBytes (f, ADR (outBuf), outP);
  2521.             written := f.count;
  2522.          END;
  2523.          Close (f);
  2524.          IF (written = outP) AND (f.res = done) THEN
  2525.             RETURN Done;
  2526.          ELSE
  2527.             RETURN Error;
  2528.          END;
  2529.       END CloseFile;
  2530.       
  2531.       
  2532.    PROCEDURE Get (VAR f : File; VAR ch : CHAR) : Status;
  2533.    (* Reads one character from the file, returns status *)
  2534.       BEGIN
  2535.          IF inP = read THEN
  2536.             ReadNBytes (f, ADR (inBuf), SIZE (inBuf));
  2537.             read := f.count;
  2538.             inP := 0;
  2539.          END;
  2540.          IF read = 0 THEN
  2541.             RETURN EOF;
  2542.          ELSE
  2543.             INC (inP);
  2544.             ch := inBuf[inP];
  2545.             RETURN Done;
  2546.          END;
  2547.       END Get;
  2548.       
  2549.       
  2550.    PROCEDURE Put (ch : CHAR);
  2551.    (* Writes one character to the file buffer *)
  2552.       BEGIN
  2553.          INC (outP);
  2554.          outBuf[outP] := ch;
  2555.       END Put;
  2556.       
  2557.       
  2558.    PROCEDURE DoWrite (VAR f : File) : Status;
  2559.    (* Writes buffer to disk only if nearly full *)
  2560.       BEGIN
  2561.          IF outP < 400 THEN   (* still room in buffer *)
  2562.             RETURN Done;
  2563.          ELSE
  2564.             WriteNBytes (f, ADR (outBuf), outP);
  2565.             written := f.count;
  2566.             IF (written = outP) AND (f.res = done) THEN
  2567.                outP := 0;
  2568.                RETURN Done;
  2569.             ELSE
  2570.                RETURN Error;
  2571.             END;
  2572.          END;
  2573.       END DoWrite;  
  2574.       
  2575. BEGIN (* module initialization *)
  2576.    ext := 0;
  2577. END Files.
  2578.  
  2579. [LISTING FOURTEEN]
  2580.  
  2581. DEFINITION MODULE KH;
  2582.  
  2583. CONST
  2584.    ID_OK        =  25;
  2585.    
  2586.    PARITY_OFF   =  150;
  2587.    ID_NONE      =  152;
  2588.    ID_ODD       =  151;
  2589.    ID_EVEN      =  150;
  2590.    
  2591.    STOP_OFF     =  140;
  2592.    ID_STOP2     =  142;
  2593.    ID_STOP1     =  141;
  2594.    
  2595.    DATA_OFF     =  130;
  2596.    ID_DATA8     =  138;
  2597.    ID_DATA7     =  137;
  2598.  
  2599.    BAUD_OFF     =  120;   
  2600.    ID_B19K2     =  128;
  2601.    ID_B9600     =  127;
  2602.    ID_B4800     =  126;
  2603.    ID_B2400     =  125;
  2604.    ID_B1200     =  124;
  2605.    ID_B600      =  123;
  2606.    ID_B300      =  122;
  2607.    ID_B150      =  121;
  2608.    ID_B110      =  120;
  2609.    
  2610.    COM_OFF      =  100;
  2611.    ID_COM2      =  101;
  2612.    ID_COM1      =  100;
  2613.  
  2614.    IDM_C2       =  24;
  2615.    IDM_C1       =  23;
  2616.    IDM_AMBER    =  22;
  2617.    IDM_GREEN    =  21;
  2618.    IDM_WHITE    =  20;
  2619.    IDM_COLORS   =  19;
  2620.    IDM_DIREND   =  18;
  2621.    ID_DIRPATH   =  17;
  2622.    ID_SENDFN    =  16;
  2623.    IDM_DIRPATH  =  15;
  2624.    IDM_SENDFN   =  14;
  2625.    IDM_TERMHELP =  13;
  2626.    IDM_HELPMENU =  12;   
  2627.    IDM_ABOUT    =  11;
  2628.    IDM_PARITY   =  10;
  2629.    IDM_STOPBITS =  9;
  2630.    IDM_DATABITS =  8;
  2631.    IDM_BAUDRATE =  7;
  2632.    IDM_COMPORT  =  6;
  2633.    IDM_QUIT     =  5;
  2634.    IDM_REC      =  4;
  2635.    IDM_SEND     =  3;
  2636.    IDM_CONNECT  =  2;
  2637.    IDM_DIR      =  1;
  2638.    IDM_OPTIONS  =  52;
  2639.    IDM_FILE     =  51;
  2640.    IDM_KERMIT   =  50;
  2641.  
  2642. END KH.
  2643.  
  2644. [LISTING FIFTEEN]
  2645.  
  2646. IMPLEMENTATION MODULE KH;
  2647. END KH.
  2648.  
  2649. [LISTING SIXTEEN]
  2650.  
  2651. #define IDM_KERMIT     50
  2652. #define IDM_FILE       51
  2653. #define IDM_OPTIONS    52
  2654. #define IDM_HELP       0
  2655. #define IDM_DIR        1
  2656. #define IDM_CONNECT    2
  2657. #define IDM_SEND       3
  2658. #define IDM_REC        4
  2659. #define IDM_QUIT       5
  2660. #define IDM_COMPORT    6
  2661. #define IDM_BAUDRATE   7
  2662. #define IDM_DATABITS   8
  2663. #define IDM_STOPBITS   9
  2664. #define IDM_PARITY     10
  2665. #define IDM_ABOUT      11
  2666. #define IDM_HELPMENU   12
  2667. #define IDM_TERMHELP   13
  2668. #define IDM_SENDFN     14
  2669. #define IDM_DIRPATH    15
  2670. #define ID_SENDFN      16
  2671. #define ID_DIRPATH     17
  2672. #define IDM_DIREND     18
  2673. #define IDM_COLORS     19
  2674. #define IDM_WHITE      20
  2675. #define IDM_GREEN      21
  2676. #define IDM_AMBER      22
  2677. #define IDM_C1         23
  2678. #define IDM_C2         24
  2679. #define ID_OK          25
  2680. #define ID_COM1        100
  2681. #define ID_COM2        101
  2682. #define ID_B110        120
  2683. #define ID_B150        121
  2684. #define ID_B300        122
  2685. #define ID_B600        123
  2686. #define ID_B1200       124
  2687. #define ID_B2400       125
  2688. #define ID_B4800       126
  2689. #define ID_B9600       127
  2690. #define ID_B19K2       128
  2691. #define ID_DATA7       137
  2692. #define ID_DATA8       138
  2693. #define ID_STOP1       141
  2694. #define ID_STOP2       142
  2695. #define ID_EVEN        150
  2696. #define ID_ODD         151
  2697. #define ID_NONE        152
  2698.  
  2699. [LISTING SEVENTEEN]
  2700.  
  2701. IMPLEMENTATION MODULE DataLink;  (* Sends and Receives Packets for PCKermit *)
  2702.  
  2703.    FROM ElapsedTime IMPORT
  2704.       StartTime, GetTime;
  2705.  
  2706.    FROM Screen IMPORT
  2707.       ClrScr, WriteString, WriteLn;
  2708.  
  2709.    FROM PMWIN IMPORT
  2710.       MPARAM, WinPostMsg;
  2711.       
  2712.    FROM Shell IMPORT
  2713.       ChildFrameWindow, comport;
  2714.                   
  2715.    FROM CommPort IMPORT
  2716.       CommStatus, GetChar, SendChar;
  2717.       
  2718.    FROM PAD IMPORT
  2719.       PacketType, yourNPAD, yourPADC, yourEOL; 
  2720.  
  2721.    FROM KH IMPORT
  2722.       COM_OFF;
  2723.       
  2724.    FROM SYSTEM IMPORT
  2725.       BYTE;
  2726.       
  2727.    IMPORT ASCII;
  2728.  
  2729.  
  2730.    CONST
  2731.       MAXtime = 100;   (* hundredths of a second -- i.e., one second *)
  2732.       MAXsohtrys = 100;
  2733.       DL_BadCS = 1;
  2734.       DL_NoSOH = 2;
  2735.       
  2736.  
  2737.    TYPE
  2738.       SMALLSET = SET OF [0..7];   (* BYTE *)               
  2739.       
  2740.    VAR
  2741.       ch : CHAR;
  2742.       status : CommStatus;
  2743.       MP1, MP2 : MPARAM;
  2744.             
  2745.  
  2746.    PROCEDURE Delay (t : CARDINAL);
  2747.    (* delay time in milliseconds *)
  2748.    
  2749.       VAR
  2750.          tmp : LONGINT;
  2751.          
  2752.       BEGIN
  2753.          tmp := t DIV 10;
  2754.          StartTime;
  2755.          WHILE GetTime() < tmp DO
  2756.          END;
  2757.       END Delay;
  2758.       
  2759.             
  2760.    PROCEDURE ByteAnd (a, b : BYTE) : BYTE;
  2761.       BEGIN
  2762.          RETURN BYTE (SMALLSET (a) * SMALLSET (b));
  2763.       END ByteAnd;
  2764.       
  2765.             
  2766.    PROCEDURE Char (c : INTEGER) : CHAR;
  2767.    (* converts a number 0-95 into a printable character *)
  2768.       BEGIN
  2769.          RETURN (CHR (CARDINAL (ABS (c) + 32)));
  2770.       END Char;
  2771.       
  2772.       
  2773.    PROCEDURE UnChar (c : CHAR) : INTEGER;
  2774.    (* converts a character into its corresponding number *)
  2775.       BEGIN
  2776.          RETURN (ABS (INTEGER (ORD (c)) - 32));
  2777.       END UnChar;
  2778.  
  2779.  
  2780.    PROCEDURE FlushUART;
  2781.    (* ensure no characters left in UART holding registers *)
  2782.       BEGIN
  2783.          Delay (500);
  2784.          REPEAT
  2785.             status := GetChar (comport - COM_OFF, ch); 
  2786.          UNTIL status = NoCharacter;
  2787.       END FlushUART;
  2788.         
  2789.  
  2790.    PROCEDURE SendPacket (s : PacketType);
  2791.    (* Adds SOH and CheckSum to packet *)
  2792.    
  2793.       VAR
  2794.          i : CARDINAL;
  2795.          checksum : INTEGER;
  2796.          
  2797.       BEGIN
  2798.          Delay (10);   (* give host a chance to catch its breath *)
  2799.          FOR i := 1 TO yourNPAD DO
  2800.             status := SendChar (comport - COM_OFF, yourPADC, FALSE);
  2801.          END;
  2802.          status := SendChar (comport - COM_OFF, ASCII.soh, FALSE);
  2803.          i := 1;
  2804.          checksum := 0;
  2805.          WHILE s[i] # 0C DO
  2806.             INC (checksum, ORD (s[i]));
  2807.             status := SendChar (comport - COM_OFF, s[i], FALSE);
  2808.             INC (i);
  2809.          END;
  2810.          checksum := checksum + (INTEGER (BITSET (checksum) * {7, 6}) DIV 64);
  2811.          checksum := INTEGER (BITSET (checksum) * {5, 4, 3, 2, 1, 0});
  2812.          status := SendChar (comport - COM_OFF, Char (checksum), FALSE);
  2813.          IF yourEOL # 0C THEN
  2814.             status := SendChar (comport - COM_OFF, yourEOL, FALSE);
  2815.          END;
  2816.       END SendPacket;
  2817.       
  2818.       
  2819.    PROCEDURE ReceivePacket (VAR r : PacketType) : BOOLEAN;
  2820.    (* strips SOH and checksum -- returns status: TRUE = good packet     *)
  2821.    (* received;  FALSE = timed out waiting for packet or checksum error *)
  2822.    
  2823.       VAR
  2824.          sohtrys : INTEGER;
  2825.          i, len : INTEGER;
  2826.          ch : CHAR;
  2827.          checksum : INTEGER;
  2828.          mycheck, yourcheck : CHAR;
  2829.          
  2830.       BEGIN
  2831.          sohtrys := MAXsohtrys;
  2832.          REPEAT
  2833.             StartTime;
  2834.             REPEAT
  2835.                status := GetChar (comport - COM_OFF, ch);
  2836.             UNTIL (status = Success) OR (GetTime() > MAXtime);
  2837.             ch := CHAR (ByteAnd (ch, 177C));   (* mask off MSB *)
  2838.             (* skip over up to MAXsohtrys padding characters, *)
  2839.             (* but allow only MAXsohtrys/10 timeouts          *)
  2840.             IF status = Success THEN
  2841.                DEC (sohtrys);
  2842.             ELSE
  2843.                DEC (sohtrys, 10);
  2844.             END;
  2845.          UNTIL (ch = ASCII.soh) OR (sohtrys <= 0);
  2846.          
  2847.          IF ch = ASCII.soh THEN
  2848.             (* receive rest of packet *)
  2849.             StartTime;
  2850.             REPEAT
  2851.                status := GetChar (comport - COM_OFF, ch);
  2852.             UNTIL (status = Success) OR (GetTime() > MAXtime);
  2853.             ch := CHAR (ByteAnd (ch, 177C));
  2854.             len := UnChar (ch);
  2855.             r[1] := ch;
  2856.             checksum := ORD (ch);
  2857.             i := 2;   (* on to second character in packet -- after LEN *)
  2858.             REPEAT
  2859.                StartTime;
  2860.                REPEAT
  2861.                   status := GetChar (comport - COM_OFF, ch);
  2862.                UNTIL (status = Success) OR (GetTime() > MAXtime);
  2863.                ch := CHAR (ByteAnd (ch, 177C));
  2864.                r[i] := ch;   INC (i);
  2865.                INC (checksum, (ORD (ch)));   
  2866.             UNTIL (i > len);
  2867.             (* get checksum character *)
  2868.             StartTime;
  2869.             REPEAT 
  2870.                status := GetChar (comport - COM_OFF, ch);
  2871.             UNTIL (status = Success) OR (GetTime() > MAXtime);
  2872.             ch := CHAR (ByteAnd (ch, 177C));
  2873.             yourcheck := ch;
  2874.             r[i] := 0C;
  2875.             checksum := checksum + 
  2876.                             (INTEGER (BITSET (checksum) * {7, 6}) DIV 64);
  2877.             checksum := INTEGER (BITSET (checksum) *  {5, 4, 3, 2, 1, 0});
  2878.             mycheck := Char (checksum);
  2879.             IF mycheck = yourcheck THEN   (* checksum OK *)
  2880.                RETURN TRUE;
  2881.             ELSE   (* ERROR!!! *)
  2882.                MP1.W1 := DL_BadCS;   MP1.W2 := 0;
  2883.                MP2.L := 0;
  2884.                WinPostMsg (ChildFrameWindow, WM_DL, MP1, MP2);
  2885.                RETURN FALSE;  
  2886.             END;
  2887.          ELSE
  2888.             MP1.W1 := DL_NoSOH;   MP1.W2 := 0;
  2889.             MP2.L := 0;
  2890.             WinPostMsg (ChildFrameWindow, WM_DL, MP1, MP2);
  2891.             RETURN FALSE;
  2892.          END;
  2893.       END ReceivePacket;
  2894.       
  2895.       
  2896.    PROCEDURE DoDLMsg (mp1, mp2 [VALUE] : MPARAM);
  2897.    (* Process DataLink Messages *)
  2898.       BEGIN
  2899.          CASE CARDINAL (mp1.W1) OF
  2900.             DL_BadCS:
  2901.                WriteString ("Bad Checksum");   WriteLn;
  2902.          |  DL_NoSOH:
  2903.                WriteString ("No SOH");   WriteLn;
  2904.          ELSE
  2905.             (* Do Nothing *)
  2906.          END;
  2907.       END DoDLMsg;
  2908.  
  2909. END DataLink.
  2910.  
  2911. [LISTING EIGHTEEN]
  2912.  
  2913. #include <os2.h>
  2914. #include "pckermit.h"
  2915.  
  2916. ICON IDM_KERMIT pckermit.ico
  2917.  
  2918. MENU IDM_KERMIT
  2919.    BEGIN
  2920.       SUBMENU "~File", IDM_FILE
  2921.          BEGIN
  2922.             MENUITEM "~Directory...",     IDM_DIR
  2923.             MENUITEM "~Connect\t^C",          IDM_CONNECT
  2924.             MENUITEM "~Send...\t^S",          IDM_SEND
  2925.             MENUITEM "~Receive...\t^R",       IDM_REC
  2926.             MENUITEM SEPARATOR
  2927.             MENUITEM "E~xit\t^X",             IDM_QUIT
  2928.             MENUITEM "A~bout PCKermit...",  IDM_ABOUT
  2929.          END
  2930.          
  2931.       SUBMENU "~Options", IDM_OPTIONS
  2932.          BEGIN
  2933.             MENUITEM "~COM port...",      IDM_COMPORT
  2934.             MENUITEM "~Baud rate...",     IDM_BAUDRATE
  2935.             MENUITEM "~Data bits...",     IDM_DATABITS
  2936.             MENUITEM "~Stop bits...",     IDM_STOPBITS
  2937.             MENUITEM "~Parity bits...",   IDM_PARITY
  2938.          END
  2939.  
  2940.       SUBMENU "~Colors", IDM_COLORS
  2941.          BEGIN
  2942.             MENUITEM "~White Mono",       IDM_WHITE
  2943.             MENUITEM "~Green Mono",       IDM_GREEN
  2944.             MENUITEM "~Amber Mono",       IDM_AMBER
  2945.             MENUITEM "Full Color ~1",     IDM_C1
  2946.             MENUITEM "Full Color ~2",     IDM_C2
  2947.          END
  2948.               
  2949.       MENUITEM "F1=Help",    IDM_HELP, MIS_HELP | MIS_BUTTONSEPARATOR
  2950.    END
  2951.  
  2952. ACCELTABLE IDM_KERMIT
  2953.    BEGIN
  2954.       "^C", IDM_CONNECT
  2955.       "^S", IDM_SEND
  2956.       "^R", IDM_REC
  2957.       "^X", IDM_QUIT
  2958.    END
  2959.    
  2960. DLGTEMPLATE IDM_COMPORT LOADONCALL MOVEABLE DISCARDABLE 
  2961. BEGIN
  2962.     DIALOG "", IDM_COMPORT, 129, 91, 143, 54, FS_NOBYTEALIGN | FS_DLGBORDER | 
  2963.                 WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  2964.     BEGIN
  2965.         CONTROL "Select COM Port", IDM_COMPORT, 10, 9, 83, 38, 
  2966.                 WC_STATIC, SS_GROUPBOX | WS_VISIBLE
  2967.         CONTROL "COM1", ID_COM1, 30, 25, 43, 10, WC_BUTTON, 
  2968.             BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  2969.         CONTROL "COM2", ID_COM2, 30, 15, 39, 10, WC_BUTTON, 
  2970.             BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2971.         CONTROL "OK", ID_OK, 101, 10, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  2972.                 BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  2973.     END
  2974. END
  2975.  
  2976. DLGTEMPLATE IDM_BAUDRATE LOADONCALL MOVEABLE DISCARDABLE 
  2977. BEGIN
  2978.     DIALOG "", IDM_BAUDRATE, 131, 54, 142, 115, FS_NOBYTEALIGN | 
  2979.                 FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  2980.     BEGIN
  2981.         CONTROL "Select Baud Rate", IDM_BAUDRATE, 8, 6, 85, 107, 
  2982.                 WC_STATIC, SS_GROUPBOX | WS_VISIBLE
  2983.         CONTROL "110 Baud", ID_B110, 20, 90, 62, 10, WC_BUTTON, 
  2984.             BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  2985.         CONTROL "150 Baud", ID_B150, 20, 80, 57, 10, WC_BUTTON, 
  2986.             BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2987.         CONTROL "300 Baud", ID_B300, 20, 70, 58, 10, WC_BUTTON, 
  2988.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2989.         CONTROL "600 Baud", ID_B600, 20, 60, 54, 10, WC_BUTTON, 
  2990.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2991.         CONTROL "1200 Baud", ID_B1200, 20, 50, 59, 10, WC_BUTTON, 
  2992.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2993.         CONTROL "2400 Baud", ID_B2400, 20, 40, 63, 10, WC_BUTTON, 
  2994.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2995.         CONTROL "4800 Baud", ID_B4800, 20, 30, 62, 10, WC_BUTTON, 
  2996.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2997.         CONTROL "9600 Baud", ID_B9600, 20, 20, 59, 10, WC_BUTTON, 
  2998.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  2999.         CONTROL "19,200 Baud", ID_B19K2, 20, 10, 69, 10, WC_BUTTON, 
  3000.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  3001.         CONTROL "OK", ID_OK, 100, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3002.         BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  3003.     END
  3004. END
  3005.  
  3006. DLGTEMPLATE IDM_DATABITS LOADONCALL MOVEABLE DISCARDABLE 
  3007. BEGIN
  3008.     DIALOG "", IDM_DATABITS, 137, 80, 140, 56, FS_NOBYTEALIGN | 
  3009.                 FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS
  3010.     BEGIN
  3011.         CONTROL "Select Data Bits", IDM_DATABITS, 8, 11, 80, 36, 
  3012.                 WC_STATIC, SS_GROUPBOX | WS_VISIBLE
  3013.         CONTROL "7 Data Bits", ID_DATA7, 15, 25, 67, 10, WC_BUTTON, 
  3014.         BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  3015.         CONTROL "8 Data Bits", ID_DATA8, 15, 15, 64, 10, WC_BUTTON, 
  3016.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  3017.         CONTROL "OK", ID_OK, 96, 12, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3018.         BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  3019.     END
  3020. END
  3021.  
  3022. DLGTEMPLATE IDM_STOPBITS LOADONCALL MOVEABLE DISCARDABLE 
  3023. BEGIN
  3024.     DIALOG "", IDM_STOPBITS, 139, 92, 140, 43, FS_NOBYTEALIGN | 
  3025.                 FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS
  3026.     BEGIN
  3027.         CONTROL "Select Stop Bits", IDM_STOPBITS, 9, 6, 80, 32, 
  3028.                 WC_STATIC, SS_GROUPBOX | WS_VISIBLE
  3029.         CONTROL "1 Stop Bit", ID_STOP1, 20, 20, 57, 10, WC_BUTTON, 
  3030.         BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  3031.         CONTROL "2 Stop Bits", ID_STOP2, 20, 10, 60, 10, WC_BUTTON, 
  3032.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  3033.         CONTROL "OK", ID_OK, 96, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3034.         BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  3035.     END
  3036. END
  3037.  
  3038. DLGTEMPLATE IDM_PARITY LOADONCALL MOVEABLE DISCARDABLE 
  3039. BEGIN
  3040.     DIALOG "", IDM_PARITY, 138, 84, 134, 57, FS_NOBYTEALIGN | FS_DLGBORDER | 
  3041.                 WS_VISIBLE | WS_SAVEBITS
  3042.     BEGIN
  3043.         CONTROL "Select Parity", IDM_PARITY, 12, 6, 64, 46, WC_STATIC, 
  3044.                 SS_GROUPBOX | WS_VISIBLE
  3045.         CONTROL "Even", ID_EVEN, 25, 30, 40, 10, WC_BUTTON, 
  3046.         BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  3047.         CONTROL "Odd", ID_ODD, 25, 20, 38, 10, WC_BUTTON, 
  3048.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  3049.         CONTROL "None", ID_NONE, 25, 10, 40, 10, WC_BUTTON, 
  3050.         BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
  3051.         CONTROL "OK", ID_OK, 88, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3052.         BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  3053.     END
  3054. END
  3055.  
  3056.  
  3057. DLGTEMPLATE IDM_ABOUT LOADONCALL MOVEABLE DISCARDABLE 
  3058. BEGIN
  3059.     DIALOG "", IDM_ABOUT, 93, 74, 229, 88, FS_NOBYTEALIGN | FS_DLGBORDER | 
  3060.                 WS_VISIBLE | WS_SAVEBITS
  3061.     BEGIN
  3062.         ICON IDM_KERMIT -1, 12, 64, 22, 16
  3063.         CONTROL "PCKermit for OS/2", 256, 67, 70, 82, 8, WC_STATIC, 
  3064.         SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3065.         CONTROL "Copyright (c) 1990 by Brian R. Anderson", 257, 27, 30, 172, 8, 
  3066.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3067.         CONTROL "Microcomputer to Mainframe Communications", 259, 13, 50, 199, 8, 
  3068.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3069.         CONTROL "  OK  ", 258, 88, 10, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3070.                 BS_DEFAULT | WS_TABSTOP | WS_VISIBLE
  3071.     END
  3072. END
  3073.  
  3074. DLGTEMPLATE IDM_HELPMENU LOADONCALL MOVEABLE DISCARDABLE 
  3075. BEGIN
  3076.     DIALOG "", IDM_HELPMENU, 83, 45, 224, 125, FS_NOBYTEALIGN | FS_DLGBORDER | 
  3077.                 WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  3078.     BEGIN
  3079.         ICON IDM_KERMIT -1, 14, 99, 21, 16
  3080.         CONTROL "PCKermit Help Menu", 256, 64, 106, 91, 8, WC_STATIC, 
  3081.                 SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3082.         CONTROL "set communications Options .................. Alt, O", 
  3083.                 258, 10, 80, 201, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3084.                 WS_GROUP | WS_VISIBLE
  3085.         CONTROL "Connect to Host ................................... Alt, F; C", 
  3086.                 259, 10, 70, 204, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3087.                 WS_GROUP | WS_VISIBLE
  3088.         CONTROL "Directory .............................................. Alt, F; D", 
  3089.                 260, 10, 60, 207, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3090.                 WS_GROUP | WS_VISIBLE
  3091.         CONTROL "Send a File .......................................... Alt, F; S", 
  3092.                 261, 10, 50, 207, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3093.                 WS_GROUP | WS_VISIBLE
  3094.         CONTROL "Receive a File ...................................... Alt, F; R", 
  3095.                 262, 10, 40, 209, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3096.                 WS_GROUP | WS_VISIBLE
  3097.         CONTROL "Exit ...................................................... Alt, F; X", 
  3098.                 263, 10, 30, 205, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3099.                 WS_GROUP | WS_VISIBLE
  3100.         CONTROL "OK", 264, 83, 9, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3101.         WS_TABSTOP | WS_VISIBLE | BS_DEFAULT
  3102.     END
  3103. END
  3104.  
  3105. DLGTEMPLATE IDM_TERMHELP LOADONCALL MOVEABLE DISCARDABLE 
  3106. BEGIN
  3107.     DIALOG "", IDM_TERMHELP, 81, 20, 238, 177, FS_NOBYTEALIGN | 
  3108.                 FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  3109.     BEGIN
  3110.         CONTROL "^E = Echo mode", 256, 10, 160, 72, 8, WC_STATIC, 
  3111.                 SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3112.         CONTROL "^L = Local echo mode", 257, 10, 150, 97, 8, WC_STATIC, 
  3113.                 SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3114.         CONTROL "^T = Terminal Mode (no echo)", 258, 10, 140, 131, 8, WC_STATIC, 
  3115.                 SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3116.         CONTROL "^N = Newline mode (<cr> --> <cr><lf>)", 259, 10, 130, 165, 8, 
  3117.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3118.         CONTROL "^O = Newline mode OFF", 260, 10, 120, 109, 8, WC_STATIC, 
  3119.                 SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3120.         CONTROL "Televideo TVI950 / IBM 7171 Terminal Emulation", 261, 10, 100, 217, 8, 
  3121.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3122.         CONTROL "Sh-F1 - Sh-F12   =   PF1 - PF12", 262, 10, 90, 135, 8, 
  3123.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3124.         CONTROL "Home                 =  Clear", 263, 10, 80, 119, 8, WC_STATIC, 
  3125.                 SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3126.         CONTROL "PgDn                  =  Page  Down (as used in PROFS)", 
  3127.                 264, 10, 70, 228, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3128.                 WS_GROUP | WS_VISIBLE
  3129.         CONTROL "PgUp                  =  Page Up (as used in PROFS)", 
  3130.                 265, 10, 60, 227, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3131.                 WS_GROUP | WS_VISIBLE
  3132.         CONTROL "Insert                 =  Insert (Enter to Clear)", 266, 10, 40, 221, 8, 
  3133.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3134.         CONTROL "Delete                =  Delete", 267, 10, 30, 199, 8, 
  3135.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3136.         CONTROL "Control-G           =  Reset (rewrites the screen)", 
  3137.                 268, 10, 20, 222, 8, 
  3138.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3139.         CONTROL "Cursor Keys (i.e., Up, Down, Left, Right) all work.", 
  3140.                 269, 10, 10, 220, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
  3141.                 WS_GROUP | WS_VISIBLE
  3142.         CONTROL "OK", 270, 193, 158, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3143.                 BS_DEFAULT | WS_TABSTOP | WS_VISIBLE
  3144.         CONTROL "End                    =  End (as used in PROFS)", 271, 10, 50, 209, 8, 
  3145.                 WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3146.     END
  3147. END
  3148.  
  3149.  
  3150. DLGTEMPLATE IDM_SENDFN LOADONCALL MOVEABLE DISCARDABLE 
  3151. BEGIN
  3152.     DIALOG "", IDM_SENDFN, 113, 90, 202, 60, FS_NOBYTEALIGN | FS_DLGBORDER | 
  3153.                 WS_VISIBLE | WS_SAVEBITS
  3154.     BEGIN
  3155.         CONTROL "Send File", 256, 4, 4, 195, 24, WC_STATIC, SS_GROUPBOX | 
  3156.                 WS_GROUP | WS_VISIBLE
  3157.         CONTROL "Enter filename:", 257, 13, 11, 69, 8, WC_STATIC, SS_TEXT | 
  3158.                 DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3159.     ICON    IDM_KERMIT -1, 15, 38, 22, 16
  3160.         CONTROL "PCKermit for OS/2", 259, 59, 45, 82, 8, WC_STATIC, SS_TEXT | 
  3161.                 DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3162.         CONTROL "OK", 260, 154, 36, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3163.                 WS_TABSTOP | WS_VISIBLE | BS_DEFAULT
  3164.         CONTROL "", ID_SENDFN, 89, 10, 98, 8, WC_ENTRYFIELD, ES_LEFT | 
  3165.         ES_MARGIN | WS_TABSTOP | WS_VISIBLE
  3166.     END
  3167. END
  3168.  
  3169. DLGTEMPLATE IDM_DIRPATH LOADONCALL MOVEABLE DISCARDABLE 
  3170. BEGIN
  3171.     DIALOG "", IDM_DIRPATH, 83, 95, 242, 46, FS_NOBYTEALIGN | FS_DLGBORDER | 
  3172.                 WS_VISIBLE | WS_SAVEBITS
  3173.     BEGIN
  3174.         CONTROL "Directory", 256, 7, 5, 227, 24, WC_STATIC, SS_GROUPBOX | 
  3175.                 WS_GROUP | WS_VISIBLE
  3176.         CONTROL "Path:", 257, 28, 11, 26, 8, WC_STATIC, SS_TEXT | DT_LEFT | 
  3177.                 DT_TOP | WS_GROUP | WS_VISIBLE
  3178.         CONTROL "OK", 258, 185, 31, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3179.                 WS_TABSTOP | WS_VISIBLE | BS_DEFAULT
  3180.         CONTROL "*.*", ID_DIRPATH, 57, 11, 166, 8, WC_ENTRYFIELD, ES_LEFT | 
  3181.         ES_AUTOSCROLL | ES_MARGIN | WS_TABSTOP | WS_VISIBLE
  3182.     END
  3183. END
  3184.  
  3185. DLGTEMPLATE IDM_DIREND LOADONCALL MOVEABLE DISCARDABLE 
  3186. BEGIN
  3187.     DIALOG "", IDM_DIREND, 149, 18, 101, 27, FS_NOBYTEALIGN | FS_DLGBORDER | 
  3188.                 WS_VISIBLE | WS_SAVEBITS
  3189.     BEGIN
  3190.         CONTROL "Cancel", 256, 30, 2, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
  3191.                 BS_DEFAULT | WS_TABSTOP | WS_VISIBLE
  3192.         CONTROL "Directory Complete", 257, 9, 16, 84, 8, WC_STATIC, SS_TEXT | 
  3193.                 DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  3194.     END
  3195. END
  3196.  
  3197. [LISTING NINETEEN]
  3198.  
  3199. NAME PCKermit WINDOWAPI
  3200. DESCRIPTION 'PCKermit: (c) Brian R. Anderson, 1990'
  3201. HEAPSIZE 16384
  3202. STACKSIZE 8192
  3203. PROTMODE
  3204. EXETYPE OS2
  3205. CODE LOADONCALL EXECUTEREAD NOIOPL NONCONFORMING
  3206. DATA LOADONCALL READWRITE MULTIPLE NONSHARED NOIOPL
  3207. EXPORTS
  3208.     WindowProc
  3209.     ChildWindowProc
  3210.     ComDlgProc
  3211.     BaudDlgProc
  3212.     DataDlgProc
  3213.     StopDlgProc
  3214.     ParityDlgProc
  3215.     AboutDlgProc
  3216.     SendFNDlgProc
  3217.     PathDlgProc
  3218.     DirEndDlgProc
  3219.     HelpDlgProc
  3220.  
  3221. [FILE PCKERMIT]
  3222.  
  3223. KH.SYM: KH.DEF
  3224.     M2 KH.DEF/OUT:KH.SYM
  3225. KH.OBJ: KH.MOD KH.SYM
  3226.     M2 KH.MOD/OUT:KH.OBJ
  3227. SHELL.SYM: SHELL.DEF
  3228.     M2 SHELL.DEF/OUT:SHELL.SYM
  3229. TERM.SYM: TERM.DEF
  3230.     M2 TERM.DEF/OUT:TERM.SYM
  3231. PAD.SYM: PAD.DEF
  3232.     M2 PAD.DEF/OUT:PAD.SYM
  3233. DATALINK.SYM: DATALINK.DEF PAD.SYM
  3234.     M2 DATALINK.DEF/OUT:DATALINK.SYM
  3235. COMMPORT.SYM: COMMPORT.DEF
  3236.     M2 COMMPORT.DEF/OUT:COMMPORT.SYM
  3237. FILES.SYM: FILES.DEF
  3238.     M2 FILES.DEF/OUT:FILES.SYM
  3239. pckermit.OBJ: pckermit.MOD SHELL.SYM KH.SYM
  3240.     M2 pckermit.MOD/OUT:pckermit.OBJ
  3241. SCREEN.SYM: SCREEN.DEF
  3242.     M2 SCREEN.DEF/OUT:SCREEN.SYM
  3243. SCREEN.OBJ: SCREEN.MOD KH.SYM SCREEN.SYM
  3244.     M2 SCREEN.MOD/OUT:SCREEN.OBJ
  3245. COMMPORT.OBJ: COMMPORT.MOD COMMPORT.SYM
  3246.     M2 COMMPORT.MOD/OUT:COMMPORT.OBJ
  3247. FILES.OBJ: FILES.MOD FILES.SYM
  3248.     M2 FILES.MOD/OUT:FILES.OBJ
  3249. SHELL.OBJ: SHELL.MOD COMMPORT.SYM KH.SYM SCREEN.SYM DATALINK.SYM PAD.SYM -
  3250. TERM.SYM SHELL.SYM
  3251.     M2 SHELL.MOD/OUT:SHELL.OBJ
  3252. TERM.OBJ: TERM.MOD COMMPORT.SYM KH.SYM SHELL.SYM SCREEN.SYM TERM.SYM
  3253.     M2 TERM.MOD/OUT:TERM.OBJ
  3254. PAD.OBJ: PAD.MOD DATALINK.SYM KH.SYM SHELL.SYM FILES.SYM SCREEN.SYM PAD.SYM
  3255.     M2 PAD.MOD/OUT:PAD.OBJ
  3256. DATALINK.OBJ: DATALINK.MOD KH.SYM PAD.SYM COMMPORT.SYM SHELL.SYM SCREEN.SYM -
  3257. DATALINK.SYM
  3258.     M2 DATALINK.MOD/OUT:DATALINK.OBJ
  3259. pckermit.res: pckermit.rc pckermit.h pckermit.ico
  3260.     rc -r pckermit.rc
  3261. pckermit.EXE: KH.OBJ pckermit.OBJ SCREEN.OBJ COMMPORT.OBJ FILES.OBJ SHELL.OBJ -
  3262. TERM.OBJ PAD.OBJ DATALINK.OBJ 
  3263.     LINK @pckermit.LNK
  3264.     rc pckermit.res
  3265. pckermit.exe: pckermit.res
  3266.     rc pckermit.res
  3267.  
  3268. [ FILE PCKERMIT.LNK]
  3269.  
  3270. KH.OBJ+
  3271. pckermit.OBJ+
  3272. SCREEN.OBJ+
  3273. COMMPORT.OBJ+
  3274. FILES.OBJ+
  3275. SHELL.OBJ+
  3276. TERM.OBJ+
  3277. PAD.OBJ+
  3278. DATALINK.OBJ
  3279. pckermit
  3280. pckermit
  3281. PM+
  3282. OS2+
  3283. M2LIB+
  3284. DOSCALLS
  3285. pckermit.edf
  3286.  
  3287. [FILE PAD.MOD]
  3288.  
  3289. IMPLEMENTATION MODULE PAD;   (* Packet Assembler/Disassembler for Kermit *)
  3290.  
  3291.    FROM SYSTEM IMPORT
  3292.       ADR;
  3293.  
  3294.    FROM Storage IMPORT
  3295.       ALLOCATE, DEALLOCATE;
  3296.       
  3297.    FROM Screen IMPORT
  3298.       ClrScr, WriteString, WriteInt, WriteHex, WriteLn;
  3299.  
  3300.    FROM DosCalls IMPORT
  3301.       ExitType, DosExit;
  3302.       
  3303.    FROM Strings IMPORT
  3304.       Length, Assign;
  3305.       
  3306.    FROM FileSystem IMPORT
  3307.       File;
  3308.       
  3309.    FROM Directories IMPORT
  3310.       FileAttributes, AttributeSet, DirectoryEntry, FindFirst, FindNext;
  3311.       
  3312.    FROM Files IMPORT
  3313.       Status, FileType, Open, Create, CloseFile, Get, Put, DoWrite;
  3314.  
  3315.    FROM PMWIN IMPORT
  3316.       MPARAM, WinPostMsg;
  3317.       
  3318.    FROM Shell IMPORT
  3319.       ChildFrameWindow, comport;
  3320.       
  3321.    FROM KH IMPORT
  3322.       COM_OFF;
  3323.             
  3324.    FROM DataLink IMPORT
  3325.       FlushUART, SendPacket, ReceivePacket;
  3326.  
  3327.    FROM SYSTEM IMPORT
  3328.       BYTE;
  3329.                         
  3330.    IMPORT ASCII;
  3331.    
  3332.  
  3333.    CONST
  3334.       myMAXL = 94;
  3335.       myTIME = 10;
  3336.       myNPAD = 0;
  3337.       myPADC = 0C;
  3338.       myEOL  = 0C;
  3339.       myQCTL = '#';
  3340.       myQBIN = '&';
  3341.       myCHKT = '1';     (* one character checksum *)
  3342.       MAXtrys = 5;
  3343.       (* From DEFINITION MODULE:
  3344.       PAD_Quit = 0;  *)
  3345.       PAD_SendPacket = 1;
  3346.       PAD_ResendPacket = 2;
  3347.       PAD_NoSuchFile = 3;
  3348.       PAD_ExcessiveErrors = 4;
  3349.       PAD_ProbClSrcFile = 5;
  3350.       PAD_ReceivedPacket = 6;
  3351.       PAD_Filename = 7;
  3352.       PAD_RequestRepeat = 8;
  3353.       PAD_DuplicatePacket = 9;
  3354.       PAD_UnableToOpen = 10;
  3355.       PAD_ProbClDestFile = 11;
  3356.       PAD_ErrWrtFile = 12;
  3357.       PAD_Msg = 13;
  3358.       
  3359.       
  3360.    TYPE
  3361.       (* From Definition Module:
  3362.       PacketType = ARRAY [1..100] OF CHAR;
  3363.       *)
  3364.       SMALLSET = SET OF [0..7];   (* a byte *)
  3365.       
  3366.                         
  3367.    VAR
  3368.       yourMAXL : INTEGER;   (* maximum packet length -- up to 94 *)
  3369.       yourTIME : INTEGER;   (* time out -- seconds *) 
  3370.       (* From Definition Module
  3371.       yourNPAD : INTEGER;   (* number of padding characters *)
  3372.       yourPADC : CHAR;   (* padding characters *)
  3373.       yourEOL  : CHAR;   (* End Of Line -- terminator *)
  3374.       *)
  3375.       yourQCTL : CHAR;   (* character for quoting controls '#' *)
  3376.       yourQBIN : CHAR;   (* character for quoting binary '&' *)
  3377.       yourCHKT : CHAR;   (* check type -- 1 = checksum, etc. *)
  3378.       sF, rF : File;   (* files being sent/received *)
  3379.       InputFileOpen : BOOLEAN;
  3380.       rFname : ARRAY [0..20] OF CHAR;
  3381.       sP, rP : PacketType;   (* packets sent/received *)
  3382.       sSeq, rSeq : INTEGER;   (* sequence numbers *)
  3383.       PktNbr : INTEGER;   (* actual packet number -- no repeats up to 32,000 *)
  3384.       ErrorMsg : ARRAY [0..40] OF CHAR;
  3385.       MP1, MP2 : MPARAM;
  3386.       
  3387.  
  3388.    PROCEDURE PtrToStr (mp [VALUE] : MPARAM; VAR s : ARRAY OF CHAR);
  3389.    (* Convert a pointer to a string into a string *)
  3390.       
  3391.       TYPE
  3392.          PC = POINTER TO CHAR;
  3393.       
  3394.       VAR
  3395.          p : PC;
  3396.          i : CARDINAL;
  3397.          c : CHAR;
  3398.          
  3399.       BEGIN
  3400.          i := 0;
  3401.          REPEAT
  3402.             p := PC (mp);
  3403.             c := p^;
  3404.             s[i] := c;
  3405.             INC (i);
  3406.             INC (mp.L);
  3407.          UNTIL c = 0C;
  3408.       END PtrToStr;
  3409.  
  3410.  
  3411.    PROCEDURE DoPADMsg (mp1, mp2 [VALUE] : MPARAM);
  3412.    (* Output messages for Packet Assembler/Disassembler *)
  3413.             
  3414.       VAR
  3415.          Message : ARRAY [0..40] OF CHAR;
  3416.          
  3417.       BEGIN
  3418.          CASE CARDINAL (mp1.W1) OF
  3419.             PAD_SendPacket:
  3420.                WriteString ("Sent Packet #");   
  3421.                WriteInt (mp2.W1, 5);
  3422.                WriteString ("  (ID: ");   WriteHex (mp2.W2, 2);   
  3423.                WriteString ("h)");
  3424.          |  PAD_ResendPacket:
  3425.                WriteString ("ERROR -- Resending:");   WriteLn;
  3426.                WriteString ("     Packet #");   
  3427.                WriteInt (mp2.W1, 5);
  3428.                WriteString ("  (ID: ");   WriteHex (mp2.W2, 2);   
  3429.                WriteString ("h)");
  3430.          |  PAD_NoSuchFile:
  3431.                WriteString ("No such file: ");   
  3432.                PtrToStr (mp2, Message);   WriteString (Message);
  3433.          |  PAD_ExcessiveErrors:
  3434.                WriteString ("Excessive errors ..."); 
  3435.          |  PAD_ProbClSrcFile:
  3436.                WriteString ("Problem closing source file...");  
  3437.          |  PAD_ReceivedPacket:
  3438.                WriteString ("Received Packet #");   
  3439.                WriteInt (mp2.W1, 5);
  3440.                WriteString ("  (ID: ");   WriteHex (mp2.W2, 2);   
  3441.                WriteString ("h)");
  3442.          |  PAD_Filename:
  3443.                WriteString ("Filename = ");   
  3444.                PtrToStr (mp2, Message);   WriteString (Message);
  3445.          |  PAD_RequestRepeat:
  3446.                WriteString ("ERROR -- Requesting Repeat:");   WriteLn;
  3447.                WriteString ("         Packet #");   
  3448.                WriteInt (mp2.W1, 5);
  3449.                WriteString ("  (ID: ");   WriteHex (mp2.W2, 2);   
  3450.                WriteString ("h)");
  3451.          |  PAD_DuplicatePacket:
  3452.                WriteString ("Discarding Duplicate:");   WriteLn;
  3453.                WriteString ("         Packet #");   
  3454.                WriteString ("  (ID: ");   WriteHex (mp2.W2, 2);   
  3455.                WriteString ("h)");
  3456.          |  PAD_UnableToOpen:
  3457.                WriteString ("Unable to open file: ");
  3458.                PtrToStr (mp2, Message);   WriteString (Message);
  3459.          |  PAD_ProbClDestFile:
  3460.                WriteString ("Error closing file: ");   
  3461.                PtrToStr (mp2, Message);   WriteString (Message);
  3462.          |  PAD_ErrWrtFile:
  3463.                WriteString ("Error writing to file: ");   
  3464.                PtrToStr (mp2, Message);   WriteString (Message);
  3465.          |  PAD_Msg:
  3466.                PtrToStr (mp2, Message);   WriteString (Message);
  3467.          ELSE
  3468.             (* Do Nothing *)
  3469.          END;
  3470.          WriteLn; 
  3471.       END DoPADMsg;
  3472.       
  3473.  
  3474.    PROCEDURE CloseInput;
  3475.    (* Close the input file, if it exists.  Reset Input File Open flag *)
  3476.       BEGIN
  3477.          IF InputFileOpen THEN
  3478.             IF CloseFile (sF, Input) = Done THEN
  3479.                InputFileOpen := FALSE;
  3480.             ELSE
  3481.                MP1.W1 := PAD_ProbClSrcFile;   MP1.W2 := 0;
  3482.                MP2.L := LONGINT (ADR (sFname));
  3483.                WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3484.             END;
  3485.          END;
  3486.       END CloseInput;
  3487.       
  3488.       
  3489.    PROCEDURE NormalQuit;
  3490.    (* Exit from Thread, Post message to Window *)
  3491.       BEGIN
  3492.          MP1.W1 := PAD_Quit;   MP1.W2 := 0;
  3493.          MP1.L := 0;
  3494.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3495.          DosExit (EXIT_THREAD, 0);
  3496.       END NormalQuit;
  3497.       
  3498.       
  3499.    PROCEDURE ErrorQuit;
  3500.    (* Exit from Thread, Post message to Window *)
  3501.       BEGIN
  3502.          MP1.W1 := PAD_Error;   MP1.W2 := 0;
  3503.          MP2.L := 0;
  3504.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3505.          DosExit (EXIT_THREAD, 0);
  3506.       END ErrorQuit;
  3507.       
  3508.       
  3509.    PROCEDURE ByteXor (a, b : BYTE) : BYTE;
  3510.       BEGIN
  3511.          RETURN BYTE (SMALLSET (a) / SMALLSET (b));
  3512.       END ByteXor;
  3513.       
  3514.       
  3515.    PROCEDURE Char (c : INTEGER) : CHAR;
  3516.    (* converts a number 0-94 into a printable character *)
  3517.       BEGIN
  3518.          RETURN (CHR (CARDINAL (ABS (c) + 32)));
  3519.       END Char;
  3520.       
  3521.       
  3522.    PROCEDURE UnChar (c : CHAR) : INTEGER;
  3523.    (* converts a character into its corresponding number *)
  3524.       BEGIN
  3525.          RETURN (ABS (INTEGER (ORD (c)) - 32));
  3526.       END UnChar;
  3527.  
  3528.       
  3529.    PROCEDURE TellError (Seq : INTEGER);
  3530.    (* Send error packet *)
  3531.       BEGIN
  3532.          sP[1] := Char (15);
  3533.          sP[2] := Char (Seq);
  3534.          sP[3] := 'E';   (* E-type packet *)
  3535.          sP[4] := 'R';   (* error message starts *)
  3536.          sP[5] := 'e';
  3537.          sP[6] := 'm';
  3538.          sP[7] := 'o';
  3539.          sP[8] := 't';
  3540.          sP[9] := 'e';
  3541.          sP[10] := ' ';
  3542.          sP[11] := 'A';
  3543.          sP[12] := 'b';
  3544.          sP[13] := 'o';
  3545.          sP[14] := 'r';
  3546.          sP[15] := 't';
  3547.          sP[16] := 0C;
  3548.          SendPacket (sP);
  3549.       END TellError;
  3550.       
  3551.       
  3552.    PROCEDURE ShowError (p : PacketType);
  3553.    (* Output contents of error packet to the screen *)
  3554.    
  3555.       VAR
  3556.          i : INTEGER;
  3557.          
  3558.       BEGIN
  3559.          FOR i := 4 TO UnChar (p[1]) DO
  3560.             ErrorMsg[i - 4] := p[i];
  3561.          END;
  3562.          ErrorMsg[i - 4] := 0C;
  3563.          MP1.W1 := PAD_Msg;   MP1.W2 := 0;
  3564.          MP2.L := LONGINT (ADR (ErrorMsg));
  3565.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3566.       END ShowError;
  3567.       
  3568.       
  3569.    PROCEDURE youInit (type : CHAR);   
  3570.    (* I initialization YOU for Send and Receive *)      
  3571.       BEGIN
  3572.          sP[1] := Char (11);   (* Length *)
  3573.          sP[2] := Char (0);   (* Sequence *)
  3574.          sP[3] := type;
  3575.          sP[4] := Char (myMAXL);
  3576.          sP[5] := Char (myTIME);
  3577.          sP[6] := Char (myNPAD);
  3578.          sP[7] := CHAR (ByteXor (myPADC, 100C));
  3579.          sP[8] := Char (ORD (myEOL));
  3580.          sP[9] := myQCTL;
  3581.          sP[10] := myQBIN;
  3582.          sP[11] := myCHKT;
  3583.          sP[12] := 0C;   (* terminator *)
  3584.          SendPacket (sP);
  3585.       END youInit;
  3586.       
  3587.  
  3588.    PROCEDURE myInit;
  3589.    (* YOU initialize ME for Send and Receive *)
  3590.    
  3591.       VAR
  3592.          len : INTEGER;
  3593.          
  3594.       BEGIN
  3595.          len := UnChar (rP[1]);
  3596.          IF len >= 4 THEN
  3597.             yourMAXL := UnChar (rP[4]);
  3598.          ELSE
  3599.             yourMAXL := 94;
  3600.          END;
  3601.          IF len >= 5 THEN
  3602.             yourTIME := UnChar (rP[5]);
  3603.          ELSE
  3604.             yourTIME := 10;
  3605.          END;
  3606.          IF len >= 6 THEN
  3607.             yourNPAD := UnChar (rP[6]);
  3608.          ELSE
  3609.             yourNPAD := 0;
  3610.          END;
  3611.          IF len >= 7 THEN
  3612.             yourPADC := CHAR (ByteXor (rP[7], 100C));
  3613.          ELSE
  3614.             yourPADC := 0C;
  3615.          END;
  3616.          IF len >= 8 THEN
  3617.             yourEOL := CHR (UnChar (rP[8]));
  3618.          ELSE
  3619.             yourEOL := 0C;
  3620.          END;
  3621.          IF len >= 9 THEN
  3622.             yourQCTL := rP[9];
  3623.          ELSE
  3624.             yourQCTL := 0C;
  3625.          END;
  3626.          IF len >= 10 THEN
  3627.             yourQBIN := rP[10];
  3628.          ELSE
  3629.             yourQBIN := 0C;
  3630.          END;
  3631.          IF len >= 11 THEN
  3632.             yourCHKT := rP[11];
  3633.             IF yourCHKT # myCHKT THEN
  3634.                yourCHKT := '1';
  3635.             END;
  3636.          ELSE
  3637.             yourCHKT := '1';
  3638.          END;
  3639.       END myInit;
  3640.       
  3641.             
  3642.    PROCEDURE SendInit;
  3643.       BEGIN
  3644.          youInit ('S');
  3645.       END SendInit;
  3646.       
  3647.       
  3648.    PROCEDURE SendFileName;
  3649.    
  3650.       VAR
  3651.          i, j : INTEGER;
  3652.          
  3653.       BEGIN
  3654.          (* send file name *)
  3655.          i := 4;   j := 0;
  3656.          WHILE sFname[j] # 0C DO
  3657.             sP[i] := sFname[j];
  3658.             INC (i);   INC (j);
  3659.          END;
  3660.          sP[1] := Char (j + 3);
  3661.          sP[2] := Char (sSeq);
  3662.          sP[3] := 'F';   (* filename packet *)
  3663.          sP[i] := 0C;
  3664.          SendPacket (sP);
  3665.       END SendFileName;
  3666.       
  3667.       
  3668.    PROCEDURE SendEOF;
  3669.       BEGIN
  3670.          sP[1] := Char (3);
  3671.          sP[2] := Char (sSeq);
  3672.          sP[3] := 'Z';   (* end of file *)
  3673.          sP[4] := 0C;
  3674.          SendPacket (sP);
  3675.       END SendEOF;
  3676.       
  3677.       
  3678.    PROCEDURE SendEOT;
  3679.       BEGIN
  3680.          sP[1] := Char (3);
  3681.          sP[2] := Char (sSeq);
  3682.          sP[3] := 'B';   (* break -- end of transmit *)
  3683.          sP[4] := 0C;
  3684.          SendPacket (sP);
  3685.       END SendEOT;
  3686.       
  3687.       
  3688.    PROCEDURE GetAck() : BOOLEAN;
  3689.    (* Look for acknowledgement -- retry on timeouts or NAKs *)
  3690.    
  3691.       VAR
  3692.          Type : CHAR;
  3693.          Seq : INTEGER;
  3694.          retrys : INTEGER;
  3695.          AckOK : BOOLEAN;
  3696.           
  3697.       BEGIN
  3698.          MP1.W1 := PAD_SendPacket;   MP1.W2 := 0;
  3699.          MP2.W1 := PktNbr;   MP2.W2 := sSeq;
  3700.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3701.       
  3702.          retrys := MAXtrys;
  3703.          LOOP
  3704.             IF Aborted THEN
  3705.                TellError (sSeq);
  3706.                CloseInput;
  3707.                ErrorQuit;
  3708.             END;
  3709.             IF ReceivePacket (rP) THEN
  3710.                Seq := UnChar (rP[2]);
  3711.                Type := rP[3];
  3712.                IF (Seq = sSeq) AND (Type = 'Y') THEN
  3713.                   AckOK := TRUE;
  3714.                ELSIF (Seq = (sSeq + 1) MOD 64) AND (Type = 'N') THEN
  3715.                   AckOK := TRUE;   (* NAK for (n + 1) taken as ACK for n *)
  3716.                ELSIF Type = 'E' THEN
  3717.                   ShowError (rP);
  3718.                   AckOK := FALSE;
  3719.                   retrys := 0;
  3720.                ELSE
  3721.                   AckOK := FALSE;
  3722.                END;
  3723.             ELSE
  3724.                AckOK := FALSE;
  3725.             END;
  3726.             IF AckOK OR (retrys = 0) THEN
  3727.                EXIT;
  3728.             ELSE
  3729.                MP1.W1 := PAD_ResendPacket;   MP1.W2 := 0;
  3730.                MP2.W1 := PktNbr;   MP2.W2 := sSeq;
  3731.                WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3732.                
  3733.                DEC (retrys);
  3734.                FlushUART;
  3735.                SendPacket (sP);
  3736.             END;
  3737.          END;
  3738.       
  3739.          IF AckOK THEN
  3740.             INC (PktNbr);
  3741.             sSeq := (sSeq + 1) MOD 64;
  3742.             RETURN TRUE;
  3743.          ELSE
  3744.             RETURN FALSE;
  3745.          END;
  3746.       END GetAck;
  3747.          
  3748.  
  3749.    PROCEDURE GetInitAck() : BOOLEAN;
  3750.    (* configuration for remote station *)
  3751.       BEGIN
  3752.          IF GetAck() THEN
  3753.             myInit;
  3754.             RETURN TRUE;
  3755.          ELSE 
  3756.             RETURN FALSE;
  3757.          END;
  3758.       END GetInitAck;
  3759.       
  3760.       
  3761.    PROCEDURE Send;
  3762.    (* Send one or more files: sFname may be ambiguous *)
  3763.    
  3764.       TYPE
  3765.          LP = POINTER TO LIST;   (* list of filenames *)
  3766.          LIST = RECORD
  3767.                    fn : ARRAY [0..20] OF CHAR;
  3768.                    next : LP;
  3769.                 END;
  3770.                 
  3771.       VAR
  3772.          gotFN : BOOLEAN;
  3773.          attr : AttributeSet;
  3774.          ent : DirectoryEntry;
  3775.          front, back, t : LP;   (* add at back of queue, remove from front *)
  3776.          
  3777.       BEGIN
  3778.          Aborted := FALSE;
  3779.          InputFileOpen := FALSE;
  3780.          
  3781.          front := NIL;   back := NIL;
  3782.          attr := AttributeSet {};   (* normal files only *)
  3783.          IF Length (sFname) = 0 THEN
  3784.             MP1.W1 := PAD_Msg;   MP1.W2 := 0;
  3785.             MP2.L := LONGINT (ADR ("No file specified..."));
  3786.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3787.             ErrorQuit;
  3788.          ELSE
  3789.             gotFN := FindFirst (sFname, attr, ent);
  3790.             WHILE gotFN DO   (* build up a list of file names *)
  3791.                ALLOCATE (t, SIZE (LIST));
  3792.                Assign (ent.name, t^.fn);
  3793.                t^.next := NIL;
  3794.                IF front = NIL THEN
  3795.                   front := t;   (* start from empty queue *)
  3796.                ELSE
  3797.                   back^.next := t;   (* and to back of queue *)
  3798.                END;
  3799.                back := t;
  3800.                gotFN := FindNext (ent);
  3801.             END;
  3802.          END;
  3803.       
  3804.          IF front = NIL THEN   
  3805.             MP1.W1 := PAD_NoSuchFile;   MP1.W2 := 0;
  3806.             MP2.L := LONGINT (ADR (sFname));
  3807.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3808.             ErrorQuit;
  3809.          ELSE
  3810.             sSeq := 0;   PktNbr := 0;
  3811.             FlushUART;
  3812.             SendInit;   (* my configuration information *)
  3813.             IF NOT GetInitAck() THEN     (* get your configuration information *)
  3814.                MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  3815.                MP2.L := 0;
  3816.                WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3817.                ErrorQuit;
  3818.             END;
  3819.              
  3820.             WHILE front # NIL DO   (* send the files *)
  3821.                Assign (front^.fn, sFname);
  3822.                PktNbr := 1;
  3823.                Send1;
  3824.                t := front;
  3825.                front := front^.next;
  3826.                DEALLOCATE (t, SIZE (LIST));
  3827.             END;
  3828.          END;
  3829.       
  3830.          SendEOT;
  3831.          IF NOT GetAck() THEN
  3832.             MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  3833.             MP2.L := 0;
  3834.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3835.             CloseInput;
  3836.             ErrorQuit;
  3837.          END;
  3838.          NormalQuit;
  3839.       END Send;
  3840.       
  3841.             
  3842.    PROCEDURE Send1;
  3843.    (* Send one file: sFname *)
  3844.    
  3845.       VAR
  3846.          ch : CHAR;
  3847.          i : INTEGER;
  3848.          
  3849.       BEGIN
  3850.          IF Open (sF, sFname) = Done THEN
  3851.             InputFileOpen := TRUE;
  3852.          ELSE;
  3853.             MP1.W1 := PAD_NoSuchFile;   MP1.W2 := 0;
  3854.             MP2.L := LONGINT (ADR (sFname));
  3855.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3856.             ErrorQuit;
  3857.          END;
  3858.          
  3859.          MP1.W1 := PAD_Filename;   MP1.W2 := 0;
  3860.          MP2.L := LONGINT (ADR (sFname));
  3861.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3862.          MP1.W1 := PAD_Msg;   MP1.W2 := 0;
  3863.          MP2.L := LONGINT (ADR ("(<ESC> to abort file transfer.)"));
  3864.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3865.             
  3866.          SendFileName;        
  3867.          IF NOT GetAck() THEN
  3868.             MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  3869.             MP2.L := 0;
  3870.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3871.             CloseInput;
  3872.             ErrorQuit;
  3873.          END;
  3874.          
  3875.          (* send file *)
  3876.          i := 4;
  3877.          LOOP
  3878.             IF Get (sF, ch) = EOF THEN   (* send current packet & terminate *)
  3879.                sP[1] := Char (i - 1);
  3880.                sP[2] := Char (sSeq);
  3881.                sP[3] := 'D';   (* data packet *)
  3882.                sP[i] := 0C;   (* indicate end of packet *)
  3883.                SendPacket (sP);
  3884.                IF NOT GetAck() THEN
  3885.                   MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  3886.                   MP2.L := 0;
  3887.                   WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3888.                   CloseInput;
  3889.                   ErrorQuit;
  3890.                END;
  3891.                SendEOF;
  3892.                IF NOT GetAck() THEN
  3893.                   MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  3894.                   MP2.L := 0;
  3895.                   WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3896.                   CloseInput;
  3897.                   ErrorQuit;
  3898.                END;
  3899.                EXIT;
  3900.             END;
  3901.                   
  3902.             IF i >= (yourMAXL - 4) THEN   (* send current packet *)
  3903.                sP[1] := Char (i - 1);
  3904.                sP[2] := Char (sSeq);
  3905.                sP[3] := 'D';
  3906.                sP[i] := 0C;
  3907.                SendPacket (sP);
  3908.                IF NOT GetAck() THEN
  3909.                   MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  3910.                   MP2.L := 0;
  3911.                   WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3912.                   CloseInput;
  3913.                   ErrorQuit;
  3914.                END;
  3915.                i := 4;
  3916.             END;
  3917.  
  3918.             (* add character to current packet -- update count *)
  3919.             IF ch > 177C THEN   (* must be quoted (QBIN) and altered *)
  3920.                (* toggle bit 7 to turn it off *)
  3921.                ch := CHAR (ByteXor (ch, 200C));
  3922.                sP[i] := myQBIN;   INC (i);
  3923.             END;
  3924.             IF (ch < 40C) OR (ch = 177C) THEN   (* quote (QCTL) and alter *)
  3925.                (* toggle bit 6 to turn it on *)
  3926.                ch := CHAR (ByteXor (ch, 100C));
  3927.                sP[i] := myQCTL;   INC (i);
  3928.             END;
  3929.             IF (ch = myQCTL) OR (ch = myQBIN) THEN   (* must send it quoted *)
  3930.                sP[i] := myQCTL;   INC (i);
  3931.             END;
  3932.             sP[i] := ch;   INC (i);
  3933.          END;   (* loop *)
  3934.          
  3935.          CloseInput;
  3936.       END Send1;
  3937.       
  3938.  
  3939.    PROCEDURE ReceiveInit() : BOOLEAN;
  3940.    (* receive my initialization information from you *)
  3941.    
  3942.       VAR
  3943.          RecOK : BOOLEAN;
  3944.          trys : INTEGER;
  3945.           
  3946.       BEGIN
  3947.          trys := 1;
  3948.          LOOP
  3949.             IF Aborted THEN
  3950.                TellError (rSeq);
  3951.                ErrorQuit;
  3952.             END;
  3953.             RecOK := ReceivePacket (rP) AND (rP[3] = 'S');
  3954.             IF RecOK OR (trys = MAXtrys) THEN
  3955.                EXIT;
  3956.             ELSE
  3957.                INC (trys);
  3958.                SendNak;
  3959.             END;
  3960.          END;
  3961.          
  3962.          IF RecOK THEN
  3963.             myInit;
  3964.             RETURN TRUE;
  3965.          ELSE
  3966.             RETURN FALSE;
  3967.          END;   
  3968.       END ReceiveInit;
  3969.       
  3970.       
  3971.    PROCEDURE SendInitAck;
  3972.    (* acknowledge your initialization of ME and send mine for YOU *)
  3973.       BEGIN
  3974.          MP1.W1 := PAD_ReceivedPacket;   MP1.W2 := 0;
  3975.          MP2.W1 := PktNbr;   MP2.W2 := rSeq;
  3976.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  3977.          INC (PktNbr);
  3978.          rSeq := (rSeq + 1) MOD 64;
  3979.          youInit ('Y');
  3980.       END SendInitAck;
  3981.       
  3982.       
  3983.    PROCEDURE ValidFileChar (VAR ch : CHAR) : BOOLEAN;
  3984.    (* checks if character is one of 'A'..'Z', '0'..'9', makes upper case *)
  3985.       BEGIN
  3986.          ch := CAP (ch);
  3987.          RETURN ((ch >= 'A') AND (ch <= 'Z')) OR ((ch >= '0') AND (ch <= '9'));
  3988.       END ValidFileChar;
  3989.  
  3990.  
  3991.    TYPE
  3992.       HeaderType = (name, eot, fail);
  3993.       
  3994.    PROCEDURE ReceiveHeader() : HeaderType;
  3995.    (* receive the filename -- alter for local conditions, if necessary *)
  3996.    
  3997.       VAR
  3998.          i, j, k : INTEGER;
  3999.          RecOK : BOOLEAN;
  4000.          trys : INTEGER;
  4001.          
  4002.       BEGIN
  4003.          trys := 1;
  4004.          LOOP
  4005.             IF Aborted THEN
  4006.                TellError (rSeq);
  4007.                ErrorQuit;
  4008.             END;
  4009.             RecOK := ReceivePacket (rP) AND ((rP[3] = 'F') OR (rP[3] = 'B'));
  4010.             IF trys = MAXtrys THEN
  4011.                RETURN fail;
  4012.             ELSIF RecOK AND (rP[3] = 'F') THEN
  4013.                i := 4;   (* data starts here *)
  4014.                j := 0;   (* beginning of filename string *)
  4015.                WHILE (ValidFileChar (rP[i])) AND (j < 8) DO
  4016.                   rFname[j] := rP[i];
  4017.                   INC (i);   INC (j);
  4018.                END;
  4019.                REPEAT
  4020.                   INC (i);
  4021.                UNTIL (ValidFileChar (rP[i])) OR (rP[i] = 0C);
  4022.                rFname[j] := '.';   INC (j);
  4023.                k := 0;
  4024.                WHILE (ValidFileChar (rP[i])) AND (k < 3) DO
  4025.                   rFname[j + k] := rP[i];
  4026.                   INC (i);   INC (k);
  4027.                END;
  4028.                rFname[j + k] := 0C;  
  4029.                MP1.W1 := PAD_Filename;   MP1.W2 := 0;
  4030.                MP2.L := LONGINT (ADR (rFname));
  4031.                WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4032.                RETURN name;
  4033.             ELSIF RecOK AND (rP[3] = 'B') THEN
  4034.                RETURN eot;
  4035.             ELSE
  4036.                INC (trys);
  4037.                SendNak;
  4038.             END;
  4039.          END;
  4040.       END ReceiveHeader;
  4041.       
  4042.       
  4043.    PROCEDURE SendNak;
  4044.       BEGIN
  4045.          MP1.W1 := PAD_RequestRepeat;   MP1.W2 := 0;
  4046.          MP2.W1 := PktNbr;   MP2.W2 := rSeq;
  4047.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4048.          FlushUART;
  4049.          sP[1] := Char (3);   (* LEN *)
  4050.          sP[2] := Char (rSeq); 
  4051.          sP[3] := 'N';   (* negative acknowledgement *)
  4052.          sP[4] := 0C;
  4053.          SendPacket (sP);
  4054.       END SendNak;
  4055.       
  4056.       
  4057.    PROCEDURE SendAck (Seq : INTEGER);
  4058.       BEGIN
  4059.          IF Seq # rSeq THEN
  4060.             MP1.W1 := PAD_DuplicatePacket;   MP1.W2 := 0;
  4061.             MP2.W1 := 0;   MP2.W2 := rSeq;
  4062.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4063.          ELSE
  4064.             MP1.W1 := PAD_ReceivedPacket;   MP1.W2 := 0;
  4065.             MP2.W1 := PktNbr;   MP2.W2 := rSeq;
  4066.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4067.             rSeq := (rSeq + 1) MOD 64;
  4068.             INC (PktNbr);
  4069.          END;
  4070.          
  4071.          sP[1] := Char (3);
  4072.          sP[2] := Char (Seq);
  4073.          sP[3] := 'Y';   (* acknowledgement *)
  4074.          sP[4] := 0C;
  4075.          SendPacket (sP);
  4076.       END SendAck;
  4077.       
  4078.       
  4079.    PROCEDURE Receive;
  4080.    (* Receives a file  (or files) *)
  4081.    
  4082.       VAR
  4083.          ch, Type : CHAR;
  4084.          Seq : INTEGER;
  4085.          i : INTEGER;
  4086.          EOF, EOT, QBIN : BOOLEAN;
  4087.          trys : INTEGER;
  4088.                   
  4089.       BEGIN
  4090.          Aborted := FALSE;
  4091.          
  4092.          MP1.W1 := PAD_Msg;   MP1.W2 := 0;
  4093.          MP2.L := LONGINT (ADR ("Ready to receive file(s)..."));
  4094.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4095.          MP1.W1 := PAD_Msg;   MP1.W2 := 0;
  4096.          MP2.L := LONGINT (ADR ("(<ESC> to abort file transfer.)"));
  4097.          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4098.  
  4099.          FlushUART;
  4100.          rSeq := 0;   PktNbr := 0;  
  4101.          IF NOT ReceiveInit() THEN   (* your configuration information *)
  4102.             MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  4103.             MP2.L := 0;
  4104.             WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4105.             ErrorQuit;
  4106.          END;
  4107.          SendInitAck;       (* send my configuration information *)
  4108.          EOT := FALSE;
  4109.          WHILE NOT EOT DO
  4110.             CASE ReceiveHeader() OF
  4111.                eot  : EOT := TRUE;   EOF := TRUE;
  4112.             |  name : IF Create (rF, rFname) # Done THEN
  4113.                          MP1.W1 := PAD_UnableToOpen;   MP1.W2 := 0;
  4114.                          MP2.L := LONGINT (ADR (rFname));
  4115.                          WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4116.                          ErrorQuit;
  4117.                       ELSE
  4118.                          PktNbr := 1;
  4119.                          EOF := FALSE;
  4120.                       END;
  4121.             |  fail : MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  4122.                       MP2.L := 0;
  4123.                       WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4124.                       ErrorQuit;
  4125.             END;
  4126.             SendAck (rSeq);   (* acknowledge for name or eot *)
  4127.             trys := 1;   (* initialize *)
  4128.             WHILE NOT EOF DO
  4129.                IF Aborted THEN
  4130.                   TellError (rSeq);
  4131.                   ErrorQuit;
  4132.                END;
  4133.                IF ReceivePacket (rP) THEN
  4134.                   Seq := UnChar (rP[2]);
  4135.                   Type := rP[3];
  4136.                   IF Type = 'Z' THEN
  4137.                      EOF := TRUE;
  4138.                      IF CloseFile (rF, Output) = Done THEN
  4139.                         (* normal file termination *)
  4140.                      ELSE
  4141.                         MP1.W1 := PAD_ProbClDestFile;   MP1.W2 := 0;
  4142.                         MP2.L := LONGINT (ADR (rFname));
  4143.                         WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4144.                         ErrorQuit;
  4145.                      END;
  4146.                      trys := 1;   (* good packet -- reset *)
  4147.                      SendAck (rSeq);
  4148.                   ELSIF Type = 'E' THEN
  4149.                      ShowError (rP);
  4150.                      ErrorQuit;
  4151.                   ELSIF (Type = 'D') AND ((Seq + 1) MOD 64 = rSeq) THEN
  4152.                   (* discard duplicate packet, and Ack anyway *)
  4153.                      trys := 1;
  4154.                      SendAck (Seq); 
  4155.                   ELSIF (Type = 'D') AND (Seq = rSeq) THEN
  4156.                      (* put packet into file buffer *)
  4157.                      i := 4;   (* first data in packet *)
  4158.                      WHILE rP[i] # 0C DO
  4159.                         ch := rP[i];   INC (i);
  4160.                         IF ch = yourQBIN THEN
  4161.                            ch := rP[i];   INC (i);
  4162.                            QBIN := TRUE;
  4163.                         ELSE
  4164.                            QBIN := FALSE;
  4165.                         END;
  4166.                         IF ch = yourQCTL THEN                  
  4167.                            ch := rP[i];   INC (i);
  4168.                            IF (ch # yourQCTL) AND (ch # yourQBIN) THEN
  4169.                               ch := CHAR (ByteXor (ch, 100C));
  4170.                            END;
  4171.                         END;
  4172.                         IF QBIN THEN
  4173.                            ch := CHAR (ByteXor (ch, 200C));
  4174.                         END;
  4175.                         Put (ch);
  4176.                      END;
  4177.                   
  4178.                      (* write file buffer to disk *)
  4179.                      IF DoWrite (rF) # Done THEN
  4180.                         MP1.W1 := PAD_ErrWrtFile;   MP1.W2 := 0;
  4181.                         MP2.L := LONGINT (ADR (rFname));
  4182.                         WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4183.                         ErrorQuit;
  4184.                      END;
  4185.                      trys := 1;
  4186.                      SendAck (rSeq);
  4187.                   ELSE
  4188.                      INC (trys);
  4189.                      IF trys = MAXtrys THEN
  4190.                         MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  4191.                         MP2.L := 0;
  4192.                         WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4193.                         ErrorQuit;
  4194.                      ELSE
  4195.                         SendNak;
  4196.                      END;
  4197.                   END;
  4198.                ELSE
  4199.                   INC (trys);
  4200.                   IF trys = MAXtrys THEN
  4201.                      MP1.W1 := PAD_ExcessiveErrors;   MP1.W2 := 0;
  4202.                      MP2.L := 0;
  4203.                      WinPostMsg (ChildFrameWindow, WM_PAD, MP1, MP2);
  4204.                      ErrorQuit;
  4205.                   ELSE
  4206.                      SendNak;
  4207.                   END;
  4208.                END;
  4209.             END;
  4210.          END;
  4211.          NormalQuit;
  4212.       END Receive;
  4213.       
  4214.       
  4215. BEGIN   (* module initialization *)
  4216.    yourEOL := ASCII.cr;
  4217.    yourNPAD := 0;
  4218.    yourPADC := 0C;
  4219. END PAD.
  4220.