[net.micro.amiga] C compiler bugs

metcalf@pavepaws.UUCP (07/18/86)

I've been doing some work lately on writing machine- and compiler-independent
graphics code, and as a result came up with a variety of bugs for Lattice,
Megamax, and Aztec compilers (Lattice and Aztec are available both on Amiga
and ST; Megamax is an ST-only phenomenon).  Compiler bugs are one of the
most annoying facts of life that a software writer has to live with; I
thought I could spare some of you some grief by listing a few of the
interesting ones I've found.

Lattice:  this was my favorite bug.  It seems that my release of the Lattice
compiler (on the Amiga, 3.03) has a very small problem in its floating-
point library; if a float becomes almost equal to +/-2.000000 (in fact, if
its binary is 0x40000002 or 0xC0000002) then all multiplications using it
fail.  Multiplying this value of -2 by itself gives -2, for example; and
multiplying it by anything else gives 0.  This is not a good thing for
people who need even vaguely reliable floating-point math.  I stumbled
across this strange bug purely by chance, and it took a LONG time to track
it down.  Here's a piece of code to show what I mean:

main()
{
	float f;
	*(long *)&f = 0xc0000002;
	printf("%f * %f = %f!!\n", f, f, f * f);
}

If you get "-2 * -2 == -2!!" then your compiler is in trouble...  and there
aren't any easy fixes.  Using double math doesn't help, since Lattice
(bizarrely) casts floats to double just to multiply; the double killer-value
is 0xc000000040000000.  All you can do is pray your code doesn't hit it.
Mine does, with dismal regularity; Lattice users lose.

Megamax:  my version (1.0) has a little float-math trouble as well.  If you
have some expression whose value is 0, and negate it and assign it to a
variable, hey presto! its value is -2.  Thus "float a = 0; a = -a" will
set a to -2.  (Compiler bugs seem to like this number..)  This problem
at least has a workaround; if you say "a = 0 - a" the problem doesn't 
appear.  Also a problem, and equally annoying, is that floating-point
comparisons don't work very well.  Try, for example, "printf("%d\n",2.0<-1.0)".
It prints 1.  This (you may notice) is not correct.  I fixed this on
an ad hoc basis in my code by checking -2 > 1.0 (which does work).

Aztec 3.20a:  when I had fixed the Megamax bug, I wiped the sweat from my
brow and turned to my trusty Aztec Manx compiler to recompile the altered
source.  What should I find but the program failing again!  After more work,
I finally traced it down to the expression "a[i] = 0 - b[i]" (yes, one of
the lines I had "fixed" so Megamax would accept it...).  It seems that 
the Aztec compiler has trouble with expressions of this form, where one
subscripted float is subtracted from a constant and assigned to another
subscripted float (it may be a more general problem, but variable subscripts
at least are required).  The result is that b[i] is ignored, and a[i] is
assigned the constant.  Why this is I don't know; my knowledge of 68000/68881
is weak but the assembler output looked ok to me.  The workaround for this
one turned out to be assigning "0 - b[i]" to an intermediate variable, and
assigning that to a[i].  So it goes...  nobody can do float math right.

Another smaller bug fell out after I had all of these in order; one of the
Lattice compiler's infamous CXERR messages (#25) resulted when compiling a 
perfectly innocuous piece of code.  It appears that Lattice has trouble
accepting more "register" declarations than it has registers!  Rather than
ignoring subsequent register declarations, it starts handing out non-
existent registers.  Combining my knowledge of CXERR meanings (from an Amiga
Mail issue) with the fact that Lattice only allows four address and six
data register values let me fix that one, but it was an annoyance.  (If
anyone wants the list of CXERR meanings, let me know and I'll type them in.)

Hopefully this will help somebody, somewhere.  Five compiler bugs in two
days was a little too much, and hopefully some programmer can come a little
closer to maintaining his sanity as a result of this posting.  Good luck...
Chris Metcalf (metcalf@yale.ARPA)		   ...!decvax!yale!metcalf
metcalf@yalecs.BITNET				...!ihnp4!hsi!yale!metcalf

braner@batcomputer.TN.CORNELL.EDU (braner) (07/22/86)

[]

Yes, nobody seems to get floating-point right!!
I am very disgusted because I'm into scientific computation.
I am very anxious to see a FP chip connected to the ST!!

I am here reposting a fix to the Megamax FP comparision bug.
Since I've done this it has worked fine. But beware: Megamax
gives you NO error messages upon division-by-0 and similar
errors - you just get garbage, and occasionally crashes.

----------------------------------------------------------------
    I took a close look, using the mmdis disassembler,
at the double.o file (extracted from the double.l library with
the mmlib program.) Only one (1) byte is wrong, looks like a typo.
In the routine _fcom, where it says BSET #31,D1 it should say D2.

    To patch it, use the following 'bth' and 'htb' programs:
Bth.prg translates the bytes of any file into both hex and
ASCII representation. The output can be edited on any text editor,
and then fed into the htb program to get it back
into binary. (Only the hex part needs to be changed, the ASCII part
is treated as a comment.) (Yes, Virginia, there is no way to assemble
the output of mmdis!)

    In this case, tell bth.prg to read double.l. In the output file,
look (on line 47) for the sequence "46 82 46 83 08 C1".
Change the C1 to C2. Run it through htb.prg to create newdbl.l,
and your done. From now on, mmlink yourprog.o newdbl.l.
You can fix the single-precision float.o in syslib the same way -
look for the same sequence and do the same change.

--------------------------------------------------------------------
/* BTH - a program to create a hex file from a binary file.
   by Moshe braner, 860613.
*/

#include <stdio.h>

#define  hinibble(b)  (((b)>>4) & 0x0F)
#define  lonibble(b)  ((b) & 0x0F)
#define  tohex(b)     ((b)<0xA ? ((b)+'0') : ((b)-0xA+'A'))

main() {
	register int c, b, i, t;
	int  n, m;
	FILE *infp, *outfp;
	char infname[80], outfname[80];
	char col[16];

	printf("\n\nBinary To Hex conversion program.  MB 8606.\n\n");
	printf("Enter name of source file: ",stdout);
	gets(infname);
	/* open file with "br" to avoid skipping of '\r's
	   by Megamax library
	*/
	if ((infp=fopen(infname,"br")) == NULL) {
		printf("cannot open source file!\n");
		exit(0);
	}
	printf("Enter name of output file: ");
	gets(outfname);
	if ((outfp=fopen(outfname,"w")) == NULL) {
		printf("cannot open output file!\n");
		exit(0);
	}
	m = 0;
	for (n=0; ; n++) {
		b=getc(infp);
		if (b != EOF)
			col[m++] = b;
		if ((b==EOF && m>0) || m>=16) {
			for (i=0; i<m; i++) {
				t = col[i];
				c = tohex(hinibble(t));
				putc(c, outfp);
				c = tohex(lonibble(t));
				putc(c, outfp);
				putc(' ', outfp);
			}
			for (; i<16; i++) {
				putc(' ', outfp);
				putc(' ', outfp);
				putc(' ', outfp);
			}
			putc(' ', outfp);
			putc('*', outfp);
			putc(' ', outfp);
			for (i=0; i<m; i++) {
				c = col[i];
				if (c>=' ' && c<='~')
					putc(c, outfp);
				else
					putc('.', outfp);
			}
			putc('\n', outfp);
			m = 0;
		}
		if (b == EOF)
			break;
	}
	printf("\nRead and translated %d bytes.\n", n);
}

------------------------------------------------------------------

/* HTB - program to create a binary file from a hex file.
   by Moshe braner, 860606.
*/

#include <stdio.h>

#define  ishex(c)  ((c>='0'&&c<='9')||(c>='A'&&c<='F')||(c>='a'&&c<='f'))

/* the following macro assumes ishex(c) and ASCII: */
#define  hexval(c) ((c<'A')?(c-'0'):((c<'a')?(c-'A'+0xA):(c-'a'+0xA)))

main() {
	register int c, b, n;
	FILE *infp, *outfp;
	char infname[80], outfname[80];

	printf("\n\nHex To Binary conversion program.  MB 8606.\n\n");
	printf("Enter name of source file: ",stdout);
	gets(infname);
	if ((infp=fopen(infname,"r")) == NULL) {
		printf("cannot open source file!\n");
		exit(0);
	}
	printf("Enter name of output file: ");
	gets(outfname);
	/* open file with "bw" to avoid translation of '\n's
	   to \r\n by Megamax library
	*/
	if ((outfp=fopen(outfname,"bw")) == NULL) {
		printf("cannot open output file!\n");
		exit(0);
	}
	n = 0;
	while ((c=getc(infp)) != EOF) {
		/* rest of line after '*' is a comment: */
		if (c == '*') {
			while ((c=getc(infp)) != '\n' && c != EOF);
			continue;
		}
		/* read one or two characters for one output byte: */
		if (ishex(c)) {
			b = hexval(c);
			if ((c=getc(infp)) == EOF)
				break;
			if (ishex(c))
				b = 16*b + hexval(c);
			if (putc(b,outfp) != b) {
				printf("error writing file!\n");
				exit(0);
			}
			n++;
		}
	}
	printf("\nWrote %d bytes.\n", n);
}
--------------------------------------------------------------------
    Another Megamax bug: after you open a file for reading using
fopen("r"), you SOMETIMES get some extra garbage at the end of the
file. It is some characters that appear near the end of the file,
repeated in a garbled order. I got around it by using fopen("br")
and doing the CRLF-->LF conversion myself.
--------------------------------------------------------------------
    While we're at it, did anybody find out how to get IO
redirection to work right for C programs compiled with Megamax
and used under Micro C-Shell? (Even though it works when using
a program as .ttp from the desk top, the same program when
invoked from microcsh as "progname > outfile" gives two bombs.)
Somebody from Megamax posted a claim that it works, but it does NOT!
(I use a 1040STf, completely off a (RA)M-disk. Monochrome.)
I wrote Megamax (twice) and Beckemeyer, but got no answers.
I also wish microcsh was not so DRI oriented - e.g. 'echo' is
a separate program on disk, but a DRI-specific 'cc' is taking up
space in the microcsh core. How about having an environment variable
that tells microcsh which compiler is used? (and activates the
appropriate IO redirection scheme...)
--------------------------------------------------------------------

- Moshe Braner

Corson Hall, Cornell University, Ithaca NY 14853
(607) 272-3487

For electronic mail, my address is:

	braner@amvax.tn.cornell.edu   (ARPANET)
	braner%amvax.tn.cornell.edu@WISCVM.BITNET (Bitnet)
	{decvax,ihnp4,cmcl2,vax135}!cornell!amvax!braner (USENET)  
------------------------------------------------------------------

braner@batcomputer.TN.CORNELL.EDU (braner) (07/27/86)

[]

The compiler bugs (especially in the floating-point package) keep
wasting my time... - here are some tidbits that might save YOU some time:

The Megamax library does not have a fabs() function, and abs()
does not always work right for FP. So I added this to math.h:

	#define fabs(x) (((x)<0.0)?(-(x)):(x))

The transcendental functions do NOT warn you if the argument passed to
them is illegal. You get GARBAGE (and you don't even know it!), or it
crashes, or (in the case of log(0)) it HANGS!

I now use LOG() and SQRT() instead of log() and sqrt(). The following
functions were also added to math.h:

	double LOG(x)
		double x;
	{
		if (x <= 0.0) {
			fprintf(stderr,"illegal arg for log()!\n");
			exit(0);
		}
		x = log(x);
		return (x);
	}

	double SQRT(x)
		double x;
	{
		if (x < 0.0) {
			fprintf(stderr,"illegal arg for sqrt()!\n");
			exit(0);
		}
		x = sqrt(x);
		return (x);
	}

If your program gets into an infinite loop, or just a loop that's too long,
there is no way to stop it unless it's doing I/O. To enable that, add the
statement "keybrk();" here and there in your loops, and #include the
following file at the top of your source:

	/* Check if a key has been pressed, and if it was
	 * a control-C print message and exit
	 */

	#include <osbind.h>

	keybrk()
	{
		if (Cconis()) {				/* any key pressed? */
			if ((Crawcin()&0x7F) == 0x03) {	/* is it ctrl-C?    */
				fputs("\nbreak!\n\n", stderr);
				exit(0);
			}
		}
	}

Finally, the simple random-number generator, defined as a macro in stdio.h:

	extern long _seed;
	#define srand(x) _seed = x;
	#define rand() (int)(_seed * 6907 + 130253)

is no good because a call to rand() does not update _seed, so you keep
getting the same nonrandom number again and again! Change to:

	#define rand() (int)(_seed = (_seed * 6907 + 130253))

Don't forget to do an srand(SEED), with SEED some integer, or else you'll
get the same sequence each time you run the program. For an unpredictable
sequence (good in games, for example) use the current (encoded!) time
as SEED!

If you want a floating-point version, returning a number between 0 & 1:

	#define RND  ((double)(rand())/65536.0 + 0.5)

(But I would NOT recommend this kind of random number generator for
 serious statistical purposes!)

   ------------------------------------------------------------------
	- Moshe Braner

	Corson Hall, Cornell University, Ithaca NY 14853
	(607) 272-3487
	braner@amvax.tn.cornell.edu   (ARPANET)
	braner%amvax.tn.cornell.edu@WISCVM.BITNET (Bitnet)
	{decvax,ihnp4,cmcl2,vax135}!cornell!amvax!braner (USENET)  
   ------------------------------------------------------------------