Keil c函数参数传递的讨论

引起我注意Keil c函数参数传递的是在一个偶然的机会,我在写一个函数的时候:如下

write_byte(ulong addr, uchar dat)

{
      …
}

我原本以为addr会通过R4—R7来传递,而dat则通过R3传递的,在调试的时候却发现dat是通过固定地址储存区传递的。因此引发了我想知道它到底是怎样传递参数的。

因为大家都知道,keil c的参数传递规则

参数    char,            int,             long,     generic 

数目  1-byte pointer     2-byte pointer      float      pointer 

1       R7             R6 & R7         R4-R7     R1-R3 

2       R5             R4 & R5 

3       R3             R2 & R3

我原本以为三个int参数都通过寄存器能传递,一个ulong和一个uchar应该也可以吧,真是有点不明白。如果写为如下

write_byte(ulong addr, uchar * dat)

{
      …
}

即一个ulong参数和一个指针参数则都能通过寄存器传递。

导致上面第二个uchar参数不通过寄存器传递的原因大概是因为第二个uchar型参数一定要通过R5来传递吧,而第一个ulong型参数正好用了R5,所以dat就只能通过固定地址的存储区来传递了。

为此我就想测试一下如果不通过寄存器来传递参数或通过模拟栈来传递参数时参数的传递情况会是怎样的。

第一个例子如下,通过寄存器传递参数,代码共101字节。

void delay(ulong dlyh, uint dlyl)

{
        while(dlyh--);

       while(dlyl--);

}

void main(void)

{
        delay(0x100, 0x50);

       while(1);
}

汇编代码如下, 

       RSEG  ?PR?_delay?TEST_PARA

_delay:

       USING  0

                     ; SOURCE LINE # 6

       MOV         dlyh?040+03H,R7            ;把从寄存器R4—R7传来的第一个参数

       MOV         dlyh?040+02H,R6         ;存入固定地址。

       MOV         dlyh?040+01H,R5         ;(有时真觉得这动作啥冒)

       MOV         dlyh?040,R4

; {

                     ; SOURCE LINE # 7

?C0001:

;         while(dlyh--);

                     ; SOURCE LINE # 8

       MOV         R0,#LOW (dlyh?040)

       MOV         A,#0FFH

       LCALL       ?C?LLDIIDATA8

       MOV         A,R4

       ORL         A,R5

       ORL         A,R6

       ORL         A,R7

       JNZ         ?C0001

?C0003:

;        while(dlyl--);

                     ; SOURCE LINE # 9

       MOV         A,dlyl?041+01H

       DEC         dlyl?041+01H

       MOV         R6,dlyl?041

       JNZ         ?C0009

       DEC         dlyl?041

?C0009:

       ORL         A,R6

       JNZ         ?C0003

; }

                     ; SOURCE LINE # 10

?C0005:

       RET     

; END OF _delay



; void main(void)

       RSEG  ?PR?main?TEST_PARA

main:

       USING  0

                     ; SOURCE LINE # 12

; {

                     ; SOURCE LINE # 13

;         delay(0x100, 0x50);

                     ; SOURCE LINE # 14

       MOV         ?_delay?BYTE+04H,#00H                  ;传递第二个参数

       MOV         ?_delay?BYTE+05H,#050H

       MOV         R7,#00H                                                 ;传递第一个参数

       MOV         R6,#01H

       MOV         R5,#00H

       MOV         R4,#00H

       LCALL       _delay                                                     ;函数调用

?C0006:

;     while(1);

                     ; SOURCE LINE # 15

       SJMP        ?C0006

; END OF main

我们看到,第一个参数在调用函数里先送到R4—R7,然后在被调用函数delay里又从R4—R7传递到固定存储地址。

第二个例子仍然是第一个例子的函数,但是不通过寄存器传递参数,共94字节。

汇编代码如下:

      RSEG  ?PR?delay?TEST_PARA

delay:

      USING    0

                    ; SOURCE LINE # 6

; {

                    ; SOURCE LINE # 7

?C0001:

;         while(dlyh--);

                    ; SOURCE LINE # 8

      MOV         R0,#LOW (dlyh?040)

      MOV         A,#0FFH

      LCALL       ?C?LLDIIDATA8                ;取参数

      MOV         A,R4

      ORL         A,R5

      ORL         A,R6

      ORL         A,R7

      JNZ         ?C0001

?C0003:

;        while(dlyl--);

                    ; SOURCE LINE # 9

      MOV         A,dlyl?041+01H

      DEC         dlyl?041+01H

      MOV         R6,dlyl?041

      JNZ         ?C0009

      DEC         dlyl?041

?C0009:

      ORL         A,R6

      JNZ         ?C0003

; }

                    ; SOURCE LINE # 10

?C0005:

      RET         

; END OF delay



; void main(void)

      RSEG  ?PR?main?TEST_PARA

main:

      USING    0

                    ; SOURCE LINE # 12

; {

                    ; SOURCE LINE # 13

;         delay(0x100, 0x50);

                    ; SOURCE LINE # 14

      CLR         A

      MOV         ?delay?BYTE+03H,A                  ;传递第一个参数

      MOV         ?delay?BYTE+02H,#01H

      MOV         ?delay?BYTE+01H,A

      MOV         ?delay?BYTE,A

      MOV         ?delay?BYTE+04H,A                  ;传递第二个参数

      MOV         ?delay?BYTE+05H,#050H

      LCALL     delay                                                ;调用函数。

?C0006:

;        while(1);

                    ; SOURCE LINE # 15

      SJMP        ?C0006

; END OF main

我们可以看到,在这个例子中,调用函数直接把参数传递到固定的存储地址,少了通过寄存器这一步,产生的代码更小。

第三个例子,还是原来的函数,但是参数通过模拟栈传递,代码共128字节。

void delay(ulong dlyh, uint dlyl) reentrant

{

      while(dlyh--);

      while(dlyl--);

}

void main(void)

{

      delay(0x100, 0x50);

      while(1);

}

产生的汇编代码如下

      RSEG  ?PR?_?delay?TEST_PARA

_?delay:

      USING    0

                    ; SOURCE LINE # 6

      MOV         A,?C_IBP

      ADD         A,#0FCH                          ;模拟栈指针减四

      MOV         ?C_IBP,A

      MOV         R0,A

      LCALL       ?C?LSTIDATA                     ;保存第一个参数

?C0001:

; {

;         while(dlyh--);

                    ; SOURCE LINE # 8

      MOV         R0,?C_IBP

      MOV         A,#0FFH

      LCALL       ?C?LLDIIDATA8         ;取参数

      MOV         A,R4

      ORL         A,R5

      ORL         A,R6

      ORL         A,R7

      JNZ         ?C0001

?C0003:

;        while(dlyl--);

                    ; SOURCE LINE # 9

      MOV         A,?C_IBP

      ADD         A,#04H

      MOV         R0,A

      INC         R0

      MOV         A,@R0

      DEC         @R0

      DEC         R0

      MOV         AR6,@R0

      JNZ         ?C0009

      DEC         @R0

?C0009:

      ORL         A,R6

      JNZ         ?C0003

; }

                    ; SOURCE LINE # 10

?C0005:

      MOV         A,?C_IBP

      ADD         A,#06H

      MOV         ?C_IBP,A

      RET         

; END OF _?delay



; void main(void)

      RSEG  ?PR?main?TEST_PARA

main:

      USING    0

                    ; SOURCE LINE # 12

; {

                    ; SOURCE LINE # 13

;         delay(0x100, 0x50);

                    ; SOURCE LINE # 14

      DEC         ?C_IBP

      DEC         ?C_IBP

      MOV         R0,?C_IBP                                 ;传递第二个参数

      MOV         @R0,#00H

      INC         R0

      MOV         @R0,#050H

      CLR         A

      MOV         R7,A                                         ;第一个参数,通过寄存器传递。

      MOV         R6,#01H

      MOV         R5,A

      MOV         R4,A

      LCALL     _?delay

?C0006:

;        while(1);

                    ; SOURCE LINE # 15

      SJMP        ?C0006

; END OF main

第三个例子可以看出,通过模拟栈进行参数传递的函数,调用函数仍然尽可能的通过寄存器组传递参数,然后被调用函数再把寄存器传递来的参数保存入模拟栈。

这里只是举了几个例子来说明keil c的参数传递情况,也许举例的函数不是很一般,不能太说明问题,各位可自己试试。

现在我们来总结一下。(这里不考虑太简单的函数,特例是函数存参数的存储器在寄存器组里面,这方面的问题可去看我的另一辩文章“keil c的一些有趣特性”)。

一、如果函数无参数,这种情况就不用考虑参数的传递,多好。

二、通过寄存器组传递参数,调用函数把参数传入寄存器组里,在被调用函数里还要把参数从寄存器组里存入固定存储地址。

三、通过固定存储区传递参数,调用函数直接把参数传入固定的存储地址里,被调用函数从这里取参数就是了。

四、通过模拟栈传递参数,有点类似于通过寄存器组传递参数,不过代码的大小及执行的时间可就……。

由此看到,并不是说通过寄存器传递参数是最好的方法,有些情况下通过固定存储区传递参数或许会有更好的表现。(如代码大小,执行时间在某些情况也会更快(这里不再举例说明了,大家可自己想想))。

永不止步步 发表于03-10 09:56 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

永不止步步
金币:67410个|学分:308217个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号