خب خب، اومدیم به پارت سوم، تا الان موفق شدیم سیستم عاملمون رو با fat16 اوکی کنیم تا بشه روی فلش بوتش کرد.


من از اون پارت دوم یکمی کار کردم روش و تونستم root region رو هم اوکی کنم.
در کل اینجوری شد فایل build.asm:
format binary as 'img' use16 org 0x0000 FLOPPY_SIZE equ 2 * 80 * 18 * 512 include 'src/fat.asm' include 'boot.asm' display '=========== InousOS ============', 0x0d, 0x0a display ' - Master Boot Record (MBR) has been created.', 0x0d, 0x0a rb (RESERVED_SECTORS-1) * BYTES_PER_SECTOR ; ========== FAT Region ========== clusters_size = BYTES_PER_SECTOR * SECTORS_PER_CLUSTER macro fat_init list_file { repeat NUMBER_FAT_COPIES current_pos = $ db MEDIA_DESCRIPTOR, 0xff ; FAT_ENTRY1 dw 0xffff ; FAT_ENTRY2 irps file_name, list_file \{ display ' - FAT entry ', %+47, " has been created for file '", file_name, "'.", 0x0d, 0x0a virtual at 0 file file_name file_size = $ end virtual file_clusters = (file_size + clusters_size - 1) / clusters_size if file_clusters = 0 dw 0xffff else repeat file_clusters if file_clusters = % dw 0xffff else dw (% + 2) end if end repeat end if \} rb (SECTORS_PER_FAT * BYTES_PER_SECTOR) - ($-current_pos) end repeat } fat_init 'build/kernel.bin' display ' - FAT area has been initialized.', 0x0d, 0x0a ; ======== END FAT Region ======== ; ========== ROOT Region ========= macro drive_init drive_name { DRIVE_NAME db drive_name ATTRIBUTE db 0x08 RESERVED db 0x00 CREATION db 0x00 CREATION_TIME dw 0x0000 CREATION_DATE dw 0x0021 LAST_ACCESS_DATE dw 0x0021 RESERVED_FAT32 dw 0x0000 LAST_WRITE_TIME dw 0x0000 LAST_WRITE_DATE dw 0x0021 STARTING_CLUSTER dw 0x0000 DRIVE_SIZE dd 0x00000000 } macro root_entry file_name { local FILE_NAME, FILE_EXT, ATTRIBUTE, \ RESERVED, CREATION, CREATION_TIME, \ CREATION_DATE, LAST_ACCESS_DATE, \ RESERVED_FAT32, LAST_WRITE_TIME, \ LAST_WRITE_DATE, STARTING_CLUSTER, DRIVE_SIZE virtual at 0 file file_name file_size = $ end virtual FILE_NAME db 'KERNEL ' FILE_EXT db 'INU' ATTRIBUTE db 0x20 RESERVED db 0x00 CREATION db 0x00 CREATION_TIME dw 0x0000 CREATION_DATE dw 0x0021 LAST_ACCESS_DATE dw 0x0021 RESERVED_FAT32 dw 0x0000 LAST_WRITE_TIME dw 0x0000 LAST_WRITE_DATE dw 0x0021 STARTING_CLUSTER dw 0x0003 DRIVE_SIZE dd file_size } macro root_init list_file { irps file_name, list_file \{ root_entry file_name \} } drive_init VOLUME_LABEL root_init 'build/kernel.bin' root_init 'build/kernel.bin' ; ======= END ROOT Region ======== rb FLOPPY_SIZE - ($-$$) - 1 db 0 display 0x0d, 0x0a
یکمی جاها نیازمند تعریف متغیر و ایناس، و یکمی کار داره برای data region.
فایل buildx.bat هم دادیم به هوش مصنوعی ( خودم زیاد batch script بلد نیستم ) و اینو داد:
@echo off setlocal enabledelayedexpansion set SRC_DIR=src set BUILD_DIR=build set DIST_DIR=dist if not exist %BUILD_DIR% mkdir %BUILD_DIR% if not exist %DIST_DIR% mkdir %DIST_DIR% for %%F in (%SRC_DIR%\*.asm) do ( fasm %%F %BUILD_DIR%\%%~nF.bin if errorlevel 1 ( pause exit /b 1 ) ) fasm build.asm %DIST_DIR%\inous.img if errorlevel 1 pause
در کل گفتم بهش یک ساختار Make استاندارد بهش بده و اینو داد، تمام فایل هارو اسمبل میکنه در اخر هم سر هم میکنه.
حالا من اینارو خودم اوکی میکنم، اومدم روی یک مبحث تمرکز کنم، بوت لودر ما باید کرنل رو لود کنه چون فایل سیستم داریم، میتونیم دو مدل ادرس دهی کنیم، یکیش اینه از مستقیم بپریم روی کرنل، یکیشم اینه که توی فایل سیستم دنبال کرنل بگردیم😐😂 خب راه حل دوم احمقانه اس! چون نمیتونیم که توی 512 بایت بیایم کرنل رو سرچ کنیم و منتظر بمونیم.
حالا توی جلسه بعدی مستقیم میریم کد نویسی اسمبلی، تا الان هر چی بوده مبانی و مباحث ساخت سیستم عامل بوده که باید بلد باشین شما، الان میخوایم دربارهی درایور ها حرف بزنیم.
ما میتونیم از وقفه های بایوس استفاده کنیم برای کیبورد و موس و یک پولینگ بزنیم که دیلی میده و زیاد جالب نمیشه، مثلا هر بار باید چک کنیم موس حرکت کرده؟ کجا حرکت کرده؟ کیبورد چیکار کرده؟ همچنین وقفه ها در حالت عادی کندن چون هر بار میرن به جدول ivt و دسترسی به همین جدول از طریق رم هست که سیکل پردازنده رو بالا میبره و در نتیجه کنده.
ما اینجا باید درایور های سیستم عامل رو بنویسیم، چون لود کردن کرنل از بوت زیاد کار سختی نیست اول میریم درایور ها رو چک کنیم و بنویسیم. و اولم میریم سراغ کیبورد. کیبورد و موس یک پورت PS/2 دارن که ما با اون کار میکنیم و حرف میزنیم.

اصلا فکر نکنید کار سختیه، کل کدایی که قراره برای درایور کیبورد بنویسیم کمتر از 1 کیلوبایته:
چون سیستم ما اموزشیه فقط پورت PS/2 ساپورت کنیم کافیه، شاید 2 یا 3 تا دیگه اضافی کنیم، بتونیم تقریبا کل سیستمارو ساپورت کنیم.
فقط تنها کار سختش تبدیل اسکن کد به کاراکتره! چون ما مثلا اگه این کارو نکنیم اگه A رو بزنیم مثلا 1 رو چاپ میکنه😂 خب ما برنامه نویس های اسمبلی از if, elif, else و اینا بدمون میاد، و حال نداریم اینجوری بزنیم مثلا:
if scancode == 0x1c: print('A') elif scancode == 0x32: print('B') elif scancode == 0x21: print('C')
چون اینجوری نزدیک 300 تا باید if بزنیم، خب اینکه توی اسمبلی از 1 کیلوبایت بیشتر میشه که!
یک چیزی داریم تو اسمبلی، مثل بلوک ترجمه میمونه، بهش یک چیزی میدی بهت یک چیز دیگه میده. مثلا اینجوریه:
scancode = 0x1c trans = bytes.maketrans(bytes(range(0xff)), b'ABC........') print(bytes.translate(scancode, trans))
اینجوری فقط دو سه تا if دیگه میخوایم که برای کلید های ویژه هستن.
قبلش باید مفهوم IRQ رو بدونین، خلاصش اینه که سخت افزار مثلا موس یا کیبورد وقتی بخواد به پردازنده بگه: "داداش، فلانی کلید A من رو زد"، میره در خونه رو میزنه، پردازنده در رو باز میکنه میبینه: عه این کیبورده، بعد به کیبورد میگه چیکارم داشتی، کیبورد هم میگه کلید A من رو زد.
اما قبل ترش میومدن از پولینگ استفاده میکردن، یعنی هر بار که پردازنده بخواد کاری انجام بده، به کیبورد زنگ میزنه و میگه خبری هست یا نه؟ کیبورد هم میگه نه یا اره.
یکجورایی مثل وب سوکت و http polling هست.
درایور ها معمولا با IRQ نوشته میشن، سیستم عامل های مدرن هم همینجورین، مثلا:


الان اگه بخوایم فنی تر بیان کنیم، میگیم اگه ما کلید A رو توی کیبورد بزنیم، مدار کیبورد اون رو میاد مثلا به 0x1c تبدیل میکنه، بعد با IRQ درخواست وقفه سخت افزاری میده به سی پی یو، بعد سی پی یو میره، به جدول ivt که مپ شده به IRQ و جامپ میکنه روی درایور. درایور هم میاد از کیبورد داده دریافت میکنه، مثلا اینارو:
اسکن کد مربوط به یک کاراکتر
وضعیت فشار دادن کلید یا ول کردن کلید
همچنین وضعیت کلید های ویژه مثل sleep، vol-up و ...
بعد درایور میاد داده رو یکسری عملیات روش انجام میده و میفرسته به درایور گرافیک، اون هم میاد مثلا شکل کرسر موس رو روی مانیتور رسم میکنه، به همین راحتی!
نکته: هر IRQ به یک ivt خاص وصل شده، مثلا IRQ 1 وصل شده به INT 9.
کلا 15 تا IRQ داریم، بقیه چیزا مثل اسپیکر و پرینتر و ... بدون IRQ هستند، یادتونه در پارت اول گفتیم دستگاه های ورودی فقط میتونن IRQ به پردازنده بفرستن، بقیه چیزا رو فقط پردازنده به دستگاه ها میفرسته.
حالا اگه بریم ته ماجرا، 32 تا وقفه اول رو اینتل رزرو کرده، بعد بایوس، اومده پورت های مثل IRQ 1 رو وصل کرده به int 9 ( مثلا خیلی خفنه 😂 )، اینجوری وقفه های خود سی پی یو با بایوس بهم میریزه و کلی مشکل پیش میاد ( دستش درد نکنه ).
باید ما اول بیایم مپینگ IRQ به IVT رو درستش کنیم، مثلا IRQ 1 رو بجای 9 بکنیمش مثلا 20 ( در هگزادسیمال 32، اخرین وقفه رزرو خود اینتل 31 هست ) به بالا و اینجوری مشکل ها برطرف میشه.
از جلسه بعدی بصورت عملی ساخت سیستم عامل رو شروع میکنیم و ساخت درایور های پایه و چیزای پایه و همه چیز.