怎样迁移C语言的算法到汇编程序

前言

这里描述的是把C语言里的算法移植到汇编程序中,迁移工作主要在x86_64的平台上,测试系统是Ubuntu 20.04.3 LTS,gcc版本9.3.0。

一.解读C语言算法

这里以冒泡算法做例子。

#include <stdio.h>
#include <stdlib.h>

void swap(int *a, int *b);
void bubble_sort(int arr[], int size);

int main()
{
    int num[16] = {5, 1, 3, 6, 2, 8, 9, 11, 13, 15, 14, 26, 45, 32, 12, 7};
    int len =16;

    printf("Before sorted:");
    for (int i=0; i < len; i++)
    {
        printf("%4d", num[i]);
    }
    printf("\n");
    bubble_sort(num, len);
    printf("After sorted:");
    for (int i=0; i < len; i++)
    {
        printf("%4d", num[i]);
    }
    printf("\n");
    return 0;
}

void bubble_sort(int arr[], int size)
{
    int i, j, flag;
    for(i = 0; i < size - 1; i++)
    {
        flag = 0;
        for(j = 0; j < size - 1; j++)
        {
            if(arr[j] > arr[j + 1])
            {
                swap(&arr[j], &arr[j + 1]);
                flag = 1;
            }
        }
        if(flag == 0)
        {
            break;
        }
    }
}

void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

这个是在网上随便一搜就能找到的例程,等会要把这个例程中的算法迁移到汇编程序中。首先对这个例程进行简单分析:

- 算法核心在bubble_sort子函数中,子函数只调用了一个swap函数。

- 这个冒泡算法是改良后的冒泡算法,其中有一个flag做标志,flag等于0标志排序完成并跳出循环。

- 主函数中用到了数组历遍算法,并使用两次,把排序前后的数组分两次打印到屏幕上。

简单分析C语言例程后要想想在汇编里面如何实现:

    1. 不改变C语言例程中的层次,把排序算法编写在子程序中。

    2. 在bubble_sort的汇编子程序中也采用双层循环,由于汇编程序中的循环都是采用ecx寄存器做计数,因而双重循环中ecx寄存器要入栈保护。

    3. 不用C语言例程中的swap算法,汇编语言里有xchg指令提供原子级的操作。

    4. 主函数里面的数组历遍并打印的代码,在汇编里要提取出来做为独立的子程序,因为c语言里的几行代码在汇编中就是一长串。

    5. 在汇编冒泡子程序中用eax作为数据交换寄存器,用ebx做待排序数组的基址寄存器,用edi做数组的变址寄存器,相当于数组下标,ecx做循环计数寄存器,edx做为c语言例程的flag标志。

可以编写算法描述文件了,写好算法描述文件后再转为汇编代码。

二、编写算法描述文件

1.先搭个框架

;-------------------------------------------------------------
;           non-Basic Description for Assembly
;This is an algorithm description attemp to describ a bubble
;sort algorithm.
;-------------------------------------------------------------
statement

end statement
declare extern function printf
declare extern function exit
begin
        pass address of banner_before to function
        call function printf
        call array_printer

        call bubble_sort

        pass address of banner_after to function
        call function printf
        call array_printer

        pass parameter for function
        call function exit
 end

2.编写statement

statement内部的就相当于汇编程序里的数据段,要依照C语言例程中用到的字符和数值来写。

c语言例程中用到

    1. num数组, int型数据。

    2. 存放数组元素个数的变量len,int型的数据。

    3. 字符串"Before sorted:"和"After sorted:"

    4. 指示printf函数打印格式的字符"%4d"

    5. 回车换行字符"\n"

    6. 再定义两个常数DONE和NOT_DONE分别为0和0xffffffff

statement
        num int array
        define len (length of array)/(sizeof int)
        banner_before char string "Before sorted: "
        banner_after char string "After sort: "
        format char string "%4d"
        key_ret char string "\n"
        define const DONE  0
        define const NOT_DONE  0xffffffff
end statement

3.编写汇编子程序的算法描述

除了两个调用的c函数外,要完成另两个子程序的编写,array_printer和bubble_sort。

先编写array_printer:

要调用c函数printf需要三个寄存器:

- rdi寄存器存放的是字符串和打印格式字符的首地址

- rsi寄存器要存放待打印出来的数值,如果没有要打印处理的数值,rsi寄存器可以不传入数据。

- 要把rax清零,才能开始调用printf。

- 调用结果会影响ecx和edx寄存器中的数值。

把这部分的c语言代码复制出来,然后照着上面的结构编写算法描述:

    for (int i=0; i < len; i++)
    {
        printf("%4d", num[i]);
    }
    printf("\n");

 这是一个单循环结构,里面调用c函数printf,还是先搭个框架:

subproc
        push rax, rcx, rdi, rsi
        let ecx = len
        loop _num_array
        push rcx, rdi
        pass array parameter to function
        call printf
        pop rdi, rcx
        let edi = edi + 1
        jump to loop _num_array until ecx decrease to zero
        pass key_ret parameter to function
        call printf
        pop rsi, rdi, rcx, rax
        return
endproc

调用printf前还是小心翼翼地把rcx放入栈中,调用完成后再出栈。现在可以完成这部分的算法描述了。

subproc array_printer
        push rax, rcx, rdi, rsi
        let ecx = len
        let edi = 0
    loop _num_array:
        push rcx, rdi
        let esi = num[edi*4]
        let rdi load address of format
        let rax = 0
        call function printf
        pop rdi, rcx
        let edi = edi + 1
        jump to loop _num_array until ecx decrease to zero
    ;end loop _num_array
        let rdi load address of ket_ret
        let rax =0
        call function printf
        pop rsi, rdi, rcx, rax
        return
endproc

再编写bubble_sort的算法描述

先把这段c语言的代码贴上来,照上面的层次转写。

void bubble_sort(int arr[], int size)
{
	int i, j, flag;
	for(i = 0; i < size - 1; i++)
	{
		flag = 0;
		for(j = 0; j < size - 1; j++)
		{
			if(arr[j] > arr[j + 1])
			{
				swap(&arr[j], &arr[j + 1]);
				flag = 1;
			}
		}
		if(flag == 0)
		{
			break;
		}
	}
}

这部分是冒泡算法的核心,双重循环结构,ecx寄存器由len-1开始递减到零才完成一次循环。swap放在内循环里,进入内循环前flag置零,只要交换数据就把flag置1,我把flag置成NOT_DONE,相应的进入循环前flag置成DONE,这样程序好读一些。按之前所设想的那样,eax唱主角,ebx当助手,ecx做循环计数,edx当旗手,edi做数组下标。还是先搭个框架吧。

subproc bubble_sort
        push registers
        let ecx = len -1
    loop outer:
        push rcx
        let edx = DONE
        ;instructments
        let ecx = len -1
        let edi = 0
    loop inner:
        ;instructments
        if no exchange then jump to label skip
        endif
        exchange two elements
        let edx = NOT_DONE
    label skip:
        let edi = edi +1
        jump to loop inner until ecx decrease to zero
    ;end loop inner
        pop rcx
        jump to loop outer until ecx decrease to zero
    ;end loop outer
    label endsub:
        pop registers
        return
endproc

现在要填写进去的就剩条件判断和数组元数交换,完整的bubble_sort子程序算法描述如下:

subproc bubble_sort
        push rax, rbx, rcx, rdx, rdi
        xor rdx, rdx
        let ecx = len - 1
    loop outer:
        let edx = DONE
        push rcx
        let ecx = len - 1
        let edi = 0
    loop inner:
        let eax = num[edi*4]

        if (eax <= num[edi*4+4]) then jump to label skip
        endif
        exchange eax, num[edi*4+4]

        let num[edi*4] = eax
        let edx = NOT_DONE
    label skip: 
        let edi = edi + 1
        jump to loop inner until ecx decrease to zero
    ;end loop inner
        pop rcx
        if (edx != DONE) then jump to label endsub
        endif
        jump to loop outer until ecx decrease to zero
    ;end loop outer
    label endsub:
        pop rdi, rdx, rcx, rbx, rax
        return
endproc

4.完成整个算法描述

回头把主程序中的描述写完整,再把statement和两个子程序的算法描述整合一起就完成了冒泡算法的算法描述了,我不再啰嗦了,把整个算法描述写在下面。

;-------------------------------------------------------------
;           non-Basic Description for Assembly
;This is an algorithm description attemp to describ a bubble
;sort algorithm.
;-------------------------------------------------------------
statement
        num int array
        define len (length of array)/(sizeof int)
        banner_before char string "Before sorted: "
        banner_after char string "After sort: "
        format char string "%4d"
        key_ret char string "\n"
        define const DONE  0
        define const NOT_DONE  0xffffffff
end statement
declare extern function printf
declare extern function exit
begin
        ;pass parameter for function
        let rdi load address of banner_before
        let rax = 0
        call function printf
        call array_printer

        call bubble_sort

        ;pass parameter for function
        let rdi load address of banner_after
        let rax = 0
        call function printf
        call array_printer

        ;pass parameter for function
        let rdi = 0
        call function exit
end

subproc array_printer
        push rax, rcx, rdi, rsi
        let ecx = len
        let edi = 0
    loop _num_array:
        push rcx, rdi
        let esi = num[edi*4]
        let rdi load address of format
        let rax = 0
        call function printf
        pop rdi, rcx
        let edi = edi + 1
        jump to loop _num_array until ecx decrease to zero
    ;end loop _num_array
        let rdi load address of ket_ret
        let rax =0
        call function printf
        pop rsi, rdi, rcx, rax
        return
endproc

subproc bubble_sort
        push rax, rbx, rcx, rdx, rdi
        xor rdx, rdx
        let ecx = len - 1
    loop outer:
        let edx = DONE
        push rcx
        let ecx = len - 1
        let edi = 0
    loop inner:
        let eax = num[edi*4]

        if (eax <= num[edi*4+4]) then jump to label skip
        endif
        exchange eax, num[edi*4+4]

        let num[edi*4] = eax
        let edx = NOT_DONE
    label skip: 
        let edi = edi + 1
        jump to loop inner until ecx decrease to zero
    ;end loop inner
        pop rcx
        if (edx != DONE) then jump to label endsub
        endif
        jump to loop outer until ecx decrease to zero
    ;end loop outer
    label endsub:
        pop rdi, rdx, rcx, rbx, rax
        return
endproc

三、依照算法描述编写汇编代码

我把完成的汇编程序贴出来,我是按照AT&T语法风格编写的,为了使用gcc编译,把汇编入口由_start改成main。编译的时候gcc要带上-no-pie这个选项。

/*----------------------------------------------------------------
  This is an example which according to bubble sort 
  algorithm attemp to sort numbers in an array.
-----------------------------------------------------------------*/
.section .data
        num:
            .int 5, 1, 3, 6, 2, 8, 9, 11, 13, 15, 14, 26, 45, 32, 12, 7
            len = (. - num)/4
        banner_before:
            .asciz "Before sorted: "
        banner_after:
            .asciz "After sorted: "
        format:
            .asciz "%4d"
        key_ret:
            .asciz "\n"
        .equ DONE, 0
        .equ NOT_DONE, 0xffffffff
//.data ends
.section .text
.global main
.extern printf
.extern exit
main:
        mov $banner_before, %rdi
        xor %rax, %rax
        call printf
        call array_printer

        call bubble_sort

        mov $banner_after, %rdi
        xor %rax, %rax
        call printf
        call array_printer

        xor %rdi, %rdi
        call exit
//end main

array_printer:
        push %rax
        push %rcx
        push %rdi
        push %rsi
        mov $len, %ecx
        xor %edi, %edi
    _num_array:
        push %rcx
        push %rdi
        mov num(,%edi,4), %esi
        mov $format, %rdi
        xor %rax, %rax
        call printf
        pop %rdi
        pop %rcx
        inc %edi
        loop _num_array
    //end loop
        mov $key_ret, %rdi
        xor %rax, %rax
        call printf
        pop %rsi
        pop %rdi
        pop %rcx
        pop %rax
        ret
//endproc

bubble_sort:
        push %rax
        push %rbx
        push %rcx
        push %rdx
        push %rdi
        xor %rdx, %rdx
        mov $len-1, %ecx
        mov $num, %ebx
    outer:
        mov $DONE, %edx
        push %rcx
        mov $len-1, %ecx
        xor %edi, %edi
    inner:
        mov (%ebx,%edi,4), %eax
        cmp 4(%ebx,%edi,4), %eax
        jna skip
        xchg 4(%ebx,%edi,4), %eax
        mov %eax, (%ebx,%edi,4)
        mov $NOT_DONE, %edx
     skip:
        inc %edi
        loop inner
    //end loop inner
        pop %rcx
        cmp $DONE, %edx
        je endsub
        loop outer
    //end loop outer
    endsub:
        pop %rdi
        pop %rdx
        pop %rcx
        pop %rbx
        pop %rax
        ret
//endproc
//.text ends
//end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值