linux-0.12 div64的实现分析

首先要说明的是,书中注释的一个错误,真是害人不浅,严重影响后续代码的阅读。

AT&T汇编:sub A, B,应该是B – A,结果放入B中。所以try_sub (a, b) 就是b– a,结果放入b中。这样再来看div64的实现:

static void div64(int * a, int * b, int * c)

{

int tmp[4];

int i;

unsigned int mask = 0;

c += 4;

for (i = 0 ; i<64 ; i++) {

if (!(mask >>= 1)) {

c–;

mask = 0x80000000;

}

tmp[0] = a[0]; tmp[1] = a[1];

tmp[2] = a[2]; tmp[3] = a[3];

if (try_sub(b,tmp)) {

*c |= mask;

a[0] = tmp[0]; a[1] = tmp[1];

a[2] = tmp[2]; a[3] = tmp[3];

}

shift_right(b);

}

}

本质上就是一个手工竖式除法运算。这里的a和b都是指向16字节内存的指针(在模拟程序中,我们只考虑64位的精度,所以足够了)。数据在a和b中都是向最高位对齐的(参见fdiv的实现),也就是说a[3],a[2]中存放了被除数,b[3],b[2]中是除数。例如,被除数111011和除数1011,在内存中的摆放形式(这里是逻辑形式,不考虑物理倒序):

  1. 11101100 … 000000000 – 128位
  2. 10110000 … 000000000 – 128 位

这样按照传统的手工计算方法10110000 … 000000000 ) 11101100 … 000000000,就是#1 – #2,结果值作为#1,然后#2右移移位,对齐上方竖式,继续#1 – #2。直到不够减为止。够减与不够减就是通过if (try_sub (b, tmp)) 来判断。商存放在c所指内存中,也是向最高位对齐的。商的计算很巧妙,够减则在对应位上置1。

总的来说,二进制的除法,因为只有0和1,所以整个过程相对于十进制简单了许多,只要相减移位即可。