第⼗⼋篇--在C++中嵌⼊汇编语⾔
基于C++宝典的学习
⼀、什么是汇编语⾔
汇编语⾔是⼀种功能很强的程序设计语⾔,也是利⽤了计算机所有硬件特性并能直接控制硬件的语⾔。在汇编语⾔中,⽤助记符(Memoni)代替操作码,⽤地址符号(Symbol)或标号(Label)代替地址码。这样⽤符号代替机器语⾔的⼆进制码,就把机器语⾔变成了汇编语⾔。
汇编语⾔⽐机器语⾔易于读写、调试和修改,同时也具有机器语⾔执⾏速度快、占⽤内存空间少等优点。但在编写复杂程序时,相对⾼级语⾔来说汇编语⾔代码量较⼤,⽽且汇编语⾔依赖于具体的机型,不能通⽤,因此不能直接在不同处理机型之间移植。虽然其移植性不好,但效率⾮常⾼,针对计算机特定硬件⽽编制的汇编语⾔程序,能准确地发挥计算机硬件的功能和特长,程序精炼⽽质量⾼,所以汇编语⾔⾄今仍是⼀种常⽤⽽强有⼒的底层开发语⾔。
⼆、汇编语⾔的特点
汇编语⾔指令使⽤⼀些具有相应含义的助忆符来表达的,所以,它要⽐机器语⾔容易掌握和运⽤。但因为要直接使⽤CPU资源,所以相对⾼级程序设计语⾔来说它⼜显得相对复杂。汇编语⾔程序归纳起来⼤
概有以下⼏个主要特点。
1. 与硬件相关:汇编语⾔指令是指机器指令的⼀种符号表⽰,⽽不同类型的CPU有不同的机器指令系统,也就有不同的汇编语⾔,所以汇编语⾔程序与机器有着密切的关系。也就是说,不同型号的CPU之间是⽆法通⽤相同汇编代码的,因此导致汇编语⾔的移植性和通⽤性降低,这是汇编语⾔天⽣的缺陷。
2. 保持了机器语⾔的优点,具有直接和简捷的特点:正因为汇编语⾔有“与机器相关性”的特性,程序员⽤汇编语⾔编写程序时,可充分发挥⾃⼰的聪明才智,对机器内部的各种资源进⾏合理的安排,让它们始终处于最佳的使⽤状态,这样做的最终效果就是程序的执⾏代码短,执⾏速度快,所以,汇编语⾔是⾼效的程序设计语⾔。另外汇编语⾔可有效地访问、控制计算机的各种硬件设备,如磁盘、存储器、CPU、I/O端⼝等,实现资源利⽤的最⼤化。
3. 编写程序复杂:汇编语⾔是⼀种⾯向机器的语⾔,其汇编指令与机器指令基本上⼀⼀对应,所以,汇编指令也同机器指令⼀样既有功能单⼀、具体的特点。要想完成某件⼯作,就必须安排CPU的每步⼯作。另外,在编写汇编语⾔程序时,还要考虑具体机型的限制、汇编指令的细节和限制等。
4. 经常与⾼级语⾔配合使⽤,应⽤⼗分⼴泛:在某些情况下,⽐如直接操作CPU执⾏中断以实现线程调度、保存CPU寄存器以存储/恢复线程状态等,仅仅使⽤⾼级语⾔是完不成的,需要借助于汇编语
⾔,但是仅使⽤汇编语⾔的话,⼤型程序恐怕需要付出⽐⾼级语⾔⼏倍的⼯作量,有时候也是没有必要的。因此,可以在⾼级语⾔⾥嵌⼊汇编语句,让仅仅⼀部分需要⾼效率的代码⽤汇编语⾔来完成,其余的框架搭建等⽤⾼级语⾔来完成,这样既保证了效率⼜降低了代码的复杂程度。这种配合使⽤在⼤型软件开发⾥经常遇到,应⽤⼗分⼴泛。
三、汇编语⾔的应⽤领域
汇编语⾔是⾯向机器的低级程序设计语⾔。它可以直接控制硬件的最下层,如寄存器、标志位、存储单元等,因⽽能充分发挥机器硬件的性能,具有其他⾼级语⾔不可替代的作⽤。汇编语⾔是计算机能够提供给⽤户的最快的、最有效的语⾔,也是能够利⽤计算机所有硬件特性并且能够直接控制硬件的唯⼀语⾔。也正因为汇编语⾔具有这些特性,在对于程序的控件和时间要求很⾼的场合,以及需要直接控制硬件的应⽤场合,使⽤汇编语⾔是必不可少的,它能够完成许多其他⾼级语⾔所⽆法完成的功能。⽐如Linux操作系统的内核,⼤多数代码是⽤C语⾔完成的,但是在某些关键的与硬件关系密切的部分,仍不可避免地使⽤了汇编语⾔。汇编语⾔试⽤的应⽤领域如下。
1. 要求执⾏效率⾼、反应快的领域:在操作系统内核、实时系统、智能仪器仪表的控制程序等领域中最主要的要求是效率⾼和反应快。汇编语⾔是直接作⽤于机器硬件的,它所设计的程序具有较⼩的时间复杂度和空间复杂度。⽽⾼级语⾔是⾯向问题来设计的,它所设计的程序运⾏时间长,并且占⽤存储空间⼤,在反应和效率上远远不如汇编语⾔。
2. 与硬件资源密切相关的软件开发领域:汇编语⾔是⾯向机器的,⽽⾼级语⾔是⾯向问题的,对于⼀些与硬件资源密切相关的软件也只能采⽤汇编语⾔。如外部设备的底层驱动程序、单⽚机控制、图像处理软件、加密解密算法等领域。
3. 受存储容量限制的应⽤领域:诸如家⽤电器的控制领域等,控制系统功能单⼀、⾏为简单,且受内存、CPU等硬件条件限制,需要使⽤汇编语⾔进⾏嵌⼊式控制。
不过,相⽐⾼级语⾔,汇编语⾔调试困难,⼯作量⼤,其不宜使⽤的领域有⼤型软件的整体开发、没有特殊要求的⼀般应⽤系统的开发等。
四、汇编语⾔的基本语法
下⾯以8086系统为例讲解汇编语⾔的基本语法和⽤法。
按功能分,8086系统的汇编指令包括数据传送指令、算数指令、逻辑指令、串处理指令、控制转移指令和处理机控制指令6⼤类。
》通⽤数据传送指令
8086系统有4类数据传送指令,以实现CPU的内部寄存器之间、CPU与存储器之间、CPU和I/O端⼝之
间的数据传送。这4类指令是通⽤数据传送指令、累加器专⽤传送指令、地址传送指令和标志传送指令。通⽤数据传送指令中包括最基本的传送MOV(Move)指令、堆栈
PUSH(Push onto the stack) 和POP(Pop from the stack) 指令、数据交换XCHG(Exchange)指令。
1. 最基本的传送指令MOV指令
MOV指令为双操作数指令,它将⼀个字节或⼀个字的操作数从源操作数复制⾄⽬的操作数,其语法格式为:
MOV DST,SRC
其中,SRC为源操作数,它可以是⽴即数、寄存器以及各种寻址的内存单元内容。DST为⽬的操作数,它可以是寄存器或者各种寻址的内存单元,不可以是⽴即数。
执⾏的操作:
(DST)<-(SRC)
对于最基本的传送指令的使⽤,有⼏点需要说明:
1. ⽬的数可以是通⽤寄存器、存储单元和段寄存器(但不允许⽤CS段寄存器)。
2. ⽴即数不能直接传送⾄段寄存器。
3. 不允许在两个存储单元间直接传送数据。
4. 不允许在两个段寄存器间直接传达信息。
以8086为例:
MOV CL, AL ;AL中的8位数据到CL
MOV DS, AX ;AX中的16位数据到DS
2. 堆栈操作指令
堆栈是按照后进先出(LIFO--Last In First Out) 原则组织的⼀段内存区域。在⼦程序调⽤和终端处理时,分别要保存返回地址和断点地址,在进⼊⼦程序和中断服务程序后,还通常需要保护通⽤寄存器原先的内容,⼦程序返回或中断处理返回主程序前,则要恢复通⽤寄存器原先的内容,并分别将返回地址或断点地址恢复到指令指针寄存器中。这些功能都要通过堆栈来实现。另外,在⾼级语⾔中除中
断及⼦程序调⽤外,参数的传递也是靠堆栈来实现的。除返回地址和断点地址的保护及恢复操作是由CPU⾃动完成的以外,寄存器的保存、恢复以及参数的传递都需要由堆栈指令来完成。
堆栈指令包括⼊栈PUSH指令和出栈POP指令。
PUSH是⼊栈指令,完成将源操作数中的⼀个字推⼊(也称压⼊)堆栈的操作。其堆栈指针SP的值始终指向刚刚⼊栈的数据处,每进⼀个字,栈顶指针SP的值减2.其语法格式为:
PUSH SRC
其中SRC为源操作数,可以是除⽴即数之外的16位的寄存器或者内存字单元的内容(两个字节)。对于⼊栈PUSH指令的使⽤,需要说明的是:
1. ⼊栈的操作数除不允许⽤⽴即操作数外,可以为通⽤寄存器、段寄存器(全部)和存储器。
2. ⼊栈时⾼位字节先⼊栈,低位字节后⼊栈。
以8086为例:
PUSH AX ;CPU通⽤寄存器⼊栈
PUSH CS ;段寄存器⼊栈
PUSH [BX+DI] ;存储器单元⼊栈
POP是出栈指令,⽤于将SP所指的堆栈顶处的⼀个字取出(也称弹出),送⾄⽬的DST中,并且SP的值加2.其语法格式为:
POP DST
DST是⽬的操作数,可以是除⽴即数之外的16位的寄存器或者内存字单元的内容(两个字节)。对于出栈POP指令的使⽤,需要说明的是:1. 出栈操作数除不允许⽤⽴即数和CS段寄存器外,可以为通⽤寄存器、段寄存器和存储器。
2. 执⾏POP SS指令后,堆栈区在存储区的位置要改变。
执⾏POP SP指令后,栈顶的位置要改变。
以8086为例:
POP AX ;CPU通⽤寄存器出栈
POP CS ;段寄存器出栈
POP [BX+DI] ;存储单元出栈
3.数据交换指令:XCHG
数据交换指令XCHG可在寄存器之间、寄存器与存储器之间进⾏数据交换,但不允许在两个存储单元之间执⾏交换过程,并且段寄存器和IP 寄存器不能参与数据交换。此外,该指令的执⾏不影响标志位。其语法格式为:
XCHG OPR1, OPR2
上述数据交换指令实现操作数OPR1和OPR2中的⼀个字或⼀个字节的数据交换。对于数据交换指令的使⽤,需要说明的是:
1. 必须有⼀个操作数在寄存器中。
2. 不能与段寄存器交换数据。
3. 存储器与存储器之间不能交换数据。
以8086为例:
XCHG AL, BL ;AL和BL间进⾏字节交换
XCHG BX, CX ;BX与CX间进⾏字交换
》累加器专⽤传送指令
累加器专⽤传送指令包括I/O端⼝向CPU输⼊信息指令IN(Input)、CPU向端⼝发送信息指令OUT(Output)以及换码指令XLAT(Translate)。
在IBM PC机⾥,所有I/O端⼝与CPU之间的通信都由IN和OUT指令来完成。其中IN完成从I/O到CPU的信息传送,⽽OUT完成从CPU到I/O的信息传送。CPU只能⽤累加器(AL或AX)接收或发送消息。
1. 输⼊IN指令
输⼊指令完成将信息从I/O通过累加器传送到CPU的操作。该指令有长格式和短格式之分。长格式语法格式为:
IN AL, PORT(每次输⼊⼀字节内容。字节传送)
IN AX, PORT(字传送)
执⾏的操作
(AL)<-(PORT)(字节)
(AX)<-(PORT+1, PORT)(字)
短格式语法格式为:
IN AL, DX(字节)
IN AX, DX(字)
执⾏的操作:
AL<-((DX))(字节)
AX<-((DX)+1, DX)(字)
CPU与外设通信时,输⼊/输出通信必须经过特殊的端⼝进⾏。外部设备最多可有65536个端⼝,端⼝号(即外设的端⼝地址)为
0000~FFFFh。其中前256个端⼝(0~FFH)可以直接在指令中指定,这就是长格式指令中的PORT,此时机器指令⽤另个字节表⽰,第⼆个字节就是端⼝号。所以⽤长格式时可以在指令中直接指定端⼝号,但只限于外设的前256个端⼝。当端⼝号⼤于255时,只能使⽤短格式指令。短格式指令要⽤寄存器DX来间接传送,此时DX的内容是端⼝号,⽽IN指令的作⽤是将DX中指⽰的端⼝号内的信息送⾄AL或AX,OUT 指令的作⽤是将AL或AX的内容送⾄DX中的内容所指⽰的端⼝中。当所送内容是⼀个字时,必须使⽤连续的两个端⼝号,端⼝地址(即DX中
的内容)只能取偶数。
总⽽⾔之,IN和OUT指令提供了字和字节两种使⽤⽅式,选⽤哪⼀种,则取决于外设端⼝宽度。8088是准16位机,外部数据总线是8位,因⽽只有字节传送指令,⽽8086还有字传送指令。以8086位例,将12位A/D变换器所得数字量输⼊,这时,A/D变换器应使⽤⼀个字端⼝,输⼊数据的程序为:
MOV DX, 02F0H ;设该端⼝为02F0H
IN AX, DX
2. 输出OUT指令
输出OUT指令完成将信息从CPU通过累加器传送到I/O的操作。与输⼊IN指令同样的原因,输出OUT指
令的语法格式也有长格式和短格式之分。
长格式语法格式为:
OUT PORT, AL(字节)
OUT PORT, AX(字)
执⾏的操作:
(PORT)<-(AL)(字节)
(PORT+1,PORT)<-(AX)(字)
短格式语法格式为:
OUT DX, AL(字节)
OUT DX, AX(字)
执⾏的操作:
((DX))<-(AL)(字节)
((DX)+1, (DX))<-AX(字)
以8086为例:
OUT PORT, AL ;直接的字节输出,PORT规定与IN指令相同
OUT PORT, AX
OUT DX, AL ;间接地字节输出
OUT DX, AX
MOV AL, 05H
OUT 27H, AL ;将字节05H传送到地址27H的端⼝
。。。
其他各种命令不赘述,不会可以查。直接进⼊汇编语⾔在C++中的应⽤
五、汇编语⾔在C++中的应⽤
》内联汇编的优点
因为在Visual C++中使⽤内联汇编不需要额外的编译器和联接器,且可以处理Visual C++中不能处理的⼀些事情,同时可以使⽤在C/C++中的变量,所以⾮常⽅便。
内联汇编代码不易于移植,如果你的程序打算在不同类型的机器(⽐如x86和Alpha)上运⾏,应当尽量避免使⽤内联汇编,这时可以使⽤MASM,因为MASM⽀持更⽅便的宏指令和数据指⽰符。
1. __asm语法
__asm关键字⽤来调⽤内联汇编,可以出现在任何合法的C或C++声明中。它不能单独出现,后⾯必须有汇编指令,可以是⼀条汇编指令、⼤括号括起来的⼀组代码,或者⾄少是⼤括号括起来的空代码。术语“__asm块”指的是任何单独的⼀条指令或⼀组指令,可以不包括在⼤括号⾥。
第⼀种语法格式:
__asm 汇编指令
第⼆种语法格式:
__asm
{
汇编指令列表
}
例如,下⾯的代码是⼀个简单的⼤括号⾥的__asm块:
__asm
{
mov al, 4
mov dx, 0xB008
汇编语言结束指令 out dx, al
}
另外,在每⼀条汇编指令前加上__asm,与前⾯的⽅法是⼀样的作⽤。例如:
__asm mov al, 4
__asm mov dx, 0xB0008
__asm out dx, al
上⾯的两个例⼦所⽣成的代码是相同的,但是在括号⾥的__asm块这种⽅式更具优势,因为⼤括号可以使汇编指令很清楚地和C或C++代码分开,避免了⽆意义的__asm关键字重复。另外,⼤括号还可以避免引起歧义。如果想把C或C++代码和__asm块放在同⼀⾏,则必须把这个__asm块放在括号⾥。如果没有括号,编译器就不能确定汇编代码结束和C或C++代码起始的位置。
另外,由于⼤括号⾥的语句和⼀般的MASM语句格式⼀样,所以可以很⽅便地从现有的MASM源程序⾥复制。
不像C或C++中的"{}",__asm块中的"{}"不会影响C或C++变量的作⽤范围。同时,__asm块可以嵌套,嵌套也不会影响变量的作⽤范围。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论