儘管我的題目是“用C語言寫操作系統”,但是,僅僅使用C語言是寫不出操作系統的。我看到很多篇關於自己動手開發操作系統的文章,幾乎全來自一篇叫“Write Your Own Operating System Tutorial”英文文章,而且,使用的全是彙編語言。如今能夠精通彙編語言的人已屬鳳毛麟角,而且現代操作系統的主體部分是用C語言寫的,難道所謂的“Own Operating System”只能用彙編語言嗎?在下不才,使用C語言(在某些部分必須結合彙編語言)寫了一個可以與上述文章中提到的操作系統相媲美的操作系統,拋磚引玉,希望能夠將其發展成爲真正的中國人自己的操作系統。
我是在Windows環境下開發的,而不是大多數人選擇的Linux環境。開發工具也是Microsoft公司的開發工具:masm615和VC15。微軟公司的masm流傳甚廣,大家應該不陌生。對vc15可能較陌生,vc15堪稱開發DOS程序的最“高檔”編譯器(儘管有很多BUG)。如果實在找不到這些開發工具,使用TASM、Turbo C或者Borland C++都是可以的。所給出的例子以masm615和vc15爲準,轉移到TASM、TC或BC平臺,應該不難。有一條原則需要注意,在這裏,源碼必須使用TINY模式編譯,也就是說,必須生成實模式代碼。
1. 建立開發環境
這一步非常的簡單。
將masm613和vc15的壓縮包分別解壓到e:/masm615和e:/msvc15目錄下。你也可以放到其他目錄下,根據自己的情況而定,但是下面用到的編譯命令需要作相應的修改。也不需要添加或修改任何的環境變量。
2. IBM PC的啓動及當時的內存使用情況
3. 開發操作系統
;
; entry.asm
; Copyright (C) 2004, Tian XiangYuan
;
.MODEL TINY,C
.386p
option expr32
option casemap:none
cmain PROTO NEAR C
.CODE
ORG 0100h ;偏移地址
_start:
jmp begin
nop
DB 'TianXiangYuan',0 ;the magic of my os
begin:
cli
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0FFFFh
sti
call cmain ;調用C語言寫的主函數
mov ax,4c00h ;調用DOS的功能(爲了調試),與我們自己的操作系統無關
int 21h
|
……
static void InitShell()
{
}
void cmain()
{
InitShell();
TermShell();
}
|
/*
*從鍵盤讀一個字符,如果沒有輸入,則等待;返回值的低字節爲asii碼,高字節爲鍵盤掃描碼
*/
static int getch()
{
int chr=0;
__asm
{
mov ah,00h
int 16h
mov chr,ax
}
return chr;
}
/*
*使用TTY模式向屏幕輸出一個字符
*/
static void putch(unsigned char key)
{
__asm
{
mov bh,0
mov al,key
mov ah,0Eh
int 10h
}
}
#define KEY_BACKSPACE 0x08
#define KEY_ENTER 0x0D
#define KEY_NEWLINE 0x0A
#define KEY_ESCAPE 0x1B
static int printk(const char* str,...)
{
…… //給大家一點空間,自己實現吧
}
static void endline()
{
putch(KEY_NEWLINE); //Line Feed (LF)
putch(KEY_ENTER); //Enter (CR)
}
static char msg_prompt[]="CMD:";
static void deal_cmd(char* cmd_line,int cmd_len)
{
…… //也請大家自己實現吧,例如,可以實現help,dir,cls,halt等命令
…… //其實,就是字符串比較的過程
}
static void TermShell()
{
char cmd_line[80]={0,};
int cmd_len=0;
endline();
printk(msg_prompt,sizeof(msg_prompt));
for (;;)
{
cmd_line[cmd_len]=getch();
switch(cmd_line[cmd_len])
{
case KEY_ENTER:
if (cmd_len>1)
deal_cmd(cmd_line,cmd_len);
//break;
case KEY_ESCAPE:
cmd_len=0;
endline();
printk(msg_prompt,sizeof(msg_prompt));
break;
case KEY_BACKSPACE:
if (cmd_len>0)
{
putch(0x08);
putch(' ');
putch(0x08);
cmd_len--;
}
break;
default:
putch(cmd_line[cmd_len]);
cmd_len++;
}
}
}
|
4. 編譯方法
@echo off
set PATH=e:/masm615/bin;e:/msvc15/bin;
set AS=e:/masm615/bin/ml.exe
set AFLAGS=/AT /W3 /X /Gd /Zp1 /nologo
set CC=e:/msvc15/bin/cl.exe
set CFLAGS=/Od /G3 /Gd /Gs /Zl /Zp1 /X /W3 /nologo
del *.obj *.com *.cod *.dbg *.pdb *.map *.lst
%AS% /c %AFLAGS% entry.asm
if errorlevel 1 goto error
%CC% /c %CFLAGS% /Fc main.c
if errorlevel 1 goto error
%AS% %AFLAGS% /Fe"boot.com" entry.obj main.obj
if errorlevel 1 goto error
goto exit
:error
echo Failure......
:exit
pause
@echo on
|
5. 引導程序
;
; bootsect.asm
; Copyright(C) 2004, Tian XiangYuan
;
.MODEL TINY,C
.386p
option expr32
option casemap:none
SYSSEG EQU 1000h
SYSOFF EQU 0100h
.CODE
ORG 7C00h
_start:
jmp begin
nop
DB 'BOOTSECT',0 ;magic
pack_size DB 16
DB 0 ;reserved
DW 60 ;sectors
DW SYSOFF ;buf_addr_off
DW SYSSEG ;buf_addr_seg
DD 2 ;sector_from
DD 0 ;sector_from_high
begin:
cli
mov ax,cs
mov ds,ax
mov es,ax
mov ss,ax
mov sp,0FFFFh
sti
mov cx,msg_load_len ;length
lea bp,msg_load ;es:bp
call display_msg
; read disk for my OS
lea si,pack_size
mov dl,80h
mov ax,4200h
int 13h ;使用LBA方式讀硬盤
jc error
; test magic of my os
lea si,magic_test ;ds:si
mov ax,SYSSEG
mov es,ax
mov di,SYSOFF
add di,3 ;es:di
mov cx,magic_test_len
cld
test_again:
cmpsb
jnz error
loop test_again
push SYSSEG
push SYSOFF
retf ;轉入操作系統執行
error:
mov ax,cs
mov es,ax
lea bp,msg_error ;es:bp
mov cx,msg_error_len
call display_msg
failure:
hlt
jmp failure
;cx : length of message
;es:bp : address of message
;void display_msg();
display_msg PROC NEAR C
;scrollup a line
push cx
push bp
mov ax,0601h
mov bh,07h
mov cx,0000h ;y/x
mov dx,184Fh ;y2/x2, 24/79
int 10h
pop bp
pop cx
;display message
mov ax,1301h
mov bx,000Ah
;mov cx,msg_error_len
mov dl,0 ;x
mov dh,24 ;y
;lea bp,msg_error ;es:bp
int 10h
ret
display_msg endp
.DATA
msg_load DB 'Loading......',0
msg_load_len DW $ - msg_load
msg_error DB 'NO BOOTER,please reboot!',0
msg_error_len DW $ - msg_error
magic_test DB 'TianXiangYuan',0
magic_test_len DW $ - magic_test
end _start
|
@echo off
set PATH=e:/masm615/bin;
set AS=e:/masm615/bin/ml.exe
set AFLAGS=/AT /W3 /WX /Gd /Zp1 /X /nologo
del *.obj *.com *.cod *.dbg *.pdb *.map
%AS% /c %AFLAGS% bootsect.asm
if errorlevel 1 goto error
%AS% %AFLAGS% /Fe"bootsect.com" bootsect.obj
if errorlevel 1 goto error
goto exit
:error
echo Failure......
:exit
pause
@echo on
|