Add basic character output for VGA graphics mode console (#649)

* Test output of colors for VGA graphics modes in examples/vga.c
* [metal] Character output in VGA graphics modes is mostly working
* [metal] Mention magic key to switch video mode, at bootup
This commit is contained in:
tkchia 2022-10-05 21:46:50 +08:00 committed by GitHub
parent b75a4654cf
commit edb8fef06c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 2059 additions and 324 deletions

View file

@ -49,12 +49,12 @@ int main(int argc, char *argv[]) {
printf("res = %d?\n", res);
return -1;
}
printf("Hello World! %.19Lg\n", atan2l(x, y));
printf("\e[92;44mHello World!\e[0m %.19Lg\n", atan2l(x, y));
// read/print loop so machine doesn't reset on metal
for (;;) {
if ((res = readansi(0, buf, 16)) > 0) {
printf("got %`'.*s\r\n", res, buf);
printf("got \e[97m%`'.*s\e[0m\r\n", res, buf);
}
}
}

View file

@ -448,6 +448,16 @@ typedef struct {
#endif
#endif
#ifndef unrollloops
#if !defined(__STRICT_ANSI__) && \
((__GNUC__ + 0) * 100 + (__GNUC_MINOR__ + 0) >= 407 || \
__has_attribute(__optimize__))
#define unrollloops __attribute__((__optimize__("unroll-loops")))
#else
#define unrollloops
#endif
#endif
#ifndef returnstwice
#if !defined(__STRICT_ANSI__) && \
(__has_attribute(__returns_twice__) || \

View file

@ -62,9 +62,13 @@ _rlinit_vesa:
testb $0b00000011,0x0417 # read keyboard shift state (as
jnz .doit # given by BIOS's IRQ 1); if Shift
# key pressed, activate code below
push %si # display brief message on magic key
mov $REAL(str.inform),%si
call .puts
pop %si
mov $0x8300,%ax # wait for the magic key for a short
mov $(250000>>16),%cx # period of time...
mov $(250000&0xffff),%dx
mov $(1000000>>16),%cx # period of time...
mov $(1000000&0xffff),%dx
push %ss
pop %es
mov %sp,%bx
@ -75,12 +79,14 @@ _rlinit_vesa:
jnz .doit2
cmpb $0,%es:(%bx)
jz .wait
call .done_inform
stc
.done: pop %ax
pop %es
ret
.doit2: mov $0x8301,%ax # we got the magic key; cancel the
int $0x15 # wait timer
int $0x15 # wait timer, & erase message
call .done_inform
.doit: pop %ax # we got the magic key; do stuff
pop %es
// fall through
@ -139,6 +145,14 @@ _rlinit_vesa:
jmp 7b
.endfn .do_vesa_rlinit
// Clear the informational message on the magic key.
.done_inform:
mov $0x0a00|' ',%ax
mov $7,%bx
mov $str.inform.end-str.inform-1,%cx
int $0x10
ret
// Preliminarily choose a "default" VESA screen mode from a list of
// gathered screen modes.
//
@ -570,6 +584,12 @@ _rlinit_vesa:
.endfn .putnl
.previous
str.inform:
#define SHGLYPH "\x7f"
.ascii "\rinfo: press ",SHGLYPH,"Shift "
.asciz "to switch video mode\r"
str.inform.end:
.endobj str.inform
str.no_vesa:
.asciz "info: no VESA\r\n"
.endobj str.no_vesa

287
libc/vga/tty-graph.c Normal file
View file

@ -0,0 +1,287 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
*/
#include "libc/intrin/newbie.h"
#include "libc/macros.internal.h"
#include "libc/str/str.h"
#include "libc/vga/vga.internal.h"
/**
* @fileoverview Routines to support output in graphical video modes for
* bare metal VGA.
*
* @see libc/vga/tty.c
*/
static void TtyDirty(struct Tty *tty, size_t gy1, size_t gx1,
size_t gy2, size_t gx2) {
if (tty->updy1 > gy1) tty->updy1 = gy1;
if (tty->updx1 > gx1) tty->updx1 = gx1;
if (tty->updy2 < gy2) tty->updy2 = gy2;
if (tty->updx2 < gx2) tty->updx2 = gx2;
}
static void TtyResetDirty(struct Tty *tty) {
tty->updy1 = tty->updx1 = tty->updy2 = tty->updx2 = 0;
}
unrollloops void _TtyBgr565Update(struct Tty *tty) {
size_t gy1 = tty->updy1, gy2 = tty->updy2,
gx1 = tty->updx1, gx2 = tty->updx2,
xsfb = tty->xsfb, xs = tty->xs;
if (gy1 < gy2 && gx1 < gx2) {
size_t yleft = gy2 - gy1, xleft;
char *cplotter = tty->fb + gy1 * xsfb + gx1 * sizeof(TtyBgr565Color);
const char *creader = tty->canvas + gy1 * xs
+ gx1 * sizeof(TtyCanvasColor);
TtyResetDirty(tty);
while (yleft-- != 0) {
TtyBgr565Color *plotter = (TtyBgr565Color *)cplotter;
const TtyCanvasColor *reader = (const TtyCanvasColor *)creader;
TtyCanvasColor c;
xleft = gx2 - gx1;
while (xleft-- != 0) {
uint16_t w;
c.w = reader->w;
++reader;
w = htole16(c.bgr.b >> 3 | c.bgr.g >> 2 << 5 | c.bgr.r >> 3 << 11);
*plotter++ = (TtyBgr565Color){w};
}
cplotter += xsfb;
creader += xs;
}
}
}
unrollloops void _TtyBgr555Update(struct Tty *tty) {
size_t gy1 = tty->updy1, gy2 = tty->updy2,
gx1 = tty->updx1, gx2 = tty->updx2,
xsfb = tty->xsfb, xs = tty->xs;
if (gy1 < gy2 && gx1 < gx2) {
size_t yleft = gy2 - gy1, xleft;
char *cplotter = tty->fb + gy1 * xsfb + gx1 * sizeof(TtyBgr555Color);
const char *creader = tty->canvas + gy1 * xs
+ gx1 * sizeof(TtyCanvasColor);
TtyResetDirty(tty);
while (yleft-- != 0) {
TtyBgr555Color *plotter = (TtyBgr555Color *)cplotter;
const TtyCanvasColor *reader = (const TtyCanvasColor *)creader;
TtyCanvasColor c;
xleft = gx2 - gx1;
while (xleft-- != 0) {
uint16_t w;
c.w = reader->w;
++reader;
w = htole16(c.bgr.b >> 3 | c.bgr.g >> 3 << 5 | c.bgr.r >> 3 << 10);
*plotter++ = (TtyBgr555Color){w};
}
cplotter += xsfb;
creader += xs;
}
}
}
unrollloops void _TtyBgrxUpdate(struct Tty *tty) {
size_t gy1 = tty->updy1, gy2 = tty->updy2,
gx1 = tty->updx1, gx2 = tty->updx2,
xsfb = tty->xsfb, xs = tty->xs;
if (gy1 < gy2 && gx1 < gx2) {
size_t yleft = gy2 - gy1, xleft;
char *cplotter = tty->fb + gy1 * xsfb + gx1 * sizeof(TtyBgrxColor);
const char *creader = tty->canvas + gy1 * xs
+ gx1 * sizeof(TtyCanvasColor);
TtyResetDirty(tty);
while (yleft-- != 0) {
TtyBgrxColor *plotter = (TtyBgrxColor *)cplotter;
const TtyCanvasColor *reader = (const TtyCanvasColor *)creader;
xleft = gx2 - gx1;
while (xleft-- != 0) {
TtyCanvasColor c = *reader++;
c.bgr.x = 0xff;
*plotter++ = c;
}
cplotter += xsfb;
creader += xs;
}
}
}
unrollloops void _TtyRgbxUpdate(struct Tty *tty) {
size_t gy1 = tty->updy1, gy2 = tty->updy2,
gx1 = tty->updx1, gx2 = tty->updx2,
xsfb = tty->xsfb, xs = tty->xs;
if (gy1 < gy2 && gx1 < gx2) {
size_t yleft = gy2 - gy1, xleft;
char *cplotter = tty->fb + gy1 * xsfb + gx1 * sizeof(TtyRgbxColor);
const char *creader = tty->canvas + gy1 * xs
+ gx1 * sizeof(TtyCanvasColor);
TtyResetDirty(tty);
while (yleft-- != 0) {
TtyRgbxColor *plotter = (TtyRgbxColor *)cplotter;
const TtyCanvasColor *reader = (const TtyCanvasColor *)creader;
TtyCanvasColor ic;
TtyRgbxColor oc;
xleft = gx2 - gx1;
while (xleft-- != 0) {
ic.w = reader->w;
++reader;
oc = (TtyRgbxColor){.rgb.r = ic.bgr.r, .rgb.g = ic.bgr.g,
.rgb.b = ic.bgr.b, .rgb.x = 0xff};
plotter->w = oc.w;
++plotter;
}
cplotter += xsfb;
creader += xs;
}
}
}
static void TtyGraphDrawBitmap(struct Tty *tty, size_t gy, size_t gx,
TtyCanvasColor fg, TtyCanvasColor bg,
const uint8_t *bitmap,
size_t bm_ht, size_t bm_wid) {
size_t xs = tty->xs;
char *cplotter = tty->canvas + gy * xs
+ gx * sizeof(TtyCanvasColor);
size_t yleft = bm_ht, xleft;
while (yleft-- != 0) {
TtyCanvasColor *plotter = (TtyCanvasColor *)cplotter;
xleft = bm_wid;
while (xleft >= 8) {
uint8_t bits = *bitmap++;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
xleft -= 8;
}
if (xleft) {
uint8_t bits = *bitmap++;
switch (xleft) {
default:
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
/* fall through */
case 6:
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
/* fall through */
case 5:
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
/* fall through */
case 4:
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
/* fall through */
case 3:
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
/* fall through */
case 2:
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
/* fall through */
case 1:
*plotter++ = __builtin_add_overflow(bits, bits, &bits) ? fg : bg;
}
}
cplotter += xs;
}
TtyDirty(tty, gy, gx, gy + bm_ht, gx + bm_wid);
}
static unrollloops void TtyGraphFillRect(struct Tty *tty,
size_t gy, size_t gx,
size_t fill_ht, size_t fill_wid,
TtyCanvasColor bg) {
size_t xs = tty->xs;
char *cplotter = tty->canvas + gy * xs + gx * sizeof(TtyCanvasColor);
size_t yleft = fill_ht, xleft;
while (yleft-- != 0) {
TtyCanvasColor *plotter = (TtyCanvasColor *)cplotter;
size_t i;
for (i = 0; i < fill_wid; ++i)
*plotter++ = bg;
cplotter += xs;
}
TtyDirty(tty, gy, gx, gy + fill_ht, gx + fill_wid);
}
static void TtyGraphMoveRect(struct Tty *tty, size_t dgy, size_t dgx,
size_t sgy, size_t sgx, size_t ht, size_t wid) {
size_t xs = tty->xs, xm = wid * sizeof(TtyCanvasColor);
char *canvas = tty->canvas;
TtyDirty(tty, dgy, dgx, dgy + ht, dgx + wid);
if (dgy < sgy) {
while (ht-- != 0) {
memmove(canvas + dgy * xs + dgx * sizeof(TtyCanvasColor),
canvas + sgy * xs + sgx * sizeof(TtyCanvasColor), xm);
++dgy;
++sgy;
}
} else if (dgy > sgy) {
while (ht-- != 0)
memmove(canvas + (dgy + ht) * xs + dgx * sizeof(TtyCanvasColor),
canvas + (sgy + ht) * xs + sgx * sizeof(TtyCanvasColor), xm);
}
}
void _TtyGraphDrawChar(struct Tty *tty, size_t y, size_t x, wchar_t wc) {
/* TODO: allow configuring a different font later. */
const uint8_t *glyph;
const size_t glyph_ht = ARRAYLEN(_vga_font_default_direct[0]);
TtyCanvasColor fg = tty->fg, bg = tty->bg;
if ((tty->pr & kTtyFlip) != 0)
fg = bg, bg = tty->fg;
if (wc < L' ' || wc >= L' ' + ARRAYLEN(_vga_font_default_direct))
glyph = _vga_font_default_direct[0];
else
glyph = _vga_font_default_direct[wc - L' '];
if (glyph_ht >= VGA_ASSUME_CHAR_HEIGHT_PX)
TtyGraphDrawBitmap(tty, y * tty->yc, x * tty->xc, fg, bg,
glyph, VGA_ASSUME_CHAR_HEIGHT_PX, 8);
else {
/*
* Glyph is not tall enough. Draw it out, then pad the bottom of the
* character cell with the background color.
*/
TtyGraphDrawBitmap(tty, y * tty->yc, x * tty->xc, fg, bg,
glyph, glyph_ht, 8);
TtyGraphFillRect(tty, y * tty->yc + glyph_ht, x * tty->xc,
VGA_ASSUME_CHAR_HEIGHT_PX - glyph_ht, 8, bg);
}
}
void _TtyGraphEraseLineCells(struct Tty *tty, size_t y, size_t x, size_t n) {
size_t yc = tty->yc, xc = tty->xc;
TtyGraphFillRect(tty, y * yc, x * xc, yc, n * xc, tty->bg);
}
void _TtyGraphMoveLineCells(struct Tty *tty, size_t dsty, size_t dstx,
size_t srcy, size_t srcx, size_t n) {
size_t yc = tty->yc, xc = tty->xc;
TtyGraphMoveRect(tty, dsty * yc, dstx * xc, srcy * yc, srcx * xc,
yc, n * xc);
}

View file

@ -20,10 +20,13 @@
#include "libc/fmt/itoa.h"
#include "libc/intrin/bits.h"
#include "libc/intrin/safemacros.internal.h"
#include "libc/runtime/directmap.internal.h"
#include "libc/runtime/pc.internal.h"
#include "libc/str/str.h"
#include "libc/str/thompike.h"
#include "libc/str/unicode.h"
#include "libc/sysv/consts/map.h"
#include "libc/sysv/consts/prot.h"
#include "libc/sysv/errfuns.h"
#include "libc/vga/vga.internal.h"
@ -37,45 +40,58 @@
* @see tool/build/lib/pty.c
*/
#define DEFAULT_FG 7
#define DEFAULT_BG 0
#define DEFAULT_FG ((TtyCanvasColor) \
{.bgr.r = 0xaa, .bgr.g = 0xaa, .bgr.b = 0xaa, \
.bgr.x = 0xff})
#define DEFAULT_BG ((TtyCanvasColor) \
{.bgr.r = 0, .bgr.g = 0, .bgr.b = 0, .bgr.x = 0xff})
#define CRTPORT 0x3d4
static void SetYn(struct Tty *tty, unsigned short yn) {
#ifndef VGA_TTY_HEIGHT
tty->yn = yn;
#endif
}
static void SetXn(struct Tty *tty, unsigned short xn) {
#ifndef VGA_TTY_WIDTH
tty->xn = xn;
#endif
}
static wchar_t *SetWcs(struct Tty *tty, wchar_t *wcs) {
static void SetYp(struct Tty *tty, unsigned short yp) {
tty->yp = yp;
}
static void SetXp(struct Tty *tty, unsigned short xp) {
tty->xp = xp;
}
static void SetXsFb(struct Tty *tty, unsigned short xsfb) {
tty->xsfb = xsfb;
}
static bool SetWcs(struct Tty *tty, bool alloc_wcs) {
#ifdef VGA_USE_WCS
tty->wcs = wcs;
return wcs;
struct DirectMap dm;
if (!alloc_wcs)
return false;
dm = sys_mmap_metal(NULL, Yn(tty) * Xn(tty) * sizeof(wchar_t),
PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
-1, 0);
if (dm.addr == (void *)-1) {
tty->wcs = NULL;
return false;
}
tty->wcs = dm.addr;
return true;
#else
return NULL;
return false;
#endif
}
static size_t Yn(struct Tty *tty) {
#ifdef VGA_TTY_HEIGHT
return VGA_TTY_HEIGHT;
#else
return tty->yn;
#endif
}
static size_t Xn(struct Tty *tty) {
#ifdef VGA_TTY_WIDTH
return VGA_TTY_WIDTH;
#else
return tty->xn;
#endif
}
static wchar_t *Wcs(struct Tty *tty) {
@ -86,23 +102,82 @@ static wchar_t *Wcs(struct Tty *tty) {
#endif
}
void _StartTty(struct Tty *tty, unsigned short yn, unsigned short xn,
static void TtyTextDrawChar(struct Tty *, size_t, size_t, wchar_t);
static void TtyTextEraseLineCells(struct Tty *, size_t, size_t, size_t);
static void TtyTextMoveLineCells(struct Tty *, size_t, size_t, size_t, size_t,
size_t);
static void TtySetType(struct Tty *tty, unsigned char type) {
tty->type = type;
switch (type) {
case PC_VIDEO_BGR565:
tty->update = _TtyBgr565Update;
goto graphics_mode;
case PC_VIDEO_BGR555:
tty->update = _TtyBgr555Update;
goto graphics_mode;
case PC_VIDEO_BGRX8888:
tty->update = _TtyBgrxUpdate;
goto graphics_mode;
case PC_VIDEO_RGBX8888:
tty->update = _TtyRgbxUpdate;
graphics_mode:
tty->drawchar = _TtyGraphDrawChar;
tty->eraselinecells = _TtyGraphEraseLineCells;
tty->movelinecells = _TtyGraphMoveLineCells;
break;
default:
tty->drawchar = TtyTextDrawChar;
tty->eraselinecells = TtyTextEraseLineCells;
tty->movelinecells = TtyTextMoveLineCells;
}
}
void _StartTty(struct Tty *tty, unsigned char type,
unsigned short yp, unsigned short xp, unsigned short xsfb,
unsigned short starty, unsigned short startx,
unsigned char chr_ht, void *vccs, wchar_t *wcs) {
struct VgaTextCharCell *ccs = vccs;
unsigned char yc, unsigned char xc, void *fb, bool alloc_wcs) {
unsigned short yn, xn, xs = xp * sizeof(TtyCanvasColor);
struct DirectMap dm;
memset(tty, 0, sizeof(struct Tty));
SetYp(tty, yp);
SetXp(tty, xp);
SetXsFb(tty, xsfb);
tty->xs = xs;
if (type == PC_VIDEO_TEXT) {
yn = yp;
xn = xp;
tty->canvas = fb;
} else {
yn = yp / yc;
xn = xp / xc;
dm = sys_mmap_metal(NULL, (size_t)yp * xs, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (dm.addr == (void *)-1) {
/* We are a bit low on memory. Try to go on anyway. */
tty->canvas = fb;
starty = startx = 0;
} else
tty->canvas = dm.addr;
}
SetYn(tty, yn);
SetXn(tty, xn);
tty->ccs = ccs;
tty->yc = yc;
tty->xc = xc;
tty->fb = fb;
if (starty >= yn) starty = yn - 1;
if (startx >= xn) startx = xn - 1;
if (chr_ht > 32) chr_ht = 32;
tty->y = starty;
tty->x = startx;
tty->chr_ht = chr_ht;
if (SetWcs(tty, wcs)) {
TtySetType(tty, type);
if (SetWcs(tty, alloc_wcs)) {
wchar_t *wcs = Wcs(tty);
size_t n = (size_t)yn * xn, i;
for (i = 0; i < n; ++i) wcs[i] = bing(ccs[i].ch, 0);
if (type == PC_VIDEO_TEXT) {
struct VgaTextCharCell *ccs = fb;
for (i = 0; i < n; ++i) wcs[i] = bing(ccs[i].ch, 0);
} else
wmemset(wcs, L' ', n);
}
_TtyResetOutputMode(tty);
}
@ -213,6 +288,25 @@ static void TtySetCodepage(struct Tty *tty, char id) {
}
}
/**
* Map the given direct color value to one of the 16 basic foreground colors
* or one of the 16 background colors.
*
* @see drivers/tty/vt/vt.c in Linux 5.9.14 source code
*/
static uint8_t TtyGetTextColor(TtyCanvasColor color) {
uint8_t r = color.bgr.r, g = color.bgr.g, b = color.bgr.b;
uint8_t hue = 0, max = MAX(MAX(r, g), b);
if (r > max / 2) hue |= 4;
if (g > max / 2) hue |= 2;
if (b > max / 2) hue |= 1;
if (hue == 7 && max <= 0x55)
hue = 8;
else if (max > 0xaa)
hue |= 8;
return hue;
}
/**
* Map the currently active foreground & background colors & terminal
* configuration to a VGA text character attribute byte.
@ -220,12 +314,13 @@ static void TtySetCodepage(struct Tty *tty, char id) {
* @see VGA_USE_BLINK macro (libc/vga/vga.internal.h)
* @see drivers/tty/vt/vt.c in Linux 5.9.14 source code
*/
static uint8_t TtyGetVgaAttr(struct Tty *tty) {
static uint8_t TtyGetTextAttr(struct Tty *tty) {
uint8_t fg = TtyGetTextColor(tty->fg), bg = TtyGetTextColor(tty->bg);
uint8_t attr;
if ((tty->pr & kTtyFlip) == 0)
attr = tty->fg | tty->bg << 4;
attr = fg | bg << 4;
else
attr = tty->bg | tty->fg << 4;
attr = bg | fg << 4;
#ifdef VGA_USE_BLINK
/*
* If blinking is enabled, we can only have the 8 dark background color
@ -240,41 +335,73 @@ static uint8_t TtyGetVgaAttr(struct Tty *tty) {
return attr;
}
static void TtyErase(struct Tty *tty, size_t dst, size_t n) {
uint8_t attr = TtyGetVgaAttr(tty);
size_t i;
static void TtyTextDrawChar(struct Tty *tty, size_t y, size_t x, wchar_t wc) {
uint8_t attr = TtyGetTextAttr(tty);
struct VgaTextCharCell *ccs = (struct VgaTextCharCell *)tty->canvas;
size_t i = tty->y * Xn(tty) + tty->x;
int c = unbing(wc);
if (c == -1) c = 0xFE;
ccs[i] = (struct VgaTextCharCell){c, attr};
}
static void TtyTextEraseLineCells(struct Tty *tty, size_t dsty, size_t dstx,
size_t n) {
uint8_t attr = TtyGetTextAttr(tty);
struct VgaTextCharCell *ccs = (struct VgaTextCharCell *)tty->canvas;
size_t dst = dsty * Xn(tty) + dstx, i;
for (i = 0; i < n; ++i)
tty->ccs[dst + i] = (struct VgaTextCharCell){' ', attr};
if (Wcs(tty)) wmemset(Wcs(tty) + dst, L' ', n);
ccs[dst + i] = (struct VgaTextCharCell){' ', attr};
}
void _TtyEraseLineCells(struct Tty *tty, size_t dsty, size_t dstx, size_t n) {
size_t xn = Xn(tty);
TtyErase(tty, dsty * xn + dstx, n);
tty->eraselinecells(tty, dsty, dstx, n);
if (Wcs(tty)) {
size_t i = dsty * Xn(tty) + dstx;
wmemset(Wcs(tty) + i, L' ', n);
}
}
void _TtyEraseLines(struct Tty *tty, size_t dsty, size_t n) {
size_t xn = Xn(tty);
TtyErase(tty, dsty * xn, n * xn);
size_t xn = Xn(tty), y;
y = dsty;
while (n-- != 0) {
_TtyEraseLineCells(tty, y, 0, xn);
++y;
}
}
static void TtyMemmove(struct Tty *tty, size_t dst, size_t src, size_t n) {
memmove(tty->ccs + dst, tty->ccs + src, n * sizeof(struct VgaTextCharCell));
if (Wcs(tty)) wmemmove(Wcs(tty) + dst, Wcs(tty) + src, n);
static void TtyTextMoveLineCells(struct Tty *tty, size_t dsty, size_t dstx,
size_t srcy, size_t srcx, size_t n) {
size_t xn = Xn(tty);
size_t dst = dsty * xn + dstx, src = srcy * xn + srcx;
struct VgaTextCharCell *ccs = (struct VgaTextCharCell *)tty->canvas;
memmove(ccs + dst, ccs + src, n * sizeof(struct VgaTextCharCell));
}
void _TtyMoveLineCells(struct Tty *tty, size_t dsty, size_t dstx,
size_t srcy, size_t srcx, size_t n) {
size_t xn = Xn(tty);
TtyMemmove(tty, dsty * xn + dstx, srcy * xn + srcx, n);
size_t dst = dsty * xn + dstx, src = srcy * xn + srcx;
tty->movelinecells(tty, dsty, dstx, srcy, srcx, n);
if (Wcs(tty)) wmemmove(Wcs(tty) + dst, Wcs(tty) + src, n);
}
void _TtyMoveLines(struct Tty *tty, size_t dsty, size_t srcy, size_t n) {
size_t xn = Xn(tty);
TtyMemmove(tty, dsty * xn, srcy * xn, n * xn);
if (dsty < srcy) {
while (n-- != 0) {
_TtyMoveLineCells(tty, dsty, 0, srcy, 0, xn);
++dsty;
++srcy;
}
} else if (dsty > srcy) {
while (n-- != 0)
_TtyMoveLineCells(tty, dsty + n, 0, srcy + n, 0, xn);
}
}
void _TtyResetOutputMode(struct Tty *tty) {
tty->updy1 = tty->updx1 = tty->updy2 = tty->updx2 = 0;
tty->pr = 0;
tty->fg = DEFAULT_FG;
tty->bg = DEFAULT_BG;
@ -354,18 +481,12 @@ static void TtyAdvance(struct Tty *tty) {
}
static void TtyWriteGlyph(struct Tty *tty, wint_t wc, int w) {
uint8_t attr = TtyGetVgaAttr(tty);
size_t i;
int c;
if (w < 1) wc = L' ', w = 1;
if ((tty->conf & kTtyRedzone) || tty->x + w > Xn(tty)) {
TtyAdvance(tty);
}
i = tty->y * Xn(tty) + tty->x;
c = unbing(wc);
if (c == -1) c = 0xFE;
tty->ccs[i] = (struct VgaTextCharCell){c, attr};
if (Wcs(tty)) Wcs(tty)[i] = wc;
tty->drawchar(tty, tty->y, tty->x, wc);
if (Wcs(tty)) Wcs(tty)[tty->y * Xn(tty) + tty->x] = wc;
if ((tty->x += w) >= Xn(tty)) {
tty->x = Xn(tty) - 1;
tty->conf |= kTtyRedzone;
@ -373,14 +494,13 @@ static void TtyWriteGlyph(struct Tty *tty, wint_t wc, int w) {
}
static void TtyWriteTab(struct Tty *tty) {
uint8_t attr = TtyGetVgaAttr(tty);
unsigned x, x2;
if (tty->conf & kTtyRedzone) {
TtyAdvance(tty);
}
x2 = MIN(Xn(tty), ROUNDUP(tty->x + 1, 8));
for (x = tty->x; x < x2; ++x) {
tty->ccs[tty->y * Xn(tty) + x] = (struct VgaTextCharCell){' ', attr};
tty->eraselinecells(tty, tty->y, x, 1);
}
if (Wcs(tty)) {
for (x = tty->x; x < x2; ++x) {
@ -637,61 +757,13 @@ static void TtyCsiN(struct Tty *tty) {
}
}
/**
* Map the given ECMA-48 / VT100 SGR color code to a color code for a VGA
* character attribute. More precisely, map
*
* red
* green
* blue
* intensity
*
* 3210
*
* to
*
* blue
* green
* red
* intensity
*
* 3210
*/
static uint8_t TtyMapEcma48Color(uint8_t color)
{
switch (color & 0b101) {
case 0b100:
case 0b001:
color ^= 0b101;
}
return color;
/** Create a direct color pixel value. */
static TtyCanvasColor TtyMapTrueColor(uint8_t r, uint8_t g, uint8_t b) {
return (TtyCanvasColor){.bgr.r = r, .bgr.g = g, .bgr.b = b, .bgr.x = 0xff};
}
/**
* Map the given (R, G, B) triplet to one of the 16 basic foreground colors
* or one of the 16 background colors.
*
* @see drivers/tty/vt/vt.c in Linux 5.9.14 source code
*/
static uint8_t TtyMapTrueColor(uint8_t r, uint8_t g, uint8_t b) {
uint8_t hue = 0, max = MAX(MAX(r, g), b);
if (r > max / 2) hue |= 4;
if (g > max / 2) hue |= 2;
if (b > max / 2) hue |= 1;
if (hue == 7 && max <= 0x55)
hue = 8;
else if (max > 0xaa)
hue |= 8;
return hue;
}
/**
* Map the given 256-color code one of the 16 basic foreground colors or one
* of the 8 background colors.
*
* @see drivers/tty/vt/vt.c in Linux 5.9.14 source code
*/
static uint8_t TtyMapXtermColor(uint8_t color) {
/** Map the given 256-color code to a direct color. */
static TtyCanvasColor TtyMapXtermColor(uint8_t color) {
uint8_t r, g, b;
if (color < 8) {
r = (color & 1) ? 0xaa : 0;
@ -713,6 +785,12 @@ static uint8_t TtyMapXtermColor(uint8_t color) {
return TtyMapTrueColor(r, g, b);
}
/** Map the given ECMA-48 / VT100 SGR color code to a direct color. */
static TtyCanvasColor TtyMapEcma48Color(uint8_t color)
{
return TtyMapXtermColor(color % 16);
}
static void TtySelectGraphicsRendition(struct Tty *tty) {
char *p, c;
unsigned x;
@ -983,10 +1061,10 @@ static void TtyCsi(struct Tty *tty) {
}
static void TtyScreenAlignmentDisplay(struct Tty *tty) {
uint8_t attr = TtyGetVgaAttr(tty);
size_t n = Yn(tty) * Xn(tty), i;
for (i = 0; i < n; ++i) tty->ccs[i] = (struct VgaTextCharCell){'E', attr};
if (Wcs(tty)) wmemset(Wcs(tty), L'E', n);
size_t yn = Yn(tty), xn = Xn(tty), y, x;
for (y = 0; y < yn; ++y)
for (x = 0; x < xn; ++x) tty->drawchar(tty, y, x, 'E');
if (Wcs(tty)) wmemset(Wcs(tty), L'E', yn * xn);
}
static void TtyEscHash(struct Tty *tty) {
@ -1083,18 +1161,21 @@ static void TtyEscAppend(struct Tty *tty, char c) {
tty->esc.s[tty->esc.i - 0] = 0;
}
static void TtyUpdateHwCursor(struct Tty *tty) {
unsigned char start = tty->chr_ht - 2, end = tty->chr_ht - 1;
unsigned short pos = tty->y * Xn(tty) + tty->x;
if ((tty->conf & kTtyNocursor)) start |= 1 << 5;
outb(CRTPORT, 0x0A);
outb(CRTPORT + 1, start);
outb(CRTPORT, 0x0B);
outb(CRTPORT + 1, end);
outb(CRTPORT, 0x0E);
outb(CRTPORT + 1, (unsigned char)(pos >> 8));
outb(CRTPORT, 0x0F);
outb(CRTPORT + 1, (unsigned char)pos);
static void TtyUpdate(struct Tty *tty) {
if (tty->type == PC_VIDEO_TEXT) {
unsigned char start = tty->yc - 2, end = tty->yc - 1;
unsigned short pos = tty->y * Xn(tty) + tty->x;
if ((tty->conf & kTtyNocursor)) start |= 1 << 5;
outb(CRTPORT, 0x0A);
outb(CRTPORT + 1, start);
outb(CRTPORT, 0x0B);
outb(CRTPORT + 1, end);
outb(CRTPORT, 0x0E);
outb(CRTPORT + 1, (unsigned char)(pos >> 8));
outb(CRTPORT, 0x0F);
outb(CRTPORT + 1, (unsigned char)pos);
} else
tty->update(tty);
}
ssize_t _TtyWrite(struct Tty *tty, const void *data, size_t n) {
@ -1184,7 +1265,7 @@ ssize_t _TtyWrite(struct Tty *tty, const void *data, size_t n) {
unreachable;
}
}
TtyUpdateHwCursor(tty);
TtyUpdate(tty);
return n;
}

1441
libc/vga/vga-font-default.c Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
#ifndef COSMOPOLITAN_LIBC_VGA_VGA_INTERNAL_H_
#define COSMOPOLITAN_LIBC_VGA_VGA_INTERNAL_H_
#include "libc/runtime/mman.internal.h"
/** Preferred width of the video screen, in character units. */
#define VGA_PREFER_TTY_HEIGHT 30
@ -111,6 +112,31 @@ struct VgaTextCharCell {
uint8_t ch, attr;
};
typedef union {
uint32_t w;
struct {
uint8_t b, g, r, x;
} bgr;
} TtyBgrxColor;
typedef union {
uint32_t w;
struct {
uint8_t r, g, b, x;
} rgb;
} TtyRgbxColor;
typedef union {
uint16_t w;
} TtyBgr565Color;
typedef union {
uint16_t w;
} TtyBgr555Color;
typedef TtyBgrxColor TtyCanvasColor;
struct Tty;
struct Tty {
/**
* Cursor position. (y, x) = (0, 0) means the cursor is on the top left
@ -123,7 +149,12 @@ struct Tty {
unsigned short yp, xp;
/**
* Number of bytes (NOTE) occupied by each row of pixels, including any
* invisible "pixels".
* invisible "pixels", in the video frame buffer.
*/
unsigned short xsfb;
/**
* Number of bytes (NOTE) occupied by each row of pixels, including any
* invisible "pixels", in the canvas.
*/
unsigned short xs;
/** Type of video buffer (from mman::pc_video_type). */
@ -131,10 +162,35 @@ struct Tty {
uint32_t u8;
uint32_t n8;
uint32_t pr;
uint8_t fg, bg, chr_ht;
TtyCanvasColor fg, bg;
/**
* Height of each character in pixels. In text modes, this is used in
* setting the shape of the hardware cursor.
*/
uint8_t yc;
/**
* Width of each character in pixels. This is mostly useful only in
* graphics modes.
*/
uint8_t xc;
uint32_t conf;
unsigned short savey, savex;
struct VgaTextCharCell *ccs;
/** Actual video frame buffer as provided by the video card. */
char *fb;
/**
* Canvas to draw into. In text modes, this is simply the frame buffer
* itself. In graphics modes, this should be separate from the frame
* buffer, & possibly allocated from main memory; we must arrange to
* update the frame buffer from the canvas every now & then. In general
* the canvas's pixel format given by _TtyCanvasType() may be
* different from the frame buffer's.
*/
char *canvas;
/**
* Which portions of the canvas have been updated & should later be drawn
* to screen with tty->update().
*/
unsigned short updy1, updx1, updy2, updx2;
#ifdef VGA_USE_WCS
wchar_t *wcs;
#endif
@ -153,11 +209,28 @@ struct Tty {
size_t i;
char p[256];
} input;
void (*update)(struct Tty *);
void (*drawchar)(struct Tty *, size_t, size_t, wchar_t);
void (*eraselinecells)(struct Tty *, size_t, size_t, size_t);
void (*movelinecells)(struct Tty *, size_t, size_t, size_t, size_t, size_t);
};
void _StartTty(struct Tty *, unsigned short, unsigned short,
unsigned short, unsigned short, unsigned char,
void *, wchar_t *);
forceinline unsigned char _TtyCanvasType(struct Tty *tty) {
return tty->type == PC_VIDEO_TEXT ? PC_VIDEO_TEXT : PC_VIDEO_BGRX8888;
}
void _TtyBgrxUpdate(struct Tty *);
void _TtyRgbxUpdate(struct Tty *);
void _TtyBgr565Update(struct Tty *);
void _TtyBgr555Update(struct Tty *);
void _TtyGraphDrawChar(struct Tty *, size_t, size_t, wchar_t);
void _TtyGraphEraseLineCells(struct Tty *, size_t, size_t, size_t);
void _TtyGraphMoveLineCells(struct Tty *, size_t, size_t, size_t, size_t,
size_t);
void _StartTty(struct Tty *, unsigned char, unsigned short, unsigned short,
unsigned short, unsigned short, unsigned short,
unsigned char, unsigned char, void *, bool);
ssize_t _TtyRead(struct Tty *, void *, size_t);
ssize_t _TtyWrite(struct Tty *, const void *, size_t);
ssize_t _TtyWriteInput(struct Tty *, const void *, size_t);
@ -170,6 +243,7 @@ void _TtyEraseLines(struct Tty *, size_t, size_t);
void _TtySetY(struct Tty *, unsigned short);
void _TtySetX(struct Tty *, unsigned short);
extern const uint8_t _vga_font_default_direct[95][13];
extern struct Tty _vga_tty;
ssize_t sys_readv_vga(struct Fd *, const struct iovec *, int);

View file

@ -52,185 +52,6 @@ ssize_t sys_writev_vga(struct Fd *fd, const struct iovec *iov, int iovlen) {
return wrote;
}
static void _vga_init_test(void *vid_buf, unsigned char vid_type,
size_t stride) {
switch (vid_type) {
case PC_VIDEO_TEXT:
break;
case PC_VIDEO_BGR565:
{
char *row_buf = (char *)vid_buf + stride * 100;
uint16_t *row_pix = (uint16_t *)row_buf;
unsigned i;
row_pix[0] = 0x0000;
row_pix[1] = 0x0000;
row_pix[2] = 0x07fc;
row_pix[3] = 0x07fc;
row_pix[4] = 0x07fc;
row_pix[5] = 0x0000;
row_pix[6] = 0x0000;
row_pix[7] = 0x0000;
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0x0000;
row_pix[1] = 0x07fc;
row_pix[2] = 0x07fc;
row_pix[3] = 0x07fc;
row_pix[4] = 0x07fc;
row_pix[5] = 0x07fc;
row_pix[6] = 0x0000;
row_pix[7] = 0x0000;
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0x07fc;
row_pix[1] = 0x07fc;
row_pix[2] = 0x0000;
row_pix[3] = 0x0000;
row_pix[4] = 0x0000;
row_pix[5] = 0x07fc;
row_pix[6] = 0x07fc;
row_pix[7] = 0x0000;
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0x07fc;
row_pix[1] = 0x07fc;
row_pix[2] = 0x07fc;
row_pix[3] = 0x07fc;
row_pix[4] = 0x07fc;
row_pix[5] = 0x07fc;
row_pix[6] = 0x07fc;
row_pix[7] = 0x0000;
for (i = 0; i < 4; ++i) {
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0xf81f;
row_pix[1] = 0xf81f;
row_pix[2] = 0x0000;
row_pix[3] = 0x0000;
row_pix[4] = 0x0000;
row_pix[5] = 0xf81f;
row_pix[6] = 0xf81f;
row_pix[7] = 0x0000;
}
}
break;
case PC_VIDEO_BGR555:
{
char *row_buf = (char *)vid_buf + stride * 100;
uint16_t *row_pix = (uint16_t *)row_buf;
unsigned i;
row_pix[0] = 0x8000;
row_pix[1] = 0x8000;
row_pix[2] = 0x83fc;
row_pix[3] = 0x83fc;
row_pix[4] = 0x83fc;
row_pix[5] = 0x8000;
row_pix[6] = 0x8000;
row_pix[7] = 0x8000;
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0x8000;
row_pix[1] = 0x83fc;
row_pix[2] = 0x83fc;
row_pix[3] = 0x83fc;
row_pix[4] = 0x83fc;
row_pix[5] = 0x83fc;
row_pix[6] = 0x8000;
row_pix[7] = 0x8000;
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0x83fc;
row_pix[1] = 0x83fc;
row_pix[2] = 0x8000;
row_pix[3] = 0x8000;
row_pix[4] = 0x8000;
row_pix[5] = 0x83fc;
row_pix[6] = 0x83fc;
row_pix[7] = 0x8000;
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0x83fc;
row_pix[1] = 0x83fc;
row_pix[2] = 0x83fc;
row_pix[3] = 0x83fc;
row_pix[4] = 0x83fc;
row_pix[5] = 0x83fc;
row_pix[6] = 0x83fc;
row_pix[7] = 0x8000;
for (i = 0; i < 4; ++i) {
row_buf += stride;
row_pix = (uint16_t *)row_buf;
row_pix[0] = 0xfc1f;
row_pix[1] = 0xfc1f;
row_pix[2] = 0x8000;
row_pix[3] = 0x8000;
row_pix[4] = 0x8000;
row_pix[5] = 0xfc1f;
row_pix[6] = 0xfc1f;
row_pix[7] = 0x8000;
}
}
break;
default:
{
char *row_buf = (char *)vid_buf + stride * 100;
uint32_t *row_pix = (uint32_t *)row_buf;
unsigned i;
row_pix[0] = 0xff000000;
row_pix[1] = 0xff000000;
row_pix[2] = 0xff00ffe0;
row_pix[3] = 0xff00ffe0;
row_pix[4] = 0xff00ffe0;
row_pix[5] = 0xff000000;
row_pix[6] = 0xff000000;
row_pix[7] = 0xff000000;
row_buf += stride;
row_pix = (uint32_t *)row_buf;
row_pix[0] = 0xff000000;
row_pix[1] = 0xff00ffe0;
row_pix[2] = 0xff00ffe0;
row_pix[3] = 0xff00ffe0;
row_pix[4] = 0xff00ffe0;
row_pix[5] = 0xff00ffe0;
row_pix[6] = 0xff000000;
row_pix[7] = 0xff000000;
row_buf += stride;
row_pix = (uint32_t *)row_buf;
row_pix[0] = 0xff00ffe0;
row_pix[1] = 0xff00ffe0;
row_pix[2] = 0xff000000;
row_pix[3] = 0xff000000;
row_pix[4] = 0xff000000;
row_pix[5] = 0xff00ffe0;
row_pix[6] = 0xff00ffe0;
row_pix[7] = 0xff000000;
row_buf += stride;
row_pix = (uint32_t *)row_buf;
row_pix[0] = 0xff00ffe0;
row_pix[1] = 0xff00ffe0;
row_pix[2] = 0xff00ffe0;
row_pix[3] = 0xff00ffe0;
row_pix[4] = 0xff00ffe0;
row_pix[5] = 0xff00ffe0;
row_pix[6] = 0xff00ffe0;
row_pix[7] = 0xff000000;
for (i = 0; i < 4; ++i) {
row_buf += stride;
row_pix = (uint32_t *)row_buf;
row_pix[0] = 0xffff00ff;
row_pix[1] = 0xffff00ff;
row_pix[2] = 0xff000000;
row_pix[3] = 0xff000000;
row_pix[4] = 0xff000000;
row_pix[5] = 0xffff00ff;
row_pix[6] = 0xffff00ff;
row_pix[7] = 0xff000000;
}
}
break;
}
}
__attribute__((__constructor__)) static textstartup void _vga_init(void) {
if (IsMetal()) {
struct mman *mm = (struct mman *)(BANE + 0x0500);
@ -249,21 +70,22 @@ __attribute__((__constructor__)) static textstartup void _vga_init(void) {
unsigned char col, row;
} bios_curs_pos_t;
bios_curs_pos_t pos = *(bios_curs_pos_t *)(BANE + 0x0450ull);
uint8_t chr_ht = *(uint8_t *)(BANE + 0x0485ull),
chr_ht_hi = *(uint8_t *)(BANE + 0x0486ull);
if (chr_ht_hi != 0 || chr_ht > 32)
chr_ht = 32;
uint8_t chr_ht, chr_ht_hi, chr_wid;
if (vid_type == PC_VIDEO_TEXT) {
chr_ht = *(uint8_t *)(BANE + 0x0485ull),
chr_ht_hi = *(uint8_t *)(BANE + 0x0486ull);
if (chr_ht_hi != 0 || chr_ht > 32 || chr_ht < 2)
chr_ht = VGA_ASSUME_CHAR_HEIGHT_PX;
} else
chr_ht = VGA_ASSUME_CHAR_HEIGHT_PX;
chr_wid = VGA_ASSUME_CHAR_WIDTH_PX;
/* Make sure the video buffer is mapped into virtual memory. */
__invert_memory_area(mm, __get_pml4t(), vid_buf_phy, vid_buf_sz, PAGE_RW);
#if 1
/* Test video frame buffer output. */
_vga_init_test(vid_buf, vid_type, stride);
#endif
/*
* Initialize our tty structure from the current screen geometry,
* screen contents, cursor position, & character height.
* screen contents, cursor position, & character dimensions.
*/
_StartTty(&_vga_tty, height, width, pos.row, pos.col, chr_ht,
vid_buf, NULL);
_StartTty(&_vga_tty, vid_type, height, width, stride, pos.row, pos.col,
chr_ht, chr_wid, vid_buf, false);
}
}