WVB Operating System(Fifth Stage)

Warunajith Bandara
3 min readAug 20, 2021

Welcome back to my fifth article on operating system implementation. In the last two articles, we learned about outputs and Segmentation. Now it would be nice if it also could get some inputs.

The operating system must be able to handle “interrupts” in order to get the inputs. When a hardware device, such as a keyboard, serial port, or timer, tells the CPU that its status has changed, an interrupt occurs. Interrupts can also be sent by the CPU due to program faults, such as when a program refers to memory it doesn’t have access to or divides a value by zero. Finally, there is also software interrupts, which are interrupts that are caused by the “intassembly code instruction and they are often used for system calls.

Interrupts Handlers

The Interrupt Descriptor(IDT) Table is used to handle interrupts. For each interrupt, the IDT specifies a handler. There are three different kinds of handlers for interrupts.

  • Task handler
  • Interrupt handler
  • Trap handler

Handling an Interrupt

When an interrupt occurs, the CPU will push some interrupt information to the stack, then check up and hop to the proper interrupt handler in the IDT. The stack will look like this when the interrupt occurs.

[esp + 12] eflags
[esp + 8] cs
[esp + 4] eip
[esp] error code?

All registers used by interrupt handlers must be preserved by pushing them onto the stack, hence the interrupt handler must be written in assembly code. Because the interrupted code is unaware of the interrupt, it will anticipate its registers to remain unchanged. It will be tedious to write all of the logic for the interrupt handler in assembly code.

Creating the Interrupt Handler

It’s a little tough to design a general interrupt handler because the CPU doesn’t put the interrupt number on the stack. This section will demonstrate how to achieve it using macros. It’s easier to use NASM’s macro functionality rather than writing one version for each interrupt. Because not all interrupts result in an error code, the value 0 will be included as an “error code” for interrupts.

The common interrupt handler does the following.

  • Push the registers on the stack.
  • Call the C function “interrupt_handler”.
  • Pop the registers from the stack.
  • Add 8 to esp(because of the error code and the interrupt number pushed earlier).
  • Execute “iret” to return to the interrupted code.

The below code shows the interrupt_handlers.s file.

These are the “interrupts.h” and “interrupts.c” files.

Loading the IDT

Next, we need to load the IDT using the below assembly code.

Programmable Interrupt Controller (PIC)

You must first configure the Programmable Interrupt Controller(PIC) before you can use hardware interrupts. Signals from the hardware can be mapped to interrupts using the PIC. Every PIC interrupt must be acknowledged, which means that a message must be sent to the PIC confirming that the interrupt has been handled. If this is not done, the PIC will stop generating interrupts. The byte 0x20 is sent to the PIC that raised the interrupt in order to acknowledge it. The following is how to implement the pic acknowledge function.

This article only describes how to handling Keyboard interrupts. So,Let’s see how to read keyboard inputs.

The keyboard generates scan codes rather than ASCII characters. A scan code depicts a button, both when it is pressed and when it is released. The scan code for the recently pressed button can be obtained from the data I/O port on the keyboard, which has the address 0x60. The following codes demonstrate how this can be accomplished.

These all the files are saved under the interrupt folder and the “interrupts_install_idt” function is called using the “kmain.c” function. Finally, Keyboard inputs will be saved in the “com1.out” file.

Thank you for reading!

References:

--

--

Warunajith Bandara

Software Engineering Undergraduate at University of Kelaniya Sri Lanka