| Amiga planar circle outline V4 -- 16-way color-specialized.
|
| Per Bresenham iter:
|   1. Precompute 4 xp records (xp_byte_w + bitMask_b + notMask_b) for
|      cx +/- bx and cx +/- by, stored at sp+0..15 (4 records x 4 bytes).
|   2. Precompute 4 yp40 words for cy +/- by and cy +/- bx, stored at
|      sp+16..23 (4 words x 2 bytes).
|   3. Plot 8 octant pixels with hardcoded color: each pixel does 4
|      branchless plane RMW ops (or.b for set bits, and.b for clear
|      bits) -- no btst, no per-plane branch.
|   4. Bresenham step.
|
| At function entry the color is masked to 4 bits and used as the index
| into a 16-entry jump table that selects the matching main loop.
| Each main loop has the color hardcoded into the per-plane RMW ops.
|
| The branchless plot saves ~20-28 cyc per plane vs V3's btst+branch
| pattern -- ~640-900 cyc per Bresenham iter.
|
| ABI: cdecl. d2-d7/a2-a6 callee-save.
|
| void surface68kAmigaCircleOutline(uint8_t *p0, uint8_t *p1,
|                                   uint8_t *p2, uint8_t *p3,
|                                   uint16_t cx, uint16_t cy,
|                                   uint16_t r,  uint8_t  color);
|
| Register allocation across the iter loop:
|   d2.w   = bx (Bresenham)
|   d3.w   = by (Bresenham)
|   d4.w   = err (Bresenham)
|   d5.w   = cx (cached)
|   a4     = cy (cached, sign-extended)
|   a0..a3 = plane bases
|   a5     = bitMaskLut
|   d0,d1,d6,d7 = scratch in precompute / plot
|
| Scratch block (24 bytes) at sp+0..23:
|   sp+0..3:   xp1 record [xp_byte_w, bitMask_b, notMask_b] for cx+bx
|   sp+4..7:   xp2 record for cx-bx
|   sp+8..11:  xp3 record for cx+by
|   sp+12..15: xp4 record for cx-by
|   sp+16..17: yp1 word (cy+by) * 40
|   sp+18..19: yp2 word (cy-by) * 40
|   sp+20..21: yp3 word (cy+bx) * 40
|   sp+22..23: yp4 word (cy-bx) * 40

                .text


| ---- XP_REC: build xp record at sp+slot for xp = cx <signOp> <xreg> ----
| signOp: add or sub
| xreg:   %d2 (bx) or %d3 (by)
| slot:   0, 4, 8, or 12
| Trashes: d0, d1, d6, d7

                .macro  XP_REC  slot, signOp, xreg
                move.w  %d5,%d6
                \signOp\().w \xreg,%d6        | d6 = xp
                move.w  %d6,%d7
                lsr.w   #3,%d7                | d7 = xp >> 3 (xp_byte)
                and.w   #7,%d6                | d6 = xp & 7
                move.b  (%a5,%d6.w),%d6       | d6 = bitMask
                move.b  %d6,%d1
                not.b   %d1                   | d1 = notMask
                move.w  %d7,\slot(%sp)        | xp_byte word
                move.b  %d6,\slot+2(%sp)      | bitMask byte
                move.b  %d1,\slot+3(%sp)      | notMask byte
                .endm


| ---- YP_REC: build yp40 word at sp+slot for yp = cy <signOp> <yreg> ----

                .macro  YP_REC  slot, signOp, yreg
                move.l  %a4,%d6
                \signOp\().w \yreg,%d6        | d6.w = yp
                move.w  %d6,%d0
                lsl.w   #3,%d6                | d6 = yp << 3
                lsl.w   #5,%d0                | d0 = yp << 5
                add.w   %d6,%d0               | d0 = yp * 40
                move.w  %d0,\slot(%sp)
                .endm


| ---- PLOT_FIXED: plot one pixel with hardcoded 4-bit color ----
| slotYp: 16, 18, 20, or 22 (yp40 word slot)
| slotXp: 0, 4, 8, or 12   (xp record slot)
| color:  literal 0..15
| Trashes: d0, d1, d7

                .macro  PLOT_FIXED  slotYp, slotXp, color
                move.w  \slotYp(%sp),%d0      | d0 = yp40
                add.w   \slotXp(%sp),%d0      | d0 += xp_byte
                move.b  \slotXp+2(%sp),%d1    | d1.b = bitMask
                move.b  \slotXp+3(%sp),%d7    | d7.b = notMask
                .if  ((\color) & 1)
                or.b    %d1,(%a0,%d0.w)
                .else
                and.b   %d7,(%a0,%d0.w)
                .endif
                .if  ((\color) & 2)
                or.b    %d1,(%a1,%d0.w)
                .else
                and.b   %d7,(%a1,%d0.w)
                .endif
                .if  ((\color) & 4)
                or.b    %d1,(%a2,%d0.w)
                .else
                and.b   %d7,(%a2,%d0.w)
                .endif
                .if  ((\color) & 8)
                or.b    %d1,(%a3,%d0.w)
                .else
                and.b   %d7,(%a3,%d0.w)
                .endif
                .endm


| ---- PLOT_8: plot all 8 octant pixels for a given hardcoded color ----

                .macro  PLOT_8  color
                PLOT_FIXED  16,  0, \color    | (cx+bx, cy+by)
                PLOT_FIXED  16,  4, \color    | (cx-bx, cy+by)
                PLOT_FIXED  18,  0, \color    | (cx+bx, cy-by)
                PLOT_FIXED  18,  4, \color    | (cx-bx, cy-by)
                PLOT_FIXED  20,  8, \color    | (cx+by, cy+bx)
                PLOT_FIXED  20, 12, \color    | (cx-by, cy+bx)
                PLOT_FIXED  22,  8, \color    | (cx+by, cy-bx)
                PLOT_FIXED  22, 12, \color    | (cx-by, cy-bx)
                .endm


| ---- CO_BODY: full Bresenham loop body for a hardcoded color ----
| Generates the per-iter precompute, branchless plot, and Bresenham
| step. Uses unique labels via \color suffix.

                .macro  CO_BODY  color
                XP_REC   0, add, %d2          | xp1 = cx+bx
                XP_REC   4, sub, %d2          | xp2 = cx-bx
                XP_REC   8, add, %d3          | xp3 = cx+by
                XP_REC  12, sub, %d3          | xp4 = cx-by
                YP_REC  16, add, %d3          | yp1 = cy+by
                YP_REC  18, sub, %d3          | yp2 = cy-by
                YP_REC  20, add, %d2          | yp3 = cy+bx
                YP_REC  22, sub, %d2          | yp4 = cy-bx

                PLOT_8  \color

                addq.w  #1,%d3
                tst.w   %d4
                bgt     .LcoDecX_\color
                add.w   %d3,%d4
                add.w   %d3,%d4
                addq.w  #1,%d4
                bra.w   .LcoLoop_\color
.LcoDecX_\color:
                subq.w  #1,%d2
                add.w   %d3,%d4
                add.w   %d3,%d4
                sub.w   %d2,%d4
                sub.w   %d2,%d4
                addq.w  #1,%d4
                bra.w   .LcoLoop_\color
                .endm


| ---- CO_LOOP_HDR: emit a labelled loop header for a color ----

                .macro  CO_LOOP_HDR  color
.LcoLoop_\color:
                cmp.w   %d3,%d2
                bcs.w   .LcoDone
                CO_BODY \color
                .endm


| ---- Function entry ----

                .equ    SP_SAVED, 44
                .equ    SP_LOCAL, 24

                .equ    SP_OFF,         (SP_SAVED + 4 + SP_LOCAL)

                .equ    SP_P0,    SP_OFF + 0
                .equ    SP_P1,    SP_OFF + 4
                .equ    SP_P2,    SP_OFF + 8
                .equ    SP_P3,    SP_OFF + 12
                .equ    SP_CX,    SP_OFF + 16 + 2
                .equ    SP_CY,    SP_OFF + 20 + 2
                .equ    SP_R,     SP_OFF + 24 + 2
                .equ    SP_COLOR, SP_OFF + 28 + 3

                .globl  _surface68kAmigaCircleOutline

_surface68kAmigaCircleOutline:
                movem.l %d2-%d7/%a2-%a6,-(%sp)
                lea     -SP_LOCAL(%sp),%sp

                | Plane bases.
                move.l  SP_P0(%sp),%a0
                move.l  SP_P1(%sp),%a1
                move.l  SP_P2(%sp),%a2
                move.l  SP_P3(%sp),%a3
                lea     bitMaskLut(%pc),%a5

                | Cache cx in d5, cy (sign-extended) in a4.
                move.w  SP_CX(%sp),%d5
                move.w  SP_CY(%sp),%d6
                ext.l   %d6
                movea.l %d6,%a4

                | Bresenham init.
                move.w  SP_R(%sp),%d2         | bx = r
                moveq   #0,%d3                | by = 0
                moveq   #1,%d4
                sub.w   %d2,%d4               | err = 1 - bx

                | Dispatch on color (low 4 bits) -> one of 16 main loops.
                | Each table entry is a bra.w (4 bytes), so index *= 4.
                moveq   #0,%d6
                move.b  SP_COLOR(%sp),%d6
                and.w   #0x0F,%d6
                add.w   %d6,%d6
                add.w   %d6,%d6
                lea     .LcoTable(%pc),%a6
                jmp     0(%a6,%d6.w)

.LcoTable:
                bra.w   .LcoLoop_0
                bra.w   .LcoLoop_1
                bra.w   .LcoLoop_2
                bra.w   .LcoLoop_3
                bra.w   .LcoLoop_4
                bra.w   .LcoLoop_5
                bra.w   .LcoLoop_6
                bra.w   .LcoLoop_7
                bra.w   .LcoLoop_8
                bra.w   .LcoLoop_9
                bra.w   .LcoLoop_10
                bra.w   .LcoLoop_11
                bra.w   .LcoLoop_12
                bra.w   .LcoLoop_13
                bra.w   .LcoLoop_14
                bra.w   .LcoLoop_15

                CO_LOOP_HDR  0
                CO_LOOP_HDR  1
                CO_LOOP_HDR  2
                CO_LOOP_HDR  3
                CO_LOOP_HDR  4
                CO_LOOP_HDR  5
                CO_LOOP_HDR  6
                CO_LOOP_HDR  7
                CO_LOOP_HDR  8
                CO_LOOP_HDR  9
                CO_LOOP_HDR  10
                CO_LOOP_HDR  11
                CO_LOOP_HDR  12
                CO_LOOP_HDR  13
                CO_LOOP_HDR  14
                CO_LOOP_HDR  15

.LcoDone:
                lea     SP_LOCAL(%sp),%sp
                movem.l (%sp)+,%d2-%d7/%a2-%a6
                rts


                .align  2
bitMaskLut:
                .byte   0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01
