keil 函数调用时的参数问题

lzscws   2008-7-8 10:52 楼主
用keil开发51的程序
写了个函数:
INT16U halSetTimer1Period(INT32U p)
{
    。。。。。
}
在调用的时候参数p不能正确的传递,可是我看R4 R5 R6 R7寄存器里面的值明明是传递的那个值。
而当把参数p的类型改成两个字节的INT16U 就能正确传递了,这是什么原因呢?

谢谢指教

回复评论 (8)

不能正确传递是指什么?   比如你 halSetTimer1Period(32) 传进函数里的是什么? 难道不是32?
点赞  2008-7-8 11:56
函数体里面引用p的部分都贴出来。
既然用了INT32U,那就传个32位的数看看。然后贴编译后的汇编代码
点赞  2008-7-8 12:19
写了个简单的试验程序
void test(unsigned long p)
{
        p++;
}

void main(void)
{
        test(0x1FFFFFFF);
        while(1);
}
采用在线调试工具,memory model = large
结果进入test()中时,p的值不是1fffffff,而是一个很奇怪的的大数。

当改成模拟器调试时,该问题不存在
当改成memory model = small,时,该问题也不存在

以下是汇编代码
-----------------------------------------------------------------
; .\main.SRC generated from: main.c
; COMPILER INVOKED BY:
;        C:\Keil\C51\BIN\C51.EXE main.c LARGE BROWSE DEBUG OBJECTEXTEND SRC(.\main.SRC)


NAME        MAIN

?PR?_test?MAIN       SEGMENT CODE
?XD?_test?MAIN       SEGMENT XDATA OVERLAYABLE
?PR?main?MAIN        SEGMENT CODE
        EXTRN        CODE (?C?LSTXDATA)
        EXTRN        CODE (?C_STARTUP)
        PUBLIC        main
        PUBLIC        _test

        RSEG  ?XD?_test?MAIN
?_test?BYTE:
          p?040:   DS   4
;
;
;
; void test(unsigned long p)

        RSEG  ?PR?_test?MAIN
_test:
        USING        0
                        ; SOURCE LINE # 4
        MOV          DPTR,#p?040
        LCALL        ?C?LSTXDATA
; {
                        ; SOURCE LINE # 5
;         p++;
                        ; SOURCE LINE # 6
        MOV          DPTR,#p?040
        MOVX         A,@DPTR
        MOV          R4,A
        INC          DPTR
        MOVX         A,@DPTR
        MOV          R5,A
        INC          DPTR
        MOVX         A,@DPTR
        MOV          R6,A
        INC          DPTR
        MOVX         A,@DPTR
        ADD          A,#01H
        MOV          R7,A
        CLR          A
        ADDC         A,R6
        MOV          R6,A
        CLR          A
        ADDC         A,R5
        MOV          R5,A
        CLR          A
        ADDC         A,R4
        MOV          R4,A
        MOV          DPTR,#p?040
        LJMP         ?C?LSTXDATA
; END OF _test

;        
; }
;
; void main(void)

        RSEG  ?PR?main?MAIN
main:
        USING        0
                        ; SOURCE LINE # 10
; {
                        ; SOURCE LINE # 11
;         test(0x1FFFFFFF);
                        ; SOURCE LINE # 12
        MOV          R7,#0FFH
        MOV          R6,#0FFH
        MOV          R5,#0FFH
        MOV          R4,#01FH
        LCALL        _test
?C0002:
;         while(1);
                        ; SOURCE LINE # 13
        SJMP         ?C0002
; END OF main

        END
谢谢指教  :)
点赞  2008-7-8 13:45
Disassembly里面的代码是这样的
发现p传递的居然是02003590(0x0000 - 0x0003里面的内容)为什么呢?  
===================================================================
C:0x0000    020035   LJMP     C:0035
     4: void test(unsigned long p)
C:0x0003    900000   MOV      DPTR,#0x0000
C:0x0006    120041   LCALL    C?LSTXDATA(C:0041)
     5: {
     6:         p++;
     7:         
     8: }
     9:  
C:0x0009    900000   MOV      DPTR,#0x0000
C:0x000C    E0       MOVX     A,@DPTR
C:0x000D    FC       MOV      R4,A
C:0x000E    A3       INC      DPTR
C:0x000F    E0       MOVX     A,@DPTR
C:0x0010    FD       MOV      R5,A
C:0x0011    A3       INC      DPTR
C:0x0012    E0       MOVX     A,@DPTR
C:0x0013    FE       MOV      R6,A
C:0x0014    A3       INC      DPTR
C:0x0015    E0       MOVX     A,@DPTR
C:0x0016    2401     ADD      A,#0x01
C:0x0018    FF       MOV      R7,A
C:0x0019    E4       CLR      A
C:0x001A    3E       ADDC     A,R6
C:0x001B    FE       MOV      R6,A
C:0x001C    E4       CLR      A
C:0x001D    3D       ADDC     A,R5
C:0x001E    FD       MOV      R5,A
C:0x001F    E4       CLR      A
C:0x0020    3C       ADDC     A,R4
C:0x0021    FC       MOV      R4,A
C:0x0022    900000   MOV      DPTR,#0x0000
C:0x0025    020041   LJMP     C?LSTXDATA(C:0041)
    10: void main(void)
    11: {
    12:         test(0x1FFFFFFF);
C:0x0028    7FFF     MOV      R7,#0xFF
C:0x002A    7EFF     MOV      R6,#0xFF
C:0x002C    7DFF     MOV      R5,#0xFF
C:0x002E    7C1F     MOV      R4,#0x1F
C:0x0030    120003   LCALL    test(C:0003)
    13:         while(1);
C:0x0033    80FE     SJMP     C:0033
C:0x0035    787F     MOV      R0,#0x7F
C:0x0037    E4       CLR      A
C:0x0038    F6       MOV      @R0,A
C:0x0039    D8FD     DJNZ     R0,C:0038
C:0x003B    758107   MOV      SP(0x81),#0x07
C:0x003E    020028   LJMP     main(C:0028)
                 C?LSTXDATA:
C:0x0041    EC       MOV      A,R4
C:0x0042    F0       MOVX     @DPTR,A
C:0x0043    A3       INC      DPTR
C:0x0044    ED       MOV      A,R5
C:0x0045    F0       MOVX     @DPTR,A
C:0x0046    A3       INC      DPTR
C:0x0047    EE       MOV      A,R6
C:0x0048    F0       MOVX     @DPTR,A
C:0x0049    A3       INC      DPTR
C:0x004A    EF       MOV      A,R7
C:0x004B    F0       MOVX     @DPTR,A
C:0x004C    22       RET      
点赞  2008-7-8 13:57
Large模式下,默认的内存使用XData段、Small模式下默认的内存使用Data段。

从汇编语言上看你在Large模式下编译器优化产生了一个函数C?LSTXDATA: 用来处理XData参数传递的问题。使用XData的0x0000地址来保存实参。

进入Test函数首先通过C?LSTXDATA:函数将寄存器R4 R5 R6 R7的值拷贝到XData段0x0000开始的4个字节,然后开始计算p++。这个计算过程你看懂了要吐血——把刚才拷贝出去的内容再一个一个拷贝到R4 R5 R6 R7寄存器中,只是最后拷贝到R7之前先做一个加一,后面跟着做进位处理。所有的处理结束之后,再调用C?LSTXDATA:函数将寄存器R4 R5 R6 R7的值拷贝到XData段0x0000开始的4个字节。

整个流程是正确的。

你观察变量的值时要注意内存所处的段。C51是哈佛结构的CPU,这点和PC上的调试不太一样。
点赞  2008-7-8 14:52
溢出了。还有堆栈设置怎么样的?
点赞  2008-7-8 16:15
就在项目的option里面,有Compact Small Large,默认Compact,按你所说,small就可以了。
点赞  2008-7-8 16:42
可是如果用small
原来的大程序就出错了
UCOS_II.C(27): error C249: 'DATA': SEGMENT TOO LARGE
......
点赞  2008-7-8 16:45
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复