// Mandelbrot fractal in C for the Sinclair ZX80, ZX81 and ZX Spectrum.
// Guy Fernando (2023)
//
#include "conio.h"
#include "graphics.h"
#include "math.h"
#include "stdio.h"
// Math definitions for 16-bit or 32-bit floating point maths.
#ifdef __MATH_MATH16
#define FLOAT _Float16
#define SQRT sqrtf16
#else
#define FLOAT float
#define SQRT sqrt
#endif
// Complex number.
typedef struct {
FLOAT real;
FLOAT imag;
} complex;
// Adds two complex numbers and returns the result as a complex number.
complex* complex_add(complex* a, complex* b)
{
static complex result;
result.real = a->real + b->real;
result.imag = a->imag + b->imag;
return &result;
}
// Multiplies two complex numbers and returns the result as a complex number.
complex* complex_mul(complex* a, complex* b)
{
static complex result;
result.real = a->real * b->real - a->imag * b->imag;
result.imag = a->real * b->imag + a->imag * b->real;
return &result;
}
// Returns the real absolute value of a complex number.
FLOAT complex_abs(complex* z)
{
return SQRT(z->real * z->real + z->imag * z->imag);
}
// Returns a point on the Mandelbrot fractal plane.
uint8_t mandelbrot(FLOAT x, FLOAT y, int max_iter)
{
static complex c, z;
c.real = x;
c.imag = y;
z.real = 0;
z.imag = 0;
uint8_t iter;
for (iter = 0; iter < max_iter && complex_abs(&z) < 2.0; iter++)
{
complex* z_squared = complex_mul(&z, &z);
complex* z_new = complex_add(z_squared, &c);
z.real = z_new->real;
z.imag = z_new->imag;
}
if (iter == max_iter)
return 1;
else
return 0;
}
// The program main entry point.
void main()
{
#if __SPECTRUM
const uint16_t WIDTH = 256, HEIGHT = 192;
#elif __ZX80__ || __ZX81__
const uint8_t WIDTH = 64, HEIGHT = 48;
#endif
const FLOAT x_min = -2.0, x_max = 1.0;
const FLOAT y_min = -1.0, y_max = 1.0;
const uint8_t max_iter = 14;
// Clear screen.
clg();
// Plot fractal.
for (uint16_t y = 0; y < HEIGHT; y++)
{
for (uint16_t x = 0; x < WIDTH; x++)
{
FLOAT x0 = x_min + (x_max - x_min) * x / (WIDTH - 1);
FLOAT y0 = y_max - (y_max - y_min) * y / (HEIGHT - 1);
if (mandelbrot(x0, y0, max_iter))
plot(x, y);
}
}
// Print title.
#if __SPECTRUM
printf("MANDELBROT - ZX SPECTRUM");
#elif __ZX81__
printf("MANDELBROT - ZX81");
#elif __ZX80__
printf("MANDELBROT - ZX80");
#endif
getch();
}
mandelbrot.c
The complex_add
function takes two complex numbers as input (a
and b
)
and returns their sum as a new complex number. It performs the addition of the real and imaginary parts
separately and stores the result in a static variable result. The function then returns a pointer to result,
allowing the result to be accessed outside the function.
The complex_mul
function takes two complex numbers as input (a
and b
)
and returns their product as a new complex number. Similar to complex_add
, it performs the
multiplication of the real and imaginary parts separately and stores the result in a static variable result.
The function returns a pointer to result.
The mandelbrot
function generates the Mandelbrot set image by iterating over each pixel in the
specified width and height. For each pixel, it calculates the corresponding complex number in the complex plane
based on the pixel's position.
The main loop of the mandelbrot
function iterates the Mandelbrot formula for each pixel until either
the maximum number of iterations is reached or the point escapes to infinity. The formula is:
\( \begin{aligned}
z(n+1) = z(n)^2 + c
\end{aligned} \)
where z(n) is the current value of z, c is the complex number for the current pixel.
Inside the loop, the variables x
and y
are used to represent the real and imaginary
parts of z
. The variables x0
and y0
represent the real and imaginary
parts of c
, respectively, which are calculated based on the pixel's position and the scaling factors.
The loop performs the iteration until either the escape condition
\( \begin{aligned}
|z| < 2
\end{aligned} \)
or the maximum number of iterations is reached. It updates the values of x
and y
in
each iteration according to the Mandelbrot formula.
After the loop, the variable iter
holds the number of iterations performed. If iter
equals the maximum number of iterations, it means the point did not escape to infinity and is considered to
be inside the Mandelbrot set. In this case, the pixel is drawn black.
Overall, the complex_add
and complex_mul
functions provide the necessary operations
to perform arithmetic with complex numbers, while the mandelbrot
function uses these operations
to iterate the Mandelbrot formula for each pixel.