CRT源码解析(一)
发表于 2006-05-05 05:41 PM 作者: tomato
环境:Visual C++ 8, Windows XP,
一、long long int的计算
0、代码:
c++ 代码:
1、表示
此环境下为big endian
所以lhs内存中表示为:f0 de bc 9a 78 56 34 12
2、赋值
lhs = 0x123456789abcdef0
asm 代码:
3、加法
result = lhs + rhs
asm 代码:
4、减法
result = lhs - rhs
和+一样,不过第一个add换成sub,adc换成sbb
5、乘法
result = lhs * rhs
先压栈rhs(先压高位,再压低位),然后压栈lhs(自右至左压栈,但是name decoration仅仅加下划线前缀而已),调用__allmul,返回值放在edx:eax
__allmul在crt\src\intel\llmul.asm
asm 代码:
先判断是不是AHI和BHI都为0,如果为0直接计算ALO * BLO
否则按上面的式子计算
tips:如果ebx开始没有用到,可以不push&pop,因为AHI = BHI = 0的分支不执行,就可以免去ebx的push&pop。在需要用到ebx后,因为push了新东西,所以需要修改参数/变量的equ,例如:
asm 代码:
6、除法
result = lhs / rhs
同上,调用__alldiv,在crt\src\intel\lldiv.asm
edi保存结果的符号
判断dividend(被除数),若小于0,inc edi并且将dividend求neg
判断divisor(除数),若小于0,inc edi并且将divisor求neg
判断divisor的高位
1) 若为0,即divisor <= 0ffffffffH,
0 : dividend.HI / divisor.LO => edx = Remainder(余数), ebx = Quotient(得数)
edx : divident.LO / divisor.LO => eax = Quotient
mov edx, ebx
2) 若不为0,即dividor > 0ffffffffH
dividend和divisor同时右移,直到divisor < 0ffffffffH
dividend’.HI : dividend’.LO / divisor’.LO => esi = Quotient
divisor.HI * esi => ... : ecx
divisor.LO * esi => edx : eax
add edx, ecx
判断进位:
(简而言之,有可能eax多算了1,比如2000H / 109H,假如cpu只能算8位的乘除,那么用移位算来的得数是20H,实际上是19H。满足eax多算了1的条件有:1、算除法后的加法时有进位;2、检查得数×除数>被除数)
a. 如果没有进位
如果edx > dividend.HI,dec esi
如果eax > dividend.LO, dec esi
b. 如果有进位, dec esi
c.
xor edx, edx
mov eax, esi
3)
判断edi是否需要neg一下edx : eax
结束
tips: 把long long int取neg的操作为:(假如在edx : eax中)
asm 代码:
7、求余
result = lhs % rhs
调用__allrem,在crt\src\intel\llrem.asm
判断符号的方法同÷,略去
判断divisor的高位
1) 若为0,即divisor <= 0ffffffffH
0 : dividend.HI / divisor.LO => edx = Remainder
edx : dividend.LO / divisor.LO => eax = Remainder
xor edx, edx
如果edi(符号)大于0,则将结果取负
2) 若不为0,即divisor > 0ffffffffH
dividend和divisor同时右移,直到divisor < 0ffffffffH
dividend’.HI : dividend’.LO / divisor’.LO => ecx = Quotient, edx = Remainder
Quotient * divisor.HI => edx : ecx
Quotient * divisor.LO => edx : eax
add edx, ecx
此时edx:eax为Quotient * divisor的值
a) 如果carry则sub edx:eax, divisor.HI:divisor.LO
b) 如果不carry
若edx > dividend.HI或eax > dividend.LO则sub edx:eax, divisor.HI:divisor.LO
c)
sub edx:eax, dividend
如果edi(符号)等于0,则将结果取负(注意!)
3)
结束
tips:第1)步处理为什么不直接拿dividend.HI : dividend.LO / divisor取remainder?
因为可能产生Integer overflow的中断
8、左移位
result = lhs << 9
lhs => edx : eax
mov cl, 移位数字
调用__allshl,在crt\src\intel\llshl.asm
判断若cl大于等于64,直接返回0
判断若cl大于等于32,把eax放到edx后清空,然后把edx shl cl&31就行了
若cl小于32
asm 代码:
tips:shld edx, eax, cl不会改动eax
9、右移位
result = lhs >> 9
__allshr在crt\src\intel\llshr.asm
和<<完全一样,不过要注意带上符号位,将shld、shl分别改为shrd、sar即可
补充:
·还有个__alldvrm函数在crt\src\intel\lldvrm.asm
和div、rem一样接受dividend和divisor做参数,返回Quotient在edx:eax,Remainder在ebx:ecx
·unsinged long long int的计算
不同的有__aulldiv、__aullrem、__aullshr、__aulldvrm,都比带符号的简单,免去了判断符号的麻烦
·64位整型文字量的表示
10000i64表示64位带符号的10000
10000ui64表示64位不带符号的10000
一、long long int的计算
0、代码:
c++ 代码:
代码:
int main()
{
long long int lhs = 0x123456789abcdef0;
long long int rhs = 0x123456789abcdef0;
long long int result = 0;
result = lhs + rhs;
result = lhs - rhs;
result = lhs * rhs;
result = lhs / rhs;
result = lhs % rhs;
result = lhs << 9;
result = rhs >> 9;
return 0;
} 此环境下为big endian
所以lhs内存中表示为:f0 de bc 9a 78 56 34 12
2、赋值
lhs = 0x123456789abcdef0
asm 代码:
代码:
mov DWORD PTR _lhs$[ebp], 9abcdef0H mov DWORD PTR _lhs$[ebp+4], 12345678H
3、加法
result = lhs + rhs
asm 代码:
代码:
mov eax, DWORD PTR _lhs$[ebp] add eax, DWORD PTR _rhs$[ebp] mov ecx, DWORD PTR _lhs$[ebp+4] adc ecx, DWORD PTR _rhs$[ebp+4] mov DWORD PTR _result$[ebp], eax mov DWORD PTR _result$[ebp+4], ecx
result = lhs - rhs
和+一样,不过第一个add换成sub,adc换成sbb
5、乘法
result = lhs * rhs
先压栈rhs(先压高位,再压低位),然后压栈lhs(自右至左压栈,但是name decoration仅仅加下划线前缀而已),调用__allmul,返回值放在edx:eax
__allmul在crt\src\intel\llmul.asm
asm 代码:
代码:
; AHI, BHI : upper 32 bits of A and B ; ALO, BLO : lower 32 bits of A and B ; ; ALO * BLO ; ALO * BHI ; + BLO * AHI
否则按上面的式子计算
tips:如果ebx开始没有用到,可以不push&pop,因为AHI = BHI = 0的分支不执行,就可以免去ebx的push&pop。在需要用到ebx后,因为push了新东西,所以需要修改参数/变量的equ,例如:
asm 代码:
代码:
push ebx ; must redefine A and B since esp has been altered A2 EQU [esp + 8] ; stack address of a B2 EQU [esp + 16] ; stack address of b
result = lhs / rhs
同上,调用__alldiv,在crt\src\intel\lldiv.asm
edi保存结果的符号
判断dividend(被除数),若小于0,inc edi并且将dividend求neg
判断divisor(除数),若小于0,inc edi并且将divisor求neg
判断divisor的高位
1) 若为0,即divisor <= 0ffffffffH,
0 : dividend.HI / divisor.LO => edx = Remainder(余数), ebx = Quotient(得数)
edx : divident.LO / divisor.LO => eax = Quotient
mov edx, ebx
2) 若不为0,即dividor > 0ffffffffH
dividend和divisor同时右移,直到divisor < 0ffffffffH
dividend’.HI : dividend’.LO / divisor’.LO => esi = Quotient
divisor.HI * esi => ... : ecx
divisor.LO * esi => edx : eax
add edx, ecx
判断进位:
(简而言之,有可能eax多算了1,比如2000H / 109H,假如cpu只能算8位的乘除,那么用移位算来的得数是20H,实际上是19H。满足eax多算了1的条件有:1、算除法后的加法时有进位;2、检查得数×除数>被除数)
a. 如果没有进位
如果edx > dividend.HI,dec esi
如果eax > dividend.LO, dec esi
b. 如果有进位, dec esi
c.
xor edx, edx
mov eax, esi
3)
判断edi是否需要neg一下edx : eax
结束
tips: 把long long int取neg的操作为:(假如在edx : eax中)
asm 代码:
代码:
neg edx neg eax sbb edx, 0
result = lhs % rhs
调用__allrem,在crt\src\intel\llrem.asm
判断符号的方法同÷,略去
判断divisor的高位
1) 若为0,即divisor <= 0ffffffffH
0 : dividend.HI / divisor.LO => edx = Remainder
edx : dividend.LO / divisor.LO => eax = Remainder
xor edx, edx
如果edi(符号)大于0,则将结果取负
2) 若不为0,即divisor > 0ffffffffH
dividend和divisor同时右移,直到divisor < 0ffffffffH
dividend’.HI : dividend’.LO / divisor’.LO => ecx = Quotient, edx = Remainder
Quotient * divisor.HI => edx : ecx
Quotient * divisor.LO => edx : eax
add edx, ecx
此时edx:eax为Quotient * divisor的值
a) 如果carry则sub edx:eax, divisor.HI:divisor.LO
b) 如果不carry
若edx > dividend.HI或eax > dividend.LO则sub edx:eax, divisor.HI:divisor.LO
c)
sub edx:eax, dividend
如果edi(符号)等于0,则将结果取负(注意!)
3)
结束
tips:第1)步处理为什么不直接拿dividend.HI : dividend.LO / divisor取remainder?
因为可能产生Integer overflow的中断
8、左移位
result = lhs << 9
lhs => edx : eax
mov cl, 移位数字
调用__allshl,在crt\src\intel\llshl.asm
判断若cl大于等于64,直接返回0
判断若cl大于等于32,把eax放到edx后清空,然后把edx shl cl&31就行了
若cl小于32
asm 代码:
代码:
shld edx, eax, cl shl eax, cl
9、右移位
result = lhs >> 9
__allshr在crt\src\intel\llshr.asm
和<<完全一样,不过要注意带上符号位,将shld、shl分别改为shrd、sar即可
补充:
·还有个__alldvrm函数在crt\src\intel\lldvrm.asm
和div、rem一样接受dividend和divisor做参数,返回Quotient在edx:eax,Remainder在ebx:ecx
·unsinged long long int的计算
不同的有__aulldiv、__aullrem、__aullshr、__aulldvrm,都比带符号的简单,免去了判断符号的麻烦
·64位整型文字量的表示
10000i64表示64位带符号的10000
10000ui64表示64位不带符号的10000
评论总数 1
评论
| | 好!正要找个64除法作参考 ;) |
| 发表于 2008-01-22 07:41 PM 作者: wq (www.nowhere.iam) |
发表评论 |
作者为 tomato 的最新文章
- C++对象模型(二) (2006-09-23)
- C++对象模型(一) (2006-09-20)
- CRT源码解析(三)字符串函数(2) (2006-05-22)
- CRT源码解析(二)字符串函数(1) (2006-05-07)
- CRT源码解析(一) (2006-05-05)




