当前位置: 首页 > web >正文

《汇编语言:基于X86处理器》第9章 编程练习

本篇记录《汇编语言:基于X86处理器》第9章 编程练习的学习笔记.

9.10 编程练习

下面的练习可以用32位模式或64位模式完成。所有字符串处理过程都假设使用的是空字符结束的字符串。即使没有明确的要求,每道练习都需编写简单的驱动程序来测试所实现的新过程。

*1.改进Str_copy过程

本章给出的Str_copy过程没有限制被复制字符的数量。编写新过程(名为Str_copyN)再接收一个输入参数,表示被复制字符数的最大值。

;9.10_1.asm   9.10 编程练习     *1.改进Str_copy过程
;本章给出的Str_copy过程没有限制被复制字符的数量。
;编写新过程(名为Str_copyN)再接收一个输入参数,表示被复制字符数的最大值。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
src BYTE "Good night Tom1",0
tgt BYTE "hello", 24 DUP(0), 0		;30字节空间.code 
;-------------------------------------------
;将字符串从源串复制到目的串。
;要求:目标串必须有足够空间容纳从源复制来的串。
;返回:无
;--------------------------------------------
Str_copyN PROC USES eax ecx esi edi,source:PTR BYTE,				;source stringtarget:PTR BYTE,				;target stringcopyCount:DWORD					;复制的字符数mov ecx, copyCount				;重复计数器mov esi, sourcemov edi, target;把edi定位到tgt字符串的最后位置
L1:	mov al, [edi] cmp al, 0je L2inc edijmp L1L2: cld								;方向为正向rep movsb						;复制宇符串 [esi]=>[edi]  source复制到target尾部ret
Str_copyN ENDPmain PROCmov ebx, LENGTHOF tgt			;30字节空间INVOKE Str_copyN, ADDR src, ADDR tgt, 10nopINVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

复制后:

** 2.Str_concat 过程

编写过程 Str_concat 将源串连接到目的串的末尾。目的串必须有足够的空间来容纳新字符。传递源串和目的串的指针。示例调用如下所示:

.data
targetStr BYT "ABCDE", 10 DUP(0)
sourceStr BYTE "FGH",0
.code
INVOKE Str_concat, ADDR targetStr, ADDR scourceStr

完整的代码如下:

;9.10_2.asm   9.10 编程练习    ** 2.Str_concat 过程
;编写过程 Str_concat 将源串连接到目的串的末尾。
;目的串必须有足够的空间来容纳新字符。传递源串和目的串的指针。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
targetStr BYTE "ABCDE", 10 DUP(0),0
sourceStr BYTE "FGH",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
;-------------------------------------------
;将字符串从源串复制到目的串。
;要求:目标串必须有足够空间容纳从源复制来的串。
;返回:无
;--------------------------------------------
Str_concat PROC USES eax ecx esi edi,target:PTR BYTE,				 source:PTR BYTE					 INVOKE Str_length, target		;获取字符串的长度,保存在EAX中mov edi, target;把edi定位到tgt字符串的最后位置add edi, eaxmov esi, sourceINVOKE Str_length, source		;获取字符串的长度,保存在EAX中mov ecx, eax					;重复计数器cld								;方向为正向rep movsb						;复制宇符串 [esi]=>[edi]  source复制到target尾部ret
Str_concat ENDPmain PROCmov ebx, LENGTHOF targetStr			;30字节空间INVOKE Str_concat, ADDR targetStr, ADDR sourceStrnopINVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

连接后:

**3.Str_remove 过程

编写过程 Str_remove从字符串中删除n个字符。需传递参数为:被删除字符在串中的位置指针,以及定义删除字符数量的整数。例如,下面的代码展示了如何从target中删除“xxxx”:

.data
target BYTE "abcxxxxdefghijklmop",0
.code
INVOKE Str_remove, ADDR [target+3], 4

完整的代码:

;9.10_3.asm   9.10 编程练习    **3.Str_remove 过程
;编写过程 Str_remove从字符串中删除n个字符。需传递参数为:被删除字符在串中的位置指针,
;以及定义删除字符数量的整数。例如,下面的代码展示了如何从target中删除“xxxx”:.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
target BYTE "abcxxxxdefghijklmop",0.code 
;-------------------------------------------
;从字符串中删除n个字符
;接收:删除的起始位置,删除的个数
;返回:无
;--------------------------------------------
Str_remove PROC USES eax ecx esi edi,tgt:PTR BYTE,					;target stringremoveCount:DWORD				;移除的字符数mov ecx, removeCount			;重复计数器mov edi, tgt					;起始位置mov esi, tgtadd esi, removeCount;[esi]==>[edi];cld							;方向为正向	;rep movsb						;[edi+removeCount]=>[edi]  字符串前移abcdefgdefghijklmop ;abcdefghijklmoplmop
L1:	mov al, [esi] cmp al, 0je L2mov [edi],alinc esiinc edijmp L1;abcdefghijklmoplmop=====>abcdefghijklmop 
L2: dec esimov BYTE PTR [esi], 0loop L2ret
Str_remove ENDPmain PROCmov ebx, LENGTHOF target	INVOKE Str_remove, ADDR target+3, 4nopINVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

删除后:

***4.Str_find 过程

编写过程 Str_find在目的串中查找第一次出现的源串,并返回其位置。输入参数为源串指针和目的串指针。如果查找成功,过程将零标志位ZF置1,用EAX指向目的串的匹配位置。否则,ZF清零,EAX无定义。例如,下面的代码查找“ABC”,并用EAX返回“A”在目的串中的位置:

.data
target BYTE "123ABC342432", 0
source BYTE "ABC", 0
pos DWORD ?
.code
INVOKE Str_find, ADDR source, ADDR target
jnz notFound
mov pos, eax			;保存位置值

完整的代码如下:

;9.10_4.asm   9.10 编程练习    ***4.Str_find 过程
;编写过程Str_find在目的串中查找第一次出现的源串,并返回其位置。
;输入参数为源串指针和目的串指针。如果查找成功,过程将零标志位ZF置1,
;用EAX指向目的串的匹配位置。否则,ZF清零,EAX无定义.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
target BYTE "123ABC342432", 0
source BYTE "ABC", 0
pos DWORD ?.code 
;-----------------------------------------------------
;从目标串中查找源串。
;接收: source:源字符串的指针,target:目的字符串的指针
;返回: 返回的位置保存到EAX中
;-----------------------------------------------------
Str_find PROC USES ecx esi edi,src:PTR BYTE,					;source stringtgt:PTR BYTE					;target stringmov edi, tgtmov esi, srcmov ebx, 0;查找
L1:	mov al, [esi + ebx] cmp [edi], alje L2							;相等	inc edi							;不相等,查找下一个字符mov al, [edi]cmp al, 0je L4							;目标字符串到结尾,没有找到子串mov ebx, 0						;子串从初始位置开始jmp L1L2: inc ediinc ebxmov al, [esi+ebx]cmp al, 0je L3							;子串在目标字符串中查找结果mov al, [edi]cmp al, 0je L4							;目标字符串到结尾,没有找到子串jmp L1 L3:	;查找成功,将零标志位ZF置1xor eax, eax					;将寄存器与自身异或,结果为 0,ZF=1mov eax, ebx					;保存位置到eax中jmp quit
L4:	;没有找到子串,将零标志位ZF置0or eax, 0FFFFFFFEh	
quit:ret
Str_find ENDPmain PROCmov ebx, LENGTHOF target			 INVOKE Str_find, ADDR source, ADDR targetjnz notFoundmov pos, eax			;保存位置值
notFound:nopINVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

把子串改为“ABCD”,再次运行调试测试:

**5.Str_nextWord 过程

编写过程Str_nextWord扫描字符串,查找第一次出现的指定分隔符,并将其替换为空字节输入参数有两个:字符串指针和分隔符。调用之后,如果发现分隔符,零标志位ZF置1,EAX为分隔符下一个字符的偏移量。否则,ZF清零,EAX无定义。下面的示例代码传递了target的指针和分隔符“,”:

.data
target BYTE "Johnson,Calvin",0
.code
INVOKE Str_nextWord, ADDR target, ','
jnz notFount

如图9-5所示,调用StrnextWord后,EAX指向了被发现(并替换)的“,”后面的那个字符。

;9.10_5.asm   9.10 编程练习    **5.Str_nextWord 过程
;编写过程Str_nextWord扫描字符串,查找第一次出现的指定分隔符,
;并将其替换为空字节输入参数有两个:字符串指针和分隔符。
;调用之后,如果发现分隔符,零标志位ZF置1,EAX为分隔符下一个字符的偏移量。
;否则,ZF清零,EAX无定义。下面的示例代码传递了target的指针和分隔符“,”:.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
target BYTE "Johnson,Calvin",0
pos DWORD ?.code 
;-----------------------------------------------------
;查找第一次出现的指定分隔符,并将其替换为空字节输入参数有两个:字符串指针和分隔符。
;接收: tgt:目的字符串的指针, 
;返回: 返回的位置保存到EAX中
;-----------------------------------------------------
Str_nextWord PROC, tgt:PTR BYTE,					;target stringval:BYTEmov edi, tgt;查找
L1:	mov al, [edi] cmp al, 0je L4							;到字符串末尾了还没有找到分隔符cmp al, valje L2							;相等跳转到L2	inc edi							;不相等,查找下一个字符jmp L1L2: mov BYTE PTR [edi], ' '			;将分隔符替换为空字符inc edi;查找成功,将零标志位ZF置1xor eax, eax					;将寄存器与自身异或,结果为 0,ZF=1jmp quit
L4:	;没有找到子串,将零标志位ZF置0or eax, 0FFFFFFFEh	
quit:ret
Str_nextWord ENDPmain PROCmov ebx, LENGTHOF target			 INVOKE Str_nextWord, ADDR target, '?'jnz notFoundmovzx eax, BYTE PTR [edi]				;eax指向分隔符的下一个字符
notFound:nopINVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

把分隔符换成?号测试:

**6.构建频率表

编写过程 Get_frequencies构建一个字符频率表。需向过程输人字符串指针,以及一个数组指针,该数组包含 256个双字,并已初始化为全0。每个数组位置都由其对应字符的ASCII码进行索引。过程返回时,数组中的每一项包含的是对应字符在串中出现的次数。例如,

.data
target BYTE "AAEBDCFBBC", 0
freqTable DWORD 256 DUP(0)
.code
INVOKE Get_frequencies, ADDR target, ADDR freqTable

图9-6展示了一个字符串和频率表的表项41(十六进制)到4B。位置41包含数值2,原因是字母A(ASCII码为41h)在字符串中出现了两次。其他字符也进行相同的计数。频率表常用于数据压缩和其他涉及字符处理的应用。例如,哈夫曼(Huffman)编码算法中,与较不常用的字符相比,最常出现字符的保存位数更少。

;9.10_6.asm   9.10 编程练习     **6.构建频率表
;编写过程 Get_frequencies构建一个字符频率表。需向过程输人字符串指针,
;以及一个数组指针,该数组包含 256个双字,并已初始化为全0。
;每个数组位置都由其对应字符的ASCII码进行索引。过程返回时,
;数组中的每一项包含的是对应字符在串中出现的次数。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
target BYTE "AAEBDCFBBC", 0
freqTable DWORD 256 DUP(0).code 
;-------------------------------------------
;构建一个字符频率表
;接收:目标字符串, 频率表
;返回:无
;--------------------------------------------
Get_frequencies PROC USES ebx esi edi,tgt:PTR BYTE,					;target stringfreq:PTR BYTE					;频率表mov esi, tgtmov edi, freq;根据[esi]中的值为索引,找频率表的位置
L1:	movzx ebx, BYTE PTR [esi]		;esi里的值为频率表索引值cmp ebx, 0je L2							;等于0表示字符串结束shl ebx, 2						;相当于ebx * 4, 频率表的位置是32位的	inc DWORD PTR[edi+ebx]			;[edi + index*4]inc esijmp L1
L2:ret
Get_frequencies ENDPmain PROCmov ebx, LENGTHOF target			 INVOKE Get_frequencies, ADDR target, ADDR freqTablenopINVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

调用后:

***7.厄拉多塞过滤算法

厄拉多塞(Eratosthenes)过滤算法,由同名的希腊数学家发明,提供了在给定范围内快速查找所有质数的方法。该算法创建一个字节数组,并按如下方式在“被标记”位置上插入1:从位置2(2是质数)开始,则数组中所有2的倍数的位置都插入1。接着,对下一个质数3,用同样的方法处理3的倍数。查找3之后的质数,该数为5,再对所有5的倍数的位置进行标记。持续这种操作直到找出质数的全部倍数。那么,剩下数组中没有被标记的位置就表示其数为质数。编写程序,创建一个含有65000个元素的数组,并显示2到65000之间的所有质数。要求,在未初始化数据段中声明该数组(参见3.4.1节),且使用STOSB把0填充到数组中。

;9.10_7.asm   9.10 编程练习    ***7.厄拉多塞过滤算法;.386
;.model flat, stdcall
;.stack 4096
;ExitProcess PROTO,dwExitCode:DWORDINCLUDE Irvine32.inc.data
src BYTE 65000 DUP(?)					;65000字节空间.code 
;-------------------------------------------
;厄拉多塞(Eratosthenes)过滤算法标记素数。
;接收:source:字节数指针, count数组元素个数。
;返回:无
;--------------------------------------------
Eratosthenes PROC USES ebx ecx esi edi,source:PTR BYTE,count:DWORDmov edi, source						;数组的指针mov ecx, count						;元素个数mov ebx, 2							;质数从2开始mov esi, 2L1:mov bl, BYTE PTR[edi + esi]cmp bl, 1							;被标记过了,不是素数je next								;判断下一个数mov ebx, 2							;从2开始判断是否有被整除的L2:cmp ebx, esije prime							;相等说明前面都没有被整除,该数是素数mov edx, 0							;高位清0mov eax, esi						;被除数放在eax中div ebx								;商在eax中,余数在edx中cmp edx, 0je next								;等于0, 有被整除,不是素数,检查下一个数cmp ebx, esijl next2							;ebx比esi小,再除下一个数jmp nextnext2:inc ebxjmp L2prime:mov eax, esi						;打印素数call WriteDec   call Crlf  换行;在数组中标记素数mov BYTE PTR [eax + edi], 0;eax的倍数都不是素数,要标记为1
L3:	add eax, esicmp eax, count						;判断有没有超出范围ja next								;大于跳转到nextmov BYTE PTR [eax + edi], 1			;标记为1jmp L3next:inc esiloop L1ret
Eratosthenes ENDPmain PROC;初始化字符串mov al, 0							;要保存的数值mov edi, OFFSET src					;EDI指向目标字符串mov ecx, LENGTHOF src				;字符计数器cld									;方向为正rep stosb							;用AL的内容实现填充INVOKE Eratosthenes, ADDR src, LENGTHOF src;打印素数, 从2开始mov esi, OFFSET srcmov ecx, LENGTHOF srcsub ecx, 2mov ebx, 2mov edi, 0
prime:movzx eax, BYTE PTR [esi+ebx]	cmp eax, 1je nextmov eax, ebxcall WriteDec ;一行输出12个质数cmp edi, 12je L1mov al, 9							;输出tab符call WriteCharjmp L2
L1:call Crlf							;换行mov edi, 0jmp next
L2:inc edi	next:inc ebxloop primecall CrlfINVOKE ExitProcess,0
main ENDP
END main

运行调试:

调用Eratosthenes过程后,标记素数:

输出素数:

*8.冒泡排序

向 9.5.1节的 BubbleSort 过程添加一个变量,进行内循环时,只要有一对数值交换就将其置1.若在某次遍历过程中发现没有数值交换,就在过程正常结束之前,利用该变量提前退出。(该变量通常被称为交换标志(exchange flag)。)

;9.10_8.asm   9.10 编程练习    *8.冒泡排序.386
.model flat, stdcall
.stack 4096BubbleSort PROTO, pArray:PTR DWORD, Count:DWORD, isChange:PTR DWORD
ExitProcess PROTO,dwExitCode:DWORD.data
tableD DWORD 100h, 20h, 3030h, 402h, 50h, 80h, 6070h, 6020h
isXchg DWORD 0.code 
main PROCmov esi, OFFSET tableDINVOKE BubbleSort, OFFSET tableD, LENGTHOF tableD, OFFSET isXchgINVOKE ExitProcess, 0
main ENDP 
;------------------------------------------
;使用冒泡算法,将一个32位有符号整数数组按升序进行排列
;接收:数组指针,数组大小, 交换判断标志
;返回:无
;------------------------------------------
BubbleSort PROC USES eax ecx esi,pArray:PTR DWORD,						;数组指针Count:DWORD,							;数组大小isChange:PTR DWORD						;用来判断内循环是否有交换mov ecx, Countdec ecx									;计数值减1
L1:	push ecx								;保存外循环计数值mov esi, pArray							;指向第一个数值
L2:	mov eax, [esi]							;取数组元素值cmp [esi + 4], eax						;比较两个数值jg L3									;如果[ESI]<=[ESI+4],不交换xchg eax, [esi+4]						;交换两数mov [esi], eaxmov [isChange], 1
L3:	add esi,4								;两个指针都向前移动一个元素loop L2									;内循环pop ecx									;恢复外循环计数值cmp [isChange], 0						;判断内循环是否有交换je L4									;没有交互,结束循环,说明已经排好序loop L1									;若计数值不等于0,则继续外循环
L4:	ret
BubbleSort ENDP
END main

运行调试:

排序后:

**9.对半查找

重新编写本章给出的对半查找过程,用寄存器来表示mid、first 和last。添加注释说明寄存器的用法。

;9.10_9.asm   9.10 编程练习    **9.对半查找
;重新编写本章给出的对半查找过程,用寄存器来表示mid、first 和last。添加注释说明寄存器的用法。;.386
;.model flat, stdcall
;.stack 4096INCLUDE Irvine32.inc
.data
array DWORD 12h, 30h, 52h, 64h, 78h, 93h, 97h.code 
;------------------------------------------
;BinarySearch
;在一个有符号整数数组中查找某个数值。
;接收:数组指针、数组大小、给定查找数值
;返回:若发现匹配项,则EAX=该匹配元素在数组中的位置;否则,EAX=-1。
;------------------------------------------
BinarySearch PROC USES ebx edx esi edi,pArray:PTR DWORD,				;数组指针Count:DWORD,					;数组大小searchVal:DWORD					;给定查找数值mov esi, pArraymov ecx, Countmov ebx, 0						;ebx=first=0(数组起始索引);mov edx, Count					 ;dec edx						;edx = last = Count-1 (数组结束索引)lea edx, [ecx - 1]				;返回间接操作数的地址 其原理返回实现的有效地址 7 - 1 = 6;lea edx, [ecx]					;这里有个现象,如果ecx是值,返回的是这个ecx的值, 7, 其实质是[ecx+0];lea edx, [esi]					;如果esi是地址,返回的就是esi的址址 00404000h   其实质是[esi+0] esi本身的值是00404000h;lea edx, [50h]					;edx = 50h;lea edx, [00404060h]			;edx = 00404060hsearchLoop:cmp ebx, edx					;比较first和lastjg	notFount					;如果first>last,未找到;计算mid = (first + last) / 2mov edi, ebx					;edi=firstadd edi, edx					;edi=first+lastshr edi, 1						;edi=(first+last)/2;比较array[mid]和搜索值mov ecx, [esi + edi*4]			;ecx = array[mid] 数组元素为DWORD型cmp eax, ecxje found						;相等,找到元素jl searchLeft					;如果搜索值 < array[mid],搜索左半部分;否则搜索右半部分
searchRight:lea ebx, [edi+1]				; first = mid + 1jmp searchLoop
searchLeft:lea edx, [edi - 1]				; last = mid - 1jmp searchLoopfound:mov eax,edi						;返回找到的索引(mid)jmp done						;返回 mid
notFount:	mov eax,-1						;查找失败
done:	ret
BinarySearch ENDPmain PROCmov eax, 52h;实现对半查找   用EAX返回INVOKE BinarySearch, ADDR array, LENGTHOF array, eaxnopINVOKE ExitProcess, 0
main ENDP END main

运行调试:

***10.字母矩阵

编写过程生成一个4x4的矩阵,矩阵元素为随机选择的大写字母。选择字母时,必须保证被选字母是元音的概率为 50%。编写测试程序,用循环调用该过程5次,并在控制台窗口显示所有矩阵。前三次矩阵的示例输出如下所示:

元音字母有五个,分别是 ‌a、e、i、o、u‌。

原理,先用RandomRange生成元音或辅音的随机会,再根据随机数,生成元音的随机数,或辅音的随机会。如果随机数为0,调用RandomRange生成元音随机数,如果随机数为1生成辅音的随机数。

RandomRange过程在范围0~n-1内生成一个随机整数,其中n是用EAX寄存器传递的输入参数。生成的随机数也用EAX返回。

;9.10_10.asm   9.10 编程练习    ***10.字母矩阵
;编写过程生成一个4x4的矩阵,矩阵元素为随机选择的大写字母。
;选择字母时,必须保证被选字母是元音的概率为 50%。
;编写测试程序,用循环调用该过程5次,并在控制台窗口显示所有矩阵。INCLUDE Irvine32.inc.data
matrixSize equ 16							;4x4 matrix
vowels BYTE 'AEIOU', 0						;元音列表
consonants BYTE 'BCDFGHJKLMNPQRSTVWXYZ', 0	;辅音列表
matrix BYTE 16 dup(0)						;存储生成的矩阵.code 
;-------------------------------------------
;生成矩阵
;要求:目标串必须有足够空间容纳从源复制来的串。
;返回:无
;--------------------------------------------
GenerateMatrix PROC USES eax ebx ecx edx esi edi,source:PTR BYTE,				    ;source stringvowel:PTR BYTE,					    ;元音列表consonant:PTR BYTE,				    ;辅音列表copyCount:DWORD					    ;复制的字符数mov ecx, copyCount				    ;重复计数器mov esi, sourcemov edi, 0mov ebx, 0;生成随机数
L1:	mov eax, 2							;生成的范围在0或1call RandomRange					;生成的随机数保存在eax中cmp eax, 0						je L2								;如果eax==0,接下来生成随机数,取元音字符;否则取辅音字母,辅音字母有21个,因此序号在0~20mov eax, 21							;设置随机数范围call RandomRange					;生成随机数0~20;mov al, BYTE PTR consonants[eax]	;取辅音字母 mov ebx, consonantmov al, BYTE PTR[ebx+eax]mov BYTE PTR[esi+edi], al			;把辅音字母存入矩阵中jmp nextL2: ;元音字母只有5个,因此序号在0~4mov eax, 5							;设置随机数范围call RandomRange					;生成随机数0~4;mov al, BYTE PTR vowels[eax]		;取元音字母 mov ebx, vowelmov al, BYTE PTR[ebx+eax]mov BYTE PTR [esi+edi], al			;把元音字母存入矩阵中[esi+edi]next:inc ediloop L1;打印4 * 4矩阵mov ecx, copyCountmov ebx, 0
L3:mov al, BYTE PTR[esi+ebx]call WriteChar inc ebxmov edx, 0mov eax, ebxmov edi, 4div edi							;余数在edx中cmp edx, 0je L4mov al, 9						;tab符号call WriteCharjmp nextChar
L4:call Crlf						;换行
nextChar:loop L3ret
GenerateMatrix ENDPmain PROCmov ecx, 5mov ebx, LENGTHOF matrix	
L1:INVOKE GenerateMatrix, ADDR matrix, ADDR vowels, ADDR consonants, matrixSizecall Crlfloop L1nopINVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

生成矩阵:

输出:

****11.字母矩阵/按元音分组

本程序以上一道编程练习生成的字母矩阵为基础。生成一个4x4的字母矩阵,其中每个字母都有 50%的概率为元音字母。遍历矩阵的每一行,每一列,和每个对角线,并产生字母组。当一组四个字母中只有两个元音字母时,显示该字母组。比如,假设生成矩阵如下所示:

则程序应显示的四字母组为POAZ、GKAE、IAGD、PAGI、ZUED、PEAD和ZAKI。各组内字母的顺序并不重要。

;9.10_11.asm   9.10 编程练习    ****11.字母矩阵/按元音分组
;本程序以上一道编程练习生成的字母矩阵为基础。生成一个4x4的字母矩阵,
;其中每个字母都有 50%的概率为元音字母。遍历矩阵的每一行,每一列,和每个对角线,
;并产生字母组。当一组四个字母中只有两个元音字母时,显示该字母组。INCLUDE Irvine32.inc.data
matrixSize equ 16							;4x4 matrix
vowels BYTE 'AEIOU', 0						;元音列表
consonants BYTE 'BCDFGHJKLMNPQRSTVWXYZ', 0	;辅音列表
matrix BYTE 16 dup(0),0						;存储生成的矩阵
tempList BYTE 4 DUP(?), 0					;临时列表.code 
;-------------------------------------------
;生成矩阵
;要求:目标串必须有足够空间容纳从源复制来的串。
;返回:无
;--------------------------------------------
GenerateMatrix PROC USES eax ebx ecx edx esi edi,source:PTR BYTE,					;source stringvowel:PTR BYTE,						;元音列表consonant:PTR BYTE,					;辅音列表copyCount:DWORD						;复制的字符数mov ecx, copyCount					;重复计数器mov esi, sourcemov edi, 0mov ebx, 0;生成随机数
L1:	mov eax, 2							;生成的范围在0或1call RandomRange					;生成的随机数保存在eax中cmp eax, 0						je L2								;如果eax==0,接下来生成随机数,取元音字符;否则取辅音字母,辅音字母有21个,因此序号在0~20mov eax, 21							;设置随机数范围call RandomRange					;生成随机数0~20;mov al, BYTE PTR consonants[eax]	;取辅音字母 mov ebx, consonantmov al, BYTE PTR[ebx+eax]mov BYTE PTR[esi+edi], al			;把辅音字母存入矩阵中jmp nextL2: ;元音字母只有5个,因此序号在0~4mov eax, 5							;设置随机数范围call RandomRange					;生成随机数0~4;mov al, BYTE PTR vowels[eax]		;取元音字母 mov ebx, vowelmov al, BYTE PTR[ebx+eax]mov BYTE PTR [esi+edi], al			;把元音字母存入矩阵中[esi+edi]next:inc ediloop L1;ret									;返回,下面的打印输出不执行;打印4 * 4矩阵mov ecx, copyCountmov ebx, 0
L3:mov al, BYTE PTR[esi+ebx]call WriteChar inc ebxmov edx, 0mov eax, ebxmov edi, 4div edi							;余数在edx中cmp edx, 0je L4							;4个数换一行mov al, 9						;tab符号call WriteCharjmp nextChar
L4:call Crlf						;换行
nextChar:loop L3ret
GenerateMatrix ENDP
;查找有2个元音字母, 就打印
FindVowel PROC USES ebx ecx edi esi,	vowel:PTR BYTE,					;元音列表count:DWORDmov esi, vowelmov ecx, countmov edi, offset tempListmov eax, 0
L1:	push ecxmov ebx, 0mov dl, BYTE PTR [edi]			;取列表的字母mov ecx, 5
L2:	cmp dl, BYTE PTR [esi+ebx]jne nextVowel					;不相等对比下一个元音字母inc eax							;相待,统计数量jmp nextCharnextVowel:inc ebxloop L2nextChar:pop ecxinc edi							;下一个字母比较loop L1cmp eax, 2jne quit						;不等于2就退出mov edx, offset tempList		;等于2打打印4个字母call WriteStringcall Crlf
quit:ret
FindVowel ENDPVowelArray PROC USES eax ebx ecx edi esi,source:PTR BYTE					;矩阵	mov esi, source;遍历行mov ecx, 4mov edi, OFFSET tempList
rowL1:	push ecxmov ebx, 0mov ecx, 4
rowL2:mov al, BYTE PTR [esi+ebx]mov BYTE PTR [edi+ebx], al		;把一行字母都放到tempListinc ebxloop rowL2;1行数据4个字母存放完成INVOKE FindVowel, ADDR vowels, 5add esi,4						;下一行数pop ecxloop rowL1call Crlf;遍历列mov esi, sourcemov ecx, 4colL1:	push ecxmov ebx, 0mov ecx, 4mov edi, OFFSET tempList
colL2:mov al, BYTE PTR [esi+ebx]mov BYTE PTR [edi], al			;把一行字母都放到tempListadd ebx, 4inc ediloop colL2;1列数据4个字母存放完成INVOKE FindVowel, ADDR vowels, 5add esi,1						;下一列数pop ecxloop colL1call Crlf;遍历对角线 ;matrix[0][0], matrix[1][1], matrix[2][2], matrix[3][3]mov ecx, 4mov ebx, 0mov esi, sourcemov edi, OFFSET tempList
L1:mov al, BYTE PTR [esi+ebx]mov BYTE PTR [edi+ebx], alinc ebxadd esi, 4loop L1INVOKE FindVowel, ADDR vowels, 5;matrix[0][3], matrix[1][2], matrix[2][1], matrix[3][0]mov ecx, 4mov ebx, 3mov esi, sourcemov edi, OFFSET tempList
L2:mov al, BYTE PTR [esi+ebx]mov BYTE PTR [edi+ebx], aldec ebxadd esi, 4loop L2INVOKE FindVowel, ADDR vowels, 5ret
VowelArray ENDPmain PROCmov ecx, 1mov ebx, LENGTHOF matrix	
L1:INVOKE GenerateMatrix, ADDR matrix, ADDR vowels, ADDR consonants, matrixSizecall Crlfloop L1nopINVOKE VowelArray, ADDR matrixINVOKE ExitProcess, 0
main ENDP 
END main

 运行结果:

**** 12.数组行求和

编写程序 calc_row_sum计算二维的字节数组、字数组或双字数组中单行的总和。过程需有如下堆栈参数:数组偏移量、行大小、数组类型、行索引。返回的和数必须在EAX中。要求使用不能用INVOKE或扩展的PROC。编写程序,分别用字节数组、字数组和双字数显式堆栈参数,组来测试过程。要求用户输人行索引,并显示被选择行的和数。

;9.10_12.asm   9.10 编程练习    **** 12.数组行求和
;编写程序 calc_row_sum计算二维的字节数组、字数组或双字数组中单行的总和。
;过程需有如下堆栈参数:数组偏移量、行大小、数组类型、行索引。
;返回的和数必须在EAX中。要求使用不能用INVOKE或扩展的PROC。
;编写程序,分别用字节数组、字数组和双字数显式堆栈参数,组来测试过程。
;要求用户输人行索引,并显示被选择行的和数。INCLUDE Irvine32.inc.data; 测试数组byteArray BYTE 1, 2, 3, 4BYTE 5, 6, 7, 8BYTE 9, 0Ah, 0Bh, 0ChbyteRows = 3byteCols = 4wordArray WORD 10h, 20h, 30h, 40hWORD 50h, 60h, 70h, 80hWORD 90h, 100h, 110h, 120hwordRows = 3wordCols = 4dwordArray DWORD 100h, 200h, 300h, 400hDWORD 500h, 600h, 700h, 800hDWORD 900h, 1000h, 1100h, 1200hdwordRows = 3dwordCols = 4prompt1 BYTE "Enter row index (0-based): ", 0prompt2 BYTE "Sum of selected row: ", 0typePrompt BYTE "Testing array type: ", 0byteType BYTE "BYTE", 0wordType BYTE "WORD", 0dwordType BYTE "DWORD", 0.code
main PROC; 测试字节数组mov edx, OFFSET typePromptcall WriteStringmov edx, OFFSET byteTypecall WriteStringcall Crlfcall GetUserInputpush eax                ; 行索引push 1                  ; 数组类型: 1=BYTEpush byteCols           ; 行大小(列数)push OFFSET byteArray   ; 数组偏移量call calc_row_sumcall DisplayResult; 测试字数组call Crlfmov edx, OFFSET typePromptcall WriteStringmov edx, OFFSET wordTypecall WriteStringcall Crlfcall GetUserInputpush eax                ; 行索引push 2                  ; 数组类型: 2=WORDpush wordCols           ; 行大小(列数)push OFFSET wordArray   ; 数组偏移量call calc_row_sumcall DisplayResult; 测试双字数组call Crlfmov edx, OFFSET typePromptcall WriteStringmov edx, OFFSET dwordTypecall WriteStringcall Crlfcall GetUserInputpush eax                ; 行索引push 4                  ; 数组类型: 4=DWORDpush dwordCols          ; 行大小(列数)push OFFSET dwordArray  ; 数组偏移量call calc_row_sumcall DisplayResultINVOKE ExitProcess, 0
main ENDP; 获取用户输入的行索引
GetUserInput PROCmov edx, OFFSET prompt1call WriteStringcall ReadIntret
GetUserInput ENDP; 显示结果
DisplayResult PROCmov edx, OFFSET prompt2call WriteStringcall WriteIntcall Crlfret
DisplayResult ENDP; 计算行总和的函数
; 参数顺序(从最后一个开始push):
; 1. 数组偏移量
; 2. 行大小(列数)
; 3. 数组类型(1=BYTE, 2=WORD, 4=DWORD)
; 4. 行索引(0-based)
calc_row_sum PROCpush ebpmov ebp, esppush ebx                ; 保存寄存器push ecxpush edxpush esimov esi, [ebp+8]        ; 数组偏移量mov ecx, [ebp+12]       ; 行大小(列数)mov edx, [ebp+16]       ; 数组类型mov eax, [ebp+20]       ; 行索引; 计算行起始地址 = 数组基址 + (行索引 * 行大小 * 元素大小)imul eax, ecx           ; 行索引 * 列数imul eax, edx           ; 再乘以元素大小add esi, eax            ; ESI现在指向行的起始地址xor eax, eax            ; 清空EAX用于累加和; 根据数组类型选择循环处理方式cmp edx, 1je sum_bytescmp edx, 2je sum_wordscmp edx, 4je sum_dwordssum_bytes:movsx ebx, BYTE PTR [esi]add eax, ebxadd esi, 1loop sum_bytesjmp donesum_words:mov bx, WORD PTR [esi]add eax, ebxadd esi, 2loop sum_wordsjmp donesum_dwords:add eax, DWORD PTR [esi]add esi, 4loop sum_dwordsdone:pop esi                 ; 恢复寄存器pop edxpop ecxpop ebxpop ebpret 16                  ; 清理16字节的堆栈参数
calc_row_sum ENDPEND main

运行调试:

***13.裁剪前导字符

编写Str_trim过程的变体,使得主调程序能从字符串中删除所有的前导字符。比如,若调用过程时,有一指针指向字符串“###ABC”,且向过程传递了字符“#”,则结果字符串应为“ABC”。

;9.10_13.asm   9.10 编程练习   ***13.裁剪前导字符
;编写Str_trim过程的变体,使得主调程序能从字符串中删除所有的前导字符。
;比如,若调用过程时,有一指针指向字符串“###ABC”,
;且向过程传递了字符“#”,则结果字符串应为“ABC”。INCLUDE Irvine32.inc.data
string_1 BYTE "###Hello",0
string_2 BYTE "###Hello#World",0
string_3 BYTE "Hello",0
string_4 BYTE "###H@$#&ello#",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 ebx ecx edi,pStr:PTR BYTE,					;指向字符串char:BYTE						;要移除的字符mov edi, pStr					;数组指针INVOKE StrLengthA, edi			;用EAX返回长度cmp eax, 0						;长度是否为零?je quit							;是:立刻退出mov esi, pStrmov ebx, 0
L1:	mov al, [edi+ebx]cmp al, char					;比较字符jne quitLoop					;不相等退出循环inc ebx							;比较下一个字符jmp L1;完成之后,如果前面有删除的字符,要把后面的字符整体前移
quitLoop:cmp ebx, 0je quit							;等于0,说明没有匹配上字符,退出mov ecx, ebx;整体左移,列如:###hello===>hellollo
L2:mov al,[edi+ebx]mov [esi], alcmp al,0						je L3							;说明已经到字符串尾,这个时候的字符串是hellolloinc esiinc edijmp L2;清除后面的字符hellollo===>hello 
L3:mov BYTE PTR[edi+ebx], 0dec ebxloop L3quit:	ret
StrTrim ENDPmain PROCmov esi, OFFSET string_1INVOKE 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

运行调试:

 

***14.去除一组字符

编写Str_trim 过程的变体,使得主调程序能从字符串末尾删除一组字符。比如,若调用过程时,有一指针指向字符串“ABC#S&”,且向过程传递了过滤字符数组“%#!;S&*”的指针,则结果字符串应为“ABC”。

;9.10_14.asm   9.10 编程练习    ***14.去除一组字符
;编写Str_trim 过程的变体,使得主调程序能从字符串末尾删除一组字符。
;比如,若调用过程时,有一指针指向字符串“ABC#S&”,
;且向过程传递了过滤字符数组“%#!;S&*”的指针,则结果字符串应为“ABC”。 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
mathchString BYTE '$%&#@', 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;------------------------------------------
;匹配分隔符
;返回:匹配上了eax=0,否则为任意值
;------------------------------------------
MatchDelimiter PROC USES ebx esi edi,pString1:PTR BYTE, char:BYTEmov esi, pString1
L1:mov bl, [esi]cmp bl, 0						;等于0表示到字符串末尾了je quitcmp bl, charjne L2							;不相等跳转到L2,比较下一个字符mov eax, 0						;相待,把eax赋为0,退出循环jmp quit
L2:inc esijmp L1
quit:ret
MatchDelimiter ENDP;------------------------------------------
;从字符串末尾删除所有与给定分隔符匹配的字符。
;返回:无
;------------------------------------------
StrTrim PROC USES eax ecx edi,pStr:PTR BYTE,					;指向字符串pMat:PTR 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					;是否为分隔符?INVOKE MatchDelimiter, pMat, al cmp al, 0						;匹配成功eax = 0jne L2							;否:插入空字节dec edi							;是:继续后退一个字符loop L1							;直到字符串的第一个字符
L2:	mov BYTE PTR [edi+1], 0			;插入一个空字节
L3:	ret
StrTrim ENDPmain PROCINVOKE ShowString, ADDR string_1INVOKE StrTrim, ADDR string_1, ADDR mathchStringINVOKE ShowString, ADDR string_1INVOKE ShowString, ADDR string_2INVOKE StrTrim, ADDR string_2, ADDR mathchStringINVOKE ShowString, ADDR string_2INVOKE ShowString, ADDR string_3INVOKE StrTrim, ADDR string_3, ADDR mathchStringINVOKE ShowString, ADDR string_3INVOKE ShowString, ADDR string_4INVOKE StrTrim, ADDR string_4, ADDR mathchStringINVOKE ShowString, ADDR string_4INVOKE ShowString, ADDR string_5INVOKE StrTrim, ADDR string_5, ADDR mathchStringINVOKE ShowString, ADDR string_5INVOKE ExitProcess, 0
main ENDP 
END main

运行调试:

http://www.xdnf.cn/news/16421.html

相关文章:

  • 新房装修是中央空调还是壁挂空调好?
  • 背包DP之完全背包
  • Agentic RAG理解和简易实现
  • UG创建的实体橘黄色实体怎么改颜色?
  • HCIP上HCIA复习静态综合实验
  • 【Java、C、C++、Python】飞机订票系统---文件版本
  • 基于springboot的小区车位租售管理系统
  • dart使用
  • 从入门到进阶:JavaScript 学习之路与实战技巧
  • C++学习笔记(十:类与对象基础)
  • 内存优化:从堆分配到零拷贝的终极重构
  • 【笔记】Handy Multi-Agent Tutorial 第四章: CAMEL框架下的RAG应用 (简介)
  • linux-开机启动流程
  • 蓝桥杯java算法例题
  • NOIP 模拟赛 7
  • ZYNQ芯片,SPI驱动开发自学全解析个人笔记【FPGA】【赛灵思
  • 同声传译新突破!字节跳动发布 Seed LiveInterpret 2.0
  • Win11批量部署神器winget
  • 滚珠导轨:手术机器人与影像设备的精密支撑
  • 升级目标API级别到35,以Android15为目标平台(三 View绑定篇)
  • 上位机程序开发基础介绍
  • Round-Robin仲裁器
  • 深入理解 BIO、NIO、AIO
  • RocketMQ学习系列之——客户端消息确认机制
  • jwt 在net9.0中做身份认证
  • [2025CVPR-图象分类方向]CATANet:用于轻量级图像超分辨率的高效内容感知标记聚合
  • C# WPF 实现读取文件夹中的PDF并显示其页数
  • 案例分享|告别传统PDA+便携打印机模式,快速实现高效率贴标
  • Class18卷积层的填充和步幅
  • uniapp之微信小程序标题对其右上角按钮胶囊