News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Displaying Total memory

Started by zak100, January 30, 2010, 01:18:12 PM

Previous topic - Next topic

zak100

Hi,
I have found a code for calculating total memory from a web tutorial. I want to display this memory which is a 32-bit number. I also found an algorithm for displaying a 32-bit number in decimal which is written at the bottom of this code. I am facing difficulty in implementing this algorithm. Can somebody plz help me in this regard?




.MODEL  TINY


        .CODE

;----------------------------------------------------------------------------------

LoadOfs EQU     0               ;must match the value in the bootloader source file


;----------------------------------------------------------------------------------

;---------------------- initialize ES segment register

        ORG     0

Start:  push    cs
        pop     ds
       
;-----------clear screen
mov ax, 3
int 10h

   
;---------------------- writing a message on screen at startup, character by character- we can't use int 21h
overdata:
       
        xor di, di
        mov ax, 0B800h
        mov es, ax
        mov si, offset msg0+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        jz TimerMesg
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgloop
       
;---------------------- done - halt
TimerMesg:

        xor di, di
        mov ax, 0B820h
        mov es, ax
        mov si, offset msgA+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgAloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        jz RTime
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgAloop

         call FindTotalMem
Halt0: hlt
jmp     Halt0

;---------------------- data area in code segment


Msg0    db      "We be bootin234!",0
msgA db 'Total minutes elapsed since Kernel start is',0

;---------------Procedure FindTotalMem
FindTotalMem:
push ax
push bx
push cx
push dx
;---------------clear all registers
xor ax, ax
xor bx, bx
xor cx, cx
xor dx, dx
;---------------Interrupt
mov ax,0e801h
int 15h
;--------------checking errors
jc Error
cmp ah,86h;------unsupported function
je Error
cmp ah, 80h;-----invalid command
je Error
jcxz use_ax;-----if cx is zero then ax and bx contain mem size
mov ax, cx;------cx is not zero so cx and dx contain mem size
mov bx, dx
use_ax:
   pop dx
   pop cx
   pop bx
   pop ax
return

Error:
   mov ax, -1
   mov bx, 0
   pop dx
   pop cx
   pop bx
   pop ax
return


;---------Display TotalMem
;Algorithm for displaying 32 bit value

;power:=7
;digit_printed_yet:=false
;repeat
;count:=0
;while DX,CX >= 0 do
;begin
;DX,CX:=DX,CX-10^power
;count:=count+1
;end
;DX,CX:=DX,CX+10^power
;count=count-1
;if count <> 0 then
;begin
;digit_printed_yet:= true
;print(count)
;end
;if count= 0 and (digit_printed_yet or(power = 1) then
;begin
;print (count)
;digit_printed_yet:=true
;end
;power:=power-1
;until power=0


Zulfi.





dedndave

hiya Zulfi

mov ax,0e801h
int 15h
;--------------checking errors
jc Error
cmp ah,86h;------unsupported function
je Error
cmp ah, 80h;-----invalid command
je Error
jcxz use_ax;-----if cx is zero then ax and bx contain mem size
mov ax, cx;------cx is not zero so cx and dx contain mem size
mov bx, dx
use_ax:
   pop dx
   pop cx
   pop bx
   pop ax
return

you get the value in ax and bx, but then you pop the registers and destroy the contents   :P
also - the word "return" - you might want to use "ret" instead
(i figure you know have the conversion code commented out)

zak100

Thanks. You are right. I should remove the "pop ax" and "pop bx" statements.  What about the conversion code. I have written the algorithm but I dont know how to implement it in Assembly. Kindly help me in this regard.

Zulfi.

dedndave

from Ralf....
;AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
;BX = extended memory above 16M, in 64K blocks
;CX = configured memory 1M to 16M, in K
;DX = configured memory above 16M, in 64K blocks

;on some systems, the BIOS returns AX=BX=0000h; in this case, use CX and DX instead of AX and BX

so - if we convert the 64 K blocks into 1 Kb blocks, then add those together and add 400h for the base 1 Mb that isn't counted,
you will wind up with a value no larger than 23 bits that represents the total memory in Kb
it is easier to work with that size than with 32 bits because a 32 bit value can have 10 digits
the 23 bit value can only have 7 digits max (4210624 Kb ~ 4 Gb) - it won't report memory over that
the question is - are you happy with displaying it in Kb instead of bytes ?

zak100

I got this from Mike's tutorial, 2008.

Quote
Notice what this routine returns. In order for us to get the amount of KB in the system, we need to do some math. EBX contains the number of 64KB blocks of memory. If we multiply this by 64, we effectivly convert the value in EBX into the amount of KB above 16MB. Afterwords, simply add this number to the number returned in EAX to get the amount of KB above 1MB. Knowing there is 1024 KB in a one megabyte, add 1024 to this number and we now have the total amount of KB in the system!
This means KB is fine. I am not using EBX / EAX b/c  I am not in protected mode. Thus I have value in AX/BX. Even if its 23 bits its difficult for me b/c I have to handle two registers not one. I can handle single register. This the prob.

Zulfi.

dedndave

not a prob, Zulfi - right after my nap, i will write a quick routine   :bg

EDIT - well - i will try to write it quickly - not sure how fast it will be - lol

EDIT - well - ok - right after my nap, i will slowly write a slow routine   :lol

dedndave

#6
ok Zulfi - let me know how this works   :U
the address of the first byte to display will be in SI
you need the normal "display zero-terminated string" routine to show it

;---------------Procedure FindTotalMem
FindTotalMem:

;save registers

        push    ax
        push    bx
        push    cx
        push    dx

;Interrupt

        mov     ax,0E801h
        int     15h
        mov     si,offset AscBuf+LoadOfs+6   ;EDIT - changed LoadSeg to LoadOfs
        jnc     calc_mem

;error handler (we just show 0 memory)

        inc     si
        mov byte ptr [si],30h
        jmp     zero_exit

;AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
;BX = extended memory above 16M, in 64K blocks
;CX = configured memory 1M to 16M, in K
;DX = configured memory above 16M, in 64K blocks

;on some systems, the BIOS returns AX=BX=0000h; in this case, use CX and DX instead of AX and BX

calc_mem:
        or      ax,ax
        jnz     use_ax

        or      bx,bx
        jnz     use_ax

        mov     ax,cx
        mov     bx,dx

use_ax: mov     cx,10000
        xor     dx,dx
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        add     ax,1024           ;add the base 1 Mb
        add     ax,bx
        adc     dx,0              ;DX:AX = 4210624 (403FC0h) max
        div     cx
        mov     cx,3030h
        mov     bl,100
        xchg    ax,dx

;DX = 0 to 421
;AX = 0 to 9999

        mov     bl,100
        div     bl
        mov     bh,al
        mov     al,ah
        call    B2Asc
        mov     al,bh
        call    B2Asc
        xchg    ax,dx
        div     bl
        mov     bh,al
        mov     al,ah
        call    B2Asc
        mov     al,bh
        call    B2Asc

;AL will always be 30h (ASCII '0')

;suppress leading zeros

        inc     si
        inc     si
        mov     cx,6

zero_loop:
        inc     si
        cmp     al,[si]
        jnz     zero_exit

        dec     cx
        jnz     zero_loop

;Returns: SI = address of first string byte to display

zero_exit:
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
;---------------

;---------------Procedure convert byte (0-99) to ASCII word and store
B2Asc:  aam
        or      ax,cx
        xchg    al,ah
        mov     [si],ax
        dec     si
        dec     si
        ret
;---------------

AscBuf  db      8 dup (0),' Kb Total Memory',0

zak100

Thanks. I would try it and let you know about its outcome.

Zulfi.

zak100

Hi,
I have removed my timer code and trying the memory size code alone. Its printing kernel's openig mesg and timer mesg but after that its not printing the AscBuf mesg. indly help me in this regard.

Quote
the address of the first byte to display will be in SI
you need the normal "display zero-terminated string" routine to show it



I cant understand this code so I cant write the code for displaying. Again I need your help in this regard.




.MODEL  TINY


        .CODE

;----------------------------------------------------------------------------------

LoadOfs EQU     0               ;must match the value in the bootloader source file
LoadSeg EQU     1000h

;----------------------------------------------------------------------------------

;---------------------- initialize ES segment register

        ORG     0

Start:  push    cs
        pop     ds
       
;-----------clear screen
mov ax, 3
int 10h

   
;---------------------- writing a message on screen at startup, character by character- we can't use int 21h
overdata:
       
        xor di, di
        mov ax, 0B800h
        mov es, ax
        mov si, offset msg0+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        jz TimerMesg
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgloop
       
;---------------------- done - halt
TimerMesg:

        xor di, di
        mov ax, 0B820h
        mov es, ax
        mov si, offset msgA+LoadOfs
       mov ah, 41h; attribute byte
       cld;
msgAloop:
        lodsb; loads al with a byte of data pted by ds:si
        or al, al
        ;jz RTime
        jz mem
        stosw; transfers the contents of al to mem location ptd by es:di
        jmp msgAloop
        mov cx, 1000
;DISPLAY_TIMER:
;-------------Find Total Memory
mem:        call FindTotalMem
Halt0: hlt
jmp     Halt0

;---------------------- data area in code segment


Msg0    db      "We be bootin234!",0
msgA db 'Total minutes elapsed since Kernel start is',0
clkcounter db 0
secs db 0
mins db 0
hrs  db 0
cnt  db 0; Its value represents the digits of Timer
s    db 0; selector for secs minutes and hrs used in displayCnt
arr  db 10 dup(0)
AscBuf  db      8 dup (0),' Kb Total Memory',0


;------------------------

;---------------Procedure FindTotalMem
;---------------Procedure FindTotalMem
FindTotalMem:

;save registers

        push    ax
        push    bx
        push    cx
        push    dx

;Interrupt

        mov     ax,0E801h
        int     15h
        mov     si,offset AscBuf+LoadSeg+6;**********
        jnc     calc_mem

;error handler (we just show 0 memory)

        inc     si
        mov byte ptr [si],30h
        jmp     zero_exit

;AX = extended memory between 1M and 16M, in K (max 3C00h = 15MB)
;BX = extended memory above 16M, in 64K blocks
;CX = configured memory 1M to 16M, in K
;DX = configured memory above 16M, in 64K blocks

;on some systems, the BIOS returns AX=BX=0000h; in this case, use CX and DX instead of AX and BX

calc_mem:
        or      ax,ax
        jnz     use_ax

        or      bx,bx
        jnz     use_ax

        mov     ax,cx
        mov     bx,dx

use_ax: mov     cx,10000
        xor     dx,dx
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        shl     bx,1
        rcl     dx,1
        add     ax,1024           ;add the base 1 Mb
        add     ax,bx
        adc     dx,0              ;DX:AX = 4210624 (403FC0h) max
        div     cx
        mov     cx,3030h
        mov     bl,100
        xchg    ax,dx

;DX = 0 to 421
;AX = 0 to 9999

        mov     bl,100
        div     bl
        mov     bh,al
        mov     al,ah
        call    B2Asc
        mov     al,bh
        call    B2Asc
        xchg    ax,dx
        div     bl
        mov     bh,al
        mov     al,ah
        call    B2Asc
        mov     al,bh
        call    B2Asc

;AL will always be 30h (ASCII '0')

;suppress leading zeros

        inc     si
        inc     si
        mov     cx,6

zero_loop:
        inc     si
        cmp     al,[si]
        jnz     zero_exit

        dec     cx
        jnz     zero_loop

;Returns: SI = address of first string byte to display

zero_exit:
        pop     dx
        pop     cx
        pop     bx
        pop     ax
        ret
;---------------

;---------------Procedure convert byte (0-99) to ASCII word and store
B2Asc:  aam
        or      ax,cx
        xchg    al,ah
        mov     [si],ax
        dec     si
        dec     si
        ret
;---------------







;----------------------------------------------------------------------------------

        END     Start



Zulfi.

dedndave

here is a routine to display zero-terminated strings

;---------------Procedure display a zero-terminated string

;DS:SI = string address
;DI = display position
;AH = display attribute
;direction flag = cleared

Dsply:  push    es
        mov     es,0B800h
        jmp short Dsply_entry

Dsply_loop:
        stosw

Dsply_entry:
        lodsb
        or      al,al
        jnz     Dsply_loop

        pop     es
        ret
;---------------

and then, here is the code to put the 2 routines together

        cld
        call    FindTotalMem    ;calculate total memory (sets the SI register)
        mov     ah,7            ;display attribute
        mov     di,0            ;display position
        call    Dsply           ;display total memory

zak100

Hi,
I am getting 514944KB. When I checked the system profiles through control panel, it says 504MB and if I multiply it by 1024 its equal to 516096KB. Kindly tell me about this disparity?

Do you know about the conversion algorithm used in this program?

Zulfi.

dedndave

well - you probably ACTUALLY have 512 Mb
the operating system, whether it is BIOS, Windows, or a combination of the two, reserves some memory and does not report it
my system profile says i have .99 Gb - i know there is 1 Gb in there   :P
because you have no windows installed, you are seeing that which is reserved by BIOS apparently
i am sure there is some way to calculate the exact amount - i just don't know what it is - lol
the fact that it printed something near the right result tells us the routine is probably working as it should
let me do a little research and see what i can learn

zak100

I think you are right. I have not heard of anybody selling a computer with a RAM of 502 MB installed in it. 512 MB is commonly known. I have not checked the computer specifications because its three years now since I bought this computer. If I get time I would let you know.

Zulfi.

dedndave

ok - i knew this, but had forgotten - lol
when your system boots up, BIOS initializes
after it is done, but before it hands control over to the disk operating system, it allows other devices to initialize
they do this by inserting ROM type memory at high addresses
BIOS searches for these ROMs and, if it finds the signature, allows them to initialize
good examples of this are the video card and the hard disk controller
when these ROMs initialize, they may reserve some of the system physical memory
to do that, they hook the BIOS service routines that report memory and subtract the amount of RAM they wish to reserve
there may be no perfect way to calculate the amount
i think, if i wanted to display a reasonable value on modern machines, i would do this...
calculate the amount as we have done using int 15h
round the value up to the next nearest 16 Mb boundry
the reason this would work is, any modern computer has memory cards that are always 16 Mb or larger (typically larger)
to modify our current code, try this.....

we are going to remove these lines and replace them

        add     ax,1024           ;add the base 1 Mb
        add     ax,bx
        adc     dx,0              ;DX:AX = 4210624 (403FC0h) max

with these lines...

        add     ax,43FFh          ;add the base 1 Mb + 16 Mb - 1 Kb
        add     ax,bx
        adc     dx,0              ;DX:AX = 4227007 (407FBFh) max
        and     ax,0C000h         ;truncate unwanted bits 4210688 (404000h) max

ok Zulfi - i am done editing - try it out   :bg

MichaelW

#14
Zulfi,

The MEMMAP application from the attachment here:

http://www.masm32.com/board/index.php?topic=7679.msg56497#msg56497

Displays the memory map returned by Interrupt 15h Function E820h. Try running it on your test system from a DOS boot diskette. On my test system, with 256MB installed, it reports:

BASE=00000000H LENGTH=0009F800 (653312)    TYPE=AVAILABLE
BASE=0009F800H LENGTH=00000800 (2048)      TYPE=RESERVED
BASE=000E0000H LENGTH=00020000 (131072)    TYPE=RESERVED
BASE=00100000H LENGTH=0FF00000 (267386880) TYPE=AVAILABLE
-----------------------------------------------------------
TOTAL LENGTH          0FFC0000 (268173312)

Extended Memory size returned by Interrupt 15h Function 88h = 67104768 bytes

Extended Memory size from CMOS RAM = 67107840 bytes


The extended memory sizes returned by Interrupt 15h Function 88h, and as read from the CMOS RAM, are in KB, but since they are 16-bit values they are essentially meaningless for a system with 64 or more MB.

The 2048-byte block at 0009F800h is the Extended BIOS Data Area (see Interrupt 15h Function C1h).

The 131072-byte block at 000E0000h is the E and F blocks combined. F block is where the BIOS is normally mapped in, and E block sometimes contains a copy of the BIOS (probably only older systems).

256MB = 268435456 bytes

268435456 – 268173312 = 262144

This leaves 262144 bytes unaccounted for, and this is exactly the total size of the A, B, C, and D blocks. As you probably know, the A and B blocks contain the VGA display buffers (along with some related stuff). C block normally contains the VGA BIOS (typically 32KB), and other expansion ROMs (a SCSI BIOS, for example) can be mapped into D block, or into the unused area of C block.


Here is the map from another system, ~two years newer than the first one, and with 512MB:

BASE=00000000H LENGTH=0009FC00 (654336)    TYPE=AVAILABLE
BASE=0009FC00H LENGTH=00000400 (1024)      TYPE=RESERVED
BASE=000F0000H LENGTH=00010000 (65536)     TYPE=RESERVED
BASE=00100000H LENGTH=1FEFC000 (535805952) TYPE=AVAILABLE
BASE=1FFFC000H LENGTH=00003000 (12288)     TYPE=ACPI_RECLAIM
BASE=1FFFF000H LENGTH=00001000 (4096)      TYPE=ACPI_NVS
-----------------------------------------------------------
TOTAL LENGTH          1FFB0000 (536543232)

Extended Memory size returned by Interrupt 15h Function 88h = 67043328 bytes

Extended Memory size from CMOS RAM = 67043328 bytes


And examining this I realized that the precise memory size for both systems can be determined by adding the length of the last block to the base of the block:

1FFFF000h + 00001000h = 20000000h = 512 * 1024 * 1024
00100000h + 0FF00000h = 10000000h = 256 *1024 * 1024
eschew obfuscation