30天自制操作系统(第25天)

本文介绍了如何通过编程控制蜂鸣器发声,包括音高设置和ON/OFF操作,同时提及了增加颜色选项以及管理多个命令行窗口的技术细节,如调整初始位置和按键事件处理。
摘要由CSDN通过智能技术生成

25.1  蜂鸣器发声

需要实现的功能:设置蜂鸣器,并使其发出声音。
蜂鸣器发声的控制
        音高操作
        AL = 0xb6; OUT(0x43, AL);
        AL = 设定值的低位 8bit; OUT(0x42, AL);
        AL = 设定值的高位 8bit; OUT(0x42, AL);
        设定值为0 时当作 65536 来处理。
        发声的音高为时钟除以设定值,也就是说设定值为1000时相当于发出1.19318KHz的声音; 设定值为10000时相当于 119.318Hz 。因此设定2712即可发出约 440Hz 的声音 1
蜂鸣器ON/OFF
        使用I/O 端口 0x61 控制。
        ON: IN(AL, 0x61); AL |= 0x03; AL &= 0x0f; OUT(0x61, AL);
        OFF: IN(AL, 0x61); AL &= 0xd; OUT(0x61, AL);
蜂鸣器发声
        EDX=20   DAX= 声音频率(单位是 mHz,即毫赫兹)例如当 EAX=4400000 时,则发出 440Hz的声音,频率设为 0 则表示停止发声
/*                a_nask.nas                */
_api_beep:					;void api_beep(int tone);
	MOV		EDX,20
	MOV		EAX,[ESP+4]
	INT		0X40
	RET

/*       在console.c文件中函数hrb_api添加判断         */
if(edx == 20){//蜂鸣器
	if(eax == 0){//停止发声 蜂鸣器OFF
		i = io_in8(0x61);
		io_out8(0x61, i&0x0d);
	}else{
		//设置声音频率
		i = 1193180000/eax;
		io_out8(0x43, 0xb6);
		io_out8(0x42, i&0xff);
		io_out8(0x42, i >> 8);
		//蜂鸣器ON
		i = io_in8(0x61);
		io_out8(0x61, (i|0x03)&0x0f);
	}
}

25.2  增加更多的颜色

需要实现的功能:由于目前系统中只有16种颜色,而根据之前设定可有256种颜色,需要增加颜色。思路:可以为光的三原色reg、green、blue(红、绿、蓝)中每种颜色赋予6个色阶2,这样一来,就可以定义出6×6×6=216种颜色。
/*                graphic.c                */
void init_palette(void){/*RGB(红绿蓝)方式,用6位十六进制数,也就是18位指定颜色*/
	(中略)
	set_palette(0,15,table_rgb);
	unsigned char table2[216*3];/*从这里开始*/
	int r, g, b;
	for(b = 0; b < 6; b++){
		for(g = 0; g < 6; g++){
			for(r = 0; r < 6; r++){
				table2[(b*36+g*6+r)*3+0] = r*51;
				table2[(b*36+g*6+r)*3+1] = g*51;
				table2[(b*36+g*6+r)*3+2] = b*51;
			}
		}
	}
	set_palette(16,231,table2);/*到这里结束*/
	return;
}

25.3  窗口初始位置

需要实现的功能:让窗口总是显示在画面的中央,且要判断当前画面中窗口的数量并自动显示在最上面思路:应用程序创建窗口时,调整其位置。
int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax){
	(中略) 
	else if(edx == 5){//创建窗口
		(中略) 
		sheet_slide(sht, (shtctl->xsize-esi)/2, (shtctl->ysize-edi)/2);
		sheet_updown(sht, shtctl->top); /*将窗口图层高度指定为当前鼠标所在图层的高度,鼠标移到上层*/
		(中略) 
	}else if(edx == 6){
		(中略) 
}

25.4  增加命令行窗口(1

需要实现的功能:显示两个命令console窗口思路:原代码已经实现显示1个命令行窗口,那么能否将与命令窗口的元素格式变成数组呢?进而实现显示两个窗口,并可以输入命令。(注:函数HariMain中的 task_cons改成task_cons[0],就不在此进行展示了
void HariMain(void){
	(中略)
	unsigned char *buf_back, buf_mouse[256], *buf_win, *buf_cons[2];
	struct SHEET *sht_back, *sht_mouse, *sht_win, *sht_cons[2];
	struct TASK *task_a, *task_cons[2];
	(中略)
	/* sht_cons */
	for(i = 0; i < 2; i++){
		sht_cons[i] = sheet_alloc(shtctl);/* 分配图层 */
		buf_cons[i] = (unsigned char*)memman_alloc_4k(memman, 256 * 165);/* 分配内存 */
		sheet_setbuf(sht_cons[i], buf_cons[i], 256, 165, -1);/* 设置图层缓冲区 */
		make_window8(buf_cons[i], 256, 165, "console", 0);/* 绘制窗口 */
		make_textbox8(sht_cons[i], 8, 28, 240, 128, COL8_000000);
		task_cons[i] = task_alloc();
		task_cons[i]->tss.esp = memman_alloc_4k(memman, 64 * 1024) + 64 * 1024 - 12;
		task_cons[i]->tss.eip = (int) &console_task;
		task_cons[i]->tss.es = 1 * 8;
		task_cons[i]->tss.cs = 2 * 8;
		task_cons[i]->tss.ss = 1 * 8;
		task_cons[i]->tss.ds = 1 * 8;
		task_cons[i]->tss.fs = 1 * 8;
		task_cons[i]->tss.gs = 1 * 8;
		*((int *) (task_cons[i]->tss.esp + 4)) = (int) sht_cons[i];
		*((int *) (task_cons[i]->tss.esp + 8)) = memtotal; /*这里! */
		task_run(task_cons[i], 2, 2);/* level=2, priority=2 */
		sht_cons[i]->task = task_cons[i];
		sht_cons[i]->flags |= 0x20;/*光标标识位*/
	}
	
	/* sht_win */
	(中略)
	
	/* 调整背景、鼠标和窗口的显示位置 */
	sheet_slide(sht_back,     0,  0);
	sheet_slide(sht_cons[1], 56,  6);
	sheet_slide(sht_cons[0],  8,  2);
	sheet_slide(sht_win,     64, 56);
	sheet_slide(sht_mouse,   mx, my);
	
	/* 调整背景、鼠标和窗口的图层高度 */
	sheet_updown(sht_back,	   0);
	sheet_updown(sht_cons[1],  1);
	sheet_updown(sht_cons[0],  2);
	sheet_updown(sht_win,      3);
	sheet_updown(sht_mouse,    4);
    (中略)
}

25.5  增加命令行窗口(2

需要实现的功能:函数hrb_api()中有struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec); 所以不管在哪个窗口中运行a.hrb,都只能在固定的其中一个窗口中显示字符,所以需要保存每个命令窗口的地址。思路:在TASK结构体中添加命令结构体和基地址,将console.c文件中task中的cons和ds_base进行赋值。
void console_task(struct SHEET *sheet, int memtotal){
    (中略)
    task->cons = &cons; /*修改前:*((int *) 0x0fec) = (int) &cons;*/
    (中略)
}

int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline){
    (中略)
    if (finfo != 0) {
        (中略)
        if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
            (中略)
            task->ds_base = (int) q; /*修改前:*((int *) 0x0fe8) = (int) &q;*/
        } else {
            (中略)
        }
        (中略)
    }
    (中略)
}

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax){
    (中略)
    int ds_base = task->ds_base; /*这里!*/
    struct CONSOLE *cons = task->cons; /*这里!*/
    (中略)
}

int *inthandler0c(int *esp){
    (中略)
    struct CONSOLE *cons = task->cons; /*这里!*/
    (中略)
}

int *inthandler0d(int *esp){
    (中略)
    struct CONSOLE *cons = task->cons; /*这里!*/
    (中略)
}

25.6  增加命令行窗口(3

需要实现的功能:当点击“×”按钮时,关闭对应的窗口思路:检查函数 cmd_app程序,发现:
        set_segmdesc(gdt + 1003, finfo->size - 1, (int) p, AR_CODE32_ER + 0x60);
        set_segmdesc(gdt + 1004, segsiz - 1, (int) q, AR_DATA32_RW + 0x60);
        (中略)
        start_app(0x1b, 1003 * 8, esp, 1004 * 8, &(task->tss.esp0));
当创建2个命令窗口时,第一个窗口数据会被第二个窗口数据覆盖,导致窗口无法正常关闭。
task—>sel 中填入 TSS 的段号 * 8 (请参照 mtask.c task_init ),将这个值除以 8,结果一定落在 3 1002 。将其加上 1000 ,就得到 1003 2002的值,我们把它用作应用程序用的代码段编号;将其加上 2000 ,即得到 2003 3002的值,我们把它用作应用程序用的数据段编号。这样一来,就不会发生段被覆盖的问题了。
int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
    (中略)
    if (finfo != 0) {
    (中略)
        if (finfo->size >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
            (中略)
            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));
            (中略)
        } else {
            (中略)
        }
        (中略)
    }
    (中略)
}

25.7  增加命令行窗口(4

需要实现的功能:当输入“shift+F11”组合键和“×”按钮时,关闭对应的窗口思路:由于25.5节中已经在TASK结构体中增加了元素,所以需要调整bootpack.c文件中的HariMain函数,将其中的task_cons[0]变成task。
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();
}/*到此结束*/ 

if (sht->buf[y * sht->bxsize + x] != sht->col_inv) {
    (中略)
    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();
        }
    }
}
  • 17
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值