| ST byte-aligned sprite save / restore. Buffer holds plane-major
| bytes: per row, plane0/1/2/3 per tile col, for w/8 tile cols. The
| inner per-tile-col macro is 4 byte copies (no chunky <-> planar
| conversion since the buffer matches the surface's plane layout).
|
| ABI: cdecl. d2-d7/a2-a6 callee-save. C signatures:
|
|   void surface68kStSpriteSaveByteAligned(uint8_t *base,
|                                          uint16_t x, uint16_t y,
|                                          uint16_t w, uint16_t h,
|                                          uint8_t *dstPlaneBytes);
|
|   void surface68kStSpriteRestoreByteAligned(uint8_t *base,
|                                             uint16_t x, uint16_t y,
|                                             uint16_t w, uint16_t h,
|                                             const uint8_t *srcPlaneBytes);

                .text


                .equ    SP_SAVED, 44
                .equ    SP_OFF,   (SP_SAVED + 4)
                .equ    SP_BASE,    SP_OFF + 0
                .equ    SP_X,       SP_OFF + 4 + 2
                .equ    SP_Y,       SP_OFF + 8 + 2
                .equ    SP_W,       SP_OFF + 12 + 2
                .equ    SP_H,       SP_OFF + 16 + 2
                .equ    SP_BUF,     SP_OFF + 20


| Per-tile-col SAVE: 4 plane bytes -> 4 contiguous bytes in buffer.
| a0 -> plane 0 byte (high or low half), strides 2 to next plane
| a1 -> output planar bytes (advanced by 4)

                .macro  SAVE_TILECOL
                move.b  (%a0),(%a1)+           | plane 0
                move.b  2(%a0),(%a1)+          | plane 1
                move.b  4(%a0),(%a1)+          | plane 2
                move.b  6(%a0),(%a1)+          | plane 3
                .endm


                .globl  _surface68kStSpriteSaveByteAligned

_surface68kStSpriteSaveByteAligned:
                movem.l %d2-%d7/%a2-%a6,-(%sp)

                move.l  SP_BASE(%sp),%a3
                move.l  SP_BUF(%sp),%a1

                move.w  SP_W(%sp),%d5
                lsr.w   #3,%d5                 | d5 = tileCols
                move.w  SP_H(%sp),%d6          | d6 = h
                move.w  SP_X(%sp),%d7

                | a4 = base + y*160 + (x>>4)*8
                move.w  SP_Y(%sp),%d0
                ext.l   %d0
                move.l  %d0,%d1
                lsl.l   #5,%d0                 | y << 5
                lsl.l   #7,%d1                 | y << 7
                add.l   %d1,%d0                | y * 160
                lea     0(%a3,%d0.l),%a4
                moveq   #0,%d0
                move.w  %d7,%d0
                lsr.w   #4,%d0
                lsl.w   #3,%d0
                ext.l   %d0
                add.l   %d0,%a4

                | Initial half offset: (x & 8) >> 3 = 0 or 1
                and.w   #8,%d7
                lsr.w   #3,%d7

.LsaveRow:
                move.w  %d5,%d3                | d3 = tileCols
                moveq   #0,%d2
                move.w  %d7,%d2
                lea     0(%a4,%d2.l),%a0       | a0 = first plane-0 byte

.LsaveCol:
                SAVE_TILECOL
                | Advance a0: bit 0 = 0 -> high, advance to low (+1).
                | bit 0 = 1 -> low, advance to next group's high (+7).
                move.l  %a0,%d4
                btst    #0,%d4
                bne.s   .LsaveColWasLo
                addq.l  #1,%a0
                bra.s   .LsaveColNext
.LsaveColWasLo:
                lea     7(%a0),%a0
.LsaveColNext:
                subq.w  #1,%d3
                bne.w   .LsaveCol

                lea     160(%a4),%a4
                subq.w  #1,%d6
                bne.w   .LsaveRow

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


| Per-tile-col RESTORE: 4 contiguous bytes from buffer -> 4 plane bytes.
| a0 -> plane 0 byte (high or low half)
| a1 -> input planar bytes (advanced by 4)

                .macro  RESTORE_TILECOL
                move.b  (%a1)+,(%a0)           | plane 0
                move.b  (%a1)+,2(%a0)          | plane 1
                move.b  (%a1)+,4(%a0)          | plane 2
                move.b  (%a1)+,6(%a0)          | plane 3
                .endm


                .globl  _surface68kStSpriteRestoreByteAligned

_surface68kStSpriteRestoreByteAligned:
                movem.l %d2-%d7/%a2-%a6,-(%sp)

                move.l  SP_BASE(%sp),%a3
                move.l  SP_BUF(%sp),%a1

                | tileCols is held in a5 (not d5) because the macro
                | trashes d5 (uses it for pb3).
                move.w  SP_W(%sp),%d0
                lsr.w   #3,%d0
                movea.w %d0,%a5
                move.w  SP_H(%sp),%d6
                move.w  SP_X(%sp),%d7

                move.w  SP_Y(%sp),%d0
                ext.l   %d0
                move.l  %d0,%d1
                lsl.l   #5,%d0
                lsl.l   #7,%d1
                add.l   %d1,%d0
                lea     0(%a3,%d0.l),%a4
                moveq   #0,%d0
                move.w  %d7,%d0
                lsr.w   #4,%d0
                lsl.w   #3,%d0
                ext.l   %d0
                add.l   %d0,%a4

                and.w   #8,%d7
                lsr.w   #3,%d7

.LrestoreRow:
                move.w  %a5,%d3                | d3 = tileCols (from a5)
                moveq   #0,%d2
                move.w  %d7,%d2
                lea     0(%a4,%d2.l),%a0

.LrestoreCol:
                RESTORE_TILECOL
                move.l  %a0,%d4
                btst    #0,%d4
                bne.s   .LrestoreColWasLo
                addq.l  #1,%a0
                bra.s   .LrestoreColNext
.LrestoreColWasLo:
                lea     7(%a0),%a0
.LrestoreColNext:
                subq.w  #1,%d3
                bne.w   .LrestoreCol

                lea     160(%a4),%a4
                subq.w  #1,%d6
                bne.w   .LrestoreRow

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


| ---- surface68kStSprite16x16Save / Restore -----------------------
|
| Specialized 16x16 sprite save/restore: 16 rows fully unrolled,
| 8 byte copies per row (2 tile cols), no col loop. Drops the asm
| walker's per-row col-init + col-loop-check overhead.
|
|   void surface68kStSprite16x16Save(uint8_t *base,
|                                    uint16_t x, uint16_t y,
|                                    uint8_t *dstBuf);
|
|   void surface68kStSprite16x16Restore(uint8_t *base,
|                                       uint16_t x, uint16_t y,
|                                       const uint8_t *srcBuf);
|
| Caller guarantees x is byte-aligned (x mod 8 == 0). Two halfOff
| variants dispatch on (x & 8): halfOff=0 reads/writes within one
| group (offsets 0/2/4/6 high half + 1/3/5/7 low half). halfOff=1
| spans two groups (low half of group N + high half of group N+1).

                .equ    SP16_SAVED, 12         | d2/a2-a3 = 3 longs
                .equ    SP16_OFF,         (SP16_SAVED + 4)
                .equ    SP16_BASE,    SP16_OFF + 0
                .equ    SP16_X,       SP16_OFF + 4 + 2
                .equ    SP16_Y,       SP16_OFF + 8 + 2
                .equ    SP16_BUF,     SP16_OFF + 12


| Macro: setup a0 = base + y*160 + group*8 + halfOff
| Trashes: d0, d1, d2; a0 left at row start

                .macro  SP16_SETUP_A0
                move.l  SP16_BASE(%sp),%a3
                move.w  SP16_X(%sp),%d0
                move.w  SP16_Y(%sp),%d1

                | a0 = base + y*160
                ext.l   %d1
                move.l  %d1,%d2
                lsl.l   #5,%d1
                lsl.l   #7,%d2
                add.l   %d2,%d1
                lea     0(%a3,%d1.l),%a0

                | a0 += (x>>4) * 8
                move.w  %d0,%d1
                lsr.w   #4,%d1
                lsl.w   #3,%d1
                ext.l   %d1
                add.l   %d1,%a0

                | a0 += halfOff (= (x & 8) >> 3)
                and.w   #8,%d0
                lsr.w   #3,%d0
                ext.l   %d0
                add.l   %d0,%a0
                | d0 = halfOff (0 or 1) for downstream dispatch
                .endm


                .globl  _surface68kStSprite16x16Save

_surface68kStSprite16x16Save:
                movem.l %d2/%a2-%a3,-(%sp)
                SP16_SETUP_A0
                move.l  SP16_BUF(%sp),%a1

                tst.w   %d0
                bne.w   .Lsp16s_low

                | halfOff=0: a0 at high half. Col 0 = high (offsets
                | 0,2,4,6); col 1 = low (offsets 1,3,5,7).
                .rept   16
                move.b  (%a0),(%a1)+
                move.b  2(%a0),(%a1)+
                move.b  4(%a0),(%a1)+
                move.b  6(%a0),(%a1)+
                move.b  1(%a0),(%a1)+
                move.b  3(%a0),(%a1)+
                move.b  5(%a0),(%a1)+
                move.b  7(%a0),(%a1)+
                lea     160(%a0),%a0
                .endr
                bra.w   .Lsp16s_done

.Lsp16s_low:
                | halfOff=1: a0 at low half (group+1). Col 0 = low of
                | this group, offsets 0,2,4,6 from a0. Col 1 = high of
                | next group, at offsets 7,9,11,13 from a0.
                .rept   16
                move.b  (%a0),(%a1)+
                move.b  2(%a0),(%a1)+
                move.b  4(%a0),(%a1)+
                move.b  6(%a0),(%a1)+
                move.b  7(%a0),(%a1)+
                move.b  9(%a0),(%a1)+
                move.b  11(%a0),(%a1)+
                move.b  13(%a0),(%a1)+
                lea     160(%a0),%a0
                .endr

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


                .globl  _surface68kStSprite16x16Restore

_surface68kStSprite16x16Restore:
                movem.l %d2/%a2-%a3,-(%sp)
                SP16_SETUP_A0
                move.l  SP16_BUF(%sp),%a1

                tst.w   %d0
                bne.w   .Lsp16r_low

                | halfOff=0: write high half (col 0) + low half (col 1).
                .rept   16
                move.b  (%a1)+,(%a0)
                move.b  (%a1)+,2(%a0)
                move.b  (%a1)+,4(%a0)
                move.b  (%a1)+,6(%a0)
                move.b  (%a1)+,1(%a0)
                move.b  (%a1)+,3(%a0)
                move.b  (%a1)+,5(%a0)
                move.b  (%a1)+,7(%a0)
                lea     160(%a0),%a0
                .endr
                bra.w   .Lsp16r_done

.Lsp16r_low:
                | halfOff=1
                .rept   16
                move.b  (%a1)+,(%a0)
                move.b  (%a1)+,2(%a0)
                move.b  (%a1)+,4(%a0)
                move.b  (%a1)+,6(%a0)
                move.b  (%a1)+,7(%a0)
                move.b  (%a1)+,9(%a0)
                move.b  (%a1)+,11(%a0)
                move.b  (%a1)+,13(%a0)
                lea     160(%a0),%a0
                .endr

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