程序11-7 linux/kernel/math/get_put.c


  1 /*

  2  * linux/kernel/math/get_put.c

  3  *

  4  * (C) 1991 Linus Torvalds

  5  */

  6

  7 /*

  8  * This file handles all accesses to user memory: getting and putting

  9  * ints/reals/BCD etc. This is the only part that concerns itself with

 10  * other than temporary real format. All other cals are strictly temp_real.

 11  */

    /*

     * 本程序处理所有对用户内存的访问:获取和存入指令/实数值/BCD数值等。这是

     * 涉及临时实数以外其他格式仅有的部分。所有其他运算全都使用临时实数格式。

     */

 12 #include <signal.h>       // 信号头文件。定义信号符号,信号结构及信号操作函数原型。

 13

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

 15 #include <linux/kernel.h>   // 内核头文件。含有一些内核常用函数的原形定义。

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

 17

    // 取用户内存中的短实数(单精度实数)。

    // 根据浮点指令代码中寻址模式字节中的内容和info结构中当前寄存器中的内容,取得短实数

    // 所在有效地址(math/ea.c),然后从用户数据区读取相应实数值。最后把用户短实数转换成

    // 临时实数(math/convert.c)。

    // 参数:tmp – 转换成临时实数后的指针;info – info结构指针;code – 指令代码。

 18 void get_short_real(temp_real * tmp,

 19         struct info * info, unsigned short code)

 20 {

 21         char * addr;

 22         short_real sr;

 23

 24         addr = ea(info,code);                        // 计算有效地址。

 25         sr = get_fs_long((unsigned long *) addr);    // 取用户数据区中的值。

 26         short_to_temp(&sr,tmp);                      // 转换成临时实数格式。

 27 }

 28

    // 取用户内存中的长实数(双精度实数)。

    // 首先根据浮点指令代码中寻址模式字节中的内容和info结构中当前寄存器中的内容,取得长

    // 实数所在有效地址(math/ea.c),然后从用户数据区读取相应实数值。最后把用户实数值转

    // 换成临时实数(math/convert.c)。

    // 参数:tmp – 转换成临时实数后的指针;info – info结构指针;code – 指令代码。

 29 void get_long_real(temp_real * tmp,

 30         struct info * info, unsigned short code)

 31 {

 32         char * addr;

 33         long_real lr;

 34

 35         addr = ea(info,code);                             // 取指令中的有效地址值。

 36         lr.a = get_fs_long((unsigned long *) addr);       // 取长8字节实数。

 37         lr.b = get_fs_long(1 + (unsigned long *) addr);

 38         long_to_temp(&lr,tmp);                            // 转换成临时实数格式。

 39 }

 40

    // 取用户内存中的临时实数。

    // 首先根据浮点指令代码中寻址模式字节中的内容和info结构中当前寄存器中的内容,取得临

    // 时实数所在有效地址(math/ea.c),然后从用户数据区读取相应临时实数值。

    // 参数:tmp – 转换成临时实数后的指针;info – info结构指针;code – 指令代码。

 41 void get_temp_real(temp_real * tmp,

 42         struct info * info, unsigned short code)

 43 {

 44         char * addr;

 45

 46         addr = ea(info,code);                             // 取指令中的有效地址值。

 47         tmp->a = get_fs_long((unsigned long *) addr);

 48         tmp->b = get_fs_long(1 + (unsigned long *) addr);

 49         tmp->exponent = get_fs_word(4 + (unsigned short *) addr);

 50 }

 51

    // 取用户内存中的短整数并转换成临时实数格式。

    // 临时整数也用10字节表示。其中低8字节是无符号整数值,高2字节表示指数值和符号位。

    // 如果高2字节最高有效位为1,则表示是负数;若最高有效位是0,表示是正数。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和 info结构中当前寄存器中的内容,

    // 取得短整数所在有效地址(math/ea.c),然后从用户数据区读取相应整数值,并保存为临时

    // 整数格式。最后把临时整数值转换成临时实数(math/convert.c)。

    // 参数:tmp – 转换成临时实数后的指针;info – info结构指针;code – 指令代码。

 52 void get_short_int(temp_real * tmp,

 53         struct info * info, unsigned short code)

 54 {

 55         char * addr;

 56         temp_int ti;

 57

 58         addr = ea(info,code);               // 取指令中的有效地址值。

 59         ti.a = (signed short) get_fs_word((unsigned short *) addr);

 60         ti.b = 0;

 61         if (ti.sign = (ti.a < 0))           // 若是负数,则设置临时整数符号位。

 62                 ti.a = - ti.a;              // 临时整数“尾数”部分为无符号数。

 63         int_to_real(&ti,tmp);               // 把临时整数转换成临时实数格式。

 64 }

 65

    // 取用户内存中的长整数并转换成临时实数格式。

    // 首先根据浮点指令代码中寻址模式字节中的内容和info结构中当前寄存器中的内容,取得长

    // 整数所在有效地址(math/ea.c),然后从用户数据区读取相应整数值,并保存为临时整数格

    // 式。最后把临时整数值转换成临时实数(math/convert.c)。

    // 参数:tmp – 转换成临时实数后的指针;info – info结构指针;code – 指令代码。

 66 void get_long_int(temp_real * tmp,

 67         struct info * info, unsigned short code)

 68 {

 69         char * addr;

 70         temp_int ti;

 71

 72         addr = ea(info,code);               // 取指令中的有效地址值。

 73         ti.a = get_fs_long((unsigned long *) addr);

 74         ti.b = 0;

 75         if (ti.sign = (ti.a < 0))           // 若是负数,则设置临时整数符号位。

 76                 ti.a = - ti.a;              // 临时整数“尾数”部分为无符号数。

 77         int_to_real(&ti,tmp);               // 把临时整数转换成临时实数格式。

 78 }

 79

    // 取用户内存中的64位长整数并转换成临时实数格式。

    // 首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,取得

    // 64位长整数所在有效地址(math/ea.c),然后从用户数据区读取相应整数值,并保存为临

    // 时整数格式。最后再把临时整数值转换成临时实数(math/convert.c)。

    // 参数:tmp – 转换成临时实数后的指针;info – info结构指针;code – 指令代码。

 80 void get_longlong_int(temp_real * tmp,

 81         struct info * info, unsigned short code)

 82 {

 83         char * addr;

 84         temp_int ti;

 85

 86         addr = ea(info,code);                            // 取指令中的有效地址值。

 87         ti.a = get_fs_long((unsigned long *) addr);      // 取用户64位长整数。

 88         ti.b = get_fs_long(1 + (unsigned long *) addr);

 89         if (ti.sign = (ti.b < 0))                   // 若是负数则设置临时整数符号位。

 90                 __asm__("notl %0 ; notl %1\n\t"     // 同时取反加1和进位调整。

 91                         "addl $1,%0 ; adcl $0,%1"

 92                         :"=r" (ti.a),"=r" (ti.b)

 93                         :"" (ti.a),"1" (ti.b));

 94         int_to_real(&ti,tmp);                       // 把临时整数转换成临时实数格式。

 95 }

 96

    // 将一个64位整数(例如N)乘10

    // 这个宏用于下面BCD码数值转换成临时实数格式过程中。方法是:N<<1 + N<<3

 97 #define MUL10(low,high) \

 98 __asm__("addl %0,%0 ; adcl %1,%1\n\t" \

 99 "movl %0,%%ecx ; movl %1,%%ebx\n\t" \

100 "addl %0,%0 ; adcl %1,%1\n\t" \

101 "addl %0,%0 ; adcl %1,%1\n\t" \

102 "addl %%ecx,%0 ; adcl %%ebx,%1" \

103 :"=a" (low),"=d" (high) \

104 :"" (low),"1" (high):"cx","bx")

105

    // 64位加法。

    // 32位的无符号数val加到64位数 <high,low> 中。

106 #define ADD64(val,low,high) \

107 __asm__("addl %4,%0 ; adcl $0,%1":"=r" (low),"=r" (high) \

108 :"" (low),"1" (high),"r" ((unsigned long) (val)))

109

    // 取用户内存中的BCD码数值并转换成临时实数格式。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得 BCD码所在有效地址(math/ea.c),然后从用户数据区读取 10字节相应BCD码值(其

    // 1字节用于符号),同时转换成临时整数形式。最后把临时整数值转换成临时实数。

    // 参数:tmp – 转换成临时实数后的指针;info – info结构指针;code – 指令代码。

110 void get_BCD(temp_real * tmp, struct info * info, unsigned short code)

111 {

112         int k;

113         char * addr;

114         temp_int i;

115         unsigned char c;

116

    // 取得BCD码数值所在内存有效地址。然后从最后1BCD码字节(最高有效位)开始处理。

    // 先取得BCD码数值的符号位,并设置临时整数的符号位。然后把9字节的BCD码值转换成

    // 临时整数格式,最后再把临时整数值转换成临时实数。

117         addr = ea(info,code);                      // 取有效地址。

118         addr += 9;                                 // 指向最后一个(第10个)字节。

119         i.sign = 0x80 & get_fs_byte(addr--);       // 取其中符号位。

120         i.a = i.b = 0;

121         for (k = 0; k < 9; k++) {                  // 转换成临时整数格式。

122                 c = get_fs_byte(addr--);

123                 MUL10(i.a, i.b);

124                 ADD64((c>>4), i.a, i.b);

125                 MUL10(i.a, i.b);

126                 ADD64((c&0xf), i.a, i.b);

127         }

128         int_to_real(&i,tmp);                       // 转换成临时实数格式。

129 }

130

    // 把运算结果以短(单精度)实数格式保存到用户数据区中。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得保存结果的有效地址addr,然后把临时实数格式的结果转换成短实数格式并存储到有效

    // 地址addr处。

    // 参数:tmp – 临时实数格式结果值;info – info结构指针;code – 指令代码。

131 void put_short_real(const temp_real * tmp,

132         struct info * info, unsigned short code)

133 {

134         char * addr;

135         short_real sr;

136

137         addr = ea(info,code);                       // 取有效地址。

138         verify_area(addr,4);                        // 为保存结果验证或分配内存。

139         temp_to_short(tmp,&sr);                     // 结果转换成短实数格式。

140         put_fs_long(sr,(unsigned long *) addr);     // 存储数据到用户内存区。

141 }

142

    // 把运算结果以长(双精度)实数格式保存到用户数据区中。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得保存结果的有效地址addr,然后把临时实数格式的结果转换成长实数格式,并存储到有

    // 效地址addr处。

    // 参数:tmp – 临时实数格式结果值;info – info结构指针;code – 指令代码。

143 void put_long_real(const temp_real * tmp,

144         struct info * info, unsigned short code)

145 {

146         char * addr;

147         long_real lr;

148

149         addr = ea(info,code);                         // 取有效地址。

150         verify_area(addr,8);                          // 为保存结果验证或分配内存。

151         temp_to_long(tmp,&lr);                       // 结果转换成长实数格式。

152         put_fs_long(lr.a, (unsigned long *) addr);   // 存储数据到用户内存区。

153         put_fs_long(lr.b, 1 + (unsigned long *) addr);

154 }

155

    // 把运算结果以临时实数格式保存到用户数据区中。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得保存结果的有效地址addr,然后把临时实数存储到有效地址addr处。

    // 参数:tmp – 临时实数格式结果值;info – info结构指针;code – 指令代码。

156 void put_temp_real(const temp_real * tmp,

157         struct info * info, unsigned short code)

158 {

159         char * addr;

160

161         addr = ea(info,code);                        // 取有效地址。

162         verify_area(addr,10);                        // 为保存结果验证或分配内存。

163         put_fs_long(tmp->a, (unsigned long *) addr); // 存储数据到用户内存区。

164         put_fs_long(tmp->b, 1 + (unsigned long *) addr);

165         put_fs_word(tmp->exponent, 4 + (short *) addr);

166 }

167

    // 把运算结果以短整数格式保存到用户数据区中。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得保存结果的有效地址addr,然后把临时实数格式的结果转换成临时整数格式。如果是负

    // 数则设置整数符号位。最后把整数保存到用户内存中。

    // 参数:tmp – 临时实数格式结果值;info – info结构指针;code – 指令代码。

168 void put_short_int(const temp_real * tmp,

169         struct info * info, unsigned short code)

170 {

171         char * addr;

172         temp_int ti;

173

174         addr = ea(info,code);                        // 取有效地址。

175         real_to_int(tmp,&ti);                        // 转换成临时整数格式。

176         verify_area(addr,2);                         // 验证或分配存储内存。

177         if (ti.sign)                                 // 若有符号位,则取负数值。

178                 ti.a = -ti.a;

179         put_fs_word(ti.a,(short *) addr);            // 存储到用户数据区中。

180 }

181

    // 把运算结果以长整数格式保存到用户数据区中。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得保存结果的有效地址addr,然后把临时实数格式的结果转换成临时整数格式。如果是负

    // 数则设置整数符号位。最后把整数保存到用户内存中。

    // 参数:tmp – 临时实数格式结果值;info – info结构指针;code – 指令代码。

182 void put_long_int(const temp_real * tmp,

183         struct info * info, unsigned short code)

184 {

185         char * addr;

186         temp_int ti;

187

188         addr = ea(info,code);                        // 取有效地址。

189         real_to_int(tmp,&ti);                        // 转换成临时整数格式。

190         verify_area(addr,4);                         // 验证或分配存储内存。

191         if (ti.sign)                                 // 若有符号位,则取负数值。

192                 ti.a = -ti.a;

193         put_fs_long(ti.a,(unsigned long *) addr);    // 存储到用户数据区中。

194 }

195

    // 把运算结果以64位整数格式保存到用户数据区中。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得保存结果的有效地址addr,然后把临时实数格式的结果转换成临时整数格式。如果是负

    // 数则设置整数符号位。最后把整数保存到用户内存中。

    // 参数:tmp – 临时实数格式结果值;info – info结构指针;code – 指令代码。

196 void put_longlong_int(const temp_real * tmp,

197         struct info * info, unsigned short code)

198 {

199         char * addr;

200         temp_int ti;

201

202         addr = ea(info,code);                        // 取有效地址。

203         real_to_int(tmp,&ti);                        // 转换成临时整数格式。

204         verify_area(addr,8);                         // 验证存储区域。

205         if (ti.sign)                                 // 若是负数,则取反加1

206                 __asm__("notl %0 ; notl %1\n\t"

207                         "addl $1,%0 ; adcl $0,%1"

208                         :"=r" (ti.a),"=r" (ti.b)

209                         :"" (ti.a),"1" (ti.b));

210         put_fs_long(ti.a,(unsigned long *) addr);       // 存储到用户数据区中。

211         put_fs_long(ti.b,1 + (unsigned long *) addr);

212 }

213

    // 无符号数<high, low>除以10,余数放在rem中。

214 #define DIV10(low,high,rem) \

215 __asm__("divl %6 ; xchgl %1,%2 ; divl %6" \

216         :"=d" (rem),"=a" (low),"=b" (high) \

217         :"" (0),"1" (high),"2" (low),"c" (10))

218

    // 把运算结果以BCD码格式保存到用户数据区中。

    // 该函数首先根据浮点指令代码中寻址模式字节中的内容和info 结构中当前寄存器中的内容,

    // 取得保存结果的有效地址addr,并验证保存10字节BCD码的用户空间。然后把临时实数格式

    // 的结果转换成BCD码格式的数据并保存到用户内存中。如果是负数则设置最高存储字节的最高

    // 有效位。

    // 参数:tmp – 临时实数格式结果值;info – info结构指针;code – 指令代码。

219 void put_BCD(const temp_real * tmp,struct info * info, unsigned short code)

220 {

221         int k,rem;

222         char * addr;

223         temp_int i;

224         unsigned char c;

225

226         addr = ea(info,code);                        // 取有效地址。

227         verify_area(addr,10);                        // 验证存储空间容量。

228         real_to_int(tmp,&i);                         // 转换成临时整数格式。

229         if (i.sign)                           // 若是负数,则设置符号字节最高有效位。

230                 put_fs_byte(0x80, addr+9);

231         else                                         // 否则符号字节设置为0

232                 put_fs_byte(0, addr+9);

233         for (k = 0; k < 9; k++) {                    // 临时整数转换成BCD码并保存。

234                 DIV10(i.a,i.b,rem);

235                 c = rem;

236                 DIV10(i.a,i.b,rem);

237                 c += rem<<4;

238                 put_fs_byte(c,addr++);

239         }

240 }

241