BBC World News 12/04/2010 - Demand for laser-cured century eggs far outstrips supply.
Imminent upset at mech keggers predicted to threaten national security.
Strange problem with memory functions...
Re: Strange problem with memory functions...
Hello,
On first call, EBX should be 0 for first memory map entry. The call sets EBX to another value. Call the interrupt again with the new value to get the 2nd entry. Continue until return EBX is 0 again.
All of the information needed can be found here. ES:DI is the pointer to the entry data structure; ECX is the sizeof(entryDataStructure). EDX must be 0x534D4150. EBX must be 0. The BIOS INT writes to ES:DI - so you define where to store the information at.Still... I have no idea how it works or even where this map it creates is located.
On first call, EBX should be 0 for first memory map entry. The call sets EBX to another value. Call the interrupt again with the new value to get the 2nd entry. Continue until return EBX is 0 again.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
Re: Strange problem with memory functions...
neon wrote:Hello,
All of the information needed can be found here. ES:DI is the pointer to the entry data structure; ECX is the sizeof(entryDataStructure). EDX must be 0x534D4150. EBX must be 0. The BIOS INT writes to ES:DI - so you define where to store the information at.
On first call, EBX should be 0 for first memory map entry. The call sets EBX to another value. Call the interrupt again with the new value to get the 2nd entry. Continue until return EBX is 0 again.
Code: Select all
proberam:
xor ebx, ebx ; ebx must be 0 to start
xor bp, bp ; keep an entry count in bp
mov edx, 0x0534D4150 ; Place "SMAP" into edx
mov eax, 0xe820
mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry
mov ecx, 24 ; ask for 24 bytes
int 0x15
jc short .failed ; carry set on first call means "unsupported function"
mov edx, 0x0534D4150 ; Some BIOSes apparently trash this register?
cmp eax, edx ; on success, eax must have been reset to "SMAP"
jne short .failed
test ebx, ebx ; ebx = 0 implies list is only 1 entry long (worthless)
je short .failed
jmp short .jmpin
.e820lp:
mov eax, 0xe820 ; eax, ecx get trashed on every int 0x15 call
mov [es:di + 20], dword 1 ; force a valid ACPI 3.X entry
mov ecx, 24 ; ask for 24 bytes again
int 0x15
jc short .e820f ; carry set means "end of list already reached"
mov edx, 0x0534D4150 ; repair potentially trashed register
.jmpin:
jcxz .skipent ; skip any 0 length entries
cmp cl, 20 ; got a 24 byte ACPI 3.X response?
jbe short .notext
test byte [es:di + 20], 1 ; if so: is the "ignore this data" bit clear?
je short .skipent
.notext:
mov ecx, [es:di + 8] ; get lower dword of memory region length
or ecx, [es:di + 12] ; "or" it with upper dword to test for zero
jz .skipent ; if length qword is 0, skip entry
inc bp ; got a good entry: ++count, move to next storage spot
add di, 24
.skipent:
test ebx, ebx ; if ebx resets to 0, list is complete
jne short .e820lp
.e820f:
mov [mmap_ent], bp ; store the entry count
mov eax, [es:di]
mov [mmap], eax
mov si, [mmap]
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
mmap_ent times 20 db 0
mmap times 20 db 0
Does "di" or "es" have to be set to an offset such as 01h?
I must be doing something wrong as it prints:
29156
:-/
Re: Strange problem with memory functions...
This might explain the confusion. ES:DI are used to refer to a segment and an offset within that segment in real mode. Please read up on real mode addressing.What I don't understand is how es:di works.
Note that this interrupt returns a data structure. It does not return a value so am uncertain where you have gotten "29156" from.
OS Development Series | Wiki | os | ncc
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
char c[2]={"\x90\xC3"};int main(){void(*f)()=(void(__cdecl*)(void))(void*)&c;f();}
Re: Strange problem with memory functions...
Thanks for the link. That cleared up the whole ES:DI thing. Or at least what it does.neon wrote:This might explain the confusion. ES:DI are used to refer to a segment and an offset within that segment in real mode. Please read up on real mode addressing.What I don't understand is how es:di works.
Note that this interrupt returns a data structure. It does not return a value so am uncertain where you have gotten "29156" from.
Ok.... So if (DI) has to be set to "01h" as the offset then where does the segment value (ES) come from?
Generally speaking this is kinda why I like writing my own code rather than try to understand somebody elses.
This is what is throwing me off a little bit:
How do I point ES:DI at the destination buffer (Just create a variable and copy it into that variable?) and why would I do that first? I thought I would do that last.For the first call to the function, point ES:DI at the destination buffer for the list. Clear EBX. Set EDX to the magic number 0x534D4150. Set EAX to 0xE820 (note that the upper word of EAX should be set to 0). Set ECX to 24. Do an INT 0x15.
-------------------------------------------------------------------------------------
If I add to the very beginning of the function:
Code: Select all
mov eax, [es:di]
mov [mmap], eax
Code: Select all
mov [mmap_ent], bp ; store the entry count
mov di, 01h
mov si, [es:di]
"1984"
Both my printing functions use the SI register to indicate what to print.
- Combuster
- Member
- Posts: 9301
- Joined: Wed Oct 18, 2006 3:45 am
- Libera.chat IRC: [com]buster
- Location: On the balcony, where I can actually keep 1½m distance
- Contact:
Re: Strange problem with memory functions...
what's the physical location of your buffer?How do I point ES:DI at the destination buffer (Just create a variable and copy it into that variable?) and why would I do that first? I thought I would do that last.
how does address generation in real mode work?
therefore, what is a/the correct value for ES and DI?
There is no technical reason to load es:di first, as long as it's done before the int. Just see it as doing the most difficult thing first. (There might be a few other reasons but they are pretty far-fetched and only really relevant for the hardcore asm developers)
Re: Strange problem with memory functions...
Hi,
You have to ask the BIOS to determine where this area of RAM ends (and stop your testing at the address that the BIOS says the area of RAM ends).
Then there's things like your stack. You should reserve a few KiB for your stack, but this RAM is "in use" even if it contains zeros. The same applies to everything else. For example, imagine some executable code is in memory that does "add ax,0x0000". There's 2 bytes that are zeros in that instruction - are they "free RAM"? No.
To do it properly (and get sane results) you have to find a completely different way to determine which areas are being used.
For an example; you could split RAM up into small chunks (e.g. 1 KiB per chunk, so that if there's 630 KiB of RAM you've got 630 chunks); and then have an array where each entry in the array says if the corresponding chunk is "free RAM" or "used RAM" or "not usable RAM". Initially you'd fill this array with "not usable RAM" values, then ask the BIOS which areas of RAM are usable and change the corresponding entries in the array to "free RAM"; then change entries in the array that correspond to things you are already using (e.g. the BIOS data area starting at 0x00000000, and the boot loader area starting at 0x00007C00) to "used RAM". After the array is initialised; you'd be able to use the array to allocate RAM (e.g. find entries in the array that are marked as "free RAM" and change them to "used RAM") and use the array to free RAM (e.g. find entries in the array that are marked as "free RAM" and change them to "used RAM"). You would also be able to check the array and count how many chunks are "free RAM" and how many are "used RAM", and use that to determine how much RAM is in use and how much RAM exists (sum of "free RAM" and "used RAM").
Of course this isn't the only way to do things - for example, MS-DOS uses a linked list to track free and unused RAM.
For a real mode OS, the BIOS "int 0x12" function is enough to determine which areas are usable RAM (and which areas aren't), because you don't need to care about any memory you can't access (e.g. memory above 0x00100000) . For a protected mode OS (or a long mode/64-bit OS) you can access a lot more RAM, and therefore need to use something more complex (like the BIOS "int 0x15, eax = 0xE820" function). However, for a protected mode OS or a long mode OS, you'd need to gather information from the BIOS then switch to protected mode or long mode, and you'd need to implement your memory manager in protected mode or long mode because you won't be able to (easily) use anything designed for real mode after the OS has left real mode.
Cheers,
Brendan
That table could be a little misleading, in that the size of the EBDA is not fixed. For example the EBDA could be 1 KiB (and the RAM from 0x00080000 to 0x0009FBFF may be usable); but the EBDA could also be 16 KiB (and only the RAM from 0x00080000 to 0x0009BFFF might be usable), or the EBDA could be any other size that is a multiple of 1 KiB (e.g. 1 KiB, 2 KiB, 3 KiB, ... ). Ancient software may have assumed that RAM up to 0x00080000 is usable and therefore (because backward compatibility is excessive on "PC compatible" computers) it's extremely unlikely that the EBDA will ever be more than 64 KiB, which means the RAM up to 0x0007FFFF is (almost) guaranteed to be free for your use.David2010 wrote:Also you said that I shouldn't test all the way to 0x0009FC00 but where should I stop testing at because "http://wiki.osdev.org/Memory_Map_%28x86%29#.22Low.22_memory_.28.3C_1_MiB.29" says I should stop testing for RAM at 0x0009FBFF.
You have to ask the BIOS to determine where this area of RAM ends (and stop your testing at the address that the BIOS says the area of RAM ends).
In general, the method you're using to calculate "used memory" is complete bullshit. For an example, imagine you turn the computer on and the BIOS uses 100 KiB of "usable RAM" in this area during initialisation, then the BIOS stops using this area, then starts your boot loader. Your boot loader counts "non-zero bytes" and decides that most of that 100 KiB that the BIOS used is still in use, even though it's not. For some computers RAM is not filled with zeros during reset (and remembers what was there before the reset), so in this case you could determine that 600 KiB of RAM is being used when it isn't.David2010 wrote:Being as the OS itself is only a little over 1 KB large I find these results to be WAY off. :-/
Then there's things like your stack. You should reserve a few KiB for your stack, but this RAM is "in use" even if it contains zeros. The same applies to everything else. For example, imagine some executable code is in memory that does "add ax,0x0000". There's 2 bytes that are zeros in that instruction - are they "free RAM"? No.
To do it properly (and get sane results) you have to find a completely different way to determine which areas are being used.
For an example; you could split RAM up into small chunks (e.g. 1 KiB per chunk, so that if there's 630 KiB of RAM you've got 630 chunks); and then have an array where each entry in the array says if the corresponding chunk is "free RAM" or "used RAM" or "not usable RAM". Initially you'd fill this array with "not usable RAM" values, then ask the BIOS which areas of RAM are usable and change the corresponding entries in the array to "free RAM"; then change entries in the array that correspond to things you are already using (e.g. the BIOS data area starting at 0x00000000, and the boot loader area starting at 0x00007C00) to "used RAM". After the array is initialised; you'd be able to use the array to allocate RAM (e.g. find entries in the array that are marked as "free RAM" and change them to "used RAM") and use the array to free RAM (e.g. find entries in the array that are marked as "free RAM" and change them to "used RAM"). You would also be able to check the array and count how many chunks are "free RAM" and how many are "used RAM", and use that to determine how much RAM is in use and how much RAM exists (sum of "free RAM" and "used RAM").
Of course this isn't the only way to do things - for example, MS-DOS uses a linked list to track free and unused RAM.
For a real mode OS, the BIOS "int 0x12" function is enough to determine which areas are usable RAM (and which areas aren't), because you don't need to care about any memory you can't access (e.g. memory above 0x00100000) . For a protected mode OS (or a long mode/64-bit OS) you can access a lot more RAM, and therefore need to use something more complex (like the BIOS "int 0x15, eax = 0xE820" function). However, for a protected mode OS or a long mode OS, you'd need to gather information from the BIOS then switch to protected mode or long mode, and you'd need to implement your memory manager in protected mode or long mode because you won't be able to (easily) use anything designed for real mode after the OS has left real mode.
Cheers,
Brendan
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.