汇编语言程序开发

汇编语言程序的语句格式

硬指令&执行性语句

硬指令:使CPU产生动作、并在程序执行时才处理的语句

执行性语句——表达处理器指令,由硬指令构成的语句,它通常对应一条机器指令,出现在程序的代码段中,格式:

1
2
标号: 硬指令助记符 操作数,操作数,... ;注释
again: mov dx, offset string ;获得string的偏移地址并保存到dx中

标号:反映硬指令位置(逻辑地址)的标识符,后跟一个冒号分隔

硬指令助记符:可以是任何一条处理器指令

处理器指令的操作数可以是立即数、寄存器和存储单元

伪指令&说明性语句

伪指令——不产生CPU动作、在程序执行前由汇编程序处理的说明性语句。伪指令与具体的处理器类型无关,但与汇编程序的版本有关

说明性语句——由伪指令构成的语句,它通常指示汇编程序如何汇编源程序:

1
2
名字  伪指令助记符  参数,参数,...	;注释
string db ‘Hello,world’ ;使用db伪指令定义一个字符串,使用变量名string表达其在主存中的逻辑地址(包含段基地址和逻辑地址)、

名字:反映伪指令位置(逻辑地址)和属性的标识符,后跟空格或制表符分隔,没有冒号

伪指令助记符:定义字节数据和字符串的DB就是伪指令

伪指令的参数可以是常数、变量名、表达式等,可以有多个,参数之间用逗号分隔

标识符

标号和名字是符合汇编程序语法的用户自定义的标识符

标识符一般最多由31个字母、数字及规定的特殊符号(如 _、$、?、@)组成,不能以数字开头。默认情况下,汇编程序不区别标识符中的字母大小写。一个程序中,每个标识符的定义是唯一的,还不能是汇编语言采用的保留字。

保留字是汇编程序已经利用的标识符,主要有:

  • 硬指令助记符——例如:MOV、ADD
  • 伪指令助记符——例如:DB、EQU
  • 操作符——例如:OFFSET、PTR
  • 寄存器名——例如:AX、CS

汇编语言源程序框架

简化段定义的源程序框架

1
2
3
4
5
6
7
8
9
10
.model small	;存储模型伪指令.model,small表示小型模式
.stack ;堆栈段
.data ;数据段
... ;数据定义
.code ;代码段
.startup ;指明程序的起始执行点,同时为程序的数据段、代码段、堆栈段设置相应的段寄存器值
... ;主程序代码
.exit 0 ;返回DOS操作系统
... ;子程序代码
end ;汇编结束

MASM 5.0/5.1不支持.startup.exit,可修改如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.model small				;存储模型伪指令.model,small表示小型模式
.stack ;堆栈段
.data ;数据段
... ;数据定义
.code ;代码段
;;.startup ;指明程序的起始执行点,同时为程序的数据段、代码段、堆栈段设置相应的段寄存器值
start: mov ax, @data
mov ds, ax
... ;主程序代码
;;.exit 0 ;返回DOS操作系统
mov ax, 4c00h
int 21h
... ;子程序代码
;;end ;汇编结束
end start

完整段定义的源程序框架

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
stack	segment stack
...
stack ends
data segment
...
data ends
code segment 'code'
assume cs:code, ds:data, ss:stack ;assume将cs,ds,ss,依次指向名为code,data,stack逻辑段
start: mov ax, data
mov ds, ax ;因为assume不为ds赋值,所以在start中为ds赋值data逻辑段
...
mov ax, 4c00h
int 21h
...
code ends
end start

汇编语言程序的开发过程

DOS系统功能调用

DOS系统主要分配21H号中断,用于程序员调用DOS操作系统工功能

  • 中断调用指令:INT 21H

在AH寄存器中设置系统功能调用号

字符输入(01H)

从键盘读入一个字符,并使用AL保存出口参数

1
2
MOV AH,01H
INT 21H ;读入的字符在AL寄存器

字符输出(02H)

在显示器当前光标位置显示给定字符,且光标右移一个字符位置,使用DL作为入口参数

1
2
3
4
5
MOV AH,02H
MOV DL,字符 ;ASCII码,例如:41H
;MOV DL,41H ;输出‘A’,方法一
;MOV DL,'A' ;输出‘A’,方法二
INT 21H

字符串输入(0AH)

需要事先在主存设置用于保存输入字符串的缓冲区,使用DS:DX作为出口参数

  • 缓冲区的第1字节填入最多接收的字符个数(包括回车符
  • 缓冲区的第2个字节存放实际输入的字符个数(不包括回车符
  • 从第3个字节开始存放输入的字符串,实际输入字符数多余定义数时,多出的字符被丢弃
1
2
3
4
5
6
7
8
9
buffer 	db	81					;可能输入的最大字符数
db 0 ;存放实际输入的字符数
db 81 dup (0) ;存放输入的字符串
...
mov ah, 0AH ;设置功能号
mov dx, seg buffer ;提供入口参数,seg获得buffer的段地址
mov ds, dx ;设置数据段
mov dx, offset buffer
int 21h

字符串输出(09H)

需要事先将欲显示的字符串保存在主存中

  • 设置入口参数DS:DX等于该字符串在主存中的首地址
  • 字符串必须以$(24H)结束
1
2
3
4
string	db	'hello,everybody','$'
mov ah, 09h
mov dx, offset string
int 21h

按键判断(0BH)

仅判断当前是否有按下的键,设置输出参数AL后退出。

  • AL = 0没有按键
  • AL = FFH已经按键

参数&变量&标号

数值型参数

常数

十进制常数 -- 以D/d结尾,默认情况后缀可省略

十六进制常数 -- 以H/h结尾

八进制 -- 以Q/q结尾

二进制 -- 以B/b结尾

字符串常数 -- 用英文缩略号括起来的单个字符或多个字符

符号常数 -- 利用一个标识符表达一个数值。符号定义伪指令有EQU=

1
2
3
符号名 equ 数值表达式
符号名 equ <字符串> ;该字符串可以是一条处理器指令
符号名 = 数值表达式

EQU用于数值等价时不能重复定义符号名,但是"="允许重复赋值

1
2
3
eg:
x = 7 ;x equ 7同样正确
x = x + 5 ;但是x equ x + 5错误

数值表达式

算术运算符:+,-,*,/,mod

逻辑运算符:and,or,xor(异或),not

移位运算符:shl,shr

关系运算符:eq,ne,gt(大于),lt(小于),ge(大于等于),le(小于等于)

  • 用FFFFH(补码-1)表示真
  • 用0000H表示假
1
2
3
4
eg:
mov bx, ((port lt 5) and 20) or ((port ge 5) and 30)
;当port < 5时,mov bx, 20
;当port >= 5时,mov bx, 30

高低分离符:high(高字节), low(低字节), highword(高字),lowword(低字)

1
2
mov ah, high 8765h	
;等价于mov ah, 87h

变量定义伪指令

格式

1
变量名 伪指令 初值表
  1. 变量名:用户自定义的标识符,表示初值表首元素的逻辑地址。变量名可以没有,在此情况下直接为初值表分配空间,无符号地址

  2. 初值表:用","分隔的参数,主要由数值常数、表达式、"?","DUP"组成

    • "?"表示初值不确定
    • 重复初值可以用DUP进行定义
1
重复次数 dup(重复参数)
  1. 变量定义伪指令有DB、DW、DD、DF、DQ、DT

定义字节单元伪指令DB

用于分配一字节或多字节单元,初值表中的每个数据一定是字节量(Byte),可以是\(0\sim 255\)的无符号数,或\(-128\sim +127\)带符号数

1
2
3
4
.data
x db 'A', -5
db 2dup(100), ?
y db 'ABC'
存储单元 偏移地址
43h(C) 0007H
42h(B) 0006H
41h(A) 0005H
——(?) 0004H
64h(100) 0003H
64h(100) 0002H
fbh(-5) 0001H
61h(a) 0000H

定义字单元伪指令DW

用于分配一个或多个单元,初值表中的每个数据一定是字量(Word),一个字单元可用于存放任何16位数据,如:一个段地址、一个偏移地址、两个字符、\(0\sim 65535\)之间的无符号数、\(-32768\sim +32767\)之间的带符号数

1
2
3
4
5
6
7
8
9
10
11
wnum	equ 5678h	;定义wnum为常量
count dw 20h ;定义count为变量
;假设count在的数据段的偏移地址是10h

mov ax, [bx + si + wnum] ;mov ax, [bx + si + 5678h]
mov ax, count ;mov ax, [0010h]
mov ax, [si + count] ;mov ax, [si + 10h]
;mov ax, count[si]
lea bx, count ;lea bx, [0010h]
;mov bx, 0010h
mov bx, offset count ;mov bx, 0010h
  • 变量实质表达的是主存地址

  • 仅使用变量名或者加个常量是直接寻址

  • 再加一个寄存器是寄存器相对寻址

  • 加两个寄存器是基址变址相对寻址

定义双字单元伪指令DD

用于分配一个或多个双字单元,初值表中的每个数据一定是一个32位的双字量

可以用来表达16位段地址和16位偏移地址的远指针

其他数据定义伪指令DF&DQ&DT

定义3字伪指令DF:为一个或多个6字节变量分配空间及初始化

定义4字伪指令DQ:为一个或多个8字节变量分配空间及初始化

定义10字节伪指令DT:为一个或多个10字节变量分配空间及初始化

练习:定义一个缓冲区,包含33H、34H、35H、36H四个字节字符,把这四个数据复制20遍,存入接着的缓冲区,最后显示出复制结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
source	db	33j, 34h, 35h, 36h
target db 80dp(?) ;紧接着分配80个空间

;分别指向源和目的的偏移地址首部
mov si, offset source
mov di, offset target

mov cx 80
again1: mov al. [si]
mov [di], al
inc si
int di
loop again1
mov di, 0
again2: mov dl, target[di]
mov ah, 2
int 21h
inc di
cmp di, 80
jb again2

定位伪指针ORG&EVEN&ALIGN

1
2
3
4
5
6
7

$表示当前的偏移地址

```assembly
;在偏移地址100H单元开始定义
dw 1, 2, $ + 4, $ + 4
;在104H单元的值为108H,106H单元的值为10AH
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

```ALIGN n```:使它后面的数据或指令从n的整数倍开始,如果不是就加上$1\sim n-1$中的一个数

n是2的乘方,且小于所在段的定位属性值

## 变量和标号的属性

### 地址操作符

```[]```表示将括起来的表达式作为存储器地址指针

```$```表示当前偏移地址

```:```表示采用指定的段地址寄存器

```offset```返回偏移地址

```seg```返回段地址

### 类型操作符

1. ```ptr```

```类型名 ptr 名字/标号```

2. ```this```

创建时采用当前地址,但为指定类型的操作数

```assembly
;b_var 和 w_var 地址相同
b_var equ this byte ;按字节访问b_var
w_var dw 10 dup(0)`;按字访问w_var
  1. type 名字/标号

    返回一个字量数值,表明名字或标号的类型;对字节、字、双字变量依次返回1,2,4