From 5aaadf1162f9d3b7a01ad633b51e05e0ca4fc231 Mon Sep 17 00:00:00 2001 From: tkchia Date: Wed, 5 Oct 2022 16:26:39 +0000 Subject: [PATCH] [metal] VGA console can now show "screen of death" upon a crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is now a new function _klog_vga(), which can be called by kprintf() to output system messages — e.g. information about CPU exceptions — on the VGA screen. --- libc/intrin/kprintf.greg.c | 2 + libc/vga/tty-graph.c | 268 ++---------------- libc/vga/tty-graph.inc | 329 +++++++++++++++++++++++ libc/vga/tty-klog.greg.c | 94 +++++++ libc/vga/tty.greg.c | 111 +++++--- libc/vga/{vga-init.c => vga-init.greg.c} | 54 ++-- libc/vga/vga.internal.h | 52 +++- 7 files changed, 595 insertions(+), 315 deletions(-) create mode 100644 libc/vga/tty-graph.inc create mode 100644 libc/vga/tty-klog.greg.c rename libc/vga/{vga-init.c => vga-init.greg.c} (70%) diff --git a/libc/intrin/kprintf.greg.c b/libc/intrin/kprintf.greg.c index 509a45843..86e9dbd46 100644 --- a/libc/intrin/kprintf.greg.c +++ b/libc/intrin/kprintf.greg.c @@ -55,6 +55,7 @@ #include "libc/sysv/consts/prot.h" #include "libc/thread/tls.h" #include "libc/thread/tls2.h" +#include "libc/vga/vga.internal.h" extern hidden struct SymbolTable *__symtab; @@ -188,6 +189,7 @@ privileged static void klog(const char *b, size_t n) { : /* no inputs */ : "a"(b[i]), "dN"(dx)); } + if (_weaken(_klog_vga)) _weaken(_klog_vga)(b, n); } else { asm volatile("syscall" : "=a"(rax), "=D"(rdi), "=S"(rsi), "=d"(rdx) diff --git a/libc/vga/tty-graph.c b/libc/vga/tty-graph.c index 4cef1371f..77cbc8a93 100644 --- a/libc/vga/tty-graph.c +++ b/libc/vga/tty-graph.c @@ -30,258 +30,24 @@ #include "libc/vga/vga.internal.h" /** - * @fileoverview Routines to support output in graphical video modes for - * bare metal VGA. + * @fileoverview Instantiation of routines for normal console output in + * graphical video modes. * - * @see libc/vga/tty.c + * @see libc/vga/tty-graph.inc */ -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; -} +#undef KLOGTTY -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); -} +#define COLOR TtyCanvasColor +#define BPP 32 +#define MAPCOLOR TtyGraphMapColor +#define DIRTY TtyGraphDirty +#undef UPDATE +#define RESETDIRTY TtyGraphResetDirty +#define DRAWBITMAP TtyGraphDrawBitmap +#define FILLRECT TtyGraphFillRect +#define MOVERECT TtyGraphMoveRect +#define DRAWCHAR _TtyGraphDrawChar +#define ERASELINECELLS _TtyGraphEraseLineCells +#define MOVELINECELLS _TtyGraphMoveLineCells +#include "libc/vga/tty-graph.inc" diff --git a/libc/vga/tty-graph.inc b/libc/vga/tty-graph.inc new file mode 100644 index 000000000..4387f36cd --- /dev/null +++ b/libc/vga/tty-graph.inc @@ -0,0 +1,329 @@ +/*-*- 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 Template for routines to support output in graphical video + * modes for bare metal VGA. + * + * If KLOGTTY is undefined, this file expands to code that writes to a + * separate off-screen canvas buffer — which should be allocated beforehand + * — before drawing to the actual video memory. + * + * If KLOGTTY is defined, this file instead expands to code that can + * implement an emergency console. The graphics routines will directly write + * to video memory. This may result in slower output, but will work even if + * we cannot allocate an off-screen canvas for whatever reason. + * + * @see libc/vga/tty.greg.c + * @see libc/vga/tty-graph.c + * @see libc/vga/tty-klog.greg.c + */ + +#ifndef KLOGTTY + +static COLOR MAPCOLOR(struct Tty *tty, TtyCanvasColor ic) { + return ic; +} + +static void DIRTY(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 RESETDIRTY(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); + RESETDIRTY(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); + RESETDIRTY(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); + RESETDIRTY(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); + RESETDIRTY(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; + } + } +} + +#else /* KLOGTTY */ + +static COLOR MAPCOLOR(struct Tty *tty, TtyCanvasColor ic) { +#if BPP == 16 + if (tty->type == PC_VIDEO_BGR565) + return htole16(ic.bgr.b >> 3 | ic.bgr.g >> 2 << 5 | ic.bgr.r >> 3 << 11); + else + return htole16(ic.bgr.b >> 3 | ic.bgr.g >> 3 << 5 | ic.bgr.r >> 3 << 10); +#else + if (tty->type == PC_VIDEO_BGRX8888) + return ic.w; + else { + TtyRgbxColor oc = (TtyRgbxColor){.rgb.r = ic.bgr.r, .rgb.g = ic.bgr.g, + .rgb.b = ic.bgr.b, .rgb.x = 0xff}; + return oc.w; + } +#endif +} + +static void DIRTY(struct Tty *tty, size_t gy1, size_t gx1, + size_t gy2, size_t gx2) { +} + +static void RESETDIRTY(struct Tty *tty) { +} + +void UPDATE(struct Tty *tty) { +} + +#endif /* KLOGTTY */ + +static void DRAWBITMAP(struct Tty *tty, size_t gy, size_t gx, + COLOR fg, COLOR 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(COLOR); + size_t yleft = bm_ht, xleft; + while (yleft-- != 0) { + COLOR *plotter = (COLOR *)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; + } + DIRTY(tty, gy, gx, gy + bm_ht, gx + bm_wid); +} + +static unrollloops void FILLRECT(struct Tty *tty, size_t gy, size_t gx, + size_t fill_ht, size_t fill_wid, COLOR bg) { + size_t xs = tty->xs; + char *cplotter = tty->canvas + gy * xs + gx * sizeof(COLOR); + size_t yleft = fill_ht, xleft; + while (yleft-- != 0) { + COLOR *plotter = (COLOR *)cplotter; + size_t i; + for (i = 0; i < fill_wid; ++i) + *plotter++ = bg; + cplotter += xs; + } + DIRTY(tty, gy, gx, gy + fill_ht, gx + fill_wid); +} + +static void MOVERECT(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(COLOR); + char *canvas = tty->canvas; + DIRTY(tty, dgy, dgx, dgy + ht, dgx + wid); + if (dgy < sgy) { + while (ht-- != 0) { + memmove(canvas + dgy * xs + dgx * sizeof(COLOR), + canvas + sgy * xs + sgx * sizeof(COLOR), xm); + ++dgy; + ++sgy; + } + } else if (dgy > sgy) { + while (ht-- != 0) + memmove(canvas + (dgy + ht) * xs + dgx * sizeof(COLOR), + canvas + (sgy + ht) * xs + sgx * sizeof(COLOR), xm); + } +} + +void DRAWCHAR(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]); + COLOR fg = MAPCOLOR(tty, tty->fg), bg = MAPCOLOR(tty, tty->bg); + if ((tty->pr & kTtyFlip) != 0) + fg = bg, bg = MAPCOLOR(tty, 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) + DRAWBITMAP(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. + */ + DRAWBITMAP(tty, y * tty->yc, x * tty->xc, fg, bg, glyph, glyph_ht, 8); + FILLRECT(tty, y * tty->yc + glyph_ht, x * tty->xc, + VGA_ASSUME_CHAR_HEIGHT_PX - glyph_ht, 8, bg); + } +} + +void ERASELINECELLS(struct Tty *tty, size_t y, size_t x, size_t n) { + size_t yc = tty->yc, xc = tty->xc; + FILLRECT(tty, y * yc, x * xc, yc, n * xc, MAPCOLOR(tty, tty->bg)); +} + +void MOVELINECELLS(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; + MOVERECT(tty, dsty * yc, dstx * xc, srcy * yc, srcx * xc, yc, n * xc); +} diff --git a/libc/vga/tty-klog.greg.c b/libc/vga/tty-klog.greg.c new file mode 100644 index 000000000..8e9897ea9 --- /dev/null +++ b/libc/vga/tty-klog.greg.c @@ -0,0 +1,94 @@ +/*-*- 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/runtime/mman.internal.h" +#include "libc/str/str.h" +#include "libc/vga/vga.internal.h" + +/* + * @fileoverview Instantiation of routines for emergency or system console + * output in graphical video modes. + * + * @see libc/vga/tty-graph.inc + */ + +#define KLOGTTY + +/* Instantiate output routines for 16-bit pixel formats. */ +#define COLOR uint16_t +#define BPP 16 +#define MAPCOLOR TtyKlog16MapColor +#define DIRTY TtyKlog16Dirty +#define UPDATE _TtyKlog16Update +#define RESETDIRTY TtyKlog16ResetDirty +#define DRAWBITMAP TtyKlog16DrawBitmap +#define FILLRECT TtyKlog16FillRect +#define MOVERECT TtyKlog16MoveRect +#define DRAWCHAR _TtyKlog16DrawChar +#define ERASELINECELLS _TtyKlog16EraseLineCells +#define MOVELINECELLS _TtyKlog16MoveLineCells +#include "libc/vga/tty-graph.inc" + +#undef COLOR +#undef BPP +#undef MAPCOLOR +#undef BG +#undef DIRTY +#undef UPDATE +#undef RESETDIRTY +#undef DRAWBITMAP +#undef FILLRECT +#undef MOVERECT +#undef DRAWCHAR +#undef ERASELINECELLS +#undef MOVELINECELLS + +/* Instantiate output routines for 32-bit pixel formats. */ +#define COLOR uint32_t +#define BPP 32 +#define MAPCOLOR TtyKlog32MapColor +#define DIRTY TtyKlog32Dirty +#define UPDATE _TtyKlog32Update +#define RESETDIRTY TtyKlog32ResetDirty +#define DRAWBITMAP TtyKlog32DrawBitmap +#define FILLRECT TtyKlog32FillRect +#define MOVERECT TtyKlog32MoveRect +#define DRAWCHAR _TtyKlog32DrawChar +#define ERASELINECELLS _TtyKlog32EraseLineCells +#define MOVELINECELLS _TtyKlog32MoveLineCells +#include "libc/vga/tty-graph.inc" + +static unsigned short klog_y = 0, klog_x = 0; + +privileged void _klog_vga(const char *b, size_t n) { + struct Tty tty; + _vga_reinit(&tty, klog_y, klog_x, kTtyKlog); + _TtyWrite(&tty, b, n); + klog_y = _TtyGetY(&tty); + klog_x = _TtyGetX(&tty); +} diff --git a/libc/vga/tty.greg.c b/libc/vga/tty.greg.c index e1a6c6a93..79c13f25d 100644 --- a/libc/vga/tty.greg.c +++ b/libc/vga/tty.greg.c @@ -67,10 +67,10 @@ static void SetXsFb(struct Tty *tty, unsigned short xsfb) { tty->xsfb = xsfb; } -static bool SetWcs(struct Tty *tty, bool alloc_wcs) { +static bool SetWcs(struct Tty *tty, unsigned init_flags) { #ifdef VGA_USE_WCS struct DirectMap dm; - if (!alloc_wcs) + if (!(init_flags & kTtyAllocWcs)) return false; dm = sys_mmap_metal(NULL, Yn(tty) * Xn(tty) * sizeof(wchar_t), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, @@ -102,48 +102,75 @@ static wchar_t *Wcs(struct Tty *tty) { #endif } +static void TtyNoopUpdate(struct Tty *); 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) { +static void TtySetType(struct Tty *tty, unsigned char type, + unsigned init_flags) { 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; + if ((init_flags & kTtyKlog) == 0) { + 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->update = TtyNoopUpdate; + tty->drawchar = TtyTextDrawChar; + tty->eraselinecells = TtyTextEraseLineCells; + tty->movelinecells = TtyTextMoveLineCells; + } + } else { + switch (type) { + case PC_VIDEO_BGR565: + case PC_VIDEO_BGR555: + tty->update = _TtyKlog16Update; + tty->drawchar = _TtyKlog16DrawChar; + tty->eraselinecells = _TtyKlog16EraseLineCells; + tty->movelinecells = _TtyKlog16MoveLineCells; + break; + case PC_VIDEO_BGRX8888: + case PC_VIDEO_RGBX8888: + tty->update = _TtyKlog32Update; + tty->drawchar = _TtyKlog32DrawChar; + tty->eraselinecells = _TtyKlog32EraseLineCells; + tty->movelinecells = _TtyKlog32MoveLineCells; + break; + default: + tty->update = TtyNoopUpdate; + 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 yc, unsigned char xc, void *fb, bool alloc_wcs) { + unsigned char yc, unsigned char xc, void *fb, + unsigned init_flags) { 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; @@ -151,14 +178,23 @@ void _StartTty(struct Tty *tty, unsigned char type, } 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. */ + if ((init_flags & kTtyKlog) != 0) { tty->canvas = fb; - starty = startx = 0; - } else - tty->canvas = dm.addr; + xs = xsfb; + } else { + 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, & initialize + * our tty as an emergency console. + */ + init_flags |= kTtyKlog; + tty->canvas = fb; + xs = xsfb; + } else + tty->canvas = dm.addr; + } } SetYn(tty, yn); SetXn(tty, xn); @@ -169,8 +205,10 @@ void _StartTty(struct Tty *tty, unsigned char type, if (startx >= xn) startx = xn - 1; tty->y = starty; tty->x = startx; - TtySetType(tty, type); - if (SetWcs(tty, alloc_wcs)) { + if ((init_flags & kTtyKlog) != 0) init_flags &= ~kTtyAllocWcs; + tty->xs = xs; + TtySetType(tty, type, init_flags); + if (SetWcs(tty, init_flags)) { wchar_t *wcs = Wcs(tty); size_t n = (size_t)yn * xn, i; if (type == PC_VIDEO_TEXT) { @@ -335,6 +373,9 @@ static uint8_t TtyGetTextAttr(struct Tty *tty) { return attr; } +static void TtyNoopUpdate(struct Tty *tty) { +} + 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; diff --git a/libc/vga/vga-init.c b/libc/vga/vga-init.greg.c similarity index 70% rename from libc/vga/vga-init.c rename to libc/vga/vga-init.greg.c index 2434c7a33..42c0491b2 100644 --- a/libc/vga/vga-init.c +++ b/libc/vga/vga-init.greg.c @@ -31,34 +31,40 @@ struct Tty _vga_tty; +void _vga_reinit(struct Tty *tty, unsigned short starty, unsigned short startx, + unsigned init_flags) { + struct mman *mm = (struct mman *)(BANE + 0x0500); + unsigned char vid_type = mm->pc_video_type; + unsigned short height = mm->pc_video_height, width = mm->pc_video_width, + stride = mm->pc_video_stride; + uint64_t vid_buf_phy = mm->pc_video_framebuffer; + void *vid_buf = (void *)(BANE + vid_buf_phy); + size_t vid_buf_sz = mm->pc_video_framebuffer_size; + uint8_t chr_ht, chr_wid; + if (vid_type == PC_VIDEO_TEXT) { + unsigned short chr_ht_val = mm->pc_video_char_height; + if (chr_ht_val > 32 || chr_ht_val < 2) + chr_ht = VGA_ASSUME_CHAR_HEIGHT_PX; + else + chr_ht = chr_ht_val; + } 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); + /* + * Initialize our tty structure from the current screen geometry, screen + * contents, cursor position, & character dimensions. + */ + _StartTty(tty, vid_type, height, width, stride, starty, startx, + chr_ht, chr_wid, vid_buf, init_flags); +} + __attribute__((__constructor__)) static textstartup void _vga_init(void) { if (IsMetal()) { struct mman *mm = (struct mman *)(BANE + 0x0500); - unsigned char vid_type = mm->pc_video_type; - unsigned short height = mm->pc_video_height, width = mm->pc_video_width, - stride = mm->pc_video_stride; - uint64_t vid_buf_phy = mm->pc_video_framebuffer; - void *vid_buf = (void *)(BANE + vid_buf_phy); - size_t vid_buf_sz = mm->pc_video_framebuffer_size; unsigned short starty = mm->pc_video_curs_info.y, startx = mm->pc_video_curs_info.x; - uint8_t chr_ht, chr_wid; - if (vid_type == PC_VIDEO_TEXT) { - unsigned short chr_ht_val = mm->pc_video_char_height; - if (chr_ht_val > 32 || chr_ht_val < 2) - chr_ht = VGA_ASSUME_CHAR_HEIGHT_PX; - else - chr_ht = chr_ht_val; - } 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); - /* - * Initialize our tty structure from the current screen geometry, - * screen contents, cursor position, & character dimensions. - */ - _StartTty(&_vga_tty, vid_type, height, width, stride, starty, startx, - chr_ht, chr_wid, vid_buf, false); + _vga_reinit(&_vga_tty, starty, startx, 0); } } diff --git a/libc/vga/vga.internal.h b/libc/vga/vga.internal.h index bd9022aad..0a02f7856 100644 --- a/libc/vga/vga.internal.h +++ b/libc/vga/vga.internal.h @@ -75,6 +75,16 @@ */ #undef VGA_PERSNICKETY_STATUS +/* Flags which are passed to _StartTty(). */ +#define kTtyAllocWcs 0x01 /* allocate Unicode character array + (if VGA_USE_WCS also defined) */ +#define kTtyKlog 0x02 /* the system might be in an abnormal + state, & we are setting up the tty + to show system messages */ + +/** + * Flags for Tty::pr. These govern properties of individual character cells. + */ #define kTtyFg 0x0001 #define kTtyBg 0x0002 #define kTtyBold 0x0004 @@ -89,6 +99,10 @@ #define kTtyStrike 0x0800 #define kTtyConceal 0x1000 +/** + * Flags for Tty::conf. These govern the current state of the teletypewriter + * as a whole. + */ #define kTtyBell 0x001 #define kTtyRedzone 0x002 #define kTtyNocursor 0x004 @@ -181,14 +195,15 @@ struct Tty { * 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. + * update the frame buffer from the canvas every now & then. + * + * During normal operation, the canvas's pixel format is 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(). + * to screen with Tty::update(). */ unsigned short updy1, updx1, updy2, updx2; #ifdef VGA_USE_WCS @@ -219,6 +234,15 @@ forceinline unsigned char _TtyCanvasType(struct Tty *tty) { return tty->type == PC_VIDEO_TEXT ? PC_VIDEO_TEXT : PC_VIDEO_BGRX8888; } +forceinline unsigned short _TtyGetY(struct Tty *tty) { + return tty->y; +} + +forceinline unsigned short _TtyGetX(struct Tty *tty) { + return tty->x; +} + +/* Routines that implement normal graphical console output. */ void _TtyBgrxUpdate(struct Tty *); void _TtyRgbxUpdate(struct Tty *); void _TtyBgr565Update(struct Tty *); @@ -228,9 +252,25 @@ void _TtyGraphEraseLineCells(struct Tty *, size_t, size_t, size_t); void _TtyGraphMoveLineCells(struct Tty *, size_t, size_t, size_t, size_t, size_t); +/* + * Routines that implement emergency or system console output in graphical + * video modes. + */ +void _TtyKlog16Update(struct Tty *); +void _TtyKlog16DrawChar(struct Tty *, size_t, size_t, wchar_t); +void _TtyKlog16EraseLineCells(struct Tty *, size_t, size_t, size_t); +void _TtyKlog16MoveLineCells(struct Tty *, size_t, size_t, size_t, size_t, + size_t); +void _TtyKlog32Update(struct Tty *); +void _TtyKlog32DrawChar(struct Tty *, size_t, size_t, wchar_t); +void _TtyKlog32EraseLineCells(struct Tty *, size_t, size_t, size_t); +void _TtyKlog32MoveLineCells(struct Tty *, size_t, size_t, size_t, size_t, + size_t); + +/* High-level teletypewriter routines. */ void _StartTty(struct Tty *, unsigned char, unsigned short, unsigned short, unsigned short, unsigned short, unsigned short, - unsigned char, unsigned char, void *, bool); + unsigned char, unsigned char, void *, unsigned); 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); @@ -246,6 +286,8 @@ void _TtySetX(struct Tty *, unsigned short); extern const uint8_t _vga_font_default_direct[95][13]; extern struct Tty _vga_tty; +void _vga_reinit(struct Tty *, unsigned short, unsigned short, unsigned); +void _klog_vga(const char *, size_t); ssize_t sys_readv_vga(struct Fd *, const struct iovec *, int); ssize_t sys_writev_vga(struct Fd *, const struct iovec *, int);