文章目录
- 前言
- 第一章:基础
- 性能指标计算
- 储存器原理
- 第二章:微处理器管理模式
- CPU工作模式
- 实模式
- 保护模式
- 虚拟8086模式(V86模式)
- 寄存器
- 概述
- GDTR(Global Descriptor Table Registr)全局描述符表寄存器
- LDTR
- IDTR
- TR
- 内存管理
- 任务
- 保护
- 第三章:指令系统
- 寻址方式
- 指令概览
- 第四章:汇编程序开发
- 第五章:子程序设计
- 特殊变量
- 栈帧构造
- 第六章:储存系统与技术
- cache
- 内存
- 辅存
- 第七章:总线技术
- 第八章:接口技术
- 可编程串行通信:8250与16550
- 定时技术:8254
- 第九章:中断技术
- 概述
- 中断处理
- 中断编程:8259
前言
本文用于打印,排版是以紧凑为主,把list直接浓缩变成一段,想看内容详见其他系列文章。具体打印,有CSDN文章打印插件,自己去油猴搜一下即可。
汇编语言笔记——微机结构基础、汇编指令基础
汇编语言笔记——汇编程序开发、汇编大作业
汇编语言笔记——接口技术与编程
北京理工大学汇编语言复习重点(可打印)
第一章:基础
性能指标计算
- 主频。CPU内部的频率,代表CPU的处理速度。
- 外频。CPU和外部(通常指内存)的通信频率,影响通信速度,如果通信速度太慢,会限制性能。
- 倍频。主频=外频×倍频
带宽=频率×位宽÷8。DDR技术下,一个周期可以有两次数据传送,所以带宽翻倍。
储存器原理
- 逆序储存:储存的基本单位是自己,字节内部是从高到低,但是字节之间按照低地址到高地址方式排列
- 最高位/最低位:在任何储存都适用。MSB,Most Significant Bit, LSB ,Least Significant Bit
- 字节,字,双字,四字。
- 数字到储存:0ED66025H在计算机中是25 60 D6 0E。注意前缀,如果第一个是数字,前缀可加可不加,如果第一个是字母,加0前缀和变量名区分。
- 储存到数字:先确定区间,再逐字节倒着写到纸上。
第二章:微处理器管理模式
CPU工作模式
- 通过修改控制寄存器CR0的控制位PE(位0)来实现从实模式切换到保护模式。
- 虚拟8086模式;特权0(最高,OS),1,2,3
实模式
这是比较古老的模式了。1 不管实际地址线有多宽,只使用低20位地址线,即寻址空间为1M,很小。2. 内存分区,用内存段首地址+偏移访问。3. 任何区域都可以被访问,分区但不设禁区 4. 不支持并行
保护模式
现在最常用的就是保护模式了。保护模式基本是把实模式的缺点都优化了。1 使用32位地址线,支持4G内存,PentiumCPU以后扩展到36位,64G内存 2. 内存页式储存,段式储存 3. 提供基于特权级的内存保护机制4. 支持多任务并行 5. 引入虚拟内存(即用磁盘虚拟出内存来,虽然速度慢,但是可以保证很多大内存程序的运行)
虚拟8086模式(V86模式)
类似于虚拟机。实际上运行在保护模式上,只是虚拟出一个实模式。你即使把实模式搞坏了,也只是搞坏了我规定出来的区域。
寄存器
概述
- 通用寄存器。RAX-RDX。RBP,RDI,RSI。R8-R15
- 部分专用寄存器。RIP(指令指针),RSP(堆栈寄存器)
- 标志寄存器。0就是正常情况,1是非正常情况。0是常态,只有发生了特定变化才会变成1。具体:
- D0:进位标志CF(Carry Flag),0代表没有进位(正常情况),1代表执行结果进位
- D2:奇偶标志PF(Parity Flag),Intel微处理器采用奇校验。0代表执行结果的低八位中有奇数个1(正常情况)。比如11011010B有5个1,则PF=0
- D6:零标志ZF(Zero Flag),0代表结果非0(正常情况),1代表结果为0
- D7:符号标志SF(Signal Flag),0代表非负(正常情况),1代表结果是负数。D6和D7配合起来可以精确判断结果是正负还是0。
- D10:方向标志DF(Direction Flag),针对字符串操作指令中地址变化方向。0代表增址(正常情况),1代表减址
- D11:溢出标志OF(Overflow Flag),带符号数运算的时候,不溢出为0,溢出为1
- D21:微处理器标识标志ID(Identification),相当于一个CPU指针,使用CPUID指令可以获取CPU信息,常有软件利用CPUID作为机器码,可以用于保护版权,但是虚拟机出现后这种方法就被破解了。
- 段寄存器。存放数据段基址,涉及到段式管理 1. CS:Code 代码段寄存器 2. DS:Data 数据段寄存器 3. SS:Stack 堆栈段寄存器 4. ES:Extra 附加数据段寄存器 5. GS: 6. FS:
GDTR(Global Descriptor Table Registr)全局描述符表寄存器
- 实模式下,采用两个16位表示20位地址。段寄存器存放段基址的高16位地址,然后加上第二个16位(逻辑地址)就行。
- 保护模式用GDTR和段寄存器寻址。
- 段选择子:保护模式下,16位段寄存器进化为段选择子,13位index用于选择,1位T1(区分GDT和LDT),2位RPL。
- 段描述符:8字节,有32位段基址,对应4G寻址空间。还有段限长,段属性字段。
- GDTR:32位基址,16位限长,指向的GDT中最多存放 2 13 2^{13} 213个段描述符,正好与段选择符的13位index对应。
- 寻址过程:1. 寻找GDTR。使用LGDT(Load GDT)指令将GDT的基地址装入GDTR,前32位就是基址,同时后16位还可以确定GDT有多大,可以存多少个段描述符。注意,是后(16位+1)/8个段描述符。之所以要+1,是因为限长为0总得有意义吧,所以就统一加一,这样限长为0代表长度实际是1,最大0xFFFF实际是0x10000,还能凑个二进制整。2. 寻找段描述符。使用段选择子,生成段描述符在GDTR上的偏移量(要×8),用这个偏移量+GDTR基地址就是段描述符基地址。3. 寻找内存段。读取8字节的段描述符,用32位找到内存基地址,搭配其他32位辅助信息使用这块内存。
- 例题:0 E003 F000 3FFH,对应基址0E003F000H,长度为3FFH+1,容纳数量为(400H/8)=80H,算的时候转二进制算。
LDTR
- LDTR中不储存LDT地址,而是存了一个段描述符。
- 寻址流程:1. GDTR确定了GDT的位置和大小 2. 用LDTR作为段选择符,选择GDT中的一个LDT描述符 3. 用LDT描述符确定LDT的位置和大小。 4. 用段寄存器在LDT中选择内存的描述符 5. 用LDT中的段描述符寻找物理内存
IDTR
- 实模式用中断向量表,地址为0H。保护模式用IDT,可移动。
- 保护模式IDTR有48位,同GDTR。但是最多256中断,所以浪费了很多空间。不需要搭配段选择子(段选择子本身只是用来在GDT或者LDT中选的),系统有其他机制。
TR
TSS,任务状态段。TR是一个段选择子,也是在GDT上选TSS描述符。但是TR是16位全用了,所以不需要乘8,直接和GDT基地址相加即可。
内存管理
- 实模式:分段,段地址*10H+offset。每一个内存段的首地址必须是16的倍数,而且段的最大长度只能是64K(因为16位偏移量的限制)
- 保护模式:1. 16:32位地址 2. 段选后,基址+偏移量形成32位线性地址 3. 如果分页,则经过分页部件转换为32位物理地址,不用分页则线性地址=物理地址。
- 段选:CS:EIP整体寻址流程(假设T1=0,默认GDT)1. CS:EIP是虚拟地址 2. GDTR锁定GDT表的位置和大小 3. CS的Index从GDT中选一个段描述符,从里面解析出内存中对应的32位区域基址与大小 4. 解析后的基址+EIP偏移量就是线性地址,如果不进行分页,那就是物理地址。
- 分页:(一个页描述符是4字节,所以×的时候是4):1. 前10位为页目录索引。PDBR的全部+页目录索引×4,得到页表描述符。 2. 中间10位为页号。页表描述符前20位基址+页号×4,得到页描述符。3. 最后12位(4K页大小)为页内偏移。页描述符前20位基址+页内偏移,得到物理地址。
- 段描述符结构:
- 段限长+1才是真正的限长。
- S(System),S=0代表系统段描述符,用户不可用,1代表代码段、数据段、堆栈段,用户可用
- E(Executable),在S=1的前提下,E=1代表代码段,可执行,E=0代表数据段、堆栈段,不可执行
- DPL。特权级,格式同RPL
- G(Granularity),粒度,为限长的单位,G=0是默认的Byte字节为单位,G=1以页为单位,一页占用
2
12
2^{12}
212,4KB的空间
任务
- TSS是任务状态段,GDT有TSS对应的8字节描述符。
- 门用于转换,比如不同任务之间的调用,比如用户程序调用系统程序,转换过程中会考虑特权级。调用门对应调用,任务门用于任务切换。
保护
- 数据访问的保护:DPL ≥ MAX(CPL, RPL); CPL是当前正在运行的程序的特权级(CS);DPL是段描述符特权级;RPL是段选择子里的请求特权级。
- 对程序的保护:段间调用或跳转,需要检查限长,特权级CPL和DPL。CPL=DPL,允许跳转和调用。CPL<DPL,禁止。CPL>DPL,此时要检查目标代码段描述符的C位。如果C位为1,表示这是一致代码段,允许跳转和调用。(很多系统调用的C就是1)
- 对输入输出的保护:略
第三章:指令系统
寻址方式
默认DS:偏移。下图,比例变址是基址:偏移,其他都是仅写偏移的写法
指令概览
- 注意:指令 dst,src。别搞反了。
- 数据传送指令:MOV(1. 常数肯定不能被赋值 2. 不可以随意用常数指定段寄存器,至少应该先送到寄存器中(段寄存器不可以给段寄存器赋值)3. CS是代码段,不可写 4. 两个内存不能传)、PUSH(至少16位)、POP、XCHG、IN(寄存器必须是累加器,如果端口号超过1字节要用DX存)、OUT、LEA、PUSHF(TODO浮点数)、POPF
- 二进制运算指令:ADD(CF=1的时候,代表无符号数溢出,OF=1的时候,代表有符号数溢出)、ADC(带进位(DST)+(SRC)+ CF → DST)、INC、SUB、SBB((DST)-(SRC)-CF → DST)、DEC、CMP(dst-src,不影响dst)、MUL(MUL SRCreg/m,字节型乘法:(AL)×(SRC)8→AX ,字型乘法: (AX)×(SRC)16→DX:AX,双字型乘法:(EAX)×(SRC)32→EDX:EAX)、IMUL、DIV(具体操作:余数在高部分,商在低部分 1. (AX)÷(SRC)8 2. (DX:AX)÷(SRC)16 3. (EDX:EAX)÷(SRC)32)、IDIV
- 逻辑运算指令:AND(按位与,影响dst)、OR、NOT、XOR、TEST(不影响dst)
- 移位指令:SHL(SHL dst,CNT。CNT是1就直接写,不是1就存CL后再移动(286以后的可以直接写立即数))、SAL、SHR、SAR、ROL(循环)、ROR、RCL(带进位循环)、RCR(普通循环是直接把移出位弄到补充位上了,带进位的是先把这一次移出的位放到CF,然后把上一次的CF放到补充位上。是用CF做一个缓冲)
- 程序控制指令:JMP(直接的短转移和近转移都是相对寻址,其他的都是赋值。注意赋值顺序是从低到高。比如JMP 12 34 56 78实际上对应78 56: 34 12。),JXX(下图)、循环指令(LOOP:短转、CX,先减后判断,0比较特殊)、子程序指令:CALL、RET、RET n、中断指令:INT n、IRET(中断处理程序中的返回)
注意有符号和无符号。对于有符号数跳转,用G和L,对应Greater和Less。
对于无符号数跳转,用A和B,对应Above和Below。
- 标志操作指令:STX/CLX命令。X可以是D(对应DF),C(CF)。CL是清0,ST是置1。NOP(空指令)
- 串操作指令:思路(先确定两个段基址,然后确定两个段起始偏移。之后确定重复次数,重复搬运。),DF(修改SI,DI指针,方向由DF决定,0就是正常,1就是反向)、指针(每次移动举例由MOVSX的X决定,如果是MOVSB使SI、DI各减1)、REP、MOVSB/W/D(内存间搬运)、STOSB/W/D(STOS配LODS,先弄到累加器,处理后再搬运到目的地)、LODSB/W/D、CMPSB/W/D、SCASB/W/D
例:把自AREA1开始的100个字数据传送到AREA2开始的区域中。
MOV AX,SEG AREA1)//先把段基址以AX为介质,加载到DS,ES中
MOV DS,AX
MOV AX,SEG(AREA2)
MOV ES,AX
LEA SI,AREA1 //将段偏移初始化
LEA DI,AREA2
MOV CX,100 //循环次数
CLD //保证DF=0
REP MOVSW //重复移动,每次移动一个word
;100个字数据传送完毕后执行下一条指令
例:把自NUM1开始的未压缩BCD码字符串转换成ASCII码,并放到NUM2中,字符串长度为8字节。设DS、ES已按要求设置。
LEA SI,NUM1
LEA DI,NUM2
MOV CX,8 //循环8次
CLD //确保DF=0
LOP:
LODSB //取Byte到AL
OR AL,30H
STOSB //把AL送到DST
LOOP LOP
第四章:汇编程序开发
- 这一章线上不好考,能看懂代码就行。
- 注意浮点运算。访问的时候用st(i)就相当于使用索引为i的寄存器。需要注意的是,这是一个栈,而不是数组,比如你st(0)储存了1.2,此时你再push进来一个数2.3,则1.2就会被挤到st(1)的位置,而st(0)永远代表栈顶。
- 数据传送(1. FLD与FST。入栈用FLD(load),出栈用FST(store)。2. FSTP。FST不等同于pop,只是将数据store到内存,并没有pop操作,所以FSTP相当于FST+POP。3. FLDPI。数据传送有一种特殊的情况,就是我们要将一些特殊常数传入栈中,比如π,我们肯定不能手写,必须用特殊指令:FLDPI加载到栈中。还有一些类似的无理数,略过。)
- 算数运算指令。(指令看起来很复杂,其实就是加减乘除4大类二元运算,就是在整数指令前加个F,每一类有5种写法:1. FADD dst,src。这是最常用的,相加,送到dst中。2. FADD src。这是次常用的,默认将结果送到s(0)中 3. FADD和FADDP。这两个都会执行pop,将新的结果送到栈顶,也就是原来的st(1))
- 超越函数指令(FSIN,可以将st(0)变成sin(st(0)),sin,cos,tan,atan写法都一样)
第五章:子程序设计
完整定义:子程序名 PROC [C | stdcall] :[第一个参数类型] [,:后续参数类型]
特殊变量
PUBLIC 名字[,…]
EXTRN 变量名:类型[,…]
子程序名 PROTO [C | stdcall] :[第一个参数类型] [,:后续参数类型]
- PUBLIC和EXTERN用法:(1. public和extrn必须在data区之前就写好。2. public可以对变量和函数使用,都不需要带类型。注意,变量必须是data区里的变量(有点像c语言中的全局变量),绝对不能是子程序里的局部变量 3. extern只能对变量使用,不能用于函数(是不能还是像C语言一样默认extrn?TODO)4. 如果想调用其他文件的函数,就是用PROTO方法声明。注意,声明用PROTO(函数原型),定义用PROC。)
- 反汇编后的三类变量名字的特点:(1. 全局。C语言直接声明,汇编中用_i1这种带下划线的标号,且要public声明 2. 静态全局。C语言中加static,汇编中用i2这种正常标号,放在程序data段 3. 局部变量。C语言中在函数中声明,汇编中使用ebp进行偏移寻址。)
栈帧构造
- 栈帧构造,是先构造参数,之后压入返回地址,然后压入ebp与其他被调用者保护,最后开局部变量空间。下图代码基本展示了这一过程。图中从29开始到36,这几句是栈上的局部变量区初始化过程。其他的就都是我们上面描述的。
- 最开始push ebp(被调用者保护)。需要特别注意的是那句mov ebp,esp,ebp是esp刚将ebp压入后的值,esp后面开了内存以后还会移动。我们在程序中,取参数,取局部变量都是要用ebp的,而不是esp,esp随着栈帧结构变化而变化。最后子程序结束,要恢复esp的时候会mov esp,ebp,将esp恢复到刚压入ebp的时候,之后再pop掉栈里的ebp(被调用者保护恢复)与返回。
- 如何用ebp取参数和局部变量呢?首先要明确栈的方向,地址减小方向是栈顶方向,也是局部变量方向。
- ebp+n是取参数,n越大,代表参数越靠后。注意,ebp取参数要跳过被调用保护寄存器和IP指针。在前面的很多程序中,IP指针一般是压EIP,占4字节,还有一个保护EBP,总计8字节,所以取的第一个参数通常是ebp+8。
- ebp-n是取局部变量。奇妙的是,局部变量也是n越大,代表声明顺序越靠后。
- 举个例子分析一下顺序问题:假设有两个局部变量,两个参数,都是DWORD,那么第一个参数就是ebp+8到ebp+11,第二个参数是ebp+12到ebp+15。第一个局部变量是ebp-4到ebp-1,第二个局部变量是。ebp-8到ebp-5。总之就是,越靠近ebp的,就越是先定义的,绝。
第六章:储存系统与技术
cache
- 局部性原理:那小小的cache为什么能支撑如此频繁的访问呢?原因就在于局部性原理:1. 时间局部性。相邻的时间内,程序会集中访问一个数据。2. 空间局部性。程序更倾向于访问空间上相邻的区域。
- cache映射:全相联映射的做法是,遍历目录表(内存和Cache存放数据本身,而目录表存放映射关系,记录了内存中的哪些数据块被存到cache中了。),看看M是否在目录表中,有的话就是hit,直接把M的内存地址转换成cache中的地址。之后再加上偏移量就是我们要的数据。如果M没有记录在目录表中,就是miss,此时就去内存中找数据。
- cache替换:cache的miss和缺页中断类似,发生miss后cache会从主存调块。如果cache中没有空闲的行,就会进行替换。替换方法同操作系统中缺页中断的替换方法,比如FIFO,LRU等,还有一个额外的随机算法。
- cache一致性:
- 单核。 - 直写式。写cache的同时,写内存。缺点在于,写完内存之前,cache不能用,会拖慢写入速度。 - 回写式。一个dirty位,1代表cache的数据被写过(修改过),则这一行被交换出去的时候需要先写一次内存,把cache的修改同步到内存。如果没修改过,那就不用同步内存。回写式比较快,但是机制比较复杂。
- 多核。MESI原则(1. I(Invalid)。无效缓存段。仅在内存中,还没有cache使用这个数据段。2. S(share)。共享缓存段。一个内存块在多个cache里都有拷贝,多个处理器共享。3. E(exclusive)。独占缓存段。一个内存块仅被一个cache使用,且没有被修改。4. M(modify)。修改缓存短。一个内存块仅被一个cache使用,且已经被修改。退出M状态前要先进行回写。),读写规则(1. 独占可写。当数据处于M和E的时候,才可以写。2. 共享只读。数据处于S时,只读。只要不让多个CPU同时写,就不会出现不一致问题。)
内存
- 存储单元数量=行数×列数×数据深度×L(Bank的数量)。注意,算出来的是bit,变Byte得÷8。
- 带宽=总线宽度×总线频率/8(B/s)。SDRAM是普通内存,就用带宽公式即可。但是DDR SDRAM一个时钟的上升沿和下降沿都可以触发信息传输,所以速度翻倍。
辅存
- 硬盘类型:1. 机械硬盘HHD。使用ATA标准( - PATA接口(Parallel ATA)。我们俗称的ATA接口就是PATA接口,速度慢,抗干扰差,逐渐被取代。 - SATA接口(Serial ATA)。串口支持热插拔 - IDE接口。这是个很大的概念,有时指代PATA,有时候指代SATA - SCSI接口。淘汰。)2. 固态硬盘SSD。接口与HHD相同,但是采用FLASH介质,读写速度不受储存位置影响,很快。但是FLASH材质坏块无法修复,且有擦写次数限制,寿命有限。
- 总空间计算:S从1开始,其他从0开始。总共有nC×nH×nS个扇区,乘以扇区大小就是总空间。
- LBA和CHS互转,这里的除是C语言中的整除:
L = [ ( C × n H + H ) × n S ] + S – 1 S = L % n S + 1 H = ( L ÷ n S ) % n H C = ( L ÷ n S ÷ n H ) \begin{gather} L=[(C×nH + H)×nS]+S–1\\ S =L\%nS+1 \\ H =(L÷nS)\%nH \\ C =(L÷nS÷nH) \\ \end{gather} L=[(C×nH+H)×nS]+S–1S=L%nS+1H=(L÷nS)%nHC=(L÷nS÷nH) - NCQ(全速命令排队)技术:如果磁盘按照FIFO算法去寻道,可能会花费很久的时间。NCQ技术对请求序列重新排列,使得数据传输更快速,磁头移动的路径更短
第七章:总线技术
- PCI:常用于计算机内部,连接IO控制中心。
- PCI-E:也是连接IO控制中心,但是不用争夺带宽,速度更快
- USB:分123代,1代速度慢,2代三挡速度,通过D+D-线切换档位,3代全双工。
- OTG:无主机情况下,通过ID引脚确定主从关系,接地的是主设备。通过电阻扩展功能。
- I2C:结构简单,两根线,设备挂上去要区分主从,速度慢。
第八章:接口技术
可编程串行通信:8250与16550
- 从硬件上来说,数字0和1有两种标准,两种标准可以通过器件相互转换:
- RS-232C。用正负电压区分1,0。负电压是1,正电压是0,-15到-5是1,5到15是0。比较奇怪,负电压反而是1。
- TTL。现在广泛使用,用高低电压区分1,0。0-0.4V为低电压,2.4到5V是高电压。这个就很正常,高就是1,低就是0。
- 异步串行帧:起始位0(必须有),字符编码,校验位,停止位。信息传输效率=有效数据传输率=字符编码长度/总长度
- 奇偶校验:1. 奇校验: 连同校验位使得所有位上的1相加为奇数 2. 偶校验:连同校验位使得所有位上的1相加为偶数
- 波特率:信息传输频率。因为串行接口以bit为单位,所以波特率=比特率。
- 8250编程:
- 000(0)。这个端口作用挺多的,而且还会和DLAB形成新的组合( - 如果DLAB=0。那么000就用于收发数据,分别对应RBR和THR。 - 如果DLAB=1。那么000就用于设置波特率,对应DLL(除数寄存器的低字节))
- 001(1)。我们只用他来设置波特率,此时DLAB=1,对应DLM(除数寄存器的高字节)
- 011(3)。LCR寄存器,用于设置异步通信帧格式,比如用几位停止,奇偶校验等。里面还有一个DLAB位,用于设置波特率。
- 101(5)。LSR寄存器,用于获取线路的状态,比如发过去的消息是否被另一端接受,线路里是否有另一端发过来的消息需要接受。
- 信息收发程序:
- 格式设置:对着LCR表就行。
- 波特率设置:f工作时钟 = f基准时钟 ÷ 除数锁存器 = 波特率 × 16,除数寄存器值=115200÷ 目标波特率。注意对着表,区分好波特率高低位。
定时技术:8254
- 编号:A1A0可以有4个排列,编号012对应3个计数器,编号3是控制寄存器。
- 初始化计数器:计数器2,工作方式4,BCD码,初值30:则控制字:10(计数器2),01(只使用低8位),100(工作方式4),1(BCD)
MOV AL,10011001B ;写控制字
OUT 43H,AL
MOV AL,30H
OUT 42H,AL ;写BCD码
- 读取数字:假定8254端口基址为40H,编写程序锁存并读取计数器0的计数值。1. 控制字:00(计数器0),00(锁存),011(工作模式与前面设置的保持一致),0(进制与前面设置的保持一致)2. 写入控制字到43H,实现锁存。3. 从40H读取两次( - 第一次读取低字节到AL - 把AL转移到AH里,准备下一次读 - 第二次读取高字节到AL,此时低字节在AH - XCHG指令交换高低字节)
- 工作方式:
- 0(计数结束中断方式)。从初始值减到0后就停止,不会重新开始。新的初始值立即生效。
- 1(可编程单稳态触发器)。新的初始值要等这次结束。
- 2(脉冲波发生器,分频器)。初始值会自动装填。
- 3(方波发生器),和2的频率相同,只是波形不一样
- 45(略)
- 分频:供一个频率为10kHz的时钟信号,要求每隔100ms采集一次数据。1. 先计算分频系数。对于一个10kHz时钟信号,其周期为1/10kHz=0.0001s=0.1ms。需要对它进行分频,生成一个周期为100ms的信号,频率为10Hz。所以要缩小1000倍,计数值为1000。2. 控制字=00(计数器0),11(16位,放1000),010(工作方式2),0(2进制)3. 先把控制字写入203H(编号3)端口4. 之后把DX切为200H(编号为0),依次对计数器0写入低八位,高八位。
- 级联分频:输入脉冲频率为10kHz,要产生周期为100s的定时信号(频率为0.01Hz)1. 计算分频系数。N=10k/0.01 =1000000。超出65536,采取级联方式,设N1=4000,N2=250 2. 下面的程序仅仅是对两个计数器进行初始化操作,级联相关的操作略过,不做要求。( - 注意端口,203是控制字,200是计数器1,201是计数器2。 - 这里采用BCD格式,比较直观。)
第九章:中断技术
概述
- 分类:
- 外部中断——狭义中断(Interrupt)。有的中断可以被屏蔽。( - 可屏蔽中断:INTR。根据某个标志位决定是否屏蔽此中断 - 不可屏蔽中断:NMI。)
- 内部中断——异常(Exception)。当指令执行期间检测到不正确操作,就会引起异常,异常不能被屏蔽(都错了还屏蔽个锤子)。INT n指令和INTO都是软中断,属于异常。( - 故障(Fault)。可以排除的问题 - 陷阱(Trap)。软中断指令INT n就属于陷阱 - 中止(Abort)。严重问题,不可恢复,直接终止,重启才行。)
中断处理
- 实模式:给出一个实例中断向量表,使用INT 8H指令调用8号中断。因为4字节一个中断描述符,所以实际的地址为8H×4=20H。
从20H取4个字节,倒着排列,即02 0E:07 46
,这就是我们中断处理程序的入口地址。
- 保护模式:升级为IDT,参考IDTR的用法。会有很多检查。
中断编程:8259
- 8259结构:略,但是需要理解
- 工作流程:1. 当一条或多条中断请求线IR0~IR7变高时,设置相应的IRR位为1;2. PR先根据IMR进行判断,有屏蔽的就直接屏蔽。3. PR之后再根据优先级请求中断服务,4. CPU响应中断时,送出中断响应信号INTA,响应第一个INTA信号时,将当前ISR中相应位置位,并把IRR中相应位复位。5. 响应完以后,再次发出信号,将ISR中对应位置0。
- 编号:8259的编号只有0和1。所以需要搭配其他位,但是我们这里编程用不到,所以只关注01即可:
- 0:ICW1和高于1的OCW(OCW2,OCW3)
- 1:OCW1和高于1的ICW(ICW2,ICW3,ICW4)。
- 编程:其实写法都是一样的,和LCR相同,都是控制字的写法。写的时候注意好端口号就行。还有主从,别搞反了。
OCW1=0010 0100B=24H
MOV AL,24H
OUT 21H,AL