***************************************************************************
***************************************************************************



The UCR Standard Library for Assembly Language Programmers,
Written By Randall Hyde and others, is

  sssssss      ss     ss       ss       sssssss     sssssss
  ss           ss     ss      ssss      ss    ss    ss
  ss           ss     ss     ss  ss     ss    ss    ss
  sssssss      sssssssss    ssssssss    sssssss     sssss        ssssssss
       ss      ss     ss    ss    ss    ss  ss      ss
       ss      ss     ss    ss    ss    ss   ss     ss
  sssssss      ss     ss    ss    ss    ss    ss    sssssss



  ww                  ww       ww       sssssss     sssssss
   ww                ww       wwww      ss    ss    ss
    ww      ww      ww       ww  ww     ss    ss    ss
     ww    wwww    ww       wwwwwwww    sssssss     sssss
      ww  ww  ww  ww        ww    ww    ss  ss      ss
       wwww    wwww         ww    ww    ss   ss     ss
        ww      ww          ww    ww    ss    ss    sssssss




We do not want any registration fees for this software.

Now for the catch...  It is more blessed to give than to receive.  
If this software saves you time and effort and you enjoy using it, 
our lives will be enriched knowing that others have appreciated our work.  
We would like to share this wonderful feeling with you.  If you like this 
software and use it, we would like you to contribute at least one routine to 
the library.  Perhaps you think this library has some neat-o routines in it.  
Imagine how nice it would become if everyone used their imagination to 
contribute something useful to it.

We hereby release this software to the public domain.  You can use it in any
way you see fit.  However, we would appreciate it if you share this software 
with others as much as it has been shared it with you.  That is not to suggest
that you give away software you have written with this package (We're not 
quite as crazy as Richard Stallman, bless his heart), but if someone else would 
like a copy of this library, please help them out.  Naturally, we would be 
tickeled pink to receive credit in software that uses these routines (which is 
the honorable thing to do) but we understand the way many corporations operate 
and won't be terribly put off if you use it without giving due credit.  

Enjoy!

If you have comments, bug reports, new code to contribute, etc., you can 
reach us through (address and email are circa 1993, if you read this in
1999, don't count on it!):

		rhyde@cs.ucr.edu		(On Internet).

or

		Randall Hyde
		Dept of Computer Science
		100 University Office Bldg
		University of California
		Riverside, Ca. 92521


COMMENTS ABOUT THE CODE:
************************

Please don't expect super optimal code here.  Most of it is fairly mediocre
(from a size/speed point of view).  Hopefully, you'll agree, it's the idea
that counts.  If you do not like something I have done, you have got the
sources -- have at it.  (Of course, it would be appreciated if you would
send any modifications to one of the E-MAIL addresses above.)


****************+******************** NOTE ************************************

Please understand the purpose of this code!  This library is here to make
assembly language programming easy.  The nature of this library encourages
people to write code in a fashion similar to that employed when they write
programs in a high level language like C.  While this familiar style of
programming does make the task easier, it is not the most appropriate
approach to use when flat-out performance is what you're seeking.  "C code
written with MOV instructions" is never as fast as pure assembly language
code employing the proper programming paradigm.  Why mention this?  Well,
some readers may have heard about assembly language's legendary performance
and they're expecting to achieve that using this library.  While programs
written with this library may very well run faster than a comparable program
written in a HLL, you will not get fantastic performance improvement until
you stop thinking in HLLs and starting "thinking" in assembly.  The purpose
of this library is to help you *avoid* thinking in assembly language.  There-
fore, this code will not help you achieve those fantastic performance levels
you've been hearing about; indeed, this library may stand in the way of that
goal.  It's not that these routines are terribly slow, mind you.  They just
encourage an inappropriate programming style if speed is what you're after.

On the other hand, since only 10-20% of the code of any given program
represents the time critical stuff (an argument long employed by HLL
supporters), there is nothing wrong with judicious use of this code within
a program that has to be fast.  As usual, if performance is your primary
goal, you must study the problem and the program you generate very carefully
to isolate the time critical portions.  If you are interested in high-
performance programming at the "micro-algorithm" level, you should take a look
at Michael Abrash's text "Zen of Assembly."  This excellent book will explain
many ways to improve the performance of your code at the sub-algorithm level
(where assembly language really shines).



COMMENTS ABOUT THIS DOCUMENTATION:
**********************************

You will have to forgive us for the inconsistent style appearing throughout
this document.  Keep in mind that this document has been prepared by many
different people.  Keeping the styles consistent is a time consuming and
difficult task.

Whenever a routine's description claims that the flags are not affected,
you should not interpret this to mean that the routine preserves the flags.
Most routines do *not* preserve any of the flags.  Such a statement simply
means that the routine does not *explicitly* return a value in one (or more)
of the flag bits.

Note that proper credit has been given to the author of each of the various
routines appearing in this library *except* for many written by Randall
Hyde.  All routines without an author by-line were probably written by
Randall Hyde (unless we screwed up somewhere and forgot to put a name
in the documentation).  Most of these routines were tested and documented
by various students in Randy Hyde's CS 13 (assembly language) and CS 191X /
CS 185 courses (Commercial Software Development).  There are too many names
to mention here, but these students definitely deserve the credit for locating
numerous bugs in the code, providing many suggestions, and doing other work.

Of course, there have been numerous suggestions and bug notices from helpful
souls on BIX and the Internet, as well.  Thank you all.

*NOTICE* We have noticed, from time to time, that there are routines in the
library which have not been documented.  Perusing the source listings will
help you locate some library routines which have slipped through the cracks.
Also keep in mind that there isn't a one to one correspondence between
source files and library routines.  Many of the source files contain
two or more library routines.  Someday we will attempt to document which
files contain which routines, but that's in the future for now.

=============================================================================

Version History:

Version 00- 	Initial release as "Randy Hyde's Standard Library for 80x86
		Assembly Language programmers"

Version 10-	Initial release as "UCR Standard Library..."  CS 191X
		students did some testing and documentation in this release.

Version 20-	More testing on several routines.  Added floating point
		library and several other routines.

Version 21-	Fixed *MAJOR* bugs in floating point package.  Added
11-1-91		several new routines.  Included new "TEST" files with
		the library.  Also included SHELL.ASM file inadvertently
		left out of Version 2.0.

Version 22-	Made some minor modifications to puth, putl, ltoa, and htoa
11-14-91	as per suggestions made by David Holm and Terje Maithesen

Version 23-	Made a small but *major* modification to the stdlib.a and
11-22-91	stdlib.a6 files to force library calls into the STDGRP group.
		Otherwise the linker substitued bad segment addresses for
		the far calls to the library routines.  A real problem when
		accessing variables in StdData.

Version 24-	Yet more changes to fix the stupid MASM group/segment:offset
12-7-91		bug.  Made various changes to the STDLIB.A file.  Also fixed
		a problem in the FP routines- forgot to declare sl_sefpa
		public.  Finally, created batch file to automatically unpack
		everything from DOS (assuming presence of PKUNZIP somewhere
		in the current path).

Version 25-	Some new macros (DOS, ExitPgm), fixed a problem with the
12-25-91	PUTI routine, added some SmartArray items.  Also added the
		GetEnv routine.

Version 26-	Maintenance release coinciding with the Dr. Dobb's article
2/20/92		in the March 1992 issue.

Version 27-	SmartLists and interrupt driven serial routines added to
6/19/92		the libraries.  Also created smaller include files for
		each of the standard library categories. (note: the serial
		routines actually existed prior to this release, they were
		cleaned up and documented for this release).  Fixed a couple
		of truly disgusting bugs in the floating point package
		(wouldn't properly print values like 8100 and hung whenever
		encountering a zero value in FADD/FSUB).

Version 28-	Modified MemInit to allow the programmer to specify how many
8/20/92		pages to reserve for the heap and the location of the heap.

Version 29-	Added HeapStart routine to the memory management code so an
10/5/92		application could get the segment address of the start of
		the heap.  This is useful when you want to deallocate the
		heap (by calling DOS' deallocate routine), for example, to
		free up the heap memory so you can run another application.
		What really needs to be done here is to write a dealloc
		routine, but HeapStart offered some flexibility.

Version 30-	Fixed bug in ATOH2 routine (it incremented DI once too far).
10/11/92-	Also fixed the same bug in ATOI2, ATOU2, ATOL2, ATOUL2, etc.
3/16/93		Added StrTrim (m) and StrBlkDel (m) to the library.  Added
		the pattern matching package to the library. Added the date
		and time routines (ATOD, DTOA, DTOA2, DTOAm xDTOA, xDTOAm,
		xDTOA2, ATOT, TTOA, TTOA2, TTOAm xTTOA, xTTOAm xTTOA2) to
		the library.  Fixed a bug in ATOI and ATOL which passed off
		the ":" character as a numeric digit.  Broke the MemInit
		routine into two separate routines: MemInit & MemInit2 which
		let the user specify the location of the heap or use all the
		available memory.  Also, no longer require that PSP be a
		global variable (However, the library does require DOS 3.3
		or later).  Fixed a bug in PRINTF/PRINTFF (it did not
		properly restore the flags and BP). Fixed a bug in the
		LSFPO routine (thanks to Tim Farley for pointing this out).
		Added the process manager package to the library.

Version 31-	Fixed a bug in strstr which prevented it from matching a
6/10/93		substring at the beginning of a string.  Added file
7/24/93		routines to the library.  Added macros for strbdel and
8/1/93		strtrim to string.a.  Fixed a bug in stricmpl, forgot to
		copy a pointer into SI within the routine.  Fixed a bug in
		CPUID which crashed the machine if a 486.

Version 32-	Fixed several bugs in the list routines.  Added some actual
3/24/94		file routines to the library.  Updated the documentation.

Version 33-	Fixed some bugs in the floating point code.  Fixed a bug
7/15/94		a bug in the pattern matching code.  Added new pattern
		match routines.  Changed the name of CPUID because it
		conflicts with the Pentium instrucion of the same name.
		Fixed several bugs in the processes package.  Fixed some
		problems in the documentation (certain routines were listed
		by the wrong name).

Version 34-	Fixed several problems in the documentation.  Some other
11/18/94	minor bug fixes including changing the CPUID name to
		CPUIDENT (to avoid conflict with Pentium CPUID instruction).
		Also modified IBML to use CPUIDENT rather than CPUID.

Version 35-	Fixed a problem with a signed comparison in the pattern
		matching code.  It turned out that if you failed on the
		first character of a string, it bombed the system.  Also
		changed the doc on patterns to fix an error.

Version 36-	Fixed a bug in the FREAD routine.  There are known bugs in
4/4/95		the floating point package, but cannot get a sample example
		to determine cause.

==============================================================================


ROUTINES WE WOULD LIKE TO HAVE:
*******************************

If you're interested in adding some routines to this
package, GREAT!  Here are some suggestions.

1) Routines which manipulate directories (read/write/etc.)
2) We did it already!
3) Length-prefixed strings package.
4) A graphics package.
5) An object-oriented programming class library.
6) Floating point functions (e.g., SIN, COS, etc.)
7) Just about anything else appearing in a HLL "standard" library.
If you've got any ideas, we would  love to discuss them with you.  The best
way to reach us is through the E-MAIL addresses above.


MISSING ROUTINES TO BE SUPPLIED IN THE FUTURE:
**********************************************


	Table Package
TblInit-	Initializes a particular table.
TblEnter-	Enters an item into a table.
TblLookup-	Looks up an item in a table.
TblFree-	Free up memory in use by a table.

	Tree Package
<pretty much the same routines as the list package>

	Set Package
<Generic set routines (not just character set routines) similar to cset pkg>


	Processes Package
Sleep-		Delays a process for some period of time.
YieldTo-	Transfers control to a specific process.
Forkm-		Allocates new PCB on the heap.
Sync-		Halts a process until another process dies.
Join-		Merges two processes together.
wait & release-	Semaphore/synchronization primitives.


	80386 Optimized Code
Despite the disclaimer about speed earlier in this document, we do have
plans to rewrite this routine for speed at some point in the future.
At that time we will write the code specifically for 80386 and later
processors (the code will probably be optimized for Pentium/586 processors
at that time).  Stay tuned.


HOW TO USE THE STANDARD LIBRARY:
********************************

When you are ready to begin programming with the library, you should
copy the shell.asm file, provided in the package, to another file in
which you will be working, i.e. myprog.asm.  The shell.asm file sets
up the machine (segments, etc.) as the UCR Standard Library expects
them.  Results are undefined for other setups.  Therefore, I strongly
suggest, that when you begin using these routines, you follow the
shell.asm format.  Later, when you are familiar with the software,
you may wish to create your own shell.asm file, but it is wise to
initially use the one provided.  The shell.asm file has comments which
tell you where to place your code, variables, etc.

There is an include file stdlib.a which
you should include in every assembly you perform which calls the stdlib
routines.  SHELL.ASM already includes this file.  *YOU MUST PLACE THE
INCLUDE STATEMENT OUTSIDE OF ANY SEGMENTS IN YOUR PROGRAM*.  Preferably
as the first line of your program (just like SHELL.ASM).  If you place
this include directive inside a segment, certain assemblers/linkers
(especially MASM) will not properly assemble and link your programs.
They will assemble and link without error, but the resulting program
will not execute correctly.

The STDLIB.A file contains macros you can use to call each of the routines
in the standard library.  For example, to call PRINTF you would use the
statement
		printf
		db	"format string",0
		db	other,vars

rather than "calling" printf.  Printf is actually a macro, you cannot call
it directly (all of the standard library routines have names like "sl_printf"
and the macro issues a call to the appropriate routine).  These macros have
two main purposes-- first, the differentiate calls to the standard library
routines (i.e., no "call" instruction is the difference); and second, they
contain some extra code to perform "smart linking" with MASM 5.1 & earlier,
TASM, and OPTASM.  MASM 6.0 supports a new directive, extrndef, which
eliminates the need for this extra code, but the extra code works nonetheless.

Starting with version 27, many of the standard library macros were separated
into smaller files.  This speeds up assembly when you don't need *all* of
the routines in the library (the macro file is getting quite large).
STDLIB.A still exists and still loads everything, but you should get in the
habit of specifying the smaller files instead.  For MASM 6.0 users, a
special set of include files "*.a6" are now available.  MASM 6.0 seems to
run out of memory if you include "stdlib.a6" (which includes everything) so
you may have to include only those files you actually use.


All of the standard library routines, and most of their local data values,
are in a segment named "stdlib".  You should not create such a segment unless
you plan on adding new routines to the standard library.


Note: 	if you want to use the pattern matching functions provided in the
	pattern matching package, you will need to include the following
	statement somewhere *after* the "include stdlib.a" or
	"include pattern.a" statement:

			matchfunc

	This declares the necessary external names required by the pattern
	matching operations.  The SHELL.ASM file contains a commented-out
	line with this statement.  If you use pattern matching in programs
	which start out as SHELL.ASM you can simply uncomment this line.



HOW THE STANDARD LIBRARY IS ORGANIZED:
**************************************

The documentation spec sheets for each of the standard library routines appear
in other files provided with the standard library.  We've organized these
routines by category.  The categories supported to date include

	Standard Input Routines
	Standard Output Routines
	Conversion Routines
	Utility Routines
	String Handling Routines
	Memory Management Routines
	Character Set Routines
	Floating Point Routines
	File I/O
	Miscellaneous Routines
	Time & Date Routines
	Smart List Routines
	Serial Port I/O
	Pattern Matching Package
	Process Package



IF YOU WANT TO PLAY WITH THE SOURCE LISTINGS
********************************************

Most users will probably use the standard library routines in object form
and never worry about the actual implementation.  If you, on the other hand,
want to get "under the hood" and take a look at how this code was written
(perhaps to fix a bug), all the source listings are provided with this
release.

We assemble the library for final distribution using TASM 3.0 with the
"/M3",  "/jjumps", and "/ic:\stdlib\include" command line options.  If you
do not specify these options you will probably get an assembly error.

All initial development of these routines was done with MASM.  By writing the
code with MASM and then assembling the final release version with TASM we
could verify that the code worked with both assemblers.

That is, at least, until MASM 6.0 came along.  All new routines written since
the introduction of MASM 6.0 were developed with MASM 6.0 and assembled with
TASM 3.0.  They should compile with MASM 5.1 as well (though we haven't
verified this).  HOWEVER, older routines written before the release of MASM 6
will probably not assemble properly under MASM 6.0 unless you specify the
MASM 5.1 compatibility options.  Furthermore, routines written after the
release of MASM 6.0 take advantage of MASM/TASM's "branch out of range"
automatic correction and may produce errors when assembled under MASM 5.1.

Moral of the story-- If you're still using MASM 5.1 (or earlier) or TASM 2.0
(or earlier), *upgrade*!

Given the divergent paths that MASM 6.0 and TASM 3.0 are taking, it is
unlikely that we will continue to provide all future code in a form which
compiles under both assemblers.  The windowing package we've created (but
have not released), for example, will only assemble under MASM 6.0.  We will
always make sure that the object code works with any assembler/linker out
there, but it's unlikely we will continue to support both MASM and TASM
at the source level for TASM indefinitely (unless BORLAND gives us good
reason to do otherwise, like having a MASM 6.x compatibility mode).  Sorry,
it's just too much work for so little return.

Of course, if you would volunteer to translate our MASM 6 code to TASM,
we'd be more than happy to give you full credit for your work.

Currently (6/93), MASM 6.0 and MASM 6.1 have some severe bugs which create
some major problems.  As soon as a stable release appears we will convert
specifically to MASM 6.x.

Acknowledgements
================

There are far too many people who have their fingers in this package to
give full credit to everyone involved.  Futhermore, this section was
added long after many hard-working people's efforts were forgotten.
If you are one of these people, send me (rhyde) email and I will
certainly rectify this situation.

Most of the routines in the library were written by Randy Hyde.
Those routines authored by someone else contain appropriate notes in the
comments found in the source listing.

Many thanks to those who have found problems in routines in the library.
This includes the students in CS 191x, CS 185, CS 162ABC, and CS 13 at
UC Riverside.  They have made important contributions to this library
and their efforts are not forgotten.

Special thanks to the CS 191x class at UC Riverside who reorganized the
documentation from its original sorry state.  Special thanks to Steve
Shah for his quick reference guide.

Last, but certainly not least, praise and glory to our Lord for giving us
all the talent to achieve this...

In the future, I will endeavor to keep this section up to date and provide
personal acknowledgements to those who have contributed to the success of
this library.
Conversion Routines
-------------------


The stdlib conversion routines follow a uniform format of storing the data
to be converted and returned.  Most routines accept input and return data
of either an ASCII string of characters, stored in the ES:DI register, or
integers, stored in the DX:AX register.  If a value is just a 16 or 8-bit
value then it will be stored in AX or AL.

Since there is a possibility of an error in the input values to be converted,
such as it does not contain a proper value to convert, we use the
carry flag to show error status.  If the error flag is set then an error has
occured and things are okay if the carry flag is clear.





Routine:  ATOL (2)
------------------


Category:             Conversion Routine

Registers on Entry:   ES:DI- Points at string to convert

Registers on Return:  DX:AX- Long integer converted from string
		      ES:DI- Points at first non-digit (ATOL2 only)

Flags Affected:       Carry flag- Error status

Examples of Usage:
		      gets         ;Get a string from user
		      ATOL         ;Convert to a value in DX:AX


Description:  ATOL converts the string of digits that ES:DI points at to a
	      long (signed) integer value and returns this value in DX:AX.
	      Note that the routine stops on the first non-digit.
	      If the string does not begin with a digit, this routine returns
	      zero.  The only exception to the "string of digits" only rule is
	      that the number can have a preceding minus sign to denote a
	      negative number.  Note that this routine does not allow leading
	      spaces.  ATOL2 works in a similar fashion except it doesn't
	      preserve the DI register.  That is, ATOL2 leaves DI pointing at
	      the first character beyond the string of digits.  ATOL/ATOL2 both
	      return the carry flag clear if it  translated the string of
	      digits without error.  It returns the carry flag set if overflow
	      occurred.


Include:              	stdlib.a or conv.a



Routine:  AtoUL (2)
-------------------

Category:            Conversion Routine

Register on entry:   ES:DI- address of the string to be converted

Register on return:  DX:AX- 32-bit unsigned integer
		     ES:DI- Points at first character beyond digits (ATOUL2
			    only)

Flags affected:      Carry flag- Set if error, clear if okay.

Examples of Usage:
		     les InputString
		     AtoUL


Description:  AtoUL converts the string pointed by ES:DI to a 32-bit unsigned
	      integer.  It places the 32-bit unsigned integer into the memory
	      address pointed by DX:AX. If there is an error in conversion,
	      the carry flag will set to one. If there is not an error, the
	      carry flag will be set to zero.

	      ATOUL2 does not preserve DI.  It returns with DI pointing at
	      the first non-digit character in the string.

Include:        stdlib.a or conv.a



Routine:    ATOU (2)
--------------------

Category:            Conversion Routine

Register on entry:   ES:DI points at string to convert

Register on return:  AX-    unsigned 16-bit integer
		     ES:DI- points at first non-digit (ATOU2 only)

Flags affected:      carry flag - error status

Example of Usage:

Description:    ATOU converts an ASCII string of digits, pointed to by ES:DI,
		to unsigned integer format. It places the unsigned 16-bit
		integer, converted from the string, into the AX register.
		ATOI works the same, except it handle unsigned 16-bit integers
		in the range 0..65535.

		ATOU2 leaves DI pointing at the first non-digit in the string.

Include:        stdlib.a or conv.a



Routine: ATOH (2)
-----------------

Category:             Conversion Routine

Registers on Entry:   ES:DI- Points to string to convert

Registers on Return:  AX- Unsigned 16-bit integer converted from hex string
		      DI (ATOH2)- First character beyond string of hex digits

Flags Affected:       Carry = Error status

Example of Usage:
		      les  DI, Str2Convrt
		      atoh                  ;Convert to value in AX.
		      putw                  ;Print word in AX.


Description:  ATOH converts a string of hexadecimal digits, pointed to by
	      ES:DI, into unsigned 16-bit numeric form. It returns the value in
	      the AX register.  If there is an error in conversion, the carry
	      flag will set to one.  If there is not an error, the carry flag
	      will be clear.  ATOH2 works the same except it leaves DI
	      pointing at the first character beyond the string of hex digits.

Include:        stdlib.a or conv.a


Routine: ATOLH (2)
------------------

Category:             Conversion Routine

Registers on Entry:   ES:DI- Points to string to convert

Registers on Return:  DX:AX- Unsigned 32-bit integer converted from hex string
		      DI (ATOLH2)- First character beyond string of hex digits

Flags Affected:       Carry = Error status

Example of Usage:
		      les  DI, Str2Convrt
		      atolh                 ;Convert to value in DX:AX

Description:  ATOLH converts a string of hexadecimal digits, pointed to by
	      ES:DI, into unsigned 32-bit numeric form. It returns the value in
	      the DX:AX register.  If there is an error in conversion, the carry
	      flag will set to one.  If there is not an error, the carry flag
	      will be clear.  ATOLH2 works the same except it leaves the DI
	      register pointing at the first non-hex digit.


Include:        stdlib.a or conv.a



Routine:   ATOI (2)
-------------------

Category:             Conversion Routine

Register on entry:    ES:DI- Points at string to convert.

Register on return:   AX- Integer converted from string.
		      DI (ATOI2)- First character beyond string of digits.

Flags affected:       Error status

Examples of Usage:
		      les  DI, Str2Convrt
		      atoi                 ;Convert to value in AX


Description:   Works just like ATOL except it translates the string to a
	       signed 16-bit integer rather than a 32-bit long integer.


Include:              stdlib.a or conv.a


Routine ITOA (2,M)
------------------

Category:             Conversion Routine

Registers on Entry:   AX- Signed 16-bit value to convert to a string
		      ES:DI- Pointer to buffer to hold result (ITOA/ITOA2
			     only).

Registers on Return:  ES:DI- Pointer to string containing converted
		      characters (ITOA/ITOAM only).
		      ES:DI- Pointer to zero-terminating byte of converted
			     string (ITOA2 only).

Flags Affected:       Carry flag is set on memory allocation error (ITOAM only)

Examples of Usage:
		      mov     ax, -1234
		      ITOAM                   ;Convert to string.
		      puts                    ;Print it.
		      free                    ;Deallocate string.

		      mov     di, seg buffer
		      mov     es, di
		      lea     di, buffer
		      mov     ax, -1234
		      ITOA		      ;Leaves string in BUFFER.

		      mov     di, seg buffer
		      mov     es, di
		      lea     di, buffer
		      mov     ax, -1234
		      ITOA2		      ;Leaves string in BUFFER and
					      ;ES:DI pointing at end of string.


Description:	These routines convert an integer value to a string of
		characters which represent that integer.  AX contains the
		signed integer you wish to convert.

		ITOAM automatically allocates storage on the heap for the
		resulting string, you do not have to pre-allocate this
		storage.  ITOAM returns a pointer to the (zero-terminated)
		string in the ES:DI registers.  It ignores the values in
		ES:DI on input.

		ITOA requires that the caller allocate the storage for the
		string (maximum you will need is seven bytes) and pass a
		pointer to this buffer in ES:DI.  ITOA returns with ES:DI
		pointing at the beginning of the converted string.

		ITOA2 also requires that you pass in the address of a buffer
		in the ES:DI register pair.  However, it returns with ES:DI
		pointing at the zero-terminating byte of the string.  This
		lets you easily build up longer strings via multiple calls
		to routines like ITOA2.

Include:        stdlib.a or conv.a



Routine:   UTOA (2,M)
---------------------

Category:            Conversion Routine

Registers on entry:   AX - unsigned 16-bit integer to convert to a string
		      ES:DI- Pointer to buffer to hold result (UTOA/UTOA2
			     only).

Registers on Return:  ES:DI- Pointer to string containing converted
		      characters (UTOA/UTOAM only).
		      ES:DI- Pointer to zero-terminating byte of converted
			     string (UTOA2 only).

Flags affected:       Carry set denotes malloc error (UTOAM only)

Example of Usage:
		      mov     ax, 65000
		      utoa
		      puts
		      free

		      mov     di, seg buffer
		      mov     es, di
		      lea     di, buffer
		      mov     ax, -1234
		      ITOA		      ;Leaves string in BUFFER.

		      mov     di, seg buffer
		      mov     es, di
		      lea     di, buffer
		      mov     ax, -1234
		      ITOA2		      ;Leaves string in BUFFER and
					      ;ES:DI pointing at end of string.


Description:    UTOAx converts a 16-bit unsigned integer value in AX to a
		string of characters which represents that value.  UTOA,
		UTOA2, and UTOAM behave in a manner analogous to ITOAx.  See
		the description of those routines for more details.


Include:       stdlib.a or conv.a



Routine:   HTOA (2,M)
---------------------

Category:            Conversion Routine

Registers on entry:   AL - 8-bit integer to convert to a string
		      ES:DI- Pointer to buffer to hold result (HTOA/HTOA2
			     only).

Registers on Return:  ES:DI- Pointer to string containing converted
		      characters (HTOA/HTOAM only).
		      ES:DI- Pointer to zero-terminating byte of converted
			     string (HTOA2 only).

Flags affected:      Carry set denotes memory allocation error (HTOAM only)


Description:    The HTOAx routines convert an 8-bit value in AL to the two-
		character hexadecimal representation of that byte.  Other
		that that, they behave just like ITOAx/UTOAx.  Note that
		the resulting buffer must have at least three bytes for
		HTOA/HTOA2.


Include:        stdlib.a or conv.a


Routine:  WTOA (2,M)
--------------------

Category:             Conversion Routine

Registers on Entry:   AX- 16-bit value to convert to a string
		      ES:DI- Pointer to buffer to hold result (WTOA/WTOA2
			     only).

Registers on Return:  ES:DI- Pointer to string containing converted
		      characters (WTOA/WTOAM only).
		      ES:DI- Pointer to zero-terminating byte of converted
			     string (WTOA2 only).

Flags Affected:       Carry set denotes memory allocation error (WTOAM only)

Example of Usage:
		      Like WTOA above


Description:  WTOAx converts the 16-bit value in AX to a string of four
	      hexadecimal digits. It behaves exactly like HTOAx except
	      it outputs four characters (and requires a five byte buffer).


Include:        stdlib.a or conv.a



Routine:  LTOA (2,M)
--------------------

Category:             Conversion Routine

Registers on entry:   DX:AX (contains a signed 32 bit integer)
		      ES:DI- Pointer to buffer to hold result (LTOA/LTOA2
			     only).

Registers on Return:  ES:DI- Pointer to string containing converted
		      characters (LTOA/LTOAM only).
		      ES:DI- Pointer to zero-terminating byte of converted
			     string (LTOA2 only).

Flags affected:       Carry set if memory allocation error (LTOAM only)


Example of Usage: 
			mov	di, seg buffer	;Get address of storage
			mov	es, di		; buffer.
			lea	di, buffer
			mov	ax, word ptr value
			mov	dx, word ptr value+2
			ltoa

Description:    LtoA converts the 32-bit signed integer in DX:AX to a string
		of characters.  LTOA stores the string at the address specified
		in ES:DI (there must be at least twelve bytes available at
		this address) and returns with ES:DI pointing at this buffer.
		LTOA2 works the same way, except it returns with ES:DI
		pointing at the zero terminating byte.  LTOAM allocates
		storage for the string on the heap and returns a pointer
		to the string in ES:DI.

Include:        stdlib.a or conv.a



Routine:  ULTOA (2,M)
---------------------

Category:             Conversion Routine

Registers on Entry:   DX:AX- Unsigned 32-bit value to convert to a string
		      ES:DI- Pointer to buffer to hold result (LTOA/LTOA2
			     only).
Registers on Return:  ES:DI- Pointer to string containing converted
		      characters (LTOA/LTOAM only).
		      ES:DI- Pointer to zero-terminating byte of converted
			     string (LTOA2 only).

Flags Affected:       Carry is set if malloc error (ULTOAM only)

Example of Usage:  
                      Like LTOA


Description:  Like LTOA except this routine handles unsigned integer values.

Include:	stdlib.a or conv.a



Routine:  SPrintf (2,M)
-----------------------

Category:            Conversion Routine
		     In-Memory Formatting Routine

Registers on entry:  CS:RET - Pointer to format string and operands of the
			      sprintf routine
		     ES:DI-   Address of buffer to hold output string
			      (sprintf/sprintf2 only)

Register on return:  ES:DI register - pointer to a string containing
				      output data (sprintf/sprintfm only).
				      Pointer to zero-terminating byte at the
				      end of the converted string (sprintf2
				      only).

Flags affected:      Carry is set if memory allocation error (sprintfm only).

Example of Usage:
		     sprintfm
		     db      "I=%i, U=%u, S=%s",13,10,0
		     db      i,u,s
		     puts
		     free


Description:   SPrintf is an in-memory formatting routine. It is similar to
	       C's sprintf routine.

	       The programmer selects the maximum length of the output string.
	       SPrintf works in a manner quite similar to printf, except sprintf
	       writes its output to a string variable rather than to the stdlib
	       standard output.

	       SPrintfm, by default, allocates 2048 characters for the string
	       and then deallocates any unnecessary storage.  An external
	       variable, sp_MaxBuf, holds the number of bytes to allocate upon
	       entry into sprintfm.  If you wish to allocate more or less than
	       2048 bytes when calling sprintf, simply change the value of this
	       public variable (type is word).  Sprintfm calls malloc to
	       allocate the storage dynamically.  You should call free to
	       return this buffer to the heap when you are through with it.

	       Sprintf and Sprintf2 expect you to pass the address of a buffer
	       to them.  You are responsible for supplying a sufficiently
	       sized buffer to hold the result.

Include:             stdlib.a or conv.a



Routine:  SScanf
----------------

Category:              Conversion Routine
		       Formatted In-Memory Conversion Routine

Registers on Entry:    ES:DI - points at string containing values to convert

Registers on return:   None

Flags affected:	       None

Example of Usage:

	      ; this code reads the values for i, j, and s from the characters
	      ; starting at memory location Buffer.

		       les   di, Buffer
		       SScanf
		       db    "%i %i %s",0
		       dd     i, j, s


Description:  SScanf provides formatted input in a fashion analogous to scanf.
              The difference is that scanf reads in a line of text from the
              stdlib standard input whereas you pass the address of a sequence
              of characters to SScanf in es:di.


Include:                stdlib.a or conv.a




Routine:  ToLower
-----------------

Category:            Conversion Routine

Register on entry:   AL- Character to (possibly) convert
				to lower case.

Register on return:  AL- Converted character.

Flags affected:      None

Example of usage:
		     mov     al, char
		     ToLower


Description:  ToLower checks the character in the AL register, if it is upper
	      case it converts it to lower case.  If it is anything else,
	      ToLower leaves the value in AL unchanged.  For high performance
	      this routine is implemented as a macro rather than as a
	      procedure call.  This routine is so short you would spend more
	      time actually calling the routine than executing the code inside.
	      However, the code is definitely longer than a (far) procedure
	      call, so if space is critical and you're invoking this code
	      several times, you may want to convert it to a procedure call to
	      save a little space.


Include:             stdlib.a or conv.a



Routine:   ToUpper
------------------

Category:             Conversion Routine

Registers on Entry:   AL- Character to (possibly) convert to upper case

Registers on Return:  AL- Converted character

Flags Affected:       None

Example of Usage:
		      mov  al, char
		      ToUpper


Description:  ToUpper checks the character in the AL register, if it is lower
	      case it converts it to upper case.  If it is anything else,
	      ToUpper leaves the value in AL unchanged.  For high performance
	      this routine is implemented as a macro rather than as a
	      procedure call (see ToLower, above).


Include:              stdlib.a or conv.a


=====================
Date & Time  Routines
=====================

These routines convert DOS system times and dates to/from ASCII strings.
They appear in this section rather than conversions because we eventually
intend to add date and time arithmetic to the package.

Note the time to string conversion routines do not output the hundredths of
a second.  Most applications do not need (or want) this.  If you want
hundredths of a second you can easily write a routine (using this code) or
modify the existing code to suit your purposes.




Routine:  DTOA (2,m)
--------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			CX-	Current year (in the range 1980-2099)
			DL-	Current day
			DH-	Current month
			ES:DI-	Points at buffer with room for at least
				nine bytes (DTOA/DTOA2 only).


Registers on Return: 	ES:DI-	DTOA sticks a string of the form MM/DD/YY
				into a buffer allocated on the heap (DTOAM
				only).

			ES:DI-	Points at the zero terminating byte at the
				end of the string (DTOA2 only).

Flags Affected:       carry-	Set if memory allocation error (DTOAM only).


Example of Usage:

			mov	ah, 2ah		;Call DOS to get the system
			int	21h		; time (also see xDTOA)
			lesi	TodaysDate	;Buffer to store string.
			DTOA			;Convert date to string.

			mov	ah, 2ah
			int	21h
			lesi	TodaysDate2
			DTOA2

			mov	ah, 2ah
			int	21h
			DTOAM			;ES:DI is allocated on heap.

Description:

DTOA converts a DOS system date (in CX/DX) to an ASCII string and deposits
the characters into a buffer specified by ES:DI on input.  ES:DI must be at
least nine bytes long (eight bytes for mm/dd/yy plus the zero terminating
byte).

DTOA2 converts a DOS system date to an ASCII string just like DTOA above.
The only difference is that it does not preserve DI.  It leaves DI pointing
at the zero terminating byte at the end of the string.  This routine is use-
ful for building up long strings with a date somewhere in the middle.

DTOAM works like DTOA except you do not pass the pointer to a buffer in ES:DI.
Instead, DTOAM allocates nine bytes for the string on the heap.  It returns
a pointer to this new string in ES:DI.

Include:              stdlib.a or date.a

Routine:  xDTOA (2,m)
---------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			ES:DI-	Points at buffer with room for at least
				nine bytes (xDTOA/xDTOA2 only).


Registers on Return: 	ES:DI-	DTOA sticks a string of the form MM/DD/YY
				into a buffer allocated on the heap (xDTOAM
				only).

			ES:DI-	Points at the zero terminating byte at the
				end of the string (xDTOA2 only).

Flags Affected:       carry-	Set if memory allocation error (xDTOAM only).


Example of Usage:

			lesi	TodaysDate	;Buffer to store string.
			xDTOA			;Convert date to string.

			lesi	TodaysDate2
			xDTOA2

			mov	ah, 2ah
			int	21h
			xDTOAM			;ES:DI is allocated on heap.

Description:

These routines work just like DTOA, DTOA2, and DTOAM except you do not pass
in the date to them, they call DOS to read the current system date and
convert that to a string.

Include:              stdlib.a or date.a


Routine:  LDTOA (2,m)
---------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			CX-	Current year (in the range 1980-2099)
			DL-	Current day
			DH-	Current month
			ES:DI-	Points at buffer with room for at least
				nine bytes (DTOA/DTOA2 only).


Registers on Return: 	ES:DI-	DTOA sticks a string of the form "mmm dd, yyyy"
				into a buffer allocated on the heap (LDTOAM
				only).

			ES:DI-	Points at the zero terminating byte at the
				end of the string (LDTOA2 only).

Flags Affected:       carry-	Set if memory allocation error (LDTOAM only).


Example of Usage:

			mov	ah, 2ah		;Call DOS to get the system
			int	21h		; time (also see xDTOA)
			lesi	TodaysDate	;Buffer to store string.
			LDATE			;Convert date to string.

			mov	ah, 2ah
			int	21h
			lesi	TodaysDate2
			LDTOA2

			mov	ah, 2ah
			int	21h
			LDTOAM			;ES:DI is allocated on heap.

Description:

These routines work just like the DTOA, DTOA2, and DTOAM routines except they
output their date in the form "mmm dd, yyyy", e.g., Jan 1, 1980.

Include:              stdlib.a or date.a

Routine:  xLDTOA (2,m)
---------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			ES:DI-	Points at buffer with room for at least
				nine bytes (xLDTOA/xLDTOA2 only).


Registers on Return: 	ES:DI-	Sticks a string of the form MMM DD, YYYY
				into a buffer allocated on the heap (xLDTOAM
				only).

			ES:DI-	Points at the zero terminating byte at the
				end of the string (xLDTOA2 only).

Flags Affected:       carry-	Set if memory allocation error (xLDTOAM only).


Example of Usage:

			lesi	TodaysDate	;Buffer to store string.
			xLDTOA			;Convert date to string.

			lesi	TodaysDate2
			xLDTOA2

			mov	ah, 2ah
			int	21h
			xLDTOAM			;ES:DI is allocated on heap.

Description:

Similar to xDTOA, xDTOA2, and xDTOAM except these routines produce strings of
the form "MMM DD, YYYY".

Include:              stdlib.a or date.a

Routine:  ATOD (2)
------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			ES:DI-	Points at string containing date to convert.


Registers on Return: 	CX-	Year (1980-2099)
			DH-	Month (1-12)
			DL-	Day (1-31)
			ES:DI-	Points at first non-date string (ATOD2 only)

Flags Affected:       	carry-	Set if bad date format.


Example of Usage:

			lesi	TodaysDate	;Buffer containing string.
			ATOD			;Convert string to date.
			jc	Error

			lesi	TodaysDate	;Buffer containing string.
			ATOD2			;Convert string to date.
			jc	Error

Description:

ATOD converts an ASCII string of the form "mm/dd/yy" or "mm-dd-yy" to a DOS
format date.  It returns the carry flag set if there is a parse error (that
is, the string is not in one of these two forms) or if the month, date, or
year values are out of range (including specifying Feb 29th on non-leap years).

ATOD2 works just like ATOD except it does not preserve DI.  It leaves DI
pointing at the first non-date character encountered in the string.

Include:              stdlib.a or date.a

Routine:  ATOT (2)
------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			ES:DI-	Points at string containing time to convert.


Registers on Return: 	CH-	Hour (0..23)
			CL-	Minutes (0..59)
			DH-	Seconds (0..59)
			DL-	Seconds/100 (0..99)

			ES:DI-	Points at first character which is not a part
				of the parsed time (ATOT2 only).

Flags Affected:       	carry-	Set if bad time format.


Example of Usage:

			lesi	CurrentTime	;Buffer containing string.
			ATOT			;Convert string to time.
			jc	Error

			lesi	CurrentTime	;Buffer containing string.
			ATOT2			;Convert string to time.
			jc	Error


Description:

ATOT converts an ASCII string of the form "hh:mm:ss" or "hh:mm:ss.xxx" to a DOS
format date.  It returns the carry flag set if there is a parse error (that
is, the string is not in one of these two forms) or if the hours, minutes,
seconds, or hundredth values are out of range.  If the string does not contain
1/100ths of a second, this routine returns zero in DL.

ATOT2 works just like ATOT except it does not preserve DI.  It leaves DI
pointing at the first character beyond the time characters.

Include:              stdlib.a or time.a

Routine:  TTOA (2,m)
--------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			CH-	Hour (0..23)
			CL-	Minutes (0..59)
			DH-	Seconds (0..59)
			DL-	1/100 seconds (0..99)
			ES:DI-	Points at buffer with room for at least
				nine bytes (TTOA/TTOA2 only).


Registers on Return: 	ES:DI-	Sticks a string of the form hh:mm:ss
				into a buffer allocated on the heap (TTOAM
				only).

			ES:DI-	Points at the zero terminating byte at the
				end of the string (TTOA2 only).

Flags Affected:       carry-	Set if memory allocation error (TTOAM only).


Example of Usage:

			mov	ah, 2ch		;Call DOS to get the system
			int	21h		; time (also see xTTOA)
			lesi	CurrentTime	;Buffer to store string.
			TTOA			;Convert Time to string.

			mov	ah, 2ch
			int	21h
			lesi	CurTime2
			TTOA2

			mov	ah, 2ch
			int	21h
			TTOAM			;ES:DI is allocated on heap.

Description:

TTOA converts the DOS system time in CX/DX to a string and stores the string
at the location specified by ES:DI.  ES:DI must point at a buffer with at
least nine characters in it (for a string of the form hh:mm:ss followed by
a zero terminating byte).

TTOA2 works like TTOA except it does not preserve DI.  It leaves DI pointing
at the zero terminating byte in the string.  This is useful for generating
long strings in memory of which TTOA is one component.

TTOAM is like TTOA except it automatically allocates storage for the string
on the heap.

Include:              stdlib.a or time.a

Routine:  xTTOA (2,m)
---------------------

Category:       Date/Time Routines

Author:		Randall Hyde

Registers on Entry:
			ES:DI-	Points at buffer with room for at least
				nine bytes (xTTOA/xTTOA2 only).


Registers on Return: 	ES:DI-	Sticks a string of the form HH:MM:SS
				into a buffer allocated on the heap xTTOAM
				only).

			ES:DI-	Points at the zero terminating byte at the
				end of the string (xTTOA2 only).

Flags Affected:       carry-	Set if memory allocation error (xTTOAM only).


Example of Usage:

			lesi	CurrentTime	;Buffer to store string.
			xTTOA			;Convert time to string.

			lesi	CurTime2
			xTTOA2

			xTTOAM			;ES:DI is allocated on heap.

Description:

These routines work just like TTOA, TTOA2, and TTOAM except you do not pass
in the time to them, they call DOS to read the current system time and
convert that to a string.

Include:              stdlib.a or time.a

File I/O Routines
-----------------

Although MS-DOS provides some fairly decent file I/O facilities, the MS-DOS
file routines are all block oriented.  That is, there is no simple routine
you can call to read a single character from a file (the most common case).
Although you can create a buffer consisting of a single byte and call MS-DOS
to read a single character into that buffer, this is very slow.  The standard
library file I/O routines provide a set of buffered I/O routines for sequent-
ially accessed files.  These routines are suitable only for files which you
sequentially read or sequentially write.  They do not work with random access
files nor can you alternately read and write to the file.  However, most file
accesses fall into the category of sequential read or write so the Standard
Library routines will work fine in most cases.  In other cases, MS-DOS
provides a reasonable API so there really isn't a need for augmentation in
the Standard Library.

The Standard Library provides routines to OPEN a file (for reading or writing
only), CREATE a new file and open it for writing, CLOSE a file, FLUSH the file
buffers associated with a file, GET a character from a file, READ a block of
bytes from a file, PUT a single character to a file, or write a block of chars
to a file.

Note that you can use the standard I/O redirection operations to redirect the
standard input and output to routines which read and write bytes through a
file.  Consider the following short routine:

Redir2File	proc	far
		push	ds
		push	es
		push	di
		mov	di, seg MyFileVar
		mov	ds, di

		les	di, ds:MyFileVar
		fputc

		pop	di
		pop	es
		pop	ds
		ret
Redir2File	endp

This routine, when called, writes the character in AL to the file specified
by the file variable "MyFileVar" (see an explanation of the FPUTC routine
for more details).  You can selectively redirect all of the standard output
routines through this procedure (hence sending all standard output to the
file) using the Standard Library SetOutAdrs routine:

		lesi	Redir2File
		SetOutAdrs

		<use print, printf, puts, puti, etc. here, all output
		 goes to the file rather than to the screen.>

		lesi	PutcStdOut	;Default DOS output
		SetOutAdrs

		<Now all output goes back to the DOS standard output>

You can also preserve the previous output address using the code:

		lesi	Redir2File
		PushOutAdrs

		<use print, printf, puts, puti, etc. here, all output
		 goes to the file rather than to the screen.>

		PopOutAdrs

		<Now all output goes back to the previous handler.>


You can do the same thing with the standard input routines when redirecting
input from a file, though this is less useful.


All file I/O routines in the library use a "File Variable" to keep track of
the specified file.  *THIS IS NOT THE SAME THING AS A DOS FILE HANDLE!*
"FileVar" is a structure defined in the "file.a" include file.  For each file
you open/close, you must create a unique file variable.


Routine:  FOPEN
---------------

Category:             File I/O

Registers on Entry:	AX contains file open mode
				(0=open for read, 1=open for write)
			ES:DI points at a file variable.
			DX:SI points at a file name.

Registers on return:	Carry is set/clear for error/no error.
			AX contains (DOS) error code if carry is set.

Flags affected:
			Carry denotes error.

Example of Usage:

MyFileVar		FileVar	<>
MyFileName		db	"file.nam"
			.
			.
			.
			mov	ax, 0			;Open for reading
			lesi	MyFileVar		;Ptr to file variable.
			ldxi	MyFileName		;Ptr to file name.
			fopen
			jc	Error

Description:

fopen opens a sequential file for reading or writing.  It calls DOS to 
actually open the file and then sets up appropriate internal variables (in
the FileVar variable) to provide efficient blocked I/O.

Include:              stdlib.a or file.a

Routine:  FCREATE
-----------------

Category:             File I/O

Registers on Entry:	
			ES:DI points at a file variable.
			DX:SI points at a file name.

Registers on return:	Carry is set/clear for error/no error.
			AX contains (DOS) error code if carry is set.

Flags affected:
			Carry denotes error.

Example of Usage:

MyFileVar		FileVar	<>
MyFileName		db	"file.nam"
			.
			.
			.
			lesi	MyFileVar		;Ptr to file variable.
			ldxi	MyFileName		;Ptr to file name.
			fcreate
			jc	Error

Description:

fcreate opens a new file for reading.  If the file already exists, fcreate
will delete it and create a new one.  Other than this, the behavior is
quite similar to fopen.

Include:              stdlib.a or file.a

Routine:  FCLOSE
----------------

Category:             File I/O

Registers on Entry:	
			ES:DI points at a file variable.

Registers on return:	Carry is set/clear for error/no error.
			AX contains (DOS) error code if carry is set.

Flags affected:
			Carry denotes error.

Example of Usage:

MyFileVar		FileVar	<>
			.
			.
			.
			lesi	MyFileVar		;Ptr to file variable.
			fclose
			jc	Error

Description:

fclose closes a file opened by fcreate or fopen.  Note that you *must* use
this call to close the file (rather than using DOS' close call).  There may
be "hot" data present in internal buffers.  This call flushes such data to
the file.

Note that you must make this call before quitting your application.  DOS will
automatically close all files upon quitting, but DOS will not automatically
flush any hot data to disk upon program termination.

Include:              stdlib.a or file.a

Routine:  FFLUSH
----------------

Category:             File I/O

Registers on Entry:	
			ES:DI points at a file variable.

Registers on return:	Carry is set/clear for error/no error.
			AX contains (DOS) error code if carry is set.

Flags affected:
			Carry denotes error.

Example of Usage:

Ptr2FileVar		dd	MyFileVar
			.
			.
			.
			les	di, Ptr2FileVar		;Ptr to file variable.
			fflush
			jc	Error

Description:

fflush will write any "hot" data (data written to the file by an application
which is currently sitting in internal buffers) to the file.  It is a good
idea to occassionally flush files to disk if you do not write the data to
the file all at once.  This helps prevents loss of data in the event of an
abnormal termination.

Include:              stdlib.a or file.a

Routine:  FGETC
---------------

Category:             File I/O

Registers on Entry:	
			ES:DI points at a file variable.

Registers on return:	AL contains byte read (if no error, C=0).
			AX contains (DOS) error code if carry is set.

Flags affected:
			Carry denotes error.

Example of Usage:

Ptr2FileVar		dd	MyFileVar
			.
			.
			.
			les	di, Ptr2FileVar		;Ptr to file variable.
			fgetc
			jc	Error
			<AL contains byte read at this point>

Description:

fgetc reads a single byte from a file opened for reading.  On EOF the carry
flag will be set and AX will contain zero.

Include:              stdlib.a or file.a

Routine:  FREAD
---------------

Category:             File I/O

Registers on Entry:	
			ES:DI points at a file variable.
			DX:SI points at the destination block.
			CX contains the number of bytes to read.

Registers on return:	AX contains actual # of bytes read (if no error, C=0).
			AX contains (DOS) error code if carry is set (AX=0
				denotes EOF).

Flags affected:
			Carry denotes error.

Example of Usage:

MyFileVar		FileVar	<>
MyBlock			db	256 dup (?)
			.
			.
			.
			lesi	MyFileVar		;Ptr to file variable.
			ldxi	MyBlock			;Place to put data.
			mov	cx, 256			;# of bytes to read.
			fread
			jc	Error

Description:

fread lets you read a block of bytes from a file opened for reading.  This
call is generally *much* faster than reading a string of single bytes if you
want to read a large number of bytes at one time.

Include:              stdlib.a or file.a

Routine:  FPUTC
---------------

Category:             File I/O

Registers on Entry:	
			ES:DI points at a file variable.
			AL    contains the character to write to the file.

Registers on return:
			AX contains (DOS) error code if carry is set.

Flags affected:
			Carry denotes error.

Example of Usage:

Ptr2FileVar		dd	MyFileVar
			.
			.
			.
			les	di, Ptr2FileVar		;Ptr to file variable.
			mov	al, Char2Write
			fputc
			jc	Error

Description:

fputs writes a single byte to a file opened for writing (or opened via the
fcreate call).  It writes the byte in AL to the output file.  Note that data
written via this call may not be written directly to the file.  For performance
reasons the fputc routine buffers up the data in memory and writes large blocks
of data to the file.  If you need to ensure that the data is properly written
to the file you will need to make a call to fclose or fflush.

Include:              stdlib.a or file.a

Routine:  FWRITE
----------------

Category:             File I/O

Registers on Entry:	
			ES:DI points at a file variable.
			DX:SI points at the source block.
			CX contains the number of bytes to write.

Registers on return:	AX contains actual # of bytes written (if no error).
			AX contains (DOS) error code if carry is set (AX=0
				denotes EOF).

Flags affected:
			Carry denotes error.

Example of Usage:

MyFileVar		FileVar	<>
MyBlock			db	256 dup (?)
			.
			.
			.
			lesi	MyFileVar		;Ptr to file variable.
			ldxi	MyBlock			;Place to put data.
			mov	cx, 256			;# of bytes to read.
			fwrite
			jc	Error

Description:

fwrite lets you write a block of bytes to a file opened for writing.  This
call is generally *much* faster than writing a string of single bytes if you
want to read a large number of bytes at one time.  Note that fwrite, like
fputc, buffers up data before writing it to disk.  If you need to commit
data to the disk surface at some point, you must call the fflush or fclose
routines.

Include:              stdlib.a or file.a

Floating Point Routines
-----------------------

The floating point routines provide a basic floating point package for
80x86 assembly language users.  The floating point package deals with
four different floating point formats: IEEE 32-bit, 64-bit, and 80-bit
formats, and an internal 81-bit format.  The external formats mostly
support the IEEE standard except for certain esoteric values such as
denormalized numbers, NaNs, infinities, and other such cases.

The package provides two "pseudo-registers", a floating point accumulator
and a floating point operand.  It provides routines to load and store these
pseudo-registers from memory operands (using the various formats) and then
all other operations apply to these two operands.  All computations use the
internal 81-bit floating point format.  The package automatically converts
between the internal format and the external format when loading and storing
values.

Do not write code which assumes the internal format is 81 bits.  This format
will change in the near future when I get a chance to add guard bits to
all the computations.  If your code assumes 81 bits, it will break at that
point.  Besides, there is no reason your code should count on the size of
the internal operations anyway.  Stick with the IEEE formats and you'll
be much better off (since your code can be easily upgraded to deal with
numeric coprocessors).

WARNING: These routines have not been sufficiently tested as of 10/10/91.
Use them with care.  Report any problems with these routines to Randy Hyde
via the electronic addresses provided in this document or by sending a
written report to UC Riverside.  As I get more time, I will further test
these routines and add additional functions to the package.

					*** Randy Hyde



Routine:  lsfpa
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at a single precision (32-bit) value to load

Registers on return:  None

Flags affected:       None

Example of Usage:
			les di, FPValue
			lsfpa

Description:	LSFPA loads a single precision floating point value into the
		internal floating point accumulator.  It also converts the
		32-bit format to the internal 81-bit format used by the
		floating point package.

Include:	stdlib.a or fp.a

Routine:  ssfpa
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at a single precision (32-bit) value where
		      this routine should store the floating point acc.

Registers on return:  None

Flags affected:       Carry set if conversion error.

Example of Usage:
			les di, FPValue
			ssfpa

Description:	SSFPA stores the floating point accumulator into a single
		precision variable in memory (pointed at by ES:DI).  It
		converts the value from the 81-bit format to the 32-bit
		value before storing the result.   The 64-bit mantissa used
		by the FP package is rounded to 24 bits during the store.
		The exponent could be out of range.  If this occurs, SSFPA
		returns with the carry flag set.

Include:	stdlib.a or fp.a


Routine:  ldfpa
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at a double precision (64-bit) value to load

Registers on return:  None

Flags affected:       None

Example of Usage:
			les di, FPValue
			ldfpa

Description:	LDFPA loads a double precision floating point value into the
		internal floating point accumulator.  It also converts the
		64-bit format to the internal 81-bit format used by the
		floating point package.

Include:	stdlib.a or fp.a

Routine:  sdfpa
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at a double precision (64-bit) value where
		      this routine should store the floating point acc.

Registers on return:  None

Flags affected:       Carry set if conversion error.

Example of Usage:
			les di, FPValue
			sdfpa

Description:	SDFPA stores the floating point accumulator into a double
		precision variable in memory (pointed at by ES:DI).  It
		converts the value from the 81-bit format to the 64-bit
		value before storing the result.   The 64-bit mantissa used
		by the FP package is rounded to 51 bits during the store.
		The exponent could be out of range.  If this occurs, SDFPA
		returns with the carry flag set.

Include:	stdlib.a or fp.a


Routine:  lefpa
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at an extended precision (80-bit) value to
		      load

Registers on return:  None

Flags affected:       None

Example of Usage:
			les di, FPValue
			lefpa

Description:	LEFPA loads an extended precision floating point value into
		the internal floating point accumulator.  It also converts the
		80-bit format to the internal 81-bit format used by the
		floating point package.

Include:	stdlib.a or fp.a


Routine:  lefpal
----------------

Category:             Floating point Routine

Registers on entry:   CS:RET points at an extended precision (80-bit) value to
		      load

Registers on return:  None

Flags affected:       None

Example of Usage:
			lefpal
			dt	1.345e-3

Description:	LEFPAL loads an extended precision floating point value into
		the internal floating point accumulator.  It also converts the
		80-bit format to the internal 81-bit format used by the
		floating point package.

		Unlike LEFPA, LEFPAL gets its operand directly from the code
		stream.  You must follow the call to lefpal with a ten-byte
		(80-bit) floating point constant.
Include:	stdlib.a or fp.a

Routine:  sefpa
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at an extended precision (80-bit) value
		      where this routine should store the floating point acc.

Registers on return:  None

Flags affected:       Carry set if conversion error.

Example of Usage:
			les di, FPValue
			sefpa

Description:	SEFPA stores the floating point accumulator into an extended
		precision variable in memory (pointed at by ES:DI).  It
		converts the value from the 81-bit format to the 80-bit
		value before storing the result.

		The exponent could be out of range.  If this occurs, SEFPA
		returns with the carry flag set.

Include:	stdlib.a or fp.a


Routine:  lsfpo
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at a single precision (32-bit) value to load

Registers on return:  None

Flags affected:       None

Example of Usage:
			les di, FPValue
			lsfpo

Description:	LSFPA loads a single precision floating point value into the
		internal floating point operand.  It also converts the
		32-bit format to the internal 81-bit format used by the
		floating point package.

Include:	stdlib.a or fp.a


Routine:  ldfpo
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at a double precision (64-bit) value to load

Registers on return:  None

Flags affected:       None

Example of Usage:
			les di, FPValue
			ldfpo

Description:	LDFPO loads a double precision floating point value into the
		internal floating point operand.  It also converts the
		64-bit format to the internal 81-bit format used by the
		floating point package.

Include:	stdlib.a or fp.a


Routine:  lefpo
---------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at an extended precision (80-bit) value to
		      load

Registers on return:  None

Flags affected:       None

Example of Usage:
			les di, FPValue
			lefpo

Description:	LEFPO loads an extended precision floating point value into
		the internal floating point operand.  It also converts the
		80-bit format to the internal 81-bit format used by the
		floating point package.

Include:	stdlib.a or fp.a


Routine:  lefpol
----------------

Category:             Floating point Routine

Registers on entry:   CS:RET points at an extended precision (80-bit) value to
		      load

Registers on return:  None

Flags affected:       None

Example of Usage:
			lefpal
			dt	1.345e-3

Description:	LEFPOL loads an extended precision floating point value into
		the internal floating point operand.  It also converts the
		80-bit format to the internal 81-bit format used by the
		floating point package.

		Unlike LEFPO, LEFPOL gets its operand directly from the code
		stream.  You must follow the call to lefpal with a ten-byte
		(80-bit) floating point constant.
Include:	stdlib.a or fp.a


Routine:  itof
--------------

Category:             Floating point Routine

Registers on entry:   AX contains a signed integer value

Registers on return:  None

Flags affected:       None

Example of Usage:
			mov	ax, -1234
			itof

Description:	ITOF converts the 16-bit signed integer in AX to a floating
		point value, storing the result in the floating point
		accumuator.

Include:	stdlib.a or fp.a


Routine:  utof
--------------

Category:             Floating point Routine

Registers on entry:   AX contains an unsigned integer value

Registers on return:  None

Flags affected:       None

Example of Usage:
			mov	ax, -1234
			itof

Description:	UTOF converts the 16-bit unsigned integer in AX to a floating
		point value, storing the result in the floating point
		accumuator.

Include:	stdlib.a or fp.a


Routine:  ultof
---------------

Category:             Floating point Routine

Registers on entry:   DX:AX contains an unsigned 32-bit integer value

Registers on return:  None

Flags affected:       None

Example of Usage:
			mov	dx, word ptr val32+2
			mov	ax, word ptr val32
			ultof

Description:	ULTOF converts the 32-bit unsigned integer in DX:AX to a
		floating point value, storing the result in the floating
		point accumuator.

Include:	stdlib.a or fp.a


Routine:  ltof
--------------

Category:             Floating point Routine

Registers on entry:   DX:AX contains a signed 32-bit integer value

Registers on return:  None

Flags affected:       None

Example of Usage:
			mov	dx, word ptr val32+2
			mov	ax, word ptr val32
			ltof

Description:	LTOF converts the 32-bit signed integer in DX:AX to a
		floating point value, storing the result in the floating
		point accumuator.

Include:	stdlib.a or fp.a


Routine:  ftoi
--------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  AX contains 16-bit signed integer

Flags affected:       Carry is set if conversion error occurs.

Example of Usage:
			ftoi
			puti		;Print AX as integer value


Description:	FTOI converts the floating point accumulator value to a
		16-bit signed integer and returns the result in AX.  If
		the floating point number will not fit in AX, FTOI returns
		with the carry flag set.

Include:	stdlib.a or fp.a


Routine:  ftou
--------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  AX contains 16-bit unsigned integer

Flags affected:       Carry is set if conversion error occurs.

Example of Usage:
			ftou
			putu		;Print AX as an unsigned value


Description:	FTOU converts the floating point accumulator value to a
		16-bit unsigned integer and returns the result in AX.  If
		the floating point number will not fit in AX, FTOU returns
		with the carry flag set.

Include:	stdlib.a or fp.a


Routine:  ftol
--------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  DX:AX contains a 32-bit signed integer

Flags affected:       Carry is set if conversion error occurs.

Example of Usage:
			ftol
			putl		;Print DX:AX as integer value


Description:	FTOL converts the floating point accumulator value to a
		32-bit signed integer and returns the result in DX:AX.  If
		the floating point number will not fit in DX:AX, FTOL returns
		with the carry flag set.

Include:	stdlib.a or fp.a


Routine:  ftoul
---------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  DX:AX contains a 32-bit unsigned integer

Flags affected:       Carry is set if conversion error occurs.

Example of Usage:
			ftoul
			putul		;Print DX:AX as an integer value


Description:	FTOUL converts the floating point accumulator value to a
		32-bit unsigned integer and returns the result in DX:AX.  If
		the floating point number will not fit in DX:AX, FTOUL returns
		with the carry flag set.

Include:	stdlib.a or fp.a


Routine:  fpadd
---------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  None

Flags affected:       None

Example of Usage:
			fpadd

Description:	FPADD adds the floating point operand to the floating point
		accumulator leaving the result in the floating point
		accumulator.

Include:	stdlib.a or fp.a


Routine:  fpsub
---------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  None

Flags affected:       None

Example of Usage:
			fpsub

Description:	FPSUB subtracts the floating point operand from the floating
		point accumulator leaving the result in the floating point
		accumulator.

Include:	stdlib.a or fp.a


Routine:  fpcmp
---------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  AX contains result of comparison.

Flags affected:       As appropriate for a comparison.  You can use the
		      conditional branches to check the comparison after
		      calling this routine.  Be sure to use the *signed*
		      conditional jumps (e.g., JG, JGE, etc.).

Example of Usage:
			fpcmp
			jge	FPACCgeFPOP

Description:	FPCMP compares the floating point accumulator to the
		floating point operand and sets the flags according to the
		result of the comparison.  It also returns a value in AX
		as follows:

			AX	Result
			-1	FPACC < FPOP
			 0	FPACC = FPOP
			 1	FPACC > FPOP

Include:	stdlib.a or fp.a


Routine:  fpmul
--------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  None

Flags affected:       None

Example of Usage:
			fpmul

Description:	FPMUL multiplies the floating point accumulator by the floating
		point operand and leaves the result in the floating point
		accumulator.

Include:	stdlib.a or fp.a


Routine:  fpdiv
---------------

Category:             Floating point Routine

Registers on entry:   None

Registers on return:  None

Flags affected:       None

Example of Usage:
			fpdiv

Description:	FPDIV divides the floating point accumulator by the floating
		point operand and leaves the result in the floating point
		accumulator.

Include:	stdlib.a or fp.a


Routine:  ftoa (2,m)
--------------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at buffer to hold result (ftoa/ftoa2 only)
		      AL- Field width for floating point value.
		      AH- Number of positions to the right of the dec pt.

Registers on return:  ES:DI points at beginning of string (ftoa/ftoam only)
		      ES:DI points at zero terminating byte (ftoa2 only)

Flags affected:       Carry is set if malloc error (ftoam only)

Example of Usage:
			mov	di, seg buffer
			mov	es, di
			lea	di, buffer
			mov	ah, 2		;Two digits after "."
			mov	al, 10		;Use a total of ten positions
			ftoa



Description:	FTOA (2,M) converts the value in the floating point accumulator
		to a string of characters which represent that value.  These
		routines use a decimal representation.  The value in AH is
		the number of digits to put after the decimal point, AL
		contains the total field width (including room for the sign
		and decimal point).  The field width specification works
		just like Pascal or FORTRAN.  If the number will not fit in
		the specified field width, FTOA outputs a bunch of "#"
		characters.

		FTOA stores the converted string at the address specified by
		ES:DI upon entry.  There must be at least AL+1 bytes at this
		address.  It returns with ES:DI pointing at the start of this
		buffer.

		FTOA2 works just like FTOA except it does not preserve DI.
		It returns with DI pointing at the zero terminating byte.

		FTOAM allocates storage for the string on the heap and returns
		a pointer to the converted string in ES:DI.

		Note: this routine preserves the value in the floating point
		accumulator but it wipes out the value in the floating point
		operand.

Include:	stdlib.a or fp.a


Routine:  etoa (2,m)
--------------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at buffer to hold result (etoa/etoa2 only)
		      AL- Field width for floating point value.

Registers on return:  ES:DI points at beginning of string (etoa/etoam only)
		      ES:DI points at zero terminating byte (etoa2 only)

Flags affected:       Carry is set if malloc error (etoam only)

Example of Usage:
			mov	al, 14		;Use a total of 14 positions
			etoam
			puts
			putcr
			free



Description:	ETOA (2,M) converts the value in the floating point accumulator
		to a string of characters which represent that value.  These
		routines use an exponential (scientific notation)
		representation.  AL contains the field width.  It contains
		the number of print position to use when outputting the
		number.  The field width specification works just like Pascal
		or FORTRAN.  If the number will not fit in the specified
		field width, ETOA outputs a bunch of "#" characters.

		ETOA stores the converted string at the address specified by
		ES:DI upon entry.  There must be at least AL+1 bytes at this
		address.  It returns with ES:DI pointing at the start of this
		buffer.

		ETOA2 works just like ETOA except it does not preserve DI.
		It returns with DI pointing at the zero terminating byte.

		ETOAM allocates storage for the string on the heap and returns
		a pointer to the converted string in ES:DI.

		Note: this routine preserves the value in the floating point
		accumulator but it wipes out the value in the floating point
		operand.

Include:	stdlib.a or fp.a


Routine:  atof
--------------

Category:             Floating point Routine

Registers on entry:   ES:DI points at a string containing the representation
		      of a floating point number in ASCII form.

Registers on return:  None

Flags affected:       None

Example of Usage:
			les	di, FPStr
			atof


Description:    ATOF converts the string pointed at by ES:DI into a floating
		point value and leaves this value in the floating point
		accumulator.  Legal floating point values are described
		by the following regular expression:


		{" "}* {+ | -} ( ([0-9]+ {"." [0-9]*}) | ("." [0-9]+)}
				{(e | E) {+ | -} [0-9] {[0-9]*}}

 "{}" denote optional items.
 "|"  denotes OR.
 "()" groups items together.



Include:	stdlib.a or fp.a

Memory Management Routines
--------------------------

The stdlib memory management routines let you dynamically allocate storage on
the heap.  These routines are somewhat similar to those provided by the "C"
programming language.  However, these routines do not perform garbage
collection, as this would introduce too many restrictions and have a very
adverse effect on speed.

The following paragraph gives a description of how the memory management
routines work.  These routines may be updated in future revisions, however,
so you should never make assumptions about the structure of the memory
management record (described below) or else your code may not work on the
next revision.

The allocation/deallocation routines should be fairly fast.  Malloc and free
use a modified first/next fit algorithm which lets the system quickly find a
memory block of the desired size without undue fragmentation problems (average
case).  The memory manager data structure has an overhead of eight bytes
(meaning each malloc operation requires at least eight more bytes than you ask
for) and a granularity of 16 bytes.  The overhead (eight bytes) per allocated
block may seem rather high, but that is part of the price to pay for faster
malloc and free routines.  All pointers are far pointers and each new item is
allocated on a paragraph boundary.  The current memory manager routines always
allocate (n+8) bytes, rounding up to the next multiple of 16 if the result is
not evenly divisible by sixteen.  The first eight bytes of the structure are
used by the memory management routines, the remaining bytes are available for
use by the caller (malloc, et. al., return a pointer to the first byte beyond
the memory management overhead structure).

NOTE: There was a major change in the way this package works starting with
version 30 of the library.  Prior to version 30, MemInit required a parameter
in the DX register to determine where to allocate the heap and how much
storage to allocate.  Furthermore, the older versions called DOS to deallocate
memory then reallocate it for the heap.  Finally, the older versions required
that you set up a global variable "PSP" containing the program segment
prefix value.

As of version 30, MemInit was split into two routines: MemInit and MemInit2.
MemInit allocates all of available memory (like the standard version of the
earlier MemInit) whereas MemInit2 lets you specify the location and size
of the heap.  The new version calls DOS to get the PSP (so you don't need
to declare the PSP variable just for MemInit).  The new version does not
reallocate memory blocks with DOS calls (which created some problems,
especially with debugger programs).  Finally the new versions work fine
with ".EXE" files which do not get all leftover memory allocated to them.

Most older STDLIB programs will work just fine with the new MemInit routine.
If you relied on MemInit to reallocate memory for you, or if you specified
the location of the heap, you will need to modify your program to use these
new versions of the MemInit routine.


Routine:  MemInit
-----------------

Category:               Memory Management Routine

Registers on Entry:     Nothing
Globals Affected:       zzzzzzseg - segment name of the last segment in your
				    program

Registers on return:    CX - number of paragraphs actually reserved by MemInit


Flags affected:         None

Example of Usage:
						; Don't forget to set up
						; zzzzzzseg before calling
						; MemInit
			MemInit


Description:  This routine initializes the memory manager system.  You must
	      call it before using any routines which call any of the memory
	      manager procedures (since a good number of the stdlib routines
	      call the memory manager, you should get in the habit of always
	      calling this routine.)  The system will "die a horrible death"
	      if you call a memory manager routine (like malloc) without first
	      calling MemInit.

	      This routine expects you to define (and set up) a global
	      names: zzzzzzseg.  "zzzzzzseg" is a dummy segment which
	      must be the name of the very last segment defined in your
	      program.  MemInit uses the name of this segment to determine the
	      address of the last byte in your program.  If you do not
	      declare this segment last, the memory manager will overwrite
	      anything which follows zzzzzzseg.  The "shell.asm" file
	      provides you with a template for your programs which properly
	      defines this segment.

	      On return from MemInit, the CX register contains the number of
	      paragraphs actually allocated.


Include:                stdlib.a or memory.a

Routine:  MemInit2
------------------

Category:               Memory Management Routine

Registers on Entry:     ES-	segment address of the start of the heap.
			CX-	Number of paragraphs to allocate for the heap.

Registers on return:    None
Flags affected:         None

Example of Usage:
			mov	cx, seg HeapSeg
			mov	es, cx
			mov	cx, HeapSize		;In paragraphs!
			MemInit2


Description:  This routine initializes the memory manager system.  You must
	      call it before using any routines which call any of the memory
	      manager procedures (since a good number of the stdlib routines
	      call the memory manager, you should get in the habit of always
	      calling this routine.)  The system will "die a horrible death"
	      if you call a memory manager routine (like malloc) without first
	      calling MemInit2 (or MemInit).

	      This routine lets you decide where the heap lies in memory
	      (as opposed to MemInit which uses all available bytes from
	      the end of your program to the end of memory).

	      Note: you should only call MemInit or MemInit2 once in your
	      program.

Include:                stdlib.a or memory.a

Routine:  Malloc
----------------

Category:              Memory Management Routine

Registers on Entry:    CX - number of bytes to reserve

Registers on return:   CX - number of bytes actually reserved by Malloc
		       ES:DI - ptr to 1st byte of memory allocated by Malloc

Flags affected:        Carry=0 if no error.
		       Carry=1 if insufficient memory.

Example of Usage:
		       mov     cx, 256
		       Malloc
		       jnc     GoodMalloc
		       print   db    "Insufficient memory to continue.",cr,lf,0
		       jmp   Quit
	  GoodMalloc:  mov   es:[di], 0          ;Init string to NULL


Description:  Malloc is the workhorse routine you use to allocate a block of
	      memory.  You give it the number of bytes you need and if it
	      finds a block large enough, it will  allocate the requested
	      amount and return a pointer to that block.

	      Most memory managers require  a small amount of overhead for each
	      block they allocate.  Stdlib's (current) memory manager requires
	      an overhead of eight bytes.  Furthermore, the grainularity is 16
	      bytes.  This means that Malloc always allocates blocks of memory
	      in paragraph multiples.  Therefore, Malloc may actually reserve
	      more storage than you specify. Therefore, the value returned in
	      CX may be somewhat greater than the requested value.  By setting
	      the minimum allocation size to a paragraph, however, the
	      overhead is reduced and the speed of Malloc is improved by a
	      considerable amount.

	      Stdlib's memory management does not do any garbage collection.
	      Doing so would place too many demands on Malloc's users.
	      Therefore, it is quite possible for you to fragment memory with
	      multiple calls to maloc, realloc, and free.  You could wind up in
	      a situation where there is enough free memory to satisfy your
	      request, but there isn't a single contiguous block large enough
	      for the request.  Malloc treats this as an insufficient memory
	      error and returns with the carry flag set.

	      If Malloc cannot allocate a block of the requested size, it
	      returns with the carry flag set.  In this situation, the contents
	      of ES:DI is undefined.  Attempting to dereference this pointer
	      will produce erratic and, perhaps, disasterous results.

Include:              stdlib.a or memory.a


Routine:  Realloc
-----------------

Category:  Memory Management Routine

Registers on Entry:   CX - number of bytes to reserve
		      ES:DI - pointer to block to  reallocate.

Registers on return:  CX - number of bytes actually reserved by Realloc.
		      ES:DI - pointer to first byte of memory allocated by
			      Realloc.

Flags affected:       Carry = 0 if no error.
		      Carry = 1 if insufficient memory.

Example of Usage:
			mov	cx, 1024	;Change block size to 1K
			les	di, CurPtr	;Get address of block into ES:DI
			realloc
			jc	BadRealloc
			mov	word ptr CurPtr, di
			mov	word ptr CurPtr+2, es


Description:  Realloc lets you change the size of an allocated block in the
	      heap.  It allows you to make the block larger or smaller.
	      If you make the  block smaller, Realloc simply frees (returns
	      to the heap) any leftover bytes at the end of the block.  If
	      you make the block larger, Realloc goes out and allocates a
	      block of the requested size, copies the bytes form the old
	      block to the beginning of the new block (leaving the bytes at
	      the end of the new block uninitialized)), and then frees the
	      old block.


Include:               stdlib.a or memory.a


Routine:  Free
--------------

Category:               Memory Management Routine

Registers on Entry:     ES:DI - pointer to block to deallocate

Registers on return:    None

Flags affected:         Carry = 0 if no error.
			Carry = 1 if ES:DI doesn't point at a Free block.

Example of Usage:
			les     di, HeapPtr
			Free

Description:  Free (possibly) deallocates storage allocated on the heap by
	      malloc or Realloc.  Free returns this storage to heap so other
	      code can reuse it later.  Note, however, that Free doesn't
	      always return storage to the heap.  The memory manager data
	      structure keeps track of the number of pointers currently
	      pointing at a block on the heap (see DupPtr, below).  If you've
	      set up several pointers such that they point at the same block,
	      Free will not deallocate the storage until you've freed all of
	      the pointers which point at the block.

	      Free usually returns an error code (carry flag = 1) if you
	      attempt to Free a block which is not currently allocated or if
	      you pass it a memory address which was not returned by malloc
	      (or Realloc).  By no means is this routine totally robust.
	      If you start calling free with arbitrary pointers in es:di
	      (which happen to be pointing into the heap) it is possible,
	      under certain circumstances, to confuse Free and it will attempt
	      to free a block it really should not.

	      This problem could be solved by adding a large amount of extra
	      code to the free routine, but it would slow it down considerably.
	      Therefore, a little safety has been sacrificed for a lot of
	      speed.  Just make sure your code is correct and everything will
	      be fine!


Include:               stdlib.a or memory.a


Routine:  DupPtr
----------------

Category:             Memory Manager Routine

Registers on Entry:   ES:DI - pointer to block

Registers on return:  None

Flags affected:       Carry = 0 if no error.
		      Carry = 1 if es:di doesn't point at a free block.

Example of Usage:
		      les     di,  Ptr
		      DupPtr


Description:  DupPtr increments the pointer count for the block at the
	      specifiied address.  Malloc sets this counter to one.  Free
	      decrements it by one.  If free decrements the value and it
	      becomes zero, free will release the storage to the heap for
	      other use.  By using DupPtr you can tell the memory manager
	      that you have several pointers pointing  at the same block
	      and that it shouldn't deallocate the storage until you free
	      all of those pointers.


Include:              stdlib.a or memory.a


Routine:  IsInHeap
------------------

Category:             Memory Management Routine

Registers on Entry:   ES:DI - pointer to a block

Registers on return:  None

Flags affected:       Carry = 0 if ES:DI is a valid pointer.
		      Carry = 1 if not.

Example of Usage:
			les	di, MemPtr
			IsInHeap
			jc	NotInHeap

Description:  This routine lets you know if es:di contains the address of
	      a byte in the heap somewhere.  It does not tell you if es:di
	      contains a valid pointer returned by malloc (see IsPtr, below).
	      For example, if es:di contains the address of some particular
	      element of an array (not necessarily the first element)
	      allocated on the heap, IsInHeap will return with the carry clear
	      denoting that the es:di points somewhere in the heap.  Keep in
	      mind that calling this routine does not validate the pointer;
	      it could be pointing at a byte which is part of the memory
	      manager data structure rather than at actual data (since the
	      memory manager maintains that informatnion within the
	      bounds of the heap). This routine is mainly useful for seeing
	      if something is allocated on the heap as opposed to somewhere
	      else (like your code, data, or stack segment).


Include:              stdlib.a or memory.a

Routine:  IsPtr
---------------

Category:               Memory Management Routine

Registers on Entry:     ES:DI - pointer to block

Registers on return:    None

Flags affected:         Carry = 0 if es:di is a valid pointer.
			Carry = 1 if not.

Example of Usage:
			les	di, MemPtr
			IsPtr
			jc	NotAPtr



Description:  IsPtr is much more specific than IsInHeap.  This routine returns
	      the carry flag clear if and only if es:di contains the address
	      of a properly allocated (and currently allocated) block on the
	      heap.  This pointer must be a value returned by Malloc, Realloc,
	      or DupPtr and that block must be currently allocated for IsPtr
	      to return the carry flag clear.


Include:                stdlib.a or memory.a

Routine:  BlockSize
-------------------

Category:               Memory Management Routine

Registers on Entry:     ES:DI - pointer to block

Registers on return:    CX-	Size of specifed block (in bytes). Returns
				zero if ES:DI does not point at a legal block.

Flags affected:		None

Example of Usage:
			les	di, MemPtr
			BlockSize



Description:

BlockSize returns the size (in bytes) of a block allocated on the heap.
If the block is not in the heap, this code returns zero in CX.
This routine does NOT verify that the block was actually allocated and
is still allocated.  It just makes sure that the pointer points at a valid
location somewhere in the heap and returns the block size from the data
structure at the specified address.  You are responsible for ensuring that
you do not use a deallocated memory block.

Include:                stdlib.a or memory.a

Routine:  MemAvail
------------------

Category:               Memory Management Routine

Registers on Entry:     None

Registers on return:    CX-	Size of largest free block on the heap
Flags affected:		None

Example of Usage:
			MemAvail



Description:

MemAvail returns the size (in paragraphs) of the largest free block on the
heap.  You can use this call to determine if there is sufficient storage
for an object on the heap.

Include:                stdlib.a or memory.a

Routine:  MemFree
-----------------

Category:               Memory Management Routine

Registers on Entry:     None

Registers on return:    CX-	Size of all free blocks on the heap.
Flags affected:		None

Example of Usage:
			MemFree



Description:

MemFree returns the size (in paragraphs) of the the free storage on the
heap.  Note that this storage might be fragmented and not all of it may
be available for use by Malloc.  To determine the largest free block
available use MemAvail.

Include:                stdlib.a or memory.a
Process Manager Routines
------------------------

The UCR Standard Library Process package provides a simple preemptive
multitasking package for 8086 assembly language programmers.  It also
provides a coroutine package and support for semaphores.

First,  *AND THIS IS VERY IMPORTANT*, this package only supports the
8086, 8088, 80188, 80186, and 80286 processors operating in real mode.
You will need to make some minor modifications to the process package if
you wish to support the 32-bit x86 processors.  The current process manager
only saves 16-bit registers, not the 32-bit registers of the 80386 and later.
You will, however, find it a relatively minor task to go in and modify this
code to support the 386 and later processors.  We will probably add support
for these processors at a later date when time allows.

Second, *THIS IS ALSO VERY IMPORTANT*, keep in mind that DOS, BIOS, and many
of the routines in the standard library ARE NOT REENTRANT. Two processes
executing at the (apparent) same time cannot both be executing DOS, BIOS, or
the same standard library routines.  It is unlikely that DOS or BIOS will
ever be made reentrant, and you shouldn't ever expect the standard library
to be made reentrant (far too much work).  The standard library provides
semaphore support through which you can control access to critical resources
including DOS, BIOS, and the UCR Standard Library.  If you are unfamiliar
with terms like reentrancy, semaphores, synchronization, and deadlock, you
should probably pick up a good text on operating systems and familiarize
yourself with these terms before attempting to use this package.

This process package provides three facilities to your assembly language
programs: A preemptive multitasking process manager, coroutine support, and
semaphore support.  There are six routines associated with the preemptive
multitasking system: PRCSINIT, PRCSQUIT, FORK, KILL, DIE, and YIELD.  There
are three routines associated with the coroutines package: COINIT, COCALL,
and COCALLL (though you'll rarely refer to COCALLL directly).  Finally, there
are two routines to support semaphores: WAITSEMAPH and RLSSEMAPH.

The PRCSINIT and PRCSQUIT routines initialize and deinitialize the interrupt
system for the multitasking system.  You must call PRCSINIT prior to
executing any of the preemptive multitasking routines or any of the semaphore
routines (the semaphore routines make sense only in the context of preemptive
multitasking).  This initializes various internal variables and patches the
INT 8 interrupt vector (timer interrupt) to point at an internal routine
in the process manager.  You must call PRCSQUIT when you are done with the
preemptive multitasking system; certainly you must call it before your
program terminates *FOR ANY REASON*.  If you do not call PRCSQUIT, the system
will probably crash shortly after you try anything else after returning to
DOS since the timer interrupt will still be calling the routine left in
memory when your program terminates.

The process manager patches into the 1/18th second clock on the PC.  There-
fore, the system will automatically perform an context switch every 55ms
or so.  If your application reprograms the timer chip, this may produce
unexpected results.  This may be particularly bothersome if you are running
a TSR which plays with the timer chip.  Absolutely no attempt was made to
make this code robust enough to work in all cases with other code which
ties into the timer interrupt.  Most well-written code will work fine, but
there are not guarantees.

The FORK routine lets you spawn a new process.  For each call to FORK your
code makes, the FORK routine returns twice- once as the parent process and
once as the child process.  FORK returns process ID information in the AX
and BX registers so that the code immediately following the FORK can figure
out if it's the parent or child process.  FORK provides the basic (and only!)
mechanism for starting a second process.

The KILL and DIE routines let you terminate a process.  KILL lets one process
terminate some other process (generally a child process).  DIE lets a
process terminate itself.

The YIELD routine gives up the current process' time slice and lets some other
process take over.

The semaphore routines, WaitSemaph and RlsSemaph, let you wait on a semaphore
or signal that semaphore, respectively.  The PROCESS.A and PROCESS.A6
include files contain the definition of a semaphore type, it is

		semaphore	struc
		SemaCnt		dw	1
		smaphrLst	dd	?
		endsmaphrlst	dd	?
		semaphore	ends

The only field you should ever play around with is the SemaCnt field.  This
value is the number of processes which are allowed to be in the critical
region controlled by the semaphore at one time.  For most mutual exclusion
problems, this value should always be one.  Do not modify this value once
the program starts running.  The process package increments and decrements
this number to keep track of the number of processes waiting to use a
resource.  If you want to allow two processes to share a resource at the
same time, you should declare your semaphore variable as follows:

		MySemaPh	semaphore	<2>

You execute the WaitSemaPh routine to see if a semaphore is currently busy.
When you get back from the WaitSemaPh call, the resource protected by the
semaphore is exclusively yours until you execute the RlsSemaPh routine.
Note that when you call the WaitSemaPh routine, the specified resource may
already be in use, in which case your process will be suspended until the
resource is freed (and anyone waiting in line ahead of you has had their
shot at the resource).  If you do not call the RlsSemaPh routine to free
the semaphore, any other process waiting on that resource will wait
indefinitely.  Also note that if you call WaitSemaPh twice on a semaphore
without releasing it inbetween, your process and any other process which
waits on that resource will deadlock.

While semaphores solve a large number of synchronization and mutual exclusion
problems, their primary use in the UCR Standard Library is to prevent re-
entrancy problems in DOS, BIOS, and the Standard Library itself.  For example,
if you have two processes which print values to the display, attempting to
run both processes concurrently will crash the system if they both attempt
to print at the same time (since this will cause DOS to be reentered).  A
simple solution is to use a DOS semaphore as follows:

In the data segment:

DOS		semaphore	{}

In Process 1:

		lesi	DOS
		WaitSemaPh
		print
		db	"Printed from process #1.",cr,lf,0
		lesi	DOS
		RlsSemaPh

In Process 2:

		lesi	DOS
		WaitSemaPh
		print
		db	"Printed from process #2.",cr,lf,0
		lesi	DOS
		RlsSemaPh

Semaphore guarantee mutual exclusion between the WaitSemaPh and RlsSemaPh
calls (for a particular semaphore variable, DOS in this case).  Hence, once
process #1 enters its *CRITICAL REGION* by executing the WaitSemaPh call,
any attempt by process two to enter its critical region will cause process
two to suspend execution until process one executes the RlsSemaPh routine.

Coroutines provide "simulated" multitasking where the processes themselves
determine when to perform a context switch.  This is quite similar to the
"cooperative multitasking" systems provided by Apple, Microsoft, and others
("cooperative multitasking" is a hyped up term to hide the fact that their
systems provide only multiprogramming, not multitasking).  There are many
advantages and disadvantages to coroutines vs. multitasking.  First of all,
reentrancy problems do not exist in a system using coroutines.  Since you
control when one process switches to another, you can make sure that such
context switches do not occur in critical regions.  Another advantage to
coroutines is that the processes themselves can determine which other process
gets the next access to the CPU.  Finally, when a coroutine is executing,
it gets full access to the CPU to handle a time-critical operation without
fear of being preempted.  On the other hand, poorly designed coroutines
provide a very crude approximation to multitasking and may actually hurt
the overall performance of the system.

The UCR Standard Library provides three routines to support coroutines:
COINIT, COCALL, and COCALLL.  Generally, you'll only see COINIT and COCALL
in a program, the standard library automatically generates COCALLL calls
for certain types of COCALL statements.  The COINIT routine initializes the
coroutine package and creates a process control block (PCB) for the currently
active routine.  COCALL switches context to some other process.  When one
process COCALLs another and that second process COCALLs the first, the first
process continues execution immediately after the first COCALL instruction
(so it behaves more like a return than a call).  In general, you should not
think of COCALL as a "call" but rather as a "switch to some other process."

You may have coroutines and multitasking active at the same time, but you
should not make a COCALL to a process which is being time-sliced by the
multitasking system.  I won't guarantee that this *won't* work, but it
seems sufficiently weird that something is bound to go wrong.

For those who are interested, the coroutine and multitasking packages
maintain the state of a process in a process control block (PCB) which
is the following structure:



		pcb		struc
		NextProc	dd	?
		regsp		dw	?
		regss		dw	?
		regip		dw	?
		regcs		dw	?

		regax		dw	?
		regbx		dw	?
		regcx		dw	?
		regdx		dw	?
		regsi		dw	?
		regdi		dw	?
		regbp		dw	?
		regds		dw	?
		reges		dw	?
		regflags	dw	?
		PrcsID		dw	?
		StartingTime	dd	?
		StartingDate	dd	?
		CPUTime		dd	?
		pcb		ends

As mentioned earlier, this code does not maintain the full state of the
80386 and later processors since it only saves the 16-bit register set.
If you like, you can easily change the definition of the PCB, and all the
code in the PROCESS.ASM file which refers to the PCB, and support full
32-bit operation.  As usual, you should rarely, if ever, play around with
the internal fields of a PCB.  That is for the process manager to do and you
could mess things up pretty back if you're not careful.

Routine: prcsinit
-----------------

Category:             Processes

Registers on entry:   None

Registers on return:  None

Flags affected:       None

Example of Usage:

			prcsinit


Description:

Prcsinit initializes the process manager.  Note that if you make a call to
the process manager, you *MUST* make a call to prcsquit before your program
quits.  Failure to do so will crash the system in short order.  Note that
you must even handle the case where the user types control-C or encounters
a critical error and aborts back to DOS.

This routine patches into the timer interrupt vector (INT 08) and may not
work properly with code in the system which is also messing around with the
timer interrupt.

Include:	stdlib.a or process.a

Routine: prcsquit
-----------------

Category:             Processes

Registers on entry:   None

Registers on return:  None

Flags affected:       None

Example of Usage:

			prcsquit


Description:

Prcsquit deinitializes the process manager.  It detaches the timer interrupt
from the internal routine and performs various other clean up chores.  You
must call this routine before your program terminates if you've called
prcsinit somewhere in your program.

Note that you cannot call prcsquit twice in a row, although you can call
the prcsinit/prcsquit combinations as many times as you like in your code.

Include:	stdlib.a or process.a

Routine: fork
-------------

Category:             Processes

Registers on entry:   	ES:DI-	Points at a PCB to hold the process info
				for the new process.  The regss and regsp
				fields of this PCB must be initialized to
				the top of a new stack for the new process.

Registers on return:  	AX-	<Parent process> Returned containing zero.
				<Child  process> Child process ID.

			BX-     <Parent process> Child's process ID.
				<Child  process> Returned containing zero.

Flags affected:       	None

Example of Usage:

ChildPCB		pcb	{0, offset endstk, seg endstk}
			 .
			 .
			 .

			lesi	ChildPCB
			fork
			cmp	ax, 0
			jne	DoChildProcess
			<Parent process continues here>
			 .
			 .
			 .
ChildsStack		db	1024 dup (?)
EndStk			dw	0



Description:

Fork spawns a new process.  You make a single call to fork but it returns
twice- once for the parent process (the original caller to fork) and once
for the child process (the new process created by fork).  On entry to fork
ES:DI must point at a PCB for the new process which has the REGSP and
REGSS fields initialized to point at the last word in a stack set aside for
the child process.  *THIS IS VERY IMPORTANT!*

On return, the code following the call to FORK can test to see whether the
parent is returning or the child is returning by looking at the value in
the AX register.  AX will contain zero upon return to the parent process.
It will contain a non-zero value, which is the process ID, when returning
to the child process.  When the parent process returns, FORK returns the
process ID of the child process in the BX register.  The parent process can
use this value to kill the child process, should that become necessary later
on.  If the child needs access to the parent's process ID, the parent process
should store away its process ID in a variable before calling the FORK
routine.  Note that with the exception of the AX, BX, SP, and SS registers,
FORK preserves all register values and returns the same set of values to both
the parent and child processes.  In particular, it preserves the value of
the DS register so the child will have access to any global variables in
use by the parent process.

FORK does *not* copy the parent's stack data to the child's stack.  Upon
return from FORK, the child's stack is empty.  If you call FORK after
pushing something on the stack (e.g., a return address because you've called
FORK inside some other procedure), that information will not be placed on the
stack of the child process.  If you such information pushed on the child's
stack, you will need to save SS:SP prior to calling FORK (in a global
variable) and then push the data pointed at by this saved value onto the
child's stack upon return.  Of course, it's a whole lot easier if you simply
don't count on anything being on the child's stack when you get back from
FORK.  In particular, don't call FORK from inside some nested routine and
expect the child process to return to the caller of the routine containing
FORK.

Include:	stdlib.a or process.a

Routine: die
------------

Category:             Processes

Registers on entry:   None

Registers on return:  None

Flags affected:       None

Example of Usage:

			die

Description:

Die kills the current process.  Control is transferred to some other process
in the process manager's ready-to-run queue.  Control never returns back to
the current process.

Note: if the current process is the only process running, calling DIE may
crash the system.

Include:	stdlib.a or process.a

Routine: kill
-------------

Category:             	Processes

Registers on entry:   	AX-	Process ID of process to terminate.

Registers on return:  	None

Flags affected:       	None

Example of Usage:

			mov	ax, ProcessID
			kill

Description:

KILL lets one process terminate another.  Typically, a parent might kill a
child process, although any process which knows the process ID of some other
process can kill that other process.

If a process passes its own ID to KILL, the system behaves exactly as though
the process called the DIE routine.

If a process passes its own ID to KILL and it is the only process in the
system, the system may crash.

Include:	stdlib.a or process.a

Routine: yield
--------------

Category:             	Processes

Registers on entry:   	None

Registers on return:  	None

Flags affected:       	None

Example of Usage:

			yield

Description:

YIELD voluntarily gives up the CPU.  The remainder of the current time slice
is given to some other process and the current process goes to the end of the
process manager's ready to run queue.  This call is particularly useful for
passing control between two cooperating process where one process needs to
wait for some action to complete and you don't feel like using semaphores
to synchronize the two activies.  This call is roughly equivalent to
"COCALL <next available process>".

Include:	stdlib.a or process.a

Routine: coinit
---------------

Category:             	Processes

Registers on entry:   	ES:DI-	Points at an empty PCB for the current process.

Registers on return:  	None

Flags affected:       	None

Example of Usage:

MainProcess		pcb	{}
			 .
			 .
			 .
			lesi	MainProcess
			coinit


Description:

COINIT initializes the coroutine package and sets up an internal pointer
to the PCB specified by ES:DI (the "current coroutine" pcb).  On the next
COCALL, the process state will be saved in the pcb you've specified on the
COINIT call.  Note that you do not have to initialize this pcb in any way,
that will all be taken care of by COINIT and the COCALL.

Include:	stdlib.a or process.a

Routine: cocall/cocalll
-----------------------

Category:             	Processes

Registers on entry:   	ES:DI-	Points at a PCB for the new coroutine
				(COCALL only).
			CS:IP-	Points at a pointer to a PCB for the new
				coroutine (COCALLL only).

Registers on return:  	None

Flags affected:       	None

Example of Usage:

OtherProcess		pcb	{----}
YetAnotherProcess	pcb	{----}
			 .
			 .
			 .
			lesi	OtherProcess
			cocall
			 .
			 .
			 .
			cocall	YetAnotherProcess

Description:

COCALL switches context between two coroutines.  There are two versions of
this call, although you use COCALL to invoke both of them: COCALL and COCALLL.
For COCALL, you must pass the address of a PCB in ES:DI.  When calling COCALL
in this fashion, the operand field of the COCALL instruction must be blank
(see the example above).  COCALLL expects the address of the pcb to follow
in the code stream.  The COCALL macro looks for an operand and, if one is
present, it automatically creates the appropriate call to COCALLL and inserts
the address of the PCB in the code stream (again, see the example above).

Before you start a coroutine for the first time by calling COCALL, you must
properly initialize the pcb for that coroutine.  You must provide initial
values for the regsp, regss, regip, and regcs fields of the pcb (fields two
through five in the pcb structure).  Regsp and regss must point at the last
word of an appropriately sized stack for that coroutine; regip and regcs
must point at the initial entry point for the coroutine.  For example, if you
want to switch between the current process and a coroutine named "CORTN", you
could use the following code:

MainCoRtn	pcb	{}
CoRtnPCB	pcb	{0,offset CoRtnStk, seg CoRtnStk,
						offset CoRtn, seg CoRtn}
		 .
		 .
		 .
		lesi	MainCoRtn
		coinit
		 .
		 .
		 .
		cocall	CoRtnPCB
		 .
		 .
		 .
CoRtn		proc
		 .
		 .
		 .
		cocall	MainCoRtn
		 .
		 .
		 .
CoRtn		endp
		 .
		 .
		 .
		db	1024 dup (?)
CoRtnStk	dw	0


Include:	stdlib.a or process.a

Routine: waitsemaph
-------------------

Category:             	Processes

Registers on entry:   	ES:DI-	Points at a semaphore variable

Registers on return:  	None

Flags affected:       	None

Example of Usage:

DOSsemaph		semaphore	{}
			 .
			 .
			 .
			lesi	DOSsemaph
			WaitSemaPh
			 .
			 . <This is the critical section>
			 .
			lesi	DOSsemaph
			RlsSemaPh

Description:

WaitSemaPh and RlsSemaPh protect critical regions in a multitasking
environment. WaitSemaPh expects you to pass the address of a semaphore
variable in ES:DI.  If that particular semaphore is not currently in use,
WaitSemaPh marks the semaphore "in use" and immediately returns.  If the
semaphore is already in use, the WaitSemaPh queues up the current process
on a waiting queue and lets some other process start running.  Once a process
is done with the resource protected by a semaphore, it must call RlsSemaPh
to release the semaphore back to the system.  If any processes are waiting
on that semaphore, the call to RlsSemaPh will activate the first such process.
Note that a process must not make two successive calls to WaitSemaPh on a
particular semaphore variable without calling RlsSemaPh between the calls.
Doing so will cause a deadlock.

Include:	stdlib.a or process.a

Routine: rlssemaph
------------------

Category:             	Processes

Registers on entry:   	ES:DI-	Points at a semaphore variable

Registers on return:  	None

Flags affected:       	None

Example of Usage:	See WaitSemaPh

Description:

RlsSemaPh releases a semaphore (also known as "signalling") that the current
process has aquired via a call to WaitSemaPh.  Please see the WaitSemaPh
explaination for more details.  You should not call RlsSemaPh without first
calling WaitSemaPh.  Doing so may cause some inconsistencies in the system.

Include:	stdlib.a or process.a

Interrupt-Driven Serial Port I/O Package
========================================

One major problem the the PC's BIOS is the lack of good interrupt driven
I/O support for the serial port.  The BIOS provides a mediocre set of polled
I/O facilities, but completely drops the ball on interrupt driven I/O.

This set of routines in the standard library provides polled I/O support
to read and set the registers on the 8250 (or other comparable chip, e.g.,
16450) as well as read and write data (polled).  In addition, there are
a pair of routines to initialize and disable the interrupt system as well
as perform I/O using interrupts.

Typical polled I/O session:

1. Initialize chip using polled I/O routines.
2. Read and write data using ComRead and ComWrite routines.

Typical interrupt driven I/O session:

1. Initialize chip using polled I/O routines.
2. Read and write data using ComIn and ComOut routines.

Of course, all the details of serial communications cannot be discussed
here- it's far too broad a subject.  These routines, like most in the
library, assume you know what you're doing.  They just make it a little
easier on you.  If you don't understand anything about serial communications,
you *might* be able to use these routines, but they were not written with
that audience in mind.  There are several good references on serial communi-
cations; "C Programmer's Guide to Serial Communications" comes to mind.  If
you've never looked at the 8250 or comparable chips before, you might want
to take a look at a reference such as this one if the routines in this
section don't make much sense.

Note: This routines are set up to use the COM1: hardware port.  See the
source listings if you want to access a different serial port.  Perhaps in
a future release we will modify this code to work with any serial port.


Routine:  ComBaud
-----------------

Author:		      	Randall Hyde

Category:		Serial Communications

Registers on entry:   	AX-	BPS (baud rate): 110, 150, 300, 600, 1200,
						 2400, 4800, 9600, 19200

Registers on return:  	None

Flags affected:       	None

Example of Usage:
			mov	ax, 9600	;Set system to 9600 bps
			ComBaud
Description:

ComBaud programs the serial chip to change its "baud rate" (technically,
it's "bits per second" not baud rate).  You load AX with the appropriate
bps value and call ComBaud, as above.  Note: if AX is not one of the legal
values, ComBaud defaults to 19.2kbps.

Include:	ser.a or stdlib.a

Routine:  ComStop
-----------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AX-	# of stop bits (1 or 2)

Registers on return:  	None

Flags affected:       	None

Example of Usage:
			mov	ax, 2		;Set system to send 2 stop bits
			ComStop
Description:

ComStop programs the serial chip to transmit the specifed number of stop
bits when sending data.  You load AX with the appropriate value and call
ComStop, as above.  Note that this only affects the output data stream. The
serial chip on the PC will always work with one incoming stop bit, regardless
of the setting.  Since additional stop bits slow down your data transmission
(by about 10%) and most devices work fine with one stop bit, you should
normally program the chip with one stop bit unless you encounter some
difficulties.  The setting of this value depends mostly on the system you
are connecting to.

Include:	ser.a or stdlib.a

Routine:  ComSize
-----------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AX-	# of data bits to transmit (5, 6, 7, or 8)

Registers on return:  	None

Flags affected:       	None

Example of Usage:
			mov	ax, 8	;Set system to send 8 data bits
			ComSize
Description:

ComSize programs the serial chip to transmit the specifed number of data
bits when sending data.  You load AX with the appropriate value and call
ComSize, as above.  The setting of this value depends mostly on the system
you are connecting to.

Include:	ser.a or stdlib.a

Routine:  ComParity
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AX-    Bits 0, 1, and 2 are defined as follows:
			       bit 0- 1 to enable parity, 0 to disable.
			       bit 1- 0 for odd parity, 1 for even.
			       bit 2- Stuck parity bit.  If 1 and bit 0 is 1, then the parity bit
				      is always set to the inverse of bit 1.

Registers on return:  	None

Flags affected:       	None

Example of Usage:
			mov	ax, 0	;Set NO parity
			ComParity
			 .
			 .
			 .
			mov	ax, 11b	;Set even parity
			ComParity
Description:

ComParity programs the serial chip to use various forms of parity error
checking.  If bit zero of AX is zero, then this routine disables parity
checking and transmission.  In this case, ComParity ignores the other
two bits (actually, the 8250 ignores them, ComParity just passes them
through).  If bit zero is a one, and bit two is a zero, then bit #1
defines even/odd parity during transmission and receiving.  If bit #0
is a one and bit two is a one, then the 8250 will always transmit bit #1
as the parity bit (forced on or off).

Include:	ser.a or stdlib.a

Routine:  ComRead
-----------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL-	Character read from port

Flags affected:       	None

Example of Usage:
			ComRead
			mov	Buffer, al

Description:

ComRead polls the port to see if a character is available in the on-chip
data register.  If not, it waits until a character is available.  Once
a character is available, ComRead reads it and returns this character in
the AL register.

Warning: do *not* use this routine while operating in the interrupt mode.
This routine is for polled I/O only.

Include:	ser.a or stdlib.a

Routine:  ComWrite
------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AL-	Character to write to port

Registers on return:  	None

Flags affected:       	None

Example of Usage:
			mov	al, 'a'
			ComWrite

Description:

ComWrite polls the port to see if the transmitter is busy.  If so, it waits
until the current transmission is through.  Once the 8250 is done with the
current character, ComWrite will put the character in AL into the 8250
transmit register.

Warning: do *not* use this routine while operating in the interrupt mode.
This routine is for polled I/O only.

Include:	ser.a or stdlib.a

Routine:  ComTstIn
------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL=0 if no char available, 1 if char available

Flags affected:       	None

Example of Usage:

		Wait4Data:	ComTstIn
				cmp	al, 0
				je	Wait4Data

Description:

ComTstIn polls the port to see if any input data is available.  If so,
it returns a one in AL, else it returns a zero.

Warning: do *not* use this routine while operating in the interrupt mode.
This routine is for polled I/O only.

Include:	ser.a or stdlib.a

Routine:  ComTstOut
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL = 1 if xmitr available, 0 if not

Flags affected:       	None

Example of Usage:

		WriteData:	<Do Something>
				ComTstOut
				cmp	al, 0
				je	WriteData
				mov	al, 'a'
				ComWrite

Description:

ComTstIn polls the port to see if the transmitter is currently busy.  If so,
it returns a zero in AL, else it returns a one.

Warning: do *not* use this routine while operating in the interrupt mode.
This routine is for polled I/O only.

Include:	ser.a or stdlib.a

Routine:  ComGetLSR
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL = LSR value

Flags affected:       	None

Example of Usage:

			ComGetLSR
			<do something with value in LSR>
Description:

Reads the LSR (line status register) and returns this value in AL.  The
LSR using the following layout.

 Line Status Register (LSR):

 bit 0-	Data Ready
 bit 1-	Overrun error
 bit 2-	Parity error
 bit 3-	Framing error
 bit 4-	Break Interrupt
 bit 5-	Transmitter holding register is empty.
 bit 6-	Transmit shift register is empty.
 bit 7-	Always zero.

Warning: In general, it is not a good idea to call this routine while
the interrupt system is active.  It won't hurt anything, but the value
you get back may not reflect properly upon the last/next character you
read.

Include:	ser.a or stdlib.a

Routine:  ComGetMSR
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL = MSR value

Flags affected:       	None

Example of Usage:

			ComGetMSR
			<do something with value in MSR>
Description:

The MSR (modem status register) bits are defined as follows:

 Modem Status Register (MSR):

 bit 0-	Delta CTS
 bit 1-	Delta DSR
 bit 2-	Trailing edge ring indicator
 bit 3-	Delta carrier detect
 bit 4-	Clear to send
 bit 5-	Data Set Ready
 bit 6-	Ring indicator
 bit 7-	Data carrier detect


Warning: In general, it is not a good idea to call this routine while
the interrupt system is active.  It won't hurt anything, but the value
you get back may not reflect properly upon the last/next character you
read.

Include:	ser.a or stdlib.a

Routine:  ComGetMCR
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL = MCR value

Flags affected:       	None

Example of Usage:

			ComGetMCR
			<do something with value in MCR>
Description:

The MCR (modem control register) bits are defined as follows:

 Modem Control Register (MCR):

 bit 0-		Data Terminal Ready (DTR)
 bit 1-		Request to send (RTS)
 bit 2-		OUT 1
 bit 3-		OUT 2
 bit 4-		Loop back control.
 bits 5-7-	Always zero.


The DTR and RTS bits control the function of these lines on the 8250.
They are useful mainly for polled I/O handshake operations (though they
*could* be used with interrupt I/O, it's rarely necessary unless your
main application is *really* slow and the data is coming in real fast.

Out1 and Out2 control output pins on the 8255.  Keep in mind that the OUT1
pin enables/disables the serial port interrupts.  Play with this *only* if
you want to control the interrupt enable.

Loop back control is mainly useful for testing the serial port or checking
to see if a serial chip is present.

Include:	ser.a or stdlib.a

Routine:  ComSetMCR
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AL = new MCR value

Registers on return:  	None

Flags affected:       	None

Example of Usage:

			mov	al, NewMCRValue
			ComSetMCR

Description:

This routine writes the value in AL to the modem control register.  See
ComGetMCR for details on the MCR register.

Include:	ser.a or stdlib.a

Routine:  ComGetLCR
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL = LCR value

Flags affected:       	None

Example of Usage:

			ComGetLCR
			<do something with value in LCR>
Description:

The LCR (line control register) bits are defined as follows:

 Line Control Register (LCR):

 bits 0,1-	Word length (00=5, 01=6, 10=7, 11=8 bits).
 bit 2-		Stop bits (0=1, 1=2 stop bits [1-1/2 if 5 data bits]).
 bit 3-		Parity enabled if one.
 bit 4-		0 for odd parity, 1 for even parity (assuming bit 3 = 1).
 bit 5-		1 for stuck parity.
 bit 6-		1=force break.
 bit 7-		1=Divisor latch access bit.  0=rcv/xmit access bit.

Since the standard library provides routines to initialize the serial chip
(which is the purpose of this port) you shouldn't really mess with this
port at all.  You may, however, use ComGetLCR to see what the current
settings are before making any changes.

Warning: (applies mainly to ComSetLCR) DO NOT, UNDER ANY CIRCUMSTANCES,
CHANGE THE DIVISOR LATCH ACCESS BIT WHILE OPERATING IN INTERRUPT MODE.
The interrupt service routine assumes the rcv/xmit register is mapped in
whenever an interrupt occurs.  If you must play with the divisor latch,
turn off interrupts before changing it.  Always set the divisor latch
access bit back to zero before turning interrupts back on.

Include:	ser.a or stdlib.a

Routine:  ComSetLCR
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AL = new LCR value

Registers on return:  	None

Flags affected:       	None

Example of Usage:

; If this maps in the divisor latch, be sure we're not operating with
; serial interrupts!

			mov	al, NewLCRValue
			ComSetLCR

Description:

This routine writes the value in AL to the line control register.  See
ComGetLCR for details on the LCR register.  Especially note the warning
about the divisor latch access bit.

Include:	ser.a or stdlib.a

Routine:  ComGetIIR
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL = IIR value

Flags affected:       	None

Example of Usage:

			ComGetIIR
			<do something with value in IIR>
Description:

The IIR (interrupt identification register) bits are defined as follows:

 Interrupt ID Register (IIR):

 bit 0-		No interrupt is pending (interrupt pending if zero).
 bits 1,2-	Binary value denoting source of interrupt:
			00-Modem status
			01-Transmitter Hold Register Empty
			10-Received Data Available
			11-Receiver line status
 bits 3-7	Always zero.

This value is of little use to anyone except the interrupt service routine.
The ISR is the only code which should really access this port.

Include:	ser.a or stdlib.a

Routine:  ComGetIER
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL = IER value

Flags affected:       	None

Example of Usage:

			ComGetIER
			<do something with value in IER>
Description:

The IER (line control register) bits are defined as follows:

 Interupt enable register (IER):

		If one:
 bit 0-		Enables received data available interrupt.
 bit 1-		Enables transmitter holding register empty interrupt.
 bit 2-		Enables receiver line status interrupt.
 bit 3-		Enables the modem status interrupt.
 bits 4-7-	Always set to zero.

Normally, the interrupt initialization procedure sets up this port.  You may
read or change its value as you deem necessary to control the types of
interrupts the system generates.  Note that the interrupt service routine
(ISR) in the library ignores errors.  You will need to modify the ISR if you
need to trap errors.

Include:	ser.a or stdlib.a

Routine:  ComSetIER
-------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AL = new IER value

Registers on return:  	None

Flags affected:       	None

Example of Usage:

			mov	al, NewIERValue
			ComSetIER

Description:

Writes the value in AL to the IER.  See ComGetIER for more details.

Include:	ser.a or stdlib.a

Routine:  ComInitIntr
---------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	None

Flags affected:       	None

Example of Usage:

			ComInitIntr

Description:

Sets up the chip to generate interrupts and programs the PC to transfer
control to the library serial interrupt service routine when an interrupt
occurs.  Note that other than interrupt initialization, this code does not
initialize the 8250 chip.

Include:	ser.a or stdlib.a

Routine:  ComDisIntr
--------------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	None

Flags affected:       	None

Example of Usage:

			ComDisIntr

Description:

This routine uninstalls the ISR and programs the chip to stop the generation
of interrupts.  You must call ComInitIntr after calling this routine to
turn the interrupt system back on.

Include:	ser.a or stdlib.a

Routine:  ComIn
---------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	None

Registers on return:  	AL=character read from buffer or port

Flags affected:       	None

Example of Usage:

			ComIn
			<Do something with AL>

Description:

ComIn is the input routine associated with interrupt I/O.  It reads the
next available character from the serial input buffer.  If no characters
are avialable in the buffer, it waits until the system receives one before
returning.

Include:	ser.a or stdlib.a

Routine:  ComOut
----------------

Author:		      	Randall Hyde

Category:             	Serial Communications

Registers on entry:   	AL=Character to output

Registers on return:  	None

Flags affected:       	None

Example of Usage:

			<Get character to write into AL>
			ComOut

Description:

ComOut is the output routine associated with interrupt I/O.  If the serial
transmitter isn't currently busy, it will immediately write the data to the
serial port.  If it is busy, it will buffer the character up.  In most cases
this routine returns quickly to its caller.  The only time this routine
will delay is if the buffer is full can you cannot add any additional
characters to it.

Include:	ser.a or stdlib.a

CHARSETS

Createsets	Creates a set on the heap
Emptyset	Cleans out set
Rangeset	Add a range of values to a set
Addstr		Add a group of characters to a set
Rmvstr		Remove a string from a set
AddChar		Add a single character to a set
Rmvchar		Remove a single character to a set
Member		Find if a character is in a set
CopySet		Makes a verbatim copy of a set to another
SetUnion	Computes the union of two sets
SetIntersect	Computes the intersection of two sets into a third
SetDifference	Removes items in second set which are in first
Nextitem        Searches the first character (item) in the set
                pointing to its mask byte
Rmvitem		Removes an item from a set



UTIL

ISize           Calculate number of spaces needed to print signed integer
USize           Calculate number of spaces needed to print unsigned integer
LSize           Calculate number of spaces needed to print signed long integer
ULSize          Calculate number of spaces needed to print unsigned long integer
IsAlNum		Checks to see if AL is in the range of A-Z, a-z, 0-9
IsXDigit	Checks to see if AL is in the range of A-F, a-f, 0-9
IsDigit		Checks to see if AL is in the range of 0-9
IsAlpha		Checks to see if AL is in the range of A-Z, a-z
IsLower		Checks to see if AL is in the range of a-z
IsUpper		Checks to see if AL is in the range of A-Z



STRINGS

The following string routines take as many as four different forms: strxxx,
strxxxl, strxxxm, and strxxxlm.  These routines differ in how they store
the destination string into memory and where they obtain their source strings.

Routines of the form strxxx generally expect a single source string address
in ES:DI or a source and destination string in ES:DI & DX:SI.  If these
routines produce a string, they generally store the result into the buffer
pointed at by ES:DI upon entry.  They return with ES:DI pointing at the
first character of the destination string.

Routines of the form strxxxl have a "literal source string".  A literal
source string follows the call to the routine in the code stream.  E.g.,

			strcatl
			db	"Add this string to ES:DI",0

Routines of the form strxxxm automatically allocate storage for a source
string on the heap and return a pointer to this string in ES:DI.

Routines of the form strxxxlm have a literal source string in the code
stream and allocate storage for the destination string on the heap.



Strcpy		Copies string. 
Strcpyl		Copies string literal
StrDup		Copies string to newly allocated memory
StrDupl		Copies string to newly allocated memory from literal
Strlen		Calculate length of string
Strcat		Concatenate two strings
Strcatm		Concatenate two strings, allocating enough memory for the final
		resulting string on the heap
Strcatl		Concatenate string from literal
Strcatml	Concatenate string from literal to allocated memory
Strchr		Searches for first occurence of a character in a string
Strstr		Searches for the position of a substring within another string
Strcmp		Compares one string to another
Strcmpl		Compares one string to literal string
Stricmp		Compares one string to another disregarding case
Strupr		Converts a string to uppercase
Struprm		Copies string to heap, then converts to upper and returns address
Strlwr		Convert string to lower case
Strlwrm		Copies string to heap, converts, then returns new string
Strset		Overwrites data on input string with character in AL
Strsetm		Allocates new strings, then overwrites with character in AL
Strspan		Compares strings, returning 1st position not equal
Strspanl	Compares strings, returning 1st position not equal, literal
Strcspan	Compares strings, returning 1st position that _IS_ equal
Strcplanl	Compares strings, returning 1st position that _IS_ equal,literal
StrIns		Inserts one string into another
StrInsl		Inserts one string into another, literal
StrInsm		Inserts one string into another after allocating memory
StrInsml	Inserts one string into another after allocating memory, literal
StrDel		Deletes characters from a string
StrDelm		Deletes characters from a copy of a string
StrTrim		Removes trailing spaces from a string
StrTrimm	Removes trailing spaces from a copy of a string
StrBlkDel	Removes leading spaces from a string
StrBlkDelm	Removes leading spaces from a copy of a string
Strrev		Reverses the characters in a string. ie: "BLAH" -> "HALB"
Strrevm		Reverses the characters in a copy of a string
StrBDel		Removes leading spaces from a string
StrBDelm	Removes leading spaces from a copy of a string
ToHex		Converts a stream of binary vaues into Intel Hex format


STDOUT

Putc		Print a character out to stdout
PutCR		Print a CR/LF to stdout
PucStdOut	Print a character to stdout
PutcBIOS	Use BIOS to print a character to the _SCREEN_
GetOuAdrs	Get the address of the current output routine
SetOutAdrs	Redirects calls to output routine to user defined
PushOutAdrs	Pushes current output address to internal stack
PopOutAdrs	Pops output address from internal stack and sets
Puts		Print a string to stdout
Puth		Print a value out in hex format
Putw		Print a value out in word hex format
Puti		Print a value out in signed integer format
Putu		Print a value out in unsigned integer format
Putl		Print a value out in signed long integer format
Putul		Print a value out in unsigned long integer format
PutISize	Print a value out in signed integer format using minimum spaces
PutUSize	Print a value out in unsigned integer format using minimum spaces
PutLSize	Print a value out in signed long format using minimum spaces
PutULSize	Print a value out in unsigned long fomat using minimum spaces
Print		Print out a literal string
Printf		Print out a literal string using C library type formatters
Printff		Print out a literal string using C library type formatters. Also
		supports printout out floating point values


STDIN

Getc		Gets a character from STDIN
GetcStdIn	Gets a character from STDIN
GetcBIOS	Gets a character using BIOS. Redirection is not allowed
SetInAdrs	Sets the address to the routine which you want to use for input
GetInAdrs	Gets the address which is being used to take input
PushInAdrs	Pushes the address of the input routine to an internal stack
PopInAdrs	Pop the address of the input routine from an internal stack
Gets		Get a string from STDIN
Getsm		Get a string in STDIN and stuff into newly alloacted buffer
Scanf		Gets string from STDIN using C library type formatters


SERIAL PORT STUFF

ComBaud		Inits the seral port to a user defined speed
ComStop		Inits number of stop bits to use in transmission
ComSize		Inits number of data bits to use in transmission
ComParity	Inits the serial port as to whether or not to use parity checking
ComRead		Reads character from serial port
ComWrite	Transmits character to serial port
ComTstIn	Checks to see if character is availble in buffer. Does not read.
ComTstOut	Checks if character can be transmitted
ComGetLSR	Reads current status of Line Status Register
ComGetMSR	Reads current status of Modem Status Regster
ComGetMCR	Reads current status of Modem Control Register
ComGetLCR	Reads current status of Line Control Regiter
ComGetIIR	Reads current status of Interrupt Identification Register
ComGetIER	Reads current status of Interupt Enable Register
ComSetMCR	Writes value to Modem Control Register
ComSetLCR	Writes value to Line Control Register
ComSetIER	Writes value to Interrupt Enable Register
ComInitIntr	Sets up interrupts and progams to control serial chip
ComDisIntr	Untinstalls all programs installed with ComInitIntr
ComIn		Reads chracter from serial port. Will wait if none available.
ComOut		Writes character to serial port, waiting if port is busy.


PROCESS

Prcsinit	Starts the process manager
Prcsquit	Shutsdown the process manager
Fork		Spawns a new process
Die		Kills the current process
Kill		Lets one process terminate another
Yield		Forces context switch, surrendering rest of current time slice
CoInit		Inits the CoRoutine package
CoCall		Switches context between two coroutines
CoCalll		Switches context between two coroutines, passing info another way
WaitSemaph	Protects critical regions in memory
RlsSemaPh	Releases a semaphore that the current process has aquired


PATTERN


Spancset	Match any number of characters belonging to a character set
Brkcset		Match any number of characters which are *not* in a character set
MatchStr	Matches a specified string
MatchToStr	Match characters in string until specified substring
MatchChar	Matches a single character
MatchChars	Matches zero or more occurrences of the same character
MatchToChar	Matches characters up to and including specified character
MatchToPat	Matches all characters up to specified characters
Anycset		Matches single character from a character set
NotAnycset	Match single character which is not in character set
EOS		Matches end of string
ARB		Matches arbitary number of characters
ARBNUM		Matches arbitary number of strings
Skip		Matches "n" arbitary characters. 
POS		Matches at position "n" in the string
RPOS		Matches at position "n" from the end of the string
GOTOpos		Moves to position in string
RGOTOpos	Moves to position "n" from end of string


MISC

Random		Generate a random number
Randomize	Reseed random number generator based on time of day
cpuid		Identifies CPU
Argc		Return number of command line parameters
Argv		Returns address to string of command line parameter specified
GetEnv		Returns address of environment table information
DOS		Invokes DOS INT 21h interrupt
ExitPgm		Exits current program and returns to DOS



MEMORY

MemInit		Initializes memory manager. Must be called first.
MemInit2	Initializes another part of memory manager
Malloc		Dynamically allocate memory
Realloc		Resize a block of memory already allocated with Malloc
Free		Deallocate a chunk of memory allocated with Malloc
DupPtr		Replicate a pointer to a chunk of memory so free won't deallocate
		it until all the pointers are taken care of
IsInHeap	Tells you if ES:DI points somewhere in the heap
IsPtr		Tells you if ES:DI points to a properlly allocated chunk of heap
BlockSize	Returns size of block currently pointed to in the heap
MemAvail	Returns size of largest free block on the heap
MemFree		Returns size of total bytes free on the heap



LIST

CreateList	Allocates storage for a list variable on the head
AppendLast	Add a node to the list
Remove1st	Removes the first item from a list
Peek1st		Looks at the first item from a list
Insert1st	Inserts a node at the first node from a list
RemoveLast	Removes the last node from a list
PeekLast	Looks at the last item from a list
InsertCur	Inserts a node into the list
InsertmCur	Builds a node on the heap, then inserts that into the list
AppendCur	Inserts a node into the list after the current node pointed to
AppendmCur	Builds node on heap, then inserts that after current node
RemoveCur	Removes current node from the list
Peek		Looks at current node on the list
SetCur		Sets the specified node as the current node
Insert		Inserts a new node before a specified node in the list
Append		Inserts a new node after a specified node in the list
Remove		Removes the specified node from the list




FLOATING POINT (FP)

lsfpa		Load single percision float value into internal accumulator
ssfpa		Store single percision float value from accumulator to memory
ldfpa		Load double percision float value into internal accumulator
sdfpa		Store double percision float value from accumulator to memory
lefpa		Load extended percision float value into internal accumulator
lefpal		lefpa with a literal value after it in the code
sefpa		Store extended percision float value from accumulator to memory
lsfpo		lsfpa a value, then convert to extended percision
ldfpo		ldfpa a value, then convert to extended percision
lefpo		lefpa a value, then convert to extended percision
lefpol		lefpo a value, with the value being literal in the code
itof		Convert a 16bit signed integer to float
utof		Convert a 16bit unsigned integer to float
ultof		Convert a 32bit unsigned integer to float
ltof		Convert a 32bit signed integer to float
ftoi		Convert float number to signed 16bit integer format
ftou		Convert float number to unsigned 16bit integer format
ftol		Convert float number to signed 32bit integer format
ftoul		Convert float number to unsigned 32bit integer format
fpadd		Add float accumulator to float operand
fpsub		Subtract float operand from the float accumulator
fpsmp		Compare float accumulator to operand and set flags accordingly
fpmul		Multiply float operand to float accumulator
fpdiv		Divides float accumulator by operand
ftoa		Converts float number into string, preserving DI
ftoa2		Converts float number into string, not preserving DI
ftoam		Converts float to string, allocating enough space for string
etoa		Convert float to string using scientific notation
etoa2		Works like etoa, except not preserving DI
etoam		Works like etoa, this time allocing space on the heap for string
atof		Converts string into float



DATE TIME

Date		Converts DOS system date into string ( mm/dd/yy )
Date2		Converts DOS system date into string, not preserving DI
Datem		Converts DOS system date into string allocated from heap
xDate		Converts current DOS system date into string
xDate2		Converts current DOS system date into string, killing DI
xDatem		Converts current DOS system date to string with memory from heap
lDate		Converts DOS date into string ( mmm, dd, yyyy )
lDate2		Converts DOS date into string killing DI
lDatem		Converts DOS date into string, memory allocated from heap
xlDate		Converts current DOS date into string
xlDate2		Converts current DOS date into string killing DI
xlDatem		Converts current DOS date into string allocated from heap
atod		Converts string (mm/dd/yy or mm-dd-yy) into DOS date
atod2		Converts string into DOS date, killing DI
atot		Converts string (hh:mm:ss or hh:mm:ss.xxx) into DOS time
atot2		Converts string into DOS time killing DI
time		Converts DOS time to string
time2		Converts DOS time to string, killing DI
timem		Converts DOS time to string, allocated from heap
xtime		Converts current DOS time to string
xtime2		Converts current DOS time to string, killing DI
xtimem		Converts current DOS time to string, allocated from heap


CONVERSION

atol		Converts string of numbers to signed 32bit integer
atoul		Converts string of numbers to unsigned 32bit integer
atou		Converts string of numbers to unsigned 16bit integer
atoh		Converts string of hex numbers to unsigned 16bit integer
atoh2		Converts string of hex numbers to unsigned 16bit int killing DI
atolh		Converts string of hex numbers to unsigned 32bit int
atolh2		Converts string of hex numbers to unsigned 32bit int killing DI
atoi		Converts string of numbers to signed 16bit integer
itoa		Converts signed integer to string
itoam		Converts signed integer to string, allocting space from heap
itoa2		Converts signed integer to string, killing DI
utoa		Converts unsigned integer to string
utoam		Converts unsigned integer to string, allocating space from heap
utoa2		Converts unsigned integer to string, killing DI
htoa		Converts 8bit hex value to string
htoa2		Converts 8bit hex value to string, killing DI
htoam		Converts 8bit hex value to string, allocating space from heap
wtoa		Converts 16bit hex value to string
wtoa2		Converts 16bit hex value to string, killing DI
wtoam		Converts 16bit hex value to string, allocating space from heap
ltoa		Converts 32bit signed integer to string
ltoa2		Converts 32bit signed integer to string, killing DI
ltoam		Converts 32bit signed integer to string, getting space from heap
ultoa		Converts 32bit unsigned int to string
ultoa2		Converts 32bit unsigned int to string, killing DI
ultoam		Converts 32bit unsigned int to string, getting space from heap
sprintf		In memory print formatting
sprintf2	In memory print formatting, killing DI
sprintfm	In memory print formatting, getting space from heap
sscanf		In memory input formatting
sscanf2		In memory input formatting, killing DI
sscanfm		In memory input formatting, getting space from heap
tolower		Converts character to lowercase
toupper		Converts character to uppercase
 		

	
By: Steve Shah
	sshah@ucrengr.ucr.edu
	sshah@watserv.ucr.edu
	sshah@mozart.ucr.edu
	
		Pick one -- any one.......
Current version:
	UCRASM 31
	Compiled 1.0 -- June 7, 1993  10:40a



Standard Input Routines:
Character Input Routines
------------------------


	The character input routines take input from either a standard
device (keyboard, etc.) or a standard library.  After the character input
routines receive the characters they either place the characters on the stack
and/or return.  The character input routines work similar to the "C" character
input routines.



Routine:  Getc
--------------


Category:             Character Input Routine


Registers on Entry:   None


Registers on Return:  AL- Character from input device.
                      AH- 0 if eof, 1 if not eof.


Flags Affected:       Carry- 0 if no error, 1 if error.  If error occurs, AX
                             contains DOS error code.


Example of Usage:  
                      getc
                      mov     KbdChar, al
                      putc


Description:  This routine reads a character from the standard input device.
              This call is synchronous, that is, it does not return until a
              character is available.  The Default input device is DOS
              standard input.

              Getc returns two types of values: extended ASCII (codes 1-255)
              and IBM keyboard scan codes.  If Getc returns a non-zero value,
              you may interpret this value as an ASCII character.  If Getc
              returns zero, you must call Getc again to get the actual
              keypress.

              The second call returns an IBM PC keyboard scan code.

              Since the user may redirect input from the DOS command line,
              there is the possibility of encountering end-of-file (eof)
              when calling getc.  Getc uses the AH register to return eof
              status.  AH contains the number of characters actually read
              from the standard input device.  If it returns one, then
              you've got a valid character.  If it returns zero, you've
              reached end of file.  Note that pressing control-z forces an
              end of file condition when reading data from the keyboard.

              This routine returns the carry flag clear if the operation
              was successful.  It returns the carry flag set if some sort
	      of error occurred while reading the character.  Note that eof
              is not an error condition.  Upon reaching the end of file,
              Getc returns with the carry flag clear.  If getc is seen from
              a file the control-z is not seen as an end-of-file marker,
              but just read in as a character of the file.

              Control-c if read from a keyboard device aborts the program.
              However if when reading something other than a keyboard
	      (files, serial ports), control-c from the input source
              returns control-c.  However when pressing control-break
              the program will abort regardless of the input source.

              Regarding CR/LF, if the input is from a device, (eg. keyboard
              serial port) getc returns whatever that device driver returns,
              (generally CR without a LF).  However if the input is from
              a file, getc stripes a single LF if it immediately follows
              the CR.

              When using getc files operate in "cooked" mode.  While
              devices operate in "pseudo-cooked" mode, which means no
	      buffering, no CR -> CR/LF, but it handles control-c, and
              control-z.

	      See the sources for more information about GETC's internal
	      operation.

Include:	stdlib.a or stdin.a




Routine:   GetcStdIn
--------------------


Category:             Character Input Routine

Register on entry:    None.

Register on return:   AL- Character from input device.

Flags affected:       AH- 0 if eof, 1 if not eof.
                      Carry- 0 if no error, 1 if error
                      (AX contains DOS error code if error occurs).


Example of Usage:
                      GetcStdIn
                      mov     InputChr, al
                      putc


Description:    This routine reads a character from the DOS standard input
                device.  This call is synchronous, that is, it does not return
                until a character is available.  See the description of Getc
                above for more details.

		The difference between Getc and GetcStdIn is that your
		program can redirect Getc using other calls in this library.
		GetcStdIn calls DOS directly without going through this
		redirection mechanism.


Include:		stdlib.a or stdin.a






Routine:   GetcBIOS
-------------------


Category:             Character Input Routine

Register on entry:    None

Register on return:   AL- Character from the keyboard.

Flags affected:       AH- 1 (always).  Carry- 0 (always).

Example of Usage:   
		      GetcBIOS
                      mov     CharRead, al
                      putc


Description:   This routine reads a character from the keyboard.  This call is
               synchronous, that is it does not return until a character is
               available.

		Note that there is no special character processing.  This
		code does *not* check for EOF, control-C, or anything
		else like that.



Include:		stdlib.a or stdin.a



Routine:  SetInAdrs
-------------------

Category:               Character Input Routine

Registers on Entry:     ES:DI - address of new input routine

Registers on return:    None 

Flags affected:

Examples of Usage:

                        mov     es, seg NewInputRoutine
                        mov     di, offset NewInputRoutine
                        SetInAdrs



                        les     di, RoutinePtr
                        SetInAdrs


Description:    This routine redirects the stdlib standard input so that it 
                calls the routine who's address you pass in es:di.  The
		routine (whose address appears in es:di) should be a "getc"
		routine which reads a character from somewhere and returns
		that character in AL.  It should also return EOF status in
		the AH register and error status in the carry flag (see
		the description of GETC for more details).


Include:                stdlib.a or stdin.a





Routine:   GetInAdrs
--------------------

Category:             Character Input Routine

Register on entry:    None

Register on return:   ES:DI - address of current input routine (called by Getc).

Flags affected:       None


Example of Usage:   
                      GetInAdrs
		      mov     word ptr SaveInAdrs, di
		      mov     word ptr SaveInAdrs+2, es


Description:   You can use this function to get the address of the current
	       input routine, perhaps so you can save it or see if it is
	       currently pointing at some particular piece of code.
	       If you want to temporarily redirect the input and then restore
	       the original input or outline, consider using
	       PushInAdrs/PopInAdrs described later.


Include:		stdlib.a or stdin.a



Routine:   PushInAdrs
---------------------

Category:             Character Input Routine

Register on entry:    ES:DI - Address of new input routine.

Register on return:   Carry=0 if operation successful.
                      Carry=1 if there were already 16 items on the stack.
                         
Example of Usage:
                      mov     es, seg NewInputRoutine
                      mov     di, offset NewInputRoutine
                      PushInAdrs
                        .
                        .
                        .
                      les     di, RoutinePtr
                      PushInAdrs


Description:   This routine "pushes" the current input address onto an
               internal stack and then copies the value in es:di into the
               current input routine pointer.  The PushInAdrs and PopInAdrs
               routines let you easily save and redirect the standard output
               and then restore the original output routine address later on.
               If you attempt to push more than 16 items on the stack,
               PushInAdrs will ignore your request and return with the
               carry flag set.  If PushInAdrs is successful, it will
               return with the carry flag clear.


Include:	stdlib.a or stdin.a





Routine:   PopInAdrs
--------------------

Category:             Character Input Routine

Register on entry:    None

Register on return:   ES:DI - Points at the previous stdout routine before
                      the pop.

Example of Usage:   
                      mov     es, seg NewInRoutine
		      mov     di, offset NewInputRoutine
                      PushInAdrs
                        .
                        .
                        .
                      PopInAdrs


Description:   PopInAdrs undoes the effects of PushInAdrs.  It pops an item
               off the internal stack and stores it into the input routine
               pointer.  The previous value in the output pointer is returned
               in es:di.

Include:	stdlib.a or stdin.a





Routine:  Gets, Getsm
---------------------

Category:             Character Input Routine

Register on entry:    ES:DI- Pointer to input buffer (gets only).

Register on return:   ES:DI - address of input of text.
                      carry-  0 if no error, 1 if error.
                      If error, AX contains: 0- End of
                      file encountered in middle of
                      string.  1- Memory allocation error (getsm only).
                      Other- DOS error code.


Flags affected:       None

Example of usage:    
                      getsm           ;Read a string from the
                                      ;keyboard
                      puts            ;Print it
		      putcr           ;Print a new line
                      free            ;Deallocate storage for
                                      ;string.

		      mov	di, seg buffer
		      mov	es, di
		      lea	di, buffer
		      gets
		      puts
		      putcr


Description:   	Reads a line of text from the stdlib standard input device.
		You must pass a pointer to the recipient buffer in es:di to
		the GETS routine.  GETSM automatically allocates storage for
		the string on the heap (up to 256 bytes) and returns a pointer
		to this block in es:di.

		Gets(m) returns all characters typed by the user except for the
		carriage return (ENTER) key code.  These routines return a
		zero-terminated string (with es:di pointing at the string).
		Exactly how Gets(m) treats the incoming data depends upon
		the source device, however, you can usually count on Gets(m)
		properly handling backspace (erases previous character),
		escape (erase entire line), and ENTER (accept current line).

		Other keys may affect Gets(m) as well.  For example, Gets(m),
		by default, calls Getc which, in turn, usually calls DOS'
		standard input routine.  If you type control-C or break while
		read from DOS' standard input it may abort the program.

		If an error occurs during input (e.g., EOF encountered in
		the middle of a line) Gets(m) returns the error code in
		AX.  If no error occurs, Gets(m) preserves AX.

Include:              	stdlib.a or stdin.a





Routine:  Scanf
---------------

Category:             Character Input Routine

Register on entry:    None

Register on return:   None

Flags affected:       None

Example of usage:   
                      scanf
                      db      "%i  %h  %^s",0
                      dd      i, x, sptr

Description:   * Formatted input from stdlib standard input.
               * Similar to C's scanf routine.
               * Converts ASCII to integer, unsigned, character, string, hex,
		 and long values of the above.
               Scanf provides formatted input in a fashion analogous to
               printf's output facilities.  Actually, it turns out that scanf
               is considerably less useful than printf because it doesn't
               provide reasonable error checking facilities (neither does C's
               version of this routine).  But for quick and dirty programs 
               whose input can be controlled in a rigid fashion (or if you're
               willing to live by "garbage in, garbage out")  scanf provides 
               a convenient way to get input from the user.  Like printf, the
               scanf routine expects you to follow the call with a format 
               string and then a list of (far pointer) memory addresses.  The
               items in the scanf format string take the following form: %^f,
               where f represents d, i, x, h, u, c, x, ld, li, lx, or lu.  
               Like printf, the "^" symbol tells scanf that the address
               following the format string is the address of a (far) pointer
               to the data rather than the address of the data location itself.
               By default, scanf automatically skips any leading whitespace 
               before attempting to read a numeric value.  You can instruct
               scanf to skip other characters by placing that character in the
               format string.  For example, the following call instructs scanf
	       to read three integers separated by commas (and/or whitespace):

                       	      scanf  
			      db                "%i,%i,%i",0
                              dd                 i1,i2,i3

               Whenever scanf encounters a non-blank character in the format
               string, it will skip that character (including multiple
               occurrences of that character) if it appears next in the input
               stream.  Scanf always calls gets to read a new line of text 
               from stdlib's standard input.  If scanf exhausts the format 
               list, it ignores any remaining characters on the line.  If
               scanf exhausts the input line before processing all of the
               format items, it leaves the remaining variables unchanged.
               Scanf always deallocates the storage allocated by gets.


Include:                stdlib.a or stdin.a


Character Output Routines
-------------------------


The stdlib character output routines allow you to print to the 
standard output device.  Although the processing of non-ASCII 
characters is undefined, most output devices handle these characters
properly.  In particular, they can handle return, line feed, back space, 
and tab.  

Most of the output routines in the standard library output data 
through the Putc routine.  They generally use the AX register upon 
entry and print the character(s) to the standard output device by
calling DOS by default. The  output is redirectable to the 
user-written routine.  However, the PutcBIOS routine prints doesn't 
use DOS.  Instead it uses BIOS routines to print the character in AL 
using the INT command for teletype-like output. 

The print routines are similar to those in C, however, they differ
in their implementation. The print routine returns to the address
immediately following the terminating byte, therefore, it is important
to remember to terminate your string with zero or you will print an
unexpected sequence of characters.



Routine:  Putc
--------------

Category:             Character Output Routine

Registers on Entry:   AL- character to output

Registers on Return:  None

Flags affected:       None

Example of Usage:
                       mov     al, 'C'
                       putc                    ;Prints "C" to std output.


Description:  Putc is the primitive character output routine.  Most other
              output routines in the standard library output data through
              this procedure.  It prints the ASCII character in AL register.  
              The processing of control codes is undefined although most output
              routines this routine links to should be able to handle return, 
              line feed, back space, and tab.  By default, this routine calls
              DOS to print the character to the standard output device.  The
              output is redirectable to to user-written routine.


Include:                stdlib.a or stdout.a



Routine:  PutCR
---------------

Category:             Character Output Routine 

Register on entry:    None

Register on return:   None

Flags affected:       None

Example of Usage:     PutCR


Description:  Using PutCR is an easy way of printing a newline to the stdlib 
              standard output. It prints a newline (carriage return/line feed) 
              to the current standard output device.


Include:                stdlib.a or stdout.a


Routine: PutcStdOut
-------------------

Category:              Character Output Routine

Registers on Entry:    AL- character to output

Registers on Return:   None

Flags Affected:        None

Example of Usage:
                       mov AL, 'C'
                       PutcStdOut        ; Writes "C" to standard output


Description:  PutcStdOut calls DOS to print the character in AL to the standard
              output device.  Although processing of non-ASCII characters and
              control characters is undefined, most output devices handle these
              characters properly.  In particular, most output devices properly
              handle return, line feed, back space, and tab.  The output is
              redirectable via DOS I/O redirection.


Include:                stdlib.a or stdout.a



Routine: PutcBIOS
-----------------

Category:              Character Output Routine

Registers on Entry:    AL- character to print

Registers on Return:   None

Flags Affected:        None

Example of Usage:
                       mov AL, "C"
                       PutcBIOS


Description:  PutcBIOS prints the character in AL using the BIOS routines,
              using INT 10H/AH=14 for teletype-like output.  Output through
              this routine cannot be redirected; such output is always sent
              to the video display on the PC (unless, of course, someone has
              patched INT 10h).  Handles return, line feed, back space, and
              tab.  Prints other control characters using the IBM Character
	      set.


Include:     	stdlib.a or stdout.a



Routine: GetOutAdrs
-------------------

Category:             Character Output Routine

Registers on Entry:   None

Registers on Return:  ES:DI- address of current output routine (called by Putc)

Flags Affected:       None

Example of Usage: 
                      GetOutAdrs
                      mov word ptr SaveOutAdrs, DI
                      mov word ptr SaveOutAdrs+2, ES

Description:  GetOutAdrs gets the address of the current output routine, perhaps
              so you can save it or see if it is currently pointing at some
              particular piece of code.  If you want to temporarily redirect
              the output and then restore the original output routine, consider
              using PushOutAdrs/PopOutAdrs described later.

Include:	stdlib.a or stdout.a




Routine:  SetOutAdrs
--------------------

Category:               Character Output Routine

Registers on Entry:     ES:DI - address of new output routine

Registers on return:    None 

Flags affected:         None

Example of Usage:

                        mov     es, seg NewOutputRoutine
                        mov     di, offset NewOutputRoutine
                        SetOutAdrs
                        les     di, RoutinePtr
                        SetOutAdrs

Description:  This routine redirects the stdlib standard output so that it
              calls the routine who's address you pass in es:di.  This routine
              expects the character to be in AL and must preserve all registers.
              It handles the printable ASCII characters and the four control
              characters return, line feed, back space, and tab.  (The routine
              may be modified in the case that you wish to handle these codes
              in a different fashion.)


Include:        stdlib.a or stdout.a


Routine:  PushOutAdrs
---------------------

Category:              Character Output Routine 

Registers on Entry:    ES:DI- Address of new output routine

Registers on Return:   None

Flags Affected:        Carry = 0 if operation is successful
                       Carry = 1 if there were already 16 items on the stack

Example of Usage:  
                       mov  ES, seg NewOutputRoutine
                       mov  DI, offset NewOutputRoutine
                       PushOutAdrs
                          .
                          .
                          . 
                       les  DI, RoutinePtr
                       PushOutAdrs


Description:  This routine "pushes" the current output address onto an internal
              stack and then uses the value in es:di as the current output
              routine address.  The PushOutAdrs and PopOutAdrs routines let you
              easily save and redirect the standard output and then restore the
              original output routine address later on.  If you attempt to push
	      more than 16 items on the stack, PushOutAdrs will ignore your
              request and return with the carry flag set.  If PushOutAdrs is
              successful, it will return with the carry flag clear.


Include:      	stdlib.a or stdout.a



Routine:  PopOutAdrs
--------------------

Category:             Character Output Routine 

Registers on Entry:   None

Registers on Return:  ES:DI- Points at the previous stdout routine before
                      the pop

Flags Affected:       None

Example of Usage:  
                      mov ES, seg NewOutputRoutine
                      mov DI, offset NewOutputRoutine
                      PushOutAdrs
                         .
                         .
                         .
                      PopOutAdrs


Description:  PopOutAdrs undoes the effects of PushOutAdrs.  It pops an item off
              the internal stack and stores it into the output routine pointer.
              The previous value in the output pointer is returned in es:di.
              Defaults to PutcStdOut if you attempt to pop too many items off
              the stack.

Include:	stdlib.a or stdout.a




Routine:  Puts
--------------

Category:            Character Output Routine 

Register on entry:   ES:DI register - contains the address of the string

Register on return:  None

Flags affected:      None

Example of Usage:
                     les     di, StrToPrt
                     puts
                     putcr


Description:   Puts prints a zero-terminated string whose address appears
	       in es:di.  Each character appearing in the string is printed
               verbatim.  There are no special escape characters.  Unlike
               the "C" routine by the same name, puts does not print a
               newline after printing the string.  Use putcr if you want
               to print the newline after printing a string with puts.


Include:        stdlib.a or stdout.a



Routine:  Puth
--------------

Category:             Character Output Routine

Register on entry:    AL 

Register on return:   AL

Flags affected:       None

Example of Usage:
                      mov     al, 1fh
                      puth


Description:    The Puth routine Prints the value in the AL register as two
                hexadecimal digits.  If the value in AL is between 0 and 0Fh, 
                puth will print a leading zero.  This routine calls the stdlib
                standard output routine (putc) to print all characters.


Include:        stdlib.a or stdout.a



Routine:  Putw
--------------

Category:             Character Output Routine

Registers on Entry:   AX- Value to print

Registers on Return:  None

Flags Affected:       None

Example of Usage: 
                      mov AX, 0f1fh
                      putw


Description:  The Putw routine prints the value in the AX register as four
              hexadecimal digits (including leading zeros if necessary).  
              This routine calls the stdlib standard output routine (putc) 
              to print all characters.

Include:        stdlib.a or stdout.a



Routine:  Puti
--------------

Category:             Character Output Routine

Registers on Entry:   AX- Value to print

Registers on Return:  None

Flags Affected:       None

Example of Usage: 
                      mov AX, -1234
                      puti


Description:  Puti prints the value in the AX register as a decimal integer.
              This routine uses the exact number of screen positions required
	      to print the number (including a position for the minus sign, if
              the number is negative).  This routine calls the stdlib standard
              output routine (putc) to print all characters.


Include:        stdlib.a or stdout.a



Routine:  Putu
--------------

Category:             Character Output Routine

Register on entry:    AX- Unsigned value to print.

Register on return:   None

Flags affected:       None

Example of Usage:
                      mov     ax, 1234
                      putu


Description:  Putu prints the value in the AX register as an unsigned integer.
              This routine uses the exact number of screen positions required
              to print the number.  This routine calls the stdlib standard
              output routine (putc) to print all characters.


Include:        stdlib.a or stdout.a




Routine:  Putl
--------------

Category:            Character Output Routine

Register on entry:   DX:AX- Value to print

Register on return:  None

Flags affected:      None

Example of Usage:
                     mov     dx, 0ffffh
                     mov     ax, -1234
                     putl


Description:   Putl prints the value in the DX:AX registers as an integer.
               This routine uses the exact number of screen positions
               required to print the number (including a position for the
               minus sign, if the number is negative).  This routine calls
               the stdlib standard output routine (putc) to print all
               characters.


Include:        stdlib.a or stdout.a



Routine:  Putul
---------------

Category:             Character Output Routine

Register on entry:    DX:AX register

Register on return:   None

Flags affected:       None

Example of Usage:
                      mov     dx, 12h
                      mov     ax, 1234
                      putul


Description:    Putul prints the value in the DX:AX registers as an unsigned
                integer.  This routine uses the exact number of screen
                positions required to print the number.  This routine calls 
		the stdlib standard output routine (putc) to print all
		characters.


Include:        stdlib.a or stdout.a


Routine:  PutISize
------------------

Category:              Character Output Routine

Registers on Entry:    AX - Integer value to print
                       CX - Minimum number of print positions to use

Registers on return:   None

Flags affected:

Example of Usage:
                       mov     cx, 5
                       mov     ax, I
                       PutISize
                           .
                           . 
                           .
                       mov     cx, 12
                       mov     ax, J
                       PutISize


Description:    PutISize prints the signed integer value in AX to the
                stdlib standard output device using a minimum of n print
                positions.  CX contains n, the minimum field width for the
                output value.  The number (including any necessary minus sign)
		is printed right justified in the output field.
                If the number in AX requires more print positions than
                specified by CX, PutISize uses however many print positions
                are necessary to actually print the number.  If you specify
                zero in CX, PutISize uses the minimum number of print positions
                required.  Of course, PutI will also use the minimum number
                of print positions without disturbing the value in the CX
                register.

                Note that, under no circumstances, will the number in AX
                ever require more than 6 print positions (-32,767 requires
                the most print positions).


Include:        stdlib.a or stdout.a



Routine:  PutUSize
------------------

Category:              Character Output Routine

Registers on entry:    AX- Value to print
		       CX- Minimum field width

Registers on return:   None

Flags affected:        None

Example of usage: 
                       mov     cx, 8
                       mov     ax, U
                       PutUSize


Description:  PutUSize prints the value in AX as an unsigned decimal integer.
              The minimum field width specified by the value in CX.
              Like PutISize above except this one prints unsigned values.  
              Note that the maximum number of print positions required by any 
	      number (e.g., 65,535) is five.


Include:        stdlib.a or stdout.a



Routine:  PutLSize
------------------

Category:            Character Output Routine

Register on entry:   DX:AX-32 bit value to print
		     CX- Minimum field width

Register on return:  None

Flags affected:      None

Example of Usage:
		     mov     cx, 16
                     mov     dx, word ptr L+2
                     mov     ax, word ptr L
                     PutLSize


Description:   PutLSize is similar to PutISize, except this prints the long 
               integer value in DX:AX.  Note that there may be as many as 
               11 print positions (e.g., -1,000,000,000).

Include:        stdlib.a or stdout.a




Routine:  PutULSize
-------------------


Category:            Character Output Routine


Register on entry:   AX : DX and CX


Register on return:  None


Flags affected:      None


Example of usage:    mov     cx, 8
                     mov     dx, word ptr UL+2
                     mov     ax, word ptr UL
                     PutULSize


Description:  Prints the value in DX:AX as a long unsigned decimal integer.
              Prints the number in a minimum field width specified by the
              value in CX.  Just like PutLSize above except this one prints
	      unsigned numbers rather than signed long integers.  The largest
              field width for such a value is 10 print positions.


Include:        stdlib.a or stdout.a


Routine:   Print
----------------

Category:             Character Output Routine

Register on entry:    CS:RET - Return address points at the string to print.

Register on return:   None

Flags affected:       None

Examples of Usage:    print
                      db      "Print this string to the display device"
		      db      13,10
                      db      "This appears on a new line"
                      db      13,10
                      db      0


Description:   Print lets you print string literals in a convenient
               fashion.  The string to print immediately follows the call
               to the print routine.  The string must contain a
               zero terminating byte and may not contain any intervening
               zero bytes.  Since the print routine returns to the address
               immediately following the zero terminating byte, forgetting
               this byte or attempting to print a zero byte in the middle
               of a literal string will cause print to return to an
               unexpected instruction.  This usually hangs up the machine.
               Be very careful when using this routine!


Include:        stdlib.a or stdout.a


Routine:        Printf
----------------------

Category:             Character Output Routine

Register on entry:    CS:RET - Return address points at the format string

Register on return:   None

Flags affected:       None

Example of Usage:
                      printf
                      db      "Indirect access to i: %^d",13,10,0
                      dd      IPtr;
                      printf
                      db      "A string allocated on the heap: %-\.32^s"
                      db      13,10,0
                      dd      SPtr



Descriptions:   Printf, like its "C" namesake, provides formatted output
                capabilities for the stdlib package.  A typical call to printf
                always takes the following form:

                        printf
                        db              "format string",0
                        dd              operand1, operand2, ..., operandn

                The format string is comparable to the one provided in the
                "C" programming language.  For most characters, printf simply
                prints the characters in the format string up to the
                terminating zero byte.  The two exceptions are characters
                prefixed by a backslash ("\") and characters prefixed by a
                percent sign ("%").  Like C's printf, stdlib's printf uses
                the backslash as an escape character and the percent sign as
                a lead-in to a format string.

		Printf uses the escape character ("\") to print special
                characters in a fashion similar to, but not identical to C's
                printf.  Stdlib's printf routine supports the following
                special characters:

                *  r     Print a carriage return (but no line feed)
                *  n     Print a new line character (carriage return/line feed).
                *  b     Print a backspace character.
                *  t     Print a tab character.
                *  l     Print a line feed character (but no carriage return).
                *  f     Print a form feed character.
                *  \     Print the backslash character.
                *  %     Print the percent sign character.
                *  0xhh  Print ASCII code hh, represented by two hex digits.

                C users should note a couple of differences between stdlib's
                escape sequences and C's.  First, use "\%" to print a percent
                sign within a format string, not "%%".  C doesn't allow the
                use of "\%" because the C compiler processes "\%" at compile
                time (leaving a single "%" in the object code) whereas printf
		processes the format string at run-time.  It would see a single
                "%" and treat it as a format lead-in character.  Stdlib's
                printf, on the other hand, processes both the "\" and "%" and
                run-time, therefore it can distinguish "\%".

                Strings of the form "\0xhh" must contain exactly two hex
                digits.  The current printf routine isn't robust enough to
                handle sequences of the form "\0xh" which contain only a
                single hex digit.  Keep this in mind if you find printf
                chopping off characters after you print a value.

                There is absolutely no reason to use any escape character
                sequences except "\0x00".  Printf grabs all characters
                following the call to printf up to the terminating zero byte
                (which is why you'd need to use "\0x00" if you want to print
                the null character, printf will not print such values).
                Stdlib's printf routine doesn't care how those characters got
                there.  In particular, you are not limited to using a single
                string after the printf call.  The following is perfectly
		legal:


                printf
                db      "This is a string",13,10
                db      "This is on a new line",13,10
                db      "Print a backspace at the end of this line:"
                db      8,13,10,0


                Your code will run a tiny amount faster if you avoid the use
                of the escape character sequences.  More importantly, the
                escape character sequences take at least two bytes.  You can
                encode most of them as a single byte by simply embedding the
                ASCII code for that byte directly into the code stream.
                Don't forget, you cannot embed a zero byte into the code
                stream.  A zero byte terminates the format string.  Instead,
                use the "\0x00" escape sequence.

                Format sequences always between with "%".  For each format
                sequence you must provide a far pointer to the associated
		data immediately following the format string, e.g.,

	                printf
        	        db      "%i %i",0
                	dd      i,j

                Format sequences take the general form "%s\cn^f" where:

                *       "%" is always the "%" character.  Use "\%" if you
                        actually want to print a percent sign.
                *       s is either nothing or a minus sign ("-").
                *       "\c" is also optional, it may or may not appear in
                        the format item.  "c" represents any printable
                        character.
                *       "n" represents a string of 1 or more decimal digits.
                *       "^" is just the caret (up-arrow) character.
                *       "f" represents one of the format characters: i, d, x,
                        h, u, c, s, ld, li, lx, or lu.

                The "s", "\c", "n", and "^" items are optional, the "%" and
		"f" items must be present.  Furthermore, the order of these
                items in the format item is very important.  The "\c" entry,
                for example, cannot precede the "s" entry.  Likewise, the "^"
                character, if present, must follow everything except the "f"
                character(s).

                The format characters i, d, x, h, u, c, s, ld, li, lx, and
                lu control the output format for the data.  The i and d
                format characters perform identical functions, they tell
                printf to print the following value as a 16-bit signed
                decimal integer.  The x and h format characters instruct
                printf to print the specified value as a 16-bit or 8-bit
                hexadecimal value (respectively).  If you specify u, printf
                prints the value as a 16-bit unsigned decimal integer.
                Using c tells printf to print the value as a single character.
                S tells printf that you're supplying the address of a
                zero-terminated character string, printf prints that string.
                The ld, li, lx, and lu entries are long (32-bit) versions of
                d/i, x, and u.  The corresponding address points at a 32-bit
                value which printf will format and print to the standard output.
		The following example demonstrates these format items:

                printf
                db      "I= %i, U= %u, HexC= %h, HexI= %x, C= %c, "
                db      "S= %s",13,10
                db      "L= %ld",13,10,0
                dd      i,u,c,i,c,s,l

                The number of far addresses (specified by operands to the "dd"
                pseudo-opcode) must match the number of "%" format items in
                the format string.  Printf counts the number of "%" format
                items in the format string and skips over this many far
                addresses following the format string.  If the number of
                items do not match, the return address for printf will be
                incorrect and the program will probably hang or otherwise
                malfunction.  Likewise (as for the print routine), the format
                string must end with a zero byte.  The addresses of the items
                following the format string must point directly at the memory
                locations where the specified data lies.

		When used in the format above, printf always prints the
                values using the minimum number of print positions for each
                operand.  If you want to specify a minimum field width, you
                can do so using the "n" format option.  A format item of the
                format "%10d" prints a decimal integer using at least ten
                print positions.  Likewise, "%16s" prints a string using at
                least 16 print positions.  If the value to print requires
                more than the specified number of print positions, printf
                will use however many are necessary.  If the value to print
                requires fewer, printf will always print the specified number,
                padding the value with blanks.  Printf will print the value
                right justified in the print field (regardless of the data's
                type).  If you want to print the value left justified in the
                output file, use the "-" format character as a prefix to the
                field width, e.g.,

                printf
                db      "%-17s",0
                dd      string

		In this example, printf prints the string using a 17 character
                long field with the string left justified in the output field.
                By default, printf blank fills the output field if the value
                to print requires fewer print positions than specified by the
                format item.  The "\c" format item allows you to change the
                padding character.  For example, to print a value, right
                justified, using "*" as the padding character you would use
                the format item "%\*10d".  To print it left justified you
                would use the format item "%-\*10d".  Note that the "-" must
                precede the "\*".  This is a limitation of the current
                version of the software.  The operands must appear in this
                order.  Normally, the address(es) following the printf
                format string must be far pointers to the actual data to print.
                On occasion, especially when allocating storage on the heap
                (using malloc), you may not know (at assembly time) the
                address of the object you want to print.  You may have only
                a pointer to the data you want to print.  The "^" format
                option tells printf that the far pointer following the format
                string is the address of a pointer to the data rather than
                the address of the data itself.  This option lets you access
		the data indirectly.

                Note: unlike C, stdlib's printf routine does not support
                floating point output.  Putting floating point into printf
		would increase the size of this routine a tremendous amount.
		Since most people don't need the floating point output
		facilities, it doesn't appear here.  Check out PRINTFF.

Include:        stdlib.a or stdout.a



Routine:  PRINTFF
-----------------


Category:             Character Output Routine

Registers on Entry:   CS:RET- Points at format string and other parameters.

Registers on Return:  If your program prints floating point values, this
		      routine modifies the floating point accumulator and
		      floating point operand "pseudo-registers" in the
		      floating point package.

Flags Affected:       None

Examples of Usage:
			printff
			db	"I = %d, R = %7.2f  F = 12.5e  G = 9.2gf\n",0
			dd	i, r, f, g

Description:  
		This code works just like printf except it also allows the
		output of floating point values.  The output formats are 
		the following:

		Single Precision:

		 mm.nnF-	Prints a field width of mm chars with nn digits
				appearing after the decimal point.

		 nnE-		Prints a floating point value using scientific
				notation in a field width of nn chars.

		Double Precision:

		 mm.nnGF-	As above, for double precision values.
		 nnGE-		As above, for double precision values.

		Extended Precision-

		 mm.nnLF-	As above, for extended precision values.
		 nnLE-		As above, for extended precision values.


		Since PRINTFF supports everything PRINTF does, you should not
		use both routines in the same program (just use PRINTF).  The
		PRINTF & PRINTFF macros check for this and will print a warning
		message if you've included both routines.  Using both will not
		cause your program to fail, but it will make your program
		unnecessarily larger.  You should not use PRINTFF unless you
		really need to print floating point values.  When you use
		PRINTFF, it forces the linker to load in the entire floating
		point package, making your program considerably larger.

Include:              	stdlib.a or fp.a

String Handling Routines
------------------------

Manipulating text is a major part of many computer applications. Typically,
strings are inputed and interpreted. This interpretation may involve some
chores such as extracting certain part of the text, copying it, or comparing
with other strings.

The string manipulation routines in C provides various functions. Therefore,
the stdlib has some C-like string handling functions (e.g. strcpy, strcmp).
In C a string is an array of characters; similarly, the string are terminated
by a "0" as a null character. In general, the input strings of these routines
are pointed by ES:DI. In some routines, the carry flag will be set to indicate
an error.

The following string routines take as many as four different forms: strxxx,
strxxxl, strxxxm, and strxxxlm.  These routines differ in how they store
the destination string into memory and where they obtain their source strings.

Routines of the form strxxx generally expect a single source string address
in ES:DI or a source and destination string in ES:DI & DX:SI.  If these
routines produce a string, they generally store the result into the buffer
pointed at by ES:DI upon entry.  They return with ES:DI pointing at the
first character of the destination string.

Routines of the form strxxxl have a "literal source string".  A literal
source string follows the call to the routine in the code stream.  E.g.,

			strcatl
			db	"Add this string to ES:DI",0

Routines of the form strxxxm automatically allocate storage for a source
string on the heap and return a pointer to this string in ES:DI.

Routines of the form strxxxlm have a literal source string in the code
stream and allocate storage for the destination string on the heap.



Routine:  Strcpy (l)
--------------------

Category:             String Handling Routine

Registers on Entry:   ES:DI - pointer to source string (Strcpy only)
		      CS:RET - pointer to  source  string (Strcpy1 only)
		      DX:SI - pointer to destination string


Registers on return:  ES:DI - points at the destination string


Flags affected:	      None


Example of Usage:
		      mov     dx, seg Dest
		      mov     si, offset Dest
		      mov     di, seg Source
		      mov     es, di
		      mov     si, offset Source
		      Strcpy

		      mov     dx, seg Dest
		      mov     si, offset Dest
		      Strcpyl
		      db      "String to copy",0


Description:  Strcpy is used to copy a zero-terminated string from one
	      location to another.  ES:DI points at the source string,
	      DX:SI points at the destination address.  Strcpy copies all
	      bytes, up to and including the zero byte, from the source
	      address to the destination address.  The target buffer must
	      be large enough to hold the string.  Strcpy performs no error
	      checking on the size of the destination buffer.

	      Strcpyl copies the  zero-terminated string immediately following
	      the call instruction to the destination address specified by
	      DX:SI.  Again, this routine expects you to ensure that the
	      taraget buffer is large enough to hold the result.

	      Note: There are no "Strcpym" or "Strcpylm" routines.  The
	      reason is simple: "StrDup" and "StrDupl" provide these functions
	      using names which are familiar to MSC and Borland C users.

Include:              stdlib.a or strings.a


Routine:  StrDup (l)
--------------------

Category:            String Handling Routine

Register on entry:   ES:dI - pointer to source string (StrDup
		     only).  CS:RET - Pointer to source string
		     (StrDupl only).

Register on return:  ES:DI - Points at the destination string
		     allocated on heap.  Carry=0 if operation
		     successful.  Carry=0 if insufficient
		     memory for new string.

Flags affected:      Carry flag

Example of usage:
		     StrDupl
		     db "String for StrDupl",0
		     jc  MallocError
		     mov word ptr Dest1, di
		     mov word ptr Dest1+2, es  ;create another
					       ;copy of this
					       ;string. Note
					       ;that es:di points
					       ;at Dest1 upon
					       ;entry to StrDup,
					       ;but it points at
					       ;the new string on
					       ;exit
		     StrDup
		     jc MallocError
		     mov word ptr Dest2, di
		     mov word ptr Dest2+2, es


Description:  StrDup and StrDupl duplicate strings.  You pass them
	      a pointer to the string (in es:di for strdup, via
	      the return address for strdupl) and they allocate
	      sufficient storage on the heap for a copy of this
	      string.  Then these two routines copy their source
	      strings to the newly allocated storage and return
	      a pointer to the new string in ES:DI.


Include:             stdlib.a or strings.a


Routine:  Strlen
----------------

Category:            String Handling Routine

Registers on entry:  ES:DI - pointer to source string.

Register on return:  CX - length of specified string.

Flags Affected:      None

Examples of Usage:
		     les   di, String
		     strlen
		     mov   sl, cx
		     printf
		     db   "Length of '%s' is %d\n",0
		     dd   String, sl


Description:  Strlen computes the length of the string whose address
	      appears in ES:DI.  It returns the number of characters
	      up to, but not including, the zero terminating byte.

Include:             stdlib.a or strings.a


Routine:  Strcat (m,l,ml)
-------------------------

Category:             String Handling Routine

Registers on Entry:   ES:DI- Pointer to first string
		      DX:SI- Pointer to second string (Strcat and Strcatm only)


Registers on Return:  ES:DI- Pointer to new string (Strcatm and Strcatml only)

Flags Affected:       Carry = 0 if no error
		      Carry = 1 if insufficient memory (Strcatm and Strcatml
							only)


Example of Usage:     les  DI, String1
		      mov  DX, seg String2
		      lea  SI, String2
		      Strcat                   ; String1 <- String1 + String2

		      les  DI, String1
		      Strcatl                  ; String1 <- String1 +
		      db  "Appended String",0  ;            "Appended String",0


		      les  DI, String1
		      mov  DX, seg String2
		      lea  SI, String2
		      Strcatm                  ; NewString <- String1 + String2
		      puts
		      free

		      les  DI, String1
		      Strcatml                 ; NewString <- String1 +
		      db  "Appended String",0  ;         "Appended String",0
		      puts
		      free


Description:  These routines concatenate two strings together.  They differ
	      mainly in the location of their source and destination operands.

	      Strcat concatenates the string pointed at by DX:SI to the end of
	      the string pointed at by ES:DI in memory.  Both strings must be
	      zero-terminated.  The buffer pointed at by ES:DI must be large
	      enough to hold the resulting string.  Strcat does NOT perform
	      bounds checking on the data.

	      ( continued on next page )







Routine:  Strcat (m,l,ml)   ( continued )
-----------------------------------------


	      Strcatm computes the length of the two strings pointed at by ES:DI
	      and DX:SI and attempts to allocate this much storage on the heap.
	      If it is not successful, Strcatm returns with the Carry flag set,
	      otherwise it copies the string pointed at by ES:DI to the heap,
	      concatenates the string DX:SI points at to the end of this string
	      on the heap, and returns with the Carry flag clear and ES:DI
	      pointing at the new (concatenated) string on the heap.

	      Strcatl and Strcatml work just like Strcat and Strcatm except you
	      supply the second string as a literal constant immediately AFTER
	      the call rather than pointing DX:SI at it (see examples above).


Include:             stdlib.a or strings.a


Routine:  Strchr
----------------

Category:            String Handling Routine

Register on entry:   ES:DI- Pointer to string.
			AL- Character to search for.

Register on return:  CX- Position (starting at zero)
			 where Strchr found the character.

Flags affected:      Carry=0 if Strchr found the character.
		     Carry=1 if the character was not present
			     in the string.

Example of usage:
		     les di, String
		     mov al, Char2Find
		     Strchr
		     jc  NotPresent
		     mov CharPosn, cx


Description:  Strchr locates the first occurrence of a character within a
	      string.  It searches through the zero-terminated string pointed
	      at by es:di for the character passed in AL. If it locates the
	      character, it returns the position of that character to the CX
	      register.  The first character in the string corresponds to the
	      location zero.  If the character is not in the string, Strchr
	      returns the carry flag set.  CX's value is undefined in that
	      case.  If Strchr locates the character in the string, it
	      returns with the carry clear.


Include:             stdlib.a or strings.a


Routine:  Strstr (l)
--------------------

Category:            String Handling Routine

Register on entry:   ES:DI - Pointer to string.
		     DX:SI - Pointer to substring(strstr).
		     CS:RET - Pointer to substring (strstrl).

Register on return:  CX - Position (starting at zero)
		     where Strstr/Strstrl found the
		     character.  Carry=0 if Strstr/
		     Strstrl found the character.
		     Carry=1 if the character was not
		     present in the string.

Flags affected:      Carry flag

Example of usage :
		     les di, MainString
		     lea si, Substring
		     mov dx, seg Substring
		     Strstr
		     jc NoMatch
		     mov i, cx
		     printf
		     db "Found the substring '%s' at location %i\n",0
		     dd Substring, i


Description:  Strstr searches for the position of a substring
	      within another string.  ES:DI points at the
	      string to search through, DX:SI points at the
	      substring.  Strstr returns the index into ES:DI's
	      string where DX:SI's string is found.  If the
	      string is found, Strstr returns with the carry
	      flag clear and CX contains the (zero based) index
	      into the string.  If Strstr cannot locate the
	      substring within the string ES:DI points at, it
	      returns the carry flag set.  Strstrl works just
	      like Strstr except it excepts the substring to
	      search for immediately after the call instruction
	      (rather than passing this address in DX:SI).


Include:              stdlib.a or strings.a


Routine:  Strcmp (l)
--------------------

Category:            String Handling Routine

Registers on entry:  ES:DI contains the address of the first string
		     DX:SI contains the address of the second string (strcmp)
		     CS:RET (contains the address of the substring (strcmpl)

Register on return:  CX (contains the position where the two strings differ)

Flags affected:      Carry flag and zero flag (string1 > string2 if C + Z = 0)
					 (string1 < string2 if C = 1)

Example of Usage:
		     les 	di, String1
		     mov 	dx, seg String2
		     lea 	si, String2
		     strcmp
		     ja		OverThere

		     les 	di, String1
		     strcmpl
		     db 	"Hello",0
		     jbe 	elsewhere



Description:  Strcmp compares the first strings pointed by ES:DI with
	      the second string pointed by DX:SI. The carry and zero flag
	      will contain the corresponding result. So unsigned branch
	      instructions such as JA or JB is recommended. If string1
	      equals string2, strcmp will return with CX containing the
	      offset of the zero byte in the two strings.

	      Strcmpl compares the first string pointed by ES:DI with
	      the substring pointed by CS:RET. The carry and zero flag
	      will contain the corresponding result. So unsigned branch
	      instructions such as JA or JB are recommended. If string1
	      equals to the substring, strcmp will return with CX
	      containing the offset of the zero byte in the two strings.

Include:             stdlib.a or strings.a


Routine:  Stricmp (l)
---------------------

Category:            String Handling Routine

Registers on entry:  ES:DI contains the address of the first string
		     DX:SI contains the address of the second string (stricmp)
		     CS:RET (contains the address of the substring (stricmpl)

Register on return:  CX (contains the position where the two strings differ)

Flags affected:      Carry flag and zero flag (string1 > string2 if C + Z = 0)
					 (string1 < string2 if C = 1)

Example of Usage:
		     les 	di, String1
		     mov 	dx, seg String2
		     lea 	si, String2
		     stricmp
		     ja		OverThere

		     les 	di, String1
		     stricmpl
		     db 	"Hello",0
		     jbe 	elsewhere



Description:	This routine is virtually identical to strcmp (l) except it
		ignores case when comparing the strings.

Include:             stdlib.a or strings.a


Routine:  Strupr (m)
--------------------

Category:            String Handling Routine
		     Conversion Routine

Register on entry:   ES:DI (contains the pointer to input string)

Register on return:  ES:DI (contains the pointer to input string
			   with characters converted to upper case)
			   Note: struprm allocates storage for a new
			   string on the heap and returns the pointer
			   to this routine in ES:DI.

Flags affected:      Carry = 1 if memory allocation error (Struprm only).

Example of Usage:
		     les 	di, lwrstr1
		     strupr
		     puts

		     mov        di, seg StrWLwr
		     mov	es, di
		     lea	di, StrWLwr
		     struprm
		     puts
		     free


Description:  Strupr converts the input string pointed by ES:DI to
	      upper case.  It will actually modify the string you pass
	      to it.

	      Struprm first makes a copy of the string on the heap and
	      then converts the characters in this new string to upper
	      case.  It returns a pointer to the new string in ES:DI.

Include:             stdlib.a or strings.a


Routine:  Strlwr (m)
--------------------

Category:            String Handling Routine
		     Conversion Routine

Register on entry:   ES:DI (contains the pointer to input string)

Register on return:  ES:DI (contains the pointer to input string
			   with characters converted to lower case).

Flags affected:      Carry = 1 if memory allocation error (strlwrm only)


Example of Usage:
		     les di, uprstr1
		     strlwr
		     puts

		     mov        di, seg StrWLwr
		     mov	es, di
		     lea	di, StrWLwr
		     strlwrm
		     puts
		     free




Description:  Strlwr converts the input string pointed by ES:DI to
	      lower case. It will actually modify the string you pass
	      to it.

	      Strlwrm first copies the characters onto the heap and then
	      returns a pointer to this string after converting all the
	      alphabetic characters to lower case.


Include:             stdlib.a or strings.a



Routine:  Strset (m)
--------------------

Category:            String Handling Routine

Register on entry:   ES:DI contains the pointer to input string (StrSet only)
		     AL    contains the character to copy
		     CX    contains number of characters to allocate for
			   the string (Strsetm only)

Register on return:  ES:DI pointer to newly allocated string (Strsetm only)

Flags affected:      Carry set if memory allocation error (Strsetm only)

Example of Usage:
		     les 	di, string1
		     mov	al, " "		;Blank fill string.
		     Strset

		     mov 	cx, 32
		     mov	al, "*"		;Create a new string w/32
		     Strsetm			; asterisks.
		     puts
		     free


Description:  Strset overwrites the data on input string pointed by
	      ES:DI with the character on AL.

	      Strsetm creates a new string on the heap with the number
	      of characters specified in CX.  All characters in the string
	      are initialized with the value in AL.

Include:             stdlib.a or strings.a


Routine:  Strspan (l)
---------------------

Category:             String Handling Routine

Registers on Entry:   ES:DI - Pointer to string to scan
		      DX:SI - Pointer to character set (Strspan only)
		      CS:RET- Pointer to character set (Strspanl only)

Registers on Return:  CX- First position in scanned string which does not
			  contain one of the characters in the character set

Flags Affected:       None

Example of Usage:
		      les  DI, String
		      mov  DX, seg CharSet
		      lea  SI, CharSet
		      Strspan           ; find first position in String with a
		      mov i, CX         ;                  char not in CharSet
		      printf
		      db  "The first char which is not in CharSet "
		      db  "occurs at position %d in String.\n",0
		      dd  i

		      les  DI, String
		      Strspanl          ; find first position in String which
		      db   "aeiou",0    ; is not a vowel
		      mov  j, CX
		      printf
		      db  "The first char which is not a vowel "
		      db  "occurs at position %d in String.\n",0
		      dd  j


Description:  Strspan(l) scans a string, counting the number of characters which
	      are present in a second string (which represents a character set).
	      ES:DI points at a zero-terminated string of characters to scan.
	      DX:SI (strspan) or CS:RET (strspanl) points at another zero-
	      terminated string containing the set of characters to compare
	      against.  The position of the first character in the string
	      pointed to by ES:DI which is NOT in the character set is returned.
	      If all the characters in the string are in the character set, the
	      position of the zero-terminating byte will be returned.

	      Although strspan and (especially) strspanl are very compact and
	      convenient to use, they are not particularly efficient.  The
	      character set routines provide a much faster alternative at the
	      expense of a little more space.


Include:               stdlib.a or strings.a


Routine:  Strcspan, Strcspanl
-----------------------------

Category:             String Handling Routine

Registers on Entry:   ES:DI - Pointer to string to scan
		      DX:SI - Pointer to character set (Strcspan only)
		      CS:RET- Pointer to character set (Strcspanl only)

Registers on Return:  CX- First position in scanned string which contains one
			  of the characters in the character set

Flags Affected:       None

Example of Usage:
		      les  DI, String
		      mov  DX, seg CharSet
		      lea  SI, CharSet
		      Strcspan          ; find first position in String with a
		      mov i, CX         ;                      char in CharSet
		      printf
		      db  "The first char which is in CharSet "
		      db  "occurs at position %d in String.\n",0
		      dd  i

		      les  DI, String
		      Strcspanl         ; find first position in String which
		      db   "aeiou",0	; is a vowel.
		      mov  j, CX
		      printf
		      db  "The first char which is a vowel occurs "
		      db  "at position %d in String.\n",0
		      dd  j


Description:  Strcspan(l) scans a string, counting the number of characters
	      which are NOT present in a second string (which represents a
	      character set).  ES:DI points at a zero-terminated string of
	      characters to scan.  DX:SI (strcspan) or CS:RET (strcspanl) points
	      at another zero-terminated string containing the set of characters
	      to compare against.  The position of the first character in the
	      string pointed to by ES:DI which is in the character set is
	      returned.  If all the characters in the string are not in the
	      character set, the position of the zero-terminating byte will be
	      returned.

	      Although strcspan and strcspanl are very compact and convenient to
	      use, they are not particularly efficient.  The character set
	      routines provide a much faster alternative at the expense of a
	      little more space.

Include:              stdlib.a or strings.a


Routine:  StrIns (m,l,ml)
-------------------------

Category:             String Handling Routine

Registers on Entry:   ES:DI - Pointer to destination string (to insert into)
		      DX:SI - Pointer to string to insert
					  (StrIns and StrInsm only)
		      CX    - Insertion point in destination string

Registers on Return:  ES:DI - Pointer to new string (StrInsm and StrInsml only)

Flags Affected:       Carry = 0 if no error
		      Carry = 1 if insufficient memory
				   (StrInsm and StrInsml only)


Example of Usage:
		      les  DI, DestStr
		      mov  DX, word ptr SrcStr+2
		      mov  SI, word ptr SrcStr
		      mov  CX, 5
		      StrIns     ; Insert SrcStr before the 6th char of DestStr

		      les  DI, DestStr
		      mov  CX, 2
		      StrInsl    ; Insert "Hello" before the 3rd char of DestStr
		      db  "Hello",0

		      les  DI, DestStr
		      mov  DX, word ptr SrcStr+2
		      mov  SI, word ptr SrcStr
		      mov  CX, 11
		      StrInsm      ; Create a new string by inserting SrcStr
				   ;         before the 12th char of DestStr
		      puts
		      putcr
		      free


Description:  These routines insert one string into another string.  ES:DI
	      points at the string into which you want to insert another.  CX
	      contains the position (or index) where you want the string
	      inserted.  This index is zero-based, so if CX contains zero, the
	      source string will be inserted before the first character in the
	      destination string.  If CX contains a value larger than the size
	      of the destination string, the source string will be appended to
	      the destination string.

	      StrIns inserts the string pointed at by DX:SI into the string
	      pointed at by ES:DI at position CX.  The buffer pointed at by
	      ES:DI must be large enough to hold the resulting string.  StrIns
	      does NOT perform bounds checking on the data.

     ( continued on next page )


Routine:  StrIns (m,l,ml)   ( continued )
-----------------------------------------

	      StrInsm does not modify the source or destination strings, but
	      instead attempts to allocate a new buffer on the heap to hold the
	      resulting string.  If it is not successful, StrInsm returns with
	      the Carry flag set, otherwise the resulting string is created and
	      its address is returned in the ES:DI registers.

	      StrInsl and StrInsml work just like StrIns and StrInsm except you
	      supply the second string as a literal constant immediately AFTER
	      the call rather than pointing DX:SI at it (see examples above).



Routine:  StrDel, StrDelm
-------------------------

Category:              String Handling Routine

Registers on Entry:    ES:DI -  pointer to string
		       CX - deletion point in  string
		       AX - number of characters to delete

Registers on return:   ES:DI - pointer to new string (StrDelm only)

Flags  affected:       Carry = 1 if memory allocation error,  0 if okay
		       (StrDelm only).

Example of Usage:
		       les     di,  Str2Del
		       mov     cx,  3          ; Delete starting at 4th char
		       mov     ax,  5          ; Delete five characters
		       StrDel                  ; Delete in place

		       les     di,  Str2Del2
		       mov     cx,  5
		       mov     ax,  12
		       StrDelm
		       puts
		       free


Description:  StrDel deletes characters from a string.  It works by computing
	      the beginning and end of the deletion point.  Then it copies all
	      the characters from the end of the deletion point to the end of
	      the string (including the zero byte) to the beginning of the
	      deletion point.  This covers up (thereby effectively deleting)
	      the undesired characters in the string.

	      Here are two degenerate cases to worry about -- 1) when you
	      specify a deletion point which is beyond the end of the string;
	      and 2) when the deletion point is within the string but the
	      length of the deletion takes you beyond the end of the string.
	      In the first case StrDel simply ignores the deletion request.  It
	      does not modify the original string.  In the second case,
	      StrDel simply deletes everything from the deletion point to the
	      end of the string.

	      StrDelm works just like StrDel except it does not delete the
	      characters in place.  Instead, it creates a new string on the
	      heap consisting of the characters up to the deletion point and
	      those following the characters to delete.  It returns a pointer
	      to the new string on the heap in ES:DI, assuming that it
	      properly allocated the storage on the heap.

Include:               stdlib.a or strings.a


Routine:  StrTrim (m)
---------------------

Category:              String Handling Routine

Registers on Entry:    ES:DI -  pointer to string

Registers on return:   ES:DI - pointer to string (new string if StrTrimm)

Flags  affected:       Carry = 1 if memory allocation error,  0 if okay
		       (StrTrimm only).

Example of Usage:
		       les     di,  Str2Trim
		       StrTrim                 ; Delete in place
		       puts

		       les     di,  Str2Trim2
		       StrTrimm
		       puts
		       free


Description:	StrTrim (m) removes trailing spaces from a string.  StrTrim
		removes the space in the specified string (by backing up the
		zero terminating byte in the string.  StrTrimm creates a new
		copy of the string (on the heap) without the trailing spaces.

Include:               stdlib.a or strings.a


Routine:  StrBlkDel (m)
-----------------------

Category:              String Handling Routine

Registers on Entry:    ES:DI -  pointer to string

Registers on return:   ES:DI - pointer to string (new string if StrBlkDelm)

Flags  affected:       Carry = 1 if memory allocation error,  0 if okay
		       (StrBlkDelm only).

Example of Usage:
		       les     di,  Str2Trim
		       StrBlkDel		; Delete in place
		       puts

		       les     di,  Str2Trim2
		       StrBlkDelm
		       puts
		       free


Description:	StrBlkDel (m) removes leading spaces from a string.  StrBlkDel
		removes the space in the specified string, modifying that
		string.  StrBlkDelm creates a new copy of the string (on the
		heap) without the leading spaces.

Include:               stdlib.a or strings.a


Routine:  StrRev, StrRevm
-------------------------

Author:               Michael Blaszczak (.B  ekiM)

Category:             String Handling Routine

Registers on Entry:   ES:DI - pointer to string

Registers on return:  ES:DI - pointer to new string (StrRevm only).

Flags affected:       Carry  = 1 if memory allocation error, 0 if okay
		      (StrRevm only).

Example of Usage:

Description:  StrRev reverses the characters in a string.  StrRev reverses,
	      in place, the characters in the string that ES:SI points at.
	      StrRevm creates a new string on the heap (which contains the
	      characters in the string ES:DI points at, only reversed) and
	      returns a pointer to the new string in ES:DI.  If StrRevm
	      cannot allocate sufficient memory for the string, it returns
	      with the carry flag set.


Include:              stdlib.a or strings.a

Routine:  StrBDel (m)
---------------------

Author:               Randall Hyde

Category:             String Handling Routine

Registers on Entry:   ES:DI - pointer to string

Registers on return:  ES:DI - pointer to new string (StrBDelm only).

Flags affected:       Carry  = 1 if memory allocation error, 0 if okay
		      (StrBDelm only).

Example of Usage:

Description:	StrBDel(m) deletes leading blanks from a string.  StrBDel
		operates on the string in place, StrBDelm creates a copy
		(on the heap) of the string without the leading blanks.

Include:              stdlib.a or strings.a


Routine:  ToHex
---------------

Category:             String Handling Routine/ Conversion Routine

Registers on Entry:   ES:DI - pointer to byte array
		      BX-     memory base address for bytes
		      CX-     number of entries in byte array

Registers on return:  ES:DI - pointer to Intel Hex format string.

Flags affected:       Carry  = 1 if memory allocation error, 0 if okay


Example of Usage:

		mov	bx, 100h	;Put data at address 100h in hex file.
		mov	cx, 10h		;Total of 16 bytes in this array.
		les	di, Buffer	;Pointer to data bytes
		ToHex			;Convert to Intel HEX string format.
		puts			;Print it.

Description:

ToHex converts a stream of binary values to Intel Hex format.  Intel HEX format
is a common ASCII data interchange format for binary data.  It takes the
following form:

	: BB HHLL RR DDDD...DDDD SS <cr> <lf>

(Note:spaces were added for clarity, they are not actually present in the
hex string)

BB is a pair of hex digits which represent the number of data bytes (The DD
entries) and is the value passed in CX.

HHLL is the hexadecimal load address for these data bytes (passed in BX).

RR is the record type.  ToHex always produces data records with the RR field
containing "00".  If you need to output other field types (usually just an
end record) you must create that string yourself.  ToHex will not do it.

DD...DD is the actual data in hex form.  This is the number of bytes specified
in the BB field.

SS is the two's complement of the checksum (which is the sum of the binary
values of the BB, HH, LL, RR, and all DD fields).

This routine allocates storage for the string on the heap and returns a pointer
to that string in ES:DI.

Include:              stdlib.a or strings.a

Utility Routines
----------------

The following routines are all Utility Routines.  The first routines listed
below compute the number of print positions required by a 16-bit and 32-bit
signed and unsigned integer value.  UlSize is like the LSize except it treats
the value in DX:AX as an unsigned long integer.  The next set of routines in
this section check the character in the AL register to see whether it is a
hexidecimal digit, if it alphabetic, if it is a lower case alphabetic, if it
is a upper case alphabetic, and if it is numeric.  Then there are some
miscellaneous routines (macros) which process command line parameters, invoke
DOS and exit the program.



Routine:  ISize
---------------

Category:            Utility Routine

Register on entry:   AX- 16-bit value to compute the
				output size for.

Register on return:  AX- Number of print positions
				required by this number (including
				the minus sign, if necessary).

Flags affected:      None

Example of usage:
		     mov     ax, I
		     ISize
		     puti                    ;Prints positions
					     ;req'd by I.


Description:         This routine computes the number of print positions
		     required by a 16-bit signed integer value.  ISize computes
		     the minimum number of character positions it takes to print
		     the signed decimal value in the AX register.  If the number
		     is negative, it will include space for the minus sign in
		     the count.


Include:             stdlib.a or util.a




Routine:  USize
---------------

Category:            Utility Routine

Register on entry:   AX- 16 bit value to compute the
				output size for

Register on return:  AX- number of print positions
			 required by this number (including
			 the minus sign, if necessary)

Flags affected:      None

Example of usage:
		     mov     ax, I
		     USize
		     puti                    ;prints position
					     ;required by I


Description:         This routine computes the number of print positions
		     required by a 16-bit signed integer value.  It also
		     computes the number of print positions required by a
		     16-bit unsigned value.  USize computes the minimum number
		     of character positions it will take to print an unsigned
		     decimal value in the AX register.  If the number is
		     negative, it will include space for the minus sign in the
		     count.


Include:             stdlib.a or util.a


Routine:  LSize
---------------

Category:            Utility Routine

Register on entry:   DX:AX   - 32-bit value to compute the
			       output size for.

Register on return:  AX - Number of print positions
			  required by this number (including
			  the minus sign, if necessary).

Flags affected:      None

Example of Usage:
		     mov     ax, word ptr L
		     mov     dx, word ptr L+2
		     LSize
		     puti                    ;Prints positions
					     ;req'd by L.


Description:         This routine computes the number of print positions
		     required by a 32-bit signed integer value.  LSize computes
		     the minimum number of character positions it will take to
		     print the signed decimal value in the DX:AX registers.  If
		     the number is negative, it will include space for the minus
		     sign in the count.


Include:             stdlib.a or util.a



Routine:  ULSize
----------------

Category:             Utility Routine

Registers on Entry:   DX:AX - 32-bit value to compute the output size for.

Registers on return:  AX - number of print positions required by this number

Flags affected:       None

Example of Usage:
		      mov     ax, word ptr L
		      mov     dx, word ptr L+2
		      ULSize
		      puti                    ; Prints positions req'd by L


Description:          ULSize computes the minimum number of character
		      positions it will take to print an unsigned decimal
		      value in the DX:AX registers.


Include:              stdlib.a or util.a



Routine:  IsAlNum
-----------------

Category:             Utility routine

Register on entry:    AL - character to check.

Register on return:   None

Flags affected:       Zero flag - set if character is alphanumeric,
		      clear if not.


Example of usage :    mov al, char
		      IsAlNum
		      je IsAlNumChar


Description :         This routine checks the character in the AL register to
		      see if it is in the range A-Z, a-z, or 0-9.  Upon return,
		      you can use the JE instruction to check to see if the
		      character was in this range (or, conversely, you can use
		      JNE to see if it is not in range).


Include:              stdlib.a or util.a


Routine:  IsXDigit
------------------

Category:               Utility Routine

Register on Entry:     AL- character to check

Registers on Return:    None

Flags Affected:         Zero flag-  Set if character is a hex digit, clear if not


Example of Usage:       mov    al, char
			IsXDigit
			je     IsXDigitChar


Description:            This routine checks the character in the AL register to
			see if it is in the range A-F, a-f, or 0-9.  Upon
			return, you can use the JE instruction to check to see
			if the character was in this range (or, conversely,
			you can use jne to see if it is not in the range).


Include:                stdlib.a or util.a


Routine:   IsDigit
------------------

Category:            Utility Routine

Register on entry:   AL- Character to check

Register on return:  None

Flags affected:	     Zero flag- set if character is numeric, clear if not.

Example of Usage:    mov   al, char
		     IsDigit
		     je  IsDecChar


Description:         This routine checks the character in the AL register to
		     see if it is in the range 0-9.  Upon return, you can use
		     the JE instruction to check to see if the character was
		     in the range (or, conversely, you can use JNE to see if it
		     is not in the range).


Include:             stdlib.a or util.a


Routine:   IsAlpha
------------------

Category:            Utility Routine

Register on entry:   AL- Character to check

Register on return:  None

Flags affected:	     Zero flag- set if character is alphabetic, clear if not.

Example of Usage:    mov   al, char
		     IsAlpha
		     je   IsAlChar


Description:         This routine checks the character in the AL register to
		     see if it is in the range A-Z or a-z.  Upon return, you
		     can use the JE instruction to check to see if the character
		     was in the range (or, conversely, you can use JNE to see
		     if it is not in the range).

Include:             stdlib.a or util.a




Routine: IsLower
----------------

Category:             Utility Routine

Registers on Entry:   AL- character to test

Registers on Return:  None


Flags Affected:       Zero = 1 if character is a lower case alphabetic character
		      Zero = 0 if character is not a lower case alphabetic
		      character

Example of Usage:     mov  AL, char        ; put char in AL
		      IsLower              ; is char lower a-z?
		      je  IsLowerChar      ; if yes, jump to IsLowerChar


Description:          This routine checks the character in the AL register to
		      see if it is in the range a-z.  Upon return, you can use
		      the JE instruction to check and see if the character was
		      in this range (or you can use JNE to check and see if
		      the character was not in this range).  This procedure is
		      implemented as a macro for high performance.


Include:              stdlib.a or util.a


Routine:  IsUpper
-----------------

Category:             Utility Routine

Registers on Entry:   AL- character to check

Registers on Return:  None

Flags Affected:       Zero flag - set if character is uppercase alpha, clear
				  if not.


Example of Usage:     mov al, char
		      IsUpper
		      je IsUpperChar


Description:          This routine checks the character in the AL register to
		      see if it is in the ranger A-Z.  Upon return, you can use
		      the JE instruction to check to see if it not in the
		      range).  It uses macro implementation for high performance.


Include:              stdlib.a or util.a

Linked list manipulation routines
=================================

These routines manipulate items in a linked list.  Internally the system
represents the data as a doubly linked list, although your program should
not rely on the internal structure of the data structure.

There are two structures of interest defined in the LISTS.A file: LIST
and NODE.  Use variables of type LIST to create brand new lists.  Use
variables of type NODE to hold the entries in the list.

These structures take the following form:

List		struc
Size		dw	?		;Size, in bytes, of a node in the list
Head		dd	0		;Ptr to start of list
Tail		dd	0		;Ptr to end of list
Current		dd	0		;Pointer to current node
List		ends

Node		struc
Next		dd	?		;Ptr to next node in list
Prev		dd	?		;Ptr to prev node in list
NodeData	db	??		;Data immediately follows Prev
Node		ends


There are two ways to create a new list: statically or dynamically.
Consider static allocation first.  In this case, you create a list variable
by declaring an object of type LIST in a data segment, e.g.,

MyList		list	<25>

You *must* supply the size (in bytes) of a node in the list.  Note that the
size should *not* include the eight bytes required for the next and prev
pointers.  This allows you to change the internal structure of the list
(e.g., to a singly linked list) without having to change other code.  You
can easily compute this as follows:

MyList		list	<(sizeof MyNode) - (sizeof Node)>

When you declare lists in this fashion, the definition automatically
initializes the list to an empty list.

You can also create a list dynamically by calling the CreateList routine.
To CreateList you must pass the size of a Node (not including the pointers)
in the CX register.  It allocates storage for the list variable on the
heap and returns a pointer to this new (empty) list in es:di.

		mov	cx, (sizeof MyNode) - (sizeof Node)
		CreateList
		mov	word ptr MyListPtr, di
		mov	word ptr MyListPtr+2, es


To create nodes for your list, you should "overload" the NODE definition
appearing the in LISTS.A file.  This works best under MASM 6.0 and TASM 3.0,
which support object-oriented programming, though it isn't that difficult to
accomplish with other assemblers.  A mechanism compatible with *all*
assemblers follows:

To create a brand new node is easy,  just do the following:

MyNode		struc
		db	(size Node) dup (0) 	;Inherit all fields from NODE.
Field1		db	?			;User-supplied fields for this
Field2		dw	?			; particular node type.
Field3		dd	?			;  "    "     "    "
Field4		real4	3.14159			;  "    "     "    "
MyNode		ends

Note that the NODE fields must appear *first* in the data structure.
The list manipulation routines assume that the list pointers in NODE appear
at the beginning of the structure.

The CurrentNode field of the list data structure points at a "current" node
in the list.  The current node is the last node operated on in the case of
insert, append, peek, etc.  In the event a node is removed, the current node
will be the next node after the node removed.  In general, the current node
can be thought of as a "cursor" which wanders through the list according to
the operations occuring.  Since most list operations occur on the next node
in a list, keeping the CurrentNode field updated speeds up access to the
list.

You can use the following routines to implement the corresponding data
structures (which can all be implemented using lists):

FIFO Queues:

AppendLastm, AppendLast, Remove1st, and Peek1st (technically, using Peek1st
is cheating, but so what).



Deques (double ended queues):

All the FIFO routines plus InsertFirstm, InsertFirst, RemoveLast, and
PeekLast (PeekLast is cheating too).


Lists:

All of the above plus InsertCur, InsertmCur, AppendCur, AppendmCur,
RemoveCur, Insert, Insertm, Append, Appendm, Remove, SetCur, NextNode,
and PrevNode.

For those who care about such things, the UCR Standard Library implements
the list data structure using a doubly linked list.  However, it is a
true generic (encapsulated) data type and your code needed be at all
concerned about the internal structure.  Furthermore, assuming you treat
it like an encapsulated data structure, you can modify the internal list
structure and not break any programs which use the list data types.





Routine:  CreateList
--------------------

Author:		      Randall Hyde

Category:             List Manipulation

Registers on entry:   	CX-	Size of data (in bytes) to store at each node

Registers on return:  	ES:DI-	Pointer to new list variable on heap

Flags affected:       	Carry set if CreateList cannot allocate sufficient
			storage on the heap for the list variable.

Example of Usage:
			mov	cx, (sizeof MyNode) - (sizeof Node)
			CreateList
			jc	ListError
			mov	word ptr ListVarPtr, di
			mov	word ptr ListVarPtr+2, es

Description:

CreateList allocates storage for a list variable on the head and initializes
that variable to the empty list.  It also sets up the size field of the
list variable based on the value passed in the CX register.  It returns
a pointer to the newly created list in the ES:DI registers.

This routine initializes the CurrentNode field to NIL.  Any node inserted
before or after the current node will be inserted as the first node in this
case.

Include:	lists.a or stdlib.a



Routine:  AppendLast (m)
------------------------

Author:		      Randall Hyde

Category:             List Manipulation

Registers on entry:   	DX:SI-	Pointer to node to add to list (AppendLast)
			DX:SI-	Pointer to block of data (sans list stuff)
				to add to end of list (AppendLastm)
			ES:DI-	Pointer to list.

Registers on return:  	ES:DI-	Pointer to list.

Flags affected:       	Carry set if AppendLastm cannot allocate sufficient
			storage on the heap for the list variable.

Examples of Usage:

; Append data statically declared as ANode to the end of the list pointed at
; by the list variable "ListVar".

			ldxi	ANode
			les	di, ListVar
			AppendLast

; Create a node from the data at address "MyData".  Build the node on the
; heap and append this node to the end of the list pointed at by ListVar.

			ldxi	MyData
			les	di, ListVar
			AppendLastm
			jc	BadListError

Description:

AppendLast and AppendLastm add a node to the end of a list.  AppendLast works
with whole nodes.  It is useful, for example when moving a node from one
list to another or when dealing with nodes that were created statically in
the program.  It requires nodes properly declared using the NODE data type
in the LIST.A include file.

AppendLastm builds a new node on the heap and appends this node to the end
of the specified list.  The difference between AppendLastm and AppendLast is
that AppendLastm does not require a predefined node.  Instead, DX:SI points
at the data for the node (the number of bytes is specified by the ListSize
field of the LIST data type).  AppendLastm allocates memory, copies the data
from DX:SI to the data field of the new node, and then links in the new node
to the specified list.

The new node added to the list becomes the CurrentNode.

Include:	stdlib.a or lists.a



Routine:  Remove1st
-------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list variable.

Registers on return:	DX:SI-	Pointer to node removed from the front of
				the list (NIL if nothing in list).

Flags affected:       	Carry set if the list was empty.

Examples of Usage:

; The following loop removes all the items from a list and processes each
; item.

DoAllOfList:		les	di, MyList
			Remove1st
			jc	DidItAll
			<manipulate this item>
			jmp	DoAllOfList
DidItAll:


Description:

Remove1st removes the first item from a list and returns a pointer to that
item in DX:SI.  If the list was empty, then it returns a NIL pointer in
DX:SI and returns with the carry flag set.

Note that you can use the AppendLast(m) and Remove1st routines to implement
and manipulate a FIFO queue data structure.  Peek1st is another useful
routine which returns the first item on a list without removing it from
the list.

The second node in the list (the one after the node just removed) becomes
the new CurrentNode.  If there are no additional nodes in the list, the
CurrentNode variable gets set to NIL.

Include:	stdlib.a or lists.a




Routine:  Peek1st
-----------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list variable.

Registers on return:	DX:SI-	Pointer to node at the beginning of
				the list (NIL if nothing in list).

Flags affected:       	Carry set if the list was empty.

Examples of Usage:

			les	di, MyList
			Peek1st
			jc	NothingThere

Description:

Peek1st is similar to Remove1st in that it returns a pointer to the first
item in a list (NIL if the list is empty).  However, it does not remove the
item from the list.  This is useful for performing a "non-destructive" read
of the first item in a FIFO queue.

This routine sets the CurrentNode field to the first node in the list.

Include:	stdlib.a or lists.a



Routine:  Insert1st (m)
-----------------------

Author:		      Randall Hyde

Category:             List Manipulation

Registers on entry:   	DX:SI-	Pointer to node to add to list (Insert1st)
			DX:SI-	Pointer to block of data (sans list stuff)
				to add to end of list (Insert1stm)
			ES:DI-	Pointer to list.

Registers on return:  	ES:DI-	Pointer to list.

Flags affected:       	Carry set if Insertm cannot allocate sufficient
			storage on the heap for the list variable.

Examples of Usage:

; Insert data statically declared as ANode to the beginning of the list
; pointed at by the list variable "ListVar".

			ldxi	ANode
			les	di, ListVar
			Insert1st

; Create a node from the data at address "MyData".  Build the node on the
; heap and insert this node to the beginning of the list pointed at by
; ListVar.

			ldxi	MyData
			les	di, ListVar
			Insert1stm
			jc	BadListError

Description:

Insert1st and Insert1stm add a node to the beginning of a list.  Insert1st
works with whole nodes.  It is useful, for example when moving a node from one
list to another or when dealing with nodes that were created statically in
the program.  It requires nodes properly declared using the NODE data type
in the LISTS.A include file.

Insert1stm builds a new node on the heap and inserts this node to the start
of the specified list.  The difference between Insert1stm and Insert1st is
that Insert1stm does not require a predefined node.  Instead, DX:SI points
at the data for the node (the number of bytes is specified by the ListSize
field of the LIST data type).  Insert1stm allocates memory, copies the data
from DX:SI to the data field of the new node, and then links in the new node
to the specified list.

Note that Insert1st/Insert1stm can be used to create Deque data structures.

The newly inserted node becomes the CurrentNode in the list.

Include:	stdlib.a or lists.a



Routine:  RemoveLast
--------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list variable.

Registers on return:	DX:SI-	Pointer to node removed from the end of
				the list (NIL if nothing in list).

Flags affected:       	Carry set if the list was empty.

Examples of Usage:

; The following loop removes all the items from a list and processes each
; item.

DoAllOfList:		les	di, MyList
			RemoveLast
			jc	DidItAll
			<manipulate this item>
			jmp	DoAllOfList
DidItAll:


Description:

RemoveLast removes the last item from a list and returns a pointer to that
item in DX:SI.  If the list was empty, then it returns a NIL pointer in
DX:SI and returns with the carry flag set.

Note that you can use the Insert1st(m) and RemoveLast routines to implement
and manipulate a DEQUE queue data structure (along with the FIFO routines:
AppendLast(m), Rmv1st, and Peek1st).  PeekLast is another useful
routine which returns the last item on a list without removing it from
the list.

The last node in the list (the one before the node just removed) becomes the
new CurrentNode in the list.  If the list is empty, CurrentNode gets set to
NIL.

Include:	stdlib.a or lists.a




Routine:  PeekLast
------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list variable.

Registers on return:	DX:SI-	Pointer to node at the end of
				the list (NIL if nothing in list).

Flags affected:       	Carry set if the list was empty.

Examples of Usage:

			les	di, MyList
			PeekLast
			jc	NothingThere

Description:

PeekLast is just like Peek1st except it looks at the last node on the list
rather than the first.  It does the same job as RemoveLast except it does
not remove the node from the list.  Great for implementing Deques.

This routine also sets the CurrentNode field to point at the last node in
the list.

Include:	stdlib.a or lists.a




Routine:  InsertCur
-------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			DX:SI-	Pointer to node to insert

Examples of Usage:

			les	di, MyList
			ldxi	NewNode
			InsertCur

Description:

InsertCur inserts the node pointed at by DX:SI before the "current" node in
the list.  The current node is the last one operated on by the software.

The newly inserted node becomes the CurrentNode in the list.

Include:	stdlib.a or lists.a




Routine:  InsertmCur
--------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			DX:SI-	Pointer to data for node to insert

Flags on exit:		Carry flag is set if malloc error occurs.

Examples of Usage:

			les	di, MyList
			ldxi	DataBlock
			InsertmCur
			jc	Error

Description:

InsertmCur builds a new node on the heap (using the block of data pointed at
by DX:SI and the size of a node in the size field of the list variable) and
then inserts the new node before the "current" node in the list.  The current
node is the last one operated on by the software.

This code treats the newly inserted node as the current node.

Include:	stdlib.a or lists.a



Routine:  AppendCur
-------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			DX:SI-	Pointer to node to append

Examples of Usage:

			les	di, MyList
			ldxi	NewNode
			AppendCur

Description:

AppendCur inserts the node pointed at by DX:SI after the "current" node in
the list.  The current node is the last one operated on by the software.

The newly inserted node becomes the CurrentNode in the list.

Include:	stdlib.a or lists.a




Routine:  AppendmCur
--------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			DX:SI-	Pointer to data for node to insert.

Flags on exit:		Carry flag is set if malloc error occurs.

Examples of Usage:

			les	di, MyList
			ldxi	DataBlock
			AppendmCur
			jc	MallocError

Description:

AppendmCur builds a new node on the heap (using the block of data pointed at
by DX:SI and the size of a node in the size field of the list variable) and
then inserts the new node after the "current" node in the list.  The current
node is the last one operated on by the software.

This code treats the newly inserted node as the current node.

Include:	stdlib.a or lists.a



Routine:  RemoveCur
-------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.

Registers on exit:	DX:SI-	Points at node removed from list (NIL if
				no such node).

Flags on return:	Carry set if the list was empty.

Examples of Usage:

			les	di, MyList
			RemoveCur
			jc	EmptyList

Description:

RemoveCur removes the current node (pointed at by CurrentNode) from the list
and returns a pointer to this node in DX:SI.  If the list was empty, RemoveCur
returns NIL in DX:SI and sets the carry flag.

This routine modifies CurrentNode so that it points at the next item in the
list (the node normally following the current node).  If there is no such
node (i.e., CurrentNode pointed at the last node in the list upon calling
RemoveCur) then this routine stores the value of the *previous* node into
CurrentNode.  If you use this routine to delete the last node in the list,
it sets CurrentNode to NIL before leaving.

Include:	stdlib.a or lists.a



Routine:  PeekCur
-----------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.

Registers on exit:	DX:SI-	Points at the current node (i.e., contains
				a copy of CurrentNode), NIL if the list
				is empty.

Flags on return:	Carry set if the list was empty.

Examples of Usage:

			les	di, MyList
			PeekCur
			jc	EmptyList

Description:

PeekCur simply returns CurrentNode in DX:SI (assuming the list is not empty).
If the list is empty, it returns the carry flag set and NIL in DX:SI.
It does not affect the value of CurrentNode.

Include:	stdlib.a or lists.a



Routine:  SetCur
----------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			CX-	Node number of new current node.

Registers on exit:	DX:SI-	Returned pointing at selected node.
				NIL if the list is empty.  Points at the
				last node in the list if the value in CX
				is greater than the number of nodes in the
				list.

Flags on return:	Carry set if the list was empty.

Examples of Usage:

			les	di, MyList
			mov	cx, NodeNum
			SetCur
			jc	EmptyList

Description:

SetCur locates the specified node in the list and sets CurrentNode to the
address of that node. It also returns a pointer to that node in DX:SI.
If CX is greater than the number of nodes in the list (or zero) then
SetCur sets CurrentNode to the last node in the list.  If the list is
empty, SetCur returns NIL in DX:SI and returns with the carry flag set.

Include:	stdlib.a or lists.a



Routine:  NextNode
------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.

Registers on exit:	DX:SI-	Returned pointing at selected node.
				NIL if the list is empty.  Points at the
				last node in the list if the current node
				was the last node in the list

Flags on return:	Carry set if the current node was the last node
			or the list was empty.

Examples of Usage:

			les	di, MyList
			NextNode
			jc	EmptyOrEnd

Description:

NextNode modifies the CurrentNode pointer so that it points to the next
node in the list, if there is one. It also returns a pointer to that node
in DX:SI.  If the list is empty, or CurrentNode points at the last node
in the list, NextNode returns with the carry flag set.

Include:	stdlib.a or lists.a



Routine:  PrevNode
------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.

Registers on exit:	DX:SI-	Returned pointing at selected node.
				NIL if the list is empty.  Points at the
				1st node in the list if the current node
				was the 1st node in the list

Flags on return:	Carry set if the current node was the 1st node
			or the list was empty.

Examples of Usage:

			les	di, MyList
			PrevNode
			jc	EmptyOr1st

Description:

PrevNode modifies the CurrentNode pointer so that it points to the previous
node in the list, if there is one. It also returns a pointer to that node
in DX:SI.  If the list is empty, or CurrentNode points at the 1st node
in the list, PrevNode returns with the carry flag set.

Include:	stdlib.a or lists.a



Routine:  Insert (m)
--------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			DX:SI-	Address of node to insert (Insert)
			DX:SI-	Pointer to data block to create node from
				(Insertm).
			CX-	Number of node to insert DX:SI in front of;
				Note that the list is one-based.  That is,
				the number of the first node in the list is
				one.  Zero corresponds to the last node in
				the list.


Flags on return:	Carry set if malloc error occurs (Insertm only).

Examples of Usage:

			les	di, MyList
			ldxi	NewNode
			mov	cx, 5
			Insert			;Inserts before Node #5.

; The following example builds a new node on the heap from the data at
; location "RawData" and inserts this before node #5 in MyList.

			les	di, MyList
			ldxi	RawData
			mov	cx, 5
			Insertm
			jc	MallocError

Description:

Insert(m) inserts a new node before a specified node in the list.  The node to
insert in front of is specified by the value in the CX register.  The first
node in the list is node #1, the second is node #2, etc.  If the value in
CX is greater than the number of nodes in the list (in particular, if CX
contains zero, which gets treated like 65,536) then Insert(m) appends the
new node to the end of the list.

Insertm allocates a new node on the heap (DX:SI points at the data fields
for the node).  If a malloc error occurs, Insertm returns the carry flag
set.

CurrentNode gets set to the newly inserted node.

Include:	stdlib.a or lists.a



Routine:  Append (m)
--------------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			DX:SI-	Address of node to insert (Append)
			DX:SI-	Pointer to data block to create node from
				(Appendm).
			CX-	Number of node to insert DX:SI after;
				Note that the list is one-based.  That is,
				the number of the first node in the list is
				one.  Zero corresponds to the last node in
				the list.


Flags on return:	Carry set if malloc error occurs (Appendm only).

Examples of Usage:

			les	di, MyList
			ldxi	NewNode
			mov	cx, 5
			Append			;Inserts after Node #5.

; The following example builds a new node on the heap from the data at
; location "RawData" and inserts this after node #5 in MyList.

			les	di, MyList
			ldxi	RawData
			mov	cx, 5
			Appendm
			jc	MallocError

Description:

Append(m) inserts a new node after a specified node in the list.  The node to
insert in front of is specified by the value in the CX register.  The first
node in the list is node #1, the second is node #2, etc.  If the value in
CX is greater than the number of nodes in the list (in particular, if CX
contains zero, which gets treated like 65,536) then Insert(m) appends the
new node to the end of the list.

Appendm allocates a new node on the heap (DX:SI points at the data fields
for the node).  If a malloc error occurs, Appendm returns the carry flag
set.

CurrentNode gets set to the newly inserted node.

Include:	stdlib.a or lists.a




Routine:  Remove
----------------

Author:		      	Randall Hyde

Category:             	List Manipulation

Registers on entry:   	ES:DI-	Pointer to list.
			CX-	# of node to delete from list.

Registers on exit:	DX:SI-	Points at node removed from list (NIL if
				no such node).

Flags on return:	Carry set if the list was empty.

Examples of Usage:

			les	di, MyList
			mov	cx, NodeNumbr
			Remove
			jc	EmptyList

Description:

Remove removes the specified node (given by CX) from the list
and returns a pointer to this node in DX:SI.  If the list was empty, Remove
returns NIL in DX:SI and sets the carry flag.

This routine modifies CurrentNode so that it points at the next item in the
list (the node normally following the current node).  If there is no such
node (i.e., CurrentNode pointed at the last node in the list upon calling
Remove) then this routine stores the value of the *previous* node into
CurrentNode.  If you use this routine to delete the last node in the list,
it sets CurrentNode to NIL before leaving.

Include:	stdlib.a or lists.a


              IBM/L 1.0
(Instruction Benchmarking Language)

This program lets you time sequences of instructions to see how much time
they *really* take to execute.  The cycle timings in most 80x86 assembly
language books are horribly inaccurate as they assume the absolute best
case.  IBM/L lets you try out some instruction sequences and see how
much time they really take.

IBM/L uses the system 1/18th second clock and measures most executions
in terms of clock ticks.  Therefore, it would be totally useless for
measure the speed of a single instruction (since all instructions execute
in *much* less than 1/18th second).  IBM/L works by repeatedly executing
a code sequence thousands (or millions) of times and measuring that amount
of time.  IBM/L automatically subtracts away the loop overhead time.

IBM/L is a very crude program, something like the "Zen Timer" (from
Michael Abrash's book "Zen of Assembly") would be more appropriate
if you need absolutely accurate timings.  The intent of this program
is to give you a good feeling for which instructions are faster than
others.

IBM/L programs begin with an optional data section. The data section begins
with a line containing "#DATA" and ends with a line containing "#ENDDATA".
All lines between these two lines are copied to an output assembly language
program inside the DSEG data segment.  Typically you would put global
variables into the program at this point.  As a general rule, you should not
use names which begin with a period.  IBM/L prefaces all its names with a
period and you could run into a conflict were you to use such names.
Note:if you are using MASM 6.0 or later, you must select the option which
allows identifiers to begin with a period.  MASM 5.1 and earlier do not
have a problem with such identifiers.

Example of a data section:

#DATA
I	dw	?
J	dw	?
K	dd	?
ch	db	?
ch2	db	?
#ENDDATA

These lines would be copied to a data segment in the created program.
Note that these names would be available to *all* code sequences you
place in the following code sections.


Following the data section are one or more code sections.  A code section
consists of optional #REPETITION and #UNRAVEL statements followed by the
actual #CODE / #ENDCODE sections.

The #REPETITION statement takes the following form:

	#REPETITION value1, value2

(The "#" must be in column one).  "value1" and "value2" must be 16-bit integer
constants (less than or equal to 65,535).

This statement instructs IBM/L to generate a loop which repeats the following
code segment (value1 * value2) times.  I used two values so I could use 16-bit
arithmetic (easy to perform in C/FLEX/BISON).  If you do not specify any
repetitions at all, the default is value1=65535 and value2=5.  Once you set
a repetitions value, that value remains in effect for all following code
sequences until you explicitly change it again.

In general, the bigger the value you choose, the more accurate the timing will
be.  However, as you choose larger and larger values for the repetitions, the
program code segments will take longer and longer to execute.  Remember,
the generated assembly language program will repeat the code seqences in a
loop the specified (value1*value2) number of times.  Short, simple, instruction
sequences will execute much faster than long, complex, instruction sequences.

If you are interested in the straight-line execution times for some
instruction(s), placing those instructions in a tight loop may dramatically
affect IBM/L's accuracy.  Don't forget, executing a control transfer instruct-
ion (necessary for a loop) flushes the pre-fetch queue and has a big effect
on execution times.  The "#UNRAVEL" statement lets you copy a block of code
several times in place (like unravelling a loop) thereby reducing the overhead
of the conditional jump instructions controlling the loop.  The "#UNRAVEL"
statement takes the following form:

	#UNRAVEL count

(The "#" must be in column one).  "count" is a 16-bit integer constant
denoting the number of times IBM/L is to repeat the code in place.

Note that the specified code sequence in the #CODE section will actually
be executed (count*value1*value2) times, since the #UNRAVEL statement
repeats the code sequence "count" times inside the loop.

In its most basic form, the #CODE section looks like the following:

	#CODE ("Title")
	%DO

		<assembly statements>

	#ENDCODE

The title can be any string you choose.  IBM/L will display this title
when printing the timing results for this code section.  IBM/L will take
the specified assembly statements and output them (multiple times if the
#UNRAVEL statement specifies) inside a loop.  At run time the generated
assembly language source file will time this code and present a count,
in ticks, for one execution of this sequence.

Example:

#unravel 16			Execute the sequence 16 times inside the loop
#repetitions 32, 30000		Do this 32*30000 times
#code ("MOV AX, 0  Instruction")
%do
		mov	ax, 0
#endcode

The above code would generate an assembly language program which executes
the MOV AX, 0 instruction 16 * 32 * 30000 times and report the amount of
time that it would take.

Most IBM/L programs have multiple code sections.  New code sections can
immediately follow the previous ones, e.g.,

#unravel 16			Execute the sequence 16 times inside the loop
#repetitions 32, 30000		Do this 32*30000 times
#code ("MOV AX, 0  Instruction")
%do
		mov	ax, 0
#endcode

#code ("XOR AX, AX Instruction")
%do
		xor	ax, ax
#ENDCODE

The above sequence would execute the MOV AX, 0 and XOR AX, AX instructions
16*32*30000 times and report the amount of time necessary to perform
these instructions.  By comparing the results you can determine which
instruction sequence is fastest.

All IBM/L programs must end with a "#END" statement.  Therefore, the
correct form of the instruction above is

#unravel 16			Execute the sequence 16 times inside the loop
#repetitions 32, 30000		Do this 32*30000 times
#code ("MOV AX, 0  Instruction")
%do
		mov	ax, 0
#endcode

#code ("XOR AX, AX Instruction")
%do
		xor	ax, ax
#ENDCODE
#END



An example of a complete IBM/L program using all of the techniques we've
seen so far is

#data
	even
i	dw	?
	db	?
j	db	?
#enddata
#unravel 16			Execute the sequence 16 times inside the loop
#repetitions 32, 30000		Do this 32*30000 times
#code ("Aligned Word MOV")
%do
		mov	ax, i
#endcode

#code ("Unaligned word MOV")
%do
		mov	ax, j
#ENDCODE
#END




There are a couple of optional sections which may appear between the
"#CODE" and the "%DO" statements.  The first of these is "%INIT" which begins
an initialization section.  IBM/L emits initialization sections before the
loop and does not count their execution time when timing the loop.  This lets
you set up important values prior to running a test which do not count
towards the timing.  E.g.,

#data
i		dd	?
#enddata

#repetitions 5,20000
#unravel 1
#code
%init
		mov	word ptr i, 0
		mov	word ptr i+2, 0
%do
		mov	cx, 200
lbl:		inc	word ptr i
		jnz	NotZero
		inc	word ptr i+2
NotZero:	loop	lbl
#endcode
#end

The code in the "%INIT" section executes only once and does not affect the
timing.


Sometimes you may want to use the "#UNRAVELS" statement to repeat a section
of code several times.  However, there may be some statements which you
only want to execute once on each loop (that is, without copying the code
several times in the loop).  The "%eachloop" section allows this.  Note that
the code executed in the "%eachloop" section is not counted in the final
timing.

Example:

#data
i		dw	?
j		dw	?
#enddata

#repetitions 2,20000
#unravel 128
#code
%init      -- The following is executed only once

		mov	i, 0
		mov	j, 0

%eachloop  -- The following is executed only 40000 times, not 128*40000 times
		
		inc	j

%do
		inc	i

#endcode
#end

In the above code, IBM/L only counts the time required to increment i.  It does
not time the instructions in the %init or %eachloop sections.

The code in the %eachloop section only executes once per loop iteration.  Even
if you use the "#unravel" statement (the "inc i" instruction above, for
example, executes 128 times per loop iteration because of #UNRAVEL).  Sometimes
you may want some sequence of instructions to execute like those in the %do
section, but not get timed.  The "%discount" section allows for this.
Here is the full form of an IBM/L source file:

#DATA
	<data declarations>
#ENDDATA

#REPETITIONS value1, value2
#UNRAVEL count
#CODE
%INIT
	<Initialization code, executed only once>
%EACHLOOP
	<Loop initialization code, executed once on each pass through the loop>
%DISCOUNT
	<Untimed statements, executed each time the %DO section executes>
%DO
	<The statements you want to time>
#ENDCODE

<additional code sections>

#END

There are several sample files which demonstrate each of these sections
included with this package.


--------------------------------

How to use IBM/L

IBM/L was created using FLEX and BISON.  As per FSF's license (indeed, going
beyond what they request) this package includes all the sources (C, ASSEMBLY,
FLEX, and BISON) for the program.  Feel free to modify it as you see fit.

To use this package you need several files.  IBML.EXE is the executable
program.  You run it as follows:

	c:> IBML filename.IBM

This reads an IBML source file (filename.IBM, above) and writes an assembly
language program to the standard output.  Normally you would use I/O
redirection to capture this program as follows:

	c:> IBML filename.IBM >filename.ASM

Once you create the assembly language source file, you can assemble and run
it.  The resulting EXE file will display the timing results.

To properly run the IBML program, you must have the "IBMLINC.A" file in the
current working directory.  This is a skeleton assembly language source file
into which IBM/L inserts your assembly source code.  Feel free to modify this
file as you see fit.  Keep in mind, however, that IBM/L expects certain
markers in the file (currently ";##") where it will insert the code.
Be careful how you deal with these existing markers if you modify the
IBMLINC.A file.

The output assembly language source file assumes the presence of the
UCR Standard Library for 80x86 Assembly Language Programmers.  In particular,
it needs the STDLIB include files (stdlib.a) and the library file (stdlib.lib).

These must be present (or in your INCLUDE/LIB environment paths) or MASM
will not be able to properly assemble the output assembly language file.

There is a batch file included in this package which demonstrates the steps
necessary to run IBM/L on a test file.Character Set Routines
----------------------

The character set routines let you deal with groups of characters as a set
rather than a string.  A set is an unordered collection of objects where
membership (presence or absence) is the only important quality.  The stdlib
set routines were designed to let you quickly check if an ASCII character is
in a set, to quickly add characters to a set or remove characters from a set.
These operations are the ones most commonly used on character sets.  The
other operations (like union, intersection, difference, etc.) are useful, but
are not as popular as the former routines.  Therefore, the data structure
has been optimized for sets to handle the membership and add/delete operations
at the slight expense of the others.

Character sets are implemented via bit vectors.  A "1" bit means that an item
is present in the set and a "0" bit means that the item is absent from the
set.  The most common implementation of a character set is to use thirty-two 
consecutive bytes, eight bytes per, giving 256 bits (one bit for each char-
acter in the character set).  While this makes certain operations (like 
assignment, union, intersection, etc.) fast and convenient, other operations
(membership, add/remove items) run much slower.  Since these are the more 
important operations, a different data structure is used to represent sets.
A faster approach is to simply use a byte value for each item in the set.  
This offers a major advantage over the thirty-two bit scheme:  for operations 
like membership it is very fast (since all you have got to do is index into 
an array and test the resulting value).  It has two drawbacks:  first, oper-
ations like set assignment, union, difference, etc., require 256 operations 
rather than thirty-two; second, it takes eight times as much memory.

The first drawback, speed, is of little consequence.  You will rarely use the
the operations so affected, so the fact that they run a little slower will be
of little consequence.  Wasting 224 bytes is a problem, however.  Especially
if you have a lot of character sets.

The approach used here is to allocate 272 bytes.  The first eight bytes con-
tain bit masks, 1, 2, 4, 8, 16, 32, 64, 128.  These masks tell you which bit
in the following 264 bytes is associated with the set.  This facilitates 
putting eight sets into 272 bytes (34 bytes per character set).  This provides
almost the speed of the 256-byte set with only a two byte overhead.  In the
stdlib.a file there is a macro that lets you define a group of character
sets:  set.  The macro is used as follows:

	set set1, set2, set3, ... , set8

You must supply between one and eight labels in the operand field.  These are
the names of the sets you want to create.  The set macro automatically 
attaches these labels to the appropriate mask bytes in the set.  The actual
bit patterns for the set begin eight bytes later (from each label).  There-
fore, the byte corresponding to chr(0) is staggered by one byte for each
set (which explains the other eight bytes needed above and beyond the 256 
required for the set).  When using the set manipulation routines, you should
always pass the address of the mask byte (i.e., the seg/offset of one of the 
labels above) to the particular set manipulation routine you are using. 
Passing the address of the structure created with the macro above will 
reference only the first set in the group.

Note that you can use the set operations for fast pattern matching appli-
cations.  The set membership operation for example, is much faster that the 
strspan routine found in the string package.  Proper use of character sets
can produce a program which runs much faster than some of the equivalent
string operations.


Note: there is a special include file in the INCLUDE directory, STDSETS.A,
which contains the bit definitions for eight commonly-used character sets:
Alpha (upper and lower case alphabetics), lower (lower case alphabetics),
upper (upper case alphabetics), digits ("0".."9"), xdigits (hexadecimal
digits: "0"-"9", 'a'-'z', and 'A'-'Z'), alphanum (upper/lower case alpha
and digits), whitespace (spaces, tabs, carriage returns, and linefeeds),
and delimiters (whitespace plus ",", ";", "<", ">", and "|").

If you want to use this standard character set in your program you must
include the STDSETS.A file in an appropriate (data) segment.  Note that
including STDLIB.A or CHARSETS.A will not give the standard sets.  You must
explicitly place an include STDSETS.A in your program to have access to
these sets.


Routine:  Createsets
--------------------

Category:             Character Set Routine

Registers on Entry:   no parameters passed

Registers on return:  ES:DI - pointer to eight sets

Flags affected:       Carry = 0 if no error. Carry = 1 if insufficient
		      memory to allocate storage for sets.

Example of Usage:
		      Createsets
		      jc      NoMemory
		      mov     word ptr SetPtr,   di
		      mov     word ptr SetPtr+2, es

Description:  Createsets allocates 272 bytes on the heap.   This is sufficient
	      room for eight character sets.  It then initializes the first
	      eight bytes of this storage with the proper mask values for
	      each set.  Location es:0[di] gets set to 1, location es:1[di]
	      gets 2, location es:2[di] gets 4, etc.  The Createsets routine
	      also initializes all of the sets to the empty set by clearing
	      all the bits to zero.

Include:              stdlib.a or charsets.a


Routine:  EmptySet
------------------

Category:             Character Set Routine

Registers on Entry:   ES:DI - pointer to first byte of desired set

Registers on return:  None

Flags affected:	      None

Example of Usage:
		      les     di,  SetPtr
		      add     di,  3          ; Point at 4th set in group.
		      Emptyset


Description:  Emptyset clears out the bits in a character set to zero
	      (thereby setting it to the empty set).  Upon entry, es:di must
	      point at the first byte of the character set you want to clear.
	      Note that this is not the address returned by Createsets.  The
	      first eight bytes of a character set structure are the
	      addresses of eight different sets.  ES:DI must point at one of
	      these bytes upon entry into Emptyset.

Include:              stdlib.a or charsets.a


Routine:  Rangeset
------------------

Category:             Character Set Routine

Registers on entry:   ES:DI (contains the address of the first byte of the set)
		      AL    (contains the lower bound of the items)
		      AH    (contains the upper bound of the items)

Registers on return:  None

Flags affected:       None

Example of Usage:
		      lea di, SetPtr
		      add di, 4
		      mov al, 'A'
		      mov ah, 'Z'
		      rangeset


Description:  This routine adds a range of values to a set with ES:DI as the
	      pointer to the set, AL as the lower bound of the set, and
	      AH as the upper bound of the set (AH has to be greater than
	      AL, otherwise, there will an error).

Include:              stdlib.a or charsets.a


Routine:  Addstr (l)
--------------------

Category:             Character Set Routine

Registers on Entry:   ES:DI- pointer to first byte of desired set
		      DX:SI- pointer to string to add to set (Addstr only)
		      CS:RET-pointer to string to add to set (Addstrl only)

Registers on Return:  None

Flags Affected:       None

Example of Usage:
		      les     di, SetPtr
		      add     di, 1           ;Point at 2nd set in group.
		      mov     dx, seg CharStr ;Pointer to string
		      lea     si, CharStr     ; chars to add to set.
		      addstr                  ;Union in these characters.
;
		      les     di, SetPtr      ;Point at first set in group.
		      addstrl
		      db      "AaBbCcDdEeFf0123456789",0
;


Description:  Addstr lets you add a group of characters to a set by
	      specifying a string containing the characters you want in
	      the set.  To Addstr you pass a pointer to a zero-terminated
	      string in dx:si.  Addstr will add (union) each character
	      from this string into the set.

	      Addstrl works the same way except you pass the string as
	      a literal string constant in the code stream rather than
	      via ES:DI.

Include:              stdlib.a or charsets.a


Routine:  Rmvstr (l)
--------------------


Category:             Character Set Routine


Registers on entry:   ES:DI contains the address of first byte of a set
		      DX:SI contains the address of string to be removed
			     from a set (Rmvstr only)
		      CS:RET pointer to string to add to set (Rmvstrl only)


Registers on return:  None


Flags affected:       None


Example of Usage:
		      les 	di, SetPtr
		      mov 	dx, seg CharStr
		      lea 	si, CharStr
		      rmvstr

		      mov 	dx, seg CharStr
		      lea 	si, CharStr
		      rmvstrl
		      db      	"ABCDEFG",0


Description:  This routine is to remove a string from a set with ES:DI
	      pointing to its first byte, and DX:SI pointing to the
	      string to be removed from the set.

	      For Rmvstrl, the string of characters to remove from the
	      set follows the call in the code stream.

Include:              stdlib.a or charsets.a


Routine:  AddChar
-----------------

Category:             Character Set Routine

Registers on Entry:   ES:DI- pointer to first byte of desired set
		      AL- character to add to the set

Registers on Return:  None

Flags affected:       None

Example of Usage:
		      les     di, SetPtr
		      add     di, 1           ;Point at 2nd set in group.
		      mov     al, Ch2Add      ;Character to add to set.
		      addchar


Description:  AddChar lets you add a single character (passed in AL)
	      to a set.

Include:              stdlib.a or charsets.a


Routine:  Rmvchar
-----------------

Category:             Character Set Routine

Registers on entry:   ES:DI (contains the address of first byte of a set)
		      AL    (contains the character to be removed)

Registers on return:  None

Flags affected:	      None

Example of Usage:
		      lea di, SetPtr
		      add di, 7		;Point at eighth set in group.
		      mov al, Ch2Rmv
		      Rmvchar

Description:  This routine removes the character in AL from a set.
	      ES:SI points to the set's mask byte. The corresponding
	      bit in the set is cleared to zero.

Include:              stdlib.a or charsets.a


Routine:  Member
----------------

Category:             Character Set Routine

Registers on entry:   ES:DI (contains the address of first byte of a set)
		      AL    (contains the character to be compared)

Registers on return:  None

Flags affected:       Zero flag (Zero = 0 if the character is in the set
				 Zero = 1 if the character is not in the set)

Example of Usage:
		      les di, SetPtr
		      add di, 1
		      mov al, 'H'
		      member
		      jne IsInSet


Description:  Member is used to find out if the character in AL is in a set
	      with ES:DI pointing to its mask byte. If the character is in
	      the set, the zero flag is set to 0. If not, the zero flag is
	      set to one.

Include:              stdlib.a or charsets.a


Routine:  CopySet
-----------------

Category:            Character Set Routine

Register on entry:   ES:DI- pointer to first byte of destination set.
		     DX:SI- pointer to first byte of source set.

Register on Return:  None

Flags affected:      None

Example of Usage:
		     les     di, SetPtr
		     add     di, 7           ;Point at 8th set in group.
		     mov     dx, seg SetPtr2 ;Point at first set in group.
		     lea     si, SetPtr2
		     copyset


Description:  CopySet copies the items from one set to another.  This is a
	      straight assignment, not a union operation.  After the
	      operation, the destination set is identical to the source set,
	      both in terms of the element present in the set and absent
	      from the set.


Include:             stdlib.a or charsets.a


Routine:  SetUnion
------------------

Category:            Character Set Routine

Register on entry:   ES:DI - pointer to first byte of destination set.
		     DX:SI - pointer to first byte of source set.

Register on return:  None

Flags affected:      None

Example of Usage:    les   di, SetPtr
		     add   di, 7              ;point at 8th set in group.
		     mov   dx, seg SetPtr2    ;point at 1st set in group.
		     lea   si, sSetPtr2
		     unionset


Description:  The SetUnion routine computes the union of two sets.
	      That is, it adds all of the items present in a source set
	      to a destination set.  This operation preserves items
	      present in the destination set before the SetUnion
	      operation.

Include:             stdlib.a or charsets.a


Routine:  SetIntersect
----------------------

Category:            Character Set Routine

Register on entry:   ES:DI - pointer to first byte of destination set.
		     DX:SI - pointer to first byte of source set.

Register on return:  None

Flags affected:      None

Example of Usage:
		     les   di, SetPtr
		     add   di, 7              ;point at 8th set in group.
		     mov   dx, seg SetPtr2    ;point at 1st set in group.
		     lea   si, SetPtr2
		     setintersect

Description:  SetIntersect computes the intersection of two sets, leaving
	      the result in the destination set.  The new set consists
	      only of those items which previously appeared in
	      both the source and destination sets.

Include:             stdlib.a or charsets.a


Routine:  SetDifference
-----------------------

Category:            Character Set Routine

Register on entry:   ES:DI - pointer to the first byte of destination set.
		     DX:SI - pointer to the first byte of the source set.

Register on return:  None

Flags affected:      None

Example of Usage:
		     les   di, SetPtr
		     add   di, 7               ;point at 8th set in group.
		     mov   dx, seg SetPtr2     ;point at 1st set in group.
		     lea   si, SetPtr2
		     setdifference


Description:  SetDifference computes the result of (ES:DI) := (ES:DI) -
	      (DX:SI).  The destination set is left with its original
	      items minus those items which are also in the source set.

Include:             stdlib.a or charsets.a


Routine:  Nextitem
------------------

Category:             Character Set Routine

Registers on entry:   ES:DI (contains the address of first byte of the set)

Registers on return:  AL (contains the first item in the set)

Flags affected:       None

Example of Usage:
		      les di, SetPtr
		      add di, 7		;Point at eighth set in group.
		      nextitem


Description:  Nextitem is the routine to search the first character (item)
	      in the set with ES:DI pointing to its mask byte. AL will
	      return the character in the set. If the set is empty, AL
	      will contain zero.

Include:              stdlib.a or charsets.a


Routine:  Rmvitem
-----------------

Category:             Character Set Routine

Registers on entry:   ES:DI (contains the address fo first byte of the set)

Registers on return:  AL (contains the first item in the set)

Flags affected:       None

Example of Usage:
		      les di, SetPtr
		      add di, 7
		      rmvitem

Description:  Rmvitem locates the first available item in the set and
	      removes it with ES:DI pointing to its mask byte. AL will
	      return the item removed. If the set is empty, AL will
	      return zero.

Include:              stdlib.a or charsets.a


Pattern Matching Routines
-------------------------

The UCR Standard Library contains a very rich set of (character string)
pattern matching routines.  These routines were designed to mimic the
pattern matching primitives found in the SNOBOL4 programming language, so
they are very powerful indeed.

These routines are actually quite simple.  They derive their power through
the use of a recursive, backtracking pattern matching algorithm and a
properly specified data structure: the "pattern".  The data type for a
pattern is the following:

Pattern		struc
MatchFunction	dd	?
MatchParm	dd	0
MatchAlternate	dd	0
NextPattern	dd	0
EndPattern	dw	?
StartPattern	dw	?
StrSeg		dw	?
Pattern		ends


The "MatchFunction" field is a pointer to a (far) procedure which tests the
current characters in the string.  You could write your own functions for
this purpose if you choose, however, several important routines are already
provided in this package.

"MatchParm" represents a four-byte value which the pattern matching algorithm
passes to the MatchFunction routine.  Typically it is a pointer to a string
or a character set though it could be any four-byte value.

"MatchAlternate" is a pointer to an alternate pattern to try if the current
pattern fails to match the string.

"NextPattern" is a pointer to another pattern in the current pattern list.
This lets you concatenate patterns to form more complex patterns.

"EndPattern", "StartPattern", and "StrSeg" are words filled in by the pattern
matching routine so other code can locate the characters matched by this
particular pattern.  In general, you should not modify these values.


The MatchFunction is where most of the work actually takes place.  Currently
the standard library provides 18 different MatchFunctions:

			Spancset
			Brkcset
			MatchStr
			MatchiStr
			MatchToStr
			MatchChar
			MatchToChar
			MatchChars
			MatchToPat
			Anycset
			NotAnycset
			EOS
			ARB
			ARBNUM
			Skip
			POS
			RPOS
			GOTOpos
		and	RGOTOpos


Note: in order to gain access to these match functions you must place the
statement:

			matchfuncs

somewhere in your program after including "pattern.a" or "stdlib.a".  This
macro defines the externals for the match functions.  Since these match
functions do not get called in the same manner as other standard library
routines, they do not get automatically linked with your programs.  There
is a comment in the "shell.asm" file which activates this macro.  Uncomment
this line and you'll be in great shape.


A brief description of the matching functions:

Spancset will match any number of characters belonging to a character set.
(This includes zero characters.)  You specify the character set via a pointer
to a UCR Stdlib CSET.  The pointer to this character set goes in the
"MatchParm" field of the above structure.

Brkcset will match any number of characters which are *not* in a character
set.  That is, it will match up to a character in the specified character
set or until the end of the string, whichever comes first.  Once again,
"MatchParm" contains a pointer to the character set to use.

MatchStr matches a specified string (something like the strcmp routine).
The "MatchParm" field points at the zero terminated string to match.

MatchiStr is like MatchStr except it converts the input string to uppercase
before comparing against the specified string (the specified string should
contain all upper case characters or it will not match).

MatchToStr matches all characters in a string up to and including the
string specified by the "MatchParm" field.

MatchChar matches a single character.  This character must appear in the
L.O. byte of the "MatchParm" field.  Note that this routine must match
exactly one character.

MatchChars matches zero or more occurrences of the same character.  Again,
the character appears in the L.O. byte of the "MatchParm" field.

MatchToChar matches all characters up to, and including, the character
specified in the L.O. byte of the "MatchParm" field.

MatchToPat matches all characters up to, and including, the characters
matched by the pattern specified in the "MatchParm" field.

Anycset matches a single character from a character set.  As usual, the
"MatchParm" field points at the character set to test.

NotAnycset matches a single character which is *not* in the specified
character set.  Once again, "MatchParm" points at the character set to use.

EOS matches the end of the string (that is, the zero terminating byte).

ARB matches an arbitrary number of characters.

ARBNUM matches an arbitrary (zero or more) number of strings matching the
pattern specified in the "MatchParm" field.

Skip matches "n" arbitrary characters.  The number of characters to skip is
specified in the L.O. word of the "MatchParm" field.

POS matches if the matching routine is currently at position "n" in the
string.  "n" is given by the L.O. word of the "MatchParm" field.

RPOS matches if the matching routine is currently at position "n" from the
end of the string.  Again, "n" is the L.O. word of the "MatchParm" field.

GOTOpos moves to the position in the string specified by the L.O. word of
the "MatchParm" field.  This routine fails if it tries to move backwards in
the string or it attempts to move beyond the end of the string.

RGOTOpos moves to the position in the string "n" chars from the end of the
string ("n" being the L.O. word of "MatchParm").  Fails if this moves you
backwards in the string.

To understand how to use these routines, a little pattern matching theory
is in order.  Pattern matching is quite similar to string comparison except
you don't need to match an exact string.  Instead, you can specify arbitrary
*patterns* of characters to match.  For example, an indentifier in a high
level language like Pascal consists of an alphabetic character following by
zero or more alphanumeric characters.  You cannot perform a single string
comparison which will accept all possible Pascal identifiers (indeed, that
single comparison would only accept a single Pascal identifier).  However,
you can easily create a pattern which will accept all Pascal ids:

	Anycset(A-Za-z) Spancset(A-Za-z0-9)

Anycset above matches a single character from the specified set (alphabetic)
and the Spancset function matches zero or more characters from the alpha-
numeric character set.  If you were to take a character string and *match*
it against the above pattern, the match would __succeed__ if the string is a
valid Pascal identifier, it would __fail__ if the string is not a valid
Pascal identifier.

Note, by the way, that the above pattern actually matches any string which
*begins* with a valid Pascal identifier.  If the match routine exhausts
the pattern before exhausting characters in the string, it considers the
match successful.  If you wanted to match *only* a Pascal identifier you
would use a pattern like the following:

	Anycset(A-Za-z) Spancset(A-Za-z0-9) EOS

The "EOS" pattern matches the end of the string, so this pattern matches Pascal
identifiers only.

Of course, you can use pattern matching to perform string comparisons using
the MatchStr function:

	MatchStr("Hello world") EOS

However, this is a frightfully expensive way to do a simple string comparison.
(Okay, it really isn't that bad, but it does take several times longer than,
say, strcmp.)

Although you wouldn't match character strings this way, using strings within
patterns is quite useful.  Consider the following which matches
		"value=<fp number>"
where "<fp number>" denotes a decimal value:

	MatchStr("value=") Spancset(0-9) MatchChar('.') Spancset(0-9)

This pattern requires that the string begin with "value=<fp number>" though
anything could follow the decimal value in the string.  If you wanted the
string to exactly match the above pattern, you could put an EOS at the end
of the pattern.

What if you wanted to test for the presence of the above pattern *anywhere*
in the string (not necessarily at the beginning)?  This is easily accomplished
by the pattern:

	ARB MatchStr("value=") Spancset(0-9) MatchChar('.') Spancset(0-9)

ARB matches an arbitrary number of characters.  At first you might think
"gee, if it matches any number of characters, what's to prevent it from
matching everything to the end of the string and causing the rest of the
pattern to fail?"  Well, simply put, the pattern matching algorithm tries
its absolute best to succeed.  So ARB will match must enough characters
so that the string "value=<fp number>" will match the rest of the pattern,
if it is present in the string.  To achieve this, the matching algorithm
usings *backtracking*.  In a nutshell, backtracking works as follows:
the current pattern matches as many characters as it can (all of them in the
case of ARB).  It then tries to match the remaining characters against the
rest of the pattern.  If that fails, then it backs up and tries again
(logically you can think of ARB giving up one character at a time from the
end of the string until the remaining patterns match).  If there is no match
after backing up back to the starting point, the whole pattern fails.
If this sounds expensive (slow), well, it is.  That's why you would never
try and use the pattern matching primitives for simple string comparisons.
That's also why you should try to avoid two adjacent patterns which match
the same set of characters (since ARB matches anything, it will match the
same characters as any adjacent pattern, hence it may be slow).

Another important feature to this pattern matching system is the ability
to do *alternation*.  Consider the following:

	MatchStr("black") | MatchStr("blue")

The "|" symbol above denotes alternation, and is read as "or".  This pattern
matches the string "black" *or* the string "blue".  Okay, now consider the
following (very typical example in pattern matching):

[MatchStr("black") | MatchStr("blue")] [MatchStr("bird") | MatchStr("berry")]

The above pattern matches the strings "blackbird", "bluebird", "blackberry",
or "blueberry".  The alternation operator lets you choose "black" or "blue"
from the first pattern and "bird" or "berry" from the second pattern.

This description of pattern matching theory could go on for quite some time,
but this is not the place for it.  This discussion is intended to serve as
only an appetizer for you.  If you want additional information on pattern
matching theory, pick up any SNOBOL4 manual, especially "Algorithms in
SNOBOL4" (by Gimpel, if I remember correctly).  Also, copies of Vanilla
SNOBOL are floating around with an electronic manual.  Among other things,
this contains the phone number and all of Catspaw which sells SNOBOL4 and
ICON products for various systems.  They sell several texts on SNOBOL4 and
ICON (which are pattern matching languages).  Since these library routines
are based on SNOBOL4, taking a look at SNOBOL4 will provide some insight
into these routines.

Now let's talk about how to actually specify a pattern in assembly language
using the pattern matching routines.  As you probably figured out already,
you don't get to use the nice (SNOBOL4-like) syntax used the preceding
examples.  Indeed, if there is any pain associated with pattern matching
in this package, it's setting up the patterns in the first place.

A pattern is a linked list of objects all of type "PATTERN" (see the
structure definition earlier).  You must fill in all but the last three
fields of this structure (or live with the default value of zero).
The Pascal identifier pattern mentioned above would look something
like the following:

pasid		pattern	<Anycset,alphabetic,0,pasid2>
pasid2  	pattern	<Spanscet,alphanum>

(note: alphabetic and alphanum are standard character sets available in
       UCR standard library.  See the section on character sets for details).

The first pattern matches a single character from the "alphabetic" character
set.  The second pattern matches zero or more characters from the "alphanum"
character set.  In both cases the alternate field is zero (NULL/NIL) because
there is no alternate to match.  In the first pattern above, the NextPattern
field contains the link to the "pasid" pattern which concatenates the two
patterns.  Pasid2 has no link field since it is the last pattern in the
list (if the field is not present, it defaults to zero/NIL/NULL).

To actually match a string against a pattern you load ES:DI with the address
of the string to test and DX:SI with the address of the first pattern in
your pattern list.  CX contains the offset of the last character you wish
to check in the string (set CX to zero if you want to match all characters
in the string). Then you execute the "match" procedure.  On return, the
carry flag denotes success (C=1) or failure (C=0), AX contains the offset
of the character in the string immediately after the match.

		lesi	MyString
		ldxi	MyPattern
		mov	cx, 0
		match
		jc	TheyMatched

By default, all patterns match characters at the beginning of the string.
As you've seen earlier in the document, you can use ARB to allow the
pattern to match at some other point in the string.  For example, the following
matches any string which contains one or more alphabetic characters followed
by one or more decimal digits:

HasAnAlpha	pattern	<ARB,0,0,HAA2>
HAA2		pattern	<Anycset,alphabetic,0,HAA3>
HAA3		pattern	<Spancset,alphabetic,0,HAA4>
HAA4		pattern	<Anycset,digits>

Since ARB does not require any parameters, you can use any value for the
second parameter to "pattern".  Zero is as good a value as any other.
Note that "Spancset" by itself would not be sufficient for the alphabetic
matches.  Spancset matches zero or more characters.  We need to match one
or more characters.  That is why the pattern above needs the Anycset and
Spancset patterns.

The above pattern data type matches its pattern *anywhere* in the target
string.  If you wanted to force a match at the end of the string, you could
use the following pattern:

HasAnAlpha	pattern	<ARB,0,0,HAA2>
HAA2		pattern	<Anycset,alphabetic,0,HAA3>
HAA3		pattern	<Spancset,alphabetic,0,HAA4>
HAA4		pattern	<Anycset,digits,0,HAA5>
HAA5		pattern	<Spancset,digits,0,HAA6>
HAA6		pattern	<EOS>

EOS doesn't require a parameter, so the above lets all the fields except the
function name default to zero.

The MatchAlternate field contains the address of a pattern to match if the
current pattern fails (and *only* if the current pattern fails).  Consider
the blue/blackbird/berry pattern described earlier.  You can easily implement
that pattern with the following statements:

BB		pattern	<MatchString,black,bluepat,BB2>
BB2		pattern	<MatchString,berry,birdpat>
bluepat		pattern	<MatchString,blue,0,BB2>
birdpat		pattern	<MatchString,bird>

black		db	"black",0
blue		db	"blue",0
bird		db	"bird",0
berry		db	"berry",0

If you match BB against the string "blackberry" BB will match the black,
then it will go to BB2 which matches the berry.  This string doesn't use
either alternate.

If you match BB against the string "blueberry" BB immediately fails, so
it tries the alternate pattern, bluepat. Bluepat matches blue and then
goes on to the BB2 pattern which matches the berry.

If you match BB against the string "blackbird", BB matches black and then
tries to match BB2 against bird.  BB2 fails and tries its alternate (birdpat)
with matches the characters "bird".

If you match BB against the string "bluebird", BB fails and tries its alter-
nate, bluepat.  Bluepat matches "blue" and passes control to its next pattern,
which is BB2.  BB2 tries to match "bird" and fails, so it passes control to
its alternate, birdpat, which matches bird.

The example above is pretty straightforward as far as alternation is concerned.
You can create some very sophisticated patterns with the alternation field.
For example, consider the following generic pattern:

	[pat1 | ] [pat2 | pat3]

"[pat1 | ]" is an easy way of saying that pat1 is optional.  You can easily
create this pattern as follows:

Pat1		pattern	<pat1func,pat1parm,pat2,pat2>
Pat2		pattern	<pat2func,pat2parm,pat3>
Pat3		pattern	<pat3func,pat3parm>

If you match a string against Pat1 and the beginning of the string does not
match Pat1, then it tries Pat2 instead (and if that fails, it tries Pat3
before failing).  If the string begins with a pattern matched by Pat1, the
matching algorithm then looks to see if the characters matching Pat1 are
followed by some character matching Pat2 or, alternately, Pat3.

There are all kinds of tricky ways you can use the alternation field to
create complex patterns and control the precendence of the pattern matching
algorithm.  This short document cannot even begin to describe the
possibilities.  You will need to experiment with this capability to discover
its true potential.


Creating Your Own Pattern Functions
-----------------------------------

Although the UCR Stdlib pattern matching routines include many of the
functions you'll typically want to use for pattern matching, it's quite
possible you'll want to write your own pattern matching functions.  This
is actually quite easy to do.  The matching functions are all far procedures
which the Match procedure calls with the following parameters:

ES:DI- Points at the first character of the character string the function
       should match against.  The match function should never look at
       characters before this string and it should not look beyond the end
       of the string (which is marked by a zero terminating byte).

DS:SI- Contains the four-byte parameter found the the MatchParm field.

CX-    Contains the last position plus one in the string you're allowed
       to compare.  Note that this may or may not point at the zero term-
       inating byte.  You must not scan beyond this character.  Generally,
       you can assume the zero terminating byte is at or after this location
       in the string.

On return, AX must contain the address (offset into the string) of the last
character matched *plus one*.  After your pattern matches, additional patterns
following in the pattern list will begin their matching at location ES:AX.
You must also return the carry set if your match succeeded, you must return
the carry clear if your match failed.

Note that the MATCH procedure is fully recursive and rentrant.  So you can
call MATCH recursively from inside your match function.  This helps make
writing your own match routines much easier.  (note: actually, you need to
call MATCH2, which is the reentrant version, from inside your match functions.)

As an example, let's consider the example above where we wanted to match
a string of one or more alphabetic characters following by one or more
digits anywhere in a string.  Consider the following pattern:

HAA		pattern	<ARB,0,0,HAA2>
HAA2		pattern	<MatchAlpha,0,0,HAA3>
HAA2		pattern	<MatchDigits>

The MatchAlpha and MatchDigits pattern functions are not provided in the
standard library, we will have to write them.  MatchAlpha matches one or
more alphabetic characters, MatchDigits matches one or more decimal digits.
Here's the routines that implement these two functions:


; Note that ES:DI & CX are already set up for these routines by the
; Match procedure.

MatchAlpha	proc	far		;Must be a far proc!
		push	dx
		push	si		;Preserve modified registers.
		ldxi	Alpha1		;Get pointer to "Match one or more
		match2			; alpha" pattern and match it.
		pop	si
		pop	dx
		ret
MatchAlpha	endp

MatchDigits	proc	far		;Must be a far proc!
		push	dx
		push	si		;Preserve modified registers.
		ldxi	Digits1		;Get pointer to "Match one or more
		match2			; digits" pattern and match it.
		pop	si
		pop	dx
		ret
MatchDigits	endp

Alpha1		pattern	<Anycset,alpha,0,Alpha2>
Alpha2		pattern	<Spancset,alpha>

Digits1		pattern	<Anycset,digits,0,Digits2>
Digits2		pattern	<Spancset,digits>


Note that the MatchAlpha and MatchDigits patterns do not require any
parameters from the MatchParm field, they intrinsically know what they
need to use.

Another way to accomplish the above is to write a generic "one or more
occurrences of a pattern" type of pattern.  The following code implements
this:

; Assume the "MatchParm" field contains a pointer to the pattern we
; want to repeat one or more times:

OneOrMore	proc	far
		push	dx
		push	di
		mov	dx, ds			;Point DX:SI at pattern.
		match2				;Make sure we get at least 1.
		jnc	Fails
MatchMore:      mov	di, ax			;Move on in string.
		match2
		jc	MatchMore
		pop	di
		pop	dx
		stc				;Return success
		ret

Fails:		pop	di
		pop	dx
		clc				;Return failure
		ret
OneOrMore	endp


A pattern which would match one or more alphabetics with this would be:

Alpha1ormore	pattern	<OneOrMore,alphaset>
AlphaSet	pattern	<Anycset,alpha>

You would specify the "Alpha1ormore" pattern to match one or more alphabetic
characters.


Of course, you can write any arbitrary function you choose for your match
function, you do not need to call MATCH2 from within your match function.
For example, a simple routine which matches one or more alphabetics followed
by one or more digits could be written as follows:

AlphaDigits	proc	far
		push	di

		cmp	di, cx
		jae	Failure
		mov	al, es:[di]
		and	al, 5fh			;Convert l.c. -> U.C.
		cmp	al, 'A'
		jb	Failure
		cmp	al, 'Z'
		ja	Failure
DoTheMore0:	inc	di
		cmp	di, cx
		jae	Failure
		mov	al, es:[di]
		and	al, 5fh
		cmp	al, 'A'
		jb	TryDigits
		cmp	al, 'Z'
		jbe	DoTheMore0

TryDigits:	mov	al, es:[di]
		xor	al, '0'			;See if in range '0'..'9'
		cmp	al, 10
		jae	Failure
DoTheMore1:	inc	di
		cmp	di, cx
		jae	Success
		mov	al, es:[di]
		xor	al, '0'
		cmp	al, 10
		jb	DoTheMore1
Success:	mov	ax, di			;Return ending posn in AX.
		pop	di
		stc				;Success!
		ret

Failure:	mov	ax, di			;Return failure position.
		pop	di
		clc				;Return failure.
		ret
AlphaDigits	endp


Note that the pattern matching function must return the failure position in
AX.  Also note that the routine must *not* search beyond the point specified
in the CX register.  These points did not appear in the previous code because
all of that was handled automatically by the MATCH2 routine.  Of course,
the matching function must set or clear the carry flag depending upon the
success of the operation.

There is a really sneaky way to simulate the use of parentheses in a pattern
to override the normal left-to-right evaluation of a pattern.  The SL_MATCH2
routine (which is what MATCH2 winds up calling) works quite well as a pattern
matching function.  Consider the following pattern:

	ParenPat	pattern	<sl_match2,HAA>

Now the ParenPat pattern will match anything the HAA pattern matches, however,
the system treats all of HAA as a single pattern (inside ParenPat) rather
than as a list of concatenated patterns.  This is real important when using
PATGRAB to extract portions of a pattern.  Patgrab can only extract the
characters belonging to a single pattern data structure (not a list).  However,
ParenPat above is a single pattern data structure which maintains the infor-
mation for the entire string matched by HAA.  Therefore, using patgrab on
ParenPat will extract the entire string matched by HAA.

Parenthetical operations can also simplify other patterns.  Keep in mind,
however, that the Alternate pointer field in the pattern structure can
also be used to simulate parenthetical patterns without the expense of
generating new patterns (see the previous examples).


A Note About Performance:

There are two aspects to efficiency programmers worry about: speed and memory
usage.  In the worst case, this pattern matching package does not fare well
in either category.  It is possible that the routines in this package could
consume several kilobytes of stack space while matching a string;  it is also
possible that the matching would take so long that it's impractical to use
this package.  Fortunately, these worst case scenerios are pathological
cases which rarely (if ever except in synthetic programs) occur.

Space is the first issue to address.  Each call to Match/Match2 can push
upwards of fifty bytes onto the stack.  This is in addition to the stack
space required by the low-level matching function.  Few of the built-in
matching functions push more than six or eight bytes, but you could write
your own matching function which pushes more.  It is very easy to design
a (synthetic) pattern which forces a nested, recursive, call of MATCH2 for
each character in the string you are going to match.  In such a case, this
package could require upwards of n*50 bytes on the stack where "n" is the
length of the string.  For a string with 100 characters, you'd probably
need 5K of stack space for the pattern matching routines alone.

In general, patterns would rarely exhibit this type of behavior.  Most low-
level pattern matching functions match several characters at once.  Further-
more, you almost never encounter patterns in real life which require a
recursive call for each character in the string.  Instead, most complex
patterns consist of simpler patterns concatenated together in a list.  This
does not require a nested recursive call for each character in the string,
rather, the package makes a call, matches some characters, then returns;  next,
the routines call the next sub-pattern in a similar fashion.  Note however,
that the state (stack space) for the previous sub-pattern has been reclaimed
from the stack at that point.

In practice, a typical pattern might require as much as 1K free stack space.
However, keep in mind that this is a "typical worst case value" not an
absolute worst case value.  Of course, you can control the amount of stack
space the pattern matching algorithms use by avoiding recursive pattern
definitions and avoiding parenthetical patterns (which stack up machine
state recursively) in complex patterns.  Of course, limiting the size of
the strings you're matching the pattern against will help as well.

Generally, the stack space used by the pattern matching algorithm is of
little concern.  Setting aside an extra kilobyte of memory, even five
kilobytes of memory, isn't a big problem for most programmers.  Speed,
on the other hand, can be a problem.  This pattern matching package uses
a generalized backtracking pattern matching algorithm.  You can easily
devise a pattern which runs in O(x**n) time where "x" is an arbitrary value
you get to pick (basically the number of possible alternations on each
sub-pattern in the pattern) and "n" is the length of the string you match.
The "O()" function notation simply means that if it takes "m" units of time
with a string of length "n", it will take "m*x" units of time for a string
of length "x+1".  Not very good.  For some patterns it could easily take
longer than your lifetime to match a string whose length is 100 characters.

Once again, we're fortunate in the sense that this terrible performance occurs
so rarely you need not be too concerned about it.  To achieve such bad per-
formance requires a specially prepared pattern matching a specially prepared
string.  Such combinations do not normally exist in nature!  However, while
100 year matching times may not occur much, most programmers are interested
in having their patterns match in milliseconds.  It is very easy to devise
a pattern which takes seconds, minutes, or possibly even hours, to match
some types of strings (second timing is very likely for some common strings
and patterns, minutes is rather rare, hours is very rare, but still much
more possible than the 100 year problem).  The really sad part is that slow
matching times is almost always due to a poor choice of pattern rather than
an intrinsic problem with the pattern matching algorithm.

Typically, all performance problems are directly related to the amount of
backtracking which must occur to match a pattern.  Backtracking almost always
occurs which you have two adjacent sub-patterns in a pattern where the
end of the first sub-pattern matches strings from the same set as the front
of the second sub-pattern.  Consider the following pattern:

		 spancset(a-z " " ",") matchstr("hello")

If you match this pattern against the string "hi there, hello" the first
spancset pattern matches the entire string.  The matchstr function would then
fail.  At this point, backtracking kicks in and backs up one character in the
string.  To the spancset function matches "hi there, hell" and the matchstr
function fails on "o".  So the algorithm backs up one more character and
tries again.  It keeps doing this until it backs up all the way to the
point where spancset matches "hi there, " and matchstr finally matches
"hello".  To get around this problem, a better choice of pattern is probably
in order.  Consider the following which generally does the same thing:

		matchtostr("hello")

Matchtostr skips over all characters in a string up to the string "hello"
and leaves the "match cursor" pointing just beyond the "o" in "hello".
This matches almost what the previous pattern will match but it does it a
whole lot faster.  It doesn't exactly match the previous pattern because
matchtostr matches any characters, not just (a-z " " ",") but for most
purposes this is exactly what you want anyway.

ARB is probably one of the worst offenders.  Anytime you use ARB (with
a sub-pattern following it in the pattern list), you are almost guaranteed
that backtracking will occur.  Therefore, you should attempt to avoid the
use of ARB in patterns where performance is a consideration.

In general, you can often redesign a pattern data structure to avoid
overlaps in adjacent sub-patterns.  Patterns which do not have such conflicts
will generally have reasonable performance.  Of course, designing such patterns
takes more effort and testing;  so it may not be worth the effort for quick
and dirty projects.  On the other hand, if you execute the pattern match more
than a few times (or the pattern matching starts to take minutes rather than
seconds) its probably worthwhile to redo the pattern.

Of course, this pattern matching package is not suitable for all pattern
matching tasks.  The whole purpose of this package was to make pattern
matching in assembly language easy, not fast.  You would never, for example,
want to write a lexical analyzer for a compiler using this package.  It
would be too slow and using languages like LEX and FLEX produce faster
(much faster) lexical analyzers and it's easier to create them with LEX/FLEX
as well.  Ditto for parsers using BISON or YACC.  Likewise, there are many
times when pattern matching languages like AWK, ICON, SNOBOL4, etc., are
more appropriate than using the routines in this package.  This package is
really intended for small pattern matching tasks inside a larger assembly
language program.  For example, parsing command line parameters or
parsing input lines typed by the user to request some activity in your
assembly language program.  While it's certainly possible to write a program
whose sole purpose is to perform some pattern matching problem, using this
package may not provide any better performance than, say, a SPITBOL (compiled
SNOBOL4) program and it would probably take you longer to write than the
comparable SNOBOL4 program.

Routine:  MATCH
---------------

Category:             Pattern Matching Routine
Author:		      Randall Hyde

Registers on Entry:   ES:DI - pointer to source string
		      DX:SI - pointer to pattern
		      CX-     offset of last valid position+1 in string.
			      Zero to match entire string.


Registers on return:  AX-     Position in string where the pattern stopped
			      matching.


Flags affected:	      carry-	0 denotes failure to match pattern.
				1 denotes success.


Example of Usage:

		lesi	StringToTest
		ldxi	PatternToMatch
		mov	cx, 0		;Match entire string.
		MATCH
		jnc	DidNotMatch

Description:

MATCH is the general purpose matching subroutine provided in the standard
library to perform pattern matching.  On entry, DX:SI must point at a
pattern (list) data structure.  See the pattern.a include file (and the
documentation preceeding this page) for more details on this data structure.

Also on entry, ES:DI should point at the first character where the pattern
matching is to begin.  This need not be the beginning of a string, ES:DI could
point into the middle of a string;  however, the pattern matching begins at
location ES:DI.

ES:CX, on entry, must point at the last byte to check *plus one*.  This
typically points at the zero terminating byte of a string, but it could
point at some character in the string before the zero terminating byte.
If CX contains zero upon entry to the MATCH routine, the MATCH code will
automatically point CX at the zero byte in the string pointed at by ES:DI.

Include:              stdlib.a or pattern.a

Routine:  MATCH2
----------------

Category:             Pattern Matching Routine
Author:		      Randall Hyde

Registers on Entry:   ES:DI - pointer to source string
		      DX:SI - pointer to pattern
		      CX-     offset of last valid position+1 in string.


Registers on return:  AX-     Position in string where the pattern stopped
			      matching.


Flags affected:	      carry-	0 denotes failure to match pattern.
				1 denotes success.


Example of Usage:
			;Typical usage in a matching function:

				ldxi	NewPattern
				MATCH2

Description:

MATCH2 is a special, reentrant, version of MATCH.  You would normally *not*
call this routine to perform pattern matching from your main program.
Instead, this routine is intended for use inside pattern matching functions
you write yourself.  Please see the accompanying documentation for more
details.

Include:              stdlib.a or pattern.a

Routine:  patgrab
-----------------

Category:             Pattern Matching Routine
Author:		      Randall Hyde

Registers on Entry:   ES:DI - pointer to a pattern structure

Registers on return:  ES:DI - String (on heap) corresponding to the chars
			      matched by the pattern.

Flags affected:	      carry-  set if insufficient space on heap to allocate
			      the string.

Example of Usage:

		lesi	SomeString
		ldxi	SomePattern
		Match
		lesi	SomePattern
		patgrab

Description:

You use patgrab to extract the substring matched by some particular pattern.
You always call this routine *after* calling MATCH.  Match stores pointer
information away in the pattern data structure, patgrab extracts this infor-
mation and builds a string to your specifications.

To grab a string which spans several sub-patterns, you can use strcat to
combine the strings or a parenthetical pattern (see the documentation
preceding these routine descriptions for details).

Include:              stdlib.a or pattern.a

Routine:  spancset
------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, spancset is only invoked in a pattern data structure.
		You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	SCexample	pattern	<spancset,alpha>

Description:

Spancset will skip over zero or more characters from the character set (cset)
specified by the "matchparm" field (the second operand above).  This routine
always succeeds and returns the "match cursor" pointing at the first character
position beyond the matched characters in the source string.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).


Routine:  brkcset
-----------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, brkcset is only invoked in a pattern data structure.
		You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	BCexample	pattern	<brkcset,alpha>

Description:

Brkcset skips over all characters which are *not* in the character set passed
in the "MatchParm" parameter.  This routine always succeeds.  It stops with
the match cursor pointing at the first character found in the specified char-
acter set (it does not "eat" that character).  This routine always succeeds.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).


Routine:  matchstr
------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, matchstr is only invoked in a pattern data structure.
		You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	MSexample	pattern	<matchstr,str2match>
	Str2Match	db	"String to match",0

Description:

Matchstr compares the next characters in the source string against the
string pointed at by the "MatchParm" parameter.  If the next set of char-
acters in the source string match, this routine succeeds and returns the
match cursor pointing one character beyond the matched string in the
source string.  If the characters do not match, this routine fails and
does not modify the match cursor.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  matchtostr
--------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, matchtostr is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	MTSexample	pattern	<matchtostr,str2match>
	Str2Match	db	"String to match",0

Description:

MatchToStr matches all characters in a string up to *and including* the
string specified by the "MatchParm" parameter.  Note that this is a very
fast (comparatively) routine and is much faster than something like
ARB followed by MatchStr (or some other combination which will force
backtracking).  This routine fails if it cannot find the specified string
in the source string (beyond the match cursor position).

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  matchchar
-------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, matchchar is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	MCexample	pattern	<matchchar, 'a'>

Description:

Matchchar tests a single character at the current match cursor position.
If the character in the L.O. byte of "MatchParm" is equal to the current
character in the source string, this routine passes over that character
in the string and returns success.  Otherwise, it does not advance the
match cursor and returns failure.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  matchtochar
---------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, matchtochar is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	MTCexample	pattern	<matchtochar, 'a'>

Description:

MatchToChar matches all characters up to *and including* the character
specified by the L.O. byte of "MatchParm".  It succeeds if it finds the
character in the string (in which case it returns the match cursor pointing
just beyond the specified character).  It fails otherwise.

This is a relatively fast matching routine and should be used in place of
something like ARB followed by MATCHCHAR.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  matchchars
--------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, matchchars is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	MCsexample	pattern	<matchchars, 'a'>

Description:

This routine matches zero or more occurrences of the specified character
starting at the match cursor position.  It always returns success.  If it
matches one or more characters it leaves the match cursor pointing beyond
the last matched character.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  matchtopat
--------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, matchtopat is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	MTPexample	pattern	<matchtopat, somepat>
	SomePat		pattern	<arbitrary_pattern....>

Description:

Matchtopat matches an arbitrary number of characters up to *and including*
the characters matched by the pattern specified by the "MatchParm" parameter.
Success or failure on return depends entirely on whether or not the pattern
specified as a parameter matches at some point or another.

MatchToPat uses "shy" pattern matching.  That is, it first attempts to match
zero characters (the empty string) followed by the parameter pattern.  If
this succeeds, it quits.  Otherwise MatchToPat matches a single character
and tries to match the parameter pattern again.  Each time it fails it matches
one additional character and tries again until there are no more characters
in the source string, at which point it fails.

SNOBOL4+ programmers- MatchToPat is the pattern which is most comparble to
the ARB PAT pattern in SNOBOL4+.

Whether or not MatchToPat is faster than ARB (stdlib version) followed by
some other pattern depends entirely on the location of the second pattern.
ARB uses a greedy algorithm and backtracks to match any following patterns.
MatchToPat uses a shy algorithm which tries the parameter pattern first and
eats characters from the source string only if the parameter pattern fails.
If the match generally occurs earlier in the source string, MatchToPat will
be faster.  If the match occurs later in the source string, ARB/PAT will
probably be faster.  If matching generally fails, MatchToPat is marginally
faster.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  anycset
-----------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, anycset is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	ACexample	pattern	<anycset, alpha>

Description:

Anycset matches a single character from the character set (cset) specified
by the "MatchParm" parameter.  If the character at the match cursor position
is in this set, anycset advances the match cursor and succeeds.  Otherwise
it does not advance the match cursor and anycset fails.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  notanycset
--------------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, notanycset is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	NACexample	pattern	<notanycset, alpha>

Description:

NotAnycset matches a single character which is *not* in character set (cset)
specified by the "MatchParm" parameter.  If the character at the match cursor
position is not in this set, notanycset advances the match cursor and succeeds.
Otherwise it does not advance the match cursor and notanycset fails.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  EOS
-------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, EOS is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	EOSexample	pattern	<EOS>

Description:

EOS matches the end of the source string (that is, the zero terminating byte
of the source string).  The standard library pattern matching package does
not require that a source string completely match a pattern for that pattern
to succeed.  Instead, the pattern need only specify a prefix of that string.
If you want the pattern to match the entire string you must stick the EOS
pattern at the end of your pattern list.

Whether EOS succeeds or fails, it does *not* advance the match cursor.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  ARB
-------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, ARB is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	ARBexample	pattern	<ARB>

Description:

ARB matches an arbitrary number of characters in a string (up to EOS).
It always succeeds.

SNOBOL4+ users- Stdlib's ARB function isn't exactly like SNOBOL4's.  This
ARB function uses a "greedy" algorithm.  It immediately grabs as many char-
acters as it can.  If there is a pattern following ARB (and there generally
is) backtracking *will* occur.  If you want an ARB operation which uses a
"shy" matching algorithm, take a look at the "MatchToPat" function.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  ARBNUM
----------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, ARBNUM is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	ANexample	pattern	<ARBNUM, SomePattern>
	SomePattern	pattern	<some_other_pattern....>

Description:

ARBNUM matches zero or more occurrences of the pattern specified by the
"MatchParm" pattern.  It always succeeds and leaves the match cursor pointing
beyond the last character matched in the source string.  If it matches zero
occurrences of the specified pattern, it still succeeds and returns the
match cursor unchanged.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  Skip
--------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, Skip is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	Sexample	pattern	<Skip, 5, 0, NextPat>

Description:

Skip matches (skips over) the next "n" characters in the source string.
"n" is the L.O. word of the "MatchParm" parameter.

Skip succeeds if there were at least "n" characters in the string.  It fails
if there were less than "n" characters in the string.  If you want Skip to
succeed even if there are less than "n" characters in the string you can
use ARB as the alternate pattern for Skip:

	SARBexample	pattern	<Skip, 5, 0, ArbPat>
	ARBPat		pattern	<ARB>

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  POS
-------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, POS is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	Pexample	pattern	<POS, 5, 0, NextPat>

Description:

POS (position) succeeds if the match cursor is currently at the location
specified by the "MatchParm" parameter.  It fails otherwise.  Note: the
first character in the source string is at position zero.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  RPOS
--------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, RPOS is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	RPexample	pattern	<RPOS, 5, 0, NextPat>

Description:

RPOS (position) succeeds if the match cursor is currently at the specified
location *from the end of the string*.  The last character in the string is
RPOS one (EOS is at position zero).

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  GotoPOS
-----------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, GotoPOS is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	GPexample	pattern	<GotoPOS, 5, 0, NextPat>

Description:

GotoPos moves the match cursor *forward* to the specified position.  It fails
if that position does not exist in the string or if you attempt to move the
match cursor backwards in the string.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

Routine:  RGotoPOS
-----------------

Category:             Pattern Matching Primitive
Author:		      Randall Hyde

Registers on Entry:   N/A
Registers on return:  N/A
Flags affected:	      N/A

Example of Usage:

	(Note: 	Generally, RGotoPOS is only invoked in a pattern data struct-
		ure.  You would not normally call this code directly from your
		program [though it is possible, see the source listings for
		details].)

	RGPexample	pattern	<RGotoPOS, 5, 0, NextPat>

Description:

RGotoPos moves the match cursor *forward* in the string to the point specified
by the "MatchParm" parameter.  This value is the position in the string from
the *end* of the string.  This function fails if you attempt to move the
match cursor backwards in the string or if the position does not exist in
the string.

Include:	stdlib.a or patterns.a (and then invoke the "matchfuncs"
		macro to obtain the external declaration for this function).

