FREEDOS 的BOOT.ASM文件詳細說明 |
|
jackkcg
站務副站長 發表:891 回覆:1050 積分:848 註冊:2002-03-23 發送簡訊給我 |
此為轉貼資料 http://depos.patching.net/showart.asp?art_id=82&cat_id=5 FREEDOS 的BOOT.ASM文件詳細說明 本文件主要用來寫引導磁區程式。這個程式引導了整個DOS系統到記憶體。
;
; file:
; boot.asm
; Description:
; DOS-C boot
;
; Copyright (c) 1997;
; Svante Frey
; All Rights Reserved
;
; ;Þfine ISFAT12 1
;Þfine ISFAT16 1
;Þfine CALCPARAMS 1
;Þfine MULTI_SEC_READ 1 ;代碼段開始。
segment .text
;巨集定義,指向基底位址
Þfine BASE 7c00h
;定義執行文件開始運行的位置.
org BASE
;程式的入口,然後跳到real_start開始運行.
Entry: jmp short real_start
;一條空指令
nop
;
;初始化基址寄存器指標BP指向地址:7c00h
;巨集定義參數表。因爲JMP指令佔用兩個位元組,NOP指令佔用一個位元組。
;所以偏移地址就從BP 3開始,就是從0000:7C03開始。
Þfine bsOemName bp 3 ; OEM標置.
Þfine bsBytesPerSec bp 0bh ; 每磁區位元組數.
Þfine bsSecPerClust bp 0dh ; 每簇磁區數
Þfine bsResSectors bp 0eh ; 保留磁區數。
Þfine bsFATs bp 10h ; FAT數目。
Þfine bsRootDirEnts bp 11h ; 根目錄登記項數。
Þfine bsSectors bp 13h ; 總磁區數
Þfine bsMedia bp 15h ; 磁片介質描標誌: fd=2side9sec, etc...
Þfine sectPerFat bp 16h ; 每個FAT的磁區數。
Þfine sectPerTrack bp 18h ; 每道磁區數
Þfine nHeads bp 1ah ; 磁頭數
Þfine nHidden bp 1ch ; 隱藏磁區數
Þfine nSectorHuge bp 20h ; 磁片磁區數# sectors if > 65536
Þfine drive bp 24h ;驅動器的數位
Þfine extBoot bp 26h ; 擴展引導的標誌。
Þfine volid bp 27h ;卷ID。
Þfine vollabel bp 2bh ;卷標。
Þfine filesys bp 36h ;文件系統。 Þfine LOADSEG 0060h Þfine FATBUF 4000h ; offset of temporary buffer for FAT
; chain ; Some extra variables
;臨時變數保存位置。
Þfine StoreSI bp 3h ;temp store ; To save space, functions that are just called once are
; implemented as macros instead. Four bytes are saved by
; avoiding the call / ret instructions.
; GETDRIVEPARMS: Calculate start of some disk areas.
; 計算磁片區的開始位置,主要根據下面的公式來算。。
; 文件起始區磁區號 = 隱藏磁區數 保留磁區數 每個FAT的磁區數X FAT數目
; 根目錄登記專案/( 每磁區位元組數/32)。
;
;巨集定義開始。
%macro GETDRIVEPARMS 0
;算出:隱藏磁區數 保留磁區數
;得到隱藏磁區數。
;低字送到SI。
mov si, word [nHidden]
;高字送到DI。
mov di, word [nHidden 2]
;加保留磁區數到SI。
add si, word [bsResSectors]
;調整DI進位元標誌。DI:SI爲每個FAT開始位置磁區。
adc di, byte 0 ; DI:SI = first FAT sector
;保存FAT表的開始位置。
mov word [fat_start], si
mov word [fat_start 2], di
;算出:每個FAT的磁區數X FAT數目
;
;得到FAT的數目。用來
mov al, [bsFATs]
;清空AH。
xor ah, ah
;算出總共有多少個磁區。
mul word [sectPerFat] ; DX:AX = total number of FAT sectors
;算出第一個根目錄DI:SI位置。
add si, ax
adc di, dx ; DI:SI = first root directory sector
;保存根目錄的位置。
mov word [root_dir_start], si
mov word [root_dir_start 2], di ; Calculate how many sectors the root directory occupies.
;計算根目錄總共有多少磁區。
;得到每磁區有多少位元組。
mov bx, [bsBytesPerSec]
;設置CL = 5。
mov cl, 5 ; divide BX by 32
;BX除以32,因爲DOS目錄是32位元組長,
;BX就是每個磁區有多少個目錄。
shr bx, cl ; BX = directory entries per sector
;得到根目錄有多少個入口
mov ax, [bsRootDirEnts]
xor dx, dx
div bx
;得到每個根目錄的磁區數。
mov word [RootDirSecs], ax ; AX = sectors per root directory
;得到DI:SI爲指向第一個資料磁區。
add si, ax
adc di, byte 0 ; DI:SI = first data sector
;保存第一個資料磁區的位置。
mov [data_start], si
mov [data_start 2], di
%endmacro ;----------------------------------------------------------------------- times 3Eh-$ $$ db 0 Þfine tempbuf bp 3Eh
dw LOADSEG %ifdef CALCPARAMS
Þfine RootDirSecs bp 27h ; # of sectors root dir uses Þfine fat_start bp 29h ; first FAT sector Þfine root_dir_start bp 2Dh ; first root directory sector Þfine data_start bp 31h ; first data sector %else
Þfine RootDirSecs bp 40h ; # of sectors root dir uses
dw 0 Þfine fat_start bp 42h ; first FAT sector
dd 0 Þfine root_dir_start bp 46h ; first root directory sector
dd 0 Þfine data_start bp 4Ah ; first data sector
dd 0
%endif ;-----------------------------------------------------------------------
; 程式入口處
;----------------------------------------------------------------------- real_start: ;清中斷
cli
;清爲DF標置爲0,執行串操作時,使地址按遞增方向變化
cld
; mov ax, cs
;清ax爲0.
xor ax, ax
; 初始化堆疊段,ss爲0.
mov ss, ax
;清ds爲0.
mov ds, ax
;指向地址0000:7c00h處.
mov bp, 7c00h
;把7BE0H的所在的地址送給sp.0000:7BE0H作爲棧區開始位置。
lea sp, [bp-20h]
;使開中斷.
Sti
; int 13h ; 重定驅動器.
; int 12h ; get memory available in AX
;設置AX = 480,爲1E0H。
mov ax, 480
;計算要把自己移動高端記憶體的位址。
;設置CL= 6,移位6次。
mov cl, 6
;左移6位.結果AX = 7800H
shl ax, cl
;AX = 7020H
sub ax, 07e0h
;設置ES = 7020H
mov es, ax
;設置SI=7C00H
mov si, bp
;設置DI = 7C00H
mov di, bp
;移動256個字,就是說512位元組.
mov cx, 256
;開始移動,從0000:7C000移到7020:7C00.
rep movsw
;保存ES=7020H到棧區。
push es
;得到cont偏移地址
mov bx, cont
;保存到棧區
push bx
;調用一條段間返回指令,結果是IP = cont的位址,CS = ES = 7020H,
;然後跳到7020:cont地方執行,就是剛才的代碼移動到的新地方繼續運
;行原來的程式。
retf
;下面是繼續執行原來的程式。
;設置資料段DS = 7020H,爲現在代碼所在的資料段地址。
cont: mov ds, ax;
;設置SS = AX = 7020爲棧段地址。
mov ss, ax
;因爲BIOS保存總共有多少個驅動器數目在DL中,所以保存BP 24H處。
mov [drive], dl ; BIOS passes drive number in DL
;調用螢幕輸出子過程。
call print
db "Loading FreeDOS...",13,10,"ROOT",0
;如果定義了計算參數,就包含進來。
%ifdef CALCPARAMS
;計算資料磁區的開始位置。
GETDRIVEPARMS
%endif ; 搜索根目錄下的文件。
; FINDfile: Searches for the file in the root directory.
;
; 返回Returns:
; AX = first cluster of file
; First, read the whole root directory
; into the temporary buffer.
;得到根目錄開始磁區。
mov ax, word [root_dir_start]
mov dx, word [root_dir_start 2]
;得到根目錄的磁區數。
mov di, word [RootDirSecs]
xor bx, bx
;得到臨時目錄地址。
mov es, [tempbuf]
;調用讀磁片子過程。
call readDisk
;如果出錯就跳到出錯處理。
jc jmp_boot_error xor di, di
;比較檔案名"KERNEL SYS",
next_entry: mov cx, 11
mov si, filename
push di
repe cmpsb
pop di
mov ax, [es:di 1ah] ; get cluster number from directory entry
;如檔案名相等就跳走。
je ffDone
;如果第一個不是KERNEL.SYS就到下一個目錄入口。
add di, byte 20h ; go to next directory entry
cmp byte [es:di], 0 ; if the first byte of the name is 0,
jnz next_entry ; there is no more files in the directory
;讀KERNEL.SYS出錯。
jc boot_error ; fail if not found
ffDone:
;如果找到KERNEL.SYS目錄,
call print
db " FAT",0
; 下面得到FAT鏈表。
; GETFATCHAIN:
;
; Reads the FAT chain and stores it in a temporary buffer in the first
; 64 kb. The FAT chain is stored an array of 16-bit cluster numbers,
; ending with 0.
;
; The file must fit in conventional memory, so it cant be larger than
; 640 kb. The sector size must be at least 512 bytes, so the FAT chain
; cant be larger than around 3 kb.
;
; Call with: AX = first cluster in chain push ax ; store first cluster number ; Load the complete FAT into memory. The FAT cant be larger
; than 128 kb, so it should fit in the temporary buffer. mov es, [tempbuf]
xor bx, bx
mov di, [sectPerFat]
mov ax, word [fat_start]
mov dx, word [fat_start 2]
call readDisk
pop ax ; restore first cluster number
jmp_boot_error: jc boot_error ; Set ES:DI to the temporary storage for the FAT chain.
push ds
push es
pop ds
pop es
mov di, FATBUF next_clust: stosw ; store cluster number
mov si, ax ; SI = cluster number
; cmp byte ptr extBoot, 29h
; jne fat_12
; cmp byte ptr filesys[4], 6 ; check for FAT-16 system
; je fat_16
;如果是FAT12處理。
%ifdef ISFAT12
; This is a FAT-12 disk. fat_12: add si, si ; multiply cluster number by 3...
add si, ax
shr si, 1 ; ...and divide by 2
lodsw ; If the cluster number was even, the cluster value is now in
; bits 0-11 of AX. If the cluster number was odd, the cluster
; value is in bits 4-15, and must be shifted right 4 bits. If
; the number was odd, CF was set in the last shift instruction. jnc fat_even
mov cl, 4
shr ax, cl ; shift the cluster number fat_even: and ah, 0fh ; mask off the highest 4 bits
cmp ax, 0fffh ; check for EOF
jb next_clust ; continue if not EOF %endif
;如果是FAT16處理。
%ifdef ISFAT16
; This is a FAT-16 disk. The maximal size of a 16-bit FAT
; is 128 kb, so it may not fit within a single 64 kb segment. fat_16: mov dx, [tempbuf]
add si, si ; multiply cluster number by two
jnc first_half ; if overflow...
add dh, 10h ; ...add 64 kb to segment value first_half: mov ds, dx ; DS:SI = pointer to next cluster
lodsw ; AX = next cluster cmp ax, 0fff8h ; >= FFF8 = 16-bit EOF
jb next_clust ; continue if not EOF
%endif finished: ; Mark end of FAT chain with 0, so we have a single
; EOF marker for both FAT-12 and FAT-16 systems. xor ax, ax
stosw push cs
pop ds call print
db " KERNEL",0 ; loadfile: Loads the file into memory, one cluster at a time.
; 讀文件進入記憶體,一次讀一簇。 mov es, [tempbuf] ; set ES:BX to load address
xor bx, bx mov si, FATBUF ; set DS:SI to the FAT chain
;
cluster_next: lodsw ; AX = next cluster to read
or ax, ax ; if EOF...
je boot_success ; ...boot was successful dec ax ; cluster numbers start with 2
dec ax mov di, word [bsSecPerClust]
and di, 0ffh ; DI = sectors per cluster
mul di
add ax, [data_start]
adc dx, [data_start 2] ; DX:AX = first sector to read
call readDisk
jnc cluster_next
boot_error: call print
db 13,10,"BOOT error!",0 jmp short $ ; sleep boot_success: call print
db " GO!",13,10,0
mov bl, [drive]
;跳到 0000:0060執行。????
jmp word LOADSEG:0
;調用這個子過程來從螢幕輸出文字提示資訊。
;
;保存SI 到 StoreSI = BP 3.
print: mov [StoreSI], si
;得到字串的首位址,因用CALL指令調用時,把下一條指令壓入棧,
;就是說這個位址就是指跟在CALL PRINT後面的字串位址。
pop si
;保存返回段地址 。
push ax
;保存返回的指令開始執行的位址。
push bx
;保存基址指標。
push di
;清空BX爲0 。
xor bx, bx ; video page 0
;調用BIOS顯示中斷10H中的功能0EH。
mov ah, 0Eh ; else print it
;裝入顯示的字串。把位元組送到 AL = 要示的位元組,SI增加1。
print1: lodsb ; get token
;比較是否到了字串的結束位置。
cmp al, 0 ; end of string?
;如果是字串結束就跳到print2運行。
je print2 ; if so, exit
;如果沒有結束就繼續調用BIOS中斷10H顯示。
int 10h ; via TTY mode
;直到顯示完成。
jmp short print1 ; until done
;設置返回的地址。
print2: pop di
pop bx
pop ax
;由於執行完LODSB指令後,SI就指向字串後面的指令。
;所以把它壓入棧區,以便返回時執行下條指令。
push si ; stack up return address
;取回原來的SI。
mov si, [StoreSI]
;跳到字串後面的指令
ret ; and jump to it ;
;讀磁片磁區資料到記憶體。
; readDisk: Reads a number of sectors into memory.
;
; Call with: DX:AX = 32-bit DOS 邏輯磁區號。
; DI = 要讀幾個磁區數。
; ES:BX = 讀出的資料放到的緩衝區位址。
; ES 必需是 64k 爲邊界(1000h, 2000h etc).
;
; Returns: CF set on error
; ES:BX points one byte after the last byte read.
;保存SI。
readDisk: push si
;保存DX
read_next: push dx
;保存AX
push ax
;
;轉換DOS邏輯磁區參數爲BIOS參數的形式。
;
;
;絕對磁區號 = 物理磁區號
; 物理磁頭號 * 每道磁區數
; 物理磁軌號 * 每道磁區數 * 磁頭數
;所以算出BIOS參數用下面的公式:
;(絕對磁區號 - 物理磁區號)/每道磁區數 = 物理磁頭號
; 物理磁軌號 * 磁頭數 ;(絕對磁區號 / 每道磁區數)的 商 等於
;物理磁頭號 物理磁軌號 * 磁頭數,(商在AX中)。
;絕對磁區號 mod 每道磁區數 等於 物理磁區號,(餘數在DX中)。
div word [sectPerTrack]
mov cx, dx ;
; (AX) = 物理磁頭號 物理磁軌號 * 磁頭數
;
;AX中有除法後商,DX中有除法後的餘數。
; 物理磁軌號 = (AX) / 磁頭數 (ax has track)
; 物理磁頭號 = (AX) mod磁頭數 (dl has head)
;
xor dx, dx
div word [nHeads] ; the following manipulations are necessary in order to
; properly place parameters into registers.
;下面處理合適參數到寄存器。
; ch = 磁片柱面號的低 8 位。
; cl = 7-6: 磁片柱面號的高 2 位
; 5-0: sector
mov dh, dl ; 磁頭號。
ror ah, 1 ; move track high bits into
ror ah, 1 ; bits 7-6 (assumes top = 0)
xchg al, ah ; swap for later
mov dl, byte [sectPerTrack]
sub dl, cl
inc cl ; 起始的磁區號。
or cx, ax ; merge cylinder into sector
mov al, dl ; al has # of sectors left %ifdef MULTI_SEC_READ
;如果定義讀多磁區
;計算有多少磁區能傳輸,因爲DMA存邊界限制。
; Calculate how many sectors can be transfered in this read
; due to dma boundary conditions.
push dx mov si, di ; temp register save
; this computes remaining bytes because of modulo 65536
; nature of dma boundary condition
mov ax, bx ; get offset pointer
neg ax ; and convert to bytes
jz ax_min_1 ; started at seg:0, skip ahead xor dx, dx ; convert to sectors
div word [bsBytesPerSec] cmp ax, di ; check remainder vs. asked
jb ax_min_1 ; less, skip ahead
mov si, ax ; transfer only what we can ax_min_1: pop dx ; Check that request sectors do not exceed track boundary
mov si, [sectPerTrack]
inc si
mov ax, cx ; get the sector/cyl byte
and ax, 03fh ; and mask out sector
sub si, ax ; si has how many we can read
mov ax, di
cmp si, di ; see if asked <= available
jge ax_min_2
mov ax, si ; get what can be xfered ;ax_min_2: mov ah, 2
ax_min_2: push ax
mov ah, 2
mov dl, [drive]
int 13h
pop ax
%else ;讀入一個磁區。
;調用BIOS中斷 13h讀功能。
mov ax, 0201h
mov dl, [drive]
int 13h
%endif
jnc read_ok ; 如果沒有錯就跳到讀完。
xor ah, ah ; 否則就重定軟盤機
int 13h
pop ax
pop dx ; 試著再讀一次這個磁區。
jmp short read_next ; read_ok:
%ifdef MULTI_SEC_READ
; cbw
; mov si, ax
; mul word ptr bsBytesPerSec ; add number of bytes read to BX
mul word [bsBytesPerSec] ; add number of bytes read to BX
add bx, ax
%else
add bx, word [bsBytesPerSec]
%endif
jnc no_incr_es ; if overflow... mov ax, es
add ah, 10h ; ...add 1000h to ES
mov es, ax no_incr_es: pop ax
pop dx ; DX:AX = last sector number ; call print
; db ".",0 %ifdef MULTI_SEC_READ
add ax, si
adc dx, byte 0 ; DX:AX = next sector to read
sub di,si ; if there is anything left to read,
jg read_next ; continue
%else
add ax, 1
adc dx, byte 0 ; DX:AX = next sector to read
dec di ; if there is anything left to read,
jnz read_next ; continue
%endif clc
pop si
ret
;要讀入內核的檔案名。
filename db "KERNEL SYS" times 01feh-$ $$ db 0 sign dw 0aa55h *********************************************************
哈哈&兵燹
最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好 Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知
K.表Knowlege 知識,就是本站的標語:Open our mind to make knowledge together!
希望能大家敞開心胸,將知識寶庫結合一起
------
********************************************************** 哈哈&兵燹 最會的2大絕招 這個不會與那個也不會 哈哈哈 粉好 Delphi K.Top的K.Top分兩個字解釋Top代表尖端的意思,希望本討論區能提供Delphi的尖端新知 K.表Knowlege 知識,就是本站的標語:Open our mind |
本站聲明 |
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。 2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。 3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇! |