《操作系统30天》-合川秀实-学习日志day6

第6天:分割编译与中断处理

封装打包程序

  • 为了缩短源程序,对bootpack.c进行分类,增加源文件的数目,减少代码行数,但是用到的函数还是要在主函数前面声明,整合到一起的时候也需要加上不同的头文件名字。大致是下面的图示过程:

   

  • 分割之后,Makefile变长,三个文件需要做同样的过程才能生成.gas和.nas文件,生成一个规则(一般规则)来编译,实际就是把这一段:
graphic.gas : graphic.c Makefile
	$(CC1) -o graphic.gas graphic.c

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

graphic.obj : graphic.nas Makefile
	$(NASK) graphic.nas graphic.obj graphic.lst

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

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

dsctbl.obj : dsctbl.nas Makefile
	$(NASK) dsctbl.nas dsctbl.obj dsctbl.lst
变成:
%.gas : %.c Makefile
	$(CC1) -o $*.gas $*.c

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

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

最后再整合成bootpack.h:包含了结构体、颜色还有全部函数的声明。只需要在调用的时候在前面加上#include”bootpack.h”这句就可以了。最终精简版结果:

昨天的问题

_load_gdtr:		; void load_gdtr(int limit, int addr);
		MOV		AX,[ESP+4]		; limit
		MOV		[ESP+6],AX
		LGDT		[ESP+6]
		RET
  • 这函数是用来把段上限和地址赋值到GDTR的48位寄存器中,不能用mov指令,只能从地址中读取6个字节,可以用AX寄存器辅助。寄存器的低16位是段上限,等于“GDT的有效字节数-1”,剩下的高32位代表GDT的开始地址。
最初状态: DWORD[ESP+4](段上限):0x0000ffff
DWORD[ESP+8](地址):0x00270000

先把上面esp+4的两个字节放入AX寄存器中,在把寄存器的值放入esp+6中。      

Set_segmdesc函数:
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
	if (limit > 0xfffff) {
		ar |= 0x8000; /* G_bit = 1 */
		limit /= 0x1000;
	}
	sd->limit_low    = limit & 0xffff;
	sd->base_low     = base & 0xffff; 		//后两字节
	sd->base_mid     = (base >> 16) & 0xff;
	sd->access_right = ar & 0xff;
	sd->limit_high   = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0);//高四位为段属性
	sd->base_high    = (base >> 24) & 0xff; 	//高2字节
	return;
}
结构体SEGMENT_DESCRIPTOR的八个字节:
struct SEGMENT_DESCRIPTOR {
	short limit_low, base_low;
	char base_mid, access_right;
	char limit_high, base_high;
};
  •  
  • 包含了段大小,起始地址,管理属性。地址用32位表示,用base表示,其中low占2个字节,high占1个字节,mid一个字节,所以段上限(limit)只能用20位,如果32位就满了,这样一来设置了一个标志位G表示Gbit,标志位为1解释成页,相当于4KB。段上限20位最大为1MB,4KB×1MB=4GB。Limit_low和limit_high里面保存段上限其中limit_high的高四位写入段属性。最后剩下的12位是剩下的段属性-“段的访问权属性”用access_right或ar表示,合并高四位中的4位就变成16位:xxxx0000xxxxxxxx,高四位称为“扩展访问权”到386时代以后才能使用,一般是“GD00”,G是上面说到的G,D为1表示32位,为0表示16位,除了运行80286程序之外都用D=1的模式。
  • 第八位的主要模式:

这段设定是有关CPU的系统模式还是应用模式,如果在应用模式执行LGDT是被阻止的,如果应用程序段是0x9a则为系统模式,0xfa则为应用模式。

初始化PIC

  • PIC是将8个中断信号集合成一个中断信号的装置,起到一个监视的功能,接受到中断信号之后再传给CPU,为了能处理更多的中断增加了一个从PIC。CPU只能接受主PIC传送的信号,只有主PIC向CPU传送从PIC数据的时候,从PIC的中断信号才有用。

硬件设定:
void init_pic(void)
/* PIC的初始化 */
{
	io_out8(PIC0_IMR,  0xff  ); /* 禁止所有中断 */
	io_out8(PIC1_IMR,  0xff  ); /* 禁止所有中断 */

	io_out8(PIC0_ICW1, 0x11  ); /*边沿触发模式 */
	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  ); /* 边沿触发模式 */
	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内部有8个寄存器对应8路IRQ信号,如果某一位值为1,则该位对应的IRQ信号被屏蔽。ICW是“初始化控制数据”,有四个(1-4)ICW1和ICW4与PIC主板配线方式,中断信号的电气特性有关,一般都是固定值。ICW3是有关主-从的连接设定,硬件上设定IRQ2与从PIC相连,所以设定为0000010。最后的ICW2才是操作系统能独立设定的,决定IRQ以哪一号通知CPU,中断信号IRQ 0-15对应INT 0x20-0x2f,INT 0x00-0x1f被CPU的系统保护占用。

中断处理程序和注册中断IDT

  • 鼠标是IRQ12键盘是IRQ1,用到的中断就是INT 0x2c和INT 0x21。

键盘的中断处理:

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();
	}
}
中断处理之后不能执行“return”只能通过执行IRETD指令,用汇编语言修改naskfunc.nas
_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
  • CALL _inthandler21就是键盘的中断,调用这个函数,前面都是做中断前的“现场保护”工作,把寄存器的值压入栈中保存,三个段寄存器相等(DS/ES/SS)。中断处理之后再把栈中的值放出来,做“恢复现场”的工作。最后就是将这个函数_inthandler21注册到IDT中,在dsctbl.c的init_gdtidt里面加入:

set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);

set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);

  • _inthandler21注册到idt的第0x21号,2 * 8表示 asm_inthandler21属于哪一个段,段号是2乘以8是因为低三位必须是0(段寄存器第三位不用)。
  • 关于号码为2的段:set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER);这个段正好涵盖了整个bootpack.hrb,最后的AR_CODE32_ER将IDT属性设定为0x008e,表示用于中断处理的有效设定。在HariMain的最后,修改了PIC的IMR,以便接受来自键盘和鼠标的中断。
  • 补充指令“io_sti();”仅仅是执行STI指令,是CLI的逆指令,执行STI指令之后,IF(中断许可标志位)变为1,CPU接受外部中断。下面是按下键盘之后的状态

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值