• 操作系统开发系列—8.时钟中断


    外部中断的情况复杂一些,因为需要建立硬件中断与向量号之间的对应关系。外部中断分为不可屏蔽中断(NMI)和可屏蔽中断两种,分别由CPU的两根引脚NMI和INTR来接收。如下图所示:

    可屏蔽中断与CPU的关系是通过对可编程中断控制器8259A建立起来的。8259A可以认为它是中断机制中所有外围设备的一个代理。在BIOS初始化它的时候,IRQ0~IRQ7被设置为对应向量号08h~0Fh,在保护模式下向量号08h~0Fh已经被占用了,所以我们不得不重新设置主从8259A。

    对8259A的设置并不复杂,通过向相应的端口写入特定的ICW来实现。主8259A对应的端口地址是20h和21h,从8259A对应的端口地址是A0h和A1h。ICW共有4个。初始化过程如下:

    1.往端口20h(主片)或A0h(从片)写入ICW1.

    2.往端口21h(主片)或A1h(从片)写入ICW2.

    3.往端口21h(主片)或A1h(从片)写入ICW3.

    4.往端口21h(主片)或A1h(从片)写入ICW4.

    这4步的顺序是不能颠倒的。

    ICW格式如下

    Init8259A:
    	mov	al, 011h
    	out	020h, al	; 主8259, ICW1.
    	call	io_delay
    
    	out	0A0h, al	; 从8259, ICW1.
    	call	io_delay
    
    	mov	al, 020h	; IRQ0 对应中断向量 0x20
    	out	021h, al	; 主8259, ICW2.
    	call	io_delay
    
    	mov	al, 028h	; IRQ8 对应中断向量 0x28
    	out	0A1h, al	; 从8259, ICW2.
    	call	io_delay
    
    	mov	al, 004h	; IR2 对应从8259
    	out	021h, al	; 主8259, ICW3.
    	call	io_delay
    
    	mov	al, 002h	; 对应主8259的 IR2
    	out	0A1h, al	; 从8259, ICW3.
    	call	io_delay
    
    	mov	al, 001h
    	out	021h, al	; 主8259, ICW4.
    	call	io_delay
    
    	out	0A1h, al	; 从8259, ICW4.
    	call	io_delay
    
    	mov	al, 11111110b	; 仅仅开启定时器中断
    	;mov	al, 11111111b	; 屏蔽主8259所有中断
    	out	021h, al	; 主8259, OCW1.
    	call	io_delay
    
    	mov	al, 11111111b	; 屏蔽从8259所有中断
    	out	0A1h, al	; 从8259, OCW1.
    	call	io_delay
    
    	ret
    

    我们通过对端口21h和A1h的操作屏蔽了所有的外部中断,写入OCW,在以下两种情况下用到它:

    1.屏蔽或打开外部中断。

    2.发送EOI给8259A以通知它中断处理结束。

    若想屏蔽或打开外部中断,只需要往8259A写入OCW1就可以了。OCW1格式如下:

    若想屏蔽某一个中断,将对应那一位设成1就可以了。EOI就是当每一次中断处理结束,需要发送一个EOI给8259A,以便继续接收中断。而发送EOI是通过往端口20h或A0h写OCW2来实现的。OCW2的格式如上图所示。

    发送EOI给8259A可以由如下代码完成:

    mov al, 20h
    out 20h或A0h, al
    

     

    这次我们来打开时钟中断(IRQ0),外部可屏蔽中断的发生受到两个因素的影响,只有当IF位为1,并且IMR相应位为0时才会发生。

    那么,如果我们想打开时钟中断的话,一方面不仅要设计一个中断处理程序,另一方面还要设置IMR,并且设置IF位。设置IMR可以通过写OCW2来完成,而设置IF可以通过指令sti来完成。

    先写一个时钟中断处理程序

    _ClockHandler:
    ClockHandler	equ	_ClockHandler - $$
    	inc	byte [gs:((80 * 0 + 70) * 2)]	; 屏幕第 0 行, 第 70 列。
    	mov	al, 20h
    	out	20h, al				; 发送 EOI
    	iretd
    

    修改初始化8259A的代码,时钟中断不再屏蔽

    mov	al, 11111110b	; 仅仅开启定时器中断
    	;mov	al, 11111111b	; 屏蔽主8259所有中断
    	out	021h, al	; 主8259, OCW1.
    	call	io_delay
    
    	mov	al, 11111111b	; 屏蔽从8259所有中断
    	out	0A1h, al	; 从8259, OCW1.
    	call	io_delay
    

    修改IDT:

    %rep 32
    		Gate	SelectorCode32, SpuriousHandler,      0, DA_386IGate
    %endrep
    .020h:		Gate	SelectorCode32,    ClockHandler,      0, DA_386IGate
    %rep 95
    		Gate	SelectorCode32, SpuriousHandler,      0, DA_386IGate
    %endrep
    .080h:		Gate	SelectorCode32,  UserIntHandler,      0, DA_386IGate
    

    运行结果:

    一个码农的日常 

    源码

  • 相关阅读:
    记swoole数据库连接池消失问题的解决方式
    表格逻辑的几个想法
    Java代码实现热部署
    一个接口代理demo
    thinkphp5 关于跨域的一些坑
    CoSky-Mirror 就像一个镜子放在 Nacos、CoSky 中间,构建一个统一的服务发现平台
    CoSky 高性能 服务注册/发现 & 配置中心
    Govern Service 基于 Redis 的服务治理平台
    Govern EventBus
    mysql中查看视图代码
  • 原文地址:https://www.cnblogs.com/joey-hua/p/5388395.html
Copyright © 2020-2023  润新知