Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Design

Implementing Multiple Computer Communications Links


OCT89: IMPLEMENTING MULTIPLE COMPUTER COMMUNICATIONS LINKS

IMPLEMENTING MULTIPLE COMPUTER COMMUNICATIONS LINKS

Mark Servello

Mark Servello is a software engineer for American Management Systems and can be reached at 1455 Frazee Road, Suite 315, San Diego, CA 92108-4304.


It's no secret that PCs have put a tremendous amount of computing power directly into the hands of end users, and that end users are becoming increasingly sophisticated in customizing the machines for their particular functions. Consequently, everyone is looking for methods to maximize communication between individual workstations, each of which is tailored on an individual basis to obtain the largest productivity improvement for its user. This communication is necessary in order to improve the effectiveness of the business unit, by allowing transfer and sharing of information within and between groups.

This article discusses the general concept of PC-to-minicomputer communication. As an example, I describe how a network can be constructed using common telephone equipment and a surplus minicomputer, which becomes a network server, and illustrate methods for getting computers to communicate under programmed, rather than terminal, control.

The Environment

In our office, we tend to work in small groups on individual projects. The members of each group share information frequently, and often one or more groups are in the document print/revision cycle. We've had a relatively new DEC PDP-11/73 machine sitting around in the office for some time (a circa 1985 leftover from an expired contract), and we decided that its multiuser capabilities would enable us to use it as a network controller. As such, we could use it for sharing information on an as-needed basis and for providing spooled access to the office's printing resources. Because laser and high-speed draft printers are not small expenditures, it is important to use them effectively, which is exactly what a multiuser computing system can do with its print spooling software.

When we moved into our current offices, we installed a second jack as a data connection at every phone outlet wall panel and several more in common usage areas. The extra line is an RJ-11 type modular jack wired to handle all eight modem control signals.

Each data jack at a telephone wall panel in the offices leads back to a single patch block in the computer room (see Figure 1). These jacks are plugged into terminal interfaces on the PDP-11, which also has all shared devices such as printers and modems connected through the terminal lines. The shared devices under PDP-11's control use all eight RS-232 modem control signals for maximum reliability at high speed. The connecting cables between the wall jacks and office PCs (and between the corresponding patch block location and the PDP-11) are standard six-conductor, with an RJ-11-to-DB25/DB9 connector at the PC's serial port. For these lines, the PDP-11 provides TD, RD, DTR, and Signal Ground, which are wired/jumpered by the connector to allow serial communication over the port. All wiring from the connector to the PDP-11 is straight-through, and each connector is labeled with the type of device it is configured for (this lesson was learned the hard way).

The Software

The hardware was, of course, in place much sooner than the software. Communication between the individual PC workstations and the PDP-11 was established relatively quickly, using a variety of communications packages configured on the PC as Digital Equipment Corporation VT100 terminal emulators. Fortunately, our phone company was careful to install all jack lines uniformly throughout the offices. Even so, some serial devices are finicky, so an RS-232 Breakout Box came in handy. (I know, programmers don't change light bulbs because it's a hardware problem, but what happens when you're programming the hardware?)

We were able to use this type of communication with the PDP-11 to accomplish simple printing to high-speed line and dot matrix printers by first using Kermit or XModem to transfer a print file to the PDP-11, then spooling the file to the device. We also used the Kermit program on the PDP-11 to establish communications between the PDP-11 and all of the shared modems and printers.

What we really needed, however, was a method by which an individual PC could behave as if the shared device (a printer for instance) was directly connected to it, so that users could run applications programs (like word processors) and print directly to the serial port. On the other end, the PDP-11 had to be capable of accepting the output to the serial port and placing it into a print file until the PC application program was finished. Once the file was completed, it had to be spooled to the appropriate printer.

Because of the variations in possible PC connections and the way the PDP-11's operating system (DEC RSTS/E) works, I decided that two programs would have to be written. The first program, running on the PC, provides configuration control and establishes sessions with the PDP-11. The second program, running on the PDP-11, performs communication functions with the PC program by providing status, executing commands, and catching print output for spooling. The DEC-provided minicomputer spooling program then routes printout files to the proper printer, as specified by the user.

I decided to use a bottom-up approach and began writing the PC program's serial port interface using Turbo Pascal 5.0.

For the actual serial interrupt service routines, I used Ray Duncan's marvelous book Advanced MS-DOS Programming (Microsoft Press, 1988) to familiarize myself with the basic PC methodology (I'm a minicomputer programmer), then employed a Turbo Pascal translation tailored for my needs. Flow control from the PDP-11 to the shared devices is by the full eight-line RS-232 modem signals. On the PC connection, however, only four lines (TD, RD, Signal Ground, and DTR) are normally used, because the PDP-11 uses DTR to determine whether a terminal is active, and logs out any process when its terminal's DTR goes low. Flow control to/from the PCs is XON/XOFF in the data path. The ISR routines in Listing One show the XOFF sent when the PC's receive queue is within one second of being full (as determined by the data rate at the current transmission speed). The XON is sent so the minicomputer can resume transmitting when at least two seconds of space are available in the receive queue.

I initially used a simple driver program with routines that would loop, checking the comm port and displaying what was received. The program then checked the keyboard, sending whatever printable characters that were typed to the comm port. Turbo Debugger made code testing simple, because we had established that communications with the PDP-11 were reliable by using a communications program. No special programming was required on the PDP-11 at this point, as its normal terminal logon and control dialogue provided data to ensure that the PC was receiving and transmitting correctly.

After the initial communication routines were verified and the configuration routines added, I began writing the code necessary to establish a session with the PDP-11. RSTS/E (resource sharing, time sharing/extended), a relatively old multiuser operating system, requires that each user identify himself with a user identification code (UIC) that consists of a project number (range 1 - 255), a programmer number (range 0 - 255), and a password. This is fine for technical people, who are (sometimes) willing and accustomed to cryptic logon sequences, but analysts and word processors are a different manner. None of my users would consent to having to log on and run a program manually each time they needed to print a document/file. I therefore had to develop PC routines that would first wake up the RSTS/E operating system on the configured serial port, log the user on, and then run a matching program on the PDP-11 to control its spooling operations. All the user would have to specify were printer selection and queue control options. Fortunately, DEC still gives the sources for the major commonly used system programs (CUSPs) into its multiuser operating systems, among which is the system logon program.

While researching the most expedient method for identifying users to the PDP-11 and logging them in automatically, I discovered that DEC has already installed an Optional Feature Patch in the RSTS/E logon program for this purpose. This patch allows the log-on program to recognize specific strings from terminal ports, to bypass password verification, and to automatically execute a captive program connected to that terminal line. The terminal line itself would serve to identify the user uniquely. I decided that the PDP-11's operating system security was sufficient for preventing unauthorized access to that machine, so long as the key word that activates the logon bypass is stored in encrypted format in the PC program.

The RSTS/E CUSP programs are written in the Basic-PLUS-2 language (a rather Pascal-like variant of Basic). It was a simple matter to implement the source change enabling this patch and to provide the logon program with the recognition string and program name to be executed when that string is detected on a terminal line. Debugging the logon sequence was quick and straightforward using the PDP-11's system console to monitor the logon program and Turbo Debugger to monitor the PC.

Figure 2 shows the sequence of operations necessary to establish a cooperative communication session between a PC and the PDP-11. The first two steps, as described earlier, control the PC's serial port and establish a valid logon session under the PDP-11's multiuser operating system. The third step, in which the minicomputer sends device information to the PC interface program, is critical. As the minicomputer maintains a table of devices available on line, which it sends to each PC when the interface program finishes the logon process, various printers can be added, removed, or moved to new locations on the PDP-11 without changing any of the PC software.

When the user selects a printer, the minicomputer specifies which spooler to use for printout routing, as shown in Step 4. The bulk of two-way communication takes place in Step 5, where the PC user can display and manipulate the print queues for the various attached printers. I decided to keep the queue manipulation operations as simple as possible for the users, even though this imposed an additional burden on the programs.

The user is able to display the queue for any printer, including the file name, user name (from the terminal connection), length, and date/time the file was queued. The user can then examine, change, or delete queue entries. This is one area where we rely upon professional courtesy, as there is no security checking on queue operations, and any user can manipulate all entries in the queue. After any necessary queue manipulation has been performed the user initiates PDP-11 file capture mode, directing the PC's serial port output to a file. This file capture continues until one minute has passed from the last output over the serial link; the file on the minicomputer is then closed and added to the spooler queue for the selected printer.

I decided to use individual command and response packets for communication between the minicomputer and the PC control programs. Table 1 shows the PDP-11 packets used and the information contained in each, while Table 2 shows the same information about the PC packets. Figure 3 shows the physical makeup of each packet.

Table 1: PDP-11 packets

  Packet    Packet           Description
  Type      Name
  ---------------------------------------------------

  100       PDP-11 OK       Tells PC program that PDP-
                            11 is active and awaiting
                            commands

  101       Device List     Tells the PC program that
            Header          device description packets
                            follow, and how many

  102       Device          Description of printer
            Description     available for use

  103       Queue List      Tells the PC program that
            Header          queue entry packets follow,
                            and how many

  104       Queue List      Contains information about
            Entry           the queue entry

  105       PDP-11 Error    Indicates an error in PDP-11
                            packet processing

Table 2: PC packets

  Packet    Packet             Description
  Type      Name
  -----------------------------------------------------------

  200       Micro             Tells PDP-11 that the
            Acknowledge       preceding packet was received
                              correctly

  201       Printer Select    Tells PDP-11 which printer to
                              use for subsequent operations

  202       Request           Asks PDP-11 to send the
            Queue List        queue list for the selected
                              printer

  203       Delete Entry      Tells PDP-11 to delete a
                              queue entry

  204       Move Entry        Tells PDP-11 to move a
                              queue entry to another
                              position in the queue

  205       Hold Entry        Tells PDP-11 to hold the
                              entry in the queue, but not
                              print the file

  206      Release Entry      Tells PDP-11 to release a
                              previously held queue entry for
                              printing in turn

  207      Print Start        Tells PDP-11 that print file
                              output is to begin from PC

  208      Print End          Tells PDP-11 that print file
                              output is finished

  209      Micro Error        Indicates an error in PC
                              packet processing

After the PC serial port has been initialized and attached, the PDP-11 recognition string activates the minicomputer program. The minicomputer program responds with an OK packet, followed by a device list header packet and as many individual device packets as necessary (up to a maximum of 20) to inform the PC program of all available print devices. This list is used to generate a menu of printer choices for the user, and is available under function key command.

When the available device list has been sent to the PC, it displays a function menu and accepts/processes commands from the user until a print file output operation begins. When a time limit of one minute has passed since any PC output activity, the minicomputer program closes the file, spooling it to the currently selected printer queue. After a file has been spooled to a printer, one additional minute is allowed for the PC program to restart command processing to the minicomputer. If no further communication is received, the PDP-11 operating system kills the minicomputer process.

Debugging the timing and content of the send/receive/respond packets sent between the two programs involved more than just the use of the Turbo Debugger on the PC. The large number of packet types and the varying sizes of data sections in each packet required a driver program to test the interchange.

The PC driver let me input packets to be sent to the PDP-11 while displaying the contents of received packets. It also provided a status display of any packet rejected because of a transmission error or bad checksum.

Listing Two shows the constant and type definitions for packets in the PC program. The PACKET_REC type definition uses the Pascal variant record structure to define each packet type, with the data portion of each packet broken into field definitions applicable to the packet type. This listing also shows the main processing portions of the packet send/receive procedures. A similar test program was written on the PDP-11 to display the contents of each packet received or sent on it, in the sequence of transmission. In addition to responding to PC commands, the PDP-11 program has commands that cause it to send PDP-11 OK, Device List, Printer Queue, and PDP-11 Error packets.

Once the command and response packet routines were working in the PDP-11 and PC programs, modifying the display/command processors on each machine into final form was fairly easy. The bottom-up implementation approach made a complete toolbox of data types and procedures available in the form of Turbo Pascal units on the PC and linkable subprograms on the minicomputer. I then had only to link these units and complete the user interface.

On the PDP-11, the same components used in the program that communicates with the PC were used to develop a system-wide communication monitor that maintains a complete transaction log of packets sent/received on the PDP-11. This log, incidentally, can also be used to provide project-oriented usage charge backs if the equipment involved must be depreciated. Fortunately, our PDP-11 was depreciated long ago, so this has not yet been necessary. Of course, there's always the next upgrade....

Conclusion

I hope this article has provided some insight into the problem of getting cooperative programs with potentially different languages to communicate on different platforms. Utilizing the bottom-up implementation approach, first establish reliable terminal communication between the devices. Don't forget your breakout box here, even if you hate hardware, because it sure can save you time. Second, implement serial communication configuration and processing routines in your program. Third, create procedures to handle the dialogue of commands and responses. Finally, ensure that the sequence and content of each dialogue are correct before moving on to complete functionality and user interface. If you build your modules into cooperating building blocks, this methodology should serve you well regardless of whether your communication medium is via a modem, twisted-pair, Ethernet, or fiber-optic LAN.

Availability

All source code for articles in this issue is available on a single disk. To order, send $14.95 (Calif. residents add sales tax) to Dr. Dobb's Journal, 501 Galveston Dr., Redwood City, CA 94063, or call 800-356-2002 (from inside Calif.) or 800-533-4372 (from outside Calif.). Please specify the issue number and format (MS-DOS, Macintosh, Kaypro).

_Implementing Multiple Computer Communications Links_ by Mark Servello

[LISTING ONE]

<a name="01f6_000b">

          Unit Serial_IO;
          {************ Unit Interface Description ***************}
          Interface
          Type Config_rec = record { contains the configuration info  }
                                   { for serial communication and user}
                                   { interface                        }
                              IRQ      : Integer;
                              Port     : Integer;
                              Data     : Integer;
                              Baud     : Integer;
                              Rate     : integer;    { bytes/sec }
                              Parity   : Char;
                              StopBits : Integer;
                              DataBits : Integer;
                              Snow     : Boolean;
                              Lines    : Integer;
                              Attention: String[40];
                              Fore     : Integer;
                              Back     : Integer;
                            end;

          Var  Current_Cfg      : Config_Rec;
          Procedure Check_Receive (var ch : char);
          Procedure Check_Send;
          Procedure Configure( New_Cfg : Config_Rec );

          {******************* Unit Implementation ******************}
          Implementation
          uses dos,crt;           { DOS and CRT units are utilized   }
          Const queue_max = 3936; { queue can hold 48 lines X 82 char}

          { *********** Serial Port Constants ***********************}
                COM1_data  = $03f8;     { COM1 Data port }
                COM1_IRQ   = $04;       { COM1 IRQ Number}
                COM2_data  = $02f8;     { COM2 Data port }
                COM2_IRQ   = $03;       { COM2 IRQ Number}
                ier_offset = 1;         { UART IER Reg   }
                mcr_offset = 4;         { UART Master Reg}
                sts_offset = 5;         { UART Status Reg}
                IRQ3_Int   = $0B;       { IntVec for IRQ3}
                IRQ4_Int   = $0C;       { IntVec for IRQ4}
                IRQ5_Int   = $0D;       { IntVec for IRQ5}
                IRQ6_Int   = $0E;       { IntVec for IRQ6}
                IRQ7_Int   = $0F;       { IntVec for IRQ7}
                PIC_CTL    = $20;       { Cmd for 8259   }
                PIC_MASK   = $21;       { Mask for 8259  }
                EOI        = $20;       { EoI command    }
                TBE        = $20;       { TBE bit        }
                XOFF_Char  = #19;       { ^S             }
                XON_Char   = #17;       { ^Q             }
                CR         = #13;
                LF         = #10;

          Type Queue_type = record
                              queue      : array[1..queue_max] of byte;
                              front,rear : integer;
                              count      : integer;
                            end;
               Port_Status = (XON, XOFF);
          Var  Transmit_Queue,
               Receive_Queue    : Queue_Type;
               Receive_Status,
               Transmit_Status  : Port_Status;
               Com_STS          : Integer;    { Serial Status I/O Port }
               mask_value       : integer;    { Control mask word      }
               old_isr          : pointer;    { storage for com port   }
                                              { ISR vector in place    }
          {**********************************************************}
          { Serial Interrupt Service Routine - grab the char and put }
          { it in the queue                                          }
          {**********************************************************}
          Procedure Serial_ISR; Interrupt;
          var ch        : byte;      { for the incoming char         }
              regs      : registers; { for using BIOS to beep bell   }
              next_rear : integer;
          begin
            inline($FA);                   { Disable interrupts      }
            ch := port[current_cfg.data];  { get character from port }
            with receive_queue do
              begin
                next_rear := rear + 1;
                if next_rear > queue_max then { wrap the pointer if  }
                  next_rear := 1;             { necessary            }
                if next_rear <> front then
                  begin                       { put char in queue    }
                    rear := next_rear;
                    queue[rear] := ch;
                  end
                else
                  begin                       { queue full,beep bell }
                    regs.ax := $0E07;
                    intr($10,regs);
                  end;
                inc(count);                   { Inc # entries and    }
                { Check for queue getting full. Send XOFF when one   }
                { second of space left                               }

                if count > (queue_max - current_cfg.rate) then
                  begin
                    Receive_status := XOFF;
                    repeat until (port[com_sts] and TBE)<>0;
                    port[current_cfg.data] := ord(XOFF_Char);
                  end;
              end;                            { END WITH             }
            inline($FB);              { Enable interrupts            }
            port[PIC_CTL] := EOI      { send end of interrupt to PIC }
          end;                        { END PROCEDURESERIAL_ISR      }

          {**********************************************************}
          { Attach Com Port Procedure - takes over interrupt vector  }
          { and initializes the UART entries in the configuration    }
          { table.                                                   }
          {**********************************************************}
          Procedure Attach_Com_Port;
          var mask_value : byte;
              Int_Num    : integer;
          begin
            Case Current_Cfg.IRQ of
              3 : Int_Num := IRQ3_Int;
              4 : Int_Num := IRQ4_Int;
              5 : Int_Num := IRQ5_Int;
              6 : Int_Num := IRQ6_Int;
              7 : Int_Num := IRQ7_Int;
            end;
            GetIntVec(Int_Num, old_ISR);      { Save old intvec      }
            SetIntVec(Int_Num, @Serial_ISR);  { point to the         }
                                              { Serial_ISR procedure }
            port[Current_Cfg.data+mcr_Offset] := $0B; { Set DSR/OUT2 }
            port[Current_Cfg.data+ier_Offset] := $01; { enable ints  }
            mask_value := port[pic_mask];             { read PIC mask}
            mask_value := mask_value and              { allow ints   }
                     (not (1 shl current_cfg.irq));   { on com port  }
            port[pic_mask] := mask_value;             { write it back}
                                                      { to PIC       }
            receive_status := XON;                    { send XON to  }
            repeat until (port[com_sts] and TBE)<>0;  { let other end}
            port[current_cfg.data] := ord(XON_Char);  { know we're   }
                                                      { here.        }
            transmit_status := XON;
          end;                                 { END ATTACH_COM_PORT }

          {**********************************************************}
          { Release Com Port Procedure - Gives the com port interrupt}
          { back to the previous holder.                             }
          {**********************************************************}
          Procedure Release_Com_Port;
          Var Int_Num : Integer;
          begin
            Case Current_Cfg.IRQ of
              3 : Int_Num := IRQ3_Int;
              4 : Int_Num := IRQ4_Int;
              5 : Int_Num := IRQ5_Int;
              6 : Int_Num := IRQ6_Int;
              7 : Int_Num := IRQ7_Int;
            end;
            mask_value := port[pic_mask];
            mask_value := mask_value or (1 shl current_cfg.IRQ);
            port[pic_mask] := mask_value;
            SetIntVec(Int_Num, Old_ISR);  { Restore the com port int-}
                                          { errupt vector            }
            Receive_Status := XOFF;
            Transmit_Status:= XOFF;
          end;

          {**********************************************************}
          { Check_Receive Procedure - This procedure checks the in-  }
          { coming com port queue. If any characters are waiting,    }
          { they are appended to the incoming string for program     }
          { processing.                                              }
          {**********************************************************}
          Procedure Check_Receive (var ch : char);
          begin
            with receive_queue do
              if front <> rear then      { Queue empty when front ptr }
                                         { = rear ptr                 }
                begin
                  front := front + 1;
                  if front > queue_max then
                    front := 1;
                  ch := chr(queue[front]);
                  Case ch of
                       XOFF_Char         : Transmit_Status := XOFF;
                       XON_Char          : Transmit_Status := XON;
                  end;                    { END CASE CH               }

                  { Check queue count and send XON if receiving stop- }
                  { ped and queue has 2 seconds of space free         }
                  dec(count);
                  if (count - (2 * current_cfg.rate)) > 0 then
                    begin
                      receive_status := XON;
                      repeat until (port[com_sts] and TBE)<>0;
                      port[current_cfg.data] := ord(XON_Char);
                    end;
                end;                      { END IF FRONT <> REAR      }
          end;                            { END PROC CHECK_RECEIVE    }

          {***********************************************************}
          { Check_Send Procedure - This procedure handles sending     }
          { chars out the COM port. If there are any characters wait- }
          { ing in the send queue, they are sent one at a time.       }
          {***********************************************************}
          Procedure Check_Send;
          Var ch   : char;
              done : boolean;
          Begin
            done := false;
            with transmit_queue do
              repeat
                if (front = rear) or   { Queue empty when front ptr }
                                       { = rear ptr                 }
                   (Transmit_Status = XOFF) then      { Don't send  }
                  done := true
                else
                  begin
                    if front > queue_max then
                      front := 1;
                    ch := chr(queue[front]);
                    repeat until (port[com_sts] and TBE)<>0;
                    port[current_cfg.data] := ord(ch);
                  end;
              until done;
          End;                            { END PROCEDURE CHECK_SEND }

          Procedure Configure( New_Cfg : Config_Rec );
          begin
           { Routine here reads configuration file based on location }
           { contained in environment string, then attaches the com  }
           { port and sets communication parameters                  }
          end;
          begin                         { Unit Initialization        }
            Configure( Current_Cfg );
          end.







<a name="01f6_000c"><a name="01f6_000c">
<a name="01f6_000d">
[LISTING TWO]
<a name="01f6_000d">


          Unit Packet_Comms;

          Interface
          Const Pkt_PDP_OK    = 100;
                Pkt_Dev_hdr   = 101;
                Pkt_Dev_lst   = 102;
                Pkt_Q_hdr     = 103;
                Pkt_Q_lst     = 104;
                Pkt_PDP_Err   = 105;
                Pkt_Micro_OK  = 200;
                Pkt_Print_Sel = 201;
                Pkt_Q_Req     = 202;
                Pkt_Q_Del     = 203;
                Pkt_Q_Move    = 204;
                Pkt_Q_Hold    = 205;
                Pkt_Q_Rel     = 206;
                Pkt_Prt_Start = 207;
                Pkt_Prt_End   = 208;
                Pkt_Micro_err = 209;
                Invalid_PDP_Packet = 01;
                Invalid_Checksum   = 02;

          Type Seq_Type   = array[1..2]  of char;
               Fname_Type = array[1..9]  of char;
               Dname_Type = array[1..20] of char;
               Packet_Rec = Record
                        Data_Checksum : array[1..5] of char;
                        Case Packet_Type : byte of
                          Pkt_PDP_OK:    (* PDP-11 OK has no fields *)();
                          Pkt_Dev_Hdr:   (Number_of_Devices : Seq_Type);
                          Pkt_Dev_Lst:   (Dev_Num     : Seq_Type;
                                          Dev_Name    : Dname_Type;
                                          Desc        : array [1..40] of char;
                                          Default     : char);
                          Pkt_Q_Hdr:     (Num_Entries : Seq_Type);
                          Pkt_Q_Lst:     (Q_Seq       : Seq_Type;
                                          Q_Filename  : Fname_Type;
                                          User        : array [1..20] of char;
                                          Length      : array [1..7]  of char;
                                          Date        : array [1..10] of char;
                                          Time        : array [1..5]  of char);
                          Pkt_PDP_Err:   (PDP_Error   : Char);
                          Pkt_Micro_Ok:  (* Micro OK has no fields *)();
                          Pkt_Print_Sel: (Print_Name  : Dname_Type);
                          Pkt_Q_Req:     (* Request for queue list *)();
                          Pkt_Q_Del:     (D_Filename  : Fname_Type;
                                          Del_Flag    : Char);
                          Pkt_Q_Move:    (M_Filename  : Fname_Type;
                                          Position    : Seq_Type);
                          Pkt_Q_Hold:    (H_Filename  : Fname_Type);
                          Pkt_Q_Rel:     (R_Filename  : Fname_Type);
                          Pkt_Prt_Start: (* Print file initialize *)();
                          Pkt_Prt_End:   (* Print file end        *)();
                          Pkt_Micro_Err: (Micro_Error : Char);
                      End;

          Procedure Receive_Packet( Var Packet : Packet_Rec );
          Procedure Send_Packet   ( Var Packet : Packet_Rec );

          Implementation
          Uses SerialIO;
          Procedure PDP_OK        ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);   forward;
          Procedure Dev_Header    ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Dev_Desc      ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Q_Header      ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Q_Entry       ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure PDP_Err       ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Micro_Ack     ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Print_Select  ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Req_Q         ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Del_Entry     ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Move_Entry    ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Hold_Entry    ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Rel_Entry     ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Print_Start   ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Print_End     ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);
          Procedure Micro_Err     ( Var Packet        : Packet_Rec;
                                    Var Comp_Checksum : Integer);

          Procedure Receive_Packet( Var Packet : Packet_Rec );
          Var Comp_Checksum,Comm_Checksum,Count,Val_Error       : Integer;
              ch              : Char;
              Err_Flag        : Boolean;
              Checksum_Str    : string[5];
          begin
            Err_Flag := False;
            Repeat
              comp_checksum := 0;
              with packet do
                begin
                  check_receive( ch );             { See if a packet's coming }
                  Val(ch, packet_type, val_error);
                  Case packet_type of
                    Pkt_PDP_OK:  PDP_OK     ( Packet, Comp_Checksum );
                    Pkt_Dev_Hdr: Dev_Header ( Packet, Comp_Checksum );
                    Pkt_Dev_Lst: Dev_Desc   ( Packet, Comp_Checksum );
                    Pkt_Q_Hdr:   Q_Header   ( Packet, Comp_Checksum );
                    Pkt_Q_Lst:   Q_Entry    ( Packet, Comp_Checksum );
                    PKT_PDP_Err: PDP_Err    ( Packet, Comp_Checksum );
                  else
                    begin
                      packet_type := Pkt_Micro_Err;
                      Micro_Error := chr(Invalid_Checksum);
                      Send_Packet( Packet );
                      Err_Flag := True;
                    end;
                  end;                              { End CASE }
                  If not Err_Flag then
                    begin
                      For Count := 1 to 5 do
                        begin
                          Check_receive( ch );
                          checksum_str := checksum_str + ch;
                        end;
                      Val(Checksum_str, comm_checksum, val_error);
                      If (val_error<>0) or (Comm_Checksum<>Comp_Checksum) then
                        begin
                          packet_type := Pkt_Micro_Err;
                          Micro_Error := chr(Invalid_Checksum);
                          Send_Packet( Packet );
                          Err_Flag := True;
                        end
                      else
                        begin
                          packet_type := Pkt_Micro_Ack;
                          Send_Packet( Packet);
                        end;                              { End Error        }
                    end;                                  { End Checksum Rcv }
                end;                                      { End With Packet  }
            Until not Err_Flag;
          end;

          Procedure Send_Packet( Var Packet : Packet_Rec );
          Var ch              : Char;
              Comp_Checksum,
              Count,
              Val_Error       : Integer;
              Err_Flag        : Boolean;
              Checksum_Str    : string[5];
              Temp_Packet     : Packet_Rec;

          begin
            Err_Flag := False;
            Repeat
              comp_checksum := 0;
              with packet do
                begin
                  Case packet_type of
                    Pkt_Micro_OK:     Micro_Ack    ( Packet, Comp_Checksum );
                    Pkt_Print_Sel:    Print_Select ( Packet, Comp_Checksum );
                    Pkt_Q_Req:        Req_Q        ( Packet, Comp_Checksum );
                    Pkt_Q_Del:        Del_Entry    ( Packet, Comp_Checksum );
                    Pkt_Q_Move:       Move_Entry   ( Packet, Comp_Checksum );
                    Pkt_Q_Hold:       Hold_Entry   ( Packet, Comp_Checksum );
                    Pkt_Q_Rel:        Rel_Entry    ( Packet, Comp_Checksum );
                    Pkt_Prt_Start:    Print_Start  ( Packet, Comp_Checksum );
                    Pkt_Prt_End:      Print_End    ( Packet, Comp_Checksum );
                    Pkt_Micro_Err:    Micro_Err    ( Packet, Comp_Checksum );
                  end;
                  Str( Comp_Checksum, Checksum_Str );
                  While (Length(Checksum_str) < 5) do
                    Checksum_Str := '0' + checksum_str;
                  For Count := 1 to 5 do
                    check_send(checksum_str[count]);
                  Receive_Packet( Temp_Packet );
                  If Temp_Packet.Packet_Type <> Pkt_PDP_OK then
                    Err_Flag := True;
                end;                                      { End With Packet  }
            Until not Err_Flag;
          end;

          {**************** Unit Initialization Main Code Block *************}
          Begin
          End.












Copyright © 1989, Dr. Dobb's Journal


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.