说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
《ProgrammingGroundUp-1-0-lettersize》
《Computer Systems A Programmer’s Perspective》
上下文链接
上篇:汇编语言学习篇5——GAS汇编实践篇(2)- helloworld.s
下篇:汇编语言学习篇5——GAS汇编实践篇(4)- power.s
第三个程序:maximum.s
功能:找到一个数字列表的最大值
寄存器作用:
%edi
将保留列表中的当前位置 。 ==》 索引变量
%ebx
将保存列表中当前的最高值。 ==》 保存最大值变量
%eax
将保存当前正在检查的元素。 ==》 保存正检测变量
Data_items
—保存数字列表数据。0用于终止数据
思路:
1. 检查当前列表元素(%eax
),看看它是否是零(终止元素)。
2. 如果是零,退出。
3. 增加当前位置(%edi
)。
4. 将列表中的下一个值加载到当前值寄存器(%eax
)。这里我们可能使用什么寻址模式?为什么?
5. 比较当前值(%eax
)和当前最大值(%ebx
)。
6. 如果当前值大于当前最大值,则将当前最大值替换为当前值。
7. 重复。
汇编程序内容:
.section .data
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 #索引寄存器的初始值为0
movl data_items(,%edi,4), %eax #加载数据的第一个字节
movl %eax, %ebx #因为这是第一项,所以%eax是最大的
start_loop: #标记循环位置(单次循环结束后应该跳转的位置)
cmpl $0, %eax #比较两个值:0和eax寄存器中的数字,将结果保存到状态寄存器中
je loop_exit #若结果为两个值相等则跳到循环结束的标志处。
incl %edi #加载下一个数值,将%edi的数值+1
movl data_items(,%edi,4), %eax #与之前介绍的作用相同,不过%edi的数值变了,故%eax为下一个要测试的数值
cmpl %ebx, %eax #比较数值
jle start_loop #若新的不是更大的话,跳到循环开始
movl %eax, %ebx #否则表示该数值比之前的大,则将值传到%ebx为最大值
jmp start_loop #无条件跳转到循环开始,进行读取下一个数值
loop_exit: #%ebx是退出系统调用的状态码,并且它已经具有最大的数字
movl $1, %eax #1是exit()系统调用
int $0x80
汇编程序解析:
结合第一个程序所学的知识,
由.section
伪指令进行分段,
①数据段(.data):
1️⃣data_items:
:符号/标记:记录数据列表的位置。
由此,程序中任何想引用该地址,都可使用data_items
符号,汇编程序将用汇编过程中的数字的开始地址替换掉。
若程序中有:movl data_items,%eax
,则数字3
会传入寄存器eax
中。
注意:这边并没data_items
的.globl
声明,这是因为我们只在程序中引用这些位置。其他文件或程序不需要知道它们的位置,故没有必要进行.globl
声明,当然也可添加,即:.globl data_items
。
2️⃣.long 3,67,34,222,45,75,54,34,44,33,22,11,66,0
:表示保留了14个.long
类型的数据大小内存空间,即整个数字列表占用56Byte。
注意:
1.最后一个数值为0
,这边用数字0
表示列表结尾,故0
必须在最后,否则0
后数字无效。当然可用其他方法来表示列表结尾。
2.虽long
的范围为0~4294967295
,但也不可超过255
,任何超过255
的数字会产生奇怪的结果,这是因为255
是允许的最大退出状态。
②代码段(.text)
3️⃣.globl _start
:为全局的标记,为何这边一定要加.globl
,因为Linux需要知道它在哪里,以便知道程序从哪开始执行。
4️⃣movl data_items(,%edi,4), %eax
==》movl BEGINNINGADDRESS(,%INDEXREGISTER,WORDSIZE)
==》此为索引寻址模式指令方法。
1.data_items
表示数字列表的开始位置地址.
2.%edi
:索引寄存器。0
则为数字3
,1
则为数字67
.
3.4
表示存储单元大小,这边.long
故为4
.
5️⃣cmpl $0, %eax
比较两个值即:比较0与eax寄存器中的数字,将比较的结果存在状态寄存器(%eflags
)中,这边并未提及,先简单认识。
6️⃣je loop_exit
若比较的结果相等则跳转到loop_exit
循环结束的标志处,此为流控制指令。【还有其他的跳跃指令】
7️⃣incl %edi
加载下一个数值,将%edi
的数值+1
简单描述一下代码段思路:先取得第一个值,该值一定为最大值,存入最大值寄存器中,之后每次先判断当前数值为不为0(是不是最后一个数字),若是则直接跳到结束。若不是,则取数字列表的下一个值进行比较,若小于当前最大值,则继续循环,若大于则替换为最大值,再继续循环。最后在利用第一个程序学到的调用退出程序的系统调用结束。
实操结果:
数据类型:
.byte
:表示每个数字占用1Byte,大小范围:0~255
.
.int
:表示每个数字占用2Byte,大小范围:0~65535
.
.long
:表示每个数字占用4Byte,大小范围:0~4294967295
.
.ascii
:表示将字符存入内存中,每个字符1Byte。如:ascii "Hello there\0"
汇编器会保留12个存储位置(字节),\0
为终止字符(永远不会显示,只是告诉程序其他部分这是字符的结束),以\
(反斜杠)开头的字母和数字表示不能在键盘上输入或在屏幕上容易看到的字符,如,\n
表示换行,\t
表示“制表符”
条件跳转与无条件跳转(jump):
条件跳转:基于前面比较或计算的结果更改路径.
无条件跳转:直接进入不同的路径
跳转指令:
je
-jump equality:如果值相等,则跳转。
jg
-jump greater:如果第二个值大于第一个值,则跳转。
jge
-jump greater equality:如果第二个值大于或等于第一个值,则跳转。
jl
-jump less:如果第二个值小于第一个值,则跳转。
jle
-jump less equality:如果第二个值小于或等于第一个值,则跳转。
jmp
-无条件转移指令:无论如何都要跳。并不需要进行比较。
PS:可用来实现选择结构与循环结构。
寻址模式:
内存地址引用:
一般形式:ADDRESS_OR_OFFSET(%BASE_OR_OFFSET,%INDEX,MULTIPLIER)
所有字段可选。
计算地址:FINAL ADDRESS = ADDRESS_OR_OFFSET + %BASE_OR_OFFSET + MULTIPLIER * %INDEX
ADDRESS_OR_OFFSET
与MULTIPLIER
必须为常数,其他两个必须为寄存器,若任何部分被漏掉则会在方程中替换为0.
①直接寻址模式
只通过使用ADDRESS_OR_OFFSET
部分来实现,如:
movl ADDRESS, %eax
:这将加载%eax,其值位于内存地址ADDRESS
.
②索引寻址模式
通过使用ADDRESS_OR_OFFSET
和%INDEX
部分完成,可使用任何通用寄存器作为索引寄存器,还可为索引寄存器使用1、2或4的常数乘法器,以便更容易地按字节、双字节和单词进行索引,如:
假设我们有一个字节字符串string_start
,并希望访问第三个字节(索引为2,因为我们开始从0开始计算索引),而%ecx
的值为2。若你想将它加载到%eax
中,则:movl string_start(,%ecx,1), %eax
它从string_start
开始,并向该地址添加1 * %ecx
,并将值加载到%eax
中.
③间接寻址模式
间接寻址模式从寄存器指定的地址加载一个值,如:
若%eax
包含一个地址,可通过如下操作将该地址的值移动到%ebx
:movl (%eax), %ebx
④基指针寻址方式
类似于间接寻址,除了它向寄存器中的地址添加一个常量,如,
若你有一条记录,其中的年龄值是4个字节,并且你在%eax中有记录的地址,你可以通过发出以下指令来检索年龄到%ebx中:movl 4(%eax), %ebx
⑤直接的方式(立即数寻址方式)
即时模式非常简单。它不遵循我们一直使用的一般形式。立即模式用于将直接值加载到寄存器或内存位置。例如,如果您想将数字12加载到%eax中,您只需执行以下操作:movl $12, %eax
⑥寄存器寻址模式
寄存器模式只是将数据移进或移出寄存器。在我们所有的例子中,寄存器寻址模式被用于其他操作数
PS:除了立即模式外,所有模式都可以用作源或目标操作数。立即模式只能是源操作数。
除了这些模式外,还有不同大小的值的移动指令