From dba7552c1ea064d7bddb80684a2f1c32d8fa4d70 Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Wed, 18 Nov 2020 08:26:03 -0800 Subject: [PATCH] Add Conway's Game of Life --- ape/ape.S | 206 ++++++------- libc/calls/calls.mk | 4 +- libc/calls/hefty/fork-nt.c | 22 +- libc/calls/ioctl.h | 3 - libc/calls/termios-internal.h | 5 +- libc/log/die.c | 1 - libc/log/oncrash.c | 1 - libc/log/ubsan.c | 1 - libc/nexgen32e/g_runstate.S | 23 -- libc/nt/iocp.h | 4 +- libc/runtime/_exit.S | 2 - libc/runtime/abort.S | 2 - libc/runtime/construct.S | 1 - libc/runtime/ftrace.greg.c | 2 +- libc/runtime/init.S | 1 - libc/runtime/internal.h | 17 +- libc/str/memfrob.c | 7 +- tool/build/ar.c | 10 +- tool/build/emubin/poke.h | 2 +- tool/tags/tags.y | 2 +- tool/viz/life.c | 527 ++++++++++++++++++++++++++++++++++ tool/viz/memzoom.c | 7 - 22 files changed, 664 insertions(+), 186 deletions(-) delete mode 100644 libc/nexgen32e/g_runstate.S create mode 100644 tool/viz/life.c diff --git a/ape/ape.S b/ape/ape.S index d8fff87e..d1df16d5 100644 --- a/ape/ape.S +++ b/ape/ape.S @@ -364,6 +364,109 @@ pcread: push %ax jmp 1b .endfn pcread +/ Waits for serial lines to become idle. +/ +/ @param di short array of serial ports (0 means not present) +/ @param si number of items in array +/ @mode long,legacy,real +sflush: mov %si,%cx + mov %di,%si + xor %dx,%dx +0: lodsb + mov %al,%dl + lodsb + mov %al,%dh + test %ax,%ax + jz 2f + add $UART_LSR,%dx + mov $UART_TTYIDL,%ah +1: in %dx,%al + and %ah,%al + rep + nop + jz 1b + loop 0b +2: ret + .endfn sflush,globl + +/ Transmits byte over serial line. +/ +/ This is both blocking and asynchronous. +/ +/ @param di character to send +/ @param si serial port +/ @mode long,legacy,real +/ @see ttytxr +sputc: push %ax + push %cx + push %dx + mov %si,%dx + add $UART_LSR,%dx + mov $UART_TTYTXR,%ah +1: in %dx,%al + and %ah,%al + jnz 2f + rep + nop + jmp 1b +2: mov %di,%ax + mov %si,%dx + out %al,%dx + pop %dx + pop %cx + pop %ax + ret + .endfn sputc,globl + +/ Shuts down personal computer. +/ +/ @mode real +/ @noreturn +apmoff: mov $0x5300,%ax # apm installation check + xor %bx,%bx # for the apm bios itself + int $APM_SERVICE + jc 1f + cmp $'P<<8|'M,%bx # did apm bios service interrupt? + jne 1f + mov $0x5301,%ax # real mode interface connect + xor %bx,%bx # to apm bios device + int $APM_SERVICE # ignore errors e.g. already connected + xor %bx,%bx + xor %cx,%cx + mov $0x5307,%ax # set power state + mov $1,%bl # for all devices within my dominion + mov $3,%cl # to off + int $APM_SERVICE +1: call panic + .endfn apmoff,globl + +/ Video put char. +/ +/ @param al is the char +/ @mode real +rvputc: push %bx # don't clobber bp,bx,di,si,cx + push %bp # original ibm pc scroll up bug + mov $7,%bx # normal mda/cga style page zero + mov $0x0e,%ah # teletype output al cp437 + int $0x10 # vidya service + pop %bp # preserves al + pop %bx + ret + .endfn rvputc + +/ Video put string. +/ +/ @param di is the string +/ @mode real +rvputs: mov %di,%si +0: lodsb + test %al,%al + je 1f + call rvputc + jmp 0b +1: ret + .endfn rvputs,globl,hidden + /*───────────────────────────────────────────────────────────────────────────│─╗ │ αcτµαlly pδrταblε εxεcµταblε § partition table ─╬─│┼ ╚────────────────────────────────────────────────────────────────────────────│*/ @@ -1143,33 +1246,6 @@ rlput2: push %di 1: ret .endfn rlput2,globl,hidden -/ Video put string. -/ -/ @param di is the string -/ @mode real -rvputs: mov %di,%si -0: lodsb - test %al,%al - je 1f - call rvputc - jmp 0b -1: ret - .endfn rvputs,globl,hidden - -/ Video put char. -/ -/ @param al is the char -/ @mode real -rvputc: push %bx # don't clobber bp,bx,di,si,cx - push %bp # original ibm pc scroll up bug - mov $7,%bx # normal mda/cga style page zero - mov $0x0e,%ah # teletype output al cp437 - int $0x10 # vidya service - pop %bp # preserves al - pop %bx - ret - .endfn rvputc - / Writes string to serial line. / / @param di NUL-terminated string @@ -1191,82 +1267,6 @@ sputs: push %bx ret .endfn sputs,globl -/ Waits for serial lines to become idle. -/ -/ @param di short array of serial ports (0 means not present) -/ @param si number of items in array -/ @mode long,legacy,real -sflush: mov %si,%cx - mov %di,%si - xor %dx,%dx -0: lodsb - mov %al,%dl - lodsb - mov %al,%dh - test %ax,%ax - jz 2f - add $UART_LSR,%dx - mov $UART_TTYIDL,%ah -1: in %dx,%al - and %ah,%al - rep - nop - jz 1b - loop 0b -2: ret - .endfn sflush,globl - -/ Transmits byte over serial line. -/ -/ This is both blocking and asynchronous. -/ -/ @param di character to send -/ @param si serial port -/ @mode long,legacy,real -/ @see ttytxr -sputc: push %ax - push %cx - push %dx - mov %si,%dx - add $UART_LSR,%dx - mov $UART_TTYTXR,%ah -1: in %dx,%al - and %ah,%al - jnz 2f - rep - nop - jmp 1b -2: mov %di,%ax - mov %si,%dx - out %al,%dx - pop %dx - pop %cx - pop %ax - ret - .endfn sputc,globl - -/ Shuts down personal computer. -/ -/ @mode real -/ @noreturn -apmoff: mov $0x5300,%ax # apm installation check - xor %bx,%bx # for the apm bios itself - int $APM_SERVICE - jc 1f - cmp $'P<<8|'M,%bx # did apm bios service interrupt? - jne 1f - mov $0x5301,%ax # real mode interface connect - xor %bx,%bx # to apm bios device - int $APM_SERVICE # ignore errors e.g. already connected - xor %bx,%bx - xor %cx,%cx - mov $0x5307,%ax # set power state - mov $1,%bl # for all devices within my dominion - mov $3,%cl # to off - int $APM_SERVICE -1: call panic - .endfn apmoff,globl - /* █ █▒ █ █ █▓▄ █ █ diff --git a/libc/calls/calls.mk b/libc/calls/calls.mk index 9ad430aa..b946b4e6 100644 --- a/libc/calls/calls.mk +++ b/libc/calls/calls.mk @@ -16,10 +16,10 @@ LIBC_CALLS_ARTIFACTS += LIBC_CALLS_A LIBC_CALLS = $(LIBC_CALLS_A_DEPS) $(LIBC_CALLS_A) LIBC_CALLS_A = o/$(MODE)/libc/calls/syscalls.a LIBC_CALLS_A_FILES := \ - $(wildcard libc/calls/*) \ $(wildcard libc/calls/typedef/*) \ $(wildcard libc/calls/thunks/*) \ - $(wildcard libc/calls/struct/*) + $(wildcard libc/calls/struct/*) \ + $(wildcard libc/calls/*) LIBC_CALLS_A_HDRS = $(filter %.h,$(LIBC_CALLS_A_FILES)) LIBC_CALLS_A_SRCS_S = $(filter %.S,$(LIBC_CALLS_A_FILES)) LIBC_CALLS_A_SRCS_C = $(filter %.c,$(LIBC_CALLS_A_FILES)) diff --git a/libc/calls/hefty/fork-nt.c b/libc/calls/hefty/fork-nt.c index 5fd8faf7..21da3c3a 100644 --- a/libc/calls/hefty/fork-nt.c +++ b/libc/calls/hefty/fork-nt.c @@ -43,22 +43,22 @@ static textwindows int64_t ParseInt(char16_t **p) { return x; } -static textwindows void WriteAll(int64_t h, void *buf, size_t n) { +static noinline textwindows void DoAll(int64_t h, void *buf, size_t n, + bool32 (*f)()) { char *p; size_t i; - uint32_t wrote; - for (p = buf, i = 0; i < n; i += wrote) { - WriteFile(h, p + i, n - i, &wrote, NULL); + uint32_t x; + for (p = buf, i = 0; i < n; i += x) { + f(h, p + i, n - i, &x, NULL); } } -static textwindows void ReadAll(int64_t h, void *buf, size_t n) { - char *p; - size_t i; - uint32_t got; - for (p = buf, i = 0; i < n; i += got) { - ReadFile(h, p + i, n - i, &got, NULL); - } +static noinline textwindows void WriteAll(int64_t h, void *buf, size_t n) { + DoAll(h, buf, n, WriteFile); +} + +static noinline textwindows void ReadAll(int64_t h, void *buf, size_t n) { + DoAll(h, buf, n, ReadFile); } textwindows void WinMainForked(void) { diff --git a/libc/calls/ioctl.h b/libc/calls/ioctl.h index e098fa16..4c1bf47a 100644 --- a/libc/calls/ioctl.h +++ b/libc/calls/ioctl.h @@ -16,9 +16,6 @@ int ioctl(int, uint64_t, void *); #include "libc/macros.h" #include "libc/sysv/consts/termios.h" -struct termios; -struct winsize; - #define ioctl(FD, REQUEST, MEMORY) ioctl$dispatch(FD, REQUEST, MEMORY) #define __IOCTL_DISPATCH(CMP, FD, REQUEST, MEMORY) \ diff --git a/libc/calls/termios-internal.h b/libc/calls/termios-internal.h index 2b468576..9d62bbc4 100644 --- a/libc/calls/termios-internal.h +++ b/libc/calls/termios-internal.h @@ -2,13 +2,12 @@ #define COSMOPOLITAN_LIBC_CALLS_TERMIOS_INTERNAL_H_ #ifndef __STRICT_ANSI__ #include "libc/bits/safemacros.h" +#include "libc/calls/struct/metatermios.h" +#include "libc/calls/struct/termios.h" #include "libc/str/str.h" #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -struct termios; -union metatermios; - #define COPY_TERMIOS(TO, FROM) \ do { \ memset((TO), 0, sizeof(*(TO))); \ diff --git a/libc/log/die.c b/libc/log/die.c index 1f456f26..bbd5082d 100644 --- a/libc/log/die.c +++ b/libc/log/die.c @@ -33,7 +33,6 @@ relegated noreturn void die(void) { if (!once) { once = true; if (!IsTiny()) { - g_runstate |= RUNSTATE_BROKEN; if (IsDebuggerPresent(false)) DebugBreak(); ShowBacktrace(STDERR_FILENO, NULL); } diff --git a/libc/log/oncrash.c b/libc/log/oncrash.c index 8fdb46a4..c5a608f8 100644 --- a/libc/log/oncrash.c +++ b/libc/log/oncrash.c @@ -203,7 +203,6 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) { int gdbpid, err; static bool once; err = errno; - g_runstate |= RUNSTATE_BROKEN; if (once) abort(); once = true; /* TODO(jart): Needs translation for ucontext_t and possibly siginfo_t. */ diff --git a/libc/log/ubsan.c b/libc/log/ubsan.c index f9317d0c..f56de0d3 100644 --- a/libc/log/ubsan.c +++ b/libc/log/ubsan.c @@ -53,7 +53,6 @@ void __ubsan_abort(const struct UbsanSourceLocation *loc, } else { abort(); } - g_runstate |= RUNSTATE_BROKEN; if (IsDebuggerPresent(false)) DebugBreak(); __start_fatal(loc->file, loc->line); fprintf(stderr, "%s\r\n", description); diff --git a/libc/nexgen32e/g_runstate.S b/libc/nexgen32e/g_runstate.S deleted file mode 100644 index fc9a8875..00000000 --- a/libc/nexgen32e/g_runstate.S +++ /dev/null @@ -1,23 +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" - - .comm g_runstate,4 - .source __FILE__ diff --git a/libc/nt/iocp.h b/libc/nt/iocp.h index 18bb0b03..b44693cd 100644 --- a/libc/nt/iocp.h +++ b/libc/nt/iocp.h @@ -1,5 +1,7 @@ #ifndef COSMOPOLITAN_LIBC_NT_IOCP_H_ #define COSMOPOLITAN_LIBC_NT_IOCP_H_ +#include "libc/nt/struct/overlapped.h" +#include "libc/nt/struct/overlappedentry.h" #if 0 /* ░░░░ ▒▒▒░░░▒▒▒▒▒▒▒▓▓▓░ @@ -26,8 +28,6 @@ │ 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 diff --git a/libc/runtime/_exit.S b/libc/runtime/_exit.S index 458333d1..b1161d64 100644 --- a/libc/runtime/_exit.S +++ b/libc/runtime/_exit.S @@ -30,7 +30,6 @@ / @asyncsignalsafe _Exit: push %rbp mov %rsp,%rbp - orl $RUNSTATE_TERMINATE,g_runstate(%rip) #if SupportsWindows() testb IsWindows() jz 1f @@ -49,4 +48,3 @@ _Exit: push %rbp 3: .quad 0 .endfn _Exit,globl,protected .hidden __NR_exit - .hidden g_runstate diff --git a/libc/runtime/abort.S b/libc/runtime/abort.S index fe1db2be..ed8d0bf3 100644 --- a/libc/runtime/abort.S +++ b/libc/runtime/abort.S @@ -39,7 +39,6 @@ abort: push %rbp mov %rsp,%rbp and $-16,%rsp sub $16,%rsp - orl $RUNSTATE_BROKEN,g_runstate(%rip) testb IsWindows() jnz 2f mov SIG_SETMASK,%edi @@ -61,4 +60,3 @@ abort: push %rbp 2: mov $134,%edi # exit(128+SIGABRT) [bash-ism] call _Exit .endfn abort,globl,protected - .hidden g_runstate diff --git a/libc/runtime/construct.S b/libc/runtime/construct.S index 2614773e..4006be2e 100644 --- a/libc/runtime/construct.S +++ b/libc/runtime/construct.S @@ -31,7 +31,6 @@ _construct: push %rbp mov %rsp,%rbp - orb $RUNSTATE_INITIALIZED,g_runstate(%rip) ezlea __init_array_start,ax # static ctors in forward order .weak __init_array_start # could be called multiple times ezlea __init_array_end,cx # idempotency recommended diff --git a/libc/runtime/ftrace.greg.c b/libc/runtime/ftrace.greg.c index e5ea3bc5..769baef4 100644 --- a/libc/runtime/ftrace.greg.c +++ b/libc/runtime/ftrace.greg.c @@ -72,7 +72,7 @@ privileged interruptfn void ftrace_hook(void) { const char *symbol; struct StackFrame *frame; LOAD_DEFAULT_RBX(); - if (g_symbols && !(g_runstate & RUNSTATE_BROKEN)) { + if (g_symbols) { frame = __builtin_frame_address(0); symbol = &g_symbols->name_base[g_symbols diff --git a/libc/runtime/init.S b/libc/runtime/init.S index 69ed4b5e..cb205094 100644 --- a/libc/runtime/init.S +++ b/libc/runtime/init.S @@ -50,7 +50,6 @@ .section .initprologue,"ax",@progbits .type _init,@function .globl _init - .comm g_runstate,4 _init: push %rbp mov %rsp,%rbp .profilable diff --git a/libc/runtime/internal.h b/libc/runtime/internal.h index e1bd32ce..24c7a81c 100644 --- a/libc/runtime/internal.h +++ b/libc/runtime/internal.h @@ -4,20 +4,15 @@ #include "libc/dce.h" #include "libc/runtime/runtime.h" -#define RUNSTATE_INITIALIZED (1 << 0) -#define RUNSTATE_BROKEN (1 << 1) -#define RUNSTATE_TERMINATE (1 << 2) - #define STACK_CEIL 0x700000000000ul #define STACK_SIZE FRAMESIZE #if !(__ASSEMBLER__ + __LINKER__ + 0) COSMOPOLITAN_C_START_ -hidden extern bool _mmap_asan_mode; -hidden extern char **g_freebsdhint; -hidden extern unsigned g_runstate; -hidden extern void *g_stacktop; +extern hidden bool _mmap_asan_mode; +extern hidden char **g_freebsdhint; +extern hidden void *g_stacktop; void _init(void) hidden; void _piro(int) hidden; @@ -29,12 +24,6 @@ void _jmpstack(void *, void *, ...) hidden noreturn; long _setstack(void *, void *, ...) hidden; int GetDosArgv(const char16_t *, char *, size_t, char **, size_t) hidden; -forceinline void AssertNeverCalledWhileTerminating(void) { - if (!NoDebug() && (g_runstate & RUNSTATE_TERMINATE)) { - abort(); - } -} - COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* ANSI */ diff --git a/libc/str/memfrob.c b/libc/str/memfrob.c index 45ebbc0b..b8ba8bec 100644 --- a/libc/str/memfrob.c +++ b/libc/str/memfrob.c @@ -24,7 +24,10 @@ * without meeting its requirements concerning secrecy or length. */ void *memfrob(void *buf, size_t size) { - unsigned char *p = (unsigned char *)buf; - for (size_t i = 0; i < size; ++i) p[i] ^= '*'; + size_t i; + unsigned char *p; + for (p = buf, i = 0; i < size; ++i) { + p[i] ^= '*'; + } return buf; } diff --git a/tool/build/ar.c b/tool/build/ar.c index af4d73cc..4e484dd9 100644 --- a/tool/build/ar.c +++ b/tool/build/ar.c @@ -107,16 +107,21 @@ int main(int argc, char *argv[]) { uint8_t *tablebuf; struct iovec iov[7]; const char *symname; + const char *outpath; Elf64_Xword symcount; struct Ints symnames; struct String symbols; struct String filenames; - const char *options, *outpath; struct Header *header1, *header2; size_t wrote, remain, objectargcount; int *offsets, *modes, *sizes, *names; int i, j, fd, err, name, outfd, tablebufsize; + if (!(argc > 2 && strcmp(argv[1], "rcsD") == 0)) { + fprintf(stderr, "%s%s%s\n", "Usage: ", argv[0], " rcsD ARCHIVE FILE..."); + return 1; + } + st = xmalloc(sizeof(struct stat)); symbols.i = 0; symbols.n = 4096; @@ -128,12 +133,9 @@ int main(int argc, char *argv[]) { symnames.n = 1024; symnames.p = xmalloc(symnames.n * sizeof(int)); - CHECK_GT(argc, 3); - options = argv[1]; outpath = argv[2]; objectargs = argv + 3; objectargcount = argc - 3; - CHECK_EQ(0, strcmp(options, "rcsD")); modes = xmalloc(sizeof(int) * objectargcount); names = xmalloc(sizeof(int) * objectargcount); sizes = xmalloc(sizeof(int) * objectargcount); diff --git a/tool/build/emubin/poke.h b/tool/build/emubin/poke.h index 27966d19..5bc451df 100644 --- a/tool/build/emubin/poke.h +++ b/tool/build/emubin/poke.h @@ -4,7 +4,7 @@ COSMOPOLITAN_C_START_ #define POKE(MEM, VAL) \ - asm volatile("mov\t%1,%%es:%0" : "=m"(MEM) : "Qi"((typeof(MEM))(VAL))) + asm volatile("mov%z0\t%1,%%es:%0" : "=m"(MEM) : "Qi"((typeof(MEM))(VAL))) COSMOPOLITAN_C_END_ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ diff --git a/tool/tags/tags.y b/tool/tags/tags.y index a913a915..5869a5ae 100644 --- a/tool/tags/tags.y +++ b/tool/tags/tags.y @@ -43,7 +43,7 @@ %token_prefix TK_ %token_type {struct Token} -%type expr {long double} +%default_type {struct Token} %syntax_error { SyntaxError(); } %token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN. diff --git a/tool/viz/life.c b/tool/viz/life.c new file mode 100644 index 00000000..d1d110d5 --- /dev/null +++ b/tool/viz/life.c @@ -0,0 +1,527 @@ +/*-*- 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/bits/bits.h" +#include "libc/bits/safemacros.h" +#include "libc/calls/calls.h" +#include "libc/calls/ioctl.h" +#include "libc/calls/struct/stat.h" +#include "libc/calls/struct/termios.h" +#include "libc/calls/struct/winsize.h" +#include "libc/calls/termios-internal.h" +#include "libc/conv/conv.h" +#include "libc/dce.h" +#include "libc/errno.h" +#include "libc/log/log.h" +#include "libc/macros.h" +#include "libc/mem/mem.h" +#include "libc/runtime/runtime.h" +#include "libc/stdio/stdio.h" +#include "libc/str/str.h" +#include "libc/str/tpenc.h" +#include "libc/sysv/consts/termios.h" +#include "libc/x/x.h" + +/** + * @fileoverview Conway's Game of Life + * + * The Game of Life, also known simply as Life, is a cellular automaton + * devised by the British mathematician John Horton Conway in 1970. It + * is Turing complete and can simulate a universal constructor or any + * other Turing machine. + * + * This program may be used by dragging the mouse in the terminal. Left + * mouse draws or erases cells. Right mouse scrolls or moves the plane. + * The space bar may be held down to move time forward. + * + * This implementation uses bitboards on an n×m wraparound plane. For an + * explanation of how this technique works on an 8×8 plane see "Bitboard + * Methods for Games" by Cameron Browne. + */ + +#define INTERRUPTED 1 +#define RESIZED 2 + +#define MOUSE_LEFT_DOWN 0 +#define MOUSE_MIDDLE_DOWN 1 +#define MOUSE_RIGHT_DOWN 2 +#define MOUSE_LEFT_UP 4 +#define MOUSE_MIDDLE_UP 5 +#define MOUSE_RIGHT_UP 6 +#define MOUSE_LEFT_DRAG 32 +#define MOUSE_MIDDLE_DRAG 33 +#define MOUSE_RIGHT_DRAG 34 +#define MOUSE_WHEEL_UP 64 +#define MOUSE_WHEEL_DOWN 65 +#define MOUSE_CTRL_WHEEL_UP 80 +#define MOUSE_CTRL_WHEEL_DOWN 81 + +#define LEFT 0x0101010101010101ul +#define RIGHT 0x8080808080808080ul +#define TOP 0x00000000000000FFul +#define BOTTOM 0xFF00000000000000ul + +#define CTRL(C) ((C) ^ 0100) +#define GOUP(x) ((x) >> 8) +#define GODOWN(x) ((x) << 8) +#define GORIGHT(x) (((x) & ~RIGHT) << 1) +#define GOLEFT(x) (((x) & ~LEFT) >> 1) +#define LEFTMOST(x) ((x)&LEFT) +#define RIGHTMOST(x) ((x)&RIGHT) +#define TOPMOST(x) ((x)&TOP) +#define BOTMOST(x) ((x)&BOTTOM) + +#define ADD(X) \ + do { \ + uint64_t c1, c2; \ + c1 = r[0] & (X); \ + c2 = r[1] & c1; \ + r[0] ^= (X); \ + r[1] ^= c1; \ + r[2] |= c2; \ + } while (0) + +#define STEP(RES, B00, B01, B02, B10, B11, B12, B20, B21, B22) \ + do { \ + uint64_t r[3] = {0}; \ + ADD(GORIGHT(GODOWN(B11)) | GORIGHT(BOTMOST(B01) >> 56) | \ + GODOWN(RIGHTMOST(B10) >> 7) | BOTMOST(RIGHTMOST(B00)) >> 7 >> 56); \ + ADD(GORIGHT(B11) | RIGHTMOST(B10) >> 7); \ + ADD(GORIGHT(GOUP(B11)) | GORIGHT(TOPMOST(B21) << 56) | \ + GOUP(RIGHTMOST(B10) >> 7) | TOPMOST(RIGHTMOST(B20)) >> 7 << 56); \ + ADD(GODOWN(B11) | BOTMOST(B01) >> 56); \ + ADD(GOUP(B11) | TOPMOST(B21) << 56); \ + ADD(GOLEFT(GODOWN(B11)) | GOLEFT(BOTMOST(B01) >> 56) | \ + GODOWN(LEFTMOST(B12) << 7) | BOTMOST(LEFTMOST(B02)) << 7 >> 56); \ + ADD(GOLEFT(B11) | LEFTMOST(B12) << 7); \ + ADD(GOLEFT(GOUP(B11)) | GOLEFT(TOPMOST(B21) << 56) | \ + GOUP(LEFTMOST(B12) << 7) | TOPMOST(LEFTMOST(B22)) << 7 << 56); \ + RES = (B11 | r[0]) & r[1] & ~r[2]; \ + } while (0) + +struct Buffer { + unsigned i, n; + char *p; +}; + +static bool erase; +static bool natural; +static bool mousemode; + +static int out; +static int action; + +static long top; +static long bottom; +static long left; +static long right; +static long tyn; +static long txn; +static long byn; +static long bxn; +static long save_y; +static long save_x; +static long save_top; +static long save_left; + +static uint64_t *board; + +static struct Buffer buffer; +static struct termios oldterm; + +static int Write(const char *s) { + return write(out, s, strlen(s)); +} + +static void HideCursor(void) { + Write("\e[?25l"); +} + +static void ShowCursor(void) { + Write("\e[?25h"); +} + +static void EnableMouse(void) { + mousemode = true; + Write("\e[?1000;1002;1015;1006h"); +} + +static void DisableMouse(void) { + mousemode = false; + Write("\e[?1000;1002;1015;1006l"); +} + +static void LeaveScreen(void) { + Write("\e[H\e[J"); +} + +static void GetTtySize(void) { + struct winsize wsize; + wsize.ws_row = tyn; + wsize.ws_col = txn; + getttysize(out, &wsize); + tyn = wsize.ws_row; + txn = wsize.ws_col; + right = left + txn; + bottom = top + tyn; +} + +static void EnableRaw(void) { + struct termios term; + 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; + ioctl(out, TCSETS, &term); +} + +static void OnExit(void) { + LeaveScreen(); + ShowCursor(); + DisableMouse(); + ioctl(out, TCSETS, &oldterm); +} + +static void OnSigInt(int sig, struct siginfo *sa, struct ucontext *uc) { + action |= INTERRUPTED; +} + +static void OnSigWinch(int sig, struct siginfo *sa, struct ucontext *uc) { + action |= RESIZED; +} + +static bool Test(long y, long x) { + return (board[(bxn >> 3) * (y >> 3) + (x >> 3)] >> + (((y & 7) << 3) + (x & 7))) & + 1; +} + +static void Set(long y, long x) { + board[(bxn >> 3) * (y >> 3) + (x >> 3)] |= 1ul << (((y & 7) << 3) + (x & 7)); +} + +static void Unset(long y, long x) { + board[(bxn >> 3) * (y >> 3) + (x >> 3)] &= + ~(1ul << (((y & 7) << 3) + (x & 7))); +} + +static void Setup(void) { + out = 1; + tyn = 80; + txn = 24; + byn = 64 * 4; + bxn = 64 * 8; + board = xcalloc((byn * bxn) >> 6, 8); + GetTtySize(); + top = byn / 2 - tyn / 2; + left = bxn / 2 - txn / 2; + right = left + txn; + bottom = top + tyn; + ioctl(out, TCGETS, &oldterm); + HideCursor(); + EnableRaw(); + EnableMouse(); + atexit(OnExit); + sigaction(SIGINT, &(struct sigaction){.sa_sigaction = OnSigInt}, NULL); + sigaction(SIGWINCH, &(struct sigaction){.sa_sigaction = OnSigWinch}, NULL); +} + +static void AppendData(char *data, unsigned len) { + char *p; + unsigned n; + if (buffer.i + len + 1 > buffer.n) { + n = MAX(buffer.i + len + 1, MAX(16, buffer.n + (buffer.n >> 1))); + if (!(p = realloc(buffer.p, n))) return; + buffer.p = p; + buffer.n = n; + } + memcpy(buffer.p + buffer.i, data, len); + buffer.p[buffer.i += len] = 0; +} + +static void AppendChar(char c) { + AppendData(&c, 1); +} + +static void AppendStr(const char *s) { + AppendData(s, strlen(s)); +} + +static void AppendWide(wint_t wc) { + unsigned i; + uint64_t wb; + char buf[8]; + i = 0; + wb = tpenc(wc); + do { + buf[i++] = wb & 0xFF; + wb >>= 8; + } while (wb); + AppendData(buf, i); +} + +static void Move(long dy, long dx) { + top = top + dy; + bottom = bottom + dy; + left = left + dx; + right = right + dx; +} + +static void OnUp(void) { + Move(-1, 0); +} + +static void OnDown(void) { + Move(+1, 0); +} + +static void OnLeft(void) { + Move(0, -1); +} + +static void OnRight(void) { + Move(0, +1); +} + +static void Generation(void) { + uint64_t *board2; + long y, x, yn, xn, yp, ym, xp, xm; + yn = byn >> 3; + xn = bxn >> 3; + board2 = xmalloc(yn * xn * 8); + for (y = 0; y < yn; ++y) { + for (x = 0; x < xn; ++x) { + ym = y ? y - 1 : yn - 1; + yp = y + 1 < yn ? y + 1 : 0; + xm = x ? x - 1 : xn - 1; + xp = x + 1 < xn ? x + 1 : 0; + STEP(board2[y * xn + x], board[ym * xn + xm], board[ym * xn + x], + board[ym * xn + xp], board[y * xn + xm], board[y * xn + x], + board[y * xn + xp], board[yp * xn + xm], board[yp * xn + x], + board[yp * xn + xp]); + } + } + free(board); + board = board2; +} + +static void OnMouseLeftDrag(long y, long x) { + if (y == save_y && x == save_x) return; + save_y = y; + save_x = x; + y += top; + x += left; + if (y < 0 || y >= byn) return; + if (x < 0 || x >= bxn) return; + if (erase) { + Unset(y, x); + } else { + Set(y, x); + } +} + +static void OnMouseLeftDown(long y, long x) { + save_y = y; + save_x = x; + y += top; + x += left; + erase = false; + if (y < 0 || y >= byn) return; + if (x < 0 || x >= bxn) return; + if ((erase = Test(y, x))) { + Unset(y, x); + } else { + Set(y, x); + } +} + +static void OnMouseRightDown(long y, long x) { + save_y = y; + save_x = x; + save_top = top; + save_left = left; +} + +static void OnMouseRightDrag(long y, long x) { + long dy, dx, h, w; + dy = save_y - y; + dx = save_x - x; + if (natural) { + dy = -dy; + dx = -dx; + } + h = bottom - top; + w = right - left; + top = save_top + dy; + left = save_left + dx; + bottom = top + h; + right = left + w; +} + +static void OnMouse(char *p) { + int e, x, y; + 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; + switch (e) { + case MOUSE_WHEEL_UP: + if (natural) { + OnDown(); + OnDown(); + OnDown(); + } else { + OnUp(); + OnUp(); + OnUp(); + } + break; + case MOUSE_WHEEL_DOWN: + if (natural) { + OnUp(); + OnUp(); + OnUp(); + } else { + OnDown(); + OnDown(); + OnDown(); + } + break; + case MOUSE_RIGHT_DOWN: + OnMouseRightDown(y, x); + break; + case MOUSE_RIGHT_DRAG: + OnMouseRightDrag(y, x); + break; + case MOUSE_LEFT_DOWN: + OnMouseLeftDown(y, x); + break; + case MOUSE_LEFT_DRAG: + OnMouseLeftDrag(y, x); + break; + default: + break; + } +} + +static void ReadKeyboard(void) { + char buf[32], *p = buf; + memset(buf, 0, sizeof(buf)); + if (readansi(0, buf, sizeof(buf)) == -1) { + if (errno == EINTR) return; + exit(errno); + } + switch (*p++) { + case 'q': + exit(0); + case ' ': + case 's': + Generation(); + break; + case 'k': + case CTRL('P'): + OnUp(); + break; + case 'j': + case CTRL('N'): + OnDown(); + break; + case 'M': + if (mousemode) { + DisableMouse(); + } else { + EnableMouse(); + } + break; + case '\e': + switch (*p++) { + case '[': + switch (*p++) { + case '<': + OnMouse(p); + break; + case 'A': + OnUp(); + break; + case 'B': + OnDown(); + break; + case 'D': + OnLeft(); + break; + case 'C': + OnRight(); + break; + default: + break; + } + break; + default: + break; + } + break; + default: + break; + } +} + +static void Draw(void) { + long y, x; + buffer.i = 0; + AppendStr("\e[H"); + for (y = top; y < bottom; ++y) { + if (y > top) AppendStr("\e[K\r\n"); + for (x = left; x < right; ++x) { + if ((0 <= y && y < byn) && (0 <= x && x < bxn)) { + if (Test(y, x)) { + AppendWide(u'█'); + } else { + AppendChar(' '); + } + } else { + AppendWide(u'∙'); + } + } + } + write(out, buffer.p, buffer.i); +} + +static void Life(void) { + do { + if (action & RESIZED) { + GetTtySize(); + action &= ~RESIZED; + } + Draw(); + ReadKeyboard(); + } while (!(action & INTERRUPTED)); +} + +int main(int argc, char *argv[]) { + if (!NoDebug()) showcrashreports(); + Setup(); + Life(); + return 0; +} diff --git a/tool/viz/memzoom.c b/tool/viz/memzoom.c index 8a48b96e..f36a2164 100644 --- a/tool/viz/memzoom.c +++ b/tool/viz/memzoom.c @@ -446,7 +446,6 @@ static void OnPrevEnd(void) { static void OnMouse(char *p) { int e, x, y; - struct Panel *ep; e = strtol(p, &p, 10); if (*p == ';') ++p; x = min(txn, max(1, strtol(p, &p, 10))) - 1; @@ -843,13 +842,7 @@ static void RangesZoom(void) { } static void MemZoom(void) { - char *p; - int c, fg2, rc, fg; - long i, n, r, w, y, x, got, have; do { - if (action & INTERRUPTED) { - break; - } if (action & RESIZED) { GetTtySize(); SetupCanvas();