汇编语言 订阅
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。 [1] 展开全文
汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。 [1]
信息
产生年代
20世纪50年代
外文名
Assembly Language
编译方式
汇编
中文名
汇编语言
学    科
软件工程
汇编语言简介
汇编语言, 即第二代计算机语言,用一些容易理解和记忆的字母,单词来代替一个特定的指令,比如:用“ADD”代表数字逻辑上的加减,“ MOV”代表数据传递等等,通过这种方法,人们很容易去阅读已经完成的程序或者理解程序正在执行的功能,对现有程序的bug修复以及运营维护都变得更加简单方便。当计算机的硬件不认识字母符号,这时候就需要一个专门的程序把这些字符变成计算机能够识别的二进制数。因为汇编语言只是将机器语言做了简单编译,所以并没有根本上解决机器语言的特定性,所以汇编语言和机器自身的编程环境息息相关,推广和移植很难,但是还是保持了机器语言优秀的执行效率,因为他的可阅读性和简便性,汇编语言到现在依然是常用的编程语言之一。 [2]  汇编语言不像其他大多数的程序设计语言一样被广泛用于程序设计。在今天的实际应用中,它通常被应用在底层,硬件操作和高要求的程序优化的场合。驱动程序、嵌入式操作系统和实时运行程序都需要汇编语言。 [1] 
收起全文
精华内容
下载资源
问答
  • 汇编语言(王爽)实验十 编写子程序

    万次阅读 多人点赞 2016-07-28 16:09:21
    标 题:汇编实验10—— 编写子程序 作 者: XHS_12302 时 间: 链 接: 实验10编写子程序 在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。同前面的所有实验一样,...

    标 题: 汇编实验10—— 编写子程序
    作 者: XHS_12302
    时 间: 2016_7_28 16:56

     

     

     

     

     

    实验10编写子程序


      在这次实验中,我们将要编写3个子程序,通过它们来认识几个常见的问题和掌握解决这些问题的方法。同前面的所有实验一样,这个实验是必须要独立完成的,在后面的课程中,将要用到这个实验中编写的3个子程序。
    1.  显示字符串
    问题
    显示字符串是现实工作中经常要用到的功能,应该编写一个通用的子程序来实现这个功能。我们应该提供灵活的调用接口,使调用者可以决定显示的位置(行、列)、内容和颜色。

    提示
    (1)  子程序的入口参数是屏幕上的行号和列号,注意在子程序内部要将它们转化为显存中的地址,首先要分析一下屏幕上的行列位置和显存地址的对应关系:
    (2)  注意保存子程序中用到的相关寄存器:
    (3)  这个子程序的内部处理和显存的结构密切相关,但是向外提供了与显存结构无关的接口。通过调用这个子程序,进行字符串的显示时可以不必了解显存的结构,为编程提供了方便。在实验中,注意体会这种设计思想。

    子程序描述
    名称:show_str
    功能:在指定的位置,用指定的颜色,显示一个用0结束的字符串。
    参数:(dh)=行号(取值范围0~24),(dl)=列号(取值范围0~79),
        (cl)=颜色,ds:si指向字符串的首地址
    返回:无
    就用举例:在屏幕的8行3列,用绿色显示data段中的字符串。

     

     

     

     

    代码:

     

    assume cs:code
       data segment
       db 'Welcome to masm!',0
       data ends
    
     code segment
       start:  mov dh,8
               mov dl,3
               mov cl,2
               mov ax,data
               mov ds,ax
               mov si,0
               call show_str
               mov ax,4c00h
               int 21h
       show_str: push dx
                 push cx
                 push ds
                 push si
    
         mov ax,0b800h
         mov es,ax
         
         mov al,160
         mul dh
         mov bx,ax
         mov al,2
         mul dl
         add bx,ax
        mov al,cl
        
             s: mov cl,[si] 
                  jcxz ok
                  mov dx,[si]
                  mov es:[bx],dx
                  mov es:[bx+1],al
                  inc si
                  add bx,2
                  loop s
    
     ok:             
      pop si
      pop ds
      pop cx
      pop dx
    ret
    code ends
    end start 

     

     

    实验截图

     

     

     

    2.  解决除法溢出的问题
    问题
    前面讲过,div指令可以做除法。当进行8位除法的时候,用al存储结果的商,ah存储结果的余数:进行16位除法的时候,用ax存储结果的商,dx存储结果的余数。可是,现在有一个问题,如果结果的商大于ah或ax所能存储的最大值,那么将如何?
    比如,下面的程序段:
     mov bh,1
     mov ax,1000
     div bh
    进行的是8位除法,结果的商为1000,而1000在ah中放不下,
    又比如,下面的程序段:
    mov ax,1000h
    mov dx,1
    mov bx,1
    div bx
    进行的是16位除法,结果的商为11000H,而11000H在ax中存放不下。
    我们在用div指令做除法的时候,很可能发生上面的情况:结果的商过大,超出了寄存器所能存储的范围。当CPU执行div等除法指令的时候。如果发生这样的情况,将引发CPU的一个内部错误。这个错误被称为:除法溢出。我们可以通过特殊的程序来处理这个错误, 这里我们不讨论这个错误的处理,这是后面的课程中要涉及的内容。

    好了,我们已经清楚了问题的所在:用div指令做除法的时候可能产生除法溢出。由于有这样的问题,在进行除法运算的时候要注意除数和被除数的值,比如1000000/10就不能用div指令来计算。那么怎么办呢?我们用下面的子程序divdw解决。


    子程序描述
    名称:divdw
    功能:进行不会产生溢出的除法运算,被除数为dword型,除数为word型,结果为dword型。
    参数:(ax)=dword型数据的低16位
        (dx)=dword型数据的高16位
        (cx)=除数
    返回:(dx)=结果的高16位,(ax)=结果的低16位
        (cx)=余数
    应用举例:计算1000000/10(F4240H/0AH)

      mov ax,4240h
      mov v dx,000fh
      mov cx,0ah
      call divdw


    提示
    给出一个公式:
    X:被除数,范围:[0,FFFF FFFF]
    N:除数,范围:[0,FFFF]
    H:X高16位,范围:[0,FFFF]
    L:X低16位,范围:[0,FFFF]
    int():描述性运算符,取商,比如:rem(38/10)=8
    rem():描述性运算符,取答数,比如:rem(38/10)=8
    公式:X/N=int(H/N)*65536+[rem(H/N)*65536+L]/N
    这个公式将可能产生溢出的除法运算:X/N,转变为多个不会产生溢出的除法运算。
    公式中,等号右边的所有除法运算都可以用div指令来做,肯定不会导致除法溢出。

    代码:

     

    assume cs:code
        code segment
        start:mov ax,4240h
              mov dx,000fh
              mov cx,0Ah
              call divdw
              mov ax,4c00H
              int 21h
    divdw:
          push ax
          mov ax,dx
          mov dx,0
          div cx
          mov bx,ax
          pop ax
          div cx
          mov cx,dx
          mov dx,bx
          ret    
        code ends
    end start

     

     

     

    程序截图:

     

    3.数值显示
    问题
    编程,将data段中的数据以十进制的形式显示出来。
    data segment
    dw 123,12666,1,8,3,38
    data ends
      这些数据在内存中都是二进制信息,标记了数值的大小。要把它们显示到屏幕上,成为我们能够读懂的信息,需要进行信息的转化。比如,数值12666,在机器中存储为二进制信息:0011000101111010B(317AH),计算机可以理解它。而我们要在显示器上读到可以理解的数值12666,我们看到的应该是一串字符:“12666”。由于 显卡遵循的是ASCII编码,为了让我们能在显示器上看到这串字符,它在机器中应以ASCII码的形式存储为:31H、32H、36H、36H、36H(字符“0”~“9”对应的ASCII码为30H~39H)。
      通过上面的分析可以看到,在概念世界中,有一个抽象的数据12666,它表示了一个数值的大小。在现实世界中它可以有多种表示形式,可以在电子机器中以高低电平(二进制)的形式存储,也可以在纸上、黑板上、屏幕上以人类的语言“12666”来书写。现在,我们面临的问题就是,要将同一抽象的数据,从一种表示形式转化为另一种表示形式。
      可见,要将数据用十进制形式显示到屏幕上,要进行两步工作:
    (1)  将用二进制信息存储的数据转变为十进制形式的字符串:
    (2)  显示十进制形式的字符串。
    第二步我们在本次实验的第一个子程序中已经实现,在这里只要调用一下show_str即可。我们来讨论第一步,因为将二进制信息转变为十进制形式的字符串也是经常要用到的功能,我们应该为它编写一个通用的子程序。


    子程序描述
    名称:dtoc
    功能:将word型数据转变为表示十进制数的字符串,字符串以0为结尾符。
    参数:(ax)=word型数据
        ds:si指向字符串的首地址
    返回:无
    应用举例:编程,将数据12666以十进制的形式在屏幕的8行3列,用绿色显示出来。
    在显示时我们调用本次实验中的第一个子程序show-str。



    提示
      下面我们对这个问题进行一下简单地分析。
    (1)  要得到字符串“12666”,就是要得到一列表示该字符的ASCII码:31H、32H、36H、36H、36H。
    十进制数码字符对应的ASCII码=十进制数码值+30H
    要得到表示十进制数的字符串,先求十进制数每位的值。
    例:对于12666,先求得每位的值:1、2、6、6、6。再将这些数分别加上30H,便得到了表示12666的ASCII码串:31H、32H、36H、36H、36H。
    (2)  那么,怎样得到每位的值呢?采用如图10.2所示的方法。

    图10.2除10取余法示意


      可见,用10除12666,共除5次,记下每次的余数,就得到了每位的值。
    (3)  综合以上分析,可得出处理过程如下:
    用12666除以10,循环5次,记下每次的余数;将每次的余数分别加30H,便得到了表示十进制数的ASCII码串,如图10.3所示。



    图10.3得到十进制每位数字字符的方法示意

     


    (4)  对(3)的质疑:
    在已知数据是12666的情况下,知道进行5次循环。可在实际问题中,数据的值是多少
    程序员并不知道,也就是说,程序员不能事先确定循环次数。
      那么,如何确定数据各位的值已经全部求出了呢?我们可以看出,只要是除到商为0,各位的值就已经全部求出。可以使用jcxz指令来实现相关的功能。

     

     

     

     

     

    代码:

     

    assume cs:code
      data segment
      db 16 dup(0)
      data ends
    
     
      code segment
        start:mov ax,12666
              mov bx,data
              mov ds,bx
              mov si,0
              call dtoc
              mov dh,8
              mov dl,3
              mov cl,2
              call show_str
              mov ax,4c00h
              int 21h
    dtoc:
           mov cx,ax    ;17
           jcxz bk
           push ax
           mov al,ah
           mov ah,0
           mov bl,10
           div bl
           mov cl,al
           mov ch,ah
           pop ax
           mov ah,ch
           div bl
           mov dl,ah
           mov dh,0
           push dx
           mov ah,cl
           jmp short dtoc   ;29
         bk:pop ax 
            add ax,30h
            mov [si],al
            
            pop ax 
            add ax,30h
            mov [si+1],al
           
            pop ax 
            add ax,30h
            mov [si+2],al
            
            pop ax 
            add ax,30h
            mov [si+3],al    ;44
            
            pop ax 
            add ax,30h
            mov [si+4],al
            mov byte ptr [si+5],0
            ret
           
         
    show_str:
         mov si,0
         mov ax,0b800h
         mov es,ax
         
         mov al,160
         mul dh
         mov bx,ax
         mov al,2
         mul dl
         add bx,ax
         mov al,cl
        
             s: mov cl,[si] 
                  jcxz ok
                  mov dx,[si]
                  mov es:[bx],dx
                  mov es:[bx+1],al
                  inc si
                  add bx,2
                  loop s
    
     ok: ret    
      code ends
    end start


    程序运行截图

     

     

    至此    实验完成    

                               题中有不足之处  希望不吝赐教

                                             文中有的 文字是借鉴别人的    除了代码和截图

                                                 完成实验参考过别的资料,在第二个实验中受过小甲鱼视频(328/2)的启发

                                                                                                   视频资料上云盘找:链接: http://pan.baidu.com/s/1nvpQiLz 密码: et5r

                                                                                                                                                                                                                                     日期:2016_7_28

    展开全文
  • 汇编语言

    千次阅读 2019-09-18 21:52:55
    汇编语言 汇编语言是一类语言,ARM,MIPS,X86。 汇编语言与CPU联系紧密。但是可移植性差。 Li $t1 , 1 # 把1放到t1寄存器里面 add $t0 , $t1 ,2 # 2加上ti里面的放到t0中 ...

    汇编语言

    汇编语言是一类语言,ARM,MIPS,X86。
    汇编语言与CPU联系紧密。但是可移植性差。

     Li $t1 , 1   # 把1放到t1寄存器里面
     add $t0 , $t1 ,2  # 2加上ti里面的放到t0中
    
    展开全文
  • 什么是汇编语言

    万次阅读 多人点赞 2018-11-19 21:21:37
    汇编语言(assembly language)是一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符(Mnemonics)代替机器指令的操作码,用地址符号(Symbol)或标号...

           汇编语言(assembly language)是一种用于电子计算机微处理器微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,助记符(Mnemonics)代替机器指令操作码,用地址符号(Symbol)或标号(Label)代替指令或操作数地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。普遍地说,特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。

           许多汇编程序为程序开发、汇编控制、辅助调试提供了额外的支持机制。有的汇编语言编程工具经常会提供宏,它们也被称为宏汇编器。

           汇编语言不像其他大多数的程序设计语言一样被广泛用于程序设计。在今天的实际应用中,它通常被应用在底层,硬件操作和高要求的程序优化的场合。驱动程序、嵌入式操作系统和实时运行程序都需要汇编语言。

            Microsoft 宏汇编器(称为 MASM)是windows下常用的汇编器。Microsoft Visual Studio 的大多数版本(专业版,旗舰版,精简版……)都包含 MASM。在运行 Microsoft Windows 的 x86 系统中,其他一些有名的汇编器包括:TASM(Turbo 汇编器),NASM(Netwide 汇编器)和 MASM32(MASM 的一种变体)。GAS(GNU 汇编器)和 NASM 是两种基于 Linux 的汇编器。在这些汇编器中,NASM 的语法与 MASM 的最相似。汇编语言最古老的编程语言,在所有的语言中,它与原生机器语言最为接近。它能直接访问计算机硬件,要求用户了解计算机架构和操作系统。

    什么是汇编器和链接器?

           汇编器(assembler)是一种工具程序,用于将汇编语言源程序转换为机器语言。链接器(linker)也是一种工具程序,它把汇编器生成的单个文件组合为一个可执行程序。还有一个相关的工具,称为调试器(debugger),使程序员可以在程序运行时,单步执行程序并检查寄存器和内存状态。

    MASM 能创建哪些类型的程序?

    32 位保护模式(32-Bit Protected Mode)32 位保护模式程序运行于所有的 32 位和 64 位版本的 Microsoft Windows 系统。它们通常比实模式程序更容易编写和理解。从现在开始,将其简称为 32 位模式。
    64 位模式(64-Bit Mode)64 位程序运行于所有的 64 位版本 Microsoft Windows 系统。
    16 位实地址模式(16-Bit Real-Address Mode)16 位程序运行于 32 位版本 Windows 和嵌入式系统。 64 位 Windows 不支持这类程序。

    汇编语言与机器语言有什么关系?

           机器语言(machine language)是一种数字语言, 专门设计成能被计算机处理器(CPU)理解。所有 x86 处理器都理解共同的机器语言。
    汇编语言(assembly language)包含用短助记符如 ADD、MOV、SUB 和 CALL 书写的语句。汇编语言与机器语言是一对一(one-to-one)的关系:每一条汇编语言指令对应一条机器语言指令。寄存器(register)是 CPU 中被命名的存储位置,用于保存操作的中间结果

    C++ 和 Java 与汇编语言有什么关系?

           高级语言如 Python、C++ 和 Java 与汇编语言和机器语言的关系是一对多(one-to-many)。比如,C++ 的一条语句就会扩展为多条汇编指令或机器指令。

    汇编语言可移植吗?

    一种语言,如果它的源程序能够在各种各样的计算机系统中进行编译和运行,那么这种语言被称为是可移植的(portable)。
    例如,一个 C++ 程序,除非需要特别引用某种操作系统的库函数,否则它就几乎可以在任何一台计算机上编译和运行。Java 语言的一大特点就是,其编译好的程序几乎能在所有计算机系统中运行。
    汇编语言不是可移植的,因为它是为特定处理器系列设计的。目前广泛使用的有多种不同的汇编语言,每一种都基于一个处理器系列。
    对于一些广为人知的处理器系列如 Motorola 68x00、x86、SUN Sparc、Vax 和 IBM-370,汇编语言指令会直接与该计算机体系结构相匹配,或者在执行时用一种被称为微代码解释器(microcode interpreter)的处理器内置程序来进行转换。

    发展历程

           说到汇编语言的产生,首先要讲一下机器语言。机器语言是机器指令的集合。机器指令展开来讲就是一台机器可以正确执行的命令。电子计算机的机器指令是一列二进制数字。计算机将之转变为一列高低电平,以使计算机的电子器件受到驱动,进行运算。

           上面所说的计算机指的是可以执行机器指令,进行运算的机器。这是早期计算机的概念。在我们常用的PC机中,有一个芯片来完成上面所说的计算机的功能。这个芯片就是我们常说的CPU(Central Processing Unit,中央处理单元)。每一种微处理器,由于硬件设计和内部结构的不同,就需要用不同的电平脉冲来控制,使它工作。所以每一种微处理器都有自己的机器指令集,也就是机器语言。CPU 只负责计算,本身不具备智能。你输入一条指令(instruction),它就运行一次,然后停下来,等待下一条指令。这些指令都是二进制的,称为操作码(opcode),比如加法指令就是00000011。编译器的作用,就是将高级语言写好的程序,翻译成一条条操作码。对于人类来说,二进制程序是不可读的,根本看不出来机器干了什么。为了解决可读性的问题,以及偶尔的编辑需求,就诞生了汇编语言。汇编语言是二进制指令的文本形式,与指令是一一对应的关系。比如,加法指令00000011写成汇编语言就是 ADD。只要还原成二进制,汇编语言就可以被 CPU 直接执行,所以它是最底层的低级语言

           早期的程序设计均使用机器语言。程序员们将用0, 1数字编成的程序代码打在纸带或卡片上,1打孔,0不打孔,再将程序通过纸带机或卡片机输入计算机,进行运算。这样的机器语言由纯粹的0和1构成,十分复杂,不方便阅读和修改,也容易产生错误。程序员们很快就发现了使用机器语言带来的麻烦,它们难于辨别和记忆,给整个产业的发展带来了障碍,于是汇编语言产生了。

           汇编语言的主体是汇编指令。汇编指令和机器指令的差别在于指令的表示方法上。汇编指令是机器指令便于记忆的书写格式。

    操作:寄存器BX的内容送到AX中
    1000100111011000              机器指令
    mov ax,bx                    汇编指令

           此后,程序员们就用汇编指令编写源程序。可是,计算机能读懂的只有机器指令,那么如何让计算机执行程序员用汇编指令编写的程序呢?这时,就需要有一个能够将汇编指令转换成机器指令的翻译程序,这样的程序我们称其为编译器。程序员用汇编语言写出源程序,再用汇编编译器将其编译为机器码,由计算机最终执行。 

    语言特点

           汇编语言是直接面向处理器(Processor)的程序设计语言。处理器是在指令的控制下工作的,处理器可以识别的每一条指令称为机器指令。每一种处理器都有自己可以识别的一整套指令,称为指令集。处理器执行指令时,根据不同的指令采取不同的动作,完成不同的功能,既可以改变自己内部的工作状态,也能控制其它外围电路的工作状态。

           汇编语言的另一个特点就是它所操作的对象不是具体的数据,而是寄存器或者存储器,也就是说它是直接和寄存器和存储器打交道,这也是为什么汇编语言的执行速度要比其它语言快,但同时这也使编程更加复杂,因为既然数据是存放在寄存器或存储器中,那么必然就存在着寻址方式,也就是用什么方法找到所需要的数据。例如上面的例子,我们就不能像高级语言一样直接使用数据,而是先要从相应的寄存器AX、BX 中把数据取出。这也就增加了编程的复杂性,因为在高级语言中寻址这部分工作是由编译系统来完成的,而在汇编语言中是由程序员自己来完成的,这无异增加了编程的复杂程度和程序的可读性。

           再者,汇编语言指令是机器指令的一种符号表示,而不同类型的CPU 有不同的机器指令系统,也就有不同的汇编语言,所以,汇编语言程序与机器有着密切的关系。所以,除了同系列、不同型号CPU 之间的汇编语言程序有一定程度的可移植性之外,其它不同类型(如:小型机和微机等)CPU 之间的汇编语言程序是无法移植的,也就是说,汇编语言程序的通用性和可移植性要比高级语言程序低。

           正因为汇编语言有“与机器相关性”的特性,程序员用汇编语言编写程序时,可充分对机器内部的各种资源进行合理的安排,让它们始终处于最佳的使用状态。这样编写出来的程序执行代码短、执行速度快。汇编语言是各种编程语言中与硬件关系最密切、最直接的一种,在时间和空间的效率上也最高的一种。

    总体特点

    1.机器相关性

           这是一种面向机器的低级语言,通常是为特定的计算机或系列计算机专门设计的。因为是机器指令的符号化表示,故不同的机器就有不同的汇编语言。使用汇编语言能面向机器并较好地发挥机器的特性,得到质量较高的程序。

    2.高速度和高效率

           汇编语言保持了机器语言的优点,具有直接和简捷的特点,可有效地访问、控制计算机的各种硬件设备,如磁盘、存储器、CPUI/O端口等,且占用内存少,执行速度快,是高效的程序设计语言

    3.编写和调试的复杂性

           由于是直接控制硬件,且简单的任务也需要很多汇编语言语句,因此在进行程序设计时必须面面俱到,需要考虑到一切可能的问题,合理调配和使用各种软、硬件资源。这样,就不可避免地加重了程序员的负担。与此相同,在程序调试时,一旦程序的运行出了问题,就很难发现。

    优点

    1、因为用汇编语言设计的程序最终被转换成机器指令,故能够保持机器语言的一致性,直接、简捷,并能像机器指令一样访问、控制计算机的各种硬件设备,如磁盘、存储器CPUI/O端口等。使用汇编语言,可以访问所有能够被访问的软、硬件资源。

    2、目标代码简短,占用内存少,执行速度快,是高效的程序设计语言,经常与高级语言配合使用,以改善程序的执行速度和效率,弥补高级语言在硬件控制方面的不足,应用十分广泛。

    缺点

    1、汇编语言是面向机器的,处于整个计算机语言层次结构的底层,故被视为一种低级语言,通常是为特定的计算机或系列计算机专门设计的。不同的处理器有不同的汇编语言语法和编译器,编译的程序无法在不同的处理器上执行,缺乏可移植性;

    2、难于从汇编语言代码上理解程序设计意图,可维护性差,即使是完成简单的工作也需要大量的汇编语言代码,很容易产生bug,难于调试;

    3、使用汇编语言必须对某种处理器非常了解,而且只能针对特定的体系结构和处理器进行优化,开发效率很低,周期长且单调。 

    语言组成

    数据传送指令

    这部分指令包括通用数据传送指令MOV、条件传送指令CMOVcc、堆栈操作指令PUSH/PUSHA/PUSHAD/POP/POPA/POPAD、交换指令XCHG/XLAT/BSWAP、地址或段描述符选择子传送指令LEA/LDS/LES/LFS/LGS/LSS等。注意,CMOVcc不是一条具体的指令,而是一个指令簇,包括大量的指令,用于根据EFLAGS寄存器的某些位状态来决定是否执行指定的传送操作。

    整数和逻辑运算指令

    这部分指令用于执行算术和逻辑运算,包括加法指令ADD/ADC、减法指令SUB/SBB、加一指令INC、减一指令DEC、比较操作指令CMP、乘法指令MUL/IMUL、除法指令DIV/IDIV、符号扩展指令CBW/CWDE/CDQE、十进制调整指令DAA/DAS/AAA/AAS、逻辑运算指令NOT/AND/OR/XOR/TEST等。

    移位指令

    这部分指令用于将寄存器或内存操作数移动指定的次数。包括逻辑左移指令SHL、逻辑右移指令SHR、算术左移指令SAL、算术右移指令SAR、循环左移指令ROL、循环右移指令ROR等。

    位操作指令

    这部分指令包括位测试指令BT、位测试并置位指令BTS、位测试并复位指令BTR、位测试并取反指令BTC、位向前扫描指令BSF、位向后扫描指令BSR等。

    条件设置指令

    这不是一条具体的指令,而是一个指令簇,包括大约30条指令,用于根据EFLAGS寄存器的某些位状态来设置一个8位的寄存器或者内存操作数。比如SETE/SETNE/SETGE等等。

    控制转移指令

    这部分包括无条件转移指令JMP、条件转移指令Jcc/JCXZ、循环指令LOOP/LOOPE/LOOPNE、过程调用指令CALL、子过程返回指令RET、中断指令INTn、INT3、INTOIRET等。注意,Jcc是一个指令簇,包含了很多指令,用于根据EFLAGS寄存器的某些位状态来决定是否转移;INT n是软中断指令,n可以是0到255之间的数,用于指示中断向量号。

    串操作指令

    这部分指令用于对数据串进行操作,包括串传送指令MOVS、串比较指令CMPS、串扫描指令SCANS、串加载指令LODS、串保存指令STOS,这些指令可以有选择地使用REP/REPE/REPZ/REPNE和REPNZ的前缀以连续操作。

    输入输出指令

    这部分指令用于同外围设备交换数据,包括端口输入指令IN/INS、端口输出指令OUT/OUTS。

    高级语言辅助指令

    这部分指令为高级语言的编译器提供方便,包括创建栈帧的指令ENTER和释放栈帧的指令LEAVE。

    控制和特权指令

    这部分包括无操作指令NOP、停机指令HLT、等待指令WAIT/MWAIT、换码指令ESC、总线封锁指令LOCK、内存范围检查指令BOUND、全局描述符表操作指令LGDT/SGDT、中断描述符表操作指令LIDT/SIDT、局部描述符表操作指令LLDT/SLDT、描述符段界限值加载指令LSR、描述符访问权读取指令LAR、任务寄存器操作指令LTR/STR、请求特权级调整指令ARPL、任务切换标志清零指令CLTS、控制寄存器和调试寄存器数据传送指令MOV、高速缓存控制指令INVD/WBINVD/INVLPG、型号相关寄存器读取和写入指令RDMSR/WRMSR、处理器信息获取指令CPUID、时间戳读取指令RDTSC等。

    浮点和多媒体指令

    这部分指令用于加速浮点数据的运算,以及用于加速多媒体数据处理的单指令多数据(SIMD及其扩展SSEx)指令。这部分指令数据非常庞大,无法一一列举,请自行参考INTEL手册。

    虚拟机扩展指令

    这部分指令包括INVEPT/INVVPID/VMCALL/VMCLEAR/VMLAUNCH/VMRESUME/VMPTRLD/VMPTRST/VMREAD/VMWRITE/VMXOFF/VMON等。 

    相关技术

    汇编器

    典型的现代汇编器(assembler)建造目标代码,由解译组语指令集的易记码(mnemonics)到操作码(OpCode),并解析符号名称(symbolic names)成为存储器地址以及其它的实体。使用符号参考是汇编器的一个重要特征,它可以节省修改程序后人工转址的乏味耗时计算。基本就是把机器码变成一些字母而已,编译的时候再把输入的指令字母替换成为晦涩难懂机器码。

    编译环境

    用汇编语言等非机器语言书写好的符号程序称为源程序,汇编语言编译器的作用是将源程序翻译成目标程序。目标程序是机器语言程序,当它被安置在内存的预定位置上后,就能被计算机的CPU处理和执行。

    汇编的调试环境总的来说比较少,也很少有非常好的编译器。编译器的选择依赖于目标处理器的类型和具体的系统平台。一般来说,功能良好的编译器用起来应当非常方便,比如,应当可以自动整理格式、语法高亮显示,集编译、链接和调试为一体,方便实用。

    对于广泛使用的个人计算机来说,可以自由选择的汇编语言编译器有MASMNASMTASMGAS、FASM、RADASM等,但大都不具备调试功能。如果是为了学习汇编语言,轻松汇编因为拥有一个完善的集成环境,是一款非常适合初学者的汇编编译器。 

    发展前景

    汇编语言是机器语言的助记符,相对于比枯燥的机器代码易于读写、易于调试和修改,同时优秀的汇编语言设计者经过巧妙的设计,使得汇编语言汇编后的代码比高级语言执行速度更快,占内存空间少等优点,但汇编语言的运行速度和空间占用是针对高级语言并且需要巧妙设计,而且部分高级语言在编译后代码执行效率同样很高,所以此优点慢慢弱化。而且在编写复杂程序时具有明显的局限性,汇编语言依赖于具体的机型,不能通用,也不能在不同机型之间移植。常说汇编语言是低级语言,并不是说汇编语言要被弃之,相反,汇编语言仍然是计算机(或微机)底层设计程序员必须了解的语言,在某些行业与领域,汇编是必不可少的,非它不可适用。只是,现在计算机最大的领域为IT软件,也是我们常说的计算机应用软件编程,在熟练的程序员手里,使用汇编语言编写的程序,运行效率与性能比其它语言写的程序相对提高,但是代价是需要更长的时间来优化,如果对计算机原理及编程基础不扎实,反而增加其开发难度,实在是得不偿失,对比2010年前后的软件开发,已经是市场化的软件行业,加上高级语言的优秀与跨平台,一个公司不可以让一个团队使用汇编语言来编写所有的东西,花上几倍甚至几十倍的时间,不如使用其它语言来完成,只要最终结果不比汇编语言编写的差太多,就能抢先一步完成,这是市场经济下的必然结果。

     

    但是,迄今为止,还没有程序员敢断定汇编语言是不需要学的,同时,汇编语言(Assembly Language)是面向机器的程序设计语言,设计精湛的汇编程序员,部分已经脱离软件开发,挤身于工业电子编程中。对于功能相对小巧但硬件对语言设计要求苛刻的行业,如4位单片机,由于其容量及运算,此行业的电子工程师一般负责从开发设计电路及软件控制,主要开发语言就是汇编,c语言使用只占极少部分,而电子开发工程师是千金难求,在一些工业公司,一个核心的电子工程师比其它任何职员待遇都高,对比起来,一般电子工程师待遇是程序员的十倍以上。这种情况是因为21世纪以来,学习汇编的人虽然也不少,但是真正能学到精通的却不多,它相对于高级语言难学,难用,适用范围小,虽然简单,但是过于灵活,学习过高级语言的人去学习汇编比一开始学汇编的人难得多,但是学过汇编的人学习高级语言却很容易,简从繁易,繁从简难。对于一个全面了解微机原理的程序员,汇编语言是必修语言。

    实际应用

    随着现代软件系统越来越庞大复杂,大量经过了封装的高级语言如C/C++Pascal/Object Pascal也应运而生。这些新的语言使得程序员在开发过程中能够更简单,更有效率,使软件开发人员得以应付快速的软件开发的要求。而汇编语言由于其复杂性使得其适用领域逐步减小。但这并不意味着汇编已无用武之地。由于汇编更接近机器语言,能够直接对硬件进行操作,生成的程序与其他的语言相比具有更高的运行速度,占用更小的内存,因此在一些对于时效性要求很高的程序、许多大型程序的核心模块以及工业控制方面大量应用。 

    历史上,汇编语言曾经是非常流行的程序设计语言之一。随着软件规模的增长,以及随之而来的对软件开发进度和效率的要求,高级语言逐渐取代了汇编语言。但即便如此,高级语言也不可能完全替代汇编语言的作用。就拿Linux内核来讲,虽然绝大部分代码是用C语言编写的,但仍然不可避免地在某些关键地方使用了汇编代码。由于这部分代码与硬件的关系非常密切,即使是C语言也会显得力不从心,而汇编语言则能够很好扬长避短,最大限度地发挥硬件的性能。

    首先,汇编语言的大部分语句直接对应着机器指令,执行速度快,效率高,代码体积小,在那些存储器容量有限,但需要快速和实时响应的场合比较有用,比如仪器仪表和工业控制设备中。

    其次,在系统程序的核心部分,以及与系统硬件频繁打交道的部分,可以使用汇编语言。比如操作系统的核心程序段、I/O接口电路的初始化程序、外部设备的低层驱动程序,以及频繁调用的子程序动态连接库、某些高级绘图程序、视频游戏程序等等。

    再次,汇编语言可以用于软件的加密和解密、计算机病毒的分析和防治,以及程序的调试和错误分析等各个方面。

    最后,通过学习汇编语言,能够加深对计算机原理和操作系统等课程的理解。通过学习和使用汇编语言,能够感知、体会和理解机器的逻辑功能,向上为理解各种软件系统的原理,打下技术理论基础;向下为掌握硬件系统的原理,打下实践应用基础。

    附注:寄存器和内存模型

    学习汇编语言,首先必须了解两个知识点:寄存器和内存模型。

    先来看寄存器。CPU 本身只负责运算,不负责储存数据。数据一般都储存在内存之中,CPU 要用的时候就去内存读写数据。但是,CPU 的运算速度远高于内存的读写速度,为了避免被拖慢,CPU 都自带一级缓存和二级缓存。基本上,CPU 缓存可以看作是读写速度较快的内存。

    但是,CPU 缓存还是不够快,另外数据在缓存里面的地址是不固定的,CPU 每次读写都要寻址也会拖慢速度。因此,除了缓存之外,CPU 还自带了寄存器(register),用来储存最常用的数据。也就是说,那些最频繁读写的数据(比如循环变量),都会放在寄存器里面,CPU 优先读写寄存器,再由寄存器跟内存交换数据。

    寄存器不依靠地址区分数据,而依靠名称。每一个寄存器都有自己的名称,我们告诉 CPU 去具体的哪一个寄存器拿数据,这样的速度是最快的。有人比喻寄存器是 CPU 的零级缓存。

    1、寄存器的种类

    早期的 x86 CPU 只有8个寄存器,而且每个都有不同的用途。现在的寄存器已经有100多个了,都变成通用寄存器,不特别指定用途了,但是早期寄存器的名字都被保存了下来。

    EAX

    EBX

    ECX

    EDX

    EDI

    ESI

    EBP

    ESP

    上面这8个寄存器之中,前面七个都是通用的。ESP 寄存器有特定用途,保存当前 Stack 的地址。

    我们常常看到 32位 CPU、64位 CPU 这样的名称,其实指的就是寄存器的大小。32 位 CPU 的寄存器大小就是4个字节。

    2、内存模型:Heap

    寄存器只能存放很少量的数据,大多数时候,CPU 要指挥寄存器,直接跟内存交换数据。所以,除了寄存器,还必须了解内存怎么储存数据。

    程序运行的时候,操作系统会给它分配一段内存,用来储存程序和运行产生的数据。这段内存有起始地址和结束地址,比如从0x1000到0x8000,起始地址是较小的那个地址,结束地址是较大的那个地址

    程序运行过程中,对于动态的内存占用请求(比如新建对象,或者使用malloc命令),系统就会从预先分配好的那段内存之中,划出一部分给用户,具体规则是从起始地址开始划分(实际上,起始地址会有一段静态数据,这里忽略)。举例来说,用户要求得到10个字节内存,那么从起始地址0x1000开始给他分配,一直分配到地址0x100A,如果再要求得到22个字节,那么就分配到0x1020。

    这种因为用户主动请求而划分出来的内存区域,叫做 Heap(堆)。它由起始地址开始,从低位(地址)向高位(地址)增长。Heap 的一个重要特点就是不会自动消失,必须手动释放,或者由垃圾回收机制来回收。

    3、内存模型:Stack

    除了 Heap 以外,其他的内存占用叫做 Stack(栈)。简单说,Stack 是由于函数运行而临时占用的内存区域

    请看下面的例子。

    int main(){int a=2;int b=3;}

    上面代码中,系统开始执行main函数时,会为它在内存里面建立一个帧(frame),所有main的内部变量(比如a和b)都保存在这个帧里面。main函数执行结束后,该帧就会被回收,释放所有的内部变量,不再占用空间。

    如果函数内部调用了其他函数,会发生什么情况?

    int main(){int a=2;int b=3;return add_a_and_b(a,b);}

    上面代码中,main函数内部调用了add_a_and_b函数。执行到这一行的时候,系统也会为add_a_and_b新建一个帧,用来储存它的内部变量。也就是说,此时同时存在两个帧:main和add_a_and_b。一般来说,调用栈有多少层,就有多少帧。

    等到add_a_and_b运行结束,它的帧就会被回收,系统会回到函数main刚才中断执行的地方,继续往下执行。通过这种机制,就实现了函数的层层调用,并且每一层都能使用自己的本地变量。

    所有的帧都存放在 Stack,由于帧是一层层叠加的,所以 Stack 叫做栈。生成新的帧,叫做"入栈",英文是 push;栈的回收叫做"出栈",英文是 pop。Stack 的特点就是,最晚入栈的帧最早出栈(因为最内层的函数调用,最先结束运行),这就叫做"后进先出"的数据结构。每一次函数执行结束,就自动释放一个帧,所有函数执行结束,整个 Stack 就都释放了。

    Stack 是由内存区域的结束地址开始,从高位(地址)向低位(地址)分配。比如,内存区域的结束地址是0x8000,第一帧假定是16字节,那么下一次分配的地址就会从0x7FF0开始;第二帧假定需要64字节,那么地址就会移动到0x7FB0。

    汇编语言保留字

    保留字(reserved words)有特殊意义并且只能在其正确的上下文中使用。默认情况下,保留字是没有大小写之分的。比如,MOV 与 mov、Mov 是相同的。

    保留字有不同的类型:

    • 指令助记符,如 MOV、ADD 和 MUL。
    • 寄存器名称。
    • 伪指令,告诉汇编器如何汇编程序。(不是机器指令)
    • 属性,提供变量和操作数的大小与使用信息。例如 BYTE 和 WORD。
    • 运算符,在常量表达式中使用。
    • 预定义符号,比如 @data,它在汇编时返回常量的整数值。

    下表是常用的保留字列表。

    $ PARITY? DWORD STDCALL
    ? PASCAL FAR SWORD
    @B QWORD FAR16 SYSCALL
    @F REAL4 FORTRAN TBYTE
    ADDR REAL8 FWORD VARARG
    BASIC REAL10 NEAR WORD
    BYTE SBYTE NEAR16 ZERO?
    C SDORD OVERFLOW?  
    CARRY? SIGN?    

    伪指令

    DW 定义字(2字节).

    PROC 定义过程.

    ENDP 过程结束.

    SEGMENT 定义段.

    ASSUME 建立段寄存器寻址.

    ENDS 段结束.

    END 程序结束.

    伪指令 (directive) 是嵌入源代码中的命令,由汇编器识别和执行。伪指令不在运行时执行,但是它们可以定义变量、宏和子程序;为内存段分配名称,执行许多其他与汇编器相关的日常任务。

    默认情况下,伪指令不区分大小写。例如,.data,.DATA 和 .Data 是相同的。

    下面的例子有助于说明伪指令和指令的区别。DWORD 伪指令告诉汇编器在程序中为一个双字变量保留空间。另一方面,MOV 指令在运行时执行,将 myVar 的内容复制到 EAX 寄存器中:

    myVar DWORD 26
    mov eax,myVar

    尽管 Intel 处理器所有的汇编器使用相同的指令集,但是通常它们有着不同的伪指令。比如,Microsoft 汇编器的 REPT 伪指令对其他一些汇编器就是无法识别的。

    定义段

    汇编器伪指令的一个重要功能是定义程序区段,也称为段 (segment)。程序中的段具有不同的作用。如下面的例子,一个段可以用于定义变量,并用 .DATA 伪指令进行标识:

    .data

    .CODE 伪指令标识的程序区段包含了可执行的指令:

    .code

    .STACK 伪指令标识的程序区段定义了运行时堆栈,并设置了其大小:

    .stack 100h

    指令

    指令(instruction)是一种语句,它在程序汇编编译时变得可执行。汇编器将指令翻译为机器语言字节,并且在运行时由 CPU 加载和执行。

    一条指令有四个组成部分:

    • 标号(可选)
    • 指令助记符(必需)
    • 操作数(通常是必需的)
    • 注释(可选)

    不同部分的位置安排如下所示:

    [label: ] mnemonic [operands] [;comment]

    现在分别了解每个部分,先从标号字段开始。

    1) 标号

    标号(label)是一种标识符,是指令和数据的位置标记。标号位于指令的前端,表示指令的地址。同样,标号也位于变量的前端,表示变量的地址。标号有两种类型:数据标号和代码标号。

    数据标号标识变量的位置,它提供了一种方便的手段在代码中引用该变量。比如,下面定义了一个名为 count 的变量:

    count DWORD 100

    汇编器为每个标号分配一个数字地址。可以在一个标号后面定义多个数据项。在下面的例子中,array 定义了第一个数字(1024)的位置,其他数字在内存中的位置紧随其后:

    array DWORD 1024, 2048
    DWORD 4096, 8192

    程序代码区(指令所在区段)的标号必须用冒号(:)结束。代码标号用作跳转和循环指令的目标。例如,下面的 JMP 指令创建一个循环,将程序控制传递给标号 target 标识的位置:

    target:
    mov ax,bx
    ...
    jmp target

    代码标号可以与指令在同一行上,也可以自己独立一行:

    L1: mov ax, bx
    L2 :

    标号命名规则要求,只要每个标号在其封闭子程序页中是唯一的,那么就可以多次使用相同的标号。

    2) 指令助记符

    指令助记符(instruction mnemonic)是标记一条指令的短单词。在英语中,助记符是帮助记忆的方法。相似地,汇编语言指令助记符,如 mov, add 和 sub,给出了指令执行操作类型的线索。下面是一些指令助记符的例子:

    助记符 说明 助记符 说明
    MOV 传送(分配)数值 MUL 两个数值相乘
    ADD 两个数值相加 JMP 跳转到一个新位置
    SUB 从一个数值中减去另一个数值 CALL 调用一个子程序

    3) 操作数

    操作数是指令输入输出的数值。汇编语言指令操作数的个数范围是 0〜3 个,每个操作数可以是寄存器、内存操作数、整数表达式和输入输岀端口。
    生成内存操作数有不同的方法,比如,使用变量名、带方括号的寄存器等。变量名暗示了变量地址,并指示计算机使用给定地址的内存内容。下表列出了一些操作数示例:

    示例 操作数类型 示例  操作数类型
    96  整数常量 eax 寄存器
    2+4 整数表达式 count 内存

    现在来考虑一些包含不同个数操作数的汇编语言指令示例。比如,STC 指令没有操作数:

    stc ;进位标志位置 1

    INC 指令有一个操作数:

    inc eax ;EAX 加 1

    MOV 指令有两个操作数:

    mov count, ebx ;将 EBX 传送给变量 count

    操作数有固有顺序。当指令有多个操作数时,通常第一个操作数被称为目的操作数,第二个操作数被称为源操作数(source operand)。

    一般情况下,目的操作数的内容由指令修改。比如,在 mov 指令中,数据就是从源操作数复制到目的操作数。

    IMUL 指令有三个操作数,第一个是目的操作数,第二个和第三个是进行乘法的源操作数:

    imul eax,ebx,5

    在上例中,EBX 与 5 相乘,结果存放在 EAX 寄存器中。

    4) 注释

    注释是程序编写者与阅读者交流程序设计信息的重要途径。程序清单的开始部分通常包含如下信息:

    • 程序目标的说明
    • 程序创建者或修改者的名单
    • 程序创建和修改的日期
    • 程序实现技术的说明

    注释有两种指定方法:

    • 单行注释,用分号(;)开始。汇编器将忽略在同一行上分号之后的所有字符。
    • 块注释,用 COMMENT 伪指令和一个用户定义的符号开始。汇编器将忽略其后所有的文本行,直到相同的用户定义符号出现为止。

    示例如下:

    COMMENT !
    This line is a comment.
    This line is also a comment.
    !

    其他符号也可以使用,只要该符号不出现在注释行中:

    COMMENT &
    This line is a comment.
    This line is also a comment.
    &

    当然,程序员应该在整个程序中提供注释,尤其是代码意图不太明显的地方。

    5) NOP(空操作)指令

    最安全(也是最无用)的指令是 NOP(空操作)。它在程序空间中占有一个字节,但是不做任何操作。它有时被编译器和汇编器用于将代码对齐到有效的地址边界

    在下面的例子中,第一条指令 MOV 生成了 3 字节的机器代码。NOP 指令就把第三条指令的地址对齐到双字边界(4 的偶数倍)

    00000000 66 8B C3 mov ax,bx
    00000003 90 nop ;对齐下条指令
    00000004 8B D1 mov edx,ecx

    x86 处理器被设计为从双字的偶数倍地址处加载代码和数据,这使得加载速度更快。

    展开全文
  • 汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽汇编语言王爽
  • [编程语言][汇编语言]计算机与汇编语言

    千次阅读 多人点赞 2015-11-30 18:47:31
    汇编语言

    一、什么是计算机 ?


    简单的说计算机是这样一种机器:当我们把一些信息输入到计算机中后,它经过计算后告诉我们信息处理的结果。计算机能够完成很多任务,甚至有些任务是极其复杂的。但计算机在完成这些任务的时候,最终其实是做数值的计算。所以我们叫这种机器为“计算”机。计算机是为了方便人类而产生的,理论上来说计算机能完成的任务人类也是能够完成的。所以,计算机很多方面其实和人类很像。

    1、CPU、内存与硬盘

    我们去电脑城买电脑的时候,卖场的工作人员通常会介绍一台电脑的配置如何。笔者的笔记本就是i7CPU、8G内存以及1TB硬盘的。什么是CPU?什么是内存?还有,什么是硬盘?这些问题可能对一个熟悉电脑的人来说小菜一碟,但对某些刚刚接触电脑的人来说还是比较困难的。下面我们打开电脑的外壳,看看这些东西到底是什么样子的。

    CPU其实是个不到半个手掌大小的方块,它的周围有一些引脚。人们所说的内存其实是一个长方形的条形电路板,也就是内存条。而硬盘是一个铁盒子,我们无法打开这个铁盒子深入地了解硬盘的内部结构。

    以上是我们感官的认识,下面我们来了解它们的功能。

    CPU(Central Processing Unit,中文名称是中央处理器)是我们计算机中最为重要的一个部件。它完成绝大多数的运算,好比是我们的大脑。

    内存(Memory)也被称为内存储器,其作用是用于暂时存放CPU中的运算数据。内存就像是我们手头的草稿纸。我们的大脑不能计算过于复杂的计算,否则就需要借助草稿纸来暂时记下中间的计算结果。CPU其实和我们的大脑是一样的,只能计算一定范围内的运算。对于稍稍复杂一些运算,CPU就需要暂时把中间结果保存在内存中。

    硬盘(Hard Disk)主要用于保存数据。我们借助草稿纸得到复杂运算的结果后,为了便于老师批阅,通常是把运算结果填写的练习本上的。CPU可以把运算结果保存在其内部,当然也可以保存在内存中。由于电子硬件的特性,计算机断电以后CPU和内存中保存的数据会完全丢失。硬盘的电子硬件特性决定了它即便是断电,也不会丢失数据。这样我们就可以把运算结果保存在硬盘上。

    2、寄存器

    什么!寄存器又是个什么东西?

    我们的大脑在计算的时候,其实计算发生在大脑内部某个空间的。我们可以通过平时的训练来扩充这个计算空间,这样我们就可以计算更加复杂的运算。说到底,我们的大脑内部也有一个类似草稿纸的空间。对于1+2这种简单运算,我们的大脑完全可以马上得到结果。对于17×8这样的运算,我们也可以比较容易地得到结果:首先计算7×8得到56,然后计算1×8的到8,然后再把之前运算结果的进位5和这次得到的8相加得到13,最终的结果就是136。为了得到了136这个最终结果,我们的大脑产生了56、8和13这些中间结果,而这些中间结果其实是保存在我们大脑内部的“草稿纸”中的。看似还比较容易,但如果我们想计算1234×5678呢?除了个别人,如果不凭借手边的草稿纸,要得到正确结果恐怕就不是那么容易了。

    我们的大脑如此,计算机又是怎样的呢?一些高手口中说的“32位计算机”、“64位计算机”,我们可以理解为一台计算机一次运算能够处理的数值的最大长度是32位或是64位的。在这里,我们先不探究32位或64位是怎样的一个长度,只需要了解计算机的运算能力不是万能的,超过一定的范围计算机就无能为力了。计算机的运算功能是由CPU来完成的,所以CPU的运算能力就是计算机的运算能力。在计算机内部也是有“草稿纸”的,这些“草稿纸”就是寄存器(register)。寄存器和内存一样,也是可以保存数值,但是寄存器能够存储数据的能力远远小于内存。

    组成寄存器和内存的电子元件是不相同的。寄存器的速度比较快,但制造成本比较高;内存的速度比较慢,但制造成本比较低。如果我们只使用内存,那么速度更快的CPU大部分时间是在等待内存;如果我们只使用寄存器,那么一台电脑将不是我们这些普通人能够买得起的。为了获得一定的运算速度,也为了能够降低成本。工程师决定在CPU内部内置一些寄存器来完成CPU的运算功能,同时CPU也需要在内存中保存其运算结果。

    在这里我们总结一下:计算机的运算功能是依靠CPU完成的;在运算过程中要CPU要用到其内部的寄存器;而当寄存器无法满足计算的需要时,我们可以把中间结果暂时保存在内存中;当计算完成并且需要永久保存其结果时,我们就把它们保存在硬盘里。

    二、机器语言与汇编语言


    1、机器语言

    我们在学习英语之前,我们听不懂美国人到底说了写什么。同样的,我们计算机也是不能理解“1+2=?”这种极其简单的计算问题。计算机只能理解机器语言,也就是二进制指令,二进制就是计算机世界中的通用语言。“1011 1000 0000 0001 0000 0000 1000 0011 1100 0000 0000 0010”这段二进制指令的含义就是让CPU计算1+2的运算。如果这么多的1和0中写错一个数字,就可能变成另外的含义了。为了方便阅读,我们可以把这段二进制指令以16进制数表示:“B80100 83C002”。可是即便如此,机器语言对我们来说还是很难阅读。

    2、汇编语言

    我们可以理解汉语,可以理解数学算式。通过学习,我们还可以理解英语、法语。当然,通过学习以及自身超强的记忆力,我们也能够理解机器语言。但这就背离了我们发明计算机的初衷,为了方便运算,我们竟然要花巨大的时间和精力来学习以及记忆一门世界上最难懂的语言。

    世界上谁会这么做呢?但不这么做又该如何让计算机明白我们的意图呢?聪明的前辈们发明了汇编语言(assembly language)这种中间语言。借助于汇编语言,我们可以很容易地将自己的意图转换为汇编语言。而汇编语言和机器语言一一对应的关系,又决定了它们之间可以非常容易地进行转换。用汇编语言表达之前机器语言就是:

        mov $1, %ax
        add $2, %ax

    嗯,有点意思了。mov很像move,add是加法运算的英文单词。只是为什么在1和2的前面有个美元符号$?%ax又是个什么东西?未知的东西好像有些多了,但别着急,这些疑问我们会一一谈到了。

    前面的代码片段中的mov、add是助记符(mnemonic),%ax是寄存器。每一行为一条指令。第一条指令的意思是把1搬进(move)ax寄存器,第二条指令的意思是把ax寄存器的数值加上(add)2。以后我们会发现,“B80100”其实就是mov $1, %ax,而”83C002“就是add $2, %ax。看看,有了汇编语言做翻译,我们是不是很容易就让计算机知道我们到底想让它做些什么事情了。

    汇编语言就是把机器指令用人们能比较理解的简短的助记符表示出来的语言。这里要提一下的是,汇编语言虽然与机器语言一一对应,但却有多种不同的表达格式。最常用的汇编语言格式为“Intel汇编格式”和“AT&T汇编格式”,而上面的代码片段就是“AT&T汇编格式”的。不过它们之间的差异很小,也很容易掌握,而且它们也很容易相互转换,完全不会影响到我们学习汇编语言本身。

    三、第一个汇编程序


    前面说了这么多文科知识,我们已经很枯燥了。下面就开始我们自己的第一个汇编程序。不过,我们首先需要知道得到最终的成品之前,我们都需要做哪些事情。

    第一步,我们在脑海里把自己的意图转换成一条条的汇编指令后,就可以通过键盘输入到计算机里并且保存为一个文本文件。这个文本文件我们称为源代码文件(source file),简称源文件。其扩展名为“s”,比如“boot.s”文件。源文件就是我们起点。

    第二步,我们通过汇编器(assembler)把源代码文件转换成目标文件(object file)。此时,我们得到的目标文件是一个包含二进制指令序列的文件,而这些二进制指令序列就是计算机能够理解的机器语言。这个过程称为汇编。每一个源文件汇编后会得到一个目标文件,当然N个源文件汇编后得到的就是N个目标文件。也就是说,汇编的过程是以源文件为单位进行的,源文件与目标文件是一一对应的。虽然如此,我们的工作还没有结束。

    第三步,我们需要通过把链接器(linker)把目标文件链接成在相应环境下可以执行的文件,即可执行文件(executable file)。这个过程我们称为链接。如果有多个目标文件,我们可以把它们链接成一个可执行文件。也就是说,在链接过程中,目标文件与可执行文件是多对一的关系。好了,到这里,我们已经得到了我们想要的成品了。

    以上这三步是我们必须完成的。当然了,肯定有朋友会问到:“第二部得到的不是已经是机器语言了吗,为什么还需要第三步呢?”说的没错,第二部中的目标文件确实已经包含机器语言了。不过,对于我们练习的小程序,用一个源文件就可以了。但是,如果一个大型项目,甚至是Linux这样的“巨型”项目,就需要分成多个模块,分别进行编辑、汇编,然后再链接成一个可执行文件。另外,目标文件缺少程序加载所必须的信息,而这些信息必须由链接器补充完整。

    好像又说了一堆枯燥的文科知识,不过一旦了解了上面的这些内容将有助于我们完成自己的第一个汇编程序。为了降低难度,尽可能少地讲解新知识,我们来编写一个Linux下的“hello, world!”汇编程序。

    1、编辑源文件

    将下面的源代码编辑在一个文本文件中,并且文件名保存为“hello.s”。

    /*
     * File:        ch1/hello.s
     * Author:      HuoYun
     * Created on:  2015/11/30 15:15:47
     * Modified on: 2015/11/30 15:15:47
     * Describe:
     *      本程序为了演示汇编以及链接的过程。
     * Compile command:
     *      as hello.s -o hello.o
     *      ld hello.o -o hello
     * Usage:
            hello
     */
    
        .section .text
        .global _start
    _start:
        # 调用系统调用,在屏幕上显示信息。
        mov $4, %eax
        mov $1, %ebx
        mov $message, %ecx
        mov $len, %edx
        int $0x80
    
        # 调用系统调用,退出程序。
        mov $1, %eax
        mov $0, %ebx
        int $0x80
    
        .section .data
    message:
        .ascii "hello, world!\n"
        len = .-message

    我们稍后来讲述如何编辑这个汇编源代码。

    2、汇编

    我们进入保存源代码文件的目录,然后执行下面的命令:

    $ as hello.s -o hello.o

    如果我们编辑的源代码没有问题,就会得到boot.o这个目标文件。但如果我们在编辑的过程中不小心输错了一个地方,汇编器可能会提示错误。这个时候就需要我们仔细地检查并且更正了。

    3、链接

    我们继续执行下面的命令:

    $ ld hello.o -o hello

    这个时候就会的到boot这个可执行文件。当然如果由于某种原因,链接器提示发生了错误,同样的,我们依然要查找并更正错误。

    4、执行

    当我们正确地得到了可执行文件boot后,我们就可以执行下面的命令来运行我们的第一个汇编程序了:

    $ ./hello

    如果得到下面的运行结果,说明我们之前的步骤都没有错误。以后我们编译程序时,多数情况下都会使用之前的步骤。所以,以后我们不再详细讲述每个程序的编译过程,而是将编译命令记录在源文件的注释中。

    hello, world!

    至此,是否有些兴奋和成就感呢?编写程序代码是个枯燥的过程。有的时候我们洋洋洒洒地写了成百上千行代码,可就是无法编译通过,或者编译通过了却没有得到我们预期的效果。这时,我们只能慢慢地寻找bug。找到了一个bug,但发现还有另外的bug。是的,我们就是在编辑、找错、再编辑、再找错这种多次重复的过程中完善程序的,最终我们得到的是一个我们期望的程序。编写程序是乏味的,产生的错误是让人厌烦的。没有一个人一开始就能写出正确无误的代码,大量的编程会降低自己的失误率。学习编程,最忌讳的就是眼高手低,因为事情经常不是你想象的那样。书中剩余的代码示例均已在Ubuntu linux 15.04操作系统下编译完成,我们尽可能地保证代码的正确性,让大家能够得到一样的结果。看到这里,如果你仅仅只是通过之前的描述直到了“hello, world!”汇编程序的允许结果,那请你亲自动手敲敲键盘,看看是否能够得到和我一样的结果。

    “纸上得来终觉浅,绝知此事要躬行。”我以这一句作为本章的结束,再一次地说明亲自编写代码的重要性。

    展开全文
  • 王爽《汇编语言》笔记(详细)

    万次阅读 多人点赞 2019-03-24 19:53:32
    一、基础知识 1、指令 机器指令:CPU能直接识别并执行的二进制编码...汇编语言发展至今,有以下3类指令组成。 汇编指令:机器码的助记符,有对应的机器码。 伪指令:没有对应的机器码,由编译器执行,计算机并不执...
  • 关于底层知识的学习,汇编当然是绕不过,当你需要调优一小段代码时,你需要看的汇编语言。通过学习汇编语言,你能更清楚明白的了解整个计算机的计算过程,指令相关知识,对学习计算机帮助甚大。 目录: 第1章 ...
  • 8086汇编语言讲座

    千人学习 2018-03-20 10:20:26
    关于8086汇编语言,是与计算机原理相结合的,通过汇编语言可以加深对计算机原理的掌握和理解。 另外汇编语言还是可以干成事的,比如破解,外挂等都会用到它。
  • 最新的TIOBE语言流行度指数显示汇编语言再次进入前10。最流行的语言中如Java、C和C++的排名没有变化,从10名之外进入前10的两种语言是Perl和汇编语言,像汇编语言这种最低级语言流行起来有些出人意料。一种解释是能...
  • 汇编语言入门教程

    千人学习 2019-12-26 10:57:49
    汇编语言入门级教程 游戏安全,游戏逆向必学的基础语言 
  • 汇编语言笔记(全)

    万次阅读 多人点赞 2019-08-01 11:40:05
    汇编语言 最近系统的学了下汇编语言,下面是学习笔记,用的书是清华大学出版社出版的汇编语言第三版,作者王爽(最经典的那版)。 汇编语...
  • win10配置汇编语言环境(王爽汇编语言环境)

    千次阅读 多人点赞 2019-03-30 16:47:20
    win10配置汇编语言环境(王爽汇编语言环境) 前言 有不少童鞋在学习“汇编语言”这门课程时,不太满足于课本上的理论知识,想要亲自上机实践一下,从而更能够更深刻的理解汇编语言的基本原理。然而,大多数同学...
  • 汇编语言程序设计

    千人学习 2017-03-01 22:19:38
    汇编语言是一门低级程序设计语言,在数以千计的计算机语言中,有着不可替代的重要地位,广泛地用于开发操作系统内核、设备驱动程序等。随着近年来物联网、嵌入式系统的发展,汇编语言在行业中的地位也再次攀升,在...
  • 汇编语言实现单片机独立按键控制LED亮灭(源码、仿真图)
  • 1、汇编语言的产生 2、汇编语言的组成 汇编语言由以下3类组成: 1、汇编指令(机器码的助记符) 2、伪指令(由编译器执行) 3、其他符号(由编译器识别) 汇编语言的核心是汇编指令,它决定了汇编语言的...
  • 汇编语言教程

    2020-04-04 21:42:08
    汇编语言基本概念简介 1.1 汇编语言是一种什么程序设计语言? 1.2汇编语言的应用(用途) 1.3虚拟机是什么? 1.4汇编语言的数据表示 1.5二进制(bit)整数 1.6二进制加法运算 1.7字节(byte)简介 1.8十六...
  • 汇编语言程序设计VII

    千人学习 2017-06-20 21:43:21
    汇编语言是一门低级程序设计语言,在数以千计的计算机语言中,有着不可替代的重要地位,广泛地用于开发操作系统内核、设备驱动程序等。随着近年来物联网、嵌入式系统的发展,汇编语言在行业中的地位也再次攀升,在...
  • 汇编语言汇编语言有什么区别

    千次阅读 2018-11-18 08:50:21
    宏汇编属于汇编语言。宏汇编本人理解就是再给取个名,便于编写程序。 例如某人叫二狗子,签名时候写二狗子不行(叫二狗子的太多了),就要取个大名,但得到公安局登记。宏汇编就相当于公安局。 ...
  • 汇编语言程序设计VI

    千人学习 2017-05-17 09:24:04
    汇编语言是一门低级程序设计语言,在数以千计的计算机语言中,有着不可替代的重要地位,广泛地用于开发操作系统内核、设备驱动程序等。随着近年来物联网、嵌入式系统的发展,汇编语言在行业中的地位也再次攀升,在...
  • 汇编语言程序设计II

    千人学习 2017-03-01 22:27:52
    汇编语言是一门低级程序设计语言,在数以千计的计算机语言中,有着不可替代的重要地位,广泛地用于开发操作系统内核、设备驱动程序等。随着近年来物联网、嵌入式系统的发展,汇编语言在行业中的地位也再次攀升,在...
  • 汇编语言程序设计V

    千人学习 2017-03-30 12:01:38
    汇编语言是一门低级程序设计语言,在数以千计的计算机语言中,有着不可替代的重要地位,广泛地用于开发操作系统内核、设备驱动程序等。随着近年来物联网、嵌入式系统的发展,汇编语言在行业中的地位也再次攀升,在...
  • 汇编语言实验2-汇编语言程序框架

    千次阅读 2017-03-31 20:17:35
    学会由汇编语言源程序到可执行文件的生成过程,学会用Debug运行程序 学会在程序中操作栈的方法 学会用loop指令编制循环程序解决简单问题 学会用bx和loop指令配合访问连贯的内存空间 2. 实验内容任务1-编制第一个汇编...
  • 汇编语言程序设计III

    千人学习 2017-03-08 20:16:58
    汇编语言是一门低级程序设计语言,在数以千计的计算机语言中,有着不可替代的重要地位,广泛地用于开发操作系统内核、设备驱动程序等。随着近年来物联网、嵌入式系统的发展,汇编语言在行业中的地位也再次攀升,在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,640
精华内容 19,456
关键字:

汇编语言