[comp.sources.amiga] v89i171: xpr - external file transfer protocol library, Part02/02

page%swap@Sun.COM (Bob Page) (08/21/89)

Submitted-by: papa@pollux.usc.edu (Marco Papa)
Posting-number: Volume 89, Issue 171
Archive-name: comm/xpr.2

[this archive consists of only the document.  ..bob]

# This is a shell archive.
# Remove anything above and including the cut line.
# Then run the rest of the file through 'sh'.
# Unpacked files will be owned by you and have default permissions.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: SHell ARchive
# Run the following text through 'sh' to create:
#	xprotocol.doc
# This is archive 2 of a 2-part kit.
# This archive created: Sun Aug 20 23:01:48 1989
echo "extracting xprotocol.doc"
sed 's/^X//' << \SHAR_EOF > xprotocol.doc
X+----------------------------------------------------------------------+
X|                                                                      |
X|                  D I S C L A I M E R   N O T I C E                   |
X|                                                                      |
X|  This document and/or  portions of the material and  data furnished  |
X|  herewith,  was developed under sponsorship of the U.S. Government.  |
X|  Neither the U.S.  nor  the U.S.D.O.E.,  nor  the  Leland  Stanford  |
X|  Junior University, nor their employees,  nor their respective con-  |
X|  tractors, subcontractors, or their employees, makes  any warranty,  |
X|  express or implied, or assumes any liability or responsibility for  |
X|  accuracy,  completeness or  usefulness of any information, appara-  |
X|  tus, product or process disclosed, or represents that its use will  |
X|  not infringe privately-owned rights.  Mention of any product,  its  |
X|  manufacturer, or suppliers shall not, nor is it intended to, imply  |
X|  approval, disapproval, or fitness for any particular use. The U.S.  |
X|  and  the University at all times  retain the right to use and dis-  |
X|  seminate same for any purpose whatsoever.                           |
X|                                                                      |
X+----------------------------------------------------------------------+
X
X
X
X        XPR: External File Transfer Protocols as Amiga Libraries.
X        =========================================================
X
X                       Version - 10 July 1989
X
X                       (C) Copyright 1989 by
X
X                         W.G.J. Langeveld
X
X                 Stanford Linear Accelerator Center
X
X
X                              ABSTRACT
X                              ========
X
X                This document describes a standard method
X                of using Amiga shared libraries  for  the
X                implementation of  external file transfer
X                protocols, as (partially) implemented  in
X                the Amiga terminal emulator VLT.
X
X
X1. Introduction.
X================
X
X        One of the most frequently asked questions of the author of a
Xcommunications program is "Why don't you implement this wonderful file transfer
Xprotocol in addition to the 25 you already have?". Clearly, implementing more
XFTP's leads to larger code size and to increased product development time and
Xcustomer support requirements, unless there is a way to have the additional
Xprotocols available as separate entities. One obvious way is to put the
Xadditional FTP's in overlays, but that only mitigates the code size problem and
Xdoes not allow protocols to be used with communications programs of different
Xvendors. Better is to open the serial device as a shared port and to have a
Xcompletely separate program access it at the same time. However, this method has
Xthe disadvantage that shared use of a single serial port can lead to
Xunpredictable results unless there is a well-established priority system
Xenforcing which program is allowed to write to the device at which time. The
Xadvantage is that the FTP can now be developed separately and even by someone
Xother than the author of the communications program. There are variations
Xinvolving inter-process communication to add access control to the latter
Xsystem, but I will not go into further detail.
X        The system described here is based on Amiga shared libraries. The
Xlibrary implements a small number of primary functions, such as "Send File(s)"
Xand "Receive File(s)". These functions are called with a single argument, a
Xpointer to an XPR_IO structure. This structure contains a number of things, the
Xmore obvious one being a pointer to a null terminated string indicating which
Xfiles are to be sent or received and addresses of "call-back" functions inside
Xthe code of the communications program to access the serial device, which is
Xopened typically in exclusive access. The scheme described here opens the
Xpossibility for the Amiga community to write a multitude of file transfer
Xprotocols, all rather small in size because they don't contain any overhead,
Xthat work with any communications program following the rules outlined in this
Xdocument.
X        Possible problems with shared libraries are that they should be
Xreentrant and that they should, if possible, not open dos.library [1]. On the
Xother hand, these problems can easily be turned into advantages: for one,
Xreentrancy is not hard to accomplish and in addition when there are multiple
Xserial ports in use all of them can use the FTP with a single copy of the code.
XNot having to open dos.library can be accomplished by having call-back functions
Xthat provide all the DOS access needed in the original communications program.
XTypically these DOS functions are already linked into the original code anyway,
Xand call-backs have to be provided for serial port access in any case.
X        For the sake of reentrancy across calls to the external protocol library
X(XPR), a field for storing a pointer to a data area is added for use by the XPR
Xinternally.
X
X        Section 2 explains the library structure itself. Section 3 covers the
XXPR_IO structure and defines all the call-back functions. Section 4 describes an
Xexample library for a simple ASCII transfer without bells or whistles and will
Xshow how to code the library part of the call-backs. Section 5 shows how to set
Xup the interface on the communications program side.
X
X        Note: the examples are all for Manx C and assembler but should be easily
Xmodifyable for Lattice or any other language. Not all source files are given in
Xthis document. This archive, however, contains the example library plus all
Xfiles needed to link it and interface to it, for Manx. Specifically, the
Xroutines that interface to XPR from VLT are in the "comm-program" subdirectory,
Xand the sources to the library are in the "library" subdirectory.
X
X        I would like to thank Marco Papa of Felsina Software for his help in
Xworking out some of the details of the XPR standard.
X
X        Neither this document, nor the XPR standard, nor the other files in this
Xarchive are in the public domain, but they may be freely distributed and used
Xfor any purpose bearing in mind the stipulations given in the disclaimer above,
Xand with the proviso that in case of further distribution all files of this
Xarchive must remain together and unchanged.
X
X
XReference:
X[1] Jim Mackraz says that opening dos.library inside a library is not a good
X    idea.
X
X
X2. XPR libraries.
X=================
X
X        Each external FTP is implemented as a separate library which lives in
Xthe libs: directory. It is mandatory that the names of XPR libraries start with
Xthe three letters "xpr" so that they are easily identified. The template for the
Xname is xpr<protocol-name>.library, where <protocol-name> is a descriptive name
Xof the protocol that is implemented. Obvious examples would be xprkermit.library
Xand xprxmodem.library, but xprmykermit.library would be fine for a
Xuser-customized kermit implementation. When thinking of a name, the  implementer
Xof an XPR library should keep in mind that communication programs will likely
Xuse the <protocol-name> part in their XPR requester.
X        Each XPR library in turn has four public functions. The functions are:
X
X        XProtocolCleanup()
X        XprotocolSetup()
X        XprotocolSend()    and
X        XprotocolReceive()
X
Xin addition to the usual open, close expunge and reserved vectors. The library
Xskeleton is given in Appendix A.
X        Typically, a session with a terminal emulator using external protocols
Xwould consist of
X
X        1. Selecting an external protocol (Using e.g. a file requester
X           showing only those files in libs: starting with "xpr").
X        2. Retrieving the library base XProtocolBase of the selected protocol
X           using OpenLibrary().
X        3. (Allocating and) initializing an XPR_IO structure.
X        4. Optionally calling XProtocolSetup() with the initialized structure.
X        5. Optionally Calling XProtocolSend() and/or XprotocolReceive() once or
X           multiple times to transfer files.
X        6. Optionally calling XProtocolSetup() to change parameters or to send
X           special commands. Perhaps repeat 5.
X        7. Calling XprotocolCleanup() to deallocate any resources allocated by
X           XProtocolSetup(). (Deallocate the XPR_IO structure if needed).
X        8. Closing the library using CloseLibrary().
X        9. Repeat the process, or
X       10. Exit.
X
X        All four XPR functions take a single argument, a pointer to an XPR_IO
Xstructure, properly initialized as described in section 5. After
XXProtocolSetup() has been called, the same XPR_IO structure should be used for
Xcalls to any of the other functions. Only the xpr_filename field is allowed to
Xbe changed between  calls. In particular, the xpr_data field is for internal use
Xby the XPR library only! It should be initialized to NULL before calling
XXProtocolSetup() and should not be changed by the communications program.
XXProtocolSetup() should only be called at the request of the user.
XXProtocolCleanup() should always be called before the library is closed.
X        In the form of a sample program, the rules above look like this:
X
X/** MyWonderFullCommProgram.c
X*
X*   Just an example. An actual implementation would likely look different.
X*
X**/
X#include <stdio.h>
X#include <functions.h>
X#include "xproto.h"
X
Xstruct Library *XProtocolBase = NULL;
X
X#define SEND 1
X#define RECEIVE 2
X#define INITIALIZE 3
X
Xmain()
X{
X   struct XPR_IO io;
X   int user_said, Waiting_for_user_input();
X
X   XProtocolBase = OpenLibrary("xprascii.library", 0L);
X   if (XProtocolBase == NULL) {
X      printf("protocol not found\n");
X      exit(10);
X   }
X
X/*
X*   Initialize structure (see later).
X*/
X   xpr_setup(io);
X/*
X*   Retrieve the initalization string
X*/
X   Get_init_string_from_user_or_wherever(buffer);
X   io->xpr_filename = buffer;
X   XProtocolSetup(io);
X
X   while (user_said = Waiting_for_user_input(filename)) {
X      if (user_said == SEND) {
X         io->xpr_filename = filename;
X         XProtocolSend(io);
X      }
X      else if (user_said == RECEIVE) {
X         io->xpr_filename = filename;
X         XProtocolReceive(io);
X      }
X      else if (user_said == INITIALIZE) {
X         io->xpr_filename = NULL;
X         XProtocolSetup(io);
X      }
X   }
X
X   XProtocolCleanup(io);
X
X   CloseLibrary(XProtocolBase);
X
X   exit(0);
X}
X
X        Clearly, only one FTP can be active at any particular instant in the
Xlife of the session of the communications program. However, this is not really a
Xlimitation in practice, and can be worked around at the cost of some amount of
Xprogramming effort.
X        XProtocolSetup(), XProtocolSend(), XProtocolReceive() and
XXProtocolCleanup() return 0L on failure, non-zero on success.
X
X
X
X
X3. The XPR_IO structure.
X========================
X
X        The XPR_IO structure definition is given in Appendix B. The reader
Xshould keep in mind that the callback functions are to be implemented by the
Xauthor of the communications program, not by the author of the external
Xprotocol. However, most communications programs already have functions that
Xperform the operations listed here, so the implementation should not be too
Xdifficult. Also, the communications program author is not required, strictly
Xspeaking, to implement any of the functions: functions that are not implemented
Xshould be indicated by initializing the corresponding XPR_IO field to NULL.
XObviously, a minimum set of functions must be implemented in order to be useful.
XOn the other hand, it is up to the implementer of the external protocol to
Xdetermine if the given set of functions is sufficient to perform the protocol
Xtransfer. In case of missing functions (indicated by NULL fields in the XPR_IO
Xstructure) suitable default actions should be taken.
X
X        We will now examine all the fields of XPR_IO in detail. 
X
X
X3.1     char  *xpr_filename;
X----------------------------
X
X        The xpr_filename field is used primarily to pass null-terminated strings
Xcontaining a file name (or file names specified by wild cards) to the functions
XXProtocolSend() or XProtocolReceive(). The XPR implementer may elect to support
Xwild cards in the file name. Call-backs for finding the first and next filename
Xmatching the pattern are provided in the XPR_IO structure, but on the other hand
XXPR implementers should take care to check that these call-backs are implemented
Xby the communications program by testing the corresponding XPR_IO fields for
XNULL. Never assume that all call-backs are implemented! If a particular
Xcall-back without which the XPR cannot function is not implemented, the XPR
Xshould fail gracefully.
X
X        The xpr_filename field can also be used to pass an initialization string
Xto XProtocolSetup(). Typically, if this field is left NULL in a call to
XXProtocolSetup(), it would be the duty of XProtocolSetup() to query the user for
Xinitialization information, using the xpr_gets function (see later). If an
Xinitialization string is present, XProtocolSetup() should NOT query the user,
Xbut this is left to the discretion of the implementer of the protocol, as is the
Xprecise form of the initialization string. It is the duty of the communications
Xprogram to determine any default initialization strings for the protocol in
Xquestion. Suggested is the use of environment variables named for the protocols
Xthey refer to, containing the initialization string. For the simple Ascii
Xprotocol shown later, the user might have a statement like
X
X        set xprascii=50
X
Xin his startup sequence, or with AmigaDOS 1.3, a file called xprascii in his
Xenv: directory containing the letters "50" (50 referring here to the number of
Xticks delay between 80-character packets - obviously more extensive
Xinitialization might be needed).
X        Given the presence of such default information, XProtocolSetup() should
Xalways be called using the default initialization string right after opening the
Xlibrary. Conversely, a mechanism (menu option) should be present in the
Xcommunications program to change the settings by calling XProtocolSetup() with a
XNULL value for this field. On the other hand, if no default initialization
Xstring is present, the legal situation can arise that XProtocolSetup() is never
Xcalled.
X        It should be noted that XProtocolSetup() can be used to implement any
Xcommands not directly related to sending or receiving files. Examples that come
Xto mind are Kermit Bye and Finish. One should keep in mind, that typically the
Xcommunications program does not know what protocol it is running, much less what
Xcommands that protocol might support. When the user asks to "setup" the external
Xprotocol, XProtocolSetup() should be called with a NULL xpr_filename field,
Xand the external protocol should request a command, as stated before. In the
Xcase of an external Kermit protocol, the user might type a Bye or Finish, and
Xthe external protocol could act accordingly.
X
X        The xpr_filename field is ignored by the XProtocolCleanup() function.
X
X
X3.2    long (*xpr_fopen)();
X---------------------------
X
X        The xpr_fopen() call-back function works in most respects identically to
Xthe stdio function fopen(). Calling sequence:
X
X        long fp = (*xpr_fopen)(char *filename, char *accessmode)
X        D0                     A0              A1
X
XThe result is a FILE structure, but one should not count on it being a
Xparticular one, since it may be compiler dependent. The return value should only
Xbe used in calls to other stdio functions. The only accesmodes available are
X"r"  (read-only)
X"w"  (write-only, create new file if none exists, truncate existing file)
X"a"  (write-only, create new file if none exists, append to existing file)
X"r+" (same as "r", but may also write)
X"w+" (same as "w", but may also read)
X"a+" (same as "a", but may also read).
X        An error return is indicated when the function returns NULL.
X        Note that the arguments must be passed in registers A0 and A1
Xrespectively. See also section 4.
X
X
X3.3     long (*xpr_fclose)();
X-----------------------------
X
X        The xpr_fclose() call-back function works in most respects identically
Xto the stdio function fclose(). Calling sequence:
X
X        (*xpr_fclose)(long filepointer)
X                      A0
X
XNote that the argument must be passed in register A0.
X
X
X3.4     long (*xpr_fread)();
X----------------------------
X
X        The xpr_fread() call-back function works in most respects identically to
Xthe stdio function fread(). Calling sequence:
X
X        long count = (*xpr_fread)(char *buffer, long size, long count,
X        D0                        A0            D0         D1
X
X                                  long fileptr)
X                                  A1
X
XThe function returns the actual number items read. The size argument is in bytes.
XThe function returns 0 on error or end of file.
X
X
X3.5     long (*xpr_fwrite)();
X-----------------------------
X
X        The xpr_fwrite() call-back function works in most respects identically
Xto the stdio function fwrite(). Calling sequence:
X
X        long count = (*xpr_fwrite)(char *buffer, long size, long count,
X        D0                         A0            D0         D1
X
X                                  long fileptr)
X                                  A1
X
XThe function returns the actual number items written. The size argument is in
Xbytes. The function returns 0 on failure.
X
X
X3.6     long (*xpr_sread)();
X----------------------------
X
X        The xpr_sread() call-back function has the following calling sequence:
X
X        long count = (*xpr_sread)(char *buffer, long size, long timeout)
X        D0                        A0            D0         D1
X
XThe first argument is a pointer to a buffer to receive the characters from the
Xserial port, with a size specified in the second argument. The third item is a
Xtimeout in microseconds. The timeout may be set to 0L if the objective is to
Xjust read any characters that may currently be available. When this argument is
Xnon-zero, the function will not return until either the timeout period has
Xexpired, or the buffer has filled up. The function returns the actual number of
Xcharacters put into the buffer, or -1L on error or timeout.
X        Note: the value 0L for the timeout argument is a special case. Remember
Xthat AmigaDOS 1.3 may have problems with small non-zero values for timeouts.
X
X
X3.7     long (*xpr_swrite)();
X-----------------------------
X
X        The xpr_swrite() call-back function has the following calling sequence:
X
X        long status = (*xpr_swrite)(char *buffer, long size)
X        D0                          A0            D0
X
XThis function writes a buffer with the given size to the serial port. It returns
X0L on success, non-zero on failure.
X
X3.8     long (*xpr_sflush)();
X----------------------------
X
X        The xpr_sflush call-back function has the following calling sequence:
X
X        long status = (*xpr_sflush)()
X        D0
X
XThis function flushes all the data in the serial port input buffer.  It is
Xtypically used to recover after a protocol error. The function returns 0L on
Xsuccess, non-zero on failure.
X
X3.9     long (*xpr_update)();
X-----------------------------
X
X        The xpr_update() call-back function has the following calling sequence:
X
X        (*xpr_update)(struct XPR_UPDATE *updatestruct)
X                      A0
Xwhere:
X
Xstruct XPR_UPDATE {     long  xpru_updatemask;
X                        char *xpru_protocol;
X                        char *xpru_filename;
X                        long  xpru_filesize;
X                        char *xpru_msg;
X                        char *xpru_errormsg;
X                        long  xpru_blocks;
X                        long  xpru_blocksize;
X                        long  xpru_bytes;
X                        long  xpru_errors;
X                        long  xpru_timeouts;
X                        long  xpru_packettype;
X                        long  xpru_packetdelay;
X                        long  xpru_chardelay;
X                        char *xpru_blockcheck;
X                        char *xpru_expecttime;
X                        char *xpru_elapsedtime;
X                        long  xpru_datarate;
X                        long  xpru_reserved1;
X                        long  xpru_reserved2;
X                        long  xpru_reserved3;
X                        long  xpru_reserved4;
X                        long  xpru_reserved5;
X                   }
X
XThis function is intended to communicate a variety of values and strings from
Xthe external protocol to the communications program for display. Hence, the
Xdisplay format itself (requester, text-I/O) is left to the implementer of the
Xcommunications program.
X        The mask xpru_updatemask indicates which of the other fields are valid,
Xi.e. have had their value updated. It is possible to update a single or multiple
Xvalues. Values that the external protocol does not use can be indicated by a
XNULL for pointers and -1L for longs.
X        The possible bit values for the xpru_updatemask are:
X
X                #define XPRU_PROTOCOL           0x00000001L
X                #define XPRU_FILENAME           0x00000002L
X                #define XPRU_FILESIZE           0x00000004L
X                #define XPRU_MSG                0x00000008L
X                #define XPRU_ERRORMSG           0x00000010L
X                #define XPRU_BLOCKS             0x00000020L
X                #define XPRU_BLOCKSIZE          0x00000040L
X                #define XPRU_BYTES              0x00000080L
X                #define XPRU_ERRORS             0x00000100L
X                #define XPRU_TIMEOUTS           0x00000200L
X                #define XPRU_PACKETTYPE         0x00000400L
X                #define XPRU_PACKETDELAY        0x00000800L
X                #define XPRU_CHARDELAY          0x00001000L
X                #define XPRU_BLOCKCHECK         0x00002000L
X                #define XPRU_EXPECTTIME         0x00004000L
X                #define XPRU_ELAPSEDTIME        0x00008000L
X                #define XPRU_DATARATE           0x00010000L
X
X        The other fields of the XPR_UPDATE structure have the following
Xmeaning:
X
Xxpru_protocol    -- a string that indicates the name of the protocol used
Xxpru_filename    -- the name of the file currently sent or received
Xxpru_filesize    -- the size of the file
Xxpru_msg         -- a "generic" message (50 characters or less)
Xxpru_errormsg    -- an "error" message  (50 characters or less)
Xxpru_blocks      -- number of transferred blocks
Xxpru_blocksize   -- size of most recently transferred block (bytes)
Xxpru_bytes       -- number of transferred bytes
Xxpru_errors      -- number of errors
Xxpru_timeouts    -- number of timeouts
Xxpru_packettype  -- type of packet (e.g. Kermit 'D'-packet)
Xxpru_packetdelay -- delay between packets in msec
Xxpru_chardelay   -- delay between characters in msec
Xxpru_blockcheck  -- block check type (e.g. "Checksum", "CRC-16", "CRC-32")
Xxpru_expecttime  -- expected transfer time (e.g. "5 min 20 sec", "00:05:30")
Xxpru_elapsedtime -- elapsed time from start of transfer (see xpru_expecttime)
Xxpru_datarate    -- rate of data transfer expressed in characters per second.
Xxpru_reserved1   -- for further expansion
X ...         .   --  ...
Xxpru_reserved5   -- for further expansion
X
X        The communications program is free to ignore any field and to only update
Xthe ones it can handle.
X        If xpru_updatemask is equal to -1L, then ALL fields are either valid or 
Xare unambiguously valued to indicate they are unused: NULL for pointers and -1L
Xfor longs.
X        When writing an external protocol, it is advisable to keep any strings
Xas short as possible, and not longer than about 50 characters. Remember, if your
Xstrings are too long, they may overflow whatever display mechanism the
Xcommunications program has chosen. It is also advisable to fill in as many
Xfields as you can, since the communications program may not choose to display
Xthe ones you favor. When writing a communications program interface to XPR, on
Xthe other hand, remember that strings can be as much as 50 characters long. If
Xyou don't receive your favorite variables, it may be possible to compute them
Xfrom those that are given. It is good practice for the external protocol to call
Xxpr_update before starting the transfer with a message in the xpru_msg field
Xindicating whether the protocol is sending or receiving a file.
X        The XPR_UPDATE structure must be provided by the external protocol, and
Xmust, of course be allocated either on the stack (as a local variable) or using
XAllocMem or malloc(). This is needed to ensure reentrancy. In general, it is a
Xgood idea to keep the entire library reentrant, since more than one
Xcommunications program may be using the same code simultaneously.
X
X
X3.10     long (*xpr_chkabort)();
X-------------------------------
X
X        The xpr_chkabort() call-back function has no arguments:
X
X        long status = (*xpr_chkabort)()
X        D0
X
XWhen it returns non-zero, it means that the user has requested an abort. It is
Xpossible to implement levels of abort by returning 1L, 2L, 3L, etc, depending on
Xthe user's actions. The highest level of abort is -1L, which should be
Xinterpreted to mean stop all actions and return. The chkabort function should be
Xcalled reasonably frequently.
X
X
X3.11    long (*xpr_chkmisc)();
X------------------------------
X
X        The xpr_chkmisc() call-back function has no arguments and returns
Xnothing.
X
X        (*xpr_chkmisc)()
X
XIt is intended to give the communications program that is currently executing
Xthe external protocol transfer a chance to service its various message ports and
Xto respond to user actions. It should be called on a regular basis.
X
X
X3.12    long (*xpr_gets)();
X---------------------------
X
X        The xpr_gets() call-back function works somewhat like the stdio function
Xgets(). Calling sequence:
X
X        long status = (*xpr_gets)(char *prompt, char *buffer)
X        D0                        A0            A1
X
XThe first argument is a pointer to a string containing a prompt, to be displayed
Xby the communications program in any manner it sees fit. The second argument
Xshould be a pointer to a buffer to receive the user's response. It should have a
Xsize of at least 256 bytes. The function returns 0L on failure or user
Xcancellation, non-zero on success. The buffer has to be supplied by the XPR.
X
X
X3.13    long (*xpr_setserial)();
X--------------------------------
X
X        The xpr_setserial() call-back function has the following calling
Xsequence:
X
X        long oldstatus = (*xpr_setserial)(long newstatus)
X        D0                                D0
X
XThis function returns the current serial device status in encoded form. If the
Xnewstatus argument is -1L, the serial device status will not be changed.
XOtherwise the serial device status will be changed to newstatus. If oldstatus
Xis returned as -1L, the call failed and the serial status was not changed.
X        Note: if the serial device status is changed with this function, the 
Xexternal protocol must change the status back to oldstatus before returning.
X
X        serial status longword:
X        .......................
X
X        byte 0:         as the SerFlags field in IOExtSer structure.
X                bit 0:  - parity on if set
X                bit 1:  - parity odd if set
X                bit 2:  - 7-wire protocol enabled if set
X                bit 3:  - queued break if set
X                bit 4:  - rad-boogie if set
X                bit 5:  - shared if set
X                bit 6:  - EOF mode if set
X                bit 7:  - Xon/Xoff disabled if set
X        byte 1:         summary of other settings
X                bit 0:  - enable mark/space parity if set
X                bit 1:  - parity mark if set, space otherwise
X                bit 2:  - 2 stop bits if set, 1 otherwise
X                bit 3:  - read wordlength is 7 if set, 8 otherwise
X                bit 4:  - write wordlength is 7 if set, 8 otherwise
X                bit 5:  - not used
X                bit 6:  - not used
X                bit 7:  - not used
X        byte 2:         specifies one of a limited set of baud rates, as in
X                        preferences.h.
X                        -    110 baud =  0
X                        -    300 baud =  1
X                        -   1200 baud =  2
X                        -   2400 baud =  3
X                        -   4800 baud =  4
X                        -   9600 baud =  5
X                        -  19200 baud =  6
X                        -   midi      =  7
X                        -  38400 baud =  8
X                        -  57600 baud =  9
X                        -  76800 baud = 10
X                        - 115200 baud = 11
X        byte 3:         not used
X
X
X3.14    long (*xpr_ffirst)();
X-----------------------------
X
X        The xpr_ffirst() call-back function has the calling sequence:
X
X        long stateinfo = (*xpr_ffirst)(char *buffer, char *pattern)
X        D0                             A0            A1
X
XThe first argument is a buffer to receive the first filename that matches the
Xpattern in the second argument. The function returns 0L if no file matching the
Xpattern was found, non-zero otherwise. The buffer should have a size of at least
X256 bytes and is provided by the XPR. See also 3.14.
X
X
X3.15    long (*xpr_fnext)();
X----------------------------
X
X        The xpr_fnext() call-back function has the calling sequence:
X
X        long stateinfo = (*xpr_fnext)(long oldstate, char *buffer, char *pattern)
X        D0                            D0             A0            A1
X
XThe first argument is a buffer to receive the next filename that matches the
Xpattern in the second argument. The function returns 0L if no further file
Xmatching the pattern was found, non-zero otherwise. The buffer should have a
Xsize of at least 256 bytes and is provided by the XPR.
X        Note: the value returned by xpr_ffirst and xpr_fnext may be used by the
Ximplementing communications program to maintain state information, but the
Xmechanism is up to the implementer. If reentrancy is not required, state
Xinformation may be kept in global variables by the implementer, and the oldstate
Xargument can be ignored. However, the external protocol implementation must pass
Xthe stateinfo variable returned by ffirst or fnext to the next invocation of
Xfnext.
X
X
X3.16    long (*xpr_finfo)();
X----------------------------
X
X        The xpr_finfo() call-back function has the calling sequence:
X
X        long info = (*xpr_finfo)(char *filename, long typeofinfo)
X        D0                       A0              D0
X
XThis function returns information about a file given its name and the type of
Xinformation requested. Notice that some information may not be accessible if
Xthe file is already write locked. Therefore, you should call this function
X(where appropriate) before opening the file.
X
X        typeofinfo value:       resulting info:              on failure:
X        ..................................................................
X
X        1L                      file size (bytes)            0L
X
X        2L                      file type: 1L is binary,     0L
X                                           2L is text.
X
X        (other values)          (to be determined)
X
X
X3.17    long  *xpr_fseek();
X---------------------------
X
X        The xpr_fseek() call-back function works in most respects identically to
Xthe stdio function fseek(). Calling sequence:
X
X        long status = (*xpr_fseek)(long fileptr, long offset, long origin)
X        D0                         A0            D0           D1
X
XThis function sets the current position of a file to "offset" from the 
Xbeginning (origin = 0), current position (origin = 1) or end (origin = 2) of
Xthe file.
XThe function returns 0 on success.
X
X
X3.18    long  *xpr_extension;
X-----------------------------
X
X        This field indicates how many extension fields follow this structure.
XBefore using any functions or fields defined in section 3.20 and later, the
XXPR must check that the desired function is indeed present by ensuring that
Xxpr_extension is larger than the position of the function beyond the xpr_data
Xfield.
X
X
X3.19    long  *xpr_data;
X------------------------
X
X        This field is for internal use by the external protocol. Typically the
Xfield is initialized to point to a structure containing information extracted
Xfrom the initialization string handed to or retrieved by the XProtocolSetup()
Xfunction, see section 2. The structure should be deallocated and the field
Xrestored to NULL by the XProtocolCleanup() function. The communications program
Xshould never access this field, except when initializing the XPR_IO structure:
Xthe field should be initialized to NULL.
X
X
X3.20    long  *xpr_options();
X-----------------------------
X
X        This function is in the first extension field of the XPR_IO structure.
XOnly use this function if the value of the xpr_extension field is 1L or larger.
XThe calling sequence is:
X
X        long status = (*xpr_options)(long n, struct xpr_option *opt[])
X        D0                           D0      A0
X
XThe function passes to the comm program a pointer to an array of n xpr_option
Xstructures, where n is limited to 31. The xpr_option structures are defined as
Xfollows:
X
Xstruct xpr_option {
X   char *xpro_description;      /* description of the option                  */
X   long  xpro_type;             /* type of option                             */
X   char *xpro_value;            /* pointer to a buffer with the current value */
X   long  xpro_length;           /* buffer size                                */
X}
X
XValid values for xpro_type are:
X
X#define XPRO_BOOLEAN 1L         /* xpro_value is "yes", "no", "on" or "off"   */
X#define XPRO_LONG    2L         /* xpro_value is string representing a number */
X#define XPRO_STRING  3L         /* xpro_value is a string                     */
X
X        The array is allocated and initialized by the XPR to default values. If
Xthe comm program implements this function, it should display the description of
Xthe option and its current value to the user and allow him/her to change them.
XThis could be accomplished either by dynamically building a requester or by
Xdisplaying each line one at a time and allow the user to enter new values or
Xaccept the default. Options that have boolean values could be implemented by the
Xcomm program as boolean gadgets, but the new value must be returned as "yes" or
X"on" for logical 1 or "no" or "off" for logical 0 in the xpro_value buffer, and
Xthat long values must be converted to a string and copied to the xpro_value
Xbuffer. Note, that the XPR, if it uses this function must recognize both "yes"
Xand "on" for logical 1 and "no" and "off" for logical 0. For options that have
Xstring values, the comm program must ensure that the new string selected by the
Xuser fits in the value buffer as determined by the xpro_length field. The buffer
Xis supplied by the XPR.
X        For example, when selecting a ZMODEM based XPR the following array of
Xxpr_option structures could be passed to the comm program:
X
Xxpro_description                xpro_value    xpro_type
X--------------------------------------------------------------
XConvert NL to NL/CR             no            XPRO_BOOLEAN
XEscape only CTRL chars          yes           XPRO_BOOLEAN
XEscape ALL chars                no            XPRO_BOOLEAN
XSend full pathname              yes           XPRO_BOOLEAN
XSend 1K blocks                  no            XPRO_BOOLEAN
XSubpacket length                512           XPRO_LONG
XDisable 32-bit CRC              no            XPRO_BOOLEAN
XProtect destination file        no            XPRO_BOOLEAN
XTimeout value (sec)             10            XPRO_LONG
XDelete after transmission       no            XPRO_BOOLEAN
XOverwrite existing file         no            XPRO_BOOLEAN
X
XNotice again, that the COMM program still knows nothing about the individual
Xoption items (and in fact there is no way for it to find out, in keeping with
Xthe philosophy of XPR). Also notice that a cheap way to implement this function
Xis to loop over the n supplied xpr_option's and to call the likely already
Ximplemented xpr_gets function with the option description and the value buffer.
X        It is important to follow a few rules when calling this function: the
Xdescription strings should be 25 characters or less. The value strings can be
Xany length up to 255 characters, but be aware that in a typical situation only
Xabout 10 to 15 of them will be displayed in a string requester.
X        The return value, status, reflects which options have changed by having
Xthe corresponding bit set. The first option in the xpr_option array corresponds
Xto  bit 0 (low-order), etc. If the comm program decides to not detect whether
Xthe options changed or not, 0x07FFFFFFL may be returned, in effect specifying
Xthat all options have changed. If nothing changed, 0L is returned. If an error
Xoccurred, the function returns -1L.
X
X
X
X
X4. An example protocol.
X=======================
X
X        The following is an annotated listing of an ascii upload protocol.
XNotice that the files supplied in this archive are likely more up to date and
Xmore extensive than the example given here.
X
X/** xprascii.c
X*
X*   These are the protocol transfer routines for a simple ASCII upload.
X*
X**/
X#include <exec/exec.h>
X#include <functions.h>
X#include <stdio.h>
X/*
X*   xproto.h is the include file given in Appendix B.
X*/
X#include "xproto.h"
X/*
X*   The following two strings must exist.
X*/
Xchar  XPRname[]   = "xprascii.library";
Xchar  XPRid[]     = "xprascii 0.9 (May 89)\r\n";
XUWORD XPRrevision = 9;
X
Xlong atol();
X/*
X*   The callxx...() routines are described later. They provide the 
X*   assembler interface from the XPR library to the call-back routines.
X*/
Xlong calla(), callaa(), callad(), calladd(), calladda();
X
Xchar *malloc();
X
X
X/**
X*
X*   Send a file
X*
X**/
Xlong XProtocolSend(IO)
Xstruct XPR_IO *IO;
X{
X   long fp, r, i;
X   long brkflag = 0, fl = 0L, sd = 0L;
X   long (*xupdate)(), (*xswrite)(), (*xfopen)(), (*xfclose)(), (*xfread)(),
X        (*xsread)(),  (*xchkabort)();
X   unsigned char *buff = NULL, *serbuff = NULL;
X   struct XPR_UPDATE xpru;
X
X/*
X*   These are the call-backs we need. If any of them isn't provided, quit.
X*   Could do some error reporting if at least xupdate is there.
X*/
X   if ((xupdate    = IO->xpr_update)   == NULL) return(0L);
X   if ((xswrite    = IO->xpr_swrite)   == NULL) return(0L);
X   if ((xfopen     = IO->xpr_fopen)    == NULL) return(0L);
X   if ((xfclose    = IO->xpr_fclose)   == NULL) return(0L);
X   if ((xfread     = IO->xpr_fread)    == NULL) return(0L);
X   if ((xsread     = IO->xpr_sread)    == NULL) return(0L);
X   if ((xchkabort  = IO->xpr_chkabort) == NULL) return(0L);
X/*
X*   Allocate a few buffers.
X*/
X   buff    = (unsigned char *) malloc(80);
X   serbuff = (unsigned char *) malloc(80);
X/*
X*   If we ran out of memory, print a message.
X*   The argument needs to go in A0: calla does this for us. 
X*/
X   if (buff == NULL || serbuff == NULL) {
X      xpru.xpru_updatemask = XPRU_ERRORMSG;
X      xpru.xpru_errormsg   = "Ran out of memory!";
X      calla(xupdate, &xpru);
X      return(0L);
X   }
X/*
X*   Read the send delay, if a XProtocolSetup() was done before.
X*   If send delay is too large, cut it off at 10 seconds.
X*   In this example, the xpr_data field contains a null terminated string
X*   containing the number of ticks to delay each 80 characters.
X*/
X   if (IO->xpr_data) {
X      sd = atol(IO->xpr_data);
X      if (sd > 500L) sd = 500L;
X   }
X/*
X*   Open the file. One could do wild card detection here.
X*   xfopen requires two arguments, in a0 and a1 respectively.
X*   Again, this must be done in assembler, and callaa does it.
X*/
X   fp = callaa(xfopen, IO->xpr_filename, "r");
X   if (fp == NULL) {
X      free(buff);
X      free(serbuff);
X      xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_FILENAME;
X      xpru.xpru_errormsg   = "Failed to open input file";
X      xpru.xpru_filename   = IO->xpr_filename;
X      calla(xupdate, &xpru);
X      return(0L);
X   }
X/*
X*   Start the transfer. See 3.8 for a discussion on how to implement
X*   xupdate. 
X*/
X   xpru.xpru_updatemask = XPRU_MSG | XPRU_FILENAME;
X   xpru.xpru_msg        = "Starting ASCII Send";
X   xpru.xpru_filename   = IO->xpr_filename;
X   calla(xupdate, &xpru);
X/*
X*   Now read 80 byte chunks from the file using xfread.
X*   xfread requires four arguments, a0, d0, d1 and a1.
X*/
X   xpru.xpru_blocks = 0L;
X   while (r = calladda(xfread, buff, 1L, 80L, fp)) {
X/*
X*   Convert line feeds to carriage returns before sending to host.
X*   fl counts the characters. Display how many characters are sent.
X*/
X      for (i = 0L; i < r; i++) if (buff[i] == '\n') buff[i] = '\r';
X      fl += r;
X      xpru.xpru_updatemask = XPRU_BYTES | XPRU_BLOCKS | XPRU_BLOCKSIZE;
X      xpru.xpru_bytes      = fl;
X      xpru.xpru_blocks++;
X      xpru.xpru_blocksize  = r;
X      calla(xupdate, &xpru);
X      callad(xswrite, buff, r);
X/*
X*   Every 80 bytes, put out a message and delay if requested.
X*/
X      xpru.xpru_updatemask  = XPRU_PACKETDELAY;
X      xpru.xpru_packetdelay = sd * 20L;  /* msec! */
X      calla(xupdate, &xpru);
X/*
X*   Can't use Delay() here, because Delay() is in dos.library!
X*   However writing an equivalent function using the timer.device is
X*   trivial.
X*/
X      TimeOut(sd);
X/*
X*   Eat any characters that might arrive from the serial port.
X*   calladd stores arg1 in a0, arg2 in d0, arg3 in d1.
X*   We're not really waiting for any characters: use a timeout of 0L.
X*/
X      while (calladd(xsread, serbuff, 80L, 0L) > 0L) ;
X/*
X*   Check for "abort" here. Perhaps should call chkmisc() as well.
X*/
X      if (brkflag = xchkabort()) break;
X   }
X/*
X*   Close the file
X*/
X   calla(xfclose, fp);
X   free(buff);
X   free(serbuff);
X/*
X*   If we got here through chkabort() say Aborted.
X*/
X   xpru.xpru_updatemask       = XPRU_MSG;
X   if (brkflag) xpru.xpru_msg = "Aborted";
X   else         xpru.xpru_msg = "Done"; 
X   calla(xupdate, &xpru);
X   if (brkflag) return(0L);
X   else         return(1L);
X}
X
X
X/**
X*
X*   Receive a file.
X*
X**/
Xlong XProtocolReceive(IO)
Xstruct XPR_IO *IO;
X{
X   long fp, r, i;
X   long brkflag = 0, fl = 0L, sd = 0L;
X   long (*xupdate)(), (*xswrite)(), (*xfopen)(), (*xfclose)(), (*xfwrite)(),
X        (*xsread)(),  (*xchkabort)();
X   unsigned char *serbuff = NULL;
X   struct XPR_UPDATE xpru;
X
X/*
X*   These are the call-backs we need. If any of them isn't provided, quit.
X*   Could do some error reporting if at least xupdate is there.
X*/
X   if ((xupdate    = IO->xpr_update)   == NULL) return(0L);
X   if ((xswrite    = IO->xpr_swrite)   == NULL) return(0L);
X   if ((xfopen     = IO->xpr_fopen)    == NULL) return(0L);
X   if ((xfclose    = IO->xpr_fclose)   == NULL) return(0L);
X   if ((xfwrite    = IO->xpr_fwrite)   == NULL) return(0L);
X   if ((xsread     = IO->xpr_sread)    == NULL) return(0L);
X   if ((xchkabort  = IO->xpr_chkabort) == NULL) return(0L);
X/*
X*   Allocate a buffer.
X*/
X   serbuff = (unsigned char *) malloc(80);
X/*
X*   If we ran out of memory, print a message.
X*   The argument needs to go in A0: calla does this for us. 
X*/
X   if (serbuff == NULL) {
X      xpru.xpru_updatemask = XPRU_ERRORMSG;
X      xpru.xpru_errormsg   = "Ran out of memory!";
X      calla(xupdate, &xpru);
X      return(0L);
X   }
X/*
X*   Open the file.
X*   xfopen requires two arguments, in a0 and a1 respectively.
X*   Again, this must be done in assembler, and callaa does it.
X*/
X   fp = callaa(xfopen, IO->xpr_filename, "w");
X   if (fp == NULL) {
X      free(serbuff);
X      xpru.xpru_updatemask = XPRU_ERRORMSG | XPRU_FILENAME;
X      xpru.xpru_errormsg   = "Failed to open output file";
X      xpru.xpru_filename   = IO->xpr_filename;
X      calla(xupdate, &xpru);
X      return(0L);
X   }
X/*
X*   Start the transfer. See 3.8 for a discussion on how to implement
X*   xupdate. 
X*/
X   xpru.xpru_updatemask = XPRU_MSG | XPRU_FILENAME;
X   xpru.xpru_msg        = "Starting ASCII Receive";
X   xpru.xpru_filename   = IO->xpr_filename;
X   calla(xupdate, &xpru);
X/*
X*   Now read 80 byte chunks from the serial port using xsread. Stop
X*   when no characters arrive for 5 sec.
X*/
X   xpru.xpru_blocks = 0L;
X   while ((r = calladd(xsread, serbuff, 80L, 5000000L)) > 0L)  {
X/*
X*   Strip high-bit before storing in file.
X*   fl counts the characters. Display how many characters are received.
X*/
X      for (i = 0L; i < r; i++) serbuff[i] &= 0177;
X      fl += r;
X      xpru.xpru_updatemask = XPRU_BYTES | XPRU_BLOCKS | XPRU_BLOCKSIZE;
X      xpru.xpru_bytes      = fl;
X      xpru.xpru_blocks++;
X      xpru.xpru_blocksize  = r;
X      calla(xupdate, &xpru);
X/* 
X*   Write 80 byte chunks to the file using xwrite
X*/
X      calladda(xfwrite, serbuff, 1L, r, fp);
X
X/*
X*   Check for "abort" here. Perhaps should call chkmisc() as well.
X*/
X      if (brkflag = xchkabort()) break;
X   }
X/*
X*   Close the file
X*/
X   calla(xfclose, fp);
X   free(serbuff);
X/*
X*   If we got here through chkabort() say Aborted.
X*/
X   xpru.xpru_updatemask       = XPRU_MSG;
X   if (brkflag) xpru.xpru_msg = "Aborted";
X   else         xpru.xpru_msg = "Done"; 
X   calla(xupdate, &xpru);
X   if (brkflag) return(0L);
X   else         return(1L);
X}
X
X
X/**
X*
X*   Setup
X*
X**/
Xlong XProtocolSetup(IO)
Xstruct XPR_IO *IO;
X{
X   long (*xupdate)(), (*xgets)();
X   struct XPR_UPDATE xpru;
X
X   if ((xupdate = IO->xpr_update) == NULL) return(0L);
X   if ((xgets   = IO->xpr_gets)   == NULL) return(0L);
X/*
X*   Allocate a bit of memory for a data buffer
X*/
X   if (IO->xpr_data == NULL) {
X      if ((IO->xpr_data = (long *) malloc(256)) == NULL) {
X         xpru.xpru_updatemask = XPRU_ERRORMSG;
X         xpru.xpru_errormsg   = "ASCII - Out of memory!";
X         calla(xupdate, &xpru);
X         return(0L);
X      }
X   }
X/*
X*   If setup string isn't handed to us, ask questions
X*/
X   if (IO->xpr_filename == NULL) {
X/*
X*   Get the value for the send dealy
X*/
X      callaa(xgets, "Enter ASCII send delay (ticks, 1 tick = 20 msec)",
X                  IO->xpr_data);
X   }
X   else {
X      strcpy(IO->xpr_data, IO->xpr_filename);
X   }
X   
X   return(1L);
X}
X
X/**
X*
X*   Cleanup
X*
X**/
Xlong XProtocolCleanup(IO)
Xstruct XPR_IO *IO;
X{
X   if (IO->xpr_data) free(IO->xpr_data);
X   IO->xpr_data = NULL;
X
X   return(1L);
X}
X
X/**
X*
X*   The following functions setup the proper registers for the call-back 
X*   functions.
X*
X**/
X#asm
X        public _callad
X_callad:
X        movea.l 8(sp),a0                ; Second argument goes in a0
X        move.l  12(sp),d0               ; Third  argument goes in d0
X/*
X*   Now this is a trick to avoid using another register.
X*   Charlie taught me this...
X*/
X        move.l  4(sp),-(sp)             ; First  argument is function
X        rts
X
X        public  _calladda
X_calladda:
X        movea.l 8(sp),a0                ; Second argument goes in a0
X        move.l  12(sp),d0               ; Third  argument goes in d0
X        move.l  16(sp),d1               ; Fourth argument goes in d1
X        movea.l 20(sp),a1               ; Fifth  argument goes in a1
X        move.l  4(sp),-(sp)             ; First  argument is function
X        rts
X
X        public  _calla
X_calla:
X        movea.l 8(sp),a0                ; Second argument goes in a0
X        move.l  4(sp),-(sp)             ; First  argument is function
X        rts
X
X        public  _callaa
X_callaa:
X        movea.l 8(sp),a0                ; Second argument goes in a0
X        movea.l 12(sp),a1               ; Third  argument goes in a1
X        move.l  4(sp),-(sp)             ; First  argument is function
X        rts
X
X        public  _calladd
X_calladd:
X        move.l  8(sp),a0                ; Second argument goes in a0
X        move.l  12(sp),d0               ; Third  argument goes in d0
X        move.l  16(sp),d1               ; Fourth argument goes in d1
X        move.l  4(sp),-(sp)             ; First  argument is function
X        rts
X
X#endasm
X/*
X*   Could have added any other functions needed for other call-backs.
X*   Could have written a fancier single one... Could've...
X*/
X                              __                
X                             /  \ o    /        
X                       -----/----\----/-----
X                           /    o \__/          
X                                                
X        Clearly it isn't very hard to implement a simple protocol. More
Xelaborate protocols are straightforward extensions to the above example. Of
Xcourse, there are a few more standard files needed to make the above example
Xinto a complete library (like Open, Close and Expunge functions and a ROM-Tag
Xstructure) but those parts are the same for any library and aren't given here.
X
X
X
X5. The interface to the communications program.
X===============================================
X
X        The following is an annotated listing of a few call-back functions as
Xthey are implemented in VLT. Also, it is shown how to initialize the XPR_IO
Xstructure. Notice that the files supplied in this archive are likely more up to
Xdate and more extensive than the minimal example given here.
X
X/** xprfuncs.c
X*
X*   Call-back functions for eXternal PRotocol support
X*
X**/
X#include <functions.h>
X#include <exec/exec.h>
X#include <stdio.h>
X/*
X*   xproto.h is given in Appendix B
X*/
X#include "xproto.h"
X/*
X*   xfer.h is a VLT private header file containing some information for
X*   file transfer protocols
X*/
X#include "xfer.h"
X
X/*
X*   These are the C versions of the interface
X*/
Xlong        vlt_update(),  vlt_swrite(),  vlt_fread(),  vlt_fopen(),
X            vlt_fclose(),  vlt_gets(),    vlt_sread(),  vlt_chkabort();
X/*
X*   These are the assembly level glue functions, see vltface.asm
X*/
Xextern long avlt_update(), avlt_swrite(), avlt_fread(), avlt_fopen(),
X            avlt_fclose(), avlt_gets(),   avlt_sread(), avlt_chkabort();
X
X/**
X*
X*   This function initializes an XPR_IO structure.
X*
X**/
Xxpr_setup(IO)
Xstruct XPR_IO *IO;
X{
X/*
X*   NULL out all the functions we don't do yet.
X*   Fill the other ones with the addresses to the assembler glue version
X*   of the interface routines. See vltface.asm
X*/
X   IO->xpr_filename  = NULL;
X   IO->xpr_fopen     = avlt_fopen;
X   IO->xpr_fclose    = avlt_fclose;
X   IO->xpr_fread     = avlt_fread;
X   IO->xpr_fwrite    = NULL;
X   IO->xpr_sread     = avlt_sread;
X   IO->xpr_swrite    = avlt_swrite;
X   IO->xpr_sflush    = NULL;
X   IO->xpr_update    = avlt_update;
X   IO->xpr_chkabort  = avlt_chkabort;
X   IO->xpr_chkmisc   = NULL;
X   IO->xpr_gets      = avlt_gets;
X   IO->xpr_setserial = NULL;
X   IO->xpr_ffirst    = NULL;
X   IO->xpr_fnext     = NULL;
X   IO->xpr_finfo     = NULL;
X   IO->xpr_fseek     = NULL;
X/*
X*   Support the 1 defined extension
X*/
X   IO->xpr_extension = 1L;
X/*
X*   But don't actually implement it yet.
X*/
X   IO->xpr_options   = NULL
X/*
X*   Especially, NULL out the XPR private data field.
X*/
X   IO->xpr_data      = NULL;
X
X   return;
X}
X
X/**
X*
X*   Interface to VLT's MsgDisplay() function.
X*
X**/
X/*
X*   These are formats for VLT's requester
X*/
Xstatic char *xprnamfmt = "%s\n%s\n\n\n\n";
Xstatic char *filnamfmt = "\n\n%s\n\n\n";
Xstatic char *blksizfmt = "\n\n\n\nBlock:  %6ld  --  Block Size:  %6ld\n";
Xstatic char *errtimfmt = "\n\n\n\n\nErrors: %6ld  --  Timeouts:    %6ld";
Xstatic char *delayfmt  = "\n\n\n\n\nPacket delay %ld";
X/*
X*   Below are some VLT globals to orchestrate the display
X*/
Xlong xpr_blocks = 0L, xpr_blocksize = 0L, xpr_errors = 0L, xpr_timeouts = 0L;
X/*
X*   The function
X*/
Xlong vlt_update(x)
Xstruct XPR_UPDATE *x;
X{
X   extern struct Window *mywindow;
X   extern char *XPR_Name;
X/*
X*   First time, determine the window size (50 chars wide, 5 lines tall).
X*/
X   SetMsgWindow(mywindow, 50, 6);
X/*
X*   Use VLT's PostMsg function to display all the information.
X*/
X   if (x->xpru_updatemask & XPRU_PROTOCOL) {
X      PostMsg(mywindow, xprnamfmt, XPR_Name, x->xpru_protocol);
X   }
X   if (x->xpru_updatemask & XPRU_MSG) {
X      PostMsg(mywindow, xprnamfmt, XPR_Name, x->xpru_msg);
X   }
X   if (x->xpru_updatemask & XPRU_ERRORMSG) {
X      PostMsg(mywindow, xprnamfmt, XPR_Name, x->xpru_errormsg);
X   }
X   if (x->xpru_updatemask & XPRU_FILENAME) {
X      PostMsg(mywindow, filnamfmt, x->xpru_filename);
X   }
X   if (x->xpru_updatemask & XPRU_PACKETDELAY) {
X      PostMsg(mywindow, delayfmt, x->xpru_packetdelay);
X   }
X   if (x->xpru_updatemask & (XPRU_BLOCKS | XPRU_BLOCKSIZE)) {
X      if (x->xpru_updatemask & XPRU_BLOCKS)    xpr_blocks    = x->xpru_blocks;
X      if (x->xpru_updatemask & XPRU_BLOCKSIZE) xpr_blocksize = x->xpru_blocksize;
X      PostMsg(mywindow, blksizfmt, xpr_blocks, xpr_blocksize);
X   }
X   if (x->xpru_updatemask & (XPRU_ERRORS | XPRU_TIMEOUTS)) {
X      if (x->xpru_updatemask & XPRU_ERRORS)   xpr_errors   = x->xpru_errors;
X      if (x->xpru_updatemask & XPRU_TIMEOUTS) xpr_timeouts = x->xpru_timeouts;
X      PostMsg(mywindow, errtimfmt, xpr_errors, xpr_timeouts);
X   }
X   return(0L);
X}
X
X/**
X*
X*   Prompt the user for input
X*
X**/
Xlong vlt_gets(s, t)
Xchar *s, *t;
X{
X/*
X*   Use VLT's DoRequest() function
X*/
X   return((long) DoRequest(mywindow, t, s, NULL, " Cancel "));
X}
X
X/**
X*
X*   Write a string to the serial port
X*
X**/
Xlong vlt_swrite(s, n)
Xchar *s;
Xlong n;
X{
X/*
X*   Use VLT's SendString() function
X*/
X   SendString(s, (int) n);
X   return(0L);
X}
X
X/**
X*
X*   Read characters from the serial port
X*
X**/
Xlong vlt_sread(buff, length, micros)
Xunsigned char *buff;
Xlong length, micros;
X{
X   extern int timeout;
X   long secs = 0L;
X
X   if (buff == NULL) return(-1L);
X/*
X*   Convert timeout to seconds and micros if necessary
X*/
X   if (micros) {
X      if (micros > 1000000L) {
X         secs   = micros / 1000000L;
X         micros = micros % 1000000L;
X      }
X   }
X/*
X*   Cheat! Only return a single character since we have such a nice
X*   readchar() function in VLT. One day I'll have to modify this to 
X*   save the odd microsecond...
X*/
X   buff[0] = (unsigned char) readchar(secs, micros);
X/*
X*   VLT has a global called timeout. This comes in xfer.h.
X*   If the read was successful, return having read a single character.
X*/
X   if (timeout == GOODREAD) return(1L);
X/*
X*   Else return error condition
X*/
X   return(-1L);
X}
X
X/**
X*
X*   Interfaces to stdio
X*
X**/
Xlong vlt_fopen(s, t)
Xchar *s, *t;
X{
X   return((long) fopen(s, t));
X}
X
Xlong vlt_fclose(fp)
XFILE *fp;
X{
X   return((long) fclose(fp));
X}
X
Xlong vlt_fread(buff, size, count, fp)
Xchar *buff;
Xlong size, count;
XFILE *fp;
X{
X   int res;
X   res = fread(buff, (int) size, (int) count, fp);
X   return((long) res);
X}
X
X/**
X*
X*   Check for Abort
X*
X**/
Xlong vlt_chkabort()
X{
X/*
X*   VLT aborts its protocols when the escape key is pressed.
X*   CheckForKey loops over the UserPort messages looking for an escape.
X*/
X   return((long) CheckForKey(69));
X}
X                              __                
X                             /  \ o    /        
X                       -----/----\----/-----
X                           /    o \__/          
X                                                
X        Clearly, this part of the implementation isn't hard either. The only
Xthing left is the assembly level glue on the communications program side. You
Xmay wonder at this point why all this assembly level stuff is necessary. It is
Xnecessary because many programs and libraries are written in small code/small
Xdata. This means that both the communications program and the library address
Xtheir code/data off of some register, in the case of Manx usually A4. The
Xproblem is that the communications program and the library are loaded in
Xdifferent parts of memory, while startup code takes care of setting up the
Xproper value for A4. And the values of A4 are different for the the
Xcommunications program and the library! Now, if you just call a library
Xfunction, the assembly level glue does a few things, among which are: (1) saving
Xthe caller's A4 somewhere safe and (2) retrieving the A4 it stored somewhere
Xwhen the library was loaded. Then the library function is executed, and the
Xfunction returns to the glue. The glue then restores A4 to the state it was in
Xbefore the library call.
X        In the case of these call-back functions, we have to do the reverse.
XAfter all, when a function like xpr_update is called, the current value of A4 is
Xthe one that goes with the library's code. If the call-back function tries to
Xaccess any data back in the communications program, we're in big trouble.
X        So what the assembly part of the call-backs has to do is (1) save the
Xlibrary's A4 (on the stack) and (2) get the value of A4 appropriate for the
Xcommunications program. Then we can push the various registers onto the stack,
Xcall the C version of the call-back and then restore the value of A4 to what the
Xlibrary wants.
X        For the above call-backs, the assembly level glue is listed below. This
Xconcludes the documentation on external protocols using Amiga shared libraries.
XIf you have any questions, comments or suggestions, contact me on BIX.
X        Meanwhile, have fun!
X
X;;; vltface.asm
X;
X;   DESCRIPTION:
X;   ===========
X;
X;        This is an interface to VLT callback functions from
X;        external protocol libraries.
X;
X;   AUTHOR/DATE:  W.G.J. Langeveld, March 1989.
X;   ============
X;
X;;;
X
X        public  _geta4
X
Xsetup   macro
X        movem.l d2/d3/d4-d7/a2-a6,-(sp)
X        jsr     _geta4                        ; Get a4.
X        endm
X
Xpush    macro
X        move.l  \1,-(sp)
X        endm
X
Xfix     macro
X        ifc     '\1',''
X                mexit
X        endc
X        ifle    \1-8
X                addq.l  #\1,sp
X        endc
X        ifgt    \1-8
X                lea     \1(sp),sp
X        endc
X        endm
X
Xrestore macro
X        fix     \1
X        movem.l (sp)+,d2/d3/d4-d7/a2-a6        
X        rts
X        endm
X
X        public  _avlt_fopen
X        public  _vlt_fopen
X        public  _avlt_fclose
X        public  _vlt_fclose
X        public  _avlt_fread
X        public  _vlt_fread
X        public  _avlt_sread
X        public  _vlt_sread
X        public  _avlt_swrite
X        public  _vlt_swrite
X        public  _avlt_update
X        public  _vlt_update
X        public  _avlt_chkabort
X        public  _vlt_chkabort
X        public  _avlt_gets
X        public  _vlt_gets
X
X_avlt_fopen:
X        setup
X        push    a1
X        push    a0
X        jsr     _vlt_fopen
X        restore 8
X
X_avlt_fclose:
X        setup
X        push    a0
X        jsr     _vlt_fclose
X        restore 4
X
X_avlt_fread:
X        setup
X        push    a1
X        push    d1
X        push    d0
X        push    a0
X        jsr     _vlt_fread
X        restore 16
X
X_avlt_sread:
X        setup
X        push    d1
X        push    d0
X        push    a0
X        jsr     _vlt_sread
X        restore 12
X
X_avlt_swrite:
X        setup
X        push    d0
X        push    a0
X        jsr     _vlt_swrite
X        restore 8
X
X_avlt_update:
X        setup
X        push    a0
X        jsr     _vlt_update
X        restore 4
X
X_avlt_chkabort:
X        setup
X        jsr     _vlt_chkabort
X        restore
X
X_avlt_gets:
X        setup
X        push    a1
X        push    a0
X        jsr     _vlt_gets
X        restore 8
X
X                              __                
X                             /  \ o    /        
X                       -----/----\----/-----
X                           /    o \__/          
X                                                
X
X
X
X
X
X
X
XAppendix A: XPR library skeleton.
X=================================
X
X;;; libface.asm
X;
X;   DESCRIPTION:
X;   ===========
X;
X;        This is the skeleton for an Amiga Exec library.
X;        This version is written for Aztec C. It is based on the example
X;        library by Jim Mackraz who got some stuff from Neil Katin.
X;        This library implements a protocol transfer library.
X;        All changes and additions by me.
X;
X;   AUTHOR/DATE:  W.G.J. Langeveld, February 1989.
X;   ============
X;
X;;;
X
X        include 'exec/types.i'
X
Xsetup   macro
X        movem.l d2/d3/d4-d7/a2-a6,-(sp)
X        jsr     _geta4                        ;set up a4 for small model
X        endm
X
Xpush    macro
X        move.l  \1,-(sp)
X        endm
X
Xfix     macro
X        ifc     '\1',''
X                mexit
X        endc
X        ifle    \1-8
X                addq.l        #\1,sp
X        endc
X        ifgt    \1-8
X                lea        \1(sp),sp
X        endc
X        endm
X
Xrestore macro
X        fix     \1
X        movem.l (sp)+,d2/d3/d4-d7/a2-a6        
X        rts
X        endm
X
X        dseg
X
X        public  _libfunctab
X_libfunctab:
X        dc.l    XPRopen
X        dc.l    XPRclose
X        dc.l    XPRexpunge
X        dc.l    $0000
X        dc.l    XPRXProtocolCleanup
X        dc.l    XPRXProtocolSetup
X        dc.l    XPRXProtocolSend
X        dc.l    XPRXProtocolReceive
X        dc.l    $ffffffff
X
X        cseg
X
X        ;=== library functions
X        public  _XPROpen
X        public  _XPRClose
X        public  _XPRExpunge
X        public  _XProtocolCleanup
X        public  _XProtocolSetup
X        public  _XProtocolSend
X        public  _XProtocolReceive
X
X        public  _geta4
X
XXPRopen:
X        setup
X        push    a6
X        jsr     _XPROpen
X        restore 4
X
XXPRclose:
X        setup
X        push    a6
X        jsr     _XPRClose
X        restore 4
X
XXPRexpunge:
X        setup
X        push    a6
X        jsr     _XPRExpunge
X        restore 4
X
XXPRXProtocolCleanup:
X        setup
X        push    a0
X        jsr     _XProtocolCleanup
X        restore 4
X
XXPRXProtocolSetup:
X        setup
X        push    a0
X        jsr     _XProtocolSetup
X        restore 4
X
XXPRXProtocolSend:
X        setup
X        push    a0
X        jsr     _XProtocolSend
X        restore 4
X
XXPRXProtocolReceive:
X        setup
X        push    a0
X        jsr     _XProtocolReceive
X        restore 4
X
X
X        end
X
X
X
X
X
XAppendix B: The xproto.h include file
X=====================================
X
X/** xproto.h
X*
X*   Include file for External Protocol Handling
X*
X**/
X/*
X*   The structure
X*/
Xstruct XPR_IO {
X                  char  *xpr_filename;      /* File name(s)             */
X                  long (*xpr_fopen)();      /* Open file                */
X                  long (*xpr_fclose)();     /* Close file               */
X                  long (*xpr_fread)();      /* Get char from file       */
X                  long (*xpr_fwrite)();     /* Put string to file       */
X                  long (*xpr_sread)();      /* Get char from serial     */
X                  long (*xpr_swrite)();     /* Put string to serial     */
X                  long (*xpr_sflush)();     /* Flush serial input buffer*/
X                  long (*xpr_update)();     /* Print stuff              */
X                  long (*xpr_chkabort)();   /* Check for abort          */
X                  long (*xpr_chkmisc)();    /* Check misc. stuff        */
X                  long (*xpr_gets)();       /* Get string interactively */
X                  long (*xpr_setserial)();  /* Set and Get serial info  */
X                  long (*xpr_ffirst)();     /* Find first file name     */
X                  long (*xpr_fnext)();      /* Find next file name      */
X                  long (*xpr_finfo)();      /* Return file info         */
X                  long (*xpr_fseek)();      /* Seek in a file           */
X                  long  *xpr_extension;     /* Number of extensions     */
X                  long  *xpr_data;          /* Initialized by Setup.    */
X                  long (*xpr_options)();    /* Multiple XPR options.    */
X              };
X/*
X*   Number of defined extensions
X*/
X#define XPR_EXTENSION 1L
X
X/*
X*   The functions
X*/
Xextern long XProtocolSend(),  XProtocolReceive(),
X            XProtocolSetup(), XProtocolCleanup();
X/*
X*   The update structure
X*/
Xstruct XPR_UPDATE {     long  xpru_updatemask;
X                        char *xpru_protocol;
X                        char *xpru_filename;
X                        long  xpru_filesize;
X                        char *xpru_msg;
X                        char *xpru_errormsg;
X                        long  xpru_blocks;
X                        long  xpru_blocksize;
X                        long  xpru_bytes;
X                        long  xpru_errors;
X                        long  xpru_timeouts;
X                        long  xpru_packettype;
X                        long  xpru_packetdelay;
X                        long  xpru_chardelay;
X                        char *xpru_blockcheck;
X                        char *xpru_expecttime;
X                        char *xpru_elapsedtime;
X                        long  xpru_datarate;
X                        long  xpru_reserved1;
X                        long  xpru_reserved2;
X                        long  xpru_reserved3;
X                        long  xpru_reserved4;
X                        long  xpru_reserved5;
X                   };
X/*
X*   The possible bit values for the xpru_updatemask are:
X*/
X#define XPRU_PROTOCOL           0x00000001L
X#define XPRU_FILENAME           0x00000002L
X#define XPRU_FILESIZE           0x00000004L
X#define XPRU_MSG                0x00000008L
X#define XPRU_ERRORMSG           0x00000010L
X#define XPRU_BLOCKS             0x00000020L
X#define XPRU_BLOCKSIZE          0x00000040L
X#define XPRU_BYTES              0x00000080L
X#define XPRU_ERRORS             0x00000100L
X#define XPRU_TIMEOUTS           0x00000200L
X#define XPRU_PACKETTYPE         0x00000400L
X#define XPRU_PACKETDELAY        0x00000800L
X#define XPRU_CHARDELAY          0x00001000L
X#define XPRU_BLOCKCHECK         0x00002000L
X#define XPRU_EXPECTTIME         0x00004000L
X#define XPRU_ELAPSEDTIME        0x00008000L
X#define XPRU_DATARATE           0x00010000L
X/*
X*   The xpro_option structure
X*/
Xstruct xpr_option {
X   char *xpro_description;      /* description of the option                  */
X   long  xpro_type;             /* type of option                             */
X   char *xpro_value;            /* pointer to a buffer with the current value */
X   long  xpro_length;           /* buffer size                                */
X}
X/*
X*   Valid values for xpro_type are:
X*/
X#define XPRO_BOOLEAN 1L         /* xpro_value is "yes", "no", "on" or "off"   */
X#define XPRO_LONG    2L         /* xpro_value is string representing a number */
X#define XPRO_STRING  3L         /* xpro_value is a string                     */
X
SHAR_EOF
echo "End of archive 2 (of 2)"
# if you want to concatenate archives, remove anything after this line
exit