尽管您的帖子正文中提出的问题更多的是关于打印寄存器的值,而不是检测系统可用的内存,但我将忠于标题问题,并提供一个如何检测的示例系统内存映射。
作为奖励,提供了一个将 32 位无符号整数显示为十六进制数字的函数,以及一个非常原始的函数print支持占位符。
Detecting memory is not an easy task, it requires full knowledge of the hardware installed1 and cannot be done without it (see Detecting memory http://wiki.osdev.org/Detecting_Memory_(x86)#Manual_Probing on OSDev).
As a simple example think of an aliased memory https://en.wikipedia.org/wiki/Aliasing_(computing)#Hardware_aliasing, the software cannot detect that without any involved and slow method.
承认与 BIOS 的合作是强制性的,我们可以看到 16 位实模式引导加载程序可以使用哪些服务。
上面提到的 OSDev 页面关于检测记忆 http://wiki.osdev.org/Detecting_Memory_(x86)已经有专门用于上述目的的服务列表,可供参考。
我们将重点关注整数 15/AX=E820h http://www.ctyme.com/intr/rb-1741.htm服务。
它的用途是返回内存范围及其描述的列表。
每次调用都会返回下一个描述符,使用ebx
跟踪进展情况。登记册ebx
应被视为不透明值。
尽管描述中Ralf 的 Brown 中断列表,描述符可以是 24 字节长,因此最好使用该长度并最终检查返回的值ecx
区分 20/24 字节描述符。
Once we have the list of descriptors they can be used by the routine dictated to allocating memory2.
It is worth nothing tow things:
描述符是not订购了。一些有缺陷的 BIOS 可能会返回重叠的区域(在这种情况下做出最保守的选择)。
可能有gaps即使描述符已排序。不报告没有内存映射的范围,这是标准孔的情况(范围从 0a0000h 到 0fffffh)。
不过,会报告 BIOS 明确保留的区域(例如从 0f0000h 到 0fffffh 的影子区域)。
In the example below the descriptors are printed on the screen along with the total amount of non reserved memory3.
顺便说一句,您可以使用itoa16
打印 32 位值的函数EAX
,假设您更改了字符在屏幕上打印的方式。
BITS 16
;Set CS to a known value
;This makes the offsets in memory and in source match
;(e.g. __START__ is at offset 5h in the binary image and at addres 7c0h:0005h)
jmp 7c0h:__START__
__START__:
;Set all the segments to CS
mov ax, cs
mov ds, ax
mov es, ax
mov ss, ax
xor sp, sp
;Clear the screen
mov ax, 03h
int 10h
;FS will be used to write into the text buffer
push 0b800h
pop fs
;SI is the pointer in the text buffer
xor si, si
;These are for the INT 15 service
mov di, baseAddress ;Offset in ES where to save the result
xor ebx, ebx ;Start from beginning
mov ecx, 18h ;Length of the output buffer (One descriptor at a time)
;EBP will count the available memory
xor ebp, ebp
_get_memory_range:
;Set up the rest of the registers for INT 15
mov eax, 0e820h
mov edx, 534D4150h
int 15h
jc _error
;Has somethig been returned actually?
test ecx, ecx
jz _next_memory_range
;Add length (just the lower 32 bits) to EBP if type = 1 or 3
mov eax, DWORD [length]
;Avoid a branch (just for the sake of less typing)
mov edx, DWORD [type] ;EDX = 1 | 2 | 3 | 4 (1 and 3 are available memory)
and dx, 01h ;EDX = 1 | 0 | 1 | 0
dec edx ;EDX = 0 | ffffffff | 0 | ffffffff
not edx ;EDX = ffffffff | 0 | ffffffff | 0
and eax, edx ;EAX = length | 0 | length | 0
add ebp, eax
;Show current memory descriptor
call show_memory_range
_next_memory_range:
test ebx, ebx
jnz _get_memory_range
;Print empty line
push WORD strNL
call print
;Print total memory available
push ebp
push WORD strTotal
call print
cli
hlt
_error:
;Print error
push WORD strError
call print
cli
hlt
;Memory descriptor returned by INT 15
baseAddress dq 0
length dq 0
type dd 0
extAttr dd 0
;This function just show the string strFormat with the appropriate values
;taken from the mem descriptor
show_memory_range:
push bp
mov bp, sp
;Extend SP into ESP so we can use ESP in memory operanda (SP is not valid in any addressing mode)
movzx esp, sp
;Last percent
push DWORD [type]
;Last percents pair
push DWORD [length]
push DWORD [length + 04h]
;Add baseAddress and length (64 bit addition)
push DWORD [baseAddress]
mov eax, DWORD [length]
add DWORD [esp], eax ;Add (lower DWORD)
push DWORD [baseAddress + 04h]
mov eax, DWORD [length + 04h]
adc DWORD [esp], 0 ;Add with carry (higher DWORD)
;First percents pair
push DWORD [baseAddress]
push DWORD [baseAddress + 04h]
push WORD strFormat
call print
mov sp, bp ;print is a mixed stdcall/cdecl, remove the arguments
pop bp
ret
;Strings, here % denote a 32 bit argument printed as hex
strFormat db "%% - %% (%%) - %", 0
strError db "Som'thing is wrong :(", 0
strTotal db "Total amount of memory: %", 0
;This is tricky, see below
strNL db 0
;Show a 32 bit hex number
itoa16:
push cx
push ebx
mov cl, 28d
.digits:
mov ebx, eax
shr ebx, cl
and bx, 0fh ;Get current nibble
;Translate nibble (digit to digital)
mov bl, BYTE [bx + hexDigits]
;Show it
mov bh, 0ch
mov WORD [fs:si], bx
add si, 02h
sub cl, 04h
jnc .digits
pop ebx
pop cx
ret
hexDigits db "0123456789abcdef"
;This function is a primitive printf, where the only format is % to show a 32 bit
;hex number
;The "cursor" is kept by SI.
;SI is always aligned to lines, so 1) never print anything bigger than 80 chars
;2) successive calls automatically print into their own lines
;3) SI is assumed at the beginning of a line
;Args
;Format
print:
push bp
mov bp, sp
push di
push cx
mov di, WORD [bp+04h] ;String
mov cx, 80*2 ;How much to add to SI to reach the next line
add bp, 06h ;Pointer to var arg
.scan:
;Read cur char
mov al, [di]
inc di
;Format?
cmp al, '%'
jne .print
;Get current arg and advance index
mov eax, DWORD [bp]
add bp, 04h
;Show the number
call itoa16
;We printed 8 chars (16 bytes)
sub cx, 10h
jmp .scan
.print:
;End of string?
test al, al
je .end
;Normal char, print it
mov ah, 0ch
mov WORD [fs:si], ax
add si, 02h
sub cx, 02h
jmp .scan
.end:
add si, cx
pop cx
pop di
pop bp
ret 02h
;Signature
TIMES 510 - ($-$$) db 0
dw 0aa55h
在 64MiB Bochs 仿真机器中,结果是
格式在哪里开始 - 结束(大小) - 类型.
使用我们得到的图片
计算得出的内存总量为 66.711.552 字节或 64 MiB - 1 KiB (EBDA) - 96 KiB(阴影区域) - 288 KiB(标准孔)。
ACPI 表被视为可用,因为它们是可回收的。
1 Particularly of what was the part of the north bridge, now iMC, dedicated to handling the DRAM. Information about installed modules (Mostly DIMM and mobile variants) can be retrieved through SPD https://en.wikipedia.org/wiki/Serial_presence_detect using either a SMBus http://smbus.org/specs/smbus20.pdf or I2C https://en.wikipedia.org/wiki/I%C2%B2C controller.
The BIOS then consider the enabled memory mapped devices and the bus topology (along with its routing and bridging information) and exposes all this through the the SMBios specification http://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.0.0.pdf.
2 Since it will use some sort of range descriptors anyway, eventually a format conversion is performed.
3 This count includes the newly type 5 (Bad memory) range.