汇编语言概述

汇编语言简介

汇编语言是一门直接与硬件相关的编程语言,它是一台机器的机器语言的符号语言。每一台计算机都有自己的指令系统,不一样的机器由于硬件的差异,采用的指令系统不一样,而汇编,也是每台机器都不太一样的,因为汇编就是用符合去代替指令中的操作码以及操作数。可以这样说,理解计算机CPU在某一架构(物理实现)下的工作原理,是学好汇编很重要的一点。

汇编语言的出现大大提高了编程效率,但是有一个问题就是不同CPU的指令集可能不同,这样就需要为不同的CPU编写不同的汇编程序。于是又出现了高级语言比如C,或者是后来的C++,Java,C#。 高级语言把多条汇编指令合成成为了一个表达式,并且去除了许多操作细节(比如堆栈操作,寄存器操作),而是以一种更直观的方式来编写程序,而面向对象的语言的出现使得程序编写更加符合我们的思维方式。我们不必把尽力放到低层的细节上,而更多的关注程序的本身的逻辑的实现。对于高级语言来说需要一个编译器来完成高级语言到汇编语言的转换。所以对比不同的CPU结构,只需要有不同编译器和汇编器就能使得我们的程序在不同CPU上都能运行了。对于目前来说,一般出现了新的指令,会有对应的新的汇编器和编译器。所以编译器可以把一些高级语言的表达式编译成新的汇编指令,这样对于高级来说不会有任何变化; 当然还有一种情况就是高级语言会增加新的语法来对应一些新的汇编语言和指令。但是这种情况出现的几率很小。所以如果编译器不支持新的指令,那么只有只用汇编会来实现了。

另外要说的一点是,对于C#和Java这种运行在虚拟机上的语言,编译过程有所不同。 对于C,C++的程序,生成的可执行文件,可以在兼容的计算机上直接运行。但是C#和JAVA这些语言则不同。他们编译过程是相似的,但是他们最终生成的并不是机器码,而是中间代码,对于C#而言叫IL代码,对于JAVA是字节码。所以C#,JAVA编译出来的文件并不能被执行。我们在使用.NET或JAVA时都需要安装.NET CLR或者JAVA虚拟机,以.NET为例,CLR实际是一个COM组件,当你点击一个.NET的EXE文件时,它和C++等不一样,不能直接被执行,而是有一个垫片程序来启动一个进程,并且初始化CLR组件。当CLR运行后,一个叫做JIT的编译器会吧EXE中的IL代码编译成对应平台的机器码,然后如同其他C++程序一样被执行。

CPU简介

  1. 控制单元:控制单元是整个CPU的指挥控制中心,由指令寄存器IR(Instruction Register)、指令译码器ID(Instruction Decoder)和操作控制器OC(Operation Controller)等,对协调整个电脑有序工作极为重要。它根据用户预先编好的程序,依次从存储器中取出各条指令,放在指令寄存器IR中,通过指令译码(分析)确定应该进行什么操作,然后通过操作控制器OC,按确定的时序,向相应的部件发出微操作控制信号。操作控制器OC中主要包括节拍脉冲发生器、控制矩阵、时钟脉冲发生器、复位电路和启停电路等控制逻辑。
  2. 运算单元:是运算器的核心。可以执行算术运算(包括加减乘数等基本运算及其附加运算)和逻辑运算(包括移位、逻辑测试或两个值比较)。相对控制单元而言,运算器接受控制单元的命令而进行动作,即运算单元所进行的全部操作都是由控制单元发出的控制信号来指挥的,所以它是执行部件。
  3. 存储单元:包括CPU片内缓存和寄存器组,是CPU中暂时存放数据的地方,里面保存着那些等待处理的数据,或已经处理过的数据,CPU访问寄存器所用的时间要比访问内存的时间短。采用寄存器,可以减少CPU访问内存的次数,从而提高了CPU的工作速度。但因为受到芯片面积和集成度所限,寄存器组的容量不可能很大。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,分别寄存相应的数据。而通用寄存器用途广泛并可由程序员规定其用途,通用寄存器的数目因微处理器而异。

寄存器

首先寄存器是CPU里面的重要的部件,简单来说,就是个存储部件,速度很快,造价高,所以只有这类硬件在计算机里比较少。寄存器是CPU里面很重要的一个部件,通常在CPU里面都有一个寄存器组,里面放着有通用寄存器,专用寄存器等(因CPU而异)。

这个是80x86处理器架构下的寄存器。

这个是支持64位的cpu里的寄存器。

后来的pentium4及更高型号处理器增加了8个通用寄存器实现更多功能

寻址方式

我们知道,寄存器里面一开始是没有东西的,由CPU的控制器决定让CPU去哪里去数据,进行什么操作。既然要取数据,就会涉及寻址。所谓寻址,就是CPU要去内存的哪个地址去取数据。在80x86下,一般有

  • 立即数寻址
  • 直接寻址
  • 寄存器寻址
  • 寄存器间接寻址
  • 基址变址寻址
    等等

在这里简单说一下实现寻址,主要是通过段寄存器CS,DS,SS,ES()来实现,一般由这些CS,DS,SS来指向起始位置,然后由其他指针寄存器去实现代码数据的移动变换(我的理解就是内存的寻址)。举例子,比如CS来说,一般CS指向正在执行的代码段的开始位置,然后会有搭档IP(指令指针)去实现指向要执行的下一条指令。然后是SS指针,SS存放堆栈段栈底,然后SP指向栈顶(入栈出栈SP自加自减),再搭配BP指针(读取任意内存位置的堆栈数据)。

利用寄存器可以实现CPU内部的寻址机制,我们就可以利用汇编找到操作数了。

CPU指令集

每个CPU都有自己的指令集合,汇编就是这些指令的符号表示。

一条指令往往是由操作码和操作数组成。利用寄存器和寻址机制,我们搞定了操作数。那么操作码呢?

在汇编语言里,也有自己的语法:

  • 基本数据操作
  • 分支结构
  • 循环结构
  • 子程序结构(高级语言里的方法)
  • 以及一些高级汇编语言技术。

然后汇编语言的更多相关细节就不展开详细说了,像调用系统中断,BIOS中断,以及DOS中断等。(中断程序就是写好了的一个函数,中断向量表存放着这个函数的入口地址)

总之汇编就是把我们计算机里面的机器操作指令转换为了便于记忆的符号标识。学习汇编可以让我们更清楚得理解程序是怎么在CPU内部运行的。

然后通过一段简单的汇编代码体会一下汇编是如何利用寄存器和寻址机制进行工作的。

DATAS SEGMENT ;定义数据段,开始
str_tip db 'when input xiaoxie word ,change to daxie',0dh,0ah,24h ;str_tip以db字节存储字符串
nextLine db 0dh,0ah,24h ;定义一个换行符
str_exit db 'welcome next time ,i will be waiting for you!' ;离开提示符
DATAS ENDS ;数据段,结束
STACKS SEGMENT ;定义堆栈段,开始
db 30h dup(?)
top label word
STACKS ENDS ;结束开始
CODES SEGMENT ;定义代码段,开始
ASSUME CS:CODES,DS:DATAS,SS:STACKS ;声明CODES,DATAS,
STACKS,存放的寄存器
START:
MOV AX,DATAS
MOV DS,AX
MOV AX,STACKS
MOV SS,AX
LEA SP,TOP ;取得地址,并存放进相应段寄存器
mov dx,offset str_tip ;中断程序的参数(方法参数)
mov ah,9 ;中断程序的类型(具体哪个方法)
int 21h ;调用DOS系统中断程序
xunhuan:
mov ah,1
int 21h ;调用1号函数,没有参数。
push ax ;保留寄存器的值,因为下一步的运算会用到该AX寄存器,防止被破坏了。
mov dx,offset nextLine
mov ah,9
int 21h ;也是调用DOS系统中断
pop ax ;系统中断调用完后,回复刚才那个函数执行时候被推进栈内的上下文相关的寄存器。因为刚才那个过程(即中断,也就是函数)只用来AX,也只推进了AX的值进栈,所以我们恢复刚才的环境的时候只需要pop出栈的值到AX就可。当然你也刚才把所有寄存器都存进栈,这时就把所有寄存器都弹出栈,不过很明显这样会浪费栈资源。
cmp al,0dh
je exit
cmp al,'a'
jb output
cmp al,'z'
ja output ;简单的分支结构,要完全理解的话,要看书。
sub al,20h
output:
mov dl,al
mov ah,2
int 21h
mov dx,offset nextLine
mov ah,9
int 21h
jmp xunhuan
exit:
mov dx,offset str_exit
mov ah,9
int 21h
MOV AH,4CH
INT 21H ;调用程序结束的函数,相当于return。
CODES ENDS
END START ;程序结束

更多文件点击此处