GENWiki

Premier IT Outsourcing and Support Services within the UK

User Tools

Site Tools


archive:computers:anderson

_KERMIT FOR OS/2_ by Brian R. Anderson

[LISTING ONE]

MODULE PCKermit; () (* *) (* PCKermit – by Brian R. Anderson *) (* Copyright © 1990 *) (* *) (* PCKermit is an implementation of the Kermit file transfer protocol *) (* developed at Columbia University. This (OS/2 PM) version is a *) (* port from the DOS version of Kermit that I wrote two years ago. *) (* My original DOS version appeared in the May 1989 issue of DDJ. *) (* *) (* The current version includes emulation of the TVI950 Video Display *) (* Terminal for interaction with IBM mainframes (through the IBM 7171). *) (* *) ()

 FROM SYSTEM IMPORT
    ADR;
  
 FROM OS2DEF IMPORT
    HAB, HWND, HPS, NULL, ULONG;
 FROM PMWIN IMPORT
    MPFROM2SHORT, HMQ, QMSG, CS_SIZEREDRAW,  WS_VISIBLE, FS_ICON,      
    FCF_TITLEBAR, FCF_SYSMENU, FCF_SIZEBORDER, FCF_MINMAX, FCF_ACCELTABLE,
    FCF_SHELLPOSITION, FCF_TASKLIST, FCF_MENU, FCF_ICON, 
    SWP_MOVE, SWP_SIZE, SWP_MAXIMIZE, 
    HWND_DESKTOP, FID_SYSMENU, SC_CLOSE, MIA_DISABLED, MM_SETITEMATTR,
    WinInitialize, WinCreateMsgQueue, WinGetMsg, WinDispatchMsg, WinSendMsg,
    WinRegisterClass, WinCreateStdWindow, WinDestroyWindow, WinWindowFromID,
    WinDestroyMsgQueue, WinTerminate, WinSetWindowText, 
    WinSetWindowPos, WinQueryWindowPos;
 FROM KH IMPORT
    IDM_KERMIT;
 FROM Shell IMPORT
    Class, Title, Child, WindowProc, ChildWindowProc, 
    FrameWindow, ClientWindow, SetPort, Pos;
 
 CONST
    QUEUE_SIZE = 1024;   (* Large message queue for async events *)
 VAR
    AnchorBlock : HAB;
    MessageQueue : HMQ;
    Message : QMSG;
    FrameFlags : ULONG;
    hsys : HWND;
 

BEGIN (* main *)

 AnchorBlock := WinInitialize(0);
  
 IF AnchorBlock # 0 THEN
    MessageQueue := WinCreateMsgQueue (AnchorBlock, QUEUE_SIZE);
  
    IF MessageQueue # 0 THEN
       (* Register the parent window class *)
       WinRegisterClass (
           AnchorBlock,
           ADR (Class),
           WindowProc,
           CS_SIZEREDRAW, 0);
       
       (* Register a child window class *)
       WinRegisterClass (
           AnchorBlock,
           ADR (Child),
           ChildWindowProc,
           CS_SIZEREDRAW, 0);
       
       (* Create a standard window *)
       FrameFlags := FCF_TITLEBAR + FCF_MENU + FCF_MINMAX + 
                     FCF_SYSMENU + FCF_SIZEBORDER + FCF_TASKLIST + 
                     FCF_ICON + FCF_SHELLPOSITION + FCF_ACCELTABLE;
       
       FrameWindow := WinCreateStdWindow (
                HWND_DESKTOP,           (* handle of the parent window *)
                WS_VISIBLE + FS_ICON,   (* the window style *)
                FrameFlags,             (* the window flags *)
                ADR(Class),             (* the window class *)
                NULL,                   (* the title bar text *)
                WS_VISIBLE,             (* client window style *)
                NULL,                   (* handle of resource module *)
                IDM_KERMIT,             (* resource id *)
                ClientWindow            (* returned client window handle *)
       );
        
       IF FrameWindow # 0 THEN
          (* Disable the CLOSE item on the system menu *)
          hsys := WinWindowFromID (FrameWindow, FID_SYSMENU);
          WinSendMsg (hsys, MM_SETITEMATTR,
             MPFROM2SHORT (SC_CLOSE, 1),
             MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED));
          (* Expand Window to Nearly Full Size, And Display the Title *)
          WinQueryWindowPos (HWND_DESKTOP, ADR (Pos));
          WinSetWindowPos (FrameWindow, 0, 
             Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6, 
             SWP_MOVE + SWP_SIZE);
          WinSetWindowText (FrameWindow, ADR (Title));
          
          SetPort;   (* Try to initialize communications port *)
       
          WHILE WinGetMsg(AnchorBlock, Message, NULL, 0, 0) # 0 DO
             WinDispatchMsg(AnchorBlock, Message);
          END;
        
          WinDestroyWindow(FrameWindow);
       END;
       WinDestroyMsgQueue(MessageQueue);
    END;
    WinTerminate(AnchorBlock);
 END;

END PCKermit.

[LISTING TWO]

DEFINITION MODULE Shell;

 FROM OS2DEF IMPORT
    USHORT, HWND;
 FROM PMWIN IMPORT
    MPARAM, MRESULT, SWP;
 EXPORT QUALIFIED
    Class, Child, Title, FrameWindow, ClientWindow,
    ChildFrameWindow, ChildClientWindow, Pos, SetPort, 
    WindowProc, ChildWindowProc;
       
 CONST
    Class = "PCKermit";
    Child ="Child";
    Title = "PCKermit -- Microcomputer to Mainframe Communications";
 
 VAR
    FrameWindow : HWND;
    ClientWindow : HWND;   
    ChildFrameWindow : HWND;
    ChildClientWindow : HWND;
    Pos : SWP;   (* Screen Dimensions: position & size *)
    comport : CARDINAL;
 PROCEDURE SetPort;
 
 PROCEDURE WindowProc ['WindowProc'] (
    hwnd : HWND;
    msg  : USHORT;   
    mp1  : MPARAM; 
    mp2  : MPARAM) : MRESULT [LONG, LOADDS];
 PROCEDURE ChildWindowProc ['ChildWindowProc'] (
    hwnd : HWND;
    msg  : USHORT;   
    mp1  : MPARAM; 
    mp2  : MPARAM) : MRESULT [LONG, LOADDS];

END Shell.

[LISTING THREE]

DEFINITION MODULE Term; (* TVI950 Terminal Emulation For Kermit *)

 EXPORT QUALIFIED
    WM_TERM, WM_TERMQUIT, 
    Dir, TermThrProc, InitTerm, PutKbdChar, PutPortChar;
 CONST
    WM_TERM = 4000H;
    WM_TERMQUIT = 4001H;
 
    
 PROCEDURE Dir (path : ARRAY OF CHAR);
 (* Displays a directory *)
 
 PROCEDURE TermThrProc;
 (* Thread to get characters from port, put into buffer, send message *)
 
 PROCEDURE InitTerm;
 (* Clear Screen, Home Cursor, Get Ready For Terminal Emulation *)
 
 PROCEDURE PutKbdChar (ch1, ch2 : CHAR);
 (* Process a character received from the keyboard *)
 PROCEDURE PutPortChar (ch : CHAR);
 (* Process a character received from the port *)
 

END Term.

[LISTING FOUR]

DEFINITION MODULE Screen; (* Module to perform "low level" screen functions (via AVIO) *)

 FROM PMAVIO IMPORT
    HVPS;
 EXPORT QUALIFIED
    NORMAL, HIGHLIGHT, REVERSE, attribute, ColorSet, hvps,
    White, Green, Amber, Color1, Color2,
    ClrScr, ClrEol, GotoXY, GetXY,	
    Right, Left, Up, Down, Write, WriteLn, WriteString,
    WriteInt, WriteHex, WriteAtt;
 
 VAR	  
    NORMAL : CARDINAL;
    HIGHLIGHT : CARDINAL;	
    REVERSE : CARDINAL;
    attribute : CARDINAL;	
    ColorSet : CARDINAL;
    hvps : HVPS;   (* presentation space used by screen module *)
       
 PROCEDURE White;
 (* Sets up colors: Monochrome White *)
    
 PROCEDURE Green;
 (* Sets up colors: Monochrome Green *)
    
 PROCEDURE Amber;
 (* Sets up colors: Monochrome Amber *)
    
 PROCEDURE Color1;
 (* Sets up colors: Blue, Red, Green *)
    
 PROCEDURE Color2;
 (* Sets up colors: Green, Magenta, Cyan *)
 
 PROCEDURE ClrScr;	  
 (* Clear the screen, and home the cursor *)	 
 
 PROCEDURE ClrEol;	  
 (* clear from the current cursor position to the end of the line *)	 
 
 PROCEDURE Right;	 
 (* move cursor to the right *)	
 
 PROCEDURE Left;	
 (* move cursor to the left *)	  
 
 PROCEDURE Up;	 
 (* move cursor up *)	  
 
 PROCEDURE Down;	
 (* move cursor down *)	 
 
 PROCEDURE GotoXY (col, row : CARDINAL);	
 (* position cursor at column, row *)	
 
 PROCEDURE GetXY (VAR col, row : CARDINAL);	
 (* determine current cursor position *)	
 PROCEDURE Write (c : CHAR);
 (* Write a Character, Teletype Mode *)
 PROCEDURE WriteString (str : ARRAY OF CHAR);
 (* Write String, Teletype Mode *)
 PROCEDURE WriteInt (n : INTEGER; s : CARDINAL);
 (* Write Integer, Teletype Mode *)
 
 PROCEDURE WriteHex (n, s : CARDINAL);
 (* Write a Hexadecimal Number, Teletype Mode *)
 
 PROCEDURE WriteLn;
 (* Write <cr> <lf>, Teletype Mode *)
 
 PROCEDURE WriteAtt (c : CHAR);	
 (* write character and attribute at cursor position *)	
 

END Screen.

[LISTING FIVE]

DEFINITION MODULE PAD; (* Packet Assembler/Disassembler for Kermit *)

 FROM PMWIN IMPORT
    MPARAM;
    
 EXPORT QUALIFIED
    WM_PAD, PAD_Quit, PAD_Error, PacketType, yourNPAD, yourPADC, yourEOL, 
    Aborted, sFname, Send, Receive, DoPADMsg;
 CONST
    WM_PAD = 5000H;
    PAD_Quit = 0;
    PAD_Error = 20;
            
 TYPE
    (* PacketType used in both PAD and DataLink modules *)
    PacketType = ARRAY [1..100] OF CHAR;
    
 VAR
    (* yourNPAD, yourPADC, and yourEOL used in both PAD and DataLink *)
    yourNPAD : CARDINAL;   (* number of padding characters *)
    yourPADC : CHAR;       (* padding characters *)
    yourEOL  : CHAR;       (* End Of Line -- terminator *)
    sFname : ARRAY [0..20] OF CHAR;
    Aborted : BOOLEAN;
 PROCEDURE Send;
 (* Sends a file after prompting for filename *)
 
 PROCEDURE Receive;
 (* Receives a file (or files) *)
 PROCEDURE DoPADMsg (mp1, mp2 : MPARAM);
 (* Output messages for Packet Assembler/Disassembler *)
          

END PAD.

[LISTING SIX]

DEFINITION MODULE DataLink; (* Sends and Receives Packets for PCKermit *)

 FROM PMWIN IMPORT
    MPARAM;
    
 FROM PAD IMPORT
    PacketType;
    
 EXPORT QUALIFIED
    WM_DL, FlushUART, SendPacket, ReceivePacket, DoDLMsg;
 CONST
    WM_DL = 6000H;
    
 PROCEDURE FlushUART;
 (* ensure no characters left in UART holding registers *)
  
 PROCEDURE SendPacket (s : PacketType);
 (* Adds SOH and CheckSum to packet *)
 
 PROCEDURE ReceivePacket (VAR r : PacketType) : BOOLEAN;
 (* strips SOH and checksum -- returns status: TRUE= good packet       *)
 (* received;  FALSE = timed out waiting for packet or checksum error  *)
 
 PROCEDURE DoDLMsg (mp1, mp2 : MPARAM);
 (* Process DataLink Messages *)
 

END DataLink.

[LISTING SEVEN]

(*) (* *) (* Copyright (C) 1988, 1989 *) (* by Stony Brook Software *) (* *) (* All rights reserved. *) (* *) (*)

DEFINITION MODULE CommPort;

 TYPE
    CommStatus = (                
             Success,   
             InvalidPort,  
             InvalidParameter,    
             AlreadyReceiving,    
             NotReceiving,  
             NoCharacter,  
             FramingError,  
             OverrunError,  
             ParityError,  
             BufferOverflow,  
             TimeOut   
    );   
    BaudRate = (  
             Baud110,   
             Baud150,   
             Baud300,   
             Baud600,   
             Baud1200,  
             Baud2400,  
             Baud4800,  
             Baud9600,  
             Baud19200  
    );   
    
    DataBits = [7..8];  
    StopBits = [1..2];  
    Parity = (Even, Odd, None);  
 PROCEDURE InitPort(port : CARDINAL; speed : BaudRate; data : DataBits;
                        stop : StopBits; check : Parity) : CommStatus;
 PROCEDURE StartReceiving(port, bufsize : CARDINAL) : CommStatus;
 PROCEDURE StopReceiving(port : CARDINAL) : CommStatus;
 PROCEDURE GetChar(port : CARDINAL; VAR ch : CHAR) : CommStatus;
 PROCEDURE SendChar(port : CARDINAL; ch : CHAR; modem : BOOLEAN) : CommStatus;

END CommPort.

[LISTING EIGHT]

DEFINITION MODULE Files; (* File I/O for Kermit *)

 FROM FileSystem IMPORT
    File;
    
 EXPORT QUALIFIED
    Status, FileType, Open, Create, CloseFile, Get, Put, DoWrite;
       
 TYPE
    Status = (Done, Error, EOF);
    FileType = (Input, Output);
 
 PROCEDURE Open (VAR f : File; name : ARRAY OF CHAR) : Status;
 (* opens an existing file for reading, returns status *)
 
 PROCEDURE Create (VAR f : File; name : ARRAY OF CHAR) : Status;
 (* creates a new file for writing, returns status *)
 
 PROCEDURE CloseFile (VAR f : File; Which : FileType) : Status;
 (* closes a file after reading or writing *)
 
 PROCEDURE Get (VAR f : File; VAR ch : CHAR) : Status;
 (* Reads one character from the file, returns status *)
 
 PROCEDURE Put (ch : CHAR);
 (* Writes one character to the file buffer *)
 
 PROCEDURE DoWrite (VAR f : File) : Status;
 (* Writes buffer to disk only if nearly full *)
 

END Files.

[LISTING NINE]

IMPLEMENTATION MODULE Shell;

 FROM SYSTEM IMPORT
    ADDRESS, ADR;
  
 IMPORT ASCII;
 
 FROM OS2DEF IMPORT
    LOWORD, HIWORD, HWND, HDC, HPS, RECTL, USHORT, NULL, ULONG;
 FROM Term IMPORT
    WM_TERM, WM_TERMQUIT, 
    Dir, TermThrProc, InitTerm, PutKbdChar, PutPortChar;
 FROM PAD IMPORT
    WM_PAD, PAD_Quit, PAD_Error, DoPADMsg, Aborted, sFname, Send, Receive;
 FROM DataLink IMPORT
    WM_DL, DoDLMsg;
          
 FROM Screen IMPORT
    hvps, ColorSet, White, Green, Amber, Color1, Color2, ClrScr, WriteLn;
    
 FROM DosCalls IMPORT
    DosCreateThread, DosSuspendThread, DosResumeThread, DosSleep;
 FROM PMAVIO IMPORT
    VioCreatePS, VioAssociate, VioDestroyPS, VioShowPS, WinDefAVioWindowProc,
    FORMAT_CGA, HVPS;
    
 FROM PMWIN IMPORT
    MPARAM, MRESULT, SWP, PSWP, 
    WS_VISIBLE, FCF_TITLEBAR, FCF_SIZEBORDER, FCF_SHELLPOSITION,
    WM_SYSCOMMAND, WM_MINMAXFRAME, SWP_MINIMIZE, HWND_DESKTOP, 
    WM_PAINT, WM_QUIT, WM_COMMAND, WM_INITDLG, WM_CONTROL, WM_HELP,
    WM_INITMENU, WM_SIZE, WM_DESTROY, WM_CREATE, WM_CHAR, 
    BM_SETCHECK, MBID_OK, MB_OK, MB_OKCANCEL, 
    KC_CHAR, KC_CTRL, KC_VIRTUALKEY, KC_KEYUP,
    SWP_SIZE, SWP_MOVE, SWP_MAXIMIZE, SWP_RESTORE,
    MB_ICONQUESTION, MB_ICONASTERISK, MB_ICONEXCLAMATION,
    FID_MENU, MM_SETITEMATTR, MM_QUERYITEMATTR, 
    MIA_DISABLED, MIA_CHECKED, MPFROM2SHORT,
    WinCreateStdWindow, WinDestroyWindow,
    WinOpenWindowDC, WinSendMsg, WinQueryDlgItemText, WinInvalidateRect,
    WinDefWindowProc, WinBeginPaint, WinEndPaint, WinQueryWindowRect,
    WinSetWindowText, WinSetFocus, WinDlgBox, WinDefDlgProc, WinDismissDlg, 
    WinMessageBox, WinPostMsg, WinWindowFromID, WinSendDlgItemMsg,
    WinSetWindowPos, WinSetActiveWindow;
 FROM PMGPI IMPORT
    GpiErase;
 FROM KH IMPORT
    IDM_KERMIT, IDM_FILE, IDM_OPTIONS, IDM_SENDFN, ID_SENDFN,
    IDM_DIR, IDM_CONNECT, IDM_SEND, IDM_REC, IDM_DIRPATH, ID_DIRPATH, 
    IDM_DIREND, IDM_QUIT, IDM_ABOUT, IDM_HELPMENU, IDM_TERMHELP, 
    IDM_COMPORT, IDM_BAUDRATE, IDM_DATABITS, IDM_STOPBITS, IDM_PARITY, 
    COM_OFF, ID_COM1, ID_COM2, PARITY_OFF, ID_EVEN, ID_ODD, ID_NONE, 
    DATA_OFF, ID_DATA7, ID_DATA8, STOP_OFF, ID_STOP1, ID_STOP2,
    BAUD_OFF, ID_B110, ID_B150, ID_B300, ID_B600, ID_B1200, ID_B2400, 
    ID_B4800, ID_B9600, ID_B19K2,
    IDM_COLORS, IDM_WHITE, IDM_GREEN, IDM_AMBER, IDM_C1, IDM_C2;
 FROM CommPort IMPORT
    CommStatus, BaudRate, DataBits, StopBits, Parity, InitPort,
    StartReceiving, StopReceiving;
 
 FROM Strings IMPORT
    Assign, Append, AppendChar;
 
 
 CONST
    WM_SETMAX = 7000H;
    WM_SETFULL = 7001H;
    WM_SETRESTORE = 7002H;
    NONE = 0;   (* no port yet initialized *)
    STKSIZE = 4096;
    BUFSIZE = 4096;   (* Port receive buffers: room for two full screens *)
    PortError = "Port Is Already In Use -- EXIT? (Cancel Trys Another Port)";
    ESC = 33C;
    
 
 VAR
    FrameFlags : ULONG;
    TermStack : ARRAY [1..STKSIZE] OF CHAR;
    Stack : ARRAY [1..STKSIZE] OF CHAR;
    TermThr : CARDINAL;
    Thr : CARDINAL;
    hdc : HDC;
    frame_hvps, child_hvps : HVPS;
    TermMode : BOOLEAN;
    Path : ARRAY [0..60] OF CHAR;
    Banner : ARRAY [0..40] OF CHAR;
    PrevComPort : CARDINAL;
    Settings : ARRAY [0..1] OF RECORD
                                  baudrate : CARDINAL;
                                  databits : CARDINAL;
                                  parity : CARDINAL;
                                  stopbits : CARDINAL;
                               END;    
 PROCEDURE SetFull;
 (* Changes window to full size *)
    BEGIN
       WinSetWindowPos (FrameWindow, 0,		
          Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6,
          SWP_MOVE + SWP_SIZE);
    END SetFull;
    
    		
 PROCEDURE SetRestore;
 (* Changes window to full size FROM maximized *)
    BEGIN
       WinSetWindowPos (FrameWindow, 0,
          Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6,		
          SWP_MOVE + SWP_SIZE + SWP_RESTORE);		  
    END SetRestore;
    
                      		  
 PROCEDURE SetMax;
 (* Changes window to maximized *)
    BEGIN
       WinSetWindowPos (FrameWindow, 0,                           
          Pos.x + 3, Pos.y + 3, Pos.cx - 6, Pos.cy - 6,		
          SWP_MOVE + SWP_SIZE + SWP_MAXIMIZE);	
    END SetMax;
    
                     								                  
 PROCEDURE SetBanner;
 (* Displays Abbreviated Program Title + Port Settings in Title Bar *)
    CONST
       PortName : ARRAY [0..1] OF ARRAY [0..5] OF CHAR =
          [["COM1:", 0C], ["COM2:", 0C]]; 
       BaudName : ARRAY [0..8] OF ARRAY [0..5] OF CHAR =
          [["110", 0C], ["150", 0C], ["300", 0C], 
           ["600", 0C], ["1200", 0C], ["2400", 0C], 
           ["4800", 0C], ["9600", 0C], ["19200", 0C]];  
       ParityName : ARRAY [0..2] OF CHAR = ['E', 'O', 'N'];
 
    BEGIN
       WITH Settings[comport - COM_OFF] DO
          Assign (Class, Banner);
          Append (Banner, " -- ");
          Append (Banner, PortName[comport - COM_OFF]);
          Append (Banner, BaudName[baudrate - BAUD_OFF]);
          AppendChar (Banner, ',');
          AppendChar (Banner, ParityName[parity - PARITY_OFF]);
          AppendChar (Banner, ',');
          AppendChar (Banner, CHR ((databits - DATA_OFF) + 30H));
          AppendChar (Banner, ',');
          AppendChar (Banner, CHR ((stopbits - STOP_OFF) + 30H)); 
          WinSetWindowText (FrameWindow, ADR (Banner));
       END;
    END SetBanner;
 
 
 PROCEDURE SetPort;
 (* Sets The Communications Parameters Chosen By User *)
    VAR
       status : CommStatus;
       rc : USHORT;
    
    BEGIN
       IF PrevComPort # NONE THEN
          StopReceiving (PrevComPort - COM_OFF);
       END;
       
       WITH Settings[comport - COM_OFF] DO
          status := InitPort (
             comport - COM_OFF,
             BaudRate (baudrate - BAUD_OFF),
             DataBits (databits - DATA_OFF),
             StopBits (stopbits - STOP_OFF),
             Parity (parity - PARITY_OFF),
          );
       END;
   
       IF status = Success THEN
          StartReceiving (comport - COM_OFF, BUFSIZE);
          PrevComPort := comport;
       ELSE
          rc := WinMessageBox (HWND_DESKTOP, FrameWindow, ADR (PortError),
                               0, 0, MB_OKCANCEL + MB_ICONEXCLAMATION);
          IF rc = MBID_OK THEN
             WinPostMsg (FrameWindow, WM_QUIT, 0, 0);
          ELSE   (* try the other port *)
             IF comport = ID_COM1 THEN
                comport := ID_COM2;
             ELSE
                comport := ID_COM1;
             END;
             SetPort;   (* recursive call for retry *)
          END;
       END;      
       SetBanner;
    END SetPort;
 PROCEDURE MakeChild (msg : ARRAY OF CHAR);
 (* Creates a child window for use by send or receive threads *)
    
    VAR
       c_hdc : HDC;
       
    BEGIN
       WinPostMsg (FrameWindow, WM_SETFULL, 0, 0);
          
       Disable (IDM_CONNECT);
       Disable (IDM_SEND);
       Disable (IDM_REC);
       Disable (IDM_DIR);
       Disable (IDM_OPTIONS);
       Disable (IDM_COLORS);
       
       (* Create a client window *)	 
       FrameFlags := FCF_TITLEBAR + FCF_SIZEBORDER;
       
       ChildFrameWindow := WinCreateStdWindow (
			ClientWindow,        (* handle of the parent window *)
			WS_VISIBLE,          (* the window style *)
			FrameFlags,          (* the window flags *)
			ADR(Child),          (* the window class *)
			NULL,                (* the title bar text *)
			WS_VISIBLE,          (* client window style *)
			NULL,                (* handle of resource module *)
			IDM_KERMIT,          (* resource id *)
			ChildClientWindow    (* returned client window handle *)
       );
       
       WinSetWindowPos (ChildFrameWindow, 0,
          Pos.cx DIV 4, Pos.cy DIV 4, 
          Pos.cx DIV 2, Pos.cy DIV 2 - 3,
          SWP_MOVE + SWP_SIZE);
       
       WinSetWindowText (ChildFrameWindow, ADR (msg));
       WinSetActiveWindow (HWND_DESKTOP, ChildFrameWindow);
                
       c_hdc := WinOpenWindowDC (ChildClientWindow);
       hvps := child_hvps;
       VioAssociate (c_hdc, hvps);
       ClrScr;	 (* clear the hvio window *)
    END MakeChild;
    
 PROCEDURE Disable (item : USHORT);
 (* Disables and "GREYS" a menu item *)   
 
    VAR
       h : HWND;
       
    BEGIN
       h := WinWindowFromID (FrameWindow, FID_MENU);
       WinSendMsg (h, MM_SETITEMATTR,
          MPFROM2SHORT (item, 1),
          MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED));
    END Disable;
    
    
 PROCEDURE Enable (item : USHORT);
 (* Enables a menu item *)
 
    VAR
       h : HWND;
       atr : USHORT;
       
    BEGIN
       h := WinWindowFromID (FrameWindow, FID_MENU);
       atr := USHORT (WinSendMsg (h, MM_QUERYITEMATTR,
                      MPFROM2SHORT (item, 1),
                      MPFROM2SHORT (MIA_DISABLED, MIA_DISABLED)));
       atr := USHORT (BITSET (atr) * (BITSET (MIA_DISABLED) / BITSET (-1)));                  
       WinSendMsg (h, MM_SETITEMATTR,
          MPFROM2SHORT (item, 1),
          MPFROM2SHORT (MIA_DISABLED, atr));
    END Enable;
    
             
 PROCEDURE Check (item : USHORT);
 (* Checks a menu item -- indicates that it is selected *)   
 
    VAR
       h : HWND;
       
    BEGIN
       h := WinWindowFromID (FrameWindow, FID_MENU);
       WinSendMsg (h, MM_SETITEMATTR,
          MPFROM2SHORT (item, 1),
          MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED));
    END Check;
    
    
 PROCEDURE UnCheck (item : USHORT);
 (* Remove check from a menu item *)
 
    VAR
       h : HWND;
       atr : USHORT;
       
    BEGIN
       h := WinWindowFromID (FrameWindow, FID_MENU);
       atr := USHORT (WinSendMsg (h, MM_QUERYITEMATTR,
                      MPFROM2SHORT (item, 1),
                      MPFROM2SHORT (MIA_CHECKED, MIA_CHECKED)));
       atr := USHORT (BITSET (atr) * (BITSET (MIA_CHECKED) / BITSET (-1)));                  
       WinSendMsg (h, MM_SETITEMATTR,
          MPFROM2SHORT (item, 1),
          MPFROM2SHORT (MIA_CHECKED, atr));
    END UnCheck;
    
             
 PROCEDURE DoMenu (hwnd : HWND; item : MPARAM);
 (* Processes Most Menu Interactions *)
 
    VAR
       rcl : RECTL;
       rc : USHORT;
       
    BEGIN
       CASE  LOWORD (item) OF
          IDM_DIR:
             SetFull;
             WinQueryWindowRect (hwnd, rcl);
             WinDlgBox (HWND_DESKTOP, hwnd, PathDlgProc, 0, IDM_DIRPATH, 0);
             hvps := frame_hvps;
             VioAssociate (hdc, hvps);
             Dir (Path);
             WinDlgBox (HWND_DESKTOP, hwnd, DirEndDlgProc, 0, IDM_DIREND, 0);
             VioAssociate (0, hvps);
             WinInvalidateRect (hwnd, ADR (rcl), 0);
       |  IDM_CONNECT:
             TermMode := TRUE;
             Disable (IDM_CONNECT);
             Disable (IDM_SEND);
             Disable (IDM_REC);
             Disable (IDM_DIR);
             Disable (IDM_OPTIONS);
             Disable (IDM_COLORS);
             (* MAXIMIZE Window -- Required for Terminal Emulation *)
             SetMax;
             hvps := frame_hvps;
             VioAssociate (hdc, hvps);
             DosResumeThread (TermThr);
             InitTerm;
       |  IDM_SEND:
             WinDlgBox (HWND_DESKTOP, hwnd, SendFNDlgProc, 0, IDM_SENDFN, 0);
             MakeChild ("Send a File");
             DosCreateThread (Send, Thr, ADR (Stack[STKSIZE]));
       |  IDM_REC:
             MakeChild ("Receive a File"); 
             DosCreateThread (Receive, Thr, ADR (Stack[STKSIZE]));
       |  IDM_QUIT:
             rc := WinMessageBox (HWND_DESKTOP, ClientWindow,
                      ADR ("Do You Really Want To EXIT PCKermit?"),
                      ADR ("End Session"), 0, MB_OKCANCEL + MB_ICONQUESTION);
             IF rc = MBID_OK THEN
                StopReceiving (comport - COM_OFF);
                WinPostMsg (hwnd, WM_QUIT, 0, 0);
             END;
       |  IDM_COMPORT:
             WinDlgBox (HWND_DESKTOP, hwnd, ComDlgProc, 0, IDM_COMPORT, 0);
             SetPort;
       |  IDM_BAUDRATE:
             WinDlgBox (HWND_DESKTOP, hwnd, BaudDlgProc, 0, IDM_BAUDRATE, 0);
             SetPort;
       |  IDM_DATABITS:
             WinDlgBox (HWND_DESKTOP, hwnd, DataDlgProc, 0, IDM_DATABITS, 0);
             SetPort;
       |  IDM_STOPBITS:
             WinDlgBox (HWND_DESKTOP, hwnd, StopDlgProc, 0, IDM_STOPBITS, 0);
             SetPort;
       |  IDM_PARITY:
             WinDlgBox (HWND_DESKTOP, hwnd, ParityDlgProc, 0, IDM_PARITY, 0);
             SetPort;
       |  IDM_WHITE:
             UnCheck (ColorSet);
             ColorSet := IDM_WHITE;
             Check (ColorSet);
             White;
       |  IDM_GREEN:
             UnCheck (ColorSet);
             ColorSet := IDM_GREEN;
             Check (ColorSet);
             Green;
       |  IDM_AMBER:
             UnCheck (ColorSet);
             ColorSet := IDM_AMBER;
             Check (ColorSet);
             Amber;
       |  IDM_C1:
             UnCheck (ColorSet);
             ColorSet := IDM_C1;
             Check (ColorSet);
             Color1;
       |  IDM_C2:   
             UnCheck (ColorSet);
             ColorSet := IDM_C2;
             Check (ColorSet);
             Color2;           
       |  IDM_ABOUT:
             WinDlgBox (HWND_DESKTOP, hwnd, AboutDlgProc, 0, IDM_ABOUT, 0);
       ELSE
          (* Don't do anything... *)
       END;
    END DoMenu;   
 PROCEDURE ComDlgProc ['ComDlgProc'] (
 (* Process Dialog Box for choosing COM1/COM2 *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       CASE msg OF
          WM_INITDLG:
             WinSendDlgItemMsg (hwnd, comport, BM_SETCHECK, 1, 0);
             WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, comport));
             RETURN 1;
       |  WM_CONTROL:
             comport := LOWORD (mp1);
             RETURN 0;
       |  WM_COMMAND:
             WinDismissDlg (hwnd, 1);
             RETURN 0;
       ELSE
          RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
       END;
    END ComDlgProc;
 
  
 PROCEDURE BaudDlgProc ['BaudDlgProc'] (
 (* Process Dialog Box for choosing Baud Rate *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       WITH Settings[comport - COM_OFF] DO
          CASE msg OF
             WM_INITDLG:
                WinSendDlgItemMsg (hwnd, baudrate, BM_SETCHECK, 1, 0);
                WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, baudrate));
                RETURN 1;
          |  WM_CONTROL:
                baudrate := LOWORD (mp1);
                RETURN 0;
          |  WM_COMMAND:
                WinDismissDlg (hwnd, 1);
                RETURN 0;
          ELSE
             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
          END;
       END;
    END BaudDlgProc;
 
  
 PROCEDURE DataDlgProc ['DataDlgProc'] (
 (* Process Dialog Box for choosing 7 or 8 data bits *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       WITH Settings[comport - COM_OFF] DO
          CASE msg OF
             WM_INITDLG:
                WinSendDlgItemMsg (hwnd, databits, BM_SETCHECK, 1, 0);
                WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, databits));
                RETURN 1;
          |  WM_CONTROL:
                databits := LOWORD (mp1);
                RETURN 0;
          |  WM_COMMAND:
                WinDismissDlg (hwnd, 1);
                RETURN 0;
          ELSE
             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
          END;
       END;
    END DataDlgProc;
 
  
 PROCEDURE StopDlgProc ['StopDlgProc'] (
 (* Process Dialog Box for choosing 1 or 2 stop bits *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       WITH Settings[comport - COM_OFF] DO
          CASE msg OF
             WM_INITDLG:
                WinSendDlgItemMsg (hwnd, stopbits, BM_SETCHECK, 1, 0);
                WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, stopbits));
                RETURN 1;
          |  WM_CONTROL:
                stopbits := LOWORD (mp1);
                RETURN 0;
          |  WM_COMMAND:
                WinDismissDlg (hwnd, 1);
                RETURN 0;
          ELSE
             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
          END;
       END;
    END StopDlgProc;
 
  
 PROCEDURE ParityDlgProc ['ParityDlgProc'] (
 (* Process Dialog Box for choosing odd, even, or no parity *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       WITH Settings[comport - COM_OFF] DO
          CASE msg OF
             WM_INITDLG:
                WinSendDlgItemMsg (hwnd, parity, BM_SETCHECK, 1, 0);
                WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, parity));
                RETURN 1;
          |  WM_CONTROL:
                parity := LOWORD (mp1);
                RETURN 0;
          |  WM_COMMAND:
                WinDismissDlg (hwnd, 1);
                RETURN 0;
          ELSE
             RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
          END;
       END;
    END ParityDlgProc;
 
  
 PROCEDURE AboutDlgProc ['AboutDlgProc'] (
 (* Process "About" Dialog Box *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       IF msg = WM_COMMAND THEN
          WinDismissDlg (hwnd, 1);
          RETURN 0;
       ELSE
          RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
       END;
    END AboutDlgProc;
 PROCEDURE SendFNDlgProc ['SendFNDlgProc'] (
 (* Process Dialog Box that obtains send filename from user *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       CASE msg OF
          WM_INITDLG:
             WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, ID_SENDFN));
             RETURN 1;
       |  WM_COMMAND:
             WinQueryDlgItemText (hwnd, ID_SENDFN, 20, ADR (sFname));
             WinDismissDlg (hwnd, 1);
             RETURN 0;
       ELSE
          RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
       END;
    END SendFNDlgProc;
    
 PROCEDURE PathDlgProc ['PathDlgProc'] (
 (* Process Dialog Box that obtains directory path from user *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       CASE msg OF
          WM_INITDLG:
             WinSetFocus (HWND_DESKTOP, WinWindowFromID (hwnd, ID_DIRPATH));
             RETURN 1;
       |  WM_COMMAND:
             WinQueryDlgItemText (hwnd, ID_DIRPATH, 60, ADR (Path));
             WinDismissDlg (hwnd, 1);
             RETURN 0;
       ELSE
          RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
       END;
    END PathDlgProc;
 PROCEDURE DirEndDlgProc ['DirEndDlgProc'] (
 (* Process Dialog Box to allow user to cancel directory *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       IF msg = WM_COMMAND THEN
          WinDismissDlg (hwnd, 1);
          RETURN 0;
       ELSE
          RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
       END;
    END DirEndDlgProc;
    
 
 PROCEDURE HelpDlgProc ['HelpDlgProc'] (
 (* Process Dialog Boxes for the HELP *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    BEGIN
       IF msg = WM_COMMAND THEN
          WinDismissDlg (hwnd, 1);
          RETURN 0;
       ELSE
          RETURN WinDefDlgProc (hwnd, msg, mp1, mp2);
       END;
    END HelpDlgProc;
 PROCEDURE KeyTranslate (mp1, mp2 : MPARAM; VAR c1, c2 : CHAR) : BOOLEAN;
 (* Translates WM_CHAR message into ascii keystroke *)
 
    VAR
		code : CARDINAL;	 
		fs : BITSET;	
		VK, KU, CH, CT : BOOLEAN;	 
 
    BEGIN
       fs := BITSET (LOWORD (mp1));	 (* flags *)				
       VK := (fs * BITSET (KC_VIRTUALKEY)) # {};			  
       KU := (fs * BITSET (KC_KEYUP)) # {};			
       CH := (fs * BITSET (KC_CHAR)) # {};			  
       CT := (fs * BITSET (KC_CTRL)) # {};			  
       IF (NOT KU) THEN			 
          code := LOWORD (mp2);	(* character code *)			  
          c1 := CHR (code);			  
          c2 := CHR (code DIV 256);			 
          IF ORD (c1) = 0E0H THEN	  (* function *)			 
             c1 := 0C;			   
          END;			 
          IF CT AND (NOT CH) AND (NOT VK) AND (code # 0) THEN			
             c1 := CHR (CARDINAL ((BITSET (ORD (c1)) * BITSET (1FH))));
          END;			 
          RETURN TRUE;
       ELSE
          RETURN FALSE;
       END;
    END KeyTranslate;
    
       
 PROCEDURE WindowProc ['WindowProc'] (
 (* Main Window Procedure -- Handles message from PM and elsewhere *)
       hwnd  : HWND;
       msg   : USHORT;   
       mp1   : MPARAM; 
       mp2   : MPARAM) : MRESULT [LONG, LOADDS];
    VAR
       ch : CHAR;
       hps       : HPS;
       pswp      : PSWP;
       c1, c2    : CHAR;
       
    BEGIN
       CASE msg OF 
          WM_HELP:
             IF TermMode THEN
                WinDlgBox (HWND_DESKTOP, hwnd, HelpDlgProc, 
                           0, IDM_TERMHELP, 0);
             ELSE
                WinDlgBox (HWND_DESKTOP, hwnd, HelpDlgProc, 
                           0, IDM_HELPMENU, 0);
             END;
             RETURN 0;
       |  WM_SETFULL:
             SetFull;
             RETURN 0;
       |  WM_SETRESTORE:
             SetRestore;
             RETURN 0;
       |  WM_SETMAX:
             SetMax;
             RETURN 0;
       |  WM_MINMAXFRAME:
             pswp := PSWP (mp1);
             IF BITSET (pswp^.fs) * BITSET (SWP_MINIMIZE) # {} THEN
                (* Don't Display Port Settings While Minimized *)
                WinSetWindowText (FrameWindow, ADR (Title));
             ELSE
                WinSetWindowText (FrameWindow, ADR (Banner));
                IF TermMode AND
                 (BITSET (pswp^.fs) * BITSET (SWP_RESTORE) # {}) THEN
                   (* Force window to be maximized in terminal mode *)
                   WinPostMsg (FrameWindow, WM_SETMAX, 0, 0);
                ELSIF (NOT TermMode) AND
                 (BITSET (pswp^.fs) * BITSET (SWP_MAXIMIZE) # {}) THEN
                   (* Prevent maximized window EXCEPT in terminal mode *)
                   WinPostMsg (FrameWindow, WM_SETRESTORE, 0, 0);
                ELSE
                   (* Do Nothing *)
                END;
             END;
             RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
       |  WM_CREATE:
             hdc := WinOpenWindowDC (hwnd);
             VioCreatePS (frame_hvps, 25, 80, 0, FORMAT_CGA, 0);
             VioCreatePS (child_hvps, 16, 40, 0, FORMAT_CGA, 0);
             DosCreateThread (TermThrProc, TermThr, ADR (TermStack[STKSIZE]));
             DosSuspendThread (TermThr);
             RETURN 0;
       |  WM_INITMENU:
             Check (ColorSet);
             RETURN 0;
       |  WM_COMMAND: 
             DoMenu (hwnd, mp1);
             RETURN 0;
       |  WM_TERMQUIT:
             TermMode := FALSE;
             DosSuspendThread (TermThr);
             VioAssociate (0, hvps);
             (* Restore The Window *)
             SetRestore;
             Enable (IDM_CONNECT);
             Enable (IDM_SEND);
             Enable (IDM_REC);
             Enable (IDM_DIR);
             Enable (IDM_OPTIONS);
             Enable (IDM_COLORS);
             RETURN 0;
       |  WM_TERM:
             PutPortChar (CHR (LOWORD (mp1)));   (* To Screen *)
             RETURN 0;
       |  WM_CHAR:
             IF TermMode THEN
                IF KeyTranslate (mp1, mp2, c1, c2) THEN
                   PutKbdChar (c1, c2);   (* To Port *)
                   RETURN 0;
                ELSE
                   RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2);
                END;
             ELSE
                RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
             END;
       |  WM_PAINT:
             hps := WinBeginPaint (hwnd, NULL, ADDRESS (NULL));
             GpiErase (hps);
             VioShowPS (25, 80, 0, hvps); 
             WinEndPaint (hps);
             RETURN 0;
       |  WM_SIZE:
             IF TermMode THEN
                RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2);
             ELSE
                RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
             END;
       |  WM_DESTROY:
             VioDestroyPS (frame_hvps);
             VioDestroyPS (child_hvps);
             RETURN 0;
       ELSE
          RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
       END;
    END WindowProc;
    
 PROCEDURE ChildWindowProc ['ChildWindowProc'] (
 (* Window Procedure for Send/Receive child windows *)
    hwnd : HWND;
    msg  : USHORT;   
    mp1  : MPARAM; 
    mp2  : MPARAM) : MRESULT [LONG, LOADDS];
    
    VAR
       mp : USHORT;
       hps : HPS;
       c1, c2 : CHAR;
    
    BEGIN
       CASE msg OF
          WM_PAINT:
             hps := WinBeginPaint (hwnd, NULL, ADDRESS (NULL));
             GpiErase (hps);
             VioShowPS (16, 40, 0, hvps); 
             WinEndPaint (hps);
             RETURN 0;
       |  WM_CHAR:
             IF KeyTranslate (mp1, mp2, c1, c2) AND (c1 = ESC) THEN
                Aborted := TRUE;
                RETURN 0;
             ELSE
                RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
             END;
       |  WM_PAD:
             mp := LOWORD (mp1);
             IF (mp = PAD_Error) OR (mp = PAD_Quit) THEN
                WriteLn;
                IF mp = PAD_Error THEN
                   WinMessageBox (HWND_DESKTOP, hwnd, 
                                  ADR ("File Transfer Aborted"),
                                  ADR (Class), 0, MB_OK + MB_ICONEXCLAMATION);
                ELSE
                   WinMessageBox (HWND_DESKTOP, hwnd, 
                                     ADR ("File Transfer Completed"),
                                     ADR (Class), 0, MB_OK + MB_ICONASTERISK);
                END;
                DosSleep (2000);
                VioAssociate (0, hvps);
                WinDestroyWindow(ChildFrameWindow);
                Enable (IDM_CONNECT);
                Enable (IDM_SEND);
                Enable (IDM_REC);
                Enable (IDM_DIR);
                Enable (IDM_OPTIONS);
                Enable (IDM_COLORS);
             ELSE
                DoPADMsg (mp1, mp2);
             END;
             RETURN 0;
       |  WM_DL:
             DoDLMsg (mp1, mp2);
             RETURN 0;
       |  WM_SIZE:
             WinSetWindowPos (ChildFrameWindow, 0,
                Pos.cx DIV 4, Pos.cy DIV 4, 
                Pos.cx DIV 2, Pos.cy DIV 2 - 3,
                SWP_MOVE + SWP_SIZE);
             RETURN WinDefAVioWindowProc (hwnd, msg, mp1, mp2);
       ELSE
          RETURN WinDefWindowProc (hwnd, msg, mp1, mp2);
       END;
    END ChildWindowProc;

BEGIN (* Module Initialization *)

  WITH Settings[ID_COM1 - COM_OFF] DO
     baudrate := ID_B1200;
     parity := ID_EVEN;
     databits := ID_DATA7;
     stopbits := ID_STOP1;
  END;
  
  WITH Settings[ID_COM2 - COM_OFF] DO
     baudrate := ID_B19K2;
     parity := ID_EVEN;
     databits := ID_DATA7;
     stopbits := ID_STOP1;
  END;
  PrevComPort := NONE;
  comport := ID_COM1;
  TermMode := FALSE;   (* Not Initially in Terminal Emulation Mode *)

END Shell.

[LISTING TEN]

IMPLEMENTATION MODULE Term; (* TVI950 Terminal Emulation for Kermit *)

 FROM Drives IMPORT
    SetDrive;
    
 FROM Directories IMPORT
    FileAttributes, AttributeSet, DirectoryEntry, FindFirst, FindNext;
    
 FROM SYSTEM IMPORT
    ADR;
 FROM OS2DEF IMPORT
    ULONG;
          
 FROM DosCalls IMPORT
    DosChDir, DosSleep;
          
 FROM Screen IMPORT
    ClrScr, ClrEol, GotoXY, GetXY,
    Right, Left, Up, Down, WriteAtt, WriteString, WriteLn, Write,
    attribute, NORMAL, HIGHLIGHT, REVERSE;		
    
 FROM PMWIN IMPORT
    WinPostMsg, MPFROM2SHORT;
 FROM Shell IMPORT
    comport, FrameWindow;
    
 FROM KH IMPORT
    COM_OFF;
          
 FROM CommPort IMPORT
    CommStatus, GetChar, SendChar;
          
 FROM Strings IMPORT
    Length, Concat;
 
 IMPORT ASCII;
 CONST
    (* Key codes:  Note: F1 -- F12 are actually Shift-F1 -- Shift-F12 *)
    F1 = 124C;
    F2 = 125C;
    F3 = 126C;
    F4 = 127C;
    F5 = 130C;
    F6 = 131C;
    F7 = 132C;
    F8 = 133C;
    F9 = 134C;
    F10 = 135C;
    F11 = 207C;
    F12 = 210C;
    AF1 = 213C;   (* Alt-F1 *)
    AF2 = 214C;   (* Alt-F2 *)
    INS = 122C;
    DEL = 123C;
    HOME = 107C;
    PGDN = 121C;   (* synonym for PF10 *)
    PGUP = 111C;   (* synonym for PF11 *)
    ENDD = 117C;   (* synonym for PF12 *)
    UPARROW = 110C;
    DOWNARROW = 120C;
    LEFTARROW = 113C;
    RIGHTARROW = 115C;
    CtrlX = 30C;
    CtrlCaret = 36C;
    CtrlZ = 32C;
    CtrlL = 14C;
    CtrlH = 10C;
    CtrlK = 13C;
    CtrlJ = 12C;
    CtrlV = 26C;
    ESC = 33C;
    BUFSIZE = 4096;   (* character buffer used by term thread *)
 
 VAR
    commStat : CommStatus;
    echo : (Off, Local, On);      
    newline: BOOLEAN;   (* translate <cr> to <cr><lf> *)
    Insert : BOOLEAN;
                
 PROCEDURE Dir (path : ARRAY OF CHAR);
 (* Change drive and/or directory; display a directory (in wide format) *)
 
    VAR
       gotFN : BOOLEAN;
       filename : ARRAY [0..20] OF CHAR;
       attr : AttributeSet;
       ent : DirectoryEntry;
       i, j, k : INTEGER;
       
    BEGIN
       filename := "";   (* in case no directory change *)
       i := Length (path);
       IF (i > 2) AND (path[1] = ':') THEN   (* drive specifier *)
          DEC (i, 2);
          SetDrive (ORD (CAP (path[0])) - ORD ('A')); 
          FOR j := 0 TO i DO   (* strip off the drive specifier *)
             path[j] := path[j + 2];
          END;
       END;
       IF i # 0 THEN
          gotFN := FALSE;
          WHILE (i >= 0) AND (path[i] # '\') DO
             IF path[i] = '.' THEN
                gotFN := TRUE;
             END;
             DEC (i);
          END;
          IF gotFN THEN
             j := i + 1;
             k := 0;
             WHILE path[j] # 0C DO
                filename[k] := path[j];
                INC (k);       INC (j);
             END;
             filename[k] := 0C;
             IF (i = -1) OR ((i = 0) AND (path[0] = '\')) THEN
                INC (i);
             END;
             path[i] := 0C;
          END;
       END;
       IF Length (path) # 0 THEN
          DosChDir (ADR (path), 0);
       END;
       IF Length (filename) = 0 THEN
          filename := "*.*";
       END;
       attr := AttributeSet {ReadOnly, Directory, Archive};
       i := 1;   (* keep track of position on line *)
       ClrScr;         
       gotFN := FindFirst (filename, attr, ent);
       WHILE gotFN DO
          WriteString (ent.name);
          j := Length (ent.name);
          WHILE j < 12 DO   (* 12 is maximum length for "filename.typ" *)
             Write (' ');
             INC (j);
          END;
          INC (i);   (* next position on this line *)
          IF i > 5 THEN
             i := 1;   (* start again on new line *)
             WriteLn;
          ELSE
             WriteString (" | ");
          END;
          gotFN := FindNext (ent);
       END;
       WriteLn;
    END Dir;
 PROCEDURE InitTerm;
 (* Clear Screen, Home Cursor, Get Ready For Terminal Emulation *)
    BEGIN
       ClrScr;
       Insert := FALSE;
       attribute := NORMAL;
    END InitTerm;   
 PROCEDURE PutKbdChar (ch1, ch2 : CHAR);
 (* Process a character received from the keyboard *)
    BEGIN
       IF ch1 = ASCII.enq THEN   (* Control-E *)
          echo := On;
       ELSIF ch1 = ASCII.ff THEN   (* Control-L *)
          echo := Local;
       ELSIF ch1 = ASCII.dc4 THEN   (* Control-T *)
          echo := Off;
       ELSIF ch1 = ASCII.so THEN   (* Control-N *)
          newline := TRUE;
       ELSIF ch1 = ASCII.si THEN   (* Control-O *)
          newline := FALSE;
       ELSIF (ch1 = ASCII.can) OR (ch1 = ESC) THEN
          attribute := NORMAL;
          WinPostMsg (FrameWindow, WM_TERMQUIT, 0, 0);
       ELSIF ch1 = 0C THEN
          Function (ch2);
       ELSE
          commStat := SendChar (comport - COM_OFF, ch1, FALSE);
          IF (echo = On) OR (echo = Local) THEN
             WriteAtt (ch1);
          END;
       END;
    END PutKbdChar;
 PROCEDURE Function (ch : CHAR);
 (* handles the function keys -- including PF1 - PF12, etc. *)
    BEGIN
       CASE ch OF
          F1 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, '@', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F2 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'A', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F3 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'B', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F4 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'C', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F5 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'D', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F6 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'E', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F7 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'F', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F8 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'G', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F9 :  commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'H', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F10, 
          PGDN: commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'I', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F11,
          AF1,
          PGUP: commStat := SendChar (comport - COM_OFF, ASCII.soh, FALSE);   
                commStat := SendChar (comport - COM_OFF, 'J', FALSE);   
                commStat := SendChar (comport - COM_OFF, ASCII.cr, FALSE);
       |  F12,
          AF2,
          ENDD: commStat := SendChar (comport - COM_OFF, ESC, FALSE);
                commStat := SendChar (comport - COM_OFF, 'Q', FALSE);
       |  INS : IF NOT Insert THEN
                   commStat := SendChar (comport - COM_OFF, ESC, FALSE);
                   commStat := SendChar (comport - COM_OFF, 'E', FALSE);
                END;
       |  DEL : commStat := SendChar (comport - COM_OFF, ESC, FALSE);
                commStat := SendChar (comport - COM_OFF, 'R', FALSE);
       |  HOME       : commStat := SendChar (comport - COM_OFF, CtrlZ, FALSE);
       |  UPARROW    : commStat := SendChar (comport - COM_OFF, CtrlK, FALSE);
       |  DOWNARROW  : commStat := SendChar (comport - COM_OFF, CtrlV, FALSE);
       |  LEFTARROW  : commStat := SendChar (comport - COM_OFF, CtrlH, FALSE);
       |  RIGHTARROW : commStat := SendChar (comport - COM_OFF, CtrlL, FALSE);
       ELSE
          (* do nothing *)
       END;
    END Function;
    
 PROCEDURE TermThrProc;
 (* Thread to get characters from port, put into buffer *)
 
    VAR
       ch : CHAR;
       
    BEGIN
       LOOP
          IF GetChar (comport - COM_OFF, ch) = Success THEN
             WinPostMsg (FrameWindow, WM_TERM, MPFROM2SHORT (ORD (ch), 0), 0);
          ELSE
             DosSleep (0);
          END
       END;
    END TermThrProc;
 VAR
    EscState, CurState1, CurState2 : BOOLEAN;
    CurChar1 : CHAR;
    
 PROCEDURE PutPortChar (ch : CHAR);
 (* Process a character received from the port *)
    BEGIN
       IF EscState THEN
          EscState := FALSE;
          IF ch = '=' THEN
             CurState1 := TRUE;
          ELSE
             Escape (ch);
          END;
       ELSIF CurState1 THEN
          CurState1 := FALSE;
          CurChar1 := ch;
          CurState2 := TRUE;
       ELSIF CurState2 THEN
          CurState2 := FALSE;
          Cursor (ch);
       ELSE
          CASE ch OF
             CtrlCaret, CtrlZ : ClrScr;
          |  CtrlL : Right;
          |  CtrlH : Left;
          |  CtrlK : Up;
          |  CtrlJ : Down;
          |  ESC   : EscState := TRUE;
          ELSE
             WriteAtt (ch);
             IF newline AND (ch = ASCII.cr) THEN
                WriteLn;
             END;
          END;
       END;
       IF echo = On THEN
          commStat := SendChar (comport - COM_OFF, ch, FALSE);
       END;
    END PutPortChar;
    
    
 PROCEDURE Escape (ch : CHAR);
 (* handles escape sequences *)
    BEGIN
       CASE ch OF
          '*' : ClrScr;
       |  'T', 'R' : ClrEol;
       |  ')' : attribute := NORMAL;
       |  '(' : attribute := HIGHLIGHT;   
       |  'f' : InsertMsg;
       |  'g' : InsertOn;
       ELSE
          (* ignore *)
       END;
    END Escape;
    
    
 PROCEDURE Cursor (ch : CHAR);
 (* handles cursor positioning *)
 
    VAR
       x, y : CARDINAL;
       
    BEGIN
       y := ORD (CurChar1) - 20H;
       x := ORD (ch) - 20H;
       GotoXY (x, y);   (* adjust for HOME = (1, 1) *)
    END Cursor;
    
    
 VAR
    cx, cy : CARDINAL;
    
 PROCEDURE InsertMsg;
 (* get ready insert mode -- place a message at the bottom of the screen *)
    BEGIN
       IF NOT Insert THEN
          GetXY (cx, cy);   (* record current position *)
          GotoXY (1, 24);
          ClrEol;
          attribute := REVERSE;
       ELSE   (* exit Insert mode *)
          GetXY (cx, cy);
          GotoXY (1, 24);
          ClrEol;
          GotoXY (cx, cy);
          Insert := FALSE;
       END;
    END InsertMsg;   
    
    
 PROCEDURE InsertOn;
 (* enter insert mode -- after INSERT MODE message is printed *)
    BEGIN
       attribute := NORMAL;
       GotoXY (cx, cy);
       Insert := TRUE;
    END InsertOn;   
    

BEGIN (* module initialization *)

 echo := Off;
 newline := FALSE;
 Insert := FALSE;
 EscState := FALSE;
 CurState1 := FALSE;
 CurState2 := FALSE;

END Term.

[LISTING ELEVEN]

IMPLEMENTATION MODULE Screen; (* module to perform "low level" screen functions (via AVIO) *)

 IMPORT ASCII;
 
 FROM SYSTEM IMPORT
    ADR;
 FROM Strings IMPORT
    Length;
    
 FROM Conversions IMPORT
    IntToString;
 FROM KH IMPORT
    IDM_GREEN;
                
 FROM Vio IMPORT
    VioSetCurPos, VioGetCurPos, VioScrollUp, 
    VioWrtNCell, VioWrtTTY, VioCell;
 CONST
    GREY = 07H;
    WHITE = 0FH;
    REV_GY = 70H;
    GREEN = 02H;
    LITE_GRN = 0AH;
    REV_GRN = 20H;
    AMBER = 06H;
    LITE_AMB = 0EH;
    REV_AMB = 60H;
    RED = 0CH;
    CY_BK = 0B0H;
    CY_BL = 0B9H;
    REV_RD = 0CFH;
    REV_BL = 9FH;
    MAGENTA = 05H;
    
          
 VAR	
    (* From Definition Module
    NORMAL : CARDINAL;
    HIGHLIGHT : CARDINAL;
    REVERSE : CARDINAL;
	attribute : CARDINAL;	
    hvps : HVPS;
    *)
   x, y : CARDINAL;	 
   bCell : VioCell;	 
    
 PROCEDURE White;
 (* Sets up colors: Monochrome White *)
    BEGIN
       NORMAL := GREY;
       HIGHLIGHT := WHITE;
       REVERSE := REV_GY;
       attribute := NORMAL;
    END White;
    
    
 PROCEDURE Green;
 (* Sets up colors: Monochrome Green *)
    BEGIN
       NORMAL := GREEN;
       HIGHLIGHT := LITE_GRN;
       REVERSE := REV_GRN;
       attribute := NORMAL;
    END Green;
    
    
 PROCEDURE Amber;
 (* Sets up colors: Monochrome Amber *)
    BEGIN
       NORMAL := AMBER;
       HIGHLIGHT := LITE_AMB;
       REVERSE := REV_AMB;
       attribute := NORMAL;
    END Amber;
    
    
 PROCEDURE Color1;
 (* Sets up colors: Blue, Red, Green *)
    BEGIN
       NORMAL := GREEN;
       HIGHLIGHT := RED;
       REVERSE := REV_BL;
       attribute := NORMAL;
    END Color1;
    
    
 PROCEDURE Color2;
 (* Sets up colors: Cyan Background; Black, Blue, White-on-Red *)
    BEGIN
       NORMAL := CY_BK;
       HIGHLIGHT := CY_BL;
       REVERSE := REV_RD;
       attribute := NORMAL;
    END Color2;
    
    
 PROCEDURE HexToString (num : INTEGER;
                        size : CARDINAL;
                        VAR buf : ARRAY OF CHAR;
                        VAR I : CARDINAL;
                        VAR Done : BOOLEAN);
 (* Local Procedure to convert a number to a string, represented in HEX *)   
 
    CONST
       ZERO = 30H;   (* ASCII code *)
       A = 41H; 
       
    VAR
       i : CARDINAL;
       h : CARDINAL;
       t : ARRAY [0..10] OF CHAR;
                              
    BEGIN
       i := 0;
       REPEAT
          h := num MOD 16;
          IF h <= 9 THEN
             t[i] := CHR (h + ZERO);
          ELSE
             t[i] := CHR (h - 10 + A);
          END;
          INC (i);
          num := num DIV 16;
       UNTIL num = 0;
       
       IF (size > HIGH (buf)) OR (i > HIGH (buf)) THEN
          Done := FALSE;
          RETURN;
       ELSE
          Done := TRUE;
       END;
       
       WHILE size > i DO
          buf[I] := '0';   (* pad with zeros *)
          DEC (size);
          INC (I);
       END;
       
       WHILE i > 0 DO
          DEC (i);
          buf[I] := t[i];
          INC (I);
       END;
       
       buf[I] := 0C;
    END HexToString;
                              
 
 PROCEDURE ClrScr;	  
 (* Clear the screen, and home the cursor *)	 
    BEGIN	  
       bCell.ch := ' ';	 (* space = blank screen *)	
       bCell.attr := CHR (NORMAL);	(* Normal Video Attribute *)	 
       VioScrollUp (0, 0, 24, 79, 25, bCell, hvps);	  
       GotoXY (0, 0);	  
    END ClrScr;     

PROCEDURE ClrEol;

 (* clear from the current cursor position to the end of the line *)    
    BEGIN     
       GetXY (x, y);     (* current cursor position *)    
       bCell.ch := ' ';    (* space = blank *)     
       bCell.attr := CHR (NORMAL);   (* Normal Video Attribute *)    
       VioScrollUp (y, x, y, 79, 1, bCell, hvps);   
    END ClrEol;     
 
 
 PROCEDURE Right;    
 (* move cursor to the right *)   
    BEGIN     
       GetXY (x, y);    
       INC (x);     
       GotoXY (x, y);     
    END Right;    
 
 
 PROCEDURE Left;   
 (* move cursor to the left *)     
    BEGIN     
       GetXY (x, y);    
       DEC (x);     
       GotoXY (x, y);     
    END Left;   
 
 
 PROCEDURE Up;    
 (* move cursor up *)     
    BEGIN     
       GetXY (x, y);    
       DEC (y);     
       GotoXY (x, y);     
    END Up;    
 
 
 PROCEDURE Down;   
 (* move cursor down *)    
    BEGIN     
       GetXY (x, y);    
       INC (y);     
       GotoXY (x, y);     
    END Down;   
 
 
 PROCEDURE GotoXY (col, row : CARDINAL);   
 (* position cursor at column, row *)   
    BEGIN     
       IF (col <= 79) AND (row <= 24) THEN     
          VioSetCurPos (row, col, hvps);   
       END;    
    END GotoXY;     
 
 
 PROCEDURE GetXY (VAR col, row : CARDINAL);   
 (* determine current cursor position *)   
    BEGIN     
       VioGetCurPos (row, col, hvps);   
    END GetXY;    
 
 PROCEDURE Write (c : CHAR);
 (* Write a Character *)
    BEGIN
       WriteAtt (c);
    END Write;
    
    
 PROCEDURE WriteString (str : ARRAY OF CHAR);
 (* Write String *)
 
    VAR
       i : CARDINAL;
       c : CHAR;
       
    BEGIN
       i := 0;
       c := str[i];
       WHILE c # 0C DO
          Write (c);
          INC (i);
          c := str[i];
       END;
    END WriteString;
    
 PROCEDURE WriteInt (n : INTEGER; s : CARDINAL);
 (* Write Integer *)
 
    VAR
       i : CARDINAL;
       b : BOOLEAN;
       str : ARRAY [0..6] OF CHAR;
       
    BEGIN
       i := 0;
       IntToString (n, s, str, i, b);
       WriteString (str);
    END WriteInt;
    
 
 PROCEDURE WriteHex (n, s : CARDINAL);
 (* Write a Hexadecimal Number *)
 
    VAR
       i : CARDINAL;
       b : BOOLEAN;
       str : ARRAY [0..6] OF CHAR;
       
    BEGIN
       i := 0;
       HexToString (n, s, str, i, b);
       WriteString (str);
    END WriteHex;
    
 
 PROCEDURE WriteLn;
 (* Write <cr> <lf> *)
    BEGIN
       Write (ASCII.cr);   Write (ASCII.lf); 
    END WriteLn;
 
 
 PROCEDURE WriteAtt (c : CHAR);   
 (* write character and attribute at cursor position *)   
 
    VAR   
       s : ARRAY [0..1] OF CHAR;    
    BEGIN     
       GetXY (x, y);
       IF (c = ASCII.ht) THEN
          bCell.ch := ' ';
          bCell.attr := CHR (attribute);   
          REPEAT
             VioWrtNCell (bCell, 1, y, x, hvps);     
             Right;
          UNTIL (x MOD 8) = 0; 
       ELSIF (c = ASCII.cr) OR (c = ASCII.lf)
        OR (c = ASCII.bel) OR (c = ASCII.bs) THEN   
          s[0] := c;    s[1] := 0C;   
          VioWrtTTY (ADR (s), 1, hvps);     
          IF c = ASCII.lf THEN
             ClrEol;
          END;
       ELSE    
          bCell.ch := c;     
          bCell.attr := CHR (attribute);   
          VioWrtNCell (bCell, 1, y, x, hvps);     
          Right;   
       END;    
    END WriteAtt;    
 

BEGIN (* module initialization *)

 ColorSet := IDM_GREEN;
 NORMAL := GREEN;
 HIGHLIGHT := LITE_GRN;
 REVERSE := REV_GRN;
 attribute := NORMAL;     

END Screen.

[LISTING TWELVE]

() (* *) (* Copyright © 1988, 1989 *) (* by Stony Brook Software *) (* and *) (* Copyright © 1990 *) (* by Brian R. Anderson *) (* All rights reserved. *) (* *) ()

IMPLEMENTATION MODULE CommPort [7];

 FROM SYSTEM IMPORT
    ADR, BYTE, WORD, ADDRESS;
 FROM Storage IMPORT
    ALLOCATE, DEALLOCATE;
    
 FROM DosCalls IMPORT
    DosOpen, AttributeSet, DosDevIOCtl, DosClose, DosRead, DosWrite;
 TYPE
    CP = POINTER TO CHAR;
    
 VAR
    pn : CARDINAL;
    Handle : ARRAY [0..3] OF CARDINAL;
    BufIn : ARRAY [0..3] OF CP;
    BufOut : ARRAY [0..3] OF CP;
    BufStart : ARRAY [0..3] OF CP;
    BufLimit : ARRAY [0..3] OF CP;
    BufSize : ARRAY [0..3] OF CARDINAL;
    Temp : ARRAY [1..1024] OF CHAR;   (* size of OS/2's serial queue *)
    
 PROCEDURE CheckPort (portnum : CARDINAL) : BOOLEAN;
 (* Check for a valid port number and open the port if it not alredy open *)
 
    CONST
       PortName : ARRAY [0..3] OF ARRAY [0..4] OF CHAR =
          [['COM1', 0C], ['COM2', 0C], ['COM3', 0C], ['COM4', 0C]];
    VAR
       Action : CARDINAL;
       
    BEGIN
       (* check the port number *)
       IF portnum > 3 THEN
          RETURN FALSE;
       END;
       (* attempt to open the port if it is not already open *)
       IF Handle[portnum] = 0 THEN
          IF DosOpen(ADR(PortName[portnum]), Handle[portnum], Action, 0,
           AttributeSet{}, 1, 12H, 0) # 0 THEN
             RETURN FALSE;
          END;
       END;
       RETURN TRUE;
    END CheckPort;
 
 PROCEDURE InitPort (portnum : CARDINAL; speed : BaudRate; data : DataBits;
                       stop : StopBits; check : Parity) : CommStatus;
 (* Initialize a port *)
    
    CONST
       Rate : ARRAY BaudRate OF CARDINAL =
                 [110, 150, 300, 600, 1200, 2400, 4800, 9600, 19200];
       TransParity : ARRAY Parity OF BYTE = [2, 1, 0];
    TYPE
       LineChar =  RECORD
                      bDataBits : BYTE;
                      bParity : BYTE;
                      bStopBits : BYTE;
                   END;
    VAR
       LC : LineChar;
             
    BEGIN
       (* Check the port number *)
       IF NOT CheckPort(portnum) THEN
          RETURN InvalidPort;
       END;
       (* Set the baud rate *)
       IF DosDevIOCtl(0, ADR(Rate[speed]), 41H, 1, Handle[portnum]) # 0 THEN
          RETURN InvalidParameter;
       END;
       (* set the characteristics *)
       LC.bDataBits := BYTE(data);
       IF stop = 1 THEN
          DEC (stop);    (* 0x00 = 1 stop bits;    0x02 = 2 stop bits *)
       END;
       LC.bStopBits := BYTE(stop);
       LC.bParity := TransParity[check];
       IF DosDevIOCtl(0, ADR(LC), 42H, 1, Handle[portnum]) # 0 THEN
          RETURN InvalidParameter;
       END;
       RETURN Success;
    END InitPort;
 PROCEDURE StartReceiving (portnum, bufsize : CARDINAL) : CommStatus;
 (* Start receiving characters on a port *)
    BEGIN
       IF NOT CheckPort(portnum) THEN
          RETURN InvalidPort;
       END;
       IF BufStart[portnum] # NIL THEN
          RETURN AlreadyReceiving;
       END;
       ALLOCATE (BufStart[portnum], bufsize);
       BufIn[portnum] := BufStart[portnum];
       BufOut[portnum] := BufStart[portnum];
       BufLimit[portnum] := BufStart[portnum];
       INC (BufLimit[portnum]:ADDRESS, bufsize - 1);
       BufSize[portnum] := bufsize;
       RETURN Success;
    END StartReceiving;
 PROCEDURE StopReceiving (portnum : CARDINAL) : CommStatus;
 (* Stop receiving characters on a port *)
    BEGIN
       IF NOT CheckPort(portnum) THEN
          RETURN InvalidPort;
       END;
       IF BufStart[portnum] # NIL THEN
          DEALLOCATE (BufStart[portnum], BufSize[portnum]);
          BufLimit[portnum] := NIL;
          BufIn[portnum] := NIL;
          BufOut[portnum] := NIL;
          BufSize[portnum] := 0;
       END;
       DosClose(Handle[portnum]);
       Handle[portnum] := 0;
       RETURN Success;
    END StopReceiving;
 PROCEDURE GetChar (portnum : CARDINAL; VAR ch : CHAR) : CommStatus;
 (* Get a character from the comm port *)
 
    VAR
       status : CARDINAL;
       read : CARDINAL;
       que : RECORD
                ct : CARDINAL;
                sz : CARDINAL;
             END;
       i : CARDINAL;
             
    BEGIN
       IF BufStart[portnum] = NIL THEN
          RETURN NotReceiving;
       END;
       IF NOT CheckPort(portnum) THEN
          RETURN InvalidPort;
       END;
       status := DosDevIOCtl (ADR (que), 0, 68H, 1, Handle[portnum]);
       IF (status = 0) AND (que.ct # 0) THEN
          status := DosRead (Handle[portnum], ADR (Temp), que.ct, read);
          IF (status # 0) OR (read = 0) THEN
             RETURN NotReceiving;
          END;
          FOR i := 1 TO read DO
             BufIn[portnum]^ := Temp[i];
             IF BufIn[portnum] = BufLimit[portnum] THEN
                BufIn[portnum] := BufStart[portnum];
             ELSE
                INC (BufIn[portnum]:ADDRESS);
             END;
             IF BufIn[portnum] = BufOut[portnum] THEN
                RETURN BufferOverflow;
             END;
          END;
       END;
       
       IF BufIn[portnum] = BufOut[portnum] THEN
          RETURN NoCharacter;
       END;
       ch := BufOut[portnum]^;
       IF BufOut[portnum] = BufLimit[portnum] THEN
          BufOut[portnum] := BufStart[portnum];
       ELSE
          INC (BufOut[portnum]:ADDRESS);
       END;
       RETURN Success;
    END GetChar;
 PROCEDURE SendChar (portnum : CARDINAL; ch : CHAR; 
                       modem : BOOLEAN) : CommStatus;
 (* send a character to the comm port *)
    
    VAR
       wrote : CARDINAL;
       status : CARDINAL;
       commSt : CHAR;
       
    BEGIN
       IF NOT CheckPort(portnum) THEN
          RETURN InvalidPort;
       END;
       status := DosDevIOCtl (ADR (commSt), 0, 64H, 1, Handle[portnum]);
       IF (status # 0) OR (commSt # 0C) THEN
          RETURN TimeOut;
       ELSE
          status := DosWrite(Handle[portnum], ADR(ch), 1, wrote);
          IF (status # 0) OR (wrote # 1) THEN
             RETURN TimeOut;
          ELSE
             RETURN Success;
          END;
       END;
    END SendChar;

BEGIN (* module initialization *)

 (* nothing open yet *)
 FOR pn := 0 TO 3 DO
    Handle[pn] := 0;
    BufStart[pn] := NIL;
    BufLimit[pn] := NIL;
    BufIn[pn] := NIL;
    BufOut[pn] := NIL;
    BufSize[pn] := 0;
 END;

END CommPort.

[LISTING THIRTEEN]

IMPLEMENTATION MODULE Files; (* File I/O for Kermit *)

 FROM FileSystem IMPORT
    File, Response, Delete, Lookup, Close, ReadNBytes, WriteNBytes;
 FROM Strings IMPORT
    Append;
    
 FROM Conversions IMPORT
    CardToString;
    
 FROM SYSTEM IMPORT
    ADR, SIZE;
    
 TYPE
    buffer = ARRAY [1..512] OF CHAR;
    
 VAR
    ext : CARDINAL;  (* new file extensions to avoid name conflict *)
    inBuf, outBuf : buffer;
    inP, outP : CARDINAL;   (* buffer pointers *)
    read, written : CARDINAL;   (* number of bytes read or written *)
                                (* by ReadNBytes or WriteNBytes    *)
     
    
 PROCEDURE Open (VAR f : File; name : ARRAY OF CHAR) : Status;
 (* opens an existing file for reading, returns status *)
    BEGIN
       Lookup (f, name, FALSE);
       IF f.res = done THEN
          inP := 0;   read := 0;
          RETURN Done;
       ELSE
          RETURN Error;
       END;
    END Open;
    
    
 PROCEDURE Create (VAR f : File; name : ARRAY OF CHAR) : Status;
 (* creates a new file for writing, returns status *)
 
    VAR
       ch : CHAR;
       str : ARRAY [0..3] OF CHAR;
       i : CARDINAL;
       b : BOOLEAN;
       
    BEGIN
       LOOP
          Lookup (f, name, FALSE);   (* check to see if file exists *)
          IF f.res = done THEN
             Close (f);
             (* Filename Clash: Change file name *)
             IF ext > 99 THEN   (* out of new names... *)
                RETURN Error;
             END;
             i := 0;
             WHILE (name[i] # 0C) AND (name[i] # '.') DO
                INC (i);   (* scan for end of filename *)
             END;
             name[i] := '.';   name[i + 1] := 'K';   name[i + 2] := 0C;
             i := 0;
             CardToString (ext, 1, str, i, b); 
             Append (name, str);   (* append new extension *)
             INC (ext);
          ELSE
             EXIT;
          END;
       END;
       Lookup (f, name, TRUE);
       IF f.res = done THEN
          outP := 0;
          RETURN Done;
       ELSE
          RETURN Error;
       END;
    END Create;
    
    
 PROCEDURE CloseFile (VAR f : File; Which : FileType) : Status;
 (* closes a file after reading or writing *)
    BEGIN
       written := outP;
       IF (Which = Output) AND (outP > 0) THEN
          WriteNBytes (f, ADR (outBuf), outP);
          written := f.count;
       END;
       Close (f);
       IF (written = outP) AND (f.res = done) THEN
          RETURN Done;
       ELSE
          RETURN Error;
       END;
    END CloseFile;
    
    
 PROCEDURE Get (VAR f : File; VAR ch : CHAR) : Status;
 (* Reads one character from the file, returns status *)
    BEGIN
       IF inP = read THEN
          ReadNBytes (f, ADR (inBuf), SIZE (inBuf));
          read := f.count;
          inP := 0;
       END;
       IF read = 0 THEN
          RETURN EOF;
       ELSE
          INC (inP);
          ch := inBuf[inP];
          RETURN Done;
       END;
    END Get;
    
    
 PROCEDURE Put (ch : CHAR);
 (* Writes one character to the file buffer *)
    BEGIN
       INC (outP);
       outBuf[outP] := ch;
    END Put;
    
    
 PROCEDURE DoWrite (VAR f : File) : Status;
 (* Writes buffer to disk only if nearly full *)
    BEGIN
       IF outP < 400 THEN   (* still room in buffer *)
          RETURN Done;
       ELSE
          WriteNBytes (f, ADR (outBuf), outP);
          written := f.count;
          IF (written = outP) AND (f.res = done) THEN
             outP := 0;
             RETURN Done;
          ELSE
             RETURN Error;
          END;
       END;
    END DoWrite;  
    

BEGIN (* module initialization *)

 ext := 0;

END Files.

[LISTING FOURTEEN]

DEFINITION MODULE KH;

CONST

 ID_OK        =  25;
 
 PARITY_OFF   =  150;
 ID_NONE      =  152;
 ID_ODD       =  151;
 ID_EVEN      =  150;
 
 STOP_OFF     =  140;
 ID_STOP2     =  142;
 ID_STOP1     =  141;
 
 DATA_OFF     =  130;
 ID_DATA8     =  138;
 ID_DATA7     =  137;
 BAUD_OFF     =  120;   
 ID_B19K2     =  128;
 ID_B9600     =  127;
 ID_B4800     =  126;
 ID_B2400     =  125;
 ID_B1200     =  124;
 ID_B600      =  123;
 ID_B300      =  122;
 ID_B150      =  121;
 ID_B110      =  120;
 
 COM_OFF      =  100;
 ID_COM2      =  101;
 ID_COM1      =  100;
 IDM_C2       =  24;
 IDM_C1       =  23;
 IDM_AMBER    =  22;
 IDM_GREEN    =  21;
 IDM_WHITE    =  20;
 IDM_COLORS   =  19;
 IDM_DIREND   =  18;
 ID_DIRPATH   =  17;
 ID_SENDFN    =  16;
 IDM_DIRPATH  =  15;
 IDM_SENDFN   =  14;
 IDM_TERMHELP =  13;
 IDM_HELPMENU =  12;   
 IDM_ABOUT    =  11;
 IDM_PARITY   =  10;
 IDM_STOPBITS =  9;
 IDM_DATABITS =  8;
 IDM_BAUDRATE =  7;
 IDM_COMPORT  =  6;
 IDM_QUIT     =  5;
 IDM_REC      =  4;
 IDM_SEND     =  3;
 IDM_CONNECT  =  2;
 IDM_DIR      =  1;
 IDM_OPTIONS  =  52;
 IDM_FILE     =  51;
 IDM_KERMIT   =  50;

END KH.

[LISTING FIFTEEN]

IMPLEMENTATION MODULE KH; END KH.

[LISTING SIXTEEN]

#define IDM_KERMIT 50 #define IDM_FILE 51 #define IDM_OPTIONS 52 #define IDM_HELP 0 #define IDM_DIR 1 #define IDM_CONNECT 2 #define IDM_SEND 3 #define IDM_REC 4 #define IDM_QUIT 5 #define IDM_COMPORT 6 #define IDM_BAUDRATE 7 #define IDM_DATABITS 8 #define IDM_STOPBITS 9 #define IDM_PARITY 10 #define IDM_ABOUT 11 #define IDM_HELPMENU 12 #define IDM_TERMHELP 13 #define IDM_SENDFN 14 #define IDM_DIRPATH 15 #define ID_SENDFN 16 #define ID_DIRPATH 17 #define IDM_DIREND 18 #define IDM_COLORS 19 #define IDM_WHITE 20 #define IDM_GREEN 21 #define IDM_AMBER 22 #define IDM_C1 23 #define IDM_C2 24 #define ID_OK 25 #define ID_COM1 100 #define ID_COM2 101 #define ID_B110 120 #define ID_B150 121 #define ID_B300 122 #define ID_B600 123 #define ID_B1200 124 #define ID_B2400 125 #define ID_B4800 126 #define ID_B9600 127 #define ID_B19K2 128 #define ID_DATA7 137 #define ID_DATA8 138 #define ID_STOP1 141 #define ID_STOP2 142 #define ID_EVEN 150 #define ID_ODD 151 #define ID_NONE 152

[LISTING SEVENTEEN]

IMPLEMENTATION MODULE DataLink; (* Sends and Receives Packets for PCKermit *)

 FROM ElapsedTime IMPORT
    StartTime, GetTime;
 FROM Screen IMPORT
    ClrScr, WriteString, WriteLn;
 FROM OS2DEF IMPORT
    HIWORD, LOWORD;
 FROM PMWIN IMPORT
    MPARAM, MPFROM2SHORT, WinPostMsg;
    
 FROM Shell IMPORT
    ChildFrameWindow, comport;
                
 FROM CommPort IMPORT
    CommStatus, GetChar, SendChar;
    
 FROM PAD IMPORT
    PacketType, yourNPAD, yourPADC, yourEOL; 
 FROM KH IMPORT
    COM_OFF;
    
 FROM SYSTEM IMPORT
    BYTE;
    
 IMPORT ASCII;
 CONST
    MAXtime = 100;   (* hundredths of a second -- i.e., one second *)
    MAXsohtrys = 100;
    DL_BadCS = 1;
    DL_NoSOH = 2;
    
 TYPE
    SMALLSET = SET OF [0..7];   (* BYTE *)               
    
 VAR
    ch : CHAR;
    status : CommStatus;
    
 PROCEDURE Delay (t : CARDINAL);
 (* delay time in milliseconds *)
 
    VAR
       tmp : LONGINT;
       
    BEGIN
       tmp := t DIV 10;
       StartTime;
       WHILE GetTime() < tmp DO
       END;
    END Delay;
    
          
 PROCEDURE ByteAnd (a, b : BYTE) : BYTE;
    BEGIN
       RETURN BYTE (SMALLSET (a) * SMALLSET (b));
    END ByteAnd;
    
          
 PROCEDURE Char (c : INTEGER) : CHAR;
 (* converts a number 0-95 into a printable character *)
    BEGIN
       RETURN (CHR (CARDINAL (ABS (c) + 32)));
    END Char;
    
    
 PROCEDURE UnChar (c : CHAR) : INTEGER;
 (* converts a character into its corresponding number *)
    BEGIN
       RETURN (ABS (INTEGER (ORD (c)) - 32));
    END UnChar;
 PROCEDURE FlushUART;
 (* ensure no characters left in UART holding registers *)
    BEGIN
       Delay (500);
       REPEAT
          status := GetChar (comport - COM_OFF, ch); 
       UNTIL status = NoCharacter;
    END FlushUART;
      
 PROCEDURE SendPacket (s : PacketType);
 (* Adds SOH and CheckSum to packet *)
 
    VAR
       i : CARDINAL;
       checksum : INTEGER;
       
    BEGIN
       Delay (10);   (* give host a chance to catch its breath *)
       FOR i := 1 TO yourNPAD DO
          status := SendChar (comport - COM_OFF, yourPADC, FALSE);
       END;
       status := SendChar (comport - COM_OFF, ASCII.soh, FALSE);
       i := 1;
       checksum := 0;
       WHILE s[i] # 0C DO
          INC (checksum, ORD (s[i]));
          status := SendChar (comport - COM_OFF, s[i], FALSE);
          INC (i);
       END;
       checksum := checksum + (INTEGER (BITSET (checksum) * {7, 6}) DIV 64);
       checksum := INTEGER (BITSET (checksum) * {5, 4, 3, 2, 1, 0});
       status := SendChar (comport - COM_OFF, Char (checksum), FALSE);
       IF yourEOL # 0C THEN
          status := SendChar (comport - COM_OFF, yourEOL, FALSE);
       END;
    END SendPacket;
    
    
 PROCEDURE ReceivePacket (VAR r : PacketType) : BOOLEAN;
 (* strips SOH and checksum -- returns status: TRUE = good packet     *)
 (* received;  FALSE = timed out waiting for packet or checksum error *)
 
    VAR
       sohtrys : INTEGER;
       i, len : INTEGER;
       ch : CHAR;
       checksum : INTEGER;
       mycheck, yourcheck : CHAR;
       
    BEGIN
       sohtrys := MAXsohtrys;
       REPEAT
          StartTime;
          REPEAT
             status := GetChar (comport - COM_OFF, ch);
          UNTIL (status = Success) OR (GetTime() > MAXtime);
          ch := CHAR (ByteAnd (ch, 177C));   (* mask off MSB *)
          (* skip over up to MAXsohtrys padding characters, *)
          (* but allow only MAXsohtrys/10 timeouts          *)
          IF status = Success THEN
             DEC (sohtrys);
          ELSE
             DEC (sohtrys, 10);
          END;
       UNTIL (ch = ASCII.soh) OR (sohtrys <= 0);
       
       IF ch = ASCII.soh THEN
          (* receive rest of packet *)
          StartTime;
          REPEAT
             status := GetChar (comport - COM_OFF, ch);
          UNTIL (status = Success) OR (GetTime() > MAXtime);
          ch := CHAR (ByteAnd (ch, 177C));
          len := UnChar (ch);
          r[1] := ch;
          checksum := ORD (ch);
          i := 2;   (* on to second character in packet -- after LEN *)
          REPEAT
             StartTime;
             REPEAT
                status := GetChar (comport - COM_OFF, ch);
             UNTIL (status = Success) OR (GetTime() > MAXtime);
             ch := CHAR (ByteAnd (ch, 177C));
             r[i] := ch;   INC (i);
             INC (checksum, (ORD (ch)));   
          UNTIL (i > len);
          (* get checksum character *)
          StartTime;
          REPEAT 
             status := GetChar (comport - COM_OFF, ch);
          UNTIL (status = Success) OR (GetTime() > MAXtime);
          ch := CHAR (ByteAnd (ch, 177C));
          yourcheck := ch;
          r[i] := 0C;
          checksum := checksum + 
                          (INTEGER (BITSET (checksum) * {7, 6}) DIV 64);
          checksum := INTEGER (BITSET (checksum) *  {5, 4, 3, 2, 1, 0});
          mycheck := Char (checksum);
          IF mycheck = yourcheck THEN   (* checksum OK *)
             RETURN TRUE;
          ELSE   (* ERROR!!! *)
             WinPostMsg (ChildFrameWindow, WM_DL, 
                         MPFROM2SHORT (DL_BadCS, 0), 0);
             RETURN FALSE;  
          END;
       ELSE
          WinPostMsg (ChildFrameWindow, WM_DL, 
                      MPFROM2SHORT (DL_NoSOH, 0), 0);
          RETURN FALSE;
       END;
    END ReceivePacket;
    
    
 PROCEDURE DoDLMsg (mp1, mp2 : MPARAM);
 (* Process DataLink Messages *)
    BEGIN
       CASE LOWORD (mp1) OF
          DL_BadCS:
             WriteString ("Bad Checksum");   WriteLn;
       |  DL_NoSOH:
             WriteString ("No SOH");   WriteLn;
       ELSE
          (* Do Nothing *)
       END;
    END DoDLMsg;

END DataLink.

[LISTING EIGHTEEN]

#include <os2.h> #include "pckermit.h"

ICON IDM_KERMIT pckermit.ico

MENU IDM_KERMIT

 BEGIN
    SUBMENU "~File", IDM_FILE
       BEGIN
          MENUITEM "~Directory...",     IDM_DIR
          MENUITEM "~Connect\t^C",          IDM_CONNECT
          MENUITEM "~Send...\t^S",          IDM_SEND
          MENUITEM "~Receive...\t^R",       IDM_REC
          MENUITEM SEPARATOR
          MENUITEM "E~xit\t^X",             IDM_QUIT
          MENUITEM "A~bout PCKermit...",  IDM_ABOUT
       END
       
    SUBMENU "~Options", IDM_OPTIONS
       BEGIN
          MENUITEM "~COM port...",      IDM_COMPORT
          MENUITEM "~Baud rate...",     IDM_BAUDRATE
          MENUITEM "~Data bits...",     IDM_DATABITS
          MENUITEM "~Stop bits...",     IDM_STOPBITS
          MENUITEM "~Parity bits...",   IDM_PARITY
       END
    SUBMENU "~Colors", IDM_COLORS
       BEGIN
          MENUITEM "~White Mono",       IDM_WHITE
          MENUITEM "~Green Mono",       IDM_GREEN
          MENUITEM "~Amber Mono",       IDM_AMBER
          MENUITEM "Full Color ~1",     IDM_C1
          MENUITEM "Full Color ~2",     IDM_C2
       END
       	 
    MENUITEM "F1=Help",    IDM_HELP, MIS_HELP | MIS_BUTTONSEPARATOR
 END

ACCELTABLE IDM_KERMIT

 BEGIN
    "^C", IDM_CONNECT
    "^S", IDM_SEND
    "^R", IDM_REC
    "^X", IDM_QUIT
 END
 

DLGTEMPLATE IDM_COMPORT LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_COMPORT, 129, 91, 143, 54, FS_NOBYTEALIGN | FS_DLGBORDER | 
              WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  BEGIN
      CONTROL "Select COM Port", IDM_COMPORT, 10, 9, 83, 38, 
              WC_STATIC, SS_GROUPBOX | WS_VISIBLE
      CONTROL "COM1", ID_COM1, 30, 25, 43, 10, WC_BUTTON, 
        BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
      CONTROL "COM2", ID_COM2, 30, 15, 39, 10, WC_BUTTON, 
        BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "OK", ID_OK, 101, 10, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
              BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_BAUDRATE LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_BAUDRATE, 131, 54, 142, 115, FS_NOBYTEALIGN | 
              FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  BEGIN
      CONTROL "Select Baud Rate", IDM_BAUDRATE, 8, 6, 85, 107, 
              WC_STATIC, SS_GROUPBOX | WS_VISIBLE
      CONTROL "110 Baud", ID_B110, 20, 90, 62, 10, WC_BUTTON, 
        BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
      CONTROL "150 Baud", ID_B150, 20, 80, 57, 10, WC_BUTTON, 
        BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "300 Baud", ID_B300, 20, 70, 58, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "600 Baud", ID_B600, 20, 60, 54, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "1200 Baud", ID_B1200, 20, 50, 59, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "2400 Baud", ID_B2400, 20, 40, 63, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "4800 Baud", ID_B4800, 20, 30, 62, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "9600 Baud", ID_B9600, 20, 20, 59, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "19,200 Baud", ID_B19K2, 20, 10, 69, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "OK", ID_OK, 100, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
	BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_DATABITS LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_DATABITS, 137, 80, 140, 56, FS_NOBYTEALIGN | 
              FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS
  BEGIN
      CONTROL "Select Data Bits", IDM_DATABITS, 8, 11, 80, 36, 
              WC_STATIC, SS_GROUPBOX | WS_VISIBLE
      CONTROL "7 Data Bits", ID_DATA7, 15, 25, 67, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
      CONTROL "8 Data Bits", ID_DATA8, 15, 15, 64, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "OK", ID_OK, 96, 12, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
	BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_STOPBITS LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_STOPBITS, 139, 92, 140, 43, FS_NOBYTEALIGN | 
              FS_DLGBORDER | WS_VISIBLE | WS_SAVEBITS
  BEGIN
      CONTROL "Select Stop Bits", IDM_STOPBITS, 9, 6, 80, 32, 
              WC_STATIC, SS_GROUPBOX | WS_VISIBLE
      CONTROL "1 Stop Bit", ID_STOP1, 20, 20, 57, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
      CONTROL "2 Stop Bits", ID_STOP2, 20, 10, 60, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "OK", ID_OK, 96, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
	BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_PARITY LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_PARITY, 138, 84, 134, 57, FS_NOBYTEALIGN | FS_DLGBORDER | 
              WS_VISIBLE | WS_SAVEBITS
  BEGIN
      CONTROL "Select Parity", IDM_PARITY, 12, 6, 64, 46, WC_STATIC, 
              SS_GROUPBOX | WS_VISIBLE
      CONTROL "Even", ID_EVEN, 25, 30, 40, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP | WS_VISIBLE
      CONTROL "Odd", ID_ODD, 25, 20, 38, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "None", ID_NONE, 25, 10, 40, 10, WC_BUTTON, 
	BS_AUTORADIOBUTTON | WS_TABSTOP | WS_VISIBLE
      CONTROL "OK", ID_OK, 88, 8, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
	BS_DEFAULT | WS_GROUP | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_ABOUT LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_ABOUT, 93, 74, 229, 88, FS_NOBYTEALIGN | FS_DLGBORDER | 
              WS_VISIBLE | WS_SAVEBITS
  BEGIN
      ICON IDM_KERMIT -1, 12, 64, 22, 16
      CONTROL "PCKermit for OS/2", 256, 67, 70, 82, 8, WC_STATIC, 
	SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Copyright (c) 1990 by Brian R. Anderson", 257, 27, 30, 172, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Microcomputer to Mainframe Communications", 259, 13, 50, 199, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "  OK  ", 258, 88, 10, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
              BS_DEFAULT | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_HELPMENU LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_HELPMENU, 83, 45, 224, 125, FS_NOBYTEALIGN | FS_DLGBORDER | 
              WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  BEGIN
      ICON IDM_KERMIT -1, 14, 99, 21, 16
      CONTROL "PCKermit Help Menu", 256, 64, 106, 91, 8, WC_STATIC, 
              SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "set communications Options .................. Alt, O", 
              258, 10, 80, 201, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Connect to Host ................................... Alt, F; C", 
              259, 10, 70, 204, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Directory .............................................. Alt, F; D", 
              260, 10, 60, 207, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Send a File .......................................... Alt, F; S", 
              261, 10, 50, 207, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Receive a File ...................................... Alt, F; R", 
              262, 10, 40, 209, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Exit ...................................................... Alt, F; X", 
              263, 10, 30, 205, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "OK", 264, 83, 9, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
	WS_TABSTOP | WS_VISIBLE | BS_DEFAULT
  END

END

DLGTEMPLATE IDM_TERMHELP LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_TERMHELP, 81, 20, 238, 177, FS_NOBYTEALIGN | 
              FS_DLGBORDER | WS_VISIBLE | WS_CLIPSIBLINGS | WS_SAVEBITS
  BEGIN
      CONTROL "^E = Echo mode", 256, 10, 160, 72, 8, WC_STATIC, 
              SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "^L = Local echo mode", 257, 10, 150, 97, 8, WC_STATIC, 
              SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "^T = Terminal Mode (no echo)", 258, 10, 140, 131, 8, WC_STATIC, 
              SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "^N = Newline mode (<cr> --> <cr><lf>)", 259, 10, 130, 165, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "^O = Newline mode OFF", 260, 10, 120, 109, 8, WC_STATIC, 
              SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Televideo TVI950 / IBM 7171 Terminal Emulation", 261, 10, 100, 217, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Sh-F1 - Sh-F12   =   PF1 - PF12", 262, 10, 90, 135, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Home                 =  Clear", 263, 10, 80, 119, 8, WC_STATIC, 
              SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "PgDn                  =  Page  Down (as used in PROFS)", 
              264, 10, 70, 228, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "PgUp                  =  Page Up (as used in PROFS)", 
              265, 10, 60, 227, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Insert                 =  Insert (Enter to Clear)", 266, 10, 40, 221, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Delete                =  Delete", 267, 10, 30, 199, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Control-G           =  Reset (rewrites the screen)", 268, 10, 20, 222, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "Cursor Keys (i.e., Up, Down, Left, Right) all work.", 
              269, 10, 10, 229, 8, WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | 
              WS_GROUP | WS_VISIBLE
      CONTROL "OK", 270, 193, 158, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
              BS_DEFAULT | WS_TABSTOP | WS_VISIBLE
      CONTROL "End                    =  End (as used in PROFS)", 271, 10, 50, 209, 8, 
              WC_STATIC, SS_TEXT | DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_SENDFN LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_SENDFN, 113, 90, 202, 60, FS_NOBYTEALIGN | FS_DLGBORDER | 
              WS_VISIBLE | WS_SAVEBITS
  BEGIN
      CONTROL "Send File", 256, 4, 4, 195, 24, WC_STATIC, SS_GROUPBOX | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Enter filename:", 257, 13, 11, 69, 8, WC_STATIC, SS_TEXT | 
              DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
ICON	IDM_KERMIT -1, 15, 38, 22, 16
      CONTROL "PCKermit for OS/2", 259, 59, 45, 82, 8, WC_STATIC, SS_TEXT | 
              DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "OK", 260, 154, 36, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
              WS_TABSTOP | WS_VISIBLE | BS_DEFAULT
      CONTROL "", ID_SENDFN, 89, 10, 98, 8, WC_ENTRYFIELD, ES_LEFT | 
	ES_MARGIN | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_DIRPATH LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_DIRPATH, 83, 95, 242, 46, FS_NOBYTEALIGN | FS_DLGBORDER | 
              WS_VISIBLE | WS_SAVEBITS
  BEGIN
      CONTROL "Directory", 256, 7, 5, 227, 24, WC_STATIC, SS_GROUPBOX | 
              WS_GROUP | WS_VISIBLE
      CONTROL "Path:", 257, 28, 11, 26, 8, WC_STATIC, SS_TEXT | DT_LEFT | 
              DT_TOP | WS_GROUP | WS_VISIBLE
      CONTROL "OK", 258, 185, 31, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
              WS_TABSTOP | WS_VISIBLE | BS_DEFAULT
      CONTROL "*.*", ID_DIRPATH, 57, 11, 166, 8, WC_ENTRYFIELD, ES_LEFT | 
	ES_AUTOSCROLL | ES_MARGIN | WS_TABSTOP | WS_VISIBLE
  END

END

DLGTEMPLATE IDM_DIREND LOADONCALL MOVEABLE DISCARDABLE BEGIN

  DIALOG "", IDM_DIREND, 149, 18, 101, 27, FS_NOBYTEALIGN | FS_DLGBORDER | 
              WS_VISIBLE | WS_SAVEBITS
  BEGIN
      CONTROL "Cancel", 256, 30, 2, 38, 12, WC_BUTTON, BS_PUSHBUTTON | 
              BS_DEFAULT | WS_TABSTOP | WS_VISIBLE
      CONTROL "Directory Complete", 257, 9, 16, 84, 8, WC_STATIC, SS_TEXT | 
              DT_LEFT | DT_TOP | WS_GROUP | WS_VISIBLE
  END

END

[LISTING NINETEEN]

HEAPSIZE 16384 STACKSIZE 16384 EXPORTS

  WindowProc
  ChildWindowProc
  

[FILE PCKERMIT]

OS2DEF.SYM: OS2DEF.DEF

  M2 OS2DEF.DEF/OUT:OS2DEF.SYM

OS2DEF.OBJ: OS2DEF.MOD OS2DEF.SYM

  M2 OS2DEF.MOD/OUT:OS2DEF.OBJ

PMWIN.SYM: PMWIN.DEF OS2DEF.SYM

  M2 PMWIN.DEF/OUT:PMWIN.SYM

PMWIN.OBJ: PMWIN.MOD OS2DEF.SYM PMWIN.SYM

  M2 PMWIN.MOD/OUT:PMWIN.OBJ

KH.SYM: KH.DEF

  M2 KH.DEF/OUT:KH.SYM

KH.OBJ: KH.MOD KH.SYM

  M2 KH.MOD/OUT:KH.OBJ

SHELL.SYM: SHELL.DEF PMWIN.SYM OS2DEF.SYM

  M2 SHELL.DEF/OUT:SHELL.SYM

TERM.SYM: TERM.DEF

  M2 TERM.DEF/OUT:TERM.SYM

PAD.SYM: PAD.DEF PMWIN.SYM

  M2 PAD.DEF/OUT:PAD.SYM

DATALINK.SYM: DATALINK.DEF PAD.SYM PMWIN.SYM

  M2 DATALINK.DEF/OUT:DATALINK.SYM

PMAVIO.SYM: PMAVIO.DEF PMWIN.SYM OS2DEF.SYM

  M2 PMAVIO.DEF/OUT:PMAVIO.SYM

PMAVIO.OBJ: PMAVIO.MOD PMAVIO.SYM

  M2 PMAVIO.MOD/OUT:PMAVIO.OBJ

PMGPI.SYM: PMGPI.DEF OS2DEF.SYM

  M2 PMGPI.DEF/OUT:PMGPI.SYM

PMGPI.OBJ: PMGPI.MOD OS2DEF.SYM PMGPI.SYM

  M2 PMGPI.MOD/OUT:PMGPI.OBJ

COMMPORT.SYM: COMMPORT.DEF

  M2 COMMPORT.DEF/OUT:COMMPORT.SYM

COMMPORT.OBJ: COMMPORT.MOD COMMPORT.SYM

  M2 COMMPORT.MOD/OUT:COMMPORT.OBJ

FILES.SYM: FILES.DEF

  M2 FILES.DEF/OUT:FILES.SYM

PCKERMIT.OBJ: PCKERMIT.MOD SHELL.SYM KH.SYM PMWIN.SYM OS2DEF.SYM

  M2 PCKERMIT.MOD/OUT:PCKERMIT.OBJ

SCREEN.SYM: SCREEN.DEF PMAVIO.SYM

  M2 SCREEN.DEF/OUT:SCREEN.SYM

SCREEN.OBJ: SCREEN.MOD SCREEN.SYM

  M2 SCREEN.MOD/OUT:SCREEN.OBJ

FILES.OBJ: FILES.MOD FILES.SYM

  M2 FILES.MOD/OUT:FILES.OBJ

SHELL.OBJ: SHELL.MOD COMMPORT.SYM KH.SYM PMGPI.SYM PMWIN.SYM PMAVIO.SYM - SCREEN.SYM DATALINK.SYM PAD.SYM TERM.SYM OS2DEF.SYM SHELL.SYM

  M2 SHELL.MOD/OUT:SHELL.OBJ

TERM.OBJ: TERM.MOD COMMPORT.SYM KH.SYM SHELL.SYM PMWIN.SYM SCREEN.SYM TERM.SYM

  M2 TERM.MOD/OUT:TERM.OBJ

PAD.OBJ: PAD.MOD DATALINK.SYM KH.SYM SHELL.SYM PMWIN.SYM COMMPORT.SYM - FILES.SYM OS2DEF.SYM SCREEN.SYM PAD.SYM

  M2 PAD.MOD/OUT:PAD.OBJ

DATALINK.OBJ: DATALINK.MOD KH.SYM PAD.SYM COMMPORT.SYM SHELL.SYM PMWIN.SYM - OS2DEF.SYM SCREEN.SYM DATALINK.SYM

  M2 DATALINK.MOD/OUT:DATALINK.OBJ

PCKERMIT.res: PCKERMIT.rc PCKERMIT.h PCKERMIT.ico

  rc -r PCKERMIT.rc

PCKERMIT.EXE: OS2DEF.OBJ PMWIN.OBJ KH.OBJ PMAVIO.OBJ PMGPI.OBJ COMMPORT.OBJ - PCKERMIT.OBJ SCREEN.OBJ FILES.OBJ SHELL.OBJ TERM.OBJ PAD.OBJ DATALINK.OBJ

  LINK @PCKERMIT.LNK
  rc PCKERMIT.res

PCKERMIT.exe: PCKERMIT.res

  rc PCKERMIT.res    

[FILE PCKERMIT.LNK]

KH.OBJ+ pckermit.OBJ+ SCREEN.OBJ+ COMMPORT.OBJ+ FILES.OBJ+ SHELL.OBJ+ TERM.OBJ+ PAD.OBJ+ DATALINK.OBJ pckermit pckermit PM+ M2LIB+ DOSCALLS+ OS2 pckermit.edf

[FILE PAD.MOD]

IMPLEMENTATION MODULE PAD; (* Packet Assembler/Disassembler for Kermit *)

 FROM SYSTEM IMPORT
    ADR;
 FROM Storage IMPORT
    ALLOCATE, DEALLOCATE;
    
 FROM Screen IMPORT
    ClrScr, WriteString, WriteInt, WriteHex, WriteLn;
 FROM OS2DEF IMPORT
    HIWORD, LOWORD;
          
 FROM DosCalls IMPORT
    ExitType, DosExit;
    
 FROM Strings IMPORT
    Length, Assign;
    
 FROM FileSystem IMPORT
    File;
    
 FROM Directories IMPORT
    FileAttributes, AttributeSet, DirectoryEntry, FindFirst, FindNext;
    
 FROM Files IMPORT
    Status, FileType, Open, Create, CloseFile, Get, Put, DoWrite;
 FROM PMWIN IMPORT
    MPARAM, MPFROM2SHORT, WinPostMsg;
    
 FROM Shell IMPORT
    ChildFrameWindow, comport;
    
 FROM KH IMPORT
    COM_OFF;
          
 FROM DataLink IMPORT
    FlushUART, SendPacket, ReceivePacket;
 FROM SYSTEM IMPORT
    BYTE;
                      
 IMPORT ASCII;
 
 CONST
    myMAXL = 94;
    myTIME = 10;
    myNPAD = 0;
    myPADC = 0C;
    myEOL  = 0C;
    myQCTL = '#';
    myQBIN = '&';
    myCHKT = '1';     (* one character checksum *)
    MAXtrys = 5;
    (* From DEFINITION MODULE:
    PAD_Quit = 0;  *)
    PAD_SendPacket = 1;
    PAD_ResendPacket = 2;
    PAD_NoSuchFile = 3;
    PAD_ExcessiveErrors = 4;
    PAD_ProbClSrcFile = 5;
    PAD_ReceivedPacket = 6;
    PAD_Filename = 7;
    PAD_RequestRepeat = 8;
    PAD_DuplicatePacket = 9;
    PAD_UnableToOpen = 10;
    PAD_ProbClDestFile = 11;
    PAD_ErrWrtFile = 12;
    PAD_Msg = 13;
    
    
 TYPE
    (* From Definition Module:
    PacketType = ARRAY [1..100] OF CHAR;
    *)
    SMALLSET = SET OF [0..7];   (* a byte *)
    
                      
 VAR
    yourMAXL : INTEGER;   (* maximum packet length -- up to 94 *)
    yourTIME : INTEGER;   (* time out -- seconds *) 
    (* From Definition Module
    yourNPAD : INTEGER;   (* number of padding characters *)
    yourPADC : CHAR;   (* padding characters *)
    yourEOL  : CHAR;   (* End Of Line -- terminator *)
    *)
    yourQCTL : CHAR;   (* character for quoting controls '#' *)
    yourQBIN : CHAR;   (* character for quoting binary '&' *)
    yourCHKT : CHAR;   (* check type -- 1 = checksum, etc. *)
    sF, rF : File;   (* files being sent/received *)
    InputFileOpen : BOOLEAN;
    rFname : ARRAY [0..20] OF CHAR;
    sP, rP : PacketType;   (* packets sent/received *)
    sSeq, rSeq : INTEGER;   (* sequence numbers *)
    PktNbr : INTEGER;   (* actual packet number -- no repeats up to 32,000 *)
    ErrorMsg : ARRAY [0..40] OF CHAR;
    
 PROCEDURE PtrToStr (mp : MPARAM; VAR s : ARRAY OF CHAR);
 (* Convert a pointer to a string into a string *)
    
    TYPE
       PC = POINTER TO CHAR;
    
    VAR
       p : PC;
       i : CARDINAL;
       c : CHAR;
       
    BEGIN
       i := 0;
       REPEAT
          p := PC (mp);
          c := p^;
          s[i] := c;
          INC (i);
          INC (mp);
       UNTIL c = 0C;
    END PtrToStr;
 PROCEDURE DoPADMsg (mp1, mp2 : MPARAM);
 (* Output messages for Packet Assembler/Disassembler *)
          
    VAR
       Message : ARRAY [0..40] OF CHAR;
       
    BEGIN
       CASE LOWORD (mp1) OF
          PAD_SendPacket:
             WriteString ("Sent Packet #");   
             WriteInt (LOWORD (mp2), 5);
             WriteString ("  (ID: ");   WriteHex (HIWORD (mp2), 2);   
             WriteString ("h)");
       |  PAD_ResendPacket:
             WriteString ("ERROR -- Resending:");   WriteLn;
             WriteString ("     Packet #");   
             WriteInt (LOWORD (mp2), 5);
             WriteString ("  (ID: ");   WriteHex (HIWORD (mp2), 2);   
             WriteString ("h)");
       |  PAD_NoSuchFile:
             WriteString ("No such file: ");   
             PtrToStr (mp2, Message);   WriteString (Message);
       |  PAD_ExcessiveErrors:
             WriteString ("Excessive errors ..."); 
       |  PAD_ProbClSrcFile:
             WriteString ("Problem closing source file...");  
       |  PAD_ReceivedPacket:
             WriteString ("Received Packet #");   
             WriteInt (LOWORD (mp2), 5);
             WriteString ("  (ID: ");   WriteHex (HIWORD (mp2), 2);   
             WriteString ("h)");
       |  PAD_Filename:
             WriteString ("Filename = ");   
             PtrToStr (mp2, Message);   WriteString (Message);
       |  PAD_RequestRepeat:
             WriteString ("ERROR -- Requesting Repeat:");   WriteLn;
             WriteString ("         Packet #");   
             WriteInt (LOWORD (mp2), 5);
             WriteString ("  (ID: ");   WriteHex (HIWORD (mp2), 2);   
             WriteString ("h)");
       |  PAD_DuplicatePacket:
             WriteString ("Discarding Duplicate:");   WriteLn;
             WriteString ("         Packet #");   
             WriteString ("  (ID: ");   WriteHex (HIWORD (mp2), 2);   
             WriteString ("h)");
       |  PAD_UnableToOpen:
             WriteString ("Unable to open file: ");
             PtrToStr (mp2, Message);   WriteString (Message);
       |  PAD_ProbClDestFile:
             WriteString ("Error closing file: ");   
             PtrToStr (mp2, Message);   WriteString (Message);
       |  PAD_ErrWrtFile:
             WriteString ("Error writing to file: ");   
             PtrToStr (mp2, Message);   WriteString (Message);
       |  PAD_Msg:
             PtrToStr (mp2, Message);   WriteString (Message);
       ELSE
          (* Do Nothing *)
       END;
       WriteLn; 
    END DoPADMsg;
    
 PROCEDURE CloseInput;
 (* Close the input file, if it exists.  Reset Input File Open flag *)
    BEGIN
       IF InputFileOpen THEN
          IF CloseFile (sF, Input) = Done THEN
             InputFileOpen := FALSE;
          ELSE
             WinPostMsg (ChildFrameWindow, WM_PAD,
                MPFROM2SHORT (PAD_ProbClSrcFile, 0),
                ADR (sFname));
          END;
       END;
    END CloseInput;
    
    
 PROCEDURE NormalQuit;
 (* Exit from Thread, Post message to Window *)
    BEGIN
       WinPostMsg (ChildFrameWindow, WM_PAD, 
          MPFROM2SHORT (PAD_Quit, 0), 0);
       DosExit (EXIT_THREAD, 0);
    END NormalQuit;
    
    
 PROCEDURE ErrorQuit;
 (* Exit from Thread, Post message to Window *)
    BEGIN
       WinPostMsg (ChildFrameWindow, WM_PAD, 
          MPFROM2SHORT (PAD_Error, 0), 0);
       DosExit (EXIT_THREAD, 0);
    END ErrorQuit;
    
    
 PROCEDURE ByteXor (a, b : BYTE) : BYTE;
    BEGIN
       RETURN BYTE (SMALLSET (a) / SMALLSET (b));
    END ByteXor;
    
    
 PROCEDURE Char (c : INTEGER) : CHAR;
 (* converts a number 0-94 into a printable character *)
    BEGIN
       RETURN (CHR (CARDINAL (ABS (c) + 32)));
    END Char;
    
    
 PROCEDURE UnChar (c : CHAR) : INTEGER;
 (* converts a character into its corresponding number *)
    BEGIN
       RETURN (ABS (INTEGER (ORD (c)) - 32));
    END UnChar;
    
 PROCEDURE TellError (Seq : INTEGER);
 (* Send error packet *)
    BEGIN
       sP[1] := Char (15);
       sP[2] := Char (Seq);
       sP[3] := 'E';   (* E-type packet *)
       sP[4] := 'R';   (* error message starts *)
       sP[5] := 'e';
       sP[6] := 'm';
       sP[7] := 'o';
       sP[8] := 't';
       sP[9] := 'e';
       sP[10] := ' ';
       sP[11] := 'A';
       sP[12] := 'b';
       sP[13] := 'o';
       sP[14] := 'r';
       sP[15] := 't';
       sP[16] := 0C;
       SendPacket (sP);
    END TellError;
    
    
 PROCEDURE ShowError (p : PacketType);
 (* Output contents of error packet to the screen *)
 
    VAR
       i : INTEGER;
       
    BEGIN
       FOR i := 4 TO UnChar (p[1]) DO
          ErrorMsg[i - 4] := p[i];
       END;
       ErrorMsg[i - 4] := 0C;
       WinPostMsg (ChildFrameWindow, WM_PAD, 
          MPFROM2SHORT (PAD_Msg, 0), ADR (ErrorMsg));
    END ShowError;
    
    
 PROCEDURE youInit (type : CHAR);   
 (* I initialization YOU for Send and Receive *)      
    BEGIN
       sP[1] := Char (11);   (* Length *)
       sP[2] := Char (0);   (* Sequence *)
       sP[3] := type;
       sP[4] := Char (myMAXL);
       sP[5] := Char (myTIME);
       sP[6] := Char (myNPAD);
       sP[7] := CHAR (ByteXor (myPADC, 100C));
       sP[8] := Char (ORD (myEOL));
       sP[9] := myQCTL;
       sP[10] := myQBIN;
       sP[11] := myCHKT;
       sP[12] := 0C;   (* terminator *)
       SendPacket (sP);
    END youInit;
    
 PROCEDURE myInit;
 (* YOU initialize ME for Send and Receive *)
 
    VAR
       len : INTEGER;
       
    BEGIN
       len := UnChar (rP[1]);
       IF len >= 4 THEN
          yourMAXL := UnChar (rP[4]);
       ELSE
          yourMAXL := 94;
       END;
       IF len >= 5 THEN
          yourTIME := UnChar (rP[5]);
       ELSE
          yourTIME := 10;
       END;
       IF len >= 6 THEN
          yourNPAD := UnChar (rP[6]);
       ELSE
          yourNPAD := 0;
       END;
       IF len >= 7 THEN
          yourPADC := CHAR (ByteXor (rP[7], 100C));
       ELSE
          yourPADC := 0C;
       END;
       IF len >= 8 THEN
          yourEOL := CHR (UnChar (rP[8]));
       ELSE
          yourEOL := 0C;
       END;
       IF len >= 9 THEN
          yourQCTL := rP[9];
       ELSE
          yourQCTL := 0C;
       END;
       IF len >= 10 THEN
          yourQBIN := rP[10];
       ELSE
          yourQBIN := 0C;
       END;
       IF len >= 11 THEN
          yourCHKT := rP[11];
          IF yourCHKT # myCHKT THEN
             yourCHKT := '1';
          END;
       ELSE
          yourCHKT := '1';
       END;
    END myInit;
    
          
 PROCEDURE SendInit;
    BEGIN
       youInit ('S');
    END SendInit;
    
    
 PROCEDURE SendFileName;
 
    VAR
       i, j : INTEGER;
       
    BEGIN
       (* send file name *)
       i := 4;   j := 0;
       WHILE sFname[j] # 0C DO
          sP[i] := sFname[j];
          INC (i);   INC (j);
       END;
       sP[1] := Char (j + 3);
       sP[2] := Char (sSeq);
       sP[3] := 'F';   (* filename packet *)
       sP[i] := 0C;
       SendPacket (sP);
    END SendFileName;
    
    
 PROCEDURE SendEOF;
    BEGIN
       sP[1] := Char (3);
       sP[2] := Char (sSeq);
       sP[3] := 'Z';   (* end of file *)
       sP[4] := 0C;
       SendPacket (sP);
    END SendEOF;
    
    
 PROCEDURE SendEOT;
    BEGIN
       sP[1] := Char (3);
       sP[2] := Char (sSeq);
       sP[3] := 'B';   (* break -- end of transmit *)
       sP[4] := 0C;
       SendPacket (sP);
    END SendEOT;
    
    
 PROCEDURE GetAck() : BOOLEAN;
 (* Look for acknowledgement -- retry on timeouts or NAKs *)
 
    VAR
       Type : CHAR;
       Seq : INTEGER;
       retrys : INTEGER;
       AckOK : BOOLEAN;
        
    BEGIN
       WinPostMsg (ChildFrameWindow, WM_PAD, 
          MPFROM2SHORT (PAD_SendPacket, 0),
          MPFROM2SHORT (PktNbr, sSeq));
    
       retrys := MAXtrys;
       LOOP
          IF Aborted THEN
             TellError (sSeq);
             CloseInput;
             ErrorQuit;
          END;
          IF ReceivePacket (rP) THEN
             Seq := UnChar (rP[2]);
             Type := rP[3];
             IF (Seq = sSeq) AND (Type = 'Y') THEN
                AckOK := TRUE;
             ELSIF (Seq = (sSeq + 1) MOD 64) AND (Type = 'N') THEN
                AckOK := TRUE;   (* NAK for (n + 1) taken as ACK for n *)
             ELSIF Type = 'E' THEN
                ShowError (rP);
                AckOK := FALSE;
                retrys := 0;
             ELSE
                AckOK := FALSE;
             END;
          ELSE
             AckOK := FALSE;
          END;
          IF AckOK OR (retrys = 0) THEN
             EXIT;
          ELSE
             WinPostMsg (ChildFrameWindow, WM_PAD,
                MPFROM2SHORT (PAD_ResendPacket, 0),
                MPFROM2SHORT (PktNbr, sSeq));
             
             DEC (retrys);
             FlushUART;
             SendPacket (sP);
          END;
       END;
    
       IF AckOK THEN
          INC (PktNbr);
          sSeq := (sSeq + 1) MOD 64;
          RETURN TRUE;
       ELSE
          RETURN FALSE;
       END;
    END GetAck;
       
 PROCEDURE GetInitAck() : BOOLEAN;
 (* configuration for remote station *)
    BEGIN
       IF GetAck() THEN
          myInit;
          RETURN TRUE;
       ELSE 
          RETURN FALSE;
       END;
    END GetInitAck;
    
    
 PROCEDURE Send;
 (* Send one or more files: sFname may be ambiguous *)
 
    TYPE
       LP = POINTER TO LIST;   (* list of filenames *)
       LIST = RECORD
                 fn : ARRAY [0..20] OF CHAR;
                 next : LP;
              END;
              
    VAR
       gotFN : BOOLEAN;
       attr : AttributeSet;
       ent : DirectoryEntry;
       front, back, t : LP;   (* add at back of queue, remove from front *)
       
    BEGIN
       Aborted := FALSE;
       InputFileOpen := FALSE;
       
       front := NIL;   back := NIL;
       attr := AttributeSet {};   (* normal files only *)
       IF Length (sFname) = 0 THEN
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_Msg, 0), 
             ADR ("No file specified..."));
          ErrorQuit;
       ELSE
          gotFN := FindFirst (sFname, attr, ent);
          WHILE gotFN DO   (* build up a list of file names *)
             ALLOCATE (t, SIZE (LIST));
             Assign (ent.name, t^.fn);
             t^.next := NIL;
             IF front = NIL THEN
                front := t;   (* start from empty queue *)
             ELSE
                back^.next := t;   (* and to back of queue *)
             END;
             back := t;
             gotFN := FindNext (ent);
          END;
       END;
    
       IF front = NIL THEN   
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_NoSuchFile, 0),
             ADR (sFname));
          ErrorQuit;
       ELSE
          sSeq := 0;   PktNbr := 0;
          FlushUART;
          SendInit;   (* my configuration information *)
          IF NOT GetInitAck() THEN     (* get your configuration information *)
             WinPostMsg (ChildFrameWindow, WM_PAD,
                MPFROM2SHORT (PAD_ExcessiveErrors, 0),
                MPFROM2SHORT (0, 0));
             ErrorQuit;
          END;
           
          WHILE front # NIL DO   (* send the files *)
             Assign (front^.fn, sFname);
             PktNbr := 1;
             Send1;
             t := front;
             front := front^.next;
             DEALLOCATE (t, SIZE (LIST));
          END;
       END;
    
       SendEOT;
       IF NOT GetAck() THEN
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_ExcessiveErrors, 0),
             MPFROM2SHORT (0, 0));
          CloseInput;
          ErrorQuit;
       END;
       NormalQuit;
    END Send;
    
          
 PROCEDURE Send1;
 (* Send one file: sFname *)
 
    VAR
       ch : CHAR;
       i : INTEGER;
       
    BEGIN
       IF Open (sF, sFname) = Done THEN
          InputFileOpen := TRUE;
       ELSE;
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_NoSuchFile, 0),
             ADR (sFname));
          ErrorQuit;
       END;
       
       WinPostMsg (ChildFrameWindow, WM_PAD,
          MPFROM2SHORT (PAD_Filename, 0), 
          ADR (sFname));
       WinPostMsg (ChildFrameWindow, WM_PAD,
          MPFROM2SHORT (PAD_Msg, 0), 
          ADR ("(<ESC> to abort file transfer.)"));
          
       SendFileName;        
       IF NOT GetAck() THEN
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_ExcessiveErrors, 0),
             MPFROM2SHORT (0, 0));
          CloseInput;
          ErrorQuit;
       END;
       
       (* send file *)
       i := 4;
       LOOP
          IF Get (sF, ch) = EOF THEN   (* send current packet & terminate *)
             sP[1] := Char (i - 1);
             sP[2] := Char (sSeq);
             sP[3] := 'D';   (* data packet *)
             sP[i] := 0C;   (* indicate end of packet *)
             SendPacket (sP);
             IF NOT GetAck() THEN
                WinPostMsg (ChildFrameWindow, WM_PAD,
                   MPFROM2SHORT (PAD_ExcessiveErrors, 0),
                   MPFROM2SHORT (0, 0));
                CloseInput;
                ErrorQuit;
             END;
             SendEOF;
             IF NOT GetAck() THEN
                WinPostMsg (ChildFrameWindow, WM_PAD,
                   MPFROM2SHORT (PAD_ExcessiveErrors, 0),
                   MPFROM2SHORT (0, 0));
                CloseInput;
                ErrorQuit;
             END;
             EXIT;
          END;
                
          IF i >= (yourMAXL - 4) THEN   (* send current packet *)
             sP[1] := Char (i - 1);
             sP[2] := Char (sSeq);
             sP[3] := 'D';
             sP[i] := 0C;
             SendPacket (sP);
             IF NOT GetAck() THEN
                WinPostMsg (ChildFrameWindow, WM_PAD,
                   MPFROM2SHORT (PAD_ExcessiveErrors, 0),
                   MPFROM2SHORT (0, 0));
                CloseInput;
                ErrorQuit;
             END;
             i := 4;
          END;
          (* add character to current packet -- update count *)
          IF ch > 177C THEN   (* must be quoted (QBIN) and altered *)
             (* toggle bit 7 to turn it off *)
             ch := CHAR (ByteXor (ch, 200C));
             sP[i] := myQBIN;   INC (i);
          END;
          IF (ch < 40C) OR (ch = 177C) THEN   (* quote (QCTL) and alter *)
             (* toggle bit 6 to turn it on *)
             ch := CHAR (ByteXor (ch, 100C));
             sP[i] := myQCTL;   INC (i);
          END;
          IF (ch = myQCTL) OR (ch = myQBIN) THEN   (* must send it quoted *)
             sP[i] := myQCTL;   INC (i);
          END;
          sP[i] := ch;   INC (i);
       END;   (* loop *)
       
       CloseInput;
    END Send1;
    
 PROCEDURE ReceiveInit() : BOOLEAN;
 (* receive my initialization information from you *)
 
    VAR
       RecOK : BOOLEAN;
       trys : INTEGER;
        
    BEGIN
       trys := 1;
       LOOP
          IF Aborted THEN
             TellError (rSeq);
             ErrorQuit;
          END;
          RecOK := ReceivePacket (rP) AND (rP[3] = 'S');
          IF RecOK OR (trys = MAXtrys) THEN
             EXIT;
          ELSE
             INC (trys);
             SendNak;
          END;
       END;
       
       IF RecOK THEN
          myInit;
          RETURN TRUE;
       ELSE
          RETURN FALSE;
       END;   
    END ReceiveInit;
    
    
 PROCEDURE SendInitAck;
 (* acknowledge your initialization of ME and send mine for YOU *)
    BEGIN
       WinPostMsg (ChildFrameWindow, WM_PAD,
          MPFROM2SHORT (PAD_ReceivedPacket, 0),
          MPFROM2SHORT (PktNbr, rSeq));
       INC (PktNbr);
       rSeq := (rSeq + 1) MOD 64;
       youInit ('Y');
    END SendInitAck;
    
    
 PROCEDURE ValidFileChar (VAR ch : CHAR) : BOOLEAN;
 (* checks if character is one of 'A'..'Z', '0'..'9', makes upper case *)
    BEGIN
       ch := CAP (ch);
       RETURN ((ch >= 'A') AND (ch <= 'Z')) OR ((ch >= '0') AND (ch <= '9'));
    END ValidFileChar;
 TYPE
    HeaderType = (name, eot, fail);
    
 PROCEDURE ReceiveHeader() : HeaderType;
 (* receive the filename -- alter for local conditions, if necessary *)
 
    VAR
       i, j, k : INTEGER;
       RecOK : BOOLEAN;
       trys : INTEGER;
       
    BEGIN
       trys := 1;
       LOOP
          IF Aborted THEN
             TellError (rSeq);
             ErrorQuit;
          END;
          RecOK := ReceivePacket (rP) AND ((rP[3] = 'F') OR (rP[3] = 'B'));
          IF trys = MAXtrys THEN
             RETURN fail;
          ELSIF RecOK AND (rP[3] = 'F') THEN
             i := 4;   (* data starts here *)
             j := 0;   (* beginning of filename string *)
             WHILE (ValidFileChar (rP[i])) AND (j < 8) DO
                rFname[j] := rP[i];
                INC (i);   INC (j);
             END;
             REPEAT
                INC (i);
             UNTIL (ValidFileChar (rP[i])) OR (rP[i] = 0C);
             rFname[j] := '.';   INC (j);
             k := 0;
             WHILE (ValidFileChar (rP[i])) AND (k < 3) DO
                rFname[j + k] := rP[i];
                INC (i);   INC (k);
             END;
             rFname[j + k] := 0C;  
             WinPostMsg (ChildFrameWindow, WM_PAD,
                MPFROM2SHORT (PAD_Filename, 0),
                ADR (rFname));
             RETURN name;
          ELSIF RecOK AND (rP[3] = 'B') THEN
             RETURN eot;
          ELSE
             INC (trys);
             SendNak;
          END;
       END;
    END ReceiveHeader;
    
    
 PROCEDURE SendNak;
    BEGIN
       WinPostMsg (ChildFrameWindow, WM_PAD,
          MPFROM2SHORT (PAD_RequestRepeat, 0),
          MPFROM2SHORT (PktNbr, rSeq));
       FlushUART;
       sP[1] := Char (3);   (* LEN *)
       sP[2] := Char (rSeq); 
       sP[3] := 'N';   (* negative acknowledgement *)
       sP[4] := 0C;
       SendPacket (sP);
    END SendNak;
    
    
 PROCEDURE SendAck (Seq : INTEGER);
    BEGIN
       IF Seq # rSeq THEN
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_DuplicatePacket, 0),
             MPFROM2SHORT (0, rSeq));
       ELSE
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_ReceivedPacket, 0),
             MPFROM2SHORT (PktNbr, rSeq));
          rSeq := (rSeq + 1) MOD 64;
          INC (PktNbr);
       END;
       
       sP[1] := Char (3);
       sP[2] := Char (Seq);
       sP[3] := 'Y';   (* acknowledgement *)
       sP[4] := 0C;
       SendPacket (sP);
    END SendAck;
    
    
 PROCEDURE Receive;
 (* Receives a file  (or files) *)
 
    VAR
       ch, Type : CHAR;
       Seq : INTEGER;
       i : INTEGER;
       EOF, EOT, QBIN : BOOLEAN;
       trys : INTEGER;
                
    BEGIN
       Aborted := FALSE;
       
       WinPostMsg (ChildFrameWindow, WM_PAD,
          MPFROM2SHORT (PAD_Msg, 0), 
          ADR ("Ready to receive file(s)..."));
       WinPostMsg (ChildFrameWindow, WM_PAD,
          MPFROM2SHORT (PAD_Msg, 0),
          ADR ("(<ESC> to abort file transfer.)"));
       FlushUART;
       rSeq := 0;   PktNbr := 0;  
       IF NOT ReceiveInit() THEN   (* your configuration information *)
          WinPostMsg (ChildFrameWindow, WM_PAD,
             MPFROM2SHORT (PAD_ExcessiveErrors, 0),
             MPFROM2SHORT (0, 0));
          ErrorQuit;
       END;
       SendInitAck;       (* send my configuration information *)
       EOT := FALSE;
       WHILE NOT EOT DO
          CASE ReceiveHeader() OF
             eot  : EOT := TRUE;   EOF := TRUE;
          |  name : IF Create (rF, rFname) # Done THEN
                       WinPostMsg (ChildFrameWindow, WM_PAD,
                             MPFROM2SHORT (PAD_UnableToOpen, 0),
                             ADR (rFname));
                       ErrorQuit;
                    ELSE
                       PktNbr := 1;
                       EOF := FALSE;
                    END;
          |  fail : WinPostMsg (ChildFrameWindow, WM_PAD,
                          MPFROM2SHORT (PAD_ExcessiveErrors, 0),
                          MPFROM2SHORT (0, 0));
                    ErrorQuit;
          END;
          SendAck (rSeq);   (* acknowledge for name or eot *)
          trys := 1;   (* initialize *)
          WHILE NOT EOF DO
             IF Aborted THEN
                TellError (rSeq);
                ErrorQuit;
             END;
             IF ReceivePacket (rP) THEN
                Seq := UnChar (rP[2]);
                Type := rP[3];
                IF Type = 'Z' THEN
                   EOF := TRUE;
                   IF CloseFile (rF, Output) = Done THEN
                      (* normal file termination *)
                   ELSE
                      WinPostMsg (ChildFrameWindow, WM_PAD,
                         MPFROM2SHORT (PAD_ProbClDestFile, 0),
                         ADR (rFname));
                      ErrorQuit;
                   END;
                   trys := 1;   (* good packet -- reset *)
                   SendAck (rSeq);
                ELSIF Type = 'E' THEN
                   ShowError (rP);
                   ErrorQuit;
                ELSIF (Type = 'D') AND ((Seq + 1) MOD 64 = rSeq) THEN
                (* discard duplicate packet, and Ack anyway *)
                   trys := 1;
                   SendAck (Seq); 
                ELSIF (Type = 'D') AND (Seq = rSeq) THEN
                   (* put packet into file buffer *)
                   i := 4;   (* first data in packet *)
                   WHILE rP[i] # 0C DO
                      ch := rP[i];   INC (i);
                      IF ch = yourQBIN THEN
                         ch := rP[i];   INC (i);
                         QBIN := TRUE;
                      ELSE
                         QBIN := FALSE;
                      END;
                      IF ch = yourQCTL THEN                  
                         ch := rP[i];   INC (i);
                         IF (ch # yourQCTL) AND (ch # yourQBIN) THEN
                            ch := CHAR (ByteXor (ch, 100C));
                         END;
                      END;
                      IF QBIN THEN
                         ch := CHAR (ByteXor (ch, 200C));
                      END;
                      Put (ch);
                   END;
                
                   (* write file buffer to disk *)
                   IF DoWrite (rF) # Done THEN
                      WinPostMsg (ChildFrameWindow, WM_PAD,
                         MPFROM2SHORT (PAD_ErrWrtFile, 0),
                         ADR (rFname));
                      ErrorQuit;
                   END;
                   trys := 1;
                   SendAck (rSeq);
                ELSE
                   INC (trys);
                   IF trys = MAXtrys THEN
                      WinPostMsg (ChildFrameWindow, WM_PAD,
                         MPFROM2SHORT (PAD_ExcessiveErrors, 0),
                         MPFROM2SHORT (0, 0));
                      ErrorQuit;
                   ELSE
                      SendNak;
                   END;
                END;
             ELSE
                INC (trys);
                IF trys = MAXtrys THEN
                   WinPostMsg (ChildFrameWindow, WM_PAD,
                      MPFROM2SHORT (PAD_ExcessiveErrors, 0),
                      MPFROM2SHORT (0, 0));
                   ErrorQuit;
                ELSE
                   SendNak;
                END;
             END;
          END;
       END;
       NormalQuit;
    END Receive;
    
    

BEGIN (* module initialization *)

 yourEOL := ASCII.cr;
 yourNPAD := 0;
 yourPADC := 0C;

END PAD.



/data/webs/external/dokuwiki/data/pages/archive/computers/anderson.txt · Last modified: 2001/11/08 10:19 by 127.0.0.1

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki