heckmeck!

Nerd content and
cringe since 1999

Alexander Grupe
Losso/ATW

June 2026

Blog

2026-05

Let’s dig into ancient system software and file an entry for the 2026 “Obsolete operating systems bug bounty” with a report that would have been pointless even back then.

Luckily (sadly?), there is no such bug bounty, but the strange behavior is real nonetheless. Here goes:

SetRGB4 overwrites memory

Summary

On Kickstart 1.3:

When address zero contains a Copper instruction to set a color register, a call to SetRGB4(vp,n,r,g,b) will overwrite the word at address 2 if the color number n matches the color register at address 0.

Come again. What?

Read the steps to reproduce and it will become clearer…

Steps to reproduce

  • Write the word 0x0186 to address zero
  • Call graphics.library’s SetRGB4 to set color #3 to RGB 7/8/9
  • Observe memory contents at address zero
        movem.l d0-a6,-(a7)     ; save registers for later
        move.l  4,a6            ; get exec, graphics, intuition
        move.l  156(a6),a6
        move.l  368(a2),a5
        move.l  ib_ActiveScreen(a5),a5
        lea     sc_ViewPort(a5),a0
        moveq   #3,d0           ; n
        moveq   #7,d1           ; r
        moveq   #8,d2           ; g
        moveq   #9,d3           ; b
        move.w  #$0186,0        ; set memory at 0
        jsr     _LVOSetRGB4(a6) ; SetRGB4(a0:vp,d0:n,d1:r,d2:g,d3:b)
        move.l  4,a6            ; prepare text
        lea     .data(pc),a1
        move.l  0,(a1)
        lea     .fmt(pc),a0
        lea     .put(pc),a2
        lea     .buf(pc),a3
        jsr     _LVORawDoFmt(a6)
        movem.l (a7)+,d0-a6     ; restore BCPL environment
        lea     .buf(pc),a0
        move.l  a0,d1
        moveq   #-1,d2
.cnt    addq.l  #1,d2
        tst.b   (a0)+
        bne.b   .cnt
        moveq   #16,d0
        move.l  172(a2),a4      ; writeoutput
        jsr     (a5)
        moveq   #0,d0
        rts
.put    move.b  d0,(a3)+
        rts
.data   dc.l    0
.fmt    dc.b    'Memory at address 0 contains: %08lx',10,0
.buf    ds.b    256

You would expect the longword 0x01860000 at address zero — after a fresh boot, address zero contains 0x00000000, and you just put a 0x0186 at the start.

But actually you will find 0x01860789 there.

Why?

It seems like some Copper list rewriting magic goes haywire.

  • In a Copper list, 0186 xxxx writes a value to color register 3
  • Some function tries to patch Copper instructions for color #3:
    0186 xxxx becomes 0186 0789 everywhere
  • That’s fine for the current screen, but address zero is also treated as part of the Copper list that needs patching

Ask me how I found out!

How did you find out?

I was working on a tiny intro for ROMA.EXE when I ran into this. Using address zero as a counter was a hack to save two bytes:

        ; we're doing a BCPL call before and we know
        ; a0 will contain 00000000 (system memory base)

        ; now we need execbase in a6

        move.l  (a0)+,a2        ; a0 = 00000004, a2 = 00000000
        move.l  (a0),a6         ; a6 = execbase

        ; now we can use (a2) as a scratch register
        ; (we do this because all other registers are in use)

In the intro, I’m calling SetRGB4 repeatedly. It was only when the counter value accidentally was equal to 390 (0186 in hex) that things started to behave oddly.

That was fun to debug. :) But not enough fun to keep digging and present the actual culprit in the ROM code here – the SetRGB4 code path is a hairy mess of branches, stack manipulation, and subroutine calls. Who knows what other surprises are lingering in there?

My workaround was to multiply the values by 7, which avoids 390 altogether…

By the way: Having a Copper list at address zero isn’t as weird as it sounds – demos for the No-CPU runner start like that. But fear not: SetRGB4 poses no threat in No-CPU land, as there is no CPU to execute it! :)

What is that? A person hanging from a rope? A rune for “WTF”?

It’s neither of those, but it has been appearing in the memory of millions of Amiga computers since the dawn of time, right below the visible screen.

You might have seen it before if you were a very bored teenager in 1992 and used your cool new Action Replay freezer cartridge to inspect the memory contents of a freshly powered-on Amiga 500. That’s a contrived example, of course, and totally not a core memory of mine.

Coppenheimer will show you the same thing in the memory inspector when you load up a Kickstart 1.3 ROM and set the width to 40 bytes:

The location might give you a hint already: That shape is a leftover of the Kickstart hand drawing process! To be precise, it’s the result of the last “fill” command. Here is what the Kickstart ROM is drawing in slow motion:

In the video, all blitter activities are highlighted, i. e. whenever a line is drawn or a a block of data is copied, filled, or inverted. The drawing process is really slow and takes its sweet time to complete: 40 frames in real time, 0.8 seconds! In fact, when I hacked up a modified Kickstart that replaced the slow drawing code, that changed the familiar disk-clicking rhythm as well! It went from click (blank screen) – nothing (still a blank screen while you go get a coffee) – click (Kickstart hand appears) to click-click, with a barely noticeable blank screen between the clicks.

So there you have it. The answer to a mystery that nobody asked for, and a really niche and nerdy T-shirt idea. But it was a welcome opportunity to squeeze a little blog post out of the ongoing Coppenheimer update; “slow-mo” mode while you see what the chips are doing will be one of the features.

previous next close