2-3 Assembly & Simulation 汇编与仿真
1. Assembler 汇编器
- Input: 汇编语⾔代码(e.g. MAL) (e.g., foo.s)
- Output: Object Code, information tables (TAL(True Assembly Language)) (e.g., foo.o)
-
Reads and Uses Directives 指示性语句
- Replace Pseudo-instructions 替换伪指令
- Produce Machine Language ⽣成机器语⾔
- Creates Object File ⽣成⽬标⽂件
2. Assembly Source Language 汇编源文件
汇编源⽂件是符号表示的⽂本⽂件, 以字节编址连续装⼊内存
e.g:
1
2
3
4
5
6
7
.data 0x10000000 规定 “当前”地址, i.e., 4个字节数据存放的起始地址
.byte 1, 2, 3, 4 每个数据 1 byte
.byte 5, 6, 7, 8 另外4个字节数据
.word 1, 2, 3, 4 4个字数据 (每个数据 4 bytes)
.asciiz "Comp 411" 以0终⽌的ASCII 字符串,也就是这行占9个bytes,最后有个结束符号‘/0’
.align 2 下⾯的16进制数据开始地址对⻬22, 即最后两位为00
.word 0xfeedbeef 16进制表示的字数据
==易错点:.asciiz “Comp 411” 以0终⽌的ASCII 字符串,也就是这行占9个bytes,最后有个结束符号‘/0’==
3. Assembler Directives 汇编指示性语句
给汇编程序指示, 但不产⽣机器指令
- .text: 后⾯放置代码段(machine code)
- .data: 后⾯放置⽤户的数据段 (binary rep of data in source file)
- .globl sym: 声明 sym 全局性, 可被其它⽂件引⽤
- .string str:将字符串 str 存储在内存中并以空值终⽌它
- .word w1…wn: 将 n 个 32 位数值存储在连续的内存字中,允许我们初始化内存中的连续字
Thing to note:
- “.data”汇编指令将以下字放⼊数据段
- “.globl” directives 使“sum” 和 “i” 变量可以全局访问
- “.space” directives 为每个变量分配4个字节
- 与“.word”相⽐, “.space” 不用初始化变量值
4. Assembler Syntax 汇编语法
-
Assembler COMMENTS 汇编注释:
语句后⾯加‘#’ (sharp) to the end of the line is ignored
-
Assembler LABELS 汇编标签 :
符号表示内存地址,采⽤它们声明的地⽅的地址值,可以是数据标签也可以是指令标签
.data 0x80000000
item: .word 1,2,3,4 # data word 数组名为item
#label item 表示数组中第⼀个字地址, 或者数组的名称
.text 0x00010000
start: add $3, $4, $2 # an instruction label
sll $3, $3, 8
andi $3, $3, 0xff
beq ..., ..., start
tips:item + i × 4才是下一位地址,因为item每个数据是 4 bytes。 (.byte声明的数据才是每个数据1byte
5. Assembler MNEMONICS 汇编程序助记符
指令的符号表示:(不包括所有指令 )
add, addu, addi, addiu, sub, subu, and, andi, or, ori, xor, xori, nor, lui, sll, sllv, sra, srav, srl, srlv, div,divu, mult, multu, mfhi, mflo, mthi, mtlo, slt, sltu, slti, sltiu, beq, bgez, bgezal, bgtz, blez, bltzal, bltz, bne, j, jal, jalr, jr, lb, lbu, lh, lhu, lw, lwl, lwr, sb, sh, sw, swl, swr, rfe
6. Pseudoinstructions 伪指令
不是指令的助记符,汇编器会将其转为真正的指令(要注意如何等价替换)
1
abs, mul, mulo, mulou, neg, negu, not, rem, remu, rol, ror, li, seq, sge, sgeu, sgt, sgtu, sle, sleu, sne, b, beqz, bge, bgeu, bgt, bgtu, ble, bleu, blt, bltu, bnez, la, ld, ulh, ulhu, ulw, sd, ush, usw, move, syscall, break, nop
常见伪指令:
-
la 将常量加载到寄存器中
1
见下面图片2
-
li 将立即数加载到寄存器中
-
move 将一个寄存器中内容移到另一个寄存器中
1
move $d,$s becomes addi $d,$s,0
la的替换
7. Procedures and Stacks 子程序和堆栈
为子程序设计的2条指令:
- jal 跳转到子程序
- jr 用于返回主程序
主程序(Caller) :能提供参数给子程序。
- $a0 -a3(寄存器4-7):专门用于传参数,规定这4个寄存器位传递参数寄存器
子程序(Callee) :完成一系列操作,执行后的结果放在主程序能获得的地方,然后返回主程序。
- $v0 - v1(寄存器2-3):两个放返回值的寄存器
- $ra(寄存器31):放返回地址的寄存器(是调用子程序的下一条指令的地址)
叶子子程序(LEAF function):不再调⽤其它子程序。
Q1:分配了4个参数寄存器, 多于4个怎么办?还有局部变量?
Q2:子程序还能调⽤其它函数子程序吗?那只有一个$ra怎么办?
A:用堆栈解决
### 7.1 Procedure Call and Stack(过程调用和栈)
过程调用的执行步骤(假定过程P调用过程Q) :
- 将参数放到Q能访问到的地⽅
- 将P中的返回地址存到特定的地⽅, 将控制转移到过程Q
- 为Q的局部变量分配空间(局部变量临时保存在栈中)
- 执⾏过程Q
- 将Q执⾏的返回结果放到P能访问到的地⽅
- 取出返回地址, 将控制转移到P, 即返回到P中执⾏
7.2 Stack
-
是⼀个“先进后出”队列
-
需⼀个栈指针sp指向栈顶元素
-
每个元素⻓度⼀致
-
用“⼊栈”(push) 和“出栈”(pop) 操作访问栈元素
-
只有堆栈顶部是直接可见的, 即“栈顶” (自上向下生长)
7.3 Procedure frame 栈帧 or activation record 活动记录:
保存寄存器内容和局部变量的堆栈段
fp为帧指针
注意Arg[5]和Arg[4]的相对位置
7.4 MIPS Stack Convention
7.5 MIPS寄存器使用情况的软件约定(假定P调用Q)
7.6 过程调用时MIPS中的栈和栈帧的变化
7.7 Stack Management Primitives 堆栈管理
(2-3ppt中40页有一个例子)
7.8 递归调用
调用前 ⼀个函数的运⾏期间调用另⼀个函数时, 在运⾏被调用函数之前, 系统需要完成3件事情: (1) 将所有的实参、 返回地址等信息传递给被调用函数保存; (2) 为被调用函数的局部变量分配存储区; (3) 将控制转移到被调函数的⼊⼝。
递归调用调用中 而从被调用函数返回调用函数之前, 系统也应完成3件⼯作: (1) 保存被调函数的计算结果; (2) 释放被调函数的数据区; (3) 依照被调函数保存的返回地址将控制转移到调用函数。 当有多个函数构成嵌套调用时, 按照后调用先返回的原则。 函数的调用原则和数据结构栈的实现是相⼀致。 也说明函数调用是通过栈实现的
A Procedure’s Storage Needs
- 主程序需传递的参数, 存⼊a0-a3,存储返回地址到ra。
- 子程序如果用到s0-s7, 则存⼊堆栈,(可以任意用t0-t9) 把返回值存⼊v0-v1, ⼦程序如果⼜调用子程序则需把上级调用的ra值放⼊堆栈, 涉及的变量也需要存入堆栈, 返回时弹出 。
例子:
7.9总结
调用者约定
调⽤者的约定(主程序):
- 存储后面子程序用到的临时寄存器内容到堆栈 (t0-t9, a0-a3, and v0-v1)
- 前四个传递参数存⼊a0-a3, 后面需要传递的参数按照规定的顺序放⼊堆栈. Why?
- 用jal 指令调用子程序, (返回地址放⼊ ra).
- 子程序返回值在 v0-v1 中进行访问。
被调用者约定
被调⽤者的约定(子程序):
- 为要保存的寄存器、 局部变量和溢出的 args分配堆栈帧空间
- 保存要使⽤的需保存的寄存器:(ra, sp, fp, gp, s0-s7)
- 如果 CALLEE 有局部变量 - 或者 - 需要访问堆栈上的 args, 保存CALLER 的帧指针并将 fp 设置为 CALLEE 堆栈的第⼀个条⽬
- 执⾏⼦程序
- 放返回值到 v0-v1
- 恢复存储的寄存器
- 恢复 sp 到原来的值
- ⽤ jr $ra 返回主程序
-
主程序要存到堆栈的内容:t0-t9, a0-a3, and v0-v1
-
子程序要存到堆栈的内容:ra, sp, fp, gp, s0-s7