格鲁吉亚军少 發表於 2023-9-21 19:17:00

汇编语言学习笔记

<h1 id="汇编语言">汇编语言</h1>
<p>主要知识点来自《汇编语言》速成指南(全程敲代码),配套材料:</p>
<ul>
<li>王爽老师的《汇编语言》</li>
<li>使用DOSbox模拟运行8086CPU汇编语言</li>
</ul>
<hr>
<h1 id="1-入门">1. 入门</h1>
<p>简单引入关于8086CPU的知识。</p>
<p>CPU内部主要由运算器、控制器、寄存器三大部分组成<sup class="footnote-ref"></sup>。<br>
运算器: 负责算术运算(+ - * / 基本运算和附加运算)和逻辑运算(包括 移位、逻辑测试或比较两个值等)。<br>
控制器: 负责应对所有的信息情况,调度运算器把计算做好。<br>
寄存器: 它们可用来暂存指令、数据和地址。既要对接控制器的命令,传达命令给运算器;还要帮运算器记录处理完或者将要处理的数据。</p>
<p>8086 CPU 中寄存器总共为 14 个,且均为 16 位<sup class="footnote-ref"></sup>。它们分为:</p>
<p><strong>通用寄存器</strong></p>
<ul>
<li>
<p>AX,BX,CX,DX称作为数据寄存器:</p>
<ul>
<li>AX (Accumulator):累加寄存器,也称之为累加器;</li>
<li>BX (Base):基地址寄存器;</li>
<li>CX (Count):计数器寄存器;</li>
<li>DX (Data):数据寄存器;</li>
</ul>
</li>
<li>
<p>SP和BP又称作为指针寄存器:</p>
<ul>
<li>SP (Stack Pointer):堆栈指针寄存器;</li>
<li>BP (Base Pointer):基指针寄存器;</li>
</ul>
</li>
<li>
<p>SI和DI又称作为变址寄存器:</p>
<ul>
<li>SI (Source Index):源变址寄存器;</li>
<li>DI (Destination Index):目的变址寄存器;</li>
</ul>
</li>
</ul>
<p><strong>控制寄存器:</strong></p>
<ul>
<li>IP (Instruction Pointer):指令指针寄存器;</li>
<li>FLAG:标志寄存器;</li>
</ul>
<p><strong>段寄存器:</strong></p>
<ul>
<li>CS (Code Segment):代码段寄存器;</li>
<li>DS (Data Segment):数据段寄存器;</li>
<li>SS (Stack Segment):堆栈段寄存器;</li>
<li>ES (Extra Segment):附加段寄存器;</li>
</ul>
<p>其中,通用寄存器和段寄存器是我们最常用的寄存器。8086CPU可以使用H和L来表示寄存器的高8位和低8位,如AX寄存器的高8位为AH,低8位为AL。但是这只针对于AX、BX、CX、DX这四个寄存器,其他的寄存器没有这样的表示方法。</p>
<p>挂载DOSBox的C盘到本地的<code>D:\masm</code>目录</p>
<pre><code class="language-asm">mount c d:\masm\
</code></pre>
<p>切换到C盘</p>
<pre><code class="language-asm">c:
</code></pre>
<p>查看文件</p>
<pre><code class="language-asm">dir
</code></pre>
<h2 id="11-使用debug">1.1 使用Debug</h2>
<pre><code class="language-asm">debug
</code></pre>
<p><strong>R D E U T A</strong></p>
<ul>
<li>R: 显示或更改寄存器的内容</li>
<li>D: 显示内存内容</li>
<li>E: 修改内存内容</li>
<li>U: 汇编指令,将机器码转换为汇编指令</li>
<li>T: 单步执行</li>
<li>A: 以汇编指令的格式显示内存内容</li>
</ul>
<blockquote>
<p>记忆方法:ture ad<br>
R: read<br>
D: display<br>
E: edit<br>
U: unassemble<br>
T: trace<br>
A: assemble</p>
</blockquote>
<h3 id="r-表示显示或更改寄存器的内容">R 表示显示或更改寄存器的内容</h3>
<p>显示:</p>
<pre><code class="language-asm">R
</code></pre>
<p>更改</p>
<pre><code class="language-asm">R [寄存器名]
</code></pre>
<h3 id="d-表示显示内存内容">D 表示显示内存内容</h3>
<pre><code class="language-asm">D
</code></pre>
<p>D指令默认显示内存中的128个字节,从0000开始,每行显示16个字节,每个字节用两个十六进制数表示,每行的最后显示这16个字节对应的ASCII码。</p>
<pre><code class="language-asm">D [地址]
</code></pre>
<pre><code class="language-asm">D [地址] [长度]
</code></pre>
<h3 id="e-表示修改内存内容修改内存中的数据">E 表示修改内存内容,修改内存中的数据</h3>
<pre><code class="language-asm">E [地址] [数据]
</code></pre>
<p>连续修改内存中的数据,从某个地址开始,修改多个数据</p>
<pre><code class="language-asm">E [地址]
</code></pre>
<h3 id="a-表示以汇编指令的格式在内存中写入指令">A 表示以汇编指令的格式在内存中写入指令</h3>
<pre><code class="language-asm">A [地址]
</code></pre>
<h3 id="t-表示单步执行搭配a使用">T 表示单步执行(搭配A使用)</h3>
<pre><code class="language-asm">T
</code></pre>
<h3 id="u-表示将机器码转换为汇编指令">U 表示将机器码转换为汇编指令</h3>
<pre><code class="language-asm">U [地址]
</code></pre>
<h2 id="12-常用指令">1.2 常用指令</h2>
<h3 id="mov-add-sub">mov add sub</h3>
<p><strong><code>mov</code>: move,移动,赋值</strong></p>
<pre><code class="language-asm">mov ax, 1
</code></pre>
<p>表示将1赋值给ax寄存器,然后ax寄存器的值为1</p>
<pre><code class="language-asm">mov ax, bx
</code></pre>
<p>表示将bx寄存器的值赋值给ax寄存器</p>
<p><strong><code>add</code>: 加法</strong></p>
<pre><code class="language-asm">add ax, 1
</code></pre>
<p>表示将ax寄存器的值加1,然后将结果赋值给ax寄存器</p>
<pre><code class="language-asm">add ax, bx
</code></pre>
<p>表示将ax寄存器的值加bx寄存器的值,然后将结果赋值给ax寄存器</p>
<p><strong><code>sub</code>: 减法</strong></p>
<pre><code class="language-asm">sub ax, 1
</code></pre>
<p>表示将ax寄存器的值减1,然后将结果赋值给ax寄存器</p>
<pre><code class="language-asm">sub ax, bx
</code></pre>
<p>表示将ax寄存器的值减bx寄存器的值,然后将结果赋值给ax寄存器</p>
<h3 id="mul-div-and-or乘除指令及所有寄存器英文名">mul div and or乘除指令及所有寄存器英文名</h3>
<p><strong><code>mul</code>: 乘法</strong></p>
<pre><code class="language-asm">mul [源操作数]
</code></pre>
<p><code>mul</code>指令进行乘法运算有两种形式:</p>
<p>若操作数为8位,则乘数在AL中,被乘数在指定的源操作数中,乘积在AX中。</p>
<p></p><div class="math display">\ = AX
\]</div><p></p><p>若操作数为16位,则乘数在AX中,被乘数在指定的源操作数中,乘积在<code>DX:AX</code>中。</p>
<p></p><div class="math display">\ = DX:AX
\]</div><p></p><pre><code class="language-asm">mul [源操作数]
</code></pre>
<p><strong><code>div</code>: 除法</strong></p>
<pre><code class="language-asm">div [源操作数]
</code></pre>
<p><code>div</code>指令进行除法运算有两种形式:</p>
<p>除数为8位,被除数为16位,默认在AX中存放。结果的商在AL中,余数在AH中。</p>
<p></p><div class="math display">\ = AL \ldots\ldots AH
\]</div><p></p><p>除数为16位,被除数为32位,默认在DX和AX中存放,DX存放高16位,AX存放低16位。结果的商在AX中,余数在DX中。</p>
<p></p><div class="math display">\ = AX \ldots\ldots DX
\]</div><p></p><p><strong><code>and</code>: 与运算</strong></p>
<pre><code class="language-asm">and [目的操作数], [源操作数]
</code></pre>
<p><strong><code>or</code>:或运算</strong></p>
<pre><code class="language-asm">or [目的操作数], [源操作数]
</code></pre>
<h3 id="shl-shr-inc-dec-xchg-neg指令中断int指令">shl shr inc dec xchg neg指令,中断int指令</h3>
<p><code>shl</code>: 逻辑左移</p>
<pre><code class="language-asm">shl [目的操作数], [移动位数]
</code></pre>
<p>表示将目的操作数的值左移移动位数位,然后将结果赋值给目的操作数。<br>
逻辑左移:左移后,右边补0</p>
<p><code>shr</code>: 逻辑右移</p>
<pre><code class="language-asm">shr [目的操作数], [移动位数]
</code></pre>
<p>表示将目的操作数的值右移移动位数位,然后将结果赋值给目的操作数。<br>
逻辑右移:右移后,左边补0</p>
<blockquote>
<p>除了逻辑左移和逻辑右移,还有算术左移和算术右移,算术左移和算术右移的区别在于,算术左移和算术右移在移动位数位的时候,左边补的是0,右边补的是符号位。<br>
指令:<code>sal</code> <code>sar</code></p>
<p>此外,还有循环左移和循环右移,循环左移和循环右移在移动位数位的时候,左边或者右边补的是移动前的最后一位。<br>
指令:<code>rol</code> <code>ror</code></p>
</blockquote>
<p><code>inc</code>: 自增</p>
<pre><code class="language-asm">inc [目的操作数]
</code></pre>
<p>表示将目的操作数的值加1,然后将结果赋值给目的操作数。</p>
<p><code>dec</code>: 自减</p>
<pre><code class="language-asm">dec [目的操作数]
</code></pre>
<p>表示将目的操作数的值减1,然后将结果赋值给目的操作数。</p>
<p><code>xchg</code>: 交换</p>
<pre><code class="language-asm">xchg [操作数1], [操作数2]
</code></pre>
<p>表示将操作数1和操作数2的值进行交换。</p>
<p><code>neg</code>: 取反</p>
<pre><code class="language-asm">neg [目的操作数]
</code></pre>
<p>表示将目的操作数的值取反,然后将结果赋值给目的操作数。</p>
<p><code>int</code>: 中断</p>
<pre><code class="language-asm">int [中断号]
</code></pre>
<p>表示执行中断号对应的中断程序。</p>
<blockquote>
<p>比如:<code>int 0</code>表示执行中断号为0的中断程序。在使用<code>div</code>指令的时候,如果除数为0,就会触发中断号为0的中断程序。<br>
只要进入中断程序,就会跳转到中断程序的入口地址,然后执行中断程序的代码,执行完中断程序的代码后,再跳转回来继续执行中断指令后面的代码。</p>
</blockquote>
<h1 id="2-基础知识">2 基础知识</h1>
<h2 id="ds寄存器段地址偏移地址物理地址">ds寄存器,段地址,偏移地址,物理地址</h2>
<p></p><div class="math display">\[段地址 \times 16 + 偏移地址 = 物理地址
\]</div><p></p><p>一个物理地址可以通过段地址和偏移地址表示。且表示不是唯一的,比如:</p>
<pre><code class="language-asm">物理地址      段地址      偏移地址
21F60H      2000H      1F60H
            2100H      0F60H
            21F0H      0060H
            21F6H      0000H
            1F00H      2F60H
</code></pre>
<p><strong>DS:数据段寄存器</strong></p>
<p>DS寄存器存放的是段地址,偏移地址存放在指令中。</p>
<pre><code class="language-asm">r ds
</code></pre>
<p>然后将DS寄存器的值修改为段地址</p>
<pre><code class="language-asm">mov ax, [偏移地址]
</code></pre>
<p>通过上述操作就可以将某一物理地址里的数据赋值给ax寄存器。</p>
<blockquote>
<p>这里可能会涉及到大小端存储的问题<br>
首先解释一下大小端存储:<br>
大端存储:高位字节存放在低位地址,低位字节存放在高位地址<br>
小端存储:高位字节存放在高位地址,低位字节存放在低位地址</p>
<p>高位字节是指在一个字节中,数值高的那部分。低位字节是指在一个字节中,数值低的那部分。比如:<code>0x1234</code>,高位字节就是<code>0x12</code>,低位字节就是<code>0x34</code>。</p>
<p>当我们在内存里从低地址向高地址扫描时,如果先扫描到的是高位字节,那么就是大端存储,如果先扫描到的是低位字节,那么就是小端存储。<br>
<strong>8086CPU是小端存储</strong>,所以在使用<code>mov</code>指令的时候,需要注意大小端存储的问题。</p>
</blockquote>
<p><strong>注意DS不能<code>mov</code>赋值立即数,只能<code>mov</code>赋值寄存器。</strong></p>
<p>举例:</p>
<pre><code class="language-asm">mov ax, 21F0
mov ds, ax
mov al,
</code></pre>
<p>表示将21F60H这个物理地址里的数据赋值给ax寄存器的低8位。</p>
<h2 id="csip寄存器">CS,IP寄存器</h2>
<p>CS : 代码段寄存器;IP : 指令指针寄存器。在8086机中,任意时刻,CPU将CS:IP指向的内容当作指令来执行<sup class="footnote-ref"></sup>。</p>
<p>CPU执行指令的过程<sup class="footnote-ref"></sup>:</p>
<ol>
<li>CPU从CS:IP指向的地址读取指令,读取的指令进入到指令缓冲器中;</li>
<li>IP = IP + 所读取的指令长度,从而指向下一条指令;</li>
<li>执行指令。转到步骤 1,重复这个过程。</li>
</ol>
<p><strong>由此我们可以看出,代码存储在内存中,CPU靠CS:IP来指向代码的位置,然后执行代码。代码也是数据,只不过是CPU执行的数据。所以代码也可以进行对应的运算,比如加法、减法等。</strong></p>
<h2 id="jmp指令">jmp指令</h2>
<p><code>jmp</code>指令用于无条件跳转,跳转到指定的地址。</p>
<p><code>jmp</code>指令有两种形式:</p>
<pre><code class="language-asm">jmp [目的操作数]
</code></pre>
<p>这种形式的<code>jmp</code>指令,目的操作数是一个寄存器,表示将寄存器的值赋值给IP寄存器,然后CPU执行下一条指令的时候,就会从IP寄存器指向的地址开始执行。</p>
<pre><code class="language-asm">jmp [段地址]:[偏移地址]
</code></pre>
<p>这种形式的<code>jmp</code>指令,目的操作数是一个物理地址,段地址和偏移地址都是直接地址。表示将段地址赋值给CS寄存器,将偏移地址赋值给IP寄存器,然后CPU执行下一条指令的时候,就会从CS:IP指向的地址开始执行。</p>
<h2 id="sssp-寄存器">SS,SP 寄存器</h2>
<p>SS: 堆栈段寄存器;SP: 堆栈指针寄存器。在8086机中,任意时刻,CPU将SS:SP指向的内容当作堆栈来使用。</p>
<p>SS:SP指向的内容是堆栈的栈顶,堆栈的栈顶是指堆栈中最后一个数据的地址。堆栈是一种特殊的数据结构,它是一种先进后出的数据结构。</p>
<p>入栈:</p>
<pre><code class="language-asm">push [源操作数]
</code></pre>
<p>表示将源操作数的值入栈,然后将SP寄存器的值减2,然后将结果赋值给SP寄存器。</p>
<p>出栈:</p>
<pre><code class="language-asm">pop [目的操作数]
</code></pre>
<p>表示将栈顶的值出栈,然后将SP寄存器的值加2,然后将结果赋值给SP寄存器。</p>
<p>需要注意的是,数据以字为单位入栈和出栈,在内存中的存储方式是小端存储,所以在入栈和出栈的时候,需要注意大小端存储的问题。</p>
<p>越界问题:</p>
<p>计算机对于出栈和入栈的操作,是不会检查栈的大小的,所以在使用栈的时候,需要注意栈的大小,防止越界。</p>
<hr>
<hr>
<p>参考与注释:</p>
<hr class="footnotes-sep">
<section class="footnotes">
<ol class="footnotes-list">
<li id="fn1" class="footnote-item"><p>CPU运算器、控制器、寄存器_CSDN ↩︎</p>
</li>
<li id="fn2" class="footnote-item"><p>x86汇编之——8086寄存器讲解_知乎 ↩︎</p>
</li>
<li id="fn3" class="footnote-item"><p>CS和IP_知乎 ↩︎ ↩︎</p>
</li>
</ol>
</section><br><br>
来源:https://www.cnblogs.com/BryceAi/p/17720742.html
頁: [1]
查看完整版本: 汇编语言学习笔记