| ST whole-map tile-paste engine, movep.l row body (NATIVE-PERF
| Phase 4).
|
| One call pastes a wTiles x hTiles map of 8x8 tiles. The copy body
| uses the 68000's byte-lane scatter instruction:
|
|   move.l  (%a0)+,%d0        | one tile row: 4 plane bytes, 12 cyc
|   movep.l %d0,d16(%a1)      | writes d16, d16+2, d16+4, d16+6, 24 cyc
|
| = 36 cyc per tile row vs ~80 for the four move.b the compiler emits
| (movep writes MSB first, matching the plane-major tile layout and
| the interleaved d16 0/2/4/6 dst pattern of stTilePaste). movem is
| useless here -- the dst is a byte scatter -- but movep IS this
| access pattern. Cells advance in even/odd pairs sharing one group
| pointer (+8/pair); an odd bx adds a head cell (displacements +1),
| an odd width a tail cell. Row displacements are compile-time
| constants (row r at r*160), so the group pointer only moves once
| per pair.
|
| void stTileMapPasteMovep(uint8_t *rowBase, const uint8_t *tiles,
|                          const uint8_t *map, uint16_t wTiles,
|                          uint16_t hTiles, uint16_t headOdd);
|
| rowBase = planar base + by*1280 + (bx>>1)*8 (group-aligned; the
| caller keeps the bx&1 head flag). CONTRACT: tiles must be EVEN
| (move.l source alignment on 68000); the C caller guards and falls
| back to the byte-copy engine for odd tileset pointers. Map bytes of
| 0xFF (TILE_MAP_SKIP) leave the cell untouched but still advance the
| walk. wTiles/hTiles >= 1 (jlTileMapPaste validated).
|
| cdecl args land in 4-byte slots (uint16_t value at slot+2), same
| convention as surface68kStTileFill8x8 (lineSpan.s). d2-d7/a2-a6 are
| callee-save; register lists (not hand masks) keep the predec movem
| mask correct.
|
| Register map: a0 tile src (scratch), a1 group walker, a2 map
| cursor, a3 tileset base, a4 rowBase (+1280/map row); d0 movep data,
| d1 map byte, d2 pairs/row, d3 tail flag, d4 rows left, d5 head
| flag, d6 pair countdown.
|
| GAS m68k syntax, assembled by m68k-atari-mint-as. ST-only: this
| file must stay in src/atarist/ (src/m68k/*.s is wildcarded into the
| Amiga build too).

                .equ    MP_SAVED,   32         | d2-d6/a2-a4 = 8 longs
                .equ    MP_OFF,     (MP_SAVED + 4)
                .equ    MP_BASE,    MP_OFF + 0
                .equ    MP_TILES,   MP_OFF + 4
                .equ    MP_MAP,     MP_OFF + 8
                .equ    MP_WTILES,  MP_OFF + 12 + 2
                .equ    MP_HTILES,  MP_OFF + 16 + 2
                .equ    MP_HEAD,    MP_OFF + 20 + 2

| Shared 8-row movep bodies (NATIVE-PERF W3). One tile row = 4 plane
| bytes at d16 0/2/4/6. Row strides are the compile-time constants the
| file already uses (160 bytes/scanline, 8 rows/tile).
|   MTR_PASTE_ROW: tile buffer -> screen scatter (a0 postinc EVEN src,
|                  movep dst -- either parity, movep is byte-lane).
|   MTR_COPY_ROW:  screen -> screen, movep gather + movep scatter
|                  (either parity BOTH sides; no alignment contract).
|   MTR_SNAP_ROW:  screen -> tile buffer gather (movep src either
|                  parity, a1 postinc EVEN dst).
                .macro  MTR_PASTE_ROW  disp
                move.l  (%a0)+,%d0
                movep.l %d0,\disp(%a1)
                .endm


                .macro  MTR_COPY_ROW  disp
                movep.l \disp(%a0),%d0
                movep.l %d0,\disp(%a1)
                .endm


                .macro  MTR_SNAP_ROW  disp
                movep.l \disp(%a0),%d0
                move.l  %d0,(%a1)+
                .endm

| One tile cell: derive src from the map byte in d1, then 8 rows of
| move.l + movep.l at half-group displacement \off (0 = even cell,
| 1 = odd cell). Falls straight through on TILE_MAP_SKIP.
                .macro  MP_CELL  off, skip
                moveq   #0,%d1
                move.b  (%a2)+,%d1             | map byte
                cmp.b   #-1,%d1                | TILE_MAP_SKIP?
                beq.s   \skip
                lsl.w   #5,%d1                 | idx * sizeof(jlTileT)
                lea     (%a3,%d1.w),%a0        | tile src (even by contract)
                MTR_PASTE_ROW  \off+0
                MTR_PASTE_ROW  \off+160
                MTR_PASTE_ROW  \off+320
                MTR_PASTE_ROW  \off+480
                MTR_PASTE_ROW  \off+640
                MTR_PASTE_ROW  \off+800
                MTR_PASTE_ROW  \off+960
                MTR_PASTE_ROW  \off+1120
\skip:
                .endm

                .text

                .globl  _stTileMapPasteMovep

_stTileMapPasteMovep:
                movem.l %d2-%d6/%a2-%a4,-(%sp)

                movea.l MP_BASE(%sp),%a4       | a4 = rowBase
                movea.l MP_TILES(%sp),%a3      | a3 = tileset base
                movea.l MP_MAP(%sp),%a2        | a2 = map cursor
                move.w  MP_WTILES(%sp),%d3
                move.w  MP_HTILES(%sp),%d4     | d4 = rows left
                move.w  MP_HEAD(%sp),%d5       | d5 = 1 if bx odd
                sub.w   %d5,%d3
                move.w  %d3,%d2
                lsr.w   #1,%d2                 | d2 = pairs per row
                and.w   #1,%d3                 | d3 = tail cell flag

.LmpRow:
                movea.l %a4,%a1                | a1 = this row's group ptr

                tst.w   %d5
                beq.s   .LmpNoHead
                MP_CELL 1, .LmpHeadSkip
                addq.l  #8,%a1
.LmpNoHead:
                move.w  %d2,%d6                | pair countdown
                beq.w   .LmpNoPairs
                subq.w  #1,%d6
.LmpPair:
                MP_CELL 0, .LmpEvenSkip
                MP_CELL 1, .LmpOddSkip
                addq.l  #8,%a1
                dbra    %d6,.LmpPair
.LmpNoPairs:
                tst.w   %d3
                beq.s   .LmpNoTail
                MP_CELL 0, .LmpTailSkip
.LmpNoTail:
                lea     1280(%a4),%a4          | next tile row of the map
                subq.w  #1,%d4
                bne.w   .LmpRow

                movem.l (%sp)+,%d2-%d6/%a2-%a4
                rts


| ---- Single-tile entries (NATIVE-PERF W3) --------------------------
|
| Register-args leaves jsr'd from the stTile.h always-inline wrappers.
| NO stacked args, NO saved registers (d0/a0/a1 are the m68k cdecl
| scratch set), NO loop -- each entry IS the MP_CELL row body. The
| only per-call tax over inlining is jsr+rts (~36 cyc), which is how
| this dodges the Phase-10 per-call-asm 4x loss (that shape paid a
| movem prologue + stacked args + in-asm derive + dbra per call).
|
| stTilePasteMovepRegs: a0 = tile src (EVEN, C guards), a1 = screen
|   dst byte (either parity). Clobbers d0, a0.
| stTileCopyMovepRegs:  a0 = screen src byte, a1 = screen dst byte
|   (either parity both sides). Clobbers d0.
| stTileSnapMovepRegs:  a0 = screen src byte (either parity), a1 =
|   tile out ptr (EVEN, C guards). Clobbers d0, a1.

                .globl  _stTilePasteMovepRegs
                .globl  _stTileCopyMovepRegs
                .globl  _stTileSnapMovepRegs

_stTilePasteMovepRegs:
                MTR_PASTE_ROW  0
                MTR_PASTE_ROW  160
                MTR_PASTE_ROW  320
                MTR_PASTE_ROW  480
                MTR_PASTE_ROW  640
                MTR_PASTE_ROW  800
                MTR_PASTE_ROW  960
                MTR_PASTE_ROW  1120
                rts


_stTileCopyMovepRegs:
                MTR_COPY_ROW   0
                MTR_COPY_ROW   160
                MTR_COPY_ROW   320
                MTR_COPY_ROW   480
                MTR_COPY_ROW   640
                MTR_COPY_ROW   800
                MTR_COPY_ROW   960
                MTR_COPY_ROW   1120
                rts


_stTileSnapMovepRegs:
                MTR_SNAP_ROW   0
                MTR_SNAP_ROW   160
                MTR_SNAP_ROW   320
                MTR_SNAP_ROW   480
                MTR_SNAP_ROW   640
                MTR_SNAP_ROW   800
                MTR_SNAP_ROW   960
                MTR_SNAP_ROW   1120
                rts
