·汉化新世纪 ·汉化新世纪论坛 ·百家争鸣 ·论坛集萃 ·汉化问答 ·软件介绍
文章首页 >> 汉化教学 >> PPC汉化 >> PPC字体修改基础二 ARM汇编语言之继续探索函数处理    Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎

PPC字体修改基础二 ARM汇编语言之继续探索函数处理

作者: 雅枫 来源:雅枫的博客 时间:2005-09-07 点击:10089

PPC字体修改基础二  
——ARM汇编语言之继续探索函数处理

这次我们一起探索ARM程序的更多细节。上边我们分析到了一个函数,看到是用寄存器来传递参数。但寄存器总是有限不可能总用寄存器,以前fast调用程序好像前3个参数使用寄存器,在PPC上,这应该是类似的。为此,我改了上次那个函数,因为MessageBoxW函数4个参数仍然用寄存器传递,所以直接从5个参数的程序开始分析。修改后代码如下:


int WINAPI MyFunc(int,int,int,int,int);
int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     int a;
    int b=4;
    // TODO: Place code here.
    MessageBox(0,_T("helloWorld"),_T("hello"),MB_OK);
    int i=MyFunc(1,2,3,4,5);
    a=b+i;
    return 0;
}

int WINAPI MyFunc(int a,int b,int c,int d2,int e)

{
    int d =a+b+c+d2+e;
    return d;
}
先看看调用部分代码:
var_4           = -4
               MOV     R3, #5
               STR     R3, [SP,#4+var_4]        ;开始操作栈了
               MOV     R2, #3                ;前四个参数还是用寄存器传递
               MOV     R3, #4
               MOV     R1, #2
               MOV     R0, #1
               BL      sub_1104C            ;Call我们的子程序
MOV     R0, #0
我们看到,第5个参数已经开始使用堆栈做传递了
STR     R3, [SP,#4+var_4]
这句的作用是将R3的值放入SP+#4+var_4所指的地址中。var_4是个常数是-4,所以这句等价于STR R3,[SP]
然后让我们看子程序部分的反汇编代码
sub_1104C                               ; CODE XREF: WinMain+34 p

 arg_0           =  0
                 ADD     R0, R0, R1
                 ADD     R1, R0, R2
                 LDR     R0, [SP,#arg_0]
                 ADD     R2, R1, R3
                 ADD     R0, R2, R0
                 RET
 ; End of function sub_1104C
结果多少让我有些惊讶,因为子程序跟主程序公用了一个堆栈,并且切入子程序时也没有保存栈顶指针,这样如果子程序崩溃,则整个程序的栈操作必然乱套。难道不会出现不稳定的情况?或许是没有操作堆栈的缘故?并且几乎所有的运算的结果都使用的寄存器。代码相当高效(甚至我想不出如何写比这更高效的程序)。这(RISC代码容易优化)就是RISC的优势所在吧,总之代码比x86下边VC++编译器优化的要好。
为了难为一下编译器,我改的代码稍微麻烦了一点:
int WINAPI MyFunc(int,int,int,int,int,int);
int WINAPI WinMain(    HINSTANCE hInstance,
                    HINSTANCE hPrevInstance,
                    LPTSTR    lpCmdLine,
                    int       nCmdShow)
{
     int a;
    int b=4;
    // TODO: Place code here.
    MessageBox(0,_T("helloWorld"),_T("hello"),MB_OK);
    int i=MyFunc(1,2,3,4,5,6);
    a=b+i;
    return 0;
}

int WINAPI MyFunc(int a,int b,int c,int d2,int e,int f)

{
    int g=3;
    int h=4;
    int i=6;
    int j=9;
    int k=i*j;
    int d =a+b+c+d2+e+f+g+h;
    return (d+k);
}
现在参数我改成了6个,并且我加了几个中间变量,以便把栈操作逼出来,调用处的代码在意料之中,最后2个参数使用了栈传递,而前四个参数使用了寄存器传递。看来可确定,PPC的API调用约定是:前四个参数分别用R0,R1,R2,R3,多余的参数从右向左依次入栈。
.text:0001101C                 MOV     R3, #6
.text:00011020                 MOV     R0, #5
.text:00011024                 STR     R3, [SP,#8+var_4]
.text:00011028                 STR     R0, [SP,#8+var_8]
.text:0001102C                 MOV     R3, #4
.text:00011030                 MOV     R2, #3
.text:00011034                 MOV     R1, #2
.text:00011038                 MOV     R0, #1
.text:0001103C                 BL      sub_11054
.text:00011040                 MOV     R0, #0
而接下来子程序的反汇编代码,多少让我有些吃惊:
.text:00011054 sub_11054                             
.text:00011054
.text:00011054 arg_0           =  0
.text:00011054 arg_4           =  4
.text:00011054
.text:00011054                 ADD     R0, R0, R1
.text:00011058                 ADD     R1, R0, R2
.text:0001105C                 LDR     R0, [SP,#arg_0]
.text:00011060                 ADD     R2, R1, R3
.text:00011064                 LDR     R1, [SP,#arg_4]
.text:00011068                 ADD     R3, R2, R0
.text:0001106C                 ADD     R0, R3, R1
.text:00011070                 ADD     R0, R0, #0x3D
.text:00011074                 RET
.text:00011074 ; End of function sub_11054
太智能了!不但没把栈操作逼出来,而且在编译时自动作了尽可能多的运算大大减少了实际运行时的效率!但这也让我想到了让编译器操作栈的方法,我把子程序代码改为如下:
int WINAPI MyFunc(int a,int b,int c,int d2,int e,int f)

{
    int g=3;
    int h=4;
    int i=6;
    int j=9*c;
    int k=i*f;
    int d =a+b+c+d2+e+f+g+h;
    return (d+k);
}
这次编译器撑不住了,终于开始使用栈:
sub_11054                               ; CODE XREF: WinMain+3C p
arg_0           =  0
arg_4           =  4
             STMFD   SP!, {R4-R6,LR}    ;首先r4,r5,r6,lr分别入栈(保存现场)
             MOV     R4, R0            ;用r4,r5,r6暂存r0-r2的值
             MOV     R5, R1
             MOV     R6, R2
             LDR     R0, [SP,#0x10+arg_4]
ADD     R1, R0, #1            
             RSB     R2, R1, R1,LSL#3    ;r2=(r1<<3)-r1
             ADD     R0, R2, R4
             ADD     R1, R0, R5
             LDR     R0, [SP,#0x10+arg_0]
             ADD     R2, R1, R6
             ADD     R3, R2, R3
             ADD     R0, R3, R0
             LDMFD   SP!, {R4-R6,PC}    ;恢复r4-r6的值并离开函数。
; End of function sub_11054
算法我就没必要写分析了,我说一下这次的堆栈操作:
这次仍然没有用栈做中间变量,而是多用了3个通用寄存器。而只有在进入与离开时操作下内存。这样能大大的减少内存的访问率,加快运行效率,微软的代码优化做的确实非常的好。
基本上函数的入口与出口就是这种形式了,下篇研究下字串的调用。


原文引自雅枫的博客,如有版权疑问请来信说明,我们会尽快处理

汉化新世纪 责任编辑: 乾 .:|:. 标签(Tag): PPC 字体 汇编

·上一篇: PPC字体修改基础一 初探ARM汇编语言 ·下一篇: NSIS Edit 修改版 VNISEdit 2.0.3(build 050916) 发布

· 版权申明: 本文引自《雅枫的博客》,如有版权疑问请及时联系本站,以便本站处理。

· 转载申明: 本文引自《雅枫的博客》[ 作者: 雅枫],如需转载请直接联系原始作者,并请注明原始出处。

相关文章                                                                                发表评论 打印此文 关闭窗口

| 设为首页 | 加入收藏 | 联系我们 | 友情链接
Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎
本站内容,除转载或版权特别申明的内容外,皆遵守 创造共用协议中文版之“署名-非商业性使用-禁止演绎 2.5 中国大陆”条款
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 China License.
本网站内容源自汉化新世纪论坛的摘录和汉化新世纪成员的原创文章。
凡汉化新世纪论坛的文字皆默认为汉化新世纪与原作者共同拥有并授权发布。
如对本站发布文章有所异议请来信告知,我们将及时删除。
凡商业摘录本站文字请先与我们联系,本站将保留非授权商业发布的追究权利。
凡非商业摘录本站文字请明显注明出处和原作者,并不得改动,凡改动必先征求原作者同意。
苏ICP备05002283号