DTFC — Draw The F***ing Circle

Published: (June 18, 2026 at 04:36 AM EDT)
6 min read
Source: Dev.to

Source: Dev.to

Or: the story of a bug that turned out to be more correct than the correct code

Boot sequence

Year: somewhere between 1983 and 1987.

Iron: a custom board — Motorola 68000 (32-bit. Pure luxury. The HW team argued about it for a week.) and a Texas Instruments TMS34010, one of the first programmable graphics chips on the planet.

VRAM: 256 kilobytes. That’s not a typo.

Mass storage: 20 megabytes. Total. Bare SCSI-like interface, no OS.

Mission: rasterize A0 architectural drawings at 400 dpi.

RTFM and do the math:

A0 = 841 × 1189 mm
At 400 dpi → ~13,300 × 18,700 pixels
1 bit per pixel → ~30 MB
Enter fullscreen mode


Exit fullscreen mode

The output bitmap did not fit on the disk.

The disk that also held the OS, the FAT, the filesystem driver,

the application, and the quadtree-compressed source drawing.

Every byte was hand-negotiated. Every cycle was audited.

The entire stack — OS, filesystem, elevator disk scheduler, swap system — was written from scratch, in assembly or C, by some people with a logic analyzer and too much coffee.

This is the environment in which one needed to DTFC.

The textbook solution (a.k.a. the one that doesn’t run)

The clean approach: place a point on the circle, then rotate it step by step around the origin.

Standard rotation matrix, angle a = 2π/N for N steps:

| cos(a)  -sin(a) |     applied to     | x |
| sin(a)   cos(a) |                    | y |
Enter fullscreen mode


Exit fullscreen mode

In code:

float x = r, y = 0;
for (int i = 0; i > n);
y = y + (x >> n);
Enter fullscreen mode


Exit fullscreen mode

Zero multiplications. Shifts and adds only.

In 16-bit fixed-point (8.8 format), this is 3-4 cycles per point.

At 8 MHz: tens of thousands of circle points per second.

The plotter can’t even keep up.

One ran it.

The circle closed perfectly.

Ship it.

Wait. There’s a bug here.

Look at that code again. Really look at it.

x = x - (y >> n);   /* x is modified HERE...         */
y = y + (x >> n);   /* ...NEW x is reused here. OOPS. */
Enter fullscreen mode


Exit fullscreen mode

That’s not the same as the clean version with a temp variable.

That’s an in-place update bug — the classic freshman mistake, the thing that gets you a raised eyebrow in code review.

And there’s worse. Look at the approximation matrix in the maths above:

| 1   -a |     det = 1×1 − (−a)×a = 1 + a²
| a    1 |
Enter fullscreen mode


Exit fullscreen mode

Determinant greater than 1. Every step should expand the radius slightly.

After N iterations, the spiral should drift outward. The circle should not close.

And yet. No drift. No spiral. Closed.

How.

POST-MORTEM — what the “bug” actually computes

Let’s expand what that broken code actually does, algebraically:

x' = x - y*a
y' = y + x'*a
   = y + (x - y*a)*a
   = y + x*a - y*a²
Enter fullscreen mode


Exit fullscreen mode

The implicit transformation matrix is:

|  1      -a   |
|  a    1-a²   |
Enter fullscreen mode


Exit fullscreen mode

Determinant:

1×(1−a²) − (−a)×a
= 1 − a² + a²
= 1
Enter fullscreen mode


Exit fullscreen mode

Exactly 1.

Not approximately. Not “close enough for graphics work”.

Exactly, algebraically, provably 1 — for ‘any’ value of a.

The “buggy” in-place update doesn’t just compensate for the spiral drift.

It perfectly cancels it — the +a² from the approximation error and the −a² from the in-place reuse cancel each other out, identically.

The bug fixed the bug.

DTFC — final, shipped, still running

/*
** DTFC - Draw The F***ing Circle
**
** Radius r, centered at origin.
** N steps for a full revolution.
** shift = bit precision (e.g. shift=6 means a ≈ 1/64)
**
** No sin(). No cos(). No multiply. No temp variable.
** Just shifts, adds, and one algebraically perfect "mistake".
**
** Determinant = 1. Circle closes. Every time.
*/
int x = r, y = 0;
for (int i = 0; i > shift);   /* wrong order. keep it. */
    y = y + (x >> shift);   /* new x. intentional.   */
}
Enter fullscreen mode


Exit fullscreen mode

Runs on a Z80. Runs on a 6809. Runs on a TMS34010. Could run on your Arduino right now.

Three lines of logic. Zero trig. Zero multiply.

One preserved determinant hiding in plain sight.

AFAIK never saw this trick in a textbook

This trick sits at the junction of:

Numerical analysis — linearization of transcendental functions

Linear algebra — determinant as area-preservation invariant

Hardware archaeology — shift-only fixed-point arithmetic

Accidental correctness — the bug that was righter than the fix

It was never written down, to anyone’s knowledge.

It lived in the fingertips of engineers at Benson, Schlumberger Graphics, OCE — people who built A0 plotters and raster engines when RAM was rationed and every cycle had a name.

One colleague — from Sun Microsystems, where he had just taped out the first hardware triangle rasterizer ever built in silicon — kept Graphics Gems on his desk, beside Knuth’s bibles.

This trick deserved a page in it.

It didn’t get one.

Consider this that page.

Halt and catch fire

Sometimes the bug is smarter than the programmer.

Sometimes the constraint is the algorithm.

And sometimes, at 8 MHz with 256k of VRAM and a plotter spooling A0 drawings for architects who will never know, one accidentally writes something that is more correct than the correct version.

The circle closed. It always closed.

— Recovered from memory, ~40 years post-silicon. Still compiles. Still works.

P.S. — Should work with floating point too. The algebra doesn’t care about your type system.

Tags: #8bit #retrocomputing #graphics #fixedpoint #TMS34010 #68000

#circledrawing #linearlgebra #determinant #GraphicsGems #accidentalgenius

0 views
Back to Blog

Related posts

Read more »

The Model Doesn't Remember. You Do

Introduction Before I dug into how an LLM works, I assumed each chat stored its memory or context in its own. The moment I realized it was just an array with al...