我会问如何获取引导加载程序中的总RAM大小和可用RAM大小。截至目前,我知道如何获得更低的内存。但由于某种原因,我无法将其打印到屏幕上,因为它保存在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 '\0'
;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
看到,以获得低记忆。但我测试了印刷轴寄存器,屏幕上没有显示任何内容。我也不知道如何在系统中获得总和可用的内存。帮助会得到极大的赞赏!
答案 0 :(得分:6)
虽然帖子正文中提到的问题更多的是关于打印寄存器的值而不是检测系统可用的内存,但我只是忠于标题问题,并提供一个示例如何检测系统内存映射。
作为奖励,提供了一个显示32位无符号整数作为十六进制数字的函数,以及支持占位符的非常原始的 print 。
检测内存并非易事,它需要完全了解已安装的硬件 1 ,如果没有它,则无法完成(请参阅OSDev上的Detecting memory)。 作为一个简单的例子,考虑一个aliased memory,如果没有任何相关和缓慢的方法,软件就无法检测到它。
我们已经承认必须与BIOS合作,我们可以看到16位实模式引导加载程序可以使用哪些服务。
上面提到的关于detecting memory的OSDev页面已经有一个专门用于标题目的的服务列表,可以参考。
我们将专注于Int 15/AX=E820h服务
它的用途是返回一个记忆范围列表及其描述
每次调用都返回下一个描述符,使用ebx
来跟踪进度。寄存器ebx
应视为不透明值
尽管 Ralf的布朗中断列表中有描述,描述符可以是24个字节长,因此最好使用该长度并最终检查ecx
中返回的值以告知20/24字节描述符开。
一旦我们有了描述符列表,它们就可以被指定用于分配内存 2 的例程使用。
值得一提的是:
描述符不有序。一些错误的BIOS可能会返回重叠区域(在这种情况下做出最保守的选择)。
即使订购了描述符,也可能存在间隙。没有报告没有内存映射的范围,这是标准孔的情况(范围从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或SMBus控制器通过I2C检索已安装模块(主要是DIMM和移动设备)的信息。 然后BIOS考虑启用的内存映射设备和总线拓扑(连同其路由和桥接信息),并通过SMBios specification公开所有这些。
2 因为它无论如何都会使用某种范围描述符,所以最终会执行格式转换。
3 此计数包括新类型5(内存不良)范围。