赞
踩
1.地址范围描述符结构(ARDS)

type字段
描述内存的类型

我们在32位环境下工作,所以在ARDS结构属性中,我们只用到低32位属性,BaseAddrLow+LengthLow 是一片内存的上限。
特别注意!!!
正常情况下,不会出现较大的内存区域不可用的情况,这意味着此返回的ARDS结构中,BaseAddrLow+LengthLow最大值就是主板配置的物理内存容量。
2.利用子功能0xE820(中断号15)获取内存容量
系统中内存各部分的类型属性不同,BIOS按照类型属性划分这片系统内存,因此查询呈迭代式(每次BIOS只返回一种类型的内存信息)。

3.利用子功能0xE801(中断号15)获取内存容量


只有在实际物理内存容量大于16时,BX才有用,否则只有AX有值。
实际物理内存和实际检测内存大小为什么差1M?
历史遗留问题,为了兼容老的ISA设备,1M作为缓冲区
然后就是(AX和CX),(BX和DX)

其英文描述完全相同,再结合后面代码解释

可知其应该是两者应该一模一样的。
4.利用子功能0xE88(中断号15)获取内存容量

5.代码
Loader.S
%include "boot.inc" section loader vstart=LOADER_BASE_ADDR LOADER_STACK_TOP equ LOADER_BASE_ADDR jmp loader_start GDT_BASE: dd 0x00000000 dd 0x00000000 CODE_DESC: dd 0x0000FFFF dd DESC_CODE_HIGH4 DATA_STACK_DESC: dd 0x0000FFFF dd DESC_DATA_HIGH4 VIDEO_DESC: dd 0x80000007 dd DESC_VIDEO_HIGH4 GDT_SIZE equ $ - GDT_BASE GDT_LIMIT equ GDT_SIZE - 1 times 59 dq 0 times 5 db 0 total_mem_bytes dd 0 ; save memory capacity ; gdt's pointer gdt_ptr dw GDT_LIMIT dd GDT_BASE ards_buf times 244 db 0 ; buffer_size ards_nr dw 0 ; buffer_num SELECTOR_CODE equ (0x0001 << 3) + TI_GDT + RPL0 SELECTOR_DATA equ (0x0002 << 3) + TI_GDT + RPL0 SELECTOR_VIDEO equ (0x0003 << 3) + TI_GDT + RPL0 loader_start: ;int 15h eax=0000E820h,edx=534D4150h('SMAP'),获取内存布局 xor ebx,ebx ;第一次调用时,ebx为0 mov edx,0x534d4150 ;edx标志值 mov di,ards_buf ;ards结构缓冲区 .e820_mem_get_loop: ;执行int 0x15后,eax值变为上面edx的值 mov eax,0x0000e820 ;所以每次执行int前都要更新为子功能号 mov ecx,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_add_low + length_low)的最大值,即内存的容量 mov cx,[ards_nr] ;遍历每一个ARDS结构体,循环次数为ARDS的数目 mov ebx,ards_buf xor edx,edx .find_max_mem_area: ;无需判断type是否为1,最大内存块一定可被使用 mov eax,[ebx] add eax,[ebx+8] add ebx,20 cmp edx,eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量 jge .next_ards mov edx,eax .next_ards: loop .find_max_mem_area jmp .mem_get_ok ;E801获取内存大小,最大支持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_try88 ;若当前e801方法失败,就尝试0x88方法 ;1算出低15MB的内存 ;ax和cx是以KB为单位的内存数量,将其转换为byte为单位 mov cx,0x400 mul cx shl edx,16 and eax,0x000FFFF or edx,eax add edx,0x100000 mov esi,edx ;2 再将16MB以上的内存转换为byte为单位 ;寄存器bx和dx中是以64KB为单位的内存数量 xor eax,eax mov ax,bx mov ecx,0x10000 mul ecx add esi,eax ;由于此方法只能测出4GB以内的内存,故32位eax足够了 ;edx肯定为0,只有eax便可 mov edx,esi jmp .mem_get_ok .e801_failed_so_try88: ;int 15后,ax存入的是KB为单位的内存容量 mov ah,0x88 int 0x15 jc .error_hlt add eax,0x0000FFFF ;16位乘法,被乘数ax,积在32位。积的高16位在dx中,积的低16位在ax中 mov cx,0x400 mul cx shl edx,16 or edx,eax add edx,0x100000 .error_hlt: hlt .mem_get_ok: mov [total_mem_bytes],edx ; 1 打开A20 gate ; 2 加载gdt ; 3 将cr0 的 pe位置1 in al,0x92 ;端口号0x92 中 第1位变成1即可 or al,0000_0010b out 0x92,al lgdt [gdt_ptr] mov eax,cr0 ;cr0寄存器第0位设置位1 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 byte [gs:160],'P' jmp $
然后在运行方面在啰嗦几句。如果代码,你是自己敲得,然后运行起来发现0xb00处大小并不是32M而是0,那很可能是ards_buf位置出错了不在0xb00(前面定义的数据不是200字节)
可以调试一下。

输入 xp /200 0xb00,(查询0xb00之后的200字节)
当然你的位置可能也会小,将0xb00改小一下就行,不出意外的话,应该在着附近。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。