瑞萨C语言和汇编混合编程
注意:详情参考《C Compiler User’s Manual 》page59 C语言调用汇编函数:
extern void asm_func( void ); ? Assembler function prototype declaration
void main()
{
:
(omitted)
:
asm_func(); ? Calls assembler function
}
汇编参考C变量:
Assembler function:
.glb _counter ? External declaration of C program's global variable
_asm_func:
:
(omitted)
:
mov.w _counter, R0 ? Reference
汇编写中断函数:
.section program
.glb _func
_int_func:
pushm R0,R1,R2,R3,A0,A1,FB ? Save registers
mov.b #01H, R0L
:
(omitted)
:
popm R0,R1,R2,R3,A0,A1,FB ? Pull registers
reit ? Return to C program
.END
C语言调用汇编带参数函数:(寄存器参数传递)
extern unsigned int asm_func(unsigned int, unsigned int);
#pragma PARAMETER asm_func(R0, R1) ? Parameters are passed via the R0 and R1
registers to the assembler function.
void main(void)
{
int i = 0x02;
int j = 0x05;
asm_func(i, j);
}
后记:
这里介绍一个万能的方法,因为汇编和C混合编程,根据不同的CPU和不同编译器,都会有所不同。我们必须清楚以下几点:
1、C语言需要参考汇编变量,那么必须在C中声明该变量(关键字extern声明),汇编需要参考C语言变量(伪指令.GLB声明),这里除了需要知道关键字外,还要注意编译器对标识符的改名规则。例如瑞萨编译器C变量名variable 在汇编中改为了_variable。
2、C函数需要调用汇编函数,那么必须知道C编译器是如何进行参数传递的,弄清楚这个,写带返回值或带参数传递的汇编函数,才知道去那个寄存器存取形参。
任何编译器,使用如下测试代码并查看汇编代码即可:
//定义全局变量
unsigned char a;
unsigned char b;
unsigned char c;
unsigned int x;
unsigned int y;
unsigned int z;
unsigned long l;
//不带参数传递
void FunNoParm(void)
{
}
_FunNoPa RTS
NOP
//1个参数传递8bit
void FunOneParm1(unsigned char i)
{
$FunOneP ENTER #01H
MOV.B:G R1L,-1H[FB]
}
EXITD
//1个参数传递16bit
void FunOneParm2(unsigned int i)
{
$FunOneP ENTER #02H
MOV.W:G R1,-2H[FB]
}
EXITD
//2个参数传递观察入栈顺序
void FunTwoParm1(unsigned char i,unsigned char j)
{
$FunTwoP ENTER #01H
MOV.B:G R1L,-1H[FB]
}
EXITD
void FunTwoParm2(unsigned int i,unsigned int j)
{
$FunTwoP ENTER #04H
MOV.W:G R1,-2H[FB]
MOV.W:G R2,-4H[FB]
}
EXITD
NOP
void FunTwoParm3(unsigned char i,unsigned int j)
{
$FunTwoP ENTER #03H
MOV.B:G R1L,-1H[FB]
MOV.W:G R2,-3H[FB]
}
EXITD
NOP
void FunThreeParm1(unsigned char i,unsigned char j,unsigned char k) {
$FunThre ENTER #01H
MOV.B:G R1L,-1H[FB]
}
EXITD
void FunThreeParm2(unsigned char i,unsigned int j,unsigned long k) {
$FunThre ENTER #03H
MOV.B:G R1L,-1H[FB]
MOV.W:G R2,-3H[FB]
}
EXITD
NOP
//返回8 位
unsigned char FunReturn8Bit(void)
{
return 0xfe;
_FunRetu MOV.B:S #FEH,R0L//8位数据通过R0L返回
RTS
NOP
}
//返回16 位
unsigned int FunReturn16Bit(void)
{
return 0xfe00;
_FunRetu MOV.W:G #FE00H,R0//16位数据通过R0返回
RTS
NOP
}
void main(void)
{
a = 0x01;
_main MOV.B:S #01H,0422H
b = 0x02;
MOV.B:S #02H,0423H
c = 0x03;
MOV.B:S #03H,0424H
x = 0xfe00;
MOV.W:G #FE00H,0410H
y = 0xfe01;
MOV.W:G #FE01H,0412H
z = 0xfe02;
MOV.W:G #FE02H,0414H
l = 0xfefefefe;
MOV.W:G #FEFEH,0416H
MOV.W:G #FEFEH,0418H
FunNoParm();
JSR.W _FunNoPa 04010H
FunOneParm1(a);
MOV.B:G 0422H,R1L//unsigned char参数a 通过R1L传递
JSR.W $FunOneP 04012H
FunOneParm2(x);
MOV.W:G 0410H,R1//unsigned int 参数x通过R1传递
JSR.W $FunOneP 0401AH
FunTwoParm1(a,b);
PUSH.B:G 0423H//unsigned char 参数b通过堆栈传递
MOV.B:G 0422H,R1L//unsigned char 参数a 通过R1L传递
JSR.W $FunTwoP 04022H
ADD.B:Q #1H,SP
FunTwoParm2(x,y);
MOV.W:G 0412H,R2//unsigned int参数y通过R2传递
MOV.W:G 0410H,R1//unsigned int参数x通过R1传递
JSR.W $FunTwoP 0402AH
FunTwoParm3(a,x);
MOV.W:G 0410H,R2//unsigned int参数x通过R2传递
MOV.B:G 0422H,R1L//unsigned char参数a通过R1L传递
JSR.W $FunTwoP 04036H
FunThreeParm1(a,b,c);
PUSH.B:G 0424H//unsigned char参数c通过堆栈传递
PUSH.B:G 0423H//unsigned char参数b通过堆栈传递
MOV.B:G 0422H,R1L//unsigned char参数a通过R1L传递
JSR.W $FunThre 04042H
ADD.B:Q #2H,SP
FunThreeParm2(a,x,l);
PUSH.W:G 0418H//unsigned char参数l通过R1L传递
PUSH.W:G 0416H//unsigned char参数l通过R1L传递
MOV.W:G 0410H,R2//unsigned int参数x通过R2传递
MOV.B:G 0422H,R1L//unsigned char参数a通过R1L传递
JSR.W $FunThre 0404AH
ADD.B:Q #4H,SP
FunReturn8Bit();
JSR.W _FunRetu 04056H
FunReturn16Bit();
JSR.W _FunRetu 0405AH
}
RTS
通过上述函数的汇编,不难发现C编译器的参数传递规律:
返回值:8Bit 通过R0L返回,16Bit通过R0返回
参数传递规律:依参数列
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
从右至左传递,寄存器传递最多2个16Bit数据(R2, R1),1个8Bit数据(R1L) 其他多出来数据通过堆栈传递。
函数返回有RTS 和EXITD 两个指令,ENTER 和EXITD成对出现。
其实还可以更简单些,比如需要写一个函数,必须要用汇编写,而且被C语言函数调用,需要参数传递。
函数原型:
unsigned int asm_func(unsigned char i,unsigned long ADDr)
{
}
然后调用,编译,查看汇编代码,函数在被调用之前有编译器自动生成的参数传递代码。那么汇编函数编写时,根据上述代码可以知道那个参数传递到了哪,在汇编中直接使用即可。