harib14a:

  接着上一天的内容,我们来思考一个问题:当所有的LEVEL中都没有任务的时候怎么办?采用前面定时器链表中加入“哨兵”的思路,我们加入一个idle任务,去执行HLT。接下来我们只需要将这个闲置任务idle放在最下层的LEVEL就行了。之后修改一下HariMain测试一下结果。

void task_idle(void)//限制任务idle
{
for (;;) {
io_hlt();//让CPU不断的HLT等待中断的到来
}
}
struct TASK *task_init(struct MEMMAN *memman)
{
struct TASK *task, *idle;
//.....
idle = task_alloc();//为dile分配空间
idle->tss.esp = memman_alloc_4k(memman, * ) + * ;
idle->tss.eip = (int) &task_idle;//dile任务做的事,执行这个函数
idle->tss.es = * ;
idle->tss.cs = * ;
idle->tss.ss = * ;
idle->tss.ds = * ;
idle->tss.fs = * ;
idle->tss.gs = * ;
//idle放在最后一层,FLAG置1
task_run(idle, MAX_TASKLEVELS - , );
return task;
}

harib14b:
  这里我们尝试创建一个命令行窗口。首先对任务B的程序进行一些修改,并将任务A的一部分融合进去,把计数代码从任务B中删除;然后单独创建一个新的任务

void HariMain(void)
{ //.....
/* sht_cons */
sht_cons = sheet_alloc(shtctl);//命令行窗口图层、缓冲区
buf_cons = (unsigned char *) memman_alloc_4k(memman, * );
sheet_setbuf(sht_cons, buf_cons, , , -);/* 设置缓冲区,将CMD窗口图层写到缓冲区中 */
make_window8(buf_cons, , , "console", );//用CMD缓冲区的内容画窗口
make_textbox8(sht_cons, , , , , COL8_000000);//CMD图层下的文本框
task_cons = task_alloc();             //CMD窗口的任务、分配空间
task_cons->tss.esp = memman_alloc_4k(memman, * ) + * - ;
task_cons->tss.eip = (int) &console_task;   //任务执行的函数(内容)
task_cons->tss.es = * ;
task_cons->tss.cs = * ;
task_cons->tss.ss = * ;
task_cons->tss.ds = * ;
task_cons->tss.fs = * ;
task_cons->tss.gs = * ;
*((int *) (task_cons->tss.esp + )) = (int) sht_cons;//将CMD图层地址放到CMD任务块首部
task_run(task_cons, , );   /* 设置LEVEL和优先级level=2, priority=2 */
//.....
sheet_slide(sht_back, , );//图层开始显示的位置
sheet_slide(sht_cons, , );
sheet_slide(sht_win, , );
sheet_slide(sht_mouse, mx, my);
sheet_updown(sht_back, );  //设置图层的高度
sheet_updown(sht_cons, );
sheet_updown(sht_win, );
sheet_updown(sht_mouse, );
//.....
}
void console_task(struct SHEET *sheet)
{
struct FIFO32 fifo;       //闪烁定时器缓冲区
struct TIMER *timer;       //闪烁定时器
struct TASK *task = task_now();//当前运行中任务地址 int i, fifobuf[], cursor_x = , cursor_c = COL8_000000;
fifo32_init(&fifo, , fifobuf, task);//当前任务写到缓冲区 timer = timer_alloc();          //闪烁定时器设置
timer_init(timer, &fifo, );
timer_settime(timer, ); for (;;) {
io_cli();       //禁止中断
if (fifo32_status(&fifo) == ) {
task_sleep(task);//缓冲区没有内容,让当前运行中的任务睡眠
io_sti();允许中断
} else {
i = fifo32_get(&fifo);
io_sti();
if (i <= ) { /* 光标闪烁定时器超时 */
if (i != ) {
timer_init(timer, &fifo, ); /* i=1;此时重新设置光标定时器,写入FIFO中改为0 */
cursor_c = COL8_FFFFFF;    //黑色背景
} else {
timer_init(timer, &fifo, ); /* i=1;此时重新设置光标定时器,写入FIFO中改为0 */
cursor_c = COL8_000000;    //白色背景
}
timer_settime(timer, );     //50个count计数的时间、文本显示框
boxfill8(sheet->buf, sheet->bxsize, cursor_c, cursor_x, , cursor_x + , );
sheet_refresh(sheet, cursor_x, , cursor_x + , );//刷新显示
}
}
}
}

harib14c:
  到这里我们已经有两个窗口了,下面我们一步一步实现按下TAB的时候切换输入的窗口的功能,这里我们先修改窗口标题栏的颜色
  1、改写make_windows8()

//我们将描绘窗口标题栏的代码和描绘窗口文本框的代码区分开来
void make_window8(unsigned char *buf, int xsize, int ysize, char *title, char act)
{ //这一部分是描绘窗口输入文本框的代码
//第一步:将设置的文本框的信息写到缓存中
boxfill8(buf, xsize, COL8_C6C6C6, , , xsize - , );
boxfill8(buf, xsize, COL8_FFFFFF, , , xsize - , );
boxfill8(buf, xsize, COL8_C6C6C6, , , , ysize - );
boxfill8(buf, xsize, COL8_FFFFFF, , , , ysize - );
boxfill8(buf, xsize, COL8_848484, xsize - , , xsize - , ysize - );
boxfill8(buf, xsize, COL8_000000, xsize - , , xsize - , ysize - );
boxfill8(buf, xsize, COL8_C6C6C6, , , xsize - , ysize - );
boxfill8(buf, xsize, COL8_848484, , ysize - , xsize - , ysize - );
boxfill8(buf, xsize, COL8_000000, , ysize - , xsize - , ysize - );
//第二步:调用这个函数,将缓存中的内容写到VRAM中
make_wtitle8(buf, xsize, title, act);
return;
}
void make_wtitle8(unsigned char *buf, int xsize, char *title, char act)
{ //这里是描绘窗口标题栏的代码,这里没有什么可讲的。
//就是把两部分代码区分开,便于设置标题的颜色(用ACT参数)
static char closebtn[][] = {
"OOOOOOOOOOOOOOO@",
"OQQQQQQQQQQQQQ$@",
"OQQQQQQQQQQQQQ$@",
"OQQQ@@QQQQ@@QQ$@",
"OQQQQ@@QQ@@QQQ$@",
"OQQQQQ@@@@QQQQ$@",
"OQQQQQQ@@QQQQQ$@",
"OQQQQQ@@@@QQQQ$@",
"OQQQQ@@QQ@@QQQ$@",
"OQQQ@@QQQQ@@QQ$@",
"OQQQQQQQQQQQQQ$@",
"OQQQQQQQQQQQQQ$@",
"O$$$$$$$$$$$$$$@",
"@@@@@@@@@@@@@@@@"
};
int x, y;
char c, tc, tbc;
if (act != ) {
tc = COL8_FFFFFF;
tbc = COL8_000084;
} else {
tc = COL8_C6C6C6;
tbc = COL8_848484;
}
boxfill8(buf, xsize, tbc, , , xsize - , );
putfonts8_asc(buf, xsize, , , tc, title);
for (y = ; y < ; y++) {
for (x = ; x < ; x++) {
c = closebtn[y][x];
if (c == '@') {
c = COL8_000000;
} else if (c == '$') {
c = COL8_848484;
} else if (c == 'Q') {
c = COL8_C6C6C6;
} else {
c = COL8_FFFFFF;
}
buf[( + y) * xsize + (xsize - + x)] = c;
}
}
return;
}

  2、修改HariMain

//加入TAB键的判断,调用make_wtitle8并修改两个窗口标题的颜色
void HariMain(void)
{                 //.....i为从FIFO中获得中断的数据
if ( <= i && i <= ) { /* 如果获得的是键盘中断的数据 */
//......
if (i == + 0x0f) { /* 键盘中断的键值是TAB键 */
if (key_to == ) { //这里的原理和光标闪烁是一样的
key_to = ;   //发送到CMD窗口
make_wtitle8(buf_win, sht_win->bxsize, "task_a", );
make_wtitle8(buf_cons, sht_cons->bxsize, "console", );
} else {
key_to = ;   //发送到任务A
make_wtitle8(buf_win, sht_win->bxsize, "task_a", );
make_wtitle8(buf_cons, sht_cons->bxsize, "console", );
}           //接着刷新CMD和窗口图层
sheet_refresh(sht_win, , , sht_win->bxsize, );
sheet_refresh(sht_cons, , , sht_cons->bxsize, );
}
}
//.....
}

harib14d:
  我们虽然让窗口的颜色从灰色变成了蓝色,下面我们实现向CMD窗口中输入字符串。只要在键盘被按下的时候,往CMD任务的FIFO中发送数据即可。考虑到很多任务都需要用到缓冲区,我们将FIFO加到任务结构体TASK中。
  1、将FIFO加到任务结构体TASK中。

struct TASK {
int sel, flags; /* sel表示GDT的编号 */
int level, priority;
struct FIFO32 fifo;//任务的缓冲区
struct TSS32 tss;//任务段的内容
};

  2、修改HariMain

//判断key_to的值,并向FIFO发送数据。
void HariMain(void)
{ //....
for (;;) {
//...
if ( <= i && i <= ) {    /* 键盘数据 */
sprintf(s, "%02X", i - ); //在背景图层显示按下的 键值
putfonts8_asc_sht(sht_back, , , COL8_FFFFFF, COL8_008484, s, );
if (i < 0x54 + && keytable[i - ] != ) { /* 在keytable表中找到一般字符 */
if (key_to == ) {   /* 把获得的字符发送给任务A的缓冲区 */
if (cursor_x < ) {//窗口的最大长度128像素
/* 每输入一个字符,光标向后移动8个像素 */
s[] = keytable[i - ];
s[] = ;
//在窗口(任务A)图层显示字符
putfonts8_asc_sht(sht_win, cursor_x, , COL8_000000, COL8_FFFFFF, s, );
cursor_x += ;
}
} else {/* 发送到CMD窗口(任务)的缓冲区 */
fifo32_put(&task_cons->fifo, keytable[i - ] + );
}
}
if (i == + 0x0e) { /* Backspace键 */
if (key_to == ) { /* 发送信息到A任务 */
if (cursor_x > ) {
         /* 闪烁光标向左移动8个像素 */
putfonts8_asc_sht(sht_win, cursor_x, , COL8_000000, COL8_FFFFFF, " ", );
cursor_x -= ;
}
} else {       /* 发送到任务CMD窗口 */
fifo32_put(&task_cons->fifo, + );
}
}
//......
}
}
//...
}

harib14e:
  这里我们来实现“!”和“%”的输入,我们先来看看Shift键的编码:
         按下    抬起
  左Shift   0x2a   0xaa
  右Shift   0x36   0xb6
  因此我们准备一个key_shift变量,当左Shift按下时置为1,当右Shift按下时置为2,两个都不按时置为0,两个都按下的时候就置为3。当key_shift为0时,我们用keytable0[]进行字符编码转换,当key_shift不为0时,我们使用keytable1[]进行转换。

void HariMain(void)
{
static char keytable0[0x80] = {
, , '', '', '', '', '', '', '', '', '', '', '-', '^', , ,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', , , 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', ':', , , ']', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', ',', '.', '/', , '*', , ' ', , , , , , ,
, , , , , , , '', '', '', '-', '', '', '', '+', '',
'', '', '', '.', , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , 0x5c, , , , , , , , , , 0x5c, ,
};
static char keytable1[0x80] = {
, , '!', 0x22, '#', '$', '%', '&', 0x27, '(', ')', '~', '=', '~', , ,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '`', '{', , , 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', '+', '*', , , '}', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', '<', '>', '?', , '*', , ' ', , , , , , ,
, , , , , , , '', '', '', '-', '', '', '', '+', '',
'', '', '', '.', , , , , , , , , , , , ,
, , , , , , , , , , , , , , , ,
, , , '_', , , , , , , , , , '|', ,
};
//.....
for (;;) {
//......
if ( <= i && i <= ) { /* 获得的是键盘的数据 */
sprintf(s, "%02X", i - );
putfonts8_asc_sht(sht_back, , , COL8_FFFFFF, COL8_008484, s, );
if (i < 0x80 + ) { /* 按照键盘的编码进行转换 */
if (key_shift == ) {
s[] = keytable0[i - ];
} else {
s[] = keytable1[i - ];
}
} else {
s[] = ;
}
if (s[] != ) { /* 一般字符 */
if (key_to == ) { /* 想任务A中写 */
if (cursor_x < ) {
/* 光标后移 */
s[] = ;
putfonts8_asc_sht(sht_win, cursor_x, , COL8_000000, COL8_FFFFFF, s, );
cursor_x += ;
}
} else { /* 向CMD窗口中写 */
fifo32_put(&task_cons->fifo, s[] + );
}
}
//........
if (i == + 0x2a) { /* 左shift ON */
key_shift |= ;
}
if (i == + 0x36) { /* 右shift ON */
key_shift |= ;
}
if (i == + 0xaa) { /* 左shift OFF */
key_shift &= ~;
}
if (i == + 0xb6) { /* 右shift OFF */
key_shift &= ~;
}
//./.....
}
}
}//.....
}

harib14f:
  接下来我们要实现大写、小写字母的输入,要区分字母的大小写,我们必须同时判断Shift键的状态以及CapsLock的状态。这里笔者总结出小写字母的条件:Shift和CapsLock的状态相同(同为ON或者OFF)我们从BIOS中获取CapsLock的状态
    binfo->leds的第四位->ScrollLock的状态
    binfo->leds的第五位->NumLock 的状态
    binfo->leds的第六位->CapsLock 的状态
  我们使用上述的数据和规则来处理大小写字母的输入问题:

void HariMain(void)
{ //......这里只取binfo->leds4-6位的数据。
int key_to = , key_shift = , key_leds = (binfo->leds >> ) & ;
//......
if ('A' <= s[] && s[] <= 'Z') { /* 大写字母A-Z之间 */
if (((key_leds & ) == && key_shift == ) || //自行在草稿纸上推算
((key_leds & ) != && key_shift != )) {
s[] += 0x20; /* 字符值0x20变为小写字母的值 */
}
}
//......
}

harib14g:
  对键盘上锁定键的支持,我们在上面已经实现了锁定键的切换,但是有时候指示灯并不进行相应的切换,下面我们来解决这个问题。先来看看常用三个锁定键的键值:
      0x3a : CapsLock
      0x45 : NumLock
      0x46 : ScrollLock
  对于键盘上锁定键LED的控制,可采用以下方法想键盘发送指令和数据;

    1. 读取状态寄存器,等待bit1的值变为0
    2. 向数据输出(0060)写入要发送的1个字节的数据
    3. 等待键盘返回1个字节的信息,这和等待键盘输入采用的方法相同(用IRQ等待或者用轮询状态寄存器bit1的值知道变为0都可以)
    4. 返回的信息如果是0xfa,表示1个字节的数据已经成功发送给键盘了。如果为0xfe则表示发送失败,返回 1 步重新发送

  要控制LED的状态,需要按照上述方法执行两次,向键盘发送EDxx数据,其中,xx的bit0(ScrollLock)bit1(NumLock)bit2(CapsLock),0表示熄灭,1表示点亮。

void HariMain(void)
{ struct FIFO32 fifo, keycmd;
int fifobuf[], keycmd_buf[];
//keycmd_wait表示键盘向控制器发送数据的状态。
// ==-1:通常状态可以发送指令
//!=-1:键盘控制器正在等待发送的数据(保存在keycmd_wait)
//key_leds设置指示灯的状态,这个数据是要送到键盘控制器的 int key_to = , key_shift = , key_leds = (binfo->leds >> ) & , keycmd_wait = -;
//初始化KEYCMD任务A想键盘发送数据的缓冲区
fifo32_put(&keycmd, KEYCMD_LED);
fifo32_put(&keycmd, key_leds);
//.....
for (;;) {
if (fifo32_status(&keycmd) > && keycmd_wait < ) {
/* 如果存在想键盘控制器发送的数据,则发送它 */
keycmd_wait = fifo32_get(&keycmd);//从KEYCMD缓冲区中获得要向键盘发送的数据
wait_KBC_sendready();        //等待ACK;等待键盘准备好
io_out8(PORT_KEYDAT, keycmd_wait);//将keycmd_wait的值送到键盘控制器
}
io_cli();
if (fifo32_status(&fifo) == ) {
task_sleep(task_a);  //任务A中没有信息,没有从键盘接收的数据,休眠
io_sti();
} else {          //任务A有信息
i = fifo32_get(&fifo);//从任务A的缓冲区得到信息
io_sti();
if ( <= i && i <= ) {/* 得到键盘数据 */
//.....
if (i == + 0x3a) { /* CapsLock */
key_leds ^= ;   //设置CapsLock指示灯亮(ley_leds的第2位为1)
fifo32_put(&keycmd, KEYCMD_LED);//先送入默认的KEYCMD_LED是为了下一次获得ACK(0xfa)
fifo32_put(&keycmd, key_leds); //接着再送入设置的指示灯状态信息
}
if (i == + 0x45) {       /* NumLock */
key_leds ^= ;         //设置NumLock 指示灯亮(ley_leds的第1位为1)
fifo32_put(&keycmd, KEYCMD_LED);//同上
fifo32_put(&keycmd, key_leds);
}
if (i == + 0x46) {       /* ScrollLock */
key_leds ^= ;         //设置ScrollLock 指示灯亮(ley_leds的第0位为1)
fifo32_put(&keycmd, KEYCMD_LED);//同上
fifo32_put(&keycmd, key_leds);
}
if (i == + 0xfa) {       /* 键盘成功接收数据返回一个ACK(0xfa) */
keycmd_wait = -;         //这是又可以继续向键盘控制器发送数据了
}
if (i == + 0xfe) {       /* 键盘接收数据失败返回ACK(0xfe) */
wait_KBC_sendready();       //等着(0xfa)键盘准备好
io_out8(PORT_KEYDAT, keycmd_wait);//重新把keycmd_wait的值发送到键盘控制器
}
//.......
}
}
}
}

《30天自制操作系统》17_day_学习笔记的更多相关文章

  1. 《30天自制操作系统》学习笔记--Mac下工具的使用

    现在来介绍官网上下的工具怎么用首先是官网地址,书上有个注释上有:hrb.osask.jp 翻译成中文大概是这个样子滴. 上面有两个文件可以下载,一个是工具,一个是工具的源代码,很好的学习资料 下面把工 ...

  2. 《30天自制操作系统》学习笔记--Mac环境搭建

    弄了三天了,终于弄好了,先说结果,就是作者在网站上放了os x的工具(hrb.osask.jp,也有linux下的工具,可以自己去下载),也就是说我白忙活了三天... 再说一下这几天都干啥了,主要是想 ...

  3. 《30天自制操作系统》学习笔记--番外篇之Mac环境下的工具介绍

    这几天又有点不务正业了,书也没看,一直在搞这个破环境,尝试各种做法,网上各种垃圾信息,浪费了很多时间,说的基本都是废话,不过还是找到了一些,赶紧写下来,不然这个过几天又忘了 首先是环境,我用的是Max ...

  4. 《30天自制操作系统》读书笔记(5) GDT&IDT

    梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas    InitialProgramLo ...

  5. 《30天自制操作系统》读书笔记(3) 引入C语言

    这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是 ...

  6. 《30天自制操作系统》读书笔记(2)hello, world

    让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实 ...

  7. 30天自制操作系统第九天学习笔记(u盘软盘双启动版本)

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第九天的课程已学完,确实有点不想写 ...

  8. 从你的u盘启动:30天自制操作系统第四天u盘启动学习笔记

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 developing environment:ubuntu 关于u盘启动自己做的操 ...

  9. 30天自制操作系统第八天学习笔记(u盘软盘双启动版本)

    暑假学习小日本的那本书:30天自制操作系统 qq交流群:122358078    ,更多学习中的问题.资料,群里分享 environment:开发环境:ubuntu 第八天的学习思考: 关于鼠标是怎么 ...

  10. 《30天自制操作系统》笔记(03)——使用Vmware

    <30天自制操作系统>笔记(03)——使用Vmware 进度回顾 在上一篇,实现了用IPL加载OS程序到内存,然后JMP到OS程序这一功能:并且总结出下一步的OS开发结构.但是遇到了真机测 ...

随机推荐

  1. 《深入理解Windows Phone 8.1 UI控件编程》基于最新的Runtime框架

    <深入理解Windows Phone 8.1 UI控件编程>本书基于最新的Windows Phone 8.1 Runtime SDK编写,全面深入地论述了最酷的UI编程技术:实现复杂炫酷的 ...

  2. [深入浅出Windows 10]不同平台设备的适配

    2.3 不同平台设备的适配 Windows 10通用应用程序针对特定的平台还会有一个子API的集合,当我们要使用到某个平台的特定API的时候(比如手机相机硬件按钮触发事件),这时候就需要调用特定平台的 ...

  3. JS中的专业术语

    本身虽然是学技术出身,但.....此处省略N个字符 1.Namespace 命名空间 允许开发人员在一个独特, 应用相关的名字的名称下捆绑所有功能的容器. 2.Class类 定义对象的特征.它是对象的 ...

  4. How to get SQLite work on windows phone 8

    1.Install SQLite for Windows Phone SDKC:\Program Files (x86)\Microsoft SDKs\Windows Phone\v8.0\Exten ...

  5. CAS单点登录系统整合——注册的问题

    最近一段时间在搞CAS单点登录系统,涉及到几个子系统的整合问题.对于注册,这里遇到了一个选择: 在子系统内完成注册,然后把信息同步到CAS系统: 在CAS系统中完成基本信息的注册,比如:用户名.邮箱. ...

  6. JQuery的动画及其幻灯片效果

    1.显示和隐藏hide()和show() 对于动画来说,显示和隐藏是最基本的效果之一,简单介绍jQuery的显示和隐藏. <script type="text/javascript&q ...

  7. [LintCode] Roman to Integer 罗马数字转化成整数

    Given a roman numeral, convert it to an integer. The answer is guaranteed to be within the range fro ...

  8. android-ProgressBar

    制定ProgressBar显示风格 * 参考系统自带的进度条 * ProgressBar分类 * 可以精确显示进度(可以显示刻度和百分比) * 不可以精确显示进度 * 标题上ProgressBar的设 ...

  9. Linux_系统管理命令(工作中经常使用到的)

    查看网络配置信息 ifconfig 查看系统资源信息(类似win系统资源管理器) top (ps: load average 负载 Task 进程   Cpus/Mem  swap 交换分区 类似wi ...

  10. Android中Activity的四种启动模式

    要了解Android的启动模式先要了解一下Activity的管理方式: 1.Activity的管理机制 Android的管理主要是通过Activity栈来进行的.当一个Activity启动时,系统根据 ...