runningwater 1 год назад
Родитель
Сommit
fa17f3c29e
4 измененных файлов с 140 добавлено и 42 удалено
  1. 1 1
      bochsrc
  2. 137 39
      boot/loader.S
  3. 2 2
      boot/mbr.S
  4. BIN
      hd30M.img

+ 1 - 1
bochsrc

@@ -18,4 +18,4 @@ log: bochsout.txt
 
 
 # logprefix: %t%e%d
 # logprefix: %t%e%d
 
 
-# memory: guest=512, host=256, block_size=512
+memory: guest=32, host=32, block_size=512

+ 137 - 39
boot/loader.S

@@ -4,7 +4,6 @@
 %include "boot.inc"
 %include "boot.inc"
 SECTION LOADER vstart=LOADER_BASE_ADDR
 SECTION LOADER vstart=LOADER_BASE_ADDR
 LOADER_STACK_TOP equ LOADER_BASE_ADDR
 LOADER_STACK_TOP equ LOADER_BASE_ADDR
-jmp loader_start
 
 
 ; 构建 GDT 及其内部描述符(GDT 的第 0 个描述符不可用)
 ; 构建 GDT 及其内部描述符(GDT 的第 0 个描述符不可用)
 GDT_BASE: dd 0x00000000
 GDT_BASE: dd 0x00000000
@@ -21,9 +20,17 @@ VIDEO_DESC: dd 0x80000007   ;limit=(0xbffff-0xb8000)/4k=0x7
 
 
 GDT_SIZE equ $ - GDT_BASE
 GDT_SIZE equ $ - GDT_BASE
 GDT_LIMIT equ GDT_SIZE - 1
 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_DATA equ (0x0002<<3) + TI_GDT + RPL0
 SELECTOR_VIDEO equ (0x0003<<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 
 gdt_ptr dw GDT_LIMIT 
         dd GDT_BASE
         dd GDT_BASE
 loadermsg db '2 loader in real.'
 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   功能描述: 打印字符串
 ; INT 0x10  功能号:0x13   功能描述: 打印字符串
@@ -48,45 +58,133 @@ loadermsg db '2 loader in real.'
 ;   2 ---- 字符串中含显示字符和显示属性。显示后,光标位置不变
 ;   2 ---- 字符串中含显示字符和显示属性。显示后,光标位置不变
 ;   3 ---- 字符串中含显示字符和显示属性。显示后,光标位置改变
 ;   3 ---- 字符串中含显示字符和显示属性。显示后,光标位置改变
 ; 无返回值
 ; 无返回值
+; offset = 0x200 + 0x100 = 0x300
 loader_start:
 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]
 [bits 32]
 p_mode_start:
 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 $

+ 2 - 2
boot/mbr.S

@@ -17,10 +17,10 @@ SECTION MBR vstart=MBR_BASE_ADDR
 	
 	
     mov eax, LOADER_START_SECTOR ; 起始扇区地址: LBA 地址
     mov eax, LOADER_START_SECTOR ; 起始扇区地址: LBA 地址
     mov bx, LOADER_BASE_ADDR ; 写入的地址
     mov bx, LOADER_BASE_ADDR ; 写入的地址
-    mov cx, 2 ; 待写入的扇区数(4 个扇区)
+    mov cx, 4 ; 待写入的扇区数(4 个扇区)
     call rd_disk_m_16  ; 读取硬盘数据
     call rd_disk_m_16  ; 读取硬盘数据
 
 
-    jmp LOADER_BASE_ADDR
+    jmp LOADER_BASE_ADDR+0x300 ; 直接跳转到 loader 的 loader_start 位置
 
 
 
 
 ;清屏,利用 0x06 号功能,上巻全部行,则可清屏
 ;清屏,利用 0x06 号功能,上巻全部行,则可清屏