《汇编语言:基于X86处理器》第8章 复习题和练习,编程练习
本篇记录《汇编语言:基于X86处理器》第8章 复习题和练习,编程练习的学习笔记.
8.10复习题和练习
8.10.1 简答题
1.若过程有堆栈参数和局部变量,那么在其结尾部分应包含哪些语句?
答:
- 恢复堆栈指针(ESP)
通过mov esp, ebp
撤销局部变量的空间分配(确保ESP指向正确的返回地址)。 - 恢复基址指针(EBP)
通过pop ebp
还原调用者的EBP值(遵循标准栈帧结构)。 - 清理堆栈参数(可选)
- 若调用约定是
stdcall
(由被调者清理参数),使用ret n
(n
为参数总字节数,如ret 8
表示清理2个4字节参数)。 - 若调用约定是
cdecl
(由调用者清理),直接ret
,无需立即清理参数。
例如:
MyProc PROC; 1. 保存调用者的EBP并建立新栈帧push ebpmov ebp, esp; 2. 分配局部变量空间(如预留8字节)sub esp, 8; ... 使用参数和局部变量的代码 ...; 3. 结尾部分mov esp, ebp ; 释放局部变量空间pop ebp ; 恢复调用者的EBPret 8 ; 返回并清理8字节参数(stdcall)
MyProc ENDP
2.当 C 函数返回 32 位整数时,返回值保存在哪里?
答:返回值存在eax中。代码验证:
#include <stdio.h>int sum(int a, int b)
{int s = a + b;return s;}
int main()
{int a = 10;int b = 20;int c = sum(a, b);printf("c = %d\n", c);return 0;
}
反汇编:
3.使用STDCALL调用规范的程序在过程调用之后如何清除堆栈?
答:ret (n*4) ; 返回并清理(n*4)字节参数(stdcall)
4.为什么LEA 指令比 OFFSET运算符功能更强?
答: OFFSET 只适用于编译时已知的地址 , 不能用 OFFSET 获得堆栈参数的地址.
1. 运行时计算 vs 汇编时计算
- OFFSET:是一个汇编时运算符,在汇编阶段由汇编器计算地址偏移量
- LEA:是一条CPU指令,在运行时计算有效地址
2. 动态地址计算能力
LEA可以计算更复杂的地址表达式:
; 使用OFFSET只能获取简单变量的地址
mov bx, OFFSET var1
; 使用LEA可以计算包含索引、比例因子等的复杂地址
lea ebx, [esi + edi*4 + 10h] ; 计算ESI + EDI*4 + 16的地址
5.在 8.2.3 节给出的 C++示例中,一个int 类型的变量需占用多少堆栈空间?
答: 一个int类型的变量始终占用 4 字节堆栈空间。
6.与STDCALL调用规范相比C调用规范会有哪些优势?
答:对比表格
特性 |
|
|
堆栈清理责任方 | 调用者 | 被调用函数 |
可变参数支持 | 是(如 | 否 |
代码体积 | 可能更小(重复优化) | 每个函数嵌入清理指令 |
典型应用场景 | C标准库、跨平台代码 | Windows API |
7.(真/假):使用PROC 伪指令时,所有参数必须列在同一行上。
答:假
8.(真/假):若向一个期望接收字数组指针的过程传递的变量包含的是字节数组偏移量,则汇编器会将其标志为错误。
答:假
;8.10.1_8.asm 8.10.1 简答题INCLUDE Irvine32.inc.data
byteArray db 1, 2, 3, 4 ; 字节数组
wordArray dw 1234h, 5678h ; 字数组.code
; 期望接收字数组指针的过程
ProcessWordArray proc ptrArray:PTR WORD; 使用 ptrArray 作为字数组的指针mov esi, ptrArraymov ax, WORD PTR [esi]mov bx, WORD PTR [esi+2]ret
ProcessWordArray endpmain procmov esi, OFFSET byteArray;语法合法,但逻辑错误:传递字节数组的偏移量invoke ProcessWordArray, offset byteArraymov edi, OFFSET wordArray;正确的用法invoke ProcessWordArray, offset wordArrayINVOKE ExitProcess, 0
main endp
END main
9.(真/假):若向一个期望接收引用参数的过程传递了立即数,则会产生一般保护错误。
答:假,似立即数不会报错。
8.10.2 算法基础
1.下面是过程 AddThree 的调用指令序列,该过程实现三个双字的加法(假设使用的是STDCALL 调用规范);
push 10h
push 20h
push 30h
call AddThree
请画出EBP被压入运行时堆栈后过程堆栈帧的示意图。
2.新建过程 AddThree,接收三个整型参数,计算并用 EAX 寄存器返回它们的和。
;8.10.2_2.asm 8.10.2 算法题
;2.新建过程 AddThree,接收三个整型参数,计算并用 EAX 寄存器返回它们的和。INCLUDE Irvine32.inc.data.code
;计算3个整型数据的和
AddThree PROC one:DWORD, two:DWORD, three:DWORDadd eax, oneadd eax, twoadd eax, threeret
AddThree ENDPmain procmov eax, 0INVOKE AddThree, 10h, 20h, 30hmov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行调试:
3.声明局部变量 pArray 作为双字数组的指针。
;8.10.2_3.asm 8.10.2 算法题
;3.声明局部变量 pArray 作为双字数组的指针。INCLUDE Irvine32.inc.data
array DWORD 11223344h, 55667788h.code
;局部变量
AddThree PROC LOCAL pArray:PTR DWORD ;声明局部变量;使用局部变量mov pArray, offset array;mov eax, [pArray]mov esi, pArraymov eax, [esi]ret
AddThree ENDPmain procmov eax, 0INVOKE AddThree mov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行调试:
4.声明局部变量 buffer 作为含有 20 个字节的数组。
;8.10.2_4.asm 8.10.2 算法题
;4.声明局部变量 buffer 作为含有 20 个字节的数组。 INCLUDE Irvine32.inc.data
array BYTE 10h,11h,12h,13h,14h,15h,16h,17h,18h,19h,20h,21h,22h,23h,24h,25h,26h,27h,28h,29h.code
;局部变量
AddThree PROC LOCAL buffer[20]:BYTE ;声明局部变量;使用局部变量mov esi, OFFSET arraymov ecx, LENGTHOF arraymov edi, 0
L1: mov al, BYTE PTR [esi]mov BYTE PTR buffer[edi], alinc esiinc ediloop L1ret
AddThree ENDPmain procmov eax, 0INVOKE AddThree mov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行调试:
5.声明局部变量pwArray指向一个 16 位无符号整数。
;8.10.2_5.asm 8.10.2 算法题
;5.声明局部变量pwArray指向一个 16 位无符号整数。INCLUDE Irvine32.inc.data
var16Bit WORD 1122h.code
;局部变量
MySub PROC LOCAL pArray:PTR WORD ;声明局部变量;使用局部变量mov pArray, offset var16Bit;mov eax, [pArray]mov esi, pArraymov ax, WORD PTR[esi]ret
MySub ENDPmain procmov eax, 0INVOKE MySub mov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行调试:
6.声明局部变量myByte包含一个8位有符号整数。
;8.10.2_6.asm 8.10.2 算法题
;6.声明局部变量myByte包含一个8位有符号整数。INCLUDE Irvine32.inc.data
varS8Bit SBYTE 55h.code
;局部变量
AddThree PROC LOCAL var8Bit:SBYTE ;声明局部变量;使用局部变量mov bl, varS8Bitmov var8Bit, blmov al, var8Bitret
AddThree ENDPmain procmov eax, 0INVOKE AddThree mov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行调试:
7.声明局部变量myArray 作为含有 20 个双字的数组。
;8.10.2_7.asm 8.10.2 算法题
;7.声明局部变量myArray 作为含有 20 个双字的数组。INCLUDE Irvine32.inc.data
array WORD 1010h,1111h,1212h,1313h,1414h,1515h,1616h,1717h,1818h,1919h,2020h,2121h,2222h,2323h,2424h,2525h,2626h,2727h,2828h,2929h.code
;局部变量
AddThree PROC LOCAL buffer[20]:WORD ;声明局部变量;使用局部变量mov esi, OFFSET arraymov ecx, LENGTHOF arraymov edi, 0
L1: mov ax, WORD PTR [esi]mov WORD PTR buffer[edi], axadd esi, 2add edi, 2loop L1ret
AddThree ENDPmain procmov eax, 0INVOKE AddThree mov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行调试:
8.新建过程 SetColor 接收两个堆栈参数:前景色和背景色,并调用Irvine32链接库的SetTextColor过程。
;8.10.2_8.asm 8.10.2 算法题
;8.新建过程 SetColor 接收两个堆栈参数:前景色和背景色,并调用Irvine32链接库的SetTextColor过程。INCLUDE Irvine32.inc.data
array BYTE "Example of setting foreground and background colors", 0
BlueTextOnGray = blue + (lightGray*16)
DefaultColor = lightGray + (black*16)
YellowTextOnBlue = yellow + (blue * 16)
RedTextOnBlue = red + (blue * 16).code
;设置前景色和背景色示例 yellow + blue * 16
SetColor PROC foreground:BYTE, background:BYTEmov eax, 0mov al, background ;背景色shl al, 4 ;背景色是4~7位add al, foregroundcall SetTextColorret
SetColor ENDPmain procINVOKE SetColor, red, bluemov edx, OFFSET arraycall WriteStringcall Crlf;mov eax,RedTextOnBlue ;mov eax,BlueTextOnGray ;call SetTextColor;call Clrscr INVOKE ExitProcess, 0
main endp
END main
运行结果:
9.新建过程 WriteColorChar 接收三个堆栈参数:字符、前景色和背景色。该过程用指定的前景色和背景色显示单个字符。
;8.10.2_9.asm 8.10.2 算法题
;9.新建过程 WriteColorChar 接收三个堆栈参数:字符、前景色和背景色。
;该过程用指定的前景色和背景色显示单个字符。INCLUDE Irvine32.inc.data
RedTextOnBlue = red + (blue * 16).code
;设置前景色和背景色示例 yellow + blue * 16
WriteColorChar PROC foreground:BYTE, background:BYTE, char:BYTEmov eax, 0mov al, background ;背景色shl al, 4 ;背景色是4~7位add al, foreground ;前景色call SetTextColormov al, charcall WriteChar ;显示单个字符ret
WriteColorChar ENDPmain procINVOKE WriteColorChar, red, blue, 's'call Crlf;mov eax,RedTextOnBlue ;call SetTextColor;call Clrscr INVOKE ExitProcess, 0
main endp
END main
运行结果:
10.编写过程 DumpMemory封装Irvine32链接库的 DumpMem 过程。要求使用已声明的参数和 USES伪指令。该过程被调用的示例为:INVOKE DumpMemory、OFFSET array、LENGTHOF array、TYPE array.
;8.10.2_10.asm 8.10.2 算法题
;10.编写过程 DumpMemory封装Irvine32链接库的 DumpMem 过程。要求使用已声明的参数和 USES伪指令。
;该过程被调用的示例为:INVOKE DumpMemory、OFFSET array、LENGTHOF array、TYPE array.INCLUDE Irvine32.inc.data
array DWORD 11111111h, 22222222h, 33333333h, 44444444h, 55555555h.code
;显示内存数据
DumpMemory PROC USES ecx ebx esi eax,offsetValue:DWORD, ;偏移count:DWORD, ;元素个数elementSize:DWORD ;每个元素所占字节数mov esi, offsetValuemov ebx, elementSizemov ecx, countcall DumpMemcall Crlfret
DumpMemory ENDPmain procmov eax, 11hmov ebx, 22hmov ecx, 33hmov esi, 44hINVOKE DumpMemory, OFFSET array, LENGTHOF array, TYPE arraymov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行结果:
11.声明过程MultArray,接收两个双字数组指针,以及表示数组元素个数的参数。同时,为该过程创建 PROTO 声明。
;8.10.2_11.asm 8.10.2 算法题
;11.声明过程MultArray,接收两个双字数组指针,以及表示数组元素个数的参数。
;同时,为该过程创建 PROTO 声明。INCLUDE Irvine32.inc
MultArray PROTO, pArray1:DWORD, pArray2:DWORD, count:DWORD.data
array1 DWORD 1111h, 2222h, 3333h, 4444h
array2 DWORD 1111h, 2222h, 3333h, 4444h.code
main procmov eax, 0INVOKE MultArray, offset array1, offset array2, LENGTHOF array1mov ebx, eaxINVOKE ExitProcess, 0
main endp;计算两个数组的和
MultArray PROC pArray1:DWORD, pArray2:DWORD, count:DWORDmov esi, pArray1mov edi, pArray2mov ecx, count
L1: mov eax, [edi]add [esi], eaxadd esi, 4add edi, 4loop L1ret
MultArray ENDPEND main
运行调试:
调用后:
8.11 编程练习
*1.FindLargest过程
新建过程 FindLargest 接收两个参数:一个是有符号双字数组指针,另一个是该数组长度的计数器。过程必须用 EAX 返回数组中最大元素的值。声明过程要求使用带参数列表的 PROC 伪指令。保存所有会被该过程修改的寄存器(EAX 除外)。编写测试程序调用FindLargest,并向其传递三个长度不同的数组,这些数组中的元素应含有负数。为FindLargest 新建 PROTO 声明。
;8.11_1.asm 8.11 编程练习 *1.FindLargest过程
;新建过程 FindLargest 接收两个参数:一个是有符号双字数组指针,另一个是该数组长度的计数器。
;过程必须用 EAX 返回数组中最大元素的值。声明过程要求使用带参数列表的 PROC 伪指令。
;保存所有会被该过程修改的寄存器(EAX 除外)。编写测试程序调用FindLargest,
;并向其传递三个长度不同的数组,这些数组中的元素应含有负数。为FindLargest 新建 PROTO 声明。INCLUDE Irvine32.inc
FindLargest PROTO pArray:DWORD, count:DWORD.data
array SDWORD 16, -32, 64, 32, -8, 80h.code
main procINVOKE FindLargest, OFFSET array, LENGTHOF array mov ebx, eaxINVOKE ExitProcess, 0
main endp
;---------------------------------------
;找出数组中最大的值
;接收:pArray:DWORD 数组指针
; count:DWORD 元素个数
;返回:eax为返回值
;---------------------------------------
FindLargest PROC USES esi ecx,pArray:DWORD, count:DWORDmov esi, pArraymov ecx, countmov eax, [esi]
L1: mov ebx, [esi]cmp eax, ebxjg L2 ;大于等于跳转到L2mov eax, [esi] ;小于把最大值赋给eax
L2: add esi, 4loop L1ret
FindLargest ENDP
END main
运行调试:
*2.棋盘
编写程序画一个8x8的棋盘,盘面为相互交替的灰色和白色方块。编程时可以使用Irvine32链接库的 SetTextColor 和 Gotoxy过程,不要使用全局变量,使用每个过程中声明的参数。每个过程只完成单一任务,以尽可能地简短。
;8.11_2.asm 8.11 编程练习 *2.棋盘INCLUDE Irvine32.inc
; 棋盘绘制程序
; 使用Irvine32库的SetTextColor和Gotoxy过程
; 绘制8x8交替灰色和白色方格的棋盘.data
; 无全局变量,仅使用局部变量和寄存器传递数据.code
main PROC; 设置初始光标位置 (0, 0)mov dh, 1 ; 行mov dl, 2 ; 列call DrawChessboardcall Crlf ;换行exit
main ENDP; 子过程:绘制整个棋盘
DrawChessboard PROC; 参数:dh = 起始行,dl = 起始列; 局部变量:ch = 行计数器,cl = 列计数器,al = 当前方格颜色pushamov ch, 0 ; 行计数器DrawRow:cmp ch, 8jge EndDrawmov cl, 0 ; 列计数器重置为0DrawColumn:cmp cl, 8jge NextRow;计算当前方格的颜色mov al, chadd al, cltest al, 1 ;按位与,判断是单数列还是偶数列jnz SetGray ;结果等于0,偶数列jmp SetWhiteSetGray:;文字颜色+背景色mov eax, black + (gray * 16)call SetTextColorjmp PrintSquareSetWhite:;文字颜色+背景色mov eax, black + (white * 16)call SetTextColorPrintSquare:;定位到当前光标位置;mov bh, 0call Gotoxy;打印一个空格作为方格mov al, ' 'call WriteChar ;因为行的宽度比列高小很多call WriteChar ;所以这里打印两个空格,显得更好看些; 移动到下一列add dl, 2 ;列宽占两个位置,显得更好看些inc cljmp DrawColumnNextRow:; 移动到下一行inc dhmov dl, 2 ;从第2列开始显示,显示效果更佳inc chjmp DrawRowEndDraw:;恢复控制台颜色mov eax, lightGray + (black * 16)call SetTextColorpoparet
DrawChessboard ENDPEND main
运行结果:
***3.变色棋盘
本题是习题2 的延伸。每隔 500 毫秒,有颜色的方块变色并再次显示棋盘。使用所有可能的4位背景色,重复该过程直到显示棋盘 16 次。(整个过程中白色方块保持不变。)
;8.11_3.asm 8.11 编程练习 ***3.变色棋盘
;本题是习题2 的延伸。每隔 500 毫秒,有颜色的方块变色并再次显示棋盘。
;使用所有可能的4位背景色,重复该过程直到显示棋盘 16 次。(整个过程中白色方块保持不变。)INCLUDE Irvine32.inc
;棋盘绘制程序
;使用Irvine32库的SetTextColor和Gotoxy过程
;绘制8x8交替灰色和白色方格的棋盘.data
;背景色0~15
array BYTE 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15.code
main PROC; 设置初始光标位置 (0, 0)mov dh, 1 ;行mov dl, 2 ;列mov esi, offset array ;背景色偏移量mov ecx, LENGTHOF array ;16种变化
L1: push ecxcall DrawChessboardpop ecxinc esimov eax, 500 ;500毫秒call Delay ;延迟500毫秒loop L1call Crlf ;换行exit
main ENDP; 子过程:绘制整个棋盘
DrawChessboard PROC;参数:dh = 起始行,dl = 起始列;局部变量:ch = 行计数器,cl = 列计数器,al = 当前方格颜色pushamov ch, 0 ;行计数器DrawRow:cmp ch, 8jge EndDrawmov cl, 0 ;列计数器重置为0DrawColumn:cmp cl, 8jge NextRow;计算当前方格的颜色mov al, chadd al, cltest al, 1 ;按位与,判断是单数列还是偶数列jnz SetGray ;结果等于0,偶数列jmp SetWhiteSetGray:;文字颜色+背景色;mov eax, black + (gray * 16)mov eax, [esi]shl eax, 4 ;背景色*16add eax, blackcall SetTextColorjmp PrintSquareSetWhite:;文字颜色+背景色mov eax, black + (white * 16)call SetTextColorPrintSquare:;定位到当前光标位置;mov bh, 0call Gotoxy;打印一个空格作为方格mov al, ' 'call WriteChar ;因为行的宽度比列高小很多call WriteChar ;所以这里打印两个空格,显得更好看些;移动到下一列add dl, 2 ;列宽占两个位置,显得更好看些inc cljmp DrawColumnNextRow:;移动到下一行inc dhmov dl, 2 ;从第2列开始显示,显示效果更佳inc chjmp DrawRowEndDraw:;恢复控制台颜色mov eax, lightGray + (black * 16)call SetTextColorpoparet
DrawChessboard ENDPEND main
运行调试:
**4.FindThrees过程
新建过程 FindThrees,若数组存在三个连续的数值3,过程返回1,否则返回 0。过程输入参数列表包括:一个数组指针和该数组的长度。过程声明要求使用带参数列表的PROC 伪指令。所有会被该过程修改的寄存器都需保存(EAX 除外)。编写一个测试程序用不同的数组多次调用FindThree.
;8.11_4.asm 8.11 编程练习 **4.FindThrees过程INCLUDE Irvine32.inc.data
array1 BYTE 1, 1, 2, 3, 4, 4, 5, 3, 3, 3
array2 BYTE 1, 1, 2, 3, 3, 4, 5, 3, 3, 6
array3 BYTE 1, 1, 2, 3, 3, 3, 5, 3, 7, 3
array4 BYTE 1, 1, 2, 3, 5, 4, 5, 3, 3, 6.code
;--------------------------------------------
;判断数组是否存在三个连续的值
;接收:pArray:数组指针
; count:数组元素个数
;返回:eax 存在3个连续的值3 eax为1,否则为0
;--------------------------------------------
FindThrees PROC pArray:DWORD, count:DWORDmov esi, pArraymov ecx, countmov ebx, 0mov eax, 0
L1: cmp BYTE PTR [esi], 3jne L2 ;不相待重新计数inc ebx ;相等计数加1jmp Next
L2: mov ebx, 0 ;重新计数
Next:inc esi ;下一个元素cmp ebx, 3je L3 ;3个连续的值3,跳出循环loop L1jmp L4
L3: mov eax, 1
L4:ret
FindThrees ENDPmain procmov eax, 0INVOKE FindThrees, OFFSET array1, LENGTHOF array1INVOKE FindThrees, OFFSET array2, LENGTHOF array2INVOKE FindThrees, OFFSET array3, LENGTHOF array3INVOKE FindThrees, OFFSET array4, LENGTHOF array4mov ebx, eaxINVOKE ExitProcess, 0
main endp
END main
运行调试:
**5.Differentinputs过程
编写过程DifferentInputs,若其三个输入参数不同,则返回EAX=1:否则返回EAX=0。过程声明要求使用带参数列表的 PROC 伪指令,并为过程新建 PROTO 声明。编写测试程序,用不同的输入值调用过程5次。
;8.11_5.asm 8.11 编程练习 **5.Differentinputs过程INCLUDE Irvine32.inc.data
array1 BYTE "Please enter the first parameter:", 0
array2 BYTE "Please enter the second parameter:", 0
array3 BYTE "Please enter the third parameter:", 0
paramArray DWORD 3 DUP(?).code
;--------------------------------------------
;判断输入的3个参数各不相同
;接收:三个输入参数
;返回:三个参数都不相同,eax为1,否则eax为0
;--------------------------------------------
DifferentInputs PROC one:DWORD, two:DWORD, three:DWORDmov eax, 0mov ebx, onecmp ebx, twoje quit ;相同则退出比较cmp ebx, threeje quit ;相同则退出比较mov ebx, twocmp ebx, threeje quit ;相同则退出比较mov eax, 1 ;都不相同 eax为1
quit:ret
DifferentInputs ENDPmain procmov ecx, 5mov esi, offset paramArray
L1: mov edx, offset array1call WriteStringcall ReadInt ;读取的值存放在eax中mov ebx, eaxcall Crlfmov edx, offset array2call WriteStringcall ReadInt ;读取的值存放在eax中mov edx, eaxcall Crlfmov edx, offset array3call WriteStringcall ReadInt ;读取的值存放在eax中call CrlfINVOKE DifferentInputs, eax, ebx, edxcall WriteDeccall Crlfloop L1INVOKE ExitProcess, 0
main endp
END main
运行调试:
**6.整数交换
创建一个随机排序整数数组。使用8.4.6节的Swap过程,编写循环代码段实现数组中每一对连续整数的交换。
;8.11_6.asm 8.11 编程练习 **6.整数交换INCLUDE Irvine32.incSwap PROTO, pValX:PTR DWORD, pValY:PTR DWORD.data
array DWORD 1100100h, 2200200h, 3300300h, 4400400h, 5500500h, 6600600h,7700700h, 8800800h
count = ($-array) / TYPE DWORD / 2 ;交换次数为元素个数/2.code
main PROC;显示交换前的数组mov esi, OFFSET Arraymov edi, esimov ebx, TYPE Arraymov ecx, LENGTHOF arraycall DumpMem ;显示数组mov ecx, count ;交换次数
L1:INVOKE Swap, ADDR[edi], ADDR[edi+4] ;最数组的第1个和第2个数add edi, 8 ;1对数据是8个字节loop L1;显示交换后的数组mov ecx, LENGTHOF arraycall DumpMemINVOKE ExitProcess, 0
main ENDP
;------------------------------------------------
Swap PROC USES eax esi edi,pValX:PTR DWORD, ;第一个整数的指针pValY:PTR DWORD ;第二个整数的指针
;交换两个32位整数的值
;返回:无
;------------------------------------------------mov esi, pValX ;获得指针mov edi, pValY ;获得指针mov eax, [esi] ;取第一个整数xchg eax, [edi] ;与第二个数交换mov [esi], eax ;替换第一个整数ret ;PROC在这里生成 RET 8
Swap ENDP
END main
运行结果:
**7.最大公约数
编写 Euclid 算法的递归实现,找出两个整数的最大公约数(GCD)。在代数书中和网上都可以找到 Euclid 算法的解释说明。编写测试程序调用该GCD 过程 5 次,要求使用如下整数对:(5,20 )、(24,18 )、(11,7 )、(432,226 )、(26,13 )。每次调用过程后,显示找到的 GCD。
;8.11_7.asm 8.11 编程练习 **7.最大公约数
;编写 Euclid 算法的递归实现,找出两个整数的最大公约数(GCD)。在代数书中和网上都可以找到 Euclid 算法的解释说明。
;编写测试程序调用该GCD 过程 5 次,要求使用如下整数对:(5,20 )、(24,18 )、(11,7 )、(432,226 )、(26,13 )。
;每次调用过程后,显示找到的 GCD。
comment !
int gcd(int a, int b) {if (b == 0) {return a;}return gcd(b, a % b);
}
!INCLUDE Irvine32.inc
;测试数据
.data
array1 DWORD 5, 24, 11, 432, 26
array2 DWORD 20, 18, 7, 226, 13.code
;-------------------------------------------
;两个数的最大公约数(GCD) 递归方式
;接收:param1, param2
;返回:eax返回的最大公约数
;--------------------------------------------
GCD PROC param1:DWORD, param2:DWORDmov eax, param1mov ebx, param2cmp ebx, 0je quit ;如果ebx == 0返回 param1mov edx, 0 ;高位清0div ebx ;商在eax中, 余数在edx中 int n = x % yINVOKE GCD, ebx, edx ;递归调用
quit:ret
GCD ENDPmain PROC;没有处理负数的情况mov esi, OFFSET array1mov edi, OFFSET array2mov ecx, LENGTHOF array1
L1:INVOKE GCD, [esi], [edi] ;传值 call WriteDeccall Crlfadd esi, TYPE array1add edi, TYPE array2loop L1INVOKE ExitProcess,0
main ENDP
END main
运行调试:
**8.相等数组元素计数器
编写过程 CountMatches接收两个有符号双字数组指针、表示两个数组长度的参数。对第一个数组中的每个元素x,若第二个数组中相应元素y与之相等,则计数器加1。最后,过程用EAX返回相等数组元素的个数。编写测试程序,用两对不同的数组指针调用该过程。要求;使用INVOKE 语句调用过程并传递堆栈参数;为 CountMatches 创建PROTO 声明;保存并恢复所有会被该过程修改的寄存器(EAX除外)。
;8.11_8.asm 8.11 编程练习 **8.相等数组元素计数器INCLUDE Irvine32.incCountMatches PROTO param1:DWORD, param2:DWORD;测试数据
.data
array1 DWORD 5, 24, 22, 11, 432, 55, 26, 78
array2 DWORD 20, 18, 22, 7, 226, 55, 13, 78.code
main PROCmov esi, OFFSET array1mov edi, OFFSET array2mov ecx, LENGTHOF array1mov eax, 0
L1:INVOKE CountMatches, [esi], [edi] ;传值 add esi, TYPE array1add edi, TYPE array2loop L1call WriteDeccall CrlfINVOKE ExitProcess,0
main ENDP
;-------------------------------------------
;统计两个数组中有相等元素的个数
;接收:param1, param2
;返回:eax返回的相同元素的个数
;--------------------------------------------
CountMatches PROC param1:DWORD, param2:DWORDmov ebx, param2cmp param1, ebxjne quit inc eax ;两个元素相等,eax加1
quit:ret
CountMatches ENDP
END main
运行结果:
**9.近似相等元素计数器
编写过程 CountNearMatches 接收两个有符号双字数组指针、表示两个数组长度的参数和表示两个匹配元素间最大允许误差(称为diff)的参数。对第一个数组中的每个元素x,若第二个数组中相应元素y与它的误差小于等于diff,则计数器加1。最后,过程用 EAX返回近似相等数组元素的个数。编写测试程序,用两对不同的数组指针调用该过程。要求:使用 INVOKE 语句调用过程并传递堆栈参数;为 CountNearMatches 创建 PROTO 声明;保存并恢复所有会被该过程修改的寄存器(EAX 除外)。
;8.11_9.asm 8.11 编程练习 **9.近似相等元素计数器INCLUDE Irvine32.incCountNearMatches PROTO param1:DWORD, param2:DWORD, diff:DWORD;测试数据
.data
array1 DWORD 24, 20, 22, 8, 432, 55, 26, 78
array2 DWORD 20, 18, 22, 7, 226, 55, 13, 78.code
main PROCmov esi, OFFSET array1mov edi, OFFSET array2mov ecx, LENGTHOF array1mov eax, 0
L1:INVOKE CountNearMatches, [esi], [edi], 2 ;传值 add esi, TYPE array1add edi, TYPE array2loop L1call WriteDeccall CrlfINVOKE ExitProcess,0
main ENDP
;-------------------------------------------
;统计两个数组中误差在diff之内的相同元素的个数
;接收:param1, param2, diff
;返回:eax返回的相近元素的个数
;--------------------------------------------
CountNearMatches PROC param1:DWORD, param2:DWORD, diff:DWORDmov ebx, param1sub ebx, param2cmp ebx, 0jae dif ;如果大于或等0跳转到difneg ebx ;如果差小于0取绝对值
dif:cmp ebx, diffja quit ;大于误差值,跳转到quit inc eax ;小于则说明在误差值之内,eax加1
quit:ret
CountNearMatches ENDP
END main
运行结果:
****10.显示过程参数
编写过程ShowParams,显示被调用过程运行时堆栈中32位参数的地址和十六进制数值。参数按照从低地址到高地址的顺序显示。过程输入只有一个整数,用以表示显示参数的个数。比如假设下述 main 中的语句调用了MySample,并传递了三个参数:
INVOKE MySample,1234h,5000h,6543h
然后,在MySample 内部就可以调用 ShowParams,并向其传递希望显示的参数个数:
MySample PROC first:DwORD, second:DWORD,third:DWORD
paramCount=3
call ShowParams, paramCount
ShowParams将按如下格式显示输出:
Stack parameters:
---------------------------------------
Address 0012FF80 = 00001234
Address 0012FF84 = 00005000
Address 0012FF88 = 00006543
实现代码:
;8.11_10.asm 8.11 编程练习 ****10.显示过程参数INCLUDE Irvine32.inc
MySample PROTO first:DWORD, second:DWORD, third:DWORD
ShowParams PROTO countParam:DWORD.data
array BYTE "请输入显示参数的个数:",0
array1 BYTE "Stack parameters:",0
array2 BYTE "----------------------------------",0
array3 BYTE "Address ",0
array4 BYTE " = ",0.code
main procINVOKE MySample, 1234h, 5000h, 6543h mov ebx, eaxINVOKE ExitProcess, 0
main endp
;---------------------------------------
;调用过程运行时堆栈参数
;接收:3个DWORD型参数
;返回:无
;---------------------------------------
MySample PROC first:DWORD, second:DWORD, third:DWORD ;输入显示参数个数mov edx, OFFSET arraycall WriteStringcall ReadInt ;输入的值保存在eax中call CrlfINVOKE ShowParams,eaxret
MySample ENDP
;---------------------------------------
;显示运行时堆栈参数
;接收:3个DWORD型参数
;返回:无
;---------------------------------------
ShowParams PROC paramCount:DWORD ;mov eax, paramCountmov ecx, paramCountpush ebpmov ebp, espmov edx, OFFSET array1call WriteStringcall Crlfmov edx, OFFSET array2call WriteStringcall Crlfadd ebp, 24
L1:mov edx, OFFSET array3call WriteStringmov eax, ebpcall WriteHexmov edx, OFFSET array4call WriteStringmov eax, [ebp]call WriteHexcall Crlfadd ebp, 4loop L1pop ebpret
ShowParams ENDP
END main
运行调试: