菜鸟请教 ,怎么用汇编调用C函数,怎么用C调用汇编写的函数,他们的参数是怎么传递的?
RT啊
考虑到C语言与汇编语言的接近程度,在此只介绍这二种程序设计语言之间的接口问题。
了解的内容:C语言源程序生成汇编语言的源程序的方法,从所生成的汇编语言源程序中,看出C语言语句用汇编语言实现的技巧,从而对C语言语句的语义有一个更进一步的理解。
掌握的内容:C语言函数参数的传递方法,在汇编语言中读取C语言函数参数的方法。
建议学习时间:2小时。
第12章 汇编语言和C语言
C/C++语言是一个被广泛使用的程序设计语言,它不仅具有良好的高级语言特征,而且还具有一些低级语言的特点,如:寄存器变量、位操作等。所以,C语言的程序与汇编语言程序之间能很平滑地衔接。另外,目前主要的C语言程序开发环境,如:Turbo C/C++、Borland C/C++等,也都提供了很好的混合编程手段。
本章主要介绍汇编语言和C语言的混合编程和调用方法。虽然其它高级语言,如:Pascal、Basic等,也可与汇编语言混合使用,但出于其应用范围的考虑,不再对它们进行介绍,感兴趣的读者可参阅有关技术资料。
12.1 汇编指令的嵌入
为了提高C语言程序内某特殊功能段的处理效率,我们可以在其源程序中嵌入一段汇编语言程序段。这样做,虽然能达到提高了程序处理效率的目的,但它无疑以丧失源程序的可移植性为代价。所以,当想用C语言和汇编语言混合编程时,程序员需要权衡采用这种方法的利与弊。
在C语言中,嵌入汇编语言的语法如下(*):
asm <opcode> <operands> <; or newline>
注意:这里的分号';'不是汇编语言中起注释作用的分号,而是作为语句的分隔符。
若C语言源程序中嵌入一条汇编语句,则可按下列方式来做:
asm mov ax, data
若要嵌入一组汇编语句,则需要用括号'{'和'}'把它们括起来。
asm {
mov ax, data1
xchg ax, data2
mov data1, ax //实现整型变量data1和data2之值的交换
}
例12.1 在C语言源程序中嵌入汇编语言语句实现赋值语句A=A+B+C,其中:A、B、C都是整型变量。
解: ……
asm {push ax //实现整型变量A=A+B+C
mov ax, A
add ax, B
add ax, C
mov A, ax
pop ax
}
12.2 C语言程序的汇编输出
在Turbo C++或Borland C++编程环境下,我们可TCC或BCC行命令把一个C语言的源程序转换成汇编语言的源程序。通过阅读汇编语言程序可以很准确地知道C语言语句的功能是如何实现的,这样,可为将来学习《编译原理》课程中的"寄存器调度"和"代码生成"等相关知识打下良好的基础。
C语言源程序转换的命令格式如下:
TCC -S t1.cpp 或 BCC -S t1.cpp ;假设其文件名为t1.cpp
若命令TCC/BCC不带参数的话,则将显示其使用方法。
下面是C语言程序及其相对应的汇编语言程序,希望读者能逐行对照理解它们语句之间的转换关系,这将能进一步理解高级语言的语句功能。
1、C语言程序清单
#include <stdio.h>
int sum(int a, int b, int c)
{
return (a+b+c);
}
void main()
{int a, b, c;
a = b = 12;
c = 32;
printf("%d", sum(a,b,c));
}
2、生成的汇编语言程序清单
…… ;一系列辅助说明信息
_TEXT segment byte public 'CODE' ;代码段的开始
;int sum(int a, int b, int c) ;C语言语句
assume cs : _TEXT
@sum$qiii proc near ;过程说明,对应于C语言sum过程
push bp ;为读取堆栈中的参数作准备,可参见第7.3.3节内容
mov bp, sp
;{
;return(a+b+c);
mov ax, word ptr [bp+4]
add ax, word ptr [bp+6]
add ax, word ptr [bp+8]
jmp short @1@58
@1@58:
;}
pop bp ;sum子程序结束的代码
ret
@sum$qiii endp
;void main() assume cs : _TEXT
_main proc near ;过程说明,对应于C语言中的主函数main()
push bp
mov bp, sp
sub sp, 6
;{int a, b, c; ;局部变量是用堆栈来存储的,请见第7.5.10节
; a = b = 12; ;给局部变量赋值
mov ax, 12 ;用给堆栈单元赋值来实现对局部变量的赋值
mov word ptr [bp-4], ax
mov word ptr [bp-2], ax
; c = 32; mov word ptr [bp-6], 32
; printf("%d", sum(a,b,c)); ;调用系统标准函数 push word ptr [bp-6]
push word ptr [bp-4]
push word ptr [bp-2]
call near ptr @sum$qiii ;用汇编语言形式调用自定义函数sum
add sp, 6
push ax
mov ax, offset DGROUP : s@
push ax
call near ptr _printf ;用汇编语言调用标准函数printf
pop cx
pop cx
; } mov sp, bp ;main子程序结束的代码
pop bp
ret
_main endp
_TEXT ends ;代码段的结束
_DATA segment word public 'DATA' ;数据段的定义
s@ label byte
db '%d'
db 0
_DATA ends
public _main ;下面说明函数的属性,请见第7.6.3节
public @sum$qiii
extrn _printf : near
_s@ equ s@
end
12.3 简单的屏幕编辑程序
下面是一个简单的屏幕编辑的C语言程序,它不仅涉及到键盘处理、光标定位、屏幕输出、字符颜色等,而且还运用了C语言和汇编语言的混合编程方法。若读者能把它改写成相同功能的汇编语言程序,那么,可以说,你已基本掌握了中断的使用方法,也对计算机输入输出的工作方式有了更进一步的认识。
该程序的功能:
◆ 可用移动光标键↑、↓、←和→移动光标1行或1列,也可用TAB/Shift+TAB、Home和End键跳跃地移动光标;
◆ 当光标已在第1行,再向上移动时,这时,光标被定位到第25行,反之也然;
◆ 当光标已在第0列,还要向左移动时,光标被定位到第79列,反之也然;
◆ 当按下^W或^Z时,屏幕将向上或向下滚动1行;
◆ 显示当前键盘的状态:大小写状态、数字键盘状态和插入/修改状态;
◆ 如果按普通的键,将在屏幕上显示该字符,如果按下用Alt、Ctrl或Shift组合的组合键,则显示该按键的扫描码;
◆ 用Esc键来结束程序的运行。
C语言的源程序清单:
#define NUM_KEY 0x20 /* 键盘状态字宏定义 */
#define CAPS_KEY 0x40
#define ESCAPE 27 /* 几个功能键的宏定义 */
#define TAB_KEY 9
#define SHIFT_TAB 15
#define CTRL_W 23
#define CTRL_Z 26
#define UP_ARROW 72
#define DOWN_ARROW 80
#define LEFT_ARROW 75
#define RIGHT_ARROW 77
#define INSERT 82
#define END_KEY 79
#define HOME_KEY 71
#define UP_SCROLL 6 /* 屏幕滚动宏定义 */
#define DOWN_SCROLL 7
#include <dos.h>
int insert, cap_key, num_key;
/* up_down:屏幕滚动方式:6-向上滚; 7-向下滚
(l_row, l_col)-(r_row, r_col):滚动矩形的对角线坐标
num:屏幕滚动的行数,0-清屏
attr:滚动后所剩下行的属性 */
cls(int up_down, int l_row, int l_col, int r_row, int r_col, int num, int attr)
{union REGS in, out;
in.h.ah = up_down; in.h.al = num;
in.h.ch = l_row; in.h.cl = l_col;
in.h.dh = r_row; in.h.dl = r_col;
in.h.bh = attr;
int86(0x10, &in, &out);
}
get_cursor(int *x, int *y) /* 取当前光标的位置,并分别存入变量x和y中 */
{union REGS in, out;
in.h.ah = 3; in.h.bh = 0;
int86(0x10, &in, &out);
*x = out.h.dh; *y = out.h.dl;
}
locate(int row, int col) /* 把光标设置在(row, col)位置 */
{union REGS in, out;
in.h.ah = 2; in.h.bh = 0;
in.h.dh = row; in.h.dl = col;
int86(0x10, &in, &out);
}
disp_string(int row, int col, char string[]) /* 在(row, col)位置显示字符串string */
{struct REGPACK in, out;
int x, y;
get_cursor(&x, &y);
locate(row, col);
in.r_ds = FP_SEG(string); in.r_dx = FP_OFF(string); in.r_ax = 0x900; intr(0x21, &in);
locate(x, y);
}
check_key_state() /* 在(row, col)位置以属性attr显示字符ch */
{char state;
state = bioskey(2);
if (state & CAPS_KEY)
{if (!cap_key) {cap_key = 1; disp_string(24, 66, "CAP$");}
}
else if (cap_key) {cap_key = 0; disp_string(24, 66, " $");}
if (state & NUM_KEY)
{if (!num_key) {num_key = 1; disp_string(24, 70, "NUM$");}
}
else if (num_key) {num_key = 0; disp_string(24, 70, " $");}
}
insert_key() /* 在最后一行显示插入/修改状态标志,并改变光标形状 */
{union REGS in, out;
insert = 1 - insert;
disp_string(24, 74, (insert ? "INS$" : " $")); /* 显示插入/修改标志 */
in.h.ah = 1;
in.h.ch = (insert ? 0 : 14); in.h.cl = 15; /* 改变光标的形状 */
int86(0x10, &in, &out);
}
move_right(int row, int col, int len) /* 在(row, col)位置之后的字符和属性向后移len个位置 */
{int j, attr;
char ch;
for (j = 79; j >= col+len; j--)
{read_char_attr(row, j-len, &ch, &attr);
write_char_attr(row, j, ch, attr);
}
}
read_char_attr(int row, int col, char *ch, int *attr) /* 在读(row, col)位置字符和属性,并分别存入ch和attr */
{union REGS in, out;
locate(i, j);
in.h.ah = 8; in.h.bh = 0;
int86(0x10, &in, &out);
*ch = out.h.al; *attr = out.h.ah;
}
write_char_attr(int row, int col, char ch, int attr) /* 在(row, col)位置以属性attr显示字符ch */
{union REGS in, out;
locate(row, col);
in.h.ah = 9; in.h.al = ch;
in.h.bh = 0; in.h.bl = attr; in.x.cx = 1;
int86(0x10, &in, &out);
}
ctos(char ascii, char str[]) /* 把字符的ASCII码转换成字符串 */
{int i;
i = 2;
do {str[i--] = ascii%10 + '0';
ascii /= 10;
} while (ascii > 0);
for (; i >= 0; i--) str[i] = ' ';
}
main()
{int k, key, row, col;
char ch1, ch2, str[]=" $"; /* 前面有3个空格 */
char msg1[]="This is a simple screen edidtor.$",
msg2[]="You can move cursor by Arrow keys, TAB/Shift-TAB, Home and End.$",
msg3[]="You can press ^W for scroll up or ^Z for scroll down.$",
msg4[]="It has some functions, such as insert/modify a char.$",
msg5[]="If you press a function key, or key combined with Alt, Ctrl, Shift, it will display the key's scan code.$",
msg6[]="The program exits when you press ESCAPE.$";
cls(UP_SCROLL, 0, 0, 24, 79, 0, 7);
disp_string(0, 0, msg1); disp_string(2, 0, msg2);
disp_string(4, 0, msg3); disp_string(6, 0, msg4);
disp_string(8, 0, msg5); disp_string(11, 0, msg6);
row = col = ch1 = insert = 0;
locate(row, col);
while (ch1 != ESCAPE)
{while (ch1 != ESCAPE)
{if (!bioskey(1)) {check_key_state(); continue;}
key = bioskey(0);
ch1 = key; ch2 = key >> 8;
if (ch1 != 0)
{switch(ch1)
{case TAB_KEY:
col = ((col&0xFFF8) + 8) %80;
break;
case CTRL_W:
cls(DOWN_SCROLL, 0, 0, 24, 79, 1, 7);
row = row + 1;
break;
case CTRL_Z:
cls(UP_SCROLL, 0, 0, 24, 79, 1, 7);
break;
default:
if (ch1 == ESCAPE) continue;
if (insert) move_right(row, col, 1);
write_char_attr(row, col, ch1, 31);
col = (col+1+80) % 80;
break;
}
locate(row, col);
continue;
}
switch (ch2)
{case UP_ARROW:
row = (row-1+25) % 25;
break;
case DOWN_ARROW:
row = (row+1+25) % 25;
break;
case LEFT_ARROW:
col = (col-1+80) % 80;
break;
case RIGHT_ARROW:
col = (col+1+80) % 80;
break;
case SHIFT_TAB:
k = col & 0xFFF8;
col = (col - ((k==0)? 8:k+80)) % 80;
break;
case HOME_KEY:
col = 0;
break;
case END_KEY:
col = 79;
break;
case INSERT:
insert_key(&insert);
break;
default:
ctos(ch2, str);
k = strlen(str)-1;
if (insert) move_right(row, col, k);
disp_string(row, col, str);
col = (col + k + 80) % 80;
break;
}
locate(row, col);
}
}
cls(UP_SCROLL, 0, 0, 24, 79, 0, 7);
}
12.4 习题
12.1、把12.3节中的C语言程序改写成汇编语言程序。
12.2、编写C语言程序,输出下面表达式的值,要求该表达式的计算用嵌入汇编语言程序段的方法来实现(注:题中所有变量都是整型)。
1)、1230 + 'A' - a
2)、b * b – 4 * a * c
3)、(a + b) / c + d
4)、9*c / 5 + 32
5)、(a % 9 + 89) * 8
6)、x * x + y * y
12.3、用汇编语言编写函数Display(Data),其功能是在当前光标处显示无符号整数Data,然后,编写一个C语言程序调用Display来显示整型变量的值。
12.4、用汇编语言实现下列C语言标准函数,并在C语言程序中验证之(假设未指明的变量都是整型)。
1)、isalpha(int Ascii) /*若Ascii是字母的Ascii码,则其函数值为真,否则为假*/
2)、isxdigit(int Ascii)
/*若Ascii是十六进制字符('0'~'9'、'A'~'F'和'a'~'f'),那么,其函数值为真,否则为假*/
3)、strlwr(char *s) /*把字符串s中的字母转换成小写*/
4)、strchr(char *s1, int Ascii)
/*在字符串s1中查找是否存在字符Ascii。若不存在,则返回NULL(即0),否则,返回指向该字符在字符串中位置的指针*/
5)、strncmp(char *s1, char *s2, int Len)
/*比较字符串s1和s2前Len个字符,若s1<s2,其值小于0;s1==s2,其值为0;否则,其值大于0*/
6)、strncpy(char *Dest, char *Src, int Len) /*把Src串中前Len个字符拷贝到Dest中*/
7)、memset(void *Buff, int Data, int Len) /*把用Data填充Buff前Len个存储单元*/
12.5、编写一个C语言程序,用TCC/BCC命令生成汇编语言程序,分析C语言语句和汇编语言语句之间的实现关系。
12.6、编写一个C语言程序,求出2~100之内的所有素数(大于1,且只能被1和自身整除的数,称为素数),然后把它改写汇编语言程序,并比较二者代码的。
12.7、编写一个C语言程序,求出2~999之内的所有能被9整除,且含有5的数,然后把它改写汇编语言程序。
12.8、用汇编语言编写一个过程Display(Data),其功能为在当前光标处显示无符号整数Data,然后编写C语言程序调用之,以达到显示数据的作用。