JMP NEXT ;继续输入下一字符 MOV AH,4CH INT 21H CODE ENDS
END START (2) 中断方式
CODE SEGMENT
ASSUME CS:CODE
START:MOV AH,1 ;输入字符 INT 21H
MOV DL,AL
MOV AH,2 ;显示字符 INT 21H
MOV AH,5 ;打印字符 INT 21H
JMP START ;下一字符
EXIT:MOV AX,4C00H
INT 21H
CODE ENDS
END START
第9章 C语言与汇编语言混合编程
1.什么是混合编程,汇编语言与C语言在混合编程时应注意什么问题? 答:将汇编语言代码嵌入到C语言中进行程序设计就是混合编程。 应注意的问题:
(1) 嵌入式汇编程序的指令格式 (2) 嵌入式汇编程序的编译和连接
(3) 汇编指令和C语言间对函数、变量及各中参数的共用问题,即接口问题 (4) C语言调用汇编模块的编程规则及变异连接方法
2.汇编语言与C语言混合编程有哪两种方式,各自的特点是什么? 答:嵌入式汇编方式:特点是在C语言程序中直接嵌入汇编语言指令。
模块调用方式:特点是C语言和汇编语言分别编写独立的模块,分别产生目标代码,然后连接生成一个完整的可执行文件。 3.说明在C语言环境下嵌入汇编语言指令格式。
答:⑴逐条嵌入格式:嵌入的各汇编指令代码前面都要加上“asm”关键字, 每行以分号或换行符结束。⑵成组嵌入格式:嵌入的各汇编指令代码放在同一个asm代码块中,代码块从“{”开始,到“}”结束,由“asm”关键字标识。
4.说明在嵌入式汇编程序中访问C程序变量的一般方法。 答:直接使用该变量名即可。
5.在Visual C环境下利用嵌入式汇编指令,完成对两个C变量的求和,结果由C程序显示。 答: main() {
int sum; int var1=5; int var2=6; __asm{
mov eax,var1 add eax,var2 mov sum,eax }
printf(“%d”,sum); }
6.在Visual C程序中输入两个整数,然后调用汇编子程序对两个数求差,结果在主程序中显示。
答: main() {
int result,var1,var2;
scanf(“%d,%d\\n”,&var1,&var2); __asm{
mov eax,var1 sub eax,var2 mov result,eax }
printf(“%d”,result); }
7.在Visual C程序中编写一个嵌入式汇编排序函数,C语言主程序提供待排序的数据并显示排序后的结果。 答:用起泡方法从大到小排序
main() {
int a[5]={2,-8,9,3,6}; int n = 5; __asm{
lea ebx,a mov ecx,n dec ecx loop1:mov edx,ecx mov esi,0
loop2:mov eax,[ebx+esi] cmp eax,[ebx+esi+2] jge next
xchg eax,[ebx+esi+2] mov [ebx+esi],eax next:add si,2 loop loop2 mov ecx,edx
loop loop1 }
for(int i=0;i++;i<5)
printf(“%d\\n”,a[i]); }
8.用嵌入汇编指令编写一个字符转换函数,实现将C语言主程序中的一个字符串内容的所有小写字母转换位大写字母。转换前后的字符串内容由C语言主程序打印显示。 答:
main() {
char str[5]={ 'h','e','l','e','o'}; int n = 5; int i=0;
__asm lea ebx,str __asm mov esi,0 for(i=0;i printf(\ __asm{ mov eax,[ebx+esi] cmp eax,61h jb next cmp eax,7ah ja next sub eax,20h mov [ebx+esi],eax next:inc esi } } printf(\ for(int i=0;i++;i<5) printf(\ } 9.在嵌入式汇编指令中调用一个C函数。 答:TURBO C++3.0下的程序如下: void fun() { printf(\ retrun; } main() { void (*p)(); p=fun; asm call p } 10.进行32位混合编程时,如何编写Visual C主程序和汇编语言过程? 答:(1) Visual C主程序要采用3种调用规范之一进行汇编子程序的调用;汇编程序中可用.model伪指令说明汇编语言的调用规范。(2) 汇编子程序中的过程名及变量名应该用public进行说明。Visual c主程序中应将用到的汇编子程序的过程名和变量名说明为外部符号,并且不能在名字前加下划线。(3) 32位汇编时要用EAX、EBX等32位寄存器(4) Visual C主程序调用汇编子程序时,通过堆栈传递参数,顺序从右到左。汇编子程序的返回值小于4字节时放到EAX寄存器中;返回值在4~8字节之间,放在EDX:EAX寄存器中;返回值大于8字节,返回值得地址指针放在EAX中。 11.说明C程序调用外部汇编模块的具体方法,并总结参数传递和汇编模块返回值的接口约定。分析C程序调用汇编模块前后的堆栈变化情况。 答:(1) C程序调用外部汇编模块的具体方法:调用程序和被调用的汇编过程必须使用相同的存储模型;汇编子程序的过程名和变量名都应说明为public且过程名和变量名前加下划线;在C中应将在本程序中用到的汇编子程序的过程名和变量名说明为外部过程和变量,并且不能在名字前加下划线。(2) 参数传递和汇编模块返回值的接口约定:通过堆栈传递参数(从右到左);传值时直接写出实际参数,传址时在extern说明语句中将参数类型说明成指针类型,并在调用时给出参数地址。返回值通过寄存器传递,16位返回值用AX,32位返回值用DX:AX,返回值大于32位则放到静态变量存储区(near调用时该数据的首地址存放在AX中,far调用时该数据的首地址存放在DX:AX中)。(3) 调用时将参数从右向左依次压入堆栈,再将返回地址压入堆栈;返回后将内容依次弹出即可。 12.编写两个数求和的汇编模块,两个操作数通过堆栈传递,在C程序中输入两个整数,调用汇编模块求和,结果在主程序中打印显示。画图说明调用汇编模块前后堆栈变化情况。 答:C语言源程序: extern \ main() { int x,y,z; scanf(\ asm mov ax,x asm mov bx,y asm push bx asm push ax z=adds(); printf(\ } 汇编语言源程序: .MODEL SMALL PUBLIC _ADDS .CODE _ADDS PROC PUSH BP MOV BP,SP MOV AX,[BP+4] MOV BX,[BP+6] ADD AX,BX POP BP RET 4 _ADDS ENDP END 调用前堆栈情况如题图9.1。调用后堆栈情况如题图9.2。 SS→SS→┇SP→┇BP偏移地址XYSP→调用后堆栈 题图9.1 题图9.2 13.若一个C主程序MAIN要调用汇编子程序SUB1,试问:(1) MAIN模块中的什么指令告诉汇编程序SUB1是在外部定义的?(2) SUB1怎么知道MAIN模块要调用它? 答:(1) EXTERN指令(2) SUB1子程序要声明为PUBLIC类型且加下划线,表明可被外部程序调用 14.编制计算一个数组中能被3整除的数据之和的子程序,并利用此子程序编制求A、B、C 三个数组中能被3整除的数据之和的主程序,要求主程序和子程序分别进行编制。 答:C语言主程序: extern \ main() { int s=0; int a[5]={3,5,7,9,14}; int b[5]={4,6,15,24,8}; int c[5]={36,72,1,83,6}; s=sum(a,5)+sum(b,5)+sum(c,5); printf(\ } 汇编语言子程序: .MODEL SMALL PUBLIC _SUM .CODE _SUM PROC 调用前堆栈 PUSH BP MOV BP,SP MOV BX,[BP+4] MOV CX,[BP+6] MOV SI,0 MOV DI,0 NEXT:MOV AX,[BX+SI] MOV DL,3 DIV DL TEST AH,0FFH JNE REPEAT ADD DI,[BX+SI] REPEAT:ADD SI,2 LOOP NEXT MOV AX,DI POP BP RET _ADDS ENDP END