AT&T汇编语法格式 和在Intel下格式不同,举例:
参考 http://www.cnblogs.com/hdk1993/p/4820353.html
mov %eax(源),%ebx(目的)
mov $4,%ebx //使用立即数
mov $value,%ebx //符号常数直接使用
movw %ax,%bx //b(byte)8位,w(word)16位,l(long)32位 操作数长度指定
*jmp *call...
ljump $section,$offset == jmp far section:offset //远转移
lcall $section,$offset == call far section:offset //远调用
lret $stack_adjust == ret far stack_adjust //远程返回
寻址方式格式问题:
AT&T section:disp(base,index,scale) 是 base+index*scale+disp
Intel section:[base+index*scale+disp]
movl -4(%ebp),%eax == mov eax,[ebp-4]
movl array(,%eax,4),%eax == mov eax,[eax*4+array]
Linux内核中用到了大量的嵌入式汇编,AT&T格式的嵌入式汇编:
_asm_("asm statements":outputs:inputs:registers-modified) //后面三个可选项
_asm_("pushl %%eax \n\t" "movl $0,%%eax \n\t" "popl %eax");
%%两个的原因是:gcc在编译C语言时,会去掉一个%号。
1 #define get_seg_byte(seg,addr) \
2 ({ \
3 register char _res; \ //定义了一个寄存器变量--res
4 _asm_("push %%fs; \ //保存fs寄存器原值
5 mov %%ax,%%fs; \ //用seg设置fs
6 movb %%fs:%2,%%al; \ //取seg:addr处1字节内容到al寄存器
7 pop %%fs" \ //恢复fs寄存器原内容
8 :"=a" (_res) \
9 :"0" (seg), "m" (*(addr))); \
10 _res; })
参考[嵌入式汇编] http://www.cnblogs.com/lsf90/archive/2012/02/21/embed.html
第8行即输出寄存器,该语句的含义是在这段代码运行结束后将eax所代表的的寄存器的值放入_res变量中,作为本函数的输出值,“=a”中的“a”称为加载代码,“=”表示这是输出寄存器,并且其中的值将被输出值替代。加载代码是CPU寄存器,内存地址以及一些数值的简写字母代号。
第9行表示在这段代码开始运行时将seg放到eax寄存器中,“0”表示使用与上面相同位置上的输出寄存器。而((*addr))表示一个内存偏移地址值。为了在上面汇编语句中使用该地址值,嵌入式汇编程序规定把输出和输入寄存器按统一顺序编号,顺序是从输出寄存器序列从左到右从上到下以“%0”开始,分别记为%0、%1···%9.因此,输出寄存器的编是%0(这里只有一个输出寄存器),输入寄存器前一部分(“0”(seg))的编号是%1,而后部分的编号是%2。上面第6行上的%2即代表((*addr))这个内存偏移量。
保护模式和实模式
参考 http://blog.chinaunix.net/uid-28541347-id-4357153.html
首先实模式和保护模式是CPU的两种工作模式。一开始PC启动时CPU是工作在实模式下的,经过某种机制后,CPU跳转到保护模式。
Intel 8086是16位CPU,它只有16位寄存器、16位数据总线和20位地址总线,它只能运行在实模式。
在实模式,物理地址=段值16+偏移段值和偏移都是16位的 具有1MB(2^16 2^4 + offset)的寻址能力。而从80386开始CPU有32位地址线,所以寻址空间可以达到4GB。单从寻址这方面说,使用16位寄存器的方法已经不够用了,必须要开发一种新方法。在保护模式下,虽然CPU还是使用原来16位的cs、ds寄存器表示内存地址的段值,不过保护模式下的段值只是一个索引,这个索引指向一个数据结构的一个表项就是GDT(or LDT)。
在保护模式下,CPU有着巨大的寻址能力,并为强大的32位操作系统提供了更好的硬件保障。所以实模式和保护模式其实就是随着CPU性能不断增强后为了前后能够兼容而设计出来的一套模式。是一个历史遗留产物。