Day6

Separated compilation and Interrupt handling

Setting GDT and IDT

1
2
3
GDT Register : 48 bits
GDTR & 0xff = GDTsizelimit =(number of bytes of GDT - 1)
GDTR & 0xffffffff00 = GDTstartAddr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
;supplyment to naskfunc.nas

;void load_gdtr(int limit , int addr )
_load_gdtr:
mov ax,[esp + 4] ; limit
mov [esp+6],ax
lgdt [esp+6] ; load 6 bytes into gdt
ret

;similar to GDTR
_load_idtr:
mov ax,[esp+4]
mov [esp+6],ax
lidt [esp+6]
ret

Init PIC(Program Interrupt Controller)

Here is where the 8259A come !

PIC

  • IRQ stands for Interrupt Request
  • The CPU can only accept 1 interrupt at a time, since there is only one pin !
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//source of int.c
#include "bootpack.h"
/*
#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
*/

/*
IMR : Interrupt Mask Register
ICW : Init Control Word
*/



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;
}

ICW1和ICW4与PIC主板配线方式、中断信号的电气特性等有关,所以就不详细说明了。电脑上设定的是上述程序所示的固定值,不会设定其他的值。如果故意改成别的什么值的话,早期的电脑说不定会烧断保险丝,或者器件冒烟;最近的电脑,对这种设定起反应的电路本身被省略了,所以不会有任何反应。

电路上,+5V与GND(地)短路时,就会发生保险丝熔断、器件冒烟的现象。这可不是吓唬你,而是真的会发生.

Since CPU only has one interrupt pin, how can it know the interrupt number ?

中断发生以后,如果CPU可以受理这个中断,CPU就会命令PIC发送2个字节的数据。

这2个字节是怎么传送的呢?CPU与PIC用IN或OUT进行数据传送时,有数据信号线连在一起。PIC就是利用这个信号线发送这2个字节数据的。送过来的数据是“0xcd 0x??”这两个字节。

由于电路设计的原因,这两个字节的数据在CPU看来,与从内存读进来的程序是完全一样的,所以CPU就把送过来的“0xcd 0x??”作为机器语言执行。这恰恰就是把数据当作程序来执行的情况。

这里的0xcd就是调用BIOS时使用的那个INT指令。我们在程序里写的“INT 0x10”,最后就被编译成了“0xcd 0x10”。所以,CPU上了PIC的当,按照PIC所希望的中断号执行了INT指令

Why why int number is not start from 0x00 ?

这次是以INT 0x200x2f接收中断信号IRQ015而设定的。这里大家可能又会有疑问了。“直接用INT 0x00~0x0f就不行吗?这样与IRQ的号码不就一样了吗?为什么非要加上0x20?”

是这样的,INT 0x00~0x1f不能用于IRQ,仅此而已。

之所以不能用,是因为应用程序想要对操作系统干坏事的时候,CPU内部会自动产生INT 0x00~0x1f,如果IRQ与这些号码重复了, CPU就分不清它到底是IRQ,还是CPU的系统保护通知。

Interrupt handler

keyboard : IRQ1 , mouse : IRQ12

1
2
3
4
5
6
7
// int.c
void inthandler21(int * esp){
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();
}

In order to return from interrupt handler, iretd instruction must be used, so we need assembly here

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
;naskfunc.nas
extern _inthandler21
_asm_inthandler21:
push es
push ds
pushad ;push all common registers
mov eax,esp
push eax
mov ax,ss
mov ds,ax
mov es,ax
call _inthandler21
pop eax
popad
pop ds
pop es
iretd

Then write the function to IDT

1
2
3
set_gatedesc(idt+0x21 , (int)asm_inthandler21 , 2<<3 , AR_INTGATE32);
//2 indicate the segment (bootpack.c)
//the lowest 3 bits are saved for other use