三十天自制作業系統(13)
第25天
這本書的這一章一開始就講如果控制主機板上的蜂鳴發專聲器發聲,看到這個我很興奮。因為到目前為止我還沒有用windows api或者自己寫程式讓電腦發出聲音,終於可以嘗試一下了,雖然不是音效卡發聲,只是最低階的蜂鳴發聲器,但是還是很興奮。
我們控制主機板上的發聲器也和控制中斷處理器是一樣的,也是要使用in和out指令進行操作。
蜂鳴發聲器的開啟和關閉:
- 使用埠0x61控制
- 開啟:IN(AL, 0X61); AL |= 0X03; AL &= 0X0F; OUT(0X61, AL);
- 關閉:IN(AL, 0X61); AL &= 0X0D; AL &= 0X0D; OUT(0X61, AL);
如果開啟之後控制發聲器的發聲頻率:
- AL = 0XB6; OUT(0X43, AL);
- AL = 設定值的低8位; OUT(0X42, AL);
- AL = 設定值的高8位; OUT(0X42, AL);
- 當設定值為0時當作65536來處理
- 發聲的頻率為時鐘除以設定值,也就是說設定值為1000時相當於發也1.19318KHZ的聲音
api設計
- edx = 20
- eax = 聲音訊率(單位是mHz, 即毫HZ),當頻率為0時表示停止發聲
else if (edx == 20) {
if (eax == 0) {
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);
i = io_in8(0x61);
io_out8(0x61, (i | 0x03) & 0x0f);
}
}
_api_beep: ; void api_beep(int tone);
MOV EDX,20
MOV EAX,[ESP+4] ; tone
INT 0x40
RET
看一下應用程式如何寫:
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 = 20000000; i >= 20000; i -= i / 100) {
/* 20KHz�~20Hz :人類可以聽到的聲音範圍 */
/* i以1%的速度遞減 */
api_beep(i);
api_settimer(timer, 1); /* 0.01秒 */
if (api_getkey(1) != 128) {
break;
}
}
api_beep(0);
api_end();
}
關於聲音就到這裡,接下來看看如何增加更多的顏色。
到目前為止我們只用了16程模式。我們使用的是顯示卡的調色盤模式,理論上有256種顏色,我們就想辦法讓作業系統支援更多的顏色。
剩下的240個顏色我們設定麼顏色呢?調色盤對應的是RGB,一共24位,我們給每個三原色中的一種分6個,那麼一共就可以定義6 * 6 * 6 = 216種顏色
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);
RGB取值分別為0, 51, 102, 153, 204, 255。這樣調色盤設定好了之後如果要取(51, 102, 153)這個顏色的話就是137號。
將下來我們寫一個應用程式測試一下。
int api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_initmalloc(void);
char *api_malloc(int size);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);
int api_getkey(int mode);
void api_end(void);
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 = x * 2;
g = y * 2;
b = 0;
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();
}
我們現在只能在命令列視窗執行應用程式,但是命令列視窗只有一個,所以我們同時只能執行一個應用程式,那麼多工對於應用程式來說沒有任何意義。我們應用讓作業系統可以同時執行多個命令列以支行多個應用程式。
如果想要支援多個命令列視窗,那麼就需要改造作業系統的程式結構。我們之前把每個命令列視窗的資料段和視窗資料存到作業系統的記憶體中。
int ds_base = *((int *) 0xfe8);
struct CONSOLE *cons = (struct CONSOLE *) *((int *) 0x0fec);
如果有多個命令列視窗的話這樣的方式就不能用了,反正要為每個命令列視窗建立一個任務,而每個任務有會維護一個TASK結構的資料,我們可以改造這個結構將這兩個值儲存到這個變數中。
struct TASK {
int sel, flags; /* sel‚ÍGDT‚Ì”Ô�†‚Ì‚±‚Æ */
int level, priority;
struct FIFO32 fifo;
struct TSS32 tss;
struct CONSOLE *cons;
int ds_base;
};
之前我們在執行應用程式的時候,直接給應用程式分配段號,現在由於存在多個命令列視窗,可能會執行多個應用程式就不能用這樣的方式分配段號了。
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);
接下來處理關閉應用程式時候的問題了。
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->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();
}
}
以上是點選關閉按鈕時關閉應用程式。這樣處理完之後就可以支援多個應用程式視窗了,為了寫程式方便,這本書就使用了作業系統執行時開啟2個命令列視窗,估計接下來還會通過輸入命令執行命令列視窗的功能。
這個作業系統還有一個和其他作業系統不一樣的地方。一開機時候自動行開啟一個視窗也就是task_a視窗。為了使作業系統更像個作業系統應該把這個視窗給取消了。
如果要取消這個視窗其實只要刪除跟執行這個視窗有關的程式碼就行了,可以刪好多東西,刪除之後看看結果怎麼樣結果是作業系統出錯了。原來的作業系統執行流程是這樣的:首先執行主程式,開啟task_a視窗,然後把作業系統的焦點放在這個視窗,游標也在task_a視窗閃爍,直到使用者按下了tab或者用滑鼠切換視窗之後才切換。但是現在不是了,現在我們想要讓游標直接在第一個開啟的命令列視窗閃爍就出問題了。因了task_a的優先順序要高,當task_a執行完之前就向命令列視窗傳送游標閃爍的訊息時候,命令列視窗連訊息佇列都還沒有建立,所以出錯了。解決方法也很簡單,只要把命令列視窗建立訊息佇列的程式語句放在傳送訊息之前就可以了。
相關文章
- 自制作業系統(一) 第一個作業系統作業系統
- 30天自制作業系統 For Linux作業系統Linux
- 30天自制作業系統:第三天作業系統
- 《30天自制作業系統》譯者序(偽)作業系統
- 30天自制作業系統(一)啟動區作業系統
- 讀懂《30天自制作業系統》的捷徑作業系統
- 30天自制作業系統——第3天實驗總結作業系統
- 30天自制作業系統——第4天實驗總結作業系統
- 透過GRUB Multiboot2引導自制作業系統boot作業系統
- 由《30天自制作業系統》引發的漫畫創作作業系統
- 釋出在《30天自制作業系統》之前的幫助閱讀貼作業系統
- 30天自制作業系統-merk11的第三天作業系統
- [自制作業系統] 第07回 認識保護模式之地址對映作業系統模式
- 30天自制作業系統-merk11的第一天作業系統
- 利用wsl2的Hyper-V虛擬機器跑自制作業系統虛擬機作業系統
- 為什麼《30天自制作業系統》封面中的貓是兩隻尾巴作業系統
- 13款最好的開源Linux作業系統Linux作業系統
- [自制作業系統] 第10回 認識保護模式之深入淺出特權級作業系統模式
- FlutterUI 呼叫系統渲染引擎-13FlutterUI
- svn + 釘釘機器人制作簡單的程式碼跟蹤系統機器人
- Cobbler實現自動化安裝作業系統作業系統
- 作業系統(1)——作業系統概述作業系統
- 作業系統(一):作業系統概述作業系統
- 0x06_自制作業系統My-OS,IDT,GDT,PIC初始化,實現鍵盤中斷作業系統
- 【工業網際網路】自適應的工業生態系統
- 企業商家制作小程式意義在哪裡?
- 自帶Linux作業系統的的智慧滑鼠:EGO!Linux作業系統Go
- Windows XP 作業系統也玩自動登入(轉)Windows作業系統
- MySQL全面瓦解13:系統函式相關MySql函式
- 質量.軟體.管理--系統思維(13)
- C#學習 [型別系統] 類(13)C#型別
- 作業系統(二):作業系統結構作業系統
- 【作業系統】作業系統綜述(一)作業系統
- 期末了,用Python寫個自動批改作業系統Python作業系統
- 作業人員護目鏡佩戴自動識別系統
- 作業系統1—作業系統概論(上)作業系統
- 作業系統2—作業系統概論(下)作業系統
- 比特幣制作比特幣