This commit is contained in:
zk⁷ 2024-04-06 21:02:47 +03:00
commit 15d842b82d
No known key found for this signature in database
GPG Key ID: 7C45AB244B221FF2
39 changed files with 969 additions and 0 deletions

51
Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

8
lib/amd64/skel.s Normal file
View File

@ -0,0 +1,8 @@
.text
.globl _start
_start:
call main
mov %rax, %rdi
call exit

BIN
lib/amd64/sys.o Normal file

Binary file not shown.

35
lib/amd64/sys.s Normal file
View 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

Binary file not shown.

33
lib/amd64/sys_time.s Normal file
View 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
View 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
View 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
View 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
View 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

Binary file not shown.

54
lib/print.c Normal file
View 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

Binary file not shown.

20
lib/random.c Normal file
View 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

Binary file not shown.

3
lib/stub.c Normal file
View File

@ -0,0 +1,3 @@
#include <sys.h>
void __stack_chk_fail() { write(2, "stack overflow\n", 15); }

BIN
lib/stub.o Normal file

Binary file not shown.

26
lib/time.c Normal file
View 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

Binary file not shown.

BIN
main.bin Executable file

Binary file not shown.

6
main.c Normal file
View File

@ -0,0 +1,6 @@
#include "sys.h"
int main() {
write(1, "Hello world!\n", 13);
return 17;
}

BIN
mandelbrot.bin Executable file

Binary file not shown.

76
mandelbrot.c Normal file
View 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

Binary file not shown.

19
random.c Normal file
View 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

Binary file not shown.

60
randomline.c Normal file
View 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
stdlib.a Normal file

Binary file not shown.

BIN
tinytris.bin Executable file

Binary file not shown.

289
tinytris.c Normal file
View 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;
}