程序语言混编

更新时间:2024-04-06 14:35:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

汇编语言没有高级语言要占用较大的存储空间和较长的运行时间等缺点,它的运行速度快是高级语言所不能比拟的。可以说高级语言与汇编语言各有千秋。有时我们采用高级语言编程速度达不到要求,全部采用汇编语言编程工作量又大,此时可以采用\混合\编程,彼此相互调用,进行参数传递,共享数据结构及数据信息,是一种有效的编程方法。这种方法可以发挥各种语言的优势和特点,充分利用现有的多种实用程序、库程序等使软件的开发周期大大缩短。

1 高级语言与汇编语言的接口需要解决的问题

1、需要说明和建立调用者与被调用者间的关系被调用的过程或函数应预先说明为外部类型,如汇编子程序,应用PUBLIC说明其可被外部模块引用;调用程序则应预先说明要引用的外部模块名。

2、参数传递问题在汇编子程序之间通常采用寄存器作为参数传递的工具,汇编语言与高级语言程序间的参数传递,一般采用堆栈来传递,即调用程序将参数依次压入堆栈中,当被转调用程序后,再从堆栈中依次弹出参数作为操作数使用。为此,必须了解各种语言的堆栈结构、生成方式和入栈方式等。BASIC、FORTRAN、PASCAL等语言其参数进栈顺序是与参数在参数表中出现的顺序相同,即从右到左;而C语言则相反。 2 C语言与汇编语言的接口 2.1 C语言调用汇编子程序

●在C程序中使用关键字\对函数作显式说明。

●参数传递顺序是按其在参数表中出现的顺序的反序被压入堆栈中,即第一个参数最后进入堆栈,它在栈中的地址最低。

●对不同的存储模式(极小、小、紧凑、中、大和巨)要选用不同的汇编语言格式,如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 结 束 语

采用两种或两种以上的编程语言组合编程,彼此相互调用,进行参数传递,是一种有效的程序设计方法。这种方法可以充分发挥各种语言的优势,充分利用现有的实用程序,是当前程序接口技术的一个重要研究和应用领域

WIN-TC使用TC2内核

所以嵌入汇编得用TC2的语法:

方法是使用asm关键字:其格式是: asm opcode <;newline> 例如: main() {

char *c=\ asm mov ah,9

asm mov dx,c asm int 33

printf(\ }

/*************** C内嵌汇编示例 ***************/ #include \ #include \ int min(int v1,int v2) {asm mov ax,v1; asm cmp ax,v2; asm jle minexit; asm mov ax,v2; minexit: return(_AX); } main()

{printf(\ getch(); }

现以Microsoft 的quick c和MASM来加以说明:

一、直接插入法:一般只用于需插入的汇编语句比较简短的情况。 如显示一行字符的程序:

char const *message=\ main() { _asm { mov ah,9

mov dx, message /*注意offset伪操作C编译器不能识别*/ int 21h } }

二、C语言程序调用汇编语言过程法:C语言程序经编译后产生.OBJ文件,汇编程序经汇编后也产生.OBJ文件,然后由连接程序把他们连接起来而形成.EXE可执行文件。应注意以下几个问题: 1.控制传送问题:

既然汇编语言程序是C语言程序的子过程,在汇编语言程序中应把过程名作为外部符号来处理,所以应在程序中声明PUBLIC 过程名。C语言程序中把此过程名定义为外部符号(extern)。且汇编语言中的过程名前应加下划线。

2.参数传送问题:

高级语言通过堆栈把参数传送给汇编语言程序。 C语言规定参数入栈的次序一程序中见到它们的次序相反,或者说C语言按从右到左的次序使参数入栈。如函数:sub(x,y,z)先使z入栈,然后是y,最后是x.调用汇编语言过程时,同样要把返回点保存入栈。如果过程为FAR属性,则CS和IP将先后入栈;如果过程为NEAR,则只保存IP。 如:

.MODEL MEDIUM .CODE PUBLIC _SUB _SUB PROC FAR PUSH BP MOV BP, SP

MOV AX, [BP+6] ;[BP+2]为IP,[BP+4]为CS(远调用) MOV BX, [BP+8] ; [BP+6]为第一个参数,[BP+8]为第二个参数... . . . pop BP RET _SUB ENDP END

以上说明的是C语言程序给汇编语言子过程传达室送参数的方法,如果汇编语言子过程有参数要回送给C语言程序,则应通过累加器回送。

3.还应该说明的是,汇编程序子过程中可以任意使用AX、BX、CX、DX、ES,如需要使用BP、SP、DI、SI、CS、DS、SS寄存器时,则应先把它们的原始内容保存入栈,并在子程序结束前恢复它们的原始内容。

下面举例说明:

使用C语言调用汇编语言子过程dif.asm计算二数之差并在C语言中显示其结果。 C语言程序:

extern unsigned long dif(int,int); main() {

printf(\ }

汇编程序子过程dif.asm .model small .code public _dif

_dif proc near push bp mov bp,sp sub dx,dx

mov ax,[bp+4] ;因为NEAR过程,调用时只压入了IP。

sub ax,[bp+6] sbb dx,0 pop bp ret _dif endp end

c语言和汇编的问题

悬赏分:100 - 解决时间:2007-6-5 16:42 #include void print() { _asm {

DSEG SEGMENT

MESSAGE DB 'HOW DO YUO DO',0DH,0AH,024H DSEG ENDS CSEG SEGMENT

ASSUME CS:CSEG,DS:DSEG BEGIN: MOV AX,DSEG MOV DS,AX

LEA DX,OFFSET MESSAGE MOV AH,9 INT 21H MOV AH,4CH INT 21H CSEG ENDS END BEGIN } }

void main() { print(); }

怎么不可以运行

用的是VC6.0

问题补充:不要复制一大堆

没人看的!!

提问者: xiaozi086520 - 初入江湖 二级

最佳答案

上面的仁兄 mian()不是在吗,你没看到?

VC 是可以嵌入汇编,可是你嵌入的是一个完整的汇编程序,这有些不合理吧. 试想,如果可以嵌入完整的汇编程序,那 VC 岂不是可以叫 VA(Visual Asm)了:) 你把那些定义段的伪代码去掉,然后将变量定义放在 __asm{} 前面(嵌入代码可以访问到这些变量的).然后再编译,应该没问题了:) 在VC中嵌入汇编,只需在 _asm {

加入实现应用的汇编代码。 } 就行了。

回答者:xiao086 - 江湖新秀 五级 5-31 21:18

其他回答 共 4 条

你的main函数在什么地方,编译器只能从你的主函数开始处理,我都没看见你的main函数,你说编译器怎么能执行?

回答者:no2iverson - 助理 二级 5-31 21:14

hehe 是不是啊,我要看一下才能回答你 的问题

回答者:飘_月亮在北 - 初入江湖 二级 5-31 22:35

DSEG SEGMENT

MESSAGE DB 'HOW DO YUO DO',0DH,0AH,024H DSEG ENDS CSEG SEGMENT

ASSUME CS:CSEG,DS:DSEG

这些都是鸟语,vc识别不了。vc是支持嵌入汇编,不是支持汇编

可以这样写

int aaa; _asm { mov eax,aaa }

如何在C语言中嵌入汇编?

悬赏分:5 - 提问时间2007-5-25 13:30

RT,如下: #include

void main() { __asm{ mov ah,01h int 21h }

printf(\ }

上边那个例子用VC6.0编译的时候不能通过,说那个int 21h中断有问题 但是在TC环境下却说\是undefined symbol 我应该怎么写代码才能通过编译啊?谢谢

上面的只是一个简单的例子,真正的作业是要嵌入一段显示图形的代码,只要帮我把上边那个编译过去就行了,剩下的我自己搞定

提问者:yz12121 - 试用期 一级

其他回答 共 2 条

在 Visual C++ 中使用内联汇编- -

使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 2003 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。

内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。

一、 优点

使用内联汇编可以在 C/C++ 代码中嵌入汇编语言指令,而且不需要额外的汇编和连接步骤。在 Visual C++ 中,内联汇编是内置的编译器,因此不需要配置诸如 MASM 一类的独立汇编工具。这里,我们就以 Visual Studio .NET 2003 为背景,介绍在 Visual C++ 中使用内联汇的相关知识(如果是早期的版本,可能会有些许出入)。

内联汇编代码可以使用 C/C++ 变量和函数,因此它能非常容易地整合到 C/C++ 代码中。它能做一些对于单独使用 C/C++ 来说非常笨重或不可能完成的任务。

内联汇编的用途包括:

使用汇编语言编写特定的函数;

编写对速度要求非常较高的代码; 在设备驱动程序中直接访问硬件; 编写 naked 函数的初始化和结束代码。

二、 关键字

使用内联汇编要用到 __asm 关键字,它可以出现在任何允许 C/C++ 语句出现的地方。我们来看一些例子:

简单的 __asm 块: __asm {

MOV AL, 2 MOV DX, 0xD007 OUT AL, DX }

在每条汇编指令之前加 __asm 关键字: __asm MOV AL, 2 __asm MOV DX, 0xD007 __asm OUT AL, DX

因为 __asm 关键字是语句分隔符,所以可以把多条汇编指令放在同一行: __asm MOV AL, 2 __asm MOV DX, 0xD007 __asm OUT AL, DX

显然,第一种方法与 C/C++ 的风格很一致,并且把汇编代码和 C/C++ 代码清楚地分开,还避免了重复输入 __asm 关键字,因此推荐使用第一种方法。

不像在 C/C++ 中的\,__asm 块的\不会影响 C/C++ 变量的作用范围。同时,__asm 块可以嵌套,而且嵌套也不会影响变量的作用范围。

为了与低版本的 Visual C++ 兼容,_asm 和 __asm 具有相同的意义。另外,Visual C++ 支持标准 C++ 的 asm 关键字,但是它不会生成任何指令,它的作用仅限于使编译器不会出现编译错误。要使用内联汇编,必须使用 __asm 而不是 asm 关键字。

三、 汇编语言

1. 指令集

内联汇编支持 Intel Pentium 4 和 AMD Athlon 的所有指令。更多其它处理器的指令可以通过 _EMIT 伪指令来创建(_EMIT 伪指令说明见下文)。

2. MASM 表达式

在内联汇编代码中,可以使用所有的 MASM 表达式(MASM 表达式是指用来计算一个数值或一个地址的操作符和操作数的组合)。

3. 数据指示符和操作符

虽然 __asm 块中允许使用 C/C++ 的数据类型和对象,但它不能使用 MASM 指示符和操作符来定义数据对象。这里特别指出,__asm 块中不允许 MASM 中的定义指示符(DB、DW、DD、DQ、DT 和 DF),也不允许使用 DUP 和 THIS 操作符。MASM 中的结构和记录也不再有效,内联汇编不接受 STRUC、RECORD、WIDTH 或者 MASK。

4. EVEN 和 ALIGN 指示符

尽管内联汇编不支持大多数 MASM 指示符,但它支持 EVEN 和 ALIGN。当需要的时候,这些指示符在汇编代码里面加入 NOP 指令(空操作)使标号对齐到特定边界。这样可以使某些处理器取指令时具有更高的效率。

5. MASM 宏指示符

内联汇编不是宏汇编,不能使用 MASM 宏指示符(MACRO、REPT、IRC、IRP 和 ENDM)和宏操作符(<>、!、&、% 和 .TYPE)。 6. 段

必须使用寄存器而不是名称来指明段(段名称\是无效的)。并且,段跨越必须显式地说明,如 ES:[EBX]。

7. 类型和变量大小

在内联汇编中,可以用 LENGTH、SIZE 和 TYPE 来获取 C/C++ 变量和类型的大大小。 * LENGTH 操作符用来取得 C/C++ 中数组的元素个数(如果不是一个数组,则结果为 1)。 * SIZE 操作符可以获取 C/C++ 变量的大小(一个变量的大小是 LENGTH 和 TYPE 的乘积)。 * TYPE 操作符可以返回 C/C++ 类型和变量的大小(如果变量是一个数组,它得到的是数组中单个元素的大小)。

例如,程序中定义了一个 8 维的整数型变量:

int iArray[8];

下面是 C 和汇编表达式中得到的 iArray 及其元素的相关值:

__asm C Size

LENGTH iArray sizeof(iArray)/sizeof(iArray[0]) 8

SIZE iArray sizeof(iArray) 32 TYPE iArray sizeof(iArray[0]) 4 8. 注释

内联汇编中可以使用汇编语言的注释,即\。例如:

__asm MOV EAX, OFFSET pbBuff ; Load address of pbBuff

因为 C/C++ 宏将会展开到一个逻辑行中,为了避免在宏中使用汇编语言注释带来的混乱,内联汇编也允许使用 C/C++ 风格的注释。

9. _EMIT 伪指令

_EMIT 伪指令相当于 MASM 中的 DB,但是 _EMIT 一次只能在当前代码段(.text 段)中定义一个字节。例如: __asm {

JMP _CodeLabel

_EMIT 0x00 ; 定义混合在代码段的数据 _EMIT 0x01

_CodeLabel: ; 这里是代码 _EMIT 0x90 ; NOP指令 }

10. 寄存器使用

一般来说,不能假定某个寄存器在 __asm 块开始的时候有已知的值。寄存器的值将不能保证会从 __asm 块保留到另外一个 __asm 块中。

如果一个函数声明为 __fastcall 调用方式,则其参数将通过寄存器而不是堆栈来传递。这将会使 __asm 块产生问题,因为函数无法被告知哪个参数在哪个寄存器中。如果函数接收了 EAX 中的参数并立即储存一个值到 EAX 中的话,原来的参数将丢失掉。另外,在所有声明为 __fastcall 的函数中,ECX 寄存器是必须一直保留的。为了避免以上的冲突,包含 __asm 块的函数不要声明为 __fastcall 调用方式。

提示:如果使用 EAX、EBX、ECX、EDX、ESI 和 EDI 寄存器,你不需要保存它。但如果你用到了 DS、SS、SP、BP 和标志寄存器,那就应该用 PUSH 保存这些寄存器。

提示:如果程序中改变了用于 STD 和 CLD 的方向标志,必须将其恢复到原来的值。

四、 使用 C/C++ 元素

1. 可用的 C/C++ 元素

C/C++ 与汇编语言可以混合使用,在内联汇编中可以使用 C/C++ 变量以及很多其它的 C/C++ 元素,包括:

符号,包括标号、变量和函数名; 常量,包括符号常量和枚举型成员; 宏定义和预处理指示符; 注释,包括\和\;

类型名,包括所有 MASM 中合法的类型;

typedef 名称,通常使用 PTR 和 TYPE 操作符,或者使用指定的的结构或枚举成员。

在内联汇编中,可以使用 C/C++ 或汇编语言的基数计数法。例如,0x100 和 100H 是相等的。

2. 操作符使用

内联汇编中不能使用诸如\一类的 C/C++ 操作符。但是,C/C++ 和 MASM 共有的操作符(比如\和\操作符),都被认为是汇编语言的操作符,是可以使用的。举个例子:

int iArray[10];

__asm MOV iArray[6], BX ; Store BX at iArray + 6 (Not scaled) iArray[6] = 0; // Store 0 at iArray+12 (Scaled)

提示:在内联汇编中,可以使用 TYPE 操作符使其与 C/C++ 一致。比如,下面两条语句是一样的: __asm MOV iArray[6 * TYPE int], 0 ; Store 0 at iArray + 12 iArray[6] = 0; // Store 0 at iArray + 12

3. C/C++ 符号使用

在 __asm 块中可以引用所有在作用范围内的 C/C++ 符号,包括变量名称、函数名称和标号。但是不能访问 C++ 类的成员函数。

下面是在内联汇编中使用 C/C++ 符号的一些限制:

每条汇编语句只能包含一个 C/C++ 符号。在一条汇编指令中,多个符号只能出现在 LENGTH、TYPE 或 SIZE 表达式中。

在 __asm 块中引用函数必须先声明。否则,编译器将不能区别 __asm 块中的函数名和标号。 在 __asm 块中不能使用对于 MASM 来说是保留字的 C/C++ 符号(不区分大小写)。MASM 保留字包含指令名称(如 PUSH)和寄存器名称(如 ESI)等。 在 __asm 块中不能识别结构和联合标签。 4. 访问 C/C++ 中的数据

内联汇编的一个非常大的方便之处是它可以使用名称来引用 C/C++ 变量。例如,如果 C/C++ 变量 iVar 在作用范围内:

__asm MOV EAX, iVar ; Stores the value of iVar in EAX

如果 C/C++ 中的类、结构或者枚举成员具有唯一的名称,则在 __asm 块中可以只通过成员名称来访问(省略\操作符之前的变量名或 typedef 名称)。然而,如果成员不是唯一的,你必须在\操作符之前加上变量名或 typedef 名称。例如,下面的两个结构都具有 SameName 这个成员变量:

struct FIRST_TYPE {

char *pszWeasel; int SameName; };

struct SECOND_TYPE {

int iWonton; long SameName; };

如果按下面方式声明变量:

struct FIRST_TYPE ftTest; struct SECOND_TYPE stTemp;

那么,所有引用 SameName 成员的地方都必须使用变量名,因为 SameName 不是唯一的。另外,由于上面的 pszWeasel 变量具有唯一的名称,你可以仅仅使用它的成员名称来引用它: __asm {

MOV EBX, OFFSET ftTest

MOV ECX, [EBX]ftTest.SameName ; 必须使用\ MOV ESI, [EBX]. pszWeasel ; 可以省略\ }

提示:省略变量名仅仅是为了书写代码方便,生成的汇编指令还是一样的。 5. 用内联汇编写函数

如果用内联汇编写函数的话,要传递参数和返回一个值都是非常容易的。看下面的例子,比较一下用独立汇编和内联汇编写的函数:

; PowerAsm.asm

; Compute the power of an integer

PUBLIC GetPowerAsm

_TEXT SEGMENT WORD PUBLIC 'CODE' GetPowerAsm PROC PUSH EBP ; Save EBP

MOV EBP, ESP ; Move ESP into EBP so we can refer ; to arguments on the stack

MOV EAX, [EBP+4] ; Get first argument MOV ECX, [EBP+6] ; Get second argument SHL EAX, CL ; EAX = EAX * (2 ^ CL) POP EBP ; Restore EBP RET ; Return with sum in EAX GetPowerAsm ENDP _TEXT ENDS END

C/C++ 函数一般用堆栈来传递参数,所以上面的函数中需要通过堆栈位置来访问它的参数(在 MASM 或其它一些汇编工具中,也允许通过名称来访问堆栈参数和局部堆栈变量)。

下面的程序是使用内联汇编写的:

// PowerC.c

#include

int GetPowerC(int iNum, int iPower);

int main() {

printf(\ }

int GetPowerC(int iNum, int iPower) { __asm {

MOV EAX, iNum ; Get first argument MOV ECX, iPower ; Get second argument SHL EAX, CL ; EAX = EAX * (2 to the power of CL) }

// Return with result in EAX }

使用内联汇编写的 GetPowerC 函数可以通过参数名称来引用它的参数。由于 GetPowerC 函数没有执行 C 的 return 语句,所以编译器会给出一个警告信息,我们可以通过 #pragma warning 禁止生成这个警告。

内联汇编的其中一个用途是编写 naked 函数的初始化和结束代码。对于一般的函数,编译器会自动帮我们生成函数的初始化(构建参数指针和分配局部变量等)和结束代码(平衡堆栈和返回一个值等)。使用内联汇编,我们可以自己编写干干净净的函数。当然,此时我们必须自己动手做一些有关函数初始化和扫尾的工作。例如:

void __declspec(naked) MyNakedFunction() {

// Naked functions must provide their own prolog. __asm {

PUSH EBP MOV ESP, EBP

SUB ESP, __LOCAL_SIZE } . . .

// And we must provide epilog. __asm {

POP EBP RET } }

6. 调用 C/C++ 函数

内联汇编中调用声明为 __cdecl 方式(默认)的 C/C++ 函数必须由调用者清除参数堆栈,下面是一个调用 C/C++ 函数例子:

#include

char szFormat[] = \ char szHello[] = \ char szWorld[] = \

void main() { __asm {

MOV EAX, OFFSET szWorld PUSH EAX

MOV EAX, OFFSET szHello PUSH EAX

MOV EAX, OFFSET szFormat PUSH EAX CALL printf

// 压入了 3 个参数在堆栈中,调用函数之后要调整堆栈 ADD ESP, 12 } }

提示:参数是按从右往左的顺序压入堆栈的。

如果调用 __stdcall 方式的函数,则不需要自己清除堆栈。因为这种函数的返回指令是 RET n,会自动清除堆栈。大多数 Windows API 函数均为 __stdcall 调用方式(仅除 wsprintf 等几个之外),下面是一个调用 MessageBox 函数的例子:

#include

TCHAR g_tszAppName[] = TEXT(\

void main() {

TCHAR tszHello[] = TEXT(\ __asm {

PUSH MB_OK OR MB_ICONINFORMATION

PUSH OFFSET g_tszAppName ; 全局变量用 OFFSET LEA EAX, tszHello ; 局部变量用 LEA PUSH EAX PUSH 0

CALL DWORD PTR [MessageBox] ; 注意这里不是 CALL MessageBox,而是调用重定位过的函数地址 } }

提示:可以不受限制地访问 C++ 成员变量,但是不能访问 C++ 的成员函数。 7. 定义 __asm 块为 C/C++ 宏

使用 C/C++ 宏可以方便地把汇编代码插入到源代码中。但是这其中需要额外地注意,因为宏将会扩展到一个逻辑行中。

为了不会出现问题,请按以下规则编写宏:

使用花括号把 __asm 块包围住;

把 __asm 关键字放在每条汇编指令之前;

使用经典 C 风格的注释(\),不要使用汇编风格的注释(\)或单行的 C/C++ 注释(\);

举个例子,下面定义了一个简单的宏:

#define PORTIO __asm \\ /* Port output */ \\ { \\

__asm MOV AL, 2 \\ __asm MOV DX, 0xD007 \\ __asm OUT DX, AL \\ }

乍一看来,后面的三个 __asm 关键字好像是多余的。其实它们是需要的,因为宏将被扩展到一个单行中:

__asm /* Port output */ { __asm MOV AL, 2 __asm MOV DX, 0xD007 __asm OUT DX, AL }

从扩展后的代码中可以看出,第三个和第四个 __asm 关键字是必须的(作为语句分隔符)。在 __asm 块中,只有 __asm 关键字和换行符会被认为是语句分隔符,又因为定义为宏的一个语句块会被认为是一个逻辑行,所以必须在每条指令之前使用 __asm 关键字。

括号也是需要的,如果省略了它,编译器将不知道汇编代码在哪里结束,__asm 块后面的 C/C++ 语句看起来会被认为是汇编指令。

同样是由于宏展开的原因,汇编风格的注释(\)和单行的 C/C++ 注释(\)也可能会出现错误。为了避免这些错误,在定义 __asm 块为宏时请使用经典 C 风格的注释(\)。

和 C/C++ 宏一样 __asm 块写的宏也可以拥有参数。和 C/C++ 宏不一样的是,__asm 宏不能返回一个值,因此,不能使用这种宏作为 C/C++ 表达式。

不要不加选择地调用这种类型的宏。比如,在声明为 __fastcall 的函数中调用汇编语言宏可能会导致不可预料的结果(请参看前文的说明)。 8. 转跳

可以在 C/C++ 里面使用 goto 转跳到 __asm 块中的标号处,也可以在 __asm 块中转跳到 __asm 块里面或外面的标号处。__asm 块内的标号是不区分大小写的(指令、指示符等也是不区分大小写的)。例如:

void MyFunction() {

goto C_Dest; /* 正确 */ goto c_dest; /* 错误 */

goto A_Dest; /* 正确 */

goto a_dest; /* 正确 */ __asm {

JMP C_Dest ; 正确 JMP c_dest ; 错误

JMP A_Dest ; 正确 JMP a_dest ; 正确

a_dest: ; __asm 标号 }

C_Dest: /* C/C++ 标号 */ return; }

不要使用函数名称当作标号,否则将转跳到函数中执行,而不是标号处。例如,由于 exit 是 C/C++ 的函数,下面的转跳将不会到 exit 标号处:

; 错误:使用函数名作为标号 JNE exit . . . exit: . . .

美元符号\用于指定当前指令位置,常用于条件跳转中,例如:

JNE $+5 ; 下面这条指令的长度是 5 个字节 JMP _Label

NOP ; $+5,转跳到了这里 . . . _Label: . . .

五、在 Visual C++ 工程中使用独立汇编

内联汇编代码不易于移植,如果你的程序打算在不同类型的机器(比如 x86 和 Alpha)上运行,你可能需要在不同的模块中使用特定的机器代码。这时候你可以使用 MASM(Microsoft Macro Assembler),因为 MASM 支持更多方便的宏指令和数据指示符。

这里简单介绍一下在 Visual Studio .NET 2003 中调用 MASM 编译独立汇编文件的步骤。

在 Visual C++ 工程中,添加按 MASM 的要求编写的 .asm 文件。在解决方案资源管理器中,右击这个文件,选择\属性\菜单项,在属性对话框中,点击\自定义生成步骤\,设置如下项目:

命令行:ML.exe /nologo /c /coff \ 输出:$(IntDir)\\$(InputName).obj

如果要生成调试信息,可以在命令行中加入\参数,还可以根据需要生成 .lst 和 .sbr 文件。

如果要在汇编文件中调用 Windows API,可以从网上下载 MASM32 包(包含了 MASM 汇编工具、非常完整的 Windows API 头文件/库文件、实用宏以及大量的 Win32 汇编例子等)。相应地,应该在命令行中加入\参数指定 Windows API 汇编头文件(.inc)的路径。MASM32 的主页是:http://www.masm32.com,里面可以下载最新版本的 MASM32 包。

回答者:mangohappy - 经理 四级 5-25 13:38

你写int 21H当然不会通过了。 应该写 int 0x21才对

汇编和c语言如何结合的?

悬赏分:0 - 解决时间:2006-2-22 11:06 汇编和c语言如何结合的,用什莫语法?

提问者: zfaaqq - 见习魔法师 二级

最佳答案

汇编语言没有高级语言要占用较大的存储空间和较长的运行时间等缺点,它的运行速度快是高级语言所不能比拟的。可以说高级语言与汇编语言各有千秋。有时我们采用高级语言编程速度达不到要求,全部采用汇编语言编程工作量又大,此时可以采用\混合\编程,彼此相互调用,进行参数传递,共享数据结构及数据信息,是一种有效的编程方法。这种方法可以发挥各种语言的优势和特点,充分利用现有的多种实用程序、库程序等使软件的开发周期大大缩短。

1 高级语言与汇编语言的接口需要解决的问题

1、需要说明和建立调用者与被调用者间的关系被调用的过程或函数应预先说明为外部类型,如汇编子程序,应用PUBLIC说明其可被外部模块引用;调用程序则应预先说明要引用的外部模块名。

2、参数传递问题在汇编子程序之间通常采用寄存器作为参数传递的工具,汇编语言与高级语言程序间的参数传递,一般采用堆栈来传递,即调用程序将参数依次压入堆栈中,当被转调用程序后,再从堆栈中依次弹出参数作为操作数使用。为此,必须了解各种语言的堆栈结构、生成方式和入栈方式等。BASIC、FORTRAN、PASCAL等语言其参数进栈顺序是与参数在参数表中出现的顺序相同,即从右到左;而C语言则相反。 2 C语言与汇编语言的接口

2.1 C语言调用汇编子程序

●在C程序中使用关键字\对函数作显式说明。

●参数传递顺序是按其在参数表中出现的顺序的反序被压入堆栈中,即第一个参数最后进入堆栈,它在栈中的地址最低。

●对不同的存储模式(极小、小、紧凑、中、大和巨)要选用不同的汇编语言格式,如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语言(C++或G++)中如何嵌入汇编 悬赏分:200 - 解决时间:2007-8-10 01:05

要求不断输入一个整数A,输出整数A-1,如果A为0就停止.由于测试数据的数量非常大,运行起来很慢.请问怎么在C中嵌套汇编来大大加大效率,编译器是C++或G++.最好给一小段代码样例,我嵌套汇编总是编译错误.谢谢各位牛人了.

我写了个C语言的例子,要求嵌套汇编后功能不能变.

如果输入输出都用汇编就更好了.(必须要VC++6.0编译运行才可以,LINUX的G++编译运行也可以)

#include

int main() { int n;

while(scanf(\ { if(n==0) break;

printf(\ } return 0; }

我看好象嵌套了汇编效率会提高3倍到5倍左右.

问题补充:如果可以,请给一小段代码样例(用我写的小C程序改下就可以了.)(读文件行不通哈.因为大量数据测试本来就会用到文件输入输出.我要的是提高效率但不通过改程序方法,只改语言.)

最好请用VC++6.0编译通过...运行输入1000000输出答案是不是999999.请不要给运行错误的答案..(范围是INT)

提问者: 火领 - 见习魔法师 二级

最佳答案

今天有点时间,重新改下了下,为避免因编译器和平台实现而出现的问题,我写了三个版本,分别是windows下vc6.0,windows下mingw和cygwin和linux下的gcc/g++。 vc6.0:

#include

const char* input = \

const char* output = \ int n;

int main() { __asm {

lea eax, n

push eax

push input loopx:

call scanf

cmp eax, 1 jne end

mov ecx, n

jecxz end dec ecx

push ecx

push output

call printf

add esp, 8

jmp loopx end:

add esp, 8 }

return 0; }

mingw/cygwin:

#include

const char* input = \

const char* output = \ int n;

int main() {

__asm__ (

\

\

\

\

\

\

\

\

\

\

\

\

\

\

\ \ );

return 0;

}

linux gcc/g++:

#include

const char* input = \ const char* output = \ int n;

int main() { __asm__ (

\ \ \ \ \ \ \ \ \ \ \ \ \ \ \

\

return 0; }

回答者:飘渺世间天 - 参将 八级 8-8 11:25

~

评论者: sflypig - 经理 四级

其他回答 共 9 条

上面的仁兄 mian()不是在吗,你没看到?

VC 是可以嵌入汇编,可是你嵌入的是一个完整的汇编程序,这有些不合理吧. 试想,如果可以嵌入完整的汇编程序,那 VC 岂不是可以叫 VA(Visual Asm)了:) 你把那些定义段的伪代码去掉,然后将变量定义放在

__asm{} 前面(嵌入代码可以访问到这些变量的).然后再编译,应该没问题了:) 在VC中嵌入汇编,只需在 _asm {

加入实现应用的汇编代码。 } 就行了。

回答者:hjbsahd - 秀才 二级 7-23 14:46

__asm{

汇编代码 }

例如a+b程序 int main() { int a,b; __asm{ mov a , eax add b , eax mov eax , a } return 0; }

回答者:ecchi - 助理 三级 7-23 14:51

怎么利用这次

回答者:金秋之韵JESSIE - 助理 二级 7-23 14:52

我不知道你的测试是干什么用的。

但是想要提高效率。你可以试着把scanf和printf都改为用文件输入输出。你scanf不会是用手输入的吧?估计是在一个文件中准备的数据。

回答者:messiahfree - 榜眼 十二级 7-23 15:05

有没有软件可以潜入的

回答者:wanlyn - 助理 三级 7-24 00:03

把数据保存在文件里!让c去读文件好了嘛

回答者:xuyue3000 - 魔法师 四级 7-24 13:04

有道理

回答者:英语一级 - 初入江湖 二级 7-30 14:08

高3倍到5倍左右.

问题补充:如果可以,请给一小段代码样例(用我写的小C程序改下就可以了.)(读文件行不通哈.因为大量数据测试本来就会用到文件输入输出.我要的是提高效率但不通过改程序方法,只改语言.)

最好请用VC++6.0编译通过...运行输入1000000输出答案是不是999999.请不要给运行错误的答案..(范围是INT)

提问者:火领 - 见习魔法师 二级

答复共 9 条

上面的仁兄 mian()不是在吗,你没看到?

VC 是可以嵌入汇编,可是你嵌入的是一个完整的汇编程序,这有些不合理吧. 试想,如果可以嵌入完整的汇编程序,那 VC 岂不是可以叫 VA(Visual Asm)了:) 你把那些定义段的伪代码去掉,然后将变量定义放在 __asm{} 前面(嵌入代码可以访问到这些变量的).然后再编译,应该没问题了:) 在VC中嵌入汇编,只需在 _asm

点击后退

编译和连接过程

按照上节所论述的各种约定,让我们编写一个C语言程序调用汇编语言子程序的简单例子,这里没有参数传递的问题。

例7.3:C语言程序调用汇编语言子程序,显示一段信息 /* C语言程序:lt703.c */

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

{ display(); }

; 汇编语言子程序:lt703s.asm

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

msg db ’Hello, C and Assembly !’,’$’ .code

PUBLIC display ;指明该过程(子程序)可供外部模块使用 display proc ;采用了一致的命名约定,共用标识符不必加下划线 mov ah,9 ;小型模式只有一个数据段,所以不必设置DS mov dx,offset msg ;寄存器AX和DX无须保护 int 21h ret _display endp end

上述两个源程序文件已经看懂了吧,那就看看应该按照怎样的步骤进行编译和连接吧: · 利用汇编程序编译汇编语言程序成为.obj 目标代码文件,例如: ML /c lt703s.asm

它将生成lt703s.obj文件。ML的缺省选项∕Cx表示保持汇编语言程序中的名字的大小写不变,这样才能在连接时不出错;选项∕Cu将使名字转变成大写,不应使用。 · 利用C编译程序编译C语言程序成为.obj 目标代码文件,例如: TCC -c lt703.c

其中,-c参数表示只是编译、不连接,结果生成lt703.obj文件。TCC缺省采用小型存储模式,若采用其他模式,要利用-m选项;若C程序中有#include包含文件行,则需加-I选项。 · 利用连接程序将各个目标代码文件连接在一起,得到可执行程序文件,例如: TLINK lib\\c0s lt703 lt703s,lt703.exe,,lib\\cs

注意,直接使用Turbo C的连接程序TLINK进行连接时,用户必须指定要连接的与存储模式一致的初始化模块和函数库文件,并且初始化模块必须是第一个文件。上例中,lib\\c0s和lib\\cs就是在lib目录下小型存储模式的初始化模块C0S.OBJ和函数库CS.LIB。

如果形成的可执行文件lt703.exe正确,它的运行结果将是: Hello, C and Assembly !

编译和连接也可以利用命令行一次完成,这样更加方便。它的一般格式为: TCC -mx -I包含文件路径 -L库文件路径 filename1 filename2 ... 例如,上例可以利用如下命令:

TCC -ms -Iinclude -Llib lt703.c lt703s.obj

其中-m 选项指定存储模式,其后的字母x为t(微型)、s(小型,缺省值)、c(紧凑)、m(中型)、l(大型)、h(巨型)之一,分别代表六种存储模式; -I选项指定连接所需的头文件等包含文件所在的路径; -L选项指定所需的初始化模块C0x.OBJ(此处的x与存储模式一样)和函数库的路径;filename1 、filename2等可以是C语言源程序名、汇编语言源程序名或目标代码文件名,C程序扩展名.c可以省略,但汇编源程序和目标代码文件的扩展名不能省略。若有.asm文件,则TCC会自动调用TASM.EXE对.asm文件进行汇编,因此TASM.EXE必须在当前目录中。若没有TASM.EXE,也可用微软ML.EXE(或MASM.EXE)对.asm文件汇编生成目标文件,再将目标文件加入TCC命令行中。 学会了吧?很简单,不是吗?

C程序的编译和模块连接也可以在Turbo C集成环境下进行。此时需要选用一致的存储模式,并建立一个工程文件,包括需编译连接的C源文件名以及汇编语言目标文件名。

点击后退

编译和连接过程

按照上节所论述的各种约定,让我们编写一个C语言程序调用汇编语言子程序的简单例子,这里没有参数传递的问题。

例7.3:C语言程序调用汇编语言子程序,显示一段信息 /* C语言程序:lt703.c */

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

{ display(); }

; 汇编语言子程序:lt703s.asm

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

msg db ’Hello, C and Assembly !’,’$’ .code

PUBLIC display ;指明该过程(子程序)可供外部模块使用 display proc ;采用了一致的命名约定,共用标识符不必加下划线 mov ah,9 ;小型模式只有一个数据段,所以不必设置DS

mov dx,offset msg ;寄存器AX和DX无须保护 int 21h ret _display endp end

上述两个源程序文件已经看懂了吧,那就看看应该按照怎样的步骤进行编译和连接吧: · 利用汇编程序编译汇编语言程序成为.obj 目标代码文件,例如: ML /c lt703s.asm

它将生成lt703s.obj文件。ML的缺省选项∕Cx表示保持汇编语言程序中的名字的大小写不变,这样才能在连接时不出错;选项∕Cu将使名字转变成大写,不应使用。 · 利用C编译程序编译C语言程序成为.obj 目标代码文件,例如: TCC -c lt703.c

其中,-c参数表示只是编译、不连接,结果生成lt703.obj文件。TCC缺省采用小型存储模式,若采用其他模式,要利用-m选项;若C程序中有#include包含文件行,则需加-I选项。 · 利用连接程序将各个目标代码文件连接在一起,得到可执行程序文件,例如: TLINK lib\\c0s lt703 lt703s,lt703.exe,,lib\\cs

注意,直接使用Turbo C的连接程序TLINK进行连接时,用户必须指定要连接的与存储模式一致的初始化模块和函数库文件,并且初始化模块必须是第一个文件。上例中,lib\\c0s和lib\\cs就是在lib目录下小型存储模式的初始化模块C0S.OBJ和函数库CS.LIB。

如果形成的可执行文件lt703.exe正确,它的运行结果将是: Hello, C and Assembly !

编译和连接也可以利用命令行一次完成,这样更加方便。它的一般格式为: TCC -mx -I包含文件路径 -L库文件路径 filename1 filename2 ... 例如,上例可以利用如下命令:

TCC -ms -Iinclude -Llib lt703.c lt703s.obj

其中-m 选项指定存储模式,其后的字母x为t(微型)、s(小型,缺省值)、c(紧凑)、m(中型)、l(大型)、h(巨型)之一,分别代表六种存储模式; -I选项指定连接所需的头文件等包含文件所在的路径; -L选项指定所需的初始化模块C0x.OBJ(此处的x与存储模式一样)和函数库的路径;filename1 、filename2等可以是C语言源程序名、汇编语言源程序名或目标代码文件名,C程序扩展名.c可以省略,但汇编源程序和目标代码文件的扩展名不能省略。若有.asm文件,则TCC会自动调用TASM.EXE对.asm文件进行汇编,因此TASM.EXE必须在当前目录中。若没有TASM.EXE,也可用微软ML.EXE(或MASM.EXE)对.asm文件汇编生成目标文件,再将目标文件加入TCC命令行中。 学会了吧?很简单,不是吗?

C程序的编译和模块连接也可以在Turbo C集成环境下进行。此时需要选用一致的存储模式,并建立一个工程文件,包括需编译连接的C源文件名以及汇编语言目标文件名。

C语言编译产生的.OBJ文件和汇编编译产生的.OBJ文件,如何连接 悬赏分:200 - 解决时间:2007-6-1 16:30

C语言程序调用汇编语言过程法:C语言程序经编译后产生.OBJ文件,汇编程序经汇编后也产生.OBJ文件,然后由连接程序把他们连接起来而形成.EXE可执行文件。 我使用的是Borland C++3.0。能在Turbo C 2.0里实现也可以。

另:书上是这样写的--主源程序经过编译后,生成a.obj文件。将汇编子程序模块b.obj复制到同一目录下。

使用工程文件连接方法,将工程文件起名为test.prj。把a.obj文件和子程序模块b.obj加载进去,F9连接生成test.exe。

请问我如何使用工程文件连接方法,把obj文件加载进去???? 在线等!高分求助!!能解决问题追加100分

提问者: boyou_ding - 助理 二级

最佳答案

一同学习。。。

Trubo C的命令行编译连接

所谓命令行编译,是指在dos下,调用Trubo C的tcc.exe程序.来完成对turbo C源程序的编译连接工作.当选择对后缀为*.asm的汇编程序文件编译时,tcc还要调用TASM后才能对后缀为.asm的文件进行编译,这种方式适合于c程序与汇编语言混合编程的编译连接,当c程序嵌入汇编指令时,也必须用此方法编译连接。 命令行编译的格式为:

tcc [选项1 选项2 ...]文件名1 文件名2...其中选项是指对后面给出的文件进行连接时的选择项,可选的常用选择项如下所示:每个选项前都带有\号,且大小写是区分的。文件名是指源文件.c或目标文件.obj或库文件.lib当不指定只编译不连接时,tcc将完成编译和连接两个步骤,对.lib库只进行形式上的连接,标准库用户不用进行连接。 例如:

tcc -ib:\\include -lb:\\lib -etest start.c body.obj myc当执行该命令时,表示将start.c源文件和body.obj目标文件及myc.c(命令行中该文件无后缀),分别进行编译(对body.obj文件不再编译),然后连接生成名为test的执行文件test.exe(由-test给出).

-ib:\\include 表示包含文件的路径是b:\\include -ib:\\lib 表示库文件的路径是b:\\lib 又例如:

tcc -ms -efile -lc:\\tc\\lib file1 file2.obj graphics.lib

其中-ms表示选择小内存模式进行编译,它也是turbo c的缺省编译模式,将file1进行编译,然后和file2.obj 及graphics.lib进行连接.生成file.exe的可执行文件.其中graphcis.lib库的路径为c:\\tc\\lib,即意为在c:\\tc\\lib目录下去寻找graphics.lib文件.当进行混合编程时,如果已有汇编程序s3.asm其命令行可写为 tcc ic:\\tc\\include -lc:\\tc\\lib -mm s1 s2 s3.asm mylib.lib

表示用中模式(-mm)编译源文件s1.c和s2.c,调用TASM对s3.asm进行编译,然后连接生成可执行文件s1.exe,编译时,到c:\\tc\\include目录中去找包含文件,到 c:\\tc\\lib目录中去找库文件mylib.lib. ---------------------------------------

http://topic.csdn.net/t/20010308/22/81569.html http://www.down22.org/plus/view.php?aid=15882

TC中的PRJ文件如何建立?请举个例子(给20分)谢谢

楼主blue_water(蓝色瀑布)2001-03-08 22:42:00 在 C/C++ / C语言 提问 问题点数:0、回复次数:5Top

1 楼fishworm(胖子)回复于 2001-03-08 23:59:00 得分 0

1。prj文件是文本文件,你可以用文本编辑器编写(如:TC、EDIT等)。 2。prj的文件名要与你的主文件同名,如:主程序为main.c则main.prj。 3。如果你使用一个非TC本有的LIB,也可以将该LIB加入prj中。Top 2 楼cpublic(CoCo)回复于 2001-03-09 05:28:00 得分 0 使用方法很简单:

我们有这样一个程序,其中有H文件和CPP文件。 Test.h

#include void test(void) {

printf(\ is project sample!\ }

Test.cpp

#include \

void main() {

test(); }

这个时候用PRJ项目来就会方便程序的开发。打开Project菜单,选Open Project,输入test.prj项目名。然后再选择Project菜单下的Add item...项,把Test.CPP加入项目中,同样TEST。H也加入项目中,保存项目文件。编译项目生成TEST。EXE文件。

PRJ文件在大型程序的开发中特别有用,最明显的就是当项目中某个文件发生改动时,只需要把项目文件重新编译一下即可。 Top

3 楼holyfire(谁最衰啊你最衰,谁最帅啊我最帅)回复于 2001-03-09 08:19:00 得分 0 楼上的,TC中好像没有Project菜单喔。 用TC new 一个文件,SaveAsTest.prj 内容为要包含的所有文件如.c,.lib等等。 my1.c my2.c my1.libTop

4 楼ICforever(仓鼠)回复于 2001-03-09 10:56:00 得分 0 tc++倒有project菜单,可以建立工程,加进app文件,h文件等

本文来源:https://www.bwwdw.com/article/zybr.html

Top