; Demonstration of how to use INT 15h AX=E820h ; Chris Giese ; http://SisAndHappy.com/ChrisGiese ; This code is public domain (no copyright). ; You can do whatever you want with it. ; 30 Sep. 2015: initial release ; Assemble this file to a DOS .COM file using NASM: ; nasm -f bin -o i15e820.com i15e820.asm ; The .COM file can be run from DOS or from a bootsector that ; can run .COM files; such as Alexei Frounze's BootProg. BITS 16 ; 16-bit code... CPU 386 ; ...but 32-bit CPU required ORG 0x100 ; TINY memory model (.COM file) push cs ; DS = CS pop ds ; (TINY memory model; should already be done) mov ax,[es:0] ; test for DOS PSP to see if we booted from DOS... cmp ax,0x20CD ; ...or from a bootloader jne no_dos inc byte [_dos] no_dos: push ds ; ES = DS; should already be done pop es ; 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 ah,bh and ah,70h ; 32-bit CPU if we changed NT or IOPL mov si,_cpu_error_msg je error mov si,_hdr_msg ; display header call puts mov si,_int15_error_msg ; display this message if INT 15h fails mov di,_e820_buf mov edx,0x534D4150 ; "SMAP" mov ecx,e820_buf_len xor ebx,ebx ; "continuation value"; initially =0 mov eax,0x0000E820 int 0x15 jc error ; CY=1 on first call to INT 15h AX=E820h... jmp short loop_bottom ; ...is an error loop_top: push bx mov bx,16 ; display values in hex mov cx,8 ; minimum field width 8 characters mov ax,[es:di + 0] ; base address of memory range mov dx,[es:di + 2] call jwrnum mov al,' ' call putc mov ax,[es:di + 8] ; size of memory range mov dx,[es:di + 10] call jwrnum mov al,' ' call putc mov ax,[es:di + 0] ; highest address of memory range mov dx,[es:di + 2] add ax,[es:di + 8] adc dx,[es:di + 10] sub ax,byte 1 sbb dx,byte 0 call jwrnum mov al,' ' call putc mov ax,[es:di + 16] ; type of memory range mov dx,[es:di + 18] call jwrnum mov si,_crlf_msg call puts pop bx ; continuation value = 0 again means we're done or ebx,ebx je done ; "In addition the SMAP signature is restored each call, although not ; required by the specification in order to handle some known BIOS bugs." mov edx,0x534D4150 ; "SMAP" mov ecx,e820_buf_len mov eax,0x0000E820 int 0x15 ; "the BIOS is permitted to return a nonzero continuation value in EBX ; and indicate that the end of the list has already been reached by ; returning with CF set on the next iteration" jc done loop_bottom: cmp eax,0x534D4150 ; EAX on return other than... je loop_top ; ..."SMAP" is an error error: call puts done: xor ax,ax or al,[_dos] jne dos_exit mov si,_reboot_msg call puts mov ah,0 ; BIOS call: wait for a key to be pressed int 0x16 int 0x19 ; re-start the boot process dos_exit: mov ax,0x4C00 ; exit to DOS with errorlevel 0 int 0x21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: putc ; action: writes ASCII character to stdout (DOS) or screen (BIOS) ; in: character in AL; [_dos] should be set ; out: (nothing) ; modifies: (nothing) ; minimum CPU: 8088 ; notes: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; putc: push dx push bx push ax xor ah,ah or ah,[_dos] jne putchar putch: mov ah,0x0E ; use BIOS for text output (INT 10h AH=0Eh) mov bx,0x0007 ; BH=page=0, BL=foreground color (7=white) int 0x10 jmp short putc_done putchar: mov ah,2 ; use DOS (INT 21h AH=02h) mov dl,al int 0x21 putc_done: pop ax pop bx pop dx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: puts ; action: writes ASCII string to stdout or screen ; in: 0-terminated string at DS:SI ; out: (nothing) ; modifies: (nothing) ; minimum CPU: 8088 ; notes: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; puts: push si push ax jmp short puts_2 puts_1: call putc puts_2: mov al,[si] inc si or al,al jne puts_1 pop ax pop si ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: jputs ; action: displays string right-justified ; in minimum field width ; in: 0-terminated string at SI, field width in CX ; out: (nothing) ; modifies: (nothing) ; minimum CPU: 8088 ; notes: ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; jputs: push es push di push cx push bx push ax mov bx,cx ; determine length of string mov di,ds mov es,di mov di,si xor ax,ax mov cx,-1 repne scasb neg cx sub cx,byte 2 ; if string length >= field width, display it normally cmp cx,bx jnb jputs_2 ; display (field width - string length) spaces neg cx add cx,bx mov al,' ' jputs_1: call putc loop jputs_1 jputs_2: call puts pop ax pop bx pop cx pop di pop es ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: ltoa ; action: converts 32-bit unsigned value to string ; in: 32-bit unsigned value in DX:AX, radix in BX, ; SI -> LAST byte (end) of buffer ; out: (nothing) ; modifies: SI decremented; points to FIRST byte of string ; minimum CPU: 8088 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ltoa: push dx push cx push ax ; store zero byte to terminate string mov byte [si],0 dec si ; extended precision divide from section 9.3.5 ; of Randall Hyde's "Art of Assembly" ; start: DX=dividend MSW, AX=dividend LSW, BX=divisor ltoa_1: push ax mov ax,dx xor dx,dx ; before div: DX=0, AX=dividend MSW, BX=divisor ; after div: AX=quotient MSW, DX=intermediate remainder div bx mov cx,ax pop ax ; before div: DX=intermediate remainder, AX=dividend LSW, BX=divisor ; after div: AX=quotient LSW, DX=remainder div bx ; end: DX=quotient MSW, AX=quotient LSW, CX=remainder xchg dx,cx add cl,'0' cmp cl,'9' jbe ltoa_2 add cl,('A'-('9'+1)) ltoa_2: dec si mov [si],cl mov cx,ax or cx,dx jne ltoa_1 pop ax pop cx pop dx ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: wrnum ; action: displays 32-bit value ; in: 32-bit unsigned value in DX:AX, radix in BX ; out: (nothing) ; modifies: (nothing) ; minimum CPU: 8088 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; wrnum: push si mov si,_num_buf call ltoa call puts pop si ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; name: jwrnum ; action: writes 32-bit value right-justified, ; in minimum field width ; in: 32-bit unsigned value in DX:AX, radix in BX, ; field width in CX ; out: (nothing) ; modifies: (nothing) ; minimum CPU: 8088 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; jwrnum: push si mov si,_num_buf call ltoa call jputs pop si ret ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; data ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; times 32 db 0 _num_buf: db 0 _dos: db 0 _e820_buf: times 20 db 0 e820_buf_len equ $ - _e820_buf _cpu_error_msg: db "32-bit CPU required (386+)", 13, 10, 0 _int15_error_msg: db "Error from INT 15h AX=E820h", 13, 10, 0 _reboot_msg: db "Press a key to reboot", 13, 10, 0 _hdr_msg: db "Memory ranges from INT 15h AX=E820h:", 13, 10 db "Start End", 13, 10 db "address Length address Type", 13, 10 db "-------- -------- -------- --------" _crlf_msg: db 13, 10, 0