アセンブリ言語でlifegameを書いた
気合いで書きました。
なんの変哲もないlifegameなのでそれ単体ではあまり面白くないですが、以前befungeで書いたものと見比べると少し面白いかもしれません。
実行
$ nasm -f elf32 a.asm
$ ld -m elf_i386 a.o
$ ./a.out
実装
section .rodata
h equ 25
w equ 80
swapped_space db 0x1b, "[7m", " ", 0x1b, "[0m", 0x0
table db 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, \
0, 0, 0, 1, 1, 0, 0, 0, 0, 0
section .bss
buf:
resb h*w
section .text
global _start
putc:
push ebp
mov ebp, esp
sub esp, 0x4
mov eax, [esp + 0xc]
mov [esp], al
mov eax, 0x4 ; write
mov ebx, 1 ; stdout
mov ecx, esp
mov edx, 1
int 0x80
leave
ret
strlen:
push ebp
mov ebp, esp
xor eax, eax
mov ebx, [esp + 0x8]
.loop:
mov cl, [ebx]
test cl, cl
je .break
inc eax
inc ebx
jmp .loop
.break:
leave
ret
putstr: ; puts without newline
push ebp
mov ebp, esp
push dword [esp + 0x8]
call strlen
mov edx, eax
mov ecx, [esp]
mov ebx, 1 ; stdout
mov eax, 0x4 ; write
int 0x80
leave
ret
sleep:
push ebp
mov ebp, esp
sub esp, 0x8
mov eax, [esp + 0x10]
mov dword [esp], eax
mov dword [esp + 0x4], 0
mov eax, 0xa2 ; nanosleep
mov ebx, esp
mov ecx, 0x0 ; NULL
int 0x80
leave
ret
_start:
mov eax, 0x37 ; fcntl
mov ebx, 0 ; stdin
mov ecx, 3; F_GETFL
int 0x80
mov edx, eax
mov eax, 0x37 ; fcntl
mov ebx, 0 ; stdin
mov ecx, 4 ; F_SETFL
or edx, 04000 ; O_NONBLOCK
int 0x80
call init
call initscr
.loop:
mov eax, 0x3 ; read
mov ebx, 0 ; stdin
push 0
mov ecx, esp ; buf
mov edx, 1
int 0x80
pop ebx
cmp eax, 1
je .break ; if read
; main process
call print
call update
push 1
call sleep
add esp, 0x4
jmp .loop
.break:
mov eax, 1 ; exit
mov ebx, 0 ; EXIT_SUCCESS
int 0x80
init:
push ebp
mov ebp, esp
sub esp, 0x8
rdtsc
xor ecx, ecx
.loop:
cmp ecx, h*w
je .break
; set buf[y][x]
xor ebx, ebx
test eax, 0x00100000
je .skip
inc ebx
.skip:
mov [buf + ecx], ebx
; update random, linear congruential generator
mov edx, 134775813 ; Borland Delphi, Virtual Pascal, https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
mul edx
inc eax
inc ecx
jmp .loop
.break:
leave
ret
initscr:
push ebp
mov ebp, esp
push 0
.loop:
mov eax, [esp]
cmp eax, h-1
je .break
inc eax
mov [esp], eax
push 0xa ; newline
call putc
add esp, 0x4
jmp .loop
.break:
leave
ret
update:
push ebp
mov ebp, esp
sub esp, h*w + 0x18
mov dword [ebp - 4], 0
.loop_i:
mov eax, [ebp - 4]
xor edx, edx
mov ebx, w
div ebx
mov [ebp - 12], eax ; y
mov [ebp - 16], edx ; x
xor ecx, ecx
mov dword [ebp - 8], 0
.loop_j:
mov eax, [ebp - 8]
xor edx, edx
mov ebx, 3
div ebx
dec eax
dec edx
add eax, [ebp - 12] ; y'
add edx, [ebp - 16] ; x'
mov [ebp - 20], eax ; y'
mov [ebp - 24], edx ; x'
cmp eax, -1
jne .skip_uf_y
mov eax, h-1
.skip_uf_y:
cmp eax, h
jne .skip_of_y
mov eax, 0
.skip_of_y:
mov [ebp - 20], eax
mov eax, [ebp - 24] ; x'
cmp eax, -1
jne .skip_uf_x
mov eax, w-1
.skip_uf_x:
cmp eax, w
jne .skip_of_x
mov eax, 0
.skip_of_x:
mov [ebp - 24], eax
mov eax, [ebp - 20]
mov ebx, w
mul ebx
add eax, [ebp - 24]
xor ebx, ebx
add bl, [buf + eax] ; count
add ecx, ebx
mov eax, [ebp - 8]
inc eax
mov [ebp - 8], eax
cmp eax, 9
jne .loop_j
.done_j:
mov eax, [ebp - 4]
xor ebx, ebx
mov bl, [buf + eax]
mov eax, 10
mul ebx
add eax, ecx
mov eax, [table + eax]
mov ebx, [ebp - 4]
add ebx, esp
mov [ebx], al
mov eax, [ebp - 4]
inc eax
mov [ebp - 4], eax
cmp eax, h*w
jne .loop_i
.done_i:
mov ecx, h*w
mov esi, esp
mov edi, buf
pushf
rep movsb
popf
leave
ret
print:
push ebp
mov ebp, esp
push 0x1b ; <esc>
call putc
mov byte [esp], 0x5b ; [
call putc
add esp, 0x4
xor edx, edx
mov eax, h-1
mov ebx, 10
div ebx
add edx, 0x30
add eax, 0x30
push edx
cmp eax, 0x30
je .skip
push eax
call putc ; h / 10
add esp, 0x4
.skip:
call putc ; h % 10
mov byte [esp], 0x46 ; F
call putc
add esp, 0x4
push -1
.loop:
mov eax, [esp]
inc eax
mov [esp], eax
mov bl, [buf + eax]
test bl, bl
je .else
push swapped_space
call putstr
add esp, 0x4
jmp .fi
.else:
push 0x20 ; <space>
call putc
add esp, 0x4
.fi:
mov eax, [esp]
cmp eax, h*w-1
je .break
xor edx, edx
mov ebx, w
div ebx
cmp edx, w - 1
jne .next
push 0xa ; newline
call putc
add esp, 0x4
.next:
jmp .loop
.break:
leave
ret
参考
コメント代わりに後から書いたコード。
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#define H 25
#define W 80
#define repeat(i,n) for (int (i) = 0; (i) < (n); ++(i))
bool buf[H][W];
void init() {
srand(time(NULL));
unsigned a = rand();
repeat (y,H) {
repeat (x,W) {
buf[y][x] = (bool)(a & 0x00100000);
a = a * 134775813 + 1;
}
}
}
void initscr() {
repeat (y,H-1) {
putchar('\n');
}
}
void print() {
printf("\x1b[%dF", H-1);
repeat (i,H*W) {
int y = i / W;
int x = i % W;
if (buf[y][x]) {
printf("\x1b[7m \x1b[0m");
} else {
putchar(' ');
}
if (y != H-1 && x == W-1) putchar('\n');
}
}
const bool table[2][9] =
{ { 0,0,0,1,0,0,0,0,0 }
, { 0,0,0,1,1,0,0,0,0 } };
void update() {
bool tmp[H][W];
repeat (i,H*W) {
int y = i / W;
int x = i % W;
int count = 0;
repeat (j,9) {
int dy = j / 3 - 1;
int dx = j % 3 - 1;
int ny = (y + dy + H) % H;
int nx = (x + dx + W) % W;
if (buf[ny][nx]) {
count += 1;
}
}
tmp[y][x] = table[buf[y][x]][count];
}
repeat (y,H) {
repeat (x,W) {
buf[y][x] = tmp[y][x];
}
}
}
int main(void) {
fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL) | O_NONBLOCK);
init();
initscr();
char c;
while (read(STDIN_FILENO, &c, 1) != 1) {
print();
update();
sleep(1);
}
exit(0);
}