The time has come in the development of the initialization process of my OS to get details about memory. I could have done this a lot simpler as both systems I use, have 4 gigs, but on every system that I'll use my software, I want detailed reporting. This code is somewhat fashioned after the example detecting memory at wiki.osdev.org, minus some functionality and a lot less bloat.My development environment is an HP Thinkcenter, dual boot Ubuntu 16.04 LT and DOS 6.22 with the ability to boot from USB. The code has been tested with BOCH's, DOS .com file and sector 0 of USB. The only part that hasn't been tested is if bits 31 - 2E aren't zero. Then again, I doubt seriously there will every be a single entry with a minimum length of 2 gig.NOTE: DosBox yields blank screen under LinuxI've included the source for NASM and if anyone has some real hardware it could be tested on with a screenshot of actual results, that would be much appreciated.The prolog and epilog for each is functionally the same, but with a few differences peculiar to each paradigm.BootI use BOCH's and DEBUG without symbols, so these page alignments make it easier to remember where the buffer is and for memory dumps.7C00  FA                cli7C01  BDF0FF            mov     bp, -16  Reserve space for BP7C04  89EC              mov     sp, bp7C06  83EC30            sub     sp, 487C09  FB                stiNot going to assume BIOS passes all segment registers pointing to the same place.7C0A  8CD0              mov     ax, ss7C0C  8ED8              mov     ds, ax7C0E  8EC0              mov     es, axThis boot sector was assembled without an ORG directive, therefore everything is an offset from zero. CS must be set appropriately.7C10  EA1500C007        jmp     0x7c0:0x15Clears the video page determined by DISP_PAGE, Sets the initial output to center of second line. This will be bumped by 1 later, so values actually start on line 3.7C15  E8CF00            call    ClrScr7C18  C746009001        mov     [bp], 0x1907C1D  89E7              mov     di, sp         DI = Pntr to E820 data7C1F  E82E00            call    GetMapIn this case as DISP_PAGE is defined as 0, 7C22 -> 7C2A and 7C31 -> 7C38 are redundant, but in the event value is changed in line 10 of source, this needs to be here.7C22  B80005            mov     ax, SAP << 167C25  50                push    ax7C26  B000              mov     al, DISP_PAGE7C28  CD10              int     VIDEOWait for reponse from operator and convert to uppercase7C2A  B80000            mov     ax, 0x07C2D  CD16              int     KEYBOARD7C2F  245F              and     al, 0x5f7C31  67870424          xchg    ax,[esp]7C35  CD10              int     VIDEO'Y' or 'y' will reboot, anything else hangs7C37  58                pop     ax7C38  3C59              cmp     al, 'Y'7C3A  7502              jnz     7c3e7C3C  CD19              int     REBOOT7C3E  F4                hlt7C3F  EBFE              jmp     $To calculate breakpoints, add this value to desired address in application body.7C50  GetMap     253  NULLS to padd object code to boot signature    7DFE  55 AADOS100  BCF0FF            mov      sp, -16103  C81E0000          enter    30, 0107  E8BD00            call     ClrScr10A  C746009001        mov      [bp],0x19010F  89E7              mov      di, sp          DI = FFC0111  E81C00            call     GetMapUnlike the original version of DEBUG that came bundled with DOS 6.22, I use a version that is 32 bit capable, but if one displays and debugs on same page, it just becomes a big mess, so output is directed to page 1 @ B9???. To see the results, I switch to that page, wait for any kind of response and then revert to page 0 again.114  B80005            mov      ax, SAP << 16   AH = 5 Set active page117  50                push     ax118  B001              mov      al, DISP_PAGE11A  CD10              int      VIDEO11C  B80000            mov      ax, GETKEY11F  CD16              int      KEYBOARD121  58                pop      ax122  CD10              int      VIDEO124  C9                leave125  CD20              int      TERMINATETo calculate breakpoints, add this value to desired address in application body.130  GetMapThis code is common to both BOOT & DOS. To determine absolute address add value denoted by GetMap in previous examples.Initialize registers required by INT 15 0  6631DB            xor   ebx, ebx 3  6689D9            mov   ecx, ebx 6  B120              mov   cl, 32 8  66BA50414D53      mov   edx, 534d4150   'SMAP' E  B820E8            mov   ax, 0xe820Loop until BL = 0, CF = 1 or EAX <> EDX when BL = 111  50                push  ax12  6652              push  edx14  C6451401          mov   [di+20], 1      ACPI 3.018  CD15              int   SYS_SERVICE1A  7211              jc    0x2d1C  80FB01            cmp   bl, 11F  720C              jc    0x2D21  7707              ja    0x2A            Not first iteration23  66673B0424        cmp   eax,[esp]   EAX = 'SMAP'28  7503              jnz   0x2D2A  E80600            call  0x332D  665A              pop   edx2F  58                pop   ax30  73DF              jnc   0x11        Read another entry32  C3                retBeing in real mode (16 bit) were limited to 32 bit data, so these 64 bit values passed by BIOS need to be digested in 32 bit chunks.33  51                push  cx          Preserve continuation34  89FE              mov   si, di36  8B4602            mov   ax,[bp+2]       Video segment39  06                push  es3A  8EC0              mov   es, ax3C  57                push  di  ; At the beginning of each conversion, bump to center of next line3D  8B7E00            mov   di,[bp]40  81C7A000          add   di,16044  897E00            mov   [bp], diBIOS colors correspond directly to map types which are displayed on center column47  8A6410            mov   ah,[si+16]4A  B0FE              mov   al,0xfe4C  AB                stosw4D  83EF06            sub   di, 6       Back to left of center50  57                push  diThe loop between 51 <-> 77 needs to be executed twice. Once for base addressand then again for length of entry. This next instruction facilitates that without the need for excessive jumps or calls.NOTE: This is the only instruction that relies on ORG 100H as it is absolutely unlike everything else that is relative.51  689D01            push 0x19DOne thing I've never liked is leading zeros unless this is a specific reason for it, so this part only displays what needs to be.54  66AD              lodsd                 Bits 31 - 056  E81F00            call  0x7859  66AD              lodsd         Bits 63 - 325B  6609C0            or    eax, eaxAt this point, if EAX = 0 we don't need to padd with CL zero's5E  7501              jnz   0x6160  C3                retCL = positions left to fill with '0' to complete all 8. Should have maybe tested for zero, but REP STOSW won't do anything if so61  6650              push  eax63  B83007            mov   ax,0x730    Attr in AH = White66  FD                std           Need to do in reverse67  F3AB              rep   stosw69  6658              pop   eax             Restore map valueBecause we don't care about CL and DF is already set, we can simplydrop into procedure that actually starts @ 0x786B  EB0E              jmp   0x7BThis is where lenth of map segment is displayed6D  5F                pop   di6E  83C728            add   di, 4071  E8E0FF            call  0x54Restore and read next map entry74  5F                pop   di75  07                pop   es76  59                pop   cx77  C3                retThis routine converts binary value in EAX to hexidecimal notation (upper case)78  B108              mov   cl, 8       Max digits7A  FD                std           Set DF to decreemntSave value of EAX and then convert AL to ASCII equivalent7B  6650              push  eax7D  240F              and   al, 0xf7F  3C0A              cmp   al, 0xa81  7202              jc    0x8583  0407              add   al, 0x7     10-15 become A-F85  0430              add   al, 0x3087  B407              mov   ah, 0x7     Display in White89  AB                stosw8A  6658              pop   eax8C  FEC9              dec   cl              Bump digits displayed8E  66C1E804          shr   eax, 492  75E7              jnz   0x7b        While EAX94  FC                cld           Reset DF to inc95  F8                clc   So primary loop won't bail.96  C3                retClearing the screen could have been done by setting video mode, but for some reason on my test machine, this takes probably 2.5 seconds, so I implemented this instead.97  06                push  es98  B800B8            mov   ax, 0xb8009B  80C401            add   ah, DISP_PAGE9E  8EC0              mov   es, axA0  894602            mov   [bp+2], axA3  57                push  diA4  31FF              xor   di, diA6  B82007            mov   ax, 0x720 White on Black with spacesA9  51                push  cxAA  B9D007            mov   cx, 0x7d0 80 x 25 wordsAD  F3AB              rep   stoswAF  59                pop   cxB0  5F                pop   diB1  07                pop   esB2  C3                retAssemble as follows forBootnasm E820.asm -D_BOOT_ -oBoot.binDOSnasm E820.asm -oE820.comHaven't quite figured out why sometimes source tabulates properly and sometimes it doesn't, but anyway I sure if you copy this into an editor of your choice, it will look a lot betterE820.asm       SAP  equ  5      ; Function to set active page    GETKEY  equ  0      ; Read keyboard input     VIDEO  equ  16         ; BIOS Video services      KEYBOARD  equ  22      BOOT  equ  25   SYS_SERVICE  equ  21%ifdef _BOOT_    DISP_PAGE   equ  0      ; Choose page 0 for Bochs    cli         ; Disable interrupts    mov bp, -16     ; Scatch area for BP    mov sp, bp    sub sp, 48      ; Space for map entries (page aligned)    sti    ; BIOS should probably of set these, but just in case    mov ax, ss    mov ds, ax    mov es, ax    ; NOTE: Because original is at zero so quirky BIOS's won't break my code.    jmp 0x7c0:Start%else  DISP_PAGE equ  1      ; Choose page 1 for DOS & DEBUB    org 256     ; For DOS com files    mov sp, -16     ; 16 Byte scratch area for BP    enter   30, 0%endif Start: call    ClrScr      ; Clear selected display page    mov word [bp], 400  ; Ouput begins right justifed Line=3 Col=40     mov di, sp      ; Point to E820 entry buffer    call    GetMap    ; This is for the benefit of BOCHS or maybe even QEMU and DOS so result    ; can be viewed before re-booting or alternatively returning to DEBUG    mov ax, 0x500   ; Select page 0    push    ax    mov al, DISP_PAGE   ; Display results page    int VIDEO    mov ax, GETKEY    int KEYBOARD    ; Wait for reponse from operator%ifdef _BOOT_    and al, 0x5f    ;     xchg    ax, [esp]%else    pop ax  %endif    int VIDEO       ; Set to default video page 0%ifdef _BOOT_    pop ax    cmp al, 'Y'    jnz $ + 4    int BOOT        ; Let's do it again    hlt    jmp $       ; Spin  %else    leave           ; Kill procedure frame    int 32      ; Terminate DOS application%endif    ; This is here so the object code for BOOT version versus DOS version    ; is at the same offset from the beginning of executable.    align   16GetMap: xor ebx, ebx    ; Continuation starts @ 0    mov ecx, ebx    mov cl, 32      ; Anything greater than 20    mov edx, 'PAMS'    mov ax, 0xe820    ; Inifinte loop unless    ;     A: RBX = 0    ;     B: EAX <> EDX    ;     C: Service call returns CF = 1  .L0:  push    ax    push    edx    mov byte [di+20], 1 ; This has something to do with ACPI 3    int SYS_SERVICE    jc  .done       ; Call return CY=1    cmp bl, 1    jb  .done       ; BL = 0    ja  .done - 3   ; Not first time    cmp eax, [esp]  ; Compare values    jnz .done    call    DMap        ; Convert and display two 64 bit values .done: pop edx    pop ax    jnc .L0     ; This might break if EAX <> EDX    ret  DMap: push    cx      ; Save continue value for INT 15H    mov si, di      ; Pointer to 4 64 bit values    mov ax, [bp+2]  ; Get video segment    push    es    mov es, ax    push    di      ; Preserve address to map data    mov di, [bp]    ; Next position to write to in video    add di, 160     ; Bump to next line    mov [bp], di    ; and save    mov ah, [si+16] ; Get entry type    mov al, 254     ; Small block    stosw           ; Set entry type indicator    sub di, 6       ; Bump back to left side    push    di    push    .C0     ; Re-entrant   ; First invocation display base address of entry, next time length.   .J0: lodsd           ; Convert and display low order 32 bits    call    WVal    lodsd    or  eax, eax    jnz $ + 3    ret    push    eax    mov ax, 0x730    std    rep stosw       ; Write CL leading zeros    pop eax    jmp WVal.J0     ; Drop into because DF is already set   .C0: pop di    add di, 40      ; Right most position of     call    .J0     ; Display length on entry    pop di    pop es    pop cx    ret    ; Converts and display 32-bit value in EAX in reverse order  WVal: mov cl, 8    std   .J0: push    eax    and al, 15    cmp al, 10    jb  $ + 4    add al, 7    add al, '0'    mov ah, 7    stosw    pop eax    dec cl    shr eax, 4    jnz .J0    ; In the event leading zero's need to be displayed, the number is returned    ; in CL    cld         ; Re-set direction flag    clc         ; So primary loop won't break    retClrScr: push es    mov ax, 0xb800    add ah, DISP_PAGE    mov es, ax    mov [bp+2], ax    push    di    xor di, di    mov ax, 0x720   ; White on Black / spaces    push    cx    mov cx, 80*25    rep stosw    pop cx    pop di    pop es    ret%ifdef _BOOT_    times   510 - ($-$$) db 0    dw  0xAA55%endif 
E820 display using X86 legacy boot sector or DOS 6.22 com file
assembly
NOTE: DosBox yields blank screen under LinuxSame here for DOSBox under Windows and also for DOS 6.20 by itself! DOSBox (and for that matter the NTVDM also) doesn't support the 0000E820h BIOS function at all. Read on to find out why a true DOS 6.20 fails ...Observations and optimizations; This is here so the object code for BOOT version versus DOS version; is at the same offset from the beginning of executable.align   16To calculate breakpoints, add this value to desired address in application body.    7C50  GetMapTo calculate breakpoints, add this value to desired address in application body.    130  GetMapThis align directive can not do what you state. Up to this point the BOOT version has 65 bytes that get padded till the address is at 80, while the DOS version has 38 bytes that get padded till the address is 48.You apparantly knew this looking at the last 2 digits in the numbers in the other comments:This boot sector was assembled without an ORG directive, therefore everything is an offset from zero. CS must be set appropriately.7C10  EA1500C007        jmp     0x7c0:0x15Only the push .C0 instruction imposes that you set the CS segment register this way. Without that push the far jump could have been omitted.You've taken good precautions with not trusting how BIOS sets the segment registers.You should take the same precaution with regard to the direction flag DF. cldSAP          equ   5      ; Function to set active pageGETKEY       equ   0      ; Read keyboard inputVIDEO        equ  16      ; BIOS Video servicesKEYBOARD     equ  22BOOT         equ  25SYS_SERVICE  equ  21When I first read the above equates, I felt a bit lost. I've never come across any api documentation that did not express all of these numbers in hexadecimal and always using precisely 2 digits. Although what you wrote is numerically correct, it would be advisable to go with the flow and write:SAP          equ  05h      ; Function to set active pageGETKEY       equ  00h      ; Read keyboard inputVIDEO        equ  10h      ; BIOS Video servicesKEYBOARD     equ  16hBOOT         equ  19hSYS_SERVICE  equ  15hI bet you more people will recognize these numbers without hesitation as being correct.cli         ; Disable interruptsmov bp, -16     ; Scatch area for BPmov sp, bpsub sp, 48      ; Space for map entries (page aligned)stiUsing cli/sti around this modification of SP is not necessary. If you would also modify the SS segment register then it would make sense as it would guarantee a consistent stackpointer.Instead of first copying BP to SP and then subtracting 48 from SP, you can write this in a single instruction using lea.mov bp, -16     ; Scratch area for BPlea sp, [bp-48] ; Space for map entries (page aligned)This has just become 40% shorter.The comment mentions page aligned. Could it be you meant paragraph aligned ?mov sp, -16     ; 16 Byte scratch area for BPenter   30, 0Having a lexical level of 0, this enter 30, 0 instruction is the equivalent ofpush    bpmov     bp, spsub     sp, 30The problem with all of this is that your program is using the word at [bp] for 2 different things at a time. You yourself will store here the offset in video memory AND ALSO you allow BIOS to use this word by asking for a SMAP-record of max. 32 bytes.Furthermore there's also this difference between the BOOT version and the DOS version. Why does the BOOT version get 48 bytes for map entries as opposed to DOS who gets only 30 bytes? Could it be you forgot the hex-prefix on 0x30 which is equal to 48?I think you can use the self same code for both versions.mov bp, -16     ; Scratch area for BPlea sp, [bp-48] ; Space for map entries (paragraph aligned)mov word [bp], 400  ; Ouput begins right justifed Line=3 Col=40 Correct instruction, misleading comment. Video offset 400 points to the 41st column on the 3rd row.So (40,2) if expressed 0-based (BIOS,...) and (41,3) if expressed 1-based (BASIC,...).mov ax, GETKEYint KEYBOARD    ; Wait for reponse from operatorThis is a tricky error! The function number should have gone in the AH register but because GETKEY is equal to 0 the code worked just fine. You did waste 1 extra byte though.and al, 0x5f    ; The intention here is to UCase the character inputted by the user. Why then do you additionally drop bit 7 (8th bit) ? In doing so you allow the user to not only use 'Y' and 'y' to reboot but also '' and ''. Just pointing out this inconsistency with the comment:'Y' or 'y' will reboot, anything else hangs.hltjmp $       ; Spin  If you're going to use hlt then best go back to it, if for some reason execution should resume.hltjmp $-1       ; Spin  leave           ; Kill procedure frameint 32      ; Terminate DOS applicationThis leave instruction (mov sp, bp pop bp) is wasted right before exiting to DOS. Apart from the CS segment register the OS doesn't expect anything else from the application to be able to terminate it. Moreover the value that leave pops back into the BP register is corrupted because of the reason I gave discussing enter 30, 0.mov  ax, [bp+2]  ; Get video segmentpush esmov  es, axpush dimov  di, [bp]    ; Next position to write to in videoadd  di, 160     ; Bump to next linemov  [bp], di    ; and saveNo need to use an intermediate step clobbering AX.push    esmov     es, [bp+2]  ; Get video segmentShorter if adding directly to memory.push    diadd     word [bp], 160     ; Bump to next line in videomov     di, [bp]Saving even more through combining both. Notice we have an offset in the low word and a segment in the high word? Ideal situation to use the les instruction.push    espush    diadd     word [bp], 160     ; Bump to next line in videoles     di, [bp]Total bytes saved 7 (from 17 bytes down to 10 bytes)push    dipush    .C0     ; Re-entrant; First invocation display base address of entry, next time length..J0: lodsd           ; Convert and display low order 32 bitscall    WVallodsdor  eax, eaxjnz $ + 3retpush    eaxmov ax, 0x730stdrep stosw       ; Write CL leading zerospop eaxjmp WVal.J0     ; Drop into because DF is already set.C0: pop diadd di, 40      ; Right most position of call    .J0     ; Display length on entryAlthough this methode of using an embedded subroutine (I wouldn't say re-entrant) works OK (I verified it), I find it is too clever for my taste. A solution that converts a 64-bit number held in 2 32-bit registers is much simpler, not to mention shorter. It's easy to write using the shrd instruction.clc         ; So primary loop won't breakretYou've accidently placed this clc before the wrong ret. call    .J0     ; Display length on entrypop dipop espop cx            <-- This is where the CLC belongs!retNow the funny thing is that an explicit clc isn't really needed if addresses and lengths stay below 4GB because the or eax, eax instruction will be the last CF modifying instruction on the execution path (CF=0).The troubles with GetMapThere's a lot to be said here. So much that I had to re-write the code to get it working.The function number for this QuerySystemAddressMap is the 32-bit number 0000E820h that goes in EAX. I gave your optimizing for code size AX approach a chance on several of my computers and they all returned an error. (CF=1 and AH=86h)The 32-bit continuation value that goes in EBX is a number who's actual value (when non-zero) is completely at the discretion of BIOS. It could be a very small monotonously incrementing counter like you are expecting but this is by no means an obligation for BIOS.That same continuation value (when zero) does not represent an error condition of some kind. Your code however treats it that way. It's only BIOS saying: Don't come back for more records!Because of this misconception your routine presently does not display the last record!The 4-character signature in EDX solely exists to guard against false positives on older BIOSes. An unaware BIOS might return CF=0 when an application calls this non-existing 0000E820h function. Therefore BIOS must acknowledge the signature on every invocation. Trying to verify this acknowledgement only once just complicates matters.By keeping the constants in EAX, ECX, and EDX outside of the loop, you've given yourself a hard time because you had to preserve these values and dealing withjnc .L0 ; This might break if EAX <> EDXwasn't so obvious anymore, that is to say, you didn't solve it.New code:GetMap:    xor     ebx, ebx    ; Continuation starts at 0.a: xor     ecx, ecx    mov     cl, 24    mov     edx, 'PAMS'    mov     eax, 0000E820h    int     15h    jc      .done    cmp     eax, edx    ; EDX='PAMS'    jne     .done    call    DMap        ; Convert and display two 64 bit values    test    ebx, ebx    jnz     .a          ; More records to retrieve.done:    retI applied all of the above to your program and the size of the executable came down from 230 bytes (minimal corrections) to 182 bytes (optimizations applied).I ran it on an Acer Aspire computer with a Phoenix BIOS and 2GB RAM.Colors legend:blue    free memorygreen   reserved memorycyan    ACPI reclaimable memoryred     ACPI non-volatile memorymagenta bad memory