为了介绍如何用YC编译器开发操作系统,作者设计并实现了一个简单的操作系统:YCOS。下面对YCOS源码进行详细分析和解释。
1. YCOS镜像构建代码ycos.cpp
#include "ycos.h"
#define L(_String) L ## _String
#define yc_assert(EP) ((EP)?((void)(_wassert(L###EP,L(__FILE__),__LINE__),0)):0)
void main()
{
char *boot_buf,*head_buf,*ker_buf,*fs_buf,*mm_buf;
int boot_len = YC_cppCompile(&boot_buf,"ycboot.cpp"); //编译引导代码
int head_len = YC_cppCompile(&head_buf,"ychead.cpp"); //编译初始化代码
int ker_len = YC_cppCompile(&ker_buf,"ycker.cpp"); //编译内核代码
create_exp(); //编译驱动和应用程序
int fs_len = YC_cppCompile(&fs_buf,"ycfs.cpp"); //编译文件管理代码
int mm_len = YC_cppCompile(&mm_buf,"ycmm.cpp"); //编译内存管理代码
yc_assert(!boot_len); //引导代码语法错误检查
yc_assert(boot_len > ycboot_SIZE); //引导代码长度错误检查
yc_assert(!head_len); //初始化代码语法错误检查
yc_assert(head_len > ychead_SIZE); //初始化代码长度错误检查
yc_assert(!ker_len); //内核代码语法错误检查
yc_assert(!fs_len); //文件管理代码语法错误检查
yc_assert(!mm_len); //内存管理代码语法错误检查
yc_assert(ycker_SIZE < ychead_SIZE + ker_len + fs_len + mm_len);
yc_assert(ycker_SIZE < ((YCEXE*)ker_buf)->codelen+((YCEXE*)fs_buf)->codelen+
((YCEXE*)mm_buf)->codelen);
byte *imgBuf = new byte[DISK_SIZE]; //把上述执行代码按指定位置写入镜像文件
memcpy(imgBuf, boot_buf, boot_len); //引导代码
memcpy(&imgBuf[ycboot_SIZE + ycker_SIZE - ychead_SIZE], head_buf, head_len);
memcpy(&imgBuf[ycboot_SIZE], ker_buf, ker_len); //内核代码
memcpy((char*)&imgBuf[ycboot_SIZE + ker_len], fs_buf, fs_len);//文件管理代码
memcpy((char*)&imgBuf[ycboot_SIZE + ker_len + fs_len], mm_buf, mm_len);
YC_writefile("ycos.img",imgBuf,DISK_SIZE); //将 imgBuf 写入镜象文件
free(boot_buf);
free(ker_buf);
free(head_buf);
free(fs_buf);
free(mm_buf);
delete imgBuf;
#define bochs_src `megs: 128
romimage: file=BIOS-bochs-latest
vgaromimage: file=VGABIOS-lgpl-latest
ata0-master: path=ycos.img, type=disk
boot: c
log: bochs.out`
YC_writefile("ycos.src", bochs_src, sizeof bochs_src);
YC_cppRun("bochs.exe -q -f ycos.src",-1); //执行bochs.exe计算机模拟器
}
void create_exp()
{
auto compiler_exp = [](char *fileptr,char *extptr)
{
char file_buf[256];
strcpy(file_buf,fileptr);
strcpy(&file_buf[strrchr(fileptr,'.') - fileptr], extptr);
remove(file_buf);
char *srcbuf;
int glen = YC_cppCompile(&srcbuf,fileptr); //调用编译函数
YC_writefile(file_buf,srcbuf,glen);
free(srcbuf);
return glen;
};
yc_assert(!compiler_exp("yctty.cpp", ".sys")); //将yctty.cpp编译到yctty.sys
yc_assert(!compiler_exp("ycshell.cpp", ".exp")); //编译到ycshell.sys文件
yc_assert(!compiler_exp("pro0.cpp", ".exp")); //将pro0.cppp编译到pro0.exp
yc_assert(!compiler_exp("pro1.cpp", ".exp")); //将pro1.cppp编译到pro1.exp
yc_assert(!compiler_exp("pro2.cpp", ".exp")); //将pro2.cppp编译到pro2.exp
yc_assert(!compiler_exp("pi2.cpp", ".exp")); //将pi2.cppp编译到pi2.exp文件
yc_assert(!compiler_exp("exp0.cpp", ".exp")); //将exp0.cppp编译到exp0.exp
yc_assert(!compiler_exp("drv.cpp", ".sys")); //将drv.cppp编译到drv.sys文件
yc_assert(!compiler_exp("dra.cpp", ".exp")); //将dra.cppp编译到dra.exp文件
yc_assert(!compiler_exp("drb.cpp", ".exp")); //将drb.cppp编译到drb.exp文件
yc_assert(!compiler_exp("ex0.cpp", ".exp")); //将ex0.cppp编译到ex0.exp文件
}
c/c++代码文件: ycos.cpp
编译:在cmd状态上键入YC命令:ycc ycos.cpp
或在YC编辑器中调入ycos.cpp, 按F6键
生成ycos.exe
执行:在命令行上键入ycos,
或在YC编辑器中调入ycos.cpp, 按F7键
将显示一个对话框。
在对话框中选择“Continue”项,按回车键便进入YCOS的界面。
源代码分析
YC_cppCompile()函数编译YCOS的所有文件
yc_assert()函数检查生成的代码长度是否与ycos.h中设置的值是否相符
语句byte *imgBuf = new byte[DISK_SIZE]申请大小为DISK_SIZE的内存
memcpy()函数将YC_cppCompile()生成的执行代码拷入imgBuf的适当位置
语句YC_writefile(“ycos.img”,imgBuf,DISK_SIZE)将imgBuf写入镜像文件ycos.img
语句#define bochs_src …和 YC_writefile(“ycos.src”,…) 生成设置文件ycos.src
语句YC_cppRun(“bochs.exe -q -f ycos.src”)执行计算机模拟器软件bochs启动YCOS
auto compiler_exp = [](char *fileptr,char *extptr){…}定义了一个局部的lambda函数,如果把它定义为全局函数,则应该按如下方式定义:
void compiler_exp(char *fileptr,char *extptr){…}
compiler_exp()函数生成键盘驱动程序yctty.sys、命令处理程序ycshell.sys和一些示例代码。
2. 头文件ycos.h
#define DATA_POS 0x90000
#define ycboot_SIZE (512 * 2) //ycboot.cpp的最大长度
#define ychead_SIZE 0x2000 //ychead.cpp的最大长度
#define ycker_SIZE (1024 * 64 * 4) //ycker ycfs ycmm ychead代码之和的最大长度
#define ycker_POS (DATA_POS - ycker_SIZE) //ycker.cpp 的调入位置
#define e820_POS 380
#define KERNEL_POS 0xC0000000 //内核映射地址
#define KERNEL_CS 8 * 1 //代码选择子
#define KERNEL_DS 8 * 2 //数据选择子
#define PAGE_SIZE 4096 //页大小
#define PAGE_MASK (~(PAGE_SIZE-1))
#define DIR_SIZE (PAGE_SIZE * 1024) //页目录大小
#define DISK_SIZE (1024 * 1024 * 2) //硬盘大小
static_assert((ycboot_SIZE % 512) == 0, "ycboot_SIZE Error!");
static_assert((ycker_SIZE % (1024 * 64)) == 0, "ycker_SIZE Error!");
static_assert((DATA_POS % (1024 * 64)) == 0, "DATA_POS Error!");
static_assert((KERNEL_POS % DIR_SIZE) == 0, "KERNEL_POS Error!");
typedef unsigned char byte;
#define to_memPtr(pLink) ((byte*)pLink + sizeof(YMEM))
#define to_memLink(_Memory) (YMEM*)((byte*)_Memory - sizeof(YMEM))
#ifndef offsetof
#define offsetof(s,m) (unsigned int)&(((s*)0)->m)
#endif
struct YMEM //内存链表结构
{
YMEM *pNext;
YMEM *pLast;
unsigned int size;
union
{
unsigned int addr;
int flag;
};
};
#define freeMemLINK_POS 0x100000 //空闲物理内存链表的内存位置
#define freeMemLink_NUM (1024*16) //空闲物理内存链表的最大项数
#define PageDir_POS (freeMemLINK_POS + sizeof(YMEM) * freeMemLink_NUM) //页目录
#define PageTable_pos (PageDir_POS + 0x1000) //页表内存位置
static_assert((PageDir_POS % PAGE_SIZE) == 0, "PageDir_POS Error!");
enum //定义按键值
{
VK_BACK = 0x08, VK_TAB,
VK_CLEAR = 0x0C, VK_RETURN,
VK_PAUSE = 0x13, VK_CAPITAL,
VK_ESCAPE = 0x1B,
VK_PRIOR = 0x21,VK_NEXT,VK_END,VK_HOME,VK_LEFT,VK_UP,VK_RIGHT,VK_DOWN,
VK_PRINT = 0x2A,
VK_INSERT = 0x2D, VK_DELETE,
VK_F1 = 0x70,VK_F2,VK_F3,VK_F4,VK_F5,VK_F6,VK_F7,VK_F8,VK_F9,VK_F10,VK_F11,
VK_F12,
VK_MULTIPLY = 0x6A, VK_ADD,
VK_SUBTRACT = 0x6D,
VK_NUMLOCK = 0x90, VK_SCROLL,
VK_LSHIFT = 0xA0, VK_RSHIFT, VK_LCONTROL, VK_RCONTROL, VK_LMENU, VK_RMENU,
};
#pragma pack(1)
struct GDT_PTR //全局描述符结构
{
unsigned short size;
char *addr;
};
struct e820map //内存信息结构
{
int nr_map;
struct e820entry
{
unsigned long long addr;
unsigned long long size;
unsigned long type;
} map[32];
};
static_assert(e820_POS + sizeof e820map <= ycboot_SIZE, "e820_POS Error!");
#pragma pack()
struct YDescriptor //段描述符结构
{
unsigned short limit_low;
unsigned short base_low;
byte base_mid;
byte attr1;
byte limit_high_attr2;
byte base_high;
};
struct YProcess //进程数据结构
{
unsigned int gs;
unsigned int fs;
unsigned int es;
unsigned int ds;
unsigned int edi;
unsigned int esi;
unsigned int ebp;
unsigned int kernel_esp;
unsigned int ebx;
unsigned int edx;
unsigned int ecx;
unsigned int eax;
unsigned int eip;
unsigned int cs;
unsigned int eflags;
unsigned int esp;
unsigned int ss;
unsigned short ldt_sel;
YDescriptor ldts[2]; //局部段描述符
int m_ticks;
int priority;
unsigned int pid;
YMEM *pHead;
char *CommandLine;
};
struct YTSS //保存堆栈指针的结构
{
unsigned int backlink;
unsigned int esp0;
unsigned int ss0;
unsigned int $[25];
};
struct YCEXE //执行文件头部结构
{
char name[32];
int srcpos;
int srclen;
int org;
int bit;
int segnum;
int funpos;
int codelen;
int addrnum;
int addrpos;
int argpos;
int innernum;
int innerpos;
int ldpos;
int verpos;
int exitpos;
int retsize;
char doswinpos;
char compress;
char testflag;
char vmflag;
int headfilepos;
int headfilelen;
int autofilelen;
int stacksize;
int length;
};
struct BINPOS
{
int pos;
int len;
};
struct ycfsCLASS //文件管理接口
{
virtual byte *stdcall get_file_code(char *fptr,int flen=0,int *pSize=0);
virtual char *stdcall get_file_name(int pos,int &size);
};
struct ycmmCLASS //内存管理接口
{
virtual YMEM *stdcall malloc(YMEM *pHead,unsigned int _Size,int isHigh=0);
virtual YMEM *stdcall realloc(YMEM *oldLink,unsigned int _NewSize);
virtual void stdcall free(YMEM *pLink);
virtual YMEM *stdcall get_head();
};
struct ycttyCLASS //显示和键盘管理接口
{
virtual void stdcall keyboard_proc();
virtual void stdcall sys_write(int *pdata);
virtual void stdcall wr_prompt();
virtual void stdcall clear_screen(int bpos,int len);
virtual void stdcall cls();
virtual void stdcall pmt_cursor_pos(int cpos);
virtual void stdcall set_disk(int mval);
virtual void stdcall display_LED(byte capsV,byte numV,byte scrollV);
virtual void stdcall sys_cursor(int curpos);
virtual void stdcall keyboard_read(int &vk_char,int &vk_value,int &vk_press,
byte &capsL,byte &numL,byte &scrollL,
byte &shiftV,byte &altV,byte &ctrlV);
};
YCOS头文件: ycos.h