NEC编译器培训手册
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
78K0C编译器
日电电子(中国)有限公司
2007 Jun. 李唐山
内容概要
C语言编程的优势和缺点 NEC的编译流程
CC78K0的扩展关键字 数据类型及类型转换
#pragma 指令
绝对地址访问,directmap 函数调用call,callt,callf 寄存器变量
saddr区域的使用
noauto函数
norec函数
中断函数处理及声明 位域(Bit Field)
循环移位函数
乘除法的运算
静态模式
Pascal函数
闪存区域
C和汇编相互调用
系统启动例行程序 ROM化过程
Bank的使用
PM+的编译选项说明 提高程序效率的建议
算法简单 容易理解 可移植性高 无需学习汇编
int u,x,y,z;void main(void){
z = ( x + y^5 ) * u;}
int u,x,y,z;void main(void){z = ( x + y^5 ) * u;}_main:
movw
ax,!_x
xch a,x add a,!_y xch a,x addc a,!_y+1xch a,x xor a,#05H xch a,x
movw _@RTARG0,ax movw ax,!_u call !@@iumul movw !_z,ax
ret
_main:movw ax,!_x xch a,x add a,!_y xch a,x addc a,!_y+1xch a,x xor a,#05H xch a,x movw _@RTARG0,ax movw ax,!_u call !@@iumul movw !_z,ax ret
不能实时掌握程序
优化程序比较困难
int u,x,y,z;void main(void){
z = ( x + y^5 ) * u;}
int u,x,y,z;void main(void){z = ( x + y^5 ) * u;}@@DATA DSEG _u:DS (2)_x:DS (2)_y:DS (2)_z:DS (2)@@CODE CSEG _main:
movw ax,!!_y movw bc,!!_x addw bc,ax xor c,#05H ; 5movw ax,!!_u muluw bc movw !!_z,bc ret
@@DATA DSEG _u:DS (2)_x:DS (2)_y:DS (2)_z:DS (2)@@CODE CSEG _main:movw ax ,!!_y movw bc ,!!_x addw bc,ax xor c ,#05H ; 5movw ax ,!!_u muluw bc movw !!_z,bc ret
p2 C
编
译
器
开
发
流
程
CC78K0的扩展关键字
p5 _ _callt/callt・・・・・・・・・・・callt函数的声明
_ _callf/ callf・・・・・・・・・・callf函数的声明
_ _sreg/ sreg・・・・・・・・・・sreg变量的声明
noauto・・・・・・・・・・・・・・noauto函数的声明
_ _leaf / norec・・・・・・・・・・norec函数的声明
_ _boolean/ boolean・・・・布尔型变量的声明
_ _interrupt ・・・・・・・・・・・・硬件中断函数
_ _interrupt_brk・・・・・・・・软件中断函数
_ _asm・・・・・・・・・・・・・・・・汇编语句
_ _pascal・・・・・・・・・・・・・pascal函数
_ _directmap・・・・・・・・・・绝对地址配置指定
_ _temp ・・・・・・・・・・・・・・・临时变量
bit ・・・・・・・・・・・・・・・・・・・・bit型变量的声明
memset/memcpy・・・・・・・存储器操作函数
p6
void --connection of null values
char --size of basic character (ASCII)
int--signed integer (-32768 ~ +32767)
long int--signed integer (-2147483648 ~ +2147483647) float --single precision floating point number
(1.17549435E-38F~3.40282347E+38F)
bit, boolean--integers represented with a single bit (0 or 1)
C 源程序
汇编输出
@@BITS BSEG _i DBIT _j DBIT _k DBIT
@@DATA DSEG
UNITP
_a:DS (1)_b:DS (2)_c:DS (2)_d:DS (2)_e:DS (2)_f:DS (4)_g:DS (4)_h:DS (4)
char a;int b;
short int c;signed d;unsigned e;long int f;float g;double h;bit i;
boolean j;__boolean k;void main(void){a=sizeof(h);}
p59
Int/short型转换为char型。
-ZI
对以下没有指定类型的,认为是char型。
A)函数的参数和返回值
B)没有指定类型的变量/函数声明
Long型转换为int型。
-ZL
[数据类型转换的设置]
CPU 控制指令#pragma NOP #pragma BRK #pragma HALT #pragma STOP
[#pragma ]指令总览
Directive: #pragma
中断指令#pragma DI #pragma EI 特殊功能寄存器#pragma sfr 汇编指令嵌入#pragma asm 绝对地址存取#pragma access
输出段的名称修改#pragma section
p22
[#pragma]特殊寄存器SFR
PUBLIC _main
_main:
mov PM0,#00H mov P0,#0FFH clr1MK0L.1ret END
#pragma sfr void main(void){
PM0 = 0x00;P0= 0xff;PMK0= 0;
}
C 源程序
汇编输出
特殊寄存器(SFR)位于『FF00h~FFFFh 』的256字节。
p28
[#pragma ]中断函数
PUBLIC _main _main:
di ei ret
#pragma DI #pragma EI void main(void){
DI();EI();
}
#pragma指令在#include之前定义,否则被认为是非法预处理指令。
DI( ),EI( )必须大写,必须加括号,必须有#pragma定义。
p38
[#pragma]CPU控制命令#pragma NOP
#pragma BRK #pragma HALT #pragma STOP void main(void) {
NOP();
BRK();
HALT();
STOP(); }PUBLIC_main _main:
nop
brk
halt
stop
ret
进入某种待机状态时,指令后应该加4个以上的NOP语句,确保成功进入。
p39
[#pragma]嵌入汇编程序一PUBLIC
_main
_main:
movw ax,#0fH xch a,x ret END
#pragma asm
void main(void){
#asm
movw ax,#0fH xch a,x #endasm
}
C 源程序
汇编输出
汇编的内容存入CSEG区段,名称为@@CODE。
不产生目标模块文件,只生成汇编源文件。
p33
[#pragma]嵌入汇编程序二
PUBLIC _a PUBLIC _b PUBLIC _main
_main:
movw ax,!_a movw !_b,ax ret END
int a;int b;
void main(void){
__asm("\tmovw ax,!_a");__asm("\tmovw !_b,ax");}
这样声明不需要#pragma asm的声明。
__asm必须小写,否则无法识别。
__asm的字符串必须符合ANSI标准,可以使用ESC字符。
[#pragma]改变输出段的名称 改变编译器输出段可以独立对每个段进行定位,这样数据单元的数据也可以独立的存储。
如果忽略开始地址,就认为使用默认的空间分配方式。
如果在C代码之后执行#pragma指令,那么会生成汇编源文件而不生成目标模块文件。
#pragma section @@CODE CC1 AT 2400H #pragma section @@DATA ??DATA #pragma section @@DATA DATA2#pragma section @@DATA @@DATA
p45
[#pragma]绝对地址读
PUBLIC _a PUBLIC _b PUBLIC
_main
_main:
mov a,!0fb01H mov !_a,a
mov a,0FE23H mov !_a,a
movw ax,!0fb06H movw !_b,ax
movw ax,0FE68H movw !_b,ax
ret
#pragma access char a;int b;
void main(void){
a = peekb(0xfb01);a = peekb(0xfe23);
b = peekw(0xfb06);b = peekw(0xfe68);}
C 源程序
汇编输出
p41
[#pragma]绝对地址写(慎用)
C 源程序
汇编输出
PUBLIC _a PUBLIC _b PUBLIC
_main
:
_main:
mov a,#05H mov !0fb01H,a mov 0FE23H,#05H movw ax,#07H movw !0fb06H,ax movw 0FE68H,#07H
ret
#pragma access char a;int b;
void main(void){
pokeb(0xfb01,5);pokeb(0xfe23,5);pokew(0xfb06,7);pokew(0xfe68,7);}
[directmap ]绝对地址分配
PUBLIC _c PUBLIC _d PUBLIC _e PUBLIC _xx PUBLIC _main _c EQU 0FE00H _d EQU 0FE20H _e EQU 0FE21H _xx
EQU 0FE30H EXTRN __mmfe00EXTRN __mmfe20EXTRN __mmfe21EXTRN __mmfe30EXTRN __mmfe31_main:
mov a,#01H mov !_c,a
mov _d,#012H set1_e.5
mov _xx,#05H mov _xx+1,#0AH
ret
__directmap char c = 0xfe00;__directmap __sreg char d = 0xfe20;__directmap __sreg char e = 0xfe21;__directmap struct x {char a;char b;
} xx = {0xfe30};void main(void){
c = 1;
d = 0x12;e.5 = 1;xx.a = 5;xx.b = 10;}
p75
[call]函数调用
P23, p40•程序区域CALL调用
•通过CALLT表调用
•通过CALLF入口调用
•Call指令三字节,callf指令双字
节,callt指令单字节
CALLF区域
0800-0FFFH,存储可以使用两字节指令
CALLF调用的子程序入口地址
CALLT区域
0040-007FH(共64字节),存储的内容为
使用单字节指令CALLT调用的子程序入
口地址。
寄存器变量
void main(void)
{
register char i,j, k;
i=1;
j=2;
i+=j;
}
EXTRN_@KREG00
PUBLIC_main
_main:
push hl
movw
ax,_@KREG00
push ax
mov l,#01H; 1
mov h,#02H; 2
mov a,h
add l,a
pop ax
movw_@KREG00,a
pop hl
ret
必须要指定-QR选项。
声明的寄存器变量存放在HL和saddr区域的(FED0H~~FEDFH)。
如果只声明了i, j,那么都分配在HL中,不动用saddr区域。
最多占用_@KREG00~11共12个字节,多余的当作普通变量。
p24
sreg 变量
PUBLIC _a PUBLIC _b PUBLIC
_main
@@DATS DSEG SADDRP _a:DS (2)
@@DATA DSEG UNITP _b:DS (2)
_main:
movw _a,#0AH
movw ax,#0AH ; 10movw !_a,ax
ret
sreg int a;int b;
void main(void){
a=10;b=10;}
Sreg 变量存储在高速RAM 区域,加快了速度。
被定义为sreg 变量的char/short/int/long 型变量每一位都自动成为布尔型变量,支持位操作。
声明时没有赋值的sreg 变量初始值自动为0。
p25
Saddr区域的使用
p25 正常模式时saddr区域的位置是(FE20H~~FEB7H) 。
Saddr区域的指令一般比较短,所以可以提高效率。
saddr区域中存放的内容:
1 sreg变量
2 外部变量/外部静止变量
3 内部静止变量
4 参数/自动变量
p26
2 外部变量/外部静止变量–RD
不管是否用sreg进行定义,外部变量/外部静止变量(const型除外)都是自动分配到saddr区域的。
extern声明的变量同样适用,处理这些变量时和存放在saddr区域一样对待。
-RD[n],不同参数n指定的不同模块之间不可以进行手动链接。
3 内部静止变量-RS
内部静止变量(const型除外)不论有没有用sreg声明,都会被分配到saddr区域。
sreg声明的变量和用RS选项指定存放在saddr区域的变量在处理时样对待。
4 参数/自动变量-RK
仅支持静态模式,必须指定-SMx参数。
参数/自动变量(const型除外)不论有没有用sreg声明,都会被分配到saddr区域。
被声明为寄存器变量的参数/变量都不会被分配到saddr区域。
同时使用了-QV参数的话,优先分配到DE寄存器。
-RS[n]参数指定的不同模块之间可以进行手动链接。
-RK[n]参数指定的不同模块之间可以进行手动链接。
saddr区域的设置
noauto 函数
noauto 函数不产生事前事后的堆栈操作代码。
所有的参数都放在寄存器或者寄存器变量用saddr 区域中(FEDCH~~FEDFH )。
编译时需要使用-QR 选项,-QR1和-QR2有不同的效果。
-SM ,-ZA 选项时,noauto 函数无效,当作普通函数处理。
noauto short nfunc(short a, short b);short i;
void main(void){
static short ii, jj;i= nfunc(ii, jj);}
noauto short nfunc(short a, short b){
short m;m = a + b;return(m);}
使用-QR1选项时
EXTRN _@KREG12EXTRN _@KREG13EXTRN _@KREG14PUBLIC _nfunc PUBLIC _main @
使用-QR2选项时
EXTRN _@KREG00EXTRN _@KREG02EXTRN _@KREG12EXTRN _@KREG13EXTRN _@KREG14PUBLIC _nfunc PUBLIC
_main
p29
norec 函数
如果函数体内没有调用别的函数,那么可以定义为norec 函数。
norec 函数不产生事前/事后的堆栈操作代码。
norec 函数的参数被存放在寄存器和saddr 区域(FEC0H-FEC7H )。
编译时需要使用-QR 选项,此时可以占用(FEC8H-FECFH )。
norec short nfunc(short a, short b);short i;
void main(void){
static short ii, jj;l = nfunc(ii, jj);}
norec short nfunc(short a, short b){
short m;m = a + b;return(m);}
使用-QR1选项时
EXTRN _@KREG12EXTRN _@KREG13EXTRN _@KREG14PUBLIC _nfunc PUBLIC
_main @
p30
使用-QR2选项时
位域
struct tagname {
unsigned char A:1;unsigned char B:1;unsigned int C:2;unsigned int D:1;};
存放位域的顺序可以改变,通过指定-RB 选项,位域按照MSB 的顺序存放,否则按照LSB 进行存储。
无符号字符类型的位域不能跨越字节边界。
无符号整型的位域不能跨越字边界,但是可以跨越字节边界。
同一类型变量的位域可以存放到同一字节单元(或字单元)。
如果变量类型不同,位域就分配到不同的字节单元(或字单元)。
使用RB 选项
不使用RB 选项
编译选项Æ数据安排Æ从MSB 开始位域
p42
Bit 型变量
bit 类型的变量被当作1位数据存放在saddr 区域。
char /int /short /long 型的sreg 变量(除了数组元素和结构成员),以及8位sfr 变量默认可以当作bit 类型变量使用。
#define ON 1#define OFF 0bit data1;bit data2;
void main (void){
data1 = ON;data2 = OFF;while(data1){
if(data1 != data2){data1 = 0;}}}
不能被定义为数组或者被数组引用。
不能用于构造体和共用体。
不能当作函数的参数被使用。
声明时不能赋初值。
p31
中断函数处理内容
p35
函数的入口地址放入指定的中断名对应的中断向量表中。
中断函数把下表中的四项(不包括ASM语句中使用的)进行保存与恢复的代码放在中断函数的开头和结尾。
①寄存器
②寄存器变量占用的saddr区域
③norec函数的参数/auto变量占用的saddr区域
④实时库占用的saddr区域(仅用于正常模式)
中断函数的修饰词
p38
<不可屏蔽/可屏蔽中断函数>__interrupt void func()
{处理过程reti }
<软件中断函数>__interrupt_brk void func()
{处理过程retb }
<不可屏蔽/可屏蔽中断函数>__interrupt void func(){处理过程reti }<软件中断函数>__interrupt_brk void func(){处理过程retb }
中断函数的声明
@@CODE CSEG _inter1:sel RB1
mov a,P1mov x,#00H ; 0 xch a,x movw !_ii,ax inc P0reti
_inter2:sel RB2
movw ax,sp movw sp,#_buff+10/* 同inter1处理*/reti @@VECT06CSEG AT 0006H _@vect06:
DW _inter1
_@vect08:
DW _inter2
#pragma interrupt INTP0 inter1 RB1#pragma interrupt INTP1 inter2 SP=buff+10 RB2void main(void);void inter1(void);void inter2(void);int ii;
unsigned char buff[10];
/* inter2()用stack区域*/
void inter1(void){
ii = P1;
P0++; /* INTP0端口输入的中断处理*/}
void inter2(void){
ii = P0;
P1++; /* INTP1端口输入的中断处理*/}
void main(void){while(1){}}
p37
循环移位函数
不需要具体描述处理过程就可以实现循环移位的功能。
通过模块指令#pragma rot 的声明来使用该功能。
rorb /rolb /rorw /rolw
#pragma rot
unsigned char a=0x11;unsigned char b=2;unsigned char c;void main(void){
c=rorb(a,b);c=rolb(a,b);}
#pragma rot unsigned char a=0x11;unsigned char b=2;unsigned char c;void main(void){c=rorb(a,b);c=rolb(a,b);}_main:
mov a,!_b mov c,a mov a,!_a ror a,1dbnz c,$$-1mov !_c,a mov a,!_b mov c,a mov a,!_a rol a,1dbnz c,$$-1mov !_c,a
ret
p48
因为乘法指令的输入/输出数据代码已经被优化,与普通的乘法指令相比,其生成的代码更小,执行速度更快。
通过模块指令#pragma mul 的声明来使用该功能。
#pragma mul
unsigned char a = 0x11;unsigned char b = 2;unsigned int i;void main(void){
i = mulu(a, b);}
#pragma mul unsigned char a = 0x11;unsigned char b = 2;unsigned int i;void main(void){i = mulu(a, b);}@@INIT DSEG UNITP _a:DS (1)_b:
DS (1)@@DATA DSEG UNITP _i:DS (1)DS
(1)
_main
mov a,!_b mov c,a mov a,!_a rol a,1dbnz c,$$-1mov
!_c,a ret
p49
采用内联扩展方式而不是函数调用方式,除法指令所占用的输入/输出数据大小是确定的。
通过模块指令#pragma div 的声明来使用该功能。
divuw /moduw
#pragma div int a=0x1234;unsigned int b=0x12;unsigned char m;unsigned int r;void main(void){r =divuw(a,b);m =moduw(a,b);}
#pragma div int a=0x1234;unsigned int b=0x12;unsigned char m;unsigned int r;void main(void){r =divuw(a,b);m =moduw(a,b);}p49
_main:
mov a,!_b mov c,a movw ax,!_a divuw c
movw !_r,ax mov a,!_b mov c,a movw ax,!_a divuw c mov a,c mov !_m,a
ret
BCD 操作函数
通过模块指令#pragma bcd 的声明。
不需要具体描述处理过程就可以实现BCD 操作。
adbcdb /sbbcdb /adbcdbe /sbbcdbe /adbcdw /sbbcdw /
adbcdwe /sbbcdwe /bcdtob /btobcde /bcdtow /wtobcd /btobcd
#pragma bcd
unsigned char a = 0x12;unsigned char b = 0x34;unsigned char c;void main(){
c = adbcdb(a, b);c = sbbcdb(b, a);}
#pragma bcd unsigned char a = 0x12;unsigned char b = 0x34;unsigned char c;void main(){ c = adbcdb(a, b);c = sbbcdb(b, a);}_main:
mov a,!_a add a,!_b adjba mov x,#00H ; 0
xch a,x movw
!_c,ax mov a,!_b sub a,!_a adjbs mov x,#00H ; 0xch a,x movw !_c,ax
ret
p50
静态模式
p58
・参数全部通过寄存器传递。
・通过寄存器传递的函数参数,存储在函数指定的静态区域。
・自动变量存储在函数指定的静态区域。
・对于leaf函数(由编译程序自动辨别)、参数及自动变量、低于
0FEDFH地址的saddr区域,按描述的顺序从高地址开始分配。
由
于saddr领域和所有模块的leaf函数共有,这块区域被称为共享区域。
共享区域的最大数量,可以使用-SM选项来指定。
SM[nn]
※nn字节(0~16) 作为共享区域分配(0~16)其余分配到函数指定的静态领域。
・加上sreg/__sreg关键字的函数参数和自动变量被分配到saddr区域,支持位操作。
Pascal函数
p60
产生堆栈修正的代码,堆栈用于在函数调用时放置参数。
该过程不在调用函数方进行,而改在被调用函数中进行。
函数调用比较频繁时,可以减少目标代码量。
如果此函数调用比较少,会增加代码量。
函数声明时,在前面加入__pascal属性。
编译时使用-ZR选项,函数调用接口的自动Pascal化。
数学函数标准库不支持pascal函数。
闪存区域和启动区域
p62
启动例行程序和中断函数可以分配在闪存区域内。
可以从启动区域调用闪存区域的函数。
决定启动例行程序,中断函数所使用的跳转表的起始地址,以 及从启动区域调用闪存区域函数的跳转表地址。
起始地址的值要和-ZB选项指定的闪存首地址相符,否则产生 链接错误。
#pragma ext_table 0x2000 #pragma interrupt INTP0 intp void intp(){处理}; 如果没有指定-ZF选项,那么 函数会被当作普通函数。
PUBLIC _intp @ECODE CSEG _intp: reti @EVECT06 CSEG AT 02009H br !_intp PUBLIC _@vect06 @@VECT06 CSEG AT 0006H _@vect06: DW 2009H
All rights reserved © 2007, NEC Electronics (China) — 41
参数/返回的扩展限制
unsigned char func1(unsigned char x, unsigned char y); unsigned char c, d, e; void main(){ c = func1(d, e); c = func2(d, e); } unsigned char func1(unsigned char x, unsigned char y) { return x + y; } func2(unsigned char x, unsigned char y){ return x + y; }
p65
函数返回值的类型定义为char/unsigned char型时,不生成返回 值的int扩展编码。
在函数原型中定义参数,且参数定义为char/unsigned char型 时,不生成参数的int扩展编码。
编译时候使用-ZB选项。
All rights reserved © 2007, NEC Electronics (China) — 42
寄存器直接引用
p68
使用直接内联扩展而不是函数调用方式访问目标寄存 器,输出代码并生成目标文件。
根据C语言描述,可以方便的进行寄存器的访问。
用#pragma realregister指令声明。
All rights reserved © 2007, NEC Electronics (China) — 43
[C调用汇编]
C代码
extern int i; extern sreg int j; extern void func1(void); void main(void) { func1(); }
p87
汇编代码
PUBLIC _func1 PUBLIC _i PUBLIC _j @@DATS DSEG _i: DS @@DATS DSEG _j: DS UNITP ( 2) SADDRP ( 2)
CODE1 CSEG AT 80H _func1: NOP NOP RET END
All rights reserved © 2007, NEC Electronics (China) — 44
[汇编调用C]
C代码
int i; sreg int j; __callt void funct(void); __callf void funcf(void); extern void func1(void); void func2(void){ NOP(); NOP(); NOP(); } void main(void){ func1(); } __callt void funct(){} __callf void funcf(){}
p91
汇编代码
PUBLIC _func1 EXTRN _func2 EXTRN _i EXTRN _j CODE1 CSEG AT 80H _func1: CALL !_func2 CALLT [?funct] CALLF !_funcf MOVW MOVW MOVW END
All rights reserved © 2007, NEC Electronics (China) — 45
AX,#01H !_i,AX _j,#02H
[系统启动]
Startup Routine内容
① Reset vector setting ③ Hardware initialization ② Stack pointer ④ ROMization Function
⑤ Main Function Call, Exit Function Call
All rights reserved © 2007, NEC Electronics (China) — 46
[系统启动一]复位地址
复位向量:
@@ VECT
CSEG AT 0 DW _@cstart
All rights reserved © 2007, NEC Electronics (China) — 47
[系统启动二]堆栈指针
使用_@STBEG设置堆栈。
_@STBEG 是链接器在选择生成堆栈指针设置时自动生成的变量。
MOVW
SP,#_@STBEG
All rights reserved © 2007, NEC Electronics (China) — 48
[系统启动三]硬件初始化
使用hdwinit,这个函数由用户提供给启动过程调用。
CALL
!_hdwinit
void hdwinit(void) { user creates }
All rights reserved © 2007, NEC Electronics (China) — 49
[系统启动三]初始化过程
C源码
#pragma DI #pragma EI void hdwinit(void){ DI(); EI(); } void main(void){ while(1){ } }
RESET Call hdwinit() Call main() while loop
All rights reserved © 2007, NEC Electronics (China) — 50
。