网站链接1:百家樂百家百家百家百家百家百家百家百家百家百家开户博彩网博彩网博彩网博彩通全讯网

顺风网 » 汇编基础一
首页 > 学习笔记 > 汇编基础一

汇编基础一

2010年6月6日 admin

MOV指令

假定程序执行之前,寄存器中的数值是全0:

寄存器  X
H L
EAX 0000 00 00
EBX 0000 00 00
ECX 0000 00 00
EDX 0000 00 00

正如前面提到的,EAX的高16bit是没有办法直接访问的,而AX对应它的低16bit,AH(high)、AL(low)分别对应AX的高、低8bit。

mov eax, 012345678h
mov ebx, 0abcdeffeh
mov ecx, 1
mov edx, 2
; 012345678h送入eax
;
0abcdeffeh送入ebx
;
000000001h送入ecx
;
000000002h送入edx 

则执行上述程序段之后,寄存器的内容变为:

  ? ?X
?H ?L
EAX 1234/0001,0010,0011,0100 56/0101,0110 78/0111,1000
EBX abcd ef fe
ECX 0000 00 01
EDX 0000 00 02

那么,你已经了解了mov这个指令(mov是move的缩写)的一种用法。它可以将数送到寄存器中。我们来看看下面的代码:

mov eax, ebx
mov ecx, edx
; ebx内容送入eax
; edx
内容送入ecx 

则寄存器内容变为:

  ? X
H L
EAX abcd ef fe
EBX abcd ef fe
ECX 0000 00 02
EDX 0000 00 02

我们可以看到,“move”之后,数据依然保存在原来的寄存器中。不妨把mov指令理解为“送入”,或“装入”。

下面我们将介绍一些指令。在介绍指令之前,我们约定:

   使用Intel文档中的寄存器表示方式

  • reg32 32-bit寄存器(表示EAX、EBX等)
  • reg16 16-bit寄存器(在32位处理器中,这AX、BX等)
  • reg8  8-bit寄存器(表示AL、BH等)
  • imm32 32-bit立即数(可以理解为常数)
  • imm16 16-bit立即数
  • imm8  8-bit立即数

在寄存器中载入另一寄存器,或立即数的值:

mov reg32, (reg32 | imm8 | imm16 | imm32)
mov reg32, (reg16 | imm8 | imm16)
mov reg8, (reg8 | imm8)

例如,mov eax, 010h表示,在eax中载入00000010h。需要注意的是,如果你希望在寄存器中装入0,则有一种更快的方法,在后面我们将提到。

交换寄存器的内容:

xchg reg32, reg32
xchg reg16, reg16
xchg reg8, reg8

例如,xchg ebx, ecx,则ebx与ecx的数值将被交换。由于系统提供了这个指令,因此,采用其他方法交换时,速度将会较慢,并需要占用更多的存储空间,编程时要避免这种情况,即,尽量利用系统提供的指令,因为多数情况下,这意味着更小、更快的代码,同时也杜绝了错误(如果说Intel的CPU在交换寄存器内容的时候也会出错,那么它就不用卖CPU了。而对于你来说,检查一行代码的正确性也显然比检查更多代码的正确性要容易)刚才的习题的程序用下面的代码将更有效:

mov eax, 0a1234h
mov bx, ax
xchg ah, al
; 0a1234h送入eax
;
ax内容送入bx
;
交换ah, al的内容 

递增或递减寄存器的值:

我们假定ax的值为8

inc reg(8,16,32) //inc ax
dec reg(8,16,32) //dec ax

这两个指令往往用于循环中对指针的操作。需要说明的是,某些时候我们有更好的方法来处理循环,例如使用loop指令,或rep前缀。这些将在后面的章节中介绍。

将寄存器的数值与另一寄存器,或立即数的值相加,并存回此寄存器:

add reg32, reg32 / imm(8,16,32)
add reg16, reg16 / imm(8,16)
add reg8, reg8 / imm(8)

例如,add eax, edx,将eax+edx的值存入eax。减法指令和加法类似,只是将add换成sub eax, edx。

需要说明的是,与高级语言不同,汇编语言中,如果要计算两数之和(差、积、商,或一般地说,运算结果),那么必然有一个寄存器被用来保存结果。在PASCAL中,我们可以用nA := nB + nC来让nA保存nB+nC的结果,然而,汇编语言并不提供这种方法。如果你希望保持寄存器中的结果,需要用另外的指令。这也从另一个侧面反映了“寄存器”这个名字的意义。数据只是“寄存”在那里。如果你需要保存数据,那么需要将它放到内存或其他地方。

类似的指令还有and、or、xor(与,或,异或)等等。它们进行的是逻辑运算。

我们称add、mov、sub、and等称为为指令助记符(这么叫是因为它比机器语言容易记忆,而起作用就是方便人记忆,某些资料中也称为指令、操作码、opcode[operation code]等);后面的参数成为操作数,一个指令可以没有操作数,也可以有一两个操作数,通常有一个操作数的指令,这个操作数就是它的操作对象;而两个参数的指令,前一个操作数一般是保存操作结果的地方,而后一个是附加的参数。

使用sub eax, eax,或者xor eax, eax,可以得到与mov eax, 0类似的效果。在高级语言中,你大概不会选择用a=a-a来给a赋值,因为测试会告诉你这么做更慢,简直就是在自找麻烦,然而在汇编语言中,你会得到相反的结论,多数情况下,以由快到慢的速度排列,这三条指令将是xor eax, eax、sub eax, eax和mov eax, 0。

我们反复强调,寄存器是CPU的一部分。从寄存器取数,其速度很显然要比从内存中取数快。那么,不难理解,xor eax, eax要比mov eax, 0更快一些。

那么,为什么a=a-a通常要比a=0慢一些呢?这和编译器的优化有一定关系。多数编译器会把a=a-a翻译成类似下面的代码(通常,高级语言通过ebp和偏移量来访问局部变量;程序中,x为a相对于本地堆的偏移量,在只包含一个32-bit整形变量的程序中,这个值通常是4):

mov eax, dword ptr [ebp-x]
sub eax,
dword ptr [ebp-x]
mov
dword ptr [ebp-x],eax

而把a=0翻译成

mov dword ptr [ebp-x], 0

上面的翻译只是示意性的,略去了很多必要的步骤,如保护寄存器内容、恢复等等。

例子

下面我们将介绍一些指令。在介绍指令之前,我们约定: 


   使用Intel文档中的寄存器表示方式

  • reg32 32-bit寄存器(表示EAXEBX等)  
  • reg16 16-bit寄存器(在32位处理器中,这AXBX等)
  • reg8  8-bit寄存器(表示ALBH等)
  • imm32 32-bit立即数(可以理解为常数)
  • imm16 16-bit立即数
  • imm8  8-bit立即数  

在寄存器中载入另一寄存器,或立即数的值:

mov reg32, (reg32 | imm8 | imm16 | imm32)
mov reg32, (reg16 | imm8 | imm16)
mov reg8, (reg8 | imm8)

例如,mov eax, 010h表示,在eax中载入00000010h。需要注意的是,如果你希望在寄存器中装入0,则有一种更快的方法,在后面我们将提到。

交换寄存器的内容:

xchg reg32, reg32
xchg reg16, reg16
xchg reg8, reg8

例如,xchg ebx, ecx,则ebxecx的数值将被交换。由于系统提供了这个指令,因此,采用其他方法交换时,速度将会较慢,并需要占用更多的存储空间,编程时要避免这种情况,即,尽量利用系统提供的指令,因为多数情况下,这意味着更小、更快的代码,同时也杜绝了错误(如果说IntelCPU在交换寄存器内容的时候也会出错,那么它就不用卖CPU了。而对于你来说,检查一行代码的正确性也显然比检查更多代码的正确性要容易)刚才的习题的程序用下面的代码将更有效:

mov eax, 0a1234h
mov bx, ax
xchg ah, al

; 0a1234h送入eax
;
ax内容送入bx
;
交换ah, al的内容

递增或递减寄存器的值:

我们假定ax的值为8

inc reg(8,16,32) //inc ax
dec reg(8,16,32) //dec ax

这两个指令往往用于循环中对指针的操作。需要说明的是,某些时候我们有更好的方法来处理循环,例如使用loop指令,或rep前缀。这些将在后面的章节中介绍。

将寄存器的数值与另一寄存器,或立即数的值相加,并存回此寄存器:

add reg32, reg32 / imm(8,16,32)
add reg16, reg16 / imm(8,16)
add reg8, reg8 / imm(8)

例如,add eax, edx,将eax+edx的值存入eax。减法指令和加法类似,只是将add换成sub eax, edx

需要说明的是,与高级语言不同,汇编语言中,如果要计算两数之和(差、积、商,或一般地说,运算结果),那么必然有一个寄存器被用来保存结果。在PASCAL中,我们可以用nA := nB + nC来让nA保存nB+nC的结果,然而,汇编语言并不提供这种方法。如果你希望保持寄存器中的结果,需要用另外的指令。这也从另一个侧面反映了“寄存器”这个名字的意义。数据只是“寄存”在那里。如果你需要保存数据,那么需要将它放到内存或其他地方。

类似的指令还有andorxor(与,或,异或)等等。它们进行的是逻辑运算。

我们称addmovsuband等称为为指令助记符(这么叫是因为它比机器语言容易记忆,而起作用就是方便人记忆,某些资料中也称为指令、操作码、opcode[operation code]等);后面的参数成为操作数,一个指令可以没有操作数,也可以有一两个操作数,通常有一个操作数的指令,这个操作数就是它的操作对象;而两个参数的指令,前一个操作数一般是保存操作结果的地方,而后一个是附加的参数。

使用sub eax, eax,或者xor eax, eax,可以得到与mov eax, 0类似的效果。在高级语言中,你大概不会选择用a=a-a来给a赋值,因为测试会告诉你这么做更慢,简直就是在自找麻烦,然而在汇编语言中,你会得到相反的结论,多数情况下,以由快到慢的速度排列,这三条指令将是xor eax, eaxsub eax, eaxmov eax, 0

我们反复强调,寄存器是CPU的一部分。从寄存器取数,其速度很显然要比从内存中取数快。那么,不难理解,xor eax, eax要比mov eax, 0更快一些。

那么,为什么a=a-a通常要比a=0慢一些呢?这和编译器的优化有一定关系。多数编译器会把a=a-a翻译成类似下面的代码(通常,高级语言通过ebp和偏移量来访问局部变量;程序中,xa相对于本地堆的偏移量,在只包含一个32-bit整形变量的程序中,这个值通常是4)

mov eax, dword ptr [ebp-x]
sub eax,
dword ptr
[ebp-x]
mov
dword ptr [ebp-x],eax

而把a=0翻译成

mov dword ptr [ebp-x], 0

上面的翻译只是示意性的,略去了很多必要的步骤,如保护寄存器内容、恢复等等。

例子

1: int myTransform(int nInput){

00401000 push ebp                   ; 保护现场原先的EBP指针
00401001 mov ebp,esp

2: return (nInput*2 + 3)% 7;

00401003 mov eax,dword ptr [nInput] ; 取参数
00401006 lea eax,[eax*2+3]        ; LEA比ADD加法更快
0040100A cdq                        ; DWORD->QWORD(扩展字长)
0040100B mov ecx,7                  ; 除数
00401010 idiv eax,ecx               ; 除
00401012 mov eax,edx                ; 商->eax(eax中保存返回值)

3:}

00401014 pop ebp                    ; 恢复现场的ebp指针
00401015 ret                        ; 返回
; 此处删除10条int 3指令,它们是方便调试用的,并不影响程序行为。
4:
5: int main(int argc, char* argv[])
6: {
00401020 push ebp                   ; 保护现场原先的EBP指针
00401021 mov ebp,esp
00401023 sub esp,10h                ; 为取argc, argv修正堆栈指针。
7: int a[3];
8: for(register int i=0; i<3; i++){
00401026 mov dword ptr [i],0        ; 0->i
0040102D jmp main+18h (00401038)    ; 判断循环条件
0040102F mov eax,dword ptr [i]      ; i->eax
00401032 add eax,1                  ; eax ++
00401035 mov dword ptr [i],eax      ; eax->i
00401038 cmp dword ptr [i],3        ; 循环条件: i与3比较
0040103C jge main+33h (00401053)    ; 如果不符合条件,则应结束循环
9: a[i] = myTransform(i);
0040103E mov ecx,dword ptr [i]      ; i->ecx
00401041 push ecx                   ; ecx (i) -> 堆栈
00401042 call myTransform (00401000); 调用myTransform
00401047 add esp,4                  ; esp+=4: 在堆中的新单元
                                    ; 准备存放返回结果
0040104A mov edx,dword ptr [i]      ; i->edx
0040104D mov dword ptr a[edx*4],eax ; 将eax(myTransform返回值)
                                    ; 放回a[i]
10: }
00401051 jmp main+0Fh (0040102f)    ; 计算i++,并继续循环
11: return 0;
00401053 xor eax,eax                ; 返回值应该是0
12: }
00401055 mov esp,ebp                ; 恢复堆栈指针
00401057 pop ebp                    ; 恢复BP
00401058 ret                        ; 返回调用者(C++运行环境)

分类: 学习笔记 标签:
本文的评论功能被关闭了.

苏公网安备 32020202000090号

苏ICP备16040328号     点击这里给我发消息站长



普人特福的博客cnzz&51la for wordpress,cnzz for wordpress,51la for wordpress