寒假OS学习第六天

寒假OS学习第六天

Hurlex——完成中断请求和定时器中断

中断服务请求

外设的所有中断都由中断控制芯片8259A汇集后连接到CPU的INTR引脚

8259A PIC每一片能管理8个中断源。
使用级联功能

时钟中断连接在主片的IR0引脚,键盘中断连接在IR1引脚
因为0~31号中断是CPU自己用的,所以IR0开始对应的就是32号中断号

要完成中断请求,首先要进行初始化

static void
pic_init()
{
    
    
	// 进行初始化
	outb(main_pic_port, 0x11);
	outb(sub_pic_set, 0x11);
	// 设置主片
	outb(main_pic_set, 0x20);  // 从0x20,即30号中断开始
	// 设置从片
	outb(sub_pic_set, 0x28);  // 从40号中断开始
	// 设置级联
	outb(main_pic_set, 0x04);  // 设置IR2引脚连接从片
	outb(sub_pic_set, 0x02);
	// 设置工作方式
	outb(main_pic_set, 0x01);
	outb(sub_pic_set, 0x01);
	// 设置允许中断
	outb(main_pic_set, 0x0);
	outb(sub_pic_set, 0x0);
}

8259A的设置我认为我只要了解了解就够了

接下来,添加中断请求处理函数
IRQ的处理函数和ISR的处理函数基本上一样

%macro IRQ 1
[GLOBAL irq_%1]
irq_%1:
	cli  ; 关闭中断,因为现在正在处理中断
	push 0  ; 无错误码
	push %1
	jmp irq_common_stub
%endmacro

IRQ 32
IRQ 33
IRQ 34
IRQ 35
IRQ 36
IRQ 37
IRQ 38
IRQ 39
IRQ 40
IRQ 41
IRQ 42
IRQ 43
IRQ 44
IRQ 45
IRQ 46
IRQ 47

[GLOBAL irq_common_stub]
[EXTERN irq_handler]

irq_common_stub:
	pusha

	mov ax, ds
	push eax

	mov ax, 0x10
	mov ds, ax
	mov fs, ax
	mov es, ax
	mov gs, ax
	mov ss, ax

	push esp
	call irq_handler
	add esp, 4

	pop ebx
	mov ds, bx
	mov fs, bx
	mov es, bx
	mov gs, bx
	mov ss, bx

	popa
	add esp, 8

	iret
.end:
static void
irq_init()
{
    
    
	idt_set(32, (uint32_t)irq_32, 0x08, 0x8E);
	idt_set(33, (uint32_t)irq_33, 0x08, 0x8E);
	idt_set(34, (uint32_t)irq_34, 0x08, 0x8E);
	idt_set(35, (uint32_t)irq_35, 0x08, 0x8E);
	idt_set(36, (uint32_t)irq_36, 0x08, 0x8E);
	idt_set(37, (uint32_t)irq_37, 0x08, 0x8E);
	idt_set(38, (uint32_t)irq_38, 0x08, 0x8E);
	idt_set(39, (uint32_t)irq_39, 0x08, 0x8E);
	idt_set(40, (uint32_t)irq_40, 0x08, 0x8E);
	idt_set(41, (uint32_t)irq_41, 0x08, 0x8E);
	idt_set(42, (uint32_t)irq_42, 0x08, 0x8E);
	idt_set(43, (uint32_t)irq_43, 0x08, 0x8E);
	idt_set(44, (uint32_t)irq_44, 0x08, 0x8E);
	idt_set(45, (uint32_t)irq_45, 0x08, 0x8E);
	idt_set(46, (uint32_t)irq_46, 0x08, 0x8E);
	idt_set(47, (uint32_t)irq_47, 0x08, 0x8E);
}

inline void
irq_handler(idt_regs_t *regs)
{
    
    
	uint8_t port = (regs->int_no >= 40) ?
		sub_pic_port : main_pic_port;
	outb(port, 0x20);  // 发射重设信号

	if(interrupt_handlers[regs->int_no])
		interrupt_handlers[regs->int_no](regs);
}

定时器中断

定时器中断能控制用户程序的执行事件
而且,这种处理函数通常都是对进程的调度处理

时钟中断由8253/8254 Timer产生的

配置芯片:

void init_timer(uint32_t frequency)
{
    
    
	interrupt_handler_register(32, timer_callback);

	// frequency:每秒的中断次数
	// PIT芯片的端口地址范围为0x40-0x43
	// 输入频率为1193180
	// D7 D6 D5 D4 D3 D2 D1 D0
	// 0  0  1  1  0  1  1  0
	// 设置运作在模式3下
	// 一共有3种模式
	// 具体为什么这么设置,看资料
	outb(0x43, 0x36);

	uint32_t divisor = 1193180 / frequency;
	uint8_t lo = (uint8_t)(divisor & 0x0FF);
	uint8_t hi = (uint8_t)((divisor >> 8) & 0xFF);

	outb(0x40, lo);
	outb(0x40, hi);
}

static void timer_callback(idt_regs_t *regs)
{
    
    
	static uint32_t tick = 0;
	tick ++;
	// print_color(rc_black, rc_light_green, "Ticks: %d\n", tick ++);
}

其他要做的事情就是在入口函数的地方加上一个init_timer而已

猜你喜欢

转载自blog.csdn.net/weixin_45206746/article/details/113418146