asm-practice/bmi-calc/main.nasm

426 lines
7.4 KiB
NASM

%define SYS_read 0
%define SYS_write 1
%define SYS_poll 7
%define SYS_exit 60
%define FD_stdin 0
%define FD_stdout 1
%define POLLIN 1
section .text
global _start
_start:
prompt_weight:
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,weight_prompt_text
mov rdx,weight_prompt_length
syscall
call read_float
cmp rax,0
jne good_weight
call clear_stdin
jmp prompt_weight
good_weight:
; convert weight to kg
divss xmm0,[lb_kg_scale]
movq r15,xmm0 ; weight
prompt_height:
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,height_prompt_text
mov rdx,height_prompt_length
syscall
call read_float
cmp rax,0
jne good_height
call clear_stdin
jmp prompt_height
good_height:
; convert height to cm
mulss xmm0,[inch_cm_scale]
movq r14,xmm0 ; height
; print metric weight
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,weight_notice_pre_text
mov rdx,weight_notice_pre_length
syscall
movq xmm0,r15
mov rdi,3
call print_float
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,weight_notice_post_text
mov rdx,weight_notice_post_length
syscall
; print metric height
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,height_notice_pre_text
mov rdx,height_notice_pre_length
syscall
movq xmm0,r14
mov rdi,3
call print_float
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,height_notice_post_text
mov rdx,height_notice_post_length
syscall
cmp r14,0
je print_inf_bmi
; calculate bmi (kg/(m^2))
movq xmm1,r14 ; height
divss xmm1,[_100f] ; cm to m
mulss xmm1,xmm1
movq xmm0,r15 ; weight
divss xmm0,xmm1
movq r15,xmm0 ; bmi
; write bmi
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,bmi_notice_text
mov rdx,bmi_notice_length
syscall
movq xmm0,r15
mov rdi,3
call print_float
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,newline_char
mov rdx,1
syscall
call clear_stdin
exit:
xor rdi,rdi
mov rax,SYS_exit
syscall
print_inf_bmi:
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,bmi_notice_text
mov rdx,bmi_notice_length
syscall
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,inf_float_text
mov rdx,inf_float_length
syscall
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,newline_char
mov rdx,1
syscall
jmp exit
clear_stdin:
; 16 - read buff
; 4 - pollfd.fd
; 2 - pollfd.events
; 2 - pollfd.revents
sub rsp,24
mov DWORD [rsp+16],FD_stdin
mov WORD [rsp+20],POLLIN
clear_stdin_loop:
mov rax,SYS_poll
lea rdi,[rsp+16]
mov rsi,1
xor rdx,rdx
syscall
cmp rax,0
jle clear_stdin_end
mov rax,SYS_read
mov rdi,FD_stdin
mov rsi,rsp
mov rdx,16
syscall
jmp clear_stdin_loop
clear_stdin_end:
add rsp,24
ret
print_float: ; void print_float(float f, int percission)
movq rax,xmm0
cmp rax,0
je print_float_zero
sub rsp,24
movss xmm1,[_10f]
mov rcx,rdi
print_float_scale_loop:
mulss xmm0,xmm1
loop print_float_scale_loop
; put digits in memory
cvtss2si rax,xmm0
mov rcx,23
mov rbx,10
print_float_mem_loop:
xor rdx,rdx
div rbx
add rdx,'0'
mov [rsp+rcx],dl
dec rcx
cmp rax,0
jne print_float_mem_loop
push rcx
push rdi
add rcx,17
; number to write
mov rdx,rcx
sub rdx,40
imul rdx,-1
sub rdx,rdi
mov rax,SYS_write
mov rdi,FD_stdout
lea rsi,[rsp+rcx]
syscall
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,period_char
mov rdx,1
syscall
pop rdi
pop rcx
lea rsi,[rsp+24]
sub rsi,rdi
mov rdx,rdi
mov rax,SYS_write
mov rdi,FD_stdout
syscall
add rsp,24
ret
print_float_zero:
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,zero_float_text
mov rdx,zero_float_length
syscall
ret
; on error, return 0 in rax. otherwise, return 1 in rax
; and the result in xmm0
read_float: ; float read_float()
push r15 ; read count
push r14 ; whole part
sub rsp,21 ; read buffer
xor r15,r15
mov r14,-1
read_float_loop:
cmp r15,20
je read_float_size_error
mov rax,SYS_read
mov rdi,FD_stdin
lea rsi,[rsp+20]
mov rdx,1
syscall
mov dil,[rsp+20]
cmp r15,0
sete ah
cmp dil,'0'
sete al
and al,ah
jnz read_float_loop
cmp dil,' '
sete al
and al,ah
jnz read_float_loop
cmp dil,0xA ; \n
je read_float_convert
cmp dil,'.'
je read_float_start_decimal
call is_number ; dil is preserved
cmp rax,0
je read_float_num_error
sub dil,'0'
mov [rsp+r15],dil
inc r15
xor rax,rax
cmp r14,-1
setne al
add r14,rax
jmp read_float_loop
read_float_start_decimal:
cmp r14,-1
jne read_float_num_error
xor r14,r14
jmp read_float_loop
read_float_convert:
; zero r14 if it is -1
xor rax,rax
cmp r14,-1
cmove r14,rax
; invert scale
sub r14,r15
imul r14,-1
mov rax,0
movq xmm0,rax
movss xmm7,[_10f]
xor rcx,rcx
read_float_convert_whole_loop:
cmp rcx,r14
je read_float_convert_dec_prepare
mulss xmm0,xmm7
xor eax,eax
mov al,[rsp+rcx]
cvtsi2ss xmm1,eax
addss xmm0,xmm1
inc rcx
jmp read_float_convert_whole_loop
read_float_convert_dec_prepare:
mov rax,1 ; return value
movss xmm7,[_0.1f]
movss xmm6,[_10f]
read_float_convert_dec_loop:
cmp rcx,r15
je read_float_end
xor ebx,ebx
mov bl,[rsp+rcx]
cvtsi2ss xmm1,ebx
mulss xmm1,xmm7
addss xmm0,xmm1
divss xmm7,xmm6
inc rcx
jmp read_float_convert_dec_loop
read_float_end:
add rsp,21
pop r14
pop r15
ret
read_float_num_error:
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,num_error_text
mov rdx,num_error_length
syscall
mov rax,0
jmp read_float_end
read_float_size_error:
mov rax,SYS_write
mov rdi,FD_stdout
mov rsi,size_error_text
mov rdx,size_error_length
syscall
mov rax,0
jmp read_float_end
; dil is preserved across calls
is_number: ; bool is_number(char n)
cmp dil,'0'
jl is_number_fail
cmp dil,'9'
jg is_number_fail
mov rax,1
ret
is_number_fail:
xor rax,rax
ret
section .data
num_error_text: db "Invalid number!",0xA
num_error_length: equ $ - num_error_text
size_error_text: db "Number too long!",0xA
size_error_length: equ $ - size_error_text
period_char: db "."
newline_char: db 0xA
zero_float_text: db ".000"
zero_float_length: equ $ - zero_float_text
inf_float_text: db "Infinity"
inf_float_length: equ $ - inf_float_text
_10f: dd 10.0
_0.1f: dd 0.1
_100f: dd 100.0
inch_cm_scale: dd 2.54
lb_kg_scale: dd 2.20462
weight_prompt_text: db "Enter weight (pounds): "
weight_prompt_length: equ $ - weight_prompt_text
height_prompt_text: db "Enter height (inches): "
height_prompt_length: equ $ - height_prompt_text
height_notice_pre_text: db "Your metric height: "
height_notice_pre_length: equ $ - height_notice_pre_text
height_notice_post_text: db "cm",0xA
height_notice_post_length: equ $ - height_notice_post_text
weight_notice_pre_text: db "Your metric weight: "
weight_notice_pre_length: equ $ - weight_notice_pre_text
weight_notice_post_text: db "kg",0xA
weight_notice_post_length: equ $ - weight_notice_post_text
bmi_notice_text: db "Your BMI: "
bmi_notice_length: equ $ - bmi_notice_text