Build your own OS (part 5)
Interrupts and inputs
Hello everyone! Welcome to the fifth instalment of my OS implementing series, in which I demonstrate how to build an operating system from scratch. Now if you haven’t followed the previous four articles I strongly recommend that you follow them before reading this one. I’ll link those articles at the end of this article for your convenience.
We were able to integrate outputs to our operating systems (article 3), and now it’s time to implement inputs. For this we need to understand what is an interrupt and how it is handled by OS, since we will need to make use of them for today’s task. Today’s goal is to use integrate interrupts into our operating systems so it will be able to take keyboard input and display in the console.
What is an interrupt?
An interrupt is a signal from a device connected to our computer or from a program within our computer that causes the operating system to halt the current process and determine what to do next.
As you can see, there are two kinds of interrupts.
- Hardware interrupts: When the state of a hardware device changes, it sends an interrupt. For example, when the user presses the button ‘A,’ the state of the keyboard changes, and when the user presses the ‘right click,’ the state of the mouse changes. These interrupts are referred to as Hardware Interrupts.
- Software interrupts: A software interrupt occurs when an application software terminates or requests a service from the operating system. Aside from that, it occurs due to an exceptional condition in the processor itself, such as when a program divides a number by zero.
When an interruption occurs, the operating system must stop what it is doing and investigate what has occurred (Which interrupt has occurred). When the operating system determines what happened, it must execute a special routine based on the interrupt that occurred. For that, we need to use interrupt handlers.
There are different kinds of hardware and software interrupts. As a result, before handling those interrupts, the OS must first determine which interrupt has occurred. Then it must run the appropriate routine for that specific interrupt. This is referred to as Interrupt Handling. This can be accomplished by establishing an Interrupt Descriptor Table (IDT). This table describes the routine that the operating system must follow for each interrupt. The interrupts are numbered (0–255), and the handler (or routine) for interrupt i is defined in the i th position of the table.
There are three main types of interrupts. They are,
- Task handler,
- Interrupt handler, and
- Trap handler.
For our implementation, we will be using trap handlers since they don’t disable other interrupts while handling one interrupt. We can disable those other interrupts manually when necessary.
Before getting into the implementation, I’d like to introduce you to one other component which becomes very important when integrating interrupts: the Programmable Interrupt Controller.
Programmable Interrupt Controller (PIC)
A programmable interrupt controller (PIC) assists the CPU in handling interrupt requests (IRQ) from multiple sources (such as external I/O devices) that may occur concurrently. It aids in the prioritization of IRQs so that the CPU can switch execution to the most appropriate interrupt handler (ISR) after the PIC evaluates the relative priorities of the IRQs.
The below table shows the hardware that raise interrupts from 0–15.
PICs allow mapping input to outputs in a configurable way. This is significant because every PIC interrupt must be acknowledged. This entails sending a message to the PIC to confirm that the interrupt was handled. If this is not done, the PIC will stop generating interrupts.
Since we are more focused on the practical implementation, I will not go into detail about how an interrupt is handled inside the OS. Instead I’ll discuss the main steps that we need to follow in order to achieve our goal of enabling keyboard interrupts.
Practical Implementation
Step 1: Create required assembly code files
Create two files named idt.s
& interrupt_handlers.s
and copy the following codes to them.
Step 2: Configure the PIC
In this step, you have to create two files containing codes written in C languages. Create two files named pic.h
and pic.c
, then copy the following codes to them.
Step 3: Configure the keyboard with relevant ASCII values
Copy the code below into two new files with the same names as given below.
Step 4: Initiate interrupts with C
Create two new files with the same names as below to store C codes.
Step 5: Update the Makefile
Now we need to link the Makefile with the newly created files. For that, add the following object file names to the first line of the Makefile:
intidt.o interrupt_handlers.o interrupts.o keyboard.o pic.o
Step 6: Configure the main C code
We have now implemented all the necessary parts required to implement interrupts to our operating system. Now we only have to call the relevant function from our main C file. Following is an example for a configured C file:
Step 7(Final Step): Run Bochs and see the output
Run the Bochs with the same command as before (make run
). If you have completed the task successfully, you should be able to type on the console with the keyboard.
If you have any issues with today’s implementation, I’ve included a link to my GitHub repo below for your convenience.
I hope you enjoyed today’s article and put it into action. I’ll see in the next article where I’ll be showing you how to integrate user modes to our OS. Until then Goodbye and Stay Safe!
-Pasan Devin Jayawardene-
Previous articles: