分享
 
 
 

When Do We Write Our Chinese OS? (3)

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

在上一篇中我们实现了一个真正意义上的引导程序,此引导程序将计算机从启动状态时的

16位实模式转到了32位的保护模式下,并且将一个用C语言写的真正的内核载入了内存执

行,引导程序的工作已经完成,接下来的工作就是编写一个优良的操作系统的内核,这是

一个非常巨大的工程,我们需要一步一步的来完成,同样,今天我们只完成非常非常小的

一步。在上一篇中,内核只是显示出一个提示语,标志着内核已经启动了,它还不具有同

用户的交互功能,在这一篇中,我们将完成一个可以接受用户键盘输入的内核,这是内核

具有交互功能的第一步——能接受用户输入。

本篇所实现的内核主要采用C++语言书写,中断处理部分用到了很小一段汇编代码,因此

从本篇中你还将知道怎样实现C++与汇编语言的混合编程,本内核开放源码,采用的是C++

类模式的开放结构,你可以很轻松的修改它,让它功能更强,性能理好。

这里想首先介绍一下本内核的程序结构,这样你在阅读源代码的时候,会一目了然。

本内核定义了一个TVideo类,封装了对于VGA显卡的处理,它的声明如下:

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(){}

};

其中 TPos 也是一个类,(以T开头的,均是类名,以E开头的是枚举类型),其声明如下

class TPos{

public:

unsigned short X ;

unsigned short Y ;

};

其 EColor是一个包含了色彩变量的枚举类型:

enum EColor{ clBlack , clBlue , clGreen , clCyan , clRed , clMagenta ,

clBrown , clLightGray , clDarkGray , clLightBlue , clLightGreen ,

clLightCyan , clLightRed , clLightMagenta , clYellow , clWhite } ;

由于C++语言的封装机制,这使得在程序中要想在屏幕上输出是非常简单的事,下面我们就

来看看主程序对它们调用的例子:

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 ] ) ;

}

这段代码非常简单,就不详加解释了,它用各种色彩打印提示信息,下面就是它的执行效

下面是接受用户输入后的效果

下面,我们将来看看这都是怎么实现的。

阅读本文,最好有那么一点的汇编基础,另外,最好已经阅读过前两篇,因为很多东东是

同前两篇,特别是第二篇相关的~~~

好了,下面开始转如正题,首先,先介绍一下怎样处理显卡,

在上一篇中,我们已经知道了,通过把数据直接写到显存中就可以在屏幕上显示,这里我

们将更深入的介绍一下

现在的显卡大多是VGA标准兼容显卡,它分字符与图形模式,本文只介绍字符模式。在字符

模式下,它有25行,每行有80列,显存的地址为于0xb8000处,对于需要显示的每一个字符

,用两个字节来描述,第一个字节是要显示的这个字符的ASCII码,第二个字节是要显示的

这个字符的色彩属性,其中高4位用来表示背景色,低4位用来表示前景色,也就是字符本

身的色彩,色彩的对照表如下:

0 Black 黑色

1 Blue 蓝色

2 Green 绿色

3 Cyan 青色

4 Red 红色

5 Magenta 洋红

6 Brown 棕色

7 Light Gray 高亮灰色

8 Dark Gray 暗灰色

9 Light Blue 高亮蓝色

A Light Green 高亮绿色

B Light Cyan 高亮青色

C Light Red 高亮红色

D Light Magenta 高亮洋红

E Yellow 黄色

F White 白色

因此,你可以组合出你想要的字体色彩,怎样组合?请参见源程序相关代码。

我们知道 0xb8000 是显存的起始地址,也就是 0,0 处的字符所在的地址,那么 x,y 处的

字符位置在哪儿呢?因为一行显示80个字符,共有25行,所以我们可以用如下的公式计算

出 x,y 处的字符在内存中的地址:0xb8000 + y*80 + x

因此如果你想在 x,y处显示一个红色的‘A’你可以这样做

char *video = 0xb8000 ;

video += y*80 + x ;

*video = 'A' ;

video++;

*video = 0x04 ;

下面我们看看怎样处理光标

首先,我们要知道我们有两个端口,端口号分别是0x3d4,0x3d5。这第一个端口用于提供

索引,第二个端口用于提供数据。光标的位置存放在以14,15为索引值的两个端口寄存器中

。每一个端口寄存器只有8位,14号寄存器放存光标的低8位,15号寄存器存放光标的高8位

比如,我们要把光标定位到 x,y 处,首先我们要得到此处的偏移量:offset = y * 8 + x

然后把低8位放到 14号寄存器里,高8位放到15号寄存器里,就像这样:

out 0x3d4 , 14 ;//指定访问14号寄存器

out 0x3d5 , offset >> 8 ;

out ox3d4 , 15 ;

out 0x3d5 , offset ;

(注,这不是最终可执行的汇编代码,只是一个示意代码,实际代码请参考源程序)

要得到光标位置可以读这两个寄存器的值,得到偏移量,然后换算成x,y,详情请参看源程

序。

下面,我们将进入主题,将讲述一下怎样处理键盘输入。

处理键盘输入有两种方式,一是通过循环就行,在主程序中不断的查询0x60端口是否有数

据,如果有数据则表示有键盘输入,且此数据就是输入的键的键盘扫描码,将扫描码转换

成相应的ASCII码,然后显示就行。 这种情况非常简单,我们就不详细描述了,你可以改

动本源程序用此种方式实现。这里,我们常用一种新的方式进行,这就是通过中断进行。

要使用中断方式,我们就必须编写自己的中断处理程序,并且要让CPU知道此中断的中断处

理程序在什么地方,这通过IDT(中断描述符表)完成。此表的每一个表项对应一个中断,

每一个表项都指明此中断的中断处理程序在什么地方。因此首要的任务是要构造一个中断

描述符表。

中断描述符表一共可有256项,即256个中断。头三十二项,也就是0~31号中断,已经被CPU

及硬件所占用了,因此我们需要从第三十三项即32号中断开始构造我们自己的中断及中断

服务程序

中断描述符每项占8个字节(64位),所以我们定义如下的一个结构来处理它:

typedef struct{

unsigned long dword0 ;

unsigned long dword1 ;

} segment_desc ;

下面是我们定义中断描述符表的程序:

segment_desc idt[ 256 ] ; /* 中断号 0~255 */

unsigned long idt_desc[ 2 ] ;

unsigned long idt_addr ;

unsigned long keyboard_addr ;

unsigned long idt_offset = 0x8 ; /* IDT 在 GDT 中的位置,此程序中也是代码段在

GDT中的位置 */

// 发送4个ICW

ToPort( 0x20 , 0x11 ) ;

ToPort( 0xA0 , 0x11 ) ;

ToPort( 0x21 , 0x20 ) ;

ToPort( 0xA1 , 0x28 ) ;

ToPort( 0x21 , 0x4 ) ; // 在 Inter 出产的硬件中,PIC之间的联系是

ToPort( 0xA1 , 0x2 ) ; // 把 PIC1的IRQ2 同 PIC2 的IRQ1 联系起来

ToPort( 0x21 , 0x1 ) ;

ToPort( 0xA1 , 0x1 ) ;

// 下面设定中断屏蔽字,只许可键盘中断

ToPort( 0x21 , 0xFD ) ;

ToPort( 0xA1 , 0xFF ) ;

keyboard_addr = ( unsigned long )keyboard_interrupt ; /* 键盘中断处理程序

的位置 */

idt[ 0x21 ].dword0 = ( keyboard_addr & 0xffff ) | ( idt_offset << 16 ) ;

idt[ 0x21 ].dword1 = ( keyboard_addr & 0xffff0000 ) | 0x8e00 ;

/* 得到整个IDT的位置描述 */

idt_addr = ( unsigned long )idt ;

idt_desc[ 0 ] = 0x800 + ( ( idt_addr & 0xffff ) << 16 ) ;

idt_desc[ 1 ] = idt_addr >> 16 ;

__asm__( "lidt %0\n""sti" : "=m"( idt_desc ) ) ; /* 用lidt指令载入 IDT 表,

并用 sti 指令开中断*/

下面我们来解释一下这个程序

ToPort是程序定义的一个函数,具体代码请见源程序,这里只需要知道,它把由第二个参

数指定的数据,发到由第一个参数指定的端口中去。

先来看看这两行

idt[ 0x21 ].dword0 = ( keyboard_addr & 0xffff ) | ( idt_offset << 16 ) ;

idt[ 0x21 ].dword1 = ( keyboard_addr & 0xffff0000 ) | 0x8e00 ;

一个 IDT 表项有64位长,0~15位是中断处理程序地址的低16位,16~31是中断处理程序所

在的段在GDT中的位置(参见第二篇)。

最高的16位是中断处理程序地址的高16位,而留下的中间的16位是用来表明此是一个中断

门还是一个陷阱门还是一个任务门,及是16位中断还是32位中断等,非常复杂,要想详细

了解请查看Intel CPU 开发人员手册(网上有下的,没找到的可以找我要)。幸运的是,

我们不需要管得太多,只需记住在正常情况下是置为0x8e00就行。

下面我们详细讲一下代码中的“// 发送4个ICW”部份

我们现在已经知道,要建立中断,我们需要填充 IDT( 中断描述符表),它需要指出当发

生中断时,应跳到哪儿去执行。

为了使中断系统起作用,我们需要对PIC(可编程的中断控制器)进行编程,PIC 是可编程

的中断控制器,它可以处理硬件中断请求(IRQ0,IRQ1..等等),如果没有PIC,我们就不得

不按规则去查询哪一个硬件发生了中断,有了PIC,当硬件发生中断时,PIC把中断信号送

到CPU,CPU处理中断。我们实际上有两上PIC,第一个PIC1(端口号0x20~0x21)处理IRQ0~IR

Q7的请求,第二个PIC2(端口号0xA0~0xA1)处理 IRQ8~IRQ15 的请求

CPU只知道逻辑意义上的中断,不区分是物理上的软件中断还是硬件中断,因此我们必须把

CPU不知道的物理中断,映射为CPU知道的逻辑意义上的中断。在实模式下,这项工作由BIO

S来做,在保护模式下需要我们自己做。

因此我们需要初使化PICs,这通过发送一些ICW(初始化命令字)来实现对PICs的控制,它

们必须被精确的依次序发送,因为,它们之间是相互依赖的

1. 发送 ICW1 到 PIC1(20h) 与 PIC2(A0h) 中

2. 发送 ICW2 到 PIC1(21h) 与 PIC2(A1h) 中

3. 发送 ICW3 到 PIC1(21h) 与 PIC2(A1h)中

4. 发送 ICW4 到 PIC1(21h) 与 PIC2(A1h)中

ICW1 用来告诉PIC, 存在ICW4,(当两个PIC串联工作时,这是必须的)

ICW2 用来告诉PIC,把 IRQ0 与 IRQ7 映射到什么地方

(每一个PIC有八个管脚处理中断(IRQ0~IRQ7)

ICW3 用来告诉PIC,它们之间应当用几号IRQ(第几根管脚)进行同信

ICW4 用来告诉PIC,我们工作在保护模式,并且是由软件来处理还是自动处理中断

ICW1的结构

7 6 5 4 3 2 1 0

0 0 0 1 M 0 C I

I : 如果 ICW3 后面还有 ICW4,则置位

C : 如果不置位,表明这两个 PIC 工作在串联状态下

M : 表明 IR0 到 IR7 的线是水平触发,在PC机中,这位应为0(边沿触发)

ICW2 表明了 IRQ0 在中断向量表中的地址,在保护模式下,你应当改变它

7 6 5 4 3 2 1 0

A7 A6 A5 A4 A3 0 0 0

IRQ1 在中断向量表中的地址为 IRQ0的+1,IRQ2~IRQ7以此类推

ICW3 只在 这两个PIC是在串联工作状态下才被发送(ICW1 的C位置0),它的目的是在两

个PIC间建立联系

ICW3 关于 PIC1 的结构

7 6 5 4 3 2 1 0

IR7 IR6 IR5 IR4 IR3 IR2 IR1 IR0

如果 IR0 是置0的,则表明此根线联到一个外围设备

如果 IR0 是置1的,则表明此根线与PIC2联结

其余的与此类似

ICW3 关于 PIC2 的结构

7 6 5 4 3 2 1 0

0 0 0 0 I R Q

最后3位是PIC1的,与PIC2相联结的IRQ号

ICW4 的结构

7 6 5 4 3 2 1 0

0 0 0 0 0 0 EOI 80x86

EOI 表明中断的最后是自动处理还是由软件辅助处理。在PC中此位通常置0,表示软件必须

处理中断的最后扫尾工作

80x86 表明PIC是否工作在80x86的体系结构下

有了上述基础知识,你现在应当可以理解了吧。

下面我们再来看看:中断屏蔽字

PIC 1 处理的中断有

0 系统时钟

1 键盘

2 重定向到IRQ9 (PIC2的IRQ1),如果此位被置1,则屏幕掉所有来自PIC2的中断

3 串口 1 (COM2/4)

4 串口 2 (COM1/3)

5 声卡

6 软驱

7 并行端口

PIC 2 处理的中断有

0 实时时钟

1 来自 IRQ2 (PIC1)

2 保留

3 保留

4 鼠标

5 数学协处理器

6 硬盘

7 保留

当某位置位0表示允许其发出中断请求,置为1表示屏蔽其中断请救

程序中,有这样两行代码:

ToPort( 0x21 , 0xFD ) ;

ToPort( 0xA1 , 0xFF ) ;

其中 0xFD 就是 11111101 ;即只允许 PIC1的第二个中断请求,即键盘中断请求。

完工!本篇任务已经胜利完成~~~ ^_^,所有源代码可在如下地址下载

ftp://202.118.239.46/Incoming/Other/BTC/temp/pyos/pyos3.zip

BTW:

在本实验进行的过程中,在BBS上得到了很多老师同学的鼓励,正是由于这种支持力量的

存在,使我获得了将本实验进行下去的力量,在此深表感谢!同时对于此中不计其数的错

误,非常希望各位老师、同学、大牛小牛:P~~,批评指教!

仍然留个mail:

xieyubo@126.com

原文:http://purec.binghua.com/Article/ShowArticle.asp?ArticleID=5

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有