C++ Operating System Development

Submitted on: 1/5/2015 3:58:00 PM
By: Steve von Takach (from psc cd)  
Level: Advanced
User Rating: By 15 Users
Compatibility: C++ (general)
Views: 9836
     The aim of this tutorial is to show you how to write a developed kernel in C++. At this stage you should be fairly competent in the use of C++ and to have a little knowledge of how protected mode functions would be recommended. Iíll try my best to walk you through the more important parts of the code.

This article has accompanying files


The aim of this tutorial is to show you how to write a developed kernel in C++. At this stage you should be fairly competent in the use of C++ and to have a little knowledge of how protected mode functions would be recommended. I’ll try my best to walk you through the more important parts of the code.

Part 1, Will look at the IDT (Interrupt Descriptor Table) and PIC (Programmable Interrupt Controller) and how to set these up.

Part 2, Will be programming the Keyboard Interrupt and how to develop a simple keyboard driver.

Part 3, Will walk through the development of a simple ‘std::cout’ and ‘std::cin’ implementation.

The end result will be a simple kernel that accepts input and processes it.
This :-

#include "iostream.h" //(cout, cin, endl)
#include "asmf.h" //(enable)

using std::cout; //so we only have to write cout.
using std::endl;
using std::cin;

int main(void)
char buffer[256]; //Max input from cin is 256 chars including '\0' char,
//This can change once we have ‘new’ and ‘delete’.
enable(); //Interrupts are disabled by my bootstrap.

cout << endl << "\nHello, what is your name? ";

cin >> buffer;

cout << "\nHi " << buffer <<"! Nice to meet you." << endl;


Let me start by saying that there are many ways of doing this. Some people use assembly, others dot the code with exorbitant inline assembly and others have assembly stubs for interrupts. Now Iím not a big believer in assembly but letís be serious, assembly just makes things difficult, especially in C++ with name mangling and such. There are times where assembly will be our saviour, but for the most part we can do this in good old pure C++.

Back to Basics:
Ok. Where to start?.. Lets just start at the beginning. Interrupts. What are they? Well, when communicating to devices we use I/O ports. These ports enable us to retrieve information, request device operations and be informed of any status changes. The question is how do we know when there is information available? One option would be to poll the port, but that is very inefficient, the other would be to have the device interrupt us when there is information available, jump to some pre-determined code to handle the device and then return to what we were doing. Interrupts are also the ONLY way the CPU will communicate any errors it encounters.

Now the first thing to do when setting up the system to accept interrupts is to re-program the Programmable Interrupt Controller (also known as the PIC). This is because in real mode there are not as many CPU error interrupts (Intel has reserved the first 31 interrupts for the CPU. It is possible to use interrupts above 18 at the moment but this is not recommended as future CPUís may use these interrupts) and to move all hardware interrupts out of the first 31 interrupts requires reprogramming the PIC: (View source, interrupt.h, for the definitions of PICM, ICW1 ect.)

void Interrupt::init_pic()
//re-programs the pic
{ //IRQ interrupts start at MASTER_VEC
outportb(PICM, ICW1);
outportb(PICS, ICW1);
outportb(PICMI, MASTER_VEC);
outportb(PICSI, SLAVE_VEC);
outportb(PICMI, 4);
outportb(PICSI, 2);
outportb(PICMI, ICW4);
outportb(PICSI, ICW4);
outportb(PICMI, 0xff);
outportb(PICSI, 0xff);

The next thing to do is to set up our Interrupt Descriptor Table. The IDT can be located anywhere in memory (unlike real mode) and it is up to the programmer to inform the CPU where to look for this table using the ĎLIDTí instruction (This is where assembly comes into the picture). The IDT can be any length, but I would not recommend anything lower than space for at least 48 interrupts (CPU + IRQ) and the max is 256.

struct idt_gate
short offset1;
short selector;
char dword_count;
char type;
short offset2;
} __attribute ((packed));

idt_gate idt[256];

The code above is the actual IDT. Each interrupt in the table must be set before it is loaded to prevent any errors. Offset 1 and 2 when placed together create a pointer to the handlerís code (offset1:offset2). Selector is the segment code selector that is used to access the code and the type of interrupt must be set. The type of handler is not very important in the kernel at the moment but must be set to either a trap (CPU interrupt) or interrupt gate, the type of code used in the interrupt (32bit), whether the interrupt is absent or present and the code permission level (eg Ring 3, but this doesnít matter for embedded kernel drivers as they can access any kernel code on any level anyway).

Once the IDT has been set we must then create an IDT descriptor. This descriptor tells the CPU how many interrupts are available in the table and what its offset in memory is.

short size __attribute ((packed));
long offset __attribute ((packed));
} idt_desc;

idt_desc.offset=(unsigned long)idt;

Now all that is left to do is load the IDT descriptor for the CPU and we have control of interrupts!

asm("lidt %0" : "=m" (idt_desc)); //Load IDT.

Thatís all! Pretty basic hey? You will have to write handlers for the CPU interrupts (at this stage a simple screen message, disabling of interrupt and a halt should do fine). For more information on CPU interrupts, check out:

For more information on the PIC have a look at:

The source code for a complete implementation of the code above is available on this page, and is in the interrupt.cpp, interrupt.h files.
/* Engesn (Derived) Operating System
* Copyright (c) 2002, 2003 Stephen von Takach

* All Rights Reserved.


* Permission to use, copy, modify and distribute this software

* is hereby granted, provided that both the copyright notice and

* this permission notice appear in all copies of the software,

* derivative works or modified versions.


winzip iconDownload article

Note: Due to the size or complexity of this submission, the author has submitted it as a .zip file to shorten your download time. Afterdownloading it, you will need a program like Winzip to decompress it.Virus note:All files are scanned once-a-day by Planet Source Code for viruses, but new viruses come out every day, so no prevention program can catch 100% of them. For your own safety, please:
  1. Re-scan downloaded files using your personal virus checker before using it.
  2. NEVER, EVER run compiled files (.exe's, .ocx's, .dll's etc.)--only run source code.

If you don't have a virus scanner, you can get one at many places on the net

Other 1 submission(s) by this author


Report Bad Submission
Use this form to tell us if this entry should be deleted (i.e contains no code, is a virus, etc.).
This submission should be removed because:

Your Vote

What do you think of this article (in the Advanced category)?
(The article with your highest vote will win this month's coding contest!)
Excellent  Good  Average  Below Average  Poor (See voting log ...)

Other User Comments

 There are no comments on this submission.

Add Your Feedback
Your feedback will be posted below and an email sent to the author. Please remember that the author was kind enough to share this with you, so any criticisms must be stated politely, or they will be deleted. (For feedback not related to this particular article, please click here instead.)

To post feedback, first please login.