本篇记录《汇编语言:基于X86处理器》第6章 复习题和练习,编程练习的学习笔记
6.10 复习题和练习
6.10.1 简答题
1.执行下述指令后,BX中的值是多少?
mov bx,0FFFFh
and bx,6Bh
答:BX中的值是6Bh.
2.执行下述指令后,BX中的值是多少?
mov bx,91BAh
and bx,92h
答:BX中的值是92h.
3.执行下述指令后,BX中的值是多少?
mov bx,0649Bh
or bx,3Ah
答:BX中的值是64BBh
4.执行下述指令后,BX中的值是多少?
mov bx, 029D6h
xor bx, 8181h
答:BX中的值是0A857h.
5.执行下述指令后,EBX中的值是多少?
mov ebx,0AFAF649Bh
or ebx,3A219604h
答:EBX中的值是0BFAFF69Fh.
6.执行下述指令后,RBX中的值是多少?
mov rbx,0AFAF649Bh
xor rbx, 0FFFFFFFFh
答:RBX中的值是0FFFFFFFF50509B64h.
完整代码测试笔记:
;6.10.1_1.asm 6.10.1 简答题 1~6题
ExitProcess PROTO
.code
main PROC
;1.执行下述指令后,BX中的值是多少?
mov bx, 0FFFFh
and bx, 6Bh
;2.执行下述指令后,BX中的值是多少?
mov bx, 91BAh
and bx, 92h
;3.执行下述指令后,BX中的值是多少?
mov bx, 0649Bh
or bx, 3Ah
;4.执行下述指令后,BX中的值是多少?
mov bx, 029D6h
xor bx, 8181h
;5.执行下述指令后,EBX中的值是多少?
mov ebx, 0AFAF649Bh
or ebx, 3A219604h
;6.执行下述指令后,RBX中的值是多少?
mov rbx, 0AFAF649Bh
xor rbx, 0FFFFFFFFh
call ExitProcess
main ENDP
END
7.下述指令序列中,写出指定的AL二进制结果值:
mov al, 01101111b
and al, 00101101b ;a. AL = 00101101b = 2Dh
mov al, 6Dh
and al, 4Ah ;b. AL = 01001000b = 48h
mov al, 00001111b
or al, 61h ;c. AL = 01101111b = 6Fh
mov al, 94h
xor al, 37h ;d. AL = 00101101b = 0A3h
答:调试验证:
8.下述指令序列中,写出指定的AL十六进制结果值:
mov al, 7Ah
not al ;a. AL = 10000101b = 85h
mov al, 3Dh
and al, 94h ;b. AL = 00010100b = 14h
mov al, 9Bh
or al, 35h ;c. AL = 10111111b = 0CFh
mov al, 72h
xor al, 0DCh ;d. AL = 11001000b = 0C8h
答:调试验证:
9.下述指令序列中,写出指定的进位标志位、零标志位和符号标志位的值:
mov al, 00001111b
test al, 00000001b ;a. CF=0 ZF=0 SF=0
mov al, 00000110b
cmp al, 00000101b ;b. CF=0 ZF=0 SF=0
mov al, 00000101b
cmp al, 00000111b ;c. CF=1 ZF=0 SF=1
答:调试验证:
10.哪条条件跳转指令根据 ECX的内容执行分支?
答:ECXZ
(Jump if ECX is Zero) 是专门根据 ECX 寄存器内容执行分支的条件跳转指令
11.JA和JNBE指令是如何受到零标志位和进位标志位的影响的?
答:JA
/JNBE
会在满足以下条件时跳转:
CF = 0且ZF = 0(无进位且结果不为零)
标志位含义
- CF = 0
- 表示无符号比较时,第一个操作数不小于第二个操作数(即 ≥)。
- 如果 CF=1,说明发生了借位(第一个操作数 < 第二个操作数),此时不跳转。
- ZF = 0
- 表示两个操作数不相等。
- 如果 ZF=1,说明两数相等(结果为零),此时不跳转。
12.执行下述代码后,EDX的最终值是多少?
mov edx, 1
mov eax, 7FFFh
cmp eax, 8000h
jl L1 (jl是有符号数比较,-1, 小于跳转)
mov edx, 0
L1:
答:EDX的最终值是1
13.执行下述代码后,EDX的最终值是多少?
mov edx, 1
mov eax, 7FFFh
cmp eax, 8000h
jb L1 ;jb是无符号数比较,小于跳转)
mov edx, 0
L1:
答:EDX的最终值是1
14.执行下述代码后,EDX的最终值是多少?
mov edx, 1
mov eax, 7FFFh
cmp eax, 0FFFF8000h
jl L2 ;jl是有符号数比较,小于跳转
mov edx, 0
L2:
答:EDX的最终值是0
15.(真/假):下述代码将跳转到标号Target。
mov eax, -30
cmp eax, -50
jg Target ;有符号比较,大于跳转 cmp结果为20
答:真
16.(真/假):下述代码将跳转到标号 Target。
mov eax,-42
cmp eax, 26
ja Target ;无符号比较,大于跳转 cmp结果为正
答:真
17.执行下列指令后,RBX的值是多少?
mov rbx, 0FFFFFFFFFFFFFFFFh
and rbx,80h
答:RBX的值是80h
18.执行下列指令后,RBX的值是多少?
mov rbx, 0FFFFFFFFFFFFFFFFh
and rbx, 808080h
答:RBX的值是808080h
19.执行下列指令后,RBX的值是多少?
mov rbx, 0FFFFFFFFFFFFFFFFh
and rbx, 80808080h
答:RBX的值是80808080h
这个验证结果是这样子,不得其解??????
6.10.2 算法基础
1.编写一条指令将 AL 中的 ASCII 数字转换为相应的二进制数。如果 AL包含的已经是二进制(00h~ 09h),则不进行转换。
;6.10.2_1.asm 6.10.2 算法基础
;1.编写一条指令将 AL 中的 ASCII 数字转换为相应的二进制数。如果 AL包含的已经是二进制(00h~ 09h),则不进行转换。
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov al, '5' ;测试值
cmp al, 30h
jb L1 ;小于跳转到L1
cmp al, 39h
ja L1 ;大于跳转到L1
sub al, 30h ;转换指令
nop
L1:
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
2.编写指令计算 32 位内存操作数的奇偶性。提示:使用本节之前给出的公式:B0 XOR B1 XOR B2 XOR B3。
;6.10.2_2.asm 6.10.2 算法基础
;2.编写指令计算 32 位内存操作数的奇偶性。提示:使用本节之前给出的公式:B0 XOR B1 XOR B2 XOR B3。
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov eax, 57573535h ;测试值
mov bx, ax ;把低16位给bx,即B2和B3字节
shr eax, 16 ;ax为高16位,即B0和B1字节
xor ah, al ;B0 XOR B1
xor ah, bh ;B0 XOR B1 XOR B2
xor ah, bl ;B0 XOR B1 XOR B2 XOR B3
nop
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
3.设有两个位映射集SetX和SetY,编写指令序列在EAX中生成一个位串,以表示属于SetX但不属于SetY的元素。
;6.10.2_3.asm 6.10.2 算法基础
;3.设有两个位映射集SetX和SetY,编写指令序列在EAX中生成一个位串,以表示属于SetX但不属于SetY的元素。
INCLUDE Irvine32.inc
.data
SetX DWORD 873B2A53h
SetY DWORD 29C7D7A5h
.code
main PROC
mov eax, SetX
mov edx, SetY
not edx ;对SetY取反,得到所有不属于SetY的位
and eax, edx ;EAX = SetX AND (NOT SetY)
;显示二进制字符串
mov ebx, TYPE DWORD
call WriteBinB
call Crlf
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
4.编写指令,若DX中的无符号数小于等于CX中的数,则跳转到标号L1。
;6.10.2_4.asm 6.10.2 算法基础
;4.编写指令,若DX中的无符号数小于等于CX中的数,则跳转到标号L1。
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov ax, 9
mov dx, 12 ;测试值
mov cx, 35 ;测试值
cmp dx, cx
jbe L1 ;无符号小于或等于跳转到L1
mov ax, 1
L1:
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
5.编写指令,若AX中的有符号数大于CX中的数,则跳转到标号 L2。
;6.10.2_5.asm 6.10.2 算法基础
;5.编写指令,若AX中的有符号数大于CX中的数,则跳转到标号 L2。
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov ax, 55 ;测试值
mov cx, 23 ;测试值
cmp ax, cx
jg L2 ;有符号数大于跳转到L2
mov ax, 99
L2: nop
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
6.编写指令,清除 AL的位0和位1,若目的操作数等于零,则代码跳转到标号L3;否则跳转到标号L4。
;6.10.2_6.asm 6.10.2 算法基础
;6.编写指令,清除 AL的位0和位1,若目的操作数等于零,则代码跳转到标号L3;否则跳转到标号L4。
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
.code
main PROC
mov al, 9 ;测试值
and al, 1100b ;清除AL的位0和位1
jz L3 ;为0跳转到L3
jnz L4 ;否则不为0跳转到L4
jmp quit
L3: mov ebx, 8
jmp quit
L4: mov ebx, 9
quit: nop
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
7.汇编语言实现下面的伪代码。使用短路求值,并假设val1和X是32位变量。
;6.10.2_7.asm 6.10.2 算法基础
;7.汇编语言实现下面的伪代码。使用短路求值,并假设val1和X是32位变量。
;if(va11 > ecx) AND (ecx > edx)
; X = 1;
;else
; X= 2;
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
.data
val1 DWORD 12
X DWORD 0
.code
main PROC
mov eax, val1
mov ecx, 10
mov edx, 9
cmp eax, ecx
jbe L1 ;小于等跳转到L1
cmp ecx, edx
jbe L1 ;小于等跳转到L1
mov X, 1
jmp quit
L1: mov X, 2
quit: nop
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
8.汇编语言实现下面的伪代码。使用短路求值,并假设x是32位变量。
;6.10.2_8.asm 6.10.2 算法基础
;8.汇编语言实现下面的伪代码。使用短路求值,并假设x是32位变量。
;if(ebx > ecx)OR(ebx > va11)
; X = 1
;else
; X = 2
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
X DWORD 0
val1 DWORD 10
.code
main PROC
mov eax, val1 ;测试值
mov ebx, 20
mov ecx, 30
cmp ebx, ecx
ja L1 ;大于跳转到L1
cmp ebx, eax
ja L1
mov X, 2
jmp quit
L1: mov X,1
quit:
nop
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
9.汇编语言实现下面的伪代码。使用短路求值,并假设X是32位变量.
;6.10.2_9.asm 6.10.2 算法基础
;9.汇编语言实现下面的伪代码。使用短路求值,并假设X是32位变量.
;if(ebx > ecx AND ebx > edx) OR (edx > eax)
; X = 1
;else
; X = 2
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
X DWORD 0
.code
main PROC
mov eax, 10 ;测试值
mov ebx, 20
mov ecx, 30
mov edx, 40
cmp edx, eax
ja L1 ;大于跳转到L1
cmp ebx, ecx
jbe L2 ;小于等于跳转到L2
cmp ebx, edx
ja L1 ;大于跳转到L1
L2:
mov X, 2
jmp quit
L1: mov X, 1
quit: nop
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
10.汇编语言实现下面的伪代码。使用短路求值,并假设A、B和N是32位有符号数。
;6.10.2_10.asm 6.10.2 算法基础
;10.汇编语言实现下面的伪代码。使用短路求值,并假设A、B和N是32位有符号数。
;while N>0
;if N!=3 AND (N<A OR N>B)
; N = N-2
;else
; N=N-1
;end while
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
N DWORD 20
A DWORD 3
B DWORD 5
.code
main PROC
.WHILE N > 0
mov eax, N ;不能直接比较两个内存变量
.IF ((N != 3) && (eax < A || eax > B))
sub N, 2 ;N = N -2
.ELSE
dec N ;N = N - 1
.ENDIF
.ENDW
INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
6.11 编程练习
●第一次测试程序时,总是用调试器进行单步执行。小细节是很容易被遗忘的,调试器可以让程序员看到实际发生了什么。
●若说明要求使用有符号数组,需确保其中包含一个负数值。
●如果指定了输人数值的范围,则测试数据应包括大于上界、在界限中和低于下界的数值。
●使用不同长度的数组,创建多个测试案例。
●当编写的程序要向数组进行写入操作时,Visual Studio调试器是评估程序正确性的最好工具。C使用调试器的 Memory窗口显示数组,可以选择为十六进制或十进制形式。
●调用被测试过程之后,应立刻再次调用该过程以检查其是否保存了所有的寄存器。示例如下:
mov esi, OFFSET array
mov ecx,count
call CalcSum ;用EAX返回和数
cal1 CalcSum ;再次调用,检查寄存器是否已保存
一般EAX中会有一个返回值,因此,EAX当然是无法保存的。所以,通常不能用EAX输人参数。
●如果打算向过程传递多个数组,则应确保不在过程中引用数组名。取而代之,在调用过程之前,将数组偏移量送入ESI或EDI。这就意味着在过程内应使用间接寻址(形如[esi]或[edi])。
●如果需要定义只用于过程内的变量,可以在变量的前面使用.data伪指令,然后在其后使用.code 伪指令。示例如下:
MyCoolProcedure PROC
.data
sum SDWORD?
.code
mov sum,0
(etc.)
和C++或Java语言中的局部变量不同,该变量仍然全局可见。不过,既然该变量是在过程内定义的,显然不打算在其他的位置使用。当然,必须使用运行时指令来初始化过程内使用变量,因为过程将会被调用多次。再次调用过程时,不会希望它留有前次调用的任何残留的数值。
6.11.2 习题
*1.填充数组
创建过程,用N个随机数填充一个双字数组,这些数必须包含在从j到k的范围内。调用过程时,传递的参数为:保存数据的数组指针、N、j和k的值。对该过程的多次调用之间,要保存所有的寄存器值。编写测试程序,用不同的j和k值两次调用该过程。利用调试器检验结果。
**2.指定范围内的数组元素求和
创建过程,返回从j~k范围(包含)内所有数组元素之和。编写测试程序调用该过程两次需传递的参数为:有符号双字数组指针、数组大小、和k的值。寄存器EAX返回和数。调用过程之间,保存其他所有的寄存器。
**3.计算考试得分
创建过程 CalcGrade,接收0~100范围内的一个整数,并用AL寄存器返回一个大写字母。对该过程的多次调用之间,保存其他所有的寄存器。按照如下规则确定返回字母:
分数范围 |
等级字母 |
90~100 |
A |
80~89 |
B |
70~79 |
C |
60~69 |
D |
0~59 |
F |
编写测试程序,在50~100范围内生成10个随机数。每次生成一个整数,并将其传递给CalcGrade过程。可以使用调试器测试程序,另外,如果选择使用本书的链接库,也可以显示每个整数及其对应的等级字母。(本程序要求用Irvine32链接库,因为它要使用RandomRange 过程。)
**4.大学注册
以6.7.3节的大学注册示例为基础,实现下述功能:
●用CMP和条件跳转指令重新编码(取代.F和.ELSEIF伪指令)。
●实现对学分值的范围检查:学分不能小于1也不能大于30。如果发现无效输人,则显示适当的错误消息。
●提示用户输人平均成绩和学分值。
●显示消息给出评估结果,如“The student canregister”或“The student cannot register”(本程序要求使用 Irvine32 链接库。)
***5.布尔计算器(1)
创建程序,其功能为简单的32位整数布尔运算器。显示菜单提示用户从下表中选择一项。
1.x AND y
2.x OR y
3.NOT x
4.x XOR y
5.Exit program(退出程序)
用户做出选择后,调用过程显示将要执行操作的名称。必须用6.5.4节给出的表驱动选择技术实现该过程。(习题6将实现运算操作。)(本程序要求使用Irvine32链接库。)
***6.布尔计算器(2)
继续编写习题5的程序,实现如下过程:
●AND_op:提示用户输人两个十六进制整数。对其进行AND操作,并用十六进制形式显示结果。
●OR_op:提示用户输入两个十六进制整数。对其进行 OR操作,并用十六进制形式显示结果
●NOT_op:提示用户输入一个十六进制整数。对其进行NOT操作,并用十六进制形式显示结果。
●XOR_op:提示用户输人两个十六进制整数。对其进行异或操作,并用十六进制形式显示结果(本程序要求使用 Irvine32 链接库。)
**7.概率和颜色
编写程序,从3种不同的颜色中随机选择一种用以在屏幕上显示文本。通过循环显示20行文本,每行都随机选择一种颜色。每种颜色出现的概率为:白色=30%,蓝色=10%,绿色=60%。建议:在0~9之间生成一个随机数。如果该数在0~2(包含)之间,则选择白色;如果该数等于3.则选择蓝色:如果该数在4~9(包含)之间,则选择绿色。运行程序10次,对其进行测试。每次运行时,观察文本行颜色的分布是否满足要求的概率。(本程序要求使用Irvine32 链接库。)
***8.消息加密
按要求修改6.3.4节的加密程序:创建包含多个字符的密钥。使用该密钥,通过将密钥与明文相应位进行按位XOR运算,来对明文加密和解密。按需重复使用密钥,直到明文中的全部字节都转换完。例如,假设密钥为“ABXmv#7”,则密钥与明文字节之间的对应如下图所示:
**9.PIN 验证
银行用个人专用识别码(Personal ldentification Number,PIN)对每个客户进行唯一标识。假设银行将用户5位PIN中每一位的可接受数值都限定在指定范围内,下表给出了PIN中从左到右每一位数字的可接受取值范围。由此可见,PIN52413是有效的,而PIN43534是无效的,因为第一个数字不在指定范围内。同样,由于最后一个数字,64532也是无效的。
数字序号 |
范围 |
1 |
5~9 |
2 |
2~5 |
3 |
4~8 |
4 |
1~4 |
5 |
3~6 |
本题任务是创建过程 ValidatePIN:接收一个字节数组指针,该数组包含一个5位的PIN值;定义两个数组保存取值范围的最小值和最大值,并使用这些数值来验证传递给过程的PIN中的每一位数字。如有任何一位数字超出范围,则立刻用EAX寄存器返回该数字的位置(1~5)。如果整个 PIN都是有效的,则用EAX返回0。对该过程的多次调用之间,要保存其他所有寄存器的值编写测试程序,使用有效和无效的字节数组,调用ValidatePIN至少四次。在调试器中运行测试程序,每次调用过程后,验证EAX中的返回值是否有效。另外,如果选择用本书的链接库、也可以在每次过程调用后在控制台上显示“Valid”或“Invalid”。
****10.奇偶性检查
数据传输系统和文件子系统通常依靠计算数据块的奇偶性(偶校验或奇校验)来进行错误检测。本题任务是创建一个过程,如果字节数组为偶校验,则EAX返回True;如果是奇校验,则EAX返回Fase。换句话说,如果计算整个数组中的所有位,则结果将为偶数或奇数。对该过程的多次调用之间,要保存其他所有寄存器的值。编写测试程序,调用该过程两次,每次都向其传递数组指针和数组长度。EAX中的过程返回值应为1(True)或0(False)。测试数据,需创建两个至少包括10字节的数组,一个为偶校验,另一个为奇校验。
提示 本章前面的内容展示了如何通过对字节序列反复使用XOR指令,来确定其奇偶性因此,建议使用循环结构。但要注意的是,由于某些机器指令会影响奇偶标志位,而其他指令不会影响奇偶标志位(查看附录B中的所有指令就能发现这一点)。所以,循环结构中检查奇偶性的代码应小心保存和恢复奇偶标志位的状态,以避免程序代码无意间修改了该标志位。