info-vax@ucbvax.ARPA (10/24/84)
From: engvax!KVC@cit-vax Well folks, it's done. The long-awaited DEUNA document is ready to be sent. I mailed a preliminary article out to INFO-VAX a few weeks ago. The new one contains a lot more detail. I am not sure how to go about distributing it... First of all, the article has already been sent to Larry Kilgallen (the Pageswapper editor). If you can wait, the best thing to do is read it when it comes out there. If you think you need to see it sooner (I do not know what the publication delay is on Pageswapper articles) I can send the article via mail. It is 104 blocks long, though, and I don't think I should send that much to the whole of INFO-VAX. On the other hand, it may be useful if it appeared in the INFO-VAX archives (this stuff does get archived, doesn't it????). Anyone have any ideas? What does the INFO-VAX maintainer think? Should I just see who is interested and send to them? I got a lot of responses to my preliminary article, though, and I expect at least as many to this one. Since my mailer isn't very smart (is anybody's?) I'd have to mail a separate copy to each person anyway, which would put a burden on the friendly people at CIT-VAX who gateway for us... How about this idea: I'll mail the article to someone on the ARPAnet who can put it where other ARPAnauts can FTP it (you cannot get to my system with FTP, only MAIL). If you really think you need the info and cannot FTP, then drop me a line and I'll mail you a special copy. Anyone think that's a problem? Am I making a lot of fuss for 104 blocks? Any volunteers who can put make it available to other FTP's? Oh well, I'll just sit back and see what people think I should do. Maybe Larry'll publish it before I have to decide... :-) By the way, is there anyway he can be reached by e-mail? It'd sure be nice to be able to submit articles electronically. What ever happened to the DECUS VAX? Weren't they gonna put the SIG newsletters on it? /Kevin Carosso engvax!kvc @ CIT-VAX.ARPA Hughes Aircraft Co.
info-vax@ucbvax.ARPA (10/24/84)
From: Richard Garland <OC.GARLAND%CU20B@COLUMBIA.ARPA> Mail-From: OC.GARLAND created at 24-Oct-84 09:26:03 Date: Wed 24 Oct 84 09:26:03-EDT From: Richard Garland <OC.GARLAND@CU20B.ARPA> Subject: Re: DEUNA details... To: engvax!KVC%cit-vax@COLUMBIA.ARPA cc: M_INFOVAX%cit-vax@COLUMBIA.ARPA, OC.GARLAND@CU20B.ARPA In-Reply-To: Message from "engvax!KVC%cit-vax@columbia.arpa" of Tue 23 Oct 84 23:40:52-EDT in re electronic input to DECUS: There is a VAX is Massachusetts on which DECUS folks have accounts for MAIL meeting announcements etc. Anybody from DEC listening? How about DEC creating a gateway from DEC-Marlboro through DEC enet to that machine? At least maybe a mailbox for suggestions. Anyone from DEC listening who knows how to ask about this please do. Rg ------- -------
info-vax@ucbvax.ARPA (10/28/84)
From: William "Chops" Westfield <BILLW@SRI-KL.ARPA> Using the DEUNA/DEQNA Ethernet Interface Under VMS Ned Freed, Kevin Carosso and Dan Newman MATHLIB Project Harvey Mudd College Claremont, CA 91711 We recently completed adapting an implementation of TCP/IP originally written for an Interlan NI1010 ethernet interface to a DEC DEUNA. This provided us a lot of insight into the way the DEUNA works. (Most of the insight came as a result of studying the VMS XEDRIVER source code and not by reading the I/O User's Guide.) ___ ______ _____ This article attempts to summarize what we learned so others will start with something more than what the I/O User's Guide provides. We also ___ ______ _____ present a program which captures and displays DEUNA performance statistics. We assume the reader is somewhat familiar with, but not necessarily an expert on, the principles of Ethernet operation. DISCLAIMER: Although the information contained herein is believed to be correct, the DEUNA and XEDRIVER are very complex and it is quite possible that our description is incorrect in some areas. We assume no responsibility for the content of this article. 1.0 The DEQNA versus the DEUNA The DEQNA is the equivalent of the DEUNA for a Q-bus system. It appears that the device driver interface for the DEQNA is nearly identical to the DEUNA. Differences appear to be minor -- we mention the ones we know of where appropriate. When we refer to the DEUNA or to XEDRIVER, we are also talking about the DEQNA and XQDRIVER unless a specific statement is made to the contrary. 2.0 The basics of XEDRIVER The VMS device driver for the DEUNA is called XEDRIVER. This driver is the one used by DECnet if it is running over the Ethernet, but DECnet's use of the device does not preclude additional utilization of the device by other programs. XEDRIVER is a very complex and sophisticated piece of software with an amazing range of capabilities. A large portion of its complexity is devoted to making it possible for multiple users to utilize the Ethernet simultaneously without disturbing each other. We wish to take this opportunity to express our appreciation to Rod Gamache, who wrote the beast. Page 2 3.0 Using the DEUNA In order to use the DEUNA via XEDRIVER, 6 basic steps must be performed: (1) Assign a channel or channels. (2) Establish device characteristics. (3) Start up the device. (4) Perform any desired I/O operations. (5) Shut down the device. (6) Deassign the channel(s). XEDRIVER provides a conventional $QIO interface for user use. (DECnet and the LAT-11 use their own special direct path into XEDRIVER at the IRP level, but other users should not use this facility. We will describe only the $QIO interface.) Regular high level languages whose I/O facilities employ RMS cannot use the DEUNA directly. First of all, certain set mode $QIO functions must be performed to establish required operational parameters. Second, $QIO write functions must be accompanied by a special P5 parameter specifying the destination address. These additional required parameters cannot be supplied by RMS. However, programs to control the DEUNA are not too difficult to write. In the following sections, we will describe the various details involved in performing each of the steps listed above. The excessive amount of detail provided is an attempt to accomodate any conceivable use for the DEUNA and is not an indication of how complex a simple program to use the device must be. 3.1 Assigning a channel When a channel is $ASSIGN'ed to the DEUNA (usually the device named "XEA0") the driver creates a new Unit Control Block (UCB), assigns a channel to it, and returns this channel to the caller. This new unit is a device in its own right and has a name of the form XEAn, where n is an ever-increasing integer greater than 0. If a user needs to $ASSIGN more than one channel to a single unit, the name of the unit associated with one channel can be determined via $GETDVI and another channel can be $ASSIGN'ed to the device name $GETDVI returns. The unit will remain in existence as long as someone has a channel assigned to it. When the last channel to a unit is $DASSGN'ed the unit is deleted automatically. 3.2 Establishing device characteristics The hardest part of using the DEUNA is setting its operating parameters properly. This is especially difficult if DECnet is running on the same device, because certain device characteristics will then be immutable. A IO$_SETMODE function $QIO with the IO$M_CTRL modifier bit set is used to set all operational parameters of the DEUNA. The parameter list used by the $QIO always has the same form -- a sequential list of ordered pairs: (parameter code, parameter value). Each parameter code is a 16 bit value and has a Page 3 symbolic name beginning with the prefix "NMA$C_". The parameters can be specified in any order in the list. Note that not all symbols of this form defined by the $NMADEF macro are valid DEUNA parameter codes; these symbols are used to establish characteristics for many different devices besides the DEUNA. A descriptor must be created that describes the parameter list and the address of this descriptor is passed as the P2 parameter in the IO$_SETMODE + IO$_CTRL $QIO call. P2 is the only parameter that this call uses. The following sections describe the various DEUNA parameters that can appear in the list. 3.2.1 The Ethernet physical address (NMA$C_PCLI_PHA) Every DEUNA on the Ethernet must be assigned a 48 bit physical address. This address serves to uniquely identify the device. Such addresses are customarily written as a series of six 8 bit hexadecimal values separated by dashes, e.g. AA-00-FA-23-3A-20. Each network packet sent by the DEUNA has a header that specifies the sender's and receiver's physical addresses. The DEUNA only bothers to receive packets that are addressed to it; normally all other packets are ignored (see promiscious mode and multicasting below). It would not make sense for one user to change the physical address of the device while someone else is using it. Consequently, when a request to change the physical address is made XEDRIVER checks to see if there is only one unit attached to the DEUNA. If so, the physical address can be changed. If not, a SS$_BADPARM error will be returned. In other words, the physical address can only be changed by an exclusive user of the device. If no one specifies a physical address the DEUNA will use its default hardware address. All DEUNA default addresses fall in the range AA-00-00-00-00-00 through AA-00-04-FF-FF-FF. Specific default addresses are uniquely assigned to each DEUNA during manufacture. The physical address parameter value appears in the parameter list as a counted string of bytes. The count word precedes the string. The first element in the string must be a modifier word, either NMA$C_LINMC_SET or NMA$C_LINMC_CLR, which specifies whether the physical address is to be set or cleared. (The symbols that begin with NMA$C_LINPR described in the I/O User's ___ ______ Guide do not seem to exist.) If NMA$C_LINMC_SET is used the physical address _____ must appear as the next 6 bytes in the string. When NMA$C_LINMC_CLR is used the rest of the string is ignored. A physical address remains in effect until it is explicitly cleared or the DEUNA goes idle (no one has channels assigned to it), at which point the physical address will reset unconditionally to the default hardware address. DECnet insists on setting the physical address to something of the form AA-00-04-00-xx-04, where xx is the DECnet node number. If some user beats DECnet to the DEUNA and sets up a different address (or even leaves the default address active) DECnet will report a circuit error and refuse to use the device. We recommend that DECnet be allowed to establish contact with the Page 4 DEUNA prior to attempting any other use so it can establish all the parameter settings it needs. If another user has to use the DEUNA prior to DECnet the physical address must be set to the value DECnet expects. In order to facilitate this, an additional modifier word for the NMA$C_PCLI_PHA parameter value has been added in version 4.0 of VMS. This modifier word, NMA$C_LINMC_SDF, sets the physical address of the DEUNA to the DECnet default address. The address is constructed by merging the value of the SYSGEN parameter SCSSYSTEMID to the constant portions of the DECnet address. 3.2.2 Protocol type (NMA$C_PCLI_PTY) Additional information is required to select who actually gets each received packet since everyone sharing the same DEUNA uses the same physical address. This information is provided by a 16 bit value called the protocol type. Each unit must select its own protocol type, so this must be specified by each unit prior to starting the DEUNA. Some protocol types already in use are: 60-00 Loopback functions 60-01 Dump/load functions 60-02 Remote console functions 60-03 DECnet 60-04 LAT (Ethernet terminal server) 60-06 Reserved for customer use by Digital 00-08 TCP/IP (as implemented by 4.2BSD UNIX) 90-00 Cross-company loopback messages The first three protocol types in the table above are handled specially by the DEUNA hardware. We only describe the functionality available via this mechanism briefly; for more information refer to the DEUNA User's Guide _____ ______ _____ (pages 4-76 to 4-99 and Appendix B). Packets with loopback function protocol types are received by the DEUNA and immediately retransmitted somewhere without informing the VAX of their presence. The first word of the data portion of the packet is used as an offset (skip count) into the packet indicating where to find a function code. The function code specifies exactly what loopback operation should be performed. The possible function codes are: 1 - reply, return packet to sender 2 - forward, send packet on to specified forwarding address In the returned packet the skip count is incremented by 8 while the protocol type is still 60-00, so whoever receives the packet sees a possibly different function code and forwarding address. Packets can be sent through multiple relay points by choosing an appropriate list of function codes and forwarding addresses. The remote console protocol type is used to determine what other DEUNAs, if any, are present on the network and their characteristics. The second word of the data portion of the packet is a function code: Page 5 5 - return this DEUNA's system ID table 6 - boot system request 9 - return this DEUNA's usage counters to sender The read internal usage counters operation is actually implemented in XEDRIVER -- it is not a hardware function like the first two. The dump/load protocol type is used for the loading and starting of remote hardware over the network for systems such as VAXELN and the LAT-11. There are no special checks in XEDRIVER to prevent any user from transmitting messages with these protocol types, so these capabilities can be used by nonprivileged user programs. 3.2.3 Access to different protocol types (NMA$C_PCLI_ACC) The first unit to use a given protocol type gets to pick how that protocol type may be used via this parameter. The possible parameter values are: Exclusive use = NMA$C_ACC_EXE This protocol type is reserved for the current user. Anyone else who tries to use it will get a SS$_BADPARM error. This access mode will be the default unless this parameter is specified. Default user = NMA$C_ACC_SHR Anyone may use this protocol type to transmit packets, but only the default user (the first one to use this protocol type) gets any received packets that have this protocol type associated with them. Shared use = NMA$C_ACC_LIM Each unit using this protocol type is required to declare a single physical address they are going to be communicating with (NMA$C_PCLI_DES parameter). Then they are locked into sending each to that single destination. When a packet of this protocol type is received, XEDRIVER figures out who its from and hands it to whoever is communicating with that particular place. If no one is sending there the packet is discarded. The NMA$C_PCLI_DES parameter (see below) is required if NMA$C_ACC_LIM is used. Whenever a protocol is shared among units all the parameter settings for each unit are cross-checked by XEDRIVER to insure the settings are compatible. Protocol sharing should be used whenever the special protocol types for loopback, system loading, or remote console functions are used. In these cases protocol sharing will prevent a single user from locking others out who need these capabilities as well. DECnet uses its protocol type (60-03) in exclusive use mode, so nonprivileged users cannot interfere with DECnet operations via XEDRIVER. Page 6 3.2.4 Shared protocol destination address (NMA$C_PCLI_DES) This parameter is used when NMA$C_PCLI_ACC is set to NMA$C_ACC_LIM to specify the address this unit is going to communicate with. See the NMA$C_PCLI_ACC parameter above. 3.2.5 Promiscious mode (NMA$C_PCLI_PRM) Promiscious mode is used to defeat the normal blocking of incoming packets whose destination addresses do not match the DEUNA's physical address. All packets regardless of physical address will be received if this mode is active (NMA$_STATE_ON). The default is promiscious mode off (NMA$_STATE_OFF). A promiscious mode user receives absolutely every packet that arrives on the network, including any packets received by other users of the same DEUNA. XEDRIVER contains special packet duplication code so who needs each packet gets their own copy. Promiscious mode also makes it possible to send packets with an arbitrary protocol type code. The protocol type for each packet transmitted must be specified in the P5 buffer of each write $QIO. Only one unit can have promiscious mode activated, so whoever turns it on first is the only one who can use it. Activating promiscious mode requires PHY_IO privilege. DECnet does not use promiscious mode. 3.2.6 Echo mode (NMA$C_PCLI_EKO) The DEUNA has the option of either receiving or ignoring the packets it transmits itself. If echo (or "half duplex") mode is off the DEUNA ignores its own transmissions (NMA$C_STATE_OFF). If echo mode is on (NMA$C_STATE_ON) any packet that is sent will also be eligible for reception. Echo mode is on by default. Echo mode is not available on the DEQNA and this parameter cannot be turned on. Packets received via echoing are treated in the same manner as packets obtained from the network -- the destination address and protocol type determine who, if anyone, gets the echoed packet. Echoed packet's checksums are not checked by the DEUNA hardware. However, XEDRIVER recognizes this fact and computes checksums for echoed packets automatically. Echo reception is a hardware feature of the DEUNA and thus everyone using the device must agree on the setting of this parameter. You have to be the exclusive user of the DEUNA to change the setting of echo mode. DECnet insists that echo mode be off. If echo mode is turned on you can do wierd things like activating default user mode and sending a packet to another user of the device. It is particularly useful for debugging, when you have no idea of what you are sending to the Page 7 network and want to receive and check it. DECnet must be shut down in order to perform such debugging operations. 3.2.7 Internal loopback or controller mode (NMA$C_PCLI_CON) If internal loopback mode is active the DEUNA's transmitter is looped back to the receiver and the entire device is isolated from the network. This is very similar to echo mode except for the enforced isolation from the network. Loopback mode is a hardware feature of the DEUNA and setting it will prevent the device from receiving any incoming packets. Consequently, you have to be the exclusive user of the DEUNA to turn on loopback mode. Loopback mode is off by default, and DECnet requires that it remain off, of course. The DEUNA will not respond to any special protocol type function requests while in internal loopback mode. (Don't confuse internal loopback mode with the loopback protocol type -- they are NOT the same thing. The former is a loopback to the local host CPU, while the latter is a loopback to someone else on the network.) The I/O User's Guide claims that the symbols NMA$C_LINCN_NOR and ___ ______ _____ NMA$C_LINCN_LOO are used to control loopback mode. These symbols do not exist. Use NMA$C_STATE_ON for NMA$C_LINCN_NOR and NMA$C_STATE_OFF for NMA$C_LINCN_LOO. These are the symbols used in XEDRIVER, at least. Loopback mode should be used when debugging a DEUNA connected to an active network to prevent any possible interference with that network from buggy programs. 3.2.8 Multicasting (NMA$C_PCLI_MLT and NMA$C_PCLI_MCA) Multicasting is a feature whereby the DEUNA can be instructed to receive messages with addresses other than the current physical address. Any packets sent to these "multicast" addresses will be received and processed by the DEUNA. Multicasting should not be confused with broadcast messages. The DEUNA, and in fact every other standard Ethernet interface, will always receive messages addressed to FF-FF-FF-FF-FF-FF (the standard broadcast address) regardless of the setting of the current physical address. Note: Physical addresses must have a 0 in the least significant bit of their first byte, while multicast addresses must have a 1 in this position. Each unit can enable or disable multicasting independently and set up its own list of multicast addresses. The lists from all the various units are combined by XEDRIVER and sent to the DEUNA hardware. Due to hardware limitations, a total of 10 different addresses can be nominated as multicast addresses by all the units combined. There are two parameters that control multicasting. The first, NMA$C_PCLI_MLT, is used to turn on and off the entire multicasting system. Multicasting is activated by setting NMA$C_PCLI_MLT to NMA$C_STATE_ON. Page 8 NMA$C_STATE_OFF is the default. The NMA$C_PCLI_MCA parameter is used to specify the current list of multicast addresses. This parameter's value is a counted string of bytes formed in the same fashion as the NMA$C_PCLI_PHA parameter's value is. The modifier word must be one of: NMA$C_LINMC_SET - Add these addresses to the list NMA$C_LINMC_CLR - Remove these addresses from the list NMA$C_LINMC_CAL - Clear the entire list unconditionally The multicasting address list is initially empty. DECnet enables only a single multicasting address, so 9 slots are left for users if DECnet is running (as of VMS version 3.5, at least). 3.2.9 Number of receive buffers (NMA$C_PCLI_BFN) This parameter specifies the number of receive buffers to maintain in system memory for this unit. Each unit must specify a (possibly different) value for this parameter. 3.2.10 Size of receive buffers (NMA$C_PCLI_BUS) The maximum size of user packet buffers on this unit is specified by this parameter. Each unit may choose its own receive buffer size. The default if this parameter is left unspecified is 512. The minimum value for this parameter is 46, the maximum 1500. Transmit buffers are allocated automatically by XEDRIVER as they are needed and can be of any size up 1500 bytes. (See the sections on padding and checksumming below for the true limits on the size of a transmitted packet.) 3.2.11 Data chaining (NMA$C_PCLI_DCH) Messages may be received by the DEUNA that will not fit into a single receive buffer. If data chaining is on (NMA$C_STATE_ON) the DEUNA has the option of splitting a received packet and placing it in two or more receive buffers. If data chaining is off (NMA$C_STATE_OFF) all packets will be truncated to fit into a single receive buffer. Data chaining is on by default. Data chaining is useful in avoiding having excessive buffers when large numbers of irregularly sized packets are expected. On the other hand, data chaining could have a tendency to fill too many of the available buffers when anomalously large packets are received. Since normal buffer sizes are fairly small (around 512 bytes), data chaining is recommended for almost all applications. Each unit can choose whether or not the packets it receives will be eligible for data chaining. Page 9 3.2.12 Checksum computation (NMA$C_PCLI_CRC) Ethernet packets normally have a 32 bit check sequence appended to them to insure detection of network errors. The check is a CRC code with this generating polynomial: G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 The entire Ethernet packet, including the destination address, source address, protocol type field and data is checksummed. The packet is treated as a binary polynomial M(x) which is divided by G(x) to produce a remainder R(x). The check sequence is the complement of R(x), with the most significant bit corresponding to the highest term in R(x). The receiver runs the entire packet including the checksum, through the same algorithm, at which time if the packet is valid the checksum register should contain the value C704DD7D. Receiver checksumming cannot be disabled. Checksums are also returned at the end of the packet by the IO$_READVBLK $QIO if there is room for it in the supplied buffer, although the reported length of the packet does not include the checksum. The checksum is returned so the user can check to make sure that packets are being properly transferred from the DEUNA to the VAX. A VAX CRC instruction can be used to check the checksum. The XEDRIVER source contains a list of the required coefficients (see CRCTABLE and the routine ASSEM_PKTS). Computation of checksums on transmitted packets will be disabled if NMA$C_PCLI_CRC is set to NMA$C_STATE_OFF. In addition, the operation of all special protocol types like loopback or remote console will be inhibited. The default for checksumming is initially NMA$C_STATE_ON, or alternately the value the first user of the DEUNA set it to. The NMAC_PCLI_CRC parameter is permanently set to NMA$C_STATE_ON on the DEQNA and cannot be turned off. The CRC takes up space in outgoing packets so its presence or absence affects the maximum allowable packet size -- see the description of padding below for more information. The CRC is computed in hardware, so the setting of this parameter affects all users of the DEUNA. Exclusive usage rights are required to change the setting of this parameter. DECnet requires that CRC checking be active. 3.2.13 Message padding (NMA$C_PCLI_PAD) The Ethernet specification imposes a minimum length limit on all packets. The DEUNA can optionally extend all packets to meet this requirement. This hardware facility is always enabled by XEDRIVER. However, XEDRIVER contains its own message padding facility that allows the lengths of short packets to be preserved. It is this facility of XEDRIVER, and not the hardware capability that is controlled by this parameter. If NMA$C_PCLI_PAD is disabled (NMA$C_STATE_OFF) all transmitted packets must be at least 46 bytes long including checksum. Shorter packets will be Page 10 extended and transmitted, but their lengths will be arbitrarily extended to the 46 byte minimum by the DEUNA. Enabling NMA$C_PCLI_PAD (NMA$C_STATE_ON) causes XEDRIVER to take some special actions. A length word is placed between the header and the data in each outgoing packet. The packet is then extended, if need be, and transmitted. Upon reception, the length word is removed and is used in lieu of length returned by the DEUNA. XEDRIVER padding is on by default. IMPORTANT: The presence of the length word in a padded packet makes padded and unpadded packets INCOMPATIBLE. Transmitters and receivers MUST agree on whether or not to pad their packets or errors will occur. For example, 4.2BSD TCP/IP does not pad its packets so the default of packet padding must be changed by VMS TCP/IP. Since padding adds some overhead to each packet it changes the maximum allowable size of a packet. The following table shows the maximum size of packets for the each possible padding and checksumming setting: padding on, checksumming on = 1494 bytes maximum size padding off, checksumming on = 1496 padding on, checksumming off = 1498 padding off, checksumming off = 1500 3.2.14 Device buffer size (NMA$C_PCLI_BSZ) This parameter controls the size of the hardware packet buffer used by the DEUNA. It affects all users of the device and exclusive access rights are required to change its value. Under normal conditions it should never be changed from its default value of 1500. Obviously, DECnet will not approve of changes to this parameter's value. 3.2.15 Protocol selection mode (NMA$C_PCLI_PRO) This undocumented parameter is used to select between normal network operations and point-to-point communications. Specifics on its use are unavailable at this time. 3.2.16 Hardware buffer quota (NMA$C_PCLI_HBQ) This undocumented parameter's function is unknown. It is, however, a write-only parameter and cannot be read back with a IO$_SENSEMODE operation. 3.2.17 Parameter summary Summing up, there are four basic categories of parameters: Page 11 (1) Mandatory parameters every unit must specify: NMA$C_PCLI_PTY, NMA$C_PCLI_BFN (2) Optional parameters any user can specify: NMA$C_PCLI_ACC, NMA$C_PCLI_DES, NMA$C_PCLI_BUS, NMA$C_PCLI_PAD, NMA$C_PCLI_MLT, NMA$C_PCLI_MCA, NMA$C_PCLI_DCH (3) Optional paramaters at most one user can use: NMA$C_PCLI_PRM (4) Optional parameters requiring exclusive usage rights: NMA$C_PCLI_PHA, NMA$C_PCLI_EKO, NMA$C_PCLI_CON, NMA$C_PCLI_CRC, NMA$C_PCLI_BSZ 3.3 Starting the DEUNA A $QIO IO$_SETMODE function with a IO$M_STARTUP modifier is used to start up the DEUNA. This runs the onboard diagnostics, which take about 6 seconds. After the diagnostics complete the device is ready for I/O operations. Only the first user of the device has to wait for the diagnostics to run. The operations of setting DEUNA parameters and actually starting the device may be combined in the same $QIO if both the IO$M_CTRL and IO$M_STARTUP modifiers are used together. Thus, the steps configuring and starting the device can be combined. 3.4 I/O operations Normal $QIO read and write operations are used to transfer packets to and from the DEUNA. The usual IO$_READVBLK and IO$_WRITEVBLK functions should be used. 3.4.1 Write $QIO parameters The P5 parameter of the IO$_WRITEVBLK $QIO specifies to whom the packet is being sent; the actual packet data (pointer in P1) contains only data. P2 specifies the length of the data block, all the other parameters are unused. P5 must point to an 8 byte buffer. The first 6 bytes specify the destination address for the packet, while the last 2 bytes specify the protocol type. The protocol type is ignored unless in promiscious mode. If promiscious mode is active this protocol specification overrides the one specified by the NMA$C_PCLI_ACC IO$_SETMODE parameter. The P5 parameter is ignored if a non-exclusive shared protocol type is being used -- XEDRIVER already knows who to send the message to. Page 12 3.4.2 Read $QIO parameters Read $QIO requests require the address of a buffer for the packet in P1 and the length of that buffer as P2. P5 can optionally point to a 14 byte buffer that will receive the destination address, the source address and the protocol type (in that order). The destination address may be either physical or multicast, the source address is always physical. The protocol type may vary from that set by NMA$C_PCLI_ACC if promiscious mode is enabled. The P5 buffer is not needed when communicating with a single other DEUNA on the Ethernet -- you should already know all the information the buffer can provide. The length of the packet received can only be determined by examining the transfer size field (the second word) in the I/O status block, so the IOSB is usually specified in read $QIO requests to the DEUNA. by examining the transfer size field (the second word) in the I/O status block, so the IOSB is usually specified in read $QIO requests to the DEUNA. 3.5 Shutting down the DEUNA After all I/O operations on the DEUNA are complete the device should be shut down in order to flush all active buffers and return buffer space to the system nonpaged memory pool. A IO$_SETMODE $QIO is used with the IO$M_CTRL and IO$M_SHUTDOWN modifiers to perform this function. Once this is applied to a unit, the unit cannot be used for additional I/O until another IO$M_STARTUP function is executed. 3.6 Deassigning the DEUNA Units attached to the DEUNA will not be dismantled until all channels to them are released. When this is done (normally via the usual $DASSGN system service) the unit will be deleted and all the parameter settings and information associated with it will be destroyed. 4.0 Debugging hints Debugging code for the DEUNA is usually pretty simple. If error returns from $QIO requests are encountered the key is to pay close attention to all values returned in the I/O status block. In particular, when performing IO$_SETMODE operations (the most error-prone of them all) the code of any offending parameter will be returned in the second longword of the IOSB. If the erroneous parameter was in fact specified, the value it was given is illegal. If the parameter is one that was left unspecified, it is required and must be specified in order for the $QIO to work. Any outstanding read requests will abort immediately if the DEUNA is unplugged from its H4000 transceiver. Most software can avoid problems caused by this by waiting a bit and then requeueing its read requests. Page 13 5.0 Sense mode $QIO's The IO$_SENSEMODE $QIO function can be used to retrieve various sorts of information about the DEUNA. A function modifier must be included with IO$_SENSEMODE to specify what information to return. 5.1 Characteristics retrieval If IO$_SENSEMODE is paired with the IO$M_CTRL modifier the $QIO will return device characteristics in a 2 longword buffer pointed to by P1 and extended device characteristics in a block of memory whose descriptor is pointed to by P2. Both parameters are optional. The returned block will be truncated to fit within the available space in the P2 buffer. The actual length of the returned information will be placed in the second word of the I/O status block if one is specified in the $QIO. The structure of the P1 buffer is shown in figure 6-4 in the I/O User's Guide. ___ ______ _____ The P2 buffer contains parameters that have the same format as those passed to a IO$_SETMODE function. However, IO$_SENSEMODE tries to return the ENTIRE list of all possible parameters in a user-supplied buffer; you don't get to pick what you want. The parameters are always returned in a specific order and all that must be done is to make the P2 buffer long enough to get what you want. (Of course, the order of the parameters may change in future versions of the driver, so it is best to search the returned list to find the desired information. See the DEUNA monitor program below for one way of doing this in Pascal.) Currently, the order in which the parameters are returned is: (1) NMA$C_PCLI_ACC - access mode for protocol (a) (2) NMA$C_PCLI_PRO - protocol selection mode (3) NMA$C_PCLI_BUS - receive buffer size (4) NMA$C_PCLI_BFN - number of receive buffers (5) NMA$C_PCLI_PHA - physical device address (b) (6) NMA$C_PCLI_PRM - promiscious mode (7) NMA$C_PCLI_MLT - multicast mode (8) NMA$C_PCLI_PAD - transmit data padding (9) NMA$C_PCLI_DCH - data chaining (10) NMA$C_PCLI_CRC - CRC generated on transmitted packets (11) NMA$C_PCLI_CON - loopback or controller mode (12) NMA$C_PCLI_PTY - protocol type (13) NMA$C_PCLI_MCA - multicast address list (c) (14) NMA$C_PCLI_EKO - echo mode (15) NMA$C_PCLI_BSZ - device buffer size (16) NMA$C_PCLI_HWA - default hardware address (d) (17) NMA$C_PCLI_DES - shared destination address (e) Notes: (a) Unless otherwise noted below, all parameter values are returned as longwords. Page 14 (b) The physical device address is returned as a counted string starting with a length word (always 6) and followed by the 6 byte address. If no one has explicitly set an address the address returned will be FF-FF-FF-FF-FF-FF, NOT the default hardware address. The NMA$C_PCLI_HWA parameter always returns the default hardware address. (c) The multicast address list is returned as a counted string containing all the addresses. There is no modifier word in the list. (d) NMA$C_PLCI_HWA always returns the default hardware address regardless of what the physical address is set to. It is returned as a counted string with no modifier word. (e) NMA$C_PLCI_DES is only returned to the shared-protocol users to whom it applies (i.e. NMA$C_PCLI_ACC is set to NMA$C_ACC_LIM). It is returned as a counted string with no modifier word. See the XEDRIVER source (in particular, the table LINE_PARAM) for more information on these parameters. 5.2 Usage counters The IO$_SENSEMODE function also has an undocumented function modifier. The DEUNA maintains a slew of statistics about what activities it has performed. These counters may be read using the function modifier IO$M_RD_COUNT along with IO$M_CTRL. If IO$M_CLR_COUNT is used as well the counters are read and then zeroed. The counters returned are in the order defined by the hardware starting with the seconds since the counters were last zeroed. Refer to figure 4-16 (page 4-31) of the DEUNA User's Guide (NOT the I/O User's Guide) _____ ______ _____ ___ ______ _____ for a complete list of all the counters. See the program at the end of this article for a demonstration of how to read the returned parameter block. The actual structure of the parameter block is similar in form to all the other DEUNA parameter blocks. The counters apply to the whole DEUNA and reflect the activities of everyone using the device, but it seems that any user can read or clear them with impunity. 6.0 SYSGEN parameter considerations Due to the potentially large size of Ethernet packets you should watch system nonpaged pool carefully when using a DEUNA (especially when TWO programs are using it at the same time). The pool may be exhausted and cause the system to hang. A possible cause of SS$_EXQUOTA errors is that the SYSTEM parameter MAXBUF, which controls the maximum size of an I/O buffer, is set to too small a value. (The routine EXE$ALLOCBUF allocates such buffers for XEDRIVER and checks this limit.) The default value for MAXBUF is around 1300 bytes, while the maximum Ethernet packet size is 1500 bytes. Page 15 7.0 A DEUNA monitoring program The following program monitors DEUNA performance by periodically sampling the internal hardware counters via a $QIO. Both transmission and reception information is presented in the form of "current" (e.g. bytes received/second), "average" (e.g. average bytes received/second), "minimum" and "maximum" (e.g. minimum and maximum bytes received/second) and "counter" (e.g. present value of the DEUNA's bytes received counter). The result is a MONITOR-like display of DEUNA activity. XEA0 is monitored by default. If you want to monitor a different device, just define a symbol to run the program. The device name to monitor will then be read off the command line with a LIB$GET_FOREIGN call. The program begins operations by clearing the terminal screen and drawing its display. To exit type either control-c or control-z. To repaint the screen, type contrl-w. The counters will be reset if the "seconds since last reset" counter exceeds 65530. The counters can be reset manually by typing control-r. The program uses the protocol type 61-02, which is not reserved for any specific purpose. This protocol type can be changed if it conflicts with anything. The program is written in VAX Pascal V2 and requires no special privileges. (* Monitor DEUNA performance statistics. Written by Dan Newman and Ned Freed, 21-July-84, MATHLIB Project, Harvey Mudd College *) [inherit ('SYS$LIBRARY:STARLET.PEN')] Program DEUNA; Const NMA$C_PCLI_BFN = %x451; NMA$C_PCLI_PTY = %xB0E; NMA$C_CTLIN_ZER = %x000; NMA$C_CTLIN_DBR = %x3F2; NMA$C_CTLIN_MBL = %x3F4; NMA$C_CTLIN_RFL = %x426; NMA$C_CTLIN_BRC = %x3E8; NMA$C_CTLIN_MBY = %x3EA; NMA$C_CTLIN_OVR = %x428; NMA$C_CTLIN_LBE = %x411; NMA$C_CTLIN_DBS = %x3F3; NMA$C_CTLIN_MBS = %xA8D; NMA$C_CTLIN_BSM = %x3F7; NMA$C_CTLIN_BS1 = %x3F6; NMA$C_CTLIN_BID = %x3F5; NMA$C_CTLIN_BSN = %x3E9; NMA$C_CTLIN_MSN = %xA8E; NMA$C_CTLIN_SFL = %x424; NMA$C_CTLIN_CDC = %x425; NMA$C_CTLIN_UFD = %x427; Type UnsignedWord = [Word] 0..65535; $Word = [Word] -32768..32767; CounterType = array [1..256] of unsigned; Counters = (BRC, DBR, MBY, MBL, RFL, OVR, LBE, BSN, DBS, MSN, MBS, BID, SFL, BSM, BS1, CDC, UFD); StartupType = Record PCLI_BFN : UnsignedWord; PCLI_BFN_VALUE : unsigned; PCLI_PTY : UnsignedWord; PCLI_PTY_VALUE : unsigned; end; (* StartupType *) StatType = Record Current, Previous : array [Counters] of unsigned; Rate, Accumulated, Min, Max : array [Counters] of real; end; (* StatType *) Var DEUNAChannel, TTYChannel : $Word; Count, Flag, Status : integer := 0; CounterBuffer : CounterType; StartupBuffer : StartupType; Stats : StatType; CounterDescriptor, IOStatus, StartupDescriptor : array [1..2] of integer; CurrentTime, LastTime : unsigned; Exit : boolean := false; DeviceName : varying [20] of char; DeviceNameLength : integer; Page 16 First : boolean := true; Index : Counters; Lines : array [Counters] of integer; LongPointer : ^unsigned; WordPointer : ^UnsignedWord; [Unbound, External (LIB$GET_FOREIGN)] Function $GetForeign ( var GetStr : varying [l1] of char; Prompt : [Class_S] packed array [l2..u2 : integer] of char := %Immed 0; %Ref OutLen, Force : integer := %Immed 0) : integer; extern; [Unbound, External (LIB$STOP)] Function $STOP ( %Immed Error : integer) : integer; extern; [Unbound, External (LIB$SET_BUFFER)] Function $SetBuffer ( Buffer : [Class_S] packed array [l1..u1 : integer] of char := %Immed 0; %Ref OldBuffer : packed array [l2..u2 : integer] of char := %Immed 0) : integer; extern; [Unbound, External (LIB$PUT_BUFFER)] Function $PutBuffer ( %Ref OldBuffer : packed array [l1..u1 : integer] of char := %Immed 0) : integer; extern; [Unbound, External (LIB$PUT_SCREEN)] Function $PutScreen ( Text : [Class_S] packed array [l1..u1 : integer] of char; %Ref LineNo, ColNo, Flags : integer) : integer; extern; [Unbound, External (LIB$ERASE_PAGE)] Function $ErasePage ( %Ref LineNo, ColNo : integer := %Immed 0) : integer; extern; Function Min (Real1, Real2 : real) : real; Begin Min := Real1; if Real2 < Real1 then Min := Real2; End; Function Max (Real1, Real2 : real) : real; Begin Max := Real1; if Real2 > Real1 then Max := Real2; End; (* Max *) Procedure IncrementPointers (Inc : integer); Begin (* IncrementPointers *) LongPointer::unsigned := LongPointer::unsigned + Inc; WordPointer::unsigned := WordPointer::unsigned + Inc; End; (* IncrementPointers *) Procedure ReadCounterBuffer; var CurrentCounter : UnsignedWord; i : integer; Begin (* ReadCounterBuffer *) WordPointer := nil; LongPointer := nil; IncrementPointers (Iaddress (Counterbuffer)); while LongPointer::unsigned < Iaddress (Counterbuffer) + size (Counterbuffer) do begin CurrentCounter := WordPointer^; IncrementPointers (2); i := int (uand (%xFFF, CurrentCounter)); if CurrentCounter = 0 then IncrementPointers (256) else if i = NMA$C_CTLIN_ZER then CurrentTime := WordPointer^ else if i = NMA$C_CTLIN_DBR then Stats.Current[DBR] := LongPointer^ else if i = NMA$C_CTLIN_MBL then Stats.Current[MBL] := LongPointer^ else if i = NMA$C_CTLIN_RFL then Stats.Current[RFL] := WordPointer^ else if i = NMA$C_CTLIN_BRC then Stats.Current[BRC] := LongPointer^ else if i = NMA$C_CTLIN_MBY then Stats.Current[MBY] := LongPointer^ Page 17 else if i = NMA$C_CTLIN_OVR then Stats.Current[OVR] := WordPointer^ else if i = NMA$C_CTLIN_LBE then Stats.Current[LBE] := WordPointer^ else if i = NMA$C_CTLIN_DBS then Stats.Current[DBS] := LongPointer^ else if i = NMA$C_CTLIN_MBS then Stats.Current[MBS] := LongPointer^ else if i = NMA$C_CTLIN_BSM then Stats.Current[BSM] := LongPointer^ else if i = NMA$C_CTLIN_BS1 then Stats.Current[BS1] := LongPointer^ else if i = NMA$C_CTLIN_BID then Stats.Current[BID] := LongPointer^ else if i = NMA$C_CTLIN_BSN then Stats.Current[BSN] := LongPointer^ else if i = NMA$C_CTLIN_MSN then Stats.Current[MSN] := LongPointer^ else if i = NMA$C_CTLIN_SFL then Stats.Current[SFL] := WordPointer^ else if i = NMA$C_CTLIN_CDC then Stats.Current[CDC] := WordPointer^ else if i = NMA$C_CTLIN_UFD then Stats.Current[UFD] := WordPointer^; if CurrentCounter > %xCFFF then IncrementPointers (4) else IncrementPointers (2); end; (* while *) End; (* ReadCounterBuffer *) Procedure PutScreen (Text : packed array [l1..u1 : integer] of char; LineNo, ColNo, Flags : integer); var Status : integer; Begin (* PutScreen *) Status := $PutScreen (Text, LineNo, ColNo, Flags); if not odd (Status) then $STOP (Status); End; (* PutScreen *) Procedure UpdateLine (Rate, Average, Min, Max : real; Current : unsigned; LineNo : integer); var String : varying [9] of char; Begin (* UpdateLine *) writev (String, Rate:7:1); PutScreen (String, LineNo, 37, 0); writev (String, Average:7:1); PutScreen (String, LineNo, 46, 0); writev (String, Min:7:1); PutScreen (String, LineNo, 55, 0); writev (String, Max:7:1); PutScreen (String, LineNo, 64, 0); writev (String, Current:9); PutScreen (String, LineNo, 72, 0); End; (* UpdateLine *) Procedure Update; var Buffer : packed array [1..1000] of char; String : varying [7] of char; Date : packed array [1..23] of char; Index : Counters; Status : integer; Begin (* Update *) (* Set up the screen buffer *) Status := $SetBuffer (Buffer); if not odd (Status) then $STOP (Status); (* Get the date *) $ASCTIM (,Date); for Status := 23 downto 13 do Date[Status] := Date[Status-1]; (* Build the Buffer *) PutScreen (Date, 2, 29, 0); for Index := BRC to UFD do UpdateLine (Stats.Rate[Index], Stats.Accumulated[Index]/Count, Stats.Min[Index], Stats.Max[Index], Stats.Current[Index], Lines[Index]); writev (String, CurrentTime:6); Status := 1; While String[Status] = ' ' do Status := succ (Status); PutScreen (substr (String, Status, 1+length(String)-Status), 24, 35, 0); (* Post the buffer to the screen via one $QIO *) Status := $PutBuffer; if not odd (Status) then $STOP (Status); Page 18 (* Stop blocking the AST's *) Status := $SETAST (1); if not odd (Status) then $STOP (Status); (* See if the "Resetting the Counters" flag needs to be removed *) if Flag = 2 then begin Flag := 0; PutScreen (' ', 1, 59, 0); PutScreen (' ', 24, 35, 0); end; (* if Flag *) if Flag = 1 then Flag := succ (Flag); End; (* Update *) Procedure Paint; var Buffer : packed array [1..900] of char; Status : integer; Begin (* Paint *) (* Set up the Screen buffer *) Status := $SetBuffer (Buffer); if not odd (Status) then $STOP (Status); (* Erase the page *) $ErasePage (1,1); (* Put up the headers *) PutScreen ('DEUNA Performance Statistics', 1, 26, 1); PutScreen ('Device', 4, 1, 0); PutScreen (DeviceName, 4, 8, 0); PutScreen ('Current', 4, 37, 8); PutScreen ('Average', 4, 46, 8); PutScreen ('Minimum', 4, 55, 8); PutScreen ('Maximum', 4, 64, 8); PutScreen ('Counter', 4, 74, 8); PutScreen ('Received:', 5, 1, 0); PutScreen ('Bytes', 6, 3, 0); PutScreen ('Packets', 7, 3, 0); PutScreen ('Multicast Bytes', 8, 3, 0); PutScreen ('Multicast Packets', 9, 3, 0); PutScreen ('Packets in Error', 10, 3, 0); PutScreen ('Lost - Internal Buffer Errors', 11, 3, 0); PutScreen ('Lost - Local Buffer Errors', 12, 3, 0); PutScreen ('Transmitted:', 13, 1, 0); PutScreen ('Bytes', 14, 3, 0); PutScreen ('Packets', 15, 3, 0); PutScreen ('Multicast Bytes', 16, 3, 0); PutScreen ('Multicast Packets', 17, 3, 0); PutScreen ('Packets Deferred', 18, 3, 0); PutScreen ('Packets Aborted', 19, 3, 0); PutScreen ('Packets with Several Errors', 20, 3, 0); PutScreen ('Packets with 1 Error', 21, 3, 0); PutScreen ('Collision Check Failures', 22, 3, 0); PutScreen ('Unrecognized Frame Destinations', 23, 1, 0); PutScreen ('Seconds Since Last Counter Reset:', 24, 1, 0); (* Now put the info to the screen in one $QIO *) Status := $PutBuffer; if not odd (Status) then $STOP (Status); End; (* Paint *) Procedure ResetDEUNA; var Status : integer; Begin (* ResetDEUNA *) Flag := 1; PutScreen ('Resetting the Counters' + chr (7), 1, 59, 3); Status := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SENSEMODE + IO$M_CTRL + IO$M_CLR_COUNT + IO$M_RD_COUNT, P2 := IAddress (CounterDescriptor)); if not odd (Status) then $STOP (Status); if not odd (IOStatus[1]) then $STOP (IOStatus[1]); ReadCounterBuffer; for Index := BRC to UFD do Stats.Previous[Index] := Stats.Current[Index]; LastTime := CurrentTime; PutScreen (' ', 24, 34, 0); End; (* ResetDEUNA *) Page 19 Procedure EnableAST; forward; Procedure AST (var Character : char); var Status : integer; Begin (* AST *) (* Kill any other I/O requests on this channel *) Status := $CANCEL (TTYChannel); if not odd (Status) then $STOP (Status); case IAddress (Character) of (* ^C and ^Z -- Exit gracefully *) 03, 26 : begin (* Shut the DEUNA down *) RetStatus := $CANCEL (DEUNAChannel); if not odd (RetStatus) then $STOP (RetStatus); RetStatus := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SETMODE + IO$M_CTRL + IO$M_SHUTDOWN); if not odd (RetStatus) then $STOP (RetStatus); RetStatus := $DASSGN (DEUNAChannel); if not odd (RetStatus) then $STOP (RetStatus); Exit := true; end; (* ^R -- Reset the DEUNA *) 18 : ResetDEUNA; (* ^W -- Repaint the screen *) 23 : begin Paint; Update; end; end; (* case *) (* Reset the AST *) EnableAST; End; (* AST *) Procedure EnableAST; var Mask : array [1..2] of integer; Status : integer; Begin (* EnableAST *) (* Out-of-band AST Mask -- Trap ^C, ^R, ^W and ^Z*) Mask[1] := 0; Mask[2] := (2**3) + (2**18) + (2**23) + (2**26); (* Queue the out-of-band AST *) Status := $QIOW (Chan := TTYChannel, Func := IO$_SETMODE + IO$M_OUTBAND, P1 := %Immed AST, P2 := IAddress (Mask)); if not odd (Status) then $STOP (Status); End; (* EnableAST *) Begin (* DEUNA *) (* These numbers represent which lines on the screen to place the data on *) Lines[BRC] := 6; Lines[DBR] := 7; Lines[MBY] := 8; Lines[MBL] := 9; Lines[RFL] := 10; Lines[OVR] := 11; Lines[LBE] := 12; Lines[BSN] := 14; Lines[DBS] := 15; Lines[MSN] := 16; Lines[MBS] := 17; Lines[BID] := 18; Lines[SFL] := 19; Lines[BSM] := 20; Lines[BS1] := 21; Lines[CDC] := 22; Lines[UFD] := 23; (* Setup the information to initialize the DEUNA *) with StartupBuffer do begin PCLI_BFN := NMA$C_PCLI_BFN; PCLI_BFN_VALUE := 1; PCLI_PTY := NMA$C_PCLI_PTY; PCLI_PTY_VALUE := %x0261; (* some unused protocol here *) end; (* with *) (* Build the descriptors for the $QIO calls *) CounterDescriptor[1] := size (CounterBuffer); CounterDescriptor[2] := IAddress (CounterBuffer); StartupDescriptor[1] := size (StartupBuffer); StartupDescriptor[2] := IAddress (StartupBuffer); Page 20 (* Get a Channel to the terminal *) Status := $ASSIGN ('TT:', TTYChannel); if not odd (Status) then $STOP (Status); (* Get a Channel to the DEUNA *) Status := $GetForeign (DeviceName, , DeviceNameLength); if not odd (Status) then $STOP (Status); if DeviceNameLength = 0 then DeviceName := 'XEA0'; Status := $ASSIGN (DeviceName, DEUNAChannel); if not odd (Status) then $STOP (Status); (* Set up the ^C, ^R, ^W and ^Z traps *) EnableAST; (* Initialize the DEUNA *) Status := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SETMODE + IO$M_STARTUP + IO$M_CTRL, P2 := IAddress (StartupDescriptor)); if not odd (Status) then $STOP (Status); if not odd (IOStatus[1]) then $STOP (IOStatus[1]); (* Start our loop *) Repeat (* Read the DEUNA counters *) Status := $QIOW (Chan := DEUNAChannel, Iosb := IOStatus, Func := IO$_SENSEMODE + IO$M_RD_COUNT + IO$M_CTRL, P2 := IAddress (CounterDescriptor)); if not odd (Status) then $STOP (Status); if not odd (IOStatus[1]) then $STOP (IOStatus[1]); (* Dissect the returned information *) ReadCounterBuffer; (* Now generate the statisitics *) if CurrentTime > 65530 then ResetDEUNA else begin if LastTime <> CurrentTime then begin if First then begin for Index := BRC to UFD do begin Stats.Previous[Index] := Stats.Current[Index]; Stats.Accumulated[Index] := 0; Stats.Min[Index] := maxint; Stats.Max[Index] := -1; end; (* for *) end else begin Count := succ (Count); for Index := BRC to UFD do begin Stats.Rate[Index] := (Stats.Current[Index] - Stats.Previous[Index]) / (CurrentTime - LastTime); Stats.Accumulated[Index] := Stats.Accumulated[Index] + Stats.Rate[Index]; Stats.Min[Index] := Min (Stats.Min[Index], Stats.Rate[Index]); Stats.Max[Index] := Max (Stats.Max[Index], Stats.Rate[Index]); Stats.Previous[Index] := Stats.Current[Index]; end; (* for *) end; (* if First *) (* Display the information on the terminal *) if First then begin First := false; Paint; end else Update; if Exit then begin $ErasePage (1,1); $EXIT (1); end; (* if Exit *) end; (* if LastTime <> CurrentTime *) LastTime := CurrentTime; Page 21 end; (* if CurrentTime > 65530 *) until 1 = 2; End. (* DEUNA *) -------