天天看點

核心雛形——跳入保護模式

源于《自己動手寫作業系統》

核心已經被我們加載進記憶體了,現在要跳入保護模式了~在拉出.asm檔案之前,有幾個inc檔案必須說明一下

lib.inc

; ------------------------------------------------------------------------
; 顯示 AL 中的數字
; ------------------------------------------------------------------------
DispAL:
	push	ecx
	push	edx
	push	edi

	mov	edi, [dwDispPos]

	mov	ah, 0Fh			; 0000b: 黑底    1111b: 白字
	mov	dl, al
	shr	al, 4
	mov	ecx, 2
.begin:
	and	al, 01111b
	cmp	al, 9
	ja	.1
	add	al, '0'
	jmp	.2
.1:
	sub	al, 0Ah
	add	al, 'A'
.2:
	mov	[gs:edi], ax
	add	edi, 2

	mov	al, dl
	loop	.begin
	;add	edi, 2

	mov	[dwDispPos], edi

	pop	edi
	pop	edx
	pop	ecx

	ret
; DispAL 結束-------------------------------------------------------------


; ------------------------------------------------------------------------
; 顯示一個整形數
; ------------------------------------------------------------------------
DispInt:
	mov	eax, [esp + 4]
	shr	eax, 24
	call	DispAL

	mov	eax, [esp + 4]
	shr	eax, 16
	call	DispAL

	mov	eax, [esp + 4]
	shr	eax, 8
	call	DispAL

	mov	eax, [esp + 4]
	call	DispAL

	mov	ah, 07h			; 0000b: 黑底    0111b: 灰字
	mov	al, 'h'
	push	edi
	mov	edi, [dwDispPos]
	mov	[gs:edi], ax
	add	edi, 4
	mov	[dwDispPos], edi
	pop	edi

	ret
; DispInt 結束------------------------------------------------------------

; ------------------------------------------------------------------------
; 顯示一個字元串
; ------------------------------------------------------------------------
DispStr:
	push	ebp
	mov	ebp, esp
	push	ebx
	push	esi
	push	edi

	mov	esi, [ebp + 8]	; pszInfo
	mov	edi, [dwDispPos]
	mov	ah, 0Fh
.1:
	lodsb
	test	al, al
	jz	.2
	cmp	al, 0Ah	; 是回車嗎?
	jnz	.3
	push	eax
	mov	eax, edi
	mov	bl, 160
	div	bl
	and	eax, 0FFh
	inc	eax
	mov	bl, 160
	mul	bl
	mov	edi, eax
	pop	eax
	jmp	.1
.3:
	mov	[gs:edi], ax
	add	edi, 2
	jmp	.1

.2:
	mov	[dwDispPos], edi

	pop	edi
	pop	esi
	pop	ebx
	pop	ebp
	ret
; DispStr 結束------------------------------------------------------------

; ------------------------------------------------------------------------
; 換行
; ------------------------------------------------------------------------
DispReturn:
	push	szReturn
	call	DispStr			;printf("\n");
	add	esp, 4

	ret
; DispReturn 結束---------------------------------------------------------


; ------------------------------------------------------------------------
; 記憶體拷貝,仿 memcpy
; ------------------------------------------------------------------------
; void* MemCpy(void* es:pDest, void* ds:pSrc, int iSize);
; ------------------------------------------------------------------------
MemCpy:
	push	ebp
	mov	ebp, esp

	push	esi
	push	edi
	push	ecx

	mov	edi, [ebp + 8]	; Destination
	mov	esi, [ebp + 12]	; Source
	mov	ecx, [ebp + 16]	; Counter
.1:
	cmp	ecx, 0		; 判斷計數器
	jz	.2		; 計數器為零時跳出

	mov	al, [ds:esi]		; ┓
	inc	esi			; ┃
					; ┣ 逐位元組移動
	mov	byte [es:edi], al	; ┃
	inc	edi			; ┛

	dec	ecx		; 計數器減一
	jmp	.1		; 循環
.2:
	mov	eax, [ebp + 8]	; 傳回值

	pop	ecx
	pop	edi
	pop	esi
	mov	esp, ebp
	pop	ebp

	ret			; 函數結束,傳回
; MemCpy 結束-------------------------------------------------------------
           

loader.inc

BaseOfLoader		equ	 09000h	; LOADER.BIN 被加載到的位置 ----  段位址
OffsetOfLoader		equ	  0100h	; LOADER.BIN 被加載到的位置 ---- 偏移位址

BaseOfLoaderPhyAddr	equ	BaseOfLoader * 10h	; LOADER.BIN 被加載到的位置 ---- 實體位址 (= BaseOfLoader * 10h)


BaseOfKernelFile	equ	 08000h	; KERNEL.BIN 被加載到的位置 ----  段位址
OffsetOfKernelFile	equ	     0h	; KERNEL.BIN 被加載到的位置 ---- 偏移位址
           

loader.asm

org  0100h

	jmp	LABEL_START		; Start

; 下面是 FAT12 磁盤的頭, 之是以包含它是因為下面用到了磁盤的一些資訊
%include	"fat12hdr.inc"
%include	"load.inc"
%include	"pm.inc"


; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------
;                                                段基址            段界限     , 屬性
LABEL_GDT:			Descriptor             0,                    0, 0						; 空描述符
LABEL_DESC_FLAT_C:		Descriptor             0,              0fffffh, DA_CR  | DA_32 | DA_LIMIT_4K			; 0 ~ 4G
LABEL_DESC_FLAT_RW:		Descriptor             0,              0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K			; 0 ~ 4G
LABEL_DESC_VIDEO:		Descriptor	 0B8000h,               0ffffh, DA_DRW                         | DA_DPL3	; 顯存首位址
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------

GdtLen		equ	$ - LABEL_GDT
GdtPtr		dw	GdtLen - 1				; 段界限
		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
; GDT 選擇子 ----------------------------------------------------------------------------------


BaseOfStack	equ	0100h
PageDirBase	equ	100000h	; 頁目錄開始位址:	1M
PageTblBase	equ	101000h	; 頁表開始位址:		1M + 4K


LABEL_START:			; <--- 從這裡開始 *************
	mov	ax, cs
	mov	ds, ax
	mov	es, ax
	mov	ss, ax
	mov	sp, BaseOfStack

	mov	dh, 0			; "Loading  "
	call	DispStrRealMode		; 顯示字元串

	; 得到記憶體數
	mov	ebx, 0			; ebx = 後續值, 開始時需為 0
	mov	di, _MemChkBuf		; es:di 指向一個位址範圍描述符結構(Address Range Descriptor Structure)
.MemChkLoop:
	mov	eax, 0E820h		; eax = 0000E820h
	mov	ecx, 20			; ecx = 位址範圍描述符結構的大小
	mov	edx, 0534D4150h		; edx = 'SMAP'
	int	15h			; int 15h
	jc	.MemChkFail
	add	di, 20
	inc	dword [_dwMCRNumber]	; dwMCRNumber = ARDS 的個數
	cmp	ebx, 0
	jne	.MemChkLoop
	jmp	.MemChkOK
.MemChkFail:
	mov	dword [_dwMCRNumber], 0
.MemChkOK:

	; 下面在 A 盤的根目錄尋找 KERNEL.BIN
	mov	word [wSectorNo], SectorNoOfRootDirectory	
	xor	ah, ah	; ┓
	xor	dl, dl	; ┣ 軟驅複位
	int	13h	; ┛
LABEL_SEARCH_IN_ROOT_DIR_BEGIN:
	cmp	word [wRootDirSizeForLoop], 0	; ┓
	jz	LABEL_NO_KERNELBIN		; ┣ 判斷根目錄區是不是已經讀完, 如果讀完表示沒有找到 KERNEL.BIN
	dec	word [wRootDirSizeForLoop]	; ┛
	mov	ax, BaseOfKernelFile
	mov	es, ax			; es <- BaseOfKernelFile
	mov	bx, OffsetOfKernelFile	; bx <- OffsetOfKernelFile	于是, es:bx = BaseOfKernelFile:OffsetOfKernelFile = BaseOfKernelFile * 10h + OffsetOfKernelFile
	mov	ax, [wSectorNo]		; ax <- Root Directory 中的某 Sector 号
	mov	cl, 1
	call	ReadSector

	mov	si, KernelFileName	; ds:si -> "KERNEL  BIN"
	mov	di, OffsetOfKernelFile	; es:di -> BaseOfKernelFile:???? = BaseOfKernelFile*10h+????
	cld
	mov	dx, 10h
LABEL_SEARCH_FOR_KERNELBIN:
	cmp	dx, 0					; ┓
	jz	LABEL_GOTO_NEXT_SECTOR_IN_ROOT_DIR	; ┣ 循環次數控制, 如果已經讀完了一個 Sector, 就跳到下一個 Sector
	dec	dx					; ┛
	mov	cx, 11
LABEL_CMP_FILENAME:
	cmp	cx, 0			; ┓
	jz	LABEL_FILENAME_FOUND	; ┣ 循環次數控制, 如果比較了 11 個字元都相等, 表示找到
	dec	cx			; ┛
	lodsb				; ds:si -> al
	cmp	al, byte [es:di]	; if al == es:di
	jz	LABEL_GO_ON
	jmp	LABEL_DIFFERENT
LABEL_GO_ON:
	inc	di
	jmp	LABEL_CMP_FILENAME	;	繼續循環

LABEL_DIFFERENT:
	and	di, 0FFE0h		; else┓	這時di的值不知道是什麼, di &= e0 為了讓它是 20h 的倍數
	add	di, 20h			;     ┃
	mov	si, KernelFileName	;     ┣ di += 20h  下一個目錄條目
	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			; "No KERNEL."
	call	DispStrRealMode		; 顯示字元串
%ifdef	_LOADER_DEBUG_
	mov	ax, 4c00h		; ┓
	int	21h			; ┛沒有找到 KERNEL.BIN, 回到 DOS
%else
	jmp	$			; 沒有找到 KERNEL.BIN, 死循環在這裡
%endif

LABEL_FILENAME_FOUND:			; 找到 KERNEL.BIN 後便來到這裡繼續
	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 的起始扇區号 (從 0 開始數的序号)
	mov	ax, BaseOfKernelFile
	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)

	jmp	$


;============================================================================
;變量
;----------------------------------------------------------------------------
wRootDirSizeForLoop	dw	RootDirSectors	; Root Directory 占用的扇區數
wSectorNo		dw	0		; 要讀取的扇區号
bOdd			db	0		; 奇數還是偶數
dwKernelSize		dd	0		; KERNEL.BIN 檔案大小

;============================================================================
;字元串
;----------------------------------------------------------------------------
KernelFileName		db	"KERNEL  BIN", 0	; KERNEL.BIN 之檔案名
; 為簡化代碼, 下面每個字元串的長度均為 MessageLength
MessageLength		equ	9
LoadMessage:		db	"Loading  "
Message1		db	"Ready.   "
Message2		db	"No KERNEL"
;============================================================================

;----------------------------------------------------------------------------
; 函數名: DispStrRealMode
;----------------------------------------------------------------------------
; 運作環境:
;	實模式(保護模式下顯示字元串由函數 DispStr 完成)
; 作用:
;	顯示一個字元串, 函數開始時 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
	shr	al, 1			; y >> 1 (其實是 y/BPB_NumHeads, 這裡BPB_NumHeads=2)
	mov	ch, al			; ch <- 柱面号
	and	dh, 1			; dh & 1 = 磁頭号
	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

	ret

;----------------------------------------------------------------------------
; 函數名: GetFATEntry
;----------------------------------------------------------------------------
; 作用:
;	找到序号為 ax 的 Sector 在 FAT 中的條目, 結果放在 ax 中
;	需要注意的是, 中間需要讀 FAT 的扇區到 es:bx 處, 是以函數一開始儲存了 es 和 bx
GetFATEntry:
	push	es
	push	bx
	push	ax
	mov	ax, BaseOfKernelFile	; ┓
	sub	ax, 0100h		; ┣ 在 BaseOfKernelFile 後面留出 4K 空間用于存放 FAT
	mov	es, ax			; ┛
	pop	ax
	mov	byte [bOdd], 0
	mov	bx, 3
	mul	bx			; dx:ax = ax * 3
	mov	bx, 2
	div	bx			; dx:ax / 2  ==>  ax <- 商, dx <- 餘數
	cmp	dx, 0
	jz	LABEL_EVEN
	mov	byte [bOdd], 1
LABEL_EVEN:;偶數
	xor	dx, dx			; 現在 ax 中是 FATEntry 在 FAT 中的偏移量. 下面來計算 FATEntry 在哪個扇區中(FAT占用不止一個扇區)
	mov	bx, [BPB_BytsPerSec]
	div	bx			; dx:ax / BPB_BytsPerSec  ==>	ax <- 商   (FATEntry 所在的扇區相對于 FAT 來說的扇區号)
					;				dx <- 餘數 (FATEntry 在扇區内的偏移)。
	push	dx
	mov	bx, 0			; bx <- 0	于是, es:bx = (BaseOfKernelFile - 100):00 = (BaseOfKernelFile - 100) * 10h
	add	ax, SectorNoOfFAT1	; 此句執行之後的 ax 就是 FATEntry 所在的扇區号
	mov	cl, 2
	call	ReadSector		; 讀取 FATEntry 所在的扇區, 一次讀兩個, 避免在邊界發生錯誤, 因為一個 FATEntry 可能跨越兩個扇區
	pop	dx
	add	bx, dx
	mov	ax, [es:bx]
	cmp	byte [bOdd], 1
	jnz	LABEL_EVEN_2
	shr	ax, 4
LABEL_EVEN_2:
	and	ax, 0FFFh

LABEL_GET_FAT_ENRY_OK:

	pop	bx
	pop	es
	ret
;----------------------------------------------------------------------------


;----------------------------------------------------------------------------
; 函數名: KillMotor
;----------------------------------------------------------------------------
; 作用:
;	關閉軟驅馬達
KillMotor:
	push	dx
	mov	dx, 03F2h
	mov	al, 0
	out	dx, al
	pop	dx
	ret
;----------------------------------------------------------------------------


; 從此以後的代碼在保護模式下執行 ----------------------------------------------------
; 32 位代碼段. 由實模式跳入 ---------------------------------------------------------
[SECTION .s32]

ALIGN	32

[BITS	32]

LABEL_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

	push	szMemChkTitle
	call	DispStr
	add	esp, 4

	call	DispMemInfo
	call	SetupPaging

	mov	ah, 0Fh				; 0000: 黑底    1111: 白字
	mov	al, 'P'
	mov	[gs:((80 * 0 + 39) * 2)], ax	; 螢幕第 0 行, 第 39 列。
	jmp	$


%include	"lib.inc"


; 顯示記憶體資訊 --------------------------------------------------------------
DispMemInfo:
	push	esi
	push	edi
	push	ecx

	mov	esi, MemChkBuf
	mov	ecx, [dwMCRNumber]	;for(int i=0;i<[MCRNumber];i++) // 每次得到一個ARDS(Address Range Descriptor Structure)結構
.loop:					;{
	mov	edx, 5			;	for(int j=0;j<5;j++)	// 每次得到一個ARDS中的成員,共5個成員
	mov	edi, ARDStruct		;	{			// 依次顯示:BaseAddrLow,BaseAddrHigh,LengthLow,LengthHigh,Type
.1:					;
	push	dword [esi]		;
	call	DispInt			;		DispInt(MemChkBuf[j*4]); // 顯示一個成員
	pop	eax			;
	stosd				;		ARDStruct[j*4] = MemChkBuf[j*4];
	add	esi, 4			;
	dec	edx			;
	cmp	edx, 0			;
	jnz	.1			;	}
	call	DispReturn		;	printf("\n");
	cmp	dword [dwType], 1	;	if(Type == AddressRangeMemory) // AddressRangeMemory : 1, AddressRangeReserved : 2
	jne	.2			;	{
	mov	eax, [dwBaseAddrLow]	;
	add	eax, [dwLengthLow]	;
	cmp	eax, [dwMemSize]	;		if(BaseAddrLow + LengthLow > MemSize)
	jb	.2			;
	mov	[dwMemSize], eax	;			MemSize = BaseAddrLow + LengthLow;
.2:					;	}
	loop	.loop			;}
					;
	call	DispReturn		;printf("\n");
	push	szRAMSize		;
	call	DispStr			;printf("RAM size:");
	add	esp, 4			;
					;
	push	dword [dwMemSize]	;
	call	DispInt			;DispInt(MemSize);
	add	esp, 4			;

	pop	ecx
	pop	edi
	pop	esi
	ret
; ---------------------------------------------------------------------------

; 啟動分頁機制 --------------------------------------------------------------
SetupPaging:
	; 根據記憶體大小計算應初始化多少PDE以及多少頁表
	xor	edx, edx
	mov	eax, [dwMemSize]
	mov	ebx, 400000h	; 400000h = 4M = 4096 * 1024, 一個頁表對應的記憶體大小
	div	ebx
	mov	ecx, eax	; 此時 ecx 為頁表的個數,也即 PDE 應該的個數
	test	edx, edx
	jz	.no_remainder
	inc	ecx		; 如果餘數不為 0 就需增加一個頁表
.no_remainder:
	push	ecx		; 暫存頁表個數

	; 為簡化處理, 所有線性位址對應相等的實體位址. 并且不考慮記憶體空洞.

	; 首先初始化頁目錄
	mov	ax, SelectorFlatRW
	mov	es, ax
	mov	edi, PageDirBase	; 此段首位址為 PageDirBase
	xor	eax, eax
	mov	eax, PageTblBase | PG_P  | PG_USU | PG_RWW
.1:
	stosd
	add	eax, 4096		; 為了簡化, 所有頁表在記憶體中是連續的.
	loop	.1

	; 再初始化所有頁表
	pop	eax			; 頁表個數
	mov	ebx, 1024		; 每個頁表 1024 個 PTE
	mul	ebx
	mov	ecx, eax		; PTE個數 = 頁表個數 * 1024
	mov	edi, PageTblBase	; 此段首位址為 PageTblBase
	xor	eax, eax
	mov	eax, PG_P  | PG_USU | PG_RWW
.2:
	stosd
	add	eax, 4096		; 每一頁指向 4K 的空間
	loop	.2

	mov	eax, PageDirBase
	mov	cr3, eax
	mov	eax, cr0
	or	eax, 80000000h
	mov	cr0, eax
	jmp	short .3
.3:
	nop

	ret
; 分頁機制啟動完畢 ----------------------------------------------------------


; SECTION .data1 之開始 ---------------------------------------------------------------------------------------------
[SECTION .data1]

ALIGN	32

LABEL_DATA:
; 實模式下使用這些符号
; 字元串
_szMemChkTitle:			db	"BaseAddrL BaseAddrH LengthLow LengthHigh   Type", 0Ah, 0
_szRAMSize:			db	"RAM size:", 0
_szReturn:			db	0Ah, 0
;; 變量
_dwMCRNumber:			dd	0	; Memory Check Result
_dwDispPos:			dd	(80 * 6 + 0) * 2	; 螢幕第 6 行, 第 0 列。
_dwMemSize:			dd	0
_ARDStruct:			; Address Range Descriptor Structure
	_dwBaseAddrLow:		dd	0
	_dwBaseAddrHigh:	dd	0
	_dwLengthLow:		dd	0
	_dwLengthHigh:		dd	0
	_dwType:		dd	0
_MemChkBuf:	times	256	db	0
;
;; 保護模式下使用這些符号
szMemChkTitle		equ	BaseOfLoaderPhyAddr + _szMemChkTitle
szRAMSize		equ	BaseOfLoaderPhyAddr + _szRAMSize
szReturn		equ	BaseOfLoaderPhyAddr + _szReturn
dwDispPos		equ	BaseOfLoaderPhyAddr + _dwDispPos
dwMemSize		equ	BaseOfLoaderPhyAddr + _dwMemSize
dwMCRNumber		equ	BaseOfLoaderPhyAddr + _dwMCRNumber
ARDStruct		equ	BaseOfLoaderPhyAddr + _ARDStruct
	dwBaseAddrLow	equ	BaseOfLoaderPhyAddr + _dwBaseAddrLow
	dwBaseAddrHigh	equ	BaseOfLoaderPhyAddr + _dwBaseAddrHigh
	dwLengthLow	equ	BaseOfLoaderPhyAddr + _dwLengthLow
	dwLengthHigh	equ	BaseOfLoaderPhyAddr + _dwLengthHigh
	dwType		equ	BaseOfLoaderPhyAddr + _dwType
MemChkBuf		equ	BaseOfLoaderPhyAddr + _MemChkBuf


; 堆棧就在資料段的末尾
StackSpace:	times	1000h	db	0
TopOfStack	equ	BaseOfLoaderPhyAddr + $	; 棧頂
; SECTION .data1 之結束 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
           

保護模式的一些代碼在之前文章淺談過了

GDT PDE PTE的一些定義

LABEL_GDT:			Descriptor             0,                    0, 0						; 空描述符
LABEL_DESC_FLAT_C:		Descriptor             0,              0fffffh, DA_CR  | DA_32 | DA_LIMIT_4K			; 0 ~ 4G
LABEL_DESC_FLAT_RW:		Descriptor             0,              0fffffh, DA_DRW | DA_32 | DA_LIMIT_4K			; 0 ~ 4G
LABEL_DESC_VIDEO:		Descriptor	 0B8000h,               0ffffh, DA_DRW                         | DA_DPL3	; 顯存首位址
; GDT ------------------------------------------------------------------------------------------------------------------------------------------------------------

GdtLen		equ	$ - LABEL_GDT
GdtPtr		dw	GdtLen - 1				; 段界限
		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
; GDT 選擇子 ----------------------------------------------------------------------------------


BaseOfStack	equ	0100h
PageDirBase	equ	100000h	; 頁目錄開始位址:	1M
PageTblBase	equ	101000h	; 頁表開始位址:		1M + 4K
           

LABEL_START在call了DispStrRealMode後的第一件事就是通過int 15h獲得目前記憶體資訊,接着找Kernel.bin。找到後通過指令lgdt加載GDT,接着關中斷——A20——寫cr0寄存器——jmp dword到保護模式——SetupPaging。

繼續閱讀