汇编语言(Assembly Language)是任何一种用于电子计算机、微处理器、微控制器或其他可编程器件的低级语言,亦称为符号语言。在汇编语言中,用助记符代替机器指令的操作码,用地址符号或标号代替指令或操作数的地址。在不同的设备中,汇编语言对应着不同的机器语言指令集,通过汇编过程转换成机器指令。特定的汇编语言和特定的机器语言指令集是一一对应的,不同平台之间不可直接移植。
总而言之,汇编语言是面向机器的程序设计语言,学好汇编能够更好的帮助理解计算机的底层原理。
《编码的奥秘》一书中有句这样的话,“实际上任何两种不同的东西经过一定的组合都可以代表所有种类的信息。”有兴趣的可以先读一读这本书,简单理解为计算机的核心是由0和1构成。计算机运行这种0和1的指令(即机器语言)完成相应的操作,而这个0和1可以由高低电频来实现,想更深入了解可以去学习一下计算机组成原理。例如摩斯密码,就是由点和划编码而成。能够表达出一些信息。
很快人们就发现0和1的指令,还是过于复杂,不方便人们阅读和编写。继而就出现了汇编语言,通过更容易让人理解的指令去编码。当然尽管是汇编语言的指令,也需要转成机器语言让计算机执行。如下图所示
机器语言是机器指令的合集。
汇编语言为了方便理解和记忆,例如执行机器指令“1000100111011000”表示把寄存器BX的内容送到AX中,汇编指令则写成 mov ax,bx(move 移动 执行该指令后 ax = bx)
1.汇编指令:机器码的助记符,有对应的机器码
2.伪指令:没有对应的机器码,由编译器执行,计算机并不执行
3.其他一些符号:比如 + - * / 由编译器识别,没有对应的机器码
上面仅供参考
正式的解释如下
指令语句:又称机器指令语句,翻译后能够产生相应的机器代码,并且能被CPU直接识别并执行相应的操作。常见的基本指令如ADD、SUB、AND等。书写指令必须遵循指令的格式规范。经过翻译后会产生相应的机器代码。指令语句是在程序运行的时候完成。
伪指令语句:指示汇编程序在翻译源程序的时候完成某些工作,比如给变量分配存储单元、给某个符号赋值等。翻译后不会产生机器代码。伪指令语句是在源程序汇编的时候完成。
宏指令语句:允许用户多次重复使用的程序代码段称为宏。
一台计算机中最重要的是CPU(中央处理器),它是用来控制整个计算机的运行和计算的。要想让cpu正常工作,我们就需要有机器语言来进行交流指挥它工作,也就是我们常说的指令和数据。而这些指令和数据就存放在存储器中,也就是我们的内存。这就好比我们的大脑和记忆,没有了记忆,大脑无法进行思考。cpu只能读取存放在内存中的数据,磁盘中的数据要是不输入内存中,是无法被cpu所执行的。
一般应具有存储数据和读写数据的功能,以8位二进制作为一个存储单元,也就是一个字节。每个单元有一个地址,是一个整数编码,可以表示为二进制整数。程序中的变量和主存储器的存储单元相对应。变量的名字对应着存储单元的地址,变量内容对应着单元所存储的数据。存储地址一般用十六进制数表示,而每一个存储器地址中又存放着一组二进制(或十六进制)表示的数,通常称为该地址的内容。
存储器被划分为许许多多个存储单元,这样的话就方便cpu来寻找指令和数据是在什么地方的。就像你在酒店找编号001的房间,你就会去到编号为001的房间,而不会去到别的房间里。我们也规定了1个存储单元里是有8个bit的也就是8个0、1代码,我们称为1个字节(Byte)。当存储单元特别多的时候,Byte就不够用了,我们就有其他的单位用来计算大小:
1KB=1024B(2^10B),
1MB=1024KB(2^20B),
1GB=1024MB(2^30B),
1TB=1024GB(2^40B)
当想到这样一个问题时:数据和指令都是由0和1构成的,一个0和1的组合到底是指令还是数据呢?计算机是如何区分这个的?(这个问法更好:汇编语言,通过编译器编译,最后送给CPU的都是二进制数,那么CPU在一堆纷繁复杂的二进制,0110000110001中如何区分指令和数据呢?)
1.每台机器的指令,其格式和代码所代表的含义都是硬性规定的,故称之为面向机器的语言,也称为机器语言。二进制,作为一种机器码,计算机可以直接识别,不需要进行任何翻译。
2.计算机硬件主要通过不同的时间段来区分指令和数据,即:取指周期(或取指微程序)取出的既为指令,执行周期计算机加载的第一条肯定是指令,然后根据这条指令去取二进制数,如果这条指令要取操作数,那么取出来的就是操作数;如果这条指令要取下一条指令,那么取出来得就是指令。
3.把指令和数据分开放是为了安全和逻辑结构清晰。
4.虽然指令和数据存放的格式一样,但是访问他们的时机不同。在取指令时期,cpu通过指令流取指令,存放在指令寄存器, 然后解释并执行指令;在执行指令时期,cpu通过数据流取数据, 存放在数据寄存器。 所以指令流取的是指令,数据流取的是数据。
一个典型的CPU大概由运算器、控制器、寄存器构成。外部总线实现CPU和主板上其他器件的联系,内部总线实现CPU内部各个部件的链接。
以CPU8086为例:
8086CPU有14个16位寄存器: AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW
AX、BX、CX、DX这4个通常用来存放一般性的数据,被称为通用寄存器
为了兼容上一代8位CPU,可分为2个8位寄存器使用:
AX可分为AH和AL BX可分为BH和BL CX分为CH和CL DX分为DH和DL
8086cpu一次性处理2种尺寸数据:字节(byte) 字(word) 一个字等于两个字节
-----指令的例子----
mov ax,18 将18送入寄存器AX AX = 18 mov ah,88 将88送入寄存器AH中 AH = 88 add ax,8 AX = AX+8 mov ax,bx AX = BX add ax,bx ax = ax+bx sub ax,bx ax = ax-bx
注意寄存器位数 正确用法 不正确用法 mov bx,ax mov ax,bl mov ax,18H mov al,20000 add ax,bx add ax,2000 寄存器位数不同,它就有可能不能使用指令。
8086有20根总线,可以传送20位地址,寻址能力达到1MB
16位结构寻址 2^16 65536bit = 64kb
采用2个16位地址合成的方法来形成20位地址
地址加法器采用 段地址 * 16 + 偏移地址 的方法合成物理地址,其实就是用一个基础地址加上一个偏移地址
例如,8086CPU要访问123C8H的内存单元,此时,段地址位1230,偏移地址为00C8,加法器给段地址123016=12300(注意此处采用的是16进制,要以16进制的思想去理解,而非,常用的10进制),并与偏移地址003C相加,就得到了20位的物理地址123C8。即123016+00C8=123C8.
"段地址"这个名称中包含着"段"的概念。这种说法可能会给初学者一定误导,误以为内存会划分成一个一个的段,然后每一段都有一个段地址。这个理解是不正确的。
其实,内存并没有分段,段的划分来自于CPU,由于8086CPU用"基础地址(段地址16)+偏移地址=物理地址"的方式给出内存单元的物理地址,使得我们可以用分段的方式来管理内存。如下图所示,我们可以认为:地址10000H到100FFH的内存单元组成一个段,该段的起始地址(基础地址)为10000H,段地址为1000H,大小为100H;我们也可以认为地址10000H到1007FH、10080H到100FFH的内存单元组成两个段,它们的起始地址(基础地址)为:10000H和10080H,段地址为:1000H和1008H,大小都为80H。
以后,在编程时可以根据需要,将若干地址连续的内存单元看作一个段,用段地址16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。有两点需要注意:段地址*16必然是16的倍数,所以一个段的起始地址也一定是16的倍数;偏移地址为16位,16位地址的寻址能力为64KB,所以一个段的长度最大为64KB。
CPU访问内存单元时,必须向内存提供内存单元的物理地址。8086CPU在内部用段地址和偏移地址移位相加的方法形成最终的物理地址。
(1)观察下面的地址
物理地址 段地址 偏移地址 21F60H 2000H 1F60H 2100H 0F60H 21F0H 0060H 21F6H 0000H 1F00H 2F60H
可以得出一个结论:CPU可以用不同的段地址和偏移地址形成同一个物理地址。
比如CPU要访问21F60H单元,则它给出的段地址SA和偏移地址EA满足SA×16+EA=21F60H即可。
(2)如果给定一个段地址,仅通过变化偏移地址来进行寻址,最多可定位多少个内存单元?
结论:偏移地址16位,变化范围为0~FFFFH,仅用偏移地址来寻址最多可寻64KB个内存单元。
比如给定段地址1000H,用偏移地址寻址,CPU的寻址范围为:10000H~1FFFFH。
在8086PC机中,存储单元的地址用两个元素来描述,即段地址和偏移地址。
"数据在21F60H内存单元中。“这句话对于8086PC机一般不这样讲,取而代之的是两种类似的说法:
①数据存在内存2000:1F60单元中;
②数据存在内存的2000段中的1F60单元中。
这两种描述都表示"数据在内存21F60单元中”。
可以根据需要,将地址连续、起始地址为16的倍数的一组内存单元定义为一个段。
段寄存器提供段地址,8086CPU有4个段寄存器:CS DS SS ES
CS和IP它们指示了CPU当前要读取指令的地址,CS叫代码段寄存器,IP为指令指针寄存器
在任意时刻,8086CPU将从CS的内容x16+ip的内容单元处开始读取一条指令执行
上图说明如下。
(1) 8086CPU当前状态:CS中的内容为2000H,IP中的内容为0000H;
(2) 内存20000H~20009H单元存放着可执行的机器码;
(3) 内存20000H~20009H单元中存放的机器码对应的汇编指令如下。
地址:20000H~20002H,内容:B8 23 01,长度:3Byte,对应汇编指令:mov ax,0123H
地址:20003H~20005H,内容:BB 03 00,长度:3Byte,对应汇编指令:mov bx,0003H
地址:20006H~20007H,内容:89 D8,长度:2Byte,对应汇编指令:mov ax,bx
地址:20008H~20009H,内容:01 D8,长度:2Byte,对应汇编指令:add ax,bx
CPU读取执行指令步骤:
1.从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器
2.IP = IP+读取的指令的长度,指向下一条指令
3.执行指令,执行完成转到步骤1,重复这个过程
8086CPU加电启动或复位后 CS和IP的值被设置为CS=FFFFH IP=0000H 开机后执行的第一条指令
改变cs:ip的指令 jmp(跳转)
“jmp段地址:偏移地址”
如:
jmp 2AE3:3,执行后:CS=2AE3H,IP=0003H,CPU将从2AE33H处读取指令。
jmp 3:0B16,执行后:CS=0003H,IP=0B16H,CPU将从00B46H处读取指令。
同时修改cs和ip jmp 0fffh:0100h
若仅修改ip内容,jmp,某个寄存器(mov ax,0fffh , jmp ax)
注意:可以理解为 jmp ax == mov ip,ax
下题可以更好的理解以上内容:
设CPU初始状态:CS=2000H,IP=0000H,请写出指令执行序列。
CPU对上图的指令的执行过程如下。
(1) 当前CS=2000H,IP=0000H,则CPU从内存2000H×16+0=20000H处读取指令,读入的指令是:B8 22 66(mov ax,6622H),读入后IP=IP+3=0003H;
(2) 指令执行后,CS=2000H,IP=0003H,则CPU从内存2000H×16+0003H =20003H处读取指令,读入的指令是:EA 03 00 00 10(jmp 1000:0003),读入后IP=IP+5=0008H;
(3) 指令执行后,CS=1000H,IP=0003H,则CPU从内存1000H×16+0003H =10003H处读取指令,读入的指令是:B8 00 00(mov ax,0000),读入后IP=IP+3=0006H;
(4) 指令执行后,CS=1000H,IP=0006H,则CPU从内存1000H×16+0006H =10006H处读取指令,读入的指令是:8B D8(mov bx,ax),读入后IP=IP+2=0008H;
(5) 指令执行后,CS=1000H,IP=0008H,则CPU从内存1000H×16+0008H =10008H处读取指令,读入的指令是:FF E3(jmp bx),读入后IP=IP+2=000AH;
(6) 指令执行后,CS=1000H,IP=0000H,CPU从内存10000H处读取指令……
经分析后,可知指令执行序列为:
(1) mov ax,6622H
(2) jmp 1000:3
(3) mov ax,0000
(4) mov bx,ax
(5) jmp bx
(6) mov ax,0123H
(7) 转到第3步执行
参考博客:
https://blog.csdn.net/cosmoslife/article/details/7671078
https://blog.csdn.net/syf442/article/details/4394723
以上很多都参考于上面的博客。《汇编语言》—王爽,这本书有时间得好好看看!!!