《汇编语言:基于X86处理器》第9章 复习题和练习
本篇记录《汇编语言:基于X86处理器》第9章 复习题和练习的学习笔记.
9.9复习题和练习
9.9.1 简答题
1.执行字符串原语时,怎样设置方向标志位CF会使变址寄存器反向遍历内存区?
答:STD 指令设置方向标志位置1使变址寄存器反向遍历内存区
2.当重复前缀与STOSW 一起使用时,变址寄存器增加或减少的值是多少?
答:STOSW保存字符串数据:将累加器内容保存到EDI寻址的内存位置,变址寄存器增加或减少的值是WORD型长度,即2个字节。
3.何种使用方式将导致CMPS 指令意义不明?
答:导致CMPS意义不明的主要情况是:未指定操作数大小、未设置方向标志(DF)、或未正确初始化指针寄存器。始终使用明确的后缀(如CMPSB)或显式操作数,并确保ESI/EDI和DF状态符合预期。
4.若方向标志位清零,且SCASB 发现了匹配的字符,则此时EDI指向哪里?
答:EDI指向发现匹配字符的下一个位置。
5.若想通过扫描发现第一次出现在数组中的特定字符,那么最好使用哪种重复前缀?
答:repne ;不相等且ECX>0则重复
6.9.3节Str_trim 过程中的方向标志位应如何设置?
答:STD;方向标志位置1,反向操作,从最后一个位置开始处理。
7.为什么9.3节的Str_trim过程要使用JNE指令?
答:要把匹配到的字符清空。mov BYTE PTR [edi+1], 0 ;插入一个空字节
8.如果目标字符串包含了一个数字,则9.3节的 Str_ucase 过程将出现什么情况?
答:数字不会变化,因此程序作了判断,遇到小写字母才会转换。
9.如果9.3节的Str_length过程使用了SCASB,那么最合适的重复前缀是哪个?
答:repne scasb ;不相等则重复下一个字符
;9.9.1_9.asm 9.9.1 简答题
;9.如果9.3节的Str_length过程使用了SCASB,那么最合适的重复前缀是哪个?.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
string BYTE "Good night Tom",0.code
;------------------------------------------
;获取字符串的长度。
;返回值,EAX返回字符串的长度
;------------------------------------------
Str_length PROC USES edi,pString:PTR BYTEmov edi, pString ;指向字符串mov eax, 0 ;字符计数器
L1: cmp BYTE PTR[edi], 0 ;字符结束?je L2 ;是:退出inc edi ;否:指向下一个字符inc eax ;计数器加1jmp L1
L2: ret
Str_length ENDP
;------------------------------------------
;使用SCASB获取字符串的长度。
;返回值,EAX返回字符串的长度
;------------------------------------------
Str_lengthScasb PROC USES edi,pString:PTR BYTEmov edi, pString ;指向字符串mov al, 0 ;检索值0cldrepne scasb ;不相等则重复下一个字符dec edi ;发现字符edi-1mov eax, edisub eax, pString ;偏移值减超始位置ret
Str_lengthScasb ENDPmain PROCINVOKE Str_length, ADDR stringmov ebx, eaxINVOKE Str_lengthScasb, ADDR stringnopINVOKE ExitProcess, 0
main ENDP
END main
运行调试验证:
10.如果9.3节的 Str_length 过程使用了 SCASB,那么它将如何计算并返回字符串长度?
答:用匹配值0的位置减去字符串的起始位置,得到字符串的长度赋给eax。
11.若数组包含1024个元素,则对半查找最多需要比较多少次?
答:10次, =1024
12.在9.5节对半查找示例中,其FillAray过程为什么必须用CLD指令清除方向标志位?
答:填充的时候正向填充。否则反向填充会覆盖其他数据。
13.在9.5节的 BinarySearch 过程中,为什么能在不影响结果的情况下删除标号L2的语句?
答:jge L2 语句前面已经做了比较,结果设置相关标志寄存器,所以这里可以不再次做比较
14.在9.5节的 BinarySearch 过程中,什么情况下有可能删除标号L4的语句?
答:把jmp L4改成jmp L1即可。
9.9.2算法基础
1.举例说明32 位模式下的基址-变址操作数。
答:计算数组的和示例
;9.9.2_1.asm 9.9.2算法基础
;1.举例说明32 位模式下的基址-变址操作数。.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
array DWORD 1010h, 2020h, 3030h, 4040h, 5050h.code
main PROCmov esi, OFFSET array ;基址mov ecx, LENGTHOF array ;数组元素个数mov edi, 0 ;变址mov eax, 0 ;计算数组的和
L1: add eax, [esi+edi] ;基址+变址add edi, 4 ;下一个元素loop L1INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
2.举例说明 32 位模式下的基址-变址-偏移量操作数。
答:
;9.9.2_1.asm 9.9.2算法基础
;1.举例说明32 位模式下的基址-变址操作数。.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
array DWORD 1010h, 2020h, 3030h, 4040h, 5050h
RowSize=($ - array)DWORD 6060h, 7070h, 8080h, 9090h, 0A0A0hDWORD 0B0B0h, 0C0C0h, 0D0D0h, 0E0E0h, 0F0F0h
row = 1
col = 3.code
main PROC;取1行3列的数mov esi, OFFSET array ;基址mov edi, RowSize ;变址;基址+变址+偏移mov eax, [esi + edi*row + col * TYPE DWORD]mov ebx, array[edi*row + col*TYPE DWORD]INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
3.假设一双字二维数组有3个逻辑行和4个逻辑列。编写表达式,用ESI和EDI指向第2行第3列。(行、列都从 0开始编号。)
答:
;9.9.2_3.asm 9.9.2算法基础
;3.假设一双字二维数组有3个逻辑行和4个逻辑列。编写表达式,用ESI和EDI指向第2行第3列。(行、列都从 0开始编号。).386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
.data
array DWORD 1010h, 2020h, 3030h, 4040h
RowSize=($ - array)DWORD 5050h, 6060h, 7070h, 8080hDWORD 9090h, 0A0A0h, 0B0B0h, 0C0C0h
row = 2
col = 3.code
main PROCmov esi, row * RowSizemov edi, col * TYPE DWORDmov eax, array[esi + edi]INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
4.编写指令,用 CMPSW 比较两个16 位的数组sourcew 和targetw。
答:
;9.9.2_4.asm 9.9.2算法基础
;4.编写指令,用 CMPSW 比较两个16 位的数组sourcew 和targetw。.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
sourceW WORD 1234h, 5678h, 9ABCh
targetW WORD 1234h, 5678h, 9ABDh.code
main PROCmov esi, OFFSET sourceWmov edi, OFFSET targetWcmpsw ;比较字ja L1 ;若sourceW > targetW则跳转;多个字数组比较cld ;方向为正mov ecx, LENGTHOF sourceW ;设置重复计数器repe cmpsw ;相等则重复;REPE前缀重复比较操作,并自动增加ESI和EDI,直到ECX等于0,或者发现了一对不相等的双字。
L1: nopINVOKE ExitProcess, 0
main ENDP
END main
运行调试:
最后一个数不相等。
5.编写指令,用SCASW 在数组wordArray 中扫描 16 位的数值0100h,并将匹配元素的偏移量复制到EAX 寄存器。
答:
;9.9.2_5.asm 9.9.2算法基础
;5.编写指令,用SCASW 在数组wordArray 中扫描 16 位的数值0100h,并将匹配元素的偏移量复制到EAX 寄存器。.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
wordArray WORD 1234h, 5678h, 0100h, 9ABCh.code
main PROCmov esi, OFFSET wordArraymov edi, OFFSET wordArraymov ax, 0100hmov ecx, LENGTHOF wordArraycld ;方向为正repne scasw ;不相等则重复jnz quit ;若未发现字会则退出sub edi, 2 ;发现扫描数字,edi指向当前位置 mov eax, edisub eax, OFFSET wordArray ;计算偏移值并保存到EAX中
quit:nopINVOKE ExitProcess, 0
main ENDP
END main
运行调试:
6.编写指令序列,用 Str_compare过程判断两个输入字符串中的较大者,并将其输出到控制台窗口。
答:
;9.9.2_6.asm 9.9.2算法基础
;6.编写指令序列,用 Str_compare过程判断两个输入字符串中的较大者,并将其输出到控制台窗口。INCLUDE Irvine32.inc.data
buffer1 BYTE 32 DUP(0) ;输入缓冲区
buffer2 BYTE 32 DUP(0)
byteCount DWORD ? ;定义计数器
msg1 BYTE "Please enter the first string: ",0
msg2 BYTE "Please enter the second string: ",0
msg3 BYTE "string equal ",0.code
;------------------------------------------
;比较两个字符串。
;无返回值,但是零标志位和进位标志位受到的影响与CMP指令相同。
;------------------------------------------
StrCompare PROC USES eax edx esi edi,string1:PTR BYTE,string2:PTR BYTEmov esi, string1mov edi, string2
L1: mov al, [esi]mov dl, [edi]cmp al, 0 ;string1结束?jne L2 ;否cmp dl, 0 ;是: string2结束?jne L2 ;否mov edx, OFFSET msg3jmp L5 ;是: 退进出且ZF=1
L2: inc esi ;inc edi ;指向下一个字符cmp al, dl ;字符相等?je L1 ;是:继续ja L3 ;buffer1 > buffer2 跳转到L3mov edx, OFFSET buffer2 ;buffer1 < buffer2jmp L5
L3: mov edx, OFFSET buffer1
L5: call WriteStringret ;否:退出
StrCompare ENDPmain PROCmov edx, OFFSET msg1call WriteStringmov edx, OFFSET buffer1 ;指向缓冲区mov ecx, SIZEOF buffer1 ;定义最大字符数call ReadString ;输入字会串 字符串存放在edx指向的缓冲区,个数存在eax中mov byteCount, eax ;有效字符数call Crlfmov edx, OFFSET msg2call WriteStringmov edx, OFFSET buffer2 ;指向缓冲区mov ecx, SIZEOF buffer2 ;定义最大字符数call ReadString ;输入字会串 字符串存放在edx指向的缓冲区,个数存在eax中mov byteCount, eax ;有效字符数call CrlfINVOKE StrCompare, ADDR buffer1, ADDR buffer2INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
7.说明如何调用 Str_trim 过程,并从一个字符串中删除所有的尾部字符“@”。
答:INVOKE Str_trim, ADDR string, ‘@’
;9.9.2_7.asm 9.9.2算法基础
;7.说明如何调用 Str_trim 过程,并从一个字符串中删除所有的尾部字符“@”。;.386
;.model flat, stdcall
;.stack 4096
;ExitProcess PROTO,dwExitCode:DWORD
INCLUDE Irvine32.inc.data
string_1 BYTE "Hello@@@",0
string_2 BYTE "Hello@World@@@",0
string_3 BYTE "Hello",0
string_4 BYTE "H@$#&@@@",0
string_5 BYTE "@Hello",0.code
;------------------------------------------
;获取字符串的长度。
;返回值,EAX返回字符串的长度
;------------------------------------------
StrLengthA PROC USES edi,pString:PTR BYTEmov edi, pString ;指向字符串mov eax, 0 ;字符计数器
L1: cmp BYTE PTR[edi], 0 ;字符结束?je L2 ;是:退出inc edi ;否:指向下一个字符inc eax ;计数器加1jmp L1
L2: ret
StrLengthA ENDP
;------------------------------------------
;显示字符串。
;返回:无
;------------------------------------------
ShowString PROC pString1:PTR BYTEmov edx, pString1call WriteStringcall Crlfret
ShowString ENDP
;------------------------------------------
;从字符串末尾删除所有与给定分隔符匹配的字符。
;返回:无
;------------------------------------------
StrTrim PROC USES eax ecx edi,pStr:PTR BYTE, ;指向字符串char:BYTE ;要移除的字符mov edi, pStr ;准备调用Str_lengthINVOKE StrLengthA, edi ;用EAX返回长度cmp eax, 0 ;长度是否为零?je L3 ;是:立刻退出mov ecx, eax ;否:ECX=字符串长度dec eaxadd edi, eax ;指向最后一个字符
L1: mov al, [edi] ;取一个字符cmp al, char ;是否为分隔符?jne L2 ;否:插入空字节dec edi ;是:继续后退一个字符loop L1 ;直到字符串的第一个字符
L2: mov BYTE PTR [edi+1], 0 ;插入一个空字节
L3: ret
StrTrim ENDP
main PROCINVOKE StrTrim, ADDR string_1, '@'INVOKE ShowString, ADDR string_1INVOKE StrTrim, ADDR string_2, '@'INVOKE ShowString, ADDR string_2INVOKE StrTrim, ADDR string_3, '@'INVOKE ShowString, ADDR string_3INVOKE StrTrim, ADDR string_4, '@'INVOKE ShowString, ADDR string_4INVOKE StrTrim, ADDR string_5, '@'INVOKE ShowString, ADDR string_5INVOKE ExitProcess, 0
main ENDP
END main
运行效果: