==================== Z80 HI-TECH C UPDATE ==================== Jon Saxton 2014-05-03 This volume contains a substantial update to the Hi-Tech C 3.09A compiler suite for Z80 CP/M. It is built upon the excellent foundation provided by John Elliott's PIPEMGR patches which can be found at: http://www.seasip.demon.co.uk/Cpm/software/Pipemgr/index.html John's changes comprise the more significant portion of this update. My own contributions are more modest. Some of my changes have been posted earlier (see Tesseract volume 88) and are included in this update, albeit with further enhancements. 1. Packaging --------- Everything has been repackaged as .ARC files for ease of transport between CP/M and other operating systems (linux, Windows, etc.). 2. Bug in memset() --------------- The memset() bug has been fixed. This was part of my earlier Hi-Tech C updates and has been carried forward without change. 3. Wildcard expansion ------------------ Programs compiled with this update get wildcard expansion of the CP/M command line automatically. There is no longer any need to call _getargs() explicitly. Enclosing an argument in quote marks (' or ") supresses expansion. This can be useful for programs like grep which may use ? and/or * in text search patterns or for program options containing a question mark: grep 'a.*end' *.h 2d:*.c grep "-?" The -R option passed to the Hi-Tech C compiler is no longer useful. (It didn't work anyway.) Implementing automatic argument expansion meant altering the order of modules in LIBC.LIB. That entailed rebuilding the entire library from scratch. A script to do that is supplied. 4. Single bdos() function ---------------------- All versions of CP/M 80 return from BDOS calls with BA = HL regardless of whether the function is returning an 8-bit or 16-bit result. Hi-Tech C provided a bdos() call for 8-bit results and a bdoshl() call for 16-bit results. John Elliott added a third, bdose(), for disk functions which could return extended error codes in H (and B) when running on CP/M 3. There is now a single bdos() function. It always returns a 16-bit result in HL. The bdoshl() routine still exists for compatibility with older source files but it is simply an alias for bdos(). John Elliott's bdose() routine no longer exists; bdos() also performs its functions. Some BDOS functions return 255 as an error flag. The old bdos() code would sign-extend that to -1 (16 bit) but that is no longer done. if (bdos(fn, data) == -1) /* This won't work */ if (bdos(fn, data) == 255) /* This works sometimes */ if ((bdos(fn, data) & 0xFF) == 255) /* This always works */ Under CP/M Plus several functions return extended error information in the upper 8 bits of the return value. (Those functions also set the standard errno global item.) Always use the third form when checking for an error result. The compiler is quite clever and doesn't make your program work harder ... it just uses the L register. John's documentation describes a way to implement a password callback function to deal with incorrect or missing passwords on protected files. For the moment this is disabled. I have never found password protection worthwhile on CP/M 3 anyway and it is fairly easy to defeat. 5. User number in file name prefix ------------------------------- The format of file name prefixes indicating drive letter and/or user number is now much more liberal. If a file "sample.txt" is on drive E: in user area 12 then depending on the current drive/user the file may be accessed as: sample.txt if current DU: is E12: 12:sample.txt if current disk is E: e:sample.txt if current user is 12 e12:sample.txt 12e:sample.txt 12:e:sample.txt (Hi-Tech C format) Note that any of these forms is acceptable for program arguments, even those containng wildcard characters (?, *). (This is all from John Elliott.) 6. Exact file sizes (CP/M Plus) ---------------------------- Exact file sizes are supported on CP/M 3. Under CP/M 2.2 file sizes are always a multiple of 128 bytes but some versions of CP/M allow setting and reading a "last sector byte count" field in the FCB. The concept was carried forward from CP/M's ancestor, ISIS, but DRI did not document the precise meaning of the count for CP/M so two distinct interpretations were possible: a. LSBC represents the number of UNUSED bytes in the last sector; b. LSBC contains the number of USED bytes but 0 means 128. Both usages have appeared in the small number of CP/M 3 utilities which support exact file lengths. My own tools use the first interpretation; John Elliott's utilities use the second. The first interpretation is slightly simpler to implement because it avoids any special-case handling. It is also historically correct. Neither of these considerations is particularly significant but we do need to have programs which work together and interpret the last sector byte count the same way. After a lifetime in software development I have learned to avoid handling special cases as far as possible. Accordingly I have modified John Elliott's routines to interpret the last sector byte count as the number of unused bytes in the last sector. With this interpretation the formula for calculating the number of bytes in a file is always (sectors * 128) - lsbc. (When running on CP/M 2 lsbc is always zero.) 7. bios() function fixed for CP/M 3 and 2.2 ---------------------------------------- The original bios() routine used the function number to construct an offset into the BIOS jump vector and from that calculated the address of the appropriate BDOS routine. Except for some character I/O routines, that method of calling the BIOS is guaranteed to crash CP/M 3. A new bios() function has been implemented which checks for CP/M 3 and if that is the current operating system then it accesses the BIOS via the sanctioned method, i.e. by invoking bdos(50) with an appropriately filled- out parameter block. The standard bios() routine takes 1, 2 or 3 parameters: short bios(int func, int bc, int de) and that is good enough for CP/M 2.2. However CP/M 3 has functions which also take inputs in A and/or HL. The routine is smart enough to figure out where to put the parameters. All the C programmer needs to do is to supply the requisite arguments in the right order. A bug in the 2.2 bios() routine has been fixed. The original code would always return an 8-bit result even though there are some functions which return 16-bit results.