小嫦娥 發表於 2022-3-6 22:18:00

汇编语言设计

<h1 id="汇编语言设计">汇编语言设计</h1>
<hr>
<p>机器语言是机器指令集合</p>
<p>早期:打纸带:1打孔,0不打孔</p>
<h2 id="汇编语言与汇编指令">汇编语言与汇编指令</h2>
<p>汇编语言主体是汇编指令</p>
<ul>
<li>
<p>汇编指令是机器指令便于记忆书写格式</p>
</li>
<li>
<p>汇编指令是机器指令助记符</p>
</li>
<li>
<blockquote>
<p>机器指令100110001100001100000</p>
<p>汇编MOV,AX,BX</p>
</blockquote>
</li>
</ul>
<h2 id="汇编语言编写程序工作过程">汇编语言编写程序工作过程</h2>
<p>汇编指令--编译器--机器码</p>
<p>伪指令--编译器执行</p>
<p>汇编指令--机器码助记符</p>
<p>其他符号--编译器识别</p>
<h2 id="计算机组成">计算机组成</h2>
<p>cpu,总线,内存,扩展槽</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224070-1807078335.png"></p>
<p>指令和数据存放在内存或磁盘上,都是二进制</p>
<p>数据如何表示:二进制B,八进制O,十进制D,十六进制H</p>
<h3 id="计算机中的存储单元">计算机中的存储单元</h3>
<p>一个存储器有128单元</p>
<p>8086有20条数据总线,那有2的20次方的,1MB的空间</p>
<p>总线:链接cpu和其他芯片的导线,</p>
<p>逻辑上总线分:数据总线,地址总线,控制总线</p>
<h3 id="地址总线">地址总线</h3>
<p>地址总线宽度,决定了可寻址总线的存储单元大小</p>
<h3 id="数据总线">数据总线</h3>
<p>cou内存和其他器件之间的数据传送是通过数据总线进行的,数据总线宽度决定了cou的外界数据传输速度</p>
<p>8088 8位,8086,16位</p>
<h3 id="控制总线">控制总线</h3>
<p>cpu对外部设备器件进行控制</p>
<h2 id="内存的读写和地址空间">内存的读写和地址空间</h2>
<p>存储单元地址(地址信息)</p>
<p>器件的选择,读或写的命令(控制信息)</p>
<p>读或写的数据(数据信息)</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224015-1908481624.png"></p>
<h3 id="什么是内存地址空间">什么是内存地址空间</h3>
<p>RAM:能读能写,没电就没了,</p>
<p>ROM:只读,系统的BIOS</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224075-1263201270.png"></p>
<p>所有物理存储器看做一个由若干存储单元组成的逻辑存储器</p>
<p>每个物理存储器在逻辑存储器中占有一个地址段,即一段地址空间</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224094-132554284.png"></p>
<h2 id="汇编语言实践语言的环境搭建">汇编语言实践语言的环境搭建</h2>
<p><strong>目标</strong>:<strong>理解计算机底层工作原理</strong></p>
<p>DOS环境<br>
链接:https://pan.baidu.com/s/1SMBlRQQXeJ4EUMmVhPfCxw?pwd=kaip<br>
提取码:kaip<br>
--来自百度网盘超级会员V5的分享</p>
<p>解压后的文件夹如下:<br>
 <img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223223947-1959879117.jpg"><br>
 其中:<br>
 DOSBox0.74-win32-installer.exe是DOS模拟器的安装文件;<br>
 MASM文件夹中是汇编程序设计中用到的命令;<br>
 EX文件夹中提供了几个汇编程序作为示例。<br>
 <br>
 照下面的提示完成环境的配置。<br>
 <strong>1. 将MASM文件夹拷贝到工作盘</strong><br>
 MASM中包含了8086汇编程序设计中要用到的几个必需的工具(masm.exe汇编、link.exe连接、Debug.exe调试,edit.com和edlin.com是两个编辑程序。),如下:<br>
 <img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223223985-771752510.jpg"><br>
 将这个文件夹拷贝到你习惯的工作盘(我用D盘作工作盘,拷贝后,这些文件在D:\MASM中)。<br>
 <strong>2. 安装DOSBox</strong><br>
 双击DOSBox0.74-win32-installer.exe,照提示安装,最后,桌面上有快捷方式:<br>
 <img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223223967-521490106.jpg"> <br>
 <strong>3. 启动DOSBox</strong><br>
 双击上面的图标,启动DOSBox0.74。启动后的界面如下:<br>
 <img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224172-980383627.jpg"><br>
 <strong>4.挂接MASM文件夹</strong><br>
 在Z:&gt;提示符后输入命令mount c d:\masm。这个命令的意思是,将本机d:\masm文件夹作为模拟器的C盘。<br>
 运行命令后的界面如下:<br>
 <img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224176-500615344.jpg"><br>
 注意:d:\masm是第1步拷贝到的工作目录,若你在第1步设置的是其他工作,请按你的实际设置改动。<br>
 下面,将工作盘转换到C盘(输入C:并回车),再查看目录(dir再回车),可以看到类似下面的界面,标志着工作环境设置好了。<br>
 <img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224230-1668468776.jpg"><br>
 在以后工作时,只需要第3、4步即可。</p>
<h1 id="寄存器和数据存储">寄存器和数据存储</h1>
<hr>
<p>运算器进行信息处理</p>
<p>寄存器进行信息存储</p>
<p>控制器协调各种器件</p>
<p>内存总线实现CPU各器件之间的联系</p>
<h3 id="14个寄存器">14个寄存器</h3>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224226-1932656096.png"></p>
<h3 id="通用寄存器ax例子">通用寄存器AX例子</h3>
<pre><code class="language-shell">在AX中存储18D

18D --十进制
12H --十六进制
10010 --二进制

</code></pre>
<p>问:8088 如何8086兼容?</p>
<p>分高低位</p>
<pre><code>01001110 00100000
高位      低位

AH AL AX
BH BL BX
CH CL CX
DH DL DX
</code></pre>
<p>字:一个字可以存在一个16位的寄存器中</p>
<p>高位字节在高位寄存器高8位,低位字节在低8位寄存器</p>
<h2 id="mov-add指令">MOV add指令</h2>
<p>mov ax.18         AX = 18 相当于赋值</p>
<p>mov ah.78         AH = 78 把78放到了高 8位</p>
<p>add ax.8            AX = AX +8在AX数据加8</p>
<p>mov.ax.bx             AX = BX BX数据送入AX中</p>
<p>add ax.bx            AX = AX+BX把AX,BX 内容相加</p>
<p><strong>汇编指令不区分大小写</strong></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224263-833333666.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224320-434982800.png"></p>
<h3 id="确定物理地址方法">确定物理地址方法</h3>
<p>川普访问内存单元都要给出内存单元地址</p>
<p>每一个内存单元在这个空间构成的空间都有唯一地址,这个地址叫物理地址</p>
<p>8086有20位地址总线,寻址能力是1mb</p>
<p>8086是16位cpu,寻址能力是64KB</p>
<p>如何解决寻址空间的矛盾</p>
<p><strong>用两个16位地址和段地址,偏移地址合成一个20位的物理地址</strong></p>
<p><strong>物理地址 = 段地址X16+偏移地址</strong></p>
<p>例子:</p>
<p>段地址: 1230 偏移1位</p>
<p>偏移地址 00C8</p>
<pre><code>12300
+00C8
= 123C8
</code></pre>
<p>物理地址 = 123C8</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224317-1990260441.png"></p>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224346-92685043.png">
<p>本质含义</p>
<p>用两个16位地址相加得到20位物理地址</p>
<p>本质含义:cpu在访问内存时,用一个基础地址和一个相对于基础地址的偏移地址相加,给出一个内存单元的物理地址</p>
<h2 id="内存的分段表示法">内存的分段表示法</h2>
<p><strong>内存并没有分段,段的划分来自于cpu</strong></p>
<p>同一段内存,多种分段方案</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224411-1601092749.png"></p>
<p>根据段地址计算偏移地址</p>
<p>4个段寄存器</p>
<p>CS-代码段DS数据段-SS栈段-ES段加</p>
<h2 id="debug的使用">debug的使用</h2>
<p>R查看寄存器的内容</p>
<p>R 寄存器名 改变指定寄存器内容(128字节)</p>
<p>RCS,RIP</p>
<p>D命令:查看内存中的内容</p>
<p>D段地址:偏移地址-列出内存中指定地址处的内容</p>
<p>D 1000:0</p>
<p>D段地址:偏移地址 结尾偏移地址-;列出内存中指定地址范围内容</p>
<p>E命令</p>
<p>E段地址:偏移地址 数据1</p>
<p>EE段地址:偏移地址</p>
<p>E 1000:0</p>
<p>逐个询问式修改</p>
<p>空格接收,继续</p>
<p>回车,结束</p>
<p>U命令将内存中机器指令翻译成汇编指令</p>
<pre><code>有汇编指令
mov ax,0123H
mov bx,0003H
mov ax,bx
add ax,bx

对应机器码
B8 23 01
BB 03 00
89 D8
01 D8


</code></pre>
<p>e 地址- 数据写入</p>
<p>d 地址查看</p>
<p>u地址-查看代码</p>
<p>A命令</p>
<pre><code>有汇编指令
mov ax,0123H
mov bx,0003H
mav ax,bx
add ax,bx
对应机器码
B8 23 01
BB 03 00
89 D8
01 D8

</code></pre>
<p>a 地址-写入汇编指令</p>
<p>d 地址-查看数据</p>
<p>u 地址 -查看代码</p>
<p>T指令(执行机器命令)</p>
<p>t 执行CS:IP处的指令</p>
<p>mov ax,123H</p>
<h2 id="两个关键的寄存器">两个关键的寄存器</h2>
<p>CS:代码寄存器</p>
<p>IP:指令指针寄存器</p>
<p>CS:IP   :cpu将内存中CS:IP指向的内容当做指令执行</p>
<p>CS内容是2000H,IP中内容是0000H</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224391-999942373.png"></p>
<p>8086PC工作过程简要概述</p>
<p>1.CS:IP指向内存单元读取指令,读取的指令进入指令缓存器</p>
<p>2.IP = IP+ 所读取的指令的长度,从而指向下一条指令</p>
<p>3.执行指令,转到步骤1</p>
<h2 id="jmp指令">jmp指令</h2>
<p>事实:执行何处的指令,去取决于CS:IP</p>
<p>应用;可以通过改变CS IP的内容,来控制执行目标的指令</p>
<p>转移指令jmp</p>
<p>同时修改cs,ip内容</p>
<p>jmp段地址:偏移地址</p>
<p>jmp:2AE3:3</p>
<p>jmp:30B16</p>
<p>用指令中给出的段地址修改cs,偏移地址修改ip、</p>
<p>仅修改ip内容</p>
<pre><code>jmp 某一合法寄存器

​        jmp ax (类似于mov IP,ax)

​        jmp bx

</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224523-1709399906.png"></p>
<h2 id="内存中字的存储">内存中字的存储</h2>
<p>8086 16位作为一个字</p>
<p>高8位,低8位</p>
<p>低位字节低地址单元,高位字节高地址单元</p>
<p>0是低地址单元,1是高地址单元</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224466-967436505.png"></p>
<p>字单元:由两个连续地址连续内存单元组成,存放一个字型数据</p>
<p>原理在一个字单元中,低地址单元存放低位字节,搞地质单元存放高位字节</p>
<h2 id="用ds和address实现字的传送">用DS和实现字的传送</h2>
<p>要解决的问题:cpu从内存单元中要读取数据</p>
<p>在8086中,内存地址有段地址和偏移地址组成(段地址:偏移地址)</p>
<p>DS寄存器存放要访问的数据和段地址</p>
<p>偏移地址用[]形式</p>
<pre><code>将段地址送入DS的方式
mov bx,1000H
mov bs, bx
套路是: 数据---一般寄存器---段寄存器
</code></pre>
<h3 id="字的传送">字的传送</h3>
<p>8086可以一次性传送一个字</p>
<p>例子:</p>
<pre><code class="language-shell">mov bx,1000H
mov ds ,bx
mov ax, # 1000:0处的字型数据送入ax
mov,cx # cx中的16位数据送到1000:0处

</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224457-1878265467.png"></p>
<pre><code> -- 1123
-- 2211
-- 1122

</code></pre>
<h2 id="ds与数据段">DS与数据段</h2>
<p>对内存单元中数据的访问</p>
<p>对于8086 可以根据需要将一组内存单元定义为一个段</p>
<p>将一组长度N,地址连续,起始地址16的倍数内存单元当做专门存储数据内存空间,从而定义的一个数据段</p>
<p>段地址 123BH 起始偏移地址0000H 长度10字节</p>
<p>处理方法 DS()</p>
<p>用DS存放数据段的段地址</p>
<p>用相关指令访问数据段中的具体单元,单元地址由</p>
<p>将123B0H--123BAH定义为数据段</p>
<p>累加数据段中前3个单元的数据</p>
<pre><code>mov ax,123BH
mov ds,ax
mov al,
add al,
add,al,
add al,
</code></pre>
<p>用mov指令操作数据</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224482-744232582.png"></p>
<p>add指令操作</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224600-1952837220.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224585-626947365.png"></p>
<h2 id="栈结构">栈结构</h2>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224527-1249074419.png"></p>
<h3 id="栈的基本操作">栈的基本操作</h3>
<p>入栈和出栈</p>
<p>入栈:将一个新的元素放到栈顶</p>
<p>出栈:从栈顶取出一个元素</p>
<p>栈顶元素总是在最后入栈,需要出栈,又最先从栈中取出</p>
<p>cpu提供栈机制</p>
<p>8086cpu提供相关指令,支持用栈的方式访问内存空间</p>
<p>基于8086cpu编程,可以将一段内存当栈来使用</p>
<pre><code>PUSH入栈和POP出栈指令
push ax:将ax从中的数据送入栈中
pop ax:从栈顶取出数据送入ax
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225196-1709307174.gif"></p>
<blockquote>
<p>问题:cpu如何知道一段内存空间被当做栈使用?</p>
<p>执行push和pop的时候,如何知道哪个单元是栈顶单元?</p>
</blockquote>
<blockquote>
<p>答案</p>
<p>8086中有两个与栈相关的寄存器</p>
<p>栈段寄存器SS--存放栈顶的段地址</p>
<p>栈顶指针寄存器SP--存放栈顶的偏移地址</p>
</blockquote>
<p>栈的操作</p>
<pre><code>mov ax,1000H

mov ss,ax

mov sp,0010H

mov ax,001AH
mov bx,001BH

push ax
push bx

pop ax
pop bx
</code></pre>
<p>!<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224642-752758586.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224628-537640958.png"></p>
<p>执行入栈,栈顶超出栈空间<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224691-1962853405.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224675-1503151727.png"></p>
<h2 id="总结"><strong>总结</strong></h2>
<p>push,pop实质上就是一种内存传送指令,可以在寄存器和内存之间传送数据,与mov指令不同的是,push指令访问的内存单元地址不是在指令中给出的,而是有SS:SP指出的</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224837-647360462.png"></p>
<p><strong>段总结</strong></p>
<p>物理地址 = 段地址x16 +偏移地址</p>
<h4 id="数据段">数据段</h4>
<ul>
<li>将段地址放在DS中</li>
<li>用mov,add,sub等访问内存单元指令时,cpu将我们定义的数据段中内容当做数据段访问</li>
</ul>
<h4 id="代码段">代码段</h4>
<ul>
<li>将段地址放在CS中,将段中第一条指令的偏移地址放在IP</li>
<li>CPU将执行我么定义四的代码段中指令</li>
</ul>
<h4 id="栈段">栈段</h4>
<ul>
<li>将段地址放在SS,将栈顶单元偏移的放在SP中</li>
<li>CPU在需要我们进行栈操作push,pop,就将我们定义的栈段当做栈空间来使用</li>
</ul>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224753-239708349.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224948-255315040.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224850-450499296.png"></p>
<h1 id="汇编语言程序">汇编语言程序</h1>
<hr>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224920-535621107.png"></p>
<p>汇编语言编程序的工作过程</p>
<p>程序员--汇编程序--编译器--机器码--计算机</p>
<p>伪指令:没有对应的机器码指令,最终不被cpu所执行</p>
<p>伪指令是编译器执行,编译器根据伪指令进行相关的编译工作</p>
<p>汇编程序:汇编程序好伪指令的文本</p>
<p>程序返回:程序结束后将cpu的控制权交还给使运行的程序系统</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224989-422456606.png"></p>
<h2 id="程序的三种伪指令">程序的三种伪指令</h2>
<p>段定义:一个会变程序是多个段组成的,这些段用来被存放代码,数据或当做栈空间来使用</p>
<p>一个有意义的汇编程序中至少有一个段,这个段用来存放代码</p>
<p>段名 segment --- 段的开始</p>
<p>段名 ends--- 段的结束</p>
<pre><code class="language-assembly">assume cs:codesg
codesg segment
        mov ax,0123H
        mov bx,0456H
        add ax,bx
        add ax,ax
        mov ax,4c00h
        int 21h
codesg ends
end



'''



end

汇编程序结束标志

assume

含义是假设某一段寄存器中某一个使用segment,ends定义段相关联--assume cs:cdoesg指的是cs寄存器与codesg关联,将定义的codesg当做程序的代码来使用
'''
</code></pre>
<p>源程序经过编译连接后变为机器码</p>
<p>源程序文件 .asm ---可执行 .exe</p>
<p>汇编程序结构</p>
<p>在debug中直接写入指令编写汇编程序</p>
<p>适用于简单的短小精悍的程序</p>
<p>单独编写成源文件在编译可执行文件--编写大程序</p>
<p><strong>注释</strong> ;</p>
<h3 id="编写一个汇编程序">编写一个汇编程序</h3>
<p>步骤</p>
<ul>
<li>
<p>定义一个段</p>
</li>
<li>
<p>实现处理任务</p>
</li>
<li>
<p>指出程序在何时结束</p>
</li>
<li>
<p>段与段寄存器关联</p>
</li>
<li>
<p>加上程序返回的代码</p>
</li>
<li>
<pre><code class="language-assembly">assume cs:abc
abc segment
abc segment
        mov ax,2
        add ax,ax
        add ax,ax
       
        mov ax,4c00h
        int 21h
abc ends
end
</code></pre>
<p>程序中出现的错误</p>
<p>语法错误</p>
<p>写错了。。。</p>
<p>逻辑错误</p>
<p>脑子抽了。。。</p>
</li>
</ul>
<h3 id="写出源程序到可执行文件过程">写出源程序到可执行文件过程</h3>
<p>编辑源程序</p>
<p>编译</p>
<p>​        对目标文件.OB对一个源程序进行编译要得到的结果</p>
<p>​        列表文件.LST是编译器将源程序编译为目标文件的过程中产生的中间结果</p>
<p>​        交叉引用文件。CRF和列表文件一样,是编译器将源程序编译为目标文件过程中产生的中间结果</p>
<p>对源程序编译结束,编译器输出最后两行告诉错误必须改正的错误</p>
<pre><code class="language-assembly">assume cs:codesg
codesg segment
        mov ax,0123H
        mov bx,0456H
        add ax,bx
        add ax,ax

        mov ax,4c00h
        int 21h
codesg ends
end



</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224741-1991663166.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224745-901087869.png"></p>
<h3 id="提示语法错误">提示语法错误</h3>
<p>severe errirs</p>
<p>找不到所给出的源文件</p>
<h3 id="连接">连接</h3>
<p>可执行文件是对一个程序进行连接要得到的最终结果</p>
<p>映象文件:是连接程序将目标文件连接为可执行文件过程中产生中间结果</p>
<p>库文件包含了一些可以调用的子程序,如果我们的程序中调用了某一个库文件中的子程序,就需要连接的时候将这个库文件和我们的目标文件连接到一起,生成可执行文件、</p>
<p>no stack segment,一个 没有栈段的错误警告,可以不去理会</p>
<p>执行程序;直接输入文件名</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224878-1734777759.png"></p>
<h2 id="程序的运行和跟踪">程序的运行和跟踪</h2>
<hr>
<h3 id="debug装在程序exe">DEBUG装在程序EXE</h3>
<p>程序加载后,DS中存放着程序所在内存区的段地址,这个内存区的偏移地址是0,程序内存区地址是DS:0</p>
<p>这个内存区前256字节存PSP,DOS用来程序进行通信</p>
<p>从256字节向后空间存放的是程序,CS值是DS+10H</p>
<p>程序加载后,CX存放代码长度</p>
<p><strong>继续命令P</strong></p>
<p>类似于T命令,逐条执行指令,显示结果,但遇到子程序,中断等,直接执行,然后显示结果</p>
<p><strong>运行命令G</strong></p>
<p>从指定地址处开始运行程序,知道断点结束或者程序正常结束</p>
<p>在DOS中执行</p>
<p>程序执行的常态</p>
<p>一个内存单元的描述:</p>
<ul>
<li>​        内存单元地址</li>
<li>​        内存单元长度</li>
</ul>
<p>[...]表示一个内存单元</p>
<p>、<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224987-632747100.png"></p>
<p>(...)表示一个内存单元或寄存器中内容</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223224977-1942906917.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225429-799132038.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225169-1713495084.png"></p>
<h2 id="loop指令">LOOP指令</h2>
<p>功能:实现循环</p>
<p>cpu执行loop指令时进行的操作</p>
<ol>
<li>
<p>(cx)=(cx)-1</p>
</li>
<li>
<p>判断cx的值</p>
<ol>
<li>
<p>不为0则转到标号处执行程序</p>
</li>
<li>
<p>如果是0就向下执行</p>
</li>
</ol>
</li>
</ol>
<p>要求</p>
<p>cx要提前存放循环次数,(cx)影响着loop指令执行结果</p>
<p>要定义一个符号</p>
<pre><code class="language-assembly">assume cs:code
code segment
        mov ax,2
        mov cx,11
        s:add ax,ax
        loop s
       
        mov ax,4c00h
        int 21h
code ends
end
</code></pre>
<p>例子:编程计算 2^2   ,   2^3</p>
<p>cx和loop指令想配合实现循环功能的三个要点</p>
<p>​        1.在cx中存放循环次数</p>
<p>​        2.用标号指定循环开始的位置</p>
<p>​        3.在标号和loop指令的中间,写上要循环执行的程序段(循环体)</p>
<p>​        例子:计算 123x236</p>
<pre><code class="language-assembly">assume cs:code
code segment
        mov ax,0
        mov cx,236
        s: add ax,123
        loop s
        mov ax,4c00h
code ends
end

</code></pre>
<p>例子</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225303-190702758.png"></p>
<p>编译:</p>
<pre><code>masm p5-1.asm
link p5-1
debug p5-1.exe

</code></pre>
<pre><code class="language-assembly">assume cs:code

code segment
        mov ax,0ffffh
        mov ds,ax
        mov bx,6
        mov al,
        mov ah,0
       
        mov dx,0
        mov cx,3
        s: add dx,ax
        loop s
       
       
        mov ax,4c00h
        int 21h
code ends
end




</code></pre>
<h3 id="引入段前缀异常现象的对策">引入段前缀,异常现象的对策</h3>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225567-23902756.png"></p>
<h2 id="访问内存单元--loop和bx">访问内存单元--loop和</h2>
<p>问题:计算ffff:0-ffff:b字节单元中的数据的和,结果存储在dx中</p>
<p>分析:略</p>
<p>对策:取出8位数据,加到16位寄存器</p>
<pre><code>mov al,ds:取出8位数据
mov ah,0
add dx,ax

</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225484-1212608601.png"></p>
<p>段前缀的使用</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225298-859423727.png"></p>
<h3 id="代码段中数据的使用">代码段中数据的使用</h3>
<p>将内存ffff:0中数据拷贝到0:200-0:20b</p>
<p>问题,程序中直接写地址,很危险</p>
<p>对策</p>
<p>在程序中段中存放数据,运行时由操作系统分配空间</p>
<p>段的类别:数据段,代码段,栈段</p>
<p>各种段中均可以有数据</p>
<p>可以在单个段中安置,也可以将数据代码,栈放入不同的段中</p>
<h3 id="实际应用">实际应用</h3>
<p>问题编程计算以下8个数据的和,结果存放到ax寄存器中</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225964-404096936.png"></p>
<p>解决方案</p>
<pre><code class="language-assembly">assume cd:code
code segment
        dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987 ;在代码段中定义数据,只要求数据本身,并未指定在哪些单元中
       
        mov bx,0
        mov ax,0
        mov cx,8
        s: add ax,cs:
        add bx,2
        loop s
       
        mov ax,4c00h
        int 21h
code ends
end



</code></pre>
<p>dw:定义一个字</p>
<p>db:定义一个字节</p>
<p>dd:定义一个双字</p>
<p>代码有问题</p>
<p>改进</p>
<pre><code class="language-assembly">assume cs:code
code segment
        dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987 ;在代码段中定义数据,只要求数据本身,并未指定在哪些单元中

start:         ; 定义一个标号,指示代码开始的位置
        mov bx,0
        mov ax,0
        mov cx,8
        s: add ax,cs:
        add bx,2
        loop s

        mov ax,4c00h
        int 21h
code ends
end start ;end 通知编译器程序结束外,还可以通知编译器入口在什么地方

</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223226059-942630035.png"></p>
<h3 id="在代码段中使用栈">在代码段中使用栈</h3>
<p>程序运行时候。定义存放的数据cs:0--cs:f,一共8个单元</p>
<p>依次将八个单元中数据入栈,然后一次出栈找到8个字单元,从而实现数据的逆序存放</p>
<p>栈需要内存空间,在程序中通过定义:空数据来获得</p>
<p>数据逆序存放程序</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225494-1780624802.png"></p>
<pre><code class="language-assembly">assume cs:codesg
codesg segment
        dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987 ;在代码段中定义数据,只要求数据本身,并未指定在哪些单元中
        dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
       
start:
        mov ax,cs
        mov ss,ax
        mov sp,30h
        mov bx,0
        mov cx,8
        s:push cs:
        add bx,2
        loop s
       
        mov bx,0
        mov cx,8
        s0:pop cs:
        add bx,2
        loop s0
       
        mov ax,4c00h
        int 21h

codesg ends
end start
</code></pre>
<p>将数据代码栈放入不同段</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225470-1185824187.png"></p>
<pre><code class="language-assembly">

assume cs:code,ds:data,ss:stack
data segment
    dw 0123H,0456H,0789H,0ABCH,0DEFH,0CBAH,0987
data ends
stack segment
    dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
stack ends

; --------------------------- 数据
code segment
start:
    mov ax,stack
    mov ss,ax
    mov sp,20h
    mov ax,data
    mov ds,ax

    mov bx,0
    mov cx,8
;---------------------------入栈
    s:push
    add bx,2
    loop s


    mov bx,0
    mov cx,8

   ; --------------------------出栈
    s0:pop
    add bx,2
    loop s0

    mov ax,4c00h
    int 21h
code ends
end

</code></pre>
<h1 id="内存寻址方式">内存寻址方式</h1>
<hr>
<h2 id="处理字符问题">处理字符问题</h2>
<p>汇编中,用‘..................'的方式指明数据是字符形式给出的,编译器把他们转化为响应的ASCII码</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225428-1818974592.png"></p>
<p>大小写字符问题</p>
<p>大写+20H ---&gt;小写 小写-20H ---&gt;大写</p>
<p>大小写转换</p>
<p>第一个字符串:小--大</p>
<p>第二个 大--小</p>
<pre><code class="language-assembly">assume cs:codesg,ds:datasg
datasg segment
    db 'BsSic'
    db 'INforMAtion'

datasg ends

codesg:segment
start:
    mov ax,datasg
    mov ds,ax
    ;第一个字符串:
    mov bx,0
    mov cx,5
    s:mov al,
    and al,11011111b
    mov ,al
    inc bx
    loop s
    ;第二个字符串
    mov bx,5
    mov,cx 11
    s0:mov al,
    or al,00100000b
    mov ,al
    inc bx
    loop s0
    mov ax,4c00h
    int 21h
codesg ends
end start

</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306223225170-1610382643.png"></p>
<h2 id="bxidata含义">含义</h2>
<p>表示一个内存单元,它的偏移地址为(bx)+idata (bx中数值加上idata)</p>
<p>mov ax,/mov ax,</p>
<p>将一个内存单元的内容送入ax</p>
<p>这个内存单元的段地址在ds中,偏移地址在ds中,偏移地址为200加上bx的数值,数学化的描述是(ax) = ((ds)*16+200+(bx))</p>
<p>指令mov,ax,其他用法</p>
<p>mov ax,</p>
<p>mov ax,200</p>
<p>mov ax,.200</p>
<p><strong>用数组方法解决大小写转换问题</strong></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306224747557-611919401.png"></p>
<pre><code class="language-assembly">assume cs:codesg,ds:datasg
datasg segment
    db 'BasiC'
    db 'MinIX'
datasg ends
codesg segment
start:
    mov ax,datasg
    mov ds,ax

    mov bx,0
    mov cx,5
    s:mov al,
    and al,11011111b
    mov ,al

    mov al,
    or al,00100000b
    mov ,al
    inc bx
    loop s

    mov ax,4c00h
    int 21h
codesg ends
end start


</code></pre>
<h2 id="si和di寄存器">SI和DI寄存器</h2>
<hr>
<p>8086cpu有十四个寄存器</p>
<p>通用寄存器 AX,BX,CX,DX</p>
<p>变址寄存器 SI,DI</p>
<p>指针寄存器 SP,DP</p>
<p>指令指针寄存器 IP</p>
<p>段寄存器 CS,SS,DS,ES</p>
<p>标志寄存器 PSW</p>
<p>执行与地址有关操作</p>
<p>SI,DI和BX相近的寄存器</p>
<p>SI:源变址寄存器</p>
<p>DI目标变址寄存器</p>
<p>区别:SI,DI不能够分成两个8位寄存器来使用</p>
<p>1.mov bx,0</p>
<ol start="2">
<li>mov s,0</li>
<li>mov di 0</li>
</ol>
<p>例子:用寄存器SI,SI实现将字符串‘welcome to masm’复制到特后面的数据区</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306225902342-534959815.png"></p>
<p>源数据起始地址:datasg:0</p>
<p>目标数据起始地址:datasg:16</p>
<p>用ds:si指向要复制的原始字符串</p>
<p>用ds:di指向目的空间</p>
<p>然后用一个循环来完成复制</p>
<p>原地址:si</p>
<p>目标地址:di</p>
<pre><code class="language-assembly">assume cs:codesg,ds:datasg
datasg segment
    db 'welcome to masm'
    db '...............'
    datasg ends
    codesg segment
    start:
      mov ax,datasg
      mov ds,ax

      mov si,0
      mov di,16
      mov cx,8
      s: mov ax,
      mov ,ax
      add si,2
      add di,2
      loop s

      mov ax,4c00h
      int 21h
    codesg ends
    end start
</code></pre>
<h3 id="bxsi-bxdi指定地址"> 指定地址</h3>
<ul>
<li> 表示一个内存单元</li>
<li>偏移地址 bx +si 即是数值上加si中的数值</li>
</ul>
<p>指令mov ax, bx+si含义</p>
<ul>
<li>将一个内存单元的内容送入ax</li>
<li>这个内存单元长度是2字节(字单元),存放一个字</li>
<li>偏移地址是bx中数值加上si中的数值</li>
<li>段地址 在ds中</li>
</ul>
<p>指令mov ax,的数学化描述</p>
<ul>
<li>(ax) = ((ds)*16+(bx)+(si))</li>
<li>mov ax,其他用法</li>
<li>mov ax, </li>
</ul>
<p>例子</p>
<pre><code class="language-assembly">mov ax,2000H
mov ds,ax
mov bx,1000H
mov si,0
mov ax,
inc si
mov cx,
inc si
mov di,si
mov ax,


</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220306233213685-771508178.png"></p>
<h3 id="bxsiidata-和-bxdiidata"> 和 </h3>
<p>偏移地址是 (bx) +(si)+idata 就是bx中的数值加上si中数值再加上idata</p>
<p>move ax,含义</p>
<p>将一个内存单元的内容送入ax</p>
<p>这个内存单元长度是2字节,存放一个字</p>
<p>偏移地址中bx中数值加上idata,段地址在ds中</p>
<p>数字化描述</p>
<p>(ax=(ds)*16)+(bx)+(si)+idata</p>
<p>指令mov,ax, 其他写法</p>
<ul>
<li>mov ax,</li>
<li>mov ax,</li>
<li>mov ax,200 </li>
<li>mov ax,.200</li>
<li>mov ax, .200</li>
<li>mov ax, </li>
</ul>
<p>例子</p>
<p>内存中数据2000:1000 BE 00 06 00 6A 22</p>
<p>程序执行后</p>
<pre><code class="language-assembly">mov ax,2000H
mov ds,ax
mov bx,1000H
mov si,0
mov ax,
inc si
mov cx,
inc si
mov di,si
mov ax,

</code></pre>
<h2 id="内存寻址方式的灵活应用">内存寻址方式的灵活应用</h2>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307220104948-1927919935.png"></p>
<p>例子</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307222058525-1797477913.png"></p>
<p>例子</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307222604106-507781286.png"></p>
<p>二重循环用了同一个cx,解决办法</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307222826706-1886364419.png"></p>
<p>但是存在问题是,cx占用寄存器资源,比较浪费</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307223233004-1931432445.png"></p>
<p>可以利用固定的储存单元,也可以用栈来存数据</p>
<p>直接寻址过程<br>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307225442438-1013451370.gif"></p>
<p>寄存器间间接寻址过程<br>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307225123766-1888606621.gif"></p>
<p>寄存器相对寻址过程<br>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307225123121-733473746.gif"><br>
基址变址寻址过程<br>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307225122379-1974189944.gif"><br>
相对基址变址寻址过程<br>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307225123285-1287231499.gif"></p>
<h3 id="哪些寄存器用于寻址">哪些寄存器用于寻址</h3>
<p>用于内存寻址的寄存器用法</p>
<p>只有bx,bp,si,di可以用在[…]对内存单元寻址</p>
<p>bx以外的通用寄存器,段寄存器不可以用[…]</p>
<p>bx,bp区别:bx默认指ds段</p>
<p>bp默认指ss段</p>
<p>正确的指令<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307231448973-98606516.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307231621067-433289129.png"></p>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307231627996-87074926.png">
<p>错误的指令</p>
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307231642496-1262847335.png">
<img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307231649790-1336012926.png">
<h3 id="汇编语言中数据位置的表达">汇编语言中数据位置的表达</h3>
<p>立即数</p>
<p>对于直接包含在机器指令的数据称为立即数</p>
<p>寄存器</p>
<p>指令要处理数据在寄存器中</p>
<p>内存:段地址和偏移地址</p>
<p>指令要处理的数据在内存中,由SA:EA确定内存单元</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220307232116518-950480357.png"></p>
<p>指令要处理的数据有多长</p>
<p>字word操作</p>
<pre><code class="language-assembly">-----------------
mov ax 1
mov bx,ds:
mov ds,ax
mov ds: ,ax
inc ax
add ax,1000
--------------------
mov al,1
mov al,bl
mov al,ds:
mov ds:,al
inc al
add al,100

--------------------
mov word ptr ds:,1
inc word ptr
inc word ptr ds:
add word ptr ,2
-----------------
mov byte ptr ds:,1
inc byte ptr
inc byte ptr ds:
add byte ptr ,2





</code></pre>
<p>字节操作</p>
<p>用word ptr 或 byte ptr指明</p>
<p>在没有寄存器参与的内存单元访问指令中,用word ptr 或 byte ptr 显性指明所要访问的内存单元长度是很有必要的,否则cpu将无法得知所要访问是字单元,还是字节单元</p>
<h2 id="div指令">DIV指令</h2>
<p>div是除法指令,使用div做除法时候</p>
<p>被除数:默认放在放在AX或DX和AX中</p>
<p>除数:8位或16位,在寄存器或内存单元中</p>
<p>div指令格式</p>
<p>​        div 寄存器</p>
<p>​        div 内存单元</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220308082338338-1911446292.png"></p>
<p>例子</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220308083432254-1338534251.png"></p>
<p>双字型数据定义</p>
<pre><code class="language-assembly">        data segment

​        db 1:定义字节型数据 在,data:0 占一个字节

dw 1:定义字型数据0001H,在data:1处,占2个字节

dd 1 定义双字型数据00000001H在data:3处,占2个字(4个字节)

data ends

data segment

​        dd 100001

​        dw 100

​        dw 0

data ends

mov ax,data

mov ds,ax

mov ax,ds:

mov dx,ds:

div word ptr ds:

mov ds:,ax
</code></pre>
<p>dup 功能</p>
<p>dup 和db,dw,dd等数据定义伪指令配合使用,用来进行数据的重复</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220308090632430-1776707914.png"></p>
<p>db 重复的次数dup(重复字节型数据)</p>
<p>dw 重复的次数dup(重复字节型数据)</p>
<p>dd 重复双字数据</p>
<pre><code class="language-assembly">assume cs:code,ds:data
data segment
        db 3 dup(0)
        db 3 dup(0,1,2)
        db 80 dup(0)
        db 3 dup('abc','ABC')
data ends

code segment
        mov ax,,data
        mov ds,ax
        mov ax,4c00h
code ends
end
</code></pre>
<h1 id="流程转移和子程序">流程转移和子程序</h1>
<hr>
<p>北京:一般情况下指令是顺序逐条执行的,实际中,需要改变执行流程</p>
<p>可以控制cpu的内存中某处代码指令</p>
<p>可以修改ip,或同时修改cs和ip指令</p>
<p>转移指令分类</p>
<ul>
<li>按转移行为</li>
<li>
<ul>
<li>段内转移:只修改ip 如imp ax</li>
<li>段间转移: 同时修改cs和ip ,imp 1000。</li>
</ul>
</li>
<li>根据指令对ip修改的范围不同</li>
<li>
<ul>
<li>段内短转移:ip修改范围128-127</li>
<li>段内近转移:ip修改范围32768-32767</li>
</ul>
</li>
<li>按转移指令</li>
<li>
<ul>
<li>无条件转移指令(imp)</li>
<li>条件转移指令 jcxz</li>
<li>循环指令 loop</li>
</ul>
</li>
<li>过程</li>
<li>中断</li>
</ul>
<h3 id="操作符-offset">操作符 offset</h3>
<p>用操作符 offset 取得标号的偏移地址</p>
<pre><code class="language-assembly">offse 标号
assume cs:codeseg
start: mov ax,offset start ;相当于mov ax,0
s:mov ax,offset s   ;相当于 mov ax,3
codeseg ends
end start

</code></pre>
<p>有如下程序段,添加2条指令,使改程序运行汇总s指令复制到s0处</p>
<pre><code class="language-assembly">
assume cs:codesg
codesg segment
        s:mov ax,bx
        mov si,offset s
        mov di,offset s0
        mov ax,cs:
        mov cs:,ax
        s0:nop
                nop ; nop机器码 占一个字节,起到一个占位的作用
        codesg ends
        end
       
</code></pre>
<p><strong>s 和s0处指令所在的内存单元是多少</strong></p>
<p>cs:offset 和cs:offset s0</p>
<p>将 s处的指令复制到s0处就是将cs:offset s处的数据复制到cs:offset s0处</p>
<p><strong>地址如何表示</strong></p>
<p>段地址已知在cs中,偏移地址已经送到si和di中</p>
<p><strong>要复制的数据有多长</strong></p>
<p>mov,ax,bx指令长度是两个字节,1个字</p>
<h3 id="jmp指令--无条件转移">jmp指令--无条件转移</h3>
<p>jmp指令的功能</p>
<p>无条件转移,可以只修改ip,也可以同时修改cs和ip</p>
<p>jmp指令要给出两种信息</p>
<p>转移的目的地址</p>
<p>转移的距离</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220308174643361-1842420274.png"></p>
<p>依据位移进行转移</p>
<p>jmp指令:依据位移进行转移</p>
<pre><code class="language-assembly">assume cs:codesg
codesg segment
        start:
                mov ax,0
                jmp short s
                add ax,1
                s:inc ax
codesg ends
end start

</code></pre>
<p>1.ip = 0003 cs:ip 指向E8 05</p>
<p>读取指令码 E8 05 进入指令缓冲器</p>
<p>ip = ip +所读指令长度 = ip +2=0005cs:ip 指向 add ax,0001</p>
<p>cpu指令缓冲器中的指令E805</p>
<p>指令E805执行后,ip=ip+05=000AH cs:ip 指向inc ax</p>
<pre><code class="language-assembly">assume cs:codesg
codesg segment
        start: mov ax,0
                jmp short s
                add ax,1
                nop
                nop
                s: inc ax
codesg ends
end start

</code></pre>
<p>俩种段内转移</p>
<p>短转移</p>
<pre><code>jmp short 标号
功能 ip = ip+8位位移
原理
8位位移 = 标号 处的地址-jmp指令后的第一个字节的地址
short 指明此处的位移是9位位移

</code></pre>
<p><strong>8位位移的范围是-128~127用补码表示</strong>,不能超出范围</p>
<p>8位位移编译程序在编译时候算出</p>
<p>进转移</p>
<pre><code>jmp near ptr 标号
功能 ip = ip +16
原理
16位位移 = 标号处的地址 imp 指令后的第一个、字节的地址
near ptr 指明此处的位移是16位位移,进行段内转移
16位移范围 -32769~32767,用补码表示
</code></pre>
<p>远转移 jmp far ptr 标号</p>
<p>段间转移:</p>
<p>far ptr 指明了跳转目的地址,即包含了标号段地址cs和偏移地址ip</p>
<p>段内转移</p>
<p>near ptr 指明了相对于当前ip转移位移,而不是转移的目的地址</p>
<p>转移地址在寄存器中jmp指令</p>
<p>指令格式 jmp 16位寄存器</p>
<p>ip = 16位寄存器</p>
<p>jmp ax    jmp bx</p>
<pre><code class="language-assembly">assume cs:codesg
codesg segement
start:mov ax,0
        mov bx,ax
        jmp bx
        mov ax,0123H
codesg ends
end start
</code></pre>
<p>转移地址在内存中的jmp指令</p>
<p>jwp word ptr 内存单元地址</p>
<p>功能:从内存单元地址处开始存放一个字,是转移的目的偏移地址</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220309092931675-1935439336.png"></p>
<p>jmp dword ptr 内存单元的地址</p>
<p>段间转移</p>
<p>功能:从内存单元地址开始存放两个字,高地址处的字是转移的目的段地址,低地址是转移的目的偏移地址</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220309093147813-1669690862.png"></p>
<h4 id="小结">小结</h4>
<p>jmp 标号</p>
<p>段间转移(远转移) : jmp far ptr 标号</p>
<p>段内短转移 jmp short 标号 8位位移</p>
<p>段内近转移: jmp near ptr 标号 16位位移</p>
<p>jmp 寄存器 jmp bx 16位移</p>
<p>jmp内存单元(表示跳转到地址)</p>
<p>段内转移 jmp word ptr 内存单元地址 jmp word ptr </p>
<p>段间转移 jmp dword ptr 内存单元地址 jmp dword ptr </p>
<p><strong>注意</strong></p>
<p>源程序中,不允许使用<strong>“jmp 2000:0100”</strong>的转移指令实现段间转移 • 这是在 Debug 中使用的汇编指令,汇编编译器并不认识 • 如果在源程序中使用,编译时也会报错。</p>
<h2 id="其他转移指令">其他转移指令</h2>
<hr>
<p>jcxz指令</p>
<p>格式 jcxz 标号</p>
<p>功能 如果 cx=0 则转移到标号执行</p>
<ul>
<li>cx 不等于 0什么也不做</li>
<li>cx = 0时ip=ip +8位位移</li>
<li>9位位移 = 标号 处的地址-jxcz指令后的第一个字节的地址</li>
<li>8位位移的范围是-128~127用补码表示</li>
<li>8位位移由编译程序在编译时算出</li>
</ul>
<p>jxcz是有条件转移指令</p>
<ul>
<li>所有的有条件转移指令</li>
<li>所有有条件转移指令都是短转移</li>
<li>对ip修改范围是-128~127</li>
<li>在对应的机器码中包含转移的位移,而不是目的的地址</li>
</ul>
<h3 id="loop指令-1">loop指令</h3>
<p>指令操作</p>
<p>cx=cx-1</p>
<p>当cx不等于0时则转移到标号处执行</p>
<p>cx=0,程序向下执行</p>
<p>loop s在执行时只涉及到s的位移</p>
<p>-4前移4个字节,补码表示FCH</p>
<ul>
<li>所有的有条件转移指令</li>
<li>所有有条件转移指令都是短转移</li>
<li>对ip修改范围是-128~127</li>
<li>在对应的机器码中包含转移的位移,而不是目的的地址</li>
</ul>
<p>根据位移进行相对转移的意义</p>
<p>对ip的修改时根据转移的目的地址和转移起始地址之间的位移进行</p>
<p>jmp short</p>
<p>jmp near ptr</p>
<p>jxcz 标号</p>
<p>loop标号</p>
<p>如果 loop s 的机器码中包含是s地址,则就对程序段的内存偏移地址有了严格的限制,易引发错误</p>
<p>当机器码中包含的是转移的位移无论s处的指令的实际地址是多少,loop指令的相对位移是不变的</p>
<h3 id="call指令-ret指令">call指令 ret指令</h3>
<pre><code>mov ax,0
call s
mov ax,4c00h
int 21h
s:add ax,1
ret
实质就是流程转移指令,它们都是修改ip同时修改cs,ip

</code></pre>
<p>call 指令</p>
<p>就是调用子程序</p>
<p>实质就是流程转移,和jmp原理相似</p>
<p>两步操作</p>
<p>当前ip或cs:ip压入栈</p>
<p>转移到标号处执行指令</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220309095637575-1808366522.png"><br>
call far ptr 标号实现的是段间转移</p>
<p>sp = sp -2</p>
<p>ssx 16+sp = cs</p>
<p>sp = sp -2</p>
<p>ssx16+sp = ip</p>
<p>相当于</p>
<p>push cs</p>
<p>push ip</p>
<p>jmp far ptr</p>
<p>转移地址在寄存器中的call指令</p>
<p>格式</p>
<p>call 16 寄存器</p>
<p>sp = sp-2</p>
<p>ss*16 +sp = ip</p>
<p>ip=16位寄存器</p>
<p>相当于</p>
<p>push ip</p>
<p>jmp 16位寄存器</p>
<p>转移地址在内存中call指令</p>
<p>call word ptr 内存单元地址</p>
<pre><code>mov sp,10h
mov ax,0123h
mov ds:,ax
call word ptr ds:
执行后 ip = 0123H ,sp=0EH

</code></pre>
<p>call word ptr</p>
<p>push cs</p>
<p>push ip</p>
<p>jmp word ptr 内存单元地址</p>
<pre><code>mov sp,10h
mov ax,0123H
mov ds:,ax
mov word ptr ds:,0
call word ptr ds:
执行cs = 0 ip = 0123H sp = 0CH
低地址放偏移地址
高地址放段地址

</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220310222550661-935954374.png"></p>
<h3 id="call-和ret配合使用">call 和ret配合使用</h3>
<p>计算2的N次方,N由cx提供</p>
<pre><code class="language-assembly">assume cs:code
code segment
start:
        mov ax,1
        mov cx,3
        call s
        mov bx,ax
        mov ax,4c00h
        int 21h
        ret
code ends
end start

</code></pre>
<pre><code class="language-assembly">assume cs:code,ss:stack
stack segment
        db 8 dup (0)
        db 8 dup (0)
stack ends
code segment
start:
        mov ax,stack
        mov ss,ax
        mov sp,16
        mov ax,1000
        call s
        mov ax,4c00h
        int 21h
        s:add ax,ax
        ret
code ends
end start


</code></pre>
<p>除法div指令</p>
<p>div是除法</p>
<p>格式 div 寄存器</p>
<p>div 内存单元</p>
<p>使用 div做除法时候</p>
<p>被除数 放在AX DX和AX中</p>
<p>除数8位16位,在寄存器或内存单元</p>
<h3 id="mull指令做乘法">mull指令做乘法</h3>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220311174735140-1452307151.png"></p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220311175018253-690954286.png"></p>
<p>计算 100*10</p>
<p>mov al,100</p>
<p>mov bl,10</p>
<p>mul bl</p>
<p>ax = 1000</p>
<p>计算 100*10000</p>
<p>分析100小于255 课10000大于255,所以必须做61乘法</p>
<p>mov ax,100</p>
<p>mov bx,10000</p>
<p>mul bx</p>
<p>结果 dx =000FH</p>
<p>ax = 4240h</p>
<p>F4240H = 1000000</p>
<h3 id="模块化程序设计">模块化程序设计</h3>
<p>调用子程序:call指令</p>
<p>返回:ret指令</p>
<p>子程序:根据提供的参数处理一定事务处理后,将返回值提供给调用者</p>
<p>参数和结果传递</p>
<p>方案</p>
<p>用寄存器</p>
<p>用内存单元</p>
<p>用栈</p>
<p>用寄存器传递参数</p>
<p>参数放到dx,bx = N</p>
<p>子程序中多个mul指令计算N^#</p>
<p>汇编子程序</p>
<pre><code>cube: mov ax,bx

mul bx

mul bx

ret
</code></pre>
<p>计算data段一组数据3次方</p>
<p>结果保存在后面dword中</p>
<pre><code class="language-assembly">assume cs:code
data segment
        dw 1,2,3,4,5,6,7,8
        dd 0,0,0,0,0,0,0,0
data ends
code segment
start:mov ax,data
        mov ds,ax
        mov si,0
        mov di,16
       
       
        mov cx,8
        S:mov bx,
        call cube
        mov ,ax
        mov .2,dx
        add si,2
        add di,4
        loop s
       
        mov ax,4c00h
        int 21h
        code ends
end start

</code></pre>
<p>用内存单元批量出阿迪数据</p>
<p>将批量数据放到内存汇总,然后将它们所在内存空间的首地址放在寄存器中,首地址放到寄存器中,传递哦给需要的子程序中</p>
<p>用栈传递参数</p>
<p>由调用者将需要传递给子程序的参数</p>
<p>压入栈中,子程序从栈中取得参数</p>
<p>进入子程序前,参数 a,b入栈</p>
<p>调用子程序,将使栈顶存放IP</p>
<p>结果 dx:ax = (a-b)^3</p>
<pre><code class="language-assembly">mov ax,1

push ax

mov ax,3

push ax

call difcube


diffcube: push bp
        mov bp,sp
        mov ax,
        sub ax,
        mov bp,ax
        mul bp
        mul bp
        pop bp
        ret 4
       
code ends
</code></pre>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220311192057189-927865808.png"></p>
<p>j寄存器冲突方案解决</p>
<p>在编写调用子程序冲突时有没有会产生冲突的寄存器</p>
<p>如果有,调用者会使用别的寄存器</p>
<p>在编写子程序时候,不要使用会产生冲突的寄存器</p>
<blockquote>
<p>但是这个方案不好</p>
</blockquote>
<p>子程序标准框架:子程序使用的寄存器入栈</p>
<p>子程序使用的寄存器出栈</p>
<p>返回(ret,retf)</p>
<p>在子程序开始,将要用到的所寄存器内容都保存起来,在子程序返回前再恢复</p>
<h3 id="标志寄存器">标志寄存器</h3>
<p>标志寄存器结构</p>
<p>flag寄存器是按位起作用,每一位都有专门的含义,记录特定信息</p>
<p>8086没有使用flag 1,3,5,12,13,14 这些位不具有任何含义</p>
<p>作用</p>
<p>用来存储相关指令某些结果</p>
<p>用来为cpu执行相关指令提供行为依据</p>
<p>用来控制CPU相关工作方式</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220311200340818-1623118336.png"></p>
<p>直接访问标志寄存器</p>
<p>pushf:将标志寄存器值压入栈</p>
<p>popf:从栈中弹出数据,送入标志寄存器</p>
<p>ZF零标志</p>
<p>ZF标记相关指令计算结果是否为0</p>
<p>ZF=1 表示结果是0 1表示逻辑真</p>
<p>ZF=0 结果不是0,0表示逻辑假</p>
<p>8086指令集中,有的指令执行是影响标志寄存器,多数是逻辑算数运算</p>
<p>有的指令执行对标志寄存器没有影响,比如mov,push,pop,多数是传送指令</p>
<p>使用一条指令时候,要注意这条指令的全部功能,包括执行结果对标记寄存的哪些标志位影响</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220311204346319-1468347508.png"></p>
<h4 id="pf奇偶标志">PF奇偶标志</h4>
<p>PF记录指令执行,结果所有二进制位1个数</p>
<p>1的个数是偶数 PF=1</p>
<p>1的个数是奇数 PF=0</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312081450863-1187775265.png"></p>
<h4 id="sf符号标志">SF符号标志</h4>
<p>结果是负 SF=1</p>
<p>不是负数 SF=0</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312081505099-1834203594.png"></p>
<h4 id="cf进位标志">CF进位标志</h4>
<p>在进行无符号数运算,CF记录了运算届法国最高有效位和更高位进位值</p>
<p>对于位数为N的无符号的数来说,对应的二进制信息最高位就是第N-1位,是最高的有效位</p>
<p>假想存在第N位,就是相对最高有效位的更高位</p>
<p>CF记录执行执行后CF=1</p>
<p>无进位或借位 CF=0</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312081520930-2134167227.png"></p>
<h4 id="of溢出标志">OF溢出标志</h4>
<p>在进行有符号数运算时候,如果超过了机器所能表示的范围叫溢出</p>
<p>CF和OF区别</p>
<p>CF是对无符号数运算有意义的进位标志位</p>
<p>OF是对有符号运算有意义的溢出标志位</p>
<p>mov al,0F0H</p>
<p>add al,88H</p>
<p>CF=1,OF=1 当无符号数运算有进位,当有符号运算溢出</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312082456126-1614153660.png"></p>
<p>adc带进位加法指令</p>
<p>格式 adc操作对象,操作对象2</p>
<p>功能操作对象1 = 操作对象1+操作对象2+CF</p>
<p>例 adc ax,bx 实现功能ax = ax+bx+CF</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312084452576-1202401329.png"></p>
<p>8086指令提供add指令,完成8位或16位加法</p>
<p>例子:计算·11EF000H +201000H 结果放在ax 高16位和 bx低16位</p>
<p>先将低16位相加,然后将16位进位值相加</p>
<p>mov ax,001EH</p>
<p>mov bx,0F000H</p>
<p>add bx,1000H</p>
<p>adc ax,0020H</p>
<p>例子 计算1E F000 1000H+20 1000 1EF0H</p>
<p>结果放在 ax 高16位 bx 次高16位 cx低16位</p>
<p>mov ax,001EH</p>
<p>mov bx,0F000H</p>
<p>mov cx,1000H</p>
<p>add cx,1EF0H</p>
<p>add bx,1000H</p>
<p>adc ax,0020H</p>
<p>128位数据相加</p>
<p>对128位数据相加</p>
<p>add128</p>
<p>两个逆序存放128数据相加</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312090055593-1386314290.png"></p>
<p>数据是128位,需要8个单元,由低地址单元到高地址单元,依次存放由低到高各个字</p>
<p>ds:si指向存储第一个数的内存空间</p>
<p>ds:di指向存储存储的第二个数内存空间</p>
<p>运算结果存储的第一个数的存储空间</p>
<h4 id="sbb指令">sbb指令</h4>
<p>带借位减法指令</p>
<p>格式 sbb操作对象,操作对象2</p>
<p>功能操作对象1 = 操作对象1-操作对象2-CF</p>
<p>与sub区别,利用CF位上记录的借位值</p>
<p>比如 sbb ax bx</p>
<p>实现功能 ax = ax-bx-CF</p>
<p>应用 对任意大的数据进行减法运算</p>
<p>计算</p>
<p>结果放在 ax,bx</p>
<p>mov bx,1000H</p>
<p>mov ax,003EH</p>
<p>sub bx,2000H</p>
<p>sbb ax,0020H</p>
<h3 id="cmp指令">cmp指令</h3>
<p>格式 cmp操作对象1,操作对象2</p>
<p>功能计算操作对象-操作对象2</p>
<p>cmp是比较指令功能相当于减法指令,知识不保存结果</p>
<p>cmp指令执行后,将对标志寄存器产生影响</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312095212649-1829954027.png"></p>
<p>无符号数比较标志位取值</p>
<p>通过cmp指令执行后相关标志位的值,可以看出结果</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312095527538-646423191.png"></p>
<p>比较指令设计思路,通过做减法运算影响标志寄存器,标志寄存器相关位取值,体现比较好的结果</p>
<p>有符号数比较与标志位取值</p>
<p>用cmp进行有符号数比较时,cpu哪些标志比较时</p>
<p>cmp ah,bh</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312095736000-2008067787.png"></p>
<p>仅凭结果正负SF无法得出结论,需要配合1是否溢出 得到结论</p>
<p>条件转移指令</p>
<p>根据单个标志位转移指令</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312105604582-560817527.png"></p>
<p>调价转移指令使用</p>
<p>j新系列指令cmp指令配合,构造条件转移指令</p>
<p>不必再考虑cmp指令对相关标志影响</p>
<p>可以直接考虑cmp指令配合</p>
<p>例子</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312110409876-640695643.png"></p>
<p>条件转移指令</p>
<p>可以根据某种条件,决定是否转移,程序执行流程</p>
<p>转移 = 修改ip</p>
<p>通过检测标志位,由标志体现调价</p>
<p>条件转移指令通常都和cmp相配合使用、</p>
<p>cmp指令改变标志位</p>
<p>双分支结结构实现</p>
<p>例子</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312113357223-1538327372.png"></p>
<p>初始设置ax=0,然后用循环依次比较每个字节的值,找到一个和8相等的数就将ax+1</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312113455403-361759407.png"></p>
<p>初始设置ax=0,然后循环依次比较每个字节的值,找到一个大于8的数就将ax+1</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312113547910-1778708888.png"></p>
<p>初始设置ax=0,然后循环依次比较每个字节,找到一个小于8的数就将ax+1</p>
<p>DF标志和串传送指令</p>
<p>在串处理指令中华,控制每次、操作后si,di增减</p>
<p>DF=0每次操作后si,di递增</p>
<p>DF=1,每次操作后si,di递减</p>
<p>对DF进行设置指令</p>
<p>cld指令 将标志旗寄存器DF为设为0</p>
<p>std指令,将标志寄存器DF设为1</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312120622220-1588821067.png"></p>
<h4 id="rep指令">rep指令</h4>
<p>rep指令常和串传送指令搭配使用</p>
<p>根据cx值,重复执行后面的指令</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312121016314-1725455274.png"></p>
<pre><code class="language-assembly">assume csd:code,ds:data
data segment
        db ''
        db ''
data ends
code segment
start:
        mov ax,data
        mov ds,ax
        mov si,0
        mov es,ax
        mov di,16
        cld
        mov cx,8
        rep movsw
       
        mov ax,4c00h
        int 21h
code ends
end start

       

</code></pre>
<p>例子,用串传送指令,将F000H中最后16个字符复制到data中</p>
<pre><code class="language-assembly">assume cs:code,ds:data
data segment
        db 16 dup (0)

data ends
code segment
start:
        mov ax,0f000h
        mov ds,ax
        mov si,0ffffh
        mov ax,data
        mov es,ax
        mov di,15
        mov cx,16
        std
        rep movsb
       
        mov ax,4c00h
        int 21h
       
code ends
end start

</code></pre>
<h1 id="中断和外部设备操作">中断和外部设备操作</h1>
<hr>
<p>移位指令 SHL OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125840284-1540564001.png"></p>
<p>逻辑右移 SHR OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125853410-961016464.png"></p>
<p>算数左移 SAL OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125900657-1099983110.png"></p>
<p>带进位循环左移 RCL OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125917700-1339624423.png"></p>
<p>循环左移ROL OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125926226-595120230.png"></p>
<p>循环右移 ROR OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125936506-1237717172.png"></p>
<p>算数右移 SAR OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125946823-417899572.png"></p>
<p>带进位循环右移 RCR OPR CNT</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312125955096-1643790980.png"></p>
<h3 id="逻辑移位指令shl-shr">逻辑移位指令shl shr</h3>
<p>SHL OPR CNT</p>
<p>将寄存器或内存单元中数据向左一维</p>
<p>将最后移出一位写入CF</p>
<p>移动位数大于1时必须用cl</p>
<p>mov al,010100001b</p>
<p>mov cl,3</p>
<p>shl al,cl</p>
<h3 id="操作显存数据">操作显存数据</h3>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312131333600-1958343161.png"></p>
<p>显示信息一种直接方式</p>
<pre><code class="language-assembly">
assume cs:codeseg,ds:datasg
datasg segment
        db 'welcome'
datasg ends

codeseg segment
start:
        mov ax,datasg
        mov ds,ax
        mov ax,0B800H
        mov es,ax
        mov si,0
        mov di,160*12+80-16
        mov cx,16
        w:mov al,
        mov es:,al
        inc di
        mov al,71H
        mov es:,al
        inc si
        inc di
        loop w
       
        mov ax,4c00h
        int 21h
codeseg ends
end start
       
       
</code></pre>
<h3 id="描述内存单元的标号">描述内存单元的标号</h3>
<p>代码段中标号可以来标记指令,段的起始地址</p>
<p>代码段中也可以用标号</p>
<p>在代码段中使用标号a,b后面没有“:”,它们描述内存地址和单元标号</p>
<p>标号a,地址code:0</p>
<p>以后内存单元都是字节</p>
<p>标号b</p>
<p>地址 code:8</p>
<p>以后内存单元都是字</p>
<p>数据标号</p>
<p>数据标号标记了存储数据的单元的地址和长度</p>
<p>数据标号不同于仅仅表示地址的地址标号</p>
<h3 id="数据的直接定址表">数据的直接定址表</h3>
<p>以十六进制形式在屏幕中间显示给定的byte数据</p>
<p>先将一个byte高4位和低4位分开,显示对应的数码字符</p>
<p>如果数值显示是0,则显示“0”</p>
<p>如果数值是1,则显示1</p>
<p>数值是11 显示B</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312161842960-885057921.png"></p>
<p>建立一张表,表中依次存储字符 0<sub>F,通过数值0</sub> ~15直接查找对应的字符</p>
<p><img src="https://img2022.cnblogs.com/blog/2185037/202203/2185037-20220312171249851-378862245.png"></p>
<pre><code class="language-assembly">assume cd:code
code segment
start:
        mov al,2Bh
        call showbyte
        mov ax,4c00h
        int 21h
        showbyte:
                jmp short show
                table db '0123456AVCDEF'
        show:
        push bx
        push es
        push cx
        mov ah,al
        mov cl,4
        shr ah,cl
        and al,00001111b
       
        mov bl,ah
        mov bh,0
        mov ah,table
        mov bx,0b800h
        mov es,bx
        mov es:,ah
       
        mov bl,al
        mov bh,0
        mov al,table
        mov es:,al
       
       
        pop cx
        pop cs
        pop bx
        ret
       
code ends
end start

</code></pre>
<p>利用表在两个数据几个之间简历哦一种映射关系,用查表方法根据得到在另一个集合中对应的数据</p><br><br>
来源:https://www.cnblogs.com/abldh12/p/15973812.html
頁: [1]
查看完整版本: 汇编语言设计