When do we write our Chinese OS? (3)

Source: Internet
Author: User

In the previous article, we implemented a true Bootstrap program that redirects the computer from the startup status
The 16-bit real mode is switched to the 32-bit protection mode, and a real kernel written in C language is loaded into the memory
Line, the boot program has been completed, the next job is to write a good operating system kernel, this is
We need to complete a very huge project step by step. Similarly, today we have only completed very, very small projects.
Step by step. In the previous article, the kernel only showed a prompt, indicating that the kernel has been started and does not have the same
User interaction function. In this article, we will complete a kernel that can accept user keyboard input, which is the kernel
The first step with interactive functions-accepting user input.

The kernel implemented in this article is mainly written in C ++, And the interrupt processing part uses a small piece of assembly code. Therefore
From this article, you will also know how to implement the mixed programming of C ++ and assembly language. The open source code of this kernel uses C ++.
You can easily modify the Open Structure of the class mode so that it has better functions and better performance.

Here, I would like to first introduce the program structure of the kernel, so that you can read the source code at a glance.
This kernel defines a tvideo class that encapsulates the processing of VGA graphics cards. Its statement is as follows:
Class tvideo {
Public:
Static TPOs getpos ();
Static void setpos (TPOs & Pos );
Static void setpos (unsigned short X, unsigned short Y );
Static void clearscreen ();
Static void print (const char * MSG, ecolor frontcolor = clwhite,
Ecolor backcolor = clblack );
Static void print (const char MSG, ecolor frontcolor = clwhite,
Ecolor backcolor = clblack );
Ecolor backcolor;
Ecolor frontcolor;
Protected:
Tvideo (){}
};
Here, TPOs is also a class (the class names starting with T and the enumeration type starting with E). Its declaration is as follows:
Class TPOs {
Public:
Unsigned short X;
Unsigned short Y;
};
Its ecolor is an enumeration type that contains color variables:
Enum ecolor {clblack, clblue, clgreen, clcyan, clred, clmagenta,
Clbrown, cllightgray, cldarkgray, cllightblue, cllightgreen,
Cllightcyan, cllightred, cllightmagenta, clyellow, clwhite };

Due to the encapsulation mechanism of the c ++ language, it is very easy to output data on the screen in the program.
Let's take a look at the example of the main program calling them:
Char * msg0 = "Welcome to hit operation system! Version 0.0003 by xyb ";
Char * msg1 = "Please input :";

Ecolor color [] = {cllightblue, cllightgreen, cllightcyan,
Cllightred, cllightmagenta, clyellow, clwhite,
Cllightblue };
Int I = 0;
While (msg0 [I]! = '/0 '){
Tvideo: Print (msg0 [I ++], color [I % 8]);
}
Tvideo: setpos (0, 2 );
I = 0;
While (msg1 [I]! = '/0 '){
Tvideo: Print (msg1 [I ++], clwhite, color [I % 8]);
}
This code is very simple and I will explain it in detail. It uses various colors to print the prompt information. The following figure shows its execution efficiency.
Result

The following figure shows the effect after receiving user input.

Next, let's see how this is implemented in the future.

Read this article, it is best to have such a little compilation basis, in addition, it is best to have read the first two articles, because many
This is the same as the first two articles, especially the second one ~~~

Now, let's get started. First, let's introduce how to handle the video card,
In the previous article, we already know that you can display the data on the screen by writing the data directly to the video memory.
We will give a more in-depth introduction

Most of the current graphics cards are VGA standard compatible graphics cards, which are in character and graphics mode. This article only introduces the character mode. In characters
In the mode, it has 25 rows, each row has 80 columns, and the display address is at 0xb8000. For each character to be displayed
It is described in two bytes. The first byte is the ASCII code of the character to be displayed, and the second byte is to be displayed.
The color attribute of this character. Four digits in height indicates the background color, and four digits in height indicates the foreground color, that is, the character base.
The color and color table are as follows:
0 black
1 blue
2 green
3 cyan blue
4 red
5 Magenta
6 Brown
7 light gray highlighted gray
8 dark gray
9 light blue highlight blue
A light green highlighted green
B light cyan highlighted blue
C light red highlighted red
D light magenta highlight
E yellow
F white
Therefore, you can combine the font colors you want and how to combine them? See source code.

We know that 0xb8000 is the starting address of the video memory, that is, the address where the characters at 0 and 0 are located.
Where are the character locations? Because a row contains 80 characters and a total of 25 lines, we can use the following formula to calculate
Address of the characters at the output position X and Y in the memory: 0xb8000 + y * 80 + x
So if you want to display a red 'A' in X and Y, you can do this.
Char * Video = 0xb8000;
Video + = y * 80 + X;
* Video = 'a ';
Video ++;
* Video = 0x04;

Next let's take a look at how to handle the cursor
First, we need to know that we have two ports: 0x3d4 and 0x3d5. The first port is used to provide
Index. The second port is used to provide data. The cursor position is stored in the two port registers with the index value
. Each port register has only 8 bits, register 14 stores the low 8 bits of the cursor, and register 15 stores the high 8 bits of the cursor.

For example, to position the cursor at X and Y, first obtain the offset: offset = y * 8 + X.

Then put the Lower 8 bits in register 14 and the higher 8 bits in register 15, as shown in the following figure:
Out 0x3d4, 14; // specify the access to register 14
Out 0x3d5, Offset> 8;
Out ox3d4, 15;
Out 0x3d5, offset;
(Note: This is not the final executable assembly code, but a sample code. For the actual code, see the source program)
To get the cursor position, read the values of the two registers, get the offset, and convert it to X, Y. For more information, see source program
.

Next, we will go to the topic to explain how to process keyboard input.
There are two ways to process keyboard input. One is to use a loop to continuously query whether port 0x60 is correct in the main program.
Data, if there is data, it indicates there is a keyboard input, and this data is the Keyboard Scan code of the Input key, the scan code is converted
The corresponding ASCII code. This situation is very simple and we will not describe it in detail. You can modify it.
The dynamic source program is implemented in this way. Here, we often use a new method, which is through interruption.

To use the interrupt method, we must write our own interrupt processing program and let the CPU know the interrupt location of the interrupt.
Where is the program? This is done through IDT (Interrupt Descriptor Table. Each table item in this table corresponds to an interrupt,
Each table item specifies where the interrupt handler is located. Therefore, the first task is to construct an interrupt.
Descriptor Table.

The Interrupt Descriptor Table contains a total of 256 items, that is, 256 interruptions. The first items, that is, 0 ~ The instance is interrupted on the 31st and has been CPU
And the hardware is occupied, so we need to construct our own interruptions and interruptions from the first item, namely the 32 interrupt.
Service programs
Each Interrupt Descriptor occupies 8 bytes (64-bit), so we define the following structure to process it:
Typedef struct {
Unsigned long dword0;
Unsigned long dword1;
} Segment_desc;
The program that defines the Interrupt Descriptor Table is as follows:
Segment_desc IDT [256];/* interrupt No. 0 ~ 255 */
Unsigned long idt_desc [2];
Unsigned long idt_addr;
Unsigned long keyboard_addr;
Unsigned long idt_offset = 0x8;/* the position of IDT in gdt.
Location in gdt */

// Send 4 ICWs
Toport (0x20, 0x11 );
Toport (0xa0, 0x11 );
Toport (0x21, 0x20 );
Toport (0xa1, 0x28 );

Toport (0x21, 0x4); // In the hardware produced by Inter, the relationship between PIC is
Toport (0xa1, 0x2); // associate the irq2 of pic1 with the irq1 of pic2.

Toport (0x21, 0x1 );
Toport (0xa1, 0x1 );

// Set the interrupt blocking character below. Only the keyboard interrupt is allowed.
Toport (0x21, 0xfd );
Toport (0xa1, 0xff );

Keyboard_addr = (unsigned long) keyboard_interrupt;/* keyboard interrupt handler
Location */
IDT [0x21]. dword0 = (keyboard_addr & 0 xFFFF) | (idt_offset <16 );
IDT [0x21]. dword1 = (keyboard_addr & 0xffff0000) | 0x8e00;

/* Get the location description of the entire IDT */
Idt_addr = (unsigned long) IDT;
Idt_desc [0] = 0x800 + (idt_addr & 0 xFFFF) <16 );
Idt_desc [1] = idt_addr> 16;

_ ASM _ ("LIDT % 0/N" "Sti": "= m" (idt_desc);/* load the IDT table with the LIDT command,
Use the STI command to interrupt */
Next, let's explain this program.
Toport is a function defined by the program. For more information about the code, see the source program.
The specified data is sent to the port specified by the first parameter.
Let's take a look at the two rows.
IDT [0x21]. dword0 = (keyboard_addr & 0 xFFFF) | (idt_offset <16 );
IDT [0x21]. dword1 = (keyboard_addr & 0xffff0000) | 0x8e00;
An IDT table has 64-bit length, ranging from 0 ~ The 15-bit is the 16-bit low address of the interrupt handler, 16-bit ~ 31 is the Interrupt Processing Program
Position of the segment in gdt (see Article 2 ).
The highest 16 bits are the 16 bits of the interrupt handler address, while the middle 16 bits are used to indicate that this is an interrupt.
The door is a trap door, a task door, 16-bit interrupt, 32-bit interrupt, etc. It is very complicated.
For more information, see the Intel CPU developer manual. (If you have any Manual Online, contact me if you have not found it ). Fortunately,
We don't need to worry too much, just remember to set it to 0x8e00 under normal circumstances.
 
The following describes in detail the "// send 4 ICWs" section in the code.

We now know that to create an interrupt, we need to fill the IDT (Interrupt Descriptor Table) and it needs to point out
Where should I perform the operation when an interruption occurs.

To make the interrupt system work, we need to program the PIC (Programmable Interrupt Controller), which is programmable
The interrupt controller that can process hardware interrupt requests (irq0, irq1.). Without pic, we cannot
Do not query which hardware is interrupted according to the rules. When the hardware is interrupted, the PIC sends the interruption signal.
To the CPU, CPU processing is interrupted. We actually have two pictures, the first pic1 (Port 0x20 ~ 0x21) irq0 ~ IR
Q7 request, second pic2 (Port: 0xa0 ~ 0xa1) irq8 ~ Irq15 requests

CPU only knows the logical interruption, but does not distinguish between physical software interruption or hardware interruption. Therefore, we must
Physical interruptions that the CPU does not know are mapped to logical interruptions that the CPU knows. In real mode, this work is done by bio
S. In the protection mode, we need to do it ourselves.

Therefore, we need to initialize pics, which is to control pics by sending some ICW (initialization command words ).
They must be precisely sent in order, because they are mutually dependent.
1. Send icw1 to pic1 (20 h) and pic2 (a0h)
2. Send icw2 to pic1 (21 h) and pic2 (a1h)
3. Send icw3 to pic1 (21 h) and pic2 (a1h)
4. Send icw4 to pic1 (21 h) and pic2 (a1h)

Icw1 is used to tell the PIC that icw4 exists (this is required when two PIC works in tandem)
Icw2 is used to tell the PIC where irq0 and irq7 are mapped.
(Each PIC has eight pins for interrupt handling (irq0 ~ Irq7)
Icw3 is used to tell the PIC that the number of IRQ (the number of pins) should be used between them for the same message.
Icw4 is used to tell PIC whether we work in protection mode and are handled by software or automatically handle interruptions.

Icw1 Structure
7 6 5 4 3 2 1 0
0 0 0 1 M 0 C I

I: If icw3 is followed by icw4
C: If no bit is set, it indicates that the two PIC instances are working in series.
M: indicates that the line from ir0 to ir7 is horizontally triggered. in PC, the line should be 0 (edge triggered)

Icw2 indicates the address of irq0 In the interrupt vector table. In protection mode, you should change it.
7 6 5 4 3 2 1 0
A7 A6 A5 A4 A3 0 0 0

The IP address of irq1 In the interrupt vector table is + 1, irq2 ~ of irq0 ~ Irq7 and so on

Icw3 is sent only when the two PIC are in the serial working state (the C position of icw1 is 0 ).
Establish connections between PIC (s)

Icw3 structure of pic1
7 6 5 4 3 2 1 0
Ir7 ir6 ir5 ir4 ir3 ir2 ir1 ir0

If ir0 is set to 0, it indicates that the root thread is connected to a peripheral device.
If ir0 is set to 1, it indicates that this root line is associated with pic2.
The rest is similar to this

Icw3 structure of pic2
7 6 5 4 3 2 1 0
0 0 0 0 I r Q
The last three digits are the IRQ numbers associated with pic2.

Icw4 Structure
7 6 5 4 3 2 1 0
0 0 0 0 0 EOI 80x86
EOI indicates whether the interrupt is automatically handled or assisted by software. In PC, this bit is usually set to 0, indicating that the software is required
Handle the final scanning of interrupted tasks
80x86 indicates whether the PIC works under the 80x86 architecture

With the above basic knowledge, you should be able to understand it now.
Next let's take a look: interrupt Blocking
The interruptions processed by PIC 1 are:
0 system clock
1 keyboard
2. Redirect to irq9 (irq1 of pic2). If this bit is set to 1, all interruptions from pic2.
3 Serial Port 1 (com2/4)
4 Serial Port 2 (COM1/3)
5. Sound Card
6. Soft drive
7. Parallel Port

The interruptions processed by PIC 2 are:
0 real-time clock
1 From irq2 (pic1)
2 reserved
3 Reserved
4 mouse
5. Mathematical coprocessor
6 hard disks
7. Retain

When a position of 0 indicates that it is allowed to send an interrupt request, if it is set to 1, it indicates that the disconnection request is blocked.
There are two lines of code in the program:
Toport (0x21, 0xfd );
Toport (0xa1, 0xff );
0xfd is 11111101, that is, only the second interrupt request of pic1 is allowed, that is, the keyboard interrupt request.

Finished! This task has been completed successfully ~~~ ^ _ ^, All source code can be downloaded at the address below
FTP: // 202.118.239.46/incoming/other/BTC/temp/pyos/pyos3.zip
BTW:
In the course of this experiment, many teachers and students have encouraged the BBS. It is precisely because of this support strength.
Yes, so I have gained the power to continue this experiment. Thank you very much! At the same time, there are countless errors
By mistake, I very much hope that all teachers, students, and Daniel and the calf will: P ~~, Criticize and advise!
Still leave a mail:
Xieyubo@126.com

Http://purec.binghua.com/Article/ShowArticle.asp? ArticleID = 5

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.