分割编译:
1.将不同功能的实现写到不同的c文件中
2.修Makefile普通规则为一般规则
%.gas : %.c Makefile
$(CC1) -o $*.gas $*.c
%.nas : %.gas Makefile
$(GAS2NASK) $*.gas $*.nas
%.obj : %.nas Makefile
$(NASK) $*.nas $*.obj $*.lst
3.提取声明部分到.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位寄存器。
>
limit 0x0000ffff
esp+4 FF
esp+5 FF
esp+6 00
esp+7 00
addr 0x00270000
esp+8 00
esp+9 00
esp+0a 27
esp+0b 00
栈的格式如上,我们把FFFF拷贝到 ESP+6 处 然后把 ESP+6~~ESP+0B 处的数据作为GDTR,所以GDTR前16位是段限,后32位是地址。
IDTR也是同理的。
_load_idtr: ; void load_idtr(int limit, int addr);
MOV AX,[ESP+4] ; limit
MOV [ESP+6],AX
LIDT [ESP+6]
RET
struct SEGMENT_DESCRIPTOR {
short limit_low, base_low;// 2 + 2
char base_mid, access_right;// 1 + 1
char limit_high, base_high;// 1 + 1
};//正好8字节
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;
}
接下来说说是如何用8个字节64位来表示段的属性的。段的属性有 大小,起始地址,管理属性(写入,执行,系统专用?)
段的地址,用base_low ,base_mid,base_high ,正好32位来表示。将地址分段是为了与80286兼容。
在剩下32位中,段上限只能使用20位,那么如何用20位的长度来表示32位的地址空间呢(也就是4GB空间),在段的属性里设置了一个标志位Gbit,如果标示位为1的时候,limit的单位解释为页,一页指4KB, 所以 1M*4KB=4GB。
而limit由limit_low和limit_high组成,一共有24位,其实我们要把段的属性写入limit_high的上4位中,所以段上限还是20位.
12位段属性中,高4位放在limit_high的高4位里,所以我们可以把ar当做16位处理 xxxx0000xxxxxxxx
高4位成为扩展访问权,这4位在80286的时代还不存在,386之后可以使用,这4位是GD00 G就是Gbit ,D是指段的模式,D=1表示32位,D=0表示16位模式。 这里的16位模式只能运行286的程序 不能调用BIOS。 我们通常使用D=1 32模式
ar的低8位:
value | hex | mean |
---|---|---|
0x00 | 00000000 | 未使用的记录表 |
0x92 | 10010010 | 系统专用 可读写 不可执行 |
0x9a | 10011010 | 系统专用 可执行 可读不可写 |
0xf2 | 11110010 | 应用程序用 可读写 不可执行 |
0xfa | 11111010 | 应用程序用 可执行 可读不可写 |
接着说中断处理就要说PIC的寄存器。
IMR寄存器 interrupt mask register 中断屏蔽寄存器,8位,如果某一位是1,对应的IRQ信号被屏蔽。
ICW寄存器 initial control word 初始化控制寄存器
ICW寄存器有4个 ICW1 ICW2 ICW3 ICW4
ICW3是有关主从连接的设定,对应某位置为1表示驱动该位的从PIC,这里我们设置为 00000100 表示驱动2号位从PIC。
#include "bootpack.h"
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;
}
这次我们以INT 0X20~0X2F接受中断信号 IRQ0~15
接下来制作我们的中断处理程序
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();
}
}
中断程序写好之后 我们要注册中断程序,
set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
gd->offset_low = offset & 0xffff;
gd->selector = selector;
gd->dw_count = (ar >> 8) & 0xff;
gd->access_right = ar & 0xff;
gd->offset_high = (offset >> 16) & 0xffff;
return;
}
make run运行后 我们发现可以成功相应键盘中断,但是却不能响应鼠标中断。