joeylib2/examples/palprobe/palprobe.c
2026-10-19 15:46:47 -05:00

124 lines
4.3 KiB
C

// Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
// See LICENSE.md for license information.
// SPDX-License-Identifier: MIT
//
// Palette-change probe (PALPROBE, a gate's program, not an example): how
// fast palette and SCB changes reach the screen, and whether they reach it
// right.
//
// It times, over PROBE_FRAMES frames each, a palette change followed by a
// present, and an SCB change followed by a present, on a screen of four SCB
// bands, and logs the rates. Then it paints the bands (color 1 of palettes
// 0..3 red, green, blue, white), makes one palette change (palette 2's
// color 1 to yellow) and one SCB change that keeps every band where it was
// (band 3 to palette 0), logs "PALPROBE: ready" and holds the screen for
// HOLD_FRAMES frames, so a gate can look at it: top to bottom red, green,
// yellow, red. scripts/verify-amiga-palette.sh is that gate.
#include <string.h>
#include <joey/joey.h>
#define PROBE_FRAMES 200u
#define HOLD_FRAMES 400u
#define BANDS 4u
#define BAND_LINES 50u
#define PAINT_COLOR 1u
#define MS_PER_SECOND 1000ul
#define COLOR_MASK 0x0FFFu
#define PALETTE_MASK 3u
#define SCB_SWAP_FIRST 0u
#define SCB_SWAP_LAST (BAND_LINES - 1u)
#define YELLOW 0xFF0u
#define CHANGED_PALETTE 2u
#define MOVED_BAND 3u
#define MOVED_TO 0u
static void check(void);
static void timeChanges(const char *name, bool scb);
// Paint the bands, make the two changes, and hold the screen.
static void check(void) {
static const uint16_t firsts[BANDS] = { 0xF00, 0x0F0, 0x00F, 0xFFF };
uint16_t colors[JL_SURFACE_COLORS_PER_PALETTE];
jlSurfaceT *stage;
uint16_t band;
uint16_t frame;
stage = jlStageGet();
for (band = 0; band < BANDS; band++) {
memset(colors, 0, sizeof(colors));
colors[PAINT_COLOR] = firsts[band];
jlPaletteSet(stage, (uint8_t)band, colors);
jlScbSetRange(stage, (uint16_t)(band * BAND_LINES), (uint16_t)(band * BAND_LINES + BAND_LINES - 1u), (uint8_t)band);
jlFillRect(stage, 0, (int16_t)(band * BAND_LINES), JL_SURFACE_WIDTH, BAND_LINES, PAINT_COLOR);
}
jlStagePresent();
jlWaitVBL();
memset(colors, 0, sizeof(colors));
colors[PAINT_COLOR] = YELLOW;
jlPaletteSet(stage, CHANGED_PALETTE, colors);
jlStagePresent();
jlScbSetRange(stage, (uint16_t)(MOVED_BAND * BAND_LINES), (uint16_t)(MOVED_BAND * BAND_LINES + BAND_LINES - 1u), MOVED_TO);
jlStagePresent();
jlLogF("PALPROBE: ready");
for (frame = 0; frame < HOLD_FRAMES; frame++) {
jlWaitVBL();
}
}
// PROBE_FRAMES frames of palette changes (or SCB changes), each presented.
static void timeChanges(const char *name, bool scb) {
uint16_t colors[JL_SURFACE_COLORS_PER_PALETTE];
jlSurfaceT *stage;
uint16_t start;
uint32_t ms0;
uint32_t elapsed;
unsigned long count;
stage = jlStageGet();
memset(colors, 0, sizeof(colors));
count = 0;
jlWaitVBL();
start = jlFrameCount();
ms0 = jlMillisElapsed();
while ((uint16_t)(jlFrameCount() - start) < PROBE_FRAMES) {
if (scb) {
jlScbSetRange(stage, SCB_SWAP_FIRST, SCB_SWAP_LAST, (uint8_t)(count & PALETTE_MASK));
} else {
colors[PAINT_COLOR] = (uint16_t)(count & COLOR_MASK);
jlPaletteSet(stage, (uint8_t)(count & PALETTE_MASK), colors);
}
jlStagePresent();
count++;
}
elapsed = jlMillisElapsed() - ms0;
jlLogF("PALPROBE: %s: %lu in %lu ms = %lu/s", name, count, (unsigned long)elapsed, (unsigned long)(count * MS_PER_SECOND / elapsed));
}
int main(void) {
jlConfigT cfg;
jlSurfaceT *stage;
uint16_t band;
cfg.codegenBytes = 0;
cfg.noAudio = true;
if (!jlInit(&cfg)) {
return 1;
}
stage = jlStageGet();
for (band = 0; band < BANDS; band++) {
jlScbSetRange(stage, (uint16_t)(band * BAND_LINES), (uint16_t)(band * BAND_LINES + BAND_LINES - 1u), (uint8_t)band);
jlFillRect(stage, 0, (int16_t)(band * BAND_LINES), JL_SURFACE_WIDTH, BAND_LINES, (uint8_t)(band + 1u));
}
jlStagePresent();
timeChanges("palette+present", false);
timeChanges("scb+present", true);
check();
jlLogF("PALPROBE: done");
jlShutdown();
return 0;
}