; use the INT 0x15, eax= 0xE820 BIOS function to get a memory map
; note: initially di is 0, be sure to set it to a value so that the BIOS code will not be overwritten. 
;       The consequence of overwriting the BIOS code will lead to problems like getting stuck in `int 0x15`
; inputs: es:di -> destination buffer for 24 byte entries
; outputs: bp = entry count, trashes all registers except esi
;use16

virtual at 500h   ;map is at linear 0500h
SMAP:
.size=0
.data=4
dd 0
rb 24*32
end virtual

.query:
        mov ax,SMAP/16;50h  ;map is at linear 0500h
        mov es,ax
        mov di,SMAP.data           ; Set di to 0x8004. Otherwise this code will get stuck in `int 0x15` after some entries are fetched
        xor ebx,ebx             ; ebx must be 0 to start
        xor bp,bp               ; keep an entry count in bp
        mov edx,'PAMS';0534d4150h      ; Place "SMAP" into edx
        mov eax,0e820h
        mov dword[es:di+20],1   ; force a valid ACPI 3.X entry
        mov ecx,24              ; ask for 24 bytes
        int 15h
        jc .failed              ; carry set on first call means "unsupported function"
        mov edx,'PAMS';0534d4150h      ; Some BIOSes apparently trash this register?
        cmp eax,edx             ; on success, eax must have been reset to "SMAP"
        jne .failed
        test ebx,ebx            ; ebx = 0 implies list is only 1 entry long (worthless)
        je .failed
        jmp @f
.loop:
        mov eax,0e820h          ; eax, ecx get trashed on every int 0x15 call
        mov dword[es:di+20],1   ; force a valid ACPI 3.X entry
        mov ecx,24              ; ask for 24 bytes again
        int 15h
        jc .end                 ; carry set means "end of list already reached"
        mov edx,'PAMS';0534d4150h      ; repair potentially trashed register
@@:
        jcxz .skip              ; skip any 0 length entries
        cmp cl,20               ; got a 24 byte ACPI 3.X response?
        jbe @f
        test byte[es:di+20],1   ; if so: is the "ignore this data" bit clear?
        je .skip
@@:
        mov ecx,[es:di+8]       ; get lower uint32_t of memory region length
        or ecx,[es:di+12]       ; "or" it with upper uint32_t to test for zero
        jz .skip                ; if length uint64_t is 0, skip entry
        inc bp                  ; got a good entry: ++count, move to next storage spot
        add di,24
.skip:
        test ebx,ebx            ; if ebx resets to 0, list is complete
        jne .loop
.end:
        mov [es:0],bp           ; store the entry count
        clc                     ; there is "jc" on end of list to this point, so the carry must be cleared
        ret
.failed:
        stc                     ; "function unsupported" error exit
        ret
