Hi,
Cemre wrote:
what is wrong with detecting memory size with the code above using direct probing? shouldn't the system report an 0xFFFFFFFF at a non-existent memory area?
Aside from incorrectly detected memory mapped devices (as already mentioned), there's no guarantee that non-existant areas will return 0xFFFFFFFF. Some motherboards may return 0x00000000, and some could return any value at all (perhaps depending on which song the local radio station was playing at the time). To avoid this, you should write a value and see if that value was stored correctly, but there are problems with this...
On some motherboards (typically 80486 and older, but nothing prevents the same problem on new computers) the bus may be left floating. Writing a value on this floating bus can result in the value being stored by the capacitance between bus lines, such that non-exists memory areas tend to behave as if there's RAM installed. Doing a dummy read or write to settle the bus will help prevent this.
Further, testing at 1 Mb increments isn't so good either - I've seen computers that map the RAM underneath the ROMs, etc at higher addresses, so that with 2 Mb of memory you get RAM from 0x00000000 to 0x000A0000 and from 0x00100000 to 0x00260000 (an extra 384 Kb). In this case you might detect that there's RAM at 0x00200000, and attempt to use non-existant memory up to 0x002FFFFF.
Also some motherboards have a memory hole from 15 Mb to 16 Mb (and lots have a BIOS option for it that someone may have enabled). Not sure what this hole was for, but if the hole is present your code will only detect 15 Mb and stop (when there's actually more).
In general use the BIOS. The BIOS "knows" all of the intricate details of the motherboard, chipset, etc.
Now for something really stupid - some older computers (namely 80486 and possibly early pentium) don't support any of the normal extended memory size BIOS functions! On these computers you must directly probe (or simply don't support them, it is a rare enough problem now).
Therefore, the method I recommend is:
Code: Select all
Try: int15 ax=0xe820, GET SYSTEM MEMORY MAP
If int15 ax=0xe820 worked return
int12, GET CONVENTIONAL MEMORY <1 Mb
Try: int15 ax=0xe801, GET MEMORY SIZE FOR >64M CONFIGURATIONS
if int15 ax=0xe801 worked return
Try: int15 ah=0x88, GET EXTENDED MEMORY SIZE (286+)
if int15 ah=0x88 worked return
Try: Manual Probing
Please note that for all of these BIOS functions there have been buggy BIOSs that behave badly. Check each BIOS function in "Ralf Brown's Interrupt List" to see what it should do
and what it might do. It is possible to write code that avoids the problems with these buggy BIOSs.
Now, if you must manually probe there are things you can do to minimize the problems.
First, test at the
end of each 4 Kb (not at the
start of each 1 Mb). This avoids any problems with strange physical memory mappings.
If your testing gets above 2 Gb then you've got problems - there will be no computers with this much memory that don't support "int15 ax=0xe820".
Do the scan in 2 seperate parts. One from 1 MB to 16 MB, and another from 16 Mb up to compensate for the memory hole possibility.
Then the actual test should be something like:
Code: Select all
mov eax,[gs:edi] ;Get original value
not dword [gs:edi] ;Invert memory
wbinvd ;Make sure memory is inverted
not eax ;Invert original
mov ebp,[gs:esi] ;Read from 0 to settle bus
cmp [gs:edi],eax ;Is it the same?
jne .doneManual ; no
not dword [gs:edi] ;Invert memory back to original state
I forgot to mention the CPU caches so far - it's possible to write a value to non-existant memory and read that same value back reliably, because it'll be stored in the CPU's cache/s. You have to either disable the caches (CD flag in CR0) or use "wbinvd" to flush them.
Another reason for using the BIOS is that manual detection (when done correctly) is
very slow - especially on modern computers with lots of RAM when the cache can't be used effectively...
Cheers,
Brendan