⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 fat16-hd.asm

📁 一个类linux的dos下开发的操作系统.
💻 ASM
字号:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; First-stage bootloader for FAT16 (DOS) hard disk partition
; Chris Giese <geezer@execpc.com>, http://www.execpc.com/~geezer/os
;
; INSTALLATION:
; 1. The kernel or second-stage bootloader loaded by this code must
;    meet these restrictions:
;	- Must start running in real mode (16-bit mode)
;	- Must be in binary format (entry point == start of file)
;	- Must be stored in the root directory of the disk
;
; 2. Change the equates/defines below as necessary:
;	equate	default value	what is it?
;	------  -------------   -----------
;	SS_ADR  10000h	    	Load address of kernel/second stage
;			    	- Must be >= 500h
;				- Preferably >= 8600h
;			    	- Must be a multiple of 10h
;
;	SS_ORG	00100h		ORG (origin) value of assembled
;				kernel/second stage. Must be a
;				multiple of 10h
;
;	SS_NAME	"LOADER  BIN"	Name of second-stage code on disk.
;				This name must be in FAT format:
;				- 8-character filename
;				- 3-character extension immediately
;				  following filename
;				- Filename and extension are both
;				  capitalized
;				- Filename and extension are both
;				  padded on the right with spaces
;
; 3. Assemble this file with NASM:
;	nasm -f bin -o fat16-hd.bin fat16-hd.asm
;
; 4. Make a FAT16 filesystem on a hard disk partition:
;	(DOS)	format d:
;	(Linux)	mkdosfs /dev/hda2
;
;
;
; 5. Copy the kernel/second stage to the root directory of
;    the hard disk partition, and name it according to SS_NAME:
;	(DOS)	copy second_stage d:\loader.bin
;	(Linux)	mount /dev/hda2 /mnt
;		cp   second_stage /mnt/loader.bin
;		umount /mnt
;
; 6. Install this first-stage code into the bootsector (sector 0) of
;    the hard disk partition, avoiding the existing BIOS parameter block:
;	(DOS)	partcopy fat16-hd.bin      0        3            -ad
;		partcopy fat16-hd.bin      24       1DC          -ad      24
;	(Linux)	dd    if=fat16-hd.bin skip=0  count=3   of=/dev/hda2
;		dd    if=fat16-hd.bin skip=36 count=476 of=/dev/hda2 seek=36
;
;    I don't recommend using RAWRITE because:
;    - the version of RAWRITE I used was buggy, and trashed sector 1
;      (the first FAT) when I used it to install code in sector 0
;    - RAWRITE can not write partial sectors, so this code will work
;      only if your partition has geometry CHS=261:255:63 (unless the
;      default BIOS parameter block is changed)
;
;    DOS users can get John Fine's PARTCOPY (Partial Copy; PCOPY) here:
;	http://www.execpc.com/~geezer/johnfine
;
; MEMORY MAP:
; real-mode interrupt vectors (IVT):	00000-003FF
; BIOS data segment:			00400-004FF
; (unused memory):			00500-07B7F
; first-stage variables:		07B80-07BFF
; first-stage code (512 bytes):		07C00-07DFF	FS_ADR
; first-stage stack:			07E00-07FFF	(max) FS_ADR + 200h
; first-stage directory buffer:		08000-081FF	FS_ADR + 400h
; first-stage FAT buffer:		08200-085FF	FS_ADR + 600h
; (unused memory):			08600-0FFFF
; default second-stage load address:	10000-
;
; OPERATION:
; If there is an error loading the second stage (LOADER.BIN was not
; found, or there was a disk error), then this code will display a
; short error message, beep, await a key pressed, and re-start the
; boot process via INT 19h.
;
;
; POSSIBLE BUGS:
; - Do I need CLI and STI?
; - Should I set SP to something safe before calling the 2nd stage?
; - Testing:
;   - test on 8088-based machine
;   - test with various disk sizes (different CHS values)
;   - see if it works with large kernel/second stage
;   - see if it works on a hard disk with no partition table
;     and a FAT16 filesystem starting at sector 0
;
;
;
;
;
; TO DO:
; - Make this code smaller?
; - Some parts of this code assume 512 bytes per sector.
;   Is this a problem?
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

SS_ADR	equ	10000h

SS_ORG	equ	100h

%define	SS_NAME	"LOADER  BIN"

;
; "No user-serviceable parts beyond this point" :)
;

FAT16_EOF	equ	0FFF8h

; first-stage address
%ifdef DOS
%define FS_ADR		100h			; .COM file for testing
%else
%define FS_ADR		7C00h
%endif

; 512 bytes for stack
ADR_STACK	equ	(FS_ADR + 400h)

; 512 bytes for one-sector directory buffer
ADR_DIRBUF	equ	(FS_ADR + 400h)


; 512 bytes for one-sector FAT buffer
ADR_FATBUF	equ	(FS_ADR + 600h)

	ORG FS_ADR
start:

; define memory used for scratchpad variables. These are initialized
; at run-time, so there's no need to waste first-stage memory on them.
; Put them just below 'start'

; (1 byte) drive we booted from; 0=A, 80h=C
boot_drive	EQU (start - 1)

; (2 bytes) sector where root directory starts
root_start	EQU (boot_drive - 2)

; (2 bytes) sector where the actual disk data starts
data_start	EQU (root_start - 2)

; (2 bytes) number of 16-byte paragraphs per sector
para_per_sector	EQU (data_start - 2)

; (2 bytes) number of 16-byte paragraphs per cluster
para_per_cluster EQU (para_per_sector - 2)

	jmp short over				; skip over BPB
	nop

; minimal, default BIOS Parameter Block (BPB)

oem_id:			; 03h	not used by this code
	db "GEEZER", 0, 0

bytes_per_sector:	; 0Bh
	dw 512

sectors_per_cluster:	; 0Dh
	db 64		; maximum (2 gig partition)

fat_start:
num_reserved_sectors:	; 0Eh
	dw 1

num_fats:		; 10h
	db 2

num_root_dir_ents:	; 11h
	dw 512

total_sectors:		; 13h	not used by this code
	dw 0

media_id:		; 15h	not used by this code
	db 0F8h

sectors_per_fat:	; 16h
	dw 256		; maximum (2 gig partition)

sectors_per_track:	; 18h
	dw 63		; maximum (2 gig partition)

heads:			; 1Ah
	dw 255		; maximum (2 gig partition)

hidden_sectors:		; 1Ch
	dd 16065	; partition 1 (CHS=1:0:1)

total_sectors_large:	; 20h	not used by this code
	dd 4192965	; CHS=261:255:63 (2 gig partition)

over:
%ifdef DOS
	mov dl,80h
%else
; evidently some buggy BIOSes jump to 07C0:0000, so fix that now
	jmp 0:over2
%endif
over2:
; ...and some BIOSes don't zero DS, so do that as well
	mov ax,cs
	mov ds,ax
	mov ss,ax				; zero SS, too
	mov sp,ADR_STACK
	mov bp,over				; for relative addressing
	cld					; string operations go up

; save [boot_drive] from DL. BIOS sets DL=0 if
; booting from drive A, DL=80h if booting from drive C
	mov [bp - (over - boot_drive)],dl

; compute first sector of root directory
	mov al,[num_fats]			; number of FATs
	cbw					; "mov ah,0"
	mul word [bp - (over - sectors_per_fat)] ; multiply by sectors/FAT
	add ax,[bp - (over - fat_start)]	; plus reserved sectors
	mov [root_start],ax

; compute first sector of disk data area
	mov bx,ax
	mov ax,[num_root_dir_ents]		; entries in root dir
	mov dl,32				; * bytes/entry (assume DH=0)
	mul dx					; == bytes in root dir
	div word [bp - (over - bytes_per_sector)] ; / bytes per sector
	add ax,bx				; = sectors
	mov [data_start],ax

; compute number of 16-byte paragraphs per disk sector
	mov dx,[bp - (over - bytes_per_sector)]
	mov cl,4
	shr dx,cl
	mov [bp - (over - para_per_sector)],dx

; compute number of 16-byte paragraphs per FAT cluster
	mov al,[sectors_per_cluster]
	cbw
	mul dx					; DX still=paragraphs/sector
	mov dx,ax
	mov [bp - (over - para_per_cluster)],dx

; OK, everything is set up for 'find_file', 'walk_fat', and 'read_sectors'
; Find the file named at 'second_stage:' in the root directory
	mov si,second_stage
	call find_file
	jc err					; disk error

; if second-stage file not found, display blinking 'F'
	mov ax,9F46h
	jne err2

; get conventional memory size
	int 12h

; subtract starting second stage address to get available mem
	sub ax,(SS_ADR >> 10)

; convert from K to bytes
	xor dh,dh
	mov dl,ah
	mov ah,al
	xor al,al
	shl ax,1
	rcl dx,1
	shl ax,1
	rcl dx,1

; if second stage file is too big...
	sub ax,[es:di + 28]
	sbb dx,[es:di + 30]

; ...display a blinking 'M'
	mov ax,9F4Dh
	jc err2

; found second-stage, load it
%ifdef DOS
	mov di,ds
	add di,(SS_ADR >> 4)
%else
	mov di,(SS_ADR >> 4)
%endif
load_2nd:
	mov es,di

; convert cluster BX to sector value in DX:AX, and get next cluster in BX
	call walk_fat
	jc err

; read an entire cluster
	xor ch,ch
	mov cl,[bp - (over - sectors_per_cluster)]
	call read_sectors
	jc err
	add di,[bp - (over - para_per_cluster)]	; advance mem ptr 1 cluster
	cmp bx,FAT16_EOF			; EOF cluster value
	jb load_2nd

; jump to second stage that is ORGed to address "SS_ORG"
%ifdef DOS
	mov ax,ds
	add ax,((SS_ADR - SS_ORG) >> 4)
%else
	mov ax,((SS_ADR - SS_ORG) >> 4)
%endif
	mov ds,ax
	mov es,ax
	mov ss,ax
	push ax
	push word SS_ORG
	retf

; disk read error; display blinking 'R'
err:
	mov ax,9F52h
err2:
	mov bx,0B800h				; assumes color emulation
	mov es,bx
	xor bx,bx
	mov [es:bx],ax

; *** BEEEP ***
	mov ax,0E07h
	int 10h

; await key pressed
	mov ah,0
	int 16h

; re-start the boot process
	int 19h

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			find_file
; action:		searches for file in root dir
; in:			11-char all-caps filename at SI, [root_start] set
; out (disk error):	CY=1
; out (file not found):	CY=0, ZF=0
; out (found it):	CY=0, ZF=1, BX=starting cluster of file,
;			ES:DI is left pointing to the directory entry
; modifies:		AX, BX, CX, DX, DI, ES
; minimum CPU:		8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

find_file:
		xor dx,dx
		mov ax,[root_start]		; 1st sector in root dir
%ifdef DOS
		mov bx,ds			; where to load it
		add bx,(ADR_DIRBUF >> 4)
%else
		mov bx,(ADR_DIRBUF >> 4)
%endif
		mov es,bx
find_file_1:
		mov cx,1
		call read_sectors		; one sector at a time
		jc find_file_5			; disk read error
		xor di,di
find_file_2:
		test [es:di],byte 0FFh		; 0 byte = end of root dir
		je find_file_3
		mov cl,11			; 'read_sectors' zeroed CH
		push si
		push di				; compare filename to dirent
			rep cmpsb
		pop di
		pop si
		je find_file_4

; if filename comparision failed, advance 32 bytes to next dir entry
		add di,byte 32
		cmp di,[bp - (over -bytes_per_sector)]
		jb find_file_2
		inc ax
		cmp ax,[bp - (over - data_start)]
		jb find_file_1			; go to next sector of root

find_file_3:
; did not find the file: return with CY=0, ZF=0
		or al,1
find_file_4:
; found the file
		mov bx,[es:di + 26]		; get first cluster of file
find_file_5:
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			walk_fat
; action:		converts cluster value to sector,
;			and gets next cluster of file from FAT
; in:			BX=cluster
; out (disk error):	CY=1
; out (success):	CY=0, DX:AX=first sector of cluster, BX=next cluster
; modifies:		AX, BX, DX
; minimum CPU:		8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

walk_fat:
	push es
	push cx
		mov ax,bx

; cluster 2 is the first data cluster
		dec ax
		dec ax

; convert from clusters to sectors
		mov dh,0
		mov dl,[bp - (over - sectors_per_cluster)]
		mul dx
		add ax,[bp - (over - data_start)]
		adc dx,byte 0

; DX:AX is return value; save it
		push dx
		push ax
%ifdef DOS
			mov ax,ds
			add ax,(ADR_FATBUF >> 4)
%else
			mov ax,(ADR_FATBUF >> 4)
%endif
			mov es,ax

; FAT16 entries are 16 bits, bytes are 8 bits, so multiply by 2
			xor dx,dx
			mov ax,bx
			shl ax,1
			rcl dx,1






; figure out which FAT sectors to load


			div word [bp - (over - bytes_per_sector)]

; remainder is byte offset into FAT; put it in BX
			mov bx,dx

; quotient in AX is FAT sector: add FAT starting sector
			add ax,[bp - (over - fat_start)]
			xor dx,dx

; check the FAT buffer to see if this sector is already loaded
			cmp ax,[bp - (over - curr_sector)]
			je walk_fat_1
			mov [curr_sector],ax



; read the target FAT sector
			mov cx,1
			call read_sectors
			jc walk_fat_4
; get 16 bits from FAT
walk_fat_1:
			mov bx,[es:bx]












; clear CY bit to signal success
			clc
walk_fat_4:
		pop ax
		pop dx
	pop cx
	pop es
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; name:			read_sectors
; action:		reads one or more disk sectors into memory
; in:			ES:0=address of memory where sectors should be read
;			DX:AX=first sector to read
;			CX=number of sectors to read
; out (disk error):	CY=1
; out (success):	CY=0
; modifies:		DX, CX, AX
; minimum CPU:		8088
; notes:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

read_sectors:
	push es
	push bx
		xor bx,bx

; add partition start
		add ax,[bp - (over - hidden_sectors) + 0]
		adc dx,[bp - (over - hidden_sectors) + 2]
read_sectors_1:
		push dx
		push cx
		push ax

; DX:AX=LBA sector number
			push ax
				mov ax,dx
				xor dx,dx

; divide LBA by sectors_per_track (spt).
; Use a two-step 32-bit/16-bit divide to avoid overflow.
; See "9.3.5 Extended Precision Division" in Randall Hyde's "Art of Assembly"
				div word [bp- (over - sectors_per_track)]

; DX=high LBA % spt, AX=high LBA / spt
				mov cx,ax
			pop ax

; DX=high LBA % spt, CX=high LBA / spt, AX=low LBA
; New 32-bit dividend is DX:AX
			div word [bp- (over - sectors_per_track)]

; CX:AX=LBA / spt, DX=LBA % spt
			xchg cx,dx
			inc cx			; CL=sector

; DX:AX=LBA / spt, do normal one-step divide by heads value
			div word [bp - (over - heads)]
			mov dh,dl		; DH=head
			mov ch,al		; CH=cylinder 7:0
			mov dl,[bp - (over - boot_drive)]
			shl cl,1
			shl cl,1		; trying to use only...
			shr ah,1		; ...8088 asm here
			rcr cl,1
			shr ah,1
			rcr cl,1		; CL7:6=cyl 9:8, CL5:0=sector

; now: read one sector
			mov ax,0201h
			int 13h
			jnc read_sectors_3

; disk error; recalibrate/reset drive
			mov ah,0
			int 13h
			jc read_sectors_2
; try the read again. If it fails a second time, give up.
			mov ax,0201h
			int 13h
			jnc read_sectors_3
read_sectors_2:
		pop ax
		pop cx
		pop dx
		jmp short read_sectors_5

; advance memory pointer
read_sectors_3:
			mov ax,es
			add ax,[bp - (over - para_per_sector)]
			mov es,ax
		pop ax
		pop cx
		pop dx

; increment DX:AX to advance to next sector
		inc ax
		jne read_sectors_4
		inc dx
read_sectors_4:
		loop read_sectors_1

; clear CY bit to signal success
		clc
read_sectors_5:
	pop bx
	pop es
	ret

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; 1st-stage data
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

curr_sector:	; which sector is in the FAT buffer
	dw -1

; name of the second stage bootloader
second_stage:
	db SS_NAME

; pad with NOPs to offset 510
	times (510 + $$ - $) nop

; 2-byte magic bootsector signature
	db 55h, 0AAh

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -