/* personal notes of renzo diomedi */

~ 00001011 ~




CONVERSIONS





in brackets are the destination register in which the result can be placed, MMX or XMM
the last four instructions are truncated conversions. rounding toward zero is automatically performed.
In the other instructions, if the conversion is inexact, the rounding is controlled by bits 13 and 14 in the XMM MXCSR register. These bits determine whether the values are rounded up or down.

The source values can be obtained from either Memory locations, or MMX registers (for 64-bit values), or XMM registers (for 64- and 128-bit values).




// convers.s
// at&t mingw-w64 in windows os

.section .data
value1:
.float 1.25, 124.79, 200.0, -312.5
value2:
.int 1, -435, 0, -25
.section .bss
.lcomm data, 16
.section .text
// .globl _start # necessary in Linux GAS
// _start: # necessary in Linux GAS
nop
cvtps2dq value1, %xmm0
cvttps2dq value1, %xmm1
cvtdq2ps value2, %xmm2
movdqu %xmm0, data


/*
MOVDQA and MOVDQU instructions are used to move 128 bits of data into the XMM registers, or to move data between XMM registers.
The A and U parts of the mnemonic stand for aligned and unaligned, referring to how the data is stored in memory.
The format of both the MOVDQA and MOVDQU instruction is
movdqa source, destination
*/


movl $1, %eax
movl $0, %ebx
// int $0x80 # Linux
int $0x21







convers.s ; convers.o ; convers.exe # windows environment >







(gdb) s
12 cvtps2dq value1, %xmm0
(gdb) s
13 cvttps2dq value1, %xmm1
(gdb) s
14 cvtdq2ps value2, %xmm2
(gdb) s
15 movdqu %xmm0, data
(gdb) print $xmm0
$1 = {v4_float = {1.40129846e-45, 1.75162308e-43, 2.80259693e-43, -nan(0x7ffec8)}, v2_double = {2.6524947387115311e-312, -nan(0xffec8000000c8)}, v16_int8 = {1, 0, 0, 0, 125, 0, 0, 0, -56, 0, 0, 0, -56, -2, -1, -1}, v8_int16 = {1, 0, 125, 0, 200, 0, -312, -1}, v4_int32 = {1, 125, 200, -312}, v2_int64 = {536870912001, -1340029796152}, uint128 = 340282342201751762702250093524836941825}
(gdb) print $xmm1
$2 = {v4_float = {1.40129846e-45, 1.7376101e-43, 2.80259693e-43, -nan(0x7ffec8)}, v2_double = {2.6312747808018783e-312, -nan(0xffec8000000c8)}, v16_int8 = {1, 0, 0, 0, 124, 0, 0, 0, -56, 0, 0, 0, -56, -2, -1, -1}, v8_int16 = {1, 0, 124, 0, 200, 0, -312, -1}, v4_int32 = {1, 124, 200, -312}, v2_int64 = {532575944705, -1340029796152}, uint128 = 340282342201751762702250093520541974529}
(gdb) print $xmm2
$3 = {v4_float = {1, -435, 0, -25}, v2_double = {-7.3498756827903427e+18, -805306368}, v16_int8 = {0, 0, -128, 63, 0, -128, -39, -61, 0, 0, 0, 0, 0, 0, -56, -63}, v8_int16 = {0, 16256, -32768, -15399, 0, 0, 0, -15928}, v4_int32 = {1065353216, -1009156096, 0, -1043857408}, v2_int64 = {-4334292427813683200, -4483333429047328768}, uint128 = 257579462558195729010253313545846390784}
(gdb)

16 movl $1, %eax
(gdb) x/4d &data
0x403000 : 1 125 200 -312
(gdb)





The ADD instruction is used to add two integer values. The ADD instruction format is
add source, destination
where source can be an immediate value, a memory location, or a register.
The destination parameter can be either a register or a value stored in a memory location
No use a memory location for both the source and destination at the same time
The result of the addition is placed in the destination location.

The ADD instruction can add 8-, 16-, or 32-bit values. b (for byte), w (for word), or l (for doubleword)

examples :

addb $10, %al # adds the immediate value 10 to the 8-bit AL register
addw %bx, %cx # adds the 16-bit value of the BX register to the CX register
addl data, %eax # adds the 32-bit integer value at the data label to EAX
addl %eax, %eax # adds the value of the EAX register to itself



.section .data
data:
.int 40
.section .text
.globl _start
_start:
nop
movl $0, %eax
movl $0, %ebx
movl $0, %ecx
movb $20, %al
addb $10, %al
movsx %al, %eax
movw $100, %cx
addw %cx, %bx
movsx %bx, %ebx
movl $100, %edx
addl %edx, %edx
addl data, %eax
addl %eax, data
movl $1, %eax
movl $0, %ebx
int $0x80

(gdb) print $eax
$1 = 70
(gdb) print $ebx
$2 = 100
(gdb) print $ecx
$3 = 100
(gdb) print $edx
$4 = 200
(gdb) x/d &data
0x804909c : 110


ADD is able to use the same instruction to add both signed and unsigned integer values.

.section .data
data:
.int -40
.section .text
.globl _start
_start:
nop
movl $-10, %eax
movl $-200, %ebx
movl $80, %ecx
addl data, %eax
addl %ecx, %eax
addl %ebx, %eax
addl %eax, data
addl $210, data
movl $1, %eax
movl $0, %ebx
int $0x80

(gdb) print $eax
$1 = -170
(gdb) print $ebx
$2 = -200
(gdb) print $ecx
$3 = 80
(gdb) x/d &data
0x80490ac : 0
(gdb)




CARRY FLAG is set when an addition results in a carry condition in the binary addition (the result is larger than the maximum value allowed).
For signed integers, the OVERFLOW FLAG is used when an overflow condition is present (the resulting value is less than the minimum negative value, or greater than the maximum positive value allowed). When these flags


//prog , carry flag
.section .text
.globl _start
_start:
nop
movl $0, %ebx
movb $190, %bl
movb $100, %al
addb %al, %bl
jc over
movl $1, %eax
int $0x80
over:
movl $1, %eax
movl $0, %ebx
int $0x80

$ ./prog
$ echo $?
0 # LINUX environment

The result code is 0, indicating that the carry condition was properly detected.

If
movb $190, %bl
movb $10, %al

$ ./prog
$ echo $?
200



# oc , overflow flag
.section .data
output:
.asciz "The result is %d\n"
.section .text
.globl _start
_start:
movl $-1590876934, %ebx
movl $-1259230143, %eax
addl %eax, %ebx
jo over
pushl %ebx
pushl $output
call printf

add $8, %esp # to clear the parameters placed on the stack for the printf function.

//if parameter is qd , then
//addl $12

pushl $0
call exit
over:
pushl $0
pushl $output
call printf

add $8, %esp # to clear the parameters placed on the stack for the printf function

//if parameter is qd ,
//then addl $12

pushl $0
call exit

$ ./addtest4
The result is 0

but if
movl $-190876934, %ebx
movl $-159230143, %eax

$ ./addtest4
The result is -350107077


The ADC instruction
If you must work with extremely large signed or unsigned integers that will not fit in the doubleword data size (the maximum size that can be used with the ADD instruction), you can divide the value into multiple doubleword data elements, and perform separate addition operations on each element. To do this properly, you must detect the carry flag for each addition. If the carry flag is present, it must be carried to the next data element pair that is added:








# sub-.s
.section .data
data:
.int 40
.section .text
//.globl _start
//_start:
nop
movl $0, %eax
movl $0, %ebx
movl $0, %ecx
movb $20, %al
subb $10, %al
movsx %al, %eax
movw $100, %cx
subw %cx, %bx # -100
movsx %bx, %ebx # -100
movl $100, %edx
subl %eax, %edx # 90
subl data, %eax # -30
subl %eax, data # 70
movl $1, %eax
movl $0, %ebx
int $0x21

sub-.s ; sub-.o ; sub-.exe # windows environment



Unsigned integer multiplication using MUL
The MUL instruction is used to multiply two unsigned integers. The format for the MUL instruction is
mul source
where source can be an 8-, 16-, or 32-bit register or memory value.
the destination operand is implied.
the destination location always uses some form of the EAX register, depending on the size of the source operand. Thus, one of the operands used in the multiplication must be placed in the AL, AX, or EAX registers, depending on the size of the value.
Due to the large values that can result from the multiplication, the destination location of the MUL instruction must be twice the size of the source operand.
If the source value is 8 bits, the destination operand is the AX register, as the result is 16 bits.

Unfortunately, when multiplying a 16-bit source operand, the EAX register is not used to hold the 32-bit result. In order to be backwardly compatible with older processors, Intel uses the DX:AX register pair to hold the 32-bit multiplication result value (this format started back in the 16-bit processor days). The high-order word of the result is stored in the DX register, while the low-order word is stored in the AX register.

For 32-bit source values, the 64-bit EDX:EAX register pair is used, again with the high-order doubleword in the EDX register, and the low-order doubleword in the EAX.




# mullo
.section .data
data1:
.int 315814
data2:
.int 165432
result:
.quad 0
.section .text
#.globl _start
#_start:
nop
movl data1, %eax
mull data2
movl %eax, result
movl %edx, result+4
pushl %edx
pushl %eax
pushl $0
movl $1, %eax
movl $0, %ebx
int $0x21


C:\>gdb -q users\rnz\desktop\mullo.exe
Reading symbols from users\rnz\desktop\mullo.exe...done.
(gdb) break 1
Breakpoint 1 at 0x401000: file users\rnz\desktop\mullo.s, line 1.
(gdb) run
Starting program: C:\users\rnz\desktop\mullo.exe
[New Thread 11600.0x32dc]
Breakpoint 1, ?? () at users\rnz\desktop\mullo.s:14
14 nop
(gdb) s
15 movl data1, %eax
(gdb) s
16 mull data2
(gdb) s
17 movl %eax, result
(gdb) s
18 movl %edx, result+4
(gdb) s
19 pushl %edx
(gdb) s
20 pushl %eax
(gdb) s
(gdb) print $eax
$1 = 706134096
(gdb) print/x $eax
$2 = 0x2a16c050
(gdb) print $edx
$3 = 12
(gdb) print/x $edx
$4 = 0xc
(gdb) x/gd &result
0x402008 : 52245741648
(gdb) x/8b &result
0x402008 : 80 -64 22 42 12 0 0 0
(gdb) x/8x &result
0x402008 : 0x50 0xc0 0x16 0x2a 0x0c 0x00 0x00 0x00
(gdb)

mullo.s ; mullo.o ; mullo.exe # windows environment





Signed integer multiplication using IMUL
While the MUL instruction can only be used for unsigned integers, the IMUL instruction can be used by both signed and unsigned integers, although you must be careful that the result does not use the most significant bit of the destination. For larger values, the IMUL instruction is only valid for signed integers.

there are 3 different instruction formats of the IMUL instruction.

The first format of the IMUL instruction takes one operand, and behaves exactly the same as the MUL instruction:
imul source
The source operand can be an 8-, 16-, or 32-bit register or value in memory, and it is multiplied with the implied operand located in the AL, AX, or EAX registers (depending on the source operand size).
The result is then placed in the AX register, the DX:AX register pair, or the EDX:EAX register pair.



The second format of the IMUL instruction enables you to specify a destination operand other than the EAX register:

imul source, destination

where source can be a 16- or 32-bit register or value in memory, and destination must be a 16- or 32-bit general-purpose register. This format enables you to specify where the result of the multiplication will go (instead of being forced to use the AX and DX registers). The downside to this format is that the multiplication result is forced into the size of the single destination register (no 64-bit results). Extreme care must be taken when using this format that you do not overflow the destination register (check the carry or overflow flags using the standard methods, after the multiplication to ensure that the result fits in the destination register).

The third format of the IMUL instruction enables you to specify three operands:

imul multiplier, source, destination

where multiplier is an immediate value, source is a 16- or 32-bit register or value in memory, and destination must be a general-purpose register. This format enables you to perform a quick multiplication of a value (the source) with a signed integer (the multiplier), storing the result in a generalpurpose register (the destination).

Just like the MUL instruction, remember to add the size character to the end of the IMUL mnemonic when using it in the GNU assembler to specify the size of the source and destination operands.

# imulx.s - An example of the IMUL instruction formats
.section .data
value1:
.int 10
value2:
.int -35
value3:
.int 400
.section .text
.globl _start
_start:
nop
movl value1, %ebx
movl value2, %ecx
imull %ebx, %ecx
movl value3, %edx
imull $2, %edx, %eax
movl $1, %eax
movl $0, %ebx
int $0x80

(gdb) info reg
eax 0x320 800
ecx 0xfffffea2 -350
edx 0x190 400
ebx 0xa 10

The EAX register contains the value of EDX (400) multiplied by the immediate value 2.
The ECX register contains the value of the EBX register (10) multiplied by the value originally loaded into the ECX register (-35).
Notice that the result was placed in the ECX register as a signed integer value.


when using signed integers and the IMUL instruction, it is important to always detect overflows in the result.


OVERFLOW CHECK


# imuljo
.section .text
//.globl _start
//_start:
nop
movw $680, %ax
movw $100, %cx
imulw %cx
jo over
movl $1, %eax
movl $0, %ebx
int $0x21
over:
movl $1, %eax
movl $1, %ebx
int $0x21


imuljo


(gdb) s
17 int $0x21
(gdb) print $ebx
$4 = 1 # OVERFLOW
(gdb)


# imuljo2
.section .text
nop
movw $68, %ax
movw $100, %cx
imulw %cx
jo over
movl $1, %eax
movl $0, %ebx
int $0x21
over:
movl $1, %eax
movl $1, %ebx
int $0x21



imuljo2


(gdb) print $ebx
$1 = 0 # OK





if you divide 9 by 2 , this produces two parts to the answer. The quotient is the number of times the divisor goes into the dividend. The remainder is how much is left over (the fractional part of the answer). The division instructions produce both the quotient and remainder parts as results of the division.

DIV (unsigned) and IDIV (signed) instructions are used for integer division.

Unsigned division

The DIV instruction is used for dividing unsigned integers. The format of the DIV instruction is div divisor

where divisor is the value that is divided into the implied dividend, and can be an 8-, 16-, or 32-bit register or value in memory. The dividend must already be stored in the AX register (for a 16-bit value), the DX:AX register pair (for a 32-bit value), or the EDX:EAX register pair (for a 64-bit value) before the DIV instruction is performed.
The maximum value allowed for the divisor depends on the size of the dividend. For a 16-bit dividend, the divisor can only be 8 bits, for a 32-bit dividend 16 bits, and for a 64-bit dividend the divisor can only be 32 bits.
The result of the division is two separate numbers: the quotient and the remainder. Both values are stored in the same registers used for the dividend value.



This means that you will LOSE the value of the dividend when the division completes.

Then the result will alter the value of the DX or EDX registers





divtest2.s

divtest2.o

divtest2.exe # windows environment



C:\>gdb -q users\rnz\desktop\divtest2.exe
Reading symbols from users\rnz\desktop\divtest2.exe...done.
(gdb) break 2
Breakpoint 1 at 0x401000: file users\rnz\desktop\divtest2.s, line 2.
(gdb) run
Starting program: C:\users\rnz\desktop\divtest2.exe
[New Thread 7120.0x2810]
Breakpoint 1, ?? () at users\rnz\desktop\divtest2.s:14
14 nop
(gdb) s
15 movb dividend, %al
(gdb) s
16 movb dividend+1, %ah
(gdb) s
17 divb divisor
(gdb) s
18 movb %al, quotient
(gdb) s
19 movb %ah, remainder
(gdb) print $ah
$1 = 1
(gdb) print $al
$2 = 4
(gdb) print $ax
$3 = 260 # 256+4



Signed division

The IDIV instruction is used exactly like the DIV instruction, but for dividing signed integers. It too uses an implied dividend, located in the AX register, the DX:AX register pair, or the EDX:EAX register pair.
Unlike the IMUL instruction, there is only one format for the IDIV instruction, which specifies the divisor used in the division:

idiv divisor

where divisor can again be an 8-, 16-, or 32-bit register or value in memory.

For signed integer division, the sign of the remainder is always the sign of the dividend.

the size of the dividend must be twice the size of the divisor

The biggest problem with integer division is detecting when an error condition has occurred, such as when a division by zero happens, or the quotient (or remainder) overflows the destination register. When an error occurs, the system produces an interrupt, which will produce an error on the Linux system,

$ ./progname
Floating point exception



Home Page