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