Wilson Huang(三点羊羽) 2020-06-17 07:39:10 1849 收藏 27
分类专栏: 微机原理、汇编语言
版权
标号(LABEL)是为一组机器指令所起的名字.标号可有可无,只有当需要用符号地址来访问该语句时,才给此语句赋予标号,以 :
作为结束符,其是指令的符号地址,代表了指令第一个字节地址。
标号是程序的目标标志,总是和某地址相联系,供转移或循环指令控制转移使用.
AGAIN: ...
...
...
JMP AGAIN
(1)段属性:标号名所在段的段基址,标号段必须在CS中
(2)偏移地址属性:标号名所在段的偏移地址,单位是字节,是16位无符号整数
(3)距离属性:当其作为控制转移类指令的操作数时,可在段内或段间转移,这是距离属性不同
SHORT
距离在-128 ~ 127之间时称短标号
NEAR
段内标号,只允许在本段转移,近标号,距离在-32768 ~ 32767之间
FAR
段间标号,允许在段间转移
距离属性可用三种方法定义:
隐含方式: 标号名后面跟 “ : ” ,隐含距离属性为
NEAR
NEXT: ... ... ... LOOP NEXT
- 1
- 2
- 3
- 4
用伪指令LABEL定义距离属性
格式:
标号名 LABEL NEAR
或标号名 LABEL FAR
NEXT LABEL FAR ... ... ... LOOP NEXT
- 1
- 2
- 3
- 4
- 5
用EQU定义标号
格式:
标号名 EQU THIS NEAR
NEXT EQU THIS NEAR ... ... ... LOOP NEXT
- 1
- 2
- 3
- 4
- 5
无条件转移指令中标号作为转移地址
格式: JMP 标号
其中标号可以是短标号,近标号或远标号
循环指令中,标号作为转移地址
格式:LOOP 标号
其中标号只能是短标号
条件转移中标号作为转移地址
格式:条件转移指令 标号
其中标号只能用短标号
属性分离符
取段地址算符SEG
MOV AX,SEG NEXT ;SEG NEXT就是取标号NEXT所在段的段地址
取偏移量算符OFFSET
MOV BX,OFFSET NEXT ;其中OFFSET NEXT就是取标号NEXT的有效地址,该语句等效于:LEA BX,NEXT
取类型算符TYPE
MOV AX,TYPE NEXT ;若NEXT为近标号,则TYPE NEXT值为FFFFH(-1),若NEXT为远标号,TYPE NEXT值为FFFEH(-2).其中-1和-2 ;无真正的物理意义,仅以数值表示标号类型而已.
变量 (Variable) 代表存放在某些存储单元的数据,这些数据在程序运行期间可以随时被修改。变量是通过变量名在程序中引用,变量名实际上是存储区中一个数据区的名字,以变量名数据的方式供程序员使用,作为指令或伪指令的操作数,大大方便了程序设计者。
变量名是数据存储单元的符号地址,由汇编语言编译链接时为变量名分配存储单元,常作为一段数据区的符号地址,代表存储区域的第一个字节地址
(1)段属性:标号名所在段的段基址
(2)偏移地址属性:标号名所在段的偏移地址
(3)类型属性:表示变量占用存储单元的字节数
类型属性 | 存储单元字节数 |
---|---|
BYTE | 1 |
WORD | 2 |
DWORD | 4 |
QWORD | 8 |
TBYTE | 10 |
指令助记符,是指令的关键部分,如MOV,AOD,SHL等指令
没有对应的指令操作码,主要用来定义变量,分配存储地址等,例如DB/DW/DD等\
操作数可以是常数、寄存器、标号、变量或表达式等。可有可无
分类:
伪指令和宏指令允许有多个操作数
伪指令操作数:由操作符构成
分号隔开
MOV AX,BX ;这里是注释
通过数据定义语句可以为数据项分配存储单元,并设置初值。代表数据项的标识符称为变量名。
格式: [变量名] DB/DW/DD/DQ/DT 表达式 [注释]
伪指令助记符 | 说明 |
---|---|
DB | 定义字节型变量,每变量分配1个存储单元 |
DW | 定义字型变量,每变量分配2个存储单元 |
DD | 定义双字型变量,每变量分配4个存储单元 |
DQ | 定义四字型变量,每变量分配8个存储单元 |
DT | 定义十字型变量,每变量分配10个存储单元 |
分类:
表达式结果是确定的数值,用于初始化内存单元
DATA DB 24H
DATA2 DW 1234H+5678H
若定义的变量处置不确定可以用“?”表示,这是分配一个与类型匹配的存储单元,用于保留内存单元
DATA_A DB ?,20H
DATA_B DW 8543H,?
以ASCII码值得形式存放在存储区中,每个字符占据一个存储单元,可以用定义字节型变量的DB定义字符串,DW只可用来定义含有两个字符的字符串,字符串用单引号括起来,用于初始化内存单元。
STR1 DB 'MASM'
STR2 DW 'MY'
定义多个类型与初值相同的变量:
格式: [变量名] DB/DW/DD n DUP (表达式)
(1) n:变量重复次数,可为常数、字符和?
BUF2 DB 20 DUP (?) ;定义了20个不确定值的字节型变量
(2)DUP还可以嵌套使用
BUF2 DB 10 DUP (2, 2 DUP (3)) ;编译时分配30个存储单元给BUF2
(1)二进制常数:以字母B结尾
(2)八进制常数:以字母Q结尾
(3)十六进制常数:以字母D结尾(可忽略)
(4)字符串常数:以ASCII码形式存放
(1)+
:加
(2)-
:减
(3)*
:乘
(4)/
:除、取整
(5)MOD
:模除、取余
(6)SHL
:左移
(7)SHR
:右移
MOV AL, 16 MOD 5 ;(AL)=1
MOV AL, 10100100B SHR 2 ;(AL)=00101001B
(1)AND
:与
(2)OR
:或
(3)NOT
:非
(4)XOR
:异或
用于数值表达式中对数值进行按位逻辑运算,结果为8、16或32位二进制数
注意与逻辑运算指令的区别!!!: 逻辑运算符在汇编时就有汇编程序完成逻辑运算了,而逻辑指令是在指令执行的时候完成
;(DL)=10110100B OR DL,NOT 7EH ;NOT 7EH在编译的时候数值已经确定为81H ;所以执行的时候等价为下面式子 OR DL,81H
- 1
- 2
- 3
- 4
(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
SEG
、OFFSET
、TYPE
加在变量名或标号名的前面
(1)SEG
:得到变量名或标号名的段基址
(2)OFFSET
:得到变量名或标号名的偏移量
(3)TYPE
:在变量名前,返回值为1(字节)、2(字)、4(双字);在标号名前,返回值为-1(NEAR)、-2(FAR)
(4)LENGTH
:用在变量前面,对于变量使用DUP进行定义的情况,汇编程序将回送分配给该变量的单元数。对于其他情况则回送1
(5)SIZE
:用在变量前面,汇编程序将回送分配给该变量的字节数,值为 LENGTH 与 TYPE 值的乘积
格式: 类型 PTR 地址表达式
功能: 确定地址表达式的存储单元为指定的类型,即用在地址表达式之前,用于指定或临时改变变量名和标号名的类型
分类: BYTE
、WORD
、DWORD
、NEAR
、FAR
等
例子:
ADD WORD PTR [SI],20 ;存储器操作数类型为字
JMP FAR PTR OK ;OK是可以进行段间转移的符号
格式: THIS 类型
功能: 返回一个具有指定类型的存储器操作数。返回的存储器操作数地址的段基址和偏移地址就是下一个将要分配的存储单元的段基址和偏移地址。
例子:
MY_WORD EQU THIS WORD ;MY_WORD是一个字变量名,他的段基址和偏移地址与字节变量MY_BYTE相同
MY_BYTE DB ?
格式: JMP SHORT PTR 目标地址
功能: 转移的目标地址等于当前IP的内容加上8位的位移量,转移的目标地址距离本条指令的下一条指令之间的偏移量范围为-128~127
格式: 变量号/标号 LABEL [类型]
功能: 通常和下一个语句所定义的变量和标号联用,给下一个语句所定义的变量和标号取别名。别名由LABEL左边的名称决定,而其类型属性或距离属性则由LABEL右边的参数来给定。
分类:
与变量连用(改变其类型属性)
TIMB LABEL BYTE ;为TIMW取别名TIMB,并修改类型为字节型
TIMW DW 1234H, 5678H
...
...
...
MOV AX, TIMW[0] ;(AX)=1234H
MOV BL, TIMB[0] ;(BX)=34H
定义堆栈段是也经常使用LABEL语句:
ASTACK SEGMENT
DW 20 DUP (?)
TOP LABEL WORD
ASTACK ENDS ;这里定义了一个由20字组成的堆栈,其栈底名称取名为TOP,类型定义为字
与标号连用(改变其距离属性)
SUBF LABEL FAR
SUBN:MOV AX,[BX+SI];隐式定义了SUBN的距离属性为NEAR,前面为SUBN去了一个别名SUBF,属性为FAR。
;SUBF和SUBN有着相同的逻辑地址,但他们的距离属性不同,可作为程序转移或调用时不同情况的入口,当段内转移或段间转移,调用不
;同标号名称,实际上是指向同一条指令
高
低
序号 | 运算符 |
---|---|
1 | LENGTH/、SIZE、WIDTH、MASK、()、[]、<> |
2 | PTR、OFFSET、SEG、TYPE、THIS |
3 | HIGH、LOW |
4 | *、/、MOD、SHL、SHR |
5 | +、- |
6 | EQ、NE、LT、LE、GT、GE |
7 | NOT |
8 | AND |
9 | OR、XOR |
10 | SHORT |
建议使用圆括号
汇编语言的源程序分成若干个段:
格式: (SEGMENT/ENDS伪指令)
段名 SEGMENT [定位类型] [组合类型] [字长选择] [类别名]
...
(段体)
...
段名 ENDS
任何一个逻辑段从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
在上面的例子中,第二个数据段是第一个数据段的后续,汇编程序把它们是合二为一,上述的代码段也如此。
其表示对段的起始边界的要求,连接程序(LINK.EXE)按下面列出的地址格式来定位段的起始地址。在进行段定位时,会根据其定位类型进行定位的,所以,各段之间就有可能出现一些空闲字节,即可能浪费几个字节单元。
PAGE(页)
表示本段从一页的边界开始,一页为256个字节,所以PAGE定义的边界的地址可以整除256,段的起始地址的后八位二进制数一定为0(即以00H结尾)
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
BYTE(字节)
表示本段可以从任何地址开始定位
对齐类型BYTE和WORD通常用于数据段的定位
用来对各个逻辑段之间的连接方式提出要求,告诉连接程序如何把不同模块中段名相同的段合并在一起。具体的组合类型如下:
NONE
表示该段与其他同名段不进行连接,独立存于存储器中,如果语句中省略组合类型,则MASM把它作为NONE处理
PUBLIC
表示该段与其他模块中的同名段在满足定位类型的前提下由低地址到高地址连接起来,组成一个比较大的逻辑段
COMMON
表示当前段与其它模块中同名段重叠,也就是说,它们的起始地址相同,共享同一个存储区。最终段的长度是同名段的最大长度。由于段覆盖,所以,前一同名段中的初始化数据被后续段的初始数据覆盖掉。段的内容为所链接的最后一个模块中的内容及没有覆盖到的前面COMMON段的部分内容。
MEMORY
除了带有MEMORY参数的逻辑段覆盖在其他同名段的最高地址,与COMMON没其他区别
STACK
表示该段为堆栈段,其组合情况与PUBLIC相同。
AT表达式
该数值表达式是当前段所指定的绝对起始地址的段地址。表示本段可以定义在表达式所指示的节边界上。
CODE SEGMENT AT 1200H ;表示该代码段段的起始位置为12000H,第一条指令从12005H处开始执行
ORG 0005H
START:
CODE ENDS
用于定义段中使用的偏移地址和寄存器的字长,只用于设置32位微型计算机中语句的段
USE16
偏移地址为16位,段内最大寻址空间为64KB
USE32
偏移地址为32位,段内最大寻址空间为4GB
类别是一个由程序员指定的用单引号括起来的字符串,用于连接时决定各逻辑段被装入的顺序,即具有相同类别名额逻辑段按出现的先后顺序被装入连续的内存中。如果一个段没有给出类别,那么,这个段的类别就为空。类别是用于段的分类,连接程序利用该类别来调整同名、同类别的段,并使它们相邻。典型的类别是"Data"和"Code"。如果指定某段的类别是"Code",那么,该段最好是代码段,这样,有的调试程序(如:CodeView)就可以顺序工作。
DATA1 SEGMENT WORD PUBLIC "Data"
…
DATA1 ENDS
上述段定义说明了该段的起始地址是下一个字地址、组合类型为PUBLIC、段类别是"Data"。
ASSUME伪指令来指明各段。放在代码段内,放在段定义语句之后
格式: ASSUME 段寄存器名:段名[,段寄存器名:段名, ...]
ASSUME CS: CODE, DS: DATA, SS:STACK
段分配语句只建立当前段和段寄存器之间的联系,但段分配语句并不能将各段的段基址装入各个段寄存器。段基址的装入需要用程序额的方法,而且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
在上例中,语句(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
我们还可以用段组来简化段寄存器的使用,把段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
定义段组后,段组内各段所定义的标号和变量,除了与定义它们的段起始点相关外,还与段组的起始点相关。规定如下:
- 如果在ASSUME伪指令中说明段组与段寄存器相对应,那么,有关标号或变量的偏移量就相对于段组起点计算;
- 如果在ASSUME伪指令中说明段组内的某各段与段寄存器相对应,那么,有关标号或变量的偏移量就相对于该段的起点。
所以,在使用段组后,程序员要谨慎使用ASSUME伪指令,并保证段寄存器的值与段组或段相一致。
表达式赋值伪指令为常量、变量、表达式或其他符号定义一个新的名字,但不分配内存空间
格式: 符号名 EQU 表达式
表达式可以是常数、数值表达式、地址表达式、变量、标号或指令助记符
(1)表达式中,变量名及标号名必须在伪指令前定义
(2)在同一程序段中,不允许同一个变量名重新定义
NUM EQU 2*4 ;NUM代表常数8
COUNT EQU 4*NUM ;已定义的符号常数可以用于表达式
STRING1 EQU 'HELLO WORLD!' ;用符号表示一个字符串
MOVE EQU MOV ;重新定义指令助记符
可以用来定义符号常数,等号语句定义的符号名允许重新定义。
格式: 符号名=数值表达式
为了指示程序中指令或数据在相应段中的偏移地址,可使用地址定位伪指令和当前地址计数器
$
表示地址计数器的值。地址计数器保存当前正在汇编的指令或数据的偏移地址。汇编程序每扫描一个字节,地址计数器的值加1
例如:
JMP $
程序跳转到本条指令,即进入死循环状态。该语句一般用于等待中断的发生。
再如:
DATA SEGMENT
ARRAY DB 'PROGRAM'
NUM EQU $-ARRAY ;$表示当前指令的偏移地址值,ARRAY是变量名,表示变量ARRAY的偏移地址值,$-ARRAY是以变量ARRAY为其 ;值地址的连续字节数,即变量名为ARRAY的字符串的字符个数
DATA ENDS
格式: ORG 数值表达式
作用: 用于调整地址计数器的当前值,数值表达式则会给出偏移地址的值。当对该语句进行汇编的时候,将地址计数器的值调整成数值表达式的结果,表达式运算的结果必须是正整数,并且以65535为模。
ORG 0100H
BEGIN: MOV AL, 15H
ORG 0500H
NUM DW 7890H
标号BEGIN的偏移地址是0100H,变量NUM的偏移地址为0500H
过程——子程序(可被程序调用),汇编语言规定必须对过程进行定义。过程定义之后就可对(调用指令CALL)和(返回指令RET)进行正确的汇编。
如果过程中要用到某些寄存器或存储单元,为了不破坏原有的信息,要将寄存器或存储单元的原有内容压栈保护或存入子程序不用的寄存器或存储单元中。起保护作用的程序段可以放在主程序中,亦可以放在子程序中。
过程定义语句的格式为:
过程名 PROC NEAR/FAR
语句
...
RET ;过程的最后一条执行指令,。将堆栈内保存的返回地址弹出,以实现程序的正确返回
过程名 ENDP
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
NAME/END伪指令
用于定义一个模块。在链接目标模块时将使用该模块名,汇编处理只进行到模块结束语句为止
格式:
NAME 模块名
...
END 标号
PUBLIC伪指令
用来说明已知模块中哪些标识符是公共的,可以被其他模块引用。
格式: PUBLIC 符号
符号可以是本模块已定义的变量、标号、变量名、过程名等
EXTERN伪指令
用于说明模块中哪些标识符是外部的,即其他模块中已被PUBLIC伪指令说明的符号
格式: EXTERN 符号名:类型[,符号名: 类型··· ]
END伪指令
一个程序模块只允许有一个END语句,后为主模块其实地址
格式: END [起始地址标号]
汇编程序将起始地址标号的段基址和偏移地址赋给当前的CS、IP
https://blog.csdn.net/weixin_43229030/article/details/106799580