(微机原理)汇编语言

您所在的位置:网站首页 伪逻辑是什么 (微机原理)汇编语言

(微机原理)汇编语言

2023-12-10 12:55| 来源: 网络整理| 查看: 265

汇编语言——伪指令详解

Wilson Huang(三点羊羽) 2020-06-17 07:39:10  1849  收藏 27

分类专栏: 微机原理、汇编语言

版权

1 汇编语言的语句结构 1.1 标号名 1.1.1 定义:

标号(LABEL)是为一组机器指令所起的名字.标号可有可无,只有当需要用符号地址来访问该语句时,才给此语句赋予标号,以 : 作为结束符,其是指令的符号地址,代表了指令第一个字节地址。

1.1.2 作用:

标号是程序的目标标志,总是和某地址相联系,供转移或循环指令控制转移使用.

AGAIN: ... ... ... JMP AGAIN 1234 1.1.3 具有的属性:

(1)段属性:标号名所在段的段基址,标号段必须在CS中

(2)偏移地址属性:标号名所在段的偏移地址,单位是字节,是16位无符号整数

(3)距离属性:当其作为控制转移类指令的操作数时,可在段内或段间转移,这是距离属性不同

SHORT 距离在-128 ~ 127之间时称短标号

NEAR 段内标号,只允许在本段转移,近标号,距离在-32768 ~ 32767之间

FAR 段间标号,允许在段间转移

距离属性可用三种方法定义:

隐含方式: 标号名后面跟 “ : ” ,隐含距离属性为NEAR

NEXT: ... ... ... LOOP NEXT 1234

用伪指令LABEL定义距离属性

格式:标号名 LABEL NEAR或标号名 LABEL FAR

NEXT LABEL FAR ... ... ... LOOP NEXT 12345

用EQU定义标号

格式:标号名 EQU THIS NEAR

NEXT EQU THIS NEAR  ... ... ... LOOP NEXT 12345 1.1.4 使用

无条件转移指令中标号作为转移地址

格式: JMP 标号

其中标号可以是短标号,近标号或远标号

循环指令中,标号作为转移地址

格式:LOOP 标号

其中标号只能是短标号

条件转移中标号作为转移地址

格式:条件转移指令 标号

其中标号只能用短标号

属性分离符

取段地址算符SEG

MOV AX,SEG NEXT ;SEG NEXT就是取标号NEXT所在段的段地址 1

取偏移量算符OFFSET

MOV BX,OFFSET NEXT ;其中OFFSET NEXT就是取标号NEXT的有效地址,该语句等效于:LEA BX,NEXT 1

取类型算符TYPE

MOV AX,TYPE NEXT ;若NEXT为近标号,则TYPE NEXT值为FFFFH(-1),若NEXT为远标号,TYPE NEXT值为FFFEH(-2).其中-1和-2 ;无真正的物理意义,仅以数值表示标号类型而已. 1 1.2 变量名

变量 (Variable) 代表存放在某些存储单元的数据,这些数据在程序运行期间可以随时被修改。变量是通过变量名在程序中引用,变量名实际上是存储区中一个数据区的名字,以变量名数据的方式供程序员使用,作为指令或伪指令的操作数,大大方便了程序设计者。

1.2.1 定义:

变量名是数据存储单元的符号地址,由汇编语言编译链接时为变量名分配存储单元,常作为一段数据区的符号地址,代表存储区域的第一个字节地址

1.2.2 具有的属性:

(1)段属性:标号名所在段的段基址

(2)偏移地址属性:标号名所在段的偏移地址

(3)类型属性:表示变量占用存储单元的字节数

类型属性存储单元字节数BYTE1WORD2DWORD4QWORD8TBYTE10 1.3 操作码 1.3.1 指令助记符

指令助记符,是指令的关键部分,如MOV,AOD,SHL等指令

1.3.2 伪指令助记符

没有对应的指令操作码,主要用来定义变量,分配存储地址等,例如DB/DW/DD等\

1.4 操作数

操作数可以是常数、寄存器、标号、变量或表达式等。可有可无

分类:

无操作数单操作数双操作数:必须用逗号隔开

伪指令和宏指令允许有多个操作数

伪指令操作数:由操作符构成

1.5 注释字段(comment field)

分号隔开

MOV AX,BX ;这里是注释 1 2 数据定义伪指令 2.1 数据定义伪指令

通过数据定义语句可以为数据项分配存储单元,并设置初值。代表数据项的标识符称为变量名。

格式: [变量名] DB/DW/DD/DQ/DT 表达式 [注释]

伪指令助记符说明DB定义字节型变量,每变量分配1个存储单元DW定义字型变量,每变量分配2个存储单元DD定义双字型变量,每变量分配4个存储单元DQ定义四字型变量,每变量分配8个存储单元DT定义十字型变量,每变量分配10个存储单元

分类:

2.1.1 数值表达式

表达式结果是确定的数值,用于初始化内存单元

DATA DB 24H DATA2 DW 1234H+5678H 12 2.1.2 ?数据项

若定义的变量处置不确定可以用“?”表示,这是分配一个与类型匹配的存储单元,用于保留内存单元

DATA_A DB ?,20H DATA_B DW 8543H,? 12 2.1.3 字符串

以ASCII码值得形式存放在存储区中,每个字符占据一个存储单元,可以用定义字节型变量的DB定义字符串,DW只可用来定义含有两个字符的字符串,字符串用单引号括起来,用于初始化内存单元。

STR1 DB 'MASM' STR2 DW 'MY' 12 2.1.4 重复操作符

定义多个类型与初值相同的变量:

格式: [变量名] DB/DW/DD n DUP (表达式)

(1) n:变量重复次数,可为常数、字符和?

BUF2 DB 20 DUP (?) ;定义了20个不确定值的字节型变量 1

(2)DUP还可以嵌套使用

BUF2 DB 10 DUP (2, 2 DUP (3)) ;编译时分配30个存储单元给BUF2 1 2.2 运算符伪指令 2.2.0 常数

(1)二进制常数:以字母B结尾

(2)八进制常数:以字母Q结尾

(3)十六进制常数:以字母D结尾(可忽略)

(4)字符串常数:以ASCII码形式存放

2.2.1 算术运算符

(1)+:加

(2)-:减

(3)*:乘

(4)/:除、取整

(5)MOD:模除、取余

(6)SHL:左移

(7)SHR:右移

MOV AL, 16 MOD 5 ;(AL)=1 MOV AL, 10100100B SHR 2 ;(AL)=00101001B 12 2.2.2 逻辑运算符

(1)AND:与

(2)OR:或

(3)NOT:非

(4)XOR:异或

用于数值表达式中对数值进行按位逻辑运算,结果为8、16或32位二进制数

注意与逻辑运算指令的区别!!!: 逻辑运算符在汇编时就有汇编程序完成逻辑运算了,而逻辑指令是在指令执行的时候完成

;(DL)=10110100B OR DL,NOT 7EH ;NOT 7EH在编译的时候数值已经确定为81H ;所以执行的时候等价为下面式子 OR DL,81H 1234 2.2.3 关系运算符

(1)EQ 等于

(2)NE 不等于

(3)LT 小于

(4)GT 大于

(5)GE 大于等于

(6)LE 小于等于

参与关系运算的必须是两个数值,若关系成立,结果为真(0FFFFH),否则为假(0000H)

MOV AX,((12 LT 8) AND 5) OR ((20 GE 9) AND 7) ;(12 LT 8)=0000H,(12 LT 8) AND 5=0 ;(20 GE 9)=0FFFFH,(20 GE 9) AND 7=7 ;AX=0007H 1234 2.2.4 分析运算符

SEG、OFFSET、TYPE加在变量名或标号名的前面

(1)SEG:得到变量名或标号名的段基址

(2)OFFSET:得到变量名或标号名的偏移量

(3)TYPE:在变量名前,返回值为1(字节)、2(字)、4(双字);在标号名前,返回值为-1(NEAR)、-2(FAR)

(4)LENGTH:用在变量前面,对于变量使用DUP进行定义的情况,汇编程序将回送分配给该变量的单元数。对于其他情况则回送1

(5)SIZE:用在变量前面,汇编程序将回送分配给该变量的字节数,值为 LENGTH 与 TYPE 值的乘积

3 属性说明伪指令 3.1 PTR操作符

格式: 类型 PTR 地址表达式

功能: 确定地址表达式的存储单元为指定的类型,即用在地址表达式之前,用于指定或临时改变变量名和标号名的类型

分类: BYTE、WORD、DWORD、NEAR、FAR等

例子:

ADD WORD PTR [SI],20 ;存储器操作数类型为字 JMP FAR PTR OK ;OK是可以进行段间转移的符号 12 3.2 THIS操作符

格式: THIS 类型

功能: 返回一个具有指定类型的存储器操作数。返回的存储器操作数地址的段基址和偏移地址就是下一个将要分配的存储单元的段基址和偏移地址。

例子:

MY_WORD EQU THIS WORD ;MY_WORD是一个字变量名,他的段基址和偏移地址与字节变量MY_BYTE相同 MY_BYTE DB ? 12 3.3 SHORT (短转移说明)

格式: JMP SHORT PTR 目标地址

功能: 转移的目标地址等于当前IP的内容加上8位的位移量,转移的目标地址距离本条指令的下一条指令之间的偏移量范围为-128~127

3.4 LABEL伪指令

格式: 变量号/标号 LABEL [类型]

功能: 通常和下一个语句所定义的变量和标号联用,给下一个语句所定义的变量和标号取别名。别名由LABEL左边的名称决定,而其类型属性或距离属性则由LABEL右边的参数来给定。

分类:

与变量连用(改变其类型属性)

TIMB LABEL BYTE ;为TIMW取别名TIMB,并修改类型为字节型 TIMW DW 1234H, 5678H ... ... ... MOV AX, TIMW[0] ;(AX)=1234H MOV BL, TIMB[0] ;(BX)=34H 1234567

定义堆栈段是也经常使用LABEL语句:

ASTACK SEGMENT DW 20 DUP (?) TOP LABEL WORD ASTACK ENDS ;这里定义了一个由20字组成的堆栈,其栈底名称取名为TOP,类型定义为字 1234

与标号连用(改变其距离属性)

SUBF LABEL FAR SUBN:MOV AX,[BX+SI];隐式定义了SUBN的距离属性为NEAR,前面为SUBN去了一个别名SUBF,属性为FAR。 ;SUBF和SUBN有着相同的逻辑地址,但他们的距离属性不同,可作为程序转移或调用时不同情况的入口,当段内转移或段间转移,调用不 ;同标号名称,实际上是指向同一条指令 1234 4 伪指令的优先级

序号运算符1LENGTH/、SIZE、WIDTH、MASK、()、[]、2PTR、OFFSET、SEG、TYPE、THIS3HIGH、LOW4*、/、MOD、SHL、SHR5+、-6EQ、NE、LT、LE、GT、GE7NOT8AND9OR、XOR10SHORT

建议使用圆括号

5 段定义与段分配伪指令

汇编语言的源程序分成若干个段:

数据段: 存放数据用堆栈段: 保存信息用程序段: 存放主程序用 5.1 段定义伪指令 5.1.1 段的基本属性

格式: (SEGMENT/ENDS伪指令)

段名 SEGMENT [定位类型] [组合类型] [字长选择] [类别名] ... (段体) ... 段名 ENDS 12345

任何一个逻辑段从SEGMENT开始,以ENDS结束

段名:

是赋予该段的一个名称,代表了该段的段地址

程序中的段名可以是唯一的,也可以与其它段同名。在同一模块中,如果有二个段同名,则后者被认为是前段的后续,这样,它们就属同一段。

当同一模块出现二个同名段时,则后者的可选项属性要么与前者相同,要么不写其属性而选用前者的段属性

DATA1 SEGMENT        ;第一个数据段 MSG DB "Hello, " DATA1 ENDS CODE1 SEGMENT        ;第一个代码段 ASSUME CS:CODE1, DS:DATA1 START: MOV AX, DATA1 MOV DS, AX MOV DX, offset MSG MOV AH, 9 INT 21H CODE1 ENDS DATA1 SEGMENT        ;第二个数据段  DB "World.$" DATA1 ENDS CODE1 SEGMENT        ;第二个代码段 MOV AX, 4C00H INT 21H CODE1 ENDS END START END 1234567891011121314151617181920

在上面的例子中,第二个数据段是第一个数据段的后续,汇编程序把它们是合二为一,上述的代码段也如此。

5.1.2 定位类型、对齐类型(ALIGN)

其表示对段的起始边界的要求,连接程序(LINK.EXE)按下面列出的地址格式来定位段的起始地址。在进行段定位时,会根据其定位类型进行定位的,所以,各段之间就有可能出现一些空闲字节,即可能浪费几个字节单元。

PAGE(页)

表示本段从一页的边界开始,一页为256个字节,所以PAGE定义的边界的地址可以整除256,段的起始地址的后八位二进制数一定为0(即以00H结尾)

段的起始位置为:XXXX XXXX XXXX 0000 0000B最多的空闲字节数:127

PARA(节)

表示本段从一节的边界开始,一节为16字节,段的起始地址的后四位二进制数一定为0(即以00H结尾)

段的起始位置为: XXXX XXXX XXXX XXXX 0000B

最多的空闲字节数:15

段对齐类型PARA是一个适用于所有段类型的对齐类型,它也是缺省的对齐类型。

DWORD(双字)

表示本段的起始位置可以能被4整除。若用二进制表示后两位,则为00B

段的起始位置为: XXXX XXXX XXXX XXXX XX00B

最多的空闲字节数:3

对齐类型DWORD通常用于80386及其以后CPU代码段的定位。

WORD(字)

表示本段从偶地址开始。若用二进制数表示,则该地址的最低位为0B

段的起始位置为: XXXX XXXX XXXX XXXX XXX0B最多的空闲字节数:1

BYTE(字节)

表示本段可以从任何地址开始定位

段的起始位置为: XXXX XXXX XXXX XXXX XXXXB最多的空闲字节数:0

对齐类型BYTE和WORD通常用于数据段的定位

5.1.3 组合类型

用来对各个逻辑段之间的连接方式提出要求,告诉连接程序如何把不同模块中段名相同的段合并在一起。具体的组合类型如下:

NONE

表示该段与其他同名段不进行连接,独立存于存储器中,如果语句中省略组合类型,则MASM把它作为NONE处理

PUBLIC

表示该段与其他模块中的同名段在满足定位类型的前提下由低地址到高地址连接起来,组成一个比较大的逻辑段

COMMON

表示当前段与其它模块中同名段重叠,也就是说,它们的起始地址相同,共享同一个存储区。最终段的长度是同名段的最大长度。由于段覆盖,所以,前一同名段中的初始化数据被后续段的初始数据覆盖掉。段的内容为所链接的最后一个模块中的内容及没有覆盖到的前面COMMON段的部分内容。

MEMORY

除了带有MEMORY参数的逻辑段覆盖在其他同名段的最高地址,与COMMON没其他区别

STACK

表示该段为堆栈段,其组合情况与PUBLIC相同。

AT表达式

该数值表达式是当前段所指定的绝对起始地址的段地址。表示本段可以定义在表达式所指示的节边界上。

CODE SEGMENT AT 1200H ;表示该代码段段的起始位置为12000H,第一条指令从12005H处开始执行 ORG 0005H START: CODE ENDS 12345 5.1.4 字长选择

用于定义段中使用的偏移地址和寄存器的字长,只用于设置32位微型计算机中语句的段

USE16

偏移地址为16位,段内最大寻址空间为64KB

USE32

偏移地址为32位,段内最大寻址空间为4GB

5.1.5 类别(CLASS)

类别是一个由程序员指定的用单引号括起来的字符串,用于连接时决定各逻辑段被装入的顺序,即具有相同类别名额逻辑段按出现的先后顺序被装入连续的内存中。如果一个段没有给出类别,那么,这个段的类别就为空。类别是用于段的分类,连接程序利用该类别来调整同名、同类别的段,并使它们相邻。典型的类别是"Data"和"Code"。如果指定某段的类别是"Code",那么,该段最好是代码段,这样,有的调试程序(如:CodeView)就可以顺序工作。

DATA1 SEGMENT WORD PUBLIC "Data" … DATA1 ENDS 123

上述段定义说明了该段的起始地址是下一个字地址、组合类型为PUBLIC、段类别是"Data"。

5.2 段分配伪指令

ASSUME伪指令来指明各段。放在代码段内,放在段定义语句之后

格式: ASSUME 段寄存器名:段名[,段寄存器名:段名, ...]

ASSUME CS: CODE, DS: DATA, SS:STACK 1

段分配语句只建立当前段和段寄存器之间的联系,但段分配语句并不能将各段的段基址装入各个段寄存器。段基址的装入需要用程序额的方法,而且6个段寄存器的装入也不尽相同。

段组:

段组伪指令GROUP是用于把源程序模块中若干个段结合成一个组,并对该段组定义一个段组名。段组伪指令的格式如下:

段组名 GROUP 段名[, 段名, ……]

其中:段名之间要用逗号间隔,段名也可以用表达式“SEG 变量”或“SEG 标号”。

举例:方法1:用一个段寄存器对应二个数据段

DATA1 SEGMENT ;第一个数据段 b1 DB 10h DATA1 ENDS DATA2 SEGMENT ;第二个数据段 b2 DB 23h DATA2 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:DATA1 ;(1) MOV AX, DATA1 MOV DS, AX ;(2)把数据段DATA1的段值赋给段寄存器DS ... MOV BL, b1 ;(3)引用DS来访问DATA1中的变量b1 ... START: ASSUME DS:DATA2 ;(4) MOV AX, DATA2 MOV DS, AX ;(5)把数据段DATA2的段值赋给段寄存器DS ... MOV AL, b2 ;(6)引用DS来访问DATA2中的变量b2 ... CODE1 ENDS END START 123456789101112131415161718192021

在上例中,语句(1)说明DS与DATA1建立联系,语句(2)对DS赋值,语句(3)用DS来访问DATA1段的变量名。语句(4)说明DS与DATA2建立联系,语句(5)对DS赋值,语句(6)用DS来访问DATA2段的变量名。

在该例子中,因为只使用一个段寄存器DS来对应二个数据段,所以,需要切换DS的对应关系(如:语句(4))。但我们也可以用段寄存器DS和ES来分别对应段DATA1和DATA2,这样,方法1就可变成方法2。

方法2:用二个段寄存器对应二个数据段

DATA1 SEGMENT b1 DB 10h DATA1 ENDS DATA2 SEGMENT b2 DB 23h DATA2 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:DATA1, ES:DATA2 START: MOV AX, DATA1 MOV DS, AX ;把数据段DATA1的段值赋给段寄存器DS MOV AX, DATA2 MOV ES, AX ;把数据段DATA2的段值赋给段寄存器ES ... MOV BL, b1 ;引用DS来访问DATA1中的变量b1 ... MOV AL, b2 ;引用ES来访问DATA2中的变量b2 ... CODE1 ENDS END START 12345678910111213141516171819

我们还可以用段组来简化段寄存器的使用,把段DATA1和DATA2组成一个数据段。所以,把方法2再改写成方法3的形式。

方法3:用一个段组组成二个数据段

GSEG GROUP DATA1, DATA2 ;把段DATA1和DATA2定义成一个段组 DATA1 SEGMENT b1 DB 10h DATA1 ENDS DATA2 SEGMENT b2 DB 23h DATA2 ENDS CODE1 SEGMENT ASSUME CS:CODE1, DS:GSEG START: MOV AX, GSEG MOV DS, AX ;把段组GSEG的段值赋给段寄存器DS ... MOV BL, b1 ;引用DS来访问DATA1中的变量b1 ... MOV AL, b2 ;引用DS来访问DATA2中的变量b2 ... CODE1 ENDS END START 123456789101112131415161718

定义段组后,段组内各段所定义的标号和变量,除了与定义它们的段起始点相关外,还与段组的起始点相关。规定如下:

如果在ASSUME伪指令中说明段组与段寄存器相对应,那么,有关标号或变量的偏移量就相对于段组起点计算;如果在ASSUME伪指令中说明段组内的某各段与段寄存器相对应,那么,有关标号或变量的偏移量就相对于该段的起点。

所以,在使用段组后,程序员要谨慎使用ASSUME伪指令,并保证段寄存器的值与段组或段相一致。

6 赋值伪指令 6.1 表达式赋值伪指令(EQU)

表达式赋值伪指令为常量、变量、表达式或其他符号定义一个新的名字,但不分配内存空间

格式: 符号名 EQU 表达式

表达式可以是常数、数值表达式、地址表达式、变量、标号或指令助记符

(1)表达式中,变量名及标号名必须在伪指令前定义

(2)在同一程序段中,不允许同一个变量名重新定义

NUM EQU 2*4 ;NUM代表常数8 COUNT EQU 4*NUM ;已定义的符号常数可以用于表达式 STRING1 EQU 'HELLO WORLD!' ;用符号表示一个字符串 MOVE EQU MOV ;重新定义指令助记符 1234 6.2 等号伪指令

可以用来定义符号常数,等号语句定义的符号名允许重新定义。

格式: 符号名=数值表达式

7 地址定义伪指令 7.1 地址计数器($)

为了指示程序中指令或数据在相应段中的偏移地址,可使用地址定位伪指令和当前地址计数器

$表示地址计数器的值。地址计数器保存当前正在汇编的指令或数据的偏移地址。汇编程序每扫描一个字节,地址计数器的值加1

例如:

JMP $ 1

程序跳转到本条指令,即进入死循环状态。该语句一般用于等待中断的发生。

再如:

DATA SEGMENT ARRAY DB 'PROGRAM' NUM EQU $-ARRAY ;$表示当前指令的偏移地址值,ARRAY是变量名,表示变量ARRAY的偏移地址值,$-ARRAY是以变量ARRAY为其 ;值地址的连续字节数,即变量名为ARRAY的字符串的字符个数 DATA ENDS 1234 7.2 定位伪指令(ORG)

格式: ORG 数值表达式

作用: 用于调整地址计数器的当前值,数值表达式则会给出偏移地址的值。当对该语句进行汇编的时候,将地址计数器的值调整成数值表达式的结果,表达式运算的结果必须是正整数,并且以65535为模。

ORG 0100H BEGIN: MOV AL, 15H ORG 0500H NUM DW 7890H 1234

标号BEGIN的偏移地址是0100H,变量NUM的偏移地址为0500H

8 过程定义伪指令

过程——子程序(可被程序调用),汇编语言规定必须对过程进行定义。过程定义之后就可对(调用指令CALL)和(返回指令RET)进行正确的汇编。

如果过程中要用到某些寄存器或存储单元,为了不破坏原有的信息,要将寄存器或存储单元的原有内容压栈保护或存入子程序不用的寄存器或存储单元中。起保护作用的程序段可以放在主程序中,亦可以放在子程序中。

过程定义语句的格式为:

过程名 PROC NEAR/FAR 语句 ... RET ;过程的最后一条执行指令,。将堆栈内保存的返回地址弹出,以实现程序的正确返回 过程名 ENDP 12345 NEAR(近过程): 该过程与调用指令CALL处在同一代码段中(段名相同),只需将返回未知的偏移地址压入堆栈FAR(远过程): 该过程与调用指令CALL处在不同代码段中(段名不同),需将返回未知的偏移地址和段基址都压入堆栈 CODE SEGMENT PARA PUBLIC 'CODE' ASSUME CS: CODE, DS: DATA, SS: STACK SUBB1 PROC NEAR/FAR 语句 ... RET SUBB1 ENDP START: CALL SUBB1 ... CALL SUBB1 ... CODE ENDS END START 1234567891011121314 9 程序模块命名与程序结束伪指令

NAME/END伪指令

用于定义一个模块。在链接目标模块时将使用该模块名,汇编处理只进行到模块结束语句为止

格式:

NAME 模块名 ... END 标号 123

PUBLIC伪指令

用来说明已知模块中哪些标识符是公共的,可以被其他模块引用。

格式: PUBLIC 符号

符号可以是本模块已定义的变量、标号、变量名、过程名等

EXTERN伪指令

用于说明模块中哪些标识符是外部的,即其他模块中已被PUBLIC伪指令说明的符号

格式: EXTERN 符号名:类型[,符号名: 类型··· ]

END伪指令

一个程序模块只允许有一个END语句,后为主模块其实地址

格式: END [起始地址标号]

汇编程序将起始地址标号的段基址和偏移地址赋给当前的CS、IP

https://blog.csdn.net/weixin_43229030/article/details/106799580 



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3