程序11-3 linux/kernel/math/ea.c


  1 /*

  2  * linux/kernel/math/ea.c

  3  *

  4  * (C) 1991 Linus Torvalds

  5  */

  6

  7 /*

  8  * Calculate the effective address.

  9  */

    /*

     * 计算有效地址。

     */

 10

 11 #include <stddef.h>         // 标准定义头文件。本程序使用了其中的offsetof()定义。

 12

 13 #include <linux/math_emu.h> // 协处理器头文件。定义临时实数结构和387寄存器操作宏等。

 14 #include <asm/segment.h>    // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。

 15

    // info结构中各个寄存器在结构中的偏移位置。offsetof()用于求指定字段在结构中的偏移位

    // 置。参见include/stddef.h文件。

 16 static int __regoffset[] = {

 17         offsetof(struct info,___eax),

 18         offsetof(struct info,___ecx),

 19         offsetof(struct info,___edx),

 20         offsetof(struct info,___ebx),

 21         offsetof(struct info,___esp),

 22         offsetof(struct info,___ebp),

 23         offsetof(struct info,___esi),

 24         offsetof(struct info,___edi)

 25 };

 26

    // info结构中指定位置处寄存器内容。

 27 #define REG(x) (*(long *)(__regoffset[(x)]+(char *) info))

 28

    // 2字节寻址模式中第2操作数指示字节SIBScaleIndexBase)的值。

 29 static char * sib(struct info * info, int mod)

 30 {

 31         unsigned char ss,index,base;

 32         long offset = 0;

 33

    // 首先从用户代码段中取得SIB字节,然后取出各个字段比特位值。

 34         base = get_fs_byte((char *) EIP);

 35         EIP++;

 36         ss = base >> 6;                    // 比例因子大小ss

 37         index = (base >> 3) & 7;           // 索引值索引代号index

 38         base &= 7;                         // 基地址代号base

    // 如果索引代号为0b100,表示无索引偏移值。否则索引偏移值offset=对应寄存器内容*比例因子。

 39         if (index == 4)

 40                 offset = 0;

 41         else

 42                 offset = REG(index);

 43         offset <<= ss;

    // 如果上一MODRM字节中的MOD不为零,或者Base不等于0b101,则表示有偏移值在base指定的

    // 寄存器中。因此偏移offset需要再加上base对应寄存器中的内容。

 44         if (mod || base != 5)

 45                 offset += REG(base);

    // 如果MOD=1,则表示偏移值为1字节。否则,若MOD=2,或者base=0b101,则偏移值为4字节。

 46         if (mod == 1) {

 47                 offset += (signed char) get_fs_byte((char *) EIP);

 48                 EIP++;

 49         } else if (mod == 2 || base == 5) {

 50                 offset += (signed) get_fs_long((unsigned long *) EIP);

 51                 EIP += 4;

 52         }

    // 最后保存并返回偏移值。

 53         I387.foo = offset;

 54         I387.fos = 0x17;

 55         return (char *) offset;

 56 }

 57

    // 根据指令中寻址模式字节计算有效地址值。

 58 char * ea(struct info * info, unsigned short code)

 59 {

 60         unsigned char mod,rm;

 61         long * tmp = &EAX;

 62         int offset = 0;

 63

    // 首先取代码中的MOD字段和R/M字段值。如果MOD=0b11,表示是单字节指令,没有偏移字段。

    // 如果R/M字段=0b100,并且MOD不为0b11,表示是2字节地址模式寻址,因此调用sib()

    // 出偏移值并返回即可。

 64         mod = (code >> 6) & 3;          // MOD字段。

 65         rm = code & 7;                  // R/M字段。

 66         if (rm == 4 && mod != 3)

 67                 return sib(info,mod);

    // 如果R/M字段为0b101,并且MOD0,表示是单字节地址模式编码且后随32字节偏移值。

    // 于是取出用户代码中4字节偏移值,保存并返回之。

 68         if (rm == 5 && !mod) {

 69                 offset = get_fs_long((unsigned long *) EIP);

 70                 EIP += 4;

 71                 I387.foo = offset;

 72                 I387.fos = 0x17;

 73                 return (char *) offset;

 74         }

    // 对于其余情况,则根据MOD进行处理。首先取出R/M代码对应寄存器内容的值作为指针tmp

    // 对于MOD=0,无偏移值。对于MOD=1,代码后随1字节偏移值。对于MOD=2,代码后有4字节

    // 偏移值。最后保存并返回有效地址值。

 75         tmp = & REG(rm);

 76         switch (mod) {

 77                 case 0: offset = 0; break;

 78                 case 1:

 79                         offset = (signed char) get_fs_byte((char *) EIP);

 80                         EIP++;

 81                         break;

 82                 case 2:

 83                         offset = (signed) get_fs_long((unsigned long *) EIP);

 84                         EIP += 4;

 85                         break;

 86                 case 3:

 87                         math_abort(info,1<<(SIGILL-1));

 88         }

 89         I387.foo = offset;

 90         I387.fos = 0x17;

 91         return offset + (char *) *tmp;

 92 }

 93