期末复习

一.计算系统概述
1.1* 计算机

1.2* 冯诺依曼模型

1.3* 计算机系统

1.4 七层抽象

1.5 从 计算->计算机->计算机系统->计算系统

二.数据的机器级表示
1.整数
1.1 无符号整数 8位的可以表示从0-255
1.2 有符号整数
1.2.1 原码表示:用开头的1表示符号,例如1000表示-0,用1001表示-1
1.2.2 反码表示:通过取反的操作表示负号,比如1000,就是0111的负数,即-7
1.2.3 补码表示:通过取反+1的操作表示符号,如1000表示-8
*补码在ALU中=计算中更加便捷,简单

2.二进制-十进制转化
2.1 整数(有符号)通过除2取余倒序相加,再在最高位补0,就可以得到1原整数的绝对值,如果是负数还需要进行取反加1的操作

3 计算
3.1 补码在计算中不需要考虑最高位的进位
3.2 符号扩展:通过最高位来决定,将需要扩展的位=最高位(如果有两个不同长度的数值做加法,首先必须将他们表示为相同长度)
3.3 溢出:比如2+6=8,超过了-7~7的表示上限,所以会导致溢出
tip:一个正数+一个负数不会溢出;两个符号相同的数相加得到另一个符号的必然溢出

4 小数
4.1 小数的负数也是取反+1
4.2 小数的进制转换(×2正序整数位相加)

5 单精度浮点数
5.1 由32位组成
根据E的不同,决定值的大小,E=0时,指数为只需要-126,其它情况-127,当E=255,如果小数部分为0,则表示正无穷或负无穷,如果小数不为0,则为NaN(not a number)
5.2 例子

6 二进制-八进制-十六进制转换
7 ACSII码用8位表示

三.数字逻辑电路
*1.位运算
1.1 位组合逻辑规则
2. CMOS电路
2.1 两种晶体管

2.2 上负下正

3 德摩根定律

4 组合逻辑电路(不能存储信息,输出只由当前的输入决定,不由任何过去存储在其中的信息决定)
4.1 译码器(只有一个输出为1(对应于被检测的输入组合),其余均为0)

4.2 多路选择器(选择一个输入连接到输出。根据选择线选择哪一个线连接到输出,n条选择线,2^n的输入)
4.3 二进制加法(S表示当前位,C表示进位)

5 时序逻辑电路
5.1 RS锁存器(a为输出,b为a的反)(注意与非门是低电平有效,或非门是高电平有效)

当S=1的时候,a必为0,又因为R=1,所以b必为0,此时再将S=1,因为b=0,所以a和b的值不会发生变化
*当s=r=1的时候,因为a和b输出都为1,不符合互补,所以禁用
5.2 门控D锁存器(为了解决两个问题:1应该什么置位,复位;2 R和S不能同时为0)

5.3 寄存器 (共享WE)

*5.4(状态机:下一个状态是由当前状态和当前输入决定的)

6 存储器(二维阵列:2^n行,每行m位,行:存储单元)
6.1 存储器的地址(多少行);存储器的寻址能力(每行几位)
*一般都是字节可寻址(8位),字是32位
6.2 示例

写数据

WE=1代表写入状态,A代表写哪个寄存器,D代表写入的数据是多少
读数据

WE=0,输出的是上一状态的值

五.运算方法和运算部件(S:sum;C:carry)

  1. 一位加法器

    (中间的符号是异或的意思:规避了全为0和有两个相同的1的情况)
    2.门级延迟(异或门是3级,与或非都是1级)

    3.行波进位加法器(当数值部分最高位Cn-1不等于符号位Cn的时候,溢出)

    涉及进位的时候,默认x异或y已经算出,所以进位间只需要2级门延迟

六.ALU与指令集
1.一些标识位
2.ALU
2.1 单数据总线(三个clock周期:1:从R0写入到LA;2:从R1写入到LB;3:ALU计算结果,写回)

2.2 双数据总线

2.3 三数据总线(不会互相占据,所以1个时钟周期)

3 寄存器的数据存储

七 指令集体系

R型指令

  1. add a0, a1, a2 //a0 = a1 + a2
  2. sub a0, a1, a2 //a0 = a1 - a2
  3. sll a0, a1, a2 //a0 = a1 << a2(低位补0)
  4. srl a0, a1, a2 //a0 = a1 >> a2(高位补0)
  5. sra a0, a1, a2 //a0 = a1 >> a2 (算术右移,高位补原来的符号位)

对于位运算,移动的位数是a2的低五位,逻辑移动都补零,算数移动都补充符号位,对于负数,算术移就是乘除,但是逻辑移不是.

  1. slt a0, a1, a2 //a1 < a2 ? a0 = 1 : a0 = 0
  2. xor a0, a1, a2 //a0 = a1 ^ a2(异或操作,不是幂运算)
  3. or a0, a1, a2 //a0 = a1 | a2
  4. and a0, a1, a2 //a0 = a1 & a2

I型指令

  1. addi a0, a1, 0x5 //a0 = a1 + 0x5
  2. subi a0, a1, 0x05 //a0 = a1 - 0x05

先将imm立即数当中的12位符号扩展到32位

  1. slli a0, a1, 0x05 //a0 = a1 << 0x05(低位补0)
  2. srli a0, a1, 0x05 //a0 = a1 >> 0x05(高位补0)
  3. srai a0, a1, 0x05 //a0 = a1 >> 0x05 (算术右移,高位补原来的符号位)

对于移动,imm不需要扩展到32位,只需要移动imm的低5位就行.

  1. slti a0, a1, 0x05 //a1 < 0x05 ? a0 = 1 : a0 = 0
  2. xori a0, a1, 0x05 //a0 = a1 ^ 0x05
  3. ori a0, a1, 0x05 //a0 = a1 | 0x05
  4. andi a0, a1, 0x05 //a0 = a1 & 0x05

就相当于R指令中a2替换成imm扩展到32位(符号扩展)

  1. lb x10, 0(x1) //将x1的值加上0,将这个值作为地址, 取出这个地址所对应的内存中的值, 将这个值赋值给x10(取出的是8位数值)
  2. lh x10, 0(x1) //从内存中取出16位数值
  3. lw x10, 0(x1) //从内存中取出32位数值

从x1的地址开始,加上立即数,之后的新地址,从这个新地址开始,取出...位的数值,将它符号扩展为32位,存储到x10中.b为字节8位,h为halfword16位,半个字,w为word,为32位

  1. lbu x10, 0(x1) //从内存中取出8位无符号数值
  2. lhu x10, 0(x1) //从内存中取出16位无符号数值

和上面差不多,但是扩展到32位时,补充0.

  1. sb x10, 0(x1) //x1的值加上0,将这个值作为地址, 将x10的值存储到上述地址所对应的内存中去 (只会将x10的值的低8位写入)
  2. sh x10, 0(x1) //只会将x10的值的低16位写入
  3. sw x10, 0(x1) //只会将x10的值的低32位写入

从x1的地址开始,加上立即数后,作为新的地址,将x10中的数值的...位写入新的地址中.

U型指令

  1. lui x10, 0x65432 //得到立即数的高20位,低位补0,立即数范围为:0x00~0xFFFFF

将立即数的高20位,即16进制下的5位,写入寄存器x10当中.

B型指令

  1. beq a1, a2, Lable if(a1 == a2){goto Label;} Lable是任意自定义的标签
  2. bne a1, a2, Lable if(a1 != a2)
  3. blt a1, a2, Lable if(a1 < a2)
  4. bgt a1, a2, Lable if(a1 > a2){goto Label;} 100与Label对应着相同的指令, 实际上在运行时Label会变成pc+xxx
  5. bge a1, a2, Lable if(a1 >= a2)
  6. ble a1, a2, Lable if(a1 <= a2)

上述是有条件的条件分支指令,只能跳转在-1024~1024条指令

  1. jal ra, symbol // 跳转到Symbol中去, 并把ra设置成返回地址 Symbol 可以是自定义的Label ,也可以是某个函数名
  2. jal ra, 100 // 跳转到pc + 100 * 2的地方中去, 并把ra设置成返回地址 pc相对寻址,对应的是位置无关代码(PIC)
  3. jalr ra, 40(x10) //跳转到x10+40 的地方中去, 并把ra设置成返回地址x10+40必须是绝对地址,指向内存中某个确定的地方(往往是函数的开头),非PIC

伪指令

  1. la rd,label 将label里面的东西存储到rd当中.如str="ing",la t1,str
  2. li rd,imm 将立即数存储到rd当中.
  3. j label 跳转到label对应的地址
  4. mv rd,rs1 将rs1中的数据加载到rd中

接近人类语言的一种翻译

一个汇编程序,想要实现功能,就和C语言一样,需要新建变量,需要对变量进行处理,需要储存新的变量,需要输出验证,下面是一些常规的写法,翻译者也不知道为什么要这样做,但是这样做确实是对的

  1. 新建变量: 在.data内,.word代表一个新的变量,通常是int型,也可以是一个数组,如a:.word 3,与int a同义;numbers:.word 1,2,3,4,与int numbers[4]={1,2,3,4}同义..space 是一个空,翻译者不知道是什么意思,但是对输出这个操作有非常重要的作用..string是字符串,字符的特点是只有一个字节.
  2. 写程序:首先要写.text,main:,然后再开始写代码.将变量加载到寄存器内:对于数组和字符串需要两步,第一步la a0,a,就是将a的地址加载到a0中(la加载的是地址,不是a的数值;如果加载的是数组或者字符串,加载的是首位的地址,可以对地址处理,来实现对数组的遍历),第二步是lw a1,0(a0),把a0中对应地址的数取出来,放到a1中,至此,我们就实现了把数据区的数组中的第一项存入一个寄存器.我们可以通过对a0,即地址进行操作,来遍历.对于数组,每次变更4,即addi,a0,a0,4,对于字符串,则变更1.对于一个整数,则只需要lw a1,a即可取出a中的数据并且存入a1中.(翻译者对寄存器了解不深刻,只知道用a...和t...寄存器一般没什么影响)
  3. 获取了数据,就可以对数据进行处理:加减法:可以直接用addi加立即数的方式来实现,也可以li加载一个立即数,然后再用add来实现;乘法,有mul,注意它不能乘以立即数,需要先用li加载立即数.当然,也可以用循环的形式来处理数据.
  4. 循环的基础写法:li a0,10(加载一个立即数),建立子例程: loop:____,loop中必须包括:对循环次数的更替:addi a0,a0,-1,即自减;重复进行循环:在子例程的最后,需要写j loop来跳回循环的开始;对循环次数是否结束的判断:
  5. 判断与分支:常用的判断是:beq a0 x0 label,(x0恒定为0,千万别改变x0),这是一个用来判断a0是否为0的判断,在上述的循环例子中,很明显,这是用来跳出循环的,后面的label写一个循环体外的label即可跳出循环.
  6. 数据的存储:即将一个数据存储到另一个寄存器中.和lw指令相反,这个不多说了.
  7. 至此,已经基本实现了一个基础的,包含循环的汇编程序.我们想通过调用输出,来检验自己写的对不对.下面是输出的写法.在汇编语言中,只有在寄存器a0中的值可以被输出,我们要做的就是把其他寄存器的值加载到a0中,最简单的方式就是(我们假定我们想输出的那个值在t0寄存器中)mv a0,t0,li a7,1,ecall,这三条语句.第一条mv为移动,第二条为输出指令,必须为a7,后面的立即数,为1时输出int型,为4时输出字符串.ecall为系统调用.这是想要输出一个寄存器里面的值,如果我们想要输出一个.data里面的字符串,那么应该la a0,str,即把str的地址加载到a0.如果想输出一个.data里面的int,应该lw a0,a,然后输出.
  8. 结束程序:li a7,10,ecall
  9. 关于字符串类型的特别说明:其实对于字符串的处理就和处理数组是一样的,都是先用la获取地址,根据地址逐项处理.不同的是字符串处理时,地址一次只更改1点,且不能使用lw和sw,而是用lb(lbu)和sb来进行读取/存储

从下面开始是翻译者基本没理解透的东西,但是这样写代码应该是对的

  1. 子例程调用:就和C语言的函数一样,就是新建一个函数,方便使用.那么,既然是个函数,肯定会有参数,汇编中子例程是没有自己的参数的,而是通过在主函数中就已经设定好某些寄存器中的数值,子例程内部直接去处理这些寄存器当中的数值即可.

1.格式:子例程名称label:addi,sp,sp -4n(n>=该子例程使用sw的次数为了保险,我们一般都选择比sw的次数大1的数.),sw ra,0(sp),一系列处理,lw ra,0(sp),addi sp,sp,4n(与上面那个是同一个n),ret.上面是最基础的,不在子例程中使用其他sw的样例.如果在子例程中使用了其他的sw,说明该寄存器在该子例程中被写入其他的数据,那么在子例程的末尾,需要用lw来恢复数据.简单来说,例如:sw a0,4(sp)(意思是将a0中的数据存入a0中,方便空出a0寄存器,存别的内容),那么在ret这条指令前,就要先lw a0,4(sp),即把a0的数值恢复到子例程之前.
2.如何调用子例程:jal ra,label
3.很遗憾我并不知道这是什么原理,但是照着这个格式写,程序确实是对的()切记不要对ra和sp进行额外的操作.

  1. 递归:即在子例程之中调用自身.调用依旧用的是jal ra,label.和调用子例程没有什么比较大的区别.唯一要注意的是,在递归到最基础的情况时,相当于该子例程调用结束,前面的sw的需要用lw恢复,前面对sp的修改在这里要补回来,然后用ret结束该子例程
  2. 上述只是一种基于C语言的翻译,其实如果你能写出C程序,借助上述翻译应该是能写出来一些程序的!快去试试吧(x)
    uploading-image-721087.png

八.CPU中央处理器

  1. 机器指令的执行阶段

    取指令->译码->执行->访存->写回
    2.单周期CPU的数据通路设计
    (好像不是)//
    步骤:1.PC更新,从IR中取指令 2.把指令分割成几个部分 3.控制器接受部分指令,生成控制指令 4.寄存器堆根据部分指令,输出对应的值 5.ALU接受寄存器堆输出的值,根据控制器的控制指令做出判断
    6.写回结果

    Z和X都是临时寄存器,进行PC+4的操作,psw用来比较两者是否相等//

此处开始是
2.1单周期的指令长度由所有通路中最长的数据通路决定

九 机器语言和汇编语言
1 结构化程序设计(选择,循环,顺序)

用MIPS写程序也类似,对变量赋值->写循环or判断or顺序结构
2 MIPS指令
2.1
.align表示地址是2^n的倍数,一般是.align 2
.data表示数据区,后面可以存word(数字),string,byte可以用来存储字符这种(类似数组)
.text表示代码区,后面要跟上.global main 换行 main:
2.2 结构
分为标记,操作码,操作数,注释
2.3 汇编过程如何实现
扫描两次:第一次扫描:记录所有的标签,建立符号表(标签名->地址的映射);第二次扫描:根据符号表生成对应的机器码

十.c语言和汇编
1 高级程序语言的翻译特性以及优缺点
高级语言需要经过(翻译)的过程才能变成机器语言
1.1 解释 :需要解释器读入高级语言编写的程序,执行程序员的指示;解释性语言不是由计算机执行,而是由解释程序执行的(不能一次性解释完整个程序)
优点:容易开发和调试,灵活,迭代快
1.2 编译:一个程序被编译一次就行,由计算机执行
优点:可以产生更高效的代码,能够更有效利用内存,程序执行更快,稳定,性能好
2 c语言翻译成MIPS语言的步骤(C语言编译器会把C语言转化成汇编语言,再把汇编语言转化成机器语言)
四个步骤:预处理->编译(转化成汇编语言)->汇编(转化成机器语言)->链接(将外部函数/多个文件和库文件合成一个可执行文件)

十一,十二.输入与输出
1.主要的输入输出设备(输入:键盘,鼠标,磁盘;输出 :显示器,打印机,磁盘)
2.字符设备(键盘、显示器:一个字符,一个字符的读写),块设备
3.内存映射寄存器(每一个IO设备都被分配给一个存储器地址空间中的地址)
4.IO的轮询模式(为了处理异步问题,需要反复询问KBSR/DSR[0]是不是1,只有1才进行接下来的赋值)
5.操作系统的自陷机制(用户程序通过自陷指令,把控制权交给操作系统;操作系统有适当的特权级别,可以操控硬件寄存器,实现IO行为)
6.用户态,内核态(从用户态进入内核态:syscall;内核态返回用户态:eret)

十三.子例程
1.子例程的调用,返回机制
调用机制:加载子例程的起始地址,加载到PC,保存返回地址(R31为返回地址寄存器);返回机制:将返回地址加载到PC中
2.jal,jr
jal:jump and link 格式:jal target跳转到目标地址,同时自动保存返回地址到r31(PC+4)(常用于调用机制)
jr :jump register 格式:jr $r(某个寄存器)跳转到寄存器保存的地址(常用于返回)
3.调用者,被调用者,寄存器的保存和恢复
(完成寄存器的保存和回恢复,否则子例程可能会改变某些寄存器的值)

(先加载寄存器的地址,再把$8的内容加载到$9,最后把$9加载到$8)
(也可以通过调用者保存寄存器)
*如果子例程又调用了子例程,则只能采用caller-save(调用者保存的策略)

4.栈和栈的操作(注意地址越大代表越靠近栈底)
$29永远指向栈顶,
push(将保存在$9的内容压入栈)
addi $sp $sp -4
sw \(9 0(\)sp)
pop (将保存在栈的数值移除栈并返回$9)
lw \(9 0(\)sp)
addi sp sp 4
5.子例程的递归调用

十四.C语言函数
1.C函数底层实现:内存,寄存器的分配

2.运行时栈(活动记录,栈)
每一次函数调用,分配一个栈帧
3.C函数调用返回的主要步骤

ra:调用者的返回地址;fp:调用者的返回帧指针;s1:为局部变量分配的寄存器;a0:参数/返回值寄存器

4.C函数的递归调用

十五.c语言的指针和数组
1.变量在内存的分配
指针类型变量保存在寄存器中,普通变量必须用栈帧保存(寄存器保存没有地址,在栈里保存有地址)
2.指针和数组的区别
指针变量可以重新赋值,而数组名是一个固定的地址,不能被重新赋值

posted @ 2025-06-16 12:49  Toby0919  阅读(13)  评论(0)    收藏  举报