1、计算机体系结构,刘昆 2009.11 徐海学院计算机系,2,汇编语言,CPU的基础工作: 执行许许多多的 指令instructions. 执行指令是CPU的主要工作. 不同那个的CPU有不同的指令集. 我们将一种特定的CPU所实现的一种指令集称为 指令集架构Instruction Set Architecture (ISA). Examples: Intel 80x86 (Pentium 4), IBM/Motorola PowerPC (Macintosh), MIPS, Intel IA64, .,3,Book: Programming From the Ground Up,“A new
2、book was just released which is based on a new concept - teaching computer science through assembly language (Linux x86 assembly language, to be exact). This book teaches how the machine itself operates, rather than just the language. Ive found that the key difference between mediocre and excellent
3、programmers is whether or not they know assembly language. Those that do tend to understand computers themselves at a much deeper level. Although almost! unheard of today, this concept isnt really all that new - there used to not be much choice in years past. Apple computers came with only BASIC and
4、 assembly language, and there were books available on assembly language for kids. This is why the old-timers are often viewed as wizards: they had to know assembly language programming.” - slashdot.org comment, 2004-02-05,4,Instruction Set Architectures,早期的时候,人们不断地向CPU内添加各种指令,以实现各种不同的甚至非常复杂的操作 VAX 竟
5、然曾经有一条专门计算多项式的指令! 精简指令集RISC 哲学 (Cocke IBM, Patterson, Hennessy, 1980s) Reduced Instruction Set Computing 保持指令集“小并简单着”, 这样的话,我们可以更加简单的设计出速度非常快的芯片. 实用软件去实现各种复杂操作,将一系列简单操作排列组合即可.,5,MIPS Architecture,MIPS 最早一家生产出商用 RISC architectures的半导体公司 我们将会学习MIPS architecture 的一些细节 Why MIPS instead of Intel 80x86? M
6、IPS is simple, elegant.简单、优雅,不被细节所累. MIPS 在嵌入式中广泛应用, x86 很少应用到嵌入式市场, 它更多的是应用到PC上,6,Assembly Variables: Registers (1/4),与高级程序设计语言C或Java不同, 汇编语言不使用变量 Why not? 保持硬件的简单 汇编语言的操作数都是(寄存器) registers 直接在硬件上实现的,数量有限 所有的操作都只能在寄存器上完成! 优势: 既然寄存器是直接用硬件实现的, 它们一定很快! (faster than 1 billionth of a second),7,Assembly
7、Variables: Registers (2/4),劣势: 既然寄存器是直接在硬件上实现的, 它们预先就规定好了固定的数目! Solution: MIPS code 必须很仔细的写,以便能够很合理、很有效的使用寄存器 32 registers in MIPS Why 32? 简单源自规整! Each MIPS register is 32 bits wide MIPS中,由32个二进制位组合在一起成为一个 字,8,Assembly Variables: Registers (3/4),我们从0到31给32个寄存器编号 当然,每个寄存器除了有编号外,还有自己的名字 按照编号的引用方式: $0,
8、 $1, $2, $30, $31,9,Assembly Variables: Registers (4/4),我们约定, 每个寄存器都取一个名字以便写代码时更方便 For now: $16 - $23 $s0 - $s7(与C语言中的变量对应) $8 - $15 $t0 - $t7(与临时变量对应) 稍后我们会介绍其他16个寄存器 通常情况下, 我们使用名字来指定寄存器,这样可以增加代码的可读性,10,C, Java variables vs. registers,In C (and most High Level Languages) 所有变量需要事先声明成为一个特定的类型 Example
9、: int fahr, celsius; char a, b, c, d, e; 所有变量ONLY表达它被声明的那个类型的一个值 (int与char用法不同,不能混淆). 在汇编语言里, 寄存器没有数值类型; 只能通过我们的代码来决定这些寄存器中的数值应该如何使用和处理,11,Comments in Assembly,另一种增加代码可读性的方法: (注释)comments! Hash (#) 被用来做MIPS的注释 任何从#(Hash Mark)后开始到行末的内容都被视为注释内容,并被忽略 This is just like the C99 / Note: Different from C.
10、C 还有一种注释格式 /* comment */ 这种方式可以跨越多行加注释,12,Assembly Instructions,In assembly language, 每一条语句 (called an Instruction), 就是执行指令集中的一条简单指令 Unlike in C (and most other High Level Languages), 每行汇编代码仅执行一条汇编指令 Instructions are related to operations (=, +, -, *, /) in C or Java Ok, enough alreadygimme my MIPS!
11、,13,MIPS Addition and Subtraction (1/4),Syntax of Instructions: 1 2,3,4 where: 1) 操作名称(“操作符”) 2) 操作结果 (“目标”) 3) 1st 操作数 (“source1”) 4) 2nd 操作数 (“source2”) Syntax is rigid: 1 operator, 3 operands Why? 通过约定好的统一的规则使硬件实现更简单,14,Addition and Subtraction of Integers (2/4),Addition in Assembly Example: add
12、$s0,$s1,$s2 (in MIPS)Equivalent to: a = b + c (in C) where MIPS registers $s0,$s1,$s2 are associated with C variables a, b, c Subtraction in Assembly Example: sub $s3,$s4,$s5 (in MIPS)Equivalent to: d = e - f (in C) where MIPS registers $s3,$s4,$s5 are associated with C variables d, e, f,15,Addition
13、 and Subtraction of Integers (3/4),How do the following C statement? a = b + c + d - e; 拆成多条汇编指令 add $t0, $s1, $s2 # temp = b + c add $t0, $t0, $s3 # temp = temp + d sub $s0, $t0, $s4 # a = temp - e Notice: C中每一行语句可以拆成许多行的MIPS. Notice: 编译时,每行的”#”后面的语句都会被当成注释(comments)忽略,16,Addition and Subtraction o
14、f Integers (4/4),How do we do this? f = (g + h) - (i + j); 使用临时变量寄存器 add $t0,$s1,$s2 # temp = g + h add $t1,$s3,$s4 # temp = i + j sub $s0,$t0,$t1 # f=(g+h)-(i+j),17,Register Zero,一个特别的立即数, the number zero (0), 在代码中频繁出现. 所以我们定义 register zero ($0 or $zero) 永远只包含 0; eg add $s0,$s1,$zero (in MIPS)f = g
15、 (in C) where MIPS registers $s0,$s1 are associated with C variables f, g 既然是在硬件中定义的, so an instruction add $zero,$zero,$s0,18,立即数Immediates,Immediates are 数值常量. 他们在代码中频繁出现,所以专门针对立即数定义了一些指令 Add Immediate:addi $s0,$s1,10 (in MIPS)f = g + 10 (in C) where MIPS registers $s0,$s1 are associated with C va
16、riables f, g Syntax similar to add instruction, except that 最后一个参数用数值代替了寄存器.,19,Immediates,MIPS中没有立即数的减法: Why? 我们尽可能地保持指令集越小越好 如果一个操作可以解开成一些简单指令,就不要将该操作包含进指令集 addi , -X = subi , X = so no subi addi $s0,$s1,-10 (in MIPS)f = g - 10 (in C) where MIPS registers $s0,$s1 are associated with C variables f,
17、 g,20,Peer Instruction,Types are associated with declaration in C (normally), but are associated with instruction (operator) in MIPS. Since there are only 8 local ($s) and 8 temp ($t) variables, we cant write MIPS for C exprs that contain 16 vars. If p (stored in $s0) were a pointer to an array of i
18、nts, then p+; would be addi $s0 $s0 1,ABC 1: FFF 2: FFT 3: FTF 4: FTT 5: TFF 6: TFT 7: TTF 8: TTT,21,“And in Conclusion”,In MIPS Assembly Language: 用寄存器代替了C语言中的变量 一条指令 (simple operation) 占一行 简单原则规整 越小越快 New Instructions: add, addi, sub New Registers: C Variables: $s0 - $s7 Temporary Variables: $t0 -
19、 $t9 Zero: $zero,22,Assembly Operands: Memory,C变量被映射成寄存器; 如果是大型数据结构,例如数组该怎么办呢? 作为计算机5大部件之一: 内存就是用来存储这样的大型数据结构 但是 MIPS arithmetic instructions 只能操作寄存器, 从来不会直接操作内存. Data transfer instructions 在内存与寄存器之间传输数据: Memory to register Register to memory,23,解剖: 5 components of any Computer,Personal Computer,Pro
20、cessor,Computer,Control (“brain”),Datapath Registers,Memory,Devices,Input,Output,These are “data transfer” instructions,寄存器被包含在处理器的数据通路中; 如果操作数在内存中, 我们必须首先将它们转移到处理器上才能进行操作, 然后当我们处理完成之后,再将其送回内存.,24,Data Transfer: Memory to Reg (2/4),要指定访问内存的具体地址, 需要两个数据值: A register 包含了一个指向内存某地址的指针 A numerical offset
21、 (in bytes) 我们需要的内存地址通常是这样两个值相加所得的和 Example: 8($t0) 根据$t0中的值找到内存中的对应地址, 再加上 8 bytes,25,Data Transfer: Memory to Reg (3/4),Load Instruction Syntax:1 2,3(4) where1) 操作码2) 寄存器存储从内存中取来的那个数值3) 数值偏移量 in bytes4) 寄存器存储一个指向内存中某地址的指针 MIPS Instruction Name: lw (meaning Load Word, so 32 bits or one word are loa
22、ded at a time),26,Data Transfer: Memory to Reg (4/4),Example: lw $t0,12($s0)本指令先取出$s0中的指针的值, 加上12 bytes 的偏移量, 将这两个值相加即得到我们要访问的内存地址,然后,将内存中该地址存储的数值送进$t0。 Notes: $s0 is called the base register基址寄存器 12 is called the offset 偏移量经常用来访问数组或结构体中的元素: 基址寄存器指向该数组或结构体的首地址 (注意偏移量必须是一个 在编译时就已经有确定值的数值常量),Data flow
23、,27,Data Transfer: Reg to Memory,我们有时候还想将内存中的数值写回内存中去 Store与Load指令的语法格式是完全一样的 MIPS Instruction Name:sw (meaning Store Word, so 32 bits or one word are loaded at a time)Example: sw $t0,12($s0)本指令先取出$s0中存放的指针的值, 加上12 bytes, 计算出两个值的和,即是要访问的内存地址,然后,将$t0中的值送入内存中该地址存放 Remember: “Store INTO memory”,Data fl
24、ow,28,Pointers v. Values,Key Concept: 一个寄存器中可以存储意义的32-bit的数值. 它可以是一个 (signed) int, 一个unsigned int, 一个pointer (memory address), 或是其它的类型 如果你写了 add $t2,$t1,$t0 那么 $t0 与 $t1 最好是可以相加的,结果是有意义的值 如果你写了 lw $t2,0($t0) 那么 $t0 里面一定要是一个地址值(pointer) Dont mix these up!别搞砸了!,29,Addressing: Byte vs. word,内存中的每一个字都有一
25、个 address, 类似于一个数组的索引 早期的计算机按字编址,就像C语言中的数组下标: Memory0, Memory1, Memory2, 计算机也需要访问 8-bit bytes as well as words (4 bytes/word) 今天的计算机都是按字节编址了, (i.e.,“Byte Addressed”) 因而 32-bit (4 byte) 字地址按 4递增 Memory0, Memory4, Memory8, ,30,Compilation with Memory,如果要选择C语言中的A5,lw的偏移量应该是多少呢?4x5=20 to select A5: byte
26、 v. word 使用寄存器来手工编译: g = h + A5;g: $s1, h: $s2, $s3:base address of A 首先,将数据从内存送到寄存器:lw $t0,20($s3) # $t0 gets A5 Add 20 to $s3 to select A5, put into $t0 然后,将它与h相加,结果送进g add $s1,$s2,$t0 # $s1 = h+A5,31,Notes about Memory,缺点: 一定记住,在按字节编址的计算机中,连续两个字的地址值相差是4而不是1. 许多汇编程序员误以为下一个字的地址就是将寄存器中的地址加1而不是加一个完整的
27、字长,他们最终不得不因为无数的错误疲于奔命。 所以记住无论是 lw 还是 sw, 基址寄存器的值与偏移量的和始终应该是4的倍数 multiple of 4 (to be word aligned),32,More Notes about Memory: Alignment,MIPS 要求所有字的首地址必须是4的倍数,Called Alignment(字对齐): 对象的起始地址一定要是字长的整数倍.,0, 4, 8, or Chex,Last hex digit of address is:,1, 5, 9, or Dhex,2, 6, A, or Ehex,3, 7, B, or Fhex,3
28、3,Role of Registers vs. Memory,如果变量数比寄存器数多怎么办? 编译器会将最经常使用的变量保留在寄存器中 不常使用的放在内存中: spilling 为什么不把所有的变量都放在寄存器里? 小就是快: 所以寄存器比内存要快 寄存器用途多多: MIPS 计算型指令每执行一条指令时,可以从两次读操作取数据,计算结果,一次写操作到寄存器中 MIPS 数据传送型指令在每一条指令中,对寄存器只有一次读或写操作,34,Peer Instruction,We want to translate *x = *y into MIPS (x, y ptrs stored in: $s0
29、$s1) A: add $s0, $s1, zero B: add $s1, $s0, zero C: lw $s0, 0($s1) D: lw $s1, 0($s0) E: lw $t0, 0($s1) F: sw $t0, 0($s0) G: lw $s0, 0($t0) H: sw $s1, 0($t0),0: A 1: B 2: C 3: D 4: EF 5: EG 6: FE 7: FH 8: HG 9: GH,35,Example,我们需要完成以下指令: int x = 5; *p = *p + x + 10; In MIPS (assume $s0 holds p, $s1 is
30、 x) addi $s1,$0,5 # x = 5 lw $t0,0($s0) # temp = *p add $t0,$t0,$s1 # temp += x addi $t0,$t0,10 # temp += 10 sw $t0,0($s0) # *p = temp,36,Loading, Storing bytes 1/2,除了需要在内存与寄存器之间按字传送外 (lw, sw), MIPS 还有按字节传送的指令: load byte: lb store byte: sb 与lw, sw格式相同 E.g., lb $s0, 3($s1) 把内存中的某个地址(“3” + s1中的地址值)所存储
31、的数值拷贝到s0的低地址字节上.,37,Loading, Storing bytes 2/2,x,那么32位中的其余24位填充什么呢? lb: 使用位扩展(sign-extend)填充剩余24位,xxxx xxxx xxxx xxxx xxxx xxxx,zzzzzzz,有些情况下我们不想使用位扩展(char类型)MIPS 不使用位扩展的指令:load byte unsigned: lbu,38,So Far.,目前为止,我们见过的指令都是操作数据的指令我们做成了一个计算器calculator. 为了实现一个真正的computer, 我们需要代码能够自己做一些决定 C (and MIPS) 提
32、供 labels标签 用来支持“goto” 在代码中跳来跳去. C: Horrible style; MIPS: Necessary! Heads up: pull out some papers and pens, youll do an in-class exercise!,39,C Decisions: if Statements,C中有两种if语句 if (condition) clause if (condition) clause1 else clause2 我们对上面第二种if语句改写以下:if (condition) goto L1; clause2; goto L2; L1:
33、 clause1;L2: 不如if-else看上去优雅, 但是效果一样,40,MIPS Decision Instructions,MIPS中的判断语句: beq register1, register2, L1 beq是“Branch if (registers are) equal” Same meaning as (using C): if (register1=register2) goto L1 MIPS中还有一个与其互补的指令 bne register1, register2, L1 bne is “Branch if (registers are) not equal” Same
34、 meaning as (using C): if (register1!=register2) goto L1 Called conditional branches条件分支,41,MIPS Goto Instruction,除了条件分支语句之外, MIPS 还有 unconditional branch无条件分支:j label 称为跳转指令: jump (or branch) 到标签label所在的代码,不需要满足任何条件 与C中的goto语句用法相同 (using C): goto label 从技术的角度来说, 它与以下写法相同: beq $0,$0,label 因为这条语句总会满足
35、条件而跳转到label.,42,Compiling C if into MIPS (1/2),Compile by handif (i = j) f=g+h; else f=g-h;Use this mapping: f: $s0 g: $s1 h: $s2 i: $s3 j: $s4,43,Compiling C if into MIPS (2/2),Final compiled MIPS code:beq $s3,$s4,True # branch i=j sub $s0,$s1,$s2 # f=g-h(false) j Fin # goto Fin True: add $s0,$s1,$
36、s2 # f=g+h (true) Fin: Note: 编译器会在处理判断语句时自动的为语句添加label去执行分支 (branches).所以我们在C语言的if语句中找不到goto和label的身影。,Compile by handif (i = j) f=g+h; else f=g-h;,44,Overflow in Arithmetic (1/2),提醒: 发生溢出是由于计算机有限的数值表示引起的 Example (4-bit unsigned numbers):+15 1111+3 0011+18 10010 但是如果我们只有4-bit寄存器,存不了5-bit, 那么这个结果就是00
37、10, 是+2, 结果就错了.,45,Overflow in Arithmetic (2/2),有些语言会自动检测出异常(如:Ada), 有些不会(如:C) MIPS有2种(+,-)运算指令,每一种指令又有两种数值方式: 下面的 可以检测出溢出异常 add (add) add immediate (addi) subtract (sub) 下面的 不会检测出溢出异常 add unsigned (addu) add immediate unsigned (addiu) subtract unsigned (subu) 编译器会自动挑选合适的运算指令类型 MIPS中的C编译器会使用 addu, a
38、ddiu, subu,不检查溢出异常,46,Two Logic Instructions,这里再来两条新指令 左移位: sll $s1,$s2,2 #s1=s2,47,Loops in C/Assembly (1/3),C中的循环; A is an array of intsdo g = g + Ai; i = i + j; while (i != h); Rewrite this as:Loop: g = g + Ai; i = i + j; if (i != h) goto Loop; Use this mapping: g, h, i, j, base of A $s1, $s2, $s
39、3, $s4, $s5,48,Loops in C/Assembly (2/3),Final compiled MIPS code: Loop: sll $t1,$s3,2 #$t1= 4*i add $t1,$t1,$s5 #$t1=addr A lw $t1,0($t1) #$t1=Ai add $s1,$s1,$t1 #g=g+Ai add $s3,$s3,$s4 #i=i+j bne $s3,$s2,Loop# goto Loop # if i!=h Original code:Loop: g = g + Ai; i = i + j; if (i != h) goto Loop;,49
40、,Loops in C/Assembly (3/3),C中有3中循环结构: while do while for 每一种都可以用另外两种中的任一种等价表达, 所以我们刚才那个例子同样适用于while and for 循环. Key Concept: 虽然MIPS有许多种循环的写法 , the key to decision making is conditional branch,50,“And in Conclusion”,判断语句:我们可以在运行时而非编译时去动态的决定如何执行语句. C 使用条件语句(conditional statements)做判断if, while, do whil
41、e, for. MIPS 使用条件分支(conditional branches)做判断: beq and bne. Unsigned add/sub 不会引起溢出异常(不检测) New MIPS Instructions: beq, bne, j, sll, srl addu, addiu, subu,Review,Memory transfer: lw, sw, lb, sb 判断语句可以使我们在运行时决定去执行哪部份代码,而无非编译时. C 使用条件分支 conditional statements语句来做决定, within if, while, do while, for. MIPS
42、 中做决定的语句是 conditional branches: beq and bne. Unsigned add/sub dont cause overflow New MIPS Instructions: beq, bne, j, sll, srl addu, addiu, subu,Loops Review,Key Concept: 虽然现在在MIPS中有很多方法来实现循环, 但最重要的一点是:所有这些方法都使用条件分支 conditional branch do g= g + Ai; i= i + j; while (i != h);Loop: g = g + Ai; i = i +
43、j; if (i != h) goto Loop;,Inequalities in MIPS (1/3),Until now, weve only tested equalities (= and != in C). General programs need to test as well. Create a MIPS Inequality Instruction: “Set on Less Than” Syntax: slt reg1,reg2,reg3 Meaning:if (reg2 reg3) reg1 = 1; else reg1 = 0; In computereeze, “se
44、t” means “set to 1”, “reset” means “set to 0”.,reg1 = (reg2 reg3);,Same thing,Inequalities in MIPS (2/3),How do we use this? Compile by hand: if (g h) goto Less; #g:$s0, h:$s1 Answer: compiled MIPS codeslt $t0,$s0,$s1 # $t0 = 1 if gh bne $t0,$0,Less # goto Less # if $t0!=0 # (if (gh) Less: Branch if
45、 $t0 != 0 (g h) Register $0 always contains the value 0, so bne and beq often use it for comparison after an slt instruction. A slt bne pair means if( )goto,Inequalities in MIPS (3/3),Now, we can implement , and ? We could add 3 more instructions, but: MIPS goal: Simpler is Better Can we implement i
46、n one or more instructions using just slt and the branches? What about ? What about ?,Immediates in Inequalities,There is also an immediate version of slt to test against constants: slti Helpful in for loopsif (g = 1) goto LoopLoop: . . . slti $t0,$s0,1 # $t0 = 1 if # $s0=1),C,M I P S,An slt beq pai
47、r means if( )goto,What about unsigned numbers?,Also unsigned inequality instructions:sltu, sltiu which sets result to 1 or 0 depending on unsigned comparisons What is value of $t0, $t1? ($s0 = FFFF FFFAhex, $s1 = 0000 FFFAhex)slt $t0, $s0, $s1 sltu $t1, $s0, $s1,MIPS Signed vs. Unsigned diff meaning
48、s!,MIPS Signed v. Unsigned is an “overloaded” term Do/Dont sign extend (lb, lbu) Dont overflow (addu, addiu, subu, multu, divu) Do signed/unsigned compare (slt, slti/sltu, sltiu),Example: The C Switch Statement (1/3),Choose among four alternatives depending on whether k has the value 0, 1, 2 or 3. C
49、ompile this C code: switch (k) case 0: f=i+j; break; /* k=0 */ case 1: f=g+h; break; /* k=1 */ case 2: f=gh; break; /* k=2 */ case 3: f=ij; break; /* k=3 */ ,Example: The C Switch Statement (2/3),This is complicated, so simplify. Rewrite it as a chain of if-else statements, which we already know how to compile: if(k=0) f=i+j; else if(k=1) f=g+h; else if(k=2) f=gh; else if(k=3) f=ij; Use this mapping:f:$s0, g:$s1, h:$s2, i:$s3, j:$s4, k:$s5,