home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-08-11 | 38.5 KB | 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