You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/asm_6.md
+50-33Lines changed: 50 additions & 33 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -28,20 +28,20 @@ The single-precision value occupies `32-bits` of memory with the following struc
28
28
29
29
The sign bit indicates whether the number is positive or negative. If the bit is set to `0`, the number is positive; if it’s `1`, the number is negative. While the idea of a sign is rather straightforward, the exponent and mantissa require a bit more explanation. To understand how floating-point numbers are represented in memory, we need to know how to convert the floating-point number from [decimal](https://en.wikipedia.org/wiki/Decimal) to [binary](https://en.wikipedia.org/wiki/Binary_number) representation.
30
30
31
-
Let's take a random floating-point number, for example - `5.625`. To convert a floating-point number to a binary representation, we need to split the number into integral and fractional parts. In our case, it is `5` and `625`. To convert the integral part of our number to binary representation, we need to divide our number by `2` repeatedly until the result is not zero. Let's take a look:
31
+
Let's take a random floating-point number, for example - `5.625`. To convert a floating-point number to a binary representation, we need to split the number into integral and fractional parts. In our case, it is `5` and `625`. To convert the integer part of a number to its binary representation, divide it by `2` repeatedly, noting down the remainders, until the quotient becomes zero. Let's take a look:
32
32
33
33
| Division | Quotient | Remainder |
34
34
|:-------------:|----------|-----------|
35
35
| $\frac{5}{2}$ | 2 | 1 |
36
36
| $\frac{5}{2}$ | 1 | 0 |
37
-
| $\frac{1}{2}| 0 | 1 |
37
+
| $\frac{1}{2}$| 0 | 1 |
38
38
39
39
To get the binary representation, we simply write down all the remainders we got during the division process. For the number `5`, the remainders are `1`, `0`, and `1`, which gives us `101` in binary (as shown in the "Remainder" column). So, the binary representation of `5` is `0b101`.
40
40
41
41
> [!NOTE]
42
42
> We will use the prefix `0b` for all binary numbers to not mix them with the decimal numbers.
43
43
44
-
To convert the fractional part of our floating-point number, we need to multiply our number by `2` until the integral part is not equal to one. Let's try to convert the fractional part of our number:
44
+
To convert the fractional part of our floating-point number, we need to multiply our number by `2` until the integral part equals 1. Let's try to convert the fractional part of our number:
45
45
46
46
| Multiplication | Result | Integral part | Fractional part |
@@ -86,15 +86,15 @@ After we convert both integral and fractional parts of our number to binary repr
86
86
87
87
As a first step, we need to shift right the integral part of our number so that only one digit remains before the point. In the case of `0b101`, we need to shift right two digits. Basically when we are doing each shift - we divide our number by `2`. This is done because our number has base `2`. Shifting the number twice, we divide our number by $$2^{2}$$. To keep the original value unchanged, we multiple it by $$2^{2}$$. As a result, our initial number `0b101.101` is now represented as `0b1.01101 * 2^2`.
88
88
89
-
After this, we need to add the number of shifts to the special number called `bias`. For single-precision floating-point numbers, it is equal to `127`. So we get - `2 + 127 = 129` or `0b10000001`. This is our `exponent`. The `mantissa` is just the fractional part of the number that we got after shifting it. We just put all the numbers of the fractional parts to the 23 `mantissa` bits.
89
+
After this, we need to add the number of shifts to the special number called `bias`. For single-precision floating-point numbers, it is equal to `127`. So we get - `2 + 127 = 129` or `0b10000001`. This is our `exponent`. The `mantissa` is just the fractional part of the number that we got after shifting it. We just put all the numbers of the fractional parts to the 23 `mantissa` bits and rest filled with zeros.
90
90
91
91
If we combine all together, we can see how our number `5.625` is represented in a computer memory:
92
92
93
93
| Sign | Exponent | Mantissa |
94
94
|------|----------|-------------------------|
95
95
| 0| 10000001 | 01101000000000000000000 |
96
96
97
-
If the fractional part of the number is periodic as we have seen with the example of the number `5.575` - we will fill the `mantissa` bits while it is possible. So the number `5.575`will be represented in a computer memory as:
97
+
If the fractional part of the number is periodic, as in the example of the number `5.575`, we fill the `mantissa` bits while it is possible. So the number `5.575`is represented in a computer memory as:
98
98
99
99
| Sign | Exponent | Mantissa |
100
100
|------|----------|-------------------------|
@@ -122,60 +122,77 @@ The `bias` for the double-precision format is `1023`. The `bias` for the extende
122
122
123
123
## Floating-point instructions
124
124
125
-
As I mentioned in the beginning of this chapter, before this point we were writing our assembly programs which were operating only with integer numbers. We have used the `general-purpose` registers to store them and instructions like `add`, `sub`, `mul`, and so on to do basic arithmetic on them. We can not use these registers and instructions to operate with the floating-point numbers. Happily, `x86_64`CPU provides special registers and instructions to operate with such numbers. The name of these registers is`XMM` registers.
125
+
As mentioned at the beginning of this chapter, before this point, we wrote assembly programs that operated only with integer numbers. We used the general-purpose registers to store them, and instructions like `add`, `sub`, `mul`, and others to perform basic arithmetic. However, we cannot use these registers and instructions to operate on floating-point numbers. Fortunately, `x86_64`CPUs provide special registers and instructions for handling such numbers. These registers are called`XMM` registers.
126
126
127
-
There are 16 `XMM` registers named `xmm0` through `xmm15`. These registers are`128-bits`. At this point, the question might raised in your head - if we have `32-bits` and `64-bits` floating point numbers why we have `128-bit` registers? The answer is that these registers were introduced as part of the [SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data)extensions instructions set. These instructions set allows to operate on `packed` data. This allows you to execute a single instruction on four `32-bits` floating point numbers for example.
127
+
There are 16 `XMM` registers named from `xmm0` through `xmm15`. These registers occupy`128bits` of memory. At this point, the question might arise in your mind - if we have `32-bit` and `64-bit` floating point numbers, why do we have `128-bit` registers? The answer is that these registers were introduced as part of the [SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data)extension instruction set. This instruction set allows for operating on `packed` data. It enables you to execute a single instruction on four `32-bit` floating point numbers, for example.
128
128
129
-
In addition to the sixteen `xmm` register, each `x86_64` CPU includes a "legacy" floating-point unit named [x87 FPU](https://en.wikipedia.org/wiki/X87). It is built as an eight-deep register stack. Each of those stack slots may hold an `80-bits` extended-precision number.
129
+
In addition to the sixteen `XMM` registers, each `x86_64` CPU includes a "legacy" floating-point unit named [x87 FPU](https://en.wikipedia.org/wiki/X87). It is built as an eight-deep register stack. Each of those stack slots may hold an `80-bit` extended-precision number.
130
130
131
-
Besides the storage for data, the CPU obviously provides instructions to operate on this data. The instructions set have the same purpose as instructions for integer data:
131
+
Besides the data storage, the CPU provides instructions to operate on this data. This instruction set has the same purpose as instructions for integer data, including:
132
132
133
133
- Data transfer instructions
134
134
- Logical instructions
135
135
- Comparison instructions
136
136
- And others, like transcendental instructions, integer/floating-point conversion instructions, and so on
137
137
138
-
In the next sections we will take a look at some of these instructions. Of course, we will not cover all the instructions supported by the modern CPUs. For more information, read the Intel [Software Developer Manual](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html).
138
+
In the next sections, we will take a look at some of these instructions. Of course, we will not cover all the instructions supported by the modern CPUs. For more information, read the Intel [Software Developer Manual](https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html).
139
139
140
-
The calling conventions for floating-point numbers is different. If a function parameter is a floating-point number - it will be passed in one of `XMM` registers - the first argument will be passed in the `xmm0` register, the second argument will be passed in `xmm1` register and so on up till`xmm7`. The rest of arguments after eights argument are passed on the [stack](./asm_3.md). The value of the functions that return a floating-point value is stored in the `xmm0` register.
140
+
The calling convention for floating-point numbers is different. If a function parameter is a floating-point number, it is passed in one of the `XMM` registers - the first argument is passed in the `xmm0` register, the second argument in the `xmm1` register, and so on up to`xmm7`. The rest of the arguments are passed on the [stack](./asm_3.md). The value of the functions that return a floating-point value is stored in the `xmm0` register.
141
141
142
142
### Data transfer instructions
143
143
144
-
As we know from the [4th part](./asm_4.md)- data transfer instructions are used to move data between different locations. `x86_64` CPU provides special set of instructions for transfer of floatingpoint data. If you are going to use the `x87 FPU` unit, one of the most common data transfer instructions are:
144
+
As we know from the [Data manipulation](./asm_4.md)chapter, data transfer instructions are used to move data between different locations. The `x86_64` CPU provides a special set of instructions to transfer floating-point data. If you are going to use the `x87 FPU` unit, the most common data transfer instructions are:
145
145
146
-
-`fld` - Load floating point value.
147
-
-`fst` - Store floating point value.
146
+
-`fld` - Loads a floating point value.
147
+
-`fst` - Stores a floating point value.
148
148
149
149
These instructions are used by the `x87 FPU` unit to store and load the floating-point numbers at/from the stack.
150
150
151
-
To work with `XMM` registers, the following data transfer instructions are supported by an `x86_64` CPU:
151
+
To work with the `XMM` registers, the `x86_64` CPU supports, among others, the following data transfer instructions:
152
152
153
-
-`movss` - Move single-precision floating-point value between the `XMM` registers or between an `XMM` register and memory,
154
-
-`movsd` - Move double-precision floating-point value between the `XMM` registers or between an `XMM` register and memory.
155
-
-`movhlps` - Move two packed single-precision floating-point values from the high quadword of an `XMM` register to the low quadword of another `XMM` register.
156
-
-`movlhps` - Move two packed single-precision floating-point values from the low quadword of an `XMM` register to the high quadword of another `XMM` register.
157
-
- And others.
153
+
-`movss` - Moves a single-precision floating-point value between the `XMM` registers or between an `XMM` register and memory.
154
+
-`movsd` - Moves double-precision floating-point value between the `XMM` registers or between an `XMM` register and memory.
155
+
-`movhlps` - Moves two-packed single-precision floating-point values from the high quadword of an `XMM` register to the low quadword of another `XMM` register.
156
+
-`movlhps` - Moves two-packed single-precision floating-point values from the low quadword of an `XMM` register to the high quadword of another `XMM` register.
158
157
159
158
### Floating-point arithmetic instructions
160
159
161
-
The floating-point arithmetic instructions perform arithmetic operations such as add, subtract, multiplication, and division on single or doubleprecision floating-point values. The following floating-point arithmetic instructions exist:
160
+
The floating-point arithmetic instructions perform arithmetic operations, such as addition, subtraction, multiplication, and division, on single or double-precision floating-point values. The following floating-point arithmetic instructions exist:
-`addss` - Adds a single-precision floating-point values.
163
+
-`addsd` - Adds a double-precision floating point values.
164
+
-`subss` - Subtracts a single-precision floating-point values.
165
+
-`subsd` - Subtracts a double-precision floating-point values.
166
+
-`mulss` - Multiplies a single-precision floating-point values.
167
+
-`mulsd` - Multiplies a double-precision floating-point values.
168
+
-`divss` - Divides a single-precision floating-point values.
169
+
-`divsd` - Divides a double-precision floating-point values.
171
170
172
-
All of these instructions expects two operands to execute the given operation of addition, subtraction, multiplication, or division. After the operation will be executed, the result will be stored in the first operand.
171
+
All of these instructions expect two operands to execute the given operation of addition, subtraction, multiplication, or division. After the operation is executed, the result is stored in the first operand.
173
172
174
-
### Floating-Point control instructions
173
+
### Floating-point control instructions
175
174
176
-
As we already know, the main goal of this type of instructions is to manage the control flow of our programs. For the integer comparison we have seen the `cmp` instruction. But this instruction will not work with floating-point values. Although, the result of the comparison is controlled by the same `rflags` registers that we have seen in the [4th part](./asm_4.md#Control transfer instructions).
175
+
As we already know, the main goal of these instructions is to manage the control flow of our programs. For integer comparisons, we used the `cmp` instruction. But this instruction does not work with floating-point values, even though the result of the comparison is controlled by the same `rflags` registers that we saw in the [Data manipulation](./asm_4.md#Control transfer instructions) chapter. The general instruction to compare floating-point numbers is `cmpss`. Instead of setting the value of the flag based on the result comparison, the instruction stores the result of the comparison in the destination register.
177
176
178
-
The general instruction to compare floating-point numbers is `cmpss`.
177
+
For example:
178
+
179
+
```assembly
180
+
;; Compare xmm0 < xmm1 and store the result in the xmm0
181
+
cmpss xmm0, xmm1, 0x1
182
+
```
183
+
184
+
The third operand of the instruction, identifies the operator:
0 commit comments