بهرحال، هر شروعی یک پایانی داره و اینجاس که ما به پایان خودمون نزدیک میشیم! پایان قشنگ و دوست داشتنی، وقتی می بینیم دست رنجمون در طول چند هفته (بسته به مهارت برنامه نویسی و حوصله و زمانی که صرف این کار کردید) ، تبدیل شده به یه چیز «تقریبا» قابل استفاده، دوست داشتنی تر هم میشه! بسیار خب کد اسمبلی که نوشتیم و کامل شده به این شکله :
org 0x7c00
bits 16
mov ax, 0
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00
mov si, welcome
call print_string
mov si, about
call print_string
mov si, newline
call print_string
mainloop:
mov si, prompt
call print_string
mov di, buffer
call get_string
mov si, buffer
cmp byte [si], 0
je mainloop
mov si, buffer
mov di, cmd_reboot
call strcmp
jc .reboot
mov si, buffer
mov di, cmd_about
call strcmp
jc .about
mov si, buffer
mov di, cmd_help
call strcmp
jc .help
mov si, badcommand
call print_string
jmp mainloop
.reboot:
int 0x19
jmp mainloop
.about:
mov si, msg_about
call print_string
jmp mainloop
.help:
mov si, msg_help
call print_string
jmp mainloop
welcome db 'Welcome to my OS', 0x0d, 0x0a, 0
about db 'Written in 16-bit real mode', 0x0d, 0x0a, 0
buffer times 64 db 0
prompt db '>> ', 0
newline db ' ', 0x0d, 0x0a, 0
badcommand db 'Bad command entered', 0x0d, 0x0a, 0
cmd_reboot db 'reboot',0
cmd_about db 'about', 0
msg_about db 'This is a 16 bit operating system, works in real mode and written in assembly language', 0x0d, 0x0a, 0
cmd_help db 'help', 0
msg_help db 'Commands are : about, help and reboot', 0x0d, 0x0a, 0
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0e
int 0x10
jmp print_string
.done:
ret
get_string:
xor cl, cl
.loop:
mov ah, 0
int 0x16
cmp al, 0x08
je .backspace
cmp al, 0x0D
je .done
cmp cl, 0x3F
je .loop
mov ah, 0x0e
int 0x10
stosb
inc cl
jmp .loop
.backspace:
cmp cl, 0
je .loop
dec di
mov byte [di], 0
dec cl
mov ah, 0x0e
mov al, 0x08
int 0x10
mov al, ' '
int 0x10
mov al, 0x08
int 0x10
jmp .loop
.done:
mov al,0
stosb
mov ah, 0x0e
mov al, 0x0d
int 0x10
mov al, 0x0a
int 0x10
ret
strcmp:
.loop:
mov al, [si]
mov bl, [di]
cmp al, bl
jne .notequal
cmp al, 0
je .done
inc di
inc si
jmp .loop
.notequal:
clc
ret
.done:
stc
ret
times 510-($-$$) db 0
dw 0xaa55
همونطور که می بینید چن قسمت جدید شروع شده ، مثلا تابع strcmp رو ساختیم، چن تا دستور و پیام تعریف کردیم و زیر تابع های reboot و about و help رو هم تعریف کردیم. خب اول یه توضیح کوچیک : دستورات باید حتما معرفی بشن. چرا؟ چون تابع strcmp ورودی کاربر رو با یه سری اطلاعات از پیش تعیین شده میاد مقایسه میکنه و در صورت برابری با مقدار وارد شده، اون دستوری که در برنامه تعریف شده رو اجرا میکنه. پس بخشی که چندین db پشت هم تعریف شده رو جدی بگیرید! بسیار خوب، یکی از توابع خوب و زیبامون تابع strcmp هست. این تابع چیه؟ ما یه دستوری در اسمبلی داریم به اسم cmp که کارش مقایسه است. حالا برای این که ببینیم دو تا رشته با هم برابرن اومدیم strcmp نوشتیم. در واقع مخفف string compare هست. خب این تابع در کد ما به این شکله :
strcmp:
.loop:
mov al, [si]
mov bl, [di]
cmp al, bl
jne .notequal
cmp al, 0
je .done
inc di
inc si
jmp .loop
.notequal:
clc
ret
.done:
stc
ret
این تابع ، میاد اول یک بایت از si رو میریزه توی ۸ بیت پایینی AX . بعدش یک بایت از di رو میریزه توی هشت بیت پایینی BX . بعد با cmp مقایسهش میکنه، در صورتی که برابر نبودن، میره سراغ notequal که توضیح داده میشه، و اگر برابر باشن، هشت بیت پایین AX چک میشه و اگر خالی بود میره done. در غیر این صورت یک کرکتر میره جلو (با دستور inc یک خونه به جلو حرکت میکنه). بعد میرسیم به بخش دوست داشتنی notequal ، توی این بخش اول Carry Flag که یک بیت از رجیستر Flag در پردازنده ماست، خالی میشه و تابع تموم میشه. در قسمت done هم این بیت پر میشه و تابع تموم میشه! دیدید؟ بسیار ساده تر از چیزی بود که فکرش رو میکردید! حالا بریم کارکرد دستورات رو بررسی کنیم :
mov si, buffer
mov di, cmd_about
call strcmp
jc .about
خب این بخش برای همه دستورات یکیه، چی کار میکنه؟ خط اول buffer (رشته ای که کاربر وارد کرده) و خط دوم دستور (دستوری که توی اون بخش توسط db تعریف کردیم) وارد رجیستر های SI و BI میشن. بعد تابع strcmp میاد این دو رشته رو با هم مقایسه میکنه. اگر شرط درست باشه، به زیرتابع مورد نظر میریم. در غیر این صورت، زیرتابع badcommand که در قسمت سه و نیم معرفی شد، فراخوانی میشه. حالا بیایم ساختار about رو بررسی کنیم (توجه کنید about ساختارش با help یکیه پس یکیشون کافیه که بررسی شه) :
.about:
mov si, msg_about
call print_string
jmp mainloop
خب تا حدود زیادی مثل badcommand عمل میکنه (تقریبا همونه با این تفاوت که یه شرط لازم داره برای اجرا شدن) ، اول رشته مورد نظرمون میره توی SI ، بعد print_string صدا زده میشه، و بعد میریم به حلقه اصلی و منتظر میمونیم تا کاربر دستور بعدی رو بده.
این زیرتابع ها، مشابهن. فقط تابع reboot یکم ممکنه اذیتتون کنه، ولی خب اون رو هم اینجا توضیح میدیم :
.reboot:
int 0x19
jmp mainloop
خب طبیعیه که jmp کارش پرش به حلقه اصلیه و برای این میذاریمش که اگر reboot رو کاربر نزنه ، چی بشه؟ هیچ اتفاقی نیفته و سیستم عامل کوچولومون همچنان منتظر باشه کاربر بهش دستور بده. اما وقفه ۱۹ هگزادسیمال یا 0x19 (که بعضی جاها ممکنه 19h هم نوشته شه) ، چیه؟ این وقفه یکی از وقفه های بایوسه که کارش ریبوت کردن (راه اندازی مجدد) سیستم عاملمونه.
تبریکات فراوان! شما الان یک سیستم عامل زیبا و کوچولو نوشتید، که سه تا دستور داره و اگر دستوری غیر از اینا بهش بدید، بهتون ارور میده. در واقع شما کاری رو پیش بردید که توسعه دهنده ها سالیان پیش انجام میدادن تا کامپیوترها بیشتر و بیشتر کاربرپسند بشن! و از این جهت شدیدا باید خوشحال باشید، چرا؟ چون کارهایی رو دوباره کردید که احتمال خیلی زیاد توی شرکتهای بزرگ کامپیوتری انجام میشده! و همچنین تشکرات فراوان بابت این که مطالب من رو خوندید و براش وقت گذاشتید. بزودی سری جدیدی از این مقالات شروع میشه که سیستم عامل کوچولومون رو میبریم توی مود حفاظت شده ( Protected Mode ) که یک مود ۳۲ بیتی هست و احتمال خیلی زیاد از بوت لودر GRUB برای بوت کردنش استفاده خواهیم کرد!
موفق باشید 🙂
سلام. من میخوام یه سیستم عامل بنویسم بوتش رو نوشتم ولی نمیدونم چطور هسته رو لود کنم و اینکه چطور هسته ای که با c++ نوشتم رو کامپایل کنم. ممنون میشم زود جواب بدید.
درود بر شما، لطفا وبسایت osdev.org رو چک بفرمایید.
به ینده پیام بده
end.cloudworlds@gmail.com
کد رو تو اسمبلر های مختلف اسمبل کردم ولی همه ارور می دهند
همون طور که گفته شده باید از nasm استفاده کنید.
مرسی عالی بود
سلام علیکم
میسه فایل iso این سیستم عامل رو بذارید
ممنون میشم
کد در گیتهابم هست.