DAY6:
1.分割源文件:
文件分类大致流程图:
段的属性:
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;
return;
}
在这个结构体里base又分为low(2字节),mid(1字节),high(1字节) 3段,合起来刚好是32位. 段上限。它表示一个段有多少个字节。可是这里有一个问题,段上限最大是4GB,也就是一个32位的数值,如果直接放进去,这 个数值本身就要占用4个字节,再加上基址(base),一共就要8个字 节,这就把整个结构体占满了。这样一来,就没有地方保存段的管理属 性信息了,这可不行。因此段上限只能使用20位。这样一来,段上限最大也只能指定到1MB为止。明明有4GB,却只能用其中的1MB. 有种又回到了16位时代的错 觉,太可悲了。在这里英特尔的叔叔们又想了一个办法,他们在段的属 性里设了一个标志位,叫做Gbit。这个标志位是1的时候,limit的单位不 解释成字节(byte),而解释成页(page)。页是什么呢?在电脑的 CPU里,1页是指4KB。 这样一来,4KB × 1M = 4GB,所以可以指定4GB的段。总算能放心了。 顺便说一句,G bit的“G”,是“granularity”的缩写,是指单位的大小。
5.初始化PIC
PIC:programmable interrupt controller
PIC是将8个中断信号1集合成一个中断信号的装置。PIC监视着输入管脚 的8个中断信号,只要有一个中断信号进来,就将唯一的输出管脚信号 变成ON,并通知给CPU。IBM的大叔们想要通过增加PIC来处理更多的 中断信号,他们认为电脑会有8个以上的外部设备,所以就把中断信号 设计成了15个,并为此增设了2个PIC。(与CPU直接相连的PIC称为主PIC(master PIC),与主PIC相连的PIC称 为从PIC(slave PIC)。主PIC负责处理第0到第7号中断信号,从PIC负 责处理第8到第15号中断信号)
代码:
int.c的主要组成部分
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寄存器:
IMR(interrupt mask register)其8位分别对应8路IRQ信号。若某路为1,则该位的IRQ信号被屏蔽,PIC忽略该路信号。(具体原因参考书上)
ICW(initial control word) ICW共有4个,分别编号1~4,有4个字节的数据。(ICW1和ICW4与PIC 主板配线方式、中断信号的电气特性等有关,ICW3是有关主—从连接的设定,对主PIC而言,第几号IRQ与从PIC相连,是用8位来设定的。ICW2决定了IRQ以哪一号中断通知CPU)
比较重要的是:INT 0x20-0x2f 15而不使用0x00-0x0f是因为在某些程序想要破坏操作系统时,CPU内部会自动产生INT 0x00~0x1f,如果这些与IRQ重复,CPU就分不清楚到底是IRQ还是CPU的系统保护通知。
6.中断处理程序的制作
鼠标是IRQ12,键盘是IRQ1,所以要编写INT 0x2c 和INT 0x21的中断处理程序(handler)。
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”(RET指令),而是必须执行IRETD指令(无法用C语言写,所以得修改汇编文件)
EXTERN _inthandler21, _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
这个函数只是将寄存器的值保存到栈里,然后将DS和ES调整到 与SS相等,再调用_inthandler21(所以又要修改naskfunc.nas中的函数),返回以后,将所有寄存器的值再返回 到原来的值,然后执行IRETD。
其次要将这个函数注册到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表示的是asm_inthandler21属 于哪一个段,即段号是2,乘以8是因为低3位有着别的意思,这里低3位必须是0。“2 * 8” 也可以写成 “2<<3”
PS:
对于PUSH EAX指令(类比于):
ADD ESP,-4
MOV [SS:ESP],EAX
ESP的值减去4,以所得结果作为地址值,将寄存器中的值 保存到该地址所对应内存里。
对于POP EAX指令(类比于):
MOV EAX,[SS:ESP]
ADD ESP,4
PUSHAD相当于:
PUSH EAX
PUSH ECX
PUSH EDX
PUSH EBX
PUSH ESP
PUSH EBP
PUSH ESI
PUSH EDI 而POPAD指令相当于按以上相反的顺序。