Assembly 如何获得RAM大小,引导加载程序

Assembly 如何获得RAM大小,引导加载程序,assembly,x86,ram,bootloader,osdev,Assembly,X86,Ram,Bootloader,Osdev,我想问一下如何在引导加载程序中获得总RAM大小和可用RAM大小。到目前为止,我知道如何获得较低的内存。但由于某些原因,我无法将其打印到屏幕上,因为它保存在ax寄存器中。这就是我到目前为止所做的: [BITS 16] ; BootLoader always starts 16 BIT Moded jmp main_bootloader ; Jump to Main Bootloader ;************** INITALIZED VARIABLES ********

我想问一下如何在引导加载程序中获得总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
;

****************************************************************;

正如你在我的主页上看到的,我是
调用get\u lower\u memory;获取低内存
,以获取低内存。但我已经测试了打印ax寄存器,屏幕上什么也没显示。我也不知道如何在系统中获得总的可用ram。非常感谢您的帮助

虽然你文章正文中提到的问题更多的是关于打印寄存器的值,而不是关于检测系统可用的内存,但我将忠实于标题问题,并给出一个如何检测系统内存映射的示例

另外,还提供了一个将32位无符号整数显示为十六进制数字的函数,以及一个支持占位符的非常原始的打印


检测内存不是一项容易的任务,它需要完全了解已安装的硬件1,没有它就无法完成(请参阅OSDev上的内容)。
举一个简单的例子,如果没有任何复杂而缓慢的方法,软件无法检测到

在确认必须与BIOS合作后,我们可以看到16位实模式引导加载程序可以使用哪些服务。
上面提到的OSDev关于的页面已经有一个专门用于标题目的的服务列表,请参考

我们将专注于服务。
它的用途是返回内存范围列表及其描述。
每次调用都返回下一个描述符,使用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千字节(EBDA)-96千字节(阴影区域)-288千字节(标准孔)。
    ACPI表被认为是可用的,因为它们是可回收的


    1特别是北桥的一部分,即现在的iMC,专门用于处理DRAM。有关已安装模块(主要是DIMM和移动变型)的信息可通过使用或控制器检索。
    BIOS然后考虑启用的内存映射设备和总线拓扑(以及它的路由和桥接信息),并通过.

    公开所有这些。 2由于它无论如何都会使用某种范围描述符,因此最终会执行格式转换


    3此计数包括新的类型5(坏内存)范围。

    虽然您文章正文中提到的问题更多是关于打印寄存器的值,而不是关于检测系统可用内存,但我将忠实于标题问题,并给出一个如何检测系统内存映射的示例

    另外,还提供了一个将32位无符号整数显示为十六进制数字的函数,以及一个支持占位符的非常原始的打印


    检测内存不是一项容易的任务,它需要完全了解已安装的硬件1,没有它就无法完成(请参阅OSDev上的内容)。
    举一个简单的例子,如果没有任何复杂而缓慢的方法,软件无法检测到

    在确认必须与BIOS合作后,我们可以看到16位实模式引导加载程序可以使用哪些服务。
    上面提到的OSDev关于的页面已经有一个专门用于标题目的的服务列表,请参考

    我们将专注于服务。
    它的用途是返回内存范围列表及其描述。
    每次调用都返回下一个描述符,使用ebx跟踪进程。寄存器ebx应视为不透明值。
    尽管Ralf的布朗中断列表中有描述,描述符的长度可以是24字节,因此最好使用该长度,并最终检查
    ecx
    中返回的值,以区分20/24字节的描述符


    一旦我们有了描述符列表,它们就可以被分配内存2的例程使用。
    两件事一文不值:

  • 描述符没有排序。一些有缺陷的BIOS可能返回重叠的区域(在这种情况下,做出最保守的选择)

  • 即使对描述符进行了排序,也可能存在间隙。不报告没有内存映射的范围,这是标准孔(ran)的情况