CP/NET
Network Operating System
Reference Nanual

 

Copyright © 1982
Digital Research
P.O. Box 579
160 Central Avenue
Pacific Grove, CA 93950
(408) 649-3896
TWX 910 360 5001

 

All Rights Reserved


COPYRIGHT

Copyright © 1980, 1981, 1982 by Digital Research. All rights reserved. No part of this publication may be reproduced, transmitted, transcribed, stored in a retrieval system, or translated into any language or computer language, in any form or by any means, electronic, mechanical, magnetic, optical, chemical, manual or otherwise, without the prior written permission of Digital Research, Post Office Box 579, Pacific Grove, California, 93950.

Portions of this manual are, however, tutorial in nature. Thus, the reader is granted permission to include the example programs, either in whole or in part, in his own programs.

DISCLAIMER

Digital Research makes no representations or warranties with respect to the contents hereof and specifically disclaims any implied warranties of merchantability or fitness for any particular purpose. Further, Digital Research reserves the right to revise this publication and to make changes from time to time in the content hereof without obligation of Digital Research to notify any person of such revision or changes.

TRADEMARKS

CP/M and CP/NET are registered trademarks of Digital Research. ASM, CP/NOS, DDT, LINK-80, MP/M II, RMAC, SID, and ZSID are trademarks of Digital Research. Altos is a registered trademark of Altos Computer Systems. Intel is a registered trademark of Intel Corporation. Keybrook is a registered trademark of Keybrook Business Systems, Inc. ULCnet is a registered trademark of Orange Compuco, Inc. Xerox, 820 Computer, and R820-II are registered trademarks of Xerox Corporation. Z80 is a registered trademark of Zilog, Inc. Corvus OMNINET is a trademark of Corvus Systems, Inc. DSC-2 is a trademark of Digital Microsystems. DB8/5200 is a trademark of Dynabyte. FileServer is a trademark of Keybrook Business Systems, Inc.

The CP/NET Network Operating System Reference Manual was prepared using the Digital Research TEX Text Formatter and printed in the United States of America by Commercial Press/Monterey.

Fifth Edition: November 1982


Foreword

CP/NET®, a network operating system, enables microcomputers to access common resources via a network. CP/NET allows microcomputers to share and transfer disk files, to share printers and consoles, and to share programs and data bases. CP/NET consists of servers running MP/M II® and requesters running CP/M®. The servers are hosts that manage the shared resources that the network requesters can access.

The hardware environment for CP/NET must include two or more microcomputers that can communicate in some way.

One of the microcomputers must execute the MP/M II operating system to provide the CP/NET server facilities. The processor executing MP/M II must be an 8080, 8085, or Z80 CPU with a minimum of 32K bytes of memory, 1 to 16 consoles, 1 to 16 logical or physical disk drives each containing up to eight megabytes, a clock/timer interrupt, and a network interface.

The CP/NET requester microcomputers must have 8080, 8085, or Z80 CPUs with at least 16K bytes of memory, 0 to 16 logical or physical disk drives each containing up to eight megabytes, and a network interface. A console is not absolutely required although it is strongly recommended.

The CP/NET Network Operating System Reference Manual is intended for several different levels of CP/NET users. It contains all the information you need to use CP/M applications programs on a CP/NET requester, to write new application programs under CP/NET, and to customize CP/NET for a specific network.

Section 1, an overview of the CP/NET system, discusses CP/NET features, network topologies, and the principles behind CP/NET operation.

Section 2 contains all the information you need to use the network when executing CP/M application programs. You need no skill level beyond that required for normal CP/M operation.

Section 3 describes the CP/NET interprocessor message format and each of the Network Disk Operating System (NDOS) functions you can invoke from application programs. This section provides the information you need to access the network primitives. Section 3 also discusses the implications of performing CP/M operations on a resource controlled by the MP/M II operating system.

Section 4 provides information for the systems programmer. This section describes how to write a custom Slave Network 1/0 System (SNIOS) that performs the CP/NET requester network functions. The mechanics of implementing and debugging a custom SNIOS are also discussed. Programmers attempting to develop an SNIOS should be familiar with CP/M and experienced in writing a custom CP/M BIOS. This section also explains how to write a custom Network Interface Process (NETWRKIF) that performs the CP/NET server network functions.

Section 4 also discusses implementing and debugging the NETWRKIF module. You must have a high degree of competence and experience with MP/M II to develop a custom NETWRKIF. You must be familiar with the process and queue descriptor data structures and the MP/M II XDOS primitive functions. Experience with implementing an XIOS for MP/M II might also be necessary.

Appendixes to this manual contain several example network communications packages.


Table of Contents

1 CP/NET Overview
  1.1 CP/NET Features
  1.2 CP/NET Configurations
  1.3 How the Requester Works
  1.4 How the Server Works

2 CP/NET Utilities
  2.1 The LOGIN Command
  2.2 The LOGOFF Command
  2.3 The NETWORK Command
  2.4 The LOCAL Command
  2.5 The ENDLIST Command
  2.6 The DSKRESET Command
  2.7 The CPNETLDR Command
  2.8 The CPNETSTS Command
  2.9 CTRL-P
  2.10 The MAIL Utility
    2.10.1 Menus
    2.10.2 Data Entry
    2.10.3 MAIL Options
    2.10.4 Error Messages

3 CP/NET Programmer's Guide
  3.1 CP/NET Interprocessor Message Format
    3.1.1 Message Format Code
    3.1.2 Message Destination Processor ID
    3.1.3 Message Source Processor ID
    3.1.4 CP/M Function Code
    3.1.5 Size
    3.1.6 CP/NET Message
    3.1.7 Additional Packaging Overhead
  3.2 Running Applications Transparently Under CP/NET
    3.2.1 MP/M II vs. CP/M File Systems
    3.2.2 Error Handling Under CP/NET
    3.2.3 Temporary Filename Translation
    3.2.4 Opening System Files on User 0
    3.2.5 Compatibility Attributes
    3.2.6 Password Protection Under CP/NET
    3.2.7 Networked List and Console Devices
  3.3 CP/NET Function Extensions to CP/M
  3.4 CP/NET Applications

4 CP/NET System Guide
  4.1 General Network Considerations
    4.1.1 Functions of the CP/NET Physical Modules
    4.1.2 Interfacing a Computer to a Network
    4.1.3 Developing a Network Layer
    4.1.4 Error Recovery
  4.2 Customizing the Requester's SNIOS
    4.2.1 Slave Network 1/0 System Entry Points
    4.2.2 Requester Configuration Table
    4.2.3 Prefiguring the Configuration Table
    4.2.4 Sending and Receiving Messages
    4.2.5 Generating and Debugging a Custom SNIOS
  4.3 Customizing the Server
    4.3.1 Detecting and Receiving Incoming Messages
    4.3.2 The Architecture of the NETWRKIF Module
    4.3.3 Elements of the NETWRKIF
    4.3.4 Enhancements and Additions to the NETWRKIF
    4.3.5 MP/M II Performance Factors and NETWRKIF
    4.3.6 Generating the NETWRKIF
    4.3.7 Debugging the NETWIRKIF
  4.4 Implementing Non-MP/M II Servers

Appendixes

A CP/NOS
  A.1 Overview
  A.2 System Requirements
  A.3 Customizing the CP/NOS
  A.4 Building the CP/NOS System
  A.5 Debugging the System

B CP/NET 1.2 Standard Message Formats

C CP/NET 1.2 Logical Message Specifications

D NDOS Function Summary

E A Simple RS-232C CP/NET System
  E.1 Protocol Handshake
  E.2 Binary Protocol Message Format
  E.3 ASCII Protocol Message Format
  E.4 Modifying the SNIOS
  E.5 Modifying the NETWRKIF

F A CP/NET System for Use with ULCnet
  F.1 Overview of ULCnet
  F.2 Customizing a ULCnet SNIOS for the Requester
  F.3 Creating the ULCnet Server

G Using CP/NET 1.2 With Corvus OMNINET
  G.1 The Corvus Engineering Transporter
  G.2 Implementation Structure
  G.3 The SNIOS Implementation
  G.4 The NETWRKIF Implementation Model
  G.5 Possible Improvements to NETWRKIF

Tables

2-1. Receive Mail Message-handling Options

3-1. Interface Attributes
3-2. BDOS Error Modes

4-1. Requester Configuration Table
4-2. Server Configuration Table
4-3. Requester Control Block

B-1. Message Field Length Table

C-1. Conventional CP/NET Messages

D-1. NDOS Functions

G-1. Transporter Command Block
G-2. Receive Result Block

Figures

1-1. Standard CP/NET Configuration
1-2. CP/NOS Configuration
1-3. Single Requester Networked to MP/M II Server
1-4. Multiple Requesters in Hub-star Configuration
1-5. Multidrop Network
1-6. Hybrid Network
1-7. CP/NET Memory Structure
1-8. A Simple Server Supporting Three Requesters

3-1. Message Format

4-1. Layered Model of a CP/NET Network Node
4-2. Network Status Byte Format
4-3. Algorithm for Interrupt-driven Requester Node
4-4. Server Architecture
4-5. Two-process NETWRKIF
4-6. Transport Process/Data-link Processes Interface
4-7. Directly Interfacing NETWRKIF to XIOS Routines
4-8. Synchronizing Data-link Activity Using Flags
4-9. A Typical Server Memory Map
4-10. Implementing Timeouts with Flags

B-1. CP/NET 1.2 Logical Message Format

E-1. Protocol Handshake
E-2. Binary Protocol Message Format
E-3. ASCII Protocol Message Format

Listings

2-1. A Typical CPNETLDR Execution
2-2. A Typical CPNETSTS Execution

4-1. SNIOS Jump Vector
4-2. Stack and Process Descriptor Allocation for Four-requester Server

E-1. Requester Network I/O System
E-2. Server Network I/F Module

F-1. Requester Network I/O System for ULCnet
F-2. NETWRKIF for Systems Running ULCnet
F-3. ULCnet Data-link Layer MP/M XIOS Module

G-1. Sample SNIOS for Corvus OMNINET
G-2. Sample Server Network I/F for Corvus OMNINET


Section 1
CP/NET Overview

By separating the logical operating system from the hardware environment and placing all hardware-independent code in a separate I/O module, CP/M and MP/M II have gained widespread industry acceptance. The CP/NET operating system uses this same design approach. CP/NET is network independent. The Slave Network I/O System (SNIOS) module contains all network-dependent code for the requester. The Network Interface Process (NETWRKIF) module contains all network-dependent code for the server. Logical messages passed to and from the SNIOS or NETWRKIF are transmitted over an arbitrary network between servers and requesters using an arbitrary network protocol.

CP/NET and CP/NOS can be combined in a composite network consisting of MP/M II servers, CP/M requesters, and diskless CP/NOS requesters.

CP/NET is a bridge between a microcomputer running MP/M II and a microcomputer running CP/M. The MP/M II server manages resources that are considered public to the network. The CP/NET requesters executing CP/M have access to the public resources of the server and to their own local private resources, which cannot be accessed from the network. This architecture permits the server's resources to be shared among the requesters, yet guarantees the security of the requester's resources.

The MP/M II server responds to the network asynchronously in real-time; the CP/M requesters perform sequential I/O and are usually not capable of monitoring a network interface in real-time. Figure 1-1 illustrates the relationship between CP/M, MP/M II, and CP/NET.

Figure 1-1
Figure 1-1. Standard CP/NET Configuration

CP/NOS, the second network operating system product, is designed for applications where the requester microcomputer lacks disk resources and is therefore unable to run CP/M. CP/NOS consists of

At the user level, CP/NOS provides a virtual CP/M 2.X system to the requester microcomputer. A requester microcomputer can consist of no more than a processor, memory, and an interface to the network. Thus, a CRT with sufficient RAM can execute CP/M programs, performing its computing locally and depending on the network to provide all disk, printer, and other I/O facilities. Figure 1-2 illustrates the relationship between CP/NOS, MP/M II, and CP/NET.

Figure 1-2
Figure 1-2. CP/NOS Configuration

1.1 CP/NET Features

CP/NET operates in multiple-processor environments ranging from tightly to loosely coupled to networked processors. In this manual, tightly coupled processors are those sharing at least a portion of common memory. Interprocessor messages communicate at memory speed. Loosely coupled processors do not have access to memory that is common or accessible by both processors; they communicate via a short, high-speed bus. Loosely coupled processors usually reside in the same physical box. Networked processors are usually physically separated and communicate over a serial link.

The CP/NET operating system is an upward-compatible version of CP/M 2.2, which provides system I/O facilities to requester microcomputers through a network. Additions to the Basic I/O System (BIOS) called the Slave Network I/O System (SNIOS), and a new Basic Disk Operating System (BDOS) called the Network Disk Operating System (NDOS), provide network access to System I/O facilities. The requester NDOS and NIOS are loaded and executed while running under CP/M 2.2.

In addition to the standard CP/M facilities, CP/NET provides the following capabilities:

The MP/M II server is implemented by adding some resident system processes at system generation (GENSYS) time. The resident system processes include server processes (SERVER) that perform the logical message-handling functions for the server and network interface processes (NETWRKIF) that you can customize for a particular hardware network interface.

1.2 CP/NET Configurations

CP/NET supports a number of different network topologies and a variety of system resources. The interprocessor message formats permit a requester to access more than one server for different resources.

Figure 1-3 illustrates an MP/M II system supporting a single CP/NET requester. The requester is a totally independent system, with its own console, printer, and disk resources. The requester can also access the MP/M II system's resources over the network. The MP/M II system also supports other users using local terminals.

Figure 1-3
Figure 1-3. Single Requester Networked to MP/M II Server

Figure 1-4 shows an active hub-star network running CP/NET. Each requester is networked to the server through a unique network port. The requesters have their own local resources, but they also share the server's disk and printer resources. This topology is simple to implement because you can adapt the network protocol from the protocol used for RS-232 console drivers. The sample system in Appendix E uses this topology.

Figure 1-4
Figure 1-4. Multiple Requesters in Active Hub-star Configuration

Figure 1-5 shows a system of three requesters and two servers networked together in a bus or multi-drop configuration. The network protocol must be capable of resolving conflicts when nodes attempt to use the network simultaneously. Each requester has access to the resources of both servers, in addition to its own local resources. Appendixes F and G provide examples of CP/NET systems using this network topology.

Figure 1-5
Figure 1-5. Multi-drop Network

Finally, you can combine these topologies, as well as other topologies like loops and trees, into a hybrid network topology. Figure 1-6 depicts such a topology, combining the bus, star, and loop forms.

Figure 1-6
Figure 1-6. Hybrid Network

1.3 How the Requester Works

The CP/NET requester software runs under an unmodified CP/M version 2 operating system. The requester operating system consists Of three object modules: NDOS.SPR, SNIOS.SPR, and CCP.SPR. These modules are system page relocatable files that can be loaded directly under the CP/M BDOS and BIOS, regardless of their size or their location in memory.

The module NDOS.SPR contains the Network Disk Operating System (NDOS), the logical portion of the CP/NET system. The NDOS determines whether devices referenced by CP/M function calls are local to the requester or whether they are located on a remote system across a network. If a referenced device is networked, the NDOS, prepares messages to be sent across the network, controls their transmission, and finally reformats the result received from the network into a form usable by the calling application program. NDOS.SPR is distributed in object form by Digital Research. No modification to this module is required to run CP/NET.

The Slave Network I/O System (SNIOS) is contained in the module SNIOS.SPR. The systems implementer must customize this software to run on a particular computer and network system. The SNIOS performs primitive operations that allow the NDOS to send and receive messages across a network. The SNIOS also provides a number of housekeeping and status functions to the NDOS. Digital Research distributes a number of example SNIOS modules in source form with CP/NET.

The final module, CCP.SPR, is a replacement for the normal CP/M CCP. Like the regular CCP, CCP.SPR is loaded directly below the operating system. However, CCP.SPR performs a number of special network functions that initialize the environment for a program.

The logical origin of SPR files is location zero. Each file has a 256-byte header, with locations 1 and 2 defined as the length of the code in the file. A bit map, appended to the end of the code, identifies bytes of the code that must be relocated when the code is loaded on a particular page (256-byte) boundary.

The CP/NET utility CPNETLDR relocates the bytes defined by the bit map. CPNETLDR loads SNIOS.SPR directly below the CP/M BDOS. NDOS.SPR is loaded directly below the SNIOS. CPNETLDR then passes control to an initialization routine. This routine modifies key areas of the operating system:

  1. Location 5, which contains a jump to the BDOS entry point, is saved away by the NDOS.
  2. Location 5 is then modified to jump to an entry point in the NDOS. This assures that the NDOS intercepts all CP/M function calls.
  3. The BIOS jump vector entries for console status, console in, console out, list status, list out, and warm boot are replaced with entries that jump into special NDOS routines. The NDOS saves the BIOS entry points for these routines, allowing direct BIOS calls to these routines to be intercepted in exactly the same way that CP/M function calls are intercepted.

After these modifications have been made, the NDOS calls the SNIOS to initialize the network. The NDOS then jumps to its own warm boot routine, which performs a disk system reset, loads CCP.SPR, and then passes control to the CCP.

When an application program calls the CP/NET operating system via location 5, the NDOS is entered instead of the BDOS. Invalid functions return to the user program immediately as errors. Functions dealing with console or printer I/O immediately pass through to the local BDOS; but these functions are intercepted by the NDOS again when the BDOS calls the BIOS. At this level, the NDOS checks whether the console or printer is a networked device. If so, the NDOS sends a request across the network for the input or output.

Some functions have no meaning when they are sent across the network to a remote server. Examples of these are Function 26 (Set DMA Address), Function 32 (Get/Set User Number), and Function 12 (Return Version Number). The local BDOS always handles these functions. But the NDOS saves certain parameters from these functions for its own use, processing them before allowing them through to the BDOS.

Finally, the NDOS checks most functions that deal with either the disk drive system or the file system to determine whether they reference local devices. If so, these functions pass unmodified to the BDOS. The NDOS also checks whether these functions reference devices that exist somewhere out on the network. If they do, the NDOS constructs a network message to be sent to the system on which the device exists. The network message contains the network function to be performed and the information necessary to perform it.

Figure 1-7 illustrates how the CP/NET operating system is organized. The solid line outlines the function flow of an operation on a networked disk drive. The dotted line traces the flow of an I/O operation to a networked list device or console. Arrows indicate possible function flow.

Figure 1-7
Figure 1-7. CP/NET Memory Structure

When an NDOS requester sends a function message out over the network, a response from the addressed server is implied. As soon as the NDOS has successfully called the SNIOS to send the message, the NDOS calls the corresponding message receive routine, also in the SNIOS. This procedure precludes the problem of trying to recover sequencing information from an arbitrary stream of messages.

The NDOS uses the network response to update the application program that made the function call. The NDOS then returns to the application program. If the device referenced was local, then the requester's BDOS updates the application program.

1.4 How the Server Works

Unlike the requester, the server software that runs under MP/M II does not modify the actual operating system. Rather, the operating system is a set of cooperating processes under MP/M II.

In its most basic form, each requester to be attached to a server requires two processes, communicating through two queues. One process, resident in the NETWRKIF.RSP module, performs the physical message transport task. The systems implementer must modify this process to accommodate the network's node-to-node protocol. The process's protocol must be compatible with that of the requester's SNIOS.

The NETWRKIF must be capable of monitoring one or more network lines in real-time and detecting when a requester is trying to send a message. The NETWRKIF must then receive the message, check it for data integrity, and send it on to the logical portion of the server, contained in the module SERVER.RSP. When the SERVER module returns its response to the logical message, the NETWRKIF must receive the message and then transmit it across the network back to the requester.

The module SERVER.RSP performs the logical operation the requester specifies. After receiving the message from the NETWRKIF, SERVER.RSP checks to make sure that the requester is logged in properly. Then SERVER.RSP responds to the message by performing a series of MP/M II operating system calls. Using the information returned by those calls, the SERVER constructs a response message and sends it to the NETWRKIF module for transmission.

Both the NETWRKIF and SERVER modules are Resident System Process files (RSPs). RSPs are built into the MP/M II system during its GENSYS operation. When MP/M II is cold started, all RSPs are automatically dispatched. Each RSP module might contain multiple processes, but only one process per RSP is automatically dispatched. Because each requester bound to a server might require one process from the NETWRKIF and one from the SERVER, both RSPs contain initialization code to create additional copies of themselves. These processes can be reentrant. They can share the same code, but they have separate data areas to avoid conflict between program variables.

One of the simplest server architectures is shown in Figure 1-8. Processes from the NETWRKIF are named NtwrkIP<x> where <x> is the ASCII representation of a hexadecimal number between 0 and F. SERVER processes are named SERVR<x>PR.

Figure 1-8
Figure 1-8. A Simple Server that Supports Three Requesters

A NtwrkIP<x> process writes the address of an input message to a queue named NtwrkQI<x>. A SERVR<x>PR process reads this queue while waiting for an input message. Because the queue is empty when the requester is not requesting service, the SERVR<x>PR process is suspended and consumes no CPU resources.

When the NtwrkIP<x> process writes to the queue, the SERVR<x>PR process is dispatched, and it begins to operate on the message. As soon as the NtwrkIP<x> process has finished sending the incoming message to NtwrkQI<x>, NTWRKIP<x> immediately tries to read a second queue, named NtwrkQO<x>. This queue is empty, and the NtwrkIP<x> process is consequently suspended until the SERVR<x>PR process writes the response message to it. The NtwrkIP<x> can then transmit the message back to the requester.

Server functions can be divided into four categories:

Session control functions permit a requester to log on to a server, log off, set compatibility attributes, set default passwords, and examine the server configuration table.

File serving functions make up the bulk of the server's work. These functions include opening and closing networked files, reading and writing files, and managing disk devices.

The server can operate as a print server in two different modes. If the MP/M module SPOOL.RSP is present in the system, requester outputs to a networked list device are spooled to a file for future printing. If no spooler exists in the system, the server manages the attaching and detaching of various print devices.

Finally, the NETWRKIF module can be designed to recognize a logical message that has no meaning to the SERVER module, but that can be operated on by a user-defined process. This feature allows you to use functions CP/NET does not provide.


Section 2
CP/NET User's Guide

This section describes the requester commands that enable you to access the network and use its resources. All the requester commands are actually COM files that reside on disk at the requester.

2.1 The LOGIN Command

The LOGIN command allows a requester to log in to a specified server. A requester must log in before any resources on the server can be accessed. Once a requester has logged in, it is not necessary to log in again even though the requester might power down and then power up again. A requester can only be logged off a server by an explicit LOGOFF command issued from the requester. The command takes the general form:

     LOGIN {password}{[mstrID]}

where password is an optional 8 ASCII-character password; the default password is PASSWORD. [mstrID] is an optional two-digit server processor ID; the default is [00]. The simplest form is

     A>LOGIN

2.2 The LOGOFF Command

The LOGOFF command allows a requester to log off from a specified server. Once a requester has logged off, the server cannot be accessed again until you issue a LOGIN command. The command takes the general form:

     LOGOFF {[mstrID]}

where [mstrID] is an optional two-digit server processor ID; the default is [00]. The most simple form is

     A>LOGOFF

2.3 The NETWORK Command

The NETWORK command enables a requester to assign selected I/O to the network. The NETWORK command updates the requester Configuration table. The command takes the general form:

     NETWORK {local dev}{=}{server dev{[srvrID]}}
where local devserver dev is the specification of a server device such as A:, B: ... P: in the case of a disk device or 0, 1 ... 15 in the case of CON: or LST:. A missing server dev defaults to 0 in the case of CON: or LST:. [srvrID] is an optional two-digit hexadecimal server processor ID. The default is [00]. Typical assignments are

     A>NETWORK LST:

     A>NETWORK LST:=3[07]    (list dev #3 on server 07)
     A>NETWORK CON:=2        (console #2 on dflt srvr)
     A>NETWORK B:=D:[F]      (logical B: is D: on server 0F)

Note: when networking drive A: to a server, the file CCP.SPR must reside on the networked drive, or warm boot operations fail. Do not network a device to a nonexistent or off-line server because network errors could result.

2.4 The LOCAL Command

The LOCAL command enables a requester to reassign selected I/O back to local from the network. The LOCAL command updates the requester configuration table. The command takes the general form:

     LOCAL {local dev}

where local dev is the specification of a local device such as LST:, A:,... CON:. The following are typical assignments:

     A>LOCAL LST:
     A>LOCAL B:

2.5 The ENDLIST Command

The ENDLIST command sends a hexadecimal 0FF to the list device, signaling that a list output to a networked printer is finished. If a spooler is resident on the server, the spool file is closed and enqueued for printing. If no spool file is present, the networked list device is freed for use by another requester.

Note: the CCP implements an endlist every time a program terminates, provided that CTRL-P is not active at the time. Turning CTRL-P off also causes an endlist.

     A>ENDLIST

2.6 The DSKRESET Command

The DSKRESET command functions exactly like the PRL that executes under MP/M II. DSKRESET resets the specified drive, so a disk can be changed. The command takes the general form:

     DSKRESET {drive(s)}

where drive is a list of the drive names to be reset. If any of the drives specified cannot be reset, the console displays the message:

     ***Reset Failed***

The following are typical disk resets:

     A>DSKRESET          (resets all drives)
     A>DSKRESET B:,F:    (reset drive B: and F:)

2.7 The CPNETLDR Command

The CPNETLDR command loads the requester CP/NET system. Specifically, the SNIOS.SPR file loads and relocates directly below the CP/M BDOS. The NDOS. SPR file loads and relocates directly below the SNIOS.

From that point on, the BIOS, BDOS, SNIOS, and NDOS remain resident in memory. The CPNETLDR requires no user customization. CPNETLDR displays an error message when loader errors are encountered. Listing 2-1 is a typical CPNETLDR execution.

     A>CPNETLDR
     CP/NET 1.2 Loader
     ---------------
     BIOS         F600H 0A00H
     BDOS         E800H 0E00H
     SNIOS   SPR  E500H 0300H
     NDOS    SPR  DB00H 0A00H
     TPA          0000H DB00H

     CP/NET 1.2 loading complete.
     <Warm Boot>
     A>

Listing 2-1. A Typical CPNETLDR Execution

2.8 The CPNETSTS Command

The CPNETSTS command displays the requester configuration table. The requester configuration table indicates the status of each logical device that is either local or assigned to a specific server on the network. Listing 2-2 shows a typical CPNETSTS execution.

     A>cpnetsts

     CP/NET 1.2 Status

     Requester processor ID = 34H
     Network Status Byte = 10H
     Disk device status:
        Drive A: = LOCAL
        Drive B: = LOCAL
        Drive C: = Drive A: on Network Server ID = 00H
        Drive D: = Drive B: on Network Server ID = 00H
        Drive E: = LOCAL
        Drive F: = LOCAL
        Drive G: = LOCAL
        Drive H: = LOCAL
        Drive I: = LOCAL
        Drive J: = LOCAL
        Drive K: = LOCAL
        Drive L: = LOCAL
        Drive M: = LOCAL
        Drive N: = LOCAL
        Drive O: = LOCAL
        Drive P: = LOCAL
     Console Device = LOCAL
     List Device = List #0 on Network Server ID 00H
     A>

Listing 2-2. A Typical CPNETSTS Execution

2.9 CTRL-P

A CTRL-P causes console output to be echoed to the list device until the next CTRL-P. The messages

     CTL-P ON

and

     CTL-P OFF

are displayed at the console. When the requester list device has been networked, the local system uses the server printer. The second CTRL-P causes a hexadedimal FF to be sent to the server, causing the server to close and print the spool file.

Note: when the requester uses the server printer with a CTRL-P active, the requester must issue a second CTRL-P to cause the server to close the spooled file and begin printing it. When the requester is using the server printer and has invoked it with a program such as PIP, the warm boot at program termination causes the required endlist character to be sent to the server to close and print the spooled file.

The program ENDLIST is not needed to terminate network list output in these situations.

2.10 The MAIL Utility

The MAIL utility allows you to send, receive, and manage electronic mail in a network environment. MAIL operates using file based function calls, so special processing by the server is not required. MAIL runs transparently on either server or requester, so only one program is required throughout the entire electronic mail system.

MAIL allows you to send messages to a single node, broadcast messages to all nodes currently logged in, or receive messages.

Messages are stored for your future examination on the temporary file drives of CP/NET servers. A user's mail file is named

     xxMAIL.TEX

where xx corresponds to your node ID. For example, if requester #5C wants his mail, the MAIL program accesses files named 5CMAIL.TEX on the temporary file drives of all the servers that node 5C currently has logged in. Every server in the CP/NET system might have one of these files, so other nodes in the network that do not have direct access to all of node 5C's servers can still send messages indirectly to it.

Menu-driven operation allows you to run the program with a minimum of instruction. Messages are limited in size to 1.7K bytes. You can enter messages into the system directly from the keyboard or through a preedited file. Options allow you to answer a message immediately while reading your mail and to delete unwanted entries.

2.10.1 Menus

Three basic menus can appear during a MAIL session:

The Main Menu determines the basic operation to be performed. The Input Source Menu specifies whether input comes from a file or whether you enter it directly. Finally, the Receive Response Menu determines the disposition of messages you receive.

Enter a menu selection by typing the number associated with the selection, followed by a carriage return. If you type an invalid character or no character at all, the menu system defaults to the last item on the menu. You simply press the carriage return for common operations.

Main Mail Menu

The main mail menu appears when you enter the mail program and when any of its options have completed execution. Main mail menu options are

     1 - Broadcast
     2 - Send Mail
     3 - Receive Mail
     4 - Exit Program

A simple carriage return or an invalid entry at this level return you to CP/M or MP/M II command level.

Input Source Menu

The input source menu allows you to specify how message input is entered into the system. The input source menu has only two options:

     1 - File
     2 - Console Input

Receive Response Menu

The receive response menu determines the disposition of messages once the user has examined them. The options are

     1 - Stop Receiving Mail
     2 - Answer Message
     3 - Delete Message From Mail File
     4 - Answer Message, Then Delete
     5 - Re-Examine Last Message
     6 - Get Next Message

2.10.2 Data Entry

In addition to the menus, MAIL prompts you for a variety of inputs. These inputs determine the destination of messages, input files, and subjects.

Destination ID Prompt

When using the send mail option, MAIL requires an explicit destination to deliver the message properly. The system prompts for the destination. The legal value is a 2-digit hexadecimal number, followed by a carriage return. This value corresponds to a CP/NET server or requester ID value.

If you enter a value that is not a legal hexadecimal number, the system displays an error message, and prompts you again. The system does not check, however, to determine whether a requester or server with this ID exists on the network.

Subject Prompt

With both the broadcast and send mail options, MAIL prompts for a subject header. This header is displayed as the title of the message and is also used for answering mail to the message that is sent.

When the system prompts for subject, you can enter a subject header from 0 to 80 bytes long, followed by a carriage return.

Input File Prompt

If a preedited file contains the text of a message, MAIL prompts for the filename. You can then enter a valid CP/M file specification. If the file specified does not exist, the system displays an OPEN ERROR, and the program aborts.

Console Input Prompt

If you choose to enter a message directly from the console, MAIL prompts for input. You can then simply type the message. Individual message lines can be up to 78 characters long. A message, whether input from the console or from a file, must be no longer than 1764 characters, about enough to fill a standard terminal display. Longer messages are truncated.

To terminate input, the user presses CTRL-Z, followed by a carriage return.

2.10.3 MAIL Options

This section explains how the CP/NET system gathers and receives mail and how you control the disposition of mail.

Broadcast

The broadcast option sends a message to every node that it can find logged in to the CP/NET system.

MAIL works differently when it is running on a server under MP/M II, from the way it works when it is running on a requester under CP/M or CP/NOS. If a requester is broadcasting, MAIL sends the specified message to every server on which it is logged in as well as to every other requester logged in to those servers. If a server is broadcasting, MAIL sends the message only to every requester logged in to that server. A server has no means of initiating transactions with other servers, although it can use its own local MP/M II system to file mail for its own requesters.

A message cannot be broadcast to the broadcasting node.

To send a message to a given server and its associated requesters, MAIL must reference that server's temporary file drive across the network. If a requester has not networked the temporary file drive of a server, no messages are sent to that server.

When the broadcast option is entered, MAIL prompts you for a subject and message. When the operation is completed, it returns to the main menu.

Send Mail

The send mail option sends a message to a specific node in the CP/NET system. The destination can be either a server or a requester. If the option is running on a requester, it first searches the network to see if the node specified is logged in. If the option finds the node is logged in, it sends the message. if the option does not find the node, it leaves the message on the first server located when MAIL searches the local configuration table. If a destination requester logs in later, its mail will be waiting for it. Mail files can accumulate that were erroneously sent to nonexistent requesters or to servers that the requester sending the message had not logged onto when it sent the message.

If the option is running on a server, mail is left on that server, whether the node it is being sent to is logged in or not.

Upon selecting the send mail option, MAIL prompts you for a destination ID, a subject, and for the message itself. MAIL then attempts to send the message. If MAIL cannot find a server with a temporary file drive to accept the message, the error NO SERVER MAIL DRIVE NETWORKED is displayed, and the program aborts.

Receive Mail

The receive mail option permits you to examine messages left for you on all the servers on which you are currently logged in. After each message is displayed, you are presented with a number of message-handling options.

If you are running MAIL on the server, only the mail file on the server is accessed. However, if MAIL is being run on a requester, each server to which the requester is logged in is searched for messages.

Each message is preceded by a header that tells you what node the message came from and the subject of the message. The actual message is then displayed. As a message is being displayed, you can halt the display by pressing CTRL-S and resume display by pressing CTRL-Q. At the end of the message, bring up the receive response menu by pressing any key. You can then take one of the options listed in Table 2-1.

Table 2-1. Receive Mail Message-handling Options
OptionExplanation
Stop receiving mailMAIL stops searching for more entries or additional files and returns to the main menu.
Answer messageMAIL prompts you to type in a reply message. The reply message is sent back to the sender of the original message. The subject of the reply message is the characters "RE: ", followed by the original subject.
Delete messageMAIL flags the message in the file as deleted. At the end of each file, or if you decide to stop receiving mail, deleted messages are physically removed from the file.
Answer, then deleteThis option answers the message message just displayed, then deletes the message.
Display next messageMessages continue to be displayed in this fashion, allowing the user to respond to each one, until no more can be found. The message "No More Messages" is then displayed, and the program returns to the main menu.

Upon completion of any message-handling options, with the exception of the reexamine option, the next message is displayed.

2.10.4 Error Messages

In addition to the error messages already mentioned, CP/NET returns file system errors. These errors display

     ERROR READING FILE
     ERROR WRITING FILE
     or
     ERROR OPENING FILE

followed by a filename. After displaying such an error, MAIL aborts.

It is possible to get the ERROR OPENING FILE message by specifying a nonexistent input file for sending or broadcasting a message. Almost all other instances of the messages, however, indicate possibly serious trouble with the network, the server file system, or the mail-handling system.


Section 3
CP/NET Programmer's Guide

This section provides information for the applications programmer who wants to write programs to run under CP/NET or to evaluate the performance and correctness of programs written for CP/M or MP/M II under the CP/NET operating system.

MP/M II performs all operations on a networked device and makes file security checks that CP/M does not usually make. Because MP/M was designed to run unmodified CP/M applications, these checks seldom prevent the use of a CP/M application under CP/NET.

3.1 CP/NET Interprocessor Message Format

The simple message format that CP/NET uses for interprocessor communication includes packaging overhead and the message itself. The packaging overhead is a header consisting of a message format code, a CP/NET destination address, a CP/NET source address, a CP/M function code, and a message size. The actual CP/NET message follows the header.

3.1.1 Message Format Code

The message format code is a single byte that specifies the format of the message itself. Digital Research reserves message formats 0-127 for general interprocessor message format codes and future use. The general interprocessor format codes follow the message format shown below, but differ in length of the individual fields. (See Appendix B.)

The odd-numbered format codes are for response messages sent back from servers to requesters. Thus, a CP/M disk read function sent from a requester to a server has a message format code of 0, and the return code sent back from the server to the requester has a message format code of 1.

Implement the general interprocessor message formats 0 and 1 as shown in Appendix A because these formats promote standardization among microcomputers from different vendors.

3.1.2 Message Destination Processor ID

The message destination processor ID field is one byte long. Destination IDs can be in the range O-0FE hex. An ID of 0FF is illegal. Many CP/NET utilities use a server destination of 0 as a default. For this reason, assign the most commonly used network server a node ID of 0.

3.1.3 Message Source Processor ID

The message source processor ID field is usually one byte long. The node sending the message always fills this field with its own ID. Valid source IDs range from 0 to 0FE hex. An ID of 0FF is illegal.

3.1.4 CP/M Function Code

The CP/M function code field is one byte long. The size of the message data field depends on the CP/M function. Each CP/M function has a specific number of bytes to be sent to the server and a specific number of bytes to be returned to the requester. Appendix C provides the logical message specification for each of the CP/M functions. Some of the CP/M function codes have no equivalent network function.

3.1.5 Size

The size field is one byte long. The size value has a bias of 1. Thus, a size of 0 specifies an actual size of 1, while a size of 255 specifies an actual size of 256. With a 1-byte size field, the minimum data field is 1 byte, and the maximum is 256.

3.1.6 CP/NET Message

The CP/NET message consists of binary data and is from 0 to 256 bytes long. The meaning of the message depends on the format, function, and size specified by the header.

3.1.7 Additional Packaging Overhead

Some networks might have to modify the standard CP/NET message to transmit it over the physical network medium, route it to the proper destination, and ensure its integrity.

For example, the message format shown in Figure 3-1 contains no cyclic redundancy code (CRC) or any other error checking as a part of the packaging overhead. The user-written SNIOS can add the error checking when it places the message onto the network, and then test the message when the SNIOS receives a message from the network. This function is intentionally left to the user, avoiding redundant error checking where standard interface protocols, both in software and hardware, might already provide error checking.

The NDOS always constructs messages using format 0. Likewise, the server processes always expect to receive messages in format 0. The server sends its response in format 1, which the NDOS requires to interpret the response. If the SNIOS and NETWRKIF must communicate using a different format, they must convert all received messages back into the standard formats 0 and 1.

+-----+-----+-----+-----+-----+-----      ------+
| FMT | DID | SID | FNC | SIZ | MSG  ...        |
+-----+-----+-----+-----+-----+-----      ------+
   ^     ^     ^     ^     ^      ^
   |     |     |     |     |      |
   |     |     |     |     |      +-- Message of length SIZ+1 bytes
   |     |     |     |     +-- Size of message = message length - 1
   |     |     |     +-- CP/NET Function Code
   |     |     +-- Message Source ID
   |     +-- Message Destination ID
   +-- Format Code
Figure 3-1. Message Format

3.2 Running Applications Transparently under CP/NET

Applications that use local devices under CP/NET use the CP/M 2.2 BDOS file system. Applications that use networked devices use he MP/M II file system. These operating systems are largely compatible with each other, so applications written to run under CP/M should run across the network with no changes.

But there are some differences between the two file systems:

Differences between the CP/M 2.2 BDOS and MP/M II file systems are more fully described in the following sections.

3.2.1 MP/M II vs. CP/M File Systems

MP/M II is a real-time, multitasking operating system. To function properly, MP/M II requires a file system capable of sharing files among multiple processes and resolving access conflicts among those processes. In contrast, CP/M is a single-task operating system, so no such conflicts can arise.

One of MP/M II's key methods for maintaining file system integrity is the File Control Block checksum. The FCB checksum takes into account the process controlling the FCB, the physical blocks allocated to the file, whether the file is open in a mode that allows other processes to share it, and other factors. When file-related functions are submitted to MP/M II, the checksum is examined. If the checksum is found to be invalid, MP/M II returns an error to the calling process.

MP/M II also returns an error if

Because a single process handles all CP/NET activity on a server all of these limitations apply to a CP/NET requester performing file operations on a remote device. These limitations, however, do not apply to a requester accessing a local device. The systems implementer should take these factors into account when designing servers for a CP/NET system.

3.2.2 Error Handling Under CP/NET

Most CP/NET function calls result in specific values returned in the CPU registers. These values can be pointers to data objects, bit vectors specifying drive status, directory codes, or success or error conditions. Directory, success, and error codes are returned in register A. Pointers and bit vectors are returned in register HL. Register A is always equal to register L, and register B is equal to register H for all CP/NET return codes.

Error Handling for Local Devices

When a CP/NET requester performs a local file operation, the function parameters pass untouched to the CP/M BDOS. The BDOS checks those parameters for validity and calls the BIOS to perform physical I/O functions. Two types of errors can arise from these local operations.

The BDOS can detect certain logical problems with a file function and return a logical error. If it does, an error code is returned in register A, but the calling application program is allowed to continue.

A physical error is returned when the BIOS is unable to successfully perform a physical operation requested by the BDOS. When the BDOS is presented with a physical error, it prints the following message on the console:

     BDOS Err on <x>:
     <error message>

where <x> is the drive referenced when the error occurred, and <error message> is one of the four following errors:

After the physical error message is printed, the BDOS waits for the user to respond to the error with one of two actions. Pressing CTRL-C causes the BDOS to perform a warm boot, aborting the program. Pressing any other key causes the BDOS to ignore the physical error and continue as if it had not occurred.

For a more complete discussion of CP/M 2.x errors, see the CP/M Operating System Manual, published by Digital Research.

Error Handling for Network Devices

When an application references a networked device, the MP/M II server performs the actual file operation and returns a message defining whether the operation was successful or not. Unlike the local case, the requester has only indirect knowledge of any error status. Direct physical error indications are impossible to obtain because a requester has no contact with the MP/M II XIOS. Instead, if an error occurs, MP/M II returns a message indicating that an error occurred and the type of error it was.

When referencing a remote device, the two types of errors possible under CP/NET are logical errors and extended errors.

Like logical errors under local CP/M, logical network errors define nonfatal error conditions, such as reading past the end of a file or attempting to open a nonexistent file. Some serious error conditions are returned as logical errors for functions that expect to process their own errors. These functions are

20Read Sequential
21Write Sequential
33Read Random
34Write Random
40Write Random with Zero Fill
42Lock Record
43Unlock Record

Errors for these functions are returned in the return code field of a CP/NET message. The NDOS formats this field into register A, so the condition code upon return to the application program looks exactly as it does under local CP/M.

Some of the following codes can be returned in register A for each of the preceding functions:

00Function Successful
01Reading Unwritten Data or No Directory Space Available
02No Available Data Block (Disk Full)
03Cannot Close Current Extent
04Seek to Unwritten Extent
05No Directory Space Available
06Random Record Greater than 3FFFF
08Record Locked by Another Process
09Invalid FCB
0AFCB Checksum Error
0BFile Verify Error
0CRecord Lock Limit Exceeded
0DInvalid File ID
0ENo Room in System Lock List

Extended errors indicate that a potentially fatal condition has occurred during the execution of an MP/M II function. The condition can be a physical error, similar to the physical errors that can occur under CP/M. Or the condition can be an error produced by the file system, indicating that the specified operation violates the integrity of the file system.

When an extended error occurs under MP/M II, the default mode of operation displays the extended error message on the console attached to the calling process, and the process aborts, MP/M II provides, however, for returning extended errors to the calling process without aborting that process. In this return error mode, register A is set to FF hexadecimal, and register H contains the extended error code.

The CP/NET server uses return error mode because if the server aborted, it could not communicate further with the requester it was servicing until MP/M II was restarted. When the server detects an extended error, it constructs a special CP/NET message. The message is two bytes long, with the first byte (the return code) set to FF. The second byte is set to the extended error code.

When the requester detects one of these special messages, it checks the error mode set by the application program with Function 45 (Set BDOS Error Mode). There are three possible modes:

If the NDOS is in default mode, it prints the following error message:

     NDOS Err <xx>, Func <yy>

where <xx> is the extended error code in hexadecimal, and <yy> is the function being performed when the error occurred, also in hexadecimal. The NDOS then performs a warm boot, aborting the program.

In return error mode, the NDOS does not display a message or abort the program. Instead, the NDOS sets register A to FF and register H to the extended error code; then it returns to the application program.

If an extended error is detected in return and display error mode, the NDOS displays the error message on the console. But the NDOS does not abort the program, setting the registers in the same manner as return error mode.

Function 45 (Set BDOS Error Mode) does not exist under CP/M. Because of this, most CP/M applications automatically run in default mode. If an extended error occurs, these applications abort.

The following extended error codes can be returned to the NDOS:

01Bad Sector--Permanent Disk Error
02Read-Only Disk
03Read-Only File
04Drive Select Error
05File Open by Another Process in Locked Mode
06Close Checksum Error
07Password Error
08File Already Exists
09Illegal ? in an FCB
0AOpen File Limit Exceeded
0BNo Room in System Lock List
0CRequester not Logged on to Server or Function Not Implemented on Server
FFUnspecified Physical Error

Extended error 0C hex is returned, not by MP/M II, but by the server itself. This error indicates that the server is unable to process an otherwise valid CP/NET message, either because the requester is not logged in to that server or because the function code contained in the message is invalid.

Extended error FF can result only from two special functions, Get Allocation Vector Address and Get Disk Parameter Address. Because these functions return a pointer in register pair HL, it is not possible to detect a regular extended error. Instead, these functions return an FFFF value in HL if a physical error occurs. The NDOS ensures that the address returned for these functions (including Get Server Configuration) never return an address with FF in the low byte, so if they return with A (or L) = 0FFH then the caller should assume an error.

Not all CP/NET functions are capable of returning extended errors. However, extended error 0C can be returned on any function, even on MP/M II functions that normally have no extended error associated with them. If an extended error is returned for such a function, the NDOS ignores it. The following functions can result in the performance of a network access but cannot produce an extended error:

1Console Input
2Console Output
5List Output
9Print String
10Read Console Buffer
24Return Login Vector
28Write Protect Disk
29Get Read-Only Vector
37Reset Drive
39Free Drive
64Login
66Send Message on Network
67Receive Message on Network
70Set Compatibility Attributes
106Set Default Password

Any other function can cause a program to abort if an MP/M II extended error occurs, if an unsupported function is passed to the server, or if the server is not logged in.

3.2.3 Temporary Filename Translation

Many common application programs use temporary files. The names of these files often have the form FILENAME.$$$ or $$$.SUB. When multiple copies of these applications run on different requesters logged on to the same server, a number of these temporary files can have the same name, causing extended MP/M II errors that abort the application program.

To solve this problem, each requester's NDOS recognizes temporary filenames destined for networked drives and implicitly renames them, so the filename an application presents to the operating system is not the one the NDOS presents to the MP/M II file system.

Each occurrence of the string $$$ in the first three bytes of a filename, as well as any filetype of $$$, forms a CP/NET message with a filename or filetype of $<xx>, where <xx> is the ASCII representation of the requester ID byte. Because all requesters have a unique ID, this modification guarantees the uniqueness of temporary filenames.

This modification is transparent to the calling application program. When the NDOS modifies a filename in a CP/NET message, it converts the filename back to its original form before updating the application's FCB. The only possible change to the FCB is that interface attributes set in the high-order bits of the filename strings modified are reset. This change poses no problems if temporary files are truly temporary. Treat temporary files like Read-Write files with the DIR attribute; delete them before the application program terminates.

Functions 17 (Search For First Directory Entry) and 18 (Search For Next Directory Entry) do not perform temporary filename translation when referencing a networked drive. If a user creates file with a temporary filename and then attempts to locate it within his directory, this can be confusing.

For example, suppose that a user working on requester 5A enters the command:

     REN $$$.$$$=BLAH.TMP

Then the user enters a DIR command. The file previously renamed appears as

     $5A.$5A

in the directory.

If a temporary file is referenced on a drive that is local to the CP/NET system, the filename passes unmodified to the BDOS. -No conversion is necessary, because there is no possibility of conflict.

3.2.4 Opening System Files on User 0

Under MP/M II, a requester running in a user number other than 0 can access certain networked files in user 0. If an MP/M II file has its t2' interface attribute set, the file is a system file. If a networked file is opened in locked or Read-Only mode from a nonzero user number, the following actions are taken:

The user of a CP/NET requester can make convenient use of these options. Because the CCP.SPR always opens files in Read-Only mode, all COM files can be placed in user 0 and marked as system files, making them accessible to all user numbers.

Because this facility does not exist under CP/M 2.x, all COM files on local devices must exist within the user numbers from which they are to be executed.

3.2.5 Compatibility Attributes

Because of MP/M II's added file security, applications written under CP/M might not work properly under MP/M II. Two basic factors contribute to the incompatibility. The first is the FCB checksum computation that MP/M II performs on open FCBs. Certain CP/M applications modify their FCBs in a way that makes their checksums invalid. Second, MP/M II defaults to opening all files in locked mode, allowing only one process to have a file open at a time. Although files can be opened in an unlocked or shared mode, an application must explicitly specify that the file is to be opened unlocked. CP/M applications have no knowledge of this procedure.

To enable CP/M applications to run unmodified under MP/M II, a system of compatibility attributes has been added. This feature is supported under CP/NET. Using compatibility attributes, a user can selectively disable parts of the MP/M II file security mechanism.

When a requester's CCP opens a COM file for loading and subsequent execution, it examines the high-order bits of the first, second, third, and fourth bytes of the filename. These bits are referred to as interface attributes Fl', F2', F3', and F4'. The CCP constructs a byte based on the interface attribute set. It then uses this byte as a parameter for Function 70 (Set Compatibility Attributes). Function 70 causes the NDOS to send a logical compatibility attribute message to every server of which it has knowledge.

Table 3-1 defines the interface attributes.

Table 3-1. Interface Attributes
AttributeMeaning
Fl'causes MP/M II to behave as though all files were opened in Read-Only mode, although write accesses are still permitted. F1' is functionally equivalent to opening a file in unlocked mode, except that record locking is not possible. Using this attribute, two programs can update the same record simultaneously, leaving the file in an indeterminate state.
F2'causes all file close operations to convert to partial close operations. A partial close uses the current FCB to update the directory but permits the application program to continue using the file without reopening it.
F3'disables FCB checksum verification during close operations. Files are closed successfully as long as MP/M II can tell the file was initially opened and still has an item on the system lock list. If the file was not opened, an error is still returned.
F4'disables all FCB checksum verification. F4' implicitly sets attributes F2' and F3' as well. Use this attribute with extreme caution because it is possible to perform valid file operations using corrupt FCBs. Doing this could result in serious damage to the files on the disk drive being referenced.

The CCP uses the interface attributes to construct a one-byte parameter for the set compatibility attributes call by setting the following bits:

All other bits are set to zero.

The set compatibility attributes logical message causes the server to change its process descriptor if the user has enabled compatibility attributes during the MP/M II GENSYS operation. Otherwise, the message is ignored.

When an application program terminates, the CCP resets all compatibility attributes. This prevents a subsequent program from operating in an environment with insufficient file security.

It is advisable to enable the minimum number of compatibility attributes necessary to allow a program to run properly. Use the following guidelines for setting the attributes:

You can use the SET utility under MP/M II to enter compatibility interface attributes into a .COM file's directory entry from an MP/M II console. For example,

     SET <filespec> [Fl=ON,F3=ON]

If you cannot use MP/M II, you can set the interface attributes under program control using Function 30 (Set File Attributes).

3.2.6 Password Protection Under CP/NET

The MP/M II file system limits file access by unprivileged users through password protection for individual files. There are three levels of password protection for files:

Use the SET utility to assign passwords under MP/M II. The procedure for assigning passwords is described in the MP/M II Operating System User' s Guide. CP/NET does not support the assignment of passwords across the network.

CP/NET does, however, allow an application program to send a Password across the network when a file is opened. This allows a user on a CP/NET requester the most basic form of password support: operation on networked files that have been previously password protected.

If a read-protected file is opened and no password is specified, an extended error is returned across the network, and the Calling application aborts. The same error is also returned when an application attempts to write to a write-protected file for which no password was provided when the file was opened. Finally, any attempt to delete, rename, or change the attributes of a delete protected file without providing a password results in an extended error.

CP/NET also supports Function 106 (Set Default Password). Function 106 provides a password against which all protected files are checked if no password is provided or if the password is incorrect. This function can relieve an application of the responsibility to parse passwords constantly into the first eight bytes of the current DMA buffer.

CCP.SPR does not support MP/M II's facility of supplying passwords when the user enters a command line. Because of this, do not password-protect COM files unless a default password utility is provided to the user.

Because CP/M 2.x does not support any kind of file protection, passwords are ignored when referencing files on drives local to a CP/NET requester.

3.2.7 Networked List and Console Devices Under CP/NET

In addition to the 16 disk devices, CP/NET allows the user to map the list and console devices across the network. A number of requesters can share a printer, or a console can be logically attached to a completely independent system running CP/NET or CP/NOS. Such a system needs only a network interface to support full CP/M capability.

Unlike most requester BDOS calls, whether a console or list device is local or networked is determined, not at the BDOS intercept level, but at the BIOS-intercept level. This feature enables application programs to make direct BIOS calls for console and printer I/O and to continue to run transparently across the network.

List device I/O is handled in the following manner: when the BIOS call is made to LISTOUT, the NDOS traps it. The NDOS examines the configuration table to determine whether the list device is local to the CP/NET system or networked. If the list device is local, the call is passed through to the BIOS unchanged.

If the list device is networked, however, the NDOS stores the character to be listed in a special buffer, located directly below the requester configuration table. When 128 characters are stored, the NDOS sends a List Output logical message to the server upon which the list device is mapped. This buffering process improves system performance because one-character messages that would congest the network communication interfaces need not be sent between each requester and server.

Under CP/M, there is no need to tell the list device when a listing is complete because only one application can list at a time, and that application has complete control of the device during that time. Under CP/NET, however, more than one requester can share a printer. So a mechanism must be included to notify the server that a listing is done and that the list device is available to other requesters.

A special provision must be included so a partially filled list buffer can be flushed to the server when a listing is finished, and so the server can release the list device. Endlist, a special character equal to FF hex, is intercepted by the NDOS as the signal to terminate a listing.

The endlist character can come from one of four sources:

  1. The CCP.SPR sends an endlist character every time it is entered and detects that a list is in progress. This causes an endlist every time a program terminates.
  2. An application can issue an endlist to terminate its own listing.
  3. Every time a CTRL-P is toggled to off, the NDOS console input routine detects this and issues its own endlist.
  4. You can use the ENDLIST utility to terminate the listing.

The server can handle listing in two different modes. If the module SPOOL.RSP is present in MP/M II, the server takes all list output messages and writes them to a dedicated spooler file. When the server detects an endlist, it inserts a CTRL-Z end-of-file character into the message, closes the spooler file, and directs the SPOOL process to begin printing the file on the appropriate list device

If a SPOOL process is not resident under MP/M II, the server, upon receiving an initial list out message, performs an explicit attach list function on the specified list device. This prevents other requesters from using the list device until the requester being serviced is finished listing. All other requesters are suspended or receive network errors if they try to use the same list device. When the server finally receives the endlist character, it issues a detach list function, freeing the list device for another process.

Both server modes have potential disadvantages. A printer that uses a CTRL-Z as an escape sequence for special printing functions cannot be used with the SPOOL.RSP. Using CTRL-Z causes the spooler to terminate a print job prematurely, assuming that an end-of-file was encountered. On the other hand, explicit attaching and detaching of list devices can cause a network error if a requester attempts to attach a list device that is already in use, has its server become suspended, and eventually times out.

Console I/O cannot be buffered and sent across the network in large blocks because it is not possible to determine when input critical to the operation of an application is needed. The NDOS must therefore send such I/O across the network one character at a time.

As with list output, the NDOS traps console-related BIOS calls. The NDOS determines whether the console is local or networked. If the console is local, no action is taken, and the local BIOS is entered. If the console is networked, a raw or unfiltered console I/O message is sent to the server. The server performs the I/O function and sends a response back to the requester.

If a networked console is used with CP/NET, the system behaves unreliably when the console is also being used as a regular MP/M II terminal because MP/M II allocates a Terminal Message Process (TMP) to each known user console. Both a server process and a TMP can be waiting for input from the same console. Because of this, typed characters can be echoed normally, doubly echoed, or not echoed at all. The actual processes might or might not receive every character.

A networked console user should also be aware that, because each character must be sent over the network, networked consoles drastically degrade the performance of the entire CP/NET system. Networked consoles are not recommended unless there is no way to support a local console, as in certain industrial process-control applications.

The CTRL-P facility of CP/M is partially handled by the NDOS. The NDOS must know when CTRL-P is active because it must send an endlist character when the facility terminates. If the CCP detects that CTRL-P is active, it will not send an endlist, even if a program terminates.

3.3 CP/NET Function Extensions to CP/M

Applications accessing networked drives use the MP/M II file system to perform file operations. Many of those operations have slightly different meanings than they do under CP/M. For example, by setting the high-order bits of an FCB filename, a file can be opened or made in locked mode, unlocked mode, or Read-Only mode. CP/NET also allows an application to place a password in the current DMA buffer for opening password-protected files. Similarly, a close operation can perform either a permanent close or a partial close.

The return codes and side-effects of MP/M II functions also differ. Error-handling differences are discussed in Section 3.2.2. The open and make functions also differ. These functions return a two-byte value, called the file ID, in the random record field of the opened FCB. The file ID is necessary for performing record locking functions.

For a complete description of how individual CP/M functions work under MP/M II, see the MP/M II Operating System Programmer's Guide.

This section describes CP/NET functions that have no counterpart under CP/M. These include MP/M II functions that do not exist under CP/M, as well as a set of dedicated CP/NET functions. All of these functions adhere to exactly the same calling conventions as the rest of CP/M and all follow the same conventions regarding return codes.

FUNCTION 38: ACCESS DRIVE
Prevents Drives from Being Reset
 RegisterValue
Entry ParametersC26H
DEDrive Vector
Return ValuesAReturn Code
HExtended Error

The Access Drive function inserts a dummy open file item in the system lock list for each drive specified in the drive vector. The drive vector is a 16-bit vector in which each possible drive is presented. Bit 0 represents drive A:, bit 1, drive B:, continuing through 15 for drive P:.

The NDOS separates the drive vector into a number of drive vectors, one per server that the NDOS can find in the requester's configuration table. The NDOS then sends a logical message to each of these servers. If any of these messages result in an extended error, the function aborts.

If a server's system lock list does not have enough room to fit all the dummy items for all the drives specified, or if the open file limit for the server process is exceeded, none of the items is inserted and Function 38 returns an extended error.

Because the NDOS sends messages to each server in sequence, an extended error on one server does not indicate that servers accessed previously failed to insert open file items. This differs from MP/M II, where only one file system controls the entire lock list. Note that drives might have to be freed after a failure resulting from an access drive call.

If the NDOS is in return error mode, an error condition on function 38 causes register A to be set to 0FFH, and register H contains one of the following codes:

0AOpen File Limit Exceeded
0BNo Room in the System Lock List
0CServer Not Logged In

Because Function 38 is meaningless to local drives under CP/NET, no call to the local BDOS is made.

FUNCTION 39: FREE DRIVE
Free Specified Disk Drives
 RegisterValue
Entry ParametersC27H
DEDrive Vector

The Free Drive function purges servers' lock lists of all items pertaining to the drives specified. The drive vector is a 16-bit vector in which each possible drive is represented. Bit 0 represents drive A:, bit 1, drive B:, continuing through 15 for drive P:.

Because dummy drive accesses, locked records, and open files are all purged, close all important files before issuing the free drive call. Otherwise, a checksum error is returned on the next file access, and data might be lost.

The CP/NET CCP issues a free drive every time a program terminates. This prevents the server process associated with the requester from becoming clogged with useless files.

Because Free Drive is meaningless under CP/M, the operating system ignores entries in the drive vector that specify drives local to the requester.

Free Drive has no error return.

FUNCTION 42: LOCK RECORD
Lock Records in a File
 RegisterValue
Entry ParametersC2AH
DEFCB Address
Return ValuesAReturn Code
HExtended Error

The Lock Record function grants a requester exclusive write access to a specific record of a file opened in unlocked mode. Using this function, any number of requester processes can simultaneously update a common file.

To lock a record, a requester application must place the logical record number to be locked in the random record field of the file's FCB. The file ID number, a two-byte value that is returned in the random record field when a file is opened in unlocked mode, must be placed in the first two bytes of the current DMA buffer. When the lock function is called, a pointer to the FCB must exist in register pair DE.

The record to be locked must reside within a block currently allocated for the file. The lock fails if the record is locked by another process or requester. This prevents two processes from simultaneously updating the same record and leaving it in an indeterminate state.

If a file was opened in locked mode, the Lock Record function always returns successfully, but no explicit action is taken because the whole file is locked in the first place.

To use the Lock Record function, follow these steps:

  1. Open the file in unlocked mode. Save the file ID returned in the random record field of the open FCB.
  2. When the application needs to update the record, lock the record, even before attempting to read it. Reading a record that is locked by another process can result in leaving the record in an indeterminate state. If an error results because the record is locked by another process, repeat this step until the record is locked successfully. Place a timeout value on retrying the lock in case another requester has locked the record and then gone off line.
  3. Read the record.
  4. Update the record.
  5. Write the record back.
  6. Unlock the record.

The Lock Record function returns a 0 in register A if successful. Otherwise, the Lock Record function returns one of the following error codes in register A:

01Reading Unwritten Data
03Cannot Close Current Extent to Access Extent Specified
04Seek to an Unwritten Extent
06Random Record Number Greater than 3FFFF
08Record Locked by Another Process
0AFCB Checksum Error
0BUnlock File Verification Error
0CProcess Record Lock Limit Exceeded
0DInvalid File ID in the DMA Buffer
0ENo Room on the System Lock List
FFExtended Error

These extended errors can occur:

01Permanent Error
04Select Error
0CRequester Not Logged In to Server

The Lock Record function has no meaning when a drive local to the requester is referenced. The function returns with register A set to 0.

FUNCTION 43: UNLOCK RECORD
Unlock Records in a File
 RegisterValue
Entry ParametersC2BH
DEFCB Address
Return ValuesAReturn Code
HExtended Error

The Unlock Record function releases a previously locked record, allowing it to be locked and written to by another requester. The record to be unlocked must be placed in the random record field of the file's FCB. The file ID is a two-byte value that is returned in the random field when a file is opened in unlocked mode. The file ID must be placed in the first two bytes of the current DMA buffer. Register pair DE must contain a pointer to the FCB.

The Unlock Record function returns successfully if

In all these cases, no action is performed.

Do not unlock a record until the requester's application program has finished updating the locked record and has written it back out to the file. Otherwise, another process might inadvertently destroy the updated information.

The Unlock Record function returns a 0 in register A if Successful. Otherwise, the function returns one of the following error codes in register A:

01Reading Unwritten Data
03Cannot Close Current Extent to Access Extent Specified
04Seek to an Unwritten Extent
06Random Record Number Greater than 3FFFF
0AFCB Checksum Error
0BUnlock File Verification Error
0DInvalid File ID in the DMA Buffer
FFExtended Error

These extended errors can occur:

01Permanent Error
04Select Error
0CServer Not Logged In
The Unlock Record function is meaningless when it references a requester's local drive; it returns a 0 in register A.

FUNCTION 45: SET BDOS ERROR MODE
Defines CP/NET Error Handling
 RegisterValue
Entry ParametersC2DH
EError Mode

The Set BDOS Error Mode function provides the NDOS with these options:

All requester application programs are initially loaded in a default environment that causes the NDOS to abort on extended errors and to display the extended error code. Use Function 45 to change this default mode, according to the contents of register E.

Table 3-2. BDOS Error Modes
RegisterExplanation
0FFHReturn Error Mode. BDOS returns extended errors coming from the network to the application program. Register A is set to 0FFH, and register H contains the extended error code. No error message is displayed on the console.
0FEHReturn and Display Mode. BDOS returns the extended error in the same manner as in Return Error Mode, but also displays an extended error message.
Any Other ValueDefault Mode.

Function 45 is not implemented across the network. The NDOS maintains its own internal error mode flag and acts upon returning network messages according to that flag.

The Set BDOS Error Mode function has no effect on physical errors returned by the requester's local BIOS. These errors always display an error message, then they give the user the option of aborting the application program or continuing.

FUNCTION 64: LOGIN
Initiate Session Between a Requester and a Server
 RegisterValue
Entry ParametersC40H
DEPtr to Login Msg
Return ValuesAReturn Code

The Login function identifies a requester to a server and initiates a session with that server. The Login function must always be successfully called before a requester can access a server's resources. Register pair DE must contain a pointer to a data structure that contains the following two fields:

00-00Server ID byte
01-08Password

The NDOS uses this structure to construct a logical LOGIN message to the server specified. Only the LOGIN message can be passed to the SERVER module without generating an extended error 0C, requester not logged in.

The server checks to see whether the password matches the password defined in the server configuration table. The server then scans the configuration table to find out whether logging in another requester exceeds the number of servers present in the system. If a server exists for the requester, and the password matches, the NDOS returns a 0 in register A. Otherwise, an error is flagged by returning an 0FFH in register A. The NDOS also returns a 0 in register A if the requester is already logged in.

FUNCTION 65: LOGOFF
Terminate a Session Between a Requester and a Server
 RegisterValue
Entry ParametersC41H
EServer ID
Return ValuesAReturn Code
HExtended Error

The Logoff function completes a session and breaks the logical binding between the server specified in register E and the calling requester. Once a Logoff has been performed, the server process is free to begin a session with another requester, if the the server's NETWRKIF can support the dynamic binding of requester nodes to server processes.

Function 65 returns a 0 if successful. It returns an extended error 0C, requester not logged on to server, if unsuccessful.

FUNCTION 66: SEND MESSAGE ON NETWORK
Send a Message to Another Network Node
 RegisterValue
Entry ParametersC42H
DEPointer to Message
Return ValuesAReturn Code

The Send Message on Network function sends messages across the network that might have no defined function on the MP/M II server. This allows applications to be written under CP/NET that use non CP/NET messages. Point-to-point communications packages, special electronic mail systems, implementation of requester synchronization functions, and special print spooling systems are examples of such applications.

To use Function 66, the address of the message to be sent must be passed in register pair DE. The message pointed to might have the standard CP/NET structure of FMT, DID, SID, FNC, SIZ, and MSG, or it might take some nonstandard format. In the latter case, the SNIOS must be able to recognize the nonstandard message and send it properly.

Unlike the usual CP/NET session protocol, the Send Message on Network function does not automatically attempt to receive a response to the message that was sent. So an application can send throw-away messages that do not require a logical acknowledgment or response. You can also define message types that can be broadcast to every node in the network.

If an application requires a logical response to a message sent using Function 66, make an explicit call to Function 67 (Receive Message on Network).

As a rule, set the FMT field of the message header of any nonstandard message sent through a CP/NET system to a value other than those reserved for use by Digital Research. Future releases can then run applications using Function 66, with minimal modification.

Function 66 returns an FF in registers A, H, and L if a network error occurred and the message was not sent.

FUNCTION 67: RECEIVE MESSAGE ON NETWORK
Receive Message from Another Network Node
 RegisterValue
Entry ParametersC43H
DEReceive Buffer Address
Return ValuesAReturn Code

The Receive Message on Network function is the counterpart of Function 66, Send Message on Network. Invoke it immediately after performing a send message if a logical response is expected. Function 67 can also be used to wait for an unsolicited message from another node.

To use Function 67, an application must pass a pointer to a buffer area into which the message can be received in register DE. Upon return, registers A, H, and L are set to 0FFH if the function failed to receive the message properly.

Like Function 66, Function 67 can handle nonstandard messages across a CP/NET network, provided that the requester's SNIOS is equipped to handle them. For a more detailed discussion on how to use Functions 66 and 67, see section 3.4.

FUNCTION 68: GET NETWORK STATUS
Get Network Status Byte from the Configuration Table
 RegisterValue
Entry ParametersC44H
Return ValuesANetwork Status Byte

The Get Network Status function returns the configuration table's network status byte in register A. It also resets any error conditions in the status byte.

For a description of the fields contained in the network status byte, see Section 4.2.1.

FUNCTION 69: GET CONFIGURATION TABLE ADDRESS
Get Configuration Table Address
 RegisterValue
Entry ParametersC45H
Return ValuesHLTable Address

The Get Configuration Table Address function returns the address of the requester configuration table maintained in the SNIOS. Using this function, an application can dynamically modify the mappings of devices across the network. The utilities NETWORK and LOCAL use Function 69 to accomplish this kind of modification

For a description of the fields in the configuration table, see Section 4.2.2.

FUNCTION 70: SET COMPATIBILITY ATTRIBUTES
Configure Server File Systems for an Application
 RegisterValue
Entry ParametersC46H
ECompatibility Attribute Byte

The Set Compatibility Attributes function selectively disables the file security mechanism on all MP/M II servers to which the calling requester has networked drives. This allows certain applications that run under CP/M but not under the MP/M II file system to run under CP/NET and access networked devices.

The CCP.SPR checks the compatibility interface attributes of all COM files that it loads for execution and performs a Set Compatibility Attributes function based on the pattern it finds. This is the only time to use this function. Applications should not modify their compatibility mode in mid-execution. Doing so might produce unpredictable results.

The compatibility attribute byte is set according to the interface attributes found in the COM file's name. The following attributes cause the corresponding bits to be set in register E prior to the call to Function 70:

F1'bit 7
F2'bit 6
F3'bit 5
F4'bits 4, 5, and 6

For a complete description of how to use compatibility attributes, see Section 3.2.5.

Function 70 has no error return. Extended error messages from servers to which the requester is not logged in are ignored.

FUNCTION 71: GET SERVER CONFIGURATION TABLE ADDRESS
Get Information About a Server
 RegisterValue
Entry ParametersC47H
EServer ID
Return ValueHLServer Configuration Table Address

The Get Server Configuration Table Address function returns a pointer to parts of the specified server's configuration table. The ID of the server to be examined is passed in register E prior to calling Function 71, and a pointer to the received information is returned in register pair HL.

The data structure addressed by HL has the following format:

00-00Server Temporary File Drive
01-01Server Network Status Byte
02-02Server ID
03-03Maximum Number of Requesters Permitted on the Server
04-04Number of Requesters Currently Logged In Bit Vector of Requesters Logged In in the Requester
05-06ID Table
07-16Requester ID Table

The information is identical with that contained in the server configuration table, except that the login password has been removed, and a byte containing the server's temporary file drive has added to the front of the table.

Function 71 can determine whether other requesters are logged into a server. The temporary file drive can be used when an application wants to leave a file on a server but does not know the capacity or type of the server's disk drives. The MAIL utility makes frequent use of Function 71.

The server configuration table is returned across the network in a Special buffer in the NDOS. If more than one call is to be made to Function 71, and the calls reference a different server each time, the buffer is overwritten by each successive call. If an application must examine more than one server configuration table at once the table must be copied down into a buffer defined by the application.

If Function 71 passes a server ID to which the calling user is not logged on, an extended error 0C, requester not logged in, is returned.

FUNCTION 106: SET DEFAULT PASSWORD
Establish a Default Password for File Access
 RegisterValue
Entry ParametersC46H
DEPassword Address

The Set Default Password function allows an application to specify a password that is checked if an incorrect password is presented during an Open File function. If a file is password protected, MP/M II first checks for a password in the current DMA buffer. If no match is found, MP/M II then checks the default password set by Function 106. If MP/M II finds a match, it allows the requested operation to succeed. Otherwise, MP/M II returns an error.

When Function 106 is performed on a requester, the requester's NDOS attempts to set the default password on every server to which a drive is networked by that requester. Since Function 106 has no error return, extended requester not logged in errors are ignored

Each server process uses an MP/M II default password slot, starting with console 0 and using as many slots as there are requesters supported.

The default password set by Function 106 persists until another default password is set.

3.4 CP/NET Applications

In addition to running standard CP/M applications packages on a CP/NET requester, you can implement special applications using the network functions available in CP/NET. The applications can handle message processing in a distributed environment. Examples include high-performance print spoolers, node-to-node transfer utilities, and network management tools.

Using Functions 66 (Send Message on Network) and 67 (Receive Message on Network) , you can define an entire set of specialized messages to provide network functions. These messages must be recognized and processed by the SNIOS and NETWRKIF, but once implemented, they can be used by application programs as though they were functions themselves.

Suppose a specific network application requires a print spooler that provides special formatting features. You can write an application program that creates messages with a special code in the format byte of the CP/NET message header. When the application wants to spool data to the special spooler on the server, it uses Function 66 to send the data.

On the server side, the NETWRKIF must be capable of recognizing the specially defined format code. When the NETWRKIF sees this format, instead of routing the message to a server process, it writes the message to a special queue. The actual spooler can reside as a process under MP/M II. The spooler reads the queue and spools the data.

Notice that Functions 66 and 67 are independent of the logical protocol of CP/NET, where every message sent by a requester implies that the requester waits to receive the message. This independence permits an application using a feature like a special spooler to return immediately after sending its message. The application need not wait for a logical acknowledgment.

Another convenient application is a file copy program that works without server intervention. Under the regular CP/NET protocol, the only way to copy a file on a local requester drive to the local drive of another requester is first to copy the file to a common networked drive, then copy it back to the other requester's drive. This is inefficient.

Instead, suppose that the users of the two requesters agree to cooperate in the copying of the file. They can do this by sending each other mail. One user invokes an application program called RECEIVE, while the other brings up an application program called SEND.

The SEND program merely reads the file into memory, then sequentially sends it to the other requester, using Function 66. The SEND program might or might not request verification from the receiving requester via Function 67. In the meantime, the RECEIVE program reads the messages from the network. No server intervention is required; only the two SNIOS modules of the requester are involved in the transmission. Even though the two requesters are only capable of sequential processing, they are still able to send and receive messages synchronously. This application does not require modifications to the SNIOS and NETWRKIF; the standard CP/NET protocol is sufficient, because such applications never reference the server.

Finally, a complex network might require automatic system monitoring and maintenance utilities. Using special message formats, you can design a set of messages that check which drives are usable on various servers, compute the best path from a requester to a given server and back, and notify the system's users of servers and requesters going on or off line. These messages can be handled automatically by the SNIOS or NETWRKIF software, or they can be implemented under the control of special application programs.


Section 4
CP/NET System Guide

The requester's NDOS and the server's SERVER module are key components in the logical structure of the CP/NET operating system. These modules, however, do not deal with the physical problems of moving a logical message from the source requester to the destination server and back again. Implementing this task varies depending on network topology, hardware, and the characteristics of the host computer systems. These modules are therefore not portable from machine to machine. You must customize them.

This section provides the network systems implementer with the information necessary to design and implement a CP/NET system efficiently. Section 4 is divided into four parts. Section 4.1 discusses general network design issues that affect CP/NET implementation. Section 4.2 details how to implement the requester network software, the SNIOS.SPR. Section 4.3 discusses the design and implementation of the server communications software, the NETWRKIF.RSP. Section 4.4 describes the design of a CP/NET server that runs under an operating system other than MP/M II. Appendixes to this manual contain several example network communications packages.

4.1 General Network Considerations

This section explains some of the basic functions of network communications software and describes, in the most general way, how communications software fits into the overall architecture. If any of the material in this section is unfamiliar to you, consult one of the many excellent textbooks available on modern networking technology. Theoretical knowledge can help you enormously in the design and implementation of your network system.

4.1.1 Functions of the CP/NET Physical Modules

The SNIOS and NETWRKIF modules function on four levels. At the lowest level, they must handle the physical transfer of a bit stream from one network node to another. This physical layer must take into account the I/O port numbers being used for communication, the physical characteristics of the network medium, network contention schemes, and other factors.

The next layer of functions must address the problem of getting complete messages from one node to another with no errors or redundant data. This data-link layer takes the bit stream from the physical layer and processes it according to its own protocol.

If any routing from node to node is required, you must include, a network-level protocol. The network layer can be as simple as identifying when a message is destined for a particular node, or it can perform complex store-and-forward operations, compute the best route from node to node, and maintain open circuits for nodes that want to communicate.

The last layer the SNIOS and NETWRKIF must address provides an interface between the low-level communications software and the logical level operating system software. In the SNIOS, this layer must transport messages to and from the NDOS. In the NETWRKIF, the transport layer reads and writes message from and to the appropriate server queues.

The layered architecture presented here can be indistinct in implementations, with single subroutines sometimes handling all four layers at once. Figure 4-1 shows the relationship of the various layers to the network interface. Notice that the physical, data link, and network layers might have to participate in the interface to recover information to perform their functions.

Figure 4-1. Layered Model of a CP/NET Network Node
Figure 4-1

Notice also the interfaces between the various levels. As a message migrates through the layers, the data in the message can change. The interface between the physical layer and the data-link layer yields bit or character data; the message itself is incomplete. The interface between the data-link and network layers produces messages, but the messages might contain routing information irrelevant to the transport layer. When a message reaches the transport layer, it might be in a format unusable by the higher logical layers of the operating system. only when the message is passed to those logical layers must it be complete and in the standard format of a CP/NET message.

The architecture described above corresponds to the four lowest layers of the network model described by the International Standards Organization (ISO). However, there are some slight differences. For example, the ISO definition of the transport layer concerns itself mostly with migrating messages from a centralized network controller to one of many possible hosts. In the model described above, the transport layer deals with moving messages that have already reached a host into the correct portion of the operating system. The model in Figure 4-1 is the basis for the following, more detailed discussion.

4.1.2 Interfacing a Computer to a Network

All network nodes need some method of controlling the communication functions that take place on the communications medium of the network. The simplest method is to have the node's CPU directly control all network communications protocols.

In this case, the network interface is a direct line into the host computer. When the communications software is called upon to send a message, the CPU must initiate the message, possibly waiting for an appropriate handshake response from the destination node. The CPU must then transmit the message, receive and process any acknowledgments, and determine whether the message should be retransmitted. If the node is receiving a message, it must, under program control, detect when the sender is trying to initiate a message, perform any handshake with the sender, receive the message, verify its correctness, and provide acknowledgment. All these tasks must be performed using programmed I/O operations or possibly some form of DMA for parts of the transmission or reception.

These tasks can take up a significant amount of the CPU's processing power. For an SNIOS, this is not a problem, because the NDOS is idle in the time interval after a message is sent and before the response is received. For a NETWRKIF, however, the multitasking nature of the server can result in serious performance degradation.

Another drawback to this method is that it places the burden of engineering communications software on the host systems implementer. This software can be extremely costly to develop for a high performance network.

The principal advantage of this method is its simplicity. If two computers have spare RS-232 ports, you can network them together with no special hardware. Many simple protocols can be readily modified to provide low-performance networks at low cost. Such a protocol is provided in Appendix E.

For higher-performance networks, it might be necessary to relieve the host CPU of the burden of physical, data-link, and network processing. In this case, an intelligent network communications controller can be useful. Many such controllers are available, and there is a variety of methods of interfacing them to a host computer.

An intelligent communications controller can perform all physical and data-link processing, as well as many network layer functions, with no host CPU intervention. The SNIOS and NETWRKIF modules must be concerned only with a nominal amount of network routing, if necessary, and with the problem of transporting the message from the controller. Because the communications controller can transfer data to the host at high speed with high reliability, the host's transport layer can be very simple and requires little CPU time. Appendix G provides a CP/NET implementation utilizing an intelligent network controller.

Intelligent controllers require special hardware that must be added to the host computer. Interfacing this hardware is not always possible. In addition, each network node needs a controller. This can be expensive.

CP/NET also works in multiprocessor environments, both loosely coupled and tightly coupled. A loosely coupled system can send messages via a high-speed, reliable bus. This reduces the data-link problem, so simply transferring data is often sufficient to ensure the message's integrity. Tightly coupled processors can share memory, so messages can be sent between nodes by mapping memory from one processor to another.

4.1.3 Developing a Network Layer

Because CP/NET is independent of the network used, the communication modules must be modified to support various network topologies. The NETWRKIF that supports a multidrop, contention network is different from the one that supports an active hub-star configuration.

Some CP/NET configurations require extremely complex interconnections. Messages destined for one server might have to pass unmodified through several servers or requesters before they reach their final destination. The network implementer must define the software necessary to accomplish this routing. For simple networks, a network layer is barely necessary. For example, a simple work station cluster, where several requesters share a single server, requires only that the destination ID field of the message match the server's ID on a request, and that the destination match the requester's ID when the server's response is sent back to the requester.

In complex networks, each node might need to keep track of other nodes on-line in the network. Some algorithms require the exchange of routing messages to maintain an accurate picture of the topology of the overall network. To do this, the communications software must recognize these routing messages as nonstandard CP/NET messages and not pass them to a server process or to the NDOS for processing.

Even requesters might need a network layer. For example, consider a daisy-chain network of several requesters with a server at one end. All the traffic for requesters farther down the chain passes through the requester adjacent to the server.

Because a CP/M requester can only operate a single task, the communications software for receiving and forwarding a message must be written as a series of interrupt routines. Because the NDOS might call on the SNIOS to transmit or receive a message of its own, these routines must be reentrant to the extent that NDOS requests can be held up until an intermediate message has been processed.

4.1.4 Error Recovery

Network transmission media are often unreliable. Messages are occasionally garbled or lost. In addition to data-link errors, networks can route messages incorrectly, or messages can be lost due to congestion in a section of the network. Because of these problems, a node must be able to recover from transmission errors

The most common form of error is garbled data. Bits that should have been zeros are received as ones, and ones are received as zeros. The easiest way to detect this type of error is to transmit a check along with the message. The check is computed by performing an arithmetic operation on the actual message before it is transmitted. If the check does not match the result of performing the same operation when the message is received, then a transmission error has probably occurred.

Most data-link protocols provide a mechanism for acknowledging that a message was received correctly. This mechanism requires a special message as an acknowledgment. The node that received the original message sends the special message back to the node that sent the original message. If an error occurs, the receiver either sends no acknowledgment or sends a negative acknowledgment, telling the sender to retransmit the message immediately.

The sender must be able to detect a transmission error and take steps to retransmit the message. This can be a problem because the sender does not know what the receiver is doing. If an error message comes back, the sender knows something has gone wrong. But if a message is lost completely, the receiver might not know it was sent and never send an error condition.

To solve this problem, the sender can send a message, then wait a predetermined interval for acknowledgment. If no acknowledgment arrives, the interval expires, and the sender times out. A timeout condition can cause the sender to retransmit the message or take other steps to recover from the error. When the message is finally sent successfully, the sender can free up the buffer that held it and continue with other processing.

For a CP/NET requester, two different levels of timeouts might be necessary. At the data-link level, a timeout can be set on the amount of time that elapses between sending a message and receiving the acknowledgment that it was received correctly. This timeout interval can be fairly short, since the transmission path is not likely to be very long.

The second timeout addresses the logical structure of CP/NET. Every message sent to the server implies a response to be sent back to the requester. A timeout can be set upon entering the requester's receive message routine. If the requester waits too long for a response, it can be assumed that the communication link or the server itself has crashed. With this kind of timeout, the error recovery involves much more than just retransmitting the initial message. A logical initialization must take place, probably including a CP/M warm boot.

A timeout scheme can successfully retransmit lost or garbled messages. Another problem arises, however, when the receiver's acknowledgment signal is lost. The sender, not receiving the acknowledgment, eventually times out and retransmits the message. In the meantime, the message has actually been successfully received. When the message arrives from the sender a second time, the receiver must have some way of knowing that the message is a duplicate. The receiver should ignore the message, but send an acknowledgment to stop the sender from sending the duplicate yet again.

The easiest way to detect duplicates is to assign a sequence number to each message. If the receiver does not receive the sequence number it was expecting, it ignores the message, even if the message was received correctly. Every time a message is received, the expected sequence number is incremented. Every time the sender receives an acknowledgment, the sequence number to be sent is incremented. If a message times out, however, the sequence number is not incremented.

All error recovery schemes should be free from deadlocks. A deadlock occurs when the sender is waiting for an action from the receiver, but the receiver is not performing that action because it is waiting for the sender to perform another action. Carefully analyze networks that store and forward messages from node to node for deadlocks because two nodes can try to transmit to one another simultaneously.

The means of avoiding deadlocks varies according to the network topology. A multidrop network can use collision detection. if two nodes attempt to use the network at the same time, they immediately detect that their messages are garbled and stop transmitting. To avoid continuous collisions and a consequent deadlock condition, the two nodes attempt to transmit again based on a random time interval, so that one node can start transmitting before the other.

In a point-to-point network, a properly designed message handshake can often avoid data-link deadlocks. At a higher level, enforcing a buffer allocation protocol can often prevent deadlocks. Waiting to transmit messages until the receiver has space for them minimizes the possibility of two messages continuously timing out.

4.2 Customizing the Requester's SNIOS

The communication interface between the logical NDOS and the actual network is contained in the Slave Network I/O System module, SNIOS.SPR. Because this interface varies depending on the computer system and network hardware, you must customize the SNIOS.

For most applications, the SNIOS need only be a sequential system. The SNIOS never needs to respond asynchronously to unsolicited messages. Only the NDOS must direct the SNIOS to receive messages. However, some networks require real-time response from their SNIOS modules to pass a message between two network nodes that have no direct means of communicating with one another.

This section details the design and preparation of an SNIOS for inclusion with a CP/NET requester and describes the installation of the utilities necessary to run the requester.

4.2.1 Slave Network I/O System Entry Points

The SNIOS must begin with a jump vector containing the network I/O system entry points, as shown below:

Listing 4-1. SNIOS Jump Vector
SNIOS:  JMP NETWORKINIT         ; Network initialize
        JMP NETWORKSTS          ; Rtn network status
        JMP CONFIGTBLADR        ; Rtn Config. Tbl Adr
        JMP SENDMSG             ; Send msg on network
        JMP RECEIVEMSG          ; Receive msg from ntwk
        JMP NTWRKERROR          ; Network error
        JMP NTWRKWBOOT          ; Network warm boot

Each jump address corresponds to a subroutine that performs the specific function. The exact responsibilities of each entry point subroutine are given below.

NETWORKINIT
This SNIOS entry point is called when control is transferred to the NDOS initialization entry point after being loaded by the CPNETLDR. This subroutine performs any required network interface initialization. Initialization includes reading back-panel switches, or some other suitable source, to obtain the requester processor ID for the configuration table. If initializing messages must be sent out over the network, send them from this routine.
NETWORKSTS
This subroutine returns a single byte in register A and determines the status of the network interface. The error bits snderr and rcverr are reset when the call is made. The format of the network status byte is shown in Figure 4-2.

Figure 4-2. Network Status Byte Format
+---+---+---+---+---+---+---+---+
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---+---+---+---+---+---+---+---+
              ^       ^   ^   ^
              |       |   |   |
              |       |   |   +-- snderr
              |       |   +-- rcverr
              |       +-- ctrlps
              +-- active
CONFIGTBLADR
This subroutine returns the requester configuration table address in the HL register pair. The requester configuration table is described in section 4.2.2.
SENDMSG
This subroutine enables messages to be sent from one processor to another via the network. The passed parameter, in registers BC, is a pointer to the message. Control is not returned from this procedure until the message has been sent. Thus, the message pointed to by the BC register pair can be modified immediately upon return. The return code, in register A, has a value of 0 indicating success or 0FFH indicating failure to access the network.
RECEIVEMSG
Messages are received from another processor through the network with this subroutine. The passed parameter, in registers BC, is a pointer to a message buffer. Control is not returned from this procedure until the message has been received and placed into the message buffer. Thus, the message in the buffer is valid immediately upon return. The return code, in register A, has a value of 0 indicating success or 0FFH indicating failure to access the network.
NTWRKERROR
When network errors are encountered, this procedure is called. Any required network interface device reinitialization should be performed. In typical SNIOS implementations, executing a return from the NTWRKERROR procedure results in a retry. If a retry is not wanted, an appropriate message is displayed on the console, and a warm boot is performed.
NTWRKWBOOT
This SNIOS procedure is called each time the NDOS reloads the CCP. The sample SNIOS in Appendix E displays a
<Warm Boot>
message on the console only as a demonstration of NTWRKWBOOT. More practical applications of this procedure include interrogating the CP/NET server for messages. In this way, each time a warm boot is performed, the user is notified of messages posted for him.

4.2.2 Requester Configuration Table

The configuration table that resides in the CP/NET requester's SNIOS allows reassignment of logical devices to networked servers. The configuration table creates a mapping of logical to physical devices that can be altered during CP/NET processing. The configuration table specifies the system I/O to be accessed through the network.

The requester configuration table is defined in Table 4-1.

Table 4-1. Requester Configuration Table
OffsetExplanation
000-000Requester status byte
001-001CP/NET requester processor ID
002-033Disk Devices; 16 two-byte pairs, first byte high-order bit on = drive on network, with the server drive code in the least significant 4 bits; the second byte contains the server processor ID.
034-035Console Device; first byte high-order bit on console I/O on network, with the server console number in the least significant 4 bits; the second byte contains the server processor ID.
036-037List Device; first byte high-order bit on = list to network, with the server list device number in the least significant 4 bits; the second byte contains the server processor ID.
038-038List Device buffer index.
039-043List Device logical message header: FMT, DID, SID, FNC and SIZ.
044-044List Device server list device number.
045-172List Device buffer.

4.2.3 Preconfiguring the Configuration Table

In many network systems, there is never any need to modify the device mappings specified through the NETWORK utility. In such systems, you can preconfigure the device mappings in the configuration table. To do this, select the devices to be networked and set the high-order bit of the first byte in the entries corresponding to those devices. Set the remote device to which the local device is to be mapped in the low-order four bits of the same byte. Finally, set the server ID of the remote device in the second byte of the entry.

Be careful when preconfiguring devices to servers that might be off line. Some CP/NET functions send messages to all servers referenced in the configuration table. If one of these servers is not capable of receiving messages, functions that might subsequently send messages to servers on line can prematurely abort.

For example, the CCP might issue a free drive function to initialize the server environment for a subsequent application program. If the previous application had left files open on two on line servers, but a third server was off line, those files are left open if the free drive message was sent to the off-line server before the on-line servers. The next application program might damage the files that were inadvertently left open.

You can solve this problem by having the error recovery in the SNIOS remove any networked device that experiences continuous timeouts, converting it back into a local device. This prevents the NDOS from making continuous references to the off-line server. A major drawback of this scheme, however, is that an application might suddenly begin referencing a local device, possibly destroying files on a local disk drive. A more secure, but less friendly protocol for dealing with off-line servers is to force a warm boot whenever a network error is encountered.

It is wise to enforce a protocol that prohibits devices from being networked until the server to which they are assigned is on line. Special utilities can be written to accomplish this by sending a dummy message to every server to which drives are mapped.

4.2.4 Sending and Receiving Messages Asynchronously

In some networks, a requester might have to receive and retransmit asynchronously a message destined for another node. For example, consider a loop network, where every node has two network ports. The network protocol specifies that all messages are sent via port #1, and all messages are received via port #2. If there is only one server in the network, but more than one requester, all messages must pass through every other requester, either as they are sent to the server or as the response returns from the server.

If a requester must asynchronously handle a communication channel, it must do so outside of the facilities provided by the single-tasking CP/M operating system. The communication protocol must be interrupt driven. An interrupt service routine must at least detect the start of a message; after that, the rest of the message can be handled sequentially or under control of additional interrupt routines. If a requester cannot support interrupts, asynchronous handling of messages might be impossible. Neither the application program nor the NDOS can periodically check for incoming messages.

A mechanism must be provided so that the NDOS, sequentially calling the SNIOS to send a message, does not collide with the asynchronous transmission of another message. Receiving messages cannot collide because only one message can come over the network at a time. To accomplish this, consider implementing the loop network described above.

As a requester's application is running, another node suddenly starts sending a message to it. The requester must now receive the message, verify its correctness, and retransmit it to another node. All of these operations must be performed without damaging the local application program. If the data-link routines do not make CP/M system calls and do not modify the message buffers used by the NDOS, the entire message can be received and transmitted transparently. When this operation is finished, the interrupt service routine returns to the application program, and processing continues. When the NDOS needs to use the network, the same data-link routines that handled the asynchronous message can be used to handle the sequential one.

It is even possible to transmit a message from the NDOS while receiving a message from some other node. To do this, the message must be able to be received a piece at a time, giving both the send and receive routines enough processor time to avoid timing out. Such a system requires a mechanism for preventing both the NDOS and the interrupt service routine from attempting simultaneous transmission. A semaphore variable can be used to control the system.

Figure 4-3 outlines a possible protocol for such a system. Both the SNIOS SENDMSG routine and the asynchronous receive interrupt service routine access a piece of reentrant code to control access to the message transmission system.

Three external events drive the system:

In this implementation, the message sending software is interrupt driven, started by enabling a transmitter interrupt. The message sending software can also operate sequentially, called by the reentrant routine that controls its use.

Figure 4-3. Algorithm for Interrupt-driven Requester Node that Stores and Forwards Messages
Figure 4-3A
Figure 4-3B

4.2.5 Generating and Debugging a Custom SNIOS

Follow these steps to generate and debug a custom SNIOS.

  1. Prepare the SNIOS.SPR file, as shown below:

    
           A>RMAC SNIOS
           A>LINK SNIOS[OS]
            

    The output of the linker is the SNIOS.SPR file.

    If you do not use RMAC and LINK-80 use ASM, PIP, and GENMOD, as shown below:

    Assemble with ORG 0000H.
           A>ASM SNIOS
           A>REN SNIOS0.HEX=SNIOS.HEX
                
    Edit the SNIOS.ASM ORG statement. Assemble with ORG 0100H.
           A>ASM SNIOS
           A>REN SNIOS1.HEX=SNIOS.HEX
            
    Concatenate the HEX files.
           A>PIP SNIOS.HEX=SNIOS0.HEX,SNIOS1.HEX
                
    Generate the SNIOS.SPR file.
           A>GENMOD SNIOS.HEX SNIOS.SPR
                

    The GENMOD program uses the difference in code origins to produce a bit map of addresses to be relocated. GENMOD then places this bit map at the end of a copy of the origin 0 code and constructs a 256-byte header to create an SPR file.

  2. Copy the following files to the requester:

    you can use DDT to debug the SNIOS as follows:

            A>DDT CPNETLDR.COM
            *IB
            *s103
            0103 07 xx
            *g
               

    where xx is the restart the debugger uses, usually 7.

    At this point, CP/NET loads, displaying the memory map, and then breaks at the specified restart. You can place breakpoints at desired locations, and then issue a G command specifying the address following the restart instruction where the CPNETLDR broke.

Communications software is difficult to debug. Because of its real-time nature, when the program is interrupted to find out what is going on, the other side of the network overruns or times out. These pointers might help you:

4.3 Customizing the Server

This section addresses the problems of designing and implementing an efficient CP/NET server under the MP/M II operating system. Because a CP/NET server must be capable of handling several simultaneous requests in real-time, the Network Interface module (NETWRKIF) must take full advantage of the real-time primitives of MP/M II.

The server's logical module, SERVER.RSP, consists of a set of processes, one for each requester supported. This section also discusses how the NETWRKIF sends and receives messages to and from those processes.

Finally, this section explains the system generation options available to the server implementer once the NETWRKIF has been implemented.

4.3.1 Detecting and Receiving Incoming Messages

The server is a passive, asynchronous system; it does not initiate CP/NET transactions. The server performs two distinct functions:

  1. The server must detect an incoming message and initialize the communications software to receive.
  2. The server must actually receive the message.

The server detects incoming messages in two ways. The first is polling, where the server periodically checks the status of the network interface. If the status changes from an idle to a ready state, the server receives a message. The second method relies on the network interface's interrupting the server. The server then transfers control to a service routine that receives the message. Either of these methods can accomplish the two functions listed above. Both methods have advantages and drawbacks.

Polling the Server
Polling is a more active method, requiring more processing overhead. If the server has a fairly heavy, continuous load of network traffic, then the status of the poll operation often indicates that a message is to be received. In this kind of system, polling has a marked advantage: the server can immediately begin receiving the message without switching contexts. But if the network traffic is subject to bursts of data mixed with periods of traffic, then the extra overhead of interrogating the network interface is inefficient.
Interrupting the Server
Interrupt driven operation is excellent for communication that occurs in bursts because no overhead is required when no communication is taking place. But very high network loads cause the server to waste a great deal of time saving the state of the process currently executing when the interrupt occurred.

Once a message has been initiated, it can be received under interrupt control, where data is processed on demand as it comes in, or under direct program control, where a process is dedicated to monitoring the incoming message. The most efficient choice depends on the type of network being used and the amount of traffic the network must handle.

In an interrupt driven communication scheme, the server responds to network events asynchronously. The network interface determines when data is processed by the host CPU. For example, when the network interface presents characters to the host, each character causes an interrupt. When the network interface performs direct memory access to transfer blocks of data, only each complete DMA transfer causes an interrupt. Depending on the protocol, each interrupt causes a specific action to be performed. The CPU is free, however, to process other tasks in between processing each piece of data. Like interrupt-driven message detection, saving the state of an interrupted process requires CPU overhead. The greater the number of interrupts required to process a message, the more system performance is degraded.

Overruns
One of the greatest problems of an interrupt-driven communications scheme develops when the interrupts occur faster than the CPU can service them. This condition is known as an overrun, and it can cause data to be lost. When an overrun occurs, the message appears to be garbled, and the sender must retransmit it. If overruns occur only when the host is extremely busy, it might be more efficient to accept the occasional garbled message in exchange for better overall response. If the number of overruns is too high, however, serious system degradation sets in. Many protocols prevent overruns by allowing the receiver to signal the sender that data is Coming in too fast.
Disabling Interrupts
The other approach to message processing uses MP/M II's facility to control processes. Unlike an interrupt service routine, which is largely transparent to MP/M II, a process is a logically complete task. Using a process-oriented protocol, you can eliminate the overrun problem by disabling interrupts while the message is being received. Disabling interrupts gives the communication program exclusive control of the CPU, so all other processing comes to a halt. If messages are fairly short, however, this method might be preferable to an interrupt-driven scheme, because no overhead is incurred by switching back and forth between a process and an interrupt service routine continually.
Selecting a Protocol
The actual data-link protocol used to process messages has not been discussed. Consider the selection of a protocol when designing how the server is going to respond to incoming messages. For example, in a CP/NET system where loosely coupled processors are communicating over a high-speed bus with little or no error checking, DMA transfer of data can be efficiently interrupt driven. But complex cyclic redundancy checks that involve extensive arithmetic operations require careful design in an interrupt-driven system, or overruns might result. Such a protocol might be better implemented using a process-oriented system.

4.3.2 NETWRKIF Module Architecture

Section 4.3.1 discusses general strategies for implementing a data-link layer protocol under MP/M II. This section deals with integrating the data-link layer into a network and transport layer. This integration allows the entire communications package to send logical requester messages to the SERVER.RSP module, and then receive the SERVER's response message for transmission back to the requester.

A dedicated server process is associated with each requester logged on to a server node. These processes are named SERVR<x>PR where <x> is an ASCII character between 0 and 9 or A and F. This character is a sequence number that serves as a unique identifier for the server process. Each server opens two queues that it expects the NETWRKIF module to have created. They are named NtwrkQI<x> and NtwrkQO<x> where <x> is the same character as the server's sequence number. The server process always reads the address of incoming messages from NtwrkQI<x>, and it always writes the address of the response message to NtwrkQO<x>.

This is the basic interface between the SERVER.RSP module supplied by Digital Research and the user-customized communications software. However, there are a variety of ways to implement the processes driving the interface.

Appendix E includes an example of the simplest NETWRKIF architecture. In this architecture, one network interface process is associated with each server. All processes execute the same reentrant code, but each process maintains local data that identifies the communications port it is using and the sets of queues through which it interfaces to the server process. This implementation handles its data-link software at the process level. It uses polled console I/O functions in the XIOS to detect incoming messages. This architecture is illustrated in Figure 4-4.

Figure 4-4. Server Architecture w/Reentrant NETWRKIF Processes
Figure 4-4

Another possible NETWRKIF architecture has only two network interface processes. An input process receives data from the network, identifies the requester that sent the message, and writes the message to the appropriate queue. An output process conditionally reads all the output queues and sends any messages it finds back out over the network.

It is also possible to force all the server processes to write their messages to a single queue by patching SERVER.RSP. In this case, the output network interface process reads the single output queue. When a message is written to it, the output process sends the message out across the network and goes back to read the queue again. An application note details how to patch SERVER.RSP. Figure 4-5 illustrates both strategies. Note that a small patch to the SERVR<x>PR processes can consolidate the output queues.

Figure 4-5. Two-process NETWRKIF
Figure 4-5

You can design a single NETWRKIF process that receives a message, writes it to the appropriate queue, then checks for any output activity. If NETWRKIF finds a message to send, it sends it, then it returns to checking for input. This kind of process has the disadvantage of being constantly busy; there is no point at which it can allow itself to become blocked. To do so might result in a deadlock or serious performance degradation.

Consider the network topology when designing the NETWRKIF architecture. For example, a NETWRKIF that uses one process per requester is suitable in an active hub-star configuration, where a unique network line is dedicated to each requester. This allows several messages to arrive at the server simultaneously.

For a multidrop topology, however, a single output and single input process NETWRKIF might be more suitable, because the network. hardware guarantees that only one message is active on the network at any one time. The same type of architecture could be applied to a loop topology.

For an active hub-star network that services several multidrop lines, it might be necessary to combine the two architectures, so that several reentrant processes are routing input to the server processes, while a set of output processes are collecting data from output queues and sending it back out of the appropriate multidrop line.

Also consider what the NETWRKIF does when it has no traffic to process. If the NETWRKIF loops madly while waiting, it will gobble up precious CPU resources, degrading the overall performance of the server system. On the other hand, the NETWRKIF must be able to respond to traffic quickly.

A number of MP/M II system calls cause a process to become blocked, so that the operating system dispatcher does not pass control back to the process until a critical condition is fulfilled. Reading an empty queue, waiting on a flag, and performing a poll call are three of the most common ways to suspend the execution of a process conditionally. Such quiescent points should be built into all NETWRKIF systems to minimize the overhead of maintaining the process when it is idle.

The processes driving the input and output queues constitute one half of a message transport layer. The NETWRKIF must also deal with how the raw message is received from the data-link and network layers that are performing the actual communication control. This interface is governed by how the data-link and network layer software is implemented.

Consider an architecture that has little or no network layer, so that the data-link software interfaces directly with the transport processes. If the data-link is included in the processes that are also performing the queuing functions, then no special interface is needed. The process can pass control from one function to another, first performing input data-link and network activities to receive a message; then computing the routing to the appropriate server input queue; then reading the response from an output queue; and finally returning to the data-link level to send the response back to the requester. The sequence can be repeated indefinitely.

Some implementations require the data-link and network layers to be under process control, with a separate set of processes controlling the transport layer. In these cases, the transport processes can use queuing for both the low-level interface to the data-link layer and the upward interface to the server processes.

This kind of architecture has the drawback of slowing down the MP/M II dispatcher with extra queuing overhead. For a small number of processes, however, the impact is slight. The architecture has the advantage of being highly modular, facilitating the future upgrade of the data-link and network layers or the transport layers. Figure 4-6 details the architecture.

Figure 4-6. A Single Transport Process Interfacing to Low-level Data-link Processes
Figure 4-6

To implement some network interfaces, it is necessary to modify the MP/M II XIOS. Interrupt service routines must access the system interrupt vector, which is usually maintained by the XIOS. If an interface routine requires polling, the routine to accomplish the polling must be placed on the list maintained by the XIOS POLLDEVICE routine.

Interfacing to data-link and network routines that reside in the XIOS is slightly more complex than interfacing to routines contained in the NETWRKIF. These routines are often not processes, but shared code fragments or interrupt service routines. They cannot use queues as an interface mechanism. Routines that are not process-oriented must communicate through a direct function linkage, through polling, or through the Flag Set/Flag Wait functions supported by MP/M II.

Because the NETWRKIF might not be able to resolve references to such routines directly, it is often necessary to enter the XIOS through its jump vector. The XIOS jump vector table is always page aligned; a pointer to that page is located in byte 7 of the MP/M II system data page - From this point, data-link routines can be called by specifying dummy console I/O or dummy list device I/O.

If dummy console or printer I/O is used, the NETWRKIF loads a non-existent device number in register D and, if necessary, a pointer to a message buffer. The I/O routine specified checks for the non-existent device number and dispatches the call to the appropriate network routine.

Figure 4-7 illustrates how the NETWRKIF module can perform calls to subroutines resident in the XIOS.

Figure 4-7. Directly Interfacing the NETVRKIF to XIOS Routines
Figure 4-7

Another method of interfacing data-link and network layer routines to a transport NETWRKIF is to have the low-level routines set a flag when a message has been processed. For example, consider a data-link routine that reads in an incoming message and checks it for validity. This routine might be a set of vectored interrupt service routines.

At this point, the NETWRKIF is not synchronized with the data link routine. When the NETWRKIF requires a message, it issues a flag-wait call to MP/M II. When the data-link routine has a complete message, it issues a flag set call. The NETWRKIF does not proceed until the flag has been set. The NETWRKIF can then transfer the message from a predefined buffer and transport it to the appropriate server process.

This type of architecture is ideal for allowing intelligent network controllers to drive the NETWRKIF transport processes. A simple interrupt service routine locates the message, builds a control block, and sets a flag to inform the NETWRKIF of the status and location of the message. Figure 4-8 shows a similar interface.

Figure 4-8. Synchronizing Data-link Activity Using Flags
Figure 4-8

To send a response message back to a requester using flags, the transport process must first identify the message to be sent and instruct the data-link layer to send it. A predefined control block can accomplish both operations. The transport process then waits on a flag until the message is sent and the flag set by the data-link.

Another possible synchronization mechanism is through the MP/M II Poll function. With this function, MP/M II suspends the calling NETWRKIF process but periodically interrogates the status of the data-link and network software through a small code fragment defined in the XIOS POLLDEVICE routine. When the status becomes true, MP/M II allows the NETWRKIF process to proceed.

If the server system supports vectored interrupts, and the location of the system's interrupt vector is known, you can write interrupt service routines that reside inside the NETWRKIF module. When the NETWRKIF performs its initialization, it simply writes the addresses of various interrupt service routines into the vector. From then on, any reference to those vector locations results in the execution of the NETWRKIF's ISRs.

This approach preserves system modularity and allows the network implementer to implement low-level routines when the XIOS itself is not available for modification. This approach still requires a synchronization mechanism between code fragments that are not part of any process and the more well-defined transport processes of the NETWRKIF.

In addition to synchronizing with low-level communications software, NETWRKIF processes might have to compete for data-link resources. For example, a transport process that wants to send a message might have to be suspended while another process is busy receiving a message. Or two reentrant processes might try to send a message out across the same network line simultaneously. These conflicts can be resolved through use of mutual exclusion (MX) queues.

An MX queue contains only one dummy message, called a token. In order to control a resource, a process must first acquire the token, leaving the MX queue empty. If another process already has the token, the first process is suspended until the second completes its resource-critical operation and replaces the token.

In this way, two low-level data-link routines--one for sending and one for receiving--can be driven without collisions by their higher-level transport processes, even if the low level routines have no explicit mechanism for sharing a network resource.

Just as the design of the network topology and error recovery schemes for CP/NET must be examined for potential deadlocks, so must the server architecture itself. A simple example of a deadlock is a process that competes for a resource using an MX queue but never restores the token to the queue when it is finished with the resource. All the other processes waiting for the resource come to a grinding halt, the network becomes congested, and eventually everything stops.

Finally, you can design an architecture that distinctly divides the data-link, network, and transport layers. The preceding synchronization strategies can be generalized to work across several layers just as easily as they can work when the server architecture divides the communications software into low-level and high-level segments. Remember that as the architecture grows more and more complex, performance of the MP/M II dispatcher and nucleus software degrade further and further. It is always wise to keep the architecture as simple as possible.

4.3.3 Elements of the NETWRKIF

This section defines the data objects and processing required to allow the server to be initialized and to operate smoothly and continuously. Through these objects, you define how many requesters a server can handle at once and how many messages can be simultaneously processed.

The following objects must be present to create the NETWRKIF.RSP module:

XDOS Entry Point
All resident system processes (RSPs) require a linkage to MP/M II's XDOS entry point because the Command Line Interpreter does not prepare an execution environment for them. This linkage is always the first two bytes of the module. When the implementer runs the MP/M II GENSYS utility to include the server modules into the operating system, GENSYS automatically fills in these two bytes with a pointer to the XDOS entry point. This allows the execution of MP/M II system functions within the body of the RSP by setting up the function parameters, loading this pointer, and dispatching.
NETWRKIF Process Descriptors
Immediately following this pointer, MP/M II expects to see a Process Descriptor. It automatically creates and executes the process to which the Descriptor refers. In the case of the NETWRKIF, this Process Descriptor controls the execution of one of the server transport processes. These processes perform the queue read and write operations to move messages into and out of the server processes. The first process must also be responsible for server and network initialization and for creating any additional transport processes.

Process Descriptors for additional transport processes must also be included, if the processes are necessary. These processes can be automatically created by linking them to the first Process Descriptor. Linking is accomplished by placing a pointer to the second Process Descriptor in the PL field of the first Process Descriptor, a pointer to the third in the PL field of the second, and so on. The chain of links terminates with a zero in the PL field of the last Process Descriptor to be created.

If you choose to have processes automatically created, remember that once processes are created, they are completely independent unless they are explicitly synchronized. The processes should not be dependent upon the first process to perform initialization for them.

Run transport processes at a very high priority, so that messages tie up the communications software for as little time as possible. The example in Appendix E runs at priority level 64, exactly the same priority as the server processes. For compute bound NETWRKIF processes, it is advisable to give the server a slightly higher priority than the NETWRKIF. The implementation in Appendix F, for example, runs at a priority of 66. This forces MP/M II always to process logical messages first if both the server and transport processes are ready at the same time.

Each transport process must have its own local stack area. Because RSPs do not have access to the extra user stack space on system calls, each stack must be capable of supporting the local storage required by the MP/M II XDOS and XIOS in addition to its own local storage.

When a process is created, its Process Descriptor's STKPTR field should point to the top of its associated stack. The top of the stack must contain the starting execution address for the process.

Queue Control Blocks
The NETWRKIF module must contain all of the queue control blocks for the entire server system. The number of QCBs varies depending on how many requesters the server system supports at one time. For each requester, there must be one input queue, named NtwrkQIO, NtwrkQI1, and so on. There must also be one output queue per requester, named NtwrkQOO, NtwrkQOI, and so on. These queues must also be created by the NETWRKIF module.

You can patch the server process code so that all processes open the same output queue, NtwrkQOO. If this patch is applied, the NETWRKIF need only include the one output QCB. The NETWRKIF examples in Appendixes F and G use this method.

The input and output queues communicate the address of the message buffer containing the message to be processed by the server or the response to be sent back to the requester. Because the message passed through the queue is only two bytes long, circular queues can be used. Both input and output queues need only buffer one message at a time because a requester must have always received a response before sending another request. Consequently, there is never more than one message from a given requester at the server at a time.

A queue capable of buffering more than one message is required only when the server processes have been patched to write all of their responses to a single queue. In this case, the queue must be capable of buffering the output from all of the servers simultaneously.

User Queue Control Blocks
Transport processes must read and write queues using user queue control blocks. These data structures contain a pointer to the appropriate QCB and a pointer to the message to be written. The queue passes only the addresses of message buffers rather than the message buffers themselves. The address of the message buffer to be accessed must be written to a location in memory, and a pointer to that location must be loaded into the appropriate UQCB.

If the UQCB can resolve the address of its associated QCB, there is no need for the NETWRKIF to open the queue using MP/M II Function 135 once the queue has been created. A pointer to the QCB can be placed in the UQCB at link time, instead. If, however, the QCB address cannot be resolved, an open queue operation must be performed. This might be the case if the system implementer breaks the NETWRKIF module into an RSP and a Banked Resident System Process (BRS).

Message Buffers
The message buffers must each be at lease 262 bytes long, 5 bytes for the CP/NET header information, and 257 bytes for the actual CP/NET message. Even though the longest CP/NET message is only 256 bytes long, the extra byte is required because the server processes use the message buffer they are passed as a temporary scratch area.

If the data-link and network layers require additional header information, the message buffers must be even longer. If the message format used by the network is different from that used by CP/NET, the message must be converted into the standard CP/NET format before it is passed to the server process. The server process expects a one-byte format code of 0, a one-byte destination code equal to the server ID, a one-byte source code, a one-byte function code, a one-byte size code, and a contiguous message in binary format. The server returns an error for any deviation from this format.

A server process always returns its response to a requester in the same message buffer that it is passed. Consequently, no transport process should modify a message in between the time that its address is written to NtwrkQI<x> and the time that its address is read back from NtwrkQO<x>. To do so can cause the server to crash.

It is not always necessary to have one buffer for every server process in the server system. Fewer buffers can be provided if the network implementer limits the number of transactions that can occur simultaneously. It is important to recognize the distinction between the number of requesters supported (the number of sessions that can be ongoing at any one time) and the number of simultaneous transactions supported (the number of messages the server can process at any one time).

Because many server processes can be idle, the number of transactions can be much lower than the number of requesters. Limiting the number of transactions can sometimes drastically improve the performance of a CP/NET server because it reduces the amount of time the operating system switches from process to process trying to service a number of file-oriented requests simultaneously.

The Server Configuration Table
The server process must interface directly with a set of objects within the NETWRKIF to perform its own initialization, maintain its own reentrant processes, and perform validity checking on its incoming messages. These three sets of objects are the server configuration table, server Process Descriptor areas, and server process stacks.

The server configuration table is defined in Table 4-2.

Table 4-2. Server Configuration Table
OffsetExplanation
00-00 Server status byte. The communications software can use this byte to signal the current state of the network. This byte has no fixed function, however.
01-01 Server processor ID. The server processes compare this field against the destination ID field of all incoming messages. An error is returned if they do not match. A server ID of FF hex is illegal. Requester utility programs use a default server ID of 0, so a CP/NET network containing only one server identifies it as node 0, for convenience.
02-02 Maximum number of requesters supported at once. Up to 16 requesters can be supported.
03-03 Number of requesters currently logged in. This field is incremented by a server process when a login takes place and decremented when a logoff takes place. Logins return an error if the maximum equals the number currently logged in.
04-05 Log-in vector. Each bit of this field indicates whether the corresponding requester ID table entry is valid and refers to a logged-in requester. When a successful login takes place, a bit is set in this vector and the corresponding table entry is updated. When a logoff occurs, the table is searched and the corresponding bit is reset.
06-21 Requester ID table. When a requester is successfully logged in, a server process locates an empty slot by checking the log-in vector, marks the slot as used, and then writes the source ID of the log-in message into this table, using the bit vector position as an index.
22-29 Log-in password. The password sent in the log in message must match this password, or the login fails, and an error is returned.

Just as the requester configuration table can be preconfigured to map certain devices as networked, the server configuration table can be preconfigured to define certain requesters as logged in without performing a login operation.

To do this, set the current number of logged-in requesters to the number of predefined logins desired. Make sure the number is less than the maximum number of requesters permitted. Otherwise, the server's behavior becomes unpredictable.

The log-in vector should have a bit set for every requester to be prelogged in, and the requester ID table should contain the logged-in requesters. For example, for a five-requester server where requesters 1, 2, and 5 are defined as already logged in, the server configuration table might look like this:

configtbl:      db 0            ; server status
                db 0            ; server ID
                db 5            ; max number of requesters
                db 3            ; currently logged in
                dw 8009h        ; log-in vector
                db 1            ; requester ID table
                ds 2
                db 2
                ds 11
                db 5
                db 'WUGGA'      ; password

The requester ID table is position independent. When a server process checks to see if a requester is logged in, it searches the entire requester table, using the entire log-in vector to check the entries for validity. Consequently, the configuration table is not sufficient to specify the process to which an incoming message should be routed.

The transport software must maintain its own routing mechanism. For example, the NETWRKIF in Appendix E maintains its routing implicitly as local data in its reentrant processes. The example in Appendix F, on the other hand, relies on a requester control block that associates a source ID number with a UQCB.

Descriptors and Stacks
The module SERVER.RSP contains only one Process Descriptor and stack area. It is consequently initialized as only one process. SERVER.RSP must have some way of creating additional copies of itself. To do this, SERVER.RSP must know how many copies to create, and where to put the additional Process Descriptors and stacks.

By convention, the NETWRKIF process writes the address of the server configuration table into location offset 0009 in the system data page. The SERVER module uses this address to locate the maximum number of requesters from the configuration table. It then creates the maximum number, less one, of processes. To locate storage to create the additional processes, the SERVER module expects to find stack areas for the extra processes directly following the configuration table.

Server process stacks must be exactly 150 bytes long, and there Must be one stack for each additional server. For example, to support a total of five servers, 4*150 = 600 bytes of storage must he allocated after the configuration table.

The server expects the top of each additional server stack to contain a pointer to a 52-byte data area in which to create the new Process Descriptor. All of the Process Descriptor data areas must be contiguous.

Here is an example of the structure required for a four requester server:

server$pds:     ds (4-1)*52     ;server Process Descriptors

; (other data or code can be defined here)

configtbl:      ds 30           ;configuration table allocation
srvr$stkl:      ds 148          ;second server stack area
                dw server$pds
                ds 148	        ;third server stack area
                dw server$pds+52
                ds 148	        ;fourth server stack area
                dw server$pds+104
Listing 4-2. Stack and Process Descriptor Allocation for a Four-requester Server
NETWRKIF Execution Requirements
The initialization code must perform the following actions:

These initialization functions need not be performed by a single process; they can be distributed among a variety of processes and interrupt service routines. The address of the configuration table should be written to the system data page with interrupts disabled. This prevents the server from loading an incorrect partial address and making its process-creation decisions on invalid data.

Figure 4-9 shows a memory map, detailing how the SERVER.RSP and NETWRKIF.RSP modules fit into the rest of MP/M II, and how they communicate with one another during initialization.

Figure 4-9. A Typical Server Memory Map
Figure 4-9

Most of the other NETWRKIF run-time functions are discussed in previous sections. The general form of the NETWRKIF is the following:

  1. Allocate a message buffer and receive a message. Check the message for data-link or network errors.
  2. Reformat the message, if necessary, into the standard CP/NET format.
  3. Compute the server process to which the message should be routed.
  4. Write the message to the server's input queue.
  5. Read the response from the server's output queue.
  6. Send the response back to the requester, and free the buffer.
  7. Repeat this process indefinitely.

4.3.4 Enhancements and Additions to the NETWRKIF

This section deals with extensions to the basic elements required to allow a CP/NET server to run under MP/M II. These extensions can increase the capabilities and improve the performance of the basic system.

Network Initialization and Maintenance
The network interface initialization can do much more than get the server processes ready to run. In addition to passing information about the network environment to the server and physical device initialization, the NETWRKIF can interrogate the network environment to identify other nodes in the system, their status, and their resources.

For example, the NETWRKIF network layer software might send out special packets to discover on-line nodes. When other NETWRKIFs and SNIOSs detect these packets, they respond with special routing packets of their own. If these routing messages are carefully designed, each node can build a table of routes to various nodes and mark other nodes as inaccessible.

Once the network has been initialized, a special network communications process intermittently circulates the routing packets. This circulation keeps the network routing information current as nodes go on and off line.

Nodes can be interrogated to identify their system resources for networking. For example, when a process similar to the routing process just described detects the existence of a node, it logs in to the node and sends out a series of dummy select disk messages. According to the error conditions returned, the process can identify the disk drives the node has available. This can also be accomplished by having a network-layer process issue its own select disk calls in response to receiving a special message.

In implementing these schemes, make sure these special messages do not interfere with regular CP/NET traffic. Some provisions are required to ensure that requests are not made to requesters that ignore the requests or mistake them for legitimate responses to previous requests. You might have to modify the SNIOS to allow it to deal with these strange messages.

Error Handling with Timeouts
Although the transport layer software of a CP/NET system is probably extremely reliable, and the possibility of garbled messages can be ignored, network data-link errors are likely in the long run. Section 3.2.2 includes a general discussion of error handling. This section details a specific error-handling implementation, using timeouts.

Once the data-link software sends a message, it waits for an acknowledgment that the message was received. If no acknowledgment arrives, a timeout is triggered and the message is retransmitted.

You can implement a watchdog timeout mechanism as an interrupt service routine or as a process. When the transport process requests transmission from the data-link software, the process initializes a timeout variable and then waits on a flag. If the watchdog routine is implemented as an interrupt service routine (ISR), it decrements the timeout variable as a multiple of the clock interrupt frequency. If the watchdog routine is implemented as an extremely high priority process, it simply decrements the variable and then executes the MP/M II delay function for a fixed number of cycles.

With either method, a timeout status and the flag on which the transport process is waiting are set if the timeout variable is decremented to zero. At the same time, the data-link software sets the same flag and a transmission success status if it receives an acknowledgment.

When the transport process resumes processing after the flag wait operation, it checks the status variable to see which event occurred first. If the transmission timed out, the process attempts to retransmit. If the transmission succeeded, the transport process Continues.

There are many variations to this method. The preceding one assumes that the message is transmitted with no handshake or initial signal to the receiver that a message is about to follow. If a handshake is implemented, it might require a timeout of its own. Several timeout points might have to be set throughout a single message, depending on how the receiver intends to acknowledge that message.

Other error conditions can occur; they can be integrated into the error-handling structure described above. For example, the receiver can transmit a negative acknowledgment, indicating that the message was received but that it was garbled. In this case, the data-link software need only set the same event flag, but instead of setting a message received status, it sets a transmit error variable. The transport process must now differentiate between three statuses rather than two when it resumes execution, but the overall structure is the same. The architecture required to implement timeouts is shown in Figure 4-10.

Figure 4-10
Figure 4-10. Implementing Timeouts with Flags
Store-and-Forward Networks
In some networks, the NETWRKIF can receive a message destined for another node that the sender could not reach directly. For these networks, implement network layer software to check the ultimate destination and send the message out along some other network line. These NETWRKIFs might need some of the following features.

The NETWRKIF might need more message buffers than there are supported requesters. Some messages are actually destined for the server processes resident on the current node, but a potentially high volume of the messages might be headed elsewhere.

The NETWRKIF must have a mechanism for receiving a message and then immediately sending it elsewhere without an intervening Queue Write-Queue Read operation. You can facilitate this type of operation by making the NETWRKIF software highly modular. It is advisable to have both network layer processes and transport layer processes, in addition to the data-link implementation you use. This gives the network layer process exclusive control of the data link layer, simplifying interprocess competition for the data-link resource.

Finally, the network software must have a method of knowing which nodes can be reached through which network lines. This method can be a static, predefined table or a dynamic message-passing scheme like the one described in the preceding "Network Initialization and Maintenance" section.

Dynamic Login Handling
A CP/NET server under MP/M II can handle 16 requesters at a time. Many more physical requester nodes might want to access the server. The source ID byte in the standard CP/NET message allows up to 255 nodes. Theoretically, 254 requesters can be waiting to access one server.

Obviously, it would be useful to have a method whereby a server process can be reused by another requester after its previous owner has logged off, Unfortunately, the information contained in the server configuration table is not sufficient to identify which specific server processes are free and which are in use.

To solve this problem, define one requester control block (RCB) for each requester to be simultaneously supported by the server. The RCB is defined in Table 4-3.

Table 4-3. Requester Control Block
OffsetExplanation
00-00Requester ID, If the control block is not in use, this field is set to FF hex.
01-03Pointer to a particular server's input QCB.
04-05A predefined pointer to byte 6 of this RCB.
06-07A buffer that contains the address of the received message to be handled by this server process.

Notice that this control block is a requester ID that can be matched with an incoming source ID, followed by a user queue control block. With this simple data structure, servers can be dynamically allocated to requesters with the following algorithm:

  1. Receive a message.
  2. Scan the RCBs for a match between the source ID of the message and the requester ID field of the RCB.
  3. If a match is found, write the message buffer address into the RCB's message buffer address field in bytes 6 and 7. Then write to the queue, using the RCB's internal UQCB.
  4. If a match is not found, but the scan reveals a free RCB (indicated by a requester ID field of FF) , and the incoming message is a login, then flag the RCB in use by writing the message's source ID into the RCB; update the message buffer address field; and write to the queue.
  5. If a match is not found and the message is not a login, send a message back to the requester with extended error 12, requester not logged in.
  6. If a match is not found, and there are no free RCBs, and the message is a login, send a message back indicating login failed.
  7. When a response message is read from the queue and the message is a logoff, then free the appropriate RCB before sending the message back to the requester.

This algorithm still does not allow more than 16 requesters to be logged in at the same time. But the algorithm does permit more than 16 requester nodes to compete for access to the server node. When more than 16 requester nodes log in, they receive login failed messages. These requester nodes cannot access the server until another requester logs off. In this kind of network it is advisable to implement an automatic logoff feature for requesters that have not used the network for a fixed period of time.

Handling Special Messages
Special messages exchange network maintenance information between nodes. These messages have almost unlimited uses. For example, you can define a special message format for a special feature, high-performance print spooler. Once the format has been implemented, custom application packages can access it using Function 66 (Send Message on Network).

There are two basic steps to processing special message formats. First, the transport processes must be able to recognize special message formats and prevent them from entering the server processes. Second, the transport processes must have an interface to pass the messages off for special processing.

The first step can be accomplished by defining additional codes in the format field of the standard CP/NET message. When the transport software recognizes a strange format, it takes the appropriate action. If the message does not contain the standard CP/NET header, the data-link software can recognize this fact and notify the transport layer.

The problem of what to do with the message once it has been recognized can be solved using the same methods that are used for transporting messages throughout the more normal portions of the NETWRKIF. For example, the special print spooler and the transport layer can communicate via a predefined queue.

Some special formats require a logical response message. Functions 66 and 67 are intentionally exempt from the standard logical protocol of CP/NET. If a logical acknowledgment is required, then the transport layer must know how to accept it from the defined interface. Otherwise, the transport layer can forget that the special message occurred.

Bank-switched NETWRKIF Modules
Because of the size of the SERVER.RSP and NETWRKIF.RSP modules in a CP/NET server, MP/M II servers usually need more common memory than is available on the server system. Because of this, CP/NET users can use only one bank of their systems, completely wasting additional banks that might be used to run auxiliary processes or as additional disk buffer.

However, you can reduce the common memory requirements of an RSP by breaking it into two modules. One, still named a resident System process, contains only the code and data that must reside in Common memory to allow MP/M II to work. The rest of the module is reformatted and placed in a banked resident system process (BRS) that can be banked out when it is not executing, allowing its address space to be used by another process.

Process Descriptors and queue control blocks are the only sections of the server code that must reside in common memory. Prepare source module containing the XDOS entry point, all transport Process Descriptors, area for server Process Descriptors, all the NETWRKQIx QCBs, and all NTWRKQOx QCBs.

The first NETWRKIF Process Descriptor still must be allocated immediately after the XDOS entry point for the module, at relative address 0002H. However, this Descriptor's memory segment value should be 0FFH identifying that a BRS module is associated with it.

If any other processes exist in the NETWRKIF--for example, watchdog timeout processes--their Process Descriptors must also be included in this module. Assemble this source module and link it into RSP format. Name the object module <netprocess>.RSP where <netprocess> is the name of the first Process Descriptor in the module.

Then use the main body of the NETWRKIF source module to form a second source module. Remove all Process Descriptors and QCBs and place the following header at relative location 0:

rsp$adr:        ds 2            ;address of associated RSP
stk$adr:        dw stk$top      ;top of stack containing entry point
brs$name:       db '<netprocess>'

where stk$top is the address of the top of the stack for the first process, and <netprocess> matches the name of the associated RSP. This is the standard format for a BRS module; it is described in more detail in the MP/M II Operating System System Guide.

Because the Process Descriptors and queue control blocks are in a completely separate RSP, they cannot be resolved as simple externals. They must be defined in terms of known offsets from the beginning of <netprocess>.RSP. At run-time, the variable rsp$adr contains a pointer to the beginning of this RSP, placed there by MP/M II's GENSYS utility. Using this pointer and the predefined offsets, required references to these data objects can be resolved.

On startup, the NETWRKIF processes perform the following initialization:

  1. Initialize the stack pointer fields in all NETWRKIF Process Descriptors with a pointer to the top of the stack associated with each process. This is not necessary for the first process because GENSYS provides the stack pointer linkage via the header data in the BRS.
  2. The make queue operations the NETWRKIF requires can be complicated because the QCB addresses must be resolved. Once they are, however, update the UQCBs associated with them with those addresses, avoiding the necessity of performing open queue functions.

The NETWRKIF.BRS module requires a different way of referencing the operating system because it does not contain a pointer to the XDOS entry point. The RSP associated with the BRS module, however, does contain such a pointer as its first two bytes. The following subroutine performs operating system calls transparently:

do$os:  lhld rsp$adr
        mov a,m
        inx h
        mov h,m
        mov l,a
        pchl

you must also assemble this module and link it into RSP format; but name it <netprocess>.BRS.

Banking out the NETWRKIF module alone might raise the BNKXIOS COMMONBASE entry point above the hardware bank-select point, allowing banked operation of MP/M II. If banking out the module does not accommodate this, you can use a patch to convert SERVER.RSP into a banked module in a similar way. The patch is detailed in CP/NET V1.2 Application Note #2, 11/11/82.

Perform GENSYS with a specified banked system. You can add memory segments to occupy the new banks. The address ranges of the new memory segments are prompted for at the end of GENSYS.

If the number of requesters to be supported still requires more common memory than is available, there is no purpose in implementing a banked version of the server.

A banked-out server has a marginally slower response time because the dispatcher must select the system bank and because of the added level of indirection in calling the operating system. This degradation, however, is insignificant.

Although banking out the server provides additional user Segments under MP/M II, resist the temptation to add additional consoles to the system. Because of the extremely high priority at which the server runs, performance on additional consoles is very poor. However, these extra banks do provide the user with a means of performing occasional jobs directly from the MP/M II level. more importantly, extra segments can enhance the server itself by using special CP/NET messages.

4.3.5 MP/M II Performance Factors Affecting the NETWRKIF

The characteristics of the network for which a server is being implemented influence the architecture of the NETWRKIF and the rest of the server software. Another important factor in designing efficient servers under MP/M II is the nature of MP/M II itself. This section points out the overhead MP/M II incurs in implementing multitasking programming environment.

The heart of the MP/M II operating system is its dispatcher. This routine is entered every time a system call is made. The dispatcher protects system resources, tests for events that could influence the execution of any process in the system, and finally chooses the processes to execute and their order. The dispatcher takes roughly 900 microseconds to execute, but interrupts are disabled for no longer than 90 microseconds. This overhead is incurred on every system call.

The limitations of the dispatcher alone place some basic constraints on communications speed. If the network is using a serial I/O device capable of buffering three characters at 10 bits per character, then the NETWRKIF had better not rely on a system call like console input to receive network messages if the transmission rate is faster than 33K bits per second and the sender sends characters as fast as possible. Even below this speed, overruns are likely if there are any other processes in the system. This assumes an extremely simple protocol. If the network has extra signal lines, most serial I/O devices permit the receiver to signal a clear to send condition back to the sender. But networks often must work without these extra signals.

Because interrupts are disabled for no longer than 90 microseconds, a network that works at the character-interrupt level functions properly at transmission speeds up to 333K bits per second. Beyond that speed, overruns are likely to occur too often for adequate performance.

At speeds higher than 333K bits per second, the network interface software can use one of three approaches:

Serial I/O is not the only possible network transmission medium. The example is provided to acquaint you with the performance of MP/M II.

The amount of time spent in the dispatcher varies depending on a number of factors. Because the dispatcher must check suspended processes against system events, keep the number of processes, queues, flags, and poll calls to a minimum. Poll calls are especially degrading. Every time the dispatcher is entered, it executes every code fragment associated with every outstanding poll call. If all 16 reentrant NETWRKIF processes polled output ports at once, the dispatcher would be very busy. In fact, enough poll calls can lengthen the dispatcher's execution time so much that it exceeds the clock interval. When this happens, the dispatcher is reentered before it has even been exited.

The design of interrupt service routines Must take the structure of the dispatcher into account. ISRs must first of all save the register image of the process they interrupted--the service routine then executes. When the ISR terminates itself, it should restore the interrupted process's registers and take one of two actions:

ISRs should perform no MP/M II system calls except for the Flag Set function. There are two reasons for this. First, ISRs are not processes, so the dispatcher has no way of saving the status of the ISR in a Process Descriptor before allowing the function to be performed. Second, the dispatcher reenables interrupts and possibly dispatches another process, leaving the ISR and the interrupted process in an indeterminate state. The Flag Set function is specifically recognized by the dispatcher to avoid dispatching a different process.

Several factors determine how often the NETWRKIF and server processes are dispatched. The most obvious is, once again, the number of processes. If MP/M II must share the CPU with more tasks, there is less CPU available. Consider the priority of the various network server processes carefully. All processes in the SERVER module run at a high priority level of 100. processes in the NETWRKIF might require other priorities. In general, assign compute-bound processes lower priorities than I/0-bound processes, to prevent processes that perform few system calls from hogging the CPU.

The dispatcher always schedules processes according to priority. Improperly setting priorities can cause processes to be permanently suspended. For example, consider a NETWRKIF module that performs all direct I/O and busy-waits for network input. Suppose this process has a priority of 60, slightly higher than the server processes. Although the dispatcher is entered every time the system clock ticks, the NETWRKIF is ready. Because the NETWRKIF has a higher priority than the server processes, the server processes never execute.

Note that because of the extremely high priority of the server process, normal user processes running under MP/M II perform very poorly. In addition, the extra process load degrades the server performance. It is recommended that normal work station terminals not be provided on an MP/M II system that is functioning as a server, although a system console can be convenient for monitoring system performance and giving the operator a means of maintaining the server's data base.

The last factor affecting the dispatch rate is the system clock frequency. Every time a clock tick occurs, the dispatcher is entered and recomputes the process to be executed next. Processes of equal priority are dispatched on a first come, first served basis. The system clock can be tuned for optimal network performance . There are no general rules on tuning because each network and the applications run on the network determine the optimal clock period. Experiment with the clock frequency to determine the best performance for the server.

In addition to designing the NETWRKIF for the server system, you might want to reexamine the XIOS used in the system. Many CP/NET users discover that once their communications system has been optimized, server performance has improved only slightly because several requesters are forcing the disk system to thrash.

Thrashing can be minimized if the XIOS is provided with efficient blocking/deblocking algorithms like those discussed in the MP/M II Operating System System Guide. These algorithms buffer disk accesses, deferring physical Read-Write operations until they are absolutely necessary. As a result, many file record Read-Write operations occur at memory speed, instead of having to wait for physical I/O from a disk drive.

Extra blocking/deblocking buffers can also improve overall server performance enormously. Because a dedicated server only requires a single tiny user program segment, or, in some cases, no user segment at all, almost all additional memory remaining after the server has been implemented can be used for disk buffers. In a bank-switched or memory-managed system, potentially huge amounts of memory can be made available for disk buffers. Providing one or more disk buffers per supported requester potentially eliminates competition between two requesters for buffer resources.

Another way to improve disk performance with limited memory for disk buffers is to limit the number of transactions that can be present in the server at one time. Even if a server is supporting 16 requester sessions, it is possible, for example, to permit only four or five messages to be active at a time. This limit reduces the amount of competition between actual processes, although competition continues between individual transactions. Quite often, however, the overhead incurred by refusing network messages and forcing requesters to retransmit them is considerably less than the overhead incurred by repeatedly having to flush disk buffers for use and reuse by individual processes.

You can estimate the average number of disk accesses an application program is likely to perform in a short time. The NETWRKIF processes can then selectively transport messages from only one requester for a short amount of time, then service another requester for an equal amount of time. The scheme allows a single process to take maximum advantage of the blocking and deblocking algorithms implemented in the server's XIOS. The major disadvantage of such a scheme is that it is extremely complex and difficult to implement efficiently. Carefully tuned, however, it can greatly improve performance.

4.3.6 Generating the NETWRKIF

To create the MP/M II server, perform the following steps:

  1. If the XIOS has been modified, generate a new version of RESXIOS.SPR or BNKXIOS.SPR or BNKXIOS.SPR, according to the instructions provided in the MP/M II Operating System System Guide.
  2. Assemble and link the NETWRKIF module:
            A>RMAC NETWRKIF
            A>LINK NETWRKIF[NR,OR]
               

    The linker generates the NETWRKIF.RSP file.

    If RMAC and LINK are not available, you must use ASM, PIP, and GENMOD, as shown below:

    Assemble with ORG 0000H.
           A>ASM NETWORKIF
           A>REN NTWRK0.HEX=NETWRKIF.HEX
               
    Now edit the NETWRKIF.ASM ORG statement to locate the module at 100 hex. Assemble with ORG 0100H.
            A>ASM NETWRKIF
            A>REN NTWRK1.HEX=NETWRKIF.HEX
               
    Concatenate the HEX files.
            A>PIP NETWRKIF.HEX=NTWRKO.HEX,NTWRK1.HEX
               
    Generate the NETWRKIF RSP file.
            A>GENMOD NETWRKIF.HEX NETWRKIF.RSP
               
  3. Copy the following files to the server boot disk.

  4. Perform a GENSYS on the MP/M II system. The GENSYS must include the SERVER.RSP file and the customized NETWRKIF.RSP; it can also include the SPOOL.RSP.

When GENSYS asks for the number of consoles, do not include the consoles (character I/O drivers) that support the requesters. Usually, the response is 1.

You must also configure the file system for the types of applications CP/NET runs, enable compatibility attributes, if necessary, and so on. These issues are discussed in the MP/M II Operating System System Guide.

4.3.7 Debugging the NETWRKIF

The MP/M II server is now ready to be debugged. There are three general strategies for debugging the server.

Debugging MP/M II Under CP/M
To debug MP/M II under CP/M, follow these steps:

  1. GENSYS the MP/M II with the top of memory set below where a CP/M system running on the same hardware would reside when it is running DDT, SID, or ZSID.
  2. Boot up CP/M on the server target computer system.
  3. Run MPMLDR under the debugger. You can halt the loader just before passing control to MP/M II through the following sequence:

            A>DDT MPMLDR.COM
            *I$B
            *G
               

    When the loader breaks, you can insert breakpoints and restart the loader.

When using this method, remember that, because CP/M is a single-tasking operating system, the entire CP/M operating system becomes part of the process in which a breakpoint is inserted every time the system encounters a breakpoint. Furthermore, DDT and SID reenable interrupts on breakpoints. If a clock tick goes off, the MP/M II dispatcher is likely to suspend CP/M and continue with other processing. This might not inconvenience you because the process that was breakpointed is also suspended. If it does affect the operation of the system, you might have to disable the system clock.
Debugging the NETWRKIF as a COM file
The example in Appendix E is set up to debug the NETWRKIF as a COM file. Debugging instructions are also included in Appendix E.
Inserting Trace Code Into the NETWRKIF
Gather run-time statistics by inserting trace code into the NETWRKIF. Although this is not very helpful for debugging real-time problems, it is the least destructive method of gathering real-time statistics. This method can also be useful when tuning the network for increased performance.

4.4 Implementing Non-MP/M II Servers

It is possible to implement a CP/NET server on any computer system, under any operating system. There are several reasons why you might choose another operating system:

The module SERVER.RSP cannot be used on a different processor or under a different operating system. So you must not only create the equivalent of the NETWRKIF for the target computer system; you must also write the logical portion of the server.

The server processes under MP/M II act essentially as a proxy for the requester assigned to them. For example, the requester wants to open a file on a networked drive but it does not have access to the operating system controlling that drive. Instead, the requester sends a message to a server process that does have direct access to the controlling operating system and asks that process to open the file for the requester. The server obligingly performs the operation for the requester and tells it what happened. This is often referred to as a ghosted process model of a server because the operating system thinks it is running the entire application program as a process, while in fact the application is running somewhere else, but has a friend to help out.

Using the logical messages included in this manual, you can write a ghosted process server for CP/NET under almost any multitasking operating system. You can even write a CP/NET server under a single-tasking operating system. (CP/NET servers have actually been implemented under CP/M.)

The basic elements of such a server are

Each of these functional modules varies depending on the environment under which it is forced to execute. The communications interface is governed by the types of process architectures the target operating system can support. The remaining modules can be a set of reentrant processes, as they are under MP/M II, or they can be a single process that keeps track of the requester it is currently servicing. If the latter method is used, the server must keep track of such context sensitive information as directory search first/search next information and shared files.

It might not be possible to support all CP/M functions under a non-MP/M II server. If this is the case, choose applications that do not require the use of the unsupportable functions.

Finally, it might be necessary to have several different computer systems and operating systems acting as servers in the same network. It is best to make the server implementation as portable as possible. Implementing the server in a high-level language is a first step to portability.

Making the system highly modular can improve its portability. For example, break the communications interface into a hardware interface module, a data link module, a network module, and a transport module. All of these modules, with the exception of the hardware interface, can port to different systems with minimal modification.

The server's function interpreter should be completely portable, but you will probably have to rewrite the file system interpreter and the operating system interface modules.


Appendix A
CP/NOS Overview

A.1 overview

CP/NOS is a version of the CP/M operating system that performs all file handling across a CP/NET network system. CP/NOS supports one local console and one local printer, but it supports only remote mass storage media. Because of this, the BDOS and BIOS modules in a CP/NOS system are considerably smaller than their counterparts in a standard CP/M system. This allows CP/NOS to fit in a fairly small (usually 4K bytes) Read-Only memory, so you do not need a bootstrap loader. CP/NOS can also be downloaded from a server. Using a small loader, you can also download a CP/NOS system from a centralized server.

Programs written under any CP/M 2.x system are fully compatible with a comparable CP/NOS system, provided that mass storage devices referenced by the application are available across the network. When BDOS calls that service, these devices are automatically translated into network functions.

Unlike CP/NET, CP/NOS cannot be loaded under an existing CP/M system. The network modules and CP/M modules must be linked together and executed in a stand-alone environment. The special problems this creates in debugging CP/NOS are discussed in this appendix.

A.2 System Requirements

CP/NOS can run on an 8080, 8085, or Z80 microprocessor, with a maximum of 64K of memory. A usual CP/NOS system can be placed in a 4K ROM.

The CP/NOS requester must be networked to an MP/M II server. The server is the same as the one used by CP/NET. CP/NOS and CP/NET requesters can even be networked to the same server.

A.3 Customizing CP/NOS

Three of the modules incorporated in CP/NOS are system dependent and must be modified to work on a particular hardware configuration. They are the CPBIOS, CPNIOS, and NETWRKIF modules

The CPBIOS can be exactly the same as the BIOS used in a CP/M system that runs on the same hardware, except that only a small portion of the BIOS is required. The only routines required are:

BOOTcold start
CONSTread console status
CONINread console character
CONOUTwrite console character
LISTwrite character to the list device
LISTSTread list device status

The CPBIOS jump vector must be the same as that of a regular BIOS, but all other entry points can be null.

The CPNIOS module takes the place of the SNIOS module in CP/NET and requires only minimal modification. The only difference is that all variables must be initialized upon cold start, including the requester configuration table. The utilities NETWORK and LOGIN are not sufficient to define the configuration table after cold start because CP/NOS has no local disk drives from which to load these utilities. The CPNIOS must also prompt the user for login information upon cold start, or a warm boot results in continuous requester not logged in extended errors as the CP/NOS requester tries to load the file CCP.SPR from a server that has no knowledge of the requester.

The SNIOS example in Appendix E contains a sample CPNIOS, conditionally assembled out. To obtain the CPNIOS version, equate the literal CPNOS to true.

Note: if the two preceding routines are to reside eventually in ROM, all variable data must be contained in data segments and cannot be initialized at run-time. Initializing values must reside in a code segment, and they must be copied down to their corresponding data segment locations at cold start. The assembly of these modules requires an assembler capable of supporting separate code and data segments; the segments must be assembled into REL file format. Use RMAC with 8080 source files.

The NETWRKIF module resides on the server and is identical to the NETWRKIF required to support CP/NET. See Section 4.3 for a discussion of NETWRKIF preparation.

A.4 Building the CP/NOS System

To generate a CP/NOS system ready for insertion into ROM, follow these steps:

  1. Assemble the modules CPBIOS and CPNIOS.
  2. Link the following modules together in the order shown, using LINK-80:

    CPNOS, CPNDOS, CPNIOS, CPBDOS, CPBIOS

    Locate the code segment where the ROM sits in the address space of the finished system. At least 1K (400 hexadecimal bytes) of RAM must be allocated for data segments. If the code segments are to be loaded into high memory (at F000H for a 4K system), data must be explicitly linked, using the D option, at least 1K in front of the code segments. For example,

            A>LINK CPNOS,CPNDOS,CPNIOS,CPBDOS,CPBIOS[LF000,DEC00]
               
These two steps produce an executable CP/NOS, capable of being programmed into ROM. At this stage, however, the system cannot be debugged from CP/M.

A.5 Debugging the System

You can create a version of CP/NOS that can be cold started from CP/M if a CP/M system with 64K RAM is available. First, type the following commands:

        A>RMAC CPNIOS
        A>RMAC CPBIOS
        A>LINK CPNOS,CPNDOS,CPNIOS,CPBDOS,CPBIOS[LF000,DEC00]
        A>GENHEX MVCPN0S 0100
        A>GENHEX CPNOS 0200
        A>PIP LDCPNOS.HEX=MVCPNOS.HEX[I],CPNOS.HEX[H]
        A>LOAD LDCPNOS

This procedure produces a file LDCPNOS.COM that is directly executable from CP/M. LDCPNOS relocates the CPNOS module to location F000H and passes control to it, destroying CP/M and replacing it with CP/NOS.

Because CP/M is destroyed by this procedure, it is not advisable to run LDCPNOS under software debugger like DDT or SID, although you can run LDCPNOS under an in-circuit emulator. To run CP/NOS under DDT or SID, use the following procedure:

  1. Link CPNOS so that all code and data reside below the address specified as END when the debugger is brought up:

            A>LINK CPNOS,CPNDOS,CPNIOS,CPBDOS,CPBIOS[L<org>,D<org-400H>]
               

    where <org> is the link origin.

  2. A>DDT CPNOS.COM
  3. Relocate CPNOS from location 100, where DDT loads it, to its link origin:

            -M100,<100+next-1>,<org>
               

    where next is the field specified by NEXT when the debugger loads CPNOS.COM, and <org> is the link origin.

  4. Begin execution with appropriate diagnostics:

            -G<org>
               

    where <org> is the link origin.


Appendix B
CP/NET 1.2 Standard Message Formats

FMTDIDSIDFNCSIZMSG
Figure B-1. CP/NET 1.2 Logical Message Format
  • FMT = Message format code
  • DID = Message destination processor ID
  • SID = Message source processor ID
  • FNC = MP/M function code
  • SIZ = Data field length - 1
  • MSG = Actual message, SIZ + 1 bytes long

Table B-1. Message Field Length Table
FMT
CODE
FMTDIDSIDFNCsizMSGComment
00111111-256Preferred format
01111111-256Returned result
02111121-65536
03111121-65536Returned result
04122111-256
05122111-256Returned result
06122121-65536
07122121-65536Returned result

Appendix C
CP/NET 1.2 Logical Message Specifications

Messages for all CP/NET functions are defined in this appendix. These messages are logical messages. Any implementation of the SNIOS or NETWRKIF modules must always present messages to the NDOS or SERVER modules in the form presented here.

You must adhere to these formats when implementing a server that runs under an operating system other than MP/M II.

Notes:
  • ss = Server ID
    rr = Requester ID
    xx = Don't care byte
    nn = Value specified
  • All numeric values are in hexadecimal.
  • All functions capable of returning extended errors are marked *EE*. Extended errors are assumed whenever the response is two bytes in length, with the following message format:

    SIZ = 01
    MSG(0) = FF
    MSG(1) = Extended Error Code

    Any response with SIZ = 01 is interpreted as an error, regardless of the value in MSG(0).

  • Any message can return the server not logged in or function not implemented on server extended error, extended error 0C.

For functions that return with the user's FCB updated (messages that have an FCB in their response), the first byte of the FCB (drive designator) is never copied back from the response message. In some cases, the random record bytes are also not copied back.

For search functions, the entire directory entry (which is NOT an FCB) is copied back to the current DMA buffer, into the position indicated by the Directory Code result byte. This means that the DMA buffer is not the actual directory sector from the disk, but merely an accumulation of directory entries in an order determined by how they were found.

Functions that return the address of a system resource (Get Allocation Vector, Get DBP, Get Server Config), the data is kept in an NDOS buffer which is overwritten on subsequent calls. The user must copy data out as needed. The NDOS guarantees that the low byte of the address is never 0FFH, so that a valid address can be distinguished from an Extended Error Code.

For functions that return a drive vector (Get Login, Get R/O), there is no way to distingush between valid vectors and errors. The message format does not allow for errors, and the NDOS ignores the possibility of an error in the response. The actual vector returned to the user is a composite of data retrieved from all known servers and the local BDOS.

Table C bgcolor=lightyellow-1. Conventional CP/NET Messages
FMTDIDSIDFNCSIZMSG
0 SYSTEM RESET:
NOT IMPLEMENTED AT SERVER
00ssrr0000
  • 00-00 = xx
01rrss0001
  • 00-00 = 0FFh
  • 01-01 = 00Ch
1 CONSOLE INPUT:
NOT IMPLEMENTED AT SERVER
00ssrr0100
  • 00-00 = xx
01rrss0101
  • 00-00 = 0FFh
  • 01-01 = 00Ch
2 CONSOLE OUTPUT:
NOT IMPLEMENTED AT SERVER
00ssrr0200
  • 00-00 = xx
01rrss0201
  • 00-00 = 0FFh
  • 01-01 = 00Ch
3 RAW CONSOLE INPUT:
00ssrr0300
  • 00-00 = Server Console #
01rrss0300
  • 00-00 = Character Input
4 RAW CONSOLE OUTPUT:
00ssrr0401
  • 00-00 = Server Console #
  • 01-01 = Character to Output
01rrss0400
  • 00-00 = 00
5 LIST OUTPUT:
00ssrr05nn
  • 00-00 = Server List #
  • 01-nn = Characters to List Device (nn = 01 to 80)
01rrss0500
  • 00-00 = 00
6 DIRECT CONSOLE 1/0:
NOT IMPLEMENTED AT SERVER
00ssrr0600
  • 00-00 = xx
01rrss0601
  • 00-00 = 0FFh
  • 01-01 = 00Ch
7 GET I/O BYTE:
NOT IMPLEMENTED AT SERVER
00ssrr0700
  • 00-00 = xx
01rrss0701
  • 00-00 = 0FFh
  • 01-01 = 00Ch
8 SET 1/0 BYTE:
NOT IMPLEMENTED AT SERVER
00ssrr0800
  • 00-00 = xx
01rrss0801
  • 00-00 = 0FFh
  • 01-01 = 00Ch
9 PRINT STRING:
NOT IMPLEMENTED AT SERVER
00ssrr0900
  • 00-00 = xx
01rrss0901
  • 00-00 = 0FFh
  • 01-01 = 00Ch
10 READ CONSOLE BUFFER:
NOT IMPLEMENTED AT SERVER
00ssrr0A00
  • 00-00 = xx
01rrss0A01
  • 00-00 = 0FFh
  • 01-01 = 00Ch
11 GET CONSOLE STATUS:
00ssrr0B00
  • 00-00 = Server Console #
01rrss0B00
  • 00-00 = Console Status Byte
12 RETURN VERSION NUMBER:
NOT IMPLEMENTED AT SERVER
00ssrr0C00
  • 00-00 = xx
01rrss0C01
  • 00-00 = 0FFh
  • 01-01 = 00Ch
13 RESET DISK SYSTEM:
NOT IMPLEMENTED AT SERVER
00ssrr0D00
  • 00-00 = xx
01rrss0D01
  • 00-00 = 0FFh
  • 01-01 = 00Ch
14 SELECT DISK: *EE*
00ssrr0E00
  • 00-00 = Selected Disk
01rrss0E00
  • 00-00 = Return Code
15 OPEN FILE: *EE*
00ssrr0F2C
  • 00-00 = User Number
  • 01-24 = FCB
  • 25-2C = Password
01rrss0F24
  • 00-00 = Directory Code
  • 01-24 = FCB
01 not copied to user; 22-24 (file ID)
not copied to user unless F5' set and not F6'.
16 CLOSE FILE: *EE*
00ssrr102C
  • 00-00 = User Number
  • 01-24 = FCB
  • 25-2C = Password (ignored)
01rrss1024
  • 00-00 = Directory Code
  • 01-24 = FCB
01 not copied to user; 22-24 (file ID)
not copied to user unless F5' set and not F6'.
17 SEARCH FOR FIRST: *EE*
00ssrr1125
  • 00-00 = Current Disk if FCB(0)='?'
  • 01-01 = User Number
  • 02-25 = FCB
01rrss1120
  • 00-00 = Directory Code
  • 01-20 = Directory Entry
Directory Entry copied to current DMA buffer
based on Directory Code.
80/0
  • 00-00 = Directory Code
  • 01-80 = Directory Sector
  • 00-00 = Directory Code
Extensions for "full search" mode, incl. CP/M 3.
18 SEARCH FOR NEXT: *EE*
00ssrr1201
  • 00-00 = xx
  • 01-01 = User Number
01rrss1220
  • 00-00 = Directory Code
  • 01-20 = Directory Entry
Directory Entry copied to current DMA buffer
based on Directory Code.
80/0
  • 00-00 = Directory Code
  • 01-80 = Directory Sector
  • 00-00 = Directory Code
Extensions for "full search" mode, incl. CP/M 3.
19 DELETE FILE: *EE*
00ssrr1324
  • 00-00 = User Number
  • 01-24 = FCB
01rrss1300
  • 00-00 = Directory Code
20 READ SEQUENTIAL: *EE*
00ssrr1424
  • 00-00 = User Number
  • 01-24 = FCB
01rrss14A4
  • 00-00 = Return Code
  • 01-24 = FCB
  • 25-A4 = Sector of Data Read
01 and 22-24 not copied to user.
21 WRITE SEQUENTIAL: *EE*
00ssrr15A4
  • 00-00 = User Number
  • 01-24 = FCB
  • 25-A4 = Sector of Data to Write
01rrss1524
  • 00-00 = Return Code
  • 01-24 = FCB
01 and 22-24 not copied to user.
22 MAKE FILE: *EE*
00ssrr1624
  • 00-00 = User Number
  • 01-24 = FCB
01rrss1624
  • 00-00 = Directory Code
  • 01-24 = FCB
01 not copied to user; 22-24 (file ID)
not copied to user unless F5' set and not F6'.
23 RENAME FILE: *EE*
00ssrr1724
  • 00-00 = User Number
  • 01-24 = FCB in RENAME format
01rrss1700
  • 00-00 = Directory Code
24 RETURN LOGIN VECTOR:
Message Sent for Each Remote Drive, Results combined with local BDOS
00ssrr1800
  • 00-00 = xx
01rrss1801
  • 00-01 = Login Vector
25 RETURN CURRENT DISK:
NOT IMPLEMENTED AT SERVER
00ssrr1900
  • 00-00 = xx
01rrss1901
  • 00-00 = 0FFh
  • 01-01 = 00Ch
26 SET DMA ADDRESS:
NOT IMPLEMENTED AT SERVER
00ssrr1A00
  • 00-00 = xx
01rrss1A01
  • 00-00 = 0FFh
  • 01-01 = 00Ch
27 GET ALLOCATION VECTOR ADDRESS: *EE*
00ssrr1B00
  • 00-00 = Current Disk
01rrss1B02-FF
  • 00-FF = Allocation Vector
NDOS guarantees low byte of
returned address cannot be FF.
28 WRITE PROTECT DISK: *EE*
00ssrr1C00
  • 00-00 = Current Disk
01rrss1C00
  • 00-00 = 00
29 GET R/O VECTOR:
Message Sent for Each Remote Drive, Results combined with local BDOS
00ssrr1D00
  • 00-00 = xx
01rrss1D01
  • 00-01 = R/O Vector
30 SET FILE ATTRIBUTES: *EE*
00ssrr1E24
  • 00-00 = User Number
  • 01-24 = FCB with File Attributes Set
01rrss1E00
  • 00-00 = Directory Code
31 GET DISK PARAMETER ADDRESS: *EE*
00ssrr1F00
  • 00-00 = Current Disk
01rrss1F0F
  • 00-0F = Disk Parameter Block
32 SET/GET USER CODE:
NOT IMPLEMENTED AT SERVER
00ssrr2000
  • 00-00 = xx
01rrss2001
  • 00-00 = 0FFh
  • 01-01 = 00Ch
33 READ RANDOM: *EE*
00ssrr2124
  • 00-00 = User Number
  • 01-24 = FCB
01rrss21A4
  • 00-00 = Return Code
  • 01-24 = FCB
  • 25-A4 = Sector of Data Read
01 not copied to user.
34 WRITE RANDOM: *EE*
00ssrr22A4
  • 00-00 = User Number
  • 01-24 = FCB
  • 25-A4 = Sector of Data to Write
01rrss2224
  • 00-00 = Return Code
  • 01-24 = FCB
01 not copied to user.
35 COMPUTE FILE SIZE: *EE*
00ssrr2324
  • 00-00 = User Number
  • 01-24 = FCB
01rrss2324
  • 00-00 = Return Code
  • 01-24 = FCB
36 SET RANDOM RECORD: *EE*
00ssrr2424
  • 00-00 = User Number
  • 01-24 = FCB
01rrss2424
  • 00-00 = Return Code
  • 01-24 = FCB
01 not copied to user.
37 RESET DRIVE: *EE*
Message Sent to All Affected Servers
00ssrr2501
  • 00-01 = Drive Vector
01rrss2500
  • 00-00 = Return Code
38 ACCESS DRIVE: *EE*
Message Sent to All Affected Servers
00ssrr2601
  • 00-01 = Drive Vector
01rrss2600
  • 00-00 = Return Code
39 FREE DRIVE: *EE*
Message Sent to All Affected Servers
00ssrr2701
  • 00-01 = Drive Vector
01rrss2700
  • 00-00 = Return Code
40 WRITE RANDOM WITH ZERO FILL: *EE*
00ssrr28A4
  • 00-00 = User Number
  • 01-24 = FCB
  • 25-A4 = Sector of Data to Write
01rrss2824
  • 00-00 = Return Code
  • 01-24 = FCB
01 not copied to user.
42 LOCK RECORD: *EE*
00ssrr2A26
  • 00-00 = User Number
  • 01-24 = FCB
  • 25-26 = File ID
01rrss2A24
  • 00-00 = Return Code
  • 01-24 = FCB
01 not copied to user.
43 UNLOCK RECORD: *EE*
00ssrr2B26
  • 00-00 = User Number
  • 01-24 = FCB
  • 25-26 = File ID
01rrss2B24
  • 00-00 = Return Code
  • 01-24 = FCB
01 not copied to user.
45 SET BDOS ERROR MODE:
NOT IMPLEMENTED AT SERVER
00ssrr2D00
  • 00-00 = xx
01rrss2D01
  • 00-00 = 0FFh
  • 01-01 = 00Ch
46 GET DISK FREE SPACE:
Extension for CP/M 3.
00ssrr2E00
  • 00-00 = Drive
01rrss2E02
  • 00-02 = Free Space, little-endian
Copied to current DMA buffer.
48 FLUSH BUFFERS:
Extension for CP/M 3.
00ssrr3000
  • 00-00 = Purge Code
01rrss3000
  • 00-00 = Return Code
64 LOGIN: *EE*
00ssrr4007
  • 00-07 = Password, 8 ASCII Chars
01rrss4000
  • 00-00 = Return Code
65 LOGOFF: *EE*
00ssrr4100
  • 00-00 = xx
01rrss4100
  • 00-00 = Return Code
66 SEND MESSAGE ON NETWORK:
NOT IMPLEMENTED AT SERVER
00ssrr42xx
  • 00-FF = xx
01rrss4201
  • 00-00 = 0FFh
  • 01-01 = 00Ch
67 RECEIVE MESSAGE ON NETWORK:
NOT IMPLEMENTED AT SERVER
00ssrr4300
  • 00-00 = xx
01rrss4301
  • 00-00 = 0FFh
  • 01-01 = 00Ch
68 GET NETWORK STATUS:
NOT IMPLEMENTED AT SERVER
00ssrr4400
  • 00-00 = xx
01rrss4401
  • 00-00 = 0FFh
  • 01-01 = 00Ch
69 GET CONFIGURATION TABLE ADDRESS:
NOT IMPLEMENTED AT SERVER
00ssrr4500
  • 00-00 = xx
01rrss4501
  • 00-00 = 0FFh
  • 01-01 = 00Ch
70 SET COMPATIBILITY ATTRIBUTES:
Message Sent to All Known Servers
00ssrr4600
  • 00-00 = Compatibility Attributes
01rrss4600
  • 00-00 = xx
71 RETURN SERVER CONFIGURATION: *EE*
00ssrr4700
  • 00-00 = xx
01rrss4716
  • 00-00 = Server Temporary File Drive
  • 01-01 = Server Status Byte
  • 02-02 = Server ID
  • 03-03 = Maximum Number of Requesters
  • 04-04 = Number Logged In
  • 05-06 = Login Vector
  • 07-16 = Requester ID's
98 FREE BLOCKS:
Extension for CP/M 3.
00ssrr6200
  • 00-00 = xx
01rrss6200
  • 00-00 = Return Code
99 TRUNCATE FILE: *EE*
Extension for CP/M 3.
00ssrr6324
  • 00-00 = User Number
  • 01-24 = FCB
01rrss6324
  • 00-00 = Directory Code
  • 01-24 = FCB
101 GET DIRECTORY LABEL BYTE:
Extension for CP/M 3.
00ssrr6500
  • 00-00 = Drive
01rrss6500
  • 00-00 = Dir Mode Byte
102 GET FILE TIME STAMPS: *EE*
Extension for CP/M 3.
00ssrr6624
  • 00-00 = User Number
  • 01-24 = FCB
01rrss6624
  • 00-00 = Directory code
  • 01-24 = FCB with timestamps in d8-d15
105 GET DATE AND TIME:
Extension for CP/M 3. Not issued by NDOS.
00ssrr6900
  • 00-00 = xx
01rrss6904
  • 00-01 = Date, days since 12/31/1977
  • 02 = Hours (BCD)
  • 03 = Minutes (BCD)
  • 04 = Seconds (BCD)
106 SET DEFAULT PASSWORD:
Message Sent to All Known Servers
00ssrr6A07
  • 00-07 = Default Password to be Set
01rrss6A00
  • 00-00 = Return Code

Appendix D
NDOS Function Summary

Table D-1. NDOS Functions
CodeFunction NameInput ParametersOutput Results
38Access DriveDE = Drive Vectornone
39Free DriveDE = Drive Vectornone
42Lock RecordDE = FCB AddressA = Err Code
43Unlock RecordDE = FCB AddressA = Err Code
45Set BDOS Error ModeE = Error Modenone
64Loginsee definitionA = Err Code
65LogoffE = Server IDnone
66Send Message on NtwrkDE = Message AdrA = Err Code
67Receive Msg from NtwkDE = Message AdrA = Err Code
68Get Network StatusnoneA = Status byte
69Get Config Table AdrnoneHL = Table Adr
70Set Compat. Attrs.E = attributesnone
71Get Server Config.E = Server IDHL= Table Adr
106Set Default Passwordsee definitionnone


Appendix E
A Simple RS-232C CP/NET System

Digital Research developed a relatively simple RS-232C point to-point protocol to provide a demonstration vehicle for CP/NET and to encourage compatibility among hardware vendors. The protocol, as implemented in the sample SNIOS and NETWRKIF in this appendix, breaks the logical message into a fixed header and a variable length data portion the size of which is obtained from the fixed header. This simplifies operation with DMA channels that need terminal counts and also provides a checksum for the header that contains the SIZ field.

This protocol can be implemented between any requester and server that support an extra RS-232 console port.

E.1 Protocol Handshake

The protocol handshake is detailed in Figure E-1.

Figure E-1
Figure E-1. Protocol Handshake

E.2 Binary Protocol Message Format

Data integrity for this protocol is maintained by a simple checksum, shown in Figure E-2, on both the header and the actual message.

Figure E-2
Figure E-2. Binary Protocol Message Format

Message format codes 00 & 01 are recommended.

Field Description:

E.3 ASCII Protocol Message Format

If the RS-232 link is not capable of transmitting 8-bit binary data, you might have to transmit each nibble of the message as a 7 bit ASCII character.

Note: the 7-bit ASCII network protocol is identical to the 8-bit protocol except that it requires twice as many bytes because each byte is transmitted in hexadecimal ASCII format.

The ASCII network protocol message format is detailed in Figure E-3.

Figure E-3
Figure E-3. ASCII Protocol Message Format

Message format codes 00 & 01 are recommended.

Field Description:

E.4 Modifying the SNIOS

The sample SNIOS can be modified for almost any requester that has a spare console port. To do so, follow these steps:

  1. Obtain assembled listings of the SNIOS.ASM source file that require modification. You can use MAC, RMAC, or ASM. if you use ASM, the title, name, if, and else statements must be removed from the source files to assemble correctly. Using RMAC is highly recommended because it simplifies the task of generating the SPR files when used in conjunction with LINK. Otherwise, the SPR files must be generated in the same manner as for MP/M II XIOS.SPR generation.
            A>RMAC SNIOS
               
  2. Study the SNIOS.PRN listing. Notice the ASCII equate. If true, it specifies that the message format is 7-bit ASCII. If false, it specifies a binary 8-bit message format. The ASCII mode is sometimes useful in debugging, but in practice do not use it where it is possible to transmit 8 bit serial data.

The only code that requires modification in the SNIOS.ASM file is contained in the CHAROUT, CHARIN, and DELAY procedures. The CHAROUT and CHARIN procedures can be conditionally assembled for a Dynabyte DB8/2, now called DB8/5200, a Digital Microsystems DSC-2 or an ALTOS 8000-2. The NOPs in the CHAROUT procedure are simply padding, so the length of the DB8/2 SNIOS and DSC-2 SNIOS is the same, which helps in the debugging of these two versions.

Perhaps the most critical area in the SNIOS that requires adjustment for a specific network configuration is in the timeout code of the CHARIN procedure. If too little time is allowed, the server might not be able to complete the function because of a heavy request load from the requesters. If too much time is specified, communication breaks on the network can go undetected for a period of time, making both error recovery and precise detection difficult. Note that this is a logical timeout, not a data-link timeout. The logical timeout determines how long the requester expects the server to take between the time it receives the message and the time it returns a response message.

Another critical parameter that requires adjustment for different environments is ALWAYS$RETRY. This equate, when true, controls conditional assembly that always produces retries on network failures. In this mode of operation, it is possible to recover from broken communication between the requester and a server. However, ALWAYS$RETRY does hang the requester in a busy retry mode when failures occur.

Listing E-1: Request Network I/O System
CP/M RMAC ASSEM 1.1	#001	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

     1                  	title	'Requester Network I/O System for CP/NET 1.2'
     2                  	page	54
     3                  
     4                  ;***************************************************************
     5                  ;***************************************************************
     6                  ;**                                                           **
     7                  ;**  R e q u e s t e r   N e t w o r k   I / O   S y s t e m  **
     8                  ;**                                                           **
     9                  ;***************************************************************
    10                  ;***************************************************************
    11                  
    12                  ;/*
    13                  ;  Copyright (C) 1980, 1981, 1982
    14                  ;  Digital Research
    15                  ;  P.O. Box 579
    16                  ;  Pacific Grove, CA 93950
    17                  ;
    18                  ;  Revised:  October 5, 1982
    19                  ;*/
    20                  
    21   0000 =         false	equ	0
    22   FFFF =         true	equ	not false
    23                  
    24   0000 =         cpnos	equ	false		; cp/net system
    25                  
    26   0000 =         DSC2	equ	false
    27   0000 =         DB82	equ	false
    28   FFFF =         Altos	equ	true
    29                  
    30   FFFF =         always$retry	equ	true	; force continuous retries
    31                  
    32   0000 =         modem	equ	false
    33                  
    34   0000 =         ASCII	equ	false
    35                  
    36   0000 =         debug	equ	false
    37                  
    38                  	CSEG
    39                  	if	cpnos
    40                  	extrn	BDOS
    41                  	else
    42   0005 =         BDOS	equ	0005h
    43                  	endif
    44                  
    45                  NIOS:
    46                  	public	NIOS
    47                  ;	Jump vector for SNIOS entry points
    48   0000 C3A900    	jmp	ntwrkinit	; network initialization
    49   0003 C3B800    	jmp	ntwrksts	; network status
    50   0006 C3C300    	jmp	cnfgtbladr	; return config table addr
    51   0009 C3C700    	jmp	sendmsg		; send message on network
    52   000C C33301    	jmp	receivemsg	; receive message from network
    53   000F C3DD01    	jmp	ntwrkerror	; network error
    54   0012 C3DE01    	jmp	ntwrkwboot	; network warm boot
CP/M RMAC ASSEM 1.1	#002	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

    55                  
    56                  	if	DB82
    57                  slave$ID	equ	12h	; slave processor ID number
    58                  	endif
    59                  	if	DSC2
    60                  slave$ID	equ	34h
    61                  	endif
    62                  	if	Altos
    63   0056 =         slave$ID	equ	56h
    64                  	endif
    65                  
    66                  	if	cpnos
    67                  ;	Initial Slave Configuration Table
    68                  Initconfigtbl:
    69                  	db	0000$0000b	; network status byte
    70                  	db	slave$ID	; slave processor ID number
    71                  	db	84h,0		; A:  Disk device
    72                  	db	81h,0		; B:   "
    73                  	db	82h,0		; C:   "
    74                  	db	83h,0		; D:   "
    75                  	db	80h,0		; E:   "
    76                  	db	85h,0		; F:   "
    77                  	db	86h,0		; G:   "
    78                  	db	87h,0		; H:   "
    79                  	db	88h,0		; I:   "
    80                  	db	89h,0		; J:   "
    81                  	db	8ah,0		; K:   "
    82                  	db	8bh,0		; L:   "
    83                  	db	8ch,0		; M:   "
    84                  	db	8dh,0		; N:   "
    85                  	db	8eh,0		; O:   "
    86                  	db	8fh,0		; P:   "
    87                  	db	0,0		; console device
    88                  	db	0,0		; list device:
    89                  	db	0		;	buffer index
    90                  	db	0		;	FMT
    91                  	db	0		;	DID
    92                  	db	slave$ID	;	SID
    93                  	db	5		;	FNC
    94                  initcfglen equ	$-initconfigtbl
    95                  	endif
    96                  
    97   0000 =         defaultmaster	equ	00h
    98                  
    99                  wboot$msg:			; data for warm boot routine
   100   0015 3C5761726D	db	''
   101   0020 24        	db	'$'
   102                  
   103                  networkerrmsg:
   104   0021 4E6574776F	db	'Network Error'
   105   002E 24        	db	'$'
   106                  
   107                  
   108                  	page
CP/M RMAC ASSEM 1.1	#003	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   109                  
   110                  	DSEG
   111                  
   112                  
   113                  ;	Slave Configuration Table
   114                  configtbl:
   115                  
   116                  Network$status:
   117   0000           	ds	1		; network status byte
   118   0001           	ds	1		; slave processor ID number
   119   0002           	ds	2		; A:  Disk device
   120   0004           	ds	2		; B:   "
   121   0006           	ds	2		; C:   "
   122   0008           	ds	2		; D:   "
   123   000A           	ds	2		; E:   "
   124   000C           	ds	2		; F:   "
   125   000E           	ds	2		; G:   "
   126   0010           	ds	2		; H:   "
   127   0012           	ds	2		; I:   "
   128   0014           	ds	2		; J:   "
   129   0016           	ds	2		; K:   "
   130   0018           	ds	2		; L:   "
   131   001A           	ds	2		; M:   "
   132   001C           	ds	2		; N:   "
   133   001E           	ds	2		; O:   "
   134   0020           	ds	2		; P:   "
   135                  
   136   0022           	ds	2		; console device
   137                  
   138   0024           	ds	2		; list device:
   139   0026           	ds	1		;	buffer index
   140   0027 00        	db	0		;	FMT
   141   0028 00        	db	0		;	DID
   142   0029 56        	db	Slave$ID	;	SID (CP/NOS must still initialize)
   143   002A 05        	db	5		;	FNC
   144   002B           	ds	1		;	SIZ
   145   002C           	ds	1		;	MSG(0)  List number
   146   002D           	ds	128		;	MSG(1) ... MSG(128)
   147                  
   148                  msg$adr:
   149   00AD           	ds	2		; message address
   150                  	if	modem
   151                  timeout$retries	equ 0		; timeout a max of 256 times
   152                  	else
   153   0064 =         timeout$retries equ 100		; timeout a max of 100 times
   154                  	endif
   155   000A =         max$retries equ	10		; send message max of 10 times
   156                  retry$count:
   157   00AF           	ds	1
   158                  
   159                  FirstPass:
   160   00B0 FF        	db	0ffh
   161                  
   162                  ;	Network Status Byte Equates
CP/M RMAC ASSEM 1.1	#004	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   163                  ;
   164   0010 =         active		equ	0001$0000b	; slave logged in on network
   165   0002 =         rcverr		equ	0000$0010b	; error in received message
   166   0001 =         senderr		equ	0000$0001b	; unable to send message
   167                  
   168                  ;	General Equates
   169                  ;
   170   0001 =         SOH	equ	01h		; Start of Header
   171   0002 =         STX	equ	02h		; Start of Data
   172   0003 =         ETX	equ	03h		; End of Data
   173   0004 =         EOT	equ	04h		; End of Transmission
   174   0005 =         ENQ	equ	05h		; Enquire
   175   0006 =         ACK	equ	06h		; Acknowledge
   176   000A =         LF	equ	0ah		; Line Feed
   177   000D =         CR	equ	0dh		; Carriage Return
   178   0015 =         NAK	equ	15h		; Negative Acknowledge
   179                  
   180   0002 =         conout	equ	2		; console output function
   181   0009 =         print	equ	9		; print string function
   182   0043 =         rcvmsg	equ	67		; receive message NDOS function
   183   0040 =         login	equ	64		; Login NDOS function
   184                  
   185                  ;	I/O Equates
   186                  ;
   187                  	if	DB82
   188                  stati	equ	83h
   189                  mski	equ	08h
   190                  dprti	equ	80h
   191                  
   192                  stato	equ	83h
   193                  msko	equ	10h
   194                  statc	equ	81h
   195                  mskc	equ	20h
   196                  dprto	equ	86h
   197                  	endif
   198                  
   199                  	if	DSC2
   200                  	if	modem
   201                  stati	equ	59h
   202                  mski	equ	02h
   203                  dprti	equ	58h
   204                  
   205                  stato	equ	59h
   206                  msko	equ	01h
   207                  dprto	equ	58h
   208                  	else
   209                  stati	equ	51h
   210                  mski	equ	02h
   211                  dprti	equ	50h
   212                  
   213                  stato	equ	51h
   214                  msko	equ	01h
   215                  dprto	equ	50h
   216                  	endif
CP/M RMAC ASSEM 1.1	#005	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   217                  	endif
   218                  
   219                  	if	Altos
   220   001F =         stati	equ	1fh
   221   0001 =         mski	equ	01h
   222   001E =         dprti	equ	1eh
   223                  
   224   001F =         stato	equ	1fh
   225   0004 =         msko	equ	04h
   226   001E =         dprto	equ	1eh
   227                  	endif
   228                  
   229                  
   230                  
   231                  	page
CP/M RMAC ASSEM 1.1	#006	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   232                  
   233                  	CSEG
   234                  ;	Utility Procedures
   235                  ;
   236                  delay:				; delay for c[a] * 0.5 milliseconds
   237   002F 3E06      	mvi	a,6
   238                  delay1:
   239   0031 0E86      	mvi	c,86h
   240                  delay2:
   241   0033 0D        	dcr	c
   242   0034 C23300    	jnz	delay2
   243   0037 3D        	dcr	a
   244   0038 C23100    	jnz	delay1
   245   003B C9        	ret
   246                  
   247                  	if	ASCII
   248                  Nib$out:			; A = nibble to be transmitted in ASCII
   249                  	cpi	10
   250                  	jnc	nibAtoF		; jump if A-F
   251                  	adi	'0'
   252                  	mov	c,a
   253                  	jmp	Char$out
   254                  nibAtoF:
   255                  	adi	'A'-10
   256                  	mov	c,a
   257                  	jmp	Char$out
   258                  	endif
   259                  
   260                  Pre$Char$out:
   261   003C 7A        	mov	a,d
   262   003D 81        	add	c
   263   003E 57        	mov	d,a		; update the checksum in D
   264                  
   265                  nChar$out:			; C = byte to be transmitted
   266                  	if	Altos
   267   003F 3E10      	mvi	a,10h
   268   0041 D31F      	out	stato
   269                  	endif
   270   0043 DB1F      	in	stato
   271   0045 E604      	ani	msko
   272   0047 CA3F00    	jz	nChar$out
   273                  
   274                  	if	DB82
   275                  	in	statc
   276                  	ani	mskc
   277                  	jz	nChar$out
   278                  	endif
   279                  
   280                  	if	DSC2
   281                  	nop			; these NOP's make DB8/2 & DSC2
   282                  	nop			;  versions the same length - saves
   283                  	nop			;  a second listing
   284                  	nop
   285                  	nop
CP/M RMAC ASSEM 1.1	#007	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   286                  	nop
   287                  	nop
   288                  	endif
   289                  
   290   004A 79        	mov	a,c
   291   004B D31E      	out	dprto
   292   004D C9        	ret
   293                  ;
   294                  Char$out:
   295   004E CD3F00    	call	nChar$out
   296                  	if	Altos
   297   0051 E3E3E3E3  	xthl! xthl! xthl! xthl
   298   0055 E3E3E3E3  	xthl! xthl! xthl! xthl
   299   0059 E3E3E3E3  	xthl! xthl! xthl! xthl  ;delay 54 usec
   300   005D C9        	ret
   301                  	else
   302                  	jmp	delay		; delay after each Char sent to Mstr
   303                  ;	ret
   304                  	endif
   305                  
   306                  	if	ASCII
   307                  Nib$in:				; return nibble in A register
   308                  	call	Char$in
   309                  	rc
   310                  	ani	7fh
   311                  	sui	'0'
   312                  	cpi	10
   313                  	jc	Nib$in$rtn 	; must be 0-9
   314                  	adi	('0'-'A'+10) and 0ffh
   315                  	cpi	16
   316                  	jc	Nib$in$rtn 	; must be 10-15
   317                  	lda	network$status
   318                  	ori	rcverr
   319                  	sta	network$status
   320                  	mvi	a,0
   321                  	stc			; carry set indicating err cond
   322                  	ret
   323                  
   324                  Nib$in$rtn:
   325                  	ora	a		; clear carry & return
   326                  	ret
   327                  	endif
   328                  
   329                  xChar$in:
   330   005E 0664      	mvi	b,100		; 100 ms  corresponds to longest possible
   331   0060 C36500    	jmp	char$in0 	;wait between master operations
   332                  
   333                  Char$in:			; return byte in A register
   334                  				;  carry set on rtn if timeout
   335                  	if	modem
   336                  	mvi	b,0		; 256 ms = 7.76 chars @ 300 baud
   337                  	else
   338                  	if	Altos
   339   0063 0603      	mvi	b,3		; 3 ms = 50 chars @ 125k baud
CP/M RMAC ASSEM 1.1	#008	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   340                  	else
   341                  	mvi 	b,50		; 50 ms = 50 chars @ 9600 baud
   342                  	endif
   343                  	endif
   344                  Char$in0:
   345   0065 0E5A      	mvi	c,5ah
   346                  Char$in1:
   347                  	if	Altos
   348   0067 3E00      	mvi	a,0
   349   0069 D31F      	out	stati
   350                  	endif
   351   006B DB1F      	in	stati
   352   006D E601      	ani	mski
   353   006F C27C00    	jnz	Char$in2
   354   0072 0D        	dcr	c
   355   0073 C26700    	jnz	Char$in1
   356   0076 05        	dcr	b
   357   0077 C26500    	jnz	Char$in0
   358   007A 37        	stc			; carry set for err cond = timeout
   359   007B C9        	ret
   360                  Char$in2:
   361   007C DB1E      	in	dprti
   362   007E C9        	ret			; rtn with raw char and carry cleared
   363                  
   364                  Net$out:			; C = byte to be transmitted
   365                  				; D = checksum
   366   007F 7A        	mov	a,d
   367   0080 81        	add	c
   368   0081 57        	mov	d,a
   369                  
   370                  	if	ASCII
   371                  	mov	a,c
   372                  	mov	b,a
   373                  	rar
   374                  	rar
   375                  	rar
   376                  	rar
   377                  	ani	0FH		; mask HI-LO nibble to LO nibble
   378                  	call	Nib$out
   379                  	mov	a,b
   380                  	ani	0FH
   381                  	jmp	Nib$out
   382                  
   383                  	else
   384   0082 C34E00    	jmp	Char$out
   385                  	endif
   386                  
   387                  Msg$in:				; HL = destination address
   388                  				; E  = # bytes to input
   389   0085 CD9000    	call	Net$in
   390   0088 D8        	rc
   391   0089 77        	mov	m,a
   392   008A 23        	inx	h
   393   008B 1D        	dcr	e
CP/M RMAC ASSEM 1.1	#009	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   394   008C C28500    	jnz	Msg$in
   395   008F C9        	ret
   396                  
   397                  Net$in:				; byte returned in A register
   398                  				; D  = checksum accumulator
   399                  
   400                  	if	ASCII
   401                  	call	Nib$in
   402                  	rc
   403                  	add	a
   404                  	add	a
   405                  	add	a
   406                  	add	a
   407                  	push	psw
   408                  	call	Nib$in
   409                  	pop	b
   410                  	rc
   411                  	ora	b
   412                  
   413                  	else
   414   0090 CD6300    	call	Char$in		;receive byte in Binary mode
   415   0093 D8        	rc
   416                  	endif
   417                  
   418                  chks$in:
   419   0094 47        	mov	b,a
   420   0095 82        	add	d		; add & update checksum accum.
   421   0096 57        	mov	d,a
   422   0097 B7        	ora	a		; set cond code from checksum
   423   0098 78        	mov	a,b
   424   0099 C9        	ret
   425                  
   426                  Msg$out:			; HL = source address
   427                  				; E  = # bytes to output
   428                  				; D  = checksum
   429                  				; C  = preamble byte
   430   009A 1600      	mvi	d,0		; initialize the checksum
   431   009C CD3C00    	call	Pre$Char$out 	; send the preamble character
   432                  Msg$out$loop:
   433   009F 4E        	mov	c,m
   434   00A0 23        	inx	h
   435   00A1 CD7F00    	call	Net$out
   436   00A4 1D        	dcr	e
   437   00A5 C29F00    	jnz	Msg$out$loop
   438   00A8 C9        	ret
   439                  
   440                  	page
CP/M RMAC ASSEM 1.1	#010	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   441                  
   442                  ;	Network Initialization
   443                  ntwrkinit:
   444                  
   445                  	if	cpnos		; copy down network assignments
   446                  	lxi	h,Initconfigtbl
   447                  	lxi	d,configtbl
   448                  	mvi	c,initcfglen
   449                  initloop:
   450                  	mov	a,m
   451                  	stax	d
   452                  	inx	h
   453                  	inx	d
   454                  	dcr	c
   455                  	jnz	initloop		; initialize config tbl from ROM
   456                  
   457                  	else
   458   00A9 3E56      	mvi	a,slave$ID		;initialize slave ID byte
   459   00AB 320100    	sta	configtbl+1		;  in the configuration tablee
   460                  	endif
   461                  
   462                  ;	device initialization, as required
   463                  
   464                  	if	Altos
   465   00AE 3E47      	mvi	a,047h
   466   00B0 D30E      	out	0eh
   467   00B2 3E01      	mvi	a,1
   468   00B4 D30E      	out	0eh
   469                  	endif
   470                  
   471                  	if	DSC2 and modem
   472                  	mvi	a,0ceh
   473                  	out	stato
   474                  	mvi	a,027h
   475                  	out	stato
   476                  	endif
   477                  
   478                  	if	cpnos
   479                  	call	loginpr			; login to a master
   480                  	endif
   481                  
   482                  initok:
   483   00B6 AF        	xra	a			; return code is 0=success
   484   00B7 C9        	ret
   485                  
   486                  
   487                  	page
CP/M RMAC ASSEM 1.1	#011	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   488                  
   489                  ;	Network Status
   490                  ntwrksts:
   491   00B8 3A0000    	lda	network$status
   492   00BB 47        	mov	b,a
   493   00BC E6FC      	ani	not (rcverr+senderr)
   494   00BE 320000    	sta	network$status
   495   00C1 78        	mov	a,b
   496   00C2 C9        	ret
   497                  
   498                  
   499                  
   500                  ;	Return Configuration Table Address
   501                  cnfgtbladr:
   502   00C3 210000    	lxi	h,configtbl
   503   00C6 C9        	ret
   504                  
   505                  
   506                  	page
CP/M RMAC ASSEM 1.1	#012	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   507                  
   508                  ;	Send Message on Network
   509                  sendmsg:			; BC = message addr
   510   00C7 60        	mov	h,b
   511   00C8 69        	mov	l,c		; HL = message address
   512   00C9 22AD00    	shld	msg$adr
   513                  re$sendmsg:
   514   00CC 3E0A      	mvi	a,max$retries
   515   00CE 32AF00    	sta	retry$count	; initialize retry count
   516                  send:
   517   00D1 2AAD00    	lhld	msg$adr
   518   00D4 0E05      	mvi	c,ENQ
   519   00D6 CD4E00    	call	Char$out	; send ENQ to master
   520   00D9 1664      	mvi	d,timeout$retries
   521                  ENQ$response:
   522   00DB CD6300    	call	Char$in
   523   00DE D2E800    	jnc	got$ENQ$response
   524   00E1 15        	dcr	d
   525   00E2 C2DB00    	jnz	ENQ$response
   526   00E5 C32B01    	jmp	Char$in$timeout
   527                  got$ENQ$response:
   528   00E8 CD1E01    	call	get$ACK0
   529   00EB 0E01      	mvi	c,SOH
   530   00ED 1E05      	mvi	e,5
   531   00EF CD9A00    	call	Msg$out		; send SOH FMT DID SID FNC SIZ
   532   00F2 AF        	xra	a
   533   00F3 92        	sub	d
   534   00F4 4F        	mov	c,a
   535   00F5 CD7F00    	call	net$out		; send HCS (header checksum)
   536   00F8 CD1801    	call	get$ACK
   537   00FB 2B        	dcx	h
   538   00FC 5E        	mov	e,m
   539   00FD 23        	inx	h
   540   00FE 1C        	inr	e
   541   00FF 0E02      	mvi	c,STX
   542   0101 CD9A00    	call	Msg$out		; send STX DB0 DB1 ...
   543   0104 0E03      	mvi	c,ETX
   544   0106 CD3C00    	call	Pre$Char$out	; send ETX
   545   0109 AF        	xra	a
   546   010A 92        	sub	d
   547   010B 4F        	mov	c,a
   548   010C CD7F00    	call	Net$out		; send the checksum
   549   010F 0E04      	mvi	c,EOT
   550   0111 CD3F00    	call	nChar$out	; send EOT
   551   0114 CD1801    	call	get$ACK		; (leave these
   552   0117 C9        	ret			;              two instructions)
   553                  
   554                  get$ACK:
   555   0118 CD6300    	call	Char$in
   556   011B DA2301    	jc	send$retry 	; jump if timeout
   557                  get$ACK0:
   558   011E E67F      	ani	7fh
   559   0120 D606      	sui	ACK
   560   0122 C8        	rz
CP/M RMAC ASSEM 1.1	#013	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   561                  send$retry:
   562   0123 E1        	pop	h		; discard return address
   563   0124 21AF00    	lxi	h,retry$count
   564   0127 35        	dcr	m
   565   0128 C2D100    	jnz	send		; send again unles max retries
   566                  Char$in$timeout:
   567   012B 3E01      	mvi	a,senderr
   568                  
   569                  	if	always$retry
   570   012D CDD201    	call	error$return
   571   0130 C3CC00    	jmp	re$sendmsg
   572                  	else
   573                  	jmp	error$return
   574                  	endif
   575                  
   576                  	page
CP/M RMAC ASSEM 1.1	#014	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   577                  
   578                  ;	Receive Message from Network
   579                  receivemsg:			; BC = message addr
   580   0133 60        	mov	h,b
   581   0134 69        	mov	l,c		; HL = message address
   582   0135 22AD00    	shld	msg$adr
   583                  re$receivemsg:
   584   0138 3E0A      	mvi	a,max$retries
   585   013A 32AF00    	sta	retry$count	; initialize retry count
   586                  re$call:
   587   013D CD4F01    	call	receive		; rtn from receive is receive error
   588                  
   589                  receive$retry:
   590   0140 21AF00    	lxi	h,retry$count
   591   0143 35        	dcr	m
   592   0144 C23D01    	jnz	re$call
   593                  receive$timeout:
   594   0147 3E02      	mvi	a,rcverr
   595                  
   596                  	if	always$retry
   597   0149 CDD201    	call	error$return
   598   014C C33801    	jmp	re$receivemsg
   599                  	else
   600                  	jmp	error$return
   601                  	endif
   602                  
   603                  receive:
   604   014F 2AAD00    	lhld	msg$adr
   605   0152 1664      	mvi	d,timeout$retries
   606                  receive$firstchar:
   607   0154 CD5E00    	call	xcharin
   608   0157 D26201    	jnc	got$firstchar
   609   015A 15        	dcr	d
   610   015B C25401    	jnz	receive$firstchar
   611   015E E1        	pop	h		; discard receive$retry rtn adr
   612   015F C34701    	jmp	receive$timeout
   613                  got$firstchar:
   614   0162 E67F      	ani	7fh
   615   0164 FE05      	cpi	ENQ		; Enquire?
   616   0166 C24F01    	jnz	receive
   617                  
   618   0169 0E06      	mvi	c,ACK
   619   016B CD3F00    	call	nChar$out 	; acknowledge ENQ with an ACK
   620                  
   621   016E CD6300    	call	Char$in
   622   0171 D8        	rc			; return to receive$retry
   623   0172 E67F      	ani	7fh
   624   0174 FE01      	cpi	SOH		; Start of Header ?
   625   0176 C0        	rnz			; return to receive$retry
   626   0177 57        	mov	d,a		; initialize the HCS
   627   0178 1E05      	mvi	e,5
   628   017A CD8500    	call	Msg$in
   629   017D D8        	rc			; return to receive$retry
   630   017E CD9000    	call	Net$in
CP/M RMAC ASSEM 1.1	#015	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   631   0181 D8        	rc			; return to receive$retry
   632   0182 C2CD01    	jnz	bad$checksum
   633   0185 CDC501    	call	send$ACK
   634   0188 CD6300    	call	Char$in
   635   018B D8        	rc			; return to receive$retry
   636   018C E67F      	ani	7fh
   637   018E FE02      	cpi	STX		; Start of Data ?
   638   0190 C0        	rnz			; return to receive$retry
   639   0191 57        	mov	d,a		; initialize the CKS
   640   0192 2B        	dcx	h
   641   0193 5E        	mov	e,m
   642   0194 23        	inx	h
   643   0195 1C        	inr	e
   644   0196 CD8500    	call	msg$in		; get DB0 DB1 ...
   645   0199 D8        	rc			; return to receive$retry
   646   019A CD6300    	call	Char$in		; get the ETX
   647   019D D8        	rc			; return to receive$retry
   648   019E E67F      	ani	7fh
   649   01A0 FE03      	cpi	ETX
   650   01A2 C0        	rnz			; return to receive$retry
   651   01A3 82        	add	d
   652   01A4 57        	mov	d,a		; update CKS with ETX
   653   01A5 CD9000    	call	Net$in		; get CKS
   654   01A8 D8        	rc			; return to receive$retry
   655   01A9 CD6300    	call	Char$in		; get EOT
   656   01AC D8        	rc			; return to receive$retry
   657   01AD E67F      	ani	7fh
   658   01AF FE04      	cpi	EOT
   659   01B1 C0        	rnz			; return to receive$retry
   660   01B2 7A        	mov	a,d
   661   01B3 B7        	ora	a		; test CKS
   662   01B4 C2CD01    	jnz	bad$checksum
   663   01B7 E1        	pop	h		; discard receive$retry rtn adr
   664   01B8 2AAD00    	lhld	msg$adr
   665   01BB 23        	inx	h
   666   01BC 3A0100    	lda	configtbl+1
   667   01BF 96        	sub	m
   668   01C0 CAC501    	jz	send$ACK 	; jump with A=0 if DID ok
   669   01C3 3EFF      	mvi	a,0ffh		; return code shows bad DID
   670                  send$ACK:
   671   01C5 F5        	push	psw		; save return code
   672   01C6 0E06      	mvi	c,ACK
   673   01C8 CD3F00    	call	nChar$out  	; send ACK if checksum ok
   674   01CB F1        	pop	psw		; restore return code
   675   01CC C9        	ret
   676                  
   677                  bad$DID:
   678                  bad$checksum:
   679   01CD 0E15      	mvi	c,NAK
   680   01CF C34E00    	jmp	Char$out  	; send NAK on bad chksm & not max retries
   681                  ;	ret
   682                  
   683                  error$return:
   684   01D2 210000    	lxi	h,network$status
CP/M RMAC ASSEM 1.1	#016	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   685   01D5 B6        	ora	m
   686   01D6 77        	mov	m,a
   687   01D7 CDDD01    	call	ntwrkerror 	; perform any required device re-init.
   688   01DA 3EFF      	mvi	a,0ffh
   689   01DC C9        	ret
   690                  
   691                  ntwrkerror:
   692                  				;  perform any required device 
   693   01DD C9        	ret			;     re-initialization
   694                  
   695                  	page
CP/M RMAC ASSEM 1.1	#017	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   696                  
   697                  ;
   698                  ntwrkwboot:
   699                  
   700                  ;	This procedure is called each time the CCP is
   701                  ;  	reloaded from disk.  This version prints ""
   702                  ;  	on the console and then returns, but anything necessary 
   703                  ;       for restart can be put here.
   704                  
   705   01DE 0E09       	mvi	c,9
   706   01E0 111500    	lxi	d,wboot$msg
   707   01E3 C30500    	jmp	BDOS
   708                  
   709                  	page
CP/M RMAC ASSEM 1.1	#018	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   710                  
   711                  	if	cpnos
   712                  ;
   713                  ;	LOGIN to a Master
   714                  ;
   715                  ; Equates
   716                  ;
   717                  buff	equ	0080h
   718                  
   719                  readbf	equ	10
   720                  
   721                  active	equ	0001$0000b
   722                  
   723                  loginpr:
   724                  	mvi	c,initpasswordmsglen
   725                  	lxi	h,initpasswordmsg
   726                  	lxi	d,passwordmsg
   727                  copypassword:
   728                  	mov	a,m
   729                  	stax	d
   730                  	inx	h
   731                  	inx	d
   732                  	dcr	c
   733                  	jnz	copypassword
   734                  	mvi	c,print
   735                  	lxi	d,loginmsg
   736                  	call	BDOS
   737                  	mvi	c,readbf
   738                  	lxi	d,buff-1
   739                  	mvi	a,50h
   740                  	stax	d
   741                  	call	BDOS
   742                  	lxi	h,buff
   743                  	mov	a,m	; get # chars in the command tail
   744                  	ora	a
   745                  	jz	dologin ; default login if empty command tail
   746                  	mov	c,a	; A = # chars in command tail
   747                  	xra	a
   748                  	mov	b,a	; B will accumulate master ID
   749                  scanblnks:
   750                  	inx	h
   751                  	mov	a,m
   752                  	cpi	' '
   753                  	jnz	pastblnks ; skip past leading blanks
   754                  	dcr	c
   755                  	jnz	scanblnks
   756                  	jmp	prelogin ; jump if command tail exhausted
   757                  pastblnks:
   758                  	cpi	'['
   759                  	jz	scanMstrID
   760                  	mvi	a,8
   761                  	lxi	d,passwordmsg+5+8-1
   762                  	xchg
   763                  spacefill:
CP/M RMAC ASSEM 1.1	#019	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   764                  	mvi	m,' '
   765                  	dcx	h
   766                  	dcr	a
   767                  	jnz	spacefill
   768                  	xchg
   769                  scanLftBrkt:
   770                  	mov	a,m
   771                  	cpi	'['
   772                  	jz	scanMstrID
   773                  	inx	d
   774                  	stax	d	;update the password
   775                  	inx	h
   776                  	dcr	c
   777                  	jnz	scanLftBrkt
   778                  	jmp	prelogin
   779                  scanMstrID:
   780                  	inx	h
   781                  	dcr	c
   782                  	jz	loginerr
   783                  	mov	a,m
   784                  	cpi	']'
   785                  	jz	prelogin
   786                  	sui	'0'
   787                  	cpi	10
   788                  	jc	updateID
   789                  	adi	('0'-'A'+10) and 0ffh
   790                  	cpi	16
   791                  	jnc	loginerr
   792                  updateID:
   793                  	push	psw
   794                  	mov	a,b
   795                  	add	a
   796                  	add	a
   797                  	add	a
   798                  	add	a
   799                  	mov	b,a	; accum * 16
   800                  	pop	psw
   801                  	add	b
   802                  	mov	b,a
   803                  	jmp	scanMstrID
   804                  
   805                  prelogin:
   806                  	mov	a,b
   807                  
   808                  dologin:
   809                  	lxi	b,passwordmsg+1
   810                  	stax	b
   811                  	dcx	b
   812                  	call	sendmsg
   813                  	inr	a
   814                  	lxi	d,loginfailedmsg
   815                  	jz	printmsg
   816                  	lxi	b,passwordmsg
   817                  	call	receivemsg
CP/M RMAC ASSEM 1.1	#020	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   818                  	inr	a
   819                  	lxi	d,loginfailedmsg
   820                  	jz	printmsg
   821                  	lda	passwordmsg+5
   822                  	inr	a
   823                  	jnz	loginOK
   824                  	jmp	printmsg
   825                  
   826                  loginerr:
   827                  	lxi	d,loginerrmsg
   828                  printmsg:
   829                  	mvi	c,print
   830                  	call	BDOS
   831                  	jmp	loginpr		; try login again
   832                  
   833                  loginOK:
   834                  	lxi	h,network$status ; HL = status byte addr
   835                  	mov	a,m
   836                  	ori	active	; set active bit true
   837                  	mov	m,a
   838                  	ret
   839                  
   840                  ;
   841                  ; Local Data Segment
   842                  ;
   843                  loginmsg:
   844                  	db	cr,lf
   845                  	db	'LOGIN='
   846                  	db	'$'
   847                  
   848                  initpasswordmsg:
   849                  	db	00h	; FMT
   850                  	db	00h	; DID Master ID #
   851                  	db	slave$ID ;SID
   852                  	db	40h	; FNC
   853                  	db	7	; SIZ
   854                  	db	'PASSWORD' ; password
   855                  initpasswordmsglen equ	$-initpasswordmsg
   856                  
   857                  
   858                  loginerrmsg:
   859                  	db	lf
   860                  	db	'Invalid LOGIN'
   861                  	db	'$'
   862                  
   863                  loginfailedmsg:
   864                  	db	lf
   865                  	db	'LOGIN Failed'
   866                  	db	'$'
   867                  
   868                  	DSEG
   869                  passwordmsg:
   870                  	ds	1	; FMT
   871                  	ds	1	; DID
CP/M RMAC ASSEM 1.1	#021	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

   872                  	ds	1	; SID
   873                  	ds	1	; FNC
   874                  	ds	1	; SIZ
   875                  	ds	8	; DAT = password
   876                  	endif
   877                  
   878   01E6           	end
CP/M RMAC ASSEM 1.1	#022	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

ACK              0006   175#  559   618   672 
ACTIVE           0010   164#  721#  836 
ALTOS            FFFF    28#   62   219   266   296   338   347   464 
ALWAYSRETRY      FFFF    30#  569   596 
ASCII            0000    34#  247   306   370   400 
BADCHECKSUM      01CD   632   662   678#
BADDID           01CD   677#
BDOS             0005    40    42#  707   736   741   830 
CHARIN           0063   308   333#  414   522   555   621   634   646   655 
CHARIN0          0065   331   344#  357 
CHARIN1          0067   346#  355 
CHARIN2          007C   353   360#
CHARINTIMEOUT    012B   526   566#
CHAROUT          004E   253   257   294#  384   519   680 
CHKSIN           0094   418#
CNFGTBLADR       00C3    50   501#
CONFIGTBL        0000   114#  447   459   502   666 
CONOUT           0002   180#
CPNOS            0000    24#   39    66   445   478   711 
CR               000D   177#  844 
DB82             0000    27#   56   187   274 
DEBUG            0000    36#
DEFAULTMASTER    0000    97#
DELAY            002F   236#  302 
DELAY1           0031   238#  244 
DELAY2           0033   240#  242 
DPRTI            001E   190#  203#  211#  222#  361 
DPRTO            001E   196#  207#  215#  226#  291 
DSC2             0000    26#   59   199   280   471 
ENQ              0005   174#  518   615 
ENQRESPONSE      00DB   521#  525 
EOT              0004   173#  549   658 
ERRORRETURN      01D2   570   573   597   600   683#
ETX              0003   172#  543   649 
FALSE            0000    21#   22    24    26    27    32    34    36 
FIRSTPASS        00B0   159#
GETACK           0118   536   551   554#
GETACK0          011E   528   557#
GOTENQRESPONSE   00E8   523   527#
GOTFIRSTCHAR     0162   608   613#
INITOK           00B6   482#
LF               000A   176#  844   859   864 
LOGIN            0040   183#
MAXRETRIES       000A   155#  514   584 
MODEM            0000    32#  150   200   335   471 
MSGADR           00AD   148#  512   517   582   604   664 
MSGIN            0085   387#  394   628   644 
MSGOUT           009A   426#  531   542 
MSGOUTLOOP       009F   432#  437 
MSKI             0001   189#  202#  210#  221#  352 
MSKO             0004   193#  206#  214#  225#  271 
NAK              0015   178#  679 
NCHAROUT         003F   265#  272   277   295   550   619   673 
NETIN            0090   389   397#  630   653 
CP/M RMAC ASSEM 1.1	#023	REQUESTER NETWORK I/O SYSTEM FOR CP/NET 1.2

NETOUT           007F   364#  435   535   548 
NETWORKERRMSG    0021   103#
NETWORKSTATUS    0000   116#  317   319   491   494   684   834 
NIOS             0000    45#   46 
NTWRKERROR       01DD    53   687   691#
NTWRKINIT        00A9    48   443#
NTWRKSTS         00B8    49   490#
NTWRKWBOOT       01DE    54   698#
PRECHAROUT       003C   260#  431   544 
PRINT            0009   181#  734   829 
RCVERR           0002   165#  318   493   594 
RCVMSG           0043   182#
RECALL           013D   586#  592 
RECEIVE          014F   587   603#  616 
RECEIVEFIRSTCHAR 0154   606#  610 
RECEIVEMSG       0133    52   579#  817 
RECEIVERETRY     0140   589#
RECEIVETIMEOUT   0147   593#  612 
RERECEIVEMSG     0138   583#  598 
RESENDMSG        00CC   513#  571 
RETRYCOUNT       00AF   156#  515   563   585   590 
SEND             00D1   516#  565 
SENDACK          01C5   633   668   670#
SENDERR          0001   166#  493   567 
SENDMSG          00C7    51   509#  812 
SENDRETRY        0123   556   561#
SLAVEID          0056    57#   60#   63#   70    92   142   458   851 
SOH              0001   170#  529   624 
STATI            001F   188#  201#  209#  220#  349   351 
STATO            001F   192#  205#  213#  224#  268   270   473   475 
STX              0002   171#  541   637 
TIMEOUTRETRIES   0064   151#  153#  520   605 
TRUE             FFFF    22#   28    30 
WBOOTMSG         0015    99#  706 
XCHARIN          005E   329#  607 

E.5 Modifying the NETWRKIF

The NETWRKIF, designed for an Altos ACS 8000-10, is also easy to modify. The NETWRKIF implements the protocol by checking for the first character of an incoming message through one of the XIOS CONIN routines. After receiving the first character and validating it, the NETWRKIF disables interrupts and reads the rest of the message in under direct process control. If an XIOS CONIN routine does not exist for the port to be used for the network, you must write one.

To modify this NETWRKIF, follow these steps:

  1. Set the NMB$SLVS equate to the number of requesters to be supported. If more than four must be supported, you must add more Process Descriptors and queues.
  2. If the server can only transmit or receive one message at a time, then the NETWRKIF supports a mutual exclusion queue to prevent collisions. To use this queue, set MUTEXIN or MUTEXOUT to true.
  3. If the server is running on a Z80 processor, set Z80 to true for more efficient implementation of character I/O.
  4. If all or some of the network RS-232 ports support only 7 bit ASCII, modify the BINARYASCII table by setting the appropriate entries to 0.
  5. Modify the network port definitions. CONSOLE4$STATUS through PRINTER2$STATUS must be modified. Also, CHARIOTBL must be modified, so that the console numbers associated with the ports listed in STATUS$PORTS match.
  6. I/O port numbers in the routines CHAR$OUT and CHAR$IN might have to be modified. You might have to implement a I/O port table similar to STATUS$PORTS. This implementation relies on the fact that the Altos ACS 8000-10 always positions its I/O ports at a fixed offset from its status ports.

The sample NETWRKIF contains a debug conditional assembly flag that permits generation of a NETWRKIF.COM file. The NETWRKIF.COM version can debug a single requester, as follows:

  1. Perform a GENSYS in which the SERVER.RSP is included; do not include a NETWRKIF.RSP. During the GENSYS, do not specify bank-switched memory.
  2. Execute the MPM.SYS produced from GENSYS, and load the NETWRKIF.COM file with DDT, SID, or ZSID.
  3. Use DDT, SID, or ZSID to debug the NETWRKIF process. This works only for a single requester.

Listing E-2: Server Network I/F Module
CP/M RMAC ASSEM 1.1	#001	MASTER NETWORK I/F MODULE

     1                  	title	'Master Network I/F Module'
     2                  	page	54
     3                  
     4                  ;***************************************************************
     5                  ;***************************************************************
     6                  ;**                                                           **
     7                  ;**      S e r v e r   N e t w o r k   I / F   M o d u l e    **
     8                  ;**                                                           **
     9                  ;***************************************************************
    10                  ;***************************************************************
    11                  
    12                  ;/*
    13                  ;  Copyright (C) 1980
    14                  ;  Digital Research
    15                  ;  P.O. Box 579
    16                  ;  Pacific Grove, CA 93950
    17                  ;
    18                  ;  Modified October 5, 1982
    19                  ;
    20                  ;*/
    21                  
    22                  	
    23   0000 =         false	equ	0
    24   FFFF =         true	equ	not false
    25                  
    26   FFFF =         z80	equ	true
    27                  
    28   0000 =         debug	equ	false
    29   0000 =         modem	equ	false
    30                  
    31   0000 =         WtchDg	equ	false		; include watch dog timer
    32                  
    33   0000 =         mutexin	equ	false		; provide mutual exclusion on input
    34   0000 =         mutexout equ	false		; provide mutual exclusion on output
    35                  
    36                  
    37                  	if	debug
    38                  
    39                  NmbSlvs	equ	1		;debug only one requester
    40                  
    41                  	lxi	sp,NtwrkIS0+2eh
    42                  	mvi	c,145
    43                  	mvi	e,64
    44                  	call	bdos		; set priority to 64
    45                  	lxi	h,UQCBNtwrkQI0	; initialize reentrant variables
    46                  	lxi	d,UQCBNtwrkQO0
    47                  	lxi	b,BufferQ0
    48                  	mvi	a,00h
    49                  	ret
    50                  
    51                  bdosadr:
    52                  	dw	0005h
    53                  
    54                  	else
CP/M RMAC ASSEM 1.1	#002	MASTER NETWORK I/F MODULE

    55                  
    56   0002 =         NmbSlvs	equ	2		;RSP is configured for two requesters
    57                  
    58                  bdosadr:
    59   0000 0000      	dw	$-$		;XDOS entry point for RSP version
    60                  
    61                  	endif
    62                  
    63                  ;  Network Interface Process #0
    64                  
    65                  NtwrkIP0:
    66   0002 0000      	dw	0		; link
    67   0004 00        	db	0		; status
    68   0005 40        	db	64		; priority
    69   0006 6400      	dw	NtwrkIS0+46	; stack pointer
    70   0008 4E7477726B	db	'NtwrkIP0'	; name
    71   0010 00        	db	0		; console
    72   0011 FF        	db	0ffh		; memseg
    73   0012           	ds	2		; b
    74   0014           	ds	2		; thread
    75   0016           	ds	2		; buff
    76   0018           	ds	1		; user code & disk slct
    77   0019           	ds	2		; dcnt
    78   001B           	ds	1		; searchl
    79   001C           	ds	2		; searcha
    80   001E           	ds	2		; active drives
    81   0020 0000      	dw	0		; HL'
    82   0022 0000      	dw	0		; DE'
    83   0024 0000      	dw	0		; BC'
    84   0026 0000      	dw	0		; AF'
    85   0028 0000      	dw	0		; IY
    86   002A 0000      	dw	0		; IX
    87   002C 8000      	dw	UQCBNtwrkQI0	; HL
    88   002E A000      	dw	UQCBNtwrkQO0	; DE
    89   0030 A600      	dw	BufferQ0	; BC
    90   0032 0000      	dw	0		; AF, A = ntwkif console dev #
    91   0034           	ds	2		; scratch
    92                  
    93                  NtwrkIS0:
    94   0036 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
    95   003E C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
    96   0046 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
    97   004E C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
    98   0056 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
    99   005E C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h
   100   0064 4206      	dw	setup
   101                  
   102                  QCBNtwrkQI0:
   103   0066           	ds	2		; link
   104   0068 4E7477726B	db	'NtwrkQI0'	; name
   105   0070 0200      	dw	2		; msglen
   106   0072 0100      	dw	1		; nmbmsgs
   107   0074           	ds	2		; dqph
   108   0076           	ds	2		; nqph
CP/M RMAC ASSEM 1.1	#003	MASTER NETWORK I/F MODULE

   109   0078           	ds	2		; msgin
   110   007A           	ds	2		; msgout
   111   007C           	ds	2		; msgcnt
   112   007E           	ds	2		; buffer
   113                  
   114                  UQCBNtwrkQI0:
   115   0080 6600      	dw	QCBNtwrkQI0	; pointer
   116   0082 8400      	dw	BufferQI0Addr	; msgadr
   117                  BufferQI0Addr:
   118   0084 A600      	dw	BufferQ0
   119                  
   120                  QCBNtwrkQO0:
   121   0086           	ds	2		; link
   122   0088 4E7477726B	db	'NtwrkQO0'	; name
   123   0090 0200      	dw	2		; msglen
   124   0092 0100      	dw	1		; nmbmsgs
   125   0094           	ds	2		; dqph
   126   0096           	ds	2		; nqph
   127   0098           	ds	2		; msgin
   128   009A           	ds	2		; msgout
   129   009C           	ds	2		; msgcnt
   130   009E           	ds	2		; buffer
   131                  
   132                  UQCBNtwrkQO0:
   133   00A0 8600      	dw	QCBNtwrkQO0	; pointer
   134   00A2 A400      	dw	BufferQO0Addr	; msgadr
   135                  BufferQO0Addr:
   136   00A4           	ds	2
   137                  
   138                  BufferQ0:
   139   00A6           	ds	1		; FMT
   140   00A7           	ds	1		; DID
   141   00A8           	ds	1		; SID
   142   00A9           	ds	1		; FNC
   143   00AA           	ds	1		; SIZ
   144   00AB           	ds	257		; MSG
   145                  
   146                  ;	Network Interface Process #1
   147                  
   148                  	if	NmbSlvs GE 2
   149                  NtwrkIP1:
   150                  
   151                  	if	NmbSlvs GE 3
   152                  	dw	NtwrkIP2	; link
   153                  	else
   154   01AC 0000      	dw	0		; link
   155                  	endif
   156                  
   157   01AE 00        	db	0		; status
   158   01AF 40        	db	64		; priority
   159   01B0 0E02      	dw	NtwrkIS1+46	; stack pointer
   160   01B2 4E7477726B	db	'NtwrkIP1'	; name
   161   01BA 00        	db	0		; console
   162   01BB FF        	db	0ffh		; memseg
CP/M RMAC ASSEM 1.1	#004	MASTER NETWORK I/F MODULE

   163   01BC           	ds	2		; b
   164   01BE           	ds	2		; thread
   165   01C0           	ds	2		; buff
   166   01C2           	ds	1		; user code & disk slct
   167   01C3           	ds	2		; dcnt
   168   01C5           	ds	1		; searchl
   169   01C6           	ds	2		; searcha
   170   01C8           	ds	2		; active drives
   171   01CA 0000      	dw	0		; HL'
   172   01CC 0000      	dw	0		; DE'
   173   01CE 0000      	dw	0		; BC'
   174   01D0 0000      	dw	0		; AF'
   175   01D2 0000      	dw	0		; IY
   176   01D4 0000      	dw	0		; IX
   177   01D6 2A02      	dw	UQCBNtwrkQI1	; HL
   178   01D8 4A02      	dw	UQCBNtwrkQO1	; DE
   179   01DA 5002      	dw	BufferQ1	; BC
   180   01DC 0001      	dw	0100h		; AF, A = ntwkif console dev #
   181   01DE           	ds	2		; scratch
   182                  
   183                  NtwrkIS1:
   184   01E0 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   185   01E8 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   186   01F0 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   187   01F8 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   188   0200 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   189   0208 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h
   190   020E 6906      	dw	init
   191                  
   192                  QCBNtwrkQI1:
   193   0210           	ds	2		; link
   194   0212 4E7477726B	db	'NtwrkQI1'	; name
   195   021A 0200      	dw	2		; msglen
   196   021C 0100      	dw	1		; nmbmsgs
   197   021E           	ds	2		; dqph
   198   0220           	ds	2		; nqph
   199   0222           	ds	2		; msgin
   200   0224           	ds	2		; msgout
   201   0226           	ds	2		; msgcnt
   202   0228           	ds	2		; buffer
   203                  
   204                  UQCBNtwrkQI1:
   205   022A 1002      	dw	QCBNtwrkQI1	; pointer
   206   022C 2E02      	dw	BufferQI1Addr	; msgadr
   207                  BufferQI1Addr:
   208   022E 5002      	dw	BufferQ1
   209                  
   210                  QCBNtwrkQO1:
   211   0230           	ds	2		; link
   212   0232 4E7477726B	db	'NtwrkQO1'	; name
   213   023A 0200      	dw	2		; msglen
   214   023C 0100      	dw	1		; nmbmsgs
   215   023E           	ds	2		; dqph
   216   0240           	ds	2		; nqph
CP/M RMAC ASSEM 1.1	#005	MASTER NETWORK I/F MODULE

   217   0242           	ds	2		; msgin
   218   0244           	ds	2		; msgout
   219   0246           	ds	2		; msgcnt
   220   0248           	ds	2		; buffer
   221                  
   222                  UQCBNtwrkQO1:
   223   024A 3002      	dw	QCBNtwrkQO1	; pointer
   224   024C 4E02      	dw	BufferQO1Addr	; msgadr
   225                  BufferQO1Addr:
   226   024E           	ds	2
   227                  
   228                  BufferQ1:
   229   0250           	ds	1		; FMT
   230   0251           	ds	1		; DID
   231   0252           	ds	1		; SID
   232   0253           	ds	1		; FNC
   233   0254           	ds	1		; SIZ
   234   0255           	ds	257		; MSG
   235                  	endif
   236                  
   237                  ;	Network Interface Process #2
   238                  
   239                  	if	NmbSlvs GE 3
   240                  NtwrkIP2:
   241                  
   242                  	if	NmbSlvs GE 4
   243                  	dw	NtwrkIP3	; link
   244                  	else
   245                  	dw	0		; link
   246                  	endif
   247                  
   248                  	db	0		; status
   249                  	db	64		; priority
   250                  	dw	NtwrkIS2+46	; stack pointer
   251                  	db	'NtwrkIP2'	; name
   252                  	db	0		; console
   253                  	db	0ffh		; memseg
   254                  	ds	2		; b
   255                  	ds	2		; thread
   256                  	ds	2		; buff
   257                  	ds	1		; user code & disk slct
   258                  	ds	2		; dcnt
   259                  	ds	1		; searchl
   260                  	ds	2		; searcha
   261                  	ds	2		; active drives
   262                  	dw	0		; HL'
   263                  	dw	0		; DE'
   264                  	dw	0		; BC'
   265                  	dw	0		; AF'
   266                  	dw	0		; IY
   267                  	dw	0		; IX
   268                  	dw	UQCBNtwrkQI2	; HL
   269                  	dw	UQCBNtwrkQO2	; DE
   270                  	dw	BufferQ2	; BC
CP/M RMAC ASSEM 1.1	#006	MASTER NETWORK I/F MODULE

   271                  	dw	0200h		; AF, A = ntwkif console dev #
   272                  	ds	2		; scratch
   273                  
   274                  NtwrkIS2:
   275                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   276                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   277                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   278                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   279                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   280                  	dw	0c7c7h,0c7c7h,0c7c7h
   281                  	dw	init
   282                  
   283                  QCBNtwrkQI2:
   284                  	ds	2		; link
   285                  	db	'NtwrkQI2'	; name
   286                  	dw	2		; msglen
   287                  	dw	1		; nmbmsgs
   288                  	ds	2		; dqph
   289                  	ds	2		; nqph
   290                  	ds	2		; msgin
   291                  	ds	2		; msgout
   292                  	ds	2		; msgcnt
   293                  	ds	2		; buffer
   294                  
   295                  UQCBNtwrkQI2:
   296                  	dw	QCBNtwrkQI2	; pointer
   297                  	dw	BufferQI2Addr	; msgadr
   298                  BufferQI2Addr:
   299                  	dw	BufferQ2
   300                  
   301                  QCBNtwrkQO2:
   302                  	ds	2		; link
   303                  	db	'NtwrkQO2'	; name
   304                  	dw	2		; msglen
   305                  	dw	1		; nmbmsgs
   306                  	ds	2		; dqph
   307                  	ds	2		; nqph
   308                  	ds	2		; msgin
   309                  	ds	2		; msgout
   310                  	ds	2		; msgcnt
   311                  	ds	2		; buffer
   312                  
   313                  UQCBNtwrkQO2:
   314                  	dw	QCBNtwrkQO2	; pointer
   315                  	dw	BufferQO2Addr	; msgadr
   316                  BufferQO2Addr:
   317                  	ds	2
   318                  
   319                  BufferQ2:
   320                  	ds	1		; FMT
   321                  	ds	1		; DID
   322                  	ds	1		; SID
   323                  	ds	1		; FNC
   324                  	ds	1		; SIZ
CP/M RMAC ASSEM 1.1	#007	MASTER NETWORK I/F MODULE

   325                  	ds	257		; MSG
   326                  	endif
   327                  
   328                  ;	Network Interface Process #3
   329                  
   330                  	if	NmbSlvs GE 4
   331                  NtwrkIP3:
   332                  	dw	0		; link
   333                  	db	0		; status
   334                  	db	64		; priority
   335                  	dw	NtwrkIS3+46	; stack pointer
   336                  	db	'NtwrkIP3'	; name
   337                  	db	0		; console
   338                  	db	0ffh		; memseg
   339                  	ds	2		; b
   340                  	ds	2		; thread
   341                  	ds	2		; buff
   342                  	ds	1		; user code & disk slct
   343                  	ds	2		; dcnt
   344                  	ds	1		; searchl
   345                  	ds	2		; searcha
   346                  	ds	2		; active drives
   347                  	dw	0		; HL'
   348                  	dw	0		; DE'
   349                  	dw	0		; BC'
   350                  	dw	0		; AF'
   351                  	dw	0		; IY
   352                  	dw	0		; IX
   353                  	dw	UQCBNtwrkQI3	; HL
   354                  	dw	UQCBNtwrkQO3	; DE
   355                  	dw	BufferQ3	; BC
   356                  	dw	0300h		; AF, A = ntwkif console dev #
   357                  	ds	2		; scratch
   358                  
   359                  NtwrkIS3:
   360                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   361                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   362                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   363                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   364                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   365                  	dw	0c7c7h,0c7c7h,0c7c7h
   366                  	dw	init
   367                  
   368                  QCBNtwrkQI3:
   369                  	ds	2		; link
   370                  	db	'NtwrkQI3'	; name
   371                  	dw	2		; msglen
   372                  	dw	1		; nmbmsgs
   373                  	ds	2		; dqph
   374                  	ds	2		; nqph
   375                  	ds	2		; msgin
   376                  	ds	2		; msgout
   377                  	ds	2		; msgcnt
   378                  	ds	2		; buffer
CP/M RMAC ASSEM 1.1	#008	MASTER NETWORK I/F MODULE

   379                  
   380                  UQCBNtwrkQI3:
   381                  	dw	QCBNtwrkQI3	; pointer
   382                  	dw	BufferQI3Addr	; msgadr
   383                  BufferQI3Addr:
   384                  	dw	BufferQ3
   385                  
   386                  QCBNtwrkQO3:
   387                  	ds	2		; link
   388                  	db	'NtwrkQO3'	; name
   389                  	dw	2		; msglen
   390                  	dw	1		; nmbmsgs
   391                  	ds	2		; dqph
   392                  	ds	2		; nqph
   393                  	ds	2		; msgin
   394                  	ds	2		; msgout
   395                  	ds	2		; msgcnt
   396                  	ds	2		; buffer
   397                  
   398                  UQCBNtwrkQO3:
   399                  	dw	QCBNtwrkQO3	; pointer
   400                  	dw	BufferQO3Addr	; msgadr
   401                  BufferQO3Addr:
   402                  	ds	2
   403                  
   404                  BufferQ3:
   405                  	ds	1		; FMT
   406                  	ds	1		; DID
   407                  	ds	1		; SID
   408                  	ds	1		; FNC
   409                  	ds	1		; SIZ
   410                  	ds	257		; MSG
   411                  	endif
   412                  
   413                  
   414                  	if	WtchDg
   415                  ;  Watchdog Timer Process
   416                  ;
   417                  WatchDogPD:
   418                  
   419                  	if	NmbSlvs GT 1
   420                  	dw	NtwrkIP1	; link to the remaining NETWRKIF PD's
   421                  	else
   422                  	dw	0		; link
   423                  	endif
   424                  
   425                  	db	0		; status
   426                  	db	64		; priority
   427                  	dw	WatchDogSTK+46	; stack pointer
   428                  	db	'WatchDog'	; name
   429                  	db	0		; console
   430                  	db	0ffh		; memseg
   431                  	ds	2		; b
   432                  	ds	2		; thread
CP/M RMAC ASSEM 1.1	#009	MASTER NETWORK I/F MODULE

   433                  	ds	2		; buff
   434                  	ds	1		; user code & disk slct
   435                  	ds	2		; dcnt
   436                  	ds	1		; searchl
   437                  	ds	2		; searcha
   438                  	ds	2		; active drives
   439                  	dw	0		; HL'
   440                  	dw	0		; DE'
   441                  	dw	0		; BC'
   442                  	dw	0		; AF'
   443                  	dw	0		; IY
   444                  	dw	0		; IX
   445                  	dw	0		; HL
   446                  	dw	0		; DE
   447                  	dw	0		; BC
   448                  	dw	0		; AF
   449                  	ds	2		; scratch
   450                  
   451                  WatchDogSTK:
   452                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   453                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   454                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   455                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   456                  	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   457                  	dw	0c7c7h,0c7c7h,0c7c7h
   458                  	dw	WatchDog
   459                  
   460                  WatchDogTime:
   461                  	dw	$-$	; one-second counter
   462                  
   463                  WatchDogTable:
   464                  ;	     Waiting Timeout   Start	Flag  Requester
   465                  	db	0,	0,	0,0,	0ah	; #0
   466                  	db	0,	0,	0,0,	0bh	; #1
   467                  	db	0,	0,	0,0,	0fh	; #2
   468                  	db	0,	0,	0,0,	0dh	; #3
   469                  	endif
   470                  
   471                  	if	mutexin or mutexout
   472                  QCBMXSXmitq:			; MX queue for requester transmitting
   473                  
   474                  	ds	2		; link
   475                  	db	'MXSXmitq'	; name
   476                  	dw	0		; msglen
   477                  	dw	1		; nmbmsgs
   478                  	ds	2		; dqph
   479                  	ds	2		; nqph
   480                  	ds	2		; msgin
   481                  	ds	2		; msgout
   482                  	ds	2		; msgcnt
   483                  	ds	2		; buffer (owner PD)
   484                  
   485                  UQCBMXSXmitq:
   486                  	dw	QCBMXSXmitq
CP/M RMAC ASSEM 1.1	#010	MASTER NETWORK I/F MODULE

   487                  ;	dw	0		; no message, since it's an MX queue
   488                  ;	db	'MXSXmitq'	; no name, since the QCB pointer is resolved
   489                  	endif
   490                  
   491                  ;	Server Configuration Table
   492                  
   493                  configtbl:
   494   0356 00        	db	0		; Server status byte
   495   0357 00        	db	0		; Server ID
   496   0358 02        	db	NmbSlvs		; Maximum number of requesters supported
   497   0359 00        	db	0		; Number of requesters currently logged-in
   498   035A 0000      	dw	0000h		; 16 bit vector of logged in requesters
   499   035C           	ds	16		; Requester ID's currently logged-in
   500   036C 5041535357	db	'PASSWORD' 	; login password
   501                  
   502   0001 =         nmsg		equ	1	; number of messages buffered
   503   0096 =         slave$stk$len	equ	96h	; server process stack size
   504                  
   505                  	if	NmbSlvs GE 2
   506                  slave1$stk:
   507   0374           	ds	slave$stk$len-2		
   508   0408 0A04      	dw	Slave1			
   509                  					
   510                  	endif
   511                  
   512                  	if	NmbSlvs GE 3
   513                  slave2$stk:
   514                  	ds	slave$stk$len-2
   515                  	dw	Slave2
   516                  	endif
   517                  
   518                  	if	NmbSlvs GE 4
   519                  slave3$stk:
   520                  	ds	slave$stk$len-2
   521                  	dw	Slave3
   522                  	endif
   523                  
   524                  	if	NmbSlvs GE 2
   525                  Slave1:
   526   040A           	ds	52		; SERVR1PR processor descriptor
   527                  	endif
   528                  
   529                  	if	NmbSlvs GE 3
   530                  Slave2:
   531                  	ds	52		; SERVR2PR processor descriptor
   532                  	endif
   533                  
   534                  	if	NmbSlvs GE 4
   535                  Slave3:
   536                  	ds	52		; SERVR3PR processor descriptor
   537                  	endif
   538                  
   539                  ;	Local Data Segment
   540                  
CP/M RMAC ASSEM 1.1	#011	MASTER NETWORK I/F MODULE

   541                  BinaryASCII:
   542   043E FF        	db	0ffh		; Requester #0: 0=7 bit ASCII, FF=8 bit binary
   543   043F FF        	db	0ffh		;       #1
   544   0440 FF        	db	0ffh		;       #2
   545   0441 FF        	db	0ffh		;       #3
   546                  
   547                  Networkstatus:
   548   0442 00        	db	0		; Slave #0 network status byte
   549   0443 00        	db	0		;       #1
   550   0444 00        	db	0		;       #2
   551   0445 00        	db	0		;       #3
   552                  
   553   0446 0000      conin:	dw	$-$		; save area for XIOS routine address
   554                  
   555   000A =         max$retries	equ	10	; maximum send message retries
   556                  ;
   557                  ;	The following tables are for use in the ALTOS i/o routines.
   558                  ;	Note that this program MUST be used with an XIOS which allows
   559                  ;	using the second printer port as a console port - Accessed as console
   560                  ;	#4
   561                  
   562   002B =         Console4$status equ	02bh
   563   002F =         Console3$status	equ	02fh
   564   002D =         Console2$status equ	02dh
   565   0029 =         Printer2$status	equ	029h	; ALSO CONSOLE #4
   566                  
   567                  	if	z80
   568                  ;
   569                  ;	ENTRIES IN THE FOLLOWING TWO TABLES MUST MATCH !!!!
   570                  
   571                  status$ports:
   572   0448 2B        	db	Console4$status ; Console 4 (Requester 0) status port
   573   0449 2F        	db	Console3$status	; Console 3 (Requester 1) status port
   574   044A 2D        	db	Console2$status	; Console 2 (Requester 2) status port
   575   044B 29        	db	Printer2$status	; Printer 2 (Requester 3) status port
   576                  	endif
   577                  
   578                  chariotbl:			; Relationship between requesters and consoles
   579   044C 03        	db	3
   580   044D 02        	db	2
   581   044E 01        	db	1
   582   044F 04        	db	4
   583                  
   584                  ;	Network Status Byte Equates
   585                  
   586   0080 =         ntwrktxrdy	equ	10000000b	; NETWRKIF ready to send msg
   587   0010 =         active		equ	00010000b	; requester logged into network
   588   0008 =         msgerr		equ	00001000b	; error in received message
   589   0004 =         ntwrk		equ	00000100b	; network alive
   590   0002 =         msgovr		equ	00000010b	; message overrun
   591   0001 =         ntwrkrxrdy	equ	00000001b	; NETWRKIF has rcvd msg
   592                  
   593                  ;	BDOS and XDOS Equates
   594                  
CP/M RMAC ASSEM 1.1	#012	MASTER NETWORK I/F MODULE

   595   0085 =         flagset	equ	133		; flag set
   596   0086 =         makeq	equ	134		; make queue
   597   0089 =         readq	equ	137		; read queue
   598   008B =         writeq	equ	139		; write queue
   599   008D =         delay	equ	141		; delay
   600   008E =         dsptch	equ	142		; dispatch
   601   0090 =         createp	equ	144		; create process
   602   009A =         sydatad	equ	154		; system data page address
   603   0083 =         poll	equ	083h		; Poll device
   604                  
   605                  ;	General Equates
   606                  
   607   0001 =         SOH	equ	01h		; Start of Header
   608   0002 =         STX	equ	02h		; Start of Data
   609   0003 =         ETX	equ	03h		; End of Data
   610   0004 =         EOT	equ	04h		; End of Transmission
   611   0005 =         ENQ	equ	05h		; Enquire
   612   0006 =         ACK	equ	06h		; Acknowledge
   613   000A =         LF	equ	0ah		; Line Feed
   614   000D =         CR	equ	0dh		; Carriage Return
   615   0015 =         NAK	equ	15h		; Negative Acknowledge
   616                  
   617   0010 =         printer2	equ	10h	; special poll device number for second 
   618                  				; printer port
   619                  
   620                  ;	Utility Procedures
   621                  
   622                  bdos:
   623   0450 2A0000    	lhld	bdosadr		; get XDOS entry point from RSP start
   624   0453 E9        	pchl
   625                  
   626                  Nibout:				; A = nibble to be transmitted in ASCII
   627   0454 FE0A      	cpi	10
   628   0456 D25F04    	jnc	nibatof		; jump if A-F
   629   0459 C630      	adi	'0'
   630   045B 4F        	mov	c,a
   631   045C C36804    	jmp	Charout
   632                  nibatof:
   633   045F C637      	adi	'A'-10
   634   0461 4F        	mov	c,a
   635   0462 C36804    	jmp	Charout
   636                  
   637                  PreCharout:
   638   0465 7A        	mov	a,d
   639   0466 81        	add	c
   640   0467 57        	mov	d,a		; update the checksum
   641                  
   642                  	if	z80		; Z80 version, using OUT A,(C) instruction
   643                  char$out:
   644                  
   645                  ;	Character output routine for network i/o
   646                  ;	using the ALTOS SIO ports
   647                  ;
   648                  ;	Z80 version: this can use indirect port numbers in a clean,
CP/M RMAC ASSEM 1.1	#013	MASTER NETWORK I/F MODULE

   649                  ;	reentrant fashion
   650                  ;
   651                  ;	Entry: C register contains 8 bit value to transmit
   652                  ;	Entry : Slave number in register b
   653                  
   654   0468 E5        	push	h
   655   0469 D5        	push	d
   656   046A C5        	push	b
   657   046B 51        	mov	d, c		; save the character
   658   046C 214804    	lxi	h, status$ports
   659   046F 48        	mov	c, b
   660   0470 0600      	mvi	b, 0		; set (BC) = (b)
   661   0472 09        	dad	b
   662   0473 4E        	mov	c,m
   663                  
   664                  ; 	Now C contains the address of the correct status port
   665                  
   666                  outputloop:
   667   0474 3E10      	mvi	a,10h
   668                  
   669                  ;	out	(c),a
   670   0476 ED79      	db	0edh,79h
   671                  
   672                  ;	in	a,(c)
   673   0478 ED78      	db	0edh,78h
   674                  
   675   047A E604      	ani	04h		; wait for TXready
   676   047C CA7404    	jz	outputloop
   677                  
   678                  ; 	In the Altos system, data registers are one below status registers...
   679                  
   680   047F 0D        	dcr	c
   681                  
   682                  ;	out	(c),d
   683   0480 ED51      	db	0edh,51h
   684                  
   685   0482 C1        	pop	b
   686   0483 D1        	pop	d
   687   0484 E1        	pop	h
   688   0485 C9        	ret
   689                  
   690                  	else
   691                  
   692                  char$out:
   693                  
   694                  ;	Character output routine for network I/O
   695                  ;	using ALTOS SIO ports
   696                  ;
   697                  ;	8080 version: This has to dispatch and then use direct port I/O
   698                  ;	--extremely messy to do reentrantly
   699                  ;
   700                  ;	Entry:	C = character to transmit
   701                  ;		B = slave id byte
   702                  
CP/M RMAC ASSEM 1.1	#014	MASTER NETWORK I/F MODULE

   703                  	push 	h
   704                  	push	d
   705                  	push	b
   706                  
   707                  	lxi	d,out0		; dispatch address = 
   708                  	mov	l,b		;   out0 + slaveid*16
   709                  	mvi	h,0
   710                  	dad	h
   711                  	dad	h
   712                  	dad	h
   713                  	dad	h
   714                  	dad	d
   715                  	mvi	a,10h		;load "get transmit status" value
   716                  	pchl			;dispatch 
   717                  
   718                  out0:
   719                  	out	Console4$status	;wait for TXready status
   720                  	in	Console4$status
   721                  	ani	4
   722                  	jz	out0
   723                  
   724                  	mov	a,c
   725                  	out	Console4$status-1	;write the character
   726                  	pop	b
   727                  	pop	d
   728                  	pop	h
   729                  	ret
   730                  
   731                  out1:	out	Console3$status
   732                  	in	Console3$status
   733                  	ani	4
   734                  	jz	out1
   735                  
   736                  	mov	a,c
   737                  	out	Console3$status-1
   738                  	pop	b
   739                  	pop	d
   740                  	pop	h
   741                  	ret
   742                  
   743                  out2:	out	Console2$status
   744                  	in	Console2$status
   745                  	ani	4
   746                  	jz	out2
   747                  
   748                  	mov	a,c
   749                  	out	Console2$status-1
   750                  	pop	b
   751                  	pop	d
   752                  	pop	h
   753                  	ret
   754                  
   755                  out3:	out	Printer2$status
   756                  	in	Printer2$status
CP/M RMAC ASSEM 1.1	#015	MASTER NETWORK I/F MODULE

   757                  	ani	4
   758                  	jz	out3
   759                  
   760                  	mov	a,c
   761                  	out	Printer2$status-1
   762                  	pop	b
   763                  	pop	d
   764                  	pop	h
   765                  	ret
   766                  
   767                  	endif
   768                  
   769                  
   770                  Nibin:				; return nibble in A register
   771   0486 CDBD04    	call	Charin
   772   0489 D8        	rc
   773   048A E67F      	ani	07fh
   774   048C D630      	sui	'0'
   775   048E FE0A      	cpi	10
   776   0490 DAA604    	jc	Nibin$return 	; must be 0-9
   777   0493 C6F9      	adi	('0'-'A'+10) and 0ffh
   778   0495 FE10      	cpi	16
   779   0497 DAA604    	jc	Nibin$return 	; must be 10-15
   780   049A 3A4204    	lda	networkstatus
   781   049D F608      	ori	msgerr
   782   049F 324204    	sta	networkstatus
   783   04A2 3E00      	mvi	a,0
   784   04A4 37        	stc
   785   04A5 C9        	ret
   786                  
   787                  Nibin$return:
   788   04A6 B7        	ora	a
   789   04A7 C9        	ret
   790                  
   791                  xChar$in:			; Get the first character using polled 
   792                  				; console I/O. Note that the rest of the 
   793                  				; message will be received using direct
   794                  				; port I/O with interrupts disabled.
   795                  				; OVERRUNS ARE NOT POSSIBLE USING THIS SCHEME
   796                  
   797   04A8 E5        	push	h
   798   04A9 C5        	push	b
   799   04AA 21BA04    	lxi	h, Charin$return
   800   04AD E5        	push	h
   801   04AE 48        	mov	c,b
   802   04AF 0600      	mvi	b,0
   803   04B1 214C04    	lxi	h, chariotbl
   804   04B4 09        	dad	b
   805   04B5 56        	mov	d, m		; Get the console number
   806   04B6 2A4604    	lhld	conin
   807   04B9 E9        	pchl			; vector off
   808                  
   809                  Charin$return:
   810   04BA C1        	pop	b
CP/M RMAC ASSEM 1.1	#016	MASTER NETWORK I/F MODULE

   811   04BB E1        	pop	h
   812   04BC C9        	ret
   813                  
   814                  
   815                  	if	z80
   816                  char$in:
   817                  
   818                  ;	Character input routine for network i/o
   819                  ;	using the ALTOS SIO ports at 125k baud
   820                  ;
   821                  ;	Z80 Version uses indirect port addresses loaded into register C
   822                  ;	
   823                  ;	Entry : Slave number in register b
   824                  ;	Exit  : Character in register a
   825                  ;
   826   04BD E5        	push	h
   827   04BE C5        	push	b
   828   04BF 214804    	lxi	h, status$ports
   829   04C2 48        	mov	c, b
   830   04C3 0600      	mvi	b, 0		; set (BC) = (b)
   831   04C5 09        	dad	b
   832   04C6 4E        	mov	c,m
   833                  
   834                  ; 	Now C contains the address of the correct status port
   835                  
   836   04C7 2E50      	mvi	l, 80
   837                  
   838                  inputloop1:
   839   04C9 2D        	dcr	l
   840   04CA CADA04    	jz	retout
   841                  
   842                  ;	in	a,(c)
   843   04CD ED78      	db	0edh,78h
   844                  
   845   04CF E601      	ani	01h		; wait for RXready
   846   04D1 CAC904    	jz	inputloop1
   847                  
   848                  ; 	In the Altos system, data registers are one below status registers...
   849                  
   850   04D4 0D        	dcr	c
   851                  
   852                  ;	in	a,(c)
   853   04D5 ED78      	db	0edh,78h	;get the character
   854                  
   855   04D7 C1        	pop	b
   856   04D8 E1        	pop	h
   857   04D9 C9        	ret
   858                  
   859                  retout:
   860   04DA 37        	stc			;set carry => error flag
   861   04DB C1        	pop	b
   862   04DC E1        	pop	h
   863   04DD C9        	ret
   864                  
CP/M RMAC ASSEM 1.1	#017	MASTER NETWORK I/F MODULE

   865                  	else
   866                  
   867                  char$in:
   868                  
   869                  ;	Character input routine for network I/O
   870                  ;	using ALTOS SIO ports
   871                  ;
   872                  ;	8080 Version uses same nasty dispatch mechanism that the output
   873                  ;	routine used
   874                  ;
   875                  ;	Entry:	B = Slave ID
   876                  ;	Exit:	A = character input
   877                  
   878                  	push 	h
   879                  	push	d
   880                  	push	b
   881                  	lxi	d,in0		; HL = in0 + 17*slaveid
   882                  	mov	l,b
   883                  	mvi	h,0
   884                  	xchg
   885                  	dad	d
   886                  	xchg
   887                  	dad	h
   888                  	dad	h
   889                  	dad 	h
   890                  	dad	h
   891                  	dad	d
   892                  
   893                  	mvi	c,80		; load status retry count
   894                  	pchl			; dispatch
   895                  
   896                  in0:
   897                  	dcr	c
   898                  	jz	retout		; error return if retry timeout
   899                  
   900                  	in	Console4$status	; wait for RXready
   901                  	ani	1
   902                  	jz	in0
   903                  
   904                  	in	Console4$status-1	; get the character
   905                  	pop	b
   906                  	pop	d
   907                  	pop	h
   908                  	ret
   909                  
   910                  in1:
   911                  	dcr	c
   912                  	jz	retout			
   913                  
   914                  	in	Console3$status		
   915                  	ani	1
   916                  	jz	in1
   917                  
   918                  	in	Console3$status-1	
CP/M RMAC ASSEM 1.1	#018	MASTER NETWORK I/F MODULE

   919                  	pop	b
   920                  	pop	d
   921                  	pop	h
   922                  	ret
   923                  
   924                  in2:
   925                  	dcr	c
   926                  	jz	retout			
   927                  
   928                  	in	Console2$status		
   929                  	ani	1
   930                  	jz	in2
   931                  
   932                  	in	Console2$status-1	
   933                  	pop	b
   934                  	pop	d
   935                  	pop	h
   936                  	ret
   937                  in3:
   938                  	dcr	c
   939                  	jz	retout			
   940                  
   941                  	in	Printer2$status		
   942                  	ani	1
   943                  	jz	in3
   944                  
   945                  	in	Printer2$status-1	
   946                  	pop	b
   947                  	pop	d
   948                  	pop	h
   949                  	ret
   950                  
   951                  retout:				; error return (carry=1)
   952                  	stc
   953                  	pop	b
   954                  	pop	d
   955                  	pop	h
   956                  	ret
   957                  
   958                  	endif
   959                  
   960                  
   961                  Netout:				; C = byte to be transmitted
   962   04DE 7A        	mov	a,d
   963   04DF 81        	add	c
   964   04E0 57        	mov	d,a
   965   04E1 3A3E04    	lda	BinaryASCII
   966   04E4 B7        	ora	a
   967   04E5 C26804    	jnz	Charout		; transmit byte in Binary mode
   968   04E8 79        	mov	a,c
   969   04E9 F5        	push	psw
   970   04EA 1F        	rar
   971   04EB 1F        	rar
   972   04EC 1F        	rar
CP/M RMAC ASSEM 1.1	#019	MASTER NETWORK I/F MODULE

   973   04ED 1F        	rar
   974   04EE E60F      	ani	0FH		; Shift HI nibble to LO nibble
   975   04F0 CD5404    	call	Nibout
   976   04F3 F1        	pop	psw
   977   04F4 E60F      	ani	0FH
   978   04F6 C35404    	jmp	Nibout
   979                  
   980                  Netin:				; byte returned in A register
   981                  				; D  = checksum accumulator
   982   04F9 3A3E04    	lda	BinaryASCII
   983   04FC B7        	ora	a
   984   04FD CA0705    	jz	ASCIIin
   985   0500 CDBD04    	call	charin		;receive byte in Binary mode
   986   0503 D8        	rc
   987   0504 C31705    	jmp	chksin
   988                  
   989                  ASCIIin:
   990   0507 CD8604    	call	Nibin
   991   050A D8        	rc
   992   050B 87        	add	a
   993   050C 87        	add	a
   994   050D 87        	add	a
   995   050E 87        	add	a
   996   050F F5        	push	psw
   997   0510 CD8604    	call	Nibin
   998   0513 D8        	rc
   999   0514 E3        	xthl
  1000   0515 B4        	ora	h
  1001   0516 E1        	pop	h
  1002                  chksin:
  1003   0517 B7        	ora	a
  1004   0518 F5        	push	psw
  1005   0519 82        	add	d		; add & update checksum accum.
  1006   051A 57        	mov	d,a
  1007   051B F1        	pop	psw
  1008   051C C9        	ret
  1009                  
  1010                  Msgin:				; HL = destination address
  1011                  				; E  = # bytes to input
  1012   051D CDF904    	call	Netin
  1013   0520 D8        	rc
  1014   0521 77        	mov	m,a
  1015   0522 23        	inx	h
  1016   0523 1D        	dcr	e
  1017   0524 C21D05    	jnz	Msgin
  1018   0527 C9        	ret
  1019                  
  1020                  Msgout:				; HL = source address
  1021                  				; E  = # bytes to output
  1022                  				; D  = checksum
  1023                  				; C  = preamble character
  1024   0528 1600      	mvi	d,0
  1025   052A CD6504    	call	PreCharout
  1026                  
CP/M RMAC ASSEM 1.1	#020	MASTER NETWORK I/F MODULE

  1027                  Msgoutloop:
  1028   052D 4E        	mov	c,m
  1029   052E 23        	inx	h
  1030   052F CDDE04    	call	Netout
  1031   0532 1D        	dcr	e
  1032   0533 C22D05    	jnz	Msgoutloop
  1033   0536 C9        	ret
  1034                  
  1035                  ;	Network Initialization
  1036                  
  1037                  nwinit:
  1038                  
  1039                  ;	device initialization, as required
  1040                  
  1041                  
  1042   0537 3E47      	mvi	a,047h		;sets up CTC for baud rate of 125k
  1043   0539 D331      	out	031h
  1044                  
  1045                  	if	nmbslvs ge 3	;initialize only the ports that are needed
  1046                  	out	030h
  1047                  	endif
  1048                  
  1049                  	if	nmbslvs ge 4
  1050                  	out	032h
  1051                  	endif
  1052                  
  1053   053B 3E01      	mvi	a,1		;count of one => max speed
  1054   053D D331      	out	031h
  1055                  
  1056                  	if	nmbslvs ge 3
  1057                  	out	030h		
  1058                  	endif
  1059                  
  1060                  	if	nmbslvs ge 4
  1061                  	out     032h
  1062                  	endif
  1063                  
  1064                  
  1065                  ;	Find address of XIOS console output routine
  1066                  
  1067   053F 2A0100    	lhld	0001h		; get warmstart entry in the XIOS jump table
  1068   0542 23        	inx	h
  1069   0543 5E        	mov	e, m
  1070   0544 23        	inx	h
  1071   0545 56        	mov	d, m
  1072   0546 210600    	lxi	h, 0006h	; Offset for conin routine
  1073   0549 19        	dad	d
  1074   054A 224604    	shld	conin		; save the address
  1075   054D AF        	xra	a		; return code is 0=success
  1076   054E C9        	ret
  1077                  
  1078                  
  1079                  ;	Network Status
  1080                  
CP/M RMAC ASSEM 1.1	#021	MASTER NETWORK I/F MODULE

  1081                  nwstat:				; C = Slave #
  1082   054F 0600      	mvi	b,0
  1083   0551 214204    	lxi	h,networkstatus
  1084   0554 09        	dad	b
  1085   0555 7E        	mov	a,m
  1086   0556 47        	mov	b,a
  1087   0557 E6F5      	ani	not (msgerr+msgovr)
  1088   0559 77        	mov	m,a
  1089   055A 78        	mov	a,b
  1090   055B C9        	ret
  1091                  
  1092                  
  1093                  ;	Return Configuration Table Address
  1094                  
  1095                  cfgadr:
  1096   055C 215603    	lxi	h,configtbl
  1097   055F C9        	ret
  1098                  
  1099                  
  1100                  ;	Send Message on Network
  1101                  
  1102                  sndmsg:				; DE = message addr
  1103                  				;  C = Slave #
  1104   0560 41        	mov	b,c
  1105   0561 3E0A      	mvi	a,max$retries	; A = max$retries
  1106                  
  1107                  send:
  1108   0563 F5        	push	psw
  1109                  
  1110                  	if	mutexout
  1111                  
  1112                  ;	Use mutual exclusion if it is possible for some unsolicited input
  1113                  ;	to stomp on your output (This is nice is you;re running some sort
  1114                  ;	of multi-drop protocol)
  1115                  
  1116                  	push	b		
  1117                  	push	d
  1118                  	mvi	c,readq
  1119                  	lxi	d,UQCBMXSXmitq
  1120                  	call	bdos		; obtain mutual exclusion token
  1121                  	pop	d
  1122                  	pop	b
  1123                  	endif
  1124                  
  1125   0564 EB        	xchg
  1126   0565 E5        	push	h
  1127   0566 F3        	di			; disable interrupts to avoid underrun
  1128   0567 0E05      	mvi	c,ENQ
  1129   0569 CD6804    	call	Charout		; send ENQ
  1130   056C CDA005    	call	getACK		; won't return on an error
  1131   056F 1E05      	mvi	e,5
  1132   0571 0E01      	mvi	c,SOH
  1133   0573 CD2805    	call	Msgout		; send SOH FMT DID SID FNC SIZ
  1134   0576 AF        	xra	a
CP/M RMAC ASSEM 1.1	#022	MASTER NETWORK I/F MODULE

  1135   0577 92        	sub	d
  1136   0578 4F        	mov	c,a
  1137   0579 CDDE04    	call	Netout		; send HCS (header checksum)
  1138   057C CDA005    	call	getACK		; won't return on an error
  1139   057F 2B        	dcx	h
  1140   0580 5E        	mov	e,m
  1141   0581 23        	inx	h
  1142   0582 1C        	inr	e
  1143   0583 0E02      	mvi	c,STX
  1144   0585 CD2805    	call	Msgout		; send STX DB0 DB1 ...
  1145   0588 0E03      	mvi	c,ETX
  1146   058A CD6504    	call	PreCharout	; send ETX
  1147   058D AF        	xra	a
  1148   058E 92        	sub	d
  1149   058F 4F        	mov	c,a
  1150   0590 CDDE04    	call	Netout		; send CKS
  1151   0593 0E04      	mvi	c,EOT
  1152   0595 CD6504    	call	PreCharout	; send EOT
  1153   0598 CDA005    	call	getACK		; won't return on an error
  1154   059B D1        	pop	d		; discard message address
  1155   059C F1        	pop	psw		; discard retry counter
  1156                  
  1157                  	if	mutexout
  1158                  	call	release$MX
  1159                  	endif
  1160                  
  1161   059D FB        	ei			; return from suspended animation
  1162   059E AF        	xra	a
  1163   059F C9        	ret			; A = 0, successful send message
  1164                  
  1165                  getACK:
  1166   05A0 CDBD04    	call	Charin
  1167   05A3 DAAB05    	jc	getACK$timeout	; receive timeout-->start error recovery
  1168   05A6 E67F      	ani	7fh
  1169   05A8 D606      	sui	ACK
  1170   05AA C8        	rz
  1171                  
  1172                  getACK$timeout:
  1173   05AB D1        	pop	d		; discard return address
  1174                  
  1175                  	if	mutexout
  1176                  	push	b
  1177                  	call	release$MX
  1178                  	pop	b
  1179                  	endif
  1180                  
  1181   05AC D1        	pop	d		; DE = message address
  1182   05AD F1        	pop	psw		; A = retry count
  1183   05AE 3D        	dcr	a
  1184   05AF C26305    	jnz	send		; continue if retry count non-zero
  1185   05B2 3D        	dcr	a		; else-->we're dead-->A = 0ffh
  1186   05B3 C9        	ret			; failed to send message
  1187                  
  1188                  	if	mutexin or mutexout
CP/M RMAC ASSEM 1.1	#023	MASTER NETWORK I/F MODULE

  1189                  
  1190                  release$MX:			; send back requester transmit MX message
  1191                  	mvi	c,writeq
  1192                  	lxi	d,UQCBMXSXmitq
  1193                  	jmp	bdos
  1194                  	endif
  1195                  
  1196                  ;	Receive Message from Network
  1197                  
  1198                  rcvmsg:				; DE = message addr
  1199                  				;  C = Slave #
  1200   05B4 41        	mov	b,c
  1201                  
  1202                  receive:
  1203   05B5 EB        	xchg
  1204   05B6 E5        	push	h
  1205   05B7 CDBF05    	call	get$ENQ
  1206                  
  1207                  ; 	a return to this point indicates an error
  1208                  
  1209                  receive$retry:
  1210   05BA FB        	ei			; re-enable other processes
  1211                  
  1212                  	if	mutexin
  1213                  	push	b
  1214                  	call	release$MX
  1215                  	pop	b
  1216                  	endif
  1217                  
  1218   05BB D1        	pop	d
  1219   05BC C3B505    	jmp	receive
  1220                  
  1221                  get$ENQ:			; get first character of message using
  1222                  				; polled console I/O
  1223   05BF CDA804    	call	xCharin
  1224   05C2 DABF05    	jc	get$ENQ
  1225   05C5 E67F      	ani	7fh
  1226   05C7 FE05      	cpi	ENQ		; Start of Message ?
  1227   05C9 C2BF05    	jnz	get$ENQ
  1228                  
  1229                  	if	mutexin
  1230                  
  1231                  ;	Don't get too involved with receiving a message if some other
  1232                  ;	NETWRKIF process is going to stomp you by sending a message along
  1233                  ;	the same line
  1234                  
  1235                  	push	b
  1236                  	push	h
  1237                  	mvi	c,readq
  1238                  	lxi	d,UQCBMXSXmitq
  1239                  	call	bdos
  1240                  	pop	h
  1241                  	pop	b
  1242                  	endif
CP/M RMAC ASSEM 1.1	#024	MASTER NETWORK I/F MODULE

  1243                  
  1244   05CC 0E06      	mvi	c,ACK
  1245   05CE F3        	di			; requester in gear now serve only him
  1246                  
  1247   05CF CD6804    	call	charout		; send ACK to requester, allowing transmit
  1248   05D2 CDBD04    	call	Charin
  1249   05D5 D8        	rc
  1250   05D6 E67F      	ani	7fh
  1251   05D8 FE01      	cpi	SOH
  1252   05DA C0        	rnz
  1253   05DB 57        	mov	d,a		; initialize the HCS
  1254   05DC 1E05      	mvi	e,5
  1255   05DE CD1D05    	call	Msgin
  1256   05E1 D4F904    	cnc	Netin
  1257   05E4 D8        	rc
  1258   05E5 7A        	mov	a,d
  1259   05E6 B7        	ora	a
  1260   05E7 C21406    	jnz	sendNAK		; jmp & send NAK if HCS <> 0
  1261   05EA 0E06      	mvi	c,ACK
  1262   05EC CD6804    	call	Charout
  1263   05EF CDBD04    	call	Charin
  1264   05F2 D8        	rc
  1265   05F3 E67F      	ani	7fh
  1266   05F5 FE02      	cpi	STX
  1267   05F7 C0        	rnz
  1268   05F8 57        	mov	d,a		; initialize the CKS
  1269   05F9 2B        	dcx	h
  1270   05FA 5E        	mov	e,m
  1271   05FB 23        	inx	h
  1272   05FC 1C        	inr	e
  1273   05FD CD1D05    	call	msgin
  1274   0600 D4BD04    	cnc	Charin
  1275   0603 D8        	rc
  1276   0604 E67F      	ani	7fh
  1277   0606 FE03      	cpi	ETX
  1278   0608 C0        	rnz
  1279   0609 82        	add	d
  1280   060A 57        	mov	d,a
  1281   060B CDF904    	call	Netin		; get Checksum byte
  1282   060E D8        	rc
  1283   060F 7A        	mov	a,d
  1284   0610 B7        	ora	a		; should be zero
  1285   0611 CA1906    	jz	sendACK		; jump if checksum OK
  1286                  
  1287                  sendNAK:			; else-->refuse the message
  1288   0614 0E15      	mvi	c,NAK
  1289   0616 C36804    	jmp	Charout		; send NAK and return to receive$retry
  1290                  
  1291                  sendACK:			; come here if message was received properly
  1292   0619 CDBD04    	call	Charin		; get EOT
  1293   061C D8        	rc
  1294   061D E67F      	ani	7fh
  1295   061F FE04      	cpi	EOT
  1296   0621 C0        	rnz
CP/M RMAC ASSEM 1.1	#025	MASTER NETWORK I/F MODULE

  1297   0622 0E06      	mvi	c,ACK
  1298   0624 CD6804    	call	Charout		; send ACK if checksum ok
  1299   0627 D1        	pop	d		; discard return address
  1300   0628 D1        	pop	d		; discard message address
  1301   0629 FB        	ei			; Dispense with the Rip Van Winkle act
  1302                  
  1303                  	if	mutexin
  1304                  	call	release$MX
  1305                  	endif
  1306                  
  1307   062A AF        	xra	a
  1308   062B C9        	ret
  1309                  
  1310                  
  1311                  restore:			
  1312                  
  1313                  ; 	This routine allows N copies of NtwrkIPx to run reentrantly.  
  1314                  ;	It takes the values that were pre-initialized in the process
  1315                  ;	descriptor and later saved on the stack and loads them into
  1316                  ;	the registers, leaving the stack image untouched.  All variables
  1317                  ;	intrinsic to the process therefore always reside on the 
  1318                  ;	process-dependent stack
  1319                  
  1320   062C F3        	di			; this is a real critical region
  1321   062D E1        	pop	h
  1322   062E 224006    	shld	rtnadr
  1323   0631 E1        	pop	h
  1324   0632 D1        	pop	d
  1325   0633 C1        	pop	b
  1326   0634 F1        	pop	psw
  1327   0635 F5        	push	psw
  1328   0636 C5        	push	b
  1329   0637 D5        	push	d
  1330   0638 E5        	push	h
  1331   0639 E5        	push	h
  1332   063A 2A4006    	lhld	rtnadr
  1333   063D E3        	xthl
  1334   063E FB        	ei
  1335   063F C9        	ret
  1336                  
  1337   0640           rtnadr:	ds	2
  1338                  
  1339                  	if	WtchDg
  1340                  
  1341                  ;	WatchDog Timer Process
  1342                  ;	This process needs adjunct processes to handle the timeout flags
  1343                  ;	that it sets.  They might possibly abort the offending NtwrkIPx
  1344                  ;	process, recreate it, and allow it to re-initialize its queues
  1345                  
  1346                  WatchDog:
  1347                  	mvi	c,Delay
  1348                  	lxi	d,60		; delay for 1 second
  1349                  	call	bdos
  1350                  	lhld	WatchDogTime
CP/M RMAC ASSEM 1.1	#026	MASTER NETWORK I/F MODULE

  1351                  	inx	h
  1352                  	shld	WatchDogTime
  1353                  	lxi	h,WatchDogTable-5
  1354                  	mvi	c,NmbSlvs
  1355                  
  1356                  WatchDogLoop:
  1357                  	lxi	d,0005h
  1358                  	dad	d
  1359                  	mov	a,m
  1360                  	ora	a
  1361                  	jz	WatchDogDec
  1362                  	inx	h
  1363                  	ana	m
  1364                  	dcx	h
  1365                  	jnz	WatchDogDec	; waiting & timeout set
  1366                  	push	h		; save HL -> WDT.waiting
  1367                  	inx	h
  1368                  	inx	h
  1369                  	di
  1370                  	mov	e,m
  1371                  	inx	h
  1372                  	mov	d,m
  1373                  	ei
  1374                  	lhld	WatchDogTime
  1375                  	mov	a,l
  1376                  	sub	e
  1377                  	mov	l,a
  1378                  	mov	a,h
  1379                  	sbb	d
  1380                  	mov	h,a
  1381                  	mvi	a,10		; # seconds since started Charin
  1382                  	sub	l
  1383                  	mvi	a,0
  1384                  	sbb	h
  1385                  	pop	h
  1386                  	jnc	WatchDogDec
  1387                  	push	h
  1388                  	inx	h
  1389                  	mvi	m,0ffh		; WDT.timeout = 0ffh
  1390                  	inx	h
  1391                  	inx	h
  1392                  	inx	h
  1393                  	push	b
  1394                  	mov	e,m		; E = Flag #
  1395                  	mvi	c,Flagset
  1396                  	call	bdos
  1397                  	pop	b
  1398                  	pop	h
  1399                  
  1400                  WatchDogDec:
  1401                  	dcr	c
  1402                  	jnz	WatchDogLoop
  1403                  
  1404                  	jmp	WatchDog
CP/M RMAC ASSEM 1.1	#027	MASTER NETWORK I/F MODULE

  1405                  	endif
  1406                  
  1407                  
  1408                  ;	Setup code for Network Interface Procedures
  1409                  
  1410                  Setup:
  1411   0642 F5        	push	psw		;create stack image of all reentrant variables
  1412   0643 C5        	push	b
  1413   0644 D5        	push	d
  1414   0645 E5        	push	h
  1415   0646 CD3705    	call	nwinit
  1416                  
  1417                  	if	mutexin or mutexout
  1418                  	mvi	c,makeq		; make the mutual exclusion queue
  1419                  	lxi	d,QCBMXSXmitq
  1420                  	call	bdos
  1421                  
  1422                  	mvi	c,writeq	; leave a token in the queue
  1423                  	lxi	d,UQCBMXSXmitq
  1424                  	call	bdos
  1425                  	endif
  1426                  
  1427                  	if	WtchDg
  1428                  	lxi	d,WatchDogPD	;since this process is linked to all other
  1429                  				;NtwrkIPx processes, creating it creates all
  1430                  				;of the others
  1431                  	mvi	c,createp
  1432                  	call	bdos
  1433                  
  1434                  	else
  1435                  
  1436                  	if	NmbSlvs GE 2
  1437   0649 11AC01    	lxi	d,NtwrkIP1	;this will create all the other NtwrkIPx
  1438                  				;processes if there's no watchdog
  1439   064C 0E90      	mvi	c,createp
  1440   064E CD5004    	call	bdos
  1441                  	endif
  1442                  	endif
  1443                  
  1444   0651 0E8E      	mvi	c,dsptch	;give everything a chance to create its queues
  1445   0653 CD5004    	call	bdos
  1446                  
  1447   0656 0E9A      	mvi	c,sydatad
  1448   0658 CD5004    	call	bdos
  1449   065B 110900    	lxi	d,9
  1450   065E 19        	dad	d
  1451   065F 115603    	lxi	d,configtbl
  1452   0662 73        	mov	m,e
  1453   0663 23        	inx	h
  1454   0664 72        	mov	m,d		; sysdatpage(9&10) = co.configtbl
  1455                  				; filling in the config tbl address is the
  1456                  				; the server processes' cue to start
  1457                  
  1458                  	if	modem
CP/M RMAC ASSEM 1.1	#028	MASTER NETWORK I/F MODULE

  1459                  ; 	Initialize the modem
  1460                  
  1461                  	mvi	c,CR
  1462                  	mvi	b,slvmodem
  1463                  	call	Charout
  1464                  	mvi	c,'Z'
  1465                  	call	Charout
  1466                  	mvi	c,CR
  1467                  	call	Charout
  1468                  
  1469                  WtSpace:
  1470                  	call	Charin
  1471                  	jc	SetupDone
  1472                  	ani	07fh
  1473                  	cpi	' '
  1474                  	jnz	WtSpace
  1475                  	mvi	c,'A'
  1476                  	call	Charout
  1477                  
  1478                  SetupDone:
  1479                  	endif
  1480                  
  1481   0665 E1        	pop	h
  1482   0666 D1        	pop	d
  1483   0667 C1        	pop	b
  1484   0668 F1        	pop	psw
  1485                  
  1486                  ;  Network Interface Reentrant Procedure
  1487                  
  1488                  Init:
  1489   0669 F5        	push	psw	; A = network i/f console dev #
  1490   066A C5        	push	B	; BC= buffer address
  1491   066B D5        	push	D	; DE= UQCB ntwrk queue out
  1492   066C E5        	push	H	; HL= UQCB ntwrk queue in
  1493   066D 5E        	mov	e,m
  1494   066E 23        	inx	h
  1495   066F 56        	mov	d,m
  1496   0670 0E86      	mvi	c,makeq
  1497   0672 CD5004    	call	bdos	; make the ntwrk queue in
  1498   0675 CD2C06    	call	restore
  1499   0678 EB        	xchg
  1500   0679 5E        	mov	e,m
  1501   067A 23        	inx	h
  1502   067B 56        	mov	d,m
  1503   067C 0E86      	mvi	c,makeq
  1504   067E CD5004    	call	bdos	; make the ntwrk queue out
  1505                  
  1506                  Loop:
  1507   0681 CD2C06    	call	restore
  1508   0684 50        	mov	d,b
  1509   0685 59        	mov	e,c
  1510                  
  1511   0686 4F        	mov	c,a
  1512   0687 CDB405    	call	rcvmsg
CP/M RMAC ASSEM 1.1	#029	MASTER NETWORK I/F MODULE

  1513                  
  1514   068A CD2C06    	call	restore
  1515   068D EB        	xchg
  1516   068E 0E8B      	mvi	c,writeq
  1517   0690 CD5004    	call	bdos
  1518                  
  1519   0693 CD2C06    	call	restore
  1520   0696 0E89      	mvi	c,readq
  1521   0698 CD5004    	call	bdos
  1522                  
  1523   069B CD2C06    	call	restore
  1524   069E 50        	mov	d,b
  1525   069F 59        	mov	e,c
  1526                  
  1527   06A0 4F        	mov	c,a
  1528   06A1 CD6005    	call	sndmsg
  1529                  
  1530   06A4 C38106    	jmp	Loop
  1531                  
  1532   06A7           	end
CP/M RMAC ASSEM 1.1	#030	MASTER NETWORK I/F MODULE

ACK              0006   612# 1169  1244  1261  1297 
ACTIVE           0010   587#
ASCIIIN          0507   984   989#
BDOS             0450    44   622# 1120  1193  1239  1349  1396  1420  1424  1432 
                       1440  1445  1448  1497  1504  1517  1521 
BDOSADR          0000    51#   58#  623 
BINARYASCII      043E   541#  965   982 
BUFFERQ0         00A6    47    89   118   138#
BUFFERQ1         0250   179   208   228#
BUFFERQI0ADDR    0084   116   117#
BUFFERQI1ADDR    022E   206   207#
BUFFERQO0ADDR    00A4   134   135#
BUFFERQO1ADDR    024E   224   225#
CFGADR           055C  1095#
CHARIN           04BD   771   816#  867#  985  1166  1248  1263  1274  1292  1470 
CHARINRETURN     04BA   799   809#
CHARIOTBL        044C   578#  803 
CHAROUT          0468   631   635   643#  692#  967  1129  1247  1262  1289  1298 
                       1463  1465  1467  1476 
CHKSIN           0517   987  1002#
CONFIGTBL        0356   493# 1096  1451 
CONIN            0446   553#  806  1074 
CONSOLE2STATUS   002D   564#  574   743   744   749   928   932 
CONSOLE3STATUS   002F   563#  573   731   732   737   914   918 
CONSOLE4STATUS   002B   562#  572   719   720   725   900   904 
CR               000D   614# 1461  1466 
CREATEP          0090   601# 1431  1439 
DEBUG            0000    28#   37 
DELAY            008D   599# 1347 
DSPTCH           008E   600# 1444 
ENQ              0005   611# 1128  1226 
EOT              0004   610# 1151  1295 
ETX              0003   609# 1145  1277 
FALSE            0000    23#   24    28    29    31    33    34 
FLAGSET          0085   595# 1395 
GETACK           05A0  1130  1138  1153  1165#
GETACKTIMEOUT    05AB  1167  1172#
GETENQ           05BF  1205  1221# 1224  1227 
INIT             0669   190   281   366  1488#
INPUTLOOP1       04C9   838#  846 
LF               000A   613#
LOOP             0681  1506# 1530 
MAKEQ            0086   596# 1418  1496  1503 
MAXRETRIES       000A   555# 1105 
MODEM            0000    29# 1458 
MSGERR           0008   588#  781  1087 
MSGIN            051D  1010# 1017  1255  1273 
MSGOUT           0528  1020# 1133  1144 
MSGOUTLOOP       052D  1027# 1032 
MSGOVR           0002   590# 1087 
MUTEXIN          0000    33#  471  1188  1212  1229  1303  1417 
MUTEXOUT         0000    34#  471  1110  1157  1175  1188  1417 
NAK              0015   615# 1288 
NETIN            04F9   980# 1012  1256  1281 
CP/M RMAC ASSEM 1.1	#031	MASTER NETWORK I/F MODULE

NETOUT           04DE   961# 1030  1137  1150 
NETWORKSTATUS    0442   547#  780   782  1083 
NIBATOF          045F   628   632#
NIBIN            0486   770#  990   997 
NIBINRETURN      04A6   776   779   787#
NIBOUT           0454   626#  975   978 
NMBSLVS          0002    39#   56#  148   151   239   242   330   419   496   505 
                        512   518   524   529   534  1045  1049  1056  1060  1354 
                       1436 
NMSG             0001   502#
NTWRK            0004   589#
NTWRKIP0         0002    65#
NTWRKIP1         01AC   149#  420  1437 
NTWRKIS0         0036    41    69    93#
NTWRKIS1         01E0   159   183#
NTWRKRXRDY       0001   591#
NTWRKTXRDY       0080   586#
NWINIT           0537  1037# 1415 
NWSTAT           054F  1081#
OUTPUTLOOP       0474   666#  676 
POLL             0083   603#
PRECHAROUT       0465   637# 1025  1146  1152 
PRINTER2         0010   617#
PRINTER2STATUS   0029   565#  575   755   756   761   941   945 
QCBNTWRKQI0      0066   102#  115 
QCBNTWRKQI1      0210   192#  205 
QCBNTWRKQO0      0086   120#  133 
QCBNTWRKQO1      0230   210#  223 
RCVMSG           05B4  1198# 1512 
READQ            0089   597# 1118  1237  1520 
RECEIVE          05B5  1202# 1219 
RECEIVERETRY     05BA  1209#
RESTORE          062C  1311# 1498  1507  1514  1519  1523 
RETOUT           04DA   840   859#  898   912   926   939   951#
RTNADR           0640  1322  1332  1337#
SEND             0563  1107# 1184 
SENDACK          0619  1285  1291#
SENDNAK          0614  1260  1287#
SETUP            0642   100  1410#
SLAVE1           040A   508   525#
SLAVE1STK        0374   506#
SLAVESTKLEN      0096   503#  507   514   520 
SNDMSG           0560  1102# 1528 
SOH              0001   607# 1132  1251 
STATUSPORTS      0448   571#  658   828 
STX              0002   608# 1143  1266 
SYDATAD          009A   602# 1447 
TRUE             FFFF    24#   26 
UQCBNTWRKQI0     0080    45    87   114#
UQCBNTWRKQI1     022A   177   204#
UQCBNTWRKQO0     00A0    46    88   132#
UQCBNTWRKQO1     024A   178   222#
WRITEQ           008B   598# 1191  1422  1516 
WTCHDG           0000    31#  414  1339  1427 
CP/M RMAC ASSEM 1.1	#032	MASTER NETWORK I/F MODULE

XCHARIN          04A8   791# 1223 
Z80              FFFF    26#  567   642   815 


Appendix F
A CP/NET System for use with ULCnet

F.1 Overview of ULCnet

ULCnet® (Universal Low Cost Network) is a local area network system designed specifically for microcomputers in the CP/M and MP/M II operating system environments. ULCnet was introduced by Orange Compuco, Inc. in June 1982 as a low cost method of sharing resources and data among microcomputers of varying manufacture and architecture. ULCnet, in combination with CP/NET, creates a cost effective method for the development of shared data base applications among single user microcomputers. ULCnet architecture readily supports CP/NET implementation.

The ULCnet connector adaptor box can be connected to any computer that has a spare RS-232 port. ULCnet employs a multidrop topology with carrier sense, multiple-access design. Contention between network nodes is arbitrated using a full-duplex collision detection mechanism.

ULCnet is available to OEMs on a private label basis and through licensing. Keybrook Business Systems, Inc., Hayward, California, a licensee of ULCnet, produces the FileServer™ system. This system uses CP/NET to drive ULCnet. For more information on ULCnet, contact

Orange Compuco, Inc.
17801-G South East Main Street
Irvine, California 92714
(714) 957-8075

Orange Compuco distributes ULCnet connector adaptor hardware with a variety of release software, including the example programs in this appendix. In addition, Orange Compuco provides documentation detailing the installation and operation of ULCnet and logical structure of the data-link layer software. This documentation includes

F.2 Customizing a ULCnet SNIOS for the Requester

The CP/NET requester listing, SNIOS for ULCnet, that appears at the end of this section, is contained in a file called ULCNIOS.ASM on the CP/NET release disk and is designed to run ULCnet in a polled environment on a Xerox® 820 computer, now called the Xerox R820-IIS. The listing uses the ULCnet short format. This means that virtual circuit numbers must be agreed upon before the requester and the server can communicate. This version assumes that the server ID is always 0, and that up to four requesters, ID 1 through 4, are on the network. The virtual circuit number and the requester ID are always the same.

This SNIOS combines the two sections of the ULCnet protocol that are user configurable, the data-link interface (DLIF) and the network I/O drivers (NIOD). The DLIF acts as a transport layer between the NDOS and the data-link routines. The NIOD contains the physical device drivers use to communicate with the ULCnet network adaptor box. The bulk of the data-link protocol is contained in a module called PBMAIN.REL. This module is proprietary to Orange Compuco, and is therefore distributed only in REL file format by Orange Compuco.

When the NDOS instructs the SNIOS to send a message, the SNIOS first converts the CP/NET message format into ULCnet short format. The SNIOS then calls the TRANSMIT routine in PBMAIN to send the message, followed by the GETTCODE routine to discover the status of the message. If the send was successful, the SNIOS returns to the NDOS. If it was not successful, the SNIOS continues to try to send the message. No timeout is included in this routine to halt transmission.

To receive a message, the SNIOS calls RECEIVE, followed by GETRCODE to check the status of the message. If the status shows success, the message is converted from ULCnet format back into CP/NET format and returns to the NDOS. If the status shows an error, the SNIOS attempts to receive the message again.

To modify the SNIOS for a requester other than a Xerox 820, follow these steps:

  1. Decide whether to make the requester operate in a polled or interrupt-driven environment. If you want interrupts, set the INTERRUPTS assembly switch to TRUE, and link the module using IPBMAIN instead of PBMAIN.
  2. If your ULCnet connector adaptor has been modified for self clocked operation, set the assembly switch SLFCLKD to TRUE. Application notes detailing how to modify the connector adaptor for self-clocked operation are available from Orange Compuco.
  3. Determine your requester's transmission speed capabilities. Set the baud rate masks BAUDSL and BAUDSH to reflect these values. Enter values for the requester's baud rate generator into the table BAUDTBL.
  4. Modify the port numbers for the baud rate generator and the UART to reflect those used by your requester.
  5. Modify the NIOD to run on your requester. The NIOD is currently set up to drive a Z80 SIO chip. If your requester has an SIO, it needs little modification. The routine PGMUART, which sets up the network port for ULCnet operation, might have to be modified. In an interrupt driven system, interrupt vectors must be set up here.
  6. Assemble and link the SNIOS by performing

            A>RMAC ULCNIOS
            A>LINK SNIOS=ULCN1OS,PBMAIN[OS]
               

    If the requester is interrupt-driven, perform

            A>LINK SNIOS=ULCNIOS,IPBKAIN[OS]
               

    to link the module. The module is then ready for installation on the CP/NET requester system disk.

Listing F-1: Requester Network I/O System for ULCnet
CP/M RMAC ASSEM 1.1	#001	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

     1                  	title 'Requester Network I/O System for ULCnet'
     2                  	page	54
     3                  
     4                  ;***************************************************************************
     5                  ;***************************************************************************
     6                  ;**                                                                       **
     7                  ;**                        SNIOS FOR ULCNET                               **
     8                  ;**                                                                       **
     9                  ;***************************************************************************
    10                  ;***************************************************************************
    11                  
    12                  ;	Developed jointly by:
    13                  ;
    14                  ;		Digital Research, Inc.
    15                  ;		P.O. Box 579
    16                  ;		Pacific Grove, CA 93950
    17                  ;	and
    18                  ;		Keybrook Business Systems, Inc.
    19                  ;		2035 National Avenue
    20                  ;		Hayward, CA 94545
    21                  
    22                  ;	This SNIOS was written for a Xerox 820 attached to Orange 
    23                  ;	Compuco's ULCnet network adaptor.  This module transports
    24                  ;	messages between the NDOS and the low-level data-link software
    25                  ;	provided by Orange Compuco.  It also contains the physical drivers
    26                  ;	usually contained in the NIOD module.  This version is not 
    27                  ;	interrupt-driven and must be linked with PBMAIN.REL.
    28                  
    29                  
    30                  
    31   0000 =         false	equ	0
    32   FFFF =         true	equ	not false
    33                  
    34   0000 =         interrupts	equ	false		; false=polled, true=interrupt-driven
    35   FFFF =         netstats	equ	true		; switch to gather network statistics
    36   FFFF =         slfclkd		equ	true		; supports self-clocked operation
    37                  
    38                  ; Linkage information
    39                  
    40                  	public	setbaud,xmit,recv,initu	; NIOD routines called by IPBMAIN
    41                  	public	inituart,pgmuart
    42                  	public	chkstat,netidle,initrecv
    43                  	public	wait,restuart,csniod
    44                  	public	dsblxmit
    45                  	public	dllbau,netadr
    46                  
    47                  	if	interrupts
    48                  	public	enblrecv,dsblrecv
    49                  	endif
    50                  
    51                  	extrn	transmit,receive	; IPBMAIN routines and objects
    52                  	extrn	gettcode,getrcode
    53                  	extrn	csdll,dllon,regshrt
    54                  	extrn	terrcnt,parcntr,ovrcntr
CP/M RMAC ASSEM 1.1	#002	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

    55                  	extrn	frmcntr,inccntr
    56                  
    57                  	if	interrupts
    58                  	extrn	rtmochk			; IPBMAIN interrupt routines
    59                  	extrn	dlisr,reisr,niisr
    60                  	endif
    61                  
    62                  
    63                  ; Hardware definitions for the Z80-SIO channel A - For the Xerox 820.
    64                  
    65   0003 =         baudsl	equ	03h		; Usable baud rates: 9600, 19.2K asynch.,
    66   002A =         baudsh	equ	2ah		; 76.8K, 153.6K, 307.2K self-clocked
    67                  
    68                  				; baud rate capability mask
    69   2A03 =         bauds	equ	(baudsh*100h)+baudsl
    70                  
    71   0000 =         baudgen	equ	0		; External baud rate generator register
    72   0006 =         siocmd	equ	6		; Command/Mode register
    73   0006 =         siostat	equ	6		; Status register
    74   0004 =         sioxmit	equ	4		; Transmit register
    75   0004 =         siorecv	equ	4		; Receive register
    76                  
    77   0002 =         xrdybit	equ	2		; Transmit buffer empty status bit
    78   0004 =         xrdymsk	equ	4		; transmit buffer empty status mask
    79   0000 =         rrdybit	equ	0		; Receive buffer full status bit
    80   0001 =         rrdymsk	equ	1		; receive buffer full status mask
    81   0003 =         carbit	equ	3		; Net Idle detect bit position
    82   0008 =         carmsk	equ	8		; Net Idle detect mask
    83   0030 =         errst	equ	030h		; Error flag reset
    84   0070 =         errbits	equ	070h		; Error bit position mask
    85   0004 =         pbit	equ	4		; Parity error bit position
    86   0010 =         pmsk	equ	10h		; parity error mask
    87   0005 =         obit	equ	5		; Overrun error bit position
    88   0020 =         omsk	equ	20h		; overrun error mask
    89   0006 =         fbit	equ	6		; Framing error bit position
    90   0040 =         fmsk	equ	40h		; framing error mask
    91   0003 =         selfbit	equ	3		; Self clock bit position
    92   0008 =         selfmsk	equ	8		; slef clock bit mask
    93   00EA =         dtron	equ	0eah		; Turn on DTR
    94   006A =         dtroff	equ	06ah		; Turn off DTR
    95   00C1 =         enarcv	equ	0c1h		; Enable receive-clock
    96   00C0 =         disrcv	equ	0c0h		; Disable receive clock
    97   000F =         enaslf	equ	00fh		; Enable Self-clock mode
    98   004F =         disslf	equ	04fh		; Disable Self-clock mode 
    99                  
   100                  ; SIO Mode 2 interrupts vector table
   101                  
   102   FF08 =         siov4	equ	0ff08h		; SIO port A xmit buffer empty
   103   FF0A =         siov5	equ	0ff0ah		; SIO port A external status change
   104   FF0C =         siov6	equ	0ff0ch		; SIO port A receive
   105   FF0E =         siov7	equ	0ff0eh		; SIO port A special receive condition
   106                  
   107                  
   108                  ; Message Buffer Offsets
CP/M RMAC ASSEM 1.1	#003	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   109                  
   110   0000 =         fmt		equ	0		; format
   111   0001 =         did		equ	fmt+1		; destination ID
   112   0002 =         sid		equ	did+1		; source ID
   113   0003 =         fnc		equ	sid+1		; server function number
   114   0004 =         siz		equ	fnc+1		; size of message (normalized to 0)
   115   0005 =         msg		equ	siz+1		; message
   116   0106 =         buf$len		equ	msg+257		; length of total message buffer
   117                  
   118                  ; ULCnet Packet Offsets
   119                  
   120   0000 =         ulc$fmt		equ	0		; packet format
   121   0001 =         ulc$v$circ	equ	ulc$fmt+1	; virtual circuit number
   122   0002 =         ulc$len$lo	equ	ulc$v$circ+1	; low order of length
   123   0003 =         ulc$len$hi	equ	ulc$len$lo+1	; high order of length
   124   0004 =         ulc$fnc		equ	ulc$len$hi+1	; start of message: function code
   125   0005 =         ulc$msg		equ	ulc$fnc+1	; CP/NET message
   126                  
   127                  ; Network Status Byte Equates
   128                  
   129   0010 =         active		equ	0001$0000b	; slave logged in on network
   130   0002 =         rcverr		equ	0000$0010b	; error in received message
   131   0001 =         senderr		equ	0000$0001b	; unable to send message
   132                  
   133                  
   134                  
   135                  	CSEG
   136   0005 =         BDOS	equ	0005h
   137                  
   138                  NIOS:
   139                  	public	NIOS
   140                  
   141                  ; Jump vector for SNIOS entry points
   142                  
   143   0000 C3E100    	jmp	ntwrkinit	; network initialization
   144   0003 C3EE00    	jmp	ntwrksts	; network status
   145   0006 C3F600    	jmp	cnfgtbladr	; return config table addr
   146   0009 C30401    	jmp	sendmsg		; send message on network
   147   000C C32001    	jmp	receivemsg	; receive message from network
   148   000F C3FA00    	jmp	ntwrkerror	; network error
   149   0012 C30301    	jmp	ntwrkwboot	; network warm boot
   150                  
   151                  
   152   0001 =         rqstr$id	equ	1	; requester ID: must be between 1 and 4
   153   004B =         fmt$byte	equ	4bh	; format byte: short format with data-link
   154                  				; acknowledge, 153.6K baud self-clocked
   155                  
   156                  	DSEG
   157                  
   158                  ; Transport Layer Data
   159                  
   160                  network$error$msg:
   161                  
   162   0000 0D0A      	db	0dh,0ah
CP/M RMAC ASSEM 1.1	#004	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   163   0002 4E6574776F	db	'Network Error'
   164   000F 0D0A      	db	0dh,0ah
   165   0011 24        	db	'$'
   166                  
   167                  
   168                  ; Requester Configuration Table
   169                  
   170                  configtbl:
   171                  Network$status:
   172                  
   173   0012           	ds	1		; network status byte
   174   0013 01        	db	rqstr$id	; slave processor ID number
   175   0014           	ds	2		; A:  Disk device
   176   0016           	ds	2		; B:   "
   177   0018           	ds	2		; C:   "
   178   001A           	ds	2		; D:   "
   179   001C           	ds	2		; E:   "
   180   001E           	ds	2		; F:   "
   181   0020           	ds	2		; G:   "
   182   0022           	ds	2		; H:   "
   183   0024           	ds	2		; I:   "
   184   0026           	ds	2		; J:   "
   185   0028           	ds	2		; K:   "
   186   002A           	ds	2		; L:   "
   187   002C           	ds	2		; M:   "
   188   002E           	ds	2		; N:   "
   189   0030           	ds	2		; O:   "
   190   0032           	ds	2		; P:   "
   191   0034           	ds	2		; console device
   192   0036           	ds	2		; list device:
   193                  
   194                  ; List Buffer Data
   195                  
   196   0038           	ds	1		;	buffer index
   197                  
   198   0039 00        	db	0		;	FMT
   199   003A 00        	db	0		;	DID
   200   003B 01        	db	rqstr$id	;	SID 
   201   003C 05        	db	5		;	FNC
   202   003D           	ds	1		;	SIZ
   203   003E           	ds	1		;	MSG(0)  List number
   204   003F           	ds	128		;	MSG(1) ... MSG(128)
   205                  
   206                  
   207                  ; ULCnet Data Definitions
   208                  
   209   00BF           netadr:	ds	3		;ULCnet network address
   210   00C2           dllbau:	ds	2		;baud rate mask
   211                  
   212   0016 =         timeval	equ	22		; WAIT routine time constant
   213                  				; 12 for 2.5 megahertz Z80
   214                  				; 22 for 4.0 megahertz Z80 	
   215                  
   216   00C4 FF        curbaud db	0ffh		; Current baud rate
CP/M RMAC ASSEM 1.1	#005	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   217                  
   218                  				
   219                  				; table to convert baud number codes
   220                  				;   into a bit mask
   221                  
   222   00C5 0102040810btbl:	db	1,2,4,8,16,32,64,128
   223                  
   224                  
   225                  baudtbl:			; async baud rate table
   226                  
   227   00CD 0E        	db	0eh		; 9600 Baud
   228   00CE 0F        	db	0fh		; 19200
   229                  
   230                  scbaudt:			; self-clock baud rate table
   231                  
   232   00CF 00        	db	0		;  62500 Baud - Not implemented
   233   00D0 0D        	db	0dh		;  76800 Baud
   234   00D1 00        	db	0		; 125000 Baud - Not implemented
   235   00D2 0E        	db	0eh		; 153600 Baud
   236   00D3 00        	db	0		; 250000 Baud - Not implemented
   237   00D4 0F        	db	0fh		; 307200 Baud
   238                  
   239                  	if	interrupts
   240                  sioiblk	db	030h,14h,4fh,15h,06ah,13h,0c1h,11h,01h,10h,10h,30h
   241                  	else
   242   00D5 30144F156Asioiblk	db	030h,14h,4fh,15h,06ah,13h,0c1h,11h,00h,10h,10h,30h
   243                  	endif
   244                  
   245   000C =         sioilen	equ	$-sioiblk
   246                  
   247                  
   248                  	page
CP/M RMAC ASSEM 1.1	#006	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   249                  
   250                  ;	Network Initialization Routine
   251                  
   252                  ntwrkinit:
   253                  
   254   00E1 CD0000    	call	csdll			; cold start the data link
   255   00E4 CD0000    	call	dllon			; initialize the SIO drivers
   256   00E7 3E01      	mvi	a,rqstr$id		; register the id with the data link
   257   00E9 CD0000    	call	regshrt
   258   00EC AF        	xra	a			; return with no error
   259   00ED C9        	ret
   260                  
   261                  
   262                  ;	Return network status byte
   263                  
   264                  ntwrksts:
   265                  
   266   00EE 3A1200    	lda	network$status
   267   00F1 47        	mov	b,a
   268   00F2 E6FC      	ani	not (rcverr or senderr)
   269   00F4 78        	mov	a,b
   270   00F5 C9        	ret
   271                  
   272                  
   273                  ;	Return configuration table address
   274                  
   275                  cnfgtbladr:
   276                  
   277   00F6 211200    	lxi	h,configtbl
   278   00F9 C9        	ret
   279                  
   280                  ;	Network error routine
   281                  
   282                  
   283                  ntwrkerror:
   284                  
   285   00FA 0E09      	mvi	c,9
   286   00FC 110000    	lxi	d,network$error$msg
   287   00FF CD0500    	call	bdos
   288                  
   289   0102 C9        	ret
   290                  
   291                  ;	Network Warm Boot Routine
   292                  
   293                  ntwrkwboot:				; this entry is unused in this version
   294                  
   295   0103 C9        	ret
   296                  
   297                  
   298                  ;	Send a Message on the Network
   299                  ;	Input:  
   300                  ;		BC=pointer to message buffer
   301                  ;	Output:
   302                  ;		A = 0 if successful
CP/M RMAC ASSEM 1.1	#007	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   303                  ;		    1 if failure
   304                  
   305                  sendmsg:
   306                  
   307   0104 C5        	push	b
   308   0105 60        	mov	h,b
   309   0106 69        	mov	l,c
   310                  
   311   0107 364B      	mvi	m,fmt$byte		;set ulc$net format byte
   312                  
   313   0109 23        	inx	h			;reformat source to virtual circuit
   314   010A 23        	inx	h
   315   010B 56        	mov	d,m
   316   010C 2B        	dcx	h
   317   010D 72        	mov	m,d
   318                  
   319                  
   320   010E 23        	inx	h
   321   010F 23        	inx	h
   322   0110 46        	mov	b,m			;save function
   323                  
   324   0111 23        	inx	h
   325   0112 5E        	mov	e,m			;get size
   326   0113 70        	mov	m,b			;function=msg(0) in ULC format
   327                  
   328   0114 1600      	mvi	d,0
   329   0116 13        	inx	d
   330   0117 13        	inx	d			;normalize CP/NET to ULC sizes
   331                  
   332   0118 2B        	dcx	h
   333   0119 72        	mov	m,d
   334   011A 2B        	dcx	h
   335   011B 73        	mov	m,e
   336                  
   337   011C C1        	pop	b			;restore buffer pointer
   338                  
   339   011D C34A01    	jmp	dl$send			;blast away
   340                  
   341                  
   342                  ;	Receive a Message on the Network
   343                  ;
   344                  ;	This routine calls the data-link routine to receive the message,
   345                  ;	then converts it into ULCnet format.
   346                  ;
   347                  ;	Input:
   348                  ;		BC = pointer to buffer to receive the message
   349                  ;	Output:
   350                  ;		A  = 0 if successful
   351                  ;		     1 if failure
   352                  
   353                  receivemsg:
   354                  
   355   0120 C5        	push	b			;save buffer pointer
   356                  
CP/M RMAC ASSEM 1.1	#008	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   357   0121 CD3701    	call	dl$receive		;slurp the message
   358                  
   359   0124 E1        	pop	h
   360   0125 3601      	mvi	m,1			;FMT = 0 (requester to server)
   361                  
   362   0127 23        	inx	h			;DID already = virtual circuit #
   363                  
   364   0128 23        	inx	h			;get length
   365   0129 5E        	mov	e,m
   366   012A 23        	inx	h
   367   012B 56        	mov	d,m
   368                  
   369   012C 1B        	dcx	d
   370   012D 1B        	dcx	d			;normalize ULC to CP/NET format
   371                  
   372   012E 23        	inx	h
   373   012F 7E        	mov	a,m			;save FNC
   374                  
   375   0130 73        	mov	m,e			;format SIZ (<256)
   376                  
   377   0131 2B        	dcx	h
   378   0132 77        	mov	m,a			;format FNC
   379                  
   380   0133 2B        	dcx	h
   381   0134 AF        	xra	a			;set success
   382   0135 77        	mov	m,a			;assume server always 0
   383                  
   384   0136 C9        	ret				;CP/NET message formatted form ULCnet
   385                  
   386                  
   387                  
   388                  ; Data Link Interface Routines
   389                  
   390                  
   391                  ; DL$RECEIVE:  Network Receive Function.
   392                  ;	Input:
   393                  ;		BC = Buffer address
   394                  
   395                  
   396                  dl$receive:
   397                  
   398   0137 50        	mov	d,b		; Buffer address in DE for data link
   399   0138 59        	mov	e,c
   400                  
   401                  rretry:
   402                  
   403   0139 AF        	xra	a		; Packet mode
   404   013A 010101    	lxi	b,257		; Buffer size
   405   013D 210000    	lxi	h,0		; Infinite wait	
   406   0140 D5        	push	d		; Save buffer address for retry
   407                  
   408   0141 CD7801    	call	psrecv		; Initiate Receive and wait for completion
   409                  
   410   0144 D1        	pop	d		; Restore buffer address
CP/M RMAC ASSEM 1.1	#009	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   411   0145 B7        	ora	a
   412   0146 C8        	rz			; Return if no error
   413                  
   414   0147 C33901    	jmp	rretry		; Jump to try again if error 
   415                  
   416                  
   417                  ; DL$SEND: Network Transmit Function
   418                  ;	Input:
   419                  ;		BC = Buffer address
   420                  
   421                  dl$send:
   422                  
   423   014A 50        	mov	d,b		; Buffer address in DE for data link
   424   014B 59        	mov	e,c
   425                  
   426                  tretry:	
   427                  	
   428   014C AF        	xra	a		; Packet mode, wait for Net Idle
   429   014D D5        	push	d		; Save buffer address for retry
   430                  	
   431   014E CD5701    	call	psxmit		; Initiate Transmit, wait for completion
   432                  
   433   0151 D1        	pop	d		; Restore buffer address
   434   0152 B7        	ora	a
   435   0153 C8        	rz			; Return if no error
   436                  
   437   0154 C34C01    	jmp	tretry		; Jump to retry if error
   438                  
   439                  ; PSXMIT:  Transmit the packet pointed at by DE.  If carry flag is set
   440                  ; 	   then don't wait for the Net to become idle.
   441                  ;
   442                  ;	   Returns the completion code in A 
   443                  ;		0	- Transmission ok and Data Link Ack Received
   444                  ;			  (In the case of multicast, no Ack required)
   445                  ;		2	- Transmission OK but no Data Link Ack received.
   446                  ;
   447                  ;		4	- Other error.
   448                  
   449                  psxmit:
   450                  
   451   0157 CD0000    	call	transmit		; This will transmit, set return code
   452                  
   453                  twait:
   454                  
   455   015A CD0000    	call	gettcode		; A := GETTCODE - Xmit return code
   456   015D 5F        	mov	e,a
   457   015E 1600      	mvi	d,0
   458   0160 216901    	lxi	h,trtbl			; dispatch on the return code
   459   0163 19        	dad	d
   460   0164 5E        	mov	e,m
   461   0165 23        	inx	h
   462   0166 66        	mov	h,m
   463   0167 6B        	mov	l,e
   464   0168 E9        	pchl 
CP/M RMAC ASSEM 1.1	#010	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   465                  
   466                  trtbl:
   467                  
   468   0169 7701      	dw	psxret			; Good transmission
   469   016B 7701      	dw	psxret			; No Data Link Ack
   470   016D 7701      	dw	psxret			; Too many collisions
   471   016F 7701      	dw	psxret			; Transmitter is disabled
   472   0171 5A01      	dw	twait			; Transmitter is idle
   473   0173 5A01      	dw	twait			; Transmitter is in progress
   474   0175 5A01      	dw	twait			; Transmitter is waiting for ack
   475                  	
   476                  psxret:	
   477                  
   478   0177 C9        	ret
   479                  
   480                  ; PSRECV:  Receive a packet into buffer pointed at by DE.  Length of
   481                  ; 	   packet must be less than length of buffer in BC. HL is the receive
   482                  ; 	   timeout count. 
   483                  ;
   484                  ;	   Upon return clear the carry bit if a packet received and ACKed.
   485                  ; 	   Set the carry flag if any error occured.
   486                  
   487                  psrecv:
   488                  
   489   0178 CD0000    	call	receive			; Receive.  Return code will be set
   490                  	
   491                  rwait:
   492                  
   493   017B CD0000    	call	getrcode		; A := GETRCODE
   494                  
   495   017E 5F        	mov	e,a
   496   017F 1600      	mvi	d,0
   497   0181 218A01    	lxi	h,rrtbl			; dispatch on the return code
   498   0184 19        	dad	d
   499   0185 5E        	mov	e,m
   500   0186 23        	inx	h
   501   0187 66        	mov	h,m
   502   0188 6B        	mov	l,e
   503   0189 E9        	pchl
   504                  
   505                  rrtbl:
   506                  
   507   018A 9601      	dw	rgood			; Good receive
   508   018C 9801      	dw	rbad			; Bad receive
   509   018E 9801      	dw	rbad			; Disabled
   510                  
   511                  	if	not interrupts
   512   0190 9801      	dw	rbad			; Still idle after timeout
   513                  	else
   514                  	dw	ridle			; Idle
   515                  	endif
   516                  
   517   0192 7B01      	dw	rwait			; Inprogress
   518   0194 7B01      	dw	rwait			; In progress and for us.
CP/M RMAC ASSEM 1.1	#011	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   519                  	
   520                  	if	interrupts
   521                  ridle:
   522                  
   523                  	call	rtmochk			; Check for timeout
   524                  	jc	ridle1			; Jump if timeout
   525                  	call	wait1			; Wait 1 ms
   526                  	jmp	rwait			; Continue to wait if no timeout
   527                  
   528                  ridle1:	
   529                  
   530                  	call	dsblrecv		; Disable the receiver
   531                  	stc
   532                  	ret				; Return with error
   533                  	endif
   534                   	
   535                  rgood:
   536                  
   537   0196 A7        	ana	a
   538   0197 C9        	ret
   539                  
   540                  rbad:
   541                  
   542   0198 37        	stc				; Indicate error
   543   0199 C9        	ret	 
   544                  	page
CP/M RMAC ASSEM 1.1	#012	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   545                  
   546                  
   547                  ; NIOD routines
   548                  
   549                  
   550                  
   551                  ; SETBAUD:  Set the baud rate based on the baud rate code in A.  Do special 
   552                  ; 	    logic for self-clocked mode.
   553                  ;
   554                  ;	 	0 = 9600 baud
   555                  ;		1 = 19200 baud
   556                  ;		9 = 76800 baud self-clock
   557                  ;		11= 153600 baud self-clock
   558                  ;		13= 307200 baud self-clock
   559                  ; 
   560                  ; If this station cannot handle the requested baud rate, then set
   561                  ; the carry flag.
   562                  
   563                  setbaud:
   564                  
   565   019A E60F      	ani	0fh		; mask all but the baud bits
   566   019C 21C400    	lxi	h,curbaud	; are we at the current baud rate?
   567   019F BE        	cmp	m
   568   01A0 C8        	rz			; yes-->all done
   569                  
   570   01A1 47        	mov	b,a		; else-->get baud rate generator value
   571   01A2 E607      	ani	7
   572   01A4 5F        	mov	e,a
   573   01A5 1600      	mvi	d,0
   574                  
   575   01A7 21C500    	lxi	h,btbl		; point to vertical-to-horizontal decode
   576   01AA 19        	dad	d		;   table
   577                  
   578                  	if	slfclkd
   579   01AB 78        	mov	a,b
   580   01AC E608      	ani	selfmsk		; is this a self-clocked value?
   581   01AE C2D601    	jnz	selfclkd
   582                  	endif
   583                  
   584   01B1 3E03      	mvi	a,baudsl	; get legal baud rate mask
   585   01B3 A6        	ana	m
   586   01B4 37        	stc
   587   01B5 C8        	rz			; return with error if its an illegal rate
   588                  
   589                  	if	slfclkd
   590   01B6 3E05      	mvi	a,5		; else-->switch off possible self-clock mode
   591   01B8 D306      	out	siocmd
   592   01BA 3E6A      	mvi	a,dtroff	; disable DTR in SIO register 5
   593   01BC D306      	out	siocmd
   594                  
   595   01BE 3E04      	mvi	a,4		; disable sync mode in register 4
   596   01C0 D306      	out	siocmd
   597   01C2 3E4F      	mvi	a,disslf
   598   01C4 D306      	out	siocmd
CP/M RMAC ASSEM 1.1	#013	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   599                  	endif
   600                  
   601   01C6 21CD00    	lxi	h,baudtbl	; point to async baud rate table
   602                  
   603                  outbau:
   604                  
   605   01C9 19        	dad	d		; get async baud rate value
   606   01CA 7E        	mov	a,m
   607   01CB D300      	out	baudgen		; load it into the baud rate generator
   608                  				; NOTE: This is not a CTC
   609                  
   610   01CD 21C400    	lxi	h,curbaud
   611   01D0 70        	mov	m,b		; set current baud byte
   612                  
   613   01D1 CDA702    	call	wait		; allow the system to reach equilibrium
   614                  
   615   01D4 A7        	ana	a		; return success
   616   01D5 C9        	ret
   617                  
   618                  	if	slfclkd
   619                  ; Throw SIO into self-clocked mode
   620                  
   621                  selfclkd:
   622                  
   623   01D6 3E2A      	mvi	a,baudsh	; Is this a legal rate?
   624   01D8 A6        	ana	m
   625   01D9 37        	stc
   626   01DA C8        	rz			; return an error if not
   627                  
   628   01DB 3E04      	mvi	a,4		; enable sync mode in register 4
   629   01DD D306      	out	siocmd
   630   01DF 3E0F      	mvi	a,enaslf
   631   01E1 D306      	out	siocmd
   632                  
   633   01E3 3E05      	mvi	a,5		; enable DTR in register 5
   634   01E5 D306      	out	siocmd
   635   01E7 3EEA      	mvi	a,dtron
   636   01E9 D306      	out	siocmd
   637                  
   638   01EB 21CF00    	lxi	h,scbaudt	; point to baud rate table for self-clock mode
   639   01EE C3C901    	jmp	outbau		; program the baud rate generator
   640                  	endif
   641                  
   642                  
   643                  ; DSBLXMIT:  Disable the transmitter if in self clocked mode
   644                  
   645                  dsblxmit:
   646                  
   647                  	if	slfclkd
   648   01F1 3AC400    	lda	curbaud		; are we in self-clocked mode?
   649   01F4 E608      	ani	selfmsk
   650   01F6 C8        	rz			; no-->don't bother
   651                  
   652   01F7 3E05      	mvi	a,5		; disable SIO from transmitting by disabling 
CP/M RMAC ASSEM 1.1	#014	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   653   01F9 D306      	out	siocmd		; DTR in register 5
   654   01FB 3E6A      	mvi	a,dtroff
   655   01FD D306      	out	siocmd
   656                  
   657   01FF 3E05      	mvi	a,5		; Enable receive by re-enabling DTR
   658   0201 D306      	out	siocmd
   659   0203 3EEA      	mvi	a,dtron
   660   0205 D306      	out	siocmd
   661                  	endif
   662                  
   663   0207 C9        	ret
   664                  
   665                  
   666                  ; XMIT:  Transmit the byte in A on network A.
   667                  
   668                  
   669                  xmit:
   670                  
   671                  	if	not interrupts
   672   0208 F5        	push	psw
   673                  
   674                  xmit1:
   675                  
   676   0209 DB06      	in	siostat		; don't overrun the transmitter if we're
   677   020B E604      	ani	xrdymsk		;  interrupt-driven; wait for TxReady
   678   020D CA0902    	jz	xmit1
   679                  
   680   0210 F1        	pop	psw
   681                  	endif
   682                  
   683   0211 D304      	out	sioxmit		; blast that byte
   684   0213 C9        	ret
   685                  
   686                  
   687                  ; RECV:  Receive a byte from Network A. Set the carry flag if there was
   688                  ; 	 a receive error.
   689                  ;
   690                  ;	 For Z80-SIO receive errors are handled by the special receive
   691                  ; 	 condition interrupts.
   692                  
   693                  recv:
   694                  
   695                  	if	not interrupts
   696   0214 CD5D02    	call	netidle	
   697   0217 DA2702    	jc	rto		; set error condition if the net went idle
   698                  
   699   021A DB06      	in	siostat		; else-->wait until a character is in the
   700   021C E601      	ani	rrdymsk		;    buffer
   701   021E CA1402    	jz	recv
   702                  
   703   0221 CD2A02    	call	chkstat		; check for receive errors
   704                  
   705                  	else
   706                  	ana	a		; clear carry flag
CP/M RMAC ASSEM 1.1	#015	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   707                  	endif
   708                  
   709   0224 DB04      	in	siorecv		; input the character
   710   0226 C9        	ret
   711                  
   712                  rto:				; set an error
   713                  
   714   0227 AF        	xra	a
   715   0228 37        	stc
   716   0229 C9        	ret
   717                  	
   718                  
   719                  ; CHKSTAT:  Check error status bits of a receive error.  If not error then
   720                  ; 	    clear the carry flag and return.  Otherwise figure out which
   721                  ; 	    error occured and increment its counter and set the carry flag.
   722                  ; 	    Issue an error reset command to the UART.
   723                  
   724                  
   725                  chkstat:
   726                  
   727   022A 3E01      	mvi	a,1		; get error status from SIO read register 1
   728   022C D306      	out	siocmd
   729   022E DB06      	in	siostat
   730                  
   731   0230 E670      	ani	errbits
   732   0232 C8        	rz			; no error occurred-->all done
   733                  
   734                  	if	netstats	; gather statistics on the type of error
   735   0233 47        	mov	b,a
   736   0234 E610      	ani	pmsk
   737   0236 CA3F02    	jz	np		; not a parity error
   738                  
   739   0239 210000    	lxi	h,parcntr	; else-->
   740   023C CD0000    	call	inccntr		; increment parity error counter
   741                  
   742                  np:
   743                  
   744   023F 78        	mov	a,b
   745   0240 E605      	ani	obit
   746   0242 CA4B02    	jz	no		; not an overrun
   747                  
   748   0245 210000    	lxi	h,ovrcntr	; else-->
   749   0248 CD0000    	call	inccntr		; increment overrun counter
   750                  
   751                  no:
   752                  
   753   024B 78        	mov	a,b
   754   024C E606      	ani	fbit
   755   024E CA5702    	jz	nf		; not a framing error
   756                  
   757   0251 210000    	lxi	h,frmcntr	; else-->
   758   0254 CD0000    	call	inccntr		; increment framing error counter
   759                  
   760                  nf:
CP/M RMAC ASSEM 1.1	#016	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   761                  	endif
   762                  
   763   0257 3E30      	mvi	a,errst		; reset error condition
   764   0259 D306      	out	siocmd
   765   025B 37        	stc			; signal an error
   766   025C C9        	ret
   767                  		
   768                  
   769                  
   770                  ; NETIDLE:  See if network A is idle. If idle then set the carry flag.
   771                  
   772                  netidle:
   773                  
   774   025D 3E10      	mvi	a,10h		; reset interrupts
   775   025F D306      	out	siocmd
   776   0261 D306      	out	siocmd		; do it twice to reject glitches on DCD
   777                  
   778   0263 DB06      	in	siostat		; is there a data-carrier detect?
   779   0265 E608      	ani	carmsk
   780   0267 C8        	rz			; yes-->net is in use-->carry flag cleared
   781                  
   782   0268 AF        	xra	a
   783   0269 CD9A01    	call	setbaud		; net is idle-->reset to hailing rate (9600)
   784   026C 37        	stc			; set net idle to true
   785   026D C9        	ret
   786                  
   787                  
   788                  	if	interrupts
   789                  
   790                  ; ENBLRECV:  Enable the channel A receiver interrupts.
   791                  
   792                  enblrecv:
   793                  
   794                  	mvi	a,1		; enable interrupts on all characters
   795                  	out	siocmd
   796                  	mvi	a,011h		; NOTE: This mask would have to be 015h on
   797                  	out	siocmd		;  channel B
   798                  	ret
   799                  
   800                  ; DSBLRECV:  Disable the channel A receiver interrupts.
   801                  
   802                  dsblrecv:
   803                  
   804                  	mvi	a,1		; Disable interrupts on received characters
   805                  	out	siocmd		;   (Keep status interrupts enabled)
   806                  	out	siocmd		; NOTE:  Channel B mask is 05h
   807                  	ret
   808                  
   809                  	endif
   810                  
   811                  
   812                  ; PGMUART:  Program the Network UART channel
   813                  
   814                  pgmuart:
CP/M RMAC ASSEM 1.1	#017	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   815                  
   816                  	if	interrupts
   817                  				; The 820 already has the SIO vector address
   818                  				; programmed from channel B.  Other 
   819                  				; implementations will have to provide linkage
   820                  				; to the vector area in the main XIOS, and 
   821                  				; load the vector offset into SIO write 
   822                  				; register 2
   823                  
   824                  	lxi	h,niisr		; load status interrupt service routine vector
   825                  	shld	siov5
   826                  	lxi	h,dlisr		; load transmit ISR vector
   827                  	shld	siov6
   828                  	lxi	h,reisr		; load receiv ISR vector
   829                  	shld	siov7
   830                  	endif
   831                  
   832   026E 21D500    	lxi	h,sioiblk	; point to SIO initialization block
   833   0271 060C      	mvi	b,sioilen	; length of block
   834   0273 F3        	di
   835                  
   836                  pgm1:
   837                  
   838   0274 7E        	mov	a,m		; output the block to the SIO
   839   0275 D306      	out	siocmd
   840   0277 23        	inx	h
   841   0278 05        	dcr	b
   842   0279 C27402    	jnz	pgm1
   843                  
   844   027C FB        	ei
   845   027D AF        	xra	a		; set up hailing baud rate = 9600
   846   027E CD9A01    	call	setbaud
   847   0281 C9        	ret
   848                  
   849                  
   850                  ; INITUART:  Initialize the uart for network A by issuing a reset command
   851                  ; 	     and clearing out the receive buffer.
   852                  
   853                  inituart:
   854                  
   855   0282 3E03      	mvi	a,3		; disable the receiver through register 3
   856   0284 D306      	out	siocmd
   857   0286 3EC0      	mvi	a,disrcv
   858   0288 D306      	out	siocmd
   859                  
   860   028A DB06      	in	siostat		; is there a garbage byte?
   861   028C E601      	ani	rrdymsk
   862   028E CA9602    	jz	initu		; no-->continue initialization
   863                  
   864   0291 DB04      	in	siorecv		; else-->eat the character
   865   0293 C38202    	jmp	inituart	; try again
   866                  
   867                  initu:
   868                  
CP/M RMAC ASSEM 1.1	#018	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   869   0296 3E30      	mvi	a,errst		; reset error conditions
   870   0298 D306      	out	siocmd
   871                  
   872   029A 3E03      	mvi	a,3		; re-enable the receiver
   873   029C D306      	out	siocmd
   874   029E 3EC1      	mvi	a,enarcv
   875   02A0 D306      	out	siocmd
   876                  
   877   02A2 C9        	ret
   878                  
   879                  ; INITRECV:  Initialize a receive operation
   880                  
   881                  initrecv:
   882                  
   883   02A3 CD8202    	call	inituart
   884                  
   885                  	if	interrupts
   886                  	call	enblrecv	; enable receiver interrupts
   887                  	endif
   888                  
   889   02A6 C9        	ret
   890                  
   891                  
   892                  ; WAIT - Wait 100 micro seconds
   893                  
   894                  wait:
   895                  
   896   02A7 3E16      	mvi	a,timeval
   897                  
   898                  w:
   899                  
   900   02A9 3D        	dcr	a		; 04
   901   02AA A7        	ana	a		; 04
   902   02AB C2A902    	jnz	w		; 12
   903                  				; ---
   904   02AE C9        	ret			; 30 T-States total
   905                  
   906                  
   907                  ; RESTUART:  Reinitialize the UART to the way it was in the
   908                  ;	     original BIOS after completing the network operations
   909                  
   910                  
   911                  restuart:
   912   02AF C9        	ret			; UART not used except by network
   913                  
   914                  
   915                  ; CSNIOD:  Do any cold start initialization which is necessary.
   916                  ;	   Must at least return the value of BAUDS
   917                  ; 	   If the network uses the printer port then set theh carry flag
   918                  ;	   otherwise clear it.
   919                  
   920                  csniod: 
   921                  	
   922   02B0 01032A    	lxi	b,bauds		; return the legal baud rates
CP/M RMAC ASSEM 1.1	#019	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

   923   02B3 B7        	ora	a		; not using a printer port
   924   02B4 C9        	ret
   925                  
   926                  
   927   02B5           	end
CP/M RMAC ASSEM 1.1	#020	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

ACTIVE           0010   129#
BAUDGEN          0000    71#  607 
BAUDS            2A03    69#  922 
BAUDSH           002A    66#   69   623 
BAUDSL           0003    65#   69   584 
BAUDTBL          00CD   225#  601 
BDOS             0005   136#  287 
BTBL             00C5   222#  575 
BUFLEN           0106   116#
CARBIT           0003    81#
CARMSK           0008    82#  779 
CHKSTAT          022A    42   703   725#
CNFGTBLADR       00F6   145   275#
CONFIGTBL        0012   170#  277 
CSDLL            0000    53   254 
CSNIOD           02B0    43   920#
CURBAUD          00C4   216#  566   610   648 
DID              0001   111#  112 
DISRCV           00C0    96#  857 
DISSLF           004F    98#  597 
DLLBAU           00C2    45   210#
DLLON            0000    53   255 
DLRECEIVE        0137   357   396#
DLSEND           014A   339   421#
DSBLXMIT         01F1    44   645#
DTROFF           006A    94#  592   654 
DTRON            00EA    93#  635   659 
ENARCV           00C1    95#  874 
ENASLF           000F    97#  630 
ERRBITS          0070    84#  731 
ERRST            0030    83#  763   869 
FALSE            0000    31#   32    34 
FBIT             0006    89#  754 
FMSK             0040    90#
FMT              0000   110#  111 
FMTBYTE          004B   153#  311 
FNC              0003   113#  114 
FRMCNTR          0000    55   757 
GETRCODE         0000    52   493 
GETTCODE         0000    52   455 
INCCNTR          0000    55   740   749   758 
INITRECV         02A3    42   881#
INITU            0296    40   862   867#
INITUART         0282    41   853#  865   883 
INTERRUPTS       0000    34#   47    57   239   511   520   671   695   788   816 
                        885 
MSG              0005   115#  116 
NETADR           00BF    45   209#
NETIDLE          025D    42   696   772#
NETSTATS         FFFF    35#  734 
NETWORKERRORMSG  0000   160#  286 
NETWORKSTATUS    0012   171#  266 
NF               0257   755   760#
NIOS             0000   138#  139 
CP/M RMAC ASSEM 1.1	#021	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

NO               024B   746   751#
NP               023F   737   742#
NTWRKERROR       00FA   148   283#
NTWRKINIT        00E1   143   252#
NTWRKSTS         00EE   144   264#
NTWRKWBOOT       0103   149   293#
OBIT             0005    87#  745 
OMSK             0020    88#
OUTBAU           01C9   603#  639 
OVRCNTR          0000    54   748 
PARCNTR          0000    54   739 
PBIT             0004    85#
PGM1             0274   836#  842 
PGMUART          026E    41   814#
PMSK             0010    86#  736 
PSRECV           0178   408   487#
PSXMIT           0157   431   449#
PSXRET           0177   468   469   470   471   476#
RBAD             0198   508   509   512   540#
RCVERR           0002   130#  268 
RECEIVE          0000    51   489 
RECEIVEMSG       0120   147   353#
RECV             0214    40   693#  701 
REGSHRT          0000    53   257 
RESTUART         02AF    43   911#
RGOOD            0196   507   535#
RQSTRID          0001   152#  174   200   256 
RRDYBIT          0000    79#
RRDYMSK          0001    80#  700   861 
RRETRY           0139   401#  414 
RRTBL            018A   497   505#
RTO              0227   697   712#
RWAIT            017B   491#  517   518   526 
SCBAUDT          00CF   230#  638 
SELFBIT          0003    91#
SELFCLKD         01D6   581   621#
SELFMSK          0008    92#  580   649 
SENDERR          0001   131#  268 
SENDMSG          0104   146   305#
SETBAUD          019A    40   563#  783   846 
SID              0002   112#  113 
SIOCMD           0006    72#  591   593   596   598   629   631   634   636   653 
                        655   658   660   728   764   775   776   795   797   805 
                        806   839   856   858   870   873   875 
SIOIBLK          00D5   240#  242#  245   832 
SIOILEN          000C   245#  833 
SIORECV          0004    75#  709   864 
SIOSTAT          0006    73#  676   699   729   778   860 
SIOV4            FF08   102#
SIOV5            FF0A   103#  825 
SIOV6            FF0C   104#  827 
SIOV7            FF0E   105#  829 
SIOXMIT          0004    74#  683 
SIZ              0004   114#  115 
CP/M RMAC ASSEM 1.1	#022	REQUESTER NETWORK I/O SYSTEM FOR ULCNET

SLFCLKD          FFFF    36#  578   589   618   647 
TERRCNT          0000    54 
TIMEVAL          0016   212#  896 
TRANSMIT         0000    51   451 
TRETRY           014C   426#  437 
TRTBL            0169   458   466#
TRUE             FFFF    32#   35    36 
TWAIT            015A   453#  472   473   474 
ULCFMT           0000   120#  121 
ULCFNC           0004   124#  125 
ULCLENHI         0003   123#  124 
ULCLENLO         0002   122#  123 
ULCMSG           0005   125#
ULCVCIRC         0001   121#  122 
W                02A9   898#  902 
WAIT             02A7    43   613   894#
XMIT             0208    40   669#
XMIT1            0209   674#  678 
XRDYBIT          0002    77#
XRDYMSK          0004    78#  677 

F.3 Creating the ULCnet Server

The server communications software is contained in the modules XIOSNET.ASM and ULCIF.ASM. XIOSNET.ASM contains modifications to MP/M II' s XIOS. ULCIF.ASM is the equivalent of the NETWRKIF transport processes.

ULCIF.ASM uses only two processes, one for input and one for output. To use ULCIF.ASM with the module SERVER.RSP, you must patch SERVER.RSP to write all message responses to a single output queue named NtwrkQO0. This patch is detailed in CP/NET V1.2 Application Note #2 dated 11-11-82.

The communications interface is interrupt driven, servicing each character as it is received by the network port. ULCIF.ASM requests the network resource through a set of dummy console I/O calls to the XIOS. A call to CONST initializes the network. Calls to CONIN and CONOUT receive and send messages on the network. The communications interface checks network status through a set of poll calls.

The ULCIF input transport process is dispatched at MP/M II cold start. This process makes all necessary queues, creates the ULCIF output process, initializes the network, and writes the configuration table address into the system data page. ULCIF then goes into a loop where it perpetually performs the following actions:

  1. Allocates a buffer for an incoming message. If no buffer is available, ULCIF repeats the allocation process until a buffer becomes available.
  2. Receives a message by placing the dummy console number in register D, a pointer to the message buffer just allocated in register pair BC, and calling CONIN in the XIOS.
  3. Converts the ULCnet format message into CP/NET format. To do this, ULCnet assumes that the virtual circuit number and the requester source ID are identical.
  4. Matches the requester ID with a requester control block. If no server is allocated to this requester and the message is a login, ULCIF allocates a server if one is available. Otherwise, ULCIF writes an extended error message to the output queue, NtwrkQO0.
  5. Using the requester control block, ULCIF writes the address of the message buffer to the appropriate input queue, NtwrkQI.
  6. Repeats.

The output process performs the following actions:

  1. Reads the output queue, NtwrkQI0.
  2. If the message is a LOGOFF function, frees the appropriate requester control block entry.
  3. Converts the message response from CP/NET format into ULCnet format. To do this, ULCnet uses the requester destination ID as the virtual circuit number.
  4. Places the dummy console number into register D, the message buffer address into register pair BC, and calls CONOUT in the XIOS.
  5. Repeats.

The ULCnet modules DLIF and NIOD are contained in the module XIOSNET.ASM. This module must be incorporated into the server's XIOS. XIOSNET.ASM handles four XIOS jump vector entries, CONST, CONIN, CONOUT, and POLLDEVICE. The jump vector in the XIOS must be modified to point to these routines. XIOSNET contains a linkage to the real XIOS routines for these functions, in this case renamed NCONST, NCONIN, NCONOUT, and POLDEV. The XIOS's interrupt vector might also have to be modified to support the SIO interrupt service routines in IPBMAIN.

When the console I/O routines are entered, they immediately check to see if the dummy console number has been supplied.

Note: you must define a console number that does not conflict with real consoles. Make the dummy console number at least larger than the number of requesters to be supported, since each server process pretends to attach to a unique console ID. If a dummy console number has not been supplied, these routines jump into the real console routines. If the dummy number has been supplied, the routines take the following steps.

CONST:
  1. performs network initialization.
  2. registers the expected Requester ID's as virtual circuit numbers by repeatedly calling REGSHRT.
  3. returns to the ULCIF. This routine is called only once.
CONIN:
  1. Calls RECEIVE, using the buffer pointer passed from ULCIF
  2. Executes the MP/M II poll function, specifying a poll device routine that repeatedly performs the GETRCODE function until its status shows that a message has been received properly.
  3. Returns to the ULCIF.
CONOUT:
  1. Calls TRANSMIT, using the buffer pointer passed from ULCIF.
  2. Executes the poll function, specifying a poll device routine that repeatedly performs the GETTCODE function until the message has been sent and received by the destination without error.
  3. Returns to the ULCIF.

The POLLDEVICE routine behaves almost like the console I/O routines. POLLDEVICE checks for specific poll device numbers to perform network status functions. If these numbers are not detected, control passes to the real POLDEV routine. If network status functions are detected, POLLDEVICE performs the appropriate status check. If the check is successful, a hexadecimal 0FF is returned in register A. If not successful, a 0 is returned.

The MP/M II dispatcher calls POLLDEVICE when it is entered. If the status returned is 0, MP/M II maintains the poll device number on a list and continues to call POLLDEVICE every time it is entered. When the returned status is FF, the dispatcher removes the device number from its list and returns control to the code that originally performed the poll function call, in this case either CONIN or CONOUT. In this manner, the communications interface operates completely transparently, requiring very little CPU resource.

The XIOSNET is designed to be interrupt driven. The IPBMAIN.REL module performs the actual data-link. This module is identical to the IPBMAIN.REL used in the SNIOS. An interrupt-driven protocol is strongly recommended. If you use the polled version, PBMAIN, calls to TRANSMIT and RECEIVE do not return until the requested operation has been performed. This means communications software uses up enormous amounts of CPU time, suspending only when a clock tick interrupts them and forces the dispatcher to be entered. This results in poor server performance.

The interrupt-driven IPBMAIN module sets up the requested operation only when TRANSMIT and RECEIVE are called. The actual protocol is driven by the arrival or departure of each character of the message. This interrupt-driven protocol consumes considerably less CPU time.

To modify the modules ULCIF and XIOSNET for your own server:

  1. Patch the module SERVER.RSP to write all of its outputs to a single queue, as described in an application note.
  2. Only three parameters must be modified in the ULCIF if four or fewer requesters are to be supported.

    Set NMB$RQSTRS to the number of requesters supported.

    Set NMB$BUFS to the number of requesters, plus one. This extra buffer permits the transmission of LOGIN error messages to the output process, even when all SERVER processes are busy. Having fewer buffers limits the burden on the server at any one time.

    Set CONSOLE$NUM to the dummy console number. The sample listing uses the arbitrarily large number hex 20. This number should be sufficient.

  3. If more than four requesters are supported, you must provide extra QCBs, requester control blocks, stack space, and Process Descriptor areas.
  4. Modify the XIOS jump vector to jump into the XIOSNET routines CONST, CONIN, CONOUT, and POLLDEVICE. You might have to make additional PUBLIC and EXTRN declarations.
  5. Include linkage access to the XIOS interrupt vector. If the XIOS has no interrupt vector, create one.
  6. Make sure the false console number specified by the ULCIF module agrees with the one used by XIOSNET.
  7. Make sure the device numbers CONIN and CONOUT use in their poll calls do not conflict with other device numbers used by the XIOS.
  8. Customize the NIOD section of XIOSNET the same way you customized this section in ULCNIOS.ASM.
  9. Create a resident or banked XIOS by linking the regular XIOS module with the network interface:

            A>LINK RESXI0S=<regu1ar XIOS modu1es>,XI0SNET,IPBMAIN[0S]
               

    If you are creating a banked system, all of XIOSNET must reside in common memory.

  10. Build the ULCIF.RSP module:

            A>RMAC ULCIF
            A>LINK ULCIF[OR]
               
  11. Perform a GENSYS, using the new RESXIOS.SPR, or perform a BNKXIOS.SPR for a banked system. Include the patched SERVER.RSP and ULCIF.RSP modules.

You must have access to the XIOS source modules to implement a ULCnet server in the manner described here. There are two reasons for this:

Both of these problems can be circumvented, but not without difficulty. If the code for XIOSNET is placed in ULCIF, the input process must initialize the interrupt vectors by performing the instruction:

        LD A,I

But to do this, the input process must know where there is empty space in the interrupt page.

Worse is the prospect of not being able to poll for network completion. Instead, the ULCIF might have to drastically reduce its own process priority, then busy wait, making repeated calls to GETTCODE and GETRCODE until the data-link completes. Alternatively, the server can use the polled version of the data-link, PBMAIN.REL. The problems associated with this version have already been described. Placing XIOSNET in the XIOS greatly improves performance.

Listing F-2: NETWRKIF for Systems Running ULCnet
CP/M RMAC ASSEM 1.1	#001	NETWRKIF FOR SYSTEMS RUNNING ULCNET

     1                  	title	'NETWRKIF for Systems Running ULCnet'
     2                  	page	54
     3                  
     4                  ;***************************************************************************
     5                  ;***************************************************************************
     6                  ;**									  **
     7                  ;**	S e r v e r   N e t w o r k   I n t e r f a c e   M o d u l e	  **
     8                  ;**									  **
     9                  ;***************************************************************************
    10                  ;***************************************************************************
    11                  
    12                  
    13                  ;***************************************************************************
    14                  ;***************************************************************************
    15                  ;**									  **
    16                  ;**	This module performs communication operations on a server	  **
    17                  ;**	equipped with Orange Compuco's ULCnet network adaptor.  	  **
    18                  ;**	The actual communications protocol is proprietary to Orange 	  **
    19                  ;**	Compuco.  It is included on the CP/NET release disk in REL	  **
    20                  ;**	file format on a module called PBMAIN.REL.  PBMAIN and a data-	  **
    21                  ;**	link interface module, DLIF, must be linked into the XIOS	  **
    22                  ;**	as console I/O routines.  A sample DLIF is included with this	  **
    23                  ;**	module.								  **
    24                  ;**									  **
    25                  ;**	This module performs the high-level transport and network 	  **
    26                  ;**	processing, then calls the DLIF via a direct XIOS console I/O	  **
    27                  ;**	function for data-link.  The following features are supported:	  **
    28                  ;**									  **
    29                  ;**		o  Queue Minimization using only 2 interface processes	  **
    30                  ;**		o  Dynamic LOGIN/LOGOFF support				  **
    31                  ;**									  **
    32                  ;**	Very little of this routine needs to be modified to run an a	  **
    33                  ;**	particular computer system.  The DLIF must be modified to 	  **
    34                  ;**	support the system's particular RS-232 hardware, and the XIOS	  **
    35                  ;**	must be modified to support interrupt-driven operation, if so	  **
    36                  ;**	desired, and also support the pseudo-console drivers of the	  **
    37                  ;**	DLIF.								  **
    38                  ;**									  **
    39                  ;***************************************************************************
    40                  ;***************************************************************************
    41                  
    42                  ;	This software was developed jointly by
    43                  ;
    44                  ;		Digital Research, Inc.
    45                  ;		P.O. Box 579
    46                  ;		Pacific Grove, CA 93950
    47                  ;	and
    48                  ;		Keybrook Business Systems, Inc.
    49                  ;		2035 National Avenue
    50                  ;		Hayward, CA 94545
    51                  
    52                  
    53                  bdosadr:
    54   0000 0000      	dw	$-$		; RSP XDOS entry point
CP/M RMAC ASSEM 1.1	#002	NETWRKIF FOR SYSTEMS RUNNING ULCNET

    55                  
    56                  ; User-Configurable Parameters (These should be the only changes needed)
    57                  
    58   0002 =         nmb$rqstrs	equ	2	; Number of requesters supported at one time
    59   0003 =         nmb$bufs	equ	3	; Number of message buffers
    60   0020 =         console$num	equ	20h	; Pseudo-console number
    61   004B =         fmt$byte	equ	4bh	; Format byte: short format with acknowledge,
    62                  				;   153.6K baud self-clocked
    63                  
    64                  ; Message Buffer Offsets
    65                  
    66   0000 =         fmt		equ	0		; format
    67   0001 =         did		equ	fmt+1		; destination ID
    68   0002 =         sid		equ	did+1		; source ID
    69   0003 =         fnc		equ	sid+1		; server function number
    70   0004 =         siz		equ	fnc+1		; size of message (normalized to 0)
    71   0005 =         msg		equ	siz+1		; message
    72   0106 =         buf$len		equ	msg+257		; length of total message buffer
    73                  
    74                  ; ULCnet Packet Offsets
    75                  
    76   0000 =         ulc$fmt		equ	0		; packet format
    77   0001 =         ulc$v$circ	equ	ulc$fmt+1	; virtual circuit number
    78   0002 =         ulc$len$lo	equ	ulc$v$circ+1	; low order of length
    79   0003 =         ulc$len$hi	equ	ulc$len$lo+1	; high order of length
    80   0004 =         ulc$fnc		equ	ulc$len$hi+1	; start of message: function code
    81   0005 =         ulc$msg		equ	ulc$fnc+1	; CP/NET message
    82                  
    83                  ; Requester Control Block Offsets
    84                  
    85   0000 =         rqstr$id	equ	0		; requester ID for this server
    86   0001 =         uqcb		equ	rqstr$id+1	; uqcb to queue to this server
    87   0005 =         buf$ptr		equ	uqcb+4		; queue message <--> msg buffer ptr
    88   0007 =         rcb$len		equ	buf$ptr+2	; length of requester control block
    89                  
    90                  
    91                  ; NETWRKIF Process Descriptors and Stack Space
    92                  
    93                  networkin:			; Receiver Process
    94                  
    95   0002 0000      	dw	0		; link
    96   0004 00        	db	0		; status
    97   0005 42        	db	66		; priority
    98   0006 6400      	dw	netstkin+46	; stack pointer
    99   0008 4E45545752	db	'NETWRKIN'	; name
   100   0010 00        	db	0		; console
   101   0011 FF        	db	0ffh		; memseg
   102   0012           	ds	2		; b
   103   0014           	ds	2		; thread
   104   0016           	ds	2		; buff
   105   0018           	ds	1		; user code & disk slct
   106   0019           	ds	2		; dcnt
   107   001B           	ds	1		; searchl
   108   001C           	ds	2		; searcha
CP/M RMAC ASSEM 1.1	#003	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   109   001E           	ds	2		; active drives
   110   0020 0000      	dw	0		; HL'
   111   0022 0000      	dw	0		; DE'
   112   0024 0000      	dw	0		; BC'
   113   0026 0000      	dw	0		; AF'
   114   0028 0000      	dw	0		; IY
   115   002A 0000      	dw	0		; IX
   116   002C 0000      	dw	0		; HL
   117   002E 0000      	dw	0		; DE
   118   0030 0000      	dw	0		; BC
   119   0032 0000      	dw	0		; AF, A = ntwkif console dev #
   120   0034           	ds	2		; scratch
   121                  
   122                  netstkin:
   123   0036 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   124   003E C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   125   0046 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   126   004E C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   127   0056 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   128   005E C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h
   129   0064 B405      	dw	setup
   130                  
   131                  networkout:			; Transmitter Process
   132                  
   133   0066 0000      	dw	0		; link
   134   0068 00        	db	0		; status
   135   0069 42        	db	66		; priority
   136   006A C800      	dw	netstkou+46	; stack pointer
   137   006C 4E45545752	db	'NETWRKOU'	; name
   138   0074 00        	db	0		; console
   139   0075 FF        	db	0ffh		; memseg
   140   0076           	ds	2		; b
   141   0078           	ds	2		; thread
   142   007A           	ds	2		; buff
   143   007C           	ds	1		; user code & disk slct
   144   007D           	ds	2		; dcnt
   145   007F           	ds	1		; searchl
   146   0080           	ds	2		; searcha
   147   0082           	ds	2		; active drives
   148   0084 0000      	dw	0		; HL'
   149   0086 0000      	dw	0		; DE'
   150   0088 0000      	dw	0		; BC'
   151   008A 0000      	dw	0		; AF'
   152   008C 0000      	dw	0		; IY
   153   008E 0000      	dw	0		; IX
   154   0090 0000      	dw	0		; HL
   155   0092 0000      	dw	0		; DE
   156   0094 0000      	dw	0		; BC
   157   0096 0000      	dw	0		; AF, A = ntwkif console dev #
   158   0098           	ds	2		; scratch
   159                  
   160                  netstkou:
   161   009A C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   162   00A2 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
CP/M RMAC ASSEM 1.1	#004	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   163   00AA C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   164   00B2 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   165   00BA C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h,0c7c7h
   166   00C2 C7C7C7C7C7	dw	0c7c7h,0c7c7h,0c7c7h
   167   00C8 8606      	dw	output
   168                  
   169                  
   170                  ; Input queue control blocks
   171                  
   172                  qcb$in$0:
   173   00CA           	ds	2		; link
   174   00CC 4E7477726B	db	'NtwrkQI0'	; name
   175   00D4 0200      	dw	2		; msglen
   176   00D6 0100      	dw	1		; nmbmsgs
   177   00D8           	ds	2		; dqph
   178   00DA           	ds	2		; nqph
   179   00DC           	ds	2		; msgin
   180   00DE           	ds	2		; msgout
   181   00E0           	ds	2		; msgcnt
   182   00E2           	ds	2		; buffer
   183                  
   184                  	if	nmb$rqstrs ge 2
   185                  qcb$in$1:
   186   00E4           	ds	2		; link
   187   00E6 4E7477726B	db	'NtwrkQI1'	; name
   188   00EE 0200      	dw	2		; msglen
   189   00F0 0100      	dw	1		; nmbmsgs
   190   00F2           	ds	2		; dqph
   191   00F4           	ds	2		; nqph
   192   00F6           	ds	2		; msgin
   193   00F8           	ds	2		; msgout
   194   00FA           	ds	2		; msgcnt
   195   00FC           	ds	2		; buffer
   196                  	endif
   197                  
   198                  	if	nmb$rqstrs ge 3
   199                  qcb$in$2:
   200                  	ds	2		; link
   201                  	db	'NtwrkQI2'	; name
   202                  	dw	2		; msglen
   203                  	dw	1		; nmbmsgs
   204                  	ds	2		; dqph
   205                  	ds	2		; nqph
   206                  	ds	2		; msgin
   207                  	ds	2		; msgout
   208                  	ds	2		; msgcnt
   209                  	ds	2		; buffer
   210                  	endif
   211                  
   212                  	if	nmb$rqstrs ge 4 
   213                  qcb$in$3:
   214                  	ds	2		; link
   215                  	db	'NtwrkQI3'	; name
   216                  	dw	2		; msglen
CP/M RMAC ASSEM 1.1	#005	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   217                  	dw	1		; nmbmsgs
   218                  	ds	2		; dqph
   219                  	ds	2		; nqph
   220                  	ds	2		; msgin
   221                  	ds	2		; msgout
   222                  	ds	2		; msgcnt
   223                  	ds	2		; buffer
   224                  	endif
   225                  
   226                  ; Output queue control blocks
   227                  
   228                  qcb$out$0:
   229   00FE           	ds	2		; link
   230   0100 4E7477726B	db	'NtwrkQO0'	; name
   231   0108 0200      	dw	2		; msglen
   232   010A 0300      	dw	nmb$bufs	; nmbmsgs
   233   010C           	ds	2		; dqph
   234   010E           	ds	2		; nqph
   235   0110           	ds	2		; msgin
   236   0112           	ds	2		; msgout
   237   0114           	ds	2		; msgcnt
   238   0116           	ds	2*nmb$bufs+1	; buffer
   239                  
   240                  ; Requester Management Table
   241                  
   242                  rqstr$table:
   243                  
   244                  ;requester 0 control block
   245                  
   246   011D FF        	db	0ffh		; requester ID (marked not in use)
   247   011E CA00      	dw	qcb$in$0	; UQCB:  QCB pointer
   248   0120 2201      	dw	$+2		;	 pointer to queue message
   249   0122 0000      	dw	$-$		; pointer to msg buffer (loaded on receive)
   250                  
   251                  	if	nmb$rqstrs ge 2
   252                  ;requester 1 control block
   253                  
   254   0124 FF        	db	0ffh		; requester ID (marked not in use)
   255   0125 E400      	dw	qcb$in$1	; UQCB:  QCB pointer
   256   0127 2901      	dw	$+2		;	 pointer to queue message
   257   0129 0000      	dw	$-$		; pointer to msg buffer (loaded on receive)
   258                  	endif
   259                  
   260                  	if	nmb$rqstrs ge 3
   261                  ;requester 2 control block
   262                  
   263                  	db	0ffh		; requester ID (marked not in use)
   264                  	dw	qcb$in$2	; UQCB:  QCB pointer
   265                  	dw	$+2		;	 pointer to queue message
   266                  	dw	$-$		; pointer to msg buffer (loaded on receive)
   267                  	endif
   268                  
   269                  	if	nmb$rqstrs ge 4
   270                  ;requester 3 control block
CP/M RMAC ASSEM 1.1	#006	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   271                  
   272                  	db	0ffh		; requester ID (marked not in use)
   273                  	dw	qcb$in$3	; UQCB:  QCB pointer
   274                  	dw	$+2		;	 pointer to queue message
   275                  	dw	$-$		; pointer to msg buffer (loaded on receive)
   276                  	endif
   277                  
   278                  ; Output user queue control block
   279                  
   280                  uqcb$out$0:
   281   012B FE00      	dw	qcb$out$0	; pointer
   282   012D 2F01      	dw	out$buffer$ptr	; pointer to queue message
   283                  
   284                  out$buffer$ptr:
   285   012F           	ds	2		; a queue read will return the message
   286                  				; buffer pointer in this location
   287                  
   288                  ; UQCB for flagging errors from receive process to send process
   289                  
   290                  uqcb$in$out$0:
   291   0131 FE00      	dw	qcb$out$0	; pointer
   292   0133 3501      	dw	in$out$buffer$ptr
   293                  				; pointer to queue message
   294                  
   295                  in$out$buffer$ptr:
   296   0135           	ds	2		; this pointer used by input process to
   297                  				;  to output "server not logged in" errors
   298                  
   299                  ; Server Configuration Table
   300                  
   301                  configtbl:
   302   0137 00        	db	0		; Server status byte
   303   0138 00        	db	0		; Server processor ID
   304   0139 02        	db	nmb$rqstrs	; Max number of requesters supported at once
   305   013A 00        	db	0		; Number of currently logged in requesters
   306   013B 0000      	dw	0000h		; 16 bit vector of logged in requesters
   307   013D           	ds	16		; Logged In Requester processor ID's
   308   014D 5041535357	db	'PASSWORD' 	; login password
   309                  
   310                  ; Stacks for server processes.  A pointer to the associated process 
   311                  ; descriptor area must reside on the top of each stack.  The stack for
   312                  ; SERVR0PR is internal to SERVER.RSP, and is consequently omitted from the
   313                  ; NETWRKIF module.
   314                  
   315   0096 =         srvr$stk$len	equ	96h	; server process stack size
   316                  
   317                  		if	nmb$rqstrs ge 2
   318   0155           srvr$stk$1:	ds	srvr$stk$len-2
   319   01E9 EB01      		dw	srvr$1$pd
   320                  		endif
   321                  
   322                  		if	nmb$rqstrs ge 3
   323                  srvr$stk$2:	ds	srvr$stk$len-2
   324                  		dw	srvr$2$pd
CP/M RMAC ASSEM 1.1	#007	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   325                  		endif
   326                  
   327                  		if	nmb$rqstrs ge 4
   328                  srvr$stk$3:	ds	srvr$stk$len-2
   329                  		dw	srvr$3$pd
   330                  		endif
   331                  
   332                  ; Memory allocation for server process descriptor copydown
   333                  ; All server process descriptor allocation must be contiguous
   334                  
   335                  		if	nmb$rqstrs ge 2
   336   01EB           srvr$1$pd:	ds	52
   337                  		endif
   338                  
   339                  		if	nmb$rqstrs ge 3
   340                  srvr$2$pd:	ds	52
   341                  		endif
   342                  
   343                  		if	nmb$rqstrs ge 4
   344                  srvr$3$pd:	ds	52
   345                  		endif
   346                  
   347                  
   348                  ; Buffer Control Block: 0 indicates buffer is free for receiving a message
   349                  ; 0ffh indicates that the buffer is in use
   350                  
   351                  buf$cb:		rept	nmb$bufs
   352                  		db	0
   353                  		endm
   354   021F+00        		DB	0
   355   0220+00        		DB	0
   356   0221+00        		DB	0
   357                  
   358                  ; Message Buffer Storage Area
   359                  
   360                  msg$buffers:	rept	nmb$bufs
   361                  		ds	buf$len
   362                  		endm
   363   0222+          		DS	BUF$LEN
   364   0328+          		DS	BUF$LEN
   365   042E+          		DS	BUF$LEN
   366                  
   367                  ; save area for XIOS routine addresses
   368                  
   369                  conin$jmp:
   370   0534 C3        	db	jmp
   371   0535 0000      conin:	dw	$-$
   372                  
   373                  conout$jmp:
   374   0537 C3        	db	jmp	
   375   0538 0000      conout:	dw	$-$
   376                  
   377                  constat$jmp:
   378   053A C3        	db	jmp	
CP/M RMAC ASSEM 1.1	#008	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   379                  constat:
   380   053B 0000      	dw	$-$
   381                  
   382                  
   383                  
   384                  
   385                  ; NETWRKIF Utility Routines
   386                  
   387                  ; Operating system linkage routine
   388                  
   389                  monx:
   390                  
   391   053D 2A0000    	lhld	bdos$adr
   392   0540 E9        	pchl
   393                  
   394                  
   395                  ; Double word subtract: DE = HL - DE
   396                  
   397                  dw$sub:
   398   0541 7D        	mov	a,l
   399   0542 93        	sub	e
   400   0543 5F        	mov	e,a
   401   0544 7C        	mov	a,h
   402   0545 9A        	sbb	d
   403   0546 57        	mov	d,a
   404   0547 C9        	ret
   405                  
   406                  ; Routine to scan requester control blocks for a match with the received 
   407                  ; source ID.
   408                  ;
   409                  ; Input:  A = Source ID to Match
   410                  ;
   411                  ; Output: 
   412                  ;	success:  HL = pointer to requester control block
   413                  ;		  A <> 0FFh
   414                  ;	no match, but a free control block found:
   415                  ;		  HL = pointer to RCB
   416                  ;		   A = 0FFh
   417                  ;		  CY = 0
   418                  ;	no match and no available RCB's:
   419                  ;		   A = 0FFh
   420                  ;		  CY = 1
   421                  
   422                  scan$table:
   423                  
   424   0548 211D01    	lxi	h,rqstr$table		;point to the start of the RCB table
   425   054B 0602      	mvi	b,nmb$rqstrs
   426   054D 110700    	lxi	d,rcb$len		;size of RCB's for scanning the table
   427                  
   428                  sc$t1:
   429                  
   430   0550 BE        	cmp	m			;RCB ID = SID?
   431   0551 C8        	rz				;yes--> a match--> return
   432                  
CP/M RMAC ASSEM 1.1	#009	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   433   0552 19        	dad	d			;else-->check next entry
   434   0553 05        	dcr	b
   435   0554 C25005    	jnz	sc$t1
   436                  
   437   0557 211D01    	lxi	h,rqstr$table		;no match-->look for a free entry
   438   055A 0602      	mvi	b,nmb$rqstrs
   439                  
   440                  sc$t2:
   441                  
   442   055C 7E        	mov	a,m
   443   055D 3C        	inr	a
   444   055E CA6A05    	jz	sc$t3			;an unoccupied entry has been found
   445                  
   446   0561 19        	dad	d			;else-->keep looking
   447   0562 05        	dcr	b
   448   0563 C25C05    	jnz	sc$t2
   449                  
   450   0566 3EFF      	mvi	a,0ffh			;outa luck-->set the big error
   451   0568 37        	stc
   452   0569 C9        	ret	
   453                  
   454                  sc$t3:					;no match, but found a free entry
   455                  
   456   056A 3D        	dcr	a			;A=0FFh
   457   056B B7        	ora	a			;CY=0
   458   056C C9        	ret
   459                  
   460                  
   461                  ; This routine free up a requester control block for somebody else who
   462                  ; might want to Log In.
   463                  ;
   464                  ;	Input:  A = source ID that just logged off
   465                  
   466                  free$rqstr$tbl:
   467                  
   468   056D 211D01    	lxi	h,rqstr$table
   469   0570 110700    	lxi	d,rcb$len
   470                  
   471                  fr$t1:
   472                  
   473   0573 BE        	cmp	m
   474   0574 C27A05    	jnz	fr$t2			;RCB ID <> SID-->keep scanning
   475                  
   476   0577 36FF      	mvi	m,0ffh			;else-->mark it as unoccupied
   477   0579 C9        	ret				;  and bug out
   478                  
   479                  fr$t2:
   480                  
   481   057A 19        	dad	d
   482   057B C37305    	jmp	fr$t1			;keep going--it's in there somewhere
   483                  
   484                  
   485                  
   486                  ; Routine to send a message on the network
CP/M RMAC ASSEM 1.1	#010	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   487                  ; Input:  HL = pointer to message buffer
   488                  
   489                  send$msg:
   490                  
   491   057E E5        	push	h
   492   057F 364B      	mvi	m,fmt$byte		;set ulc$net format byte
   493                  
   494   0581 23        	inx	h			;virtual circuit = requester ID
   495                  
   496   0582 23        	inx	h
   497   0583 23        	inx	h
   498                  
   499   0584 46        	mov	b,m			;save function number
   500                  
   501   0585 23        	inx	h			;get SIZ
   502   0586 5E        	mov	e,m
   503                  
   504   0587 1600      	mvi	d,0			;normalize CP/NET to ULCnet length
   505   0589 13        	inx	d
   506   058A 13        	inx	d
   507                  
   508   058B 70        	mov	m,b			;put FNC in first message byte
   509                  
   510   058C 2B        	dcx	h			;store length
   511   058D 72        	mov	m,d
   512   058E 2B        	dcx	h
   513   058F 73        	mov	m,e
   514                  
   515   0590 C1        	pop	b			;restore buffer pointer
   516   0591 1620      	mvi	d,console$num		;set up fake console number for xios
   517   0593 C33705    	jmp	conout$jmp		;blast that packet
   518                  
   519                  
   520                  ; Routine to receive a message on the network
   521                  ; Input:  DE = pointer to buffer
   522                  
   523                  rcv$message:
   524                  
   525   0596 42        	mov	b,d
   526   0597 4B        	mov	c,e
   527   0598 C5        	push	b			;save buffer pointer
   528   0599 1620      	mvi	d,console$num
   529   059B CD3405    	call	conin$jmp		;receive the message
   530                  
   531   059E E1        	pop	h
   532   059F 3600      	mvi	m,0			;FMT = 0 (requester to server)
   533                  
   534   05A1 23        	inx	h
   535   05A2 46        	mov	b,m			;save rqstr ID = virtual circuit
   536                  
   537   05A3 3A3801    	lda	configtbl+1
   538   05A6 77        	mov	m,a			;DID = server ID
   539                  
   540   05A7 23        	inx	h
CP/M RMAC ASSEM 1.1	#011	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   541   05A8 5E        	mov	e,m			;get low order length
   542                  
   543   05A9 70        	mov	m,b			;SID = requester ID
   544                  
   545   05AA 23        	inx	h
   546   05AB 56        	mov	d,m			;get hi order length
   547                  
   548   05AC 1B        	dcx	d
   549   05AD 1B        	dcx	d			;normalize ULCnet to CP/NET SIZ
   550                  
   551   05AE 23        	inx	h
   552   05AF 46        	mov	b,m			;get FNC
   553                  
   554   05B0 73        	mov	m,e			;store SIZ
   555                  
   556   05B1 2B        	dcx	h
   557   05B2 70        	mov	m,b			;store FNC	
   558                  	
   559   05B3 C9        	ret				;ULCnet message formatted
   560                  
   561                  
   562                  
   563                  
   564                  
   565                  ; Network I/F Receiver Process
   566                  
   567                  
   568                  setup:					;initialize NETWRKIF
   569                  
   570   05B4 0603      	mvi	b,nmb$rqstrs+1		;loop counter for making n+1 queues
   571   05B6 0E86      	mvi	c,134			;make queue function code
   572   05B8 11CA00    	lxi	d,qcb$in$0
   573                  
   574                  makeq:					;make all input and output queue(s)
   575                  
   576   05BB C5        	push	b
   577   05BC D5        	push	d
   578   05BD CD3D05    	call	monx
   579                  
   580   05C0 E1        	pop	h
   581   05C1 111A00    	lxi	d,26
   582   05C4 19        	dad	d
   583   05C5 EB        	xchg
   584                  
   585   05C6 C1        	pop	b
   586   05C7 05        	dcr	b
   587   05C8 C2BB05    	jnz	makeq
   588                  			
   589   05CB 0E9A      	mvi	c,154
   590   05CD CD3D05    	call	monx
   591                  
   592   05D0 110900    	lxi	d,9			;write configuration table address
   593   05D3 19        	dad	d			; into system data page, allowing
   594   05D4 113701    	lxi	d,configtbl		;  server initialization to proceed
CP/M RMAC ASSEM 1.1	#012	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   595   05D7 F3        	di
   596   05D8 73        	mov	m,e
   597   05D9 23        	inx	h
   598   05DA 72        	mov	m,d
   599   05DB FB        	ei
   600                  
   601   05DC 2B        	dcx	h			;point to XIOS jump table page
   602   05DD 2B        	dcx	h
   603   05DE 2B        	dcx	h
   604   05DF 66        	mov	h,m
   605   05E0 2E00      	mvi	l,0
   606                  
   607   05E2 110600    	lxi	d,6
   608   05E5 19        	dad	d			;point to constat
   609   05E6 223B05    	shld	constat
   610                  
   611   05E9 23        	inx	h
   612   05EA 23        	inx	h
   613   05EB 23        	inx	h			;point to conin
   614   05EC 223505    	shld	conin
   615                  
   616   05EF 23        	inx	h
   617   05F0 23        	inx	h
   618   05F1 23        	inx	h
   619   05F2 223805    	shld	conout			;point to conout
   620                  
   621   05F5 1620      	mvi	d,console$num
   622   05F7 CD3A05    	call	constat$jmp		;use constat to initialize ulcnet
   623                  
   624   05FA 116600    	lxi	d,networkout		;create network I/F output process
   625   05FD 0E90      	mvi 	c,144
   626   05FF CD3D05    	call	monx
   627                  
   628                  input:					;input process loop
   629                  
   630                  ; Find a free buffer
   631                  
   632   0602 211F02    	lxi	h,buf$cb		;point to buffer control block
   633   0605 112202    	lxi	d,msg$buffers		;point to base of buffer area
   634   0608 0603      	mvi	b,nmb$bufs		;get total number of buffers
   635                  
   636                  input2:
   637                  
   638   060A 7E        	mov	a,m
   639   060B 3C        	inr	a
   640   060C C22306    	jnz	input3			;we found a free buffer-->use it
   641                  
   642   060F E5        	push	h			;point to next buffer
   643   0610 210601    	lxi	h,buf$len
   644   0613 19        	dad	d
   645   0614 EB        	xchg
   646                  
   647   0615 E1        	pop	h			;point to next buffer control field
   648   0616 23        	inx	h
CP/M RMAC ASSEM 1.1	#013	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   649                  
   650   0617 05        	dcr	b			;have we scanned all the buffers?
   651   0618 C20A06    	jnz	input2
   652                  
   653   061B 0E8E      	mvi	c,142			;uh oh, we're all clogged up
   654   061D CD3D05    	call	monx			;dispatch and go sleepy bye for a bit
   655   0620 C30206    	jmp	input			;try again
   656                  
   657                  input3:
   658                  
   659   0623 36FF      	mvi	m,0ffh			;found a buffer-->mark it used
   660                  
   661   0625 D5        	push	d
   662                  
   663                  ; Receive the message
   664                  
   665   0626 CD9605    	call	rcv$message
   666                  
   667   0629 E1        	pop	h
   668   062A E5        	push	h
   669                  
   670   062B 23        	inx	h			;check requester table to see 
   671   062C 23        	inx	h			;  whether the source requester
   672   062D 7E        	mov	a,m			;    is logged-in
   673   062E CD4805    	call	scan$table
   674                  
   675   0631 3C        	inr	a
   676   0632 CA4A06    	jz	input4			;not logged-in-->go check for login
   677                  
   678                  input6:
   679                  
   680   0635 110500    	lxi	d,buf$ptr		;else-->update message buffer pointer
   681   0638 19        	dad	d
   682                  
   683   0639 D1        	pop	d
   684   063A 73        	mov	m,e
   685   063B 23        	inx	h
   686   063C 72        	mov	m,d
   687                  
   688   063D 11FBFF    	lxi	d,uqcb-buf$ptr-1	;point to the uqcb for this requester
   689   0640 19        	dad	d
   690   0641 EB        	xchg
   691                  
   692   0642 0E8B      	mvi	c,139			;write the message to the queue
   693   0644 CD3D05    	call	monx
   694                  
   695   0647 C30206    	jmp	input			;round and round we go
   696                  
   697                  input4:					;else-->requester not logged-in
   698                  
   699   064A D1        	pop	d
   700   064B 13        	inx	d
   701   064C 13        	inx	d
   702   064D 13        	inx	d
CP/M RMAC ASSEM 1.1	#014	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   703   064E DA6006    	jc	input5			;bomb the message if there's no
   704                  					; table entries left
   705                  
   706   0651 1A        	ldax	d	
   707   0652 FE40      	cpi	64			;is it a login?
   708   0654 C26006    	jnz	input5
   709                  
   710   0657 1B        	dcx	d			;yes-->mark the control block with
   711   0658 1A        	ldax	d			;  the source ID
   712   0659 77        	mov	m,a
   713                  
   714   065A 1B        	dcx	d			;go do the queue write
   715   065B 1B        	dcx	d
   716   065C D5        	push	d
   717   065D C33506    	jmp	input6
   718                  
   719                  input5:					;flag a "not logged in" extended error
   720                  
   721   0660 EB        	xchg
   722   0661 23        	inx	h
   723   0662 3601      	mvi	m,1			;set SIZ=1
   724   0664 23        	inx	h
   725   0665 36FF      	mvi	m,0ffh			;set return code to error
   726   0667 23        	inx	h
   727   0668 360C      	mvi	m,0ch			;flag extended error 12
   728                  
   729   066A 11FAFF    	lxi	d,fmt-msg-1
   730   066D 19        	dad	d			;point back at message start
   731   066E 3601      	mvi	m,1			;format = 1
   732                  
   733   0670 23        	inx	h			;swap DID and SID
   734   0671 7E        	mov	a,m
   735   0672 23        	inx	h
   736   0673 46        	mov	b,m
   737   0674 77        	mov	m,a
   738   0675 2B        	dcx	h
   739   0676 70        	mov	m,b
   740   0677 2B        	dcx	h
   741                  
   742   0678 223501    	shld	in$out$buffer$ptr	;write buffer pointer to queue msg buf
   743                  
   744   067B 113101    	lxi	d,uqcb$in$out$0		;write to the queue
   745   067E 0E8B      	mvi	c,139
   746   0680 CD3D05    	call	monx
   747   0683 C30206    	jmp	input			;try again
   748                  
   749                  
   750                  
   751                  ;  Network I/F transmitter process
   752                  
   753                  output:
   754                  
   755   0686 112B01    	lxi	d,uqcb$out$0		;read the output queue-->go sleepy
   756   0689 0E89      	mvi	c,137			;  bye until some server process
CP/M RMAC ASSEM 1.1	#015	NETWRKIF FOR SYSTEMS RUNNING ULCNET

   757   068B CD3D05    	call	monx			;    sends a response
   758                  
   759   068E 2A2F01    	lhld	out$buffer$ptr	
   760   0691 EB        	xchg
   761   0692 D5        	push	d			;save message pointer
   762                  
   763   0693 210300    	lxi	h,fnc			;get message function code
   764   0696 19        	dad	d
   765   0697 7E        	mov	a,m
   766   0698 2B        	dcx	h
   767                  
   768   0699 FE41      	cpi	65			;is it a logoff?
   769   069B C2A206    	jnz	output2
   770                  
   771   069E 7E        	mov	a,m			;load SID
   772   069F CC6D05    	cz	free$rqstr$tbl		;yes-->free up the server process
   773                  
   774                  output2:
   775                  
   776   06A2 E1        	pop	h
   777   06A3 E5        	push	h
   778   06A4 CD7E05    	call	send$msg		;send the message
   779                  
   780   06A7 E1        	pop	h			;retrieve message pointer
   781                  
   782   06A8 112202    	lxi	d,msg$buffers		;DE = pointer - message buffer base
   783   06AB CD4105    	call	dw$sub
   784                  
   785   06AE 011F02    	lxi	b,buf$cb		;BC = DE/buf$len + buf$cb
   786                  
   787                  output3:
   788                  
   789   06B1 7B        	mov	a,e
   790   06B2 B2        	ora	d
   791   06B3 CAC106    	jz	output4
   792                  
   793   06B6 EB        	xchg
   794   06B7 110601    	lxi	d,buf$len
   795   06BA CD4105    	call	dw$sub
   796   06BD 0C        	inr	c
   797   06BE C3B106    	jmp	output3
   798                  
   799                  output4:
   800                  
   801   06C1 AF        	xra	a
   802   06C2 02        	stax	b			;free the buffer for re-use
   803                  
   804   06C3 C38606    	jmp	output			;transmission without end, amen
   805                  
   806   06C6           	end
CP/M RMAC ASSEM 1.1	#016	NETWRKIF FOR SYSTEMS RUNNING ULCNET

BDOSADR          0000    53#  391 
BUFCB            021F   351#  632   785 
BUFLEN           0106    72#  361   363   364   365   643   794 
BUFPTR           0005    87#   88   680   688 
CONFIGTBL        0137   301#  537   594 
CONIN            0535   371#  614 
CONINJMP         0534   369#  529 
CONOUT           0538   375#  619 
CONOUTJMP        0537   373#  517 
CONSOLENUM       0020    60#  516   528   621 
CONSTAT          053B   379#  609 
CONSTATJMP       053A   377#  622 
DID              0001    67#   68 
DWSUB            0541   397#  783   795 
FMT              0000    66#   67   729 
FMTBYTE          004B    61#  492 
FNC              0003    69#   70   763 
FREERQSTRTBL     056D   466#  772 
FRT1             0573   471#  482 
FRT2             057A   474   479#
INOUTBUFFERPTR   0135   292   295#  742 
INPUT            0602   628#  655   695   747 
INPUT2           060A   636#  651 
INPUT3           0623   640   657#
INPUT4           064A   676   697#
INPUT5           0660   703   708   719#
INPUT6           0635   678#  717 
MAKEQ            05BB   574#  587 
MONX             053D   389#  578   590   626   654   693   746   757 
MSG              0005    71#   72   729 
MSGBUFFERS       0222   360#  633   782 
NETSTKIN         0036    98   122#
NETSTKOU         009A   136   160#
NETWORKIN        0002    93#
NETWORKOUT       0066   131#  624 
NMBBUFS          0003    59#  232   238   351   360   634 
NMBRQSTRS        0002    58#  184   198   212   251   260   269   304   317   322 
                        327   335   339   343   425   438   570 
OUTBUFFERPTR     012F   282   284#  759 
OUTPUT           0686   167   753#  804 
OUTPUT2          06A2   769   774#
OUTPUT3          06B1   787#  797 
OUTPUT4          06C1   791   799#
QCBIN0           00CA   172#  247   572 
QCBIN1           00E4   185#  255 
QCBOUT0          00FE   228#  281   291 
RCBLEN           0007    88#  426   469 
RCVMESSAGE       0596   523#  665 
RQSTRID          0000    85#   86 
RQSTRTABLE       011D   242#  424   437   468 
SCANTABLE        0548   422#  673 
SCT1             0550   428#  435 
SCT2             055C   440#  448 
SCT3             056A   444   454#
CP/M RMAC ASSEM 1.1	#017	NETWRKIF FOR SYSTEMS RUNNING ULCNET

SENDMSG          057E   489#  778 
SETUP            05B4   129   568#
SID              0002    68#   69 
SIZ              0004    70#   71 
SRVR1PD          01EB   319   336#
SRVRSTK1         0155   318#
SRVRSTKLEN       0096   315#  318   323   328 
ULCFMT           0000    76#   77 
ULCFNC           0004    80#   81 
ULCLENHI         0003    79#   80 
ULCLENLO         0002    78#   79 
ULCMSG           0005    81#
ULCVCIRC         0001    77#   78 
UQCB             0001    86#   87   688 
UQCBINOUT0       0131   290#  744 
UQCBOUT0         012B   280#  755 

Listing F-3: ULCnet Data-link Layer MP/M XIOS Module
CP/M RMAC ASSEM 1.1	#001	ULCNET DATA LINK LAYER MP/M XIOS MODULE

     1                  	title 'ULCNET Data Link Layer MP/M XIOS Module'
     2                  	page	54
     3                  
     4                  ;*****************************************************************************
     5                  ;* This module must be linked into the server's XIOS.  It is designed to     *
     6                  ;* run under MP/M for the Xerox 820, but should be easily customized.  It    *
     7                  ;* contains the ULCnet interface modules DLIF and NIOD.  The DLIF is an      *
     8                  ;* interface between the transport software contained in ULCIF.RSP and the   *
     9                  ;* data-link software contained in IPBMAIN.REL.  The NIOD contains the actual*
    10                  ;* hardware drivers required to run ULCnet.  The module IPBMAIN.REL must also*
    11                  ;* be linked into the XIOS.                                                  *
    12                  ;*****************************************************************************
    13                  
    14                  ;	This software is the result of a joint effort between
    15                  ;
    16                  ;		Digital Research, Inc.
    17                  ;		P.O. Box 579
    18                  ;		Pacific Grove, CA 93950
    19                  ;	and
    20                  ;		Keybrook Business Systems, Inc.
    21                  ;		2035 National Avenue
    22                  ;		Hayward, CA 94545	
    23                  
    24                  ; Conditional assembly control
    25                  
    26   FFFF =         true		equ	0ffffh
    27   0000 =         false		equ	not true
    28                  
    29   FFFF =         interrupts	equ	true		; false=polled, true=interrupt-driven
    30   FFFF =         netstats	equ	true		; switch to gather network statistics
    31   FFFF =         slfclkd		equ	true		; supports self-clocked operation
    32                  
    33                  ; Linkage information
    34                  
    35                  	public	nconst,nconin,nconout	; XIOS console jump table entries
    36                  	public	polldevice		; XIOS polling routine
    37                  	public	setbaud,xmit,recv,initu	; NIOD routines called by IPBMAIN
    38                  	public	inituart,pgmuart
    39                  	public	chkstat,netidle,initrecv
    40                  	public	wait,restuart,csniod
    41                  	public	dsblxmit
    42                  	public	dllbau,netadr
    43                  
    44                  	if	interrupts
    45                  	public	enblrecv,dsblrecv
    46                  	endif
    47                  
    48                  	extrn	transmit,receive	; IPBMAIN routines and objects
    49                  	extrn	gettcode,getrcode
    50                  	extrn	csdll,dllon,regshrt
    51                  	extrn	terrcnt,parcntr,ovrcntr
    52                  	extrn	frmcntr,inccntr
    53                  	extrn	xdos,const,conin,conout	; linkage back to the rest of XIOS
    54                  	extrn	poldev
CP/M RMAC ASSEM 1.1	#002	ULCNET DATA LINK LAYER MP/M XIOS MODULE

    55                  
    56                  	if	interrupts
    57                  	extrn	rtmochk			; IPBMAIN interrupt routines
    58                  	extrn	dlisr,reisr,niisr
    59                  	endif
    60                  
    61                  
    62                  ; Hardware definitions for the Z80-SIO channel A - For the Xerox 820.
    63                  
    64   0003 =         baudsl	equ	03h		; Usable baud rates: 9600, 19.2K asynch.,
    65   002A =         baudsh	equ	2ah		; 76.8K, 153.6K, 307.2K self-clocked
    66                  
    67                  				; baud rate capability mask
    68   2A03 =         bauds	equ	(baudsh*100h)+baudsl
    69                  
    70   0000 =         baudgen	equ	0		; External baud rate generator register
    71   0006 =         siocmd	equ	6		; Command/Mode register
    72   0006 =         siostat	equ	6		; Status register
    73   0004 =         sioxmit	equ	4		; Transmit register
    74   0004 =         siorecv	equ	4		; Receive register
    75                  
    76   0002 =         xrdybit	equ	2		; Transmit buffer empty status bit
    77   0004 =         xrdymsk	equ	4		; transmit buffer empty status mask
    78   0000 =         rrdybit	equ	0		; Receive buffer full status bit
    79   0001 =         rrdymsk	equ	1		; receive buffer full status mask
    80   0003 =         carbit	equ	3		; Net Idle detect bit position
    81   0008 =         carmsk	equ	8		; Net Idle detect mask
    82   0030 =         errst	equ	030h		; Error flag reset
    83   0070 =         errbits	equ	070h		; Error bit position mask
    84   0004 =         pbit	equ	4		; Parity error bit position
    85   0010 =         pmsk	equ	10h		; parity error mask
    86   0005 =         obit	equ	5		; Overrun error bit position
    87   0020 =         omsk	equ	20h		; overrun error mask
    88   0006 =         fbit	equ	6		; Framing error bit position
    89   0040 =         fmsk	equ	40h		; framing error mask
    90   0003 =         selfbit	equ	3		; Self clock bit position
    91   0008 =         selfmsk	equ	8		; slef clock bit mask
    92   00EA =         dtron	equ	0eah		; Turn on DTR
    93   006A =         dtroff	equ	06ah		; Turn off DTR
    94   00C1 =         enarcv	equ	0c1h		; Enable receive-clock
    95   00C0 =         disrcv	equ	0c0h		; Disable receive clock
    96   000F =         enaslf	equ	00fh		; Enable Self-clock mode
    97   004F =         disslf	equ	04fh		; Disable Self-clock mode 
    98                  
    99                  ; SIO Mode 2 interrupts vector table
   100                  
   101   FF08 =         siov4	equ	0ff08h		; SIO port A xmit buffer empty
   102   FF0A =         siov5	equ	0ff0ah		; SIO port A external status change
   103   FF0C =         siov6	equ	0ff0ch		; SIO port A receive
   104   FF0E =         siov7	equ	0ff0eh		; SIO port A special receive condition
   105                  
   106   0020 =         netcon	equ	20h		; fake console number called by ULCIF for
   107                  				; network operations
   108                  
CP/M RMAC ASSEM 1.1	#003	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   109                  ; polling equates
   110                  
   111   0020 =         ulctx	equ	20h		; transmission poll number
   112   0021 =         ulcrx	equ	21h		; receive poll number
   113                  	page
CP/M RMAC ASSEM 1.1	#004	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   114                  
   115                  
   116                  
   117                  ; ULCnet Data Definitions
   118                  
   119   0000           netadr:	ds	3		;ULCnet network address
   120   0003           dllbau:	ds	2		;baud rate mask
   121                  
   122   0016 =         timeval	equ	22		; WAIT routine time constant
   123                  				; 12 for 2.5 megahertz Z80
   124                  				; 22 for 4.0 megahertz Z80 	
   125                  
   126                  dev$table:			;polling device table
   127                  
   128   0005 9800      	dw	twait		;receive poll wait
   129   0007 D300      	dw	rwait		;transmit poll wait
   130   0002 =         num$devices	equ	($-dev$table)/2	
   131                  
   132   0009           tcode:	ds	1			; Transmit Return code
   133   000A           rcode:	ds	1			; Receive Return code
   134                  
   135   000B FF        curbaud db	0ffh			; Current baud rate
   136                  
   137                  				
   138   000C 0102040810btbl:	db	1,2,4,8,16,32,64,128	; table to convert baud number codes
   139                  					;   into a bit mask
   140                  
   141                  baudtbl:				; async baud rate table
   142                  
   143   0014 0E        	db	0eh			; 9600 Baud
   144   0015 0F        	db	0fh			; 19200
   145                  
   146                  scbaudt:				; self-clock baud rate table
   147                  
   148   0016 00        	db	0			;  62500 Baud - Not implemented
   149   0017 0D        	db	0dh			;  76800 Baud
   150   0018 00        	db	0			; 125000 Baud - Not implemented
   151   0019 0E        	db	0eh			; 153600 Baud
   152   001A 00        	db	0			; 250000 Baud - Not implemented
   153   001B 0F        	db	0fh			; 307200 Baud
   154                  
   155                  	if	interrupts
   156   001C 30144F156Asioiblk	db	030h,14h,4fh,15h,06ah,13h,0c1h,11h,01h,10h,10h,30h
   157                  	else
   158                  sioiblk	db	030h,14h,4fh,15h,06ah,13h,0c1h,11h,00h,10h,10h,30h
   159                  	endif
   160                  
   161   000C =         sioilen	equ	$-sioiblk
   162                  
   163                  	page
CP/M RMAC ASSEM 1.1	#005	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   164                  
   165                  
   166                  
   167                  ; ULCnet data-link interface code
   168                  
   169                  
   170                  ; POLLDEVICE:  Device polling routine.
   171                  ;	Input:
   172                  ;		C = device number to poll
   173                  ;	Output:
   174                  ;		A = 0    if not ready
   175                  ;		    0ffh if ready
   176                  
   177                  polldevice:			
   178                  
   179   0028 79        	mov	a,c		; if not a network poll, go to the real
   180   0029 D620      	sui	ulctx		;   routine
   181   002B DA0000    	jc	poldev
   182                  
   183   002E FE02      	cpi	num$devices	; check for poll number in bounds
   184   0030 DA3600    	jc	devok
   185                  
   186   0033 3E00      	mvi	a,0		; out-of-bounds-->don't do anything
   187   0035 C9        	ret
   188                  
   189                  devok:
   190                  
   191   0036 6F        	mov	l,a
   192   0037 2600      	mvi	h,0
   193   0039 29        	dad	h		; multiply index by 2
   194                  
   195   003A 110500    	lxi	d,dev$table	; index into the poll routine table
   196   003D 19        	dad	d
   197                  
   198   003E 5E        	mov	e,m
   199   003F 23        	inx	h
   200   0040 56        	mov	d,m		; get the routine address
   201                  
   202   0041 EB        	xchg
   203   0042 E9        	pchl			; dispatch
   204                  
   205                  
   206                  
   207                  ;
   208                  ; NCONST: Console status entry point.  If register D = fake network
   209                  ;	  console ID, do network initialization.  Otherwise, go back to
   210                  ;	  the real console routines.
   211                  
   212                  nconst:
   213                  
   214   0043 3E20      	mvi	a,netcon	; Check if network call
   215   0045 BA        	cmp	d
   216   0046 C20000    	jnz	const		; Jump to normal CONST if not network
   217                  
CP/M RMAC ASSEM 1.1	#006	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   218   0049 CD0000    	call	csdll		; Cold start the data link
   219   004C CD0000    	call	dllon		; Initialize the SIO Drivers
   220   004F AF        	xra	a		; Initialize all the short addresses
   221                  
   222                  nxtadd:
   223                  
   224   0050 3C        	inr	a
   225   0051 FE05      	cpi	5		; Check for last address
   226   0053 C8        	rz
   227   0054 F5        	push	psw
   228   0055 CD0000    	call	regshrt
   229   0058 F1        	pop	psw
   230   0059 C35000    	jmp	nxtadd		; Jump to process next address
   231                  
   232                  
   233                  ; NCONIN:  Console In entry point.  If register D = the fake network ID
   234                  ;	   then receive a network message, using polled status checks of
   235                  ;	   an interrupt-driven data-link.  Otherwise, go back to the real
   236                  ;	   CONIN routine.
   237                  
   238                  nconin:
   239                  
   240   005C 3E20      	mvi	a,netcon	; Check for network call
   241   005E BA        	cmp	d
   242   005F C20000    	jnz	conin		; Jump to normal CONIN if not network
   243                  
   244   0062 50        	mov	d,b		; Setup for PSRECEIVE
   245   0063 59        	mov	e,c
   246                  
   247                  rretry:
   248                  
   249   0064 AF        	xra	a		; Packet mode
   250   0065 010101    	lxi	b,257		; Buffer size
   251   0068 210000    	lxi	h,0		; Infinite wait	
   252   006B D5        	push	d		; Save buffer address for retry
   253   006C CDC100    	call	psrecv
   254   006F D1        	pop	d		; Restore buffer address
   255   0070 B7        	ora	a
   256   0071 C8        	rz			; Return if no error
   257                  
   258   0072 C36400    	jmp	rretry		; Jump to try again if error 
   259                  
   260                  
   261                  ; NCONOUT:  Console out entry point.  If D = fake console ID, send a network
   262                  ;	    message.  Otherwise, just head for the real CONOUT routine.
   263                  
   264                  
   265                  nconout:
   266                  
   267   0075 3E20      	mvi	a,netcon	; Check for network call
   268   0077 BA        	cmp	d
   269   0078 C20000    	jnz	conout		; Jump to normal CONOUT if not network
   270                  
   271   007B 50        	mov	d,b		; Setup for PSXMIT
CP/M RMAC ASSEM 1.1	#007	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   272   007C 59        	mov	e,c
   273                  
   274                  tretry:
   275                  
   276   007D AF        	xra	a		; Packet mode, wait for Net Idle
   277   007E D5        	push	d		; Save buffer address for retry
   278   007F CD8800    	call	psxmit
   279   0082 D1        	pop	d		; Restore buffer address
   280   0083 B7        	ora	a
   281   0084 C8        	rz			; Return if no error
   282                  
   283   0085 C37D00    	jmp	tretry		; Jump to retry if error
   284                  
   285                  
   286                  ; PSXMIT:  Transmit the packet pointed at by DE.  If carry flag is set
   287                  ;	   then don't wait for the Net to become idle.
   288                  ;
   289                  ; Returns the completion code in A:
   290                  ;
   291                  ;	0	- Transmission ok and Data Link Ack Received
   292                  ;		  (In the case of multicast, no Ack required)
   293                  ;	2	- Transmission OK but no Data Link Ack received.
   294                  ;
   295                  ;	4	- Other error.
   296                  
   297                  psxmit:
   298                  
   299   0088 CD0000    	call	transmit		; TRETCODE := TRANSMIT(TBUFPTR,)
   300                  
   301   008B 0E83      	mvi	c,83h			; Poll the transmitter for completion
   302   008D 1E20       	mvi	e,ulctx
   303   008F CD0000    	call	xdos			
   304                  
   305   0092 3A0900    	lda	tcode			; Fetch return code
   306   0095 C3CE00    	jmp	exitdl
   307                  	
   308                  ; TWAIT:  Transmission completion poll routine.
   309                  ;
   310                  ;	  Output:
   311                  ;		A = 0    if not complete
   312                  ;		    0ffh if complete
   313                  
   314                  twait:
   315                  
   316   0098 CD0000    	call	gettcode		; A := GETTCODE - Xmit return code
   317                  
   318   009B 5F        	mov	e,a			; get return code processing vectore
   319   009C 1600      	mvi	d,0
   320   009E 21A700    	lxi	h,trtbl
   321   00A1 19        	dad	d
   322                  
   323   00A2 5E        	mov	e,m			; dispatch on return code
   324   00A3 23        	inx	h
   325   00A4 66        	mov	h,m
CP/M RMAC ASSEM 1.1	#008	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   326   00A5 6B        	mov	l,e
   327   00A6 E9        	pchl
   328                  
   329                  ; Return code dispatch table
   330                  
   331   00A7 B700      trtbl:	dw	psxret			; Good transmission
   332   00A9 B700      	dw	psxret			; No Data Link Ack
   333   00AB B700      	dw	psxret			; Too many collisions
   334   00AD B700      	dw	psxret			; Transmitter is disabled
   335   00AF B500      	dw	tsleep			; Transmitter is idle
   336   00B1 B500      	dw	tsleep			; Transmitter is in progress
   337   00B3 B500      	dw	tsleep			; Transmitter is waiting for ack
   338                  
   339                  tsleep:
   340                  
   341   00B5 AF        	xra	a			; Code for continue to sleep
   342   00B6 C9        	ret
   343                  
   344                  psxret:					; Enter here if something happened
   345                  
   346   00B7 D2BB00    	jnc	twakeup			; Jump if no transmit error
   347   00BA 2F        	cma				; Else-->Indicate error
   348                  
   349                  twakeup:
   350                  
   351   00BB 320900    	sta	tcode			; Store return code
   352   00BE 3EFF      	mvi	a,0ffh			; Signal poll successful
   353   00C0 C9        	ret
   354                  
   355                  
   356                  
   357                  ; PSRECV:  Receive a packet into buffer pointed at by DE.  Length of
   358                  ; 	   packet must be less than length of buffer in BC. HL is the receive
   359                  ; 	   timeout count. 
   360                  ;
   361                  ;	   Upon return clear the carry bit if a packet received and ACKed.
   362                  ;   	   Set the carry flag if any error occured.
   363                  ;
   364                  
   365                  
   366                  psrecv:
   367                  
   368   00C1 CD0000    	call	receive			;  := RECEIVE(HL,DE,BC)
   369                  
   370   00C4 0E83      	mvi	c,83h			; Poll until receive complete
   371   00C6 1E21      	mvi	e,ulcrx
   372   00C8 CD0000    	call	xdos			
   373                  
   374   00CB 3A0A00    	lda	rcode			; Fetch return code
   375                  
   376                  ; Common exit routine for returning to the pseudo-console handler
   377                  
   378                  exitdl:
   379                  	
CP/M RMAC ASSEM 1.1	#009	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   380   00CE B7        	ora	a			; Assume no error
   381   00CF F0        	rp				; Return if no error
   382                  
   383   00D0 2F        	cma
   384   00D1 37        	stc				; Indicate error
   385   00D2 C9        	ret		
   386                  
   387                  ; RWAIT:  Poll routine to detect receive status.
   388                  ;
   389                  ;	Output:
   390                  ;		A = 0    if receive not complete
   391                  ;		    0ffh if receive complete
   392                  
   393                  rwait:
   394                  
   395   00D3 CD0000    	call	getrcode		; A := GETRCODE
   396                  
   397   00D6 5F        	mov	e,a			; form dispatch vector
   398   00D7 1600      	mvi	d,0
   399   00D9 21E200    	lxi	h,rrtbl
   400   00DC 19        	dad	d
   401                  
   402   00DD 5E        	mov	e,m			; dispatch on receive completion code
   403   00DE 23        	inx	h
   404   00DF 66        	mov	h,m
   405   00E0 6B        	mov	l,e
   406   00E1 E9        	pchl
   407                  
   408                  ; Receive completion code dispatch table
   409                  
   410   00E2 F000      rrtbl:	dw	rgood			; Good receive
   411   00E4 F600      	dw	rbad			; Bad receive
   412   00E6 F600      	dw	rbad			; Disabled
   413                  
   414                      	if	not interrupts
   415                  	dw	rbad			; Still idle after timeout
   416                  	else
   417   00E8 FA00      	dw	ridle			; Idle
   418                          endif
   419                  
   420   00EA EE00      	dw	rsleep			; Inprogress
   421   00EC EE00      	dw	rsleep			; In progress and for us.
   422                  
   423                  rsleep:	
   424                  
   425   00EE AF        	xra	a			; Code for continue to sleep
   426   00EF C9        	ret
   427                  
   428                  rgood:
   429                  rwakeup:
   430                  
   431   00F0 320A00    	sta	rcode			; Store return code
   432   00F3 3EFF      	mvi	a,0ffh			; Wake up code
   433   00F5 C9        	ret
CP/M RMAC ASSEM 1.1	#010	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   434                  
   435                  rbad:
   436                  
   437   00F6 2F        	cma				; Code for error
   438   00F7 C3F000    	jmp	rwakeup			; Jump to wake up receive process
   439                  
   440                      	if interrupts
   441                  
   442                  ridle:
   443                  
   444   00FA CD0000    	call	rtmochk			; Check for timeout
   445   00FD DAF600    	jc	rbad			; if timeout, signal error
   446   0100 C3EE00    	jmp	rsleep			; Continue to wait if no timeout
   447                  
   448   0103 C9        	ret
   449                  
   450                        	endif
   451                  	page
CP/M RMAC ASSEM 1.1	#011	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   452                  
   453                  
   454                  ; NIOD routines
   455                  
   456                  
   457                  
   458                  ; SETBAUD:  Set the baud rate based on the baud rate code in A.  Do special 
   459                  ; 	    logic for self-clocked mode.
   460                  ;
   461                  ;	 	0 = 9600 baud
   462                  ;		1 = 19200 baud
   463                  ;		9 = 76800 baud self-clock
   464                  ;		11= 153600 baud self-clock
   465                  ;		13= 307200 baud self-clock
   466                  ; 
   467                  ; If this station cannot handle the requested baud rate, then set
   468                  ; the carry flag.
   469                  
   470                  setbaud:
   471                  
   472   0104 E60F      	ani	0fh		; mask all but the baud bits
   473   0106 210B00    	lxi	h,curbaud	; are we at the current baud rate?
   474   0109 BE        	cmp	m
   475   010A C8        	rz			; yes-->all done
   476                  
   477   010B 47        	mov	b,a		; else-->get baud rate generator value
   478   010C E607      	ani	7
   479   010E 5F        	mov	e,a
   480   010F 1600      	mvi	d,0
   481                  
   482   0111 210C00    	lxi	h,btbl		; point to vertical-to-horizontal decode
   483   0114 19        	dad	d		;   table
   484                  
   485                  	if	slfclkd
   486   0115 78        	mov	a,b
   487   0116 E608      	ani	selfmsk		; is this a self-clocked value?
   488   0118 C24001    	jnz	selfclkd
   489                  	endif
   490                  
   491   011B 3E03      	mvi	a,baudsl	; get legal baud rate mask
   492   011D A6        	ana	m
   493   011E 37        	stc
   494   011F C8        	rz			; return with error if its an illegal rate
   495                  
   496                  	if	slfclkd
   497   0120 3E05      	mvi	a,5		; else-->switch off possible self-clock mode
   498   0122 D306      	out	siocmd
   499   0124 3E6A      	mvi	a,dtroff	; disable DTR in SIO register 5
   500   0126 D306      	out	siocmd
   501                  
   502   0128 3E04      	mvi	a,4		; disable sync mode in register 4
   503   012A D306      	out	siocmd
   504   012C 3E4F      	mvi	a,disslf
   505   012E D306      	out	siocmd
CP/M RMAC ASSEM 1.1	#012	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   506                  	endif
   507                  
   508   0130 211400    	lxi	h,baudtbl	; point to async baud rate table
   509                  
   510                  outbau:
   511                  
   512   0133 19        	dad	d		; get async baud rate value
   513   0134 7E        	mov	a,m
   514   0135 D300      	out	baudgen		; load it into the baud rate generator
   515                  				; NOTE: This is not a CTC
   516                  
   517   0137 210B00    	lxi	h,curbaud
   518   013A 70        	mov	m,b		; set current baud byte
   519                  
   520   013B CD1E02    	call	wait		; allow the system to reach equilibrium
   521                  
   522   013E A7        	ana	a		; return success
   523   013F C9        	ret
   524                  
   525                  	if	slfclkd
   526                  ; Throw SIO into self-clocked mode
   527                  
   528                  selfclkd:
   529                  
   530   0140 3E2A      	mvi	a,baudsh	; Is this a legal rate?
   531   0142 A6        	ana	m
   532   0143 37        	stc
   533   0144 C8        	rz			; return an error if not
   534                  
   535   0145 3E04      	mvi	a,4		; enable sync mode in register 4
   536   0147 D306      	out	siocmd
   537   0149 3E0F      	mvi	a,enaslf
   538   014B D306      	out	siocmd
   539                  
   540   014D 3E05      	mvi	a,5		; enable DTR in register 5
   541   014F D306      	out	siocmd
   542   0151 3EEA      	mvi	a,dtron
   543   0153 D306      	out	siocmd
   544                  
   545   0155 211600    	lxi	h,scbaudt	; point to baud rate table for self-clock mode
   546   0158 C33301    	jmp	outbau		; program the baud rate generator
   547                  	endif
   548                  
   549                  
   550                  ; DSBLXMIT:  Disable the transmitter if in self clocked mode
   551                  
   552                  dsblxmit:
   553                  
   554                  	if	slfclkd
   555   015B 3A0B00    	lda	curbaud		; are we in self-clocked mode?
   556   015E E608      	ani	selfmsk
   557   0160 C8        	rz			; no-->don't bother
   558                  
   559   0161 3E05      	mvi	a,5		; disable SIO from transmitting by disabling 
CP/M RMAC ASSEM 1.1	#013	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   560   0163 D306      	out	siocmd		; DTR in register 5
   561   0165 3E6A      	mvi	a,dtroff
   562   0167 D306      	out	siocmd
   563                  
   564   0169 3E05      	mvi	a,5		; Enable receive by re-enabling DTR
   565   016B D306      	out	siocmd
   566   016D 3EEA      	mvi	a,dtron
   567   016F D306      	out	siocmd
   568                  	endif
   569                  
   570   0171 C9        	ret
   571                  
   572                  
   573                  ; XMIT:  Transmit the byte in A on network A.
   574                  
   575                  
   576                  xmit:
   577                  
   578                  	if	not interrupts
   579                  	push	psw
   580                  
   581                  xmit1:
   582                  
   583                  	in	siostat		; don't overrun the transmitter if we're
   584                  	ani	xrdymsk		;  interrupt-driven; wait for TxReady
   585                  	jz	xmit1
   586                  
   587                  	pop	psw
   588                  	endif
   589                  
   590   0172 D304      	out	sioxmit		; blast that byte
   591   0174 C9        	ret
   592                  
   593                  
   594                  ; RECV:  Receive a byte from Network A. Set the carry flag if there was
   595                  ; 	 a receive error.
   596                  ;
   597                  ;	 For Z80-SIO receive errors are handled by the special receive
   598                  ; 	 condition interrupts.
   599                  
   600                  recv:
   601                  
   602                  	if	not interrupts
   603                  	call	netidle	
   604                  	jc	rto		; set error condition if the net went idle
   605                  
   606                  	in	siostat		; else-->wait until a character is in the
   607                  	ani	rrdymsk		;    buffer
   608                  	jz	recv
   609                  
   610                  	call	chkstat		; check for receive errors
   611                  
   612                  	else
   613   0175 A7        	ana	a		; clear carry flag
CP/M RMAC ASSEM 1.1	#014	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   614                  	endif
   615                  
   616   0176 DB04      	in	siorecv		; input the character
   617   0178 C9        	ret
   618                  
   619                  rto:				; set an error
   620                  
   621   0179 AF        	xra	a
   622   017A 37        	stc
   623   017B C9        	ret
   624                  	
   625                  
   626                  ; CHKSTAT:  Check error status bits of a receive error.  If not error then
   627                  ; 	    clear the carry flag and return.  Otherwise figure out which
   628                  ; 	    error occured and increment its counter and set the carry flag.
   629                  ; 	    Issue an error reset command to the UART.
   630                  
   631                  
   632                  chkstat:
   633                  
   634   017C 3E01      	mvi	a,1		; get error status from SIO read register 1
   635   017E D306      	out	siocmd
   636   0180 DB06      	in	siostat
   637                  
   638   0182 E670      	ani	errbits
   639   0184 C8        	rz			; no error occurred-->all done
   640                  
   641                  	if	netstats	; gather statistics on the type of error
   642   0185 47        	mov	b,a
   643   0186 E610      	ani	pmsk
   644   0188 CA9101    	jz	np		; not a parity error
   645                  
   646   018B 210000    	lxi	h,parcntr	; else-->
   647   018E CD0000    	call	inccntr		; increment parity error counter
   648                  
   649                  np:
   650                  
   651   0191 78        	mov	a,b
   652   0192 E605      	ani	obit
   653   0194 CA9D01    	jz	no		; not an overrun
   654                  
   655   0197 210000    	lxi	h,ovrcntr	; else-->
   656   019A CD0000    	call	inccntr		; increment overrun counter
   657                  
   658                  no:
   659                  
   660   019D 78        	mov	a,b
   661   019E E606      	ani	fbit
   662   01A0 CAA901    	jz	nf		; not a framing error
   663                  
   664   01A3 210000    	lxi	h,frmcntr	; else-->
   665   01A6 CD0000    	call	inccntr		; increment framing error counter
   666                  
   667                  nf:
CP/M RMAC ASSEM 1.1	#015	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   668                  	endif
   669                  
   670   01A9 3E30      	mvi	a,errst		; reset error condition
   671   01AB D306      	out	siocmd
   672   01AD 37        	stc			; signal an error
   673   01AE C9        	ret
   674                  		
   675                  
   676                  
   677                  ; NETIDLE:  See if network A is idle. If idle then set the carry flag.
   678                  
   679                  netidle:
   680                  
   681   01AF 3E10      	mvi	a,10h		; reset interrupts
   682   01B1 D306      	out	siocmd
   683   01B3 D306      	out	siocmd		; do it twice to reject glitches on DCD
   684                  
   685   01B5 DB06      	in	siostat		; is there a data-carrier detect?
   686   01B7 E608      	ani	carmsk
   687   01B9 C8        	rz			; yes-->net is in use-->carry flag cleared
   688                  
   689   01BA AF        	xra	a
   690   01BB CD0401    	call	setbaud		; net is idle-->reset to hailing rate (9600)
   691   01BE 37        	stc			; set net idle to true
   692   01BF C9        	ret
   693                  
   694                  
   695                  	if	interrupts
   696                  
   697                  ; ENBLRECV:  Enable the channel A receiver interrupts.
   698                  
   699                  enblrecv:
   700                  
   701   01C0 3E01      	mvi	a,1		; enable interrupts on all characters
   702   01C2 D306      	out	siocmd
   703   01C4 3E11      	mvi	a,011h		; NOTE: This mask would have to be 015h on
   704   01C6 D306      	out	siocmd		;  channel B
   705   01C8 C9        	ret
   706                  
   707                  ; DSBLRECV:  Disable the channel A receiver interrupts.
   708                  
   709                  dsblrecv:
   710                  
   711   01C9 3E01      	mvi	a,1		; Disable interrupts on received characters
   712   01CB D306      	out	siocmd		;   (Keep status interrupts enabled)
   713   01CD D306      	out	siocmd		; NOTE:  Channel B mask is 05h
   714   01CF C9        	ret
   715                  
   716                  	endif
   717                  
   718                  
   719                  ; PGMUART:  Program the Network UART channel
   720                  
   721                  pgmuart:
CP/M RMAC ASSEM 1.1	#016	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   722                  
   723                  	if	interrupts
   724                  				; The 820 already has the SIO vector address
   725                  				; programmed from channel B.  Other 
   726                  				; implementations will have to provide linkage
   727                  				; to the vector area in the main XIOS, and 
   728                  				; load the vector offset into SIO write 
   729                  				; register 2
   730                  
   731   01D0 210000    	lxi	h,niisr		; load status interrupt service routine vector
   732   01D3 220AFF    	shld	siov5
   733   01D6 210000    	lxi	h,dlisr		; load transmit ISR vector
   734   01D9 220CFF    	shld	siov6
   735   01DC 210000    	lxi	h,reisr		; load receiv ISR vector
   736   01DF 220EFF    	shld	siov7
   737                  	endif
   738                  
   739   01E2 211C00    	lxi	h,sioiblk	; point to SIO initialization block
   740   01E5 060C      	mvi	b,sioilen	; length of block
   741   01E7 F3        	di
   742                  
   743                  pgm1:
   744                  
   745   01E8 7E        	mov	a,m		; output the block to the SIO
   746   01E9 D306      	out	siocmd
   747   01EB 23        	inx	h
   748   01EC 05        	dcr	b
   749   01ED C2E801    	jnz	pgm1
   750                  
   751   01F0 FB        	ei
   752   01F1 AF        	xra	a		; set up hailing baud rate = 9600
   753   01F2 CD0401    	call	setbaud
   754   01F5 C9        	ret
   755                  
   756                  
   757                  ; INITUART:  Initialize the uart for network A by issuing a reset command
   758                  ; 	     and clearing out the receive buffer.
   759                  
   760                  inituart:
   761                  
   762   01F6 3E03      	mvi	a,3		; disable the receiver through register 3
   763   01F8 D306      	out	siocmd
   764   01FA 3EC0      	mvi	a,disrcv
   765   01FC D306      	out	siocmd
   766                  
   767   01FE DB06      	in	siostat		; is there a garbage byte?
   768   0200 E601      	ani	rrdymsk
   769   0202 CA0A02    	jz	initu		; no-->continue initialization
   770                  
   771   0205 DB04      	in	siorecv		; else-->eat the character
   772   0207 C3F601    	jmp	inituart	; try again
   773                  
   774                  initu:
   775                  
CP/M RMAC ASSEM 1.1	#017	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   776   020A 3E30      	mvi	a,errst		; reset error conditions
   777   020C D306      	out	siocmd
   778                  
   779   020E 3E03      	mvi	a,3		; re-enable the receiver
   780   0210 D306      	out	siocmd
   781   0212 3EC1      	mvi	a,enarcv
   782   0214 D306      	out	siocmd
   783                  
   784   0216 C9        	ret
   785                  
   786                  ; INITRECV:  Initialize a receive operation
   787                  
   788                  initrecv:
   789                  
   790   0217 CDF601    	call	inituart
   791                  
   792                  	if	interrupts
   793   021A CDC001    	call	enblrecv	; enable receiver interrupts
   794                  	endif
   795                  
   796   021D C9        	ret
   797                  
   798                  
   799                  ; WAIT - Wait 100 micro seconds
   800                  
   801                  wait:
   802                  
   803   021E 3E16      	mvi	a,timeval
   804                  
   805                  w:
   806                  
   807   0220 3D        	dcr	a		; 04
   808   0221 A7        	ana	a		; 04
   809   0222 C22002    	jnz	w		; 12
   810                  				; ---
   811   0225 C9        	ret			; 30 T-States total
   812                  
   813                  
   814                  ; RESTUART:  Reinitialize the UART to the way it was in the
   815                  ;	     original BIOS after completing the network operations
   816                  
   817                  
   818                  restuart:
   819   0226 C9        	ret			; UART not used except by network
   820                  
   821                  
   822                  ; CSNIOD:  Do any cold start initialization which is necessary.
   823                  ;	   Must at least return the value of BAUDS
   824                  ; 	   If the network uses the printer port then set theh carry flag
   825                  ;	   otherwise clear it.
   826                  
   827                  csniod: 
   828                  	
   829   0227 01032A    	lxi	b,bauds		; return the legal baud rates
CP/M RMAC ASSEM 1.1	#018	ULCNET DATA LINK LAYER MP/M XIOS MODULE

   830   022A B7        	ora	a		; not using a printer port
   831   022B C9        	ret
   832                  
   833   022C           	end
CP/M RMAC ASSEM 1.1	#019	ULCNET DATA LINK LAYER MP/M XIOS MODULE

BAUDGEN          0000    70#  514 
BAUDS            2A03    68#  829 
BAUDSH           002A    65#   68   530 
BAUDSL           0003    64#   68   491 
BAUDTBL          0014   141#  508 
BTBL             000C   138#  482 
CARBIT           0003    80#
CARMSK           0008    81#  686 
CHKSTAT          017C    39   610   632#
CONIN            0000    53   242 
CONOUT           0000    53   269 
CONST            0000    53   216 
CSDLL            0000    50   218 
CSNIOD           0227    40   827#
CURBAUD          000B   135#  473   517   555 
DEVOK            0036   184   189#
DEVTABLE         0005   126#  130   195 
DISRCV           00C0    95#  764 
DISSLF           004F    97#  504 
DLISR            0000    58   733 
DLLBAU           0003    42   120#
DLLON            0000    50   219 
DSBLRECV         01C9    45   709#
DSBLXMIT         015B    41   552#
DTROFF           006A    93#  499   561 
DTRON            00EA    92#  542   566 
ENARCV           00C1    94#  781 
ENASLF           000F    96#  537 
ENBLRECV         01C0    45   699#  793 
ERRBITS          0070    83#  638 
ERRST            0030    82#  670   776 
EXITDL           00CE   306   378#
FALSE            0000    27#
FBIT             0006    88#  661 
FMSK             0040    89#
FRMCNTR          0000    52   664 
GETRCODE         0000    49   395 
GETTCODE         0000    49   316 
INCCNTR          0000    52   647   656   665 
INITRECV         0217    39   788#
INITU            020A    37   769   774#
INITUART         01F6    38   760#  772   790 
INTERRUPTS       FFFF    29#   44    56   155   414   440   578   602   695   723 
                        792 
NCONIN           005C    35   238#
NCONOUT          0075    35   265#
NCONST           0043    35   212#
NETADR           0000    42   119#
NETCON           0020   106#  214   240   267 
NETIDLE          01AF    39   603   679#
NETSTATS         FFFF    30#  641 
NF               01A9   662   667#
NIISR            0000    58   731 
NO               019D   653   658#
CP/M RMAC ASSEM 1.1	#020	ULCNET DATA LINK LAYER MP/M XIOS MODULE

NP               0191   644   649#
NUMDEVICES       0002   130#  183 
NXTADD           0050   222#  230 
OBIT             0005    86#  652 
OMSK             0020    87#
OUTBAU           0133   510#  546 
OVRCNTR          0000    51   655 
PARCNTR          0000    51   646 
PBIT             0004    84#
PGM1             01E8   743#  749 
PGMUART          01D0    38   721#
PMSK             0010    85#  643 
POLDEV           0000    54   181 
POLLDEVICE       0028    36   177#
PSRECV           00C1   253   366#
PSXMIT           0088   278   297#
PSXRET           00B7   331   332   333   334   344#
RBAD             00F6   411   412   415   435#  445 
RCODE            000A   133#  374   431 
RECEIVE          0000    48   368 
RECV             0175    37   600#  608 
REGSHRT          0000    50   228 
REISR            0000    58   735 
RESTUART         0226    40   818#
RGOOD            00F0   410   428#
RIDLE            00FA   417   442#
RRDYBIT          0000    78#
RRDYMSK          0001    79#  607   768 
RRETRY           0064   247#  258 
RRTBL            00E2   399   410#
RSLEEP           00EE   420   421   423#  446 
RTMOCHK          0000    57   444 
RTO              0179   604   619#
RWAIT            00D3   129   393#
RWAKEUP          00F0   429#  438 
SCBAUDT          0016   146#  545 
SELFBIT          0003    90#
SELFCLKD         0140   488   528#
SELFMSK          0008    91#  487   556 
SETBAUD          0104    37   470#  690   753 
SIOCMD           0006    71#  498   500   503   505   536   538   541   543   560 
                        562   565   567   635   671   682   683   702   704   712 
                        713   746   763   765   777   780   782 
SIOIBLK          001C   156#  158#  161   739 
SIOILEN          000C   161#  740 
SIORECV          0004    74#  616   771 
SIOSTAT          0006    72#  583   606   636   685   767 
SIOV4            FF08   101#
SIOV5            FF0A   102#  732 
SIOV6            FF0C   103#  734 
SIOV7            FF0E   104#  736 
SIOXMIT          0004    73#  590 
SLFCLKD          FFFF    31#  485   496   525   554 
TCODE            0009   132#  305   351 
CP/M RMAC ASSEM 1.1	#021	ULCNET DATA LINK LAYER MP/M XIOS MODULE

TERRCNT          0000    51 
TIMEVAL          0016   122#  803 
TRANSMIT         0000    48   299 
TRETRY           007D   274#  283 
TRTBL            00A7   320   331#
TRUE             FFFF    26#   27    29    30    31 
TSLEEP           00B5   335   336   337   339#
TWAIT            0098   128   314#
TWAKEUP          00BB   346   349#
ULCRX            0021   112#  371 
ULCTX            0020   111#  180   302 
W                0220   805#  809 
WAIT             021E    40   520   801#
XDOS             0000    53   303   372 
XMIT             0172    37   576#
XRDYBIT          0002    76#
XRDYMSK          0004    77#  584 


Appendix G
Using CP/NET 1.2 with CORVUS OMNINET

Corvus OMNINET is an inexpensive, high-performance CSMA/CA networking system supporting up to 63 hosts on a one-megabit-per second, twisted-pair cable. OMNINET host interface adaptors are intelligent coprocessors that deal with all aspects of network communication of the host in which they are installed, up to and including the transport layer of the ISO open system model. The sample SNIOS and NETWRKIF files following this discussion show one way to use Corvus engineering transporters to implement a CP/NET system.

G.1 The Corvus Engineering Transporter

The Corvus engineering transporter is a card for evaluating Corvus OMNINET with minimum modification to an existing Z80 system. The transporter is not an end-user product, but it is similar enough in hardware design to most production systems using OMNINET to work with little modification.

General information about the Corvus transporter is presented here to help you understand the operation of the sample codes at the end of this appendix. For more information, refer to Corvus documentation.

Communication with the transporter hardware is simplified by the fact that the transporter is microprocessor-based and uses autonomous DMA to access its host computer's memory directly. All communication between host and transporter is controlled by well organized data structures existing in host memory. The only port I/O the host ever does is the transmission, to the transporter hardware, of 24-bit pointer objects (as three serial bytes, most significant byte first) via an output port. Note that all Corvus multibyte objects are in most significant byte first order. These pointer objects refer to transporter command blocks, described in Table G-1.

Table G-1. Transporter Command Block
FieldSizeExplanation
OPERATION COMMAND CODE 8 bits sends a message.
RESULT BLOCK POINTER 24 bits gives the address of a data structure for the transporter to update with completion information.
SOCKET CODE 8 bits defines which of the 4 virtual communication channels to use for this operation.
DATA BUFFER POINTER 24 bits gives the address of a message buffer for this operation.
DATA LENGTH FIELD 16 bits gives the length of the message to be transmitted or maximum message length accepted, if this is a receive operation. The maximum length allowed for a single message packet is 2048.
CONTROL FIELD LENGTH 8 bits gives the length of an independent auxiliary message that can be sent to a special CONTROL buffer in the destination host at an address different from that of the destination message buffer. In the case of a receive command, this field specifies the largest such CONTROL message acceptable.
DESTINATION HOST 8 bits specifies network address of the target host. Legal network addresses are 0-63, or 255 for broadcast messages. A host's address is set by switches connected to the transporter hardware.

Not all fields are used by all commands, but the syntax of the command block is usually consistent, except in the case of special diagnostic commands.

The result pointer in the command block must contain the address of a large enough data structure in host memory to accept the completion information that the specified command produces. Note that the result block is associated with the operation the command block describes. If more than one operation is posted to the transporter hardware, each must have its own result block available. Table G-2 describes a typical result block.

Table G-2. Receive Result Block
FieldSizeExplanation
OPERATION STATUS CODE 8 bits set to 254 by the transporter processor once it has read and accepted the command block. This field is later set by the transporter to a result code when it has completed the requested operation.
SOURCE HOST NUMBER 8 bits gives the network address of the node from which this message packet came.
ACTUAL DATA LENGTH 16 bits gives the actual length of the message in the receive buffer.
CONTROL MESSAGE BUFFER 0-255 bytes a buffer large enough to accept any CONTROL message transmitted with the main message packet. The command block that points to this result block must allow such messages.

Up to four simultaneous receive operations can be in progress at any one time, waiting for messages for the four logical sockets in the host. Only one message can be posted for transmission at any one time, but this can be done even while four receive operations are pending. Messages from one node are only acceptable to another node if it has a receive command outstanding specifying the socket to which the message is directed.

In use, the host processor must build a command block, then post it to the transporter hardware by outputting one byte at a time of its 24-bit address to the transporter via an output port. The transporter uses an input ready status bit to synchronize this transfer. Command pointers can be transfers done at any time except while the transporter is processing a command block to transmit a message. That operation ties up the transporter until the message has been delivered, or the transporter has given up trying. Network latency is low, so the transporter is unavailable only briefly.

Once the transporter has read and accepted a command, it sets the operation status code in the result block to 254. It is advisable for the host to preset this byte to 255 before sending the transporter the pointer, so that the transporter can confirm that the command was accepted by checking for the change.

The host then polls all active result blocks, waiting for any operation status code to change to a value other than 0FEh. This change means the transporter has completed the operation associated with that result block, and data and result information are available. To simplify interpretation of results, all error codes are between 80h and 0FEh, and all success codes are less than 80h. Send and receive calls that succeed give the number of retries as a completion code, but this code is always less than 7Fh.

OMNINET transporter interfaces usually support generation of a host interrupt whenever the transporter writes to a result block. This relieves the host of having to poll result blocks for completion. To simplify OMNINET evaluation, the engineering transporter is not usually configured to use interrupts. The sample programs demonstrate the use of the transporter both without interrupts and with external interrupt hardware. Servers usually need interrupt hardware or an XIOS polling routine to achieve a usable throughput, but the sample drivers can be made to run without either if high throughput is not a goal.

The coprocessor interface structure the transporter uses is close to the ideal model of a perfect transport layer. The transporter hardware deals with all retries, message acknowledgments, packet sequencing checking, and error detection totally transparently to the host it serves. The data-structure based message interface between the host and transport layer is useful even in implementing non-OMNINET interrupt-driven transport layers for CP/NET.

G.2 Implementation Structure

In the sample implementation, very few OMNINET features were needed. All CP/NET traffic is on one logical channel (SOCKET 2), leaving the others free for such non-CP/NET uses as providing bootstrap channels between diskless devices and optional processes to load them, providing non-CP/NET peripheral sharing routines or even supporting a second network operating system in concurrent use.

Because CP/NET processes its own control fields (message headers), the control message options are not used and are set to zero. In the evaluation transporter, the most significant byte of the memory address is not used and is always set to zero. Other hardware implementations can use this byte for segment control to allow the message buffers to be banked out, or for a 16-bit processor.

The network node ID of an OMNINET host is set by six switches on its transporter hardware. In this implementation, the NODE number is the CP/NET network ID. Set the ID of the SERVER to 00. A requester can have any other unique OMNINET ID code except 0FF hex. This ID code freedom is achieved by a routine in the NETWRKIF module that binds requester ID codes dynamically to processes in the SERVER.RSP module by tracking login and logoff messages. Hence, up to 63 requesters can be supported, as long as no more than NSLAVES are logged in at any one time. Because the transporter handles all low-level communication concerns, the NETWRKIF module is relatively compact; and 16 requesters are easily supported in most systems.

To simplify coding the interface modules, data structure constructor macros eliminate the need for typing all the definitions again and again for each requester. This technique requires that the indices into the resulting arrays of data structures be computed at run-time, but this is easy to do and, where possible, is part of initialization.

G.3 The SNIOS Implementation

The intelligent nature of the OMNINET interface makes coding the SNIOS a simple exercise. Allocate a set of prefabricated transporter command blocks and associated result blocks. Even though the requester never has more than one operation pending at a time, it is simpler to use separate command blocks for each needed operation type than to recycle the same command block.

Unfortunately, relocating 8080 assemblers like RMAC do not easily deal with relocation of multibyte pointers that are not in Intel® standard memory order. It is simplest to set the result block pointers at initialization; that approach is used here.

After setting up these pointers, the NTWRKINIT routine posts a prebuilt transporter command block called INITTCB to the transporter via the routine called OMNI$STROBE. If the transporter does not accept the pointer, initialization aborts and an error returns to the NDOS. If the transporter accepts the pointer, NTWRKINIT calls OMNI$WFDONE to poll the result block associated with INITTCB until the transporter reports a completion. If the initialization operation succeeds, the node number presently set into the transporter's switches is found as a result code. If initialization fails, a value > 80h corresponding to an error code is found and returned to NTWRKINIT, and NTWRKINIT aborts and returns an error code to the NDOS. Otherwise, the node number returned is installed in configtbl and the default message buffer's SID field, the requester ID and a banner print on the console, and a success code is returned to the NDOS.

The NTWRKERROR entry is functionally identical to NTWRKINIT except that it does not print a banner or requester ID code.

The NTWRKSTS, CNFGTBLADR, and NTWRKWBOOT routines are identical in function and operation to those used with other transport layers.

When the NDOS calls the SENDMSG routine, the BC register pair contains a pointer to the message to be sent on the network. This routine translates the CP/NET header information of that message into a form consistent with OMNINET and then puts it into a prefabricated transporter command block called TXTCB. The CP/NET DID is used as the target node physical address on the network. The address of the whole message, including the CP/NET header, is placed in the buffer field of TXTCB after the pointer is rearranged into MSB, LSB sequence. The CP/NET SIZ field is adjusted to give the total message length, including the CP/NET header, and is placed in the appropriate field of the TXTCB.

The OMNINET interface primitives OMNI$STROBE and OMNI$WFDONE again post the command to the transporter and, if successful, await completion of the transmission operation. The completion code is transformed into a flag the NDOS expects. Because a very busy server might not have a buffer posted when the requester sends the message, even though 'the transporter does multiple retries by itself, a retry loop tries to send the message again, if necessary. In practice, retries are rare, but the retry loop is useful when debugging a server.

Like SENDMSG, the RECEIVEMSG routine is primarily an exercise in the translation of parameters and their transmission to the transporter. The operation of RECEIVEMSG is easily understood by reading its code, with one exception; if a receive is posted, and no message ever comes in, the transporter waits forever for a message. To simplify debugging and recovery from network errors, the OMNI$WFDONE routine times out after about 20 seconds (on a 2 mhz processor) and returns an error flag to its caller. Most servers ordinarily respond in this time, so the RECEIVEMSG routine issues a cancel receive command to the transporter via a prefabricated command block called UNRXTCB. RECEIVEMSG then returns to the NDOS with an error code.

If the receive call is not cancelled, an unsolicited or late message might be written into host memory at the requested address long after the host is using that memory for something else. Most autonomous transport layers support this kind of cancellation.

The implementation here is less than 280h bytes long, including the default 138-byte message buffer. If space is tight, the message printing and banner routines can be placed in the default buffer, a single transporter command block and result block can be recycled for all commands, and concessions to modularity can be made to yield an even smaller SNIOS.

G.4 The NETWRKIF Implementation Model

This sample OMNINET NETWRKIF uses a slightly different intermodule communication model from the one usually used to implement a serial asynchronous star network. Instead of using one process per server process to implement the network input and output, a single input process and a single output process route all messages. This type of structure is far more efficient for any party-line type of network interface hardware because fewer dispatches occur per transaction. Those transactions that do occur take less time and far less code is required to implement the NETWRKIF. In addition, the structure is easier to understand and debug, and all traffic converges through one piece of code, allowing you to implement message routing extensions to your network.

This model is easily understood by studying the general function of the network receiver and transmitter process separately.

The network receiver process in this version is named SERVERX. It is responsible for collecting each incoming message as it arrives, identifying the server process it is for, and writing a pointer to the message into that process's input queue. In addition, SERVERX functions as a surrogate server process to advise requesters that are not logged in that they have no server process to use.

SERVERX uses run-time binding of requester ID codes to server processes. SERVERX does this by keeping a table of the input queue addresses of all the server processes it supports and the ID code of the requester currently logged in to each process. SERVERX examines each incoming messages SID field and searches the table to find out whether SID is presently associated with a server process. If not, an error reply message is constructed in the same buffer that the message arrived in, and SERVERX writes this message directly to the network output process for transmission back to the requester.

For this process to function properly, SERVERX must track all login and logoff messages that pass through it. Every time a login message is received, SERVERX checks its mapping table to find out whether that requester is currently associated with a server process. If it is, no action is taken. If not, SERVERX tries to find an idle server entry in the table. Idle entries are shown in this table as in use by requester 255. If a free server entry is located, SERVERX enters the requester's ID into it, and then sends the login message to that server process's input queue. If none are available, an error reply message is constructed by SERVERX and sent back to the requester.

Logoff messages are handled by finding that requester's server entry, marking it as empty (255) , and then routing the logoff message to the server's input queue. If that requester was never logged in in the first place, SERVERX sends it an error, as previously explained.

Because there is no way to know which server process an incoming message will be for at the time a buffer is posted to the transporter for a receive call, buffers are not permanently assigned to particular server processes. Instead, a list of empty buffers is kept in an MP/M II queue, and SERVERX obtains the buffers from the queue as needed and available for posting to the transporter.

The OMNINET primitives are similar to those used by the SNIOS, except that an MX queue ensures that the transporter is not in use by another process when SERVERX wants to post a command block pointer to it.

As the arrival time of the next message is unknown, SERVERX must be suspended while it waits for the next message to arrive. This can be done by an XDOS flag wait in the WF$RXDONE OMNINET primitive or by delay-based polling. If your XIOS can be easily modified, another alternative is to add an XIOS polling routine. Using the delay call to suspend the process drastically reduces network throughput because only 60 incoming messages can arrive per second.

The SERVETX process is extremely simple. It reads messages from a single input queue and posts them, using mutual exclusion, to the transporter. Because messages are quickly disposed of by the network, there is no point in suspending SERVETX. It uses a different completion routine than SERVERX, which merely waits until a completion code is received from the transporter, and then returns to its caller. To simplify debugging, a timeout is included to prevent a hardware or software problem from locking up the system.

Once SERVETX has finished sending the message, it returns the buffer that it was in to the free buffer management queue, making it available for SERVERX. SERVETX then goes back to read its input queue to wait for another message to process.

Theoretically, such a system can function with fewer buffers than server processes. But in practice, it is best to have at least one more buffer than the number of server processes in the pool to deal with messages such as failed login attempts that never get routed to a server.

The rest of the code in each process simply initializes data structures, creates queues, initializes hardware, and performs other routine tasks.

Note that the distribution version of CP/NET 1.2 does not work with this SERVETX process without a minor patch. SERVER.RSP must be patched to create output UQCBs with the same name for all server processes instead of making each queue name unique. Once this is done, all processes in SERVER.RSP direct their output to a single SERVETX process. Instructions for installing this patch are included in CP/NET V1.2 Application Note 02.

G.5 Possible Improvements to NETWRKIF

This interface is by no means ideal. Little error recovery is done for registers that fail to log off. A watchdog timing process can be easily added to correct this problem. This process is not shown here, to simplify understanding of the OMNINET interface. But such a process is only needed in systems with more physical requesters than server processes to prevent their being locked up by departed users.

One possible improvement is to further reduce the number of dispatches per CP/NET transaction by using direct code to manage the buffer list and using the transporter mutual exclusion function instead of the MP/M II queue facility. The M/PM II queue facility is powerful and easy to use, but avoid using it in situations where dispatch overhead exceeds the time for which a process is likely to require suspension unless the suspension is unavoidable for process synchronization reasons.

Another worthwhile improvement is to modify the NETWRKIF to minimize the period during which the server cannot respond to incoming messages, by seeing that the next buffer is more quickly posted for the next received message after a receive completion occurs. The present version does not do this until the incoming message has been processed by SERVERX. This causes unneeded network traffic because messages sent by requesters during this time are futile.

High-performance servers can make good use of two physical sets of transporter hardware, with different node addresses, on the same loop. Using two transporters can totally bypass the need to use MX techniques because one transporter can be reserved solely for transmitting messages.

Interesting networks can be easily constructed by having more than one OMNINET loop, each with its own transporter. The SERVERX process associated with each loop can filter messages not intended for local SLVSPs to a second, third, or fourth SERVETX process associated with higher level loops. Such filtering bridges can be used to build hierarchical CP/NET systems of any degree of complexity.

Other processes can concurrently send and receive messages totally unrelated to the CP/NET context using the same transporter as long as they honor the MXomni mutual exclusion queues and do not use the same socket for their communication as CP/NET. These processes can implement a variety of supervisory and auxiliary functions, or they can implement additional concurrent virtual circuits that cooperating requesters can use for point-to-point traffic. Such point-to-point virtual circuits can be coordinated by CP/NET mail functions.

Listing G-1. Sample Slave Network I/O System for Corvus OMNINET
CP/M RMAC ASSEM 1.1	#001	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

     1                    title	'Sample Slave Network I/O System for CORVUS OMNINET 20 Oct 82'
     2                  	page	54
     3                  
     4                  ;________________________________________________________________________
     5                  ;________________________________________________________________________
     6                  ;									;
     7                  ;	SAMPLE SLAVE NETWORK IO SYSTEM FOR CP/NET 1.2			;
     8                  ;	VERSION FOR CORVUS OMNINET "ENGINEERING" TRANSPORTER		;
     9                  ;		(Requires RMAC for assembly)				;
    10                  ;									;
    11                  ;	COPYRIGHT (C) 1982 by	VANO ASSOCIATES, INC.			;
    12                  ;				P.O. BOX 12730				;
    13                  ;				New Brighton, MN  55112			;
    14                  ;				U.S.A.					;
    15                  ;				(612) 631-1245				;
    16                  ;				ALL RIGHTS RESERVED			;
    17                  ;									;
    18                  ;	ANY USE OF THIS CODE without the imbedded copyright notice and	;
    19                  ;	banner is hereby strictly prohibited.				;
    20                  ;									;
    21                  ;	Permission is hereby granted to Digital Research Inc. to use	;
    22                  ;	this source file for educational and illustrative purposes in 	;
    23                  ;	conjunction with CP/Net 80 documentation.  Any other use of 	;
    24                  ;	this code without the EXPRESS WRITTEN PERMISSION of VANO	;
    25                  ;	ASSOCIATES INC. is hereby strictly prohibited.			;
    26                  ;									;
    27                  ;	This file is provided courtesy of:				;
    28                  ;									;
    29                  ;			R2E (Realisations Etude Electroniques)		;
    30                  ;			Z.A.I. de Courtaboeuf				;
    31                  ;			BP 73	91942 Les Ulis				;
    32                  ;			FRANCE						;
    33                  ;									;
    34                  ;	who sponsored the development of one of its ancestors.		;
    35                  ;________________________________________________________________________
    36                  ;________________________________________________________________________
    37                  
    38                  ;	*****	CONSTANT DECLARATIONS	*****
    39                  
    40   0000 =         FALSE	equ	0
    41   FFFF =         TRUE	equ	not FALSE
    42                  
    43                  ;	configuration and option constants
    44   0064 =         TXTRIES equ	100		;Transmit message retries
    45   008A =         BUFFSIZE equ	138		;max default buffer size
    46   0200 =         MAXMSG	equ	512		;largest message accepted by receiver
    47   0080 =         SKT0	equ	80h		;legal omninet socket tokens
    48   0090 =           SKT1	equ	90h
    49   00A0 =           SKT2	equ	0a0h
    50   00B0 =           SKT3	equ	0b0h
    51   00A0 =         SOCKET	equ	SKT2		;this SNIOS uses only channel 2
    52                  
    53                  ;	OMININET Constants
    54                  ;  Completion/return codes
CP/M RMAC ASSEM 1.1	#002	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

    55   0000 =         NOERR	equ	0		;done (no errors or retries)
    56   00C0 =         ETXOK	equ	0c0h		;echo succeeded with no retries (not used here)
    57   0080 =         ETXFAIL	equ	80h		;Transmit failed
    58   0081 =         E2LONG	equ	81h		;wouldn't fit in destination socket
    59   0082 =         ENOSKT	equ	82h		;destination socket not set up
    60   0083 =         EBDCTL	equ	83h		;bad control field length
    61   0084 =         EBDSKT	equ	84h		;illegal socket number
    62   0085 =         EBDDES	equ	85h		;invalid destination node number/socket in use
    63   0086 =         EBDNODE	equ	86h		;bad node number in command (not 0-7fh or ffh)
    64   00FE =         ECMDOK	equ	0feh		;command has been read by transporter
    65                  ; legal command tokens
    66   0040 =         SENDF	equ	40h		;send message
    67   00F0 =         RCVF	equ	0f0h		;set up receive socket
    68   0010 =         ENDRCVF	equ	10h		;stop receive
    69   0020 =         INITF	equ	20h		;initialize transporter
    70                  ; Transporter control ports
    71   00F8 =         NETBASE	equ	0f8h		;base address of transporter IO interface
    72   00F9 =           TSTAT	equ	Netbase+1	;ready status port
    73   0010 =           TCRDY	equ	10h		;status mask for ready bit
    74   00F8 =           TDATA	equ	Netbase		;command block pointer port
    75                  
    76                  ;	Network Status Byte Constants
    77                  ;
    78   0010 =         ACTIVE		equ	10h	;slave logged in on network
    79   0002 =         RCVERR		equ	2h	;error in received message
    80   0001 =         SENDERR		equ	1h	;unable to send message
    81                  
    82                  ;	CP/M BDOS function constants
    83   0005 =         BDOS	equ	5		;absolute BDOS entry
    84   0009 =         PRINTF	equ	9		;print message function
    85   0002 =         CONOUTF	equ	2		;output char in E to console
    86                  
    87                  ;	General Constants
    88   000A =         LF	equ	0ah		;Line Feed
    89   000D =         CR	equ	0dh		;Carriage Return
    90                  
    91                  ;	*****	GENERATED CODE AND DATA BEGIN HERE	*****
    92                  
    93                  ;	Public Jump vector for SNIOS entry points
    94   0000 C3F400    	jmp	ntwrkinit	;network initialization
    95   0003 C34801    	jmp	ntwrksts	;network status
    96   0006 C35201    	jmp	cnfgtbladr	;return config table addr
    97   0009 C36701    	jmp	sendmsg		;send message on network
    98   000C C3A601    	jmp	receivemsg	;receive message from network
    99   000F C33801    	jmp	ntwrkerror	;network error
   100   0012 C35601    	jmp	ntwrkwboot	;network warm boot
   101                  
   102                  ;	Public Slave Configuration Table
   103                  configtbl:
   104                  Network$status:
   105   0015 00        	db	0				;network status byte
   106   0016 00        slvid1:	db	0				;slave ID (from switches)
   107   0017 0000000000	db	0,0,	0,0,	0,0,	0,0	;Disk map table for units A:-P:
   108   001F 0000000000	  db	0,0,	0,0,	0,0,	0,0
CP/M RMAC ASSEM 1.1	#003	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

   109   0027 0000000000	  db	0,0,	0,0,	0,0,	0,0
   110   002F 0000000000	  db	0,0,	0,0,	0,0,	0,0
   111   0037 0000      	db	0,0				;console device
   112   0039 0000      	db	0,0				;list device
   113   003B 00        	db	0				;buffer index
   114                  ;
   115   003C 00        dflt:	db	0				;FMT (DEFAULT MESSAGE BUFFER)
   116   003D 00        	  db	0				;DID
   117   003E 00        slvid2:	  db	0				;SID
   118   003F 05        	  db	5				;FNC
   119   0040 00        	  db	0				;SIZ
   120   0041           	  ds	1				;MSG(0)  List number
   121   0042           	  ds	BUFFSIZE			;MSG(1) ... MSG(128)
   122                  
   123                  
   124                  ;	*****	PREFABRICATED OMNINET TRANSPORTER COMMAND BLOCKS   *****
   125                  
   126                  ;	Command block for transmitting a message
   127                  TXtcb:
   128   00CC 40         TXtcmd:	db	SENDF		;command field
   129   00CD 00        		db	0		;bits 16-24 of result block ptr
   130   00CE 0000       TXtrslt:	db	0,0		;result block pointer (MSB,LSB)
   131   00D0 A0         TXtskt:	db	SOCKET		;socket (channel)  number
   132   00D1 00        		db	0		;bits  16-24 of message buffer ptr
   133   00D2 0000       TXtmsg:	db	0,0		;message buffer pointer (MSB,LSB)
   134   00D4 0000       TXtdlen:	db	0,0		;data field length (MSB,LSB)
   135   00D6 00         TXtclen:	db	0		;control field length
   136   00D7 00         TXtdest:	db	0		;Destination address (transport layers)
   137                  ;	Result vector for above command block
   138                  TXresult:
   139   00D8 00         TXrcode:	db	0		;return code
   140                  
   141                  ;	Command block for setting up a receive operation
   142                  RXtcb:
   143   00D9 F0         RXtcmd:	db	RCVF		;command field
   144   00DA 00        		db	0
   145   00DB 0000       RXtrslt:	db	0,0		;result block pointer (MSB,LSB)
   146   00DD A0         RXtskt:	db	SOCKET		;socket number
   147   00DE 00        		db	0
   148   00DF 0000       RXtmsg:	db	0,0		;message address (MSB,LSB)
   149   00E1 02         RXtdlen:	db	MAXMSG/256	;max data field length (MSB,LSB)
   150   00E2 00        		db	MAXMSG and 255
   151   00E3 00         RXtclen:	db	0		;max control field length
   152   00E4 00         RXtdest:	db	0		;(not used in a receive operation)
   153                  ;	Result vector for receiver
   154                  RXresult:
   155   00E5 00         RXrcode:	db	0		;return code
   156   00E6 00         RXrsrce:	db	0		;source HOST #
   157   00E7 0000       RXrdlen:	db	0,0		;received message length (MSB,LSB)
   158                  
   159                  ;	Command block for receive cancel operation
   160                  UNRXtcb:
   161   00E9 10         UNRXtcmd:	db	ENDRCVF		;command field
   162   00EA 00        		db	0
CP/M RMAC ASSEM 1.1	#004	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

   163   00EB 0000       UNRXtrslt:	db	0,0		;result block pointer (MSB,LSB)
   164   00ED A0         UNRXtskt:	db	SOCKET		;socket number
   165                  ;	Result vector for receive cancel
   166                  UNRXresult:
   167   00EE 00         UNRXrcode:	db	0		;return code
   168                  
   169                  ;	Command block for transporter initialization command
   170                  INITtcb:
   171   00EF 20         INITtcmd:	db	INITF		;command field
   172   00F0 00        		db	0
   173   00F1 0000       INITtrslt:	db	0,0		;result block pointer (MSB,LSB)
   174                  ;	Result vector for initialization
   175                  INITresult:
   176   00F3 00         INITrcode:	db	0		;return code (if valid,=ID code)
   177                  
   178                  
   179                  ;	*****	PUBLIC CODE ENTRIES BEGIN HERE   *****
   180                  
   181                  ;	Externally accessed routine to initialize transporter
   182                  ;		(RETURNS A=0 if succeeds, else 0ffh.)
   183                  ntwrkinit:
   184   00F4 CD3801    	call	ntwrkerror		;init transporter, tcbs and id code
   185   00F7 D8        	rc				;return error if init fails
   186   00F8 110601    	lxi	d,initmsg		;else prinw slave ID and banner
   187   00FB CDF001    	call	print$msg
   188   00FE 3A1600    	lda	slvid1
   189   0101 CDD601    	call	prhex			;print slave ID
   190   0104 AF        	xra	a			;and return to caller with a=0
   191   0105 C9        	ret
   192                  
   193                  initmsg:
   194   0106 0D0A534E49	db	CR,LF,'SNIOS (c)1982 Vano Associates Inc.'
   195   012A 0D0A534C41	db	CR,LF,'SLAVE ID = $'
   196                  
   197                  
   198                  ;	Externally accessed routine inits or re-inits module
   199                  ;		(RETURNS A=0 if succeeds, else 0ffh.)
   200                  ntwrkerror:
   201   0138 AF        	xra	a
   202   0139 321500    	sta	Network$status		;zero network status byte
   203   013C CDF501    	call	omni$init		;init transporter, tcbs and id code
   204   013F D8        	rc				;carry means error, A=0ffh
   205   0140 321600    	sta	slvid1			;update this slaves id in table
   206   0143 323E00    	sta	slvid2			;and default message
   207   0146 AF        	xra	a			;and return with no error
   208   0147 C9        	ret
   209                  
   210                  
   211                  ;	Externally accessed routine returns Network Status Byte in A
   212                  ;		(also clears any error bits active)
   213                  ntwrksts:
   214   0148 211500    	lxi	h,network$status
   215   014B 46        	mov	b,m
   216   014C 3EFC      	mvi	a,not(RCVERR or SENDERR)
CP/M RMAC ASSEM 1.1	#005	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

   217   014E A0        	ana	b
   218   014F 77        	mov	m,a
   219   0150 78        	mov	a,b
   220   0151 C9        	ret
   221                  
   222                  
   223                  ;	Externally accessed routine Returns Configuration Table Ptr in HL
   224                  cnfgtbladr:
   225   0152 211500    	lxi	h,configtbl
   226   0155 C9        	ret
   227                  
   228                  
   229                  ;	Externally accessed routine is called each time the CCP is reloaded
   230                  ;	from disk.  (Dummy procedure for now.)
   231                  ntwrkwboot:
   232   0156 115C01    	lxi	d,wboot$msg		;return via print$msg
   233   0159 C3F001    	jmp	print$msg
   234                  
   235                  wboot$msg:
   236   015C 0D0A3C4350	db	CR,LF,'$'
   237                  
   238                  
   239                  ;	Externally accessed routine sends Message BC--> on Network
   240                  ;		(returns A=0 if succeeds, else A=0ffh.)
   241                  ;
   242                  ;	NOTE that although the OMNINET transporter does its own transport
   243                  ;	  layer retries, this routine does additional retries to deal with
   244                  ;	  servers that are slow in posting receive calls since transport
   245                  ;	  level retries are exhausted in a very short real-time period.
   246                  sendmsg:
   247   0167 61        	mov	h,c		;move buffer pointer to Transporter ctrl block
   248   0168 68        	mov	l,b		;(note reversed byte order for Transporter.)
   249   0169 22D200    	shld	TXtmsg
   250                  ;
   251   016C 210400    	lxi	h,4		;get CP/Net message length from SIZ field
   252   016F 09        	dad	b
   253   0170 6E        	mov	l,m
   254   0171 2600      	mvi	h,0
   255   0173 110600    	lxi	d,6		;add packet header lgth to get actual size
   256   0176 19        	dad	d		;  of packet for transport layer purposes
   257   0177 7C        	mov	a,h		;swap bytes to MSB, LSB order
   258   0178 65        	mov	h,l
   259   0179 6F        	mov	l,a
   260   017A 22D400    	shld	TXtdlen		;store length in TCB data length field
   261                  ;
   262   017D 03        	inx	b		;get DID from message
   263   017E 0A        	ldax	b
   264   017F 32D700    	sta	TXtdest		;put it into TCB destination address field
   265                  ;
   266   0182 116400    	lxi	d,TXTRIES	;use DE as retry counter
   267                  ;
   268                  send$again:			;head of message transmission retry loop
   269   0185 D5        	push	d
   270   0186 01CC00    	lxi	b,TXtcb		;send TCB pointer to transporter hardware
CP/M RMAC ASSEM 1.1	#006	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

   271   0189 CD2E02    	call	omni$strobe
   272   018C D1        	pop	d
   273   018D DAA101    	jc	snderr		;if not accepted, goto fatal error handler
   274                  ;
   275   0190 01D800    	lxi	b,TXresult	;else poll result block until completion code
   276   0193 D5        	push	d		;is returned by hardware
   277   0194 CD5C02    	call	omni$wfdone
   278   0197 D1        	pop	d
   279                  ;
   280   0198 E680      	ani	80h		;completion codes 80h-ffh are error codes
   281   019A C8        	rz			;return 00 to caller if no errors
   282                  ;
   283   019B 1B        	dcx	d		;else decrement retry counter
   284   019C 7B        	mov	a,e
   285   019D B2        	ora	d
   286   019E C28501    	jnz	send$again	;retry transmit if any retries left
   287                  ;
   288   01A1 3E01      snderr:	mvi	a,SENDERR	;goto common exit code to update error flags
   289   01A3 C3CE01    	jmp	nerr		;(part of receivemsg routine)
   290                  
   291                  
   292                  ;	Externally accessed routine waits for a message directed to this node
   293                  ;	and returns it in the buffer BC-->.  To aid debugging, a timeout of
   294                  ;	about 20 seconds (2 Mhz processor) is implemented that will return an
   295                  ;	error if no message is received.  That is long enough for most normal
   296                  ;	servers to respond.
   297                  ;
   298                  ;	(RETURNS A=0 if good msg, =0ffh if  bad msg or timeout.)
   299                  receivemsg:
   300   01A6 68        	mov	l,b		;swap buffer pointer bytes to MSB,LSB order
   301   01A7 61        	mov	h,c
   302   01A8 22DF00    	shld	RXtmsg		;put buffer ptr to its TCB field
   303                  ;
   304   01AB 01D900    	lxi	b,RXtcb
   305   01AE CD2E02    	call	omni$strobe	;post control block address to hardware
   306   01B1 DACC01    	jc	rxerr		;fatal error if hardware won't accept it
   307                  ;
   308   01B4 01E500    	lxi	b,RXresult
   309   01B7 CD5C02    	call	omni$wfdone	;else wait for a completion from hardware
   310   01BA E680      	ani	80h
   311   01BC C8        	rz			;return 00 to caller if no error reported
   312                  ;  the rest is the fatal error handler for receive calls
   313   01BD 01E900    	lxi	b,UNRXtcb	;otherwise cancel the receive call
   314   01C0 CD2E02    	call	omni$strobe	; (using prefabricated cancel command block)
   315   01C3 D2CC01    	jnc	rxerr		;If won't accept this command either, quit here
   316                  ;
   317   01C6 01EE00    	lxi	b,UNRXresult	;else wait for completion of cancel command
   318   01C9 CD5C02    	call	omni$wfdone	;ignore result (always fatal error return)
   319   01CC 3E02      rxerr:	mvi	a,RCVERR	;exit via code that updates status byte
   320                  
   321                  ;	This is also used by sendmsg to update Network$status and return 0ffh
   322   01CE 211500    nerr:	lxi	h,Network$status
   323   01D1 B6        	ora	m
   324   01D2 77        	mov	m,a		;update status
CP/M RMAC ASSEM 1.1	#007	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

   325   01D3 3EFF      	mvi	a,0ffh
   326   01D5 C9        	ret			;return 0ffh to caller
   327                  
   328                  
   329                  ;	*****   UTILITY ROUTINES CALLED BY ABOVE BEGIN HERE  *****
   330                  
   331                  ;	prints A in hex on console
   332   01D6 F5        prhex:	push	psw
   333   01D7 07        	rlc
   334   01D8 07        	rlc
   335   01D9 07        	rlc
   336   01DA 07        	rlc
   337   01DB CDDF01    	call	nibl		;print high nibble
   338   01DE F1        	pop	psw		;and fall through to  print low nibble
   339                  
   340   01DF E60F      nibl:	ani	0fh
   341   01E1 C630      	adi	'0'
   342   01E3 FE3A      	cpi	'9'+1
   343   01E5 DAEA01    	jc	printa
   344   01E8 C607      	adi	7
   345   01EA 5F        printa:	mov	e,a
   346   01EB 0E02      	mvi	c,CONOUTF
   347   01ED C30500    	jmp 	BDOS		;print ascii and return
   348                  
   349                  
   350                  ;	print message DE--> until $ on console device
   351                  print$msg:
   352   01F0 0E09      	mvi	c,PRINTF		;prints $ delimited string DE-->
   353   01F2 C30500    	jmp	BDOS			;bdos(printf,wboot$msg)
   354                  
   355                  
   356                  ;	*****	LOW LEVEL OMNINET TRANSPORTER DRIVERS BEGIN HERE   *****
   357                  
   358                  ;	Initialize transporter and return its ID code in A or 0ffh if can't.
   359                  ;	Carry is also set if error, clear if no error.
   360                  omni$init:			;initialize pointers in our control blocks
   361   01F5 11D800    	lxi	d,TXresult	;NOTE: this is done at run time to avoid
   362   01F8 63        	mov	h,e		;  relocation problems caused by the need to
   363   01F9 6A        	mov	l,d		;  have pointers for CORVUS transporter use
   364   01FA 22CE00    	shld	TXtrslt		;  in MSB, LSB form instead of 8080 format.
   365                  ;
   366   01FD 11E500    	lxi	d,RXresult
   367   0200 63        	mov	h,e
   368   0201 6A        	mov	l,d
   369   0202 22DB00    	shld	RXtrslt
   370                  ;
   371   0205 11EE00    	lxi	d,UNRXresult
   372   0208 63        	mov	h,e
   373   0209 6A        	mov	l,d
   374   020A 22EB00    	shld	UNRXtrslt
   375                  ;
   376   020D 11F300    	lxi	d,INITresult
   377   0210 63        	mov	h,e
   378   0211 6A        	mov	l,d
CP/M RMAC ASSEM 1.1	#008	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

   379   0212 22F100    	shld	INITtrslt
   380                  ;
   381   0215 01EF00    	lxi	b,INITtcb	;send init command block pointer to transporter
   382   0218 CD2E02    	call	omnistrobe	;to reset it and get its ID code
   383   021B 9F        	sbb	a		;in case of error, preset return code 0 or ff
   384   021C D8        	rc			;fatal error if hardware won't accept pointer
   385                  ;
   386   021D 01F300    	lxi	b,INITresult	;else wait for result of operation
   387   0220 CD5C02    	call	omni$wfdone	;wait for done
   388   0223 321600    	sta	slvid1		;result code should be ID code so put in table
   389   0226 323E00    	sta	slvid2		;and in default message SID
   390                  ;
   391   0229 07        	rlc			;set CY=bit 7 of return code
   392   022A 1F        	rar			;so CY=1 if error
   393   022B D0        	rnc			;return with ID code if no error
   394   022C 9F        	sbb	a		;else set carry=1 and A=0ffh and return
   395   022D C9        	ret
   396                  
   397                  
   398                  ;	Sends the 16 bit POINTER in BC to the transporter hardware as
   399                  ;	a 24 bit pointer (MSB first).  Returns CY set if hardware will
   400                  ;	not accept any byte in a reasonable time else CY clear.
   401                  omni$strobe:
   402   022E 210200    	lxi	h,2		;Find address of rslt block from TCB BC-->
   403   0231 09        	dad	b		;pre-set result code in block to ff (busy)
   404   0232 7E        	mov	a,m
   405   0233 23        	inx	h
   406   0234 6E        	mov	l,m
   407   0235 67        	mov	h,a
   408   0236 36FF      	mvi	m,0ffh
   409                  ;
   410   0238 AF        	xra	a		;MSB is always 0
   411   0239 CD4302    	call	omni$st		;send bits 23-16 of pointer to hardware
   412   023C D8        	rc			;(abort if timeout)
   413                  ;
   414   023D 78        	mov	a,b		;send bits 15-8 of pointer to hardware
   415   023E CD4302    	call	omni$st
   416   0241 D8        	rc			;(abort if timeout)
   417                  ;
   418   0242 79        	mov	a,c		;send bits 7-0 of pointer to hardware
   419                  				; (fall into omni$st)
   420                  
   421                  ;	called by omni$strobe to send one byte from A to transporter hardware
   422                  ;	returns CY set if hardware doesn't come ready in a reasonable time.
   423                  omni$st:
   424   0243 F5        	push	psw		;save data for now
   425   0244 1150C3    	lxi	d,50000		;set timeout
   426                  omni$st0:
   427   0247 DBF9      	in	TSTAT		;read status port and check busy bit
   428   0249 E610      	ani	TCRDY
   429   024B CA5302    	jz	omni$st1	;if busy, go increment and test timeout
   430                  ;
   431   024E F1        	pop	psw		;else output the byte
   432   024F D3F8      	out	TDATA		;to the transporter TCB pointer input register
CP/M RMAC ASSEM 1.1	#009	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

   433   0251 B7        	ora	a
   434   0252 C9        	ret			;and return with no error shown (CY=0)
   435                  ;
   436                  omni$st1:			;else
   437   0253 1B        	dcx	d
   438   0254 7A        	mov	a,d
   439   0255 B3        	ora	e
   440   0256 C24702    	jnz	omni$st0	;loop back if not timed out yet
   441                  ;
   442   0259 F1        	pop	psw		;else
   443   025A 37        	stc
   444   025B C9        	ret			;return error flag (CY=1)
   445                  
   446                  
   447                  ;	waits till timeout (about 20 secs) for result block BC--> to show done
   448                  ;	returns A=returned status code.  If timeout occurs, the returned
   449                  ;	status will still be 0FEH or 0FFH.
   450                  omni$wfdone:			
   451   025C 11FFFF    	lxi	d,0ffffh	;setup timeout counters
   452   025F 2E14      	mvi	l,20
   453                  ;
   454                  omni$wfdone1:
   455   0261 0A        	ldax	b		;is the result code still > 0f0h?
   456   0262 FEF0      	cpi	0f0h
   457   0264 D8        	rc			;no, return to caller
   458                  ;
   459   0265 1B        	dcx	d		;else decrement timeout
   460   0266 7B        	mov	a,e
   461   0267 B2        	ora	d
   462   0268 C26102    	jnz	omni$wfdone1	;timeout yet?
   463   026B 2D        	dcr	l
   464   026C C26102    	jnz	omni$wfdone1	;no, go back and check again
   465                  ;
   466   026F 0A        	ldax	b		;yes, timeout
   467   0270 C9        	ret			;return with completion code in A
   468                  
   469                  
   470   0271           	end
CP/M RMAC ASSEM 1.1	#010	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

ACTIVE           0010    78#
BDOS             0005    83#  347   353 
BUFFSIZE         008A    45#  121 
CNFGTBLADR       0152    96   224#
CONFIGTBL        0015   103#  225 
CONOUTF          0002    85#  346 
CR               000D    89#  194   195   236 
DFLT             003C   115#
E2LONG           0081    58#
EBDCTL           0083    60#
EBDDES           0085    62#
EBDNODE          0086    63#
EBDSKT           0084    61#
ECMDOK           00FE    64#
ENDRCVF          0010    68#  161 
ENOSKT           0082    59#
ETXFAIL          0080    57#
ETXOK            00C0    56#
FALSE            0000    40#   41 
INITF            0020    69#  171 
INITMSG          0106   186   193#
INITRCODE        00F3   176#
INITRESULT       00F3   175#  376   386 
INITTCB          00EF   170#  381 
INITTCMD         00EF   171#
INITTRSLT        00F1   173#  379 
LF               000A    88#  194   195   236 
MAXMSG           0200    46#  149   150 
NERR             01CE   289   322#
NETBASE          00F8    71#   72    74 
NETWORKSTATUS    0015   104#  202   214   322 
NIBL             01DF   337   340#
NOERR            0000    55#
NTWRKERROR       0138    99   184   200#
NTWRKINIT        00F4    94   183#
NTWRKSTS         0148    95   213#
NTWRKWBOOT       0156   100   231#
OMNIINIT         01F5   203   360#
OMNIST           0243   411   415   423#
OMNIST0          0247   426#  440 
OMNIST1          0253   429   436#
OMNISTROBE       022E   271   305   314   382   401#
OMNIWFDONE       025C   277   309   318   387   450#
OMNIWFDONE1      0261   454#  462   464 
PRHEX            01D6   189   332#
PRINTA           01EA   343   345#
PRINTF           0009    84#  352 
PRINTMSG         01F0   187   233   351#
RCVERR           0002    79#  216   319 
RCVF             00F0    67#  143 
RECEIVEMSG       01A6    98   299#
RXERR            01CC   306   315   319#
RXRCODE          00E5   155#
RXRDLEN          00E7   157#
CP/M RMAC ASSEM 1.1	#011	SAMPLE SLAVE NETWORK I/O SYSTEM FOR CORVUS OMNINET 20 OCT 82

RXRESULT         00E5   154#  308   366 
RXRSRCE          00E6   156#
RXTCB            00D9   142#  304 
RXTCLEN          00E3   151#
RXTCMD           00D9   143#
RXTDEST          00E4   152#
RXTDLEN          00E1   149#
RXTMSG           00DF   148#  302 
RXTRSLT          00DB   145#  369 
RXTSKT           00DD   146#
SENDAGAIN        0185   268#  286 
SENDERR          0001    80#  216   288 
SENDF            0040    66#  128 
SENDMSG          0167    97   246#
SKT0             0080    47#
SKT1             0090    48#
SKT2             00A0    49#   51 
SKT3             00B0    50#
SLVID1           0016   106#  188   205   388 
SLVID2           003E   117#  206   389 
SNDERR           01A1   273   288#
SOCKET           00A0    51#  131   146   164 
TCRDY            0010    73#  428 
TDATA            00F8    74#  432 
TRUE             FFFF    41#
TSTAT            00F9    72#  427 
TXRCODE          00D8   139#
TXRESULT         00D8   138#  275   361 
TXTCB            00CC   127#  270 
TXTCLEN          00D6   135#
TXTCMD           00CC   128#
TXTDEST          00D7   136#  264 
TXTDLEN          00D4   134#  260 
TXTMSG           00D2   133#  249 
TXTRIES          0064    44#  266 
TXTRSLT          00CE   130#  364 
TXTSKT           00D0   131#
UNRXRCODE        00EE   167#
UNRXRESULT       00EE   166#  317   371 
UNRXTCB          00E9   160#  313 
UNRXTCMD         00E9   161#
UNRXTRSLT        00EB   163#  374 
UNRXTSKT         00ED   164#
WBOOTMSG         015C   232   235#

Listing G-2. Sample Server Network I/O for Corvus OMNINET
CP/M RMAC ASSEM 1.1	#001	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

     1                    title 'Sample Server Network I/F for CORVUS OMNINET 20-Oct-82'
     2                  	page 54
     3                  
     4                  ;________________________________________________________________________
     5                  ;________________________________________________________________________
     6                  ;									;
     7                  ;	SAMPLE MASTER NETWORK IO SYSTEM FOR CP/NET 1.2			;
     8                  ;	VERSION FOR CORVUS OMNINET "ENGINEERING" TRANSPORTER		;
     9                  ;		(Requires RMAC for assembly)				;
    10                  ;									;
    11                  ;	COPYRIGHT (C) 1982 by	VANO ASSOCIATES, INC.			;
    12                  ;				P.O. BOX 12730				;
    13                  ;				New Brighton, MN  55112			;
    14                  ;				U.S.A.					;
    15                  ;				(612) 631-1245				;
    16                  ;				ALL RIGHTS RESERVED			;
    17                  ;									;
    18                  ;	ANY USE OF THIS CODE without the imbedded copyright notice	;
    19                  ;	is hereby strictly prohibited.					;
    20                  ;									;
    21                  ;	Permission is hereby granted to Digital Research Inc. to use	;
    22                  ;	this source file for educational and illustrative purposes in 	;
    23                  ;	conjunction with CP/Net 80 documentation.  Any other use of 	;
    24                  ;	this code without the EXPRESS WRITTEN PERMISSION of VANO	;
    25                  ;	ASSOCIATES INC. is hereby strictly prohibited.			;
    26                  ;									;
    27                  ;	This file is provided courtesy of:				;
    28                  ;									;
    29                  ;		R2E (Realisations Etude Electroniques)			;
    30                  ;		Z.A.I. de Courtaboeuf					;
    31                  ;		BP 73	91942 Les Ulis					;
    32                  ;		FRANCE							;
    33                  ;									;
    34                  ;	who sponsored the development of one of its ancestors.		;
    35                  ;									;
    36                  ;	Note that this version requires that the CP/NET SLAVESP		;
    37                  ;	process be properly patched to send all output traffic		;
    38                  ;	to output queue 0.  For the current (1.2) beta release, the	;
    39                  ;	following patch is enough:					;
    40                  ;									;
    41                  ;	Make this change in unrelocated server.rsp module.		;
    42                  ;		-a543							;
    43                  ;		0543  mvi a,30						;
    44                  ;		0545  jmp 34f						;
    45                  ;	Then resave the module and its bit map.				;
    46                  ;									;
    47                  ;________________________________________________________________________
    48                  ;________________________________________________________________________
    49                  
    50   FFFF =         YES	equ	0ffffh
    51   0000 =         NO	equ	not YES
    52                  
    53                  ;	assembly mode switches
    54   0000 =         DEBUG		equ	NO		;assemble for debugging with rdt
CP/M RMAC ASSEM 1.1	#002	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

    55   FFFF =         RSP		equ	YES		;assemble as a resident process
    56   0000 =         INTERRUPT 	equ	NO		;transporter can interrupt (advisable)
    57                  
    58                  ;	Logical Configuration constants
    59   0002 =         NSLAVES		equ	2		;maximum number of slaves supported
    60   0096 =         SRVR$STK$SIZ	equ	150		;stack size  needed by SLVSPs
    61   0034 =         SRVR$PD$SIZ	equ	52		;PD size for SLVSPs
    62   0118 =         BUFFSIZE	equ	280		;maximum message buffer size
    63   0003 =         NMSG$BUFFS	equ	1+NSLAVES	;number of message buffers allocated
    64   0040 =         RX$PRIORITY	equ	64		;receive process priority
    65   003F =         TX$PRIORITY	equ	63		;usually higher than rx
    66                  
    67                  ;	Physical configuration constants (FOR OUR INSTALLATION)
    68   00F8 =         OMNI$BASE	equ	0F8h		;transporter base address
    69   00A0 =         OMNI$SOCKET	equ	0a0h		;omninet transporter socket code (2)
    70   0008 =         OMNI$FLAG	equ	8		;XDOS flag for int. driven transporter
    71   0007 =         RST$NUM		equ	7		;interrupt level if interrupt driven
    72   0038 =           INT$VCTR 	equ	RST$NUM * 8
    73                  
    74                  ;	transporter IO PORT constants for CORVUS "ENGINEERING" transporter
    75   00F8 =         OMNI$DATA	equ	OMNI$BASE	;TCB pointer data port
    76   00F9 =         OMNI$STAT	equ	OMNI$BASE + 1	;status port
    77   0010 =         OMNI$RDY	equ	10h		;ready bit (=1) in OMNI$STAT
    78                  ;  the rest are not part of standard CORVUS "ENGINEERING" transporter
    79   00FA =         OMNI$ACK	equ	OMNI$BASE + 2	;int ack port (any data write)
    80   00FB =         OMNI$MASK	equ	OMNI$BASE + 3	;int mask port (b0, 1= enbl)
    81   0001 =         OMNI$PENDING	equ	1		;int pending (=1) in "   "
    82   0001 =         OMNI$ENABLE	equ	1		;int enable mask command
    83   0000 =         OMNI$DISABLE	equ	0		;int disable mask command
    84                  
    85                  ;	BDOS and XDOS Equates
    86   0009 =         PRINTF		equ	9		;message to console
    87   0084 =         FLAGWAITF	equ	132		;flag wait
    88   0085 =         FLAGSETF	equ	133		;flag set
    89   0086 =         MAKEQ		equ	134		;make queue
    90   0089 =         READQ		equ	137		;read queue
    91   008B =         WRITEQ		equ	139		;write queue
    92   008D =         DELAY		equ	141		;delay
    93   008E =         DSPTCH		equ	142		;dispatch
    94   0090 =         CREATEP		equ	144		;create process
    95   0091 =         SET$PRIORITY	equ	145		;set caller's priority
    96   0093 =         DETACH		equ	147		;detach console
    97   009A =         SYDATAD		equ	154		;get system data page address
    98                  
    99                  ;	MISC useful constants
   100   000D =         CR		equ	0dh		;carriage return
   101   000A =         LF		equ	0ah		;line feed
   102                  
   103                  
   104                  codeseg:
   105                    if not RSP
   106                  ;	.PRL Initialization entry point for whole module
   107                  	lxi	sp,ServerxSTKTOP	;switch to rx process stack
   108                  	mvi	c,SET$PRIORITY
CP/M RMAC ASSEM 1.1	#003	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   109                  	mvi	e,RX$PRIORITY
   110                  	call	bdos
   111                      if not DEBUG
   112                  	  mvi	c,DETACH
   113                  	  call	bdos			;detach console
   114                      endif ; DEBUG
   115                  	ret
   116                  
   117                  bdosadr:
   118                  	dw	codeseg - 100h + 5	;bdos entry pointer
   119                    else ; not RSP
   120                  ;	in an rsp, this is filled in by GENSYS and the rx process is created
   121                  ;	automatically
   122                  bdosadr:
   123   0000 0000      	dw	0000h
   124                    endif ; not RSP
   125                  
   126                  page
CP/M RMAC ASSEM 1.1	#004	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   127                  
   128                  ;________________________________________________________________________
   129                  ;________________________________________________________________________
   130                  ;									;
   131                  ;	This is the network receiver server process module		;
   132                  ;									;
   133                  ;	The receive server obtains a buffer from FreeBuff and gives it	;
   134                  ;	to the transporter hardware for receive use.  It then waits	;
   135                  ;	for a message completion by calling the wf$rx$done routine	;
   136                  ;	Once a return from that routine occurs, the receiver server 	;
   137                  ;	checks the slave number and sends a pointer to that message	;
   138                  ;	buffer to the SLVSP support process corresponding to that	;
   139                  ;	slave's server. Once the message pointer has been passed, the	;
   140                  ;	process loops back for the next message and continues in this	;
   141                  ;	fashion forever.						;
   142                  ;									;
   143                  ;	At present, receive errors are considered to be the Slave's	;
   144                  ;	problem since normal error recovery is allegedly handled by the ;
   145                  ;	transporter firmware.  Only error free messages are passed on, 	;
   146                  ;	the rest are ignored unless the error is the absence of a free	;
   147                  ;	support process in which case a "NOT LOGGED IN" error is sent	;
   148                  ;	by the receiver process to the offending slave.			;
   149                  ;									;
   150                  ;	In order to prevent clobbering the transporter when it is busy	;
   151                  ;	transmitting, the receiver must be synchronized with the	;
   152                  ;	transmit server.  In this implementation, this is handled by	;
   153                  ;	an MX Queue.							;
   154                  ;									;
   155                  ;________________________________________________________________________
   156                  ;________________________________________________________________________
   157                  
   158                  ;	receiver server process descriptor (position dependent if RSP)
   159                  ServerxPD:
   160   0002 0000      	dw	0			;link
   161   0004 0040      	db	0,RX$PRIORITY		;status,priority
   162   0006 6400      	dw	$ + 94			;stack pointer
   163   0008 5365727665	db	'ServeRX '		;name
   164   0010 00FF      	db	0,0ffh			;console, memseg
   165   0012           	ds	82			;reserved for MP/M use and stack
   166                  ServerxSTKTOP:
   167   0064 9800      	dw	InitRX			;startup PC for process
   168                  
   169                  ;	User queue control block array used by this module for message queues. 
   170                  ;	 Each element is 3 words long and is one UQCB followed by its message.
   171   0006 =         UQCBLEN	equ	6			;constant used to index array
   172   0004 =         XQCBMSG	equ	4			;subindex for  message word
   173                  
   174                  INUQCB:					;array name
   175   0000 #         ??xx	set	0
   176                  	rept	NSLAVES
   177                  	  dw	(inqcb$array + ??xx)	;;Q pointer, msg addr, message word
   178                  	  dw	$+2
   179                  	  dw	0
   180                  ??xx	  set	??xx + INQCB$SIZE
CP/M RMAC ASSEM 1.1	#005	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   181                  	endm
   182   0066+AC04      	  DW	(INQCB$ARRAY + ??XX)
   183   0068+6A00      	  DW	$+2
   184   006A+0000      	  DW	0
   185   006C+C604      	  DW	(INQCB$ARRAY + ??XX)
   186   006E+7000      	  DW	$+2
   187   0070+0000      	  DW	0
   188                  
   189                  ;	UQCB used by ServeRX to get free buffers from Q
   190   0072 1E057600  gbuf$uqcb:	dw	buffQCB,newbuff
   191   0076 0000      newbuff:	dw	0		;message is a free buffer ptr from pool
   192                  
   193                  ;	UQCB used by ServeRX to get transporter from MX Q
   194   0078 A8087C00  omnirx$uqcb:	dw	omniQ,rx$mx$msg
   195   007C 0000      rx$mx$msg:	dw	0
   196                  
   197                  ;	UQCB used by ServeRX to send error messages to outQ
   198   007E E0048200  err$out$uqcb:	dw	outQCB,err$out$msg ;pointer, msgadr
   199   0082           err$out$msg:	ds	2		   ;used to send error messages
   200                  
   201                  ;	receiver transporter control block
   202   0084 F0        rxtcb:	db	0f0h			;post read command
   203   0085 00        	db	0			;result hi (always 0)
   204                  rxrsltp:
   205   0086 0000      	db	0,0			;result middle and low (NOT 8080 order)
   206   0088 A0        	db	OMNI$SOCKET		;transporter message socket code
   207   0089 00        	db	0			;data pointer high (always 0)
   208   008A 0000      	db	0,0			;data pointer middle, low
   209   008C 01        	db	BUFFSIZE/256		;data max length hi
   210   008D 18        	db	BUFFSIZE and 255	;data max length lo
   211   008E 0000      	db	0,0			;ctrl lgth (0 for now), host (not used)
   212                  
   213   0090 0000000000rxrslt:	db	0,0,0,0,0,0,0,0		;result block for rx
   214                  
   215                  ;________________________________________________________________________
   216                  ;									;
   217                  ;	Receiver server process initialization entry point		;
   218                  ;		(initializes all of module)				;
   219                  ;________________________________________________________________________
   220   0098 CDCD08    InitRX:	call	omni$init	;init hardware & get ID code from its switches
   221   009B 32FB02    	sta	configtbl+1	; store ID in config table as master ID
   222                  ;
   223   009E 0E86      	mvi	c,MAKEQ		;create the free buffer Q
   224   00A0 111E05    	lxi	d,buffQCB
   225   00A3 CDA408    	call	bdos
   226                  ;
   227   00A6 11AC04    	lxi	d,inqcb$array
   228   00A9 0E02      	mvi	c,NSLAVES	;create input Qs (1/slave supported)
   229                  make$inQs:
   230   00AB D5        	push	d
   231   00AC C5        	push	b
   232   00AD 0E86      	mvi	c,MAKEQ
   233   00AF CDA408    	call	bdos
   234   00B2 C1        	pop	b
CP/M RMAC ASSEM 1.1	#006	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   235   00B3 D1        	pop	d
   236   00B4 211A00    	lxi	h,INQCB$SIZE
   237   00B7 19        	dad	d
   238   00B8 EB        	xchg
   239   00B9 0D        	dcr	c
   240   00BA C2AB00    	jnz	make$inQs
   241                  ;
   242   00BD 11E004    	lxi	d,outQCB	;create the output Queue (only 1)
   243   00C0 0E86      	mvi	c,MAKEQ
   244   00C2 CDA408    	call	bdos
   245                  ;
   246   00C5 11B901    	lxi	d,ServetxPD	;create the network output process
   247   00C8 0E90      	mvi	c,CREATEP
   248   00CA CDA408    	call	bdos
   249                  ;
   250   00CD 0E9A      	mvi	c,SYDATAD	;get system data page address
   251   00CF CDA408    	call	bdos
   252   00D2 110900    	lxi	d,9
   253   00D5 19        	dad	d		;install config table address at sysdat(9)
   254   00D6 11FA02    	lxi	d,configtbl
   255   00D9 73        	mov	m,e
   256   00DA 23        	inx	h
   257   00DB 72        	mov	m,d
   258                  ;
   259   00DC 219000    	lxi	h,rxrslt	;initialize transporter command block result
   260   00DF 55        	mov	d,l		;field to point to receive result block
   261   00E0 5C        	mov	e,h		; (done at run time because of reversed byte
   262   00E1 EB        	xchg			;  order used by CORVUS.)
   263   00E2 228600    	shld	rxrsltp
   264                  
   265                  
   266                  ;	Receiver server process loop head
   267   00E5 0E89      RXloop:	mvi	c,READQ
   268   00E7 117200    	lxi	d,gbuf$uqcb
   269   00EA CDA408    	call	bdos		;get a free message buffer from Q
   270                  ;
   271                  RXretry:
   272   00ED 2A7600    	lhld	newbuff
   273   00F0 5C        	mov	e,h
   274   00F1 55        	mov	d,l
   275   00F2 EB        	xchg			;swap bytes for CORVUS command block
   276   00F3 228A00    	shld	rxtcb+6		;put buffer address pointer in rx tcb
   277                  ;
   278   00F6 117800    	lxi	d,omnirx$uqcb	;read MX message from OMNINET HARDWARE MX Q
   279   00F9 0E89      	mvi	c,READQ
   280   00FB CDA408    	call	bdos
   281                  ;
   282   00FE 018400    	lxi	b,rxtcb		;send TCB pointer to hardware
   283   0101 CDF508    	call	omni$strobe
   284                  ;
   285   0104 F5        	push	psw		;return MX message
   286   0105 117800    	lxi	d,omnirx$uqcb
   287   0108 0E8B      	mvi	c,WRITEQ
   288   010A CDA408    	call	bdos
CP/M RMAC ASSEM 1.1	#007	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   289   010D F1        	pop	psw		;restore return code from omni$strobe routine
   290                  ;
   291   010E DAED00    	jc	RXretry		;no choice except to retry if not accepted
   292                  ;
   293   0111 019000    	lxi	b,rxrslt	;wait for a completion from hardware
   294   0114 CD2309    	call	wfrxdone
   295   0117 E680      	ani	80h		;if error on message, re-post buffer
   296   0119 C2ED00    	jnz	RXretry
   297                  ;
   298                  ;  buffer contains a valid message at this point, so process it
   299   011C 2A7600    	lhld	newbuff		;get FMT to A
   300   011F 7E        	mov	a,m
   301   0120 23        	inx	h		;get SID to C
   302   0121 23        	inx	h
   303   0122 4E        	mov	c,m
   304                  ;
   305   0123 E6FE      	ani	0feh		;look for login/logoff messages
   306   0125 C24601    	jnz	RXl2		;message type 0 or 1?
   307   0128 23        	inx	h		;yes, check FNC
   308   0129 7E        	mov	a,m
   309   012A FE40      	cpi	40h		;login?
   310   012C C23801    	jnz	RXl1		;not login, go on
   311   012F CDA301    	call	logiton		;ELSE try to find a free SLVSP in table
   312   0132 C26C01    	jnz	RXl3		;found one (or already logged in), go on
   313   0135 C34C01    	jmp	RX$send$err	;sorry,no free processes, go advise slave
   314                  ;
   315   0138 FE41      RXl1:	cpi	41h		;logoff?
   316   013A C24601    	jnz	RXl2		;not logoff, go on
   317   013D CD9A01    	call	logitoff	;ELSE try to remove that slave from table
   318   0140 C26C01     	jnz	RXl3		;if successful, go on
   319   0143 C34C01    	jmp	RX$send$err	;otherwise go tell slave it wasn't logged in
   320                  ;
   321   0146 CD8001    RXl2:	call	get$slvsp	;not login/logoff so get slvsp msg address
   322   0149 C26C01    	jnz	RXl3		; for that slave if it is logged in and go
   323                  				; send message to its Q else fall through
   324                  ;
   325                  ;	this code sends a "NOT LOGGED IN" error message back to requester
   326                  RX$send$err:
   327   014C 2A7600    	lhld	newbuff		;build an error message in the same buffer
   328   014F 228200    	shld	err$out$msg
   329   0152 3601      	mvi	m,1		;FMT=1
   330   0154 23        	inx	h
   331   0155 7E        	mov	a,m		;swap DID and SID
   332   0156 23        	inx	h
   333   0157 46        	mov	b,m
   334   0158 77        	mov	m,a
   335   0159 2B        	dcx	h
   336   015A 70        	mov	m,b
   337   015B 23        	inx	h		;leave FNC field alone
   338   015C 23        	inx	h
   339   015D 23        	inx	h
   340   015E 3601      	mvi	m,1		;SIZ=1
   341   0160 23        	inx	h
   342   0161 36FF      	mvi	m,0ffh		;message = 0FFH (extended error flag)
CP/M RMAC ASSEM 1.1	#008	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   343   0163 23        	inx	h
   344   0164 360C      	mvi	m,12		;"NOT LOGGED IN" code
   345   0166 117E00    	lxi	d,err$out$uqcb	;post to network transmitter process
   346   0169 C37801    	jmp rxl4		;using common write Q code
   347                  ;
   348                  ;  this code sends the message address to the appropriate SLVSP Q
   349   016C 2A7600    RXl3:	lhld	newbuff		;DE--> msg field of correct UQCB here
   350   016F EB        	xchg			;put message ptr in UQCB message field
   351   0170 73        	mov	m,e
   352   0171 23        	inx	h
   353   0172 72        	mov	m,d
   354   0173 11FBFF    	lxi	d,-(XQCBMSG + 1);index back to UQCB base address
   355   0176 19        	dad	d
   356   0177 EB        	xchg
   357                  ;
   358   0178 0E8B      rxl4:	mvi	c,WRITEQ
   359   017A CDA408    	call	bdos		;send it to Queue
   360   017D C3E500    	jmp	RXloop		;go back and get another buffer and continue
   361                  	
   362                  
   363                  ;	routine dynamically maps physical slave number passed in C
   364                  ;	to a slave support process and returns its INUQCB message buffer addr
   365                  ;	in DE and A = 0 with flags set if no room or not found, else NZ
   366                  get$slvsp:
   367   0180 79        	mov	a,c		;A= requester ID
   368   0181 0602      	mvi	b,NSLAVES	;set up for table search
   369   0183 21B301    	lxi	h,idtbl
   370                  find$match:			;search till match or table end
   371   0186 BE        	cmp	m
   372   0187 C29101    	jnz	not$match	; goto not$match if not this one
   373   018A 23        	inx	h		;else match found, get ptr to SLVSP message
   374   018B 5E        	mov	e,m
   375   018C 23        	inx	h
   376   018D 56        	mov	d,m		;its slvsp msg addr
   377   018E 37        	stc
   378   018F 9F        	sbb	a
   379   0190 C9        	ret			;and return TRUE in A to caller
   380                  not$match:
   381   0191 23        	inx	h		;no match, skip to next entry
   382   0192 23        	inx	h
   383   0193 23        	inx	h
   384   0194 05        	dcr	b		;any more entries?
   385   0195 C28601    	jnz	find$match	;loop back until all searched
   386   0198 AF        	xra	a		;else return failure (A=00)
   387   0199 C9        	ret
   388                  
   389                  
   390                  ;	removes entry (C=SID) from map table (but still returns msg ptr)
   391                  logitoff:
   392   019A CD8001    	call	get$slvsp
   393   019D C8        	rz			;not in table, just exit
   394   019E 2B        	dcx	h		;else mark entry as free and then exit
   395   019F 2B        	dcx	h
   396   01A0 36FF      	mvi	m,0ffh
CP/M RMAC ASSEM 1.1	#009	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   397   01A2 C9        	ret
   398                  
   399                  ;	installs entry (C=SID) in first free entry of map table and returns
   400                  ;	msg address. RETURNS A=0 if no space, else non-zero.
   401                  logiton:
   402   01A3 CD8001    	call	get$slvsp	;see if already in table
   403   01A6 C0        	rnz			;if so, just use old entry
   404   01A7 C5        	push	b		;else look for a free entry (CODE=FF)
   405   01A8 0EFF      	mvi	c,0ffh
   406   01AA CD8001    	call	get$slvsp
   407   01AD C1        	pop	b
   408   01AE C8        	rz			;no free entries, exit
   409   01AF 2B        	dcx	h		;else enter SID in table and return success
   410   01B0 2B        	dcx	h
   411   01B1 71        	mov	m,c
   412   01B2 C9        	ret			;PSW is still correct from search
   413                  
   414                  ;	Slave mapping table has one entry per SLVSP.  First  byte = SID
   415                  ;	of the requester currently using SLVSP (0ffh if none).  Next word is
   416                  ;	the address of the message field of that SLVSP's input UQCB.
   417                  idtbl:
   418   0000 #         ??xx	set	0
   419                  	rept	NSLAVES
   420                  	  db	0ffh
   421                  	  dw	(INUQCB + XQCBMSG + ??xx)
   422                  ??xx	  set	??xx + UQCBLEN
   423                  	endm
   424   01B3+FF        	  DB	0FFH
   425   01B4+6A00      	  DW	(INUQCB + XQCBMSG + ??XX)
   426   01B6+FF        	  DB	0FFH
   427   01B7+7000      	  DW	(INUQCB + XQCBMSG + ??XX)
   428                  
   429                  page
CP/M RMAC ASSEM 1.1	#010	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   430                  
   431                  ;________________________________________________________________________
   432                  ;________________________________________________________________________
   433                  ;									;
   434                  ;	This is the network transmitter server process module.		;
   435                  ;	NOTE THAT THE OMNINET TRANSPORTER MUST NOT BE DISTURBED ONCE	;
   436                  ;	A TRANSMIT HAS BEEN POSTED UNTIL IT RETURNS A COMPLETION.	;
   437                  ;	An MX Queue is used in this version to protect the transporter	;
   438                  ;	from other processes.						;
   439                  ;									;
   440                  ;	This process reads a message from the SLVSP output Q and when	;
   441                  ;	awakened by one posts that buffer for transmission via the	;
   442                  ;	transporter to the requester.  This process then waits until	;
   443                  ;	the transporter reports a completion as determined by the	;
   444                  ;	wf$txdone routine.  The buffer pointer from that message is	;
   445                  ;	then sent back to the FreeBuff Q and the process loops back for	;
   446                  ;	another	message from the SLVSP output Q.  Transmitter errors	;
   447                  ;	are considered the Transporter's problem and are ignored here.	;
   448                  ;________________________________________________________________________
   449                  ;________________________________________________________________________
   450                  ;	Transmitter server process descriptor
   451                  ServetxPD:
   452   01B9 0000      	dw	0			;link
   453   01BB 003F      	db	0,TX$PRIORITY		;status,priority
   454   01BD 1B02      	dw	$ + 94			;stack pointer
   455   01BF 5365727665	db	'ServeTX '		;name
   456   01C7 00FF      	db	0,0ffh			;console, memseg
   457   01C9           	ds	82			;reserved for MP/M use and as stack
   458   021B 4302      	dw	InitTX			;stack top has startup PC
   459                  
   460                  ;	There is only one output queue (SLVSP --> NTWRKIF)
   461                  OUTUQCB:
   462   021D E0042102  UQCBNtwrkQO0:	dw	outQCB,outQMSG	;pointer, msgadr
   463   0221           outQMSG:	ds	2		;used to receive msg pointer from SLVSP
   464                  
   465                  ;	used by ServeTX to return them to Q when done (used at init also)
   466   0223 1E052702  pbuf$uqcb:	dw	buffQCB,oldbuff
   467   0227 0000      oldbuff:	dw	0		;msg is a freed buff ptr back to pool
   468                  
   469                  ;	UQCB used by ServeTX to get transporter from MX Q
   470   0229 A8082D02  omnitx$uqcb:	dw	omniQ,tx$mx$msg
   471   022D 0000      tx$mx$msg:	dw	0
   472                  
   473                  ;	transmitter transporter control block
   474   022F 40        txtcb:	db	40h			;command
   475   0230 00        	db	0			;result hi
   476                  txrsltp:
   477   0231 0000      	db	0,0			;result middle and low
   478   0233 A0        	db	OMNI$SOCKET		;transporter message socket code
   479   0234 000000    	db	0,0,0			;data ptr (MSB,SB,LSB)
   480   0237 0000      	db	0,0			;length (MSB,LSB)
   481   0239 00        	db	0			;control length
   482   023A 00        	db	0			;dest host
   483                  
CP/M RMAC ASSEM 1.1	#011	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   484   023B 0000000000txrslt:	db	0,0,0,0,0,0,0,0		;result block for tx
   485                  
   486                  ;________________________________________________________________________
   487                  ;									;
   488                  ;	ServeTX initialization entry point				;
   489                  ;________________________________________________________________________
   490                  InitTX:
   491   0243 215C05    	lxi	h,msgbuffs	;preload the Free buffer Q with buffer ptrs
   492   0246 0E03      	mvi	c,NMSG$BUFFS	;from start of buffer space
   493                  freeloop:
   494   0248 222702    	shld	oldbuff
   495   024B E5        	push	h
   496   024C C5        	push	b
   497   024D 0E8B      	mvi	c,WRITEQ
   498   024F 112302    	lxi	d,pbuf$uqcb
   499   0252 CDA408    	call	bdos
   500   0255 C1        	pop	b
   501   0256 E1        	pop	h
   502   0257 111801    	lxi	d,BUFFSIZE
   503   025A 19        	dad	d
   504   025B 0D        	dcr	c
   505   025C C24802    	jnz	freeloop
   506                  ;
   507   025F 213B02    	lxi	h,txrslt	;initialize TX Transporter Command Block
   508   0262 5C        	mov	e,h		;to point to TX Result Block
   509   0263 55        	mov	d,l
   510   0264 EB        	xchg	
   511   0265 223102    	shld	txrsltp
   512                  
   513                  ;	ServeTX process loop
   514                  TXloop:
   515   0268 0E89      	mvi	c,READQ		;wait for a message in network output Q
   516   026A 111D02    	lxi	d,outuqcb
   517   026D CDA408    	call	bdos
   518                  ;
   519   0270 2A2102    	lhld	outQMSG
   520   0273 5C        	mov	e,h
   521   0274 55        	mov	d,l		;put message buffer address in TX TCB
   522   0275 EB        	xchg			;(NOTE, NOT (8080 byte order)
   523   0276 223502    	shld	txtcb+6
   524                  ;
   525   0279 13        	inx	d
   526   027A 1A        	ldax	d		;set transport layer destination addr=DID
   527   027B 323A02    	sta	txtcb + 11
   528                  ;
   529   027E 210300    	lxi	h,3
   530   0281 19        	dad	d		;calculate physical message length
   531   0282 6E         	mov	l,m		;from SIZ field
   532   0283 2600      	mvi	h,0
   533   0285 110600    	lxi	d,6		;put in TCB length field
   534   0288 19        	dad	d
   535   0289 55        	mov	d,l
   536   028A 5C        	mov	e,h
   537   028B EB        	xchg
CP/M RMAC ASSEM 1.1	#012	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   538   028C 223702    	shld	txtcb+8
   539                  ;
   540   028F 112902    	lxi	d,omnitx$uqcb	;get transporter hardware MX message
   541   0292 0E89      	mvi	c,READQ
   542   0294 CDA408    	call	bdos
   543                  ;
   544                  TXretry:
   545   0297 012F02    	lxi	b,txtcb		;send TCB pointer to hardware
   546   029A CDF508    	call	omni$strobe	;if can't, not much else to do but try again
   547   029D DA9702    	jc	TXretry		;  (ALTHOUGH THIS IS A FATAL HARDWARE ERROR)
   548                  ;
   549   02A0 013B02    	lxi	b,txrslt	;wait for transmit completion
   550   02A3 CD3409    	call	wftxdone	;ignore errors here as no recovery possible
   551                  ;
   552   02A6 112902    	lxi	d,omnitx$uqcb
   553   02A9 0E8B      	mvi	c,WRITEQ
   554   02AB CDA408    	call	bdos		;release MX msg
   555                  ;
   556   02AE 2A2102    	lhld	outQMSG		;send the buffer back to FREEBUFF Q
   557   02B1 222702    	shld	oldbuff
   558   02B4 0E8B      	mvi	c,WRITEQ
   559   02B6 112302    	lxi	d,pbuf$uqcb
   560   02B9 CDA408    	call	bdos
   561                  ;
   562   02BC C36802    	jmp	txloop		;and go back and do it all with next msg
   563                  
   564                  
   565                  page
CP/M RMAC ASSEM 1.1	#013	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   566                  
   567   02BF 4E5457524Bcnote:	db 'NTWRKIF (c)1982 VANO ASSOCIATES, INC. - ALL RIGHTS RESERVED'
   568                  ;________________________________________________________________________
   569                  ;________________________________________________________________________
   570                  ;									;
   571                  ;	GLOBAL Master Configuration Table and storage			;
   572                  ;	(address must be installed on SysData page(9,10) at init.)	;
   573                  ;________________________________________________________________________
   574                  ;________________________________________________________________________
   575                  configtbl:
   576   02FA 00        	db	0		;Master status byte
   577   02FB 00        	db	0		;Master processor ID
   578   02FC 02        	db	NSLAVES		;Maximum number of slaves supported
   579   02FD 00        	db	0		;Number of logged in slaves
   580   02FE 0000      	dw	0		;16 bit vector of logged in slaves
   581   0300           	ds	16		;Slave processor ID array
   582   0310 5041535357	db	'PASSWORD' 	;login password
   583                  
   584                  ;	builds Server stacks and initializes them with PD storage pointers
   585   0000 #         ??xx	set	0
   586                  	rept	NSLAVES
   587                  	  ds	SRVR$STK$SIZ - 2
   588                  	  dw	srvr$pd$base + ??xx
   589                  	  ??xx	set ??xx + SRVR$PD$SIZ
   590                  	endm
   591   0318+          	  DS	SRVR$STK$SIZ - 2
   592   03AC+4404      	  DW	SRVR$PD$BASE + ??XX
   593   03AE+          	  DS	SRVR$STK$SIZ - 2
   594   0442+7804      	  DW	SRVR$PD$BASE + ??XX
   595                  
   596                  ;	allocates PD storage
   597                  srvr$pd$base:
   598   0444           	ds	NSLAVES * SRVR$PD$SIZ
   599                  
   600                  ;________________________________________________________________________
   601                  ;________________________________________________________________________
   602                  ;									;
   603                  ;	INTERPROCESS QUEUES (both local and global) and COMMON data	;
   604                  ;________________________________________________________________________
   605                  ;________________________________________________________________________
   606                  
   607                  ;	ServeRX --> SLVSP message queues (INPUT), 1/slave support proc.
   608   001A =         INQCB$SIZE	equ	26	;constant used for index calculation
   609                  inqcb$array:			;ARRAY BASE NAME
   610                  ;
   611                  ;	generate INQCBs as required
   612   0030 #         ??xx	set	'0'
   613                  	rept	NSLAVES
   614                  	  ds	2		;;link
   615                  	  db	4eh,74h,77h,72h	;;common name is NTwrkQI
   616                  	  db	6bh,51h,49h	;;(macro can't do lower case)
   617                  	  db	??xx		;;slave ID
   618                  	  dw	2,1		;;msglen, nmbmsgs
   619                  	  ds	12		;;MP/M pointers and buffers
CP/M RMAC ASSEM 1.1	#014	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   620                  ??xx	  set	??xx + 1
   621                    if (??xx EQ ('9'+1))
   622                  ??xx	    set	??xx + 7
   623                    endif
   624                  	endm
   625   04AC+          	  DS	2
   626   04AE+4E747772  	  DB	4EH,74H,77H,72H
   627   04B2+6B5149    	  DB	6BH,51H,49H
   628   04B5+30        	  DB	??XX
   629   04B6+02000100  	  DW	2,1
   630   04BA+          	  DS	12
   631   04C6+          	  DS	2
   632   04C8+4E747772  	  DB	4EH,74H,77H,72H
   633   04CC+6B5149    	  DB	6BH,51H,49H
   634   04CF+31        	  DB	??XX
   635   04D0+02000100  	  DW	2,1
   636   04D4+          	  DS	12
   637                  
   638                  ;	SLVSP --> NETWRKIF queue (OUTPUT)
   639   04E0           outQCB:	ds	2		;link
   640   04E2 4E7477726B	db	'NtwrkQO0'	;name
   641   04EA 02001000  	dw	2,16		;msglen, nmbmsgs
   642   04EE           	ds	48		;Used by MP/M
   643                  
   644                  ;	free buffer list management queue
   645                  buffQCB:
   646   051E           	ds	2		;link
   647   0520 4672656542	db	'FreeBuff'	;name
   648   0528 02001000  	dw	2,16		;msglen, nmbmsgs
   649   052C           	ds	48		;reserved for MP/M
   650                  
   651                  
   652                  ;	global message buffer pool
   653   055C           msgbuffs:	ds	NMSG$BUFFS * BUFFSIZE
   654                  
   655                  ;	Utility Procedure to allow indirect BDOS/XDOS access as needed by RSP
   656   08A4 2A0000    bdos:	lhld	bdosadr
   657   08A7 E9        	pchl
   658                  
   659                  page
CP/M RMAC ASSEM 1.1	#015	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   660                  
   661                  ;________________________________________________________________________
   662                  ;________________________________________________________________________
   663                  ;									;
   664                  ;	low level omninet support routines				;
   665                  ;________________________________________________________________________
   666                  ;________________________________________________________________________
   667                  
   668                  ;	Transporter mutual exclusion QUEUE
   669   08A8           omniQ:	ds	2
   670   08AA 4D586F6D6E	db	'MXomniQ '
   671   08B2 00000100  	dw	0,1			;msglen, nmsgs
   672   08B6           	ds	12			;dqph,nqph,msgin,msgout,msgcnt,buff
   673                  
   674                  ;	UQCB used by omni$init to load MX Q
   675   08C2 A808C608  omni$init$uqcb:	dw	omniQ,init$mx$msg
   676   08C6 0000      init$mx$msg:	dw	0
   677                  
   678                  
   679                  ;	Initialization transporter control block
   680                  inittcb:
   681   08C8 20        	db	20h			;command
   682   08C9 00        	db	0			;result hi
   683                  initrsltp:
   684   08CA 0000      	db	0,0			;result middle and low
   685                  ;
   686                  initrslt:
   687   08CC 00        	db	0			;result block for init
   688                  
   689                  
   690                  ;	initializes transporter hardware and return its network ID code in A
   691                  omni$init:
   692   08CD 11A808    	lxi	d,omniQ
   693   08D0 0E86      	mvi	c,MAKEQ
   694   08D2 CDA408    	call	bdos		;create hardware MX Q
   695   08D5 11C208    	lxi	d,omni$init$uqcb ;send it one message
   696   08D8 0E8B      	mvi	c,WRITEQ
   697   08DA CDA408    	call bdos
   698                    if INTERRUPT
   699                  	call int$init		;(optional) setup interrupt system
   700                    endif
   701   08DD 21CC08    	lxi	h,initrslt	;install result block pointer in initialization
   702   08E0 55        	mov	d,l		;TCB
   703   08E1 5C        	mov	e,h		;NOTE: NOT 8080 order, MSB,LSB
   704   08E2 EB        	xchg
   705   08E3 22CA08    	shld	initrsltp
   706                  ;
   707   08E6 01C808    	lxi	b,inittcb	;post initialization command block to 
   708   08E9 CDF508    	call	omnistrobe	;hardware
   709   08EC D8        	rc			;cy=1 means can't talk to hardware
   710                  ;
   711   08ED 01CC08    	lxi	b,initrslt	;wait for a completion from operation
   712   08F0 CD2309    	call	omni$wfdone
   713   08F3 B7        	ora	a
CP/M RMAC ASSEM 1.1	#016	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   714   08F4 C9        	ret			;return ID/result code to caller with flags set
   715                  
   716                  
   717                  ;	sends the command block pointer in BC to transporter hardware
   718                  omni$strobe:
   719   08F5 210200    	lxi	h,2		;first preset result code byte in
   720   08F8 09        	dad	b		;result block TCB result field --> to 0ffh
   721   08F9 7E        	mov	a,m
   722   08FA 23        	inx	h
   723   08FB 6E        	mov	l,m
   724   08FC 67        	mov	h,a
   725   08FD 36FF      	mvi	m,0ffh
   726                  ;
   727   08FF AF        	xra	a		;send bits 23-16 of ptr to hardware (always 0)
   728   0900 CD0A09    	call	omni$st
   729   0903 D8        	rc			;carry means can't talk to hardware
   730                  ;
   731   0904 78        	mov	a,b		;send bits 15-8 of ptr to hardware
   732   0905 CD0A09    	call	omni$st
   733   0908 D8        	rc
   734                  ;
   735   0909 79        	mov	a,c		;send bits 7-0 of ptr to hardware
   736                  				;fall into omni$st to send last byte
   737                  
   738                  ;	called by omni$strobe to send one byte to transporter when ready
   739                  ;	(waits a reasonable time for transporter to come ready and if
   740                  ;	it doesn't, returns with carry set;  this is a fatal error) returns
   741                  ;	cy=0 if succeeds
   742                  omni$st:
   743   090A F5        	push	psw		;save data for now
   744   090B 1150C3    	lxi	d,50000		;set timeout
   745                  omni$st0:
   746   090E DBF9      	in	OMNI$STAT	;see if transporter will accept byte
   747   0910 E610      	ani	OMNI$RDY
   748   0912 CA1A09    	jz	omni$st1	;if busy, go decrement timeout and retry
   749   0915 F1        	pop	psw		;else output the byte and return with CY=0
   750   0916 D3F8      	out	OMNI$DATA
   751   0918 B7        	ora	a
   752   0919 C9        	ret
   753                  omni$st1:
   754   091A 1B        	dcx	d		;loop back if not timeout yet
   755   091B 7B        	mov	a,e
   756   091C B2        	ora	d
   757   091D C20E09    	jnz	omni$st0
   758   0920 F1        	pop	psw
   759   0921 37        	stc
   760   0922 C9        	ret			;else return CY=1 as error flag
   761                  
   762                  
   763                  ;	routine waits for a completion to occur on the result block
   764                  ;	pointed to by BC.  This routine is used by the initialization
   765                  ;	and receiver processes.  If there is no interrupt hardware in
   766                  ;	the system, ONLY ONE MESSAGE CAN BE RECEIVED PER CLOCK TICK of
   767                  ;	the system clock.  This will considerably reduce server throughput
CP/M RMAC ASSEM 1.1	#017	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   768                  ;	in most systems.
   769                  omni$wfdone:
   770                  wfrxdone:
   771   0923 0A        	ldax	b		;all completion codes are < 0f0h
   772   0924 FEF0      	cpi	0f0h		;see if already done before suspending caller
   773   0926 D8        	rc			;yes, return immediately
   774                  ;	else suspend caller until a completion occurs
   775   0927 C5        	push	b
   776                    if INTERRUPT
   777                  	lxi	d,OMNI$FLAG	;wait for ISR to set flag
   778                  	mvi	c,FLAGWAITF
   779                  	call	bdos
   780                    else
   781   0928 110100    	lxi	d,1		;if no ISR, poll result block once/tick
   782   092B 0E8D      	mvi	c,DELAY
   783   092D CDA408    	call	bdos
   784                    endif
   785   0930 C1        	pop	b
   786   0931 C32309    	jmp	omni$wfdone
   787                  
   788                  ;	As above but instead polls continually to give transmitter priority
   789                  ;	since transmitter usually unloads messages in less time than MP/M
   790                  ;	dispatch overhead, it is not worth suspending it.
   791                  ;	A timeout routine is included to avoid locking up system if hardware
   792                  ;	fails so diagnosing the problem is possible with RDT.
   793                  wftxdone:
   794   0934 1150C3    	lxi	d,50000		;initialize hardware fail timeout
   795   0937 0A        wftxd0:	ldax	b		;done yet?
   796   0938 FEF0      	cpi	0f0h
   797   093A 3F        	cmc			;set up carry properly in case of return
   798   093B D0        	rnc			;yes, return to caller with result in A, CY=0
   799   093C 1B        wftxd1:	dcx	d		;if not timeout, loop back
   800   093D 7B        	mov	a,e
   801   093E B2        	ora	d
   802   093F C23709    	jnz	wftxd0
   803   0942 37        	stc
   804   0943 C9        	ret			;else return to caller with CY=1 as error flag
   805                  
   806                  page
CP/M RMAC ASSEM 1.1	#018	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   807                  
   808                    if INTERRUPT
   809                  ;
   810                  ;	Since the CORVUS "ENGINEERING" transporter has no interrupt hardware
   811                  ;	associated with it, the details of the interrupt initialization and
   812                  ;	service routines will vary from system to system.  The skeleton of
   813                  ;	our code is provided here as a guide to understanding what is needed.
   814                  ;
   815                  ;	Routine initializes interrupt hardware and attaches ISR to XIOS
   816                  ;	at run-time (in somewhat bizarre fashion.)  It would be better
   817                  ;	to make your ISR a permanent part of your XIOS since if not
   818                  ;	used it does no harm to the system.
   819                  int$init:
   820                  	di
   821                  	mvi	a,(jmp)		;build jump in vector
   822                  	sta	(INT$VCTR)
   823                  	lxi	h,omni$isr
   824                  	shld	(INT$VCTR + 1)	;install new isr
   825                  	out	OMNI$ACK	;clear interrupt latch
   826                  	mvi	a,OMNI$ENABLE	;unmask transporter interrupt
   827                  	out	OMNI$MASK
   828                  ; this code does an extremely Klugey run-time linkage to needed XIOS routines
   829                  	lhld	1		;find CBOOT in MPM-II BIOS simulation table
   830                  	mvi	l,1
   831                  	mov	e,m
   832                  	inx	h
   833                  	mov	d,m
   834                  	push	d		;save to find exit$reg.
   835                  ;
   836                  	xchg			;need to go one more level to find real entry
   837                  	inx	h
   838                  	mov	e,m
   839                  	inx	h
   840                  	mov	d,m		;this is address of real CBOOT entry in XIOS
   841                  ;
   842                  	lxi	h,9		;calculate PDISP entry from CBOOT address
   843                  	dad	d
   844                  	shld	pdisp		;and save it in local vector		
   845                  ;
   846                  	lxi	d,3		;XDOS address is 3 bytes above PDISP
   847                  	dad	d
   848                  	shld	xd$adr		;save it in a local vector
   849                  ;
   850                  	pop	h		;get XIOS branch table address back
   851                  	mvi	l,40h		;calculate address of EXIT$REGION entry
   852                  	mov	e,m
   853                  	inx	h
   854                  	mov	d,m
   855                  	xchg
   856                  	shld	exit$region	;save it for later use in pre-empt routine
   857                  	ei
   858                  	ret
   859                  
   860                  ;	omninet isr sets the appropriate XDOS flag and causes a dispatch
CP/M RMAC ASSEM 1.1	#019	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

   861                  omni$isr:
   862                  	shld	svhl
   863                  	pop	h
   864                  	push	psw		;save PSW and HL
   865                  	shld	svret		;save return address
   866                  	lxi	h,0		;swap stacks
   867                  	dad	sp
   868                  	shld	svstk
   869                  	lxi	sp,isr$stk
   870                  	push	d		;save the other registers on new stack
   871                  	push	b
   872                  ;
   873                  	out	OMNI$ACK 	;clear interrupt latch
   874                  ;
   875                  	lhld	exit$region	; do a PRE-EMPT by patching a RET into table
   876                  	mov	a,m		; (Very KLUGEY but there's no other way.)
   877                  	push	psw		; save what was in XIOS branch table entry
   878                  	push	h		; and put a RET there to prevent XDOS from
   879                  	mvi	m,(RET)		; re-enabling interrupts
   880                  ;
   881                  	mvi	c,FLAGSETF	;call XDOS to set isr flag
   882                  	mvi	e,OMNI$FLAG
   883                  	call	xdos
   884                  ;
   885                  	pop	h
   886                  	pop	psw
   887                  	mov	m,a		;restore XIOS table entry
   888                  ;
   889                  	pop	b		;pop interrupted registers
   890                  	pop	d
   891                  	lhld	svstk		;restore interrupted stack
   892                  	sphl			;restore other regs. and exit
   893                  	pop	psw
   894                  	lhld	svret
   895                  	push	h
   896                  	lhld	svhl
   897                  	db	(JMP)		; via dispatcher
   898                  pdisp:	dw	0		;(link to dispatcher)
   899                  
   900                  xdos:	db	(JMP)		;special XDOS entry
   901                  xd$adr:	dw	0		;for ISR use
   902                  
   903                  ;	ISR data areas
   904                  exit$region:
   905                  	dw	0		;address of XDOS critical region exit routine
   906                  	ds	64		;isr stack space
   907                  isr$stk:
   908                  svhl:	dw	0		;temporary reg storage
   909                  svret:	dw	0
   910                  svstk:	dw	0		;careful, make sure all of .RSP is reserved
   911                  
   912                    endif ; of if INTERRUPT
   913                  
   914   0944           	end
CP/M RMAC ASSEM 1.1	#020	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

BDOS             08A4   110   113   225   233   244   248   251   269   280   288 
                        359   499   517   542   554   560   656#  694   697   779 
                        783 
BDOSADR          0000   117#  122#  656 
BUFFQCB          051E   190   224   466   645#
BUFFSIZE         0118    62#  209   210   502   653 
CNOTE            02BF   567#
CODESEG          0000   104#  118 
CONFIGTBL        02FA   221   254   575#
CR               000D   100#
CREATEP          0090    94#  247 
DEBUG            0000    54#  111 
DELAY            008D    92#  782 
DETACH           0093    96#  112 
DSPTCH           008E    93#
ERROUTMSG        0082   198   199#  328 
ERROUTUQCB       007E   198#  345 
FINDMATCH        0186   370#  385 
FLAGSETF         0085    88#  881 
FLAGWAITF        0084    87#  778 
FREELOOP         0248   493#  505 
GBUFUQCB         0072   190#  268 
GETSLVSP         0180   321   366#  392   402   406 
IDTBL            01B3   369   417#
INITMXMSG        08C6   675   676#
INITRSLT         08CC   686#  701   711 
INITRSLTP        08CA   683#  705 
INITRX           0098   167   220#
INITTCB          08C8   680#  707 
INITTX           0243   458   490#
INQCBARRAY       04AC   177   182   185   227   609#
INQCBSIZE        001A   180   236   608#
INTERRUPT        0000    56#  698   776   808 
INTVCTR          0038    72#  822   824 
INUQCB           0066   174#  421   425   427 
LF               000A   101#
LOGITOFF         019A   317   391#
LOGITON          01A3   311   401#
MAKEINQS         00AB   229#  240 
MAKEQ            0086    89#  223   232   243   693 
MSGBUFFS         055C   491   653#
NEWBUFF          0076   190   191#  272   299   327   349 
NMSGBUFFS        0003    63#  492   653 
NO               0000    51#   54    56 
NOTMATCH         0191   372   380#
NSLAVES          0002    59#   63   176   228   368   419   578   586   598   613 
OLDBUFF          0227   466   467#  494   557 
OMNIACK          00FA    79#  825   873 
OMNIBASE         00F8    68#   75    76    79    80 
OMNIDATA         00F8    75#  750 
OMNIDISABLE      0000    83#
OMNIENABLE       0001    82#  826 
OMNIFLAG         0008    70#  777   882 
OMNIINIT         08CD   220   691#
CP/M RMAC ASSEM 1.1	#021	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

OMNIINITUQCB     08C2   675#  695 
OMNIMASK         00FB    80#  827 
OMNIPENDING      0001    81#
OMNIQ            08A8   194   470   669#  675   692 
OMNIRDY          0010    77#  747 
OMNIRXUQCB       0078   194#  278   286 
OMNISOCKET       00A0    69#  206   478 
OMNIST           090A   728   732   742#
OMNIST0          090E   745#  757 
OMNIST1          091A   748   753#
OMNISTAT         00F9    76#  746 
OMNISTROBE       08F5   283   546   708   718#
OMNITXUQCB       0229   470#  540   552 
OMNIWFDONE       0923   712   769#  786 
OUTQCB           04E0   198   242   462   639#
OUTQMSG          0221   462   463#  519   556 
OUTUQCB          021D   461#  516 
PBUFUQCB         0223   466#  498   559 
PRINTF           0009    86#
READQ            0089    90#  267   279   515   541 
RSP              FFFF    55#  105 
RSTNUM           0007    71#   72 
RXL1             0138   310   315#
RXL2             0146   306   316   321#
RXL3             016C   312   318   322   349#
RXL4             0178   346   358#
RXLOOP           00E5   267#  360 
RXMXMSG          007C   194   195#
RXPRIORITY       0040    64#  109   161 
RXRETRY          00ED   271#  291   296 
RXRSLT           0090   213#  259   293 
RXRSLTP          0086   204#  263 
RXSENDERR        014C   313   319   326#
RXTCB            0084   202#  276   282 
SERVERXPD        0002   159#
SERVERXSTKTOP    0064   107   166#
SERVETXPD        01B9   246   451#
SETPRIORITY      0091    95#  108 
SRVRPDBASE       0444   588   592   594   597#
SRVRPDSIZ        0034    61#  589   598 
SRVRSTKSIZ       0096    60#  587   591   593 
SYDATAD          009A    97#  250 
TXLOOP           0268   514#  562 
TXMXMSG          022D   470   471#
TXPRIORITY       003F    65#  453 
TXRETRY          0297   544#  547 
TXRSLT           023B   484#  507   549 
TXRSLTP          0231   476#  511 
TXTCB            022F   474#  523   527   538   545 
UQCBLEN          0006   171#  422 
UQCBNTWRKQO0     021D   462#
WFRXDONE         0923   294   770#
WFTXD0           0937   795#  802 
WFTXD1           093C   799#
CP/M RMAC ASSEM 1.1	#022	SAMPLE SERVER NETWORK I/F FOR CORVUS OMNINET 20-OCT-82

WFTXDONE         0934   550   793#
WRITEQ           008B    91#  287   358   497   553   558   696 
XQCBMSG          0004   172#  354   421   425   427 
YES              FFFF    50#   51    55