From df08b541af8189f8f9d871935dec5f8d828f7a37 Mon Sep 17 00:00:00 2001 From: tkchia Date: Tue, 13 Sep 2022 17:14:10 +0800 Subject: [PATCH] Bare metal VGA: implement "status report" escape codes (#613) * Bare metal VGA: implement "status report" escape codes * Minor fix to pseudoteletypewriter code Co-authored-by: tkchia --- examples/hello4.c | 17 ++++++++++++++ libc/calls/readv-metal.c | 14 ++++++++++++ libc/vga/readv-vga.c | 49 ++++++++++++++++++++++++++++++++++++++++ libc/vga/tty.c | 13 ++++++++--- libc/vga/vga-init.S | 1 + libc/vga/vga.internal.h | 11 +++++++++ libc/vga/writev-vga.c | 9 ++++---- tool/build/lib/pty.c | 9 +++++--- 8 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 libc/vga/readv-vga.c diff --git a/examples/hello4.c b/examples/hello4.c index f3aecca99..497d5ab36 100644 --- a/examples/hello4.c +++ b/examples/hello4.c @@ -8,13 +8,30 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/math.h" +#include "libc/calls/termios.h" +#include "libc/isystem/unistd.h" +#include "libc/str/str.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/termios.h" STATIC_YOINK("vga_console"); int main(int argc, char *argv[]) { volatile long double x = -.5; volatile long double y = 1.5; + struct termios tio; + char buf[4]; + ssize_t res; + if (tcgetattr(0, &tio) != -1) { + tio.c_lflag &= ~(ECHO | ICANON); + tcsetattr(0, TCSANOW, &tio); + } + write(1, "\e[5n", 4); + res = read(0, buf, 4); + if (res != 4 || memcmp(buf, "\e[0n", 4) != 0) { + printf("res = %d?\n", res); + return -1; + } printf("Hello World! %.19Lg\n", atan2l(x, y)); return 0; } diff --git a/libc/calls/readv-metal.c b/libc/calls/readv-metal.c index bb2cf19bb..378167a08 100644 --- a/libc/calls/readv-metal.c +++ b/libc/calls/readv-metal.c @@ -20,15 +20,29 @@ #include "libc/calls/struct/fd.internal.h" #include "libc/calls/struct/iovec.h" #include "libc/calls/struct/iovec.internal.h" +#include "libc/intrin/weaken.h" #include "libc/macros.internal.h" #include "libc/str/str.h" #include "libc/sysv/errfuns.h" +#include "libc/vga/vga.internal.h" ssize_t sys_readv_metal(struct Fd *fd, const struct iovec *iov, int iovlen) { int i; size_t got, toto; struct MetalFile *file; switch (fd->kind) { + case kFdConsole: + /* + * The VGA teletypewriter code may wish to send out "status report" + * escape sequences, in response to requests sent to it via write(). + * Read & return these if they are available. + */ + if (weaken(sys_readv_vga)) { + ssize_t res = weaken(sys_readv_vga)(fd, iov, iovlen); + if (res > 0) + return res; + } + /* fall through */ case kFdSerial: return sys_readv_serial(fd, iov, iovlen); case kFdFile: diff --git a/libc/vga/readv-vga.c b/libc/vga/readv-vga.c new file mode 100644 index 000000000..a9668723f --- /dev/null +++ b/libc/vga/readv-vga.c @@ -0,0 +1,49 @@ +/*-*- 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/calls/struct/fd.internal.h" +#include "libc/calls/struct/iovec.h" +#include "libc/calls/struct/iovec.internal.h" +#include "libc/vga/vga.internal.h" + +ssize_t sys_readv_vga(struct Fd *fd, const struct iovec *iov, int iovlen) { + /* NOTE: this routine is always non-blocking. */ + size_t i, redd = 0; + ssize_t res = 0; + for (i = 0; i < iovlen; ++i) { + void *input = iov[i].iov_base; + size_t len = iov[i].iov_len; + res = _TtyRead(&_vga_tty, input, len); + if (res < 0) + break; + redd += res; + if (redd != len) + return redd; + } + if (!redd) + return res; + return redd; +} diff --git a/libc/vga/tty.c b/libc/vga/tty.c index 17eef9731..722bd3fab 100644 --- a/libc/vga/tty.c +++ b/libc/vga/tty.c @@ -536,15 +536,18 @@ static void TtyDeleteLines(struct Tty *tty) { } static void TtyReportDeviceStatus(struct Tty *tty) { - _TtyWriteInput(tty, "\e[0n", 4); + static const char report[] = "\e[0n"; + _TtyWriteInput(tty, report, strlen(report)); } static void TtyReportPreferredVtType(struct Tty *tty) { - _TtyWriteInput(tty, "\e[?1;0c", 4); + static const char report[] = "\e[?1;0c"; + _TtyWriteInput(tty, report, strlen(report)); } static void TtyReportPreferredVtIdentity(struct Tty *tty) { - _TtyWriteInput(tty, "\e/Z", 4); + static const char report[] = "\e/Z"; + _TtyWriteInput(tty, report, strlen(report)); } static void TtyBell(struct Tty *tty) { @@ -1148,9 +1151,11 @@ ssize_t _TtyWriteInput(struct Tty *tty, const void *data, size_t n) { if (cr && i < sizeof tty->input.p) { p[i++] = '\n'; } +#ifdef VGA_PERSNICKETY_STATUS if (!(tty->conf & kTtyNoecho)) { _TtyWrite(tty, p + tty->input.i, i - tty->input.i); } +#endif tty->input.i = i; return n; } @@ -1159,6 +1164,7 @@ ssize_t _TtyRead(struct Tty *tty, void *buf, size_t size) { char *p; size_t n; n = MIN(size, tty->input.i); +#ifdef VGA_PERSNICKETY_STATUS if (!(tty->conf & kTtyNocanon)) { if ((p = memchr(tty->input.p, '\n', n))) { n = MIN(n, tty->input.p - p + 1); @@ -1166,6 +1172,7 @@ ssize_t _TtyRead(struct Tty *tty, void *buf, size_t size) { n = 0; } } +#endif memcpy(buf, tty->input.p, n); memcpy(tty->input.p, tty->input.p + n, tty->input.i - n); tty->input.i -= n; diff --git a/libc/vga/vga-init.S b/libc/vga/vga-init.S index 8b4c657cc..7e332a5d7 100644 --- a/libc/vga/vga-init.S +++ b/libc/vga/vga-init.S @@ -72,3 +72,4 @@ vga_console: .endobj vga_console,globl,hidden .previous .yoink sys_writev_vga + .yoink sys_readv_vga diff --git a/libc/vga/vga.internal.h b/libc/vga/vga.internal.h index fe2b8a921..bfc6488e1 100644 --- a/libc/vga/vga.internal.h +++ b/libc/vga/vga.internal.h @@ -60,6 +60,14 @@ * (https://www.delorie.com/djgpp/doc/rbinter/id/22/1.html) */ #undef VGA_USE_BLINK +/** + * If VGA_PERSNICKETY_STATUS is defined, then when deciding how to return + * status response codes (e.g. "\e[0n"), the tty code will pay attention to + * the terminal's termios mode (TODO). If undefined, the tty code will + * simply return any response strings immediately, & will not echo them — + * per the common use case. + */ +#undef VGA_PERSNICKETY_STATUS #define kTtyFg 0x0001 #define kTtyBg 0x0002 @@ -146,6 +154,9 @@ void _TtyErase(struct Tty *, size_t, size_t); void _TtySetY(struct Tty *, unsigned short); void _TtySetX(struct Tty *, unsigned short); +extern struct Tty _vga_tty; + +ssize_t sys_readv_vga(struct Fd *, const struct iovec *, int); ssize_t sys_writev_vga(struct Fd *, const struct iovec *, int); COSMOPOLITAN_C_END_ diff --git a/libc/vga/writev-vga.c b/libc/vga/writev-vga.c index 6df7f93c8..d7a7afa1d 100644 --- a/libc/vga/writev-vga.c +++ b/libc/vga/writev-vga.c @@ -31,20 +31,21 @@ #include "libc/runtime/pc.internal.h" #include "libc/str/str.h" -static struct Tty vga_tty; #ifdef VGA_USE_WCS static wchar_t vga_wcs[VGA_TTY_HEIGHT * VGA_TTY_WIDTH]; #else static wchar_t * const vga_wcs = NULL; #endif +struct Tty _vga_tty; + ssize_t sys_writev_vga(struct Fd *fd, const struct iovec *iov, int iovlen) { size_t i, wrote = 0; ssize_t res = 0; for (i = 0; i < iovlen; ++i) { - void *input = iov[i].iov_base; + void *output = iov[i].iov_base; size_t len = iov[i].iov_len; - res = _TtyWrite(&vga_tty, input, len); + res = _TtyWrite(&_vga_tty, output, len); if (res < 0) break; wrote += res; @@ -75,6 +76,6 @@ __attribute__((__constructor__)) static textstartup void _vga_init(void) { * Initialize our tty structure from the current screen contents, current * cursor position, & character height. */ - _StartTty(&vga_tty, VGA_TTY_HEIGHT, VGA_TTY_WIDTH, pos.row, pos.col, + _StartTty(&_vga_tty, VGA_TTY_HEIGHT, VGA_TTY_WIDTH, pos.row, pos.col, chr_ht, vid_buf, vga_wcs); } diff --git a/tool/build/lib/pty.c b/tool/build/lib/pty.c index 362c0232a..ad0426e60 100644 --- a/tool/build/lib/pty.c +++ b/tool/build/lib/pty.c @@ -641,15 +641,18 @@ static void PtyDeleteLines(struct Pty *pty) { } static void PtyReportDeviceStatus(struct Pty *pty) { - PtyWriteInput(pty, "\e[0n", 4); + static const char report[] = "\e[0n"; + PtyWriteInput(pty, report, strlen(report)); } static void PtyReportPreferredVtType(struct Pty *pty) { - PtyWriteInput(pty, "\e[?1;0c", 4); + static const char report[] = "\e[?1;0c"; + PtyWriteInput(pty, report, strlen(report)); } static void PtyReportPreferredVtIdentity(struct Pty *pty) { - PtyWriteInput(pty, "\e/Z", 4); + static const char report[] = "\e/Z"; + PtyWriteInput(pty, report, strlen(report)); } static void PtyBell(struct Pty *pty) {