汇编语言(以8086为例)
<h1 id="汇编语言以8086为例">汇编语言(以8086为例)</h1><p>【通俗易懂的汇编语言(王爽老师的书)】 </p>
<h2 id="01-序言">01-序言</h2>
<h3 id="p3-由机器指令到汇编指令">P3 由机器指令到汇编指令</h3>
<p><strong>机器语言:</strong> 是机器指令的集合。</p>
<p><strong>机器指令:</strong> 是一台机器可以正确执行的命令。机器指令由一串二进制数表示,例如01010000。</p>
<p>由于机器指令的可读性极差,所以有了汇编语言与汇编指令。</p>
<p></p>
<p><strong>汇编语言:</strong> 的主体是汇编指令。</p>
<p><strong>汇编指令</strong>和机器指令表达的意思是相同的,他们的差别在于指令的表示方法上:汇编指令是机器指令便于记忆的书写形式;汇编指令是机器指令的<strong>助记符</strong>。</p>
<ul>
<li>机器指令:1000100111011000</li>
<li>汇编指令:MOV AX BX</li>
<li>操作:将寄存器BX的内容送到AX中</li>
</ul>
<p></p>
<p><strong>工作过程:</strong> 汇编语言-> 编译器-> 机器码-> 计算机执行</p>
<p></p>
<p><strong>汇编语言程序包含:</strong></p>
<ul>
<li>汇编指令——机器码的助记符</li>
<li>伪指令——由编译器执行</li>
<li>其他符号——由编译器识别</li>
</ul>
<p></p>
<h3 id="p4-计算机的组成">P4 计算机的组成</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240304150139-udj8ahh.png"></p>
<p>CPU想要工作,就必须向它提供指令和数据。而<strong>指令和数据在存储器(内存)中存放</strong>。</p>
<p></p>
<p><strong>指令和数据的表示:</strong> 数据和指令存储在内存或磁盘上;数据和指令都是二进制信息。</p>
<p></p>
<p><strong>存储单元:</strong> 存储器被划分为若干个存储单元,每个存储单元从0开始顺序编号;实际的内存空间很“大”。例如8086有20条数据线,寻址空间为1MB。</p>
<p></p>
<p><strong>总线:</strong> 从物理上将,总线是一根根导线的集合;逻辑上则将总线分为地址总线、数据总线、控制总线三种总线。</p>
<p>CPU通过<strong>地址总线</strong>来指定存储单元,地址总线的宽度决定了可寻址的存储单元的大小,N根地址总线对应2<sup>N</sup>的寻址空间。</p>
<p>CPU与内存或其他器件之间的数据传输是通过<strong>数据总线</strong>来进行的,数据总线的宽度决定了CPU和外界的数据传送的速度。</p>
<p>CPU通过<strong>控制总线</strong>对外部器件进行控制,控制总线是一些不同控制线的集合,控制总线的宽度决定了CPU对外部器件的控制能力。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240304151157-1m4yji7.png"><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240304151748-5bopp59.png"><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240304151757-ilgtopc.png"></p>
<p></p>
<h3 id="p5-内存的读写和地址空间">P5 内存的读写和地址空间</h3>
<p>CPU想要<strong>进行数据的读写</strong>,必须和外部器件进行三类信息的交互:</p>
<ul>
<li>存储单元的地址(地址信息)</li>
<li>器件的选择,读/写命令(控制信息)</li>
<li>读或写的数据(数据信息)</li>
</ul>
<p></p>
<p><strong>内存地址空间:</strong> 可用于存储数据和指令的内存地址的范围。由地址总线决定。</p>
<p>从CPU看地址空间:</p>
<ul>
<li>将各类存储器看作一个逻辑存储器——统一编址<br>
每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间(下图左侧为8086)<br>
<img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240304153115-x30epz7.png"></li>
<li>独立编址</li>
</ul>
<p></p>
<h3 id="p6-语言实践环境搭建">P6 语言实践环境搭建</h3>
<p>本课以8086CPU为例进行讲解。</p>
<p>因此实践方案为——DOS环境。具体软件为<strong>DOSBox</strong></p>
<p></p>
<h2 id="02-访问寄存器和内存">02-访问寄存器和内存</h2>
<h3 id="p7-本章导言">P7 本章导言</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305133439-d3wd9v2.png"></p>
<p></p>
<h3 id="p8-寄存器及数据存储">P8 寄存器及数据存储</h3>
<p><strong>寄存器:</strong> 是CPU内部的信息存储单元</p>
<p><strong>8086CPU有14个寄存器:</strong> 8086CPU所有的寄存器都是16位的,可以存放两个字节,存放的最大值为 2<sup>16</sup>-1</p>
<ul>
<li>通用寄存器:AX、BX、CX、DX</li>
<li>变址寄存器:SI、DI</li>
<li>指针寄存器:SP、BP</li>
<li>指令指针寄存器:IP</li>
<li>段寄存器:CS、SS、DS、ES</li>
<li>标志寄存器:PSW</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305134134-1n5limn.png"></p>
<p></p>
<p>但是有个问题,<strong>8086上一代CPU中的寄存器都是8位的,如何保证程序的兼容性?</strong></p>
<p><strong>解决方案:</strong> 将通用寄存器均分为两个独立的8位寄存器使用。例如,AX可以分为AH(high)和AL(low)。</p>
<p></p>
<p>8086是16位CPU,所以8086的<strong>字长(word size)</strong> 为16bit</p>
<p>一个<strong>字(word)</strong> 可以存放在一个16位寄存器中,这个字的高位字节存放在这个寄存器的高8位寄存器,这个字的低位字节存放在这个寄存器的低8位寄存器。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305134650-d29helq.png"></p>
<p></p>
<h3 id="p9-mov和add指令">P9 mov和add指令</h3>
<p><strong>在用中学</strong></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305135118-qp7h2b5.png"></p>
<p>懂了吧!那么直接开始做题</p>
<p>例01:注意溢出哦,溢出直接舍掉</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305135521-mebform.png"></p>
<p>例02:注意溢出哦,溢出直接舍掉,不能进到AH中</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305135541-d0zm1ux.png"></p>
<p></p>
<h3 id="p10-确定物理地址的方法">P10 确定物理地址的方法</h3>
<p>CPU访问内存单元时要给出内存单元的地址。</p>
<p>所有的内存单元构成的存储空间是一个一维的线性空间,每个内存单元在这个空间中都有唯一的地址,这个唯一的地址称为<strong>物理地址</strong>。</p>
<p></p>
<p>8086有2<strong>0位地址总线</strong>,可传送20位地址,寻址能力为1M。</p>
<p>8086是<strong>16位结构的CPU</strong>,运算器一次最多可以处理16位的数据,寄存器的最大宽度为16位。</p>
<p>那么在8086内部处理、传输、暂存的地址也是16位,寻址能力也只有16位。可是它有20位地址总线,剩下的难道浪费了吗?</p>
<p>8086给出了一个<strong>解决方案</strong>:用两个16位地址(段地址、偏移地址)合成一个20位的物理地址。</p>
<p><strong>地址加法器合成物理地址的方法:物理地址 = 段地址*16+偏移地址</strong></p>
<p>对于一个物理地址,可以通过不同的段地址和偏移地址获得。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305141138-doyx4g1.png"></p>
<p></p>
<h3 id="p11-内存的分段表示法">P11 内存的分段表示法</h3>
<p>已知8086CPU用“物理地址 = 段地址*16+偏移地址”的方式给出内存单元的物理地址。</p>
<p>段地址,就是分段的方式管理内存。但是要注意,<strong>内存本身并没有分段</strong>,段的划分来自于CPU。</p>
<p></p>
<p>根据合成物理地址的方法,可以得出两个结论:</p>
<ol>
<li>因为<strong>物理地址 = 段地址*16+偏移地址</strong>,段地址被*16了,所以<strong>一个段的起始地址一定是16的倍数(即最后一位一定为0,12340H这样)</strong></li>
<li>偏移地址为16位,16位地址的寻址能力为64K,所以<strong>一个段的最大长度为64K</strong></li>
</ol>
<p></p>
<p><strong>8086中存储单元地址的表示方法:</strong></p>
<p>例如,数据在21F60H内存单元中,段地址是2000H,那么有两种表示方法:</p>
<ol>
<li>数据存在内存 2000:1F60 单元中</li>
<li>数据存在内存的 2000H 段中的 1F60H 单元中</li>
</ol>
<p></p>
<p>物理地址 = 段地址*16+偏移地址,那段地址和偏移地址分别怎么来呢?</p>
<p>段地址很重要!所以8086提供了4个段寄存器,专门用于存放段地址:</p>
<ul>
<li>CS——代码段寄存器</li>
<li>DS——数据段寄存器</li>
<li>SS——栈段寄存器</li>
<li>ES——附加段寄存器</li>
</ul>
<p>而偏移地址可以用多种方法提供——8086有丰富的取址方式</p>
<p></p>
<h3 id="p12-debug的使用">P12 Debug的使用</h3>
<p>Debug是DOS系统中著名的调试程序,也可以运行在windows系统上</p>
<p>使用Debug程序,可以查看CPU各种寄存器中的内容、内存的情况,并且在机器指令级跟踪程序的运行</p>
<p></p>
<p><strong>具体操作:</strong></p>
<p>打开DOSBox,先进行挂载,选中工作目录</p>
<pre><code>mount c d:\0105\MASM //将Dos里的c盘,绑定到本机上d:\0105\MASM这个目录
c: //切换到c盘
dir //ls,确定一下挂载是否成功
</code></pre>
<p>debug的使用</p>
<pre><code>debug //启动程序,看到'-',说明启动成功
</code></pre>
<p>查看寄存器:R命令</p>
<pre><code>r //查看寄存器内容
r [寄存器名] //改变指定寄存器内容,输入在冒号时输入改变值
r[寄存器名] //不加空格也可以
</code></pre>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/TZ0PPIWOE3I5I65E478-20240305144207-jx6dfpc.png"></p>
<p></p>
<p>查看内存中的内容:D命令</p>
<pre><code>d //列出预设地址内存处,128个字节的内容
d [段地址:偏移地址] //列出内存中指定地址处,128个字节的内容dd
d [段地址:偏移地址] [结尾偏移地址] //列出内存中指定地址处,指定范围的内容
</code></pre>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305144443-hugxkdq.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/X691J50H73TGUNBE-20240305144558-e4x5xr0.png"></p>
<p></p>
<p>改变内存中的内容:E命令</p>
<pre><code>e [段地址:偏移地址] [数据1] [数据2] //默认是16进制,不需要加'H'
e [段地址:偏移地址] //逐个询问式修改。空格=接受,继续。回车=结束
</code></pre>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/HSO3JP1D21XVM90-20240305144721-pdngv8t.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/7LCCEB8XT3E4AV@9NYW-20240305144910-93jom3n.png"></p>
<p></p>
<p>将机器指令翻译成汇编指令:U命令</p>
<pre><code>u [段地址:偏移地址]
</code></pre>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/ZGK@96Z0A1I7Y1DQEWZH-20240305145121-mahoou7.png"></p>
<p></p>
<p>以汇编指令的格式在内存中写入机器指令:A命令</p>
<pre><code>a [段地址:偏移地址] //逐个询问式。空白内容时回车即可退出。
</code></pre>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305145520-hv5himl.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/A8CTL958FED_L575HWTX-20240305145540-ul6uwf7.png"></p>
<p></p>
<p>执行机器指令:T命令</p>
<pre><code>t //执行 CS:IP 处的指令
</code></pre>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/12XHWIAUNQFUB79LQC4UG-20240305145800-yi9xmeb.png"></p>
<p></p>
<p>退出debug:Q命令</p>
<pre><code>q //quit
</code></pre>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/8ONYRESZAZSRECNHKC02-20240305145916-axidfay.png"></p>
<p></p>
<h3 id="p13-csip与代码段">P13 CS、IP与代码段</h3>
<p><strong>CS:</strong> 代码段寄存器</p>
<p><strong>IP:</strong> 指令指针寄存器</p>
<p><strong>CS:IP:</strong> CPU将内存中 CS:IP 指向的内容当做指令执行</p>
<p></p>
<p><strong>8086工作过程简要描述:</strong></p>
<ol>
<li>从CS:IP指向的内存单元读取指令,读取的指令进入指令缓冲器;</li>
<li>IP = IP+所读取指令的长度,从而指向下一条指令;</li>
<li>执行指令。转到步骤(1),重复这个过程</li>
</ol>
<p></p>
<h3 id="p14-jmp指令">P14 jmp指令</h3>
<p>当我们需要修改下一条将要执行的指令时,本质上是需要修改CS、IP的值</p>
<p>那么该如何改变CS、IP的值呢?</p>
<p>使用debug直接修改——不靠谱;</p>
<p>用mov指令修改CS、IP寄存器——有些操作时不合法的</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/7337CCVMKKR3ZWFKTC17-20240305155140-slw1jd0.png"></p>
<p>所以答案是,<strong>jmp指令:</strong> 修改CS、IP的指令</p>
<p><strong>使用方法:</strong></p>
<ul>
<li>同时修改CS、IP的内容<br>
<code>jmp [段地址:偏移地址]</code>。例如:<code>jmp 2000:3</code><br>
功能:用指令中给出的段地址修改CS,偏移地址修改IP</li>
<li>仅修改IP的内容<br>
<code>jmp [某一合法寄存器]</code>。例如:<code>jmp ax</code><br>
功能:类似于<code>mov IP,ax</code></li>
</ul>
<p></p>
<p>jmp指令实践:</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/P@QKW7JPO@JPZDESNPX-20240305155337-7k2jazs.png"><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/KN@F8RMG@38EP6A79-20240305155614-zhhtx6v.png"></p>
<p></p>
<h3 id="p15-内存中字的存储">P15 内存中字的存储</h3>
<p>对于8086CPU,16位作为一个字。</p>
<p>16位的字存储在一个16位的<strong>寄存器</strong>中,高8位放高字节,低8位放低字节。</p>
<p>16位的字在<strong>内存</strong>中需要2个连续字节存储,低位字节存放在低地址单元,高位字节存放在高地址单元。</p>
<p>例如,存放4E20H 和 0012H</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/HWXE_@SXR7PKKWZLX-20240305160025-hxpghuf.png"></p>
<p></p>
<p><strong>字单元:</strong> 由两个地址连续的内存单元组成,存放一个<strong>字型数据(16位)</strong></p>
<p><strong>原理:</strong> 8086一个字是16位,需要内存中两个连续的字节来存储。</p>
<p></p>
<h3 id="p16-用ds和address实现字的传送">P16 用DS和实现字的传送</h3>
<p>CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址。而内存地址由段地址和偏移地址组成。怎样同时给出两个地址呢?</p>
<p><strong>解决方案:DS和配合</strong></p>
<ul>
<li>用DS寄存器存放要访问的数据的段地址</li>
<li>偏移地址用[...]的形式直接给出</li>
<li>注意:不能直接给DS赋值,只能通过通用寄存器给DS赋值</li>
<li>注意区分是,读写一个字还是一个字型</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/77I6G1GTVG6DEOMZW-20240305171401-wrjg49w.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240305173113-dqmzxp3.png"></p>
<p></p>
<h3 id="p17-ds与数据段">P17 DS与数据段</h3>
<p>要求:实现对内存单元中数据的访问</p>
<p>处理方法:<code>(DS):()</code>。用DS存放数据段的段地址;用相关指令访问数据段中的具体单元,单元地址由指出</p>
<p>将123B0H~123BAH的内存单元定义为数据段:</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/FTIFHTWWK8@G6@G3I-20240305183205-tv1drn8.png"></p>
<p>练习:</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/RKMIT5SQROBW@NHC2-20240305184503-71nr9bi.png"></p>
<p></p>
<h4 id="总结不一定完全"><strong>总结(不一定完全)</strong></h4>
<p><strong>用mov指令操作数据:</strong></p>
<table>
<thead>
<tr>
<th>指令形式</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>mov 寄存器, 数据</td>
<td>mov ax,8</td>
</tr>
<tr>
<td>mov 寄存器, 寄存器</td>
<td>mov ax,bx</td>
</tr>
<tr>
<td>mov 寄存器, 内存单元</td>
<td>mov ax,</td>
</tr>
<tr>
<td>mov 内存单元, 寄存器</td>
<td>mov ,ax</td>
</tr>
<tr>
<td>mov 段寄存器, 寄存器</td>
<td>mov ds,ax</td>
</tr>
<tr>
<td>mov 寄存器, 段寄存器</td>
<td>mov ax,ds</td>
</tr>
<tr>
<td>mov 内存单元, 段寄存器</td>
<td>mov ,ds</td>
</tr>
<tr>
<td>mov 段寄存器, 内存单元</td>
<td>mov ds,</td>
</tr>
</tbody>
</table>
<p><strong>add和sub指令:</strong></p>
<table>
<thead>
<tr>
<th>指令形式</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td>add 寄存器, 数据</td>
<td>add ax,8</td>
</tr>
<tr>
<td>add 寄存器, 寄存器</td>
<td>add ax,bx</td>
</tr>
<tr>
<td>add 寄存器, 内存单元</td>
<td>add ax,</td>
</tr>
<tr>
<td>add 内存单元, 寄存器</td>
<td>add ,ax</td>
</tr>
</tbody>
</table>
<p></p>
<p><strong>用DS和形式访问内存中数据段方法小结:</strong></p>
<ul>
<li>字在内存中存储时,要用两个地址连续的内存单元来存放。字的低位字节存放在低地址单元中,高位字节存放在高地址单元中</li>
<li>用mov指令访问内存单元时,可以在mov指令中只给出单元的偏移地址,此时段地址默认在DS寄存器中</li>
<li>表示一个偏移地址位address的内存单元</li>
<li>在内存和寄存器之间传送字型数据时,高地址单元和高8位寄存器对应,低地址单元和低8位寄存器对应</li>
<li>mov 、add 、sub是具有两个操作对象的指令,访问内存中的数据段。<br>
jmp是具有一个操作对象的指令,对应内存中的代码段</li>
</ul>
<p></p>
<h3 id="p18-栈及栈操作的实现">P18 栈及栈操作的实现</h3>
<p>栈:先进后出。每次操作最顶上的元素。</p>
<p><strong>栈有两个基本的操作:入栈和出栈</strong></p>
<p></p>
<p>现在的CPU都有栈的设计,例如8086就提供相关的指令,支持用栈的方式访问内存空间。</p>
<p><strong>主要有两个指令:</strong></p>
<ul>
<li>push:入栈。<code>push ax</code>,将ax的数据送入栈中</li>
<li>pop:出栈。<code>pop ax</code>,从栈顶取出数据送入ax</li>
</ul>
<p><strong>push ax:</strong></p>
<ol>
<li>SP = SP-2</li>
<li>将ax中的内容送入SS:SP指向的内存单元处,SS:SP此时指向新栈顶</li>
</ol>
<p><strong>pop ax:</strong></p>
<ol>
<li>将SS:SP指向的内存单元处的数据送入ax中</li>
<li>SP= SP+2,SS:SP指向当前栈顶下面的单元,以当前栈顶下面的单元为新的栈顶。</li>
</ol>
<p>push、pop实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据。与mov指令不同的是,Push和pop指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出。</p>
<p></p>
<p><strong>提问:</strong></p>
<ol>
<li>CPU如何知道一段内存空间被当作栈使用?</li>
<li>执行push和pop的时候,如何知道哪个单元是栈顶单元?</li>
</ol>
<p><strong>回答:</strong></p>
<p>在8086CPU中,由两个与栈相关的寄存器——SS和SP</p>
<p>栈段寄存器SS——存放栈顶的段地址,栈顶指针寄存器SP——存放栈顶的偏移地址</p>
<p><strong>任何时刻,</strong><code>**SS:SP**</code><strong>指向栈顶元素。</strong></p>
<p>注意,<strong>栈空间是从高地址向低地址增加的</strong>。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/J6PC1R95BOSTCTZPWF8-20240305190748-5izsjci.png"></p>
<p></p>
<p>如何避免栈顶超界问题?</p>
<p><strong>8086CPU并不能保证栈不会超界。</strong></p>
<p></p>
<h3 id="p19-关于段的总结">P19 关于“段”的总结</h3>
<p></p>
<p>物理地址 = <strong>段</strong>地址*16+偏移地址</p>
<ul>
<li>编程时,可以根据需要,将一组内存单元定义为一个段</li>
<li>起始地址为16的倍数,长度小于等于64K的一组地址连续的内存单元,定义为一个段</li>
<li>将一段内存定义为一个段,用一个段地址指示段,用偏移地址访问段内单元——在程序中可以完全由程序员安排。</li>
</ul>
<p></p>
<p>目前为止已经学习了三种段:</p>
<ul>
<li>数据段<br>
将段地址放在DS中<br>
用mov、add、sub等访问内存单元的指令时,CPU将我们定义的数据段中的内容当作数据段来访问</li>
<li>代码段<br>
将段地址放在CS中,将段中第一条指令的偏移地址放在IP中。CS:IP指向即将执行的指令<br>
CPU将执行我们定义的代码段中的指令</li>
<li>栈段<br>
将段地址放在SS中,将栈顶单元的偏移地址放在SP中。SS:SP指向栈顶<br>
CPU在需要进行栈操作(push、pop)时,将我们定义的栈段当作栈空间来使用。</li>
</ul>
<p></p>
<h2 id="03-汇编语言程序">03-汇编语言程序</h2>
<h3 id="p20-本章导学">P20 本章导学</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/A57OR602USYNANCMVBU-20240306105930-eoti1e4.png"></p>
<p></p>
<h3 id="p21-用汇编语言写的源程序">P21 用汇编语言写的源程序</h3>
<h4 id="汇编程序结构">汇编程序结构</h4>
<p><strong>汇编程序:</strong> 包含汇编指令和伪指令的文本。</p>
<p>汇编程序的<strong>注释</strong>是<code>;</code>,默认为10进制</p>
<p><strong>汇编程序->编译器->机器码</strong></p>
<p><strong>汇编程序可以是:</strong></p>
<ul>
<li>在Debug中直接写入指令编写的汇编程序<br>
适用于功能简单、短小精悍的程序<br>
只需要包含汇编指令即可</li>
<li>单独编写成源文件后再编译为可执行文件的程序<br>
适用于编写大程序<br>
不只有汇编指令,还有指导编译器工作的伪指令<br>
源程序由一些段构成,这些段存放代码、数据,或将某个段当作栈空间</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/Y3DIS5R363N_29GQNW9MC-20240306110126-wtb5giz.png"></p>
<p></p>
<h4 id="伪指令">伪指令</h4>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/YUNDWIYLSWNMOH174ZG-20240306110553-6dtt8qk.png"></p>
<p><strong>程序中的三种伪指令:</strong></p>
<ul>
<li>段定义<br>
一个汇编程序是由多个段组成的,这些段被用来存放代码、数据或者当作栈空间来使用<br>
一个有意义的汇编程序中至少要有一个段,这个段用来存放代码<br>
定义程序中的段:每个段都需要有段名<br>
格式:<code>段名 segment</code>——段的开始;<code>段名 ends</code>——段的结束</li>
<li>end(不是ends)<br>
汇编程序的结束标记。若程序结尾处不加end,编译器在编译程序时,不知道程序在何处结束。</li>
<li>assume(假设)<br>
含义是,假设某一段段寄存器和程序中的某个用<code>segment...ends</code>定义的段相关联。<br>
格式:<code>assume 寄存器:段名</code><br>
例如,<code>assume cs:codesg</code>指cs寄存器与codesg这个段关联,将定义的codesg当作程序的代码段来使用</li>
</ul>
<p></p>
<h4 id="如何写出一个程序来">如何写出一个程序来</h4>
<ol>
<li>定义一个段</li>
<li>实现处理任务</li>
<li>指出程序在何处结束</li>
<li>段与段寄存器关联</li>
<li>加上程序返回的代码</li>
</ol>
<p>以实现2*3的程序为例:</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/_ZY_HVERU6BMAI-20240306111627-yuwjlzq.png"></p>
<p></p>
<h4 id="程序中可能的错误">程序中可能的错误</h4>
<ul>
<li>语法错误<br>
程序在编译时被编译器发现的错误,容易被发现</li>
<li>逻辑错误<br>
在运行时发生的错误,不容易被发现</li>
</ul>
<p></p>
<h3 id="p22-由源程序到程序运行">P22 由源程序到程序运行</h3>
<h4 id="由源程序到程序运行的过程">由源程序到程序运行的过程</h4>
<p>编写 -> 源程序文件<code>.asm</code> -> 编译 -> 目标文件<code>.obj</code> -> 连接 ->可执行文件<code>.exe</code></p>
<p></p>
<h4 id="编译">编译</h4>
<p>软件/指令:<code>masm</code></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/2BQXQ7BRYC4OWFC1KOF-20240306112618-0r4crno.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306113829-i8yhst5.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/PK2Q9WH35EF2U@6IM-20240306113446-ln1jfut.png"></p>
<p></p>
<h4 id="连接">连接</h4>
<p>软件/指令:<code>link</code></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/LN4K1145F4LIV097L1-20240306113744-snf59x0.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306113815-i4u3t61.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306114008-i57qu45.png"></p>
<p></p>
<h3 id="p23-用debug跟踪程序的执行">P23 用debug跟踪程序的执行</h3>
<p>用debug装载程序:<code>debug file.exe</code></p>
<p>有256(100H)字节的程序段前缀(PSP),作为数据区</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306135819-7ewczlx.png"></p>
<p>用<strong>p命令/g命令</strong>执行:</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306140334-hw43b80.png"></p>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306140522-ql9pdg0.png"></p>
<p></p>
<h3 id="p24-和">p24 <code>[...]</code>和<code>(...)</code></h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306140738-xr0y01y.png"></p>
<ul>
<li><code>[...]</code>:表示一个内存单元。是汇编语法规定的表示形式</li>
<li><code>(...)</code>:表示一个内存单元(只能用物理地址)或寄存器中的内容。是为学习方便大家约定的内容。</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306141319-un31p2c.png"></p>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/G@C0@M74LHNVA2KJ2OIW4-20240306141839-vrk6r4l.png"></p>
<p></p>
<h3 id="p25-loop指令">p25 Loop指令</h3>
<p>功能:实现循环(计数型循环)</p>
<p><strong>指令格式:</strong><code>loop 标号</code></p>
<p><strong>CPU执行loop指令时的操作:</strong></p>
<ol>
<li>(cx) = (cx) -1</li>
<li>判断cx中的值,不为零则转至标号处执行程序;如果为零则向下执行。</li>
</ol>
<p><strong>注意:cx</strong>中要提前存放循环次数,循环多少次则cx设置为多少;并且需要定义一个<strong>标号</strong>。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240306142645-d3iztrc.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/S5E7M82VHGOE267C7MP9L-20240306143127-0urc2m3.png"></p>
<p>用loop指令编程实例:</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/R44CR_YWUTJUJT59B8F-20240306143812-9mnwa52.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/ZZA3G7X2YG551@2XP55-20240306145547-peabzn8.png"></p>
<p></p>
<h3 id="p26-loop指令使用再例">p26 Loop指令使用再例</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/YGYU1FCANH@QKS7TLBAF-20240306145749-kj6pdw4.png"></p>
<p>注意,一定要分析,累加之后,是否有可能超过dx的存储能力</p>
<p></p>
<p>如果你在编写的时候遇到了这个问题:<code>mov ax,</code>被编译成了<code>mov ax,6</code></p>
<p>别急,看下一章</p>
<p></p>
<h3 id="p27-段前缀的使用">p27 段前缀的使用</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/RG2_MSMCQ97E753-20240306151738-xiwbnkk.png"></p>
<p>为了解决这个“异常”现象,对策是:在前面显式地写上段寄存器。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/ED22WYIJ9BUOQWCRF-20240306152220-83a7jcf.png"></p>
<p>这些出现在访问内存单元的指令中,用于显式的指明内存单元的段地址的'ds、cs、ss、es',在汇编语言中被称为<strong>段前缀</strong>。</p>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/3VIPJLNOAZ68333526-20240306153903-0wf9rey.png"></p>
<p></p>
<h3 id="p28-在代码段中使用数据">p28 在代码段中使用数据</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/EG6KNMNRW7A9JFHJQV7-20240306155556-iyvfzun.png"></p>
<p><strong>对策:</strong></p>
<ul>
<li>在程序的段中存放数据,运行时由操作系统分配空间。</li>
<li>段的类别:数据段、代码段、栈段</li>
<li>各类段中均可由数据</li>
<li>可以在单个的段中安置,也可以将数据、代码、栈放入不同的段中</li>
</ul>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/XI9NBDBRQJK2KXE6UH5-20240306155646-j5qhsjp.png"></p>
<ul>
<li>dw(define word):定义一个字</li>
<li>db:定义一个字节</li>
<li>dd:定义一个双字</li>
</ul>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/6KF86EMQT5VSWIQ7-20240306155947-elcv5ku.png"></p>
<p>直接在代码段中添加数据的程序有问题,因为CPU并不知道代码段是从<code>cs:0010</code>开始的,还将IP设置为0000。</p>
<p>所以怎么让代码从<code>cs:0010</code>开始呢?</p>
<p>用<strong>标号</strong>标记程序的开始</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/QBT5L0QDCGJB@8NW6M9FM-20240306160148-scrp6aa.png"></p>
<p></p>
<h3 id="p29-在代码段中使用栈">p29 在代码段中使用栈</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/CVP2K@5FCQ1906AMU1F5L-20240307151317-v1snv5n.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/K7V77OHRIQZ_QY7LD-20240307151326-tiig6g7.png"></p>
<p></p>
<h3 id="p30-将数据代码栈放入不同段">p30 将数据、代码、栈放入不同段</h3>
<p>将数据、栈、代码放在一个段的<strong>缺点</strong>:</p>
<ul>
<li>程序显得混乱,编程和阅读时都要注意何处是数据、何处是栈、何处是代码;</li>
<li>只能应用于要处理的数据很少的,栈空间也小,代码也少的情况。</li>
</ul>
<p>解决方法:将数据、代码、栈放入不同段</p>
<p>注意<strong>代码段仍然需要标号标记</strong></p>
<p>注意:DS是程序开始的位置,但是后面紧跟着100H的PSP,所以<code>DS:100H</code>才是数据开始的地方。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/9WFPJMQ7@ULP99N8R1S_D-20240307153026-q8wl6ig.png"></p>
<p></p>
<h2 id="04-内存寻址方式">04-内存寻址方式</h2>
<h3 id="p31-本章导学">P31 本章导学</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/HA5HIO3NRYCAP0A43-20240307153840-p4jnhgj.png"></p>
<h3 id="p32-处理字符问题">P32 处理字符问题</h3>
<p>汇编程序中,用<code>'abc'</code>的方式指明数据是以字符形式给出的。编译器将它们转化成相对应的ASCII码。ascii码是一字节大小的数据。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/OEK0NOT4KS4RR4QOXM6-20240307160012-t8b3z6f.png"></p>
<p>可根据这个,做<strong>大小写转换:</strong></p>
<ul>
<li>小写转大写:and 1101 1111</li>
<li>大写转小写:or 0010 0000</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/9RX3WDXODAQA15LV-20240307160404-xve81a0.png"></p>
<p></p>
<h3 id="p33-bxidata方式寻址">P33 方式寻址</h3>
<p><code></code>表示一个内存单元,它的偏移地址为(bx)+idata,即bx中的数据加上idata</p>
<p><code>mov ax,/mov ax,</code>的含义:<code>(ax) = ((ds)*16+200+(bx))</code>。</p>
<p><code>mov ax,</code>的其他常见写法:<code>mov ax,200/mov ax,.200</code></p>
<p><strong>应用:</strong> 做数组处理。可将<code>(ds)*16+idata</code>看作基址,将bx看做偏移量。</p>
<p></p>
<h3 id="p34-si和di寄存器">P34 SI和DI寄存器</h3>
<p>SI和DI是8086CPU中和BX功能相近的寄存器。</p>
<p><strong>SI:</strong> source index,源变址寄存器。</p>
<p><strong>DI:</strong> destination index,目标变址寄存器。</p>
<p><strong>区别:</strong> SI和DI不能分成两个8位寄存器来用。</p>
<p><strong>应用问题:</strong> 用寄存器SI和DI实现将字符串'welcome to masm!'复制到它后面的数据区中。</p>
<p><strong>解决方法:</strong> 用<code>DS:SI</code>指向要复制的原始字符串,用<code>DS:DI</code>指向目的空间。用一个循环来完成复制</p>
<p></p>
<h3 id="p35-bxsi和bxdi方式寻址">P35 和方式寻址</h3>
<p>:表示一个内存单元,偏移地址为<code>(BX)+(SI)</code>。<strong>将BX称作基址,SI称作变址。</strong> 基址变址寻址。</p>
<p></p>
<h3 id="p36-bxsiidata和bxdiidata方式寻址">P36 和方式寻址</h3>
<p>:表示一个内存单元,偏移地址为<code>(BX)+(SI)+Idata</code>。</p>
<p>其他不同的写法:<code>mov ax, / mov ax, / mov ax,200 / mov ax,.200 / mov ax,.200</code></p>
<p></p>
<h3 id="p37-不同寻址方式的灵活应用">P37 不同寻址方式的灵活应用</h3>
<h4 id="对内存的寻址方式">对内存的寻址方式</h4>
<table>
<thead>
<tr>
<th>形式</th>
<th>名称</th>
<th>特点</th>
<th>意义</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td></td>
<td>直接寻址</td>
<td>用一个常量/立即数来表示地址</td>
<td>用于直接定位一个内存单元</td>
<td>mov ax,</td>
</tr>
<tr>
<td></td>
<td>寄存器间接寻址</td>
<td>用一个变量来表示内存地址</td>
<td>用于间接定位一个内存单元</td>
<td>mov bx,0<br>mov ax,</td>
</tr>
<tr>
<td></td>
<td>寄存器相对寻址</td>
<td>用一个变量和一个常量表示地址</td>
<td>可在一个起始地址的基础上,用变量间接定位内存单元</td>
<td>mov bx,4<br>mov ax</td>
</tr>
<tr>
<td></td>
<td>基址变址寻址</td>
<td>用两个变量表示地址</td>
<td></td>
<td>mov ax,</td>
</tr>
<tr>
<td></td>
<td>相对基址变址寻址</td>
<td>用两个变量和一个常量表示地址</td>
<td></td>
<td>mov ax,</td>
</tr>
</tbody>
</table>
<p></p>
<p><strong>案例1:将datasg段中每个单词的头一个字母改为大写字母</strong></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/TOJDAF7V1KA9JOZCR-20240311151145-54brvzu.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/V414M54N85I11OW8R55-20240311152911-g0ovzk7.png"></p>
<p><strong>案例2:将datasg段中每个单词都改为大写字母</strong></p>
<p><strong>如何解决需要进行双重循环,但只有一个cx的冲突:</strong></p>
<p><strong>法一:</strong> 用dx暂存cx</p>
<p><strong>缺陷:</strong> CPU中寄存器有限,这样的话dx就会被占用,太浪费</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/_PIPP7A5IRRIS33W34-20240311154422-7bq186o.png"></p>
<p><strong>法二:</strong> 用固定空间保存数据</p>
<p><strong>缺陷:</strong> 在运行过程中,该数据有被覆盖的风险</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/Y1F583C20L4ERQDJ3-20240311154544-24nbkd1.png"></p>
<p><strong>法三:</strong> 用栈保存数据</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/AGT7_HT1J6IHK2YE91EE-20240311154647-ce7a5a9.png"></p>
<p></p>
<h3 id="p38-不同寻址方式演示">P38 不同寻址方式演示</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/UP3ES0WN_UQ2FWFE-20240311155012-7oqevlh.png"></p>
<p></p>
<h3 id="p39-用于内存寻址的寄存器">P39 用于内存寻址的寄存器</h3>
<p><strong>只有</strong><code>**bx、bp、si、di**</code><strong>可以用在</strong> <code>**[...]**</code><strong>内对内存单元进行寻址</strong></p>
<p>bx以外的通用寄存器、段寄存器不可以用在[...]内</p>
<p><code>**bx、bp、si、di**</code><strong>的搭配:</strong></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/DN3GKTC@_T6USXC9J-20240311163100-k5zpzti.png"></p>
<p><strong>bx、bp的区别:</strong> bx默认ds段;bp默认ss段</p>
<p></p>
<h3 id="p40-在哪里有多长">P40 在哪里?有多长?</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/M8@0REA9RRASEJ0D1-20240311163455-x0b1bnn.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/ERR892_ZEIIL6BHJT2I-20240311164009-nffjzwk.png"></p>
<p></p>
<h3 id="p41-寻址方式的综合应用">P41 寻址方式的综合应用</h3>
<p><strong>要求:</strong> 编程修改内存中的过时数据。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/C85T4C6F387CR-20240311165040-xo5zh6s.png"></p>
<p></p>
<h3 id="p42-用div指令实现触发">P42 用div指令实现触发</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/YV4GKKZZ3@L35U5R_UL7-20240311171802-02e7pch.png"></p>
<p><strong>切记</strong>,提前在默认的寄存器中设置好被除数,且默认寄存器不作别的用处。</p>
<p></p>
<h3 id="p43-用dup设置内存空间">P43 用dup设置内存空间</h3>
<p>功能:dup和db、dw、dd等数据定义伪指令配合使用,用于进行数据重复。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/IV7RYXO57EBOLVL4W6I-20240311174045-em7p4dm.png"></p>
<p>使用格式:<code>db [重复次数] dup ('重复的内容')</code></p>
<p></p>
<h2 id="05-流程转移与子程序">05-流程转移与子程序</h2>
<h3 id="p44-本章导学">P44 本章导学</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/ZAJHTY3KUPI8OM8EH-20240312163704-0aws7r5.png"></p>
<h3 id="p45-转移综述">P45 ”转移“综述</h3>
<p><strong>需求:</strong> 在时间过程中,长需要改变程序执行的流程</p>
<p><strong>转移指令:</strong></p>
<ul>
<li>可以控制CPU执行内存中某处代码的指令</li>
<li>可以修改IP,或同时修改CS和IP的指令</li>
</ul>
<p><strong>转移指令的分类:</strong></p>
<ul>
<li>按转移行为<br>
段内转移:只修改IP,如<code>jmp ax</code><br>
段间转移:同时修改CS和IP,如<code>jmp 1000:0</code></li>
<li>根据指令对IP修改的范围不同<br>
段内短转移:IP修改范围为<code>-128~127</code>,用一个字节来表示<br>
段内近转移:IP修改范围为<code>-32768~32767</code>,用一个字来表示</li>
<li>按转移指令<br>
无条件转义指令:jmp<br>
条件转移指令:jcxz<br>
循环指令:loop<br>
过程<br>
终端</li>
</ul>
<h3 id="p46-操作符offset">P46 操作符offset</h3>
<p><strong>格式:</strong><code>offset 标号</code>。如<code>offset start</code></p>
<p><strong>用法:</strong> 通过<code>offset</code>获取标号所在指令的<strong>偏移地址</strong></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/@U9GJ2H2BG2QETBIDO-20240312164531-l3grlqc.png"></p>
<h3 id="p47-jmp指令">P47 jmp指令</h3>
<p>jmp指令的<strong>功能</strong>:无条件转移,可以只修改IP,也可以同时修改CS和IP</p>
<p>jmp指令的<strong>用法</strong>:<code>jmp short 标号</code></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313112605-f6e8fk2.png"></p>
<p><strong>注意</strong>:<code>jmp short</code>的机器指令中,包含的是跳转目标指令和IP的相对位置,而不是目标地址</p>
<p></p>
<p><strong>两种段内转移:</strong></p>
<ul>
<li><code>jmp short 标号</code>:<br>
功能:(IP)=(IP)+8位位移<br>
核心:8位位移 = "标号"的地址-jmp指令后的第一个字节的地址;8位位移由编译程序在编译时算出</li>
<li><code>jmp near ptr 标号</code>:<br>
功能:(IP)=(IP)+16位位移<br>
核心:8位位移 = "标号"的地址-jmp指令后的第一个字节的地址;8位位移由编译程序在编译时算出</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313112654-a36iavn.png"></p>
<p></p>
<p><strong>远转移:</strong><code>jmp far ptr 标号</code>直接指明了跳转到的目的地址,即包含了标号的段地址CS和偏移地址IP</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313112631-auifmme.png"></p>
<p></p>
<p><strong>转移地址在寄存器中的jmp:</strong><code>jmp 16位寄存器</code></p>
<p>功能:IP = (16位寄存器)</p>
<p>例如:<code>jmp ax</code></p>
<p></p>
<p><strong>转移地址在内存中的jmp指令</strong></p>
<p>注意:段地址在高位,偏移地址在低位;段地址和偏移地址各用一个字保存</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313113117-1h2318h.png"></p>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313113932-3y4z1x1.png"></p>
<p></p>
<h3 id="p48-其他转移指令">P48 其他转移指令</h3>
<h4 id="jcxz指令">jcxz指令</h4>
<p><strong>格式:</strong><code>jcxz 标号</code></p>
<p><strong>功能:</strong> 如果(cx)=0,则跳转到标号处执行;当(cx)≠0时,程序向下执行</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313114842-l20t5cm.png"></p>
<p></p>
<h3 id="loop指令">loop指令</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313115309-a1rm7pp.png"></p>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313115407-7g2eoqo.png"></p>
<p></p>
<h3 id="p49-call-指令和-ret-指令">P49 call 指令和 ret 指令</h3>
<p><code>call 标号</code>:调用子程序</p>
<p><code>ret</code>:从子程序返回</p>
<p><strong>实质:</strong> 都是流程转移指令,它们都修改IP,或同时修改CS和IP</p>
<p></p>
<h4 id="call指令">call指令</h4>
<p>call指令进行的两步操作:</p>
<ol>
<li>将当前的 IP 或 CS和IP 压入栈中;<br>
相当于:<code>push IP</code></li>
<li>转移到标号处执行指令<br>
相当于:<code>jmp near ptr 标号</code></li>
</ol>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313115743-spfmgiq.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313120000-c98pu0j.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313120207-ggw5093.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313120223-1zl9jka.png"></p>
<h4 id="ret指令">ret指令</h4>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313120628-giiambd.png"></p>
<p></p>
<h3 id="p50-call-和-ret-的配合使用">P50 call 和 ret 的配合使用</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313182900-spgho3m.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313182908-vxanh2x.png"></p>
<p></p>
<h3 id="p51-mul指令">P51 mul指令</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313183050-wr80a8w.png"></p>
<p></p>
<h3 id="p52-汇编语言的模块化程序设计">P52 汇编语言的模块化程序设计</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313185206-c2yun5y.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313185220-97m3a8i.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313190046-hj0czuv.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313190908-ihnx168.png"></p>
<p></p>
<h3 id="p53-寄存器冲突的问题">P53 寄存器冲突的问题</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313191419-ye95zns.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313192308-l5yfg9t.png"></p>
<p>实际上就是计组里讲的,“保护现场“</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240313192529-kj2fuve.png"></p>
<h3 id="p54-标志寄存器">P54 标志寄存器</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/9AX8APADK970OEO3SX5-20240314101205-oeswa1c.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/9JJM@YA72_HSRFK_1-20240314102113-hzz6f52.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314102330-0l57kzb.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314102352-eq42s6b.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314102523-a5tq29n.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314102827-vzjyc54.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314103610-uqqurz4.png"></p>
<p></p>
<h3 id="p55-带进错位的加减法">P55 带进(错)位的加减法</h3>
<h4 id="adc">adc</h4>
<p><strong>格式:</strong><code>adc 操作对象1,操作对象2</code></p>
<p><strong>功能:</strong> 操作对象1 = 操作对象1+操作对象2+CF</p>
<p>adc是带进位加法指令,利用了CF位上记录的进位值</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314141523-xs32ye7.png"></p>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314142337-7jfc92t.png"></p>
<p>两个讨论的回答:</p>
<ul>
<li>不可以。<code>sub ax,ax</code>是为了将CF置为零</li>
<li>不可以。<code>inc di</code>是不会产生进位的;但<code>add di,2</code>是有可能产生进位标记的</li>
</ul>
<h4 id="sbb">sbb</h4>
<p><strong>格式:</strong><code>sbb 操作对象1,操作对象2</code></p>
<p><strong>功能:</strong> 操作对象1 = 操作对象1-操作对象2-CF</p>
<p>sbb是带借位减法指令,利用了CF位上记录的借位值</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314142506-6t8qa06.png"></p>
<p></p>
<h3 id="p56-cmp和条件转移指令">P56 cmp和条件转移指令</h3>
<h4 id="cmp">cmp</h4>
<p><strong>格式:</strong><code>cmp 操作对象1,操作对象2</code></p>
<p><strong>功能:</strong>操作对象1-操作对象2</p>
<p><strong>注意:</strong> cmp是比较指令,功能相当于减法指令,只是不保存结果,只影响标志寄存器</p>
<p><strong>应用:</strong> 用标志寄存器的值来确定比较结果</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314142825-w3jto3z.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314143048-kvy20du.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314143326-ozag6qk.png"></p>
<h4 id="条件转移指令">条件转移指令</h4>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314143629-6sh2199.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314144943-vvmocc9.png"></p>
<p></p>
<h3 id="p57-条件转移指令应用">P57 条件转移指令应用</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314150714-etd1vcq.png"></p>
<p></p>
<h3 id="p58-df标志和串传送指令">P58 DF标志和串传送指令</h3>
<h4 id="串传送指令">串传送指令</h4>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314151009-dsmwtch.png"></p>
<p></p>
<h4 id="rep指令">rep指令</h4>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240314151639-z1j3fty.png"></p>
<p></p>
<p><code>**rep+movsb/movsw**</code><strong>能高效地成片传送数据</strong></p>
<p></p>
<h2 id="06-中断及外部设备操作">06-中断及外部设备操作</h2>
<h3 id="p59-阶段导学">P59 阶段导学</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/6HIN8B@E9KC6@1SYZ@852-20240318160650-hzmd62k.png"></p>
<h3 id="p60-移位指令">P60 移位指令</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/U%7B4ZDEFT89RJ7C%60V5EQQEGX-20240318160804-lptpa2f.png"></p>
<ul>
<li>逻辑左移(SHL):<br>
将最高位(最左边的)移入CF中,其他位向左移一位,末尾补零<br>
<strong>相当于,X=X*2</strong></li>
<li>循环左移(ROL):<br>
将最高位(最左边的)移入CF中,其他位向左移一位,末尾最高位</li>
<li>逻辑右移(SHR):<br>
将最低位(最右边的)移入CF中,其他位向右移一位,首位补零<br>
<strong>相当于,X=X/2</strong></li>
<li>算术右移(SAR):<br>
将最低位(最右边的)移入CF中,其他位向右移一位,最高位不变<br>
最高位不变是为了保证“符号”不变。通常情况下,最高位是符号位</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318161620-iutr79z.png"></p>
<p>当移动位数大于1时,必须使用<code>cl</code>表示移动位数。</p>
<p></p>
<h3 id="p61-操作显存数据">P61 操作显存数据</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318162154-t5022t5.png"></p>
<p><strong>屏幕上的内容 = 显存中的数据。</strong> 所以想要操作屏幕上的内容,就要操作显存中的数据。</p>
<p>更具体的说,就是需要操作<code>B8000h~BFFFFh</code>,即<strong>显示缓冲区</strong>中的数据。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318162405-75jaedv.png"></p>
<p><strong>每一个显示内容需要两个字节。</strong> 低位字节存储要显示符号的ASCII码,高位字节存储显示属性,即颜色。其中高位字节中内容的前四位,是存储的背景颜色;后四位是存储的前景颜色。</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318163323-jap8924.png"></p>
<p></p>
<h3 id="p62-描述内存单元的标号">P62 描述内存单元的标号</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318164308-yt4s0bq.png"></p>
<p>简化版offset。</p>
<p>当没有冒号的时候,被称为数据标号,数据标号能同时描述内存地址和单元长度(即db=字节,dw=字)</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318164600-r951j9z.png"></p>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318165005-ga9vxwn.png"></p>
<p>数据标号描述的内存地址,是包括段地址的。</p>
<p>扩展用法:将标号当做数据来定义(用c语言来做通俗解释就是:指针的指针)</p>
<p></p>
<h3 id="p63-数据的直接定址表">P63 数据的直接定址表</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318165519-monzi6m.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318194857-vlggdjk.png"><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318195238-4985msu.png"></p>
<h4 id="直接定址表">直接定址表</h4>
<p><strong>问题求解思路:</strong> 利用表,在两个数据集合之间建立一种映射关系,用查表的方法根据给出的数据得出其在另一集合中的对应数据。</p>
<p><strong>优点:</strong></p>
<ul>
<li>算法清晰和简洁</li>
<li>加快运算速度</li>
<li>使程序易于扩充</li>
<li>空间换时间的方案</li>
</ul>
<p></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318195415-fzwapun.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318195428-4iubohv.png"></p>
<p></p>
<h3 id="p64-代码的直接定址表no">P64 代码的直接定址表(NO)</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318200618-tk6dlf5.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318201029-6tfdey5.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318201303-6yh1cqi.png"></p>
<p></p>
<h3 id="p65-中断及其处理">P65 中断及其处理</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318201948-qpktwpi.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240318202119-umm8dpk.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319130725-t93mw4y.png"></p>
<p>低位保存IP,高位保存CS</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131009-r161tdz.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131500-fjeqxhf.png"></p>
<p></p>
<h3 id="p66-编制中断处理程序">P66 编制中断处理程序</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131749-1xyyisz.png"></p>
<p>中断向量表:<code>0000:0000~0000:03FF</code></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131802-20aq7oj.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131820-nwzcy6l.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131833-u815fms.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131842-lvf29v1.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131854-o05tmmy.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319131903-s5w3a64.png"></p>
<p></p>
<h3 id="p67-单步中断">P67 单步中断</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319134000-4znp67o.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319134150-43r7fm2.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319134658-fah2yjp.png"></p>
<p></p>
<h3 id="p68-由int指令引发的中断">P68 由int指令引发的中断</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319143239-kzpqsjb.png"> <img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319143612-4ggkmph.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319143822-u6h3l0h.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319145352-f3hpmph.png"></p>
<p></p>
<h3 id="p69-bios和dos中断处理">P69 BIOS和DOS中断处理</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319145417-9gk739x.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319145433-hpehfzq.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319145448-st9450o.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319145458-sk3xa95.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319145507-9nxii6s.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319145515-trxplhe.png"></p>
<p></p>
<h3 id="p70-端口的读写">P70 端口的读写</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319151349-akvnu7y.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319151628-gsqs3jh.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319151905-c3t2pmx.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319151936-dybhxul.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319152507-aeeaj08.png"></p>
<p></p>
<h3 id="p71-操作cmos-ram芯片">P71 操作CMOS RAM芯片</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319152621-sy6dr74.png"></p>
<ul>
<li>128 个字节的 RAM 中存储:内部实时钟、系统配置信息、相关的程序(用于开机时配置系统信息)。</li>
<li>CMOS RAM 芯片靠电池供电,关机后其内部的实时钟仍可正常工作, RAM 中的信息不丢失。</li>
<li>该芯片内部有两个端口,端口地址为70h和71h,CPU 通过这两个端口读写CMOS RAM:<br>
70h地址端口,存放要访问的CMOS RAM单元的地址;<br>
71h数据端口,存放从选定的单元中读取的数据,或要写入到其中的数据。</li>
<li>读取CMOS RAM的两个步骤:<br>
将要读取的单元地址送入70h地址端口;<br>
从数据端口71h读出指定单元的内容。</li>
</ul>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319152844-00606k1.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319153053-7x8tfzp.png"></p>
<p></p>
<h3 id="p72-外设连接与中断">P72 外设连接与中断</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319153539-rgrbsuw.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319153550-0fyl9aj.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319153844-8u9v563.png"></p>
<p></p>
<h3 id="p73-pc机键盘的处理过程">P73 PC机键盘的处理过程</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319154421-hwjwi35.png"></p>
<p>断码 = 通码+80H</p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319154431-ll1ic03.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319154441-qogmihm.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319154455-ibub456.png"></p>
<p></p>
<h3 id="p74-定制键盘输入处理">P74 定制键盘输入处理</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319160021-p77bo28.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319160446-h5ko92g.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319160903-heyhwm6.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319160921-yi6fx3e.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319161436-6a44y5s.png"></p>
<p></p>
<h3 id="p78-磁盘读写">P78 磁盘读写</h3>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319161812-is6rgkc.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319161733-87mfqvz.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319161741-5w1r1of.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319161749-fv9fktb.png"></p>
<p><img src="https://picture-for-blog.oss-cn-wulanchabu.aliyuncs.com/03/image-20240319161802-v9ol9ae.png"></p>
<p></p>
<p></p>
<p></p>
<p></p>
<p></p><br><br>
来源:https://www.cnblogs.com/Baii1/p/18257123
頁:
[1]