[汇编版]冒泡排序、快速排序、堆排序
[汇编版]冒泡排序、快速排序、堆排序
刚刚看完<Intel汇编程序语言设计>一书,用汇编写了几个基本的排序算法。编写的汇编函数代码都是是使用stdcall调用规范的,所以C语言工程也可以链接并调用这些函数的,怎么调用以后在再文章说明。另外,我把程序的不同模块放在不同的源文件中,使代码结构更清晰。和C编译器生成的汇编代码不同的是,为了效率最佳,内存操作最少,我在函数中都没有定义局部变量,大量使用寄存器,刚好算法不是太复杂,寄存器够用,坏处是代码可读性比较差了。程序的编译环境是VS2008,关于如何配制编译环境,可以参看我之前的文章<用Visual Studio 2008编写Win32汇编程序>。算法都是些基本的算法,就不多加说明,只是在程序中加了少许注释。我把几个算法都放在一个工程中,下面是工程中的源文件和简单介绍。
Head.inc
BubbleSort.asm
QuickSort.asm
HeapSort.asm
PrintNumber.asm
main.asm
包含文件:Head.inc
包含文件类似于C/C++的头文件,我们可以把引用的库函数,或者是其他源文件中定义的函数的声明放在里面。和C语言一样,包含文件中的函数声明告诉编译器,在这个源文件中我们没有这个函数的实现,但是你可以使用它,在Link时可以在其他.obj文件中找到它的实现。(此外,和C语言一样,如果被调用的函数在源代码中的位置调用者之后,也要在调用者上面的位置声明被调用的函数),下面是源代码:
ExitProcess PROTO, dwExitCode:DWORD ;Windows API GetStdHandle PROTO, nStdHandle:DWORD ;Windows API WriteConsoleA PROTO,hConsoleOutput:DWORD, ;Windows API lpBuffer:PTR DWORD, nNumberOfCharsToWrite:DWORD, lpNumberOfCharsWritten:PTR DWORD, lpReserved:PTR DWORD PrintNumber PROTO, inputNumber:SDWORD BubbleSort PROTO, pArray:PTR SDWORD, len : SDWORD HeapSort PROTO, pArray : PTR SDWORD, len : SDWORD AdjustHeap PROTO, pArray : PTR SDWORD, len : SDWORD, rootIndex : SDWORD QuickSort PROTO, pArray : PTR SDWORD, len : SDWORD QuickSortRecursion PROTO,pArray:PTR SDWORD, headIndex : SDWORD, tailIndex : SDWORD
源文件:BubbleSort.asm
和C语言一样,一个汇编源文件可以不包括入口点函数,而仅包含一些库函数的实现。但你也不能把一个函数定义放里面就完事了,它需要包含完整的格式,包括:TITLE,.386,.MODEL,.stack,.data,.code等部分,当然其中有些字段是可选的,但是必须是一个完整的合法的格式,源文件在末尾以END结尾。和C语言一样,每个源文件都会编译成一个.obj文件,最后所有的.obj文件由Linker生成.exe或者.dll文件。代码如下:
TITLE Bubble Sort .386 .MODEL flat,stdcall INCLUDE Head.inc .code BubbleSort PROC USES eax ebx ecx edx esi edi, pArray:PTR SDWORD, len : SDWORD mov EAX,pArray mov EDX,len sub EDX,2 shl EDX,2 ;EAX=pArray,EDX=LastIndex-1,ESI=i,EDI=j L1: mov ESI,0 mov EDI,4 L2: mov EBX,[EAX+ESI] mov ECX,[EAX+EDI] cmp EBX,ECX jl L3 ;exchange two numbers mov [EAX+ESI],ECX mov [EAX+EDI],EBX L3: ADD ESI,4 ADD EDI,4 CMP ESI,EDX jle L2 sub EDX,4 cmp EDX,0 jge L1 ret BubbleSort ENDP END
源文件:QuickSort.asm
TITLE Quick Sort .386 .MODEL flat,stdcall INCLUDE Head.inc .code QuickSort PROC USES eax , pArray:PTR SDWORD, len : SDWORD mov eax,len dec eax INVOKE QuickSortRecursion,pArray,0,eax ret QuickSort ENDP QuickSortRecursion PROC USES eax ebx ecx esi edi, pArray : PTR SDWORD, headIndex : SDWORD, tailIndex : SDWORD mov EAX,pArray mov ESI,headIndex mov EDI,tailIndex mov EBX,[EAX+4*ESI] ;ESI = left pointer, EDI = right pointer, EBX= middle number L1: CMP ESI,EDI jge L4 L2: cmp ESI,EDI jge L4 cmp EBX,[EAX+4*EDI] jg E1 dec EDI jmp L2 E1: mov ECX,[EAX+4*EDI] mov [EAX+4*ESI],ECX inc ESI L3: cmp ESI,EDI jge L4 cmp EBX,[EAX+4*ESI] jl E2 inc ESI jmp L3 E2: mov ECX,[EAX+4*ESI] mov [EAX+4*EDI],ECX dec EDI jmp L1 L4: mov [EAX+4*ESI],EBX dec ESI inc EDI cmp ESI,headIndex jle L5 INVOKE QuickSortRecursion,pArray,headIndex,ESI L5: cmp EDI,tailIndex jge END1 INVOKE QuickSortRecursion,pArray,EDI,tailIndex END1: ret QuickSortRecursion ENDP END
源文件:HeapSort.asm
TITLE Heap Sort .386 .MODEL flat,stdcall INCLUDE Head.inc .code HeapSort PROC USES eax ebx ecx edx esi edi, pArray : PTR SDWORD, len : SDWORD ;build heap. ;EAX=(len-2)/2 mov EAX,len sub EAX,2 shr EAX,1 L1: INVOKE AdjustHeap,pArray,len,EAX DEC EAX CMP EAX,0 jge L1 ;EAX=pArray, EBX=len, ECX=LastIndex mov EAX,pArray mov EBX,len mov ECX,EBX dec ECX ;begin to sort L2: cmp EBX,2 jl END1 mov ESI,[EAX+4*ECX] mov EDI,[EAX] mov [EAX+4*ECX],EDI mov [EAX],ESI dec EBX dec ECX INVOKE AdjustHeap,pArray,EBX,0 jmp L2 END1: ret HeapSort ENDP ;---------------------------- ;AdjustHeap method used to adjust the binary-tree to a heap, ;it assume that the initial condition is two sub-tree of the binary-tree are ;heap but the root node is not biggest number. ;---------------------------- AdjustHeap PROC USES eax ebx ecx edx esi edi, pArray : PTR SDWORD, len : SDWORD, rootIndex : SDWORD mov EDX,rootIndex mov EAX,pArray ;ESI = left sub-node index, EDI= right sub-node index L1: mov ESI,EDX shl ESI,1 inc ESI cmp ESI,len jge END1 mov EDI,ESI inc EDI cmp EDI,len jge Exchange mov EBX,[EAX+4*ESI] mov ECX,[EAX+4*EDI] cmp EBX,ECX jge Exchange mov ESI,EDI ;ESI=selected sub-node index(used to exchange with father node), ;EDI= father node index Exchange: mov EDI,EDX mov EBX,[EAX+4*ESI] mov ECX,[EAX+4*EDI] cmp EBX,ECX jle END1 mov [EAX+4*ESI],ECX mov [EAX+4*EDI],EBX mov EDX,ESI jmp L1 END1: ret AdjustHeap ENDP END
源文件:PrintNumber.asm
该源文件用来在控制台打印数字,在WIN32汇编中,I/O操作不再使用中断,而是调用Windows API,这和C/C++,C#等高级语言是一样的。
TITLE Print Number .386 .MODEL flat,stdcall INCLUDE Head.inc .code PrintNumber PROC USES eax ecx edx esi edi, inputNumber:SDWORD LOCAL IsNegative:DWORD, outputBuffer[13]:BYTE, handle:DWORD, charWritten:DWORD ;Decide if inputNumber is a negative number, ;if it is, then IsNegative=1,inputNumber=-inputNumber mov IsNegative,0 bt inputNumber,31 jnc L1 mov IsNegative,1 neg inputNumber L1: mov EAX,inputNumber mov EDX,0 ;EDI will be the dividend. mov EDI,10 lea ESI,outputBuffer ;"line break" is insert at the end of outputBuffer mov [ESI+11],BYTE PTR 0Ah mov [ESI+12],BYTE PTR 0Dh ;ECX is the current index of outputBuffer mov ECX,11 ;convert inputNumber to chars and store it into outputBuffer. L2: div edi add EDX,'0' dec ECX mov [ESI+ECX],DL mov EDX,0 cmp EAX,0 jnz L2 ;if inputNumber is negative number, insert '-' at front of string cmp IsNegative,0 jz L3 dec ECX mov [ESI+ECX],BYTE PTR '-' ;print the number L3: INVOKE GetStdHandle,-11 mov handle,eax lea EDX,charWritten ;ESI+ECX is the beginning of printed buffer. add ESI,ECX ;len of printed buffer = 13-ECX sub ECX,13 neg ECX INVOKE WriteConsoleA,handle,ESI,ECX,EDX,0 ret PrintNumber ENDP END
源文件:main.asm(入口点)
TITLE It is Main function .386 .MODEL flat,stdcall INCLUDE Head.inc .data pArray SDWORD -2147483648,100,2147483647,-100,-100,2147483647 len SDWORD ($-pArray)/4 .code main PROC USES eax ecx, INVOKE BubbleSort,ADDR pArray,len ;INVOKE HeapSort,ADDR pArray,len ;INVOKE QuickSort,ADDR pArray,len ;Print Numbers mov EAX,OFFSET pArray mov ECX,len P1: INVOKE PrintNumber,[EAX] ADD EAX,4 LOOP P1 INVOKE ExitProcess,0 main ENDP END main
发现我在文中总是说“和C语言一样”,这是我的一点感触。发现和其他高级语言,比如C#那样抽象程度非常高的语言,C语言和汇编真的很接近,C语言只是汇编的朴素的封装而已,C语言大约只是封装了对通用寄存器的操作,2者生成的.obj文件都是可以相互链接的。