Push
This commit is contained in:
commit
15d842b82d
51
Makefile
Normal file
51
Makefile
Normal file
|
@ -0,0 +1,51 @@
|
|||
ARCH = $(shell uname -m)
|
||||
# no-builtin: do not generate memcpy() calls
|
||||
# no-stack-protector: do not attempt to load SSP canary from TLS
|
||||
# no-unwind-tables: avoid eh_frame sections on AArch64
|
||||
# no-asynchronous-unwind-tables: avoid eh_frame sections
|
||||
CFLAGS = -Os -march=native -std=c11 -pipe -Wall -Wconversion \
|
||||
-fno-builtin \
|
||||
-fno-jump-tables \
|
||||
-fno-stack-protector \
|
||||
-ffunction-sections \
|
||||
-fno-unwind-tables \
|
||||
-fno-asynchronous-unwind-tables
|
||||
CPPFLAGS = -Iinclude
|
||||
LD ?= ld.gold
|
||||
LDFLAGS = -s -N -O3 --gc-sections
|
||||
|
||||
ifdef DEBUG
|
||||
CFLAGS += -O0 -g -fasynchronous-unwind-tables
|
||||
LDFLAGS =
|
||||
endif
|
||||
|
||||
ifeq ($(ARCH), i686)
|
||||
CPPFLAGS += -DARCH_386
|
||||
else ifeq ($(ARCH), aarch64)
|
||||
CPPFLAGS += -DARCH_aarch64
|
||||
else
|
||||
CFLAGS += -m64
|
||||
LDFLAGS += -melf_x86_64
|
||||
CPPFLAGS += -DARCH_amd64
|
||||
ARCH = amd64
|
||||
endif
|
||||
|
||||
LIB_OBJS = lib/print.o lib/time.o lib/int64.o lib/random.o lib/stub.o
|
||||
LIB_OBJS += lib/$(ARCH)/skel.o lib/$(ARCH)/sys.o lib/$(ARCH)/sys_time.o
|
||||
|
||||
all: main.bin mandelbrot.bin random.bin randomline.bin tinytris.bin
|
||||
|
||||
%.bin: %.o stdlib.a
|
||||
$(LD) $(LDFLAGS) -o $@ $< stdlib.a
|
||||
|
||||
%.o: %.s
|
||||
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ -c $<
|
||||
|
||||
stdlib.a: $(LIB_OBJS)
|
||||
ar cru stdlib.a $(LIB_OBJS)
|
||||
|
||||
format:
|
||||
clang-format -i *.c include/*.h lib/*.c
|
||||
|
||||
clean:
|
||||
rm -f *.bin *.o lib/*.o lib/*/*.o stdlib.a
|
19
include/print.h
Normal file
19
include/print.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef PRINT_H
|
||||
#define PRINT_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define print(x) \
|
||||
_Generic((x), int16_t \
|
||||
: printint, int32_t \
|
||||
: printint, int64_t \
|
||||
: printint, uint16_t \
|
||||
: printuint, uint32_t \
|
||||
: printuint, uint64_t \
|
||||
: printuint, char*: printstr)(x)
|
||||
|
||||
void printint(int64_t n);
|
||||
void printuint(uint64_t n);
|
||||
void printstr(const char *s);
|
||||
|
||||
#endif
|
13
include/random.h
Normal file
13
include/random.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef RANDOM_H
|
||||
#define RANDOM_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
typedef struct {
|
||||
int64_t seed;
|
||||
} RandGen;
|
||||
|
||||
RandGen randinit();
|
||||
int64_t randnext(RandGen *g);
|
||||
|
||||
#endif
|
23
include/sys.h
Normal file
23
include/sys.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef SYS_H
|
||||
#define SYS_H
|
||||
|
||||
#include <types.h>
|
||||
|
||||
int read(int fd, const char *buffer, int size);
|
||||
int write(int fd, const char *buffer, int size);
|
||||
int ioctl(int fd, int cmd, void *args);
|
||||
_Noreturn void exit(int code);
|
||||
|
||||
int select(int n, void *r, void *w, void *e, struct UTime *tv);
|
||||
|
||||
void nanosleep(struct NTime *t, struct NTime *rem);
|
||||
void usleep(unsigned long usecs);
|
||||
|
||||
int gettimeofday(struct UTime *t, struct Timezone *tz);
|
||||
int64_t utime();
|
||||
|
||||
// ioctl constants
|
||||
#define TCGETS 0x5401
|
||||
#define TCSETSF 0x5404
|
||||
|
||||
#endif
|
22
include/types.h
Normal file
22
include/types.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
struct UTime {
|
||||
size_t secs;
|
||||
size_t usecs;
|
||||
};
|
||||
|
||||
struct NTime {
|
||||
size_t secs;
|
||||
size_t nsecs;
|
||||
};
|
||||
|
||||
struct Timezone {
|
||||
int tz_minuteswest;
|
||||
int tz_dsttime;
|
||||
};
|
||||
|
||||
#endif
|
7
lib/aarch64/skel.s
Normal file
7
lib/aarch64/skel.s
Normal file
|
@ -0,0 +1,7 @@
|
|||
.text
|
||||
.globl _start
|
||||
|
||||
_start:
|
||||
bl main // returns to x0
|
||||
mov x8, 94 // SYS_exit_group
|
||||
svc 0
|
29
lib/aarch64/sys.s
Normal file
29
lib/aarch64/sys.s
Normal file
|
@ -0,0 +1,29 @@
|
|||
.section .text.read
|
||||
.globl read
|
||||
read:
|
||||
mov x8, 0x3f // SYS_read
|
||||
svc 0
|
||||
ret
|
||||
|
||||
// write(int fd=r0, char *data=r1, int data_len=r2)
|
||||
.section .text.write
|
||||
.globl write
|
||||
write:
|
||||
mov x8, 0x40 // SYS_write
|
||||
svc 0
|
||||
ret
|
||||
|
||||
.section .text.ioctl
|
||||
.globl ioctl
|
||||
ioctl:
|
||||
mov x8, 29 // SYS_ioctl
|
||||
svc 0
|
||||
ret
|
||||
|
||||
.section .text.exit
|
||||
.globl exit
|
||||
exit:
|
||||
mov x8, 94 // SYS_exit_group
|
||||
svc 0
|
||||
ret
|
||||
|
24
lib/aarch64/sys_time.s
Normal file
24
lib/aarch64/sys_time.s
Normal file
|
@ -0,0 +1,24 @@
|
|||
// pselect6(int n, void *read_fds, void *write_fds, void *err_fds, void *tv, void *sigmask)
|
||||
.section .text.pselect6
|
||||
.globl pselect6
|
||||
pselect6:
|
||||
mov x8, 0x48 // SYS_pselect6
|
||||
svc 0
|
||||
ret
|
||||
|
||||
// nanosleep(timespec *t, timespec *rem)
|
||||
.section .text.nanosleep
|
||||
.globl nanosleep
|
||||
nanosleep:
|
||||
mov x8, 0x65 // SYS_nanosleep
|
||||
svc 0
|
||||
ret
|
||||
|
||||
// gettimeofday(timeval *t, timezone *tz)
|
||||
.section .text.gettimeofday
|
||||
.globl gettimeofday
|
||||
gettimeofday:
|
||||
mov x8, 0xa9
|
||||
svc 0
|
||||
ret
|
||||
|
8
lib/amd64/memset.s
Normal file
8
lib/amd64/memset.s
Normal file
|
@ -0,0 +1,8 @@
|
|||
// fill(long *dest, long value, long length)
|
||||
.global fill
|
||||
fill:
|
||||
mov %rsi, %rax
|
||||
mov %rdx, %rcx
|
||||
rep stosq
|
||||
ret
|
||||
|
BIN
lib/amd64/skel.o
Normal file
BIN
lib/amd64/skel.o
Normal file
Binary file not shown.
8
lib/amd64/skel.s
Normal file
8
lib/amd64/skel.s
Normal file
|
@ -0,0 +1,8 @@
|
|||
.text
|
||||
.globl _start
|
||||
|
||||
_start:
|
||||
call main
|
||||
mov %rax, %rdi
|
||||
call exit
|
||||
|
BIN
lib/amd64/sys.o
Normal file
BIN
lib/amd64/sys.o
Normal file
Binary file not shown.
35
lib/amd64/sys.s
Normal file
35
lib/amd64/sys.s
Normal file
|
@ -0,0 +1,35 @@
|
|||
.section .text.read
|
||||
|
||||
.globl read
|
||||
read:
|
||||
// fd in %edi, data in %rsi, data_len in %edx
|
||||
movl $0, %eax
|
||||
syscall
|
||||
ret
|
||||
|
||||
.section .text.write
|
||||
|
||||
// write(int fd, char *data, int data_len)
|
||||
.globl write
|
||||
write:
|
||||
// fd in %edi, data in %rsi, data_len in %edx
|
||||
movl $1, %eax
|
||||
syscall
|
||||
ret
|
||||
|
||||
.section .text.ioctl
|
||||
|
||||
.globl ioctl
|
||||
ioctl:
|
||||
movl $16, %eax
|
||||
syscall
|
||||
ret
|
||||
|
||||
.section .text.exit
|
||||
|
||||
// exit(int code)
|
||||
.globl exit
|
||||
exit:
|
||||
mov $231, %eax
|
||||
syscall
|
||||
|
BIN
lib/amd64/sys_time.o
Normal file
BIN
lib/amd64/sys_time.o
Normal file
Binary file not shown.
33
lib/amd64/sys_time.s
Normal file
33
lib/amd64/sys_time.s
Normal file
|
@ -0,0 +1,33 @@
|
|||
.section .text.select
|
||||
|
||||
// select
|
||||
// int n %edi
|
||||
// void *read_fds %rsi
|
||||
// void *write_fds %rdx
|
||||
// void *err_fds %rcx
|
||||
// void *tv %r8
|
||||
.globl select
|
||||
select:
|
||||
movl $23, %eax
|
||||
movq %rcx, %r10
|
||||
syscall
|
||||
ret
|
||||
|
||||
.section .text.nanosleep
|
||||
|
||||
// nanosleep(timespec *t, timespec *rem)
|
||||
.globl nanosleep
|
||||
nanosleep:
|
||||
movl $35, %eax
|
||||
syscall
|
||||
ret
|
||||
|
||||
.section .text.gettimeofday
|
||||
|
||||
// gettimeofday(timeval *t, timezone *tz)
|
||||
.globl gettimeofday
|
||||
gettimeofday:
|
||||
movl $96, %eax
|
||||
syscall
|
||||
ret
|
||||
|
9
lib/i386/skel.s
Normal file
9
lib/i386/skel.s
Normal file
|
@ -0,0 +1,9 @@
|
|||
.text
|
||||
.globl _start
|
||||
|
||||
_start:
|
||||
call main
|
||||
movl %eax, %ebx
|
||||
movl $1, %eax
|
||||
int $0x80
|
||||
|
39
lib/i386/sys.s
Normal file
39
lib/i386/sys.s
Normal file
|
@ -0,0 +1,39 @@
|
|||
.text
|
||||
|
||||
.globl read
|
||||
read:
|
||||
movl $3, %eax
|
||||
movl 4(%esp), %ebx
|
||||
movl 8(%esp), %ecx
|
||||
movl 12(%esp), %edx
|
||||
int $0x80
|
||||
ret
|
||||
|
||||
// write(int fd, char *data, int data_len)
|
||||
.globl write
|
||||
write:
|
||||
movl $4, %eax
|
||||
movl 4(%esp), %ebx
|
||||
movl 8(%esp), %ecx
|
||||
movl 12(%esp), %edx
|
||||
int $0x80
|
||||
ret
|
||||
|
||||
// ioctl(int fd, int cmd, void *args);
|
||||
.globl ioctl
|
||||
ioctl:
|
||||
movl $0x36, %eax
|
||||
movl 4(%esp), %ebx
|
||||
movl 8(%esp), %ecx
|
||||
movl 12(%esp), %edx
|
||||
int $0x80
|
||||
ret
|
||||
|
||||
// exit(int code)
|
||||
.globl exit
|
||||
exit:
|
||||
mov $0xfc, %eax
|
||||
movl 4(%esp), %ebx
|
||||
int $0x80
|
||||
ret
|
||||
|
32
lib/i386/sys_time.s
Normal file
32
lib/i386/sys_time.s
Normal file
|
@ -0,0 +1,32 @@
|
|||
.text
|
||||
|
||||
// select(int n, void *read_fds, void *write_fds, void *err_fds, void *tv)
|
||||
.globl select
|
||||
select:
|
||||
movl $142, %eax
|
||||
movl 4(%esp), %ebx
|
||||
movl 8(%esp), %ecx
|
||||
movl 12(%esp), %edx
|
||||
movl 16(%esp), %esi
|
||||
movl 20(%esp), %edi
|
||||
int $0x80
|
||||
ret
|
||||
|
||||
// nanosleep(timespec *t, timespec *rem)
|
||||
.globl nanosleep
|
||||
nanosleep:
|
||||
movl $162, %eax
|
||||
movl 4(%esp), %ebx
|
||||
movl 8(%esp), %ecx
|
||||
int $0x80
|
||||
ret
|
||||
|
||||
// gettimeofday(timeval *t, timezone *tz)
|
||||
.globl gettimeofday
|
||||
gettimeofday:
|
||||
movl $78, %eax
|
||||
movl 4(%esp), %ebx
|
||||
movl 8(%esp), %ecx
|
||||
int $0x80
|
||||
ret
|
||||
|
64
lib/int64.c
Normal file
64
lib/int64.c
Normal file
|
@ -0,0 +1,64 @@
|
|||
#if ARCH_386
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef size_t uint;
|
||||
|
||||
static void quotrem64(a, b, q, r) uint64_t a, b;
|
||||
uint64_t *q, *r;
|
||||
{
|
||||
double qfloat;
|
||||
|
||||
// Special case
|
||||
if (a < b) {
|
||||
*q = 0;
|
||||
if (r != NULL)
|
||||
*r = a;
|
||||
}
|
||||
|
||||
// Use float for approximation
|
||||
qfloat = (double)a / (double)b;
|
||||
if (b >> 16) {
|
||||
// Quotient has less than 48 bits
|
||||
*q = (uint64_t)qfloat;
|
||||
if (r != NULL)
|
||||
*r = a - b * (*q);
|
||||
} else {
|
||||
uint64_t qapprox, rem;
|
||||
qapprox = (uint64_t)qfloat;
|
||||
rem = a - b * qapprox;
|
||||
*q = qapprox + (uint64_t)((uint)rem / (uint)b);
|
||||
if (r != NULL)
|
||||
*r = (uint)rem % (uint)b;
|
||||
}
|
||||
}
|
||||
|
||||
#define ONE_FOURTH (1 << 30)
|
||||
#define ONE_HALF (ONE_FOURTH * 2.0)
|
||||
#define ONE (ONE_FOURTH * 4.0)
|
||||
|
||||
uint64_t __fixunsdfdi(double x) {
|
||||
double upper = x / ONE;
|
||||
uint64_t result = (uint)upper;
|
||||
result <<= 32;
|
||||
while (result > x)
|
||||
result -= (1ULL << 32);
|
||||
x -= (double)result;
|
||||
result += (uint)x;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t __divdi3(uint64_t a, uint64_t b) {
|
||||
uint64_t q;
|
||||
quotrem64(a, b, &q, NULL);
|
||||
return q;
|
||||
}
|
||||
|
||||
uint64_t __moddi3(uint64_t a, uint64_t b) {
|
||||
uint64_t q, r;
|
||||
quotrem64(a, b, &q, &r);
|
||||
return r;
|
||||
}
|
||||
|
||||
#endif
|
BIN
lib/int64.o
Normal file
BIN
lib/int64.o
Normal file
Binary file not shown.
54
lib/print.c
Normal file
54
lib/print.c
Normal file
|
@ -0,0 +1,54 @@
|
|||
#include <stdint.h>
|
||||
#include <sys.h>
|
||||
|
||||
void printuint(uint64_t n) {
|
||||
char digits[20];
|
||||
int x = 19;
|
||||
uint64_t hi = 0;
|
||||
const uint64_t N_1E9 = 1000000000;
|
||||
while (n) {
|
||||
if (n >= N_1E9) {
|
||||
hi = n / N_1E9;
|
||||
uint32_t lo = (uint32_t)(n % N_1E9);
|
||||
for (int i = 0; i < 9; i++) {
|
||||
uint32_t digit = lo % 10;
|
||||
lo = lo / 10;
|
||||
digits[x] = '0' + (char)digit;
|
||||
x--;
|
||||
}
|
||||
n = hi;
|
||||
} else {
|
||||
uint32_t lo = (uint32_t)n;
|
||||
uint32_t digit = lo % 10;
|
||||
lo = lo / 10;
|
||||
digits[x] = '0' + (char)digit;
|
||||
x--;
|
||||
n = lo;
|
||||
}
|
||||
}
|
||||
if (x == 19) {
|
||||
digits[x] = '0';
|
||||
x--;
|
||||
}
|
||||
write(1, &digits[x + 1], 19 - x);
|
||||
}
|
||||
|
||||
void printint(int64_t n) {
|
||||
if (n < 0) {
|
||||
char minus = '-';
|
||||
write(1, &minus, 1);
|
||||
printuint((uint64_t)(-n));
|
||||
} else {
|
||||
printuint((uint64_t)(n));
|
||||
}
|
||||
}
|
||||
|
||||
void printstr(const char *s) {
|
||||
char *p = s;
|
||||
int l = 0;
|
||||
while (*p) {
|
||||
l++;
|
||||
p++;
|
||||
}
|
||||
write(1, s, l);
|
||||
}
|
BIN
lib/print.o
Normal file
BIN
lib/print.o
Normal file
Binary file not shown.
20
lib/random.c
Normal file
20
lib/random.c
Normal file
|
@ -0,0 +1,20 @@
|
|||
#include <random.h>
|
||||
#include <sys.h>
|
||||
|
||||
RandGen randinit();
|
||||
int64_t randnext(RandGen *g);
|
||||
|
||||
RandGen randinit() {
|
||||
RandGen g;
|
||||
g.seed = utime();
|
||||
return g;
|
||||
}
|
||||
|
||||
int64_t randnext(RandGen *g) {
|
||||
int64_t ret = g->seed;
|
||||
ret = 0x5DEECE66D * ret + 0xB;
|
||||
ret &= 0xffffffffffff;
|
||||
g->seed = ret;
|
||||
ret = ret ^ (ret >> 16) ^ (ret >> 32);
|
||||
return ret;
|
||||
}
|
BIN
lib/random.o
Normal file
BIN
lib/random.o
Normal file
Binary file not shown.
3
lib/stub.c
Normal file
3
lib/stub.c
Normal file
|
@ -0,0 +1,3 @@
|
|||
#include <sys.h>
|
||||
|
||||
void __stack_chk_fail() { write(2, "stack overflow\n", 15); }
|
BIN
lib/stub.o
Normal file
BIN
lib/stub.o
Normal file
Binary file not shown.
26
lib/time.c
Normal file
26
lib/time.c
Normal file
|
@ -0,0 +1,26 @@
|
|||
#include <sys.h>
|
||||
|
||||
void usleep(unsigned long usecs) {
|
||||
struct NTime t;
|
||||
t.secs = usecs / 1000000;
|
||||
t.nsecs = 1000 * (usecs % 1000000);
|
||||
nanosleep(&t, NULL);
|
||||
}
|
||||
|
||||
int64_t utime() {
|
||||
struct UTime t;
|
||||
gettimeofday(&t, NULL);
|
||||
return t.secs * 1000000 + t.usecs;
|
||||
}
|
||||
|
||||
#if ARCH_aarch64
|
||||
// emulate select with pselect6
|
||||
|
||||
int pselect6(int nfds, void *r, void *w, void *e, struct NTime *t, void *p);
|
||||
|
||||
int select(int nfds, void *r, void *w, void *e, struct UTime *t) {
|
||||
struct NTime nt = {t->secs, 1000 * t->usecs};
|
||||
int ret = pselect6(nfds, r, w, e, &nt, NULL);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
BIN
lib/time.o
Normal file
BIN
lib/time.o
Normal file
Binary file not shown.
6
main.c
Normal file
6
main.c
Normal file
|
@ -0,0 +1,6 @@
|
|||
#include "sys.h"
|
||||
|
||||
int main() {
|
||||
write(1, "Hello world!\n", 13);
|
||||
return 17;
|
||||
}
|
BIN
mandelbrot.bin
Executable file
BIN
mandelbrot.bin
Executable file
Binary file not shown.
76
mandelbrot.c
Normal file
76
mandelbrot.c
Normal file
|
@ -0,0 +1,76 @@
|
|||
#include "sys.h"
|
||||
|
||||
#define ROWS 25
|
||||
#define COLS 80
|
||||
#define ASPECT 1.6
|
||||
#define VERBATIM(s) #s
|
||||
#define AS_STRING(s) VERBATIM(s)
|
||||
|
||||
const char *header = "\r\e[" AS_STRING(ROWS) "A";
|
||||
|
||||
static int test(c1, c2)
|
||||
double c1, c2;
|
||||
{
|
||||
double x, y;
|
||||
x = y = 0;
|
||||
for (int n = 0; n < 1000; n++) {
|
||||
double newx = x * x - y * y + c1;
|
||||
double newy = 2 * x * y + c2;
|
||||
x = newx;
|
||||
y = newy;
|
||||
if ((x * x + y * y) > 10.0) {
|
||||
return n / 124;
|
||||
}
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
|
||||
// double refx = 0.3245046418497685;
|
||||
// double refy = 0.04855101129280834;
|
||||
|
||||
// double refx = 0.3215000630401344;
|
||||
// double refy = 0.04855009999999;
|
||||
|
||||
// double refx = 0.32158;
|
||||
// double refy = 0.048;
|
||||
|
||||
double refx = 0.32151002;
|
||||
double refy = 0.04800001;
|
||||
|
||||
char buffer[32768];
|
||||
|
||||
int main() {
|
||||
int pos = 0;
|
||||
double scale = 2;
|
||||
double x, y;
|
||||
for (int i = 0; i < 610; i++) {
|
||||
y = refy + scale;
|
||||
for (int i = 0; i < ROWS; i++) {
|
||||
x = refx - scale * ASPECT;
|
||||
y -= 2 * scale / (double)(ROWS);
|
||||
for (int j = 0; j < COLS; j++) {
|
||||
x += 2 * scale / (double)(COLS)*ASPECT;
|
||||
int r = test(x, y);
|
||||
buffer[pos++] = '\e';
|
||||
buffer[pos++] = '[';
|
||||
buffer[pos++] = '4';
|
||||
buffer[pos++] = r + '0';
|
||||
buffer[pos++] = 'm';
|
||||
buffer[pos++] = ' ';
|
||||
}
|
||||
buffer[pos++] = '\e';
|
||||
buffer[pos++] = '[';
|
||||
buffer[pos++] = '0';
|
||||
buffer[pos++] = 'm';
|
||||
buffer[pos++] = '\n';
|
||||
}
|
||||
write(1, buffer, pos);
|
||||
usleep(30000);
|
||||
scale *= 0.95;
|
||||
// Rewind cursor
|
||||
pos = 0;
|
||||
for (const char *p = header; *p; p++)
|
||||
buffer[pos++] = *p;
|
||||
}
|
||||
return 0;
|
||||
}
|
BIN
random.bin
Executable file
BIN
random.bin
Executable file
Binary file not shown.
19
random.c
Normal file
19
random.c
Normal file
|
@ -0,0 +1,19 @@
|
|||
#include <print.h>
|
||||
#include <random.h>
|
||||
#include <sys.h>
|
||||
|
||||
int main() {
|
||||
RandGen g = randinit();
|
||||
int i;
|
||||
int64_t n;
|
||||
int buckets[19];
|
||||
|
||||
for (i = 0; i < 3000000; i++) {
|
||||
n = randnext(&g);
|
||||
buckets[n % 19] += 1;
|
||||
}
|
||||
for (i = 0; i < 19; i++) {
|
||||
print(buckets[i]);
|
||||
print("\n");
|
||||
}
|
||||
}
|
BIN
randomline.bin
Executable file
BIN
randomline.bin
Executable file
Binary file not shown.
60
randomline.c
Normal file
60
randomline.c
Normal file
|
@ -0,0 +1,60 @@
|
|||
#include <memory.h>
|
||||
#include <random.h>
|
||||
#include <sys.h>
|
||||
|
||||
#define BUFSIZE 4096
|
||||
|
||||
static void copybytes(char *dest, const char *src, int length) {
|
||||
register const char *p;
|
||||
const char *end = src + length;
|
||||
for (p = src; p < end; p++)
|
||||
*dest++ = *p;
|
||||
}
|
||||
|
||||
// Copy a line including the terminating \n and returns the length.
|
||||
static int readline(char *dest, int destsize) {
|
||||
static char buffer[BUFSIZE];
|
||||
static int pos;
|
||||
if (pos < BUFSIZE / 2) {
|
||||
int n_read = read(0, buffer + pos, BUFSIZE - pos);
|
||||
if ((n_read == 0) && (pos == 0)) {
|
||||
write(2, "End of file.\n", 13);
|
||||
return -1;
|
||||
}
|
||||
pos += n_read;
|
||||
}
|
||||
|
||||
for (const char *p = buffer; p < buffer + pos; p++) {
|
||||
if (*p == '\n') {
|
||||
p++;
|
||||
int linelength = p - buffer;
|
||||
copybytes(dest, buffer, linelength);
|
||||
copybytes(buffer, p, BUFSIZE - linelength);
|
||||
pos -= linelength;
|
||||
return linelength;
|
||||
}
|
||||
}
|
||||
write(2, "Buffer capacity exceeded\n", 25);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int main() {
|
||||
char selected[4096];
|
||||
char buffer[4096];
|
||||
int lineno = 0;
|
||||
int selsize = 0;
|
||||
RandGen g = randinit();
|
||||
for (;;) {
|
||||
int len = readline(buffer, 4096);
|
||||
if (len <= 0)
|
||||
break;
|
||||
lineno++;
|
||||
int64_t rnd = randnext(&g);
|
||||
if (rnd % lineno == 0) {
|
||||
copybytes(selected, buffer, len);
|
||||
selsize = len;
|
||||
}
|
||||
}
|
||||
write(1, selected, selsize);
|
||||
return 0;
|
||||
}
|
BIN
tinytris.bin
Executable file
BIN
tinytris.bin
Executable file
Binary file not shown.
289
tinytris.c
Normal file
289
tinytris.c
Normal file
|
@ -0,0 +1,289 @@
|
|||
#include <random.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys.h>
|
||||
#include <termios.h>
|
||||
|
||||
#define FRAME_DURATION_USECS 200000
|
||||
#define WIDTH 24
|
||||
|
||||
// contains ' ' or '#'
|
||||
char board[10 * 20];
|
||||
char screen[WIDTH * 21];
|
||||
|
||||
struct state {
|
||||
uint16_t score;
|
||||
int x, y;
|
||||
uint32_t shape, angle;
|
||||
uint16_t shpbits;
|
||||
RandGen g;
|
||||
|
||||
char lastkey;
|
||||
} state;
|
||||
|
||||
uint16_t getshape(uint32_t idx, uint32_t angle) {
|
||||
int shift1 = 16 * (angle & 1);
|
||||
int shift2 = 12 * (angle & 3);
|
||||
|
||||
static const uint32_t shapes_sym[3] = {
|
||||
0x444400F0ul,
|
||||
// ## #
|
||||
// ## ##
|
||||
// #
|
||||
0x02310036ul,
|
||||
// ## #
|
||||
// ## ##
|
||||
// #
|
||||
0x01320063ul,
|
||||
};
|
||||
|
||||
static const uint64_t shapes_asym[3] = {
|
||||
// 270 262 072 232
|
||||
// ### # # #
|
||||
// # ## ### ##
|
||||
// # #
|
||||
0x270262072232ull,
|
||||
// 170 622 074 223
|
||||
// ### ## # #
|
||||
// # # ### #
|
||||
// # ##
|
||||
0x170622074223ull,
|
||||
// 470 226 071 322
|
||||
0x470226071322ull,
|
||||
};
|
||||
|
||||
switch (idx) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
return (shapes_sym[idx - 1] >> shift1) & 0xffff;
|
||||
case 4:
|
||||
return 0x0660;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
return (shapes_asym[idx - 5] >> shift2) & 0xfff;
|
||||
default:
|
||||
exit(43); // impossible
|
||||
}
|
||||
}
|
||||
|
||||
static bool fits(int px, int py) {
|
||||
for (int bit = 0; bit < 16; bit++) {
|
||||
int sx = px + bit % 4;
|
||||
int sy = py + bit / 4;
|
||||
if (((state.shpbits >> bit) & 1) == 0)
|
||||
continue;
|
||||
if (sx < 0 || sx >= 10)
|
||||
return false;
|
||||
if (sy < 0 || sy >= 20)
|
||||
return false;
|
||||
if (board[10 * sy + sx] != ' ')
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void copy8(char *dst, const char *src) {
|
||||
*(uint64_t *)(dst) = *(uint64_t *)(src);
|
||||
}
|
||||
|
||||
static inline void copy4(char *dst, const char *src) {
|
||||
*(uint32_t *)(dst) = *(uint32_t *)(src);
|
||||
}
|
||||
|
||||
static inline void copy2(char *dst, const char *src) {
|
||||
*(uint16_t *)(dst) = *(uint16_t *)(src);
|
||||
}
|
||||
|
||||
static void init_board() {
|
||||
for (int i = 0; i < 10 * 20; i++)
|
||||
board[i] = ' ';
|
||||
}
|
||||
|
||||
// Find complete lines and pop them.
|
||||
static void poplines(struct state *st) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
bool win = true;
|
||||
for (int j = 0; j < 10; j++) {
|
||||
if (board[10 * i + j] != '#')
|
||||
win = false;
|
||||
}
|
||||
if (win) {
|
||||
st->score++;
|
||||
// slide down
|
||||
for (int k = i; k > 0; k--) {
|
||||
copy8(&board[10 * k], &board[10 * (k - 1)]);
|
||||
copy2(&board[10 * k + 8], &board[10 * (k - 1) + 8]);
|
||||
}
|
||||
// top line
|
||||
for (int j = 0; j < 10; j++)
|
||||
board[j] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void init_screen() {
|
||||
for (int i = 0; i <= 20; i++) {
|
||||
for (int j = 0; j < WIDTH; j++) {
|
||||
char c = ' ';
|
||||
if (j == 10)
|
||||
c = '|';
|
||||
if (i == 20 && j <= 10)
|
||||
c = '-';
|
||||
if (j == WIDTH - 1)
|
||||
c = '\n';
|
||||
screen[WIDTH * i + j] = c;
|
||||
}
|
||||
}
|
||||
// help
|
||||
const char s0[8] = " LINES ";
|
||||
copy8(&screen[WIDTH * 7 + 13], s0);
|
||||
const char s2[8] = "2 down ";
|
||||
copy8(&screen[WIDTH * 11 + 13], s2);
|
||||
const char s4[4] = "4 <-";
|
||||
copy4(&screen[WIDTH * 12 + 13], s4);
|
||||
const char s5[8] = "5 rotate";
|
||||
copy8(&screen[WIDTH * 13 + 13], s5);
|
||||
const char s6[4] = "6 ->";
|
||||
copy4(&screen[WIDTH * 14 + 13], s6);
|
||||
const char sq[8] = "q quit ";
|
||||
copy8(&screen[WIDTH * 15 + 13], sq);
|
||||
}
|
||||
|
||||
void update_screen(struct state *st) {
|
||||
for (int i = 0; i < 20; i++) {
|
||||
// copy 10 bytes
|
||||
*(uint64_t *)(&screen[WIDTH * i]) = *(uint64_t *)(&board[10 * i]);
|
||||
*(uint16_t *)(&screen[WIDTH * i + 8]) = *(uint16_t *)(&board[10 * i + 8]);
|
||||
}
|
||||
// draw shape
|
||||
for (int bit = 0; bit < 16; bit++) {
|
||||
int sx = st->x + bit % 4;
|
||||
int sy = st->y + bit / 4;
|
||||
if ((st->shpbits >> bit) & 1)
|
||||
screen[WIDTH * sy + sx] = '@';
|
||||
}
|
||||
// show score on line 8:
|
||||
uint16_t score = st->score;
|
||||
if (score >= 1000)
|
||||
score = 999;
|
||||
char score_str[4] = {
|
||||
' ',
|
||||
score >= 100 ? '0' + (score / 100) : ' ',
|
||||
score >= 10 ? '0' + ((score / 10) % 10) : ' ',
|
||||
'0' + (score % 10),
|
||||
};
|
||||
copy4(&screen[WIDTH * 8 + 14], score_str);
|
||||
}
|
||||
|
||||
static void rewind() {
|
||||
const char esc[6] = "\r\e[21A";
|
||||
write(1, esc, 6);
|
||||
}
|
||||
|
||||
static void show_screen() { write(1, screen, WIDTH * 21); }
|
||||
|
||||
static void newshape(struct state *st) {
|
||||
while (1) {
|
||||
uint64_t rnd = (uint64_t)randnext(&st->g);
|
||||
uint32_t s = (rnd >> 16) % 8;
|
||||
uint32_t s2 = (rnd >> 28) % 4;
|
||||
if (s > 0) {
|
||||
st->x = 3;
|
||||
st->y = 0;
|
||||
st->shape = s;
|
||||
st->angle = s2;
|
||||
st->shpbits = getshape(s, s2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void next_frame(struct state *st) {
|
||||
// Wait for next frame or next key.
|
||||
struct UTime dt = {0, FRAME_DURATION_USECS};
|
||||
// Bitmask for stdin
|
||||
uint64_t readmask = 1;
|
||||
select(1, &readmask, NULL, NULL, &dt);
|
||||
if (readmask & 1) {
|
||||
char key;
|
||||
read(0, &key, 1);
|
||||
st->lastkey = key;
|
||||
} else {
|
||||
st->lastkey = ' ';
|
||||
}
|
||||
// handle controls
|
||||
const int x = st->x;
|
||||
const int y = st->y;
|
||||
switch (st->lastkey) {
|
||||
case '4':
|
||||
if (fits(x - 1, y))
|
||||
st->x--;
|
||||
return;
|
||||
case '5':
|
||||
st->angle++;
|
||||
st->shpbits = getshape(st->shape, st->angle);
|
||||
if (!fits(x, y)) {
|
||||
st->angle--;
|
||||
st->shpbits = getshape(st->shape, st->angle);
|
||||
}
|
||||
case '6':
|
||||
if (fits(x + 1, y))
|
||||
st->x++;
|
||||
return;
|
||||
case 'q':
|
||||
exit(0);
|
||||
}
|
||||
// move shape down
|
||||
if (fits(x, y + 1))
|
||||
st->y++;
|
||||
else {
|
||||
// freeze shape
|
||||
if (fits(x, y)) {
|
||||
for (int bit = 0; bit < 16; bit++) {
|
||||
int sx = st->x + bit % 4;
|
||||
int sy = st->y + bit / 4;
|
||||
if (((st->shpbits >> bit) & 1) == 0)
|
||||
continue;
|
||||
board[10 * sy + sx] = '#';
|
||||
}
|
||||
} else {
|
||||
// impossible
|
||||
exit(42);
|
||||
}
|
||||
poplines(st);
|
||||
newshape(st);
|
||||
|
||||
if (!fits(st->x, st->y)) {
|
||||
write(1, "GAME OVER\n", 10);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void setup_tty() {
|
||||
// Deactivate echo and buffering.
|
||||
struct termios st;
|
||||
ioctl(0, TCGETS, &st);
|
||||
st.c_lflag &= ~(uint32_t)(ICANON | ECHO);
|
||||
ioctl(0, TCSETSF, &st);
|
||||
}
|
||||
|
||||
int main() {
|
||||
state.lastkey = ' ';
|
||||
state.score = 0;
|
||||
setup_tty();
|
||||
init_board();
|
||||
init_screen();
|
||||
show_screen();
|
||||
newshape(&state);
|
||||
while (1) {
|
||||
update_screen(&state);
|
||||
rewind();
|
||||
show_screen();
|
||||
next_frame(&state);
|
||||
}
|
||||
return 0;
|
||||
}
|
Loading…
Reference in a new issue