Dealing with floating numbers is quite different than integers. Below, you will see how we store and manipulate the floating point numbers.
val1: dq 12.34
We have seen how to print integer values, and might expect that printing
floating point values is a matter of simply loading "val1" into rsi,
as we did with an integer, but that does not work.
Also, since we are using the printf
function, we need to
specify the format, and we use "%g" here.
It is one of several formats that we could use.
The printf
function can take multiple types of arguments.
For example, these are all valid printf
calls from C:
printf("hello\n");
printf("%d\n", 3);
printf("%f\n", 3.4);
The A register is used to indicate more information to printf
.
We put the value 0 in it before, when printing an integer.
For printing a floating point value, we will use the value 1.
Next, let us see how we store and print a floating number in NASM:
extern printf ; We will use this external function
section .data ; Data section, initialized variables
val1: dq 12.34
mystr2: db "%g", 10, 0 ; String format to use
section .text
global main
main:
push rbp ; remember the current rbp value
mov rdi, mystr2 ; the format to use
;mov rsi, [val1]
movsd xmm0, [val1] ; the value to print
mov al, 1 ; 1 for a floating point argument
call printf
mov rax, 0 ; return value
pop rbp ; restore the former rbp value
ret
Try it out, and verify that it works.
Questions
val1 : dq 12.34
mean?
What happens if you change it to
val1 : dw 12.34
instead? Explain why the result is different.
push rbp
:
This instruction pushes the value of the base pointer (rbp) onto the stack.
The purpose of pushing rbp is to save its current value so that it can be
restored later (during the pop rbp instruction).
pop rbp
:
This instruction restores the value of the base pointer (rbp) from the stack.
Restoring rbp is necessary to return the stack to its initial state, ensuring
proper stack management.
If you remember we used registers like rax, rbx while dealing with integer numbers. Those registers (rax, rbx, rcx, rdx) are general purpose registers in NASM. For floating point numbers, we have a dedicated set of registers (xmm0 to xmm15). They are specifically designed to hold floating numbers and perform operations on them, so we will make use of these registers.
movsd
command from the previous example. Use addsd
command to
add two floating numbers. Finally, to print the result you need to follow
the instruction of previous example.
addsd xmm0, xmm1 ; Add the contents of xmm1 to xmm0
Then, subtract one floating point number from another and display the result.
Use the subsd
command to perform subtraction with floating point numbers.
Questions
subsd xmm0, xmm1
does.
That is, if xmm1 has the value 3.3 and xmm0 has the value 2.2,
what is the result? Where is the result stored?
movsd
as a "move string" command in Appendix B
(also in chapter 9).
What does the movsd
command in the program do?
movsd
command from Part 1
with mov
,
what happens and why?
extern printf
section .data
val1: dq 3.14
val2: dq 5
result: dq 0.0
mystr1: db "%g", 10, 0 ; String format to use
section .text
global main
main:
push rbp
mov rdi, mystr1
mov rbx, [val2]
movsd xmm0, [val1]
addsd xmm0, rax
movsd [result], xmm0
mov al, 1
call printf
mov rax, 0
pop rbp
ret
This code will not run. The assembler gives an error message. cvtsi2sd
.
Here is an example:
float1: dq 3.14
integer1: dq 5
result: dq 0.0 ; to store the result
format_str: db "Result: %g", 10, 0 ; String format to use
; ...
; Load the floating-point number into xmm0
movsd xmm0, [float1]
; Load the integer into register
mov rax, [integer1]
; Convert the integer in xmm1 to a floating-point number
cvtsi2sd xmm1, rax
; Add the contents of xmm1 to xmm0
addsd xmm0, xmm1
; Store the result back in memory
movsd [result], xmm0
Implement this code, compile it, and run it. Verify that it works.
Questions
cvtsi2sd
command do?
cvtsi2sd
, i.e.
what should "xxx" be to make xxx rax, xmm1
work?
In this lab, we have learned: