
 |
Main |
 |
Visual Basic |
 |
C++ |
 |
JavaScript |
 |
Flash |
 |
Games |
 |
Miscellaneous |
 |
Tech |
 |
About |
Expand All -
Collapse All
| | 
Develop an OS in C++
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 :-//--------------------Kernel.cpp--------------------
#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 ‘ne // w' 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;
}
//--------------------Kernel.cpp--------------------
Introduction:
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
{
unsigned short offset1;
unsigned short selector;
unsigned char dword_count;
unsigned char type;
unsigned 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. struct
{
unsigned short size __attribute ((packed));
unsigned long offset __attribute ((packed));
} idt_desc;
idt_desc.size=(256*8)-1;
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:
http://member.netease.com/~laoyang/control/interrup.htmlFor more information on the PIC have a look at:
http://users.win.be/W0005997/GI/pic.html
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.
Note:
/* Engesn (Derived) Operating System
* Copyright (c) 2003, 2004 Lam Ri Hui
* 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.
*/
|