Cool stuff to stumble upon, new and old.
What’s Cool? ·
What’s Cool? II ·
What’s Cool? III ·
What’s Cool? IV ·
What’s Cool? V
- Fonts before Mac (1983)
Font archeology on the Mac – learn why the original Macintosh system font was called “Elefont” and how it evolved, together with other early bitmap fonts for the Mac. Susan Kare’s pixel work is always worth contemplating in awe! Bonus: Through this article, I’ve learned what a Twiggy Mac is. - Those Secret Fonts from the ISA-16 PS/2 Models (Again)
More font archeology, but in the IBM PC world and five years later: VileR traces back the origins of some extra ROM fonts in certain PS/2 models to an internal IBM software package. Fascinating detective work, leaving nostonepixel unturned! - Fredrik Liljegren on the creation of Pinball Dreams
Fast forward four more years, and we’ve arrived in 1992 when The Silents set out to publish their first game, Pinball Dreams, and founded “Digital Illusions”, nowadays known as “DICE”. This interview sheds some light on how it all came to be: One guy drew a multi-screen pinball table and the others thought: Heh, interesting… Ah, the simpler times! :) - More retrocomputing, less nostalgia
Speaking of simpler times: Is it really nostalgia when we still hand-pixel pretty pictures on the Amiga to this day, or compose SID tunes for the C64? Or is it more about the fun in exercising crafts you’ve learnt in your youth and simply still enjoy? I would agree that “real nostalgia” hits different; the other day, I was struck out of the blue with the feeling how incredibly cool it was in 1995 that my feeble Amiga could play along on the internet: browsing the web, sending e-mail around the planet, learning HTML from other websites, creating your own content for the whole world to see. It’s bittersweet how that level of excitement can never be brought back, but it’s also blissful and rewarding to keep using that ol’ box! - Yes, you can store data on a bird
I’m sure this story is currently making the rounds everywhere in the geekosphere, but the headline is just golden. As is the tech, in hardware and biological form: a bird drawing a bird in the spectogram!
Nordlicht 2025 was a blast, and provided me with some much needed wellness time. There was a ton of great releases, lots of fun, and the new party location was awesome. Kudos to the organizers!

Luckily my latest 512 byte intro – Temba, seine Arme weit! – also made it to the podium, even with the tough competition in the 512 byte category. Phew! :)

The 4×4 copperchunky rotozoom Amiga competition is coming along nicely!
Back in 2016, Photon/Scoopex released Grade My Waterbear, a true-color rotozoomer intro, along with a bold claim:
This is the first and only maximum size, minimum bandwidth truecolor 4x4 OCS chunky mode. No-one will beat those specs, so.
If you’re unfamiliar with Amiga tech-tech, this is about doing image effects with the help of the Copper, the display coprocessor that can do things like “wait for screen position x,y and change the background color”. This way you can get around palette limitations (usually 32 colors at a time) and use all 4,096 colors of the Amiga – albeit in fat 4×4 pixels.
Of course, there are hundreds of variations of this technique, all with different technical aspects and limitations. That’s not important here, the main thing is: Photon’s oldschool bragging worked nicely, triggering countless efforts to one-up the effect under very specific constraints – even years after the fact. “Only 50 columns? Pathetic! I can do 52!” :)
The intros even come with helpful little scales nowadays!

Up until today, we’ve got all these gems related to the original 2016 production. And I’m pretty sure we haven’t seen the end of it. Isn’t that delightful?
Update: It goes on already! Days after this post, Batman Group came out with another rotozoomer (added below). Now the cropped image size I chose for the screenshots isn’t enough to display all the columns anymore, hehe… :)
Production | Who and when |
---|---|
![]() |
Scoopex |
![]() |
Damones This seems to merely have coincided with Photon’s release, but what a gorgeous HAM rotozoomer nevertheless! |
![]() |
Damones |
![]() |
Loonies & Struts |
![]() |
Batman Group |
![]() |
Loonies & TTE |
![]() |
Lemon. |
![]() |
Batman Group & Capsule |
Anyhoo… The point of this post is not to announce that I will be taking part in this as well. Cycle-exact CPU writes? Emulator-breaking hardware abuse? Hell, no! :) With regards to rotozooming, I’m happy with my own little bootblock from 2016.
But: Speaking of emulator breaking (i. e. using tricks so advanced that they’re not properly emulated yet) – I was a little worried when I read that “…And One Up The Sleeve” requires the latest WinUAE release to render correctly. “Sigh, time to finally update Coppenheimer as well,” I thought.
But no! Turns out the rather dated vAmiga core used in Coppenheimer already emulated Hannibal’s tricks perfectly. So, in addition to compiling some pretty screenshots and links for any Amiga-inclined blog readers, I would like to praise Dirk W. Hoffmann for the stellar emulation effort with vAmiga, and also mithrendal for the vAmigaWeb project!

One day a Coppenheimer update will come… I’m sure of it. :) I would really like to provide links to those intros with a running emulator instead of blurry YouTube recordings (but that’s more of a question of ROM copyright and web safety concerns).
As mentioned over at Bluesky, I’m currently on sick leave and taking the time to make myself more familiar with ZX0 compression – also in preparation of another 512 byte entry for Nordlicht, maybe. :)
Thankfully, ZX0 author Einar Saukas has provided reference implementations in several programming languages (C, Kotlin, Java), and with that, I was able to quickly peek what’s happening behind the scenes, gather diagnostic logs, etc.
Summary: I saved two bits for my use case, and two bytes for everyone!
Edit: The 68000 decompression routine referenced below is a port by Emmanuel Marty, not a reference implementation by Einar Saukas. Thanks to both of you for your work!
That’s a long end marker!
One thing that has bothered me was that my compressed data always
ended with the bytes 0x5556
or 0xAAAB
. Can’t this be shorter?
This has to do with how ZX0 marks the end of the data stream.
- ZX0 has three modes:
- Output the next n bytes literally (uncompressed)
- Copy n bytes from last position
- Copy n bytes from a specific position
- ZX0 knows two flavors of bytes, so to speak:
- Whole data bytes (uncompressed, direct data)
- Bit-packed bytes (control bits and specially-encoded values)
- Both are interspersed in the compressed data
- ZX0 will try to cram as many bits as possible into each non-data byte; you can think of all non-data bytes together as a single stream of bits
- Byte lenghts and offsets use a Elias gamma coding
scheme.
This way, common short values use fewer bits in the control data stream. For example:
- 1 =
1
- 2 =
011
- 3 =
001
- 4 =
01011
- 5 =
01001
- 6 =
00011
- 7 =
00001
- 8 =
0101011
- etc.
- 1 =
- The “copy n specific bytes” mode uses two values to calculate the start offset:
- An Elias-coded value (variable bit length)
- An unchanged, literal byte taken from the data (8 bits)
- Combined into
eliasValue << 8 | byteValue
- When the “copy n specific bytes” mode encounters an Elias value of 256, this marks the end of the compressed stream. (The compressor outputs that value when it’s done.)
- The value 256 is where the
0x5556
and0xAAAB
values come from.- 256 encodes to
01010101010101011
- That’s
0xAAAB
in hexadecimal - Or
0x15556
when shifted one bit to the left
- 256 encodes to
Curious, I used the decompression code to trace what other Elias values are used in my data for the “copy from” position, in addition to the end marker. I only saw 5 distinct values being used:
Elias value | Decoded | Meaning |
---|---|---|
1 | 1 | Copy n bytes from position 0x01.. |
011 | 2 | Copy n bytes from position 0x02.. |
001 | 3 | Copy n bytes from position 0x03.. |
01011 | 4 | Copy n bytes from position 0x04.. |
01010101010101011 | 256 | End marker |
So if my data only uses start positions up to 0x04..
– could I just use a
smaller value
for the end marker? Maybe 7, as it uses the same number of Elias bits as 4 and would leave some leeway.
01010101010101011
= old end marker, 25600001
= new end marker, 7- 12 bits saved!
But I couldn’t just replace the new end-marker value in the 68000 decompression code.
It doesn’t even use the value 256 directly, but rather
checks if the byte portion
ends up as 0x00
(the beq
branch is taken, then):
.get_offset: moveq #-2,d0 ; initialize value to $fe bsr.s .elias_loop ; read high byte of match offset addq.b #1,d0 ; obtain negative offset high byte beq.s .done ; exit if EOD marker
Bummer! I did, however, change the
beq
to a bge
check (greater or equal than 0, signed). For
this check to succeed, we only need an Elias value of 129 instead of 256:
End marker | Effect of addq.b #1 | End detection |
---|---|---|
256 | 0xFFFFFEFF becomes 0xFFFFFE00 | beq is true |
129 | 0xFFFFFF7E becomes 0xFFFFFF7F | bge is true |
This limits the copy-from offsets to the 0x80..
range (acceptable for my data), but sadly,
129 is only 2 bits shorter when Elias-encoded.
01010101010101011
= 256, using 17 bits010101010101001
= 129, using 15 bits
When the stars align right, we might chop off the last byte of our compressed data – in case it was only used for the lower Elias bits of 256 before. Not a huge win for all that hassle…
Needs more research: Maybe there’s another way of encoding the “end of stream” condition in 68000-assembly-friendly form. Especially when it can be be tailored and hard-coded to my specific data. For now, I’m happy with the 2 bits saved! :)
An unexpected save
The bigger surprise came when I was looking at the 68000 decompression code some more.
In the loop to copy literal bytes, it does this:
.literals: bsr.s .get_elias ; read number of literals to copy subq.l #1,d0 ; dbf will loop until d0 is -1, not 0 .copy_lits: move.b (a0)+,(a1)+ ; copy literal byte dbf d0,.copy_lits ; loop for all literal byte
This gets assembled to:
.literals: bsr.s .get_elias ; 6136 subq.l #1,d0 ; 5380 .copy_lits: move.b (a0)+,(a1)+ ; 12d8 dbf d0,.copy_lits ; 51c8 fffc
That dbf
instruction is huge, requiring an
extra word to encode the branching target (here: 0xFFFC
= -4 signed).
Now, for short loops a dbf
instruction can be replaced with a subq
and a conditional branch,
like this:
dbf code | subq version |
---|---|
; ; d0 = # of iterations ; subq.l #1,d0 .loop ; ; do stuff ; dbf d0,.loop ; 51c8 fffc ; ; d0 is -1 now ; | ; ; d0 = # of iterations ; subq.l #1,d0 .loop ; ; do stuff ; subq.w #1,d0 ; 5340 bge.b .loop ; 6cfa ; ; d0 is -1 now ; |
Of course, that’s still the same size – both versions take up 4 bytes.
As noted in the decompressor code comment, a dbf
will always loop until the
counter register becomes -1. If we want 23 loop executions, we need to put 23-1 = 22 into register d0.
(Hence the subq #1
before the loop.)
With subq
and a branch, however, we can
change the branch condition to end at zero. We’ll be getting 23 executions when d0 is 23,
and don’t need to subtract 1 before the loop!
Only difference: After the loop, d0 will contain 0, not -1.
Is it important if d0 ends up as 0 or -1? Let’s check what happens after the loop!
.literals: bsr.s .get_elias ; read number of literals to copy subq.l #1,d0 ; dbf will loop until d0 is -1, not 0 .copy_lits: move.b (a0)+,(a1)+ ; copy literal byte dbf d0,.copy_lits ; loop for all literal bytes add.b d1,d1 ; read 'match or rep-match' bit bcs.s .get_offset ; if 1: read offset, if 0: rep-match
The code either continues or jumps elsewhere. This is what happens in each case:
“Match or rep-match” = 0 | “Match or rep-match” = 1 |
---|---|
add.b d1,d1 bcs.s .get_offset ; ; branch not taken ; .rep_match: bsr.s .get_elias ... .get_elias: moveq #1,d0 ... | add.b d1,d1 bcs.s .get_offset ; ; branch taken ; .get_offset: moveq #-2,d0 ... |
Turns out d0 will always be overwritten! Thus, we can rewrite the loop like so:
subq.l #1,d0;5380.copy_lits: move.b (a0)+,(a1)+ ; 12d8dbf d0,.copy_lits;51c8fffcsubq.w #1,d0 ; 5340 bgt.s .copy_lits ; 6efa
Cool! We just removed unnecessary code from the decompression routine that has been sitting there the whole time! Everyone using unzx0_68000.S can save two whopping bytes now.
That can make quite the difference in a 512 byte intro! :)
I need to stop here for now, as I’m getting more excited than my doctor would recommend. Time for a nap, and let’s hope I didn’t overlook anything here!
Cool stuff to stumble upon, new and old.
What’s Cool? ·
What’s Cool? II ·
What’s Cool? III ·
What’s Cool? IV
- Viewing images
You cannot write an image viewer for the terminal without understanding everything about file formats and displaying images on a computer screen first. Right? Like, everything! A post full of side quests about pixels, compression, aliasing, image libraries, color spaces, and math. There’s a section about cursed mouse pointers, too! :) - My color cycling efforts at Datastorm
Dalton/Tulou shows off and explains some of his color cycling Amiga pictures (i. e. animation by palette changes). These were all made for the Datastorm demo parties which had dedicated color cycling competitions, apparently – I must have missed that when I was there in 2014… - The REM-arkable Misadventures of LIST
Another post about everything – here, it’s everything about Commodore BASIC’s un-tokenizer implementation on the Commodore PET: the “LIST” command. An earlier post dealt with the opposite direction, i. e. the tokenizer for parsing BASIC code. These articles examine quirks and bugs across different BASIC versions, with detailled explanations exactly why and how something goes wrong, down to the 6502 machine code. - Alternative Layout System
What if all words in a paragraph have the same width? Why not cram in long words near the margin by bending them? Or reduce the text size of the last syllable? How about horizontally stretching the last word or letter? Turns out it has all been done, often in historic Hebrew and Arabic manuscripts, and it’s all demonstrated here (and in the book).