这一篇主要说明数据传送的问题。
首先,在AT&T汇编中的数据也是分类的。我记得学win16汇编的时候没有这个概念,只知道可以按寄存器的高低位来传送数据。下面是win32汇编的几种命令:
.ascii | 文本字符串(不带’\0’) |
.asciz | 以空格符结尾的文本字符串 |
.byte | 字节值 |
.double | 双精度浮点数 |
.float | 单精度浮点数 |
.int | 32位整数 |
.long | 同上 |
.octa | 16字节整数 |
.quad | 8字节整数 |
.short | 16位整数 |
.single | 单精度浮点数(同float) |
这里有个小技巧,在数据段中可以定义一个.equ 名字 常量
那么这个名字就成了这个常量的代替。有点像是C中的宏定义。
关于.bss段还有两个配合使用的命令
.comm 声明未初始化的数据的通用内存区域
.lcomm 声明未初始化的数据的本地通用内存区域。
comm段声明的区域属于共享内存段中的。
但是lcomm是不会从本地汇编代码之外进行数据访问的。
还有上一节我提到的,bss段的数据是不包含在可执行文件中的。但是数据段(data)必须包含在可执行文件中。
在.data段中还可以使用一个命令可以分配空间并且自动填充0。
用法如下:
.section .data
Buffer:
.fill 1000
表示创建一个buffer段,并且生成1000个字节的内存区域,并且自动填充为0。
这个应该可以用来实现C标准库函数中的memset函数。不用for循环,效率也高了。
不过只能对data用。
关于数据传送的重点就是mov指令了
在AT&T汇编中,mov是分长度的。Windows汇编是不分长度的。。(话说AT&T汇编真是各种麻烦)
movl用于32位的长字值
movw用于16位
movb用于8位
变质内存模式,表达式格式:
Base_address (offset_address,index,size)
其意义为
Base_address+offset_address+index*size
movl $2,%edi
movl values(,&edi<4),%eax
表示把第3个4字节的变址值加载到eax寄存器中
注意如果在一个标签前面加上了$,那么表示取这个标签的地址,而不是值。
这个有点像C语言&的形式,但是符号是$.
GUN汇编器不允许把值与寄存器相加,必须把值放在括号之外
Movl %edx,4(%edi)
由于时间关系。所以接下来的总结会比较简略。
数据交换指令
XCHG | 在两个寄存器之间或寄存器和内存位置之间交换值 |
BSWAP | 反转一个32位寄存器中的字节顺序 |
XADD | 交换两个值并且把总和存储在目标操作数中 |
CMPXCHG | 把一个值和一个外部值进行比较,并且交换它和另一个值 |
CMPXCHG8B | 比较两个64位值并且交换他们 |
BSWAP貌似对于网络编程改变字节序很有用。应该是用来优化上层代码的,直接把功能内置到CPU指令中了.
堆栈指令
Push和pop。这两个指令使用ESP这个寄存器。
并且AT&T汇编一直是将内存区域从高到低来使用的。
也就是说每次PUSH,ESP都是递减的。
注意:
其实汇编中说的‘堆栈’其实就是单指’栈’。(因为这个说法弄的我一直没看懂为什么malloc为什么分配之后存储的不是0,以后再说为什么)
关于优化内存访问
我个人总结如下:
1.内存基址对准,和C中结构体的对准一致。
2.避免分撒的小数据传输,使用单一的大型数据传输
3.避免在堆栈中使用大的数据长度,好比多媒体扩展的数和FPU存放的数(就是浮点寄存器)
4.将长度一致的变量设置在程序的开头定义,而将长度不一致的放在程序的结尾定义。
附:gas汇编器支持.alig命令,用来在特定边界内对准定义的数据元素。