
第 2 章 处理器体系结构《计算机系统系统架构与操作系统的高度集成》Umakishore Ramachandran William D. Leahy Jr. 著2.1 指令集体系结构ISA概述指令集体系结构ISA是处理器硬件与软件之间的抽象接口定义了处理器能够理解和执行的所有指令集合。ISA 的核心内容 指令集处理器能执行的所有操作 寄存器处理器内部的高速存储单元 数据类型处理器原生支持的数据格式 寻址方式如何计算操作数的地址 内存模型内存的组织方式和访问规则 RISC vs CISC CISCx86指令复杂数量多长度可变 代表Intel/AMD x86 特点单条指令可完成复杂操作直接操作内存 RISCARM/RISC-V指令简单数量少长度固定 代表ARM、RISC-V、MIPS 特点Load/Store架构只有LOAD/STORE访问内存 现代趋势x86内部用RISC微操作实现CISC指令2.2 LC-2200 指令集体系结构LC-2200 是本书使用的教学用简化指令集设计简洁便于理解处理器工作原理。2.2.1 寄存器LC-2200 寄存器组织16个32位通用寄存器 编号 | 名称 | 用途 ------|--------|--------------------------- R0 | $zero | 硬连线为0读取永远返回0写入无效 R1 | $at | 汇编器临时寄存器 R2-R3 | $v0-v1 | 函数返回值 R4-R7 | $a0-a3 | 函数参数前4个 R8-R15| $t0-t7 | 临时寄存器调用者保存 特殊寄存器 PC程序计数器存储下一条指令地址每次自动加4 FLAGS状态寄存器 ZZero运算结果为零时置1 NNegative运算结果为负时置1 CCarry产生进位时置1 VOverflow有符号溢出时置1 寄存器 vs 内存对比 寄存器 1ns数量极少16个CPU内部 内存50100ns容量大GB级CPU外部 核心原则尽量将频繁使用的数据放在寄存器中2.2.2 指令格式LC-2200 指令格式32位固定长度 R型指令寄存器操作 ┌────────┬────────┬────────┬────────┬────────────────┐ │ opcode │ DR │ SR1 │ SR2 │ unused │ │ 4位 │ 4位 │ 4位 │ 4位 │ 16位 │ └────────┴────────┴────────┴────────┴────────────────┘ 用途寄存器间的算术/逻辑运算 例ADD DR, SR1, SR2 → DR SR1 SR2 I型指令立即数操作 ┌────────┬────────┬────────┬────────────────────────┐ │ opcode │ DR │ SR1 │ immediate │ │ 4位 │ 4位 │ 4位 │ 20位 │ └────────┴────────┴────────┴────────────────────────┘ 用途使用立即数的操作 例ADDI DR, SR1, imm → DR SR1 imm符号扩展 J型指令跳转操作 ┌────────┬────────┬────────────────────────────────┐ │ opcode │ SR │ offset │ │ 4位 │ 4位 │ 24位 │ └────────┴────────┴────────────────────────────────┘ 用途跳转指令 例BEQ SR, offset → if SR0, PC PC offset 1 操作码opcode分配 0000ADD加法 0001NAND与非 0010ADDI立即数加法 0011LW加载字 0100SW存储字 0101BEQ相等跳转 0110JALR跳转并链接寄存器 0111HALT停机2.2.3 寻址方式LC-2200 支持的寻址方式 1. 立即数寻址Immediate Addressing 操作数直接包含在指令中 例ADDI R1, R0, 5 ; R1 0 5 5 特点速度最快但操作数范围受限20位有符号数-524288524287 2. 寄存器寻址Register Addressing 操作数在寄存器中 例ADD R3, R1, R2 ; R3 R1 R2 特点速度快操作数在寄存器中 3. 基址偏移寻址BaseOffset Addressing 有效地址 基址寄存器 偏移量 例LW R1, 4(R2) ; R1 Memory[R2 4] 特点用于访问数组、结构体等数据结构 4. PC相对寻址PC-Relative Addressing 有效地址 PC 偏移量 例BEQ R1, 10 ; if R10, PC PC 10 1 特点用于跳转指令代码位置无关 C语言与寻址方式对应 int a 5; → ADDI R1, R0, 5 立即数 int b a c; → ADD R2, R1, R3 寄存器 int x arr[i]; → LW R4, 0(R5) 基址偏移 if (a 0) goto L; → BEQ R1, offset PC相对2.3 数据表示2.3.1 整数表示无符号整数无符号整数Unsigned Integer n位无符号整数可以表示 0 2^n - 1 8位无符号整数示例 00000000 0 00000001 1 01111111 127 11111111 255 二进制转十进制按位权求和 10110101 1×2^7 0×2^6 1×2^5 1×2^4 0×2^3 1×2^2 0×2^1 1×2^0 128 32 16 4 1 181有符号整数补码补码Twos Complement 为什么使用补码 ✓ 加法和减法使用相同的硬件电路 ✓ 0 只有一种表示避免 0 和 -0 的问题 ✓ 符号位自然参与运算 n位补码可以表示 -2^(n-1) 2^(n-1) - 1 8位补码-128 127 求补码的方法 正数与原码相同 负数将正数的二进制取反然后加1 例-5 的8位补码 5 的二进制00000101 取反 11111010 加1 11111011 ← -5 的补码 验证11111011 -1×2^7 1×2^6 1×2^5 1×2^4 1×2^3 0×2^2 1×2^1 1×2^0 -128 64 32 16 8 2 1 -5 ✓ 补码加法示例5 (-3) ? 5 00000101 -3 11111101 和 00000010 2 ✓最高位进位丢弃#includestdio.h#includelimits.hintmain(){/* 有符号整数溢出未定义行为*/intmaxINT_MAX;/* 2147483647 */printf(INT_MAX %d\n,max);printf(INT_MAX 1 %d\n,max1);/* 溢出结果为 -2147483648 *//* 无符号整数溢出定义行为模运算*/unsignedintumaxUINT_MAX;/* 4294967295 */printf(UINT_MAX %u\n,umax);printf(UINT_MAX 1 %u\n,umax1);/* 结果为 0 *//* 查看 -5 的内存表示小端序*/intneg5-5;unsignedchar*bytes(unsignedchar*)neg5;printf(-5 的内存表示小端: );for(inti0;i4;i)printf(%02X ,bytes[i]);printf(\n);/* 输出FB FF FF FF *//* 有符号与无符号混合运算的陷阱 */intsigned_val-1;unsignedintunsigned_val1;if(signed_valunsigned_val){printf(signed_val unsigned_val\n);}else{printf(signed_val unsigned_val-1被转换为大正数\n);}return0;}2.3.2 浮点数表示IEEE 754 浮点数标准 单精度浮点数float32位 ┌──┬──────────┬───────────────────────┐ │S │ 指数E │ 尾数M │ │1位│ 8位 │ 23位 │ └──┴──────────┴───────────────────────┘ 值 (-1)^S × 1.M × 2^(E-127) 双精度浮点数double64位 ┌──┬──────────────┬───────────────────────────────────┐ │S │ 指数E │ 尾数M │ │1位│ 11位 │ 52位 │ └──┴──────────────┴───────────────────────────────────┘ 值 (-1)^S × 1.M × 2^(E-1023) 例将 -6.75 表示为单精度浮点数 步骤1S 1负数 步骤26.75 110.11二进制 1.1011 × 2^2 步骤3E 2 127 129 10000001 步骤4M 10110000000000000000000去掉隐含的1 结果1 10000001 10110000000000000000000 0xC0D80000 特殊值 E0, M0±0正零和负零 E255, M0±∞无穷大 E255, M≠0NaNNot a Number非数 E0, M≠0非规格化数极小的数#includestdio.h#includefloat.h#includemath.hintmain(){/* 浮点数精度问题 */floata0.1f,b0.2f;floatcab;printf(0.1 0.2 %.20f\n,c);/* 不是精确的0.3 *//* 输出0.30000001192092895508 *//* 错误用 比较浮点数 */if(c0.3f){printf(相等\n);}else{printf(不相等浮点数精度问题\n);/* 这行会执行 */}/* 正确用误差范围比较 */floatepsilon1e-6f;if(fabsf(c-0.3f)epsilon){printf(近似相等正确做法\n);}/* 查看浮点数的IEEE 754表示 */floatf-6.75f;unsignedint*bits(unsignedint*)f;printf(-6.75 的IEEE 754表示: 0x%08X\n,*bits);/* 0xC0D80000 *//* 浮点数范围 */printf(float 最大值: %e\n,FLT_MAX);/* 约 3.4e38 */printf(float 最小正值: %e\n,FLT_MIN);/* 约 1.2e-38 */printf(float 精度: %d 位十进制\n,FLT_DIG);/* 约 6 位 *//* 特殊值 */floatinf1.0f/0.0f;floatnan0.0f/0.0f;printf(1.0/0.0 %f\n,inf);/* inf */printf(0.0/0.0 %f\n,nan);/* nan */printf(isinf: %d, isnan: %d\n,isinf(inf),isnan(nan));return0;}2.3.3 字符表示ASCIIAmerican Standard Code for Information Interchange 7位编码128个字符0127 常用字符 0948570x300x39 AZ65900x410x5A az971220x610x7A 空格320x20 \n换行100x0A \t制表符90x09 \0空字符00x00 Unicode 与 UTF-8 Unicode覆盖全球所有语言的字符集 UTF-8变长编码14字节兼容ASCII UTF-8 编码规则 U0000U007F0xxxxxxx1字节兼容ASCII U0080U07FF110xxxxx 10xxxxxx2字节 U0800UFFFF1110xxxx 10xxxxxx 10xxxxxx3字节 例中U4E2D的UTF-8编码 U4E2D 0100 1110 0010 1101 3字节格式1110xxxx 10xxxxxx 10xxxxxx 填入11100100 10111000 10101101 十六进制E4 B8 AD#includestdio.h#includectype.h#includestring.hintmain(){/* ASCII 码操作 */charcA;printf(A 的ASCII码: %d\n,c);/* 65 */printf(A 32 %c\n,c32);/* a大写转小写*//* 数字字符转整数 */chardigit7;intnumdigit-0;/* 7 */printf(7 - 0 %d\n,num);/* 字符分类函数 */printf(isalpha(A): %d\n,isalpha(A));/* 非0真*/printf(isdigit(5): %d\n,isdigit(5));/* 非0真*/printf(isspace( ): %d\n,isspace( ));/* 非0真*/printf(toupper(a): %c\n,toupper(a));/* A *//* 字符串与字节 */charstr[]Hello;printf(字符串长度: %zu\n,strlen(str));/* 5 */printf(数组大小: %zu\n,sizeof(str));/* 6含\0*//* 查看字符串的内存表示 */printf(Hello 的字节: );for(inti0;i5;i){printf(%02X ,(unsignedchar)str[i]);}printf(\n);/* 48 65 6C 6C 6F 00 */return0;}2.4 指令类型2.4.1 算术与逻辑指令/* LC-2200 算术逻辑指令对应的C语言操作 *//* ADD R3, R1, R2 → R3 R1 R2 */inta5,b3;intcab;/* c 8 *//* NAND R3, R1, R2 → R3 ~(R1 R2) *//* NAND 是功能完备的可以实现所有逻辑函数 *//* 用 NAND 实现 NOTNAND R2, R1, R1 → R2 ~R1 *//* 用 NAND 实现 AND先NAND再NOT *//* 用 NAND 实现 OR德摩根定律A|B ~(~A ~B) *//* ADDI R1, R0, 5 → R1 R0 5 5 */intx5;/* 等价于 x 0 5 *//* 移位操作通过加法模拟*/intval3;intleft_shiftvalval;/* val 1 6 */2.4.2 数据传送指令/* LC-2200 数据传送指令 *//* LWLoad Word从内存加载数据到寄存器 *//* LW R1, 4(R2) → R1 Memory[R2 4] */intarr[10]{10,20,30,40,50};int*ptrarr;intval*(ptr1);/* 等价于 LW R1, 4(R_ptr)每个int 4字节*//* 汇编LW R1, 4(R_arr) ; 加载 arr[1] 20 *//* SWStore Word将寄存器数据存储到内存 *//* SW R1, 8(R2) → Memory[R2 8] R1 */arr[2]100;/* 等价于 SW R_100, 8(R_arr)偏移 2×48 字节*//* 完整的数组求和示例汇编伪代码 int arr[5] {10, 20, 30, 40, 50}; int sum 0; for (int i 0; i 5; i) sum arr[i]; ADDI R_sum, R0, 0 ; sum 0 ADDI R_i, R0, 0 ; i 0 ADDI R_n, R0, 5 ; n 5 ADDI R_arr, R0, arr_addr ; R_arr arr[0] loop: ; 检查 i n ADD R_tmp, R_i, R0 ; R_tmp i NAND R_tmp, R_tmp, R_tmp ; 取反模拟NOT ; ... 省略比较逻辑 ... LW R_temp, 0(R_arr) ; R_temp arr[i] ADD R_sum, R_sum, R_temp ; sum arr[i] ADDI R_arr, R_arr, 4 ; R_arr 4下一个元素 ADDI R_i, R_i, 1 ; i BEQ R0, loop ; 无条件跳转R0永远为0 done: */2.4.3 控制流指令/* LC-2200 控制流指令 *//* BEQBranch if EqualSR0时跳转 *//* BEQ SR, offset → if SR 0, PC PC offset 1 *//* 实现 if-else通过减法判断相等 if (a b) { x 1; } else { x 2; } 汇编伪代码 ADD R_diff, R_a, R0 ; R_diff a NAND R_b_neg, R_b, R_b ; R_b_neg ~b ADDI R_b_neg, R_b_neg, 1 ; R_b_neg -b补码 ADD R_diff, R_diff, R_b_neg ; R_diff a - b BEQ R_diff, then_branch ; if a-b 0, goto then ADDI R_x, R0, 2 ; x 2else分支 BEQ R0, done ; goto done then_branch: ADDI R_x, R0, 1 ; x 1if分支 done: *//* JALRJump And Link Register跳转并保存返回地址 *//* JALR DR, SR → DR PC 1; PC SR *//* 用于函数调用 *//* 函数调用示例 int result add(3, 5); 汇编伪代码 ADDI R_a0, R0, 3 ; 参数 a 3 ADDI R_a1, R0, 5 ; 参数 b 5 ADDI R_addr, R0, add_func ; 函数地址 JALR R_ra, R_addr ; 调用函数R_ra 返回地址 add_func: ADD R_v0, R_a0, R_a1 ; 计算 a b JALR R0, R_ra ; 返回跳转到返回地址 */2.5 汇编语言编程; LC-2200 汇编语言示例计算 123...10 的和 .data N: .word 10 ; N 10 result: .word 0 ; 存储结果 .text main: LW $t0, N($zero) ; $t0 N 10 ADDI $t1, $zero, 0 ; $t1 sum 0 ADDI $t2, $zero, 1 ; $t2 i 1 loop: ; 检查 i N通过计算 N - i 1如果为0则退出 ; 简化直接用 BEQ 检查 i N1 ADDI $t3, $t0, 1 ; $t3 N 1 11 ; 计算 i - (N1) ; 如果结果为0说明 i N1退出循环 ; 省略具体的减法和比较实现 ADD $t1, $t1, $t2 ; sum i ADDI $t2, $t2, 1 ; i BEQ $zero, loop ; 无条件跳转$zero永远为0 done: SW $t1, result($zero) ; 存储结果 HALT ; 停机 ; 预期结果sum 12...10 552.6 从高级语言到机器语言编译过程的完整链条 C语言源码.c ↓ 预处理器cpp展开宏和头文件 预处理后的源码 ↓ 编译器cc1语法分析、语义分析、代码生成 汇编代码.s ↓ 汇编器as将汇编代码转换为机器码 目标文件.o包含机器码和符号表 ↓ 链接器ld合并目标文件解析符号引用 可执行文件ELF/PE格式 ↓ 加载器OS将程序加载到内存 内存中的进程 具体示例int c a b; C代码 int c a b; 汇编代码x86-64 mov eax, [rbp-8] ; 加载 a 到 EAX add eax, [rbp-4] ; EAX b mov [rbp-12], eax ; 将结果存储到 c 机器码十六进制 8B 45 F8 ; mov eax, [rbp-8] 03 45 FC ; add eax, [rbp-4] 89 45 F4 ; mov [rbp-12], eax 二进制第一条指令 10001011 01000101 11111000/* 演示从C代码到机器码的过程 */#includestdio.h/* 简单函数计算两数之和 */intadd(inta,intb){returnab;}/* 查看函数的机器码x86-64*/voidshow_machine_code(){unsignedchar*code(unsignedchar*)add;printf(add函数的机器码前16字节:\n);for(inti0;i16;i){printf(%02X ,code[i]);}printf(\n);/* 典型输出x86-64 55 push rbp 48 89 E5 mov rbp, rsp 89 7D FC mov [rbp-4], edi ; 保存参数a 89 75 F8 mov [rbp-8], esi ; 保存参数b 8B 45 FC mov eax, [rbp-4] ; 加载a 03 45 F8 add eax, [rbp-8] ; 加上b 5D pop rbp C3 ret ; 返回 */}intmain(){printf(add(3, 5) %d\n,add(3,5));show_machine_code();return0;}本章小结知识点核心内容重要程度ISA 概念硬件软件接口RISC vs CISC⭐⭐⭐⭐⭐LC-2200 寄存器16个32位寄存器R0永远为0⭐⭐⭐⭐指令格式R型/I型/J型32位固定长度⭐⭐⭐⭐寻址方式立即数/寄存器/基址偏移/PC相对⭐⭐⭐⭐⭐补码表示求补码方法补码加法⭐⭐⭐⭐⭐IEEE 754浮点数格式精度问题⭐⭐⭐⭐编译过程C→汇编→机器码→可执行文件⭐⭐⭐⭐思考题为什么 LC-2200 只有 NAND 逻辑指令而没有 AND、OR、NOT补码为什么能用相同的加法电路实现减法为什么不能用直接比较两个浮点数RISC 和 CISC 各有什么优缺点现代处理器如何取长补短为什么 R0 寄存器被硬连线为 0这有什么好处参考文献Umakishore Ramachandran William D. Leahy Jr.,《计算机系统系统架构与操作系统的高度集成》