3. 第二个汇编程序
例 18.2. 求一组数的最大值的汇编程序
#PURPOSE: This program finds the maximum number of a
# set of data items.
#
#VARIABLES: The registers have the following uses:
#
# %edi - Holds the index of the data item being examined
# %ebx - Largest data item found
# %eax - Current data item
#
# The following memory locations are used:
#
# data_items - contains the item data. A 0 is used
# to terminate the data
#
.section .data
data_items: #These are the data items
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.section .text
.globl _start
_start:
movl $0, %edi # move 0 into the index register
movl data_items(,%edi,4), %eax # load the first byte of data
movl %eax, %ebx # since this is the first item, %eax is
# the biggest
start_loop: # start loop
cmpl $0, %eax # check to see if we've hit the end
je loop_exit
incl %edi # load next value
movl data_items(,%edi,4), %eax
cmpl %ebx, %eax # compare values
jle start_loop # jump to loop beginning if the new
# one isn't bigger
movl %eax, %ebx # move the value as the largest
jmp start_loop # jump to loop beginning
loop_exit:
# %ebx is the status code for the _exit system call
# and it already has the maximum number
movl $1, %eax #1 is the _exit() syscall
int $0x80
汇编、链接、运行:
$ as max.s -o max.o
$ ld max.o -o max
$ ./max
$ echo $?
这个程序在一组数中找到一个最大的数,并把它作为程序的退出状态。这组数在 .data 段给出:
data_items:
.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
.long 指示声明一组数,每个数占 32 位,相当于 C 语言中的数组。这个数组开头定义了一个符号data_items ,汇编器会把数组的首地址作为data_items 符号所代表的地址,data_items 类似于 C 语言中的数组名。data_items 这个标号没有用.globl 声明,因为它只在这个汇编程序内部使用,链接器不需要用到这个名字。除了.long 之外,常用的数据声明还有:
-
.byte,也是声明一组数,每个数占 8 位 -
.ascii,例如.ascii "Hello world",声明 11 个数,取值为相应字符的 ASCII 码。注意,和 C 语言不同,这样声明的字符串末尾是没有'\0'字符的,如果需要以'\0'结尾可以声明为.ascii "Hello world\0"。
data_items 数组的最后一个数是 0,我们在一个循环中依次比较每个数,碰到 0 的时候让循环终止。在这个循环中:
-
edi寄存器保存数组中的当前位置,每次比较完一个数就把edi的值加 1,指向数组中的下一个数。 -
ebx寄存器保存到目前为止找到的最大值,如果发现有更大的数就更新ebx的值。 -
eax寄存器保存当前要比较的数,每次更新edi之后,就把下一个数读到eax中。
_start:
movl $0, %edi
初始化 edi ,指向数组的第 0 个元素。
movl data_items(,%edi,4), %eax
这条指令把数组的第 0 个元素传送到 eax 寄存器中。 data_items 是数组的首地址, edi 的值是数组的下标,4 表示数组的每个元素占 4 字节,那么数组中第 edi 个元素的地址应该是 data_items + edi * 4 ,写在指令中就是 data_items(,%edi,4) ,这种地址表示方式在下一节还会详细解释。
movl %eax, %ebx
ebx 的初始值也是数组的第 0 个元素。下面我们进入一个循环,循环的开头定义一个符号start_loop ,循环的末尾之后定义一个符号loop_exit 。
start_loop:
cmpl $0, %eax
je loop_exit
比较 eax 的值是不是 0,如果是 0 就说明到达数组末尾了,就要跳出循环。 cmpl 指令将两个操作数相减,但计算结果并不保存,只是根据计算结果改变 eflags 寄存器中的标志位。如果两个操作数相等,则计算结果为 0, eflags 中的 ZF 位置 1。 je 是一个条件跳转指令,它检查 eflags 中的 ZF 位,ZF 位为 1 则发生跳转,ZF 位为 0 则不跳转,继续执行下一条指令。可见比较指令和条件跳转指令是配合使用的,前者改变标志位,后者根据标志位决定是否跳转。 je 可以理解成“jump if equal”,如果参与比较的两数相等则跳转。
incl %edi
movl data_items(,%edi,4), %eax
将 edi 的值加 1,把数组中的下一个数传送到 eax 寄存器中。
cmpl %ebx, %eax
jle start_loop
把当前数组元素 eax 和目前为止找到的最大值 ebx 做比较,如果前者小于等于后者,则最大值没有变,跳转到循环开头比较下一个数,否则继续执行下一条指令。 jle 表示“jump if less than or equal”。
movl %eax, %ebx
jmp start_loop
更新了最大值 ebx 然后跳转到循环开头比较下一个数。 jmp 是一个无条件跳转指令,什么条件也不判断,直接跳转。 loop_exit 符号后面的指令调 _exit 系统调用退出程序。