【时钟中断的实现】

2020-12-02  本文已影响0人  月下蓑衣江湖夜雨

效果

效果

下面那一行,是main函数中的死循环输出变量!
右上方的字符是时钟中断服务程序打印的。
_start.asm

SELECTOR_KERNEL_CS  equ 8

; 导入函数
extern  cstart
extern  main

; 导入全局变量
extern  gdt_ptr
extern  idt_ptr
extern  c_cs;
extern  c_ds;
extern  c_es;
extern  c_ss;

; 导出 _start
global _start   

[SECTION .bss]
StackSpace      resb    2 * 1024
StackTop:       ; 栈顶

[section .text] ; 代码在此
_start:
    ; 此时内存看上去是这样的(更详细的内存情况在 LOADER.ASM 中有说明):
    ;              ┃                                    ┃
    ;              ┃                 ...                ┃
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■Page  Tables■■■■■■┃
    ;              ┃■■■■■(大小由LOADER决定)■■■■┃ PageTblBase
    ;    00101000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■Page Directory Table■■■■┃ PageDirBase = 1M
    ;    00100000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃□□□□ Hardware  Reserved □□□□┃ B8000h ← gs
    ;       9FC00h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■LOADER.BIN■■■■■■┃ somewhere in LOADER ← esp
    ;       90000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■KERNEL.BIN■■■■■■┃
    ;       80000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃■■■■■■■■KERNEL■■■■■■■┃ 30400h ← KERNEL 入口 (KernelEntryPointPhyAddr)
    ;       30000h ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┋                 ...                ┋
    ;              ┋                                    ┋
    ;           0h ┗━━━━━━━━━━━━━━━━━━┛ ← cs, ds, es, fs, ss
    ;
    ;
    ; GDT 以及相应的描述符是这样的:
    ;
    ;                     Descriptors               Selectors
    ;              ┏━━━━━━━━━━━━━━━━━━┓
    ;              ┃         Dummy Descriptor           ┃
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_FLAT_C    (0~4G)     ┃   8h = cs
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_FLAT_RW   (0~4G)     ┃  10h = ds, es, fs, ss
    ;              ┣━━━━━━━━━━━━━━━━━━┫
    ;              ┃         DESC_VIDEO                 ┃  1Bh = gs
    ;              ┗━━━━━━━━━━━━━━━━━━┛
    ;
    ; 注意! 在使用 C 代码的时候一定要保证 ds, es, ss 这几个段寄存器的值是一样的
    ; 因为编译器有可能编译出使用它们的代码, 而编译器默认它们是一样的. 比如串拷贝操作会用到 ds 和 es.
    ;
    ;


    ; 把 esp 从 LOADER 挪到 KERNEL
    mov esp, StackTop   ; 堆栈在 bss 段中
    sgdt    [gdt_ptr]   ; cstart() 调用的relocate_gdt中将会用到 gdt_ptr
    
    mov word[c_cs], cs
    mov word[c_ds], ds
    mov word[c_es], es
    mov word[c_ss], ss
    
    call    cstart      ; 在此函数中改变了gdt_ptr,让它指向新的GDT
    lgdt    [gdt_ptr]   ; 使用新的GDT
    lidt    [idt_ptr]   ; 使用IDT
    mov ah, 0x74 ;灰底红字
    mov al, 'I'
    mov [gs:((80*0 + 70)*2)], ax
    sti
    call    main        ; 重新放置好GDT后,重新进入main
    ;ud2
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $
    jmp $

cstart.c中的main

// 设置完GDT、IDT后,进入main
PUBLIC void main(){
    uint32 num = 0;
    while(1){
        for(int i=0; i<80; i++){
            print_str_fix_pos((80*24+i)*2, " ");
        }
    
        ++num;
        
       sprintf(buf, "[main], num is %d\n", num);
       print_str_fix_pos((80*24+0)*2, buf);
    }
}

interrupt.c中初始化8259A

PRIVATE void init_8259A(){
    out_byte(INT_M_CTL, 0x11);                  // Master 8259, ICW1. == 0001 0001
    out_byte(INT_S_CTL, 0x11);                  // Slave  8259, ICW1.
    out_byte(INT_M_CTLMASK, INT_VECTOR_IRQ0);   // Master 8259, ICW2. 设置 '主8259' 的中断入口地址为 0x20.
    out_byte(INT_S_CTLMASK, INT_VECTOR_IRQ8);   // Slave  8259, ICW2. 设置 '从8259' 的中断入口地址为 0x28
    out_byte(INT_M_CTLMASK, 0x4);               // Master 8259, ICW3. IR2 对应 '从8259'. 0000 0100
    out_byte(INT_S_CTLMASK, 0x2);               // Slave  8259, ICW3. 对应 '主8259' 的 IR2.
    out_byte(INT_M_CTLMASK, 0x1);               // Master 8259, ICW4. 
    out_byte(INT_S_CTLMASK, 0x1);               // Slave  8259, ICW4.
    //out_byte(INT_M_CTLMASK,   0xFD);          // Master 8259, OCW1. 只打开了键盘中断
    out_byte(INT_M_CTLMASK, 0xFE);              // Master 8259, OCW1. 只打开了时钟中断
    out_byte(INT_S_CTLMASK, 0xFF);              // Slave  8259, OCW1. 
}

时钟中断服务程序!

[section .text] ; 代码在此
ALIGN   16
hwint00:        ; Interrupt routine for irq 0 (the clock).
    pushad
    push ds
    push es
    push fs
    push gs


    inc byte [gs:((80*0 + 70)*2)]
    
    mov al, 20h
    out 20h, al
    
    ;恢复源寄存器的内容
    pop gs
    pop fs
    pop es
    pop ds
    popad
    
    iretd
上一篇 下一篇

猜你喜欢

热点阅读