SSE技术所提供的指令可以对压缩的单精度浮点数进行运算,类似于MMX,SSE也提供了新的指令用于将数据赋值给XMM寄存器,或者从XMM寄存器获取数据,以及对XMM寄存器中的压缩浮点数进行数学运算等。SSE指令有两种后缀:一种是PS后缀,例如下面要提到的MOVAPS指令...

    本文由zengl.com站长对汇编教程英文版相应章节进行翻译得来。

    汇编教程英文版的下载地址:点此进入原百度盘   , 点此进入Dropbox网盘   , 点此进入Google Drive  (Dropbox与Google Drive里是Assembly_Language.pdf文档)

    另外再附加一个英特尔英文手册的共享链接地址:
    点此进入原百度盘点此进入Dropbox网盘点此进入Google Drive  (在某些例子中会用到,Dropbox与Google Drive里是intel_manual.pdf文档)

    Dropbox与Google Drive可能需要通过代理访问。

    本篇翻译对应汇编教程英文原著的第528页到第535页,对应原著第17章 (注意这里的页数不是页脚的页数,而是pdf电子档顶部,分页输入框中的页数,也就是包含了目录,前言部分的总页数,pdf电子档总页数是577,当前已翻译完的页数为535 / 577)。

    上一篇介绍了MMX技术提供的指令,这一篇就介绍SSE技术所提供的指令。

Using SSE Instructions 使用SSE指令:

    SSE技术所提供的指令可以对压缩的单精度浮点数进行运算,类似于MMX,SSE也提供了新的指令用于将数据赋值给XMM寄存器,或者从XMM寄存器获取数据,以及对XMM寄存器中的压缩浮点数进行数学运算等。

    SSE指令有两种后缀:一种是PS后缀,例如下面要提到的MOVAPS指令,PS后缀的指令可以对压缩单精度浮点数里的多个浮点数同时进行数学运算,这种操作方式类似于上一篇介绍的MMX指令,可以参考上一篇文章里的图1

    另一种是SS后缀,例如下面要提到的MOVSS指令,这种后缀的指令则只能对scalar single-precision floating-point value(标量单精度浮点数)进行操作,标量单精度浮点数是指压缩浮点数里的低32位的浮点数。如下图所示:


图1

    上图只是个演示图(仅作参考),用于说明SS后缀的指令只会对压缩浮点数里的低32位的浮点数进行操作,如上图中的A1、B1部分,至于结果寄存器里的高位部分的浮点数的值,则由具体的指令以及指令的操作数来决定,例如下面会提到的MOVSS指令,当该指令的源操作数为内存位置,目标操作数为XMM寄存器时,指令执行后,XMM寄存器的高位部分的3个单精度浮点数会被全部清零,当MOVSS指令的源与目标操作数都为XMM寄存器时,则目标XMM寄存器的高位的3个单精度浮点数会保持不变。

    下面就对SSE所提供的具体的指令集进行介绍。

Moving data 数据传值指令:

    在之前的"汇编数据处理 (四) 数据处理结束篇"的文章里,我们介绍过SSE相关的传值指令,如下表所示:

Instruction
指令
Description
指令描述
MOVAPS Move four aligned, packed single-precision values to XMM registers or memory
将4个对齐的压缩单精度浮点数加载到XMM寄存器或内存中
MOVUPS Move four unaligned, packed single-precision values to XMM registers or memory
将4个非对齐的压缩单精度浮点数加载到XMM寄存器或内存中
MOVSS Move a single-precision value to memory or the low doubleword of a register
将一个单精度浮点数加载到内存中或加载到一个XMM寄存器的低32位
MOVLPS Move two single-precision values to memory or the low quadword of a register
将2个单精度浮点数加载到内存中或加载到一个XMM寄存器的低64位
MOVHPS Move two single-precision values to memory or the high quadword of a register
将2个单精度浮点数加载到内存中或加载到一个XMM寄存器的高64位
MOVLHPS Move two single-precision values from the low quadword to the high quadword
将2个单精度浮点数从XMM寄存器的低64位传值到高64位
MOVHLPS Move two single-precision values from the high quadword to the low quadword
将2个单精度浮点数从XMM寄存器的高64位传值到低64位

    这些指令的具体指令格式,所需的操作数等,都可以参考 http://x86.renejeschke.de 该链接对应的页面,例如:在该页面处,可以找到MOVAPS指令的链接地址为 http://x86.renejeschke.de/html/file_module_x86_id_180.html ,进入此链接地址,可以看到MOVAPS指令的格式如下:

Opcode
指令操作码
Mnemonic
助记符
Description
指令描述
0F 28 /r MOVAPS xmm1, xmm2/m128 Move packed single-precision floating-point values from xmm2/m128 to xmm1.
将压缩单精度浮点数从一个XMM寄存器或128位内存里,传值到一个XMM寄存器中
0F 29 /r MOVAPS xmm2/m128, xmm1 Move packed single-precision floating-point values from xmm1 to xmm2/m128.
将压缩单精度浮点数从一个XMM寄存器,传值到一个XMM寄存器或128位的内存中


    上面的助记符使用的是Intel的汇编语法,源与目标操作数的顺序和AT&T汇编语法的顺序刚好相反。

    在MOVAPS指令所在的链接页面,还给出了比上表更加详细的描述信息,从其描述信息里,可以看到,当源或目标操作数为内存位置时,该内存位置必须是16字节对齐的,否则就会产生general-protection exception (#GP) 即通用保护性异常,在之前的"汇编数据处理 (四) 数据处理结束篇"的文章中,有一个ssefloat.s的程式,该程式使用的是MOVUPS指令用于对非对齐的内存数据进行操作,如果我们将ssefloat.s里的MOVUPS指令改为MOVAPS的话,程式运行时就会抛出"segmentation fault"的段错误。

   gas汇编器提供了.align的伪指令,可以让内存位置按照指定的字节数进行对齐,如下面的代码片段:

.section .data
.align 16
value1:
	.float 12.34, 2345.543, -3493.2, 0.4491
.section .text
.globl _start
_start:
	movaps value1, %xmm0


    在.align后面只需给出需要对齐的字节数即可,这样上面的value1标签所在的内存位置就是16字节对齐的了(也就是内存地址是16的整数倍),代码中的MOVAPS指令执行时,才不会抛出异常。

Processing data 数据处理相关的指令:

    SSE技术除了提供了上面介绍的传值指令外,还提供了很多额外的指令,可以用于对压缩的单精度浮点数进行处理。下面就对这些指令进行介绍。

Arithmetic instructions 数学运算指令:

     SSE里有很多数学运算指令,可以对XMM寄存器里的压缩浮点数进行数学运算,如下表所示:

Instruction
指令
Description
描述
ADDPS Add two packed values.
对两个压缩单精度浮点数进行加法运算
SUBPS Subtract two packed values.
对两个压缩单精度浮点数进行减法运算
MULPS Multiply two packed values.
对两个压缩单精度浮点数进行乘法运算
DIVPS Divide two packed values.
对两个压缩单精度浮点数进行除法运算
RCPPS Compute the reciprocal of a packed value.
计算压缩单精度浮点数的近似倒数
SQRTPS Compute the square root of a packed value.
计算压缩单精度浮点数的平方根
RSQRTPS Compute the reciprocal square root of a packed value.
计算压缩单精度浮点数的平方根的近似倒数
MAXPS Compute the maximum values in two packed values.
获取两个压缩单精度浮点数中的最大值
MINPS Compute the minimum values in two packed values.
获取两个压缩单精度浮点数中的最小值
ANDPS Compute the bitwise logical AND of two packed values.
对两个压缩单精度浮点数进行按位逻辑与的运算
伪表达式:
Destination[0..127] = Destination[0..127] & Source[0..127];
ANDNPS Compute the bitwise logical AND NOT of two packed values.
先对压缩浮点数里的目标操作数进行取反,再与源操作数进行按位与运算
伪表达式:
Destination[0..127] = ~Destination[0..127] & Source[0..127];
ORPS Compute the bitwise logical OR of two packed values.
对两个压缩单精度浮点数进行按位逻辑或的运算
伪表达式:
Destination[0..127] = Destination[0..127] | Source[0..127];
XORPS Compute the bitwise logical exclusive-OR of two packed values.
对两个压缩单精度浮点数进行按位逻辑异或的运算
伪表达式:
Destination[0..127] = Destination[0..127] xor Source[0..127];

    上表中的每个指令都有两个操作数:源操作数可以是一个XMM寄存器也可以是一个128位的内存位置,目标操作数则必须是一个XMM寄存器。至于每个指令的具体描述信息可以参考 http://x86.renejeschke.de 该链接对应的页面。

    下面的ssemath.s程式就演示了这些指令的用法:

# ssemath.s - An example of using SSE arithmetic instructions
.section .data
.align 16
value1:
	.float 12.34, 2345., -93.2, 10.44
value2:
	.float 39.234, 21.4, 100.94, 10.56
.section .bss
	.lcomm result, 16
.section .text
.globl _start
_start:
	nop
	movaps value1, %xmm0
	movaps value2, %xmm1

	addps %xmm1, %xmm0
	sqrtps %xmm0, %xmm0
	maxps %xmm1, %xmm0
	movaps %xmm0, result

	movl $1, %eax
	movl $0, %ebx
	int $0x80


    上面的代码里,会先通过movaps指令将value1与value2处的压缩单精度浮点数依次传值给XMM0与XMM1中。再对这两个XMM寄存器里的压缩浮点数依次执行addps加运算,以及sqrtps的平方根运算,结果都存储在XMM0寄存器里,接着通过maxps指令将XMM0与XMM1中的最大值获取出来,并保存在XMM0里。最后就可以通过movaps指令将XMM0中的结果保存到result内存位置处了。

    在对上面的程式进行汇编链接后,就可以在gdb调试器里查看到指令的运行情况:

$ as -gstabs -o ssemath.o ssemath.s
$ ld -o ssemath ssemath.o
$ gdb -q ssemath
Reading symbols from /root/asm_example/adv/ssemath...done.
(gdb) b _start 
Breakpoint 1 at 0x8048074: file ssemath.s, line 13.
(gdb) r
Starting program: /root/asm_example/adv/ssemath 

Breakpoint 1, _start () at ssemath.s:13
13		nop
(gdb) s
14		movaps value1, %xmm0
(gdb) s
15		movaps value2, %xmm1
(gdb) s
17		addps %xmm1, %xmm0
(gdb) print $xmm0
$1 = {v4_float = {12.3400002, 2345, -93.1999969, 10.4399996}, 
.............................................................
(gdb) print $xmm1
$2 = {v4_float = {39.2340012, 21.3999996, 100.940002, 10.5600004}, 
.............................................................
(gdb) 


    可以看到,在执行完两个movaps指令后,XMM0寄存器里就存储了value1处定义的压缩单精度浮点数了,XMM1里则存储了value2处定义的压缩单精度浮点数。继续往下执行:

17		addps %xmm1, %xmm0
(gdb) s
18		sqrtps %xmm0, %xmm0
(gdb) print $xmm0
$1 = {v4_float = {51.5740013, 2366.3999, 7.74000549, 21}
......................................................
(gdb) 


    在执行完addps的加运算指令后,XMM0寄存器里就存储了两个压缩浮点数的和,即压缩浮点数中的每个浮点数的和,例如:之前XMM0的低32位浮点数为12.3400002,XMM1的低32位浮点数为39.2340012,那么它们的和就是51.5740013,该和值存储在XMM0的低32位中,其他三个浮点数的加运算也是同理。

    继续往下执行:

18		sqrtps %xmm0, %xmm0
(gdb) s
19		maxps %xmm1, %xmm0
(gdb) print $xmm0
$2 = {v4_float = {7.18150425, 48.6456566, 2.78208661, 4.5825758},
...............................................................
(gdb) 


    在执行完sqrtps指令后,XMM0里的浮点数就都变为了原来的值的平方根。例如:上面的7.18150425就是原来的51.5740013的平方根。其他三个浮点数也都是原来值的平方根。

    继续往下执行:

19		maxps %xmm1, %xmm0
(gdb) s
20		movaps %xmm0, result
(gdb) print $xmm0
$3 = {v4_float = {39.2340012, 48.6456566, 100.940002, 10.5600004},
.............................................................
(gdb) s
22		movl $1, %eax
(gdb) x/4f &result
0x80490c0 :	39.2340012	48.6456566	100.940002	10.5600004
(gdb) 


    从上面的输出可以看出来,maxps指令会将XMM1与XMM0里的每对浮点数的最大值给提取出来,并存储到XMM0的对应位置处,例如:XMM1的低32位浮点值是39.2340012,XMM0的原来的低32位浮点值是7.18150425(该值是之前平方根的结果),39.2340012大于7.18150425,因此,XMM0存储的结果的低32位浮点值就是39.2340012 ,其他三个浮点数也都是同理得出来的。

    上面的运算结果都存储在XMM0里,通过movaps %xmm0, result指令就可以将XMM0里的压缩浮点数存储到result内存位置处,从上面的x/4f &result命令的输出可以看到,result里存储的压缩浮点数与XMM0中的值相等。

Comparison instructions 比较指令:

    SSE技术还提供了一些比较指令,可以用于对压缩单精度浮点数的值进行比较,这些指令同样有PS与SS两种后缀,如下表所示:

Instruction
指令
Description
描述
CMPPS Compare packed values.
对压缩单精度浮点数进行比较
CMPSS Compare scalar values.
对标量单精度浮点数进行比较
COMISS Compare scalar values and set the EFLAGS register.
对标量单精度浮点数进行比较,同时设置EFLAGS标志寄存器
UCOMISS Compare scalar values (including invalid values) and set the EFLAGS register.
对标量单精度浮点数进行比较(包括NaN之类的无效的值),并且设置EFLAGS寄存器

    UCOMISS指令在对标量单精度浮点数进行比较时,还可以对not-a-number (NaN)即非数字类型的特殊值进行比较,详情可以参考 http://x86.renejeschke.de/html/file_module_x86_id_317.html 该链接对应的页面。CMPSS与COMISS指令也可以从 http://x86.renejeschke.de/ 该页面里查找到对应的链接地址。

    下面主要看下CMPPS指令,它可以对压缩单精度浮点数里的每个浮点数都进行比较操作,指令格式如下:

CMPPS imp, source, destination

    其中,source源操作数可以是一个XMM寄存器或者是一个128位尺寸的内存位置,destination目标操作数则必须是一个XMM寄存器,这里需要注意的是imp操作数,该操作数是一个立即数,其值用于表示需要执行什么类型的比较操作,如下表所示:

Imp Value
imp操作数的值
Comparison
需要执行的比较操作
0 Equal
进行等于比较
1 Less than
进行小于比较
2 Less than or equal
进行小于等于的比较
3 Unordered
检测操作数中,是否存在无效的浮点数
4 Not equal
进行不等于的比较
5 Not less than
进行不小于的比较
6 Not less than or equal
进行不小于等于的比较
7 Ordered
检测操作数中包含的值,是否都是有效的浮点数

    因此,要检测两个XMM寄存器里的压缩浮点数是否相等,可以使用类似下面的指令:

CMPPS $0, %xmm1, %xmm0

    上面的imp操作数为0,说明是对XMM1与XMM0进行等于的比较操作,比较的结果会以位掩码的形式(当二进制全为1时表示相等,当二进制全为0时表示不相等)存储在XMM0中。

    上表里有个unordered的比较操作,当两操作数中,至少有一个是无效的浮点数时,unordered的比较结果就会是true 。另外还有个ordered类型的比较操作,当两操作数都是有效的浮点数时,ordered的比较结果就会是true 。

    我们可以使用下面的伪表达式来描述CMPPS指令的比较操作:

switch(imp) {
	case 0:
		Operator = OperatorEqual;
		break;
	case 1:
		Operator = OperatorLessThan;
		break;
	case 2:
		Operator = OperatorLessOrEqual;
		break;
	case 3:
		Operator = OperatorUnordered;
		break;
	case 4:
		Operator = OperatorNotEqual;
		break;
	case 5:
		Operator = OperatorNotLessThan;
		break;
	case 6:
		Operator = OperatorNotLessOrEqual;
		break;
	case 7:
		Operator = OperatorOrdered;
		break;
}
CMP0 = Destination[0..31] Operator Source[0..31];
CMP1 = Destination[32..63] Operator Source[32..63];
CMP2 = Destination[64..95] Operator Source[64..95];
CMP4 = Destination[96..127] Operator Source[96..127];
if(CMP0 == true) Destination[0..31] = 0xFFFFFFFF;
else Destination[0..31] = 0;
if(CMP1 == true) Destination[32..63] = 0xFFFFFFFF;
else Destination[32..63] = 0;
if(CMP2 == true) Destination[64..95] = 0xFFFFFFFF;
else Destination[64..95] = 0;
if(CMP3 == true) Destination[96..127] = 0xFFFFFFFF;
else Destination[96..127] = 0;


    可以看到,CMPPS指令会先由imp操作数来决定需要执行的比较操作,在上面的伪代码里,就以Operator来表示,该指令会对源与目标操作数里的压缩单精度浮点数的每个浮点数都执行一次Operator操作,操作的结果为true时,就将0xFFFFFFFF即二进制全为1的值存储到目标操作数的对应位置处,当操作结果为false时,则将0存储到目标操作数里。

    gas汇编器还提供了一些伪指令可以等效于imp对应的不同操作,使用gas提供的这些伪指令的话,就不需要imp操作数了,只需source与destination操作数即可,可用的伪指令如下表所示:

Pseudo Instruction
伪指令
Description
描述
CMPEQPS Equal
进行等于比较
CMPLTPS Less than
进行小于比较
CMPLEPS Less than or equal
进行小于等于的比较
CMPUORDPS Unordered
检测操作数中,是否存在无效的浮点数
CMPNEQPS Not equal
进行不等于的比较
CMPNLTPS Not less than
进行不小于的比较
CMPNLEPS Not less than or equal
进行不小于等于的比较
CMPORDPS Ordered
检测操作数中包含的值,是否都是有效的浮点数

    下面的ssecomp.s程式就演示了如何使用CMPEQPS伪指令:

# ssecomp.s - An example of using SSE comparison instructions
.section .data
.align 16
value1:
	.float 12.34, 2345., -93.2, 10.44
value2:
	.float 12.34, 21.4, -93.2, 10.45
.section .bss
	.lcomm result, 16
.section .text
.globl _start
_start:
	nop
	movaps value1, %xmm0
	movaps value2, %xmm1

	cmpeqps %xmm1, %xmm0
	movaps %xmm0, result

	movl $1, %eax
	movl $0, %ebx
	int $0x80


    上面的代码中,先将value1与value2处需要进行比较的两个压缩单精度浮点数分别赋值给XMM0与XMM1,接着就可以使用cmpeqps伪指令,对这两个XMM寄存器里的压缩浮点数进行等于的比较操作了,结果会保存到result对应的内存位置处。

    在汇编链接后,可以在gdb调试器里查看指令的执行情况:

$ as -gstabs -o ssecomp.o ssecomp.s 
$ ld -o ssecomp ssecomp.o
$ gdb -q ssecomp
Reading symbols from /root/asm_example/adv/ssecomp...done.
(gdb) b _start
Breakpoint 1 at 0x8048074: file ssecomp.s, line 13.
(gdb) r
Starting program: /root/asm_example/adv/ssecomp 

Breakpoint 1, _start () at ssecomp.s:13
13		nop
(gdb) s
14		movaps value1, %xmm0
(gdb) s
15		movaps value2, %xmm1
(gdb) s
17		cmpeqps %xmm1, %xmm0
(gdb) s
18		movaps %xmm0, result
(gdb) s
20		movl $1, %eax
(gdb) x/4x &result 
0x80490c0 :	0xffffffff	0x00000000	0xffffffff	0x00000000
(gdb) print $xmm0
$1 = {v4_float = {-nan(0x7fffff), 0, -nan(0x7fffff), 0},
........................................................
(gdb)


    从result的输出情况中,可以看到,value1与value2处定义的两个压缩浮点数中,第一个和第三个浮点数是相等的(结果的二进制位全为1),其他两个数是不相等的(结果的二进制位全为0),和预期的一致。不过需要注意的是,cmpeqps等只是伪指令,它会被汇编器自动转为对应的CMPPS指令的操作码。

SSE integer instructions ---- SSE整数指令:

    SSE技术除了提供了和压缩浮点数相关的指令外,还在原来MMX技术的基础上,扩展了一些用于处理MMX寄存器里的64位压缩整数的指令。扩展的指令如下表所示:

Instruction
指令
Description
描述
PAVGB Computes the average of packed unsigned byte integers
计算压缩无符号字节整数的平均值
PAVGW Computes the average of packed unsigned word integers
计算压缩无符号字整数的平均值
PEXTRW Copies a word from an MMX register or XMM register to a general-purpose register
从MMX寄存器或XMM寄存器中,拷贝字整数到指定的通用寄存器里
PINSRW Copies a word from a general-purpose register to an MMX register
从通用寄存器中,拷贝字整数到指定的MMX寄存器或XMM寄存器里
PMAXUB Computes the maximum value of packed unsigned byte integers
计算压缩无符号字节整数的最大值
PMAXSW Computes the maximum value of packed signed word integers
计算压缩有符号字整数的最大值
PMINUB Computes the minimum value of packed unsigned byte integers
计算压缩无符号字节整数的最小值
PMINSW Computes the minimum value of packed signed word integers
计算压缩有符号字整数的最小值
PMULHUW Multiplies packed unsigned word integers and stores the high result
对压缩无符号字整数进行乘运算,并将结果的高16位存储到目标寄存器
PSADBW Computes the sum of the absolute differences of unsigned byte integers
计算压缩无符号字节整数的差值的绝对值的总和

    限于篇幅,这些指令的具体功能,请参考 http://x86.renejeschke.de/ 该链接对应的页面。这些指令中,比较难理解的是 PSADBW 指令,从 http://x86.renejeschke.de/html/file_module_x86_id_253.html 该链接对应的页面里,我们可以看到,该指令可以使用如下的伪表达式来描述它的具体功能:

if(OperandSize == 64) {
	//PSADBW instructions when using 64-bit operands:
	Temporary0 = GetAbsoluteValue(Destination[0..7] - Source[0..7]);
	Temporary1 = GetAbsoluteValue(Destination[8..15] - Source[8..15]);
	Temporary2 = GetAbsoluteValue(Destination[16..23] - Source[16..23]);
	Temporary3 = GetAbsoluteValue(Destination[24..31] - Source[24..31]);
	Temporary4 = GetAbsoluteValue(Destination[32..39] - Source[32..39]);
	Temporary5 = GetAbsoluteValue(Destination[40..47] - Source[40..47]);
	Temporary6 = GetAbsoluteValue(Destination[48..55] - Source[48..55]);
	Temporary7 = GetAbsoluteValue(Destination[56..63] - Source[56..63]);
	Destination[0..15] = CalculateSum(Temporary0...Temporary7);
	Destination[16..63] = 0;
}
else {
	//PSADBW instructions when using 128-bit operands:
	Temporary0 = GetAbsoluteValue(Destination[0..7] - Source[0..7]);
	Temporary1 = GetAbsoluteValue(Destination[8..15] - Source[8..15]);
	Temporary2 = GetAbsoluteValue(Destination[16..23] - Source[16..23]);
	Temporary3 = GetAbsoluteValue(Destination[24..31] - Source[24..31]);
	Temporary4 = GetAbsoluteValue(Destination[32..39] - Source[32..39]);
	Temporary5 = GetAbsoluteValue(Destination[40..47] - Source[40..47]);
	Temporary6 = GetAbsoluteValue(Destination[48..55] - Source[48..55]);
	Temporary7 = GetAbsoluteValue(Destination[56..63] - Source[56..63]);
	Temporary8 = GetAbsoluteValue(Destination[64..71] - Source[64..71]);
	Temporary9 = GetAbsoluteValue(Destination[72..79] - Source[72..79]);
	Temporary10 = GetAbsoluteValue(Destination[80..87] - Source[80..87]);
	Temporary11 = GetAbsoluteValue(Destination[88..95] - Source[88..95]);
	Temporary12 = GetAbsoluteValue(Destination[96..103] - Source[96..103]);
	Temporary13 = GetAbsoluteValue(Destination[104..111] - Source[104..111]);
	Temporary14 = GetAbsoluteValue(Destination[112..119] - Source[112..119]);
	Temporary15 = GetAbsoluteValue(Destination[120..127] - Source[120..127]);
	Destination[0..15] = CalculateSum(Temporary0...Temporary7);
	Destination[16..63] = 0;
	Destination[64..79] = CalculateSum(Temporary8...Temporary15);
	Destination[80..127] = 0;
}


    有了上面的伪表达式,就容易理解PSADBW指令的作用了。

    限于篇幅,本章就到这里,下一篇介绍SSE2技术提供的指令。

    OK,休息,休息一下 o(∩_∩)o~~
上下篇

下一篇: 全书结束篇 使用IA-32平台提供的高级功能 (四) SSE2、SSE3相关指令

上一篇: 使用IA-32平台提供的高级功能 (二)

相关文章

汇编里使用Linux系统调用 (三) 系统调用结束篇

汇编开发相关工具 (一)

基本数学运算 (一)

优化汇编指令 (二)

基本数学运算 (三) 除法和移位指令

使用内联汇编 (一)