Compare commits
36 Commits
fa7b4c70b8
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 7006e8a4fe | |||
| 2df9271b40 | |||
| 681f0abc79 | |||
| a48d4bbf9f | |||
| b33192de27 | |||
| c081838dd9 | |||
| 23e9001ea1 | |||
| 65d1d4aa52 | |||
| 82e55b3e1e | |||
| 6d3279f850 | |||
| 1820d4fdf8 | |||
| 81367baa99 | |||
| 33b6a82a42 | |||
| 755f9b9f06 | |||
| c3563fcda1 | |||
| 719bc17fcd | |||
| 5c91ace69b | |||
| 872c842e7c | |||
| a6a7debf2e | |||
| 57987228b0 | |||
| 83d3f3ac5a | |||
| 45c6d0eb83 | |||
| 670a6a2602 | |||
| e70b9ac765 | |||
| 6112537e9b | |||
| 3e59f36d4e | |||
| f26553e7c7 | |||
| db00e98b4c | |||
| b449c66a66 | |||
| 7dc5237c3e | |||
| 7fd699ea38 | |||
| b5c539a3a2 | |||
| ce343ff9db | |||
| d6ee106682 | |||
| 5b56f6bcb6 | |||
| 4a8db75bef |
@@ -0,0 +1,17 @@
|
||||
name: build_test
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: arch
|
||||
steps:
|
||||
- name: fix package cache
|
||||
run: rm -fr /var/cache/pacman/pkg/* && pacman -Syy archlinux-keyring --needed --noconfirm && pacman-key --init && pacman-key --populate archlinux
|
||||
- name: get dependencies
|
||||
run: pacman -Sy nodejs make nasm binutils --needed --noconfirm
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build
|
||||
run: make -j$(nproc)
|
||||
@@ -0,0 +1,39 @@
|
||||
<img src="https://git.pupes.org/repo-avatars/e4ede9d30f070c9e191eace5a88dcaa40434b9cadf60204122fab5a83aec9a9f" alt="pulsar in asm game of life" width="196"></img>
|
||||
[](https://git.pupes.org/PoliEcho/asm-game-of-life/actions?workflow=build_test.yaml)
|
||||
# AMD64 Assembly Game of life
|
||||
|
||||
> [!WARNING]
|
||||
> this program will break if your terminal is too small
|
||||
|
||||
## Dependencies
|
||||
> AMD64 Linux Kernel
|
||||
### Build only
|
||||
> nasm
|
||||
> ld
|
||||
> make
|
||||
|
||||
## Download
|
||||
[releases](https://git.pupes.org/PoliEcho/asm-game-of-life/releases)
|
||||
|
||||
## Build
|
||||
```shell
|
||||
make
|
||||
```
|
||||
|
||||
## Controls
|
||||
| key | action |
|
||||
|--------|-------------------------|
|
||||
| arrows | move cursor |
|
||||
| ENTER | invert cell |
|
||||
| j/k | change simulation speed |
|
||||
| p | start/stop simulation |
|
||||
| q | quit |
|
||||
|
||||
|
||||
## Warning
|
||||
delays in TTY may be diferent depending on cpu clockspeed use j/k to adjust
|
||||
|
||||
## Notes
|
||||
if screen does not clear properly after loading just move the cursor around a bit
|
||||
looks best in TTY
|
||||
it sometimes SEGFAULTS in TTY on start but only on non debug builds and it is not consistent
|
||||
+36
-2
@@ -14,17 +14,21 @@ section .bss
|
||||
simulation_running: RESB 1
|
||||
|
||||
next_frame_ptr: RESQ 1
|
||||
section .data
|
||||
extern simulation_speed
|
||||
|
||||
section .rodata
|
||||
clear: db ESC_CHAR, "[2J", 0
|
||||
global reset
|
||||
reset: db ESC_CHAR, "[0m", 0
|
||||
resetLen: equ $-reset-1
|
||||
global resetLen
|
||||
|
||||
home_cursor: db ESC_CHAR, "[H", 0
|
||||
|
||||
statusbar: db ESC_CHAR, "[32;100m", "Use arrow keys to move cursor, enter to invert cell j/k to change simulation speed, p to simulation", 0
|
||||
START_STOP_pos: equ $-statusbar-17
|
||||
statusbar: db ESC_CHAR, "[32;100m", "Use arrow keys to move cursor, enter to invert cell j/k to change simulation speed, p to simulation. Frame Delay multiplier:", 0
|
||||
statusbarLen: equ $-statusbar
|
||||
START_STOP_pos: equ statusbarLen-42
|
||||
|
||||
|
||||
start_str: db "START", 0
|
||||
@@ -48,6 +52,7 @@ extern string_copy
|
||||
extern memory_set
|
||||
extern memory_copy
|
||||
extern alloc
|
||||
extern unsigned_int_to_ascii
|
||||
|
||||
global init_gameboard
|
||||
init_gameboard:
|
||||
@@ -94,6 +99,7 @@ print_game_ui:
|
||||
push rdi
|
||||
add rdi, [gameboard_size]
|
||||
sub di, [term_cols]
|
||||
push rdi
|
||||
add rdi, START_STOP_pos
|
||||
|
||||
mov cl, [simulation_running]
|
||||
@@ -106,6 +112,34 @@ print_game_ui:
|
||||
.end_simulation_running_check:
|
||||
call string_copy
|
||||
|
||||
pop rdi
|
||||
add rdi, statusbarLen
|
||||
|
||||
movss xmm0, [simulation_speed]
|
||||
|
||||
cvttss2si rsi, xmm0
|
||||
push rsi
|
||||
push rdi
|
||||
call unsigned_int_to_ascii
|
||||
pop rdi
|
||||
add rdi, rax
|
||||
|
||||
mov byte [rdi], '.'
|
||||
inc rdi
|
||||
|
||||
pop rax
|
||||
cvtsi2ss xmm1, rax
|
||||
|
||||
subss xmm0, xmm1; get only value after decimal point
|
||||
mov rax, 10
|
||||
cvtsi2ss xmm1, rax
|
||||
|
||||
mulss xmm0, xmm1
|
||||
cvttss2si rsi, xmm0; get first decimal point as int
|
||||
|
||||
call unsigned_int_to_ascii; rdi already set
|
||||
|
||||
|
||||
pop rdi
|
||||
call print_str
|
||||
|
||||
|
||||
+160
-42
@@ -12,17 +12,26 @@ section .bss
|
||||
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
|
||||
@@ -36,39 +45,23 @@ 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
|
||||
push r14
|
||||
|
||||
|
||||
lea r12, [multipurpuse_buf]
|
||||
|
||||
.main_loop:
|
||||
xor r13, r13
|
||||
|
||||
; put the cursor where it should be
|
||||
mov rdi, r12; multipurpuse_buf pointer is in 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; multipurpuse_buf pointer is in r12
|
||||
call print_str
|
||||
|
||||
call print_cursor
|
||||
|
||||
|
||||
|
||||
xor rax, rax
|
||||
@@ -79,12 +72,29 @@ handle_user_input:; main loop of the program
|
||||
mov word [r12+4], POLLIN
|
||||
mov rdi, r12
|
||||
mov rsi, 1; only one file descriptor is provided
|
||||
mov rdx, 500; no timeout. maybe use this for final sleep but run if user inputs something TODO
|
||||
movss xmm0, [speed_multiplier]
|
||||
movss xmm1, [simulation_speed]
|
||||
mulss xmm0, xmm1; callculate sleep lenght
|
||||
cvttss2si rdx, xmm0; truncate and copy to rdx
|
||||
mov r14, rdx
|
||||
syscall
|
||||
|
||||
mov sil, [running_in_tty]
|
||||
test sil, sil
|
||||
jz .skip_tty_mul
|
||||
push rax
|
||||
mov rax, 2500; magic number
|
||||
xor rdx,rdx
|
||||
mul r14
|
||||
mov r14, rax
|
||||
pop rax
|
||||
|
||||
.skip_tty_mul:
|
||||
test rax, rax; SYS_POLL returns 0 when no change happens within timeout
|
||||
jz .no_input
|
||||
|
||||
.repeat_read:
|
||||
|
||||
xor rax, rax
|
||||
mov qword [r12], rax; zeroout the buffer
|
||||
|
||||
@@ -95,8 +105,18 @@ handle_user_input:; main loop of the program
|
||||
syscall; read user input
|
||||
|
||||
cmp rax, EAGAIN
|
||||
je .no_input
|
||||
jne .handle_input
|
||||
mov al, [running_in_tty]
|
||||
test al, al
|
||||
jz .no_input
|
||||
; this runs only if running in tty
|
||||
|
||||
test r14, r14
|
||||
jz .no_input; timeout
|
||||
dec r14
|
||||
jmp .repeat_read
|
||||
|
||||
.handle_input:
|
||||
mov rax, [r12]
|
||||
|
||||
cmp eax, 0x00415B1B; check if input is more than left arrow
|
||||
@@ -119,41 +139,41 @@ handle_user_input:; main loop of the program
|
||||
dec word [cursor_rows]
|
||||
jnz .move_cursor_up
|
||||
inc word [cursor_rows]
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
.move_cursor_up:
|
||||
lea rdi, [cursor_up]
|
||||
call print_str
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
|
||||
.arrow_down:
|
||||
mov r8w, [cursor_rows]
|
||||
inc r8w
|
||||
cmp word r8w, r9w
|
||||
ja .no_input
|
||||
ja .end_input_handling
|
||||
mov word [cursor_rows], r8w
|
||||
lea rdi, [cursor_down]
|
||||
call print_str
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
|
||||
.arrow_right:
|
||||
mov r8w, [cursor_cols]
|
||||
inc r8w
|
||||
cmp word r8w, r10w
|
||||
ja .no_input
|
||||
ja .end_input_handling
|
||||
mov word [cursor_cols], r8w
|
||||
lea rdi, [cursor_right]
|
||||
call print_str
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
|
||||
.arrow_left:
|
||||
dec word [cursor_cols]
|
||||
jnz .move_cursor_left
|
||||
inc word [cursor_cols]
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
.move_cursor_left:
|
||||
lea rdi, [cursor_left]
|
||||
call print_str
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
|
||||
.handle_single_byte_chars:
|
||||
|
||||
@@ -176,12 +196,12 @@ handle_user_input:; main loop of the program
|
||||
je .hashtag_present
|
||||
|
||||
mov byte [rdi], '#'
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
|
||||
.hashtag_present:
|
||||
|
||||
mov byte [rdi], ' '
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
|
||||
.check_p:
|
||||
cmp al, 'p'
|
||||
@@ -189,38 +209,63 @@ handle_user_input:; main loop of the program
|
||||
|
||||
xor byte [simulation_running], 0x01; switch simulation on or off
|
||||
|
||||
jmp .no_input
|
||||
jmp .end_input_handling
|
||||
|
||||
.check_j:
|
||||
cmp al, 'j'
|
||||
jne .check_k
|
||||
|
||||
; TODO implement simulation speed
|
||||
movss xmm0, [simulation_speed]
|
||||
mov eax, 0x3DCCCCCD; 0.1f
|
||||
vmovd xmm1, eax
|
||||
|
||||
jmp .no_input
|
||||
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
|
||||
|
||||
; TODO implement simulation speed
|
||||
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 .no_input
|
||||
pop r12
|
||||
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 r14
|
||||
pop r13
|
||||
pop r12
|
||||
ret
|
||||
|
||||
@@ -289,4 +334,77 @@ reset_terminal:
|
||||
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
|
||||
|
||||
|
||||
+57
-4
@@ -4,7 +4,7 @@
|
||||
|
||||
section .bss
|
||||
global multipurpuse_buf
|
||||
multipurpuse_buf: RESB 8
|
||||
multipurpuse_buf: RESB 16
|
||||
|
||||
global term_rows
|
||||
term_rows: RESW 1
|
||||
@@ -19,11 +19,18 @@ section .bss
|
||||
|
||||
extern cursor_rows
|
||||
extern cursor_cols
|
||||
|
||||
|
||||
global running_in_tty
|
||||
running_in_tty: RESB 1
|
||||
|
||||
section .rodata
|
||||
extern resetLen
|
||||
|
||||
hide_cursor: db ESC_CHAR, "[?25l", 0
|
||||
show_cursor: db ESC_CHAR, "[?25h", 0
|
||||
|
||||
help_text: db "asm-game-of-life [args]",0xA,"-h display this help menu",0xA,"Controls:",0xA,"use arrow keys to move around",0xA,"ENTER to invert cell",0xA,"p to START/STOP simulation",0xA,"k to increase simulation speed",0xA,"j to decrese simulation speed",0xA, 0
|
||||
|
||||
section .text
|
||||
extern print_str
|
||||
extern unsigned_int_to_ascii
|
||||
@@ -55,11 +62,50 @@ _start:
|
||||
|
||||
; handle args
|
||||
pop rcx; get argc (number of arguments)
|
||||
pop rax; get rid of program name arugument
|
||||
cmp rcx, 1
|
||||
jle .no_arguments_provided
|
||||
; TODO hanndle arguments
|
||||
dec rcx
|
||||
.handle_arg:
|
||||
pop rax
|
||||
mov word di, [rax]
|
||||
|
||||
cmp di, 0x682D; check if -h was passed
|
||||
jne .next_arg
|
||||
lea rdi, [help_text]
|
||||
call print_str
|
||||
jmp .exit_program
|
||||
|
||||
.next_arg:
|
||||
dec rcx
|
||||
test rcx, rcx
|
||||
jnz .handle_arg
|
||||
|
||||
.no_arguments_provided:
|
||||
|
||||
|
||||
pop rax; get rid of null termination of argv
|
||||
; handle enviroment vars
|
||||
|
||||
.handle_env:
|
||||
pop rax
|
||||
test rax, rax; test if we reached end of envs
|
||||
jz .no_envs
|
||||
|
||||
mov dword edi, [rax]
|
||||
cmp edi, 0x4D524554; check for "TERM" inverted becose endiannes
|
||||
jne .handle_env
|
||||
mov qword rdi, [rax+5]; remove the TERM= part this should never segfault since there sould allwas be other data behind enviroment vars and i dont mind garbage
|
||||
mov rsi, 0xffffffffff
|
||||
and rdi, rsi
|
||||
mov rsi, 0x78756e696c
|
||||
cmp rdi, rsi; check for "linux"
|
||||
jne .no_envs
|
||||
mov byte [running_in_tty], 1
|
||||
|
||||
.no_envs:
|
||||
|
||||
|
||||
call init_alloc
|
||||
|
||||
xor rax, rax
|
||||
@@ -85,16 +131,23 @@ _start:
|
||||
mov rdx, O_NONBLOCK
|
||||
syscall
|
||||
|
||||
lea rdi, [hide_cursor]
|
||||
call print_str
|
||||
|
||||
call print_game_ui
|
||||
|
||||
call disable_canonical_mode_and_echo
|
||||
|
||||
call print_game_ui
|
||||
|
||||
call handle_user_input
|
||||
|
||||
call reset_terminal
|
||||
|
||||
lea rdi, [show_cursor]
|
||||
call print_str
|
||||
|
||||
|
||||
.exit_program:
|
||||
mov rax, SYS_EXIT
|
||||
mov rdi, 0 ; return code
|
||||
syscall
|
||||
|
||||
+1
-1
@@ -61,7 +61,7 @@ unsigned_int_to_ascii: ; takes pointer to array in rdi and value stored in rsi D
|
||||
ret
|
||||
|
||||
global string_copy
|
||||
string_copy:; takes pointer to destination in rdi and pointer to source in rsi
|
||||
string_copy:; takes pointer to destination in rdi and pointer to source in rsi and return lenght in rax
|
||||
|
||||
xor rax, rax
|
||||
xor rcx, rcx
|
||||
|
||||
@@ -5,6 +5,7 @@ SYS_WRITE equ 1
|
||||
SYS_BRK equ 12
|
||||
SYS_FCNTL equ 72
|
||||
SYS_POLL equ 7
|
||||
SYS_NANOSLEEP equ 35
|
||||
|
||||
STDIN equ 0
|
||||
STDOUT equ 1
|
||||
|
||||
Reference in New Issue
Block a user