ch11汇编语言与高级语言溷合程序设计.docx

上传人:b****6 文档编号:8430581 上传时间:2023-01-31 格式:DOCX 页数:12 大小:23.50KB
下载 相关 举报
ch11汇编语言与高级语言溷合程序设计.docx_第1页
第1页 / 共12页
ch11汇编语言与高级语言溷合程序设计.docx_第2页
第2页 / 共12页
ch11汇编语言与高级语言溷合程序设计.docx_第3页
第3页 / 共12页
ch11汇编语言与高级语言溷合程序设计.docx_第4页
第4页 / 共12页
ch11汇编语言与高级语言溷合程序设计.docx_第5页
第5页 / 共12页
点击查看更多>>
下载资源
资源描述

ch11汇编语言与高级语言溷合程序设计.docx

《ch11汇编语言与高级语言溷合程序设计.docx》由会员分享,可在线阅读,更多相关《ch11汇编语言与高级语言溷合程序设计.docx(12页珍藏版)》请在冰豆网上搜索。

ch11汇编语言与高级语言溷合程序设计.docx

ch11汇编语言与高级语言溷合程序设计

汇编语言与高级语言混合程序设计

本章对汇编语言与C语言混合程序进行设计。

主要介绍汇编语言和C语言的混合编程和调用方法。

7.1.汇编语言和高级语言

程序设计语言按照功能的不同可以分为3种:

机器语言、汇编语言和高级语言。

把机器语言指令以助记符来表示,就成汇编语言指令,汇编语言指令必须用工具软件翻译(汇编过程)成机器语言指令才能执行,其它高级语言也必须翻译(编译或解释)为机器语言才能执行。

高级语言是独立于机器的通用语言,不依赖于特定计算机的硬件结构和指令系统。

用高级语言写的源程序,可以在不同的计算机上重新编译(或解释)后运行,而得到相同运行结果。

高级语言包括C/C++、Basic、Pascal、Java等。

C/C++语言是一种应用广泛,并且实现灵活的一种计算机编程语言,用C/C++语言编出来的程序,可以在很多平台上运行,可移植性强。

C/C++不仅具有良好的高级语言特征,而且还具有一些低级语言的特点,如:

寄存器变量、位操作等。

所以,C/C++语言的程序与汇编语言程序之间能很平滑地衔接。

另外,目前主要的C/C++语言程序开发环境,如:

TurboC/C++、BorlandC/C++等,也都提供了很好的混合编程手段。

计算机操作系统等大型软件,一般都采用C/C++语言和汇编语言混合编程来完成。

虽然除了C/C++以外的其它高级也可以与汇编语言进行混合编程,但下面我们重点讲述C/C++与汇编语言的混合编程问题。

7.2.为什么要混合编程?

有时为了提高关键代码的执行效率,可以采用汇编语言来实现低层关键代码的功能,如数据采集、定时、调度等,而用C/C++等高级语言来实现高层如数据处理等功能。

通过C/C++与汇编语言之间的相互调用,实现汇编语言和高级语言的混合编程,发挥各自语言的优势。

这种混合编程的方法优点是提高了关键程序段的执行效率,而其缺点是程序移植性变差。

在操作系统软件中,如linux,、uC/OSII等就采用了C/C++与汇编语言的混合编程。

操作系统中,一般是低层的硬件相关的代码(任务切换调度、硬件中断、定时中断等)都采用汇编语言实现,这部分与具体硬件相关,也叫平台相关的代码;而高层的服务功能采用C/C++语言来实现,这部分与具体硬件无关,也叫平台无关的代码。

平台相关的汇编语言代码部分是操作系统移植到不同处理器时需要修改的部分。

7.3.如何混合编程?

为了实现一个任务,我们把一个任务按功能划分为很多模块,每个模块以函数或子程序的形式存在,根据每个模块的特点选用汇编语言或C/C++语言,我们把它们叫做汇编语言模块或C/C++语言模块。

这两种不同语言编写的模块,都会编译成目标文件(.obj),最后将多个目标文件连接在一起,形成一个完整的可执行文件(.exe)。

为了完成一个特定任务的多个模块,模块之间不是孤立的。

一种情况是,模块中函数之间可能存在着调用关系,也就是,可能存在着汇编语言模块中的函数调用另一个C/C++模块中的函数的情况;也可能存在着C/C++模块中的函数调用汇编语言编写的模块函数的可能。

另一种情况是,也可能存在着一个模块引用另一个模块公共变量的情况。

为了不同模块之间调用和引用的正确,要对模块之间的调用和引用进行约定。

汇编语言与C/C++语言混合编程,需要考虑四个方面的约定(规范):

(1)存储模式的约定---如何为模块分配内存的约定;

(2)函数名称的约定---如何声明函数名称约定;

(3)函数调用约定---汇编语言与C语言之间的参数如何传递的约定;

(4)编译方法。

有两种混合编程方式,一种是嵌入汇编指令的格式;另一种是独立汇编模块格式。

7.3.1存储模式的约定

存储模式用于处理程序、数据、堆栈在主存中的分配和存取,决定代码和数据的默认指针类型,例如段寄存器CS、DS、SS、ES的设置就与所采用的存储模式有关。

存储模式在C语言中也称为编译模式或主存模式。

TurboC提供了六种存储模式,分别是:

微型模式(tiny)、小型模式(small)、紧凑模式(compact)、中型模式(medium)、大型模式(large)和巨型模式(huge)。

混合编程时必须保持汇编语言和C语言的存储模式一致。

在汇编语言中使用伪指令.model***,表示在汇编语言中采用的存储模式,其中***表示上述6种模式中的一种。

若C采用小模式,则在汇编语言中要使用.modelsmall。

7.3.2函数调用约定

要想实现高级语言与汇编语言的混合编程,必须知道这两种语言之间的调用约定。

当高级语言函数被编译成机器码时,有一个问题就必须解决:

因为CPU没有办法知道一个函数调用需要多少个、什么样的参数。

即计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用与者和函数本身来协调。

为此,计算机提供了一种被称为栈的数据结构来支持参数传递。

函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。

函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。

在参数传递中,必须明确说明以下三个方面:

1)当参数个数多于一个时,按照什么顺序把参数压入堆栈;

2)函数调用后,由谁来把堆栈恢复原样;

3)调用函数返回值放在什么地方。

在高级语言中,通过函数调用约定来说明这个问题。

如果定义的约定和使用的约定不一致,则将导致堆栈被破坏,导致严重问题。

常见的调用约定有如下5种:

1)stdcall---pascal调用约定;

2)cdecl---C调用约定;

3)fastcall---与stdcall类似,速度更快;

4)thiscall---C++类成员函数缺省的调用约定;

5)nakedcall---很少见的调用约定,不建议使用。

1.stdcall调用约定

stdcall很多时候被称为pascal调用约定,因为pascal是早期很常见的一种教学用计算机程序设计语言,其语法严谨,使用的函数调用约定是stdcall。

在MicrosoftC++系列的C/C++编译器中,常常用PASCAL宏来声明这个调用约定,类似的宏还有WINAPI和CALLBACK。

stdcall是Win32API函数的调用约定。

stdcall调用约定声明的语法为:

int__stdcallfunction(inta,intb)/*pascal调用约定*/

stdcall的调用约定:

1)参数从右向左压入堆栈;

2)被调用函数自动清理堆栈;

3)函数名自动加前导的下划线,后面紧跟一个@符号,其后紧跟着参数的尺寸。

以上述这个函数为例,参数b首先被压栈,然后是参数a,函数调用function(1,2)调用处翻译成汇编语言将变成:

push2;第二个参数入栈

push1;第一个参数入栈

callfunction;调用函数,自动把cs:

eip入栈

而对于函数自身开始处,则翻译为:

pushebp;保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针,可以在函数退出时恢复

movebp,esp;保存堆栈指针

moveax,[ebp+8H];堆栈中ebp指向位置之前依次保存有ebp,cs:

eip,a,b,ebp+8指向a

addeax,[ebp+0CH];堆栈中ebp+0CH处保存了b,实现加法功能

movesp,ebp;恢复esp

popebp

ret8;自动恢复堆栈,返回值在eax中。

在编译时,函数名被翻译成_function@8。

注意不同编译器会插入自己的汇编代码以提供编译的通用性,但是大体代码如此。

其中在函数开始处保留esp到ebp中,在函数结束恢复是编译器常用的方法。

从函数调用看,2和1依次被push进堆栈,而在函数中又通过相对于ebp(即刚进函数时的堆栈指针)的偏移量存取参数。

函数结束后,ret8表示清理8个字节的堆栈,函数自己恢复了堆栈。

__stdcall是在windows程序设计中出现的最多的调用规则,所有的不可变参数的API调用都使用这个规则。

2.cdecl调用约定

cdecl调用约定又称为C调用约定,是C语言缺省的调用约定,它的定义语法是:

intfunction(inta,intb)/*不加修饰就是C调用约定*/

int__cdeclfunction(inta,intb)/*明确指出C调用约定*/

cdecl调用约定:

1)参数从右向左压入堆栈;

2)函数本身不清理堆栈,调用者负责清理堆栈;

3)函数名自动加前导的下划线_function。

cdecl调用约定的参数压栈顺序是和stdcall是一样的,参数首先由右向左压入堆栈。

所不同的是,函数本身不清理堆栈,调用者负责清理堆栈。

由于这种变化,C调用约定允许函数的参数的个数是不固定的,这也是C语言的一大特色。

对于前面的function函数,使用cdecl后的汇编码变成:

调用处

push2;第二个参数入栈

push1;第一个参数入栈

callfunction;调用函数,自动把cs:

eip入栈

addesp,8;注意:

由调用者恢复堆栈

被调用函数_function处

pushebp;保存ebp寄存器,该寄存器将用来保存堆栈的栈顶指针

movebp,esp;保存堆栈指针

moveax,[ebp+8H];堆栈中ebp指向位置之前依次保存有ebp,cs:

eip,a,b,ebp+8指向a

addeax,[ebp+0CH];堆栈中ebp+12处保存了b

movesp,ebp;恢复esp

popebp

ret;注意,这里没有完全恢复堆栈,返回值在eax中。

由于参数按照从右向左顺序压栈,因此最开始的参数在最接近栈顶的位置,因此当采用不定个数参数时,第一个参数在栈中的位置肯定能知道,只要不定的参数个数能够根据第一个后者后续的明确的参数确定下来,就可以使用不定参数。

cdecl调用约定最大好处在于由于是调用者清理栈,它可以处理可变参数,缺点则在于它增加了程序的大小,因为在每个调用返回的时候,需要多执行一条清理栈的指令。

3.fastcall调用约定

fastcall调用约定和stdcall类似,

fastcall调用约定:

1)函数的第一个和第二个DWORD参数(或者尺寸更小的)通过ECX和EDX传递,其他参数通过从右向左的顺序压栈;

2)被调用函数自动清理堆栈;

3)函数名修改规则同stdcall。

其声明语法为:

int__fastcallfunction(inta,intb)

__fastcall在windows内核设计中被广泛的使用,由于两个参数由寄存器直接传递,采用这种规则的函数效率要比以上两种规则高。

4.thiscall调用约定

thiscall是C++成员函数的默认调用约定,编译期间,这种调用会根据函数是否支持可变参数表来决定采用什么方式清理堆栈。

如果成员函数不支持可变参数,那么它就是用参数入栈,ECX保存this指针的方式进行调用,如果成员函数支持可变参数,那么它的调用和__cdecl类似,唯一不同的是将this指针最后压入栈中进行传递。

5.调用函数返回值

以上4种调用约定中,返回值保存方式是一样的。

被调用函数的返回值,按下列规则传递给调用者:

1)如果返回值小于或等于16位,则将其存放在AX寄存器中;

2)如果返回值是32位,则存放在DX,AX寄存器对中,其中DX存储高16位,AX存储低16位;

3)如果返回值大于32位,则存放在静态变量存储区;AX寄存器存放指向这个存储区的偏移地址;对于32位far指针,则还利用DX存放段地址。

由此可见,汇编语言子程序向C程序返回处理结果时,是通过AX和DX完成的;但对于不同长度的返回数据,使用寄存器的情况也不同。

当返回值为char、short、int类型,仅需要使用寄存器AX;当返回值类型为long时,低16位在AX中,高16位在DX中;而当返回值类型为float、double时,AX传送存储地址的偏移量;如果是far指针,DX传送段地址,偏移地址仍然在AX中。

7.4.嵌入汇编指令格式

嵌入汇编方式把插入的汇编语言语句作为C语言的组成部分,不使用完全独立的汇编模块,所以比调用汇编子程序更方便、快捷,并且在大存储模式、小存储模式下都能正常编译通过。

C语言程序中含有嵌入式汇编语言语句时,C编译器首先将C代码的源程序(.c)编译成汇编语言源文件(.asm),然后激活汇编程序TurboAssembler将产生的汇编语言源文件编译成目标文件(.obj),最后激活Tlink将目标文件链接成可执行文件(.exe)。

若要在C语言源程序中嵌入一条汇编语句,则用保留了asm声明,格式如下:

asmmovax,bx

若要在C语言源程序中嵌入一组汇编语句,则需要用括号'{'和'}'把它们括起来。

asm{

  movax,data1

movbx,data2

movdata1,bx

movdata2,ax  /*实现整型变量data1和data2之值的交换*/

}

例1:

将字符串中的小写字母转变为大写字母显示

/*upcase.c*/

#include

voidupper(char*dest,char*src)

{asmmovsi,src/*dest和src是地址指针*/

asmmovdi,dest

asmcld

loop:

asmlodsb/*C语言定义的标号*/

asmcmpal,'a'

asmjbcopy/*转移到copy标号*/

asmcmpal,'z'

asmjacopy/*不是’a’到’z’之间的字符原样复制*/

asmsubal,20h/*是小写字母转换成大写字母*/

copy:

asmstosb

asmandal,al/*C语言中,字符串用NULL(0)结尾*/

asmjnzloop

}

main()/*主程序*/

{

charstr[]="ThisStartedOutAsLowercase!

";

charchr[100];

upper(chr,str);/*调用汇编函数*/

printf("Originstring:

\n%s\n",str);

printf("UppercaseString:

\n%s\n",chr);

}

编辑完成后,在命令行输入如下编译命令,选项-I和-L分别指定头文件和库函数的所在目录:

TCC-B-Iinclude-Llibupcase.c

生成可执行文件upcase.exe,程序运行后输出的结果将是:

Originstring:

ThisStartedOutAsLowercase!

UppercaseString:

THISSTARTEDOUTASLOWERCASE!

7.5.独立汇编模块格式

模块划分:

有的模块用C语言实现,有的模块用汇编语言实现。

每个模块源程序(C源程序或汇编源程序)独立编译为.obj目标文件,之后用链接工具把所有的obj整合到一个exe可执行文件。

模块之间的调用有两种情况,一种是C调用汇编;另一种是汇编调用C。

对于C语言模块调用汇编语言模块中函数(或使用变量)的情况,声明如下:

1)在C语言模块中,用extern说明要调用汇编模块中的函数(或使用变量)。

其格式如下:

extern  返回值类型 函数名(参数类型表);

extern  变量类型 变量名;

2)在汇编语言模块中,用PUBLIC声明被C语言模块调用的函数(或使用变量)。

其格式如下:

PUBLIC_程序标识符(函数名或变量名)

对于汇编语言模块调用C语言模块中函数(或使用变量)的情况,声明如下:

1)在汇编语言模块中,用extern说明要调用C语言模块中的函数(或使用的变量)。

其格式如下:

EXTRN  _函数名:

类型(NEAR,FAR)

EXTRN  _变量名:

类型 (BYTE,WORD,DWORD)

2)在C语言模块中,用PUBLIC声明被汇编语言模块调用的函数(或使用的变量)。

其格式如下:

函数名/*在C语言模块中,要求全局可见*/

变量名/*在C语言模块中,要求全局可见*/

          

例2:

C语言程序模块调用汇编语言子程序模块,显示一段信息

/*C语言程序:

main.c*/

externvoiddisplay(void);/*说明display是外部函数*/

main()

{   display();

}

;汇编语言子程序模块:

display.asm

          .modelsmall,c;存储模式,采用小型存储模式和C语言类型

          .data;数据段

msgdb’Hello,CandAssembly!

’,’$’

          .code;代码段

          PUBLIC_display;外部C模块可以使用本过程

_displayproc;采用了一致的命名约定

          movah,9;小型模式只有一个数据段,所以不必设置DS

          movdx,offsetmsg;寄存器AX和DX无须保护

          int21h

          ret

_displayendp

          end

例3:

uC/OSII中汇编语言部分。

注意本例中函数和变量的定义格式及程序中的使用。

;1.全局和外部引用定义部分

;1.1本汇编模块中实现的4个函数(C模块会调用下边四个汇编实现的函数)

PUBLIC_OSTickISR;定时中断汇编函数

PUBLIC_OSStartHighRdy;开始执行最高级任务汇编函数

PUBLIC_OSCtxSw;任务切换汇编函数

PUBLIC_OSIntCtxSw;中断中任务切换汇编函数

;1.2C模块中声明和实现的函数和变量(本汇编模块中要引用它们)

EXTRN_OSIntExit:

FAR;函数3个

EXTRN_OSTimeTick:

FAR

EXTRN_OSTaskSwHook:

FAR

EXTRN_OSIntNesting:

BYTE;变量7个

EXTRN_OSTickDOSCtr:

BYTE

EXTRN_OSPrioHighRdy:

BYTE

EXTRN_OSPrioCur:

BYTE

EXTRN_OSRunning:

BYTE

EXTRN_OSTCBCur:

DWORD

EXTRN_OSTCBHighRdy:

DWORD

.MODELLARGE;存储模式

.CODE;代码开始

;2.1开始执行最高级任务汇编函数实现部分

_OSStartHighRdyPROCFAR

_OSStartHighRdyENDP

;2.2多任务切换汇编函数实现部分

_OSCtxSwPROCFAR

PUSHA;保存当前旧任务环境到堆栈中

PUSHES

PUSHDS

MOVAX,SEG_OSTCBCur;引用C中声明的变量

MOVDS,AX;

LESBX,DWORDPTRDS:

_OSTCBCur;保存当前旧任务的SS:

SP到当前任务的TCB中,OSTCBCur->OSTCBStkPtr=SS:

SP

MOVES:

[BX+2],SS

MOVES:

[BX+0],SP

CALLFARPTR_OSTaskSwHook;调用C中函数

MOVAX,WORDPTRDS:

_OSTCBHighRdy+2;_OSTCBCur=_OSTCBHighRdy

MOVDX,WORDPTRDS:

_OSTCBHighRdy

MOVWORDPTRDS:

_OSTCBCur+2,AX

MOVWORDPTRDS:

_OSTCBCur,DX

MOVAL,BYTEPTRDS:

_OSPrioHighRdy

MOVBYTEPTRDS:

_OSPrioCur,AL

LESBX,DWORDPTRDS:

_OSTCBHighRdy;取出新任务的堆栈指针,SS:

SP=OSTCBHighRdy->OSTCBStkPtr

MOVSS,ES:

[BX+2]

MOVSP,ES:

[BX]

POPDS;弹出新任务的环境

POPES

POPA

IRET;跳到新任务执行

_OSCtxSwENDP

其它汇编函数实现略,参考uC/OSII源程序中OS_CPU_A.ASM文件。

例4.C模块与汇编模块混合调用

/*****main.c模块*****/

#include

externintmaxx(int,int);/*引用汇编实现的函数*/

intmain(intargc,char*argv[]){

intx;

{_asmmovah,1/*嵌入汇编指令,等待键盘输入*/

_asmint21h

}

x=maxx(8,9);/*调用汇编函数maxx()*/

printf("%d",x);/*显示返回值*/

return1;

}

;*****sub.asm模块*****;

.MODELLARGE;存储模式为large

.CODE

public_maxx;外部可见函数_maxx

_maxxprocfar;_maxx函数实现

pushbp

movbp,sp

movax,[bp+6]

cmpax,[bp+8]

jgeexit

movax,[bp+8]

exit:

popbp

ret

_maxxendp

End

7.6.编译、汇编和连接的方法

步骤一,用MASM汇编ASM语言源程序,生成汇编语言目标文件(.OBJ)。

步骤二,在TURBOC集成环境下建立一个工程文件,该文件包括需要编译连接的C语言源程序和汇编语言目标文件名(.OBJ)。

步骤三,在TURBOC集成环境下,按F9功能键,制作出工程,生成可执行文件(.EXE)。

 

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1