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