30天自制操作系统读书笔记(六)
首先先整理下(转自谷月轩的博客)
现在我们拥有这么9个文件:
- ipl10.nas InitialProgramLoader, 占用了软盘的第一个扇区并符合启动盘的规范, 默认被载入地址是0x7c00 到 0x7e00, 负责将10个柱面读入到0x8200到0x34fff (10个柱面共10*2*18 = 360 个扇区但是第一个没有被读入);
- asmhead.nas 包含一些暂时未知的设定;
- naskfun.nas 包含供C语言程序使用的汇编函数;
- bootpack.h 各种常量定义, 函数定义;
- hankaku.txt 字库文件;
- fifo.c 一个完整的循环队列的实现, 用于中断的缓冲;
- graphic.c 提供绘图函数, 绘制系统界面和指针, 打印字符;
- dsctbl.c GDT(全局符号描述符表) 和 IDT(中断描述符表) 的设定;
- int.c Interrupt Service Routines 的处理.
恩,接下里我们补坑。
介绍下GDT和IDT
DT 全称是全局段描述符表, 用来提供程序执行是需要的关于内存的各种信息,大小为8字节, 包含了: 段的大小; 段的起始地址; 段的管理属性等信息。(具体的作用请百度GDT详解。下文只是针对书本代码进行一些简单的解释。)
C语言描述:
struct SEGMENT_DESCRIPTOR { short limit_low, base_low; char base_mid, access_right; char limit_high, base_high; };
我们知道现在段地址是32位的。在这个结构里用base表示,但是它分成了3段
Low(2字节) mid(1字节) high(1字节) 刚好32位。
为什么要分成3段呢?为了兼容80286时代的CPU。
段上限 limit,这个看上去占了24个字节,但是limit_high中的上4位,用来保存段属性了,所以只能用20 位。
那还有剩下12位的段属性。
高四位称为“扩展访问权”。这4位是由“GD00”构成的,其中G是指刚才G bit,D指段模式,1表示32位。
低8位表示一些权限。不细写了。
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) 0x00270000; struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *) 0x0026f800;
这2个地址是作者随意的,反正这里面没有其他程序在占用。。。
for (i = 0; i < 8192; i++) { set_segmdesc(gdt + i, 0, 0, 0); }
挨个初始化
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar) { //上面说了limit只占20位,所以只能表示最多1M的空间 //所以当输入的limit大于20位时,就执行页模式(不细写了)就要将G bit置1然后limit右 //移4位。 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; return; }
load_gdtr(0xffff, 0x00270000);
至于这句嘛,很简单因为C语言不能直接操作cpu所以,只能改用汇编写入gdtr这个寄存器了。
还有就是IDT,他和GDT差不多,各位之前学习汇编的时候一定已经接触过了,毕竟应该没有哪本汇编教材不介绍大名鼎鼎的int中断吧。
PIC:
处理中断的辅助芯片。 与CPU直接相连的PIC成为主PIC,与主PIC相连的PIC称为从PIC
,主PIC负责处理第0到第7号中断信号,从PIC负责处理第8到第15号中断信号。
从PIC通过第二号IRQ与主PIC相连。
void init_pic(void) /* PIC初始化*/ { io_out8(PIC0_IMR, 0xff ); //禁止主PIC的所有中断 io_out8(PIC1_IMR, 0xff ); //禁止从PIC的所有中断 // 主PIC设定 io_out8(PIC0_ICW1, 0x11 ); //边沿触发模式 (edge trigger mode) io_out8(PIC0_ICW2, 0x20 ); //IRQ0~7 由 INT 20~27 接收 io_out8(PIC0_ICW3, 1 << 2); //PIC1从PIC由IRQ2 连接 io_out8(PIC0_ICW4, 0x01 ); //无缓冲区模式 //从PIC设定 io_out8(PIC1_ICW1, 0x11 ); //边沿触发模式 (edge trigger mode) io_out8(PIC1_ICW2, 0x28 ); // IRQ0~7 由 INT 28~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的寄存器:
IMR :interrupt mask register “中断屏蔽寄存器”
8位对应8路IRQ信号,置1表示忽略该路信号。
ICW :initial control word “初始化控制数据”
ICW 有4个,每个1个字节。
ICW 1 和ICW 4 声明了主板配线方式( = = 完全不懂哎);
ICW 3 是主从设定, 表示触发器的哪一位连着从触发器; (一般是IRQ2 啦);
ICW 2 决定IRQ以哪一个中断号通知CPU, INT 0x0~0x19 不能被使用.
到现在为止准备工作算是完成了。
今天接下来的东西只介绍了那么一点,写成笔记也没什么必要,等完成的差不多了在一块整理吧!