作者:冯老师,华清远见嵌入式学院讲师。

译自“Using as The GNU Assembler January 1994”。

参考Tornado随机文档“GNU Toolkit User's Guide"。

GNU Assembler

80386 Dependent Features

■ AT&T语法 vs. Intel语法

为了保持和gcc的输出的兼容性,as支持AT&T System V/386汇编语法,它和Intel语法有相当大的差别。强调这个是因为几乎所有的80386文档只使用Intel语法。两者之间的显著区别是:

● AT&T的立即数有前缀'$',Intel的立即数没有前缀(Intel 'push 4'为AT&T 'push Û)。AT&T的寄存器有前缀'%',Intel的寄存器没有前缀。AT&T的绝对跳转(和相对PC的跳转相反)jump/call操作 数有前缀'*',Intel没有前缀。

● AT&T和Intel语法的源和目的操作数的顺序相反。Intel的'add eax, 4'等效于AT&T的'addl Û, %eax'。使用'source, dest'规范的目标是为了和以前的Unix汇编器保持兼容。

● AT&T语法中的内存操作数的宽度是根据操作码名称的最后一个字符决定的。操作码后缀'b','w',和'l'指定了byte(8- bit),word(16-bit),和long(32-bit)的内存引用。Intel语法则对内存操作数(不是操作码)加前缀:'byte ptr','word ptr'和'dword ptr'。这样,Intel 'mov al, byte ptr foo'等效于AT&T的'movb foo, $al'。

● AT&T中的long jumps和calls的立即形式为'lcall/ljmp $section, $offset';Intel语法为'call/jmp far section:offset'。同样,AT&T的远程返回指令为'lret $stack-adjust',而Intel格式为'ret far stack-adjust'。

● AT&T汇编器不提供对多节(multiple section)程序的支持。Unix风格的系统希望所有的程序都是单节的。

■ 操作码命名(opcode naming)

操作码前有一个字符的后缀,指定了操作数的宽度。字母'b','w',和'l'指定了byte,word, 和long型的操作数。如果指令中没有后缀并且不包含内存操作数,那么as将基于目标寄存器操作数(指令中的最后一个寄存器)填充这个缺少的后缀。所 以,'mov %ax, %bx'等效于'movw %ax, %bx';同样,'mov Û, %bx'等效于'movw Û, %bx'。注意这个特点不和AT&T的Unix汇编器兼容,后者假定缺少的后缀为long型操作数宽度。(这个不兼容型并不影响编译器的输出,因 为编译器总是显式地指定操作码后缀。)

AT&T和Intel中的操作码的格式几乎全部一样,但有一个例外。AT&T的符号扩展 (sign extend)和零扩展(zero exten)指令需要指定2个宽度,一个宽度用来指定sign/zero扩展的from,另一个用来零扩展to。在AT&T语法中使用2个操作码 后缀。符号扩展和零扩展的基本名称是'movs...'和'movz...'(Intel格式为'movsx'和'movzx')。操作码后缀加在这个基 本名称后,from在之前。所以,AT&T语法中'movxbl %al, %edx'意思为”move sign extend from %al to %edx“。所以可能的后缀有'bl'(从byte到long),'bw'(从byte到word),和'wl'(从word到long)。

Intel风格的指令:

● 'cbw' - 符号扩展byte '%al'到word '%ax',

● 'cwde' - 符号扩展word '%ax'到long '%eax',

● 'cwd' - 符号扩展word '%ax'到long '%dx:%ax',

● 'cdq' - 符号扩展dword '%eax'到quad '%edx:%eax',

在AT&T中分别叫'cbtw','cwtl','cwtd',和'cltd'。

远程call/jump指令在AT&T中分别为'lcall'和'ljmp',而Intel的格式为'call far'和'jump far'。

■ 寄存器命名(register naming)

寄存器操作数总有前缀'%'。80386的寄存器包括:

● 8个32-bit寄存器 '%eax'(accumulator),'%ebx','%ecx','%edx','%edi','%esi','%ebp'(<I>frame</I> pointer),和'%esp'(stack pointer)。

● 8个低16-bit的以上寄存器:'%ax','%bx','%cx','%dx','%di','%si','%bp',和'%sp'。

● 6个节寄存器'%cs'(代码节),'%ds'(数据节),'%ss'(堆栈节),'%es', '%fs',和'%gs'。

● 3个处理器控制寄存器'%cr0','%cr2',和'%cr3'。

● 6个调试寄存器'%db0','%db1','%db2','%db3','%db6',和'%db7'。

● 2个测试寄存器'%tr6'和'%tr7'。

● 8个浮点寄存器栈'%st'或等效的'%st(0)','%st(1)','%st(2)','%st(3)','%st(4)','%st(5)','%st(6)',和'%st(7)'。

■ 操作码前缀(opcode prefixes)

操作码前缀用于修改以下操作码。它们用于重复字符串指令,提供节覆盖(section overrides),执行总线锁定操作,以及给出操作数和地址的宽度(对于通常的32-bit操作数,可以使用一个”操作数宽度“操作码前缀,来指定 16-bit的操作数)。操作码前缀通常占据一行,没有操作数,并且必须直接位于它们所作用于的指令之前。例如'scas'(字符串扫描)指令可以这样重 复:

repne

scas

下面列出操作码前缀:

● 节覆盖前缀'cs','ds','ss','es','fs','gs'。

● 操作数/地址宽度前缀'data16'和'addr16',将32-bit的操作数/地址改变为16-bit的操作数/地址。注:16-bit寻址模式(即8086和80286寻址模式)还没有支持。

● 总线锁定前缀'lock',在执行它修饰的指令时禁止中断。(只对特定指令有效,参考80386指令手册。)

● 等待协处理器指令'wait',等待协处理器完成当前指令。对于80386/80387组合这不再需要。

● 'rep','repe',和'repne'前缀用来修饰字符串指令,使它们重复'%ecx'次。

注:操作码前缀(1字节):

0xF0 - LOCK;

0xF2 - REPNE/REPNZ(只用于字符串指令);

0xF3 - REP(只用于字符串指令);

0xF3 - REPE/REPZ(只用于字符串指令);

0xF3 - Streaming SIMD扩展指令;

段前缀:

0x2E - CS段覆盖;

0x36 - SS段覆盖;

0x3E - DS段覆盖;

0x26 - ES段覆盖;

0x64 - FS段覆盖;

0x65 - GS段覆盖。

操作数宽度覆盖:0x66

操作数宽度覆盖:0x66

■ 内存引用(memory references)

Intel语法中间接内存引用的形式为:

section:[base + index*scale + disp]

等效的AT&T语法为:

section:disp(base, index, scale)

其中base和index分别为可选的、32-bit的基址和索引寄存器;disp为可选的偏移 (displacement),scale的取值为1,2,4,和8,乘以index计算操作数的地址。如果不指定scale,scale取值为1。 section指定了内存操作数的可选的节寄存器,它可以覆盖缺省的节寄存器(参考80386手册中的节寄存器的缺省值)。注意AT&T语法中的 节覆盖必须有'%'前缀。如果指定的节覆盖碰巧和缺省的节寄存器相同,那么as并不输出任何节寄存器覆盖前缀。所以,可以使用节寄存器覆盖来强调给定的内 存操作数的节寄存器。

下面是Intel和AT&T的内存引用的例子:

AT&T:'-4(%ebp)',Intel:'[ebp-4]';

base是'%ebp',disp为'-4'。使用缺省的节(%ss)。index和scale缺省。

AT&T:'foo(,%eax,4)',Intel:'[foo + eax*4]';

index为%eax(scale为4),disp为'foo'。节寄存器缺省为%ds。

AT&T:'foo(,1)',Intel:'[foo]';

使用foo指向的值当作内存操作数。注意base和index都缺省,但只有一个',',这是一个语法例外。

AT&T:'%gs:foo',Intel:'gs:foo'。

绝对的call和jump的操作数必须有前缀'*'。如果不指定前缀,as选择PC(program counter)相对寻址。

任何有内存操作数的指令必须指定它的宽度(byte,word,long),使用操作码后缀('b','w','l')。

■ 跳转指令的处理(handling of jump instrucion)

跳转指令总是使用最小可能的偏移(displacement)进行优化。处理方法是,如果目标地址足够近, 那么使用字节(8-bit)偏移的跳转。如果字节偏移不够,那么使用long(32-bit)偏移。不支持word(16-bit)偏移跳转(即在跳转指 令前加'addr16'操作码前缀),这是因为80386坚持,如果使用word偏移那么%eip将被掩码为16-bit。

注意'jcxz','jecxz','loop','loopz','loope','loopnz'和 'loopne'指令只使用byte偏移,所以如果你使用这些指令(gcc不使用),你会得到一个错误消息(以及不正确的代码)。AT&T对这个 问题的解决是扩展'jcxz foo'为:

jcxz    cx_zero

jmp     cx_nonzero

cx_zero:

jmp     foo

cx_nonzero:

■ 浮点(floating point)

(omitted)

■ 写16-bit代码(wrting 16-bit code)

GAS除了支持”纯“32-bit i386代码外,还提供对实模式和16-bit保护模式代码段的有限支持。

(to be continued)

文章来源:华清远见嵌入式学院,原文地址:http://www.embedu.org/Column/Column854.htm

更多相关嵌入式免费资料查看华清远见讲师博文>>

GNU for x86汇编语法的更多相关文章

  1. GNU风格 ARM汇编语法指南

    汇编源程序一般用于系统最基本的初始化:初始化堆栈指针.设置页表.操作 ARM的协处理器等.这些初始化工作完成后就可以跳转到C代码main函数中执行. 1.  GNU汇编语言语句格式 任何Linux汇编 ...

  2. GNU风格 ARM汇编语法5

    . GNU汇编程序中的常数 <>十进制数以非0数字开头,如:123和9876: <>二进制数以0b开头,其中字母也可以为大写: <>八进制数以0开始,如:,: &l ...

  3. GNU风格 ARM汇编语法2

    .GNU汇编程序中的标号symbol(或label) 标号只能由a-z,A-Z,-,".",_等(由点.字母.数字.下划线等组成,除局部标号外,不能以数字开头)字符组成. Symb ...

  4. (转)GNU风格ARM汇编语法指南(非常详细)5

    原文地址:http://zqwt.012.blog.163.com/blog/static/120446842010111482417545/ 6.GNU汇编程序中的常数 <1>    十 ...

  5. (转)GNU风格ARM汇编语法指南(非常详细)2

    原文地址:http://zqwt.012.blog.163.com/blog/static/120446842010111481828392/ 2.GNU汇编程序中的标号symbol(或label) ...

  6. GNU风格 ARM汇编语法1

    汇编源程序一般用于系统最基本的初始化:初始化堆栈指针.设置页表.操作 ARM的协处理器等. 这些初始化工作完成后就可以跳转到C代码main函数中执行. 1.GNU汇编语言语句格式 任何Linux汇编行 ...

  7. (转)GNU风格ARM汇编语法指南(非常详细)1

    原文地址:http://zqwt.012.blog.163.com/blog/static/120446842010111481551809/ 汇编源程序一般用于系统最基本的初始化:初始化堆栈指针.设 ...

  8. GNU风格 ARM汇编语法4

    .GNU汇编语言定义入口点 汇编程序的缺省入口是_start标号,用户也可以在连接脚本文件中用ENTRY标志指明其它入口点. 例:定义入口点 .section .data < initializ ...

  9. GNU风格 ARM汇编语法3

    . GNU汇编程序中的分段 <1>.section伪操作 .section <section_name> {,”<flags>”} Starts a new cod ...

随机推荐

  1. 攻城狮在路上(壹) Hibernate(七)--- 通过Hibernate操纵对象(下)

    一.与触发器协同工作: 当Hibernate与数据库的触发器协同工作时,会出现以下两类问题: 1.触发器使Session缓存中的数据和数据库中的不一致: 出现此问题的原因是触发器运行在数据库内,它执行 ...

  2. IBM AppScan 安全扫描:支持弱 SSL 密码套件 分类: 数据安全 2014-06-28 11:34 1844人阅读 评论(0) 收藏

    问题描述: ​ 解决方法: 1.Server 2008(R2) 根据appScan的修订建议访问地址:http://msdn.microsoft.com/en-us/library/windows/d ...

  3. android 入门-生命周期 activity

    生命周期 activity http://blog.csdn.net/android_tutor/article/details/5772285 http://www.cnblogs.com/John ...

  4. hibernate 关联映射

    关联关系大致分为两大类: 1.单向关系:只需单向访问关联端.例如:只能通过老师访问学生,或者只能通过学生访问老师. 2.双向关系:关联的两端可以互相访问.例如:老师和学生之间可以互相访问. 单向关联可 ...

  5. Javascript实现时钟

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  6. Codeforces Round #227 (Div. 2) E. George and Cards set内二分+树状数组

    E. George and Cards   George is a cat, so he loves playing very much. Vitaly put n cards in a row in ...

  7. LayoutInflater(四)

    如果说要按类型来划分的话,自定义View的实现方式大概可以分为三种,自绘控件.组合控件.以及继承控件.那么下面我们就来依次学习一下,每种方式分别是如何自定义View的. 一.自绘控件 自绘控件的意思就 ...

  8. LoadRunner检查点学习实例

    LoadRunner只会检测脚本中事务的执行状态,而实际的事务执行结果则需要通过检查点来完成. 例如一个登录事务,LR只关心事务本身的执行状态,也就是说哪怕实际操作密码错误产生登录失败的业务操作,其事 ...

  9. LoadRunner中循环操作

    Action() {     //Loadrunner中的FOR,WHILE,DO 循环语句 int i;   int whileloop = 1;   //FOR 循环   for (i=1;i&l ...

  10. 2016.8.22 JavaScript入门之三

    1.对一个数组的末尾追加数据的一种简便方法是通过push()功能. 例如: var myArray = [["John", 23], ["cat", 2]];m ...