用keil开发51的程序
写了个函数:
INT16U halSetTimer1Period(INT32U p)
{
。。。。。
}
在调用的时候参数p不能正确的传递,可是我看R4 R5 R6 R7寄存器里面的值明明是传递的那个值。
而当把参数p的类型改成两个字节的INT16U 就能正确传递了,这是什么原因呢?
谢谢指教
不能正确传递是指什么? 比如你 halSetTimer1Period(32) 传进函数里的是什么? 难道不是32?
函数体里面引用p的部分都贴出来。
既然用了INT32U,那就传个32位的数看看。然后贴编译后的汇编代码
写了个简单的试验程序
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
谢谢指教 :)
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
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上的调试不太一样。
就在项目的option里面,有Compact Small Large,默认Compact,按你所说,small就可以了。
可是如果用small
原来的大程序就出错了
UCOS_II.C(27): error C249: 'DATA': SEGMENT TOO LARGE
......