扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
对于参数个数可变的子程序,当参数不超过4个时,可以使用寄存器R0~R3来进行参数传递,当参数超过4个时,还可以使用数据栈来传递参数. 在参数传递时,将所有参数看做是存放在连续的内存单元中的字数据。然后,依次将各名字数据传送到寄存器R0,R1,R2,R3; 如果参数多于4个,将剩余的字数据传送到数据栈中,入栈的顺序与参数顺序相反,即最后一个字数据先入栈.
创新互联专注于企业网络营销推广、网站重做改版、罗庄网站定制设计、自适应品牌网站建设、H5高端网站建设、商城系统网站开发、集团公司官网建设、外贸营销网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为罗庄等各大城市提供网站开发制作服务。
汇编语言没有高级语言要占用较大的存储空间和较长的运行时间等缺点,它的运行速度快是高级语言所不能比拟的。可以说高级语言与汇编语言各有千秋。有时我们采用高级语言编程速度达不到要求,全部采用汇编语言编程工作量又大,此时可以采用"混合"编程,彼此相互调用,进行参数传递,共享数据结构及数据信息,是一种有效的编程方法。这种方法可以发挥各种语言的优势和特点,充分利用现有的多种实用程序、库程序等使软件的开发周期大大缩短。
1 高级语言与汇编语言的接口需要解决的问题
1、需要说明和建立调用者与被调用者间的关系被调用的过程或函数应预先说明为外部类型,如汇编子程序,应用PUBLIC说明其可被外部模块引用;调用程序则应预先说明要引用的外部模块名。
2、参数传递问题在汇编子程序之间通常采用寄存器作为参数传递的工具,汇编语言与高级语言程序间的参数传递,一般采用堆栈来传递,即调用程序将参数依次压入堆栈中,当被转调用程序后,再从堆栈中依次弹出参数作为操作数使用。为此,必须了解各种语言的堆栈结构、生成方式和入栈方式等。BASIC、FORTRAN、PASCAL等语言其参数进栈顺序是与参数在参数表中出现的顺序相同,即从右到左;而C语言则相反。
2 C语言与汇编语言的接口
2.1 C语言调用汇编子程序
●在C程序中使用关键字"ExTERn"对函数作显式说明。
●参数传递顺序是按其在参数表中出现的顺序的反序被压入堆栈中,即第一个参数最后进入堆栈,它在栈中的地址最低。
●对不同的存储模式(极小、小、紧凑、中、大和巨)要选用不同的汇编语言格式,如C程序为小模式,汇编用近过程,C程序为大模式,汇编用远过程。
●汇编程序取C的参数。远过程返回地址占四个字节,BP压入占二字节,所以第一个参数在BP+6所指向的单元。对于近过程第一个参数在BP+4所指向的单元。
●汇编程序中寄存器的保护。TuRboC允许子过程使用SI和DI存放局部变量,当寄存器变量多于二个时,多余部分会自动转到堆栈中存储。因此,汇编过程的格式为:
PUSH BP
MOV BP,SP
PUSH DI
PUSH SI�
……….
语 句�
………….
POP SI
POP DI
POP BP
RET
●返回值。每种C数据类型都有一个标准的返回位置,一般在AX中(极小、小、中模式),DX:AX(紧凑、大、巨模式),如:chaR,unSignEdchaR,Enum,ShoRTinT等,返回值位置为AX,且返回数据必须放置在RET指令之前。汇编子程序要定义为远过程,并用PUBLIC伪指令把过程名定义为公共。例
:#includE〃STdio.h〃
#includE〃STdlib.h〃
chaRMESSagE[]=〃MESSagE〃;
inTfaRREVSTR(chaRfaR STR);
Voidmain(Void)
{ REVSTR((chaRfaR )MESSagE);
printf(〃%S〃,MESSagE);
}
REVSTR.asm
…
PUBLIC-REVSTR-
REVSTR PROC
PUSH BP
MOVBP,SP
PUSH DS
PUSH SI
PUSH DI
…
POP SI
POP AX
MOV DS,AX
POP BP
MOV AX,CX
RET
REVSTR ENDP
END
2.2 C语言嵌入汇编
在C程序中允许直接编写汇编语言代码,这称作嵌入汇编。C程序中嵌入汇编后可以无分号(C语言的语句以分号结束,汇编语句是C语言中唯一以换行结束的语句),以关键词ASM张一个嵌入汇编指令,如需多个ASM语句,可以将它们放在花括号内。如,
ASMMOVAX,DX/ 注释 /
ASM
PUSHAX}/ 注释 /
C语言允许嵌入四类汇编命令:一般指令、串指令、跳转指令、数据分配和定义指令,嵌入汇编比调用汇编子程序更方便、灵活、功能也更强。但嵌入汇编不是一个完整的汇编程序,所以许多错误不能马上检查出来。
3 结 束 语
采用两种或两种以上的编程语言组合编程,彼此相互调用,进行参数传递,是一种有效的程序设计方法。这种方法可以充分发挥各种语言的优势,充分利用现有的实用程序,是当前程序接口技术的一个重要研究和应用领域
c语言可以嵌套汇编:
按照TC2.0的帮助系统所以说的,在TC2.0下是可以用汇编的,方法是使用asm关键字:其格式是:
asm opcode operands ;newline,如同别的注释一样,之间的表示可选的;例如:
main()
{
char *c="hello,world/n/r$";
asm mov ah,9;asm mov dx,c;asm int 33;
printf("You sucessed!/n");
}
或者是:
main()
{
char *c="hello,world/n/r$";
asm mov ah,9
asm mov dx,c
asm int 33
printf("You sucessed!");
}
两种格式其实是一种.如果你用的是第一种的样式,记住:
每一句汇编语句都要以asm开头,如果一行内有多个句子,
那么千万不要忘记在两个句子之间的这个semicolon(分号),
但是最后一句汇编后面(如果后面没有其它的语句)的分号可有可无,象第一个例子中的
asm int 33;后面的分号就可以不要,因为它的后面没有其它
的语句了.但如果是这样:
asm mov ah,9; asm mov dx,c;asm int 33; printf("You sucessed!");
那么asm int 33;后面的分号便还是留下好,以免出现编译错误!
在这一点上颇象C语言.
还有一种格式是
asm{ assembly language statement},这种格式应该被普遍的欢迎.
它们的例子如下(其中的语句排列格式与上面两种相同):
asm{
mov ax,var1
add ax,var2
......
}
但是要注意这种格式TC2.0是不支持的!
只有后来的TC++3.0及后来的IDE支持!
工具的使用:
一旦你的C源文件里包括了这些好东西,则必须用TCC.EXE的COMMAND-LINE来编译,具体的命令参数TCC.EXE已经提供,这里不复阐述了.最简单的是:TCC C源文件名(使用这个方法,TCC会自动调用TASM.EXE和TLINK.EXE,并且能够使TLINK.EXE正确的找到需要的.obj和.lib文件,如果你单步编译的话,可能会碰到很多的问题,主要是TLINK.EXE它自己并不会去找.obj和.lib文件,你自己可以建一个.bat文件,如果要指定.lib文件的目录的话可以用/L参数,在文章的后面有一个例子).但大家要注意了,看一下你的TC目录下面到底是否有TASM.EXE文件,并在TURBOC.CFG(这个文件包括TCC.EXE运行期参数,这里面所有参数在运很期都将被自动TCC.EXE使用,例如:-IH:/TC/INCLUDE/
-LH:/TC/LIB/)文件中设置好一些参数,并确认TASM.EXE的版本号要2.0以上,以及是否能够向下兼容.但是在大多数的情况下TC的目录是没有TASM.EXE的,或是版本不正常.
如果你有TASM.EXE文件并且TURBOC.CFG文件也已经写好了,但是还要注意一个
问题:运行TCC.EXE时要在独立的DOS SHELL下面(不要害怕,这不是一个新东西,我的意思
是,不在诸如TC下的DOS SHELL下面运行,我曾经败在这个问题下,当我发现时直想揍电脑
一顿,还好没有,不然就没有这篇文件了.)
还有一句重要的话:TC2.0支持大部分8086指令(当然用法有一些约定,不过现在我并不打算
进行详细说明,因为那是一件很繁杂的事,以后有时间或许会写出来----如果大家需要的话).
如果说上面我所说的那些约定很繁杂的话,那么下面的方法该是多么简单啊!
让我们使用Borland为TC2.0内建的变量来进行伪汇编.
或许你还不知道在TC2.0中还有一些内建的pseudo寄存器(可以看作是register 型的变量,但是它们比register型的变量好用的多)
_AX,_AH,_AL,
_BX,_BH,_BL,
_CX,_CH,_CL,
_DX,_DH,_DL,
_DI,_SI,_SP,
_CS,_DS,_ES,_SS
注意这些寄存器的size,_AX,_BX,_CX,_DX,_CS,_DS,_ES,_SS,_SI,_DI,_SP等都是16位的寄存器相当于C语言的unsigned int类型,其余的都是8位的寄存器(相当于unsigned char)(TC怎么可能支持32位的寄存呢,所以EAX等是不能用的,FS,GS和IP寄存器都是无效的),还有就是在传递参数的时候千万不要忘记使用强制类型转换.
中断调用指令是:__int__(interrupt_#)(注意int的前辍和后辍都是两个underscores)
For example:
#includedos.h
unsigned int _stklen=0x200;
unsigned int _heaplen=0;
main()
{
_DX=(unsigned int)"Hello,world./r/n$";
_AX=0x900;
__int__(0x21);
}
dos.h它是包含__int__()内建中断调用语句的头文件,因此是不可
缺少的._stklen和_heaplen是定义运行期堆栈和堆大小的两个内部
引用变量(这是个我自己想的名词,意指如果这两个变量在源文件中
显式的声明了,那么编译程序会自会引用来构造编译时期的信息以产生
用户希望的目标文件,如果不显式的声明则编译程序自动确定).
这两个变量也有一些约定,如果_stklen不显式声明,_heaplen赋值为零
都表示栈和堆都是defult的.
最后在TC2.0中还有一个没有说明的标志位寄存器flags,它也是内建
pseudo寄存器是:_FLAGS,是一个16位寄存器.这些内建的寄存器都可以进行
运算,但是要注意它们所代表的类型(必要时进行类型转换);
看起来这是不是一种好的办法啊(而且使用这种方法只要用个一个dos.h头文件就好,
不需要用TCC编译,可以直接在TC20的IDE下编译).
TC2.0中也提供了一些简单好用的函数来实现对DOS功能的调用如:
int86(...),int86x(...)(但是这些方法实际仍然要调用函数,所以不如使用
伪寄存器,又因为要牵涉到union REGS结构的内存分配所以系统的开销是增大了,
而使用伪寄存器是最简洁的),端口通信函数如:inportb(...),inport(...),
outportb(...),outport(...),指针转换函数:FP_OFF,FP_SEG,MK_FP,这些函数在
帮助系统中都有,有用时大家可以查阅.
tlinkbat.bat的例子:
rem The lib environment variable is the directory of the .obj and .lib file
set lib=h:/tc/lib/
rem 这下面的句子中的c0s(C 零S)是一个.OBJ文件,是一个C程序的STARTUP文件
tlink %lib%c0s %1,%1,%1,/L%lib%emu.lib %lib%maths.lib %lib%cs.lib
set lib=
(使用时可将以rem开头的句子删除)
___________________________________________________
一些约定:
我们先说一下在TC20下写汇编(内联汇编--自己起的名字,大家可以想叫什么叫什么)时的编译器的编译原则:
1.所有在main()函数外的的汇编语言的语句都作为数据声明语句处理,也即在编译器编译时会将它放在数据段中,如:
asm string1 db "Hello",,,'world!',0ah,0xd,"$"
main()
{
asm mov dx,offset string1
asm mov ah,9
asm int 33
asm mov dx,offset string2
asm int 33
}
asm string2 db "the string can be declared after the main() function!$"
象这些样子在main()外面的汇编语言的数据定义语句(事实上不管是什么汇编语句,
只要是在main()之外,包括这个句子:asm mov ax,0x4c00),在编译后都放在数据段中,而C语言的数据声明语句仍按C的规则!
2.所有在main()函内的汇编语言的语句在编译后都放在代码段中,包括这个句子:
asm string2 db "the string can be declared after the main() function!$"
3.不要在以asm 开头的语句中使用C语言的关键字,这会导致编译阶段的错误
那么,根据这三条大家会得到什么样的结论呢?(先闭上眼想一想,你可能会由此变的
很赞赏自己,是的你应该这样相信自己是对的!)
让我们一起看一下这个结论:
1.根据编译原则1得到:不可以在main()外面写汇编命令语句(不要笑,正是与C语言相同才值得注意!),在任何地方都不要进行任何的段定义和宏定义(这是因为编译后的形式决定的,也即:在TC20下所有的汇编格式的语句只能是,直接性的数据定义和语句指令)!
2根据编译原则2得到:不可以在main()之内使用汇编的语句进行数据定义(同样不要笑,
大多数人在第一次在TC20下写汇编都会有这样的错误的)
3.如同类强制类型这样的事是不可以在以asm开头的汇编语句中使用的
好了,天即朗,气瞬清!这样一说,一个大体的框架就出来了!只要遵守这个原则写,就可避免很多莫名其妙的错误出现!
通俗的说:
汇编语句的数据定义放在main()外面,指令放在main()里面.
如果你没有更好的文档,那么记住我的这些话!
一些细节的问题:
在以asm开头的内联汇编语句中是不支持C的转义字符的,但是用C语言声明一个字符数组(含有转义字符的),然后用int 33 ah=9这功能时输出这个字符串时,其中的转义字符是有效的(这主要是因为编译后其内部表示形式不同造成的,自己想想会有答案的).
内联汇编支持C的一些如数值表示,字符串声明格式等,
如:一个十六进制的数据可以用两种方式表示:0xa 和0ah,字符串可以是这样:
"Hello,world!$"(如同C)也可以这样'Hello,world!$'(用汇编自己的方式).
象C一样你同样要注意赋值的类型,而且要比C更严格(汇编从来不自己动手做
如同类型转换啊这样事),所以一切的事完全要你自己做好!而且你不要企图以C的形式
做这件事,如这样的格式 asm mov dx,(unsigned)a(a是一个这样的东西,
char a[ ]="hello,world!";),而且这样句子也会导致错误:asm mov dx,word ptr a(逻辑错误),不过这不是在编译时的错误,而是运行期的错误(具体的原因自己想一想,象word label这样的东西的运算作用和会导致的后果),你可以这样用一个句子做"中间人"如int i=(unsigned)a;asm mov dx,i(也千万不要用asm mov dx,(unsigned)a 这样的句子.但是,告诉大家一个好消息,你可以用指针指向一个字符串,然后你会惊讶你竟然可以这样:
char *p="hello,world";asm mov dx,p,然后用int 33 ah=9的功能输出这个字符串而不会有错误(这也表现出指针的特点,它是一个二字节的(TC20下)变量,含有的是一个地址,这与其指向的变量的类型是毫无关系的).
内汇汇编语句不支持-这个运算符.还有标号的问题,在最后的例子中你会年看到一些特别之处!
上面所说的只是很细小并微少的一些事(也是很常遇到的),尚有很多的细节要说,但由于本人时间有限不能一一列举,如C的结构在内联汇编的应用等大家可以按照其运行机理去想想一下用法;另外,由于这只是一件学习的事,所以还是大家自己学(找一下有关文档,当然现在已经没有什么比较完整的了),情况会好的多,我在对内联汇编的学习过程中领会到了不少的东西,例如编译原理方面的知识,以及如何做会使代码更高效,占空间最少等的方法.最后向大家推荐一种方法,在利用TCC的-S开关可以生成C源文件的汇编代码
(或许很多的人都用过)是很好的学习材料!祝大家学有所成!
Cstarter
02-11-17
/* 由于个人的时间和能力有限,难免有错误和不详细的地方,请大家见谅!
My Email:wxe85@sina.com Cstarter1985@hotmail.com QQ:170594633 */
一些例子:
下面这个例子是对沈美明 温冬婵的
IBM-PC 汇编语言程序设计清华版第十一章程序的改写
可直接在命令行上键入 tcc filename 就可以,当然你要有TASM.EXE
/*
asm mus_frep dw 330,294,262,294,3 dup(330)
asm dw 3 dup(294),330,392,392
asm dw 330,294,262,294,4 dup(330)
asm dw 294,294,330,294,262,-1
asm mus_time dw 6 dup(25),50
asm dw 2 dup (25,25,50)
asm dw 12 dup(25),100
*/
asm mus_frep dw 330,392,330,294,330,392,330,294,330
asm dw 330,392,330,294,262,294,330,392,294
asm dw 262,262,220,196,196,220,262,294,330,262
asm dw -1
asm mus_time dw 3 dup (50),25,25,50,25,25,100
asm dw 2 dup (50,50,25,25),100
asm dw 3 dup (50,25,25),100
main()
{
asm jmp start
/*设置发声的频率,这一段在沈美明 温冬婵的
IBM-PC 汇编语言程序设计清华版第十一章有详细的说明 */
sound:
asm mov al,0b6h
asm out 43h,al
asm mov dx,12h
asm mov ax,533h*896
asm div di
asm out 42h, al
asm mov al,ah
/* 这个延时是用来防止两次IO操作的最后一次操作的错误,
因为CPU比总线的速度快很多,所以 要延时等待第一次操作完成后再进行第二次操作*/
asm mov cx,1000
delay:
asm loop delay
asm out 42h,al
asm in al,61h
asm mov ah,al
asm or al,3
asm out 61h,al
/* 使用中断15H功能86H延时CX:DX=微秒数*/
asm mov ax,2710h
asm mul bx
asm mov cx,dx
asm mov dx,ax
asm mov ah,86h
asm int 15h /*可用__int__(0x15);代替*/
asm mov al,ah
asm out 61h,al
asm jmp add_count
/*------------------*/
start:
asm mov si,offset mus_frep
asm lea bp,mus_time
frep:
asm mov di,[si]
asm cmp di,-1
asm je end_mus
asm mov bx,[bp]
asm jmp sound
add_count: /*标号不能用汇编语言写*/
asm add si,2
asm add bp,2
asm jmp frep
end_mus:;
}
对于上面的程序大家可用伪寄存器的方法写一个,要容易的多!
/*一个发声程序!(引自PC技术内幕电力版--这个版不好,不如清华版的)*/
#include"dos.h"
main()
{
static union REGS ourregs;
outportb(0x43,0xb6);
outportb(0x42,0xee);
outportb(0x42,0);
outportb(0x61,(inportb(0x61)|0x03));
ourregs.h.ah=0x86;
ourregs.x.cx=0x001e;
ourregs.x.dx=0x8480;
int86(0x15,ourregs,ourregs);
outportb(0x61,(inportb(0x61)0xfc));
}
一、 参数传递的基本规则(ATPCS(ARM—Thumb Procedure Call Standard))
1、 参数传递
二、汇编程序、C程序相互调用举例
1、 C程序调用汇编程序
汇编程序的设计要遵守ATPCS(ARM—Thumb Procedure Call Standard),保证程序调用时参数的正确传递。在汇编程序中使用EXPORT 伪操作声明本程序,使得本程序可以被别的程序调用。在C程序使用extern声明该汇编程序。
下面是一个C程序调用汇编程序的例子。其中汇编程序strcopy实现字符串复制功能,C程序调用strcopy完成字符串复制的工作。
//C程序
#include stdio.h
extern void strcopy(char *d, const char *s);
int main( )
{
const char *srcstr=”First string-source”;
char dststr[ ]=”Second string-destination”;
printf(“Before copying:\n”);
printf(“%s\n %s\n”, srcstr,dststr);
strcopy(dststr,srcstr);
printf(“After copying:\n”);
printf(“%s\n %s\n “,srcstr,dststr);
while(1) ;
}
;汇编程序
AREA Scopy, CODE, READONLY
EXPORT strcopy
Strcopy
LDRB R2, [R1], #1
STRB R2, [R0], #1
CMPR2,#0
BNE Strcopy
MOV PC, LR
END
2、 汇编程序调用C程序
汇编程序的设计要遵守ATPCS,保证程序调用时参数的正确传递。在汇编程序中使用IMPORT伪操作声明将要调用的C程序。下面是一个汇编程序调用C程序的例子。其中在汇编程序中设置好各参数的值。本例中有6个参数,分别使用寄存器R0存放第1个参数,
R1存放第2个参数, R2存放第3个参数, R3存放第4个参数, 第5个、第6个参数利用数据栈传送。由于利用数据栈传递参数,在程序调用结束后要调整数据栈指针。
//C程序g( )返回6个参数的和
int g( int a, int b, int c, int d, int e, int f )
{
printf(“e=%d\n”, e);
printf(“f=%d\n”, f);
return (a+b+c+d+e+f);
}
; 汇编程序调用C程序 g( ) 计算6个整数 i, 2*i, 3*i, 4*i, 5*i, 6*i的和
EXPORT f
AREA f ,CODE, READONLY
IMPORT g
MOV R0, #1
ADD R1, R0, R0
ADD R2, R1, R0
ADD R3, R2, R0
ADD R4, R3, R0
ADD R5, R4, R0
STR R4, [SP, #-4]!
STR R5, [SP, #-4]!
BL g
ADD SP, SP, #4
ADD SP, SP, #4
STOP B STOP
END
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流