汇编语言Irvine32链接库过程详细说明
本节将逐一介绍《Irvine32链接库》一节中 Irvine32 链接库中的过程是如何使用的,同时也会忽略一些更高级的过程,它们将在后续章节中进行解释。
产生的输出如下所示:
下面的例子调用了 GetMseconds,并保存了返回值。执行循环之后,代码第二次调用 GetMseconds,并将两次返回的时间值相减,结果就是执行循环的大致时间:
MsgBoxAsk 用 EAX 中的返回值表示用户选择的是哪个按钮,返回值有两个选择,都是预先定义的 Windows 常数:IDYES (值为 6)或 IDNO(值为 7)。调用示例如下:
调用示例如下:
调用示例如下:
参阅 ReadDec 过程的说明,详细了解进位标志位是如何受到影响的。
函数利用公式里的种子生成一个随机数值,并且每次都使用前次生成的随机数作为种子,来生成后续随机数。下述代码段展示了一个调用 Random32 的例子:
ReadFromFile 返回时要查看进位标志位的值:如果 CF 清零,则 EAX 包含了从文件中读取的字节数;如果 CF 置 1,则 EAX 包含了数字系统错误代码。调用 WriteWindowsMsg 程就可以获得该错误的文本。在下面的例子中,从文件读取的 5000 个字节复制到了缓冲区变量:
Readlnt 设置溢出标志位,如果输入数值无法表示为 32 位有符号数(范围:-2 147 483 648 至 +2 147 483 647),则显示一个错误信息。返回值包括所有的有效数字,直到遇见第一个非数字字符。例如,如果用户输入 +123ABC,则返回值为 +123。调用示例如下:
AH 寄存器为虚拟扫描码,DX 为虚拟键码,EBX 为键盘标志位。下述伪代码说明了调用 ReadKey 时的各种结果:
颜色常量在 Irvine32.inc 文件中进行定义。要获得完整的颜色字节数值,就将背景色乘以 16 再加上前景色。例如,下述常量表示在蓝色背景上输出黄色字符:
CloseFile
CloseFile 过程关闭之前已经创建或打开的文件(参见 CreateOutputFile 和 OpenlnputFile)。该文件用一个 32 位整数的句柄来标识,句柄由 EAX 传递。如果文件成功关闭,EAX 中的返回值就是非零的。示例如下:mov eax,fileHandle call CloseFile
Clrscr
Clrscr 过程清除控制台窗口。该过程通常在程序开始和结束时被调用。如果在其他时间调用这个过程,就需要先调用 WaitMsg 来暂停程序,这样就可以让用户在屏幕被清除之前,阅读屏幕上的信息。调用示例如下:call WaitMsg ; "Press any key..." call Clrscr
CreateOutputFile
CreateOutputFile 过程创建并打开一个新的磁盘文件,进行写操作。调用该过程时,将文件名的偏移量送入 EDX。过程返回后,如果文件创建成功则 EAX 将包含一个有效文件句柄(32 位整数),否则,EAX 将等于 INVALID_HANDLE_VALUE(一个预定义的常数)。调用示例如下:.data filename BYTE "newfile.txt",0 .code mov edx,OFFSET filename call CreateOutputFile下面的伪代码描述的是调用 CreateOutputFile 之后,可能会出现的结果:
if EAX = INVALID_HANDLE_VALUE the file was not created successfully else EAX = handle for the open file endif
Crlf
Crlf 过程将光标定位在控制台窗口下一行的开始位置。它写的字符串包含了 ASCII 字符代码 ODh 和 OAh。调用示例如下:call Crlf
Delay
Delay 过程按照特定毫秒数暂停程序。在调用 Delay 之前,将预定时间间隔送入 EAXO 调用示例如下:mov eax,1000 ;1 秒 call Delay
DumpMen
DumpMen 过程在控制台窗口中用十六进制的形式显示一段内存区域。ESI 中存放的是内存区域首地址;ECX 中存放的是单元个数;EBX 中存放的是单元大小(1 = 字节,2 = 字,4 = 双字)。下述调用示例用十六进制形式显示了包含 11 个双字的数组:.data array DWORD 1,2,3,4,5,6,7,8,9,0Ah,0Bh .code main PROC mov esi,OFFSET array ;首地址偏移量 mov ecx, LENGTHOF array ;单元个数 mov ebx,TYPE array ;双字格式 call DumpMen
产生的输出如下所示:
00000001 00000002 00000003 00000004 00000005 00000006
00000007 00000008 00000009 0000000A 0000000B
DumpRegs
DumpRegs 过程用十六进制形式显示 EAX、EBX、ECX、EDX、ESI、EDI、EBP、ESP、EIP 和 EFL(EFLAGS)的内容,以及进位标志位、符号标志位、零标志位、溢出标志位、辅助进位标志位和奇偶标志位的值。调用示例如下:call DumpRegs示例输出如下所示:
EAX=00000613 EBX=00000000 ECX=000000FF EDX=00000000
ESI=00000000 EDI=00000100 EBP=0000091E ESP=000000F6
EIP=00401026 EFL=00000286 CF=0 SF=1 ZF=0 OF=0 AF=0 PF=1
GetCommandTail
GetCommandTail 过程将程序命令行复制到一个空字节结束的字符串。如果命令行是空,则进位标志位置 1 ;否则进位标志位清零。该过程的作用在于能让程序用户通过命令行传递参数。假设有一程序 Encrypt.exe 读取输入文件 filel.txt,并产生输出文件 file2.txt。程序运行时,用户可以通过命令行传递这两个文件名:Encrypt filel.txt file2.txt当 Encrypt 程序启动时,它可以调用 GetCommandTail,检索这两个文件名。调用 GetCommandTail 时,EDX 必须包含一个数组的偏移量,该数组至少要有 129 个字节。调用示例如下:
.data cmdTail BYTE 129 DUP ( 0 ) ;空缓冲区 .code mov edx,OFFSET cmdTail call GetCommandTail ;填充缓冲区在 Visual Studio 中运行应用程序时,有一种方法可以传递命令行参数。在 Project 菜单中,选择 <projectname>Properties。在 Property Pages 窗口,展开 Configuration Properties 选项,选择 Debugging。然后,在右边 Command Arguments 面板的编辑行中输入程序的命令参数。
GetMaxXY
GetMaxXY 过程获取控制台窗口缓冲区的大小。如果控制台窗口缓冲区大于可视窗口尺寸,则自动显示滚动条。GetMaxXY 没有输入参数。当过程返回时,DX 寄存器包含了缓冲区的列数,AX 寄存器包含了缓冲区的行数。每个数值的可能范围都不超过 255,这也许会小于实际窗口缓冲区的大小。调用示例如下:.data rows BYTE ? cols BYTE ? .code call GetMaxXY mov rows, al mov cols,dl
GetMseconds
GetMseconds 过程获取主机从午夜开始经过的毫秒数,并用 EAX 返回该值。在计算事件间隔时间时,这个过程是非常有用的。过程不需要输入参数。下面的例子调用了 GetMseconds,并保存了返回值。执行循环之后,代码第二次调用 GetMseconds,并将两次返回的时间值相减,结果就是执行循环的大致时间:
.data startTime DWORD ? .code call GetMseconds mov startTime,eax LI : ;(loop body) loop LI call GetMseconds sub eax, startTime ;EAX = 循环时间,按毫秒计
GetTextColor
GetTextColor 过程获取控制台窗口当前的前景色和背景色,它没有输入参数。返回时,AL 中的高四位是背景色,低四位是前景色。调用示例如下:.data color byte ? .code call GetTextColor mov color,AL
Gotoxy
Gotoxy 过程将光标定位到控制台窗口的指定位置。默认情况下,控制台窗口的X轴范围为 0〜79,Y 轴范围为 0〜24。调用 Gotoxy 时,将 Y 轴(行数)传递到 DH 寄存器,X 轴(列数)传递到 DL 寄存器。调用示例如下:mov dh, 10 ;第 10 行 mov dl, 20 ;第 20 列 call Gotoxy ;定位光标用户可能会修改控制台窗口大小,因此可以调用 GetMaxXY 获取当前窗口的行列数。
IsDigit
IsDigit 过程确定 AL 中的数值是否是一个有效十进制数的 ASCII 码。过程被调用时,将一个 ASCII 字符传递到 AL。如果 AL 包含的是一个有效十进制数,则过程将零标志位置 1;否则,清除零标志位。调用示例如下:mov AL,somechar call IsDigit
MsgBox
MsgBox 过程显示一个带选择项的图形界面弹出消息框。(当程序运行于控制台窗口时有效。)过程用 EDX 传递一个字符串的偏移量,该字符串将显示在消息框中。还可以用 EBX 传递消息框标题字符串的偏移量,如果标题为空,则 EBX 为 0。调用示例如下:.data caption BYTE "Dialog Title", 0 HelloMsg BYTE "This is a pop-up message box.", 0dh, 0ah BYTE "Click OK to continue...", 0 .code mov ebx,OFFSET caption mov edx,OFFSET HelloMsg call MsgBox
MsgBoxAsk
MsgBoxAsk 过程显示带有 Yes 和 No 按钮的图形弹岀消息框。(当程序运行于控制台窗口时有效。)过程用 EDX 传递问题字符串的偏移量,该问题字符串将显示在消息框中。还可以用 EBX 传递消息框标题字符串的偏移量,如果标题为空,则 EBX 为 0。MsgBoxAsk 用 EAX 中的返回值表示用户选择的是哪个按钮,返回值有两个选择,都是预先定义的 Windows 常数:IDYES (值为 6)或 IDNO(值为 7)。调用示例如下:
.data caption BYTE "Survey Completed",0 question BYTE "Thank you for completing the survey." BYTE 0dh,0ah BYTE "Would you like to receive the results?",0 .code mov ebx,OFFSET caption mov edx,OFFSET question call MsgBoxAsk ;查看 EAX 中的返回值
OpenlnputFile
OpenlnputFile 过程打开一个已存在的文件进行输入。过程用 EDX 传递文件名的偏移量。当从过程返回时,如果文件成功打开,则 EAX 就包含有效的文件句柄。 否则,EAX 等于 INVALID_HANDLE_VALUE(一个预定义的常数)。调用示例如下:
.data filename BYTE "myfile.txt",0 .code mov edx,OFFSET filename call OpenlnputFile下述伪代码显示了调用 OpenlnputFile 后可能的结果:
if EAX = INVALID_HANDLE_VALUE the file was not opened successfully else EAX = handle for the open file endif
ParseDecimal32
ParseDecimal32 过程将一个无符号十进制整数字符串转换为 32 位二进制数。非数字符号之前所有的有效数字都要转,前导空格要忽略。过程用 EDX 传递字符 串的偏移量,用 ECX 传递字符串的长度,用 EAX 返回二进制数值。调用示例如下:
.data buffer BYTE "8193" bufSize = ($ - buffer) .code mov edx,OFFSET buffer mov ecx, bufSize call Pars eDecimal32 ;返回 EAX
- 如果整数为空,则 EAX=0 且 CF=1
- 如果整数只有空格,则 EAX=0 且 CF=1
- 如果整数大于(2³²-1),则 EAX=0 且 CF=1
- 否则,EAX 为转换后的数,且 CF=0
参阅 ReadDec 过程的说明,详细了解进位标志位是如何受到影响的。
Parselnteger32
Parselnteger32 过程将一个有符号十进制整数字符串转换为32位二进 制数。字符串开始到第一个非数字符号之间所有的有效数字都要转,前导空格要忽略。过程用 EDX 传递字符串的偏移量,用 ECX 传递字符串的长度,用 EAX 返回二进制数值。调用示例如下:.data buffer byte ,'-8193" bufSize = ($ - buffer) .code mov edx,OFFSET buffer mov ecx,bufSize call Parselnteger32 ;返回 EAX字符串可能包含一个前导加号或减号,但其后只能跟十进制数字。如果数值不能表示为 32 位有符号整数(范围:-2 147 483 648 到 +2 147 483 647),则溢出标志位置 1,且在控制 台显示一个错误信息。
Random32
Random32 过程生成一个 32 位随机整数并用 EAX 返回该数。当被反复调用时,Random32 就会生成一个模拟的随机数序列,这些数由一个简单的函数产生,该函数有一个输入称为种子(seed)。函数利用公式里的种子生成一个随机数值,并且每次都使用前次生成的随机数作为种子,来生成后续随机数。下述代码段展示了一个调用 Random32 的例子:
.data randVal DWORD ? .code call Random32 mov randVal, eax
Randomize
Randomize 过程对 Random32 和 RandomRange 过程的第一个种子进行初始化。种子等于一天中的时间,精度为 1/100 秒。每当调用 Random32 和 RandomRaiige 的程序运行时,生成的随机数序列都不相同。而 Randomize 程只需要在程序开头调用一次。 下面的例子生成了 10 个随机整数:call Randomize mov ecx,10 L1: call Random32 ;在此使用或显示 EAX 中的随机数 loop L1
RandomRange
RandomRange 过程在范围 0〜n-1 内生成一个随机整数,其中 n 是用 EAX 寄存器传递的输入参数。生成的随机数也用 EAX 返回。下面的例子在 0 到 4999 之间生成一个随机整数,并将其放在变量 randVal 中。.data randVal DWORD ? .code mov eax,5000 call RandomRange mov randVal, eax
ReadChar
ReadChar 过程从键盘读取一个字符,并用 AL 寄存器返回,字符不在控制台窗口中回显。调用示例如下:.data char BYTE ? .code call ReadChar mov char,al如果用户按下的是扩展键,如功能键、方向键、Ins 键或 Del 键,则过程就把 AL 清零,而 AH 包含的是键盘扫描码。EAX 的高字节没有使用。下述伪代码描述了调用 ReadChar 之后可能产生的结果:
if an extended key was pressed AL = 0 AH = keyboard scan code else AL = ASCII key value endif
ReadDec
ReadDec 过程从键盘读取一个 32 位无符号十进制整数,并用 EAX 返回该值,前导空格要忽略。返回值为遇到第一个非数字字符之前的所有有效数字。比如,如果用户输入123AEC,则 EAX 中的返回值为 123。下面是一个调用示例:.data intVal DWORD ? .code call ReadDec mov intVal,eaxReadDec 会影响进位标志位:
- 如果整数为空,则 EAX=0 且 CF=1
- 如果整数只有空格,则 EAX=0 且 CF=1
- 如果整数大于(2³²-1),则 EAX=0 且 CF=1
- 否则,EAX 为转换后的数,且 CF=0
ReadFromFile
ReadFromFile 过程读取存储缓冲区中的一个输入磁盘文件。当调用 ReadFromFile 时,用 EAX 传递打开文件的句柄,用 EDX 传递缓冲区的偏移量,用 ECX 传递读取的最大字节数。ReadFromFile 返回时要查看进位标志位的值:如果 CF 清零,则 EAX 包含了从文件中读取的字节数;如果 CF 置 1,则 EAX 包含了数字系统错误代码。调用 WriteWindowsMsg 程就可以获得该错误的文本。在下面的例子中,从文件读取的 5000 个字节复制到了缓冲区变量:
.data BUFFER_SIZE = 5000 buffer BYTE BUFFER_SIZE DUP(?) bytesRead DWORD ? .code mov edx,OFFSET buffer ;指向缓冲区 mov ecx,BUFFER_SIZE ;读取的最大字节数 call ReadFromFile ; 读文件 }如果此时进位标志位清零,则可以执行如下指令:
mov bytesRead, eax ;实际读取的字节数但是,如果此时进位标志位置 1,就可以调用 WriteWindowsMsg 过程,显示错误代码以及该应用最近产生错误的说明:
call WriteWindowsMsg
ReadHex
ReadHex 过程从键盘读取一个 32 位十六进制整数,并用 EAX 返回相应的二进制数。对无效字符不进行任何错误检查。字母 A 到 F 的大小写都可以使用。最多能够输入 8 个数字(超出的字符将被忽略),前导空格将被忽略。调用示例如下:.data hexVal DWORD ? .code call ReadHex mov hexVal,eax
Readlnt
Readlnt 过程从键盘读取一个 32 位有符号整数,并用 EAX 返回该值。用户可以键入前置加号或减号,而其后跟的只能是数字。Readlnt 设置溢出标志位,如果输入数值无法表示为 32 位有符号数(范围:-2 147 483 648 至 +2 147 483 647),则显示一个错误信息。返回值包括所有的有效数字,直到遇见第一个非数字字符。例如,如果用户输入 +123ABC,则返回值为 +123。调用示例如下:
.data intVal SDWORD ? .code call Readlnt mov intVal,eax
ReadKey
ReadKey 过程执行无等待键盘检查。换句话说,它检查键盘输入缓冲区以查看用户是否有按键操作。如果没有发现键盘数据,则零标志位置 1。如果 ReadKey 发现有按键,则清除零标志位,且向 AL 送入 0 或 ASCII 码。若 AL 为 0,表示用户可能按下了一个特殊键(功能键、方向键等)。AH 寄存器为虚拟扫描码,DX 为虚拟键码,EBX 为键盘标志位。下述伪代码说明了调用 ReadKey 时的各种结果:
if no_keyboard_data then ZF = 1 else ZF = 0 if AL = 0 then extended key was pressed, and AH = scan code, DX = virtual key code, and EBX = keyboard flag bits else AL = the key's ASCII code endif endif当调用 ReadKey 时,EAX 和 EDX 的高 16 位会被覆盖。
ReadString
ReadString 过程从键盘读取一个字符串,直到用户键入回车键。过程用 EDX 传递缓冲区的偏移量,用 ECX 传递用户能键入的最大字符数加 1(保留给终止空字节),用 EAX 返回用户键入的字符数。示例调用如下:.data buffer BYTE 21 DUP(0) ;输入缓冲区 byteCount DWORD ? ;定义计数器 .code mov edx,OFFSET buffer ;指向缓冲区 mov ecxz SIZEOF buffer ;定义最大字符数 call ReadString ;输入字符串 mov byteCount, eax ;字符数ReadString 在内存中字符串的末尾自动插入一个 null 终止符。用户输入“ABCDEFG”后,buffer 中前 8 个字节的十六进制形式和 ASCII 形式如下所示:
41 42 43 44 45 46 47 00 ABCDEFG
变量 byteCoun t等于 7。SetTextColor
SetTextColor 过程(仅在 Irvine32 链接库中)设置输出文本的前景色和背景色。调用 SetTextColor 时,给 EAX 分配一个颜色属性。下列预定义的颜色常数都可以用于前景色和背景色:black = 0 | red = 4 | gray = 8 | lightRed = 12 |
blue = 1 | magenta = 5 | lightBlue = 9 | light Magenta = 13 |
green = 2 | brown = 6 | light Green = 10 | yellow = 14 |
cyan = 3 | lightGray = 7 | lightCyan = 11 | white = 15 |
颜色常量在 Irvine32.inc 文件中进行定义。要获得完整的颜色字节数值,就将背景色乘以 16 再加上前景色。例如,下述常量表示在蓝色背景上输出黄色字符:
yellow + (blue * 16)
下列语句设置为蓝色背景上输出白色字符:
mov eax,white + (blue * 16) ; 蓝底白字
call SetTextColor
yellow + (blue SHL 4)
位移是在汇编时执行的,因此它只能用常数作操作数。Str_length
Str_length 过程返回空字节结束的字符串的长度。过程用 EDX 传递字符串的偏移量,用 EAX 返回字符串的长度。调用示例如下:.data buffer BYTE "abcde",0 bufLength DWORD ? .code mov edx, OFFSET buffer ;指向字符串 call Str_length ;EAX=5 mov bufLength, eax ;保存长度
WaitMsg
WaitMsg 过程显示“Press any key to continue…”消息,并等待用户按键。当用户想在数据滚动和消失之前暂停屏幕显示时,这个过程就很有用。过程没有输入参数。 调用示例如下:call WaitMsg
WriteBin
WriteBin 过程以 ASCII 二进制格式向控制台窗口输出一个整数。过程用 EAX 传递该整数。为了便于阅读,二进制位以四位一组的形式进行显示。调用示例如下:mov eax,12346AF9h call WriteBin示例代码显示如下:
0001 0010 0011 0100 0110 1010 1111 1001
WriteBinB
WriteBinB 过程以 ASCII 二进制格式向控制台窗口输出一个 32 位整数。过程用 EAX 寄存器传递该整数,用 EDX 表示以字节为单位的显示大小(1、2,或 4)。为了便于阅读,二进制位以四位一组的形式进行显示。调用示例如下:mov eax,00001234h mov ebx,TYPE WORD ; 两个字节 call WriteBinB ; 显示 0001 0010 0011 0100
WriteChar
WriteChar 过程向控制台窗口写一个字符。过程用 AL 传递字符(或其 ASCII 码)。调用示例如下:mov al, 'A' call WriteChar ;显示:"A"
WriteDec
WriteDec 过程以十进制格式向控制台窗口输出一个 32 位无符号整数,且没有前置 0。过程用 EAX 寄存器传递该整数。调用示例如下:mov eax,295 call WriteDec ;显示:"295"
WriteHex
WriteHex 过程以 8 位十六进制格式向控制台窗口输出一个 32 位无符号整数,如果需要,应插入前置 0。过程用 EAX 传递整数。调用示例如下:mov eax,7FFFh call WriteHex ;显示:"00007FFF"
WriteHexB
WriteHexB 过程以十六进制格式向控制台窗口输岀一个 32 位无符号整数,如果需要,应插入前置 0。过程用 EAX 传递整数,用 EBX 表示显示格式的字节数(1、2,或 4)。调用示例如下:mov eax, 7FFFh mov ebx, TYPE WORD ;两个字节 call WriteHexB ;显示:"7FFF"
Writelnt
Writelnt 过程以十进制向控制台窗口输岀一个 32 位有符号整数,有前置符号,但没有前置 0。过程用 EAX 传递整数。调用示例如下:mov eax, 216543 call Writelnt ;显示:"+216543"
WriteString
WriteString 过程向操作台窗口输出一个空字节结束的字符串。过程用 EDX 传递字符串的偏移量。调用示例如下:.data prompt BYTE "Enter your name: ",0 .code mov edx,OFFSET prompt call WriteString
WriteToFile
WriteToFile 过程向一个输出文件写入缓冲区内容。过程用 EAX 传递有效的文件句柄,用 EDX 传递缓冲区偏移量,用 ECX 传递写入的字节数。当过程返回时,如果 EAX 大于 0,则其包含的是写入的字节数;否则,发生错误。下述代码调用了 WriteToFile:BUFFER_SIZE = 5000 .data fileHandle DWORD ? buffer BYTE BUFFER_SIZE DUP(?) .code mov eax, fileHandle mov edx, OFFSET buffer mov ecx, BUFFER SIZE call WriteToFile下面的伪代码说明了调用 WriteToFile 之后对 EAX 返回值的处理:
if EAX = 0 then error occurred when writing to file call WriteWindowsMessage to see the error else EAX = number of bytes written to the file endif
WriteWindowsMsg
WriteWindowsMsg 过程向控制台窗口输出应用程序在调用系统函数时最近产生的错误信息。调用示例如下:call WriteWindowsMsg下面的例子展示了一个消息字符串:
Error 2: The system cannot find the file specified.