Hi! Thanks for working on this. While testing this patch (with a little script that outputs all the xterm 256 color escapes), I noticed that it only works for 32-bit wsdisplays. You can try out a range of colour depths using the "vesa" option in the bios bootloader, for example, in qemu-system-i386. I've made a few adjustments to dev/rasops so that 8/16/24-bit wsdisplays can also use the 256-color extensions. It seems to work. I'm not sure the reproduction on 8-bit displays is 100% perfect (certainly the antialiasing could be prettier when using an antialiased font), but it's definitely usable. Updated patch and the test script are attached.
Attachment:
simple-256.sh
Description: Bourne shell script
Index: dev/wscons/wsemul_vt100_subr.c
===================================================================
RCS file: /cvsroot/src/sys/dev/wscons/wsemul_vt100_subr.c,v
retrieving revision 1.34
diff -u -p -r1.34 wsemul_vt100_subr.c
--- dev/wscons/wsemul_vt100_subr.c 3 Aug 2023 22:11:41 -0000 1.34
+++ dev/wscons/wsemul_vt100_subr.c 28 Oct 2025 12:35:24 -0000
@@ -561,6 +561,26 @@ wsemul_vt100_handle_csi(struct vt100base
flags |= WSATTR_WSCOLORS;
fgcol = ARG(vd, n) - 30;
break;
+#define EXIST_ARG2(i) ((vd->nargs - i) >= 3)
+#define ARG2_OR_DEF(i) (EXIST_ARG2(i) ? ARG(vd, i + 2) : 0)
+ case 38:
+ if (vd->nargs == n + 1)
+ break;
+ if (ARG(vd, n + 1) == 5) {
+ flags |= WSATTR_WSCOLORS;
+ if (vd->scrcapabilities &
+ WSSCREEN_256COL)
+ fgcol = ARG2_OR_DEF(n);
+ n += (EXIST_ARG2(n) ? 2 : 1);
+ break;
+ }
+ if (ARG(vd, n + 1) == 2) {
+ n = (vd->nargs - n > 5 ? n + 4 :
+ vd->nargs);
+ break;
+ }
+ n++;
+ break;
case 39:
fgcol = vd->msgattrs.default_fg;
break;
@@ -570,9 +590,39 @@ wsemul_vt100_handle_csi(struct vt100base
flags |= WSATTR_WSCOLORS;
bgcol = ARG(vd, n) - 40;
break;
+ case 48:
+ if (vd->nargs == n + 1)
+ break;
+ if (ARG(vd, n + 1) == 5) {
+ flags |= WSATTR_WSCOLORS;
+ if (vd->scrcapabilities &
+ WSSCREEN_256COL)
+ bgcol = ARG2_OR_DEF(n);
+ n += (EXIST_ARG2(n) ? 2 : 1);
+ break;
+ }
+ if (ARG(vd, n + 1) == 2) {
+ n = (vd->nargs - n > 5 ? n + 4 :
+ vd->nargs);
+ break;
+ }
+ n++;
+ break;
case 49:
bgcol = vd->msgattrs.default_bg;
break;
+ case 90: case 91: case 92: case 93:
+ case 94: case 95: case 96: case 97:
+ /* bright foreground color */
+ flags |= WSATTR_WSCOLORS;
+ fgcol = ARG(vd, n) - 82;
+ break;
+ case 100: case 101: case 102: case 103:
+ case 104: case 105: case 106: case 107:
+ /* bright background color */
+ flags |= WSATTR_WSCOLORS;
+ bgcol = ARG(vd, n) - 92;
+ break;
default:
#ifdef VT100_PRINTUNKNOWN
printf("CSI%dm unknown\n", ARG(vd, n));
Index: dev/wscons/wsdisplayvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/wscons/wsdisplayvar.h,v
retrieving revision 1.57
diff -u -p -r1.57 wsdisplayvar.h
--- dev/wscons/wsdisplayvar.h 1 Mar 2023 08:42:33 -0000 1.57
+++ dev/wscons/wsdisplayvar.h 28 Oct 2025 12:35:24 -0000
@@ -107,6 +107,7 @@ struct wsscreen_descr {
#define WSSCREEN_RESIZE 32 /* can resize */
#define WSSCREEN_FREE 64 /* free() this struct when deleting
* internal only, do not set */
+#define WSSCREEN_256COL 128
void *modecookie;
};
Index: dev/rasops/rasops.c
===================================================================
RCS file: /cvsroot/src/sys/dev/rasops/rasops.c,v
retrieving revision 1.128
diff -u -p -r1.128 rasops.c
--- dev/rasops/rasops.c 15 May 2022 16:43:39 -0000 1.128
+++ dev/rasops/rasops.c 28 Oct 2025 12:35:24 -0000
@@ -80,6 +80,8 @@ static const uint32_t rasops_rmask32[4 +
MBE(0xffffffff),
};
+uint8_t rasops_ecmap[256 * 3];
+
/* ANSI colormap (R,G,B). Upper 8 are high-intensity */
const uint8_t rasops_cmap[256 * 3] = {
0x00, 0x00, 0x00, /* black */
@@ -140,9 +142,39 @@ const uint8_t rasops_cmap[256 * 3] = {
};
/* True if color is gray */
-static const uint8_t rasops_isgray[16] = {
+static const uint8_t rasops_isgray[256] = {
1, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1,
};
#ifdef RASOPS_APPLE_PALETTE
@@ -535,22 +567,26 @@ rasops_reconfig(struct rasops_info *ri,
#endif
#if NRASOPS8 > 0
case 8:
+ ri->ri_caps |= WSSCREEN_256COL;
rasops8_init(ri);
break;
#endif
#if (NRASOPS15 + NRASOPS16) > 0
case 15:
case 16:
+ ri->ri_caps |= WSSCREEN_256COL;
rasops15_init(ri);
break;
#endif
#if NRASOPS24 > 0
case 24:
+ ri->ri_caps |= WSSCREEN_256COL;
rasops24_init(ri);
break;
#endif
#if NRASOPS32 > 0
case 32:
+ ri->ri_caps |= WSSCREEN_256COL;
rasops32_init(ri);
break;
#endif
@@ -619,8 +655,8 @@ rasops_allocattr_color(void *cookie, int
return EINVAL;
#ifdef RASOPS_CLIPPING
- fg &= 7;
- bg &= 7;
+ fg &= 0xff;
+ bg &= 0xff;
#endif
if ((flg & WSATTR_BLINK) != 0)
@@ -904,7 +940,7 @@ rasops_init_devcmap(struct rasops_info *
p = rasops_cmap;
- for (i = 0; i < 16; i++) {
+ for (i = 0; i < ((ri->ri_caps & WSSCREEN_256COL) ? 256 : 16); i++) {
if (ri->ri_rnum <= 8)
c = (uint32_t)(*p >> (8 - ri->ri_rnum)) << ri->ri_rpos;
else
@@ -923,6 +959,32 @@ rasops_init_devcmap(struct rasops_info *
c |= (uint32_t)(*p << (ri->ri_bnum - 8)) << ri->ri_bpos;
p++;
+#define EP_BLUE_RAW(x) (48 * ((x - 16) % 6))
+#define EP_GREEN_RAW(x) (48 * (((x - 16)/6) % 6))
+#define EP_RED_RAW(x) (48 * (((x - 16)/36) % 6))
+#define EP_BLUE(x) ((EP_BLUE_RAW(x) >> (8 - ri->ri_bnum)) << ri->ri_bpos)
+#define EP_GREEN(x) ((EP_GREEN_RAW(x) >> (8 - ri->ri_gnum)) << ri->ri_gpos)
+#define EP_RED(x) ((EP_RED_RAW(x) >> (8 - ri->ri_rnum)) << ri->ri_rpos)
+#define EP_COL(x) EP_RED(x) | EP_GREEN(x) | EP_BLUE(x)
+#define EP_GREY_BYTE(x) (1 + ((x - 232) * 11))
+#define GREYSCALE_BLUE(x) ((EP_GREY_BYTE(x) >> (8 - ri->ri_bnum)) << ri->ri_bpos)
+#define GREYSCALE_GREEN(x) ((EP_GREY_BYTE(x) >> (8 - ri->ri_gnum)) << ri->ri_gpos)
+#define GREYSCALE_RED(x) ((EP_GREY_BYTE(x) >> (8 - ri->ri_rnum)) << ri->ri_rpos)
+#define EP_GREY(x) GREYSCALE_RED(x) | GREYSCALE_GREEN(x) | GREYSCALE_BLUE(x)
+ if (i >= 16) {
+ rasops_ecmap[i * 3] =
+ (i < 232 ? EP_RED_RAW(i) : EP_GREY_BYTE(i));
+ rasops_ecmap[i * 3 + 1] =
+ (i < 232 ? EP_GREEN_RAW(i) : EP_GREY_BYTE(i));
+ rasops_ecmap[i * 3 + 2] =
+ (i < 232 ? EP_BLUE_RAW(i) : EP_GREY_BYTE(i));
+ c = (i < 232 ? EP_COL(i) : EP_GREY(i));
+ } else {
+ rasops_ecmap[i * 3] = rasops_cmap[i * 3];
+ rasops_ecmap[i * 3 + 1] = rasops_cmap[i * 3 + 1];
+ rasops_ecmap[i * 3 + 2] = rasops_cmap[i * 3 + 2];
+ }
+
/*
* Swap byte order if necessary. Then, fill the word for
* generic routines, which want this.
@@ -975,8 +1037,8 @@ void
rasops_unpack_attr(long attr, int *fg, int *bg, int *underline)
{
- *fg = ((uint32_t)attr >> 24) & 0xf;
- *bg = ((uint32_t)attr >> 16) & 0xf;
+ *fg = ((uint32_t)attr >> 24) & 0xff;
+ *bg = ((uint32_t)attr >> 16) & 0xff;
if (underline != NULL)
*underline = (uint32_t)attr & WSATTR_UNDERLINE;
}
Index: dev/rasops/rasops.h
===================================================================
RCS file: /cvsroot/src/sys/dev/rasops/rasops.h,v
retrieving revision 1.51
diff -u -p -r1.51 rasops.h
--- dev/rasops/rasops.h 25 Jul 2025 18:19:12 -0000 1.51
+++ dev/rasops/rasops.h 28 Oct 2025 12:35:24 -0000
@@ -145,7 +145,7 @@ struct rasops_info {
int ri_xorigin; /* where ri_bits begins (x) */
int ri_yorigin; /* where ri_bits begins (y) */
uint32_t
- ri_devcmap[16]; /* color -> framebuffer data */
+ ri_devcmap[256]; /* color -> framebuffer data */
/* The emulops you need to use, and the screen caps for wscons */
struct wsdisplay_emulops ri_ops;
@@ -190,6 +190,7 @@ void rasops_erasecols(void *, int, int,
int rasops_get_cmap(struct rasops_info *, uint8_t *, size_t);
extern const uint8_t rasops_cmap[256 * 3];
+extern uint8_t rasops_ecmap[256 * 3];
#ifdef _RASOPS_PRIVATE
/*
@@ -203,11 +204,11 @@ void rasops15_init(struct rasops_info *)
void rasops24_init(struct rasops_info *);
void rasops32_init(struct rasops_info *);
-#define ATTR_BG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 16) & 0xf])
-#define ATTR_FG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 24) & 0xf])
+#define ATTR_BG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 16) & 0xff])
+#define ATTR_FG(ri, attr) ((ri)->ri_devcmap[((uint32_t)(attr) >> 24) & 0xff])
-#define ATTR_MASK_BG __BITS(16, 19)
-#define ATTR_MASK_FG __BITS(24, 27)
+#define ATTR_MASK_BG __BITS(16, 23)
+#define ATTR_MASK_FG __BITS(24, 31)
#define DELTA(p, d, cast) ((p) = (cast)((uint8_t *)(p) + (d)))
Index: dev/rasops/rasops_putchar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/rasops/rasops_putchar.h,v
retrieving revision 1.8
diff -u -p -r1.8 rasops_putchar.h
--- dev/rasops/rasops_putchar.h 10 Aug 2019 01:24:17 -0000 1.8
+++ dev/rasops/rasops_putchar.h 28 Oct 2025 12:35:24 -0000
@@ -151,15 +151,24 @@ NAME(RASOPS_DEPTH)(void *cookie, int row
/*
* This is independent to positions/lengths of RGB in pixel.
*/
- off[0] = (((uint32_t)attr >> 16) & 0xf) * 3;
- off[1] = (((uint32_t)attr >> 24) & 0xf) * 3;
+ off[0] = (((uint32_t)attr >> 16) & 0xff) * 3;
+ off[1] = (((uint32_t)attr >> 24) & 0xff) * 3;
- r[0] = rasops_cmap[off[0]];
- r[1] = rasops_cmap[off[1]];
- g[0] = rasops_cmap[off[0] + 1];
- g[1] = rasops_cmap[off[1] + 1];
- b[0] = rasops_cmap[off[0] + 2];
- b[1] = rasops_cmap[off[1] + 2];
+ if (ri->ri_caps & WSSCREEN_256COL) {
+ r[0] = rasops_ecmap[off[0]];
+ r[1] = rasops_ecmap[off[1]];
+ g[0] = rasops_ecmap[off[0] + 1];
+ g[1] = rasops_ecmap[off[1] + 1];
+ b[0] = rasops_ecmap[off[0] + 2];
+ b[1] = rasops_ecmap[off[1] + 2];
+ } else {
+ r[0] = rasops_cmap[off[0]];
+ r[1] = rasops_cmap[off[1]];
+ g[0] = rasops_cmap[off[0] + 1];
+ g[1] = rasops_cmap[off[1] + 1];
+ b[0] = rasops_cmap[off[0] + 2];
+ b[1] = rasops_cmap[off[1] + 2];
+ }
#endif
while (height--) {