Build your own operating system-part05

Interrupts and Input

Interrupts Handlers

Creating an Entry in the IDT

Bit:     | 31              16 | 15 | 14 13 | 12 | 11 | 10 9 8 | 7 6 5 | 4 3 2 1 0 |
Content: | offset high | P | DPL | 0 | D | 1 1 0 | 0 0 0 | reserved |
Bit:     | 31              16 | 15              0 |
Content: | segment selector | offset low |
0xDEAD8E00
0x0008BEEF
idt[0] = 0xDEAD8E00
idt[1] = 0x0008BEEF

Handling an Interrupt

[esp + 12] eflags
[esp + 8] cs
[esp + 4] eip
[esp] error code?
struct cpu_state {
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
.
.
.
unsigned int esp;
} __attribute__((packed));

struct stack_state {
unsigned int error_code;
unsigned int eip;
unsigned int cs;
unsigned int eflags;
} __attribute__((packed));

void interrupt_handler(struct cpu_state cpu, struct stack_state stack, unsigned int interrupt);

Creating a Generic Interrupt Handler

%macro no_error_code_interrupt_handler %1
global interrupt_handler_%1
interrupt_handler_%1:
push dword 0 ; push 0 as error code
push dword %1 ; push the interrupt number
jmp common_interrupt_handler ; jump to the common handler
%endmacro
%macro error_code_interrupt_handler %1
global interrupt_handler_%1
interrupt_handler_%1:
push dword %1 ; push the interrupt number
jmp common_interrupt_handler ; jump to the common handler
%endmacro
common_interrupt_handler: ; the common parts of the generic interrupt handler
; save the registers
push eax
push ebx
.
.
.
push ebp
; call the C function
call interrupt_handler
; restore the registers
pop ebp
.
.
.
pop ebx
pop eax
; restore the esp
add esp, 8
; return to the code that got interrupted
iret
no_error_code_interrupt_handler 0 ; create handler for interrupt 0
no_error_code_interrupt_handler 1 ; create handler for interrupt 1
.
.
.
error_code_handler 7 ; create handler for interrupt 7
.
.
.

Loading the IDT

global  load_idt    ; load_idt - Loads the interrupt descriptor table (IDT).
; stack: [esp + 4] the address of the first entry in the IDT
; [esp ] the return address
load_idt:
mov eax, [esp+4] ; load the address of the IDT into register eax
lidt eax ; load the IDT
ret ; return to the calling function

Programmable Interrupt Controller (PIC)

#include "io.h"    #define PIC1_PORT_A 0x20
#define PIC2_PORT_A 0xA0
/* The PIC interrupts have been remapped */
#define PIC1_START_INTERRUPT 0x20
#define PIC2_START_INTERRUPT 0x28
#define PIC2_END_INTERRUPT PIC2_START_INTERRUPT + 7
#define PIC_ACK 0x20 /** pic_acknowledge:
* Acknowledges an interrupt from either PIC 1 or PIC 2.
*
* @param num The number of the interrupt
*/
void pic_acknowledge(unsigned integer interrupt)
{
if (interrupt < PIC1_START_INTERRUPT || interrupt > PIC2_END_INTERRUPT) {
return;
}
if (interrupt < PIC2_START_INTERRUPT) {
outb(PIC1_PORT_A, PIC_ACK);
} else {
outb(PIC2_PORT_A, PIC_ACK);
}
}

Reading Input from the Keyboard

#include "io.h"    #define KBD_DATA_PORT   0x60    /** read_scan_code:
* Reads a scan code from the keyboard
*
* @return The scan code (NOT an ASCII character!)
*/
unsigned char read_scan_code(void)
{
return inb(KBD_DATA_PORT);
}

software engineering undergraduate university of kelaniya