汇编语言初次尝试


作业-1:汇编语言

黄芃洋 2021212021


题目要求

问题-1

* 1.1 请结合程序上下文逻辑,分析当所实现的“排序算法”不能正确排序时,_check_flag 为零还是非零?

* 1.2 请分析 main函数的执行流程,画出其伪代码流程图

问题-2:请理解给定的do_swap汇编函数功能,写出等价的 C 语言函数代码;提示需注意数据类型,如长度、有无符号等(unsigned char ?无符号 1 Byte 长的变量?)。

问题-3请用汇编语言实现冒泡排序函数(需要有详尽的代码注释+运行结果截图);提示会调用到已给定的do_swap汇编函数

(提示会自动对所实现的冒泡排序函数进行重复测试,无需自行输入字符串;请注意回答上方的问题 1.2

以下是作答:

问题-1
1.1

当所实现的“排序算法”不能正确排序时,_check_flag 为零。我们可以用不同方法进行判断,分析如下:

  • 首先我们定位到_check_flag 的位置

    第一句汇编指令将 _ check_flag 一字节数据值存入eax寄存器并进行无符号扩展;第二、三句汇编指令用于检查al寄存器中的值、也就是_check_flag是否为0,如果不为0,那么ZF将被清除、jne指令也就将跳转到标记为continue_test的位置

    如果为0,那么就不跳转,继续执行接下来的指令,那么我们来看一下接下来的指令:

    根据上下文关系以及初始化数据段(.data)中的内容,我们可以知道上面的这段指令代表排序错误,并会输出“Sorry but try it again!”等字样,因此可以判断此时实现的“排序算法”不能正确排序,_check_flag 为零。

  • 当然我们也可以在标记为end_test_loop的位置判断出来,本段指令如下

    第一句汇编指令将 _ check_flag 一字节数据值存入eax寄存器并进行无符号扩展;第二、三句汇编指令用于检查al寄存器中的值、也就是_ check_flag是否为0,如果为0,那么ZF将被设置、je指令也就将跳转到标记为end_main的位置,而如果跳转到标志为end_main处,则程序立刻停止,因此显然,为_check_flag为0时“排序算法”不是正确排序

    同时我们来接着往下看如果_ check_flag非零的情况,根据上下文关系以及初始化数据段(.data)中的内容,我们可以知道上面的这段指令代表排序正确,并会输出“Well done!”等字样,因此可以判断此时实现的“排序算法”能正确排序。

  • 当然,我们也可以直接看到在#Function-3# 对排序结果得正确性进行检查的函数中:

    分析可知,当发现排序错误时,它会将 _ check_flag置零

    综上反复论证,我们可以得出结论:当所实现的“排序算法”不能正确排序时,_check_flag 为零

1.2

根据main函数的汇编指令可以得到其流程图如下:


问题-2

为了写出等价的c语言代码,首先我们来逐条分析并注释一下给定的do_swap汇编函数:

do_swap:                          
                                  
	push	ebp                
	mov	ebp, esp
	sub	esp, 4

	mov	eax, DWORD PTR [ebp+8]                #把位于内存地址ebp+8的4字节大小的值移入eax寄存器
	movzx	eax, BYTE PTR [eax]               #访问eax中存储的地址并将该地址中的一字节数据放入eax中并进行无符号位扩展,                                                此时eax中存储的是变量1的值
	mov	BYTE PTR [ebp-1], al                  #将变量1的值填入ebp-1的位置

	mov	eax, DWORD PTR [ebp+12]               #把位于内存地址ebp+12的四字节大小的值移入eax寄存器
	movzx	edx, BYTE PTR [eax]               #访问eax中存储的地址并将该地址中的一字节数据放入edx中并进行无符号位扩展,                                                此时edx中存储的是变量2的值
	mov	eax, DWORD PTR [ebp+8]                #把位于内存地址ebp+8的四字节大小的值移入eax寄存器
	mov	BYTE PTR [eax], dl                    #访问eax中存储的地址并将该地址中的一字节数据替换成dl中的值,也就相当于将变                                                量2的值存入变量1的位置

	mov	eax, DWORD PTR [ebp+12]               #把位于内存地址ebp+12的四字节大小的值移入eax寄存器
	movzx	edx, BYTE PTR [ebp-1]             #把位于ebp-1位置的一字节输入放入edx中进行无符号扩展,此时edx中存储的是变                                                量1的值
	mov	BYTE PTR [eax], dl                    #访问eax中存储的地址,并将该地址中一字节数据替换成dl中的值,也就相当于将变                                                量1的值存入变量2的位置

	leave
	ret

根据上面的分析,我们能够写出对应的C语言代码:

void do_swap(unsigned char *a, unsigned char *b) {
    unsigned char temp = *a;
    *a = *b;
    *b = temp;
}

问题-3

为解决该问题,我们首先可以用C语言实现此功能,用以确定逻辑结构,下面是我先写出的C语言代码(并且还附上了do_swap函数的c代码实现,并配套了main函数用以调用和试验此函数):

//bubble sort
#include<stdio.h>
void bubble( unsigned char *arr);
void do_swap(unsigned char *a, unsigned char *b);
int main(){
    unsigned char arr[]={'p','T','v','K','c','g','W','D','F','h'};
	int i;
	bubble(arr);
	for(i=0;i<10;i++){
		printf("%c",arr[i]);
	}	
}
void bubble( unsigned char *arr){
	int j,k;
	for(j=0;j<9;j++){
		for(k=0;k<9-j;k++){
			if(arr[k]<96){ //如果k大写 
				if(arr[k]>arr[k+1])do_swap(&arr[k],&arr[k+1]);//k+1也是大写且比k小 
				else if(arr[k+1]>96)do_swap(&arr[k],&arr[k+1]);//k+1是小写 
				else continue; //k+1是大写但是不比k小 
			} 
			else{//k是小写 
			    if(arr[k+1]>96){//k+1也是小写 
			        if(arr[k]>arr[k+1])do_swap(&arr[k],&arr[k+1]);
					else continue;	
				}
				else continue;
		    }
		}
   } 
}

void do_swap(unsigned char *a, unsigned char *b) {
    unsigned char temp = *a;
    *a = *b;
    *b = temp;
}

在确定了逻辑结构之后,使用汇编指令进行实现(我几乎为每一句指令都附上了注释):

#Function-4# 冒泡排序函数
do_bubble_sort:                          #*# 问题-3:请用汇编语言实现冒泡排序函数;
                                         #          提示会调用到已给定的do_swap汇编函数
	push	ebp
	mov	ebp, esp
	sub	esp, 16

### ------------------------------------冒泡排序正文----------------------------------------开始

mov     BYTE PTR[ebp-1], 0	
jmp     outerloop	
	

	
initialize:                       
mov       BYTE PTR[ebp-2], 0	           #对k进行初始化
jmp       innerloop	                       #进入内循环
	           

#-------------------------------------------------------------------
judgment:                            
movzx     edx, BYTE PTR[ebp-2]	              #将k值移入edx中
movzx     eax, byte ptr _char_list[edx]         #将arr[k]的值存入eax
add       edx,1		                       #k+1
movzx     ebx, byte ptr _char_list[edx]		#将arr[k+1]的值存入ebx	
cmp       al, 96		                        #将arr[k]和小写比较
ja        k_lower	                              #如果arr[k]是小写,则跳转到k_lower


k_capital:
cmp       al,bl                               #比较arr[k]和arr[k+1]
ja        swap                                #如果k>k+1 跳转swap

cmp       bl,96                               #将arr[k+1]和小写比较
ja        swap                                #k+1是小写 跳转swap
                                                                              
jmp       no_swap                             #其余跳转到 no_swap


k_lower:
cmp       bl,96                               #判断arr[k+1]是否为小写
jle       no_swap                             #如果bl是大写则跳转到no_swap

cmp       al,bl                               #是小写的话就来判断arr[k]和arr[k+1]的大小
ja        swap                                #k+1<k 跳转swap
                                                                       
jmp       no_swap                             #其余跳转到 no_swap


#-------------------------------------------------------------------
swap:
lea    eax,byte ptr _char_list[edx]           #将存储k+1的地址放入eax中
push   eax
sub    edx,1                                 #k
lea    eax,byte ptr _char_list[edx]           #将存储k的地址放入eax中
push   eax
call   do_swap
jmp   kadd


no_swap:
jmp  kadd



kadd:
movzx     edx, BYTE PTR[ebp-2]                 #将k值移入edx中
add       edx,1                                #k值+1
mov       BYTE PTR[ebp-2],dl                 #将新k值重新存入ebp-2位置
jmp       innerloop                           #跳转到内循环的入口 


#-------------------------------------------------------------------
innerloop:                        
mov     eax, 9
movzx   edx, BYTE PTR[ebp-2]                  #将k值移入edx中     	
movzx   ebx, BYTE PTR[ebp-1]                  #将j值移入edx中  
sub     eax, ebx	                            #9-j的值存入eax中
cmp     eax, edx	                            #对比9-j和k
ja      judgment                              #9-j>k就跳转
                                               #否则就进行j++
movzx     ecx, BYTE PTR[ebp-1]                 #将j值移入ecx中       
add       ecx,1                                #j值+1
mov      BYTE PTR[ebp-1],cl                       #将新j值重新存入ebp-1位置
                                               #并回到外循环
			
	
outerloop:   
movzx   ecx, BYTE PTR[ebp-1]                  #将j值移入ecx中         
cmp     ecx, 8	                              #判断j值与8的关系
jle     initialize	                      #若j小于等于8就进入内循环准备初始化

### ------------------------------------冒泡排序正文----------------------------------------结束

end_bubble_sort:
	leave
	ret

放入给到的程序中能得到如下的运行结果:

可知功能已经实现,汇编指令无误


文章作者: autumnwt
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 autumnwt !
  目录