前言
从着手“自制操作系统” 到现在, 不知不觉间已经过去2周了。有的读者朋友读到这里, 可能已经花了更长的时间;也有的朋友, 经过努力也可能只用了一周左右就读到了这里。 开发个操作系统需要些必备知识, 像编程语言的知识, 相关算法和技巧等。 到现在为止,这些知识的介绍就结束了。 知道了这些, 今后只要灵活运用前几章的学习内容,就 可以开发出不错的操作系统了。到现在为止的学 习过程觉得怎么样?回过头来想一想,如果大家有什么不明白的内容,也许趁着现在就弄清楚比较好。 那么我们赶快把操作系统我们的做得更像一个真正的操作系统吧。 大家现在可以接触多任务了。可是按计划,我们是从第15天开始才学# 可多任务的,所以今天还是暂时不学习多任务吧。 所以今天打算学点儿别的。 学点儿什么呢?好吧, 就学提高画面分辨率吧。 嗯, 从开发操作系统的角度来看, 现在这样的320x200的画面也没什么问题, 可毕竟还是大画面好。 以前, 我们特意创建了struct BOOTINFO, 就是为了能在以后扩大画面。那时我们没有写成“320” 而是特意写成了 “binfo ->xsize" , 这种很麻烦的方式。 这种麻烦辛苦, 现在终于得到了回报。
一、提高分辨率
高分辨率的利用方法因显卡不同而不同。首先,为了能通过“make run” 运行,我们只考虑支持QEMU模拟器的显卡。这个卡顺利运行以后,再去支持其他的显卡。 由于画面切换中我们要使用BIOS,所以就需要改写asmhead.nas的“画面模式设定”部分了。 好久没写汇编程序了哦。
; 设定画面模式
MOV BX,0x4101 ; VBE的640x480x8bi彩色
MOV AX,0x4f02
INT 0x10
MOV BYTE [VMODE],8 ; 记下画面模式
MOV WORD [SCRNX],640
MOV WORD [SCRNY],480
MOV DWORD [VRAM],0xe0000000
程序的构成几乎没什么变化。但是数值是0x4101或0x4f02,有点儿怪怪的。这些数字是怎样查出来的?估计会有人问这个问题,可与其追问这样的问题,不如先看看画面扩大后的“操作系统”。所以, 我们先运行 make run。
因为大家已经习惯于以前的大文字显示,所以640x480看起来非常宽阔。高分辨率画面,嗯, 感觉就是不一样,好像变成了另外一个操作系统。
下面来说明为什么这个程序能够在640x480画面上运行。 其实说起来也很简单。给AX赋值0x4f02, 给BX赋值画面模式号码,这样就可以切换到高分辨率画面模式了。为什么呢?原本就是这样的。这次我们只是正好使用到了这个功能。以前画面是320x200的时候,我们用的是“AH=0;AL=画面模式号码;”。现在切换 到新画面时就使用“AX=0x4f02;"。 大家是不是很纳闷:“画面模式有新旧之分吗 ”是的,实际上是有新旧之分的。就说显卡吧,每当有新显卡面世时,性能就会提高,反过来想,以前的显卡,声音性能差,颜色数又少, 分辨率又低。刚开始的时候,电脑规格是以IBM公司为中心决定的,他们也规定了画面模式的相关规格。而且各家显卡公司也都迎合IBM的规格来制作显卡。 可是过了一段时间,其他显卡公司的图像处理技术超越了IBM,在IBM制定规格前,就出现 了具有各样画面模式的显卡。这造成了多家显卡 公司的竞争,使得在各家公司之间,画面模式的 设定方法和使用方法都各不相同。 这样的情况,我们这些普通程序员是难以应付的。显卡的种类太多,我们记不住那么多的设 定方法,而且事实上,连参考资料都很难得到。这样一来,本应是高性能的显卡,却只能像老显卡一样,通过BIOS设定为320x200来使用。 有鉴于此,多家显卡公司经过协商,成立了VESA协会(Video Electronics Standards Association 视频电子标准协会)。此后,这个协会制定了虽然不能说完全兼容、但几乎可以通用的设定方法,制作了专用的BIOS。这个追加的 BIOS被称作“VESA BIOS extension”( VESA-BIOS 扩展,简略为VBE)。利用它,就可以使用显卡的高分辨率功能了。 因此,切换到不使用VBE的画面模式时用“AH=0;AL=画面模式号码;而切换到使用VBE 的画面模式时用“AX=0x4f02;BX=画面模式号码;”。而这种必须使用VBE才能利用的画面模式就称作 “新”画面模式。
VBE的画面模式号码如下。
0x101······640x480x 8bit 彩色
0x103······800x 600x 8bit 彩色
0x105······1024x768x 8bit彩色
0x107······1280x1024x 8bit彩色
还有其他一些画面模式,因为现在不需要,我们就省略了。另外,在QEMU中不能指定最下面的0x107。实际指定的时候,要像在asmhead.nas中所做的那样,将以上的画面模式号码值加上0x4000,再赋值到BX中去。不这样做就不能顺利运行。所以,如果想要将画面扩展得特别大的话,请尝试运行以下程序。
MOV BX,0x4105 ;VBE的1024x768x8bit彩色
MOV AX,0x4f02
INT 0x10
MOV BYTE[VMODE],8 ;记下画面模式(参考C语言)
MOV WORD[SCRNX],1024
MOV WORD [SCRNY],768
MOV DWORD[VRAM],0xe0000000
画面会变得很宽哦。
二.键盘输入
对高分辨率的支持我们已经完成了,比预想的要快。所以现在我们通过从键盘输入信息来稍稍放松一下吧。 再对hariblle运行一次“make run" 然后试着按下“A”键。按下“A”键的时候,应该显 示“1E”,键弹起的时候应该显示 9E”。
下面我们按下“B”键吧。按下的时候显示“30”,弹起的时候显示“B0”。再按下“C” 这样罗列下去,可就浪费纸张了,所以我们把这些值都归纳到下表里。表里的值是按下键时的数值。在此基础上加上0x80就可以得到键弹起时的数值。
这个表中有写着“保留?” 的地方是指, 虽然现在按下哪个键都不出现该数值,但将来键盘 的键数量一旦增加,那个数值就可以被分配使用了。
我们想利用这个表实现当“A”键被按下的时候就显示“A”。嗯,以前曾经通过计数来测试性能,现在也已经腻了,所以我们来修改bootpack.c。 不再是让其计数,而是让其充分HLT(休眠),以便节电。
for (;;) {
io_cli();
if (fifo32_status(&fifo) == 0) {
io_stihlt();
} else {
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) { /* 键盘数据 */
sprintf(s, "%02X", i - 256);
putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
if (i == 0x1e + 256) {
putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, "A", 1);
}
} else if (512 <= i && i <= 767) { /* 鼠标数据 */
}
} else if (i == 10) { /* 10秒定时器} */
putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
} else if (i == 3) { /* 3秒定时器} */
putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
} else if (i == 1) { /* 光标用定时器} */
timer_init(timer3, &fifo, 0);
boxfill8(buf_back, binfo->scrnx, COL8_FFFFFF, 8, 96, 15, 111);
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
} else if (i == 0) { /* 光标用定时器 */
timer_init(timer3, &fifo, 1);
boxfill8(buf_back, binfo->scrnx, COL8_008484, 8, 96, 15, 111);
timer_settime(timer3, 50);
sheet_refresh(sht_back, 8, 96, 16, 112);
}
}
}
}
虽然在这段程序中没有出现,但我们已经把窗口的名字由 改为了 counter window 再按一下“A”键,哦, 少 make run “A”显示出来了。
到了这一步,我们希望也能输入“B”和“C”等字符。那么,我们来动手写程序。 首先我们创建以下程序:
if(256<=i && i<= 511){
/*键盘数据 */
sprintf(s,"02X",i-256);
putfonts8_asc_sht(sht_back,0,16,COL8_FFFFFF,COL8_008484,s,2);
if(i == 0x1e +256){
putfonts8_asc_sht(sht_win,40,28,COL8_000000,COL8_C6C6C6,"A",1);
}
if(i==0x30+256){
putfonts8_asc_sht(sht_win,40,28,COL8_000000,COL8_C6C6C6,"B",1);
}
if(i ==0x2e+256){
putfonts8_asc_sht(sht_win,40,28,COL8_000000,COL8_C6C6C6,"C",1);
}
if(i==0x20+256){
putfonts8_asc_sht(sht_win,40,28,COL8_000000,COL8_C6C6C6, "D",1);
}
if(i == 0x12+256){
putfonts8_asc_sht(sht_win,40,28,COL8_000000,COL8 C6C6C6,"E",1);
}else if(512<=i&& i<= 767){/* 鼠标数据 */
如果我们像上面这样写程序,仅仅是字母(26 个)和数字(10个),就得写36个i语句。必须要想出一个好办法。
大家是不是已经想到了喽:
static char keytable[0x54] = {
0, 0, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '^', 0, 0,
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '@', '[', 0, 0, 'A', 'S',
'D', 'F', 'G', 'H', 'J', 'K', 'L', ';', ':', 0, 0, ']', 'Z', 'X', 'C', 'V',
'B', 'N', 'M', ',', '.', '/', 0, '*', 0, ' ', 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, '7', '8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.'
};
if (256 <= i && i <= 511) { /* 键盘数据 */
sprintf(s, "%02X", i - 256);
putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
if (i < 256 + 0x54) {
if (keytable[i - 256] != 0) {
s[0] = keytable[i - 256];
s[1] = 0;
putfonts8_asc_sht(sht_win, 40, 28, COL8_000000, COL8_C6C6C6, s, 1);
}
}
}
之所以把keytable[]设定为static char,是因为希望程序被编译为汇编语言的时候,static char能编译成DB指令。设定调色板的时候也是如此,大家还记得吗? 现在我们来解释这个程序的运行机制。比如说keytable[0x1e]对应的是“A"。而“A”的字符 代码是0x41。如果i == 0x1e+256的话,keytable[i-256]就是“A”,所以s[0]也就是“A”了。
同理,“B”、 “C”、 “Z”以及“5”等也应该可以显示了。
让我们确认一下吧。运行 'make run”。 不错,很顺利呀。
运行“make run” 后,按下“@”等键,却显示出“W”。大家也许会想“唉?怎么回事儿?” 这是因为当“@”被按下的时候,HariMain接收到了0x11(请确认左上方的显示内容),这不是 HariMain的bug,而是QEMU的问题。还有其他几个键,按下以后会显示出奇怪的字符。这些好像都是QEMU的问题。
三.追加内容
我们已经进行到了这个阶段, 就想稍稍放松一下了。你看,光标也能闪烁了, 窗口也有了。 我们先把程序放在一边,来看一看画面的截图吧。
当这个画面出现的时候, 感到一股成功的喜悦。 虽然看起来有了不小的进步,但实际上我们也只是在窗口中添加了一些画,改变了鼠标和字符的显示位置以及颜色而已。如果我们按下退格键(BackSpace键),还可以改写已输入的字符哦。 大家试着输入自己喜欢的信息吧。
int mx, my, i, cursor_x, cursor_c;
make_textbox8(sht_win, 8, 28, 144, 16, COL8_FFFFFF);
cursor_x = 8;
cursor_c = COL8_FFFFFF;
for (;;) {
io_cli();
if (fifo32_status(&fifo) == 0) {
io_stihlt();
} else {
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) { /* 键盘数据 */
sprintf(s, "%02X", i - 256);
putfonts8_asc_sht(sht_back, 0, 16, COL8_FFFFFF, COL8_008484, s, 2);
if (i < 0x54 + 256) {
if (keytable[i - 256] != 0 && cursor_x < 144) { /* 一般字符 */
/* 显示1个字符就前移1次光标 */
s[0] = keytable[i - 256];
s[1] = 0;
putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, s, 1);
cursor_x += 8;
}
}
if (i == 256 + 0x0e && cursor_x > 8) { /* 退格键 */
/* 用空格键把光标消去后,后移1次光标 */
putfonts8_asc_sht(sht_win, cursor_x, 28, COL8_000000, COL8_FFFFFF, " ", 1);
cursor_x -= 8;
}
/* 光标再显示 */
boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
} else if (512 <= i && i <= 767) { /* 鼠标数据 */
...
} else if (i == 10) { /* 10秒定时器} */
putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
} else if (i == 3) { /* 3秒定时器} */
putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
} else if (i <= 1) { /* 光标} */
if (i != 0) {
timer_init(timer3, &fifo, 0);
cursor_c = COL8_000000;
} else {
timer_init(timer3, &fifo, 1);
cursor_c = COL8_FFFFFF;
}
timer_settime(timer3, 50);
boxfill8(sht_win->buf, sht_win->bxsize, cursor_c, cursor_x, 28, cursor_x + 7, 43);
sheet_refresh(sht_win, cursor_x, 28, cursor_x + 8, 44);
}
}
}
}
cursor_x是用来记住光标显示位置的变量 ,输入一个字符后,这个变量就递增“8”。cursor_c 变量则表示现在光标的颜色。 它每0.5秒变化一次。 make_textb0x8的数是用来描绘文字输入背景的,内容如下。 这个函数没什么特别难的东西, 大家大致读一下就可以了。
void make_textbox8(struct SHEET *sht, int x0, int y0, int sx, int sy, int c)
{
int x1 = x0 + sx, y1 = y0 + sy;
boxfill8(sht->buf, sht->bxsize, COL8_848484, x0 - 2, y0 - 3, x1 + 1, y0 - 3);
boxfill8(sht->buf, sht->bxsize, COL8_848484, x0 - 3, y0 - 3, x0 - 3, y1 + 1);
boxfill8(sht->buf, sht->bxsize, COL8_FFFFFF, x0 - 3, y1 + 2, x1 + 1, y1 + 2);
boxfill8(sht->buf, sht->bxsize, COL8_FFFFFF, x1 + 2, y0 - 3, x1 + 2, y1 + 2);
boxfill8(sht->buf, sht->bxsize, COL8_000000, x0 - 1, y0 - 2, x1 + 0, y0 - 2);
boxfill8(sht->buf, sht->bxsize, COL8_000000, x0 - 2, y0 - 2, x0 - 2, y1 + 0);
boxfill8(sht->buf, sht->bxsize, COL8_C6C6C6, x0 - 2, y1 + 1, x1 + 0, y1 + 1);
boxfill8(sht->buf, sht->bxsize, COL8_C6C6C6, x1 + 1, y0 - 2, x1 + 1, y1 + 1);
boxfill8(sht->buf, sht->bxsize, c, x0 - 1, y0 - 1, x1 + 0, y1 + 0);
return;
}
像haribllh那样玩儿是最高兴的了。这才是操作系统的乐趣所在!那么,我们继续玩点儿别的吧。 大家还记得,为了使鼠标动起来,我们付出了多少辛苦吗?我们付出了辛苦,终于让鼠标动 了起来,可鼠标动起来后,却一点儿都没用到。它只是一个装饰物(也许还碍手碍脚的?)。 好不容易让鼠标动起来了,我们看看能用它干些什么吧。做什么好呢?嗯,还是来移动窗口 吧。所以下面我们就使用鼠标完成窗口移动吧。 只要添写4行程序就可以了。
一说到窗口的移动,感觉好像很难。但实际并不难,4行代码就搞定啦!
for (;;) {
io_cli();
if (fifo32_status(&fifo) == 0) {
io_stihlt();
} else {
i = fifo32_get(&fifo);
io_sti();
if (256 <= i && i <= 511) { /* 键盘数据 */
} else if (512 <= i && i <= 767) { /* 鼠标数据 */
if (mouse_decode(&mdec, i - 512) != 0) {
/* 从这里开始! */
sheet_slide(sht_mouse, mx, my);
if ((mdec.btn & 0x01) != 0) {
/* 按下左键、移动sht_win */
sheet_slide(sht_win, mx - 80, my - 8);
}
/* 到这里结束! */
}
} else if (i == 10) { /* 10秒定时器} */
putfonts8_asc_sht(sht_back, 0, 64, COL8_FFFFFF, COL8_008484, "10[sec]", 7);
} else if (i == 3) { /* 3秒定时器 */
putfonts8_asc_sht(sht_back, 0, 80, COL8_FFFFFF, COL8_008484, "3[sec]", 6);
} else if (i <= 1) {
}
}
}
这样就完成了。 我们赶紧运行启动之后, 请随意在画面上某个地方。 make run – 点击一下, 窗口很快就移动到那里了。
即使窗口跑到了画面外,也没有问题。因为我们已经针对鼠标指针提前采取了对策,这就如同图层跑到了画面外面也可以动起来一样。
总结
真棒!能简单地实现这个功能,多亏在制作图层时下的一番苦功。做得不错!先表扬一下几天前的自己吧(笑) 唉,玩得正高兴,不知不觉就到了今天的结束时间了。明天我们就要挑战,“多任务”了。 今天正好是第2周, 我们看看haribote.sys的大小吧。23908字节也就是23KB。到现在为止,仅用 23KB就可以完成像操作系统那样的功能了,不错不错。今天的内容不算多,大家可以复习一下之前的内容哦。好了,我们明天见吧。
标签:asc,sht,14,COL8,--,30,cursor,画面,256 From: https://blog.csdn.net/suy123/article/details/145166461