Kurumi Atelier Day10

我们总共支持 256 个中断,所以这里创建一个数组

pub static IDT: Mutex<[IdtEntry; 256]> = Mutex::new([IdtEntry::new(); 256]);  

接下来初始化并载入 IDT

pub fn init() {  
    let ptr: DescriptorTablePointer =
        DescriptorTablePointer::new_idtp(&IDT.lock()[..]);

    unsafe { dtables::lidt(&ptr) };
}

ISR 被调用时我们需要进行 pushadpopad,所以添加下面几个 macro

#[macro_export]
macro_rules! scratch_push {  
    () => (asm!(
        "push rax
        push rcx
        push rdx
        push rdi
        push rsi
        push r8
        push r9
        push r10
        push r11"
        : : : : "intel", "volatile"
    ));
}

#[macro_export]
macro_rules! scratch_pop {  
    () => (asm!(
        "pop r11
        pop r10
        pop r9
        pop r8
        pop rsi
        pop rdi
        pop rdx
        pop rcx
        pop rax"
        : : : : "intel", "volatile"
    ));
}

#[macro_export]
macro_rules! preserved_push {  
    () => (asm!(
        "push rbx
        push rbp
        push r12
        push r13
        push r14
        push r15"
        : : : : "intel", "volatile"
    ));
}

#[macro_export]
macro_rules! preserved_pop {  
    () => (asm!(
        "pop r15
        pop r14
        pop r13
        pop r12
        pop rbp
        pop rbx"
        : : : : "intel", "volatile"
    ));
}

#[macro_export]
macro_rules! interrupt {  
    ($name:ident, $body:expr) => {

        #[naked]
        unsafe extern fn $name() {
            #[inline(never)]
            fn inner() {
                $body
            }

            scratch_push!();
            preserved_push!();

            inner();

            preserved_pop!();
            scratch_pop!();
            iret!();

            intrinsics::unreachable();
        }
    };
}

feature #[naked] 可以使函数没有栈帧上的操作,比如

push ebp  
mov ebp, esp  
...
ret  

尝试添加

pub fn init() {  
    // ...
    interrupt!(isr32, {
        pic::send_eoi(32);
    });

    interrupt!(isr33, {
        let scancode = unsafe { inb(0x60) };

        if let Some(c) = keyboard::from_scancode(scancode as usize) {
            kprint!("{}", c);
        }

        pic::send_eoi(33);
    });

    // IDT Table
    IDT.lock()[32].set_func(isr32);
    IDT.lock()[33].set_func(isr33);

    unsafe { enable(); }
}

因为我们 PIC 被映射到了 0X20 的位置,所以 IDT 的 32 为 IRQ0(定时器),33 为 IRQ1(键盘),以此类推。当键盘中断发生时可以读取 0x60 来获取扫描码然后转换成按键,这里可以参考 Keyboard Scan Codes

pub fn from_scancode(code: usize) -> Option<char> {  
    let printable = match code {
        0x1e => 'a',
        0x30 => 'b',
        0x2e => 'c',
        // ...
        _ => return None,
    };

    Some(printable)
}

现在我们的 OS 可以输入了

Reference

Interrupt Service Routines - OSDev Wiki