自制操作系统6-分割编译与中断处理

DAY6 分割编译与中断处理

2020.10.18

今天主要整理了一下源文件,并实现中断处理。

1. 分割源文件

文档:harib03a

今天先做点热身运动再继续昨天剩下的程序。

现在的bootpack.c已经长达300行,我们决定把它分割为几部分。

将源文件分割为几部分的优缺点:

优点

  • 分类得好的话,修改时容易定位到对应代码处。
  • 只需要编译修改过的文件,提高make的速度。
  • 单个源文件都不长,多个小文件比一个大文件更好处理。

缺点

  • 源文件数量增加。
  • 分类得不好的话,修改时不容易定位。
image-20201018132202736 image-20201018132230187

分割后每个文件都要对要调用的函数进行声明。

记得还要对Makefile进行相应的修改。

整理Makefile

文档:harib03b

源文件分割成功后,Makefile又有点长了。有很多地方都有点冗余,例如:

bootpack.gas : bootpack.c Makefile
	$(CC1) -o bootpack.gas bootpack.c

graphic.gas : graphic.c Makefile
	$(CC1) -o graphic.gas graphic.c

dsctbl.gas : dsctbl.c Makefile
	$(CC1) -o dsctbl.gas dsctbl.c
bootpack.nas : bootpack.gas Makefile
	$(GAS2NASK) bootpack.gas bootpack.nas

graphic.nas : graphic.gas Makefile
	$(GAS2NASK) graphic.gas graphic.nas

dsctbl.nas : dsctbl.gas Makefile
	$(GAS2NASK) dsctbl.gas dsctbl.nas

把他们归纳起来,利用一般规则进行优化。

# 一般規則

%.gas : %.c Makefile
	$(CC1) -o $*.gas $*.c

%.nas : %.gas Makefile
	$(GAS2NASK) $*.gas $*.nas

%.obj : %.nas Makefile
	$(NASK) $*.nas $*.obj $*.lst

make.exe会首先寻找普通的生成规则,如果没找到就尝试用一般规则。所以有个别想用单独规则编译的文件,使用普通生成规则也不会有冲突。

整理头文件

文档:harib03c

graphic.c … 187行
dsctbl.c … 67行
bootpack.c … 81行
合计 … 335行

分割后合起来因为每个源文件都要重复声明函数头,所以比分割前的280行多了不少。

现在我们将这些重复部分全部去掉,把他们归纳起来放到名为bootpack.h的文件里。虽然扩展名改变了,但它也是C语言的文件。

//bootpack.h节选

/* asmhead.nas */
struct BOOTINFO { /* 0x0ff0-0x0fff */
	char cyls; 
	char leds; 
	char vmode; 
	char reserve;
	short scrnx, scrny; 
	char *vram;
};
#define ADR_BOOTINFO	0x00000ff0

/* naskfunc.nas */
void io_hlt(void);
void io_cli(void);
void io_out8(int port, int data);
int io_load_eflags(void);
void io_store_eflags(int eflags);
void load_gdtr(int limit, int addr);
void load_idtr(int limit, int addr);

/* graphic.c */
void init_palette(void);
void set_palette(int start, int end, unsigned char *rgb);
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1);

在源文件前,加上

#include "bootpack.h"

就可以让编译器去读这个头文件,见到了这一行,就将该行替换成所指定文件的内容。

前文提到sprintf时,说要加上#include <stdio.h>,其实也是因为stdio.h中含有对sprintf的声明。(" ")(< >)的区别只是文件所处位置的不同,前者表示该头文件与源文件位于同一个文件里,后者表示该头文件位于编译器所提供的文件夹里。

#define把用到的地址都只写在了bootpack.h文件里,如果想要变更地址的话,只修改bootpach.h一个文件就行了。

bootpack.h … 69行
graphic.c … 156行
dsctbl.c … 51行
bootpack.c … 25行
合计 … 301行

已经缩短了34行。

2. 初始化PIC

文档:harib03d

恢复正轨,接着昨天继续做鼠标指针的移动。

为了鼠标移动必须使用中断,使用中断必须将GDT和IDT正确无误地初始化。但是,还有一件事没做,那就是初始化PIC。

PIC(Programmable Interrupt Controller 可编程中断控制器):PIC是将8个中断信号集合成一个中断信号的装置,PIC监视着输入管脚的8个中断信号,只要有一个中断信号进来,就将唯一的输出管脚信号变成ON,并通知给CPU。IBM的大佬们通过增加PIC来处理更多的中断信号,所以使用了两个PIC。

它们的线路连接如图:

image-20201018163915665

讲清楚PIC的硬件结构才能顺利设定PIC。

//bootpack.h节选 
//#define 对PIC中相应的端口号进行了声明

/* int.c */
void init_pic(void);
void inthandler21(int *esp);
void inthandler27(int *esp);
void inthandler2c(int *esp);
#define PIC0_ICW1		0x0020
#define PIC0_OCW2		0x0020
#define PIC0_IMR		0x0021
#define PIC0_ICW2		0x0021
#define PIC0_ICW3		0x0021
#define PIC0_ICW4		0x0021
#define PIC1_ICW1		0x00a0
#define PIC1_OCW2		0x00a0
#define PIC1_IMR		0x00a1
#define PIC1_ICW2		0x00a1
#define PIC1_ICW3		0x00a1
#define PIC1_ICW4		0x00a1

很多端口号都相同,但因为PIC有些细微的规则,比如写入ICW1后,紧接着一定要写入ICW2,所以即使相同,设定时也能区分开来。

//int.c的主要组成部分
//PIC的初始化程序

void init_pic(void)
/* PIC的初始化 */
{
    io_out8(PIC0_IMR, 0xff ); /* 禁止所有中断 */
    io_out8(PIC1_IMR, 0xff ); /* 禁止所有中断 */
    
    io_out8(PIC0_ICW1, 0x11 ); /* 边沿触发模式(edge trigger mode) */
    io_out8(PIC0_ICW2, 0x20 ); /* IRQ0-7由INT20-27接收 */
    io_out8(PIC0_ICW3, 1 << 2); /* PIC1由IRQ2连接 */
    io_out8(PIC0_ICW4, 0x01 ); /* 无缓冲区模式 */
    
    io_out8(PIC1_ICW1, 0x11 ); /* 边沿触发模式(edge trigger mode) */
    io_out8(PIC1_ICW2, 0x28 ); /* IRQ8-15由INT28-2f接收 */
    io_out8(PIC1_ICW3, 2 ); /* PIC1由IRQ2连接 */
    io_out8(PIC1_ICW4, 0x01 ); /* 无缓冲区模式 */
    
    io_out8(PIC0_IMR, 0xfb ); /* 11111011 PIC1以外全部禁止 */
    io_out8(PIC1_IMR, 0xff ); /* 11111111 禁止所有中断 */
    return;
}

**简单介绍一下PIC的寄存器。**PIC中的寄存器都是8b的。

  • IMR(Interrupt Mask Register 中断屏蔽寄存器):8位分别对应8路IRQ信号,某一位置1时,就会屏蔽对应的IRQ信号,PIC忽视该路信号。
  • ICW(Initial Control Word 初始化控制数据):ICW有4个,一共4B的数据。
    • ICW1和ICW4:与PIC主板配线方式、中断信号的电气特性相关,使用上述程序所示的固定值即可,乱改别的什么值的话,可能会烧坏保险丝哦(吓唬脸)。(其实会烧坏和器件冒烟的是早期电脑,如今的电脑对这种设定起反应的电路已经省略了。)
    • ICW3:有关主-从连接的设定。如果全部置1,那么主PIC就能驱动8个从PIC。所以根据上面的连接图,我们设定成00000100。
    • ICW2:决定了IRQ以哪一号中断通知CPU。中断发生以后,如果CPU可以受理这个中断,就会命令PIC发送两个字节的数据。这两个字节的数据被CPU解读为指令,是"0xcd 0x??"。0xcd就是调用BIOS使用的INT指令。按INT 0x20-0x2f接收中断信号IRQ0-15设定。**为什么不使用INT 0x00-0x0f?**因为INT 0x00-0x1f是在应用程序想要对操作系统干坏事的时候,CPU内部产生的系统保护通知。

3. 中断处理程序的制作

文档:harib03e

鼠标是IRQ12,键盘是IRQ1。所以我们编写了用于INT 0x2c和INT 0x21的中断处理程序

中断处理程序(handler):中断发生时所要调用的程序。

//int.c节选

void inthandler21(int *esp)
/* 来自PS/2键盘的中断 */
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 21 (IRQ-1) : PS/2 keyboard");
	for (;;) {
		io_hlt();
	}
}

void inthandler2c(int *esp)
/* 来自PS/2鼠标的中断 */
{
	struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
	boxfill8(binfo->vram, binfo->scrnx, COL8_000000, 0, 0, 32 * 8 - 1, 15);
	putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, "INT 2C (IRQ-12) : PS/2 mouse");
	for (;;) {
		io_hlt();
	}
}

中断处理完成之后,不能执行return(RET),而是必须执行IRET。所以,我要又要借助汇编语言了。

;naskfunc.nas节选

		EXTERN	_inthandler21, _inthandler27, _inthandler2c

_asm_inthandler21:
		PUSH	ES
		PUSH	DS
		PUSHAD
		MOV		EAX,ESP
		PUSH	EAX
		MOV		AX,SS
		MOV		DS,AX
		MOV		ES,AX
		CALL	_inthandler21
		POP		EAX
		POPAD
		POP		DS
		POP		ES
		IRETD

鼠标程序同理。

PUSHAD表示

PUSH EAX
PUSH ECX
PUSH EDX
PUSH EBX
PUSH ESP
PUSH EBP
PUSH ESI
PUSH EDI

POPAD同理。

将函数注册到IDT中

/* IDT的设定 */
set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);

2*8表示的是属于哪一个段,即段号是2,乘以8是因为低3位有着别的意思,所以低3位必须是0。

最后的AR_INTGATE32将IDT的属性设置为0x008e,表示中断处理有效。

还记得我们之前设置的段号2是谁吗?那个刚好覆盖了整个bootpack.hrb的段。

set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);

在bootpack.c中的HariMain开中断,并设置PIC中的IMR以便接收来自键盘和鼠标的中断

//bootpack.c的HariMain节选

io_sti(); /* 开中断 

//中间的处理

io_out8(PIC0_IMR, 0xf9); /* (11111001) */
io_out8(PIC1_IMR, 0xef); /* (11101111) */

运行试试看

image-20201018172405370

可以接收到来自键盘的中断了,但对鼠标还是没反应。。。

已经很棒了,明天见!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lor :)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值