📄 汇006.txt
字号:
和高级语言的REPEAT型的循环一样,.REPEAT-.UNTIL[CXZ]的循环体也会至少被执行一次。
.WHILE-.ENDW和.REPEAT-.UNTIL[CXZ]的循环体内还可再含有循环伪指令,这样就构成了循环结构的嵌套。
汇编程序在生产指令代码时会进行代码优化,以便尽可能得到最优化的指令序列。
3、辅助循环伪指令
(1)、终止循环伪指令
.BREAK
.BREAK .IF condition
该伪指令用来终止包含它的最内层循环。前者是无条件终止循环,后者是仅当逻辑表达式condition为真时,才终止循环。
.WHILE 1 .REPEAT
…
.BREAK .IF condition
…
…
.BREAK .IF condition
…
ENDW .UNTIL 0
对于以上二个循环,如果没有指令来终止循环的话,它们都将进入死循环状态,但如果在该层循环体内,存在伪指令“.BREAK .IF condition”的话,那么,当逻辑表达式condition为真时,该循环就会被终止了。
(2)、循环继续伪指令
.CONTINUE
.CONTINUE .IF condition
该伪指令用于直接跳转到包含它的最内层循环的计算循环条件表达式的代码处。前者是无条件转移到计算循环条件表达式的代码处,后者是仅当条件表达式condition为真时,才进行这样的跳转。
辅助循环伪指令.BREAK和.CONTINUE只能在伪指令.WHILE-.ENDW和.REPEAT-.UNTIL的循环体内使用。
例6.12 显示9个数字字母'1'~'9',26个大写字母,和显示任意输入的数字字符,并用按“回车”键来结束本程序的运行。
解:
DATA1 SEGMENT
MSG1 DB 13, 10, "Iteration: "
NUM1 DB '1', "$"
MSG2 DB 13, 10, "Alphabet: $"
NUM2 DB 'A', " $"
MSG3 DB 13, 10, "Type digits, then press ENTER: $"
DATA1 ENDS
CODE1 SEGMENT
ASSUME CS:CODE1, DS:DATA1
START: MOV AX, DATA1
MOV DS, AX
MOV CX, 9
MOV AH, 09H
MOV DX, OFFSET MSG1
.REPEAT
INT 21H
INC NUM1 ;显示Iteration: 1,2,~,9
.UNTILCXZ
MOV DX, OFFSET MSG2
INT 21H ;显示字符串"Alphabet:"
MOV AH, 09H
MOV DX, OFFSET NUM2
.REPEAT
INT 21H
INC NUM2 ;显示当前字母
;当前字母向后移
.UNTIL NUM2 > 'Z' ;显示整个大写字母表
MOV AH, 09H
MOV DX, OFFSET MSG3
INT 21H
.WHILE 1 ;循环条件为永真的循环
MOV AH, 07H
INT 21H ;不带回显地从键盘读一个字符
.BREAK .IF AL == 13 ;如果输入“回车”键,则终止循环
.CONTINUE .IF (AL<'0') || (AL>'9') ;如果字符不是数字字符,则继续循环
MOV DL, AL
MOV AH, 02H
INT 21H ;显示所输入的数字字母
.ENDW
MOV AX, 4C00H
INT 21H
CODE1 ENDS
END START
6.3 段的基本属性
在通常情况下,一个复杂的应用程序会由若干个模块组成,一个模块又会含有多个段。而不同模块的段之间、同一模块的段之间往往存在某种联系,这种联系就要体现在段属性的说明上。
段定义的一般格式如下:
段名 SEGMENT [对齐类型] [组合类型] [类别]
…
段名 ENDS
段属性“对齐类型”、“组合类型”和“类别”要按此顺序说明,但这些可选项可根据需要选择书写。如果源程序中不指定某个属性,那么,汇编程序将使用该属性的缺省值。
程序中的段名可以是唯一的,也可以与其它段同名。在同一模块中,如果有二个段同名,则后者被认为是前段的后续,这样,它们就属同一段。
当同一模块出现二个同名段时,则后者的可选项属性要么与前者相同,要么不写其属性而选用前者的段属性。
例6.13 同段名的作用
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
在上面的例子中,第二个数据段是第一个数据段的后续,汇编程序把它们是合二为一,上述的代码段也如此。
下面,详细说明段属性的含义及其作用。
6.3.1 对齐类型(ALIGN)
对齐类型表示当前段对起始地址的要求,连接程序(LINK.EXE)按表6.1的地址格式来定位段的起始地址。在进行段定位时,会根据其定位类型进行定位的,所以,各段之间就有可能出现一些空闲字节,即可能浪费几个字节单元。
段对齐类型PARA是一个适用于所有段类型的对齐类型,它也是缺省的对齐类型。对齐类型BYTE和WORD通常用于数据段的定位,对齐类型DWORD通常用于80386及其以后CPU代码段的定位。
表6.1 段对齐类型与段起始地址之间的对应关系
对齐类型 起始地址(二进制) 功能说明 最多的空闲字节数
BYTE xxxx xxxx xxxx xxxx xxxx 下一个字节地址
0
WORD xxxx xxxx xxxx xxxx xxx0 下一个字地址
1
DWORD xxxx xxxx xxxx xxxx xx00 下一个双字地址
3
PARA xxxx xxxx xxxx xxxx 0000 下一个节地址
15
PAGE xxxx xxxx xxxx 0000 0000 下一个页地址
127
6.3.2 组合类型(COMBINE)
组合类型是告诉连接程序如何把不同模块中段名相同的段合并在一起。具体的组合类型如下:
NONE 表示当前段在逻辑上独立于其它模块,并有其自己的基地址。NONE是缺省的组合类型。
PUBLIC 表示当前段与其它模块中同段名的PUBLIC类型段组合成一个段。组合的先后次序取决于LINK程序中目标模块排列的次序。在组合时,后续段的起始地址要按其对齐类型进行定位,所以,同名段之间可能有间隔。
COMMON 表示当前段与其它模块中同名段重叠,也就是说,它们的起始地址相同。最终段的长度是同名段的最大长度。由于段覆盖,所以,前一同名段中的初始化数据被后续段的初始数据覆盖掉。
STACK 组合类型STACK表示当前段是堆栈栈,其组合情况与PUBLIC相同。
AT 数值表达式 该数值表达式是当前段所指定的绝对起始地址的段地址。
6.3.3 类别(CLASS)
类别是一个由程序员指定的用单引号括起来的字符串。如果一个段没有给出类别,那么,这个段的类别就为空。类别是用于段的分类,连接程序利用该类别来调整同名、同类别的段,并使它们相邻。典型的类别是"Data"和"Code"。如果指定某段的类别是"Code",那么,该段最好是代码段,这样,有的调试程序(如:CodeView)就可以顺序工作。
例如:
DATA1 SEGMENT WORD PUBLIC "Data"
…
DATA1 ENDS
上述段定义说明了该段的起始地址是下一个字地址、组合类型为PUBLIC、段类别是"Data"。
6.3.4 段组(GROUP)
段组伪指令GROUP是用于把源程序模块中若干个段结合成一个组,并对该段组定义一个段组名。段组伪指令的格式如下:
段组名 GROUP 段名[, 段名, ……]
其中:段名之间要用逗号间隔,段名也可以用表达式“SEG 变量”或“SEG 标号”。
下面举例说明段组伪指令的使用方法和作用。
例6.12 段组的作用
方法1:用一个段寄存器对应二个数据段
DATA1 SEGMENT ;第一个数据段
b1 DB 10h
DATA1 ENDS
DATA2 SEGMENT ;第二个数据段
b2 DB 23h
DATA2 ENDS
CODE1 SEGMENT
ASSUME CS:CODE1, DS:DATA1 ;(1)
START: MOV AX, DATA1
MOV DS, AX ;(2)把数据段DATA1的段值赋给段寄存器DS
…
MOV BL, b1 ;(3)引用DS来访问DATA1中的变量b1
…
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伪指令,并保证段寄存器的值与段组或段相一致。
6.4 简化的段定义
前面,我们介绍了完整的段定义格式,用完整的段定义格式虽然可以控制段的各种属性,但程序员很少会这样做。现在的汇编程序提供了一种简化的段定义方式,它使定义段更简单、方便。
6.4.1 存储模型说明伪指令
在使用简化的段定义方式之前,必须使用存储模式说明伪指令来描述源程序所采用的存储模式。该伪指令说程序所使用的存储模式,汇编程序将用该存储模式生成相应的ASSUME和GROUP语句,同时也为其它的简化段创建等价的预定义。
程序存储模式说明伪指令的格式如下:
.MODEL 存储模式[,语言类型] [,操作系统类型] [,堆栈类型]
程序可选的存储模式有:TINY、SMALL、COMPACT、MEDIUM、LARGE、HUGE和FLAT。
伪指令.MODEL必须写在源程序的首部,且只能出现一次,其前内容只能是注释。
如果用伪指令来指定程序所遵循的语言类型,那么,将不允许子程序的嵌套定义。与子程序定义有关的内容请见第7.5节。
一、存储模式
如果要用汇编语言编写被高级语言调用的子程序,那么,该汇编程序的存储模式必须与该高级语言编译(或解释)程序所使用的存储模式相匹配。汇编语言程序所能使用的存储模式、符号及其相关信息如表6.2所列。
在程序中,还可伪指令OPTION SEGMENT和SEGMENT来指定段的规模。
有关存储模式的具体规定如下:
、TINY
在汇编程序MASM 6.11和TASM 4.0,该存储类型是为编写COM文件类型而设置的。程序员还可用汇编命令行选项/AT和连接命令选项/TINY来达到此目的。
表6.2 存储模式的符号及其相关含义
代码的位距 数据的位距 段的宽度 数据段和代码段能否合并
Code Distance Data Distance Segment Width Data & Code Combined?
Tiny
Small
Compact
Medium
Large
Huge
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -