分享
 
 
 

AT&T/x86/asm语法

王朝other·作者佚名  2008-05-18
窄屏简体版  字體: |||超大  

Homepage:http://www.whitecell.org

DJGPP使用AT&T格式的汇编语法,和一般的intel格式的语法有点不同。主要不同点如下:

AT&T语法颠倒了源和目的操作数的位置, 目的操作数在源操作数之后。寄存器操作数要有个%的前缀,立即数操作数要有个$符号的前缀。存储器操作数的大小取决于操作码的最后一个字符。 它们是b(8-bit), w(16-bit)和l(32-bit)。

这里有一些例子。左边部分是at&t格式,右边是intel指令格式。

movw %bx, %ax// mov ax, bx

xorl %eax, %eax// xor eax, eax

movw $1, %ax// mov ax,1

movb X, %ah// mov ah, byte ptr X

movw X, %ax// mov ax, word ptr X

movl X, %eax// mov eax, X

大部分操作指令,at&t和intel都是差不多的,除了这些:

movsSD // movsx

movzSD // movz

S和D分辨代表源和目的操作数后缀。

movswl %ax, %ecx// movsx ecx, ax

cbtw // cbw

cwtl // cwde

cwtd // cwd

cltd // cdq

lcall $S,$O // call far S:O

ljmp $S,$O // jump far S:O

lret $V // ret far V

操作嘛,前缀不能与它们作用的指令写在同一行。例如rep和stosd应该是两个相互独立的指令, 存储器的情况也有一点不同。通常intel格式的如下:

section:[base + index*scale + disp]

被写成:

section:disp(base, index, scale)

这里有些例子:

movl 4(%ebp), %eax // mov eax, [ebp+4])

addl (%eax,%eax,4), %ecx // add ecx, [eax + eax*4])

movb $4, %fs:(%eax) // mov fs:eax, 4)

movl _array(,%eax,4), %eax // mov eax, [4*eax + array])

movw _array(%ebx,%eax,4), %cx// mov cx, [ebx + 4*eax + array])

Jump 指令通常是个短跳转。可是, 下面这些指令都是只能在一个字节的范围内跳转: jcxz,jecxz,loop,loopz,loope,loopnz和loopne。象在线文档所说的那样,一个jcxz foo可以扩展成以下工作:

jcxz cx_zero

jmp cx_nonzero

cx_zero:

jmp foo

cx_nonzero:

文档也注意到了mul和imul指令。 扩展的乘法指令只用一个操作数,例如, imul $ebx, $ebx将不会把结果放入edx:eax。使用imul %ebx中的单操作数来获得扩展结果。

--------------------------------------------------------------------------------

Inline Asm

我将首先开始inline asm, 因为似乎关于这方面的疑问非常多。这是最基本的语法了, 就象在线帮助信息

中描述的:

__asm__(asm statements : outputs : inputs : registers-modified);

这四个字段的含义是:

asm statements - AT&T 的结构, 每新行都是分开的。

outputs - 修饰符一定要用引号引起来, 用逗号分隔

inputs - 修饰符一定要用引号引起来, 用逗号分隔

registers-modified - 名字用逗号分隔

一个小小的例子:

__asm__("

pushl %eax

movl $1, %eax

popl %eax"

);

假如你不用到特别的输入输出变量或者修改任何寄存器的值,一般来说是不会使用到其他的三个字段的,让我们来分析一下输入变量。

int i = 0;

__asm__("

pushl %%eax

movl %0, %%eax

addl $1, %%eax

movl %%eax, %0

popl %%eax"

:

: "g" (i)

);// increment i

不要为上面的代码所困扰! 我将尽力来解释它。我们想让输入变量i加1,没有任何输出变量, 也没有改变寄存器值(我们保存了eax值)。 因此,第二个和最后一个字段是空的。 因为指定了输入字段, 我们仍需要保留一个空的输出字段, 但是没有最后一个字段, 因为它没被使用。在两个空冒号之间留下一个新行或者至少一个空格。

下面让我们来看看输入字段。 附加描述符可以修正指令来让你给定的编译器来正确处理这些变量。他们一般被附上双引号。 那么这个"g"是用来做什么的呢? 只要是合法的汇编指令,"g"就让编译器决定该在哪里加载i的值。一般来说,你的大部分输入变量都可以被赋予"g", 让编译器决定如何去加载它们 (gcc甚至可以优化它们!)。 其他描述符使用"r" (加载到任何可用的寄存器去), "a" (ax/eax), "b"(bx/ebx), "c" (cx/ecx), "d" (dx/edx), "D" (di/edi), "S" (si/esi), 等等。

我们将要提到一个在asm代码里面的如%0的输入变量。如果我们有两个输入, 他们会一个是%0,一个是%1,在输入段里按顺序排列 (如下一个例子)。假如N个输入变量且没有输出变量, 从%0到%N-1将和输入字段里的变量相对应, 按顺序排列。

如果任何的输入, 输出, 寄存器修改字段被使用, 汇编代码里的寄存器名必须用两个%来代替一个%。对应于第一个没有使用最后三个字段的例子。

让我们看看两个输入变量且引入了"volatile"的例子:

int i=0, j=1;

__asm__ __volatile__("

pushl %%eax

movl %0, %%eax

addl %1, %%eax

movl %%eax, %0

popl %%eax"

:

: "g" (i), "g" (j)

);// increment i by j

Okay, 现在我们已经有了两个输入变量了。没问题了, 我们只需要记住%0对应第一个输入变量(在这个例子中是i), %1对应在i后面的列出的j。

Oh yeah, 这个volatile到底是什么意思呢? 它防止你的编译器修改你的汇编代码,就是不进行优化(纪录, 删除, 结合,等等优化手段。), 不改变代码原样来汇编它们。建议一般情况下使用volatile选项。

让我们来看看输出字段:

int i=0;

__asm__ __volatile__("

pushl %%eax

movl $1, %%eax

movl %%eax, %0

popl %%eax"

: "=g" (i)

);// assign 1 to i

这看起来非常象我们前面提到的输入字段的例子; 确实也没有很大的不同。所有的输出修饰符前面都应该加上=字符,他们同样在汇编代码里面用%0到%N-1来表示, 在输出字段按顺序排列。你一定会问如果同时有输入和输出字段会怎么排序的呢? 好,下面一个例子就是让大家知道如何同时处理输入输出字段的。

int i=0, j=1, k=0;

__asm__ __volatile__("

pushl %%eax

movl %1, %%eax

addl %2, %%eax

movl %%eax, %0

popl %%eax"

: "=g" (k)

: "g" (i), "g" (j)

);// k = i + j

Okay, 唯一个不清楚的地方就是汇编代码中的变量的个数。我马上来解释一下。

当同时使用输入字段和输出字段的时候:

%0 ... %K 是输出变量

%K+1 ... %N 是输入变量

在我们的例子中, %0对应k, %1对应i, %2对应j。很简单,是吧?

到现在为止我们都没有使用最后一个字段(registers-modified)。如果我们要在我们的汇编代码里使用任何寄存器, 我们要明确的用push和pop指令来保存它们, 或者列到最后一个字段里面让gcc来处理它们。

这是前面的一个例子, 没有明确的保留和存贮eax。

int i=0, j=1, k=0;

__asm__ __volatile__("

pushl %%eax

/*译者注:好像原文说的有点问题,明明是保存了eax的值,:(*/

movl %1, %%eax

addl %2, %%eax

movl %%eax, %0

popl %%eax"

: "=g" (k)

: "g" (i), "g" (j)

: "ax", "memory"

);// k = i + j

我们让gcc来保存和存贮eax, 如果必要的话。一个16-bit寄存器名代表了32-, 16-或8-bit寄存器。 如果我们要改写内存 (写入一个变量等。), 建议在register-modified字段里面来指定"memroy"修饰符。这意味着除了第一个例子我们都应该加上这个修饰符, 但是直到现在我才提出来, 是为了更简单易懂。

在你的内联汇编里面定位标号应该使用b或f来作为终止符, 尤其是向后向前的跳转。(译者注:b代表向后跳转,f代表向前跳转)

For example,

__asm__ __volatile__("

0:

...

jmp 0b

...

jmp 1f

...

1:

...

);

这里有个用c代码和内联汇编代码混合写的跳转程序的例子(thanks to Srikanth B.R for this tip).

void MyFunction( int x, int y )

{

__asm__( "Start:" );

__asm__( ...do some comparison... );

__asm__( "jl Label_1" );

CallFunction( &x, &y );

__asm__("jmp Start");

Label_1:

return;

}

--------------------------------------------------------------------------------

External Asm

Blah... Okay fine. Here's a clue: Get some of your C/C++ files, 且用gcc -S file.c来编译。 然

后查看file.S文件。基本结构如下:

.file "myasm.S"

.data

somedata: .word 0

...

.text

.globl __myasmfunc

__myasmfunc:

...

ret

Macros, macros! 头文件libc/asmdefs.h便于你写asm。 在你的汇编代码最前面包含此头文件然后就可以

使用宏了。一个例子: myasm.S:

#include <libc/asmdefs.h>

.file "myasm.S"

.data

.align 2

somedata: .word 0

...

.text

.align 4

FUNC(__MyExternalAsmFunc)

ENTER

movl ARG1, %eax

...

jmp mylabel

...

mylabel:

...

LEAVE

这是一个很经典的汇编代码框架。

///////////////////////////////////////////////////////////////////////////////

linux下gcc的汇编格式是at&t格式的,和我们平时用的intel格式的汇编语法不一样,所以很多熟悉windows汇编的人到linux下有点无所适从,所以我贴了我以前写的这篇文档,帮助大家理解at&t汇编,做个参考手册

////////////////////////////////////////////////////////////////////////////////

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有