libc: add Sprinter palette API (graphics + text planes)
palette.h/video/palette.c wrap BIOS $A4 PIC_SET_PAL/PIC_GET_PAL and $A6 SET_PAL_INIT for the 8 palette pages (0-3 graphics, 4-7 text planes). gfx_palette.c and conio/text_palette.c are thin per-domain wrappers; text_palette.c maps the text "plane" 0..3 (paper/ink/blink-paper/ blink-ink) onto BIOS pages 4..7. Covered by tests/text_palette. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* text_palette — interactive smoke test for the text-mode palette API.
|
||||
*
|
||||
* Walks through four palette states. Each stage clears the screen first
|
||||
* and then renders 16 lines, one per ink-colour index, each line showing:
|
||||
*
|
||||
* <RGB values read back from the INK plane>
|
||||
* < part 1: fg = N on bg = 0 — colour N text on the default black >
|
||||
* < part 2: fg = 15 on bg = N — bright-white text on a coloured bg >
|
||||
*
|
||||
* Stages:
|
||||
* 1) Initial CGA palette (boot default).
|
||||
* 2) Grayscale ramp installed via text_pal_load() in all four text
|
||||
* planes (4..7) so flash is fully suppressed; both ink (low nibble)
|
||||
* and paper (mid nibble) get 16 shades — bit 7 of the attribute
|
||||
* byte is folded into the paper index so we cover all 16 bg slots.
|
||||
* 3) CGA defaults restored via pal_reset_at(PAL_CGA, plane, 0) for
|
||||
* plane = 4..7 — explicit per-plane reset in case BIOS interprets
|
||||
* the A register narrowly.
|
||||
* 4) Sinclair palette via pal_reset_at(PAL_SINCLAIR, plane, 0) for
|
||||
* plane = 4..7.
|
||||
*
|
||||
* Memory: tiny (one bank). The 1 KB palette buffer for text_pal_load()
|
||||
* is static — keeps it out of the (small) stack frame.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <conio.h>
|
||||
#include <palette.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* 256 entries × 4 bytes (B, G, R, 0). Static so it lives in DATA. */
|
||||
static uint8_t pal_buf[256 * 4];
|
||||
|
||||
#define ATTR_DEFAULT COLOR(COLOR_LIGHTGRAY, COLOR_BLACK)
|
||||
|
||||
/* For each colour index N=0..15 print:
|
||||
* - RGB triple read back from the INK plane
|
||||
* - sample text in fg=N on bg=0
|
||||
* - sample text in fg=15 on bg=N
|
||||
*
|
||||
* The bg=N attribute uses ALL FOUR bits of the mid nibble (including the
|
||||
* blink bit). In the CGA default this means N >= 8 will flash; with our
|
||||
* grayscale loader the four planes are identical, so it just selects one
|
||||
* of 16 paper colours. */
|
||||
static void show_palette_state(void)
|
||||
{
|
||||
for (uint8_t n = 0; n < 16; n++) {
|
||||
uint8_t r = 0, g = 0, b = 0;
|
||||
text_pal_get_color(TEXT_PAL_INK, n, &r, &g, &b);
|
||||
|
||||
textattr(ATTR_DEFAULT);
|
||||
cprintf(" #%X R=%3u G=%3u B=%3u ", n, r, g, b);
|
||||
|
||||
/* part 1: fg=N on bg=0 */
|
||||
textattr((uint8_t)(n & 0x0F));
|
||||
cprintf(" fg=%X on bg=0 ", n);
|
||||
|
||||
/* part 2: fg=15 on bg=N (4-bit bg slot, may carry blink bit) */
|
||||
textattr((uint8_t)(((n & 0x0F) << 4) | 0x0F));
|
||||
cprintf(" white on bg=%X ", n);
|
||||
|
||||
textattr(ATTR_DEFAULT);
|
||||
cputs("\r\n");
|
||||
}
|
||||
}
|
||||
|
||||
/* Build a 16-step grayscale ramp and load it into ALL FOUR text planes
|
||||
* (paper, ink, blink-paper, blink-ink). Identical paper/blink-paper and
|
||||
* ink/blink-ink means there is nothing to alternate between → no flash.
|
||||
*
|
||||
* INK / BLINK_INK index by attr's low nibble: 16 fg grey levels
|
||||
* PAPER / BLINK_PAPER index by attr's mid nibble: 16 bg grey levels
|
||||
* (full 4 bits incl. bit 7) */
|
||||
static void load_grayscale_all_planes(void)
|
||||
{
|
||||
/* INK and BLINK_INK: shade taken from attr & 0x0F. */
|
||||
for (uint16_t a = 0; a < 256; a++) {
|
||||
uint8_t v = (uint8_t)((a & 0x0F) * 17u); /* 0,17,34,...,255 */
|
||||
pal_buf[a * 4 + 0] = v; /* B */
|
||||
pal_buf[a * 4 + 1] = v; /* G */
|
||||
pal_buf[a * 4 + 2] = v; /* R */
|
||||
pal_buf[a * 4 + 3] = 0;
|
||||
}
|
||||
text_pal_load(TEXT_PAL_INK, 0, 0, pal_buf);
|
||||
text_pal_load(TEXT_PAL_BLINK_INK, 0, 0, pal_buf);
|
||||
|
||||
/* PAPER and BLINK_PAPER: shade taken from (attr >> 4) & 0x0F. */
|
||||
for (uint16_t a = 0; a < 256; a++) {
|
||||
uint8_t v = (uint8_t)(((a >> 4) & 0x0F) * 17u);
|
||||
pal_buf[a * 4 + 0] = v;
|
||||
pal_buf[a * 4 + 1] = v;
|
||||
pal_buf[a * 4 + 2] = v;
|
||||
pal_buf[a * 4 + 3] = 0;
|
||||
}
|
||||
text_pal_load(TEXT_PAL_PAPER, 0, 0, pal_buf);
|
||||
text_pal_load(TEXT_PAL_BLINK_PAPER, 0, 0, pal_buf);
|
||||
}
|
||||
|
||||
/* Reset all four text-mode planes (BIOS palette pages 4..7) to a built-in
|
||||
* default. We loop over the pages explicitly because the BIOS doc isn't
|
||||
* crisp about whether one SET_PAL_INIT call covers all four pages or
|
||||
* just the one named in A. Looping is always safe. */
|
||||
static void reset_all_text_planes(uint8_t type)
|
||||
{
|
||||
for (uint8_t p = 4; p <= 7; p++) {
|
||||
pal_reset_at(type, p, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void wait_key(const char *prompt)
|
||||
{
|
||||
cputs(prompt);
|
||||
(void)getch();
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
uint8_t prev_mode = get_videotextmode();
|
||||
set_videotextmode(TEXT_MODE_80x32);
|
||||
textattr(ATTR_DEFAULT);
|
||||
|
||||
/* ----- (1) Initial CGA palette ---------------------------------- */
|
||||
clrscr();
|
||||
cputs("[1] Initial CGA palette (boot default)\r\n");
|
||||
cputs("---------------------------------------\r\n");
|
||||
show_palette_state();
|
||||
wait_key("\r\nPress any key to install grayscale...");
|
||||
|
||||
/* ----- (2) Grayscale ramp in all four planes -------------------- */
|
||||
load_grayscale_all_planes();
|
||||
clrscr();
|
||||
cputs("[2] Grayscale ramp via text_pal_load() (planes 4..7)\r\n");
|
||||
cputs("-----------------------------------------------------\r\n");
|
||||
show_palette_state();
|
||||
wait_key("\r\nPress any key to restore CGA...");
|
||||
|
||||
/* ----- (3) Restore CGA defaults --------------------------------- */
|
||||
reset_all_text_planes(PAL_CGA);
|
||||
clrscr();
|
||||
cputs("[3] CGA palette restored (planes 4..7)\r\n");
|
||||
cputs("---------------------------------------\r\n");
|
||||
show_palette_state();
|
||||
wait_key("\r\nPress any key to install Sinclair...");
|
||||
|
||||
/* ----- (4) Sinclair palette ------------------------------------- */
|
||||
reset_all_text_planes(PAL_SINCLAIR);
|
||||
clrscr();
|
||||
cputs("[4] Sinclair/Spectrum palette (planes 4..7)\r\n");
|
||||
cputs("--------------------------------------------\r\n");
|
||||
show_palette_state();
|
||||
wait_key("\r\nPress any key to exit...");
|
||||
|
||||
/* Leave the system in a sane state. */
|
||||
reset_all_text_planes(PAL_CGA);
|
||||
textattr(ATTR_DEFAULT);
|
||||
clrscr();
|
||||
set_videotextmode(prev_mode);
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user