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,42 @@
|
|||||||
|
/*
|
||||||
|
* text_palette.c — text-mode palette wrappers.
|
||||||
|
*
|
||||||
|
* Thin layer over libc/video/palette.c. Translates the text "plane
|
||||||
|
* number" 0..3 (paper/ink/blink-paper/blink-ink) into the underlying
|
||||||
|
* BIOS palette page 4..7 used by $A4.
|
||||||
|
*
|
||||||
|
* For an introduction to the four-plane text colour model see
|
||||||
|
* <palette.h> (top-of-file doc-block).
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <conio.h>
|
||||||
|
#include <palette.h>
|
||||||
|
|
||||||
|
void text_pal_load(uint8_t plane, uint8_t start, uint8_t count,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
pal_load((uint8_t)(plane + 4u), start, count, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_pal_set_color(uint8_t plane, uint8_t attr,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
pal_set_color((uint8_t)(plane + 4u), attr, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_pal_get(uint8_t plane, uint8_t start, uint8_t count, uint8_t *data)
|
||||||
|
{
|
||||||
|
pal_get((uint8_t)(plane + 4u), start, count, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_pal_get_color(uint8_t plane, uint8_t attr,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||||
|
{
|
||||||
|
pal_get_color((uint8_t)(plane + 4u), attr, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void text_pal_reset(void)
|
||||||
|
{
|
||||||
|
pal_reset(PAL_CGA);
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* gfx_palette.c — graphics-side palette wrappers.
|
||||||
|
*
|
||||||
|
* The "legacy" entry points (gfx_pal_load / gfx_pal_set) keep their own
|
||||||
|
* inline-asm implementation rather than thunking through pal_*; this
|
||||||
|
* avoids depending on SDCC's __sdcccall(1) stack-shuffling for nested
|
||||||
|
* calls, which empirically misbehaves here (the trampoline corrupts the
|
||||||
|
* text palette so the next text-mode print hangs). Functionally these
|
||||||
|
* are identical to the old gfx_core.c versions.
|
||||||
|
*
|
||||||
|
* The newer get / reset helpers are simple wrappers — they're either
|
||||||
|
* not on hot paths or are first introduced here, so the thunk overhead
|
||||||
|
* doesn't matter and we get to share the asm with libc/video/palette.c.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <gfx.h>
|
||||||
|
#include <palette.h>
|
||||||
|
|
||||||
|
static uint8_t gpl_num_;
|
||||||
|
static uint8_t gpl_start_;
|
||||||
|
static uint8_t gpl_count_;
|
||||||
|
static uint16_t gpl_data_;
|
||||||
|
|
||||||
|
void gfx_pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
||||||
|
const uint8_t *data)
|
||||||
|
{
|
||||||
|
gpl_num_ = pal_num;
|
||||||
|
gpl_start_ = start;
|
||||||
|
gpl_count_ = count;
|
||||||
|
gpl_data_ = (uint16_t)(uintptr_t)data;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
push ix
|
||||||
|
ld a, (_gpl_start_)
|
||||||
|
ld e, a ; E = start
|
||||||
|
ld a, (_gpl_count_)
|
||||||
|
ld d, a ; D = count (0 → 256)
|
||||||
|
ld hl, (_gpl_data_) ; HL = data
|
||||||
|
ld b, #0xFF ; mask
|
||||||
|
ld a, (_gpl_num_) ; A = palette number
|
||||||
|
ld c, #0xA4 ; BIOS PIC_SET_PAL
|
||||||
|
rst #0x08
|
||||||
|
pop ix
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gfx_pal_set(uint8_t pal_num, uint8_t idx,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
uint8_t entry[4];
|
||||||
|
entry[0] = b;
|
||||||
|
entry[1] = g;
|
||||||
|
entry[2] = r;
|
||||||
|
entry[3] = 0;
|
||||||
|
gfx_pal_load(pal_num, idx, 1, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gfx_pal_get(uint8_t pal_num, uint8_t start, uint8_t count,
|
||||||
|
uint8_t *data)
|
||||||
|
{
|
||||||
|
pal_get(pal_num, start, count, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gfx_pal_get_color(uint8_t pal_num, uint8_t idx,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||||
|
{
|
||||||
|
pal_get_color(pal_num, idx, r, g, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gfx_pal_reset(void)
|
||||||
|
{
|
||||||
|
pal_reset(PAL_GRAPH);
|
||||||
|
}
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
/*
|
||||||
|
* palette.h — Sprinter palette (low-level).
|
||||||
|
*
|
||||||
|
* The Sprinter has eight 256-colour palette pages, shared by graphics and
|
||||||
|
* text modes:
|
||||||
|
*
|
||||||
|
* pal_num 0..3 — graphics palettes. Each character cell selects which
|
||||||
|
* of the four it uses through bits 7..6 of its mode byte.
|
||||||
|
* Used by graphics modes 0x81 / 0x82.
|
||||||
|
* pal_num 4..7 — text-mode planes — together they form the colour table
|
||||||
|
* of the text-mode attribute byte:
|
||||||
|
* 4 = paper (background, non-blink phase)
|
||||||
|
* 5 = ink (foreground, non-blink phase)
|
||||||
|
* 6 = paper-blink (background during blink half-cycle)
|
||||||
|
* 7 = ink-blink (foreground during blink half-cycle)
|
||||||
|
* Each plane holds 256 BGR colours indexed directly by
|
||||||
|
* the 8-bit attribute byte of the cell.
|
||||||
|
*
|
||||||
|
* Entry format — 4 bytes per colour, in Blue-Green-Red-pad order; the pad
|
||||||
|
* byte is reserved and must be 0.
|
||||||
|
*
|
||||||
|
* Notes on the underlying BIOS ($A4):
|
||||||
|
* - `count = 0` means 256 entries (full plane).
|
||||||
|
* - On write, data is AND-masked against `pal_mask` before reaching
|
||||||
|
* VRAM. pal_load/pal_set_color hard-code mask = 0xFF (no masking).
|
||||||
|
*
|
||||||
|
* Blink semantics (text planes):
|
||||||
|
* The hardware constantly alternates between planes 4↔6 (paper) and
|
||||||
|
* 5↔7 (ink). To DISABLE blink everywhere in IBM-CGA style, copy plane
|
||||||
|
* 4 → 6 and 5 → 7 (so both phases show the same colour). To ENABLE
|
||||||
|
* ZX-Spectrum style flash, swap entries 6 ↔ 7 for the desired attrs.
|
||||||
|
* The system default (PAL_CGA) leaves flash visible only for attribute
|
||||||
|
* bytes with bit 7 set.
|
||||||
|
*
|
||||||
|
* Backed by BIOS PIC_SET_PAL/PIC_GET_PAL ($A4) and SET_PAL_INIT ($A6).
|
||||||
|
* Higher-level wrappers live in <conio.h> (text_pal_*) and <gfx.h>
|
||||||
|
* (gfx_pal_*); use those directly unless you need raw plane control.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PALETTE_H
|
||||||
|
#define PALETTE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/* Default-palette types for pal_reset(). Values match BIOS $A6 B-register. */
|
||||||
|
#define PAL_GRAPH 1 /* graphics palette (planes 0..3) */
|
||||||
|
#define PAL_SINCLAIR 2 /* Spectrum palette (text planes 4..7, ZX colours) */
|
||||||
|
#define PAL_CGA 3 /* CGA text palette (text planes 4..7, IBM CGA) */
|
||||||
|
|
||||||
|
/* Load a contiguous block of palette entries.
|
||||||
|
* pal_num: 0..7 (0..3 graphics, 4..7 text)
|
||||||
|
* start: first slot (0..255)
|
||||||
|
* count: number of slots (0 means 256)
|
||||||
|
* bgr0: pointer to count entries of 4 bytes each: Blue, Green, Red, 0 */
|
||||||
|
void pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
||||||
|
const uint8_t *bgr0);
|
||||||
|
|
||||||
|
/* Read a contiguous block of palette entries back into RAM.
|
||||||
|
* Same parameter shape as pal_load — bgr0 here is a write buffer of
|
||||||
|
* count*4 bytes that receives B,G,R,0 quadruples. */
|
||||||
|
void pal_get (uint8_t pal_num, uint8_t start, uint8_t count,
|
||||||
|
uint8_t *bgr0);
|
||||||
|
|
||||||
|
/* Convenience: set one entry from an RGB triple. */
|
||||||
|
void pal_set_color(uint8_t pal_num, uint8_t slot,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b);
|
||||||
|
|
||||||
|
/* Convenience: read one entry into R,G,B pointers (any may be NULL). */
|
||||||
|
void pal_get_color(uint8_t pal_num, uint8_t slot,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b);
|
||||||
|
|
||||||
|
/* Restore a built-in default palette (BIOS $A6 SET_PAL_INIT).
|
||||||
|
* type: PAL_GRAPH / PAL_SINCLAIR / PAL_CGA.
|
||||||
|
* Internally:
|
||||||
|
* PAL_GRAPH → A=0, E=0, B=1 (resets graphics palette 0)
|
||||||
|
* PAL_SINCLAIR → A=0, E=0, B=2
|
||||||
|
* PAL_CGA → A=0, E=0, B=3 (resets all text planes 4..7)
|
||||||
|
*
|
||||||
|
* Use pal_reset_at() if you need a non-zero page or a non-zero graphics
|
||||||
|
* palette index. */
|
||||||
|
void pal_reset(uint8_t type);
|
||||||
|
|
||||||
|
/* Full-control variant of pal_reset.
|
||||||
|
* pal_page: BIOS A register — "palette page" hardware index.
|
||||||
|
* graph_pal: BIOS E register — for PAL_GRAPH, target palette 0..3.
|
||||||
|
* type: BIOS B register — PAL_GRAPH / PAL_SINCLAIR / PAL_CGA. */
|
||||||
|
void pal_reset_at(uint8_t type, uint8_t pal_page, uint8_t graph_pal);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* palette.c — Sprinter palette (BIOS $A4 PIC_SET_PAL / PIC_GET_PAL,
|
||||||
|
* BIOS $A6 SET_PAL_INIT).
|
||||||
|
*
|
||||||
|
* Direction in $A4 is selected by bit 7 of A:
|
||||||
|
* A = pal_num → write entries from RAM to VRAM
|
||||||
|
* A = 0x80 | pal_num → read entries from VRAM into RAM
|
||||||
|
*
|
||||||
|
* Lives under libc/video/ because palette control is shared between
|
||||||
|
* graphics (mode 0x81/0x82) and text (mode 0x03) — pal_num 0..3 for
|
||||||
|
* graphics, 4..7 for the four text-mode planes. The conio and gfx
|
||||||
|
* subsystems each ship thin domain-specific wrappers on top.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <palette.h>
|
||||||
|
|
||||||
|
/* Statics used to pass parameters to the inline asm — SDCC __sdcccall(1)
|
||||||
|
* gives us only HL natively, so the rest go through memory. */
|
||||||
|
static uint8_t pal_num_;
|
||||||
|
static uint8_t pal_start_;
|
||||||
|
static uint8_t pal_count_;
|
||||||
|
static uint16_t pal_data_;
|
||||||
|
|
||||||
|
/* ---- $A4 PIC_SET_PAL — write entries to VRAM --------------------- */
|
||||||
|
|
||||||
|
void pal_load(uint8_t pal_num, uint8_t start, uint8_t count,
|
||||||
|
const uint8_t *bgr0)
|
||||||
|
{
|
||||||
|
pal_num_ = pal_num;
|
||||||
|
pal_start_ = start;
|
||||||
|
pal_count_ = count;
|
||||||
|
pal_data_ = (uint16_t)(uintptr_t)bgr0;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
push ix
|
||||||
|
ld a, (_pal_start_)
|
||||||
|
ld e, a ; E = start
|
||||||
|
ld a, (_pal_count_)
|
||||||
|
ld d, a ; D = count (0 → 256)
|
||||||
|
ld hl, (_pal_data_) ; HL = data
|
||||||
|
ld b, #0xFF ; B = mask (no AND)
|
||||||
|
ld a, (_pal_num_) ; A = palette number (bit7 = 0 → write)
|
||||||
|
ld c, #0xA4 ; BIOS PIC_SET_PAL
|
||||||
|
rst #0x08
|
||||||
|
pop ix
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- $A4 PIC_GET_PAL — read entries from VRAM -------------------- *
|
||||||
|
* Same function id; bit 7 of A flips it to read mode. */
|
||||||
|
|
||||||
|
void pal_get(uint8_t pal_num, uint8_t start, uint8_t count, uint8_t *bgr0)
|
||||||
|
{
|
||||||
|
pal_num_ = (uint8_t)(pal_num | 0x80); /* bit 7 = read */
|
||||||
|
pal_start_ = start;
|
||||||
|
pal_count_ = count;
|
||||||
|
pal_data_ = (uint16_t)(uintptr_t)bgr0;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
push ix
|
||||||
|
ld a, (_pal_start_)
|
||||||
|
ld e, a ; E = start
|
||||||
|
ld a, (_pal_count_)
|
||||||
|
ld d, a ; D = count (0 → 256)
|
||||||
|
ld hl, (_pal_data_) ; HL = buffer
|
||||||
|
ld b, #0xFF ; B = mask
|
||||||
|
ld a, (_pal_num_) ; A = 0x80 | pal_num → read
|
||||||
|
ld c, #0xA4 ; BIOS PIC_GET_PAL (= $A4)
|
||||||
|
rst #0x08
|
||||||
|
pop ix
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- one-colour helpers ------------------------------------------ */
|
||||||
|
|
||||||
|
void pal_set_color(uint8_t pal_num, uint8_t slot,
|
||||||
|
uint8_t r, uint8_t g, uint8_t b)
|
||||||
|
{
|
||||||
|
uint8_t entry[4];
|
||||||
|
entry[0] = b;
|
||||||
|
entry[1] = g;
|
||||||
|
entry[2] = r;
|
||||||
|
entry[3] = 0;
|
||||||
|
pal_load(pal_num, slot, 1, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pal_get_color(uint8_t pal_num, uint8_t slot,
|
||||||
|
uint8_t *r, uint8_t *g, uint8_t *b)
|
||||||
|
{
|
||||||
|
uint8_t entry[4];
|
||||||
|
pal_get(pal_num, slot, 1, entry);
|
||||||
|
if (b) *b = entry[0];
|
||||||
|
if (g) *g = entry[1];
|
||||||
|
if (r) *r = entry[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- $A6 SET_PAL_INIT — restore a built-in default palette ------- *
|
||||||
|
* Signature: A = pal_page, E = graphics palette index (0..3), B = type.
|
||||||
|
* Type is one of PAL_GRAPH (1) / PAL_SINCLAIR (2) / PAL_CGA (3). */
|
||||||
|
|
||||||
|
static uint8_t reset_page_;
|
||||||
|
static uint8_t reset_graph_;
|
||||||
|
static uint8_t reset_type_;
|
||||||
|
|
||||||
|
void pal_reset_at(uint8_t type, uint8_t pal_page, uint8_t graph_pal)
|
||||||
|
{
|
||||||
|
reset_type_ = type;
|
||||||
|
reset_page_ = pal_page;
|
||||||
|
reset_graph_ = graph_pal;
|
||||||
|
|
||||||
|
__asm
|
||||||
|
push ix
|
||||||
|
ld a, (_reset_graph_)
|
||||||
|
ld e, a ; E = graphics palette index (0..3)
|
||||||
|
ld a, (_reset_type_)
|
||||||
|
ld b, a ; B = type (1=GRAPH, 2=SINCLAIR, 3=CGA)
|
||||||
|
ld a, (_reset_page_) ; A = palette page (last — A is needed)
|
||||||
|
ld c, #0xA6 ; BIOS SET_PAL_INIT
|
||||||
|
rst #0x08
|
||||||
|
pop ix
|
||||||
|
__endasm;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pal_reset(uint8_t type)
|
||||||
|
{
|
||||||
|
pal_reset_at(type, 4, 0);
|
||||||
|
pal_reset_at(type, 5, 0);
|
||||||
|
pal_reset_at(type, 6, 0);
|
||||||
|
pal_reset_at(type, 7, 0);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Build text_palette.exe — uses lib/sprinter.lib in TINY memory mode.
|
||||||
|
|
||||||
|
PROJ_ROOT := $(abspath $(CURDIR)/../..)
|
||||||
|
EXAMPLE := text_palette
|
||||||
|
include $(PROJ_ROOT)/app.mk
|
||||||
@@ -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