Clearing ten characters of the Commodore 64’s screen with a loop takes 103 processor cycles. Writing out the ten stores one after another takes 42. The work is identical; the other 61 cycles went on decrementing a counter and branching back, ten times over. On the C64 demo scene, where an effect had to finish inside a single raster line or a single frame, those cycles decided what could be done at all. Speedcode is the scene’s name for removing them: unrolling a loop into straight-line code, often thousands of instructions long.
Where the cycles go
The Codebase64 wiki’s article on the subject, by the demo coder Cruzer/CML, starts with exactly that example:
; looped: 103 cycles
lda #0
ldx #9
loop: sta screen,x ; 5 cycles
dex ; 2
bpl loop ; 3 when taken; unrolled: 42 cycles
lda #0
sta screen+0 ; 4 cycles each
sta screen+1
sta screen+2
; ... and so on to
sta screen+9The loop costs 2 + 2 cycles to set up and 10 cycles a pass, less one for the final branch that is not taken. The unrolled version costs 2 + 4 × 10. Unrolling also saves a cycle on every store, because sta screen,x takes 5 cycles and sta screen+n takes 4, a point Pasi Ojala made in the comments of his own code.
Cruzer describes the idea as stripping away “the administrative costs of the loop logic”. The bill comes in memory: ten bytes of loop became 32 bytes of stores, and a loop body repeated a thousand times becomes a thousand copies of itself.
What it was for
Cruzer calls loop unrolling “one of the earliest optimization tricks invented”, and suggests it “was probably first done to get the most rastersplits on the same line, and then later utilized to break DYCP records”. Both were contests on the C64 scene: changing the colour as many times as possible within one raster line, and moving as many characters as possible in a DYCP scroller.
Ojala’s 1993 article on sprite stretching in C= Hacking shows the same arithmetic at work. Before each frame his stretcher clears a 128-byte table, and he timed the unrolled clear, “one load (lda #$00) and 128 store instructions”, at 514 cycles plus 12 for the call and return. He then printed his fastest stretcher, from the “Fore!” part of Megademo92, and added that unrolling its inner loop would make it 17% faster, and unrolling both loops about 25% faster.
Nobody types it twice
Speedcode is long, repetitive and easy to get wrong, so coders soon stopped typing it. Cruzer’s article is mostly about the alternatives. He typed his first speedcode by hand “back in the good old lamerdays”, which took “about an afternoon on a good day” and had to be done all over again whenever a parameter changed. Then he wrote BASIC programs to generate it, then assembler macros and scripts, and finally generators that run on the C64 itself.
His worked example is an 8×8-character plasma. Written as loops, it can only fill a 20×20-character area in one frame; as speedcode it fills the full 40×25 screen. The hand-written version is a 5,297-line source file and its code takes over $3000 bytes (12K). The generator that builds the same code at start-up takes about $140 bytes (320) and runs in about a third of a second, so the demo loads faster and can rebuild the code to switch between variations of the effect.
Memory still runs out. Cruzer’s advice for a crowded 64K is to spend it where it buys the most cycles, starting with the innermost loop, “since it runs the most times”, and to put one-off setup code in memory the effect will later overwrite.
Other machines
The same trade works on any processor. On the ZX Spectrum the Z80’s block-copy instruction LDIR takes 21 T-states for every byte but the last, while the single-step LDI takes 16, so a row of LDI instructions copies about a quarter faster than one LDIR. Software scrolling and double buffering show where that mattered.
The other speedcode
In 1980s Commodore magazines the word meant something else. Abacus Software advertised its BASIC compilers as turning programs into “fast 6510 machine language and/or compact speedcode”, and in 1987 explained that speedcode “takes up less space yet protects your programs from prying eyes”. This was a compact intermediate code run by a small interpreter, slower than true machine code: Florian Müller’s 1988 book C64: Tips, Tricks und Tools explains that compiler output is called “Speedcode” to avoid confusing it with “Maschinensprache total”, machine language through and through. The two meanings point in opposite directions, one making programs smaller and the other making them bigger.
Sources
- Cruzer/CML, “Speedcode a.k.a. Loop Unrolling”, Codebase64 wiki (web, secondary): the definition, the probable origin, the 10-character example, the plasma’s sizes and timings, and the memory advice. https://codebase64.net/doku.php?id=base:speedcode
- Pasi Ojala, “The Demo Corner: Stretching Sprites”, C= Hacking issue 5, March 1993: the unrolled 128-byte clear and its timing, the 17% and 25% speedups, and the 4- and 5-cycle stores.
- Abacus Software advertisements: Ahoy! issue 17, May 1985 (BASIC Compiler 64), and Commodore Magazine vol. 8 no. 10, October 1987 (Basic-64 and Basic-128).
- Florian Müller, C64: Tips, Tricks und Tools (Markt&Technik, 1988), section 6.3.1, “Was ist ein Compiler?”.
- Zilog, Z80 CPU User Manual (UM008011-0816):
LDIandLDIRtimings.
See also
- Self-modifying code: often combined with speedcode
- Stable raster
- DYCP
- Size coding: the opposite discipline