杭州平望科技
   
首页 关于平望 产品中心 服务中心 FAQ 方案与案例 联系我们 论坛BBS

 嵌入式系统提供商

    杭州平望科技与国内多家著名工控厂商建立了战略合作伙伴关系, 同时是台湾元太(PVI)液晶产品的中国区授权代理。 可为用户提供基于ARM架构主板、X86架构主板、嵌入式2.5 3.5 5.25寸主板、嵌入式PC104主板、单板电脑、 嵌入式PC104扩展卡、GPRS模块,数据采集模块,串口/以太网转换模块,2.5寸-17寸宽温、高亮真彩TFT工业 模拟液晶屏、数字液晶屏,触摸屏、触摸显示器,并可根据客户的需求提供完善的液晶驱动解决方案。

ARM嵌入式硬件方案

基于ARM、CPLD、FPGA、DSP等系列嵌入式微处理器,可运行Linux、Wince等操作系统嵌入式硬件平台

工控板液晶搭配方案

X86架构主板及工控机,2.5寸-17寸宽温、高亮真彩TFT液晶屏,提供液晶驱动解决方案
 
首 页 >  问题内容 
ARM过程调用标准 (三)

为了更细致的理解,下面是代码是 Norcroft C v4.00 为上述代码生成的...
        AREA |C$code|, CODE, READONLY        IMPORT  |__main||x$codeseg|        B       |__main|        DCB     &6d,&61,&69,&6e        DCB     &00,&00,&00,&00        DCD     &ff000008        IMPORT  |x$stack_overflow|        EXPORT  one        EXPORT  mainmain        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4        CMPS    sp, sl        BLLT    |x$stack_overflow|        BL      one        MOV     a1, #0        LDMEA   fp, {fp,sp,pc}^        DCB     &6f,&6e,&65,&00        DCD     &ff000004        EXPORT  zero        EXPORT  twoone        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4        CMPS    sp, sl        BLLT    |x$stack_overflow|        BL      zero        LDMEA   fp, {fp,sp,lr}        B       two        IMPORT  |_printf|two        ADD     a1, pc, #L000060-.-8        B       |_printf|L000060        DCB     &6d,&61,&69,&6e        DCB     &2e,&2e,&2e,&6f        DCB     &6e,&65,&2e,&2e        DCB     &2e,&74,&77,&6f        DCB     &0a,&00,&00,&00zero        MOVS    pc, lr        AREA |C$data||x$dataseg|        END这个例子不遵从 32 为体系。APCS-32 规定只是简单的说明了标志不需要被保存。所以删除 LDM 的‘^’后缀,并在函数 zero 中删除 MOVS 的‘S’后缀。则代码就与遵从 32-bit 的编译器生成的一样了。
保存代码指针包含这条设置回溯结构的指令(STMFD ...)的地址再加上 12 字节。记住,对于 26-bit 代码,你需要去除其中的 PSR 来得到实际的代码地址。

现在我们查看刚进入函数的时候:

pc 总是包含下一个要被执行的指令的位置。
lr (总是)包含着退出时要装载到 pc 中的值。在 26-bit 位代码中它还包含着 PSR。
sp 指向当前的栈块(chunk)限制,或它的上面。这是用于复制临时数据、寄存器和类似的东西到其中的地方。在 RISC OS 下,你有可选择的至少 256 字节来扩展它。
fp 要么是零,要么指向回溯结构的最当前的部分。
函数实参布置成(下面)描述的那样。
 

实际参数
APCS 没有定义记录、数组、和类似的格局。这样语言可以自由的定义如何进行这些活动。但是,如果你自己的实现实际上不符合 APCS 的精神,那么将不允许来自你的编译器的代码与来自其他编译器的代码连接在一起。典型的,使用 C 语言的惯例。
前 4 个整数实参(或者更少!)被装载到 a1 - a4。
前 4 个浮点实参(或者更少!)被装载到 f0 - f3。
其他任何实参(如果有的话)存储在内存中,用进入函数时紧接在 sp 的值上面的字来指向。换句话说,其余的参数被压入栈顶。所以要想简单。最好定义接受 4 个或更少的参数的函数。
 

函数退出
通过把返回连接值传送到程序计数器中来退出函数,并且:
如果函数返回一个小于等于一个字大小的值,则把这个值放置到 a1 中。
如果函数返回一个浮点值,则把它放入 f0 中。
sp、fp、sl、v1-v6、和 f4-f7 应当被恢复(如果被改动了)为包含在进入函数时它所持有的值。
我测试了故意的破坏寄存器,而结果是(经常在程序完全不同的部分)出现不希望的和奇异的故障。
ip、lr、a2-a4、f1-f3 和入栈的这些实参可以被破坏。
在 32 位模式下,不需要对 PSR 标志进行跨越函数调用的保护。在 26 位模式下必须这样,并通过传送 lr 到 pc 中(MOVS、或 LDMFD xxx^)来暗中恢复。必须从 lr 重新装载 N、Z、C 和 V,跨越函数保护这些标志不是足够的。
 建立栈回溯结构
对于一个简单函数(固定个数的参数,不可重入),你可以用下列指令建立一个栈回溯结构:
function_name_label        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4这个片段(来自上述编译后的程序)是最基本的形式。如果你要破坏其他不可破坏的寄存器,则你应该在这个 STMFD 指令中包含它们。
下一个任务是检查栈空间。如果不需要很多空间(小于 256 字节)则你可以使用:

        CMPS    sp, sl        BLLT    |x$stack_overflow|这是 C 版本 4.00 处理溢出的方式。在以后的版本中,你要调用 |__rt_stkovf_split_small|。接着做你自己的事情...

通过下面的指令完成退出:

        LDMEA   fp, {fp,sp,pc}^还有,如果你入栈了其他寄存器,则也在这里重新装载它们。选择这个简单的 LDM 退出机制的原因是它比分支到一个特殊的函数退出处理器(handler)更容易和更合理。
用在回溯中的对这个协议的一个扩展是把函数名字嵌入到代码中。紧靠在函数(和 MOV ip, sp)的前面的应该是:

        DCD     &ff0000xx这里的‘xx’是函数名字符串的长度(包括填充和终结符)。这个字符串是字对齐、尾部填充的,并且应当被直接放置在 DCD &ff....的前面。
所以一个完整的栈回溯代码应当是:

        DCB     "my_function_name", 0, 0, 0, 0        DCD     &ff000010my_function_name        MOV     ip, sp        STMFD   sp!, {fp, ip, lr, pc}        SUB     fp, ip, #4        CMPS    sp, sl                    ; 如果你不使用栈        BLLT    |x$stack_overflow|        ; 则可以省略        ...处理...        LDMEA   fp, {fp, sp, pc}^要使它遵从 32-bit 体系,只须简单的省略最后一个指令的‘^’。注意你不能在一个编译的 26-bit 代码中使用这个代码。实际上,你可以去除它,但这不是我愿意打赌的事情。 
如果你不使用栈,并且你不需要保存任何寄存器,并且你不调用任何东西,则没有必要设置 APCS 块(但在调试阶段对跟踪问题仍是有用的)。在这种情况下你可以:

my_simple_function        ...处理...        MOVS    pc, lr(再次,对 32 位 APCS 使用 MOV 而不是 MOVS,但是不要冒险与 26 位代码连接)。
 

APCS 标准
总的来说,有多个版本的 APCS (实际上是 16 个)。我们只关心在 RISC OS 上可能遇到的。
APCS-A
就是 APCS-Arthur;由早期的 Arthur 所定义。它已经被废弃,原因是它有不同的寄存器定义(对于熟练的 RISC OS 程序员它是某种异类)。它用于在 USR 模式下运行的 Arthur 应用程序。不应该使用它。

sl = R13, fp = R10, ip = R11, sp = R12, lr = R14, pc = R15。
PRM (p4-411) 中说“用 r12 作为 sp,而不是在体系上更自然的 r13,是历史性的并先于 Arthur 和 RISC OS 二者。”
栈是分段的并可按需要来扩展。
26-bit 程序计数器。
不在 FP 寄存器中传递浮点实参。
不可重入。标志必须被恢复。
APCS-R
就是 APCS-RISC OS。用于 RISC OS 应用程序在 USR 模式下进行操作;或在 SVC 模式下的模块/处理程序。

sl = R10, fp = R11, ip = R12, sp = R13, lr = R14, pc = R15。
它是唯一的最通用的 APCS 版本。因为所有编译的 C 程序都使用 APCS-R。
显式的栈限制检查。
26-bit 程序计数器。
不在 FP 寄存器中传递浮点实参。
不可重入。标志必须被恢复。
APCS-U
就是 APCS-Unix,Acorn 的 RISCiX 使用它。它用于 RISCiX 应用程序(USR 模式)或内核(SVC 模式)。

sl = R10, fp = R11, ip = R12, sp = R13, lr = R14, pc = R15。
隐式的栈限制检查(使用 sl)。
26-bit 程序计数器。
不在 FP 寄存器中传递浮点实参。
不可重入。标志必须被恢复。
APCS-32
它是 APCS-2(-R 和 -U)的一个扩展,允许 32-bit 程序计数器,并且从执行在 USR 模式下的一个函数中退出时,允许标志不被恢复。其他事情同于 APCS-R。
Acorn C 版本 5 支持生成 32-bit 代码;在用于广域调试的 32 位工具中,它是最完整的开发发行。一个简单的测试是要求你的编译器导出汇编源码(而不是制作目标代码)。你不应该找到:
MOVS PC, R14
或者
LDMFD R13!, {Rx-x, PC}^

对编码有用的东西
首先要考虑的是该死的 26/32 位问题。 简单的说,不转弯抹角绝对没有方法为两个版本的 APCS 汇编同一个通用代码。但是幸运的这不是问题。APCS 标准不会突然改变。RISC OS 的 32 位版本也不会立刻变异。所以利用这些,我们可以设计一种支持两种版本的方案。这将远远超出 APCS,对于 RISC OS 的 32 位版本你需要使用 MSR 来处理状态和模式位,而不是使用 TEQP。许多现存的 API 实际上不需要保护标志位。所以在我们的 32 版本中可以通过把 MOVS PC,... 变成 MOV PC,...,和把 LDM {...}^ 变成 LDM {...},并重新建造来解决。objasm 汇编器(v3.00 和以后)有一个 {CONFIG} 变量可以是 26 或 32。可以使用它建造宏...
my_function_name        MOV     ip, sp        STMFD   sp!, {fp, ip, lr, pc}        SUB     fp, ip, #4        ...处理...        [ {CONFIG} = 26          LDMEA   fp, {fp, sp, pc}^        |          LDMEA   fp, {fp, sp, pc}        ]我未测试这个代码。它(或类似的东西)好象是保持与两个版本的 APCS 相兼容的最佳方式,也是对 RISC OS 的不同版本,26 位版本和将来的 32 位版本的最佳方法。
测试是否处于 32 位? 如果你要求你的代码有适应性,有一个最简单的方法来确定处理器的 PC 状态:

   TEQ     PC, PC     ; 对于 32 位是 EQ;对于 26 位是 NE使用它你可以确定:

26 位 PC,可能是 APCS-R 或 APCS-32。
32 位 PC,不能 APCS-R。所有 26-bit 代码(TEQP 等)面临着失败!

法律公告 | 友情链接 | 人才招聘 | 联系我们 | 企业邮局 |
ARM主板 | 凌动主板 | 工控主板 | 工业液晶屏 | 固态硬盘 |
© 2007-2008 www.pvontek.com All rights reserved. 版权所有 杭州平望科技有限公司 浙ICP备07032065号
地址:杭州市滨江区南环路3730号源越大厦4层 电话:0571-88868188 邮箱:sales@pvontek.com