我被卡在學習彙編語言的基礎知識中,用華氏溫度對K & R的攝氏例子進行了學習。下面是我所指的C代碼:需要解釋K&R fahr-to-cels的彙編指令示例
#include <stdio.h>
main()
{
int fahr, celsius;
int lower, upper, step;
lower = 0;
upper = 300;
step = 20;
fahr = lower;
while (fahr <= upper) {
celsius = 5 * (fahr-32)/9;
printf("%d\t%d\n", fahr, celsius);
fahr = fahr + step;
}
}
隨着GCC 4.4.7(GNU/Linux的x86-64的),我獲得以下拆解:
$ gcc -O0 -g -ansi -pedantic l1-2a.c
$ gdb -q a.out
(gdb) disas /m main
(gdb) disas /m main
Dump of assembler code for function main:
6 {
0x00000000004004c4 <+0>: push %rbp
0x00000000004004c5 <+1>: mov %rsp,%rbp
0x00000000004004c8 <+4>: sub $0x20,%rsp
7 int fahr, celsius;
8 int lower, upper, step;
9
10 lower = 0;
0x00000000004004cc <+8>: movl $0x0,-0xc(%rbp)
11 upper = 300;
0x00000000004004d3 <+15>: movl $0x12c,-0x8(%rbp)
12 step = 20;
0x00000000004004da <+22>: movl $0x14,-0x4(%rbp)
13
14 fahr = lower;
0x00000000004004e1 <+29>: mov -0xc(%rbp),%eax
0x00000000004004e4 <+32>: mov %eax,-0x14(%rbp)
15 while (fahr <= upper) {
0x00000000004004e7 <+35>: jmp 0x400532 <main+110>
0x0000000000400532 <+110>: mov -0x14(%rbp),%eax
0x0000000000400535 <+113>: cmp -0x8(%rbp),%eax
0x0000000000400538 <+116>: jle 0x4004e9 <main+37>
16 celsius = 5 * (fahr-32)/9;
0x00000000004004e9 <+37>: mov -0x14(%rbp),%edx
0x00000000004004ec <+40>: mov %edx,%eax
0x00000000004004ee <+42>: shl $0x2,%eax
0x00000000004004f1 <+45>: add %edx,%eax
0x00000000004004f3 <+47>: lea -0xa0(%rax),%ecx
0x00000000004004f9 <+53>: mov $0x38e38e39,%edx
0x00000000004004fe <+58>: mov %ecx,%eax
0x0000000000400500 <+60>: imul %edx
0x0000000000400502 <+62>: sar %edx
0x0000000000400504 <+64>: mov %ecx,%eax
0x0000000000400506 <+66>: sar $0x1f,%eax
0x0000000000400509 <+69>: mov %edx,%ecx
0x000000000040050b <+71>: sub %eax,%ecx
0x000000000040050d <+73>: mov %ecx,%eax
0x000000000040050f <+75>: mov %eax,-0x10(%rbp)
17 printf("%d\t%d\n", fahr, celsius);
0x0000000000400512 <+78>: mov $0x400638,%eax
0x0000000000400517 <+83>: mov -0x10(%rbp),%edx
0x000000000040051a <+86>: mov -0x14(%rbp),%ecx
0x000000000040051d <+89>: mov %ecx,%esi
0x000000000040051f <+91>: mov %rax,%rdi
0x0000000000400522 <+94>: mov $0x0,%eax
0x0000000000400527 <+99>: callq 0x4003b8 <[email protected]>
18 fahr = fahr + step;
0x000000000040052c <+104>: mov -0x4(%rbp),%eax
0x000000000040052f <+107>: add %eax,-0x14(%rbp)
19 }
20 }
0x000000000040053a <+118>: leaveq
0x000000000040053b <+119>: retq
End of assembler dump.
什麼是不明確的,我是這個片段:
16 celsius = 5 * (fahr-32)/9;
0x00000000004004e9 <+37>: mov -0x14(%rbp),%edx
0x00000000004004ec <+40>: mov %edx,%eax
0x00000000004004ee <+42>: shl $0x2,%eax
0x00000000004004f1 <+45>: add %edx,%eax
0x00000000004004f3 <+47>: lea -0xa0(%rax),%ecx
0x00000000004004f9 <+53>: mov $0x38e38e39,%edx
0x00000000004004fe <+58>: mov %ecx,%eax
0x0000000000400500 <+60>: imul %edx
0x0000000000400502 <+62>: sar %edx
0x0000000000400504 <+64>: mov %ecx,%eax
0x0000000000400506 <+66>: sar $0x1f,%eax
0x0000000000400509 <+69>: mov %edx,%ecx
0x000000000040050b <+71>: sub %eax,%ecx
0x000000000040050d <+73>: mov %ecx,%eax
0x000000000040050f <+75>: mov %eax,-0x10(%rbp)
我的意思是,我明白了一切達:
lea -0xa0(%rax),%ecx
因爲它是從%eax
寄存器,其保持5*fahr
從其減去160
,如:
5 * (fahr-32)/9 <=> (5*fahr - 5*32)/9 <=> (5*fahr - 160)/9
從而%ecx
後(以及完整%rcx
)存儲5*fahr - 160
。然而,我不知道它如何除以9。這似乎是一些騙局,如「乘以逆」,以避免分裂,但我不知道它是如何工作的。
這就是所謂的魔數分割。有關說明,請參閱[這裏](http://ridiculousfish.com/blog/posts/labor-of-division-episode-i.html)。 – Jester 2014-12-02 22:52:58
'0x38e38e39' = 2^33/9. – 2014-12-02 22:53:02
試圖通過觀察編譯器生成的程序集來學習程序集可能是一個糟糕的主意,要知道編譯器可能會做得比在各種方式下更好地接受可讀性! – Clifford 2014-12-02 23:14:10