汇编语言学习笔记

四、汇编语言程序格式

###语句基本格式
汇编语言程序中的语句由 4 项组成,格式如下:

[name] operation operand [;comment]

名字(name)项是一个符号。
操作项(operation)是一个操作的助记符,它可以是指令、伪操作或宏指令名。
操作数(operand)项由一个或多个表达式组成,它提供为执行所要求的操作而需要的信息。
注释(comment)项用来说明程序或语句的功能。“;”为识别注释项的开始。“;”也可以从一行的第一个字符开始, 此时整行都是注释,常用来说明下面一段程序的功能。

名字项

名字项可以是:标号或变量
源程序中用下列字符来表示名字:
字母 AZ 数字 09 专用字符 ? . @ _ $
除数字外,所有字符都可以放在源语句的第一个位置
名字中如果用到 . ,则必须是第一个字符
可以用很多字符来说明名字,但只有前面的 31 个字符能被汇编程序所识别

标号

标号在代码段中定义,后面跟着冒号:,它可以作为过程名定义。
标号有3 种属性:段、偏移及类型
属性定义标号的段起始地址,此值必须在一个段寄存器中,而标号的段则总是在CS寄存器中。
偏移属性,标号的偏移地址是从段起始地址到定义标号的位置之间的字节数。
类型属性,用来指出该标号是在本段内引用还是在其他段中引用的。如是在段内引用的,则称为NEAR,否则为FAR。

变量

变量在数据段或附加数据段中定义,后面不跟冒号。变量经常在操作数字段出现。它也有段、偏移及类型三种属性。
属性:定义变量的段起始地址,此值必须在一个段寄存器中。
偏移属性:变量的偏移地址是从段的起始地址到定义变量的位置之间的字节数。在当前段内给出变量的偏移值等于当前地址计数器的值。
类型属性:变量的类型属性定义该变量所保留的字节数。如 BYTE(1个字节长)、WORD(2个字节长)、DWORD(4个字节长) 。

注释项

注释项用来说明一段程序、一条或几条指令的功能,它是可有可无的。
但是,对于汇编语言程序来说,注释项的作用是很明显的,它可以使程序容易被读懂,因此汇编语言程序必须写好注释。
注释应该写出本条( 或本段 )指令在程序中的功能和作用,而不应该只写指令的动作。

数据定义及存储器分配伪操作

伪操作的格式是:

[Variable] Mnemonic  Operand , … ,Operand [;Comments] 

操作数(Operand)可以是常数,或者是表达式 。
助记符(Mnemonic)字段说明所用伪操作的助记符,即伪操作,说明所定义的数据类型。
变量(Variable)字段是可选的,它用符号地址表示,其作用与指令语句前的标号相同,但它的后面不跟冒号。如果语句中有变量,则表示第一个字节的偏移地址。
注释(Comments)字段用来说明该伪操作的功能,它也是可选的。

Mnemonic(助记符)

DB 伪操作用来定义字节,其后的每个操作数都占有一个字节。
DW 伪操作用来定义字,其后的每个操作数占有一个字(16位,其低位字节在第一个字节地址中,高位字节在第二个字节地址中)。
DD 伪操作用来定义双字,其后的每个操作数占有两个字(32位)。

例如
image.png

a. 操作数可以是常数 , 或是表达式
b. 操作数 ? 可以保留存储空间,但不存入数据
c. 操作数还可以使用复制操作符来复制某个(或某些)操作数。其格式为:

repeat-count DUP(operand, … ,operand)

其中repeat-count可以是一个表达式,它的值应该是一个正整数,用来指定括号中的操作数的重复次数
d. 可以用DW或DD伪操作存地址。 DW把变量或标号的偏移地址存入存储器;DD把由16位段地址和16位偏移地址组成的整个地址存入存储器。
e. 用DD伪操作存入地址时,第1个字为偏移地址,第2个字为段地址。
image.png
image.png

###伪操作
####定义属性伪操作 LABEL
LABEL伪操作来定义属性
其格式为

name  LABEL  type

对于数据项可表示为:

variable-name  LABEL  type

其中类型可以是BYTE,WORD,DWORD。
对于可执行的代码可表示为:

label-name  LABEL  type

其中类型可以是NEAR或FAR。对于16位段,NEAR为2字节,FAR为4字节可。

表达式赋值伪操作 EQU

有时程序中多次出现同一个表达式,可以用赋值伪操作给表达式赋予一个名字。其格式如下:Expression_name EQU Expression
此后,表达式名可以代替该表达式。EQU 的引入提高了程序的可读性,也使其更加易于修改。
表达式可以是任何有效的操作数格式,任何可求出常数值的表达式,任何有效的助记符。

a. 在EQU语句的表达式中,如果有变量或标号的表达式,则在该语句前应该先给出它们的定义
b. 与EQU相类似的 =伪操作也可以作为赋值伪操作使用。区别是EQU伪操作中的表达式名是不允许重复定义的,而 = 伪操作则允许重复定义
image.png

地址计数器与对准伪操作

1. 地址计数器 $

在汇编程序对源程序汇编时,用地址计数器来保存当前正在汇编的指令的偏移地址。
当开始汇编或在每一段开始时,把地址计数器初始化为零,以后在汇编过程中,每处理一条指令,地址计数器就增加一个值,此值为该指令所需要的字节数。
地址计数器的值可用$来表示,汇编语言允许用户直接用$来引用地址计数器的值。

【例】JNE $+6
表示的转向地址是JNE指令的首地址加上6。

当$用在指令中时,它表示本条指令的第一个字节的地址。在这里,$+6 必须是另一条指令的首地址。否则,汇编程序将指示出错信息。
当\$用在伪操作的参数字段时(例如DB的参数),则和它用在指令中的情况不同,它所表示的是地址计数器的当前值

2. ORG 伪操作

ORG伪操作用来设置当前地址计数器的值,其格式为

ORG  constant expression

ORG伪操作可以使下一个字节的地址成为常数表达式的值n。

操作数项

操作数项由一个或多个表达式组成,多个操作数项之间一般用逗号分开。
对于指令,操作数项一般给出操作数地址,它们可能有一个或两个,或一个也没有。
对于伪操作或宏指令,则给出它们所要求的参数。
操作数项可以是常数、寄存器、标号、变量或由表达式组成。
表达式是常数、寄存器、标号、变量与一些操作符相组合的序列,可以有数字表达式地址表达式两种。
在汇编期间,汇编程序按照一定的优先规则对表达式进行计算后可得到一个数值或一个地址

1. 算数操作符

算术操作符有 +,-,*,/MOD

符号地址 + -常数有意义,*、/、MOD时意义不明确

2. 逻辑与移位操作符

逻辑操作符有 AND,OR,XORNOT;移位操作符有SHLSHR
它们都是按位操作的,只能用于数字表达式中。
逻辑操作符要求汇编程序对其前后两个操作数(或表达式)作指定的逻辑操作。
移位操作符的格式是:

expression SHL(或SHR) numshift

汇编程序将 expression 左移或右移 numshift 位,如移位数大于 15,则结果为 0。

3. 关系操作符

它有 EQ(相等)、NE(不等)、LT(小于)、GT(大于)、LE(小于或等于)、GE(大于或等于)6 种。
关系操作符的两个操作数必须都是数字,或是同一段内的两个存储器地址。
计算的结果应为逻辑值,结果为真,表示为0FFFFH;结果为假,表示为0。

4. 数值回送操作符

它主要有 TYPE,LENGTH,SIZE,OFFSET,SEG 等。这些操作符把一些特征或存储器地址的一部分作为数值回送。

1. TYPE
TYPE  expression

a. 如该表达式是变量,则汇编程序将回送该变量的以字节数表示的类型:DB为1,DW为2,DD为4。
b. 如果表达式是标号,则汇编程序将回送代表该标号类型的数值: NEAR 为-1,FAR为-2。
c. 如果表达式为常数,则应回送0。

2. LENGTH
LENGTH  Variable

对于变量中使用 DUP的情况,汇编程序将回送分配给该变量的单元数,而对于其他情况则送1。
image.png

只针对通过 DUP 分配的单元

3. SIZE

SIZE Variable:
汇编程序应回送分配给该变量的字节数。此值是LENGTH值和TYPE值的乘积。

对于上面2例:

MOV CX,SIZE FEES	;	等于MOV CX,200
MOV CX,SIZE ARRAY	;	等于MOV CX,2
4. OFFSET
OFFSET  Variable 或 label

汇编程序将回送变量或标号的偏移地址值

5. SEG
SEG  Variable 或 label

汇编程序将回送变量或标号的段地址值

5. 属性操作符

主要有PTR段操作符、SHORT、THIS、HIGH、LOW、HIGHWORD和LOWWORD

1. PTR
type  PTR  expression

PTR 用来建立一个符号地址,但它本身并不分配存储器,只是用来给已分配的存储地址赋予另一种属性,使该地址具有另一种类型
格式中的类型字段表示所赋予的新的类型属性,而表达式字段则是被取代类型的符号地址。
image.png

2. 段操作符

段操作符:
用来表示一个标量、变量或地址表达式的段属性。可用段前缀指定某段的地址操作数:

MOV AX,ES:[BX+SI]

可见它是用段寄存器:地址表达式来表示的。此外,也可以用段名:地址表达式或段组名:地址表达式来表示其段属性。

3. SHORT

SHORT用来修饰 JMP指令中转向地址的属性,指出转向地址是在下一条指令地址的-128-+127个字节范围之内。

4. THIS
THIS attribute 或 type

它可以像PTR一样建立一个指定类型(BYTE、WORD、DWORD)的或指定距离(NEAR或FAR)的地址操作数。
该操作数的段地址和偏移地址与下一个存储单元地址相同
image.png

#####5. HIGH和LOW / HIGHWORD和LOWWORD
* HIGH和LOW:
称为字节分离操作符,它接收一个数或地址表达式,HIGH取其高位字节,LOW取其低位字节。

  • HIGHWORD和LOWWORD:
    称为字分离操作符,它接收一个数或地址表达式,HIGHWORD取其高位字,LOWWORD取其低位字。这两个操作符是MASM6版支持的。

操作符的优先级别

操作符的优先级别 从高到低排列如下:
(1) 在圆括号中的项,方括号中的项,结构变量(变量,字段),然后是LENGTH、SIZE等
(2) 名:(段取代)
(3) PTR,OFFSET,SEG,TYPE ,THIS 及段操作符
(4) HIGH 和 LOW
(5) 乘法和除法:*,/,MOD,SHL,SHR
(6) 加法和减法:+,-
(7) 关系操作:EQ,NE,LT,LE,GT,GE
(8) 逻辑:NOT
(9) 逻辑:AND
(10) 逻辑:OR,XOR
(11) SHORT

汇编源程序结构

段定义伪操作

SEGMENT/ENDS

格式为

segname SEGMENT [align-type] [combine-type] [use-type] ['class']
...
segname ENDS

定位类型 (align-type)
定位类型说明段起始地址边界值的情况 , 可以是:
PARA指定段的起始地址必须从小段边界开始,即段起始地址的最低的16进制数位必须为0。这样,偏移地址可以从0开始,为默认项。
BYTE该段可以从任何地址开始。这样,起始偏移地址可能不是0。
WORD该段必须从字的边界开始,即段起始地址必须为偶数。
DWORD该段必须从双字的边界开始,即段起始地址的最低十六进制数位必须为4的倍数。
PAGE该段必须从页的边界开始,即段起始地址的最低8位必须为0(该地址能被 256 整除)。

组合类型(combine-type)
组合类型说明程序连接时的段合并方法,可以是:
PRIVATE该段为私有段,在连接时不与其他模块中的同名段合并,默认值。
PUBLIC 该段连接时可以把不同模块中的同名段相连接而形成一个段,其连接次序由连接命令指定。每一分段都从小段的边界开始,因此各模块的原有段之间可能存在小于16个字节的间隙。
COMMON 该段在连接时可以把不同模块中的同名段重叠而形成一个段,由于各同名分段有相同的起始地址,所以会产生覆盖。COMMON 的连接长度是各分段中的最大长度。重叠部分的内容取决于排列在最后一段的内容。
AT expression 使段地址是表达式所计算出来的16位值。
MEMORY 与 PUBLIC 同义。
STACK 把不同模块中的同名段组合而形成一个堆栈段,长度为各原有段的总和,各原有段之间并无PUBLIC所连接段中的间隙,而且栈顶可自动指向连接后形成的大堆栈段的栈顶。

使用类型(use-type).
使用类型只适用于386及其后继机型,它用来说明使用16位寻址方式还是32位寻址方式
USE16 使用 16 位寻址方式。
USE32 使用 32 位寻址方式。
当使用16位寻址方式时,段长不超过64KB,地址的形式是16位段地址和16位偏移地址;当使用32位寻址方式时,段长可达4GB, 地址的形式是16位段地址和32位偏移地址。在实模式下, 使用USE16。使用类型的默认项是USE16

类别(‘class’)
类别在引号中给出连接时组成段组的类型名。
类别说明并不能把相同类别的段合并起来,但在连接后形成的装入模块中,可以把它们的位置靠在一起。

ASSUME

ASSUME伪操作来实现段和段寄存器的关系

例如:

data   segment         ;	定义数据段
       …
data   ends
extra  segment         ;	定义附加段
       …
extra  ends
code   segment         ;	定义代码段
       assume cs:code, ds:data, es:extra
start: mov   ax, data
       mov   ds, ax    ; 段地址 → 段寄存
	   mov   ax, extra
       mov   es, ax    ; 段地址 → 段寄存器
       …
code   ends
       end   start

存储模型与简化段定义伪操作

汇编程序(MASM5.0与6.0)除支持前面讨论的完整的段定义伪操作segment外,还提供了一种新的较简单的段定义方法。
这种方法不能像 segment伪操作那样具有较完整的表达能力,但它确实比较简单易用。

MODEL伪操作

MODEL 伪操作的格式如下

.MODEL memory_model [,model_options]

.MODEL 存储模型 [,语言类型] [,操作系统类型] [,堆栈选项]

memory_model用来表示存储模型,即用来说明在存储器中是如何安放各个段的,存储模型共有七种。
七种存储模型:
Tiny:所有数据和代码在一个段,都是近访问。
Small:所有数据放在一个 64KB数据段,所有代码放在另一个64KB的代码段,都是近访问。
Medium:代码使用多个段,而数据则合并成一个64KB段,数据是近访问,代码则可远访问。
Compact:所有代码都放在一个 64KB的代码段内,数据则可放在多个段内,代码是近访问,数据则可远访问。
Large:代码和数据都可多个段,都可远访问。
Huge:与Large相同,但允许数据段超过64KB。
Flat:允许使用32位偏移量,DOS下不允许使用,在OS2或其他保护模式操作系统下使用。

model options 允许用户指定的三种选项,即语言类型、操作系统类型、堆栈选项。
model options 允许用户指定的三种选项:
语言类型:是指该汇编语言程序作为某一种高级语言程序的过程而为该高级语言程序调用,如C,BASIC,FORTRAN,PASCAL。
操作系统类型:说明程序运行于哪种操作系统下(OSDOS或OSOS2),默认是OS_DOS。
堆栈选项:NEARSTACK或FARSTACK。
NEARSTACK时把堆栈段和数据段组合到一个DGROUP段,DS和SS均指向DGROUP段,FARSTACK是指堆栈段和数据段并不合并。
存储模型为TINY,SMALL,MEDIUM和FLAT时,默认为NEARSTACK;存储模型为COMPACT、LARGE和HUGE时,默认项FARSTACK

与简化段定义有关的预定义符号

汇编程序给出了与简化段定义有关的一组预定义符号,它们可在程序中出现,并由汇编程序识别使用。如在完整的段定义情况下,在程序的一开始,需要用段名装入数据段寄存器。

mov ax,data_seg1
mov ds,ax 

若用简化段定义,则数据段只用.data来定义,而并未给出段名,此时可用:

mov ax, @data
mov ds, ax 

这里预定义符号@data就给出了数据段的段名

程序开始和结束伪操作

END

表示源程序结束的伪操作END,其格式为

END [label]

其中label指示程序开始执行的起始地址。如果多个程序模块相连接,则只有主程序要使用标号,其他子程序模块则只用END而不必指定标号。

.STARTUP

.STARTUP 用来定义程序的初始入口点,并且产生设置 DS,SS 和SP 的代码。如果程序中使用了.STARTUP,则结束程序的 END 伪操作中不必再指定程序的入口点标号。

.EXIT

.EXIT 用来产生退出程序并返回操作系统的代码,格式为:
.EXIT [return-value]
其中return-value为返回给操作系统的值。常用0作为返回值。



处身寒夜,把握星光。