汇编语言基本关键字

合集下载

C语言中嵌套汇编语言

C语言中嵌套汇编语言

在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, 2MOV DX, 0xD007OUT 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 关键字,因此推荐使用第一种方法。

汇编语言

汇编语言

处理器指令格式汇编语言程序设计程序用程序设计语言编写,由指令构成指令由操作码和操作数(地址码)组成操作码(Opcode)表明处理器执行的操作►例如数据传送、加法运算、跳转等操作操作码操作数►汇编语言使用指令助记符表示操作数(Operand)是参与操作的数据对象►主要以寄存器名或地址形式指明数据的来源►汇编语言使用寄存器、常量、变量等形式表示传送指令的助记符:MOV(取自Move)►将数据从一个位置传送到另一个位置►类似高级语言的赋值语句mov dest,src;源操作数src:被传送的数据或数据所在的位置;目的操作数dest:数据将要传送到的位置mov dest,src 操作码操作数目的操作数dest 30H30H被传送的数据源操作数src使用最多、最基本的数据传送指令传送指令的助记符:MOV (取自Move )►将数据从一个位置传送到另一个位置►类似高级语言的赋值语句mov dest,src ;源操作数src :被传送的数据或数据所在的位置;目的操作数dest :数据将要传送到的位置mov eax, 100;EAX ←100 (常量)mov eax, dvar;EAX ←dvar (变量)mov eax,ebx;EAX ←EBX(寄存器)指令格式(Instruction Format处理器指令的二进制编码0~4字节1~3字节0/1字节0/1字节0/1/2/4字节0/1/2/4字节指令前缀操作码Mod R/MSIB位移量立即数代码格式(Code Format)机器代码(Machine Code)操作码操作数指令的一般格式IA-32处理器的指令格式低字节高字节IA-32处理器采用可变长度指令格式操作码►可选的指令前缀(用于扩展指令功能,0~4字节)►1~3字节的主要操作码操作数►可选的寻址方式域(包括ModR/M和SIB字段,0或1字节)►可选的位移量(0、1、2或4字节)►可选的立即数(0、1、2或4字节)mov eax,ebx ;机器代码:8B C3mov eax,[ebx] ;机器代码:8B 03mov eax,[ebx+esi*4+80h] ;机器代码:8B 84 B3 80 00 00 000~4字节1~3字节0/1字节0/1字节0/1/2/4字节0/1/2/4字节指令前缀操作码Mod R/M SIB位移量立即数IA-32处理器的指令格式低字节高字节汇编语言语句格式汇编语言程序设计源程序由语句组成通常一个语句常占一行(支持续行符“\”)一个语句不超过132个字符,4个部分执行性语句:表达处理器指令,实现功能标号: 硬指令助记符操作数, 操作数;注释 说明性语句:表达伪指令,控制汇编方式名字伪指令助记符参数, 参数, …;注释1. 标号与名字标号:执行性语句中标号与名字是用户定义的标识符►冒号分隔►表示处理器指令在主存中的逻辑地址►指示分支、循环等程序的目的地址名字:说明性语句中►空格或制表符分隔►变量名、段名、子程序名等►反映变量、段和子程序等的逻辑地址标识符(最多由31个字母、数字及规定的特殊符号组成►不能以数字开头►一个源程序中,用户定义的每个标识符必须唯一►不能是保留字(Reserved Word)=关键字(Key Word)•硬指令助记符:MOV …•伪指令助记符:BYTE …•操作符:OFFSET …•寄存器名:EAX …取名原则类似高级语言,但默认不区别大小写字母助记符是帮助记忆指令功能的符号►硬指令助记符表示处理器指令►伪指令助记符表达一个汇编命令处理器指令示例:传送指令MOV 伪指令示例:字节变量定义►助记符:BYTE (或DB )►功能:在主存占用若干存储空间,用于保存变量值msg byte 'Hello, Assembly !',13,10,0msg byte 'Hello, Assembly !',13,10,0处理器指令的操作数:表示参与操作的对象►具体的常量►保存在寄存器的数据►保存在存储器中的变量►逗号前常是目的操作数,逗号后常是源操作数 伪指令的参数:►常量、变量名、表达式等►可以有多个,参数之间用逗号分隔msg byte 'Hello, Assembly !',13,10,0msg byte 'Hello, Assembly !',13,10,0mov eax,offset msgmov eax,offset msg语句中分号后的内容是注释►对指令或程序进行说明,使用英文或中文均可►汇编程序不对它们做任何处理►注释利于阅读,应养成书写注释的好习惯►注释可以用分号开头,占用一个语句行;数据段的变量msg byte 'Hello, Assembly !',13,10,0 ;定义字符串;代码段的指令mov eax,offset msg ;EAX获得msg的偏移地址语句的4个组成部分要用分隔符分开►标号后的冒号►注释前的分号►操作数间和参数间的逗号►分隔其他部分采用一个或多个空格或制表符分隔符都是英文标点标号: 硬指令助记符 操作数, 操作数;注释名字 伪指令助记符 参数, 参数, … ;注释汇编语言不直接支持结构化程序设计为了清晰表达语句,以及整个源程序,建议:►标号和名字从首列开始书写►通过制表符对齐指令助记符和注释部分►助记符与操作数和参数之间用空格或者制表符分隔标号: 硬指令助记符操作数, 操作数;注释名字伪指令助记符参数, 参数, …;注释首列对齐对齐;执行性语句标号: 硬指令助记符操作数, 操作数;注释start: mov eax,offset msg ;EAX获得msg的偏移地址;说明性语句名字伪指令助记符参数, 参数, …;注释msg byte 'Hello, Assembly !',13,10,0 ;定义字符串源程序框架汇编语言程序设计汇编程序为汇编语言制定了严格的语法规范►例如,语句格式、标识符定义、保留字、注释符等 汇编程序也为源程序书写设计了框架结构►数据段、代码段等的定义►程序起始执行的位置►汇编结束的标示►…基于MASM 6.x简化段定义格式配合IO32.INC和IO32.LIB文件具备键盘输入和显示器输出子程序本书的MASM源程序框架;eg0000.asm in Windows Consoleinclude io32.inc ;包含32位输入输出文件.data ;定义数据段…;数据定义(数据待填).code ;定义代码段start: ;程序执行起始位置…;主程序(指令待填)exit 0 ;程序正常执行终止…;子程序(指令待填)end start ;汇编结束用于声明常用的常量定义、过程说明、共享的子程序库等►相当于C和C++语言中,包含头文件的作用本书的IO32.INC包含文件的前3个语句include io32.inc .686 ;32位指令.model flat,stdcall;选择平展模型,标准调用规范option casemap:none;告知MASM区分用户定义标识符的大小写MASM支持段的简化定义►数据段定义伪指令.DATA ;创建一个数据段►代码段定义伪指令.CODE ;创建一个代码段►堆栈段定义伪指令.STACK ;创建一个堆栈段(Windows自动维护堆栈段,用户可以不必设置)include io32.inc .data …;数据定义.code …;程序指令程序的开始和结束程序开始执行的位置►使用一个标号(例如:START )►作为汇编结束END 伪指令的参数 应用程序执行终止►语句“EXIT 0”终止程序执行►返回操作系统,并提供一个返回代码(0)源程序汇编结束►使用END 伪指令语句执行终止≠汇编结束执行终止≠汇编结束.codestart: …exit 0…end start;eg0000.asm in Windows Console include io32.inc.data;数据定义.codestart:;主程序exit 0;子程序end start;数据段…;代码段,主程序…;代码段,子程序…本书的简化表达信息显示程序汇编语言程序设计#include <stdio.h>int main(){printf("Hello, world !\n");exit(0);}Hello, world !显示信息printf("Hello, world !\n");在数据段给出这个字符串形式的信息:;数据段msg byte 'Hello, Assembly!',13,10,0;定义要显示的字符串在代码段编写显示字符串的程序:;代码段mov eax,offset msg ;指定字符串的偏移地址call dispmsg ;调用I/O子程序显示信息"Hello, world !\n""\n"printf( );字符串结尾字符;eg0000.asm in Windows Consoleinclude io32.inc .data;数据定义.codestart:;主程序exit 0;子程序end start;数据段…;代码段,主程序…;代码段,子程序…本书的简化表达;eg0101.asminclude io32.inc.data ;数据段msg byte 'Hello, Assembly!',13,10,0 .code ;代码段start: ;程序执行起始位置mov eax,offset msgcall dispmsgexit 0 ;程序正常执行终止end start ;汇编结束Hello, Assembly !运行结果汇编程序通常不提供任何函数或程序库必须利用操作系统的编程资源本书配套键盘输入和显示器输出的I/O子程序含IO32.INC和IO32.LIB,需要包含文件声明源程序文件开始使用包含命令声明INCLUDE IO32.INC 子程序调用方法MOV EAX,入口参数CALL 子程序名子程序名DISPMSG入口参数EAX=字符串地址功能说明显示字符串(以0结尾);数据段,字符串定义msg byte 'Hello, Assembly!',13,10,0 ;字符串;代码段,字符串显示mov eax,offset msg ;指定字符串的偏移地址call dispmsg ;调用I/O子程序显示信息C语言格式符子程序名功能说明printf("%s",a)DISPMSG显示字符串(以0结尾)printf("%c",a)DISPC显示一个字符printf("\n")DISPCRLF光标回车换行,到下行首列DISPRD显示8个32位通用寄存器内容DISPRF显示6个状态标志的状态printf("%X",a)DISPHD以十六进制形式显示8位数据printf("%u",a)DISPUID显示无符号十进制整数printf("%d",a)DISPSID显示有符号十进制整数C语言格式符子程序名功能说明scanf("%s",&a)READMSG输入一个字符串(回车结束)scanf("%c",&a)READC输入一个字符(回显)scanf("%X",&a)READHD输入8位十六进制数据scanf("%u",&a)READUID 输入无符号十进制整数(≤232-1)scanf("%d",&a)READSID 输入有符号十进制整数(-231~231-1)include io32.inc.datamsg byte 'Hello, Assembly!',13,10,0.code start: mov eax,offset msg call dispmsg exit 0end start 汇编语言程序#include <stdio.h>int main(){printf("Hello, world !\n");exit(0);}C语言程序。

汇编语言equ的用法

汇编语言equ的用法

汇编语言equ的用法汇编语言是一种底层的编程语言,直接操作计算机硬件,可以实现高效的算法和程序。

在汇编语言中,equ是一个非常重要的关键字,用于定义符号常量。

equ是equation的缩写,意思是“等式”。

在汇编语言中,我们可以使用equ关键字来定义一个符号常量,它的值在整个程序中不会发生改变。

这样做的好处是可以提高程序的可读性和可维护性,因为我们可以使用有意义的符号代替数字或字符串,更加清晰地表达程序的含义。

equ的语法格式为:符号名 equ 表达式其中符号名是我们所定义的常量的名字,可以使用任何合法的汇编语言标识符;表达式是一个算术表达式,可以包含数字、符号常量、运算符等。

下面是一个例子,定义了一个名为counter的符号常量,它的值为10:counter equ 10在程序中,我们可以使用counter代替数字10,如下所示:mov eax, counter ;将eax寄存器的值设置为10使用符号常量可以使程序更加易读易懂,并且方便修改。

如果我们要修改counter的值,只需要修改它的定义,整个程序中所有使用counter的地方都会自动更新。

除了定义数字常量,我们还可以使用equ定义字符串常量,如下所示:msg db 'Hello, world!', 0这里我们定义了一个字符串常量msg,它的值为“Hello, world!”,0是字符串的结束符号。

我们可以使用msg来输出这个字符串:mov eax, 4 ;调用系统调用4,输出字符串mov ebx, 1 ;输出到标准输出mov ecx, msg ;msg是字符串常量的符号名mov edx, 13 ;字符串的长度int 0x80 ;调用系统调用在程序中使用符号常量可以使程序更加清晰易懂,并且方便修改。

但是需要注意的是,符号常量只是一种宏定义,它不会分配内存空间,也不会检查定义的有效性。

因此,在使用符号常量时,需要确保其定义的正确性和合理性。

汇编语言---GCC内联汇编

汇编语言---GCC内联汇编

汇编语⾔---GCC内联汇编GCC⽀持在C/C++代码中嵌⼊汇编代码,这些代码被称作是"GCC Inline ASM"(GCC内联汇编);⼀、基本内联汇编GCC中基本的内联汇编⾮常易懂,格式如下:__asm__ [__volatile__] ("instruction list");其中,1.__asm__:它是GCC定义的关键字asm的宏定义(#define __asm__ asm),它⽤来声明⼀个内联汇编表达式,所以,任何⼀个内联汇编表达式都以它开头,它是必不可少的;如果要编写符合ANSI C标准的代码(即:与ANSI C兼容),那就要使⽤__asm__;2.__volatile__:它是GCC关键字volatile的宏定义;这个选项是可选的;它向GCC声明"不要动我所写的instruction list,我需要原封不动地保留每⼀条指令";如果不使⽤__volatile__,则当你使⽤了优化选项-O进⾏优化编译时,GCC将会根据⾃⼰的判断来决定是否将这个内联汇编表达式中的指令优化掉;如果要编写符合ANSI C标准的代码(即:与ANSI C兼容),那就要使⽤__volatile__;3.instruction list:它是汇编指令列表;它可以是空列表,⽐如:__asm__ __volatile__("");或__asm__("");都是合法的内联汇编表达式,只不过这两条语句什么都不做,没有什么意义;但并⾮所有"instruction list"为空的内联汇编表达式都是没意义的,⽐如:__asm__("":::"memory");就是⾮常有意义的,它向GCC声明:"我对内存做了改动",这样,GCC在编译的时候,就会将此因素考虑进去;例如:__asm__("movl %esp,%eax");或者是__asm__("movl 1,1,0x80");或者是__asm__("movl 1,1,0x80");instruction list的编写规则:当指令列表⾥⾯有多条指令时,可以在⼀对双引号中全部写出,也可将⼀条或多条指令放在⼀对双引号中,所有指令放在多对双引号中;如果是将所有指令写在⼀对双引号中,那么,相邻俩条指令之间必须⽤分号";"或换⾏符(\n)隔开,如果使⽤换⾏符(\n),通常\n后⾯还要跟⼀个\t;或者是相邻两条指令分别单独写在两⾏中;规则1:任意两条指令之间要么被分号(;)或换⾏符(\n)或(\n\t)分隔开,要么单独放在两⾏;规则2:单独放在两⾏的⽅法既可以通过\n或\n\t的⽅法来实现,也可以真正地放在两⾏;规则3:可以使⽤1对或多对双引号,每1对双引号⾥⾯可以放1条或多条指令,所有的指令都要放在双引号中;例如,下⾯的内联汇编语句都是合法的:__asm__("movl %eax,%ebx sti popl %edi subl %ecx,%ebx");__asm__("movl %eax,%ebx; sti popl %edi; subl %ecx,%ebx");__asm__("movl %eax,%ebx; sti\n\t popl %edi subl %ecx,%ebx");如果将指令放在多对双引号中,则,除了最后⼀对双引号之外,前⾯的所有双引号⾥的最后⼀条指令后⾯都要有⼀个分号(;)或(\n)或(\n\t);⽐如,下⾯的内联汇编语句都是合法的:__asm__("movl %eax,%ebx sti\n" "popl %edi;" "subl %ecx,%bx");__asm__("movl %eax,%ebx; sti\n\t" "popl %edi; subl %ecx,%ebx");__asm__("movl %eax,%ebx; sti\n\t popl %edi\n" "subl %ecx,%ebx"); ⼆、带有C/C++表达式的内联汇编GCC允许你通过C/C++表达式指定内联汇编中"instruction list"中的指令的输⼊和输出,你甚⾄可以不关⼼到底使⽤哪些寄存器,完全依靠GCC来安排和指定;这⼀点可以让程序员免去考虑有限的寄存器的使⽤,也可以提⾼⽬标代码的效率;1.带有C/C++表达式的内联汇编语句的格式:__asm__ [__volatile__]("instruction list":Output:Input:Clobber/Modify);圆括号中的内容被冒号":"分为四个部分:A. 如果第四部分的"Clobber/Modify"可以为空;如果"Clobber/Modify"为空,则其前⾯的冒号(:)必须省略;⽐如:语句 __asm__("movl%%eax,%%ebx":"=b"(foo):"a"(inp):);是⾮法的,⽽语句__asm__("movl %%eax,%%ebx":"=b"(foo):"a"(inp));则是合法的;B.如果第⼀部分的"instruction list"为空,则input、output、Clobber/Modify可以为空,也可以不为空;⽐如,语句__asm__("":::"memory");和语句__asm__(""::);都是合法的写法;C. 如果Output、Input和Clobber/Modify都为空,那么,Output、Input之前的冒号(:)可以省略,也可以不省略;如果都省略,则此汇编就退化为⼀个基本汇编,否则,仍然是⼀个带有C/C++表达式的内联汇编,此时"instruction list"中的寄存器的写法要遵循相关规定,⽐如:寄存器名称前⾯必须使⽤两个百分号(%%);基本内联汇编中的寄存器名称前⾯只有⼀个百分号(%);⽐如,语句__asm__("movl %%eax,%%ebx"::);__asm__("movl %%eax,%%ebx":);和语句__asm__("movl %%eax,%%ebx");都是正确的写法,⽽语句__asm__("movl %eax,%ebx"::);__asm__("movl%eax,%ebx":);和语句__asm__("movl %%eax,%%ebx");都是错误的写法;D.如果Input、Clobber/Modify为空,但Output不为空,则,Input前⾯的冒号(:)可以省略,也可以不省略;⽐如,语句__asm__("movl%%eax,%%ebx":"=b"(foo):);和语句__asm__("movl %%eax,%%ebx":"=b"(foo));都是正确的;E. 如果后⾯的部分不为空,⽽前⾯的部分为空,则,前⾯的冒号(:)都必须保留,否则⽆法说明不为空的部分究竟是第⼏部分;⽐如,Clobber/Modify、Output为空,⽽Input不为空,则Clobber/Modify前⾯的冒号必须省略,⽽Output前⾯的冒号必须保留;如果Clobber/Modify不为空,⽽Input和Output都为空,则Input和Output前⾯的冒号都必须保留;⽐如,语句 __asm__("movl %%eax,%%ebx"::"a"(foo));和__asm__("movl %%eax,%%ebx":::"ebx");注意:基本内联汇编中的寄存器名称前⾯只能有⼀个百分号(%),⽽带有C/C++表达式的内联汇编中的寄存器名⾂前⾯必须有两个百分号(%%);2.Output:Output部分⽤来指定当前内联汇编语句的输出,称为输出表达式;格式为: "操作约束"(输出表达式)例如:__asm__("movl%%rc0,%1":"=a"(cr0));这个语句中的Output部分就是("=a"(cr0)),它是⼀个操作表达式,指定了⼀个内联汇编语句的输出部分;Output部分由两个部分组成:由双引号括起来的部分和由圆括号括起来的部分,这两个部分是⼀个Output部分所不可缺少的部分;⽤双引号括起来的部分就是C/C++表达式,它⽤于保存当前内联汇编语句的⼀个输出值,其操作就是C/C++赋值语句"="的左值部分,因此,圆括号中指定的表达式只能是C/C++中赋值语句的左值表达式,即:放在等号=左边的表达式;也就是说,Output部分只能作为C/C++赋值操作左边的表达式使⽤;⽤双引号括起来的部分就指定了C/C++中赋值表达式的右值来源;这个部分被称作是"操作约束"(Operation Constraint),也可以称为"输出约束";在这个例⼦中的操作约束是"=a",这个操作约束包含两个组成部分:等号(=)和字母a,其中,等号 (=)说明圆括号中的表达式cr0是⼀个只写的表达式,只能被⽤作当前内联汇编语句的输出,⽽不能作为输⼊;字母a是寄存器EAX/AX/AL的缩写,说明cr0的值要从寄存器EAX中获取,也就是说cr0=eax,最终这⼀点被转化成汇编指令就是:movl %eax,address_of_cr0;注意:很多⽂档中都声明,所有输出操作的的操作约束都必须包含⼀个等号(=),但是GCC的⽂档中却明确地声明,并⾮如此;因为等号(=)约束说明当前的表达式是⼀个只写的,但是还有另外⼀个符号:加号(+),也可以⽤来说明当前表达式是可读可写的;如果⼀个操作约束中没有给出这两个符号中的任何⼀个,则说明当前表达式是只读的;因此,对于输出操作来说,肯定必须是可写的,⽽等号(=)和加号(+)都可表⽰可写,只不过加号(+)同时也可以表⽰可读;所以, 对于⼀个输出操作来说,其操作约束中只要包含等号(=)或加号(+)中的任意⼀个就可以了;等号(=)与加号(+)的区别:等号(=)表⽰当前表达式是⼀个纯粹的输出操作,⽽加号(+)则表⽰当前表达式不仅仅是⼀个输出操作,还是⼀个输⼊操作;但⽆论是等号(=)还是加号(+),所表⽰的都是可写,只能⽤于输出,只能出现在Output部分,⽽不能出现在Input部分;在Output部分可以出现多个输出操作表达式,多个输出操作表达式之间必须⽤逗号(,)隔开;3、Input:Input部分⽤来指定当前内联汇编语句的输⼊;称为输⼊表达式;格式为: "操作约束"(输⼊表达式)例如:__asm__("movl %0,%%db7"::"a"(cpu->db7));其中,表达式"a"(cpu->db7)就称为输⼊表达式,⽤于表⽰⼀个对当前内联汇编的输⼊;Input同样也由两部分组成:由双引号括起来的部分和由圆括号括起来的部分;这两个部分对于当前内联汇编语句的输⼊来说也是必不可少的;在这个例⼦中,由双引号括起来的部分是"a",⽤圆括号括起来的部分是(cpu->db7);⽤双引号括起来的部分就是C/C++表达式,它为当前内联汇编语句提供⼀个输⼊值;在这⾥,圆括号中的表达式cpu->db7是⼀个C/C++语⾔的表达式,它不必是左值表达式,也就是说,它不仅可以是放在C/C++赋值操作左边的表达式,还可以是放在C/C++赋值操作右边的表达式;所以,Input可以是⼀个变量、⼀个数字,还可以是⼀个复杂的表达式(如:a+b/c*d);⽐如,上例还可以这样写:__asm__("movl %0,%%db7"::"a"(foo));__asm__("movl%0,%%db7"::"a"(0x12345));__asm__("movl %0,%%db7"::"a"(va:vb/vc));⽤双引号括起来的部分就是C/C++中赋值表达式的右值表达式,⽤于约束当前内联汇编语句中的当前输⼊;这个部分也成为"操作约束",也可以成为是"输⼊约束";与输出表达式中的操作约束不同的是,输⼊表达式中的操作约束不允许指定等号(=)约束或加号(+)约束,也就是说,它只能是只读的;约束中必须指定⼀个寄存器约束;例⼦中的字母a表⽰当前输⼊变量cpu->db7要通过寄存器EAX输⼊到当前内联汇编语句中;三、操作约束:Operation Constraint操作约束只会出现在带有C/C++表达式的内联汇编语句中;每⼀个Input和Output表达式都必须指定⾃⼰的操作约束Operation Constraint;约束的类型有:寄存器约束、内存约束、⽴即数约束、通⽤约束;操作表达式的格式:"约束"(C/C++表达式)即:"Constraint"(C/C++ expression)1.寄存器约束:当你的输⼊或输出需要借助于⼀个寄存器时,你需要为其指定⼀个寄存器约束;可以直接指定⼀个寄存器名字;⽐如:__asm__ __volatile__("movl %0,%%cr0"::"eax"(cr0));也可以指定寄存器的缩写名称;⽐如:__asm__ __volatile__("movl %0,%%cr0"::"a"(cr0));如果指定的是寄存器的缩写名称,⽐如:字母a;那么,GCC将会根据当前操作表达式中C/C++表达式的宽度来决定使⽤%eax、%ax还是%al;⽐如:unsigned short __shrt;__asm__ __volatile__("movl%0,%%bx"::"a"(__shrt));由于变量__shrt是16位⽆符号类型m⼤⼩是两个字节,所以,编译器编译出来的汇编代码中,则会让此变量使⽤寄存器%ax;⽆论是Input还是Output操作约束,都可以使⽤寄存器约束;常⽤的寄存器约束的缩写:r:I/O,表⽰使⽤⼀个通⽤寄存器,由GCC在%eax/%ax/%al、%ebx/%bx/%bl、%ecx/%cx/%cl、%edx/%dx/%dl中选取⼀个GCC认为是合适的;q:I/O,表⽰使⽤⼀个通⽤寄存器,与r的意义相同;g:I/O,表⽰使⽤寄存器或内存地址;m:I/O,表⽰使⽤内存地址;a:I/O,表⽰使⽤%eax/%ax/%al;b:I/O,表⽰使⽤%ebx/%bx/%bl;c:I/O,表⽰使⽤%ecx/%cx/%cl;d:I/O,表⽰使⽤%edx/%dx/%dl;D:I/O,表⽰使⽤%edi/%di;S:I/O,表⽰使⽤%esi/%si;f:I/O,表⽰使⽤浮点寄存器;t:I/O,表⽰使⽤第⼀个浮点寄存器;u:I/O,表⽰使⽤第⼆个浮点寄存器;A:I/O,表⽰把%eax与%edx组合成⼀个64位的整数值;o:I/O,表⽰使⽤⼀个内存位置的偏移量;V:I/O,表⽰仅仅使⽤⼀个直接内存位置;i:I/O,表⽰使⽤⼀个整数类型的⽴即数;n:I/O,表⽰使⽤⼀个带有已知整数值的⽴即数;F:I/O,表⽰使⽤⼀个浮点类型的⽴即数;2.内存约束:如果⼀个Input/Output操作表达式的C/C++表达式表现为⼀个内存地址(指针变量),不想借助于任何寄存器,则可以使⽤内存约束;⽐如:__asm__("lidt %0":"=m"(__idt_addr));或__asm__("lidt %0"::"m"(__idt_addr));内存约束使⽤约束名"m",表⽰的是使⽤系统⽀持的任何⼀种内存⽅式,不需要借助于寄存器;使⽤内存约束⽅式进⾏输⼊输出时,由于不借助于寄存器,所以,GCC不会按照你的声明对其做任何的输⼊输出处理;GCC只会直接拿来使⽤,对这个C/C++ 表达式⽽⾔,究竟是输⼊还是输出,完全依赖于你写在"instruction list"中的指令对其操作的⽅式;所以,不管你把操作约束和操作表达式放在Input部分还是放在Output部分,GCC编译⽣成的汇编代码都是⼀样的,程序的执⾏结果也都是正确的;本来我们将⼀个操作表达式放在Input或Output部分是希望GCC能为我们⾃动通过寄存器将表达式的值输⼊或输出;既然对于内存约束类型的操作表达式来说,GCC不会为它做任何事情,那么放在哪⾥就⽆所谓了;但是从程序员的⾓度来看,为了增强代码的可读性,最好能够把它放在符合实际情况的地⽅;3.⽴即数约束:如果⼀个Input/Output操作表达式的C/C++表达式是⼀个数字常数,不想借助于任何寄存器或内存,则可以使⽤⽴即数约束;由于⽴即数在C/C++表达式中只能作为右值使⽤,所以,对于使⽤⽴即数约束的表达式⽽⾔,只能放在Input部分;⽐如:__asm__ __volatile__("movl %0,%%eax"::"i"(100));⽴即数约束使⽤约束名"i"表⽰输⼊表达式是⼀个整数类型的⽴即数,不需要借助于任何寄存器,只能⽤于Input部分;使⽤约束名"F"表⽰输⼊表达式是⼀个浮点数类型的⽴即数,不需要借助于任何寄存器,只能⽤于Input部分;4.通⽤约束:约束名"g"可以⽤于输⼊和输出,表⽰可以使⽤通⽤寄存器、内存、⽴即数等任何⼀种处理⽅式;约束名"0,1,2,3,4,5,6,7,8,9"只能⽤于输⼊,表⽰与第n个操作表达式使⽤相同的寄存器/内存;通⽤约束"g"是⼀个⾮常灵活的约束,当程序员认为⼀个C/C++表达式在实际操作中,⽆论使⽤寄存器⽅式、内存⽅式还是⽴即数⽅式都⽆所谓时,或者程序员想实现⼀个灵活的模板,以让GCC可以根据不同的C/C++表达式⽣成不同的访问⽅式时,就可以使⽤通⽤约束g;例如:#define JUST_MOV(foo) __asm__("movl %0,%%eax"::"g"(foo))则,JUST_MOV(100)和JUST_MOV(var)就会让编译器产⽣不同的汇编代码;对于JUST_MOV(100)的汇编代码为:#APPmovl $100,%eax #⽴即数⽅式;#NO_APP对于JUST_MOV(var)的汇编代码为:#APPmovl 8(%ebp),%eax #内存⽅式;#NO_APP像这样的效果,就是通⽤约束g的作⽤;5.修饰符:等号(=)和加号(+)作为修饰符,只能⽤于Output部分;等号(=)表⽰当前输出表达式的属性为只写,加号(+)表⽰当前输出表达式的属性为可读可写;这两个修饰符⽤于约束对输出表达式的操作,它们俩被写在输出表达式的约束部分中,并且只能写在第⼀个字符的位置;符号&也写在输出表达式的约束部分,⽤于约束寄存器的分配,但是只能写在约束部分的第⼆个字符的位置上;⽤符号&进⾏修饰时,等于向GCC声明:"GCC不得为任何Input操作表达式分配与此Output操作表达式相同的寄存器";其原因是修饰符&意味着被其修饰的Output操作表达式要在所有的Input操作表达式被输⼊之前输出;即:GCC会先使⽤输出值对被修饰符&修饰的Output操作表达式进⾏填充,然后,才对Input操作表达式进⾏输⼊;这样的话,如果不使⽤修饰符&对Output操作表达式进⾏修饰,⼀旦后⾯的Input操作表达式使⽤了与Output操作表达式相同的寄存器,就会产⽣输⼊输出数据混乱的情况;相反,如果没有⽤修饰符&修饰输出操作表达式,那么,就意味着GCC会先把Input操作表达式的值输⼊到选定的寄存器中,然后经过处理,最后才⽤输出值填充对应的Output操作表达式;所以, 修饰符&的作⽤就是要求GCC编译器为所有的Input操作表达式分配别的寄存器,⽽不会分配与被修饰符&修饰的Output操作表达式相同的寄存器;修饰符&也写在操作约束中,即:&约束;由于GCC已经规定加号(+)或等号(=)占据约束的第⼀个字符,那么& 约束只能占⽤第⼆个字符;例如:int __out, __in1, __in2;__asm__("popl %0\n\t""movl %1,%%esi\n\t""movl %2,%%edi\n\t":"=&a"(__out):"r"(__in1),"r"(__in2));注意: 如果⼀个Output操作表达式的寄存器约束被指定为某个寄存器,只有当⾄少存在⼀个Input操作表达式的寄存器约束为可选约束(意思是GCC可以从多个寄存器中选取⼀个,或使⽤⾮寄存器⽅式)时,⽐如"r"或"g"时,此Output操作表达式使⽤符号&修饰才有意义;如果你为所有的Input操作表达式指定了固定的寄存器,或使⽤内存/⽴即数约束时,则此Output操作表达式使⽤符号&修饰没有任何意义;⽐如:__asm__("popl%0\n\t" "movl %1,%esi\n\t" "movl %2,%edi\n\t" :"=&a"(__out) :"m"(__in1),"c"(__in2));此例中的Output操作表达式完全没有必要使⽤符号&来修饰,因为__in1和__in2都已经被指定了固定的寄存器,或使⽤了内存⽅式,GCC⽆从选择;如果你已经为某个Output操作表达式指定了修饰符&,并指定了固定的寄存器,那么,就不能再为任何Input操作表达式指定这个寄存器了,否则会出现编译报错;⽐如:__asm__("popl %0; movl %1,%%esi; movl %2,%%edi;":"=&a"(__out):"a"(__in1),"c"(__in2));对这条语句的编译就会报错;相反,你也可以为Output指定可选约束,⽐如"r"或"g"等,让GCC为此Output操作表达式选择合适的寄存器,或使⽤内存⽅式,GCC在选择的时候,会排除掉已经被Input操作表达式所使⽤过的所有寄存器,然后在剩下的寄存器中选择,或者⼲脆使⽤内存⽅式;⽐如:__asm__("popl %0; movl %1,%%esi; movl %2,%%edi;":"=&r" (__out):"a"(__in1),"c"(__in2));这三个修饰符只能⽤在Output操作表达式中,⽽修饰符%则恰恰相反,它只能⽤在Input操作表达式中;修饰符%⽤于向GCC声明:"当前Input操作表达式中的C/C++表达式可以与下⼀个Input操作表达式中的C/C++表达式互换";这个修饰符⼀般⽤于符合交换律运算的地⽅;⽐如:加、乘、按位与&、按位或|等等;例如:__asm__("addl %1,%0\n\t":"=r"(__out):"%r"(__in1),"0"(__in2));其中,"0"(__in2)表⽰使⽤与第⼀个Input操作表达式("r"(__in1))相同的寄存器或内存;由于使⽤符号%修饰__in1的寄存器⽅式r,那么就表⽰,__in1与__in2可以互换位置;加法的两个操作数交换位置之后,和不变;修饰符 I/O 意义= O 表⽰此Output操作表达式是只写的+ O 表⽰此Output操作表达式是可读可写的& O 表⽰此Output操作表达式独占为其指定的寄存器% I 表⽰此Input操作表达式中的C/C++表达式可以与下⼀个Input操作表达式中的C/C++表达式互换四、占位符每⼀个占位符对应⼀个Input/Output操作表达式;带C/C++表达式的内联汇编中有两种占位符:序号占位符和名称占位符;1.序号占位符:GCC 规定:⼀个内联汇编语句中最多只能有10个Input/Output操作表达式,这些操作表达式按照他们被列出来的顺序依次赋予编号0到9;对于占位符中的数字⽽⾔,与这些编号是对应的;⽐如:占位符%0对应编号为0的操作表达式,占位符%1对应编号为1的操作表达式,依次类推;由于占位符前⾯要有⼀个百分号%,为了去边占位符与寄存器,GCC规定:在带有C/C++表达式的内联汇编语句的指令列表⾥列出的寄存器名称前⾯必须使⽤两个百分号(%%),⼀区别于占位符语法;GCC对占位符进⾏编译的时候,会将每⼀个占位符替换为对应的Input/Output操作表达式所指定的寄存器/内存/⽴即数;例如:__asm__("addl %1,%0\n\t":"=a"(__out):"m"(__in1),"a"(__in2));这个语句中,%0对应Output操作表达式"=a"(__out),⽽"=a"(__out)指定的寄存器是%eax,所以,占位符%0被替换为%eax;占位符%1对应Input操作表达式"m"(__in1),⽽"m"(__in1)被指定为内存,所以,占位符%1被替换位__in1的内存地址;⽤⼀句话描述:序号占位符就是前⾯描述的%0、%1、%2、%3、%4、%5、%6、%7、%8、%9;其中,每⼀个占位符对应⼀个Input/Output的C/C++表达式;2.名称占位符:由于GCC中限制这种占位符的个数最多只能由这10个,这也就限制了Input/Output操作表达式中C/C++表达式的数量做多只能有10个;如果需要的C/C++表达式的数量超过10个,那么,这些需要占位符就不够⽤了;GCC内联汇编提供了名称占位符来解决这个问题;即:使⽤⼀个名字字符串与⼀个C/C++表达式对应;这个名字字符串就称为名称占位符;⽽这个名字通常使⽤与C/C++表达式中的变量完全相同的名字;使⽤名字占位符时,内联汇编的Input/Output操作表达式中的C/C++表达式的格式如下:[name] "constraint"(变量)此时,指令列表中的占位符的书写格式如下:%[name]这个格式等价于序号占位符中的%0,%1,$2等等;使⽤名称占位符时,⼀个name对应⼀个变量;例如:__asm__("imull %[value1],%[value2]":[value2] "=r"(data2):[value1] "r"(data1),"0"(data2));此例中,名称占位符value1就对应变量data1,名称占位符value2对应变量data2;GCC编译的时候,同样会把这两个占位符分别替换成对应的变量所使⽤的寄存器/内存地址/⽴即数;⽽且也增强了代码的可读性;这个例⼦,使⽤序号占位符的写法如下:__asm__("imull %1,%0":"=r"(data2):"r"(data1),"0"(data2));五、寄存器/内存修改标⽰(Clobber/Modify)有时候,当你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进⾏修改,希望GCC在编译时能够将这⼀点考虑进去;那么你就可以在Clobber/Modify部分声明这些寄存器或内存;1.寄存器修改通知:这种情况⼀般发⽣在⼀个寄存器出现在指令列表中,但⼜不是Input/Output操作表达式所指定的,也不是在⼀些Input/Output操作表达式中使⽤"r"或"g"约束时由GCC选择的,同时,此寄存器被指令列表中的指令所修改,⽽这个寄存器只供当前内联汇编语句使⽤的情况;⽐如:__asm__("movl %0,%%ebx"::"a"(__foo):"bx");//这个内联汇编语句中,%ebx出现在指令列表中,并且被指令修改了,但是却未被任何Input/Output操作表达式是所指定,所以,你需要在Clobber/Modify部分指定"bx",以让GCC知道这⼀点;因为你在Input/Output操作表达式中指定的寄存器,或当你为⼀些Input/Output操作表达式使⽤"r"/"g"约束,让GCC为你选择⼀个寄存器时,GCC对这些寄存器的状态是⾮常清楚的,它知道这些寄存器是被修改的,你根本不需要在Clobber/Modify部分声明它们;但除此之外,GCC对剩下的寄存器中哪些会被当前内联汇编语句所修改则⼀⽆所知;所以,如果你真的在当前内联汇编指令中修改了它们,那么就最好在 Clobber/Modify部分声明它们,让GCC针对这些寄存器做相应的处理;否则,有可能会造成寄存器不⼀致,从⽽造成程序执⾏错误;在Clobber/Modify部分声明这些寄存器的⽅法很简单,只需要将寄存器的名字⽤双引号括起来就可以;如果要声明多个寄存器,则相邻两个寄存器名字之间⽤逗号隔开;例如:__asm__("movl %0,%%ebx; popl %%ecx"::"a"(__foo):"bx","cx");这个语句中,声明了bx和cx,告诉GCC:寄存器%ebx和%ecx可能会被修改,要求GCC考虑这个因素;寄存器名称串:"al"/"ax"/"eax":代表寄存器%eax"bl"/"bx"/"ebx":代表寄存器%ebx"cl"/"cx"/"ecx":代表寄存器%ecx"dl"/"dx"/"edx":代表寄存器%edx"si"/"esi":代表寄存器%esi"di"/"edi":代表寄存器%edi所以,只需要使⽤"ax","bx","cx","dx","si","di"就可以了,因为他们都代表对应的寄存器;如果你在⼀个内敛汇编语句的Clobber/Modify部分向GCC声明了某个寄存器内存发⽣了改变,GCC在编译时,如果发现这个被声明的寄存器的内容在此内联汇编之后还要继续使⽤,那么,GCC会⾸先将此寄存器的内容保存起来,然后在此内联汇编语句的相关代码⽣成之后,再将其内容回复;另外需要注意的是,如果你在Clobber/Modify部分声明了⼀个寄存器,那么这个寄存器将不能再被⽤作当前内敛汇编语句的Input/Output操作表达式的寄存器约束,如果Input/Output操作表达式的寄存器约束被指定为"r"/"g",GCC也不会选择已经被声明在Clobber /Modify部分中的寄存器;例如:__asm__("movl %0,%%ebx"::"a"(__foo):"ax","bx");这条语句中的Input操作表达式"a"(__foo)中已经指定了寄存器%eax,那么在Clobber/Modify部分中个列出的"ax"就是⾮法的;编译时,GCC会报错;2.内存修改通知:除了寄存器的内容会被修改之外,内存的内容也会被修改;如果⼀个内联汇编语句的指令列表中的指令对内存进⾏了修改,或者在此内联汇编出现的地⽅,内存内容可能发⽣改变,⽽被改变的内存地址你没有在其Output操作表达式中使⽤"m"约束,这种情况下,你需要使⽤在Clobber/Modify部分使⽤字符串"memory"向GCC声明:"在这⾥,内存发⽣了,或可能发⽣了改变";例如:void* memset(void* s, char c, size_t count){__asm__("cld\n\d""rep\n\t""stosb":/*no output*/:"a"(c),"D"(s),"c"(count):"cx","di","memory");return s;}如果⼀个内联汇编语句的Clobber/Modify部分存在"memory",那么GCC会保证在此内联汇编之前,如果某个内存的内容被装⼊了寄存器,那么,在这个内联汇编之后,如果需要使⽤这个内存处的内容,就会直接到这个内存处重新读取,⽽不是使⽤被存放在寄存器中的拷贝;因为这个时候寄存器中的拷贝很可能已经和内存处的内容不⼀致了;3.标志寄存器修改通知:当⼀个内联汇编中包含影响标志寄存器eflags的条件,那么也需要在Clobber/Modify部分中使⽤"cc"来向GCC声明这⼀点;。

汇编语言关键字

汇编语言关键字

汇编语言关键字在计算机科学领域中,汇编语言是一种低级别的编程语言,用于与计算机硬件进行直接交互。

它是计算机指令的文本表示,由一系列的关键字组成。

了解和熟悉汇编语言的关键字对于理解计算机底层运行机制以及进行系统级编程至关重要。

本文将介绍一些常见的汇编语言关键字,帮助读者了解其功能和用法。

一、数据传输指令数据传输指令用于将数据从一个位置传输到另一个位置。

以下是几个常见的数据传输指令:1. MOV:MOV指令用于将一个数据从一个位置复制到另一个位置。

它可以用于将数据从寄存器传输到内存,或者从内存传输到寄存器。

2. PUSH:用于将数据压入堆栈中。

堆栈是一种后进先出(LIFO)的数据结构,常用于存储临时变量和函数调用返回地址。

3. POP:与PUSH相反,POP指令用于将数据从堆栈中弹出,并存储到指定的位置。

二、算术和逻辑指令算术和逻辑指令用于对数据进行算术和逻辑运算。

以下是一些常用的算术和逻辑指令:1. ADD:用于将两个数相加,并将结果保存在指定位置。

可以用于寄存器之间的相加,也可以用于寄存器和内存之间的相加。

2. SUB:与ADD类似,SUB指令用于将一个数减去另一个数,并将结果保存在指定位置。

3. AND:用于执行按位与运算。

将两个数的每个对应位作与操作,并将结果保存在指定位置。

4. OR:与AND指令类似,OR指令用于执行按位或运算。

5. XOR:用于执行按位异或运算。

将两个数的每个对应位作异或操作,并将结果保存在指定位置。

三、分支和循环指令分支和循环指令用于控制程序的流程和执行顺序。

以下是一些常用的分支和循环指令:1. JMP:JMP指令用于无条件跳转到指定的地址。

可以用于实现程序的跳转和循环。

2. JZ和JNZ:JZ指令用于在前一个操作的结果为零时跳转到指定地址,而JNZ则相反,用于在结果不为零时跳转。

3. CMP:CMP指令用于比较两个数据的大小关系,并根据比较结果设置标志位,用于后续的条件跳转。

汇编语言学习第4章

汇编语言学习第4章

不同而不同。
(名字项,常称为标号) 标号是程序设计人员自己定义的表示符号,用来表示本语句的符号地址
(即该指令的偏移地址,也就是该单元与其所处段基址的偏移量)是可有
可无的,只有当需要用符号地址来访问该语句时才需要。 2.operation(操作符)
操作符项可以是指令、伪操作或宏指令的助记符。对于指令,作用是指出
1.等价语句EQU
等价语句的一般使用格式如下: SYMBOL EQU EXPRESSION
作用是用左边的符号名代表右边的表达式。
注意:等价语句不会给符号名分配存储空间,符号名不能与其它符号同名, 也不能被重新定义。
(1)用符号名代表常量或表达式
例4.14 (2)用符号名代表字符串 例4.15 (3)用符号名代表关键字或指令助记符 例4.16
例4.21
2.定义字变量的伪指令为DW
一个变量占一个字空间
例4.22:WORD1 DW DW 89H, 1909H, -1 0ABCDH, ?, 0
上面的定义语句经汇编后所产生出的内存单元分配情况如下:
… 89 00 09 19 FF FF CD AB --00 00 …
例4.23
3.双字变量定义伪指令DD 每个双字变量占用二个连续的字单元(四个字节)。
功能和作用,而不应该只写出指令的动作。
4.2运算符号
4.2.1算术运算符
算术运算符有:+、-、*、/和MOD。 其中: +、-、*、/就是我们算术中常用的加、减、乘、除。 MOD算符是模运算。指除法运算后得到的余数。 例如:5 MOD 2为1。 注意:算术运算符可以用于数字表达式或地址表达式中,但当它用于地址 表达式时,只有当其结果有明确的物理意义时才是有效的。 例如:将两个地址相乘或相除是无意义的。地址可以做加减运算,但也必 须注意物理意义。例如把两个不同段的地址相加减也是无意义的。 例4.1 例4.2

汇编语言switch语句

汇编语言switch语句

汇编语言switch语句什么是汇编语言?汇编语言是一种低级程序设计语言,用于与计算机硬件直接交互。

与高级语言相比,汇编语言更接近机器码,可以直接操作计算机的寄存器、内存和输入输出设备。

汇编语言的指令由操作码和操作数组成,使用助记符代替了机器码的二进制表示,更易于理解和编写。

在汇编语言中,switch语句可以让程序根据不同的情况执行不同的代码块。

它通常用于处理多重条件判断,实现分支控制逻辑。

switch语句的基本语法如下所示:switch (expression){case constant1:执行代码块1break;case constant2:执行代码块2break;case constant3:执行代码块3break;default:默认执行代码块break;}其中,expression是一个表达式或变量,用于进行判断;constant1、constant2等是常量,与expression进行比较。

如果expression的值与某个常量匹配,则执行相应的代码块,直到遇到break语句或switch语句结束。

如果没有匹配的常量,将执行default后的代码块。

下面将逐步回答关于汇编语言switch语句的一些常见问题。

一、如何在汇编语言中实现switch语句的功能?在汇编语言中,由于没有switch关键字,我们需要使用条件分支和跳转指令来实现类似的功能。

一般情况下,可以使用cmp指令将expression 的值与每个常量进行比较,再选择对应的代码块执行。

如果没有匹配的常量,可以通过无条件跳转指令(jmp)执行default后的代码块。

二、如何处理switch语句中的多个常量情况?为了实现多个常量的判断,可以使用多个cmp指令。

比如,对于三个常量的情况,可以使用三个cmp指令将expression的值与每个常量进行比较,再根据比较结果选择执行相应的代码块。

三、如何避免代码块之间的控制流乱跳?为了避免代码块之间的控制流乱跳,我们需要使用跳转指令来明确指示接下来应该执行的代码块。

集合知识点总结[汇编]

集合知识点总结[汇编]

集合知识点总结[汇编]一、汇编语言基础1、汇编语言是一种低级的机器语言,它是由机器指令和操作数构成的。

汇编语言帮助计算机硬件完成如输入/输出操作和内存管理等操作;2、汇编语言与高级语言的最大区别是,汇编语言的执行效率更高,但是在程序的开发上需要更多的努力,需要使用许多汇编指令来实现;3、汇编语言是由一系列指令构成的,指令可以被分为四种:控制指令、储存器操作指令、数据传输指令、计算指令等;4、汇编语言有许多共同的特点,如易于学习,编写简单,可以轻松实现多种操作等;5、汇编语言中有许多指令,如:MOV指令用于对寄存器的操作,ADD指令用于实现二进制数的加法,CMP指令用于实现二进制数的比较等;二、汇编语言的数据类型1、汇编语言的数据类型有无符号数据、有符号数据、比特(bit)、字节(byte)、字(word)、双字(double word)、四字(quad word)等;2、无符号数据是指没有正负号,即汇编语言中只用二进制;3、有符号数据是指含有正负号;4、比特是指由0或1组成的一个二进制数据,也就是最小的数据单位;5、字节是汇编中的一个基本数据单位,由8个比特组成;6、字是汇编中的一个基本数据单位,由16个比特组成;7、双字是汇编中的一个基本数据单位,由32个比特组成;4、四字是汇编中的一个基本数据单位,由64个比特组成。

三、汇编语言指令1、单操作数指令:单操作数指令是指汇编语言中只有一个操作数的指令,如INC、DEC、PUSH、POP等;2、双操作数指令:双操作数指令是指汇编语言中有两个操作数的指令,如MOV、ADD、SUB等;3、控制指令:控制指令是指能实现程序的控制、跳转和循环的指令,如JMP、LOOP、JB、JZ等;4、汇编关键字:汇编关键字是与汇编中的指令和数据有关的一些字,如DB、DW、DD 等;5、立即数指令:立即数指令是指指令的操作数是一个数值而不是地址的指令,如MOV AL,78H等。

汇编语言程序设计

汇编语言程序设计

第四章汇编语言程序设计(assembly languageprogramming)§4.1 汇编语言(assembly language)一.概述汇编语言:一种符号语言,它用助记符表示指令的操作码和操作数,用标号或符号代表地址、常量和变量,与机器语言几乎一一对应汇编语言程序:用汇编语言编写的程序汇编:把汇编语言源程序翻译成机器语言目标程序的过程汇编语言源程序手工汇编或汇编程序机器语言目标程序汇编程序:用来完成汇编工作的程序,有小汇编ASM宏汇编MASM动态调试程序DEBUG二.汇编语言的语句格式: [名称] 指令助记符 [操作数] [;注释]带方括号的项有时可没有,注释项完全可以没有每个部分用空格分开每行最多可有132个字符,但最好不要超过屏宽80语句可分成指令性语句和指示性语句(伪指令语句)指令性语句汇编后可生成机器码[标号:] 指令助记符 [操作数] [;注释]指示性语句指示汇编程序处理一些工作[名称] 伪指令(指示符) [操作数] [;注释]1.名称(或称标识符)给指令或存储单元地址起的名字,由字母、数字、字符组成字母:A~Z ,a~z数字:0~9字符:可打印+-*/=()[]〈〉;.' ’ ,_:?@$&(非打印空格制表符TAB 回车换行)(界符:,;:.()[]〈〉+-*/=?_@&$' ’界符用来表示某个标志的结束)数字不能作名称的第一个字符,圆点.仅能作第一个字符保留字不能作标识符($、?是保留字,与其它字符组合除外)名称最长为31个字符当名称后跟冒号,表示该名称是其后指令的标号,代表该指令的开始地址,其他指令可以用该标号作为转移地址当名称不跟冒号,它可能是标号或变量名,伪指令前的名称不跟冒号冒号隐含NEAR属性,例:供段内调用写成 OUTPUT:OUT DX ,AL供段间调用写成 OUTPUT OUT DX ,AL2.指令助记符8086/8088指令,也可以是伪指令,如果指令有前缀(LOCK、REP等)则前缀和指令用空格分开3.操作数指令执行的对象,可能有一、二个或没有名称指令助记符操作数注释RET ;返回(无操作数)COUNT: INC CX ;CX加1(1个操作数)MOV AL,BL ;ALBL(2个操作数)伪指令可有多个操作数COST DB 3,4,5,6,7,8 ;(6个操作数,用逗号分开)操作数可以是常数、寄存器名、标号、变量、表达式,MOV AX,[BP+4];(第二个操作数为表达式)4.注释可选项,使程序易读,汇编时不作处理注释前面要加分号,它可位于操作数之后,也可位于行首三.常量与变量1.常量,也叫常数,没有属性的纯数,汇编时已确定的值·数字常量为0~65535中的数(16位寄存器使用,伪操作可定义32位),进制加后缀说明,十进制加D(可省),二进制加B,八进制加Q,十六进制加H,若十六进制第一位为字母,则前头应加0·字符和字符串叫串常量,是ASCII码字符串,必须加单(或双)引号例:‘A’,“ABC”,汇编后变成41H,414243H2.变量,用于表达数值(或串)的标识符,有三个属性① 段属性(SEGMENT)② 偏移地址属性(OFFSET)③ 类型属性(TYPE),用DB、DW、DD定义§4.2 伪指令(pseudo-instruction)一.符号定义伪指令1.等值EQU格式:符号名 EQU 表达式用来给符号定义一个值,程序中出现该符号就用其值代替,EQU只能定义一次DATA EQU 1234 ;代表一个数XYZ EQU ALPHA[SI] ;代表一个地址2.等号 =格式:符号名 = 表达式意义与EQU一样,但程序中可重新定义符号的值EMP = 6 ;EMP代表6EMP =EMP + 1 ;EMP现在代表73.解除PURGE格式:PURGE 符号名(符号1,符号2,……,符号n)用于解除所定义的符号使该符号在以后的定义中有效例:原定义 TAB EQU 5可用 PURGE TAB 来解除然后可重新定义 TAB EQU 10二.数据定义伪指令用于预置存储器或定义变量1.定义字节DB格式:[变量名称] DB 表达式例:DATA1 DB 2,3,4,5;从DATA1单元开始存放4字节数据2.定义字DW格式:[变量名称] DW 表达式例:TAB DW 1234H;TAB单元内容为34H,TAB+1单元内容为12H 3.定义双字DD格式:[变量名称] DD 表达式每个数据二字(四字节)低位部分在低地址,高位部分在高地址·用DB/DW/DD定义的数每行不得超过16项,超过16项必须换行DB/DW/DD用法<1> SUM DB ? ;给SUM单元分配一个字节,内容未定<2> TAB DB 20 DUP(0);给TAB开始单元分配20字节,内容为0<3> TIME DW 100 DUP(?);给TIME开始单元分配100字,内容未定<4> ADDR DD TABLE ;TABLE的地址(双字)给ADDR例:DATA SEGMENTORG 100HTABLE DB 1,2,3,4ADDR DD TABLEDATA ENDS假设汇编后DS=13A2H(如果ADDR用DW定义,只得偏移量)(如果TABLE是变量,ADDR得地址,是常量,ADDR得数值)<5> LETTER DB ‘ABCDEFG’;将字符串以ASCII码形式填入LETTER开始的内存<6> HIS DB 3 DUP(‘WELCOME!’,0DH,0AH);从HIS单元开始重复3次存放WELCOME!和回车换行符4.标号LABEL格式:标号名 LEBEL 类型标号用于说明可执行代码在汇编语言程序中的位置,即符号地址,供调用和转移之用标号有三个属性段属性偏移量属性距离属性(即格式中的类型):NEAR(近程)和FAR(远程)NEAR:本标号为段内标号,调用本标号只提供偏移地址,段基址为当前代码段FAR:本标号为段间标号,调用本标号提供偏移地址和段基址一个具有NEAR属性的标号也可用标号加冒号作后缀,并排列在代码行的开头来隐含如 AGAIN LABEL NEARXOR AX,BUFF[BX]可写成 AGAIN:XOR AX,BUFF[BX]例:ROOT LABEL NEAR ;以下程序所用的ROOT标号是段内属性COMP PROC NEAR ;以下程序所用的过程下的标号是段内属性TIME PROC FAR ;以下程序所用的过程下的标号是段间属性三.运算符1.算术运算符+、-、*、/、MOD即加、减、乘、除、除法取余数(如19 MOD 7=5)操作数是数字,结果也是数字存储器地址运算时只有加减,例TAB+2、BETA-5等2.逻辑运算符AND、OR、XOR、NOT即与、或、异或、非操作数是数字,结果也是数字例:AND BX,DAT AND 0FEH3.关系算符EQ、NE、LT、GT、LE、GE即相等、不等、小于、大于、小于等于、大于等于若关系是假结果为0,若关系是真结果为0FFFFH例:MOV BX,PAD LT 3则PAD的值小于3时,汇编成MOV BX,0FFFFH否则,汇编成MOV BX,04.分析运算符可把存储器操作数分解成它的组成部分,如段值、段内偏移量、类型5.合成算符由已存在的存储器操作数生成一个段值与偏移量相同,而类型不同的新的存储器操作数以下讨论分析算符和合成算符1.取段基址SEG它加于一个变量或标号之前,回送段基址,例:ASSUME CS:SEG BEGIN ;令CS为BEGIN程序段段基址MOV AX,SEG VARN ;将VARN的段基址送AX2.取偏移量OFFSET它加于一个变量或标号之前,取其偏移量,例:MOV BX,OFFSET SUM ;将SUM的段内偏移量存入BX3.取类型码TYPE它加于一个变量或标号之前,取其类型代码DB DW DD DQ DT NEAR FAR1 2 4 8 10 -1 -2例:NG1 DB ‘A’,‘D’,3NG2 DW 88,265……MOV AL,TYPE NG1 ;NG1定义字节,AL 1MOV AL,TYPE NG2 ;NG2定义字,AL 24.取长度LENGTH它加于一个变量之前,取分配给变量的项数例:TAB DB 150 DUP(?);150项,150字节FUM DW 150 DUP(?);150项,300字节则,MOV CX,LENGTH TAB ;CX 150MOV AX,LENGTH FUM ;AX 150·注意:LENGTH返回的存储区必须用DUP()来定义,否则返回为1 5.取字节数SIZE它加于一个变量之前,取回变量所占字节总数,有SIZE = LENGTH * TYPE由上例,LENGTH TAB = 150,TYPE TAB = 1LENGTH FUM = 150,TYPE FUM = 2可知: SIZE TAB = 150SIZE FUM = 300以上5个叫数值返回算符6.类型指示PTR格式:类型 PTR 地址表达式表示PTR右边的(存储器)操作数是左边的类型,有:BYTE、WORD、DWORD、NEAR、FAR例:INC BYTE PTR [BX] ;将BX指向的单元字节加1MOV WORD PTR [DI],99 ;立即数99送DI指向的字中JMP NEAR PTR FOK ;以近程方式跳转到FOK(只取FOK偏移地址)7.指定符THIS(合成算符)用于定义当前所指单元中的类型格式:THIS 类型/距离经THIS定义过的标号具有当前汇编段、偏移量和所规定的类型或距离等属性,例:FOOB EQU THIS BYTE;下面的字类型变量FOOW在这里指定为字节型FOOBFOOW DW 120 DUP(?)定义后,对同一数据块(FOOB和FOOW有相同的段和段内偏移量)有两种类型访问FOOB时为字节操作,访问FOOW时为字操作ADD AL,FOOB[3] ;将数组第四字节与AL相加MOV AX,FOOW[4]将数组第五六字节组成的字送AX也可以这样来构成FOOB:FOOB EQU BYTE PTR FOOW又例:DATAF EQU THIS FARDATAN:MOV AX,FOO这时 JMP DATAN为近程转移JMP DATAF为远程转移8.段修改符:用于对某一地址表达式指定临时段基址,如MOV AX,ES:[BX];指定ES为BX的段基址,对当前指令有效9.短程符SHORT与NEAR、FAR功能类似,位移量一字节范围 -128~+127,对应一条短转移指令例:JMP SHORT LAB;转移到标号LAB的地址10.方括号 [ ]表示操作数(加方括号)是一个地址偏移量,不是数值格式:[表达式] 或者 [表达式][ ]例:MOV [BX],AX ;将AX内容送BX所指单元MOV [BX+7],AX ;将AX内容送BX+7所指单元MOV AX,[BX][SI] ;将BX+SI所指单元内容送AX11.取高/低字节HIGH/LOW用来分离常量的高/低字节,对存储器操作数无效例:DATA EQU 789AHMOV AL,HIGH DATA ;AL=78HMOV AL,LOW DATA ;AL=9AH四.段定义伪指令1.SEGMENT—ENDS格式:[段名] SEGMENT [定位类型] [组合类型] [‘类别名’]┇[段名] ENDS·段名·定位类型(Align),给出实际段地点的种类或段长度的信息<1> PARA 段起始地址从一个节(paragraph)的边界开始<2> BYTE 段地址可从任意绝对地址开始<3> WORD 段地址从任意一个字的边界开始<4> PAGE 段地址从某一页的边界开始(一页等于256字节)<5> INPAG 段长度小于一页未说明定位类型时则默认为PARA·组合类型(Combine),又称联合类型,程序中各程序段的连接和定位方法<1> PUBLIC 将段名相同的程序段(亦称模块)依此紧密连接,但彼此不相互覆盖<2> COMMON将段名相同的程序段连接,各段都从同一地址开始<3> AT表达式段定位在由表达式(结果必须是常数)所指定的节的边界上例:AT 1234H,则段地址被定位在物理地址为12340H处,如果希望从12345H开始,则在SEGMENT命令的下一行写上ORG 5AT 不能向前引用<4>STACK 表示这个段是运行期间的堆栈段<5>MEMORY 该段是相互连接的几个段中地址最高的段<6>NONE本段与其他段无组合关系未说明联合类型时则默认为NONE,不和别的段连接·‘类别名’(Class),也叫组名,加单引号,汇编后类别名相同的程序段代码集中在一起定位,形成一个统一的物理段,类别名可自定,约定的有CODE (代码段)、DATA(数据段)、STACK(堆栈段)、CONST(常数)、MEMORY(存储)等2.ASSUME段寄存器说明伪指令,指明所定义的段名所使用的段寄存器(告诉汇编程序在运行期间通过哪一个段寄存器寻址才能找到所要的指令和数据),本语句一般在定义的代码段中第一条出现格式:ASSUME 段寄存器:段名 [,…]例: ASSUME CS:CODE,DS:DATA ;用SEGMENT—ENDS定义ASSUME CS:SEG KGF,DS:SEG BEGIN;由算符定义ASSUME ES:NOTHING ;用关键字定义,表示不使用ES(取消ES段寄存器)(保留字NOTHING在这里作为一个段名参数,ASSUME NOTHING表示取消所有段寄存器,各个段寄存器只能在指令性语句中由MOV指令赋值)·ASSUME只是设定段寄存器与逻辑段的对应关系,并没给段寄存器装入实际值,所以程序中必须对DS、ES、SS赋值,而CS由系统赋值3.ORG定点伪指令(段内定位),用以确定下一条指令(或变量)在当前段中的偏移地址格式:ORG 表达式表达式以65536(64K)为模计算,超过64K则取其余数本语句前未定义过的变量不可出现在表达式中,表达式可包含$(程序计数器当前值)如:ORG OFFSET $+1000表达式必须为正值,若为负值,就会从当前段的地址高端开始表达式最好不要写成OFFSET $-1000,以免把汇编过的1000个字节覆盖掉ORG指令不能带标识符,如START:ORG 0和SKIP ORG 100都是错的例:CODE SEGMENT ;段起始ORG 100H ;本程序代码从偏移地址100H开始装入ASSUME CS:CODE ;装入代码段地址到CS中START:IN AL,30H ;程序段SHL AL,1OUT 32H,ALJMP STARTCODE ENDS ;程序段结束END START ;汇编结束例:DATA SEGMENTORG 50HDAT DW 1,2,$+1┇DATA ENDS注意DAT不能定义为字节,否则与$不匹配五.过程定义伪指令格式:过程名 PROC 属性┇过程体RET过程名 ENDP·过程名不可缺省,它和标号一样有三个属性:段属性、偏移地址属性、距离属性·距离属性在PROC后指定,有NEAR和FAR,如果希望过程能让别的程序调用,则必须是FAR属性·一个过程允许多个入口,入口处有标号,标号要说明距离属性例:延时100ms子程序DELAY PROC ;隐含NEARMOV BL,10 4TDLY1: MOV CX,2801 ;内循环延时10ms 10TWAIT0: LOOP WAIT0 9/5T DEC BL 2TJNZ DLY1 8/4TRET 8TDELAY ENDP六.结束伪指令·NAME:给模块(源程序)命名格式:NAME 模块名称它出现在源程序的最前端·END:汇编结束格式:END [标号名]它通知汇编程序本模块汇编到此结束标号名是可选项,若选取,应指向执行本程序的起始地址若一个源程序是多模块,只有主模块的END后加标号,子模块只有END七.宏指令宏指令:在汇编语言源程序中多次重复出现的程序段,用一个名字来定义,然后当成一条指令来使用宏汇编:源程序中的宏指令经汇编程序翻译后扩展成对应程序段的机器码宏指令用MACRO—ENDM来定义,如:CRLF MACROMOV DL,0DHMOV AH,02HINT 21H┇ENDM(CRLF作回车换行)§4.3 汇编语言程序设计(assembly language programming)一.设计要求1.程序简明、易读、易调试、易修改2.程序占用内存要少(包括程序长度及运行时所需空间)3.程序运行速度要快二.基本设计方法1.选择合适的计算方法2.绘制程序流程图3.编制程序4.上机调试三.汇编语言程序格式和基本结构一般一个完整的汇编语言程序至少应包括以下三个程序段简化段格式:.MODEL SMALL.STACK 64H.DATA……;紧接指令代码从偶地址开始存放.CODESTART: MOV AX,@DATAMOV DS,AX……END STARTDATA SEGMENT ‘DATA’┇数据段DATA ENDSSTACK SEGMENT ‘STACK’┇堆栈段STACK ENDSCODE SEGMENT ‘CODE’ASSUME CS:CODE,DS:DATA,SS:STACKSTART:MOV AX,DATAMOV DS,AXMOV AX,STACKMOV SS,AX┇代码段CODE ENDSEND START程序的基本结构分为顺序结构、分支结构、循环结构和子程序结构(一)顺序结构一种简单程序,按顺序执行例1.将200H单元的低4位和高4位分别送入201H和202H单元的低4位,这二单元的高4位清0200HX X201H 0202H 0DATA SEGMENTORG 200HBCD DB 47HDB 2 DUP(?)DATA ENDSSTACK SEGMENTSTA DB 20 DUP(?)TOP EQU LENGTH STASTACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACKSTART:MOV AX,DATAMOV DS,AXMOV AX,STACK MOV SS,AXMOV AX,TOPMOV SP,AXMOV BX,OFFSET BCD MOV AL,[BX]AND AL,0FHMOV [BX+1],AL MOV AL,[BX]MOV CL,4ROL AL,CLAND AL,0FHMOV [BX+2],AL HLTCODE ENDS END START例2.将ADDR1和ADDR2两单元开始的二个16位无符号数相加,考虑到进位,将其结果存放在SUM开始的三个单元中DATA SEGMNETADDR1 DW 7854HADDR2 DW 9981HSUM DB 3 DUP(0)DATA ENDSSATCK SEGMENTSTA DB 20 DUP(?)TOP EQU LENGTH STASTACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACKSTART: MOV AX,DATAMOV DS,AXMOV AX,STACKMOV SS,AXMOV AX,TOPMOV SP,AXMOV AX,ADDR1ADD AX,ADDR2MOV WORD PTR SUM,AXADC SUM+2,0HLTCODE ENDSEND START例3.查表将DATA1单元中字节类型数据(0~0FH)转换成ASCII码,并存入ASCII单元中DATA SEGMENTASCTAB DB 30H,31H,32H,33H,34H,35H,36H,37HDB 38H,39H,41H,42H,43H,44H,45H,46HDATA1 DB 09HASCII DB ?DATA ENDSSTACK SEGMENT STACKDW 10 DUP(?)STACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACKSTART PROC FARASCTAB 30 031 1┇93941 A42 B┇46 F┇ASCIIPUSH DSMOV AX,0PUSH AXMOV AX,DATAMOV DS,AXMOV BX,OFFSET ASCTABMOV AL,DATA1XLATMOV ASCII,ALRETSTART ENDPCODE ENDSEND START例4.将200H和201H单元字节的高4位对调,低4位不变CODE SEGMENT200H201HORG 200HDATA1 DB 0F3H,47HASSUME CS:CODE,DS:CODESTART:MOV AX,CODEMOV DS,AXMOV CL,4MOV AX,WORD PTR DATA1 ;AX=47F3HROL AX,CL ;AX=7F34H ROL AH,CL ;AX=F734H ROL AL,CL ;AX=F743H MOV WORD PTR DATA1,AXHLTCODE ENDSEND START(二)分支结构通过判断产生分支,借助于条件转移指令跳转到相应的分支地址执行分支程序分支程序由三部分组成① 测试部分,负责产生决定分支的条件② 定向部分,根据测试条件是否满足,决定程序是否分支③ 标注部分,标明分支的去向利用跳转表也可使程序转移到分支地址例1.16位二进制补码X在DATA1单元,求其绝对值送DATA2单元(设X≠8000H)∣X∣= X,X≥0-X, X<0DATA SEGMENTDATA1 DW 9F87HDATA2 DW ?DATA ENDSSTACK SEGMENTSTA DB 20 DUP(?)TOP EQU LENGTH STA STACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACK START:MOV AX,DATAMOV DS,AXMOV AX,STACKMOV SS,AXMOV AX,TOPMOV SP,AXMOV AX,DATA1AND AX,AXJNS ABS0NEG AXABS0: MOV DATA2,AXHLTCODE ENDSEND START例2.多重分支学生成绩按分数段划分为:A、90~100(5AH~64H)B、80~89 (50H~59H)C、70~79 (46H~4FH)D、60~69 (3CH~45H)E、 <60 ( <3CH)已知分数存放在MARK单元,请用ASCII码的A、B、C、D、E去代表MARK单元中的分数所属的段,并存于GRADE单元DATA SEGMENTMARK DB 81GRADE DB ?DATA ENDSCODE SEGMENT ASSUME CS:CODE,DS:DATA START:MOV AX,DATAMOV DS,AXMOV BX,OFFSET MARKMOV AL,[BX]CMP AL,3CHJC LPECMP AL,46HJC LPDCMP AL,50HJC LPCCMP AL,5AHJC LPBMOV AL,41H ;‘A’JMP SHORT DONELPB: MOV AL,42H ;‘B’JMP SHORT DONELPC: MOV AL,43H ;‘C’JMP SHORT DONELPD: MOV AL,44H ;‘D’JMP SHORT DONELPE: MOV AL,45H ;‘E’DONE: MOV BX,OFFSET GRADE MOV [BX],ALHLTCODE ENDSEND START法2:直接查表转换(顺序结构)DATA SEGMENTTAB DB ‘EEEEEEDCBAA’MARK DB 81GRADE DB ?DATA ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA START:MOV AX,DATAMOV DS,AXMOV BX,OFFSET TABMOV AL,MARKMOV AH,0MOV CL,10DIV CLXLATMOV GRADE,ALHLTCODE ENDSEND START(三)循环结构使机器重复执行一系列指令,是一种闭合的分支结构循环程序由四部分组成① 初始化部分(或预置部分),负责设置循环初值② 处理部分,循环过程的主体③ 控制部分,修改初值,判断是否循环循环次数由一计数器控制循环次数由某一指定条件是否满足来决定④ 结束部分,处理循环程序的最后结果例1.将DTAB单元开始的一组字节补码数(≤255个)求平均值,结果存入AVE单元,若结果为负,在SYM置FFH否则置0DATA SEGMENTDTAB DB 0FDH,0FCH,05H,0F8H,……DB 08H,25H,83H,97H,……COUNT EQU $-DTABAVE DB ?SYM DB ?DATA ENDSSTACK SEGMENT STACKSTA DB 20 DUP(?)STACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACK AVER PROC FARSTART:PUSH DSMOV AX,0PUSH AXMOV AX,DATAMOV DS,AXLEA BX,DTABMOV CX,COUNTXOR DX,DXNEXT: MOV AL,[BX]CBWADD DX,AXINC BXLOOP NEXTMOV AX,DX MOV CL,COUNTIDIV CLMOV AVE,ALMOV SYM,0AND AL,ALJNS DONEMOV SYM,0FFHDONE: RETAVER ENDPCODE ENDSEND START循环控制方法:循环次数由计数器控制例2.将8位二进制小数规格化设需规格化的小数在DATA1单元,要求规格化后使其最高位为1,并存入DATA2单元,办法是把小数左移至最高为位为1为止,左移次数存入DATA3单元,若小数是0,则在DATA2和DATA3单元存入0示例:DATA1 DATA2 DATA322H 88H 02H01H 80H 07HCBH CBH 00H00H 00H 00HDATA SEGMENTDATA1 DB 22HDATA2 DB ?DATA3 DB ?DATA ENDSSTACK SEGMENTSTA DB 20 DUP(?)TOP EQU LENGTH STA STACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACKSTART:MOV DATAMOV DS,AXMOV AX,STACKMOV SS,AXMOV AX,TOPMOV SP,AXMOV CL,0MOV AL,DATA1 ;取数AND AL,AL ;设ZF、SF标志JZ DONECHKSF:JS DONEINC CLADD AL,AL ;左移一位JMP SHORT CHKSFDONE: MOV DATA2,ALMOV DATA3,CLHLTCODE ENDSEND START循环控制方法:循环次数由某一指定条件是否满足来决定例3.多重循环将n个无符号字节数从小到大排序,方法是依此比较相邻两单元的数,若前小后大不交换第一轮比较n-1次,最大数沉底(高地址)第二轮比较n-2次,次大数沉到最大数上面第n-1轮比较完若在某一轮比较时没有出现交换,说明顺序已排好,不必后续比较,故设交换标志AH=1代表不交换,AH=2代表有交换DATA SEGMENTLIST DB 18,6,11,3,1,2,3,9,8,7,6 DB 111,110,99,112,115,114,113,98,96,97 COUNT EQU $-LISTDATA ENDSSTACK SEGMENT STACKSTA DW 10 DUP(?)STACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACKSET PROC FARSTART:PUSH DSMOV AX,0PUSH AXMOV AX,DATAMOV DS,AXMOV DX,COUNT-1 ;n-1轮(外循环)EXGO:MOV CX,DX ;每轮次数(内循环)MOV AH,01H ;交换标志MOV BX,OFFSET LIST ;数据块首址INGO: MOV AL,[BX]INC BXCMP AL,[BX]JC NEXT ;数1小,不交换XCHG AL,[BX] ;数1>数2,交换DEC BXXCHG AL,[BX]INC BX ;恢复数据指针MOV AH,02H ;有交换标志NEXT: LOOP INGODEC DXJZ DONEDEC AHJNZ EXGODONE: RETSET ENDPCODE ENDSEND START(四)子程序结构基本要求:① 子程序的开始(入口处)应给予一个标号,结束处有返回指令② 通用子程序要说明入口条件(入口参数)和出口条件(出口参数)③ 调用子程序要注意保护现场和恢复现场调用—返回的堆栈操作:CALL target ;段内SPSP-2,[SP+1,SP] IP,IPIP+disp段间SPSP-2,[SP+1,SP] CS,CSsegSPSP-2,[SP+1,SP] IP,IPoffsetRET ;段内IP [SP+1,SP],SPSP+2;段间IP [SP+1,SP],SPSP+2CS [SP+1,SP],SPSP+2RET n ;如上操作后SPSP+n·子程序入口标号应说明距离属性·对于一个FAR过程,过程初必须先保护程序段前缀中的中断指令INT 20H 的断点地址(DS:0000),它是一个程序正常结束退出的中断处理程序例1.将内存200H单元开始的一个五字节十六进制数显示出来(低位在低地址)DATA SEGMENTORG 200HNUM DB 9AH,78H,56H,34H,12HDATA ENDSSTACK SEGMENTSTA DB 20 DUP(?)TOP EQU LENGTH STASTACK ENDSCODE SEGMENTASSUME CS:CODE,DS:DATA,SS:STACKSTART:MOV AX,DATAMOV DS,AXMOV BX,5MOV AX,STACKMOV SS,AXMOV SP,TOPMOV SI,OFFSET NUMNEXT: MOV DH,[SI+BX-1] ;(不用AL,因调用MOV DL,DH ; display返回时DL→AL)MOV CL,4ROL DL,CLAND DL,0FHCALL DISPLAYMOV DL,DHAND DL,0FHCALL DISPLAYDEC BXJNZ NEXTMOV AX,4C00HINT 21HDISPLAY PROC NEARADD DL,30HCMP DL,3AHJB OKADD DL,07H;(如果DL=3AH,3AH+7=41H是‘A’)OK: MOV AH,02HINT 21HRET。

说明高级语言、汇编语言、机器语言的差别及其联系

说明高级语言、汇编语言、机器语言的差别及其联系

说明高级语言、汇编语言、机器语言的差别及其联系高级语言、汇编语言和机器语言是计算机领域中使用的三种不同类型的语言。

它们在很多方面都有着不同的特点和应用,但它们之间也存在着联系。

本文将详细介绍高级语言、汇编语言和机器语言的差异以及它们之间的联系。

一、高级语言高级语言是一种计算机编程语言,它比较接近自然语言,使用它编写的程序比较容易理解和维护。

高级语言的编写方式是基于算法和逻辑,开发者可以使用高级语言来描述程序的功能和操作。

高级语言通常使用关键字和语法来表示不同的程序结构,开发者可以通过这些结构来组织程序逻辑。

高级语言的编写方式比较方便,它可以让开发者专注于程序的逻辑和功能,而无需关注计算机底层的细节。

此外,高级语言还提供了许多内置的函数和类库,这些函数和类库可以帮助开发者快速实现一些常见的功能。

常见的高级语言有C、C++、Java、Python等。

这些语言都有着各自的特点和应用场景。

例如,C语言适合编写系统级程序和底层驱动程序,Java语言适合编写跨平台应用程序,Python语言适合编写数据处理和科学计算程序。

二、汇编语言汇编语言是一种低级语言,它是一种将汇编指令翻译成机器指令的程序语言。

汇编语言直接使用机器指令,它与机器语言之间的差别在于,汇编语言使用助记符来代替二进制代码,这样就使得编程更加容易。

汇编语言可以直接访问计算机系统底层的硬件资源,例如CPU、内存等。

因此,汇编语言可以实现非常高效的程序,这些程序可以直接访问计算机的硬件资源,从而提高了程序的执行效率。

汇编语言通常用于编写系统级程序和底层驱动程序。

例如,操作系统的内核就是使用汇编语言编写的。

此外,一些对性能要求非常高的程序,例如视频编解码器、图形处理器等,也需要使用汇编语言来实现。

三、机器语言机器语言是一种最底层的计算机语言,它是计算机硬件直接执行的语言。

机器语言是由0和1组成的二进制代码,这些代码直接被计算机硬件执行。

机器语言是计算机硬件能够理解和执行的唯一语言。

汇编语言中macro的用法

汇编语言中macro的用法

汇编语言中macro的用法在汇编语言中,`macro`是一个非常有用的工具,它可以帮助程序员简化代码的编写和调试过程。

本文将深入探讨`macro`的用法及其在汇编语言中的应用。

一、`macro`的概念`macro`是一种允许程序员在源代码中定义可重用代码片段的汇编指令。

它类似于高级语言中的函数或宏定义,可以在程序的不同部分调用,并在编译时将其展开为实际的汇编指令。

二、`macro`的语法在汇编语言中,我们可以使用`macro`关键字来定义一个`macro`指令。

其基本语法如下:```macro macro_name parameter1, parameter2, ..., parameterN; 宏代码部分endm```其中,`macro_name`是宏的名称,`parameter1`、 `parameter2`等是宏的参数,可以根据需要进行定义。

在宏代码部分,我们可以编写一系列的汇编指令,用于实现特定的功能。

三、`macro`的使用示例下面通过一个简单的示例来说明`macro`的使用方法。

假设我们需要编写一个汇编程序,用于计算一个数的平方。

这个功能可以通过一个`macro`来实现:```macro square numbermov ax, numbermul axendm```在上面的示例中,我们定义了一个名为`square`的`macro`,它接受一个参数`number`,将其存储在`AX`寄存器中,并使用`mul`指令计算`number`的平方。

在程序的其他部分,我们可以通过调用`square`宏来使用这个功能。

例如:```mov cx, 5square cx```上述代码片段通过`square`宏计算`CX`寄存器中的值的平方,并将结果存储在`AX`寄存器中。

四、`macro`的优势通过使用`macro`,我们可以实现以下几个优势:1. 代码重用:宏定义可以在程序的不同部分多次使用,避免了重复编写相同的代码片段,提高了代码的可维护性和可读性。

汇编语言汇编语言实现ifwhilefor以及编写冒泡排序

汇编语言汇编语言实现ifwhilefor以及编写冒泡排序

汇编语言汇编语言实现ifwhilefor以及编写冒泡排序在计算机科学中,汇编语言是一种底层编程语言,它直接与计算机的硬件交互。

在汇编语言中,我们可以利用条件语句(if)、循环语句(while、for)以及排序算法(冒泡排序)来实现各种功能。

本文将介绍汇编语言中如何使用这些关键字来编写相应功能的代码,并且以冒泡排序算法为例进行详细说明。

一、if语句实现if语句在汇编语言中通常使用条件判断指令来实现。

下面是一个示例代码,演示如何使用if语句判断两个数的大小关系:```section .datanum1 db 10num2 db 20result db 0section .textglobal _start_start:mov al, byte[num1]mov bl, byte[num2]cmp al, bl ; 比较num1和num2的值jg greater ; 如果num1 > num2,则跳转到greater标签处jl less ; 如果num1 < num2,则跳转到less标签处jmp equal ; 如果num1 = num2,则跳转到equal标签处greater:mov byte[result], 1jmp doneless:mov byte[result], -1jmp doneequal:mov byte[result], 0jmp donedone:; 其他操作,比如打印结果或进行下一步操作```在上述代码中,我们将两个数分别存在`num1`和`num2`变量中,用`cmp`指令进行比较,根据比较结果使用`jmp`指令跳转到相应的标签位置。

通过这样的比较和跳转操作,我们可以实现基本的if语句功能。

二、while语句实现while语句在汇编语言中可以通过使用条件循环指令来实现。

下面是一个示例代码,演示如何使用while语句计算1到10的和:```section .datasum dw 0counter dw 1section .textglobal _start_start:mov cx, 10 ; 设置循环次数mov ax, 1 ; 设置计数器初始值loop_start:add word[sum], ax ; 将计数器的值加到sum中inc ax ; 计数器自增1loop loop_start ; 循环开始; 其他操作,比如打印结果或进行下一步操作```在上述代码中,我们将循环次数存储在`cx`寄存器中,将计数器的初始值存储在`ax`寄存器中。

32个C语言关键字

32个C语言关键字

32个C语言关键字
32个C语言关键字
C语言是一种计算机程序设计语言,它既具有高级语言的.特点,又具有汇编语言的特点。

那么,C语言一共有多少个关键字?下面我们一起来看看。

C语言一共有32个关键字,如下所述:
auto :声明自动变量
short :声明短整型变量或函数
int:声明整型变量或函数
long :声明长整型变量或函数
float:声明浮点型变量或函数
double :声明双精度变量或函数
char :声明字符型变量或函数
struct:声明结构体变量或函数
union:声明共用数据类型
enum :声明枚举类型
typedef:用以给数据类型取别名
const :声明只读变量
unsigned:声明无符号类型变量或函数
signed:声明有符号类型变量或函数
extern:声明变量是在其他文件正声明
register:声明寄存器变量
static :声明静态变量
volatile:说明变量在程序执行中可被隐含地改变
void :声明函数无返回值或无参数,声明无类型指针
if:条件语句
else :条件语句否定分支(与 if 连用)
switch :用于开关语句case:开关语句分支
for:一种循环语句
do :循环语句的循环体
while :循环语句的循环条件
goto:无条件跳转语句
continue:结束当前循环,开始下一轮循环
break:跳出当前循环
default:开关语句中的“其他”分支
sizeof:计算数据类型长度
return :子程序返回语句(可以带参数,也可不带参数)循环条件下载全文。

汇编语言语法

汇编语言语法

汇编语言语法
汇编语言是一种低级语言,它使用符号代表机器指令,用于编写底层程序。

以下是汇编语言的语法规则:
1. 指令格式
汇编语言的指令格式通常由操作码和操作数组成。

操作码表示要执行的操作,操作数则是指令要操作的数据。

指令格式通常如下:
操作码操作数1, 操作数2
其中,操作数可以是寄存器、内存地址或立即数。

2. 注释
汇编语言中的注释以分号(;)开头,可以在指令后面或单独一行中使用。

注释用于解释代码的作用和用途,提高代码的可读性。

3. 标号
汇编语言中的标号用于标识程序中的位置,通常用于跳转指令和数据定义。

标号以字母或下划线开头,后面可以跟数字和字母。

标号后面必须加冒号(:)。

4. 寄存器
汇编语言中的寄存器用于存储数据和执行操作。

常用的寄存器包括AX、BX、CX、DX等。

寄存器名通常以字母开头,后面可以跟数字。

5. 内存地址
汇编语言中的内存地址用于访问内存中的数据。

内存地址通常由段地址和偏移地址组成。

段地址和偏移地址可以用方括号括起来表示,例如[0x1000:0x0000]。

6. 数据定义
汇编语言中的数据定义用于定义变量和常量。

数据定义通常以关键字开头,例如DB表示定义一个字节,DW表示定义一个字。

数据定义后面可以跟变量名和初始值。

7. 跳转指令
汇编语言中的跳转指令用于改变程序的执行流程。

常用的跳转指令包括JMP、JE、JNE等。

跳转指令后面通常跟标号。

以上是汇编语言的语法规则,掌握这些规则可以编写简单的汇编程序。

汇编逻辑运算类指令

汇编逻辑运算类指令

汇编逻辑运算类指令
汇编语言中的逻辑运算类指令包括与、或、非和异或等操作。

这些指令用于对数据进行逻辑运算,常用于处理位操作和逻辑判断。

首先是“与”操作,通常用AND指令表示,在大多数汇编语言
中是用关键字“AND”来表示。

AND指令用于将两个操作数的对应位
进行逻辑与操作,结果为1的位保留,结果为0的位清零。

其次是“或”操作,通常用OR指令表示,在汇编语言中是用关
键字“OR”来表示。

OR指令用于将两个操作数的对应位进行逻辑或
操作,只要有一个操作数的对应位为1,结果位就为1。

接下来是“非”操作,通常用NOT指令表示,在汇编语言中是
用关键字“NOT”来表示。

NOT指令用于对操作数的每一位进行取反
操作,即1变为0,0变为1。

最后是“异或”操作,通常用XOR指令表示,在汇编语言中是
用关键字“XOR”来表示。

XOR指令用于将两个操作数的对应位进行
异或操作,即相同则结果为0,不同则结果为1。

这些逻辑运算指令在汇编语言中被广泛应用于各种数据处理和控制流程中,能够对数据进行精确的位操作和逻辑判断,是编程中不可或缺的重要指令。

通过合理的组合和应用,可以实现复杂的逻辑运算和数据处理功能。

C语言基础知识点汇总(精简)

C语言基础知识点汇总(精简)

C语言基础知识点汇总(精简)C语言是一种计算机程序设计语言,它既具有高级语言的特点,又具有汇编语言的特点。

下面是一些C语言的基础知识点:1、变量和常量:变量是存储在计算机内存中的一段数据,常量是在程序中使用的一些不会改变的数据。

2、数据类型:C语言支持多种数据类型,包括整数类型、浮点数类型、字符类型和指针类型。

其中,整型和浮点型用于存储整数和浮点数,字符型用于存储字符,而指针用于存储内存地址。

3、运算符和表达式:C 语言提供了丰富的运算符,包括算术运算符、赋值运算符、比较运算符、逻辑运算符和位运算符等。

其中,算术运算符用于执行基本的算术运算,赋值运算符用于将一个值赋给另一个变量,比较运算符用于比较两个值的大小,逻辑运算符用于执行逻辑运算,位运算符用于对二进制数进行操作。

表达式是由运算符和操作数组成的代数式。

4、流控制语句:C 语言支持三种流控制语句,包括 if、else if、else、for、while、do-while 循环和 switch 语句。

其中,if 语句用于根据条件执行代码块,else if 语句用于多分支选择,else 语句用于忽略某个条件,for 循环用于重复执行一段代码,while 循环用于执行一段代码,do-while 循环用于在循环体内执行代码,switch语句用于根据条件执行代码块。

5、函数:C 语言中的函数是一种代码块,用于执行特定的任务。

函数可以接受参数,并返回一个值。

函数定义应该包含在一个文件中,并且使用关键字 function 声明。

函数是C语言的基本单元,可以定义一些操作重复性任务的代码段,以提高代码的复用性和可维护性。

6、指针:指针是C语言中的一种关键字,用于指向内存中的某个位置。

指针是一种特殊的变量,用于存储内存地址。

在 C 语言中,指针用于引用内存中的值,可以通过指针访问内存中的变量、函数和数据结构。

7、字符串:字符串是一种存储字符的数据类型。

在 C 语言中,字符串用于存储字符数据,可以通过字符串函数进行处理和操作。

汇编语言基础

汇编语言基础

汇编语言基础(一)汇编语言的基本语法1.字符集(1)英文字符:A~Z a~z(2)数字字符:0~9(3)算术运算符:+ - * /(4)关系运算符:<=>(5)分隔符: ,: ;() [] ’ (空格) TAB(6)控制符: CR(回车) LF(换行) FF(换页)(7)其他字符: & - (下划线)?. $ @ ! %在用汇编语言编写程序时,程序中的指令助记符、标识符、运算符、分隔符等,均应由上述字符集中的字符组成。

使用其他字符均为非法字符,8086/8088宏汇编程序不能识别和翻译。

2.标识符标识符在程序中用作变量名、常量名、记录名、段名等。

规定如下:(1)标识符由1~31个字符组成,打头的字符必须是字母、?(问号)、@和 - (下划线)。

(2)从第2个字符开始。

组成标识符的字符可以是字母、数字、@、?、 - 等,不能使用其他字符。

3.保留字8086/8088中指令助记符、伪指令、寄存器名、表达式运算符及属性操作符等都是系统的保留字。

保留字不能用作标识符。

4.语句80x86宏汇编语言有三种基本语句,即指令语句、伪指令语句和宏指令语句。

指令语句对应着机器的一种操作,汇编时产生一个目标代码;伪指令是为汇编程序提供编译信息、指标汇编程序做某些操作的语句,它不产生目标代码,与机器的操作无关。

一条语句在源程序中一般只占一行,长度超过一行时必须用续行符号&。

5.汇编语言程序结构用汇编语言编写的源程序,在结构上具有以下特点:(1)由若干逻辑段组成,各逻辑段由伪指令语句定义和说明。

(2)整个源程序以END伪指令结束。

(3)每个逻辑段由语句序列组成,各语句可以是指令语句、伪指令语句、宏指令语句、注释语句或空行语句。

(二)汇编语言和汇编处理过程计算机完成汇编任务是由一个称为汇编程序(Assembler)软件工具实现的。

汇编程序是一种系统软件。

汇编程序加工的对象是汇编语言程序,称为源程序,而汇编后产生的结果是机器语言程序,称为目标程序。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

汇编语言基本关键字
aaa对非压缩BCD码加法之和调整
aas对非压缩BCD码减法之差调整
aam乘法调整aad被除数调整
add不带进位标志位的加法adc带进位标志位的加法
and逻辑与
assume指定段寄存器
bswap双字单操作数内部交换
bt位测试bts位测试并置一
btr位测试并清零btc位测试并取反
bsf/bsr正,反向位扫描
call调用
cbw字节转换为字cwd字转换为双字cwde字转换为扩展的双字cdq双字转换为四字
cmp比较cmpxchg比较并交换
cmps串比较
code定义简化代码段
const定义简化常数数据段
daa对压缩BCD码加法之和调整das对压缩BCD码减法之差调整
data定义简化数据段
db/dw/dd/dq/dt定义字节/字/双字/四字/十字变量
dec减一
df定义32位便宜地址的远地址指针
div无符号数除法
equ等价textequ文本等价
even取偶偏移地址
fardata,fardata定义简化独立数据段
group定义段组
idiv有符号整数除法
imul有符号整数乘法
in输入
inc加一
ins/outs输入/输出串元素
jcxz/jecxz若cx=0/ecx=0,跳转
jmpdopd无条件跳转到DOPD处取出指令继续执行
label为$定义符号
Lahf标志位低八位送AH
lea偏移地址送通用寄存器lda传送进入数据段的地址指针
les传送进入附加数据段的地址指针lfs传送进入FS段的地址指针
lgs传送进入GS段的地址指针lss传送进入堆栈段的地址指针
local说明局部变量
lods读出串元素
Loop/loopd无条件循环cx/ecx为循环次数
loopnz/loopnzd非零或不等时循环,cx/ecx为循环次数
loopz/loopzd为零或相等时循环,cx/ecx为循环次数
model存储模式
MOV传送movsx符号扩展传送,movzx零扩展传送
movs串传送
mul无符号数乘法
neg求负
not逻辑非
offset得到操作数的偏移地址
or逻辑或
org设置段内偏移地址
out输出
pop出栈popa所有16为寄存器出栈popad所有32位寄存器出栈popf标志出栈
proc,endp过程定义
ptr合成操作符p88
purge取消宏定义p191
push进栈pusha所有16位寄存器进栈pushad所有32位寄存器进栈pushf标志进栈rcl/rcr包含进位的左,右循环移位
rep无条件重复前缀
repe相等或为零时,重复前缀
repne不相等,不为零,重复前缀
ret返回
rol/ror不包含进位的左,右循环移位
sahf AH送标志位寄存器低八位
sbb带减借位标志位的减法sub不带减借位标志位的减法
sbyte/sword/sdword定义有符号的字节,字,双字,变量
scas搜索串
seg得到操作数的段基址
segment,ends定义完整段
shl/shr逻辑左移、右移sal/sar算术左移,右移
shld/shrd双精度左,右移
stack定义简化堆栈段
startup/exit程序开始,结束
stos存储串元素
struc/struct定义结构数据类型
test测试
type得到变量一个数据项的字节数
xadd交换并相加
xchg两操作数相互交换
xor逻辑异或。

相关文档
最新文档