先體驗一下編譯仿真方法:
30天自制操作系統(tǒng)光盤代碼在下面鏈接,但是沒有編譯仿真工具:
https://gitee.com/zhanfei3000/30dayMakeOS
仿真工具在下面鏈接:
https://gitee.com/909854136/nask-code-ide
這是一個集成的編譯仿真工具,只需要把上面仿真工具的文件夾:
\nask-code-ide-master\crtools
復制到源碼文件加下,并改名為z_tools就可以按照書中的方法編譯仿真了:
\30dayMakeOS-master\z_tools
z_tools如下:
在代碼30dayMakeOS-master\01_day目錄下執(zhí)行下面編譯仿真指令就可以看到仿真出的操作系統(tǒng)了。
后面章節(jié)源碼寫了 makefile就簡單了,只要輸入make就可以編輯了 然后再輸入make run就可以仿真了
標題一、代碼執(zhí)行順序(前內容六天的內容)
ipl10.nas–>asmhead.nas–>boopack.c
標題二、代碼閱讀
1.ipl10.nas(將軟盤內容拷貝到內存中)
; haribote-ipl
; TAB=4
; 讀取軟盤內容到內存中,然后跳轉到0xc200開始執(zhí)行,就是asmhead.nas文件
CYLS EQU 10 ; CYLS=10 讀取是10個柱面
ORG 0x7c00 ; 指明程序裝載地址
; 以下這段是FAT12格式軟盤專用代碼 0x7c00--0x7dff
JMP entry
DB 0x90
DB "HARIBOTE" ; 啟動區(qū)的名字可以是任意的,但必須是8字節(jié)
DW 512 ; 每個扇區(qū)(sector)的大小必須為512字節(jié)
DB 1 ; 簇(cluster)的大小必須為1個扇區(qū)
DW 1 ; FAT的起始位置(一般從第一個扇區(qū)開始)
DB 2 ; FAT的個數(shù)(必須為2)
DW 224 ; 根目錄的大小(一般設為244項)
DW 2880 ; 該磁盤的的大小(必須為2880扇區(qū))
DB 0xf0 ; 磁盤的種類(必須為0xfd)
DW 9 ; FAT的長度(必須為9扇區(qū))
DW 18 ; 一個磁道(track)有幾個扇區(qū)(必須為18)
DW 2 ; 磁頭數(shù)(必須為2)
DD 0 ; 不使用分區(qū)(必須為0)
DD 2880 ; 重寫一次磁盤大小
DB 0,0,0x29 ; 意義不明,固定
DD 0xffffffff ; (可能是)卷標號碼
DB "HARIBOTEOS " ; 磁盤名稱(11字節(jié))
DB "FAT12 " ; 磁盤格式名稱(8字節(jié))
RESB 18 ; 先騰出18字節(jié)
; 程序核心
entry:
MOV AX,0 ; AX=0 初始化寄存器
MOV SS,AX ; SS=AX=0
MOV SP,0x7c00 ; SP=0x7c00
MOV DS,AX ; DS=AX=0
; 讀磁盤(從軟盤中讀數(shù)據裝到內存中0x8200--0x83ff
MOV AX,0x0820 ; AX=0x0820 設置緩存區(qū)的段地址
MOV ES,AX ; ES=AX=0x0820 ES:BX就是緩存區(qū)的地址
MOV CH,0 ; CH=0 CH表示柱面號
MOV DH,0 ; DH=0 DH表示磁頭號
MOV CL,2 ; CL=2 CL表示扇區(qū)號
readloop:
MOV SI,0 ; SI=0, 用于記錄錯誤次數(shù),實現(xiàn)試錯功能(非必須功能)
retry:
MOV AH,0x02 ; AH=0x02 13號中斷所需參數(shù),表示操作類型,0x02(讀盤),0x03寫盤,0x04校驗,0x0c尋道
MOV AL,1 ; AL=1 AL處理對象的扇區(qū)數(shù),表示一次只能讀取1個扇區(qū)
MOV BX,0 ; BX=0 緩沖地址
MOV DL,0x00 ; DL=0x00 DL表示驅動器號
INT 0x13 ; BIOS提供的服務,用于操作軟盤
JNC next ; CF=0,跳轉到next執(zhí)行
ADD SI,1 ; SI=SI+1,記錄嘗試的次數(shù),實現(xiàn)試錯功能(非必須功能)
CMP SI,5 ;
JAE error ; SI >= 5 跳轉到error執(zhí)行
MOV AH,0x00 ; SI<5 AH=0x00 清空INT 0x13的錯誤碼
MOV DL,0x00 ; DL=0x00 設置驅動器號
INT 0x13 ;
JMP retry ; 跳轉到retry執(zhí)行
next:
MOV AX,ES ; AX=ES
ADD AX,0x0020 ; AX=AX+0x0020
MOV ES,AX ; ES=AX ES向后移動了一個扇區(qū)的大小
ADD CL,1 ; CL=CL+1 扇區(qū)號加1
CMP CL,18 ;
JBE readloop ; CL <= 18 跳轉到readloop執(zhí)行
MOV CL,1 ; CL > 18 CL=1
ADD DH,1 ; DH=1 準備讀取磁頭0的內容
CMP DH,2 ;
JB readloop ; DH < 2 跳轉到readloop執(zhí)行
MOV DH,0 ; DH>=2 說明已讀取完成
ADD CH,1 ; CH=CH+1 準備讀取下一個柱面
CMP CH,CYLS ;
JB readloop ; CH < CYLS 跳轉到readloop執(zhí)行
; 磁盤內容裝載內容的結束地址告訴haribote.sys
MOV [0x0ff0],CH ; [0x0ff0]=CH 將CYLS的值寫入到內存地址0x0ff0中,可以參考asmhead.nas中對應的變量賦值
JMP 0xc200 ; 跳轉到0xc200
error:
MOV SI,msg ;SI=msg 顯示錯誤信息
putloop:
MOV AL,[SI] ; AL=[SI] 讀取[SI]內存中的信息
ADD SI,1 ; SI=SI+1
CMP AL,0 ;
JE fin ; AL==0, 錯誤信息顯示完畢,跳轉到fin
MOV AH,0x0e ; AH=0x0e 設置顯示屬性
MOV BX,15 ; BX=15 設置顯示屬性
INT 0x10 ; 調用BIOS顯示服務
JMP putloop ;
fin:
HLT ; 讓CPU停止等待命令
JMP fin ;
msg:
DB 0x0a, 0x0a ;
DB "load error"
DB 0x0a ;
DB 0 ;
RESB 0x7dfe-$ ;
DB 0x55, 0xaa ; 按規(guī)定設置字節(jié)
2.asmhead.nas(完成一些不能用c語言實現(xiàn)的功能,因為編碼問題,有一些亂碼,大概能看明白)
; haribote-os boot asm
; TAB=4
[INSTRSET "i486p"]
VBEMODE EQU 0x105 ; 1024 x 768 x 8bit 彩色
; 顯示模式
; 0x100 : 640 x 400 x 8bit 彩色
; 0x101 : 640 x 480 x 8bit 彩色
; 0x103 : 800 x 600 x 8bit 彩色
; 0x105 : 1024 x 768 x 8bit 彩色
; 0x107 : 1280 x 1024 x 8bit 彩色
BOTPAK EQU 0x00280000 ; 加載bootpack
DSKCAC EQU 0x00100000 ; 磁盤緩存的位置
DSKCAC0 EQU 0x00008000 ; 磁盤緩存的位置(實模式)
; BOOT_INFO 相關
CYLS EQU 0x0ff0 ; 引導扇區(qū)設置
LEDS EQU 0x0ff1
VMODE EQU 0x0ff2 ; 關于顏色的信息
SCRNX EQU 0x0ff4 ; 分辨率X
SCRNY EQU 0x0ff6 ; 分辨率Y
VRAM EQU 0x0ff8 ; 圖像緩沖區(qū)的起始地址
ORG 0xc200 ; 這個的程序要被裝載的內存地址
; 確認VBE是否存在
MOV AX,0x9000
MOV ES,AX
MOV DI,0
MOV AX,0x4f00
INT 0x10
CMP AX,0x004f
JNE scrn320
; 檢查VBE的版本
MOV AX,[ES:DI+4]
CMP AX,0x0200
JB scrn320 ; if (AX < 0x0200) goto scrn320
; 取得畫面模式信息
MOV CX,VBEMODE
MOV AX,0x4f01
INT 0x10
CMP AX,0x004f
JNE scrn320
; 畫面模式信息的確認
CMP BYTE [ES:DI+0x19],8 ;顏色數(shù)必須為8
JNE scrn320
CMP BYTE [ES:DI+0x1b],4 ;顏色的指定方法必須為4(4是調色板模式)
JNE scrn320
MOV AX,[ES:DI+0x00] ;模式屬性bit7不是1就不能加上0x4000
AND AX,0x0080
JZ scrn320 ; 模式屬性的bit7是0,所以放棄
; 畫面設置
MOV BX,VBEMODE+0x4000
MOV AX,0x4f02
INT 0x10
MOV BYTE [VMODE],8 ; 屏幕的模式(參考C語言的引用)
MOV AX,[ES:DI+0x12]
MOV [SCRNX],AX
MOV AX,[ES:DI+0x14]
MOV [SCRNY],AX
MOV EAX,[ES:DI+0x28] ;VRAM的地址
MOV [VRAM],EAX
JMP keystatus
scrn320:
MOV AL,0x13 ; VGA圖、320x200x8bit彩色
MOV AH,0x00
INT 0x10
MOV BYTE [VMODE],8 ; 記下畫面模式(參考C語言)
MOV WORD [SCRNX],320
MOV WORD [SCRNY],200
MOV DWORD [VRAM],0x000a0000
; 通過 BIOS 獲取指示燈狀態(tài)
keystatus:
MOV AH,0x02
INT 0x16 ; keyboard BIOS
MOV [LEDS],AL
; PIC關閉一切中斷
; 根據AT兼容機的規(guī)格,如果要初始化PIC,
; 必須在CLI之前進行,否則有時會掛起。
; 隨后進行PIC的初始化。
MOV AL,0xff
OUT 0x21,AL
NOP ; 如果連續(xù)執(zhí)行OUT指令,有些機種會無法正常運行
OUT 0xa1,AL
CLI ; 禁止CPU級別的中斷
; 為了讓CPU能夠訪問1MB以上的內存空間,設定A20GATE
CALL waitkbdout
MOV AL,0xd1
OUT 0x64,AL
CALL waitkbdout
MOV AL,0xdf ; enable A20
OUT 0x60,AL
CALL waitkbdout
; 切換到保護模式
[INSTRSET "i486p"] ; 說明使用486指令
LGDT [GDTR0] ; 設置臨時GDT
MOV EAX,CR0
AND EAX,0x7fffffff ; 設bit31為0(禁用分頁)
OR EAX,0x00000001 ; bit0到1轉換(保護模式過渡)
MOV CR0,EAX
JMP pipelineflush
pipelineflush:
MOV AX,1*8 ; 可讀寫的段 32bit
MOV DS,AX
MOV ES,AX
MOV FS,AX
MOV GS,AX
MOV SS,AX
; bootpack傳遞
MOV ESI,bootpack ; 轉送源
MOV EDI,BOTPAK ; 轉送目標
MOV ECX,512*1024/4
CALL memcpy
; 磁盤數(shù)據最終轉送到它本來的位置去
; 首先從啟動扇區(qū)開始
MOV ESI,0x7c00 ; 轉送源
MOV EDI,DSKCAC ; 轉送目標
MOV ECX,512/4
CALL memcpy
; 剩余的全部
MOV ESI,DSKCAC0+512 ; 轉送源
MOV EDI,DSKCAC+512 ; 轉送源目標
MOV ECX,0
MOV CL,BYTE [CYLS]
IMUL ECX,512*18*2/4 ; 從柱面數(shù)變換為字節(jié)數(shù)/4
SUB ECX,512/4 ; 減去 IPL 偏移量
CALL memcpy
; 必須由asmhead來完成的工作,至此全部完畢
; 以后就交由bootpack來完成
; bootpack啟動
MOV EBX,BOTPAK
MOV ECX,[EBX+16]
ADD ECX,3 ; ECX += 3;
SHR ECX,2 ; ECX /= 4;
JZ skip ; 沒有要轉送的東西時
MOV ESI,[EBX+20] ; 轉送源
ADD ESI,EBX
MOV EDI,[EBX+12] ; 轉送目標
CALL memcpy
skip:
MOV ESP,[EBX+12] ; 堆棧的初始化
JMP DWORD 2*8:0x0000001b
waitkbdout:
IN AL,0x64
AND AL,0x02
JNZ waitkbdout ; AND的結果如果不是0,就跳到waitkbdout
RET
memcpy:
MOV EAX,[ESI]
ADD ESI,4
MOV [EDI],EAX
ADD EDI,4
SUB ECX,1
JNZ memcpy ; 減法運算的結果如果不是0,就跳轉到memcpy
RET
; memcpy地址前綴大小
ALIGNB 16
GDT0:
RESB 8 ; 初始值
DW 0xffff,0x0000,0x9200,0x00cf ; 可以讀寫的段(segment)32bit
DW 0xffff,0x0000,0x9a28,0x0047 ; 可執(zhí)行的文件的32bit寄存器(bootpack用)
DW 0
GDTR0:
DW 8*3-1
DD GDT0
ALIGNB 16
bootpack:
3.bookpack.c(主函數(shù)文件,完成初始化等操作)
#include "bootpack.h"
#include <stdio.h>
//該結構體用于控制鼠標
struct MOUSE_DEC {
unsigned char buf[3], phase;
int x, y, btn;
};
extern struct FIFO8 keyfifo, mousefifo; //外部變量,定義在fifo.c文件中
void enable_mouse(struct MOUSE_DEC *mdec); //啟動鼠標的函數(shù)
void init_keyboard(void); //初始化鍵盤
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat); //處理鼠標信息
void HariMain(void) //主函數(shù)
{
struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO; //啟動信息數(shù)據結構
char s[40], mcursor[256], keybuf[32], mousebuf[128];
int mx, my, i;
struct MOUSE_DEC mdec;
init_gdtidt(); //初始化gdt.idt
init_pic(); //初始化pic
io_sti(); //執(zhí)行STI指令
fifo8_init(&keyfifo, 32, keybuf); //初始化鍵盤緩存區(qū)
fifo8_init(&mousefifo, 128, mousebuf); //初始化鼠標緩存區(qū)
io_out8(PIC0_IMR, 0xf9); //設置中斷
io_out8(PIC1_IMR, 0xef); //因為鍵盤中斷是IRQ1,鼠標中斷時IRQ12,所以需要打開主從電路上的對應管腳
init_keyboard(); //初始化鍵盤
init_palette(); //初始化調色板
init_screen8(binfo->vram, binfo->scrnx, binfo->scrny); //初始化屏幕,形成最初的窗口界面
//獲取畫面中央的坐標
mx = (binfo->scrnx - 16) / 2;
my = (binfo->scrny - 28 - 16) / 2;
init_mouse_cursor8(mcursor, COL8_008484); //鼠標光標的顯示
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
sprintf(s, "(%3d, %3d)", mx, my); //將鼠標位置轉換成字符串
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s); //顯示字符串,這個函數(shù)的位置在哪里?
enable_mouse(&mdec); //啟動鼠標
for (;;) {
io_cli(); //關閉中斷
//如果鍵盤緩沖區(qū)和鼠標緩沖區(qū)中都沒有數(shù)據
if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
io_stihlt(); //打開中斷并執(zhí)行hlt命令
} else {
//如果鍵盤緩存區(qū)中有數(shù)據
if (fifo8_status(&keyfifo) != 0) {
i = fifo8_get(&keyfifo); //從緩存區(qū)中讀取數(shù)據(FIFO)
sprintf(s, "%02X", i); //將數(shù)據已字符串形式輸出
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 16, 15, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 16, COL8_FFFFFF, s);
} else if (fifo8_status(&mousefifo) != 0) { //如果鼠標緩存區(qū)中有函數(shù)(鼠標和鍵盤的數(shù)據是怎么存入到對應緩存區(qū)的?)
i = fifo8_get(&mousefifo); //從緩存區(qū)中讀取數(shù)據
io_sti(); //打開中斷
if (mouse_decode(&mdec, i) != 0) { //對鼠標信息進行處理
sprintf(s, "[lcr %4d %4d]", mdec.x, mdec.y);
if ((mdec.btn & 0x01) != 0) {
s[1] = 'L';
}
if ((mdec.btn & 0x02) != 0) {
s[3] = 'R';
}
if ((mdec.btn & 0x04) != 0) {
s[2] = 'C';
}
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 32, 16, 32 + 15 * 8 - 1, 31);
putfonts8_asc(binfo->vram, binfo->scrnx, 32, 16, COL8_FFFFFF, s);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, mx, my, mx + 15, my + 15);
mx += mdec.x;
my += mdec.y;
if (mx < 0) {
mx = 0;
}
if (my < 0) {
my = 0;
}
if (mx > binfo->scrnx - 16) {
mx = binfo->scrnx - 16;
}
if (my > binfo->scrny - 16) {
my = binfo->scrny - 16;
}
sprintf(s, "(%3d, %3d)", mx, my);
boxfill8(binfo->vram, binfo->scrnx, COL8_008484, 0, 0, 79, 15);
putfonts8_asc(binfo->vram, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
putblock8_8(binfo->vram, binfo->scrnx, 16, 16, mx, my, mcursor, 16);
}
}
}
}
}
#define PORT_KEYDAT 0x0060
#define PORT_KEYSTA 0x0064
#define PORT_KEYCMD 0x0064
#define KEYSTA_SEND_NOTREADY 0x02
#define KEYCMD_WRITE_MODE 0x60
#define KBC_MODE 0x47
//功能:等待鍵盤控制電路準備完畢
//如果鍵盤控制電路可以接受CPU指令,CPU從設備號碼0x0064處所讀取的數(shù)據倒數(shù)第二位應該是0,否則就是一直循環(huán)等待
void wait_KBC_sendready(void)
{
for (;;) {
if ((io_in8(PORT_KEYSTA) & KEYSTA_SEND_NOTREADY) == 0) { //判斷第二位的情況
break;
}
}
return;
}
//功能:初始化鍵盤
void init_keyboard(void)
{
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_WRITE_MODE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, KBC_MODE);
return;
}
#define KEYCMD_SENDTO_MOUSE 0xd4
#define MOUSECMD_ENABLE 0xf4
//功能:啟用鼠標
void enable_mouse(struct MOUSE_DEC *mdec)
{
wait_KBC_sendready();
io_out8(PORT_KEYCMD, KEYCMD_SENDTO_MOUSE);
wait_KBC_sendready();
io_out8(PORT_KEYDAT, MOUSECMD_ENABLE);
mdec->phase = 0;
return;
}
//處理鼠標信息
int mouse_decode(struct MOUSE_DEC *mdec, unsigned char dat)
{
if (mdec->phase == 0) {
if (dat == 0xfa) {
mdec->phase = 1;
}
return 0;
}
if (mdec->phase == 1) {
if ((dat & 0xc8) == 0x08) {
mdec->buf[0] = dat;
mdec->phase = 2;
}
return 0;
}
if (mdec->phase == 2) {
mdec->buf[1] = dat;
mdec->phase = 3;
return 0;
}
if (mdec->phase == 3) {
mdec->buf[2] = dat;
mdec->phase = 1;
mdec->btn = mdec->buf[0] & 0x07;
mdec->x = mdec->buf[1];
mdec->y = mdec->buf[2];
if ((mdec->buf[0] & 0x10) != 0) {
mdec->x |= 0xffffff00;
}
if ((mdec->buf[0] & 0x20) != 0) {
mdec->y |= 0xffffff00;
}
mdec->y = - mdec->y;
return 1;
}
return -1;
}
4.dsctbl.c(gdt和idt設置)
#include "bootpack.h"
//初始化gdt和idt
void init_gdtidt(void)
{
struct SEGMENT_DESCRIPTOR *gdt = (struct SEGMENT_DESCRIPTOR *) ADR_GDT; //提前設置好的GDT在內存中的地址
struct GATE_DESCRIPTOR *idt = (struct GATE_DESCRIPTOR *) ADR_IDT; //提前設置好的IDT在內存中的地址
int i;
for (i = 0; i <= LIMIT_GDT / 8; i++) { //對所有的全局描述符進行初始化
set_segmdesc(gdt + i, 0, 0, 0); //先將所有的全局描述符設置為0
}
set_segmdesc(gdt + 1, 0xffffffff, 0x00000000, AR_DATA32_RW); //設置第1號描述符,段基址為0,大小4gb,可讀寫32位段
set_segmdesc(gdt + 2, LIMIT_BOTPAK, ADR_BOTPAK, AR_CODE32_ER); //設置第2號描述符,段基址0x00280000,大小0x0007ffff,屬性0x4092
load_gdtr(LIMIT_GDT, ADR_GDT); //載入gdtr到cpu中
for (i = 0; i <= LIMIT_IDT / 8; i++) { //對所有的idt描述符進行初始化
set_gatedesc(idt + i, 0, 0, 0); //先將所有的idt描述符設為0
}
load_idtr(LIMIT_IDT, ADR_IDT); //載入idtr到cpu中
set_gatedesc(idt + 0x21, (int) asm_inthandler21, 2 * 8, AR_INTGATE32); //對idt描述符賦值,注意第二個變量,是偏移量
set_gatedesc(idt + 0x27, (int) asm_inthandler27, 2 * 8, AR_INTGATE32);
set_gatedesc(idt + 0x2c, (int) asm_inthandler2c, 2 * 8, AR_INTGATE32);
return;
}
//功能:對段描述符賦值
//參數(shù):段描述符地址,長度、基址、屬性值
void set_segmdesc(struct SEGMENT_DESCRIPTOR *sd, unsigned int limit, int base, int ar)
{
//判斷段描述大小的計數(shù)單位
if (limit > 0xfffff) { //如果段界限超過了限制1MB
ar |= 0x8000; /* G_bit = 1 */ //將G位設置為1,即大小以Kb為單位
limit /= 0x1000; //換算成KB
}
sd->limit_low = limit & 0xffff; //從低16位開始設置,即段界限的低16位
sd->base_low = base & 0xffff; //接著設置16-31的16位數(shù)據,即基地址的低16位
sd->base_mid = (base >> 16) & 0xff; //設置32-39的8位數(shù)據,即基地址的中間8位,將base右移16位,然后做與運算,取出中間8位
sd->access_right = ar & 0xff; //設置40-47位,即ar的第八位
sd->limit_high = ((limit >> 16) & 0x0f) | ((ar >> 8) & 0xf0); //設置49-56位,limit_high比較特殊,其中四位是段界限,4位是段屬性值
sd->base_high = (base >> 24) & 0xff; //設置57-63位,即段基址的高8位
return;
}
//功能:設置門描述符
//參數(shù):門描述符地址,偏移、段選擇符,屬性值
void set_gatedesc(struct GATE_DESCRIPTOR *gd, int offset, int selector, int ar)
{
gd->offset_low = offset & 0xffff; //設置0-15位,偏移地址的低16位
gd->selector = selector; //設置16-31位,制度段選擇子
gd->dw_count = (ar >> 8) & 0xff; //設置32-39位,基本上全是0
gd->access_right = ar & 0xff; //設置40-47位,門描述符屬性
gd->offset_high = (offset >> 16) & 0xffff; //設置48-63Wie,偏移地址的高16位
return;
}
5.graphic.c
//用于處理屏幕顯示
#include "bootpack.h"
//初始化調色板
void init_palette(void)
{
static unsigned char table_rgb[16 * 3] = { //設置調色板變量,這里3個字符一組,組成了一個顏色,顏色應該是計算機中已經設定好的
0x00, 0x00, 0x00,
0xff, 0x00, 0x00,
0x00, 0xff, 0x00,
0xff, 0xff, 0x00,
0x00, 0x00, 0xff,
0xff, 0x00, 0xff,
0x00, 0xff, 0xff,
0xff, 0xff, 0xff,
0xc6, 0xc6, 0xc6,
0x84, 0x00, 0x00,
0x00, 0x84, 0x00,
0x84, 0x84, 0x00,
0x00, 0x00, 0x84,
0x84, 0x00, 0x84,
0x00, 0x84, 0x84,
0x84, 0x84, 0x84
};
set_palette(0, 15, table_rgb); //設置調色板
return;
}
//功能:設置調色板,將顏色和編號對上
void set_palette(int start, int end, unsigned char *rgb)
{
int i, eflags;
eflags = io_load_eflags(); //匯編語言函數(shù)
io_cli(); //關閉中斷
io_out8(0x03c8, start); //寫入端口
for (i = start; i <= end; i++) { //每三個一組合成一個rgb顏色
io_out8(0x03c9, rgb[0] / 4);
io_out8(0x03c9, rgb[1] / 4);
io_out8(0x03c9, rgb[2] / 4);
rgb += 3;
}
io_store_eflags(eflags); //匯編語言函數(shù)
return;
}
//功能:畫一個窗口
//其中xsize表示窗口寬度,理論上應該等于x1-x0
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;
}
//初始化屏幕
void init_screen8(char *vram, int x, int y)
{
boxfill8(vram, x, COL8_008484, 0, 0, x - 1, y - 29);
boxfill8(vram, x, COL8_C6C6C6, 0, y - 28, x - 1, y - 28);
boxfill8(vram, x, COL8_FFFFFF, 0, y - 27, x - 1, y - 27);
boxfill8(vram, x, COL8_C6C6C6, 0, y - 26, x - 1, y - 1);
boxfill8(vram, x, COL8_FFFFFF, 3, y - 24, 59, y - 24);
boxfill8(vram, x, COL8_FFFFFF, 2, y - 24, 2, y - 4);
boxfill8(vram, x, COL8_848484, 3, y - 4, 59, y - 4);
boxfill8(vram, x, COL8_848484, 59, y - 23, 59, y - 5);
boxfill8(vram, x, COL8_000000, 2, y - 3, 59, y - 3);
boxfill8(vram, x, COL8_000000, 60, y - 24, 60, y - 3);
boxfill8(vram, x, COL8_848484, x - 47, y - 24, x - 4, y - 24);
boxfill8(vram, x, COL8_848484, x - 47, y - 23, x - 47, y - 4);
boxfill8(vram, x, COL8_FFFFFF, x - 47, y - 3, x - 4, y - 3);
boxfill8(vram, x, COL8_FFFFFF, x - 3, y - 24, x - 3, y - 3);
return;
}
//顯示字體
void putfont8(char *vram, int xsize, int x, int y, char c, char *font)
{
int i;
char *p, d /* data */;
for (i = 0; i < 16; i++) {
p = vram + (y + i) * xsize + x; //顯示版面的一行,此處應明白屏幕顯示原理
d = font[i]; //顯示字體,按位顯示
if ((d & 0x80) != 0) { p[0] = c; }
if ((d & 0x40) != 0) { p[1] = c; }
if ((d & 0x20) != 0) { p[2] = c; }
if ((d & 0x10) != 0) { p[3] = c; }
if ((d & 0x08) != 0) { p[4] = c; }
if ((d & 0x04) != 0) { p[5] = c; }
if ((d & 0x02) != 0) { p[6] = c; }
if ((d & 0x01) != 0) { p[7] = c; }
}
return;
}
void putfonts8_asc(char *vram, int xsize, int x, int y, char c, unsigned char *s)
{
extern char hankaku[4096];
for (; *s != 0x00; s++) {
putfont8(vram, xsize, x, y, c, hankaku + *s * 16);
x += 8;
}
return;
}
void init_mouse_cursor8(char *mouse, char bc)
{
static char cursor[16][16] = {
"**************..",
"*OOOOOOOOOOO*...",
"*OOOOOOOOOO*....",
"*OOOOOOOOO*.....",
"*OOOOOOOO*......",
"*OOOOOOO*.......",
"*OOOOOOO*.......",
"*OOOOOOOO*......",
"*OOOO**OOO*.....",
"*OOO*..*OOO*....",
"*OO*....*OOO*...",
"*O*......*OOO*..",
"**........*OOO*.",
"*..........*OOO*",
"............*OO*",
".............***"
};
int x, y;
for (y = 0; y < 16; y++) {
for (x = 0; x < 16; x++) {
if (cursor[y][x] == '*') {
mouse[y * 16 + x] = COL8_000000; //顯示鼠標
}
if (cursor[y][x] == 'O') {
mouse[y * 16 + x] = COL8_FFFFFF;
}
if (cursor[y][x] == '.') {
mouse[y * 16 + x] = bc;
}
}
}
return;
}
//功能:顯示背景
//vram和vxsize是關于vram的信息
//pxsize,pysize是想要顯示的圖形大小
//px0、py0制定圖像在畫面上的顯示位置
//buf指定圖形存放的地址
//bxsize指定每一行含有的像素數(shù)
void putblock8_8(char *vram, int vxsize, int pxsize,
int pysize, int px0, int py0, char *buf, int bxsize)
{
int x, y;
for (y = 0; y < pysize; y++) {
for (x = 0; x < pxsize; x++) {
vram[(py0 + y) * vxsize + (px0 + x)] = buf[y * bxsize + x];
}
}
return;
}
6.fifo.c
#include "bootpack.h"
#define FLAGS_OVERRUN 0x0001
//功能:初始化FIFO緩存區(qū)
//參數(shù):緩存區(qū)結構體,大小,緩存區(qū)地址
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;
}
//功能:向緩存區(qū)寫入數(shù)據
int fifo8_put(struct FIFO8 *fifo, unsigned char data)
{
if (fifo->free == 0) { //如果緩存區(qū)大小等于零,代表緩存區(qū)已經被寫滿
fifo->flags |= FLAGS_OVERRUN; //將覆蓋標志置1
return -1; //返回一個錯誤值
}
fifo->buf[fifo->p] = data; //讀取當前緩存區(qū)的第一個數(shù)據
fifo->p++; //將讀取指針后移
if (fifo->p == fifo->size) { //如果已經讀完緩存區(qū)
fifo->p = 0; //將讀取指針指向第一個位置
}
fifo->free--; //緩存區(qū)可用位置減1
return 0;
}
//功能:從緩存區(qū)讀取數(shù)據
int fifo8_get(struct FIFO8 *fifo)
{
int data;
if (fifo->free == fifo->size) { //如果緩存區(qū)是空的,返回錯誤
return -1;
}
data = fifo->buf[fifo->q]; //讀取數(shù)據
fifo->q++; //讀取指針后移
if (fifo->q == fifo->size) { //讀到最后一個將讀取指針指向第一個位置
fifo->q = 0;
}
fifo->free++; //可用區(qū)域加1 緩存區(qū)是循環(huán)寫入的
return data;
}
int fifo8_status(struct FIFO8 *fifo) //判斷緩存區(qū)的狀態(tài)
{
return fifo->size - fifo->free;
}
7. int.c
#include "bootpack.h"
#include <stdio.h>
//功能:中斷初始化函數(shù),初始化pic
void init_pic(void)
{
io_out8(PIC0_IMR, 0xff ); //主片禁止所有中斷
io_out8(PIC1_IMR, 0xff ); //從片禁止所有中斷
//設置pic0,主片
io_out8(PIC0_ICW1, 0x11 ); //邊沿觸發(fā)模式
io_out8(PIC0_ICW2, 0x20 ); //IRQ0-7由INT20-27接收
io_out8(PIC0_ICW3, 1 << 2); //PIC1由IRQ2接收
io_out8(PIC0_ICW4, 0x01 ); //無緩沖區(qū)模式
//設置pic1,從片
io_out8(PIC1_ICW1, 0x11 ); //邊沿觸發(fā)模式
io_out8(PIC1_ICW2, 0x28 ); //IRQ8-15由INT28-2f接收
io_out8(PIC1_ICW3, 2 ); //PIC1由IRQ2連接
io_out8(PIC1_ICW4, 0x01 ); //無緩沖區(qū)模式
io_out8(PIC0_IMR, 0xfb ); //11111011,PIC1以外的全部禁止
io_out8(PIC1_IMR, 0xff ); //11111111,禁止PIC1的所有中斷
return;
}
#define PORT_KEYDAT 0x0060
struct FIFO8 keyfifo;
//鍵盤中斷處理,鍵盤是IRQ1,所以編寫INT 0x21
//這里已經有了C語言編寫的函數(shù),為什么還要添加匯編語言的函數(shù)?
//是在匯編語言中調用該函數(shù)
void inthandler21(int *esp)
{
unsigned char data;
io_out8(PIC0_OCW2, 0x61);
data = io_in8(PORT_KEYDAT); //讀取數(shù)據
fifo8_put(&keyfifo, data); //將數(shù)據寫入緩存區(qū)
return;
}
struct FIFO8 mousefifo;
//功能:鼠標中斷處理,編寫INT 0x2c
void inthandler2c(int *esp)
{
unsigned char data;
io_out8(PIC1_OCW2, 0x64);
io_out8(PIC0_OCW2, 0x62);
data = io_in8(PORT_KEYDAT); //讀取數(shù)據
fifo8_put(&mousefifo, data); //將數(shù)據寫入緩存區(qū)
return;
}
void inthandler27(int *esp) */
{
io_out8(PIC0_OCW2, 0x67);
return;
}
8. naskfunc.nas(匯編和c語言文件之間的橋梁)
; naskfunc
; TAB=4
[FORMAT "WCOFF"] ;
[INSTRSET "i486p"] ;
[BITS 32] ;
[FILE "naskfunc.nas"] ;
;定義外部符號,可以從文件外進行調用
GLOBAL _io_hlt, _io_cli, _io_sti, _io_stihlt
GLOBAL _io_in8, _io_in16, _io_in32
GLOBAL _io_out8, _io_out16, _io_out32
GLOBAL _io_load_eflags, _io_store_eflags
GLOBAL _load_gdtr, _load_idtr
GLOBAL _asm_inthandler21, _asm_inthandler27, _asm_inthandler2c
EXTERN _inthandler21, _inthandler27, _inthandler2c
[SECTION .text]
_io_hlt: ; void io_hlt(void);
HLT
RET
_io_cli: ; void io_cli(void);
CLI
RET
_io_sti: ; void io_sti(void);
STI
RET
_io_stihlt: ; void io_stihlt(void);
STI
HLT
RET
_io_in8: ; int io_in8(int port); 從指定端口中讀取數(shù)據
MOV EDX,[ESP+4] ; port,獲取端口號
MOV EAX,0 ;清空ax寄存器
IN AL,DX ;從DX指定的端口中讀取數(shù)據到al
RET
_io_in16: ; int io_in16(int port);
MOV EDX,[ESP+4] ; port
MOV EAX,0
IN AX,DX
RET
_io_in32: ; int io_in32(int port);
MOV EDX,[ESP+4] ; port
IN EAX,DX
RET
_io_out8: ; void io_out8(int port, int data); 向指定端口寫入數(shù)據
MOV EDX,[ESP+4] ; port 獲取端口號
MOV AL,[ESP+8] ; data 獲取要寫入的數(shù)據
OUT DX,AL ;將al中的數(shù)據寫入到dx指定的端口中
RET
_io_out16: ; void io_out16(int port, int data);
MOV EDX,[ESP+4] ; port
MOV EAX,[ESP+8] ; data
OUT DX,AX
RET
_io_out32: ; void io_out32(int port, int data);
MOV EDX,[ESP+4] ; port
MOV EAX,[ESP+8] ; data
OUT DX,EAX
RET
_io_load_eflags: ; int io_load_eflags(void);
PUSHFD ; 將eflags寄存器壓入棧中,入棧順序是EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI
POP EAX ; 將edi的值彈出到eax中
RET
_io_store_eflags: ; void io_store_eflags(int eflags);
MOV EAX,[ESP+4]
PUSH EAX
POPFD ; 將棧中的寄存器值彈出到eflags寄存器中
RET
_load_gdtr: ; void load_gdtr(int limit, int addr);加載GDTR
MOV AX,[ESP+4] ; limit
MOV [ESP+6],AX
LGDT [ESP+6]
RET
_load_idtr: ; void load_idtr(int limit, int addr); 記載IDTR,原理與加載GDTR相同
MOV AX,[ESP+4] ; limit
MOV [ESP+6],AX
LIDT [ESP+6]
RET
;這個函數(shù)只是將寄存器的值保存在棧里,然后將DS和ES調整到與SS相等,再調用_inthandler21,返回后將所有寄存器的值再返回到原來的值,然后執(zhí)行IRETD
;之所以如此小心翼翼地保護寄存器,原因在于,中斷處理發(fā)生在函數(shù)處理途中,通過IREDT從中斷處理后,寄存器就亂了
_asm_inthandler21:
PUSH ES
PUSH DS
PUSHAD ;PUSHAD指令壓入32位寄存器,其入棧順序是:EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI .
MOV EAX,ESP ;eax=esp
PUSH EAX 壓入eax的值,即esp
MOV AX,SS
MOV DS,AX ;ds=ss
MOV ES,AX ;es=ss
CALL _inthandler21 ;調用inthandler21函數(shù),c語言編寫
POP EAX ;彈出esp的值到eax中
POPAD
POP DS
POP ES
IRETD
_asm_inthandler27:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler27
POP EAX
POPAD
POP DS
POP ES
IRETD
_asm_inthandler2c:
PUSH ES
PUSH DS
PUSHAD
MOV EAX,ESP
PUSH EAX
MOV AX,SS
MOV DS,AX
MOV ES,AX
CALL _inthandler2c
POP EAX
POPAD
POP DS
POP ES
IRETD
標題三、其它
根據書中的描述,整個項目的編譯過程如圖
最后,asmhead文件和bookpack文件會編譯到一起,bookpack的內容就是從bootpack標號開始?
關于中斷處理程序的調用
如果要讓一個中斷處理程序發(fā)揮作用,首先要將其注冊到idt中,書中使用了函數(shù)set_gatedesc(idt+0x21,(int)asm_inthandler21,2*8,AR_INTGATE32)2
即將_asm_inthandler21注冊為idt的第21號,如果發(fā)生中斷了,cpu就會自動調用asm_inthandler21文章來源:http://www.zghlxwxcb.cn/news/detail-806138.html
2表示asm_inthandler屬于那一個段,因低3位必須是0,所以寫成2*8
(ps:一定要讀源碼)
————————————————
版權聲明:本文為CSDN博主「qq_35041101」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權協(xié)議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_35041101/article/details/51866877文章來源地址http://www.zghlxwxcb.cn/news/detail-806138.html
到了這里,關于《30天自制操作系統(tǒng)》學習筆記(七)的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!