新概念语法:AT& T x86 asm 语法

  DJGPP 使用AT&T格式汇编语法el格式语法有点区别主要区别点如下:

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

  这里有些例子 左边部分是el指令格式右边是at&t格式

movw %bx, %ax // mov ax, bx
xorl %eax, %eax // xor eax, eax
movw $1, %ax // mov ax,1
moVB X, %ah // mov ah, ptr X
movw X, %ax // mov ax, word ptr X
movl X, %eax // mov eax, X


  大部分操作指令at%t和el都是差不多除了这些:

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应该是两个相互独立指令, 存储器情况也有点区别通常el格式如下:

  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 _.gif' />(,%eax,4), %eax // mov eax, [4*eax + .gif' />])
movw _.gif' />(%ebx,%eax,4), %cx // mov cx, [ebx + 4*eax + .gif' />])


  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-modied);

  这 4个字段含义是:

asm statements - AT&T 结构, 每新行都是分开
outputs - 修饰符定要用引号引起来, 用逗号分隔
inputs - 修饰符定要用引号引起来, 用逗号分隔
registers-modied - 名字用逗号分隔
个小小例子:
__asm__("
pushl %eax\n
movl $1, %eax\n
popl %eax"
);


  假如你不用到特别输入输出变量或者修改任何寄存器般来说是不会使用到其他 3个字段,

  让我们来分析下输入变量

i = 0;
__asm__("
pushl %%eax\n
movl %0, %%eax\n
addl $1, %%eax\n
movl %%eax, %0\n
popl %%eax"
:
: "g" (i)
); // increment i


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

  下面让我们来看看输入字段 附加描述符可以修正指令来让你给定编译器来正确处理这些变量他们般被附上双引号 那么这个"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将和输入字段里变量相对应, 按顺序排列

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

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

i=0, j=1;
__asm__ __volatile__("
pushl %%eax\n
movl %0, %%eax\n
addl %1, %%eax\n
movl %%eax, %0\n
popl %%eax"
:
: "g" (i), "g" (j)
); // increment i by j


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

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

  让我们来看看输出字段:

i=0;
__asm__ __volatile__("
pushl %%eax\n
movl $1, %%eax\n
movl %%eax, %0\n
popl %%eax"
: "=g" (i)
); // assign 1 to i


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

i=0, j=1, k=0;
__asm__ __volatile__("
pushl %%eax\n
movl %1, %%eax\n
addl %2, %%eax\n
movl %%eax, %0\n
popl %%eax"
: "=g" (k)
: "g" (i), "g" (j)
); // k = i + j


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

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

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

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

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

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

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

i=0, j=1, k=0; __asm__ __volatile__("
pushl %%eax\n /*译者注:好像原文说有点问题明明是保存了eax:(*/
movl %1, %%eax\n
addl %2, %%eax\n
movl %%eax, %0\n
popl %%eax"
: "=g" (k)
: "g" (i), "g" (j)
: "ax", "memory"
); // k = i + j


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

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

For example,
__asm__ __volatile__("
0:\n
...
jmp 0b\n
...
jmp 1f\n
...
1:\n
...
);


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

void MyFunction( x, y )
{
__asm__( "Start:" );
__asm__( ...do some comparison... );
__asm__( "jl Label_1" );
CallFunction( &x, &y );
__asm__("jmp Start");
Label_1:
;
}
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:

#
.file "myasm.S"
.data
.align 2
somedata: .word 0
...
.text
.align 4
FUNC(__MyExternalAsmFunc)
ENTER
movl ARG1, %eax
...
jmp mylabel
...
mylabel:
...
LEAVE




  这是个好纯粹汇编代码框架



Tags:  ampasm语法 新概念语法

延伸阅读

最新评论

发表评论