7 高级汇编语言技术
7.1 子程序的另一种写法
为了更方便地阅读和调用,子程序的另一种写法是
func proc
;function realization
func endp
其中func是子程序的标号,调用和返回的写法不变
7.2 程序的多文件组织
一个程序可以由多个文件组成,一个文件由多个子程序组成

假定p1.asm是主程序所在的文件,p2.asm是被调用的子程序所在的文件,那么分别如下
p1.asm
extrn subp:far
assume cs:code
code segment
start:
;xxxxx
call far ptr subp; 调用子程序
;xxxxx
mov ax, 4c00H
int 21H
code ends
end start
p2.asm
public subp ;必须是public才能被调用
assume cs:code ;子文件也要写这些
code segment
subp proc
;xxxxxx
retf ;注意是retf
subp endp
在进行编译和连接时如下
masm p1.asm;
masm p2.asm;
link p1.obj + p2.obj;
7.3 汇编指令汇总
略,用到再查吧
7.4 汇编过程

汇编过程
有两次编译
- 确定地址,翻译机器码,将字符标号原样写出
- 标号代真,将字符标号用计算出的地址/偏移量代换
7.5 宏汇编
程序开始之前的预处理,根据预处理命令对程序做出相应处理
经过预处理后编译器才能对程序进行编译
在C语言中,宏定义写法为
#define //xxx
在汇编语言中,宏是源程序中一段有独立功能的代码,由用户定义
在编程时多次使用的功能可以打包为一条宏指令,定义和调用方法如下
assume cs:code
code segment
mproc macro p1, p2, p3 ;虚参
mov ax, p1
mov cx, p2
mov dx, p3
endm
start:
mov ax, data
mov ds, ax
mproc [bx], bx, 100H ;实参
mov ax, 4c00H
int 21H
code ends
end start
其中mproc是宏的标号,p1, p2, p3是宏的虚拟参数表
宏是先定义后调用,必须在开始前定义(不然start不好找)
宏展开后如下
mov ax, [bx]
mov cx, bx
mov dx, 100H
这在编译器编译时一定会展开,用实参代替虚参,而不像函数一样转移
子程序和宏的区别
子程序是模块化的,多次CALL也只是转移,因此可以先调用后定义,且不需要参数列表;但由于多次push, pop,时间开销大
宏需要先定义,且需要指定参数列表,但是宏的参数传送简单,时间开销少;但是宏必须展开,空间开销大
简单说:子程序用时间大换空间小,宏用空间大换时间小
在子程序本身比较短,参数又很多的情况下,宏指令更有效
当然,宏也可以没有参数
pushreg macro
push ax
push bx
push cx
push dx
push bp
;.....
endm
popreg macro
;.....
pop bp
pop dx
pop cx
pop bx
pop ax
endm
这可以在调用函数前后使用,这样子程序的时间开销也变小了,而且子程序更短
pushreg
call far ptr func
popreg
;......
func:
;不用push
;....
;不用pop
retf
宏中的局部标号
在定义宏的时候可以用一个局部标号,必须在宏头下的第一句话
func macro p1
local next ;局部标号next
cmp p1, 0
jge next
neg p1
next:
endm
如果这样的程序调用了宏
func var
;.....
func bx
在展开后如下
;第一个
;...
cmp var, 0
jge xx0000
neg var
xx0000:
endm
;第二个
;...
cmp bx, 0
jge xx0001
neg bx
xx0001:
endm
发现局部标号的地址不受宏之外的程序影响,按顺序排列
不同标签共享一个排序
宏的虚参是操作码的一部分
宏的虚参也可以作为指令的一部分
leap macro cond, lab
j&cond lab
endm
调用如下
leap z, there
leap nz, here
展开如下
jz there
jnz here
就是jxxx的xxx可以被参数代替
宏库
将用到的宏定义分类放到不同的宏库文件中,也是一个程序文件
与.asm文件类似的,宏库的文件后缀是.mac
如name.mac,内容如下
macro1 macro p1, p2
;xxxx
endm
macro2 macro p3, p4
;xxxx
endm
;xxxx
注意,name中不能含有数字
在主程序文件中调用时
include name.mac
;xxxx
macro1 bx, ax
;xxxx
7.6 条件汇编
根据条件把一段程序包括/排除在编译后的文件内,常常和宏一起使用
下面是一个例子,用于求最大值
max macro k, a, b, c
local next, out
mov ax, a
if k-1
if k-2
cmp c, ax
jle next
mov ax, c
endif ;对应k-2
next:
cmp b, ax
jle out
mov ax, b
endif ;对应k-1
out:
endm
宏调用如下
max 1, p
max 2, p, q
max 3, p, q, r
宏展开如下
mov ax, p ;k=1,k-1为假,将内部包括的都不执行
??0001: ;结束宏,out的第一个局部标号
mov ax, p
??0002: ;next的第一个局部标号,可以发现不同label是共享排序的
cmp q, ax
jle ??0003
mov ax, q
??0003: ;out的第二个局部标号
;最后一个宏展开是完整的宏,不赘述
ifndef p(if not define)在虚参p未定义时满足,执行内部的语句
7.7 重复汇编
用于连续产生类似的一组代码,如下面将字符A到Z的ASCII码填入了table数组
char = 'A'
table label byte ;相当于定义了一个数组,数组名是table,数组类型是byte
rept 26
db char
char = char + 1
endm
使用
rept 表达式 ;重复的次数
;重复的操作
endm
不定重复伪操作IRP
格式如下
IRP 哑元,<自变量表>
;重复的内容
endm
下面是一个例子,用于入栈
IRP reg, <ax, bx, cx, dx>
push reg
endm
对于字符串类型的变量表,有专用的表示
array label byte
IRPC k, 12345
db 'NO.&k'
endm
编译后array中存了db 'NO.1'到db 'NO.5'
7.8 反汇编
逆向工程,将机器语言转换为汇编语言(低级转高级)
debug就是一个反汇编工具
7.9 混合编程
使用两种或以上的语言变成,利用每种语言的优势
在使用客场语言时,用对于的后缀包裹起来,例如在C++中使用汇编
_asm
{
;xxx
}