The COPYTILE subroutine uses auto‑increment registers to move four pixels per instruction, shaving cycles from each frame.
*Tom Moertel’s 2013 blog details a low‑level optimization that turned a choppy 28×28‑pixel background into playable arcade‑grade motion. The trick hinges on the 6809 CPU’s auto‑increment addressing and careful loop construction.*
In December 2013 Tom Moertel posted a detailed recount of a desperate fix for a college‑aged side‑scroller that ran on an authentic 1980s PC. The game, a faithful clone of a popular arcade title, suffered from severe slowdown because each frame required redrawing the entire background tile map. A gifted programmer friend had already applied every conventional optimization—loop unrolling, draw elimination, waste removal—yet the game remained unplayable. Moertel was called in to look for a "hard fix". What he found was not a new algorithm but a clever exploitation of the Motorola 6809’s addressing modes that cut the per‑frame work to a fraction of its original cost.
The game redrew the background every frame. Each tile measured 28 × 28 pixels, with 16 colors, so each pixel occupied half a byte. In memory a tile was stored as 28 rows of 14 bytes. The screen buffer held 320 × 240 pixels, laid out as 240 rows of 160 bytes. To copy a tile, the code had to move 28 rows, each row requiring 14 byte copies. On a 6809 without hardware blitters, every byte had to be fetched, written, and the pointers advanced manually. The sheer volume of byte‑wise work created the choppy motion that broke immersion in the side‑scroller.
The 6809 offered 16‑bit index registers and auto‑increment addressing, letting the program read and write two‑byte words (four pixels) in a single instruction. The routine loaded a word from the source tile with LDU ,X++ and stored it to the destination with STU ,Y++. A seven‑iteration inner loop handled the 14‑byte row, while a 28‑iteration outer loop handled the rows. Constants SCRW = 160, TILW = 14, TILH = 28, and WOFF = SCRW‑TILW defined screen width, tile width, tile height, and the offset needed to jump to the next destination row. By moving four pixels per cycle and using tight loops, the code cut the per‑frame cycle count dramatically.
The friend’s original code was already heavily tuned. Loops had been unrolled, eliminating unnecessary branch overhead. Redundant draw calls were stripped, and any wasteful memory accesses were removed. The programmer had exhausted conventional speed‑ups, leaving only the raw pixel copy routine as the remaining bottleneck. When Tom Moertel examined the code he found it "tight" and noted that "everywhere I looked, he had already done all the things I could think of." This assessment set the stage for a more radical, architecture‑specific fix.
Side‑scrollers rely on smooth scrolling to maintain player immersion. The original implementation’s frame‑by‑frame tile redraw caused visible stutter, making the game unplayable. By slashing the cycles needed to copy each 28 × 28 tile, the hack restored fluid motion, matching the expectations set by contemporary arcade cabinets. The solution demonstrates how low‑level hardware tricks were essential for delivering arcade‑grade experiences on sluggish 8‑bit PCs, a lesson that still resonates in modern performance‑critical programming.
Each tile measured 28 × 28 pixels and was stored as 28 contiguous rows of 14 bytes, with each pixel using half a byte for one of 16 colors.
The system used a Motorola 6809 processor, which provided 16‑bit index registers and auto‑increment addressing modes.
The screen buffer was 160 bytes wide per row, representing 320 four‑bit pixels.
Moertel’s 2013 expose shows that even on hardware that seems primitive by today’s standards, deep knowledge of a CPU’s quirks can unlock performance previously thought impossible. The 6809‑based tile copy routine turned a sluggish side‑scroller into a playable experience, underscoring the timeless value of low‑level optimization in game development.
Sources: [Tom Moertel’s Blog – A great old-timey game‑programming hack (December 14, 2013)]