| Shared 68000 fast path for the planar surface buffer.
|
| Holds surface68kFillSpan4Planes -- the per-row body of the 4-plane
| planar rect/circle fill. Only the Amiga port calls it (ST is
| word-interleaved and uses its own St* routines); it lives here
| because the file is globbed into both ports' builds. The earlier
| chunky clear/fill primitives that this file once also held were
| removed once both ports went native-planar.
|
| Calling convention: cdecl (gcc-amigaos / gcc-atari-mint).
|   d0/d1/a0/a1 are caller-save (scratch).
|   d2-d7/a2-a6 are callee-save (must be preserved).
|   16-bit shorts are sign-extended to int (4 bytes) on the call stack.
|
| GAS m68k syntax. Assembled by the gcc driver via binutils m68k-as.

                .text


| ----------------------------------------------------------------
| void surface68kFillSpan4Planes(uint8_t *p0, uint8_t *p1,
|                                uint8_t *p2, uint8_t *p3,
|                                uint16_t numMid,
|                                uint8_t  leftMask, uint8_t rightMask,
|                                uint8_t  fb0, uint8_t fb1,
|                                uint8_t  fb2, uint8_t fb3);
|
| Fill ONE planar row across 4 planes -- the per-row body of
| jlpFillRect lifted into asm. Each pN points at the leading
| byte (already advanced by planeBase + y*40 + byteFirst on the C
| side). leftMask and rightMask are the partial-byte masks for the
| left/right edges; numMid is the count of full bytes between them.
| fbN is 0x00 or 0xFF, the per-plane fill byte (caller pre-classifies
| (colorIndex >> N) & 1 -> 0xFF or 0x00).
|
| Used by Amiga jlpFillCircle (one call per scanline span) and
| Amiga jlpFillRect (one call per row of the rect). Replaces
| the C inner loop whose ~13 cyc/byte was the gating cost on
| jlFillCircle r=40 even after C-side inlining.
|
| Mask convention is uniform for all planes:
|   leading byte  := (*p & ~leftMask)  | (fbN & leftMask)
|   middle bytes  := fbN
|   trailing byte := (*p & ~rightMask) | (fbN & rightMask)
| -- branchless: the same arithmetic produces "set" or "clear" based
| on whether fbN is 0xFF or 0x00.
|
| ABI: m68k cdecl. d2-d7/a2-a6 callee-save. Only a2/a3 are actually
| used by the body (planes 2/3); a4-a6 are not touched, so only
| d2-d7/a2-a3 are movem'd here.
| Stack offset to first arg after MOVEM: 8 regs * 4 = 32 bytes saved
| + 4 ret PC = 36.
| ----------------------------------------------------------------
                .globl  _surface68kFillSpan4Planes

                .equ    SP_SAVED, 32
                .equ    SP_RPC,    4
                .equ    SP_OFF,   (SP_SAVED + SP_RPC)

                .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_NMID,  SP_OFF + 16 + 2  | int -> low word at +2
                .equ    SP_LMASK, SP_OFF + 20 + 3  | int -> low byte at +3
                .equ    SP_RMASK, SP_OFF + 24 + 3
                .equ    SP_FB0,   SP_OFF + 28 + 3
                .equ    SP_FB1,   SP_OFF + 32 + 3
                .equ    SP_FB2,   SP_OFF + 36 + 3
                .equ    SP_FB3,   SP_OFF + 40 + 3

| Per-plane work is fully inlined (hand-unrolled 4x rather than
| bsr+rts, to dodge ~12 cyc per return + the per-plane re-test of
| numMid that an earlier build paid). Register convention across all
| four planes:
|   d1 = leftMask, d2 = ~leftMask, d3 = rightMask, d4 = ~rightMask
|   d5 = this plane's fb byte, d6/d0/d7 = per-plane scratch.
|
| The unmasked middle run is filled with long stores rather than
| byte-at-a-time. fb is 0x00 or 0xFF, so the long is built with a
| byte->word->long sign-extend (ext.w / ext.l). The mid pointer is
| advanced to a long boundary by storing 0-3 fb bytes (align run),
| then move.l (an)+ fills (midRem >> 2) longs, then a 0-3 byte tail
| drains the remainder. The masked left/right edge bytes keep their
| per-plane read-modify-write; only the unmasked middle changed.
|
| Plain text version of the per-plane body (translate to asm 4x with
| different a-regs and fb stack offsets):
|
|   move.b  (an),%d6          | --- leading masked edge byte ---
|   and.b   %d2,%d6
|   move.b  fb,%d5
|   move.b  %d5,%d7
|   and.b   %d1,%d7
|   or.b    %d7,%d6
|   move.b  %d6,(an)+
|   < if has-middle path: >
|     move.b  %d5,%d7         | build replicated fill long in d7
|     ext.w   %d7
|     ext.l   %d7
|     move.l  an,%d6          | align = (-an) & 3, clamped to numMid
|     neg.l   %d6
|     and.w   #3,%d6
|     move.w  numMid,%d0
|     < clamp d6 to d0; d0 = midRem = numMid - align >
|     < store d6 fb bytes to reach a long boundary >
|     < midLongs = midRem >> 2; tail = midRem & 3 >
|   .longN:
|     move.l  %d7,(an)+
|     dbra    midLongs,.longN
|     < store tail fb bytes >
|   < trailing masked edge byte: >
|   move.b  (an),%d6
|   and.b   %d4,%d6
|   move.b  %d5,%d7
|   and.b   %d3,%d7
|   or.b    %d7,%d6
|   move.b  %d6,(an)

_surface68kFillSpan4Planes:
                movem.l %d2-%d7/%a2-%a3,-(%sp)

                move.b  SP_LMASK(%sp),%d1
                move.b  %d1,%d2
                not.b   %d2
                move.b  SP_RMASK(%sp),%d3
                move.b  %d3,%d4
                not.b   %d4

                move.l  SP_P0(%sp),%a0
                move.l  SP_P1(%sp),%a1
                move.l  SP_P2(%sp),%a2
                move.l  SP_P3(%sp),%a3

                | One-time numMid test. d0.w = numMid; if 0 jump to
                | the no-middle entry. The with-middle path now fills
                | the unmasked middle run with long stores (move.l
                | (an)+) instead of byte-at-a-time, after aligning the
                | mid pointer to a long boundary and draining a 0-3
                | byte tail. The masked left/right edge bytes keep their
                | per-plane read-modify-write exactly as before.
                |
                | The mid fill byte fbN is 0x00 or 0xFF, so the long
                | replication is a byte->word->long sign-extend (ext.w
                | then ext.l): 0xFF -> 0xFFFFFFFF, 0x00 -> 0x00000000.
                |
                | Per-plane register use during the mid run:
                |   d5 = fbN byte (held for tail + trailing edge)
                |   d7 = fbN replicated to a long (move.l fill value)
                |   d6 = scratch / align count / midLongs count
                |   d0 = numMid, then midRem, then tail-byte count
                | Masks d1-d4 stay live across all four planes. Local
                | numeric labels 1f-7f are reused per plane; each
                | reference resolves to the nearest copy.
                move.w  SP_NMID(%sp),%d0
                beq     .LfsNoMid

                | ---- WITH-MIDDLE PATH ----
                | Plane 0
                move.b  (%a0),%d6
                and.b   %d2,%d6
                move.b  SP_FB0(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a0)+
                move.b  %d5,%d7
                ext.w   %d7
                ext.l   %d7
                move.l  %a0,%d6
                neg.l   %d6
                and.w   #3,%d6
                move.w  SP_NMID(%sp),%d0
                cmp.w   %d0,%d6
                bls.s   1f
                move.w  %d0,%d6
1:              sub.w   %d6,%d0
                tst.w   %d6
                beq.s   2f
3:              move.b  %d5,(%a0)+
                subq.w  #1,%d6
                bne.s   3b
2:              move.w  %d0,%d6
                lsr.w   #2,%d6
                and.w   #3,%d0
                tst.w   %d6
                beq.s   5f
                subq.w  #1,%d6
4:              move.l  %d7,(%a0)+
                dbra    %d6,4b
5:              tst.w   %d0
                beq.s   7f
6:              move.b  %d5,(%a0)+
                subq.w  #1,%d0
                bne.s   6b
7:              move.b  (%a0),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a0)

                | Plane 1
                move.b  (%a1),%d6
                and.b   %d2,%d6
                move.b  SP_FB1(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a1)+
                move.b  %d5,%d7
                ext.w   %d7
                ext.l   %d7
                move.l  %a1,%d6
                neg.l   %d6
                and.w   #3,%d6
                move.w  SP_NMID(%sp),%d0
                cmp.w   %d0,%d6
                bls.s   1f
                move.w  %d0,%d6
1:              sub.w   %d6,%d0
                tst.w   %d6
                beq.s   2f
3:              move.b  %d5,(%a1)+
                subq.w  #1,%d6
                bne.s   3b
2:              move.w  %d0,%d6
                lsr.w   #2,%d6
                and.w   #3,%d0
                tst.w   %d6
                beq.s   5f
                subq.w  #1,%d6
4:              move.l  %d7,(%a1)+
                dbra    %d6,4b
5:              tst.w   %d0
                beq.s   7f
6:              move.b  %d5,(%a1)+
                subq.w  #1,%d0
                bne.s   6b
7:              move.b  (%a1),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a1)

                | Plane 2
                move.b  (%a2),%d6
                and.b   %d2,%d6
                move.b  SP_FB2(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a2)+
                move.b  %d5,%d7
                ext.w   %d7
                ext.l   %d7
                move.l  %a2,%d6
                neg.l   %d6
                and.w   #3,%d6
                move.w  SP_NMID(%sp),%d0
                cmp.w   %d0,%d6
                bls.s   1f
                move.w  %d0,%d6
1:              sub.w   %d6,%d0
                tst.w   %d6
                beq.s   2f
3:              move.b  %d5,(%a2)+
                subq.w  #1,%d6
                bne.s   3b
2:              move.w  %d0,%d6
                lsr.w   #2,%d6
                and.w   #3,%d0
                tst.w   %d6
                beq.s   5f
                subq.w  #1,%d6
4:              move.l  %d7,(%a2)+
                dbra    %d6,4b
5:              tst.w   %d0
                beq.s   7f
6:              move.b  %d5,(%a2)+
                subq.w  #1,%d0
                bne.s   6b
7:              move.b  (%a2),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a2)

                | Plane 3
                move.b  (%a3),%d6
                and.b   %d2,%d6
                move.b  SP_FB3(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a3)+
                move.b  %d5,%d7
                ext.w   %d7
                ext.l   %d7
                move.l  %a3,%d6
                neg.l   %d6
                and.w   #3,%d6
                move.w  SP_NMID(%sp),%d0
                cmp.w   %d0,%d6
                bls.s   1f
                move.w  %d0,%d6
1:              sub.w   %d6,%d0
                tst.w   %d6
                beq.s   2f
3:              move.b  %d5,(%a3)+
                subq.w  #1,%d6
                bne.s   3b
2:              move.w  %d0,%d6
                lsr.w   #2,%d6
                and.w   #3,%d0
                tst.w   %d6
                beq.s   5f
                subq.w  #1,%d6
4:              move.l  %d7,(%a3)+
                dbra    %d6,4b
5:              tst.w   %d0
                beq.s   7f
6:              move.b  %d5,(%a3)+
                subq.w  #1,%d0
                bne.s   6b
7:              move.b  (%a3),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a3)

                movem.l (%sp)+,%d2-%d7/%a2-%a3
                rts

.LfsNoMid:
                | ---- NO-MIDDLE PATH (just leading + trailing) ----
                | Plane 0
                move.b  (%a0),%d6
                and.b   %d2,%d6
                move.b  SP_FB0(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a0)+
                move.b  (%a0),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a0)

                | Plane 1
                move.b  (%a1),%d6
                and.b   %d2,%d6
                move.b  SP_FB1(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a1)+
                move.b  (%a1),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a1)

                | Plane 2
                move.b  (%a2),%d6
                and.b   %d2,%d6
                move.b  SP_FB2(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a2)+
                move.b  (%a2),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a2)

                | Plane 3
                move.b  (%a3),%d6
                and.b   %d2,%d6
                move.b  SP_FB3(%sp),%d5
                move.b  %d5,%d7
                and.b   %d1,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a3)+
                move.b  (%a3),%d6
                and.b   %d4,%d6
                move.b  %d5,%d7
                and.b   %d3,%d7
                or.b    %d7,%d6
                move.b  %d6,(%a3)

                movem.l (%sp)+,%d2-%d7/%a2-%a3
                rts
