30天自制作業系統——第4天實驗總結
實驗日期 | 實驗專案 |
---|---|
2020.10.22 | 第4天 C語言和畫面顯示的練習 |
文章目錄
一、實驗主要內容
1、 內容1 用C語言實現記憶體寫入
(1).內容概要
- 實驗內容:用C語言實現記憶體寫入,即向指定地址中寫入指定的資料。
- 實驗重點:理解寫入的原理和函式引數傳入記憶體地址中的位置。
使用匯編語言編寫一個write_mem8函式來實現對指定地址中資料的寫入,wirte_mem8函式的引數1是地址,引數2是寫入該地址的資料值。呼叫函式時,會在記憶體中開闢函式棧空間,函式引數從棧頂指標esp+4的位置開始儲存,有如下對應關係:
第一個引數的存放地址 | ESP+4 |
第二個引數的存放地址 | ESP+8 |
第三個引數的存放地址 | ESP+12 |
另外,指定記憶體地址的地方,可以自由使用暫存器EAX,ECX,EDX,其他暫存器因為在C語言編譯生成的機器語言中用於記憶非常重要的值,因此只能使用其值,不能改變其值。
(2).關鍵程式碼分析
naskfunc.nas程式碼分析
[FORMAT "WCOFF"]
[INSTRSET "i486p"] ;告訴nask,程式是給486使用的
[BITS 32]
[FILE "naskfunc.nas"]
GLOBAL _io_hlt ,_write_mem8;
[SECTION .text]
_write_mem8: ;void write_mem8(int addr,int data)
MOV ECX,[ESP+4] ;[ESP+4]存放的是地址
MOV AL ,[ESP+8] ;[ESP+8]存放的是資料
MOV [ECX],AL
RET
wirte_mem8函式是使用匯編語言寫的,將地址和寫入資料存放到對應的暫存器中,再利用相應的間接定址的方式把資料寫入指定地址中去。之和的實驗中會使用C語言直接對記憶體寫入,現在先暫時用匯編實現。
bootpack.c程式碼分析
void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{
int i;
for(i=0xa0000;i<=0xaffff;i++)//0xa0000-0xaffff這64k大小的記憶體中用來設定顏色
{
write_mem8(i,15);//將第2個引數的值寫入第1個參數列示的地址中
}
for(;;)
{
io_hlt();
}
}
這部分程式碼在for迴圈中呼叫write_mem8函式將64k大小的圖形緩衝區中每個畫素換成15,即白色所對應的畫素數值。替換完成後,執行程式碼,螢幕就會變成白色。
2、 內容2 條紋圖案
(1).內容概要
- 實驗內容:繪製條紋圖案
為了改變顯示在螢幕上的圖案,繪製出不同的圖案效果。可以使用C語言中的邏輯運算子去改變顏色的色號。圖形資料處理方式總結如下:
和1相或(OR) | 使特定位變1 |
和0相與(AND) | 使特定位變0 |
和1相異或(XOR) | 使特定位反轉 |
(2).關鍵程式碼分析
Bootpack.c程式碼分析
void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{
int i;
for(i=0xa0000;i<=0xaffff;i++)
{
write_mem8(i,i & 0x0f);//i和0x0f相與,得到16種不同的色號
}
for(;;)
{
io_hlt();
}
}
這部分程式碼主要是用來繪製條紋圖案。將i和0x0f相與,將會是i的值從0—f開始迴圈,對應在畫面上的效果就是0-f對應 16種顏色,按照列依次排列形成條紋圖案。
程式碼實現的效果如下:
3、 內容3 挑戰指標和指標的應用
(1).內容概要
- 實驗內容: 瞭解指標的概念以及用法;學會使用指標將資料寫入記憶體,並能夠完成相應圖形的繪製。
- 實驗重點:掌握幾種使用指標將資料寫入記憶體的方法,並能夠熟練地使用指標。
在組合語言中,可以省略BYTE等的情況:源和目的運算元位數相同;源和目的運算元均是暫存器。當我們使用指標(地址的專用變數)時,可以相應用於BYTE,WORD類地址。對應關係如下:
char *p | 用於BYTE類地址,1個位元組 |
short *p | 用於WORD類地址,2個位元組 |
int *p | 用於DWORD類地址,4個位元組 |
引入指標後,對於MOV ECX,I;MOV BYTE [ECX], (I &0x0f)兩句組合語言可以用C語言替代,宣告一個指標變數p,指標變數指標地址ECX,*p表示該地址中存放的值,使用賦值語句即可改變地址p中存放的數值。
下表是對將資料寫入記憶體的幾種方式的總結:
方式 | 用法(使用的語句) |
---|---|
組合語言MOV指令 | MOV ECX,地址;MOV AL,資料;MOV [ECX],AL |
型別轉換 | *((char *) i)=資料,i為寫入的位置 |
指標 | i[p]=資料;p[i]=資料; *(p+i)=資料;這3種方式中p為寫入的首地址,需要提前指定,i為相對於首地址的偏移量p=(char *)i , *p=資料 |
(2).關鍵程式碼分析
bootpack.c程式碼分析
void io_hlt(void);
void write_mem8(int addr,int data);
void HariMain(void)
{
int i;
char *p;
for(i=0xa0000;i<=0xaffff;i++)
{
p=(char *)i;//這樣寫是為了消除警告
*p=i & 0x0f;
}
for(;;)
{
io_hlt();
}
}
這部分程式碼是使用指標向記憶體寫入資料,實現之前的圖形繪製。其中p=(char )i用到了型別轉換。型別轉換是改變數值型別的命令,一般不必每次都注意型別轉換,但是這裡的給指標變數賦值時需要先強制型別轉換為對應型別,不然會存在警告,因為不是每個數值都可以用來作為記憶體地址。在for迴圈中內容也可以相應使用型別轉換將資料寫入記憶體的語句替換,如((char *) i)=i & 0x0f,或者使用指標的其他寫法,如i[p]=i &0x0f,p[i]=i &0x0f, *(p+i)=i & 0x0f。
4、 內容4 色號設定
(1).內容概要
- 實驗內容: 設定相應的顏色,在8位彩色模式下,初始化調色盤。
本次實驗中使用320*200的8位顏色模式,色號使用8位(二進位制)數表示,但是這種方式可以指定的顏色很少。一般指定顏色是採用#ffffff的方式,使用6位16進位制的數,用2位16進位制的數表示R,2位16進位制的數表示G,2位16進位制的數表示B,也就是RGB的色彩表示方法。實驗中用到的0-15號碼的顏色如下:
調色盤的訪問步驟:
a.設定中斷遮蔽
b.將設定的調色盤號碼寫入0x03c8,將顏色按照RGB的順序寫入0x03c9。當設定多個調
色板號碼時,可以省略調色盤號碼,直接寫入顏色。
c.如果想要讀出調色盤的狀態,則首先將調色盤號碼寫入0x03c7,在從0x03c9中依次讀
出RGB。
d.取消中斷遮蔽。
彙編指令:IN指令是從裝置取得電氣訊號,OUT指令是向裝置傳送電訊號。IN和OUT指令可以實現CPU和裝置之間的埠通訊。PUSHFD指令是將標誌位按照雙位元組壓入棧,即PUSH EFLAGS,POPFD指令是將標誌位按照雙位元組彈出棧,即POP EFLAGS。
EFLAGS暫存器的介紹:EFLAGS 是一組用來存放進位標誌和中斷標誌等標誌的儲存器。進位標誌可以用JC或者JNC來判斷進位標誌是否為0.。對於中斷標誌,需要讀入EFLAGS,檢查第9位的情況,第9位為0,忽視中斷請求,否則則是立即處理中斷請求。
(2).關鍵程式碼分析
- 和之前內容1,2,3部分相同的程式碼不做說明
- 彙編程式碼部分
_io_cli: ; void io_cli(void);//設定中斷位為0
CLI
RET
_io_sti: ; void io_sti(void);//設定中斷位為1
STI
RET
- init_palette程式碼分析
void init_palette(void)
{
static unsigned char table_rgb[16 * 3] = {//定義16種顏色的陣列
0x00, 0x00, 0x00, /* 0:黒 */
0xff, 0x00, 0x00, /* 1:亮紅 */
…
0x84, 0x84, 0x84 /* 15:暗灰 */
};
set_palette(0, 15, table_rgb);//設定調色盤
return;
/* C語言中static char 語句只能用於資料,相當於彙編中的DB指令*/
}
這部分程式碼是初始化調色盤,首先定義一個16*3的二維資料,每行表示一種顏色,將0-15號顏色按照RGB的順序呼叫set_palette寫入調色盤
- set_palette程式碼分析
void set_palette(int start, int end, unsigned char *rgb)
{
int i, eflags;
eflags = io_load_eflags(); /* 記錄中斷許可標誌的值 */
io_cli(); /* 將中斷許可標誌置為0,禁止中斷 */
io_out8(0x03c8, start);
for (i = start; i <= end; i++) {
io_out8(0x03c9, rgb[0] / 4);
io_out8(0x03c9, rgb[1] / 4);
io_out8(0x03c9, rgb[2] / 4);
rgb += 3;
}
io_store_eflags(eflags); /* 復原中斷許可標誌 */
return;
}
這部分程式碼是設定的顏色寫入對應的裝置號碼中。首先使用io_load_sflags()函式記錄中斷許可標誌,接著呼叫io_cli()函式設定中斷遮蔽,將設定的調色盤號碼寫入0x03c8,使用for迴圈從start到end按照RGB的順序分別將end-start+1鍾顏色寫入0x03c9。最後使用io_store_aflags()取消中斷遮蔽。
5、 內容5 繪製矩形和系統介面
(1).內容概要
- 實驗內容: 通過畫素座標繪製不同顏色,不同大小,不同位置的矩形。
- 實驗重點:掌握畫素座標和繪製圖形之間的關係,即影像的座標系;學會利用座標關係畫畫。
(2).關鍵程式碼分析
#define COL8_000000 0
…
#define COL8_848484 15
採用巨集定義不同的色號
void boxfill8(unsigned char *vram, int xsize, unsigned char c, int x0, int y0, int x1, int y1)
{
int x, y;
for (y = y0; y <= y1; y++) {
for (x = x0; x <= x1; x++)
vram[y * xsize + x] = c;
}
return;
}
這部分程式碼是繪製矩形的程式碼。輸入4個座標引數,分別表示矩形左上角的座標和矩形右下角的座標,從而確定整個矩形。y*xsize+x表示按照列的方式進行訪問,vram[y * xsize + x] = c表示給座標為(x,y)的畫素設定值為c。
void HariMain(void)
{
char *p;
init_palette(); /*設定調色盤*/
p = (char *) 0xa0000; /* 顏色設定的首地址*/
boxfill8(p, 320, COL8_FF0000, 20, 20, 120, 120);//繪製矩形左上角座標為(20,20),右下角座標為(120,120)
boxfill8(p, 320, COL8_00FF00, 70, 50, 170, 150);
boxfill8(p, 320, COL8_0000FF, 120, 80, 220, 180);
for (;;) {
io_hlt();
}
}
這部分程式碼是繪製三個不同顏色矩形的程式碼。繪製系統介面的程式碼型別,關鍵在於去確定矩形的位置。座標系中y軸正方向水平向下,x軸正方向水平向右。以下是對系統介面的繪製的分析。
boxfill8(vram, xsize, COL8_008484, 0, 0, xsize - 1, ysize - 29);
//系統介面的上半部分
boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 28, xsize - 1, ysize - 28);
boxfill8(vram, xsize, COL8_FFFFFF, 0, ysize - 27, xsize - 1, ysize - 27);
//上部分介面和下部分介面的分界
boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize - 26, xsize - 1, ysize - 1);
//介面下半部分
boxfill8(vram, xsize, COL8_FFFFFF, 3, ysize - 24, 59, ysize - 24);
boxfill8(vram, xsize, COL8_FFFFFF, 2, ysize - 24, 2, ysize - 4);
boxfill8(vram, xsize, COL8_848484, 3, ysize - 4, 59, ysize - 4);
boxfill8(vram, xsize, COL8_848484, 59, ysize - 23, 59, ysize - 5);
//左下角的方框
boxfill8(vram, xsize, COL8_000000, 2, ysize - 3, 59, ysize - 3);
boxfill8(vram, xsize, COL8_000000, 60, ysize - 24, 60, ysize - 3);
//左下角方框的陰影效果
boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 24, xsize - 4, ysize - 24);
boxfill8(vram, xsize, COL8_848484, xsize - 47, ysize - 23, xsize - 47, ysize - 4);
boxfill8(vram, xsize, COL8_FFFFFF, xsize - 47, ysize - 3, xsize - 4, ysize - 3);
boxfill8(vram, xsize, COL8_FFFFFF, xsize - 3, ysize - 24, xsize - 3, ysize - 3);
//右下角方框
兩份程式碼繪製效果如下:
二、遇到的問題及解決方法
1、 描述問題1
- 問題描述
教材第77頁,set_palette函式中rgb[0]/4、rgb[1]/4、rgb[2]/4為什麼要除以4?
- 解決方法
起初原本的想法是除以4之後,是為了提高顏色的灰度。經過試驗發現確實如此,當增大除數的時候,顏色顯示會變得越來越暗,除以一個數相當於是進行右移操作,顏色的畫素值會變小,對應的顏色也會逐漸加深,但是對於除以4的解釋還是不夠全面。後來通過同學的啟示,從圖形顯示模式入手,查閱資料得到如下原因。
本次實驗繪製圖形是在3202008的八位彩色圖形模式下進行的。通過查閱資料可以知道VGA指定調色盤顏色時,一個顏色頻道只有6位,而我們使用的24位顏色模式中8位表示R,8位表示G,8位表示B,已經超出了顏色頻道的表示位數,所以需要捨棄2位。考慮到顏色偏差不能太大,選擇右移操作捨棄低2位,對應的操作就是除以4。
三、程式設計創新點
1、 描述創新點1,關鍵程式碼及結果截圖
- 創新點1
運用本次所學的圖形顯示技術,繪製了一個黃色的小人(以下簡稱小黃人),通過迴圈控制座標的變化,可以使小黃人的雙臂擺動,向螢幕前的你打招呼。背景顏色更換為亮灰色。
- 關鍵程式碼
矩形繪製程式碼和原始碼一致
圓的繪製程式碼
圓弧的繪製程式碼和圓的繪製程式碼基本一致,不過因為是繪製圓弧,需要對x,y兩個座標進行位置的限制,使其繪製出的結果是一個弧形。
主體函式部分
使用ly和ry控制手上下揮動,m用來控制揮手速度,k用來控制抬手的高度
- 結果截圖
小黃人揮手的動畫效果通過3張圖片展示。
四、實驗心得體會
- 本次實驗是自制作業系統的第4天,實驗主要涉及的內容C語言的畫面顯示練習。筆者循序漸進從彙編開始實現記憶體寫入,再到後面的指標使用,一步步進階,設定調色盤,熟悉圖形的座標系,最終可以使用C語言來繪製出不同的圖形,甚至是動畫的效果。第一次感覺自制作業系統有點樣子了,可以實現畫面顯示,動畫效果。
- 實驗中涉及的知識點基本上都是之前學習過的,如函式呼叫的引數地址,返回值存放的暫存器,指標的使用等等,因此實驗過程中比較順利。本次實驗的重點就是畫面顯示,對於一個圖形畫面,設計的關鍵就是去給對應位置的畫素設定不同的顏色,從而達到畫圖的效果,但對於一些比較複雜的影像,需要程式設計的程式程式碼也會是很長很複雜的。
相關文章
- 30天自制作業系統——第3天實驗總結作業系統
- 30天自制作業系統 For Linux作業系統Linux
- 30天自制作業系統:第三天作業系統
- 《30天自制作業系統》譯者序(偽)作業系統
- 30天自制作業系統(一)啟動區作業系統
- 讀懂《30天自制作業系統》的捷徑作業系統
- 30天自制作業系統-merk11的第三天作業系統
- 30天自制作業系統-merk11的第一天作業系統
- 由《30天自制作業系統》引發的漫畫創作作業系統
- 三十天自制作業系統(13)作業系統
- 釋出在《30天自制作業系統》之前的幫助閱讀貼作業系統
- 為什麼《30天自制作業系統》封面中的貓是兩隻尾巴作業系統
- 【全棧React】第30天: 總結和更多的資源全棧React
- 自制作業系統(一) 第一個作業系統作業系統
- 作業系統總結作業系統
- [自制作業系統] 第07回 認識保護模式之地址對映作業系統模式
- 作業系統身份驗證和口令檔案身份驗證總結作業系統
- 30 天精通 RxJS (25):Subject 總結JS
- 作業系統實驗六實驗報告作業系統
- 作業系統實驗七實驗報告作業系統
- 《作業系統30天》-合川秀實-學習日誌day11作業系統
- 作業系統寫題總結作業系統
- 《作業系統》分析與總結作業系統
- 作業系統實驗第七天:從滑鼠接受資料作業系統
- [自制作業系統] 第10回 認識保護模式之深入淺出特權級作業系統模式
- 常用快取系統使用經驗總結快取
- 這兩天的面試經驗總結面試
- 作業系統實驗——程式控制作業系統
- 作業系統之“實驗一”作業系統
- flex實驗總結Flex
- 實驗一 總結
- 作業系統常用知識總結!作業系統
- 【javaWeb】第55天——java web階段性總結JavaWeb
- 作業系統實驗4 系統呼叫聯絡作業系統
- 實習2天的總結
- 實驗0、瞭解和熟悉作業系統實驗作業系統
- Cobbler自動化批量安裝Linux作業系統 - 運維總結Linux作業系統運維
- 作業系統實驗2 程式控制和系統呼叫作業系統