制盘命令

ddatsh

dev #asm asm
# 空的 1.44M 的软盘镜像文件
dd if=/dev/zero of=bootsect.img bs=512 count=2880

# 格式化软盘镜像文件为 FAT12 文件系统
mkfs.msdos -F 12 bootsect.img

nasm bootsect.asm -o bootsec
nasm setup.asm -o SETUP

mkdir a
mount -t vfat -o loop bootsect.img a/
cp bootsect a/
sync
umount a/

# 拷贝引导加载程序到软盘镜像的第一个扇区
dd if=bootsect.img of=bootloader skip=1 seek=1 bs=512 count=2879
;本引导程序加载从装有fat12文件系统的软盘中加载setup程序到内存,并执行setup程序

bits 16

org 0000h

start:
	jmp main

;设置BIOS Parameter Block (BPB)
db 90h ;注意这里必须填充1个字节,由于jmp指令只占2个字节
bpbName					DB "xxxxxxxx" ;这里只能是8个字节
bpbBytesPerSector:  	DW 512
bpbSectorsPerCluster: 	DB 1
bpbReservedSectors: 	DW 1
bpbNumberOfFATs: 	DB 2
bpbRootEntries: 	DW 224
bpbTotalSectors: 	DW 2880
bpbMedia: 		DB 0xF0
bpbSectorsPerFAT: 	DW 9
bpbSectorsPerTrack: 	DW 18
bpbHeadsPerCylinder: 	DW 2
bpbHiddenSectors: 	DD 0
bpbTotalSectorsBig:     DD 0
bsDriveNumber: 	        DB 0
bsUnused: 		DB 0
bsExtBootSignature: 	DB 0x29
bsSerialNumber:	        DD 0xa0a1a2a3
bsVolumeLabel: 	        DB "MOS FLOPPY"
bsFileSystem: 	        DB "FAT12"

;------------------------------------------------------------------
;打印消息,ds:[si]存放显示字符串的起始地址,字符串以0结尾
;使用10h系统调用,功能号为0eh,写字符到光标位置,光标进一
;al=预读字符
;bh=页号
;bl=前景颜色(图形模式)
;------------------------------------------------------------------
PrintMesg:
	lodsb 
	or al, al
	jz pmo 
	;调用bios显示字符
	mov ah, 0eh
	int 10h 
	jmp PrintMesg
pmo:
	ret

;------------------------------------------------------------------
;引导程序
;------------------------------------------------------------------
main:
	;让所有段寄存器的值为7c0h
	cli
	mov ax, 07c0h
	mov ds, ax
	mov es, ax
;	mov fs, ax
;	mov gs, ax

	;用于堆栈
	mov ax, 0000h
	mov ss, ax
	mov sp, 0ffffh;
	sti

	;加载根目录	
LoadRoot:
	mov si, loadRootD 
	call PrintMesg
	
	;计算根目录的起始扇区
	xor dx, dx
	mov ax, word [bpbSectorsPerFAT]
	xor cx, cx
	mov cl, byte [bpbNumberOfFATs]
	mul cx
	add ax, word [bpbReservedSectors] 
	mov word [rootDStartS], ax
	;计算根目录占扇区的长度
	xor dx, dx
	mov ax, 020h 
	mul word [bpbRootEntries]
	div word [bpbBytesPerSector] 
	or dx, dx
	jz StoreRootDlenS
	inc ax
StoreRootDlenS:
	mov word [rootDLenS], ax
	mov ax, word[rootDStartS]
	;将根目录读入es:rootDOff内存中 
	mov cx, word [rootDLenS]
	mov dx, ax
	add dx, cx
	mov word [DataStartS], dx
	mov bx, rootDOff 
	call ReadSectors
	;查找setup文件名
	mov cx, word [bpbRootEntries]
	mov bx, rootDOff 
FindSetup:
	push cx
	mov si, setupName   
	mov di, bx	
	mov cx, 11
	repe cmpsb
	jz GetCluster
	add bx, 20h
	pop cx
	loop FindSetup 
	;查了整个目录都没找到setup文件 
	mov si, setupNotFind 
	call PrintMesg
	int 18h
GetCluster:
	mov ax, word [bx + 001ah]	
	mov	word [setupCluster], ax
LoadFAT:
	mov ax, word [bpbReservedSectors]
	mov cx, word [bpbSectorsPerFAT]
	mov bx, fatOff 
	call ReadSectors
loadSetup:
	mov bx, setupBase
	mov es, bx
	mov bx, setupOff 
LoadSetupLoop:
	mov ax, word [setupCluster] 
	call ClusterToLba	
	xor cx, cx
	mov cl, byte [bpbSectorsPerCluster]
	call ReadSectors
	;读取下一个族
	mov ax, word [setupCluster]
	mov cx, ax
	shr cx, 01h
	add cx, ax ; 3/2个字节
	mov bx, fatOff
	add bx, cx
	mov dx, word [bx]	
	test ax, 0001h
	jz even
	and dx, 0fffh
	jmp NextCluster
even:
	shr dx, 04h
NextCluster:
	mov [setupCluster], dx
	cmp dx, 0ff0h
	jnb RunSetup
	xor ax, ax
	mov al, byte [bpbSectorsPerCluster] 
	mul word [bpbBytesPerSector]
	mov bx, setupOff
	add bx, ax
	jmp LoadSetupLoop
RunSetup:
	jmp setupBase:setupOff
	
;------------------------------------------------------------------
;将族号(从第0个开始)转换为扇区的逻辑号,ax存放要转换的族号,输出ax存放扇区号
;------------------------------------------------------------------
ClusterToLba:
	push bx
	push dx	
	sub ax, 0002h
	xor dx, dx
	mov bl, byte [bpbSectorsPerCluster]
	mov bh, 0 
	mul bx 
	add ax, word [DataStartS]
	pop dx
	pop bx
	ret

;------------------------------------------------------------------
;将逻辑扇区号转换为柱面/磁头/扇区
;逻辑扇区号存放在ax中
;分别存放在absoluteCylinder, absoluteHead, absoluteSector变量中
;对于1.44M的软盘,总共有两面(磁头号0和1),每面80个磁道(磁道号0-79)
;每个磁道有18个扇区(扇区号1-18)
;计算方法:
;	absoluteSector = logicSector % bpbSectorsPerTrack + 1
;	absoluteHead = (logicSector / bpbBytesPerSector) % bpbHeadsPerCylinder
;	absoluteCylinder = (logicSector / bpbBytesPerSector) / bpbHeadsPerCylinder
;------------------------------------------------------------------
LbaToCHS:
	push dx
	xor dx, dx
	div word [bpbSectorsPerTrack]
	inc dx
	mov word [absoluteSector], dx
	xor dx, dx
	div word [bpbHeadsPerCylinder]
	mov word [absoluteHead], dx
	mov word [absoluteCylinder], ax
	pop dx
	ret
;------------------------------------------------------------------
;ax为读取扇区的逻辑号的起始位置,cx中存放要读取扇区的个数,es:bx
;存放将扇区读入内存的起始地址
;使用13h系统调用,功能号为2
;dl=驱动器号	dh=低6位是磁头号,高2位为磁道(柱面)号的10、11位
;ch=磁道(柱面)号的0-7位
;cl=低6位是扇区号,高2位为磁道(柱面)号的8、9位
;al=读取的扇区数
;es:bx=数据的存放地址
;------------------------------------------------------------------
ReadSectors:
	push dx
	push ax
	xor ax, ax
	int 13h
	pop ax
ReadSectorsLoop1:
	or cx, cx
	jz ReadSectorOut
	mov dl, 05h ; 出错尝试的次数
ReadSectorsLoop2:
	push ax
	push cx
	push bx
	call LbaToCHS
	;判断cx是否超过了一个磁道
	mov bx, word [bpbSectorsPerTrack] 
	sub bx, word [absoluteSector] 
	inc bx 
	cmp cl, bl
	jbe StartReadSectors
	mov cl, bl
StartReadSectors:
	mov dl, byte [bsDriveNumber]
	mov dh, byte [absoluteHead] 
	mov ch, byte [absoluteCylinder]
	mov al, cl
	mov cl, byte [absoluteSector]
	mov ah, 02h
	pop bx
	int 13h
	jnc ReadNextSectors
	;复位磁盘
	xor ax, ax
	int 13h
	dec dl
	jnz ReadSectorsLoop2
	mov si, readSectorError
	call PrintMesg
	int 18h
ReadNextSectors:
	xor ah, ah
	push ax
	mul word [bpbBytesPerSector]
	add bx, ax
	pop ax
	pop cx
	pop dx
	sub cx, ax
	add ax, dx
	jmp ReadSectorsLoop1 	
ReadSectorOut:
	pop dx
	ret

loadRootD db "Ld Root Dir", 0dh, 0ah, 0
readSectorError db "Rd Sector Error", 0
setupNotFind db "File Not Find", 0
rootDStartS dw 0000h 
rootDLenS dw 0000h
rootDOff equ 0200h
fatOff equ 0200h
setupBase equ 0900h
setupOff equ 0000h
setupName db "SETUP"
setupCluster dw 0000h
DataStartS dw 0000h

absoluteSector dw 0000h
absoluteCylinder dw 0000h
absoluteHead dw 0000h

times 510 - ($ - $$) db 0
dw 0aa55h
;*********************************************
;	Stage2.asm
;		- Second Stage Bootloader
;
;	Operating Systems Development Series
;*********************************************

org 0x0					; offset to 0, we will set segments later

bits 16					; we are still in real mode

; we are loaded at linear address 0x10000

jmp main				; jump to main

;*************************************************;
;	Prints a string
;	DS=>SI: 0 terminated string
;************************************************;

Print:
	lodsb					; load next byte from string from SI to AL
	or			al, al		; Does AL=0?
	jz			PrintDone	; Yep, null terminator found-bail out
	mov			ah,	0eh	; Nope-Print the character
	int			10h
	jmp			Print		; Repeat until null terminator found
PrintDone:
	ret					; we are done, so return

;*************************************************;
;	Second Stage Loader Entry Point
;************************************************;

main:
	cli					; clear interrupts
	push			cs		; Insure DS=CS
	pop			ds

	mov			si, Msg
	call			Print

	cli					; clear interrupts to prevent triple faults
	hlt					; hault the syst

;*************************************************;
;	Data Section
;************************************************;

Msg	db	"Preparing to load operating system...",13,10,0