由于在暑假匆忙接收的嵌入式项目中需要使用特别大的数组,非分页RAM的内存不够用了,没办法,硬着头皮尝试使用分页RAM,但是完全没有单片机的基础,导致极其的困难。之前写程序都是按照纯软件的思维,主要考虑架构,不会考虑到每个变量具体存在哪个物理地址这么底层的问题,结果被飞思卡尔这分页地址、prm文件什么的搞得一头雾水,而网上的资料又少,讲的又大同小异的笼统,最后写出来的程序因为这分页地址的原因存在各种问题(还以为把变量放到分页RAM了,结果现在稍微懂了点回去看,发现其实很多根本还是分配在非分页区。晕倒~。但是居然还能相对正常运行也是很神奇)。这些天各种找相关的资料,结果发现在CodeWarrior的官方文档资料里其实把我想知道的都讲的很清楚了(还是官方文档给力,以后学什么东西直接找官方文档,不去到处找网上一堆零零散散的资源来学了)。本着学习的态度,将逐步把官方文档翻译一遍,供大家一起交流学习进步。
翻译的资料是公开的,我想应该不会有什么版权问题,如涉及版权问题,请联系我删除文章,原文档在这里(https://www.nxp.com/pages/codewarrior-development-studio-for-hcs12x-microcontrollers-classic-ide-v5.2:CW-HCS12X?&tab=Documentation_Tab&linkline=Users-Guides),另感谢NXP提供的学习资料。
译者注:译者博客(http://blog.csdn.net/lin_strong),转载请保留这条。此为官方文档TN240,仅供学习交流使用,请勿用于商业用途。
这个文档描述了程序员要怎么样帮助HCS12X编译器来产生在数据访问上更加优化的代码。我们将讨论以下主题:
有一些现成的用于转换地址的函数。
这些函数的实现放在datapage.c 中,它们用于比如当你将一个__rptr指针赋值给个__far指针时。
1 比如如下代码片段(变量定义在非分页扩展寻址区)
char data;
volatile char temp;
void func1(void) {
char *__far ptr;
char *__rptr rptr;
ptr = &data;
rptr = ptr;
temp = *rptr;
}
2 会产生如下代码:
20: ptr = &data;
0000 ce0000 [2] LDX #GLOBAL(data)
0003 c600 [1] LDAB #GLOBAL_PAGE(data)
21: rptr = ptr;
0005 160000 [4] JSR _CONV_GLOBAL_TO_LOGICAL
22: temp = *rptr;
0008 5b16 [2] STAB /*RPAGE*/22
000a a600 [3] LDAA 0,X
000c 7a0000 [3] STAA temp
3 运行时函数_CONV_GLOBAL_TO_LOGICAL的实现在datapage.c中,可以在那里找到进一步转换的例程。
一般来说,程序员不需要关心这些转换例程。编译器会在需要的时候自行使用它们来转换地址。如果你真的需要使用它们,我们推荐你仔细阅读datapage.c中的注释以搞清楚怎么传递参数并获得返回值。
即使一个对象是被定义在一个__RPAGE,__EPAGE或__SHORT segment中,这对象也可以使用它的全局地址来访问,通过简单地使用一个__far指针。
下例展示了怎么把一个变量定义在__RPAGE segment中,并使用一个__far指针来访问它。
1 把一个变量定义在__RPAGE segment中并使用__far指针是十分直接的:
#pragma DATA_SEG __RPAGE_SEG PAGED_RAM
char data[10] = {
0x10, 0x20, 0x30, 0x40, 0x50
};
#pragma DATA_SEG DEFAULT
volatile char temp;
void func1(void) {
char *__far ptr;
ptr = data;
temp = *ptr;
}
2 会产生如下代码:
29: ptr = data;
0000 ce0000 [2] LDX #GLOBAL(data)
0003 c600 [1] LDAB #GLOBAL_PAGE(data)
30: temp = *ptr;
0005 5b10 [2] STAB /*GPAGE*/16
0007 18a600 [4] GLDAA 0,X
000a 7a0000 [3] STAA temp
3 使用全局寻址来定义一个变量并使用一个逻辑指针来访问的方法同理。
与其一会使用逻辑地址来访问某个变量,一会又使用全局地址来访问,你可以利用ANSI C的类型转换操作符。这个方法不需要额外的指针变量。
1 比如如下代码片段:
#pragma DATA_SEG __RPAGE_SEG PAGED_RAM
char data[10] = {
0x10, 0x20, 0x30, 0x40, 0x50
};
#pragma DATA_SEG DEFAULT
volatile char temp;
void func1(void) {
temp = *((char *__far)data);
temp = *((char *__rptr)data);
}
2 会产生如下代码:
20: temp = *((char *__far)data);
0002 ce0000 [2] LDX #GLOBAL(data)
0005 c600 [1] LDAB #GLOBAL_PAGE(data)
0007 5b10 [2] STAB /*GPAGE*/16
0009 18a600 [4] GLDAA 0,X
000c 7a0000 [3] STAA temp
21: temp = *((char *__rptr)data);
000f c600 [1] LDAB #PAGE(data)
0011 5b16 [2] STAB /*RPAGE*/22
0013 f60000 [3] LDAB data
0016 7b0000 [3] STAB temp
3 所以上面那个data第一次使用了全局地址来访问,第二次使用了逻辑地址来访问,而都没有用到额外的指针变量(请注意:这个例子的第20行产生了跟上一个例子的第29到30行一样的代码)。
4 使用全局寻址模式来定义这个变量
#pragma DATA_SEG __GPAGE_SEG PAGED_RAM
char data[10] = {
0x10, 0x20, 0x30, 0x40, 0x50
};
#pragma DATA_SEG DEFAULT
5 同样的代码会产生如下代码:
23: temp = *((char *__far)data);
0000 c600 [1] LDAB #GLOBAL_PAGE(data)
0002 5b10 [2] STAB /*GPAGE*/16
0004 18f60000 [4] GLDAB data
0008 7b0000 [3] STAB temp
24: temp = *((char *__rptr)data);
000b ce0000 [2] LDX #data
000e c600 [1] LDAB #PAGE(data)
0010 5b16 [2] STAB /*RPAGE*/22
0012 a600 [3] LDAA 0,X
0014 7a0000 [3] STAA temp
译者注: 可能有人会想知道怎么看到那些编译后生成的汇编代码,很简单,在代码页上右键,在弹出菜单中点Disassemble选项,然后就可以看到当前页编译后的汇编代码了。