diff --git a/.gitignore b/.gitignore index 844d010c..dc86715e 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ /TAGS /bx_enh_dbg.ini /tool/emacs/*.elc +/usr/share/dict/words diff --git a/ape/ape.S b/ape/ape.S index e3853100..73c7b3c4 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -815,8 +815,8 @@ ape.pe: .ascin "PE",4 .short .LDLLEXE # DllCharacteristics .quad 0x0000000000100000 # StackReserve .quad 0x0000000000100000 # StackCommit - .quad 0x0000000000080000 # HeapReserve - .quad 0x0000000000001000 # HeapCommit + .quad 0x0000000000000000 # HeapReserve + .quad 0x0000000000000000 # HeapCommit .long 0x00000000 # LoaderFlags .long 16 # NumberOfDirectoryEntries .long 0,0 # ExportsDirectory diff --git a/dsp/core/getintegercoefficients.c b/dsp/core/getintegercoefficients.c index f52f4555..a47325ef 100644 --- a/dsp/core/getintegercoefficients.c +++ b/dsp/core/getintegercoefficients.c @@ -85,7 +85,7 @@ long GetIntegerCoefficients(long N[static 6], const double C[static 6], long M, N[i] = 0; } } - if (!IsTiny() && least > 1) { + if (!NoDebug() && least > 1) { for (j[0] = 0; j[0] < J[0]; ++j[0]) { for (j[1] = 0; j[1] < J[1]; ++j[1]) { for (j[2] = 0; j[2] < J[2]; ++j[2]) { diff --git a/dsp/scale/gyarados.c b/dsp/scale/gyarados.c index 36a208eb..586d6ebd 100644 --- a/dsp/scale/gyarados.c +++ b/dsp/scale/gyarados.c @@ -75,7 +75,6 @@ static struct SamplingSolution *NewSamplingSolution(long n, long s) { ss->indices = xcalloc(n * s, sizeof(short)); return ss; } - static bool IsNormalized(int n, double A[n]) { int i; double x; diff --git a/dsp/tty/hidecursor.c b/dsp/tty/hidecursor.c index 6e798cfc..7200e55e 100644 --- a/dsp/tty/hidecursor.c +++ b/dsp/tty/hidecursor.c @@ -24,6 +24,9 @@ #include "libc/nt/console.h" #include "libc/nt/runtime.h" #include "libc/nt/struct/consolecursorinfo.h" +#include "libc/runtime/runtime.h" + +/* TODO(jart): DELETE */ static int ttysetcursor(int fd, bool visible) { struct NtConsoleCursorInfo ntcursor; diff --git a/dsp/tty/ident.c b/dsp/tty/ident.c index ef840b40..ae986a69 100644 --- a/dsp/tty/ident.c +++ b/dsp/tty/ident.c @@ -30,6 +30,8 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/errfuns.h" +/* TODO(jart): DELETE */ + static int ttyident_probe(struct TtyIdent *ti, int ttyinfd, int ttyoutfd, const char *msg) { ssize_t rc; diff --git a/dsp/tty/send.c b/dsp/tty/send.c index 55366653..ccbf3a6b 100644 --- a/dsp/tty/send.c +++ b/dsp/tty/send.c @@ -20,6 +20,8 @@ #include "dsp/tty/tty.h" #include "libc/str/str.h" +/* TODO(jart): DELETE */ + /** * Sends data to teletypewriter. * @@ -27,4 +29,6 @@ * * @return 0 on success, or -1 w/ errno */ -int ttysend(int fd, const char *str) { return ttywrite(fd, str, strlen(str)); } +int ttysend(int fd, const char *str) { + return ttywrite(fd, str, strlen(str)); +} diff --git a/dsp/tty/sendtitle.c b/dsp/tty/sendtitle.c index 499379f3..8cc51eda 100644 --- a/dsp/tty/sendtitle.c +++ b/dsp/tty/sendtitle.c @@ -22,6 +22,8 @@ #include "libc/runtime/gc.h" #include "libc/x/x.h" +/* TODO(jart): DELETE */ + /** * Changes text in title bar of pseudo-teletypewriter window. * diff --git a/dsp/tty/ttyraw.c b/dsp/tty/ttyraw.c index 025aed48..54451d0e 100644 --- a/dsp/tty/ttyraw.c +++ b/dsp/tty/ttyraw.c @@ -38,6 +38,8 @@ #include "libc/sysv/errfuns.h" #include "libc/x/x.h" +/* TODO(jart): DELETE */ + #define FD STDOUT_FILENO static struct TtyRaw { diff --git a/dsp/tty/write.c b/dsp/tty/write.c index ee5f24ab..c153292c 100644 --- a/dsp/tty/write.c +++ b/dsp/tty/write.c @@ -25,6 +25,8 @@ #include "libc/sysv/consts/poll.h" #include "libc/sysv/consts/termios.h" +/* TODO(jart): DELETE */ + /** * Sends data to teletypewriter the pain-free way. * diff --git a/examples/acid2.c b/examples/acid2.c new file mode 100644 index 00000000..a7300d83 --- /dev/null +++ b/examples/acid2.c @@ -0,0 +1,136 @@ +#if 0 +/*─────────────────────────────────────────────────────────────────╗ +│ To the extent possible under law, Justine Tunney has waived │ +│ all copyright and related or neighboring rights to this file, │ +│ as it is written in the following disclaimers: │ +│ • http://unlicense.org/ │ +│ • http://creativecommons.org/publicdomain/zero/1.0/ │ +╚─────────────────────────────────────────────────────────────────*/ +#endif +#include "libc/calls/calls.h" +#include "libc/calls/ioctl.h" +#include "libc/calls/struct/termios.h" +#include "libc/fmt/fmt.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/termios.h" + +int yn2, xn2; +int yn3, xn3; +char b[128], inbuf[128]; +int y, x, yn, xn, my, mx; +struct termios term, oldterm; + +int main(int argc, char *argv[]) { + int i; + setvbuf(stdout, inbuf, _IONBF, 128); /* make things slower */ + + /* raw mode */ + ioctl(1, TCGETS, &oldterm); + memcpy(&term, &oldterm, sizeof(term)); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 1; + term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON); + term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL); + term.c_cflag &= ~(CSIZE | PARENB); + term.c_oflag &= ~OPOST; + term.c_cflag |= CS8; + term.c_iflag |= IUTF8; + ioctl(1, TCSETSF, &term); + + /* get cursor position and display dimensions */ + printf("\e7\e[6n\e[9979;9979H\e[6n\e8"); + read(0, b, sizeof(b)); + sscanf(b, "\e[%d;%dR\e[%d;%dR", &y, &x, &yn, &xn); + + printf("\e[1q"); /* turn on led one */ + printf("\e[2J"); /* clear display */ + printf("\e#8"); /* fill display with E's */ + printf("\e[H"); + + /* clear display again */ + printf("\e[2q"); /* turn on led two */ + for (i = 0; i < yn; ++i) { + if (i) printf("\n"); + printf(" "); + printf("\e[0K"); + } + for (i = 0; i < yn - 1; ++i) { + if (i) printf("\eM"); + printf("\e[1K"); + } + + printf("\e(0"); /* line drawing mode */ + printf("\e[3q"); /* turn on led three */ + printf("\e[H"); + + /* move to center */ + my = yn / 2; + mx = xn / 2 - 7; + if (y > my) { + printf("\e[%dA", y - my); + } else if (y < my) { + printf("\e[%dB", my - y); + } + if (x > mx) { + printf("\e[%dD", x - mx); + } else if (x < mx) { + printf("\e[%dC", mx - x); + } + + printf("\e[90;103m"); /* black on yellow */ + printf("\e[90;103ma ` a"); /* draw nose */ + + printf("\e[0m"); /* reset style */ + printf("\e(B"); /* ascii mode */ + + /* draw corners */ + printf("\e[H"); /* top left */ + printf("A"); + printf("\e[9979C"); /* rightmost */ + printf("B"); + printf("\e[9979;9979H"); /* bottom right corner */ + printf("\e[C"); /* move right gets clamped */ + printf("D"); /* write, set redzone flag */ + printf("\e[2A"); /* move up, unsets redzone */ + + /* gnu screen now reports out of bounds position */ + /* kitty hasnt got a redzone reporting next line */ + printf("\e[6n"); + read(0, b, sizeof(b)); + sscanf(b, "\e[%d;%dR", &yn2, &xn2); + + /* writes to (yn-3,xn-1) normally and (yn-2,0) in gnu screen */ + printf("!"); + + /* draw ruler on top */ + printf("\e[H"); + for (i = 8; i + 1 < xn; i += 8) { + printf("\e[%dG%d", i + 1, i); /* set column */ + } + + printf("\e[9979;9979H"); /* bottom right */ + printf("\e[9979D"); /* leftmost */ + printf("C"); + + /* let's break gnu screen again with multimonospace redzone */ + printf("\e[%d;9979H", yn / 2); /* right middle */ + printf("\e[D"); /* left */ + printf("A"); + printf("\e[6n"); + read(0, b, sizeof(b)); + sscanf(b, "\e[%d;%dR", &yn2, &xn2); + + printf("\e[%dH", yn / 2); + printf("%d %d vs. %d %d\r\n", yn, xn, yn2, xn2); + printf("%d %d vs. %d %d\r\n", yn / 2, xn, yn2, xn2); + printf("\e#6double width\e#5\r\n"); + printf("\e[3mthis text is so \e[1mitalic\e[0m\r\n"); + printf("\e[1;20mpress any fraktur exit\e[0m"); + printf("\a"); + + read(0, b, sizeof(b)); + printf("\r\n"); + ioctl(1, TCSETS, &oldterm); + return 0; +} diff --git a/examples/backtrace.c b/examples/backtrace.c index 66aae016..270c0259 100644 --- a/examples/backtrace.c +++ b/examples/backtrace.c @@ -8,12 +8,14 @@ ╚─────────────────────────────────────────────────────────────────*/ #endif #include "libc/calls/calls.h" +#include "libc/log/backtrace.h" #include "libc/log/log.h" #include "libc/mem/mem.h" #include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/fileno.h" /* @@ -45,9 +47,9 @@ void hello(void) { gc(malloc(1)); - backtrace(stdout); + ShowBacktrace(STDOUT_FILENO, NULL); setenv("ADDR2LINE", "", true); - backtrace(stdout); + ShowBacktrace(STDOUT_FILENO, NULL); } void world(void) { diff --git a/examples/examples.mk b/examples/examples.mk index 765e2566..f3417d58 100644 --- a/examples/examples.mk +++ b/examples/examples.mk @@ -141,9 +141,9 @@ o/$(MODE)/examples/nesemu1.com.dbg: \ $(APE) @$(APELINK) -o/$(MODE)/usr/share/dict/words: usr/share/dict/words.gz +usr/share/dict/words: usr/share/dict/words.gz @$(MKDIR) $(dir $@) - @$(GZ) $(ZFLAGS) <$< >$@ + @$(GZ) $(ZFLAGS) -d <$< >$@ o/$(MODE)/examples/ugh.ok: o/$(MODE)/examples/wut.com $< diff --git a/examples/hellojs.c b/examples/hellojs.c index 8afb234d..db098b3a 100644 --- a/examples/hellojs.c +++ b/examples/hellojs.c @@ -15,6 +15,8 @@ #include "libc/sysv/consts/o.h" #include "third_party/duktape/duktape.h" +STATIC_YOINK("zip_uri_support"); + static duk_ret_t NativePrint(duk_context *ctx) { duk_push_string(ctx, " "); duk_insert(ctx, 0); diff --git a/examples/ispell.c b/examples/ispell.c index 816b83cf..ed7ab73f 100644 --- a/examples/ispell.c +++ b/examples/ispell.c @@ -22,6 +22,8 @@ #include "libc/sysv/consts/fileno.h" #include "libc/x/x.h" +STATIC_YOINK("zip_uri_support"); + /** * @fileoverview Simple Interactive Spell Checker. * diff --git a/examples/kilo.c b/examples/kilo.c index 43ab0ad5..e8cfd0f3 100644 --- a/examples/kilo.c +++ b/examples/kilo.c @@ -51,6 +51,7 @@ Contact: antirez@gmail.com\"\n\ */ #define KILO_VERSION "0.0.1" +#define SYNTAX 1 #ifndef _BSD_SOURCE #define _BSD_SOURCE @@ -169,14 +170,16 @@ void editorSetStatusMessage(const char *fmt, ...); * There is no support to highlight patterns currently. */ /* C / C++ */ -const char *const C_HL_extensions[] = {".c", ".cpp", NULL}; +const char *const C_HL_extensions[] = {".c", ".h", ".cpp", NULL}; const char *const C_HL_keywords[] = { /* A few C / C++ keywords */ "switch", "if", "while", "for", "break", "continue", "return", "else", "struct", "union", "typedef", "static", "enum", "class", /* C types */ "int|", "long|", "double|", "float|", "char|", "unsigned|", "signed|", - "void|", NULL}; + "void|", "const|", "size_t|", "ssize_t|", "uint8_t|", "int8_t|", + "uint16_t|", "int16_t|", "uint32_t|", "int32_t|", "uint64_t|", "int64_t|", + NULL}; /* Here we define an array of syntax highlights by extensions, keywords, * comments delimiters and flags. */ @@ -201,7 +204,10 @@ void disableRawMode(int64_t fd) { /* Called at exit to avoid remaining in raw mode. */ void editorAtExit(void) { + char buf[64]; disableRawMode(STDIN_FILENO); + write(STDOUT_FILENO, buf, + sprintf(buf, "\e[%d;%dH\r\n\r\n\r\n", E.screenrows, E.screencols)); } /* Raw mode: 1960 magic shit. */ @@ -530,8 +536,10 @@ void editorUpdateSyntax(erow *row) { * state changed. This may recursively affect all the following rows * in the file. */ int oc = editorRowHasOpenComment(row); - /* if (row->hl_oc != oc && row->idx + 1 < E.numrows) */ - /* editorUpdateSyntax(&E.row[row->idx + 1]); */ +#if SYNTAX + if (row->hl_oc != oc && row->idx + 1 < E.numrows) + editorUpdateSyntax(&E.row[row->idx + 1]); +#endif row->hl_oc = oc; } @@ -586,15 +594,17 @@ void editorUpdateRow(erow *row) { * respecting tabs, substituting non printable characters with '?'. */ free(row->render); for (j = 0; j < row->size; j++) { - if (row->chars[j] == CTRL('I')) tabs++; + if (row->chars[j] == '\t') tabs++; } row->render = malloc(row->size + tabs * 8 + nonprint * 9 + 1); idx = 0; for (j = 0; j < row->size; j++) { - if (row->chars[j] == CTRL('I')) { + if (row->chars[j] == '\t') { row->render[idx++] = ' '; - while ((idx + 1) % 8 != 0) row->render[idx++] = ' '; + while (idx % 8 != 0) { + row->render[idx++] = ' '; + } } else { row->render[idx++] = row->chars[j]; } @@ -603,7 +613,9 @@ void editorUpdateRow(erow *row) { row->render[idx] = '\0'; /* Update the syntax highlighting attributes of the row. */ - /* editorUpdateSyntax(row); */ +#if SYNTAX + editorUpdateSyntax(row); +#endif } /* Insert a row at the specified position, shifting the other rows on the bottom @@ -918,38 +930,46 @@ void editorRefreshScreen(void) { r = &E.row[filerow]; int len = r->rsize - E.coloff; - /* int current_color = -1; */ +#if SYNTAX + int current_color = -1; +#endif if (len > 0) { if (len > E.screencols) len = E.screencols; char *c = r->render + E.coloff; - /* unsigned char *hl = r->hl + E.coloff; */ +#if SYNTAX + unsigned char *hl = r->hl + E.coloff; +#endif int j; for (j = 0; j < len; j++) { - /* if (hl[j] == HL_NONPRINT) { */ - /* char sym; */ - /* abAppend(&ab, "\e[7m", 4); */ - /* if (c[j] <= 26) */ - /* sym = '@' + c[j]; */ - /* else */ - /* sym = '?'; */ - /* abAppend(&ab, &sym, 1); */ - /* abAppend(&ab, "\e[0m", 4); */ - /* } else if (hl[j] == HL_NORMAL) { */ - /* if (current_color != -1) { */ - /* abAppend(&ab, "\e[39m", 5); */ - /* current_color = -1; */ - /* } */ - abAppend(&ab, c + j, 1); - /* } else { */ - /* int color = editorSyntaxToColor(hl[j]); */ - /* if (color != current_color) { */ - /* char buf_[16]; */ - /* int clen = snprintf(buf_, sizeof(buf_), "\e[%dm", color); */ - /* current_color = color; */ - /* abAppend(&ab, buf_, clen); */ - /* } */ - /* abAppend(&ab, c + j, 1); */ - /* } */ +#if SYNTAX + if (hl[j] == HL_NONPRINT) { + char sym; + abAppend(&ab, "\e[7m", 4); + if (c[j] <= 26) + sym = '@' + c[j]; + else + sym = '?'; + abAppend(&ab, &sym, 1); + abAppend(&ab, "\e[0m", 4); + } else if (hl[j] == HL_NORMAL) { + if (current_color != -1) { + abAppend(&ab, "\e[39m", 5); + current_color = -1; + } +#endif + abAppend(&ab, c + j, 1); +#if SYNTAX + } else { + int color = editorSyntaxToColor(hl[j]); + if (color != current_color) { + char buf_[16]; + int clen = snprintf(buf_, sizeof(buf_), "\e[%dm", color); + current_color = color; + abAppend(&ab, buf_, clen); + } + abAppend(&ab, c + j, 1); + } +#endif } } abAppend(&ab, "\e[39m", 5); @@ -1200,8 +1220,8 @@ void editorMoveCursor(int key) { void editorProcessKeypress(int64_t fd) { /* When the file is modified, requires Ctrl-q to be pressed N times * before actually quitting. */ - int c, times; static int quit_times; + int c, c2, times, oldcx; c = editorReadKey(fd); switch (c) { @@ -1226,16 +1246,16 @@ void editorProcessKeypress(int64_t fd) { case CTRL('U'): case CTRL('X'): { - int c2 = editorReadKey(fd); + c2 = editorReadKey(fd); switch (c2) { case CTRL('S'): editorSave(); break; - case CTRL('C'): - /* Erase in Display (ED): Clear from cursor to end of screen. */ - write(STDOUT_FILENO, "\r\e[0J", 5); + case CTRL('C'): { + /* write(STDOUT_FILENO, "\r\e[0J", 5); */ exit(0); break; + } default: /* ignore */ break; } @@ -1254,7 +1274,7 @@ void editorProcessKeypress(int64_t fd) { break; case CTRL('K'): { /* Kill Line */ - int oldcx = E.cx; + oldcx = E.cx; do { editorMoveCursor(ARROW_RIGHT); } while (E.cx); @@ -1294,7 +1314,7 @@ void editorProcessKeypress(int64_t fd) { case HOME_KEY: case CTRL('A'): - while (E.cx) editorMoveCursor(ARROW_LEFT); + while (E.cx || E.coloff) editorMoveCursor(ARROW_LEFT); break; case END_KEY: case CTRL('E'): diff --git a/examples/printargs.c b/examples/printargs.c index 70b992bd..eae6b7d3 100644 --- a/examples/printargs.c +++ b/examples/printargs.c @@ -72,5 +72,9 @@ int main(int argc, char *argv[], char **envp) { fmt), kAuxiliaryValues[i].name, key, val, kAuxiliaryValues[i].description); } + printf("\nSpecial Directories:\n"); + printf(" ☼ kTmpPath = %`'s\n", kTmpPath); + printf(" ☼ kNtSystemDirectory = %`'s\n", kNtSystemDirectory); + printf(" ☼ kNtWindowsDirectory = %`'s\n", kNtWindowsDirectory); return 0; } diff --git a/examples/printmysymbols.c b/examples/printmysymbols.c index 759251d7..64cd0cdf 100644 --- a/examples/printmysymbols.c +++ b/examples/printmysymbols.c @@ -9,16 +9,18 @@ #endif #include "libc/calls/calls.h" #include "libc/elf/elf.h" +#include "libc/log/backtrace.h" #include "libc/log/log.h" #include "libc/runtime/symbols.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/fileno.h" int main(int argc, char *argv[]) { int rc = 0; char *filename; struct SymbolTable *tab = NULL; - if ((filename = finddebugbinary()) != NULL && - (tab = opensymboltable(filename))) { + if ((filename = FindDebugBinary()) != NULL && + (tab = OpenSymbolTable(filename))) { for (unsigned i = 0; i < tab->count; ++i) { printf("%p %s\n", tab->addr_base + tab->symbols[i].addr_rva, getelfstring(tab->elf, tab->elfsize, tab->name_base, @@ -26,9 +28,9 @@ int main(int argc, char *argv[]) { } } else { perror("printmysymbols"); - backtrace(stderr); + ShowBacktrace(STDERR_FILENO, NULL); rc = 1; } - closesymboltable(&tab); + CloseSymbolTable(&tab); return rc; } diff --git a/examples/tiny-raw-linux-tutorial.S b/examples/tiny-raw-linux-tutorial.S index d8dc6e9d..81801c40 100644 --- a/examples/tiny-raw-linux-tutorial.S +++ b/examples/tiny-raw-linux-tutorial.S @@ -58,6 +58,3 @@ _start: mov $12,%rdx # arg no. 3 is length jmp 0b .endfn _start,globl .source __FILE__ - - .rodata.cst4 -1: .float -1.5 diff --git a/examples/ttyinfo.c b/examples/ttyinfo.c index bbab0dca..1b9740db 100644 --- a/examples/ttyinfo.c +++ b/examples/ttyinfo.c @@ -16,7 +16,6 @@ #include "libc/fmt/fmt.h" #include "libc/log/check.h" #include "libc/log/log.h" -#include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" @@ -27,82 +26,138 @@ #include "libc/sysv/consts/termios.h" #include "libc/x/x.h" -/* - "\e[c" → "\e[?1;2c" - "\e[x" → "\e[2;1;1;112;112;1;0x" - "\e[>c" → "\e[>83;40500;0c" - "\e[6n" → "\e[52;1R" -*/ +#define CTRL(C) ((C) ^ 0b01000000) +#define ENABLE_MOUSE_TRACKING "\e[?1000;1002;1015;1006h" +#define DISABLE_MOUSE_TRACKING "\e[?1000;1002;1015;1006l" +#define PROBE_DISPLAY_SIZE "\e7\e[9979;9979H\e[6n\e8" -#define CTRL(C) ((C) ^ 0b01000000) -#define PROBE_VT100 "\e[c" /* e.g. "\e[?1;2c", "\e[>0c" */ -#define PROBE_SECONDARY "\e[>c" /* "\e[>83;40500;0c" (Screen v4.05.00) */ -#define PROBE_PARAMETERS "\e[x" /* e.g. "\e[2;1;1;112;112;1;0x" */ -#define PROBE_CURSOR_POSITION "\e[6n" /* e.g. "\e[𝑦;𝑥R" */ -#define PROBE_SUN_DTTERM_SIZE "\e[14t" /* e.g. "\e[𝑦;𝑥R" */ +char code[512]; +struct winsize wsize; +struct termios oldterm; +volatile bool resized, killed; -int fd_; -jmp_buf jb_; -ssize_t got_; -uint8_t buf_[128]; -struct TtyIdent ti_; -struct winsize wsize_; -volatile bool resized_; - -void OnResize(void) { - resized_ = true; -} -void OnKilled(int sig) { - longjmp(jb_, 128 + sig + 1); +void onresize(void) { + resized = true; } -void getsome(void) { - if ((got_ = read(fd_, buf_, sizeof(buf_))) == -1 && errno != EINTR) { - printf("%s%s\r\n", "error: ", strerror(errno)); - longjmp(jb_, EXIT_FAILURE + 1); +void onkilled(int sig) { + killed = true; +} + +void restoretty(void) { + write(1, DISABLE_MOUSE_TRACKING, strlen(DISABLE_MOUSE_TRACKING)); + ioctl(1, TCSETS, &oldterm); +} + +int rawmode(void) { + static bool once; + struct termios t; + if (!once) { + if (ioctl(1, TCGETS, &oldterm) != -1) { + atexit(restoretty); + } else { + return -1; + } + once = true; } - if (got_ >= 0) { - printf("%`'.*s\r\n", got_, buf_); - if (got_ > 0 && buf_[0] == CTRL('C')) { - longjmp(jb_, EXIT_SUCCESS + 1); + memcpy(&t, &oldterm, sizeof(t)); + t.c_cc[VMIN] = 1; + t.c_cc[VTIME] = 1; + t.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON | + IGNBRK | BRKINT); + t.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL | ISIG); + t.c_cflag &= ~(CSIZE | PARENB); + t.c_oflag &= ~OPOST; + t.c_cflag |= CS8; + t.c_iflag |= IUTF8; + ioctl(1, TCSETS, &t); + write(1, ENABLE_MOUSE_TRACKING, strlen(ENABLE_MOUSE_TRACKING)); + write(1, PROBE_DISPLAY_SIZE, strlen(PROBE_DISPLAY_SIZE)); + return 0; +} + +void getsize(void) { + if (getttysize(1, &wsize) != -1) { + printf("termios says terminal size is %hu×%hu\r\n", wsize.ws_col, + wsize.ws_row); + } else { + printf("%s\n", strerror(errno)); + } +} + +const char *describemouseevent(int e) { + static char buf[64]; + buf[0] = 0; + if (e & 0x10) { + strcat(buf, " ctrl"); + } + if (e & 0x40) { + strcat(buf, " wheel"); + if (e & 0x01) { + strcat(buf, " down"); + } else { + strcat(buf, " up"); + } + } else { + switch (e & 3) { + case 0: + strcat(buf, " left"); + break; + case 1: + strcat(buf, " middle"); + break; + case 2: + strcat(buf, " right"); + break; + default: + unreachable; + } + if (e & 0x20) { + strcat(buf, " drag"); + } else if (e & 0x04) { + strcat(buf, " up"); + } else { + strcat(buf, " down"); } } - if (resized_) { - CHECK_NE(-1, getttysize(fd_, &wsize_)); - printf("SIGWINCH → %hu×%hu\r\n", wsize_.ws_row, wsize_.ws_col); - resized_ = false; - } -} - -void probe(const char *s) { - printf("%`'s → ", s); - write(fd_, s, strlen(s)); - getsome(); + return buf + 1; } int main(int argc, char *argv[]) { - int rc; - char ttyname[64]; - struct termios old; - CHECK_NE(-1, (fd_ = open("/dev/tty", O_RDWR))); - CHECK_NE(-1, ttyconfig(fd_, ttysetrawmode, 0, &old)); - if (!(rc = setjmp(jb_))) { - xsigaction(SIGTERM, OnKilled, 0, 0, NULL); - xsigaction(SIGWINCH, OnResize, 0, 0, NULL); - if (ttyident(&ti_, STDIN_FILENO, STDOUT_FILENO) != -1) { - ttysendtitle(fd_, "justine was here", &ti_); - fputs(ttydescribe(ttyname, sizeof(ttyname), &ti_), stdout); + int e, c, y, x, n, yn, xn; + xsigaction(SIGTERM, onkilled, 0, 0, NULL); + xsigaction(SIGWINCH, onresize, 0, 0, NULL); + xsigaction(SIGCONT, onresize, 0, 0, NULL); + rawmode(); + getsize(); + while (!killed) { + if (resized) { + printf("SIGWINCH "); + getsize(); + resized = false; + } + if ((n = readansi(0, code, sizeof(code))) == -1) { + if (errno == EINTR) continue; + printf("ERROR: READ: %s\r\n", strerror(errno)); + exit(1); + } + printf("%`'.*s ", n, code); + if (iscntrl(code[0]) && !code[1]) { + printf("is CTRL-%c a.k.a. ^%c\r\n", CTRL(code[0]), CTRL(code[0])); + if (code[0] == CTRL('C') || code[0] == CTRL('D')) break; + } else if (startswith(code, "\e[") && endswith(code, "R")) { + yn = 1, xn = 1; + sscanf(code, "\e[%d;%dR", &yn, &xn); + printf("inband signalling says terminal size is %d×%d\r\n", xn, yn); + } else if (startswith(code, "\e[<") && + (endswith(code, "m") || endswith(code, "M"))) { + e = 0, y = 1, x = 1; + sscanf(code, "\e[<%d;%d;%d%c", &e, &y, &x, &c); + printf("mouse %s at %d×%d\r\n", describemouseevent(e | (c == 'm') << 2), + x, y); + } else { + printf("\r\n"); } - fputs("\r\n", stdout); - probe(PROBE_VT100); - probe(PROBE_SECONDARY); - probe(PROBE_PARAMETERS); - probe(PROBE_CURSOR_POSITION); - /* probe(PROBE_SUN_DTTERM_SIZE); */ - getsome(); - for (;;) getsome(); } - ttyrestore(fd_, &old); - ttyidentclear(&ti_); - return rc - 1; + return 0; } diff --git a/libc/calls/calls.h b/libc/calls/calls.h index 3680cc46..213537bc 100644 --- a/libc/calls/calls.h +++ b/libc/calls/calls.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_SYSCALLS_H_ #define COSMOPOLITAN_LIBC_CALLS_SYSCALLS_H_ +#include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/timespec.h" #include "libc/calls/typedef/sighandler_t.h" #include "libc/dce.h" @@ -58,12 +59,10 @@ struct iovec; struct rlimit; struct rusage; struct sigaction; -struct siginfo; struct sigset; struct stat; struct sysinfo; struct tms; -struct ucontext; struct utsname; typedef int sig_atomic_t; @@ -210,6 +209,7 @@ size_t getfiledescriptorsize(int); ssize_t copy_file_range(int, long *, int, long *, size_t, uint32_t); ssize_t copyfd(int, int64_t *, int, int64_t *, size_t, uint32_t); ssize_t read(int, void *, size_t); +ssize_t readansi(int, char *, size_t); ssize_t readlinkat(int, const char *, char *, size_t); ssize_t splice(int, int64_t *, int, int64_t *, size_t, uint32_t); ssize_t vmsplice(int, const struct iovec *, int64_t, uint32_t); diff --git a/libc/time/getitimer.c b/libc/calls/getitimer.c similarity index 94% rename from libc/time/getitimer.c rename to libc/calls/getitimer.c index 4e5f4393..ee47d772 100644 --- a/libc/time/getitimer.c +++ b/libc/calls/getitimer.c @@ -17,9 +17,10 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" #include "libc/dce.h" #include "libc/sysv/errfuns.h" -#include "libc/time/time.h" /** * Retrieves last setitimer() value, correcting for remaining time. @@ -29,9 +30,8 @@ */ int getitimer(int which, struct itimerval *curvalue) { if (!IsWindows()) { - int getitimer$sysv(int, struct itimerval *) hidden; return getitimer$sysv(which, curvalue); } else { - return enosys(); /* TODO(jart): Implement me! */ + return setitimer$nt(which, NULL, curvalue); } } diff --git a/libc/calls/hefty/mkntcmdline.c b/libc/calls/hefty/mkntcmdline.c index 839284b6..6675056a 100644 --- a/libc/calls/hefty/mkntcmdline.c +++ b/libc/calls/hefty/mkntcmdline.c @@ -45,7 +45,7 @@ static int mkntcmdline_append(char16_t **p, size_t *i, size_t *n, char16_t c) { * Converts System V argv to Windows-style command line. * * Escaping is performed and it's designed to round-trip with - * getdosargv() or getdosargv(). This function does NOT escape + * GetDosArgv() or GetDosArgv(). This function does NOT escape * command interpreter syntax, e.g. $VAR (sh), %VAR% (cmd). * * @param argv is an a NULL-terminated array of UTF-8 strings diff --git a/libc/calls/internal.h b/libc/calls/internal.h index b2f67b0e..48876175 100644 --- a/libc/calls/internal.h +++ b/libc/calls/internal.h @@ -181,6 +181,7 @@ i64 sendfile$sysv(i32, i32, i64 *, u64) hidden; i64 splice$sysv(i32, i64 *, i32, i64 *, u64, u32) hidden; i64 vmsplice$sysv(i32, const struct iovec *, i64, u32) hidden; i64 write$sysv(i32, const void *, u64) hidden; +int getitimer$sysv(i32, struct itimerval *) hidden; int setresgid$sysv(uint32_t, uint32_t, uint32_t) hidden; int setresuid$sysv(uint32_t, uint32_t, uint32_t) hidden; u32 getgid$sysv(void) hidden; @@ -204,6 +205,7 @@ u32 prot2nt(i32, i32) privileged; void __restore_rt() hidden; void __sigenter$xnu(void *, i32, i32, void *, void *) hidden noreturn; int utimensat$xnu(int, const char *, const struct timespec *, int) hidden; +int nanosleep$xnu(const struct timespec *, struct timespec *) hidden; void stat2linux(void *) hidden; void xnutrampoline(void *, i32, i32, const struct __darwin_siginfo *, const struct __darwin_ucontext *) hidden noreturn; @@ -247,6 +249,8 @@ ssize_t read$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden; ssize_t write$nt(struct Fd *, const struct iovec *, size_t, ssize_t) hidden; int utimensat$nt(int, const char *, const struct timespec *, int) hidden; int getrusage$nt(int, struct rusage *) hidden; +int setitimer$nt(int, const struct itimerval *, struct itimerval *) hidden; +int nanosleep$nt(const struct timespec *, struct timespec *) hidden; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § syscalls » windows nt » support ─╬─│┼ @@ -257,6 +261,7 @@ void ntcontext2linux(struct ucontext *, const struct NtContext *) hidden; struct NtOverlapped *offset2overlap(int64_t, struct NtOverlapped *) hidden; bool32 ntsetprivilege(i64, const char16_t *, u32) hidden; bool32 onntconsoleevent$nt(u32) hidden; +void onntalarm(void *, uint32_t, uint32_t) hidden; int ntaccesscheck(const char16_t *, u32) paramsnonnull() hidden; i64 ntreturn(u32); i64 winerr(void) nocallback privileged; diff --git a/libc/calls/ioctl-tcsets-nt.c b/libc/calls/ioctl-tcsets-nt.c index 2a0c9e98..5bcd9e08 100644 --- a/libc/calls/ioctl-tcsets-nt.c +++ b/libc/calls/ioctl-tcsets-nt.c @@ -41,15 +41,23 @@ textwindows int ioctl$tcsets$nt(int ignored, uint64_t request, } inmode &= ~(kNtEnableLineInput | kNtEnableEchoInput | kNtEnableProcessedInput); + if (tio->c_lflag & ICANON) inmode |= kNtEnableLineInput; if (tio->c_lflag & ECHO) inmode |= kNtEnableEchoInput; if (tio->c_lflag & (IEXTEN | ISIG)) inmode |= kNtEnableProcessedInput; + inmode |= kNtEnableWindowInput; + if (NtGetVersion() >= kNtVersionWindows10) { + inmode |= kNtEnableVirtualTerminalInput; + } SetConsoleMode(in, inmode); } if (outok) { - SetConsoleMode(out, outmode | kNtEnableProcessedOutput | - (NtGetVersion() >= kNtVersionWindows10 - ? kNtEnableVirtualTerminalProcessing - : 0)); + outmode |= kNtEnableWrapAtEolOutput; + outmode |= kNtEnableProcessedOutput; + if (!(tio->c_oflag & OPOST)) outmode |= kNtDisableNewlineAutoReturn; + if (NtGetVersion() >= kNtVersionWindows10) { + outmode |= kNtEnableVirtualTerminalProcessing; + } + SetConsoleMode(out, outmode); } return 0; } else { diff --git a/libc/calls/ioctl-tiocgwinsz-nt.c b/libc/calls/ioctl-tiocgwinsz-nt.c index 5aac3c3f..129cfd4b 100644 --- a/libc/calls/ioctl-tiocgwinsz-nt.c +++ b/libc/calls/ioctl-tiocgwinsz-nt.c @@ -28,25 +28,36 @@ #include "libc/sysv/errfuns.h" textwindows int ioctl$tiocgwinsz$nt(int fd, struct winsize *ws) { + int i, fds[3]; uint32_t mode; struct NtConsoleScreenBufferInfoEx sbinfo; - if (!isfdkind(fd, kFdFile)) return ebadf(); - if (!GetConsoleMode(g_fds.p[fd].handle, &mode)) return enotty(); - memset(&sbinfo, 0, sizeof(sbinfo)); - sbinfo.cbSize = sizeof(sbinfo); - if (GetConsoleScreenBufferInfoEx(g_fds.p[fd].handle, &sbinfo)) { - ws->ws_col = sbinfo.srWindow.Right - sbinfo.srWindow.Left; - ws->ws_row = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top; - ws->ws_xpixel = 0; - ws->ws_ypixel = 0; - return 0; - } else if (g_ntstartupinfo.dwFlags & kNtStartfUsecountchars) { - ws->ws_col = g_ntstartupinfo.dwXCountChars; - ws->ws_row = g_ntstartupinfo.dwYCountChars; - ws->ws_xpixel = 0; - ws->ws_ypixel = 0; - return 0; - } else { - return winerr(); + fds[0] = fd, fds[1] = 1, fds[2] = 0; + for (i = 0; i < ARRAYLEN(fds); ++i) { + if (isfdkind(fds[i], kFdFile) || isfdkind(fds[i], kFdConsole)) { + if (GetConsoleMode(g_fds.p[fds[i]].handle, &mode)) { + memset(&sbinfo, 0, sizeof(sbinfo)); + sbinfo.cbSize = sizeof(sbinfo); + if (GetConsoleScreenBufferInfoEx(g_fds.p[fds[i]].handle, &sbinfo)) { + ws->ws_col = sbinfo.srWindow.Right - sbinfo.srWindow.Left + 1; + ws->ws_row = sbinfo.srWindow.Bottom - sbinfo.srWindow.Top + 1; + ws->ws_xpixel = 0; + ws->ws_ypixel = 0; + return 0; + } else if (g_ntstartupinfo.dwFlags & kNtStartfUsecountchars) { + ws->ws_col = g_ntstartupinfo.dwXCountChars; + ws->ws_row = g_ntstartupinfo.dwYCountChars; + ws->ws_xpixel = 0; + ws->ws_ypixel = 0; + return 0; + } else { + winerr(); + } + } else { + enotty(); + } + } else { + ebadf(); + } } + return -1; } diff --git a/libc/calls/nanosleep-nt.c b/libc/calls/nanosleep-nt.c new file mode 100644 index 00000000..c2cc7f30 --- /dev/null +++ b/libc/calls/nanosleep-nt.c @@ -0,0 +1,44 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/nt/enum/status.h" +#include "libc/nt/errors.h" +#include "libc/nt/nt/time.h" +#include "libc/nt/synchronization.h" +#include "libc/str/str.h" +#include "libc/sysv/errfuns.h" + +textwindows int nanosleep$nt(const struct timespec *req, struct timespec *rem) { + int64_t millis, hectonanos, relasleep; + if (rem) memcpy(rem, req, sizeof(*rem)); + hectonanos = req->tv_sec * 10000000ull + div100int64(req->tv_nsec); + hectonanos = MAX(1, hectonanos); + relasleep = -hectonanos; + if (NtError(NtDelayExecution(true, &relasleep))) { + millis = div10000int64(hectonanos); + millis = MAX(1, millis); + if (SleepEx(millis, true) == kNtWaitIoCompletion) { + return eintr(); + } + } + if (rem) memset(rem, 0, sizeof(*rem)); + return 0; +} diff --git a/libc/log/getsymbolname.c b/libc/calls/nanosleep-xnu.c similarity index 82% rename from libc/log/getsymbolname.c rename to libc/calls/nanosleep-xnu.c index 9d8a038e..587d4eb0 100644 --- a/libc/log/getsymbolname.c +++ b/libc/calls/nanosleep-xnu.c @@ -17,16 +17,15 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/elf/elf.h" -#include "libc/runtime/symbols.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/timeval.h" +#include "libc/macros.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/sock/internal.h" -/** - * Finds starting address of symbol. - * @param symbol table pointer (null propagating) - * @return name string or null if not found - */ -const char *getsymbolname(struct SymbolTable *t, const struct Symbol *symbol) { - return (t && symbol) - ? getelfstring(t->elf, t->elfsize, t->name_base, symbol->name_rva) - : NULL; +int nanosleep$xnu(const struct timespec *req, struct timespec *rem) { + long millis; + millis = div1000int64(req->tv_nsec); + millis = MAX(1, millis); + return select$sysv(0, 0, 0, 0, &(struct timeval){req->tv_sec, millis}); } diff --git a/libc/calls/nanosleep.c b/libc/calls/nanosleep.c index e31ecb50..b6f514ff 100644 --- a/libc/calls/nanosleep.c +++ b/libc/calls/nanosleep.c @@ -17,48 +17,23 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/internal.h" -#include "libc/calls/struct/timespec.h" -#include "libc/calls/struct/timeval.h" -#include "libc/conv/conv.h" #include "libc/dce.h" -#include "libc/macros.h" -#include "libc/nexgen32e/nexgen32e.h" -#include "libc/nt/enum/status.h" -#include "libc/nt/errors.h" -#include "libc/nt/nt/time.h" -#include "libc/nt/synchronization.h" -#include "libc/sock/internal.h" -#include "libc/str/str.h" #include "libc/sysv/errfuns.h" /** * Sleeps for a particular amount of time. */ int nanosleep(const struct timespec *req, struct timespec *rem) { - long res, millis, hectonanos; if (!req) return efault(); if (!IsWindows()) { if (!IsXnu()) { return nanosleep$sysv(req, rem); } else { - return select$sysv( - 0, 0, 0, 0, /* lool */ - &(struct timeval){req->tv_sec, div1000int64(req->tv_nsec)}); + return nanosleep$xnu(req, rem); } } else { - if (rem) memcpy(rem, req, sizeof(*rem)); - if (req->tv_sec && req->tv_nsec) { - hectonanos = MAX(1, req->tv_sec * 10000000L + div100int64(req->tv_nsec)); - } else { - hectonanos = 1; - } - if (NtError(NtDelayExecution(true, &hectonanos))) { - millis = div10000int64(hectonanos); - res = SleepEx(millis, true); - if (res == kNtWaitIoCompletion) return eintr(); - } - if (rem) memset(rem, 0, sizeof(*rem)); - return 0; + return nanosleep$nt(req, rem); } } diff --git a/libc/calls/now.c b/libc/calls/now.c index 7ea7adcf..623bf7fa 100644 --- a/libc/calls/now.c +++ b/libc/calls/now.c @@ -31,13 +31,7 @@ static struct Now { bool once; uint64_t k0; long double r0, cpn; -} now_; - -/** - * Returns timestamp without needing system calls. - * @note uses microsecond scale fallback on k8 or vm - */ -long double (*nowl)(void); +} g_now; static long double GetTimeSample(void) { uint64_t tick1, tick2; @@ -48,7 +42,7 @@ static long double GetTimeSample(void) { nanosleep(&(struct timespec){0, 100000}, NULL); time2 = dtime(CLOCK_MONOTONIC); tick2 = rdtsc(); - return (time2 - time1) * 1e9 / (tick2 - tick1); + return (time2 - time1) * 1e9 / MAX(1, tick2 - tick1); } static long double MeasureNanosPerCycle(void) { @@ -62,15 +56,15 @@ static long double MeasureNanosPerCycle(void) { } static void InitTime(void) { - now_.cpn = MeasureNanosPerCycle(); - now_.r0 = dtime(CLOCK_REALTIME); - now_.k0 = rdtsc(); - now_.once = true; + g_now.cpn = MeasureNanosPerCycle(); + g_now.r0 = dtime(CLOCK_REALTIME); + g_now.k0 = rdtsc(); + g_now.once = true; } long double converttickstonanos(uint64_t ticks) { - if (!now_.once) InitTime(); - return ticks * now_.cpn; /* pico scale */ + if (!g_now.once) InitTime(); + return ticks * g_now.cpn; /* pico scale */ } long double converttickstoseconds(uint64_t ticks) { @@ -83,15 +77,7 @@ long double nowl$sys(void) { long double nowl$art(void) { uint64_t ticks; - if (!now_.once) InitTime(); - ticks = unsignedsubtract(rdtsc(), now_.k0); - return now_.r0 + converttickstoseconds(ticks); + if (!g_now.once) InitTime(); + ticks = unsignedsubtract(rdtsc(), g_now.k0); + return g_now.r0 + converttickstoseconds(ticks); } - -INITIALIZER(301, _init_nowl, { - if (X86_HAVE(INVTSC)) { - nowl = nowl$art; - } else { - nowl = nowl$sys; - } -}) diff --git a/libc/nexgen32e/ispunct.S b/libc/calls/nowl.S similarity index 82% rename from libc/nexgen32e/ispunct.S rename to libc/calls/nowl.S index fe0d6e9a..05f1bcf7 100644 --- a/libc/nexgen32e/ispunct.S +++ b/libc/calls/nowl.S @@ -17,25 +17,23 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/nexgen32e/x86feature.h" #include "libc/macros.h" -/ Checks for printable characters that aren't space / alnum. +/ Returns timestamp without needing system calls. / -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -ispunct:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - mov (%rcx,%rdi),%dl - test $16,%dl - je 1f - xor %eax,%eax - and $7,%dl - sete %al -1: .leafepilogue - .endfn ispunct,globl +/ @return seconds since unix epoch in %st0 +/ @note uses microsecond scale fallback on k8 or vm + .initbss 202,_init_nowl +nowl: .quad 0 + .endobj nowl,globl + .previous + + .init.start 202,_init_nowl + ezlea nowl$sys,ax + ezlea nowl$art,cx + testb X86_HAVE(INVTSC)+kCpuids(%rip) + cmovnz %rcx,%rax + stosq + .init.end 202,_init_nowl .source __FILE__ diff --git a/libc/calls/onntalarm.c b/libc/calls/onntalarm.c new file mode 100644 index 00000000..a8b19efc --- /dev/null +++ b/libc/calls/onntalarm.c @@ -0,0 +1,31 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/internal.h" +#include "libc/calls/struct/siginfo.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/sig.h" + +void onntalarm(void *lpArgToCompletionRoutine, uint32_t dwTimerLowValue, + uint32_t dwTimerHighValue) { + siginfo_t info; + memset(&info, 0, sizeof(info)); + info.si_signo = SIGALRM; + __sigenter(info.si_signo, &info, NULL); +} diff --git a/libc/calls/onntconsoleevent.c b/libc/calls/onntconsoleevent.c index d64cdcc7..d34d5973 100644 --- a/libc/calls/onntconsoleevent.c +++ b/libc/calls/onntconsoleevent.c @@ -37,9 +37,9 @@ textwindows bool32 onntconsoleevent(uint32_t CtrlType) { case kNtCtrlCloseEvent: sig = pushpop(SIGHUP); break; - case kNtCtrlLogoffEvent: - case kNtCtrlShutdownEvent: - sig = pushpop(SIGTERM); + case kNtCtrlLogoffEvent: // only received by services so hack hack hack + case kNtCtrlShutdownEvent: // only received by services so hack hack hack + sig = pushpop(SIGALRM); break; default: return false; diff --git a/libc/calls/pread.c b/libc/calls/pread.c index b4b10f7f..a9101e07 100644 --- a/libc/calls/pread.c +++ b/libc/calls/pread.c @@ -21,6 +21,7 @@ #include "libc/bits/weaken.h" #include "libc/calls/calls.h" #include "libc/calls/internal.h" +#include "libc/calls/struct/iovec.h" #include "libc/dce.h" #include "libc/macros.h" #include "libc/runtime/runtime.h" @@ -47,7 +48,7 @@ ssize_t pread(int fd, void *buf, size_t size, int64_t offset) { } else if (!IsWindows()) { rc = pread$sysv(fd, buf, size, offset); } else if (isfdkind(fd, kFdFile)) { - rc = read$nt(&g_fds.p[fd], buf, size, offset); + rc = read$nt(&g_fds.p[fd], (struct iovec[]){{buf, size}}, 1, offset); } else { rc = ebadf(); } diff --git a/libc/calls/preadv.c b/libc/calls/preadv.c index a6366078..eb0f1b7d 100644 --- a/libc/calls/preadv.c +++ b/libc/calls/preadv.c @@ -48,7 +48,7 @@ ssize_t preadv(int fd, struct iovec *iovec, int count, int64_t off) { */ if (!once) { once = true; - if (IsLinux() && iovec->iov_len >= __NR_preadv_linux) { + if (IsModeDbg() || (IsLinux() && iovec->iov_len >= __NR_preadv_linux)) { /* * Read size is too large to detect older kernels safely without * introducing nontrivial mechanics. We'll try again later. diff --git a/libc/calls/pwritev.c b/libc/calls/pwritev.c index 8617a553..93efb84a 100644 --- a/libc/calls/pwritev.c +++ b/libc/calls/pwritev.c @@ -29,7 +29,12 @@ #define __NR_pwritev_linux 0x0128 /** - * Writes data from multiple buffers to file descriptor at offset. + * Writes data from multiple buffers to offset. + * + * Please note that it's not an error for a short write to happen. This + * can happen in the kernel if EINTR happens after some of the write has + * been committed. It can also happen if we need to polyfill this system + * call using pwrite(). * * @param count is recommended to be 16 or fewer; if it exceeds IOV_MAX * then the extra buffers are simply ignored @@ -48,7 +53,7 @@ ssize_t pwritev(int fd, const struct iovec *iovec, int count, int64_t off) { */ if (!once) { once = true; - if (IsLinux() && iovec->iov_len >= __NR_pwritev_linux) { + if (IsModeDbg() || (IsLinux() && iovec->iov_len >= __NR_pwritev_linux)) { /* * Write size is too large to detect older kernels safely without * introducing nontrivial mechanics. We'll try again later. diff --git a/libc/calls/readansi.c b/libc/calls/readansi.c new file mode 100644 index 00000000..2f16a63d --- /dev/null +++ b/libc/calls/readansi.c @@ -0,0 +1,124 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/str/thompike.h" +#include "libc/sysv/errfuns.h" + +/** + * Reads single keystroke or control sequence from character device. + * + * When reading ANSI UTF-8 text streams, characters and control codes + * are oftentimes encoded as multi-byte sequences. This function knows + * how long each sequence is, so that each read consumes a single thing + * from the underlying file descriptor, e.g. + * + * "a" ALFA + * "\316\261" ALPHA + * "\033[A" CURSOR UP + * "\033[38;5;202m" ORANGERED + * "\eOP" PF1 + * + * This routine generalizes to ascii, utf-8, chorded modifier keys, + * function keys, color codes, c0/c1 control codes, cursor movement, + * mouse movement, etc. + * + * Userspace buffering isn't required, since ANSI escape sequences and + * UTF-8 are decoded without peeking. Noncanonical overlong encodings + * can cause the stream to go out of sync. This function recovers such + * events by ignoring continuation bytes at the beginning of each read. + * + * String control sequences, e.g. "\e_hello\e\\" currently are not + * tokenized as a single read. Lastly note, this function has limited + * support for UNICODE representations of C0/C1 control codes, e.g. + * + * "\000" NUL + * "\300\200" NUL + * "\302\233A" CURSOR UP + * + * @param buf is guaranteed to receive a NUL terminator if size>0 + * @return number of bytes read (helps differentiate "\0" vs. "") + * @see examples/ttyinfo.c + * @see ANSI X3.64-1979 + * @see ISO/IEC 6429 + * @see FIPS-86 + * @see ECMA-48 + */ +ssize_t readansi(int fd, char *buf, size_t size) { + int i, j; + uint8_t c; + enum { kAscii, kUtf8, kEsc, kCsi, kSs } t; + if (size) buf[0] = 0; + for (j = i = 0, t = kAscii;;) { + if (i + 2 >= size) return enomem(); + if (read(fd, &c, 1) != 1) return -1; + buf[i++] = c; + buf[i] = 0; + switch (t) { + case kAscii: + if (c < 0200) { + if (c == '\e') { + t = kEsc; + } else { + return i; + } + } else if (c >= 0300) { + t = kUtf8; + j = ThomPikeLen(c) - 1; + } + break; + case kUtf8: + if (!--j) return i; + break; + case kEsc: + switch (c) { + case '[': + t = kCsi; + break; + case 'N': + case 'O': + t = kSs; + break; + case 0x20 ... 0x2F: + break; + default: + return i; + } + break; + case kCsi: + switch (c) { + case ':': + case ';': + case '<': + case '=': + case '>': + case '?': + case '0' ... '9': + break; + default: + return i; + } + break; + case kSs: + return i; + default: + unreachable; + } + } +} diff --git a/libc/calls/setitimer-nt.c b/libc/calls/setitimer-nt.c new file mode 100644 index 00000000..9ddbd269 --- /dev/null +++ b/libc/calls/setitimer-nt.c @@ -0,0 +1,104 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" +#include "libc/calls/internal.h" +#include "libc/calls/struct/itimerval.h" +#include "libc/conv/conv.h" +#include "libc/errno.h" +#include "libc/log/check.h" +#include "libc/nexgen32e/nexgen32e.h" +#include "libc/nt/files.h" +#include "libc/nt/runtime.h" +#include "libc/nt/synchronization.h" +#include "libc/nt/thread.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/itimer.h" +#include "libc/sysv/errfuns.h" + +/** + * @fileoverview Heartbreaking polyfill for SIGALRM on NT. + * + * Threads are used to trigger the SIGALRM handler, which should + * hopefully be an unfancy function like this: + * + * void OnAlarm(int sig, struct siginfo *si, struct ucontext *uc) { + * g_alarmed = true; + * } + * + * This is needed because WIN32 provides no obvious solutions for + * interrupting i/o operations on the standard input handle. + */ + +static struct ItimerNt { + int64_t ith; + uint32_t tid; + struct itimerval itv; +} g_itimernt; + +static uint32_t ItimerWorker(void *arg) { + do { + if (!WaitForSingleObject(g_itimernt.ith, -1)) { + onntalarm(NULL, 0, 0); + } + } while (g_itimernt.ith && g_itimernt.tid == GetCurrentThreadId()); + return 0; +} + +textwindows int setitimer$nt(int which, const struct itimerval *newvalue, + struct itimerval *out_opt_oldvalue) { + int32_t period; + int64_t ith, duetime; + if (which != ITIMER_REAL) return einval(); + if (newvalue) { + if (newvalue->it_value.tv_sec && newvalue->it_value.tv_usec) { + if (!(ith = CreateWaitableTimer(NULL, false, NULL))) { + return winerr(); + } + duetime = -(newvalue->it_value.tv_sec * HECTONANOSECONDS + + newvalue->it_value.tv_usec * 10); + period = newvalue->it_value.tv_sec * 1000 + + div1000int64(newvalue->it_value.tv_usec); + if (!period && newvalue->it_value.tv_usec) period = 1; + if (!SetWaitableTimer(ith, &duetime, period, NULL, NULL, false)) { + errno = GetLastError(); + CloseHandle(ith); + return -1; + } + } else { + ith = 0; + } + if (g_itimernt.ith) { + CloseHandle(g_itimernt.ith); + g_itimernt.ith = 0; + } + } else { + ith = 0; + } + if (out_opt_oldvalue) { + memcpy(out_opt_oldvalue, &g_itimernt.itv, sizeof(struct itimerval)); + } + if (ith) { + g_itimernt.ith = ith; + memcpy(&g_itimernt.itv, newvalue, sizeof(struct itimerval)); + CloseHandle( + CreateThread(NULL, STACKSIZE, ItimerWorker, NULL, 0, &g_itimernt.tid)); + } + return 0; +} diff --git a/libc/time/setitimer.c b/libc/calls/setitimer.c similarity index 75% rename from libc/time/setitimer.c rename to libc/calls/setitimer.c index 395d4460..4cf00541 100644 --- a/libc/time/setitimer.c +++ b/libc/calls/setitimer.c @@ -24,7 +24,18 @@ #include "libc/time/time.h" /** - * Schedules delivery of one-shot or intermittent wakeup signal, e.g. + * Schedules delivery of one-shot or intermittent interrupt signal, e.g. + * + * Raise SIGALRM every 1.5s: + * + * CHECK_NE(-1, sigaction(SIGALRM, + * &(struct sigaction){.sa_sigaction = missingno}, + * NULL)); + * CHECK_NE(-1, setitimer(ITIMER_REAL, + * &(const struct itimerval){{1, 500000}, {1, 500000}}, + * NULL)); + * + * Set single-shot 50ms timer callback to interrupt laggy connect(): * * CHECK_NE(-1, sigaction(SIGALRM, * &(struct sigaction){.sa_sigaction = missingno, @@ -34,21 +45,29 @@ * &(const struct itimerval){{0, 0}, {0, 50000}}, * NULL)); * if (connect(...) == -1 && errno == EINTR) { ... } - * CHECK_NE(-1, setitimer(ITIMER_REAL, - * &(const struct itimerval){{0, 0}, {0, 0}}, - * NULL)); + * + * Disarm timer: + * + * CHECK_NE(-1, setitimer(ITIMER_REAL, &(const struct itimerval){0}, NULL)); + * + * Be sure to check for EINTR on your i/o calls, for best low latency. * * @param which can be ITIMER_REAL, ITIMER_VIRTUAL, etc. * @param newvalue specifies the interval ({0,0} means one-shot) and - * duration ({0,0} means disarm) in microseconds + * duration ({0,0} means disarm) in microseconds ∈ [0,999999] and + * if this parameter is NULL, we'll polyfill getitimer() behavior * @param out_opt_old may receive remainder of previous op (if any) * @return 0 on success or -1 w/ errno */ int setitimer(int which, const struct itimerval *newvalue, struct itimerval *out_opt_oldvalue) { if (!IsWindows()) { - return setitimer$sysv(which, newvalue, out_opt_oldvalue); + if (newvalue) { + return setitimer$sysv(which, newvalue, out_opt_oldvalue); + } else { + return getitimer$sysv(which, out_opt_oldvalue); + } } else { - return enosys(); /* TODO(jart): Implement me! */ + return setitimer$nt(which, newvalue, out_opt_oldvalue); } } diff --git a/libc/calls/struct/stat.h b/libc/calls/struct/stat.h index 98e246f7..7a4bb64c 100644 --- a/libc/calls/struct/stat.h +++ b/libc/calls/struct/stat.h @@ -3,7 +3,7 @@ #include "libc/calls/struct/timespec.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct stat { +struct stat { /* linux abi */ int64_t st_dev; /* 0: id of device with file */ int64_t st_ino; /* 8: inode number in disk b-tree */ int64_t st_nlink; /* 16: hard link count */ diff --git a/libc/stubs/unprovable.S b/libc/calls/thunks/onntalarm.S similarity index 93% rename from libc/stubs/unprovable.S rename to libc/calls/thunks/onntalarm.S index 9ef9507f..2018eddd 100644 --- a/libc/stubs/unprovable.S +++ b/libc/calls/thunks/onntalarm.S @@ -18,14 +18,10 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.h" -.real +.text.windows .source __FILE__ -.code16 # ∩ .code32 ∩ .code64 -__unprovable: - push %bp - mov %sp,%bp - .softicebp - pop %bp - ret - .endfn __unprovable,globl,hidden +onntalarm$nt: + ezlea onntalarm,ax + jmp nt2sysv + .endfn onntalarm$nt,globl,hidden diff --git a/libc/calls/typedef/sigaction_f.h b/libc/calls/typedef/sigaction_f.h index 2e955e9d..34ec6353 100644 --- a/libc/calls/typedef/sigaction_f.h +++ b/libc/calls/typedef/sigaction_f.h @@ -1,10 +1,9 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_TYPEDEF_SIGACTION_F_H_ #define COSMOPOLITAN_LIBC_CALLS_TYPEDEF_SIGACTION_F_H_ +#include "libc/calls/struct/siginfo.h" +#include "libc/calls/ucontext.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) -struct siginfo; -struct ucontext; - typedef void (*sigaction_f)(int, struct siginfo *, struct ucontext *); #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/libc/calls/ucontext.h b/libc/calls/ucontext.h index 2af25a1e..911a6ccc 100644 --- a/libc/calls/ucontext.h +++ b/libc/calls/ucontext.h @@ -1,6 +1,5 @@ #ifndef COSMOPOLITAN_LIBC_CALLS_UCONTEXT_H_ #define COSMOPOLITAN_LIBC_CALLS_UCONTEXT_H_ -#include "libc/calls/calls.h" #include "libc/calls/struct/sigaltstack.h" #include "libc/calls/struct/sigset.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) diff --git a/libc/calls/writev.c b/libc/calls/writev.c index 1a61dd84..3a0a2336 100644 --- a/libc/calls/writev.c +++ b/libc/calls/writev.c @@ -27,6 +27,11 @@ /** * Writes data from multiple buffers. * + * Please note that it's not an error for a short write to happen. This + * can happen in the kernel if EINTR happens after some of the write has + * been committed. It can also happen if we need to polyfill this system + * call using write(). + * * @return number of bytes actually handed off, or -1 w/ errno */ ssize_t writev(int fd, const struct iovec *iov, int iovlen) { diff --git a/libc/conv/conv.h b/libc/conv/conv.h index 4ac49914..4e9306d8 100644 --- a/libc/conv/conv.h +++ b/libc/conv/conv.h @@ -27,9 +27,9 @@ long long llabs(long long) libcesque pureconst; char *ltpcpy(char *, long) paramsnonnull() libcesque nocallback; int llog10(unsigned long) libcesque pureconst; int unsleb128(const void *, size_t, int64_t *); -int atoi(const char *) paramsnonnull() libcesque nosideeffect; -long atol(const char *) paramsnonnull() libcesque nosideeffect; -long long atoll(const char *) paramsnonnull() libcesque nosideeffect; +int atoi(const char *) paramsnonnull() libcesque; +long atol(const char *) paramsnonnull() libcesque; +long long atoll(const char *) paramsnonnull() libcesque; unsigned long strtoul(const char *, char **, int) paramsnonnull((1)); long long strtoll(const char *, char **, int) paramsnonnull((1)); unsigned long long strtoull(const char *, char **, int) paramsnonnull((1)); @@ -39,8 +39,7 @@ intmax_t strtoimax(const char *, char **, int) paramsnonnull((1)); uintmax_t strtoumax(const char *, char **, int) paramsnonnull((1)); intmax_t wcstoimax(const wchar_t *, wchar_t **, int); long wcstol(const wchar_t *, wchar_t **, int); -long strtol(const char *, char **, int) - paramsnonnull((1)) libcesque nosideeffect; +long strtol(const char *, char **, int) paramsnonnull((1)) libcesque; /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § conversion » time ─╬─│┼ diff --git a/libc/conv/conv.mk b/libc/conv/conv.mk index 062cf03c..6d7f9f60 100644 --- a/libc/conv/conv.mk +++ b/libc/conv/conv.mk @@ -35,8 +35,9 @@ LIBC_CONV_A_CHECKS = \ $(LIBC_CONV_A_HDRS:%=o/$(MODE)/%.ok) LIBC_CONV_A_DIRECTDEPS = \ - LIBC_STUBS \ LIBC_NEXGEN32E \ + LIBC_STUBS \ + LIBC_STR \ LIBC_TINYMATH \ LIBC_SYSV \ THIRD_PARTY_COMPILER_RT diff --git a/libc/fmt/palandprintf.c b/libc/fmt/palandprintf.c index 47e09e1a..dd0012aa 100644 --- a/libc/fmt/palandprintf.c +++ b/libc/fmt/palandprintf.c @@ -92,14 +92,21 @@ static int ppatoi(const char **str) { * * Precision Modifiers * - * - `%.8s` supplied character length (ignore nul terminator) - * - `%.*s` supplied character length argument (ignore nul terminator) + * - `%.8s` supplied byte length (obeys nul terminator) + * - `%.*s` supplied byte length argument (obeys nul terminator) + * - `%`.*s` supplied byte length argument c escaped (ignores nul terminator) + * - `%#.*s` supplied byte length argument visualized (ignores nul terminator) + * - `%.*hs` supplied char16_t length argument (obeys nul terminator) + * - `%.*ls` supplied wchar_t length argument (obeys nul terminator) * * Formatting Modifiers * * - `%,d` thousands separators * - `%'s` escaped c string literal - * - `%'c` escaped c character literal + * - `%`c` c escaped character + * - `%`'c` c escaped character quoted + * - `%`s` c escaped string + * - `%`'s` c escaped string quoted * - `%`s` escaped double quoted c string literal * - `%`c` escaped double quoted c character literal * - `%+d` plus leftpad if positive (aligns w/ negatives) @@ -114,7 +121,7 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) { void *p; char qchar; long double ldbl; - wchar_t charbuf[3]; + wchar_t charbuf[1]; const char *alphabet; int (*out)(long, void *); unsigned char signbit, log2base; @@ -281,14 +288,15 @@ hidden int palandprintf(void *fn, void *arg, const char *format, va_list va) { break; case 'c': + precision = 1; + flags |= FLAGS_PRECISION; qchar = '\''; p = charbuf; - charbuf[0] = va_arg(va, int); - charbuf[1] = L'\0'; + charbuf[0] = va_arg(va, int); /* assume little endian */ goto showstr; case 'm': - p = weaken(strerror)(lasterr); + p = weaken(strerror) ? weaken(strerror)(lasterr) : "?"; signbit = 0; goto showstr; diff --git a/libc/fmt/stoa.c b/libc/fmt/stoa.c index 3de67697..1cd3b627 100644 --- a/libc/fmt/stoa.c +++ b/libc/fmt/stoa.c @@ -44,7 +44,7 @@ static int StoaEmitWordEncodedString(int f(long, void *), void *a, uint64_t w) { } static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) { - if (0 <= c && c <= 127) { + if (isascii(c)) { return f(c, a); } else { return StoaEmitWordEncodedString(f, a, tpenc(c)); @@ -52,7 +52,7 @@ static int StoaEmitUnicode(int f(long, void *), void *a, wint_t c) { } static int StoaEmitQuoted(int f(long, void *), void *a, wint_t c) { - if (0 <= c && c <= 127) { + if (isascii(c)) { return StoaEmitWordEncodedString(f, a, cescapec(c)); } else { return StoaEmitWordEncodedString(f, a, tpenc(c)); @@ -92,8 +92,8 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, wint_t wc; unsigned n; emit_f emit; - bool justdobytes; unsigned w, c, pad; + bool justdobytes, ignorenul; p = data; if (!p) { @@ -105,13 +105,35 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, if (StoaEmitQuote(out, arg, flags, qchar, signbit) == -1) return -1; } - if (!(flags & FLAGS_PRECISION)) { - if (signbit == 63) { - precision = tinywcsnlen((const wchar_t *)p, -1); - } else if (signbit == 15) { - precision = tinystrnlen16((const char16_t *)p, -1); + ignorenul = false; + justdobytes = false; + if (signbit == 15 || signbit == 63) { + if (flags & FLAGS_QUOTE) { + emit = StoaEmitQuoted; + ignorenul = flags & FLAGS_PRECISION; } else { - precision = strlen(p); + emit = StoaEmitUnicode; + } + } else if ((flags & FLAGS_HASH) && weaken(kCp437)) { + justdobytes = true; + emit = StoaEmitVisualized; + ignorenul = flags & FLAGS_PRECISION; + } else if (flags & FLAGS_QUOTE) { + emit = StoaEmitQuoted; + ignorenul = flags & FLAGS_PRECISION; + } else { + justdobytes = true; + emit = StoaEmitByte; + } + + if (!(flags & FLAGS_PRECISION)) precision = -1; + if (!(flags & FLAGS_PRECISION) || !ignorenul) { + if (signbit == 63) { + precision = tinywcsnlen((const wchar_t *)p, precision); + } else if (signbit == 15) { + precision = tinystrnlen16((const char16_t *)p, precision); + } else { + precision = strnlen(p, precision); } } @@ -138,32 +160,17 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, if (spacepad(out, arg, pad) == -1) return -1; } - justdobytes = false; - if (signbit == 15 || signbit == 63) { - if (flags & FLAGS_QUOTE) { - emit = StoaEmitQuoted; - } else { - emit = StoaEmitUnicode; - } - } else if ((flags & FLAGS_HASH) && weaken(kCp437)) { - justdobytes = true; - emit = StoaEmitVisualized; - } else if (flags & FLAGS_QUOTE) { - emit = StoaEmitQuoted; - } else { - justdobytes = true; - emit = StoaEmitByte; - } - if (justdobytes) { while (precision--) { wc = *p++ & 0xff; + if (!wc && !ignorenul) break; if (emit(out, arg, wc) == -1) return -1; } } else { while (precision--) { if (signbit == 15) { wc = *(const char16_t *)p; + if (!wc && !ignorenul) break; if (IsUcs2(wc)) { p += sizeof(char16_t); } else if (IsUtf16Cont(wc)) { @@ -177,10 +184,12 @@ int stoa(int out(long, void *), void *arg, void *data, unsigned long flags, } } else if (signbit == 63) { wc = *(const wint_t *)p; + if (!wc && !ignorenul) break; p += sizeof(wint_t); if (!wc) break; } else { wc = *p++ & 0xff; + if (!wc && !ignorenul) break; if (!isascii(wc)) { if (ThomPikeCont(wc)) continue; n = ThomPikeLen(wc) - 1; diff --git a/libc/intrin/pcmpgtb.c b/libc/intrin/pcmpgtb.c index 3dd7c3ae..2f37d3ec 100644 --- a/libc/intrin/pcmpgtb.c +++ b/libc/intrin/pcmpgtb.c @@ -23,6 +23,8 @@ /** * Compares signed 8-bit integers w/ greater than predicate. * + * Note that operands can be xor'd with 0x80 for unsigned compares. + * * @param 𝑎 [w/o] receives result * @param 𝑏 [r/o] supplies first input vector * @param 𝑐 [r/o] supplies second input vector diff --git a/libc/intrin/pmovmskb.c b/libc/intrin/pmovmskb.c index 5dcee3f0..4baf4794 100644 --- a/libc/intrin/pmovmskb.c +++ b/libc/intrin/pmovmskb.c @@ -19,6 +19,12 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/pmovmskb.h" +/** + * Turns result of byte comparison into bitmask. + * + * @param 𝑝 is byte vector to crunch + * @see pcmpeqb(), bsf(), etc. + */ uint32_t(pmovmskb)(const uint8_t p[16]) { uint32_t i, m; for (m = i = 0; i < 16; ++i) { diff --git a/libc/intrin/pshufd.c b/libc/intrin/pshufd.c index f2178195..d2b5fcdb 100644 --- a/libc/intrin/pshufd.c +++ b/libc/intrin/pshufd.c @@ -18,6 +18,7 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/intrin/pshufd.h" +#include "libc/str/str.h" /** * Shuffles int vector. @@ -30,8 +31,5 @@ void(pshufd)(int32_t b[4], const int32_t a[4], uint8_t m) { t[1] = a[(m & 0b00001100) >> 2]; t[2] = a[(m & 0b00110000) >> 4]; t[3] = a[(m & 0b11000000) >> 6]; - b[0] = t[0]; - b[1] = t[1]; - b[2] = t[2]; - b[3] = t[3]; + memcpy(b, t, 16); } diff --git a/libc/intrin/psubb.c b/libc/intrin/psubb.c index 1f02b6f5..4bf95158 100644 --- a/libc/intrin/psubb.c +++ b/libc/intrin/psubb.c @@ -28,9 +28,9 @@ * @param 𝑐 [r/o] supplies second input vector * @mayalias */ -void(psubb)(int8_t a[16], const int8_t b[16], const int8_t c[16]) { +void(psubb)(uint8_t a[16], const uint8_t b[16], const uint8_t c[16]) { unsigned i; - int8_t r[16]; + uint8_t r[16]; for (i = 0; i < 16; ++i) { r[i] = b[i] - c[i]; } diff --git a/libc/intrin/psubb.h b/libc/intrin/psubb.h index fd6e0f46..37b04263 100644 --- a/libc/intrin/psubb.h +++ b/libc/intrin/psubb.h @@ -4,7 +4,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -void psubb(int8_t[16], const int8_t[16], const int8_t[16]); +void psubb(uint8_t[16], const uint8_t[16], const uint8_t[16]); #define psubb(A, B, C) \ INTRIN_SSEVEX_X_X_X_(psubb, SSE2, "psubb", INTRIN_NONCOMMUTATIVE, A, B, C) diff --git a/libc/isystem/windows.h b/libc/isystem/windows.h index 9d58b213..2219bd37 100644 --- a/libc/isystem/windows.h +++ b/libc/isystem/windows.h @@ -37,6 +37,7 @@ #define PVOID void* #define PVOID64 void* +#define LPCVOID const void* #define CHAR char #define SHORT short #define CONST const diff --git a/libc/linux/close.h b/libc/linux/close.h new file mode 100644 index 00000000..a517c9ec --- /dev/null +++ b/libc/linux/close.h @@ -0,0 +1,12 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_CLOSE_H_ +#define COSMOPOLITAN_LIBC_LINUX_CLOSE_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxClose(long fd) { + long rc; + asm volatile("syscall" : "=a"(rc) : "0"(3), "D"(fd) : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_CLOSE_H_ */ diff --git a/libc/linux/exit.h b/libc/linux/exit.h index 655af633..e5479f2e 100644 --- a/libc/linux/exit.h +++ b/libc/linux/exit.h @@ -3,7 +3,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -forceinline noreturn long LinuxExit(int rc) { +forceinline noreturn long LinuxExit(long rc) { asm volatile("syscall" : /* no outputs */ : "a"(0xE7), "D"(rc) diff --git a/libc/linux/fstat.h b/libc/linux/fstat.h new file mode 100644 index 00000000..40b4578f --- /dev/null +++ b/libc/linux/fstat.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_FSTAT_H_ +#define COSMOPOLITAN_LIBC_LINUX_FSTAT_H_ +#include "libc/calls/struct/stat.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxFstat(long fd, struct stat *st) { + long rc; + asm volatile("syscall" + : "=a"(rc) + : "0"(5), "D"(fd), "S"(st) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_FSTAT_H_ */ diff --git a/libc/linux/mmap.h b/libc/linux/mmap.h new file mode 100644 index 00000000..024a72bd --- /dev/null +++ b/libc/linux/mmap.h @@ -0,0 +1,20 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_MMAP_H_ +#define COSMOPOLITAN_LIBC_LINUX_MMAP_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxMmap(void *addr, size_t size, long prot, long flags, + long fd, long off) { + long rc; + register long r10 asm("r10") = flags; + register long r8 asm("r8") = fd; + register long r9 asm("r9") = off; + asm volatile("syscall" + : "=a"(rc) + : "0"(9), "D"(addr), "S"(size), "d"(prot), "r"(r10), "r"(r8), + "r"(r9) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_MMAP_H_ */ diff --git a/libc/linux/munmap.h b/libc/linux/munmap.h new file mode 100644 index 00000000..e9515ea5 --- /dev/null +++ b/libc/linux/munmap.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_MUNMAP_H_ +#define COSMOPOLITAN_LIBC_LINUX_MUNMAP_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxMunmap(void *addr, size_t size) { + long rc; + asm volatile("syscall" + : "=a"(rc) + : "0"(0xb), "D"(addr), "S"(size) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_MUNMAP_H_ */ diff --git a/libc/linux/open.h b/libc/linux/open.h new file mode 100644 index 00000000..919836b9 --- /dev/null +++ b/libc/linux/open.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_LINUX_OPEN_H_ +#define COSMOPOLITAN_LIBC_LINUX_OPEN_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) + +forceinline long LinuxOpen(const char *path, long flags, long mode) { + long rc; + asm volatile("syscall" + : "=a"(rc) + : "0"(2), "D"(path), "S"(flags), "d"(mode) + : "rcx", "r11", "memory"); + return rc; +} + +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_LINUX_OPEN_H_ */ diff --git a/libc/linux/read.h b/libc/linux/read.h index b3fae18a..67319f48 100644 --- a/libc/linux/read.h +++ b/libc/linux/read.h @@ -2,7 +2,7 @@ #define COSMOPOLITAN_LIBC_LINUX_READ_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) -forceinline long LinuxRead(int fd, void *data, unsigned long size) { +forceinline long LinuxRead(long fd, void *data, unsigned long size) { long rc; asm volatile("syscall" : "=a"(rc), "=m"(*(char(*)[size])data) diff --git a/libc/linux/write.h b/libc/linux/write.h index 413f1a29..2d904978 100644 --- a/libc/linux/write.h +++ b/libc/linux/write.h @@ -1,9 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_LINUX_WRITE_H_ #define COSMOPOLITAN_LIBC_LINUX_WRITE_H_ #if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ -forceinline long LinuxWrite(int fd, const void *data, unsigned long size) { +forceinline long LinuxWrite(long fd, const void *data, unsigned long size) { long rc; asm volatile("syscall" : "=a"(rc) @@ -13,6 +12,5 @@ forceinline long LinuxWrite(int fd, const void *data, unsigned long size) { return rc; } -COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_LINUX_WRITE_H_ */ diff --git a/libc/log/asan.c b/libc/log/asan.c index 7b923d62..03d665c2 100644 --- a/libc/log/asan.c +++ b/libc/log/asan.c @@ -17,25 +17,14 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/bits/safemacros.h" #include "libc/bits/weaken.h" #include "libc/calls/calls.h" -#include "libc/conv/conv.h" #include "libc/conv/itoa.h" #include "libc/log/asan.h" #include "libc/log/backtrace.h" -#include "libc/log/log.h" -#include "libc/macros.h" #include "libc/mem/hook/hook.h" #include "libc/runtime/directmap.h" -#include "libc/runtime/internal.h" #include "libc/runtime/memtrack.h" -#include "libc/runtime/missioncritical.h" -#include "libc/runtime/runtime.h" -#include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/prot.h" @@ -154,9 +143,9 @@ static const char *__asan_describe_access_poison(int c) { } static noreturn void __asan_die(const char *msg, size_t size) { - __print(msg, size); - PrintBacktraceUsingSymbols(stderr, __builtin_frame_address(0), - getsymboltable()); + write(STDERR_FILENO, msg, size); + PrintBacktraceUsingSymbols(STDERR_FILENO, __builtin_frame_address(0), + GetSymbolTable()); DebugBreak(); _Exit(66); } @@ -170,7 +159,7 @@ static noreturn void __asan_report_deallocate_fault(void *addr, int c) { p = mempcpy(p, ibuf, int64toarray_radix10(c, ibuf)); p = stpcpy(p, " at 0x"); p = mempcpy(p, ibuf, uint64toarray_fixed16((intptr_t)addr, ibuf, 48)); - p = stpcpy(p, "\n"); + p = stpcpy(p, "\r\n"); __asan_die(buf, p - buf); } @@ -186,7 +175,7 @@ static noreturn void __asan_report_memory_fault(uint8_t *addr, int size, p = stpcpy(p, kind); p = stpcpy(p, " at 0x"); p = mempcpy(p, ibuf, uint64toarray_fixed16((intptr_t)addr, ibuf, 48)); - p = stpcpy(p, "\n"); + p = stpcpy(p, "\r\n"); __asan_die(buf, p - buf); } @@ -209,13 +198,6 @@ static void __asan_morgue_flush(void) { } } -static bool __asan_is_mapped(void *p) { - int x, i; - x = (intptr_t)p >> 16; - i = FindMemoryInterval(&_mmi, x); - return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; -} - static void *__asan_allocate(size_t align, size_t size, int underrun, int overrun) { char *p, *s; @@ -394,44 +376,62 @@ void __asan_install_malloc_hooks(void) { HOOK(hook$malloc_usable_size, __asan_malloc_usable_size); } -void __asan_map_shadow(void *addr, size_t size) { - int i, n, x; - char *a, *b; +static bool __asan_is_mapped(int x) { + int i = FindMemoryInterval(&_mmi, x); + return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; +} + +void __asan_map_shadow(void *p, size_t n) { + int i, x, a, b; struct DirectMap sm; - a = (char *)ROUNDDOWN(SHADOW((intptr_t)addr), FRAMESIZE); - b = (char *)ROUNDDOWN(SHADOW((intptr_t)addr + size - 1), FRAMESIZE); - for (; a <= b; a += FRAMESIZE) { + a = SHADOW((uintptr_t)p) >> 16; + b = ROUNDUP(SHADOW(ROUNDUP((uintptr_t)p + n, 8)), 1 << 16) >> 16; + for (; a < b; ++a) { if (!__asan_is_mapped(a)) { - sm = DirectMap(a, FRAMESIZE, PROT_READ | PROT_WRITE, + sm = DirectMap((void *)((uintptr_t)a << 16), 1 << 16, + PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if (sm.addr == MAP_FAILED || - TrackMemoryInterval(&_mmi, (intptr_t)a >> 16, (intptr_t)a >> 16, - sm.maphandle) == -1) { + TrackMemoryInterval(&_mmi, a, a, sm.maphandle) == -1) { abort(); } } } } -textstartup void __asan_init(int argc, char *argv[], char **envp, - intptr_t *auxv) { - int i; - static bool once; - register intptr_t rsp asm("rsp"); - if (!once) { - __asan_map_shadow(_base, _end - _base); - __asan_map_shadow((void *)ROUNDDOWN(rsp, STACKSIZE), STACKSIZE); - for (i = 0; i < argc; ++i) __asan_map_shadow(argv[i], strlen(argv[i])); - for (; *envp; ++envp) __asan_map_shadow(*envp, strlen(*envp)); - __asan_map_shadow(auxv, sizeof(intptr_t) * 2); - __asan_install_malloc_hooks(); - once = true; +static char *__asan_get_stack_base(void) { + register uintptr_t rsp asm("rsp"); + return (char *)ROUNDDOWN(ROUNDDOWN(rsp, STACKSIZE), FRAMESIZE); +} + +static textstartup size_t __asan_get_auxv_size(intptr_t *auxv) { + unsigned i; + for (i = 0;; i += 2) { + if (!auxv[i]) break; + } + return (i + 2) * sizeof(intptr_t); +} + +static textstartup void __asan_shadow_string_list(char **list) { + for (; *list; ++list) { + __asan_map_shadow(*list, strlen(*list) + 1); } } +textstartup void __asan_init(int argc, char **argv, char **envp, + intptr_t *auxv) { + static bool once; + if (once) return; + __asan_map_shadow(_base, _end - _base); + __asan_map_shadow(__asan_get_stack_base(), STACKSIZE); + __asan_shadow_string_list(argv); + __asan_shadow_string_list(envp); + __asan_map_shadow(auxv, __asan_get_auxv_size(auxv)); + __asan_install_malloc_hooks(); +} + static textstartup void __asan_ctor(void) { - /* __cxa_atexit(__asan_morgue_flush, NULL, NULL); */ - getsymboltable(); + __cxa_atexit(__asan_morgue_flush, NULL, NULL); } const void *const g_asan_ctor[] initarray = {__asan_ctor}; diff --git a/libc/log/attachdebugger.c b/libc/log/attachdebugger.c index 72ce8996..6c17ed00 100644 --- a/libc/log/attachdebugger.c +++ b/libc/log/attachdebugger.c @@ -29,10 +29,13 @@ #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.h" #include "libc/stdio/stdio.h" +#include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/w.h" +#define RESTORE_TTY "\e[?1000;1002;1015;1006l\e[?25h" + /** * Launches GDB debugger GUI for current process. * @@ -50,17 +53,18 @@ * @note this is called via eponymous spinlock macro wrapper */ relegated int(attachdebugger)(intptr_t continuetoaddr) { - int ttyin, ttyout; + int ttyfd; struct StackFrame *bp; char pidstr[11], breakcmd[40]; const char *se, *elf, *gdb, *rewind, *layout; if (IsGenuineCosmo() || !(gdb = GetGdbPath()) || - (ttyin = ttyout = open(_PATH_TTY, O_RDWR, 0)) == -1) { + (ttyfd = open(_PATH_TTY, O_RDWR, 0)) == -1) { return -1; } + write(ttyfd, RESTORE_TTY, strlen(RESTORE_TTY)); snprintf(pidstr, sizeof(pidstr), "%u", getpid()); layout = "layout asm"; - if ((elf = finddebugbinary())) { + if ((elf = FindDebugBinary())) { se = "-se"; if (fileexists(__FILE__)) layout = "layout src"; } else { @@ -78,7 +82,7 @@ relegated int(attachdebugger)(intptr_t continuetoaddr) { rewind = NULL; breakcmd[0] = '\0'; } - return spawnve(0, (int[3]){ttyin, ttyout, STDERR_FILENO}, gdb, + return spawnve(0, (int[3]){ttyfd, ttyfd, STDERR_FILENO}, gdb, (char *const[]){ "gdb", "--tui", "-p", pidstr, diff --git a/libc/log/backtrace.h b/libc/log/backtrace.h index ce31ae14..eb16ea3b 100644 --- a/libc/log/backtrace.h +++ b/libc/log/backtrace.h @@ -2,12 +2,11 @@ #define COSMOPOLITAN_LIBC_LOG_BACKTRACE_H_ #include "libc/nexgen32e/stackframe.h" #include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -void showbacktrace(FILE *, const struct StackFrame *); -int PrintBacktraceUsingSymbols(FILE *, const struct StackFrame *, +void ShowBacktrace(int, const struct StackFrame *); +int PrintBacktraceUsingSymbols(int, const struct StackFrame *, struct SymbolTable *); COSMOPOLITAN_C_END_ diff --git a/libc/log/backtrace2.c b/libc/log/backtrace2.c index 163cc43c..5eaf1081 100644 --- a/libc/log/backtrace2.c +++ b/libc/log/backtrace2.c @@ -31,14 +31,13 @@ #include "libc/nexgen32e/gc.h" #include "libc/runtime/runtime.h" #include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/fileno.h" #define kBacktraceMaxFrames 128 #define kBacktraceBufSize ((kBacktraceMaxFrames - 1) * (16 + 1)) -static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { +static int PrintBacktraceUsingAddr2line(int fd, const struct StackFrame *bp) { ssize_t got; intptr_t addr; size_t i, j, gi; @@ -47,7 +46,7 @@ static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { const struct StackFrame *frame; const char *debugbin, *p1, *p2, *p3, *addr2line; char buf[kBacktraceBufSize], *argv[kBacktraceMaxFrames]; - if (!(debugbin = finddebugbinary()) || !(addr2line = GetAddr2linePath())) { + if (!(debugbin = FindDebugBinary()) || !(addr2line = GetAddr2linePath())) { return -1; } i = 0; @@ -88,11 +87,11 @@ static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { strlen(" (discriminator ") - 1)) && (p3 = memchr(p2, '\n', got - (p2 - p1)))) { if (p3 > p2 && p3[-1] == '\r') --p3; - fwrite(p1, 1, p2 - p1, f); + write(fd, p1, p2 - p1); got -= p3 - p1; p1 += p3 - p1; } else { - fwrite(p1, 1, got, f); + write(fd, p1, got); break; } } @@ -103,20 +102,21 @@ static int PrintBacktraceUsingAddr2line(FILE *f, const struct StackFrame *bp) { return 0; } -static int PrintBacktrace(FILE *f, const struct StackFrame *bp) { +static int PrintBacktrace(int fd, const struct StackFrame *bp) { if (!IsTiny()) { - if (PrintBacktraceUsingAddr2line(f, bp) != -1) { + if (PrintBacktraceUsingAddr2line(fd, bp) != -1) { return 0; } } - return PrintBacktraceUsingSymbols(f, bp, getsymboltable()); + return PrintBacktraceUsingSymbols(fd, bp, GetSymbolTable()); } -void showbacktrace(FILE *f, const struct StackFrame *bp) { +void ShowBacktrace(int fd, const struct StackFrame *bp) { static bool noreentry; + if (!bp) bp = __builtin_frame_address(0); if (!noreentry) { noreentry = true; - PrintBacktrace(f, bp); + PrintBacktrace(fd, bp); noreentry = 0; } } diff --git a/libc/log/backtrace3.c b/libc/log/backtrace3.c index 5fc6f1e3..c123085c 100644 --- a/libc/log/backtrace3.c +++ b/libc/log/backtrace3.c @@ -20,6 +20,7 @@ #include "libc/alg/bisectcarleft.h" #include "libc/assert.h" #include "libc/bits/weaken.h" +#include "libc/calls/calls.h" #include "libc/conv/itoa.h" #include "libc/fmt/fmt.h" #include "libc/log/backtrace.h" @@ -28,26 +29,26 @@ #include "libc/nexgen32e/stackframe.h" #include "libc/runtime/missioncritical.h" #include "libc/runtime/symbols.h" -#include "libc/stdio/stdio.h" #include "libc/str/str.h" /** * Prints stack frames with symbols. * - * PrintBacktraceUsingSymbols(stdout, NULL, getsymboltable()); + * PrintBacktraceUsingSymbols(STDOUT_FILENO, NULL, GetSymbolTable()); * * @param f is output stream * @param bp is rbp which can be NULL to detect automatically * @param st is open symbol table for current executable * @return -1 w/ errno if error happened */ -int PrintBacktraceUsingSymbols(FILE *f, const struct StackFrame *bp, +int PrintBacktraceUsingSymbols(int fd, const struct StackFrame *bp, struct SymbolTable *st) { + char *p; size_t gi; intptr_t addr; int64_t addend; struct Garbages *garbage; - char *p, buf[256], ibuf[21]; + char buf[256], ibuf[21]; const struct Symbol *symbol; const struct StackFrame *frame; if (!st) return -1; @@ -78,8 +79,11 @@ int PrintBacktraceUsingSymbols(FILE *f, const struct StackFrame *bp, } else { p = stpcpy(p, "UNKNOWN"); } + *p++ = '\r'; *p++ = '\n'; - __print(buf, p - buf); + if (write(fd, buf, p - buf) == -1) { + return -1; + } } return 0; } diff --git a/libc/log/check.h b/libc/log/check.h index 77129d50..a8eef092 100644 --- a/libc/log/check.h +++ b/libc/log/check.h @@ -10,7 +10,7 @@ #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define CHECK(X, ...) __CHK(eq, ==, true, "true", !!(X), #X, "" __VA_ARGS__) +#define CHECK(X, ...) __CHK(ne, !=, false, "false", !!(X), #X, "" __VA_ARGS__) #define CHECK_EQ(Y, X, ...) __CHK(eq, ==, Y, #Y, X, #X, "" __VA_ARGS__) #define CHECK_NE(Y, X, ...) __CHK(ne, !=, Y, #Y, X, #X, "" __VA_ARGS__) #define CHECK_LE(Y, X, ...) __CHK(le, <=, Y, #Y, X, #X, "" __VA_ARGS__) @@ -19,7 +19,7 @@ COSMOPOLITAN_C_START_ #define CHECK_GT(Y, X, ...) __CHK(gt, >, Y, #Y, X, #X, "" __VA_ARGS__) #define CHECK_NOTNULL(X, ...) __CHK(ne, !=, NULL, "NULL", X, #X, "" __VA_ARGS__) -#define DCHECK(X, ...) __DCHK(eq, ==, true, "true", !!(X), #X, "" __VA_ARGS__) +#define DCHECK(X, ...) __DCHK(ne, !=, false, "false", !!(X), #X, "" __VA_ARGS__) #define DCHECK_EQ(Y, X, ...) __DCHK(eq, ==, Y, #Y, X, #X, "" __VA_ARGS__) #define DCHECK_NE(Y, X, ...) __DCHK(ne, !=, Y, #Y, X, #X, "" __VA_ARGS__) #define DCHECK_LE(Y, X, ...) __DCHK(le, <=, Y, #Y, X, #X, "" __VA_ARGS__) diff --git a/libc/log/checkaligned.c b/libc/log/checkaligned.c index 6f2fdb77..bf73f438 100644 --- a/libc/log/checkaligned.c +++ b/libc/log/checkaligned.c @@ -29,7 +29,7 @@ STATIC_YOINK("stoa"); void __check_fail_aligned(unsigned bytes, uint64_t ptr) { fflush(stderr); if (!IsTiny()) memsummary(fileno(stderr)); - (dprintf)(fileno(stderr), "%s%d%s%#p\n", "error: pointer not ", bytes, + (dprintf)(fileno(stderr), "%s%d%s%#p\r\n", "error: pointer not ", bytes, "-byte aligned: ", ptr); die(); } diff --git a/libc/log/checkfail.c b/libc/log/checkfail.c index 5ee422c6..d4ff2649 100644 --- a/libc/log/checkfail.c +++ b/libc/log/checkfail.c @@ -52,10 +52,10 @@ relegated void __check_fail(const char *suffix, const char *opstr, strtoupper(sufbuf); (fprintf)(stderr, - "check failed\n" - "\tCHECK_%s(%s, %s);\n" - "\t\t → %#lx (%s)\n" - "\t\t%s %#lx (%s)\n", + "check failed\r\n" + "\tCHECK_%s(%s, %s);\r\n" + "\t\t → %#lx (%s)\r\n" + "\t\t%s %#lx (%s)\r\n", sufbuf, wantstr, gotstr, want, wantstr, opstr, got, gotstr); if (!isempty(fmt)) { @@ -63,18 +63,18 @@ relegated void __check_fail(const char *suffix, const char *opstr, va_start(va, fmt); (vfprintf)(stderr, fmt, va); va_end(va); - fputc('\n', stderr); + fputs("\r\n", stderr); } - (fprintf)(stderr, "\t%s\n\t%s%s%s%s\n", strerror(lasterr), SUBTLE, + (fprintf)(stderr, "\t%s\r\n\t%s%s%s%s\r\n", strerror(lasterr), SUBTLE, getauxval(AT_EXECFN), g_argc > 1 ? " \\" : "", RESET); for (i = 1; i < g_argc; ++i) { - (fprintf)(stderr, "\t\t%s%s\n", g_argv[i], i < g_argc - 1 ? " \\" : ""); + (fprintf)(stderr, "\t\t%s%s\r\n", g_argv[i], i < g_argc - 1 ? " \\" : ""); } if (!IsTiny() && lasterr == ENOMEM) { - (fprintf)(stderr, "\n"); + (fprintf)(stderr, "\r\n"); fflush(stderr); PrintMemoryIntervals(fileno(stderr), &_mmi); } diff --git a/libc/log/checkfail_ndebug.c b/libc/log/checkfail_ndebug.c index 21c88cc4..6e6fc2fc 100644 --- a/libc/log/checkfail_ndebug.c +++ b/libc/log/checkfail_ndebug.c @@ -47,5 +47,5 @@ relegated void ___check_fail_ndebug(uint64_t want, uint64_t got, __print(bx, uint64toarray_radix16(got, bx)); __print_string(" ("); __print(bx, int64toarray_radix10(lasterr, bx)); - __print_string(")\n"); + __print_string(")\r\n"); } diff --git a/libc/log/die.c b/libc/log/die.c index 241bb8d4..1f456f26 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -18,10 +18,12 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/bits/bits.h" +#include "libc/log/backtrace.h" #include "libc/log/log.h" #include "libc/runtime/internal.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/exit.h" +#include "libc/sysv/consts/fileno.h" /** * Aborts process after printing details on its current state. @@ -33,7 +35,7 @@ relegated noreturn void die(void) { if (!IsTiny()) { g_runstate |= RUNSTATE_BROKEN; if (IsDebuggerPresent(false)) DebugBreak(); - backtrace(stderr); + ShowBacktrace(STDERR_FILENO, NULL); } exit(EXIT_FAILURE); unreachable; diff --git a/libc/log/gdbexec.c b/libc/log/gdbexec.c index f21da7d8..9eba4558 100644 --- a/libc/log/gdbexec.c +++ b/libc/log/gdbexec.c @@ -38,7 +38,7 @@ int(gdbexec)(const char *cmd) { char pidstr[11], breakcmd[40]; if (!(gdb = GetGdbPath())) return -1; snprintf(pidstr, sizeof(pidstr), "%u", getpid()); - if ((elf = finddebugbinary())) { + if ((elf = FindDebugBinary())) { se = "-se"; } else { se = "-q"; diff --git a/libc/log/getsymboltable.c b/libc/log/getsymboltable.c index 7127594c..654971cd 100644 --- a/libc/log/getsymboltable.c +++ b/libc/log/getsymboltable.c @@ -24,15 +24,15 @@ * Returns debug binary symbol table, as global singleton. * @return symbol table, or NULL w/ errno on first call */ -struct SymbolTable *getsymboltable(void) { +struct SymbolTable *GetSymbolTable(void) { static bool once; static struct SymbolTable *singleton; const char *debugbin; if (!once) { once = true; - if ((debugbin = finddebugbinary()) && - (singleton = opensymboltable(debugbin))) { - __cxa_atexit(closesymboltable, &singleton, NULL); + if ((debugbin = FindDebugBinary()) && + (singleton = OpenSymbolTable(debugbin))) { + __cxa_atexit(CloseSymbolTable, &singleton, NULL); } } return singleton; diff --git a/libc/log/log.h b/libc/log/log.h index d2b14d3a..efadeafb 100644 --- a/libc/log/log.h +++ b/libc/log/log.h @@ -33,7 +33,6 @@ typedef struct FILE FILE; extern FILE *g_logfile; -void backtrace(FILE *) relegated; /* shows fn backtrace and args */ void perror(const char *) relegated; /* print the last system error */ void die(void) relegated noreturn; /* print backtrace and abort() */ void meminfo(int); /* shows malloc statistics &c. */ diff --git a/libc/log/malloc_stats.c b/libc/log/malloc_stats.c index 262ad8f4..a8029901 100644 --- a/libc/log/malloc_stats.c +++ b/libc/log/malloc_stats.c @@ -25,7 +25,7 @@ STATIC_YOINK("ntoa"); void malloc_stats(void) { struct MallocStats res = dlmalloc_stats(g_dlmalloc); - (fprintf)(stderr, "max system bytes = %'10zu\n", res.maxfp); - (fprintf)(stderr, "system bytes = %'10zu\n", res.fp); - (fprintf)(stderr, "in use bytes = %'10zu\n", res.used); + (fprintf)(stderr, "max system bytes = %'10zu\r\n", res.maxfp); + (fprintf)(stderr, "system bytes = %'10zu\r\n", res.fp); + (fprintf)(stderr, "in use bytes = %'10zu\r\n", res.used); } diff --git a/libc/log/meminfo.c b/libc/log/meminfo.c index d3e0a0c9..1de0e43c 100644 --- a/libc/log/meminfo.c +++ b/libc/log/meminfo.c @@ -26,7 +26,7 @@ STATIC_YOINK("ntoa"); STATIC_YOINK("stoa"); static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) { - (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\n", start, end, used_bytes, + (dprintf)(*(int *)arg, "%p - %p : %08zx / %08lx\r\n", start, end, used_bytes, (intptr_t)end - (intptr_t)start); } @@ -35,7 +35,7 @@ static void onmemchunk(void *start, void *end, size_t used_bytes, void *arg) { */ void meminfo(int fd) { memsummary(fd); - (dprintf)(fd, "%*s %*s %*s %*s\n", POINTER_XDIGITS, "start", + (dprintf)(fd, "%*s %*s %*s %*s\r\n", POINTER_XDIGITS, "start", POINTER_XDIGITS, "end", 8, "used", 8, "size"); malloc_inspect_all(onmemchunk, &fd); } diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 2cc4cc75..e7216a59 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -32,9 +32,9 @@ #include "libc/runtime/internal.h" #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" #include "libc/str/str.h" #include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/sig.h" @@ -49,17 +49,17 @@ STATIC_YOINK("stoa"); struct siginfo; -aligned(1) const char kGregOrder[17] = { +const char kGregOrder[17] aligned(1) = { 13, 11, 8, 14, 12, 9, 10, 15, 16, 0, 1, 2, 3, 4, 5, 6, 7, }; -aligned(1) const char kGregNames[17][4] = { +const char kGregNames[17][4] aligned(1) = { "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", "RDI", "RSI", "RBP", "RBX", "RDX", "RAX", "RCX", "RSP", "RIP", }; -aligned(1) const char kGodHatesFlags[12] = "CVPRAKZSTIDO"; -aligned(1) const char kCrashSigNames[8][5] = {"QUIT", "FPE", "ILL", "SEGV", +const char kGodHatesFlags[12] aligned(1) = "CVPRAKZSTIDO"; +const char kCrashSigNames[8][5] aligned(1) = {"QUIT", "FPE", "ILL", "SEGV", "TRAP", "ABRT", "BUS"}; int kCrashSigs[8]; @@ -75,60 +75,60 @@ relegated static const char *TinyStrSignal(int sig) { return "???"; } -relegated static void ShowFunctionCalls(FILE *f, ucontext_t *ctx) { +relegated static void ShowFunctionCalls(int fd, ucontext_t *ctx) { struct StackFrame *bp; struct StackFrame goodframe; - fputc('\n', f); + write(fd, "\r\n", 2); if (ctx && ctx->uc_mcontext.rip && ctx->uc_mcontext.rbp) { goodframe.next = (struct StackFrame *)ctx->uc_mcontext.rbp; goodframe.addr = ctx->uc_mcontext.rip; bp = &goodframe; - showbacktrace(f, bp); + ShowBacktrace(fd, bp); } } -relegated static void DescribeCpuFlags(FILE *f, unsigned efl) { - size_t i; +relegated static void DescribeCpuFlags(int fd, unsigned flags) { + unsigned i; + char buf[64], *p; + p = buf; for (i = 0; i < ARRAYLEN(kGodHatesFlags); ++i) { - if (efl & 1) { - fputc(' ', f); - fputc(kGodHatesFlags[i], f); - fputc('F', f); + if (flags & 1) { + *p++ = ' '; + *p++ = kGodHatesFlags[i]; + *p++ = 'F'; } - efl >>= 1; + flags >>= 1; } - (fprintf)(f, " %s%d\n", "IOPL", efl & 3); + p = stpcpy(p, " IOPL"); + *p++ = '0' + (flags & 3); + write(fd, buf, p - buf); } -relegated static void ShowGeneralRegisters(FILE *f, ucontext_t *ctx) { +relegated static void ShowGeneralRegisters(int fd, ucontext_t *ctx) { size_t i, j, k; long double st; - fputc('\n', f); + write(fd, "\r\n", 2); for (i = 0, j = 0, k = 0; i < ARRAYLEN(kGregNames); ++i) { - if (j > 0) { - fputc(' ', f); - } - (fprintf)(f, "%-3s %016lx", kGregNames[(unsigned)kGregOrder[i]], + if (j > 0) write(fd, " ", 1); + (dprintf)(fd, "%-3s %016lx", kGregNames[(unsigned)kGregOrder[i]], ctx->uc_mcontext.gregs[(unsigned)kGregOrder[i]]); if (++j == 3) { j = 0; memcpy(&st, (char *)&ctx->fpustate.st[k], sizeof(st)); - (fprintf)(f, " %s(%zu) %Lf", "ST", k, st); + (dprintf)(fd, " %s(%zu) %Lf", "ST", k, st); ++k; - fputc('\r', f); - fputc('\n', f); + write(fd, "\r\n", 2); } } - fflush(stderr); - DescribeCpuFlags(f, ctx->uc_mcontext.gregs[REG_EFL]); + DescribeCpuFlags(fd, ctx->uc_mcontext.gregs[REG_EFL]); } -relegated static void ShowSseRegisters(FILE *f, ucontext_t *ctx) { +relegated static void ShowSseRegisters(int fd, ucontext_t *ctx) { size_t i; - fputc('\n', f); + write(fd, "\r\n", 2); for (i = 0; i < 8; ++i) { - (fprintf)(f, VEIL("r", "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\n"), "XMM", - i + 0, ctx->fpustate.xmm[i + 0].u64[0], + (dprintf)(fd, VEIL("r", "%s%-2zu %016lx%016lx %s%-2d %016lx%016lx\r\n"), + "XMM", i + 0, ctx->fpustate.xmm[i + 0].u64[0], ctx->fpustate.xmm[i + 0].u64[1], "XMM", i + 8, ctx->fpustate.xmm[i + 8].u64[0], ctx->fpustate.xmm[i + 8].u64[1]); } @@ -149,23 +149,23 @@ relegated static void ShowMemoryMappings(int outfd) { } } -relegated static void ShowCrashReport(int err, FILE *f, int sig, +relegated static void ShowCrashReport(int err, int fd, int sig, ucontext_t *ctx) { struct utsname names; - (fprintf)(f, VEIL("r", "\n%serror%s: Uncaught SIG%s\n %s\n %s\n"), RED2, - RESET, TinyStrSignal(sig), getauxval(AT_EXECFN), strerror(err)); + (dprintf)(fd, VEIL("r", "\r\n%serror%s: Uncaught SIG%s\r\n %s\r\n %s\r\n"), + RED2, RESET, TinyStrSignal(sig), getauxval(AT_EXECFN), + strerror(err)); if (uname(&names) != -1) { - (fprintf)(f, VEIL("r", " %s %s %s %s\n"), names.sysname, names.nodename, + (dprintf)(fd, VEIL("r", " %s %s %s %s\r\n"), names.sysname, names.nodename, names.release, names.version); } - ShowFunctionCalls(f, ctx); + ShowFunctionCalls(fd, ctx); if (ctx) { - ShowGeneralRegisters(f, ctx); - ShowSseRegisters(f, ctx); + ShowGeneralRegisters(fd, ctx); + ShowSseRegisters(fd, ctx); } - fputc('\n', f); - fflush(f); - ShowMemoryMappings(fileno(f)); + write(fd, "\r\n", 2); + ShowMemoryMappings(fd); } relegated static void RestoreDefaultCrashSignalHandlers(void) { @@ -215,7 +215,7 @@ relegated void oncrash(int sig, struct siginfo *si, ucontext_t *ctx) { : 0); } if (gdbpid > 0 && (sig == SIGTRAP || sig == SIGQUIT)) return; - ShowCrashReport(err, stderr, sig, ctx); - quick_exit(128 + sig); + ShowCrashReport(err, STDERR_FILENO, sig, ctx); + exit(128 + sig); unreachable; } diff --git a/libc/log/perror.c b/libc/log/perror.c index 428fca89..1589f5bd 100644 --- a/libc/log/perror.c +++ b/libc/log/perror.c @@ -17,12 +17,14 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/errno.h" +#include "libc/fmt/fmt.h" #include "libc/log/internal.h" #include "libc/log/log.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" void perror(const char *message) { - fprintf(stderr, "%s%s%s: %s: %m: %s\n", RED2, "error", RESET, - program_invocation_name, message); + fprintf(stderr, "%s%s%s: %s: %s: %s\r\n", RED2, "error", RESET, + program_invocation_name, strerror(errno), message); } diff --git a/libc/log/ubsan.c b/libc/log/ubsan.c index 67773ce9..042d2945 100644 --- a/libc/log/ubsan.c +++ b/libc/log/ubsan.c @@ -56,7 +56,7 @@ void __ubsan_abort(const struct UbsanSourceLocation *loc, g_runstate |= RUNSTATE_BROKEN; if (IsDebuggerPresent(false)) DebugBreak(); startfatal(loc->file, loc->line); - fprintf(stderr, "%s\n", description); + fprintf(stderr, "%s\r\n", description); die(); unreachable; } @@ -86,7 +86,7 @@ void __ubsan_handle_type_mismatch(struct UbsanTypeMismatchInfo *type_mismatch, type_mismatch->alignment); } else { description = __ubsan_buf; - snprintf(__ubsan_buf, sizeof(__ubsan_buf), "%s\n\t%s %s %p %s %s", + snprintf(__ubsan_buf, sizeof(__ubsan_buf), "%s\r\n\t%s %s %p %s %s", "insufficient size", kind, "address", pointer, "with insufficient space for object of type", type_mismatch->type->name); diff --git a/libc/log/vflogf.c b/libc/log/vflogf.c index 5fe4a11c..dc7dbfd3 100644 --- a/libc/log/vflogf.c +++ b/libc/log/vflogf.c @@ -72,7 +72,7 @@ void vflogf_onfail(FILE *f) { fseek(f, SEEK_SET, 0); f->beg = f->end = 0; clearerr(f); - (fprintf)(f, "performed emergency log truncation: %s\n", strerror(err)); + (fprintf)(f, "performed emergency log truncation: %s\r\n", strerror(err)); } } @@ -111,10 +111,10 @@ void(vflogf)(unsigned level, const char *file, int line, FILE *f, } (vfprintf)(f, fmt, va); va_end(va); - fputc('\n', f); + fputs("\r\n", f); if (level == kLogFatal) { startfatal(file, line); - (fprintf)(stderr, "fatal error see logfile\n"); + (fprintf)(stderr, "fatal error see logfile\r\n"); die(); unreachable; } diff --git a/libc/macros.h b/libc/macros.h index e3009eb2..be651c7a 100644 --- a/libc/macros.h +++ b/libc/macros.h @@ -15,7 +15,7 @@ #define TRUE 1 #define FALSE 0 -#define ROUNDUP(X, K) (((X) + ((K)-1)) & ~((K)-1)) +#define ROUNDUP(X, K) (((X) + (K)-1) & -(K)) #define ROUNDDOWN(X, K) ((X) & -(K)) #define ABS(X) ((X) >= 0 ? (X) : -(X)) #define MIN(X, Y) ((Y) > (X) ? (X) : (Y)) diff --git a/libc/nexgen32e/bcopy.S b/libc/nexgen32e/bcopy.S index 21430ce9..a7986bb7 100644 --- a/libc/nexgen32e/bcopy.S +++ b/libc/nexgen32e/bcopy.S @@ -29,4 +29,3 @@ bcopy: jmp memmove .endfn bcopy,globl .source __FILE__ - .source __FILE__ diff --git a/libc/nexgen32e/cescapec.S b/libc/nexgen32e/cescapec.S index f3c1c3a4..2ff8ea00 100644 --- a/libc/nexgen32e/cescapec.S +++ b/libc/nexgen32e/cescapec.S @@ -25,8 +25,10 @@ / This turns stuff like (char)0xFF into \0377. The returned / string is word-encoded, e.g. '\\'|'0'<<010|'3'<<020|etc. / +/ @param dil contains byte to escape / @see libc/nexgen32e/cescapec.c cescapec: + movzbl %dil,%edi lea -7(%rdi),%ecx cmp $85,%cl ja 1f @@ -63,12 +65,11 @@ cescapec: ret .LQM: #endif -1: movzbl %dil,%eax - cmp $127,%eax +1: mov %edi,%eax + lea -0x20(%rax),%ecx + cmp $0x5E,%ecx ja 2f - ezlea kCtype,cx - testb $16,(%rcx,%rax) - jne 3f + ret 2: and $-64,%eax mov %edi,%ecx and $56,%ecx @@ -78,7 +79,7 @@ cescapec: or %ecx,%edi lea (%rdi,%rax,4),%eax add $'0<<030|'0<<020|'0<<010|'\\,%eax -3: ret + ret .endfn cescapec,globl .initro 300,_init_cescapec diff --git a/libc/nexgen32e/ctype.S b/libc/nexgen32e/ctype.S deleted file mode 100644 index 10e11ed4..00000000 --- a/libc/nexgen32e/ctype.S +++ /dev/null @@ -1,101 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -/ Returns nonzero if %dil ∈ [ \t\v\r\n]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isspace:push $1 - jmp ctypep - .endfn isspace,globl - -/ Returns nonzero if %dil ∈ [A-Za-z]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isalpha:push $2 - jmp ctypep - .endfn isalpha,globl - -/ Returns nonzero if %dil ∈ [0-9]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isdigit:push $4 - jmp ctypep - .endfn isdigit,globl - -/ Returns nonzero if %dil ∈ [0-9A-Za-z]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isalnum:push $6 - jmp ctypep - .endfn isalnum,globl - -/ Returns nonzero if %dil ∈ [0-9A-fa-f]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isxdigit:push $8 - jmp ctypep - .endfn isxdigit,globl - -/ Returns nonzero if %dil ∈ [ -~] a.k.a. [\x20-\x7e]. -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isprint:push $16 - jmp ctypep - .endfn isprint,globl - -/ Returns nonzero if %dil ∈ [a-z] -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -islower:push $32 - jmp ctypep - .endfn islower,globl - -/ Returns nonzero if %dil ∈ [A-Z] -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isupper:push $64 - jmp ctypep - .endfn isupper,globl - -/ Returns nonzero if %dil ∈ [ \t] -/ -/ @param edi is character to test (treated as uint8_t) -/ @return nonzero if character matches, otherwise 0 -isblank:push $-128 -/ fallthrough - .endfn isblank,globl - -ctypep: pop %rsi - movzbl %dil,%edi - ezlea kCtype,cx - movzbl (%rcx,%rdi),%eax - and %esi,%eax - ret - .endfn ctypep - .source __FILE__ diff --git a/libc/nexgen32e/hextoint.S b/libc/nexgen32e/hextoint.S deleted file mode 100644 index 68937521..00000000 --- a/libc/nexgen32e/hextoint.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -hextoint: - .leafprologue - .profilable - mov %edi,%eax - sar $6,%eax - and $1,%eax - lea (%rax,%rax,8),%eax - add %edi,%eax - and $15,%eax - .leafepilogue - .endfn hextoint,globl - .source __FILE__ diff --git a/libc/nexgen32e/iscntrl.S b/libc/nexgen32e/iscntrl.S deleted file mode 100644 index 3818bd22..00000000 --- a/libc/nexgen32e/iscntrl.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iscntrl:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - mov (%rcx,%rdi),%al - shr $4,%al - xor $1,%eax - and $1,%eax -1: .leafepilogue - .endfn iscntrl,globl - .source __FILE__ diff --git a/libc/nexgen32e/isgraph.S b/libc/nexgen32e/isgraph.S deleted file mode 100644 index 75f508d0..00000000 --- a/libc/nexgen32e/isgraph.S +++ /dev/null @@ -1,38 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -isgraph:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rax - ezlea kCtype,cx - mov (%rcx,%rax),%al - shr $4,%al - mov %eax,%edx - xor %eax,%eax - cmp $32,%edi - setne %al - and %edx,%eax -1: .leafepilogue - .endfn isgraph,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswalnum.S b/libc/nexgen32e/iswalnum.S deleted file mode 100644 index 1807fce1..00000000 --- a/libc/nexgen32e/iswalnum.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswalnum: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $6,%eax -1: .leafepilogue - .endfn iswalnum,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswalpha.S b/libc/nexgen32e/iswalpha.S deleted file mode 100644 index da6c5e4f..00000000 --- a/libc/nexgen32e/iswalpha.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswalpha: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $2,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswalpha,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswblank.S b/libc/nexgen32e/iswblank.S deleted file mode 100644 index 8355a213..00000000 --- a/libc/nexgen32e/iswblank.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswblank: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $-128,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswblank,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswcntrl.S b/libc/nexgen32e/iswcntrl.S deleted file mode 100644 index 96c7769b..00000000 --- a/libc/nexgen32e/iswcntrl.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswcntrl: - .leafprologue - .profilable - movslq %edi,%rax - cmp $127,%eax - ja 1f - ezlea kCtype,cx - mov (%rcx,%rax),%al - shr $4,%al - xor $1,%eax - and $1,%eax -1: .leafepilogue - .endfn iswcntrl,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswdigit.S b/libc/nexgen32e/iswdigit.S deleted file mode 100644 index dd00e32b..00000000 --- a/libc/nexgen32e/iswdigit.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswdigit: - .leafprologue - .profilable - movl %edi,%eax - cmpl $127,%edi - ja 1f - subl $48,%eax - cmpl $9,%eax - setbe %al - movzbl %al,%eax -1: .leafepilogue - .endfn iswdigit,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswgraph.S b/libc/nexgen32e/iswgraph.S deleted file mode 100644 index 051af6a2..00000000 --- a/libc/nexgen32e/iswgraph.S +++ /dev/null @@ -1,38 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswgraph: - .leafprologue - .profilable - movl %edi,%eax - cmpl $127,%edi - ja 1f - movslq %edi,%rdx - ezlea kCtype,cx - movb (%rcx,%rdx),%dl - shrb $4,%dl - cmpl $32,%edi - setne %al - movzbl %al,%eax - andl %edx,%eax -1: .leafepilogue - .endfn iswgraph,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswlower.S b/libc/nexgen32e/iswlower.S deleted file mode 100644 index 71a5f2b2..00000000 --- a/libc/nexgen32e/iswlower.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswlower: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $32,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswlower,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswprint.S b/libc/nexgen32e/iswprint.S deleted file mode 100644 index c313f276..00000000 --- a/libc/nexgen32e/iswprint.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswprint: - .leafprologue - .profilable - movl $1,%eax - cmpl $127,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - movb (%rcx,%rdi),%al - shrb $4,%al - andl $1,%eax -1: .leafepilogue - .endfn iswprint,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswpunct.S b/libc/nexgen32e/iswpunct.S deleted file mode 100644 index 78e8cdfd..00000000 --- a/libc/nexgen32e/iswpunct.S +++ /dev/null @@ -1,39 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswpunct: - .leafprologue - .profilable - mov $1,%eax - cmp $127,%edi - ja 1f - movslq %edi,%rdi - ezlea kCtype,cx - mov (%rcx,%rdi),%dl - xor %eax,%eax - test $16,%dl - je 1f - xor %eax,%eax - and $7,%dl - sete %al -1: .leafepilogue - .endfn iswpunct,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswspace.S b/libc/nexgen32e/iswspace.S deleted file mode 100644 index 00c6b4d9..00000000 --- a/libc/nexgen32e/iswspace.S +++ /dev/null @@ -1,33 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswspace: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $1,%eax -1: .leafepilogue - .endfn iswspace,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswupper.S b/libc/nexgen32e/iswupper.S deleted file mode 100644 index 1ed6ae18..00000000 --- a/libc/nexgen32e/iswupper.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswupper: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $64,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswupper,globl - .source __FILE__ diff --git a/libc/nexgen32e/iswxdigit.S b/libc/nexgen32e/iswxdigit.S deleted file mode 100644 index e2d4ecf1..00000000 --- a/libc/nexgen32e/iswxdigit.S +++ /dev/null @@ -1,34 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -iswxdigit: - .leafprologue - .profilable - movslq %edi,%rax - cmpl $127,%eax - ja 1f - ezlea kCtype,cx - movb (%rcx,%rax),%al - andl $8,%eax - movzbl %al,%eax -1: .leafepilogue - .endfn iswxdigit,globl - .source __FILE__ diff --git a/libc/nexgen32e/kctype.S b/libc/nexgen32e/kctype.S deleted file mode 100644 index c5d4afd3..00000000 --- a/libc/nexgen32e/kctype.S +++ /dev/null @@ -1,63 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 sw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" -#include "libc/notice.inc" - - .initbss 300,_init_kCtype -kCtype: .rept 256 - .byte 0 - .endr - .endobj kCtype,globl,hidden - .previous - - .initro 300,_init_kCtype -.LkCtype.rodata: -/ ┌─isblank -/ │┌─isupper -/ ││┌─islower -/ │││┌─isprint -/ ││││┌─isxdigit -/ │││││┌─isdigit -/ ││││││┌─isalpha -/ │││││││┌─isspace -/ ││││││││ - .byte 9, 0b00000000 # 00─08 ∅─◘ - .byte 1, 0b10000001 # 09─09 ○ - .byte 4, 0b00000001 # 0a─0d ◙─♪ - .byte 18, 0b00000000 # 0e─1f ♫─▼ - .byte 1, 0b10010001 # 20─20 - .byte 15, 0b00010000 # 21─2f !─/ - .byte 10, 0b00011100 # 30─39 0─9 - .byte 7, 0b00010000 # 3a─40 :─@ - .byte 6, 0b01011010 # 41─46 A─F - .byte 20, 0b01010010 # 47─5a G─Z - .byte 6, 0b00010000 # 5b─60 [─` - .byte 6, 0b00111010 # 61─66 a─f - .byte 20, 0b00110010 # 67─7a g─z - .byte 4, 0b00010000 # 7b─7e {─~ - .byte 129, 0b00000000 # 7f─ff ⌂─λ - .endobj .LkCtype.rodata # 32b ─ 13% - .byte 0,0 # terminatr - .previous - - .init.start 300,_init_kCtype - call rldecode - .init.end 300,_init_kCtype - .source __FILE__ diff --git a/libc/nexgen32e/tolower.S b/libc/nexgen32e/tolower.S deleted file mode 100644 index 0b5209bd..00000000 --- a/libc/nexgen32e/tolower.S +++ /dev/null @@ -1,31 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -tolower:.leafprologue - .profilable - movslq %edi,%rax - cmp $255,%eax - ja 1f - ezlea kToLower,cx - movzbl (%rcx,%rax),%eax -1: .leafepilogue - .endfn tolower,globl - .source __FILE__ diff --git a/libc/nexgen32e/toupper.S b/libc/nexgen32e/toupper.S deleted file mode 100644 index 071faef0..00000000 --- a/libc/nexgen32e/toupper.S +++ /dev/null @@ -1,35 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -toupper:.leafprologue - .profilable - xor %eax,%eax - cmp $255,%edi - ja 1f - movslq %edi,%rax - ezlea kCtype,cx - mov (%rcx,%rax),%al - and $32,%eax - movzbl %al,%eax - andn %edi,%eax,%eax # TODO(jart): FIX -1: .leafepilogue - .endfn toupper,globl - .source __FILE__ diff --git a/libc/nexgen32e/towupper.S b/libc/nexgen32e/towupper.S deleted file mode 100644 index 8aa8462f..00000000 --- a/libc/nexgen32e/towupper.S +++ /dev/null @@ -1,36 +0,0 @@ -/*-*- mode:unix-assembly; indent-tabs-mode:t; tab-width:8; coding:utf-8 -*-│ -│vi: set et ft=asm ts=8 tw=8 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/macros.h" - -towupper: - .leafprologue - .profilable - movl %edi,%eax - cmpl $127,%edi - ja 1f - movslq %edi,%rdx - ezlea kCtype,cx - movb (%rcx,%rdx),%dl - andl $32,%edx - movzbl %dl,%edx - andn %edi,%edx,%eax -1: .leafepilogue - .endfn towupper,globl - .source __FILE__ diff --git a/libc/nt/KernelBase/CancelIo.s b/libc/nt/KernelBase/CancelIo.s index ca6db4cf..04652094 100644 --- a/libc/nt/KernelBase/CancelIo.s +++ b/libc/nt/KernelBase/CancelIo.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CancelIo,CancelIo,101 + + .text.windows +CancelIo: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_CancelIo(%rip) + leave + ret + .endfn CancelIo,globl + .previous diff --git a/libc/nt/KernelBase/CancelIoEx.s b/libc/nt/KernelBase/CancelIoEx.s index b1f1dff9..d056d52c 100644 --- a/libc/nt/KernelBase/CancelIoEx.s +++ b/libc/nt/KernelBase/CancelIoEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CancelIoEx,CancelIoEx,102 + + .text.windows +CancelIoEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CancelIoEx(%rip),%rax + jmp __sysv2nt + .endfn CancelIoEx,globl + .previous diff --git a/libc/nt/KernelBase/CancelSynchronousIo.s b/libc/nt/KernelBase/CancelSynchronousIo.s index ee949b11..66c45c06 100644 --- a/libc/nt/KernelBase/CancelSynchronousIo.s +++ b/libc/nt/KernelBase/CancelSynchronousIo.s @@ -1,2 +1,15 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CancelSynchronousIo,CancelSynchronousIo,103 + + .text.windows +CancelSynchronousIo: + push %rbp + mov %rsp,%rbp + .profilable + mov %rdi,%rcx + sub $32,%rsp + call *__imp_CancelSynchronousIo(%rip) + leave + ret + .endfn CancelSynchronousIo,globl + .previous diff --git a/libc/nt/KernelBase/CreateIoCompletionPort.s b/libc/nt/KernelBase/CreateIoCompletionPort.s index 0115b474..6f080d0f 100644 --- a/libc/nt/KernelBase/CreateIoCompletionPort.s +++ b/libc/nt/KernelBase/CreateIoCompletionPort.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CreateIoCompletionPort,CreateIoCompletionPort,195 + + .text.windows +CreateIoCompletionPort: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CreateIoCompletionPort(%rip),%rax + jmp __sysv2nt + .endfn CreateIoCompletionPort,globl + .previous diff --git a/libc/nt/KernelBase/CreateThread.s b/libc/nt/KernelBase/CreateThread.s index 96963560..c5b66df5 100644 --- a/libc/nt/KernelBase/CreateThread.s +++ b/libc/nt/KernelBase/CreateThread.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CreateThread,CreateThread,224 + + .text.windows +CreateThread: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CreateThread(%rip),%rax + jmp __sysv2nt6 + .endfn CreateThread,globl + .previous diff --git a/libc/nt/KernelBase/CreateWaitableTimerW.s b/libc/nt/KernelBase/CreateWaitableTimerW.s index dae909bb..916d6fa5 100644 --- a/libc/nt/KernelBase/CreateWaitableTimerW.s +++ b/libc/nt/KernelBase/CreateWaitableTimerW.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_CreateWaitableTimerW,CreateWaitableTimerW,234 + + .text.windows +CreateWaitableTimer: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_CreateWaitableTimerW(%rip),%rax + jmp __sysv2nt + .endfn CreateWaitableTimer,globl + .previous diff --git a/libc/nt/KernelBase/GetQueuedCompletionStatus.s b/libc/nt/KernelBase/GetQueuedCompletionStatus.s index 026f4ec6..a8acb1bd 100644 --- a/libc/nt/KernelBase/GetQueuedCompletionStatus.s +++ b/libc/nt/KernelBase/GetQueuedCompletionStatus.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_GetQueuedCompletionStatus,GetQueuedCompletionStatus,695 + + .text.windows +GetQueuedCompletionStatus: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_GetQueuedCompletionStatus(%rip),%rax + jmp __sysv2nt6 + .endfn GetQueuedCompletionStatus,globl + .previous diff --git a/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s b/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s index c310de42..7fa2e68d 100644 --- a/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s +++ b/libc/nt/KernelBase/GetQueuedCompletionStatusEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_GetQueuedCompletionStatusEx,GetQueuedCompletionStatusEx,696 + + .text.windows +GetQueuedCompletionStatusEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_GetQueuedCompletionStatusEx(%rip),%rax + jmp __sysv2nt6 + .endfn GetQueuedCompletionStatusEx,globl + .previous diff --git a/libc/nt/KernelBase/PostQueuedCompletionStatus.s b/libc/nt/KernelBase/PostQueuedCompletionStatus.s index 6584c832..f4b8e0e4 100644 --- a/libc/nt/KernelBase/PostQueuedCompletionStatus.s +++ b/libc/nt/KernelBase/PostQueuedCompletionStatus.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_PostQueuedCompletionStatus,PostQueuedCompletionStatus,1221 + + .text.windows +PostQueuedCompletionStatus: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_PostQueuedCompletionStatus(%rip),%rax + jmp __sysv2nt + .endfn PostQueuedCompletionStatus,globl + .previous diff --git a/libc/nt/KernelBase/ReadFileEx.s b/libc/nt/KernelBase/ReadFileEx.s index ae5ffddc..f3f7db44 100644 --- a/libc/nt/KernelBase/ReadFileEx.s +++ b/libc/nt/KernelBase/ReadFileEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_ReadFileEx,ReadFileEx,1312 + + .text.windows +ReadFileEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_ReadFileEx(%rip),%rax + jmp __sysv2nt6 + .endfn ReadFileEx,globl + .previous diff --git a/libc/nt/KernelBase/SetWaitableTimer.s b/libc/nt/KernelBase/SetWaitableTimer.s index b0677465..b1e75275 100644 --- a/libc/nt/KernelBase/SetWaitableTimer.s +++ b/libc/nt/KernelBase/SetWaitableTimer.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_SetWaitableTimer,SetWaitableTimer,1583 + + .text.windows +SetWaitableTimer: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_SetWaitableTimer(%rip),%rax + jmp __sysv2nt6 + .endfn SetWaitableTimer,globl + .previous diff --git a/libc/nt/KernelBase/WriteFileEx.s b/libc/nt/KernelBase/WriteFileEx.s index 448d1b81..065387c9 100644 --- a/libc/nt/KernelBase/WriteFileEx.s +++ b/libc/nt/KernelBase/WriteFileEx.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp KernelBase,__imp_WriteFileEx,WriteFileEx,1824 + + .text.windows +WriteFileEx: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_WriteFileEx(%rip),%rax + jmp __sysv2nt6 + .endfn WriteFileEx,globl + .previous diff --git a/libc/nt/files.h b/libc/nt/files.h index 7b473963..b096ec9a 100644 --- a/libc/nt/files.h +++ b/libc/nt/files.h @@ -121,10 +121,10 @@ bool32 CreateDirectory(const char16_t *lpPathName, struct NtSecurityAttributes *lpSecurityAttributes); bool32 RemoveDirectory(const char16_t *lpPathName); -int32_t DuplicateHandle(int64_t hSourceProcessHandle, int64_t hSourceHandle, - int64_t hTargetProcessHandle, int64_t *lpTargetHandle, - uint32_t dwDesiredAccess, bool32 bInheritHandle, - uint32_t dwOptions); +bool32 DuplicateHandle(int64_t hSourceProcessHandle, int64_t hSourceHandle, + int64_t hTargetProcessHandle, int64_t *lpTargetHandle, + uint32_t dwDesiredAccess, bool32 bInheritHandle, + uint32_t dwOptions); bool32 GetHandleInformation(int64_t hObject, uint32_t *out_lpdwFlags); bool32 SetHandleInformation(int64_t hObject, uint32_t dwMask, uint32_t dwFlags); @@ -244,6 +244,7 @@ bool32 ReadFileScatter( uint32_t nNumberOfBytesToReadThatsMultipleOfFileVolumeSectorSize, uint32_t *lpReserved, struct NtOverlapped *inout_lpOverlapped) paramsnonnull(); + bool32 WriteFileGather(int64_t hFileOpenedWithOverlappedAndNoBuffering, const union NtFileSegmentElement aSegmentArray[], uint32_t nNumberOfBytesToWrite, uint32_t *lpReserved, diff --git a/libc/nt/iocp.h b/libc/nt/iocp.h new file mode 100644 index 00000000..18bb0b03 --- /dev/null +++ b/libc/nt/iocp.h @@ -0,0 +1,79 @@ +#ifndef COSMOPOLITAN_LIBC_NT_IOCP_H_ +#define COSMOPOLITAN_LIBC_NT_IOCP_H_ +#if 0 +/* ░░░░ + ▒▒▒░░░▒▒▒▒▒▒▒▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▓▓▓▓▓▓░ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓░ + ▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▒ ▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒▒▒░░░░▒▒▒▒▒▒▓▓▓▓▓▓ █▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓░ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ▒▒▒▒░░░▒▒▒▒▒▒▒▓▓▓▓▓▓ ▒▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▒ + ▒▒▒▒▓▓ ▓▒▒▓▓▓▓ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▓ ▓▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓ + ░░░░░░░░░░░▒▒▒▒ ▓▓▓▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓█ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓▓ ▓▓▓▓▓▒▒▒▒▒▒▒▒▒▒▓▓▓ + ░▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓░ ░▓███▓ + ▒▒░░░░░░░░░░▒▒▒▒▒▓▓░ ▒▓▓▓▒▒▒ ░▒▒▒▓ ████████████ + ▒▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▒▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒░ ░███ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ███ + ▒▒░░░░░░░░░░▒▒▒▒▒▒▓▓ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ▓██ + ▒░░░░░░░░░░░▒▒▒▒▒▓▓ ▓▓▓▓▒▒▒▒▒▒▒▒░░░▒▒▒▒▒▓ ▓██ + ▒▒░░░▒▒▒░░░▒▒░▒▒▒▓▓▒ ▒▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▒ ███ + ░▒▓ ░▓▓▓▓▒▒▒▒▒▒▒▒░░░░▒▒▒▒▓ ▓██ +╔────────────────────────────────────────────────────────────────▀▀▀─────────│─╗ +│ cosmopolitan § new technology » i/o completion ports ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ +#endif +#include "libc/nt/struct/overlapped.h" +#include "libc/nt/struct/overlappedentry.h" + +#define kNtFileSkipCompletionPortOnSuccess 1 +#define kNtFileSkipSetEventOnHandle 2 + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +typedef void (*NtOverlappedCompletionRoutine)( + uint32_t dwErrorCode, uint32_t dwNumberOfBytesTransfered, + struct NtOverlapped *inout_lpOverlapped); + +int64_t CreateIoCompletionPort(int64_t FileHandleOrNeg1, + int64_t opt_ExistingCompletionPortOrZero, + void *StatePointer, + uint32_t NumberOfConcurrentThreads); + +bool32 GetQueuedCompletionStatus(int64_t CompletionPort, + uint32_t *lpNumberOfBytesTransferred, + void *StatePointerPointer, + struct NtOverlapped **lpOverlapped, + uint32_t dwMilliseconds); + +bool32 GetQueuedCompletionStatusEx( + int64_t CompletionPort, + struct NtOverlappedEntry *out_lpCompletionPortEntries, uint32_t ulCount, + uint32_t *out_ulNumEntriesRemoved, uint32_t dwMilliseconds, + bool32 fAlertable); + +bool32 PostQueuedCompletionStatus(int64_t CompletionPort, + uint32_t dwNumberOfBytesTransferred, + uint32_t *dwCompletionKey, + struct NtOverlapped *opt_lpOverlapped); + +bool32 SetFileCompletionNotificationModes(int64_t FileHandle, + unsigned char Flags); + +bool32 ReadFileEx(int64_t hFile, void *lpBuffer, uint32_t nNumberOfBytesToRead, + uint32_t *lpNumberOfBytesRead, + struct NtOverlapped *opt_lpOverlapped, + NtOverlappedCompletionRoutine lpCompletionRoutine); + +bool32 WriteFileEx(int64_t hFile, const void *lpBuffer, + uint32_t nNumberOfBytesToWrite, + struct NtOverlapped *lpOverlapped, + NtOverlappedCompletionRoutine lpCompletionRoutine); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_IOCP_H_ */ diff --git a/libc/nt/ipc.h b/libc/nt/ipc.h index 52158e2d..ad015e1f 100644 --- a/libc/nt/ipc.h +++ b/libc/nt/ipc.h @@ -28,17 +28,17 @@ #endif /* CreateNamedPipe:dwOpenMode */ -#define kNtPipeAccessInbound 0x00000001 +#define kNtPipeAccessInbound 0x00000001 #define kNtPipeAccessOutbound 0x00000002 -#define kNtPipeAccessDuplex 0x00000003 +#define kNtPipeAccessDuplex 0x00000003 /* CreateNamedPipe::dwPipeMode */ -#define kNtPipeWait 0x00000000 -#define kNtPipeNowait 0x00000001 -#define kNtPipeReadmodeByte 0x00000000 -#define kNtPipeReadmodeMessage 0x00000002 -#define kNtPipeTypeByte 0x00000000 -#define kNtPipeTypeMessage 0x00000004 +#define kNtPipeWait 0x00000000 +#define kNtPipeNowait 0x00000001 +#define kNtPipeReadmodeByte 0x00000000 +#define kNtPipeReadmodeMessage 0x00000002 +#define kNtPipeTypeByte 0x00000000 +#define kNtPipeTypeMessage 0x00000004 #define kNtPipeAcceptRemoteClients 0x00000000 #define kNtPipeRejectRemoteClients 0x00000008 @@ -55,28 +55,34 @@ COSMOPOLITAN_C_START_ struct NtOverlapped; struct NtSecurityAttributes; -int CreatePipe(int64_t *out_hReadPipe, int64_t *out_hWritePipe, - const struct NtSecurityAttributes *opt_lpPipeAttributes, - uint32_t nSize) paramsnonnull((1, 2)); -void *CreateNamedPipe( +bool32 CreatePipe(int64_t *out_hReadPipe, int64_t *out_hWritePipe, + const struct NtSecurityAttributes *opt_lpPipeAttributes, + uint32_t nSize) paramsnonnull((1, 2)); + +int64_t CreateNamedPipe( const char16_t *lpName, uint32_t dwOpenMode, uint32_t dwPipeMode, uint32_t nMaxInstances, uint32_t nOutBufferSize, uint32_t nInBufferSize, uint32_t nDefaultTimeOut, const struct NtSecurityAttributes *opt_lpSecurityAttributes) paramsnonnull((1)); -bool32 ConnectNamedPipe(int64_t *hNamedPipe, struct NtOverlapped *lpOverlapped); + bool32 CallNamedPipe(const char16_t *lpNamedPipeName, void *lpInBuffer, uint32_t nInBufferSize, void *lpOutBuffer, uint32_t nOutBufferSize, uint32_t *lpBytesRead, uint32_t nTimeOut); + +bool32 ConnectNamedPipe(int64_t *hNamedPipe, struct NtOverlapped *lpOverlapped); bool32 WaitNamedPipe(const char16_t *lpNamedPipeName, uint32_t nTimeOut); bool32 DisconnectNamedPipe(int64_t *hNamedPipe); + bool32 SetNamedPipeHandleState(int64_t *hNamedPipe, uint32_t *lpMode, uint32_t *lpMaxCollectionCount, uint32_t *lpCollectDataTimeout); + bool32 PeekNamedPipe(int64_t *hNamedPipe, void *lpBuffer, uint32_t nBufferSize, uint32_t *lpBytesRead, uint32_t *lpTotalBytesAvail, uint32_t *lpBytesLeftThisMessage); + bool32 TransactNamedPipe(int64_t *hNamedPipe, void *lpInBuffer, uint32_t nInBufferSize, void *lpOutBuffer, uint32_t nOutBufferSize, uint32_t *lpBytesRead, diff --git a/libc/nt/kernel32/SetFileCompletionNotificationModes.s b/libc/nt/kernel32/SetFileCompletionNotificationModes.s index ed3a3bb4..aa890d72 100644 --- a/libc/nt/kernel32/SetFileCompletionNotificationModes.s +++ b/libc/nt/kernel32/SetFileCompletionNotificationModes.s @@ -1,2 +1,12 @@ .include "o/libc/nt/codegen.inc" .imp kernel32,__imp_SetFileCompletionNotificationModes,SetFileCompletionNotificationModes,1318 + + .text.windows +SetFileCompletionNotificationModes: + push %rbp + mov %rsp,%rbp + .profilable + mov __imp_SetFileCompletionNotificationModes(%rip),%rax + jmp __sysv2nt + .endfn SetFileCompletionNotificationModes,globl + .previous diff --git a/libc/nt/master.sh b/libc/nt/master.sh index 8040d723..f1dd08e4 100755 --- a/libc/nt/master.sh +++ b/libc/nt/master.sh @@ -341,11 +341,11 @@ imp 'CallWindowProc' CallWindowProcW user32 1537 imp 'CallbackMayRunLong' CallbackMayRunLong KernelBase 100 imp 'CancelDC' CancelDC gdi32 1038 imp 'CancelDeviceWakeupRequest' CancelDeviceWakeupRequest kernel32 113 -imp 'CancelIo' CancelIo KernelBase 101 -imp 'CancelIoEx' CancelIoEx KernelBase 102 +imp 'CancelIo' CancelIo KernelBase 101 1 +imp 'CancelIoEx' CancelIoEx KernelBase 102 2 imp 'CancelOverlappedAccess' CancelOverlappedAccess advapi32 1091 imp 'CancelShutdown' CancelShutdown user32 1538 -imp 'CancelSynchronousIo' CancelSynchronousIo KernelBase 103 +imp 'CancelSynchronousIo' CancelSynchronousIo KernelBase 103 1 imp 'CancelTimerQueueTimer' CancelTimerQueueTimer kernel32 118 imp 'CancelWaitableTimer' CancelWaitableTimer KernelBase 105 imp 'CascadeChildWindows' CascadeChildWindows user32 1539 @@ -625,7 +625,7 @@ imp 'CreateIcon' CreateIcon user32 1615 imp 'CreateIconFromResource' CreateIconFromResource user32 1616 imp 'CreateIconFromResourceEx' CreateIconFromResourceEx user32 1617 imp 'CreateIconIndirect' CreateIconIndirect user32 1618 -imp 'CreateIoCompletionPort' CreateIoCompletionPort KernelBase 195 +imp 'CreateIoCompletionPort' CreateIoCompletionPort KernelBase 195 4 imp 'CreateJobObjectA' CreateJobObjectA kernel32 209 imp 'CreateJobObject' CreateJobObjectW kernel32 210 imp 'CreateJobSet' CreateJobSet kernel32 211 @@ -702,7 +702,7 @@ imp 'CreateSymbolicLinkTransactedA' CreateSymbolicLinkTransactedA kernel32 imp 'CreateSymbolicLinkTransacted' CreateSymbolicLinkTransactedW kernel32 238 imp 'CreateSystemThreads' CreateSystemThreads user32 1623 imp 'CreateTapePartition' CreateTapePartition kernel32 240 -imp 'CreateThread' CreateThread KernelBase 224 +imp 'CreateThread' CreateThread KernelBase 224 6 imp 'CreateThreadpool' CreateThreadpool KernelBase 225 imp 'CreateThreadpoolCleanupGroup' CreateThreadpoolCleanupGroup KernelBase 226 imp 'CreateThreadpoolIo' CreateThreadpoolIo KernelBase 227 @@ -717,7 +717,7 @@ imp 'CreateUmsThreadContext' CreateUmsThreadContext kernel32 252 imp 'CreateWaitableTimerA' CreateWaitableTimerA kernel32 253 imp 'CreateWaitableTimerExA' CreateWaitableTimerExA kernel32 254 imp 'CreateWaitableTimerEx' CreateWaitableTimerExW KernelBase 233 -imp 'CreateWaitableTimer' CreateWaitableTimerW KernelBase 234 +imp 'CreateWaitableTimer' CreateWaitableTimerW KernelBase 234 3 imp 'CreateWellKnownSid' CreateWellKnownSid KernelBase 235 imp 'CreateWindowEx' CreateWindowExW user32 1625 12 imp 'CreateWindowExA' CreateWindowExA user32 1624 12 @@ -2516,8 +2516,8 @@ imp 'GetPtrCalDataArray' GetPtrCalDataArray KernelBase 692 imp 'GetPublisherCacheFolder' GetPublisherCacheFolder KernelBase 693 imp 'GetPublisherRootFolder' GetPublisherRootFolder KernelBase 694 imp 'GetQueueStatus' GetQueueStatus user32 1936 -imp 'GetQueuedCompletionStatus' GetQueuedCompletionStatus KernelBase 695 -imp 'GetQueuedCompletionStatusEx' GetQueuedCompletionStatusEx KernelBase 696 +imp 'GetQueuedCompletionStatus' GetQueuedCompletionStatus KernelBase 695 5 +imp 'GetQueuedCompletionStatusEx' GetQueuedCompletionStatusEx KernelBase 696 6 imp 'GetROP2' GetROP2 gdi32 1695 imp 'GetRandomRgn' GetRandomRgn gdi32 1696 imp 'GetRasterizerCaps' GetRasterizerCaps gdi32 1697 @@ -4320,7 +4320,7 @@ imp 'PolylineTo' PolylineTo gdi32 1791 imp 'PoolPerAppKeyStateInternal' PoolPerAppKeyStateInternal KernelBase 1220 imp 'PostMessageA' PostMessageA user32 2204 imp 'PostMessage' PostMessageW user32 2205 -imp 'PostQueuedCompletionStatus' PostQueuedCompletionStatus KernelBase 1221 +imp 'PostQueuedCompletionStatus' PostQueuedCompletionStatus KernelBase 1221 4 imp 'PostQuitMessage' PostQuitMessage user32 2206 1 imp 'PostThreadMessageA' PostThreadMessageA user32 2207 imp 'PostThreadMessage' PostThreadMessageW user32 2208 @@ -4517,7 +4517,7 @@ imp 'ReadEncryptedFileRaw' ReadEncryptedFileRaw advapi32 1601 imp 'ReadEventLogA' ReadEventLogA advapi32 1602 imp 'ReadEventLog' ReadEventLogW advapi32 1603 imp 'ReadFile' ReadFile KernelBase 1311 5 -imp 'ReadFileEx' ReadFileEx KernelBase 1312 +imp 'ReadFileEx' ReadFileEx KernelBase 1312 5 imp 'ReadFileScatter' ReadFileScatter KernelBase 1313 5 imp 'ReadProcessMemory' ReadProcessMemory KernelBase 1314 imp 'ReadStateAtomValue' ReadStateAtomValue KernelBase 1315 @@ -6225,7 +6225,7 @@ imp 'SetFileAttributesA' SetFileAttributesA KernelBase 1507 2 imp 'SetFileAttributesTransactedA' SetFileAttributesTransactedA kernel32 1314 imp 'SetFileAttributesTransacted' SetFileAttributesTransactedW kernel32 1315 imp 'SetFileBandwidthReservation' SetFileBandwidthReservation kernel32 1317 -imp 'SetFileCompletionNotificationModes' SetFileCompletionNotificationModes kernel32 1318 +imp 'SetFileCompletionNotificationModes' SetFileCompletionNotificationModes kernel32 1318 2 imp 'SetFileInformationByHandle' SetFileInformationByHandle KernelBase 1509 imp 'SetFileIoOverlappedRange' SetFileIoOverlappedRange KernelBase 1510 imp 'SetFilePointer' SetFilePointer KernelBase 1511 4 @@ -6425,7 +6425,7 @@ imp 'SetVolumeLabel' SetVolumeLabelW kernel32 1401 imp 'SetVolumeMountPointA' SetVolumeMountPointA kernel32 1402 imp 'SetVolumeMountPoint' SetVolumeMountPointW kernel32 1403 imp 'SetVolumeMountPointWStub' SetVolumeMountPointWStub kernel32 1404 -imp 'SetWaitableTimer' SetWaitableTimer KernelBase 1583 +imp 'SetWaitableTimer' SetWaitableTimer KernelBase 1583 6 imp 'SetWaitableTimerEx' SetWaitableTimerEx KernelBase 1584 imp 'SetWinEventHook' SetWinEventHook user32 2388 imp 'SetWinMetaFileBits' SetWinMetaFileBits gdi32 1917 @@ -7188,7 +7188,7 @@ imp 'WriteConsoleOutputCharacter' WriteConsoleOutputCharacterW KernelBase imp 'WriteConsoleOutputCharacterA' WriteConsoleOutputCharacterA KernelBase 1819 5 imp 'WriteEncryptedFileRaw' WriteEncryptedFileRaw advapi32 1857 imp 'WriteFile' WriteFile KernelBase 1823 5 -imp 'WriteFileEx' WriteFileEx KernelBase 1824 +imp 'WriteFileEx' WriteFileEx KernelBase 1824 5 imp 'WriteFileGather' WriteFileGather KernelBase 1825 5 imp 'WritePrivateProfileSectionA' WritePrivateProfileSectionA kernel32 1565 imp 'WritePrivateProfileSection' WritePrivateProfileSectionW kernel32 1566 diff --git a/libc/nt/nt/time.h b/libc/nt/nt/time.h index 2d5d2e42..6274d5c3 100644 --- a/libc/nt/nt/time.h +++ b/libc/nt/nt/time.h @@ -35,7 +35,7 @@ COSMOPOLITAN_C_START_ each release.” ──Quoth MSDN */ #endif -NtStatus NtDelayExecution(bool32 alertable, int64_t *hectonanoseconds); +NtStatus NtDelayExecution(bool32 alertable, int64_t *AbsCobolOrNegRelHectoNano); #if ShouldUseMsabiAttribute() #include "libc/nt/nt/thunk/time.inc" diff --git a/libc/nt/struct/guid.h b/libc/nt/struct/guid.h new file mode 100644 index 00000000..ce4cbe10 --- /dev/null +++ b/libc/nt/struct/guid.h @@ -0,0 +1,15 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_GUID_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_GUID_H_ +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct NtGuid { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_GUID_H_ */ diff --git a/libc/nt/struct/overlappedentry.h b/libc/nt/struct/overlappedentry.h new file mode 100644 index 00000000..5fd5385e --- /dev/null +++ b/libc/nt/struct/overlappedentry.h @@ -0,0 +1,16 @@ +#ifndef COSMOPOLITAN_LIBC_NT_STRUCT_OVERLAPPEDENTRY_H_ +#define COSMOPOLITAN_LIBC_NT_STRUCT_OVERLAPPEDENTRY_H_ +#include "libc/nt/struct/overlapped.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct NtOverlappedEntry { + uint32_t *lpCompletionKey; + struct NtOverlapped *lpOverlapped; + uint32_t *Internal; + uint32_t dwNumberOfBytesTransferred; +}; + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_LIBC_NT_STRUCT_OVERLAPPEDENTRY_H_ */ diff --git a/libc/nt/synchronization.h b/libc/nt/synchronization.h index 75584463..ceb32bfe 100644 --- a/libc/nt/synchronization.h +++ b/libc/nt/synchronization.h @@ -1,6 +1,10 @@ #ifndef COSMOPOLITAN_LIBC_NT_SYNCHRONIZATION_H_ #define COSMOPOLITAN_LIBC_NT_SYNCHRONIZATION_H_ +#include "libc/nt/struct/criticalsection.h" +#include "libc/nt/struct/filetime.h" #include "libc/nt/struct/linkedlist.h" +#include "libc/nt/struct/securityattributes.h" +#include "libc/nt/struct/systemtime.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ #if 0 @@ -30,34 +34,19 @@ COSMOPOLITAN_C_START_ ╚────────────────────────────────────────────────────────────────────────────│*/ #endif -struct NtCriticalSection; -struct NtFileTime; -struct NtSystemTime; +typedef void (*NtTimerapcroutine)(void *lpArgToCompletionRoutine, + uint32_t dwTimerLowValue, + uint32_t dwTimerHighValue); void Sleep(uint32_t dwMilliseconds); uint32_t SleepEx(uint32_t dwMilliseconds, bool32 bAlertable); + void GetSystemTime(struct NtSystemTime *lpSystemTime); bool32 SystemTimeToFileTime(const struct NtSystemTime *lpSystemTime, struct NtFileTime *lpFileTime); -void GetSystemTimeAsFileTime( - struct NtFileTime *out_lpSystemTimeAsFileTime); /* win8+ */ -void GetSystemTimePreciseAsFileTime( - struct NtFileTime *out_lpSystemTimeAsFileTime); /* win8+ */ -void InitializeCriticalSection(struct NtCriticalSection *lpCriticalSection); -void EnterCriticalSection(struct NtCriticalSection *lpCriticalSection); -void LeaveCriticalSection(struct NtCriticalSection *lpCriticalSection); -int32_t InitializeCriticalSectionAndSpinCount( - struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); -uint32_t SetCriticalSectionSpinCount( - struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); -int32_t TryEnterCriticalSection(struct NtCriticalSection *lpCriticalSection); -void DeleteCriticalSection(struct NtCriticalSection *lpCriticalSection); -int32_t SetEvent(int64_t hEvent); -int32_t ResetEvent(int64_t hEvent); -int32_t PulseEvent(int64_t hEvent); -int32_t ReleaseSemaphore(int64_t hSemaphore, int32_t lReleaseCount, - int *lpPreviousCount); -int32_t ReleaseMutex(int64_t hMutex); +void GetSystemTimeAsFileTime(struct NtFileTime *); // win8+ +void GetSystemTimePreciseAsFileTime(struct NtFileTime *); // win8+ + uint32_t WaitForSingleObject(int64_t hHandle, uint32_t dwMilliseconds); uint32_t WaitForMultipleObjects(uint32_t nCount, const void **lpHandles, bool32 bWaitAll, uint32_t dwMilliseconds); @@ -67,6 +56,30 @@ uint32_t WaitForMultipleObjectsEx(unsigned int nCount, const void **lpHandles, bool32 bWaitAll, uint32_t dwMilliseconds, bool32 bAlertable); +int64_t CreateWaitableTimer(struct NtSecurityAttributes *lpTimerAttributes, + bool32 bManualReset, const char16_t *lpTimerName); +bool32 SetWaitableTimer(int64_t hTimer, const int64_t *lpDueTimeAsFtOrNegRela, + int32_t opt_lPeriodMs, NtTimerapcroutine opt_callback, + void *lpArgToCallback, bool32 fUnsleepSystem); + +int32_t SetEvent(int64_t hEvent); +int32_t ResetEvent(int64_t hEvent); +int32_t PulseEvent(int64_t hEvent); + +int32_t ReleaseMutex(int64_t hMutex); +int32_t ReleaseSemaphore(int64_t hSemaphore, int32_t lReleaseCount, + int *lpPreviousCount); + +void InitializeCriticalSection(struct NtCriticalSection *lpCriticalSection); +void EnterCriticalSection(struct NtCriticalSection *lpCriticalSection); +void LeaveCriticalSection(struct NtCriticalSection *lpCriticalSection); +int32_t TryEnterCriticalSection(struct NtCriticalSection *lpCriticalSection); +void DeleteCriticalSection(struct NtCriticalSection *lpCriticalSection); +int32_t InitializeCriticalSectionAndSpinCount( + struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); +uint32_t SetCriticalSectionSpinCount( + struct NtCriticalSection *lpCriticalSection, uint32_t dwSpinCount); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NT_SYNCHRONIZATION_H_ */ diff --git a/libc/nt/thread.h b/libc/nt/thread.h index 560a90b7..fb4378ed 100644 --- a/libc/nt/thread.h +++ b/libc/nt/thread.h @@ -1,6 +1,8 @@ #ifndef COSMOPOLITAN_LIBC_NT_THREADS_H_ #define COSMOPOLITAN_LIBC_NT_THREADS_H_ #include "libc/nt/enum/threadaccess.h" +#include "libc/nt/struct/overlapped.h" +#include "libc/nt/struct/securityattributes.h" #include "libc/nt/thunk/msabi.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -31,6 +33,13 @@ COSMOPOLITAN_C_START_ ╚────────────────────────────────────────────────────────────────────────────│*/ #endif +typedef uint32_t (*NtThreadStartRoutine)(void *lpParameter); + +int64_t CreateThread(struct NtSecurityAttributes *lpThreadAttributes, + size_t dwStackSize, NtThreadStartRoutine lpStartAddress, + void *lpParameter, uint32_t dwCreationFlags, + uint32_t *opt_lpThreadId); + void ExitThread(uint32_t dwExitCode) noreturn; int64_t GetCurrentThread(void); uint32_t GetCurrentThreadId(void); @@ -47,6 +56,10 @@ bool32 SetThreadPriorityBoost(int64_t hThread, bool32 bDisablePriorityBoost); bool32 GetThreadPriorityBoost(int64_t hThread, bool32 *pDisablePriorityBoost); bool32 GetThreadIOPendingFlag(int64_t hThread, bool32 *lpIOIsPending); +bool32 CancelSynchronousIo(int64_t hThread); +bool32 CancelIo(int64_t hFile); +bool32 CancelIoEx(int64_t hFile, struct NtOverlapped opt_lpOverlapped); + #if ShouldUseMsabiAttribute() #include "libc/nt/thunk/thread.inc" #endif /* ShouldUseMsabiAttribute() */ diff --git a/libc/nt/thunk/msabi.h b/libc/nt/thunk/msabi.h index 69edc9ee..89793d50 100644 --- a/libc/nt/thunk/msabi.h +++ b/libc/nt/thunk/msabi.h @@ -9,8 +9,9 @@ * generate code that calls MS ABI functions directly, without needing * to jump through the assembly thunks. */ -#if __GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \ - (__has_attribute(__ms_abi__) || defined(__llvm__)) +#if !defined(__STRICT_ANSI__) && \ + (__GNUC__ * 100 + __GNUC_MINOR__ >= 408 || \ + (__has_attribute(__ms_abi__) || defined(__llvm__))) #define __msabi __attribute__((__ms_abi__)) #endif @@ -18,7 +19,7 @@ * Returns true if header should provide MS-ABI overrides. */ #ifndef ShouldUseMsabiAttribute -#if defined(__msabi) && defined(NDEBUG) && !defined(__STRICT_ANSI__) +#if defined(__msabi) && defined(NDEBUG) && !defined(__PG__) #define ShouldUseMsabiAttribute() 1 #else #define ShouldUseMsabiAttribute() 0 diff --git a/libc/nt/winsock.h b/libc/nt/winsock.h index 23385b7e..1c749ff4 100644 --- a/libc/nt/winsock.h +++ b/libc/nt/winsock.h @@ -1,10 +1,9 @@ #ifndef COSMOPOLITAN_LIBC_NT_WINSOCK_H_ #define COSMOPOLITAN_LIBC_NT_WINSOCK_H_ +#include "libc/nt/struct/guid.h" #include "libc/nt/struct/overlapped.h" #include "libc/nt/struct/pollfd.h" #include "libc/sock/sock.h" -#if !(__ASSEMBLER__ + __LINKER__ + 0) -COSMOPOLITAN_C_START_ #if 0 /* ░▓█████████████████████████████████████████████▓▒ ░█▓░░░░░░░░░▓██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓██▓▒░ @@ -46,9 +45,8 @@ COSMOPOLITAN_C_START_ ╚────────────────────────────────────────────────────────────────────────────│*/ #endif -#define kNtSioSocketCloseNotify 0x9800000Du -#define kNtSioUdpConnreset 0x9800000Cu -#define kNtSioUdpNetreset 0x9800000F +#define kNtWsaFlagOverlapped 0x01 +#define kNtWsaFlagNoHandleInherit 0x80 #define kNtTfDisconnect 0x01 #define kNtTfReuseSocket 0x02 @@ -57,6 +55,67 @@ COSMOPOLITAN_C_START_ #define kNtTfUseSystemThread 0x10 #define kNtTfUseKernelApc 0x20 +#define kNtSoConnectTime 0x700C +#define kNtSoUpdateAcceptContext 0x700B +#define kNtSoUpdateConnectContext 0x7010 + +#define kNtSioAddressListChange 0x28000017u +#define kNtSioAddressListQuery 0x48000016u +#define kNtSioAddressListSort 0xC8000019u +#define kNtSioAssociateHandle 0x88000001u +#define kNtSioEnableCircularQueueing 0x28000002u +#define kNtSioFindRoute 0x48000003u +#define kNtSioFlush 0x28000004u +#define kNtSioGetBroadcastAddress 0x48000005u +#define kNtSioGetExtensionFunctionPointer 0xC8000006u +#define kNtSioGetGroupQos 0xC8000008u +#define kNtSioGetQos 0xC8000007u +#define kNtSioMulticastScope 0x8800000Au +#define kNtSioMultipointLoopback 0x88000009u +#define kNtSioQueryRssProcessorInfo 0x48000025u +#define kNtSioQueryTargetPnpHandle 0x48000018u +#define kNtSioReserved1 0x8800001Au +#define kNtSioReserved2 0x88000021u +#define kNtSioRoutingInterfaceChange 0x88000015u +#define kNtSioRoutingInterfaceQuery 0xC8000014u +#define kNtSioSetGroupQos 0x8800000Cu +#define kNtSioSetQos 0x8800000Bu +#define kNtSioSocketCloseNotify 0x9800000Du +#define kNtSioTranslateHandle 0xC800000Du +#define kNtSioUdpConnreset 0x9800000Cu +#define kNtSioUdpNetreset 0x9800000Fu + +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +#define kNtWsaidAcceptex \ + { \ + 0xB5367DF1, 0xCBAC, 0x11CF, { \ + 0x95, 0xCA, 0x00, 0x80, 0x5F, 0x48, 0xA1, 0x92 \ + } \ + } + +#define kNtWsaidConnectex \ + { \ + 0x25A207B9, 0xDDF3, 0x4660, { \ + 0x8E, 0xE9, 0x76, 0xE5, 0x8C, 0x74, 0x06, 0x3E \ + } \ + } + +#define kNtWsaidDisconnectex \ + { \ + 0x7FDA2E11, 0x8630, 0x436F, { \ + 0xA0, 0x31, 0xF5, 0x36, 0xA6, 0xEE, 0xC1, 0x57 \ + } \ + } + +#define kNtWsaidTransmitfile \ + { \ + 0xB5367DF0, 0xCBAC, 0x11CF, { \ + 0x95, 0xCA, 0x00, 0x80, 0x5F, 0x48, 0xA1, 0x92 \ + } \ + } + enum NtWsaEComparator { COMP_EQUAL, COMP_NOTLESS }; enum NtWsaCompletionType { @@ -96,13 +155,6 @@ struct NtWsaData { char szSystemStatus[129]; }; -struct NtGuid { - uint32_t Data1; - uint16_t Data2; - uint16_t Data3; - uint8_t Data4[8]; -}; - struct NtSocketAddress { struct sockaddr *lpSockaddr; int32_t iSockaddrLength; @@ -512,6 +564,14 @@ void GetAcceptExSockaddrs( struct sockaddr **out_RemoteSockaddr /*[*RemoteSockaddrLength]*/, int *out_RemoteSockaddrLength); +bool32 ConnectEx(int64_t s, const struct sockaddr *name, int namelen, + const void *opt_lpSendBuffer, uint32_t dwSendDataLength, + uint32_t *out_lpdwBytesSent, + struct NtOverlapped *inout_lpOverlapped); + +bool32 DisconnectEx(int64_t s, struct NtOverlapped *inout_opt_lpOverlapped, + uint32_t dwFlags, uint32_t dwReserved); + COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* COSMOPOLITAN_LIBC_NT_WINSOCK_H_ */ diff --git a/libc/runtime/_exit.S b/libc/runtime/_exit.S index 590ba5f4..28192d74 100644 --- a/libc/runtime/_exit.S +++ b/libc/runtime/_exit.S @@ -25,9 +25,6 @@ / Terminates process, ignoring destructors and atexit() handlers. / -/ Normally exit() or quick_exit() is better. This won't even flush -/ stdio streams. Sometimes that makes sense, like after fork(). -/ / @param edi is exit code ∈ [0,256) / @note _exit() is same thing / @asyncsignalsafe diff --git a/libc/runtime/closesymboltable.c b/libc/runtime/closesymboltable.c index 7981892a..56552edc 100644 --- a/libc/runtime/closesymboltable.c +++ b/libc/runtime/closesymboltable.c @@ -25,7 +25,7 @@ * Frees symbol table. * @return 0 on success or -1 on system error */ -int closesymboltable(struct SymbolTable **table) { +int CloseSymbolTable(struct SymbolTable **table) { int rc; struct SymbolTable *t; rc = 0; diff --git a/libc/runtime/directmap.c b/libc/runtime/directmap.c index de1f6850..22e7f972 100644 --- a/libc/runtime/directmap.c +++ b/libc/runtime/directmap.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/calls.h" #include "libc/calls/internal.h" #include "libc/macros.h" #include "libc/nt/memory.h" diff --git a/libc/runtime/executive.S b/libc/runtime/executive.S index 1a6e0467..92ae7d43 100644 --- a/libc/runtime/executive.S +++ b/libc/runtime/executive.S @@ -57,42 +57,14 @@ _executive: ud2 #ifdef __PG__ -/ Enables plaintext function tracing if --ftrace flag passed. -/ -/ The --ftrace CLI arg is removed before main() is called. This -/ code is intended for diagnostic purposes and assumes binaries -/ are trustworthy and stack isn't corrupted. Logging plain text -/ allows program structure to easily be visualized and hotspots -/ identified w/ sed | sort | uniq -c | sort. A compressed trace -/ can be made by appending --ftrace 2>&1 | gzip -4 >trace.gz to -/ the CLI arguments. Have fun. -/ -/ @see libc/runtime/ftrace.greg.c -/ @see libc/crt/crt.S .init.start 800,_init_ftrace push %rdi push %rsi - xor %edx,%edx - loadstr "--ftrace",di - xor %ecx,%ecx -0: inc %ecx - mov (%r13,%rcx,8),%rsi - test %edx,%edx - jz 1f - mov %rsi,-8(%r13,%rcx,8) -1: test %rsi,%rsi - jz 2f - test %edx,%edx - jnz 0b - call tinystrcmp - test %eax,%eax - setz %dl - jmp 0b -2: sub %rdx,%r12 - test %edx,%edx - jz 2f + mov %r12d,%edi + mov %r13,%rsi call ftrace_init -2: pop %rsi + mov %eax,%r12d + pop %rsi pop %rdi .init.end 800,_init_ftrace -#endif /* -pg */ +#endif diff --git a/libc/runtime/findcombinary.c b/libc/runtime/findcombinary.c index e62f56b0..ef6bf33d 100644 --- a/libc/runtime/findcombinary.c +++ b/libc/runtime/findcombinary.c @@ -28,25 +28,25 @@ struct FindComBinary { char buf[PATH_MAX]; }; -static struct FindComBinary findcombinary_; +static struct FindComBinary g_findcombinary; /** * Returns path of binary without debug information, or null. * * @return path to non-debug binary, or -1 w/ errno */ -const char *findcombinary(void) { +const char *FindComBinary(void) { size_t len; const char *p; - if (!findcombinary_.once) { - findcombinary_.once = true; + if (!g_findcombinary.once) { + g_findcombinary.once = true; if ((p = (const char *)getauxval(AT_EXECFN)) && - (len = strlen(p)) < ARRAYLEN(findcombinary_.buf)) { - findcombinary_.res = memcpy(findcombinary_.buf, p, len + 1); - if (len > 4 && memcmp(&findcombinary_.buf[len - 4], ".dbg", 4) == 0) { - findcombinary_.buf[len - 4] = '\0'; + (len = strlen(p)) < ARRAYLEN(g_findcombinary.buf)) { + g_findcombinary.res = memcpy(g_findcombinary.buf, p, len + 1); + if (len > 4 && memcmp(&g_findcombinary.buf[len - 4], ".dbg", 4) == 0) { + g_findcombinary.buf[len - 4] = '\0'; } } } - return findcombinary_.res; + return g_findcombinary.res; } diff --git a/libc/runtime/finddebugbinary.c b/libc/runtime/finddebugbinary.c index f3a9a909..d672c0f6 100644 --- a/libc/runtime/finddebugbinary.c +++ b/libc/runtime/finddebugbinary.c @@ -31,7 +31,7 @@ * * @return path to debug binary, or -1 w/ errno */ -const char *finddebugbinary(void) { +const char *FindDebugBinary(void) { static char buf[PATH_MAX]; if (buf[0]) return &buf[0]; const char *const trybins[] = {program_invocation_name, diff --git a/libc/runtime/ftrace.greg.c b/libc/runtime/ftrace.greg.c index 0ce88056..e5ea3bc5 100644 --- a/libc/runtime/ftrace.greg.c +++ b/libc/runtime/ftrace.greg.c @@ -24,6 +24,7 @@ #include "libc/calls/internal.h" #include "libc/calls/struct/sigset.h" #include "libc/dce.h" +#include "libc/intrin/repmovsb.h" #include "libc/macros.h" #include "libc/nexgen32e/stackframe.h" #include "libc/nt/files.h" @@ -50,7 +51,7 @@ static char g_buf[512]; static const char *g_lastsymbol; static struct SymbolTable *g_symbols; -forceinline int getnestinglevel(struct StackFrame *frame) { +forceinline int GetNestingLevel(struct StackFrame *frame) { int nesting = -2; while (frame) { ++nesting; @@ -81,7 +82,7 @@ privileged interruptfn void ftrace_hook(void) { frame->addr - g_symbols->addr_base)] .name_rva]; if (symbol != g_lastsymbol && - (nesting = getnestinglevel(frame)) * 2 < ARRAYLEN(g_buf) - 3) { + (nesting = GetNestingLevel(frame)) * 2 < ARRAYLEN(g_buf) - 4) { i = 2; j = 0; while (nesting--) { @@ -91,6 +92,7 @@ privileged interruptfn void ftrace_hook(void) { while (i < ARRAYLEN(g_buf) - 2 && symbol[j]) { g_buf[i++] = symbol[j++]; } + g_buf[i++] = '\r'; g_buf[i++] = '\n'; __print(g_buf, i); } @@ -100,13 +102,42 @@ privileged interruptfn void ftrace_hook(void) { } /** - * Installs plaintext function tracer. Do not call. + * Enables plaintext function tracing if --ftrace flag passed. + * + * The --ftrace CLI arg is removed before main() is called. This + * code is intended for diagnostic purposes and assumes binaries + * are trustworthy and stack isn't corrupted. Logging plain text + * allows program structure to easily be visualized and hotspots + * identified w/ sed | sort | uniq -c | sort. A compressed trace + * can be made by appending --ftrace 2>&1 | gzip -4 >trace.gz to + * the CLI arguments. Have fun. + * * @see libc/runtime/_init.S for documentation */ -textstartup void ftrace_init(void) { - g_buf[0] = '+'; - g_buf[1] = ' '; - if ((g_symbols = opensymboltable(finddebugbinary()))) { - __hook(ftrace_hook, g_symbols); +textstartup int ftrace_init(int argc, char *argv[]) { + int i; + bool foundflag; + foundflag = false; + for (i = 1; i <= argc; ++i) { + if (!foundflag) { + if (argv[i]) { + if (strcmp(argv[i], "--ftrace") == 0) { + foundflag = true; + } else if (strcmp(argv[i], "----ftrace") == 0) { + strcpy(argv[i], "--ftrace"); + } + } + } else { + argv[i - 1] = argv[i]; + } } + if (foundflag) { + --argc; + g_buf[0] = '+'; + g_buf[1] = ' '; + if ((g_symbols = OpenSymbolTable(FindDebugBinary()))) { + __hook(ftrace_hook, g_symbols); + } + } + return argc; } diff --git a/libc/runtime/getdosargv.c b/libc/runtime/getdosargv.c index c160b7f0..c48a0c8e 100644 --- a/libc/runtime/getdosargv.c +++ b/libc/runtime/getdosargv.c @@ -25,6 +25,7 @@ #include "libc/str/appendchar.h" #include "libc/str/str.h" #include "libc/str/tpenc.h" +#include "libc/str/utf16.h" /* TODO(jart): Make early-stage data structures happen. */ #undef isspace @@ -39,11 +40,7 @@ struct DosArgv { wint_t wc; }; -static textwindows void decodedosargv(struct DosArgv *st) { - st->s += getutf16(st->s, &st->wc); -} - -static textwindows void appenddosargv(struct DosArgv *st, wint_t wc) { +static textwindows void AppendDosArgv(struct DosArgv *st, wint_t wc) { AppendChar(&st->p, st->pe, wc); } @@ -66,7 +63,7 @@ static textwindows void appenddosargv(struct DosArgv *st, wint_t wc) { * @see libc/runtime/ntspawn.c * @note kudos to Simon Tatham for figuring out quoting behavior */ -textwindows int getdosargv(const char16_t *cmdline, char *buf, size_t size, +textwindows int GetDosArgv(const char16_t *cmdline, char *buf, size_t size, char **argv, size_t max) { bool inquote; size_t i, argc, slashes, quotes; @@ -75,9 +72,9 @@ textwindows int getdosargv(const char16_t *cmdline, char *buf, size_t size, st.p = buf; st.pe = buf + size; argc = 0; - decodedosargv(&st); + st.wc = DecodeNtsUtf16(&st.s); while (st.wc) { - while (st.wc && iswspace(st.wc)) decodedosargv(&st); + while (st.wc && iswspace(st.wc)) st.wc = DecodeNtsUtf16(&st.s); if (!st.wc) break; if (++argc < max) { argv[argc - 1] = st.p < st.pe ? st.p : NULL; @@ -88,27 +85,27 @@ textwindows int getdosargv(const char16_t *cmdline, char *buf, size_t size, if (st.wc == '"' || st.wc == '\\') { slashes = 0; quotes = 0; - while (st.wc == '\\') decodedosargv(&st), slashes++; - while (st.wc == '"') decodedosargv(&st), quotes++; + while (st.wc == '\\') st.wc = DecodeNtsUtf16(&st.s), slashes++; + while (st.wc == '"') st.wc = DecodeNtsUtf16(&st.s), quotes++; if (!quotes) { - while (slashes--) appenddosargv(&st, '\\'); + while (slashes--) AppendDosArgv(&st, '\\'); } else { - while (slashes >= 2) appenddosargv(&st, '\\'), slashes -= 2; - if (slashes) appenddosargv(&st, '"'), quotes--; + while (slashes >= 2) AppendDosArgv(&st, '\\'), slashes -= 2; + if (slashes) AppendDosArgv(&st, '"'), quotes--; if (quotes > 0) { if (!inquote) quotes--; - for (i = 3; i <= quotes + 1; i += 3) appenddosargv(&st, '"'); + for (i = 3; i <= quotes + 1; i += 3) AppendDosArgv(&st, '"'); inquote = (quotes % 3 == 0); } } } else { - appenddosargv(&st, st.wc); - decodedosargv(&st); + AppendDosArgv(&st, st.wc); + st.wc = DecodeNtsUtf16(&st.s); } } - appenddosargv(&st, '\0'); + AppendDosArgv(&st, '\0'); } - appenddosargv(&st, '\0'); + AppendDosArgv(&st, '\0'); if (size) buf[min(st.p - buf, size - 1)] = '\0'; if (max) argv[min(argc, max - 1)] = NULL; return argc; diff --git a/libc/runtime/getdosenviron.h b/libc/runtime/getdosenviron.h index 8d09aedf..be84af80 100644 --- a/libc/runtime/getdosenviron.h +++ b/libc/runtime/getdosenviron.h @@ -22,6 +22,7 @@ #ifndef __STRICT_ANSI__ #include "libc/bits/safemacros.h" #include "libc/str/appendchar.h" +#include "libc/str/str.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) /** @@ -34,25 +35,28 @@ * @param max is the pointer count capacity of envp * @return number of variables decoded, excluding NULL-terminator */ -static inline int getdosenviron(const char16_t *env, char *buf, size_t size, +static inline int GetDosEnviron(const char16_t *env, char *buf, size_t size, char **envp, size_t max) { - const char16_t *s = env; - size_t envc = 0; + wint_t wc; + size_t envc; + char *p, *pe; + bool endstring; + const char16_t *s; + s = env; + envc = 0; if (size) { - wint_t wc; - char *p = buf; - char *pe = buf + size - 1; + p = buf; + pe = buf + size - 1; if (p < pe) { - s += getutf16(s, &wc); + wc = DecodeNtsUtf16(&s); while (wc) { if (++envc < max) { envp[envc - 1] = p < pe ? p : NULL; } - bool endstring; do { AppendChar(&p, pe, wc); endstring = !wc; - s += getutf16(s, &wc); + wc = DecodeNtsUtf16(&s); } while (!endstring); buf[min(p - buf, size - 2)] = u'\0'; } diff --git a/libc/runtime/gothere.greg.c b/libc/runtime/gothere.greg.c index c591b968..30e1cbff 100644 --- a/libc/runtime/gothere.greg.c +++ b/libc/runtime/gothere.greg.c @@ -48,6 +48,7 @@ privileged interruptfn void GOT_HERE(long num) { msg[len++] = 'e'; msg[len++] = ' '; len += int64toarray_radix10(num, &msg[len]); + msg[len++] = '\r'; msg[len++] = '\n'; msg[len] = '\0'; __print(msg, len); diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index 22f4699a..dacac5bb 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -25,7 +25,7 @@ void *__cxa_finalize(void *) hidden; void _executive(int, char **, char **, long (*)[2]) hidden noreturn; void __stack_chk_fail(void) noreturn relegated; void __stack_chk_fail_local(void) noreturn relegated hidden; -int getdosargv(const char16_t *, char *, size_t, char **, size_t) hidden; +int GetDosArgv(const char16_t *, char *, size_t, char **, size_t) hidden; forceinline void AssertNeverCalledWhileTerminating(void) { if (!NoDebug() && (g_runstate & RUNSTATE_TERMINATE)) { diff --git a/libc/runtime/isheap.c b/libc/runtime/isheap.c index 40b289e7..ede96732 100644 --- a/libc/runtime/isheap.c +++ b/libc/runtime/isheap.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/macros.h" #include "libc/runtime/memtrack.h" #include "libc/runtime/runtime.h" @@ -28,12 +29,13 @@ */ bool isheap(void *p) { int x, i; -#if 1 - register intptr_t rsp asm("rsp"); - if ((intptr_t)p >= rsp) return false; -#endif - if ((intptr_t)p <= (intptr_t)_end) return false; - x = (intptr_t)p >> 16; - i = FindMemoryInterval(&_mmi, x); - return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; + register uintptr_t rsp asm("rsp"); + if (ROUNDDOWN(rsp, STACKSIZE) == ROUNDDOWN((intptr_t)p, STACKSIZE)) { + return false; + } else { + if ((intptr_t)p <= (intptr_t)_end) return false; + x = (intptr_t)p >> 16; + i = FindMemoryInterval(&_mmi, x); + return i < _mmi.i && x >= _mmi.p[i].x && x <= _mmi.p[i].y; + } } diff --git a/libc/runtime/mapanon.c b/libc/runtime/mapanon.c index 504080aa..4486f373 100644 --- a/libc/runtime/mapanon.c +++ b/libc/runtime/mapanon.c @@ -24,5 +24,5 @@ void *mapanon(size_t mapsize) { return mmap(NULL, mapsize, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_NONBLOCK | MAP_ANONYMOUS, -1, 0); + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } diff --git a/libc/runtime/memtrack.h b/libc/runtime/memtrack.h index 64adb453..3f7e4274 100644 --- a/libc/runtime/memtrack.h +++ b/libc/runtime/memtrack.h @@ -1,10 +1,12 @@ #ifndef COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ #define COSMOPOLITAN_LIBC_RUNTIME_MEMTRACK_H_ +#include "libc/macros.h" +#include "libc/runtime/runtime.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -#define kAutomapStart 0x0000100000000000 -#define kAutomapSize 0x0000100000000000 +#define kAutomapStart 0x0000100080000000 // asan can't spread its poison here +#define kAutomapSize 0x00000fff80000000 #define kFixedmapStart 0x0000200000000000 struct MemoryIntervals { @@ -12,8 +14,8 @@ struct MemoryIntervals { struct MemoryInterval { int x; int y; - } p[64]; - long h[64]; + } p[128]; + long h[128]; }; extern struct MemoryIntervals _mmi; diff --git a/libc/runtime/opensymboltable.c b/libc/runtime/opensymboltable.c index b915fb64..b2e46f21 100644 --- a/libc/runtime/opensymboltable.c +++ b/libc/runtime/opensymboltable.c @@ -30,9 +30,9 @@ /** * Maps debuggable binary into memory and indexes symbol addresses. * - * @return object freeable with closesymboltable(), or NULL w/ errno + * @return object freeable with CloseSymbolTable(), or NULL w/ errno */ -struct SymbolTable *opensymboltable(const char *filename) { +struct SymbolTable *OpenSymbolTable(const char *filename) { unsigned i, j; struct SymbolTable *t; const Elf64_Sym *symtab, *sym; @@ -56,7 +56,7 @@ struct SymbolTable *opensymboltable(const char *filename) { t->count = j; carsort1000(t->count, (void *)t->symbols); } else { - closesymboltable(&t); + CloseSymbolTable(&t); } return t == MAP_FAILED ? NULL : t; } diff --git a/libc/runtime/printmemoryintervals.c b/libc/runtime/printmemoryintervals.c index 9689ffab..573e4782 100644 --- a/libc/runtime/printmemoryintervals.c +++ b/libc/runtime/printmemoryintervals.c @@ -28,18 +28,18 @@ void PrintMemoryIntervals(int fd, const struct MemoryIntervals *mm) { int i, frames, maptally, gaptally; maptally = 0; gaptally = 0; - (dprintf)(fd, "%s%zd%s\n", "mm->i == ", mm->i, ";"); + (dprintf)(fd, "%s%zd%s\r\n", "mm->i == ", mm->i, ";"); for (i = 0; i < mm->i; ++i) { if (i && mm->p[i].x != mm->p[i - 1].y + 1) { frames = mm->p[i].x - mm->p[i - 1].y - 1; gaptally += frames; - (dprintf)(fd, "%s%,zd%s\n", "/* ", frames, " */"); + (dprintf)(fd, "%s%,zd%s\r\n", "/* ", frames, " */"); } frames = mm->p[i].y + 1 - mm->p[i].x; maptally += frames; - (dprintf)(fd, "%s%3u%s0x%08x,0x%08x%s%,zd%s\n", "mm->p[", i, "]=={", + (dprintf)(fd, "%s%3u%s0x%08x,0x%08x%s%,zd%s\r\n", "mm->p[", i, "]=={", mm->p[i].x, mm->p[i].y, "}; /* ", frames, " */"); } - (dprintf)(fd, "%s%,zd%s%,zd%s\n\n", "/* ", maptally, " frames mapped w/ ", + (dprintf)(fd, "%s%,zd%s%,zd%s\r\n\r\n", "/* ", maptally, " frames mapped w/ ", gaptally, " frames gapped */"); } diff --git a/libc/runtime/runtime.h b/libc/runtime/runtime.h index e0e0c3ac..fcb11eb8 100644 --- a/libc/runtime/runtime.h +++ b/libc/runtime/runtime.h @@ -41,7 +41,6 @@ void *mapanon(size_t) vallocesque attributeallocsize((1)); int setjmp(jmp_buf) libcesque returnstwice paramsnonnull(); void longjmp(jmp_buf, int) libcesque noreturn paramsnonnull(); void exit(int) noreturn; -void quick_exit(int) noreturn; void _exit(int) libcesque noreturn; void _Exit(int) libcesque noreturn; long _setstack(void *, void *, ...); diff --git a/libc/runtime/symbols.h b/libc/runtime/symbols.h index 9e9211e9..9608336e 100644 --- a/libc/runtime/symbols.h +++ b/libc/runtime/symbols.h @@ -1,27 +1,8 @@ -/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│ -│vi: set net ft=c ts=8 sts=2 sw=2 fenc=utf-8 :vi│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ #ifndef COSMOPOLITAN_LIBC_SYMBOLS_H_ #define COSMOPOLITAN_LIBC_SYMBOLS_H_ -#if !(__ASSEMBLER__ + __LINKER__ + 0) #include "libc/elf/elf.h" #include "libc/runtime/ezmap.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ struct Symbol { @@ -46,13 +27,11 @@ struct SymbolTable { struct Symbol symbols[]; }; -struct SymbolTable *getsymboltable(void); -const char *findcombinary(void); -const char *finddebugbinary(void); -struct SymbolTable *opensymboltable(const char *) nodiscard; -int closesymboltable(struct SymbolTable **); -const struct Symbol *bisectsymbol(struct SymbolTable *, intptr_t, int64_t *); -const char *getsymbolname(struct SymbolTable *, const struct Symbol *); +struct SymbolTable *GetSymbolTable(void); +const char *FindComBinary(void); +const char *FindDebugBinary(void); +struct SymbolTable *OpenSymbolTable(const char *) nodiscard; +int CloseSymbolTable(struct SymbolTable **); void __hook(void (*)(void), struct SymbolTable *); COSMOPOLITAN_C_END_ diff --git a/libc/runtime/winmain.greg.c b/libc/runtime/winmain.greg.c index 7e6f6073..5a26b133 100644 --- a/libc/runtime/winmain.greg.c +++ b/libc/runtime/winmain.greg.c @@ -38,8 +38,8 @@ static struct CmdExe { bool result; struct OldNtConsole { - unsigned codepage; - unsigned mode; + uint32_t codepage; + uint32_t mode; int64_t handle; } oldin, oldout; } g_cmdexe; @@ -82,6 +82,8 @@ static textwindows void NormalizeCmdExe(void) { SetConsoleCP(kNtCpUtf8); GetConsoleMode(handle, &g_cmdexe.oldin.mode); SetConsoleMode(handle, g_cmdexe.oldin.mode | kNtEnableProcessedInput | + kNtEnableEchoInput | kNtEnableLineInput | + kNtEnableWindowInput | kNtEnableVirtualTerminalInput); } if (GetFileType((handle = hstdout)) == kNtFileTypeChar || @@ -92,7 +94,10 @@ static textwindows void NormalizeCmdExe(void) { SetConsoleOutputCP(kNtCpUtf8); GetConsoleMode(handle, &g_cmdexe.oldout.mode); SetConsoleMode(handle, g_cmdexe.oldout.mode | kNtEnableProcessedOutput | - kNtEnableVirtualTerminalProcessing); + kNtEnableWrapAtEolOutput | + (NtGetVersion() >= kNtVersionWindows10 + ? kNtEnableVirtualTerminalProcessing + : 0)); } } } @@ -136,11 +141,11 @@ textwindows int WinMain(void *hInstance, void *hPrevInstance, *(/*unconst*/ int *)&hostos = WINDOWS; cmd16 = GetCommandLine(); env16 = GetEnvironmentStrings(); - count = getdosargv(cmd16, argblock, ARG_MAX, argarray, 512); + count = GetDosArgv(cmd16, argblock, ARG_MAX, argarray, 512); for (i = 0; argarray[0][i]; ++i) { if (argarray[0][i] == '\\') argarray[0][i] = '/'; } - getdosenviron(env16, envblock, ENV_MAX, envarray, 512); + GetDosEnviron(env16, envblock, ENV_MAX, envarray, 512); FreeEnvironmentStrings(env16); _executive(count, argarray, envarray, auxarray); } diff --git a/libc/str/decodentsutf16.c b/libc/str/decodentsutf16.c new file mode 100644 index 00000000..8e2db23c --- /dev/null +++ b/libc/str/decodentsutf16.c @@ -0,0 +1,33 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "libc/str/utf16.h" + +wint_t DecodeNtsUtf16(const char16_t **s) { + wint_t x, y; + if (!IsUcs2((x = *(*s)++))) { + if ((y = *(*s)++)) { + x = MergeUtf16(x, y); + } else { + x = 0; + } + } + return x; +} diff --git a/libc/str/hextoint.c b/libc/str/hextoint.c new file mode 100644 index 00000000..03a9e101 --- /dev/null +++ b/libc/str/hextoint.c @@ -0,0 +1,32 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int hextoint(int c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } else { + return 0; + } +} diff --git a/libc/str/isalnum.c b/libc/str/isalnum.c new file mode 100644 index 00000000..1b94dc17 --- /dev/null +++ b/libc/str/isalnum.c @@ -0,0 +1,25 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isalnum(int c) { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'z'); +} diff --git a/libc/log/backtrace.c b/libc/str/isalpha.c similarity index 94% rename from libc/log/backtrace.c rename to libc/str/isalpha.c index 459c8813..64a6d94a 100644 --- a/libc/log/backtrace.c +++ b/libc/str/isalpha.c @@ -17,8 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/backtrace.h" +#include "libc/str/str.h" -void backtrace(FILE *f) { - showbacktrace(f, __builtin_frame_address(0)); +int isalpha(int c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z'); } diff --git a/tool/build/lib/init.c b/libc/str/isascii.c similarity index 96% rename from tool/build/lib/init.c rename to libc/str/isascii.c index 3170e726..10b0994e 100644 --- a/tool/build/lib/init.c +++ b/libc/str/isascii.c @@ -17,7 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "tool/build/lib/machine.h" -void InitMachine(struct Machine *m) { +int isascii(int c) { + return 0x00 <= c && c <= 0x7F; } diff --git a/libc/str/isblank.c b/libc/str/isblank.c new file mode 100644 index 00000000..f1f43ec8 --- /dev/null +++ b/libc/str/isblank.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isblank(int c) { + return c == ' ' || c == '\t'; +} diff --git a/libc/str/iscntrl.c b/libc/str/iscntrl.c new file mode 100644 index 00000000..423965e9 --- /dev/null +++ b/libc/str/iscntrl.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iscntrl(int c) { + return (0x00 <= c && c <= 0x1F) || c == 0x7F; +} diff --git a/libc/str/isdigit.c b/libc/str/isdigit.c new file mode 100644 index 00000000..208528c0 --- /dev/null +++ b/libc/str/isdigit.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isdigit(int c) { + return '0' <= c && c <= '9'; +} diff --git a/libc/str/isgraph.c b/libc/str/isgraph.c new file mode 100644 index 00000000..94d0b0c4 --- /dev/null +++ b/libc/str/isgraph.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isgraph(int c) { + return 0x21 <= c && c <= 0x7E; +} diff --git a/libc/str/islower.c b/libc/str/islower.c new file mode 100644 index 00000000..2578519b --- /dev/null +++ b/libc/str/islower.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int islower(int c) { + return 'a' <= c && c <= 'z'; +} diff --git a/libc/str/isprint.c b/libc/str/isprint.c new file mode 100644 index 00000000..5e091465 --- /dev/null +++ b/libc/str/isprint.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isprint(int c) { + return 0x20 <= c && c <= 0x7E; +} diff --git a/libc/str/ispunct.c b/libc/str/ispunct.c new file mode 100644 index 00000000..7ae42d2c --- /dev/null +++ b/libc/str/ispunct.c @@ -0,0 +1,25 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int ispunct(int c) { + return (0x21 <= c && c <= 0x7E) && !('0' <= c && c <= '9') && + !('A' <= c && c <= 'Z') && !('a' <= c && c <= 'z'); +} diff --git a/libc/str/isspace.c b/libc/str/isspace.c new file mode 100644 index 00000000..e5873bad --- /dev/null +++ b/libc/str/isspace.c @@ -0,0 +1,25 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isspace(int c) { + return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\f' || + c == '\v'; +} diff --git a/libc/str/isupper.c b/libc/str/isupper.c new file mode 100644 index 00000000..a374f695 --- /dev/null +++ b/libc/str/isupper.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isupper(int c) { + return 'A' <= c && c <= 'Z'; +} diff --git a/libc/str/iswalnum.c b/libc/str/iswalnum.c new file mode 100644 index 00000000..ea098a73 --- /dev/null +++ b/libc/str/iswalnum.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswalnum(wint_t wc) { + return isalnum(wc); +} diff --git a/libc/str/iswalpha.c b/libc/str/iswalpha.c new file mode 100644 index 00000000..225f6f66 --- /dev/null +++ b/libc/str/iswalpha.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswalpha(wint_t wc) { + return isalpha(wc); +} diff --git a/libc/str/iswblank.c b/libc/str/iswblank.c new file mode 100644 index 00000000..91a32d70 --- /dev/null +++ b/libc/str/iswblank.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswblank(wint_t wc) { + return isblank(wc); +} diff --git a/libc/str/iswcntrl.c b/libc/str/iswcntrl.c new file mode 100644 index 00000000..4b67fae7 --- /dev/null +++ b/libc/str/iswcntrl.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswcntrl(wint_t wc) { + return (0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F); +} diff --git a/libc/str/iswctype.c b/libc/str/iswctype.c index a4a48ab0..d41a2a55 100644 --- a/libc/str/iswctype.c +++ b/libc/str/iswctype.c @@ -20,33 +20,71 @@ #include "libc/macros.h" #include "libc/str/str.h" -static const struct WcTypes { - char name[8]; - wctype_t type; -} wctypes[] = {{"alnum", 0x006}, {"alpha", 0x002}, {"blank", 0x080}, - {"cntrl", 0x100}, {"digit", 0x004}, {"graph", 0x200}, - {"lower", 0x020}, {"print", 0x010}, {"punct", 0x300}, - {"space", 0x001}, {"upper", 0x040}, {"xdigit", 0x008}}; +#define ALNUM 1 +#define ALPHA 2 +#define BLANK 3 +#define CNTRL 4 +#define DIGIT 5 +#define GRAPH 6 +#define LOWER 7 +#define PRINT 8 +#define PUNCT 9 +#define SPACE 10 +#define UPPER 11 +#define XDIGIT 12 -wctype_t wctype(const char *s) { - char name[8]; - strncpy(name, s, sizeof(name)); - for (unsigned i = 0; i < ARRAYLEN(wctypes); ++i) { - if (memcmp(name, wctypes[i].name, sizeof(name)) == 0) { - return wctypes[i].type; +static const struct { + char name[7]; + char type; +} kWcTypes[] = { + {"alnum", ALNUM}, {"alpha", ALPHA}, {"blank", BLANK}, {"cntrl", CNTRL}, + {"digit", DIGIT}, {"graph", GRAPH}, {"lower", LOWER}, {"print", PRINT}, + {"punct", PUNCT}, {"space", SPACE}, {"upper", UPPER}, {"xdigit", XDIGIT}, +}; + +static int CompareStrings(const char *l, const char *r) { + size_t i = 0; + while (l[i] == r[i] && r[i]) ++i; + return (l[i] & 0xff) - (r[i] & 0xff); +} + +wctype_t wctype(const char *name) { + unsigned i; + for (i = 0; i < ARRAYLEN(kWcTypes); ++i) { + if (CompareStrings(name, kWcTypes[i].name) == 0) { + return kWcTypes[i].type; } } return 0; } -int iswctype(wint_t c, wctype_t type) { - if (c < 128) { - if (!(type & ~0xff)) { - return kCtype[(unsigned char)type]; - } - if (type == 0x100) return iscntrl((unsigned char)c); - if (type == 0x200) return isgraph((unsigned char)c); - if (type == 0x300) return ispunct((unsigned char)c); +int iswctype(wint_t wc, wctype_t type) { + switch (type) { + case ALNUM: + return iswalnum(wc); + case ALPHA: + return iswalpha(wc); + case BLANK: + return iswblank(wc); + case CNTRL: + return iswcntrl(wc); + case DIGIT: + return iswdigit(wc); + case GRAPH: + return iswgraph(wc); + case LOWER: + return iswlower(wc); + case PRINT: + return iswprint(wc); + case PUNCT: + return iswpunct(wc); + case SPACE: + return iswspace(wc); + case UPPER: + return iswupper(wc); + case XDIGIT: + return iswxdigit(wc); + default: + return 0; } - return 0; } diff --git a/libc/str/iswdigit.c b/libc/str/iswdigit.c new file mode 100644 index 00000000..c8b9f247 --- /dev/null +++ b/libc/str/iswdigit.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswdigit(wint_t wc) { + return isdigit(wc); +} diff --git a/libc/str/iswgraph.c b/libc/str/iswgraph.c new file mode 100644 index 00000000..d900b0f2 --- /dev/null +++ b/libc/str/iswgraph.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswgraph(wint_t wc) { + return isgraph(wc); +} diff --git a/libc/str/iswlower.c b/libc/str/iswlower.c new file mode 100644 index 00000000..2b360779 --- /dev/null +++ b/libc/str/iswlower.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswlower(wint_t wc) { + return islower(wc); +} diff --git a/libc/str/iswprint.c b/libc/str/iswprint.c new file mode 100644 index 00000000..4475214d --- /dev/null +++ b/libc/str/iswprint.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswprint(wint_t wc) { + return !iswcntrl(wc); +} diff --git a/libc/str/iswpunct.c b/libc/str/iswpunct.c new file mode 100644 index 00000000..c8a8ab05 --- /dev/null +++ b/libc/str/iswpunct.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswpunct(wint_t wc) { + return ispunct(wc); +} diff --git a/libc/str/iswspace.c b/libc/str/iswspace.c new file mode 100644 index 00000000..71f0a255 --- /dev/null +++ b/libc/str/iswspace.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswspace(wint_t wc) { + return isspace(wc); +} diff --git a/libc/str/iswupper.c b/libc/str/iswupper.c new file mode 100644 index 00000000..faea172c --- /dev/null +++ b/libc/str/iswupper.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswupper(wint_t wc) { + return isupper(wc); +} diff --git a/libc/str/iswxdigit.c b/libc/str/iswxdigit.c new file mode 100644 index 00000000..2a70c05f --- /dev/null +++ b/libc/str/iswxdigit.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int iswxdigit(wint_t wc) { + return isxdigit(wc); +} diff --git a/libc/str/isxdigit.c b/libc/str/isxdigit.c new file mode 100644 index 00000000..7ef928d7 --- /dev/null +++ b/libc/str/isxdigit.c @@ -0,0 +1,25 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int isxdigit(int c) { + return ('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || + ('a' <= c && c <= 'f'); +} diff --git a/libc/str/str.h b/libc/str/str.h index 7472292c..4090fc73 100644 --- a/libc/str/str.h +++ b/libc/str/str.h @@ -8,7 +8,6 @@ COSMOPOLITAN_C_START_ fourth age telecommunications */ extern const uint8_t gperf_downcase[256]; -extern const uint8_t kCtype[256]; extern const uint8_t kToLower[256]; extern const uint8_t kToUpper[256]; extern const uint16_t kToLower16[256]; @@ -64,6 +63,7 @@ void *isnotplaintext(const void *, size_t) nothrow nocallback nosideeffect; #define INVALID_CODEPOINT 0xfffd +wint_t DecodeNtsUtf16(const char16_t **); unsigned getutf16(const char16_t *, wint_t *); int pututf16(char16_t *, size_t, wint_t, bool); int iswalnum(wint_t); @@ -78,8 +78,8 @@ int iswupper(wint_t); int iswxdigit(wint_t); int iswpunct(wint_t); int iswprint(wint_t); -unsigned towlower(unsigned); -unsigned towupper(unsigned); +wint_t towlower(wint_t); +wint_t towupper(wint_t); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § strings ─╬─│┼ diff --git a/libc/str/thompike.h b/libc/str/thompike.h index 982e77f9..b8763b10 100644 --- a/libc/str/thompike.h +++ b/libc/str/thompike.h @@ -5,7 +5,7 @@ #define ThomPikeCont(x) (((x)&0b11000000) == 0b10000000) #define ThomPikeByte(x) ((x) & (((1 << ThomPikeMsb(x)) - 1) | 3)) #define ThomPikeLen(x) (7 - ThomPikeMsb(x)) -#define ThomPikeMsb(x) ((x) < 252 ? bsr(~(x)&0xff) : 1) +#define ThomPikeMsb(x) (((x)&0xff) < 252 ? bsr(~(x)&0xff) : 1) #define ThomPikeMerge(x, y) ((x) << 6 | (y)&0b00111111) #endif /* COSMOPOLITAN_LIBC_STR_THOMPIKE_H_ */ diff --git a/libc/str/tolower.c b/libc/str/tolower.c new file mode 100644 index 00000000..8e71c6bd --- /dev/null +++ b/libc/str/tolower.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int tolower(int c) { + return 'A' <= c && c <= 'Z' ? c + ('a' - 'A') : c; +} diff --git a/libc/str/toupper.c b/libc/str/toupper.c new file mode 100644 index 00000000..3217f0bd --- /dev/null +++ b/libc/str/toupper.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +int toupper(int c) { + return 'a' <= c && c <= 'z' ? c - ('a' - 'A') : c; +} diff --git a/libc/str/towlower.c b/libc/str/towlower.c new file mode 100644 index 00000000..48bb7db7 --- /dev/null +++ b/libc/str/towlower.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +wint_t towlower(wint_t wc) { + return tolower(wc); +} diff --git a/libc/str/towupper.c b/libc/str/towupper.c new file mode 100644 index 00000000..0d91e7c5 --- /dev/null +++ b/libc/str/towupper.c @@ -0,0 +1,24 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" + +wint_t towupper(wint_t wc) { + return toupper(wc); +} diff --git a/libc/str/tpenc.S b/libc/str/tpenc.S index 328f1048..a1c07e34 100644 --- a/libc/str/tpenc.S +++ b/libc/str/tpenc.S @@ -24,19 +24,15 @@ / / @param edi is int to encode / @return rax is word-encoded byte buffer -/ @clob edi,rax,flags / @note invented on a napkin in a new jersey diner tpenc: .leafprologue .profilable - push %rcx - push %rdx mov %edi,%edi xor %eax,%eax cmp $127,%edi jbe 3f bsr %edi,%ecx - ezlea kTpenc,dx - mov -7*(1+1)(%rdx,%rcx,2),%ecx + mov kTpenc-7*(1+1)(,%rcx,2),%ecx 1: mov %edi,%edx shr $6,%edi and $0b00111111,%dl @@ -47,12 +43,11 @@ tpenc: .leafprologue jnz 1b 2: or %ch,%al 3: or %rdi,%rax - pop %rdx - pop %rcx .leafepilogue .endfn tpenc,globl .rodata + .align 4 kTpenc: .rept 4 # MSB≤10 (0x7FF) .byte 1,0b11000000 # len,mark .endr @@ -68,4 +63,5 @@ kTpenc: .rept 4 # MSB≤10 (0x7FF) .rept 6 # MSB≤31 (0xffffffff) .byte 5,0b11111100 # len,mark .endr + .zero 2 .endobj kTpenc diff --git a/libc/str/tpenc.h b/libc/str/tpenc.h index a6cb8ded..0400a1bd 100644 --- a/libc/str/tpenc.h +++ b/libc/str/tpenc.h @@ -16,7 +16,7 @@ uint64_t tpenc(int32_t) pureconst; asm("call\ttpenc" \ : "=a"(Buf), "+D"(Code) \ : /* inputs */ \ - : "cc"); \ + : "rcx", "rdx", "cc"); \ } \ Buf; \ }) diff --git a/libc/str/tprecode16to8.c b/libc/str/tprecode16to8.c index 30070a56..4d4e63db 100644 --- a/libc/str/tprecode16to8.c +++ b/libc/str/tprecode16to8.c @@ -19,7 +19,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/conv/conv.h" #include "libc/str/str.h" -#include "libc/str/tpencode.h" +#include "libc/str/tpenc.h" +#include "libc/str/utf16.h" /** * Transcodes UTF-16 to UTF-8. @@ -30,19 +31,25 @@ * @return number of bytes written excluding NUL */ size_t tprecode16to8(char *dst, size_t dstsize, const char16_t *src) { - size_t i = 0; + size_t i; + uint64_t w; + wint_t x, y; + i = 0; if (dstsize) { for (;;) { - wint_t wc; - src += abs(getutf16(src, &wc)); - if (!wc || dstsize == 1) { - dst[i] = '\0'; - break; + if (!(x = *src++)) break; + if (IsUtf16Cont(x)) continue; + if (!IsUcs2(x)) { + if (!(y = *src++)) break; + x = MergeUtf16(x, y); + } + w = tpenc(x); + while (w && i + 1 < dstsize) { + dst[i++] = w & 0xFF; + w >>= 8; } - size_t got = abs(tpencode(&dst[i], dstsize, wc, false)); - dstsize -= got; - i += got; } + dst[i] = 0; } return i; } diff --git a/libc/str/tprecode8to16.c b/libc/str/tprecode8to16.c index 3418898f..3ed2436f 100644 --- a/libc/str/tprecode8to16.c +++ b/libc/str/tprecode8to16.c @@ -17,9 +17,9 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/conv/conv.h" #include "libc/str/str.h" -#include "libc/str/tpdecode.h" +#include "libc/str/thompike.h" +#include "libc/str/utf16.h" /** * Transcodes UTF-8 to UTF-16. @@ -30,19 +30,31 @@ * @return number of shorts written excluding NUL */ size_t tprecode8to16(char16_t *dst, size_t dstsize, const char *src) { - size_t i = 0; + size_t i; + unsigned n; + uint64_t w; + wint_t x, y; + i = 0; if (dstsize) { for (;;) { - wint_t wc; - src += abs(tpdecode(src, &wc)); - if (!wc || dstsize == 1) { - dst[i] = u'\0'; - break; + if (!(x = *src++ & 0xff)) break; + if (ThomPikeCont(x)) continue; + if (!isascii(x)) { + n = ThomPikeLen(x); + x = ThomPikeByte(x); + while (--n) { + if (!(y = *src++ & 0xff)) goto stop; + x = ThomPikeMerge(x, y); + } + } + w = EncodeUtf16(x); + while (w && i + 1 < dstsize) { + dst[i++] = w & 0xFFFF; + w >>= 16; } - size_t got = abs(pututf16(&dst[i], dstsize, wc, false)); - dstsize -= got; - i += got; } + stop: + dst[i] = 0; } return i; } diff --git a/libc/str/utf16.h b/libc/str/utf16.h index b3ed77c1..ab7beba5 100644 --- a/libc/str/utf16.h +++ b/libc/str/utf16.h @@ -8,5 +8,13 @@ #define IsUcs2(wc) (((wc)&UTF16_MASK) != UTF16_MOAR) #define IsUtf16Cont(wc) (((wc)&UTF16_MASK) == UTF16_CONT) #define MergeUtf16(lo, hi) ((((lo)-0xD800) << 10) + ((hi)-0xDC00) + 0x10000) +#define EncodeUtf16(wc) \ + (likely((0x0000 <= (wc) && (wc) <= 0xFFFF) || \ + (0xE000 <= (wc) && (wc) <= 0xFFFF)) \ + ? (wc) \ + : 0x10000 <= (wc) && (wc) <= 0x10FFFF \ + ? (((((wc)-0x10000) >> 10) + 0xD800) | \ + ((((wc)-0x10000) & 1023) + 0xDC00) << 16) \ + : 0xFFFD) #endif /* COSMOPOLITAN_LIBC_STR_UTF16_H_ */ diff --git a/libc/stubs/xnu.S b/libc/stubs/xnu.S index 2df54fc0..f6827244 100644 --- a/libc/stubs/xnu.S +++ b/libc/stubs/xnu.S @@ -21,5 +21,5 @@ .source __FILE__ _start_xnu: - jmp abort + ud2 .endfn _start_xnu,weak diff --git a/libc/sysv/consts.sh b/libc/sysv/consts.sh index c0183ac5..adc2ee92 100755 --- a/libc/sysv/consts.sh +++ b/libc/sysv/consts.sh @@ -214,16 +214,16 @@ syscon open O_APPEND 0x0400 8 8 8 0x00000004 # bsd consensus & kNtFi syscon open O_CREAT 0x40 0x0200 0x0200 0x0200 0x00000040 # bsd consensus & NT faked as Linux syscon open O_EXCL 0x80 0x0800 0x0800 0x0800 0x00000080 # bsd consensus & NT faked as Linux syscon open O_TRUNC 0x0200 0x0400 0x0400 0x0400 0x00000200 # bsd consensus & NT faked as Linux +syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0x02000000 # kNtFileFlagBackupSemantics syscon open O_DIRECT 0x4000 0 0x010000 0 0x00200000 # kNtFileFlagNoBuffering>>8 +syscon open O_CLOEXEC 0x080000 0x01000000 0x100000 0x010000 0x00080000 # NT faked as Linux +syscon open O_TMPFILE 0x410000 0 0 0 0x04000100 # Linux 3.11+ (c. 2013) & kNtFileAttributeTemporary|kNtFileFlagDeleteOnClose +syscon open O_SPARSE 0 0 0 0 0x00040000 # we invented it syscon open O_NDELAY 0x0800 4 4 4 0 # bsd consensus & kNtFileFlagWriteThrough>>8 → 0x00800000 (???) syscon open O_NONBLOCK 0x0800 4 4 4 0 # bsd consensus -syscon open O_CLOEXEC 0x080000 0x01000000 0x100000 0x010000 0x00080000 # NT faked as Linux -syscon open O_SPARSE 0 0 0 0 0x00040000 # we invented it -syscon open O_TMPFILE 0x410000 0 0 0 0x04000100 # Linux 3.11+ (c. 2013) & kNtFileAttributeTemporary|kNtFileFlagDeleteOnClose syscon open O_ASYNC 0x2000 0x40 0x40 0x40 0 # bsd consensus syscon open O_NOFOLLOW 0x020000 0x0100 0x0100 0x0100 0 # bsd consensus syscon open O_SYNC 0x101000 0x80 0x80 0x80 0 # bsd consensus -syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0 syscon open O_NOCTTY 0x0100 0x020000 0x8000 0x8000 0 # used for remote viewing (default behavior on freebsd) syscon open O_DSYNC 0x1000 0x400000 0 0x80 0 syscon open O_RSYNC 0x101000 0 0 0x80 0 @@ -236,19 +236,19 @@ syscon compat O_LARGEFILE 0 0 0 0 0 syscon compat MAP_FILE 0 0 0 0 0 # consensus syscon mmap MAP_SHARED 1 1 1 1 1 # forced consensus & faked nt syscon mmap MAP_PRIVATE 2 2 2 2 2 # forced consensus & faked nt -syscon mmap MAP_FIXED 16 16 16 16 16 # unix consensus; openbsd appears to forbid; faked nt +syscon mmap MAP_FIXED 0x10 0x10 0x10 0x10 0x10 # unix consensus; openbsd appears to forbid; faked nt syscon mmap MAP_ANONYMOUS 0x20 0x1000 0x1000 0x1000 0x20 # bsd consensus; faked nt -syscon compat MAP_ANON 0x20 0x1000 0x1000 0x1000 0x20 # bsd consensus; faked nt syscon mmap MAP_NORESERVE 0x4000 0x40 0 0 0 # Linux calls it "reserve"; NT calls it "commit"? which is default? syscon mmap MAP_GROWSDOWN 0x0100 0 0x0400 0x0400 0x100000 # MAP_STACK on BSD; MEM_TOP_DOWN on NT -syscon compat MAP_STACK 0x020000 0 0x0400 0x4000 0x100000 syscon mmap MAP_HUGETLB 0x040000 0 0 0 0x80000000 # kNtSecLargePages syscon mmap MAP_HUGE_MASK 63 0 0 0 0 syscon mmap MAP_HUGE_SHIFT 26 0 0 0 0 syscon mmap MAP_LOCKED 0x2000 0 0 0 0 -syscon mmap MAP_NONBLOCK 0x010000 0 0 0 0 +syscon mmap MAP_NONBLOCK 0x10000 0 0 0 0 syscon mmap MAP_POPULATE 0x8000 0 0 0 0 # can avoid madvise(MADV_WILLNEED) on private file mapping syscon mmap MAP_TYPE 15 0 0 0 0 # what is it +syscon compat MAP_ANON 0x20 0x1000 0x1000 0x1000 0x20 # bsd consensus; faked nt +syscon compat MAP_STACK 0x020000 0 0x0400 0x4000 0x100000 syscon compat MAP_EXECUTABLE 0x1000 0 0 0 0 # ignored syscon compat MAP_DENYWRITE 0x0800 0 0 0 0 syscon compat MAP_32BIT 0x40 0 0x080000 0 0 # iffy @@ -581,6 +581,35 @@ syscon so SO_TIMESTAMPING 37 0 0 0 0 syscon so SO_TIMESTAMPNS 35 0 0 0 0 syscon so SO_WIFI_STATUS 41 0 0 0 0 +syscon sol SOL_IP 0 0 0 0 0 # consensus +syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff # bsd+nt consensus (todo: what's up with ipproto_icmp overlap) +syscon sol SOL_TCP 6 6 6 6 6 # consensus +syscon sol SOL_UDP 17 17 17 17 17 # consensus +syscon sol SOL_IPV6 41 41 41 41 41 +syscon sol SOL_ICMPV6 58 58 58 58 0 +syscon sol SOL_AAL 265 0 0 0 0 +syscon sol SOL_ALG 279 0 0 0 0 +syscon sol SOL_ATM 264 0 0 0 0 +syscon sol SOL_BLUETOOTH 274 0 0 0 0 +syscon sol SOL_CAIF 278 0 0 0 0 +syscon sol SOL_DCCP 269 0 0 0 0 +syscon sol SOL_DECNET 261 0 0 0 0 +syscon sol SOL_IRDA 266 0 0 0 0 +syscon sol SOL_IUCV 277 0 0 0 0 +syscon sol SOL_KCM 281 0 0 0 0 +syscon sol SOL_LLC 268 0 0 0 0 +syscon sol SOL_NETBEUI 267 0 0 0 0 +syscon sol SOL_NETLINK 270 0 0 0 0 +syscon sol SOL_NFC 280 0 0 0 0 +syscon sol SOL_PACKET 263 0 0 0 0 +syscon sol SOL_PNPIPE 275 0 0 0 0 +syscon sol SOL_PPPOL2TP 273 0 0 0 0 +syscon sol SOL_RAW 255 0 0 0 0 +syscon sol SOL_RDS 276 0 0 0 0 +syscon sol SOL_RXRPC 272 0 0 0 0 +syscon sol SOL_TIPC 271 0 0 0 0 +syscon sol SOL_X25 262 0 0 0 0 + # {set,get}sockopt(fd, level=IPPROTO_TCP, X, ...) # » most elite of all tuning groups # @@ -591,7 +620,7 @@ syscon so SO_WIFI_STATUS 41 0 0 0 0 # @see https://www.iana.org/assignments/tcp-parameters/tcp-parameters.txt # # group name GNU/Systemd XNU's Not UNIX FreeBSD OpenBSD XENIX Commentary -syscon tcp TCP_NODELAY 1 1 1 1 1 # consensus +syscon tcp TCP_NODELAY 1 1 1 1 1 # strong consensus for disabling nagle's algorithm syscon tcp TCP_MAXSEG 2 2 2 2 0 # reduces tcp segment size; see also tcp offloading syscon tcp TCP_FASTOPEN 23 261 0x0401 0 15 # reduces roundtrips; for listener; Linux 3.7+ (c. 2012) / or is windows it 0x22? /proc/sys/net/ipv4/tcp_fastopen TODO(jart): MSG_FASTOPEN syscon tcp TCP_KEEPIDLE 4 0 0x100 0 0 # keepalives @@ -621,6 +650,39 @@ syscon tcp TCP_REPAIR_OPTIONS 22 0 0 0 0 # what is it syscon tcp TCP_REPAIR_QUEUE 20 0 0 0 0 # what is it syscon tcp TCP_THIN_LINEAR_TIMEOUTS 16 0 0 0 0 # what is it +syscon iproto IPPROTO_IP 0 0 0 0 0 # consensus +syscon iproto IPPROTO_ICMP 1 1 1 1 1 # consensus +syscon iproto IPPROTO_TCP 6 6 6 6 6 # consensus +syscon iproto IPPROTO_UDP 17 17 17 17 17 # consensus +syscon iproto IPPROTO_RAW 255 255 255 255 255 # consensus +syscon iproto IPPROTO_HOPOPTS 0 0 0 0 0 # consensus +syscon iproto IPPROTO_IDP 22 22 22 22 22 # consensus +syscon iproto IPPROTO_IGMP 2 2 2 2 2 # consensus +syscon iproto IPPROTO_PUP 12 12 12 12 12 # consensus +syscon iproto IPPROTO_AH 51 51 51 51 0 # unix consensus +syscon iproto IPPROTO_DSTOPTS 60 60 60 60 0 # unix consensus +syscon iproto IPPROTO_EGP 8 8 8 8 0 # unix consensus +syscon iproto IPPROTO_ENCAP 98 98 98 98 0 # unix consensus +syscon iproto IPPROTO_ESP 50 50 50 50 0 # unix consensus +syscon iproto IPPROTO_FRAGMENT 44 44 44 44 0 # unix consensus +syscon iproto IPPROTO_GRE 47 47 47 47 0 # unix consensus +syscon iproto IPPROTO_ICMPV6 58 58 58 58 0 # unix consensus +syscon iproto IPPROTO_IPIP 4 4 4 4 0 # unix consensus +syscon iproto IPPROTO_IPV6 41 41 41 41 0 # unix consensus +syscon iproto IPPROTO_NONE 59 59 59 59 0 # unix consensus +syscon iproto IPPROTO_PIM 103 103 103 103 0 # unix consensus +syscon iproto IPPROTO_ROUTING 43 43 43 43 0 # unix consensus +syscon iproto IPPROTO_RSVP 46 46 46 46 0 # unix consensus +syscon iproto IPPROTO_TP 29 29 29 29 0 # unix consensus +syscon iproto IPPROTO_MPLS 137 0 137 137 0 +syscon iproto IPPROTO_MTP 92 92 92 0 0 +syscon iproto IPPROTO_SCTP 132 132 132 0 0 +syscon iproto IPPROTO_MH 135 0 135 0 0 +syscon iproto IPPROTO_UDPLITE 136 0 136 0 0 +syscon iproto IPPROTO_BEETPH 94 0 0 0 0 +syscon iproto IPPROTO_COMP 108 0 0 0 0 +syscon iproto IPPROTO_DCCP 33 0 0 0 0 + syscon misc EXTA 14 0x4b00 0x4b00 0x4b00 0 # bsd consensus syscon misc EXTB 15 0x9600 0x9600 0x9600 0 # bsd consensus syscon misc ERA 0x02002c 45 45 0 0 @@ -932,39 +994,6 @@ syscon icmp6 ICMP6_ROUTER_RENUMBERING 138 138 138 138 0 # unix consen syscon icmp6 ICMP6_RR_RESULT_FLAGS_FORBIDDEN 0x0100 0x0100 0x0100 0x0100 0 # unix consensus syscon icmp6 ICMP6_RR_RESULT_FLAGS_OOB 0x0200 0x0200 0x0200 0x0200 0 # unix consensus -syscon iproto IPPROTO_HOPOPTS 0 0 0 0 0 # consensus -syscon iproto IPPROTO_ICMP 1 1 1 1 1 # consensus -syscon iproto IPPROTO_IDP 22 22 22 22 22 # consensus -syscon iproto IPPROTO_IGMP 2 2 2 2 2 # consensus -syscon iproto IPPROTO_IP 0 0 0 0 0 # consensus -syscon iproto IPPROTO_PUP 12 12 12 12 12 # consensus -syscon iproto IPPROTO_RAW 255 255 255 255 255 # consensus -syscon iproto IPPROTO_TCP 6 6 6 6 6 # consensus -syscon iproto IPPROTO_UDP 17 17 17 17 17 # consensus -syscon iproto IPPROTO_AH 51 51 51 51 0 # unix consensus -syscon iproto IPPROTO_DSTOPTS 60 60 60 60 0 # unix consensus -syscon iproto IPPROTO_EGP 8 8 8 8 0 # unix consensus -syscon iproto IPPROTO_ENCAP 98 98 98 98 0 # unix consensus -syscon iproto IPPROTO_ESP 50 50 50 50 0 # unix consensus -syscon iproto IPPROTO_FRAGMENT 44 44 44 44 0 # unix consensus -syscon iproto IPPROTO_GRE 47 47 47 47 0 # unix consensus -syscon iproto IPPROTO_ICMPV6 58 58 58 58 0 # unix consensus -syscon iproto IPPROTO_IPIP 4 4 4 4 0 # unix consensus -syscon iproto IPPROTO_IPV6 41 41 41 41 0 # unix consensus -syscon iproto IPPROTO_NONE 59 59 59 59 0 # unix consensus -syscon iproto IPPROTO_PIM 103 103 103 103 0 # unix consensus -syscon iproto IPPROTO_ROUTING 43 43 43 43 0 # unix consensus -syscon iproto IPPROTO_RSVP 46 46 46 46 0 # unix consensus -syscon iproto IPPROTO_TP 29 29 29 29 0 # unix consensus -syscon iproto IPPROTO_MPLS 137 0 137 137 0 -syscon iproto IPPROTO_MTP 92 92 92 0 0 -syscon iproto IPPROTO_SCTP 132 132 132 0 0 -syscon iproto IPPROTO_MH 135 0 135 0 0 -syscon iproto IPPROTO_UDPLITE 136 0 136 0 0 -syscon iproto IPPROTO_BEETPH 94 0 0 0 0 -syscon iproto IPPROTO_COMP 108 0 0 0 0 -syscon iproto IPPROTO_DCCP 33 0 0 0 0 - syscon sio SIOCADDMULTI 0x8931 0x80206931 0x80206931 0x80206931 0 # bsd consensus syscon sio SIOCATMARK 0x8905 0x40047307 0x40047307 0x40047307 0 # bsd consensus syscon sio SIOCDELMULTI 0x8932 0x80206932 0x80206932 0x80206932 0 # bsd consensus @@ -1413,35 +1442,6 @@ syscon msg MSG_RST 0x1000 0 0 0 0 syscon msg MSG_STAT 11 0 0 0 0 syscon msg MSG_SYN 0x0400 0 0 0 0 -syscon sol SOL_IP 0 0 0 0 0 # consensus -syscon sol SOL_SOCKET 1 0xffff 0xffff 0xffff 0xffff # bsd+nt consensus -syscon sol SOL_TCP 6 6 6 6 6 -syscon sol SOL_UDP 17 17 17 17 17 -syscon sol SOL_IPV6 41 41 41 41 41 -syscon sol SOL_ICMPV6 58 58 58 58 0 -syscon sol SOL_AAL 265 0 0 0 0 -syscon sol SOL_ALG 279 0 0 0 0 -syscon sol SOL_ATM 264 0 0 0 0 -syscon sol SOL_BLUETOOTH 274 0 0 0 0 -syscon sol SOL_CAIF 278 0 0 0 0 -syscon sol SOL_DCCP 269 0 0 0 0 -syscon sol SOL_DECNET 261 0 0 0 0 -syscon sol SOL_IRDA 266 0 0 0 0 -syscon sol SOL_IUCV 277 0 0 0 0 -syscon sol SOL_KCM 281 0 0 0 0 -syscon sol SOL_LLC 268 0 0 0 0 -syscon sol SOL_NETBEUI 267 0 0 0 0 -syscon sol SOL_NETLINK 270 0 0 0 0 -syscon sol SOL_NFC 280 0 0 0 0 -syscon sol SOL_PACKET 263 0 0 0 0 -syscon sol SOL_PNPIPE 275 0 0 0 0 -syscon sol SOL_PPPOL2TP 273 0 0 0 0 -syscon sol SOL_RAW 255 0 0 0 0 -syscon sol SOL_RDS 276 0 0 0 0 -syscon sol SOL_RXRPC 272 0 0 0 0 -syscon sol SOL_TIPC 271 0 0 0 0 -syscon sol SOL_X25 262 0 0 0 0 - syscon in IN_LOOPBACKNET 127 127 127 127 0 # unix consensus syscon in IN_ACCESS 1 0 0 0 0 syscon in IN_ALL_EVENTS 0x0fff 0 0 0 0 @@ -2477,9 +2477,9 @@ syscon termios NETGRAPHDISC 0 0 0x6 0 -1 # boop syscon termios H4DISC 0 0 0x7 0 -1 # boop syscon termios ISIG 0b0000000000000001 0b0000000010000000 0b0000000010000000 0b0000000010000000 0b0000000000000001 # termios.c_lflag|=ISIG makes Ctrl-C, Ctrl-\, etc. generate signals -syscon termios ICANON 0b0000000000000010 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000000010 # termios.c_lflag&=~ICANON disables 1960's version of gnu readline +syscon termios ICANON 0b0000000000000010 0b0000000100000000 0b0000000100000000 0b0000000100000000 0b0000000000000010 # termios.c_lflag&=~ICANON disables 1960's version of gnu readline (see also VMIN) syscon termios XCASE 0b0000000000000100 0 0 16777216 0b0000000000000100 # termios.c_lflag -syscon termios ECHO 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 # termios.c_lflag&=~ECHO is for passwords +syscon termios ECHO 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 # termios.c_lflag&=~ECHO is for passwords and raw mode syscon termios ECHOE 0b0000000000010000 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000010000 # termios.c_lflag syscon termios ECHOK 0b0000000000100000 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000100000 # termios.c_lflag syscon termios ECHONL 0b0000000001000000 0b0000000000010000 0b0000000000010000 0b0000000000010000 0b0000000001000000 # termios.c_lflag @@ -2493,8 +2493,8 @@ syscon termios PENDIN 0b0100000000000000 536870912 536870912 536870912 0b syscon termios IEXTEN 0b1000000000000000 0b0000010000000000 0b0000010000000000 0b0000010000000000 0b1000000000000000 # termios.c_lflag&=~IEXTEN disables platform input processing magic syscon termios EXTPROC 65536 0b0000100000000000 0b0000100000000000 0b0000100000000000 65536 # termios.c_lflag -syscon termios IGNBRK 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_iflag it's complicated UNIXCONSENSUS -syscon termios BRKINT 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 # termios.c_iflag it's complicated UNIXCONSENSUS +syscon termios IGNBRK 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 0b0000000000000001 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS +syscon termios BRKINT 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 0b0000000000000010 # termios.c_iflag it's complicated, uart only? UNIXCONSENSUS syscon termios IGNPAR 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 0b0000000000000100 # termios.c_iflag|=IGNPAR ignores parity and framing errors; see PARMRK UNIXCONSENSUS syscon termios PARMRK 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 0b0000000000001000 # termios.c_iflag|=PARMRK passes-through parity bit UNIXCONSENSUS syscon termios INPCK 0b0000000000010000 0b0000000000010000 0b0000000000010000 0b0000000000010000 0b0000000000010000 # termios.c_iflag|=INPCK enables parity checking UNIXCONSENSUS @@ -2554,8 +2554,8 @@ syscon termios VQUIT 1 9 9 9 0 # termios.c_cc[VQUIT]=𝑥 syscon termios VERASE 2 3 3 3 0 # termios.c_cc[VERASE]=𝑥 syscon termios VKILL 3 5 5 5 0 # termios.c_cc[VKILL]=𝑥 syscon termios VEOF 4 0 0 0 0 # termios.c_cc[VEOF]=𝑥 -syscon termios VTIME 5 17 17 17 0 # termios.c_cc[VTIME]=𝑥 yields 𝑥×𝟷𝟶𝟶ms non-canonical read timeout -syscon termios VMIN 6 16 16 16 0 # termios.c_cc[VMIN]=𝑥 +syscon termios VTIME 5 17 17 17 0 # termios.c_cc[VTIME]=𝑥 sets non-canonical read timeout to 𝑥×𝟷𝟶𝟶ms which is needed when entering escape sequences manually with the escape key +syscon termios VMIN 6 16 16 16 0 # termios.c_cc[VMIN]=𝑥 in non-canonical mode can be set to 0 for non-blocking reads, 1 for single character raw mode reads, or higher to buffer syscon termios VSWTC 7 0 0 0 0 # termios.c_cc[VSWTC]=𝑥 syscon termios VSTART 8 12 12 12 0 # termios.c_cc[VSTART]=𝑥 syscon termios VSTOP 9 13 13 13 0 # termios.c_cc[VSTOP]=𝑥 diff --git a/libc/sysv/consts/O_DIRECTORY.s b/libc/sysv/consts/O_DIRECTORY.s index 7d95cf9b..72a3af84 100644 --- a/libc/sysv/consts/O_DIRECTORY.s +++ b/libc/sysv/consts/O_DIRECTORY.s @@ -1,2 +1,2 @@ .include "libc/sysv/consts/syscon.inc" -.syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0 +.syscon open O_DIRECTORY 0x010000 0x100000 0x020000 0x020000 0x02000000 diff --git a/libc/sysv/consts/ipproto.h b/libc/sysv/consts/ipproto.h index 08527221..f904d30e 100644 --- a/libc/sysv/consts/ipproto.h +++ b/libc/sysv/consts/ipproto.h @@ -41,38 +41,39 @@ hidden extern const long IPPROTO_UDPLITE; COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ -#define IPPROTO_AH SYMBOLIC(IPPROTO_AH) -#define IPPROTO_BEETPH SYMBOLIC(IPPROTO_BEETPH) -#define IPPROTO_COMP SYMBOLIC(IPPROTO_COMP) -#define IPPROTO_DCCP SYMBOLIC(IPPROTO_DCCP) -#define IPPROTO_DSTOPTS SYMBOLIC(IPPROTO_DSTOPTS) -#define IPPROTO_EGP SYMBOLIC(IPPROTO_EGP) -#define IPPROTO_ENCAP SYMBOLIC(IPPROTO_ENCAP) -#define IPPROTO_ESP SYMBOLIC(IPPROTO_ESP) -#define IPPROTO_FRAGMENT SYMBOLIC(IPPROTO_FRAGMENT) -#define IPPROTO_GRE SYMBOLIC(IPPROTO_GRE) -#define IPPROTO_HOPOPTS SYMBOLIC(IPPROTO_HOPOPTS) +#define IPPROTO_IP LITERALLY(0) #define IPPROTO_ICMP LITERALLY(1) -#define IPPROTO_ICMPV6 SYMBOLIC(IPPROTO_ICMPV6) -#define IPPROTO_IDP SYMBOLIC(IPPROTO_IDP) -#define IPPROTO_IGMP SYMBOLIC(IPPROTO_IGMP) -#define IPPROTO_IP LITERALLY(0) -#define IPPROTO_IPIP SYMBOLIC(IPPROTO_IPIP) -#define IPPROTO_IPV6 SYMBOLIC(IPPROTO_IPV6) -#define IPPROTO_MAX SYMBOLIC(IPPROTO_MAX) -#define IPPROTO_MH SYMBOLIC(IPPROTO_MH) -#define IPPROTO_MPLS SYMBOLIC(IPPROTO_MPLS) -#define IPPROTO_MTP SYMBOLIC(IPPROTO_MTP) -#define IPPROTO_NONE SYMBOLIC(IPPROTO_NONE) -#define IPPROTO_PIM SYMBOLIC(IPPROTO_PIM) -#define IPPROTO_PUP SYMBOLIC(IPPROTO_PUP) -#define IPPROTO_RAW LITERALLY(255) -#define IPPROTO_ROUTING SYMBOLIC(IPPROTO_ROUTING) -#define IPPROTO_RSVP SYMBOLIC(IPPROTO_RSVP) -#define IPPROTO_SCTP SYMBOLIC(IPPROTO_SCTP) -#define IPPROTO_TCP LITERALLY(6) -#define IPPROTO_TP SYMBOLIC(IPPROTO_TP) -#define IPPROTO_UDP LITERALLY(17) -#define IPPROTO_UDPLITE SYMBOLIC(IPPROTO_UDPLITE) +#define IPPROTO_TCP LITERALLY(6) +#define IPPROTO_UDP LITERALLY(17) +#define IPPROTO_RAW LITERALLY(255) + +#define IPPROTO_AH SYMBOLIC(IPPROTO_AH) +#define IPPROTO_BEETPH SYMBOLIC(IPPROTO_BEETPH) +#define IPPROTO_COMP SYMBOLIC(IPPROTO_COMP) +#define IPPROTO_DCCP SYMBOLIC(IPPROTO_DCCP) +#define IPPROTO_DSTOPTS SYMBOLIC(IPPROTO_DSTOPTS) +#define IPPROTO_EGP SYMBOLIC(IPPROTO_EGP) +#define IPPROTO_ENCAP SYMBOLIC(IPPROTO_ENCAP) +#define IPPROTO_ESP SYMBOLIC(IPPROTO_ESP) +#define IPPROTO_FRAGMENT SYMBOLIC(IPPROTO_FRAGMENT) +#define IPPROTO_GRE SYMBOLIC(IPPROTO_GRE) +#define IPPROTO_HOPOPTS SYMBOLIC(IPPROTO_HOPOPTS) +#define IPPROTO_ICMPV6 SYMBOLIC(IPPROTO_ICMPV6) +#define IPPROTO_IDP SYMBOLIC(IPPROTO_IDP) +#define IPPROTO_IGMP SYMBOLIC(IPPROTO_IGMP) +#define IPPROTO_IPIP SYMBOLIC(IPPROTO_IPIP) +#define IPPROTO_IPV6 SYMBOLIC(IPPROTO_IPV6) +#define IPPROTO_MAX SYMBOLIC(IPPROTO_MAX) +#define IPPROTO_MH SYMBOLIC(IPPROTO_MH) +#define IPPROTO_MPLS SYMBOLIC(IPPROTO_MPLS) +#define IPPROTO_MTP SYMBOLIC(IPPROTO_MTP) +#define IPPROTO_NONE SYMBOLIC(IPPROTO_NONE) +#define IPPROTO_PIM SYMBOLIC(IPPROTO_PIM) +#define IPPROTO_PUP SYMBOLIC(IPPROTO_PUP) +#define IPPROTO_ROUTING SYMBOLIC(IPPROTO_ROUTING) +#define IPPROTO_RSVP SYMBOLIC(IPPROTO_RSVP) +#define IPPROTO_SCTP SYMBOLIC(IPPROTO_SCTP) +#define IPPROTO_TP SYMBOLIC(IPPROTO_TP) +#define IPPROTO_UDPLITE SYMBOLIC(IPPROTO_UDPLITE) #endif /* COSMOPOLITAN_LIBC_SYSV_CONSTS_IPPROTO_H_ */ diff --git a/libc/sysv/systemfive.S b/libc/sysv/systemfive.S index 24b0666a..5108b382 100644 --- a/libc/sysv/systemfive.S +++ b/libc/sysv/systemfive.S @@ -90,7 +90,6 @@ systemfive: .quad 0 .endobj systemfive,globl,hidden .previous - .Lanchorpoint: systemfive.linux: movswl %ax,%eax @@ -104,24 +103,31 @@ systemfive.linux: cmp $-4095,%rax jae systemfive.error ret + .endfn systemfive.linux,globl,hidden systemfive.error: neg %eax / 𝑠𝑙𝑖𝑑𝑒 + .endfn systemfive.error systemfive.errno: mov %eax,errno(%rip) - pushpop -1,%rax + push $-1 + pop %rax stc ret + .endfn systemfive.errno systemfive.enosys: mov ENOSYS(%rip),%eax jmp systemfive.errno + .endfn systemfive.enosys systemfive.openbsd: shr $48,%rax jmp systemfive.bsd + .endfn systemfive.openbsd,globl,hidden systemfive.freebsd: shr $32,%rax movzwl %ax,%eax / 𝑠𝑙𝑖𝑑𝑒 + .endfn systemfive.freebsd,globl,hidden systemfive.bsd: cmp $0xfff,%ax jae systemfive.enosys @@ -132,26 +138,21 @@ systemfive.bsd: pop %rbp jc systemfive.errno ret + .endfn systemfive.bsd systemfive.xnu: -/ Do this -/ 0x35022721530005 -/ │└┴┴─┐ -/ └┐ ├┬┐ -/ 0x00000002000153 +/ do this to rax +/ 0x????????2153???? +/ │└┴┴─┐ +/ └┐ ├┬┐ +/ 0x0000000002000153 mov %eax,%r11d - shr $0x10,%eax - and $0xfff,%eax - shr $0x1c,%r11d - shl $0x18,%r11d + shr $4*7,%r11d + shl $4*6,%r11d + shl $4*1,%eax + shr $4*5,%eax or %r11d,%eax jmp systemfive.bsd - .endfn systemfive.errno,globl,hidden - .endfn systemfive.error,globl,hidden - .endfn systemfive.enosys,globl,hidden .endfn systemfive.xnu,globl,hidden - .endfn systemfive.freebsd,globl,hidden - .endfn systemfive.openbsd,globl,hidden - .endfn systemfive.linux,globl,hidden .previous / Initializes System Five system call support. diff --git a/libc/testlib/testmain.c b/libc/testlib/testmain.c index a10d526b..5dda0cc9 100644 --- a/libc/testlib/testmain.c +++ b/libc/testlib/testmain.c @@ -51,11 +51,14 @@ static testonly void PrintUsage(int rc, FILE *f) { static testonly void GetOpts(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "?hb")) != -1) { + while ((opt = getopt(argc, argv, "?hbv")) != -1) { switch (opt) { case 'b': runbenchmarks_ = true; break; + case 'v': + ++g_loglevel; + break; case '?': case 'h': PrintUsage(EXIT_SUCCESS, stdout); diff --git a/libc/testlib/testmem.c b/libc/testlib/testmem.c index b66d4ad5..d7d7de0b 100644 --- a/libc/testlib/testmem.c +++ b/libc/testlib/testmem.c @@ -33,6 +33,8 @@ #include "libc/sysv/consts/prot.h" #include "libc/testlib/testlib.h" +/* TODO(jart): DELETE */ + struct TestMemoryStack g_testmem; struct TestMemoryStack g_testmem_trash; static struct TestAllocation g_testmem_scratch[2][8]; @@ -86,16 +88,7 @@ static struct TestAllocation talloc(size_t n) { } } -static void testmem_fini(void) { - CHECK_EQ(0, g_testmem.i); - free_s(&g_testmem.p); - while (g_testmem_trash.i) { - testmem_destroy(testmem_pop(&g_testmem_trash)); - } -} - static textstartup void testmem_init(void) { - atexit(testmem_fini); g_testmem.p = g_testmem_scratch[0]; g_testmem.n = ARRAYLEN(g_testmem_scratch[0]); g_testmem_trash.p = g_testmem_scratch[1]; diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 68448e0c..4836ee85 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -17,10 +17,10 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/safemacros.h" #include "libc/bits/weaken.h" #include "libc/calls/internal.h" #include "libc/errno.h" +#include "libc/macros.h" #include "libc/nt/process.h" #include "libc/stdio/stdio.h" #include "libc/testlib/testlib.h" @@ -36,10 +36,8 @@ void testlib_finish(void) { } noreturn void testlib_abort(void) { - int rc; testlib_finish(); - rc = min(255, g_testlib_failed); - quick_exit(rc); /* so we don't run __testmemory_fini() */ + exit(MIN(255, g_testlib_failed)); unreachable; } diff --git a/libc/time/localtime.c b/libc/time/localtime.c index e1ba128d..a19afd2c 100644 --- a/libc/time/localtime.c +++ b/libc/time/localtime.c @@ -28,7 +28,19 @@ #define TM_ZONE tm_zone #define INITIALIZE(x) x = 0 +STATIC_YOINK("zip_uri_support"); +STATIC_YOINK("usr/share/zoneinfo/Beijing"); +STATIC_YOINK("usr/share/zoneinfo/Berlin"); +STATIC_YOINK("usr/share/zoneinfo/Boulder"); +STATIC_YOINK("usr/share/zoneinfo/Chicago"); STATIC_YOINK("usr/share/zoneinfo/GST"); +STATIC_YOINK("usr/share/zoneinfo/Honolulu"); +STATIC_YOINK("usr/share/zoneinfo/Israel"); +STATIC_YOINK("usr/share/zoneinfo/Japan"); +STATIC_YOINK("usr/share/zoneinfo/London"); +STATIC_YOINK("usr/share/zoneinfo/Melbourne"); +STATIC_YOINK("usr/share/zoneinfo/New_York"); +STATIC_YOINK("usr/share/zoneinfo/Singapore"); /* clang-format off */ /* @@ -807,15 +819,15 @@ getrule(strp, rulep) static time_t transtime(janfirst, year, rulep, offset) -const time_t janfirst; -const int year; -register const struct rule * const rulep; -const int32_t offset; + const time_t janfirst; + const int year; + register const struct rule * const rulep; + const int32_t offset; { - register int leapyear; - register time_t value; - register int i; - int d, m1, yy0, yy1, yy2, dow; + register int leapyear; + register time_t value; + register int i; + int d, m1, yy0, yy1, yy2, dow; INITIALIZE(value); leapyear = isleap(year); @@ -857,9 +869,9 @@ const int32_t offset; */ m1 = (rulep->r_mon + 9) % 12 + 1; yy0 = (rulep->r_mon <= 2) ? (year - 1) : year; - yy1 = div100int64(yy0); - yy2 = rem100int64(yy0); - dow = (div10int64(26 * m1 - 2) + + yy1 = yy0 / 100; + yy2 = yy0 % 100; + dow = ((26 * m1 - 2) / 10 + 1 + yy2 + yy2 / 4 + yy1 / 4 - 2 * yy1) % 7; if (dow < 0) dow += DAYSPERWEEK; diff --git a/libc/time/strftime.c b/libc/time/strftime.c index 21d43ad1..5c056070 100644 --- a/libc/time/strftime.c +++ b/libc/time/strftime.c @@ -16,6 +16,7 @@ │ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED │ │ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" #include "libc/fmt/fmt.h" #include "libc/macros.h" @@ -381,6 +382,7 @@ static char *strftime_timefmt(char *p, const char *pe, const char *format, */ size_t strftime(char *s, size_t size, const char *f, const struct tm *t) { char *p; + assert(t); p = strftime_timefmt(s, s + size, f, t); if (p < s + size) { *p = '\0'; diff --git a/libc/time/time.h b/libc/time/time.h index 772ea20c..8e5203c5 100644 --- a/libc/time/time.h +++ b/libc/time/time.h @@ -27,9 +27,8 @@ int sleep(uint32_t); int usleep(uint32_t); int nanosleep(const struct timespec *, struct timespec *); unsigned alarm(unsigned); -int getitimer(int, struct itimerval *) paramsnonnull(); -int setitimer(int, const struct itimerval *, struct itimerval *) - paramsnonnull((2)); +int getitimer(int, struct itimerval *); +int setitimer(int, const struct itimerval *, struct itimerval *); void tzset(void); struct tm *gmtime(const int64_t *); diff --git a/libc/time/time.mk b/libc/time/time.mk index ba1d8daf..cd5b0067 100644 --- a/libc/time/time.mk +++ b/libc/time/time.mk @@ -3,28 +3,11 @@ PKGS += LIBC_TIME -LIBC_TIME_ZONEINFOS = \ - Beijing \ - Berlin \ - Boulder \ - Chicago \ - GST \ - Honolulu \ - Israel \ - Japan \ - London \ - Melbourne \ - New_York \ - Singapore \ - Sydney \ - UTC - LIBC_TIME_ARTIFACTS += LIBC_TIME_A +LIBC_TIME_ZONEINFOS = $(wildcard usr/share/zoneinfo/*) LIBC_TIME = $(LIBC_TIME_A_DEPS) $(LIBC_TIME_A) LIBC_TIME_A = o/$(MODE)/libc/time/time.a -LIBC_TIME_A_FILES := \ - $(wildcard libc/time/struct/*) \ - $(wildcard libc/time/*) +LIBC_TIME_A_FILES := $(wildcard libc/time/struct/*) $(wildcard libc/time/*) LIBC_TIME_A_HDRS := $(filter %.h,$(LIBC_TIME_A_FILES)) LIBC_TIME_A_SRCS_S = $(filter %.S,$(LIBC_TIME_A_FILES)) LIBC_TIME_A_SRCS_C = $(filter %.c,$(LIBC_TIME_A_FILES)) @@ -37,7 +20,8 @@ LIBC_TIME_A_OBJS = \ $(LIBC_TIME_A_SRCS:%=o/$(MODE)/%.zip.o) \ $(LIBC_TIME_A_SRCS_S:%.S=o/$(MODE)/%.o) \ $(LIBC_TIME_A_SRCS_C:%.c=o/$(MODE)/%.o) \ - o//libc/time/zoneinfo.o + $(LIBC_TIME_A_SRCS_C:%.c=o/$(MODE)/%.o) \ + $(LIBC_TIME_ZONEINFOS:%=o/$(MODE)/%.zip.o) LIBC_TIME_A_CHECKS = \ $(LIBC_TIME_A).pkg \ @@ -87,10 +71,6 @@ o/$(MODE)/libc/time/now.o: \ OVERRIDE_CFLAGS += \ -O3 -o//libc/time/zoneinfo.o: \ - $(LIBC_TIME_ZONEINFOS:%=usr/share/zoneinfo/%) - @build/zipobj $(OUTPUT_OPTION) $(LIBC_TIME_ZONEINFOS:%=usr/share/zoneinfo/%) - LIBC_TIME_LIBS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x))) LIBC_TIME_SRCS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x)_SRCS)) LIBC_TIME_HDRS = $(foreach x,$(LIBC_TIME_ARTIFACTS),$($(x)_HDRS)) diff --git a/libc/tinymath/ceil.S b/libc/tinymath/ceil.S index 54f6b77a..d9ff7777 100644 --- a/libc/tinymath/ceil.S +++ b/libc/tinymath/ceil.S @@ -23,14 +23,14 @@ tinymath_ceil: .leafprologue .profilable - movsd .Llol(%rip),%xmm1 - movsd .Lcat(%rip),%xmm2 + movsd nan(%rip),%xmm1 + movsd sig(%rip),%xmm2 andpd %xmm0,%xmm1 comisd %xmm1,%xmm2 jbe 1f cvttsd2siq %xmm0,%rax pxor %xmm1,%xmm1 - movsd .Lmog(%rip),%xmm2 + movsd one(%rip),%xmm2 cvtsi2sdq %rax,%xmm1 cmpnlesd %xmm1,%xmm0 andpd %xmm2,%xmm0 @@ -39,16 +39,9 @@ tinymath_ceil: .endfn tinymath_ceil,globl .alias tinymath_ceil,ceil - .rodata.cst16 -.Llol: .long 4294967295 - .long 2147483647 - .long 0 - .long 0 - .rodata.cst8 -.Lcat: .long 0 - .long 1127219200 -.Lmog: .long 0 - .long 1072693248 +nan: .double nan +sig: .double 0x0010000000000000 +one: .double 1 / vroundsd $_MM_FROUND_TO_POS_INF|_MM_FROUND_NO_EXC,%xmm0,%xmm0,%xmm0 diff --git a/libc/tzfile.h b/libc/tzfile.h index 0f054156..6db15509 100644 --- a/libc/tzfile.h +++ b/libc/tzfile.h @@ -91,7 +91,7 @@ struct tzhead { */ #ifndef TZ_MAX_TIMES -#define TZ_MAX_TIMES 2000 +#define TZ_MAX_TIMES 1200 #endif /* !defined TZ_MAX_TIMES */ #ifndef TZ_MAX_TYPES diff --git a/libc/unicode/blocks.txt b/libc/unicode/blocks.txt new file mode 100644 index 00000000..56877db1 --- /dev/null +++ b/libc/unicode/blocks.txt @@ -0,0 +1,344 @@ +# Blocks-13.0.0.txt +# Date: 2019-07-10, 19:06:00 GMT [KW] +# © 2019 Unicode®, Inc. +# For terms of use, see http://www.unicode.org/terms_of_use.html +# +# Unicode Character Database +# For documentation, see http://www.unicode.org/reports/tr44/ +# +# Format: +# Start Code..End Code; Block Name + +# ================================================ + +# Note: When comparing block names, casing, whitespace, hyphens, +# and underbars are ignored. +# For example, "Latin Extended-A" and "latin extended a" are equivalent. +# For more information on the comparison of property values, +# see UAX #44: http://www.unicode.org/reports/tr44/ +# +# All block ranges start with a value where (cp MOD 16) = 0, +# and end with a value where (cp MOD 16) = 15. In other words, +# the last hexadecimal digit of the start of range is ...0 +# and the last hexadecimal digit of the end of range is ...F. +# This constraint on block ranges guarantees that allocations +# are done in terms of whole columns, and that code chart display +# never involves splitting columns in the charts. +# +# All code points not explicitly listed for Block +# have the value No_Block. + +# Property: Block +# +# @missing: 0000..10FFFF; No_Block + +0000..007F; Basic Latin +0080..00FF; Latin-1 Supplement +0100..017F; Latin Extended-A +0180..024F; Latin Extended-B +0250..02AF; IPA Extensions +02B0..02FF; Spacing Modifier Letters +0300..036F; Combining Diacritical Marks +0370..03FF; Greek and Coptic +0400..04FF; Cyrillic +0500..052F; Cyrillic Supplement +0530..058F; Armenian +0590..05FF; Hebrew +0600..06FF; Arabic +0700..074F; Syriac +0750..077F; Arabic Supplement +0780..07BF; Thaana +07C0..07FF; NKo +0800..083F; Samaritan +0840..085F; Mandaic +0860..086F; Syriac Supplement +08A0..08FF; Arabic Extended-A +0900..097F; Devanagari +0980..09FF; Bengali +0A00..0A7F; Gurmukhi +0A80..0AFF; Gujarati +0B00..0B7F; Oriya +0B80..0BFF; Tamil +0C00..0C7F; Telugu +0C80..0CFF; Kannada +0D00..0D7F; Malayalam +0D80..0DFF; Sinhala +0E00..0E7F; Thai +0E80..0EFF; Lao +0F00..0FFF; Tibetan +1000..109F; Myanmar +10A0..10FF; Georgian +1100..11FF; Hangul Jamo +1200..137F; Ethiopic +1380..139F; Ethiopic Supplement +13A0..13FF; Cherokee +1400..167F; Unified Canadian Aboriginal Syllabics +1680..169F; Ogham +16A0..16FF; Runic +1700..171F; Tagalog +1720..173F; Hanunoo +1740..175F; Buhid +1760..177F; Tagbanwa +1780..17FF; Khmer +1800..18AF; Mongolian +18B0..18FF; Unified Canadian Aboriginal Syllabics Extended +1900..194F; Limbu +1950..197F; Tai Le +1980..19DF; New Tai Lue +19E0..19FF; Khmer Symbols +1A00..1A1F; Buginese +1A20..1AAF; Tai Tham +1AB0..1AFF; Combining Diacritical Marks Extended +1B00..1B7F; Balinese +1B80..1BBF; Sundanese +1BC0..1BFF; Batak +1C00..1C4F; Lepcha +1C50..1C7F; Ol Chiki +1C80..1C8F; Cyrillic Extended-C +1C90..1CBF; Georgian Extended +1CC0..1CCF; Sundanese Supplement +1CD0..1CFF; Vedic Extensions +1D00..1D7F; Phonetic Extensions +1D80..1DBF; Phonetic Extensions Supplement +1DC0..1DFF; Combining Diacritical Marks Supplement +1E00..1EFF; Latin Extended Additional +1F00..1FFF; Greek Extended +2000..206F; General Punctuation +2070..209F; Superscripts and Subscripts +20A0..20CF; Currency Symbols +20D0..20FF; Combining Diacritical Marks for Symbols +2100..214F; Letterlike Symbols +2150..218F; Number Forms +2190..21FF; Arrows +2200..22FF; Mathematical Operators +2300..23FF; Miscellaneous Technical +2400..243F; Control Pictures +2440..245F; Optical Character Recognition +2460..24FF; Enclosed Alphanumerics +2500..257F; Box Drawing +2580..259F; Block Elements +25A0..25FF; Geometric Shapes +2600..26FF; Miscellaneous Symbols +2700..27BF; Dingbats +27C0..27EF; Miscellaneous Mathematical Symbols-A +27F0..27FF; Supplemental Arrows-A +2800..28FF; Braille Patterns +2900..297F; Supplemental Arrows-B +2980..29FF; Miscellaneous Mathematical Symbols-B +2A00..2AFF; Supplemental Mathematical Operators +2B00..2BFF; Miscellaneous Symbols and Arrows +2C00..2C5F; Glagolitic +2C60..2C7F; Latin Extended-C +2C80..2CFF; Coptic +2D00..2D2F; Georgian Supplement +2D30..2D7F; Tifinagh +2D80..2DDF; Ethiopic Extended +2DE0..2DFF; Cyrillic Extended-A +2E00..2E7F; Supplemental Punctuation +2E80..2EFF; CJK Radicals Supplement +2F00..2FDF; Kangxi Radicals +2FF0..2FFF; Ideographic Description Characters +3000..303F; CJK Symbols and Punctuation +3040..309F; Hiragana +30A0..30FF; Katakana +3100..312F; Bopomofo +3130..318F; Hangul Compatibility Jamo +3190..319F; Kanbun +31A0..31BF; Bopomofo Extended +31C0..31EF; CJK Strokes +31F0..31FF; Katakana Phonetic Extensions +3200..32FF; Enclosed CJK Letters and Months +3300..33FF; CJK Compatibility +3400..4DBF; CJK Unified Ideographs Extension A +4DC0..4DFF; Yijing Hexagram Symbols +4E00..9FFF; CJK Unified Ideographs +A000..A48F; Yi Syllables +A490..A4CF; Yi Radicals +A4D0..A4FF; Lisu +A500..A63F; Vai +A640..A69F; Cyrillic Extended-B +A6A0..A6FF; Bamum +A700..A71F; Modifier Tone Letters +A720..A7FF; Latin Extended-D +A800..A82F; Syloti Nagri +A830..A83F; Common Indic Number Forms +A840..A87F; Phags-pa +A880..A8DF; Saurashtra +A8E0..A8FF; Devanagari Extended +A900..A92F; Kayah Li +A930..A95F; Rejang +A960..A97F; Hangul Jamo Extended-A +A980..A9DF; Javanese +A9E0..A9FF; Myanmar Extended-B +AA00..AA5F; Cham +AA60..AA7F; Myanmar Extended-A +AA80..AADF; Tai Viet +AAE0..AAFF; Meetei Mayek Extensions +AB00..AB2F; Ethiopic Extended-A +AB30..AB6F; Latin Extended-E +AB70..ABBF; Cherokee Supplement +ABC0..ABFF; Meetei Mayek +AC00..D7AF; Hangul Syllables +D7B0..D7FF; Hangul Jamo Extended-B +D800..DB7F; High Surrogates +DB80..DBFF; High Private Use Surrogates +DC00..DFFF; Low Surrogates +E000..F8FF; Private Use Area +F900..FAFF; CJK Compatibility Ideographs +FB00..FB4F; Alphabetic Presentation Forms +FB50..FDFF; Arabic Presentation Forms-A +FE00..FE0F; Variation Selectors +FE10..FE1F; Vertical Forms +FE20..FE2F; Combining Half Marks +FE30..FE4F; CJK Compatibility Forms +FE50..FE6F; Small Form Variants +FE70..FEFF; Arabic Presentation Forms-B +FF00..FFEF; Halfwidth and Fullwidth Forms +FFF0..FFFF; Specials +10000..1007F; Linear B Syllabary +10080..100FF; Linear B Ideograms +10100..1013F; Aegean Numbers +10140..1018F; Ancient Greek Numbers +10190..101CF; Ancient Symbols +101D0..101FF; Phaistos Disc +10280..1029F; Lycian +102A0..102DF; Carian +102E0..102FF; Coptic Epact Numbers +10300..1032F; Old Italic +10330..1034F; Gothic +10350..1037F; Old Permic +10380..1039F; Ugaritic +103A0..103DF; Old Persian +10400..1044F; Deseret +10450..1047F; Shavian +10480..104AF; Osmanya +104B0..104FF; Osage +10500..1052F; Elbasan +10530..1056F; Caucasian Albanian +10600..1077F; Linear A +10800..1083F; Cypriot Syllabary +10840..1085F; Imperial Aramaic +10860..1087F; Palmyrene +10880..108AF; Nabataean +108E0..108FF; Hatran +10900..1091F; Phoenician +10920..1093F; Lydian +10980..1099F; Meroitic Hieroglyphs +109A0..109FF; Meroitic Cursive +10A00..10A5F; Kharoshthi +10A60..10A7F; Old South Arabian +10A80..10A9F; Old North Arabian +10AC0..10AFF; Manichaean +10B00..10B3F; Avestan +10B40..10B5F; Inscriptional Parthian +10B60..10B7F; Inscriptional Pahlavi +10B80..10BAF; Psalter Pahlavi +10C00..10C4F; Old Turkic +10C80..10CFF; Old Hungarian +10D00..10D3F; Hanifi Rohingya +10E60..10E7F; Rumi Numeral Symbols +10E80..10EBF; Yezidi +10F00..10F2F; Old Sogdian +10F30..10F6F; Sogdian +10FB0..10FDF; Chorasmian +10FE0..10FFF; Elymaic +11000..1107F; Brahmi +11080..110CF; Kaithi +110D0..110FF; Sora Sompeng +11100..1114F; Chakma +11150..1117F; Mahajani +11180..111DF; Sharada +111E0..111FF; Sinhala Archaic Numbers +11200..1124F; Khojki +11280..112AF; Multani +112B0..112FF; Khudawadi +11300..1137F; Grantha +11400..1147F; Newa +11480..114DF; Tirhuta +11580..115FF; Siddham +11600..1165F; Modi +11660..1167F; Mongolian Supplement +11680..116CF; Takri +11700..1173F; Ahom +11800..1184F; Dogra +118A0..118FF; Warang Citi +11900..1195F; Dives Akuru +119A0..119FF; Nandinagari +11A00..11A4F; Zanabazar Square +11A50..11AAF; Soyombo +11AC0..11AFF; Pau Cin Hau +11C00..11C6F; Bhaiksuki +11C70..11CBF; Marchen +11D00..11D5F; Masaram Gondi +11D60..11DAF; Gunjala Gondi +11EE0..11EFF; Makasar +11FB0..11FBF; Lisu Supplement +11FC0..11FFF; Tamil Supplement +12000..123FF; Cuneiform +12400..1247F; Cuneiform Numbers and Punctuation +12480..1254F; Early Dynastic Cuneiform +13000..1342F; Egyptian Hieroglyphs +13430..1343F; Egyptian Hieroglyph Format Controls +14400..1467F; Anatolian Hieroglyphs +16800..16A3F; Bamum Supplement +16A40..16A6F; Mro +16AD0..16AFF; Bassa Vah +16B00..16B8F; Pahawh Hmong +16E40..16E9F; Medefaidrin +16F00..16F9F; Miao +16FE0..16FFF; Ideographic Symbols and Punctuation +17000..187FF; Tangut +18800..18AFF; Tangut Components +18B00..18CFF; Khitan Small Script +18D00..18D8F; Tangut Supplement +1B000..1B0FF; Kana Supplement +1B100..1B12F; Kana Extended-A +1B130..1B16F; Small Kana Extension +1B170..1B2FF; Nushu +1BC00..1BC9F; Duployan +1BCA0..1BCAF; Shorthand Format Controls +1D000..1D0FF; Byzantine Musical Symbols +1D100..1D1FF; Musical Symbols +1D200..1D24F; Ancient Greek Musical Notation +1D2E0..1D2FF; Mayan Numerals +1D300..1D35F; Tai Xuan Jing Symbols +1D360..1D37F; Counting Rod Numerals +1D400..1D7FF; Mathematical Alphanumeric Symbols +1D800..1DAAF; Sutton SignWriting +1E000..1E02F; Glagolitic Supplement +1E100..1E14F; Nyiakeng Puachue Hmong +1E2C0..1E2FF; Wancho +1E800..1E8DF; Mende Kikakui +1E900..1E95F; Adlam +1EC70..1ECBF; Indic Siyaq Numbers +1ED00..1ED4F; Ottoman Siyaq Numbers +1EE00..1EEFF; Arabic Mathematical Alphabetic Symbols +1F000..1F02F; Mahjong Tiles +1F030..1F09F; Domino Tiles +1F0A0..1F0FF; Playing Cards +1F100..1F1FF; Enclosed Alphanumeric Supplement +1F200..1F2FF; Enclosed Ideographic Supplement +1F300..1F5FF; Miscellaneous Symbols and Pictographs +1F600..1F64F; Emoticons +1F650..1F67F; Ornamental Dingbats +1F680..1F6FF; Transport and Map Symbols +1F700..1F77F; Alchemical Symbols +1F780..1F7FF; Geometric Shapes Extended +1F800..1F8FF; Supplemental Arrows-C +1F900..1F9FF; Supplemental Symbols and Pictographs +1FA00..1FA6F; Chess Symbols +1FA70..1FAFF; Symbols and Pictographs Extended-A +1FB00..1FBFF; Symbols for Legacy Computing +20000..2A6DF; CJK Unified Ideographs Extension B +2A700..2B73F; CJK Unified Ideographs Extension C +2B740..2B81F; CJK Unified Ideographs Extension D +2B820..2CEAF; CJK Unified Ideographs Extension E +2CEB0..2EBEF; CJK Unified Ideographs Extension F +2F800..2FA1F; CJK Compatibility Ideographs Supplement +30000..3134F; CJK Unified Ideographs Extension G +E0000..E007F; Tags +E0100..E01EF; Variation Selectors Supplement +F0000..FFFFF; Supplementary Private Use Area-A +100000..10FFFF; Supplementary Private Use Area-B + +# EOF diff --git a/libc/unicode/unicode-properties.txt b/libc/unicode/unicode-properties.txt new file mode 100644 index 00000000..bbd3eaea --- /dev/null +++ b/libc/unicode/unicode-properties.txt @@ -0,0 +1,30 @@ +Lu = Letter, uppercase +Ll = Letter, lowercase +Lt = Letter, titlecase +Lm = Letter, modifier +Lo = Letter, other +Mn = Mark, nonspacing +Mc = Mark, spacing combining +Me = Mark, enclosing +Nd = Number, decimal digit +Nl = Number, letter +No = Number, other +Pc = Punctuation, connector +Pd = Punctuation, dash +Ps = Punctuation, open +Pe = Punctuation, close +Pi = Punctuation, initial quote (may behave like Ps or Pe depending on usage) +Pf = Punctuation, final quote (may behave like Ps or Pe depending on usage) +Po = Punctuation, other +Sm = Symbol, math +Sc = Symbol, currency +Sk = Symbol, modifier +So = Symbol, other +Zs = Separator, space +Zl = Separator, line +Zp = Separator, paragraph +Cc = Other, control +Cf = Other, format +Cs = Other, surrogate +Co = Other, private use +Cn = Other, not assigned (including noncharacters) diff --git a/libc/unicode/wcwidth.c b/libc/unicode/wcwidth.c index f58620da..0cd39d90 100644 --- a/libc/unicode/wcwidth.c +++ b/libc/unicode/wcwidth.c @@ -24,9 +24,9 @@ */ int wcwidth(wchar_t ucs) { if (ucs == 0) return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) { + if ((0 <= ucs && ucs < 32) || (0x7f <= ucs && ucs < 0xA0)) { return -1; - } else if (0 <= ucs && ucs < kCombiningCharsBits && + } else if ((0 <= ucs && ucs < kCombiningCharsBits) && !!(kCombiningChars[ucs >> 3] & (1 << (ucs & 7)))) { return 0; } else if (0 <= ucs && ucs < kEastAsianWidthBits) { diff --git a/libc/x/x.h b/libc/x/x.h index 05b6d0cd..d5454319 100644 --- a/libc/x/x.h +++ b/libc/x/x.h @@ -60,6 +60,8 @@ void *xcalloc(size_t, size_t) attributeallocsize((1, 2)) _XMAL; void *xvalloc(size_t) attributeallocsize((1)) _XMALPG; void *xmemalign(size_t, size_t) attributeallocalign((1)) attributeallocsize((2)) _XMAL; +void *xmemalignzero(size_t, size_t) attributeallocalign((1)) + attributeallocsize((2)) _XMAL; char *xstrdup(const char *) _XPNN _XMAL; char *xstrndup(const char *, size_t) _XPNN _XMAL; char *xstrcat(const char *, ...) paramsnonnull((1)) nullterminated() _XMAL; diff --git a/libc/runtime/quick_exit.c b/libc/x/xmemalignzero.c similarity index 84% rename from libc/runtime/quick_exit.c rename to libc/x/xmemalignzero.c index 4d3de77e..656f276a 100644 --- a/libc/runtime/quick_exit.c +++ b/libc/x/xmemalignzero.c @@ -17,18 +17,18 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/bits/weaken.h" -#include "libc/runtime/runtime.h" -#include "libc/stdio/stdio.h" +#include "libc/log/log.h" +#include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/x/x.h" /** - * Terminates process normally, running minimal cleanup. - * @noreturn + * Allocates aligned cleared memory, or dies. */ -noreturn textexit void quick_exit(int rc) { - if (weaken(fflush)) { - if (weaken(stdout)) weaken(fflush)(*weaken(stdout)); - if (weaken(stderr)) weaken(fflush)(*weaken(stderr)); - } - _Exit(rc); +void *xmemalignzero(size_t alignment, size_t bytes) { + void *p; + p = memalign(alignment, bytes); + if (!p) xdie(); + memset(p, 0, bytes); + return p; } diff --git a/libc/zipos/zipos.S b/libc/zipos/zipos.S index 567c2316..91eb56b3 100644 --- a/libc/zipos/zipos.S +++ b/libc/zipos/zipos.S @@ -19,6 +19,10 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.h" +/ static_yoink this symbol for open(zip:...) support. + zip_uri_support = 0 + .globl zip_uri_support + .yoink __zip_start .yoink __zip_end .yoink __zipos_close diff --git a/libc/zipos/zipos.mk b/libc/zipos/zipos.mk index 63e47390..82a95029 100644 --- a/libc/zipos/zipos.mk +++ b/libc/zipos/zipos.mk @@ -12,8 +12,7 @@ LIBC_ZIPOS_A_SRCS_C = $(filter %.c,$(LIBC_ZIPOS_A_FILES)) LIBC_ZIPOS = \ $(LIBC_ZIPOS_A_DEPS) \ - $(LIBC_ZIPOS_A) \ - o/$(MODE)/libc/zipos/zipos.o + $(LIBC_ZIPOS_A) LIBC_ZIPOS_A_SRCS = \ $(LIBC_ZIPOS_A_SRCS_S) \ diff --git a/test/libc/fmt/palandprintf_test.c b/test/libc/fmt/palandprintf_test.c index c797aa78..45eb6600 100644 --- a/test/libc/fmt/palandprintf_test.c +++ b/test/libc/fmt/palandprintf_test.c @@ -676,16 +676,24 @@ TEST(snprintf, formatStringLiteral) { EXPECT_EQ('\\' | 'n' << 8, cescapec('\n')); EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec('\377')); EXPECT_STREQ("\"hi\\n\"", Format("%`'s", "hi\n")); + EXPECT_STREQ("\"\\000\"", Format("%`'.*s", 1, "\0")); +} + +TEST(palandprintf, precisionStillRespectsNulTerminatorIfNotEscOrRepr) { + EXPECT_STREQ("Makefile - 25 lines ", + Format("%.20s - %d lines %s", "Makefile", 25, "")); } BENCH(palandprintf, bench) { - EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23))); - EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23))); - EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN))); - EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN))); EZBENCH2("ascii %s", donothing, Format("%s", VEIL("r", "hiuhcreohucreo"))); EZBENCH2("utf8 %s", donothing, Format("%s", VEIL("r", "hi (╯°□°)╯"))); EZBENCH2("snprintf %hs", donothing, Format("%hs", VEIL("r", u"hi (╯°□°)╯"))); EZBENCH2("snprintf %ls", donothing, Format("%ls", VEIL("r", L"hi (╯°□°)╯"))); - EZBENCH2("int64toarray", donothing, int64toarray_radix10(-3, buffer)); + EZBENCH2("23 %x", donothing, Format("%x", VEIL("r", 23))); + EZBENCH2("23 %d", donothing, Format("%d", VEIL("r", 23))); + EZBENCH2("INT_MIN %x", donothing, Format("%x", VEIL("r", INT_MIN))); + EZBENCH2("INT_MIN %d", donothing, Format("%d", VEIL("r", INT_MIN))); + EZBENCH2("23 int64toarray", donothing, int64toarray_radix10(23, buffer)); + EZBENCH2("INT_MIN int64toarray", donothing, + int64toarray_radix10(INT_MIN, buffer)); } diff --git a/test/libc/intrin/intrin_test.c b/test/libc/intrin/intrin_test.c index e15d1cc2..61437fbf 100644 --- a/test/libc/intrin/intrin_test.c +++ b/test/libc/intrin/intrin_test.c @@ -1342,7 +1342,7 @@ TEST(paddusw, fuzz) { TEST(psubb, fuzz) { int i, j; - int8_t x[16], y[16], a[16], b[16]; + uint8_t x[16], y[16], a[16], b[16]; for (i = 0; i < 100; ++i) { RngSet(x, sizeof(x)); RngSet(y, sizeof(y)); diff --git a/test/libc/nexgen32e/cescapec_test.c b/test/libc/nexgen32e/cescapec_test.c new file mode 100644 index 00000000..7469a1bd --- /dev/null +++ b/test/libc/nexgen32e/cescapec_test.c @@ -0,0 +1,33 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/escape/escape.h" +#include "libc/testlib/testlib.h" + +TEST(cescapec, test) { + EXPECT_EQ(' ', cescapec(0x20)); + EXPECT_EQ('~', cescapec(0x7E)); + EXPECT_EQ('\\' | 'r' << 8, cescapec('\r')); + EXPECT_EQ('\\' | 'n' << 8, cescapec('\n')); + EXPECT_EQ('\\' | '0' << 8 | '0' << 16 | '0' << 24, cescapec(0)); + EXPECT_EQ('\\' | '1' << 8 | '7' << 16 | '7' << 24, cescapec(0x7F)); + EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec(0xFF)); + EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec(0xFFFF)); + EXPECT_EQ('\\' | '3' << 8 | '7' << 16 | '7' << 24, cescapec(-1)); +} diff --git a/test/libc/runtime/getdosargv_test.c b/test/libc/runtime/getdosargv_test.c index a5a1863b..7aaeb016 100644 --- a/test/libc/runtime/getdosargv_test.c +++ b/test/libc/runtime/getdosargv_test.c @@ -20,34 +20,34 @@ #include "libc/runtime/internal.h" #include "libc/testlib/testlib.h" -TEST(getdosargv, empty) { +TEST(GetDosArgv, empty) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(0, getdosargv(u"", buf, size, argv, max)); + EXPECT_EQ(0, GetDosArgv(u"", buf, size, argv, max)); EXPECT_EQ(NULL, argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, emptyish) { +TEST(GetDosArgv, emptyish) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(0, getdosargv(u" ", buf, size, argv, max)); + EXPECT_EQ(0, GetDosArgv(u" ", buf, size, argv, max)); EXPECT_EQ(NULL, argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, basicUsage) { +TEST(GetDosArgv, basicUsage) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(3, getdosargv(u"a\t \"b c\" d ", buf, size, argv, max)); + EXPECT_EQ(3, GetDosArgv(u"a\t \"b c\" d ", buf, size, argv, max)); EXPECT_STREQ("a", argv[0]); EXPECT_STREQ("b c", argv[1]); EXPECT_STREQ("d", argv[2]); @@ -56,12 +56,12 @@ TEST(getdosargv, basicUsage) { tfree(buf); } -TEST(getdosargv, advancedUsage) { +TEST(GetDosArgv, advancedUsage) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosargv(u"(╯°□°)╯︵ ┻━┻", buf, size, argv, max)); + EXPECT_EQ(2, GetDosArgv(u"(╯°□°)╯︵ ┻━┻", buf, size, argv, max)); EXPECT_STREQ("(╯°□°)╯︵", argv[0]); EXPECT_STREQ("┻━┻", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -69,12 +69,12 @@ TEST(getdosargv, advancedUsage) { tfree(buf); } -TEST(getdosargv, testAegeanGothicSupplementaryPlanes) { +TEST(GetDosArgv, testAegeanGothicSupplementaryPlanes) { size_t max = 4; /* these symbols are almost as old as dos */ size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosargv(u"𐄷𐄸𐄹𐄺𐄻𐄼 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", buf, size, argv, max)); + EXPECT_EQ(2, GetDosArgv(u"𐄷𐄸𐄹𐄺𐄻𐄼 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", buf, size, argv, max)); EXPECT_STREQ("𐄷𐄸𐄹𐄺𐄻𐄼", argv[0]); EXPECT_STREQ("𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -82,12 +82,12 @@ TEST(getdosargv, testAegeanGothicSupplementaryPlanes) { tfree(buf); } -TEST(getdosargv, realWorldUsage) { +TEST(GetDosArgv, realWorldUsage) { size_t max = 512; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(5, getdosargv(u"C:\\Users\\jtunn\\printargs.com oh yes yes yes", + EXPECT_EQ(5, GetDosArgv(u"C:\\Users\\jtunn\\printargs.com oh yes yes yes", buf, size, argv, max)); EXPECT_STREQ("C:\\Users\\jtunn\\printargs.com", argv[0]); EXPECT_STREQ("oh", argv[1]); @@ -99,12 +99,12 @@ TEST(getdosargv, realWorldUsage) { tfree(buf); } -TEST(getdosargv, bufferOverrun_countIsStillAccurate_truncatesMemoryWithGrace) { +TEST(GetDosArgv, bufferOverrun_countIsStillAccurate_truncatesMemoryWithGrace) { size_t max = 3; size_t size = 7; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(3, getdosargv(u"a\t \"b c\" d ", buf, size, argv, max)); + EXPECT_EQ(3, GetDosArgv(u"a\t \"b c\" d ", buf, size, argv, max)); EXPECT_STREQ("a", argv[0]); EXPECT_STREQ("b c", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -112,44 +112,44 @@ TEST(getdosargv, bufferOverrun_countIsStillAccurate_truncatesMemoryWithGrace) { tfree(buf); } -TEST(getdosargv, pureScanningMode) { +TEST(GetDosArgv, pureScanningMode) { size_t max = 0; size_t size = 0; char *buf = NULL; char **argv = NULL; - EXPECT_EQ(3, getdosargv(u"a b c", buf, size, argv, max)); + EXPECT_EQ(3, GetDosArgv(u"a b c", buf, size, argv, max)); } -TEST(getdosargv, justSlashQuote) { +TEST(GetDosArgv, justSlashQuote) { size_t max = 4, size = 16; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosargv(u"\"\\\\\\\"\"", buf, size, argv, max)); + EXPECT_EQ(1, GetDosArgv(u"\"\\\\\\\"\"", buf, size, argv, max)); EXPECT_STREQ("\\\"", argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, quoteInMiddleOfArg_wontSplitArg) { +TEST(GetDosArgv, quoteInMiddleOfArg_wontSplitArg) { size_t max = 4, size = 16; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosargv(u"hi\"\"there", buf, size, argv, max)); + EXPECT_EQ(1, GetDosArgv(u"hi\"\"there", buf, size, argv, max)); EXPECT_STREQ("hithere", argv[0]); max = 4, size = 16; - EXPECT_EQ(1, getdosargv(u"hi\" \"there", buf, size, argv, max)); + EXPECT_EQ(1, GetDosArgv(u"hi\" \"there", buf, size, argv, max)); EXPECT_STREQ("hi there", argv[0]); tfree(argv); tfree(buf); } -TEST(getdosargv, waqQuoting1) { +TEST(GetDosArgv, waqQuoting1) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); EXPECT_EQ(2, - getdosargv(u"a\\\\\"\"\"\"\"\"\"\"b c\" d", buf, size, argv, max)); + GetDosArgv(u"a\\\\\"\"\"\"\"\"\"\"b c\" d", buf, size, argv, max)); EXPECT_STREQ("a\\\"\"b", argv[0]); EXPECT_STREQ("c d", argv[1]); EXPECT_EQ(NULL, argv[2]); @@ -157,12 +157,12 @@ TEST(getdosargv, waqQuoting1) { tfree(buf); } -TEST(getdosargv, waqQuoting2) { +TEST(GetDosArgv, waqQuoting2) { size_t max = 4; size_t size = ARG_MAX; char *buf = tmalloc(size * sizeof(char)); char **argv = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosargv(u"\"a\\\"b c\" d", buf, size, argv, max)); + EXPECT_EQ(2, GetDosArgv(u"\"a\\\"b c\" d", buf, size, argv, max)); EXPECT_STREQ("a\"b c", argv[0]); EXPECT_STREQ("d", argv[1]); EXPECT_EQ(NULL, argv[2]); diff --git a/test/libc/runtime/getdosenviron_test.c b/test/libc/runtime/getdosenviron_test.c index ee909c21..7b2425d2 100644 --- a/test/libc/runtime/getdosenviron_test.c +++ b/test/libc/runtime/getdosenviron_test.c @@ -21,14 +21,14 @@ #include "libc/str/str.h" #include "libc/testlib/testlib.h" -TEST(getdosenviron, testOneVariable) { +TEST(GetDosEnviron, testOneVariable) { #define kEnv u"A=Und wird die Welt auch in Flammen stehen\0" size_t max = 2; size_t size = sizeof(kEnv) >> 1; char *block = tmalloc(size); char16_t *env = memcpy(tmalloc(sizeof(kEnv)), kEnv, sizeof(kEnv)); char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosenviron(env, block, size, envp, max)); + EXPECT_EQ(1, GetDosEnviron(env, block, size, envp, max)); EXPECT_STREQ("A=Und wird die Welt auch in Flammen stehen", envp[0]); EXPECT_EQ(NULL, envp[1]); ASSERT_BINEQ(u"A=Und wird die Welt auch in Flammen stehen  ", block); @@ -38,7 +38,7 @@ TEST(getdosenviron, testOneVariable) { #undef kEnv } -TEST(getdosenviron, testTwoVariables) { +TEST(GetDosEnviron, testTwoVariables) { #define kEnv \ (u"𐌰𐌱𐌲𐌳=Und wird die Welt auch in Flammen stehen\0" \ u"𐌴𐌵𐌶𐌷=Wir werden wieder auferstehen\0") @@ -47,7 +47,7 @@ TEST(getdosenviron, testTwoVariables) { char *block = tmalloc(size); char16_t *env = memcpy(tmalloc(sizeof(kEnv)), kEnv, sizeof(kEnv)); char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(2, getdosenviron(env, block, size, envp, max)); + EXPECT_EQ(2, GetDosEnviron(env, block, size, envp, max)); EXPECT_STREQ("𐌰𐌱𐌲𐌳=Und wird die Welt auch in Flammen stehen", envp[0]); EXPECT_STREQ("𐌴𐌵𐌶𐌷=Wir werden wieder auferstehen", envp[1]); EXPECT_EQ(NULL, envp[2]); @@ -57,14 +57,14 @@ TEST(getdosenviron, testTwoVariables) { #undef kEnv } -TEST(getdosenviron, testOverrun_truncatesWithGrace) { +TEST(GetDosEnviron, testOverrun_truncatesWithGrace) { #define kEnv u"A=Und wird die Welt auch in Flammen stehen\0" size_t max = 2; size_t size = sizeof(kEnv) >> 2; char *block = tmalloc(size); char16_t *env = memcpy(tmalloc(sizeof(kEnv)), kEnv, sizeof(kEnv)); char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(1, getdosenviron(env, block, size, envp, max)); + EXPECT_EQ(1, GetDosEnviron(env, block, size, envp, max)); EXPECT_STREQ("A=Und wird die Welt ", envp[0]); EXPECT_EQ(NULL, envp[1]); ASSERT_BINEQ(u"A=Und wird die Welt   ", block); @@ -74,30 +74,30 @@ TEST(getdosenviron, testOverrun_truncatesWithGrace) { #undef kEnv } -TEST(getdosenviron, testEmpty_doesntTouchMemory) { - EXPECT_EQ(0, getdosenviron(u"", NULL, 0, NULL, 0)); +TEST(GetDosEnviron, testEmpty_doesntTouchMemory) { + EXPECT_EQ(0, GetDosEnviron(u"", NULL, 0, NULL, 0)); } -TEST(getdosenviron, testEmpty_zeroTerminatesWheneverPossible_1) { +TEST(GetDosEnviron, testEmpty_zeroTerminatesWheneverPossible_1) { size_t max = 1; char **envp = tmalloc(max * sizeof(char *)); - EXPECT_EQ(0, getdosenviron(u"", NULL, 0, envp, max)); + EXPECT_EQ(0, GetDosEnviron(u"", NULL, 0, envp, max)); EXPECT_EQ(NULL, envp[0]); tfree(envp); } -TEST(getdosenviron, testEmpty_zeroTerminatesWheneverPossible_2) { +TEST(GetDosEnviron, testEmpty_zeroTerminatesWheneverPossible_2) { size_t size = 1; char *block = tmalloc(size); - EXPECT_EQ(0, getdosenviron(u"", block, size, NULL, 0)); + EXPECT_EQ(0, GetDosEnviron(u"", block, size, NULL, 0)); EXPECT_BINEQ(u" ", block); tfree(block); } -TEST(getdosenviron, testEmpty_zeroTerminatesWheneverPossible_3) { +TEST(GetDosEnviron, testEmpty_zeroTerminatesWheneverPossible_3) { size_t size = 2; char *block = tmalloc(size); - EXPECT_EQ(0, getdosenviron(u"", block, size, NULL, 0)); + EXPECT_EQ(0, GetDosEnviron(u"", block, size, NULL, 0)); EXPECT_BINEQ(u"  ", block); tfree(block); } diff --git a/test/libc/str/strchr_test.c b/test/libc/str/strchr_test.c index cfce6b82..2e2ae89c 100644 --- a/test/libc/str/strchr_test.c +++ b/test/libc/str/strchr_test.c @@ -31,6 +31,11 @@ TEST(strchr, text) { char buf[] = "hellothere"; EXPECT_STREQ("there", strchr(buf, 't')); } +TEST(strchr, testsse) { + char buf[] = "hellohellohellohellohellohellohellohello" + "theretheretheretheretheretheretherethere"; + EXPECT_STREQ("theretheretheretheretheretheretherethere", strchr(buf, 't')); +} TEST(rawmemchr, text) { char buf[] = "hellothere"; EXPECT_STREQ("there", rawmemchr(buf, 't')); diff --git a/test/libc/str/tpenc_test.c b/test/libc/str/tpenc_test.c index 64f2fff9..32bf0416 100644 --- a/test/libc/str/tpenc_test.c +++ b/test/libc/str/tpenc_test.c @@ -37,8 +37,16 @@ TEST(tpenc, test) { EXPECT_EQ(0x8080808080FEul, tpenc(INT_MIN)); } +TEST(tpenc, theimp) { + ASSERT_EQ(0x88989FF0, tpenc(L'😈')); +} + +TEST(tpenc, testBeyondTheStandard) { + ASSERT_EQ(0xBFBFBFBFBFFF, tpenc(-1)); +} + uint64_t Tpenc(int x) { - return (v = tpenc(x)); + return (v = tpenc(VEIL("r", x))); } BENCH(tpenc, bench) { @@ -47,6 +55,7 @@ BENCH(tpenc, bench) { EZBENCH(donothing, Tpenc(' ')); EZBENCH(donothing, Tpenc(0x7f)); EZBENCH(donothing, Tpenc(L'▄')); + EZBENCH(donothing, Tpenc(-1)); EZBENCH(donothing, Tpenc(INT_MIN)); fprintf(stderr, "\n"); } diff --git a/test/libc/str/tpencode_test.c b/test/libc/str/tpencode_test.c index 697192b6..8c0e7ab4 100644 --- a/test/libc/str/tpencode_test.c +++ b/test/libc/str/tpencode_test.c @@ -61,3 +61,7 @@ TEST(tpencode, testMathematicalNotMuhPolicyDrivenBehavior_nonCanonicalNul) { ASSERT_BINEQ(u"└Ç", PROGN(ASSERT_EQ(2, tpencode(buf, 8, 0, true)), buf)); ASSERT_BINEQ(u"└Ç", PROGN(ASSERT_EQ(2, (tpencode)(buf, 8, 0, true)), buf)); } + +TEST(tpencode, testC1Csi) { + ASSERT_BINEQ(u"┬¢", PROGN(ASSERT_EQ(2, tpencode(buf, 8, 0x9B, false)), buf)); +} diff --git a/test/libc/str/tprecode16to8_test.c b/test/libc/str/tprecode16to8_test.c new file mode 100644 index 00000000..471b7022 --- /dev/null +++ b/test/libc/str/tprecode16to8_test.c @@ -0,0 +1,27 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "libc/testlib/testlib.h" + +TEST(tprecode16to8, test) { + char b[128]; + EXPECT_EQ(69, tprecode16to8(b, 128, u"(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹")); + EXPECT_STREQ("(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹", b); +} diff --git a/test/libc/str/tprecode8to16_test.c b/test/libc/str/tprecode8to16_test.c index 208dc170..2c449c87 100644 --- a/test/libc/str/tprecode8to16_test.c +++ b/test/libc/str/tprecode8to16_test.c @@ -28,3 +28,9 @@ TEST(tprecode8to16, test) { EXPECT_STREQ(u"hello☻♥", buf); tfree(buf); } + +TEST(tprecode8to16, test2) { + char16_t b[128]; + EXPECT_EQ(34, tprecode8to16(b, 128, "(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹")); + EXPECT_STREQ(u"(╯°□°)╯︵L┻━┻ 𐌰𐌱𐌲𐌳𐌴𐌵𐌶𐌷𐌸𐌹", b); +} diff --git a/test/libc/str/undeflate_test.c b/test/libc/str/undeflate_test.c index c9dbe793..2e0ccd7d 100644 --- a/test/libc/str/undeflate_test.c +++ b/test/libc/str/undeflate_test.c @@ -42,6 +42,7 @@ #include "libc/zip.h" #include "third_party/zlib/zlib.h" +STATIC_YOINK("zip_uri_support"); STATIC_YOINK("libc/testlib/hyperion.txt"); TEST(undeflate, testEmbeddedPlaintextConstant) { @@ -86,7 +87,7 @@ TEST(undeflate, testEmbeddedCompressedZipFile_theHardWay) { struct DeflateState ds; uint8_t *map, *cd, *cf, *lf, *data; found = false; - ASSERT_NE(-1, (fd = open(findcombinary(), O_RDONLY))); + ASSERT_NE(-1, (fd = open(FindComBinary(), O_RDONLY))); ASSERT_NE(-1, fstat(fd, &st)); ASSERT_NE(MAP_FAILED, (map = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0))); diff --git a/test/libc/time/strftime_test.c b/test/libc/time/strftime_test.c index be0891ef..4f1f2924 100644 --- a/test/libc/time/strftime_test.c +++ b/test/libc/time/strftime_test.c @@ -18,11 +18,14 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/calls/calls.h" +#include "libc/limits.h" #include "libc/runtime/runtime.h" #include "libc/testlib/testlib.h" #include "libc/time/time.h" -textstartup static void strftime_test_init(void) { setenv("TZ", "GST", true); } +textstartup static void strftime_test_init(void) { + setenv("TZ", "GST", true); +} const void *const strftime_test_ctor[] initarray = {strftime_test_init}; testonly char *FormatTime(const char *fmt, struct tm *tm) { @@ -37,6 +40,12 @@ TEST(strftime_100, iso8601_ShakaZuluTime) { FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); } +TEST(xiso8601, testUnixYearZero) { + int64_t t = 0; + ASSERT_STREQ("1970-01-01T00:00:00Z", + FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); +} + TEST(strftime_100, rfc2822_ShakaZuluTime) { int64_t t = 0x5cd04d0e; ASSERT_STREQ("Mon, 06 May 2019 15:04:46 +0000", @@ -66,3 +75,27 @@ TEST(strftime_201, rfc822_GoogleStandardTime) { ASSERT_STREQ("Mon, 06 May 19 08:04:46 -0700", FormatTime("%a, %d %b %y %T %z", localtime(&t))); } + +/* TEST(xiso8601, testModernity_TODO) { */ +/* int64_t t = (1600 - 1970) * 31536000; */ +/* ASSERT_STREQ("1600-01-01T00:00:00Z", */ +/* FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); */ +/* } */ + +TEST(xiso8601, testAtLeastBetterThanTraditionalUnixLimit) { + int64_t t = 10737418235; + ASSERT_STREQ("2310-04-04T16:10:35Z", + FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); +} + +TEST(xiso8601, testSomethingHuge) { + int64_t t = 7707318812667; + ASSERT_STREQ("246205-03-18T20:24:27Z", + FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); +} + +/* TEST(xiso8601, testMostOfStelliferousEra_TODO) { */ +/* int64_t t = INT64_MAX; */ +/* ASSERT_STREQ("somethinghuge-01-01T00:00:00Z", */ +/* FormatTime("%Y-%m-%dT%H:%M:%SZ", gmtime(&t))); */ +/* } */ diff --git a/test/tool/build/lib/divmul_test.c b/test/tool/build/lib/divmul_test.c index 6f85b542..a2a34f13 100644 --- a/test/tool/build/lib/divmul_test.c +++ b/test/tool/build/lib/divmul_test.c @@ -48,7 +48,6 @@ void OnSigFpe(void) { void SetUp(void) { m->xedd = xedd; - InitMachine(m); CHECK_NE(-1, xsigaction(SIGFPE, OnSigFpe, SA_NODEFER, 0, oldsigfpe)); } diff --git a/test/tool/build/lib/iovs_test.c b/test/tool/build/lib/iovs_test.c new file mode 100644 index 00000000..c36b398b --- /dev/null +++ b/test/tool/build/lib/iovs_test.c @@ -0,0 +1,85 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/testlib/testlib.h" +#include "tool/build/lib/iovs.h" + +TEST(iovs, testEmpty) { + struct Iovs iv; + InitIovs(&iv); + EXPECT_EQ(0, iv.i); + EXPECT_GE(iv.n, iv.i); + FreeIovs(&iv); +} + +TEST(iovs, testAppendEmptyString_wontCreateEmptyEntries) { + struct Iovs iv; + InitIovs(&iv); + EXPECT_NE(-1, AppendIovs(&iv, "", 0)); + EXPECT_NE(-1, AppendIovs(&iv, NULL, 0)); + EXPECT_EQ(0, iv.i); + EXPECT_GE(iv.n, iv.i); + FreeIovs(&iv); +} + +TEST(iovs, testAppendContiguousVectors_coalescesAdjacentEntries) { + struct Iovs iv; + char buf[8], *b = buf; + InitIovs(&iv); + EXPECT_NE(-1, AppendIovs(&iv, b + 0, 4)); + EXPECT_NE(-1, AppendIovs(&iv, b + 4, 4)); + EXPECT_EQ(1, iv.i); + EXPECT_GE(iv.n, iv.i); + EXPECT_EQ(8, iv.p[0].iov_len); + EXPECT_EQ(b, iv.p[0].iov_base); + FreeIovs(&iv); +} + +TEST(iovs, testAppendNoncontiguousVectors_growsMemoryAndPreservesOrdering) { + struct Iovs iv; + char buf[8], *b = buf; + InitIovs(&iv); + EXPECT_NE(-1, AppendIovs(&iv, b + 0, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 2, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 4, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 6, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 1, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 3, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 5, 1)); + EXPECT_NE(-1, AppendIovs(&iv, b + 7, 1)); + EXPECT_EQ(8, iv.i); + EXPECT_GE(iv.n, iv.i); + EXPECT_EQ(b + 0, iv.p[0].iov_base); + EXPECT_EQ(1, iv.p[0].iov_len); + EXPECT_EQ(b + 2, iv.p[1].iov_base); + EXPECT_EQ(1, iv.p[1].iov_len); + EXPECT_EQ(b + 4, iv.p[2].iov_base); + EXPECT_EQ(1, iv.p[2].iov_len); + EXPECT_EQ(b + 6, iv.p[3].iov_base); + EXPECT_EQ(1, iv.p[3].iov_len); + EXPECT_EQ(b + 1, iv.p[4].iov_base); + EXPECT_EQ(1, iv.p[4].iov_len); + EXPECT_EQ(b + 3, iv.p[5].iov_base); + EXPECT_EQ(1, iv.p[5].iov_len); + EXPECT_EQ(b + 5, iv.p[6].iov_base); + EXPECT_EQ(1, iv.p[6].iov_len); + EXPECT_EQ(b + 7, iv.p[7].iov_base); + EXPECT_EQ(1, iv.p[7].iov_len); + FreeIovs(&iv); +} diff --git a/test/tool/build/lib/machine_test.c b/test/tool/build/lib/machine_test.c index aac04266..8b264199 100644 --- a/test/tool/build/lib/machine_test.c +++ b/test/tool/build/lib/machine_test.c @@ -98,27 +98,30 @@ const uint8_t kTenthprime2[] = { 0xC3, // }; -int64_t base; -uint8_t *real; -size_t realsize; struct Machine *m; void SetUp(void) { - base = 0; m = NewMachine(); - m->cr3 = MallocPage(); - realsize = 0x10000; - real = tmemalign(PAGESIZE, ROUNDUP(realsize, PAGESIZE)); - RegisterMemory(m, base, real, realsize); - m->ip = base; - Write64(m->sp, m->ip + realsize); + m->ip = 0; + ReserveVirtual(m, 0, 4096); + ASSERT_EQ(0x5000, m->real.i); + ASSERT_EQ(0x1007, Read64(m->real.p + 0x0000)); // PML4T + ASSERT_EQ(0x2007, Read64(m->real.p + 0x1000)); // PDPT + ASSERT_EQ(0x3007, Read64(m->real.p + 0x2000)); // PDE + ASSERT_EQ(0x4007, Read64(m->real.p + 0x3000)); // PT + Write64(m->sp, 4096); } void TearDown(void) { - ResetRam(m); - free(m->cr3); - tfree(real); - free(m); + FreeVirtual(m, 0, 4096); + ASSERT_EQ(0x5000, m->real.i); + ASSERT_EQ(0x1007, Read64(m->real.p + 0x0000)); // PML4T + ASSERT_EQ(0x2007, Read64(m->real.p + 0x1000)); // PDPT + ASSERT_EQ(0x3007, Read64(m->real.p + 0x2000)); // PDE + ASSERT_EQ(0x0000, Read64(m->real.p + 0x3000)); // PT + ASSERT_EQ(0x4000, m->realfree->i); + ASSERT_EQ(0x1000, m->realfree->n); + FreeMachine(m); } int ExecuteUntilHalt(struct Machine *m) { @@ -134,121 +137,121 @@ int ExecuteUntilHalt(struct Machine *m) { } TEST(machine, test) { - memcpy(real, kTenthprime, sizeof(kTenthprime)); + VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_EQ(15, Read32(m->ax)); } TEST(machine, testFpu) { - memcpy(real, kPi80, sizeof(kPi80)); + VirtualRecv(m, 0, kPi80, sizeof(kPi80)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_TRUE(fabs(3.14159 - FpuPop(m)) < 0.0001); - m->ip = base; + m->ip = 0; ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_TRUE(fabs(3.14159 - FpuPop(m)) < 0.0001); } BENCH(machine, benchPrimeNumberPrograms) { - memcpy(real, kTenthprime2, sizeof(kTenthprime2)); - EZBENCH2("tenthprime2", m->ip = base, ExecuteUntilHalt(m)); + VirtualRecv(m, 0, kTenthprime2, sizeof(kTenthprime2)); + EZBENCH2("tenthprime2", m->ip = 0, ExecuteUntilHalt(m)); ASSERT_EQ(15, Read32(m->ax)); - memcpy(real, kTenthprime, sizeof(kTenthprime)); - EZBENCH2("tenthprime", m->ip = base, ExecuteUntilHalt(m)); + VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime)); + EZBENCH2("tenthprime", m->ip = 0, ExecuteUntilHalt(m)); ASSERT_EQ(15, Read32(m->ax)); } BENCH(machine, benchFpu) { - memcpy(real, kPi80, sizeof(kPi80)); - EZBENCH2("pi80", m->ip = base, PROGN(ExecuteUntilHalt(m), FpuPop(m))); + VirtualRecv(m, 0, kPi80, sizeof(kPi80)); + EZBENCH2("pi80", m->ip = 0, PROGN(ExecuteUntilHalt(m), FpuPop(m))); } BENCH(machine, benchLoadExec2) { uint8_t kMovCode[] = {0xbe, 0x03, 0x00, 0x00, 0x00}; - memcpy(real, kMovCode, sizeof(kMovCode)); + VirtualRecv(m, 0, kMovCode, sizeof(kMovCode)); LoadInstruction(m); - EZBENCH2("mov", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("mov", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec3) { uint8_t kMovdCode[] = {0x66, 0x0f, 0x6e, 0xc0}; Write64(m->ax, 0); - memcpy(real, kMovdCode, sizeof(kMovdCode)); + VirtualRecv(m, 0, kMovdCode, sizeof(kMovdCode)); LoadInstruction(m); - EZBENCH2("movd", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("movd", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec4) { uint8_t kAddpsRegregCode[] = {0x0f, 0x58, 0xC0}; uint8_t kAddpsMemregCode[] = {0x0f, 0x58, 0x00}; Write64(m->ax, 0); - memcpy(real, kAddpsRegregCode, sizeof(kAddpsRegregCode)); + VirtualRecv(m, 0, kAddpsRegregCode, sizeof(kAddpsRegregCode)); LoadInstruction(m); - EZBENCH2("addps reg reg", m->ip = base, ExecuteInstruction(m)); - memcpy(real, kAddpsMemregCode, sizeof(kAddpsMemregCode)); + EZBENCH2("addps reg reg", m->ip = 0, ExecuteInstruction(m)); + VirtualRecv(m, 0, kAddpsMemregCode, sizeof(kAddpsMemregCode)); LoadInstruction(m); - EZBENCH2("addps mem reg", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addps mem reg", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec5) { uint8_t kPaddwRegregCode[] = {0x66, 0x0F, 0xFD, 0xC0}; uint8_t kPaddwMemregCode[] = {0x66, 0x0F, 0xFD, 0x00}; Write64(m->ax, 0); - memcpy(real, kPaddwRegregCode, sizeof(kPaddwRegregCode)); + VirtualRecv(m, 0, kPaddwRegregCode, sizeof(kPaddwRegregCode)); LoadInstruction(m); - EZBENCH2("paddw", m->ip = base, ExecuteInstruction(m)); - memcpy(real, kPaddwMemregCode, sizeof(kPaddwMemregCode)); + EZBENCH2("paddw", m->ip = 0, ExecuteInstruction(m)); + VirtualRecv(m, 0, kPaddwMemregCode, sizeof(kPaddwMemregCode)); LoadInstruction(m); - EZBENCH2("paddw mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("paddw mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec6) { uint8_t kPsubqRegregCode[] = {0x66, 0x0F, 0xFB, 0xC0}; uint8_t kPsubqMemregCode[] = {0x66, 0x0F, 0xFB, 0x00}; Write64(m->ax, 0); - memcpy(real, kPsubqRegregCode, sizeof(kPsubqRegregCode)); + VirtualRecv(m, 0, kPsubqRegregCode, sizeof(kPsubqRegregCode)); LoadInstruction(m); - EZBENCH2("psubq", m->ip = base, ExecuteInstruction(m)); - memcpy(real, kPsubqMemregCode, sizeof(kPsubqMemregCode)); + EZBENCH2("psubq", m->ip = 0, ExecuteInstruction(m)); + VirtualRecv(m, 0, kPsubqMemregCode, sizeof(kPsubqMemregCode)); LoadInstruction(m); - EZBENCH2("psubq mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("psubq mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddqMem) { uint8_t kAddMemregCode[] = {0x48, 0x03, 0x08}; Write64(m->ax, 0); - memcpy(real, kAddMemregCode, sizeof(kAddMemregCode)); + VirtualRecv(m, 0, kAddMemregCode, sizeof(kAddMemregCode)); LoadInstruction(m); - EZBENCH2("addq mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addq mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddlMem) { uint8_t kAddMemregCode[] = {0x03, 0x08}; Write64(m->ax, 0); - memcpy(real, kAddMemregCode, sizeof(kAddMemregCode)); + VirtualRecv(m, 0, kAddMemregCode, sizeof(kAddMemregCode)); LoadInstruction(m); - EZBENCH2("addl mem", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addl mem", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddq) { uint8_t kAddqCode[] = {0x48, 0x01, 0xd8}; Write64(m->ax, 0); - memcpy(real, kAddqCode, sizeof(kAddqCode)); + VirtualRecv(m, 0, kAddqCode, sizeof(kAddqCode)); LoadInstruction(m); - EZBENCH2("addq", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addq", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchAddb) { uint8_t kAddbCode[] = {0x00, 0xd8}; Write64(m->ax, 0); - memcpy(real, kAddbCode, sizeof(kAddbCode)); + VirtualRecv(m, 0, kAddbCode, sizeof(kAddbCode)); LoadInstruction(m); - EZBENCH2("addb", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("addb", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchXorReg) { - memcpy(real, kTenthprime, sizeof(kTenthprime)); + VirtualRecv(m, 0, kTenthprime, sizeof(kTenthprime)); LoadInstruction(m); - EZBENCH2("xor", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("xor", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchLoadExec8) { @@ -257,16 +260,16 @@ BENCH(machine, benchLoadExec8) { OpFinit(m); *FpuSt(m, 0) = M_PI; FpuSetTag(m, 0, kFpuTagValid); - memcpy(real, kFchsCode, sizeof(kFchsCode)); + VirtualRecv(m, 0, kFchsCode, sizeof(kFchsCode)); LoadInstruction(m); - EZBENCH2("fchs", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("fchs", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchPushpop) { uint8_t kPushpop[] = {0x50, 0x58}; Write64(m->ax, 0); - memcpy(real, kPushpop, sizeof(kPushpop)); - EZBENCH2("pushpop", m->ip = base, + VirtualRecv(m, 0, kPushpop, sizeof(kPushpop)); + EZBENCH2("pushpop", m->ip = 0, PROGN(LoadInstruction(m), ExecuteInstruction(m), LoadInstruction(m), ExecuteInstruction(m))); } @@ -274,29 +277,27 @@ BENCH(machine, benchPushpop) { BENCH(machine, benchPause) { uint8_t kPause[] = {0xf3, 0x90}; Write64(m->ax, 0); - memcpy(real, kPause, sizeof(kPause)); + VirtualRecv(m, 0, kPause, sizeof(kPause)); LoadInstruction(m); - EZBENCH2("pause", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("pause", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchClc) { uint8_t kClc[] = {0xf8}; Write64(m->ax, 0); - memcpy(real, kClc, sizeof(kClc)); + VirtualRecv(m, 0, kClc, sizeof(kClc)); LoadInstruction(m); - EZBENCH2("clc", m->ip = base, ExecuteInstruction(m)); + EZBENCH2("clc", m->ip = 0, ExecuteInstruction(m)); } BENCH(machine, benchNop) { uint8_t kNop[] = {0x90}; Write64(m->ax, 0); - memcpy(real, kNop, sizeof(kNop)); + VirtualRecv(m, 0, kNop, sizeof(kNop)); LoadInstruction(m); - EZBENCH2("nop", m->ip = base, ExecuteInstruction(m)); -} - -TEST(machine, sizeIsReasonable) { - ASSERT_LE(sizeof(struct Machine), 65536 * 3); + EZBENCH2("nop", m->ip = 0, ExecuteInstruction(m)); + EZBENCH2("nop w/ load", m->ip = 0, + PROGN(LoadInstruction(m), ExecuteInstruction(m))); } TEST(x87, fprem1) { @@ -308,7 +309,7 @@ TEST(x87, fprem1) { 0xf4, // hlt 0x00, 0x00, 0xc0, 0xbf, // .float -1.5 }; - memcpy(real, prog, sizeof(prog)); + VirtualRecv(m, 0, prog, sizeof(prog)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_LDBL_EQ(1, FpuPop(m)); } @@ -323,7 +324,11 @@ TEST(x87, fprem2) { 0x00, 0x60, 0x5e, 0x75, 0x64, 0xd9, 0x45, 0x43, // 0x5b, 0x14, 0xea, 0x9d, 0x77, 0xb2, 0x0b, 0x3d, // }; - memcpy(real, prog, sizeof(prog)); + VirtualRecv(m, 0, prog, sizeof(prog)); ASSERT_EQ(kMachineHalt, ExecuteUntilHalt(m)); ASSERT_LDBL_EQ(1.1766221079117338e-14, FpuPop(m)); } + +TEST(machine, sizeIsReasonable) { + ASSERT_LE(sizeof(struct Machine), 65536 * 3); +} diff --git a/test/tool/build/lib/pml4t_test.c b/test/tool/build/lib/pml4t_test.c deleted file mode 100644 index 8c7c0e2d..00000000 --- a/test/tool/build/lib/pml4t_test.c +++ /dev/null @@ -1,158 +0,0 @@ -/*-*- 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│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/check.h" -#include "libc/macros.h" -#include "libc/mem/mem.h" -#include "libc/stdio/stdio.h" -#include "libc/str/str.h" -#include "libc/testlib/ezbench.h" -#include "libc/testlib/testlib.h" -#include "tool/build/lib/memory.h" -#include "tool/build/lib/pml4t.h" - -struct Unmapped { - size_t i; - struct UnmappedEntry { - void *addr; - size_t size; - } p[8]; -}; - -struct Unmapped unmapped; -uint64_t *pt2, *pt3, *pt4; -pml4t_t cr3; - -void *NewPage(void) { - void *p; - p = tmemalign(4096, 4096); - CHECK_ALIGNED(4096, p); - memset(p, 0, 4096); - return p; -} - -void FreePage(void *p) { - tfree(p); -} - -int FakeMunmap(void *addr, size_t size) { - return 0; -} - -int MockMunmap(void *addr, size_t size) { - CHECK_LT(unmapped.i, ARRAYLEN(unmapped.p)); - unmapped.p[unmapped.i].addr = addr; - unmapped.p[unmapped.i].size = size; - unmapped.i++; - return 0; -} - -void SetUp(void) { - memset(cr3, 0, sizeof(cr3)); -} - -void TearDown(void) { - unmapped.i = 0; - FreePml4t(cr3, -0x800000000000, 0x800000000000, FreePage, FakeMunmap); -} - -TEST(pml4t, testHighestAddress) { - ASSERT_NE(-1, - RegisterPml4t(cr3, 0x7fffffffe000, 0x3133700000, 0x2000, NewPage)); - ASSERT_TRUE(IsValidPage(cr3[255])); - pt2 = UnmaskPageAddr(cr3[255]); - ASSERT_TRUE(IsValidPage(pt2[511])); - pt3 = UnmaskPageAddr(pt2[511]); - ASSERT_TRUE(IsValidPage(pt3[511])); - pt4 = UnmaskPageAddr(pt3[511]); - ASSERT_TRUE(IsValidPage(pt4[510])); - ASSERT_TRUE(IsValidPage(pt4[511])); - EXPECT_EQ(0x3133700000, UnmaskPageAddr(pt4[510])); - EXPECT_EQ(0x3133701000, UnmaskPageAddr(pt4[511])); -} - -TEST(pml4t, testLowestAddress) { - ASSERT_NE(-1, - RegisterPml4t(cr3, -0x800000000000, 0x31337000, 0x2000, NewPage)); - ASSERT_TRUE(IsValidPage(cr3[256])); - pt2 = UnmaskPageAddr(cr3[256]); - ASSERT_TRUE(IsValidPage(pt2[0])); - pt3 = UnmaskPageAddr(pt2[0]); - ASSERT_TRUE(IsValidPage(pt3[0])); - pt4 = UnmaskPageAddr(pt3[0]); - ASSERT_TRUE(IsValidPage(pt4[0])); - ASSERT_TRUE(IsValidPage(pt4[1])); -} - -TEST(pml4t, testOverlapsExistingRegistration_overwritesRegistration) { - ASSERT_NE(-1, - RegisterPml4t(cr3, 0x7fffffffe000, 0x31337000, 0x1000, NewPage)); - ASSERT_TRUE(IsValidPage(cr3[255])); - pt2 = UnmaskPageAddr(cr3[255]); - ASSERT_TRUE(IsValidPage(pt2[511])); - pt3 = UnmaskPageAddr(pt2[511]); - ASSERT_TRUE(IsValidPage(pt3[511])); - pt4 = UnmaskPageAddr(pt3[511]); - EXPECT_TRUE(IsValidPage(pt4[510])); - EXPECT_EQ(0x31337000, UnmaskPageAddr(pt4[510])); - ASSERT_NE(-1, RegisterPml4t(cr3, 0x7fffffffe000, 0x31337000 + 0x1000, 0x1000, - NewPage)); - EXPECT_TRUE(IsValidPage(pt4[510])); - EXPECT_EQ(0x31337000 + 0x1000, UnmaskPageAddr(pt4[510])); -} - -TEST(pml4t, testFindPml4t_holeTooSmall_skipsOver) { - ASSERT_NE(-1, RegisterPml4t(cr3, 0x700000000, 0, 0x1000, NewPage)); - ASSERT_NE(-1, RegisterPml4t(cr3, 0x700005000, 0, 0x1000, NewPage)); - ASSERT_EQ(0x700001000, FindPml4t(cr3, 0x700000000, 0x01000)); - ASSERT_EQ(0x700006000, FindPml4t(cr3, 0x700000000, 0x10000)); -} - -TEST(pml4t, testFindPml4t_bigAllocation) { - ASSERT_EQ(0x00200000, FindPml4t(cr3, 0x00200000, 0x00400000)); - ASSERT_EQ(0x00201000, FindPml4t(cr3, 0x00201000, 0x00400000)); - ASSERT_EQ(0xff200000, FindPml4t(cr3, 0xff200000, 0x00400000)); - ASSERT_EQ(0xff201000, FindPml4t(cr3, 0xff201000, 0x00400000)); -} - -TEST(pml4t, testFreePmlt) { - ASSERT_NE(-1, RegisterPml4t(cr3, 0x000005000, 0x123000, 0x2000, NewPage)); - ASSERT_NE(-1, RegisterPml4t(cr3, 0x000000000, 0x321000, 0x1000, NewPage)); - ASSERT_NE(-1, FreePml4t(cr3, -0x800000000000, 0x800000000000, FreePage, - MockMunmap)); - ASSERT_EQ(2, unmapped.i); - EXPECT_EQ(0x321000, unmapped.p[0].addr); - EXPECT_EQ(0x1000, unmapped.p[0].size); - EXPECT_EQ(0x123000, unmapped.p[1].addr); - EXPECT_EQ(0x2000, unmapped.p[1].size); -} - -BENCH(pml4t, bench) { - EZBENCH2("RegisterPml4t 1mb fresh", - FreePml4t(cr3, -0x800000000000, 0x800000000000, free, FakeMunmap), - RegisterPml4t(cr3, 0x00100000, 0x31337000, 0x00100000, MallocPage)); - EZBENCH2("RegisterPml4t 1gb fresh", - FreePml4t(cr3, -0x800000000000, 0x800000000000, free, FakeMunmap), - RegisterPml4t(cr3, 0x40000000, 0x31337000, 0x40000000, MallocPage)); - EZBENCH2("RegisterPml4t 1mb overwrite", donothing, - RegisterPml4t(cr3, 0x00100000, 0x31337000, 0x00100000, MallocPage)); - EZBENCH2("RegisterPml4t 1gb overwrite", donothing, - RegisterPml4t(cr3, 0x40000000, 0x31337000, 0x40000000, MallocPage)); - FreePml4t(cr3, -0x800000000000, 0x800000000000, free, FakeMunmap); -} diff --git a/test/tool/build/lib/pty_test.c b/test/tool/build/lib/pty_test.c new file mode 100644 index 00000000..12dc860f --- /dev/null +++ b/test/tool/build/lib/pty_test.c @@ -0,0 +1,235 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/log.h" +#include "libc/mem/mem.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/testlib/ezbench.h" +#include "libc/testlib/testlib.h" +#include "libc/unicode/unicode.h" +#include "tool/build/lib/pty.h" + +char *render(struct MachinePty *pty) { + static struct Buffer b; + int y; + b.i = 0; + for (y = 0; y < pty->yn; ++y) { + MachinePtyAppendLine(pty, &b, y); + } + b.p[b.i] = 0; + return b.p; +} + +const char widelatin[] aligned(16) = "\ +A-BCDEFGHIJKLMNOPQRSTUVWXYZ\r\n\ +ab-cdefghijklmnopqrstuvwxyz\r\n\ +012-3456789\r\n\ +ABCD-EFGHIJKLMNOPQRSTUVWXYZ\r\n\ +abcde-fghijklmnopqrstuvwxyz\r\n\ +012345-6789\r\n\ +ABCDEFG-HIJKLMNOPQRSTUVWXYZ\r\n\ +abcdefgh-ijklmnopqrstuvwxyz\r\n\ +012345678-9"; + +static const char widelatin_golden[] = "\ +A-BCDEFGHIJKLMNOPQRSTUVWXYZ \ +ab-cdefghijklmnopqrstuvwxyz \ +012-3456789 \ +ABCD-EFGHIJKLMNOPQRSTUVWXYZ \ +abcde-fghijklmnopqrstuvwxyz \ +012345-6789 \ +ABCDEFG-HIJKLMNOPQRSTUVWXYZ \ +abcdefgh-ijklmnopqrstuvwxyz \ +012345678-9▂ \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "; + +TEST(pty, testFunWidth) { + struct MachinePty *pty = MachinePtyNew(); + MachinePtyWrite(pty, widelatin, ARRAYLEN(widelatin) - 1); + EXPECT_STREQ(widelatin_golden, render(pty)); + MachinePtyFree(pty); +} + +const char hyperion[] aligned(16) = "\ +Fanatics have their dreams, wherewith they weave \ +A paradise for a sect; the savage too \ +From forth the loftiest fashion of his sleep \ +Guesses at Heaven; pity these have not \ +Trac'd upon vellum or wild Indian leaf \ +The shadows of melodious utterance. \ +But bare of laurel they live, dream, and die; \ +For Poesy alone can tell her dreams, \ +With the fine spell of words alone can save \ +Imagination from the sable charm \ +And dumb enchantment. Who alive can say, \ +'Thou art no Poet may'st not tell thy dreams?' \ +Since every man whose soul is not a clod \ +Hath visions, and would speak, if he had loved \ +And been well nurtured in his mother tongue. \ +Whether the dream now purpos'd to rehearse \ +Be poet's or fanatic's will be known \ +When this warm scribe my hand is in the grave."; + +static const wchar_t hyperion_golden[] = L"\ +Fanatics have their dreams, wherewith they weave A paradise for a sect; the sava\ +ge too From forth the loftiest fashion of his sleep Guesses at Heaven; pity thes\ +e have not Trac'd upon vellum or wild Indian leaf The shadows of melodious utter\ +ance. But bare of laurel they live, dream, and die; For Poesy alone can tell her\ + dreams, With the fine spell of words alone can save Imagination from the sable \ +charm And dumb enchantment. Who alive can say, 'Thou art no Poet may'st not tell\ + thy dreams?' Since every man whose soul is not a clod Hath visions, and would s\ +peak, if he had loved And been well nurtured in his mother tongue. Whether the d\ +ream now purpos'd to rehearse Be poet's or fanatic's will be known When this war\ +m scribe my hand is in the grave. \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + "; + +TEST(pty, testPureAscii_benefitsFromVectorization) { + struct MachinePty *pty = MachinePtyNew(); + MachinePtyWrite(pty, hyperion, ARRAYLEN(hyperion) - 1); + EXPECT_STREQN(pty->wcs, hyperion_golden, ARRAYLEN(hyperion_golden) - 1); + MachinePtyFree(pty); +} + +static const char kKiloAnsi[] = "\ +\e[?25l\e[H\ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐\e[39m\e[0K\r\n\ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi─────────\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# SYNOPSIS\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# Freestanding Hermetically-Sealed Monolithic Repository\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# REQUIREMENTS\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# You can run your programs on any operating system, but you have\e[39m\e[0K\r\n\ +# to build them on Linux 2.6+ (or WSL) using GNU Make. A modern C\e[39m\e[0K\r\n\ +# compiler that\'s statically-linked comes included as a courtesy.\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# EXAMPLES\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# # build and run everything\e[39m\e[0K\r\n\ +# make -j8 -O\e[39m\e[0K\r\n\ +# make -j8 -O MODE=dbg\e[39m\e[0K\r\n\ +# make -j8 -O MODE=opt\e[39m\e[0K\r\n\ +# make -j8 -O MODE=rel\e[39m\e[0K\r\n\ +# make -j8 -O MODE=tiny\e[39m\e[0K\r\n\ +#\e[39m\e[0K\r\n\ +# # build individual target\e[39m\e[0K\r\n\ +\e[0K\e[7mMakefile - 340 lines 1/340\e[0m\r\n\ +\e[0KHELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find\e[1;1H\e[?25h"; + +static const wchar_t kKiloGolden[] = L"\ +#-*-mode:makefile-gmake;indent-tabs-mode:t;tab-width:8;coding:utf-8-*-┐ \ +#───vi: set et ft=make ts=8 tw=8 fenc=utf-8 :vi───────── \ +# \ +# SYNOPSIS \ +# \ +# Freestanding Hermetically-Sealed Monolithic Repository \ +# \ +# REQUIREMENTS \ +# \ +# You can run your programs on any operating system, but you have \ +# to build them on Linux 2.6+ (or WSL) using GNU Make. A modern C \ +# compiler that's statically-linked comes included as a courtesy. \ +# \ +# EXAMPLES \ +# \ +# # build and run everything \ +# make -j8 -O \ +# make -j8 -O MODE=dbg \ +# make -j8 -O MODE=opt \ +# make -j8 -O MODE=rel \ +# make -j8 -O MODE=tiny \ +# \ +# # build individual target \ +Makefile - 340 lines 1/340\ +HELP: Ctrl-S = save | Ctrl-Q = quit | Ctrl-F = find \ +"; + +TEST(pty, testLongestPossibleCharacter) { + EXPECT_EQ(60, strlen("\e[21;22;27;24;25;29;38;2;255;255;255;48;2;255;255;" + "255m\377\277\277\277\277\277")); + struct Buffer b = {0}; + struct MachinePty *pty = MachinePtyNew(); + const char *s = "\e[1;2;3;4;5;6;7;9m" + "h" + "\e[0;" + "38;2;255;255;255;" + "48;2;255;255;255m" + "\377\277\277\277\277\277" + "\e[0m"; + MachinePtyWrite(pty, s, strlen(s)); + MachinePtyAppendLine(pty, &b, 0); + AppendChar(&b, '\0'); + EXPECT_STREQ("\e[1;2;4;7;5;9m" + "𝒉" + "\e[22;24;27;25;29;38;2;255;255;255;48;2;255;255;255m" + "\377\277\277\277\277\277" + "\e[0m▂ " + " ", + b.p); + MachinePtyFree(pty); + free(b.p); +} + +TEST(pty, test) { + struct MachinePty *pty = MachinePtyNew(); + MachinePtyWrite(pty, kKiloAnsi, strlen(kKiloAnsi)); + EXPECT_STREQN(kKiloGolden, pty->wcs, wcslen(kKiloGolden)); + MachinePtyFree(pty); +} + +BENCH(pty, bench) { + struct MachinePty *pty = MachinePtyNew(); + EZBENCH2("pty write ascii", donothing, + MachinePtyWrite(pty, hyperion, sizeof(hyperion) - 1)); + EZBENCH2("pty write kilo", donothing, + MachinePtyWrite(pty, kKiloAnsi, sizeof(kKiloAnsi) - 1)); + EZBENCH2("pty render", donothing, render(pty)); +} diff --git a/test/tool/build/lib/test.mk b/test/tool/build/lib/test.mk index 88a00835..410689ab 100644 --- a/test/tool/build/lib/test.mk +++ b/test/tool/build/lib/test.mk @@ -36,6 +36,7 @@ TEST_TOOL_BUILD_LIB_DIRECTDEPS = \ LIBC_NEXGEN32E \ LIBC_RUNTIME \ LIBC_FMT \ + LIBC_STR \ LIBC_STDIO \ LIBC_LOG \ LIBC_SYSV \ diff --git a/libc/nexgen32e/clamp1.S b/third_party/dtoa/divmax.S similarity index 89% rename from libc/nexgen32e/clamp1.S rename to third_party/dtoa/divmax.S index 4247acf6..e5733ad6 100644 --- a/libc/nexgen32e/clamp1.S +++ b/third_party/dtoa/divmax.S @@ -18,18 +18,16 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/macros.h" +.source __FILE__ -clamp1: .leafprologue - minps .Lmax(%rip),%xmm0 - maxps .Lmin(%rip),%xmm0 - .leafepilogue - .endfn clamp1,globl,hidden +/ Avoid dtoa needing .data section. + .bss + .align 4 +dtoa_divmax: + .zero 4 + .endobj dtoa_divmax,globl + .previous - .rodata.cst16 -.Lmin: .rept 4 - .float 0 - .endr -.Lmax: .rept 4 - .float 0.99609375 - .endr - .source __FILE__ + .init.start 202,_init_dtoa_divmax + movb $2,dtoa_divmax(%rip) + .init.end 202,_init_dtoa_divmax diff --git a/third_party/dtoa/dtoa.c b/third_party/dtoa/dtoa.c index 16a1470a..8d72df56 100644 --- a/third_party/dtoa/dtoa.c +++ b/third_party/dtoa/dtoa.c @@ -400,10 +400,11 @@ Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. #ifdef SET_INEXACT #define dtoa_divmax 27 #else -int dtoa_divmax = 2; /* Permit experimenting: on some systems, 64-bit integer */ - /* division is slow enough that we may sometimes want to */ - /* avoid using it. We assume (but do not check) that */ - /* dtoa_divmax <= 27.*/ +/* Permit experimenting: on some systems, 64-bit integer */ +/* division is slow enough that we may sometimes want to */ +/* avoid using it. We assume (but do not check) that */ +/* dtoa_divmax <= 27.*/ +extern int dtoa_divmax; #endif typedef struct BF96 { /* Normalized 96-bit software floating point numbers */ diff --git a/third_party/duktape/duktape.mk b/third_party/duktape/duktape.mk index d0b5ad6c..70487a70 100644 --- a/third_party/duktape/duktape.mk +++ b/third_party/duktape/duktape.mk @@ -32,7 +32,7 @@ THIRD_PARTY_DUKTAPE_A_DIRECTDEPS = \ LIBC_FMT \ LIBC_TIME \ LIBC_MEM \ - LIBC_MATH \ + LIBC_TINYMATH \ LIBC_UNICODE \ LIBC_NEXGEN32E diff --git a/third_party/stb/ycbcr.c b/third_party/stb/ycbcr.c index d2e9d6bd..30ae98b0 100644 --- a/third_party/stb/ycbcr.c +++ b/third_party/stb/ycbcr.c @@ -26,7 +26,7 @@ /* this is a reduced-precision calculation of YCbCr-to-RGB introduced to make sure the code produces the same results in both SIMD and scalar */ -#define FLOAT2FIXED(x) (((int)((x)*4096.0f + 0.5f)) << 8) +#define FLOAT2FIXED(x) (((int)((x)*4096.0f + .5f)) << 8) void stbi__YCbCr_to_RGB_row(unsigned char *out, const unsigned char *y, const unsigned char *pcb, const unsigned char *pcr, @@ -45,9 +45,9 @@ void stbi__YCbCr_to_RGB_row(unsigned char *out, const unsigned char *y, g = y_fixed + (cr * -FLOAT2FIXED(0.71414f)) + ((cb * -FLOAT2FIXED(0.34414f)) & 0xffff0000); b = y_fixed + cb * FLOAT2FIXED(1.77200f); - r /= 1048576; - g /= 1048576; - b /= 1048576; + r >>= 20; + g >>= 20; + b >>= 20; b4[0] = MIN(255, MAX(0, r)); b4[1] = MIN(255, MAX(0, g)); b4[2] = MIN(255, MAX(0, b)); diff --git a/tool/build/emulator.c b/tool/build/blinkenlights.c similarity index 61% rename from tool/build/emulator.c rename to tool/build/blinkenlights.c index b9ae03b1..02932944 100644 --- a/tool/build/emulator.c +++ b/tool/build/blinkenlights.c @@ -19,9 +19,11 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "dsp/tty/tty.h" #include "libc/alg/arraylist2.h" +#include "libc/assert.h" #include "libc/bits/safemacros.h" #include "libc/calls/calls.h" #include "libc/calls/ioctl.h" +#include "libc/calls/struct/iovec.h" #include "libc/calls/struct/sigaction.h" #include "libc/calls/struct/stat.h" #include "libc/calls/struct/termios.h" @@ -42,12 +44,14 @@ #include "libc/sock/sock.h" #include "libc/stdio/stdio.h" #include "libc/str/str.h" +#include "libc/str/thompike.h" #include "libc/str/tpdecode.h" #include "libc/str/tpencode.h" #include "libc/sysv/consts/auxv.h" #include "libc/sysv/consts/ex.h" #include "libc/sysv/consts/exit.h" #include "libc/sysv/consts/fileno.h" +#include "libc/sysv/consts/itimer.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/poll.h" @@ -81,6 +85,7 @@ #include "tool/build/lib/pml4t.h" #include "tool/build/lib/pty.h" #include "tool/build/lib/stats.h" +#include "tool/build/lib/syscall.h" #include "tool/build/lib/throw.h" #define USAGE \ @@ -88,7 +93,8 @@ \n\ DESCRIPTION\n\ \n\ - x86 Visualizing Emulator\n\ + Emulates x86 Linux Programs w/ Dense Machine State Visualization\n\ + Please keep still and only watchen astaunished das blinkenlights\n\ \n\ FLAGS\n\ \n\ @@ -108,18 +114,9 @@ ARGUMENTS\n\ It should use x86_64 in accordance with the System Five ABI.\n\ The SYSCALL ABI is defined as it is written in Linux Kernel.\n\ \n\ -PERFORMANCE\n\ +FEATURES\n\ \n\ - 1000 MIPS w/ NOP loop\n\ - Over 9000 MIPS w/ SIMD & Algorithms\n\ -\n\ -COMPLETENESS\n\ -\n\ - Long user mode is supported in addition to SSE3 and SSSE3.\n\ - Real mode and legacy mode are supported with limited APIs.\n\ - Integer ops are implemented rigorously with lots of tests.\n\ - Floating point instructions are yolo, and tunable more so.\n\ - Loading, virtual memory management, and SYSCALL need work.\n\ + 8086, 8087, i386, x86_64, SSE3, SSSE3, POPCNT, MDA, CGA, TTY\n\ \n" #define DUMPWIDTH 64 @@ -136,10 +133,45 @@ COMPLETENESS\n\ #define INT 0x100 #define QUIT 0x200 #define EXIT 0x400 +#define ALARM 0x800 -#define CTRL(C) ((C) ^ 0100) -#define ALT(C) (('\e' << 010) | (C)) -#define SEX(x, b) ((x) | ((x) & (1ull << (b)) ? -(1ull << (b)) : 0)) +#define kXmmIntegral 0 +#define kXmmDouble 1 +#define kXmmFloat 2 + +#define kXmmDecimal 0 +#define kXmmHex 1 +#define kXmmChar 2 + +#define CTRL(C) ((C) ^ 0100) + +enum Mouse { + kMouseLeftDown = 0, + kMouseMiddleDown = 1, + kMouseRightDown = 2, + kMouseLeftUp = 4, + kMouseMiddleUp = 5, + kMouseRightUp = 6, + kMouseLeftDrag = 32, + kMouseMiddleDrag = 33, + kMouseRightDrag = 34, + kMouseWheelUp = 64, + kMouseWheelDown = 65, +}; + +struct MachineState { + uint64_t ip; + uint8_t cs[8]; + uint8_t ss[8]; + uint8_t es[8]; + uint8_t ds[8]; + uint8_t fs[8]; + uint8_t gs[8]; + uint8_t reg[16][8]; + uint8_t xmm[16][16]; + struct MachineFpu fpu; + struct MachineSse sse; +}; struct Panels { union { @@ -149,8 +181,8 @@ struct Panels { struct Panel breakpoints; struct Panel mapshr; struct Panel maps; - struct Panel tracehr; - struct Panel trace; + struct Panel frameshr; + struct Panel frames; struct Panel displayhr; struct Panel display; struct Panel registers; @@ -174,44 +206,59 @@ static const char kRegisterNames[16][4] = { "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", }; -static const char kSegmentNames[16][4] = { - "ES", "CS", "SS", "DS", "FS", "GS", -}; +static bool react; +static bool tuimode; +static bool alarmed; +static bool colorize; +static bool mousemode; +static bool printstats; static int tyn; static int txn; static int tick; static int speed; static int vidya; -static int ttyfd; -static bool react; -static bool ssehex; +static int ttyin; +static int focus; +static int ttyout; +static int opline; +static int action; +static int xmmdisp; static int exitcode; -static bool tuimode; +static int codezoom; +static int readzoom; +static int writezoom; +static int stackzoom; + static long rombase; -static bool colorize; static long codesize; -static long ssewidth; -static char *codepath; -static void *onbusted; -static unsigned focus; -static unsigned opline; -static bool printstats; -static unsigned action; static int64_t opstart; -static int64_t memstart; -static struct Elf elf[1]; -static struct Dis dis[1]; -static struct Panels pan; +static int64_t codestart; static int64_t readstart; +static int64_t mapsstart; static int64_t writestart; static int64_t stackstart; -static struct Machine m[2]; +static int64_t framesstart; +static int64_t breakpointsstart; +static char *codepath; +static void *onbusted; +static char *statusmessage; +static struct Machine *m; static struct MachinePty *pty; + +static struct Panels pan; +static struct MachineState laststate; +static struct Breakpoints breakpoints; +static struct Elf elf[1]; +static struct Dis dis[1]; +static uint8_t xmmtype[16]; +static uint8_t xmmsize[16]; + +static long double statusexpires; +static struct termios oldterm; static char logpath[PATH_MAX]; static char systemfailure[128]; static struct sigaction oldsig[4]; -static struct Breakpoints breakpoints; static void SetupDraw(void); static void Redraw(void); @@ -220,6 +267,13 @@ static char *FormatDouble(char *b, double x) { return g_fmt(b, x); } +static int64_t SignExtend(uint64_t x, char b) { + char k; + assert(1 <= b && b <= 64); + k = 64 - b; + return (int64_t)(x << k) >> k; +} + static void SetCarry(bool cf) { m->flags = SetFlag(m->flags, FLAGS_CF, cf); } @@ -241,37 +295,58 @@ static bool IsDebugBreak(void) { } static bool IsRet(void) { - if (m->xedd->op.map == XED_ILD_MAP0) { - switch (m->xedd->op.opcode) { - case 0xC2: - case 0xC3: - case 0xCA: - case 0xCB: - case 0xCF: - return true; - default: - return false; - } - } else { - return false; + switch (m->xedd->op.dispatch) { + case 0x0C2: + case 0x0C3: + case 0x0CA: + case 0x0CB: + case 0x0CF: + return true; + default: + return false; } } -static uint8_t CycleSseType(uint8_t t) { +static int GetXmmTypeCellCount(int r) { + switch (xmmtype[r]) { + case kXmmIntegral: + return 16 / xmmsize[r]; + case kXmmFloat: + return 4; + case kXmmDouble: + return 2; + default: + unreachable; + } +} + +static uint8_t CycleXmmType(uint8_t t) { switch (t) { + default: case kXmmIntegral: return kXmmFloat; case kXmmFloat: return kXmmDouble; case kXmmDouble: return kXmmIntegral; - default: - unreachable; } } -static uint8_t CycleSseWidth(uint8_t w) { +static uint8_t CycleXmmDisp(uint8_t t) { + switch (t) { + default: + case kXmmDecimal: + return kXmmHex; + case kXmmHex: + return kXmmChar; + case kXmmChar: + return kXmmDecimal; + } +} + +static uint8_t CycleXmmSize(uint8_t w) { switch (w) { + default: case 1: return 2; case 2: @@ -280,8 +355,6 @@ static uint8_t CycleSseWidth(uint8_t w) { return 8; case 8: return 1; - default: - unreachable; } } @@ -291,31 +364,187 @@ static int GetPointerWidth(void) { static int64_t GetIp(void) { switch (GetPointerWidth()) { + default: case 8: return m->ip; case 4: return Read64(m->cs) + (m->ip & 0xffff); case 2: return Read64(m->cs) + (m->ip & 0xffff); - default: - abort(); } } static int64_t GetSp(void) { switch (GetPointerWidth()) { + default: case 8: return Read64(m->sp); case 4: return Read64(m->ss) + Read32(m->sp); case 2: return Read64(m->ss) + Read16(m->sp); - default: - abort(); } } -static void OnBusted(void) { +static void UpdateXmmTypes(int regtype, int rmtype) { + xmmtype[RexrReg(m->xedd->op.rde)] = regtype; + if (IsModrmRegister(m->xedd->op.rde)) { + xmmtype[RexbRm(m->xedd->op.rde)] = rmtype; + } +} + +static void UpdateXmmSizes(int regsize, int rmsize) { + xmmsize[RexrReg(m->xedd->op.rde)] = regsize; + if (IsModrmRegister(m->xedd->op.rde)) { + xmmsize[RexbRm(m->xedd->op.rde)] = rmsize; + } +} + +static void UpdateXmmType(void) { + switch (m->xedd->op.dispatch) { + case 0x12E: // UCOMIS + case 0x12F: // COMIS + case 0x151: // SQRT + case 0x152: // RSQRT + case 0x153: // RCP + case 0x158: // ADD + case 0x159: // MUL + case 0x15C: // SUB + case 0x15D: // MIN + case 0x15E: // DIV + case 0x15F: // MAX + case 0x1C2: // CMP + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { + UpdateXmmTypes(kXmmDouble, kXmmDouble); + } else { + UpdateXmmTypes(kXmmFloat, kXmmFloat); + } + break; + case 0x12A: // CVTPI2PS,CVTSI2SS,CVTPI2PD,CVTSI2SD + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { + UpdateXmmSizes(8, 4); + UpdateXmmTypes(kXmmDouble, kXmmIntegral); + } else { + UpdateXmmSizes(4, 4); + UpdateXmmTypes(kXmmFloat, kXmmIntegral); + } + break; + case 0x15A: // CVT{P,S}{S,D}2{P,S}{S,D} + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 2) { + UpdateXmmTypes(kXmmFloat, kXmmDouble); + } else { + UpdateXmmTypes(kXmmDouble, kXmmFloat); + } + break; + case 0x15B: // CVT{,T}{DQ,PS}2{PS,DQ} + UpdateXmmSizes(4, 4); + if (Osz(m->xedd->op.rde) || Rep(m->xedd->op.rde) == 3) { + UpdateXmmTypes(kXmmIntegral, kXmmFloat); + } else { + UpdateXmmTypes(kXmmFloat, kXmmIntegral); + } + break; + case 0x17C: // HADD + case 0x17D: // HSUB + case 0x1D0: // ADDSUB + if (Osz(m->xedd->op.rde)) { + UpdateXmmTypes(kXmmDouble, kXmmDouble); + } else { + UpdateXmmTypes(kXmmFloat, kXmmFloat); + } + break; + case 0x164: // PCMPGTB + case 0x174: // PCMPEQB + case 0x1D8: // PSUBUSB + case 0x1DA: // PMINUB + case 0x1DC: // PADDUSB + case 0x1DE: // PMAXUB + case 0x1E0: // PAVGB + case 0x1E8: // PSUBSB + case 0x1EC: // PADDSB + case 0x1F8: // PSUBB + case 0x1FC: // PADDB + UpdateXmmSizes(1, 1); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x165: // PCMPGTW + case 0x175: // PCMPEQW + case 0x171: // PSRLW,PSRAW,PSLLW + case 0x1D1: // PSRLW + case 0x1D5: // PMULLW + case 0x1D9: // PSUBUSW + case 0x1DD: // PADDUSW + case 0x1E1: // PSRAW + case 0x1E3: // PAVGW + case 0x1E4: // PMULHUW + case 0x1E5: // PMULHW + case 0x1E9: // PSUBSW + case 0x1EA: // PMINSW + case 0x1ED: // PADDSW + case 0x1EE: // PMAXSW + case 0x1F1: // PSLLW + case 0x1F6: // PSADBW + case 0x1F9: // PSUBW + case 0x1FD: // PADDW + UpdateXmmSizes(2, 2); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x166: // PCMPGTD + case 0x176: // PCMPEQD + case 0x172: // PSRLD,PSRAD,PSLLD + case 0x1D2: // PSRLD + case 0x1E2: // PSRAD + case 0x1F2: // PSLLD + case 0x1FA: // PSUBD + case 0x1FE: // PADDD + UpdateXmmSizes(4, 4); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x173: // PSRLQ,PSRLQ,PSRLDQ,PSLLQ,PSLLDQ + case 0x1D3: // PSRLQ + case 0x1D4: // PADDQ + case 0x1F3: // PSLLQ + case 0x1F4: // PMULUDQ + case 0x1FB: // PSUBQ + UpdateXmmSizes(8, 8); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x16B: // PACKSSDW + case 0x1F5: // PMADDWD + UpdateXmmSizes(4, 2); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x163: // PACKSSWB + case 0x167: // PACKUSWB + UpdateXmmSizes(1, 2); + UpdateXmmTypes(kXmmIntegral, kXmmIntegral); + break; + case 0x16F: // MOVDQA Vdq Wdq + if (Osz(m->xedd->op.rde) && IsModrmRegister(m->xedd->op.rde)) { + xmmtype[RexrReg(m->xedd->op.rde)] = xmmtype[RexbRm(m->xedd->op.rde)]; + xmmsize[RexrReg(m->xedd->op.rde)] = xmmsize[RexbRm(m->xedd->op.rde)]; + } + break; + default: + return; + } +} + +static void CopyMachineState(struct MachineState *ms) { + ms->ip = m->ip; + memcpy(ms->cs, m->cs, sizeof(m->cs)); + memcpy(ms->ss, m->ss, sizeof(m->ss)); + memcpy(ms->es, m->es, sizeof(m->es)); + memcpy(ms->ds, m->ds, sizeof(m->ds)); + memcpy(ms->fs, m->fs, sizeof(m->fs)); + memcpy(ms->gs, m->gs, sizeof(m->gs)); + memcpy(ms->reg, m->reg, sizeof(m->reg)); + memcpy(ms->xmm, m->xmm, sizeof(m->xmm)); + memcpy(&ms->fpu, &m->fpu, sizeof(m->fpu)); + memcpy(&ms->sse, &m->sse, sizeof(m->sse)); +} + +static void OnSigBusted(void) { CHECK(onbusted); longjmp(onbusted, 1); } @@ -348,32 +577,74 @@ static void ScrollOp(struct Panel *p, long op) { } } -static void GetTtySize(void) { +static int TtyWriteString(const char *s) { + return write(ttyout, s, strlen(s)); +} + +static void OnFeed(void) { + TtyWriteString("\e[K\e[2J"); +} + +static void HideCursor(void) { + TtyWriteString("\e[?25l"); +} + +static void ShowCursor(void) { + TtyWriteString("\e[?25h"); +} + +static void EnableMouseTracking(void) { + mousemode = true; + TtyWriteString("\e[?1000;1002;1015;1006h"); +} + +static void DisableMouseTracking(void) { + mousemode = false; + TtyWriteString("\e[?1000;1002;1015;1006l"); +} + +static void ToggleMouseTracking(void) { + if (mousemode) { + DisableMouseTracking(); + } else { + EnableMouseTracking(); + } +} + +static void LeaveScreen(void) { + TtyWriteString(gc(xasprintf("\e[%d;%dH\e[S\r\n", tyn, txn))); +} + +static void GetTtySize(int fd) { struct winsize wsize; - wsize.ws_row = 25; - wsize.ws_col = 80; - getttysize(ttyfd, &wsize); + wsize.ws_row = tyn; + wsize.ws_col = txn; + getttysize(fd, &wsize); tyn = wsize.ws_row; txn = wsize.ws_col; } static void TuiRejuvinate(void) { - GetTtySize(); - ttyhidecursor(STDOUT_FILENO); - ttyraw(kTtySigs); - xsigaction(SIGBUS, OnBusted, SA_NODEFER, 0, NULL); -} - -static void OnCtrlC(void) { - if (tuimode) { - action |= INT; - } else { - HaltMachine(m, kMachineExit); - } + struct termios term; + DEBUGF("TuiRejuvinate"); + GetTtySize(ttyout); + HideCursor(); + memcpy(&term, &oldterm, sizeof(term)); + term.c_cc[VMIN] = 1; + term.c_cc[VTIME] = 1; + term.c_iflag &= ~(INPCK | ISTRIP | PARMRK | INLCR | IGNCR | ICRNL | IXON); + term.c_lflag &= ~(IEXTEN | ICANON | ECHO | ECHONL); + term.c_cflag &= ~(CSIZE | PARENB); + term.c_cflag |= CS8; + term.c_iflag |= IUTF8; + CHECK_NE(-1, ioctl(ttyout, TCSETS, &term)); + xsigaction(SIGBUS, OnSigBusted, SA_NODEFER, 0, NULL); + EnableMouseTracking(); } static void OnQ(void) { LOGF("OnQ"); + if (action & FAILURE) exit(1); action |= INT; breakpoints.i = 0; } @@ -382,24 +653,44 @@ static void OnV(void) { vidya = !vidya; } -static void OnWinch(void) { - LOGF("OnWinch"); +static void OnSigWinch(void) { action |= WINCHED; } -static void OnCont(void) { - LOGF("OnCont"); +static void OnSigInt(void) { + if (tuimode) { + action |= INT; + } else { + action |= EXIT; + } +} + +static void OnSigAlarm(void) { + action |= ALARM; +} + +static void OnSigCont(void) { TuiRejuvinate(); SetupDraw(); Redraw(); } +static void TtyRestore1(void) { + DEBUGF("TtyRestore1"); + ShowCursor(); + TtyWriteString("\e[0m"); +} + +static void TtyRestore2(void) { + DEBUGF("TtyRestore2"); + ioctl(ttyout, TCSETS, &oldterm); + DisableMouseTracking(); +} + static void TuiCleanup(void) { - sigaction(SIGWINCH, oldsig + 0, NULL); sigaction(SIGCONT, oldsig + 2, NULL); - ttyraw(-1); - ttyshowcursor(STDOUT_FILENO); - CHECK_NE(-1, close(ttyfd)); + TtyRestore1(); + DisableMouseTracking(); tuimode = false; } @@ -419,7 +710,10 @@ static void ResolveBreakpoints(void) { } static void BreakAtNextInstruction(void) { - struct Breakpoint b = {.addr = m->ip + m->xedd->length, .oneshot = true}; + struct Breakpoint b; + memset(&b, 0, sizeof(b)); + b.addr = GetIp() + m->xedd->length; + b.oneshot = true; PushBreakpoint(&breakpoints, &b); } @@ -428,21 +722,71 @@ static void LoadSyms(void) { DisLoadElf(dis, elf); } +static int DrainInput(int fd) { + char buf[32]; + struct pollfd fds[1]; + for (;;) { + fds[0].fd = fd; + fds[0].events = POLLIN; + if (poll(fds, ARRAYLEN(fds), 0) == -1) return -1; + if (!(fds[0].revents & POLLIN)) break; + if (read(fd, buf, sizeof(buf)) == -1) return -1; + } + return 0; +} + +static int ReadCursorPosition(int *out_y, int *out_x) { + int y, x; + char *p, buf[32]; + if (readansi(ttyin, buf, sizeof(buf)) == 1) return -1; + p = buf; + if (*p == '\e') ++p; + if (*p == '[') ++p; + y = strtol(p, &p, 10); + if (*p == ';') ++p; + x = strtol(p, &p, 10); + if (*p != 'R') return ebadmsg(); + if (out_y) *out_y = MAX(1, y) - 1; + if (out_x) *out_x = MAX(1, x) - 1; + return 0; +} + +static int GetCursorPosition(int *out_y, int *out_x) { + TtyWriteString("\e[6n"); + return ReadCursorPosition(out_y, out_x); +} + +static int GetTerminalDimensions(int *out_y, int *out_x) { + TtyWriteString("\e7\e[9979;9979H\e[6n\e8"); + return ReadCursorPosition(out_y, out_x); +} + void TuiSetup(void) { + int y, x; + bool report; static bool once; + report = false; if (!once) { - LOGF("loaded program %s\n%s", codepath, gc(FormatPml4t(m->cr3))); + LOGF("loaded program %s\n%s", codepath, gc(FormatPml4t(m))); LoadSyms(); ResolveBreakpoints(); - Dis(dis, m, elf->base, elf->base, 100); + ioctl(ttyout, TCGETS, &oldterm); + xsigaction(SIGALRM, OnSigAlarm, 0, 0, 0); + xsigaction(SIGINT, OnSigInt, 0, 0, oldsig + 3); + atexit(TtyRestore2); once = true; + report = true; } - CHECK_NE(-1, (ttyfd = open("/dev/tty", O_RDWR))); - xsigaction(SIGWINCH, OnWinch, 0, 0, oldsig + 0); - xsigaction(SIGCONT, OnCont, SA_RESTART, 0, oldsig + 2); - xsigaction(SIGINT, OnCtrlC, 0 /* SA_NODEFER */, 0, oldsig + 3); - memcpy(&m[1], &m[0], sizeof(m[0])); + xsigaction(SIGCONT, OnSigCont, SA_RESTART | SA_NODEFER, 0, oldsig + 2); + CopyMachineState(&laststate); TuiRejuvinate(); + if (report) { + DrainInput(ttyin); + y = 0; + if (GetCursorPosition(&y, NULL) != -1) { + TtyWriteString(gc(xasprintf("\e[%dS", y))); + } + } } static void ExecSetup(void) { @@ -513,7 +857,7 @@ static void SetupDraw(void) { if (ssey) ++ssey; a = 12 + 1 + DUMPWIDTH; - b = DISPWIDTH; + b = DISPWIDTH + 1; dx[1] = txn >= a + b ? txn - a : txn; dx[0] = txn >= a + b + b ? txn - a - b : dx[1]; @@ -566,15 +910,15 @@ static void SetupDraw(void) { pan.maps.bottom = c2y[1]; pan.maps.right = dx[1] - 1; - pan.tracehr.top = c2y[1]; - pan.tracehr.left = dx[0]; - pan.tracehr.bottom = c2y[1] + 1; - pan.tracehr.right = dx[1] - 1; + pan.frameshr.top = c2y[1]; + pan.frameshr.left = dx[0]; + pan.frameshr.bottom = c2y[1] + 1; + pan.frameshr.right = dx[1] - 1; - pan.trace.top = c2y[1] + 1; - pan.trace.left = dx[0]; - pan.trace.bottom = c2y[2]; - pan.trace.right = dx[1] - 1; + pan.frames.top = c2y[1] + 1; + pan.frames.left = dx[0]; + pan.frames.bottom = c2y[2]; + pan.frames.right = dx[1] - 1; pan.displayhr.top = c2y[2]; pan.displayhr.left = dx[0]; @@ -688,18 +1032,37 @@ static void DrawHr(struct Panel *p, const char *s) { long i, wp, ws, wl, wr; if (p->bottom - p->top < 1) return; wp = p->right - p->left; - ws = strlen(s); + ws = strwidth(s); wl = wp / 4 - ws / 2; wr = wp - (wl + ws); for (i = 0; i < wl; ++i) AppendWide(&p->lines[0], u'─'); AppendStr(&p->lines[0], s); for (i = 0; i < wr; ++i) AppendWide(&p->lines[0], u'─'); + AppendStr(&p->lines[0], "\e[0m"); } -static void DrawTerminal(struct Panel *p) { - long y, yn; +void DrawTerminal(struct Panel *p) { + long i, y, yn; + if (pty->conf & kMachinePtyBell) { + if (!alarmed) { + alarmed = true; + setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {0, 800000}}), NULL); + } + AppendStr(&pan.displayhr.lines[0], "\e[1m"); + } + AppendStr( + &pan.displayhr.lines[0], + gc(xasprintf("──────────TELETYPEWRITER──%s──%s──%s──%s", + (pty->conf & kMachinePtyLed1) ? "\e[1;31m◎\e[0m" : "○", + (pty->conf & kMachinePtyLed2) ? "\e[1;32m◎\e[0m" : "○", + (pty->conf & kMachinePtyLed3) ? "\e[1;33m◎\e[0m" : "○", + (pty->conf & kMachinePtyLed4) ? "\e[1;34m◎\e[0m" : "○"))); + for (i = 36; i < pan.displayhr.right - pan.displayhr.left; ++i) { + AppendWide(&pan.displayhr.lines[0], u'─'); + } for (yn = MIN(pty->yn, p->bottom - p->top), y = 0; y < yn; ++y) { MachinePtyAppendLine(pty, p->lines + y, y); + AppendStr(p->lines + y, "\e[0m"); } } @@ -716,7 +1079,6 @@ void DrawDisplay(struct Panel *p) { VirtualSend(m, gc(xmalloc(25 * 80 * 2)), 0xb8000, 25 * 80 * 2)); break; default: - DrawHr(&pan.displayhr, "TELETYPEWRITER"); DrawTerminal(p); break; } @@ -731,8 +1093,8 @@ static void DrawFlag(struct Panel *p, long i, char name, bool value) { static void DrawRegister(struct Panel *p, long i, long r) { char buf[32]; uint64_t value, previous; - value = Read64(m[0].reg[r]); - previous = Read64(m[1].reg[r]); + value = Read64(m->reg[r]); + previous = Read64(laststate.reg[r]); if (value != previous) AppendPanel(p, i, "\e[7m"); snprintf(buf, sizeof(buf), "%-3s", kRegisterNames[r]); AppendPanel(p, i, buf); @@ -743,13 +1105,14 @@ static void DrawRegister(struct Panel *p, long i, long r) { AppendPanel(p, i, " "); } -static void DrawSegment(struct Panel *p, long i, long r) { +static void DrawSegment(struct Panel *p, long i, const uint8_t seg[8], + const uint8_t last[8], const char *name) { char buf[32]; uint64_t value, previous; - value = Read64(GetSegment(m + 0, 0, r)); - previous = Read64(GetSegment(m + 1, 0, r)); + value = Read64(seg); + previous = Read64(last); if (value != previous) AppendPanel(p, i, "\e[7m"); - snprintf(buf, sizeof(buf), "%-3s", kSegmentNames[r]); + snprintf(buf, sizeof(buf), "%-3s", name); AppendPanel(p, i, buf); AppendPanel(p, i, " "); snprintf(buf, sizeof(buf), "0x%016lx", value); @@ -763,16 +1126,16 @@ static void DrawSt(struct Panel *p, long i, long r) { long double value; bool isempty, changed; isempty = FpuGetTag(m, r) == kFpuTagEmpty; - if (isempty) AppendPanel(p, i, "\e[2m"); - value = m[0].fpu.st[(r + m[0].fpu.sp) & 0b111]; - changed = value != m[1].fpu.st[(r + m[0].fpu.sp) & 0b111]; + if (isempty) AppendPanel(p, i, "\e[38;5;241m"); + value = m->fpu.st[(r + m->fpu.sp) & 0b111]; + changed = value != laststate.fpu.st[(r + m->fpu.sp) & 0b111]; if (!isempty && changed) AppendPanel(p, i, "\e[7m"); snprintf(buf, sizeof(buf), "ST%d ", r); AppendPanel(p, i, buf); AppendPanel(p, i, FormatDouble(buf, value)); if (changed) AppendPanel(p, i, "\e[27m"); AppendPanel(p, i, " "); - if (isempty) AppendPanel(p, i, "\e[22m"); + if (isempty) AppendPanel(p, i, "\e[39m"); } static void DrawCpu(struct Panel *p) { @@ -785,16 +1148,16 @@ static void DrawCpu(struct Panel *p) { DrawRegister(p, 5, 9), DrawRegister(p, 5, 13), DrawSt(p, 5, 5); DrawRegister(p, 6, 10), DrawRegister(p, 6, 14), DrawSt(p, 6, 6); DrawRegister(p, 7, 11), DrawRegister(p, 7, 15), DrawSt(p, 7, 7); - snprintf(buf, sizeof(buf), "RIP 0x%016x FLG", m[0].ip); + snprintf(buf, sizeof(buf), "RIP 0x%016x FLG", m->ip); AppendPanel(p, 8, buf); - DrawFlag(p, 8, 'C', GetFlag(m[0].flags, FLAGS_CF)); - DrawFlag(p, 8, 'P', GetFlag(m[0].flags, FLAGS_PF)); - DrawFlag(p, 8, 'A', GetFlag(m[0].flags, FLAGS_AF)); - DrawFlag(p, 8, 'Z', GetFlag(m[0].flags, FLAGS_ZF)); - DrawFlag(p, 8, 'S', GetFlag(m[0].flags, FLAGS_SF)); - DrawFlag(p, 8, 'I', GetFlag(m[0].flags, FLAGS_IF)); - DrawFlag(p, 8, 'D', GetFlag(m[0].flags, FLAGS_DF)); - DrawFlag(p, 8, 'O', GetFlag(m[0].flags, FLAGS_OF)); + DrawFlag(p, 8, 'C', GetFlag(m->flags, FLAGS_CF)); + DrawFlag(p, 8, 'P', GetFlag(m->flags, FLAGS_PF)); + DrawFlag(p, 8, 'A', GetFlag(m->flags, FLAGS_AF)); + DrawFlag(p, 8, 'Z', GetFlag(m->flags, FLAGS_ZF)); + DrawFlag(p, 8, 'S', GetFlag(m->flags, FLAGS_SF)); + DrawFlag(p, 8, 'I', GetFlag(m->flags, FLAGS_IF)); + DrawFlag(p, 8, 'D', GetFlag(m->flags, FLAGS_DF)); + DrawFlag(p, 8, 'O', GetFlag(m->flags, FLAGS_OF)); AppendPanel(p, 8, " "); if (m->fpu.ie) AppendPanel(p, 8, " IE"); if (m->fpu.de) AppendPanel(p, 8, " DE"); @@ -808,44 +1171,34 @@ static void DrawCpu(struct Panel *p) { if (m->fpu.c1) AppendPanel(p, 8, " C1"); if (m->fpu.c2) AppendPanel(p, 8, " C2"); if (m->fpu.bf) AppendPanel(p, 8, " BF"); - DrawSegment(p, 9, 4), DrawSegment(p, 9, 3), DrawSegment(p, 9, 1); - DrawSegment(p, 10, 5), DrawSegment(p, 10, 0), DrawSegment(p, 10, 2); + DrawSegment(p, 9, m->fs, laststate.fs, "FS"); + DrawSegment(p, 9, m->ds, laststate.ds, "DS"); + DrawSegment(p, 9, m->cs, laststate.cs, "CS"); + DrawSegment(p, 10, m->gs, laststate.gs, "GS"); + DrawSegment(p, 10, m->es, laststate.es, "ES"); + DrawSegment(p, 10, m->ss, laststate.ss, "SS"); } static void DrawXmm(struct Panel *p, long i, long r) { float f; double d; long j, k, n; - uint8_t type; bool changed; char buf[32]; uint8_t xmm[16]; uint64_t ival, itmp; int cells, left, cellwidth, panwidth; memcpy(xmm, m->xmm[r], sizeof(xmm)); - changed = memcmp(xmm, m[1].xmm[r], sizeof(xmm)) != 0; + changed = memcmp(xmm, laststate.xmm[r], sizeof(xmm)) != 0; if (changed) AppendPanel(p, i, "\e[7m"); left = sprintf(buf, "XMM%-2d", r); AppendPanel(p, i, buf); - type = m->xmmtype[r / 8][r % 8]; - switch (type) { - case kXmmFloat: - cells = 4; - break; - case kXmmDouble: - cells = 2; - break; - case kXmmIntegral: - cells = 16 / ssewidth; - break; - default: - unreachable; - } + cells = GetXmmTypeCellCount(r); panwidth = p->right - p->left; cellwidth = MIN(MAX(0, (panwidth - left) / cells - 1), sizeof(buf) - 1); for (j = 0; j < cells; ++j) { AppendPanel(p, i, " "); - switch (type) { + switch (xmmtype[r]) { case kXmmFloat: memcpy(&f, xmm + j * sizeof(f), sizeof(f)); FormatDouble(buf, f); @@ -856,19 +1209,19 @@ static void DrawXmm(struct Panel *p, long i, long r) { break; case kXmmIntegral: ival = 0; - for (k = 0; k < ssewidth; ++k) { - itmp = xmm[j * ssewidth + k] & 0xff; + for (k = 0; k < xmmsize[r]; ++k) { + itmp = xmm[j * xmmsize[r] + k] & 0xff; itmp <<= k * 8; ival |= itmp; } - if (!ssehex) { - if (0 && ssewidth == 1 && (040 <= ival && ival < 0200 - 1)) { - sprintf(buf, "%`'c", ival); + if (xmmdisp == kXmmHex || xmmdisp == kXmmChar) { + if (xmmdisp == kXmmChar && iswalnum(ival)) { + sprintf(buf, "%lc", ival); } else { - int64toarray_radix10(SEX(ival, ssewidth * 8), buf); + uint64toarray_fixed16(ival, buf, xmmsize[r] * 8); } } else { - uint64toarray_fixed16(ival, buf, ssewidth * 8); + int64toarray_radix10(SignExtend(ival, xmmsize[r] * 8), buf); } break; default: @@ -895,15 +1248,15 @@ static void ScrollCode(struct Panel *p) { long i, n; n = p->bottom - p->top; i = GetIp() / DUMPWIDTH; - if (!(memstart <= i && i < memstart + n)) { - memstart = i; + if (!(codestart <= i && i < codestart + n)) { + codestart = i; } } static void ScrollReadData(struct Panel *p) { long i, n, addr; n = p->bottom - p->top; - i = m->readaddr / DUMPWIDTH; + i = m->readaddr / (DUMPWIDTH * (1 << readzoom)); if (!(readstart <= i && i < readstart + n)) { readstart = i - n / 3; } @@ -927,18 +1280,49 @@ static void ScrollStack(struct Panel *p) { } } -static void DrawMemory(struct Panel *p, long beg, long a, long b) { +static uint8_t Downsample(uint8_t al, uint8_t bl, uint8_t cl, uint8_t dl) { + int16_t ax, bx; + bx = bl; + bx += cl; + bx *= 3; + ax = al; + ax += dl; + ax += bx; + ax += 4; + ax >>= 3; + al = ax; + return al; +} + +static uint8_t Sharpen(uint8_t al, uint8_t bl, uint8_t cl) { + int16_t ax, bx, cx; + ax = al; + bx = bl; + cx = cl; + ax *= -1; + bx *= +6; + cx *= -1; + ax += bx; + ax += cx; + ax += 2; + ax >>= 2; + return MIN(255, MAX(0, ax)); +} + +static void DrawMemory(struct Panel *p, int zoom, long startline, long histart, + long hiend) { char buf[16]; - long i, j, k, c; bool high, changed; + long i, j, k, c, width; high = false; + width = DUMPWIDTH * (1 << zoom); for (i = 0; i < p->bottom - p->top; ++i) { - snprintf(buf, sizeof(buf), "%p ", (beg + i) * DUMPWIDTH); + snprintf(buf, sizeof(buf), "%p ", (startline + i) * width); AppendStr(&p->lines[i], buf); - for (j = 0; j < DUMPWIDTH; ++j) { - k = (beg + i) * DUMPWIDTH + j; + for (j = 0; j < width; ++j) { + k = (startline + i) * DUMPWIDTH + j; c = VirtualBing(k); - changed = a <= k && k < b; + changed = histart <= k && k < hiend; if (changed && !high) { high = true; AppendStr(&p->lines[i], "\e[7m"); @@ -958,10 +1342,12 @@ static void DrawMemory(struct Panel *p, long beg, long a, long b) { static void DrawMaps(struct Panel *p) { int i; char *text, *p1, *p2; - p1 = text = FormatPml4t(m->cr3); + p1 = text = FormatPml4t(m); for (i = 0; p1; ++i, p1 = p2) { if ((p2 = strchr(p1, '\n'))) *p2++ = '\0'; - AppendPanel(p, i, p1); + if (i >= mapsstart) { + AppendPanel(p, i - mapsstart, p1); + } } free(text); } @@ -973,16 +1359,18 @@ static void DrawBreakpoints(struct Panel *p) { long i, line, sym; for (line = 0, i = breakpoints.i; i--;) { if (breakpoints.p[i].disable) continue; - addr = breakpoints.p[i].addr; - sym = DisFindSym(dis, addr); - name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; - s = buf; - s += sprintf(s, "%p ", addr); - CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf)); - AppendPanel(p, line, buf); - if (sym != -1 && addr != dis->syms.p[sym].addr) { - snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr); - AppendPanel(p, line, buf); + if (line >= breakpointsstart) { + addr = breakpoints.p[i].addr; + sym = DisFindSym(dis, addr); + name = sym != -1 ? dis->syms.stab + dis->syms.p[sym].name : "UNKNOWN"; + s = buf; + s += sprintf(s, "%p ", addr); + CHECK_LT(Demangle(s, name, DIS_MAX_SYMBOL_LENGTH), buf + ARRAYLEN(buf)); + AppendPanel(p, line - breakpointsstart, buf); + if (sym != -1 && addr != dis->syms.p[sym].addr) { + snprintf(buf, sizeof(buf), "+%#lx", addr - dis->syms.p[sym].addr); + AppendPanel(p, line, buf); + } } ++line; } @@ -1001,7 +1389,7 @@ static int GetPreferredStackAlignmentMask(void) { } } -static void DrawTrace(struct Panel *p) { +static void DrawFrames(struct Panel *p) { int i, n; long sym; uint8_t *r; @@ -1017,25 +1405,25 @@ static void DrawTrace(struct Panel *p) { s = line; s += sprintf(s, "%p %p ", Read64(m->ss) + bp, rp); s = Demangle(s, name, DIS_MAX_SYMBOL_LENGTH); - AppendPanel(p, i, line); + AppendPanel(p, i - framesstart, line); if (sym != -1 && rp != dis->syms.p[sym].addr) { snprintf(line, sizeof(line), "+%#lx", rp - dis->syms.p[sym].addr); - AppendPanel(p, i, line); + AppendPanel(p, i - framesstart, line); } if (!bp) break; if (bp < sp) { - AppendPanel(p, i, " [STRAY]"); - } else { + AppendPanel(p, i - framesstart, " [STRAY]"); + } else if (bp - sp <= 0x1000) { snprintf(line, sizeof(line), " %,ld bytes", bp - sp); - AppendPanel(p, i, line); + AppendPanel(p, i - framesstart, line); } if (bp & GetPreferredStackAlignmentMask() && i) { - AppendPanel(p, i, " [MISALIGN]"); + AppendPanel(p, i - framesstart, " [MISALIGN]"); } ++i; if (((Read64(m->ss) + bp) & 0xfff) > 0xff0) break; if (!(r = FindReal(m, Read64(m->ss) + bp))) { - AppendPanel(p, i, "CORRUPT FRAME POINTER"); + AppendPanel(p, i - framesstart, "CORRUPT FRAME POINTER"); break; } sp = bp; @@ -1074,6 +1462,10 @@ forceinline void CheckFramePointer(void) { } } +static bool IsExecuting(void) { + return (action & (CONTINUE | STEP | NEXT | FINISH)) && !(action & FAILURE); +} + static void Redraw(void) { int i, j; for (i = 0; i < ARRAYLEN(pan.p); ++i) { @@ -1085,30 +1477,32 @@ static void Redraw(void) { DrawDisplay(&pan.display); DrawCpu(&pan.registers); DrawSse(&pan.sse); - ScrollCode(&pan.code); - ScrollStack(&pan.stack); - ScrollReadData(&pan.readdata); - ScrollWriteData(&pan.writedata); DrawHr(&pan.breakpointshr, "BREAKPOINTS"); DrawHr(&pan.mapshr, "MAPS"); - DrawHr(&pan.tracehr, m->bofram[0] ? "PROTECTED FRAMES" : "FRAMES"); + DrawHr(&pan.frameshr, m->bofram[0] ? "PROTECTED FRAMES" : "FRAMES"); DrawHr(&pan.ssehr, "SSE"); DrawHr(&pan.codehr, "CODE"); DrawHr(&pan.readhr, "READ"); DrawHr(&pan.writehr, "WRITE"); DrawHr(&pan.stackhr, "STACK"); DrawMaps(&pan.maps); - DrawTrace(&pan.trace); + DrawFrames(&pan.frames); DrawBreakpoints(&pan.breakpoints); - DrawMemory(&pan.code, memstart, GetIp(), GetIp() + m->xedd->length); - DrawMemory(&pan.readdata, readstart, m->readaddr, m->readaddr + m->readsize); - DrawMemory(&pan.writedata, writestart, m->writeaddr, + DrawMemory(&pan.code, codezoom, codestart, GetIp(), + GetIp() + m->xedd->length); + DrawMemory(&pan.readdata, readzoom, readstart, m->readaddr, + m->readaddr + m->readsize); + DrawMemory(&pan.writedata, writezoom, writestart, m->writeaddr, m->writeaddr + m->writesize); - DrawMemory(&pan.stack, stackstart, GetSp(), GetSp() + GetPointerWidth()); - if (PrintPanels(ttyfd, ARRAYLEN(pan.p), pan.p, tyn, txn) == -1) { + DrawMemory(&pan.stack, stackzoom, stackstart, GetSp(), + GetSp() + GetPointerWidth()); + if (PrintPanels(ttyout, ARRAYLEN(pan.p), pan.p, tyn, txn) == -1) { LOGF("PrintPanels Interrupted"); CHECK_EQ(EINTR, errno); } + if (statusmessage && nowl() < statusexpires) { + TtyWriteString(statusmessage); + } } static void ReactiveDraw(void) { @@ -1121,6 +1515,37 @@ static void ReactiveDraw(void) { } } +static void HandleAlarm(void) { + alarmed = false; + action &= ~ALARM; + pty->conf &= ~kMachinePtyBell; + free(statusmessage); + statusmessage = NULL; +} + +static void HandleAppReadInterrupt(void) { + DEBUGF("HandleAppReadInterrupt"); + if (action & ALARM) { + HandleAlarm(); + } + if (action & WINCHED) { + GetTtySize(ttyout); + action &= ~WINCHED; + } + if (action & INT) { + action &= ~INT; + if (action & CONTINUE) { + action &= ~CONTINUE; + } else { + if (tuimode) { + LeaveScreen(); + TuiCleanup(); + } + exit(0); + } + } +} + static int OnPtyFdClose(int fd) { return 0; } @@ -1134,39 +1559,88 @@ static bool HasPendingInput(int fd) { return fds[0].revents & (POLLIN | POLLERR); } -static ssize_t ConsumePtyInput(int fd) { +static ssize_t ReadPtyFdDirect(int fd, void *data, size_t size) { char *buf; ssize_t rc; + DEBUGF("ReadPtyFdDirect"); buf = malloc(PAGESIZE); pty->conf |= kMachinePtyBlinkcursor; - ReactiveDraw(); - rc = read(fd, buf, PAGESIZE); + if (tuimode) DisableMouseTracking(); + for (;;) { + ReactiveDraw(); + if ((rc = read(fd, buf, PAGESIZE)) != -1) break; + CHECK_EQ(EINTR, errno); + HandleAppReadInterrupt(); + } + if (tuimode) EnableMouseTracking(); pty->conf &= ~kMachinePtyBlinkcursor; - ReactiveDraw(); - if (rc > 0) MachinePtyWriteInput(pty, buf, rc); + if (rc > 0) { + MachinePtyWriteInput(pty, buf, rc); + ReactiveDraw(); + rc = MachinePtyRead(pty, data, size); + } free(buf); return rc; } -static ssize_t OnPtyFdRead(int fd, void *data, size_t size) { +static ssize_t OnPtyFdReadv(int fd, const struct iovec *iov, int iovlen) { + int i; ssize_t rc; - if (!size) return 0; - if (HasPendingInput(fd)) { - if ((rc = ConsumePtyInput(fd)) <= 0) return rc; + void *data; + size_t size; + for (size = i = 0; i < iovlen; ++i) { + if (iov[i].iov_len) { + data = iov[i].iov_base; + size = iov[i].iov_len; + break; + } } - while (!(rc = MachinePtyRead(pty, data, size))) { - if ((rc = ConsumePtyInput(fd)) <= 0) return rc; + if (size) { + if (!(rc = MachinePtyRead(pty, data, size))) { + rc = ReadPtyFdDirect(fd, data, size); + } + return rc; + } else { + return 0; } - return rc; } -static ssize_t OnPtyFdWrite(int fd, const void *data, size_t size) { - if (tuimode) { - return MachinePtyWrite(pty, data, size); - } else { - MachinePtyWrite(pty, data, size); - return write(fd, data, size); +static void DrawTerminalOnly(void) { + struct Buffer b; + int i, y, yn, xn, tly, tlx, conf; + conf = pty->conf & kMachinePtyNocursor; + pty->conf |= kMachinePtyNocursor; + memset(&b, 0, sizeof(b)); + yn = MIN(tyn, pty->yn); + xn = MIN(txn, pty->xn); + tly = tyn / 2 - yn / 2; + tlx = txn / 2 - xn / 2; + AppendStr(&b, "\e[0m\e[H"); + for (y = 0; y < tyn; ++y) { + if (y) AppendStr(&b, "\r\n"); + if (tly <= y && y <= tly + yn) { + for (i = 0; i < tlx; ++i) { + AppendChar(&b, ' '); + } + MachinePtyAppendLine(pty, &b, y - tly); + } + AppendStr(&b, "\e[0m\e[K"); } + AppendFmt(&b, "\e[%d;%dH", tly + pty->y + 1, tlx + pty->x + 1); + write(ttyout, b.p, b.i); + free(b.p); + pty->conf |= conf; +} + +static ssize_t OnPtyFdWritev(int fd, const struct iovec *iov, int iovlen) { + int i; + size_t size; + for (size = i = 0; i < iovlen; ++i) { + MachinePtyWrite(pty, iov[i].iov_base, iov[i].iov_len); + size += iov[i].iov_len; + } + if (!tuimode) DrawTerminalOnly(); + return size; } static int OnPtyFdTiocgwinsz(int fd, struct winsize *ws) { @@ -1216,8 +1690,8 @@ static int OnPtyFdIoctl(int fd, uint64_t request, void *memory) { static const struct MachineFdCb kMachineFdCbPty = { .close = OnPtyFdClose, - .read = OnPtyFdRead, - .write = OnPtyFdWrite, + .readv = OnPtyFdReadv, + .writev = OnPtyFdWritev, .ioctl = OnPtyFdIoctl, }; @@ -1366,38 +1840,48 @@ static void OnDiskService(void) { } static void OnVidyaServiceSetMode(void) { - vidya = m->ax[0]; + if (FindReal(m, 0xB0000)) { + vidya = m->ax[0]; + } else { + WARNF("maybe you forgot -r flag"); + } } static void OnVidyaServiceGetMode(void) { m->ax[0] = vidya; - m->ax[1] = 80; /* columns */ - m->bx[1] = 0; /* page */ + m->ax[1] = 80; // columns + m->bx[1] = 0; // page } static void OnVidyaServiceSetCursorPosition(void) { - pty->y = m->dx[1]; - pty->x = m->dx[0]; + MachinePtySetY(pty, m->dx[1]); + MachinePtySetX(pty, m->dx[0]); } static void OnVidyaServiceGetCursorPosition(void) { m->dx[1] = pty->y; m->dx[0] = pty->x; - m->cx[1] = 5; /* cursor ▂ scan lines 5..7 of 0..7 */ + m->cx[1] = 5; // cursor ▂ scan lines 5..7 of 0..7 m->cx[0] = 7 | !!(pty->conf & kMachinePtyNocursor) << 5; } +static int GetVidyaByte(unsigned char b) { + if (0x20 <= b && b <= 0x7F) return b; + if (b == 0xFF) b = 0x00; + return kCp437[b]; +} + static void OnVidyaServiceWriteCharacter(void) { - char buf[12]; int i, n, y, x; - y = pty->y; - x = pty->x; - n = FormatCga(m->bx[0], buf); - n += tpencode(buf + n, 6, kCp437[m->ax[0]], false); - i = Read16(m->cx); - while (i--) MachinePtyWrite(pty, buf, n); - pty->y = y; - pty->x = x; + char *p, buf[32]; + p = buf; + p += FormatCga(m->bx[0], p); + p = stpcpy(p, "\e7"); + p += tpencode(p, 8, GetVidyaByte(m->ax[0]), false); + p = stpcpy(p, "\e8"); + for (i = Read16(m->cx); i--;) { + MachinePtyWrite(pty, buf, p - buf); + } } static char16_t VidyaServiceXlatTeletype(uint8_t c) { @@ -1409,7 +1893,7 @@ static char16_t VidyaServiceXlatTeletype(uint8_t c) { case 0177: return c; default: - return kCp437[c]; + return GetVidyaByte(c); } } @@ -1448,18 +1932,19 @@ static void OnVidyaService(void) { static void OnKeyboardServiceReadKeyPress(void) { uint8_t b; - ReactiveDraw(); - read(0, &b, 1); - switch (b) { - case 0177: - b = '\b'; - break; - case CTRL('C'): - raise(SIGINT); - break; - default: - break; + ssize_t rc; + pty->conf |= kMachinePtyBlinkcursor; + if (tuimode) DisableMouseTracking(); + for (;;) { + ReactiveDraw(); + if ((rc = read(0, &b, 1)) != -1) break; + CHECK_EQ(EINTR, errno); + HandleAppReadInterrupt(); } + if (tuimode) EnableMouseTracking(); + pty->conf &= ~kMachinePtyBlinkcursor; + ReactiveDraw(); + if (b == 0x7F) b = '\b'; m->ax[0] = b; m->ax[1] = 0; } @@ -1520,7 +2005,6 @@ static void OnInt15h(void) { } static bool OnHalt(int interrupt) { - LOGF("OnHalt(%d)", interrupt); ReactiveDraw(); switch (interrupt) { case 1: @@ -1576,29 +2060,47 @@ static void OnPageDown(void) { opstart += tyn / 2; } -static void OnUpArrow(void) { - if (action & CONTINUE) { - if (speed >= -1) { - speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip - } else { - speed >>= 1; - } +static void SetStatus(const char *fmt, ...) { + char *s; + va_list va; + int y, x, n; + va_start(va, fmt); + s = gc(xvasprintf(fmt, va)); + va_end(va); + n = strwidth(s); + y = tyn - (n / txn + 1); + x = txn / 2 - n / 2; + free(statusmessage); + statusmessage = xasprintf("\e[0m\e[%d;%dH%s", y + 1, x + 1, s); + TtyWriteString(statusmessage); + setitimer(ITIMER_REAL, &((struct itimerval){{0, 0}, {1, 0}}), NULL); + statusexpires = nowl() + 1; +} + +static void OnTurbo(void) { + if (speed >= -1) { + speed = MIN(0x40000000, MAX(1, speed) << 1); // 1..40mips skip } else { - --opstart; + speed >>= 1; } - LOGF("speed %d", speed); + SetStatus("speed %,d", speed); +} + +static void OnSlowmo(void) { + if (speed > 0) { + speed >>= 1; + } else { + speed = MAX(-(5 * 1000), MIN(-1, speed) << 1); // 1ms..5s delay + } + SetStatus("speed %,d", speed); +} + +static void OnUpArrow(void) { + --opstart; } static void OnDownArrow(void) { - if (action & CONTINUE) { - if (speed > 0) { - speed >>= 1; - } else { - speed = MAX(-(5 * 1000), MIN(-10, speed) << 1); // 10ms..5s delay - } - } else { - ++opstart; - } + ++opstart; } static void OnHome(void) { @@ -1617,10 +2119,6 @@ static void OnTab(void) { focus = (focus + 1) % ARRAYLEN(pan.p); } -static void OnFeed(void) { - write(ttyfd, "\e[K\e[J", 6); -} - static void OnUp(void) { } @@ -1666,116 +2164,172 @@ static void OnRestart(void) { action |= RESTART; } -static void OnSseType(void) { +static void OnXmmType(void) { uint8_t t; - long i, j; - t = CycleSseType(m->xmmtype[0][0]); - for (i = 0; i < 2; ++i) { - for (j = 0; j < 8; ++j) { - m->xmmtype[i][j] = t; + unsigned i; + t = CycleXmmType(xmmtype[0]); + for (i = 0; i < 16; ++i) { + xmmtype[i] = t; + } +} + +static void SetXmmSize(int bytes) { + unsigned i; + for (i = 0; i < 16; ++i) { + xmmsize[i] = bytes; + } +} + +static void SetXmmDisp(int disp) { + xmmdisp = disp; +} + +static void OnXmmSize(void) { + SetXmmSize(CycleXmmSize(xmmsize[0])); +} + +static void OnXmmDisp(void) { + SetXmmDisp(CycleXmmDisp(xmmdisp)); +} + +static bool HasPendingKeyboard(void) { + return HasPendingInput(ttyin); +} + +static void Sleep(int ms) { + poll((struct pollfd[]){{ttyin, POLLIN}}, 1, ms); +} + +static void OnMouse(char *p) { + enum Mouse e; + int i, x, y, dy; + struct Panel *ep; + e = strtol(p, &p, 10); + if (*p == ';') ++p; + x = min(txn, max(1, strtol(p, &p, 10))) - 1; + if (*p == ';') ++p; + y = min(tyn, max(1, strtol(p, &p, 10))) - 1; + e |= (*p == 'm') << 2; + for (ep = 0, i = 0; i < ARRAYLEN(pan.p); ++i) { + if ((pan.p[i].left <= x && x < pan.p[i].right) && + (pan.p[i].top <= y && y < pan.p[i].bottom)) { + ep = &pan.p[i]; + break; + } + } + if (ep) { + dy = 3; + switch (e) { + case kMouseWheelUp: + if (ep == &pan.disassembly) opstart -= dy; + if (ep == &pan.code) codestart -= dy; + if (ep == &pan.readdata) readstart -= dy; + if (ep == &pan.writedata) writestart -= dy; + if (ep == &pan.stack) stackstart -= dy; + if (ep == &pan.maps) mapsstart = MAX(0, mapsstart - 1); + if (ep == &pan.frames) framesstart = MAX(0, framesstart - 1); + if (ep == &pan.breakpoints) { + breakpointsstart = MAX(0, breakpointsstart - 1); + } + break; + case kMouseWheelDown: + if (ep == &pan.disassembly) opstart += dy; + if (ep == &pan.code) codestart += dy; + if (ep == &pan.readdata) readstart += dy; + if (ep == &pan.writedata) writestart += dy; + if (ep == &pan.stack) stackstart += dy; + if (ep == &pan.maps) mapsstart += 1; + if (ep == &pan.frames) framesstart += 1; + if (ep == &pan.breakpoints) breakpointsstart += 1; + break; + default: + break; } } } -static void OnSseWidth(void) { - ssewidth = CycleSseWidth(ssewidth); -} - -static void OnSseHex(void) { - ssehex = !ssehex; -} - -static bool HasPendingKeyboard(void) { - return HasPendingInput(ttyfd); -} - -static bool IsExecuting(void) { - return (action & (CONTINUE | STEP | NEXT | FINISH)) && !(action & FAILURE); -} - static void ReadKeyboard(void) { - int c; - char b[20]; - ssize_t rc; - size_t i, n; - if ((rc = read(ttyfd, b, 16)) != -1) { - for (n = rc, i = 0; i < n; ++i) { - switch (b[i++]) { - CASE('q', OnQ()); - CASE('v', OnV()); - CASE('s', OnStep()); - CASE('n', OnNext()); - CASE('f', OnFinish()); - CASE('x', OnSseHex()); - CASE('c', OnContinue()); - CASE('R', OnRestart()); - CASE('t', OnSseType()); - CASE('w', OnSseWidth()); - CASE('u', OnUp()); - CASE('d', OnDown()); - CASE('B', PopBreakpoint(&breakpoints)); - CASE('\t', OnTab()); - CASE('\r', OnEnter()); - CASE('\n', OnEnter()); - CASE(CTRL('C'), OnCtrlC()); - CASE(CTRL('D'), OnCtrlC()); - CASE(CTRL('\\'), OnQuit()); - CASE(CTRL('Z'), raise(SIGSTOP)); - CASE(CTRL('L'), OnFeed()); - CASE(CTRL('P'), OnUpArrow()); - CASE(CTRL('N'), OnDownArrow()); - CASE(CTRL('V'), OnPageDown()); - case '\e': - switch (b[i++]) { - CASE('v', OnPageUp()); - case '[': - switch (b[i++]) { - CASE('A', OnUpArrow()); - CASE('B', OnDownArrow()); - CASE('F', OnEnd()); - CASE('H', OnHome()); - case '1': - switch (b[i++]) { - CASE('~', OnHome()); - default: - break; - } + char buf[64], *p = buf; + if (readansi(ttyin, buf, sizeof(buf)) == -1) { + if (errno == EINTR) return; + FATALF("readkeyboard failed: %s", strerror(errno)); + } + switch (*p++) { + CASE('q', OnQ()); + CASE('v', OnV()); + CASE('s', OnStep()); + CASE('n', OnNext()); + CASE('f', OnFinish()); + CASE('c', OnContinue()); + CASE('R', OnRestart()); + CASE('x', OnXmmDisp()); + CASE('t', OnXmmType()); + CASE('T', OnXmmSize()); + CASE('u', OnUp()); + CASE('d', OnDown()); + CASE('B', PopBreakpoint(&breakpoints)); + CASE('M', ToggleMouseTracking()); + CASE('\t', OnTab()); + CASE('\r', OnEnter()); + CASE('\n', OnEnter()); + CASE(CTRL('C'), OnSigInt()); + CASE(CTRL('D'), OnSigInt()); + CASE(CTRL('\\'), OnQuit()); + CASE(CTRL('Z'), raise(SIGSTOP)); + CASE(CTRL('L'), OnFeed()); + CASE(CTRL('P'), OnUpArrow()); + CASE(CTRL('N'), OnDownArrow()); + CASE(CTRL('V'), OnPageDown()); + CASE(CTRL('T'), OnTurbo()); + case '\e': + switch (*p++) { + CASE('v', OnPageUp()); + CASE('t', OnSlowmo()); + case '[': + switch (*p++) { + CASE('<', OnMouse(p)); + CASE('A', OnUpArrow()); + CASE('B', OnDownArrow()); + CASE('F', OnEnd()); + CASE('H', OnHome()); + case '1': + switch (*p++) { + CASE('~', OnHome()); + default: break; - case '4': - switch (b[i++]) { - CASE('~', OnEnd()); - default: - break; - } + } + break; + case '4': + switch (*p++) { + CASE('~', OnEnd()); + default: break; - case '5': - switch (b[i++]) { - CASE('~', OnPageUp()); - default: - break; - } + } + break; + case '5': + switch (*p++) { + CASE('~', OnPageUp()); + default: break; - case '6': - switch (b[i++]) { - CASE('~', OnPageDown()); - default: - break; - } + } + break; + case '6': + switch (*p++) { + CASE('~', OnPageDown()); + default: break; - case '7': - switch (b[i++]) { - CASE('~', OnHome()); - default: - break; - } - break; - case '8': - switch (b[i++]) { - CASE('~', OnEnd()); - default: - break; - } + } + break; + case '7': + switch (*p++) { + CASE('~', OnHome()); + default: break; + } + break; + case '8': + switch (*p++) { + CASE('~', OnEnd()); default: break; } @@ -1787,14 +2341,9 @@ static void ReadKeyboard(void) { default: break; } - } - } else if (errno == EINTR) { - LOGF("ReadKeyboard Interrupted"); - } else if (errno == EAGAIN) { - poll((struct pollfd[]){{ttyfd, POLLIN}}, 1, -1); - } else { - perror("ReadKeyboard"); - exit(1); + break; + default: + break; } } @@ -1812,7 +2361,8 @@ static int64_t ParseHexValue(const char *s) { } static void HandleBreakpointFlag(const char *s) { - struct Breakpoint b = {0}; + struct Breakpoint b; + memset(&b, 0, sizeof(b)); if (isdigit(*s)) { b.addr = ParseHexValue(s); } else { @@ -1826,11 +2376,6 @@ static noreturn void PrintUsage(int rc, FILE *f) { exit(rc); } -static void LeaveScreen(void) { - char buf[64]; - write(ttyfd, buf, sprintf(buf, "\e[%d;%dH\e[S\r\n", tyn - 1, txn - 1)); -} - static void Exec(void) { long op; ssize_t bp; @@ -1895,6 +2440,7 @@ static void Tui(void) { for (;;) { if (!(action & FAILURE)) { LoadInstruction(m); + UpdateXmmType(); } else { m->xedd = (struct XedDecodedInst *)m->icache[0]; m->xedd->length = 1; @@ -1902,13 +2448,21 @@ static void Tui(void) { m->xedd->op.opcode = 0xCC; } if (action & WINCHED) { - LOGF("WINCHED"); - GetTtySize(); + GetTtySize(ttyout); action &= ~WINCHED; } interactive = ++tick > speed; if (interactive && speed < 0) { - dsleep(.001L * -speed); + Sleep(-speed); + } + if (action & ALARM) { + HandleAlarm(); + } + if (action & FAILURE) { + ScrollCode(&pan.code); + ScrollStack(&pan.stack); + ScrollReadData(&pan.readdata); + ScrollWriteData(&pan.writedata); } if (!(action & CONTINUE) || interactive) { tick = 0; @@ -1918,10 +2472,10 @@ static void Tui(void) { } if (action & FAILURE) { LOGF("TUI FAILURE"); - PrintMessageBox(ttyfd, systemfailure, tyn, txn); + PrintMessageBox(ttyout, systemfailure, tyn, txn); ReadKeyboard(); - } else if (!IsExecuting() || ((interactive || !(action & CONTINUE)) && - !(action & INT) && HasPendingKeyboard())) { + } else if (!IsExecuting() || (!(action & CONTINUE) && !(action & INT) && + HasPendingKeyboard())) { ReadKeyboard(); } if (action & INT) { @@ -1954,7 +2508,7 @@ static void Tui(void) { op = GetDisIndex(); ScrollOp(&pan.disassembly, op); VERBOSEF("%s", DisGetLine(dis, m, op)); - memcpy(&m[1], &m[0], sizeof(m[0])); + CopyMachineState(&laststate); if (!(action & CONTINUE)) { action &= ~STEP; if (action & NEXT) { @@ -1975,6 +2529,12 @@ static void Tui(void) { } if (!IsDebugBreak()) { ExecuteInstruction(m); + if (!(action & CONTINUE) || interactive) { + ScrollCode(&pan.code); + ScrollStack(&pan.stack); + ScrollReadData(&pan.readdata); + ScrollWriteData(&pan.writedata); + } if (IsLongBranch()) { Disassemble(); } @@ -1995,7 +2555,7 @@ static void Tui(void) { } } if ((action & (FINISH | NEXT | CONTINUE)) && - (bp = IsAtBreakpoint(&breakpoints, m->ip)) != -1) { + (bp = IsAtBreakpoint(&breakpoints, GetIp())) != -1) { action &= ~(FINISH | NEXT | CONTINUE); LOGF("BREAK %p", breakpoints.p[bp].addr); break; @@ -2056,19 +2616,43 @@ int Emulator(int argc, char *argv[]) { int rc, fd; codepath = argv[optind++]; pty = MachinePtyNew(); - InitMachine(m); - m->cr3 = MallocPage(); m->fds.p = xcalloc((m->fds.n = 8), sizeof(struct MachineFd)); Restart: action = 0; LoadProgram(m, codepath, argv + optind, environ, elf); m->fds.i = 3; m->fds.p[0].fd = STDIN_FILENO; - m->fds.p[0].cb = &kMachineFdCbPty; + m->fds.p[0].cb = &kMachineFdCbHost; m->fds.p[1].fd = STDOUT_FILENO; - m->fds.p[1].cb = &kMachineFdCbPty; + m->fds.p[1].cb = &kMachineFdCbHost; m->fds.p[2].fd = STDERR_FILENO; - m->fds.p[2].cb = &kMachineFdCbPty; + m->fds.p[2].cb = &kMachineFdCbHost; + if (tuimode) { + ttyin = isatty(0) ? 0 : open("/dev/tty", O_RDWR | O_NOCTTY); + ttyout = isatty(1) ? 1 : open("/dev/tty", O_RDWR | O_NOCTTY); + } else { + ttyin = -1; + ttyout = -1; + } + if (ttyout != -1) { + atexit(TtyRestore1); + xsigaction(SIGWINCH, OnSigWinch, 0, 0, 0); + tyn = 24; + txn = 80; + GetTtySize(ttyout); + if (isatty(0)) { + m->fds.p[0].fd = 0; + m->fds.p[0].cb = &kMachineFdCbPty; + } + if (isatty(1)) { + m->fds.p[1].fd = 1; + m->fds.p[1].cb = &kMachineFdCbPty; + } + if (isatty(2)) { + m->fds.p[2].fd = 2; + m->fds.p[2].cb = &kMachineFdCbPty; + } + } while (!(action & EXIT)) { if (!tuimode) { Exec(); @@ -2093,15 +2677,15 @@ Restart: } static void OnlyRunOnFirstCpu(void) { - uint64_t bs; - bs = 1; + uint64_t bs = 1; sched_setaffinity(0, sizeof(bs), &bs); } int main(int argc, char *argv[]) { - int rc; - m->mode = XED_MACHINE_MODE_LONG_64; - ssewidth = 2; /* 16-bit is best bit */ + m = NewMachine(); + speed = 16; + SetXmmSize(2); + SetXmmDisp(kXmmHex); if (!NoDebug()) showcrashreports(); /* OnlyRunOnFirstCpu(); */ if ((colorize = cancolor())) { @@ -2114,6 +2698,5 @@ int main(int argc, char *argv[]) { } GetOpts(argc, argv); if (optind == argc) PrintUsage(EX_USAGE, stderr); - rc = Emulator(argc, argv); - return rc; + return Emulator(argc, argv); } diff --git a/tool/build/emubin/emubin.mk b/tool/build/emubin/emubin.mk index 918aeb7a..38f5ddc8 100644 --- a/tool/build/emubin/emubin.mk +++ b/tool/build/emubin/emubin.mk @@ -11,7 +11,8 @@ TOOL_BUILD_EMUBIN_BINS = \ o/$(MODE)/tool/build/emubin/prime.bin \ o/$(MODE)/tool/build/emubin/prime.bin.dbg \ o/$(MODE)/tool/build/emubin/pi.bin \ - o/$(MODE)/tool/build/emubin/pi.bin.dbg + o/$(MODE)/tool/build/emubin/pi.bin.dbg \ + o/$(MODE)/tool/build/emubin/linmap.elf TOOL_BUILD_EMUBIN_A = o/$(MODE)/tool/build/emubin/emubin.a TOOL_BUILD_EMUBIN_FILES := $(wildcard tool/build/emubin/*) @@ -50,6 +51,13 @@ o/$(MODE)/tool/build/emubin/%.bin.dbg: \ $(TOOL_BUILD_EMUBIN_A).pkg @$(ELFLINK) -e emucrt -z max-page-size=0x10 +o/$(MODE)/tool/build/emubin/%.elf: \ + $(TOOL_BUILD_EMUBIN_DEPS) \ + $(TOOL_BUILD_EMUBIN_A) \ + o/$(MODE)/tool/build/emubin/%.o \ + $(ELF) + @$(ELFLINK) + o/dbg/tool/build/emubin/lisp.real.com.dbg: \ $(TOOL_BUILD_EMUBIN_DEPS) \ $(TOOL_BUILD_EMUBIN_A) \ diff --git a/tool/build/emubin/linmap.c b/tool/build/emubin/linmap.c new file mode 100644 index 00000000..654174cd --- /dev/null +++ b/tool/build/emubin/linmap.c @@ -0,0 +1,32 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/linux/exit.h" +#include "libc/linux/fstat.h" +#include "libc/linux/mmap.h" +#include "libc/linux/open.h" + +struct stat st; + +void _start(void) { + long fd = LinuxOpen("/etc/passwd", 0, 0); + LinuxFstat(fd, &st); + LinuxMmap((void *)0x000000000000, st.st_size, 1, 2, fd, 0); + LinuxExit(0); +} diff --git a/tool/build/emubin/lisp.c b/tool/build/emubin/lisp.c index 4814f369..b390b88b 100644 --- a/tool/build/emubin/lisp.c +++ b/tool/build/emubin/lisp.c @@ -16,237 +16,20 @@ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │ │ PERFORMANCE OF THIS SOFTWARE. │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "tool/build/emubin/lisp.h" -#define TRACE 0 -#define ERRORS 1 -#define LONG long -#define WORD short -#define WORDS 8192 +#define TRACE 0 // print eval input output +#define RETRO 1 // auto capitalize input +#define ERRORS 1 // print messages or undefined behavior +#define DELETE 1 // allow backspace to rub out symbol +#define QUOTES 1 // allow 'X shorthand (QUOTE X) +#define MUTABLE 0 // allow setting globals +#define PROMPT 1 // show repl prompt +#define WORD short +#define WORDS 8192 /*───────────────────────────────────────────────────────────────────────────│─╗ -│ The LISP Challenge § Impure x86_64 Linux 8086 PC BIOS System Integration ─╬─│┼ -╚────────────────────────────────────────────────────────────────────────────│*/ - -#define TYPE(x) /* a.k.a. x&1 */ \ - ({ \ - char IsAtom; \ - asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \ - IsAtom; \ - }) - -#define OBJECT(t, v) /* a.k.a. v<<1|t */ \ - ({ \ - __typeof(v) Val = (v); \ - asm("shl\t%0" : "+r"(Val)); \ - Val | (t); \ - }) - -#define SUB(x, y) /* a.k.a. x-y */ \ - ({ \ - __typeof(x) Reg = (x); \ - asm("sub\t%1,%0" : "+rm"(Reg) : "g"(y)); \ - Reg; \ - }) - -#define STOS(di, c) asm("stos%z1" : "+D"(di), "=m"(*(di)) : "a"(c)) -#define LODS(si) \ - ({ \ - typeof(*(si)) c; \ - asm("lods%z2" : "+S"(si), "=a"(c) : "m"(*(si))); \ - c; \ - }) - -#define REAL_READ_(REG, BASE, INDEX, DISP) \ - ({ \ - __typeof(*(BASE)) Reg; \ - if (__builtin_constant_p(INDEX) && !(INDEX)) { \ - asm("mov\t%c2(%1),%0" \ - : REG(Reg) \ - : "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \ - "m"(BASE[(INDEX) + (DISP)])); \ - } else { \ - asm("mov\t%c3(%1,%2),%0" \ - : REG(Reg) \ - : "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \ - "i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \ - } \ - Reg; \ - }) - -/* #ifdef __REAL_MODE__ */ -#define REAL_READ(BASE, INDEX, DISP) /* a.k.a. b[i] */ \ - (sizeof(*(BASE)) == 1 ? REAL_READ_("=Q", BASE, INDEX, DISP) \ - : REAL_READ_("=r", BASE, INDEX, DISP)) -/* #else */ -/* #define REAL_READ(BASE, INDEX, DISP) BASE[INDEX + DISP] */ -/* #endif */ - -#define REAL_READ_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP) \ - ({ \ - __typeof(*(OBJECT->MEMBER)) Reg; \ - if (!(OBJECT)) { \ - asm("mov\t%c2(%1),%0" \ - : REG(Reg) \ - : "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP)), \ - "m"(OBJECT->MEMBER)); \ - } else { \ - asm("mov\t%c3(%1,%2),%0" \ - : REG(Reg) \ - : "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP)), \ - "m"(OBJECT->MEMBER)); \ - } \ - Reg; \ - }) - -/* #ifdef __REAL_MODE__ */ -#define REAL_READ_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \ - (sizeof(*(OBJECT->MEMBER)) == 1 \ - ? REAL_READ_ARRAY_FIELD_("=Q", OBJECT, MEMBER, INDEX, DISP) \ - : REAL_READ_ARRAY_FIELD_("=r", OBJECT, MEMBER, INDEX, DISP)) -/* #else */ -/* #define REAL_READ_ARRAY_FIELD(o, m, i, d) o->m[i + d] */ -/* #endif */ - -#define REAL_WRITE_ARRAY_FIELD_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \ - do { \ - if (!(OBJECT)) { \ - asm("mov\t%1,%c3(%2)" \ - : "=m"(OBJECT->MEMBER) \ - : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \ - "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP))); \ - } else { \ - asm("mov\t%1,%c4(%2,%3)" \ - : "=m"(OBJECT->MEMBER) \ - : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \ - "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ - "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ - sizeof(*(OBJECT->MEMBER)) * (DISP))); \ - } \ - } while (0) - -/* #ifdef __REAL_MODE__ */ -#define REAL_WRITE_ARRAY_FIELD(OBJECT, MEMBER, INDEX, DISP, VALUE) \ - do { \ - __typeof(*(OBJECT->MEMBER)) Reg; \ - switch (sizeof(*(OBJECT->MEMBER))) { \ - case 1: \ - REAL_WRITE_ARRAY_FIELD_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \ - break; \ - default: \ - REAL_WRITE_ARRAY_FIELD_("ri", OBJECT, MEMBER, INDEX, DISP, VALUE); \ - break; \ - } \ - } while (0) -/* #else */ -/* #define REAL_WRITE_ARRAY_FIELD(o, m, i, d, v) o->m[i + d] = v */ -/* #endif */ - -long jb[8]; -int setjmp(void *) __attribute__((__returns_twice__)); -int longjmp(void *, int) __attribute__((__noreturn__)); - -static inline void *SetMemory(void *di, int al, unsigned long cx) { - asm("rep stosb" - : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) - : "0"(di), "1"(cx), "a"(al)); - return di; -} - -static inline void *CopyMemory(void *di, void *si, unsigned long cx) { - asm("rep movsb" - : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) - : "0"(di), "1"(si), "2"(cx)); - return di; -} - -static void RawMode(void) { -#ifndef __REAL_MODE__ - int rc; - int c[14]; - asm volatile("syscall" - : "=a"(rc) - : "0"(0x10), "D"(0), "S"(0x5401), "d"(c) - : "rcx", "r11", "memory"); - c[0] &= ~0b0000010111111000; // INPCK|ISTRIP|PARMRK|INLCR|IGNCR|ICRNL|IXON - c[2] &= ~0b0000000100110000; // CSIZE|PARENB - c[2] |= 0b00000000000110000; // CS8 - c[3] &= ~0b1000000001011010; // ECHONL|ECHO|ECHOE|IEXTEN|ICANON - asm volatile("syscall" - : "=a"(rc) - : "0"(0x10), "D"(0), "S"(0x5402), "d"(c) - : "rcx", "r11", "memory"); -#endif -} - -__attribute__((__noinline__)) static void PrintChar(LONG c) { -#ifdef __REAL_MODE__ - asm volatile("mov\t$0x0E,%%ah\n\t" - "int\t$0x10" - : /* no outputs */ - : "a"(c), "b"(7) - : "memory"); -#else - static short buf; - int rc; - buf = c; - asm volatile("syscall" - : "=a"(rc) - : "0"(1), "D"(1), "S"(&buf), "d"(1) - : "rcx", "r11", "memory"); -#endif -} - -static void PrintString(char *s) { - char c; - for (;;) { - if (!(c = REAL_READ(s, 0, 0))) break; - PrintChar(c); - ++s; - } -} - -static int XlatChar(LONG c) { - if (c == 0x7F) return '\b'; - if (c >= 'a') { - asm volatile("" ::: "memory"); - if (c <= 'z') c -= 'a' - 'A'; - } - return c; -} - -static int EchoChar(LONG c) { - if (c != '\b') { - PrintChar(c); - if (c == '\r') { - PrintChar('\n'); - } - } - return c; -} - -__attribute__((__noinline__)) static noinline int ReadChar(void) { - int c; -#ifdef __REAL_MODE__ - asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory"); -#else - static int buf; - asm volatile("syscall" - : "=a"(c) - : "0"(0), "D"(0), "S"(&buf), "d"(1) - : "rcx", "r11", "memory"); - c = buf; -#endif - return EchoChar(XlatChar(c)); -} - -/*───────────────────────────────────────────────────────────────────────────│─╗ -│ The LISP Challenge § Pure Original LISP Machine ─╬─│┼ +│ The LISP Challenge § LISP Machine ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ #define ATOM 0 @@ -261,10 +44,8 @@ __attribute__((__noinline__)) static noinline int ReadChar(void) { #define ATOM_CAR 50 #define ATOM_CDR 58 #define ATOM_CONS 66 -#define ATOM_LABEL 76 -#define ATOM_LAMBDA 88 -#define ATOM_SET 102 -#define ATOM_DEFUN 110 +#define ATOM_LAMBDA 76 +#define ATOM_SET 90 #define Quote(x) List(ATOM_QUOTE, x) #define List(x, y) Cons(x, Cons(y, NIL)) @@ -279,9 +60,6 @@ __attribute__((__noinline__)) static noinline int ReadChar(void) { #define VALUE(x) ((x) >> 1) #define PTR(i) ((i) << 1 | CONS) -#define ARRAYLEN(A) \ - ((sizeof(A) / sizeof(*(A))) / ((unsigned)!(sizeof(A) % sizeof(*(A))))) - struct Lisp { WORD mem[WORDS]; unsigned char syntax[256]; @@ -289,25 +67,24 @@ struct Lisp { WORD globals; WORD index; char token[128]; + long jb[8]; char str[WORDS]; }; _Static_assert(sizeof(struct Lisp) <= 0x7c00 - 0x600, "LISP Machine too large for real mode"); -_Alignas(char) const char kSymbols[] = "\ -NIL\0T\0QUOTE\0ATOM\0EQ\0COND\0CAR\0CDR\0CONS\0LABEL\0LAMBDA\0SET\0DEFUN\0"; - -_Alignas(WORD) const WORD kGlobals[] = { - [0] = PTR(2), // ((T . T) (NIL . NIL)) - [1] = PTR(4), // - [2] = ATOM_T, // (T . T) - [3] = ATOM_T, // - [4] = PTR(6), // ((NIL . NIL)) - [5] = NIL, // - [6] = NIL, // (NIL . NIL) - [7] = NIL, // -}; +_Alignas(char) const char kSymbols[] = "NIL\0" + "T\0" + "QUOTE\0" + "ATOM\0" + "EQ\0" + "COND\0" + "CAR\0" + "CDR\0" + "CONS\0" + "LAMBDA\0" + "SET\0"; #ifdef __REAL_MODE__ static struct Lisp *const q; @@ -315,56 +92,55 @@ static struct Lisp *const q; static struct Lisp q[1]; #endif -static void Print(LONG); +static void Print(long); static WORD GetList(void); static WORD GetObject(void); -static void PrintObject(LONG); -static WORD Eval(LONG, LONG); +static void PrintObject(long); +static WORD Eval(long, long); static void SetupSyntax(void) { - q->syntax[' '] = ' '; - q->syntax['\t'] = ' '; - q->syntax['\r'] = ' '; - q->syntax['\n'] = ' '; - q->syntax['('] = '('; - q->syntax[')'] = ')'; - q->syntax['.'] = '.'; - q->syntax['\''] = '\''; + unsigned char *syntax = q->syntax; + asm("" : "+bSD"(syntax)); + syntax[' '] = ' '; + syntax['\r'] = ' '; + syntax['\n'] = ' '; + syntax['('] = '('; + syntax[')'] = ')'; + syntax['.'] = '.'; +#if QUOTES + syntax['\''] = '\''; +#endif } -static inline WORD Car(LONG x) { - return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 0); +static inline WORD Car(long x) { + return PEEK_ARRAY(q, mem, VALUE(x), 0); } -static inline WORD Cdr(LONG x) { - return REAL_READ_ARRAY_FIELD(q, mem, VALUE(x), 1); +static inline WORD Cdr(long x) { + return PEEK_ARRAY(q, mem, VALUE(x), 1); +} + +static WORD Set(long i, long k, long v) { + POKE_ARRAY(q, mem, VALUE(i), 0, k); + POKE_ARRAY(q, mem, VALUE(i), 1, v); + return i; } static WORD Cons(WORD car, WORD cdr) { -#if TRACE - PrintString("CONS->"); - Print(car); - PrintString(" "); - Print(cdr); -#endif int i, cell; i = q->index; - REAL_WRITE_ARRAY_FIELD(q, mem, i, 0, car); - REAL_WRITE_ARRAY_FIELD(q, mem, i, 1, cdr); + POKE_ARRAY(q, mem, i, 0, car); + POKE_ARRAY(q, mem, i, 1, cdr); q->index = i + 2; cell = OBJECT(CONS, i); -#if TRACE - PrintString("CONS<-"); - Print(cell); -#endif return cell; } static void SetupBuiltins(void) { CopyMemory(q->str, kSymbols, sizeof(kSymbols)); - CopyMemory(q->mem, kGlobals, sizeof(kGlobals)); - q->index = ARRAYLEN(kGlobals); + q->mem[0] = PTR(2); q->globals = PTR(0); + q->index = 4; } static char *StpCpy(char *d, char *s) { @@ -383,7 +159,7 @@ WORD Intern(char *s) { c = LODS(z); while (c) { for (j = 0;; ++j) { - if (c != REAL_READ(s, j, 0)) { + if (c != PEEK(s, j, 0)) { break; } if (!c) { @@ -400,7 +176,33 @@ WORD Intern(char *s) { } forceinline unsigned char XlatSyntax(unsigned char b) { - return REAL_READ_ARRAY_FIELD(q, syntax, b, 0); + return PEEK_ARRAY(q, syntax, b, 0); +} + +static void PrintString(char *s) { + char c; + for (;;) { + if (!(c = PEEK(s, 0, 0))) break; + PrintChar(c); + ++s; + } +} + +static int GetChar(void) { + int c; + c = ReadChar(); +#if RETRO + if (c >= 'a') { + CompilerBarrier(); + if (c <= 'z') c -= 'a' - 'A'; + } +#endif +#if DELETE + if (c == '\b') return c; +#endif + PrintChar(c); + if (c == '\r') PrintChar('\n'); + return c; } static void GetToken(void) { @@ -409,20 +211,20 @@ static void GetToken(void) { b = q->look; t = q->token; while (XlatSyntax(b) == ' ') { - b = ReadChar(); + b = GetChar(); } if (XlatSyntax(b)) { STOS(t, b); - b = ReadChar(); + b = GetChar(); } else { while (b && !XlatSyntax(b)) { - if (b != '\b') { + if (!DELETE || b != '\b') { STOS(t, b); } else if (t > q->token) { PrintString("\b \b"); if (t > q->token) --t; } - b = ReadChar(); + b = GetChar(); } } STOS(t, 0); @@ -447,12 +249,14 @@ static WORD GetList(void) { switch (*q->token & 0xFF) { default: return AddList(GetObject()); - case '\'': - return AddList(GetQuote()); case ')': return NIL; case '.': return ConsumeObject(); +#if QUOTES + case '\'': + return AddList(GetQuote()); +#endif } } @@ -460,15 +264,17 @@ static WORD GetObject(void) { switch (*q->token & 0xFF) { default: return Intern(q->token); - case '\'': - return GetQuote(); case '(': return GetList(); +#if QUOTES + case '\'': + return GetQuote(); +#endif } } static WORD ReadObject(void) { - q->look = ReadChar(); + q->look = GetChar(); GetToken(); return GetObject(); } @@ -477,11 +283,18 @@ static WORD Read(void) { return ReadObject(); } -static void PrintAtom(LONG x) { +static void PrintAtom(long x) { PrintString(q->str + VALUE(x)); } -static void PrintList(LONG x) { +static void PrintList(long x) { +#if QUOTES + if (Car(x) == ATOM_QUOTE) { + PrintChar('\''); + PrintObject(Cadr(x)); + return; + } +#endif PrintChar('('); PrintObject(Car(x)); while ((x = Cdr(x))) { @@ -491,12 +304,13 @@ static void PrintList(LONG x) { } else { PrintString(" . "); PrintObject(x); + break; } } PrintChar(')'); } -static void PrintObject(LONG x) { +static void PrintObject(long x) { if (TYPE(x) == ATOM) { PrintAtom(x); } else { @@ -504,19 +318,13 @@ static void PrintObject(LONG x) { } } -static void Print(LONG i) { +static void Print(long i) { PrintObject(i); PrintString("\r\n"); } __attribute__((__noreturn__)) static void Reset(void) { - longjmp(jb, 1); -} - -__attribute__((__noreturn__)) static void OnUndefined(LONG x) { - PrintString("UNDEF! "); - Print(x); - Reset(); + longjmp(q->jb, 1); } __attribute__((__noreturn__)) static void OnArity(void) { @@ -524,145 +332,69 @@ __attribute__((__noreturn__)) static void OnArity(void) { Reset(); } +__attribute__((__noreturn__)) static void OnUndefined(long x) { + PrintString("UNDEF! "); + Print(x); + Reset(); +} + #if !ERRORS -#define OnUndefined(x) __builtin_unreachable() #define OnArity() __builtin_unreachable() +#define OnUndefined(x) __builtin_unreachable() #endif /*───────────────────────────────────────────────────────────────────────────│─╗ │ The LISP Challenge § Bootstrap John McCarthy's Metacircular Evaluator ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ -static WORD Atom(LONG x) { +static WORD Atom(long x) { return BOOL(TYPE(x) == ATOM); } -static WORD Null(LONG x) { +static WORD Null(long x) { return BOOL(!x); } -static WORD Eq(LONG x, LONG y) { - return BOOL(x == y); /* undefined if !Atom(x)||!Atom(y) */ +static WORD Eq(long x, long y) { + return BOOL(x == y); } -static WORD Assoc(LONG x, LONG y) { - if (Null(y)) OnUndefined(x); - if (Eq(Caar(y), x)) return Cdar(y); - return Assoc(x, Cdr(y)); +static WORD Arg1(long e, long a) { + return Eval(Cadr(e), a); } -static WORD Append(LONG x, LONG y) { -#if TRACE - PrintString("APPEND->"); - Print(x); - PrintString(" "); - Print(y); -#endif - if (!Null(x)) { - x = Cons(Car(x), Append(Cdr(x), y)); - } else { - x = y; - } -#if TRACE - PrintString("APPEND<-"); - Print(x); -#endif - return x; +static WORD Arg2(long e, long a) { + return Eval(Caddr(e), a); } -/** - * Gives list of pairs of corresponding elements of the lists x and y. - * E.g. pair[(A,B,C);(X,(Y,Z),U)] = ((A.X),(B.(Y,Z)),(C.U)) - * @note recoded to make lists in dot notation - * @note it's zip() basically - */ -static WORD Pair_(LONG x, LONG y) { +static WORD Append(long x, long y) { + return Null(x) ? y : Cons(Car(x), Append(Cdr(x), y)); +} + +static WORD Evcon(long c, long a) { + return Eval(Caar(c), a) ? Eval(Cadar(c), a) : Evcon(Cdr(c), a); +} + +static WORD Evlis(long m, long a) { + return m ? Cons(Eval(Car(m), a), Evlis(Cdr(m), a)) : NIL; +} + +static WORD Assoc(long x, long y) { + if (!y) OnUndefined(x); + return Eq(Caar(y), x) ? Cdar(y) : Assoc(x, Cdr(y)); +} + +static WORD Pair(long x, long y) { if (Null(x) && Null(y)) { return NIL; - } else if (TYPE(x) == CONS && TYPE(y) == CONS) { - return Cons(Cons(Car(x), Car(y)), Pair_(Cdr(x), Cdr(y))); + } else if (!Atom(x) && !Atom(y)) { + return Cons(Cons(Car(x), Car(y)), Pair(Cdr(x), Cdr(y))); } else { OnArity(); } } -static WORD Pair(LONG x, LONG y) { -#if TRACE - PrintString("PAIR->"); - Print(x); - PrintString(" "); - Print(y); -#endif - x = Pair_(x, y); -#if TRACE - PrintString("PAIR<-"); - Print(x); -#endif - return x; -} - -static WORD Appq(long m) { - if (m) { - return Cons(List(ATOM_QUOTE, Car(m)), Appq(Cdr(m))); - } else { - return NIL; - } -} - -static WORD Apply(long f, long a) { - return Eval(Cons(f, Appq(a)), NIL); -} - -static WORD Evcon(LONG c, LONG a) { - if (Eval(Caar(c), a)) { - return Eval(Cadar(c), a); - } else { - return Evcon(Cdr(c), a); - } -} - -static WORD Evlis_(LONG m, LONG a) { - if (m) { - return Cons(Eval(Car(m), a), Evlis_(Cdr(m), a)); - } else { - return NIL; - } -} - -static WORD Evlis(LONG m, LONG a) { -#if TRACE - PrintString("EVLIS->"); - Print(m); - PrintString(" "); - Print(a); -#endif - m = Evlis_(m, a); -#if TRACE - PrintString("EVLIS<-"); - Print(m); -#endif - return m; -} - -static WORD Set(LONG e) { - WORD name, value; - name = Car(e); - value = Cadr(e); - q->globals = Cons(Cons(name, value), q->globals); - return value; -} - -static WORD Defun(LONG e) { - WORD name, args, body, lamb; - name = Car(e); - args = Cadr(e); - body = Caddr(e); - lamb = Cons(ATOM_LAMBDA, List(args, body)); - q->globals = Cons(Cons(name, lamb), q->globals); - return name; -} - -static WORD Evaluate(LONG e, LONG a) { +static WORD Evaluate(long e, long a) { if (Atom(e)) { return Assoc(e, a); } else if (Atom(Car(e))) { @@ -670,26 +402,24 @@ static WORD Evaluate(LONG e, LONG a) { case ATOM_QUOTE: return Cadr(e); case ATOM_ATOM: - return Atom(Eval(Cadr(e), a)); + return Atom(Arg1(e, a)); case ATOM_EQ: - return Eq(Eval(Cadr(e), a), Eval(Caddr(e), a)); + return Eq(Arg1(e, a), Arg2(e, a)); case ATOM_COND: return Evcon(Cdr(e), a); case ATOM_CAR: - return Car(Eval(Cadr(e), a)); + return Car(Arg1(e, a)); case ATOM_CDR: - return Cdr(Eval(Cadr(e), a)); + return Cdr(Arg1(e, a)); case ATOM_CONS: - return Cons(Eval(Cadr(e), a), Eval(Caddr(e), a)); - case ATOM_DEFUN: - return Defun(Cdr(e)); + return Cons(Arg1(e, a), Arg2(e, a)); +#if MUTABLE case ATOM_SET: - return Set(Cdr(e)); + return Cdar(Set(a, Cons(Arg1(e, a), Arg2(e, a)), Cons(Car(a), Cdr(a)))); +#endif default: return Eval(Cons(Assoc(Car(e), a), Evlis(Cdr(e), a)), a); } - } else if (Eq(Caar(e), ATOM_LABEL)) { - return Eval(Cons(Caddar(e), Cdr(e)), Cons(Cons(Cadar(e), Car(e)), a)); } else if (Eq(Caar(e), ATOM_LAMBDA)) { return Eval(Caddar(e), Append(Pair(Cadar(e), Evlis(Cdr(e), a)), a)); } else { @@ -697,7 +427,8 @@ static WORD Evaluate(LONG e, LONG a) { } } -static WORD Eval(LONG e, LONG a) { +static WORD Eval(long e, long a) { + WORD r; #if TRACE PrintString("->"); Print(e); @@ -717,9 +448,13 @@ static WORD Eval(LONG e, LONG a) { ╚────────────────────────────────────────────────────────────────────────────│*/ void Repl(void) { - setjmp(jb); +#if ERRORS + setjmp(q->jb); +#endif for (;;) { +#if PROMPT PrintString("* "); +#endif Print(Eval(Read(), q->globals)); } } @@ -728,8 +463,10 @@ int main(int argc, char *argv[]) { /* RawMode(); */ SetupSyntax(); SetupBuiltins(); +#if PROMPT PrintString("THE LISP CHALLENGE V1\r\n" "VISIT GITHUB.COM/JART\r\n"); +#endif Repl(); return 0; } diff --git a/tool/build/emubin/lisp.h b/tool/build/emubin/lisp.h new file mode 100644 index 00000000..de2c13ae --- /dev/null +++ b/tool/build/emubin/lisp.h @@ -0,0 +1,182 @@ +/*───────────────────────────────────────────────────────────────────────────│─╗ +│ The LISP Challenge § Hardware Integration w/ x86_64 Linux & 8086 PC BIOS ─╬─│┼ +╚────────────────────────────────────────────────────────────────────────────│*/ + +#define CompilerBarrier() asm volatile("" ::: "memory"); + +#define TYPE(x) /* a.k.a. x&1 */ \ + ({ \ + char IsAtom; \ + asm("test%z1\t$1,%1" : "=@ccnz"(IsAtom) : "Qm"((char)x)); \ + IsAtom; \ + }) + +#define OBJECT(t, v) /* a.k.a. v<<1|t */ \ + ({ \ + __typeof(v) Val = (v); \ + asm("shl\t%0" : "+r"(Val)); \ + Val | (t); \ + }) + +#define SUB(x, y) /* a.k.a. x-y */ \ + ({ \ + __typeof(x) Reg = (x); \ + asm("sub\t%1,%0" : "+rm"(Reg) : "g"(y)); \ + Reg; \ + }) + +#define STOS(di, c) asm("stos%z1" : "+D"(di), "=m"(*(di)) : "a"(c)) +#define LODS(si) \ + ({ \ + typeof(*(si)) c; \ + asm("lods%z2" : "+S"(si), "=a"(c) : "m"(*(si))); \ + c; \ + }) + +#define PEEK_(REG, BASE, INDEX, DISP) \ + ({ \ + __typeof(*(BASE)) Reg; \ + if (__builtin_constant_p(INDEX) && !(INDEX)) { \ + asm("mov\t%c2(%1),%0" \ + : REG(Reg) \ + : "bDS"(BASE), "i"((DISP) * sizeof(*(BASE))), \ + "m"(BASE[(INDEX) + (DISP)])); \ + } else { \ + asm("mov\t%c3(%1,%2),%0" \ + : REG(Reg) \ + : "b"(BASE), "DS"((long)(INDEX) * sizeof(*(BASE))), \ + "i"((DISP) * sizeof(*(BASE))), "m"(BASE[(INDEX) + (DISP)])); \ + } \ + Reg; \ + }) + +#define PEEK(BASE, INDEX, DISP) /* a.k.a. b[i] */ \ + (sizeof(*(BASE)) == 1 ? PEEK_("=Q", BASE, INDEX, DISP) \ + : PEEK_("=r", BASE, INDEX, DISP)) + +#define PEEK_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP) \ + ({ \ + __typeof(*(OBJECT->MEMBER)) Reg; \ + if (!(OBJECT)) { \ + asm("mov\t%c2(%1),%0" \ + : REG(Reg) \ + : "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP)), \ + "m"(OBJECT->MEMBER)); \ + } else { \ + asm("mov\t%c3(%1,%2),%0" \ + : REG(Reg) \ + : "b"(OBJECT), "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP)), \ + "m"(OBJECT->MEMBER)); \ + } \ + Reg; \ + }) + +#define PEEK_ARRAY(OBJECT, MEMBER, INDEX, DISP) /* o->m[i] */ \ + (sizeof(*(OBJECT->MEMBER)) == 1 \ + ? PEEK_ARRAY_("=Q", OBJECT, MEMBER, INDEX, DISP) \ + : PEEK_ARRAY_("=r", OBJECT, MEMBER, INDEX, DISP)) + +#define POKE_ARRAY_(REG, OBJECT, MEMBER, INDEX, DISP, VALUE) \ + do { \ + if (!(OBJECT)) { \ + asm("mov\t%1,%c3(%2)" \ + : "=m"(OBJECT->MEMBER) \ + : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), \ + "bDS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP))); \ + } else { \ + asm("mov\t%1,%c4(%2,%3)" \ + : "=m"(OBJECT->MEMBER) \ + : REG((__typeof(*(OBJECT->MEMBER)))(VALUE)), "b"(OBJECT), \ + "DS"((long)(INDEX) * sizeof(*(OBJECT->MEMBER))), \ + "i"(__builtin_offsetof(__typeof(*(OBJECT)), MEMBER) + \ + sizeof(*(OBJECT->MEMBER)) * (DISP))); \ + } \ + } while (0) + +#define POKE_ARRAY(OBJECT, MEMBER, INDEX, DISP, VALUE) /* o->m[i]=v */ \ + do { \ + __typeof(*(OBJECT->MEMBER)) Reg; \ + switch (sizeof(*(OBJECT->MEMBER))) { \ + case 1: \ + POKE_ARRAY_("Q", OBJECT, MEMBER, INDEX, DISP, VALUE); \ + break; \ + default: \ + POKE_ARRAY_("r", OBJECT, MEMBER, INDEX, DISP, VALUE); \ + break; \ + } \ + } while (0) + +int setjmp(void *) __attribute__((__returns_twice__)); +int longjmp(void *, int) __attribute__((__noreturn__)); + +static inline void *SetMemory(void *di, int al, unsigned long cx) { + asm("rep stosb" + : "=D"(di), "=c"(cx), "=m"(*(char(*)[cx])di) + : "0"(di), "1"(cx), "a"(al)); + return di; +} + +static inline void *CopyMemory(void *di, void *si, unsigned long cx) { + asm("rep movsb" + : "=D"(di), "=S"(si), "=c"(cx), "=m"(*(char(*)[cx])di) + : "0"(di), "1"(si), "2"(cx)); + return di; +} + +static void RawMode(void) { +#ifndef __REAL_MODE__ + int rc; + int c[14]; + asm volatile("syscall" + : "=a"(rc) + : "0"(0x10), "D"(0), "S"(0x5401), "d"(c) + : "rcx", "r11", "memory"); + c[0] &= ~0b0000010111111000; // INPCK|ISTRIP|PARMRK|INLCR|IGNCR|ICRNL|IXON + c[2] &= ~0b0000000100110000; // CSIZE|PARENB + c[2] |= 0b00000000000110000; // CS8 + c[3] &= ~0b1000000001011010; // ECHONL|ECHO|ECHOE|IEXTEN|ICANON + asm volatile("syscall" + : "=a"(rc) + : "0"(0x10), "D"(0), "S"(0x5402), "d"(c) + : "rcx", "r11", "memory"); +#endif +} + +__attribute__((__noinline__)) static void PrintChar(long c) { +#ifdef __REAL_MODE__ + asm volatile("mov\t$0x0E,%%ah\n\t" + "int\t$0x10" + : /* no outputs */ + : "a"(c), "b"(7) + : "memory"); +#else + static short buf; + int rc; + buf = c; + asm volatile("syscall" + : "=a"(rc) + : "0"(1), "D"(1), "S"(&buf), "d"(1) + : "rcx", "r11", "memory"); +#endif +} + +static int ReadChar(void) { + int c; +#ifdef __REAL_MODE__ + asm volatile("int\t$0x16" : "=a"(c) : "0"(0) : "memory"); +#else + static int buf; + asm volatile("syscall" + : "=a"(c) + : "0"(0), "D"(0), "S"(&buf), "d"(1) + : "rcx", "r11", "memory"); + c = buf; +#endif + return c; +} diff --git a/tool/build/emubin/lisp.lds b/tool/build/emubin/lisp.lds index 55ab5881..25dc53bd 100644 --- a/tool/build/emubin/lisp.lds +++ b/tool/build/emubin/lisp.lds @@ -23,12 +23,13 @@ ENTRY(_start) SECTIONS { .text 0x7c00 - 0x600 : { - *(.start) + *(.start .start.*) rodata = .; *(.rodata .rodata.*) . = 0x1fe; SHORT(0xaa55); *(.text .text.*) + BYTE(0x90); _etext = .; . = ALIGN(512); } diff --git a/tool/build/emubin/lisp.lisp b/tool/build/emubin/lisp.lisp index dea2498c..b8bf7312 100644 --- a/tool/build/emubin/lisp.lisp +++ b/tool/build/emubin/lisp.lisp @@ -1,12 +1,30 @@ -(DEFUN FF (X) - (COND ((ATOM X) X) - ((QUOTE T) (FF (CAR X))))) -(FF '(A B C)) +;; (SET 'APPLY '(LAMBDA (E ARGS) +;; ((LAMBDA (APPQ) +;; (CONS )) +;; '(LAMBDA (M) +;; (COND ((EQ M 'NIL) 'NIL) +;; ('T (CONS (QUOTE (CAR M)) +;; (APPQ (CONS 'QUOTE (CDR M)))))))))) +;; (SET 'LIST '(LAMBDA (X Y) (CONS X (CONS Y 'NIL)))) +;; (SET 'AND '(LAMBDA (P Q) (COND ((EQ P 'T) Q) ('T 'F)))) +;; (SET 'OR '(LAMBDA (P Q) (COND ((EQ P 'T) 'T) ('T Q)))) +;; (SET 'NOT '(LAMBDA (P) (COND ((EQ P 'F) 'T) ('T 'T)))) +;; (SET 'IMPLIES '(LAMBDA (P Q) (COND ((EQ P 'T) Q) ('T 'T)))) -((LABEL FF - (LAMBDA (X) - (COND ((ATOM X) - X) - ((QUOTE T) - (FF (CAR X)))))) - (QUOTE ((A B) C))) +((LAMBDA (CALL MKQUOT NULL AND APPEND KEYS VALS E A) + (CALL (CONS (CONS (QUOTE LAMBDA) (CONS (KEYS (QUOTE A)) (CONS E NIL))) (VALS (QUOTE A))))) + (QUOTE (LAMBDA (X) (X))) + (QUOTE (LAMBDA (X) (CONS (QUOTE QUOTE) (CONS X NIL)))) + (QUOTE (LAMBDA (P Q) (COND ((EQ P (QUOTE T)) Q) ((QUOTE T) (QUOTE F))))) + (QUOTE (LAMBDA (X) (AND (QUOTE (ATOM X)) (QUOTE (EQ X NIL))))) + (QUOTE (LAMBDA (X Y) (COND ((EQ X NIL) Y) ((QUOTE T) (CONS (CAR X) (APPEND (QUOTE (CDR X)) (QUOTE Y))))))) + (QUOTE (LAMBDA (A) (COND ((EQ A NIL) NIL) ((QUOTE T) (CONS (CAR (CAR A)) (KEYS (QUOTE (CDR A)))))))) + (QUOTE (LAMBDA (A) (COND ((EQ A NIL) NIL) ((QUOTE T) (CONS (MKQUOT (QUOTE (CDR (CAR A)))) (VALS (QUOTE (CDR A)))))))) + (QUOTE (AND (QUOTE A) (QUOTE C))) + (CONS (CONS (QUOTE A) (QUOTE B)) (CONS (CONS (QUOTE C) (QUOTE D)) NIL))) + +((LAMBDA (FF X) (FF 'X)) + '(LAMBDA (X) + (COND ((ATOM X) X) + ((QUOTE T) (FF '(CAR X))))) + '((A) B C)) diff --git a/tool/build/emubin/lispstart.S b/tool/build/emubin/lispstart.S index 34e33fbf..f34d670f 100644 --- a/tool/build/emubin/lispstart.S +++ b/tool/build/emubin/lispstart.S @@ -17,11 +17,15 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +.code16 +.section .start,"ax",@progbits - .code16 - .section .start,"ax",@progbits _start: jmp 1f 1: ljmp $0x600>>4,$_begin + .type _start,@function + .size _start,.-_start + .globl _start + _begin: push %cs pop %ds push %cs @@ -42,26 +46,24 @@ _begin: push %cs mov $v_sectors+0x0200,%ax int $0x13 xor %bp,%bp - sub $6,%sp - call main - nop - .type _start,@function - .size _start,.-_start - .globl _start - .globl v_sectors - .globl main + jmp main + .type _begin,@function + .size _begin,.-_begin + .section .start.setjmp,"ax",@progbits setjmp: mov %sp,%ax - stosw # sp + stosw xchg %ax,%si - movsw %ss:(%si),(%di) # ip + movsw %ss:(%si),(%di) mov %bp,%ax - stosw # bp - ret + stosw + ret $6 .type setjmp,@function .size setjmp,.-setjmp .globl setjmp + .previous + .section .start.longjmp,"ax",@progbits longjmp: mov (%di),%sp mov 2(%di),%dx @@ -72,23 +74,4 @@ longjmp: .type longjmp,@function .size longjmp,.-longjmp .globl longjmp - - .globl q.syntax - .type q.syntax,@function - .globl q.look - .type q.look,@function - .globl q.globals - .type q.globals,@function - .globl q.index - .type q.index,@function - .globl q.token - .type q.token,@function - .globl q.str - .type q.str,@function - - .globl boot - .type boot,@function - .globl bss - .type bss,@function - .globl rodata - .type rodata,@function + .previous diff --git a/tool/build/emubin/spiral.real.c b/tool/build/emubin/spiral.c similarity index 100% rename from tool/build/emubin/spiral.real.c rename to tool/build/emubin/spiral.c diff --git a/tool/build/lib/argv.h b/tool/build/lib/argv.h new file mode 100644 index 00000000..11328fcc --- /dev/null +++ b/tool/build/lib/argv.h @@ -0,0 +1,11 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_ARGV_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_ARGV_H_ +#include "tool/build/lib/machine.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +void LoadArgv(struct Machine *, const char *, char **, char **); + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_ARGV_H_ */ diff --git a/tool/build/lib/breakpoint.c b/tool/build/lib/breakpoint.c index 434d0869..2c8b43c5 100644 --- a/tool/build/lib/breakpoint.c +++ b/tool/build/lib/breakpoint.c @@ -19,6 +19,7 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/arraylist2.h" #include "libc/assert.h" +#include "libc/log/log.h" #include "tool/build/lib/breakpoint.h" void PopBreakpoint(struct Breakpoints *bps) { diff --git a/tool/build/lib/buffer.c b/tool/build/lib/buffer.c index 4a894e8f..634a01cc 100644 --- a/tool/build/lib/buffer.c +++ b/tool/build/lib/buffer.c @@ -22,6 +22,8 @@ #include "libc/errno.h" #include "libc/fmt/fmt.h" #include "libc/mem/mem.h" +#include "libc/str/str.h" +#include "libc/str/tpenc.h" #include "libc/str/tpencode.h" #include "tool/build/lib/buffer.h" @@ -38,8 +40,13 @@ void AppendStr(struct Buffer *b, const char *s) { } void AppendWide(struct Buffer *b, wint_t wc) { - char cbuf[8]; - AppendData(b, cbuf, tpencode(cbuf, 8, wc, false)); + uint64_t wb; + wb = wc; + if (!isascii(wb)) wb = tpenc(wb); + do { + AppendChar(b, wb & 0xFF); + wb >>= 8; + } while (wb); } void AppendFmt(struct Buffer *b, const char *fmt, ...) { diff --git a/tool/build/lib/dis.c b/tool/build/lib/dis.c index 02a53213..bff902cc 100644 --- a/tool/build/lib/dis.c +++ b/tool/build/lib/dis.c @@ -178,9 +178,9 @@ static long DisAppendOpLines(struct Dis *d, struct Machine *m, int64_t addr) { void *r; int64_t ip; unsigned k; - uint8_t b[15]; struct DisOp op; long i, n, symbol; + uint8_t *p, b[15]; n = 15; ip = addr - Read64(m->cs); if ((symbol = DisFindSym(d, ip)) != -1) { @@ -202,8 +202,9 @@ static long DisAppendOpLines(struct Dis *d, struct Machine *m, int64_t addr) { if (!(r = FindReal(m, addr))) return -1; k = 0x1000 - (addr & 0xfff); if (n <= k) { - memcpy(b, r, n); + p = r; } else { + p = b; memcpy(b, r, k); if ((r = FindReal(m, addr + k))) { memcpy(b + k, r, n - k); @@ -212,7 +213,7 @@ static long DisAppendOpLines(struct Dis *d, struct Machine *m, int64_t addr) { } } xed_decoded_inst_zero_set_mode(d->xedd, m->mode); - xed_instruction_length_decode(d->xedd, b, n); + xed_instruction_length_decode(d->xedd, p, n); n = d->xedd->op.error ? 1 : d->xedd->length; op.addr = addr; op.size = n; @@ -240,7 +241,6 @@ long Dis(struct Dis *d, struct Machine *m, uint64_t addr, uint64_t ip, } const char *DisGetLine(struct Dis *d, struct Machine *m, size_t i) { - char *p; void *r[2]; uint8_t b[15]; if (i >= d->ops.i) return ""; @@ -250,10 +250,9 @@ const char *DisGetLine(struct Dis *d, struct Machine *m, size_t i) { xed_instruction_length_decode( d->xedd, AccessRam(m, d->ops.p[i].addr, d->ops.p[i].size, r, b, true), d->ops.p[i].size); - d->addr = d->ops.p[i].addr; d->m = m; - p = DisLineCode(d, d->buf); - CHECK_LT(p - d->buf, sizeof(d->buf)); + d->addr = d->ops.p[i].addr; + CHECK_LT(DisLineCode(d, d->buf) - d->buf, sizeof(d->buf)); return d->buf; } diff --git a/tool/build/lib/elfwriter.c b/tool/build/lib/elfwriter.c index 817e79e4..611747c1 100644 --- a/tool/build/lib/elfwriter.c +++ b/tool/build/lib/elfwriter.c @@ -17,10 +17,8 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/arraylist.h" #include "libc/alg/arraylist2.h" #include "libc/assert.h" -#include "libc/bits/safemacros.h" #include "libc/calls/calls.h" #include "libc/log/check.h" #include "libc/macros.h" @@ -44,7 +42,8 @@ static const Elf64_Ehdr kObjHeader = { .e_machine = EM_NEXGEN32E, .e_version = 1, .e_ehsize = sizeof(Elf64_Ehdr), - .e_shentsize = sizeof(Elf64_Shdr)}; + .e_shentsize = sizeof(Elf64_Shdr), +}; static size_t AppendSection(struct ElfWriter *elf, const char *name, int sh_type, int sh_flags) { @@ -193,7 +192,7 @@ void elfwriter_close(struct ElfWriter *elf) { void elfwriter_align(struct ElfWriter *elf, size_t addralign, size_t entsize) { elf->entsize = entsize; elf->addralign = addralign; - elf->wrote = roundup(elf->wrote, addralign); + elf->wrote = ROUNDUP(elf->wrote, addralign); } size_t elfwriter_startsection(struct ElfWriter *elf, const char *name, @@ -214,7 +213,7 @@ void *elfwriter_reserve(struct ElfWriter *elf, size_t size) { do { greed = greed + (greed >> 1); } while (need > greed); - greed = roundup(greed, FRAMESIZE); + greed = ROUNDUP(greed, FRAMESIZE); CHECK_NE(-1, ftruncate(elf->fd, greed)); CHECK_NE(MAP_FAILED, mmap((char *)elf->map + elf->mapsize, greed - elf->mapsize, PROT_READ | PROT_WRITE, diff --git a/tool/build/lib/elfwriter_yoink.c b/tool/build/lib/elfwriter_yoink.c index 9f1ee7f4..f68adc13 100644 --- a/tool/build/lib/elfwriter_yoink.c +++ b/tool/build/lib/elfwriter_yoink.c @@ -23,11 +23,11 @@ void elfwriter_yoink(struct ElfWriter *elf, const char *symbol) { unsigned char *p; struct ElfWriterSymRef sym; - const unsigned char nopl[8] = "\x0f\x1f\x04\x25\x00\x00\x00\x00"; + const unsigned char kNopl[8] = "\017\037\004\045\000\000\000\000"; p = elfwriter_reserve(elf, 8); - memcpy(p, nopl, sizeof(nopl)); + memcpy(p, kNopl, sizeof(kNopl)); sym = elfwriter_linksym(elf, symbol, ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), STV_HIDDEN); - elfwriter_appendrela(elf, sizeof(nopl) - 4, sym, R_X86_64_32, 0); - elfwriter_commit(elf, sizeof(nopl)); + elfwriter_appendrela(elf, sizeof(kNopl) - 4, sym, R_X86_64_32, 0); + elfwriter_commit(elf, sizeof(kNopl)); } diff --git a/tool/build/lib/endian.h b/tool/build/lib/endian.h index af12655c..da716f70 100644 --- a/tool/build/lib/endian.h +++ b/tool/build/lib/endian.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_TOOL_BUILD_LIB_ENDIAN_H_ #define COSMOPOLITAN_TOOL_BUILD_LIB_ENDIAN_H_ +#include "libc/dce.h" #include "libc/str/str.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) #if __BYTE_ORDER__ + 0 == 1234 diff --git a/tool/build/lib/fds.h b/tool/build/lib/fds.h index 860b3b8b..27d016e9 100644 --- a/tool/build/lib/fds.h +++ b/tool/build/lib/fds.h @@ -1,5 +1,6 @@ #ifndef COSMOPOLITAN_TOOL_BUILD_LIB_FDS_H_ #define COSMOPOLITAN_TOOL_BUILD_LIB_FDS_H_ +#include "libc/calls/struct/iovec.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -9,8 +10,8 @@ struct MachineFds { int fd; struct MachineFdCb { int (*close)(int); - ssize_t (*read)(int, void *, size_t); - ssize_t (*write)(int, const void *, size_t); + ssize_t (*readv)(int, const struct iovec *, int); + ssize_t (*writev)(int, const struct iovec *, int); int (*ioctl)(int, uint64_t, void *); } * cb; } * p; diff --git a/tool/build/lib/instruction.c b/tool/build/lib/instruction.c index d1076cd4..a3d283fa 100644 --- a/tool/build/lib/instruction.c +++ b/tool/build/lib/instruction.c @@ -75,12 +75,12 @@ void LoadInstruction(struct Machine *m) { key = ip & (ARRAYLEN(m->icache) - 1); m->xedd = (struct XedDecodedInst *)m->icache[key]; if ((ip & 0xfff) < 0x1000 - 15) { - if (ip - (ip & 0xfff) == m->codevirt && ip) { - addr = m->codereal + (ip & 0xfff); + if (ip - (ip & 0xfff) == m->codevirt && m->codehost) { + addr = m->codehost + (ip & 0xfff); } else { m->codevirt = ip - (ip & 0xfff); - m->codereal = ResolveAddress(m, m->codevirt); - addr = m->codereal + (ip & 0xfff); + m->codehost = ResolveAddress(m, m->codevirt); + addr = m->codehost + (ip & 0xfff); } if (!IsOpcodeEqual(m->xedd, addr)) { DecodeInstruction(m, addr, 15); diff --git a/tool/build/lib/ioports.c b/tool/build/lib/ioports.c index 21591368..02861ac6 100644 --- a/tool/build/lib/ioports.c +++ b/tool/build/lib/ioports.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/calls/struct/iovec.h" #include "libc/sysv/consts/fileno.h" #include "tool/build/lib/ioports.h" @@ -26,7 +27,7 @@ static int OpE9Read(struct Machine *m) { fd = STDIN_FILENO; if (fd >= m->fds.i) return -1; if (!m->fds.p[fd].cb) return -1; - if (m->fds.p[fd].cb->read(m->fds.p[fd].fd, &b, 1) == 1) { + if (m->fds.p[fd].cb->readv(m->fds.p[fd].fd, &(struct iovec){&b, 1}, 1) == 1) { return b; } else { return -1; @@ -38,7 +39,7 @@ static void OpE9Write(struct Machine *m, uint8_t b) { fd = STDOUT_FILENO; if (fd >= m->fds.i) return; if (!m->fds.p[fd].cb) return; - m->fds.p[fd].cb->write(m->fds.p[fd].fd, &b, 1); + m->fds.p[fd].cb->writev(m->fds.p[fd].fd, &(struct iovec){&b, 1}, 1); } uint64_t OpIn(struct Machine *m, uint16_t p) { diff --git a/tool/build/lib/iovs.c b/tool/build/lib/iovs.c new file mode 100644 index 00000000..50ec67a9 --- /dev/null +++ b/tool/build/lib/iovs.c @@ -0,0 +1,53 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/str/str.h" +#include "tool/build/lib/iovs.h" + +/** + * Appends memory region to i/o vector builder. + */ +int AppendIovs(struct Iovs *ib, void *base, size_t len) { + unsigned i, n; + struct iovec *p; + if (len) { + p = ib->p; + i = ib->i; + n = ib->n; + if (i && (intptr_t)base == (intptr_t)p[i - 1].iov_base + p[i - 1].iov_len) { + p[i - 1].iov_len += len; + } else { + if (unlikely(i == n)) { + n += n >> 1; + if (p == ib->init) { + if (!(p = malloc(sizeof(struct iovec) * n))) return -1; + memcpy(p, ib->init, sizeof(ib->init)); + } else { + if (!(p = realloc(p, sizeof(struct iovec) * n))) return -1; + } + ib->p = p; + ib->n = n; + } + p[i].iov_base = base; + p[i].iov_len = len; + ++ib->i; + } + } + return 0; +} diff --git a/tool/build/lib/iovs.h b/tool/build/lib/iovs.h new file mode 100644 index 00000000..09089a84 --- /dev/null +++ b/tool/build/lib/iovs.h @@ -0,0 +1,31 @@ +#ifndef COSMOPOLITAN_TOOL_BUILD_LIB_IOVS_H_ +#define COSMOPOLITAN_TOOL_BUILD_LIB_IOVS_H_ +#include "libc/calls/struct/iovec.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#if !(__ASSEMBLER__ + __LINKER__ + 0) +COSMOPOLITAN_C_START_ + +struct Iovs { + struct iovec *p; + unsigned i, n; + struct iovec init[2]; +}; + +int AppendIovs(struct Iovs *, void *, size_t); + +forceinline void InitIovs(struct Iovs *ib) { + ib->p = ib->init; + ib->i = 0; + ib->n = ARRAYLEN(ib->init); +} + +forceinline void FreeIovs(struct Iovs *ib) { + if (ib->p != ib->init) { + free(ib->p); + } +} + +COSMOPOLITAN_C_END_ +#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ +#endif /* COSMOPOLITAN_TOOL_BUILD_LIB_IOVS_H_ */ diff --git a/tool/build/lib/loader.c b/tool/build/lib/loader.c index 035ded92..48c60785 100644 --- a/tool/build/lib/loader.c +++ b/tool/build/lib/loader.c @@ -23,20 +23,21 @@ #include "libc/elf/elf.h" #include "libc/elf/struct/phdr.h" #include "libc/log/check.h" +#include "libc/log/log.h" #include "libc/macros.h" +#include "libc/nexgen32e/vendor.h" #include "libc/runtime/runtime.h" #include "libc/stdio/stdio.h" #include "libc/sysv/consts/fileno.h" #include "libc/sysv/consts/map.h" #include "libc/sysv/consts/o.h" #include "libc/sysv/consts/prot.h" +#include "tool/build/lib/argv.h" #include "tool/build/lib/endian.h" #include "tool/build/lib/loader.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" -#define DSOLOL "ERROR: ELF not ET_EXEC try `gcc -static -o foo foo.c`\n" - static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, Elf64_Phdr *phdr) { void *rbss; @@ -45,13 +46,20 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, align = MAX(phdr->p_align, PAGESIZE); CHECK_EQ(1, popcnt(align)); CHECK_EQ(0, (phdr->p_vaddr - phdr->p_offset) % align); + /*-Type-Offset---VirtAddr-----------PhysAddr-----------FileSiz--MemSiz---Flg-Align----*/ + /*-LOAD-0x000000-0x0000000000400000-0x0000000000400000-0x0008e4-0x0008e4-R-E-0x200000-*/ + /*-LOAD-0x000fe0-0x0000000000600fe0-0x0000000000600fe0-0x000030-0x000310-RW--0x200000-*/ felf = (int64_t)(intptr_t)code; vstart = ROUNDDOWN(phdr->p_vaddr, align); vbss = ROUNDUP(phdr->p_vaddr + phdr->p_filesz, align); vend = ROUNDUP(phdr->p_vaddr + phdr->p_memsz, align); fstart = felf + ROUNDDOWN(phdr->p_offset, align); - fend = felf + ROUNDUP(phdr->p_offset + phdr->p_filesz, align); + fend = felf + phdr->p_offset + phdr->p_filesz; bsssize = vend - vbss; + LOGF("LOADELFLOADSEGMENT" + " VSTART %#lx VBSS %#lx VEND %#lx" + " FSTART %#lx FEND %#lx BSSSIZE %#lx", + vstart, vbss, vend, fstart, fend, bsssize); m->brk = MAX(m->brk, vend); CHECK_GE(vend, vstart); CHECK_GE(fend, fstart); @@ -61,12 +69,9 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, CHECK_GE(vend - vstart, fstart - fend); CHECK_LE(phdr->p_filesz, phdr->p_memsz); CHECK_EQ(felf + phdr->p_offset - fstart, phdr->p_vaddr - vstart); - CHECK_NE(-1, RegisterMemory(m, vstart, (void *)fstart, fend - fstart)); - if (bsssize) { - CHECK_NE(MAP_FAILED, (rbss = mmap(NULL, bsssize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); - CHECK_NE(-1, RegisterMemory(m, vbss, rbss, bsssize)); - } + CHECK_NE(-1, ReserveVirtual(m, vstart, fend - fstart)); + VirtualRecv(m, vstart, (void *)fstart, fend - fstart); + if (bsssize) CHECK_NE(-1, ReserveVirtual(m, vbss, bsssize)); if (phdr->p_memsz - phdr->p_filesz > bsssize) { VirtualSet(m, phdr->p_vaddr + phdr->p_filesz, 0, phdr->p_memsz - phdr->p_filesz - bsssize); @@ -76,11 +81,8 @@ static void LoadElfLoadSegment(struct Machine *m, void *code, size_t codesize, static void LoadElf(struct Machine *m, struct Elf *elf) { unsigned i; Elf64_Phdr *phdr; - if (elf->ehdr->e_type != ET_EXEC) { - write(STDERR_FILENO, DSOLOL, strlen(DSOLOL)); - exit(1); - } m->ip = elf->base = elf->ehdr->e_entry; + LOGF("LOADELF ENTRY %p", m->ip); for (i = 0; i < elf->ehdr->e_phnum; ++i) { phdr = getelfsegmentheaderaddress(elf->ehdr, elf->size, i); switch (phdr->p_type) { @@ -114,10 +116,11 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars, struct Elf *elf) { int fd; ssize_t rc; + int64_t sp; + char *real; void *stack; struct stat st; - char *real, *memory; - size_t i, codesize, mappedsize, extrasize, stacksize; + size_t i, codesize, mappedsize, extrasize; DCHECK_NOTNULL(prog); elf->prog = prog; if ((fd = open(prog, O_RDONLY)) == -1 || @@ -148,14 +151,11 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars, ResetCpu(m); if (m->mode == XED_MACHINE_MODE_REAL) { elf->base = 0x7c00; - CHECK_NE(MAP_FAILED, - (memory = mmap(NULL, BIGPAGESIZE, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); - RegisterMemory(m, 0, memory, BIGPAGESIZE); + CHECK_NE(-1, ReserveVirtual(m, 0, BIGPAGESIZE)); m->ip = 0x7c00; Write64(m->cs, 0); Write64(m->dx, 0); - memcpy(memory + 0x7c00, elf->map, 512); + VirtualRecv(m, m->ip, elf->map, 512); if (memcmp(elf->map, "\177ELF", 4) == 0) { elf->ehdr = (void *)elf->map; elf->size = codesize; @@ -166,11 +166,9 @@ void LoadProgram(struct Machine *m, const char *prog, char **args, char **vars, elf->size = 0; } } else { - stacksize = STACKSIZE; - CHECK_NE(MAP_FAILED, (stack = mmap(NULL, stacksize, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0))); - Write64(m->sp, 0x0000800000000000); - RegisterMemory(m, 0x0000800000000000 - stacksize, stack, stacksize); + sp = 0x800000000000; + Write64(m->sp, sp); + CHECK_NE(-1, ReserveVirtual(m, sp - STACKSIZE, STACKSIZE)); LoadArgv(m, prog, args, vars); if (memcmp(elf->map, "\177ELF", 4) == 0) { elf->ehdr = (void *)elf->map; diff --git a/tool/build/lib/machine.c b/tool/build/lib/machine.c index 5f3416ab..8ebcc057 100644 --- a/tool/build/lib/machine.c +++ b/tool/build/lib/machine.c @@ -17,11 +17,6 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/assert.h" -#include "libc/conv/conv.h" -#include "libc/dce.h" -#include "libc/log/check.h" -#include "libc/log/log.h" #include "libc/macros.h" #include "libc/runtime/runtime.h" #include "tool/build/lib/abp.h" @@ -558,7 +553,7 @@ static void OpMovZvqpIvqp(struct Machine *m, uint32_t rde) { WriteRegister(rde, RegRexbSrm(m, rde), m->xedd->op.uimm0); } -static void OpIncZv(struct Machine *m, uint32_t rde) { +static relegated void OpIncZv(struct Machine *m, uint32_t rde) { if (!Osz(rde)) { Write32(RegSrm(m, rde), Inc32(Read32(RegSrm(m, rde)), 0, &m->flags)); } else { @@ -566,7 +561,7 @@ static void OpIncZv(struct Machine *m, uint32_t rde) { } } -static void OpDecZv(struct Machine *m, uint32_t rde) { +static relegated void OpDecZv(struct Machine *m, uint32_t rde) { if (!Osz(rde)) { Write32(RegSrm(m, rde), Dec32(Read32(RegSrm(m, rde)), 0, &m->flags)); } else { @@ -888,22 +883,6 @@ static void OpBsubiImm(struct Machine *m, uint32_t rde) { Bsubi(m, rde, m->xedd->op.uimm0); } -static relegated void LoadFarPointer(struct Machine *m, uint32_t rde, - uint8_t seg[8]) { - uint32_t fp; - fp = Read32(ComputeReserveAddressRead4(m, rde)); - Write64(seg, (fp & 0x0000ffff) << 4); - Write16(RegRexrReg(m, rde), fp >> 16); -} - -static relegated void OpLes(struct Machine *m, uint32_t rde) { - LoadFarPointer(m, rde, m->es); -} - -static relegated void OpLds(struct Machine *m, uint32_t rde) { - LoadFarPointer(m, rde, m->ds); -} - static void OpLgdtMs(struct Machine *m, uint32_t rde) { } @@ -1234,6 +1213,22 @@ static void OpNegEb(struct Machine *m, uint32_t rde) { AluEb(m, rde, Neg8); } +static relegated void LoadFarPointer(struct Machine *m, uint32_t rde, + uint8_t seg[8]) { + uint32_t fp; + fp = Read32(ComputeReserveAddressRead4(m, rde)); + Write64(seg, (fp & 0x0000ffff) << 4); + Write16(RegRexrReg(m, rde), fp >> 16); +} + +static relegated void OpLes(struct Machine *m, uint32_t rde) { + LoadFarPointer(m, rde, m->es); +} + +static relegated void OpLds(struct Machine *m, uint32_t rde) { + LoadFarPointer(m, rde, m->ds); +} + static relegated void Loop(struct Machine *m, uint32_t rde, bool cond) { uint64_t cx; cx = Read64(m->cx) - 1; @@ -1386,10 +1381,12 @@ static void OpSalc(struct Machine *m, uint32_t rde) { } static void OpNopEv(struct Machine *m, uint32_t rde) { - if (ModrmMod(rde) == 0b01 && ModrmReg(rde) == 0 && ModrmRm(rde) == 0b101) { - OpBofram(m, rde); - } else { - OpNoop(m, rde); + switch (ModrmMod(rde) << 6 | ModrmReg(rde) << 3 | ModrmRm(rde)) { + case 0x45: + OpBofram(m, rde); + break; + default: + OpNoop(m, rde); } } diff --git a/tool/build/lib/machine.h b/tool/build/lib/machine.h index 130d7e21..41469d2c 100644 --- a/tool/build/lib/machine.h +++ b/tool/build/lib/machine.h @@ -5,10 +5,6 @@ #include "tool/build/lib/fds.h" #include "tool/build/lib/pml4t.h" -#define kXmmIntegral 0 -#define kXmmDouble 1 -#define kXmmFloat 2 - #define kMachineHalt -1 #define kMachineDecodeError -2 #define kMachineUndefinedInstruction -3 @@ -28,7 +24,7 @@ struct Machine { uint8_t cs[8]; uint8_t ss[8]; uint64_t codevirt; - uint8_t *codereal; + uint8_t *codehost; uint32_t mode; uint32_t flags; uint32_t tlbindex; @@ -59,13 +55,15 @@ struct Machine { uint8_t r15[8]; }; }; - uint8_t *real; - uint64_t realsize; - uint64_t *cr3; - struct TlbEntry { - int64_t v; - void *r; + struct MachineTlb { + int64_t virt; + uint8_t *host; } tlb[16]; + struct MachineReal { + size_t i, n; + uint8_t *p; + } real; + uint64_t cr3; uint8_t xmm[16][16] aligned(16); uint8_t es[8]; uint8_t ds[8]; @@ -76,34 +74,34 @@ struct Machine { union { uint32_t cw; struct { - unsigned im : 1; /* invalid operation mask */ - unsigned dm : 1; /* denormal operand mask */ - unsigned zm : 1; /* zero divide mask */ - unsigned om : 1; /* overflow mask */ - unsigned um : 1; /* underflow mask */ - unsigned pm : 1; /* precision mask */ - unsigned _p1 : 2; /* reserved */ - unsigned pc : 2; /* precision: 32,∅,64,80 */ - unsigned rc : 2; /* rounding: even,→-∞,→+∞,→0 */ + unsigned im : 1; // invalid operation mask + unsigned dm : 1; // denormal operand mask + unsigned zm : 1; // zero divide mask + unsigned om : 1; // overflow mask + unsigned um : 1; // underflow mask + unsigned pm : 1; // precision mask + unsigned _p1 : 2; // reserved + unsigned pc : 2; // precision: 32,∅,64,80 + unsigned rc : 2; // rounding: even,→-∞,→+∞,→0 }; }; union { uint32_t sw; struct { - unsigned ie : 1; /* invalid operation */ - unsigned de : 1; /* denormalized operand */ - unsigned ze : 1; /* zero divide */ - unsigned oe : 1; /* overflow */ - unsigned ue : 1; /* underflow */ - unsigned pe : 1; /* precision */ - unsigned sf : 1; /* stack fault */ - unsigned es : 1; /* exception summary status */ - unsigned c0 : 1; /* condition 0 */ - unsigned c1 : 1; /* condition 1 */ - unsigned c2 : 1; /* condition 2 */ - unsigned sp : 3; /* top stack */ - unsigned c3 : 1; /* condition 3 */ - unsigned bf : 1; /* busy flag */ + unsigned ie : 1; // invalid operation + unsigned de : 1; // denormalized operand + unsigned ze : 1; // zero divide + unsigned oe : 1; // overflow + unsigned ue : 1; // underflow + unsigned pe : 1; // precision + unsigned sf : 1; // stack fault + unsigned es : 1; // exception summary status + unsigned c0 : 1; // condition 0 + unsigned c1 : 1; // condition 1 + unsigned c2 : 1; // condition 2 + unsigned sp : 3; // top stack + unsigned c3 : 1; // condition 3 + unsigned bf : 1; // busy flag }; }; int tw; @@ -111,28 +109,33 @@ struct Machine { int64_t ip; int64_t dp; } fpu; - struct { + struct MachineSse { union { uint32_t mxcsr; struct { - unsigned ie : 1; /* invalid operation flag */ - unsigned de : 1; /* denormal flag */ - unsigned ze : 1; /* divide by zero flag */ - unsigned oe : 1; /* overflow flag */ - unsigned ue : 1; /* underflow flag */ - unsigned pe : 1; /* precision flag */ - unsigned daz : 1; /* denormals are zeros */ - unsigned im : 1; /* invalid operation mask */ - unsigned dm : 1; /* denormal mask */ - unsigned zm : 1; /* divide by zero mask */ - unsigned om : 1; /* overflow mask */ - unsigned um : 1; /* underflow mask */ - unsigned pm : 1; /* precision mask */ - unsigned rc : 2; /* rounding control */ - unsigned ftz : 1; /* flush to zero */ + unsigned ie : 1; // invalid operation flag + unsigned de : 1; // denormal flag + unsigned ze : 1; // divide by zero flag + unsigned oe : 1; // overflow flag + unsigned ue : 1; // underflow flag + unsigned pe : 1; // precision flag + unsigned daz : 1; // denormals are zeros + unsigned im : 1; // invalid operation mask + unsigned dm : 1; // denormal mask + unsigned zm : 1; // divide by zero mask + unsigned om : 1; // overflow mask + unsigned um : 1; // underflow mask + unsigned pm : 1; // precision mask + unsigned rc : 2; // rounding control + unsigned ftz : 1; // flush to zero }; }; } sse; + struct MachineRealFree { + uint64_t i; + uint64_t n; + struct MachineRealFree *next; + } * realfree; struct FreeList { uint32_t i; void *p[6]; @@ -141,18 +144,23 @@ struct Machine { int64_t bofram[2]; jmp_buf onhalt; int64_t faultaddr; - uint8_t stash[4096]; - uint8_t xmmtype[2][8]; - uint8_t icache[4096 / 4][40] aligned(16); struct MachineFds fds; + uint8_t stash[4096]; + uint8_t icache[1024][40]; } aligned(64); +struct Machine *NewMachine(void) nodiscard; +void FreeMachine(struct Machine *); +void ResetMem(struct Machine *); void ResetCpu(struct Machine *); +void ResetTlb(struct Machine *); void LoadInstruction(struct Machine *); void ExecuteInstruction(struct Machine *); -struct Machine *NewMachine(void) nodiscard; -void LoadArgv(struct Machine *, const char *, char **, char **); -void InitMachine(struct Machine *); +long AllocateLinearPage(struct Machine *); +int ReserveVirtual(struct Machine *, int64_t, size_t); +char *FormatPml4t(struct Machine *) nodiscard; +int64_t FindVirtual(struct Machine *, int64_t, size_t); +int FreeVirtual(struct Machine *, int64_t, size_t); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/memory.c b/tool/build/lib/memory.c index 58d911b8..7d6a7ab6 100644 --- a/tool/build/lib/memory.c +++ b/tool/build/lib/memory.c @@ -31,39 +31,54 @@ #include "tool/build/lib/throw.h" void SetReadAddr(struct Machine *m, int64_t addr, uint32_t size) { - m->readaddr = addr; - m->readsize = size; + if (size) { + m->readaddr = addr; + m->readsize = size; + } } void SetWriteAddr(struct Machine *m, int64_t addr, uint32_t size) { - m->writeaddr = addr; - m->writesize = size; + if (size) { + m->writeaddr = addr; + m->writesize = size; + } } -void *FindReal(struct Machine *m, int64_t v) { - uint64_t *p; - unsigned skew; - unsigned char i; - skew = v & 0xfff; - v &= -0x1000; +void *FindReal(struct Machine *m, int64_t virt) { + uint8_t *host; + uint64_t real, pte, *pt; + unsigned skew, level, i; + if (!(-0x800000000000 <= virt && virt < 0x800000000000)) { + return NULL; + } + skew = virt & 0xfff; + virt &= -0x1000; for (i = 0; i < ARRAYLEN(m->tlb); ++i) { - if (m->tlb[i].v == v && m->tlb[i].r) { - return (char *)m->tlb[i].r + skew; + if (m->tlb[i].virt == virt && m->tlb[i].host) { + return m->tlb[i].host + skew; } } - for (p = m->cr3, i = 39; i >= 12; i -= 9) { - if (IsValidPage(p[(v >> i) & 511])) { - p = UnmaskPageAddr(p[(v >> i) & 511]); - } else { + level = 39; + real = m->cr3; + for (;;) { + if (real + 0x1000 > m->real.n) { return NULL; } + host = m->real.p + real; + if (level < 12) break; + pt = (uint64_t *)host; + pte = pt[(virt >> level) & 511]; + if (!(pte & 1)) { + return NULL; + } + real = pte & 0x00007ffffffff000; + level -= 9; } m->tlbindex = (m->tlbindex + 1) & (ARRAYLEN(m->tlb) - 1); m->tlb[m->tlbindex] = m->tlb[0]; - m->tlb[0].r = p; - m->tlb[0].v = ROUNDDOWN(v, 0x1000); - DCHECK_NOTNULL(p); - return (char *)p + skew; + m->tlb[0].host = host; + m->tlb[0].virt = virt; + return host + skew; } void *ResolveAddress(struct Machine *m, int64_t v) { @@ -196,7 +211,7 @@ void *LoadStr(struct Machine *m, int64_t addr) { if (!addr) return NULL; if (!(page = FindReal(m, addr))) return NULL; if ((p = memchr(page, '\0', have))) { - SetReadAddr(m, addr, p - page); + SetReadAddr(m, addr, p - page + 1); return page; } CHECK_LT(m->freelist.i, ARRAYLEN(m->freelist.p)); @@ -205,7 +220,7 @@ void *LoadStr(struct Machine *m, int64_t addr) { for (;;) { if (!(page = FindReal(m, addr + have))) break; if ((p = memccpy(copy + have, page, '\0', 0x1000))) { - SetReadAddr(m, addr, have + (p - (copy + have))); + SetReadAddr(m, addr, have + (p - (copy + have)) + 1); return (m->freelist.p[m->freelist.i++] = copy); } have += 0x1000; @@ -217,19 +232,19 @@ void *LoadStr(struct Machine *m, int64_t addr) { } void *LoadBuf(struct Machine *m, int64_t addr, size_t size) { - char *buf, *copy; size_t have, need; + char *buf, *copy, *page; have = 0x1000 - (addr & 0xfff); if (!addr) return NULL; if (!(buf = FindReal(m, addr))) return NULL; if (size > have) { CHECK_LT(m->freelist.i, ARRAYLEN(m->freelist.p)); if (!(copy = malloc(size))) return NULL; - memcpy(copy, buf, have); + buf = memcpy(copy, buf, have); do { need = MIN(0x1000, size - have); - if ((buf = FindReal(m, addr + have))) { - memcpy(copy + have, buf, need); + if ((page = FindReal(m, addr + have))) { + memcpy(copy + have, page, need); have += need; } else { free(copy); diff --git a/tool/build/lib/memorymalloc.c b/tool/build/lib/memorymalloc.c index 1d9fdbf6..8fea2bb9 100644 --- a/tool/build/lib/memorymalloc.c +++ b/tool/build/lib/memorymalloc.c @@ -17,35 +17,167 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/assert.h" #include "libc/calls/calls.h" +#include "libc/log/check.h" +#include "libc/log/log.h" #include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" #include "libc/str/str.h" +#include "libc/sysv/errfuns.h" +#include "libc/x/x.h" +#include "tool/build/lib/buffer.h" +#include "tool/build/lib/endian.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/pml4t.h" -void *MallocPage(void) { - void *p; - size_t n; - if ((p = memalign(4096, 4096))) { - memset(p, 0, 4096); - } - return p; -} - -int RegisterMemory(struct Machine *m, int64_t v, void *r, size_t n) { - return RegisterPml4t(m->cr3, v, (int64_t)(intptr_t)r, n, MallocPage); -} - -void ResetRam(struct Machine *m) { - FreePml4t(m->cr3, -0x800000000000, 0x800000000000, free, munmap); -} - struct Machine *NewMachine(void) { struct Machine *m; - m = memalign(alignof(struct Machine), sizeof(struct Machine)); - memset(m, 0, sizeof(struct Machine)); + m = xmemalignzero(alignof(struct Machine), sizeof(struct Machine)); m->mode = XED_MACHINE_MODE_LONG_64; - InitMachine(m); + ResetCpu(m); + ResetMem(m); return m; } + +static void FreeMachineRealFree(struct Machine *m) { + struct MachineRealFree *rf; + while ((rf = m->realfree)) { + m->realfree = rf->next; + free(rf); + } +} + +void FreeMachine(struct Machine *m) { + if (m) { + FreeMachineRealFree(m); + free(m->real.p); + free(m); + } +} + +void ResetMem(struct Machine *m) { + FreeMachineRealFree(m); + ResetTlb(m); + m->real.i = 0; + m->cr3 = AllocateLinearPage(m); +} + +long AllocateLinearPage(struct Machine *m) { + uint8_t *p; + size_t i, n; + struct MachineRealFree *rf; + if ((rf = m->realfree)) { + DCHECK(rf->n); + DCHECK_EQ(0, rf->i & 0xfff); + DCHECK_EQ(0, rf->n & 0xfff); + DCHECK_LE(rf->i + rf->n, m->real.i); + i = rf->i; + rf->i += 0x1000; + if (!(rf->n -= 0x1000)) { + m->realfree = rf->next; + free(rf); + } + } else { + i = m->real.i; + n = m->real.n; + p = m->real.p; + if (i == n) { + if (n) { + n += n >> 1; + } else { + n = 0x10000; + } + n = ROUNDUP(n, 0x1000); + if ((p = realloc(p, n))) { + m->real.p = p; + m->real.n = n; + ResetTlb(m); + } else { + return -1; + } + } + DCHECK_EQ(0, i & 0xfff); + DCHECK_EQ(0, n & 0xfff); + DCHECK_LE(i + 0x1000, n); + m->real.i += 0x1000; + } + memset(m->real.p + i, 0, 0x1000); /* TODO: lazy page clearing */ + return i; +} + +static uint64_t MachineRead64(struct Machine *m, unsigned long i) { + CHECK_LE(i + 8, m->real.n); + return Read64(m->real.p + i); +} + +static void MachineWrite64(struct Machine *m, unsigned long i, uint64_t x) { + CHECK_LE(i + 8, m->real.n); + Write64(m->real.p + i, x); +} + +int ReserveVirtual(struct Machine *m, int64_t virt, size_t size) { + int64_t level, pt, ti, mi, end; + for (end = virt + size; virt < end; virt += 0x1000) { + for (pt = m->cr3, level = 39; level >= 12; level -= 9) { + pt = pt & 0x00007ffffffff000; + ti = (virt >> level) & 511; + DEBUGF("reserve %p level %d table %p index %ld", virt, level, pt, ti); + mi = pt + ti * 8; + pt = MachineRead64(m, mi); + if (!(pt & 1)) { + if ((pt = AllocateLinearPage(m)) == -1) return -1; + MachineWrite64(m, mi, pt | 7); + } + } + } + return 0; +} + +int64_t FindVirtual(struct Machine *m, int64_t virt, size_t size) { + uint64_t i, pt, got; + got = 0; + do { + if (virt >= 0x800000000000) return enomem(); + for (pt = m->cr3, i = 39; i >= 12; i -= 9) { + pt = MachineRead64(m, (pt & 0x7ffffffff000) + ((virt >> i) & 511) * 8); + if (!(pt & 1)) break; + } + if (i >= 12) { + got += 1ull << i; + } else { + virt += 0x1000; + got = 0; + } + } while (got < size); + return virt; +} + +int FreeVirtual(struct Machine *m, int64_t base, size_t size) { + struct MachineRealFree *rf; + uint64_t i, mi, pt, la, end, virt; + for (virt = base, end = virt + size; virt < end;) { + for (pt = m->cr3, i = 39;; i -= 9) { + mi = (pt & 0x7ffffffff000) + ((virt >> i) & 511) * 8; + pt = MachineRead64(m, mi); + if (!(pt & 1)) { + break; + } else if (i == 12) { + MachineWrite64(m, mi, 0); + la = pt & 0x7ffffffff000; + if (m->realfree && la == m->realfree->i + m->realfree->n) { + m->realfree->n += 0x1000; + } else if ((rf = malloc(sizeof(struct MachineRealFree)))) { + rf->i = la; + rf->n = 0x1000; + rf->next = m->realfree; + m->realfree = rf; + } + break; + } + } + virt += 1ull << i; + } + return 0; +} diff --git a/tool/build/lib/pml4t.c b/tool/build/lib/pml4t.c deleted file mode 100644 index 6f8800a8..00000000 --- a/tool/build/lib/pml4t.c +++ /dev/null @@ -1,192 +0,0 @@ -/*-*- 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│ -╞══════════════════════════════════════════════════════════════════════════════╡ -│ Copyright 2020 Justine Alexandra Roberts Tunney │ -│ │ -│ This program is free software; you can redistribute it and/or modify │ -│ it under the terms of the GNU General Public License as published by │ -│ the Free Software Foundation; version 2 of the License. │ -│ │ -│ This program is distributed in the hope that it will be useful, but │ -│ WITHOUT ANY WARRANTY; without even the implied warranty of │ -│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ -│ General Public License for more details. │ -│ │ -│ You should have received a copy of the GNU General Public License │ -│ along with this program; if not, write to the Free Software │ -│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ -│ 02110-1301 USA │ -╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/log/check.h" -#include "libc/macros.h" -#include "libc/mem/mem.h" -#include "libc/str/str.h" -#include "libc/sysv/errfuns.h" -#include "tool/build/lib/memory.h" -#include "tool/build/lib/pml4t.h" - -static int64_t MakeAddress(unsigned short a[4]) { - uint64_t x; - x = 0; - x |= a[0]; - x <<= 9; - x |= a[1]; - x <<= 9; - x |= a[2]; - x <<= 9; - x |= a[3]; - x <<= 12; - return SignExtendAddr(x); -} - -static uint64_t *GetPageTable(pml4t_t p, long i, void *NewPhysicalPage(void)) { - uint64_t *res; - DCHECK_ALIGNED(4096, p); - DCHECK(0 <= i && i < 512); - if (IsValidPage(p[i])) { - res = UnmaskPageAddr(p[i]); - } else if ((res = NewPhysicalPage())) { - DCHECK_ALIGNED(4096, res); - p[i] = MaskPageAddr(res) | 0b11; - } - return res; -} - -static void PtFinder(uint64_t *a, uint64_t *b, uint64_t n, pml4t_t pd, int k) { - unsigned i; - uint64_t e, c; - while (*b - *a < n) { - i = (*b >> k) & 511; - e = pd[i]; - c = ROUNDUP(*b + 1, 1 << k); - if (!IsValidPage(e)) { - *b = c; - } else if (k && *b - *a + (c - *b) > n) { - PtFinder(a, b, n, UnmaskPageAddr(e), k - 9); - } else { - *a = *b = c; - } - if (((*b >> k) & 511) < i) { - break; - } - } -} - -/** - * Locates free memory range. - * - * @param h specifies signedness and around where to start searching - * @return virtual page address with size bytes free, or -1 w/ errno - */ -int64_t FindPml4t(pml4t_t pml4t, uint64_t h, uint64_t n) { - uint64_t a, b; - n = ROUNDUP(n, 4096) >> 12; - a = b = (h & 0x0000fffffffff000) >> 12; - if (!n || n > 0x10000000) return einval(); - PtFinder(&a, &b, n, pml4t, 9 * 3); - if (b > 0x0000001000000000) return eoverflow(); - if (h < 0x0000800000000000 && b > 0x0000000800000000) return eoverflow(); - if (b - a < n) return enomem(); - return a << 12; -} - -/** - * Maps virtual page region to system memory region. - * - * @param pml4t is root of 48-bit page tables - * @param v is fixed page-aligned virtual address, rounded down - * @param r is real memory address, rounded down - * @param n is number of bytes needed, rounded up - * @return 0 on success, or -1 w/ errno - * @note existing pages are overwritten - */ -int RegisterPml4t(pml4t_t pml4t, int64_t v, int64_t r, size_t n, - void *NewPhysicalPage(void)) { - unsigned i, j, k, l; - uint64_t *pdpt, *pdt, *pd, u; - if (!n) return 0; - u = ROUNDDOWN(r, 4096); - n = ROUNDUP(n, 4096) >> 12; - i = (v >> 39) & 511; - j = (v >> 30) & 511; - k = (v >> 21) & 511; - l = (v >> 12) & 511; - if (u + n > 0x800000000000) return eoverflow(); - if (r + n > 0x800000000000) return eoverflow(); - for (; i < 512; ++i) { - if (!(pdpt = GetPageTable(pml4t, i, NewPhysicalPage))) return -1; - for (; j < 512; ++j) { - if (!(pdt = GetPageTable(pdpt, j, NewPhysicalPage))) return -1; - for (; k < 512; ++k) { - if (!(pd = GetPageTable(pdt, k, NewPhysicalPage))) return -1; - for (; l < 512; ++l) { - pd[l] = MaskPageAddr(u) | 0b11; - if (!--n) return 0; - u += 4096; - } - l = 0; - } - k = 0; - } - j = 0; - } - return enomem(); -} - -/** - * Unmaps pages and frees page tables. - */ -int FreePml4t(pml4t_t pml4t, int64_t addr, uint64_t size, - void FreePhysicalPageTable(void *), - int FreePhysicalPages(void *, size_t)) { - int rc; - char *pages; - uint64_t i, *pdpt, *pdt, *pd; - unsigned short r, s[4], a[4], R[2][2] = {{256, 512}, {0, 256}}; - a[0] = addr >> 39; - a[1] = addr >> 30; - a[2] = addr >> 21; - a[3] = addr >> 12; - size = ROUNDUP(size, 4096) >> 12; - for (rc = r = 0; r < ARRAYLEN(R); ++r) { - for (a[0] &= 511; size && R[r][0] <= a[0] && a[0] < R[r][1]; ++a[0]) { - if (!IsValidPage(pml4t[a[0]])) continue; - pdpt = UnmaskPageAddr(pml4t[a[0]]); - for (s[1] = (a[1] &= 511); size && a[1] < 512; ++a[1]) { - if (!IsValidPage(pdpt[a[1]])) continue; - pdt = UnmaskPageAddr(pdpt[a[1]]); - for (s[2] = (a[2] &= 511); size && a[2] < 512; ++a[2]) { - if (!IsValidPage(pdt[a[2]])) continue; - pd = UnmaskPageAddr(pdt[a[2]]); - for (s[3] = (a[3] &= 511); size && a[3] < 512; ++a[3]) { - if (IsValidPage(pd[a[3]])) { - pages = UnmaskPageAddr(pd[a[3]]); - pd[a[3]] = 0; - for (i = 1; i + 1 < size && a[3] + i < 512; ++i) { - if (!IsValidPage(pd[a[3] + i])) break; - if (UnmaskPageAddr(pd[a[3] + i]) != pages + i * 4096) break; - pd[a[3] + i] = 0; - } - FreePhysicalPages(pages, i * 4096); - a[3] += i - 1; - size -= i; - } - } - if (s[3] == 0 && a[3] == 512) { - FreePhysicalPageTable(pd); - pdt[a[2]] = 0; - } - } - if (s[2] == 0 && a[2] == 512) { - FreePhysicalPageTable(pdt); - pdpt[a[1]] = 0; - } - } - if (s[1] == 0 && a[1] == 512) { - FreePhysicalPageTable(pdpt); - pml4t[a[0]] = 0; - } - } - } - return 0; -} diff --git a/tool/build/lib/pml4t.h b/tool/build/lib/pml4t.h index 349e98f5..919341d1 100644 --- a/tool/build/lib/pml4t.h +++ b/tool/build/lib/pml4t.h @@ -4,17 +4,9 @@ COSMOPOLITAN_C_START_ #define IsValidPage(x) ((x)&1) -#define UnmaskPageAddr(x) ((void *)SignExtendAddr(MaskPageAddr(x))) -#define MaskPageAddr(x) ((int64_t)(intptr_t)(x)&0x00007ffffffff000) -#define SignExtendAddr(x) (!((x)&0x800000000000) ? (x) : (x) | -0x800000000000) - -typedef uint64_t pml4t_t[512] aligned(4096); - -int FreePml4t(pml4t_t, int64_t, uint64_t, void (*)(void *), - int (*)(void *, size_t)); -int RegisterPml4t(pml4t_t, int64_t, int64_t, size_t, void *(*)(void)); -int64_t FindPml4t(pml4t_t, uint64_t, uint64_t); -char *FormatPml4t(pml4t_t) nodiscard; +#define MaskPageAddr(x) ((x)&0x00007ffffffff000) +#define UnmaskPageAddr(x) SignExtendAddr(MaskPageAddr(x)) +#define SignExtendAddr(x) ((int64_t)((uint64_t)(x) << 16) >> 16) COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/pml4tfmt.c b/tool/build/lib/pml4tfmt.c index 18c72a65..434ffb84 100644 --- a/tool/build/lib/pml4tfmt.c +++ b/tool/build/lib/pml4tfmt.c @@ -17,6 +17,7 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/log/check.h" #include "libc/macros.h" #include "libc/mem/mem.h" #include "libc/x/x.h" @@ -59,12 +60,17 @@ static void FormatEndPage(struct Pml4tFormater *pp, int64_t end) { AppendFmt(&pp->b, "%p %p %,ld bytes", end - 1, size, size); } -char *FormatPml4t(uint64_t pml4t[512]) { +static void *GetPt(struct Machine *m, uint64_t r) { + CHECK_LE(r + 0x1000, m->real.n); + return m->real.p + r; +} + +char *FormatPml4t(struct Machine *m) { uint64_t *pd[4]; unsigned short i, a[4]; struct Pml4tFormater pp = {0}; unsigned short range[][2] = {{256, 512}, {0, 256}}; - pd[0] = pml4t; + pd[0] = GetPt(m, m->cr3); for (i = 0; i < ARRAYLEN(range); ++i) { a[0] = range[i][0]; do { @@ -72,19 +78,19 @@ char *FormatPml4t(uint64_t pml4t[512]) { if (!IsValidPage(pd[0][a[0]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); } else { - pd[1] = UnmaskPageAddr(pd[0][a[0]]); + pd[1] = GetPt(m, UnmaskPageAddr(pd[0][a[0]])); do { a[2] = a[3] = 0; if (!IsValidPage(pd[1][a[1]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); } else { - pd[2] = UnmaskPageAddr(pd[1][a[1]]); + pd[2] = GetPt(m, UnmaskPageAddr(pd[1][a[1]])); do { a[3] = 0; if (!IsValidPage(pd[2][a[2]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); } else { - pd[3] = UnmaskPageAddr(pd[2][a[2]]); + pd[3] = GetPt(m, UnmaskPageAddr(pd[2][a[2]])); do { if (!IsValidPage(pd[3][a[3]])) { if (pp.t) FormatEndPage(&pp, MakeAddress(a)); diff --git a/tool/build/lib/pty.c b/tool/build/lib/pty.c index 31e81594..5e9d8d3d 100644 --- a/tool/build/lib/pty.c +++ b/tool/build/lib/pty.c @@ -18,28 +18,356 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/alg/arraylist2.h" +#include "libc/assert.h" #include "libc/bits/bits.h" #include "libc/bits/safemacros.h" #include "libc/conv/conv.h" #include "libc/conv/itoa.h" #include "libc/log/check.h" +#include "libc/log/log.h" #include "libc/macros.h" #include "libc/mem/mem.h" #include "libc/nexgen32e/bsr.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "libc/str/thompike.h" +#include "libc/str/tpenc.h" #include "libc/unicode/unicode.h" #include "libc/x/x.h" #include "tool/build/lib/pty.h" +/** + * @fileoverview Pseudoteletypewriter + * + * \t TAB + * \a BELL + * \r CURSOR START + * \b CURSOR LEFT + * \177 CURSOR LEFT + * \n CURSOR DOWN AND START IF OPOST + * \f CURSOR DOWN AND START IF OPOST + * \v CURSOR DOWN AND START IF OPOST + * \eD CURSOR DOWN AND START + * \eE CURSOR DOWN + * \eM CURSOR UP + * \ec FULL RESET + * \e7 SAVE CURSOR POSITION + * \e8 RESTORE CURSOR POSITION + * \e(0 DEC SPECIAL GRAPHICS + * \e(B USAS X3.4-1967 + * \e#5 SINGLE WIDTH + * \e#6 DOUBLE WIDTH + * \e#8 SO MANY E + * \eZ → \e/Z REPORT IDENTITY + * \e[𝑛A CURSOR UP [CLAMPED] + * \e[𝑛B CURSOR DOWN [CLAMPED] + * \e[𝑛C CURSOR RIGHT [CLAMPED] + * \e[𝑛D CURSOR LEFT [CLAMPED] + * \e[𝑦;𝑥H SET CURSOR POSITION [CLAMPED] + * \e[𝑥G SET CURSOR COLUMN [CLAMPED] + * \e[𝑦d SET CURSOR ROW [CLAMPED] + * \e[𝑛E CURSOR DOWN AND START [CLAMPED] + * \e[𝑛F CURSOR UP AND START [CLAMPED] + * \e[𝑛S SCROLL UP + * \e[𝑛T SCROLL DOWN + * \e[𝑛@ INSERT CELLS + * \e[𝑛P DELETE CELLS + * \e[𝑛L INSERT LINES + * \e[𝑛M DELETE LINES + * \e[J ERASE DISPLAY FORWARDS + * \e[1J ERASE DISPLAY BACKWARDS + * \e[2J ERASE DISPLAY + * \e[K ERASE LINE FORWARD + * \e[1K ERASE LINE BACKWARD + * \e[2K ERASE LINE + * \e[𝑛X ERASE CELLS + * \e[0m RESET + * \e[1m BOLD + * \e[2m FAINT + * \e[3m ITALIC + * \e[4m UNDER + * \e[5m BLINK + * \e[7m INVERT + * \e[8m CONCEAL + * \e[9m STRIKE + * \e[20m FRAKTUR + * \e[21m DUNDER + * \e[22m RESET BOLD & FAINT + * \e[23m RESET ITALIC & FRAKTUR + * \e[24m RESET UNDER & DUNDER + * \e[25m RESET BLINK + * \e[27m RESET INVERT + * \e[28m RESET CONCEAL + * \e[29m RESET STRIKE + * \e[39m RESET FOREGROUND + * \e[49m RESET BACKGROUND + * \e[30m BLACK FOREGROUND + * \e[31m RED FOREGROUND + * \e[32m GREEN FOREGROUND + * \e[33m YELLOW FOREGROUND + * \e[34m BLUE FOREGROUND + * \e[35m MAGENTA FOREGROUND + * \e[36m CYAN FOREGROUND + * \e[37m WHITE FOREGROUND + * \e[40m BLACK BACKGROUND + * \e[41m RED BACKGROUND + * \e[42m GREEN BACKGROUND + * \e[44m YELLOW BACKGROUND + * \e[44m BLUE BACKGROUND + * \e[45m MAGENTA BACKGROUND + * \e[46m CYAN BACKGROUND + * \e[47m WHITE BACKGROUND + * \e[90m BRIGHT BLACK FOREGROUND + * \e[91m BRIGHT RED FOREGROUND + * \e[92m BRIGHT GREEN FOREGROUND + * \e[93m BRIGHT YELLOW FOREGROUND + * \e[94m BRIGHT BLUE FOREGROUND + * \e[95m BRIGHT MAGENTA FOREGROUND + * \e[96m BRIGHT CYAN FOREGROUND + * \e[97m BRIGHT WHITE FOREGROUND + * \e[100m BRIGHT BLACK BACKGROUND + * \e[101m BRIGHT RED BACKGROUND + * \e[102m BRIGHT GREEN BACKGROUND + * \e[103m BRIGHT YELLOW BACKGROUND + * \e[104m BRIGHT BLUE BACKGROUND + * \e[105m BRIGHT MAGENTA BACKGROUND + * \e[106m BRIGHT CYAN BACKGROUND + * \e[107m BRIGHT WHITE BACKGROUND + * \e[38;5;𝑥m XTERM256 FOREGROUND + * \e[48;5;𝑥m XTERM256 BACKGROUND + * \e[38;2;𝑟;𝑔;𝑏m RGB FOREGROUND + * \e[48;2;𝑟;𝑔;𝑏m RGB BACKGROUND + * \e[?25h SHOW CURSOR + * \e[?25l HIDE CURSOR + * \e[s SAVE CURSOR POSITION + * \e[u RESTORE CURSOR POSITION + * \e[0q RESET LEDS + * \e[1q TURN ON FIRST LED + * \e[2q TURN ON SECOND LED + * \e[3q TURN ON THIRD LED + * \e[4q TURN ON FOURTH LED + * \e[c → \e[?1;0c REPORT DEVICE TYPE + * \e[5n → \e[0n REPORT DEVICE STATUS + * \e[6n → \e[𝑦;𝑥R REPORT CURSOR POSITION + * \e7\e[9979;9979H\e[6n\e8 → \e[𝑦;𝑥R REPORT DISPLAY DIMENSIONS + * + * @see https://vt100.net/docs/vt220-rm/chapter4.html + * @see https://invisible-island.net/xterm/ + * @see ANSI X3.64-1979 + * @see ISO/IEC 6429 + * @see FIPS-86 + * @see ECMA-48 + */ + struct MachinePty *MachinePtyNew(void) { struct MachinePty *pty; pty = xcalloc(1, sizeof(struct MachinePty)); MachinePtyResize(pty, 25, 80); + MachinePtyFullReset(pty); + MachinePtyErase(pty, 0, pty->yn * pty->xn); return pty; } +static void MachinePtyFreePlanes(struct MachinePty *pty) { + free(pty->wcs); + free(pty->fgs); + free(pty->bgs); + free(pty->prs); +} + +void MachinePtyFree(struct MachinePty *pty) { + if (pty) { + MachinePtyFreePlanes(pty); + free(pty); + } +} + +static wchar_t *MachinePtyGetXlatAscii(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < 128; ++i) { + xlat[i] = i; + } + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatLineDrawing(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < 128; ++i) { + if (0x5F <= i && i <= 0x7E) { + xlat[i] = u" ◆▒␉␌␍␊°±␤␋┘┐┌└┼⎺⎻─⎼⎽├┤┴┬│≤≥π≠£·"[i - 0x5F]; + } else { + xlat[i] = i; + } + } + once = true; + } + return xlat; +} + +static void MachinePtyXlatAlphabet(wchar_t xlat[128], int a, int b) { + unsigned i; + for (i = 0; i < 128; ++i) { + if ('a' <= i && i <= 'z') { + xlat[i] = i - 'a' + a; + } else if ('A' <= i && i <= 'Z') { + xlat[i] = i - 'A' + b; + } else { + xlat[i] = i; + } + } +} + +static wchar_t *MachinePtyGetXlatItalic(void) { + static bool once; + static wchar_t xlat[128]; + if (!once) { + MachinePtyXlatAlphabet(xlat, L'𝑎', L'𝐴'); + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatBoldItalic(void) { + static bool once; + static wchar_t xlat[128]; + if (!once) { + MachinePtyXlatAlphabet(xlat, L'𝒂', L'𝑨'); + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatBoldFraktur(void) { + static bool once; + static wchar_t xlat[128]; + if (!once) { + MachinePtyXlatAlphabet(xlat, L'𝖆', L'𝕬'); + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatFraktur(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < ARRAYLEN(xlat); ++i) { + if ('A' <= i && i <= 'Z') { + xlat[i] = L"𝔄𝔅ℭ𝔇𝔈𝔉𝔊ℌℑ𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔ℜ𝔖𝔗𝔘𝔙𝔚𝔛𝔜ℨ"[i - 'A']; + } else if ('a' <= i && i <= 'z') { + xlat[i] = i - 'a' + L'𝔞'; + } else { + xlat[i] = i; + } + } + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatDoubleWidth(void) { + unsigned i; + static bool once; + static wchar_t xlat[128]; + if (!once) { + for (i = 0; i < ARRAYLEN(xlat); ++i) { + if ('!' <= i && i <= '~') { + xlat[i] = -(i - '!' + L'!'); + } else { + xlat[i] = i; + } + } + once = true; + } + return xlat; +} + +static wchar_t *MachinePtyGetXlatSgr(struct MachinePty *pty) { + switch (!!(pty->pr & kMachinePtyFraktur) << 2 | + !!(pty->pr & kMachinePtyItalic) << 1 | + !!(pty->pr & kMachinePtyBold) << 0) { + case 0b100: + case 0b110: + return MachinePtyGetXlatFraktur(); + case 0b101: + case 0b111: + return MachinePtyGetXlatBoldFraktur(); + case 0b011: + return MachinePtyGetXlatBoldItalic(); + case 0b010: + return MachinePtyGetXlatItalic(); + default: + return MachinePtyGetXlatAscii(); + } +} + +static void MachinePtySetXlat(struct MachinePty *pty, wchar_t *xlat) { + pty->xlat = xlat; + pty->pr &= ~(kMachinePtyItalic | kMachinePtyFraktur); +} + +static void MachinePtySetCodepage(struct MachinePty *pty, char id) { + unsigned i; + switch (id) { + default: + case 'B': + MachinePtySetXlat(pty, MachinePtyGetXlatAscii()); + break; + case '0': + MachinePtySetXlat(pty, MachinePtyGetXlatLineDrawing()); + break; + } +} + +void MachinePtyErase(struct MachinePty *pty, long dst, long n) { + DCHECK_LE(dst + n, pty->yn * pty->xn); + wmemset((void *)(pty->wcs + dst), ' ', n); + wmemset((void *)(pty->prs + dst), 0, n); +} + +void MachinePtyMemmove(struct MachinePty *pty, long dst, long src, long n) { + DCHECK_LE(src + n, pty->yn * pty->xn); + DCHECK_LE(dst + n, pty->yn * pty->xn); + memmove(pty->wcs + dst, pty->wcs + src, n * 4); + memmove(pty->fgs + dst, pty->fgs + src, n * 4); + memmove(pty->bgs + dst, pty->bgs + src, n * 4); + memmove(pty->prs + dst, pty->prs + src, n * 4); +} + +void MachinePtyFullReset(struct MachinePty *pty) { + pty->y = 0; + pty->x = 0; + pty->pr = 0; + pty->u8 = 0; + pty->n8 = 0; + pty->conf = 0; + pty->save = 0; + pty->state = 0; + pty->esc.i = 0; + pty->input.i = 0; + pty->xlat = MachinePtyGetXlatAscii(); + MachinePtyErase(pty, 0, pty->yn * pty->xn); +} + +void MachinePtySetY(struct MachinePty *pty, int y) { + pty->conf &= ~kMachinePtyRedzone; + pty->y = MAX(0, MIN(pty->yn - 1, y)); +} + +void MachinePtySetX(struct MachinePty *pty, int x) { + pty->conf &= ~kMachinePtyRedzone; + pty->x = MAX(0, MIN(pty->xn - 1, x)); +} + void MachinePtyResize(struct MachinePty *pty, int yn, int xn) { unsigned y, ym, xm, y0; uint32_t *wcs, *fgs, *bgs, *prs; @@ -59,119 +387,204 @@ void MachinePtyResize(struct MachinePty *pty, int yn, int xn) { memcpy(bgs + y * xn, pty->bgs + y * pty->xn, xm * 4); memcpy(prs + y * xn, pty->prs + y * pty->xn, xm * 4); } - free(pty->wcs); - free(pty->fgs); - free(pty->bgs); - free(pty->prs); + MachinePtyFreePlanes(pty); pty->wcs = wcs; pty->fgs = fgs; pty->bgs = bgs; pty->prs = prs; - pty->y = MIN(pty->y, yn - 1); - pty->x = MIN(pty->x, xn - 1); pty->yn = yn; pty->xn = xn; + MachinePtySetY(pty, pty->y); + MachinePtySetX(pty, pty->x); } -void MachinePtyFree(struct MachinePty *pty) { - if (pty) { - free(pty->wcs); - free(pty->fgs); - free(pty->bgs); - free(pty->prs); - free(pty); - } -} - -static void MachinePtyScrollPlane(struct MachinePty *pty, uint32_t *p) { - memcpy(p, p + pty->xn, sizeof(p[0]) * pty->xn * (pty->yn - 1)); - memset(p + pty->xn * (pty->yn - 1), 0, sizeof(p[0]) * pty->xn); +static void MachinePtyConcatInput(struct MachinePty *pty, const char *data, + size_t n) { + CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, data, n); } static void MachinePtyScroll(struct MachinePty *pty) { - MachinePtyScrollPlane(pty, pty->wcs); - MachinePtyScrollPlane(pty, pty->fgs); - MachinePtyScrollPlane(pty, pty->bgs); - MachinePtyScrollPlane(pty, pty->prs); + MachinePtyMemmove(pty, 0, pty->xn, pty->xn * (pty->yn - 1)); + MachinePtyErase(pty, pty->xn * (pty->yn - 1), pty->xn); +} + +static void MachinePtyReverse(struct MachinePty *pty) { + MachinePtyMemmove(pty, pty->xn, 0, pty->xn * (pty->yn - 1)); + MachinePtyErase(pty, 0, pty->xn); +} + +static void MachinePtyIndex(struct MachinePty *pty) { + if (pty->y < pty->yn - 1) { + ++pty->y; + } else { + MachinePtyScroll(pty); + } +} + +static void MachinePtyReverseIndex(struct MachinePty *pty) { + if (pty->y) { + --pty->y; + } else { + MachinePtyReverse(pty); + } +} + +static void MachinePtyCarriageReturn(struct MachinePty *pty) { + MachinePtySetX(pty, 0); } static void MachinePtyNewline(struct MachinePty *pty) { - pty->x = 0; - if (++pty->y == pty->yn) { - --pty->y; - if (!(pty->conf & kMachinePtyNoopost)) { - MachinePtyScroll(pty); - } + MachinePtyIndex(pty); + if (!(pty->conf & kMachinePtyNoopost)) { + MachinePtyCarriageReturn(pty); } } -static void SetMachinePtyCell(struct MachinePty *pty, wint_t wc) { - uint32_t w, i; - if ((w = MAX(0, wcwidth(wc))) > 0) { - i = pty->y * pty->xn + pty->x; - pty->wcs[i] = wc; +static void MachinePtyAdvance(struct MachinePty *pty) { + DCHECK_EQ(pty->xn - 1, pty->x); + DCHECK(pty->conf & kMachinePtyRedzone); + pty->conf &= ~kMachinePtyRedzone; + pty->x = 0; + if (pty->y < pty->yn - 1) { + ++pty->y; + } else { + MachinePtyScroll(pty); + } +} + +static void MachinePtyWriteGlyph(struct MachinePty *pty, wint_t wc, int w) { + uint32_t i; + if (w < 1) wc = ' ', w = 1; + if ((pty->conf & kMachinePtyRedzone) || pty->x + w > pty->xn) { + MachinePtyAdvance(pty); + } + i = pty->y * pty->xn + pty->x; + pty->wcs[i] = wc; + if ((pty->prs[i] = pty->pr) & (kMachinePtyFg | kMachinePtyBg)) { pty->fgs[i] = pty->fg; pty->bgs[i] = pty->bg; - pty->prs[i] = pty->pr; - if ((pty->x += w) >= pty->xn) { - pty->x = 0; - MachinePtyNewline(pty); - } + } + if ((pty->x += w) >= pty->xn) { + pty->x = pty->xn - 1; + pty->conf |= kMachinePtyRedzone; } } -static void MachinePtyTab(struct MachinePty *pty) { +static void MachinePtyWriteTab(struct MachinePty *pty) { unsigned x, x2; - x2 = pty->x + ROUNDUP(pty->x + 1, 8); - if (x2 >= pty->xn) x2 = pty->xn - 1; + if (pty->conf & kMachinePtyRedzone) { + MachinePtyAdvance(pty); + } + x2 = MIN(pty->xn, ROUNDUP(pty->x + 1, 8)); for (x = pty->x; x < x2; ++x) { - pty->wcs[pty->y * pty->xn + x] = 0; + pty->wcs[pty->y * pty->xn + x] = ' '; } - pty->x = x2; -} - -static void MachinePtyCursorSet(struct MachinePty *pty, int y, int x) { - pty->y = MAX(0, MIN(pty->yn - 1, y)); - pty->x = MAX(0, MIN(pty->xn - 1, x)); -} - -static void MachinePtyCursorMove(struct MachinePty *pty, int dy, int dx) { - int n; - if (pty->esc.i > 1) { - n = atoi(pty->esc.s); - dy *= n; - dx *= n; + if (x2 < pty->xn) { + pty->x = x2; + } else { + pty->x = pty->xn - 1; + pty->conf |= kMachinePtyRedzone; } - MachinePtyCursorSet(pty, pty->y + dy, pty->x + dx); } -static void MachinePtyCursorPosition(struct MachinePty *pty) { +static int MachinePtyAtoi(const char *s, const char **e) { + int i; + for (i = 0; isdigit(*s); ++s) i *= 10, i += *s - '0'; + if (e) *e = s; + return i; +} + +static int MachinePtyGetMoveParam(struct MachinePty *pty) { + int x = MachinePtyAtoi(pty->esc.s, NULL); + if (x < 1) x = 1; + return x; +} + +static void MachinePtySetCursorPosition(struct MachinePty *pty) { int row, col; - row = MAX(1, atoi(pty->esc.s)); - col = MAX(1, atoi((char *)firstnonnull(strchr(pty->esc.s, ';'), "x") + 1)); - MachinePtyCursorSet(pty, row - 1, col - 1); + const char *s = pty->esc.s; + row = max(1, MachinePtyAtoi(s, &s)); + if (*s == ';') ++s; + col = max(1, MachinePtyAtoi(s, &s)); + MachinePtySetY(pty, row - 1); + MachinePtySetX(pty, col - 1); } -static void MachinePtyEraseRange(struct MachinePty *pty, size_t i, size_t j) { - size_t n; - n = (j - i) * sizeof(uint32_t); - memset(pty->wcs + i, 0, n); - memset(pty->fgs + i, 0, n); - memset(pty->bgs + i, 0, n); - memset(pty->prs + i, 0, n); +static void MachinePtySetCursorRow(struct MachinePty *pty) { + MachinePtySetY(pty, MachinePtyGetMoveParam(pty) - 1); +} + +static void MachinePtySetCursorColumn(struct MachinePty *pty) { + MachinePtySetX(pty, MachinePtyGetMoveParam(pty) - 1); +} + +static void MachinePtyMoveCursor(struct MachinePty *pty, int dy, int dx) { + int n = MachinePtyGetMoveParam(pty); + MachinePtySetY(pty, pty->y + dy * n); + MachinePtySetX(pty, pty->x + dx * n); +} + +static void MachinePtyScrollUp(struct MachinePty *pty) { + int n = MachinePtyGetMoveParam(pty); + while (n--) MachinePtyScroll(pty); +} + +static void MachinePtyScrollDown(struct MachinePty *pty) { + int n = MachinePtyGetMoveParam(pty); + while (n--) MachinePtyReverse(pty); +} + +static void MachinePtySetCursorStatus(struct MachinePty *pty, bool status) { + if (status) { + pty->conf &= ~kMachinePtyNocursor; + } else { + pty->conf |= kMachinePtyNocursor; + } +} + +static void MachinePtySetMode(struct MachinePty *pty, bool status) { + const char *p = pty->esc.s; + switch (*p++) { + case '?': + while (isdigit(*p)) { + switch (MachinePtyAtoi(p, &p)) { + case 25: + MachinePtySetCursorStatus(pty, status); + break; + default: + break; + } + if (*p == ';') { + ++p; + } + } + break; + default: + break; + } +} + +static void MachinePtySaveCursorPosition(struct MachinePty *pty) { + pty->save = (pty->y & 0x7FFF) | (pty->x & 0x7FFF) << 16; +} + +static void MachinePtyRestoreCursorPosition(struct MachinePty *pty) { + MachinePtySetY(pty, (pty->save & 0x00007FFF) >> 000); + MachinePtySetX(pty, (pty->save & 0x7FFF0000) >> 020); } static void MachinePtyEraseDisplay(struct MachinePty *pty) { - switch (atoi(pty->esc.s)) { - case 3: - case 2: - pty->y = 0; - pty->x = 0; + switch (MachinePtyAtoi(pty->esc.s, NULL)) { case 0: - MachinePtyEraseRange(pty, pty->y * pty->xn + pty->x, pty->yn * pty->xn); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, + pty->yn * pty->xn - (pty->y * pty->xn + pty->x)); break; case 1: - MachinePtyEraseRange(pty, 0, pty->y * pty->xn + pty->x); + MachinePtyErase(pty, 0, pty->y * pty->xn + pty->x); + break; + case 2: + case 3: + MachinePtyErase(pty, 0, pty->yn * pty->xn); break; default: break; @@ -179,17 +592,122 @@ static void MachinePtyEraseDisplay(struct MachinePty *pty) { } static void MachinePtyEraseLine(struct MachinePty *pty) { - switch (atoi(pty->esc.s)) { + switch (MachinePtyAtoi(pty->esc.s, NULL)) { case 0: - MachinePtyEraseRange(pty, pty->y * pty->xn + pty->x, - pty->y * pty->xn + pty->xn); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, pty->xn - pty->x); break; case 1: - MachinePtyEraseRange(pty, pty->y * pty->xn + pty->xn, - pty->y * pty->xn + pty->x); + MachinePtyErase(pty, pty->y * pty->xn, pty->x); break; case 2: - MachinePtyEraseRange(pty, pty->y * pty->xn, pty->y * pty->xn + pty->xn); + MachinePtyErase(pty, pty->y * pty->xn, pty->xn); + break; + default: + break; + } +} + +static void MachinePtyEraseCells(struct MachinePty *pty) { + int i, n, x; + i = pty->y * pty->xn + pty->x; + n = pty->yn * pty->xn; + x = min(max(MachinePtyAtoi(pty->esc.s, NULL), 1), n - i); + MachinePtyErase(pty, i, x); +} + +static int MachinePtyArg1(struct MachinePty *pty) { + return max(1, MachinePtyAtoi(pty->esc.s, NULL)); +} + +static void MachinePtyInsertCells(struct MachinePty *pty) { + int n = min(pty->xn - pty->x, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, pty->y * pty->xn + pty->x + n, + pty->y * pty->xn + pty->x, pty->xn - (pty->x + n)); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, n); +} + +static void MachinePtyInsertLines(struct MachinePty *pty) { + int n = min(pty->yn - pty->y, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, (pty->y + n) * pty->xn, pty->y * pty->xn, + (pty->yn - pty->y - n) * pty->xn); + MachinePtyErase(pty, pty->y * pty->xn, n * pty->xn); +} + +static void MachinePtyDeleteCells(struct MachinePty *pty) { + int n = min(pty->xn - pty->x, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, pty->y * pty->xn + pty->x, + pty->y * pty->xn + pty->x + n, pty->xn - (pty->x + n)); + MachinePtyErase(pty, pty->y * pty->xn + pty->x, n); +} + +static void MachinePtyDeleteLines(struct MachinePty *pty) { + int n = min(pty->yn - pty->y, MachinePtyArg1(pty)); + MachinePtyMemmove(pty, pty->y * pty->xn, (pty->y + n) * pty->xn, + (pty->yn - pty->y - n) * pty->xn); + MachinePtyErase(pty, (pty->y + n) * pty->xn, n * pty->xn); +} + +static void MachinePtyReportDeviceStatus(struct MachinePty *pty) { + MachinePtyWriteInput(pty, "\e[0n", 4); +} + +static void MachinePtyReportPreferredVtType(struct MachinePty *pty) { + MachinePtyWriteInput(pty, "\e[?1;0c", 4); +} + +static void MachinePtyReportPreferredVtIdentity(struct MachinePty *pty) { + MachinePtyWriteInput(pty, "\e/Z", 4); +} + +static void MachinePtyBell(struct MachinePty *pty) { + pty->conf |= kMachinePtyBell; +} + +static void MachinePtyLed(struct MachinePty *pty) { + switch (MachinePtyAtoi(pty->esc.s, NULL)) { + case 0: + pty->conf &= ~kMachinePtyLed1; + pty->conf &= ~kMachinePtyLed2; + pty->conf &= ~kMachinePtyLed3; + pty->conf &= ~kMachinePtyLed4; + break; + case 1: + pty->conf |= kMachinePtyLed1; + break; + case 2: + pty->conf |= kMachinePtyLed2; + break; + case 3: + pty->conf |= kMachinePtyLed3; + break; + case 4: + pty->conf |= kMachinePtyLed4; + break; + default: + break; + } +} + +static void MachinePtyReportCursorPosition(struct MachinePty *pty) { + char *p; + char buf[2 + 10 + 1 + 10 + 1]; + p = buf; + *p++ = '\e'; + *p++ = '['; + p += uint64toarray_radix10((pty->y + 1) & 0x7fff, p); + *p++ = ';'; + p += uint64toarray_radix10((pty->x + 1) & 0x7fff, p); + *p++ = 'R'; + MachinePtyWriteInput(pty, buf, p - buf); +} + +static void MachinePtyCsiN(struct MachinePty *pty) { + switch (MachinePtyAtoi(pty->esc.s, NULL)) { + case 5: + MachinePtyReportDeviceStatus(pty); + break; + case 6: + MachinePtyReportCursorPosition(pty); break; default: break; @@ -229,57 +747,96 @@ static void MachinePtySelectGraphicsRendition(struct MachinePty *pty) { switch (t) { case kSgr: switch (code[0]) { + case 38: + t = kSgrFg; + break; + case 48: + t = kSgrBg; + break; case 0: - pty->fg = 0; - pty->bg = 0; pty->pr = 0; + pty->xlat = MachinePtyGetXlatSgr(pty); break; case 1: pty->pr |= kMachinePtyBold; - break; - case 21: - pty->pr &= ~kMachinePtyBold; + pty->xlat = MachinePtyGetXlatSgr(pty); break; case 2: pty->pr |= kMachinePtyFaint; break; - case 22: - pty->pr &= ~kMachinePtyFaint; + case 3: + pty->pr |= kMachinePtyItalic; + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 4: + pty->pr |= kMachinePtyUnder; + break; + case 5: + pty->pr |= kMachinePtyBlink; break; case 7: pty->pr |= kMachinePtyFlip; break; + case 8: + pty->pr |= kMachinePtyConceal; + break; + case 9: + pty->pr |= kMachinePtyStrike; + break; + case 20: + pty->pr |= kMachinePtyFraktur; + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 21: + pty->pr |= kMachinePtyUnder | kMachinePtyDunder; + break; + case 22: + pty->pr &= ~(kMachinePtyFaint | kMachinePtyBold); + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 23: + pty->pr &= ~kMachinePtyItalic; + pty->xlat = MachinePtyGetXlatSgr(pty); + break; + case 24: + pty->pr &= ~(kMachinePtyUnder | kMachinePtyDunder); + break; + case 25: + pty->pr &= ~kMachinePtyBlink; + break; case 27: pty->pr &= ~kMachinePtyFlip; break; + case 28: + pty->pr &= ~kMachinePtyConceal; + break; + case 29: + pty->pr &= ~kMachinePtyStrike; + break; + case 39: + pty->pr &= ~kMachinePtyFg; + break; + case 49: + pty->pr &= ~kMachinePtyBg; + break; case 90 ... 97: code[0] -= 90 - 30; code[0] += 8; + /* fallthrough */ case 30 ... 37: pty->fg = code[0] - 30; pty->pr |= kMachinePtyFg; pty->pr &= ~kMachinePtyTrue; break; - case 38: - t = kSgrFg; - break; - case 39: - pty->pr &= ~kMachinePtyFg; - break; case 100 ... 107: code[0] -= 100 - 40; code[0] += 8; + /* fallthrough */ case 40 ... 47: pty->bg = code[0] - 40; pty->pr |= kMachinePtyBg; pty->pr &= ~kMachinePtyTrue; break; - case 48: - t = kSgrBg; - break; - case 49: - pty->pr &= ~kMachinePtyBg; - break; default: break; } @@ -336,63 +893,53 @@ static void MachinePtySelectGraphicsRendition(struct MachinePty *pty) { } } -static void MachinePtyHideCursor(struct MachinePty *pty) { - pty->conf |= kMachinePtyNocursor; -} - -static void MachinePtyShowCursor(struct MachinePty *pty) { - pty->conf &= ~kMachinePtyNocursor; -} - -static void MachinePtyReportCursorPosition(struct MachinePty *pty) { - char *p; - char buf[2 + 10 + 1 + 10 + 1]; - p = buf; - *p++ = '\e'; - *p++ = '['; - p += uint64toarray_radix10((pty->y + 1) & 0xffffffff, p); - *p++ = ';'; - p += uint64toarray_radix10((pty->x + 1) & 0xffffffff, p); - *p++ = 'R'; - CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, buf, p - buf); -} - -static void MachinePtyCsiN(struct MachinePty *pty) { - switch (atoi(pty->esc.s)) { - case 6: - MachinePtyReportCursorPosition(pty); - break; - default: - break; - } -} - -static void MachinePtyCsiScrollUp(struct MachinePty *pty) { - int n; - n = atoi(pty->esc.s); - n = MAX(1, n); - while (n--) { - MachinePtyScroll(pty); - } -} - static void MachinePtyCsi(struct MachinePty *pty) { switch (pty->esc.s[pty->esc.i - 1]) { - case 'A': - MachinePtyCursorMove(pty, -1, +0); - break; - case 'B': - MachinePtyCursorMove(pty, +1, +0); - break; - case 'C': - MachinePtyCursorMove(pty, +0, +1); - break; - case 'D': - MachinePtyCursorMove(pty, +0, -1); - break; case 'f': case 'H': - MachinePtyCursorPosition(pty); + MachinePtySetCursorPosition(pty); + break; + case 'G': + MachinePtySetCursorColumn(pty); + break; + case 'd': + MachinePtySetCursorRow(pty); + break; + case 'F': + pty->x = 0; + /* fallthrough */ + case 'A': + MachinePtyMoveCursor(pty, -1, +0); + break; + case 'E': + pty->x = 0; + /* fallthrough */ + case 'B': + MachinePtyMoveCursor(pty, +1, +0); + break; + case 'C': + MachinePtyMoveCursor(pty, +0, +1); + break; + case 'D': + MachinePtyMoveCursor(pty, +0, -1); + break; + case 'S': + MachinePtyScrollUp(pty); + break; + case 'T': + MachinePtyScrollDown(pty); + break; + case '@': + MachinePtyInsertCells(pty); + break; + case 'P': + MachinePtyDeleteCells(pty); + break; + case 'L': + MachinePtyInsertLines(pty); + break; + case 'M': + MachinePtyDeleteLines(pty); break; case 'J': MachinePtyEraseDisplay(pty); @@ -400,24 +947,52 @@ static void MachinePtyCsi(struct MachinePty *pty) { case 'K': MachinePtyEraseLine(pty); break; - case 'm': - MachinePtySelectGraphicsRendition(pty); + case 'X': + MachinePtyEraseCells(pty); + break; + case 's': + MachinePtySaveCursorPosition(pty); + break; + case 'u': + MachinePtyRestoreCursorPosition(pty); break; case 'n': MachinePtyCsiN(pty); break; - case 'S': - MachinePtyCsiScrollUp(pty); - break; - case 'l': - if (strcmp(pty->esc.s, "?25l") == 0) { - MachinePtyHideCursor(pty); - } + case 'm': + MachinePtySelectGraphicsRendition(pty); break; case 'h': - if (strcmp(pty->esc.s, "?25h") == 0) { - MachinePtyShowCursor(pty); - } + MachinePtySetMode(pty, true); + break; + case 'l': + MachinePtySetMode(pty, false); + break; + case 'c': + MachinePtyReportPreferredVtType(pty); + break; + case 'q': + MachinePtyLed(pty); + break; + default: + break; + } +} + +static void MachinePtyScreenAlignmentDisplay(struct MachinePty *pty) { + wmemset((void *)pty->wcs, 'E', pty->yn * pty->xn); +} + +static void MachinePtyEscHash(struct MachinePty *pty) { + switch (pty->esc.s[1]) { + case '5': + MachinePtySetXlat(pty, MachinePtyGetXlatAscii()); + break; + case '6': + MachinePtySetXlat(pty, MachinePtyGetXlatDoubleWidth()); + break; + case '8': + MachinePtyScreenAlignmentDisplay(pty); break; default: break; @@ -425,46 +1000,99 @@ static void MachinePtyCsi(struct MachinePty *pty) { } static void MachinePtyEsc(struct MachinePty *pty) { + switch (pty->esc.s[0]) { + case 'c': + MachinePtyFullReset(pty); + break; + case '7': + MachinePtySaveCursorPosition(pty); + break; + case '8': + MachinePtyRestoreCursorPosition(pty); + break; + case 'E': + pty->x = 0; + case 'D': + MachinePtyIndex(pty); + break; + case 'M': + MachinePtyReverseIndex(pty); + break; + case 'Z': + MachinePtyReportPreferredVtIdentity(pty); + break; + case '(': + MachinePtySetCodepage(pty, pty->esc.s[1]); + break; + case '#': + MachinePtyEscHash(pty); + break; + default: + break; + } +} + +static void MachinePtyCntrl(struct MachinePty *pty, int c01) { + switch (c01) { + case '\a': + MachinePtyBell(pty); + break; + case 0x85: + case '\f': + case '\v': + case '\n': + MachinePtyNewline(pty); + break; + case '\r': + MachinePtyCarriageReturn(pty); + break; + case '\e': + pty->state = kMachinePtyEsc; + pty->esc.i = 0; + break; + case '\t': + MachinePtyWriteTab(pty); + break; + case 0x7F: + case '\b': + pty->x = MAX(0, pty->x - 1); + break; + case 0x84: + MachinePtyIndex(pty); + break; + case 0x8D: + MachinePtyReverseIndex(pty); + break; + case 0x9B: + pty->state = kMachinePtyCsi; + break; + default: + break; + } } static void MachinePtyEscAppend(struct MachinePty *pty, char c) { pty->esc.i = MIN(pty->esc.i + 1, ARRAYLEN(pty->esc.s) - 1); pty->esc.s[pty->esc.i - 1] = c; - pty->esc.s[pty->esc.i - 0] = '\0'; + pty->esc.s[pty->esc.i - 0] = 0; } ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { int i; + wchar_t wc; const uint8_t *p; for (p = data, i = 0; i < n; ++i) { switch (pty->state) { case kMachinePtyAscii: - if (p[i] < 0b10000000) { - switch (p[i]) { - case '\e': - pty->state = kMachinePtyEsc; - pty->esc.i = 0; - break; - case '\t': - MachinePtyTab(pty); - break; - case '\r': - pty->x = 0; - break; - case '\n': - MachinePtyNewline(pty); - break; - case 0177: - case '\b': - pty->x = MAX(0, pty->x - 1); - break; - case '\f': - break; - case '\a': - break; - default: - SetMachinePtyCell(pty, p[i]); - break; + if (0x00 <= p[i] && p[i] <= 0x7F) { + if (0x20 <= p[i] && p[i] <= 0x7E) { + if ((wc = pty->xlat[p[i]]) >= 0) { + MachinePtyWriteGlyph(pty, wc, 1); + } else { + MachinePtyWriteGlyph(pty, -wc, 2); + } + } else { + MachinePtyCntrl(pty, p[i]); } } else if (!ThomPikeCont(p[i])) { pty->state = kMachinePtyUtf8; @@ -477,7 +1105,12 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { pty->u8 = ThomPikeMerge(pty->u8, p[i]); if (--pty->n8) break; } - SetMachinePtyCell(pty, pty->u8); + wc = pty->u8; + if ((0x00 <= wc && wc <= 0x1F) || (0x7F <= wc && wc <= 0x9F)) { + MachinePtyCntrl(pty, wc); + } else { + MachinePtyWriteGlyph(pty, wc, wcwidth(wc)); + } pty->state = kMachinePtyAscii; pty->u8 = 0; --i; @@ -528,7 +1161,7 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { } break; default: - abort(); + unreachable; } } return n; @@ -536,8 +1169,7 @@ ssize_t MachinePtyWrite(struct MachinePty *pty, const void *data, size_t n) { ssize_t MachinePtyWriteInput(struct MachinePty *pty, const void *data, size_t n) { - const char *p = data; - CONCAT(&pty->input.p, &pty->input.i, &pty->input.n, data, n); + MachinePtyConcatInput(pty, data, n); if (!(pty->conf & kMachinePtyNoecho)) { MachinePtyWrite(pty, data, n); } @@ -561,65 +1193,187 @@ ssize_t MachinePtyRead(struct MachinePty *pty, void *buf, size_t size) { return n; } -void MachinePtyAppendLine(struct MachinePty *pty, struct Buffer *buf, - unsigned y) { - bool atcursor; - uint32_t x, i, fg, bg, pr, wc, w; - if (y >= pty->yn) return; - for (fg = bg = pr = x = 0; x < pty->xn; x += w) { - i = y * pty->xn + x; - wc = pty->wcs[i]; - w = MAX(0, wcwidth(wc)); - atcursor = y == pty->y && x == pty->x; - if ((w && atcursor) || - (pty->prs[i] != pr || pty->fgs[i] != fg || pty->bgs[i] != bg)) { - fg = pty->fgs[i]; - bg = pty->bgs[i]; - pr = pty->prs[i]; - AppendStr(buf, "\e[0"); - if (w && atcursor) { - pr ^= kMachinePtyFlip; +static char *MachinePtyEncodeRgb(char *p, int rgb) { + *p++ = '2'; + *p++ = ';'; + p += uint64toarray_radix10((rgb & 0x0000ff) >> 000, p); + *p++ = ';'; + p += uint64toarray_radix10((rgb & 0x00ff00) >> 010, p); + *p++ = ';'; + p += uint64toarray_radix10((rgb & 0xff0000) >> 020, p); + return p; +} + +static char *MachinePtyEncodeXterm256(char *p, int xt) { + *p++ = '5'; + *p++ = ';'; + p += uint64toarray_radix10(xt, p); + return p; +} + +char *MachinePtyEncodeStyle(char *p, uint32_t xr, uint32_t pr, uint32_t fg, + uint32_t bg) { + *p++ = '\e'; + *p++ = '['; + if (pr & (kMachinePtyBold | kMachinePtyFaint | kMachinePtyFlip | + kMachinePtyUnder | kMachinePtyDunder | kMachinePtyBlink | + kMachinePtyStrike | kMachinePtyFg | kMachinePtyBg)) { + if (xr & (kMachinePtyBold | kMachinePtyFaint)) { + if ((xr & (kMachinePtyBold | kMachinePtyFaint)) ^ + (pr & (kMachinePtyBold | kMachinePtyFaint))) { + *p++ = '2'; + *p++ = '2'; + *p++ = ';'; } - if (pr & kMachinePtyBold) AppendStr(buf, ";1"); - if (pr & kMachinePtyFaint) AppendStr(buf, ";2"); - if (pr & kMachinePtyBlink) AppendStr(buf, ";5"); - if (pr & kMachinePtyFlip) AppendStr(buf, ";7"); - if (pr & kMachinePtyFg) { - if (pr & kMachinePtyTrue) { - AppendFmt(buf, ";38;2;%d;%d;%d", (fg & 0x0000ff) >> 000, - (fg & 0x00ff00) >> 010, (fg & 0xff0000) >> 020); - } else { - AppendFmt(buf, ";38;5;%d", fg); - } + if (pr & kMachinePtyBold) { + *p++ = '1'; + *p++ = ';'; } - if (pr & kMachinePtyBg) { - if (pr & kMachinePtyTrue) { - AppendFmt(buf, ";48;2;%d;%d;%d", (bg & 0x0000ff) >> 000, - (bg & 0x00ff00) >> 010, (bg & 0xff0000) >> 020); - } else { - AppendFmt(buf, ";48;5;%d", bg); - } + if (pr & kMachinePtyFaint) { + *p++ = '2'; + *p++ = ';'; } - AppendStr(buf, "m"); } - if (w) { - AppendWide(buf, wc); - } else { - w = 1; - if (atcursor) { - if (!(pty->conf & kMachinePtyNocursor)) { - if (pty->conf & kMachinePtyBlinkcursor) { - AppendStr(buf, "\e[5m"); - } - AppendWide(buf, u'▂'); - if (pty->conf & kMachinePtyBlinkcursor) { - AppendStr(buf, "\e[25m"); - } + if (xr & (kMachinePtyUnder | kMachinePtyDunder)) { + if ((xr & (kMachinePtyUnder | kMachinePtyDunder)) ^ + (pr & (kMachinePtyUnder | kMachinePtyDunder))) { + *p++ = '2'; + *p++ = '4'; + *p++ = ';'; + } + if (pr & kMachinePtyUnder) { + *p++ = '4'; + *p++ = ';'; + } + if (pr & kMachinePtyDunder) { + *p++ = '2'; + *p++ = '1'; + *p++ = ';'; + } + } + if (xr & (kMachinePtyFlip | kMachinePtyBlink | kMachinePtyStrike)) { + if (xr & kMachinePtyFlip) { + if (!(pr & kMachinePtyFlip)) *p++ = '2'; + *p++ = '7'; + *p++ = ';'; + } + if (xr & kMachinePtyBlink) { + if (!(pr & kMachinePtyBlink)) *p++ = '2'; + *p++ = '5'; + *p++ = ';'; + } + if (xr & kMachinePtyStrike) { + if (!(pr & kMachinePtyStrike)) *p++ = '2'; + *p++ = '9'; + *p++ = ';'; + } + } + if (xr & (kMachinePtyFg | kMachinePtyTrue)) { + *p++ = '3'; + if (pr & kMachinePtyFg) { + *p++ = '8'; + *p++ = ';'; + if (pr & kMachinePtyTrue) { + p = MachinePtyEncodeRgb(p, fg); + } else { + p = MachinePtyEncodeXterm256(p, fg); } } else { - AppendChar(buf, ' '); + *p++ = '9'; + } + *p++ = ';'; + } + if (xr & (kMachinePtyBg | kMachinePtyTrue)) { + *p++ = '4'; + if (pr & kMachinePtyBg) { + *p++ = '8'; + *p++ = ';'; + if (pr & kMachinePtyTrue) { + p = MachinePtyEncodeRgb(p, bg); + } else { + p = MachinePtyEncodeXterm256(p, bg); + } + } else { + *p++ = '9'; + } + *p++ = ';'; + } + DCHECK_EQ(';', p[-1]); + p[-1] = 'm'; + } else { + *p++ = '0'; + *p++ = 'm'; + } + return p; +} + +void MachinePtyAppendLine(struct MachinePty *pty, struct Buffer *buf, + unsigned y) { + uint64_t u; + size_t need; + char *p, *pb; + uint32_t i, j, w, wc, np, xp, pr, fg, bg, ci; + if (y >= pty->yn) return; + need = buf->i + pty->xn * 60; + if (need > buf->n) { + CHECK_NOTNULL((buf->p = realloc(buf->p, need))); + buf->n = need; + } + i = y * pty->xn; + j = (y + 1) * pty->xn; + pb = buf->p + buf->i; + ci = !(pty->conf & kMachinePtyNocursor) && y == pty->y ? i + pty->x : -1; + for (pr = 0; i < j; i += w) { + np = pty->prs[i]; + if (!(np & kMachinePtyConceal)) { + wc = pty->wcs[i]; + DCHECK(!(0x00 <= wc && wc <= 0x1F)); + DCHECK(!(0x7F <= wc && wc <= 0x9F)); + if (0x20 <= wc && wc <= 0x7E) { + u = wc; + w = 1; + } else { + u = tpenc(wc); + w = max(1, wcwidth(wc)); + } + } else { + u = ' '; + w = 1; + } + if (i == ci) { + if (u != ' ') { + np ^= kMachinePtyFlip; + } else { + u = tpenc(u'▂'); + if (pty->conf & kMachinePtyBlinkcursor) { + np |= kMachinePtyBlink; + } } } + fg = bg = -1; + xp = pr ^ np; + if (np & (kMachinePtyFg | kMachinePtyBg)) { + if (np & kMachinePtyFg) { + if (pty->fgs[i] != fg) xp |= kMachinePtyFg; + fg = pty->fgs[i]; + } + if (np & kMachinePtyBg) { + if (pty->bgs[i] != bg) xp |= kMachinePtyBg; + bg = pty->bgs[i]; + } + } + p = pb; + if (xp) { + pr = np; + p = MachinePtyEncodeStyle(p, xp, pr, fg, bg); + } + do { + *p++ = u & 0xFF; + u >>= 8; + } while (u); + DCHECK_LE(p - pb, 60); + pb = p; } - AppendStr(buf, "\e[0m"); + DCHECK_LE(pb - buf->p, buf->n); + buf->i = pb - buf->p; } diff --git a/tool/build/lib/pty.h b/tool/build/lib/pty.h index 4e91f22a..e4918d55 100644 --- a/tool/build/lib/pty.h +++ b/tool/build/lib/pty.h @@ -2,19 +2,31 @@ #define COSMOPOLITAN_TOOL_BUILD_LIB_PTY_H_ #include "tool/build/lib/buffer.h" -#define kMachinePtyFg 0x01 -#define kMachinePtyBg 0x02 -#define kMachinePtyTrue 0x04 -#define kMachinePtyBold 0x08 -#define kMachinePtyFaint 0x10 -#define kMachinePtyFlip 0x20 -#define kMachinePtyBlink 0x40 +#define kMachinePtyFg 0x0001 +#define kMachinePtyBg 0x0002 +#define kMachinePtyBold 0x0004 +#define kMachinePtyFlip 0x0008 +#define kMachinePtyFaint 0x0010 +#define kMachinePtyUnder 0x0020 +#define kMachinePtyDunder 0x0040 +#define kMachinePtyTrue 0x0080 +#define kMachinePtyBlink 0x0100 +#define kMachinePtyItalic 0x0200 +#define kMachinePtyFraktur 0x0400 +#define kMachinePtyStrike 0x0800 +#define kMachinePtyConceal 0x1000 -#define kMachinePtyNocursor 0x01 -#define kMachinePtyBlinkcursor 0x02 -#define kMachinePtyNocanon 0x04 -#define kMachinePtyNoecho 0x08 -#define kMachinePtyNoopost 0x10 +#define kMachinePtyBell 0x001 +#define kMachinePtyRedzone 0x002 +#define kMachinePtyNocursor 0x004 +#define kMachinePtyBlinkcursor 0x008 +#define kMachinePtyNocanon 0x010 +#define kMachinePtyNoecho 0x020 +#define kMachinePtyNoopost 0x040 +#define kMachinePtyLed1 0x080 +#define kMachinePtyLed2 0x100 +#define kMachinePtyLed3 0x200 +#define kMachinePtyLed4 0x400 #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ @@ -24,16 +36,18 @@ struct MachinePty { int x; int yn; int xn; + uint32_t u8; + uint32_t n8; uint32_t pr; uint32_t fg; uint32_t bg; - uint32_t u8; - uint32_t n8; + uint32_t conf; + uint32_t save; uint32_t *wcs; + uint32_t *prs; uint32_t *fgs; uint32_t *bgs; - uint32_t *prs; - uint32_t conf; + wchar_t *xlat; enum MachinePtyState { kMachinePtyAscii, kMachinePtyUtf8, @@ -57,6 +71,11 @@ ssize_t MachinePtyRead(struct MachinePty *, void *, size_t); ssize_t MachinePtyWrite(struct MachinePty *, const void *, size_t); ssize_t MachinePtyWriteInput(struct MachinePty *, const void *, size_t); void MachinePtyAppendLine(struct MachinePty *, struct Buffer *, unsigned); +void MachinePtyFullReset(struct MachinePty *); +void MachinePtyMemmove(struct MachinePty *, long, long, long); +void MachinePtyErase(struct MachinePty *, long, long); +void MachinePtySetY(struct MachinePty *, int); +void MachinePtySetX(struct MachinePty *, int); COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/build/lib/reset.c b/tool/build/lib/reset.c index 3192c4ef..65376a06 100644 --- a/tool/build/lib/reset.c +++ b/tool/build/lib/reset.c @@ -60,17 +60,9 @@ static void ResetSse(struct Machine *m) { m->sse.rc = RINT; m->sse.ftz = false; memset(m->xmm, 0, sizeof(m->xmm)); - memset(m->xmmtype, 0, sizeof(m->xmmtype)); -} - -static void ResetTlb(struct Machine *m) { - m->tlbindex = 0; - memset(m->tlb, 0, sizeof(m->tlb)); } void ResetCpu(struct Machine *m) { - m->codevirt = 0; - m->codereal = NULL; m->faultaddr = 0; m->stashsize = 0; m->stashaddr = 0; @@ -89,7 +81,13 @@ void ResetCpu(struct Machine *m) { memset(m->reg, 0, sizeof(m->reg)); memset(m->bofram, 0, sizeof(m->bofram)); memset(&m->freelist, 0, sizeof(m->freelist)); - ResetTlb(m); ResetSse(m); ResetFpu(m); } + +void ResetTlb(struct Machine *m) { + m->tlbindex = 0; + memset(m->tlb, 0, sizeof(m->tlb)); + m->codevirt = 0; + m->codehost = NULL; +} diff --git a/tool/build/lib/sse.c b/tool/build/lib/sse.c index eb5f6da5..a26c835f 100644 --- a/tool/build/lib/sse.c +++ b/tool/build/lib/sse.c @@ -94,7 +94,6 @@ #include "libc/intrin/punpcklwd.h" #include "libc/intrin/pxor.h" #include "libc/macros.h" -#include "libc/str/str.h" #include "tool/build/lib/case.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" diff --git a/tool/build/lib/ssefloat.c b/tool/build/lib/ssefloat.c index d1b23525..44e95135 100644 --- a/tool/build/lib/ssefloat.c +++ b/tool/build/lib/ssefloat.c @@ -24,6 +24,7 @@ #include "libc/intrin/pshufw.h" #include "libc/intrin/shufpd.h" #include "libc/intrin/shufps.h" +#include "libc/log/log.h" #include "libc/macros.h" #include "libc/math.h" #include "libc/str/str.h" @@ -387,33 +388,26 @@ static void VspsdWspsd(struct Machine *m, uint32_t rde, double_v opd(struct Machine *, double_v, double_v)) { float_v xf, yf; double_v xd, yd; - switch (Rep(rde) | Osz(rde)) { - case 0: - memcpy(&yf, GetModrmRegisterXmmPointerRead16(m, rde), 16); - memcpy(&xf, XmmRexrReg(m, rde), 16); - xf = opf(m, xf, yf); - memcpy(XmmRexrReg(m, rde), &xf, 16); - break; - case 1: - memcpy(&yd, GetModrmRegisterXmmPointerRead16(m, rde), 16); - memcpy(&xd, XmmRexrReg(m, rde), 16); - xd = opd(m, xd, yd); - memcpy(XmmRexrReg(m, rde), &xd, 16); - break; - case 2: - memcpy(&yd, GetModrmRegisterXmmPointerRead8(m, rde), 8); - memcpy(&xd, XmmRexrReg(m, rde), 8); - xd = opd(m, xd, yd); - memcpy(XmmRexrReg(m, rde), &xd, 8); - break; - case 3: - memcpy(&yf, GetModrmRegisterXmmPointerRead4(m, rde), 4); - memcpy(&xf, XmmRexrReg(m, rde), 4); - xf = opf(m, xf, yf); - memcpy(XmmRexrReg(m, rde), &xf, 4); - break; - default: - unreachable; + if (Rep(rde) == 2) { + memcpy(&yd, GetModrmRegisterXmmPointerRead8(m, rde), 8); + memcpy(&xd, XmmRexrReg(m, rde), 8); + xd = opd(m, xd, yd); + memcpy(XmmRexrReg(m, rde), &xd, 8); + } else if (Rep(rde) == 3) { + memcpy(&yf, GetModrmRegisterXmmPointerRead4(m, rde), 4); + memcpy(&xf, XmmRexrReg(m, rde), 4); + xf = opf(m, xf, yf); + memcpy(XmmRexrReg(m, rde), &xf, 4); + } else if (Osz(rde)) { + memcpy(&yd, GetModrmRegisterXmmPointerRead16(m, rde), 16); + memcpy(&xd, XmmRexrReg(m, rde), 16); + xd = opd(m, xd, yd); + memcpy(XmmRexrReg(m, rde), &xd, 16); + } else { + memcpy(&yf, GetModrmRegisterXmmPointerRead16(m, rde), 16); + memcpy(&xf, XmmRexrReg(m, rde), 16); + xf = opf(m, xf, yf); + memcpy(XmmRexrReg(m, rde), &xf, 16); } } diff --git a/tool/build/lib/ssemov.c b/tool/build/lib/ssemov.c index a56b39b3..86c69bf8 100644 --- a/tool/build/lib/ssemov.c +++ b/tool/build/lib/ssemov.c @@ -172,7 +172,12 @@ static void MovqVqWq(struct Machine *m, uint32_t rde) { } static void MovssVpsWps(struct Machine *m, uint32_t rde) { - memcpy(XmmRexrReg(m, rde), GetModrmRegisterXmmPointerRead4(m, rde), 4); + if (IsModrmRegister(rde)) { + memcpy(XmmRexrReg(m, rde), XmmRexbRm(m, rde), 4); + } else { + memcpy(XmmRexrReg(m, rde), ComputeReserveAddressRead4(m, rde), 4); + memset(XmmRexrReg(m, rde) + 4, 0, 12); + } } static void MovssWpsVps(struct Machine *m, uint32_t rde) { @@ -180,11 +185,16 @@ static void MovssWpsVps(struct Machine *m, uint32_t rde) { } static void MovsdVpsWps(struct Machine *m, uint32_t rde) { - memcpy(XmmRexrReg(m, rde), GetModrmRegisterXmmPointerRead16(m, rde), 8); + if (IsModrmRegister(rde)) { + memcpy(XmmRexrReg(m, rde), XmmRexbRm(m, rde), 8); + } else { + memcpy(XmmRexrReg(m, rde), ComputeReserveAddressRead8(m, rde), 8); + memset(XmmRexrReg(m, rde) + 8, 0, 8); + } } static void MovsdWpsVps(struct Machine *m, uint32_t rde) { - memcpy(GetModrmRegisterXmmPointerWrite16(m, rde), XmmRexrReg(m, rde), 8); + memcpy(GetModrmRegisterXmmPointerWrite8(m, rde), XmmRexrReg(m, rde), 8); } static void MovhlpsVqUq(struct Machine *m, uint32_t rde) { diff --git a/tool/build/lib/syscall.c b/tool/build/lib/syscall.c index 3cc81b7d..30f9122c 100644 --- a/tool/build/lib/syscall.c +++ b/tool/build/lib/syscall.c @@ -34,6 +34,8 @@ #include "libc/log/log.h" #include "libc/macros.h" #include "libc/mem/mem.h" +#include "libc/nexgen32e/vendor.h" +#include "libc/runtime/gc.h" #include "libc/runtime/runtime.h" #include "libc/sock/sock.h" #include "libc/str/str.h" @@ -65,6 +67,7 @@ #include "libc/x/x.h" #include "tool/build/lib/case.h" #include "tool/build/lib/endian.h" +#include "tool/build/lib/iovs.h" #include "tool/build/lib/machine.h" #include "tool/build/lib/memory.h" #include "tool/build/lib/pml4t.h" @@ -93,8 +96,8 @@ const struct MachineFdCb kMachineFdCbHost = { .close = close, - .read = read, - .write = write, + .readv = readv, + .writev = writev, .ioctl = ioctl, }; @@ -281,10 +284,10 @@ static int XlatTcp(int x) { switch (x) { XLAT(1, TCP_NODELAY); XLAT(2, TCP_MAXSEG); - XLAT(23, TCP_FASTOPEN); XLAT(4, TCP_KEEPIDLE); XLAT(5, TCP_KEEPINTVL); XLAT(6, TCP_KEEPCNT); + XLAT(23, TCP_FASTOPEN); default: return x; } @@ -398,18 +401,58 @@ static int XlatRusage(int x) { } } -static void *VirtualSendRead(struct Machine *m, void *dst, int64_t addr, - uint64_t n) { +static void VirtualSendRead(struct Machine *m, void *dst, int64_t addr, + uint64_t n) { VirtualSend(m, dst, addr, n); SetReadAddr(m, addr, n); - return dst; } -static void *VirtualRecvWrite(struct Machine *m, int64_t addr, void *dst, - uint64_t n) { - VirtualRecv(m, addr, dst, n); +static void VirtualRecvWrite(struct Machine *m, int64_t addr, void *src, + uint64_t n) { + VirtualRecv(m, addr, src, n); SetWriteAddr(m, addr, n); - return dst; +} + +static int AppendIovsReal(struct Machine *m, struct Iovs *ib, int64_t addr, + size_t size) { + void *real; + size_t have; + unsigned got; + while (size) { + if (!(real = FindReal(m, addr))) return efault(); + have = 0x1000 - (addr & 0xfff); + got = MIN(size, have); + if (AppendIovs(ib, real, got) == -1) return -1; + addr += got; + size -= got; + } + return 0; +} + +static int AppendIovsGuest(struct Machine *m, struct Iovs *iv, int64_t iovaddr, + long iovlen) { + int rc; + long i, iovsize; + struct iovec *guestiovs; + if (!__builtin_mul_overflow(iovlen, sizeof(struct iovec), &iovsize) && + (0 <= iovsize && iovsize <= 0x7ffff000)) { + if ((guestiovs = malloc(iovsize))) { + VirtualSendRead(m, guestiovs, iovaddr, iovsize); + for (rc = i = 0; i < iovlen; ++i) { + if (AppendIovsReal(m, iv, (intptr_t)guestiovs[i].iov_base, + guestiovs[i].iov_len) == -1) { + rc = -1; + break; + } + } + free(guestiovs); + } else { + rc = enomem(); + } + } else { + rc = eoverflow(); + } + return rc; } static struct sigaction *CoerceSigactionToCosmo( @@ -463,44 +506,52 @@ static int OpMadvise(struct Machine *m, int64_t addr, size_t length, } static int64_t OpBrk(struct Machine *m, int64_t addr) { - void *real; - if (addr && addr != m->brk) { - if (addr < m->brk) { - addr = ROUNDUP(addr, FRAMESIZE); - FreePml4t(m->cr3, addr, m->brk - addr, free, munmap); + addr = ROUNDUP(addr, PAGESIZE); + if (addr > m->brk) { + if (ReserveVirtual(m, m->brk, addr - m->brk) != -1) { + m->brk = addr; + } + } else if (addr < m->brk) { + if (FreeVirtual(m, addr, m->brk - addr) != -1) { m->brk = addr; - } else { - addr = ROUNDUP(addr, FRAMESIZE); - if ((real = mmap(NULL, addr - m->brk, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)) != MAP_FAILED) { - CHECK_NE(-1, RegisterMemory(m, m->brk, real, addr - m->brk)); - m->brk = addr; - } } } return m->brk; } static int64_t OpMmap(struct Machine *m, int64_t virt, size_t size, int prot, - int flags, int fd, int64_t off) { - void *real; + int flags, int fd, int64_t offset) { + void *tmp; + LOGF("MMAP%s %p %,ld %#x %#x %d %#lx", IsGenuineCosmo() ? " SIMULATED" : "", + virt, size, prot, flags, fd, offset); flags = XlatMapFlags(flags); if (fd != -1 && (fd = XlatFd(m, fd)) == -1) return -1; - real = mmap(NULL, size, prot, flags & ~MAP_FIXED, fd, off); - if (real == MAP_FAILED) return -1; if (!(flags & MAP_FIXED)) { - if (0 <= virt && virt < 0x400000) virt = 0x400000; - if ((virt = FindPml4t(m->cr3, virt, size)) == -1) return -1; + if (!virt) { + if ((virt = FindVirtual(m, m->brk, size)) == -1) return -1; + m->brk = virt + size; + } else { + if ((virt = FindVirtual(m, virt, size)) == -1) return -1; + } + } + if (ReserveVirtual(m, virt, size) == -1) return -1; + if (fd != -1 && !(flags & MAP_ANONYMOUS)) { + /* TODO: lazy page loading */ + CHECK_NOTNULL((tmp = malloc(size))); + CHECK_EQ(size, pread(fd, tmp, size, offset)); + VirtualRecvWrite(m, virt, tmp, size); + free(tmp); } - CHECK_NE(-1, RegisterMemory(m, virt, real, size)); return virt; } static int OpMunmap(struct Machine *m, int64_t addr, uint64_t size) { - return FreePml4t(m->cr3, addr, size, free, munmap); + return FreeVirtual(m, addr, size); } static int OpMsync(struct Machine *m, int64_t virt, size_t size, int flags) { + return enosys(); +#if 0 size_t i; void *page; virt = ROUNDDOWN(virt, 4096); @@ -510,42 +561,7 @@ static int OpMsync(struct Machine *m, int64_t virt, size_t size, int flags) { if (msync(page, 4096, flags) == -1) return -1; } return 0; -} - -static void *GetDirectBuf(struct Machine *m, int64_t addr, size_t *size) { - void *page; - *size = MIN(*size, 0x1000 - (addr & 0xfff)); - if (!(page = FindReal(m, addr))) return MAP_FAILED; - return page; -} - -static struct iovec *GetDirectIov(struct Machine *m, int64_t addr, int *len) { - int i; - size_t n, size; - struct iovec *iov; - if (!__builtin_mul_overflow(sizeof(*iov), *len, &n) && n <= 0x7ffff000) { - if ((iov = malloc(n))) { - VirtualSendRead(m, iov, addr, n); - for (i = 0; i < *len; ++i) { - size = iov[i].iov_len; - if ((iov[i].iov_base = GetDirectBuf( - m, (int64_t)(intptr_t)iov[i].iov_base, &size)) == MAP_FAILED) { - free(iov); - return (struct iovec *)efault(); - } - if (size < iov[i].iov_len) { - iov[i].iov_len = size; - *len = i + 1; - break; - } - } - return iov; - } else { - return (struct iovec *)-1; - } - } else { - return (struct iovec *)eoverflow(); - } +#endif } static int OpClose(struct Machine *m, int fd) { @@ -575,36 +591,39 @@ static int OpOpenat(struct Machine *m, int dirfd, int64_t path, int flags, } static int OpPipe(struct Machine *m, int64_t pipefds_addr) { - void *p[2]; - uint8_t b[8]; - int rc, i, j, *pipefds; - if ((i = MachineFdAdd(&m->fds)) == -1) return -1; - if ((j = MachineFdAdd(&m->fds)) == -1) return -1; - if ((rc = pipe((pipefds = BeginStoreNp(m, pipefds_addr, 8, p, b)))) != -1) { - EndStoreNp(m, pipefds_addr, 8, p, b); - m->fds.p[i].cb = &kMachineFdCbHost; - m->fds.p[i].fd = pipefds[0]; - m->fds.p[j].cb = &kMachineFdCbHost; - m->fds.p[j].fd = pipefds[1]; - } else { + int i, j, pipefds[2]; + if ((i = MachineFdAdd(&m->fds)) != -1) { + if ((j = MachineFdAdd(&m->fds)) != -1) { + if (pipe(pipefds) != -1) { + m->fds.p[i].cb = &kMachineFdCbHost; + m->fds.p[i].fd = pipefds[0]; + m->fds.p[j].cb = &kMachineFdCbHost; + m->fds.p[j].fd = pipefds[1]; + pipefds[0] = i; + pipefds[1] = j; + VirtualRecvWrite(m, pipefds_addr, pipefds, sizeof(pipefds)); + return 0; + } + MachineFdRemove(&m->fds, j); + } MachineFdRemove(&m->fds, i); - MachineFdRemove(&m->fds, j); } - return rc; + return -1; } static int OpDup(struct Machine *m, int fd) { - int i, rc; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((i = MachineFdAdd(&m->fds)) == -1) return -1; - if ((rc = dup(fd)) != -1) { - m->fds.p[i].cb = &kMachineFdCbHost; - m->fds.p[i].fd = rc; - rc = i; - } else { - MachineFdRemove(&m->fds, i); + int i; + if ((fd = XlatFd(m, fd)) != -1) { + if ((i = MachineFdAdd(&m->fds)) != -1) { + if ((fd = dup(fd)) != -1) { + m->fds.p[i].cb = &kMachineFdCbHost; + m->fds.p[i].fd = fd; + return i; + } + MachineFdRemove(&m->fds, i); + } } - return rc; + return -1; } static int OpDup2(struct Machine *m, int fd, int newfd) { @@ -708,24 +727,70 @@ static int OpSetsockopt(struct Machine *m, int fd, int level, int optname, } static ssize_t OpRead(struct Machine *m, int fd, int64_t addr, size_t size) { - void *data; ssize_t rc; - if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf(); - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = m->fds.p[fd].cb->read(m->fds.p[fd].fd, data, size)) != -1) { - SetWriteAddr(m, addr, rc); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = m->fds.p[fd].cb->readv(m->fds.p[fd].fd, iv.p, iv.i)) != -1) { + SetWriteAddr(m, addr, rc); + } + } + } else { + rc = ebadf(); } + FreeIovs(&iv); + return rc; +} + +static ssize_t OpPread(struct Machine *m, int fd, int64_t addr, size_t size, + int64_t offset) { + ssize_t rc; + struct Iovs iv; + InitIovs(&iv); + if ((rc = XlatFd(m, fd)) != -1) { + fd = rc; + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = preadv(fd, iv.p, iv.i, offset)) != -1) { + SetWriteAddr(m, addr, rc); + } + } + } + FreeIovs(&iv); return rc; } static ssize_t OpWrite(struct Machine *m, int fd, int64_t addr, size_t size) { - void *data; ssize_t rc; - if (!(0 <= fd && fd < m->fds.i) || !m->fds.p[fd].cb) return ebadf(); - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = m->fds.p[fd].cb->write(m->fds.p[fd].fd, data, size)) != -1) { - SetReadAddr(m, addr, size); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = m->fds.p[fd].cb->writev(m->fds.p[fd].fd, iv.p, iv.i)) != -1) { + SetReadAddr(m, addr, rc); + } + } + } else { + rc = ebadf(); } + FreeIovs(&iv); + return rc; +} + +static ssize_t OpPwrite(struct Machine *m, int fd, int64_t addr, size_t size, + int64_t offset) { + ssize_t rc; + struct Iovs iv; + InitIovs(&iv); + if ((rc = XlatFd(m, fd)) != -1) { + fd = rc; + if ((rc = AppendIovsReal(m, &iv, addr, size)) != -1) { + if ((rc = pwritev(fd, iv.p, iv.i, offset)) != -1) { + SetReadAddr(m, addr, rc); + } + } + } + FreeIovs(&iv); return rc; } @@ -746,9 +811,9 @@ static int IoctlTcgets(struct Machine *m, int fd, int64_t addr, if ((rc = fn(fd, TCGETS, &tio)) != -1) { memcpy(&tio2, &tio, sizeof(tio)); tio2.c_iflag = 0; - if (tio.c_iflag & ISIG) tio2.c_iflag |= ISIG_LINUX; - if (tio.c_iflag & ICANON) tio2.c_iflag |= ICANON_LINUX; - if (tio.c_iflag & ECHO) tio2.c_iflag |= ECHO_LINUX; + if (tio.c_lflag & ISIG) tio2.c_lflag |= ISIG_LINUX; + if (tio.c_lflag & ICANON) tio2.c_lflag |= ICANON_LINUX; + if (tio.c_lflag & ECHO) tio2.c_lflag |= ECHO_LINUX; tio2.c_oflag = 0; if (tio.c_oflag & OPOST) tio2.c_oflag |= OPOST_LINUX; VirtualRecvWrite(m, addr, &tio2, sizeof(tio2)); @@ -762,9 +827,9 @@ static int IoctlTcsets(struct Machine *m, int fd, int64_t request, int64_t addr, VirtualSendRead(m, &tio, addr, sizeof(tio)); memcpy(&tio2, &tio, sizeof(tio)); tio2.c_iflag = 0; - if (tio.c_iflag & ISIG_LINUX) tio2.c_iflag |= ISIG; - if (tio.c_iflag & ICANON_LINUX) tio2.c_iflag |= ICANON; - if (tio.c_iflag & ECHO_LINUX) tio2.c_iflag |= ECHO; + if (tio.c_lflag & ISIG_LINUX) tio2.c_lflag |= ISIG; + if (tio.c_lflag & ICANON_LINUX) tio2.c_lflag |= ICANON; + if (tio.c_lflag & ECHO_LINUX) tio2.c_lflag |= ECHO; tio2.c_oflag = 0; if (tio.c_oflag & OPOST_LINUX) tio2.c_oflag |= OPOST; return fn(fd, request, &tio2); @@ -791,44 +856,34 @@ static int OpIoctl(struct Machine *m, int fd, uint64_t request, int64_t addr) { } } -static ssize_t OpPread(struct Machine *m, int fd, int64_t addr, size_t size, - int64_t offset) { - void *data; - ssize_t rc; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = pread(fd, data, size, offset)) != -1) SetWriteAddr(m, addr, rc); - return rc; -} - -static ssize_t OpPwrite(struct Machine *m, int fd, int64_t addr, size_t size, - int64_t offset) { - void *data; - ssize_t rc; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((data = GetDirectBuf(m, addr, &size)) == MAP_FAILED) return efault(); - if ((rc = pwrite(fd, data, size, offset)) != -1) SetReadAddr(m, addr, size); - return rc; -} - static ssize_t OpReadv(struct Machine *m, int fd, int64_t iovaddr, int iovlen) { ssize_t rc; - struct iovec *iov; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((iov = GetDirectIov(m, iovaddr, &iovlen)) == MAP_FAILED) return -1; - rc = readv(fd, iov, iovlen); - free(iov); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsGuest(m, &iv, iovaddr, iovlen)) != -1) { + rc = m->fds.p[fd].cb->readv(m->fds.p[fd].fd, iv.p, iv.i); + } + } else { + rc = ebadf(); + } + FreeIovs(&iv); return rc; } static ssize_t OpWritev(struct Machine *m, int fd, int64_t iovaddr, int iovlen) { ssize_t rc; - struct iovec *iov; - if ((fd = XlatFd(m, fd)) == -1) return -1; - if ((iov = GetDirectIov(m, iovaddr, &iovlen)) == MAP_FAILED) return -1; - rc = writev(fd, iov, iovlen); - free(iov); + struct Iovs iv; + InitIovs(&iv); + if ((0 <= fd && fd < m->fds.i) && m->fds.p[fd].cb) { + if ((rc = AppendIovsGuest(m, &iv, iovaddr, iovlen)) != -1) { + rc = m->fds.p[fd].cb->writev(m->fds.p[fd].fd, iv.p, iv.i); + } + } else { + rc = ebadf(); + } + FreeIovs(&iv); return rc; } @@ -850,33 +905,25 @@ static int OpFaccessat(struct Machine *m, int dirfd, int64_t path, int mode, return faccessat(dirfd, LoadStr(m, path), mode, flags); } -static int OpFstatat(struct Machine *m, int dirfd, int64_t path, int64_t st, +static int OpFstatat(struct Machine *m, int dirfd, int64_t path, int64_t staddr, int flags) { int rc; - void *stp[2]; - uint8_t *stbuf; + struct stat st; flags = XlatAtf(flags); if ((dirfd = XlatAfd(m, dirfd)) == -1) return -1; - if (!(stbuf = malloc(sizeof(struct stat)))) return enomem(); - if ((rc = fstatat(dirfd, LoadStr(m, path), - BeginStoreNp(m, st, sizeof(stbuf), stp, stbuf), flags)) != - -1) { - EndStoreNp(m, st, sizeof(stbuf), stp, stbuf); + if ((rc = fstatat(dirfd, LoadStr(m, path), &st, flags)) != -1) { + VirtualRecvWrite(m, staddr, &st, sizeof(struct stat)); } - free(stbuf); return rc; } -static int OpFstat(struct Machine *m, int fd, int64_t st) { +static int OpFstat(struct Machine *m, int fd, int64_t staddr) { int rc; - void *stp[2]; - uint8_t *stbuf; + struct stat st; if ((fd = XlatFd(m, fd)) == -1) return -1; - if (!(stbuf = malloc(sizeof(struct stat)))) return enomem(); - if ((rc = fstat(fd, BeginStoreNp(m, st, sizeof(stbuf), stp, stbuf))) != -1) { - EndStoreNp(m, st, sizeof(stbuf), stp, stbuf); + if ((rc = fstat(fd, &st)) != -1) { + VirtualRecvWrite(m, staddr, &st, sizeof(struct stat)); } - free(stbuf); return rc; } @@ -1091,21 +1138,34 @@ static int OpGettimeofday(struct Machine *m, int64_t tv, int64_t tz) { static int OpPoll(struct Machine *m, int64_t fdsaddr, uint64_t nfds, int32_t timeout_ms) { - int rc; - size_t n; - struct pollfd *fds; - if (!__builtin_mul_overflow(sizeof(*fds), nfds, &n) && n <= 0x7ffff000) { - if ((fds = malloc(n))) { - VirtualSendRead(m, fds, fdsaddr, n); - rc = poll(fds, nfds, timeout_ms); - free(fds); - return rc; + int count, i; + uint64_t fdssize; + struct pollfd *hostfds, *guestfds; + if (!__builtin_mul_overflow(nfds, sizeof(struct pollfd), &fdssize) && + fdssize <= 0x7ffff000) { + hostfds = malloc(fdssize); + guestfds = malloc(fdssize); + if (hostfds && guestfds) { + VirtualSendRead(m, guestfds, fdsaddr, fdssize); + memcpy(hostfds, guestfds, fdssize); + for (i = 0; i < nfds; ++i) { + hostfds[i].fd = XlatFd(m, hostfds[i].fd); + } + if ((count = poll(hostfds, nfds, timeout_ms)) != -1) { + for (i = 0; i < count; ++i) { + hostfds[i].fd = guestfds[i].fd; + } + VirtualRecvWrite(m, fdsaddr, hostfds, count * sizeof(struct pollfd)); + } } else { - return enomem(); + count = enomem(); } + free(guestfds); + free(hostfds); } else { - return einval(); + count = einval(); } + return count; } static int OpSigprocmask(struct Machine *m, int how, int64_t setaddr, @@ -1303,7 +1363,7 @@ void OpSyscall(struct Machine *m, uint32_t rde) { SYSCALL(0x177, vmsplice(di, P(si), dx, r0)); CASE(0xE7, HaltMachine(m, di | 0x100)); default: - /* LOGF("missing syscall 0x%03x", ax); */ + LOGF("missing syscall 0x%03x", ax); ax = enosys(); break; } diff --git a/tool/build/lib/throw.c b/tool/build/lib/throw.c index 53414fcc..87fcc2f6 100644 --- a/tool/build/lib/throw.c +++ b/tool/build/lib/throw.c @@ -18,9 +18,12 @@ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "libc/log/check.h" +#include "libc/log/log.h" +#include "libc/nexgen32e/vendor.h" #include "libc/runtime/runtime.h" #include "libc/str/str.h" #include "tool/build/lib/address.h" +#include "tool/build/lib/endian.h" #include "tool/build/lib/throw.h" static bool IsHaltingInitialized(struct Machine *m) { @@ -40,7 +43,15 @@ void ThrowDivideError(struct Machine *m) { void ThrowSegmentationFault(struct Machine *m, int64_t va) { m->faultaddr = va; - m->ip -= m->xedd->length; + if (m->xedd) m->ip -= m->xedd->length; + WARNF("%s%s ADDR %p IP %p AX %lx CX %lx DX %lx BX %lx SP %lx " + "BP %lx SI %lx DI %lx R8 %lx R9 %lx R10 %lx R11 %lx R12 %lx R13 %lx " + "R14 %lx R15 %lx", + "SEGMENTATION FAULT", IsGenuineCosmo() ? " SIMULATED" : "", va, m->ip, + Read64(m->ax), Read64(m->cx), Read64(m->dx), Read64(m->bx), + Read64(m->sp), Read64(m->bp), Read64(m->si), Read64(m->di), + Read64(m->r8), Read64(m->r9), Read64(m->r10), Read64(m->r11), + Read64(m->r12), Read64(m->r13), Read64(m->r14), Read64(m->r15)); HaltMachine(m, kMachineSegmentationFault); } @@ -49,7 +60,7 @@ void ThrowProtectionFault(struct Machine *m) { } void OpUd(struct Machine *m, uint32_t rde) { - m->ip -= m->xedd->length; + if (m->xedd) m->ip -= m->xedd->length; HaltMachine(m, kMachineUndefinedInstruction); } diff --git a/tool/build/lib/time.c b/tool/build/lib/time.c index 87efbcdc..86e81cba 100644 --- a/tool/build/lib/time.c +++ b/tool/build/lib/time.c @@ -24,13 +24,14 @@ #include "tool/build/lib/endian.h" #include "tool/build/lib/time.h" +/** + * @fileoverview i am the timelorde + */ + void OpPause(struct Machine *m, uint32_t rde) { sched_yield(); } -/** - * I am the timelorde. - */ void OpRdtsc(struct Machine *m, uint32_t rde) { uint64_t c; struct timespec ts; diff --git a/tool/build/tinyemu.c b/tool/build/tinyemu.c index 93839ed6..71597228 100644 --- a/tool/build/tinyemu.c +++ b/tool/build/tinyemu.c @@ -30,22 +30,17 @@ #include "tool/build/lib/pty.h" #include "tool/build/lib/syscall.h" -struct Machine m[1]; - int main(int argc, char *argv[]) { int rc; struct Elf elf; - const char *codepath; - codepath = argv[1]; + struct Machine *m; if (argc < 2) { fputs("Usage: ", stderr); fputs(argv[0], stderr); fputs(" PROG [ARGS...]\n", stderr); return EX_USAGE; } - m->cr3 = MallocPage(); - m->mode = XED_MACHINE_MODE_LONG_64; - InitMachine(m); + m = NewMachine(); LoadProgram(m, argv[1], argv + 2, environ, &elf); m->fds.i = 3; m->fds.n = 8; diff --git a/tool/build/zipobj.c b/tool/build/zipobj.c index c12282ff..69ec6152 100644 --- a/tool/build/zipobj.c +++ b/tool/build/zipobj.c @@ -279,7 +279,9 @@ void ProcessFile(struct ElfWriter *elf, const char *path) { map = NULL; } EmitZip(elf, path, pathlen, map, &st); - CHECK_NE(-1, munmap(map, st.st_size)); + if (st.st_size) { + CHECK_NE(-1, munmap(map, st.st_size)); + } } void PullEndOfCentralDirectoryIntoLinkage(struct ElfWriter *elf) { diff --git a/tool/calc/calc.c b/tool/calc/calc.c index 0a6c1755..94e9594e 100644 --- a/tool/calc/calc.c +++ b/tool/calc/calc.c @@ -368,6 +368,11 @@ static long double FnBsr(struct Numbers *a) { return bsr(a->x); } +static long double FnBsrl(struct Numbers *a) { + if (!a) MissingArgumentError(); + return bsrl(a->x); +} + static long double FnBsfl(struct Numbers *a) { if (!a) MissingArgumentError(); return bsfl(a->x); @@ -610,7 +615,7 @@ static const struct Fn { {"bsfl", FnBsfl}, {"bsfl", FnBsfl}, {"bsr", FnBsr}, - {"bsr", FnBsr}, + {"bsrl", FnBsrl}, {"cbrt", FnCbrt}, {"ceil", FnCeil}, {"copysign", FnCopysign}, diff --git a/tool/emacs/cosmo-cpp-constants.el b/tool/emacs/cosmo-cpp-constants.el index 20ebdaee..f68097f7 100644 --- a/tool/emacs/cosmo-cpp-constants.el +++ b/tool/emacs/cosmo-cpp-constants.el @@ -143,7 +143,14 @@ "IM_FEELING_NAUGHTY" "__REAL_MODE__" "__x86__" - "__i386__")) + "__i386__" + "__W__" + "__PG__" + "__MFENTRY__" + "__MNO_VZEROUPPER__" + "__FSANITIZE_UNDEFINED__" + "__MNOP_MCOUNT__" + "__MRECORD_MCOUNT__")) (defconst cosmo-cpp-constants (append cosmo-cpp-constants-c11 diff --git a/tool/net/redbean.c b/tool/net/redbean.c index e01b2fcc..362923c4 100644 --- a/tool/net/redbean.c +++ b/tool/net/redbean.c @@ -62,6 +62,7 @@ #include "libc/sysv/consts/so.h" #include "libc/sysv/consts/sock.h" #include "libc/sysv/consts/sol.h" +#include "libc/sysv/consts/tcp.h" #include "libc/sysv/errfuns.h" #include "libc/time/struct/tm.h" #include "libc/time/time.h" @@ -1136,10 +1137,13 @@ void ProcessConnection(void) { } } -static void SetReusePortAndAllowMultipleProcesses(void) { +static void TuneServerSocket(void) { int yes = 1; LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))); LOGIFNEG1(setsockopt(server, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_FASTOPEN, &yes, sizeof(yes))); + LOGIFNEG1(setsockopt(server, IPPROTO_TCP, TCP_QUICKACK, &yes, sizeof(yes))); } void RedBean(void) { @@ -1147,6 +1151,7 @@ void RedBean(void) { programfile = (const char *)getauxval(AT_EXECFN); CHECK(OpenZip(programfile)); xsigaction(SIGINT, OnTerminate, 0, 0, 0); + xsigaction(SIGHUP, OnTerminate, 0, 0, 0); xsigaction(SIGTERM, OnTerminate, 0, 0, 0); xsigaction(SIGCHLD, SIG_IGN, 0, 0, 0); xsigaction(SIGPIPE, SIG_IGN, 0, 0, 0); @@ -1154,7 +1159,7 @@ void RedBean(void) { xsigaction(SIGALRM, OnAlarm, 0, 0, 0); if (setitimer(ITIMER_REAL, &kHeartbeat, NULL) == -1) notimer = true; CHECK_NE(-1, (server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); - SetReusePortAndAllowMultipleProcesses(); + TuneServerSocket(); CHECK_NE(-1, bind(server, &serveraddr, sizeof(serveraddr))); CHECK_NE(-1, listen(server, 10)); DescribeAddress(serveraddrstr, &serveraddr); diff --git a/tool/viz/deathstar.c b/tool/viz/deathstar.c index acbefb2e..6a887d2a 100644 --- a/tool/viz/deathstar.c +++ b/tool/viz/deathstar.c @@ -35,8 +35,8 @@ const char *kShades[] = { jmp_buf jb_; double light_[3] = {-50, 0, 50}; -struct Sphere pos_ = {20, 20, 20, 20}; -struct Sphere neg_ = {1, 1, -6, 20}; +struct Sphere pos_ = {11, 11, 11, 11}; +struct Sphere neg_ = {1, 1, -4, 11}; static void OnCtrlC(int sig) { longjmp(jb_, 1); @@ -128,7 +128,7 @@ int main() { double ang; struct termios old; if (cancolor()) { - ttyhidecursor(fileno(stdout)); + WRITE("\e[?25l"); if (!setjmp(jb_)) { xsigaction(SIGINT, OnCtrlC, 0, 0, NULL); ang = 0; @@ -143,7 +143,7 @@ int main() { usleep(1. / FRAMERATE * 1e6); } } - ttyshowcursor(fileno(stdout)); + WRITE("\e[0m\e[H\e[J\e[?25h"); return 0; } else { return 1; diff --git a/tool/viz/printvideo.c b/tool/viz/printvideo.c index 92a2b32e..dda19279 100644 --- a/tool/viz/printvideo.c +++ b/tool/viz/printvideo.c @@ -144,10 +144,7 @@ Effects Shortcuts:\n\ H +Hue ALT+H -Hue\n\ S +Saturation ALT+S -Saturation\n\ L +Lightness ALT+L -Lightness\n\ - F1 {Unsharp,Sharp}\n\ - F2 {Gaussian,Boxblur}\n\ - F3 Sobel\n\ - F4 Emboss\n\ + CTRL-G {Unsharp,Sharp}\n\ \n\ Environment Variables:\n\ SOX overrides location of SoX executable\n\ @@ -1157,8 +1154,6 @@ static optimizesize void ReadKeyboard(void) { case CTRL('G'): sharp_ = (sharp_ + 1) % kSharpMAX; break; - case CTRL('B'): - longjmp(jb_, 1); case CTRL('\\'): raise(SIGQUIT); break; diff --git a/libc/log/bisectsymbol.c b/tool/viz/tailf.c similarity index 51% rename from libc/log/bisectsymbol.c rename to tool/viz/tailf.c index e94f5ebe..e9a663fc 100644 --- a/libc/log/bisectsymbol.c +++ b/tool/viz/tailf.c @@ -17,27 +17,86 @@ │ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ │ 02110-1301 USA │ ╚─────────────────────────────────────────────────────────────────────────────*/ -#include "libc/alg/bisectcarleft.h" -#include "libc/assert.h" -#include "libc/runtime/symbols.h" +#include "libc/bits/safemacros.h" +#include "libc/calls/calls.h" +#include "libc/calls/struct/stat.h" +#include "libc/macros.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/sig.h" +#include "libc/time/time.h" +#include "libc/x/x.h" /** - * Finds symbol associated with address. - * @param symbol table pointer (null propagating) - * @return symbol address or NULL if not found + * @fileoverview tail -f with lower poll rate + * @see busybox not having interval flag */ -const struct Symbol *bisectsymbol(struct SymbolTable *t, - intptr_t virtual_address, - int64_t *opt_out_addend) { - const struct Symbol *symbol = NULL; - int64_t addend = (intptr_t)virtual_address; - if (t && t->count) { - unsigned key = (unsigned)((intptr_t)virtual_address - t->addr_base - 1); - unsigned i = bisectcarleft((const int32_t(*)[2])t->symbols, t->count, key); - assert(0 <= i && i < t->count); - symbol = &t->symbols[i]; - addend = (int64_t)key - (int64_t)symbol->addr_rva; - } - if (opt_out_addend) *opt_out_addend = addend; - return symbol; + +int fd; +bool exited; +struct stat st; +char buf[FRAMESIZE]; + +int WriteString(const char *s) { + return write(1, s, strlen(s)); +} + +void HideCursor(void) { + WriteString("\e[?25l"); +} + +void ShowCursor(void) { + WriteString("\e[?25h"); +} + +void OnInt(void) { + exited = true; +} + +void OnExit(void) { + ShowCursor(); +} + +int main(int argc, char *argv[]) { + char *p; + ssize_t n; + size_t i, j; + bool chopped; + if (argc < 2) return 1; + if ((fd = open(argv[1], O_RDONLY)) == -1) return 2; + if (fstat(fd, &st) == -1) return 3; + n = st.st_size - MIN(st.st_size, sizeof(buf)); + if ((n = pread(fd, buf, sizeof(buf), n)) == -1) return 4; + for (p = buf + n, i = 0; i < 10; ++i) { + p = firstnonnull(memrchr(buf, '\n', p - buf), buf); + } + chopped = false; + if (buf + n - p) ++p; + i = st.st_size - (buf + n - p); + atexit(OnExit); + HideCursor(); + xsigaction(SIGINT, OnInt, 0, 0, 0); + xsigaction(SIGTERM, OnInt, 0, 0, 0); + while (!exited) { + if (fstat(fd, &st) == -1) return 5; + if (i > st.st_size) i = 0; + for (; i < st.st_size; i += n) { + if ((n = pread(fd, buf, sizeof(buf), i)) == -1) return 6; + j = n; + while (j && (buf[j - 1] == '\n' || buf[j - 1] == '\r')) --j; + if (j) { + if (chopped) { + WriteString("\r\n"); + } + write(1, buf, j); + } + chopped = j != n; + } + dsleep(.01); + } + close(fd); + WriteString("\r\n"); + return 0; } diff --git a/tool/viz/unicode.c b/tool/viz/unicode.c new file mode 100644 index 00000000..8c1849a2 --- /dev/null +++ b/tool/viz/unicode.c @@ -0,0 +1,92 @@ +/*-*- 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│ +╞══════════════════════════════════════════════════════════════════════════════╡ +│ Copyright 2020 Justine Alexandra Roberts Tunney │ +│ │ +│ This program is free software; you can redistribute it and/or modify │ +│ it under the terms of the GNU General Public License as published by │ +│ the Free Software Foundation; version 2 of the License. │ +│ │ +│ This program is distributed in the hope that it will be useful, but │ +│ WITHOUT ANY WARRANTY; without even the implied warranty of │ +│ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU │ +│ General Public License for more details. │ +│ │ +│ You should have received a copy of the GNU General Public License │ +│ along with this program; if not, write to the Free Software │ +│ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA │ +│ 02110-1301 USA │ +╚─────────────────────────────────────────────────────────────────────────────*/ +#include "libc/fmt/fmt.h" +#include "libc/mem/mem.h" +#include "libc/runtime/gc.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/unicode/unicode.h" +#include "libc/x/x.h" + +int a, b, w, i; +char name[512]; + +void DisplayUnicodeCharacter(void) { + int c, cw; + c = i; + cw = wcwidth(c); + if (cw < 1) { + c = '?'; + cw = 1; + } + if (w) { + if (w + 1 + cw > 80) { + printf("\n"); + w = 0; + } else { + fputc(' ', stdout); + w += 1 + cw; + } + } + if (!w) { + printf("%08x ", i); + w = 9 + cw; + } + fputwc(c, stdout); +} + +void DisplayUnicodeBlock(void) { + if (a == 0x10000) { + printf("\n\n\n\n\n\n\n " + "ASTRAL PLANES\n\n\n\n\n"); + } + if (a == 0x0590 /* hebrew */) return; + if (a == 0x0600 /* arabic */) return; + if (a == 0x08a0 /* arabic */) return; + if (a == 0x0750 /* arabic */) return; + if (a == 0x0700 /* syriac */) return; + if (a == 0x10800 /* cypriot */) return; + printf("\n\n%-60s%20s\n" + "──────────────────────────────────────────────" + "──────────────────────────────────\n", + name, gc(xasprintf("%04x .. %04x", a, b))); + w = 0; + for (i = a; i <= b; ++i) { + DisplayUnicodeCharacter(); + } +} + +int main(int argc, char *argv[]) { + FILE *f; + char *line; + size_t linesize; + printf("\n\n\n\n\n UNICODE PLANES\n\n\n\n"); + f = fopen("libc/unicode/blocks.txt", "r"); + line = NULL; + linesize = 0; + while (!feof(f)) { + if (getline(&line, &linesize, f) == -1) break; + if (sscanf(line, "%x..%x; %s", &a, &b, name) != 3) continue; + DisplayUnicodeBlock(); + } + free(line); + fclose(f); + return 0; +}