asm-practice/bmi-calc/main.nasm

385 lines
6.7 KiB
NASM
Raw Normal View History

2022-10-06 01:46:33 -07:00
%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
; 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
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)
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_reverse_loop:
xor rdx,rdx
div rbx
add rdx,'0'
mov [rsp+rcx],dl
dec rcx
cmp rax,0
jne print_float_reverse_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
; 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
xor rcx,rcx
read_float_convert_whole_loop:
cmp rcx,r14
je read_float_convert_dec_prepare
mulss xmm0,[_10f]
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
2022-10-06 02:05:13 -07:00
mov rdi,FD_stdout
2022-10-06 01:46:33 -07:00
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
2022-10-06 02:05:13 -07:00
mov rdi,FD_stdout
2022-10-06 01:46:33 -07:00
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
_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