汇编语言第4版 王爽(一)
<p>本书采用以8086CPU为中央处理器的PC机来进行学习。8086CPU常用而结构简洁,可以方便地进行实践,便于教学。纯粹的8086PC机已经不存在了,但是现在的任何一台PC机中的微处理器,只要是和Intel兼容的系列,都可以用8086的方式进行工作。</p><h5 id="附注1">附注1</h5>
<p>微机中常用的Intel系列微处理器的主要发展过程是:8080,8086/8088,80186,80286,80386,80486,Pentium,Pentium II,Pentium III, Pentium4.<br>
8086/8088是一个重要的阶段,它们略有区别,功能相同。8088被IBM用在它所生产的第一台微机上,该微机的结构事实上成为以后微机的基本结构。<br>
80386是第二个重要的型号,随着微机应用及性能的发展,在微机上构造可靠的多任务操作系统的问题日益突出。人们希望自己的PC机能够稳定地同时运行多个程序,同时处理多项工作,或将PC机用作主机服务器,运行UNIX那样的多用户系统。<br>
8086/8088不具备实现一个完善的多任务操作系统的功能。为此Intel开发了80286,80286具备了对多任务系统的支持。但对8086/8088的兼容却做得不好。这妨碍了用户对原8086机上的程序的使用。IBM最早基于80286开发了多任务系统OS/2,结果犯了一个战略错误。</p>
<p>随后Intel开发了80386微处理器,可以在3个模式下工作。</p>
<ol>
<li>实模式:工作方式相当于一个8086</li>
<li>保护模式:提供支持多任务环境的工作方式,建立保护机制(这与VAX等小型机类似)。</li>
<li>虚拟8086模式:可从保护模式切换至其中的一种8086工作方式。这种方式的提供使用户可以方便地在保护模式下运行一个或多个原8086程序。</li>
</ol>
<p>80286的缺陷在于,它只提供了实模式和保护模式,没有提供虚拟8086模式。这使得基于80286构造的多任务系统,不能方便地运行原8086系统中的程序。如果运行原8086系统中的程序,需要重启计算机,使CPU工作在实模式下才行。<br>
从80386到当前的CPU,提供8086实模式的目的是为了兼容。现今CPU的真正有效力的工作模式是支持多任务操作系统的保护模式。</p>
<h4 id="基础知识">基础知识</h4>
<h5 id="汇编语言的组成">汇编语言的组成</h5>
<ol>
<li>汇编指令:机器码的助记符,有对应的机器码</li>
<li>伪指令:没有对应的机器码,由编译器执行,计算机并不执行。</li>
<li>其他符号:如+、-、*、/等,由编译器识别,没有对应的机器码。</li>
</ol>
<p>8088的数据总线宽度是8,8086的数据总线宽度是16。8088送2字节数据第一次送低字节,第二次送高字节。</p>
<h6 id="检测点11">检测点1.1</h6>
<blockquote>
<p>1、13<br>
2、1024 0 1023<br>
3、2^13 2^10<br>
4、2^30 2^20 2^10<br>
5、64 1 16 4<br>
6、1 1 2 2 4<br>
7、512 256<br>
8、二进制</p>
</blockquote>
<p>扩展插槽通过总线和CPU相连,接口卡插在扩展插槽上。通过总线,CPU可以直接控制这些接口卡,从而实现CPU对外设的间接控制。简而言之,就是CPU通过总线向接口卡发送命令,接口卡根据CPU的命令控制外设进行工作。<br>
<img src="https://img2022.cnblogs.com/blog/1980824/202206/1980824-20220605154627520-748946569.png"></p>
<p>CPU在操控这些存储器时,把它们都当做内存来对台,把它们总的看作一个由若干存储单元组成的逻辑存储器,这既是我们所说的内存地址空间。学习汇编时,我们所面对的是内存地址空间。</p>
<p><img src="https://img2022.cnblogs.com/blog/1980824/202206/1980824-20220605154723328-1452603787.png"></p>
<p>每个物理存储器在这个逻辑存储器中占有一个地址段,即一端地址空间。CPU在这段地址空间中读写数据,实际上就是在相对应的物理存储器中读写数据。<br>
假设,上图中的内存地址空间的地址段分配如下<br>
地址0~7FFFH的32KB空间为主随机存储器的地址空间;<br>
地址8000H~9FFFH的8KB空间为显存地址空间<br>
地址A000H~FFFFH的24KB空间为各个ROM的地址空间。</p>
<p>内存地址空间的大小受CPU总线宽度的限制。</p>
<p>8086PC机内存地址空间分配的基本情况,8086的地址总线宽度为20,寻址空间为1MB<br>
<img src="https://img2022.cnblogs.com/blog/1980824/202206/1980824-20220605155858183-670184500.png"></p>
<h4 id="寄存器">寄存器</h4>
<h6 id="通用寄存器">通用寄存器</h6>
<p>8086CPU有14个寄存器,分别是AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。</p>
<p>8086CPU的所有寄存器都是16位的,可以存放两个字节。AX、BX、CX、DX是通用寄存器。</p>
<p>8086CPU的上一代CPU中的寄存器都是8位的,为了保证兼容,是原来基于8086CPU编写的程序稍加修改就可以运行在8086CPU上,故通用寄存器都可以分为两个可独立使用的8位寄存器来用:如,AX可分为AL和AH。</p>
<p>字:记为word,由两个字节组成,分别称为这个字的高位字节和低位字节。</p>
<p>在进行数据传送或运算时,要注意指令的两个操作对象的位数应当是一致的。两个操作对象的位数不一致的指令是错误的。</p>
<p>检测点2.1</p>
<blockquote>
<p>AX=F4A3H<br>
AX=31A3H<br>
AX=3123H<br>
AX=6246H<br>
BX=826CH<br>
CX=6246H<br>
AX=826CH<br>
AX=04D8H<br>
AX=0482H<br>
AX=6C82H<br>
AX=D882H<br>
AX=D888H<br>
AX=D810H<br>
AX=6246H</p>
</blockquote>
<blockquote>
<p>mov ax,2<br>
add ax,ax<br>
add ax,ax<br>
add ax,ax</p>
</blockquote>
<p>8086是16位机,也可以说8086是16位结构的CPU。<br>
概括地讲,16位结构(16位机,字长为16位)描述了一个CPU具有下面几个方面的结构特性:</p>
<ul>
<li>运算器一次最多可以处理16位的数据;</li>
<li>寄存器的最大宽度为16位;</li>
<li>寄存器和运算器之间的通路为16位。</li>
</ul>
<p>在8086内部,能够一次性处理、传输、暂时存储的信息的最大长度是16位的。内存单元的地址在送上地址总线之前,必须在CPU中处理、传输、暂时存放,对于16位CPU,能一次性处理、传输、暂时存储16位的地址。</p>
<p>8086CPU有20位地址总线,可以传送20位地址,达到1MB寻址能力,内部又是16位结构。8086CPU采用一种内部用两个16位地址合成的方法来形成一个20位的物理地址。<br>
<img src="https://img2022.cnblogs.com/blog/1980824/202206/1980824-20220605163034086-1683265539.png"><br>
地址加法器采用物理地址=段地址X16+偏移地址的方法用段地址和偏移地址合成物理地址。<br>
8086CPU的这种寻址功能是“基础地质+偏移地址=物理地址”寻址模式的一种具体实现方案。8086CPU中,段地址X16可看作是基础地址。</p>
<h6 id="段的概念">段的概念</h6>
<p>内存并没有分段,段的划分来自于CPU。段的大小也来自于划分,可大可小。<br>
将若干地址连续的内存单元看作一个段,用段地址X16定位段的起始地址(基础地址),用偏移地址定位段中的内存单元。<br>
有两点需要注意,段地址X16必然是16的倍数,所以一个段的起始地址也一定是16的倍数,偏移地址为16位,16位地址的寻址能力为64KB,所以一个段的长度最大为64KB。</p>
<p>检测点2.2</p>
<blockquote>
<p>00010H 1000FH<br>
1001H 2000H<br>
段地址小于1001H或大于2000H</p>
</blockquote>
<h6 id="段寄存器">段寄存器</h6>
<p>4个段寄存器:CS,DS,SS,ES<br>
8086PC机中,任意时刻,CPU将CS:IP指向的内容当做指令执行。</p>
<p>读取一条指令后,IP中的值自动增加,以使CPU可以读取下一条指令,IP中增加的值为读取的指令的长度(字节数)。例如读入B82301,长度为3个字节,IP中的值加3</p>
<p>8086CPU的工作过程可以简要描述如下。<br>
(1)从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;<br>
(2)IP=IP+所读取指令的长度,从而指向下一条指令;<br>
(3)执行指令,转到步骤(1),重复这个过程。</p>
<p>在8086CPU加电启动或复位后(即CPU刚开始工作时)CS和IP被设置为CS=FFFFH,IP=0000H。</p>
<h6 id="指令ffff0h">指令FFFF0H</h6>
<blockquote>
<p>FFFF0h与07C00h,这两个都是机器启动后默认访问的内存地址。<br>
首先要知道bios是用来初始化硬件的最底层的软件(然后才是操作系统),因此计算机启动后必须最先被执行。另外我们都知道CPU只能执行内存中的内容的,而一般内存中的数据是易失性的,断电之后内容就会消失。工程师的解决方法是:将存放bios的rom芯片与内存芯片统一编址(不明白的话去看看微机原理与接口的书就明白了)。这样就可以把存放bios的ROM芯片看作是数据永远不会消失不允许被更改的内存了。<br>
开机启动后默认的CS=FFFFh IP=0000h。这个地址就是bios的地址。这段内存空间很小,所以不能够容下操作系统等大型程序。</p>
</blockquote>
<blockquote>
<p>相对bios而言操作系统的功能更强大,更新也更快但是也需要更多的空间,通常放在硬盘中。但是如果没有bios的话,那么将会连硬盘都不能使用,又如何启动存放在硬盘中的操作系统呢?正是由于此,机器启动后自动执行bios使其它完成硬件初始化(这样包括硬盘在内的cpu以外的其他硬件设备就可以工作了)。bios完成硬件初始化的任务后,就要把权力移交给操作系统。</p>
</blockquote>
<blockquote>
<p>工程师进行了强制性的规定:到内存中的07c00h 处寻找系统的引导程序,即CS=0000h IP=7c00h。也就是说任何系统,他的引导程序都必须安排在07c00h开始的地方,否则就不能被正确的引导。当引导程序完成后我们就进入了Linux Windows等系统了。</p>
</blockquote>
<blockquote>
<p>伪指令org用来规定目标程序存放单元的偏移量。比如,如果在源程序的第一条指令前用了如下指令:<br>
org 200h<br>
那么,汇编程序会把指令指针的ip的值设成200h,即目标程序的第一个字节放在200h处,后面的 内容则顺序存放,除非遇上另一个org 语句</p>
</blockquote>
<h6 id="修改csip的指令">修改CS、IP的指令</h6>
<p>mov指令不能用于设置CS、IP的值,因为8086CPU没有提供这样的功能。能够改变CS、IP内容的指令被统称为转移指令。<br>
可以用<em>jmp 段地址:偏移地址</em>来修改CS、IP的内容。如果想仅修改IP的内容,可用形如<em>jmp 某一合法寄存器</em>的指令完成。</p>
<h6 id="代码段">代码段</h6>
<p>对于8086PC机,在编程时,可以根据需要,将长度为N(N<=64KB)的一组代码,存在一组地址连续、起始地址为16的倍数的内存单元中。我们可以认为,这段内存时用来存放代码的,从而定义了一个代码段。<br>
为了使代码段中的指令被执行,我们需要设置CS、IP的值,使其指向代码段的第一条指令。</p>
<p>检测点2.3</p>
<blockquote>
<p>4次<br>
每次读取指令后和执行完jmp指令后<br>
0</p>
</blockquote>
<p>Debug</p>
<blockquote>
<p>Debug是DOS、Windows都提供的实模式(8086方式)程序的调试工具。使用它可以查看CPU各种寄存器中的内容、内存的情况和在机器码机跟踪程序的运行。</p>
</blockquote>
<p>我们用到的Debug功能</p>
<ul>
<li>R命令查看、改变寄存器的内容</li>
<li>D命令查看内存中的内容</li>
<li>E命令改写内存中的内容</li>
<li>U命令将内存中的机器指令翻译成汇编指令</li>
<li>T命令执行一条机器指令</li>
<li>A命令以汇编指令的格式在内存中写入一条机器指令。</li>
<li>P命令将一个子程序调用指令、循环指令、中断指令或一个重复字符串指令,停止在下一条指令上。</li>
</ul>
<p>在Windows中进入DOS方式,此时进入的是虚拟8086模式的DOS。 现在的Windows10默认已经移除了这个插件,所以使用dosbox。</p>
<h6 id="安装dosbox和汇编工具">安装dosbox和汇编工具</h6>
<blockquote>
<p>链接:https://pan.baidu.com/s/1UwSpoh9mU0PtJ00BF63zRA<br>
提取码:hiaj</p>
</blockquote>
<ol>
<li>下载后解压并安装dosbox,我安装在e:\dosbox目录下</li>
<li>将asm文件夹移到e:\dosbox目录下<br>
<img src="https://img2022.cnblogs.com/blog/1980824/202206/1980824-20220607105637776-1574597721.png"></li>
<li>打开dosbox,输入</li>
</ol>
<blockquote>
<p>mount c E:\dosbox\asm<br>
C:</p>
</blockquote>
<p>或者找到下图中所在位置的conf文件,将上述两条代码加到最后。<br>
<img src="https://img2022.cnblogs.com/blog/1980824/202206/1980824-20220607105828491-1482831229.png"></p>
<h5 id="寄存器内存访问">寄存器(内存访问)</h5>
<p>字单元</p>
<blockquote>
<p>存放一个字型数据(16位)的内存单元,由两个连续的内存单元组成。高地址内存单元存放字型数据的高位字节,低地址单元中存放字型数据的低位字节。我们将起始地址为N的字单元简称为N地址字单元。</p>
</blockquote>
<p>DS和<br>
8086CPU不支持将数据直接送入段寄存器的操作,所以将段地址送入ds要进过通用寄存器中转。<br>
mov指令支持将数据直接送入寄存器(非段寄存器),将一个寄存器中的内容送入另一个寄存器,将内存单元中的数据送入一个寄存器(可以是段寄存器),将寄存器(可以是段寄存器)中的数据送入内存单元,将一个寄存器的内容送入一个段寄存器,讲一个段寄存器的内容送入一个寄存器中<br>
段寄存器不能作为add/sub指令的操作对象。</p>
<p>检测点3.1</p>
<blockquote>
<p>AX=2662H<br>
BX=E626H<br>
AX=E626H<br>
AX=2662H<br>
BX=D6E6H<br>
AX=FD48H<br>
AX=2C14H<br>
AX=0<br>
AX=00E6H<br>
BX=0<br>
BX=0026H<br>
AX=000CH</p>
</blockquote>
<blockquote>
<p>1,mov ax,6622H<br>
2,jmp 0fff:0100<br>
3,mov ax,2000H<br>
4,mov ds,ax<br>
5,mov ax,<br>
6,mov ax,<br>
数据和程序在计算机中都是以二进制的形式存放的,在区别数据和程序时,关键是看段地址,如果段地址是ds,则说明存放的是数据,如果段地址是CS,则说明内存存放的是指令。</p>
</blockquote>
<h6 id="栈">栈</h6>
<p>LIFO(Last In First Out,后进先出)8086CPU的入栈和出栈操作都是以字为单位进行的。</p>
<p>任意时刻,SS:SP指向栈顶元素。push和pop指令执行时,CPU从SS和SP中得到栈顶的地址。</p>
<p>push的过程,SP = SP - 2,将数据送入SS:SP指向的内存单元处,高字节送入高地址(靠近栈底),低字节送入低地址(靠近栈顶),SS:SP此时指向新栈顶。</p>
<p>pop的过程,将SS:SP指向的内存单元的数据送入寄存器或内存单元,SP=SP+2,此时SS:SP指向新的栈顶元素(原来栈顶元素的下面的一个元素)<br>
栈空时,SS:SP指向栈空间最高地址单元的下一个单元,栈满时,SS:SP指向栈空间的最低地址单元,即栈顶元素的低地址单元。</p>
<p>8086CPU并没有记录栈顶上限和栈底的寄存器,我们在编程的时候要自己操心栈顶超界的问题,要根据可能用到的最大栈空间,来安排站的大小,防止入栈的数据太多而导致的超界;执行出栈操作的时候也要注意,防止栈空时继续出栈导致的超界。</p>
<p>8086CPU只知道栈顶在何处(SS:SP),而不知道安排的栈空间有多大,就像CPU只知道当前要执行的指令在何处(CS:IP),而不知道要执行的指令有多少。8086CPU的工作机理:它只考虑当前的状况:当前的栈顶,当前要执行的指令。</p>
<p>push、pop指令的操作对象可以是寄存器,内存单元,段寄存器。</p>
<p>pop、push等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0~FFFFH。</p>
<h6 id="栈段">栈段</h6>
<p>一段内存,可以既是代码的存储空间,又是数据的存储空间,还可以是栈空间,也可以什么都不是。关键在于CPU寄存器的设置,即CS、IP、SS、SP、DS的指向。</p>
<p>检测点3.2</p>
<blockquote>
<p>mov ax,2000H<br>
mov ss,ax<br>
mov sp,0010H</p>
</blockquote>
<blockquote>
<p>mov ax,1000H<br>
mov ss,ax<br>
mov sp,0000H</p>
</blockquote>
<p>实验2<br>
在使用debug的t指令逐指令运行程序时,发现运行修改栈段寄存器SS的指令,它的下一条指令<em>mov sp,10</em>也紧接着执行。<br>
(1)略<br>
(2)在2000:0 f内存中,存放着SS、CS、IP、标志寄存器等内容。</p><br><br>
来源:https://www.cnblogs.com/blogofyf/p/16351291.html
頁:
[1]