如何获取 RAM 大小,引导加载程序
How to get RAM Size , bootloader
我想问一下如何在引导加载程序中获取 Total RAM Size 和 Usable RAM Size。截至目前,我知道如何获得较低的内存。但出于某种原因我无法将其打印到屏幕上,因为它保存在 ax 寄存器中。这是我目前所拥有的:
[BITS 16] ; BootLoader always starts 16 BIT Moded
jmp main_bootloader ; Jump to Main Bootloader
;************** INITALIZED VARIABLES *********************;
string db 'BoneOS Loading . . .', 0x0
string2 db 'Starting of 16Bit Bootloader' , 0x0
press_to_cont db 'Press any key to continue . . .' , 0x0
carry_flag_err db ' CARRY FLAG HAS BEEN SET! ERROR ' , 0x0
magic_number equ 0x534D4150
limit dw 0
base dw 0
low_memory dd 0
answer resb 64
;*********************************************************;
;******************** GDTs *****************************;
null_descriptor :
dd 0 ; null descriptor--just fill 8 bytes with zero
dd 0
; Notice that each descriptor is exactally 8 bytes in size. THIS IS IMPORTANT.
; Because of this, the code descriptor has offset 0x8.
code_descriptor: ; code descriptor. Right after null descriptor
dw 0FFFFh ; limit low
dw 0 ; base low
db 0 ; base middle
db 10011010b ; access
db 11001111b ; granularity
db 0 ; base high
; Because each descriptor is 8 bytes in size, the Data descritpor is at offset 0x10 from
; the beginning of the GDT, or 16 (decimal) bytes from start.
data_descriptor: ; data descriptor
dw 0FFFFh ; limit low (Same as code)
dw 0 ; base low
db 0 ; base middle
db 10010010b ; access
db 11001111b ; granularity
db 0 ; base high
end_of_gdt:
toc:
dw end_of_gdt - null_descriptor - 1 ; limit (Size of GDT)
dd null_descriptor ; base of GDT
load_gdt:
lgdt [toc]
.done:
ret
;*********************************************************;
;*************** LABELS FOR MAIN **************************;
print_char_boot:
mov ah, 0Eh ; Code For BIOS To Print Char 0Eh
.repeat:
lodsb ; Load Byte From SI Register
cmp al, 0 ; Compare AL With 0 If so Done
je .done
int 10h ; Call Interupt. Checks AH Register for code 0EH = Print char
jmp .repeat ; Loop Back
.done:
ret ; Return to previous code
print_new_line:
mov al, 0 ; null terminator '[=10=]'
;Adds a newline break '\n'
mov ah, 0x0E
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
ret
get_pressed_key:
mov ah, 0
int 0x16 ;BIOS Call. Key goes to al register
ret
GET_RAM_SIZE:
reboot:
mov si, press_to_cont
call print_char_boot
call get_pressed_key ; Gets Pressed Key
int 19h ;Reboot
ret
enable_A20: ; Enabling A20 Line For Full Memory
cli ; Stop Interupts before doing so
call a20wait ; a20wait call
mov al,0xAD ; Send 0xAD Command to al register
out 0x64,al ; Send command 0xad (disable keyboard).
call a20wait ; When controller ready for command
mov al,0xD0 ; Send 0xD0 Command to al register
out 0x64,al ; Send command 0xd0 (read from input)
call a20wait2 ; When controller ready for command
in al,0x60 ; Read input from keyboard
push eax ; Save Input by pushing to stack
call a20wait ; When controller ready for command
mov al,0xD1 ; mov 0xD1 Command to al register
out 0x64,al ; Set command 0xd1 (write to output)
call a20wait ; When controller ready for command
pop eax ; Pop Input from Keyboard
or al,2 ; Mov 0xD3 to al register
out 0x60,al ; Set Command 0xD3
call a20wait ; When controller ready for command
mov al,0xAE ; Mov Command 0xAE To al register
out 0x64,al ; Write command 0xae (enable keyboard)
call a20wait ; When controller ready for command
sti ; Enable Interrupts after enabling A20 Line
ret
a20wait:
in al,0x64 ; input from 0x64 port, goes to al register
test al,2 ; compares al register with 2
jnz a20wait ; If it is zero loop again
ret
a20wait2:
in al,0x64 ; input from 0x64 port, goes to al register
test al,1 ; compares al register with 2
jz a20wait2 ; If it is zero loop again
ret
get_lower_memory:
clc ; Clears Carry Flag
int 0x12 ; BIOS Call Request Lower Memory Size in KB
jc .err ; If Carry Flag Has Been Set , the system its running on dosent support this
jmp .done ; If Sucessfull ax register contains contiguous low memory in KB
.err:
times 5 call print_new_line ; Prints New Line
mov si, carry_flag_err
call print_char_boot
jmp .repeat
.done:
ret
.repeat:
jmp .repeat
;**************************************************************;
;*******************'MAIN' BOOTLOADER FUNCTION ****************;
main_bootloader:
xor ax, ax
mov ss, ax
mov sp, 4096
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
mov si, string ; si register usefull for lodsb command
call print_char_boot ; Call print_char_boot label below
call print_new_line ; Prints New Line
mov si, string2
call print_char_boot
times 2 call print_new_line
; Enable A20 Line
call enable_A20
call get_lower_memory ; Get Low Memory
mov si,ax
call print_char_boot
times 5 call print_new_line
call reboot ; Reboot
;call null_descriptor
jmp $ ; Infinite Loop
;Bootloader gets infinite loop
;Incase No Infinite Loop in Kernel
;****************************************************************;
;************************* BOOTLOADER REQUIREMENTS **************;
times 510 - ($ - $$) db 0 ; Has to be 512 bytes .. Repeats 510 byes to make it so
dw 0xAA55 ; BootLoader Sig. To Validate this is a bootloader
;
****************************************************************;
正如你在我的主页上看到的那样,我是 call get_lower_memory ; Get Low Memory
,以获得低内存。但我已经测试过打印 ax 寄存器,但屏幕上没有显示任何内容。而且我也不知道如何在系统中获得总的和可用的内存。帮助将不胜感激!
虽然你的 post 的 body 中提出的问题更多是关于打印寄存器的值而不是关于检测系统可用的内存,但我会很忠诚针对标题问题,并举例说明如何检测系统内存映射。
作为奖励,提供了将 32 位无符号整数显示为十六进制数字的函数,以及支持占位符的非常原始的 print。
检测内存不是一件容易的事,它需要对安装的硬件有全面的了解1,没有它就无法完成(参见 OSDev 上的 Detecting memory)。
作为一个简单的例子,想想一个 aliased memory,如果没有任何涉及和缓慢的方法,软件无法检测到它。
承认与 BIOS 的合作是强制性的,我们可以看到 16 位实模式引导加载程序可以使用哪些服务。
上面提到的 OSDev 关于 detecting memory 的页面已经有一个专用于标题目的的服务列表,请参考。
我们将专注于 Int 15/AX=E820h 服务。
它的用途是 return 内存范围列表及其描述。
每次调用 return 下一个描述符,使用 ebx
来跟踪进度。寄存器 ebx
应被视为不透明值。
尽管 Ralf's Brown Interrupt List 中有描述,但描述符可以是 24 字节长,因此最好使用该长度并最终检查 returned in ecx
中的值] 区分 20/24 字节的描述符。
一旦我们有了描述符列表,它们就可以被分配内存的例程使用2.
拖东西不值钱:
描述符未排序。一些有问题的 BIOS 可能 return 重叠 区域(在这种情况下做出最保守的选择)。
即使对描述符进行排序,也可能存在差距。不报告没有内存映射的范围,这是标准孔的情况(范围从 0a0000h 到 0fffffh)。
虽然报告了 BIOS 明确保留的区域(例如从 0f0000h 到 0fffffh 的阴影区域)。
在下面的示例中,描述符与非保留内存总量一起打印在屏幕上3。
顺便说一句,你可以使用itoa16
函数在EAX
中打印一个32位值,假设你改变了字符在屏幕上的打印方式。
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 特别是北桥的部分,现在是iMC,专用于处理DRAM。可以通过 SPD using either a SMBus or I2C 控制器检索有关已安装模块(主要是 DIMM 和移动变体)的信息。
BIOS 然后考虑启用的内存映射设备和总线拓扑(连同其路由和桥接信息)并通过 SMBios specification.
公开所有这些
2 因为它无论如何都会使用某种范围描述符,所以最终会执行格式转换。
3 此计数包括新类型 5(坏内存)范围。
我想问一下如何在引导加载程序中获取 Total RAM Size 和 Usable RAM Size。截至目前,我知道如何获得较低的内存。但出于某种原因我无法将其打印到屏幕上,因为它保存在 ax 寄存器中。这是我目前所拥有的:
[BITS 16] ; BootLoader always starts 16 BIT Moded
jmp main_bootloader ; Jump to Main Bootloader
;************** INITALIZED VARIABLES *********************;
string db 'BoneOS Loading . . .', 0x0
string2 db 'Starting of 16Bit Bootloader' , 0x0
press_to_cont db 'Press any key to continue . . .' , 0x0
carry_flag_err db ' CARRY FLAG HAS BEEN SET! ERROR ' , 0x0
magic_number equ 0x534D4150
limit dw 0
base dw 0
low_memory dd 0
answer resb 64
;*********************************************************;
;******************** GDTs *****************************;
null_descriptor :
dd 0 ; null descriptor--just fill 8 bytes with zero
dd 0
; Notice that each descriptor is exactally 8 bytes in size. THIS IS IMPORTANT.
; Because of this, the code descriptor has offset 0x8.
code_descriptor: ; code descriptor. Right after null descriptor
dw 0FFFFh ; limit low
dw 0 ; base low
db 0 ; base middle
db 10011010b ; access
db 11001111b ; granularity
db 0 ; base high
; Because each descriptor is 8 bytes in size, the Data descritpor is at offset 0x10 from
; the beginning of the GDT, or 16 (decimal) bytes from start.
data_descriptor: ; data descriptor
dw 0FFFFh ; limit low (Same as code)
dw 0 ; base low
db 0 ; base middle
db 10010010b ; access
db 11001111b ; granularity
db 0 ; base high
end_of_gdt:
toc:
dw end_of_gdt - null_descriptor - 1 ; limit (Size of GDT)
dd null_descriptor ; base of GDT
load_gdt:
lgdt [toc]
.done:
ret
;*********************************************************;
;*************** LABELS FOR MAIN **************************;
print_char_boot:
mov ah, 0Eh ; Code For BIOS To Print Char 0Eh
.repeat:
lodsb ; Load Byte From SI Register
cmp al, 0 ; Compare AL With 0 If so Done
je .done
int 10h ; Call Interupt. Checks AH Register for code 0EH = Print char
jmp .repeat ; Loop Back
.done:
ret ; Return to previous code
print_new_line:
mov al, 0 ; null terminator '[=10=]'
;Adds a newline break '\n'
mov ah, 0x0E
mov al, 0x0D
int 0x10
mov al, 0x0A
int 0x10
ret
get_pressed_key:
mov ah, 0
int 0x16 ;BIOS Call. Key goes to al register
ret
GET_RAM_SIZE:
reboot:
mov si, press_to_cont
call print_char_boot
call get_pressed_key ; Gets Pressed Key
int 19h ;Reboot
ret
enable_A20: ; Enabling A20 Line For Full Memory
cli ; Stop Interupts before doing so
call a20wait ; a20wait call
mov al,0xAD ; Send 0xAD Command to al register
out 0x64,al ; Send command 0xad (disable keyboard).
call a20wait ; When controller ready for command
mov al,0xD0 ; Send 0xD0 Command to al register
out 0x64,al ; Send command 0xd0 (read from input)
call a20wait2 ; When controller ready for command
in al,0x60 ; Read input from keyboard
push eax ; Save Input by pushing to stack
call a20wait ; When controller ready for command
mov al,0xD1 ; mov 0xD1 Command to al register
out 0x64,al ; Set command 0xd1 (write to output)
call a20wait ; When controller ready for command
pop eax ; Pop Input from Keyboard
or al,2 ; Mov 0xD3 to al register
out 0x60,al ; Set Command 0xD3
call a20wait ; When controller ready for command
mov al,0xAE ; Mov Command 0xAE To al register
out 0x64,al ; Write command 0xae (enable keyboard)
call a20wait ; When controller ready for command
sti ; Enable Interrupts after enabling A20 Line
ret
a20wait:
in al,0x64 ; input from 0x64 port, goes to al register
test al,2 ; compares al register with 2
jnz a20wait ; If it is zero loop again
ret
a20wait2:
in al,0x64 ; input from 0x64 port, goes to al register
test al,1 ; compares al register with 2
jz a20wait2 ; If it is zero loop again
ret
get_lower_memory:
clc ; Clears Carry Flag
int 0x12 ; BIOS Call Request Lower Memory Size in KB
jc .err ; If Carry Flag Has Been Set , the system its running on dosent support this
jmp .done ; If Sucessfull ax register contains contiguous low memory in KB
.err:
times 5 call print_new_line ; Prints New Line
mov si, carry_flag_err
call print_char_boot
jmp .repeat
.done:
ret
.repeat:
jmp .repeat
;**************************************************************;
;*******************'MAIN' BOOTLOADER FUNCTION ****************;
main_bootloader:
xor ax, ax
mov ss, ax
mov sp, 4096
mov ax, 07C0h ; Set data segment to where we're loaded
mov ds, ax
mov si, string ; si register usefull for lodsb command
call print_char_boot ; Call print_char_boot label below
call print_new_line ; Prints New Line
mov si, string2
call print_char_boot
times 2 call print_new_line
; Enable A20 Line
call enable_A20
call get_lower_memory ; Get Low Memory
mov si,ax
call print_char_boot
times 5 call print_new_line
call reboot ; Reboot
;call null_descriptor
jmp $ ; Infinite Loop
;Bootloader gets infinite loop
;Incase No Infinite Loop in Kernel
;****************************************************************;
;************************* BOOTLOADER REQUIREMENTS **************;
times 510 - ($ - $$) db 0 ; Has to be 512 bytes .. Repeats 510 byes to make it so
dw 0xAA55 ; BootLoader Sig. To Validate this is a bootloader
;
****************************************************************;
正如你在我的主页上看到的那样,我是 call get_lower_memory ; Get Low Memory
,以获得低内存。但我已经测试过打印 ax 寄存器,但屏幕上没有显示任何内容。而且我也不知道如何在系统中获得总的和可用的内存。帮助将不胜感激!
虽然你的 post 的 body 中提出的问题更多是关于打印寄存器的值而不是关于检测系统可用的内存,但我会很忠诚针对标题问题,并举例说明如何检测系统内存映射。
作为奖励,提供了将 32 位无符号整数显示为十六进制数字的函数,以及支持占位符的非常原始的 print。
检测内存不是一件容易的事,它需要对安装的硬件有全面的了解1,没有它就无法完成(参见 OSDev 上的 Detecting memory)。
作为一个简单的例子,想想一个 aliased memory,如果没有任何涉及和缓慢的方法,软件无法检测到它。
承认与 BIOS 的合作是强制性的,我们可以看到 16 位实模式引导加载程序可以使用哪些服务。
上面提到的 OSDev 关于 detecting memory 的页面已经有一个专用于标题目的的服务列表,请参考。
我们将专注于 Int 15/AX=E820h 服务。
它的用途是 return 内存范围列表及其描述。
每次调用 return 下一个描述符,使用 ebx
来跟踪进度。寄存器 ebx
应被视为不透明值。
尽管 Ralf's Brown Interrupt List 中有描述,但描述符可以是 24 字节长,因此最好使用该长度并最终检查 returned in ecx
中的值] 区分 20/24 字节的描述符。
一旦我们有了描述符列表,它们就可以被分配内存的例程使用2.
拖东西不值钱:
描述符未排序。一些有问题的 BIOS 可能 return 重叠 区域(在这种情况下做出最保守的选择)。
即使对描述符进行排序,也可能存在差距。不报告没有内存映射的范围,这是标准孔的情况(范围从 0a0000h 到 0fffffh)。
虽然报告了 BIOS 明确保留的区域(例如从 0f0000h 到 0fffffh 的阴影区域)。
在下面的示例中,描述符与非保留内存总量一起打印在屏幕上3。
顺便说一句,你可以使用itoa16
函数在EAX
中打印一个32位值,假设你改变了字符在屏幕上的打印方式。
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 特别是北桥的部分,现在是iMC,专用于处理DRAM。可以通过 SPD using either a SMBus or I2C 控制器检索有关已安装模块(主要是 DIMM 和移动变体)的信息。
BIOS 然后考虑启用的内存映射设备和总线拓扑(连同其路由和桥接信息)并通过 SMBios specification.
2 因为它无论如何都会使用某种范围描述符,所以最终会执行格式转换。
3 此计数包括新类型 5(坏内存)范围。