2-5 Addressing Modes 寻址模式
ARM 与 Mips
ARM 是最流⾏的嵌⼊式设备指令集架构 (跟MIPS一样也是RISC),主要区别是 MIPS 有更多的寄存器, 而ARM 有更多的寻址模式(ARM 9个, Mips 3个) 。 算术逻辑和数据传输有类似的指令集核心。
一般RISC的设计用于嵌入式计算
RISC设计原则
- RISC:单周期执行(简单操作) CISC: 许多多周期操作
- RISC:Load/store 内存访问架构 CISC: 寄存器内存和内存内存指令
- RISC:少数内存寻址模式(1.寄存器寻址 2.立即数寻址 3.基址寻址) CISC: 多种模式
- RISC:固定指令格式 CISC: 多种格式和⻓度
-
RISC:依赖编译器优化 CISC: ⼿⼯组装以获得良好的性能
概括起来, RISC以硬件容易实现, ⽽且让编译器更容易⽣成代码为设计准则。
指令长度与格式
Length
- 固定长度(MIPS) • ⼤部分 32 bits • 简单实现 (next PC often just PC+4) • 代码密度: 每⼀条代码32 bits
- 可变长度的指令(x86) • 代码密度 • x86 有8位组成的指令 • 取指复杂(下⼀条指令从哪⾥开始?)
- 折中: 两种⻓度的指令 • E.g., MIPS16 或 ARM 的混合⻓度
Encoding
- ⼀些简单的指令编码简化了译码器,MIPS只有三种格式的指令(R型、I型、J型)
- x86 译码器是⼀个繁琐的逻辑
有多少显式寄存器操作数
操作数模型: 有多少显式操作数
- 3个: 通用 add R1,R2,R3 表示 [R1] = [R2] + [R3] (MIPS 使⽤这个)
- 2个: 显式累加器 add R1,R2 表示 [R1] = [R1] + [R2](x86 使⽤这个)
- 1个: ⼀个隐式累加器 add R1 表示 ACC = ACC + [R1]
- 4+个: 仅在特殊情况下有用
Q:为什么要少? A:主要是代码密度(程序⼆进制中每条指令的⼤小)
多少个寄存器
- 寄存器比内存快, 尽可能多好吗? 不,寄存器更快的原因之⼀: 它们的数量更少(小即快原则)
- 另⼀个: 直接寻址(⽆地址计算) – 更多的寄存器, 意味着指令中寄存器的位更多 – 因此, 指令更⼤
- 并⾮所有内容都可以放⼊寄存器 •⽐如 结构、 数组、 指针
- 更多的寄存器意味着更多的保存/恢复 • 跨函数调⽤、 陷阱和上下⽂切换
- 趋势: 更多寄存器: 8 (x86)! 32 (MIPS) ! 128 (IA64) • 64 位 x86 有 16 个 64 位整数和 16 个 128 位 FP 寄存器
x86的寄存器结构
操作数模型: 寄存器还是内存?
-
“LOAD/STORE”架构
• 内存访问指令(加载和存储) • 单独的加法、 减法、 除法等运算指令 • 示例: MIPS、 ARM、 SPARC、 PowerPC
-
混合操作数模型(x86、 VAX) • 操作数可以来⾃寄存器或内存 • x86 示例: addl 100, 4(%eax)
1 2 3
1. 从内存位置 [4 + %eax] 取数 2. 将该值加“100” 3. 结果存储到内存位置 [4 + %eax]
将需要 MIPS 中的三个指令
寻址方式
形式地址与有效地址
- 形式地址: 指令中直接给出的地址编码。
- 有效地址: 根据形式地址和寻址⽅式计算出来的操作数在内存单元中的地址。
-
寻址方式定义:指令代码中地址字段的⼀部分,指明操作数的获取⽅式或操作数地址的计算⽅式
-
指令中每⼀个地址字段均有其寻址⽅式编码(或隐含寻址⽅式)
1. 立即寻址—操作数直接在指令代码中给出
说明:
- 立即寻址只能作为双操作数指令的源操作数。
- Operand = Imme. Data 例: MOV AX,1000H
2.寄存器直接寻址
说明:
- 操作数在寄存器中, 指令地址字段给出寄存器的地址(编码)
- EA = Rn, Operand = (Rn) 例: MOV BX, AX
3.存储器直接寻址
说明:
- 操作数在存储器中, 指令地址字段直接给出操作数在存储器中的地址
- EA = A, Operand = (A) 例: MOV AX, [1000H]
4. 寄存器间接寻址
说明:
- 操作数在存储器中, 指令地址字段中给出的寄存器的内容是操作数在存储器中的地址。
- EA = (Rn), Operand = ((Rn)) 例: MOV AX, [BX]
5. 存储器间接寻址
说明:
-
操作数在存储器中, 指令地址字段中给出的存储器地址的单元内容是操作数在存储器中的地址。
-
EA = (A1), Operand = ((A1)) 例: MOV R1, @(1000H) PDP-11的指令
6. 基址寻址
- 操作数在存储器中, 指令地址字段给出⼀基址寄存器和⼀形式地址, 基变址寄存器的内容与形式地址之和是操作数的内存地址。
- EA = (Rb)+A, Operand = ((Rb)+A) 例: MOV AX, 1000H[BX] BX即为基址
==基址寻址的作用: 较短的形式地址长度可以实现较大的存储空间的寻址。==
7. 变址寻址
- 操作数在存储器中, 指令地址字段给出⼀变址寄存器和⼀形式地址,变址寄存器的内容与形式地址之和是操作数的内存地址。
- EA = (Rx)+A, Operand = ((Rx)+A)
-
有的系统中, 变址寻址完成后, 变址寄存器的内容将⾃动进⾏调整。
- Rx= (Rx) + ?? 操作数Data的字节数? 例: MOV AX, 1000H[DI] (完成后DI会自动加一)
8. 相对寻址
- 基址寻址的特例, 由程序计数器PC作为基址寄存器, 指令中给出的形式地址作为位移量, ⼆者之和是操作数的内存地址。
- EA = (PC)+A, Operand = ((PC)+A) 例: JNE A
9. 堆栈寻址
- 堆栈的结构: ⼀段内存区域。
- 栈底, 栈顶,
- 堆栈指针(SP): 是⼀个特殊寄存器部件, 指向栈顶
- 堆栈操作: PUSH ( 从寄存器到堆栈), POP (从堆栈到寄存器)
压栈PUSH
压栈操作: PUSH Rn, 假定寄存器Rn为16位寄存器 (SP) = (Rn), SP = (SP) - 2
要注意,压栈的时候栈指针要==减==一个值
出栈POP
出栈操作: POP Rn, 假定寄存器Rn为16位寄存器 SP = (SP) + 2, Rn = ((SP))
要注意,压栈的时候栈指针要==加==一个值
10. 页面寻址
- 将程序计算器PC的高位部分与形式地址 拼接形成操作数的有效地址。
- EA = (PC)H,拼接 A
- 内存分位若⼲⻚, (PC)H,指明⻚地址, 形式地址A表明⻚内的位移量
MIPS寻址方式
PC-relative寻址中:由程序计数器PC作为基址寄存器, 指令中给出的形式地址作为位移量, ⼆者之和是操作数的内存地址。
1. Indirect Addressing 寄存器间接寻址
注意la指令,是一个伪指令,la 伪指令: la $r, x
:代表将变量 x 的地址“加载”到寄存器 r 中
可以用下列两个指令替换:如果 x 的地址很⼩(适合 16 位) , 那么单个 ori 即可, 如果 x 的地址较⼤, 使⽤组合lui + ori
- 小于16bits(0x0100)
1
ori $2,$0,0x0100 #x的地址
- 大于16bits(0x80000010)
1
2
lui $2,0x8000 #xhighbits地址
ori $2,$2,0x0010 #xlowbits地址
2. Displacement Addressing 基址加变址
What we want:
- 相对于寄存器偏移处的内存位置的内容 offset relative to a register
- 地址是寄存器内容与偏移量的 和
Example 1:
C语言:
1
2
3
4
5
int a[5];
main() {
int i = 3;
a[i] = 2;
}
MIPS Assembly
.data 0x0100
.global a
a: .space 20 #为5个未初始化的整数分配空间(20-bytes)
.text
.global main
main:
addi $2,$0,3 // i in $2
addi $3,$0,2
sll $1,$2,2 // i*4 in $1
sw $3,a($1)
注意:必须使地址字对齐
Example 2:
C语言:
1
2
3
4
5
6
struct p {
int x, y; }
main() {
p.x = 3;
p.y = 2;
}
MIPS Assembly:
.data
.global p
p: .space 8 #Allocates space for 2 uninitialized integers (8-bytes)
.text
.global main
main:
la $1,p
addi $2,$0,3
sw $2,0($1)
addi $2,$0,2
sw $2,4($1)
注意:结构中各个字段的偏移量是汇编器/编译器已知的常量
Real-World Addressing 注意事项
- 在实际中, $gp 通常⽤作所有变量的基地址
- 这种⽅式只能寻址首尾32K内存(offset为16Bit) ?什么方式
- 有时会⽣成两个指令序列:
1
2
lui $1,xhighbits
lw $2,xlowbits($1)
- 在上电时给gp初始化⼀个值,那么,所有小变量区的变量就可以通过lw r1, offset(gp)来访问
MIPS 指令系统的细节信息
- $0寄存器总是具有数值0 (即使试图对它写⼊其他数值, 也是如此)
- 转移和跳转指令将返回地址PC+4装⼊链接寄存器( link register)
- 所有指令改变⽬标寄存器的所有 32 位信息 (包括lui, lb, lh), 并读取源寄存器的所有32位信息 (add, sub, and, or, 等等)
- ⽴即数算术和逻辑指令进⾏如下扩展:
- 逻辑⽴即数(logical immediates) 零扩展到 32位
- 算术⽴即数(arithmetic immediates) 符号扩展到32位
- 指令lb 和 lh 装⼊的数据进⾏如下扩展: sb 和sbu不存在这种扩展问题
- ==lbu, lhu 是零扩展==
- ==lb, lh 是符号扩展==
- ==下⾯的算术和逻辑指令可能会出现溢出(有溢出标志):add, sub, addi==
- ==以下指令不会出现溢出(逻辑运算和无符号运算都不存在溢出): addu, subu, addiu, and, or, xor, nor, shifts,mult, multu, div, divu==
总结: MIPS R3000的显著特点
- 32位固定格式的指令 (3 种格式)
- 32 个32位 GPR (R0 = 0) 和 32 个 FP 寄存器 (以及 HI LO)
- 根据软件约定划分
- 3 地址, 寄存器-寄存器算术指令
- 对于load/store指令, 单⼀寻址模式: base+displacement –没有间接寻址(indirection) –16位⽴即数 加 LUI
- 简单的转移条件
- 与0⽐较, 或者判断两个寄存器是否相等
- 没有条件码
- 延迟转移
- 即使转移发⽣, 也要执⾏转移(或跳转) 之后的指令
- (在50%的时间, 编译器能够在延迟转移的时间中填充有⽤⼯作)