// DOS whole-map tile paste engine (NATIVE-PERF Phase 6 W3). DOS was
// the only port whose jlTileMapPaste ran the generic per-cell path:
// each cell paid a cdecl call into jlpGenericTilePaste, a pixels
// check, a fresh (by+ty)*160 derive, and a ROLLED 8-iteration row
// loop. This engine hoists the check and the derive out of the loop
// and pastes each cell as 8 dword row moves at constant
// displacements -- an 8x8 4bpp tile row is 4 bytes, exactly one
// 32-bit load/store on the 386. Dispatched through the
// jlpTileMapPaste alias from tile.c's shared whole-map arm (the W3
// seam; not a JL_HAS jlp op); tile.c owns the single union dirty
// mark. Included from the tail of src/core/port.h like stTile.h.
#ifndef JOEY_DOS_TILE_H
#define JOEY_DOS_TILE_H

#include <stdint.h>
#include <string.h>

#include "joey/tile.h"
#include "surfaceInternal.h"

// Paste one tile row: memcpy(_,_,4) compiles to ONE 32-bit load/store
// pair on DJGPP -O2 (the genericTile.c GENERIC_TILE_COPY_ROWS
// precedent), and the constant displacements keep both pointers
// loop-invariant -- 16 movs per tile, zero per-row pointer or counter
// updates. i386 allows unaligned 32-bit access (slower when the
// caller's jlTileT source is odd-aligned, never a fault), so no
// 68k-style parity guard or C fallback is needed; the destination is
// always dword-aligned (row stride 160, column offset bx*4).
#define DOS_MAP_CELL_ROW(_r) memcpy(d + (_r) * SURFACE_BYTES_PER_ROW, s + (_r) * TILE_BYTES_PER_ROW, TILE_BYTES_PER_ROW)

static inline __attribute__((always_inline)) void dosTileMapPaste(jlSurfaceT *dst, uint8_t bx, uint8_t by, uint8_t wTiles, uint8_t hTiles, const jlTileT *tiles, const uint8_t *map) {
    const uint8_t *tileBytes;
    const uint8_t *s;
    const uint8_t *m;
    uint8_t       *d;
    uint16_t       dRowStep;
    uint8_t        idx;
    uint8_t        tx;
    uint8_t        ty;

    // One pixels check for the whole map (the generic path re-checks
    // per cell). A pixel-less surface pastes nothing, exactly like
    // jlpGenericTilePaste; tile.c still issues the union mark either
    // way, matching the generic arm's behavior byte-for-byte.
    if (dst->pixels == NULL) {
        return;
    }
    tileBytes = (const uint8_t *)tiles;
    m         = map;
    // Row-0 byte of block (bx, by): same derivation as genericTile.c's
    // GENERIC_TILE_ROW0, hoisted to once per map.
    d         = &dst->pixels[SURFACE_ROW_OFFSET((uint16_t)by << 3) + ((uint16_t)bx << 2)];
    // d advances TILE_BYTES_PER_ROW per cell; after wTiles cells this
    // step lands it on the next tile row's first cell.
    dRowStep  = (uint16_t)((uint16_t)(TILE_PIXELS_PER_SIDE * SURFACE_BYTES_PER_ROW) - ((uint16_t)wTiles << 2));
    for (ty = hTiles; ty != 0u; ty--) {
        for (tx = wTiles; tx != 0u; tx--) {
            idx = *m++;
            if (idx != TILE_MAP_SKIP) {
                // sizeof(jlTileT) is the tileset stride (32; pinned by
                // tile.c's tileMapStrideProof) -- gcc folds this to shl 5.
                s = tileBytes + (size_t)idx * sizeof(jlTileT);
                DOS_MAP_CELL_ROW(0);
                DOS_MAP_CELL_ROW(1);
                DOS_MAP_CELL_ROW(2);
                DOS_MAP_CELL_ROW(3);
                DOS_MAP_CELL_ROW(4);
                DOS_MAP_CELL_ROW(5);
                DOS_MAP_CELL_ROW(6);
                DOS_MAP_CELL_ROW(7);
            }
            d += TILE_BYTES_PER_ROW;
        }
        d += dRowStep;
    }
}
#undef DOS_MAP_CELL_ROW

#endif
