南山耕夫 發表於 2020-5-15 09:09:00

汇编语言-05[BX]和loop指令

<h2>和内存单元的描述</h2>
<h3>内存单元</h3>
<p>mov ax,</p>
<p>将一个内存单元的内容送入ax,这个内存单元的长度为2字节(<strong>字单元</strong>),存放一个字,偏移地址为0, 段地址在ds中。</p>
<p>mov al,</p>
<p>将一个内存单元的内容送入al,这个内存单元的长度为1字节(<strong>字节单元</strong>),存放一个字节,偏移地址为0,段地址在ds中。</p>
<p>要完整地描述一个内存单元, 需要两种信息:<strong>1.内存单元的地址;2.内存单元的长度(类型)</strong>。</p>
<p>用表示一个内存单元时,0表示单元的偏移地址,段地址默认在ds中,单元的长度(类型)可以由具体指令中的其他操作对象(比如说寄存器)指出。</p>
<h3></h3>
<p>同样也表示一个内存单元,它的偏移地址在bx 中,比如下面的指令:</p>
<p>mov ax,</p>
<p>将一个内存单元的内容送入ax,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ds中。</p>
<p>mov al,</p>
<p>将一个内存单元的内容送入al,这个内存单元的长度为1字节(字节单元),存放一个字节,偏移地址在bx中,段地址在ds中。</p>
<h3>loop</h3>
<p>循环指令</p>
<h3>符号"()”</h3>
<p>使用一个描述性的符号”()”来表示一个寄存器或一个内存单元中的内容。</p>
<p>(ax)表示ax中的内容、(al)表示al中的内容;</p>
<p>(20000H)表示内存20000H 单元的内容(()中的内存单元的地址为物理地址);</p>
<p>((ds)*16+(bx))表示:ds中的内容为ADR1,bx中的内容为ADR2,内存ADR1 x16 +ADR2单元的内容。或者ds中的ADR1作为段地址,bx中的ADR2作为偏移地址,内存ADR1:ADR2单元的内容。</p>
<p>”()”中的元素可以有3 种类型: 1.寄存器名; 2.段寄存器名; 3.内存单元的物理地址(一个20 位数据) 。</p>
<p>(ax)、(ds)、(al)、(ex)、(20000H)、((ds)*16+(bx))等是正确的用法;(2000:0)、((ds):1000H)等是不正确的用法,这里没有计算出物理地址。</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200508124907927-1667070365.png" alt=""></p>
<h3>约定符号idata表示常量</h3>
<p>mov ax, , 表示将ds:0处的数据送入ax中。指令中,在“[... ]"里用一个常量0表示内存单元的偏移地址。我们用idata表示常量</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200508125609542-845563961.png" alt=""></p>
<h2></h2>
<p>mov ax, </p>
<p>功能: bx中存放的数据作为一个偏移地址EA,段地址SA默认在ds中,将SA:EA中的数据送入ax中。即:(ax)=((ds)* 16+(bx))。</p>
<p>mov ,ax</p>
<p>功能: bx中存放的数据作为一个偏移地址EA,段地址SA 默认在ds中,将ax中的数据送入内存SA:EA 处。即:((ds)*16+(bx))=(ax) 。</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200508162917347-642041645.png" alt=""></p>
<p>inc bx的含义是bx 中的内容加1;&nbsp; &nbsp;mov bx,1&nbsp; &nbsp; &nbsp;inc bx&nbsp; 执行后,bx=2 。</p>
<p>最终结果</p>
<p><em><em><em><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200508163435048-1721229792.png" alt=""></em></em></em></p>
<h2>Loop指令</h2>
<p>loop 指令的格式是: loop 标号,CPU执行loop指令的时候,要进行两步操作,1.(cx)=(cx) - 1; 2.判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行。</p>
<p>用loop 指令来实现循环功能, ex 中存放循环次数。</p>
<div class="cnblogs_code">
<pre>assume cs:code<span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">代码段code</span>
<span style="color: rgba(0, 0, 0, 1)">
code segment</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">代码段开始</span>
    mov ax,<span style="color: rgba(128, 0, 0, 1)">2</span>    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">ax寄存器赋值为2</span>
    mov cx,<span style="color: rgba(128, 0, 0, 1)">11</span>      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">cx记录循环次数</span>
    s: add ax,ax   <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">循环标记</span>
    loop s    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">判断cx中的值,不为零则转至标号处执行程序,如果为零则向下执行</span>
<span style="color: rgba(0, 0, 0, 1)">   
    mov ax</span>,4c00h   <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">退出程序</span>
    <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> 21h
code ends</span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">代码段结束</span>
<span style="color: rgba(0, 0, 0, 1)">
end   </span><span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">结束(编译器识别)</span></pre>
</div>
<h3>标号</h3>
<p>标号s,它实际上标识了一个地址,这个地址处有一条指令:add ax,ax。</p>
<h3>loop s</h3>
<p>CPU 执行loop s的时候,要进行两步操作:</p>
<p>1.(cx)=(cx) - 1;</p>
<p>2.判断cx中的值,不为0则转至标号s所标识的地址处执行(这里的指令是add&nbsp;ax,ax),如果为零则执行下一条指令(下一条指令是mov ax,4c00h) 。</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200508164532907-122919409.png" alt=""></p>
<p>执行loop s时,首先要将(cx)减1,然后若(cx)不为0,则向前转至s处执行add ax,ax。所以,可以利用cx来控制add ax,ax的执行次数。</p>
<p>在cx中存放循环次数;<br>loop指令中的标号所标识地址要在前面;<br>要循环执行的程序段,要写在标号和loop指令的中间。</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200509161754655-786602299.png" alt=""></p>
<h2>Debug跟踪loop指令实现循环程序</h2>
<p>计算ffff:0006单元中的数乘以3,结果存储在dx中。</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200509170932118-1796347771.png" alt=""></p>
<p>第一条指令mov ax,0ffffh 。我们知道,大于9FFFh 的十六进制数据A000H 、A001H... C000H、C001H... FFFEH、FFFFH等,在书写的时候都是以字母开头的。而在<span style="color: rgba(255, 0, 0, 1)"><strong>汇编源程序中,数据不能以字母开头</strong></span>,所以要在前面加0。比如,9138h 在汇编源程序中可以直接写为"9138h",而<strong>A000h在汇编源程序中要写为“0A000h"</strong>。</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200510215035645-311827446.png" alt=""></p>
<p>loop 指令分两个步骤:第一步先将(cx)减1,即(cx)=2;第二步因为(cx)不等于0 , 将IP设为0012h。指令"loop 0012"执行后。</p>
<p>debug调试时想跳过前边所有步骤可以用"g 偏移地址"直接执行到cs:偏移地址的位置,相当于给指令寄存器IP赋值。</p>
<h2>Debug和汇编编译器masm对指令的不同处理</h2>
<p>"mov al,"、“mov bl,"、“mov dl,",但Debug 和编译器对这些指令中的” " 却有不同的解释。Debug 将它解释为” " 是一个内存单元,"idata" 是内存单元的偏移地址;而编译器将“" 解释为“idata" 。</p>
<p>可将偏移地址送入bx 寄存器中, 用 的方式来访问内存单元。比如我们可以这样访问2000:0 单元:</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200511171242683-78242390.png" alt=""></p>
<p>&nbsp;</p>
<p>在“[]”,中直接给出内存单元的偏移地址。这样做,在汇编源程序中也是可以的,只不过,要在“[]"的前面显式地给出段地址所在的段寄存器。</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200511171502679-105722303.png" alt=""></p>
<p>汇编源程序中以下指令的含义:</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200511171325839-2127603066.png" alt=""></p>
<p>Debug P2.exe</p>
<p><img src="https://img2020.cnblogs.com/blog/1208477/202005/1208477-20200513100149291-1456833298.png" alt=""></p>
<p>总结:</p>
<p>1.在汇编源程序中,如果用指令访问一个内存单元,则在指令中必须用”[...],来表示内存单元,如果在“0”里用一个常量idata直接给出内存单元的偏移地址,就要在"[]"的前面显式地给出段地址所在的段寄存器,例如mov al ,ds:。如果没有在“[]"的前面显式地给出段地址所在的段寄存器,例如mov al,,那么,编译器masm将把指令中的”"解释为"idata"。</p>
<p>2.如果在"[]"里用寄存器,比如bx,间接给出内存单元的偏移地址,则段地址默认在ds中。当然,也可以显式地给出段地址所在的段寄存器。</p>
<h2>loop和的联合应用</h2>
<p>计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中。</p>
<div class="cnblogs_code">
<pre>assume cs:<span style="color: rgba(0, 0, 0, 1)">codesg

codesg segment
    mov ax</span>,<span style="color: rgba(0, 0, 0, 1)">0fffh
    mov ds</span>,<span style="color: rgba(0, 0, 0, 1)">ax
    mov bx</span>,<span style="color: rgba(128, 0, 0, 1)">0</span>    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">初始化ds:bx指向ffff:0</span>
<span style="color: rgba(0, 0, 0, 1)">   
    mov dx</span>,<span style="color: rgba(128, 0, 0, 1)">0</span>    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">初始化累加寄存器dx,(dx)=0</span>
<span style="color: rgba(0, 0, 0, 1)">   
    mov cx</span>,<span style="color: rgba(128, 0, 0, 1)">12</span>    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">初始化循环计数寄存器cx,(cx)=12</span>
<span style="color: rgba(0, 0, 0, 1)">   
s</span>:mov al,<span style="color: rgba(0, 0, 0, 1)">
    mov ah</span>,<span style="color: rgba(128, 0, 0, 1)">0</span><span style="color: rgba(0, 0, 0, 1)">
    add dx</span>,ax    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">间接向dx中加上((ds)*16+(bx))单元的数值</span>
    inc bx    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">bx自加一,ds:bx指向下一个单元</span>
<span style="color: rgba(0, 0, 0, 1)">    loop s
   
    mov ax</span>,<span style="color: rgba(0, 0, 0, 1)">4c00H
    </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> 21H
codesg ends

end</span></pre>
</div>
<p>“mov al,"中的bx就可以看作一个代表内存单元地址的变量,我们可以不写新的指令,仅通过改变bx中的数值,改变指令访问的内存单元。</p>
<h2>段前缀</h2>
<p>指令“mov ax, "中,内存单元的偏移地址由bx给出,而段地址默认在ds中。我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器。比如:</p>
<p>(1) mov ax,ds:<br>将一个内存单元的内容送入ax, 这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ds中。</p>
<p>(2) mov ax,cs:<br>将一个内存单元的内容送入ax, 这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在cs中。</p>
<p>(3) mov ax,ss:<br>将一个内存单元的内容送入ax, 这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在ss中。</p>
<p>(4) mov ax,es:<br>将一个内存单元的内容送入ax, 这个内存单元的长度为2字节(字单元),存放一个字,偏移地址在bx中,段地址在es中。</p>
<p>(5) mov ax,ss:<br>将一个内存单元的内容送入ax, 这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在ss中。</p>
<p>(6) mov ax,cs:<br>将一个内存单元的内容送入ax, 这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为0,段地址在cs中。</p>
<p>这些出现在访问内存单元的指令中,用千显式地指明内存单元的段地址的"ds:" "cs:" "ss:" "es:", 在汇编语言中称为段前缀。</p>
<h2>一段安全的空间</h2>
<p>随意向一段内存空间写入内容是很危险的,因为这段空间中可能存放着重要的系统数据或代码。</p>
<p>总结:</p>
<p>(1) 我们需要直接向一段内存中写入内容;<br>(2) 这段内存空间不应存放系统或其他程序的数据或代码,否则写入操作很可能引发错误;<br>(3) DOS方式下, 一般情况,0:200~0:2ff空间中没有系统或其他程序的数据或代码; .<br>(4) 以后,我们需要直接向一段内存中写入内容时,就使用<span style="color: rgba(255, 0, 0, 1)"><strong>0:200~0:2ff</strong></span>这段空间。</p>
<h2>段前缀的使用</h2>
<p>将内存ffff:0 - ffff:b单元中的数据复制到0:200 ~ 0:20b单元中。</p>
<div class="cnblogs_code">
<pre>assume cs:<span style="color: rgba(0, 0, 0, 1)">codesg

codesg segment

      mov ax</span>,<span style="color: rgba(0, 0, 0, 1)">0ffffh
      mov ds</span>,ax    <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">(ds)=0ffffh</span>
<span style="color: rgba(0, 0, 0, 1)">      
      mov ax</span>,<span style="color: rgba(0, 0, 0, 1)">0020h
      mov es</span>,ax      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">(es) =0020h</span>
<span style="color: rgba(0, 0, 0, 1)">      
      mov bx</span>,O      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">(bx)=O, 此时ds:bx指向ffff:0,es:bx指向0020:0</span>
<span style="color: rgba(0, 0, 0, 1)">      
      mov cx</span>,<span style="color: rgba(128, 0, 0, 1)">12</span>      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">(ex)=12,循环12次</span>
<span style="color: rgba(0, 0, 0, 1)">      
   s</span>:mov dl,      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">(dl)=((ds)*16+ (bx)),将ffff:bx中的数据送入dl</span>
      mov es:,dl      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">((es)*16+(bx))=(dl) ,将dl中的数据送入0020:bx</span>
      inc bx      <span style="color: rgba(0, 128, 0, 1)">#</span><span style="color: rgba(0, 128, 0, 1)">(bx)=(bx)+1</span>
<span style="color: rgba(0, 0, 0, 1)">      loop s
   
    mov ax</span>,<span style="color: rgba(0, 0, 0, 1)">4c00H
    </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> 21H
codesg ends

end</span></pre>
</div>
<p>使用es存放目标空间0020:0-0020:b的段地址,用ds存放源始空间而ffff:b的段地址。在访问内存单元的指令“mov es:,al" 中,显式地用段前缀“es:“给出单元的段地址,这样就不必在循环中重复设置ds 。</p>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    如果这篇文章对你有用,可以关注本人微信公众号获取更多ヽ(^ω^)ノ~<br>
<img id="ViewPicture1_GalleryImage" alt="微信公众号二维码" src="https://images.cnblogs.com/cnblogs_com/aeolian/1679458/o_wechat_gzh_qrcode.jpg" style="height: 258 px; width: 258 px; border-width: 0px">
<br><br><br>
来源:https://www.cnblogs.com/aeolian/p/12849825.html
頁: [1]
查看完整版本: 汇编语言-05[BX]和loop指令