Advertisement
ASP_Volume2 Miscellaneous #41527

C++ Operating System Development

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.

AI

AI Summary: This codebase represents a historical implementation of the logic described in the metadata. Our preservation engine analyzes the structure to provide context for modern developers.

Source Code
original-source
<p><br>
            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&#8217;ll try my best to walk you through the more important 
            parts of the code.</p>
           <p>Part 1, Will look at the IDT 
            (Interrupt Descriptor Table) and PIC (Programmable Interrupt 
            Controller) and how to set these up.<br>
            <br>
            Part 2, Will be programming the Keyboard Interrupt and 
            how to develop a simple keyboard driver.<br>
            <br>
            Part 3, Will walk through the development of a simple 
            &#8216;std::cout&#8217; and &#8216;std::cin&#8217; implementation.</p>
           <p>The end result will be a simple kernel that accepts input 
            and processes it. <br>
            This :-</p>
           <p>//--------------------Kernel.cpp--------------------<br>
            #include &quot;iostream.h&quot; //(cout, cin, endl)<br>
            #include &quot;asmf.h&quot; //(enable)</p>
           <p>using std::cout; //so we only have to write cout.<br>
            using std::endl;<br>
            using std::cin;</p>
           <p>int main(void)<br>
            {<br>
            char buffer[256]; //Max input from cin is 256 chars including 
            '\0' char,<br>
            //This can change once we have &#8216;new&#8217; and &#8216;delete&#8217;.<br>
            enable(); //Interrupts are disabled by my bootstrap.</p>
           <p> cout &lt;&lt; endl &lt;&lt; &quot;\nHello, what is your 
            name? &quot;;</p>
           <p> cin &gt;&gt; buffer;</p>
           <p> cout &lt;&lt; &quot;\nHi &quot; &lt;&lt; buffer &lt;&lt;&quot;! 
            Nice to meet you.&quot; &lt;&lt; endl;<br>
            }</p>
           <p>//--------------------Kernel.cpp--------------------<br>
           </p>
<strong>Introduction:</strong><br>
   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++.</p>
   <p><strong>Back to Basics:</strong><br>
   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.</p>
   <p>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.)</p>
   <p><span style="font-size:10pt; color:#000000"><tt><span style="color:#0000ff">void</span> 
   Interrupt::init_pic() <span style="color:#008000"><br>
   </span><span style="font-size:10pt; color:#000000"><tt>  </tt></span>  <span style="color:#008000">//re-programs 
   the pic</span> <br>
   {   <span style="color:#008000">//IRQ interrupts 
   start at MASTER_VEC</span> <br>
     outportb(PICM, ICW1); <br>
     outportb(PICS, ICW1); <br>
     outportb(PICMI, MASTER_VEC); <br>
     outportb(PICSI, SLAVE_VEC); <br>
     outportb(PICMI, <span style="color:#600000">4</span>); 
   <br>
     outportb(PICSI, <span style="color:#600000">2</span>); 
   <br>
     outportb(PICMI, ICW4); <br>
     outportb(PICSI, ICW4); <br>
     outportb(PICMI, <span style="color:#600000">0xff</span>); 
   <br>
     outportb(PICSI, <span style="color:#600000">0xff</span>); 
   <br>
   }</tt></span><br>
   <br>
   </p>
   <p>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.</p>
   <p><span style="font-size:10pt; color:#000000"><tt> <span style="color:#0000ff">struct</span> 
   idt_gate <br>
   { <span style="color:#0000ff"><br>
     unsigned</span> <span style="color:#0000ff">short</span> 
   offset1; <span style="color:#0000ff"><br>
     unsigned</span> <span style="color:#0000ff">short</span> 
   selector; <span style="color:#0000ff"><br>
     unsigned</span> <span style="color:#0000ff">char</span> 
   dword_count; <span style="color:#0000ff"><br>
     unsigned</span> <span style="color:#0000ff">char</span> 
   type; <span style="color:#0000ff"><br>
     unsigned</span> <span style="color:#0000ff">short</span> 
   offset2; <br>
   } __attribute ((packed)); <br>
   <br>
   idt_gate idt[<span style="color:#600000">256]</span>;</tt></span></p>
   <p>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).</p>
   <p>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.</p>
   <p><span style="font-size:10pt; color:#000000"><tt> <span style="color:#0000ff">struct</span> 
   <br>
   { <span style="color:#0000ff"><br>
     unsigned</span> <span style="color:#0000ff">short</span> 
   size __attribute ((packed)); <span style="color:#0000ff"><br>
     unsigned</span> <span style="color:#0000ff">long</span> 
   offset __attribute ((packed)); <br>
   } idt_desc;</tt></span></p>
   <p><span style="font-size:10pt; color:#000000"><tt> idt_desc.size=(<span style="color:#600000">256</span>*<span style="color:#600000">8</span>)-<span style="color:#600000">1</span>;<br>
   </tt></span><span style="font-size:10pt; color:#000000"><tt>idt_desc.offset=(<span style="color:#0000ff">unsigned</span> 
   <span style="color:#0000ff">long</span>)idt;</tt></span></p>
   <p>Now all that is left to do is load the IDT descriptor 
   for the CPU and we have control of interrupts! </p>
   <p><span style="font-size:10pt; color:#000000"><tt><span style="color:#0000ff">asm</span>(<span style="color:#aaaaaa">"lidt 
   %0"</span> : <span style="color:#aaaaaa">"=m"</span> 
   (idt_desc)); <span style="color:#008000">//Load IDT.</span></tt></span></p>
   <p>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:<br>
   http://member.netease.com/~laoyang/control/interrup.html</p>
   <p>For more information on the PIC have a look at:<br>
   http://users.win.be/W0005997/GI/pic.html</p>
   <p>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.<br>
   Note: <br>
   <span style="font-size:10pt; color:#000000"><tt> <span style="color:#008000">/* 
   Engesn (Derived) Operating System</span> <span style="color:#008000"> 
   <br>
   * Copyright (c) 2002, 2003 Stephen von Takach</span> <span style="color:#008000"> 
   <br>
   * All Rights Reserved.</span> <span style="color:#008000"> 
   <br>
   * </span> <span style="color:#008000"> <br>
   * Permission to use, copy, modify and distribute this 
   software</span><span style="color:#008000"><br>
   * is hereby granted, provided that both the copyright 
   notice and</span><span style="color:#008000"><br>
   * this permission notice appear in all copies of the software,</span><span style="color:#008000"><br>
   * derivative works or modified versions. </span> <span style="color:#008000"><br>
   */</span>
Original Comments (3)
Recovered from Wayback Machine