The following code works as expected when compiled with gcc, however when compiled with sdcc (4.2.0) the conditional test (x2 + y2 >= 800) doesn't ever appear to be evaluated as true (as only spaces are printed).
#include <stdio.h>
void main()
{
const char* a[8] = {"\x1b[0m", "\x1b[0;41m", "\x1b[0;42m", "\x1b[0;43m",
"\x1b[0;44m", "\x1b[0;45m", "\x1b[0;46m", "\x1b[0;47m"};
int x, y;
int x2, y2, x0, y0, i, n;
char c;
int xa = -500;
int xb = 300;
int ya = 240;
int yb = -250;
int xd = 7;
int yd= 15;
int m = 20;
y0 = ya;
while (y0 > yb)
{
x0 = xa;
while (x0 < xb)
{
y = 0;
x = 0;
c = ' ';
n = 0;
i = 0;
while (i < m)
{
x2 = (x * x) / 200;
y2 = (y * y) / 200;
if ((x2 + y2) >= 800)
{
if (i > 9) c = '0'; else c = '0' + i;
if (i > 7) n = 7; else n = i;
i = m;
}
y = (x * y / 100 + y0);
x = (x2 - y2 + x0);
/* if ((x > 16383) || (y > 16383)) printf(" %d %d ", x ,y); */
i++;
}
printf("%s%c", a[n], c);
x0 = x0 + xd;
}
printf("%s\n", a[0]);
y0 = y0 - yd;
}
}
This behaviour is consistent when using sdcc (4.2.0) on Debian (bookworm) and sdcc 4.0.0 on Debian(bulseye).
$ sdcc -mz80 --no-std-crt0 --data-loc 0 sdc-crt0-args.rel sdc-cpm.rel sdc-mandlebrot.c
$ sdobjcopy -Iihex -Obinary --gap-fill 0 sdc-mandlebrot.ihx sdc-mandlebrot.com
However if I change int x, y; to long x, y; it works, even though neither x or y ever exceed 16K.
Am I doing something wrong, or is there a bug with sdcc?
SDCC uses 16 bits for an
int, that much you found out. However, you have an overflow in your code.This is the relevant excerpt, all variables are
int:Because of the products being actually squares,
x2andy2can never be negative.To get a sum of at least 800, we can differentiate these corner cases:
x2is 800 or more,y2is 0.x2is 0,y2is 800 or more.x2is 400 or more,y2is 400 or more.For any of
x2ory2to be 400 or more, the square ofxory, respectively, needs to be 400 * 200 = 80000 or more.This is clearly more than an
intcan represent. The highest value is 32767, divided by 200 will result in 163. So the maximum ofx2 + y2is 2 * 163 = 326.Why does it work, if you declare
xandyaslong?Well, by integer promotion the squares are
long, too, and the quotient will be correct. The assignments tox2andy2silently cast down toint, but the result is small enough.This gives a hint how to have an intermediate square in
long. For this, at least one of the operands needs to be long. But to make it crystal clear to future readers, use explicit casts instead of implicit casts, and don't forget the constants.Pro-tip: These expressions look ugly. You might want to try the compiler's optimization capability to inline. For this, define a local helper function. You might want to comment the function extensively.
Final note: While SDCC is a project driven by voluntaries, and may therefore have some bugs, it is a matured system built by experienced developers. So this case is not a bug in SDCC, but a bug in your code.
I found some compiler bugs in my career, even in expensive and validated closed source compilers, but only every so many years. The vast majority of bugs were my own, more than 99,99%. So I never assume a compiler bug, if something does not work as expected, but always a user bug. You should develop that mind set, too.
Only if you can prove by the generated assembly code, you can point to the compiler.