概念

  • 汇编语言:用指令助记符、符号地址、标号、伪指令等符号编写程序的语言成为汇编语言。用这种语言书写的程序叫做汇编语言源程序或者称为源程序。

  • 汇编:把汇编语言源程序翻译成在机器上能执行的机器语言程序的过程叫做汇编,完成汇编过程的程序叫做汇编程序。

  • 交叉汇编:一个机器汇编,另一个机器执行。

  • 驻留汇编:一个机器自身汇编,并且汇编结果在自身机器上运行。

  • 汇编语句的格式

    [标号] 指令助记符 [操作数] [; 注解]

    其中标号例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 1 assume cs:code
2 code segment
3 a: db 1, 2, 3, 4, 5, 6, 7, 8
4 b: dw 0
5 start: mov si, offset a
6 mov bx, offset b
7 mov cx, 8
8 s: mov al, cs:[si]
9 add cs:[bx], al
10 inc si
11 loop s
12
13 mov ax, 4c00h
14 int 21h
15 code ends

上述代码中code, start, s: 皆为标号,仅表示内存单元的地址,对于”:”的地址标号,仅可在代码段中使用。

  • 汇编语言中出现的常数可以有7种:二进制数,八进制数,十进制数,十六进制数,十进制浮点数,十六进制浮点数,字符和字符串。
1
2
3
4
5
6
MOV AL, 01000001B ;二进制数
MOV AL, 65 ;十进制数
MOV AL, 65D ;十进制数
MOV AL, 41H ;十六进制数
MOV AL, 0F8H ;十六进制数(当数字的一个字符是A-F时,在字符前添加一个数字0以区分变量)
MOV AL, 'B' ;字符和字符串

伪指令

类似于C语言中的标识符,常用于数据定义,存储控件分配,条件转移,运算等。

  • 定义数据伪指令
    • DB-定义字节,每个数据一个字节。
    • DW-定义字,每个数据两个字节。
    • DD-定义双字,每个数据四个字节。
    • DQ-定义4字,每个数据八个字节。
    • DT-定义10个字节,每个数据十个字节,用于压缩式十进制数。
1
2
3
4
5
6
7
8
9
DATA1 DB 5,6,7,100 ;即DATA1为该组数的首地址,从此地址开始存放5,6,7,100每个数一个字节,共占四个字节。

DATA2 DB ? ;也可以不存放数据,?表示在DATA2单元中存放的内容是随机的。

STR1 DB 'aabbc' ;字符串以ASCII码的形式存放,每个字符占用一个字节,用单引号包裹。
STR2 DW 'ab' ;DW只可以用来定义只含有两个字符的字符串!!!

BUF1 DB 20 DUP(?) ;重复数据定义使用DUP操作符,该句表示定义20个内容随机的数据。
BUF2 DB 10 DUP(2, 2 DUP (3)) ;DUP可嵌套使用,该句表示分配30个存储单元给BUF2
  • 段定义伪指令SEGMENT和ENDS
    一般来说,一个完整的汇编源程序至少由3个段组成,即堆栈段,数据段和代码段。段定义伪指令可将源程序划分为若干段,以便生成目的代码和链接的时候将同名段进行组合。

    段定义的一般格式为:

1
2
3
4
5
6
段名  SEGMENT  [定位类型] [组合类型] [类别]
段内容
...
...
...
段名 ENDS

  注意SEGMENT与ENDS须成对使用,缺一不可。

  • 设定段寄存器伪指令ASSUME: ASSUME——段寄存器定义伪指令。用于指明各段,放在代码段内,放在段定义语句之后。
    格式: ASSUME CS: CODE, DS: DATA, SS: STACK

段分配语句只建立当前段和段寄存器之间的关系,但段分配语句并不能将各段的地址装入段寄存器。

**段组:**

段组伪指令GROUP是用于把源程序模块中的若干个段结合成一个组,并对该段组定义一个段组名。段组伪指令格式如下:

`段组名 GROUP 段名 [, 段名, ......]`

其中段名之间要用逗号分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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的形式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
  • 表达式赋值伪指令EQU:EQU伪指令给符号定义一个值,类似于C语言的 #define凡是出现该符号的地方,汇编是均用其值代替。

    格式:符号名 EQU 表达式

1
2
3
表达式可以是常数、数值表达式、地址表达式、变量、标号或者指令助记符
DATA1 EQU 50
DATA DB DATA1 DUP(?) ;表示给DATA分配50个字节的存储空间,存储内容随机。
  • 地址定义伪指令$:

    $表示地址计数器的值。地址计数器保存当前正在汇编的指令或数据的偏移地址。汇编程序每扫描一个字节,地址计数器的值加1

1
2
3
4
5
6
7
JMP $ ;程序跳转到本条指令,即进入死循环状态。该语句一般用于等待中断的发生。

再如
DATA SEGMENT
ARRAY DB 'PROGRAM'
NUM EQU $-ARRAY ;$表示当前指令的偏移地址值,ARRAY是变量名,表示变量ARRAY的偏移地址值,$-ARRAY是以变量ARRAY为其值地址的连续字节数,即变量名为ARRAY的字符串的字符个数
DATA ENDS
  • 过程定义伪指令PROC与ENDP

    过程——子程序(可被程序调用),汇编语言规定必须对过程进行定义。过程定义之后就可对调用指令CALL与返回执行RET进行正确的汇编。

    如果过程中要用到某些寄存器或者存储单元,为了不破坏原有信息,要将寄存器或存储单元的原有内容压栈保护或存入子程序不用的寄存器或存储单元中。起保护作用的程序段可以放在主程序中,亦可放在子程序中。

    过程定义的语句格式为:

1
2
3
4
5
过程名 PROC NEAR/FAR
语句
...
RET ;过程的最后一条执行指令,将堆栈内保存的返回地址弹出,以实现程序的正确返回
过程名 ENDP

其中,NEAR(近过程)表示该过程与调用指令CALL处在同一代码段中(段名相同),只需将返回位置的偏移地址压入堆栈。

FAR(远过程)表示该过程与调用指令CALL处在不同的代码段中(段名不同),需将返回位置的偏移地址与段基址都压入堆栈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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

用于定义一个模块。在链接目标模块时将使用该模块名,汇编处理只进行到模块结束语句

格式:

1
2
3
NAME 模块名
...
END 标号
  • END伪指令

一个程序模块只允许有一个END语句,后为主模块真起始地址

格式:END [起始地址标号]

  • PUBLIC伪指令

用来说明已知模块中哪些标识符是公共的,可以别其他模块引用

格式:PUBLIC 符号

符号可以是本模块已定义的变量、标号、变量名、过程名等

  • EXTERN伪指令

用于说明模块中哪些标识符是外部的,即其他模块中已被PUBLIC伪指令说明的符号

格式:EXTERN 符号名: 类型[, 符号名: 类型...]

  • 宏定义伪指令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
MOV CL, 4
SAL AL, CL

若该两条指令在程序中要多次使用,就可以用一条宏命令来代替,当然在使用宏命令前要首先对其进行定义,格式如下:

SHIFT MACRO
MOV CL, 4
SAL AL, CL
ENDM
这样定义之后,凡是要使AL中内容左移4位的操作都可以用一条宏命令SHIFT来代替。宏命令一般格式为:
宏命令名 MACRO[形式参量表]
...
...宏体
...
ENDM