30天自制操作系统——第二十四天增加命令行窗口

蜂鸣发声器

目前制作的操作系统还不能发声呢,由于调用声卡的过程比较复杂,这里我们就先实现蜂鸣器发声好了。蜂鸣器发出的声音,是那种哔哔哔哔的,听起来有些奇怪。这里设置声音的频率随着时间以100Hz的速度降低,当声音频率降到20Hz以下或键盘按下任意键时结束。

beepdown.c

void HariMain(void)
{
     
	int i, timer;
	timer = api_alloctimer();
	api_inittimer(timer, 128);
	for (i = 20000000; i >= 20000; i -= 100000) {
     
		/* 20KHz~20Hz : 人类可以听到的声音范围 */
	
		api_beep(i);
		api_settimer(timer, 1);	
		if (api_getkey(1) != 128) {
     
			break;
		}
	}
	api_beep(0);
	api_end();
}

这里没有调用声卡,所以虚拟机里不用设置声卡也可以听到声音。

make run 一下——

30天自制操作系统——第二十四天增加命令行窗口_第1张图片

将 i 改为递增的,以 i %的速度递增则会听到声音频率升高的声音,发出的声音要比之前感觉起来好多了。

beepup.c

void api_end(void);
int api_getkey(int mode);
int api_alloctimer(void);
void api_inittimer(int timer, int data);
void api_settimer(int timer, int time);
void api_beep(int tone);

void HariMain(void)
{
     
	int i, timer;
	timer = api_alloctimer();
	api_inittimer(timer, 128);
	for (i = 20000; i <= 20000000; i += i / 100) {
     
		api_beep(i);
		api_settimer(timer, 1);		
		if (api_getkey(1) != 128) {
     
			break;
		}
	}
	api_beep(0);
	api_end();
}

make run一下——
30天自制操作系统——第二十四天增加命令行窗口_第2张图片

显示多种颜色

现在来实现可以显示多种颜色的应用程序,将光的三原色RGB(红、绿、蓝)中每个颜色赋予6个色阶,这样可以定义出6×6×6=216种颜色。

graphic.c节选:

void init_palette(void)
{
     
	static unsigned char table_rgb[16 * 3] = {
     
		(略)
	};
	unsigned char table2[216 * 3];
	int r, g, b;
	set_palette(0, 15, table_rgb);
	for (b = 0; b < 6; b++) {
     
		for (g = 0; g < 6; g++) {
     
			for (r = 0; r < 6; r++) {
     
				table2[(r + g * 6 + b * 36) * 3 + 0] = r * 51;
				table2[(r + g * 6 + b * 36) * 3 + 1] = g * 51;
				table2[(r + g * 6 + b * 36) * 3 + 2] = b * 51;
			}
		}
	}
	set_palette(16, 231, table2);
	return;
}

这里RGB颜色与色号的对应关系为:色号 = 16 + r * 1 + g * 6 + b * 36 。

实现显示多种颜色的应用程序color,这里将红色通道关闭,仅显示绿色和蓝色的色阶变化。

color.c节选

void HariMain(void)
{
     
	char *buf;
	int win, x, y, r, g, b;
	api_initmalloc();
	buf = api_malloc(144 * 164);
	win = api_openwin(buf, 144, 164, -1, "color");

	for (y = 0; y < 128; y++) {
     
		for (x = 0; x < 128; x++) {
     
			r = 0;
			g = x * 2;
			b = y * 2;
			buf[(x + 8) + (y + 28) * 144] = 16 + (r / 43) + (g / 43) * 6 + (b / 43) * 36;			
		}
	}
	
	api_refreshwin(win, 8, 28, 136, 156);
	api_getkey(1); 
	api_end();
}

make run——

30天自制操作系统——第二十四天增加命令行窗口_第3张图片

让色彩更加丰富,可以用两种颜色交替排列,看上去就像是这两种颜色混合在一起一样。纯色中间可以产生3个中间色,这样6级色阶能够可以显示出21级色阶。(6 + 5 × 3 = 21)

color2.hrb节选:

unsigned char rgb2pal(int r, int g, int b, int x, int y);

void HariMain(void)
{
     
	char *buf;
	int win, x, y;
	api_initmalloc();
	buf = api_malloc(144 * 164);
	win = api_openwin(buf, 144, 164, -1, "color2");
	for (y = 0; y < 128; y++) {
     
		for (x = 0; x < 128; x++) {
     
			buf[(x + 8) + (y + 28) * 144] = rgb2pal(0, x * 2, y * 2, x, y);
		}
	}
	api_refreshwin(win, 8, 28, 136, 156);
	api_getkey(1); 
	api_end();
}

unsigned char rgb2pal(int r, int g, int b, int x, int y)
{
     
	static int table[4] = {
      3, 1, 0, 2 };
	int i;
	x &= 1; /* 判断是偶数还是奇数 */
	y &= 1;
	i = table[x + y * 2];	/* 用来生成中间色的常量 */
	r = (r * 21) / 256;	/* r为0~20 */
	g = (g * 21) / 256;
	b = (b * 21) / 256;
	r = (r + i) / 4;	/* r为0~5 */
	g = (g + i) / 4;
	b = (b + i) / 4;
	return 16 + r + g * 6 + b * 36;
}

执行make run,有点渐变色效果——

30天自制操作系统——第二十四天增加命令行窗口_第4张图片

增加命令行窗口

我们熟知的操作系统都是可以同时运行多个应用程序的,目前制作的操作系统只有一个命令行窗口,且命令行窗口只能执行一个应用程序。我们想要同时运行两个应用程序,最简单的方法就是同时启动两个命令行窗口。

实现这个功能,需要修改的地方包括:

1.将与命令行窗口相关的变量定义为数组,通过循环进行相同的处理,这样不管增加多少个命令行窗口都很方便。

2.将用于判断向哪个命令行窗口输出字符的变量cons,保存到每个任务各自的TASK结构中,这样就可以由不同的任务读取不同的值了。

bootpack.h节选:

struct TASK {
     
	int sel, flags; /* sel代表GDT编号 */
	int level, priority;
	struct FIFO32 fifo;
	struct TSS32 tss;
	struct CONSOLE *cons;  /* 变量cons保存到这里 */
	int ds_base;
};

3.在启动应用程序时,指定了段号,当启动下一个应用程序时,之前的使用的段会被覆盖。比如color.hrb 指定了1003号 和 1004号,当运行color2.hrb时,这两个段会被覆盖,因此需要为color.hrb和color2.hrb分配编号不同的段。

console.c节选:

(略)
set_segmdesc(gdt + task->sel / 8 + 1000, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
set_segmdesc(gdt + task->sel / 8 + 2000, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);
(略)
start_app(0x1b, task->sel + 1000 * 8, esp, task->sel + 2000 * 8, &(task->tss.esp0));
(略)

4.实现正常关闭窗口,当使用Shift +F1 键盘强制结束 和用鼠标点击 × 按钮会以当前点击的窗口为对象。

bootpack.c节选,添加键盘强制结束代码:

if (i == 256 + 0x3b && key_shift != 0) {
     
	task = key_win->task;
	if (task != 0 && task->tss.ss0 != 0) {
     	/* Shift+F1 */
		cons_putstr0(task->cons, "\nBreak(key) :\n");
		io_cli();	/* 强制结束处理时,禁止任务切换 */
		task->tss.eax = (int) &(task->tss.esp0);
		task->tss.eip = (int) asm_end_app;
		io_sti();
	}
}

bootpack.c节选,添加鼠标点击×按钮结束代码:

if (sht->bxsize - 21 <= x && x < sht->bxsize - 5 && 5 <= y && y < 19) {
     
	/* 点击“×”按钮 */										
    if ((sht->flags & 0x10) != 0) {
     		/* 是否为应用程序窗口 */
		task = sht->task;
		cons_putstr0(task->cons, "\nBreak(mouse) :\n");
		io_cli();	/* 强制结束处理时,禁止任务切换 */
		task->tss.eax = (int) &(task->tss.esp0);
		task->tss.eip = (int) asm_end_app;
		io_sti();
	}
}

task_a窗口不是应用程序,而是操作系统的一部分,放在这里有些碍眼了,把它删除掉。

删除相关代码后,执行一下make run——

30天自制操作系统——第二十四天增加命令行窗口_第5张图片
注:本文参照《30天自制操作系统》制作,感谢各位的持续关注,原著源码链接见第十九篇。

你可能感兴趣的