asm-game-of-life/src/input.asm

415 lines
7.0 KiB
NASM

%include "symbols.asm"
section .bss
alignb 16
termios: RESZ 1; 60 bytes is needed i use 64 for alligment and it is easier to work with
extern multipurpuse_buf
extern term_rows
extern term_cols
extern gameboard_ptr
extern simulation_running
extern running_in_tty
section .data
cursor_rows: dw 1
cursor_cols: dw 1
global simulation_speed
simulation_speed dd 1.0
section .rodata
extern reset
cursor_up: db ESC_CHAR, "[1A", 0
cursor_down: db ESC_CHAR, "[1B", 0
cursor_right: db ESC_CHAR, "[1C", 0
cursor_left: db ESC_CHAR, "[1D", 0
cursor_color: db ESC_CHAR, "[45m", 0
speed_multiplier dd 50.0
arrow_switch_statement:
dq handle_user_input.arrow_up
dq handle_user_input.arrow_down
dq handle_user_input.arrow_right
dq handle_user_input.arrow_left
section .text
extern print_str
extern step_simulation
extern unsigned_int_to_ascii
extern print_game_ui
extern string_copy
global handle_user_input
handle_user_input:; main loop of the program
push r12
push r13
lea r12, [multipurpuse_buf]
.main_loop:
xor r13, r13
; put the cursor where it should be
call print_cursor
xor rax, rax
mov qword [r12], rax; zeroout the buffer
movss xmm0, [speed_multiplier]
movss xmm1, [simulation_speed]
mulss xmm0, xmm1; callculate sleep lenght
cvttss2si rdx, xmm0; truncate and copy to rdx
mov byte al, [running_in_tty]
test al, al; test if we are running in tty
jz .pts
mov rax, rdx
xor rdx, rdx
mov rcx, 1000; convert miliseconds to seconds
div rcx
mov qword [r12], rax; store seconds
mov rax, rdx
xor rdx, rdx
mov rcx, 1000000
mul rcx
mov qword [r12+8], rax; store nanoseconds
mov rax, SYS_NANOSLEEP
mov rdi, r12
xor rsi, rsi; ignore some remaining time or something
syscall
mov rax, 1; simulate sys_poll output
jmp .end_input_handling
.pts:
mov rax, SYS_POLL
mov dword [r12], STDIN; create pollfd struct
mov word [r12+4], POLLIN
mov rdi, r12
mov rsi, 1; only one file descriptor is provided
; rdi already set
syscall
.end_delay_handling:
test rax, rax; SYS_POLL returns 0 when no change happens within timeout
jz .no_input
xor rax, rax
mov qword [r12], rax; zeroout the buffer
mov rax, SYS_READ
mov rdi, STDIN
lea rsi, [r12]
mov rdx, 8; size of multipurpuse buffer
syscall; read user input
cmp rax, EAGAIN
je .no_input
mov rax, [r12]
cmp eax, 0x00415B1B; check if input is more than left arrow
jl .handle_single_byte_chars
bswap eax
sub eax, 0x1B5B4100
shr eax, 8
cmp al, 3
ja .no_input
mov r9w, [term_rows]
dec r9w
mov r10w, [term_cols]
jmp [arrow_switch_statement+(rax*8)]; lets hope this works
.arrow_up:
dec word [cursor_rows]
jnz .move_cursor_up
inc word [cursor_rows]
jmp .end_input_handling
.move_cursor_up:
lea rdi, [cursor_up]
call print_str
jmp .end_input_handling
.arrow_down:
mov r8w, [cursor_rows]
inc r8w
cmp word r8w, r9w
ja .end_input_handling
mov word [cursor_rows], r8w
lea rdi, [cursor_down]
call print_str
jmp .end_input_handling
.arrow_right:
mov r8w, [cursor_cols]
inc r8w
cmp word r8w, r10w
ja .end_input_handling
mov word [cursor_cols], r8w
lea rdi, [cursor_right]
call print_str
jmp .end_input_handling
.arrow_left:
dec word [cursor_cols]
jnz .move_cursor_left
inc word [cursor_cols]
jmp .end_input_handling
.move_cursor_left:
lea rdi, [cursor_left]
call print_str
jmp .end_input_handling
.handle_single_byte_chars:
cmp al, 0xa; NEWLINE (enter key)
jne .check_p
xor rax, rax; zeroout rax
mov ax, [cursor_rows]
dec ax
mul word [term_cols]
mov cx, [cursor_cols]
dec cx
add ax, cx
mov rdi, [gameboard_ptr]
add rdi, rax
mov cl, [rdi]
cmp cl, '#'
je .hashtag_present
mov byte [rdi], '#'
jmp .end_input_handling
.hashtag_present:
mov byte [rdi], ' '
jmp .end_input_handling
.check_p:
cmp al, 'p'
jne .check_j
xor byte [simulation_running], 0x01; switch simulation on or off
jmp .end_input_handling
.check_j:
cmp al, 'j'
jne .check_k
movss xmm0, [simulation_speed]
mov eax, 0x3DCCCCCD; 0.1f
vmovd xmm1, eax
addss xmm0, xmm1
; wont check for overflows since the user would need be crazy to reach that number
movss [simulation_speed], xmm0
jmp .end_input_handling
.check_k:
cmp al, 'k'
jne .check_q
movss xmm0, [simulation_speed]
mov eax, 0x3DCCCCCD; 0.1f
vmovd xmm1, eax
subss xmm0, xmm1
ucomiss xmm0, xmm1; check if number is smaller than 0.1
jb .end_input_handling
movss [simulation_speed], xmm0
jmp .end_input_handling
.check_q:
cmp al, 'q'
jne .end_input_handling
jmp .function_exit
ret; exit if q pressed
.end_input_handling:
mov r13b, 1
.no_input:
mov al, [simulation_running]
test al, al
jz .dont_step
call step_simulation
mov r13b, 1
.dont_step:
test r13b, r13b
jz .main_loop
call print_game_ui
jmp .main_loop
.function_exit:
pop r13
pop r12
ret
global disable_canonical_mode_and_echo
disable_canonical_mode_and_echo:
mov rax, SYS_IOCTL
mov rdi, STDIN
mov rsi, TCGETS
lea rdx, [termios]
syscall
; save original termios struct
%ifdef AVX2
%ifdef AVX512
vmovdqa64 zmm0, [termios]
%else
vmovdqa ymm0, [termios]
vmovdqa ymm1, [termios+32]
%endif
%else
vmovdqa xmm0, [termios]
vmovdqa xmm1, [termios+16]
vmovdqa xmm2, [termios+32]
vmovdqa xmm3, [termios+64]
%endif
mov eax, [termios+12]; get c_lflag
and eax, NOT_ECHO; disable ECHO
and eax, NOT_ICANON; disable ICANON
mov [termios+12], eax
mov rax, SYS_IOCTL
mov rdi, STDIN
mov rsi, TCSETS
lea rdx, [termios]
syscall
; load original termios struct
%ifdef AVX2
%ifdef AVX512
vmovdqa64 [termios], zmm0
%else
vmovdqa [termios], ymm0
vmovdqa [termios+32], ymm1
%endif
%else
vmovdqa [termios], xmm0
vmovdqa [termios+16], xmm1
vmovdqa [termios+32], xmm2
vmovdqa [termios+64], xmm3
%endif
ret
global reset_terminal
reset_terminal:
mov rax, SYS_IOCTL
mov rdi, STDIN
mov rsi, TCSETS
lea rdx, [termios]
syscall
ret
print_cursor:
push r12
push r13
lea r12, [multipurpuse_buf]
mov rdi, r12
mov word [rdi], 0x5B1B; will store ESC_CHAR, '[' they have to be in reverse order here due to little endian
add rdi, 2
push rdi
xor rsi, rsi
mov si, [cursor_rows]
call unsigned_int_to_ascii
pop rdi
add rdi, rax; add lenght of string to pointer
mov byte [rdi], ';'
inc rdi
push rdi
mov si, [cursor_cols]
call unsigned_int_to_ascii
pop rdi
add rdi, rax
mov byte [rdi], 'H'
inc rdi
mov byte [rdi], 0; null terminate
mov rdi, r12
call print_str
; write there real cursor
xor r13, r13
mov rdi, r12
lea rsi, [cursor_color]
call string_copy
mov rdi, r12
add r13, rax
add rdi, r13
xor rax, rax
mov ax, [cursor_rows]
dec ax
mul word [term_cols]; get index of character
add ax, [cursor_cols]
dec ax
xor rsi, rsi
add rax, [gameboard_ptr]
mov sil, [rax]; now we got the character in si
mov byte [rdi], sil
inc rdi
inc r13
lea rsi, [reset]
call string_copy
mov rdi, r12
add r13, rax
add rdi, r13
lea rsi, [cursor_left]
call string_copy
mov rdi, r12
add r13, rax
add rdi, r13
inc rdi
mov byte [rdi], 0; null terminate
mov rdi, r12
call print_str
pop r13
pop r12
ret