一些汇编指令和寄存器。

  天学习C中对于一些代码中,执行原理不是清楚的了解,如简单的字符型char数据,编译器是如何处理的。以及等等很多只看过程远远不能清楚了解内部原理,对于学习有很深的障碍。所有有必要去学习基础汇编与寄存器的知识,通过编译器的反汇编来了解。

  16位寄存器:

4个数据寄存器(AX,BX,CX,DX)               2个变址和指针寄存器(SI,DI)2个指针寄存器(SP,BP)

4个段寄存器(ES,CS,DS,SS)                1个指令指针寄存器(IP)1个标志寄存器(Flags)

  32位寄存器:

4个数据寄存器(EAX,EBX,ECX,EDX)     2个变址和指针寄存器(ESI,EDI)        2个指针寄存器(ESP,EBP)

6个段寄存器(ES、CS、SS、DS、FS、GS)    1个指令指针寄存器(EIP)      1个标志寄存器(EFlags)


一、数据寄存器:

            AX通常称为累加器,用于乘、除运算、字的输出输入、中间结果的缓存。

            BX通常称为基地址寄存器,用于作为存储器指针来使用。

            CX通常计数寄存器,在循环和字符串操作时,要用它来控制循环次数,在位操作中,当移
多位时,要用CL来指明移位的位数。

            DX通常称为数据寄存器,在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存
放I/O的端口地址。

   在16位CPU中,AX、BX、CX和DX不能作为基址和变址寄存器来存放存储单元的地址,在32位CPU中,其32位寄存器EAX、EBX、ECX和EDX不仅可传送数据、暂存数据,保存算术逻辑运算结果,而且也可作为指针寄存器。

二、变址寄存器:

             32位CPU的中两个寄存器ESI、EDI称为变址寄存器。它们主要用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。

三、指针寄存器:

             32位CPU中EBP、ESP称为指针寄存器。主要用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便。

注:

       BP为基指针寄存器,用它可直接存取堆栈中的数据。

       SP为堆栈指针寄存器,用它只可访问栈顶。

四、指令指针寄存器:

              32位CPU中EIP为指令指针寄存器。指令指针EIP是存放下次将要执行的指令在代码段的偏移量。在具有预取指令功能的系统中,下次要执行的指令通常已被预取到指令队列中,除非发生转移情况。


一些汇编指令

mov指令

              1、普通的mov指令

              2、做符号扩展的movs

              3、做零扩展的movz

类型:

普通的mov指令有 movb(完成一个字节的复制)movw(完成两个字节的复制)

                             movl(完成4个字节的复制)   movq(完成8个字节的复制)

movs指令:         movsbw    作符号扩展的1字节复制到2字节
    movsbl      作符号扩展的1字节复制到4字节
                            movsbq     作符号扩展的1字节复制到8字节
                            movswl      作符号扩展的2字节复制到4字节
                            movswq     作符号扩展的2字节复制到8字节
                            movslq       作符号扩展的4字节复制到8字节

movz指令:

      movzbw     作0扩展的1字节复制到2字节
movzbl
      作0扩展的1字节复制到4字节
movzbq
     作0扩展的1字节复制到8字节
movzwl
      作0扩展的2字节复制到4字节
movzwq
     作0扩展的2字节复制到8字节
movzlq
       作0扩展的4字节复制到8字节


为什么使用movs指令(符号扩展指令)


转:

  如果要完成下面的c语言代

char c = -1;
int i = c;

如果翻译成下面的汇编代码,会发现一个问题

      用movb把%al寄存器里的-1,复制到%ebx寄存器,结果变成了255。等等,为什么会这样?

.section .text
.global _start

fmt:
    .ascii "%d\n\0"

_start:
    movb $-1, %al      #把-1赋值到寄存器al
    xorl %ebx, %ebx    #把寄存器%ebx 赋值为0
    movb %al, %bl      #把al的值赋值到%ebx寄存器的低8位 (引用%ebx寄存器低8位的方法就是使用%bl寄存器)

    xorq %rax, %rax
    movl %ebx, %esi
    movq $fmt, %rdi
    call printf        #调用printf 打印ebx寄存器的内容,会发现输出变成了255

    movl $0, %edi      #调用exit退出进程
    call exit

接上段,学过原码,补码,反码的同学知道,在二进制的角度看待一个数。其在内存中表示正数,表示负数,依赖机器是怎么解释最高bit位的1。c语言里面signed类型,如果最高bit为1,认为它是一个负数。unsigned类型,始终认为是正数。

回到刚刚的-1变成255的问题。

作为用户,只想在由char 类型转为int,输出还是-1,就这么简单。

(事实上c语言已经做了自动转换,这里的char,int只是指代上面的汇编代码里的类型)

char类型的-1在内存中的表示:11111111 

(由于最高bit位为1,且类型为signed,所以解释成-1)

使用movb指令把char类型的-1复制到int类型里:

char类型的-1复制到int类型在内存中的表示:00000000000000000000000011111111

(由于最高bit位为0,且类型为signed,所以解释成255)

int类型的-1在内存中的表示:11111111111111111111111111111111

两边一对比就知道,char复制到int需要把多出来的字节作符号位扩展

 如果要完成下面的c语言代码:

unsigned char c = -1;
unsigned i = c;
printf("%x:%d\n", i, i); //输出 0xff, 255
这时候就是movz指令大显身手的时候

movb $-1, %al      #%al  = 0xff
movzbl %al, %ebx   #%ebx = 0x000000ff



使用:

              1.CPU内部寄存器之间数的任意传送(除了码段寄存器CS和指令指针IP以外)。

              2.立即数传送至CPU内部的通用寄存器组(即AX、BX、CX、DX、BP、SP、SI、DI)给这些寄存器赋初值。

              3.CPU内部寄存器(除了CS和IP以外)与存储器(所有寻址方式)之间的数据传送,可以实现一个字节或一个字的传送。

              4.能实现立即数给寄存器存储单元赋值。

eg:

      MOV CL,4 ;CL←4,字节传送

     MOV DX,0FFH ;DX←00FFH,字传送

     MOV SI,200H ;SI←0200H,字传送

     MOV BVAR,0AH ;字节传送 

    假设BVAR是一个字节变量,定义如下:BVAR  DB 0

     MOV WVAR,0BH ;字传送 

    假设wvar是一个字变量,定义如下:wvar  dw 0

寄存器传送

   mov ah,al ;ah←al,字节传送

   mov bvar,ch ;bvar←ch ,字节传送

   mov ax,bx ;ax←bx,字传送

   mov ds,ax ;ds←ax,字传送

   mov [bx],al ;[bx]←al,字节传送

存储器传送

   mov al,[bx] ;al←ds:[bx]

   mov dx,[bp] ;dx←ss:[bp+0]

   mov dx,[bp+4] ;dx←ss:[bp+4]

   mov es,[si] ;es←ds:[si]

段寄存器传送

   MOV [SI],DS

   MOV AX,DS ;AX←DS

   MOV ES,AX ;ES←AX←DS 



注: 

              1.MOV指令不能在两个寄存器之间进行数据直接传送。

              2.MOV指令不能在两个段寄存器之间进行数据直接传送。

              3.立即数不能直接传送给寄存器。

非法指令的主要现象:

       两个操作数的类型不一致

      无法确定是字节量还是字量操作

       两个操作数都是存储器

段寄存器的操作有一些限制 

      ① 目的操作数不能是立即寻址方式。

      ② 源操作数与目的操作数不能同时为存储器寻址方式,即两个内存单元之间不能直接传送数据。

      ③ 立即数不能直接送段寄存器,即段寄存器只能通过寄存器或存储单元传送数据。

      ④ 两个段寄存器之间不允许直接传送数据。

      ⑤ 不允许给CSIPPSW三个寄存器传送数据,即这3个寄存器的值用户无权改变。

      ⑥ 源操作数和目的操作数必须字长相等。

      ⑦  MOV指令不影响标志位。

交换指令

   通用寄存器与通用寄存器之间交换

   通用寄存器与累加器之间交换

   通用寄存器或存储器之间交换

  不能在两存储单元之间交换,段寄存器与指令指针IP也不能作为一个源或目标操作数。


lea指令

     取[寄存器]的值

             mov    eax,4;

             lea      ebx,[eax];//执行后ebx=2

    对于寄存器来说:第二个操作数是寄存器必须要加[],不然报错,这里lea就是取[寄存器]的值,如: mov eax,2 
            lea ebx,[eax];执行后ebx=2 

            mov ebx,eax;等同于上句 
            lea ebx,eax;编译器报错: error A2070: 
invalid instruction operands 

    对于变量来说加不加[]都是一样的效果,都是取变量的地址,相当于指针 如: 

            num dword 2;

            lea ebx,num ;
            lea eax,[num]; eax为num的地址,如eax=4206598,随程序不同不同,这时ebx==eax

mov对于变量来说 

            num dword 2;

            mov eax,2;

            mov ebx,num; 

            mov ecx,[num];执行完ebx==ecx==2   
mov中对寄存器 
            mov ebx,eax;ebx==2 
            mov ecx,[eax];可能会报错,因为这里翻译成汇编是mov ecx,DS:[eax] 

lea对变量来说(无影响)取地址,对寄存器来说加[]时取值,不加[]时非法。

mov对变量来说(无影响)取值,对寄存器来说加[]取地址,不加[]是取值。



push指令

            进栈指令 PUSH (push onto the stack)

    出栈指令 POP (pop from the stack)

    指令的汇编格式:PUSH SRC ;POP DST

            指令的基本功能:PUSH指令在程序中常用来暂存某些数据,而POP指令又可将这些数据恢复。

                   PUSH SRC (SP)<-(SP)-2 ;   (SP)<-(SRC)

   POP DST (DST)<-((SP));   (SP)<-(SP)

            指令支持的寻址方式:push 和 pop指令不能不能使用立即数寻址方式。

转:

进栈指令push

push reg/mem/seg;sp<-sp-2,ss<-reg/mem/seg

进栈指令先使堆栈指令sp减2,然后把一个字操作数存入堆栈顶部。堆栈操作的对象只能是字操作数,进栈时底字节存放于低地址,高字节存放于高地址,sp相应向低地址移动两个字节单元。

push AX
PUSH [2000H]
PUSH CS
出栈指令pop
pop reg/seg/mem;reg/seg/mem<-ss:[sp],sp<-sp+2
出栈指令把栈顶的一个字传送至指定的目的操作数,然后堆栈指针sp加2。目的操作数应为字操作数,字从栈顶弹出时,低地址字节送低字节,高地址字节送高字节。
pop AX
POP [2000H]
POP SS堆栈可以用来临时存放数据,以便随时恢复它们。也常用于子程序见传递参数。
注意几点:
(1)、因为堆栈指针sp总是指向已经存入数据的栈顶(不是空单元),所以PUSH指令是将(SP)减2,后将内容压栈(即先修改SP是指指向空单元,后压入数据),而POP是先从栈顶弹出一个字,后将堆栈指针SP加2.
(2)、PUSH CS是合法的,但是POP CS是不合法的。
(3)、因为SP总是指向栈顶,而用PUSH和POP指令存取数时都是在栈顶进行的,所以堆栈是先进后出或叫后进先出的。栈底在高地址,堆栈是从高地址向低地址延伸的,所有栈底就是最初的栈顶。
(4)、用PUSH指令和POP指令时只能按字访问堆栈,不能按字节访问堆栈。
(5)、PUSH和POP指令都不影响标志。

call指令与ret指令

         call与ret都是转移指令,它们可以改变IP值,或者同时改变CS与IP的值,往往在程序中使用它们进行子程序模块的设计。

转:

ret用栈的数据修改IP的内容,实现近转移
retf用栈中的数据修改CS与IP的内容,实现远转移

ret执行步骤:
(1):(IP)=((SS)*16+SP)
(2):(SP)=(SP)+2

retf执行步骤:
(1):(IP)=((SS)*16+SP)
(2):(SP)=(SP)+2
(3):(CS)=((SS)*16+SP)
(4):(SP)=(SP)+2

可以将ret理解为:POP IP
可以将retf理解为:POP IP  POP CS

CALL指令的执行步骤(call不能实现短转移):
(1)将当前的IP或CS与IP压入栈中
(2)转移
call 标号(将当前IP压栈,转移到标号处执行)
执行步骤:
(1) (sp)=(sp)-2
    ((ss)*16+(sp))=(ip)   (实现当前IP值压栈)

(2)(ip)=(ip)+16位位移    (转移到标号处)
  16位位移=标号处地址-call指令后的第一个字节的地址
  位移范围为-32768-32767由补码给出
  位移在编译时计算
  call 标号 相当于  call near ptr 标号


              

          

             













  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值