Never save something for a special occasion. Every day in your life is a special occasion.
分类: C/C++
2011-01-22 13:06:47
目录:
第二章 混合语言.............................. 31
Little Endian.................................31
函数与参数的命名规则..........................32
全局变量................................32
局部变量................................32
函数..................................33
函数的参数..............................34
参数的传递................................34
返回值....................................34
寄存器内容的保存............................34
在 C程序调用汇编语言函数.......................35
在汇编程序调用 C函数.........................36
使用汇编语言撰写 ISR函数........................38
正文:
第二章 混合语言
Holtek C&ASM混合编程相关议题:
Little endian
函数与参数的命名规则
参数的传递
返回值
寄存器内容的保存
在 C程序中调用汇编语言函数
在汇编程序中调用C 函数
使用汇编语言编写 ISR函数
Little Endian
HT-MCU采用 little-Endian 的数据格式。
例如:
long var @ 0x40;
var = 0x1234;
地址 0x40 存放数据 0x34,地址 0x41存放数据 0x12。
函数与参数的命名规则
大小写:
HT-Assembler 在处理符号名称时不分大小写。(事实上,所有的符号名称不管原来的形式为何,都将被译成大写字母。)而 HT-CCompiler 区分大小。因此,凡在 C 中定义被 ASM 程序引用的变量及函数,都必须以大写字母来命名。
前缀下划线:
当 CCompiler 将 C 编译为 ASM 时,会在全局变量与 C 语言函数的名称前加上前缀下划线(underscore)。
至于局部变量,可查看C编译器所生成的汇编语言文件,以找出 C局部变量在编译后的名称(如 CR3[1] 等~)。
提示:
如果只是宣告但未使用的局部变量,C编译器不会为其保留存储器空间。
例如1:全局变量
TimerCt
TMP
编译后
_TimerCt
_TMP
例如2:局部变量
void main(){
int i, j, k; ; k 未被使用
long m;
char c;
i = j = m = c = 2;
#asm
set CR3[1].2 ; set bit 10 of m, i.e. m︱= 0x400
#endasm
}
汇编语言文件中对应的部分如下:
#line 2 “C:\Holtek IDE\SAMPLE\NAME.C”
LOCAL CR1 DB ? ; i
#pragma debug variable 2 CR1 i
#line 2 “C:\Holtek IDE\SAMPLE\NAME.C”
LOCAL CR2 DB ? ; j
#pragma debug variable 2 CR2 j
#line 3 “C:\Holtek IDE\SAMPLE\NAME.C”
LOCAL CR3 DB 2 DUP (?) ; m
#pragma debug variable 2 CR3 m
#line 4 “C:\Holtek IDE\SAMPLE\NAME.C”
LOCAL CR4 DB ? ; c
#pragma debug variable 2 CR4 c
第二与第三行表示 i 被译成汇编语言中的 CR1,同理 j、m、c,但k没有被使用所以没有被编译。
注意: 如果加入新的局部变量或调整局部变量的顺序,那么编译后的名称会有所改变。
例如3:函数
GetKey
IsBusy
译成
_GetKey
_IsBusy
函数的参数:
C 函数的参数被译为函数名加后缀序号。
例如:
GetKey (int row, long col)
row 被译为 GetKey0
col 被译为 GetKey1
参数的传递
由于单片机的资源限制,盛群C 编译器通过RAM存储器取代堆栈传递参数至函数。
函数参数的命名是以函数名称附加由 0开始的后缀序号(前面刚讲过)。
就像局部变量一样,函数参数也是配置在数据存储器的区块 0。
例如:
void function (int a, int b)
参数 a 将被译成为 function0,参数 b 将被译为 function1。
BYTE!
混合编程时,即使函数参数的数据类型超过一个字节,在汇编语言中仍应该把它宣告为 BYTE的数据类型。
例如:
参数为 WORD (2 个字节) 的数据类型时,应该使用“DB 2 DUP(?)”来宣告。
返回值
C 函数的返回值会被存于 A 寄存器或 RH 系统变量。
如果返回值的大小是一个字节(例如 char、unsigned char、int、unsigned int、short、unsigned short) ,
则返回值被储存于 A 寄存器,如果返回值的大小是二个字节(例如 long、unsigned long、pointer)则高字节被储存于RH而低字节被储存于 A寄存器。
注意: RH变量位于RAM 存储器的区块0。
寄存器内容的保存
除了 ISR 函数, 在所有使用汇编语言设计的函数中, 不需要保存寄存器的内容(CCompiler已经帮我们做了?)。
如果要用汇编语言设计 ISR 函数, 则使用者有责任将 ISR 中所用到的寄存器的内容保存起来,当 ISR 函数执行完毕回到被中断之处前,将原先的数据回存给这些寄存器。
在C程序中调用汇编语言函数
分为2部分:
→ ASM文件中:
如果返回值为二个字节,则将RH宣告为 外部(external)字节变量(BYTE)。
将以下划线为前缀的函数名称宣告为 公用函数(public)。
如果函数有参数,将这些参数宣告在数据存储器的区块 0,以及宣告为公用的参数。注意参数的命名。
将返回值放入A 寄存器或RH系统变量。
→ C文件中:
将以大写字母命名的函数宣告为外部函数(external)。
调用此函数。
例如:
在C程序调用汇编函数 long KEYIN( int row, long col )
ASM文件:
;; 将变量 RH 宣告为外部变量
EXTERN RH:BYTE
;; 将函数与参数宣告为公用的
PUBLIC _KEYIN, KEYIN0, KEYIN1
;; 将函数参数安排在存储器的区块0 ; 假设单片机具有多个ram 区块(bank)
RAMBANK0 KEYINDATA
KEYINDATA .section ‘data’
KEYIN0 DB ? ;row
KEYIN1 DB 2 DUP (?) ;col, 注意不是‘KEYIN1 DW ?’哦
;function body
CODE .section ‘code’
_KEYIN:
...
MOV A, KEYIN0 ;取得参数 row 值
...
MOV A, KEYIN1 ;取得参数 col 的低字节
...
MOV A, KEYIN1[ 1 ] ;取得参数 col 的高字节
...
;;将返回值放入寄存器 A 及系统变量RH 中
MOV A, 0A0H ; 假设返回值是 0xA010
MOV RH, A ; 将高字节 0xA0 存入变量 RH
MOV A, 10H ; 将低字节 0x10 存入寄存器 A
RET
C文件:
// 将被调用的函数宣告为外部函数,同时以大写字母对此函数命名
extern long KEYIN(int row, long col);
long rc;
...
// 调用外部函数
rc = KEYIN(10, 20L);
在汇编程序中调用C函数
和前面的C调ASM一样,也分2部分:
→ C文件中:
宣告以大写字母命名的函数。
→ ASM文件中:
如果返回值是二个字节,则宣告 RH为外部的(external)字节变量(BYTE)。
宣告以下划线为前缀命名的函数为外部函数(external)。
如果函数需要参数,则将这些参数宣告为外部参数(external),要注意参数的命名规则。
如果函数有参数,将输入值设定给参数。
调用C 函数。
对于单RomBank的单片机直接Call即可。
对于多RomBank的单片机在Call前必须先设置 BP 寄存器(BankPointer)为函数所在Bank。
从 A寄存器或系统变量 RH中取出返回值。
例如:
在ASM程序调用C函数 long KEYIN( int row, long col )
假设单片机具有多个RomBank。
C文件:
long KEYIN(int row, long col){
...
}
ASM文件:
; 宣告 RH 为外部变量
EXTERN RH:BYTE
; 宣告以下划线为前缀的函数名为外部函数
extern_KEYIN:near
;; 将函数的参数宣告为外部变量
extern_KEYIN0:byte ; 参数 row
extern_KEYIN1:byte ; 参数 col, 虽然它是 long(2 bytes)但是仍要宣告为 BYTE
code_ki .section ‘code’
;; 设定参数的输入值, 准备调用函数 KEYIN(0x10, 0x200L)
mov a, 10
mov KEYIN0,a ; 参数 row
mov a, 2
mov KEYIN1[ 1 ],a ; 参数 col 的高字节
clr KEYIN1 ; 参数 col 的低字节
;;调用位于多个 ROM 存储器区块的函数
;;先要将 BP 寄存器设定为函数所在的存储器区块编号
mov a, bank _KEYIN
mov bp, a ; 更改区块编号 bank number
call _KEYIN ; 调用函数
;; 从寄存器 A 或系统变量RH 中取得返回值(低高字节分别在 寄存器A 和系统变量RH 中)
...
使用汇编语言撰写ISR函数
ISR(中断服务程序)是由硬件中断所调用,不可由程序自行调用,因此不会有参数的输入,也不会有值返回。
如果用汇编语言撰写 ISR函数,则不会与C语言的程序有关系,只需要将此汇编语言文件加入项目即可。
不论 ISR是由汇编语言或C 语言所撰写,都不要从 ISR中调用 C函数。