汇编语言学习笔记(3)——指令系统
<p> </p><hr><hr>
<p>1、指令概述</p>
<p> </p>
<ul>
<li><strong>指令</strong>:通知 CPU执行某种操作的“命令” , CPU全部指令的集合,称为<strong>指令系统</strong></li>
<li>指令的书写格式
<ul>
<li><strong>目标指令</strong>(机器指令)格式为:二进制形式的序列(即:一串0,1代码)。注意:硬件只能识别,存储,运行目标指令</li>
<li><strong>符号指令</strong>:用规定的操作码和操作数助记符, 按照规定的书写格式书写的指令,格式为:字 符串形式的序列</li>
</ul>
</li>
<li><strong>指令的组成</strong>:大多数指令由<strong>操作码和操作数</strong>两个部分组成
<ul>
<li>操作码:通知CPU执行什么操作(唯一)</li>
<li>操作数:指令的操作对象(一个或多个)</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022140354607-1539694455.png" alt="" width="304" height="90" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<ul>
<li>目标程序的生成
<ul>
<li>CPU只能识别,存储,运行目标指令,而用机器指令编程非常困难。于是早期的专家们发明了符号指令,再经过软件把符号指令→机器指令。 图示如下:</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022140439761-693843007.png" alt="" width="382" height="54" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<p> </p>
<p> </p>
<ul>
<li><strong>指令长度</strong>:指令在存储器中占用的字节数称为指令长度。 86x86指令长度(机器指令长度)为1~16字节。</li>
<li><strong>指令地址</strong>:单字节指令占用1个内存单元,多字节指令占用连续的内存单元,存放指令第一字节的内存单元地址,称为“指令地址” 。</li>
<li><strong>指令存放</strong>:指令存放在内存中时,先存放操作码,再存放操 作数。多字节操作数连续存放。顺序依据<strong>小端法原则</strong>,即: 低位字节存放在低地址单元,高位字 节存放在相邻的高地址单元</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022140957314-857442786.png" alt="" width="373" height="84" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li>符号指令的书写格式</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022141019604-605117261.png" alt="" width="325" height="29" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<ul>
<li style="list-style-type: none">
<ul>
<li>操作码:CPU执行的操作</li>
<li>操作数:指令执行时的操作对象(一个或多个)</li>
<li>标号:以字母开头,后跟字母,数字,下划线,长度 ≤31字符,标号又称符号地址,代表该指令的逻辑地 址。可有可无,设置是为了程序的转向</li>
<li>注解:以“ ;”开头,不执行,打印程序清单时照 原样打印,“系统保留字”不能做标号。</li>
</ul>
</li>
</ul>
<hr><hr>
<p> </p>
<p>2、寻址方式</p>
<ul>
<li> 寻址方式:操作数是指令的操作对象,寻址方式就是在指令中,使用特定的助忆符(地址表达式), 告知 CPU 如何计算出操作数的地址,从而可以正确去除操作数进行后继指令操作与地址码有 密切关系。形成操作数地址码的过程就是寻址。</li>
<li>操作数所在的四种不同的物理位置</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022141321361-1969639915.png" alt="" width="571" height="361" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li>操作数的分类
<ul>
<li>操作数包含在指令中,这种操作数称为<strong>立即数</strong> 。</li>
<li>操作数存放在CPU的某个寄存器中,这种操作数称为<strong>寄存器操作数</strong>。</li>
<li>操作数存放在存储器中,这种操作数称为<strong>存储器操作数 (内存操作数)</strong> 。</li>
<li>操作数存放在I/O端口中,这种操作数称为 <strong>I/O端口操作数</strong>。</li>
</ul>
</li>
<li>立即寻址:操作数包含在本条指令中,是指令 的一部分,完整地取出该条指令,也就获得了操作数。</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022141638626-199122047.png" alt="" width="434" height="391" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li style="list-style-type: none">
<ul>
<li>下例源操作数即为立即寻址</li>
</ul>
</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">MOV EAX,12345678H
MOV BL,10101010B ;AAH → BL
MOV CL, – </span><span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)"> ;FCH →CL
MOV DL,’A’ ;41H →DL
ADD AL,0C8H
MOV SI,</span><span style="color: rgba(128, 0, 128, 1)">3</span>*<span style="color: rgba(128, 0, 128, 1)">5</span> ;<span style="color: rgba(128, 0, 128, 1)">15</span> →SI</pre>
</div>
<p> </p>
<ul>
<li style="list-style-type: none">
<ul>
<li>立即数书写规定:
<ul>
<li>立即数以数字开头,以A~F开头的16进制数,必须前缀0。</li>
<li>立即数的数制用后缀表示,B表示二进制数,H表示十六 进制数, D或缺省为十进制数,单引号括起来的字符编 译成相应的ASCII码 。</li>
<li>可以用+ – * / 组成立即数表达式</li>
</ul>
</li>
</ul>
</li>
<li>寄存器寻址:操作数在CPU的某个寄存器中, 符号指令中直接写出寄存器名称。</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022142348450-338764910.png" alt="" width="557" height="443" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<ul>
<li style="list-style-type: none">
<ul>
<li>下述6条指令,目标操作数即为寄存器寻址</li>
</ul>
</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">MOV EAX,12345678H
MOV BL,10101010B ;AAH → BL
MOV CL, – </span><span style="color: rgba(128, 0, 128, 1)">4</span><span style="color: rgba(0, 0, 0, 1)"> ;FCH →CL
MOV DL,’</span><span style="color: rgba(128, 0, 128, 1)">5</span><span style="color: rgba(0, 0, 0, 1)">’ ;35H →DL
ADD AL,0C8H
MOV SI,</span><span style="color: rgba(128, 0, 128, 1)">3</span>*<span style="color: rgba(128, 0, 128, 1)">5</span> ;<span style="color: rgba(128, 0, 128, 1)">15</span> →SI</pre>
</div>
<ul>
<li style="list-style-type: none">
<ul>
<li>下述指令,目标操作数和源操作数均为寄存器寻址</li>
</ul>
</li>
</ul>
<div class="cnblogs_code">
<pre>MOV AX , DS ;DS内容 → AX</pre>
</div>
<ul>
<li style="list-style-type: none">
<ul>
<li>下述单操作数指令,操作数均为寄存器寻址</li>
</ul>
</li>
</ul>
<div class="cnblogs_code">
<pre>INC SI ;SI+<span style="color: rgba(128, 0, 128, 1)">1</span><span style="color: rgba(0, 0, 0, 1)"> → SI (Increase 增量)
DEC DI ;DI – </span><span style="color: rgba(128, 0, 128, 1)">1</span> → DI (Decrease 减量)</pre>
</div>
<p> </p>
<p> </p>
<p> </p>
<ul>
<li> 存储器操作数寻址方式
<ul>
<li>在读写内存操作数之前,CPU必须知道相关存 储单元的物理地址。</li>
<li>由于CPU对存储器采用分段管理, 因此指令格式 中只能写出存放操作数的内存单元的“逻辑地 址” 。</li>
<li>程序员的责任仅在于正确的书写逻辑地址表达 式,然后由CPU自动运算以求出物理地址。</li>
<li>存储单元逻辑地址表达式的一般形式</li>
</ul>
</li>
</ul>
<p> <img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022143645661-620991714.png" alt="" width="393" height="109" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li style="list-style-type: none">
<ul>
<li>实模式下,物理地址=段寄存器×2<sup>4</sup> +偏移地址</li>
<li>直接寻址
<ul>
<li>地址表达式的格式1:段寄存器:[偏移地址]<br>
<ul>
<li>如:MOV AL, ES:</li>
<li>注:这种格式很少使用,通常情况下,程序员 并不知道某单元的偏移地址。</li>
</ul>
</li>
<li>地址表达式的格式2: 段寄存器:变量名。用变量名代表存储单元的有效地址</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022144016923-1714098435.png" alt="" width="545" height="422" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li style="list-style-type: none">
<ul>
<li style="list-style-type: none">
<ul>
<li>汇编语言允许为某单元起一个“名字” ,这个 名字就称为该单元的“变量名”,经汇编之后, 变量名有段基址和偏移量两种属性。</li>
<li>由于变量名是唯一的,程序中不能有重复的变 量名, “段寄存器:”可以省略。</li>
</ul>
</li>
<li>寄存器间接寻址(寄存器间接寻址又称间接寻址,间址):操作数在内存单元,该单元的段基址在段寄存器中 有效地址在间址寄存器中,CPU首先进行地址计算<br>
<ul>
<li>间接寻址的地址表达式:段寄存器:[间址寄存器]
<ul>
<li>某单元的物理地址=段寄存器内容×16+间址寄存器</li>
</ul>
</li>
<li>访问约定的逻辑段,间接寻址的地址表达式 简化为: [间址寄存器]<br>
<ul>
<li>某单元的物理地址=约定的段寄存器内容×16+ 间址寄存器</li>
</ul>
</li>
<li>间址寄存器和约定访问的逻辑段</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022144424514-850527801.png" alt="" width="467" height="189" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022144455175-1242294686.png" alt="" width="495" height="372" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li style="list-style-type: none">
<ul>
<li style="list-style-type: none">
<ul>
<li>注意:BX间址,约定访问的是数据段,因此, “DS:”可省;BP间址约定访问的是堆栈段,因此 “DS:”不可省</li>
</ul>
</li>
<li>基址寻址
<ul>
<li>在基址寻址中,有效地址由两部分组成。 一部分在基址寄存器中,另一部分为常量 。</li>
<li>基址寻址的地址表达式:段寄存器:[基址寄存器+位移量]
<ul>
<li>物理地址=段寄存器内容×16+基址寄存器+位移量</li>
</ul>
</li>
<li>访问约定的逻辑段,简化的地址表达式: [基址寄存器+位移量]
<ul>
<li>物理地址=约定的段寄存器内容×16+基址寄存器 +位移量</li>
</ul>
</li>
<li>基址寄存器和约定访问的逻辑段</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022145236057-87580012.png" alt="" width="418" height="165" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022145300604-1943763742.png" alt="" width="453" height="358" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li style="list-style-type: none">
<ul>
<li>变址寻址
<ul>
<li>有比例因子的变址寻址其地址表达式为段寄存器:[比例因子*变址寄存器+位移量]<br>
<ul>
<li>物理地址=段寄存器×16+比例因子×变址寄存器 +位移量</li>
</ul>
</li>
<li>没有比例因子的变址寻址其地址表达式为:段寄存器:[变址寄存器+位移量]
<ul>
<li>访问约定的逻辑段可简化为 : [变址寄存器+位移量]</li>
<li>物理地址=约定的段寄存器×16+变址寄存器+位移量</li>
</ul>
</li>
<li>物理地址=约定的段寄存器×16+变址寄存器+位移量</li>
</ul>
</li>
</ul>
</li>
</ul>
<p> </p>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022145435416-757085460.png" alt="" width="413" height="151" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022145456086-1475563255.png" alt="" width="493" height="386" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li style="list-style-type: none">
<ul>
<li>基址加变址寻址
<ul>
<li>存储单元的有效地址由3部分组成</li>
<li>有比例因子的基址加变址的地址表达式为: 段寄存器:[基址寄存器+比例因子*变址寄存器+位移量]
<ul>
<li>访问约定逻辑段其地址表达式简化为: [基址寄存器+比例因子*变址寄存器+位移量]</li>
</ul>
</li>
<li>无比例因子基址加变址的地址表达式为: 段寄存器:[基址寄存器+变址寄存器+位移量]
<ul>
<li>访问约定逻辑段:[基址寄存器+变址寄存器+位移量]</li>
</ul>
</li>
<li>基址寄存器和变址寄存器都是16位或都是32位, 否则(16位寻址和32位寻址混合使用)是非法 指令 </li>
<li>默认的段寄存器不一致,这样的组合虽然是合 法,但容易出错</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022145755249-86754852.png" alt="" width="516" height="407" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<p> </p>
<ul>
<li>要点总结:
<ul>
<li>指令中的操作数有4种:立即数、寄存器 操作数、内存操作数和I/O端口操作数。</li>
<li>访问立即数使用立即寻址方式。</li>
<li>访问寄存器操作数使用寄存器寻址方式。 </li>
<li>访问内存操作数有5种寻址方式: 直接寻址、间址、基址、变址、基址加变址。 </li>
<li>访问I/O端口操作数使用I/O端口寻址方式。 </li>
<li>关于16位寻址和32位寻址
<ul>
<li>16位寻址:采用16位间址、基址、变址、基址加变址 </li>
<li>32位寻址:采用32位间址、基址、变址、基址加变址 </li>
</ul>
</li>
<li>在实模式下,一个逻辑段的体积最大为64K, 存储 单元的有效地址为16位,不可能超过 FFFFH,因此,在实模式下运行的程序通常采 用16位寻址</li>
<li>关于段约定和段超越在用间址、基址、变址、基址加变址寻址内存操作数时,其地址表达式都有2种书写格式——有段前缀和无段前缀
<ul>
<li>如用BP、EBP、ESP参与寻址,CPU自动认为是访问堆栈段, 因此段超越前缀“SS:” 可省</li>
<li>如用BP、EBP、ESP参与寻址非堆栈段,必须明确写出段超越前缀</li>
<li>如用BX、SI、DI、EAX~EDX、ESI、EDI参与寻址, CPU自动认为是访问数据段,因此“ DS:”可省</li>
<li> 如用BX、SI、DI、EAX、EDX、ESI、EDI参与寻址非数据段,必须明确写出段超越前缀</li>
</ul>
</li>
<li>使用段约定访问内存操作数是最常用的编程风格</li>
</ul>
</li>
</ul>
<p> </p>
<hr><hr>
<p>3、标志寄存器</p>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022150355599-41652019.png" alt="" width="484" height="82" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<ul>
<li>80486标志寄存器为32位,实际使用15位</li>
<li>15位标志分为两类:状态标志和控制标志
<ul>
<li>状态标志记录了当前指令执行后的状态信息 </li>
<li>控制标志用来控制微处理器操作</li>
</ul>
</li>
<li>状态标志
<ul>
<li>C标志—进位/借位标志<br>
<ul>
<li>字节加/减,最高位(D7)产生进位/借位时: C标志置1,否则置0</li>
<li>字加/减,最高位(D15)产生进位/借位时: C标志置1,否则置0</li>
<li>双字加/减,最高位(D31)产生进位/借位时: C标志置1,否则置0</li>
</ul>
</li>
<li>A标志—辅助进位/辅助借位标志<br>
<ul>
<li>字节、字、双字加/减,D3位产生进位/借位时: A标志置1,否则置0</li>
</ul>
</li>
<li>S标志—符号标志<br>
<ul>
<li>字节运算后,结果的最高位D7位为1, S标志置1,否则置0</li>
<li>字运算后,结果的最高位D15位为1, S标志置1,否则置0</li>
<li>双字运算后,结果的最高位D31位为1, S标志置1,否则置0</li>
</ul>
</li>
<li>Z标志—结果标志<br>
<ul>
<li>运算结果为全0时,Z标志置1,否则置0</li>
</ul>
</li>
<li>P标志—奇偶标志(实际上是偶标志)<br>
<ul>
<li>运算结果中,最低的1个字节中“1”的个数为偶数个(没有“1”也是偶数),P标 志置1,否则置0</li>
</ul>
</li>
<li>O标志—溢出标志<br>
<ul>
<li>运算结果产生溢出,则O标志置1,否则置0</li>
</ul>
</li>
</ul>
</li>
</ul>
<hr><hr>
<p> 4、语句类型和格式</p>
<ul>
<li>语句类型<br>
<ul>
<li>汇编语言源程序包括的语句类型为:<strong>指令性语句</strong>和<strong>指示性语句</strong></li>
<li>指令性语句即为通常所说的符号指令。
<ul>
<li>符号指令:经汇编后,其机器指令通知CPU进行什么操作。</li>
</ul>
</li>
<li>指示性语句包括伪指令和宏指令。
<ul>
<li>伪指令:是非机器指令,是在汇编链接期间 进行操作的。为汇编程序,链接程序提供汇编链接信息</li>
</ul>
</li>
<li>符号指令和伪指令区别</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022153507497-204156605.png" alt="" width="535" height="170" loading="lazy"></p>
<ul>
<li>
<ul>
<li>指令性语句(符号指令)的格式为:</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022153629615-975582812.png" alt="" width="617" height="28" loading="lazy"></p>
<p> </p>
<ul>
<li>
<ul>
<li>指示性语句(伪指令)的格式为:</li>
</ul>
</li>
</ul>
<p><img src="https://img2020.cnblogs.com/blog/2531609/202110/2531609-20211022153658029-34032845.png" alt="" width="643" height="29" loading="lazy"></p>
<ul>
<ul>
<li>
<ul>
<li>说明:标号名、变量名命名规则:以除数字以外的字母或符号 开头,后跟字母、数字…长度≤31个字符</li>
</ul>
</li>
<li>常用的伪指令:<br>
<ul>
<li>数据定义:DB、DW、DD、DF、DQ、DT<br>
<ul>
<li>DB:字节字义伪指令</li>
<li>DW:字定义伪指令</li>
<li>DD:双字定义伪指令</li>
<li>DF:三字定义伪指令</li>
<li>DQ:四字定义伪指令</li>
<li>DT:五字定义伪指令</li>
</ul>
</li>
<li>符号定义:EQU、=
<ul>
<li>用EQU定义的符号常数,其值在后继语句中不能 更改;用“=”定义的符号常数,其值在后继语句中 可以重新定义</li>
</ul>
</li>
</ul>
</li>
<li>常用运算符:$、SEG、OFFSET、PTR、算术运算、逻辑运算、 关系运算
<ul>
<li>$运算符
<ul>
<li>汇编程序对源程序是逐行汇编的,$运算符可以返回汇编计数器的当前值。</li>
<li>应用:$运算符紧跟在 DB、DW、DD伪指令之后,统 计字符串的长度。</li>
</ul>
</li>
<li>SEG运算符
<ul>
<li>格式: SEG 段名或变量名或标号名</li>
<li>功能: 计算某一逻辑段的段基址</li>
</ul>
</li>
<li>OFFSET运算符
<ul>
<li>格式:OFFSET 变量名或标号名</li>
<li>功能:算出某个变量或标号名所在单元的 相对于段首的偏移地址(有效地址)。</li>
</ul>
</li>
<li>PTR运算符
<ul>
<li>格式: 类型说明符 PTR 地址表达式</li>
<li>功能: 在本条指令中临时修改地址表达式的属性</li>
<li>应用:在下列指令中必须用PTR临时修改或者显示说明 内存操作数的属性
<ul>
<li>在双操作数指令中(如:MOV,ADD,SUB,CMP……)
<ul>
<li>源操作数为立即数,目标为直接寻址的内存操作数, 当二者类型属性不一致时,后者必须用PTR临时修改其 属性,使源目两个操作数类型属性一致。</li>
<li>源为单字节/双字节立即数,目标操作数为间址、 变址、基址或基址加变址寻址的内存操作数,无论两者 类型属性是否已经一致,后者都必须用PTR显示说明其 属性,使其与源操作数属性一致。</li>
<li>源操作数,目标操作数中有一方为直接寻址的内存 器操作数,但二者类型属性不一致,必须用PTR临时修 改其中存储器操作数的属性 。</li>
</ul>
</li>
<li>在单操作数指令中(如:INC,DEC……)
<ul>
<li>操作数为间址、变址、基址或基址加变址寻址的存储器操作数,必须用PTR说明是字节操作,字操作,还是 双字操作(具体要根据使用该条指令操作的意图);</li>
<li>操作数是直接寻址方式的存储器操作数,是否使用 PTR要看:指令对操作数的类型属性要求是否与操作数的 类型属性一致(例: PUSH 指令)或依据该条指令的操作 意图(按照字节方式?字方式?等)。</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>方括号运算符<br>
<ul>
<li>用方括号括起来的地址表达式是访问内存操 作数常用的寻址方式,方括号的另一用途是 标注数组元素的下标,下标从0开始。</li>
</ul>
</li>
<li>算术运算符、逻辑运算符、关系运算符<br>
<ul>
<li>算术运算符: +, -, *, /</li>
<li>逻辑运算符: AND(与), OR(或), XOR(异或),NOT(非), SHL(左移位),SHR(右移位)</li>
<li>关系运算符: EQ(等于), NE(不等于), GT(大于),LT(小 于), GE(大于或等于),LE(小于等于)
<ul>
<li>注:指令汇编后,如果为真,则结果为FFFFH, 如果为假,则结果为0</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
</ul>
<hr><hr>
<p> </p><br><br>
来源:https://www.cnblogs.com/taking/p/15439009.html
頁:
[1]