#define YCORG -1 //该设置使编译器生成头部为YCEXE结构的可执行文件
#include "ycio.cpp" //提供前缀为ycio_的函数和C/C++标准函数
ycfsCLASS *YCFS; //文件管理接口
ycttyCLASS *YCTTY; //显示和键盘接口
ycmmCLASS *YCMM; //内存管理接口
char kernel_stack[1024]; //内核堆栈
int isr_reenter; //中断重入
int process_count; //进程计数
YTSS ytss;
asm void main()
{
mov esp, &kernel_stack + sizeof(kernel_stack)
lgdt gdt_descr //装入全局段描述符
lidt idt_descr //装入中断段描述符
jmp os_init
}
//硬件中断服务程序宏定义
#define hard_ISR(hard_proc,irqNo) \
pushad \
push ds \
push es \
push fs \
push gs \
mov ax, ss \
mov ds, ax \
mov es, ax \
in al, 0x21 \
or al, (1 << irqNo) \
out 0x21, al /* 屏蔽中断 */ \
mov al, 0x20 \
out 0x20, al /* 设置EOI位 */ \
inc dword isr_reenter \
cmp dword isr_reenter, 0 \
je hir_1 \
sti \
call hard_proc \
cli \
jmp hir_2 \
hir_1: mov esp, &kernel_stack + sizeof(kernel_stack) \
sti \
call hard_proc \
cli \
mov esp, currentProcess \
lea eax, [esp + offsetof(YProcess, ldt_sel)] \
lldt [eax] \
mov ytss.esp0, eax \
hir_2: in al, 0x21 \
and al, ~(1 << irqNo) \
out 0x21, al /* 去屏蔽中断 */ \
dec dword isr_reenter \
pop gs \
pop fs \
pop es \
pop ds \
popad \
iret \
#define P_KRNL 0
#define P_USER 3
#define INDEX_TSS 3
void os_init()
{
printk(1,80*5,0x6f,"Setting Interrupt Server Routine (ISR)... - ycker.cpp");
set_idt(0x80, int_80_ISR, P_USER); //设置软中断80h的ISR
set_idt(0x20, align(16)asm[](){ hard_ISR(clock_proc,0) }, P_KRNL); //时钟ISR
set_idt(0x21, align(16)asm[](){ hard_ISR(keyboard_proc,1) },P_KRNL);//键盘ISR
set_idt(0x22, asm[]() { mov ecx,2; hard_ISR(dump_ISR,2) }, P_KRNL);
set_idt(0x23, asm[]() { mov ecx,3; hard_ISR(dump_ISR,3) }, P_KRNL);
set_idt(0x24, asm[]() { mov ecx,4; hard_ISR(dump_ISR,4) }, P_KRNL);
set_idt(0x25, asm[]() { mov ecx,5; hard_ISR(dump_ISR,5) }, P_KRNL);
set_idt(0x26, asm[]() { mov ecx,6; hard_ISR(dump_ISR,6) }, P_KRNL);
set_idt(0x27, asm[]() { mov ecx,7; hard_ISR(dump_ISR,7) }, P_KRNL);
set_idt(0x28, asm[]() { mov ecx,8; call dump_ISR; hlt }, P_KRNL);
set_idt(0x29, asm[]() { mov ecx,9; call dump_ISR; hlt }, P_KRNL);
set_idt(0x2a, asm[]() { mov ecx,10; call dump_ISR; hlt }, P_KRNL);
set_idt(0x2b, asm[]() { mov ecx,11; call dump_ISR; hlt }, P_KRNL);
set_idt(0x2c, asm[]() { mov ecx,12; call dump_ISR; hlt }, P_KRNL);
set_idt(0x2d, asm[]() { mov ecx,13; call dump_ISR; hlt }, P_KRNL);
set_idt(0x2e, asm[]() { mov ecx,14; call dump_ISR; hlt }, P_KRNL);
set_idt(0x2f, asm[]() { mov ecx,15; call dump_ISR; hlt }, P_KRNL);
set_idt(0, asm[]() { push 0; call exception; }, P_KRNL); //设置异常中断
set_idt(1, asm[]() { push 1; call exception; }, P_KRNL);
set_idt(2, asm[]() { push 2; call exception; }, P_KRNL);
set_idt(3, asm[]() { push 3; call exception; }, P_USER);
set_idt(4, asm[]() { push 4; call exception; }, P_USER);
set_idt(5, asm[]() { push 5; call exception; }, P_KRNL);
set_idt(6, asm[]() { push 6; call exception; }, P_KRNL);
set_idt(7, asm[]() { push 7; call exception; }, P_KRNL);
set_idt(8, asm[]() { push 8; call exception; }, P_KRNL);
set_idt(9, asm[]() { push 9; call exception; }, P_KRNL);
set_idt(10, asm[]() { push 10; call exception; }, P_KRNL);
set_idt(11, asm[]() { push 11; call exception; }, P_KRNL);
set_idt(12, asm[]() { push 12; call exception; }, P_KRNL);
set_idt(13, asm[]() { push 13; call exception; }, P_KRNL);
set_idt(14, asm[]() { push 14; call exception; }, P_KRNL);
set_idt(15, asm[]() { push 15; call exception; }, P_KRNL);
set_descriptor((YDescriptor*)&Ygdt[INDEX_TSS],(int)&ytss,sizeof ytss,0x89);
printk(1,80*6,0x4f,"Loading drivers: ycfs ycmm yctty - ycker.cpp");
ycio_set_driver((byte*)(KERNEL_POS + *(int*)(KERNEL_POS+DATA_POS+0)),"ycfs");
ycio_get_driver(&YCFS,"ycfs"); //获得文件管理函数接口YCFS
ycio_set_driver((byte*)(KERNEL_POS + *(int*)(KERNEL_POS+DATA_POS+4)),"ycmm");
ycio_get_driver(&YCMM,"ycmm"); //获得内存管理函数接口YCMM
ycio_set_driver(YCFS->get_file_code("yctty.sys"), "yctty"); //安装yctty驱动
ycio_get_driver(&YCTTY,"yctty"); //获得显示和键盘管理函数接口YCTTY
auto enable_irq = [](int irqNo)
{
short dx = irqNo < 8 ? 0x21 : 0xa1;
byte al = inb(dx) & ~(1 << (irqNo % 8));
outb(dx,al);
};
for(int ii=0; ii<16; ii++) enable_irq(ii); //使能16个硬件中断
ycio_create_process(YCFS->get_file_code("ycshell.sys")); //创建shell进程
printk(1,80*7,0x2f,"Going to shell ...... - ycker.cpp");
asm
{
mov esp, &procTable //shell进程结构数据地址
lea eax, [esp + offsetof(YProcess, ldt_sel)]
lldt [eax] //装入shell进程的段描述符
mov ytss.esp0, eax
mov word ytss.ss0, KERNEL_DS //外层到内层的 ss:esp
mov ax, 8 * INDEX_TSS
ltr ax
//以下代码使程序进入 ycshell.cpp 的 main() 函数
dec dword isr_reenter //eax = procTable[0].eax
pop gs //ebx = procTable[0].ebx
pop fs
pop es //ss = procTable[0].ss
pop ds //esp = procTable[0].esp
popad //cs = procTable[0].cs
iret //eip = procTable[0].eip = main() of ycshell.cpp
}
}
void set_idt(byte irqNo,void (*handler)(), byte privilege)
{
struct YGATE
{
unsigned short offset_low;
unsigned short selector;
byte dcount;
byte attr;
unsigned short offset_high;
};
YGATE *pGate = (YGATE*) &Yidt [ irqNo ];
pGate->offset_low = (int)handler & 0xffff;
pGate->selector = KERNEL_CS;
pGate->dcount = 0;
pGate->attr = 0x8e | (privilege << 5); //DA_IGate
pGate->offset_high = ((int)handler >> 16) & 0xffff;
}
void clock_proc()
{
currentProcess->m_ticks--;
if(isr_reenter) return;
if(currentProcess->m_ticks > 0) return;
int greatest_ticks = 0;
while(1)
{
YProcess *pProc, *endProc = &procTable[process_count];
for(pProc=procTable; pProc<endProc; pProc++)
{
if(pProc->m_ticks <= greatest_ticks) continue;
greatest_ticks = pProc->m_ticks;
currentProcess = pProc;
}
if(greatest_ticks) break;
for(pProc=procTable; pProc<endProc; pProc++)
pProc->m_ticks = pProc->priority;
}
}
void keyboard_proc()
{
YCTTY->keyboard_proc();
}
struct
{
char name[36];
void *pObj;
} drTable[256];
int drCnt;
int sys_set_driver(byte *drvSrc,char *drvName) //安装驱动程序
{
if(strlen(drvName) >= sizeof(drTable[0].name)) return 2;
if(sys_get_driver(nullptr,drvName)) return 1; //该驱动程序已被安装
if(drCnt >= sizeof drTable/sizeof drTable[0]) return 3; //超出最大数,不能安装
if(!drvSrc) return 4; //驱动代码地址为0
int bb = drvSrc[0]==0x68 ? 1 : drvSrc[0]==0xe8 && drvSrc[5]==0x68 ? 6 : 0;
if(bb)
{
*(void**)&drvSrc[bb] = &drTable[drCnt].pObj; //设置main(pObj)的pObj参数
}
else
{
if(*(short*)drvSrc != *(short*)"YC") return 5;
YCEXE *pYcexe = (YCEXE*)drvSrc; //驱动代码文件头结构
YMEM *pLink = YCMM->malloc(currentProcess->pHead,pYcexe->codelen,true);
if(!pLink) return 5;
drvSrc = to_memPtr(pLink);
code_copy(drvSrc,pYcexe,&drTable[drCnt].pObj);//驱动代码重定位拷贝到内存
}
strcpy(drTable[drCnt].name, drvName);
drCnt++;
((void(*)())drvSrc)(); //运行驱动程序中的main()函数
return 0;
}
int sys_get_driver(void *pObj,char *drvName) //获取驱动程序函数接口指针
{
for(int ii=0; ii<drCnt; ii++)
{
if(*(int*)drTable[ii].name != *(int*)drvName) continue;
if(strcmp(drTable[ii].name,drvName)) continue;
if(pObj) *(void**)pObj = drTable[ii].pObj;
return 1;
}
if(pObj) *(void**)pObj = nullptr;
return 0;
}
void sys_write(int *pdata) //写字符串到屏幕
{
YCTTY->sys_write(pdata); //调用yctty.cpp函数
}
void sys_clear_screen(int bpos,int len) //清屏
{
YCTTY->clear_screen(bpos,len); //调用yctty.cpp函数
}
unsigned int sys_create_process(byte *codeptr,int *pdata) //创建进程
{
YProcess *pProc = procTable; //进程表头部
YProcess *endProc = &procTable[process_count]; //进程表尾部
//查找空闲进程表
for(; pProc<endProc; pProc++) if(!pProc->eip) break;
if(pProc >= endProc)
{
if(process_count >= sizeof(procTable)/sizeof(procTable[0])) return 0;
pProc = &procTable[process_count++];
}
static unsigned int proc_id = 0;
//申请内存 ,设置进程内存链表
YCEXE *pYcexe = (YCEXE*)codeptr;
int stackSize = pYcexe->stacksize;
pProc->pHead = YCMM->malloc(nullptr,stackSize + pYcexe->codelen + 32 +
pdata[1] + 1 + 32*sizeof(char*));
if(!pProc->pHead) return 0;
pProc->pHead->pLast = pProc->pHead->pNext = pProc->pHead;
//设置删除进程代码 - yc_delete_process()
codeptr = to_memPtr(pProc->pHead) + stackSize;
codeptr[ 0] = 0xe8; *(int*)&codeptr[1] = 32 - 5; //call 32
codeptr[ 5] = 0xb8 + 0; *(int*)&codeptr[6] = 4; //mov eax,4
codeptr[10] = 0xb8 + 1; *(int*)&codeptr[11] = 0; //mov ecx,0
codeptr[15] = 0xcd; codeptr[16] = 0x80; //int 80h
codeptr[17] = 0xc3; //ret
//设置命令行参数
pProc->CommandLine = (char*)codeptr + pYcexe->codelen + 32;
memcpy(pProc->CommandLine,(char*)pdata[0],pdata[1]);
pProc->CommandLine[pdata[1]] = 0;
char **argv = (char**)&pProc->CommandLine[pdata[1] + 1];
int argc = getArgA(pProc->CommandLine,argv,32);
static char *envp[] = { "PATH=C:/", "include=c:/include", nullptr };
//重定位拷贝代码到内存。code_copy()位于ycio.cpp
code_copy(&codeptr[32],pYcexe,(void*)argc,argv,envp);
pProc->pid = proc_id++;
if(proc_id == 0) proc_id++;
memcpy(&pProc->ldts[0], &Ygdt[KERNEL_CS / 8], sizeof(YDescriptor));
pProc->ldts[0].attr1 = 0x98 | (P_USER << 5); //局部描述符属性=c,特权级=3
memcpy(&pProc->ldts[1], &Ygdt[KERNEL_DS / 8], sizeof(YDescriptor));
pProc->ldts[1].attr1 = 0x92 | (P_USER << 5); //局部描述符属性=r/w,特权级=3
pProc->cs = (8 * 0) | 4 | P_USER; //设置进程代码选择子和特权级
pProc->ds =
pProc->es =
pProc->fs =
pProc->gs =
pProc->ss = (8 * 1) | 4 | P_USER; //设置进程数据堆栈选择子和特权级
pProc->esp = (unsigned int)to_memPtr(pProc->pHead) + stackSize; //进程堆栈
pProc->eip = (unsigned int)codeptr; //进程执行代码
pProc->eflags = 0x0202;
int dPos = INDEX_TSS + 1 + pProc - procTable;
pProc->ldt_sel = 8 * dPos;
pProc->m_ticks = pProc->priority = 5;
set_descriptor((YDescriptor*)&Ygdt[dPos],(int)pProc->ldts,
sizeof pProc->ldts,0x82);
return pProc->pid;
}
void sys_delete_process(int procID) //删除进程
{
if(!procID) procID = currentProcess->pid; //进程号
YProcess *endProc = &procTable[process_count]; //进程表尾
for(YProcess *pProc=procTable; pProc<endProc; pProc++) //遍历进程表查找
{
if(pProc->pid != procID) continue;
pProc->eip = pProc->m_ticks = pProc->priority = 0;
clock_proc();
yc_run_link(pProc->pHead, [](YMEM *pLink)
{
YCMM->free(pLink); //释放进程所占内存
});
YCTTY->wr_prompt(); //显示提示符
break;
}
}
void set_descriptor(YDescriptor *pDesc, int baddr, int limit, short attrV)
{
pDesc->limit_low = limit & 0x0ffff;
pDesc->base_low = baddr & 0x0ffff;
pDesc->base_mid = (baddr >> 16) & 0xff;
pDesc->attr1 = attrV & 0xff;
pDesc->limit_high_attr2 = ((limit >> 16) & 0x0f) | (attrV >> 8) & 0xf0;
pDesc->base_high = (baddr >> 24) & 0xff;
}
int sys_get_current_process()
{
return currentProcess->pid;
}
void *sys_malloc(unsigned int _Size)
{
YMEM *pLink = YCMM->malloc(currentProcess->pHead,_Size);
if(!pLink) return nullptr;
yc_add_link(pLink,currentProcess->pHead);
return to_memPtr(pLink);
}
void *sys_realloc(void *_Memory,unsigned int _NewSize)
{
if(!_Memory) return sys_malloc(_NewSize);
YMEM *oldLink = to_memLink(_Memory);
YMEM *pLink = YCMM->realloc(oldLink,_NewSize);
if(pLink != oldLink)
{
if(!pLink) return nullptr;
yc_add_link(pLink,currentProcess->pHead);
sys_free(_Memory);
}
return to_memPtr(pLink);
}
void sys_free(void *_Memory)
{
if(!_Memory) return;
YMEM *pLink = to_memLink(_Memory);
YCMM->free(pLink);
yc_delete_link(pLink,currentProcess->pHead);
}
void sys_cursor(int curpos)
{
YCTTY->sys_cursor(curpos);
}
void *sys_call[] = { sys_write, //0
sys_set_driver, //1
sys_get_driver, //2
sys_create_process, //3
sys_delete_process, //4
sys_get_current_process, //5
sys_malloc, //6
sys_realloc, //7
sys_free, //8
sys_cursor, //9
sys_clear_screen, //10
};
align(16)
asm void int_80_ISR()
{
pushad
push ds
push es
push fs
push gs
mov bx, ss
mov ds, bx
mov es, bx
mov esi, esp //esi = 进程结构地址
inc dword isr_reenter
cmp dword isr_reenter, 0
jne sy_1
mov esp, &kernel_stack + sizeof(kernel_stack) //切换到内核栈
sy_1: sti
push edx
push ecx
call dword [&sys_call + eax * 4] //call 系统调用子程序(ecx,edx)
add esp, 4 * 2
mov [esi + offsetof(YProcess, eax)], eax //返回值
cli
cmp dword isr_reenter, 0
jne sy_2
mov esp, currentProcess
lea eax, [esp + offsetof(YProcess, ldt_sel)]
lldt [eax]
mov ytss.esp0, eax
sy_2: dec dword isr_reenter
pop gs
pop fs
pop es
pop ds
popad
iret
}
void dump_ISR()
{
int irq;
asm { mov irq, ecx; }
printk(1,80*2,0x74,"dump_ISR: %d %s",irq,irq>=8 ? "hlt..." : "");
}
void exception(int irqNo, int eip, int cs, int eflags)
{
char *msg2[] = {"#DE Divide Error",
"#DB RESERVED",
"— NMI Interrupt",
"#BP Breakpoint",
"#OF Overflow",
"#BR BOUND Range Exceeded",
"#UD Invalid Opcode (Undefined Opcode)",
"#NM Device Not Available (No Math Coprocessor)",
"#DF Double Fault",
" Coprocessor Segment Overrun (reserved)",
"#TS Invalid TSS",
"#NP Segment Not Present",
"#SS Stack-Segment Fault",
"#GP General Protection",
"#PF Page Fault",
"— (Intel reserved. Do not use.)",
"#MF x87 FPU Floating-Point Error (Math Fault)",
"#AC Alignment Check",
"#MC Machine Check",
"#XF SIMD Floating-Point Exception" };
printk(1,80*3,0x74,`
interrupt Error: %d - %s
eFlags: %p
CS:EIP %x:%p`, irqNo, msg2[irqNo], eflags, cs, eip);
while(1);
}
YProcess procTable[1024];
YProcess *currentProcess = procTable;
align(8)
int64 Ygdt[1024] = {0, 0x00cf9a000000ffff, //大小=4G 基址=0 rd/exec代码段描述符
0x00cf92000000ffff}; //大小=4G 基址=0 rd/wr数据段描述符
int64 Yidt[129];
align(4) GDT_PTR idt_descr = { sizeof Yidt, (char*)Yidt };
align(4) GDT_PTR gdt_descr = { sizeof Ygdt-1, (char*)Ygdt };
C/C++代码文件: ycker.cpp
源代码分析
语句set_idt(0x80, DA_IGate, int_80_ISR, P_USER)将int_80_ISR()函数设置为int 80h中断服务程序,服务程序提供sys_set_driver,sys_create_process,sys_malloc等9个内核功能。80h中断是应用程序(也叫用户进程)调用系统内核的唯一接口。
语句set_idt(0x20, DA_IGate, align(16)asm { hard_ISR(clock_proc,0) }, P_KRNL)设置时钟中断服务程序:
align(16)asm { hard_ISR(clock_proc,0) }
它是一个匿名的汇编函数,起始地址16字节对齐。hard_ISR是宏命令:
#define hard_ISR(hard_proc,irqNo)
其中汇编语句 mov esp,currentProcess 使堆栈指向当前进程指针:currentProcess,查看其结构定义可知,执行下列语句:
pop gs
pop fs
pop es
pop ds
popad
iret
后,CPU的寄存器之值改变为:
eax = currentProcess->eax
ebx = currentProcess->ebx
……
eip = currentProcess->eip
cs = currentProcess->cs
eflags = currentProcess->eflags
esp = currentProcess->esp
ss = currentProcess->ss
可见中断程序退出后便进入了currentProcess->eip指向的代码。
如果在执行时钟中断程序时按顺序让currentProcess指向不同的进程,就可以实现进程之间的切换,也就是同时执行多个应用程序。
语句set_idt(0x21,DA_IGate, align(16)asm { hard_ISR(keyboard_proc,1) }, P_KRNL)
设置键盘中断服务程序。其中函数:void keyboard_proc() { YCTTY->keyboard_proc(); }
调用了yctty.cpp中的接口函数。
int ycfs_pos = (int)(KERNEL_POS + DATA_POS + 0)获得在ychead.cpp设置的ycfs.cpp的代码位置
yc_set_driver((byte*)(KERNEL_POS + ycfs_pos),“ycfs”)设置驱动程序:ycfs.cpp。查看ycio.cpp可知,yc_set_driver调用了内核接口1号功能:
mov eax, 1
int 80h
从数组sys_call可知,内核接口1号功能调用sys_set_driver()函数。
语句yc_get_driver(&YCFS,“ycfs”)获得ycfs.cpp的接口指针YCFS,通过YCFS可调用ycfs.cpp中的函数。
语句yc_get_driver(&YCMM,“ycmm”)获得ycmm.cpp接口YCMM
语句YCFS->get_code(“yctty.sys”), “yctty”)用YCFS接口从ycfs.cpp中获得驱动程序代码:yctty.sys
语句yc_set_driver(YCFS->get_code(“yctty.sys”), “yctty”)设置驱动程序yctty.cpp
语句yc_create_process(YCFS->get_code(“ycshell.exe”))创建进程执行shell程序,它是0号进程,其进程数据为procTable[0]
汇编语句mov esp,&procTable将ycshell.exe进程数据地址放入esp,
语句lea eax,[esp + offsetof(YProcess,ldt_sel)]将进程的局部段描述符procTable[0].ldt_sel的地址放入eax,
语句lldt [eax]装入进程的局部段描述符。
执行下列代码:
pop gs
pop fs
pop es
pop ds
popad
iret
后,CPU的指令寄存器eip = procTable[0].eip, 而procTable[0].eip 已被设置为 ycshell.sys代码的首地址,故程序便进入ycshell.cpp的main()函数。
每次时钟中断都要执行clock_proc()函数,该函数检查当前正在等待运行的process_count个进程,按一定的规则用语句currentProcess = pProc把其中一个进程设置为当前进程,以便退出中断时切换到该进程。
每次按键中断都要执行keyboard_proc()函数,该函数通过语句YCTTY->keyboard_proc()调用yctty.cpp的ycttyCLASS::keyboard_proc()函数。
sys_set_driver(byte *drvSrc,char *drvName)函数安装驱动程序,应用程序通过软件中断 int 80h 调用它。
sys_get_driver(void *pObj,char *drvName)函数获得驱动程序接口地址,应用程序通过软件中断 int 80h 调用它。