前言:
这篇笔记主要学习8086汇编中的伪指令。
编程语言:
汇编语言
以下为主题内容:
笔记参考 "杨季文《80x86汇编语言程序设计》"
语句种类和格式
汇编语言有两种类型,一种是指令语句,另一种是伪指令语句。
- 指令语句有着与其对应的机器指令。
- 伪指令语句没有对应的机器指令
在宏汇编语言中,还有一种特殊的语句,称为宏指令语句。
在汇编语言中,不仅有各种类型的运算符,还有许多操作符。通过运算符、操作符及括号把常数和符号连起来就得到表达式。
表达可分为数值表达式和地址表达式。
数值表达式
数值表达式指的是在汇编过程中能够由汇编程序计算出数值的表达式,所以组成数值表达式的各部分必须在汇编时就能完全确定。
常数
- 十进制常数

-
十六进制常数

-
二进制常数

- 八进制常数

- 字符串常数(串常数)

总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
;定义不同值的变量
data1 db 12H ;十六进制
data2 db 00101011B ;二进制
data3 db 12Q ;八进制
data4 db 'A' ;字符串
data ends
code segment
start:
mov ax,data
mov ds,ax
;传送常量
mov al,12H
mov al,00101011B
mov al,12Q
mov al,'A'
;传送变量
mov al,data1
mov al,data2
mov al,data3
mov al,data4
;
mov ax,4c00h
int 21h
code ends
end start

算数运算符

总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
;定义不同值的变量
data1 db 12H ;十六进制
data2 db 00101011B ;二进制
data3 db 12Q ;八进制
data4 db 'A' ;字符串
data ends
code segment
start:
mov ax,data
mov ds,ax
;传送常量
mov al,12H+2
mov al,00101011B-0BH
mov al,12Q/2
mov al,('A'-21H)*2
;传送变量
mov al,data1+2
mov al,data2-0BH
;错误变量在编译后表示内存地址,而在
;8086汇编中修改内存地址只能使用加法和减法。
;mov al,data3/2
;mov al,(data4-21H)*2
mov ax,4c00h
int 21h
code ends
end start

关系运算符

总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
;关系比较符
data1 db 12H GT 1024H ;十六进制
data2 db 00101011B LT 30H ;二进制
data3 db 12Q NE 0 ;八进制
data4 db 'A' EQ 40H ;字符串
data ends
code segment
start:
mov ax,data
mov ds,ax
;传送变量
mov al,data1
mov al,data2
mov al,data3
mov al,data4
;
mov ax,4c00h
int 21h
code ends
end start

关系不成立结果为0,关系成立结果为0FFFFH(-1)。不要理所当然的认为关系不成立结果为-1。
逻辑运算符
逻辑运算符包括按位操作的 "与"(AND)、"或"(OR)、"异或"(XOR)、"非"(NOT)、左移(SHL)、右移(SHR)。
例如:
总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
code segment
start:
mov ax,1 shl 3
add cx,1024 shr 4
or al,3 and 47h
and bl,not(7 or 54h)
;
mov ax,4c00h
int 21h
code ends
end start

逻辑运算符是伪指令,它是由汇编程序在编译时完成。
在数值表达式中使用操作符
汇编语言中有如下操作符可用在数值表达式中:HIGH、LOW、LENGTH、SIZE、OFFSET、SEG、TYPE、WIDTH 和 MASK 等。
- HIGH

- LOW

总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code
code segment
start:
mov ax,HIGH (1234H + 5)
mov ax,HIGH 1234H + 5
mov ax,LOW 1234H - 3
;
mov ax,4c00h
int 21h
code ends
end start

运算符和操作符的优先级


地址表达式

变量和标号
变量和标号分别代表存储单元。变量表示的存储单元中存放的数值;标号表示的存储单元中存放的指令代码。
数据定义语句
一般格式如下:
[变量名] 数据定义符 表达式 [,表达式,...,表达式] ;注释
例如:
VARB DB 3
VARW DW -12345
DB 1
变量名是可选的,如果使用变量名,那么它就直接代表该语句所定义若干数据项中的第一项数据。各表达式间用逗号隔开。
例如:
BUFF DB 100, 3+4, 5*6
变量名是一段数据的起始地址。数据段的第一个数据是ds:0
定义字节变量
定义一个字节大小的变量。定义字节变量的数据定义符是 db
count db 100
db 0DH,0AH,'$'
table db 0,1,4,9,16
定义字变量
每个变量可存储两个字节。定义字变量的数据定义符是 dw
例如:
flag dw 2FCDH,1024,-1
VECT dw 0
dw 2047
定义双字变量
每一双字变量占四字节存储单元。定义双字变量数据定义符 dd
例如:
vector dd 4
far ptr dd 12345678H,0
定义未初始化变量
变量定义时使用问号(?),表示这是一个未初始化的变量。
例如:
inbufff db 5,?,?,8,?
varw dw ?
oldv dd ?
定义字符串
定义字节数据伪指令 db 也可方便用于定义字符串。字符串要用引号括起来,单引号和双引号都可以。
例如:
MESS1 db 'hello!'
相同于
MESS1 db 'H','E','L','L','O','!'
用引号把字符串括起来,要比把每一个字符用引号括起来方便
MESS2 db "How are you ?",0DH,0AH,24H
在定义变量数据时,需要注意 "字" 和 "双字"大小的变量,在内存中是小端存储(即 "高高低低" 原则)
varb db 12h
varw dw 1234h
vard dd 12345678h
定义其他类型的变量
定义 8 字节数据变量,使用定义符是 DQ,定义 10 字节数据变量,定义符是 DT 。
例如:
DT 0
DQ ?
重复操作符 DUP
有时需要定义数组,有时还需要定义数据缓冲区。为此,汇编语言提供了重复操作符 DUP 。
例如:
buffer db 8 dup(0) ;定义由8个字节组成的缓冲区,每个字节的初始值为0。
总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
VARB DB 3
VARW DW -12345
DB 1
BUFF DB 100, 3+4, 5*6
MESS1 db 'hello!'
MESS2 dd 12345678h
MESS3 DT 1234H
MESS4 DQ 1234H
MESS5 db 8 dup(12H)
data ends
code segment
start:
mov ax,data
mov ds,ax
xor ax,ax
xor bx,bx
;变量名的本质
mov al,VARB
mov bl,ds:0
mov ax,VARW
mov bx,ds:1
;
mov ax,4c00h
int 21h
code ends
end start



变量和标号
变量表示内存单元,这种内存单元中存放的数值。标号也表示内存单元,这种内存单元中存放的是机器指令。
变量和标号的属性
变量和标号具有如下三种属性:
- 段值:变量或标号对应的内存单元所在段的段值。
- 偏移,变量或标号对一个存储单元的起始地址的段内偏移。
- 类型,变量的类型主要是 BYTE(字节)、WORD(字)和 DWORD(双字);
标号类型主要是 NEAR(近)、far(远)。近:表示段内标号,远,表示段间标号。
析值操作符

- 操作符 SEG 返回变量或标号所在段的段值
例如:
mov ax,SEG VARW
mov ds,ax
- 操作符 OFFSET 返回变量或标号的偏移
例如:
mov bx,OFFSET VARW
add di,OFFSTE VARW+2
mov si,OFFSET VARB
lea 是汇编指令,由对应的硬编码。OFFSET 是伪指令,由编译器解析。
- 操作符 TYPE 返回变量或标号的类型,类型用数值表示。
常见的类型和对应的数值如下:
BYTE 1
WORD 2
DWORD 4
NEAR -1
FAR -2
变量的类型值是对应类型的变量所占用的字节数,而标号的类型值却没有实际的物理意义。
- 操作符 LENGTH 返回利用 DUP 定义的数组中元素的个数。如果变量定义语句中没有 DUP,则返回 1。
如果嵌套使用 DUP 则返回最外层的重复数。
例如:
mov cx,LENGTH VARW
mov cx,LENGTH BUFF
mov cx,LENGTH MESS
- 操作符 SIZE 返回用 DUP 定义的数组占用的字节数,可按下式计算:
SIZE 变量 = (LENGTH 变量) * (TYPE 变量)
例如:
mov cx,SIZE VARW
mov cx,SIZE BUFF
mov cx,SIZE MESS
属性操作符
- 操作符 PTR
mov [si],1
是错误的指令,因为指针寄存器所指的存储器操作数的类型,即要访问的存储器操作数是字节类型还是字类型。
程序员要在源程序中明确指明,要访问的存储器操作数是字节类型还是字类型。这可利用 PTR 操作符来指明。
mov word ptr [si],1
mov byte ptr [si],1
ptr 是最常用的合成操作符,用在地址表达式前,用于指定或临时改变变量和标号的类型。
一般格如下:
类型 ptr 地址表达式
其中,类型可以是 BYTE、WORD、DWORD、NEAR 和 FAR。无论地址表达式的类型是什么,当前均以 ptr 前面的类型为准。
- 操作符 THIS
操作符 THIS 的一般格式如下:
THIS 类型
其中类型可以是 BYTE、WORD、DWORD、NEAR 和 FAR 等。它会返回一个具有指定类型的存储器操作数。
操作符 THIS 一般使用在符号定义语句中,从而定义一个具有类型、段值和偏移三属性的表示存储器操作数的符号。
例如:
MY_BYTE EQU THIS BYTE
MY_WORD DW ?
总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
VARB DB 3
VARW DW -12345
DB 1
BUFF DB 100, 3+4, 5*6
MESS1 db 'hello!'
MESS2 dd 12345678h
MESS3 DT 1234H
MESS4 DQ 1234H
MESS5 db 8 dup(12H)
MY_BYTE EQU THIS BYTE
MY_BYTE1 db ?
data ends
code segment
start:
;SEG 获取变量所在的段值
mov ax, SEG VARB
mov ds,ax
;
mov bx,offset VARW
add di,offset VARW+2
;
mov ax,TYPE VARW
mov ax,LENGTH MESS4
mov ax,LENGTH MESS5
mov bx,size VARB
mov bx,size MESS5
;
mov al,byte ptr VARW
mov bx,word ptr BUFF
;mov ax,MY_BYTE ;报错数据类型不匹配
mov al,MY_BYTE
;
mov ax,4c00h
int 21h
code ends
end start




常用伪指令语句和源程序组织
符号定义语句
通过符号定义语句,可把常数、表达式等用符号来表示。恰当地使用符号定义语句,不仅可大大方便程序的书写和阅读,对程序的调试和修改很有利。
等价语句 EQU
等价语句的一般格式如下:
符号名 EQU 表达式
- 用符号来代表常数或数值表达式。
COUNT EQU 100
BUFF_LEN EQU 4*COUNT
LTX EQU 1
RDX EQU LTX+50
INBUFFER DB COUNT,?,COUNT DUP(?)
- 用符号表示一个字符串。可用一个简短的符号表示一个复杂的字符串,以后当汇编程序遇到所定义的符号时,就用字符串代替。
例如:
HELLO EQU "How are you !"
- 重新定义关键字或指令助记符。即给汇编怨言的关键字或指令助记符起一个别名。
例如:
MOVE EQU MOV
COUNT EQU CX
安排上述语句后,可用 MOVE 代替指令助记符 MOV,用 COUNT 代表寄存器 CX 。
例如:
MOVE AX,CX
MOV COUNT,100
- 伪代码THIS,为伪代码紧接着的变量声明一个别名,并自定义分配类型。
例如:
VARW EQU THIS WORD
VARB DB 2 DUP(0)
FLAG DW ?
FALG1 EQU BYTE PTR FLAG
FLAG2 EQU BYTE PTR FLAG+1
在这之后可使用这些符号
例如:
MOV AX,VARW
MOV AL,FLAG1
MOV FLAG2,AL
可自定义类型BYTE、WORD、DWORD、NEAR和FAR。伪代码THIS仅有改变数据类型的作用,并不会为这个别名分配存储单元。
总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
COUNT EQU 100
BUFF_LEN EQU 4*COUNT
LTX EQU 1
RDX EQU LTX+50
INBUFFER DB COUNT,?,COUNT DUP(?)
;
HELLO EQU "How are you !"
;
MOVE EQU MOV
COUNTS EQU CX
;
VARW EQU THIS WORD
VARB DB 2 DUP(0)
FLAG DW ?
FLAG1 EQU BYTE PTR FLAG
FLAG2 EQU BYTE PTR FLAG+1
data ends
code segment
start:
mov ax,data
mov ds,ax
;
move ax,COUNT
move ax,BUFF_LEN
move COUNTS,RDX
MOV AX,VARW
MOV AL,FLAG1
MOV FLAG2,AL
;
mov ax,4c00h
int 21h
code ends
end start

等号语句(=)
汇编语言还专门提供了等号语句来定义符号常量,即用符号表示一个常量。
等号语句一般格式如下:
符号名 = 数值表达式
例如:
XX = 10
YY = 20+300/4
数值表达式中不能含有向前引用的符号名称。用等号语句定义的符号可被重定义。
例如:
ABCD = 1
ABCD = 100
ABCD = 2 * ABCD +1
定义符号名
定义符号名的一般格式:
符号名 LABEL 类型
其中类型可以是 BYTE、WORD、DWORD、NEAR 和 FAR 等。
该语句的功能是为LABLE伪指令,下面紧接着的变量声明别名,属性都一样除了类型可以自定义。
例如:
BUFFER LABEL WORD
BUFF DB 100 dup(0)
LABLE伪指令声明的标号,不会为其分配存储空间
再如:
QUIT LABEL FAR
EXIT: MOV AH,4CH
这样指令 "MOV AH,4CH" 就有了两个标号QUIT和EXIT,但它们的类型不同。
总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
XX = 10
YY = 20+300/4
ABCD = 1
ABCD = 100
ABCD = 2 * ABCD + 1
BUFFER LABEL WORD
BUFF DB 10 dup(0CCH)
data ends
code segment
start:
mov ax,data
mov ds,ax
;相当于这条汇编指令,定义了两个不同属性的标号。
QUIT LABEL FAR
EXIT: MOV AH,4CH
;
mov ax,offset BUFF
mov ch,BUFF
mov bx,offset BUFFER
mov dx,BUFFER
;
call QUIT
call far ptr EXIT
int 21h
mov ax,4c00h
int 21h
code ends
end start

LABEL 不仅可以用在数据段的变量,还可用在代码段的标号。
段定义语句
为了与存储器的分段结构相对应,汇编与越南的源程序也由若干个段组成。段定义语句就是用来按段组织程序和利用存储器的。
段的开始和结束
段定义的一般格式:
段名 segment [定位类型] [组合类型] ['类别']
........
段名 ENDS
段使用设定语句
汇编程序根据段开始语句和段结束语句判断出源程序的划分,为了有效地产生目标代码,汇编程序还要来哦姐各程序段与段寄存器地对应关系。段寄存器与程序段的对应关系由段使用设定语句说明。
段使用设定语句格式如下:
assume 段寄存器名:段名[,段寄存器名:段名,....]
段寄存器名可以是 CS、DS、SS 和 ES。段名可任意定义,一般使用固定且有意义的段名,如code、data、stack等等。
assume 伪指令中的段名也可以是特别关键字nothing,它表示某个段寄存器不再与任何段有对应关系。
总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
data1 segment
XXX dw 1
YYY dw 2
data1 ends
data2 segment
BBB dw 3
CCC dw 4
data2 ends
code segment
assume cs:code,ds:data1,es:data2
start:
mov ax,data1
mov ds,ax
mov ax,data2
mov es,ax
;
mov ax,XXX
assume ds:data2,es:nothing
mov ax,data2
mov ds,ax
mov ax,XXX
mov ax,4c00h
int 21h
code ends
end start


ORG语句
汇编程序在对源程序汇编的过程中,使用地址计数器来把偶你当前正在汇编的指令或者变量的偏移地址。通常情况下偏移地址是逐步增减的,但是程序员可以只用ORG 语句调整当前的偏移地址。
ORG 语句格式如下:
ORG 数值表达式
总结
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code
code segment
ORG 100H
start:
mov ax,1234h
mov ax,4c00h
int 21h
code ends
end start

$ 符号
;程序名:masm16.asm
;功能:验证 masm16 的汇编指令
;========================================
assume cs:code,ds:data
data segment
string db 'hello!'
len = $-string
data ends
code segment
ORG 100H
start:
mov ax,1234h
jmp $+9
org $+4
mov ax,2
mov ax,len
mov ax,4
mov ax,4c00h
int 21h
code ends
end start

$ 表示当前数据所在的偏移地址,可以用在代码段和数据段。
第一个完整的源程序
;程序名:T3-1.ASM
;功能:显示信息"HELLO"
;========================
assume cs:code,ds:data,ss:sseg
sseg segment
dw 256 dup(?)
sseg ends
;---------------------------------
val1 equ 1
val2 equ 2
val3 equ 3
data segment
mess db 'HELLO',0dh,0ah,'$'
BUFF db 1,2,3,10 dup(0)
data ends
code segment
start:
;mov ax,data
mov ax,seg mess
mov ds,ax;
mov al,LENGTH BUFF
mov al,val1*val2/val3
mov ax,1234h GT 1024h
mov ax,high(1234h+5)
mov ax,high 1234h+5
mov ax,low 1234h-3
;设数据段寄存器
L1:
mov dx,offset mess
mov ah,9 ;显示信息 "HELLO"
int 21h
mov ax,4c00h
int 21h
code ends