// Scanline control byte (SCB) accessors.
//
// Each scanline holds one uint8_t SCB value in range 0..15 selecting
// which of the 16 palettes that scanline uses at display time.

#include <stddef.h>
#include <string.h>

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


// ----- Public API (alphabetical) -----

uint8_t jlScbGet(const jlSurfaceT *s, uint16_t line) {
    if (s == NULL || line >= SURFACE_HEIGHT) {
        return 0;
    }
    return s->scb[line];
}


void jlScbSet(jlSurfaceT *s, uint16_t line, uint8_t paletteIndex) {
    if (s == NULL || line >= SURFACE_HEIGHT) {
        return;
    }
    if (paletteIndex >= SURFACE_PALETTE_COUNT) {
        return;
    }
    s->scb[line] = paletteIndex;
    if (s == gStage) {
        gStageScbDirty = true;
    }
}


void jlScbSetRange(jlSurfaceT *s, uint16_t firstLine, uint16_t lastLine, uint8_t paletteIndex) {
    uint16_t last;

    if (s == NULL) {
        return;
    }
    if (paletteIndex >= SURFACE_PALETTE_COUNT) {
        return;
    }
    if (firstLine >= SURFACE_HEIGHT) {
        return;
    }

    last = lastLine;
    if (last >= SURFACE_HEIGHT) {
        last = SURFACE_HEIGHT - 1;
    }
    if (last < firstLine) {
        return;
    }

#if defined(JOEYLIB_PLATFORM_IIGS)
    // llvm-mos lowers memset to `jsl memset` -- libc's `while (n--)
    // *d++ = c` byte loop behind a far call (the finding-#79 class; the
    // old comment claiming an MVP/PEI lowering was wrong). That one call
    // was essentially the whole cost of this op on the IIgs, and a C
    // word-fill fares no better (llvm-mos re-derives the far pointer
    // every store). One JSL into the asm [dp],y word fill instead.
    {
        uint16_t fill = (uint16_t)((uint16_t)paletteIndex | ((uint16_t)paletteIndex << 8));

        iigsByteFill(&s->scb[firstLine], fill, (uint16_t)(last - firstLine + 1u));
    }
#else
    // memset is far cheaper than the per-iter loop (scb is uint8_t,
    // sizeof(uint8_t)==1, so the call form below is exact); Amiga/ST/DOS
    // libc memset is already a vectorized fill.
    memset(&s->scb[firstLine], paletteIndex, (size_t)(last - firstLine + 1));
#endif
    if (s == gStage) {
        gStageScbDirty = true;
    }
}
