【计算机组成原理】考纲第四章 MIPS指令系统及汇编语言
<blockquote><p><strong>(四)、MIPS指令系统及汇编语言</strong></p>
<p>(1)指令系统的基本知识(指令格式、寻址方式)</p>
<p>(2)MIPS汇编语言</p>
</blockquote>
<h3 id="41-指令系统的基本知识">4.1 指令系统的基本知识</h3>
<h4 id="411-指令系统概述">4.1.1 指令系统概述</h4>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020174502093.png" alt="image-20201020174502093" loading="lazy"></p>
<h4 id="412-指令格式">4.1.2 指令格式</h4>
<p><strong>机器指令</strong>是计算机硬件可以执行的、表示一种基本操作的<strong>二进制代码</strong>。</p>
<ul>
<li>
<p>指令格式:<strong>操作码 + 操作数(操作数地址)</strong></p>
<ul>
<li>操作码:指明指令的操作性质</li>
<li>操作数:指明操作数的位置(或操作数本身)</li>
</ul>
</li>
<li>
<p>指令表示:</p>
<ul>
<li>机器表示:二级制代码</li>
<li>符号化表示:助记符,如 <code>MOV AX, BX</code></li>
</ul>
</li>
<li>
<p>操作码结构:</p>
<ul>
<li>
<p>固定长度操作码:操作码长度固定不变</p>
<p>①硬件设计简单;②指令译码时间开销较小;③指令空间效率较低</p>
</li>
<li>
<p>可变长度操作码:操作码长度随指令地址数目的不同而不同</p>
<p>①硬件设计复杂;②指令译码时间开销较大;③指令空间利用率较高</p>
</li>
</ul>
</li>
<li>
<p>指令长度:</p>
<ul>
<li>定长指令系统:如MIPS指令</li>
<li>变长指令系统:一般为字节的整数倍,如X86指令</li>
</ul>
</li>
</ul>
<h4 id="413-寻址方式">4.1.3 寻址方式</h4>
<h5 id="1-什么是寻址">(1) 什么是寻址?</h5>
<p>寻址方式 就是根据 <u>形式地址</u> 计算出操作数 <u>有效地址</u> 的方法。</p>
<ul>
<li>形式地址:指令给出直接的操作数的地址编码</li>
<li>有效地址:操作数实际在内存中存储的地址</li>
</ul>
<h5 id="2-怎么寻址">(2) 怎么寻址?</h5>
<ol>
<li>
<p>寻址方式的确定</p>
<ul>
<li>
<p><strong>在操作码中</strong>给定寻址方式:如 MIPS 指令,指令中仅有一个主(虚)存地址的,且指令中仅有几种寻址方式。 Load/store 型机器指令属于这种情况。</p>
</li>
<li>
<p>指令中专门的<strong>寻址方式位</strong>:如 X86 指令,指令中有多个操作数,且寻址方式各不相同,需要各自说明寻址方式。</p>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202223602.png" alt="image-20201020202223602" loading="lazy"></p>
</li>
</ul>
</li>
<li>
<p>按照指定方式进行寻址</p>
<table>
<thead>
<tr>
<th style="text-align: center">寻址方式</th>
<th style="text-align: center">示意图</th>
<th style="text-align: center">说明</th>
<th style="text-align: center">操作数位置</th>
<th style="text-align: center">访问操作数所需访存次数</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center">立即寻址</td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202344630.png" alt="image-20201020202344630" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202708944.png" alt="image-20201020202708944" loading="lazy"></td>
<td style="text-align: center">指令中</td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">寄存器直接寻址</td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202355521.png" alt="image-20201020202355521" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202721699.png" alt="image-20201020202721699" loading="lazy"></td>
<td style="text-align: center">寄存器</td>
<td style="text-align: center">0</td>
</tr>
<tr>
<td style="text-align: center">寄存器间接寻址</td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202407657.png" alt="image-20201020202407657" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202733645.png" alt="image-20201020202733645" loading="lazy"></td>
<td style="text-align: center">存储器</td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">基址寻址</td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202416157.png" alt="image-20201020202416157" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202746061.png" alt="image-20201020202746061" loading="lazy"></td>
<td style="text-align: center">存储器</td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">变址寻址</td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202516488.png" alt="image-20201020202516488" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202755018.png" alt="image-20201020202755018" loading="lazy"></td>
<td style="text-align: center">存储器</td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">相对寻址</td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202523635.png" alt="image-20201020202523635" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202807345.png" alt="image-20201020202807345" loading="lazy"></td>
<td style="text-align: center">存储器</td>
<td style="text-align: center">1</td>
</tr>
<tr>
<td style="text-align: center">堆栈寻址</td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020203015603.png" alt="image-20201020203015603" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020202846921.png" alt="image-20201020202846921" loading="lazy"></td>
<td style="text-align: center">存储器</td>
<td style="text-align: center">1</td>
</tr>
</tbody>
</table>
</li>
</ol>
<h3 id="42-mips汇编语言"><font color="red">4.2 MIPS汇编语言</font></h3>
<h4 id="421-mips指令系统">4.2.1 MIPS指令系统</h4>
<h5 id="1-mips-r2000r3000-寄存器结构">(1) MIPS R2000/R3000 寄存器结构</h5>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020211446917.png" alt="image-20201020211446917" loading="lazy"></p>
<h5 id="2-mips-寄存器使用约定">(2) MIPS 寄存器使用约定</h5>
<ul>
<li>为了保持硬件的简单,汇编语言不使用变量,操作数都是寄存器(registers),寄存器<strong>没有数值类型</strong>(与C语言等高级语言不同)</li>
<li>MIPS有 <strong>32</strong> 个寄存器,每个寄存器存放数据的宽度为 <strong>32</strong> bits(1个字)</li>
</ul>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020211604562.png" alt="image-20201020211604562" loading="lazy"></p>
<h5 id="3-mips-指令格式">(3) MIPS 指令格式</h5>
<ul>
<li>
<p>MIPS 只有<strong>3种指令格式</strong>,<strong>32位固定长度</strong>指令格式</p>
<p>原因:冯·诺伊曼计算机建立在两个原则之上——①指令与数值的表示形式一致;②全部程序可以被存储在内存中,像数据一样被读写。为了简化计算机系统的软/硬件,使得适用于数据操作的内存技术完全适用于指令操作,MIPS将指令也按照数据相同的”按字存储“的方式存储,即一条指令占32位,同时将32位划分为不同的字段,每一个字段提供指令的一部分信息,不同的划分方式形成了不同的指令格式——R、I、J,详细见下表。</p>
</li>
<li>
<p>MIPS 指令格式一览表</p>
<table>
<thead>
<tr>
<th style="text-align: center">R (Register )</th>
<th style="text-align: center">I (Immediate)</th>
<th style="text-align: center">J(Jump)</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020212525677.png" alt="image-20201020212525677" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020212612818.png" alt="image-20201020212612818" loading="lazy"></td>
<td style="text-align: center"><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020212700024.png" alt="image-20201020212700024" loading="lazy"></td>
</tr>
<tr>
<td style="text-align: center"><u>opcode</u>:与 <u>func</u> 组合起来,决定该条指令名(操作符),<u>opcode</u>等于0时代表所有R类型指令;<br><u>rs</u> (Source Register):通常指定存放第一个操作数;<br><u>rt</u> (Target Register):通常指定存放第二个操作数;<br><u>rd</u> (Destination Register):通常指定存放计算结果的寄存器;<br><u>shamt</u>:存储执行移位运算时要移的位数,该字段在不进行移位的指令中通常置0</td>
<td style="text-align: center"><u>opcode</u> : I类型指令中<u>opcode</u>可以唯一确定一条指令;<br><u>rs</u>:表示唯一的操作数寄存器;<br><u>rt</u>:指定存储计算结果的寄存器;<br><u>立即数</u>:16bits,可表示$2^{16}$ 个不同的整数值,在 <code>addi</code>, <code>slti</code>, <code>sltiu</code> 等指令中中立即数通过位扩展(符号扩展)方式扩展到32位</td>
<td style="text-align: center"><code>opcode</code>:与前两者一致;<br><u>target address</u>:26bits,利用字对齐,可以表示出32-bit地址的<strong>28</strong>位</td>
</tr>
<tr>
<td style="text-align: center"><code>add</code>, <code>addu</code>, <code>sub</code>, <code>subu</code>, <code>and</code>, <code>or</code>, <code>jr</code></td>
<td style="text-align: center"><code>addi</code>, <code>ori</code>, <code>lui</code>, <code>lw</code>, <code>sw</code>, <code>beq</code>, <code>bnq</code></td>
<td style="text-align: center"><code>j</code>, <code>jal</code></td>
</tr>
<tr>
<td style="text-align: center">说明:5bits指定寄存器位置正好可以对应32 ($2^5$)个寄存器</td>
<td style="text-align: center">说明:如果立即数超过16bits所能表示的范围,可以借助 <code>lui</code> 指令——将一个16bit的立即数存入寄存器的高16位,并将寄存器的低16位置 0</td>
<td style="text-align: center">$New PC = {\PC,Addr,00\}$</td>
</tr>
</tbody>
</table>
</li>
</ul>
<h5 id="4-mips-指令语法">(4) MIPS 指令语法</h5>
<ul>
<li>注意 “指令语法” 与 “指令格式” 的不同:<font color="red">指令语法是程序员书写的语法规范</font>,语法是固定的,用过约定好的规则使硬件实现更简单;而<font color="red">指令格式是指机器码(二进制代码)的格式</font></li>
<li>指令语法:<code>操作符, 目标, 源1, 源2</code>
<ul>
<li>操作符:指明操作的名称</li>
<li>目标:存放操作的结果</li>
<li>源1 (2):第一 (二) 个操作数</li>
</ul>
</li>
</ul>
<h5 id="5-mips-寻址方式">(5) MIPS 寻址方式</h5>
<ul>
<li>
<p>MIPS <strong>没有间接寻址</strong>,且Load/Store指令采用单一寻址模式(基址寻址)</p>
</li>
<li>
<p>MIPS 寻址方式</p>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201020213211600.png" alt="image-20201020213211600" loading="lazy"></p>
</li>
</ul>
<h5 id="6-mips-指令介绍">(6) MIPS 指令介绍</h5>
<ul>
<li>
<p>Load / Store 指令(取数/存储指令)</p>
<ul>
<li>
<p>格式:<strong>I</strong> 类型指令</p>
</li>
<li>
<p>分类:①取数指令:<code>LB </code>(取字节),<code>LBU </code>(取不带符号字节),<code>LH</code> (取半字), <code>LHU </code>(取不带符号的半字),<code>LW </code>(取字),<code>LWL</code>,<code>LWR </code> 等;②<strong><strong>存储指令</strong></strong>:<code>SB </code>(存字节),<code>SH </code>(存半字),<code>SW </code>(存字), <code>SWL </code>,<code>SWR</code> 等</p>
</li>
</ul>
</li>
<li>
<p>运算指令</p>
<ul>
<li>格式:<strong>R</strong> 类型指令, <strong>I</strong> 类型指令</li>
<li>分类:①算数运算:<code>add</code>, <code>addu</code>, <code>addi</code>, <code>addiu</code>, <code>sub</code>, <code>subu</code>, <code>mul</code>, <code>mulu</code>, <code>div</code>, <code>divu</code>, <code>mfhi</code>, <code>mflo</code>等;②逻辑运算:<code>and</code>, <code>andi</code>, <code>or</code>, <code>ori</code>, <code>xor</code>, <code>xori</code>, <code>nor</code>等;③移位运算:<code>sll</code>, <code>srl</code>, <code>sra</code>, <code>sllv</code>, <code>srlv</code>, <code>srav</code> 等</li>
</ul>
</li>
<li>
<p>跳转指令</p>
<ul>
<li>格式:<strong>J</strong> 类型指令,<strong>R</strong> 类型指令</li>
<li>分类:<code>j</code>, <code>jal</code>, <code>jr</code>, <code>jalr</code></li>
</ul>
</li>
<li>
<p>转移指令</p>
<ul>
<li>格式:<strong>I</strong> 类型指令</li>
<li>分类:<code>beq</code>(相等转移), <code>bne</code>(不等转移), <code>blez</code>(小于或等于0转移), <code>bgtz</code>(大于0转移), <code>bltz</code>(小于0转移), <code>bltzal</code>, <code>bgezal</code> 等</li>
</ul>
</li>
<li>
<p>特殊指令</p>
<ul>
<li>格式:<strong>R</strong> 类型指令</li>
<li>分类:<code>syscall</code>(系统调用), <code>break</code>(断点)</li>
</ul>
</li>
<li>
<details>
<summary>R类型指令编码示例</summary>
<pre><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201021113502626.png"><hr><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201021113400955.png"><text></text></pre></details>
</li>
<li>
<details>
<summary>使用MIPS实现swap递归函数</summary>
<pre><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201021113904723.png"></pre></details>
</li>
</ul>
<h4 id="422-mips汇编语言详解">4.2.2 MIPS汇编语言详解</h4>
<h5 id="1-mips的数据通路与内存布局">(1) MIPS的数据通路与内存布局</h5>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201021120837190.png" alt="image-20201021120837190" loading="lazy"></p>
<h5 id="2-mips的寄存器传送的控制逻辑">(2) MIPS的寄存器传送的控制逻辑</h5>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201021115826816.png" alt="image-20201021115826816" loading="lazy"></p>
<h5 id="3-mips-汇编语言中的-算术逻辑移位运算">(3) MIPS 汇编语言中的 算术、逻辑、移位运算</h5>
<ul>
<li>
<p>整数加减法</p>
<ul>
<li>加法:<code>add $s0, $s1, $s2</code></li>
<li>减法:<code>sub $s3, $s4, $s5</code></li>
</ul>
</li>
<li>
<p>0号寄存器、立即数</p>
<ul>
<li>
<p>0号寄存器:定义 <code>$0</code> 为数字 <code>0</code> ,也可写作 <code>$zero</code></p>
</li>
<li>
<p>立即数:即数值常量,MIPS针对立即数设置了专门指令,如 <code>addi $s0, $s1, 10</code></p>
<p>需要注意的是,<font color="red">MIPS没有立即数的减法</font>,可以用立即数加实现</p>
</li>
</ul>
</li>
<li>
<p>算数运算的溢出</p>
<ul>
<li>可检测出溢出异常的指令:<code>add</code>, <code>sub</code>, <code>addi</code></li>
<li>不会检测出溢出异常的指令:<code>addu</code>, <code>subu</code>, <code>addiu</code></li>
<li>MIPS中的C编译器会使用 <code>addu</code>, <code>addiu</code>, <code>subu</code>,即不检查溢出异常</li>
</ul>
</li>
<li>
<p>移位运算</p>
<ul>
<li>逻辑左移 <code>sll</code>:左移并且<strong>补0</strong>,位移量为立即数</li>
<li>逻辑右移 <code>srl</code>:右移并且<strong>补0</strong>,位移量为立即数</li>
<li>算数右移 <code>sra</code>:右移并且<strong>在空位做符号扩展填充</strong>,位移量为立即数</li>
<li><code>sllv</code>, <code>srlv</code>, <code>srav</code>:移位量存储在寄存器中,处理方式与立即数位移量类似</li>
</ul>
</li>
</ul>
<h5 id="4-mips-汇编语言中的-数据存取">(4) MIPS 汇编语言中的 数据存取</h5>
<ul>
<li>
<p>MIPS算术指令只能操作寄存器,不能直接操作内存,需要<strong>通过数据存取指令在内存与寄存器之间传输数据</strong></p>
</li>
<li>
<p>数据存取指令</p>
<ul>
<li>
<p>内存到寄存器:<code>lw</code></p>
<p><code>lw $t0, 12($s0)</code> :<code>$s0</code>称为<u>基址寄存器</u>,<code>12</code>称为<u>偏移量</u></p>
</li>
<li>
<p>寄存器到内存:<code>sw</code></p>
<p><code>sw $t0, 12($s0)</code> :与 <code>lw</code> 相同</p>
</li>
</ul>
</li>
<li>
<p>字节的存取指令</p>
<ul>
<li><code>lb $s0, 3($s1)</code> :把内存中的某个地址(<code>3 + s1中的地址值</code>)所存储的数值拷贝到<code>s0</code>的低地址字节上(其余24位使用符号扩展)</li>
<li><code>sb $s0, 3($s1)</code> :把<code>s0</code>的低地址字节所存储的数值存储到内存中的某个地址(<code>3 + s1中的地址值</code>)上</li>
</ul>
</li>
<li>
<p>寻址:现代计算机按字节编址,32-bit 字地址按 <strong>4</strong> 递增</p>
</li>
<li>
<p>字对齐:对象的起始位置一定要是字长的整数倍,故MIPS中<code>lw</code>和<code>sw</code>指令的基址寄存器的值与偏移量都应该是4的倍数</p>
</li>
</ul>
<h5 id="5-mips汇编中的-分支循环">(5) MIPS汇编中的 分支、循环</h5>
<ul>
<li>
<p>条件分支指令</p>
<ul>
<li><code>beq $1, $2, Label1</code>:相当于C语言中的 <code>if($1==$2) goto L1;</code></li>
<li><code>bne $1, $2, Label1</code>:相当于C语言中的 <code>if($1!=$2) goto L2;</code></li>
</ul>
</li>
<li>
<p>无条件分支指令</p>
<ul>
<li><code>j Label</code>:无条件跳转到标签label所在的代码,相当于 <code>goto Label</code></li>
</ul>
</li>
<li>
<p>不等式指令</p>
<ul>
<li><code>slt $1, $2, $3</code>:相当于 <code>$1 = ($2 < $3) ? 1 : 0;</code></li>
<li><code>slti</code>:<code>slt</code>的立即数版本</li>
</ul>
</li>
</ul>
<h5 id="6-一些小练习">(6) 一些小练习</h5>
<ul>
<li>
<p>IF - ELSE 语句</p>
<pre><code class="language-c">// C语言原代码
if (i == j) f = g + h;
else f = g - h;
</code></pre>
<pre><code class="language-assembly"># MIPS汇编实现代码如下
beq $s3, $s4, Label_True # branch i==j
sub $s0, $s1, $s2 # f=g-h (false)
j Label_Finish # goto Fin
Label_True:
add $s0, $s1, $s2 # f=g+h (True)
Label_Finish:
</code></pre>
</li>
<li>
<p>SWITCH 语句</p>
<pre><code class="language-c">// C语言原代码
switch (k) {
case 0: f = i + j; break;
case 1: f = g + h; break;
case 2: f = g - h; break;
case 3: f = i - j; break;
}
// 简化为IF-ELSE语句
if (k == 0) f = i + j;
else if (k == 1) f = g + h;
else if (k == 2) f = g - h;
else f = i - j;
</code></pre>
<pre><code class="language-assembly"># MIPS汇编实现代码如下
bne $s5, $0, Label1 # branch k!=0
add $s0, $s3, $s4 # k==0 so f=i+j
j Label_Exit # end of case so Exit
Label1:
addi $t0, $s5, -1 # $t0=k-1
bne $t0, $0, Label2 # branch k!=1
add $s0, $s1, $s2 # k==1 so f=g+h
j Label_Exit # end of case so Exit
Label2:
addi $t0, $s5, -2 # $t0=k-2
bne $t0, $0, Label3 # branch k!=2
sub $s0, $s1, $s2 # k==2 so f=g-h
j Label_Exit # end of case so Exit
Label3:
addi $t0, $s5, -3 # $t0=k-3
bne $t0, $0, Label_Exit # branch k!=3
sub $s0, $s3, $s4 # k==3 so f=i-j
Label_Exit:
</code></pre>
</li>
<li>
<p>DO - WHILE 语句</p>
<pre><code class="language-C">// C语言原代码
do {
g = g + A;
i = i + j;
} while (i != h);
// 改写为GOTO语句
Label_Loop:
g = g + A;
i = i + j;
if (i != h) goto Label_Loop;
</code></pre>
<pre><code class="language-assembly"># MIPS汇编实现代码如下
Label_Loop:
sll $t1, $s3, 2 #$t1= 4*i
add $t1, $t1, $s5 #$t1=addr A
lw$t1, 0($t1) #$t1=A
add $s1, $s1, $t1 #g=g+A
bne $s3, $s2, Label_Loop #if i!= h goto Label_Loop
</code></pre>
</li>
</ul>
<h5 id="7-mips-支持函数功能的指令">(7) MIPS 支持函数功能的指令</h5>
<ul>
<li>
<p>可同时执行跳转和存储返回地址的指令</p>
<ul>
<li><strong><code>jal Label</code>:执行步骤 - ①link:将下一条指令地址存入<code>$ra</code>;②jump:向指定的<code>Label</code>跳转</strong></li>
<li>一般配合 <code>jr $ra</code> 使用,即 <code>jal Label</code> 存指令到 <code>$ra</code>,并跳转到调用函数的头部,函数执行完毕后使用<code>jr $ra</code>跳转到调用处的下一条指令,完成一次函数的调用</li>
</ul>
</li>
<li>
<p><font color="red">嵌套调用的实现</font></p>
<ul>
<li>嵌套调用会覆盖<code>$ra</code> 中的返回地址信息,故需要另寻他路——使用 <strong>Stack 栈</strong></li>
</ul>
</li>
<li>
<p>寄存器 <code>$sp</code> 始终指向栈空间最后被使用的位置(可以理解为”栈指针“,栈从下往上递增,栈指针地址递减)</p>
<ul>
<li>调用规则:①将需要保存的值压入栈中;②指定参数(如果需要的话);③<code>jal</code>调用函数;④从栈中恢复相关的值</li>
<li>调用过程中的规则:①通过<code> jal</code> 指令调用 , 使用<code>jr $ra</code>指令返回;②最多可接受<strong>4</strong>个入口参数——<code>$a0</code>, <code>$a1</code>, <code>$a2</code>, <code>$a3</code>;③</li>
</ul>
<pre><code class="language-assembly"># C语言 原代码
int sumSquare(int x, int y) { return mult(x, x) + y; }
# MIPS汇编实现
sumSqure:
addi $sp, $sp, -8 # space on stack
sw $ra, 4($sp) # save ret addr
sw $a1, 0($sp) # save y
add $a1, $a0, $zero # prep args
jal mult # call mult
lw $a1, 0($sp) # restore y
add $v0, $v0, $a1 # mult()+y
lw $ra, 4($sp) # get ret addr
addi $sp, $sp, 8 # restore stack
jr $ra
mult:
#mult函数
</code></pre>
</li>
</ul>
<h5 id="8-mips-通用寄存器使用规范">(8) MIPS 通用寄存器使用规范</h5>
<ul>
<li>通用寄存器分配(见 <u>4.2.1(2)寄存器使用约定</u> )</li>
<li><strong>特殊寄存器(系统维护)</strong>
<ul>
<li><code>$at</code>:编译器随时可能使用,最好不要用</li>
<li><code>$k0~$k1</code>:操作系用随时可能使用,最好不要用</li>
<li><code>$gp,$fp</code>:自动维护,无需操作</li>
</ul>
</li>
<li><strong>保存寄存器(程序员维护)</strong>
<ul>
<li><code>$0</code> :<font color="Red">不能改变</font>,恒为 0</li>
<li><code>$s0-$s7</code> :<font color="red">如果被修改了需要恢复</font>。如果被调用函数改变了这些寄存器的值,则必须在函数返回之前将这些寄存器的原始值恢复</li>
<li><code>$sp</code> :<font color="red">如果被修改了需要恢复</font>。栈指针在 <code>jal</code> 执行之前或之后必须是指向的同一地址,不然调用函数无法从栈里正确恢复数据</li>
</ul>
</li>
<li><strong>易变寄存器(程序员维护)</strong>
<ul>
<li><code>$ra</code> :<font color="red">会改变</font>。 <code>jal</code> 会自动更改这个寄存器值,但调用函数需要手动将其值保存在栈上</li>
<li><code>$v0~$v1</code> :<font color="red">会改变</font>。始终保存最新的返回值</li>
<li><code>$a0~$a3</code> :<font color="red">会改变</font>。调用函数如果在调用完成后还要用到这些寄存器中的值,就要在调用前将这些值保存在自己的栈空间内</li>
<li><code>$t0~$t7</code> :<font color="red">会改变</font>。任何函数在任何时候都可以更新这些寄存器中的值;与<code>$a0~$a3</code>类似,调用函数时需手动将这些值保存在栈空间内以防止丢失(被覆盖)</li>
</ul>
</li>
</ul>
<h5 id="9-mips-汇编程序">(9) MIPS 汇编程序</h5>
<ul>
<li>
<p><strong>汇编语言语句</strong></p>
<ul>
<li>可执行指令:为处理器生成在运行时执行的机器码,告诉处理器该做什么</li>
<li>伪指令、宏:由汇编程序翻译成真正的指令,简化编程人员的工作</li>
<li>汇编伪指令:当翻译代码时为汇编程序提供信息,用来定义段、分配内存变量等;不可执行 ——汇编伪指令不是指令集的一部分</li>
</ul>
</li>
<li>
<p><strong>程序模板</strong></p>
<ul>
<li>
<p><code>.DATA</code> :伪指令,定义程序的<strong>数据段</strong>,程序变量需要在该伪指令下定义,汇编程序会分配和初始化变量的存储空间</p>
</li>
<li>
<p><code>.TEXT</code> :定义程序的<strong>代码段</strong></p>
</li>
<li>
<p><code>.GLOBL</code>:伪指令,声明 一个符号为<strong>全局的</strong>,可被其它文件引用,通常用来声明一个程序的 main 过程</p>
</li>
</ul>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201023114851165.png" alt="image-20201023114851165" loading="lazy"></p>
</li>
<li>
<p><strong>数据定义</strong></p>
<ul>
<li>
<p>目的:为变量的存储划分内存(可能会有选择的为数据分配标签)</p>
</li>
<li>
<p>语法:[名字:] 伪指令 初始值 [, 初始值 ] ……</p>
</li>
<li>
<p>数据伪指令</p>
<p><code>.BYTE</code> :以 8 位字节存储数值表</p>
<p><code>.HALF</code> :以 16 位(半字长)存储数值表</p>
<p><code>.WORD</code> :以 32 位(一个字长)存储数值表</p>
<p><code>.WORD w:n</code> :将 32 位数值 w 存入 n 个边界对齐的连续的字中</p>
<p><code>.FLOAT</code> :以 单精度浮点数存储数值表</p>
<p><code>.DOUBLE</code> :以 双精度浮点数存储数值表</p>
</li>
<li>
<p>字符串伪指令</p>
<p><code>.ASCII</code> :为 一个 ASCII 字符串分配字节序列</p>
<p><code>.ASCIIZ</code> :与 .ASCII 伪指令类似 , 但字符串 以 NULL 结尾</p>
<p><code>.SPACE n</code> :为 数据段中 n 个未初始化的字节分配空间</p>
</li>
<li>
<p>实例</p>
<pre><code class="language-assembly">.DATA
var1: .BYTE 1, 2,'Z'
str1: .ASCIIZ "My String\n"
var2: .WORD 0x12345678
</code></pre>
<p>汇编程序会为标签(也可理解为变量)构建符号表,为每一个数据段的标签计算地址,如下图。</p>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201023152646359.png" alt="image-20201023152646359" loading="lazy"></p>
</li>
</ul>
</li>
<li>
<p><strong>内存对齐、字节序</strong></p>
<ul>
<li>
<p><code>.ALIGN n</code> - 对下一个定义的数据做 $2^n$ 字节对齐</p>
</li>
<li>
<p>对齐:字的地址是<strong>4</strong>的整数倍(即地址的2位最低有效位必须是 $00_b$),半字的地址是<strong>2</strong>的整数倍</p>
</li>
<li>
<p>字节序、端</p>
<p>①<strong>小端字节排序</strong>:内存地址 = 最低有效字节 的地址,例子 : Intel IA-32, Alpha</p>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201023153250944.png" alt="image-20201023153250944" loading="lazy"></p>
<p>②<strong>大端字节排序</strong>:内存地址 = 最高有效字节 的地址,例子 : SPARC, PA-RISC</p>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201023153314396.png" alt="image-20201023153314396" loading="lazy"></p>
<p>▲注意:MIPS 可以操作以上两种字节序</p>
</li>
</ul>
</li>
<li>
<p><strong>系统调用</strong></p>
<ul>
<li>
<p><code>syscall</code> :MIPS提供特殊的 <code>syscall</code> 指令,从操作系统获取服务</p>
</li>
<li>
<p>作用:程序通过系统调用实现<u>输入输出</u></p>
</li>
<li>
<p><code>syscall</code>系统服务流程:①从 <code>$v0</code> 寄存器中读取服务数;②从 <code>$a0</code>, <code>$a1</code> 等寄存器中读取参数值(如果有)③发送 <code>syscall</code> 指令;④从结果寄存器中取回返回值(如果有)</p>
</li>
<li>
<p>程序示例 与 系统服务列表</p>
<pre><code class="language-assembly">move $a0, $s0 #copy value of $s0 to $a0
li $v0, 1 #load the service number
syscall #system service start
</code></pre>
</li>
</ul>
<p><img src="https://gitee.com/FujiW/FigureBed/raw/master/img/image-20201023154214712.png" alt="image-20201023154214712" loading="lazy"></p>
</li>
<li>
<p><strong>参数传递</strong></p>
<ul>
<li>
<p>寄存器方法(使用通用寄存器<code>$a0~$a3</code>)</p>
</li>
<li>
<p>栈方法(使用栈 <code>$sp</code>)</p>
<p>栈适用于以下情况:①不适用使用寄存器时,用来存储变量 / 数据结构;②过程调用中保存和恢复寄存器;③实现递归</p>
</li>
</ul>
</li>
</ul>
<h5 id="10-一些小练习">(10) <font color="red">一些小练习</font></h5>
<ul>
<li>
<p><strong>选择排序</strong></p>
<pre><code class="language-assembly"># Objective: Sort array using selection sort algorithm
# Input:$a0 = pointer to first, $a1 = pointer to last
# Output: array is sorted in place
##########################################################
sort:
addiu $sp, $sp, 4 # allocate one word on stack
sw $ra, 0($sp) # save return address on stack
top:
jal max # call max procedure
lw $t0, 0($a1) # $t0 = last value
sw $t0, 0($v0) # swap last and max values
sw $v1, 0($a1)
addiu $a1, $a1, 4 # decrement pointer to last
bne $a0, $a1, top # more elements to sort
lw $ra, 0($ sp) # pop return address
addiu $sp, $sp, 4
jr $ra # return to caller
# Objective: Find the address and value of maximum element
# Input: $a0 = pointer to first, $a1 = pointer to last
# Output: $v0 = pointer to max, $v1 = value of max
##########################################################
max:
move $v0, $a0 # max pointer = first pointer
lw $v1, 0($v0) # $v1 = first value
beq $a0, $a1, ret # if (first == last) return
move $t0, $a0 # $t0 = array pointer
loop:
addi $t0, $t0, 4 # point to next array element
lw $t1, 0($t0) # $t1 = value of A[
ble $t1, $v1, skip # if (A <= max) then skip
move $v0, $t0 # found new maximum
move $v1, $t1
skip:
bne $t0, $a1, loop # loop back if more elements
ret:
jr $ra
</code></pre>
</li>
<li>
<p><strong>递归过程</strong></p>
<pre><code class="language-c">int fact(int n) {
if (n < 2) return 1;
else return (n*fact(n-1));
}
</code></pre>
<pre><code class="language-assembly">fact:
slti $t0,$a0,2 # (n<2)?
beq $t0,$0,else # if false branch to else
li $v0,1 # $v0 = 1
jr $ra # return to caller
else:
addiu $sp,$sp, -8 # allocate 2 words on stack
sw $a0,4($sp) # save argument n
sw $ra,0($sp) # save return address
addiu $a0,$a0, -1 # argument = n-1
jal fact # call fact(n-1)
lw $a0,4($sp) # restore argument
lw $ra,0($sp) # restore return address
mul $v0,$a0,$v0 # $v0 = n*fact(n-1)
addi $sp,$sp,8 # free stack frame
jr $ra # return to caller
</code></pre>
</li>
</ul>
<hr><br><br>
来源:https://www.cnblogs.com/littlecan2020/p/13864913.html
頁:
[1]