本教程提供的示例程序包含了一个小而实用的 32 位链接库,只需要在程序的 INCLUDE 后面添加如下代码行就可以使用该链接库:
有些宏封装在了 Irvine32 链接库的过程中,这样传递参数就更加容易。其他宏则提供新的功能。下表详细介绍了每个宏。
宏名 | 形式参数 | 说明 |
---|---|---|
mDump | varName, useLabel | 用变量名和默认属性显示一个变量 |
mDumpMem | abbress, itemCount, componentsize | 显示内存区域 |
mGotoxy | X,Y | 将光标位置设置在控制台窗口缓冲区 |
mReadString | varName | 从键盘读取一个字符串 |
mShow | itsName, format | 用各种格式显示一个变量或寄存器 |
mShowRegister | itsName, regValue | 显示32位寄存器名,并用十六进制显示其内容 |
mWrite | text | 向控制台窗口输出一个字符串文本 |
mWriteSpace | count | 向控制台窗口输出一个或多个空格 |
mWriteString | buffer | 向控制台窗口输岀一个字符串变量的内容 |
宏 mDumpMem 在控制台窗口显示一个内存区域。向其传递的第一个实参为包含待显示内存偏移量的常数、寄存器或者变量,第二个实参应为待显示内存中存储对象的数量,第三个实参为每个存储对象的大小。
宏在调用mDumpMem库过程时,分别将这三个实参分配给 ESI、ECX 和 EBX。现假设有一数据定义如下:
下面的语句按照默认属性显示数组:
输出为:
下面的语句则将同一个数组显示为字节序列:
输出为:
下面的代码把三个数值压入堆栈,并设置好 EBX、ECX 和 ESI,然后调用 mDumpMem 显示堆栈:
显示出来的结果堆栈区域表明,宏已经先把 EBX、ECX 和 ESI 压入了堆栈。这些数值之后是在调用 mDumpMem 之前入栈的 3 个整数:
实现宏代码清单如下:
mDumpMem MACRO address:REQ, itemCount:REQ, componentsize:REQ
;用 DumpMem 过程显示一个内存区域。
;接收:内存偏移量、显示对象的数量,以及每个存储对象的大小。
;避免用 EBX、ECX 和 ESI 传递实参。
push ebx
push ecx
push esi
mov esi, address
mov ecx, itemCount
mov ebx, componentSize
call DumpMem
pop esi
pop ecx
pop ebx
ENDM
宏 mDump 用十六进制显示一个变量的地址和内容。传递给它的参数有:变量名和(可选的)一个字符以表明在该变量之后应显示的标号。显示格式自动与变量的大小属性(BYTE、WORD 或 DWORD)匹配。
下面的例子展示了对 mDump 的两次调用::
代码执行后,产生的输出如下所示:
下面是宏 mDump 的代码清单,它反过来又调用了 mDumpMem。代码用一个新的伪指令 IFNE (若不为空)来发现主调者是否向第二个形参传递了实参:
;-----------------------------------------------
mDump MACRO varName:REQ, useLabel
;用其已知属性显示一个变量。
;接收:varName为变量名。
;如果 useLabel 不为空,则显示变量名。
;-----------------------------------------------
call Crlf
IFNB <useLabel>
mWrite "Variable name: &varName"
ENDIF
mDumpMem OFFSET varName, LENGTHOF varName, TYPE varName
ENDM
&varName 中的符号 & 是替换操作符,它允许将 varName 形参的值插入到字符串文本中。
宏 mGotoxy 把光标定位在控制台窗口缓冲区内指定的行列上。可以向其传递 8 位立即数、内存操作数和寄存器值:
实现 下面是宏的源代码清单:
;-----------------------------
mGotoxy MACRO X:REQ, Y:REQ
;设置光标在控制台窗口的位置。
;接收:X和Y坐标(类型为BYTE)。避免用DH和DL传递实参。
;-----------------------------
push edx
mov dh,Y
mov dl,X
call Gotoxy
pop edx
ENDM
若宏的实参是寄存器,它们有时可能会与宏内使用的寄存器发生冲突。比如,调用 mGotoxy 时用了 DH 和 DL,那么就不会生成正确的代码。为了说明原因,现在来查看上述参数被替换后展开的代码:
假设 DL 传递的是 Y 值,DH 传递的是 X 值,代码行 2 会在代码行 3 有机会把列值复制 到DL之前就替换了 DH的原值。
提示:只要有可能,宏定义应该用注释说明哪些寄存器不能用作实参。
宏 mReadSrting 从键盘读取一个字符串,并将其存储在缓冲区。在这个宏的内部封装了一个对 ReadString 库过程的调用。需向其传递缓冲区名:
下面是宏的源代码:
;-----------------------------------------
mReadString MACRO varName:REQ
;从标准输入读到缓冲区。
;接收:缓冲区名。避免用 ECX 和 EDX 传递实参。
;-----------------------------------------
push ecx
push edx
mov edx,OFFSET varName
mov ecx,SIZEOF varName
call Readstring
pop edx
pop ecx
ENDM
宏 mShow 按照主调者选择的格式显示任何寄存器或变量的名字和内容。传递给它的是寄存器名,其后可选择性地加上一个字母序列,以表明期望的格式。字母选择如下:H = 十六进制,D = 无符号十进制,I 二有符号十进制,B 二二进制,N = 换行。
可以组合多种输出格式,还可以指定多个换行。默认格式为“HIN”。mShow 是一种有用的辅助调试工具,经常被 DumpRegs 库过程使用。可以把mShow当作调试工具,显示重要寄存器或变量的值。
【示例】下面的语句将 AX 寄存器的值显示为十六进制、有符号十进制、无符号十进制和二进制:
输出如下:
【示例】下面的语句在同一行上,用无符号十进制格式显示 AX, BX, CX 和 DX:
相应输出如下:
【示例】下面的代码调用 mShow,用无符号十进制格式显示 mydword 的内容,并换行:
实现 mShow的实现代码太长不便在这里给岀,不过可以在本书安装文件夹(C:\Irvine)内的Macros.inc文件中找到完整代码。在编写mShow时,需要注意在寄存器被宏 自身的内部语句修改之前显示其当前值。
宏 mShowRegister 显示单个 32 位寄存器的名称,并用十六进制格式显示其内容。传递给它的是希望被显示的寄存器名,其后紧跟寄存器本身。下面的宏调用指定了被显示的名称为 EBX:
产生的输出如下:
下面的调用使用尖括号把标号括起来,其原因是标号内有一个空格:
产生输出如下:
实现宏的源代码如下:
;------------------------------------
mShowRegister MACRO regName, regValue
LOCAL tempStr
;显示寄存器名和内容。
;接收:寄存器名,寄存器值
;------------------------------------
.data
tempStr BYTE " ®Name=",0
.code
push eax
;显示寄存器名
push edx
mov edx,OFFSET tempStr
call WriteString
pop edx
;显示寄存器内容
mov eax,regValue
call WriteHex
pop eax
ENDM
宏 mWriteSpace 向控制台窗口输出一个或多个空格。可以选择性地向其传递一个整数形参,以指定空格数 ( 默认为一个 )。例如,下面的语句写了 5 个空格:
实现mWriteSpace的源代码如下:
;-------------------------------------------
mWriteSpace MACRO count:=<1>
;向控制台窗口输出一个或多个空格。
;接收:一个整数以指定空格数。
;默认个数为l。
;-------------------------------------------
LOCAL spaces
.data
spaces BYTE count DUP(' '),0
.code
push edx
mov edx,OFFSET spaces
call WriteString
pop edx
ENDM
宏 mWriteSrting 向控制台窗口输出一个字符串变量的内容。从宏的内部来看,它通过在同一语句行上传递字符串变量名简化了对 WriteString的调用。例如:
mWriteString 的实现如下,它将 EDX 保存到堆栈,然后把字符串偏移量赋给 EDX,在过程调用后,再从堆栈恢复 EDX 的值:
;------------------------------
mWriteString MACRO buffer:REQ
;向标准输出写一个字符串变量。
;接收:字符串变量名。
;------------------------------
push edx
mov edx,OFFSET buffer
call WriteString
pop edx
ENDM