2

I am writing a simple program where I am supposed to get the first two command line arguments, which are integers and then do some operations with them. But here I have a problem. When I want to print out the results of these operations I only get 0.00 and I cannot figure out why.

section .data
first dq 0
second dq 0
fp db '%.2f '

section .bss
sqr1 resq 1
sqr2 resq 1
divRes resq 1

section .text
extern printf 
global main

main:
push rbp
mov rbp, rsp
push rbx
push rsi
push rdi

mov rdi, [rsi+8]
mov rcx, 0
mov rax, 0
readFirst:
mov byte al, [rdi+rcx]
cmp al, 0
je next1
sub al, 30h
push rax
inc rcx
jmp readFirst

next1:
mov rdx, 10
mov rbx, 1
atoi1:
pop rax
mul bl
add [first], ax
mov rax, rbx
mul dl
mov rbx, rax
loop atoi1

mov rcx, 0
mov rax, 0
mov rdi, [rsi+16]
readSecond:
mov byte al, [rdi+rcx]
cmp al, 0
je next2
sub al, 30h
push rax
inc rcx
jmp readSecond

next2:
mov rdx, 10
mov rbx, 1
atoi2:
pop rax
mul bl
add [second], ax
mov rax, rbx
mul dl
mov rbx, rax
loop atoi2

fild qword[first]
fsqrt
fstp qword[sqr1]

mov rax, 0
mov rdi, fp
mov rsi, [sqr1]
call printf

fild qword[second]
fsqrt
fstp qword[sqr2]

mov rax, 0
mov rdi, fp
mov rsi, [sqr2]
call printf

fild qword[first]
fild qword[second]
fdivp
fstp qword[divRes]

mov rax, 0
mov rdi, fp
mov rsi, [divRes]
call printf


pop rdi
pop rsi
pop rbx
mov rsp, rbp
pop rbp
ret

I tested the arguments and they are stored correctly into the memory. It just seems that the problem is in the coprocessor instructions.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
whatever
  • 21
  • 1
  • 2
    Since you are using a 64-bit x86 processor any reason you don't use the SSE (SIMD) instructions like SQRTSD etc? – Michael Petch Jan 30 '18 at 22:48
  • Secondly have you used GDB or some other debugger to load up this code and step through it looking at the registerss and memory at each instruction and follow the flow of the code to understand where it might be going wrong? – Michael Petch Jan 30 '18 at 22:51
  • yes, I tried the GDB and it seems to get the sqrt wrong, but the values in `first` and `second` are stored correctly so it really confuses me – whatever Jan 30 '18 at 22:57
  • 1
    Well I don't think the issue is your actual `fsqrt` or saving to memory code. The problem is that you incorrectly mix 32-bit floats (scalars), 64-bit double (double scalar) and integers.Clearly your code is reading integers at first, you load them into the x87 and perform the sqrt but you save back the double (64-bit) to memory. That is fine. But using GCC you need to use `%Lf` as the format for doubles. Then you have the issue that floats and doubles have to be passed in the SSE registers. First double/float is in XMM0 not RDI – Michael Petch Jan 30 '18 at 23:44
  • You'll need to load RDI with the address of the format specifier and the double (scalar double) can be transferred from memory to XMM0 with an instruction like movsd xmm0, [sqr1] which moves the scalar double (64-bit double floating point value) to the XMM0 register – Michael Petch Jan 30 '18 at 23:45
  • And since RAX is the number of floats/doubles that are being passed to a variadic function (printf is variadic) then you'll have to set AL (or RAX if you wish) to the value 1 if you are passing a single float/double. – Michael Petch Jan 30 '18 at 23:54
  • 1
    Doh, the format specifier you used s correct, sorry. Keep it as `%.2f` as floats are promoted to doubles automatically (with variadic functions like printd) so `%lf` will print doubles by default. – Michael Petch Jan 31 '18 at 00:23
  • And then when you think you have all that there is another potential snafu. You need to make sure the stack is aligned on a 16 byte (or 32 depending on arguments) boundary. By default just prior to a function (including `main`) the stack is 16 byte aligned. `CALL` pushes an 8 byte return address. Stack is misaligned by 8 bytes. You push 4 registers on the stack (32 bytes total). 32+8 is not evenly divisible by 16. You need to push an additional 8 bytes to get 16 byte alignment again (40+8 is evenly divisible by 16). Incorrect alignment will likely cause `printf` to crash when printing floats – Michael Petch Jan 31 '18 at 00:58
  • More information on alignment and calling `printf` can be found in this SO answer: https://stackoverflow.com/questions/16097173/printing-floating-point-numbers-from-x86-64-seems-to-require-rbp-to-be-saved – Michael Petch Jan 31 '18 at 00:59
  • So in all you need to set RAX to 1 to match the number of doubles you pass to `printf`, set XMM0 to the value of the first double, make sure the stack is 16-byte aligned before calling `printf`. The alignment can be fixed by pushing any 64-bit general purpose register to put an additional 8 bytes on the stack (doesn't matter what register), remember to pop it off when cleaning up. – Michael Petch Jan 31 '18 at 01:00
  • 1
    thank youu! such a simple fix, it works now – whatever Jan 31 '18 at 09:15
  • I'm a seasoned assembly coder, and I don't find it **that** simple... at least compared to custom calling conventions designed specifically for particular purpose. I mean, I have to check my code every time after writing `printf` call, to make sure I didn't forget any of those conditions, while I can usually write about 10-15% of my asm source without double-checking (I don't count the final review before executing it in debugger, so that's like 10-15% with double checking only, rest with triple or many-more checking). – Ped7g Jan 31 '18 at 14:16

1 Answers1

0

when printing a float number you cant just send float parameters using normal x86 registers (rdi,rsi,rdx,rcx,r9,r8) . there are the xmm registers ,they are indexed xmm0..7. what you need to do is to move the float you want to print to xmm0,xmm1... using "movsd" instruction and not the normal mov instruction and move to rax number of paramaters sent through xmm registers. then you can call printf

mov rdi,fp
mov rax,1
movsd xmm0 qword[divres]
call printf

this should work