home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CP/M
/
CPM_CDROM.iso
/
cpm
/
utils
/
asmutl
/
zsm23.lbr
/
LIB.DZC
/
LIB.DOC
Wrap
Text File
|
1991-08-28
|
39KB
|
1,074 lines
This library uses the parameter passing mecanism of the standard c library:
in a procedure call of the form
aa = _proc(bb, cc);
the assembler code would be
; evaluate cc to hl (or de or bc)
push hl
; evaluate bb to hl
push hl
call _proc
pop de
pop de ; restore stack
;
; aa is returned in hl
Through any of these procedure calls, bc, ix & iy are guaranteed, de can
be destroyed, and the returned value comes back in hl, otherwise hl will
be destroyed.
Strings are passed as pointers to arrays of characters, so to set a string
on the stack, a typical sequence might be:
ld hl,str
.dseg
str: db 'This is a string\0' NOTE the 0 byte to end it
.cseg
push hl
The parameter passed is the address of the string, and strings are always
terminated by a zero byte.
As shown above, when there are multiple arguments, they are pushed in reverse
order: i.e. the last (rightmost) argument is pushed first, then the next
going left, and so on till the leftmost argument has been pushed. Note also
that these words pushed on the stack are sometimes destroyed, so in
a case such as:
ld hl,(_value)
push hl
call _something
pop de
de cannot be guaranteed to contain (_value) after it has been popped.
_open: _open("name", fcb)
Open a file for block based (i.e. 128 byte chunk) I/O. The first parameter
is the name of the file, the second is the address of a 36 byte area of
memory to use to hold the fcb. If the call succedes, the fcb address is
returned, otherwise -1 is returned. This call will fail if the name is not
a valid CP/M filename, or if the file does not exist.
_creat: _create("name", fcb)
Create is similar to open, except that if the file exists it is truncated
to zero length. as open, it returns the fcb, or -1 for an error. Errors
are: bad name, no room in directory, or file exists and is read only.
_close: _close(fcb)
After a file has been written or modified, this call closes the file.
_read: _read(fcb, buffer, nbl)
Read takes and fcb, the address of a buffer, and a count of blocks to read,
and reads the number specified into the buffer. It returns the number of
blocks (128 bytes per block) read, which is usually the same as the number
specified, except at end of file. For example if a file holds seven blocks,
_read(fcb, buffer, 4) returns 4, reading 4 blocks, the next
_read(fcb, buffer, 4) only returns 3, and all subsequent reads will return
zero.
_write: _write(fcb, buffer, nbl)
Just like read, except that it writes to the file. Consider it an error
(usually disk full) if write returns less than the number specified.
_tell: _tell(fcb)
Tell returns the number of the next block to be written or read in the fcb
given. Note that files bigger than 65536 records will cause trouble, as
the largest value that can be returned is a 16 bit integer.
_seek: _seek(fcb, offset, whence)
Move the read / write position around in the file. This permits random
access to the file, by specifying that reads and writes happen in
different places. offset is a number of blocks which may be negative.
whence specifies how the offset is to be applied: if 0 the offset is from
the beginning of the file, and should be greater than or equal to zero,
if 1 it is relative to the current position, and can take any value, and
if 2, offset is worked relative to the end of the file. To give some
examples: _seek(fcb, 0, 0) rewinds the file to the start, seek(fcb, 15, 0)
will access block 15 (blocks are numbered from zero), seek(fcb, -1, 1)
steps back a block, causing the most recently processed block to be
used again, seek(fcb, 0, 2) moves to the end of the file.
_printf
_sprintf
_fprintf
_xprintf:
These four form a family of routines used for formatted output. Each one
takes a format string and a list of arguments to be output with the
format, but they all output in different ways. They all scan the format
string, and copy characters one for one, until a '%' is encountered. The '%'
means that an argument is to be converted, and a letter is used to specify
the type of conversion. %c simply outputs the argument as a character, %s
assumes that the argument is the address of a string, and outputs the
string, %d prints the argument as a signed decimal number, %u prints it
as an unsigned decimal number, %x prints as a hexadecimal number, %o
prints in octal, and %b prints in binary. In order to print a '%'
character, use '%%' in the format string: this prints a '%', but does not
use an argument in the way that (say) %d does. In addition, by specifying a
number between the '%' and the format specifier, a minimum field width
can be given: the output will be padded so that it's size is at least
as big as given: so %5d would convert 100 to " 100". Note that if the
field width is too small it is not an error, output continues till everything
has been printed, so %2d with 1234 would print "1234". In addition if the
width specifier has a leading zero (e.g. %06x) numeric output is zero padded
instead of being space filled: %04x with 1024 (400 hex) would print
"0400", whereas %4x would just print " 400". Field width is always
ignored for character conversion - %c outputs exactly one character.
Field width can be supplied dynamically: if the width is given as a '*'
then an argument is consumed to provide the width. Note that argument
conversion procedes from left to right, and in the case of a '*' width
specifier, the width is taken first. Note that a '0' can still be
given before a '*' to create zero fill: %0*o with 10 and 2048 will
print "0000004000", and the 10 should be on the left of the 2048.
To provide a full example:
_printf("%d hello %% >%6s< %04x ]%*u[", 42, "xyz", 100, 6, -1)
would output
"42 hello % > xyz< 0064 ] 65535["
and return 33. All four routines return the number of characters output.
As shown above, _printf expects to have all the arguments pushed, and the
format as the last thing. The other three expect an additional parameter
prior to the format: _fprintf expects a file pointer as returned by _fopen
(q.v.), _sprintf expects that address of a buffer: instead of outputting
the data, it is placed in the buffer, and a zero byte is added at the end
to terminate the string, and _xprintf expects to receive the address of a
function to call to deal with generated characters. What will happen is
that _xprintf will push a word containing the character, and then call the
supplied function. This function MUST adhere to the standard calling
convention (preserve bc, ix, iy), and the word pushed is cleaned up
by _xprintf. So a _fprintf call might be:
_fprintf(fp, "Error: %s", string)
where fp is the file pointer. _fprintf, _sprintf and _xprintf all expect their
respective additional parameters to be first (i.e. pushed last). Note
also that none of these add a trailing newline: if such is desired it must
be included in the format string, and it is acceptable to output several
lines with one _printf call by having several newlines in the format string.
_puts: _puts(string)
_fputs: _fputs(string, fp)
These two are used to simply print a string, no conversion of any kind
is done, they just send out the characters till a zero byte is found.
_puts sends to the screen, whereas _fputs sends to the file specified
with fp - which was obtained from _fopen (q.v.)
_gets: _gets(buffer)
_fgets: _fgets(buffer, fp)
These two read a line from the keyboard (_gets) or a file (_fgets).
In both cases the end of line (<cr> for the keyboard, or <cr><lf> for
file input) is not part of the line, so if the line above were input,
the last character would be the 'r' of the second word 'for'. The strings
are returned with a zero byte to mark the end, and both routines return
the number of characters in the buffer (i.e. an empty lines gives zero).
As a special case, _fgets returns -1 to signify end of file.
_fwrite: n2 = _fwrite(fp, buff, n1);
_fwrite & _fread (see below) provide raw file access something like
_write and _read, except that they use file pointers as provided by
_fopen (q.v.). Note that these routines use repeated calls to _putc &
_getc (qq.v.) to interface to the file, so to prevent <cr><lf>
compression/expansion, the file should be open in object mode. In the
above example, fp is a file pointer as returned by _fopen, buff is the
address of the buffer used for the data transfer, and n1 is the number
of bytes to be transferred, n2 is the number of bytes actually transferred,
consider it an error if n2 is not equal to n1.
_fread: n2 = _fread(fp, buff, n1);
read n1 bytes to buff from file accessed through fp, n2 will differ from
n1 on an end of file condition, and after that successive calls will
return n2 as zero.
_fopen: fp = fopen(name, mode, buffer);
In this example, name is a standard CP/M name, presented as a string
with a zero byte to end it, mode is the mode in which the file is
opened: "r" for read, "w" for write, "a" for append. These modes are
passed as strings. Because _getc and _putc usually expects to work
on text files, they usually do <cr><lf> compression/expansion. To
prevent this, for reading an executable file byte by byte, prefix the
mode by an 'o' char: "or", "ow", "oa". _fopen expects to be handed a
buffer which is used for all the I/O for the file in question - the
address of this buffer is the third parameter. The buffer should be
fbsize bytes long, as defined in STDHDR.I. A typical calling sequence
might be:
; assume the name pointer is already in hl
ld de,buff ; get buffer address
.useg
buff: ds fbsize
.cseg
push de ; & save it
ld de,mode
.dseg
mode: db 'r\0'
.cseg
push de ; save the mode
push hl ; and the filename
call _fopen
pop de
pop de
pop de ; restore the stack
ld a,h
or l ; check for NULL in hl
jr z,error ; error if so
If _fopen goes ok, the third arg is returned UNLESS the name is the name
of a logical CP/M device (CON: RDR: etc.), in which case a special fp is
returned. _putc, _getc, _fprintf etc. etc. treat these special fp's
just like normal file fp's, so there is nothing special that needs to
be done to use them. If there was an error, hl is returned with zero
in it. There is a special CP/M device NUL: if it is opened for input
then it returns continuous EOF's, and when opened for output, whatever
it is handed vanishes into a black hole. The full list of CP/M devices
is: "CON:", "r" - read from keyboard, "CON:", "w" - write to screen,
"RDR:", "r" - read from modem port, "PUN:", "w" - write to modem port,
"LST:", "w" - write to printer port, plus "NUL:" for both read and
write as specified above. In addition, the file BDOS.I holds .var
variable definitions for these special devices: stdin is standard
input, stdout is standard output. stdrdr, stdpun, stdlst, are the
RDR:, PUN:, and LST: ports respectively; stderr is the screen, and
stdkbd is the keyboard. stdin defaults to the same as stdkbd, and stdout
defaults to the same as stderr, except that it is possible to "redirect"
input or output, by using RARX.O or PARX.O. If RARX.O is used and the
program is invoked A>PROG <FILE then stdkbd would still read from the
keyboard, but stdin would now read from file FILE. See ARX.DOC for a
more complete description of redirection.
_fncheck: fp = _fncheck(name, extlist, buffer)
This routine opens a file, checking for various single letter extensions
on the filename. Name is a filename, except that if it has an extension
(following the '.' in the filename) it must consist of exactly one letter.
Extlist is a list of extensions that the file can legally have, and buffer
is a fp buffer as given to _fopen. The action of _fncheck depends on
whether name has an extension. If it does, it verified against extlist.
If the extension character is not in the list, hl returns zero. If the
extension is in extlist, then _fncheck attempts to _fopen name for
read, and returns a vaild fp or zero as appropriate. If name has no
extension, _fncheck repeatedly tries to find a file by using the
characters in extlist one at a time until it finds a file, or it runs
out of characters. To give some examples:
_fncheck("file.q", "az", buffer) will fail: q not in "az"
_fncheck("file.a", "az", buffer) will try to open file.a and
return a value as appropriate.
_fncheck("file", "az", buffer) will first try file.a, if that
fails it wil then try file.z. If either succede than
a valid fp is returned, otherwise zero.
_fclose: _fclose(fp)
After _fopen or _fncheck have opened a file, _fclose will close it,
flushing any pending writes, and using the BDOS file close function to
finish up everything. This is not needed for files that were open for
read, but it does no harm in such a case, however it MUST be called for
files that were opened for write or append.
_rename: status = _rename(oldname, newname)
Rename a file: this changes oldname to newname: both names are passed as
strings, complete with zero bytes on the end. This invokes the BDOS rename
function on the specified file. status (i.e. hl) contains zero if all went
well, or -1 if there was an error. Note that a drive specifier (leading "B:")
can be given with either or both of the names, but if two different
drives are given it is considered an error. If no drive specifier is
provided, then the default drive is used.
_unlink: status = _unlink(name)
Erases name from the disk. status returns 0 if it succeded, or -1
on an error (file not found, or file R/O).
_putchar: _putchar(ch);
_putc: _putc(ch, fp);
These two do character output. _putchar(ch) sends ch to stdout, note
that '\n' (linefeed) maps to <cr><lf> , so it is not necessary to _putchar
an explicit '\r' (carriage return). _putc(ch, fp) sends ch to file fp:
fp is returned from _fopen. Both of these return the output character
in hl, except that if _putc is writing to a file, and the write fails
(usually because of disk full, or a file open for read), then it will
return EOF, or -1 to signify the error. _putchar usually writes to
the screen unless output redirection has occurred - see fopen above
and ARX.DOC for an explanation of redirection.
_kbhit: test = _kbhit();
This checks if there is a character waiting at the keyboard, and returns
true if there is keyboard input waiting to be processed.
_getchar: ch = _getchar();
_getc: ch = _getc(fp);
_getchar() returns the next character from stdin, which will respect
redirection, and _getc(fp) gets the next character from the file
referenced by fp. As does _putc, _getc knows about stdrdr etc. etc.
_ungetc: ch = ungetc(ch, fp)
This "pushes" a character back onto an input stream: the next call to
_getc for the fp given returns ch, just as if it were a character in
the file. This is guaranteed only for one char push back, and only if
at least one character has been read. It returns the pushed back character,
or EOF on an error. fp must be a read filepointer returned by fopen, or one
of the stdin / stdkbd read filepointers.
_stty: _stty(fp, mode);
_stty changes the "mode" of CP/M devices and files:
for all CP/M "std" devices except stdkbd (keyboard input), mode is
0 => do <cr><lf> compression / expansion, or
1 => don't.
stdkbd allows four modes:
0 => normal buffered input (use BDOS call for buffered input)
1 => unbuffered input with <cr> mapped to <lf>
2 => unbuffered input without <cr> mapping
3 => unbuffered input without echo
for files:
0 => change to text mode (i.e. fopen (name, "r" / "w" / "a", ...)
1 => change to object mode (i.e. fopen (name, "or" / "ow" / "oa", ...)
Note for files, text mode means that 0x1a (control Z) flags an end of file,
and that <cr><lf> pairs come in as just a single <lf> (newline). Object
mode means read the file as is, i.e. no compression of <cr><lf> pairs, and
EOF is only flagged when the physical end of file is hit (i.e. last character
in the last block).
_scnwld: count = _scnwld(name, function)
This processes a CP/M wildcard file specifications: _scnwld takes the
wildcard name (a la B:*.* or C:ABC*.?Q?), and calls function once for each
file that matches, returning the count of calls that it made. function
should expect to receive the filename as a string pushed immediately
before the call, and must rspect the usual calling convention (i.e. preserve
bc, ix, iy).
_pfile: name = _pfile(fcb);
This decodes the filename from a CP/M fcb as might be given back by _open,
(or by directly creating a CP/M fcb), or a fp given by _fopen. name will
be a pointer to a string that contains the decoded name, given that fcb
points to either a CP/M fcb (i.e. what open returns), or to a fp. The string
is returned in a buffer that is valid to the next call of _pfile, at which
time it will be overwritten. Note that several other routines use this same
buffer: notably _itoa and _scrypt (in xlib.l). Because of this, the best
thing to do is to use the buffer immediately: either print it (_puts or
_printf), or use _strcpy (q.v.) to make a copy in a local buffer.
_stat: mode = stat(file, mask, bits);
Changes the R/O and SYS bits of the file, whose name is passed as a string
with a zero byte at the end; and returns the new value. The R/O bit is
bit 0, and the SYS bit is bit 1. To change either of these bits, set the
corresponding bit in mask, and provide the desired value in bits:
i.e. mask == 1, bits == 1 would set the file R/O, without altering the SYS
bit, and would return the new bit pair. In particular, this means it is
possible to enquire the current value by just setting mask to zero.
_swapin: size = _swapin(file, address)
This loads the file given into memory at the specified address, returning
the size in bytes, or -1 on an error - file not found or bad filename. If
the filename has no extension then swapin wil automatically add a .OVR to it.
_upper: _upper(str)
_lower: _lower(str)
These expect the address of a zero byte terminated string, and on return
the string will be completely in upper or lower case, as appropriate.
_inchr: offset = _inchr(str, ch)
This finds the offset into a string of a character: it returns the position
of first occurence of ch in str, or -1 if no match: so _inchr("hello", 'l')
would return 2 (bytes are numbered from zero), and _inchr("hello", 'q') would
return -1.
_instr: offset = _instr(str, sub)
This is just like _inchr, except that it looks for a substring rather than
a single character, returns count to first char of substring, or -1 on no
match: _instr("hello", "lo") returns 3 (bytes numbered from zero),
whereas _instr("hello", "elo") returns -1.
_rinchr: offset = _rinchr(str, ch)
_rinstr: offset = _rinstr(str, sub)
These two are just like _inchr and _instr above, except that instead of
giving the offset to the first occurance, they give the offset to the
last, so for example _rinchr("hello", 'l') returns 3. Note that they still
return -1 if no match is to be found.
_index: addr = _index(str, ch)
_insdex: addr = _insdex(str, sub)
_rindex: addr = _rindex(str, ch)
_rinsdex: addr = _rinsdex(str, sub)
These four match the above four one to one (_inchr and _index, _instr and
_insdex, etc.) except that instead of returning the offset into the
string, they return the actual address of the first or last occurance, as
appropriate. Note also that in a no match situation, they return zero rather
than -1.
_tinstr: offset = _tinstr(str, sub)
This finds a substring in a string as a token: it is similar to _instr,
but it also inspects the context of any substring it finds: if either
end of the substring is an alphanumeric (first or last char) and the char
in the main string that extends just beyond that char is also alphanumeric
then the substring is rejected:
_tinstr("...push.de", "pu")
would return -1: there is an 's' after the 'u' in the main string, whereas:
_tinstr("...push.de", "de")
would work: _tinstr knows about the ends of the main string.
_nconv: _nconv(str)
This converts a string with alphabetic words to "name" format. What it does is
to scan for groups of alphabetic characters in either upper or lower case,
surrounded by non alphabetic characters. What it then does is to force the
first letter in each of these groups to be upper case, while converting the
rest to lower case: so "HeLLo woRLd" would become "Hello World".
_prefix: test = _prefix(str, sub);
_suffix: test = _suffix(str, sub);
This checks to see if sub is a prefix / suffix of str, and returns true
if so, false if not. Hence _prefix("Hello World", "Hel") returns true,
whereas prefix("Hello World", "xyz") returns false.
_chrcnt: count = _chrcnt(str, ch)
_chrcnt returns a count of how many times character ch occurs in string str:
_chrcnt("Hello World", 'l') returns 3: there are three 'l's in the string.
_chrcat: _chrcat(str, ch)
This takes character ch, and appends it to str: so if str contained "hel"
on entry, and ch was 'p', str on exit would be "help", complete with a new
trailing zero byte. _chrcat assumes that the string pointer provided
addresses a buffer with sufficient space: it does no error checking at all.
_detab: _detab(dest, src)
_entab: _entab(dest, src)
_detab copies from string src, making a new string at dest, but as it copies
it converts tabs to the correct number of spaces. It assumes that tab stops
are every eight characters. _entab reverses this operation: it converts
groups of spaces to tabs wherever possible. Note that entab can be given the
same string as both source and destination, but if this is tried with _detab
it may cause a program crash.
_strip: _strip(str)
_strip simply removes any trailing white space from str: it does this
by placing a new zero byte after the last non-white space character in str.
_trim: _trim(str, n)
_trim checks the length of string str, and if it is greater than n then
a zero byte is inserted to make its length equal to n. If its length is
less than n then no action is taken.
_byp: dest = _byp(str)
_unbyp: dest = _unbyp(str)
_byp returns the address of the first non-white space character in its
argument string, effectively bypassing leading white space. _unbyp is
similar, except that it returns the address of the first white space
character: instead of stepping over white space, it steps over everything
but white space.
_strcmp: val = _strcmp(s, t)
_strcmp returns a value telling whether s is greater than, equal to, or
less than t, i.e. it returns a number greater than zero if s comes after
t in a dictionary, 0 if they are the same, and a negative number if s comes
before t. Full ASCII ordering is used: the strings can containany ASCII
character from ^A (0x01) to DEL (0x7f). Characters above DEL can cause
unpredictable results in ordering, however differences are always detected.
Note that for equality s need not be the same as t, what is being tested is
if the bytes addressed by the two pointers are the same.
_strequ: val = _strequ(s, t)
Check if s and t point to similar strings: return true (i.e. non-zero) if so,
false (zero) if not.
_strlen: val = _strlen(s)
This gets the length of a string, and return it as an integer.
_strcpy: _strcpy(s, t)
_strcpy reads the string at t and makes a copy of it in the buffer addressed
by s. This is useful to save permanent copies of the strings returned by
procedures such as _pfile or _itoa (qq.v.). Note that no error checking is
done: s must address enough memory to receive the string, also unpredictable
results may occur if s and t overlap.
_strcat: strcat(s, t)
This takes string t, and adds it to the end of string s. It leaves the
current contents of s alone, up to but not including the terminating zero.
(What happens is that the terminating zero on s gets replaced by the first
char of t, and the rest of t is copied over up to and including the zero on
the end of t. As in _strcpy it is the responsibility of the caller to ensure
that there is enough memory addressed by s to receive the new bytes, and the
caller should ensure that s and t don't overlap.
_strrev: _strrev(s)
This takes its argument string and reverses the bytes in place: so on entry
if s pointed to "hello", on exit it would point to "olleh". The terminating
zero byte is left undisturbed.
_strswp: _strswp(s, t)
_strswp moves the bytes addressed by t to s, and the bytes addressed by s to t
effectively swapping the two strings. Note that it there has to be enough
memory to do this, if s is shorter than t, there must still be enough memory
in the buffer addressed by s to take all of t. As in _strcpy and _strcat,
unpredictable results will follow if s and t overlap.
_strmov: _strmov(s, t)
This is very similar to _strcat in that it moves the string t to s, however
unlike _strcpy it does not move the zero byte terminator. This makes it
useful for replacing a small part of a large string without disturbing the
rest of it.
_strncmp: val = _strncmp(s, t, n)
Just like _strcmp, this compares two strings, however it also takes a third
parameter which is a maximum count to compare: i.e. if no differences have
found in the first n bytes then the strings are considered equal, and zero
gets returned.
_strnequ: val = _strnequ(s, t, n)
This is like _strequ: it tests for equality, but it stops after n bytes of
testing, and returns true if no differences were found.
_strncpy: _strncpy(s, t, n)
This copies string t to string s, but will copy no more than n characters.
Note that it will at most move n + 1 characters because it always adds a
trailing zero, and this is not included in the count.
_strncat: _strncat(s, t, n)
This adds t to the end of s, but like _strncpy it stops after n characters.
As in _strncpy, the zero terminator is always added, but never included in
the count n.
_strnrev: _strnrev(s, n)
This reverses the string s, but if it's length exceeds n then only the first
n bytes are affected, the remainder are left undisturbed. For example if s
points to "hello", and n is 3, then on exit s would point to "lehlo": the
first three bytes in the buffer have been reversed and the rest is left
alone.
_strnmov: _strnmov(s, t, n)
This does a limited (i.e. max n bytes move) string copy from t to s, however
like _strmov it does not move the zero byte terminator.
_memcmp: val = _memcmp(s, t, n)
This is similar to _strncmp in that it compares the bytes in two memory
buffers, and is set to compare a maximum of n bytes, however unlike _strncmp
it will not stop at the end of a string, it always compares exactly n bytes.
_memequ: val = _memequ(s, t, n)
Like _strnequ this checks memory for equality, however like _memcmp it does
not stop at the end of a string, it keeps going till it has checked n bytes.
_memcpy: _memcpy(s, t, n)
Move bytes from t to s: this simply moves n bytes, no more and no less. Note
that for this procedure, s and t can overlap: checks are made before the
copy is started so that data is moved in the right order (head first or
tail first).
_memrev: _memrev(s, n)
This just reverses n bytes addressed by s - like _strnrev but without the
checking for end of string.
_memset: _memset(s, v, n)
Set n bytes, addressed by s, to contain value v.
_memswp: _memswp(s, t, n)
This just swaps the first n bytes addressed by s and t.
_toupper: i = _toupper(ch)
This returns ch untouched unless ch is lower case ('a' - 'z'), in which case
it is converted to upper case. Note that for _toupper, _tolower and all the
_is????? routines, only the least significant byte of the argument is
considered, i.e. if hl is used to push it, then only l will be significant:
h can contain anything.
_tolower: i = _tolower(ch)
Returns ch converted to lower case if necessary.
_isupper: i = _isupper(ch)
Returns true (non-zero) if ch is an upper case letter, false otherwise.
_islower: i = _islower(ch)
Returns true if ch is a lower case letter.
_isdigit: i = _isdigit(ch)
Returns true if ch is a digit ('0' - '9')
_isprint: i = _isprint(ch)
Returns true if ch is a printable char (' ' - '~')
_isgraph: i = _isgraph(ch)
Returns true if ch is a graphic char ('!' - '~')
_isascii: i = _isascii(ch)
Returns true if ch is a valid ascii char (0 - 0x7f)
_isalpha: i = _isalpha(ch)
Returns true if ch is alphabetic ('A' - 'Z' or 'a' - 'z')
_isalnum: i = _isalnum(ch)
Returns true if ch is alphanumeric ('A' - 'Z', 'a' - 'z' or '0' - '9')
_isxdigit: i = _isxdigit(ch)
Returns true if ch is a hexadecimal digit ('A' - 'F', 'a' - 'f' or '0' - '9')
_isspace: i = _isspace(ch)
Returns true if ch is a white space char (tab - 0x09, lf - 0x0a, ff - 0x0c,
cr - 0x0d, or ' ')
_iscntrl: i = _iscntrl(ch)
Returns true if ch is a control char (0 - 0x1f or 0x7f)
_ispunct: i = _ispunct(ch)
Returns true if ch is a punctuation char: i.e. not a control char or an
alphanumeric
_atoi: val = _atoi(s)
If string s contains a number (e.g. "1234") then _atoi returns the value
of this number: a leading '-' creates a negative number. Leading white
space in the string is skipped.
_atoix: val = _atoix(s)
This is just like _atoi in that it converts a string to a number, but
it also recognises hexadecimal constants starting with 0x (eg 0x7fff),
or octal (leading 0) or binary (leading 0b). Hence "76", "0x4c", "0114"
and "0b1001100" all give the same value. As in _atoi, leading white
space is skipped.
_itoa: str = _itoa(val)
This reverses atoi: it takes an integer argument, and returns the address
of a buffer that holds the number printed as a string. This is the same
buffer as used by _pfile, so the same precautions should be observed
regarding use and saving of the string.
_isqr: i = _isqr(j)
_isqr returns the integer square root of an unsigned argument.
_abs: i = _abs(j)
_abs returns the absolute value of its argument taken as a signed integer.
_sgn: i = _sgn(j)
_sgn returns the "sign" of its argument taken as a signed integer: if it
is greater than 0 then 1 is returned, if less than zero -1, if zero then
zero is returned.
_min: i = _min(j, k)
_max: i = _max(j, k)
These return the minimum or maximum (as appropriate) of two signed
integer arguments.
_umin: i = _umin(j, k)
_umax: i = _umax(j, k)
These return the minimum or maximum (as appropriate) of two unsigned
integer arguments.
_lcm: i = _lcm(j, k)
This returns the lowest common multiple of two integer arguments, i.e.
the smallest number that both can divide. Note that if such a number
is greater than 65535, then the return value will be undefined.
_gcd: i = _gcd(j, k)
This returns the greatest common divisor of two arguments, i.e. the biggest
number that will divide into both.
_swab: _swab(dest, src, n)
This transfers n bytes from src to dest (like _memcpy), but it swaps
adjecent bytes as it goes. Note that if n is given odd, it is reduced
by 1 to make it even.
_bdos: hl = _bdos(c, de)
This calls into bdos, with arguments as shown, returns value is what
bdos gives back in hl.
_bios: a = bios(n, bc, de)
This calls the nth entry in bios table (1 is warm boot), with bc and de
registers set as shown. The return value is whatever comes back in a or hl
as appropriate to the routine in question.
_inp: ch = _inp(port)
This inputs a byte from one of the Z80's I/O ports.
_outp: _outp(ch, port)
This output a byte to one of the Z80's I/O ports.
_signal: _signal(mode)
This manipulates the way that ^C's are handled. mode can be one of three
things: if zero then normal ^C trapping is enabled, i.e. a reboot occurs;
if 1 then ^C's are ignored:, in fact the jump to zero is intercepted, and
control is passed back into bdos to do the input again; any other value is
taken as the address of a jump_buffer created by _setjmp, which is used
to cause a _longjmp to the environment saved in the jump_buffer.
_setjmp: val = _setjmp(env);
When called in normal flow, _setjmp sets up data for a non-local goto. It
saves it's environment in the array provided (which must be 4 words long),
and returns zero.
_longjmp: _longjmp(env, val);
This executes a non-local goto, which also cleans up the stack & frame
pointers: instead of returning to the caller of _longjmp, this call causes
execution to continue as if the call to _setjmp that initiated the
environment env had just returned. Note however that the value "returned"
by _setjmp in this case is val, which if non-zero provides a means to
determine if it was a "setup" call to _setjmp or a subsequent call to
_longjmp somewhere else. This call restores sp, bc, and ix to their
original values. If the environment is no longer valid then control
returns back out of _longjmp - this in itself implies an error.
_resrv: _resrv(n)
Successive calls to _sbrk keep on taking more memory, this determines
how much should be reserved for the stack, without any calls to _resrv,
the default is 1K, however it can be made as small or large as is needed.
_sbrk: ptr = _sbrk(n)
This returns a pointer to a block of memory n bytes long that can be used
exclusively by the caller of sbrk. Note that successive calls return higher
and higher addresses, and when the space available is exhausted, _sbrk
returns -1 to show the error. Note also that successive blocks will be
contiguous, so space for a string can be dynamically allocated by
repeated _sbrk(1) calls.
_sleep: _sleep(n)
This just waits for n seconds.
_pause: _pause(n)
This waits for n milliseconds - these previous two assume a 4MHz Z80 - with
other processors / clock speeds, the constant will have to be altered in
_pause.
_pack: _pack(p, r, n)
_unpack: _unpack(p, r, n)
These two handle rad50 compression: pack reads plain text from p, and
packs it rad50 into r, packing a total of n triplets. _unpack reverses this:
it unpacks n rad50 triplets reading from r, and placing the unpacked plain
text at p.
_call: hl = _call(addr, hl, a, de, bc)
_calla: a = _calla(addr, hl, a, de, bc)
These two allow calling of an arbitrary machine language address: they both
call the routine at addr, with hl, a, de, and bc set as shown, however
_call returns whatever came back in hl, whereas _calla returns whatever came
back in a.
_exit: _exit
This is not a procedure per se, instead each of the arx.o modules have
_exit defined in such a way that a call to _exit will terminate the
program, closing stdout properly if needed.
The rest of these routines are system subroutines, these all have a #
prefix.
#mul: integer multiply - returns hl * de in hl - destroys de & a
#udiv: unsigned integer divide - returns hl / de in hl, hl % de in
de. hl % de is the remainder on division when hl is divided
by de. destroys a
#div: signed integer divide - de and hl behave as above, also
destroys a
#muldiv: returns hl * de / bc in hl, with remainder in de, this will
give better accuracy than calls to #mul & #div as it retains
a full 32 bit inner product. destroys a & bc
#shl: shift left: returns hl left shifted de times in hl (with
zero fill), destroys de & a
#shr: shift right: returns hl right shifted de times in hl, (with
zero fill), destroys de & a
#rotate: returns hl rotated left de times in hl, destroys de & a
#neg: returns -hl in hl, destroys a
#cpl: returns bitwise not hl in hl, destroys a (~ in zsm exprs.)
#not: returns logical not hl in hl, destroys a (! in zsm exprs.)
#and: returns hl and de in hl, destroys a
#or: returns hl or de in hl, destroys a
#xor: returns hl xor de in hl, destroys a
#arg2: after entry to a routine of the type _proc(aa, bb), get
aa to de & bb to hl. Correct exit with maintenance of
stack discipline (i.e. matching your pushes and pops),
is a 'ret' instruction.
#arg2b: works like #arg2 getting two arguments to hl & de, except
that it also pushes bc, so with correct stack discipline,
the return from a routine that enters with an #arg2b would
be a 'pop bc' followed by a 'ret'.
#arg3: similar to #arg2b: for a routine _proc(aa, bb, cc), get
aa to de, bb to hl & cc to bc - leaves old bc on stack
so correct exit with maintenance of stack discipline is
a 'pop bc' followed by a 'ret'.
#csv: save bc & ix on stack & set ix from sp as initialisation
for a procedure: allows ix to be used as a frame pointer
#cret: any procedure that uses #csv MUST return by jumping to
#cret to restore ix & bx - note that stack discipline need
not be maintained as long as the data on the stack from ix
up has not been altered, and ix itself is unchanged: typical
usage is
proc: call #csv
ld hl,-76
add hl,sp
ld sp,hl ; allocate local variables
ld l,(ix+6)
ld h,(ix+7) ; get the first arg to hl
ld (ix-2),l
ld (ix-1),h ; save for later
etc. etc. etc.
jp #cret ; return
in the above example, arguments to proc are accessible
at ix+6/7, ix+8/9, ix+10/11 etc. for the 1st. 2nd. 3rd. args,
and local variable space starts at (ix-1) and proceeds down.
sp has been altered by proc, however since sp & ix are
equal on exit from #csv, the first thing #cret does is to
ld sp,ix
hence resetting sp.
#csvx,
#cretx: expanded csv & cret - in addition to saving ix & bc, these
also save iy, hl', de' and bc'; due to increased stack
usage arguments start at ix+14/15 etc.
#ucsa: force char in a upper case
#lcsa: force char in a lower case
#iswa: return with z flag set iff char in a is white space
#byp
#unbyp: these are direct assembler interfaces to _byp and _unbyp:
the only difference is that they take the parameter
directly in hl, rather than requiring that it be pushed
in the stack as is the usual convention.
#setcmp: set signed hl and de for an unsigned compare: all this does
is to add 0x8000 to both hl and de. For unsigned values it
is possible to test hl >= de simply by doing a 'or a' and a
'sbc hl,de' and testing the carry. This does not work on
signed values, however if a pair of signed integers are
passed into setcmp then the resulting values can be compared
by doing a 'sbc hl,de'. Destroys a
#setch: do the above, but to hl only: i.e. add 0x8000 to hl. destroys
a
The following is a list of further external labels that are reserved - these
are used by the above routines and their names should not clash with any
external labels in the program being linked.
#bsave #cbdos #cconv #cpylp #ctop
#digfms #digfmt #doprnt #fcb #flags
#kbdbf #kbdhd #kbdpc #rdrpc #sbuff
#spad #spsav #ssiz #stdin #stdout
#wfcb #xxfp #xxstr