Quick Google Search

Creating Bootable USB:

Creating Bootable USB......

Bootable USB , here we assume that you are using either Vista or Windows 7 to create a bootable USB.

1. Insert your USB (4GB+ preferable) stick to the system and backup all the data from the USB as we are going to format the USB to make it as bootable.

2. Open elevated Command Prompt. To do this, type in CMD in Start menu search field and hit Ctrl + Shift + Enter. Alternatively,

navigate to Start > All programs >Accessories > right click on Command Prompt and select run as administrator.

3. When the Command Prompt opens, enter the following command:

DISKPART and hit enter.

LIST DISK and hit enter.

Once you enter the LIST DISK command, it will show the disk number of your USB drive. In the below image my USB drive disk no is Disk 1.

4. In this step you need to enter all the below commands one by one and hit enter. As these commands are self explanatory, you can easily guess what these commands do.

SELECT DISK 1 (Replace DISK 1 with your disk number)

CLEAN

CREATE PARTITION PRIMARY

SELECT PARTITION 1

ACTIVE

FORMAT FS=NTFS

(Format process may take few seconds)
ASSIGN

EXIT

Don’t close the command prompt as we need to execute one more command at the next step. Just minimize it.

5. Insert your Windows DVD in the optical drive and note down the drive letter of the optical drive and USB media. Here I use

 “D” as my optical (DVD) drive letter and “H” as my USB drive letter.

6. Go back to command prompt and execute the following commands:

D:CD BOOT and hit enter. Where “D” is your DVD drive letter.

CD BOOT and hit enter to see the below message.
BOOTSECT.EXE/NT60 H:

(Where “H” is your USB drive letter)

7. Copy Windows DVD contents to USB.

You are done with your bootable USB. You can now use this bootable USB as bootable DVD on any computer that comes with

USB boot feature (most of the current motherboards support this feature).

Note that this bootable USB guide will not work if you are trying to make a bootable USB on XP computer.

Hacker cracks W-LAN password:

Hacker cracks W-LAN password in 20 mins using Amazon cloud:


A German hacker says he cracked the wireless LAN password of his neighbour in 20 minutes – using the cloud computing power available on Amazon in a demonstration which he says should sensitise people and businesses to security issues.


Thomas Roth, described in Der Spiegel magazine as an IT security expert, will report on his experiment at next week’s Black Hat DC 2011 hacker conference in Washington.

The holder of a W-LAN password can not only spy on the network’s user, but also use the account to secretly start attacks on other servers, or initiate illegal downloads.

The cloud computing systems run not only by Amazon but also Google and Microsoft effectively rent out computing power. For companies which occasionally need such capacity, the idea is attractive as it saves them from having to invest in powerful computing systems of their own.

The amount of computer power in ‘the cloud’ has grown to enormous proportions – and prices have thus fallen, to between $1 and $2 an hour for the use of a very fast computer.

Roth said he easily used this power to show how a W-LAN password could be cracked.

There is already a service called WPACracker which uses 400 computers in the Amazon cloud at the same time to elicit passwords. Roth said he did not even need to use this – rather, he rented the power of a cluster GPU Instance – a group of four extremely fast computer processors.

This took just 20 minutes to crack the WPA password of his neighbour, who had agreed to the experiment. An improvement in the software could reduce this time to around six minutes, he said – which would cost less than $2.

The software tried 70 million words from a dictionary one after another, in a ‘brute force’ attack to find the password.

The WPA password security system is one of the newest, although it has been superseded by the WPA2 system in the newest WLAN systems.

However, the longer the password, the safer it is – and WPA allows up to 63 letters and numbers to be used. The best idea is to use at least 20 figures, without any recognisable words, but using capital as well as small letters as well as numbers and other signs.

Roth said he will publish his software on the internet – not to enable criminals to use it, but to sensitise people to the security issues.

“People tell me it is not possible to crack WPA,” he told Reuters newswire. “And if it were possible, they say it would cost a fortune.” But he said it is actually relatively easy.

Amazon said researchers would often use its system in order to show how security systems can be improved. But the firm said it would be an infringement of its conditions of use to compromise the security of a network. 

All About Movie Tags:

Original Sources-

CAM -
A cam is a theater rip usually done with a digital video camera. A mini tripod is sometimes used, but a lot of the time this wont be possible, so the camera make shake. Also seating placement isn't always idle, and it might be filmed from an angle. If cropped properly, this is hard to tell unless there's text on the screen, but a lot of times these are left with triangular borders on the top and bottom of the screen. Sound is taken from the onboard microphone of the camera, and especially in comedies, laughter can often be heard during the film. Due to these factors picture and sound quality are usually quite poor, but sometimes we're lucky, and the theater will be fairly empty and a fairly clear signal will be heard.

TELESYNC (TS) - A telesync is the same spec as a CAM except it uses an external audio source (most likely an audio jack in the chair for hard of hearing people). A direct audio source does not ensure a good quality audio source, as a lot of background noise can interfere. A lot of the times a telesync is filmed in an empty cinema or from the projection booth with a professional camera, giving a better picture quality. Quality ranges drastically, check the sample before downloading the full release. A high percentage of Telesyncs are CAMs that have been mislabeled.

TELECINE (TC) -
A telecine machine copies the film digitally from the reels. Sound and picture should be very good, but due to the equipment involved and cost telecines are fairly uncommon. Generally the film will be in correct aspect ratio, although 4:3 telecines have existed. A great example is the JURASSIC PARK 3 TC done last year. TC should not be confused with TimeCode , which is a visible counter on screen throughout the film.

SCREENER (SCR) -
A pre VHS tape, sent to rental stores, and various other places for promotional use. A screener is supplied on a VHS tape, and is usually in a 4:3 (full screen) a/r, although letterboxed screeners are sometimes found. The main draw back is a "ticker" (a message that scrolls past at the bottom of the screen, with the copyright and anti-copy telephone number). Also, if the tape contains any serial numbers, or any other markings that could lead to the source of the tape, these will have to be blocked, usually with a black mark over the section. This is sometimes only for a few seconds, but unfortunately on some copies this will last for the entire film, and some can be quite big. Depending on the equipment used, screener quality can range from excellent if done from a MASTER copy, to very poor if done on an old VHS recorder thru poor capture equipment on a copied tape. Most screeners are transferred to VCD, but a few attempts at SVCD have occurred, some looking better than others.

DVD-SCREENER (DVDscr) -Same premise as a screener, but transferred off a DVD. Usually letterbox , but without the extras that a DVD retail would contain. The ticker is not usually in the black bars, and will disrupt the viewing. If the ripper has any skill, a DVDscr should be very good. Usually transferred to SVCD or DivX/XviD.

DVDRip - A copy of the final released DVD. If possible this is released PRE retail (for example, Star Wars episode 2) again, should be excellent quality. DVDrips are released in SVCD and DivX/XviD.

VHSRip -Transferred off a retail VHS, mainly skating/sports videos and XXX releases.

TVRip -TV episode that is either from Network (capped using digital cable/satellite boxes are preferable) or PRE-AIR from satellite feeds sending the program around to networks a few days earlier (do not contain "dogs" but sometimes have flickers etc) Some programs such as WWF Raw Is War contain extra parts, and the "dark matches" and camera/commentary tests are included on the rips. PDTV is capped from a digital TV PCI card, generally giving the best results, and groups tend to release in SVCD for these. VCD/SVCD/DivX/XviD rips are all supported by the TV scene.

WORKPRINT (WP) -A workprint is a copy of the film that has not been finished. It can be missing scenes, music, and quality can range from excellent to very poor. Some WPs are very different from the final print (Men In Black is missing all the aliens, and has actors in their places) and others can contain extra scenes (Jay and Silent Bob) . WPs can be nice additions to the collection once a good quality final has been obtained.

DivX Re-Enc -A DivX re-enc is a film that has been taken from its original VCD source, and re-encoded into a small DivX file. Most commonly found on file sharers, these are usually labeled something like Film.Name.Group(1of2) etc. Common groups are SMR and TND. These aren't really worth downloading, unless you're that unsure about a film u only want a 200mb copy of it. Generally avoid.



Watermarks -

A lot of films come from Asian Silvers/PDVD (see below) and these are tagged by the people responsible. Usually with a letter/initials or a little logo, generally in one of the corners. Most famous are the "Z" "A" and "Globe" watermarks.

Asian Silvers / PDVD -
These are films put out by eastern bootleggers, and these are usually bought by some groups to put out as their own. Silvers are very cheap and easily available in a lot of countries, and its easy to put out a release, which is why there are so many in the scene at the moment, mainly from smaller groups who don't last more than a few releases. PDVDs are the same thing pressed onto a DVD. They have removable subtitles, and the quality is usually better than the silvers. These are ripped like a normal DVD, but usually released as VCD.

Formats-

VCD -
VCD is an mpeg1 based format, with a constant bitrate of 1150kbit at a resolution of 352x240 (NTCS). VCDs are generally used for lower quality transfers (CAM/TS/TC/Screener(VHS)/TVrip(analogue) in order to make smaller file sizes, and fit as much on a single disc as possible. Both VCDs and SVCDs are timed in minutes, rather than MB, so when looking at an mpeg, it may appear larger than the disc capacity, and in reality u can fit 74min on a CDR74.

SVCD -
SVCD is an mpeg2 based (same as DVD) which allows variable bit-rates of up to 2500kbits at a resolution of 480x480 (NTSC) which is then decompressed into a 4:3 aspect ratio when played back. Due to the variable bit-rate, the length you can fit on a single CDR is not fixed, but generally between 35-60 Mins are the most common. To get a better SVCD encode using variable bit-rates, it is important to use multiple "passes". this takes a lot longer, but the results are far clearer.

XVCD/XSVCD -
These are basically VCD/SVCD that don't obey the "rules". They are both capable of much higher resolutions and bit-rates, but it all depends on the player to whether the disc can be played. X(S)VCD are total non-standards, and are usually for home-ripping by people who don't intend to release them.

KVCD
KVCD is a modification to the standard MPEG-1 and MPEG-2 GOP structure and Quantization Matrix. It enables you to create over 120 minutes of near DVD quality video, depending on your material, on a single 80 minute CD-R/CD-RW. We have published these specifications as KVCDx3, our official resolution, which produce 528x480 (NTSC) and 528x576 (PAL) MPEG-1 variable bit rate video, from 64Kbps to 3,000Kbps. Using a resolution of 352x240 (NTSC) or 352x288 (PAL), it's possible to encode video up to ~360 minutes of near VCD quality on a single 80 minute CD-R. The mpeg files created will play back in most modern standalone DVD players. You must burn the KVCD MPEG files as non-standard VCD or non-standard SVCD (depends on your player) with Nero or VCDEasy.

DivX / XviD -
DivX is a format designed for multimedia platforms. It uses two codecs, one low motion, one high motion. most older films were encoded in low motion only, and they have problems with high motion too. A method known as SBC (Smart Bit-rate Control) was developed which switches codecs at the encoding stage, making a much better print. The format is Ana orphic and the bit-rate/resolution are interchangeable. Due to the higher processing power required, and the different codecs for playback, its unlikely we'll see a DVD player capable of play DivX for quite a while, if at all. There have been players in development which are supposedly capable, but nothing has ever arisen. The majority of PROPER DivX rips (not Re-Encs) are taken from DVDs, and generally up to 2hours in good quality is possible per disc. Various codecs exist, most popular being the original Divx3.11a and the new XviD codecs.

CVD -
CVD is a combination of VCD and SVCD formats, and is generally supported by a majority of DVD players. It supports MPEG2 bit-rates of SVCD, but uses a resolution of 352x480(ntsc) as the horizontal resolution is generally less important. Currently no groups release in CVD.

DVD-R -
Is the recordable DVD solution that seems to be the most popular (out of DVD-RAM, DVD-R and DVD+R). it holds 4.7gb of data per side, and double sided discs are available, so discs can hold nearly 10gb in some circumstances. SVCD mpeg2 images must be converted before they can be burnt to DVD-R and played successfully. DVD>DVDR copies are possible, but sometimes extras/languages have to be removed to stick within the available 4.7gb.

MiniDVD -
MiniDVD/cDVD is the same format as DVD but on a standard CDR/CDRW. Because of the high resolution/bit-rates, its only possible to fit about 18-21 mins of footage per disc, and the format is only compatible with a few players.

Avast Free antivirus 2014/2013 30 yrs license key full download

AVAST has acquired Jumpshot to provide you with a cleaner, faster PC experience.

For 25 years, you've trusted us to protect your PC.
With Jump shot, we’ll make that PC more efficient and faster than ever.


Download avast! Free Antivirus

avast! Internet SecurityOr try our best-selling virus protection: Internet Security


Internet Security
Blocks viruses & spywareesyes
Allows assistance from a geek friendyesyes
Secures shopping & bankingyes
Runs risky programs safelyyes
Blocks hacker attacksyes
Secures personal datayes
Stops annoying SPAMyes
Blocks phishing scams
yes


Free Antivirus
DownloadTotally free

avast! Free antivirus




Avast Free antivirus 30 yrs license key full download :
W9740570R9942A0910-8NB2E62T

How Google Works?

The Google Search Engine

Google's search engine is a powerful tool. Without search engines like Google, it would be practically impossible to find the information you need when you browse the Web. Like all search engines, Google uses a special algorithm to generate search results. While Google shares general facts about its algorithm, the specifics are a company secret. This helps Google remain competitive with other search engines on the Web and reduces the chance of someone finding out how to abuse the system.
Google uses automated programs called spiders orcrawlers, just like most search engines. Also like other search engines, Google has a large index ofkeywords and where those words can be found. What sets Google apart is how it ranks search results, which in turn determines the order Google displays results on its search engine results page (SERP). Google uses a trademarked algorithm called PageRank, which assigns each Web page a relevancy score.
A Web page's PageRank depends on a few factors:
  • The frequency and location of keywords within the Web page: If the keyword only appears once within the body of a page, it will receive a low score for that keyword.
  • How long the Web page has existed: People create new Web pages every day, and not all of them stick around for long. Google places more value on pages with an established history.
  • The number of other Web pages that link to the page in question: Google looks at how many Web pages link to a particular site to determine its relevance.
Out of these three factors, the third is the most important. It's easier to understand it with an example. Let's look at a search for the terms "Planet Earth."

As more Web pages link to Discovery's Planet Earth page, the Discovery page's rank increases. When Discovery's page ranks higher than other pages, it shows up at the top of the Google search results page.
Because Google looks at links to a Web page as a vote, it's not easy to cheat the system. The best way to make sure your Web page is high up on Google's search results is to provide great content so that people will link back to your page. The more links your page gets, the higher its PageRank score will be. If you attract the attention of sites with a high PageRank score, your score will grow faster.
Google initiated an experiment with its search engine in 2008. For the first time, Google is allowing a group of beta testers to change the ranking order of search results. In this experiment, beta testers can promote or demote search results and tailor their search experience so that it's more personally relevant. Google executives say there's no guarantee that the company will ever implement this feature into the search engine globally.­
Google offers many different kinds of services in addition to chat. In the next section, we'll see how some of them work.

Google Nexus 5 with Android 4.4 KitKat

Latest reports claimed that Google Nexus 5 running with the Android 4.4 KitKat will be released on October 15.


Previous reports said that the new Google smartphone will be introduced and shipped on the last week of October but new rumours claimed earlier release date.

The source of this rumour comes from whisperings at Google Launchpad, a two-week event for developers and startups, where the date was reportedly overheard.

Some of the rumoured specs and features of the upcoming Google Nexus 5 include a 4.95-inch full-HD (1080p) IPS LCD display, Qualcomm Snapdragon 800 quad-core processor clocked at 2.3GHz, 2GB of RAM, 8-megapixel rear camera with OIS and a 1.3-megapixel front-facing camera.

The alleged Nexus 4 successor has also been expected to include a host of sensors like ambient light, proximity, pressure as well as accelerometer, compass, notification LED and gyroscope.

Powerful processor - The Google Nexus 5 is expected to pack a quadcore Snapdragon 800 processor chip clocking at 2.3 GHz. Although, this is not as great as iPhone 5S' 64-bit processor, it will still deliver a faster performance like its rivals.

Operating System - One of the most anticipated features of Google Nexus 5 is the upcoming Android 4.4 KitKat, which promises better performance and new and exciting features in your handset.

LTE Connectivity - One of the criticisms on the previous LG Nexus 4 is the lack of LTE support. It is most likely that the feature will arrive in the new Google handset.

Other Improved Features - The successor of the popular LG Nexus 4 is rumoured to have improved features such as larger internal storage, better camera and bigger RAM.

Friendly Price - One of the most attractive attributes of Google devices is its incredibly cheap price. It is speculated that Google Nexus 5 will follow the pricing strategy of its predecessor.

                        

Kernel development- Linux Kernel

Introduction

Kernel development is not an easy task. This is a testament to your programming expertise: To develop a kernel is to say that you understand how to create software that interfaces with and manages the hardware. A kernel is designed to be a central core to the operating system - the logic that manages the resources that the hardware has to offer.
One of the most important system resources that you need to manage is the processor or CPU - this is in the form of allotting time for specific operations, and possibly interrupting a task or process when it is time for another scheduled event to happen. This implies multitasking. There is cooperative multitasking, in which the program itself calls a 'yield' function when it wants to give up processing time to the next runnable process or task. There is preemptive multitasking, where the system timer is used to interrupt the current process to switch to a new process: a form of forcive switch, this more guarantees that a process can be given a chunk of time to run. There are several scheduling algorithms used in order to find out what process will be run next. The simplest of which is called 'Round Robin'. This is where you simply get the next process in the list, and choose that to be runnable. A more complicated scheduler involves 'priorities', where certain higher-priority tasks are allowed more time to run than a lower-priority task. Even more complicated still is a Real-time scheduler. This is designed to guarantee that a certain process will be allowed at least a set number of timer ticks to run. Ultimately, this number one resource calculates to time.
The next most important resource in the system is fairly obvious: Memory. There are some times where memory can be more precious than CPU time, as memory is limited, however CPU time is not. You can either code your kernel to be memory- efficient, yet require alot of CPU, or CPU efficient by using memory to store caches and buffers to 'remember' commonly used items instead of looking them up. The best approach would be a combination of the two: Strive for the best memory usage, while preserving CPU time.
The last resource that your kernel needs to manage are hardware resources. This includes Interrupt Requests (IRQs), which are special signals that hardware devices like the keyboard and hard disk can use to tell the CPU to execute a certain routine to handle the data that these devices have ready. Another hardware resource is Direct Memory Access (DMA) channels. A DMA channel allows a device to lock the memory bus and transfer it's data directly into system memory whenever it needs to, without halting the processor's execution. This is a good way to improve performance on a system: a DMA-enabled device can transfer data without bothering the CPU, and then can interrupt the CPU with an IRQ, telling it that the data transfer is complete: Soundcards and ethernet cards are known for using both IRQs and DMA channels. The third hardware resource is in the form of an address, like memory, but it's an address on the I/O bus in the form of a port. A device can be configured, read, or given data using it's I/O port(s). A Device can use many I/O ports, typically in the form of ranges like ports 8 through 16, for example.

Overview

This tutorial was created in an attempt to show you, the reader, how to set up the basics for a kernel. This involves:
1) Setting up your development environment
2) The basics: Setting the stage for GRUB
3) Linking in other files and calling main()
4) Printing to the screen
5) Setting up a custom Global Descriptor Table (GDT)
6) Setting up a custom Interrupt Descriptor Table (IDT)
7) Setting up Interrupt Service Routines (ISRs) to handle your Interrupts and IRQs
8) Remapping the Programmable Interrupt Controllers (PICs) to new IDT entries
9) Installing and servicing IRQs
10) Managing the Programmable Interval Timer / System Clock (PIT)
11) Managing Keyboard IRQs and Keyboard Data

Getting Started

Kernel development is a lengthy process of writing code, as well as debugging various system components. This may seem to be a rather daunting task at first, however you don't nessarily require a massive toolset to write your own kernel. This kernel development tutorial deals mainly with using the Grand Unified Bootloader (GRUB) to load your kernel into memory. GRUB needs to be directed to a protected mode binary image: this 'image' is our kernel, which we will be building.
For this tutorial, you will need at the very least, a general knowledge of the C programming language. X86 Assembler knowledge is highly recommended and beneficial as it will allow you to manipulate specific registers inside your processor. This being said, your toolset will need at the bare minimum, a C compiler that can generate 32-bit code, a 32-bit Linker, and an Assembler which is able to generate 32-bit x86 output.
For hardware, you must have a computer with a 386 or later processor (this includes 386, 486, 5x86, 6x86, Pentium, Athlon, Celeron, Duron, and such). It is preferable that you have a secondary computer set up to be your testbed, right beside your development machine. If you cannot afford a second computer, or simply do not have the room for a second computer on your desk, you may either use a Virtual Machine suite, or you may also use your development machine as the testbed (although this leads to slower development time). Be prepared for many sudden reboots as you test and debug your kernel on real hardware.

Required Hardware for Testbed

- a 100% IBM Compatible PC with:
- a 386-based processor or later (486 or later recommended)
- 4MBytes of RAM
- a VGA compatible video card with monitor
- a Keyboard
- a Floppy Drive
(Yes, that's right! You don't even NEED a hard disk on the testbed!)

Recommended Hardware for Development

- a 100% IBM Compatible PC with:
- a Pentium II or K6 300MHz
- 32MBytes of RAM
- a VGA compatible videocard with monitor
- a Keyboard
- a Floppy drive
- a Hard disk with enough space for all development tools and space for documents and source code
- Microsoft Windows, or a flavour of Unix (Linux, FreeBSD)
- an Internet connection to look up documents
(A mouse is highly recommended)

Toolset

Compilers

- The Gnu C Compiler (GCC) [Unix]
- DJGPP (GCC for DOS/Windows) [Windows]

Assemblers

- Netwide Assembler (NASM) [Unix/Windows]

Virtual Machines

- VMWare Workstation 4.0.5 [Linux/Windows NT/2000/XP]
- Microsoft VirtualPC [Windows NT/2000/XP]
- Bochs [Unix/Windows]

The Basic Kernel

In this section of the tutorial, we will delve into a bit of assembler, learn the basics of creating a linker script as well as the reasons for using one, and finally, we will learn how to use a batch file to automate the assembling, compiling, and linking of this most basic protected mode kernel. Please note that at this point, the tutorial assumes that you have NASM and DJGPP installed on a Windows or DOS-based platform. We also assume that you have a a minimal understanding of the x86 Assembly language.

The Kernel Entry

The kernel's entry point is the piece of code that will be executed FIRST when the bootloader calls your kernel. This chunk of code is almost always written in assembly language because some things, such as setting a new stack or loading up a new GDT, IDT, or segment registers, are things that you simply cannot do in your C code. In many beginner kernels as well as several other larger, more professional kernels, will put all of their assembler code in this one file, and put all the rest of the sources in several C source files.
If you know even a small amount of assembler, the actual code in this file should be very straight forward. As far as code goes, all this file does is load up a new 8KByte stack, and then jump into an infinite loop. The stack is a small amount of memory, but it's used to store or pass arguments to functions in C. It's also used to hold local variables that you declare and use inside your functions. Any other global variables are stored in the data and BSS sections. The lines between the 'mboot' and 'stublet' blocks make up a special signature that GRUB uses to verify that the output binary that it's going to load is, infact, a kernel. Don't struggle too hard to understand the multiboot header.
; This is the kernel's entry point. We could either call main here,
; or we can use this to setup the stack or other nice stuff, like
; perhaps setting up the GDT and segments. Please note that interrupts
; are disabled at this point: More on interrupts later!
[BITS 32]
global start
start:
    mov esp, _sys_stack     ; This points the stack to our new stack area
    jmp stublet

; This part MUST be 4byte aligned, so we solve that issue using 'ALIGN 4'
ALIGN 4
mboot:
    ; Multiboot macros to make a few lines later more readable
    MULTIBOOT_PAGE_ALIGN	equ 1<<0
    MULTIBOOT_MEMORY_INFO	equ 1<<1
    MULTIBOOT_AOUT_KLUDGE	equ 1<<16
    MULTIBOOT_HEADER_MAGIC	equ 0x1BADB002
    MULTIBOOT_HEADER_FLAGS	equ MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE
    MULTIBOOT_CHECKSUM	equ -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
    EXTERN code, bss, end

    ; This is the GRUB Multiboot header. A boot signature
    dd MULTIBOOT_HEADER_MAGIC
    dd MULTIBOOT_HEADER_FLAGS
    dd MULTIBOOT_CHECKSUM
    
    ; AOUT kludge - must be physical addresses. Make a note of these:
    ; The linker script fills in the data for these ones!
    dd mboot
    dd code
    dd bss
    dd end
    dd start

; This is an endless loop here. Make a note of this: Later on, we
; will insert an 'extern _main', followed by 'call _main', right
; before the 'jmp $'.
stublet:
    jmp $


; Shortly we will add code for loading the GDT right here!


; In just a few pages in this tutorial, we will add our Interrupt
; Service Routines (ISRs) right here!



; Here is the definition of our BSS section. Right now, we'll use
; it just to store the stack. Remember that a stack actually grows
; downwards, so we declare the size of the data before declaring
; the identifier '_sys_stack'
SECTION .bss
    resb 8192               ; This reserves 8KBytes of memory here
_sys_stack:

		
The kernel's entry file: 'start.asm'

The Linker Script

The Linker is the tool that takes all of our compiler and assembler output files and links them together into one binary file. A binary file can have several formats: Flat, AOUT, COFF, PE, and ELF are the most common. The linker we have chosen in our toolset, if you can remember, was the LD linker. This is a very good multi-purpose linker with an extensive feature set. There are versions of LD that exist which can output a binary in any format that you wish. Regardless of what format you choose, there will always be 3 'sections' in the output file. 'Text' or 'Code' is the executable itself. The 'Data' section is for hardcoded values in your code, such as when you declare a variable and set it to 5. The value of 5 would get stored in the 'Data' section. The last section is called the 'BSS' section. The 'BSS' consists of uninitialized data; it stores any arrays that you have not set any values to, for example. 'BSS' is a virtual section: It doesn't exist in the binary image, but it exists in memory when your binary is loaded.
What follows is a file called an LD Linker Script. There are 3 major keywords that might pop out in this linker script: OUTPUT_FORMAT will tell LD what kind of binary image we want to create. To keep it simple, we will stick to a plain "binary" image. ENTRY will tell the linker what object file is to be linked as the very first file in the list. We want the compiled version of 'start.asm' called 'start.o' to be the first object file linked, because that's where our kernel's entry point is. The next line is 'phys'. This is not a keyword, but a variable to be used in the linker script. In this case, we use it as a pointer to an address in memory: a pointer to 1MByte, which is where our binary is to be loaded to and run at. The 3rd keyword is SECTIONS. If you study this linker script, you will see that if defines the 3 main sections: '.text', '.data', and '.bss'. There are 3 variables defined also: 'code', 'data', 'bss', and 'end'. Do not get confused by this: the 3 variables that you see are actually variables that are in our startup file, start.asm. ALIGN(4096) ensures that each section starts on a 4096byte boundary. In this case, that means that each section will start on a separate 'page' in memory.
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x00100000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    . = ALIGN(4096);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(4096);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(4096);
  }
  end = .;
}
The Linker Script: 'link.ld'

Assemble and Link!

Now, we must assemble 'start.asm' as well as use the linker script, 'link.ld' shown above, to create our kernel's binary for GRUB to load. The simplest way to do this in Unix is to create a makefile script to do the assembling, compiling, and linking for you, however, most of the people here including myself, use a flavour of Windows. Here, we can create a batch file. A batch file is simply a collection of DOS commands that you can execute with one command: the name of the batch file itself. Even simpler: you just need to double-click the batch file in order to compile your kernel under windows.
Shown below is the batch file we will use for this tutorial. 'echo' is a DOS command that will say the following text on the screen. 'nasm' is our assembler that we use: we compile in aout format, because LD needs a known format in order to resolve symbols in the link process. This assembles the file 'start.asm' into 'start.o'. The 'rem' command means 'remark'. This is a comment: it's in the batch file, but it doesn't actually mean anything to the computer. 'ld' is our linker. The '-T' argument tells LD that a linker script follows. '-o' means the output file follows. Any other arguments are understood as files that we need to link together and resolve in order to create kernel.bin. Lastly, the 'pause' command will display "Press a key to continue..." on the screen and wait for us to press a key so that we can see what our assembler or linker gives out onscreen in terms of syntax errors.
echo Now assembling, compiling, and linking your kernel:
nasm -f aout -o start.o start.asm
rem Remember this spot here: We will add 'gcc' commands here to compile C sources


rem This links all your files. Remember that as you add *.o files, you need to
rem add them after start.o. If you don't add them at all, they won't be in your kernel!
ld -T link.ld -o kernel.bin start.o
echo Done!
pause
		
Our builder batch file: 'build.bat'

Creating Main and Linking C Sources

In normal C programming practice, the function main() is your normal program entry point. In order to try to keep your normal programming practices and familiarize yourself with kernel development, this tutorial will keep the main() function the entry point for your C code. As you remember in the previous section of this tutorial, we tried to keep minimal assembler code. In later sections, we will have to go back into 'start.asm' in order to add Interrupt Service Routines to call C functions.
In this section of the tutorial, we will attempt to create a 'main.c' as well as a header file to include some common function prototypes: 'system.h'. 'main.c' will also contain the function main() which will serve as your C entry point. As a rule in kernel development, we should not normally return from main(). Many Operating Systems get main to initialize the kernel and subsystems, load the shell application, and then finally main() will sit in an idle loop. The idle loop is used in a multitasking system when there are no other tasks that need to be run. Here is an example 'main.c' with the basic main, as well as the function bodies for functions that we will need in the next part of the tutorial.
#include < system.h >

/* You will need to code these up yourself!  */
unsigned char *memcpy(unsigned char *dest, const unsigned char *src, int count)
{
    /* Add code here to copy 'count' bytes of data from 'src' to
    *  'dest', finally return 'dest' */
}

unsigned char *memset(unsigned char *dest, unsigned char val, int count)
{
    /* Add code here to set 'count' bytes in 'dest' to 'val'.
    *  Again, return 'dest' */
}

unsigned short *memsetw(unsigned short *dest, unsigned short val, int count)
{
    /* Same as above, but this time, we're working with a 16-bit
    *  'val' and dest pointer. Your code can be an exact copy of
    *  the above, provided that your local variables if any, are
    *  unsigned short */
}

int strlen(const char *str)
{
    /* This loops through character array 'str', returning how
    *  many characters it needs to check before it finds a 0.
    *  In simple words, it returns the length in bytes of a string */
}

/* We will use this later on for reading from the I/O ports to get data
*  from devices such as the keyboard. We are using what is called
*  'inline assembly' in these routines to actually do the work */
unsigned char inportb (unsigned short _port)
{
    unsigned char rv;
    __asm__ __volatile__ ("inb %1, %0" : "=a" (rv) : "dN" (_port));
    return rv;
}

/* We will use this to write to I/O ports to send bytes to devices. This
*  will be used in the next tutorial for changing the textmode cursor
*  position. Again, we use some inline assembly for the stuff that simply
*  cannot be done in C */
void outportb (unsigned short _port, unsigned char _data)
{
    __asm__ __volatile__ ("outb %1, %0" : : "dN" (_port), "a" (_data));
}

/* This is a very simple main() function. All it does is sit in an
*  infinite loop. This will be like our 'idle' loop */
void main()
{
    /* You would add commands after here */

    /* ...and leave this loop in. There is an endless loop in
    *  'start.asm' also, if you accidentally delete this next line */
    for (;;);
}
		
'main.c': Our kernel's small, yet important beginnings
Before compiling this, we need to add 2 lines into 'start.asm'. We need to let NASM know that main() is in an 'external' file and we need to call main() from the assembly file, also. Open 'start.asm', and look for the line that says 'stublet:'. Immediately after that line, add the lines:
    extern _main
    call _main
Now wait just a minute. Why are there leading underscores for '_main', when in C, we declared it as 'main'? The compiler gcc will put an underscore in front of all of the function and variable names when it compiles. Therefore, to reference a function or variable from our assembly code, we must add an underscore to the function name if the function is in a C source file!.
This is actually good enough to compile 'as is', however we are still missing our 'system.h'. Simply create a blank text file named 'system.h'. Add all the function prototypes for memcpy, memset, memsetw, strlen, inportb, and outportb to this file. It is wise to use macros to prevent an include file, or 'header' file from declaring things more than once using some nice #ifndef, #define, and #endif tricks. We will include this file in each C source file in this tutorial. This will define each function that you can use in your kernel. Feel free to expand upon this library with anything you think you will need. Observe:
#ifndef __SYSTEM_H
#define __SYSTEM_H

/* MAIN.C */
extern unsigned char *memcpy(unsigned char *dest, const unsigned char *src, int count);
extern unsigned char *memset(unsigned char *dest, unsigned char val, int count);
extern unsigned short *memsetw(unsigned short *dest, unsigned short val, int count);
extern int strlen(const char *str);
extern unsigned char inportb (unsigned short _port);
extern void outportb (unsigned short _port, unsigned char _data);

#endif
		
Our global include file: 'system.h'
Next, we need to find out how to compile this. Open your 'build.bat' from the previous section in this tutorial, and add the following line to compile your 'main.c'. Please note that this assumes that 'system.h' is in an 'include' directory in your kernel sources directory. This command executes the compiler 'gcc'. Among the various arguments passed in, there is '-Wall' which gives you warnings about your code. '-nostdinc' along with '-fno-builtin' means that we aren't using standard C library functions. '-I./include' tells the compiler that our headers are in the 'include' directory inside the current. '-c' tells gcc to compile only: No linking yet! Remembering from the previous section in this tutorial, '-o main.o' is the output file that the compiler is to make, with the last argument, 'main.c'. In short, compile 'main.c' into 'main.o' with options best for kernels.
Right click the batch file and select 'edit' to edit it!
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -I./include -c -o main.o main.c
		
Add this line to 'build.bat'
Don't forget to follow the instructions we left in 'build.bat'! You need to add 'main.o' to the list of object files that need to be linked to create your kernel! Finally, if you are stuck creating our accessory functions like memcpy, a solution 'main.c' is shown here.

Printing to the Screen

Now, we will try to print to the screen. In order to print to the screen, we need a way to manage scrolling the screen as needed, also. It might be nice to allow for different colors on the screen as well. Fortunately, a VGA video card makes it rather simple: It gives us a chunk of memory that we write both attribute byte and character byte pairs in order to show information on the screen. The VGA controller will take care of automatically drawing the updated changes on the screen. Scrolling is managed by our kernel software. This is technically our first driver, that we will write right now.
As mentioned, above, the text memory is simply a chunk of memory in our address space. This buffer is located at 0xB8000, in physical memory. The buffer is of the datatype 'short', meaning that each item in this text memory array takes up 16-bits, rather than the usual 8-bits that you might expect. Each 16-bit element in the text memory buffer can be broken into an 'upper' 8-bits and a 'lower' 8-bits. The lower 8 bits of each element tells the display controller what character to draw on the screen. The upper 8-bits is used to define the foreground and background colors of which to draw the character.
151211870
BackcolorForecolorCharacter
The upper 8-bits of each 16-bit text element is called an 'attribute byte', and the lower 8-bits is called the 'character byte'. As you can see from the above table, mapping out the parts of each 16-bit text element, the attribute byte gets broken up further into 2 different 4-bit chunks: 1 representing background color and 1 representing foreground color. Now, because of the fact that only 4-bits define each color, there can only be a maximum of 16 different colors to choose from (Using the equation (num bits ^ 2) - 4^2 = 16). Below is a table of the default 16-color palette.
ValueColorValueColor
0BLACK8DARK GREY
1BLUE9LIGHT BLUE
2GREEN10LIGHT GREEN
3CYAN11LIGHT CYAN
4RED12LIGHT RED
5MAGENTA13LIGHT MAGENTA
6BROWN14LIGHT BROWN
7LIGHT GREY15WHITE
Finally, to access a particular index in memory, there is an equation that we must use. The text mode memory is a simple 'linear' (or flat) area of memory, but the video controller makes it appear to be an 80x25 matrix of 16-bit values. Each line of text is sequential in memory; they follow eachother. We therefore try to break up the screen into horizontal lines. The best way to do this is to use the following equation:

index = (y_value * width_of_screen) + x_value;

This equation shows that to access the index in the text memory array for say (3, 4), we would use the equation to find that 4 * 80 + 3 is 323. This means that to draw to location (3, 4) on the screen, we need to write to do something similar to this:

unsigned short *where = (unsigned short *)0xB8000 + 323;
*where = character | (attribute << 8);
Following now is 'scrn.c', which is where all of our functions dealing with the screen will be. We include our 'system.h' file so that we can use outportb, memcpy, memset, memsetw, and strlen. The scrolling method that we use is rather interesting: We take a chunk of text memory starting at line 1 (NOT line 0), and copy it over top of line 0. This basically moves the entire screen up one line. To complete the scroll, we erase the last line of text by writing spaces with our attribute bytes. The putch function is possibly the most complicated function in this file. It is also the largest, because it needs to handle any newlines ('\n'), carriage returns ('\r'), and backspaces ('\b'). Later, if you wish, you may handle the alarm character ('\a' - ASCII character 7), which is only supposed to do a short beep when it is encountered. I have included a function to set the screen colors also (settextcolor) if you wish.
#include < system.h >

/* These define our textpointer, our background and foreground
*  colors (attributes), and x and y cursor coordinates */
unsigned short *textmemptr;
int attrib = 0x0F;
int csr_x = 0, csr_y = 0;

/* Scrolls the screen */
void scroll(void)
{
    unsigned blank, temp;

    /* A blank is defined as a space... we need to give it
    *  backcolor too */
    blank = 0x20 | (attrib << 8);

    /* Row 25 is the end, this means we need to scroll up */
    if(csr_y >= 25)
    {
        /* Move the current text chunk that makes up the screen
        *  back in the buffer by a line */
        temp = csr_y - 25 + 1;
        memcpy (textmemptr, textmemptr + temp * 80, (25 - temp) * 80 * 2);

        /* Finally, we set the chunk of memory that occupies
        *  the last line of text to our 'blank' character */
        memsetw (textmemptr + (25 - temp) * 80, blank, 80);
        csr_y = 25 - 1;
    }
}

/* Updates the hardware cursor: the little blinking line
*  on the screen under the last character pressed! */
void move_csr(void)
{
    unsigned temp;

    /* The equation for finding the index in a linear
    *  chunk of memory can be represented by:
    *  Index = [(y * width) + x] */
    temp = csr_y * 80 + csr_x;

    /* This sends a command to indicies 14 and 15 in the
    *  CRT Control Register of the VGA controller. These
    *  are the high and low bytes of the index that show
    *  where the hardware cursor is to be 'blinking'. To
    *  learn more, you should look up some VGA specific
    *  programming documents. A great start to graphics:
    *  http://www.brackeen.com/home/vga */
    outportb(0x3D4, 14);
    outportb(0x3D5, temp >> 8);
    outportb(0x3D4, 15);
    outportb(0x3D5, temp);
}

/* Clears the screen */
void cls()
{
    unsigned blank;
    int i;

    /* Again, we need the 'short' that will be used to
    *  represent a space with color */
    blank = 0x20 | (attrib << 8);

    /* Sets the entire screen to spaces in our current
    *  color */
    for(i = 0; i < 25; i++)
        memsetw (textmemptr + i * 80, blank, 80);

    /* Update out virtual cursor, and then move the
    *  hardware cursor */
    csr_x = 0;
    csr_y = 0;
    move_csr();
}

/* Puts a single character on the screen */
void putch(unsigned char c)
{
    unsigned short *where;
    unsigned att = attrib << 8;

    /* Handle a backspace, by moving the cursor back one space */
    if(c == 0x08)
    {
        if(csr_x != 0) csr_x--;
    }
    /* Handles a tab by incrementing the cursor's x, but only
    *  to a point that will make it divisible by 8 */
    else if(c == 0x09)
    {
        csr_x = (csr_x + 8) & ~(8 - 1);
    }
    /* Handles a 'Carriage Return', which simply brings the
    *  cursor back to the margin */
    else if(c == '\r')
    {
        csr_x = 0;
    }
    /* We handle our newlines the way DOS and the BIOS do: we
    *  treat it as if a 'CR' was also there, so we bring the
    *  cursor to the margin and we increment the 'y' value */
    else if(c == '\n')
    {
        csr_x = 0;
        csr_y++;
    }
    /* Any character greater than and including a space, is a
    *  printable character. The equation for finding the index
    *  in a linear chunk of memory can be represented by:
    *  Index = [(y * width) + x] */
    else if(c >= ' ')
    {
        where = textmemptr + (csr_y * 80 + csr_x);
        *where = c | att;	/* Character AND attributes: color */
        csr_x++;
    }

    /* If the cursor has reached the edge of the screen's width, we
    *  insert a new line in there */
    if(csr_x >= 80)
    {
        csr_x = 0;
        csr_y++;
    }

    /* Scroll the screen if needed, and finally move the cursor */
    scroll();
    move_csr();
}

/* Uses the above routine to output a string... */
void puts(unsigned char *text)
{
    int i;

    for (i = 0; i < strlen(text); i++)
    {
        putch(text[i]);
    }
}

/* Sets the forecolor and backcolor that we will use */
void settextcolor(unsigned char forecolor, unsigned char backcolor)
{
    /* Top 4 bytes are the background, bottom 4 bytes
    *  are the foreground color */
    attrib = (backcolor << 4) | (forecolor & 0x0F)
}

/* Sets our text-mode VGA pointer, then clears the screen for us */
void init_video(void)
{
    textmemptr = (unsigned short *)0xB8000;
    cls();
}
		
Printing to the screen: 'scrn.c'
Next, we need to compile this into our kernel. To do that, you need to edit 'build.bat' in order to add a new gcc compile command. Simply copy the command in 'build.bat' that corresponds to 'main.c' and paste it right afterwards. In our newly pasted line, change 'main' to 'scrn'. Again, don't forget to add 'scrn.o' to the list of files that LD needs to link! Before we can use these in main, you must add the function prototypes for putch, puts, cls, init_video, and settextcolor into 'system.h'. Don't forget the 'extern' keyword and the semicolons as these are each function prototypes:
extern void cls();
extern void putch(unsigned char c);
extern void puts(unsigned char *str);
extern void settextcolor(unsigned char forecolor, unsigned char backcolor);
extern void init_video();
		
Add these to 'system.h' so we can call these from 'main.c'
Now, it's safe to use our new screen printing functions in out main function. Open 'main.c' and add a line that calls init_video(), and finally a line that calls puts, passing it a string: puts("Hello World!"); Finally, save all your changes, double click 'build.bat' to make your kernel, debugging any syntax errors. Copy your 'kernel.bin' to your GRUB floppy disk, and if all went well, you should now have a kernel that prints 'Hello World!' on a black screen in white text!

The GDT

A vital part of the 386's various protection measures is the Global Descriptor Table, otherwise called a GDT. The GDT defines base access privileges for certain parts of memory. We can use an entry in the GDT to generate segment violation exceptions that give the kernel an opportunity to end a process that is doing something it shouldn't. Most modern operating systems use a mode of memory called "Paging" to do this: It is alot more versatile and allows for higher flexibility. The GDT can also define if a section in memory is executable or if it is infact, data. The GDT is also capable of defining what are called Task State Segments (TSSes). A TSS is used in hardware-based multitasking, and is not discussed here. Please note that a TSS is not the only way to enable multitasking.
Note that GRUB already installs a GDT for you, but if we overwrite the area of memory that GRUB was loaded to, we will trash the GDT and this will cause what is called a 'triple fault'. In short, it'll reset the machine. What we should do to prevent that problem is to set up our own GDT in a place in memory that we know and can access. This involves building our own GDT, telling the processor where it is, and finally loading the processor's CS, DS, ES, FS, and GS registers with our new entries. The CS register is also known as the Code Segment. The Code Segment tells the processor which offset into the GDT that it will find the access privileges in which to execute the current code. The DS register is the same idea, but it's not for code, it's the Data segment and defines the access privileges for the current data. ES, FS, and GS are simply alternate DS registers, and are not important to us.
The GDT itself is a list of 64-bit long entries. These entries define where in memory that the allowed region will start, as well as the limit of this region, and the access privileges associated with this entry. One common rule is that the first entry in your GDT, entry 0, is known as the NULL descriptor. No segment register should be set to 0, otherwise this will cause a General Protection fault, and is a protection feature of the processor. The General Protection Fault and several other types of 'exceptions' will be explained in detail under the section on Interrupt Service Routines (ISRs).
Each GDT entry also defines whether or not the current segment that the processor is running in is for System use (Ring 0) or for Application use (Ring 3). There are other ring types, but they are not important. Major operating systems today only use Ring 0 and Ring 3. As a basic rule, any application causes an exception when it tries to access system or Ring 0 data. This protection exists to prevent an application from causing the kernel to crash. As far as the GDT is concerned, the ring levels here tell the processor if it's allowed to execute special privileged instructions. Certain instructions are privileged, meaning that they can only be run in higher ring levels. Examples of this are 'cli' and 'sti' which disable and enable interrupts, respectively. If an application were allowed to use the assembly instructions 'cli' or 'sti', it could effectively stop your kernel from running. You will learn more about interrupts in later sections of this tutorial.
Each GDT entry's Access and Granularity fields can be defined as follows:
765430
PDPLDTType
P - Segment is present? (1 = Yes)
DPL - Which Ring (0 to 3)
DT - Descriptor Type
Type - Which type?
765430
GD0ASeg Len. 19:16
G - Granularity (0 = 1byte, 1 = 4kbyte)
D - Operand Size (0 = 16bit, 1 = 32-bit)
0 - Always 0
A - Available for System (Always set to 0) 
In our tutorial kernel, we will create a GDT with only 3 entries. Why 3? We need one 'dummy' descriptor in the beginning to act as our NULL segment for the processor's memory protection features. We need one entry for the Code Segment, and finally, we need one entry for the Data Segment registers. To tell the processor where our new GDT table is, we use the assembly opcode 'lgdt'. 'lgdt' needs to be given a pointer to a special 48-bit structure. This special 48-bit structure is made up of 16-bits for the limit of the GDT (again, needed for protection so the processor can immediately create a General Protection Fault if we want a segment whose offset doesn't exist in the GDT), and 32-bits for the address of the GDT itself.
We can use a simple array of 3 entries to define our GDT. For our special GDT pointer, we only need one to be declared. We call it 'gp'. Create a new file, 'gdt.c'. Get gcc to compile your 'gdt.c' by adding a line to your 'build.bat' as outlined in previous sections of this tutorial. Once again, I remind you to add 'gdt.o' to the list of files that LD needs to link in order to create your kernel! Analyse the following code which makes up the first half of 'gdt.c':
#include < system.h >

/* Defines a GDT entry. We say packed, because it prevents the
*  compiler from doing things that it thinks is best: Prevent
*  compiler "optimization" by packing */
struct gdt_entry
{
    unsigned short limit_low;
    unsigned short base_low;
    unsigned char base_middle;
    unsigned char access;
    unsigned char granularity;
    unsigned char base_high;
} __attribute__((packed));

/* Special pointer which includes the limit: The max bytes
*  taken up by the GDT, minus 1. Again, this NEEDS to be packed */
struct gdt_ptr
{
    unsigned short limit;
    unsigned int base;
} __attribute__((packed));

/* Our GDT, with 3 entries, and finally our special GDT pointer */
struct gdt_entry gdt[3];
struct gdt_ptr gp;

/* This will be a function in start.asm. We use this to properly
*  reload the new segment registers */
extern void gdt_flush();
		
Managing your GDT with 'gdt.c'
You will notice that we added a declaration for a function that does not exist yet: gdt_flush(). gdt_flush() is the function that actually tells the processor where the new GDT exists, using our special pointer that includes a limit as seen above. We need to reload new segment registers, and finally do a far jump to reload our new code segment. Learn from this code, and add it to 'start.asm' right after the endless loop after 'stublet' in the blank space provided:
; This will set up our new segment registers. We need to do
; something special in order to set CS. We do what is called a
; far jump. A jump that includes a segment as well as an offset.
; This is declared in C as 'extern void gdt_flush();'
global _gdt_flush     ; Allows the C code to link to this
extern _gp            ; Says that '_gp' is in another file
_gdt_flush:
    lgdt [_gp]        ; Load the GDT with our '_gp' which is a special pointer
    mov ax, 0x10      ; 0x10 is the offset in the GDT to our data segment
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov ss, ax
    jmp 0x08:flush2   ; 0x08 is the offset to our code segment: Far jump!
flush2:
    ret               ; Returns back to the C code!
		
Add these lines to 'start.asm'
It's not enough to actually reserve space in memory for a GDT. We need to write values into each GDT entry, set the 'gp' GDT pointer, and then we need to call gdt_flush() to perform the update. There is a special function which follows, called 'gdt_set_entry()', which does all the shifts to set each field in the given GDT entry to the appropriate value using easy to use function arguments. You must add the prototypes for these 2 functions (at very least we need 'gdt_install') into 'system.h' so that we can use them in 'main.c'. Analyse the following code - it makes up the rest of 'gdt.c':
/* Setup a descriptor in the Global Descriptor Table */
void gdt_set_gate(int num, unsigned long base, unsigned long limit, unsigned char access, unsigned char gran)
{
    /* Setup the descriptor base address */
    gdt[num].base_low = (base & 0xFFFF);
    gdt[num].base_middle = (base >> 16) & 0xFF;
    gdt[num].base_high = (base >> 24) & 0xFF;

    /* Setup the descriptor limits */
    gdt[num].limit_low = (limit & 0xFFFF);
    gdt[num].granularity = ((limit >> 16) & 0x0F);

    /* Finally, set up the granularity and access flags */
    gdt[num].granularity |= (gran & 0xF0);
    gdt[num].access = access;
}

/* Should be called by main. This will setup the special GDT
*  pointer, set up the first 3 entries in our GDT, and then
*  finally call gdt_flush() in our assembler file in order
*  to tell the processor where the new GDT is and update the
*  new segment registers */
void gdt_install()
{
    /* Setup the GDT pointer and limit */
    gp.limit = (sizeof(struct gdt_entry) * 3) - 1;
    gp.base = &gdt;

    /* Our NULL descriptor */
    gdt_set_gate(0, 0, 0, 0, 0);

    /* The second entry is our Code Segment. The base address
    *  is 0, the limit is 4GBytes, it uses 4KByte granularity,
    *  uses 32-bit opcodes, and is a Code Segment descriptor.
    *  Please check the table above in the tutorial in order
    *  to see exactly what each value means */
    gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);

    /* The third entry is our Data Segment. It's EXACTLY the
    *  same as our code segment, but the descriptor type in
    *  this entry's access byte says it's a Data Segment */
    gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF);

    /* Flush out the old GDT and install the new changes! */
    gdt_flush();
}
		
Add this to 'gdt.c'. It does some of the dirty work relating to the GDT!
Don't forget the prototypes in 'system.h'!
Now that our GDT loading infrastructure is in place, and we compile and link it into our kernel, we need to call gdt_install() in order to actually do our work! Open 'main.c' and add 'gdt_install();' as the very first line in your main() function. The GDT needs to be one of the very first things that you initialize because as you learned from this section of the tutorial, it's very important. You can now compile, link, and send our kernel to our floppy disk to test it out. You won't see any visible changes on the screen: this is an internal change. Onto the Interrupt Descriptor Table (IDT)!

The IDT

The Interrupt Descriptor Table, or IDT, is used in order to show the processor what Interrupt Service Routine (ISR) to call to handle either an exception or an 'int' opcode (in assembly). IDT entries are also called by Interrupt Requests whenever a device has completed a request and needs to be serviced. Exceptions and ISRs are explained in greater detail in the next section of this tutorial, accessible here.
Each IDT entry is similar to that of a GDT entry. Both have hold a base address, both hold an access flag, and both are 64-bits long. The major differences in these two types of descriptors is in the meanings of these fields. In an IDT, the base address specified in the descriptor is actually the address of the Interrupt Service Routine that the processor should call when this interrupt is 'raised' (called). An IDT entry doesn't have a limit, instead it has a segment that you need to specify. The segment must be the same segment that the given ISR is located in. This allows the processor to give control to the kernel through an interrupt that has occured when the processor is in a different ring (like when an application is running).
The access flags of an IDT entry are also similar to a GDT entry's. There is a field to say if the descriptor is actually present or not. There is a field for the Descriptor Privilege Level (DPL) to say which ring is the highest number that is allowed to use the given interrupt. The major difference is the rest of the access flag definition. The lower 5-bits of the access byte is always set to 01110 in binary. This is 14 in decimal. Here is a table to give you a better graphical representation of the access byte for an IDT entry.
76540
PDPLAlways 01110 (14)
P - Segment is present? (1 = Yes)
DPL - Which Ring (0 to 3)
Create a new file in your kernel directory called 'idt.c'. Edit your 'build.bat' file to add another line to make GCC also compile 'idt.c'. Finally, add 'idt.o' to the ever growing list of files that LD needs to link together to create your kernel. 'idt.c' will declare a packed structure that defines each IDT entry, the special IDT pointer structure needed to load the IDT (similar to loading a GDT, but alot less work!), and also declare an array of 256 IDT entries: This will become our IDT.
#include < system.h >

/* Defines an IDT entry */
struct idt_entry
{
    unsigned short base_lo;
    unsigned short sel;        /* Our kernel segment goes here! */
    unsigned char always0;     /* This will ALWAYS be set to 0! */
    unsigned char flags;       /* Set using the above table! */
    unsigned short base_hi;
} __attribute__((packed));

struct idt_ptr
{
    unsigned short limit;
    unsigned int base;
} __attribute__((packed));

/* Declare an IDT of 256 entries. Although we will only use the
*  first 32 entries in this tutorial, the rest exists as a bit
*  of a trap. If any undefined IDT entry is hit, it normally
*  will cause an "Unhandled Interrupt" exception. Any descriptor
*  for which the 'presence' bit is cleared (0) will generate an
*  "Unhandled Interrupt" exception */
struct idt_entry idt[256];
struct idt_ptr idtp;

/* This exists in 'start.asm', and is used to load our IDT */
extern void idt_load();
		
This is the beginning half of 'idt.c'. Defines the vital data structures!
Again, like 'gdt.c', you will notice that there is a declaration of a function that physically exists in another file. 'idt_load' is written in assembly language just like 'gdt_flush'. All 'idt_load' is is calling the 'lidt' assembly opcode using our special IDT pointer which we create later in 'idt_install'. Open up 'start.asm', and add the following lines right after the 'ret' for '_gdt_flush':
; Loads the IDT defined in '_idtp' into the processor.
; This is declared in C as 'extern void idt_load();'
global _idt_load
extern _idtp
_idt_load:
    lidt [_idtp]
    ret
		
Add this to 'start.asm'
Setting up each IDT entry is alot easier than building a GDT entry. We have an 'idt_set_gate' function which accepts the IDT entry number, the base address of our Interrupt Service Routine, our Kernel Code Segment, and the access flags as outlined in the table introduced above. Again, we have an 'idt_install' function which sets up our special IDT pointer as well as clears out the IDT to a default known state of cleared. Finally, we would load the IDT by calling 'idt_load'. Please note that you can add ISRs to your IDT at any time after the IDT is loaded. More about ISRs later.
/* Use this function to set an entry in the IDT. Alot simpler
*  than twiddling with the GDT ;) */
void idt_set_gate(unsigned char num, unsigned long base, unsigned short sel, unsigned char flags)
{
    /* We'll leave you to try and code this function: take the
    *  argument 'base' and split it up into a high and low 16-bits,
    *  storing them in idt[num].base_hi and base_lo. The rest of the
    *  fields that you must set in idt[num] are fairly self-
    *  explanatory when it comes to setup */
}

/* Installs the IDT */
void idt_install()
{
    /* Sets the special IDT pointer up, just like in 'gdt.c' */
    idtp.limit = (sizeof (struct idt_entry) * 256) - 1;
    idtp.base = &idt;

    /* Clear out the entire IDT, initializing it to zeros */
    memset(&idt, 0, sizeof(struct idt_entry) * 256);

    /* Add any new ISRs to the IDT here using idt_set_gate */

    /* Points the processor's internal register to the new IDT */
    idt_load();
}
		
The rest of 'idt.c'. Try to figure out 'idt_set_gate'. It's easy!
Finally, be sure to add 'idt_set_gate' and 'idt_install' as function prototypes in 'system.h'. Remember that we need to call these functions from other files, like 'main.c'. Call 'idt_install' from inside our 'main()' function, right after the call to 'gdt_install'. You should be able to compile your kernel without problems. Take some time to experiment a bit with your new kernel. If you try to do an illegal operation like dividing by zero, you will find that your machine will reset! We can catch these 'exceptions' by installing Interrupt Service Routines in our new IDT.
If you got stuck writing 'idt_set_gate', you may find the solution to this section of the tutorial here.

Interrupt Service Routines

Interrupt Service Routines, or ISRs, are used to save the current processor state and set up the appropriate segment registers needed for kernel mode before the kernel's C-level interrupt handler is called. This can all be handled in about 15 or 20 lines of assembly language, including calling our handler in C. We need to also point the correct entry in the IDT to the correct ISR in order to handle the right exception.
An Exception is a special case that the processor encounters when it cannot continue normal execution. This could be something like dividing by zero: The result is an unknown or non-real number, so the processor will cause an exception so that the kernel can stop that process or task from causing any problems. If the processor finds that a program is trying to access a piece of memory that it shouldn't, it will cause a General Protection Fault. When you set up paging, the processor causes a Page Fault, but this is recoverable: you can map a page in memory to the faulted address - but that's for another tutorial.
The first 32 entries in the IDT correspond to Exceptions that can possibly be generated by the processor, and therefore need to be handled. Some exceptions will push another value onto the stack: an Error Code value which is specific to the exception caused.
Exception #DescriptionError Code?
0Division By Zero ExceptionNo
1Debug ExceptionNo
2Non Maskable Interrupt ExceptionNo
3Breakpoint ExceptionNo
4Into Detected Overflow ExceptionNo
5Out of Bounds ExceptionNo
6Invalid Opcode ExceptionNo
7No Coprocessor ExceptionNo
8Double Fault ExceptionYes
9Coprocessor Segment Overrun ExceptionNo
10Bad TSS ExceptionYes
11Segment Not Present ExceptionYes
12Stack Fault ExceptionYes
13General Protection Fault ExceptionYes
14Page Fault ExceptionYes
15Unknown Interrupt ExceptionNo
16Coprocessor Fault ExceptionNo
17Alignment Check Exception (486+)No
18Machine Check Exception (Pentium/586+)No
19 to 31Reserved ExceptionsNo
As mentioned earlier, some exceptions push an error code onto the stack. To decrease the complexity, we handle this by pushing a dummy error code of 0 onto the stack for any ISR that doesn't push an error code already. This keeps a uniform stack frame. To track which exception is firing, we also push the interrupt number on the stack. We use the assembler opcode 'cli' to disable interrupts and prevent an IRQ from firing, which could possibly otherwise cause conflicts in our kernel. To save space in the kernel and make a smaller binary output file, we get each ISR stub to jump to a common 'isr_common_stub'. The 'isr_common_stub' will save the processor state on the stack, push the current stack address onto the stack (gives our C handler the stack), call our C 'fault_handler' function, and finally restore the state of the stack. Add this code to 'start.asm' in the provided space, filling out all 32 ISRs:
; In just a few pages in this tutorial, we will add our Interrupt
; Service Routines (ISRs) right here!
global _isr0
global _isr1
global _isr2
...                ; Fill in the rest here!
global _isr30
global _isr31

;  0: Divide By Zero Exception
_isr0:
    cli
    push byte 0    ; A normal ISR stub that pops a dummy error code to keep a
                   ; uniform stack frame
    push byte 0
    jmp isr_common_stub

;  1: Debug Exception
_isr1:
    cli
    push byte 0
    push byte 1
    jmp isr_common_stub
    
...                ; Fill in from 2 to 7 here!

;  8: Double Fault Exception (With Error Code!)
_isr8:
    cli
    push byte 8        ; Note that we DON'T push a value on the stack in this one!
                   ; It pushes one already! Use this type of stub for exceptions
                   ; that pop error codes!
    jmp isr_common_stub

...                ; You should fill in from _isr9 to _isr31 here. Remember to
                   ; use the correct stubs to handle error codes and push dummies!

; We call a C function in here. We need to let the assembler know
; that '_fault_handler' exists in another file
extern _fault_handler

; This is our common ISR stub. It saves the processor state, sets
; up for kernel mode segments, calls the C-level fault handler,
; and finally restores the stack frame.
isr_common_stub:
    pusha
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10   ; Load the Kernel Data Segment descriptor!
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp   ; Push us the stack
    push eax
    mov eax, _fault_handler
    call eax       ; A special call, preserves the 'eip' register
    pop eax
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8     ; Cleans up the pushed error code and pushed ISR number
    iret           ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP!
		
Add this to 'start.asm' in the spot we indicated in "The Basic Kernel"
Create yourself a new file called 'isrs.c'. Once again, remember to add the appropriate line to get GCC to compile the file in 'build.bat'. Add the file 'isrs.o' to LD's list of files so that it gets linked into the kernel. 'isrs.c' is rather straight-forward: declare our regular #include line, declare the prototypes of each of the ISRs from inside 'start.asm', point the IDT entry to the correct ISR, and finally, create an interrupt handler in C to service all of our exceptions generically. I'll leave it up to you to fill in the holes here:
#include < system.h >

/* These are function prototypes for all of the exception
*  handlers: The first 32 entries in the IDT are reserved
*  by Intel, and are designed to service exceptions! */
extern void isr0();
extern void isr1();
extern void isr2();

...                             /* Fill in the rest of the ISR prototypes here */

extern void isr29();
extern void isr30();
extern void isr31();

/* This is a very repetitive function... it's not hard, it's
*  just annoying. As you can see, we set the first 32 entries
*  in the IDT to the first 32 ISRs. We can't use a for loop
*  for this, because there is no way to get the function names
*  that correspond to that given entry. We set the access
*  flags to 0x8E. This means that the entry is present, is
*  running in ring 0 (kernel level), and has the lower 5 bits
*  set to the required '14', which is represented by 'E' in
*  hex. */
void isrs_install()
{
    idt_set_gate(0, (unsigned)isr0, 0x08, 0x8E);
    idt_set_gate(1, (unsigned)isr1, 0x08, 0x8E);
    idt_set_gate(2, (unsigned)isr2, 0x08, 0x8E);
    idt_set_gate(3, (unsigned)isr3, 0x08, 0x8E);

    ...                         /* Fill in the rest of these ISRs here */

    idt_set_gate(30, (unsigned)isr30, 0x08, 0x8E);
    idt_set_gate(31, (unsigned)isr31, 0x08, 0x8E);
}

/* This is a simple string array. It contains the message that
*  corresponds to each and every exception. We get the correct
*  message by accessing like:
*  exception_message[interrupt_number] */
unsigned char *exception_messages[] =
{
    "Division By Zero",
    "Debug",
    "Non Maskable Interrupt",
    
    ...                         /* Fill in the rest here from our Exceptions table */
    
    "Reserved",
    "Reserved"
};

/* All of our Exception handling Interrupt Service Routines will
*  point to this function. This will tell us what exception has
*  happened! Right now, we simply halt the system by hitting an
*  endless loop. All ISRs disable interrupts while they are being
*  serviced as a 'locking' mechanism to prevent an IRQ from
*  happening and messing up kernel data structures */
void fault_handler(struct regs *r)
{
    /* Is this a fault whose number is from 0 to 31? */
    if (r->int_no < 32)
    {
        /* Display the description for the Exception that occurred.
        *  In this tutorial, we will simply halt the system using an
        *  infinite loop */
        puts(exception_messages[r->int_no]);
        puts(" Exception. System Halted!\n");
        for (;;);
    }
}
		
The contents of 'isrs.c'
Wait, we have a new structure here as an argument to 'fault_handler': struct 'regs'. In this case, 'regs' is a way of showing the C code what the stack frame looks like. Remember that in 'start.asm' that we push a pointer to the stack onto the stack itself: this is so that we may be able to retrieve any error codes and interrupt numbers from the handlers themselves. This design is what allows us to use the same C handler for each different ISR and still be able to tell which exception or interrupt actually happened.
/* This defines what the stack looks like after an ISR was running */
struct regs
{
    unsigned int gs, fs, es, ds;      /* pushed the segs last */
    unsigned int edi, esi, ebp, esp, ebx, edx, ecx, eax;  /* pushed by 'pusha' */
    unsigned int int_no, err_code;    /* our 'push byte #' and ecodes do this */
    unsigned int eip, cs, eflags, useresp, ss;   /* pushed by the processor automatically */ 
};
		
Defines a stack frame pointer argument. Add this to 'system.h'
Open 'system.h' and add the definition to struct 'regs' as well as the function prototype for 'isrs_install' so that we can call it from in 'main.c'. Finally, call 'isrs_install' from in our 'main' function, right after we install our new IDT. It would be a good idea to test out the exception handlers in our kernel now.

OPTIONAL: In 'main', add some tester code that will divide a number by zero. As soon as the processor encounters this, the processor will generate a "Divide By Zero" Exception, and you will see that appear on the screen! When you test that, and it works, you can delete your exception causing code (the 'putch(myvar / 0);' line, or whatever you decide to write.
You may find the complete solution to 'start.s' here, and the complete solution to 'isrs.c' here.

IRQs and PICs

Interrupt Requests or IRQs are interrupts that are raised by hardware devices. Some devices generate an IRQ when they have data ready to be read, or when they finish a command like writing a buffer to disk, for example. It's safe to say that a device will generate an IRQ whenever it wants the processor's attention. IRQs are generated by everything from network cards and sound cards to your mouse, keyboard, and serial ports.
Any IBM PC/AT Compatible computer (anything with a 286 and later processor) has 2 chips that are used to manage IRQs. These 2 chips are known as the Programmable Interrupt Controllers or PICs. These PICs also go by the name '8259'. One 8259 acts as the 'Master' IRQ controller, and one is the 'Slave' IRQ controller. The slave is connected to IRQ2 on the master controller. The master IRQ controller is connected directly to the processor itself, to send signals. Each PIC can handle 8 IRQs. The master PIC handles IRQs 0 to 7, and the slave PIC handles IRQs 8 to 15. Remember that the slave controller is connected to the primary controller through IRQ2: This means that every time an IRQ from 8 to 15 occurs, IRQ2 fires at exactly the same time.
When a device signals an IRQ, remember that an interrupt is generated, and the CPU pauses whatever it's doing to call the ISR to handle the corresponding IRQ. The CPU then performs whatever necessary action (like reading from the keyboard, for example), and then it must tell the PIC that the interrupt came from that the CPU has finished executing the correct routine. The CPU tells the right PIC that the interrupt is complete by writing the command byte 0x20 in hex to the command register for that PIC. The master PIC's command register exists at I/O port 0x20, while the slave PIC's command register exists at I/O port 0xA0.
Before we get into writing our IRQ management code, we need to also know that IRQ0 to IRQ7 are originally mapped to IDT entries 8 through 15. IRQ8 to IRQ15 are mapped to IDT entries 0x70 through 0x78. If you remember the previous section of this tutorial, IDT entries 0 through 31 are reserved for exceptions. Fortunately, the Interrupt Controllers are 'programmable': You can change what IDT entries that their IRQs are mapped to. For this tutorial, we will map IRQ0 through IRQ15 to IDT entries 32 through 47. To start us off, we must add some ISRs to 'start.asm' in order to service our interrupts:
global _irq0
...                ; You complete the rest!
global _irq15

; 32: IRQ0
_irq0:
    cli
    push byte 0    ; Note that these don't push an error code on the stack:
                   ; We need to push a dummy error code
    push byte 32
    jmp irq_common_stub

...                ; You need to fill in the rest!

; 47: IRQ15
_irq15:
    cli
    push byte 0
    push byte 47
    jmp irq_common_stub

extern _irq_handler

; This is a stub that we have created for IRQ based ISRs. This calls
; '_irq_handler' in our C code. We need to create this in an 'irq.c'
irq_common_stub:
    pusha
    push ds
    push es
    push fs
    push gs
    mov ax, 0x10
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    mov eax, esp
    push eax
    mov eax, _irq_handler
    call eax
    pop eax
    pop gs
    pop fs
    pop es
    pop ds
    popa
    add esp, 8
    iret
		
Add this chunk of code to 'start.asm'
Just like each section of this tutorial before this one, we need to create a new file called 'irq.c'. Edit 'build.bat' to add the appropriate line to get GCC to compile to source, and also remember to add a new object file to get LD to link into our kernel.
#include < system.h >

/* These are own ISRs that point to our special IRQ handler
*  instead of the regular 'fault_handler' function */
extern void irq0();
...                    /* Add the rest of the entries here to complete the declarations */
extern void irq15();

/* This array is actually an array of function pointers. We use
*  this to handle custom IRQ handlers for a given IRQ */
void *irq_routines[16] =
{
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0
};

/* This installs a custom IRQ handler for the given IRQ */
void irq_install_handler(int irq, void (*handler)(struct regs *r))
{
    irq_routines[irq] = handler;
}

/* This clears the handler for a given IRQ */
void irq_uninstall_handler(int irq)
{
    irq_routines[irq] = 0;
}

/* Normally, IRQs 0 to 7 are mapped to entries 8 to 15. This
*  is a problem in protected mode, because IDT entry 8 is a
*  Double Fault! Without remapping, every time IRQ0 fires,
*  you get a Double Fault Exception, which is NOT actually
*  what's happening. We send commands to the Programmable
*  Interrupt Controller (PICs - also called the 8259's) in
*  order to make IRQ0 to 15 be remapped to IDT entries 32 to
*  47 */
void irq_remap(void)
{
    outportb(0x20, 0x11);
    outportb(0xA0, 0x11);
    outportb(0x21, 0x20);
    outportb(0xA1, 0x28);
    outportb(0x21, 0x04);
    outportb(0xA1, 0x02);
    outportb(0x21, 0x01);
    outportb(0xA1, 0x01);
    outportb(0x21, 0x0);
    outportb(0xA1, 0x0);
}

/* We first remap the interrupt controllers, and then we install
*  the appropriate ISRs to the correct entries in the IDT. This
*  is just like installing the exception handlers */
void irq_install()
{
    irq_remap();

    idt_set_gate(32, (unsigned)irq0, 0x08, 0x8E);
    ...          /* You need to add the rest! */
    idt_set_gate(47, (unsigned)irq15, 0x08, 0x8E);
}

/* Each of the IRQ ISRs point to this function, rather than
*  the 'fault_handler' in 'isrs.c'. The IRQ Controllers need
*  to be told when you are done servicing them, so you need
*  to send them an "End of Interrupt" command (0x20). There
*  are two 8259 chips: The first exists at 0x20, the second
*  exists at 0xA0. If the second controller (an IRQ from 8 to
*  15) gets an interrupt, you need to acknowledge the
*  interrupt at BOTH controllers, otherwise, you only send
*  an EOI command to the first controller. If you don't send
*  an EOI, you won't raise any more IRQs */
void irq_handler(struct regs *r)
{
    /* This is a blank function pointer */
    void (*handler)(struct regs *r);

    /* Find out if we have a custom handler to run for this
    *  IRQ, and then finally, run it */
    handler = irq_routines[r->int_no - 32];
    if (handler)
    {
        handler(r);
    }

    /* If the IDT entry that was invoked was greater than 40
    *  (meaning IRQ8 - 15), then we need to send an EOI to
    *  the slave controller */
    if (r->int_no >= 40)
    {
        outportb(0xA0, 0x20);
    }

    /* In either case, we need to send an EOI to the master
    *  interrupt controller too */
    outportb(0x20, 0x20);
}
		
The contents of 'irq.c'
In order to actually install the IRQ handling ISRs, we need to call 'irq_install' from inside the 'main' function in 'main.c'. Before you add the call, you need to add function prototypes to 'system.h' for 'irq_install', 'irq_install_handler', and 'irq_uninstall_handler'. 'irq_install_handler' is used for allowing us to install a special custom IRQ sub handler for our device under a given IRQ. In a later section, we will use 'irq_install_handler' to install a custom IRQ handler for both the System Clock (The PIT - IRQ0) and the Keyboard (IRQ1). Add 'irq_install' to the 'main' function in 'main.c', right after we install our exception ISRs. Immediately following that line, it's safe to allow IRQs to happen. Add the line:
__asm__ __volatile__ ("sti");
Congratulations, you have now followed how to step by step create a simple kernel that is capable of handling IRQs and Exceptions. An IDT is installed, along with a custom GDT to replace the original one loaded by GRUB. If you have understood all that is mentioned up until this point, you have passed one of the biggest hurdles associated with Operating System development. Most hobbyist OS developers do not successfully get past installing ISRs and an IDT. Next, we will learn about the simplest device to use an IRQ: The Programmable Interval Timer (PIT).

The PIT: A System Clock

The Programmable Interval Timer (PIT, model 8253 or 8254), also called the System Clock, is a very useful chip for accurately generating interrupts at regular time intervals. The chip itself has 3 channels: Channel 0 is tied to is tied to IRQ0, to interrupt the CPU at predictable and regular times, Channel 1 is system specific, and Channel 2 is connected to the system speaker. As you can see, this single chip offers several very important services to the system.
The only channels that you should every be concerned with are Channels 0 and 2. You may use Channel 2 in order to make the computer beep. In this section of the tutorial, we are only concerned with Channel 0 - mapped to IRQ0. This single channel of the timer will allow you to accurately schedule new processes later on, as well as allow the current task to wait for a certain period of time (as will be demonstrated shortly). By default, this channel of the timer is set to generate an IRQ0 18.222 times per second. It is the IBM PC/AT BIOS that defaults it to this. A reader of this tutorial has informed me that this 18.222Hz tick rate was used in order for the tick count to cycle at 0.055 seconds. Using a 16-bit timer tick counter, the counter will overflow and wrap around to 0 once every hour.
To set the rate at which channel 0 of the timer fires off an IRQ0, we must use our outportb function to write to I/O ports. There is a Data register for each of the timer's 3 channels at 0x40, 0x41, and 0x42 respectively, and a Command register at 0x43. The data rate is actually a 'divisor' register for this device. The timer will divide it's input clock of 1.19MHz (1193180Hz) by the number you give it in the data register to figure out how many times per second to fire the signal for that channel. You must first select the channel that we want to update using the command register before writing to the data/divisor register. What is shown in the following two tables is the bit definitions for the command register, as well as some timer modes.
7654310
CNTRRWModeBCD
CNTR - Counter # (0-2)
RW - Read Write mode
(1 = LSB, 2 = MSB, 3 = LSB then MSB)
Mode - See right table
BCD - (0 = 16-bit counter,
1 = 4x BCD decade counters)
ModeDescription
0Interrupt on terminal count
1Hardware Retriggerable one shot
2Rate Generator
3Square Wave Mode
4Software Strobe
5Hardware Strobe
Bit definitions for 8253 and 8254 chip's Command Register located at 0x43
To set channel 0's Data register, we need to select counter 0 and some modes in the Command register first. The divisor value we want to write to the Data register is a 16-bit value, so we will need to transfer both the MSB (Most Significant Byte) and LSB (Least Significant Byte) to the data register. This is a 16-bit value, we aren't sending data in BCD (Binary Coded Decimal), so the BCD field should be set to 0. Finally, we want to generate a Square Wave: Mode 3. The resultant byte that we should set in the Command register is 0x36. The above 2 paragraphs and tables can be summed up into this function. Use it if you wish, we won't use it in this tutorial to keep things simple. For accurate and easy timekeeping, I recommend setting to 100Hz in a real kernel.
void timer_phase(int hz)
{
    int divisor = 1193180 / hz;       /* Calculate our divisor */
    outportb(0x43, 0x36);             /* Set our command byte 0x36 */
    outportb(0x40, divisor & 0xFF);   /* Set low byte of divisor */
    outportb(0x40, divisor >> 8);     /* Set high byte of divisor */
}
		
Not bad, eh?
Create a file called 'timer.c', and add it to your 'build.bat' as you've been shown in the previous sections of this tutorial. As you analyse the following code, you will see that we keep track of the amount of ticks that the timer has fired. This can be used as a 'system uptime counter' as your kernel gets more complicated. The timer interrupt here simply uses the default 18.222Hz to figure out when it should display a simple "One second has passed" message every second. If you decide to use the 'timer_phase' function in your code, you should change the 'timer_ticks % 18 == 0' line in 'timer_handler' to 'timer_ticks % 100 == 0' instead. You could set the timer phase from any function in the kernel, however I recommend setting it in 'timer_install' if anything, to keep things organized.
#include < system.h >

/* This will keep track of how many ticks that the system
*  has been running for */
int timer_ticks = 0;

/* Handles the timer. In this case, it's very simple: We
*  increment the 'timer_ticks' variable every time the
*  timer fires. By default, the timer fires 18.222 times
*  per second. Why 18.222Hz? Some engineer at IBM must've
*  been smoking something funky */
void timer_handler(struct regs *r)
{
    /* Increment our 'tick count' */
    timer_ticks++;

    /* Every 18 clocks (approximately 1 second), we will
    *  display a message on the screen */
    if (timer_ticks % 18 == 0)
    {
        puts("One second has passed\n");
    }
}

/* Sets up the system clock by installing the timer handler
*  into IRQ0 */
void timer_install()
{
    /* Installs 'timer_handler' to IRQ0 */
    irq_install_handler(0, timer_handler);
}
		
Example of using the system timer: 'timer.c'
Remember to add a call to 'timer_install' in the 'main' function in 'main.c'. Having trouble? Remember to add a function prototype of 'timer_install' to 'system.h'! The next bit of code is more of a demonstration of what you can do with the system timer. If you look carefully, this simple function waits in a loop until the given time in 'ticks' or timer phases has gone by. This is almost the same as the standard C library's function 'delay', depending on your timer phase that you set:
/* This will continuously loop until the given time has
*  been reached */
void timer_wait(int ticks)
{
    unsigned long eticks;

    eticks = timer_ticks + ticks;
    while(timer_ticks < eticks);
}
If you wish, add this to 'timer.c' and a prototype to 'system.h'
Next, we will discuss how to use the keyboard. This involves installing a custom IRQ handler just like this tutorial, with hardware I/O on each interrupt.

The Keyboard

A keyboard is the most common way for a user to give a computer input, therefore it is vital that you create a driver of some sort for handling and managing the keyboard. When you get down to it, getting the basics of the keyboard isn't too bad. Here we will show the basics: how to get a key when it is pressed, and how to convert what's called a 'scancode' to standard ASCII characters that we can understand properly.
A scancode is simply a key number. The keyboard assigns a number to each key on the keyboard; this is your scancode. The scancodes are numbered generally from top to bottom and left to right, with some minor exceptions to keep layouts backwards compatible with older keyboards. You must use a lookup table (an array of values) and use the scancode as the index into this table. The lookup table is called a keymap, and will be used to translate scancodes into ASCII values rather quickly and painlessly. One last note about a scancode before we head into code is that if bit 7 is set (test with 'scancode & 0x80'), then this is the keyboard's way of telling us that a key was just released. Create yourself a 'kb.h' and do all your standard proceedures like adding a line for GCC and adding a file to LD's command line.
/* KBDUS means US Keyboard Layout. This is a scancode table
*  used to layout a standard US keyboard. I have left some
*  comments in to give you an idea of what key is what, even
*  though I set it's array index to 0. You can change that to
*  whatever you want using a macro, if you wish! */
unsigned char kbdus[128] =
{
    0,  27, '1', '2', '3', '4', '5', '6', '7', '8',	/* 9 */
  '9', '0', '-', '=', '\b',	/* Backspace */
  '\t',			/* Tab */
  'q', 'w', 'e', 'r',	/* 19 */
  't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',	/* Enter key */
    0,			/* 29   - Control */
  'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',	/* 39 */
 '\'', '`',   0,		/* Left shift */
 '\\', 'z', 'x', 'c', 'v', 'b', 'n',			/* 49 */
  'm', ',', '.', '/',   0,				/* Right shift */
  '*',
    0,	/* Alt */
  ' ',	/* Space bar */
    0,	/* Caps lock */
    0,	/* 59 - F1 key ... > */
    0,   0,   0,   0,   0,   0,   0,   0,
    0,	/* < ... F10 */
    0,	/* 69 - Num lock*/
    0,	/* Scroll Lock */
    0,	/* Home key */
    0,	/* Up Arrow */
    0,	/* Page Up */
  '-',
    0,	/* Left Arrow */
    0,
    0,	/* Right Arrow */
  '+',
    0,	/* 79 - End key*/
    0,	/* Down Arrow */
    0,	/* Page Down */
    0,	/* Insert Key */
    0,	/* Delete Key */
    0,   0,   0,
    0,	/* F11 Key */
    0,	/* F12 Key */
    0,	/* All other keys are undefined */
};		
Sample keymap. Add this array to your 'kb.c'
Converting a scancode to an ASCII value is easy with this:

mychar = kbdus[scancode];

Note that although we leave comments for the function keys and shift/control/alt, we leave them as 0's in the array: You need to think up some random values such as ASCII values that you normally wouldn't use so that you can trap them. I'll leave this up to you, but you should keep a global variable to be used as a key status variable. This keystatus variable will have 1 bit set for ALT, one for CONTROL, and one for SHIFT. It's also a good idea to have one for CAPSLOCK, NUMLOCK, and SCROLLLOCK. This tutorial will explain how to set the keyboard lights, but we will leave it up to you to actually write the code for it.
The keyboard is attached to the computer through a special microcontroller chip on your mainboard. This keyboard controller chip has 2 channels: one for the keyboard, and one for the mouse. Also note that it is through this keyboard controller chip that you would enable the A20 address line on the processor to allow you to access memory past the 1MByte mark (GRUB enables this, you don't need to worry about it). The keyboard controller, being a device accessible by the system, has an address on the I/O bus that we can use for access and control. The keyboard controller has 2 main registers: a Data register at 0x60, and a Control register at 0x64. Anything that the keyboard wants to send the computer is stored into the Data register. The keyboard will raise IRQ1 whenever it has data for us to read. Observe:
/* Handles the keyboard interrupt */
void keyboard_handler(struct regs *r)
{
    unsigned char scancode;

    /* Read from the keyboard's data buffer */
    scancode = inportb(0x60);

    /* If the top bit of the byte we read from the keyboard is
    *  set, that means that a key has just been released */
    if (scancode & 0x80)
    {
        /* You can use this one to see if the user released the
        *  shift, alt, or control keys... */
    }
    else
    {
        /* Here, a key was just pressed. Please note that if you
        *  hold a key down, you will get repeated key press
        *  interrupts. */

        /* Just to show you how this works, we simply translate
        *  the keyboard scancode into an ASCII value, and then
        *  display it to the screen. You can get creative and
        *  use some flags to see if a shift is pressed and use a
        *  different layout, or you can add another 128 entries
        *  to the above layout to correspond to 'shift' being
        *  held. If shift is held using the larger lookup table,
        *  you would add 128 to the scancode when you look for it */
        putch(kbdus[scancode]);
    }
}
		
This might look intimidating, but it's 80% comments ;) Add to 'kb.c'
As you can see, the keyboard will generate an IRQ1 telling us that it has data ready for us to grab. The keyboard's data register exists at 0x60. When the IRQ happens, we call this handler which reads from port 0x60. This data that we read is the keyboard's scancode. For this example, we check if the key was pressed or released. If it was just pressed, we translate the scancode to ASCII, and print that character out with one line. Write a 'keyboard_install' function that calls 'irq_install_handler' to install the custom keyboard handler for 'keyboard_handler' to IRQ1. Be sure to make a call to 'keyboard_install' from inside 'main'.
In order to set the lights on your keyboard, you must send the keyboard controller a command. There is a specific proceedure for sending the keyboard a command. You must first wait for the keyboard controller to let you know when it's not busy. To do this, you read from the Control register (When you read from it, it's called a Status register) in a loop, breaking out when the keyboard isn't busy:

if ((inportb(0x64) & 2) == 0) break;
After that loop, you may write the command byte to the Data register. You don't write to the control register itself except for in special cases. To set the lights on the keyboard, you first send the command byte 0xED using the described method, then you send the byte that says which lights are to be on or off. This byte has the following format: Bit0 is Scroll lock, Bit1 is Num lock, and Bit2 is Caps lock.
Now that you have basic keyboard support, you may wish to expand upon the code. This section on the keyboard was more to show you how to do the basics rather than give an extremely detailed overview of all of the keyboard controller's functions. Note that you use the keyboard controller to enable and handle the PS/2 mouse port. The auxilliary channel on the keyboard controller manages the PS/2 mouse. Up to this point we have a kernel that can draw to the screen, handle exceptions, handle IRQs, handle the timer, and handle the keyboard. Click to find what's next in store for your kernel development.

What's Left

What you do next to your kernel is completely up to you. The next thing you should think of writing is a memory manager. A memory manager will allow you to grab chunks of memory so that you can dynamically allocate and free memory as you need it. Using a memory manager, you can use more complicated data structures such as linked lists and binary trees to allow for more efficient storage and manipulation of data. It's also a way of preventing applications from writing to kernel pages, which is a feature of protection.
It's possible to write a VGA driver, also. Using a VGA driver, you can set up different graphics modes in your kernel, allowing higher resolutions and graphical display options such as buttons and images. If you want to go further, you could eventually look into VESA video modes for high color and higher resolutions.
You could eventually write a device interface which would allow you to load or unload kernel 'modules' as you need them. Add support for filesystems and disk drives so that you can access files off disks and open applications.
It's very possible that you add multitasking support and design scheduling algorithms to give certain tasks higher priority and longer time to run according to what the application is designed to run at. The multitasking system closely relies on your memory manager to give each task a separate space in memory.

Example kernel online source tree

start.asm
gdt.c
idt.c
irq.c
isrs.c
kb.c
main.c
scrn.c
timer.c
include/system.h
link.ld
build.bat
dev_kernel_grub.img
Get the whole tutorial and example kernel here (110KBytes).
I hope that this tutorial has given you a more thorough understanding of some of the various low-level items involved in creating a kernel: a driver for your processor and memory.

Popular Posts