20192426陈瀚文汇编语言程序设计学习笔记
<h1 id="汇编语言程序设计邢建川">汇编语言程序设计——邢建川</h1><p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>汇编语言程序设计——邢建川<ul><li>第一章 基础知识<ul><li>1.1 汇编语言的一般概念<ul><li>1.机器语言</li><li>2.高级语言</li><li>3.汇编语言</li></ul></li><li>1.2 学习和使用汇编语言的目的</li><li>1.3 进位计数制及其相互转换<ul><li>1.进位计数制</li><li>2.各种数制间的相互转换</li></ul></li><li>1.4 带符号数的表示<ul><li>1.原码</li><li>2.补码</li></ul></li><li>1.5 字符的表示<ul><li>1.ASCII码</li></ul></li><li>1.6 基本逻辑运算</li></ul></li><li>第二章 IBM-PC微机的功能结构<ul><li>2.1 IBM-PC微机基本结构<ul><li>1.微机的一般构成</li><li>2.Intel 8086/8088 CPU的功能结构</li></ul></li><li>2.2 Intel 8086/8088CPU寄存器结构及其用途</li><li>2.3 存储器的组织结构</li><li>2.4 堆栈及其操作方法</li></ul></li><li>第三章 寻址方式与指令系统<ul><li>3.1 寻址方式</li><li>3.2 指令系统</li><li>3.3 指令编码</li></ul></li><li>第四章 汇编语言程序格式<ul><li>4.1 汇编语言语句种类及其格式</li><li>4.2 汇编语言数据</li><li>4.3 符号定义语句</li><li>4.4 表达式与运算符</li><li>4.5 程序的段结构</li><li>4.6 过程定义伪指令</li><li>4.7 当前位置计数器$与定位伪指令ORG(Origin)</li><li>4.8 编题伪指令TITLE</li><li>4.9 从程序返回操作系统的方法</li></ul></li></ul></li></ul></div><p></p>
<h2 id="第一章-基础知识">第一章 基础知识</h2>
<h3 id="11-汇编语言的一般概念">1.1 汇编语言的一般概念</h3>
<h4 id="1机器语言">1.机器语言</h4>
<ul>
<li><strong>机器语言</strong>就是把控制计算机的<strong>命令</strong>和<strong>各种数据</strong>直接用<strong>二进制数码</strong>表示的一种程序设计语言</li>
<li>优点:运行速度最快,程序长度最短</li>
<li>缺点:用二进制数表示的内容既不便于记忆又难于阅读</li>
</ul>
<h4 id="2高级语言">2.高级语言</h4>
<ul>
<li>使用类似于自然语言的一些语句来编制程序</li>
<li>特点:程序设计简单,但程序<strong>效率较机器语言低</strong></li>
</ul>
<h4 id="3汇编语言">3.汇编语言</h4>
<ul>
<li>在一些如系统管理、实时控制等应用场合,高级语言难以满足要求,但机器语言又过于复杂。为了便于记忆和阅读,便出现了汇编语言</li>
<li>定义:使用<strong>字母和符号来表示机器语言的命令</strong>,用<strong>十进制数</strong>或<strong>十六进制数</strong>来表示数据的计算机程序设计语言</li>
<li>与机器语言的关系:一条汇编语言的语句与一条机器语言指令对应,汇编语言程序与机器语言程序<strong>效率相同</strong></li>
<li>不同类型计算机有不同的机器指令系统和汇编语言描述,对于一台计算机来说,机器语言的执行主要取决于计算机的CPU</li>
</ul>
<hr>
<h3 id="12-学习和使用汇编语言的目的">1.2 学习和使用汇编语言的目的</h3>
<ol>
<li>
<p>从根本上认识、理解计算机的工作过程</p>
</li>
<li>
<p>在计算机系统中,某些功能必须用汇编语言程序来实现</p>
</li>
<li>
<p>汇编语言程序的效率高于高级语言程序</p>
</li>
</ol>
<hr>
<h3 id="13-进位计数制及其相互转换">1.3 进位计数制及其相互转换</h3>
<h4 id="1进位计数制">1.进位计数制</h4>
<ul>
<li>使用一定个数的数码的组合来表示数字,这种表示方法成为进位计数制</li>
<li>位权:一个数的各个位置上所表示的基本数值,简称<strong>权</strong></li>
<li>基数:每个数位上<strong>能使用</strong>不同数码的个数称为基数,例如十进制基数为10,二进制基数为2</li>
<li>在计算机中数据表示一般采用二进制数,对人来说二进制不便于书写和阅读,因此书写时常使用8进制和16进制</li>
<li>在书写不同进位计数制数时,为了区别,在数的尾部用一个字母来表示对应的进制
<ul>
<li>B(Binary)——二进制数</li>
<li>O(Octal)或Q——八进制数</li>
<li>D(Decimal)——十进制数</li>
<li>H(Hexadecimal)——十六进制数</li>
<li>若未使用字母,则默认为十进制数</li>
</ul>
</li>
</ul>
<h4 id="2各种数制间的相互转换">2.各种数制间的相互转换</h4>
<ol>
<li>
<p>十进制整数转换为二进制数</p>
<ul>
<li>
<p>减权定位法</p>
<ul>
<li>
<p>从二进制数高位起,依次用待转换的十进制数与各位权值进行比较;</p>
</li>
<li>
<p>如果够减,则该数位系数K<sub>i</sub>=1,同时减去该位权值,余数作为下一次比较的值</p>
</li>
<li>
<p>例如:将325转换为二进制数</p>
<ul>
<li>
<p>因为2<sup>9</sup>>(512)>325>2<sup>8</sup>(256),因此从K<sub>8</sub>位开始比较</p>
</li>
<li>
<table>
<thead>
<tr>
<th style="text-align: center">减权比较</th>
<th style="text-align: center">K<sub>i</sub></th>
<th style="text-align: center">对应二进制数</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">325-256=69</td>
<td style="text-align: center">K<sub>8</sub></td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">69<128</td>
<td style="text-align: center">K<sub>7</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">69-64=5</td>
<td style="text-align: center">K<sub>6</sub></td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">5<32</td>
<td style="text-align: center">K<sub>5</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">5<16</td>
<td style="text-align: center">K<sub>4</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">5<8</td>
<td style="text-align: center">K<sub>3</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">5-4=1</td>
<td style="text-align: center">K<sub>2</sub></td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">1<2</td>
<td style="text-align: center">K<sub>1</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">1-1=0</td>
<td style="text-align: center">K<sub>0</sub></td>
<td style="text-align: center">1</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>所以325D=101000101B</p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>除基取余法</p>
<ul>
<li>将十进制数除以基数2,其余数为二进制数的最低位,再用其商除2,其余数为次低位,反复直到商为0</li>
<li>还是以将325转换为二进制数为例:
<ul>
<li>
<table>
<thead>
<tr>
<th style="text-align: center">除基数2</th>
<th style="text-align: center">余数</th>
<th style="text-align: center">K<sub>i</sub></th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">325÷2=162</td>
<td style="text-align: center">1</td>
<td style="text-align: center">K<sub>0</sub></td>
</tr>
<tr>
<td style="text-align: center">162÷2=81</td>
<td style="text-align: center">0</td>
<td style="text-align: center">K<sub>1</sub></td>
</tr>
<tr>
<td style="text-align: center">81÷2=40</td>
<td style="text-align: center">1</td>
<td style="text-align: center">K<sub>2</sub></td>
</tr>
<tr>
<td style="text-align: center">40÷2=20</td>
<td style="text-align: center">0</td>
<td style="text-align: center">K<sub>3</sub></td>
</tr>
<tr>
<td style="text-align: center">20÷2=10</td>
<td style="text-align: center">0</td>
<td style="text-align: center">K<sub>4</sub></td>
</tr>
<tr>
<td style="text-align: center">10÷2=5</td>
<td style="text-align: center">0</td>
<td style="text-align: center">K<sub>5</sub></td>
</tr>
<tr>
<td style="text-align: center">5÷2=2</td>
<td style="text-align: center">1</td>
<td style="text-align: center">K<sub>6</sub></td>
</tr>
<tr>
<td style="text-align: center">2÷2=1</td>
<td style="text-align: center">0</td>
<td style="text-align: center">K<sub>7</sub></td>
</tr>
<tr>
<td style="text-align: center">1÷2=0</td>
<td style="text-align: center">1</td>
<td style="text-align: center">K<sub>8</sub></td>
</tr>
</tbody>
</table>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>十进制小数转换为二进制数</p>
<ul>
<li>
<p>减权定位法</p>
<ul>
<li>
<p>例:将十进制数0.645转换为二进制数</p>
</li>
<li>
<table>
<thead>
<tr>
<th style="text-align: center">减权比较</th>
<th style="text-align: center">K<sub>i</sub></th>
<th style="text-align: center">对应二进制数</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">0.645-0.5=0.145</td>
<td style="text-align: center">K<sub>-1</sub></td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">0.145<0.25</td>
<td style="text-align: center">K<sub>-2</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">0.145-0.125=0.02</td>
<td style="text-align: center">K<sub>-3</sub></td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">0.02<0.0625</td>
<td style="text-align: center">K<sub>-4</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">0.02<0.03125</td>
<td style="text-align: center">K<sub>-5</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">0.02-0.015625</td>
<td style="text-align: center">K<sub>-6</sub></td>
<td style="text-align: center">1</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>所以0.645D=0.101001B(注意,转换时应根据<strong>程序要求的精度</strong>或<strong>计算机的字长</strong>确定二进制的位数)</p>
</li>
</ul>
</li>
<li>
<p>乘积取整法</p>
<ul>
<li>
<p>例:将0.8125D转换为二进制数</p>
</li>
<li>
<table>
<thead>
<tr>
<th style="text-align: center">乘以基数</th>
<th style="text-align: center">K<sub>i</sub></th>
<th style="text-align: center">整数部分</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">0.8125×2=1.625</td>
<td style="text-align: center">K<sub>-1</sub></td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">0.625×2=1.25</td>
<td style="text-align: center">K<sub>-2</sub></td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">0.25×2=0.5</td>
<td style="text-align: center">K<sub>-3</sub></td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">0.5×2=1</td>
<td style="text-align: center">K<sub>-4</sub></td>
<td style="text-align: center">1</td>
</tr>
</tbody>
</table>
</li>
<li>
<p>所以0.8125D=0.1101B</p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>二进制整数转换为十进制数</p>
<ul>
<li>
<p>按权相加法</p>
<ul>
<li>例 101000101B = 1×2<sup>8</sup>+1×2<sup>6</sup>+1×2<sup>2</sup>+1×2<sup>0</sup> = 256+64+4+1 = 325</li>
</ul>
</li>
<li>
<p>逐次乘基相加法</p>
<ul>
<li>从二进制数的最高位开始,将最高位的1乘以2并与次高位的数相加,之后将结果乘以2再与低一位相加,重复此过程直至最低位加入结果,如下图所示,101000101B = 325</li>
<li><img src="https://img2022.cnblogs.com/blog/1813633/202203/1813633-20220310194947510-522045221.png" alt="逐次乘基相加法" loading="lazy"></li>
</ul>
</li>
</ul>
</li>
<li>
<p>二进制小数转换为十进制数</p>
<ul>
<li>按权相加法
<ul>
<li>例 0.101001B = 1×2<sup>-1</sup>+1×2<sup>-3</sup>+1×2<sup>-6</sup> = 0.5+0.125+0.0156 = 0.640625D</li>
</ul>
</li>
<li>逐次除基相加法
<ul>
<li>从二进制数的最低位开始,将最低位的1除以2并与次低位的数相加,之后将结果除以2再与高一位相加,重复此过程直至最高位加入结果,如下图所示,0.101001B = 0.640625D</li>
<li><img src="https://img2022.cnblogs.com/blog/1813633/202203/1813633-20220310195733147-169319843.png" alt="逐次除基相加法" loading="lazy"></li>
</ul>
</li>
</ul>
</li>
<li>
<p>二进制与八进制和十六进制间的转换</p>
<p><strong>三位二进制数</strong>对应<strong>一位八进制数</strong>,<strong>四位二进制数</strong>对应<strong>一位十六进制数</strong></p>
</li>
</ol>
<hr>
<h3 id="14-带符号数的表示">1.4 带符号数的表示</h3>
<h4 id="1原码">1.原码</h4>
<ul>
<li>
<p>二进制数的最高位表示符号,0表示正,1表示负。数值部分用二进制数绝对值表示</p>
</li>
<li>
<p>8位二进制数原码最大数为01111111(+127),最小数为11111111(-127)</p>
</li>
<li>
<p>8位二进制数表示范围:-127≤ X≤ +127</p>
</li>
<li>
<p>0的<strong>原码有两种表示形式</strong>:00000000(+0)和10000000(-0)</p>
</li>
</ul>
<h4 id="2补码">2.补码</h4>
<ul>
<li>带符号数X的补码表示<sub>补</sub>定义:<sub>补</sub> = M+X(Mod M),其中M为机器数的位数,例如位数为8则M=2<sup>8</sup>,符号位仍然是0为正,1为负</li>
<li>对于正数,补码与原码相同,对于负数,将<strong>真值</strong>的各位取反再加一即可得到补码;或者将<strong>原码</strong>的符号位保持不变,其余各位取反再加一即可得到补码</li>
<li>8位二进制数补码最大数为01111111 = [+127]<sub>补</sub>,最小数为10000000 = [-128]<sub>补</sub></li>
<li>0的补码<strong>只有一个</strong>,即00000000</li>
<li><sub>补</sub> = <sub>补</sub>+<sub>补</sub></li>
<li><strong><sub>补</sub> = <sub>补</sub>-<sub>补</sub> = <sub>补</sub>+[-Y]<sub>补</sub></strong></li>
</ul>
<hr>
<h3 id="15-字符的表示">1.5 字符的表示</h3>
<h4 id="1ascii码">1.ASCII码</h4>
<ul>
<li>标准ASCII码为一字节,其中用低七位表示字符编码,用最高位作为奇偶校验位</li>
<li>标准ASCII码共有128个
<ul>
<li>非打印ASCII码(33个),用于控制操作</li>
<li>可打印ASCII码(95个)</li>
</ul>
</li>
</ul>
<hr>
<h3 id="16-基本逻辑运算">1.6 基本逻辑运算</h3>
<p>计算机内部采用二进制数表示信息,而“0”和“1”正好对应逻辑“真”和“假”两个状态。</p>
<ol>
<li>
<p>“与”运算(AND)</p>
<p>也叫做逻辑乘,常用<span class="math inline">\(\land\)</span>或<span class="math inline">\(\cdot\)</span>表示</p>
</li>
<li>
<p>“或”运算(OR)</p>
<p>也叫逻辑加,用<span class="math inline">\(\lor\)</span>或+表示</p>
</li>
<li>
<p>“非”运算</p>
<p>指对逻辑变量取相反的一个逻辑值,若<span class="math inline">\(A\)</span>为1,则<span class="math inline">\(\bar{A}\)</span> = 0</p>
</li>
<li>
<p>“异或”运算</p>
<p>通常用<span class="math inline">\(\bigoplus\)</span>表示,即 <span class="math inline">\(F = A \bigoplus B\)</span>,A与B相同时,F为1,否则F为0</p>
</li>
</ol>
<hr>
<h2 id="第二章-ibm-pc微机的功能结构">第二章 IBM-PC微机的功能结构</h2>
<h3 id="21-ibm-pc微机基本结构">2.1 IBM-PC微机基本结构</h3>
<h4 id="1微机的一般构成">1.微机的一般构成</h4>
<ol>
<li>
<p>微机的一般构成:运算器、控制器、存储器、输入设备和输出设备</p>
</li>
<li>
<p>其中将<strong>运算器</strong>和<strong>控制器</strong>两大部件集成在一个集成电路芯片上,称为中央处理器,简称CPU</p>
</li>
<li>
<p>系统采用总线结构,系统总线分为地址总线、数据总线、控制总线三类</p>
</li>
<li>
<p>主存储器:用来存放程序和数据的部件,由若干存储单元组成,对每个存储单元按照地址进行访问</p>
<p><strong>指示存储单元编号的地址长度决定了存储器的最大容量</strong>,例如一个10位二进制数表示的地址可以用来区分2<sup>10</sup>=1K个单元</p>
<p>除主存外,计算机一般还配置辅助存储器,也成为外存</p>
</li>
<li>
<p>习惯上将CPU与主存储器合称为主机</p>
</li>
<li>
<p>输入输出设备及接口</p>
<p>I/O设备的工作速度、工作原理以及所处理的信息格式等与主机相差很大,因此I/O设备要通过<strong>I/O接口</strong>与系统总线连接</p>
</li>
</ol>
<h4 id="2intel-80868088-cpu的功能结构">2.Intel 8086/8088 CPU的功能结构</h4>
<ol>
<li>
<p>汇编语言程序由一系列的指令(指令序列)构成。</p>
<p>CPU执行指令序列就是重复“取指令——执行指令”两个步骤</p>
<ul>
<li>
<p>串行方式</p>
<p>特点:系统总线空闲时间较多</p>
</li>
<li>
<p>指令流水线方式</p>
<p>执行单元(EU)与总线接口单元(BIU)</p>
<p>特点:减少了系统总线的空闲时间</p>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="22-intel-80868088cpu寄存器结构及其用途">2.2 Intel 8086/8088CPU寄存器结构及其用途</h3>
<ol>
<li>
<p>通用寄存器8个+控制寄存器2个+段寄存器4个</p>
</li>
<li>
<p>如下图所示,其中H代表高位,L代表低位</p>
<p><img src="https://img2022.cnblogs.com/blog/1813633/202203/1813633-20220310212650841-1303272839.png" alt="寄存器" loading="lazy"></p>
</li>
<li>
<p>通用寄存器</p>
<ul>
<li>
<p>数据寄存器</p>
<p>包括AX、BX、CX、DX四个寄存器,它们既可以是<strong>16位寄存器</strong>,也可以当作<strong>8个独立的8位寄存器</strong>使用。存放操作数或运算的结果</p>
<p>例如:MOV AX,BX; 将BX的内容送到AX中</p>
<p>ADD CH,DH; 将DH和CH的内容相加,结果放入CH</p>
<p>在某些指令中,不需要明确指出使用的寄存器名,隐含使用对应寄存器。例如在循环指令LOOP中,CX被隐含指定作循环次数计数用;移位指令SHL AX,CL;CL被固定用作移位次数</p>
</li>
<li>
<p>指针寄存器</p>
<p>有<strong>堆栈指针</strong>SP和<strong>基址指针</strong>BP,一般被用来存放16位地址,在形成20位的物理地址时常被用做偏移量使用</p>
<p>SP指针——在进行堆栈操作时被隐含使用,用来指向堆栈顶部单元</p>
<p>BP指针——被用来指向堆栈段内某一存储单元</p>
</li>
<li>
<p>变址寄存器</p>
<p>有两个16位的变址寄存器SI(源变址寄存器)和DI(目的变址寄存器),一般被用来作地址指针,也可以作为通用数据寄存器存放操作数和运算结果</p>
</li>
</ul>
</li>
<li>
<p>段寄存器</p>
<ul>
<li>代码段,数据段,堆栈段,附加段</li>
<li>地址增长方向:从上往下</li>
<li>CPU在访问存储器时必须指明:
<ul>
<li>所访问的存储单元属于哪个段(指明使用的段寄存器)</li>
<li>该存储单元与段基址的偏移量</li>
</ul>
</li>
</ul>
</li>
<li>
<p>指令指针IP</p>
<ul>
<li>CPU从存储器取指令时,以<strong>段寄存器CS</strong>作为代码段的基址指针,以<strong>IP的内容作为偏移量</strong>,共同形成指令的存放地址</li>
<li>当CPU取出指令后,IP内容<strong>自动修改</strong>为指向下一条指令</li>
<li>IP的内容<strong>既不能用指令去读取,也不能用指令赋值</strong>。只能通过某些指令的执行而<strong>自动修改</strong>。</li>
</ul>
</li>
<li>
<p>标志寄存器</p>
<ul>
<li>
<p>用来反映CPU在程序运行时的某些状态(是否进位、奇偶性、结果的符号、结果是否为零等)</p>
</li>
<li>
<p>8086/8088CPU标志寄存器长度为16位,但只定义了其中的9位</p>
</li>
<li>
<p><img src="https://img2022.cnblogs.com/blog/1813633/202203/1813633-20220311103446884-1831592749.png" alt="标志寄存器" loading="lazy"></p>
</li>
<li>
<p>进位标志位CF</p>
<p>在算术运算时,若最高位(字操作的第15位,字节操作的第7位)产生进位或借位时CF被自动置“1”;在移位指令中,CF用来存放左移最高位或右移最低位移出的数值</p>
</li>
<li>
<p>奇偶标志位PF</p>
<p>当指令操作结果的<strong>低8位</strong>中含有1的个数为偶数时,PF置1,否则置0</p>
</li>
<li>
<p>辅助进位标志位AF</p>
<p>在算数运算时,若<strong>低字节的低四位</strong>向<strong>高四位</strong>产生进位或借位,AF置1,否则置0</p>
</li>
<li>
<p>零值标志位ZF</p>
<p>结果各位全为0,则ZF置1,否则置0</p>
</li>
<li>
<p>符号标志位SF</p>
<p>运算结果为负数时置1,为正数时置0</p>
</li>
<li>
<p>溢出标志位OF</p>
<p>当运算结果超过机器用补码所能表示数的范围时,OF置1,否则置0</p>
<p>对于<strong>字节数据</strong>,机器用补码所能表示数的范围为-128 ~ +127</p>
<p>对于<strong>字数据</strong>,机器用补码所能表示数的范围为-32768 ~ +32767</p>
</li>
<li>
<p>单步标志位TF</p>
<p>也叫跟踪位,供<strong>调试程序</strong>使用</p>
</li>
<li>
<p>中断允许标志位IF</p>
<p>IF为1,CPU可以响应<strong>可屏蔽中断</strong>,否则不允许响应可屏蔽中断</p>
</li>
<li>
<p>方向标志位DF</p>
<p>用来规定串操作指令的增减方向。DF=0时,串操作指令自动使变址寄存器(SI和DI)的值递增;DF=1使,串操作指令自动使变址寄存器的值递减</p>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="23-存储器的组织结构">2.3 存储器的组织结构</h3>
<ol>
<li>
<p>存储器的组成</p>
<ul>
<li>存储器由若干存储单元构成,存储单元的多少表示了存储器的容量</li>
<li>每个存储单元存放相同长度的二进制数</li>
<li>每个存储单元有一个<strong>唯一的</strong>地址</li>
<li>8086/8088CPU有20根地址线,即可以产生1M字节地址空间,为了方便书写,在源程序中<strong>常用5位十六进制数</strong>来表示一个存储单元的地址(00000H~FFFFFH)</li>
<li>任何两个相邻的字节单元构成一个字单元,即1字=2字节</li>
<li>在定义一个地址时必须指出是字节或字类型属性</li>
</ul>
</li>
<li>
<p>存储器的段结构</p>
<ul>
<li>
<p>8086/8088可寻址的存储空间为1MB。而CPU内部寄存器长度只有16位,能够直接访问的最大地址空间是64KB</p>
</li>
<li>
<p>8086/8088CPU将1MB的存储空间划分为若干个段,每个段<strong>最大长度</strong>为64KB</p>
</li>
<li>
<p>每个段的基址必须是一个<strong>小节</strong>的首地址</p>
<blockquote>
<p><strong>小节</strong>:在存储器中规定从0地址开始,每16个字节单元称为一个小节。因此,1MB内存可划分为64K个小节</p>
</blockquote>
</li>
<li>
<p><strong>逻辑段</strong>在物理存储器中可以是邻接的、间隔的、部分重叠的和完全重叠的。也就是说,内存中的一个物理存储单元可以映像到一个或多个逻辑段中</p>
<blockquote>
<p><strong>逻辑段</strong>指在汇编语言源程序中设置的段</p>
</blockquote>
</li>
<li>
<p>在任意时刻,一个程序只能访问4个当前段中的内容,四个段寄存器CS、DS、SS和ES分别保存了它们段基址的高16位地址,称为<strong>段基值</strong>。段基址的最低4位为0(对应十六进制的最低位为0)</p>
<blockquote>
<p><strong>端基值</strong>就是<strong>段基址</strong>放在寄存器中高16位的地址</p>
</blockquote>
</li>
</ul>
</li>
<li>
<p>逻辑地址与物理地址及对应关系</p>
<ul>
<li>
<p>物理地址</p>
<p>在1MB的存储空间中,每个存储单元的物理地址是唯一的(20位地址,从00000H~FFFFFH)</p>
</li>
<li>
<p>逻辑地址</p>
<p>在程序设计中,为了便于程序的开发和对存储器进行动态管理,使用逻辑地址。包含段基值和偏移量两个部分</p>
</li>
<li>
<p>逻辑地址转换为物理地址</p>
<p>将逻辑地址的端基值左移4位,形成20位的段基址(低4位为0)然后与16为的偏移量相加,结果即为20位的物理地址</p>
</li>
<li>
<p>逻辑地址的来源</p>
<table>
<tbody><tr align="center">
<td rowspan="3">序号</td>
<td rowspan="3">操作类型</td>
<td colspan="3">逻辑地址</td>
</tr>
<tr align="center">
<td colspan="2">段基值</td>
<td rowspan="2">偏移量(OFFSET)</td>
</tr>
<tr align="center">
<td>隐含来源</td>
<td>允许替代来源</td>
</tr>
<tr align="center">
<td>1</td>
<td>取指令</td>
<td>CS</td>
<td>无</td>
<td>IP</td>
</tr>
<tr align="center">
<td>2</td>
<td>堆栈操作</td>
<td>SS</td>
<td>无</td>
<td>SP</td>
</tr>
<tr align="center">
<td>3</td>
<td>取源串</td>
<td>DS</td>
<td>CS,SS,ES</td>
<td>SI</td>
</tr>
<tr align="center">
<td>4</td>
<td>存目的串</td>
<td>ES</td>
<td>无</td>
<td>DI</td>
</tr>
<tr align="center">
<td>5</td>
<td>以BP为基址</td>
<td>SS</td>
<td>CS,DS,ES</td>
<td>有效地址EA</td>
</tr>
<tr align="center">
<td>6</td>
<td>存取一般变量</td>
<td>DS</td>
<td>CS,SS,ES</td>
<td>有效地址EA</td>
</tr>
</tbody></table>
<blockquote>
<p>如果允许替代来源,就叫做<strong>段超越</strong>,它表示段基值除使用隐含的段寄存器外是否可以指定其他段寄存器来提供</p>
</blockquote>
<blockquote>
<p><strong>有效地址EA</strong>表示根据指令所采用的寻址方式计算出来的段内偏移量</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="24-堆栈及其操作方法">2.4 堆栈及其操作方法</h3>
<p>堆栈是一个特定的存储区,访问该存储区一般需要按照专门的规则进行操作</p>
<p><strong>堆栈的用途</strong>:主要用于暂存数据以及在过程调用或处理中断时保存断点信息</p>
<ol>
<li>
<p>堆栈的构造</p>
<p>堆栈一般分为:专用堆栈存储器和软件堆栈</p>
<p>堆栈的一端是固定的,称为<strong>栈底</strong>,是存储器的<strong>最大地址单元</strong></p>
<p>另一端是浮动的,称为<strong>栈顶</strong>,随着堆栈中存放信息的多少而改变</p>
<p>为了确定栈顶的位置,通常使用堆栈指针SP指示栈顶</p>
<p>堆栈存取数据的规则:“先进后出FILO”</p>
</li>
<li>
<p>8086/8088堆栈的组织</p>
<p>当SP被初始化时,指向栈底+2单元,<strong>其值就是堆栈的长度</strong></p>
<blockquote>
<p>指向栈底+2单元的原因是当栈为空的时候,栈中没有元素,也就不存在栈顶元素,所以SP只能指向栈的最底部单元下面的单元,该单元的偏移地址为栈最底部的字单元的偏移地址+2</p>
</blockquote>
<p>数据在堆栈中存放格式:以<strong>字</strong>(两字节)为单位存放,数据的<strong>低8位</strong>放在<strong>较低</strong>地址单元,<strong>高8位</strong>放在<strong>较高</strong>地址单元</p>
</li>
<li>
<p>堆栈操作</p>
<ul>
<li>
<p>设置堆栈</p>
<p>例如:STACK1 SEGMENT PARA STACK DB100 DUP (0)</p>
<p> STACK1 ENDS</p>
</li>
<li>
<p>进栈PUSH</p>
<p>例如:PUSH AX;将寄存器AX的内容压入堆栈</p>
<p> PUSH DS;将段寄存器DS的内容压入堆栈</p>
<p> PUSHF;将标志寄存器内容压入堆栈</p>
<p>进栈过程:首先将堆栈指针SP-2,即指向一个空的堆栈字单元。之后将要储存的内容送入SP指向的字单元中</p>
</li>
<li>
<p>出栈POP</p>
<p>出栈操作由POP指令<strong>或</strong>机器自动实现,它从堆栈顶部弹出<strong>一个字</strong>到通用寄存器、段寄存器或<strong>字</strong>存储单元</p>
<p>例如:POP AX;将栈顶字单元内容弹出到AX</p>
<p> POP DS;将栈顶字单元内容弹出到段寄存器DS</p>
<p> POPF;将栈顶字单元内容送回标志寄存器F</p>
<p>出栈过程:首先将SP指向的字单元内容送往指定的寄存器或存储器。之后将堆栈指针SP内容+2</p>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="第三章-寻址方式与指令系统">第三章 寻址方式与指令系统</h2>
<h3 id="31-寻址方式">3.1 寻址方式</h3>
<ol>
<li>
<p>一条指令由<strong>操作码</strong>和<strong>操作数</strong>两部分组成</p>
<blockquote>
<p><strong>操作码</strong>:表示该指令应完成的具体操作,如加法、减法、乘法、移位等,在汇编语言中使用一定的符号表示,称为助记符。如ADD、PUSH、POP、MOV等</p>
<p><strong>操作数</strong>:操作码的操作对象</p>
</blockquote>
</li>
<li>
<p>寻址方式:寻找指令中所需操作数的各种方法,也就是提供指令中操作数的存放信息的方式</p>
</li>
<li>
<p>Intel 8086/8088各指令中提供操作数的方法有一下四种:</p>
<ul>
<li>立即数操作数——操作数在指令代码中提供。立即数可以是8位,也可以是16位</li>
<li>寄存器操作数——操作数在CPU的通用寄存器<strong>或</strong>段寄存器中</li>
<li>存储器操作数——操作数在内存的存储单元中</li>
<li>I/O端口操作数—— 操作数在输入/输出<strong>接口</strong>的寄存器中</li>
</ul>
</li>
<li>
<p>存储器存储单元的逻辑地址由端基值和偏移量组成。</p>
<p><strong>偏移量</strong>标识了该存储单元与段起始地址之间的距离,也叫做有效地址EA。</p>
<p>有效地址EA是<strong>位移量、基址和变址</strong>这三个地址分量的几种组合,由CPU的执行单元EU计算出来的。</p>
<blockquote>
<p><strong>位移量</strong>:指令中直接给出的一个8位或16位数,一般源程序中以操作数名字(变量名或标号)的形式出现</p>
<p><strong>基址</strong>:由基址寄存器BX或基址指针BP提供的内容</p>
<p><strong>变址</strong>:由源变址寄存器SI或目的变址寄存器DI提供的内容</p>
</blockquote>
</li>
<li>
<p>直接寻址</p>
<p>指令由操作码+操作数组成,操作数的有效地址EA只有<strong>位移量</strong>地址分量</p>
<p>在汇编语言源程序中,直接寻址方式用<strong>符号</strong>或<strong>常数</strong>来表示</p>
<ul>
<li>
<p>用符号表示</p>
<p>MOV BX,VAR;</p>
<p>表示将数据段中,偏移了VAR个字节距离的字单元内容送到寄存器BX中</p>
</li>
<li>
<p>用常数表示</p>
<p>MOV AX,DS:<strong>[</strong>64H<strong>]</strong>(不加方括号则表示将100送到AX中)</p>
<p>表示从当前数据段开始,偏移100个字节的字单元内容送到AX中。<strong>不能写为MOV AX,64H,一定要带方括号</strong></p>
</li>
</ul>
</li>
<li>
<p>寄存器间接寻址</p>
<ul>
<li>指令由操作码+操作数组成</li>
<li>操作数有效地址EA直接从基址寄存器(BX或BP)或变址寄存器(SI或DI)中获得</li>
<li>寄存器间接寻址就是事先将偏移量存放在某个寄存器(BX、BP、SI或DI)中,这些寄存器就如同一个<strong>地址指针</strong></li>
<li>在程序运行期间,只要对寄存器内容进行修改,就可以实现用同一条指令实现对不同存储单元进行操作</li>
<li>当指令中使用的是<strong>BP寄存器</strong>,则隐含表示使用<strong>SS段寄存器</strong>,其余情况则隐含使用<strong>DS段寄存器</strong></li>
</ul>
</li>
<li>
<p>基址寻址/变址寻址</p>
<ul>
<li>操作数的有效地址EA等于基址分量或变址分量加上指令中给出的位移量</li>
<li>指令由操作码+寻址特征+位移量组成,指令中使用BX或BP时为<strong>基址寻址</strong>,指令中使用SI或DI时为<strong>变址寻址</strong>。</li>
<li>段寄存器的隐含使用规则与间接寻址方式相同</li>
<li>这两种寻址方式只需通过改变寄存器的内容就可用一条指令访问不同的存储单元,并且由于增加了一个位移量分量,因此它们能够很<strong>方便地访问数组和表格数据</strong></li>
<li>由于这两种方式中寄存器的内容是相对于由位移量指定的初始单元,因此也叫寄存器相对寻址。</li>
</ul>
</li>
<li>
<p>基址变址寻址</p>
<ul>
<li>操作数的有效地址是三个地址分量之和,即:EA = 基址+变址+位移量</li>
<li>指令由操作码+寻址特征+位移量</li>
<li>在此方式中,程序运行期间有两个地址分量可以修改。因此此方式是最灵活的一种寻址方式,可以<strong>方便地对二维数组进行访问</strong></li>
</ul>
</li>
<li>
<p>串操作寻址方式</p>
<ul>
<li>在寻找<strong>源操作数</strong>时,隐含使用<strong>SI</strong>作为地址指针。</li>
<li>在寻找<strong>目的串</strong>时,隐含使用<strong>DI</strong>作为地址指针。</li>
<li>在串操作完成之后,<strong>自动</strong>对SI和DI进行修改,使它们指向下一个操作数。</li>
</ul>
</li>
<li>
<p>I/O端口寻址</p>
<p>在计算机系统,对I/O端口的寻址方式有存储器编址方法和I/O端口编址方法两种方法</p>
<ul>
<li>
<p>存储器编址方法</p>
<p>将I/O端口视为存储器的一个单元,对端口的访问就如同访问存储单元一样。访问存储器的指令和各种寻址方式同样适用对I/O端口的访问</p>
<p>特点:程序设计<strong>灵活</strong>,但需要<strong>占用存储地址空间</strong>。</p>
</li>
<li>
<p>I/O端口编址方法(8086/8088系统采用方式)</p>
<p>I/O端口的地址与存储器地址<strong>分开</strong>,并使用<strong>专门的</strong>输入指令和输出指令</p>
<p>可以最多访问<strong>64K个字节端口或32K个字端口</strong>,用专门的IN指令和OUT指令访 问。寻址方式有直接端口寻址和寄存器间接端口寻址两种</p>
<ul>
<li>
<p>直接端口寻址</p>
<p>在指令中直接给出端口地址,端口地址一般采用2位十六进制数,也可以用<strong>符号</strong>表示。直接端口寻址可访问的端口数为0~255个</p>
<p>例如:IN AL,25H;</p>
</li>
<li>
<p>寄存器间接端口寻址</p>
<p>把I/O端口的地址先送到<strong>DX</strong>中,用DX作间接寻址寄存器</p>
<p>例如:MOV DX,378H</p>
<p> OUT DX,AL</p>
<p>如果访问的端口地址值<strong>大于255</strong>,则必须用I/O端口的<strong>间接寻址方式</strong></p>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="32-指令系统">3.2 指令系统</h3>
<ol>
<li>
<p>一种计算机所能执行的各种类型的<strong>指令的集合</strong>称为该计算机的指令系统</p>
</li>
<li>
<p>从指令的格式划分,一般可以分为三种:</p>
<ul>
<li>
<p>双操作数指令:OPR DEST SRC</p>
</li>
<li>
<p>单操作数指令:OPR DEST</p>
</li>
<li>
<p>无操作数指令:OPR</p>
<p>对于无操作数指令,包含两种情况:</p>
<ul>
<li>指令不需要操作数,如暂停指令HLT</li>
<li>在指令格式中,<strong>没有显式地指明操作数</strong>,但是它<strong>隐含指明了操作数的存放地方</strong>,如指令PUSHF</li>
</ul>
</li>
</ul>
</li>
<li>
<p>Intel8086/8088CPU指令系统的指令可以分为六大类:</p>
<ul>
<li>
<p>传送类指令</p>
<p>传送类指令的作用是将<strong>数据信息</strong>或<strong>地址信息</strong>传送到一个<strong>寄存器或存储单元</strong>中,可以分为以下四种情况:</p>
<ul>
<li>
<p>通用数据传送指令。指令格式:MOV DEST,SRC。将源操作数指定的内容传送到目的操作数</p>
<p>MOV指令对标志寄存器的各位无影响,可以是字节数据传送也可以是字数据传送,但是<strong>源操作数和目的操作数的长度必须一致</strong>。</p>
<p>MOV指令可以分为以下几种情况:</p>
<ul>
<li>立即数传送到通用寄存器或存储单元</li>
<li>寄存器之间的传送</li>
<li>寄存器与存储单元之间传送</li>
</ul>
<blockquote>
<p>MOV指令在使用时需注意以下几个问题:</p>
<ol>
<li>立即数<strong>只能作源操作数</strong>,且它<strong>不能传送给段寄存器</strong></li>
<li><strong>段寄存器CS只能作源操作数,段寄存器之间不能直接传送</strong></li>
<li><strong>存储单元之间</strong>不能直接传送数据</li>
<li>MOV指令不影响标志位</li>
</ol>
</blockquote>
</li>
<li>
<p>交换指令。指令格式:XCHG DEST,SRC。将源操作数和目的操作数两者内容相互交换</p>
<p>数据交换可以在<strong>寄存器之间</strong>或<strong>寄存器与存储器单元之间</strong>进行。但是不能在存储单元之间直接进行数据交换,应当使用一个寄存器通过两次寄存器与存储单元之间的交换完成。 <strong>寄存器只能使用通用寄存器</strong></p>
</li>
<li>
<p>标志传送指令</p>
<p>对标志寄存器进行存取的指令有4条,它们<strong>都是无操作数指令</strong>,即<strong>指令隐含指定标志寄存器、AH寄存器或堆栈为操作数</strong></p>
<ul>
<li>
<p>取标志寄存器指令。指令格式:LAHF。</p>
<p>功能:将标志寄存器的<strong>低8位</strong>送入AH寄存器,即将标志 SF、ZF、AF、PF和CF分别送入AH的第7、6、4、2、0 位,而AH的第5、3、1位不确定。指令执行对标志寄存器各位<strong>无影响</strong></p>
</li>
<li>
<p>存储标志寄存器指令。指令格式:SAHF。</p>
<p>功能:将寄存器AH中的<strong>第7、6、4、2、0位</strong>分别送入标志寄 存器的SF、ZF、AF、PF和CF各标志位。而标志寄存器<strong>高8 位中的各标志位不受影响</strong></p>
</li>
<li>
<p>标志进栈指令。指令格式:PUSHF。</p>
<p>功能:先将堆栈指针SP减2,使其指向堆栈顶部的<strong>空字</strong>单元,然后将16位标志寄存器的内容送SP指向的<strong>字</strong>单元</p>
</li>
<li>
<p>标志出栈指令。指令格式:POPF。</p>
<p>功能:将由SP指向的堆栈顶部的一个字单元的内容送 入标志寄存器,<strong>然后</strong>SP的内容加2</p>
</li>
</ul>
</li>
<li>
<p>地址传送指令</p>
<p>这类指令有<strong>两类3条</strong>,它们的作用是<strong>将存储单元的地址送寄存器</strong></p>
<ul>
<li>
<p>装入有效地址。指令格式:LEA DEST,SRC。</p>
<p>源操作数SRC必须是<strong>一个字节或字存储器操作数 (地址)</strong>,DEST必须是一个<strong>16位通用寄存器</strong>。</p>
<p>功能:将SRC存储单元地址中的偏移量,即有效地址EA传送到一个16位通用寄存器中。指令执行对标志寄存器各位无影响</p>
</li>
<li>
<p>装入地址指针指令。指令格式:LDS DEST,SRC; LES DEST,SRC;</p>
<p>DEST是任意一个<strong>16位通用寄存器</strong>。SRC必须是一个<strong>存储器操作数</strong>。</p>
<p>功能:把SRC存储单元开始的4个字节单元的内容(<strong>32位地址指针</strong>)送入DEST通用寄存器和段寄存器 DS(LDS指令)或ES(LES指令),其中<strong>低字</strong>单 元内容为<strong>偏移量</strong>送通用寄存器,<strong>高字</strong>单元内容为<strong>段基值</strong>送DS或ES</p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>算术运算类指令</p>
<p>8086/8088指令系统中有<strong>加、减、乘、除</strong>指令,这些指令可以对<strong>字节数据或字数据</strong>进行运算。参加运算的数可以是<strong>无符号数</strong>,也可以是<strong>带符号数</strong>,可以是二进制数,也可以是是十进制数(以BCD码表示)。<strong>带符号数用补码表示</strong></p>
<ul>
<li>
<p>加法指令。指令格式:ADD DEST,SRC。</p>
<p>DEST只能是<strong>通用寄存器或存储器操作数</strong>。不能是立即数。SRC可以是通用寄存器、存储器或立即数操作数。<strong>DEST和SRC不能都为存储器操作数</strong></p>
<p>功能:将目的操作数和源操作数相加,其和存放到目的操作数中,而<strong>源操作数内容保持不变</strong>。根据相加的结果将影响到标志寄存器的CF、PF、AF、ZF、SF和OF。可以是字节操作数相加,也可以是字操作数相加</p>
</li>
<li>
<p>带进位加法指令。指令格式:ADC DEST,SRC</p>
<p>功能与ADD基本相同,所不同的是其结果还要加上<strong>进位标志CF</strong>的值</p>
<p>用ADC指令可实现数据长度大于16位的两数相加</p>
<p>例:计算12349678H+377425H</p>
<p> MOV AX, 1234H</p>
<p> MOV BX, 9678H</p>
<p> ADD BX, 7425H(此时进位标志为1)</p>
<p> ADC AX, 37H(将低16位相加产生的进位加到了高16位中)</p>
</li>
<li>
<p>加1指令。指令格式:INC DEST</p>
<p>目的操作数可以是任意的8位、16位通用寄存器或存储器操作数。目的操作数被视为<strong>带符号</strong>二进制数</p>
<p>功能:将目的操作数加 1,并送回到目的操作数。根据指令执行结果设置PF、AF、ZF、SF和OF标志,但不影响CF。主要用于某些计数器的计数和修改地址指针</p>
</li>
<li>
<p>减法指令。指令格式:SUB DEST,SRC</p>
<p>目的操作数DEST和源操作数SRC可以是8位或16位的通用寄存器、存储器操作数,但两者<strong>不能同时为存储器操作数</strong>。立即数只能作源操作数</p>
<p>功能:<strong>目的操作数</strong>的内容<strong>减去源操作数</strong>的内容,结果送入目的操作数,源操作数中内容保持不变。操作结果将影响标志位CF、PF、AF、ZF、SF和OF</p>
</li>
<li>
<p>带借位减法。指令格式:SBB DEST,SRC</p>
<p>功能与SUB指令基本相同,不同的是在两个操作数相减后再减去进位标志CF的值。主要用于长度大于 16位的数相减,即将低16位相减的结果引入高位部 分的减法中。</p>
<p>根据指令执行结果设置PF、AF、ZF、SF、OF和CF</p>
</li>
<li>
<p>减1指令。指令格式:DEC DEST</p>
<p>DEST可以是8位或16位的通用寄存器存储器操作数, 该指令将DEST看作是<strong>带符号</strong>二进制数</p>
<p>功能:单操作数指令。将目的操作数的内容减1后,送回到目的操作数。根据指令执行结果设置PF、AF、ZF、SF和OF, 但<strong>不影响CF</strong></p>
<p>DEC指令的使用类似INC指令。主要用于<strong>计数和修改地址指针</strong>,计数方向与INC指令相反</p>
</li>
<li>
<p>求负数指令。指令格式:NEG DEST</p>
<p>DEST可以是任意一个8位或16位的通用寄存器或存储器操作数,<strong>被视为带符号的操作数</strong></p>
<p>功能:用零减去目的操作数的内容,并送回目的操作数。NEG指令将影响标志PF、AF、ZF、SF、CF和OF</p>
<ul>
<li>对进位标志CF的影响:只有当<strong>操作数为零</strong>时,进位标志CF被<strong>置零</strong>,其它情况都被置1</li>
<li>对溢出标志OF的影响:<strong>当字节操作数为-128,或字操作数为-32768时</strong>,执行NEG指令的结果操作数将无变化,但溢出标志OF被置1</li>
</ul>
<p>由于机器中<strong>带符号数用补码表示</strong>,求操作数的负数就是求补操作。因此NEG指令也叫<strong>取补指令</strong></p>
</li>
</ul>
</li>
<li>
<p>位操作类指令</p>
<ul>
<li>
<p>逻辑运算指令</p>
<p>共有四条,这4条指令都是执行按位逻辑运算。指令格式分别是:</p>
<ul>
<li>逻辑“与”指令 AND DEST, SRC</li>
<li>逻辑“或”指令 OR DEST, SRC</li>
<li>逻辑“异或”指令 XOR DEST, SRC</li>
<li>逻辑“非”指令 NOT DEST</li>
</ul>
<p>DEST和SRC可以是8位或16位的通用寄存器或存储器操作数,但两者<strong>不能同时为存储器操作数</strong>,SRC可以为立即数</p>
<p>功能:除用来实现各种逻辑运算之外,<strong>还常用于对字节或字数据的某些位的组合、分离或位设置</strong>。</p>
<p>NOT指令对标志无影响。而其余三条指令将根据结果影响SF、ZF和PF,而CF和OF总是置0,AF为不确定</p>
</li>
<li>
<p>测试指令。指令格式:TEST DEST, SRC</p>
<p>功能与AND指令相似,实现源操作数与目的操作数进行按位“<strong>逻辑与</strong>”运算,<strong>对标志位的影响与AND指令相同,但运算的结果不送入目的操作数</strong>,即目的操作数内容也将保持不变。主要用于<strong>测试某一操作数的一位或几位的状态</strong></p>
</li>
<li>
<p>移位/循环移位指令</p>
<p>这一类指令共有8条,分为3类:</p>
<ul>
<li>算术移位
<ul>
<li>算术左移 SAL DEST,COUNT</li>
<li>算术右移 SAR DEST,COUNT</li>
</ul>
</li>
<li>逻辑移位
<ul>
<li>逻辑左移 SHL DEST,COUNT</li>
<li>逻辑右移 SHR DEST,COUNT</li>
</ul>
</li>
<li>循环移位
<ul>
<li>小循环
<ul>
<li>循环左移 ROL DEST,COUNT</li>
<li>循环右移 ROR DEST,COUNT</li>
</ul>
</li>
<li>大循环
<ul>
<li>带进位循环左移 RCL DEST,COUNT</li>
<li>带进位循环右移 RCR DEST,COUNT</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>这8条指令具有以下几个共同点:</p>
<ul>
<li>DEST为操作对象,它可以是字节或字操作数, 可以是通用寄存器或存储器操作数</li>
<li>COUNT用来决定移位/循环的位数,即确定移位的次数</li>
<li>在执行移位时,根据指令不同,每移位一次,最高位(左移)或最低位(右移)都要送到进位标志CF</li>
<li>前4条移位指令根据移位结束后修改标志位CF、PF、ZF、SF和OF,而<strong>AF不确定</strong>。而<strong>后4条循环移位</strong>指令根据移位结束后的结果<strong>仅修改CF和OF</strong></li>
<li>对溢出标志位OF的影响:<strong>移位次数为1时</strong>,移位前后操作数的<strong>符号位发生变化</strong>,则OF被置1,否则置0。移位次数<strong>大于1时</strong>, OF<strong>不确定</strong>。</li>
<li>对于<strong>多字节或多字数据</strong>的移位,需要使用<strong>带进位循环移位指令</strong></li>
</ul>
</li>
</ul>
</li>
<li>
<p>串操作类指令</p>
</li>
<li>
<p>程序转移类指令</p>
</li>
<li>
<p>处理器控制类指令</p>
<ul>
<li>标志位操作指令
<ul>
<li>清除进位标志CLC:置CF为0</li>
<li>置1进位标志STC:置CF为1</li>
<li>进位标志取反CMC:CF的值取反</li>
<li>清除方向标志CLD:置DF为0</li>
<li>置1方向标志STD:置DF为1</li>
<li>清除中断标志CLI:置IF为0</li>
<li>置1中断标志STI:置IF为1</li>
</ul>
</li>
<li>与外部事件同步的指令
<ul>
<li>HLT:暂停指令</li>
<li>WAIT:等待指令</li>
<li>ESC:外部协处理器指令前缀</li>
<li>LOCK:总线锁定指令</li>
</ul>
</li>
<li>空操作指令NOP:执行一次NOP占用CPU三个时钟周期,它不改变任何寄存器或存储单元内容,主要用于延时</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="33-指令编码">3.3 指令编码</h3>
<ol>
<li>
<p>汇编:将汇编语言程序转换为机器语言程序的过程</p>
</li>
<li>
<p>汇编程序:在计算机中实现汇编过程的系统程序</p>
</li>
<li>
<p>Intel8086/8088汇编指令的编码格式有四种基本格式:</p>
<ul>
<li>
<p>双操作数指令编码格式</p>
<p>操作数可以是以下两种情形:</p>
<ul>
<li>
<p>一个操作数在寄存器中,另一操作数在寄存器或存储器中</p>
</li>
<li>
<p>目的操作数在寄存器或存储器中,源操作数是立即数</p>
</li>
</ul>
<p>这类指令的机器目标代码长度为2~6个字节。整个指令编码可以包含4个部分,但其中某些部 分在一些指令的编码中可以没有:</p>
<ul>
<li>操作特征部分:OPCODE操作码字段+方向字段d+字/字节字段W</li>
<li>寻址特征部分:MOD+REG+R/M</li>
<li>位移量部分:根据寻址特征中MOD和R/M字段确定的有效地址计算方法,位移量可以是:
<ul>
<li>没有位移量</li>
<li>1字节位移量disp8</li>
<li>2字节位移量disp16</li>
</ul>
</li>
<li>立即数部分:总是位于指令编码的最后1~2字节</li>
</ul>
</li>
<li>
<p>单操作数指令编码格式</p>
<p>指令编码为2~3字节,包括操作特征、寻址特征和位移量三部分</p>
<p>操作特征部分:OPCODE+V(移位/循环指令限定)+W</p>
<blockquote>
<p>V=0时,指令中使用常数1作为移位或循环次数</p>
<p>V=1时,指令中使用寄存器CL作移位次数</p>
</blockquote>
</li>
<li>
<p>与AX或AL有关的指令编码格式</p>
<p>用于<strong>隐含</strong>指定AX/AL作为一个操作数的<strong>双操作数指令</strong>。采用这种编码格式的指令,除一个操作数隐含指定为AX/AL外,另一个操作数可以是<strong>立即数或存储单元</strong></p>
</li>
<li>
<p>其它指令编码格式</p>
<p>如<strong>标志位操作指令</strong>、<strong>堆栈操作指令</strong>等。这些指令的编码格式<strong>一般只有一个字节</strong></p>
</li>
</ul>
</li>
</ol>
<hr>
<h2 id="第四章-汇编语言程序格式">第四章 汇编语言程序格式</h2>
<h3 id="41-汇编语言语句种类及其格式">4.1 汇编语言语句种类及其格式</h3>
<p>汇编语言的语句可以分为指令语句和伪指令语句</p>
<ol>
<li>
<p>指令语句:每一条指令语句在汇编时都要产生一个可供CPU执 行的机器目标代码,它又叫<strong>可执行语句</strong>。一条指令语句最多可以包含4个字段:</p>
<ul>
<li>
<p>标号字段</p>
<p>标号是<strong>可选</strong>字段,它后面必须有“:”。标号是一条指令的<strong>符号地址</strong>,代表了该指令的第一个字节存放地址。一般放在一个程序段或子程序的入口处,控制程序的执行转到该程序位置。在<strong>转移指令</strong>或<strong>子程序调用指令</strong>中,可直接引用这个标号</p>
</li>
<li>
<p>指令助记符字段</p>
<p>该字段是一条指令的<strong>必选项</strong>,它表示这条语句要求CPU完成什么具体操作,如MOV、ADD、SHL等。有些指令还可以在指令助记符的前面加上<strong>前缀</strong>,实现一定的附加操作</p>
</li>
<li>
<p>操作数字段</p>
</li>
<li>
<p>注释字段</p>
<p>注释字段为可选项,该字段以分号“;“开始,它的作用是为阅读程序的人加上一些说明性内容,它不会产生机器目标代码,它不会影响程序和指令的功能</p>
</li>
</ul>
</li>
<li>
<p>伪指令语句:又叫命令语句。伪指令本身并不产生对应的机器目标代码。它仅仅是告诉汇编程序对其后面的指令语句和伪指令语句的<strong>操作数</strong>应该如何处理。一条伪指令语句可以包含四个字段:</p>
<ul>
<li>
<p>符号名字段</p>
<p>该字段为<strong>可选项</strong>。根据伪指令的不同,符号名可以是常量名、变量名、<strong>过程名、结构名和记录名</strong>等等</p>
<p>一条伪指令语句的符号名可以作其它伪指令语句或指令语句的操作数,这时它表示一个常 量或存储器地址</p>
<blockquote>
<p>符号名后面<strong>没有冒号</strong>“:”,这是与指令语句的重要区别</p>
</blockquote>
</li>
<li>
<p>伪指令符字段</p>
<p>是伪指令语句的必选项,它规定了汇编 程序所要完成的具体操作</p>
</li>
<li>
<p>操作数字段</p>
<p>该字段是否需要,以及需要几个是由伪指令符字段来决定</p>
</li>
<li>
<p>注释字段为可选项,该字段必须以分号开始。其作用与指令语句的注释字段相同</p>
</li>
</ul>
</li>
<li>
<p>标识符</p>
<p>指令语句中的<strong>标号</strong>和<strong>伪指令语句中符号名</strong>统称为标识符。标识符是由若干个字符构成的</p>
</li>
<li>
<p>标识符构成规则:</p>
<ul>
<li>
<p>字符的个数为1~31个</p>
</li>
<li>
<p>第一个字符必须是字母、问号、@或下划线“_”这4种字符之一</p>
</li>
<li>
<p>从第二个字符开始,可以是字母、数字、@ 、“_”或问号“?”</p>
</li>
<li>
<p>不能使用属于系统专用的保留字</p>
<blockquote>
<p>保留字:CPU中各<strong>寄存器名</strong>(如AX、CS等),<strong>指令助记符</strong>(如MOV、ADD),<strong>伪指令符</strong>(如SEGMENT、DB)、表达式中的<strong>运算符</strong>(如GE、EQ)以及<strong>属性操作符</strong>(如PTR、OFFSET等)</p>
</blockquote>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="42-汇编语言数据">4.2 汇编语言数据</h3>
<ol>
<li>
<p>数据是指令和伪指令语句中操作数的基本组成部分。一个数据由<strong>数值</strong>和<strong>属性</strong>两部分构成</p>
</li>
<li>
<p>在汇编语言中常用的数据形式有:<strong>常数</strong>、<strong>变量</strong>和<strong>标号</strong></p>
<ul>
<li>
<p>常数:在汇编期间其值已完全确定,并且在程序运行过程中,其值不会发生变化</p>
<p>常数有以下几种形式:</p>
<ul>
<li>二进制数</li>
<li>八进制数</li>
<li>十进制数</li>
<li>十六进制数</li>
<li>实数。一般格式为:±整数部分 • 小数部分E ± 指数部分</li>
<li>字符串常数</li>
</ul>
<p>常数在程序中可以用在以下几种情况:</p>
<ul>
<li>作指令语句的源操作数</li>
<li>在指令语句的<strong>直接寻址方式</strong>、<strong>变址(基址)寻址方式</strong>或<strong>基址变址寻址方式</strong>中作位移量</li>
<li>在数据定义伪指令中使用</li>
</ul>
</li>
<li>
<p>变量:变量<strong>用来表示存放数据的存储单元</strong>,这些数据在程序运行期间可以被改变</p>
<p>程序中以变量名的形式来访问变量,因此,<strong>可以认为变量名就是存放数据的存储单元地址</strong></p>
<ul>
<li>
<p>变量的定义与预置</p>
<p>定义变量就是给变量在内存中分配一定的存储单元。定义变量使用数据定义伪指令DB、DW、DD、DQ和DT等</p>
<p>当变量被定义后,就具有了以下三个属性:</p>
<ul>
<li>
<p>段属性:表示变量存放在哪一个逻辑段中</p>
</li>
<li>
<p>偏移量属性:表示变量所在位置与段起始点之间的字节数</p>
<blockquote>
<p>段属性和偏移量属性就构造了变量的逻辑地址</p>
</blockquote>
</li>
<li>
<p>类型属性:表示变量占用存储单元的字节数</p>
</li>
</ul>
<p>在变量的定义语句中,给变量<strong>赋初值</strong>的表达式可以使用下面4种形式:</p>
<ul>
<li>
<p>数值表达式。例如:DATA1 DB 32,30H(DATA1的内容为32(20H),DATA1+1单元内容为30H)</p>
</li>
<li>
<p>?表达式</p>
<p>不带引号的问号“?”表示可以预置任意内容</p>
</li>
<li>
<p>字符串表达式</p>
<p>对于DB伪指令,字符串为用<strong>引号</strong>括起来的<strong>不超过255个字符</strong>。给每一个字符分配一个字节单元。字符串按<strong>从左到右</strong>,将字符的ASCII编码值以<strong>地址递增</strong>的排列顺序依次存放</p>
</li>
<li>
<p>DUP表达式</p>
<p>DUP称为重复数据操作符。使用DUP表达式的一般格式为:</p>
<p>变量名 + {DB|DW|DD} + 表达式1 DUP (表达式2)</p>
<p>其中,表达式1是重复的次数,表达式2是重复的内容</p>
<p>DUP还可以嵌套使用,即表达式2又可以是一个带DUP的表达式</p>
</li>
</ul>
</li>
<li>
<p>变量的使用</p>
<ul>
<li>
<p>在指令语句中引用</p>
<p>在<strong>指令语句</strong>中直接引用变量名就是对其存储单元的内容进行存取。当变量出现在变址(基址)寻址或基址变址寻址的操作数中时表示取用该变量的<strong>偏移量</strong></p>
</li>
<li>
<p>在伪指令语句中引用</p>
<p>表示取变量 地址的<strong>偏移量</strong></p>
</li>
</ul>
</li>
</ul>
</li>
<li>
<p>标号</p>
<p>标号写在一条指令的前面,它就是该指令在内存的存放地址的符号表示,也就是<strong>指令地址的别名</strong></p>
<p>标号主要用在程序中需要改变程序的执行顺序时,用来标记转移的目的地,即作转移指令的操作数</p>
<p>每个标号具有三属性:</p>
<ul>
<li>
<p>段属性(SEG):即段基值</p>
</li>
<li>
<p>偏移量属性(OFFSET):即地址的偏移量</p>
</li>
<li>
<p>距离属性(也叫类型属性):表示该标号可以被段内还是段间的指令调用</p>
<p>NEAR(近):该标号只能作段内转移</p>
<p>FAR(远):该标号可以被非本段的转移和调用指令使用</p>
<p>标号的距离属性可以有<strong>两种</strong>方法来指定:</p>
<ul>
<li>
<p>隐含方式:当标号加在指令语句前面时,它隐含为NEAR属性</p>
</li>
<li>
<p>用LABEL<strong>伪</strong>指令给标号指定距离属性。该语句应与指令语句<strong>连用</strong></p>
<blockquote>
<p>LABEL伪指令还可以用来定义<strong>变量的属性</strong>,即改变一个变量的属性,如把字变量的高低字节作为字节变量来处理</p>
</blockquote>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="43-符号定义语句">4.3 符号定义语句</h3>
<p>在源程序设计中,使用符号定义语句可以将<strong>常数</strong>或<strong>表达式</strong>等内容用某个指定的符号来表示。在8086/8088汇编语言中有<strong>两种</strong>符号定义语句:</p>
<ol>
<li>
<p>等值语句。语句格式:符号名 EQU 表达式</p>
<p>功能:用<strong>符号名</strong>来表示EQU右边的表达式。后面的程序中一旦出现该符号名,汇编程序将把它替换成该表达式。表达式可以是任何形式,常见以下几种:</p>
<ul>
<li>常数或数值表达式</li>
<li>地址表达式</li>
<li>变量、寄存器名或指令助记符</li>
</ul>
</li>
<li>
<p>等号语句。语句格式:符号名=表达式</p>
<p>功能:与等值语句具有相同的作用。但等号语句可以对一个符号<strong>进行多次定义</strong></p>
<blockquote>
<p>注意:等值语句与等号语句都不会为符号分配存储单元。 因此所定义的符号<strong>没有段、偏移量和类型等属性</strong></p>
</blockquote>
</li>
</ol>
<hr>
<h3 id="44-表达式与运算符">4.4 表达式与运算符</h3>
<p>表达式是指令或伪指令语句操作数的常见形式。它由<strong>常数、变量、标号</strong>等通过操作运算符连接而成</p>
<blockquote>
<p>注意:任何表达式的值在程序<strong>被汇编的过程中</strong>进行计算确定,而不是到程序运行时才计算</p>
</blockquote>
<p>8086/8088宏汇编语言中的操作运算符可以分为五类:</p>
<ol>
<li>
<p>算术运算符</p>
<p>+、—、*、 / 、MOD、SHL、SHR、[ ]</p>
<ul>
<li>运算符“+”和“-”也可作单目运算符,表示数的正负</li>
<li>使用“+”、“-”、“*”、和“/”运算符时,参加运算 的数和运算结果都是<strong>整数</strong></li>
<li>“/”运算为取商的整数部分,而“MOD”运算取除法运算的余数</li>
<li>“SHR ”和“SHL”为<strong>逻辑移位</strong>运算符</li>
<li>下标运算符“[ ]”具有相加的作用。一般使用格式: 表达式1 [表达式2]。作用:将表达式1与表达式2的值相加后形成一个存储器操作数的<strong>地址</strong></li>
</ul>
</li>
<li>
<p>逻辑运算符</p>
<p>逻辑运算符有NOT、AND、OR和XOR等四个,它们执行的都是<strong>按位逻辑运算</strong></p>
</li>
<li>
<p>关系运算符</p>
<p>关系运算符包括:EQ(等于)、NE(不等于)、LT(小于)、 LE(小于等于)、GT(大于)、GE(大于等于)</p>
<p>功能:关系运算符用来比较<strong>两个表达式</strong>的大小。关系运算符比较的两个表达式必须<strong>同为常数或同一逻辑段中的变量</strong>。如果是<strong>常量</strong>的比较,则按<strong>无符号数</strong>进行比较;如果是<strong>变量</strong>的比较,则比较它们的<strong>偏移量的大小</strong>。其结果只能是”真“或”假“</p>
</li>
<li>
<p>数值返回运算符</p>
<p>该类运算符有5个,<strong>它们将变量或标号的某些特征值或存储单元地址的一部分提取出来</strong></p>
<ul>
<li>
<p>SEG运算符:取变量或标号所在段的段基值</p>
</li>
<li>
<p>OFFSET运算符:取变量或标号在段内的偏移量</p>
</li>
<li>
<p>TYPE运算符:取变量或标号的类型属性,并用<strong>数字形式</strong>表示。对<strong>变量</strong>来说就是取它的<strong>字节长度</strong>。</p>
</li>
<li>
<p>LENGTH运算符:取变量的长度。</p>
<p>如果变量是用重复数据操作符DUP说明的,则LENGTH运算取外层DUP给定的值。</p>
<p>如果没有用DUP说明,则LENGTH运算返回值总是1</p>
</li>
<li>
<p>SIZE运算符:<strong>只能作用于变量</strong>,SIZE取值等于LENGTH和TYPE两个运算符返回值的<strong>乘积</strong></p>
</li>
</ul>
</li>
<li>
<p>属性修改运算符</p>
<p>这一类运算符用来对<strong>变量、标号或存储器操作数</strong>的类型属性进行<strong>修改或指定</strong></p>
<ul>
<li>
<p>PTR运算符。使用格式:类型 PTR 地址表达式</p>
<p>功能:将地址表达式所指定的<strong>标号、变量</strong>或用其它形式表示的<strong>存储器地址</strong>的类型属性修改为“类型”所指的值。这种修改是<strong>临时的</strong>,只在含有该运算符的语句内有效</p>
</li>
<li>
<p>HIGH/LOW运算符。使用格式:{HIGH|LOW} 表达式</p>
<p>功能:如果表达式为一个<strong>常量</strong>,则将其分离成<strong>高8位和低8位</strong>;如果表达式是一个<strong>地址(段基值或偏移量)</strong>时,则分离出它的<strong>高字节和低字节</strong></p>
</li>
<li>
<p>THIS运算符</p>
<p>功能:一般<strong>与等值运算符EQU连用</strong>,用来定义一个变量或标号的<strong>类型属性</strong>。所定义的变量或标号的段基值和偏移量与紧跟其后的变量或标号相同</p>
</li>
</ul>
</li>
<li>
<p>运算符的优先级</p>
<p>在一个表达式中如果存在多个运算符时,在计算时就有先 后顺序问题。不同的运算符具有不同的运算优先级别</p>
<table>
<thead>
<tr>
<th style="text-align: center">优先级别</th>
<th style="text-align: center">运算符</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">(最高)1</td>
<td style="text-align: center">LENGTH,SIZE ,圆括号</td>
</tr>
<tr>
<td style="text-align: center">2</td>
<td style="text-align: center">PTR,OFFSET,SEG,TYPE,THIS</td>
</tr>
<tr>
<td style="text-align: center">3</td>
<td style="text-align: center">HIGH,LOW</td>
</tr>
<tr>
<td style="text-align: center">4</td>
<td style="text-align: center">*,/,MOD,SHR,SHL</td>
</tr>
<tr>
<td style="text-align: center">5</td>
<td style="text-align: center">+,-</td>
</tr>
<tr>
<td style="text-align: center">6</td>
<td style="text-align: center">EQ,NE,LT,LE,GT,GE</td>
</tr>
<tr>
<td style="text-align: center">7</td>
<td style="text-align: center">NOT</td>
</tr>
<tr>
<td style="text-align: center">8</td>
<td style="text-align: center">AND</td>
</tr>
<tr>
<td style="text-align: center">(最低)9</td>
<td style="text-align: center">OR,XOR</td>
</tr>
</tbody>
</table>
<p>汇编程序在计算表达式时,按以下规则进行运算:</p>
<ul>
<li>先执行优先级别高的运算,再算较低级别运算</li>
<li>相同优先级别的操作,按照在表达式中的顺序,从左到右进行</li>
<li>可以用圆括号改变运算的顺序</li>
</ul>
</li>
</ol>
<hr>
<h3 id="45-程序的段结构">4.5 程序的段结构</h3>
<p>8086/8088在管理内存时,按照逻辑段进行划分, 不同的逻辑段可以用来存放不同目的的数据。在程序 中使用四个段寄存器CS,DS,ES和SS来访问它们。在源程序设计时,使用<strong>伪指令来定义和使用</strong>这些逻辑段。</p>
<ol>
<li>
<p>段定义伪指令</p>
<p>伪指令SEGMENT和ENDS用于定义一个逻辑段。使用时必须<strong>配对</strong>,分别表示定义的开始与结束</p>
<p>一般格式:</p>
<p>段名 SEGMENT [定位类型] [组合类型] [‘类别名']</p>
<p>…...</p>
<p>(本段语句序列)</p>
<p>…...</p>
<p>段名 ENDS</p>
<p>段定义伪指令语句各部分的作用如下:</p>
<ul>
<li>
<p>段名:一个段的开始与结尾用的段名必须一致</p>
</li>
<li>
<p>定位类型:定位类型用于决定段的起始边界,即第一个可存放数据 的位置(不是段基址)。它可以有4种取值:</p>
<ul>
<li>PAGE: 表示该段从一个页面的边界开始</li>
<li>PARA:表示该段从一个小节的边界开始</li>
<li>WORD:表示该段从一个偶数字节地址开始,即段起始 单元地址的最后一位二进制数一定是0</li>
<li>BYTE:表示该段起始单元地址可以是任一地址值</li>
</ul>
</li>
<li>
<p>组合类型:组合类型说明符用来指定段与段之间的连接关系和定位。 它有六种取值选择:</p>
<ul>
<li>若未指定组合类型,表示本段与其它段<strong>无连接关系</strong>。在装入内存时,本段有自己的物理段,因此有自己的段基址</li>
<li>PUBLIC:在满足定位类型的前提下,将与该段<strong>同名</strong>的段邻接在一起,形成一个新的逻辑段,共用一个段基址。段内的所有偏移量调整为相对于新逻辑段的段基址</li>
<li>COMMON: 产生一个覆盖段。</li>
<li>STACK:把所有<strong>同名段</strong>连接成一个连续段,且系统自动对SS段寄存器<strong>初始化</strong>为该连续段的<strong>段基址</strong>。并<strong>初始化</strong>堆栈指针SP。</li>
<li>AT表达式:表示本段可定位在表达式所指示的<strong>小节</strong>边界上。表达式的值也就是段基值</li>
<li>MEMORY:表示本段在存储器中应定位在所有其它段之后的<strong>最高</strong>地址上。如果有多个用MEMORY说明的段,则只处理第一个用MEMORY说明的段。其余的被视为COMMON</li>
</ul>
</li>
<li>
<p>类别名:类别名为某一个段或几个相同<strong>类型</strong>段设定的<strong>类型名称</strong>。系统在进行连接处理时,把类别名相同的段存放在相邻的存储区,但段的划分与使用仍按原来的设定</p>
<p>类别名必须用<strong>单引号</strong>引起来。所用字符串可任意选定, 但它不能使用程序中的标号、变量名或其它定义的符号</p>
<p>在定义一个段时,<strong>段名是必须有的项</strong>,而定位类型、组合类型和类别名三个参数是可选项。各个参数之间用空格分隔。各参数之间的顺序不能改变。在进行程序设计时,如果程序不大,一般只需要定义<strong>三个段</strong>就可以了</p>
</li>
</ul>
</li>
<li>
<p>段寻址伪指令</p>
<p>段寻址伪指令ASSUME的作用是告诉汇编程序,在处理源程序时,定义的段与哪个寄存器关联。ASSUME并不设置各个段寄存器的具体内容,段寄存器的值是在<strong>程序运行时</strong>设定的</p>
<p>一般格式:ASSUME 段寄存器名:段名,段寄存器名:段名,......</p>
<p>在一个代码段中可以有几条ASSUME伪指令,对于前面的设置,可以用ASSUME<strong>改变</strong>原来的设置</p>
<p>可以使用关键字NOTHING将前面的设置删除</p>
</li>
<li>
<p>段寄存器的装入</p>
<p>段寄存器的初值(段基值)装入需要用程序的方法来实现。四个段寄存器的装入方法略有不同</p>
<ul>
<li>
<p>DS和ES的装入:在程序中,使用<strong>数据传送语句</strong>来实现对DS和ES的装入</p>
</li>
<li>
<p>SS的装入</p>
<ul>
<li>在段定义伪指令的组合类型项中,使用STACK参数,并在段寻址伪指令ASSUME语句中把该段与<strong>SS</strong>段寄存器关联</li>
<li>如果在段定义伪指令的组合类型中,<strong>未使用STACK参数</strong>,或者是在程序中要调换到另一个堆栈,这时,可以使用类似于DS和ES的装入方法</li>
</ul>
</li>
<li>
<p>CS的装入</p>
<p>CPU在执行指令之前根据CS和IP的内容来从内存中提取指令,即必须在程序执行之前装入CS和IP的值。因此,CS和IP的初始值就<strong>不能用可执行语句来装入</strong></p>
<p>装入CS和IP一般有下面两种情况:</p>
<ul>
<li>由系统软件按照结束伪指令指定的地址装入初始的CS和IP。任何一个源程序都<strong>必须</strong>以END伪指令来结束。其格式为:END 起始地址</li>
<li>在程序运行期间,当执行某些指令时,CPU自动修改CS和IP,使它们指向新的代码段</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h3 id="46-过程定义伪指令">4.6 过程定义伪指令</h3>
<p>在程序设计过程中,常常将具有一定功能的程序段 设计成一个子程序。在<strong>MASM</strong>宏汇编程序中,用过 程(PROCEDURE)来构造子程序</p>
<p>过程定义伪指令格式如下:</p>
<p>过程名 PROC </p>
<p>...... RET ......</p>
<p>过程名 ENDP</p>
<p><strong>过程名</strong>是子程序的名称,它被用作过程调用指令<strong>CALL的目的操作数</strong>。它<strong>类同一个标号的作用</strong>。具有段、偏移量和距离三个属性。而距离属性使用NEAR和FAR来指定,若没有指 定,则<strong>隐含为NEAR</strong></p>
<p>每一个过程中<strong>必须</strong>包含有返回指令RET,其作用是控制CPU从子程序中返回到调用该过程的主程序</p>
<hr>
<h3 id="47-当前位置计数器与定位伪指令orgorigin">4.7 当前位置计数器$与定位伪指令ORG(Origin)</h3>
<p>汇编程序在汇编源程序时,<strong>每遇到一个逻辑段</strong>,就要为其设置一个位置计数器,它用来记录该逻辑段中定义的每一个数据或每一条指令<strong>在逻辑段中</strong>的相对位置</p>
<p>在源程序中,使用符号$来表示位置计数器的当前值。因此,$被称为<strong>当前计数器</strong>。它位于不同的位置具有不同的值</p>
<p>位置计数器$在使用上完全类似<strong>变量</strong>的使用</p>
<p><strong>定位伪指令ORG</strong>——用来改变位置计数器的值</p>
<p>格式:ORG 数值表达式</p>
<p>作用:将数值表达式的值赋给当前位置计数器$ 。<strong>ORG语句为其后的数据或指令设置起始偏移量</strong></p>
<p>表达式的值必须为<strong>正值</strong>。表达式中也可以<strong>包含</strong>有当前位置计数器的<strong>现行值$</strong></p>
<hr>
<h3 id="48-编题伪指令title">4.8 编题伪指令TITLE</h3>
<p>语句格式:TITLE 标题名</p>
<p>作用:给所在程序指定一个标题。以便在列表文件的每一页的第一行都显示这个标题。其中标题是用户任意选用的<strong>字符串</strong>,字符个数不能超过<strong>60</strong></p>
<hr>
<h3 id="49-从程序返回操作系统的方法">4.9 从程序返回操作系统的方法</h3>
<p>为了使程序运行结束后,能够正确地返回到操作系统,需要在程序中加上一些必要的语句。一般有以下两种方法:</p>
<ol>
<li>
<p>使用程序段前缀PSP(Program Segment Prefix)实现返回</p>
<p>DOS系统将一个.EXE文件(可执行文件)装入内存时,在该文件的前面生成一个程序段前缀PSP,其长度为100H字节。同时让<strong>DS和ES</strong>都指向PSP的开始,而<strong>CS</strong>指向该程序的代码段,即第一条可执行指令</p>
<p>为了使程序执行完后,正确返回DOS,需要做以下三个操作:</p>
<ul>
<li>将用户程序编制成一个过程,类型为FAR</li>
<li>将PSP的起始<strong>逻辑地址</strong>压栈,即将INT 20H指令的<strong>地址</strong>压栈</li>
<li>在用户程序结尾处,使用一条RET指令。执行该指令将使保存在堆栈中的PSP的起始地址弹出到CS和IP中</li>
</ul>
</li>
<li>
<p>使用DOS系统功能调用实现返回</p>
<p>执行DOS功能调用4CH,也可以控制用户程序结束,并返回DOS操作系统</p>
<p>在程序结束时,使用两条指令:</p>
<p>MOV AH,4CH</p>
<p>INT 21H</p>
</li>
</ol><br><br>
来源:https://www.cnblogs.com/chw123/p/15987705.html
頁:
[1]