The Mandelbrot set "lives" in a two-dimensional grid between -2 and +1 on the x-axis, and between 1 and -1 on the y-axis, which is really plotting imaginary numbers, so between i and -i. So it lives in a pretty small space, with almost all of its coordinates being fractional values less than one. Plus it's computationally exhaustive, so a hardware floating point unit would be nice.
BDS C doesn't support floating point types, which is OK because using them probably would suck without a math co-processor anyway. So we can shift decimal places and use fixed-point math. But even that is limited, because BDS C doesn't support long types either.
However, there are some libraries that allow the use of structures to do floating-point and long integer math. They don't supply the types, but they do allow conversion using byte values cast as chars. As I said, it's hackish.
I chose the long hack, 'cause I think it's best suited to an 8-bit processor. And the output is in ASCII, making an image with characters. It's actually displaying the set by showing the number of iterations required to rule-out a value of the set, making the image shown as a negative.
Hope you enjoy it.
To build, you'll need my program, Shostak's long library and the make file. Copy-and-paste each of these three files onto your BDS C disk and build using this command:
SUBMIT MAKE MANDELBR
Mandelbr.c:
- Code: Select all
/* Mandelbr.c */
/* */
/* Generates ASCII output of the Mandelbrot set */
/* using (long) integer math from the BDS C */
/* library that represents longs as char[4] */
/* */
/* Written by Wayne Parham */
#include <stdio.h>
#define DISPCOLS 80
#define DISPROWS 23
#define INIT_LEFT -560 /* (x size 800, 80 steps) */
#define INIT_RIGHT 240
#define INIT_TOP 220 /* (y size 460, 23 steps) */
#define INIT_BOTTOM -240
#define INIT_X_INCR 10 /* (step ratio 2:1, y:x ) */
#define INIT_Y_INCR 20
#define INIT_MAX_ITER 50
char disbuf[DISPCOLS * DISPROWS];
void mandelbrot( boundL, boundR, boundT, boundB,
x_incr, y_incr, max, disp )
char boundL[4];
char boundR[4];
char boundT[4];
char boundB[4];
char x_incr[4];
char y_incr[4];
char max[4];
int disp;
{
char c[4];
char n[4];
char cx[4];
char cy[4];
char zx[4];
char zy[4];
char zxx[4];
char zyy[4];
char zxzy[4];
char zxxpluszyy[4];
char zxxminuszyy[4];
char p;
int i;
/* for( cy=boundT; cy > boundB; cy -= y_incr ) { */
i = 0; /* for( cx=boundL; cx < boundR; cx += x_incr ) { */
for( lassign( cy, boundT ); lcomp( cy, boundB ) == 1; lsub( cy, cy, y_incr ) ) {
for( lassign( cx, boundL ); lcomp( cx, boundR) == -1; ladd( cx, cx, x_incr ) ) {
itol( n, 0 ); /* n = 0; */
itol( zy, 0 ); /* zy = 0; */
itol( zx, 0 ); /* zx = 0; */
p = ' ';
while( lcomp( n, max ) == -1 ) { /* while( n < max ) { */
itol( c, 200 );
lmul( zxx, zx, zx );
ldiv( zxx, zxx, c ); /* zxx = zx * zx / 200; */
lmul( zyy, zy, zy );
ldiv( zyy, zyy, c ); /* zyy = zy * zy / 200; */
itol( c, 800 );
ladd( zxxpluszyy, zxx, zyy );
if( lcomp( zxxpluszyy, c ) == 1 ) { /* if( (zxx + zyy) > 800 ) { */
itol( c, 9 );
if( lcomp( n, c ) == 1 ) { /* if( n > 9 ) { */
p = '0'; /* (char) p = '0' */
} else { /* } else { */
p = ltou(n) + 48; /* (char) p = n + 48 */
} /* } (an ASCII conversion) */
lassign( n, max ); /* n = max; */
}
itol( c, 100 );
lmul( zxzy, zx, zy );
ldiv( zxzy, zxzy, c); /* zy = zx * zy / 100 + cy; */
ladd( zy, zxzy, cy );
lsub( zxxminuszyy, zxx, zyy );
ladd( zx, zxxminuszyy, cx ); /* zx = zxx - zyy + cx; */
itol( c, 1 );
ladd( n, n, c ); /* n++; */
}
disbuf[i] = p;
i++;
if( disp )
putchar( p );
}
if( disp )
putchar( '\n' );
}
}
void display() {
int i,x,y;
i = 0;
putchar( '\n' );
for( y=0; y < DISPROWS; y++ ) {
for( x=0; x < DISPCOLS; x++ ) {
putchar( disbuf[i] );
i++;
}
putchar( '\n' );
}
}
char getcmd() {
char cmd;
cmd = getchar();
putchar( 0x08 );
switch( cmd ) {
case 'A' :
case 'a' :
puts( "Entering auto mode." );
break;
case 'L' :
case 'l' :
puts( "Moving left and re-rendering." );
break;
case 'R' :
case 'r' :
puts( "Moving right and re-rendering." );
break;
case 'U' :
case 'u' :
puts( "Moving up and re-rendering." );
break;
case 'D' :
case 'd' :
puts( "Moving down and re-rendering." );
break;
case '.' :
puts( "Re-centering and re-rendering." );
break;
case '+' :
puts( "Zooming in and re-rendering." );
break;
case '-' :
puts( "Zooming out and re-rendering." );
break;
case 'X' :
case 'x' :
puts( "Enjoy your day!" );
break;
default :
break;
}
return( cmd );
}
void autorender( boundL, boundR, boundT, boundB, x_incr, y_incr, max )
char boundL[4];
char boundR[4];
char boundT[4];
char boundB[4];
char x_incr[4];
char y_incr[4];
char max[4];
{
int i, move, setnum, setratio, plot_points;
char maxdist[4];
char twf[4];
char ten[4];
char five[4];
char two[4];
char one[4];
itol( maxdist, 1280 );
itol( twf, 25 );
itol( ten, 10 );
itol( five, 5 );
itol( two, 2 );
itol( one, 1 );
plot_points = DISPCOLS * DISPROWS;
while( 1 ) {
for( i=0; i < plot_points; i++ ) {
if( disbuf[i] == ' ' ) {
setnum++;
}
}
setratio = 100 * setnum / plot_points;
if( (setratio > 90) || (setratio < 10) ) {
itol( boundL, INIT_LEFT );
itol( boundR, INIT_RIGHT );
itol( boundT, INIT_TOP );
itol( boundB, INIT_BOTTOM );
itol( x_incr, INIT_X_INCR );
itol( y_incr, INIT_Y_INCR );
itol( max, INIT_MAX_ITER );
} else {
move = rand() % 6;
switch( move ) {
case 0 :
if( lcomp( x_incr, one ) == 1 ) { /* if( x_incr > 1 ) { */
if( lcomp( x_incr, five ) != 0 ) { /* if( x_incr != 5 ) { */
ldiv( x_incr, x_incr, two ); /* x_incr = x_incr / 2; */
ldiv( y_incr, y_incr, two ); /* y_incr = y_incr / 2; */
ldiv( boundL, boundL, two ); /* boundL = boundL / 2; */
ldiv( boundR, boundR, two ); /* boundR = boundR / 2; */
ldiv( boundT, boundT, two ); /* boundT = boundT / 2; */
ldiv( boundB, boundB, two ); /* boundB = boundB / 2; */
} else { /* } else { */
lmul( x_incr, x_incr, ten ); /* */
ldiv( x_incr, x_incr, twf ); /* x_incr = x_incr * 10 / 25; */
lmul( y_incr, y_incr, ten ); /* */
ldiv( y_incr, y_incr, twf ); /* y_incr = y_incr * 10 / 25; */
lmul( boundL, boundL, ten ); /* */
ldiv( boundL, boundL, twf ); /* boundL = boundL * 10 / 25; */
lmul( boundR, boundR, ten ); /* */
ldiv( boundR, boundR, twf ); /* boundR = boundR * 10 / 25; */
lmul( boundT, boundT, ten ); /* */
ldiv( boundT, boundT, twf ); /* boundT = boundT * 10 / 25; */
lmul( boundB, boundB, ten ); /* */
ldiv( boundB, boundB, twf ); /* boundB = boundB * 10 / 25; */
}
}
break;
case 1 :
if( lcomp( x_incr, maxdist ) == -1 ) { /* if( x_incr < 1280 ) { */
if( lcomp( x_incr, two ) != 0 ) { /* if( x_incr != 2 ) { */
lmul( x_incr, x_incr, two ); /* x_incr = x_incr * 2; */
lmul( y_incr, y_incr, two ); /* y_incr = y_incr * 2; */
lmul( boundL, boundL, two ); /* boundL = boundL * 2; */
lmul( boundR, boundR, two ); /* boundR = boundR * 2; */
lmul( boundT, boundT, two ); /* boundT = boundT * 2; */
lmul( boundB, boundB, two ); /* boundB = boundB * 2; */
} else { /* } else { */
lmul( x_incr, x_incr, twf ); /* */
ldiv( x_incr, x_incr, ten ); /* x_incr = x_incr * 25 / 10; */
lmul( y_incr, y_incr, twf ); /* */
ldiv( y_incr, y_incr, ten ); /* y_incr = y_incr * 25 / 10; */
lmul( boundL, boundL, twf ); /* */
ldiv( boundL, boundL, ten ); /* boundL = boundL * 25 / 10; */
lmul( boundR, boundR, twf ); /* */
ldiv( boundR, boundR, ten ); /* boundR = boundR * 25 / 10; */
lmul( boundT, boundT, twf ); /* */
ldiv( boundT, boundT, ten ); /* boundT = boundT * 25 / 10; */
lmul( boundB, boundB, twf ); /* */
ldiv( boundB, boundB, ten ); /* boundB = boundB * 25 / 10; */
}
}
break;
case 2 :
lsub( boundL, boundL, x_incr ); /* boundL = boundL - x_incr; */
lsub( boundR, boundR, x_incr ); /* boundR = boundR - x_incr; */
break;
case 3 :
ladd( boundL, boundL, x_incr ); /* boundL = boundL + x_incr; */
ladd( boundR, boundR, x_incr ); /* boundR = boundR + x_incr; */
break;
case 4 :
ladd( boundT, boundT, y_incr ); /* boundT = boundT + y_incr; */
ladd( boundB, boundB, y_incr ); /* boundB = boundB + y_incr; */
break;
case 5 :
lsub( boundT, boundT, y_incr ); /* boundT = boundT - y_incr; */
lsub( boundB, boundB, y_incr ); /* boundB = boundB - y_incr; */
break;
}
}
mandelbrot( boundL, boundR, boundT, boundB, x_incr, y_incr, max, 0 );
display();
}
}
void instructions() {
puts( "\n" );
puts( "The Mandelbrot set includes all two-dimensional c values for which the function\n" );
puts( "z = z^2 + c does not diverge when iterated from z = 0. It was named in tribute\n" );
puts( "to the mathematician Benoit Mandelbrot, a pioneer of fractal geometry. He was\n" );
puts( "first to see a visualization of the set on March 1, 1980, at IBM's Thomas J.\n" );
puts( "Watson Research Center in Yorktown Heights, New York.\n\n" );
puts( "The Mandelbrot set has its origin in complex dynamics, a field first\n" );
puts( "investigated by the French mathematicians Pierre Fatou and Gaston Julia at the\n" );
puts( "beginning of the 20th century. This fractal was first defined and drawn in 1978\n" );
puts( "by Robert W. Brooks and Peter Matelski as part of a study of Kleinian groups.\n\n" );
puts( "Mandelbrot studied the parameter space of quadratic polynomials in an article\n" );
puts( "that appeared in 1980. The mathematical study of the Mandelbrot set really\n" );
puts( "began with work by the mathematicians Adrien Douady and John H. Hubbard (1985),\n" );
puts( "who established many of its fundamental properties and named the set in honor\n" );
puts( "of Mandelbrot for his influential work in fractal geometry.\n\n" );
puts( "This program displays the set on an ASCII terminal, much like the earliest\n" );
puts( "computers did. It is a throwback to the computing technologies of yesteryear.\n" );
puts( "It will display the set and then allow you to zoom or scroll. You can zoom with\n" );
puts( "'+' or '-' or move in any direction with 'u', 'd', 'l' or 'r'. Enter '.' to\n" );
puts( "reset the view. Set auto mode with the 'a' key.\n\n" );
return;
}
void main() {
int changed;
int running;
char maxdist[4];
char twf[4];
char ten[4];
char five[4];
char two[4];
char one[4];
char boundL[4];
char boundR[4];
char boundT[4];
char boundB[4];
char x_incr[4];
char y_incr[4];
char max[4];
char* disbuf;
char cmd;
itol( maxdist, 1280 );
itol( twf, 25 );
itol( ten, 10 );
itol( five, 5 );
itol( two, 2 );
itol( one, 1 );
itol( boundL, INIT_LEFT );
itol( boundR, INIT_RIGHT );
itol( boundT, INIT_TOP );
itol( boundB, INIT_BOTTOM );
itol( x_incr, INIT_X_INCR );
itol( y_incr, INIT_Y_INCR );
itol( max, INIT_MAX_ITER );
instructions();
srand();
mandelbrot( boundL, boundR, boundT, boundB, x_incr, y_incr, max, 1 );
running = 1;
while( running ) {
cmd = getcmd();
changed = 1;
switch( cmd ) {
case 'A' :
case 'a' :
autorender( boundL, boundR, boundT, boundB, x_incr, y_incr, max );
break;
case 'X' :
case 'x' :
running = 0;
break;
case 'L' :
case 'l' :
lsub( boundL, boundL, x_incr ); /* boundL = boundL - x_incr; */
lsub( boundR, boundR, x_incr ); /* boundR = boundR - x_incr; */
break;
case 'R' :
case 'r' :
ladd( boundL, boundL, x_incr ); /* boundL = boundL + x_incr; */
ladd( boundR, boundR, x_incr ); /* boundR = boundR + x_incr; */
break;
case 'U' :
case 'u' :
ladd( boundT, boundT, y_incr ); /* boundT = boundT + y_incr; */
ladd( boundB, boundB, y_incr ); /* boundB = boundB + y_incr; */
break;
case 'D' :
case 'd' :
lsub( boundT, boundT, y_incr ); /* boundT = boundT - y_incr; */
lsub( boundB, boundB, y_incr ); /* boundB = boundB - y_incr; */
break;
case '.' :
itol( boundL, INIT_LEFT );
itol( boundR, INIT_RIGHT );
itol( boundT, INIT_TOP );
itol( boundB, INIT_BOTTOM );
itol( x_incr, INIT_X_INCR );
itol( y_incr, INIT_Y_INCR );
break;
case '+' :
if( lcomp( x_incr, one ) == 1 ) { /* if( x_incr > 1 ) { */
if( lcomp( x_incr, five ) != 0 ) { /* if( x_incr != 5 ) { */
ldiv( x_incr, x_incr, two ); /* x_incr = x_incr / 2; */
ldiv( y_incr, y_incr, two ); /* y_incr = y_incr / 2; */
ldiv( boundL, boundL, two ); /* boundL = boundL / 2; */
ldiv( boundR, boundR, two ); /* boundR = boundR / 2; */
ldiv( boundT, boundT, two ); /* boundT = boundT / 2; */
ldiv( boundB, boundB, two ); /* boundB = boundB / 2; */
} else { /* } else { */
lmul( x_incr, x_incr, ten ); /* */
ldiv( x_incr, x_incr, twf ); /* x_incr = x_incr * 10 / 25; */
lmul( y_incr, y_incr, ten ); /* */
ldiv( y_incr, y_incr, twf ); /* y_incr = y_incr * 10 / 25; */
lmul( boundL, boundL, ten ); /* */
ldiv( boundL, boundL, twf ); /* boundL = boundL * 10 / 25; */
lmul( boundR, boundR, ten ); /* */
ldiv( boundR, boundR, twf ); /* boundR = boundR * 10 / 25; */
lmul( boundT, boundT, ten ); /* */
ldiv( boundT, boundT, twf ); /* boundT = boundT * 10 / 25; */
lmul( boundB, boundB, ten ); /* */
ldiv( boundB, boundB, twf ); /* boundB = boundB * 10 / 25; */
}
} else {
printf ( "\rMaximum zoom reached. " );
}
break;
case '-' :
if( lcomp( x_incr, maxdist ) == -1 ) { /* if( x_incr < 1280 ) { */
if( lcomp( x_incr, two ) != 0 ) { /* if( x_incr != 2 ) { */
lmul( x_incr, x_incr, two ); /* x_incr = x_incr * 2; */
lmul( y_incr, y_incr, two ); /* y_incr = y_incr * 2; */
lmul( boundL, boundL, two ); /* boundL = boundL * 2; */
lmul( boundR, boundR, two ); /* boundR = boundR * 2; */
lmul( boundT, boundT, two ); /* boundT = boundT * 2; */
lmul( boundB, boundB, two ); /* boundB = boundB * 2; */
} else { /* } else { */
lmul( x_incr, x_incr, twf ); /* */
ldiv( x_incr, x_incr, ten ); /* x_incr = x_incr * 25 / 10; */
lmul( y_incr, y_incr, twf ); /* */
ldiv( y_incr, y_incr, ten ); /* y_incr = y_incr * 25 / 10; */
lmul( boundL, boundL, twf ); /* */
ldiv( boundL, boundL, ten ); /* boundL = boundL * 25 / 10; */
lmul( boundR, boundR, twf ); /* */
ldiv( boundR, boundR, ten ); /* boundR = boundR * 25 / 10; */
lmul( boundT, boundT, twf ); /* */
ldiv( boundT, boundT, ten ); /* boundT = boundT * 25 / 10; */
lmul( boundB, boundB, twf ); /* */
ldiv( boundB, boundB, ten ); /* boundB = boundB * 25 / 10; */
}
} else {
printf ( "\rMaximum distance reached. " );
}
break;
default :
changed = 0;
break;
}
if( running && changed ) {
mandelbrot( boundL, boundR, boundT, boundB, x_incr, y_incr, max, 0 );
display();
}
}
return;
}