This library uses the same parameter passing mechanism as lib.l: see lib.doc for a discussion of this. _crypt: c2 = _crypt(c1); _setcr: _setcr(str); _crypt and _setcr implement an encryption system: _setcr initiates the system, and repeated calls to _crypt encrypt or decrypt the data. The string passed to _setcr may be considered a password, this string is used to initiate the crypt system. Once this has been done, each character of plain text is passed to _crypt, which returns the encrypted result. It should be noted that the action performed by _crypt is self inverting, so in a typical application, a file might be processed byte by byte through _crypt, after a password had been passed to _setcr. This would generate an encrypted version of the file, which could be restored by repeating the process. NOTE: No guarantees are given as to the security of this system: you use it at your own risk. In practice the encryption process should deter casual perusal, although if the NSA were to set a Cray 2 on this they might be able to break it. _rnd: i = _rnd(n); This returns a random number from 0 to n - 1 (unsigned); This routine uses an algorithm based on multiply overflow, rather than remainder on division, so there is no weighting problem with repeated calls doing _rnd(40000) _rand: i = _rand() This returns a random 16 bit quantity. _srand: _srand(a, b); In _rnd & _rand, certain values are used to seed the random number generator, and while they may be different from one program to the next, for successive runs of a given program they will be the same, hence causing _rand to return the same sequence of numbers. _srand provides a means to reseed the R.N.G., this can be done in one of four ways: 1. _srand(0) prints a "canned" string, and waits for the user to hit return. While it's waiting, a counter is kept running, and the resulting value is used to reseed the R.N.G. 2. _srand(1, str) prints str, and waits for any character to be typed, again running the counter. Hwever it does not "eat" the character typed in the same way as _srand(0) will: typical use might be the signon message printed by an interactive program, and as soon as the user responds, the R.N.G. is reseeded, and the program can carry on. 3. _srand(2) this one starts by reading the refresh register, then does some disk i/o, then gets a second value from the refresh register. It then uses the two 8 bit values as one 16 bit value to reseed the R.N.G. 4. _srand(3, n) simply uses n directly to reseed the R.N.G. Note that n is used in combination with the current seed, so repeated _srand(3, n)'s with the same value of n will keep reseeding the R.N.G. to different states. Any other value given as the first argument is used directly, the only reason that _srand(3, n) is present is to allow n to be 0, 1, 2 or 3. _setlin: _setlin(x1, y1, x2, y2) _point: i = _point(xp, yp) _setlin and _point are a pair of routines that operate a straight line drawing algorithm. To set up, _setlin is called with the two endpoints of the line in x,y coordinates: drawing starts at x1,y1 and goes to x2,y2. _point gets the next point on the line: xp and yp are the addresses of two integer variables into which the coordinates are placed. The return value i will be true (non- zero) while the values put in xp and yp lie between x1,y1 and x2,y2, but after the last point (x2,y2) has been returned, _point starts returning false (zero) although it will continue to return coordinate pairs that extend the line. _assert: _assert(test, fmt, a1, a2, a3, ...) _assert is used to check a condition while a program is running: it inspects it's first argument, if true then _assert simply returns. If the first argument is false (zero) then the format and arguments are processed as in _printf producing an error message, after which the program aborts: control is passed back to CP/M. _initp ok = _initp(array, size, width) _perm ok = _perm() These two handle generation of all possible permutations of a given data array. _initp is called to set the system up: array is the address of the data that will be permuted, size is the number of elements in the array, and width is the width of each one. Note that size must be between 1 and 16 inclusive, and width must be between 1 and 255 inclusive. If the call is successful, ok is zero, if there is an error (size or width out of range) ok gets -1. _perm is called to generate the next permutation: it moves the data around within the array passed to _initp, so this area of memory must remain useable throughout the operation. If _initp has not been called or returned an error (-1) then _perm will also return -1, and take no other action. If _initp was successful, then _perm will return 1 until it generates the last permutation (i.e. the data in array is returned to it's original state), at which point it returns 0 to show that this is the final permutation. However, successive calls to _perm will continue to cycle through the same sequence of permutations, returning 1 again until the data returns to it's original order etc. etc. _printf, _sprintf, _fprintf, _xprintf: The arguments passed to these are exactly as in the _printf family in LIB.L: however more options are available in the formatting. In all cases a '-' immediately following the '%' that introduces a format specifier indicates that the information should be left justified rather than the default right justification. In addition for strings ('%s') a second number can be given by preceding it with a period ('.'): the effect of this is to specify a maximum field width: so if %.10s was given, then at most ten characters from the string would be printed, the remainder would be discarded. This can be used in conjunction with a minimum field width: for example assume %12.10s was given. In this case there would always be at least two blanks on the left, because the output must be at least 12 characters wide, but only a maximum of 10 can be printed from the string. In addition two other numeric output formats are recognised. %r %R: these print in roman numerals for values between 1 and 4999 - %r uses lower case, and %R uses upper case. Zero is printed as '0' and values greater than 4999, which cannot be represented using the standard ASCII character set, are replaced by a number of '*' characters to signify the error. %a %A: these print in 'alphabetic' notation: zero is again printed as '0', but 1 becomes 'a' or 'A' (%a generates lower case, and %A generates upper case), 2 becomes 'b', 3 - 'c', ... 25 - 'y', 26 - 'z', 27 - 'aa', 28 - 'ab' and so on. As with all numeric outputs, %r, %R, %a and %A recognise a minimum field width before the format specifier, and the '-' to force left justification. Finally %z causes a 'recursive' _printf to occur. The argument is taken as the address of an array of integer words which is assumed to be the following: the first word is the address of a format string, and the second and subsequent words are the values to be processed by the format. _scanf, _sscanf, _fscanf, _xscanf: These four routines form a set of routines for doing formatted input, in a similar manner to _printf et al. The parameters are similar: _scanf takes a format as it's first argument, followed by a series of pointers to locations where the scanned values are returned. _scanf reads from standard input (the keyboard, unless redirected - for an explanation of redirection read ARX.DOC). _sscanf takes an additional parameter before the format which is the address of a string (zero byte terminated) from which the scanning will be done. _fscanf has a similar first parameter, but in this case it is a file pointer such as _fopen would return (see LIB.DOC for an explanation of _fopen and file pointers). _xscanf takes the address of a procedure that can be called to get successive characters: this procedure must observe the standard protocol (can destroy de and a, value returned in hl, bc, ix, iy must be preserved). The format string is similar to that of _printf: to start a conversion a '%' character is given, followed by an optional assignment suppression character: '*', followed by an optional maximum field width given as a number, followed by a format specifier. Format specifiers are just like in _printf: 'd' for a number, 'u' for an unsigned number, 'o' for an octal number, 'x' for a hexadecimal number, 'b' for a binary number, 's' for a string, and 'c' for a character. Other characters in the format string must match the input, if a mismatch occurs then scanning stops. Note that since '%' has special meaning in the format string, if a '%' in the input is to be matched, then '%%' should be given in the format string. White space in the format string matches optional white space in the input stream, so it is a good idea to place a space after every conversion specifier in the format string to cause the input field delimiters to be consumed. In the simplest case _scanf will parse a sequence of fields separated by white space. As an example, if the format were: '%d %x %s' then: '42 a3e hello ' would be suitable input. The arguments that should follow the above format are two pointers to integers: these will receive 42 and 0xa3e respectively, and the address of a character buffer that will receive the string 'hello' complete with a zero byte to terminate it. In this case scanf would return 3: it's return value is the number of successfully converted and assigned fields. Note that conversions that are not assigned because of a '*' are not included in the return count. Note however, if the format were: '%d %2x %s' then the two integers would get 42 and 0xa3 (two characters maximum), and the string would receive 'e' (first byte following the 'a3'). Assignemt supression causes a field to be scanned, but it is then thrown away: so no argument should be pushed onto the stack to receive the scanned value. Note that if '%05s' is given as the format specifier, then normal white space supression is ignored: this simply transferrs the next five characters verbatim to the string buffer provided. There is a final conversion specifier that behaves somewhat like 's': it allows recognition of strings of arbitrary length, but delimited by non white space characters. If the format specifier is '[' then characters upto a matching ']' are taken as a set that defines what the string must be composed of. In cases where a range of characters (e.g. ABCDEFGHI) needs to be given A-I will suffice. Note also that characters are converted until one is found that is not in the range given in []: if the reverse is required, i.e. conversion of all characters not specified, then by providing a '^' as the first character, the sense of testing is reversed. Since ']', '^' and '-' have special meaning, they can be escaped by being preceded with a '\' (which can also escape itself). Note however, these significant characters should not be used as part of a range specifier: for example if the range 'WXYZ[\]' were being scanned, it would be necessary to do it as 'W-[\\\]' where the 'W-[' gives the first five, the '\\' gives a '\', and the '\]' gives the ']'. Note also, because of the way ZSM works, when assembling a string that contains a backslash it must itself be escaped to ZSM, so the above format in a ZSM source would appear as 'W-[\\\\\\]', because each '\\' pair in the ZSM db string source gets converted to a single '\' in the program. To give some working examples: if a CP/M directory name were to be converted to a drive, a filename, and an extension the following might be useable: '%c:%8[A-Z0-9].%3s' where the '%c' would match the drive specifier and assign it into an integer pointer. The ':' matches, then the %8[A-Z0-9] would match an alphanumeric only string with a maximum length of 8 characters, followed by a '.' and another string of up to 3 characters. In this case however, a filename such as A:-HELP.TXT would fail as the '-' would not match. This can be better done by: '%c:%8[^.].%3s' where the %8[^.] matches anything that is not a '.'. Of course in this case spaces would be considered valid characters, however by adding suitable characters to the group in the [], this problem could be avoided. One last word of warning: in all cases except 's' and '[' the parameter given must be a pointer to an integer, so that the value can be properly saved, and 's' and '[' require the address of a buffer big enough to take the string. In the case of scanning from a file (_fscanf) or from standard input (_scanf), -1 is returned if the first thing encountered in the input is an end of file. This is different from a zero which means that input was read, but could not be matched at all. _qsort _qsort(array, size, width, test) _qsort is used to sort an array in memory. It takes four parameters: the first is the address of the array to be sorted, the second is the number of elements in the array, the third is the width in bytes of each element, and the fourth is a routine that will be used to test if one element is smaller, equal to, or greater than a second. test will be called with the addresses of two elements from the array on the stack: as an example if the array contained integers, then the parameters given to test would be pointers to two of these integers, or if the array contained pointers to strings, test would receive pointers to pointer to strings. test should compare the two elements given to it and return a negative number if the first was smaller, zero if they were equal, and a positive number if the first was greater. Note that the first argument is given in terms of the standard calling order: test(arg1, arg2) - so in fact the first argument would be the one pushed immediately prior to the call to test: on the stack on entry to test would be: second arg first arg return address to _qsort stack pointer ---> Note that test must adhere to the standard calling convention: i.e. it can destroy de and a, it's result is returned in hl; and bc, ix and iy cannot be damaged. _search pointer = _search(array, size, width, test, match) _search scans a sorted array looking for a specific element. The first four parameters are exactly as in _qsort, however the array must already be sorted (i.e. by a call to _qsort). match is a pointer to an entry like those in the array (e.g. if the aray contained integers, match would be a pointer to an integer). _search returns the address in the array of the first element in the array greater than or equal to the match element, or NULL if all elements are less than match. _exec _exec(program) _execl _execl(program, arg1, arg2, ... argn, 0) These two provide the means to chain from one program to another: in both cases the first parameter is the address of a string containing the name of the program to chain to. Note that the '.COM' extension is added by the routines. _exec simply invokes the program with no arguments, setting the command tail buffer at 0x0080 empty, and the default fcbs at 0x005c empty as well. _execl allows one or more arguments to be given to the program: arg1, arg2, etc. are pointers to strings containing the arguments: they are terminated by a NULL (zero) pointer. These are placed into the buffer at 0x0080, in the same manner as CCP does, in addition the default fcbs at 0x005c and 0x006c are initialised. _alloc memory = _alloc(size) _alloc provides a more flexible memory allocation system than _sbrk in LIB.L: in particular it is possible to return a block of memory to the system for further use. _alloc is the basic memory allocation routine: it takes a size and returns a pointer to a block of memory that many bytes big. NOTE: _alloc places certain internal accounting information in the area just outside the block returned, so when using this memory do not access areas anywhere outside it. Also note that unlike _sbrk, _alloc does not return contiguous memory. If there is insufficient memory available to fulfill the request, _alloc returns NULL (zero) to signify the error. _realloc newmem = _realloc(memory, size) _realloc allows a block of memory handed out by _alloc to have it's size changed after the fact. The first parameter should be a pointer previously returned by _alloc, and the second should be the new size. _realloc returns a pointer to the new area, which may not the at the same address. Note however that if the new pointer is different, than _realloc will copy as much of the original data as makes sense (i.e. the smaller of the original and new sizes) _free _free(memory) _free returns a block of memory previously given out by _alloc or _realloc to the system, for subsequent re-use. Note that the data contained in the memory is not affected, but it's validity cannot be guaranteed: _alloc or _realloc may hand portions of it out at any subsequent time. _xfree _xfree(memory, size) _xfree allows a piece of memory NOT given out by _alloc or _realloc to be added to the system for subsequent use. The first parameter is the address of the memory being handed to the _alloc system, and size should be it's size in bytes. This might be used to allow _alloc to use a large data table that was used once for initialisation, but would then remain idle. Caveat: The above four routines require that strict discipline be maintained regarding their usage: if a program writes outside the bounds of a memory area given to it, or _free is handed an arbitrary pointer, then this may well lead to a program crash. _alloca memory = _alloca(size) _alloca is similar to _alloc, but it allocates memory in the stack frame of the procedure that called it. It allocates the memory by adjusting the stack pointer to create a block on the stack, and then returning a pointer to it. It is the responsibility of the calling procedure to reset the stack pointer: if #csv and #cret are in use, then the jump to #cret will clean up automatically, otherwise some other means must be found to reset the stack pointer. Note that any values pushed onto the stack before a call to _alloca cannot be subsequently popped off: they live above the block, and the stack pointer points to the bottom. 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. #addcom #alcb #alcp #argfcb #doexec #doprnt #doscan #getmem #seed #setfcb #testdh