地址线经过设备选择电路发出设备选择信号----> 启动命令经过命令译码器后令D(完成触发器)为0,B(状态触发器)为1----> B向设备发出启动命令,设备开始启动 --->设备工作结束返回数据到DBR,同时改变B和D为0和1---> D为1后,和MASK(屏蔽触发器)一起经过与非门和非门,将电流输送到INTR----> 多个INTR(同时工作)经过排队器排队后,输送到设备编码器--->设备编码器,给出中断程序首地址---> CPU收到中断指令之后开始执行中断程序。
默认EINT为0,不允许执行中断程序,当CPU查询到有中断指令且优先级高于现执行程序,则将EINT调为1,开始执行中断程序,执行完毕后调用指令恢复,或者物理硬件反弹。
在一条指令执行阶段结束前,CPU会检测是否有中断指令,如果有就开始执行中断程序
这里保存原程序数据的方法有很多种,包括使用入栈指令将数据压入保护程序、或者将数据保存到其他暂时空闲的寄存器,或者保存到主存之中。
A、单重中断:当有中断程序正在执行时,如果有优先级更高的中断请求,仍然执行本中断程序。
B、多重中断:当有中断程序正在执行时,如果有优先级更高的中断请求,允许程序被中断后执行新的中断程序。
C、两重中断的区别:
宏观上看,I/O接口和CPU是并行工作的,主要体现在(I/O接口在启动外部设备时,CPU仍然在运行主程序)
从微观上看,当外部设备准备完成后,还是要通过中断指令占用CPU,主进程还是要被搁置,故又是串行化的,还显劣势。
DMA方式将CPU从外部设备和主存的数据交换中解脱出来。从前面我们可以知道,程序查询方式和程序中断方式都必须要用CPU一个寄存器作为媒介,传递数据,这样CPU就参与到外部设备和主存的数据交换中,严重浪费CPU的性能。
这种方式虽然简单,但是有很大弊端。当DMA占用主存后,CPU因为失去主存占有权,就处于不工作状态或保持状态(指令缓冲区中有指令但无法从主存中获取想要的数据),DMA占用主存后,因为数据的传输不是一次传输完成的,而是要一次一次传输,所以中间有很长时间,DMA方式是对主存的占用是处于空闲状态的,这严重浪费了CPU的性能。
这种方式是DMA在数据准备好后,发出总线占用请求,占用总线一个访存周期,数据传输完毕后,放弃总线使用权,还给CPU,这样就充分利用了总线和主存,减少了主存对CPU性能的消耗。
在DMA发出总线占用请求时会出现三种情况:1、总线闲置,直接占用 2、CPU正在占用主线,要等待CPU数据访存完毕。 3、CPU和DMA同时申请占用,则CPU要让出主线占用权(因为DMA通常连接的都是高速外部设备,如果等待时间过长,容易造成数据丢失。)
下面是这种方式的时间对比图:
我们将DMA和CPU分成等分的工作周期,DMA和CPU交替占用工作周期进行工作。这样做避免了DMA和CPU频繁交换总线和主存的占用权(提出占用申请和返回同意申请也是需要时间的),这样也就节省了一些时间(这样不需要申请占用和归还主线及主存的使用权)
分析:
设备将数据存入BR---> 然后向控制逻辑单元发出请求信号---> 控制单元收到请求信号后,发出总线占用请求--->如果CPU未占用,则获得总线控制权 (CPU给出总线允许信号HLDA)---->AR接入总线中的地址线生效,存入位置被选中---->控制单元返回DMA正在传输数据的信号给外部设备--->控制单元控制数据线将BR中数据根据AR提供的地址,存入主存当中--->储存完毕后增加WC(字计数器)的值,改变AR(使指向下一个地址)--->最后检查WC,中断机构根据自身中断触发器判断是否发出停止DMA服务的请求信号(中断请求)。
CPU预处理阶段,其会将DMA所需要的连接的设备码(DAR)发送到DMA的DAR寄存器、将主存首地址(AR)发送到DMA寄存器、将WC发送到WC寄存器中;上面用作DMA的初始化前提;
DMA数据传送阶段:首先发出总线占用请求,判断是否可占用--->如果可占用,首先将自身AR保存的主存地址发到总线--->然后将数据送I/O设备(或主存)--->单次数据传送完毕后,修改AR指向下一个要保存(或取出)的内存单元,修改字计数器 --->最后检查字计数器看数据是否全部传输完成--->如果没有就再次进行传送数据流程,如果已经结束,就向CPU申请程序中断
后处理阶段:CPU收到DMA程序中断指令之后,执行中断服务程序,结束DMA的工作状态。
多个DMA接口共享一条DMA请求线连接到CPU,当CPU收到DMA请求时,会按DMA响应电路依次查看接口是否发出DMA请求,先查询到的接口获得总线访问权限。(离CPU越近的DMA接口越占优势。
这种方式每个接口都有一个独立的请求线,这样CPU在收到请求后,根据自己的算法或优先级设置给出主存和总线控制权限(通过响应线来响应) ,这种方式更加灵活。
程序中断方式 | DMA方式 | |
数据传送 | 程序(CPU调用指令来传送) | 硬件(由DMA中控制单元控制传送) |
响应时间 | 一个指令执行完毕 | 一个存储周期(CPU一个存储周期后即可获得主存和总线的控制权) |
处理异常情况 | 能(中断程序出错后亦可以被中断) | 不能(数据传输过程,由硬件控制,没有中断机制) |
中断请求目的 | 为了执行中断程序传递数据 | 为了执行中断程序,进行数据传递完毕后的后处理工作 |
优先级 | 低(中断方式一般连接低速设备) | 高(DMA方式,一般连接高速外部设备) |
当CPU需要外部设备输入或输出时,在预指令执行阶段,将选择的设备的设备码存入DMA的设备地址寄存器中---->这样在DMA启动的时候,就只有一台外部设备能够进行数据交互。
根据存储单元给定位数决定无符号数位数,如果是在寄存器中保存,那么寄存器的长度就反映了其能储存的数据范围,如给定寄存器位数为8位,那么数据范围就是0-255(8个0---8个1);
我们需要一位用0/1来代表正号和负号,还需要一位来代表小数位置(小数没有固定硬件表示)
如: +1101 机器码就是 01101; -1101 机器码就是 11101;+0.1101 机器码就表示为 0 1101(紫色代表小数占用位); -0.1101 机器码表示就是 1 1101(紫色表示小数位); +1101就表示为 01101 这种情况,就是整数,小数位在最后。
优势:表示简单易懂;
不足:造成计算单元更复杂,比如同样是加法运算,就分为下面四种情况
加法运算可能情况 | 结果 |
---|---|
两个正整数 | 加法 正数 |
一正一负 | 减法,根据绝对值情况而定,结果可正可负 |
两负 | 加法 负数 |
一负一正 | 减法 等同于第二种情况 |
这中情况的出现时因为加数有可能是负数导致的,那我们有没有一个数能够代替负数等价地去直接进行加法运算呢?
补码表示法解决了这个问题。
钟表只有12个小时,指针会不断旋转指向不同的值,这时如果我们这时时针指向6,那么我们要让6变为3有两种方法:
+1011的补码是+1011 + 10000 = 11011,因为四个数据位,还是1011(正数的补码是其本身)。
-1011的补码是-1011 + 10000 = 0101(负数的补码是正数)
为了区分到底是负数还是正数的补码,我们在他们前面加上0或1来区分(0为正数的补码,1为负数的补码)
我们用2^5来计算两个数的补码: 100000 -1011 = 010101,因仅保留五位,故为1,0101;100000 + 1011 = 101011,因为是保留五位,故为0,1011。
可见最高位是1则是负数的补数,最高位是0则是正数的补数,故可得出如下公式:
整数补码公式:
小数补码公式及例子:
这里注意如果x为-1.0000时,其补码为1.0000,不能表示原码,因为1占用了符号位。
原理:正常来讲:100000 - 1011 = 11111 + 1 - 1011 = 11111 - 1011 + 1;
原理:10.0000 + 0.1011 = 10.1011 因位数仅5位,固为0.1011;
原理:10.0000 - 0.1011 =1.1111 -0.1011 + 1 = 1.0101;
真值为负时,只需要将[x]补 保留符号位不变,其他取反加一,得到[x]原,例如0,1011(补码) ==》0,0101(原码)
真值为正时,原码与补码完全相同。
+0和-0的补码相同,原码不同。
原理:例如真值为+0.0000 补码为 0.0000,因为0.0000 + 10.0000 =10.0000(截去1,还是原值),负0同理。
反码获取只需要将原码符号位保留不变,正整数反码跟原码相同,负整数保留符号位不变,其他位取反(不用加1)
因为不用加1,相当于模数减1。
例子:-0.1010的反码:(2-2^(-4))-0.1010= 1.1111 - 0.1010 = 1.0101;(实际就是x的补码的最后一位减1)
取个别值分析:
设:[y]补 =y0.y1 y2 y3 y4....
情况一:[y]补 =0.y1 y2 y3 y4...
[y]补 =[y]原 ===>y = 0.y1 y2 y3 y4... ===> -y=-0.y1 y2 y3 y4 y5...
===>[-y]补=1.y1 y2 y3... + 2^n;( 字体表示取反)
情况二:[y]补 =1.y1 y2 y3 y4...
[y]原=[y]补 (排斥符号位)取反末位加1 = 1.y1 y2 y3... + 2^n
===> y=-(0.y1 y2 y3... + 2^n )==> -y=0.y1 y2 y3... + 2^n
===再次数值位取反===>[-y]补 = 0.y1 y2 y3 y4 y5... + 2^n
十进制 | 二进制 | 补码 |
+21 | +10101 | 0,01011 |
-21 | -10101 | 1,01011 |
+31 | +11111 | 0,00001 |
-31 | -11111 | 1,00001 |
0,01011 < 1,01011 (错误)因为+21 > -21; 0,00001 < 1,00001 因为+31 > -31;
我们可以发现,正数的符号识别位为1,负数的识别位为0。这样就是解决了大小比较问题。
十进制 二进制 | 补码 | 移码 |
+21 +10101 | 0,01011 | 1,01011 |
-21 -10101 | 1,01011 | 0,01011 |
+31 +11111 | 0,00001 | 1,00001 |
-31 -11111 | 1,00001 | 0,00001 |