page@swan.ulowell.edu (Bob Page) (10/22/88)
Submitted-by: dillon@cory.berkeley.edu (Matt Dillon) Posting-number: Volume 2, Issue 7 Archive-name: util/suplib.docs [Matt sent 2 DISKS full of stuff ... my first week on the job! :-) ..Bob] # This is a shell archive. Remove anything before this line # then unpack it by saving it in a file and typing "sh file" # (Files unpacked will be owned by you and have default permissions). # This archive contains the following files: # ./dio.doc # ./qint.doc # ./summary.doc # ./suplib.doc # if `test ! -s ./dio.doc` then echo "writing ./dio.doc" cat > ./dio.doc << '\Rogue\Monster\' DIO EXEC device driver IO support routines... makes everything easy. dfd = dio_open(name, unit, flags, req/NULL) open an IO device. Note: in some cases you might have to provide a request structure with some fields initialized (example, the console device requires certain fields to be initialized). For instance, if openning the SERIAL.DEVICE, you would want to give an IOExtSer structure which is completely blank execept for the io_SerFlags field. The request structure's message and reply ports need not be initialized. The request structure is no longer needed after the dio_open(). returns NULL = error, else DIO descriptor (a pointer) returned. dio_close(dfd) close an IO device. Any pending asyncronous requests are AbortIO()'d and then Wait'ed on for completion. dio_closegrp(dfd) close EVERY DIO DESCRIPTOR ASSOCIATED WITH THE dio_open() call that was the parent for this descriptor. That is, you can get a descriptor using dio_open(), dio_dup() it a couple of times, then use dio_closegrp() on any ONE of the resulting descriptors to close ALL of them. dio_cact(dfd,bool) If an error occurs (io_Error field), the io_Actual field is usually not modified by the device driver, and thus contains garbage. To provide a cleaner interface, you can have the DIO_CTL() and DIO_CTL_TO() calls automatically pre-clear this field so if an io_Error does occur, the field is a definate 0 instead of garbage. In most cases you will want to do this. An exception is the TIMER.DEVICE, which uses the io_Actual field for part of the timeout structure. This flags the particular dio descriptor to do the pre-clear, and any new descriptors obtained by DIO_DUP()ing this one will also have the pre-clear flag set. dio_dup(dfd) Returns a new channel descriptor referencing the same device. The new descriptor has it's own signal and IO request structure. For instance, if you openned the serial device, you might want to dup the descriptor so you can use one channel to pend an asyncronous read, and the other channel to write out to the device and do other things without disturbing the asyncronous read. sig = dio_signal(dfd) get the signal number (0..31) used for a DIO descriptor. This allows you to Wait() for asyncronous requests. Note that if your Wait() returns, you should double check using dio_isdone() req = dio_ctl_to(dfd, command, buf, len, to) Same as DIO_CTL() below, but (A) is always syncronous, and (B) will attempt to AbortIO()+WaitIO() the request if the timeout occurs before the IO completes. the 'to' argument is in microseconds. If timeout occurs before request completes, and DIO aborts the request, some devices, such as the SERIAL.DEVICE do not have the io_Actual field set properly. Always check the io_Error field for an abort before using io_Actual. req = dio_ctl(dfd, command, buf, len) DIO_CTL() is the basis for the entire library. It works as follows: (1) If the channel isn't clear (there is an asyncronous IO request still pending), DIO_CTL() waits for it to complete (2) If the command is 0, simply return a pointer to the io request structure. (3) If the DIO_CACT() flag is TRUE, the io_Actual field of the request is cleared. (4) Set the io_Data field to 'buf', and io_Length field to 'len' If the command is positive, use DoIO(). If the command negative, take it's absolute value and then do a SendIO(). (The command is placed in the io_Command field, of course). (5) return the IO request structure bool= dio_isdone(dfd) return 1 if current channel is clear (done processing), else 0. e.g. if you did, say, an asyncronous read, and dio_isdone() returns true, you can now use the data buffer returned and look at the io_Actual field. You need not do a dio_wait() after dio_isdone() returns 1. req = dio_wait(dfd) Wait on the current channel for the request to complete and then return the request structure. (nop if channel is clear) req = dio_abort(dfd) Abort the request on the current channel (nop if channel is clear). Sends an AbortIO() if the channel is active and then WaitIO()'s the request. ------ MACROS ------ dio_simple() and related macros return the !io_Error field. That is, 0=ERROR, 1=OK dio_actual() returns the io_Actual field instead of !io_Error. NOTE: the io_Actual field may not be set by the device if an error condition exists. To make the io_ctl() and io_ctl_to() call automatically clear the io_Actual field before doing the io operation, use the DIO_CACT() call. The reason this isn't done automatically by default is that some devices require parameters to be passed in the io_Actual field (like the timer.device). Remember, Asyncronous IO is done by sending -com instead of com. (that is, negative command). CALL Syncronous IO Asyncronous IO dio_simple(dfd,com) 0=ERROR, 1=OK undefined dio_actual(dfd,com) io_Actual undefined dio_reset(dfd) 0=ERROR, 1=OK n/a dio_update(dfd) 0=ERROR, 1=OK n/a dio_clear(dfd) 0=ERROR, 1=OK n/a dio_stop(dfd) 0=ERROR, 1=OK n/a dio_start(dfd) 0=ERROR, 1=OK n/a dio_flush(dfd) 0=ERROR, 1=OK n/a dio_getreq(dfd) returns a ptr to the IO request structure NOTE: If you use the following, you probably want to have the device library automatically clear the io_Actual field before sending the request so you get 0 if an error occurs. That is: dio_cact(dfd,1): dio_read(dfd,buf,len) returns actual bytes read dio_write(dfd,buf,len) returns actual bytes written The timeout argument for dio_readto() and dio_writeto() is in MICROSECONDS, up to 2^31uS. dio_readto(dfd,buf,len,to) returns actual bytes read dio_writeto(dfd,buf,len,to) returns actual bytes written The asyncronous dio_reada() and dio_writea() do not return anything. dio_reada(dfd,buf,len) begin asyncronous read dio_writea(dfd,buf,len) begin asyncronous write \Rogue\Monster\ else echo "will not over write ./dio.doc" fi if [ `wc -c ./dio.doc | awk '{printf $1}'` -ne 6534 ] then echo `wc -c ./dio.doc | awk '{print "Got " $1 ", Expected " 6534}'` fi if `test ! -s ./qint.doc` then echo "writing ./qint.doc" cat > ./qint.doc << '\Rogue\Monster\' QINT DOCUMENTATION Matthew Dillon dillon@ucbvax.berkeley.edu ARPA ...!ihnp4!ucbvax!dillon USENET NOTE!!!!! Lattice Users must replace the 'jsr _geta4' call with the appropriate call to retrieve the address register for the small data model before compiling! You might have to make other modifications as well (I don't know since I don't have Lattice). The Calls: char oldpri; range -128 to 127 char newpri; char pri; long signum; 0 .. 31 void (*vector)(); function vector returning nothing void (*oldvec)(); While active Q interrupt vectors exist, the tc_ExceptCode in the task structure will be modified. The old tc_ExceptCode is used if an unknown exception occurs (one that is not a Q interrupt). oldpri = SetQPri(newpri) Set the task's current Q priority. Any Q interrupts of lower or equal priority that occur will be queued until the priority is dropped sufficiently. The initial task priority is -128 (essentially allowing all Q interrupts -127 to 127 to occur). oldvec = SetQVector(signum, vector, arg, pri) If vector is non-null, enables the exception at the specified priority (-127 to 127). specified vector is called in a C compatible way with one user argument (arg). Specifying a priority of -128 does not make sense because this is the lowest allowed priority and the Q interrupt will thus never occur. If vector is null, the exception and Q interrupt is disabled. After the last Q interrupt is removed, tc_ExceptCode is restored to its original value. BUGS: The only bug that I know of is a problem with EXEC. What is Good: An exception will not occur while one is Forbid()n What is Bad: If an exception comes in while Forbid()n, it will NOT be immediately entered when you Permit(). Whoops. The exception *will* occur when EXEC next checks its signals and exceptions, which occurs on the obvious EXEC library calls (SetExcept(), SetSignal(), etc...) and perhaps Wait(). In most cases you can ignore the problem. GENERAL WORKINGS OF Q INTERRUPTS: If you know how EXEC signals and the 68000 interrupt structure works, then you know how Q interrupts work. Simply replace "processor" with "task" and "0-7" with "-128 to 127" for task Q interrupt priorities (this is different from the Task's scheduler priority). Q interrupts work just like 68000 interrupts. If the task is currently running at a Q interrupt priority N, only Q interrupts of HIGHER priority can occur. Q interrupts of LOWER OR EQUAL priority are queued and will occur as soon as the priority is lowered. Everything occurs in priority order, the highest priority pending interrupt is always the one running (or the task's main routine if it has the highest priority). Thus, while a Q interrupt handler is running at some specific priority, other Q interrupts at the same priority will wait until the first one finishes and then execute in a FIFO fashion. THE INTERRUPT VECTOR ROUTINE: A specific Q interrupt vector is a C subroutine called with one longword argument (that specified when you SetQVector()'d it). This works fine with the small model. The handler runs with all normal EXEC processing enabled, and in the context of its task. It can be viewed almost as a subroutine call from the task. However, you must be careful about the reentrancy of certain functions. STDIO, DOS, and many other library calls are not reentrant, and you must use SetQPri() to ensure such calls are not interrupted. NOTE that you CAN mix certain calls. I don't have much info on what combinations work, but certainly most library calls that do not depend on being called singularly from tasks will work (for example, GetMsg()). And, of course, if all your handler does is make some small calculations this can interrupt anything. If you cause an exception (the same exception) from within the handler, it will be remembered. (That is, the signal is cleared before the handler is called), and occur after your handler returns. USES FOR Q INTERRUPTS: Use #1: To be able to execute menu options which do not effect the 'current' operation. E.G. if you are doing a ray tracing you could make the intuition window's signal bit a Q interrupt and handle Intuition messages that way.... The main loop generating the ray tracing would not have to continuously check for Intuition messages. Use #2: Lets say you are writing a terminal program and want to display data comming in smoothly while sending a file. While this can be done easily with the asynchronous ability of IO devices, it would be even easier if you handled the receive with a Q interrupt... that way, you could display received data (SendIO to the console device) even if the file sender is in the middle of a DOS call to read the next file block. Many more uses (I hope!). -Matt \Rogue\Monster\ else echo "will not over write ./qint.doc" fi if [ `wc -c ./qint.doc | awk '{printf $1}'` -ne 4986 ] then echo `wc -c ./qint.doc | awk '{print "Got " $1 ", Expected " 4986}'` fi if `test ! -s ./summary.doc` then echo "writing ./summary.doc" cat > ./summary.doc << '\Rogue\Monster\' SUMMARY.DOC Summary of library functions -------- ASYNCHRONOUS FILE SUPPORT -------- xfi = xfopen(file, mode, bytes) mode: r-read w-write w+-append err = xfclose(xfi) n = xfread(xfi, buf, n) 0 on EOF/error n = xfgets(xfi, buf, max) -1 on EOF/error else string length err = xfwrite(xfi, buf, n) bmov(src,dest,bytes) bcmp(src,dest,bytes) bset(src, bytes, c) bzero(src, bytes) checkbreak() resetbreak() disablebreak() (not available as a run-time library call) enablebreak() (not available as a run-time library call) openlibs(flags) closelibs(flags) closelibs(-1) close all libraries openned w/ openlibs() bool = wildcmp(wildstr, namestr) mountrequest(bool) fhprintf(fh, ctrlstr, args...) llink(list, en) (OBSOLETE) lunlink(en) (OBSOLETE) SYSTEM SUPPORT port = CreateUniquePort(name, priority) create port w/unique name DeleteUniquePort(port) delete port w/unique name PutSyncMsg(port, msg) normal synchronous message PutSyncMsgSimple(port, ptr, cmd) simple synchronous message retcmd = WaitMsg(msg) wait for message to come back CheckMsg(msg) check if message has come back GetHead(list) GetTail(list) NextNode(node) IO SUPPORT dfd = dio_open(name, unit, flags, req/NULL) dio_close(dfd) dio_closegrp(dfd) dio_cact(dfd,bool) dio_dup(dfd) sig = dio_signal(dfd) req = dio_ctl_to(dfd, command, buf, len, to) req = dio_ctl(dfd, command, buf, len) bool= dio_isdone(dfd) req = dio_wait(dfd) req = dio_abort(dfd) also a large array of macros are available. if command < 0, then asyncronous execution is implied. Macros: SYCHRONOUS ASYNCHRONOUS dio_simple(dfd,com) 0=ERROR, 1=OK undefined dio_actual(dfd,com) io_Actual undefined dio_reset(dfd) 0=ERROR, 1=OK n/a dio_update(dfd) 0=ERROR, 1=OK n/a dio_clear(dfd) 0=ERROR, 1=OK n/a dio_stop(dfd) 0=ERROR, 1=OK n/a dio_start(dfd) 0=ERROR, 1=OK n/a dio_flush(dfd) 0=ERROR, 1=OK n/a dio_getreq(dfd) returns a ptr to the IO request structure dio_read(dfd,buf,len) returns actual bytes read dio_write(dfd,buf,len) returns actual bytes written dio_readto(dfd,buf,len,to) returns actual bytes read dio_writeto(dfd,buf,len,to) returns actual bytes written dio_reada(dfd,buf,len) begin asyncronous read dio_writea(dfd,buf,len) begin asyncronous write \Rogue\Monster\ else echo "will not over write ./summary.doc" fi if [ `wc -c ./summary.doc | awk '{printf $1}'` -ne 2888 ] then echo `wc -c ./summary.doc | awk '{print "Got " $1 ", Expected " 2888}'` fi if `test ! -s ./suplib.doc` then echo "writing ./suplib.doc" cat > ./suplib.doc << '\Rogue\Monster\' SUPLIB.DOC General C Support Library COMPILATION Compile all modules using a precompiled symbol table of all the AMIGA include's (*/*.H) ... do *NOT* include standard aztec includes (stdio.h, etc...). The Makefile for the precompiled symbol table is in the LOCAL directory. You must use the +L option (32 bit ints) for ALL COMPILATIONS, including the generation of the symbol table when compiling SUPLIB (SUP32.LIB). Also, use +B (no .begin reference), +CD (large code and data) model when compiling this source. MODULES QINT: These are exception based prioritized software interrupt routines. see QINT.DOC ASYNCOP: Asynchronous function execution. Make asynchronous function calls (not incredibly fast). XFIO: Asyncronous file IO. Allows sequential asyncronous access to files for both reading (reads ahead asyncronously) and writing (writes asyncronously). Usually employed by CPU bound programs not wishing to be slowed down even more by the disk. Extremely useful for implementation of capture and serial protocols. DIO: Device IO package. This is a Generic interface for handling the Amiga's EXEC devices. It makes your code smaller and much easier to read. You no longer need to be a guru to use devices. BSTRING: memory move/set/compare routines. Operations are done in longwords when possible. SYS: System enhancement calls MISC: misc. routines (break checking, openning/closing libraries), date and time routines, setfiledate, etc... --------------------------------------------------------------------- QINTS SEE QINT.DOC ASYNCH OP handle= NewAsyncOp() Create a new task for handling asynchronous function calls. (void) StartAsyncOp(handle, func, arg1, arg2, arg3) Queue up a function for the handle. Multiple functions may be queueud. NOTE: The registers A4 and A5 will be initialized to what they were when NewAsyncOp() was called. The function must preserve D4-D7/A2-A3. D0-D3/A0-A1/A4-A6 may be destroyed by the function. bool = CheckAsyncOp(handle, n) Return TRUE if a minimum of N async operations started with StartAsyncOp() have completed, FALSE otherwise. The number of async operations in progress is the number started minus the number already waited for. (void)= WaitAsyncOp(handle, n) Wait for N of the operations in progress to complete. -1 can be specified to wait for ALL the operations in progress to complete. For example, if you queue up 3 commands, CheckAsyncOp(handle, 3) will check if all 3 have completed, and WaitAsyncOp(handle, 3) waits and removes their reply messages as well as adjusts the number of 'operations in progress' to 0. I.E, if you were to WaitAsyncOp(handle, 2) instead of 3, after it returns there will be 1 operation in progress left. If you were to do another StartAsyncOp(), there would now by 2 in progress. (void) CloseAsyncOp(handle) Wait for all operations in progress to end (WaitAsyncOp(handle,-1)), then remove the task. XFIO xfi = xfopen(file, mode, bytes) err = xfclose(xfi) n = xfread(xfi, buf, n) n = xfgets(xfi, buf, max) err = xfwrite(xfi, buf, n) mode is "r", "w", or "w+". No seeking is allowed as you can see. If you openned for reading, you may NOT use xfwrite(), and if you openned for writing, you may NOT use xfread(). r read w write-newfile w+ write-append The specified buffer size (bytes) is used to create two buffers of (bytes/2) bytes, double buffering either asyncronous read ahead, or asyncronous writes. 'err' returns 1 if a write error occured. err is returned by xfclose() (xfclose() waits for any asyncronous writes to complete and thus can return whether they failed or not). Once set, err stays set forever. XFREAD: 0 is returned on EOF or error XFGETS: the length of the string is returned. 0 is a valid length (a blank line). -1 is returned on EOF or error. The newline is removed and a string terminator (0) added. DIO SEE DIO.DOC BSTRING (void) bmov(src,dest,bytes) bool = bcmp(src,dest,bytes) (void) bset(src, bytes, c) (void) bzero(src, bytes) These functions do various memory operations. bcmp() is does an unsigned comparison, of course. bcmp() only checks for equivalence, returning TRUE (1) if the buffers are the same, FALSE (0) otherwise. bcmp() uses longword compares when possible. bmov() does an ascending or decending copy as appropriate. bmov(), bzero(), and bset() use longword and MULTIPLE REGISTER operations when possible. These functions are the same as the BSet(), BZero(), BMov(), and BCmp() in my run-time library DRES.LIBRARY. bool = checkbreak() Check whether the process has received a ^C or ^D signal. ^D is also checked here allowing a more reliable break-mechanism, as Aztec and Lattice stdio routines will clear ^C even when break is disabled. (void) resetbreak() (void) disablebreak() (void) enablebreak() resetbreak() clears both the ^C and ^D signals. disablebreak() and enablebreak() modify the global variable Enable_Abort and thus stdio's automatic break detection/abort. bool = openlibs(flags) bool = closelibs(flags) See the flag definitions in XMISC.H. openlibs() opens all specified libraries, returning 0 if one or more could not be openned. closelibs() closes all specified libraries. openlibs() does not open a library that is already open (if you make the call more than once), and simply uses the already open descriptor. closelibs(-1) closes ALL libraries openned with openlibs(), but NOT libraries openned otherwise. Note that you cannot open or close DOS or EXEC. This is because the C startup will do this for you, and also to prevent linker warning messages. Window= GetConWindow() This functions retrieves the struct Window * from the console device associated with this process. NULL is returned if the window could not be found (still, operation may not be dependable if the process's console is not a console device). buf = datetos(date, buf, ctl) DATESTAMP *date; char *buf; char *ctl; This function converts a DOS DateStamp structure into a string and places it in the specified buffer. ctl specifies the format of the date by pieces (ctl can be NULL, indicating "D M Y h:m:s"). If not NULL, ctl is a string containing combinations of the following characters. Spacing must also be specified. Any unrecognized characters are passed to the output buffer verbatim. D The day 23 M The month Jul Y The year 1988 h The hour 03 m The minute 23 s The seconds 04 This function is equivalent to DateToS() in my run time library DRES.LIBRARY (void) llink(list, en) (OBSOLETE) (void) lunlink(en) (OBSOLETE) see XMISC.H . Simple doubly-linked list routines. XLIST is both the list base and an element. The list base should be initialized to zero before use. (void) mountrequest(bool) enable or disable the DOS requester which comes up when you attempt to open a path not currently mounted. Normal mode is TRUE (1), meaning that you get the requester. This routine remembers the previous contents of pr_WindowPtr. The call mountrequest(0) may be made multiple times and then mountrequest(1) will restore the original contents of pr_WindowPtr. RemSemaphore() FindSemaphore() (SEE EXEC DOCUMENTATION FOR CALLING PARAMETERS) AddSemaphore() These functions fix the broken bindings in older Lattice and Aztec libraries. bool = setfiledate(file, date) char *file; DATESTAMP *date; This function implements the new ACTION_SET_DATE packet and sets the timestamp of a file. You cannot set the timestamp for the root of a filesystem with this call. This function is equivalent to the SetFileDate() function in DRES.LIBRARY. bool = wildcmp(wildstr, namestr) compare the wildcard string (containing '*'s and '?'s) with the file name (namestr) and return TRUE (1) if they compare, and FALSE (0) otherwise. This function is equivalent to the WildCmp() function in DRES.LIBRARY. (void) fhprintf(fh, ctrlstr, args...) uses the EXEC formatted printing call to format text and then writes it to an AMIGADOS file handle. rval = AutoAllocMiscResource(resno, value) resno: MR_SERIALPORT, SERIALBITS, PARALLELPORT, or PARALLELBITS value: -1 to allocate, 0 to check. This functions allocates or checks the specified resource. 0 (ZERO) is returned on SUCCESS (allocated or could allocate), NON-ZERO is returned if the resource is already allocated by somebody else. (void)= AutoFreeMiscResource(resno) Free's a resource you allocated. YOU MUST OWN THE RESOURCE! Neither of these functions require you to open the misc.resource resource. font = GetFont(name, ysize) This function searches both memory and the disk for the requested font, and automatically opens/closes the diskfont library if it is not already open. It opens the font, incrementing the reference count. (void) InitDeemuNW(ary, nw) short *ary; NW *nw; ary points to the 'NW',' ' Deemu[] array entry. The NewWindow structure is initialized according to the Deemu entry. Currently, TopEdge, LeftEdge, Width, Height, DetailPen and BlockPen will be initialized... less if the Deemu entry contains less information. char *GetDEnv(name) char *name; Return a string to the enviroment variable name, returning NULL if it does not exist. bool SetDEnv(name, str) char *name, *str; Set the enviroment variable name to the string str. Return C-TRUE (1) on success, or C-FALSE (0) on failure. \Rogue\Monster\ else echo "will not over write ./suplib.doc" fi if [ `wc -c ./suplib.doc | awk '{printf $1}'` -ne 9714 ] then echo `wc -c ./suplib.doc | awk '{print "Got " $1 ", Expected " 9714}'` fi echo "Finished archive 1 of 1" # if you want to concatenate archives, remove anything after this line exit -- Bob Page, U of Lowell CS Dept. page@swan.ulowell.edu ulowell!page Have five nice days.