|
|
@@ -4,7 +4,6 @@
|
|
|
%include "boot.inc"
|
|
|
SECTION LOADER vstart=LOADER_BASE_ADDR
|
|
|
LOADER_STACK_TOP equ LOADER_BASE_ADDR
|
|
|
-jmp loader_start
|
|
|
|
|
|
; 构建 GDT 及其内部描述符(GDT 的第 0 个描述符不可用)
|
|
|
GDT_BASE: dd 0x00000000
|
|
|
@@ -21,9 +20,17 @@ VIDEO_DESC: dd 0x80000007 ;limit=(0xbffff-0xb8000)/4k=0x7
|
|
|
|
|
|
GDT_SIZE equ $ - GDT_BASE
|
|
|
GDT_LIMIT equ GDT_SIZE - 1
|
|
|
-times 60 dq 0 ; 此处预留 60 个描述符的空位
|
|
|
+times 120 dd 0 ; 此处预留 60 个描述符的空位
|
|
|
|
|
|
-SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于 (CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0
|
|
|
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
+; total_mem_bytes 用于保存内在容量,以字节为单位,此位置比较好记
|
|
|
+; 当前偏移 loader.bin 文件头(4*8+60*8=512 字节)0x200 字节 loader.bin 的加载地址是 0x900
|
|
|
+; 故 total_mem_bytes 内存地址是 0xb00
|
|
|
+; offset 0x200
|
|
|
+total_mem_bytes dd 0
|
|
|
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
+
|
|
|
+SELECTOR_CODE equ (0x0001<<3) + TI_GDT + RPL0 ; 相当于(CODE_DESC - GDT_BASE)/8 + TI_GDT + RPL0
|
|
|
SELECTOR_DATA equ (0x0002<<3) + TI_GDT + RPL0
|
|
|
SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0
|
|
|
|
|
|
@@ -31,6 +38,9 @@ SELECTOR_VIDEO equ (0x0003<<3) + TI_GDT + RPL0
|
|
|
gdt_ptr dw GDT_LIMIT
|
|
|
dd GDT_BASE
|
|
|
loadermsg db '2 loader in real.'
|
|
|
+; 人工对齐 total_mem_bytes4+gdt_ptr6+loadermsg17+ards_buf227+ards_nr2, 共 256 字节
|
|
|
+ards_buf times 227 db 0
|
|
|
+ards_nr dw 0 ; 用于记录 ARDS 结构数量
|
|
|
|
|
|
;----------------------------------------------------------------
|
|
|
; INT 0x10 功能号:0x13 功能描述: 打印字符串
|
|
|
@@ -48,45 +58,133 @@ loadermsg db '2 loader in real.'
|
|
|
; 2 ---- 字符串中含显示字符和显示属性。显示后,光标位置不变
|
|
|
; 3 ---- 字符串中含显示字符和显示属性。显示后,光标位置改变
|
|
|
; 无返回值
|
|
|
+; offset = 0x200 + 0x100 = 0x300
|
|
|
loader_start:
|
|
|
- mov sp, LOADER_BASE_ADDR
|
|
|
- mov bp, loadermsg ; ES:BP = 字符串地址
|
|
|
- mov cx, 17 ; CX = 字符串长度
|
|
|
- mov ax, 0x1301 ; AH = 13h, AL = 01h
|
|
|
- mov bx ,0x001f ; 页号 0(BH=0)蓝底粉红色(BL=1fh)
|
|
|
- mov dx, 0x1800 ; 坐标(行,列)
|
|
|
- int 0x10 ; 10h 号中断
|
|
|
-
|
|
|
-;---------------------- 准备进入保护模式 ------------------------------------------
|
|
|
-; 1 打开 A20
|
|
|
-; 2 加载 GDT
|
|
|
-; 3 将 cr0 的 pe 位置 1
|
|
|
-
|
|
|
- ;-------------------------- 打开 A20 --------------------------------
|
|
|
- in al, 0x92
|
|
|
- or al, 0000_0010b
|
|
|
- out 0x92, al
|
|
|
-
|
|
|
- ;-------------------------- 加载 GDT --------------------------------
|
|
|
- lgdt [gdt_ptr]
|
|
|
-
|
|
|
- ;-------------------------- cr0 第 0 位置 1 --------------------------
|
|
|
- mov eax, cr0
|
|
|
- or eax, 0x00000001
|
|
|
- mov cr0, eax
|
|
|
-
|
|
|
- jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线
|
|
|
+ ;------------------------ 获取物理内存容量 ---------------------------------------
|
|
|
+ ; int 15h eax=E820h, edx = 534D4150h('SMAP') 获取内存布局
|
|
|
+ xor ebx, ebx ; 第一次调用时,ebx 值要为 0
|
|
|
+ mov edx, 0x534d4150 ; edx 只赋值一次,循环体中不会改变(字符串 SMAP 的 ASCII 码)
|
|
|
+ mov di, ards_buf ; ards 结构缓冲区 es:di(es mbr.S 中已经赋值)
|
|
|
+ .e820_mem_get_loop:
|
|
|
+ mov eax, 0xe820 ; 执行 int 0x15 后, eax 值变为 0x534d150, 所以每次执行 Int 前都要更新子功能号
|
|
|
+ mov ecx, 20 ; ards 地址范围描述结构大小是 20 字节
|
|
|
+ int 0x15
|
|
|
+ jc .e820_failed_so_try_e801 ; cf 位为 1 则有错误发生,尝试 0xe801 子功能
|
|
|
+ add di, cx ; 使 di 增加 20 字节指向缓冲区中新的 ARDS 结构位置
|
|
|
+ inc word [ards_nr] ; 记录 ards 数量
|
|
|
+ cmp ebx, 0 ; 若 ebx 为 0 且 cf 不为 1,这说明 ards 全部返回,当前已是最后一个
|
|
|
+ jnz .e820_mem_get_loop
|
|
|
+
|
|
|
+ ; 在所有 ards 结构中,找出(base_addr_low + length_low) 的最大值,即内存的容量
|
|
|
+ mov ecx, [ards_nr] ; 遍历第一个 ards 结构体,循环次数是 ards 的数量
|
|
|
+ mov ebx, ards_buf
|
|
|
+ xor edx, edx ; edx 为最大的内存容量,在此先清 0
|
|
|
+ .find_max_mem_area: ; 不需要判断 type 是否为 1, 最大的内存块一定是可被使用的
|
|
|
+ mov eax, [ebx] ; base_addr_low
|
|
|
+ add eax, [ebx+8] ; length_low
|
|
|
+ add ebx, 20 ; 指向缓冲区中下一个 ards 结构
|
|
|
+ cmp edx, eax
|
|
|
+ jge .next_ards
|
|
|
+ mov edx, eax ; edx 为总内存大小
|
|
|
+ .next_ards:
|
|
|
+ loop .find_max_mem_area
|
|
|
+ jmp .mem_get_ok
|
|
|
+
|
|
|
+ ; int 15h ax=E801h 获取内存大小,最大支持 4G
|
|
|
+ ; 返回后, ax cx 值一样, 以 KB 为单位, bx dx 值一样, 以 64KB 为单位
|
|
|
+ ; 在 ax 和 cx 寄存器中低 16MB, 在 bx 和 dx 寄存器中为 16MB 到 4GB
|
|
|
+ .e820_failed_so_try_e801:
|
|
|
+ mov ax, 0xe801
|
|
|
+ int 0x15
|
|
|
+ jc .e801_failed_so_try_88 ; 若当前 e801 方法失败,就尝试 0x88 方法
|
|
|
+ ;;; 1 先算出低 15MB 的内存 ax 和 cx 中是以 KB 为单位的内存数量,将其转换为以 byte 为单位
|
|
|
+ mov cx, 0x400 ; 0x400=1024, cx 和 ax 值一样,cx 用作乘数
|
|
|
+ mul cx
|
|
|
+ shl edx, 16
|
|
|
+ and eax, 0xFFFF
|
|
|
+ or edx, eax
|
|
|
+ add edx, 0x100000 ; ax 只是 15MB,故要加 1MB=1024*1024=1048576=0x100000
|
|
|
+ mov esi, edx ; 先把低 15MB 的内存容量存入 esi 寄存器备份
|
|
|
+ ;;; 2 再将 16MB 以上的内存转换为 byte 为单位, 寄存器 bx 和 dx 中是以 64KB 为单位的内存数量
|
|
|
+ xor eax, eax
|
|
|
+ mov ax, bx
|
|
|
+ mov ecx, 0x10000 ; 64*1024=0x10000
|
|
|
+ mul ecx ; 32 位乘法,默认的被乘数是 eax,积为 64 位,高 32 位存入 edx, 低 32 位存入 eax
|
|
|
+
|
|
|
+ add esi, eax ; 由于些方法只能测试 4G 以内的内存,故 32 位 eax 足够, edx 为 0
|
|
|
+ mov edx, esi ; edx 为总内存大小
|
|
|
+ jmp .mem_get_ok
|
|
|
+
|
|
|
+ ; int 15h ah=88h 获取内存大小,只能获取 64MB 之内
|
|
|
+ .e801_failed_so_try_88:
|
|
|
+ ; int 15 后,ax 存入的是以 KB 为单位的内存容量
|
|
|
+ mov ah, 0x88
|
|
|
+ int 0x15
|
|
|
+ jc .error_hlt
|
|
|
+ and eax, 0xffff
|
|
|
+ mov cx, 0x400 ; 1024
|
|
|
+ mul cx
|
|
|
+ ; 16 位乘法,被乘数是 ax,积为 32 位,高 16 位在 dx 中,低 16 位在 ax 中
|
|
|
+ shl edx, 16 ; 把 dx 移动高 16 位
|
|
|
+ and eax, 0xFFFF
|
|
|
+ or edx, eax ; 把积的低 16 位组合到 edx 中,成为 32 位的积
|
|
|
+ add edx, 0x100000 ; 0x88 子功能只会返回 1MB 以上的内存,故实际内存大小要加上 1MB=1024*1024=1048576=0x100000
|
|
|
+ jmp .mem_get_ok
|
|
|
+
|
|
|
+ .error_hlt:
|
|
|
+ mov byte [gs:0], 'e'
|
|
|
+ mov byte [gs:1], 'r'
|
|
|
+ mov byte [gs:2], 'r'
|
|
|
+ mov byte [gs:3], 'o'
|
|
|
+ mov byte [gs:4], 'r'
|
|
|
+ mov byte [gs:5], '_'
|
|
|
+ mov byte [gs:6], 'h'
|
|
|
+ mov byte [gs:7], 'l'
|
|
|
+ mov byte [gs:8], 't'
|
|
|
+ jmp $
|
|
|
+
|
|
|
+ .mem_get_ok:
|
|
|
+ mov [total_mem_bytes], edx ; 将内存换为 byte 单位后存入 total_mem_bytes 处
|
|
|
+
|
|
|
+ ; 显示 loadermsg
|
|
|
+ mov sp, LOADER_BASE_ADDR
|
|
|
+ mov bp, loadermsg ; ES:BP = 字符串地址
|
|
|
+ mov cx, 17 ; CX = 字符串长度
|
|
|
+ mov ax, 0x1301 ; AH = 13h, AL = 01h
|
|
|
+ mov bx ,0x001f ; 页号 0(BH=0)蓝底粉红色(BL=1fh)
|
|
|
+ mov dx, 0x1800 ; 坐标(行,列)
|
|
|
+ int 0x10 ; 10h 号中断
|
|
|
+
|
|
|
+ ;---------------------- 准备进入保护模式 ------------------------------------------
|
|
|
+ ; 1 打开 A20
|
|
|
+ ; 2 加载 GDT
|
|
|
+ ; 3 将 cr0 的 pe 位置 1
|
|
|
+
|
|
|
+ ;-------------------------- 打开 A20 --------------------------------
|
|
|
+ in al, 0x92
|
|
|
+ or al, 0000_0010b
|
|
|
+ out 0x92, al
|
|
|
+
|
|
|
+ ;-------------------------- 加载 GDT --------------------------------
|
|
|
+ lgdt [gdt_ptr]
|
|
|
+
|
|
|
+ ;-------------------------- cr0 第 0 位置 1 --------------------------
|
|
|
+ mov eax, cr0
|
|
|
+ or eax, 0x00000001
|
|
|
+ mov cr0, eax
|
|
|
+
|
|
|
+ jmp dword SELECTOR_CODE:p_mode_start ; 刷新流水线
|
|
|
|
|
|
[bits 32]
|
|
|
p_mode_start:
|
|
|
- mov ax, SELECTOR_DATA
|
|
|
- mov ds, ax
|
|
|
- mov es, ax
|
|
|
- mov ss, ax
|
|
|
- mov esp, LOADER_STACK_TOP
|
|
|
- mov ax, SELECTOR_VIDEO
|
|
|
- mov gs, ax
|
|
|
+ mov ax, SELECTOR_DATA
|
|
|
+ mov ds, ax
|
|
|
+ mov es, ax
|
|
|
+ mov ss, ax
|
|
|
+ mov esp, LOADER_STACK_TOP
|
|
|
+ mov ax, SELECTOR_VIDEO
|
|
|
+ mov gs, ax
|
|
|
|
|
|
- mov byte [gs:160], 'P'
|
|
|
+ mov byte [gs:160], 'P'
|
|
|
|
|
|
- jmp $
|
|
|
+ jmp $
|