%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