7 高级汇编语言技术

学习笔记
作者: MingXiao

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 汇编过程

汇编过程

两次编译

  1. 确定地址,翻译机器码,将字符标号原样写出
  2. 标号代真,将字符标号用计算出的地址/偏移量代换

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

就是jxxxxxx可以被参数代替

宏库

将用到的宏定义分类放到不同的宏库文件中,也是一个程序文件

.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
}


Comments