第7天
PIC初始化之后寿弱,再寫中斷處理函數(shù)揍异,然后把中斷處理函數(shù)的入口地址注冊(cè)在IDT中⊙锇希現(xiàn)在重點(diǎn)是中斷處理函數(shù)如何編寫溺健。
PIC中還有一個(gè)寄存器OCW,如果鍵盤發(fā)生中斷阎抒,需要向PIC發(fā)送0X60+IRQ號(hào)碼憔狞,執(zhí)行這行代碼之后PIC才會(huì)繼續(xù)監(jiān)視IRQ1的中斷是否發(fā)生族淮。OCW設(shè)置完畢后凳干,CPU再?gòu)亩丝谥凶x入鍵盤數(shù)據(jù)晴裹,計(jì)算機(jī)的硬件規(guī)定被济,鍵盤數(shù)據(jù)總共8位救赐,從0x0060端口讀入。附上中斷處理程序
void inthandler21(int *esp)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
unsigned char data, s[4];
io_out8(PIC0_OCW2, 0x61);
data = io_in8(PORT_KEYDAT);
sprintf(s, "%02X", data);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 23, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
return;
}
像上面的程序那樣在中斷處理程序中既要讀取端口數(shù)據(jù)又要顯示到屏幕上只磷,作大量的圖像處理工作经磅,如果這個(gè)時(shí)候正好有一個(gè)中斷進(jìn)來(lái),計(jì)算機(jī)就不能處理钮追,只能不作任何反應(yīng)预厌。為了不影響中斷處理,把inthandle21中斷處理程序中的圖像處理部份拿出來(lái)元媚,inthandle21中斷處理程序只做一個(gè)工作:讀取端口數(shù)據(jù)轧叽,把數(shù)據(jù)放入緩存區(qū),把圖像處理工作放在主程序中處理刊棕。主程序一直查詢緩存區(qū)是否有數(shù)據(jù)炭晒,如果有數(shù)據(jù)就把數(shù)據(jù)顯示到屏幕上。接下來(lái)重點(diǎn)就是怎么寫緩存區(qū)的程序了甥角。
鍵盤的緩沖區(qū)程序其實(shí)就是大學(xué)學(xué)過(guò)的隊(duì)列网严,也就是能保證先進(jìn)先出的數(shù)據(jù)結(jié)構(gòu)。當(dāng)然我們?cè)趯懖僮飨到y(tǒng)當(dāng)然無(wú)法用鏈表嗤无,只能用數(shù)組實(shí)現(xiàn)一個(gè)隊(duì)列震束。
先定義一個(gè)隊(duì)列的結(jié)構(gòu)體
struct FIFO8
{
unsigned char *buf;
int p;//下一個(gè)數(shù)據(jù)定入地址
int q;//下一個(gè)數(shù)據(jù)讀出地址
int size;//隊(duì)列長(zhǎng)度
int free;//表示隊(duì)列有沒(méi)有數(shù)據(jù)的字節(jié)數(shù)
int flags;//標(biāo)記隊(duì)列有沒(méi)有溢出,最低位是1表示溢出
};
第一步:初始化
void fifo8_init(struct FIFO8 *fifo, int size, unsigned char *buf)
{
fifo->size = size;
fifo->buf = buf;
fifo->free = size;
fifo->flags = 0;
fifo->p = 0;
fifo->q = 0;
return;
}
第二步:存入數(shù)據(jù)
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
{
if (fifo->free == 0)
{
fifo->flags |= FLAGS_OVERRUN;
return -1;
}
fifo->buf[fifo->p] = data;
fifo->p++;
if (fifo->p == fifo->size)
{
fifo->p = 0;
}
fifo->free--;
return 0;
}
第三步:讀取數(shù)據(jù)
int fifo8_get(struct FIFO8 *fifo)
{
int data;
if (fifo->free == fifo->size)
{
return -1;
}
data = fifo->buf[fifo->q];
fifo->q++;
if (fifo->q == fifo->size)
{
fifo->q = 0;
}
fifo->free++;
return data;
}
附:查詢數(shù)據(jù)量
int fifo8_status(struct FIFO8 *fifo)
{
return fifo->size - fifo->free;
}
鍵盤的中斷碼是IRQ1当犯,而鼠標(biāo)則要晚得多垢村,是IRQ12。如果要讓鼠標(biāo)操作有效必須發(fā)行指令嚎卫,讓下面兩個(gè)裝置有效嘉栓,一個(gè)是鼠標(biāo)控制電路,一個(gè)是鼠標(biāo)本身。首先要激活鼠標(biāo)控制電路胸懈,鼠標(biāo)控制電路包含在鍵盤控制電路里担扑。然后發(fā)送激活鼠標(biāo)的指令,其實(shí)發(fā)送這個(gè)指令實(shí)際上就是CPU發(fā)送數(shù)據(jù)到鼠標(biāo)控制器趣钱,也就是鍵盤控制器涌献。
鼠標(biāo)的中斷處理程序和鼠標(biāo)差不多,也就是從端口中讀取數(shù)據(jù)放入隊(duì)列中首有,甚到CPU讀取數(shù)據(jù)的端口號(hào)都是一樣的燕垃。然后在主程序中從鼠標(biāo)隊(duì)列中取出數(shù)據(jù)進(jìn)行處理。鍵盤的處理非常簡(jiǎn)單井联,從端口讀進(jìn)來(lái)的數(shù)據(jù)就是鍵盤的掃描碼卜壕。那么從端口讀進(jìn)來(lái)的鼠標(biāo)數(shù)據(jù)表示什么意思呢?
第八天
鼠標(biāo)一開始設(shè)置完成會(huì)自動(dòng)發(fā)生一次中斷烙常,這次中斷發(fā)送到CPU的數(shù)據(jù)為0xfa轴捎,只是表示鼠標(biāo)已經(jīng)設(shè)置完成,將會(huì)向CPU發(fā)送數(shù)據(jù)蚕脏。我們每次對(duì)鼠標(biāo)操作都會(huì)引起鼠標(biāo)向CPU發(fā)送三次中斷侦副,每次中斷發(fā)送一個(gè)字節(jié),一共三個(gè)字節(jié)驼鞭,我們要把這三個(gè)字節(jié)湊到一起處理才是有意義的秦驯。
鼠標(biāo)一次性接收3字節(jié)數(shù)據(jù),其中第一個(gè)字節(jié)表示鼠標(biāo)的動(dòng)作挣棕,第一個(gè)字節(jié)的高4位取值范圍是03译隘,如果出現(xiàn)其它值,說(shuō)明鼠標(biāo)出現(xiàn)錯(cuò)誤洛心。第一個(gè)字節(jié)的低4位取值范圍8F固耘,如果出現(xiàn)其他值也說(shuō)明鼠標(biāo)發(fā)生錯(cuò)誤,如果第0位為1說(shuō)明鼠標(biāo)左鍵按下皂甘,如果第1位為1說(shuō)明右鍵被按下玻驻。第二個(gè)字節(jié)為鼠標(biāo)水平方向移動(dòng)的多少,正值為右偿枕,負(fù)值為左璧瞬。第三個(gè)字節(jié)為鼠標(biāo)豎直方向移動(dòng)多少,正值向上渐夸,負(fù)值向下嗤锉。
目前已經(jīng)能處理鼠標(biāo)操作傳入的3次中斷數(shù)據(jù),現(xiàn)在就是簡(jiǎn)單得在畫面上顯示出來(lái)墓塌。首先把原來(lái)鼠標(biāo)圖像所在的位置給畫成背景色瘟忱,然在計(jì)算新的鼠標(biāo)位置奥额,然后在新的鼠標(biāo)位置上畫一下鼠標(biāo)圖像,然后就感覺鼠標(biāo)移動(dòng)了访诱。
接下來(lái)插入講解一下如何從CPU實(shí)模式跳入保護(hù)模式垫挨。我們首先把顯卡模式設(shè)置好,然后把一些需要BIOS做的工作給做好触菜。將下來(lái)就開始跳入保護(hù)模式了九榔。關(guān)掉CPU級(jí)別中斷,往ox60號(hào)端口寫入0xdf涡相,可以讓CPU使用超過(guò)1M的內(nèi)存容量哲泊。然后設(shè)置CR0寄存器,把CR0寄存器讀出來(lái)催蝗,然后把最高位和最低位設(shè)置為0切威,再放入CR0,CPU就跳入保護(hù)模式了丙号,進(jìn)入保護(hù)模式后馬上要執(zhí)行JMP指令先朦,才能使接下來(lái)的指令正常執(zhí)行。接下來(lái)只要把特定的程序復(fù)制到內(nèi)存中就行了槽袄。也就是IDT,GDT,主程序烙无,棧及其他這個(gè)所放的位置在內(nèi)存中放好就行了锋谐。
保護(hù)模式和實(shí)模式的的區(qū)別就在于計(jì)算內(nèi)存地址時(shí)遍尺,是使用段寄存器的值直接指定地址值的一部份呢,還是通過(guò)GDT 使用段寄存器的值指定并非實(shí)際存在的地址號(hào)碼涮拗。
第九天
鼠標(biāo)處理告一段落乾戏,這一天主要做內(nèi)存管理,內(nèi)存管理的第一步是檢測(cè)計(jì)算機(jī)內(nèi)存容量有多大三热。
先檢測(cè)CPU有沒(méi)有緩存鼓择,如果CPU是486以上就有緩存,以下就沒(méi)有緩存也就不需要關(guān)閉緩存就漾。檢測(cè)方法就是看往CPU標(biāo)志寄存器第18位寫入是否有效呐能。然后對(duì)CR0寄存器的某些位設(shè)置為0,才能關(guān)閉掉緩存抑堡。關(guān)掉緩存之后摆出,開始檢測(cè)內(nèi)存容量,檢測(cè)的方法也很簡(jiǎn)單首妖,先把內(nèi)存地址從小到大偎漫,每次讀一個(gè)內(nèi)存內(nèi)容,然后把內(nèi)存內(nèi)容取否有缆,寫入內(nèi)存象踊,再讀一次這個(gè)內(nèi)存温亲,如果新的內(nèi)容跟預(yù)計(jì)取反的內(nèi)容一樣說(shuō)明這個(gè)內(nèi)存地址是有效的。為了提高效率我們沒(méi)有一個(gè)字節(jié)一個(gè)字節(jié)檢測(cè)杯矩,而是每次檢測(cè)4KB栈虚,所以這4KB內(nèi)存中最一4個(gè)字節(jié)是有效的,那就認(rèn)為這4KB都是有效內(nèi)存史隆。內(nèi)存大小知道了之后节芥,就可以進(jìn)行內(nèi)存管理了。
內(nèi)存管理的基礎(chǔ)是內(nèi)存分配和內(nèi)存釋放逆害。內(nèi)存是否使用可以在內(nèi)存中做一個(gè)位圖來(lái)表示头镊,比如內(nèi)存有16MB,我們以4KB為一個(gè)分配單位魄幕,那么一共需要4096位標(biāo)記相艇,也就是512字節(jié)。在這512字節(jié)中纯陨,如第1位是0坛芽,就表示0~0xfff這個(gè)內(nèi)存地址未使用。如果以這種方式管理內(nèi)存翼抠,從代碼上來(lái)講比較簡(jiǎn)單咙轩。但是也有不足,那就是分配最小內(nèi)存地址不靈活阴颖,上面的例子中以4KB為一個(gè)單位分配活喊,如果只需要1B,那么這樣分配就太浪費(fèi)了量愧。如果位圖以1字節(jié)為單位的話钾菊,那么位圖就需要16777216個(gè)標(biāo)記位,也就是2MB個(gè)標(biāo)記位偎肃,太浪費(fèi)了煞烫。
還有一種就是這本書使用的內(nèi)存管理方式,類似于從XXX號(hào)地址開始的YYY字節(jié)的空間是空著的累颂≈拖辏可以創(chuàng)建這樣一種數(shù)據(jù)結(jié)構(gòu)
struct FREEINFO
{
unsigned int addr, size;
}
struct MEMMAN
{
int frees;
struct FREEINFO free[1000];
}
struct MEMMAN memman;
memman.frees = 1;
memman.free[0].addr = 0x400000;
memman.free[0].size = 0x07c00000;
如果需要100KB的空間,查看memman中free的情況紊馏,找出100MB以上的可用空間即可
for(int i = 0; i < memman.frees; i++)
{
if(memman.free[i].size >= 100 * 1024)
{
"找到可用空間';
"從地址memman.free[i].addr開始的100KB空間;
}
}
用這種方法可以實(shí)現(xiàn)內(nèi)存的管理料饥。