;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Demo of INT 15h AX=E820h ; Chris Giese http://my.execpc.com/~geezer ; Release date: June 5, 2011 ; This code is public domain (no copyright). ; You can do whatever you want with it. ; ; This is a DOS .COM program. Assemble with NASM: ; nasm -f bin -o int15.com int15.asm ; ; If you want to use this code in a bootsector (no DOS), you must remove ; the code that uses these DOS functions: ; INT 21h AH=4Ch: program exit (return to DOS) ; INT 21h AH=02h: write one character to stdout (could use INT 10h AH=0Eh) ; ; INT 15h AX=E820h memory types: ; Type 1: Usable (normal) RAM ; Type 2: Reserved - unusable (could be ROM or reserved RAM such as EBDA) ; Type 3: ACPI reclaimable memory - usable after ACPI tables have been read ; Type 4: ACPI NVS memory ; Type 5: Area containing bad memory ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; BITS 16 ; 16-bit (real-mode) code ORG 100h ; .COM file ; check for 32-bit CPU pushf pushf pop bx ; old FLAGS -> BX mov ax,bx xor ah,70h ; try changing b14 (NT)... push ax ; ... or b13:b12 (IOPL) popf pushf pop ax ; new FLAGS -> AX popf xor bh,ah ; 32-bit CPU if we changed NT... and bh,70h ; ...or IOPL jne got_32bit_cpu mov si,_cpu_msg call puts jmp exit ; try INT 15h AX=E820h got_32bit_cpu: mov eax,0000E820h mov ebx,0 mov ecx,20 mov edx,534D4150h ; 'SMAP' mov di,_int15_e820_buf int 15h jnc int15_e820_supported mov si,_int15_msg call puts jmp exit ; it works! int15_e820_supported: mov si,_header_msg call puts again: ; For a bootloader, you could just store the memory ranges somewhere ; accessible by the kernel. The code below (between push ebx and pop ebx) ; displays the ranges. push ebx mov bl,16 ; display numbers in hex mov cx,16 ; display numbers in 16 columns mov eax,[_int15_e820_buf + 0] mov edx,[_int15_e820_buf + 4] call jwrnum ; start address of range mov al,' ' call putchar mov eax,[_int15_e820_buf + 8] mov edx,[_int15_e820_buf + 12] call jwrnum ; length of range mov al,' ' call putchar mov al,[_int15_e820_buf + 8] add eax,[_int15_e820_buf + 0] adc edx,[_int15_e820_buf + 4] sub eax,byte 1 sbb edx,byte 0 call jwrnum ; end address of range mov al,' ' call putchar mov eax,[_int15_e820_buf + 16] xor edx,edx mov cx,8 ; display numbers in 8 columns call jwrnum ; type of range mov si,_crlf_msg call puts pop ebx or ebx,ebx je exit mov eax,0000E820h mov ecx,20 mov edx,534D4150h ; 'SMAP' int 15h jnc again exit: mov ax,4C00h int 21h ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: putchar ; action: writes one character to stdout ; in: AL=character ; out: (nothing) ; modifies: (nothing) ; minimum CPU: 8088 ; notes: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; putchar: push dx push ax mov dl,al mov ah,2 int 21h pop ax pop dx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: puts ; action: writes 0-terminated string to stdout ; in: DS:SI -> string ; out: (nothing) ; modifies: (nothing) ; minimum CPU: 8088 ; notes: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; puts: push si push dx push ax cld jmp short puts_2 puts_1: call putchar puts_2: lodsb or al,al jne puts_1 pop ax pop dx pop si ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: lltoa ; action: converts 64-bit unsigned value to string ; in: 64-bit unsigned value in EDX:EAX, radix in EBX, ; DS:SI -> zero byte at END of 64-char buffer ; out: (nothing) ; modifies: SI ; minimum CPU: '386 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; lltoa: push edx push ecx push eax .1: ; two-step 64:32 divide (EDX:EAX / EBX) to avoid overflow ; See extended precision division in Randall Hyde's "Art of Assembly" mov ecx,eax mov eax,edx xor edx,edx div ebx xchg ecx,eax div ebx xchg ecx,edx ; remainder in CL if radix <256 -- convert from binary to hex ASCII add cl,'0' cmp cl,'9' jbe .2 add cl,('A'-('9'+1)) .2: dec si mov [si],cl ; loop until quotient == 0 push eax or eax,edx pop eax jne .1 pop eax pop ecx pop edx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: wrnum ; action: displays 64-bit value ; in: 64-bit unsigned value in EDX:EAX, radix in BL ; out: (nothing) ; modifies: (nothing) ; minimum CPU: '386 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; wrnum: push esi push ebx movzx ebx,bl mov si,_num_buf call lltoa call puts pop ebx pop esi ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: jwrnum ; action: displays 64-bit value right-justified ; in: 64-bit unsigned value in EDX:EAX, radix in BL, ; field width in CX ; out: (nothing) ; modifies: (nothing) ; minimum CPU: '386 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; jwrnum: push esi push ebx movzx ebx,bl mov si,_num_buf call lltoa ; pad with this many spaces on the left: ; cx - (_num_buf - si) = cx + si - _num_buf ; to make the displayed number right-justified push cx push ax add cx,si sub cx,_num_buf inc cx mov al,' ' jmp short .2 .1: call putchar .2: loop .1 pop ax pop cx call puts pop ebx pop esi ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; data ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; _int15_e820_buf: ; 20 bytes dq 0 ; 8-byte range address dq 0 ; 8-byte range length dd 0 ; 4-byte range type ; largest value (64 bits) displayed in lowest radix (binary) ; is 64 characters long -- thus, the length of this buffer times 64 db 0 _num_buf: db 0 _cpu_msg: db "Sorry, 32-bit CPU required (386+)" _crlf_msg: db 13, 10, 0 _int15_msg: db "The BIOS in this PC does not support INT 15h AX=E820h", 13, 10, 0 _header_msg: db "Memory ranges from INT 15h AX=E820h:", 13, 10 db "Start Length End Type", 13, 10 db "---------------- ---------------- ---------------- --------", 13, 10, 0