![]() |
#2
zklhp2011-07-15 21:01
考虑到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语言程序调用之,以达到显示数据的作用。 |
RT啊