三十天自制操作系統(tǒng)(2)

第4天

今天一開(kāi)始也學(xué)最后學(xué)的用匯編寫(xiě)一個(gè)能被C語(yǔ)言調(diào)用的函數(shù),函數(shù)名叫write_mem8,功能是在指定內(nèi)容中寫(xiě)入特定的數(shù)據(jù)。這個(gè)函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)表示目標(biāo)地址,第二個(gè)參數(shù)表示寫(xiě)入的數(shù)據(jù)。

這個(gè)函數(shù)最重要的功能就是往指定的內(nèi)存地址中寫(xiě)入數(shù)據(jù),那么用C語(yǔ)言可不可能也指定一個(gè)內(nèi)存地址呢?其實(shí)是可以的,使用C語(yǔ)言中指針的概念就可以了。C語(yǔ)言中所謂的指針其是就是平時(shí)所謂的內(nèi)存地址。比如想要往0xa0000內(nèi)存地址中寫(xiě)入數(shù)據(jù),可以這樣:

int i;
char* p;
i  = 0xa0000;
p = (char*)i;
*p = i;

這樣就把變量i 中的內(nèi)容寫(xiě)到內(nèi)存0xa0000中了,而這個(gè)地址就是前面寫(xiě)的操作系統(tǒng)中的顯存地址,往這個(gè)地方寫(xiě)入數(shù)據(jù)就相當(dāng)于在屏幕上畫(huà)上內(nèi)容。

在進(jìn)入32位之前,用BIOS中斷把顯卡設(shè)置成調(diào)色板模式。要寫(xiě)設(shè)置顯卡中的調(diào)色板,只能使用匯編語(yǔ)言中的in和out指令。首先將屏蔽中斷;將想要設(shè)定的調(diào)色板號(hào)碼寫(xiě)入0x03c8,按RGB序順寫(xiě)入0x03c9,如果還要繼續(xù)寫(xiě)入,只需要繼續(xù)寫(xiě)入0x03c9;如果要讀出調(diào)色板狀態(tài),將調(diào)色板的號(hào)碼寫(xiě)入0x03c7,再?gòu)?x03c9讀取3次,調(diào)出的順序是RGB,如果要讀取下一個(gè),只要繼續(xù)從0x03c9中調(diào)取下一個(gè)調(diào)色板;結(jié)束之后,取消中斷屏蔽。

中斷屏蔽指令:

CLI(clear interrupt flag)

取消中斷屏蔽指令:

STI(set interrupt flag)

根據(jù)C語(yǔ)言的規(guī)定,匯編中放入eax中的數(shù)據(jù)就是C語(yǔ)言的返回值。

第五天

在32位CPU的模式下操作系統(tǒng)中顯示字符和在屏幕上畫(huà)圖的原理是一樣的,字符只是一種特殊的圖形。本書(shū)里把一個(gè)字符定義為8*16個(gè)像素的位圖,那么一個(gè)字符就占16字節(jié)。一般為了畫(huà)字符方便,而且操作系統(tǒng)為了支持不同字體功能,會(huì)另外單獨(dú)定義一個(gè)字體文件,這個(gè)操作系統(tǒng)會(huì)在加載內(nèi)存的時(shí)候把字體數(shù)據(jù)讀入內(nèi)存中。如果定義ASCII編碼的256個(gè)字符,每個(gè)字符16字節(jié),那么一共4096個(gè)字節(jié),還可以接受。

sprintf(地址, 格式,值,值,值,....)

關(guān)于格式的詳細(xì)說(shuō)明:

  1. %d,單純的十進(jìn)制數(shù)
  2. %5d,5位十進(jìn)制數(shù)
  3. %05d,在前面補(bǔ)0,強(qiáng)制達(dá)到時(shí)5位
  4. %x,單純十六進(jìn)制數(shù)字母用小寫(xiě)
  5. %X,單純十六進(jìn)制數(shù)字母用大寫(xiě)

在屏幕上顯示鼠標(biāo),也就是在屏幕上畫(huà)一個(gè)圖像。本書(shū)把鼠標(biāo)定義為16*16個(gè)像素的位圖,每個(gè)像素是8個(gè)字節(jié),總共256個(gè)字節(jié)。鼠標(biāo)圖像有三種顏色,第一是鼠標(biāo)的邊緣,我們定義為黑色,位圖中用*表標(biāo);第二個(gè)是鼠標(biāo)本身的顏色,我們定義為黑色,位圖中用0表示;第三個(gè)是背景色,就是不顯示顏色,用背景色補(bǔ)充,用.表示。

在32位CPU模式下,內(nèi)存需要分段,為了表示一個(gè)段,需要三個(gè)信息:1、段的大?。?、段的起始地址;3、段的管理屬性(禁止寫(xiě)入,禁止執(zhí)行,系統(tǒng)專(zhuān)用等)。CPU用8個(gè)字節(jié)表示這些信息,但是段寄存器只有16位。表示的方法跟調(diào)色板很相似,先有一個(gè)段選擇符,存放在段寄存器里,然后在內(nèi)存中預(yù)先設(shè)定這8個(gè)字節(jié)的信息。16位段寄存器的低3位不能使用,能用的只有13位,所以一共能表示8192個(gè)段。每個(gè)段需要8個(gè)字節(jié)表示,那么一共需要8192*8=64KB表示,這64KB的數(shù)據(jù)就是GDT:global descriptor table,全局段號(hào)記錄表。GDT存入在內(nèi)存的某個(gè)地方,然后將內(nèi)存的起始地址和有效設(shè)定個(gè)數(shù)存放在CPU的GDTR(global descriptor table rigister)中。

IDT就是中斷記錄表(interrupt descriptor table),當(dāng)CPU遇到中斷后,暫時(shí)停止正在處理的任務(wù),轉(zhuǎn)而執(zhí)行中斷程序。IDT記錄了0~255的中斷號(hào)碼與調(diào)用函數(shù)的對(duì)應(yīng)關(guān)系,設(shè)定方法與GDT很相似。而且要先設(shè)定GDT再設(shè)定IDT。設(shè)定IDT的目的是想讓我們的操作系統(tǒng)對(duì)鼠標(biāo)的移動(dòng)作出反應(yīng)。

第六天

用C語(yǔ)言寫(xiě)的操作系統(tǒng)源文件bootpack.c已經(jīng)很長(zhǎng)了,現(xiàn)在考慮要將源文件根據(jù)不同的功能拆分開(kāi)來(lái)。分成三個(gè)部分:1、關(guān)于屏幕上畫(huà)圖的部分graph.c;2、關(guān)于GDT\IDT設(shè)置的部分dsctbl.c;3、其他處理bootpack.c

由于源文件多了相應(yīng)的Makefile里的編繹規(guī)則也就多了。Makefile有一個(gè)技巧可以將類(lèi)似的生成規(guī)則

%.gas : %.c Makefile
  $(CC1) -o $*.gas $*.c

%.nas : %.gas Makefile
  $(GAS2NASK $*gas $*.nas

make.exe會(huì)先尋找普通的生成規(guī)則,如果沒(méi)有找到,就嘗試用一般規(guī)則。

C語(yǔ)言中的include有<>和“”的區(qū)別,前者表示頭文件在編繹器的文件夾中,后者表示頭文件在源文件所在的文件夾中。

_load_gdtr ; void load_gdtr(int limit, int addr); 
  mov ax, [esp + 4]; limit
  mov [esp + 6], ax
  lgdt [esp + 6]
  ret

這個(gè)函數(shù)將指定的段上限和地址賦值給GDTR這個(gè)48位寄存器,也就是6個(gè)字節(jié)。低16位存放段上限,它等于GDT的有效字節(jié)數(shù)-1,剩余的4個(gè)字節(jié)代表GDT的開(kāi)始地址。上面程序把傳入的參數(shù)limit低2字節(jié)移到高2字節(jié),然后執(zhí)行l(wèi)gdt指令。

GDT中每一個(gè)段信息用8個(gè)字節(jié)表示,寫(xiě)的操作系統(tǒng)中用一個(gè)定義了一個(gè)結(jié)構(gòu)體表示。

struct SEGMENT_DESCRIPTOR
{
  short limit_low, base_low;
  char base_mid, access_right;
  char limit_high, base_high; 
};

段的地址也叫做段基礎(chǔ),用32位表示,在結(jié)構(gòu)體中用4個(gè)字段表示,分別是base_low(2個(gè)字節(jié)), base_mid(1個(gè)字節(jié)), base_high(1個(gè)字節(jié))。正好一共4個(gè)字節(jié)。

段的大小也叫做段上限,結(jié)構(gòu)體中用3個(gè)字節(jié)表示,分別是limit_low(2個(gè)字節(jié)),limit_high(1個(gè)字節(jié))。雖然段上限我們結(jié)構(gòu)體里使用了3個(gè)字節(jié),但是實(shí)際是有效的只有20位,另外4位表示段屬性。limit_high的高4位表示段屬性。段大小用20位表示,那么一個(gè)段最大的只有1MB。但是可以通過(guò)段屬性中的一個(gè)位設(shè)置成1就可以把段上限的單位從字節(jié)轉(zhuǎn)換成頁(yè),一頁(yè)表示4KB。那么20位段上限就可以不示4GB。

結(jié)構(gòu)體中還有一個(gè)字段access_right,表示段屬性,也叫做段的訪問(wèn)權(quán)屬性。段屬性一共用12個(gè)字節(jié)表示,高4位保存在limit_high字段的高4位中,這4位又被叫做擴(kuò)展訪問(wèn)權(quán),這4位由GD00構(gòu)成,G表示段上限字段的單位是字節(jié)還是頁(yè)(0表示字節(jié),1表示頁(yè))。D字段表示段模式,1是指32位,0是指16位。access_right簡(jiǎn)單說(shuō)明如下:

  1. 00000000(0x00): 未使用的記錄 段
  2. 10010010(0x92):系統(tǒng)專(zhuān)用,可讀寫(xiě),不可執(zhí)行
  3. 10011010(0x9a):系統(tǒng)專(zhuān)用,可執(zhí)行,可讀不可寫(xiě)
  4. 11110010(0xf2):應(yīng)用程序用,可讀寫(xiě),不可執(zhí)行
  5. 11111010(0xfa):應(yīng)用程序用,可執(zhí)行,可讀不可寫(xiě)

在使用中斷之前一定要先設(shè)置PIC,programmable interrupt controller。電腦的中斷信用有15個(gè),兩個(gè)PIC,與CPU直接相連的稱(chēng)為主PIC,與主PIC相連的PIC稱(chēng)為從PIC。從PIC通過(guò)2號(hào)IRQ與主PIC相連。

PIC的所有寄存器都是8位的,IMR是interrupt mask register的縮寫(xiě),中斷屏蔽寄存器。8位分別對(duì)應(yīng)8路IRQ信號(hào),如果某一位設(shè)置為1則忽略該路信號(hào)。

ICW是initial control word的縮寫(xiě),為初始化控制數(shù)據(jù),一共有4個(gè),編號(hào)分別為1-4。ICW1、ICW3,ICW4的設(shè)定值為固定,操作系統(tǒng)唯一能設(shè)置的是ICW2。ICW2表示IRQ以幾號(hào)中斷通知CPU。我們寫(xiě)的操作系統(tǒng)里把015號(hào)中斷設(shè)置為0x200x2f號(hào)中斷。

匯編指令pushad相當(dāng)于

push eax
push ecx
push edx
push ebx
push esp
push ebp
push esi
push edi

相對(duì)應(yīng)的也有popad

想讓CPU處理中斷,先寫(xiě)中斷的處理程序,然后再把中斷處理程序的入口地址注冊(cè)IDT中,等到中斷信號(hào)發(fā)生的時(shí)候,系統(tǒng)會(huì)自動(dòng)調(diào)用中斷處理程序。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容