توضیحات بوت لودر سوم تدریس شده در کلاس استاد رحیم پور
بسم الله الرحمن الرحیم
توضیحات مختصر در مورد بوت لودر سوم که استاد در کلاس در اون زمان تدریس کردند .
//بسم الله الرحمن الرحیم
;============================
;Developing a Simple Opeating System(SOS)
;comments and explanations by :
;Seyyed Hossein Hasan Pour
;Master.huricane@gmail.com
;www.forum.ustmb.ir
;Boot Loader(BL.asm)
;============================
Code Segment
Assume CS:Code
start: Jmp Begin ;Jump to Start point of executable Code
;============================================
; System Data Segment
; BIOS Parameter Block (BPB)
; Disk Parameter Block(DPB)
; describing number of tracks, number of sectors, cluster size,
; root directory size, FAT size ...
;=======================================
bpbOEM DB "My OS " ; the name of OS (8 byte)"Original Equipment Manufacturer"
bpbBytesPerSector DW 512
bpbSectorsPerCluster DB 1 ;group sectors into larger blocks that are called clusters
;The sectors in a cluster are continuous,
; so each cluster is a continuous block of space on the disk.
bpbReservedSectors DW 1 ;the number of sectors not included in FAT12.
bpbNumberOfFATs DB 2
bpbRootEntries DW 224 ;have a maximum of 224 directories within its Root Directory
bpbTotalSectors DW 2880 ;disk size= 1 MG
bpbMedia DB 0F0H ;FA:it is a RamDisk
;F0 = 11110000 binary. This means
;it is a single sided,
;9 sectors per FAT, 80 tracks, and is a movable disk.
bpbSectorsPerFAT DW 9
bpbSectorsPerTrack DW 18
bpbHeadsPerCylinder DW 2
bpbHiddenSectors DD 0
bpbTotalSectorsBig DD 0
bsDriveNumber DB 0
bsUnused DB 0
bsExtBootSignature DB 29H ;represents the type and version of this BIOS Parameter Block
;0x28 and 0x29 indicate this is a MS/PC-DOS version 4.0
bsSerialNumber DD 0a0a1a2a3H
bsVolumeLabel DB "RemoveAble " ;must be 11 - no less -no more
bsFileSystem DB "FAT12 "
; User Data Segment
;=======================================
absoluteSector DB 0
absoluteHead DB 0
absoluteTrack DB 0
KernelName DB "KERNEL SOS",0
cluster DW 0000H ; start Cluster of Data in Fat
datasector DW 0000H ; Strat Sector For Data thar placed at end of Root Dir
Progress DB ".",0
LOSMessage DB 0AH,0DH,"SOS:", 0
MsgRoot DB 0AH,0DH,"-Root",0
MsgFat12 DB 0AH,0DH,"-Fat",0
MsgKernel DB 0AH,0DH,"-KERNEL",0
;==============================================================
; Print a String With Delay:
; DS:SI --> Index of each character
; this string terminated by 0
;=======================================
PrintDelay:
Push AX
DPL:lodsb ; load next byte from string from DS:SI to AL
or al, al ; Does AL=0? (is String Terminated?)
jz DPrintDone ; Yes, terminated
mov ah, 0eh
int 10h
Mov DI , 0FFFFH
L1: Mov DX , 01500H
L2: Dec DX
JNZ L2
Dec DI
JNZ L1
jmp DPL ; Repeat until null terminator(0) found
DPrintDone:
Pop AX
ret ; we are done, so return
;================================================================
; Convert LBA to CHS
; AX=>LBA Address to convert
;
; absolute track = logical sector / (sectors per track * number of heads)
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute sector = (logical sector / sectors per track) + 1
;=======================================
LBACHS:
xor dx, dx ; prepare dx:ax for operation
Lea SI, bpbSectorsPerTrack
div WORD PTR[SI] ; calculate
inc dl ; adjust for sector 0
Lea SI, absoluteSector
mov BYTE PTR[SI], dl
xor dx, dx ; prepare dx:ax for operation
Lea SI, bpbHeadsPerCylinder
div WORD PTR[SI] ; calculate
Lea SI, absoluteHead
mov BYTE PTR[SI], dl
Lea SI, absoluteTrack
mov BYTE PTR[SI], al
ret
;==========================================================
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;=======================================
//اینجا برای اینکه ادرس فیزیکی رو تبدیل به منطقی کنیم از این تابع استفاده میکنیم
//فرمول که بالا نوشته شده
ClusterLBA:
//مشخصه دیگه آ ایکس که حاوی مقدار ادرس فیزیکی مورد نظر ماست دوتا ازش کم میکنیم و بعد این حاصل رو ضرب در تعداد سکتور در هر کلاستر میکنیم چون در هر کلاستر یک سکتور بیشتر نداریم درنتیجه قبلش تو س ایکس مقدار یک رو قرار میدیم بعد مال یعنی ضرب میکنیم در س ایکس .
یادتون نرفته که وقتی مال رو بکار میبریم محتوای درون ا ایکس ضرب در این چیزی میشه که مال چیز نوشتیم ..
sub ax, 0002H ; zero base cluster number
mov CX, 00001H ;[bpbSectorsPerCluster]
mul cx
بعد هم مقدار بدست اومده از ضرب که تو ا ایکس قرار گرفته رو با ادرس سکتور داده (این اولین سکتور بخش داده هست )جمع میکنیم . و تمام
Lea SI, datasector ; base data sector
add ax, WORD PTR[SI] ; base data sector
ret
;=========================================================
; Reads a series of sectors
; CX=>Number of sectors to read
; AX=>Starting sector
; ES:BX=>Buffer to read to
;===================================
//اینجا هم قراره یک مجموعه ای از سکتور ها رو بخونیم .
//تعداد سکتورهایی که قراره بخونیمشون تو س ایکس قرار میگیره
//سکتور اغازین هم تو ا ایکس (یعنی سکتور شروع )
//بافر هم که یادتون هست . لازمش داریم . کجا بود بافر ؟
//درسته تو ES:BX
ReadSectors:
mov di, 0005H ; five retries for error
//خوندن اصلی ما از اینجا شروع میشه .
SECTORLOOP:
//ثباتها رو یه جای امن ذخیره کردیم
push ax
push bx
push cx
چون ادرس شروع ما ادرس شروع سکتور منطقی هست باید اونو تبدیل به معادل فیزیکش کنیم . پس تابع تبدیل گر رو صدا میزنیم
call LBACHS ; convert starting sector to CHS
//این هم از قرار معلوم شماره تابعی هست که کار خوندن رو انجام میده . یک سکتور یک سکتور
//بجای اینکه بنویسیم
//mov ah,02h// شماره تابع برای خوندن
mov al,01h //تعداد سکتوری که باید خونده بشه
//یه سره نوشتیم
//mov ax,0201h
mov AX, 0201H ; BIOS read sector (AH=02H) Read one Sector (AL=01H)
//وقتی تابع بالا رو فراخونی کردیم توش مکان فیزیکی چند چیز دیگه رو هم معلوم کردیم
//و اونا رو تو یه متغییرهایی ذخیره کردیم
//شماره ترک فیزیک
//شماره سکتور فیزیکی
//شماره هد
اینها چون تو متغییر بودن با اس آی به ادرسشون اشاره میکنیم و
بعد محختوای اس آی رو قرار میدیم تو ثبات مربوطه .
//تو بخش قبل دیدیم که مثلا ثبات سی ال جایی بود که ما باید شماره سکتور شروعمون رو توش قرار میدادیم . یا ثبات سی اچ جایی بود که ما باید شماره شیارمون رو توش قرار میدادیم .
Lea SI, absoluteTrack
mov ch, BYTE PTR[SI] ; track
Lea SI, absoluteSector
mov cl, BYTE PTR[SI] ; sector
Lea SI, absoluteHead
mov dh, BYTE PTR[SI] ; head
//اینجا هم شماره درایوی که میخواییم از روش بخونیم رو به ثبات مربوطه میدیم
Lea SI, bsDriveNumber
mov dl, BYTE PTR[SI] ; drive
//بعد که کارمون تموم شد . وقفه 13 رو فراخونی میکنم .
int 13H ; invoke BIOS
//اینجا هم گفتیم که اگه همه چیز به خوبی و خوشی خونده شد برو بخش بعدی وگرنه
jnc SUCCESS ; test for read error
//برو یه بار دیگه بخون .
//برای اینکار هم یادتونه دیگه باید حتما فلاپی درایو رو ریست میکردیم .
//این کار رو با فراخونی تابع صفر از وقفه 13 انجام میدادیم .
باز مثل بالا بجای اینکه بگیم
mov ah,0
int 13
//گفتیم xor ax,ax
که کل Ax
رو صفر میکنه این دستور .
xor ax, ax ; BIOS reset disk
int 13H ; invoke BIOS
//از ثبات دی آی یکی کم کن . اون بالا اگه یادتون بشه یه مقداری رو به دی آی داده بودیم . این دی آی بخاطر اینه که ببینه چند بار برنامه در خوندن با مشکل روبرو شد . که مثلا اگه 5 بار بیشتر سعی کردو نشد کلا بیخیال شه و از تابع بیاد بیرون .
dec di ; decrement error counter
//بعد باید برگردیم بالا و تابع رو دوباره اجرا کنیم دیگه درسته ؟ وقتشه که متغییرهای ثباتها مون رو هم برگردونیم توشون که همه چی عین بار اولی که تابع داره اجرا میشه اجرا بشه .
pop cx
pop bx
pop ax
اینجا هم گفتیم که اگه دی آی که یکی کمش کردیم صفر نشد برو بالای تابع یک بار دیگه سعی کن بخونی وگرنه
jnz SECTORLOOP ; attempt to read again
//ریست؟
int 18H ; see end of Code for Extera Info
//حالا اگه موفقیت امیز بودخوندن کد ما . ما منتقل میشیم به اینجا
SUCCESS:
//اینجا یه پیامی بما نمایش داده میشه که مثلا آره تونستیم یه چیزایی بخونیم .
mov si, offset Progress
call PrintDelay
//این مقادیر ثباتها رو یه جایی ذخیره میکنیم .
pop cx
pop bx
pop ax
تعداد سکتور هایی که باید بخونیم رو با ب ایکس جمع میکنیم .
ب ایکس آفست ما هست دیگه نه .
Lea SI, bpbBytesPerSector
add bx, WORD PTR[SI] ; queue next buffer
این هم یعنی بریم برای خوندن سکتور بعدی .
inc ax ; queue next sector
loop ReadSectors ; read next sector
ret
;========================================================
; Get start and Size of root directory
; compute size of root directory and store in "cx"
; compute location of root directory and store in "ax"
;=======================================
GetRootDirInfo:
xor cx, cx
xor dx, dx
mov ax, 0020H ; 32 byte directory entry
Lea SI, bpbRootEntries
mul WORD PTR[SI] ; total size of directory
Lea SI, bpbBytesPerSector
div WORD PTR[SI] ; sectors used by directory
mov CX, AX
;mov AL, 2 ; [bpbNumberOfFATs] Get number of FATs (Useually 2)
;Lea SI, bpbSectorsPerFAT
;mul Byte PTR[SI] ; number of FATs * sectors per FAT; get number of sectors
;Lea SI, bpbReservedSectors
;add ax, word Ptr[SI] ; add reserved sectors ==> AX = starting sector of root directory
mov AX, 19
mov DX, AX ; Save Start Point of Data
Add DX, CX
Lea SI, datasector ; use datasector to load Kernel file
mov word Ptr[SI], DX
ret
;==========================================================
;==========================================================
;==========================================================
; Bootloader Entry Point
;=======================================
Begin:
cli
;-------------------- Set Values of DS and ES
mov ax, 07C0H
mov ds, ax
mov es, ax
;--------------------- create stack Segment (Use End of Code Segment for Stack)
mov ax, 0
mov ss, ax
mov sp, 0FFFFH
Lea SI, bsDriveNumber
mov Byte PTR[SI], DL
sti
mov si, offset LOSMessage ; our message to print
call PrintDelay ; call our print function
//تا اینجا که بوت لودر ما بود .
//اینجا برای اینکه کرنلمون رو لود کنیم بایدبریم تو روت دایرکتوری . اونجا اسمشو پیدا کنیم و بعد بببینیم که اولین سکتورش کجاست
;--------------------------------------------------------------
;-------------- Load Root Directory
//یه پیامی نمایش میدیم که اقا ما رسیدیم اینجا
mov si, offset MsgRoot ; our message to print
call PrintDelay ; call our print function
//بعد اطلاعات روت دایرکتوری رو محاسبه میکنیم . اینکه روت دایرکتوری تو سیستم فایل فت 12 کجاست دقیقا و از کدوم سکتور شروع میشه .
call GetRootDirInfo ; Get Root directory Info(CX= size , AX= Start)
//اینجا هم یه فضایی هست که روت دایرکتوریمون رو میخواهیم لود کنیم توش .
//بیاریمش تو رم .
mov BX, 0200H ; Destination of Root directory
//حالا شروع میکنیم به خوندن روت دایرکتوری از روی فلاپی دیسک و منتقلا کردن اون روی حافظه رم .
call ReadSectors ;Load root directory to Destination
;--------------------------------------------------------------
;-------------- browse root directory for Kernel
//بعد از اینکه روت دایرکتوری رو وارد رم کردیم حالا توش شروع میکنیم به جستجو کردن دنبال کرنلمون
//باید تمام ورودی های روت دایرکتوریموون رو بگردیم دیگه درسته ؟
//چند تا ورودی داشت ؟ ورودی تو متغییر روت انتریز وجودداره .
//خیلی راحت اون رو توی ثیابت س ایکس قرار میدیم و از س ایکس بعنوان یک شماره استفاده میکنیم
//تا از اول تا اخر این ورودی ها رو چک کنیم و فایل کرنلمون رو پیدا کنیم .
Lea SI, bpbRootEntries
mov cx, WORD PTR[SI] ; load loop counter
//روت دایرکتوری رو تا حاقظه رم تو ادرس 200 قرار داده بودیم دیگه درسته ؟
//خیلی خوب پس اولین ورودی ما باید اینجا باشه .
mov di, 0200H ; locate first root entry(7C00:0200)
_LOOP:
//مقدار س ایکس و فعلا ذخیره میکنیم
push cx
//11کاراکتر ؟ ! این اندازه اسم فایل ماست که باید تو این ورودی ها بگردیم دنبالش .
mov cx, 000BH ; eleven character name
//اسم کرنلمون رو دریافت میکنیم
Lea si, KernelName ; Kernel name to find
//مقدار دی آی رو ذخیره میکنیم
push di
حالا با دستور زیر بصورت اتوماتیک کاراکتر به کاراکتر جایی که اس آی داره اشاره میکنه با جایی که دی آی داره اشاره میکنه با هم مقایسه میشن
//این کار به تعداد س ایکس تکرار میشه . برای همین بود که ما 11 رو تو س ایکس قرار دادیم .
rep cmpsb ; test for entry match
;compare DI and SI for CX Character(Byre)
//ادرس ورودی تو دی آی بود دیگه درسته . اون بازیابی میکنیم
pop di
//حالا اگه کرنل ما پیدا شد بریم برای خوندن بقیه فایل برای این کار باید بریم توجدول فت
je LOAD_FAT
//اگه پیدا نکردیم تو این انتری
//س ایکسی که اول پوش کردیم (تعداد انتری ها ) اینو اول بازیابی میکنیم . تا وقتی این پایین لوپ رو استفاده کردیم اتوماتیک یکی ازش کم بشه م
pop cx
//قبلش باید مشخص کنیم که ورودی بعدی رو بریم بخونیم .
//خوب ورودی بعدی کجاست ؟ 32 بایت اونورتر !
//هر کدوم از ورودی های روت دایرکتوری 32 بایت میگیرن
//برای همین وقتی ما آدرس ورودی اول رو که 200 بود با 32 جمع کنیم میرسیم اول ورودی دو.
add di, 0020H ; queue next directory entry
//حالا برو اول تابع و دوباره بگرد.
loop _LOOP
//تمام روت دایرکتوری ها رو گشتی و باز کرنل رو پیدا نکردی ؟
//خوب هنگ کن دیگه!
jmp hang
;--------------------------------------------------------------------
;--------------- Load FAT
LOAD_FAT:
//خوب اینهم از جدول فت و اینجا که ریسدیم یه پیام به خودمون نشون میدیم که آی ملت با موفقیت رسیدیم اینجا .
mov si, offset MsgFat12 ; our message to print
call PrintDelay ; call our print function
//اینجا هم کلاستر شروع کرنل رو توی د ایکس قرارمیدیم . کلاستر اغازین کرنل کجاست ؟
//دی آی که آدرس اسم کرنل رو داشت (همون ورودی روت دایرکتوری ) حالا اگه 26 به این اضافه کنیم چی میشه ؟ میشه کلاستر شروع کرنل .
mov dx, WORD PTR[di + 001AH] ; DX= starting cluster of Kernel(Byte 26 and 27)
; di= start address of Kernel Name in Ram
; 1AH(26)= Start address of first Cluster of Kernel
; [di + 0x001A] = start Point of boot image in RootDir
Lea SI, cluster
mov WORD PTR[SI], dx ; file's first cluster
xor ax, ax
mov AX, 0002H ;SI=[bpbNumberOfFATs=2] number of FATs
; compute size of FAT and store in "cx"
;Lea SI, bpbSectorsPerFAT
;mul WORD PTR[bpbSectorsPerFAT] ; sectors used by FATs
mov cx, 18;ax
mov ax,1; [bpbReservedSectors] ; compute location of FAT and store in "ax"
; read image file into memory (07C0:1000)
mov ax, 1000H
mov es, ax ; destination for Kernel
mov bx, 0000H ; destination for Kernel
push bx
;---------------------------------------------------------------------------------
;--------------- Load KERNEL Binary Data
mov si, offset MsgKernel ; our message to print
call PrintDelay ; call our print function
LOAD_IMAGE:
Lea SI, cluster
mov ax, WORD PTR[SI] ; cluster to read
pop bx ; buffer to read into
call ClusterLBA ; convert cluster to LBA: Get Sector Number
mov CX, 0001H ;[bpbSectorsPerCluster=1]: Sectors to be Read
call ReadSectors
push bx
; compute next cluster
Lea SI, cluster
mov ax, WORD PTR[SI] ; identify current cluster
mov cx, ax ; copy current cluster
mov dx, ax ; copy current cluster
shr dx, 0001H ; divide by two
add cx, dx ; sum for (3/2)
mov bx, 0200H ; location of FAT in memory
add bx, cx ; index into FAT
mov dx, WORD PTR[bx] ; read two bytes from FAT
test ax, 0001H
jnz ODD_CLUSTER
EVEN_CLUSTER:
and dx, 0000111111111111b ; take low twelve bits
jmp _DONE
ODD_CLUSTER:
mov CL , 4
shr dx , CL ; take high twelve bits
_DONE:
Lea SI, cluster
mov WORD PTR[SI], dx ; store new cluster
cmp dx, 0FF0H ; test for end of file
jb LOAD_IMAGE
ORG 510
DB 55H ;Boot_Signiture
DB 0AAH ;Boot_Signiture
Code Ends
End start
;http://www.storagereview.com/guide/clustFragmentation.html
;--------------------------------------------------------------
;Int 18H
;INT 18h? traditionally jumped to an implementation of BASIC stored in ROM.
;This call would typically be invoked if the BIOS was unable
;to identify any bootable volumes on startup.
;(At the time the original IBM PC was released in 1981,
;the BASIC in ROM was a key feature.)
;As time went on and BASIC was no longer shipped on all PCs,
;this interrupt would simply display an error message indicating
;that no bootable volume was found(famously, "No ROM BASIC",
;or more self-explanatory messages in later BIOS versions)
; in other BIOS versions it would prompt the user to insert
;a bootable volume and press a key, and then after the user did
; so it would loop back to the bootstrap loader to try booting again.
;http://en.wikipedia.org/wiki/BIOS_interrupt_call
;---------------------------------------------------------------
;Here are two recommended changes to the O/S boot sector code
; in order to enhance the booting capabilities of the BIOS.
;D.1 Use DL for Drive Number Use the drive number passed in
;the DL register by the BIOS when control is
;transferred to the boot sector for INT 13h accesses
;to load the O/S, instead of having the drive number hard-coded.
; This would allow booting from drives other than just
;00h (A:) and 80h (C:).
;D.2 INT 18h on Boot Failure
;If an O/S is either not present, or otherwise not able to load, execute an INT 18h
;instruction so that control can be returned to the BIOS. Currently, hard drive boot
;sectors do this, but floppy diskette boot sectors execute an INT 19h instead of INT
;18h. The BIOS Boot Specification defines INT 18h as the recovery vector for failed
;boot attempts. Both of these solutions should be backward compatible
; with previous BIOS and O/S versions.
سلام
اینکه برای نوشتن باید مشخصات سکتور فیزیکی تبدیل به منطقی بشه
(یعنی عکس عمل خواندن)چجوری باید اینکارو انجام بدیم؟یعنی همین تبدیل رو؟ :o
توی همین بوت لودر شماره 3 باید فرمت کردن هارد رو هم قرار بدیم ؟و همچنین نوشتن در هارد؟
اخه من فک میکردم فقط همون طراحی منو رو باید اضافه کنیم!
البته جواب سوالمو هم میدونم هم نمیدونم!پس بهتره بپرسم!!!!
ویرایش توسط Hossein : 28th December 2012 در ساعت 12:14 PMدلیل: ترکیب پستها
سلام
اینکه برای نوشتن باید مشخصات سکتور فیزیکی تبدیل به منطقی بشه
(یعنی عکس عمل خواندن)چجوری باید اینکارو انجام بدیم؟یعنی همین تبدیل رو؟ :o
توی همین بوت لودر شماره 3 باید فرمت کردن هارد رو هم قرار بدیم ؟و همچنین نوشتن در هارد؟
اخه من فک میکردم فقط همون طراحی منو رو باید اضافه کنیم!
البته جواب سوالمو هم میدونم هم نمیدونم!پس بهتره بپرسم!!!!
اون فرمولهای LBA و CHS یی که در سورس کد اومده و توضیح دادم برای همین تبدیلها استفاده میشن . توضیحات در مورد LBA و CHS رو بخونید لطفا
علاقه مندی ها (Bookmarks)