This repository contains the HI-TECH C Compiler for Z80 v3.09 along with updates and enhancements. It runs natively on a Zilog Z80/Z180/Z280 processor under the CP/M operating system or under emulation on your Windows/Linux/macOS system using RunCPM1, SIMH AltairZ802 or ZXCC3.
Each release is a consolidated milestone with various updates and patches applied. You should read this README.md file for details.
The latest release is V3.09-17 (see Modification History below).
If you only wish to download the latest binary distribution, download it from
https://raw.githubusercontent.com/agn453/HI-TECH-Z80-C/master/htc-bin.lbr
and optionally the Z280 binary distribution (if you need it) from
https://raw.githubusercontent.com/agn453/HI-TECH-Z80-C/master/z280bin.lbr
I welcome bug reports/fixes and additional commentry and discussion.
You can raise an issue here on GitHub or contact me directly via e-mail at tony.nicholson@computer.org or by posting to the USENET newsgroup forum at comp.os.cpm in the "Consolidating Updates for HI-TECH C for Z80 CP/M v3.09" discussion thread4.
The first release is a slightly modified (see below) v3.09.
The HI-TECH Z80 CP/M C compiler v3.09 is provided free of charge for any use, private or commercial, strictly as-is. No warranty or product support is offered or implied.
You may use this software for whatever you like, providing you acknowledge that the copyright to this software remains with HI-TECH Software.
The dist folder contains the entire compiler including the original distributed library source-code in Huffman-encoded archive files (CPM.HUF, GEN.HUF, FLOAT.HUF and STDIO.HUF). NB: I have not rebuilt the .HUF files - so please use updated run-time library source files in the following folders.
The cpm, gen, float and stdio folders contain the library source-code that has been extracted from the .HUF archive files using DEHUFF.COM and the updated/modified files.
The doc folder contains the documentation in ASCII text as HTCZ80.TXT.
In chronological order - so be sure to check the latest updates at the end of this README file.
The original HI-TECH C linker (LINK.COM) has been renamed as LINQ.COM and the main compiler front-end (C.COM) has been patched to reflect this name change. This was done to avoid a name clash with the Digital Research supplied CP/M linker. The original version is kept as C-ORIG.COM and the patched version is dist/C309.COM
I've edited the documentation to remove the underlining and bolding of text by over-printing and fixed some layout issues and typos.
As supplied in v3.09, the LIBF.LIB produces inaccurate floating point trigonometry results - as reported by "Ed" in his posting to the comp.os.cpm USENET Newsgroup.
Since we're on the topic of Hitech-C for CP/M, below is a problem I
discovered with the freeware release.
LIBF.LIB supplied with the freeware Hitech-C v3.09 produces inaccurate trig
results. I found that by simply recompiling the library with the existing
source files from FLOAT.HUF, it fixed the problem. Absolutely no changes
to the sources were required.
The re-compiled LIBF.LIB from the FLOAT.HUF library sources in float has been copied to the distribution files in dist. A CP/M submit file to rebuild LIBF.LIB has been added to float.
A test folder has been created. In it is a TESTTRIG.C program for testing this release.
The late Jon Saxton (former SYSOP of the Australian Tesseract RCPM) collected together a number of patches and updates - See Tesseract volume 091.
These include contributions from John Elliott via http://www.seasip.info/Cpm/software/index.html - in particular for PIPEMGR which is a CP/M 3 RSX that implements piping and redirection.
John's contributions will be implemented in a later release.
This release includes updates to memset(), bios() and bdos() library routines. In addition the stat(), close() and sys_err() routines are updated to remove references to bdoshl() and to include CP/M 3 error messages.
memset()
The Hi-Tech C implementation of memset() is seriously broken and is
almost guaranteed to give trouble in any program which uses it.
The original implementation simply does not agree with the function
declaration. It now does.
void memset(void s, char c, size_t n)
Sets n bytes of memory starting at the location pointed to by s with
the character c.
bios()
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.
Under CP/M 3 the calling of the BIOS SELMEM (function 27) is intercepted
by the BDOS (to prevent you selecting a memory bank that could change the
memory bank that you are executing from). Also, if you're using the
Digital Research banked BDOS then when it invokes a direct BIOS call
(BDOS function 50) it makes a call to the BIOS MOVE routine to copy the
parameter block into common memory. This means you cannot cascade a
bios() call to XMOVE (function 29) and MOVE (function 25) without
corrupting memory. Simeon Cran's ZPM3 banked BDOS replacement does not
have this restriction.
bdos()
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 3 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.
These are the Jon Saxton modified versions of John Elliott's patches.
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 "-?"
_getargs() also recognises piping of stdin and stdout using the
Unix-style >filename (redirect stdout to filename) >>filename
(append stdout to file) and <filename (read stdin from file).
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.
Filename drive and user number prefixes
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 (?, *).
Exact file sizes (CP/M 3 and DOS+ v2.5)
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. Jon Saxton's 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.)
NB: This change is now detected automatically for releases v3.09-14 (and
later) by the start-up module (CRTCPM.OBJ) - see below.
strcasecmp()
The case-insensitive string comparison function strcasecmp() has been
implemented. Its function prototype is in string.h. It works just like
strcmp() except that upper- and lower-case letters are treated as
identical. There is also a strncasecmp() analogue of strncmp() which
allows one to limit the comparison to a certain number of characters.
toupper() and tolower()
The functions toupper() and tolower() were implemented as macros which
added a case-shift value to the character parameter without checking to
see if the parameter was a letter. To use these routines correctly it was
necessary to do a range check, e.g.
if (isupper(c))
c = tolower(c);
These operations now invoke the correspondingly named routines in LIBC.LIB
and it is no longer necessary to pre-test the character.
Support for read/write files
The stdio functions now allow files to be opened in modes "r+" and "w+" -
ie, reading and writing are supported on the same file.
Remember to fflush() or fseek() between a read and a write.
This code is experimental.
PIPEMGR support (CP/M 3)
Programs automatically check for PIPEMGR when they load, and if it
is present the C streams stdin, stdout and stderr are routed to their
PIPEMGR counterparts - no modification of the source is required.
The special device files (CON: LST: PUN: and RDR:) are joined by RSX:
(PIPEMGR stdin/stdout streams) and ERR: (PIPEMGR stderr stream). If
PIPEMGR is not present, these files behave like CON:
The variable:
extern char _piped;
is nonzero if PIPEMGR is present.
The command
FOO <CON:
will behave differently if PIPEMGR is present. If it is not present,
then you get line-based input from the Hi-Tech C library. If PIPEMGR
is present then you get character-based input from PIPEMGR.
The line-based input in the library will now return EOF if a control-Z
is typed on a line by itself. Previously it would never return EOF on
line-based input.
Unfortunately if you're using Simeon Cran's replacement ZPM3 then it
does not allow control-Z to be typed on an input line.
I'm not yet sure how to get round this.
Remember that when PIPEMGR has parsed the command line, you may have a
number of blank (zero-length) arguments.
CP/M 3 extended error support
To enable CP/M 3 extended error support, put the line
bdos(0x2D,0xFF);
near the beginning of your program. Then errors such as read-only files
will not cause the program to terminate abruptly; instead the call which
caused the error will fail, and perror() will explain the reason. The
variable:
extern int errno;
will contain (16+x) for CP/M 3 extended error x.
File passwords (CP/M 3)
NB: This feature is disabled. You will need to change the
PWDREC definition in the file BDOS.AS to true and rebuild
and replace the module in LIBC.LIB to enable it.
There is partial support for file passwords. To enable it, declare a
function:
#include <cpm.h>
char *mypass(struct FCB *fcb)
{
/* For the file specified by "fcb", a password needs to be entered.
Return a pointer to it (8 characters, uppercase, packed with
spaces)
*/
}
and insert a line near the beginning of your program:
_passwd = mypass;
Then your routine ("mypass" in this example) will be called when
CP/M reports a password error; it will be called repeatedly until
the user enters the correct password or a it returns a NULL pointer
(ie, the user has given up).
Checks for a ZCPR3 environment
The variable
extern void *_z3env;
is 0 if one is not present, or its address if one is. It also passes it
as a third parameter to main():
int main(int argc, char **argv, void *z3env);
The Z3 environment address must be passed to the program in HL.
Graceful exits
Compiled programs exit gracefully if run on an 8080 or 8086 processor,
or if there is not enough memory for the program and its static data.
This is done in the CRTCPM.OBJ module.
CP/M 3 compatible error system
exit(0) and _exit(0) set the CP/M 3 error code to 0.
exit(n) and _exit(n) for non-zero n set the error code to 0xFF00 | (n&0x7F).
The practical upshot is that exit(0) translates as "OK"; other values
translate as "error". You can use this in conjunction with CP/M 3's
SUBMIT features. If the next command in a .SUB file is preceded by
a : character, it will be ignored:
CPROG
:OTHER
will only execute OTHER if CPROG exited with the value 0.
Extended getenv()
Under CP/M 3, getenv() uses the search path set by SETDEF to locate the
ENVIRON file. So if you like to keep ENVIRON on a drive that isn't A:,
programs will still find it.
Updated the previous release to adjust the ordering of modules in LIBC.LIB - programs should now link correctly. Also added the missing documentation for the debugger in doc/DEBUGMAN.TXT (this was previously available from the now defunct hitech.com.au web site) and scanned PDFs of the 1989 vintage documentation.
Some of the string related function prototypes in STRING.H are missing from the LIBC.LIB library. Most of these are from an update to the MSX flavour of HI-TECH C by Arnold Metsalaar. I've cherry-picked the following routines which are applicable to generic CP/M as well as MSX.
STRING.H
The header file has been changed to reflect the available
functions in LIBC.LIB. There are still missing routines -
namely strcoll() strcspn() strpbrk() and strspn() and these
have been commented out for now.
strchr() and strrchr()
char *strchr(char *s, int c)
char *strrchr(char *s, int c)
These functions - as well as index() and rindex() (which are identical)
previously returned a NULL for no match. The functions now return
a pointer to the character string's ending NUL character.
stricmp() and strnicmp()
char stricmp(char *s1, char *s2)
char strnicmp(char *s1, char *s2, size_t n)
Case-insensitive versions of strcmp() and strncmp() comparison routines.
Can also be referenced as strcasecmp() and strncasecmp().
strstr(), strnstr(), stristr() and strnistr()
char *strstr(char *t, char *s)
char *strnstr(char *t, char *s, unsigned int n)
char *strcasestr(char *t, char *s)
char *strncasestr(char *t, char *s, unsigned int n)
These extra functions locate the first occurrence of string s in
string t. The functions strnstr() and strcasestr() read at most
n characters from the string t. The functions strcasestr() and
strncasestr() use case insensitive comparisons.
All these functions return a pointer to the first character of
the first occurence of string s in string t if found, and NULL
otherwise.
strdup()
char *strdup(char *s)
Allocates a new buffer for and copies the string pointed to
by s to it. Returns a pointer to the copy of the string or NULL
if the memory allocation failed. The memory block can be released
using free().
strtok()
char *strtok(char *s, char *tok, size_t toklen, char *brk)
Copies characters from s to tok until it encounters one of the
characters in brk or until toklen-1 characters have been copied
(whichever comes first). It then adds a NUL character to the
end of the string. This is a non-conforming POSIX function.
TIME.H
Now includes a prototype for strftime() - see below.
strftime()
size_t strftime(char *s, size_t maxs, char *f, struct tm *t)
Converts a time value t to a string using the format string f
into the string s of size maxs (including a terminating NUL).
It acts as a sprintf() function for date/time values. The
following are valid in the format string -
%A full weekday name (Monday)
%a abbreviated weekday name (Mon)
%B full month name (January)
%b abbreviated month name (Jan)
%c standard date and time representation
%d day-of-month (01-31)
%H hour (24 hour clock) (00-23)
%I hour (12 hour clock) (01-12)
%j day-of-year (001-366)
%M minute (00-59)
%m month (01-12)
%p local equivalent of AM or PM
%S second (00-59)
%U week-of-year, first day sunday (00-53)
%W week-of-year, first day monday (00-53)
%w weekday (0-6, sunday is 0)
%X standard time representation
%x standard date representation
%Y year with century
%y year without century (00-99)
%Z timezone name
%% percent sign
the standard date string is equivalent to:
%a %b %d %Y
the standard time string is equivalent to:
%H:%M:%S
the standard date and time string is equivalent to:
%a %b %d %H:%M:%S %Y
strftime() returns the number of characters placed in the
buffer, not including the terminating NUL, or zero if more
than maxs characters were produced.
This is the first experimental release of the libraries that have been built for a Zilog Z280 MPU. Most of this work was retrieved from a distribution of the UZI-280 operating system for the CPU280 system. It was written by Stefan Nitschke and Alexander Schmid.
To use this on a system with a Zilog Z280 MPU, merge the files from the dist folder (i.e. the Z80 version) with those from the z280dist folder onto a drive in the search path (usually drive A in user area 0). On CP/M 3 the *.COM, *.H, CRTCPM.OBJ and the *.LIB files should be set with the system file attribute so they can be found from all user areas.
The z280dist folder contains the following files -
C280.COM
The compiler front-end for driving the HI-TECH Z80 C compiler
for CP/M to produce code to run on a Zilog Z280 MPU. It accepts
command lines similar to the C.COM front-end from the *dist*
folder - with a modified command-line switch to perform optimisation
of generated assembly language to use the enhanced Z280 instructions.
You can optimise for size (-O2) or speed (-OF2). For example,
c280 -of2 ...
will cause the OPTIMH.COM pass to perform speed optimisation.
OPTIMH.COM and OPTIMH.C
This is the Z280 assembly language optimiser (with source-code).
The following instructions are optimised -
call amul -> multw hl,de
call lmul -> multuw hl,de
call csv -> push iy, push ix, lda ix,(sp+0) (+6 bytes)
jp cret -> ld sp,ix pop ix pop iy ret (+6 bytes)
ld (hl),c
inc hl -> ldw (hl),bc
ld (hl),b -> inc hl
ld (hl),e
inc hl -> ldw (hl),de
ld (hl),d -> inc hl
ld c,(hl)
inc hl -> ldw bc,(hl)
ld b,(hl) -> inc hl
ld e,(hl)
inc hl -> ldw de,(hl)
ld d,(hl) -> inc hl
ld c,(ix+n) or iy
ld b,(ix+n+1) -> ldw bc,(ix+n) (2 byte)
ld e,(ix+n) or iy
ld d,(ix+n+1) -> ldw de,(ix+n) (2 byte)
ld l,(ix+n) or iy
ld h,(ix+n+1) -> ldw hl,(ix+n) (2 byte)
ld (ix+n),c or iy
ld (ix+n+1),b -> ldw (ix+n),bc (2 byte)
ld (ix+n),e or iy
ld (ix+n+1),d -> ldw (ix+n),de (2 byte)
ld (ix+n),l or iy
ld (ix+n+1),h -> ldw (ix+n),hl (2 byte)
or a
sbc hl,bc -> subw hl,bc (1 byte)
or a
sbc hl,de -> subw hl,de (1 byte)
push ix
pop de
ld hl,nn
add hl,de -> lda hl,(ix+nn) (3 byte)
push ix -> ld e,ixl
pop de -> ld d,ixh
push ix
pop hl -> lda hl,(ix+0)
In addition memory addressing with large offset -
push ix
pop de
ld hl,nn
add hl,de -> lda hl,(ix+nn) (3 byte)
push iy
pop de
ld hl,nn
add hl,de -> lda hl,(iy+nn) (3 byte)
LIB280C.LIB and LIB280F.LIB
These are the library modules for the Z280 C run-time library
and floating point library that contain optimised Z280 routines.
These will only execute on a Z280 MPU - and not on a Z80!
They have been built from sources of the v3.09-4 release.
I'm making available the raw updates for MSX-DOS 2 for those interested in the last known updates to run HI-TECH C for Z80 under MSX2. You'll find the individual files in the msx2dist folder and its sub-folders.
These were extracted from files produced by Arnold Metselaar in 2013 and distributed as LZH archives on the MSX Banzai web site at http://msxbanzai.tni.nl/dev/software.html
One of these archive files is known to be damaged - the one containing the I/O library for MSX-DOS 2 (LDOS2_04.LZH) and I have replaced the source code for only one of the modules with that from an earlier version obtained from the Wayback machine at archive.org. There's still a missing (_UTIME.AS) that I'll try to re-assemble from the binary in the cc_01/LIBDOS2.LIB library.
The msx2dist/original folder contains the as-downloaded .LZH archives (including the previous versions from archive.org).
Also note that Yeongman Seo is currently working on a toolchain for developing MSX-DOS/DOS 2 applications using the HI-TECH Z80 C compiler under a Windows Win32 environment. This is available on GitHub at
https://github.com/sharksym/CPMEMU_HI-TECH_C
The getenv() function was not correctly looking up the location of the environment file under CP/M 3 for the "default" entry in the drive search chain. It will now locate the ENVIRON file if you have set-up the default search chain with a default entry. For example
; Set search chain to current drive, RAMdisk, C: then A:
setdef * m: c: a:
in your CP/M 3 PROFILE.SUB file (or manually entered on the command line).
Updated dist/LIBC.LIB and z280dist/LIBC280.LIB too.
Updates merged from @tsupplis
Ron Murray's overlay capabilities have been added, along with some miscellaneous fixes - mainly to the CP/M BDOS definitions in CPM.H and to fix an incompatibility with conflicting _TIME macros in both TIME.H and CPM.H
Rebuilt libraries LIBC.LIB and LIBF.LIB (as well as LIB280C.LIB and LIB280F.LIB for the Z280). New snapshot as Release V3.09-5.
The CP/M start-up module CRTCPM.OBJ was not checking the CP/M version prior to checking for the existence of the PIPEMGR RSX. This caused compiled programs under CP/M 2.2 to fail to initialise correctly.
A new version of CRTCPM.OBJ is available (and the source-code in ZCRTCPM.AS has been updated to correct this).
Andrey Nikitin has contributed the sources for the DEHUFF and ENHUFF programs that were used by HI-TECH Software to distribute the library source files. I've placed the extracted source files and the resulting binary produced by the latest compiler in the huff folder. Also, the Huffman-encoded archive containing these sources has been placed in the dist folder as HUFF.HUF. There's also an original version that produces some diagnostic errors in HUFFORIG.HUF.
These sources may be built using the HI-TECH C compiler or using the gcc compiler under Linux or macOS. I built them natively under CP/M using -
c -O -v enhuff.c encode.c misc.c
c -O -v dehuff.c decode.c misc.c
(If you're using a Z280 use 'c280 -of2 -v ...')
Merged a minor change into stdio/DOPRNT.C from @tupplis so that various printf() routines can now output strings greater than 255 characters.
Updated the dist/LIBC.LIB and z280dist/LIB280C.LIB libraries too and created a new v3.09-6 release to consolidate the recent updates.
Updated the cpm/C.C source to include the V3.09-6 identifier and supply a pre-compiled version in dist/C309-6.COM that can be copied to the CP/M system path (usually drive A in user area 0) as C.COM
Also included a version of the start-up code for the Z280 under CP/M as z280dist/CRTCPM.OBJ (with source-code in z280dist/Z280CPM.AS). This checks whether the compiled binary is running on a Z280 MPU.
I've included Jon Saxton's version of the PIPEMGR RSX for handling pipes under CP/M 3.
This is a modified version of the original by John Elliott that's detected and used by each of the releases of HI-TECH C available from this repository.
You'll find the original PIPEMGR.ARC distribution from Tesseract volume 089, a PIPEMGR.LBR containing the same files and individual extracted files in the pipemgr folder.
Also included is the source and executable for TEE which has the PIPEMGR RSX attached and behaves like its namesake in the Unix world, along with missing files from the original Van Nuys tools (PIPEMGR.H and PIPEMGR.C).
The rest of the Van Nuys tools are available from John Elliott's original PIPEMGR page at
http://www.seasip.info/Cpm/software/Pipemgr/index.html
@tsupplis provided updates to the self-relocating image start-up module to support the improvements from the standard CP/M start-up (in CRTCPM.OBJ). The source changes are in cpm/ZRRTCPM.AS and the compiled object replaces dist/RRTCPM.OBJ
To build a self-relocating image, use the -A compile option.
-
Declaration for rename() missing from dist/UNIXIO.H
-
Make CPU check in the start-up modules consistent. Output message for Z80 (or Z280 MPU) if the incorrect processor is detected. For example -
This CP/M program requires a Z80 CPU.
-
Include the modified floating-point version of float/DOPRNT.AS to support outputting strings greater than 255 characters by the various printf() routines.
-
With @tsupplis provided fix for the compiler front-end dist/C309-7.COM now handles input correctly from submit files - resolving issue #9. Updated dist/SIGNAL.H, cpm/SIGNAL.C, cpm/C.C and stdio/GETARGS.C (a mix of input from and output to the console device stream was previously discarding input from CP/M 3 submit files).
-
The start-up modules have been corrected to properly report an error when run under MS-DOS. (Previously the assembly language routine was incorrectly being optimised - and an absolute jump was changed to a relative branch).
-
Updated both dist/LIBC.LIB and dist/LIBF.LIB object libraries and the Z280 ones in z280dist/LIB280C.LIB and z280dist/LIB280F.LIB.
-
Include dist/STDINT.H for some standard type definitions (and referenced by the overlay support routine).
Recently Microchip Technology Inc. (the current owners of the HI-TECH intellectual property) also released the MS-DOS version of the HI-TECH C Compiler v4.11 - a cross-compiler for building CP/M and ROM-able programs for the Z80 using MS-DOS. I've uploaded this version to GitHub at
https://github.com/agn453/HI-TECH-Z80-C-Cross-Compiler
Thanks to @tsupplis, the following enhancements are now available in the latest release v3.09-7.
- The compiler front end now supports building CP/M programs using overlays. Just copy the dist/C309-7.COM to your system directory as C.COM, along with the symbol table builder dist/SYMTOAS.COM and overlay support library dist/LIBOVR.LIB.
To build a program with overlays, specify the -F option when compiling the resident part of the main program (which writes a symbol file) and -Lovr to use the ovrload() routine; and for each of the overlay segments compile using the -Y option and specify the symbol file on the command line. For example (these files are in the test folder too) --
Firstly the simple main program
A>type TESTOVR.C
#include <cpm.h>
#include <stdio.h>
#include <overlay.h>
int main(int argc, char ** argv)
{
intptr_t i;
printf("overlay test\n");
i=ovrload("testovr1",0);
printf("overlay return is %d\n",i);
i=ovrload("testovr1",0);
printf("overlay return is %d\n",i);
i=ovrload("testovr2",0);
printf("overlay return is %d\n",i);
i=ovrload("testovr1",0);
printf("overlay return is %d\n",i);
return 0;
}
A>type testovr1.c
void * yop(void *a) {
printf("hello from overlay 01\n");
return 0;
}
A>type testovr2.c
void * yop(void *a) {
printf("hello from overlay 02\n");
return 0;
}
A>c -ftestovrx.sym -o testovr.c -lovr
A:C COM (User 0)
Hi-Tech C Compiler (CP/M-80) V3.09-7
Copyright (C) 1984-87 HI-TECH SOFTWARE
Updated from https://github.com/agn453/HI-TECH-Z80-C
A>c -y -o testovr1.c testovrx.sym
A:C COM (User 0)
Hi-Tech C Compiler (CP/M-80) V3.09-7
Copyright (C) 1984-87 HI-TECH SOFTWARE
Updated from https://github.com/agn453/HI-TECH-Z80-C
TESTOVR1.C
TESTOVRX.SYM
A>>c -y -o testovr2.c testovrx.sym
A:C COM (User 0)
Hi-Tech C Compiler (CP/M-80) V3.09-7
Copyright (C) 1984-87 HI-TECH SOFTWARE
Updated from https://github.com/agn453/HI-TECH-Z80-C
TESTOVR2.C
TESTOVRX.SYM
A>testovr
A:TESTOVR COM
overlay test
hello from overlay 01
overlay return is 0
hello from overlay 01
overlay return is 0
hello from overlay 02
overlay return is 0
hello from overlay 01
overlay return is 0
A>
-
There are additional testing programs added to the test folder along with the output of a testing run in test/TEST.LOG that was generated on a CP/M 3 system.
-
Since wildcard expansion using _getargs() has been supported since v3.09-3, it is no longer necessary to use the -R option. This is now deprecated and ignored if you use it on the command line.
-
A new -H option may be specified to obtain help with the command-line options. It types out the system's OPTIONS file - so be sure this is copied from the dist folder to the system directory (usually drive A: in user area 0, and marked with the SYS and RO attributes).
Addressing some minor changes from issue #18 -
-
A description of the -E option was missing from the dist/OPTIONS help message file.
-
For CP/M emulators the options emitted by the compiler front-end have been adjusted to be consistently in uppercase.
-
Report an error if the OPTIONS file can't be found (for the -H help text).
I've re-issued this as a revised v3.09-7b release too.
The compiler front-end source in cpm/C.C has been updated so that it can be compiled to produce a Z280 MPU version. Co-existence with the Z80 version is now also possible due to the renaming of the various Z280 versions start-up modules to C280CPM.OBJ, D280CPM.OBJ and R280CPM.OBJ.
The original source-code to the C280 front-end is lost (not included with the UZI-280 distribution files) - so now Z280 users (like me) have the updated version supporting overlays etc. The original binary from the UZI-280 distribution is in the file z280dist/C280ORIG.COM
You'll find updated Z280 files in the z280dist folder. The z280dist/C280-8.COM is the new front-end and can be copied to the system drive as C280.COM. It tests for, and will only run on a Z280 MPU. Be sure to copy the start-up object modules (?280CPM.OBJ files) and the z280dist/LIB280*.LIB libraries to the system drive too.
This is also a new release milestone at v3.09-8.
- Minor change to allow size optimization (-O2 switch) for Z280.
Include an easy-download CP/M library file containing the binary distribution (all the files you need from the dist folder to get it running under CP/M).
Download this from
https://raw.githubusercontent.com/agn453/HI-TECH-Z80-C/master/htc-bin.lbr
and extract the files using one of the CP/M .LBR extraction tools, e.g. NULU.COM,
- Revert change defining "Z80" symbol back to the lowercase form "z80" in the main driver. There are some inconsistencies with how this is used in the other compiler modules - and in the library sourcefiles.
- Modify z280/OPTIMH.C and the front-end driver to select between size and speed optimisations. Previously some additional code was added to unroll calls to the csv() routine and jumps to cret() into inline-code. OPTIMH now accepts a -F option to include them (for faster code), else it omits them for reduced code-size. This is selected by using the -OF2 and -O2 option to the C280 front-end.
Up until now, I've kept various replaced files in this repository by renaming them to include a hash (or octothorpe) "#" character in their names. They have now been removed to avoid confusion. GitHub is already tracking changes for us - and comparing commits will show them from now on! If you need to refer to them you can download one of the previous releases.
Andrey Nikitin has made available a decompilation of the HI-TECH C V3.09 code generation program CGEN.COM based on work by himself and a group of Russian enthusiasts.
You'll find this at
https://github.com/nikitinprior/dcgen
Issue #24 raised a limitation with calling the bios() function to perform inter-bank memory moves using the CP/M 3 BIOS XMOVE and MOVE routines (function 29 and 25). I've updated the description for bios() above to document this and the SELMEM (function 27) restriction.
As per issue #25 - corrected the function prototype definitions in dist/TIME.H for ctime(), gmtime() and localtime(). Updated the htc-bin.lbr binary distribution library archive file too.
Andrea Nikitin has posted some more decompiled source-code for the C pre-processor (CPP.COM) and a revised pre-processor for the compiler that supports C++ style comments. You can get this from his GitHub repository at
https://github.com/nikitinprior/dcpp
I've added a CP/M library format file containing the HI-TECH C COMPILER V1.3 for Z80 CP/M in the historical folder. The files in historical/HTC13.LBR can be extracted using one of the CP/M library utilities like NULU.COM, and are from the original eight-inch floppy disk distribution media that I purchased in 1984.
Andrey Nikitin noticed that this version has an object file dump utility that can decode the HI-TECH C Z80 object file format. He's done a disassembly and reconstruction of this utility that you can get from his GitHub repository at
https://github.com/nikitinprior/ddump
Andrey Nikitin has managed to reconstruct the C language source for the object-code librarian program LIBR.COM by disassembling the program. You'll find this in his repository at
https://github.com/nikitinprior/dlibr
I've added a mktime() routine to the C library (LIBC.LIB and LIB280C.LIB) and consolidated the recent updates as a release v3.09-9.
mktime()
time_t mktime(struct tm *)
A routine to convert the broken out time (in the structure pointed
to by the parameter) into a time value representing the number
of seconds since the Unix epoch (since CP/M has no knowledge of the
local timezone - the time is assumed to be respresenting Universal
Coordinated Time or UTC). The value returned is the number of
seconds since midnight 01-Jan-1970.
This can be used (for example) to manipulate a file's time-stamp
or for computing time differences.
External declarations, as well as the tm structure definition, are
contained in the TIME.H include file. The tm structure includes
at least the following fields:
int tm_sec; /* seconds (0 - 60) */
int tm_min; /* minutes (0 - 59) */
int tm_hour; /* hours (0 - 23) */
int tm_mday; /* day of month (1 - 31) */
int tm_mon; /* month of year (0 - 11) */
int tm_year; /* year - 1900 */
int tm_wday; /* day of week (Sunday = 0) */
int tm_yday; /* day of year (0 - 365) */
int tm_isdst; /* is summer time in effect? */
-
Added a few missing bdos() function constants for CP/M 3, MP/M, ZSDOS and ZPM3.
-
I forgot to include the testing logs (in the test folder)
-
Add -Y description to dist/OPTIONS file.
Andrey Nikitin and Mark Ogden have been busy. They've reconstructed the C source-code for the Z80 code optimiser. You'll find it at
https://github.com/nikitinprior/doptim
Reported by Mark Ogden, the fgets() library routine (in LIBC.LIB and LIB280C.LIB) has been updated to prevent a character string terminator (NULL) from being written to outside the buffer bounds.
You can update just the object library file, or fetch and extract the files from the updated binary distribution from
https://raw.githubusercontent.com/agn453/HI-TECH-Z80-C/master/htc-bin.lbr
I've added a binary Z280 distribution library file too. Get it from
https://raw.githubusercontent.com/agn453/HI-TECH-Z80-C/master/z280bin.lbr
Just extract the contents to your system drive (A:) and set them with the SYS file attribute (so they are accessible from all user areas and via the CP/M 3 search path).
Mark Ogden has supplied some fixes to the C front-end so that it can process long command lines (particularly when wildcard expanded filename are specified). I've also added a minor change to the Z280 front-end to use the C280OPTS file when help (-H) is requested from the C280 command.
The new release V3.09-10 consolidates all the changes since V3.09-9. As usual, updated binary distribution libraries are available too.
A long-standing bug with the generation of Z80 assembly language output containing the C source-code as comments (when using the -S command-line option without optimisation) has been fixed. A replacement C309-10.COM (and C280-10.COM) is available - and the source-code in cpm/C390-10.C has been updated. Thanks to Andrey Nikitin for reporting this.
On the Internet archive WayBackMachine, I found some documentation describing the HI-TECH object-code (.OBJ) and symbol-table (.SYM) file formats in the ZIP archive symfiles.zip.
I've extracted the old MS-Word format files and converted them to ASCII text in the doc/OBJCODE.TXT and doc/SYMFILE.TXT files.
Passing a pointer to a function parameter can give rise to a warning
or compilation failure (with both operation: arguments redeclared
and argument list conflicts with prototype
error messages). One
such example is the qsort()
routine which needs a pointer to a
comparison routine as one of its arguments.
The bug can be worked around by using a typedef'ed parameter. The
header library prototype for qsort()
in STDLIB.H has been updated.
Thanks to Andrey Nikitin for the heads up on this bug.
NB: This issue has been resolved with the V3.09-11 release (see below).
The re-direction of error message output (from the C or C280 command) is currently broken. This is a side-effect of the fix to the pre-processor assembly language output. I've temporarily added a workaround to discard the error message redirect and only send error messages to the console device CON:. This works under CP/M and ZXCC emulation until I find a fix.
Both Andrey Nikitin and Mark Ogden have reconstructed the source code to the HI-TECH object code linker. You'll find it at
https://github.com/nikitinprior/dlink
and it can be compiled using gcc under Linux or CLANG under macOS to produce a executable that doesn't require Z80 emulation for cross-compilation.
Mark Ogden supplied the following detail regarding an issue with
code generation when casting a char
/unsigned char
to a
long
. The current compiler emits incorrect Z80 instructions.
The following snippet of code shows the problem and work-arounds.
void func(char *pia, unsigned char *pib) {
long a;
unsigned long b;
a = pia[1]; /* ok */
a = pib[1]; /* bad sets a = 0 */
b = pia[1]; /* bad sets b = 0 */
b = pib[1]; /* bad sets b = 0 */
/* work arounds */
/* for the simple assignment case use
* (int)pia[1], (unsigned)pia[1] or (unsigned)pib[1]
* dependent on whether you need sign extension
*
* for example -
*/
a = (unsigned) pib[1];
b = (int) pia[1];
b = (unsigned) pib[1];
/* if used in an expression you may also need to add
* another cast to (long) or (unsigned long)
* e.g. (unsigned long)(unsigned)
*/
}
For the three failing cases, the code generated is
ld l,(hl) ; picks up the char from the array
ld hl,0
ld d,l
ld e,l
whereas the correct optimised code should be
ld e,(hl)
ld hl,0
ld d,l
When the PIPEMGR I/O redirection is not detected, the standard files
for stdin, stdout and stderr now default to use the console CON:
device for compiler output. With I/O redirection, these use the
PIPEMGR pseudo device names RSX:
and ERR:
. This fixes the
issue with the compiler front-end error message output - so the temporary
work-around I made a few days ago has been removed.
This fix has changed the start-up modules CRTCPM.OBJ
and
RRTCPM.OBJ
to use a new _initrsx()
routine when PIPEMGR is
detected (the source is in the cpm/CLEANUP.C module of LIBC).
To indicate this fix is included, I've bumped the release to V3.09-11 and updated the binary distribution library files.
Fixed the order of modules in the C library (LIBC.LIB and LIB280C.LIB)
to prevent an undefined symbol:
error during linking.
Also added a new feature to the main front-end driver (C.COM) to accept
an -N
switch. This causes the resulting CP/M program to be
linked with a new start-up module NRTCPM.OBJ
. This provides a
minimal version of _getargs() instead of the standard enhanced default
version of_getargs(). Using -N
can significantly reduce
the size of the generated CP/M binary .COM file at the expense of
wild-card argument processing and command-line file redirection of
stdout and stdin (using the >file
, >>file
and <file
modifiers).
Phillip Stevens has submited updates to the floating point library
routines for acos()
, asin()
, atan()
, atan2()
,
cos()
, eval_poly()
, exp()
, float()
, log()
,
sin()
and sinh()
. These are from a backport of the
routines from the HI-TECH Z80 cross compiler V7.80pl2.
These fix -
-
some typos in the
atan()
coefficients - affectingacos()
andasin()
, -
improved accuracy of the polynomial estimation for
asin()
andacos()
by restricting the range of the polynomial in use, -
correct the sign of
atan2()
to reflect the standard usage, -
allow integer powers of negative bases, and
-
fixes to the intrinsic
float.as
floating point arithmetic functions.
These changes seem to provide more accurate results.
I've rebuilt the floating-point libraries (LIBF.LIB and LIB280F.LIB).
Please let me know if you notice any issues.
Phillip Stevens has back-ported some more of the V7.80pl2 library source.
A summary of the updates are -
-
float/LTOF.AS -
lbtof()
andabtof()
loaded wrong registers, andlltof()
improved conversion of long to float accuracy by up to 7 bits. -
gen/ATOL.C - recognise a
+
in a number constant (e.g.+123456L
). -
gen/BRELOP.AS, gen/FRELOP.AS, gen/LRELOP.AS - avoid the use of an
ex af af'
op-code (some CP/M systems use the alternate accumulator and flags register for interrupts without doing a push/pop to save it). -
gen/LONGJMP.AS - fixes.
-
gen/IDIV.AS - fixes.
-
plus add miscellaneous type casts and fix typos in cpm/ABORT.C, gen/RAND.C, gen/STRFTIME.C, stdio/FILBUF.C, stdio/GETW.C and stdio/UNGETC.C
Updated libraries (LIBC.LIB/LIBF.LIB and LIB280C.LIB/LIB280F.LIB) are in the dist folder and the binary distribution libraries.
The Z280 front-end C280.COM
has been modified to allow optimisation
of Z80 assembly language input files when either the -O2
or -OF2
options are specified. This uses the same OPTIMH
optimisation
rules as it uses when C280
compiles C programs.
This means a command like C280 -O2 -C MODULE.AS
will firstly
perform a source-code optimisation pass before invoking the assember
ZAS
. Testing this on the library build procedures squeezes
a few bytes from some of the modules.
If you wish to examine the result of the OPTIMH
pass, you can use
the -S
option to retain the optimised source in a file with
the .AS2
file type.
For example -
10E>c280 -s -o2 -v execl.as
A:C280 COM (User 0)
Hi-Tech Z280 C Compiler (CP/M-80) V3.09-13
Copyright (C) 1984-87 HI-TECH SOFTWARE
Updated from https://github.com/agn453/HI-TECH-Z80-C
0:A:OPTIMH EXECL.AS EXECL.AS2
71 bytes optimized away
122 bytes replaced
0:A:ZAS -J -OEXECL.OBJ EXECL.AS2
ERA M:$$EXEC.$$$
10E>
There's newly optimised Z280 object libraries (LIB280C.LIB and LIB280F.LIB)
and CP/M COM files in the z280dist folder, including the new V3.09-13
compiler front-end as C280-13.COM
(which you should copy and rename to
C280.COM
on the drive where you install the rest of the compiler files).
The Z280 binary distribution library has been updated too. Get it from
https://raw.githubusercontent.com/agn453/HI-TECH-Z80-C/master/z280bin.lbr
(If you're wondering - there's been no changes that affect the V3.09-12 Z80 release files).
Following on from Mark Ogden's consultations with John Elliott (the author of the ZXCC emulator and PIPEMGR), I have accepted a proposal from Mark Ogden to revert the interpretation of exact file sizes back to John Elliot's original DOS Plus interpretation.
As mentioned in the release v3.09-3 notes (above), there are two common conventions that can be used to record exact file sizes in CP/M 3 and DOS Plus.
- Record the number of bytes USED in the last sector (as used by DOS Plus)
or
- Record the number of UNUSED bytes in the last sector (used by ISX for ISIS emulation)
NB: The compiler release V3.09-13 was set to select this via
an EXACT
environment variable (defined in the 0:A:ENVIRON file).
This feature has been removed from the current release V3.09-14 (and
subsequent versions) and replaced with the following.
The start-up module (CRTCPM.OBJ) detects whether you are running the
compiler under CP/M 2 or CP/M 3 (or DOS Plus) and defaults a global
variable _exact
to enable DOS Plus interpretation of exact file
size.
To allow you to continue with the alternative ISX/ISIS interpretation, you can override the value of the global variable as follows -
#include <stdio.h>
extern char _exact;
..
int main(int argc, char ** argv)
{
_exact = 'I'; /* Enable ISIS exact file size */
..
}
The values of _exact
can also be only one of the following -
_exact = 'C'; /* No exact file size - like CP/M 2.2 uses */
or
_exact = 'D'; /* DOS Plus convention for exact file size */
The C library close() routine uses the selected exact file size mode to write the last record USED (DOS Plus) or UNUSED (ISIS) byte value to the directory entry.
Note: You need to take extra care if you're using an emulator like ZXCC
which has been updated to only support the DOS Plus
convention or the CP/M 2.2 interpretation (as per the original version
by John Elliott). Using the wrong mode will cause the file to be
extended or truncated in the last sector. The CP/M 2.2 mode causes
Ctrl-Z
to be written at the end of text files (as per the CP/M
convention).
The PIPEMGR source code has also been reverted to support only the
DOS plus and CP/M 2.2 file size convention. Piping a text concatenation
(using the >>
operator) will now write the extra text to the end
of the file (and before the Ctrl-Z
marker if the file has this in
the last sector). Files written to by PIPEMGR will use the DOS Plus
exact file size convention.
Mark Ogden has supplied a couple of fixes.
The first was a result of an incorrect assumption about a compiler optimisation from later versions of the HI-TECH Z80 cross-compiler. V3.09-x does not pass an initial function argument in the DE register. Only the longjmp() routine (in gen/LONGJMP.AS) is affected.
The second concerns a floating point division not detecting an underflow condition. The fldiv() routine (in float/FLOAT.AS) now detects this and returns a zero result.
Updated LIBC.LIB and LIBF.LIB contain the fixes (as well as the Z280 versions LIB280C.LIB and LIB280F.LIB), and I've bumped the release to V3.09-15.
As reported by Mark Ogden, there are a couple of bugs in the compiler driver program.
-
The first is regarding the use of the self-relocating (-A) option, where the code compiled for the linker should use the
cpm
psect segment (as per the relocatable start-up module RRTCPM.OBJ); and -
If you specify a .SYM file on the command line without specifying the overlay (-Y) option, the compiler driver tries to assemble a non-existing temporary file.
The latest compiler driver (in cpm/C309-16.C) addresses both by passing
the cpm
psect to the linker and ignores .SYM files unless the
overlay (-Y) option is specified.
The binaries for the compiler driver (in dist//C309-16.COM and z280dist/C280-16.COM) have been updated as well as the binary distribution library files to be release V3.09-16 (download link at the top of this README file).
The C library has had the ordering of modules adjusted to resolve an undefined symbol error (thanks to Mark Ogden for bringing this to my attention).
When the compiler driver was executed without producing an output file, it
left behind a stray $$EXEC.$$$
file on the current drive (or on the
temporary drive if you specified a TMP environment variable in your
0:A:ENVIRON.
file). This now fixed.
I've also bumped the version number to be release V3.09-17.
I've added a CP/M library format (.LBR) file containing the unmodified HI-TECH C COMPILER V3.09 for Z80 CP/M to the historical folder. The files in historical/Z80V309.LBR can be extracted using one of the CP/M library utilities like NULU.COM, or a Unix tool like lar,
The following updates have been made to the Z280 assembly language optimiser (OPTIMH.COM) -
-
Comments in the OPTIMH.C source file have been translated from German into English;
-
The counters for the additional byte and replaced counts are now more accurate;
-
The processing summary output now includes "speed" or "size";
-
The default filetype for the output file has been changed from .ASO to .AS2 to be consistent with the compiler behaviour when it optimises and saves the output assembler source (i.e compilation using the
-O2
or-OF2
with the-S
option); -
Some
call csv
andjp cret
speed optimisations were not being detected; and -
Assembly language statement labels on optimised code were being omitted in the output file. Processing now detects them and outputs them on a separate line prior to performing optimisation.
I've rebuilt the Z280 optimised libraries (LIB280C.LIB, LIB280F.LIB) and compiler frontend C280-17.COM using the updated optimiser - and these files are now available in both the z280dist folder and updated Z280 binary distribution at
https://raw.githubusercontent.com/agn453/HI-TECH-Z80-C/master/z280bin.lbr
Please raise an issue if you have any problems with them.
--
-- Tony Nicholson, Friday 15-Mar-2024
Footnotes
-
RunCPM is a multi-platform, portable, Z80 CP/M 2.2 emulator. It is actively maintained and available from https://github.com/MockbaTheBorg/RunCPM ↩
-
SIMH AltairZ80 is one of many computer system simulators from the Open SIMH Project (https://opensimh.org). It emulates 8080/Z80/8085 and MC68000 S-100 bus computers (like the MITS Altair with various interface cards and peripherals). You can obtain source-code for over 70 different systems from https://github.com/open-simh/simh or just the AltairZ80 specific simulator and various prebuilt CP/M operating system kits from Peter Schorn's site at https://schorn.ch/altair.html ↩
-
ZXCC is a CP/M 2/3 emulator for cross compiling and running most CP/M binary command files under Linux/Unix/macOS and Microsoft Windows. It was authored by John Elliott and with various updates may be obtained from https://github.com/agn453/ZXCC ↩
-
The newsgroup comp.os.cpm can be accessed via a USENET provider like www.eternal-september.org using newsreader software like Mozilla Thunderbird. An archived copy of the original thread can be read via Google Groups at https://groups.google.com/forum/#!topic/comp.os.cpm/V9Qwoc3--Ak - but since February 2024 you will need to access comp.os.cpm via a USENET provider to be able to post messages). ↩