简单来说,每执行一次这个指令将会将一个描述内存区域的数据结构保存到ES:DI,然后返回一个ebx,用于确定下一个能够探测的内存区域,当ebx=0时,表示当前已经时最后一个内存区域了。那么ebx的初始值呢,就是设置成ebx=0。其中可能遇到检查发生错误的情况,这个指令就会直接将CF设置为1。
下图时输入时各个寄存器的作用:
寄存器 | 作用 |
---|---|
eax | 功能码,当输入e820h时能够探测内存 |
ebx | 主要用于指向内存区域,第一次调用时ebx=0,被称为continuation value |
es:di | 用于指令执行后,在指向的内存写入描述内存区域的数据结构ARDS(Address Range Descriptor Structure) |
ecx | 用于限制指令填充的ARDS的大小,实际上大多数情况这个是无效的,无论ecx设置为多少,BIOS始终会填充20字节的ARDS |
edx | 0534D4150h(‘SMAP’),输入时在edx,输出时将会在eax中 |
下面是输出时各个寄存器(标志)的结果:
寄存器 | 结果 |
---|---|
CF | 当没有发生错误时,CF=0,否则CF=1 |
eax | 0534D4150h(‘SMAP’) |
ebx | 指向下一个内存区域,而不是调用之前的内存区域,当ebx=0且CF=0时,表示当前是最后一个内存区域。 |
es:di | 和调用之前一样,如果要保存多个ARDS,需要手动修改es:di |
ecx | 返回写入的ARDS的大小 |
ARDS的结构(共20字节,Type为4字节)
偏移 | 名称 | 意义 |
---|---|---|
0 | BaseAddrLow | 基地址的低32位 |
4 | BaseAddrHigh | 基地址的高32位 |
8 | LengthLow | 长度(字节)的低32位 |
12 | LengthHigh | 长度(字节)的高32位 |
16 | Type | 这个内存区域的类型 |
ARDS的Type取值如下:
取值 | 名称 | 意义 |
---|---|---|
1 | AddressRangeMemory | 可以被OS使用的内存 |
2 | AddressRangeReserved | 正在使用的区域或者不能系统保留不能使用的区域 |
其他 | 未定义 | 各个具体机器会有不同的意义,在这里我们暂时不用关心,将它视为AddressRangeReserved即可 |
(好像csdn的汇编代码块高亮用不了,就只能这样了)
;当前处于实模式
_MemChkBuf: times 256 db 0
_dwMCRNumber: dw 0 ;记录ARDS的数量
...
mov ebx,0
mov di,_MemChkBuf ;请忽略es,假设已经正确设置好了
.loop:
mov eax,0e820h
mov ecx,20
mov edx,0534D4150h ;'SMAP'
int 15h
jc LABEL_MEM_CHK_FAIL ;检查CF,是否发生错误
add di,20 ;由于执行INT 15h之后es和di不发生改变,就需要手动修改
inc dword [_dwMCRNumber] ;记录ARDS的数量
cmp ebx,0;判断是否到达最后一个内存区域
jne .loop
jmp LABEL_MEM_CHK_OK ;ebx=0到达最后一个内存区域
LABEL_MEM_CHK_FAIL:
mov dword [_dwMCRNumber],0
LABEL_MEM_CHK_OK:
如果要读取就从开始es:di开始的地方开始逐个读取内存区域,得到的结果和这个类似
当然Linux上也有e820都差不多