heckmeck!

Nerd content and
cringe since 1999

Alexander Grupe
Losso/ATW

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! :)

previous next close