本文由zengl.com站长对
http://pan.baidu.com/share/link?shareid=3717576860&uk=940392313 汇编教程英文版相应章节进行翻译得来。
另外再附加一个英特尔英文手册的共享链接地址:
http://pan.baidu.com/share/link?shareid=2345340326&uk=940392313 (在某些例子中会用到)
本篇翻译对应汇编教程英文原著的第290页到第303页,翻译完前9章 (注意这里的页数不是页脚的页数,而是pdf电子档顶部,分页输入框中的页数,也就是包含了目录,前言部分的总页数,pdf电子档总页数是577,当前已翻译完的页数为303 / 577)。
Floating-Point Conditional Branches 浮点条件分支指令:
要判断一个整数是否大于,等于或小于另一个整数,可以通过CMP指令,再加上EFLAGS寄存器里的各种标志来进行判断,但是,要比较两个浮点数的大小就没有这么简单,在FPU里提供了一些专门用于浮点数比较的指令。
The FCOM instruction family FCOM指令集:
FCOM指令集可以将ST0里的浮点值与其他FPU寄存器或内存里的浮点值进行比较,该指令集中可用的指令如下表所示:
Instruction 指令 |
Description 描述 |
FCOM |
Compare the ST0 register with the ST1 register.
将ST0与ST1里的浮点数进行比较 |
FCOM ST(x) |
Compare the ST0 register with another FPU register.
将ST0与ST(x)进行比较 |
FCOM source |
Compare the ST0 register with a 32- or 64-bit
memory value.
将ST0与32位或64位内存里的浮点数进行比较 |
FCOMP |
Compare the ST0 register with the ST1 register value
and pop the stack.
将ST0和ST1进行比较,然后将ST0弹出寄存器栈 |
FCOMP ST(x) |
Compare the ST0 register with another
FPU register value and pop the stack.
将ST0与ST(x)进行比较,然后将ST0弹出寄存器栈 |
FCOMP source |
Compare the ST0 register with a 32 or 64-bit
memory value and pop the stack.
将ST0与32位或64位内存里的浮点数进行比较,然后将ST0弹出寄存器栈 |
FCOMPP |
Compare the ST0 register with the ST1 register
and pop the stack twice.
将ST0与ST1进行比较,然后将ST0与ST1都弹出寄存器栈 |
FTST |
Compare the ST0 register with the value 0.0.
将ST0与浮点值0.0进行比较 |
|
上面这些指令比较的结果会被设置到FPU状态寄存器的C0、C2和C3这三个条件代码位里,可能的比较结果如下表所示:
Condition 比较条件 |
C3 |
C2 |
C0 |
ST0 > source |
0 |
0 |
0 |
ST0 < source |
0 |
0 |
1 |
ST0 = source |
1 |
0 |
0 |
|
下面的fcomtest.s程式就演示了FCOM比较指令的用法:
# fcomtest.s - An example of the FCOM instruction
.section .data
value1:
.float 10.923
value2:
.float 4.5532
.section .text
.globl _start
_start:
nop
flds value1
fcoms value2
fstsw
sahf
ja greater
jb lessthan
movl $1, %eax
movl $0, %ebx
int $0x80
greater:
movl $1, %eax
movl $2, %ebx
int $0x80
lessthan:
movl $1, %eax
movl $1, %ebx
int $0x80
|
上面代码先通过flds value1指令将value1里的10.923加载到ST0,然后使用fcoms value2指令将ST0里的10.923与value2内存里的4.5532进行比较。
接着,代码里使用了一个技巧来检测浮点比较的结果:先用fstsw指令将FPU状态寄存器里的值设置到AX寄存器,再通过sahf指令将AH里的值加载到EFLAGS寄存器,之所以这么做,是因为AH寄存器的0 , 2 , 4 , 6 及 7位依次对应EFLAGS寄存器里的carry进位,parity奇偶,aligned对齐,zero是否为零,及sign符号标志位,而当我们通过fstsw指令将状态寄存器的值设置到AX后,AH里的0 , 2 及 6位正好就是C0,C2和C3的值,这样经过fstsw,sahf两条指令后,FPU状态寄存器和EFLAGS寄存器就会有如下的对应关系:
-
FPU状态寄存器里的C0对应EFLAGS寄存器里的carry进位标志
-
状态寄存器里的C2对应EFLAGS寄存器里的parity奇偶标志
-
状态寄存器里的C3对应EFLAGS寄存器里的zero是否为零标志
FPU里的三个条件代码位映射到EFLAGS标志寄存器后,就可以使用JA,JB,JZ之类的跳转指令来根据浮点比较的结果,执行不同的处理了,例如前面提到过,当ST0 = source时,C3就会被设置为1,那么经过映射后,对应的zero标志也就会为1,再根据前面流程控制篇里的介绍,JZ指令就会发生跳转,其他的大于,小于的情况也是同理。
之所以可以使用这一技巧来检测浮点的比较结果,都要归功于英特尔工程师的巧妙设计。
fcomtest.s程式会根据浮点数的比较结果产生不同的退出码,在linux命令行下可以使用echo $?命令来进行查看:
$ ./fcomtest
$ echo $?
2
$ |
上面输出结果为2,表示代码里value1内存里的值大于value2内存里的值。你也可以修改代码,使用不同的值来进行测试。
在检测两个浮点数是否相等时,要注意的是:浮点数在加载到FPU寄存器后,会被转为双精度扩展的浮点格式,在这一转化过程里,有可能会产生一些舍入上的误差值,这样,标准单精度或标准双精度浮点数在加载到FPU寄存器后,不一定会完全等于原来的值,所以,在这种情况下,进行等于比较时,最好是通过它们的差值是否在一定的误差范围内来进行判断。
The FCOMI instruction family FCOMI指令集:
前面的fcomtest.s例子是通过fstsw和sahf两条指令来建立起FPU状态寄存器和EFLAGS标志寄存器之间的映射关系的,那么是否存在一种指令,能够在比较后,自动完成这一映射呢?答案是:可以的。
从Pentium Pro处理器开始,就可以使用FCOMI指令在比较浮点数后,自动将FPU状态寄存器里C0,C2和C3的值设置到EFLAGS寄存器的carry进位,parity奇偶,及zero是否为零标志位。
下表显示了FCOMI指令集里可用的指令:
Instruction
指令 |
Description
描述 |
FCOMI ST(x),ST |
Compare the ST0 register with the ST(x) register.
将ST0与ST(x)进行比较 |
FCOMIP ST(x),ST |
Compare the ST0 register with the ST(x) register
and pop the stack.
将ST0与ST(x)进行比较,然后将ST0弹出寄存器栈 |
FUCOMI ST(x),ST |
Check for unordered values before the comparison.
在比较之前,先检测需要比较的值是否是有效的浮点数 |
FUCOMIP ST(x),ST |
Check for unordered values before the comparison
and pop the stack afterward.
在比较前,先检测是否是有效的浮点数,比较后,再将ST0弹出寄存器栈 |
|
上面这些FCOMI指令只能比较两个FPU寄存器里的值,不能直接和内存里的浮点数进行比较,所以如果要对内存里的值进行FCOMI的比较,就必须先将内存里的值加载到FPU寄存器。
上表中,最后两条FUCOMI和FUCOMIP指令在进行比较前,会先利用FPU的tag标记寄存器来判断需要比较的值是否是有效的浮点数,如果不是有效的浮点数,就会抛出异常。
FCOMI指令的比较结果可以直接使用EFLAGS寄存器里的各种标志来进行判断,如下表所示:
Condition 比较条件 |
ZF |
PF |
CF |
ST0 > ST(x) |
0 |
0 |
0 |
ST0 < ST(x) |
0 |
0 |
1 |
ST0 = ST(x) |
1 |
0 |
0 |
|
下面的fcomitest.s程式就演示了FCOMI指令的用法:
# fcomitest.s - An example of the FCOMI instruction
.section .data
value1:
.float 10.923
value2:
.float 4.5532
.section .text
.globl _start
_start:
nop
flds value2
flds value1
fcomi %st(1), %st(0)
ja greater
jb lessthan
movl $1, %eax
movl $0, %ebx
int $0x80
greater:
movl $1, %eax
movl $2, %ebx
int $0x80
lessthan:
movl $1, %eax
movl $1, %ebx
int $0x80
|
上面代码里依次将value2和value1加载到FPU寄存器栈,这样加载后,value1里的10.923就会位于ST0,而value2里的4.5532就会位于ST1,然后使用FCOMI指令进行比较后,EFLAGS寄存器里就保存了比较的结果,接着就可以使用JA和JB指令来进行大于,小于的判断操作。
fcomitest.s经过汇编链接后,测试结果如下:
$ as -gstabs -o fcomitest.o fcomitest.s
$ ld -o fcomitest fcomitest.o
$ ./fcomitest
$ echo $?
2
$ |
可以看到FCOMI指令的比较结果和之前提到的FCOM指令的比较结果一致,你也可以使用其他的值进行测试。
The FCMOV instruction family FCMOV指令集:
类似于整数的CMOV指令,FCMOV指令集可以让你根据条件来进行浮点数的传值,该指令集里的每个指令都是根据EFLAGS标志寄存器里的各种标志来决定是否需要将ST(x)里的值传值到ST0 。
FCMOV指令通常会配合FCOMI指令来一起使用,因为FCOMI在完成浮点数的大小比较后,会自动设置EFLAGS里对应的标志,而这些标志就可以让FCMOV决定是否需要进行传值操作。
下表显示了FCMOV指令集里可用的各种指令:
Instruction
指令 |
Description
描述 |
FCMOVB |
Move if ST(0) is below ST(x).
如果ST0小于ST(x),即EFLAGS里的carry标志被设置时,就进行传值 |
FCMOVE |
Move if ST(0) is equal to ST(x).
如果ST0等于ST(x),即EFLAGS里的zero标志被设置时,就进行传值 |
FCMOVBE |
Move if ST(0) is below or equal to ST(x).
如果ST0小于等于ST(x),即EFLAGS里的carry或zero标志被设置时,就进行传值 |
FCMOVU |
Move if ST(0) is unordered.
浮点无序传值,即当EFLAGS里的Parity标志被设置时,就进行传值 |
FCMOVNB |
Move if ST(0) is not below ST(x).
如果ST0不小于ST(x),即EFLAGS里的carry标志清零时,就进行传值 |
FCMOVNE |
Move it ST(0) is not equal to ST(x).
如果ST0不等于ST(x),即EFLAGS里的zero标志被清零时,就进行传值 |
FCMOVNBE |
Move if ST(0) is not below or equal to ST(x).
如果ST0不小于等于ST(x),即EFLAGS里的carry和zero标志都为零时,就进行传值 |
FCMOVNU |
Move if ST(0) is not unordered.
浮点有序传值,即当EFLAGS里的Parity标志被清零时,就进行传值 |
|
FCMOV指令集的指令格式如下:
fcmovxx source, destination |
上面格式里,source源操作数是ST(x)寄存器,而destination目标操作数是ST(0)寄存器。
下面的fcmovtest.s程式演示了FCMOV指令的用法:
# fcmovtest.s - An example of the FCMOVxx instructions
.section .data
value1:
.float 20.5
value2:
.float 10.90
.section .text
.globl _start
_start:
nop
finit
flds value1
flds value2
fcomi %st(1), %st(0)
fcmovb %st(1), %st(0)
movl $1, %eax
movl $0, %ebx
int $0x80
|
上面的代码先将value1和value2依次压入FPU寄存器栈,完成压栈操作后,ST0里为10.90,ST1里则为20.5,接着通过FCOMI指令比较ST0和ST1的大小,并根据比较的结果设置EFLAGS里的各种标志,由于ST0小于ST1,所以EFLAGS里的carry标志就会被设置,最后执行FCMOVB指令时,就会将ST1里的20.5传值给ST0,结果就是ST0和ST1里的值都为20.5,该程序调试输出的情况如下:
$ as -gstabs -o fcmovtest.o fcmovtest.s
$ ld -o fcmovtest fcmovtest.o
$ gdb -q fcmovtest
Reading symbols from /home/zengl/Downloads/asm_example/fcmov/fcmovtest...done.
....................................
15 fcmovb %st(1), %st(0)
(gdb) s
16 movl $1, %eax
(gdb) info all
....................................
eflags 0x203 [ CF IF ]
....................................
st0 20.5 (raw 0x4003a400000000000000)
st1 20.5
....................................
(gdb) |
提示:FCMOV指令集只能用于Pentium Pro及以后的处理器,之前的处理器则不能使用这些指令。
Saving and Restoring the FPU State 保存和恢复FPU的状态:
之前的章节提到过,FPU的数据寄存器还可以用作MMX的寄存器,用于存放80位的压缩整数,所以如果你的程序既要使用MMX的指令,又要使用FPU的浮点指令的话,就容易出错,庆幸的是,英特尔提供了一些特殊的指令来保存和恢复FPU的的当前工作状态,这样当你进行MMX的工作流程之前,可以先将FPU的当前状态保存到内存里,等MMX相关运算完毕,再切换回之前的工作状态,这样程序就不容易出错了。
下面就介绍保存和恢复FPU工作状态相关的指令。
Saving and restoring the FPU environment 保存和恢复FPU的环境:
我们可以使用FSTENV指令来将FPU的当前环境保存到内存里,该指令会将FPU里下面这些寄存器的内容进行保存:
-
Control register 控制寄存器
-
Status register 状态寄存器
-
Tag register 标记寄存器
-
FPU instruction pointer offset FPU指令指针偏移值
-
FPU data pointer FPU数据指针
-
FPU last opcode executed FPU里最后执行过的操作码
这些值会被保存在一个28字节的内存块里,可以使用FLDENV指令来将内存里保存的这些数据恢复到FPU环境中,下面的fpuenv.s程式就演示了这些指令的用法:
# fpuenv.s - An example of the FSTENV and FLDENV instructions
.section .data
value1:
.float 12.34
value2:
.float 56.789
rup:
.byte 0x7f, 0x0b
.section .bss
.lcomm buffer, 28
.section .text
.globl _start
_start:
nop
finit
flds value1
flds value2
fldcw rup
fstenv buffer
finit
flds value2
flds value1
fldenv buffer
movl $1, %eax
movl $0, %ebx
int $0x80
|
fpuenv.s程式在初始化FPU后,将value1和value2依次加载到FPU寄存器栈,然后修改了控制寄存器,将控制寄存器里的舍入方式设为向上舍入,接着使用FSTENV指令将FPU当前的环境保存到buffer内存位置,在gdb调试器里,可以通过x命令来查看buffer里保存的数据:
(gdb) info float
Status Word: 0x3000
TOP: 6
Control Word: 0x0b7f IM DM ZM OM UM PM
PC: Extended Precision (64-bits)
RC: Round up
(gdb) x/28bx &buffer
0x80490c0 <buffer>: 0x7f 0x0b 0xff 0xff 0x00 0x30 0xff 0xff
0x80490c8 <buffer+8>: 0xff 0x0f 0xff 0xff 0xc5 0x3c 0x01 0xc1
0x80490d0 <buffer+16>: 0x60 0x00 0x80 0x03 0xac 0xdc 0x60 0xc5
0x80490d8 <buffer+24>: 0x7b 0x00 0xff 0xff
(gdb)
|
可以看到buffer内存里的0x7f 0x0b对应控制寄存器里的值,0x00 0x30对应状态寄存器里的值。在数据保存到内存中后,代码再通过finit指令对FPU重新初始化,并重新加载了两个值到寄存器栈,可以通过info float及info all命令来查看重新初始化FPU后寄存器里的值。
最后,代码使用fldenv buffer指令将buffer里保存的数据恢复到FPU中,FPU里的控制寄存器将恢复为之前设置的向上舍入方式,但是FPU数据寄存器里的值并不会被恢复,因为FSTENV和FLDENV指令只能备份和恢复除了数据寄存器以外的其他寄存器,如果想将数据寄存器里的值也备份到内存,则可以使用下面要介绍的指令。
Saving and restoring the FPU state 保存和恢复FPU的完整工作状态:
前面的FSTENV和FLDENV指令不能保存数据寄存器,如果想将数据寄存器也一起进行保存和恢复的话,可以使用FSAVE和FRSTOR指令,其中,FSAVE指令用于将FPU里所有的寄存器包括数据寄存器里的值保存到一个108字节的内存块里,然后重新初始化FPU。而FRSTOR指令则用于将FSAVE指令保存的数据恢复到FPU寄存器。
下面的fpusave.s程式就演示了FSAVE和FRSTOR指令的用法:
# fpusave.s - An example of the FSAVE and FRSTOR instructions
.section .data
value1:
.float 12.34
value2:
.float 56.789
rup:
.byte 0x7f, 0x0b
.section .bss
.lcomm buffer, 108
.section .text
.globl _start
_start:
nop
finit
flds value1
flds value2
fldcw rup
fsave buffer
flds value2
flds value1
frstor buffer
movl $1, %eax
movl $0, %ebx
int $0x80
|
在FLDS指令加载数据到寄存器栈,及FLDCW指令将控制寄存器设为向上舍入后,就使用FSAVE指令将FPU里的所有数据保存到buffer内存里,在FSAVE指令执行之前,FPU寄存器的情况如下:
(gdb) info all
.................................
st0 56.78900146484375 (raw 0x4004e327f00000000000)
st1 12.340000152587890625 (raw 0x4002c570a40000000000)
st2 0 (raw 0x00000000000000000000)
st3 0 (raw 0x00000000000000000000)
st4 0 (raw 0x00000000000000000000)
st5 0 (raw 0x00000000000000000000)
st6 0 (raw 0x00000000000000000000)
st7 0 (raw 0x00000000000000000000)
fctrl 0xb7f 2943
fstat 0x3000 12288
ftag 0xfff 4095
.................................
(gdb) info float
R7: Valid 0x4002c570a40000000000 +12.34000015258789062
=>R6: Valid 0x4004e327f00000000000 +56.78900146484375
R5: Empty 0x00000000000000000000
R4: Empty 0x00000000000000000000
R3: Empty 0x00000000000000000000
R2: Empty 0x00000000000000000000
R1: Empty 0x00000000000000000000
R0: Empty 0x00000000000000000000
Status Word: 0x3000
TOP: 6
Control Word: 0x0b7f IM DM ZM OM UM PM
PC: Extended Precision (64-bits)
RC: Round up
Tag Word: 0x0fff
(gdb) |
可以看到value1和value2里的两个值被加载到寄存器栈,同时fctrl控制寄存器的值为0xb7f即舍入方式被设为
Round up向上舍入。
当FSAVE指令执行完后,FPU寄存器的情况如下:
(gdb) info all
.................................
st0 0 (raw 0x00000000000000000000)
st1 0 (raw 0x00000000000000000000)
st2 0 (raw 0x00000000000000000000)
st3 0 (raw 0x00000000000000000000)
st4 0 (raw 0x00000000000000000000)
st5 0 (raw 0x00000000000000000000)
st6 56.78900146484375 (raw 0x4004e327f00000000000)
st7 12.340000152587890625 (raw 0x4002c570a40000000000)
fctrl 0x37f 895
fstat 0x0 0
ftag 0xffff 65535
.................................
(gdb) info float
R7: Empty 0x4002c570a40000000000
R6: Empty 0x4004e327f00000000000
R5: Empty 0x00000000000000000000
R4: Empty 0x00000000000000000000
R3: Empty 0x00000000000000000000
R2: Empty 0x00000000000000000000
R1: Empty 0x00000000000000000000
=>R0: Empty 0x00000000000000000000
Status Word: 0x0000
TOP: 0
Control Word: 0x037f IM DM ZM OM UM PM
PC: Extended Precision (64-bits)
RC: Round to nearest
Tag Word: 0xffff
.................................
(gdb) |
可以看到,FSAVE指令会将FPU进行重新初始化,控制寄存器的值恢复为默认的0x037f,此时舍入方式为默认的
Round to nearest(舍入到最接近的值)。之所以value1和value2的值
56.789和
12.34会跑到栈底ST6及ST7的位置,是因为Status状态寄存器由于被重新初始化为默认值,
TOP为0,即此时的栈顶ST0对应的是R0寄存器(在FSAVE执行前,ST0对应的是R6寄存器),ST6对应R6,ST7对应R7,所以之前加载的两个数就跑到栈底去了,另外R6和R7显示
Empty,是因为Tag标记寄存器也初始化为默认值,所有的数据寄存器此时都被认为是空的,即使R6和R7里有数据,也会被FPU认为是空的,之后再FLDS加载数据时,R7和R6的数据就会被覆盖掉(FPU重新初始化时不会将数据寄存器里的数据清空,只会将其他寄存器初始化为默认值)。
另外,FSAVE指令还会将FPU之前的数据都保存到buffer内存位置:
(gdb) x/108bx &buffer
0x80490c0 <buffer>: 0x7f 0x0b 0xff 0xff 0x00 0x30 0xff 0xff
0x80490c8 <buffer+8>: 0xff 0x0f 0xff 0xff 0xc5 0x3c 0x01 0xc1
0x80490d0 <buffer+16>: 0x60 0x00 0x80 0x03 0xec 0x1c 0x4d 0xc5
0x80490d8 <buffer+24>: 0x7b 0x00 0xff 0xff 0x00 0x00 0x00 0x00
0x80490e0 <buffer+32>: 0x00 0xf0 0x27 0xe3 0x04 0x40 0x00 0x00
0x80490e8 <buffer+40>: 0x00 0x00 0x00 0xa4 0x70 0xc5 0x02 0x40
0x80490f0 <buffer+48>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x80490f8 <buffer+56>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x8049100 <buffer+64>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x8049108 <buffer+72>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x8049110 <buffer+80>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x8049118 <buffer+88>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x8049120 <buffer+96>: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x8049128 <buffer+104>: 0x00 0x00 0x00 0x00
(gdb)
|
此时buffer内存里的值不仅包括FPU控制,状态及标记寄存器里的值,还包括所有数据寄存器里的值。在执行完FRSTOR指令后,FPU寄存器的情况如下:
(gdb) info all
...............................
st0 56.78900146484375 (raw 0x4004e327f00000000000)
st1 12.340000152587890625 (raw 0x4002c570a40000000000)
st2 0 (raw 0x00000000000000000000)
st3 0 (raw 0x00000000000000000000)
st4 0 (raw 0x00000000000000000000)
st5 0 (raw 0x00000000000000000000)
st6 0 (raw 0x00000000000000000000)
st7 0 (raw 0x00000000000000000000)
fctrl 0xb7f 2943
fstat 0x3000 12288
ftag 0xfff 4095
...............................
(gdb) |
从上面的输出可以看到,数据寄存器和控制寄存器里的值都恢复为FSAVE执行之前的值了。
Waiting versus Nonwaiting Instructions 等待与非等待指令:
在之前的FPU寄存器介绍篇里,曾提到过,状态寄存器里有6个浮点异常标志位,如果浮点指令执行时发生了异常(如除零异常),就会设置对应的异常标志位,大部分的浮点指令,如前面提到的FSAVE指令,在执行前都会先检测之前的浮点指令是否产生了异常,如果产生了异常,就必须等待异常处理完毕,再继续执行当前的指令。这些指令就属于等待指令。
FPU里还存在一种非等待指令,可以不处理之前指令产生的异常,而直接执行,可用的非等待指令如下表所示:
Instruction
指令 |
Description
描述 |
FNCLEX |
Clear the floating-point exception flags
清理浮点异常标志 |
FNSAVE |
Save the FPU state in memory
保存FPU状态到内存里 |
FNSTCW |
Save the FPU control register.
保存FPU控制寄存器的值 |
FNSTENV |
Save the FPU operating environment in memory.
保存FPU的操作环境到内存里 |
FNSTSW |
Save the FPU status register in memory
or the AX register.
将FPU状态寄存器的值保存到内存或AX寄存器 |
|
Optimizing Floating-Point Calculations 优化浮点计算:
浮点计算经常是汇编程序里最耗时的部分,英特尔提供了一些建议,可以帮助优化你的汇编浮点计算:
-
确保要操作的浮点数没有超过数据寄存器可以容纳的尺寸
-
没有很高精度要求的计算中,将精度控制位设为单精度
-
对于简单的三角函数计算,尽量使用查询表的方法
-
尽可能打破依赖链,例如可以将z = a + b + c + d表达式替换为
x = a + b; y = c + d; z = x + y.这三个表达式。
-
尽可能的将方程式里的值保存在FPU寄存器里
-
当整数和浮点数一起计算时,最好将整数加载到FPU寄存器,再进行计算,
这样比直接和整数进行浮点运算的速度要快,例如FIDIV指令可以替换为:
先用FILD加载整数到FPU寄存器,再使用FDIVP指令对FPU寄存器里的值进行运算。
-
尽可能使用FCOMI指令来代替FCOM指令。
最后就是原著第9章的总结部分,限于篇幅就不多说了,下一篇开始介绍汇编里和字符串操作相关的指令。
转载请注明来源:www.zengl.com
OK,就到这里,休息,休息一下 o(∩_∩)o~~