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

📄 loader.asm

📁 ucos在x86上的移植代码。吐血推荐啊
💻 ASM
字号:
;/* loader.asm :
; *     系统初始化模块代码,负责将系统内核加载至内存并进入保护模式,最终将
; *   系统控制权交由内核。
; *
; * Copyright(c) 2007, Alex P.Wonder
; * phoenixwonder@gmail.com
; *
; */

%include "bootsec.inc"
%include "pm.inc"

;%define _BOOT_DEBUG_
%ifdef _BOOT_DEBUG_
  org 0100h
%else
	org 0
	;org 0x90000
%endif

BaseOfLdrStack			equ 0x0000	; loader的堆栈基址
BaseOfKernel				equ 0x8000	; 内核模块加载后的内存存放地址 -- 段
OffsetOfKernel			equ 0x00		; 内核模块加载后的内存存放地址 -- 偏移
BaseOfKernelPhyAddr equ BaseOfKernel*16
;KernelEntryPhyAddr	equ 0x30400	;内核运行时的内存地址

[SECTION .s16]
[BITS 16]
	jmp L_START

	;--变量--
	wSectorNo						dw 0
	wRootDirSizeForLoop dw 0
	;OffsetOfLoader 			dw 0
	KernelFileName 			db 'KERNEL  BIN'  ;内核模块, ELF格式, 32位保护模式代码

	loadmsg							db 18,'starting kernel...', 0


	errmsg							db 12,'load failet!', 0

	welcome							db 9,'found it!', 0

	bOdd 								db 0
	dwKernelSize				dw 0

;--GDT描述符初始化--
L_GDT: Descriptor 0, 0, 0 ;空描述符

; 0 ~ 4G可执行代码
L_DESC_FLAT_C: Descriptor 0, 0fffffh, DA_CR | DA_32 | DA_LIMIT_4K

; 0 ~ 4G可读写段
L_DESC_FLAT_RW: Descriptor 0, 0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K

; 显存
L_DESC_VIDEO: Descriptor 0b8000h, 0ffffh, DA_DRW ;| DA_DPL3

GdtLen		equ 	$ - L_GDT
GdtPtr		dw GdtLen
					dd BaseOfLoaderPhyAddr + L_GDT

SelectorFlatC		equ L_DESC_FLAT_C	 - L_GDT
SelectorFlatRW	equ	L_DESC_FLAT_RW - L_GDT
SelectorVideo		equ	L_DESC_VIDEO	 - L_GDT ;+ EA_RPL3

;================================================
; BS+BPB表内容
;================================================
	DECLARE_BOOTSEC_INFO

;================================================
; loader主函数
;================================================
L_START:
;	mov		ax, 0x4f02
;	mov		bx, 0x4111 ;640x480, 5:6:5
;	int 0x10

	mov			ax, cs
	mov			ds, ax
	mov			ss,	ax
	mov 		es,	ax
	mov			sp, BaseOfLdrStack
	mov     ax, 0xb800
	mov     gs, ax

	push ebp 
	mov  ebp, esp
	sub  sp, 8
	mov  word [bp - 2], ds
	mov  word [bp - 4], loadmsg + 1
	mov  word [bp - 6], 0  ;y
	mov  word [bp - 8], 0	 ;x
	call PrintStr
	add  sp, 8
	pop  ebp

	; --在根目录中查找系统引导程序文件'kernel.bin'--
	mov word [wSectorNo], SectorNoOfRootDir ;根目录扇区号

	mov ax, [BPB_RootEntCnt] ; 根目录文件总数 -> wRootDirSizeForLoop(文件查找总次数)
	mov word [wRootDirSizeForLoop], ax

L_SEARCH_ROOT_DIR_HERE:
	mov ax, BaseOfKernel
	mov es, ax	; (es) <- BaseOfKernel
	mov bx, 0   ; es:bx -> BaseOfKernel: 0

	mov ax, [wSectorNo]  ; 读一个扇区FAT表内容到内存es:bx
	mov cl, 1
	call ReadSector

	mov si, KernelFileName		; ds:si -> 'KERNEL.BIN'
	mov di, 0

	cld
	mov dx, 10h  ; 512 / 32(一个FAT项占用的字节数) = 16(一个扇区可以存有16个FAT项)
L_SEARCH_FOR_KERNELBIN:
	cmp dx, 0
	jz  L_GOTO_NEXT_SECTOR ; 如果当前扇区中没找到,则查找下一个FAT扇区
	dec dx

	mov cx, 11 ;8.3 fat12 filename format
L_CMP_FILENAME:              ;
	cmp cx, 0                  ;
	jz  L_FILENAME_FOUND       ;
	dec cx                     ;
	lodsb		; ds:si -> al, si++
	cmp al, byte [es:di]       ;
	jz	L_GO_ON                ; 当前字符相同则继续比较文件名的下一个字符;
	jmp L_DIFFERENT            ; 否则,取下一个FAT目录项比较

L_GO_ON:                     ;
	inc di                     ;
	jmp L_CMP_FILENAME         ;

L_DIFFERENT:
	and di, 0ffe0h             ; di & 0xffe0 = 当前比较目录项的首字节地址
	add di, 20h                ; 跳过32字节,即取下一个目录项的首字节地址
	mov si, KernelFileName     ; KernelFileName首字节地址 -> (si)
	
	;显示字符串
	push ebp 
	mov  ebp, esp
	sub  sp, 8
	mov  word [bp - 2], es
	mov  word [bp - 4], di
	mov  word [bp - 6], 8  ;y
	mov  word [bp - 8], 0	 ;x
	call PrintStr
	add  sp, 8
	pop  ebp

	dec word [wRootDirSizeForLoop] ;wRootDirSizeForLoop--, 总比较次数-1
	cmp word [wRootDirSizeForLoop], 0
	jz  L_NO_KERNELBIN	;总次数为0时还没有找到,放弃查找

	jmp L_SEARCH_FOR_KERNELBIN

L_GOTO_NEXT_SECTOR:
	inc word [wSectorNo] ; wSectorNo++, 下一个FAT扇区
	jmp L_SEARCH_ROOT_DIR_HERE

L_NO_KERNELBIN:
	call KillMotor ;关闭驱动器马达

	;显示字符串
	push ebp 
	mov  ebp, esp
	sub  sp, 8
	mov  word [bp - 2], ds
	mov  word [bp - 4], errmsg + 1
	mov  word [bp - 6], 7  ;y
	mov  word [bp - 8], 0	 ;x
	call PrintStr
	add  sp, 8
	pop  ebp
	
	hlt

L_FILENAME_FOUND: ;找到了KERNEL.BIN
  mov		ax, RootDirSectors
  and		di, 0xffe0  ;这时候的di还是指向根目录区中对应的目录项(KERNEL.BIN)

	; 保存文件尺寸
  push	eax
  mov		eax, [es:di + 01ch]  ; 文件尺寸放到在目录项偏移地址0x1ch处
  mov		dword [dwKernelSize], eax
  pop		eax

  add		di, 01ah		;找到首Sector, 01ah偏移位置存放了该文件在数据区中的首簇号

  mov   cx, word [es:di]
  push  cx  ; 保存首簇号, 1)
  add 	cx, ax	;  (cx) <= RootDirSectors + 首簇号
  add		cx, DeltaSectorNo ; 数据区的第一个簇号是2,不是0或1, 所以DeltaSectorNo=1 + 9*2 - 2=17
                          ; 执行此指令后, (cx) == kernel.bin的第一个逻辑扇区号

  mov 	ax, BaseOfKernel
  mov 	es, ax
  mov 	bx, 0  ;BaseOfKernel:0中的FAT表内容已经不再需要了,直接在此内存地址放置内核文件的完整映像

  mov 	ax, cx ;逻辑扇区号=>(ax)
L_GOON_LOADING_FILE:
 	mov 	cl, 1 ;读一个扇区到 es:bx
 	call	ReadSector
 	pop		ax		; 当前簇号 => (ax)
 	call  GetFATEntry ;获得下一个簇号,并保存在ax中
 	cmp		ax, 0ff8h
 	jae		L_FILE_LOADED ;文件读取结束
 	push  ax ;保存当前簇号

 	; 下面几行代码获得当前簇号对应的逻辑扇区号
 	mov 	dx, RootDirSectors
 	add		ax, dx
 	add		ax, DeltaSectorNo
 	add		bx, [BPB_BytsPerSec] ;指向下一个存放文件内容的内存地址
 	jmp		L_GOON_LOADING_FILE

L_FILE_LOADED:
	;--文件全部加载完毕, 9000h:0000h -> kernel.bin(elf)

	;显示字符串
	push ebp 
	mov  ebp, esp
	sub  sp, 8
	mov  word [bp - 2], ds
	mov  word [bp - 4], welcome + 1
	mov  word [bp - 6], 6  ;y
	mov  word [bp - 8], 0	 ;x
	call PrintStr
	add  sp, 8
	pop  ebp

	call KillMotor ;关闭驱动器马达

	;----进入保护模式----
	lgdt		[GdtPtr]

	cli ;关闭所有中断

	;开A20
	in			al, 92h
	or			al, 00000010b
	out			92h, al

	;置eflags中的保护模式标志位
	mov			eax, cr0
	or			eax, 1
	mov			cr0, eax
	
	;跳到保护模式起始地址
	jmp dword SelectorFlatC:(BaseOfLoaderPhyAddr + L_PM_START)

;[SECTION .data]
ALIGN 32
L_DATA:
StackSpace: times 1024 db 0
TopOfStack equ BaseOfLoaderPhyAddr + $ ;栈顶

;[SECTION .s32]
ALIGN 32
[BITS 32]
L_PM_START: ;保护模式起始地址

	mov			ax, SelectorVideo
	mov			gs, ax

	;数据段初始化
	mov			ax, SelectorFlatRW
	mov			ds, ax
	mov			es, ax
	mov			fs, ax
	mov			ss, ax
	mov			esp, TopOfStack

;	;显示一个调试用的字符
;	mov			ah, 0x0f
;	mov			al, 'P'
;	mov			[gs:((80*10+0)*2)], ax
	
	call 		InitKernel
	jmp			SelectorFlatC:KernelEntryPhyAddr

;
; 从位于9000h:0000h中ELF格式的内核模块中抽出代码部分搬移至
; BaseOfKernelPhyAddr内存地址处.
;
InitKernel:
	xor			esi, esi

	mov			cx, word [BaseOfKernelPhyAddr + 2ch] ;获得程序头项个数(也即代码/数据段数)
	movzx		ecx, cx ;扩展cx->ecx,高位写0

	mov			esi, [BaseOfKernelPhyAddr + 1ch] ; e_phoff
	add			esi, BaseOfKernelPhyAddr ; [BaseOfKernelPhyAddr + e_phoff] => 程序头表在实际内存中的地址
.Begin:
	mov			eax, [esi + 0] ; p_type == 0 -> PT_NULL
	cmp			eax, 0
	jz			.NoAction ; 程序头项对应的内容不是可执行的代码

	;memcpy, from kernel elf in memory to program vaddr
	push 		dword [esi + 010h] ; 源数据字节长度
	mov			eax, [esi + 04h]
	add			eax, BaseOfKernelPhyAddr
	push		eax ; 源数据地址
	push		dword [esi + 08h] ; 代码加载后的逻辑内存地址(目的内存地址)
	call		_MemCpy
	add			esp, 12 ;释放上面分配的堆栈资源

.NoAction:
	add			esi, 020h ;取下一个程序头项内容
	dec			ecx
	jnz			.Begin ;循环搬移所有程序头项内容
	ret

;
; 内存复制.
;  (sp + 4)  - 目的地址
;  (sp + 8)  - 源地址
;  (sp + 12) - 复制字节数
;
_MemCpy:
	mov		ebp, esp
	push  ecx
	push  ebp
	push  esi
	push  edi

	mov   ecx, dword [ebp + 12]
	mov		esi, dword [ebp + 8]
	mov		edi, dword [ebp + 4]
	rep   movsb

	pop   edi
	pop   esi
	pop		ebp
	pop		ecx
	ret

;================================================
;    				定义一些函数(实模式)
;================================================

[SECTION .s16]
[BITS 16]
;
;读取一个扇区
;  ax - 起始逻辑扇区号
;  cl - 扇区数
;  es:bx - 返回地址
;
ReadSector:
	push 	bp
	mov		bp, sp
	sub		esp, 2   ;开辟两个字节存放需要读取的扇区数
	mov   byte [bp - 2], cl
	push	bx
	mov		bl, [BPB_SecsPerTrk]
	div   bl			; y在al中(商), z在ah中(余数)
	inc		ah			; z++(扇区号)
	mov		cl, ah  ; cl <- 起始扇区号
	mov		dh, al  ; dh <- y
	shr   al, 1   ; 柱面号
	mov		ch, al
	and 	dh, 1 ; 磁头号
	pop		bx
	mov		dl, [BS_DrvNum]
.CoOnReading:
	mov ah, 2
	mov al, byte [bp - 2]
	int	13h

	jc	.CoOnReading	; 如果读取错误,CF=1, 这里就不停地读,直到正确为止
	add esp, 2
	pop bp
	ret

;
; 功  能:
;   根据文件的簇号获得对应FAT表项内容(文件下一个簇号).
; 输  入:
; 	ax - 文件的当前簇号
; 输  出:
;		ax - 下一个簇号
; 返回值:
;
GetFATEntry:
	push es
	push bx

	push ax

	mov ax, BaseOfLoader
	sub ax, 0100h
	mov es, ax     ;BaseOfLoader地址前空出4k给es段(用来临时保存读取的FAT扇区)

	pop ax

	mov byte [bOdd], 0
	mov bx, 3
	mul bx    ; ax*3 => (dx:ax)
	mov bx, 2
	div bx    ; (dx:ax) / 2 => (ax):商, (dx):余数
	cmp dx, 0
	jz  L_EVEN
	mov byte [bOdd], 1
L_EVEN:
	xor 	dx, dx
	mov 	bx, [BPB_BytsPerSec]
	div 	bx ; 求逻辑扇区号对应的FAT项在第几个FAT内容扇区中, ax/BPB_BytsPerSec => dx(余数), ax(商)
	push 	dx ; dx保存FAT项在FAT扇区中的偏移
	mov  	bx, 0

	; 连续读取2个逻辑扇区号对应的FAT表扇区到内存地址es:bx
	add  	ax, SectorNoOfFAT1
	mov  	cl, 2
	call 	ReadSector

	pop  	dx
	add  	bx, dx 			;(es:bx + dx) 保存文件的FAT12项
	mov  	ax, [es:bx] ;读2个字节到ax
	cmp  	byte [bOdd], 1
	jnz  	L_EVEN_2
	shr  	ax, 4
L_EVEN_2:
	and  ax, 0fffh
L_GET_FAT_ENTRY_OK:

	pop bx
	pop es
	ret

;
; 功  能:
;   短字符串显示,短字符串中第一个字节保存的是字符串的长度,因此字符串
; 的长度范围为0~255.
;  i.e.  shrtMsg db 8, '12345678'
;
; 输  入:
; 	(es:bp)    - 短字符串地址
;   (dh), (dl) - Y, X
;   (bl)       - 字符属性(b7: 闪烁, b6~b4: 背景色, b3: 字符高亮, b2~b0: 字符颜色)
; 输  出:
;   (无)
;
ShowShortMsg:
	push ax
	push bx
	push cx

	xor cx, cx
	mov cl, [bp] 	;获得字符串字符个数
	inc bp 				;指向字符串中第一个字符

	mov ah, 19
	mov bh, 0 		;页号
	mov al, 0 		;光标不动
	int 10h

	dec bp ;恢复bp的值
	pop cx
	pop bx
	pop ax
	ret

;==================================================================
; C格 式: void PrintInt(int x, int y, unsigned char val)
; 说  明:
;
; 参  数:
;
; 返回值:
;
;==================================================================
PrintInt:
	push ebp
	mov  ebp, esp
	
	push eax
	push ebx
	push ecx

	mov ax, 0xb800
	mov gs, ax
	
	mov ebx, [ebp + 8] ;x
	mov ecx, [ebp + 12] ;y

	mov al, cl
	mov cl, 80
	mul cl
	add eax, ebx  
	shl eax, 1 ; (y*80 + x)*2
	
	xor ebx, ebx
	mov bl, [ebp + 16]
	mov bh, 0x0f ;黑底白字
	mov [gs:eax], bx
	
	pop ecx
	pop ebx
	pop eax
	
	leave
	ret

;==================================================================
; C格 式: void PrintStr(int x, int y, char *str) (实地址模式)
; 说  明:
;							
; 参  数:
;
; 返回值:
;   = 0 -- fail
;   > 0 -- successful
;==================================================================
PrintStr:
	push bp
	mov  bp, sp
	
	push ax
	push bx
	push cx
	push es
	push si
	push di
	
	mov si, [bp + 8] ;  off of str
	mov ax, [bp + 10] ; seg of str
	mov es, ax
	
	;xor di, di
	mov ax, [bp + 6] 
	mov bl, 80
	mul bl
	add ax, [bp + 4]
	shl ax, 1 ; (y*80 + x)*2
	mov di, ax
	
.L0:
	mov al, [es:si]
	cmp al, 0
	jz  .quitit
	
	;显示一个字符	
	mov ah, 0x0f
	mov [gs:di], ax
	
	;指向下一个字符地址
	inc si
	add di, 2
	jmp .L0

.quitit:
	pop di
	pop si
	pop es
	pop cx
	pop bx
	pop ax
	
	mov sp, bp
	pop bp
	ret

KillMotor:
	push 		dx
	mov			dx, 03f2h
	mov 		al, 0
	out			dx, al
	pop			dx
	ret

⌨️ 快捷键说明

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