混和編程
1)內(nèi)嵌匯編
HCS08 C 語言提供一個很有用的功能,內(nèi)嵌匯編。凡是C語言程序段能出現(xiàn)的地方匯編語言源程序都可以出現(xiàn),但是匯編語言源程序必需位于一個C語言函數(shù)中。
語法格式
1) "asm" <匯編指令> ";" ["/*" 注釋 "*/"]
雙引號中的內(nèi)容為關(guān)鍵字,方括號中的內(nèi)容為可選項。
例如:
asm sta COPCTL; /* 喂狗 */
2) "asm" "{"
<匯編指令 1> [";" 注釋]
<匯編指令 2> [";" 注釋]
"}"
要求:大括號內(nèi)每條匯編指令占一行;標(biāo)號以“:”結(jié)尾占一行;注釋以“;”開始;可以用變量名訪問全局變量和C 函數(shù)中的局部變量。匯編語言結(jié)束前要保證堆棧內(nèi)容與匯編開始前一致。
例如:用內(nèi)嵌匯編方法實現(xiàn)統(tǒng)計字符串中字符的個數(shù)。
int strlen (char *str)
/*** 'str' 參數(shù)由堆棧傳遞. 函數(shù)返回字符串'str'的長度
假定字符串的長度小于256!
*/
{
asm {
LDHX str ; 裝入指針
CLRA ; 初始化計數(shù)器
BRA test ; 跳到test
loop:
AIX #1 ; 指針加1
INCA ; 計數(shù)器加1
test:
TST 0,X ; 字符串是否結(jié)束?
BNE loop ; 下一字符
CLRX ; 返回值在 X:A(參見后文)
};
/* 這里可以繼續(xù)寫 C 語言程序段 */
}
2)混和調(diào)用
混和調(diào)用指的是C語言函數(shù)調(diào)用匯編語言函數(shù)和匯編語言函數(shù)調(diào)用C語言函數(shù)。實現(xiàn)混和調(diào)用要解決兩個問題1:常量和變量的相互訪問;2調(diào)用協(xié)議
1常量和變量的相互訪問
C 語言函數(shù)訪問匯編語言常量、變量
在匯編語言源程序中采用匯編語法定義變量和常量;在C語言中把這些變量、常量聲明為外部常量變量,即可以用變量、常量名來訪問。匯編器和編譯器敏感段名的大小寫。
例如:
匯編源程序中
XDEF ASMData, ASMConst ;聲明 ASMData, ASMConst 可以在模塊外訪問
MyData: SECTION
ASMData: DS.W 1 ; 定義變量
MyConst: SECTION
ASMConst: DC.W $44A6 ; 定義常量
在 C 語言源程序中
#pragma DATA_SEG MyData /* 定義變量段 */
extern int ASMData; /* 變量 ASMData 是外部模塊聲明的 */
#pragma DATA_SEG DEFAULT /* 返回默認(rèn)變量段 */
#pragma CONST_SEG MyConst /* 定義常數(shù)段 */
extern const int ASMConst; /* 常量 ASMConst 是外部模塊聲明的*/
#pragma CONST_SEG DEFAULT /*返回默認(rèn)常數(shù)段 */
匯編語言訪問C語言變量、常量
在C語言中用標(biāo)準(zhǔn)格式定義常量、變量,在匯編語言中聲明這些常量變量是外部定義即可。
例如:
在C語言源文件中:
unsigned int CData; /* 定義變量 */
unsigned const int CConst; /* 定義常量 */
在匯編語言源文件中:
XREF CData ; 外部聲明的變量
XREF CConst; 外部聲明的常量
使用:
LDD CConst
STD CData
2)調(diào)用協(xié)議
調(diào)用協(xié)議,即函數(shù)參數(shù)傳遞和返回值的協(xié)議。對于HC08和HCS08調(diào)用協(xié)議是不同的。
HC08 參數(shù)傳遞:調(diào)用者函數(shù)把所需參數(shù)從左到右依次入棧,調(diào)用結(jié)束后由調(diào)用者函數(shù)從堆棧中清除傳遞的參數(shù)。在函數(shù)傳遞的是值參數(shù)的情況下,如果最后一個參數(shù)長度為2字節(jié),則用X:A寄存器對傳遞;如果最后一個參數(shù)長度為1字節(jié)且倒數(shù)第2個參數(shù)長度超過1字節(jié),則最后一個參數(shù)用寄存器A傳遞;如果最后一個參數(shù)長度為1字節(jié)且倒數(shù)第2個參數(shù)長度也是1字節(jié),則最后一個參數(shù)用寄存器X傳遞,倒數(shù)第2個參數(shù)用寄存器A傳遞;
HCS08返回值:函數(shù)返回值總是在寄存器中,根據(jù)返回值的類型不同,而采用不同的寄存器
返回值類型
寄存器
char (signed 或 unsigned)
A
int (signed 或 unsigned)
H:X
pointers/arrays(指針/數(shù)組)
H:X
function pointers(函數(shù)指針)
H:X
如果返回值的長度超過2字節(jié),則在H:X存放返回值的地址。
例如:寫一段C語言源程序,然后把它編譯為匯編語言,觀察調(diào)用規(guī)則
#include /* 為了包含中斷允許宏定義*/
#include
unsigned char r;
unsigned char f1(unsigned char v1,unsigned char v2,unsigned char v3,unsigned char v4) {
v1+=1;
v2+=1;
v3+=1;
v4+=1;
return(v4);
}
void f0(void) {
r=f1(4,9,6,3);
}
void main(void) {
EnableInterrupts; /*中斷允許 */
f0();
for(;;) {
__RESET_WATCHDOG(); /* 喂狗 */
}
}
在C語言源程序中f0函數(shù)調(diào)用f1函數(shù),傳遞參數(shù)并有返回值
函數(shù)f1編譯后的匯編語言
PSHA ; 有參數(shù)用X、A傳遞,入棧保存防止破壞
PSHX
TSX ; X=SP+1
INC 5,X ; v1+=1,參數(shù)v1在sp+6處
INC 4,X ; v2+=1; 參數(shù)v2在sp+5處
INC 1,X ; v3+=1; 參數(shù)v3在A中,被本函數(shù)入棧
INC ,X ; v4+=1; 參數(shù)v4在X中,被本函數(shù)入棧
LDA ,X ; return(v4);返回值在A中
AIS #2 ; 調(diào)整堆棧指針,指向返回地址
RTS ; 返回f0函數(shù)
函數(shù)f0編譯后的匯編語言
LDA #4
PSHA ; 參數(shù)v1入棧
LDA #9
PSHA ; 參數(shù)v2入棧
LDA #6 ; 參數(shù)v3用A傳遞
LDX #3 ; 參數(shù)v4用X傳遞
BSR f1 ; 調(diào)用函數(shù)f1
AIS #2 ; 清除傳遞的參數(shù)
STA r ; 保存返回值
RTS ; 返回main函數(shù)
Main函數(shù)編譯后的匯編語言
CLI
BSR f0
L3:
STA _COPCTL
BRA L3
只要遵循以上調(diào)用規(guī)則,就可以實現(xiàn)用C函數(shù)和匯編函數(shù)的相互調(diào)用。調(diào)用時使用對方定義的函數(shù)名。
16.2.5啟動代碼
在啟動main函數(shù)之前HCS08 C 語言會自動運行一段啟動代碼,進(jìn)行硬件初始化和建立C語言的運行環(huán)境。起動代碼一般由編譯器自動生成,也可以由用戶自寫的啟動代碼替代自動生成的啟動代碼。啟動代碼的名字為start08.c,它的主要工作是:
l 禁止中斷
l 從ROM到RAM復(fù)制并初始化數(shù)據(jù)
l 未初始化的數(shù)據(jù)區(qū)域清零
l 為堆棧分配并初始化堆棧
l 如果用到堆,創(chuàng)建并初始化堆
l 允許中斷
l 調(diào)用main()函數(shù)
分類: