Add Conway's Game of Life

main
Justine Tunney 2020-11-18 08:26:03 -08:00
parent db33973e0a
commit dba7552c1e
22 changed files with 664 additions and 186 deletions

206
ape/ape.S
View File

@ -364,6 +364,109 @@ pcread: push %ax
jmp 1b jmp 1b
.endfn pcread .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 αcτµαlly pδrταblε εxεcµταblε § partition table
*/ */
@ -1143,33 +1246,6 @@ rlput2: push %di
1: ret 1: ret
.endfn rlput2,globl,hidden .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. / Writes string to serial line.
/ /
/ @param di NUL-terminated string / @param di NUL-terminated string
@ -1191,82 +1267,6 @@ sputs: push %bx
ret ret
.endfn sputs,globl .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
/* /*

View File

@ -16,10 +16,10 @@ LIBC_CALLS_ARTIFACTS += LIBC_CALLS_A
LIBC_CALLS = $(LIBC_CALLS_A_DEPS) $(LIBC_CALLS_A) LIBC_CALLS = $(LIBC_CALLS_A_DEPS) $(LIBC_CALLS_A)
LIBC_CALLS_A = o/$(MODE)/libc/calls/syscalls.a LIBC_CALLS_A = o/$(MODE)/libc/calls/syscalls.a
LIBC_CALLS_A_FILES := \ LIBC_CALLS_A_FILES := \
$(wildcard libc/calls/*) \
$(wildcard libc/calls/typedef/*) \ $(wildcard libc/calls/typedef/*) \
$(wildcard libc/calls/thunks/*) \ $(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_HDRS = $(filter %.h,$(LIBC_CALLS_A_FILES))
LIBC_CALLS_A_SRCS_S = $(filter %.S,$(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)) LIBC_CALLS_A_SRCS_C = $(filter %.c,$(LIBC_CALLS_A_FILES))

View File

@ -43,22 +43,22 @@ static textwindows int64_t ParseInt(char16_t **p) {
return x; 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; char *p;
size_t i; size_t i;
uint32_t wrote; uint32_t x;
for (p = buf, i = 0; i < n; i += wrote) { for (p = buf, i = 0; i < n; i += x) {
WriteFile(h, p + i, n - i, &wrote, NULL); f(h, p + i, n - i, &x, NULL);
} }
} }
static textwindows void ReadAll(int64_t h, void *buf, size_t n) { static noinline textwindows void WriteAll(int64_t h, void *buf, size_t n) {
char *p; DoAll(h, buf, n, WriteFile);
size_t i; }
uint32_t got;
for (p = buf, i = 0; i < n; i += got) { static noinline textwindows void ReadAll(int64_t h, void *buf, size_t n) {
ReadFile(h, p + i, n - i, &got, NULL); DoAll(h, buf, n, ReadFile);
}
} }
textwindows void WinMainForked(void) { textwindows void WinMainForked(void) {

View File

@ -16,9 +16,6 @@ int ioctl(int, uint64_t, void *);
#include "libc/macros.h" #include "libc/macros.h"
#include "libc/sysv/consts/termios.h" #include "libc/sysv/consts/termios.h"
struct termios;
struct winsize;
#define ioctl(FD, REQUEST, MEMORY) ioctl$dispatch(FD, REQUEST, MEMORY) #define ioctl(FD, REQUEST, MEMORY) ioctl$dispatch(FD, REQUEST, MEMORY)
#define __IOCTL_DISPATCH(CMP, FD, REQUEST, MEMORY) \ #define __IOCTL_DISPATCH(CMP, FD, REQUEST, MEMORY) \

View File

@ -2,13 +2,12 @@
#define COSMOPOLITAN_LIBC_CALLS_TERMIOS_INTERNAL_H_ #define COSMOPOLITAN_LIBC_CALLS_TERMIOS_INTERNAL_H_
#ifndef __STRICT_ANSI__ #ifndef __STRICT_ANSI__
#include "libc/bits/safemacros.h" #include "libc/bits/safemacros.h"
#include "libc/calls/struct/metatermios.h"
#include "libc/calls/struct/termios.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
struct termios;
union metatermios;
#define COPY_TERMIOS(TO, FROM) \ #define COPY_TERMIOS(TO, FROM) \
do { \ do { \
memset((TO), 0, sizeof(*(TO))); \ memset((TO), 0, sizeof(*(TO))); \

View File

@ -33,7 +33,6 @@ relegated noreturn void die(void) {
if (!once) { if (!once) {
once = true; once = true;
if (!IsTiny()) { if (!IsTiny()) {
g_runstate |= RUNSTATE_BROKEN;
if (IsDebuggerPresent(false)) DebugBreak(); if (IsDebuggerPresent(false)) DebugBreak();
ShowBacktrace(STDERR_FILENO, NULL); ShowBacktrace(STDERR_FILENO, NULL);
} }

View File

@ -203,7 +203,6 @@ relegated void __oncrash(int sig, struct siginfo *si, ucontext_t *ctx) {
int gdbpid, err; int gdbpid, err;
static bool once; static bool once;
err = errno; err = errno;
g_runstate |= RUNSTATE_BROKEN;
if (once) abort(); if (once) abort();
once = true; once = true;
/* TODO(jart): Needs translation for ucontext_t and possibly siginfo_t. */ /* TODO(jart): Needs translation for ucontext_t and possibly siginfo_t. */

View File

@ -53,7 +53,6 @@ void __ubsan_abort(const struct UbsanSourceLocation *loc,
} else { } else {
abort(); abort();
} }
g_runstate |= RUNSTATE_BROKEN;
if (IsDebuggerPresent(false)) DebugBreak(); if (IsDebuggerPresent(false)) DebugBreak();
__start_fatal(loc->file, loc->line); __start_fatal(loc->file, loc->line);
fprintf(stderr, "%s\r\n", description); fprintf(stderr, "%s\r\n", description);

View File

@ -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__

View File

@ -1,5 +1,7 @@
#ifndef COSMOPOLITAN_LIBC_NT_IOCP_H_ #ifndef COSMOPOLITAN_LIBC_NT_IOCP_H_
#define 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 #if 0
/* ░░░░ /* ░░░░
@ -26,8 +28,6 @@
cosmopolitan § new technology » i/o completion ports cosmopolitan § new technology » i/o completion ports
*/ */
#endif #endif
#include "libc/nt/struct/overlapped.h"
#include "libc/nt/struct/overlappedentry.h"
#define kNtFileSkipCompletionPortOnSuccess 1 #define kNtFileSkipCompletionPortOnSuccess 1
#define kNtFileSkipSetEventOnHandle 2 #define kNtFileSkipSetEventOnHandle 2

View File

@ -30,7 +30,6 @@
/ @asyncsignalsafe / @asyncsignalsafe
_Exit: push %rbp _Exit: push %rbp
mov %rsp,%rbp mov %rsp,%rbp
orl $RUNSTATE_TERMINATE,g_runstate(%rip)
#if SupportsWindows() #if SupportsWindows()
testb IsWindows() testb IsWindows()
jz 1f jz 1f
@ -49,4 +48,3 @@ _Exit: push %rbp
3: .quad 0 3: .quad 0
.endfn _Exit,globl,protected .endfn _Exit,globl,protected
.hidden __NR_exit .hidden __NR_exit
.hidden g_runstate

View File

@ -39,7 +39,6 @@ abort: push %rbp
mov %rsp,%rbp mov %rsp,%rbp
and $-16,%rsp and $-16,%rsp
sub $16,%rsp sub $16,%rsp
orl $RUNSTATE_BROKEN,g_runstate(%rip)
testb IsWindows() testb IsWindows()
jnz 2f jnz 2f
mov SIG_SETMASK,%edi mov SIG_SETMASK,%edi
@ -61,4 +60,3 @@ abort: push %rbp
2: mov $134,%edi # exit(128+SIGABRT) [bash-ism] 2: mov $134,%edi # exit(128+SIGABRT) [bash-ism]
call _Exit call _Exit
.endfn abort,globl,protected .endfn abort,globl,protected
.hidden g_runstate

View File

@ -31,7 +31,6 @@
_construct: _construct:
push %rbp push %rbp
mov %rsp,%rbp mov %rsp,%rbp
orb $RUNSTATE_INITIALIZED,g_runstate(%rip)
ezlea __init_array_start,ax # static ctors in forward order ezlea __init_array_start,ax # static ctors in forward order
.weak __init_array_start # could be called multiple times .weak __init_array_start # could be called multiple times
ezlea __init_array_end,cx # idempotency recommended ezlea __init_array_end,cx # idempotency recommended

View File

@ -72,7 +72,7 @@ privileged interruptfn void ftrace_hook(void) {
const char *symbol; const char *symbol;
struct StackFrame *frame; struct StackFrame *frame;
LOAD_DEFAULT_RBX(); LOAD_DEFAULT_RBX();
if (g_symbols && !(g_runstate & RUNSTATE_BROKEN)) { if (g_symbols) {
frame = __builtin_frame_address(0); frame = __builtin_frame_address(0);
symbol = symbol =
&g_symbols->name_base[g_symbols &g_symbols->name_base[g_symbols

View File

@ -50,7 +50,6 @@
.section .initprologue,"ax",@progbits .section .initprologue,"ax",@progbits
.type _init,@function .type _init,@function
.globl _init .globl _init
.comm g_runstate,4
_init: push %rbp _init: push %rbp
mov %rsp,%rbp mov %rsp,%rbp
.profilable .profilable

View File

@ -4,20 +4,15 @@
#include "libc/dce.h" #include "libc/dce.h"
#include "libc/runtime/runtime.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_CEIL 0x700000000000ul
#define STACK_SIZE FRAMESIZE #define STACK_SIZE FRAMESIZE
#if !(__ASSEMBLER__ + __LINKER__ + 0) #if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
hidden extern bool _mmap_asan_mode; extern hidden bool _mmap_asan_mode;
hidden extern char **g_freebsdhint; extern hidden char **g_freebsdhint;
hidden extern unsigned g_runstate; extern hidden void *g_stacktop;
hidden extern void *g_stacktop;
void _init(void) hidden; void _init(void) hidden;
void _piro(int) hidden; void _piro(int) hidden;
@ -29,12 +24,6 @@ void _jmpstack(void *, void *, ...) hidden noreturn;
long _setstack(void *, void *, ...) hidden; long _setstack(void *, void *, ...) 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)) {
abort();
}
}
COSMOPOLITAN_C_END_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* ANSI */ #endif /* ANSI */

View File

@ -24,7 +24,10 @@
* without meeting its requirements concerning secrecy or length. * without meeting its requirements concerning secrecy or length.
*/ */
void *memfrob(void *buf, size_t size) { void *memfrob(void *buf, size_t size) {
unsigned char *p = (unsigned char *)buf; size_t i;
for (size_t i = 0; i < size; ++i) p[i] ^= '*'; unsigned char *p;
for (p = buf, i = 0; i < size; ++i) {
p[i] ^= '*';
}
return buf; return buf;
} }

View File

@ -107,16 +107,21 @@ int main(int argc, char *argv[]) {
uint8_t *tablebuf; uint8_t *tablebuf;
struct iovec iov[7]; struct iovec iov[7];
const char *symname; const char *symname;
const char *outpath;
Elf64_Xword symcount; Elf64_Xword symcount;
struct Ints symnames; struct Ints symnames;
struct String symbols; struct String symbols;
struct String filenames; struct String filenames;
const char *options, *outpath;
struct Header *header1, *header2; struct Header *header1, *header2;
size_t wrote, remain, objectargcount; size_t wrote, remain, objectargcount;
int *offsets, *modes, *sizes, *names; int *offsets, *modes, *sizes, *names;
int i, j, fd, err, name, outfd, tablebufsize; 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)); st = xmalloc(sizeof(struct stat));
symbols.i = 0; symbols.i = 0;
symbols.n = 4096; symbols.n = 4096;
@ -128,12 +133,9 @@ int main(int argc, char *argv[]) {
symnames.n = 1024; symnames.n = 1024;
symnames.p = xmalloc(symnames.n * sizeof(int)); symnames.p = xmalloc(symnames.n * sizeof(int));
CHECK_GT(argc, 3);
options = argv[1];
outpath = argv[2]; outpath = argv[2];
objectargs = argv + 3; objectargs = argv + 3;
objectargcount = argc - 3; objectargcount = argc - 3;
CHECK_EQ(0, strcmp(options, "rcsD"));
modes = xmalloc(sizeof(int) * objectargcount); modes = xmalloc(sizeof(int) * objectargcount);
names = xmalloc(sizeof(int) * objectargcount); names = xmalloc(sizeof(int) * objectargcount);
sizes = xmalloc(sizeof(int) * objectargcount); sizes = xmalloc(sizeof(int) * objectargcount);

View File

@ -4,7 +4,7 @@
COSMOPOLITAN_C_START_ COSMOPOLITAN_C_START_
#define POKE(MEM, VAL) \ #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_ COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */ #endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */

View File

@ -43,7 +43,7 @@
%token_prefix TK_ %token_prefix TK_
%token_type {struct Token} %token_type {struct Token}
%type expr {long double} %default_type {struct Token}
%syntax_error { SyntaxError(); } %syntax_error { SyntaxError(); }
%token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN. %token AUTO BREAK CASE CHAR CONST CONTINUE DEFAULT DO DOUBLE ELSE ENUM EXTERN.

527
tool/viz/life.c 100644
View File

@ -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;
}

View File

@ -446,7 +446,6 @@ static void OnPrevEnd(void) {
static void OnMouse(char *p) { static void OnMouse(char *p) {
int e, x, y; int e, x, y;
struct Panel *ep;
e = strtol(p, &p, 10); e = strtol(p, &p, 10);
if (*p == ';') ++p; if (*p == ';') ++p;
x = min(txn, max(1, strtol(p, &p, 10))) - 1; x = min(txn, max(1, strtol(p, &p, 10))) - 1;
@ -843,13 +842,7 @@ static void RangesZoom(void) {
} }
static void MemZoom(void) { static void MemZoom(void) {
char *p;
int c, fg2, rc, fg;
long i, n, r, w, y, x, got, have;
do { do {
if (action & INTERRUPTED) {
break;
}
if (action & RESIZED) { if (action & RESIZED) {
GetTtySize(); GetTtySize();
SetupCanvas(); SetupCanvas();