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

📄 loader.asm

📁 一个加载OS内核的源代码
💻 ASM
📖 第 1 页 / 共 2 页
字号:
org 0100h
		jmp LABEL_START														;跳转指令
		nop
		%include "fat12hdr.inc"
		%include "pm.inc"		
		LABEL_GDT:					Descriptor		0,  0,  0			;定义一个空描述符
		LABEL_DESC_FLAT_C:	Descriptor		0,  0FFFFFh,	DA_CR | DA_32 | DA_LIMIT_4K	 
		LABEL_DESC_FLAT_RW:	Descriptor		0,	0FFFFFh,	DA_DRW | DA_32 | DA_LIMIT_4K 
		LABEL_DESC_VIDEO:		Descriptor	0B8000h, 0FFFFh, DA_DRW | DA_DPL3						 
		
		GdtLen equ $ - LABEL_GDT
		GdtPtr	dw	GdtLen
						dd	BaseOfLoaderPhyAddr + LABEL_GDT					;GDT的基地址和长度					
						
		;选择子
		SelectorFlatC		equ  LABEL_DESC_FLAT_C - LABEL_GDT
		SelectorFlatRW	equ  LABEL_DESC_FLAT_RW - LABEL_GDT
		SelectorVideo 	equ LABEL_DESC_VIDEO - LABEL_GDT + SA_RPL3				
		
;==========================================================================
;变量及宏
;--------------------------------------------------------------------------
wRootDirSizeForLoop			dw	14							; Root Directory 占用的扇区数
wSectorNo 							dw	0								; 要读取的扇区号
bOdd										db	0								; 奇数还是偶数
dwKernelSize						dd	0								; KERNEL.BIN 文件大小

PageDirBase 						equ 	100000h				;  页目录基址,内存1M处
PageTblBase							equ		101000h				;  页表基址,1M + 4K处

BaseOfStack					equ			0100h
BaseOfLoader				equ			09000h			;Loader.bin被加载的位置:段地址    
OffsetOfLoader			equ			0100h				;Loader.bin被加载的位置: 偏移地址
																				;09000:0100h = 090100h = 577K左右
BaseOfKernelFile		equ			08000h			;内核被加载的位置---段地址				 
OffsetOfKernelFile	equ			0h					;内核被加载的位置---偏移
																				;08000:0h = 080000h = 512K
BaseOfLoaderPhyAddr equ			BaseOfLoader * 10h

BaseOfKernelFilePhyAddr	equ	BaseOfKernelFile * 10h
KernelEntryPointPhyAddr	equ	030400h	
; 大约193k,注意:1、必须与 MAKEFILE 中参数 -Ttext 的值相等!!
;2、这是个地址而非仅仅是个偏移,如果 -Ttext 的值为 0x400400,
;则它的值也应该是 0x400400。

;======================================================================
;字符串
;----------------------------------------------------------------------
KernelFileName		db	"KERNEL  BIN", 0	; KERNEL.BIN 之文件名
; 为简化代码, 下面每个字符串的长度均为 MessageLength
MessageLength		equ	13
LoadMessage:		db	"Loading      "
Message1				db	"Loader  Ready"
Message2				db	"No KERNEL    "
;======================================================================
		
	LABEL_START:
		;初始化寄存器
		mov ax, cs
		mov es, ax
		mov ds, ax
		mov ss, ax
		mov sp, BaseOfStack
		
		;显示字符串
		mov dh, 0																;"Loading "
		call DispStrRealMode										;
		
		;得到内存数
			mov	ebx, 0
			mov	di, _MemChkBuf
		.loop:
			mov	eax, 0E820h
			mov	ecx, 20
			mov	edx, 0534D4150h
			int	15h
			jc	LABEL_MEM_CHK_FAIL
			add	di, 20
			inc	dword [_dwMCRNumber]
			cmp	ebx, 0
			jne	.loop
			jmp	LABEL_MEM_CHK_OK
		LABEL_MEM_CHK_FAIL:
			mov	dword [_dwMCRNumber], 0
		LABEL_MEM_CHK_OK:		
		
		;软驱复位
		xor	ah, ah	; ┓
		xor	dl, dl	; ┣ 软驱复位
		int	13h			; ┛
		
		mov word [wSectorNo], SectorNoOfRootDerectory
	LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
		cmp word [wRootDirSizeForLoop], 0				;检查根目录扇区是否读完
		jz LABEL_NO_KERNELBIN										;读完则跳转	
		dec word [wRootDirSizeForLoop]					;计数器减1
		
		;还有扇区没有读完则读取扇区
		mov ax, BaseOfKernelFile
		mov es, ax
		mov bx, OffsetOfKernelFile							;es:bx 准备好缓冲区
		mov cl, 1																;读取一个扇区
		mov ax, [wSectorNo]											;从[wSectorNo]开始读
		call ReadSector
		
		mov si, KernelFileName									;指向要读取的内核文件名字
		mov di, OffsetOfKernelFile
		cld
		mov dx, 10h															;一个扇区可以存放10h个条目
		
	LABEL_SEARCH_FOR_KERNELBIN:
		cmp	dx, 0																; ┓循环次数控制, 如果已
		jz	LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR	; ┣ 经读完了一个 Sector,
		dec	dx																	; ┛ 就跳到下一个 Sector
		mov	cx, 11
	
	LABEL_CMP_FILENAME:
		cmp	cx, 0									; ┓
		jz	LABEL_FILENAME_FOUND	; ┣ 循环次数控制, 如果比较了 11 个
		dec	cx										; ┛字符都相等, 表示找到
		lodsb
		cmp	al, byte [es:di]
		jz	LABEL_GO_ON
		jmp LABEL_DIFFERENT
	LABEL_GO_ON:
		inc di
		jmp LABEL_CMP_FILENAME		
	
	LABEL_DIFFERENT:
		and di, 0FFE0h
		add di, 20h
		mov si, KernelFileName
		jmp LABEL_SEARCH_FOR_KERNELBIN
	
	LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR:
		add word [wSectorNo], 1
		jmp LABEL_SEARCH_IN_ROOT_DIR_BEGIN
		
	LABEL_NO_KERNELBIN:
		mov dh,	2
		call DispStrRealMode	
		
	%ifdef _LOADER_DEBUG_
		mov ax, 4C00h
		int 21h
	%else
		jmp $
	%endif		
	
	LABEL_FILENAME_FOUND:			
		mov	ax, RootDirSectors
		and	di, 0FFF0h		; di -> 当前条目的开始
	
		push	eax
		mov	eax, [es : di + 01Ch]		; 	┓
		mov	dword [dwKernelSize], eax	; ┛保存 KERNEL.BIN 文件大小
		pop	eax
	
		add	di, 01Ah		; di -> 首 Sector
		mov	cx, word [es:di]
		push	cx			; 保存此 Sector 在 FAT 中的序号
		add	cx, ax
		add	cx, DeltaSectorNo	; 这时 cl 里面是 LOADER.BIN 的起始扇区号
		mov	ax, BaseOfKernelFile; (从 0 开始数的序号)
		mov	es, ax			; es <- BaseOfKernelFile
		mov	bx, OffsetOfKernelFile	; bx <- OffsetOfKernelFile	于是
		; es:bx = BaseOfKernelFile:OffsetOfKernelFile = 
		;BaseOfKernelFile * 10h + OffsetOfKernelFile
		mov	ax, cx			; ax <- Sector 号
	
	LABEL_GOON_LOADING_FILE:
		push	ax			; ┓
		push	bx			; ┃
		mov	ah, 0Eh		; ┃ 每读一个扇区就在 "Loading  " 后面打一个
		mov	al, '.'		; ┃ 点, 形成这样的效果:
		mov	bl, 0Fh		; ┃ Loading ......
		int	10h				; ┃
		pop	bx				; ┃
		pop	ax				; ┛
	
		mov	cl, 1
		call	ReadSector
		pop	ax			; 取出此 Sector 在 FAT 中的序号
		call	GetFATEntry
		cmp	ax, 0FFFh
		jz	LABEL_FILE_LOADED
		push	ax			; 保存 Sector 在 FAT 中的序号
		mov	dx, RootDirSectors
		add	ax, dx
		add	ax, DeltaSectorNo
		add	bx, [BPB_BytsPerSec]
		jmp	LABEL_GOON_LOADING_FILE
	LABEL_FILE_LOADED:
		call	KillMotor		; 关闭软驱马达
	
		mov	dh, 1			; "Ready."
		call	DispStrRealMode			; 显示字符串
	
		;下面准备跳入保护模式
		;加载 GDTR
		lgdt [GdtPtr]
		;关中断
		cli
		;A20
		in al, 92h
		or al, 00000010B
		out 92h, al
		;准备切换到保护模式
		mov eax, cr0
		or eax, 1
		mov cr0, eax
		;跳入保护模式
		jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr + LABEL_PM_START)
				
;------------------------------------------------------------------
; 函数名: DispStrRealMode
;------------------------------------------------------------------
; 作用:
;	显示一个字符串, 函数开始时 dh 中应该是字符串序号(0-based)
DispStrRealMode:
	mov	ax, MessageLength
	mul	dh
	add	ax, LoadMessage							;确定要显示的字符串的偏移
	mov	bp, ax											; ┓
	mov	ax, ds											; ┣ ES:BP = 串地址
	mov	es, ax											; ┛
	mov	cx, MessageLength						; CX = 串长度
	mov	ax, 01301h									; AH = 13,  AL = 01h
	mov	bx, 0007h										; 页号为0(BH = 0) 黑底白字(BL = 07h)
	mov	dl, 0
	add	dh, 3												; 从第 3 行往下显示
	int	10h													; int 10h
	ret

;-------------------------------------------------------------------
; 函数名: ReadSector
;-------------------------------------------------------------------
; 作用:
;	从序号(Directory Entry 中的 Sector 号)为 ax 的的 Sector 开始, 
; 将 cl 个 Sector 读入 es:bx 中
ReadSector:
	; ---------------------------------------------------------------
	; 怎样由扇区号求扇区在磁盘中的位置(扇区号 -> 柱面号, 起始扇区, 磁头号)
	; ---------------------------------------------------------------
	; 设扇区号为 x
	;                          ┌ 柱面号 = y >> 1
	;       x           ┌ 商 y ┤
	; -------------- => ┤      └ 磁头号 = y & 1
	;  每磁道扇区数     │
	;                   └ 余 z => 起始扇区号 = z + 1
	push	bp
	mov	bp, sp
	sub	esp, 2							; 辟出两个字节的堆栈区域保存要读的扇区数:
													; byte [bp-2]	
	mov	byte [bp-2], cl
	push	bx								; 保存 bx
	mov	bl, [BPB_SecPerTrk]	; bl: 除数
	div	bl									; y 在 al 中, z 在 ah 中
	inc	ah									; z ++
	mov	cl, ah							; cl <- 在柱面上的起始扇区号
	mov	dh, al							; dh <- y
	and	dh, 1								; dh & 1 = 磁头号
	shr	al, 1								; y >> 1 (其实是 y/BPB_NumHeads, 
													;这里BPB_NumHeads=2)
	mov	ch, al							; ch <- 柱面号
	pop	bx									; 恢复 bx
													; 至此, "柱面号, 起始扇区, 磁头号" 全部得到 
	mov	dl, [BS_DrvNum]		  ; 驱动器号 (0 表示 A 盘)
.GoOnReading:
	mov	ah, 2								; 读
	mov	al, byte [bp-2]			; 读 al 个扇区
	int	13h
	jc	.GoOnReading				; 如果读取错误 CF 会被置为 1, 这时就不停地读, 
													; 直到正确为止
	add	esp, 2
	pop	bp

⌨️ 快捷键说明

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