Search (using Google):  Web Karig

 

20 December 2003

Finding memory, part 1

One of the system loader's chores is to find out what memory there is for the system to use, and to pass this information on to the kernel.

The system loader has to do it because the services that provide this information are all real-mode BIOS functions. (The system loader runs in real mode; the system itself does not.)

There are actually four BIOS services for providing this information, but I'm going to talk about only one of them right now. This service is INT 15h, AX=E801h. I'll talk about this one because it is present on a good percentage of new computers, it returns more accurate information than other services previously available, and it works on my laptop. :)

Using this service is very simple:

		mov	ax, 0xE801
		int	0x15

The function returns the following:

  • AX = extended memory: number of kilobytes between the 1MB and 16MB marks; the maximum is therefore 0x3C00 (15,360).
  • BX = extended memory: number of 64-kilobyte blocks between the 16MB and 4GB marks.
  • CX = configured memory: same as AX.
  • DX = configured memory: same as BX.
  • Carry flag SET on error, otherwise CLEAR.

The distinction between extended and configured memory is apparently that "configured" memory is memory detected the old-fashioned way, through jumpers or switches on the motherboard, and "extended" memory is memory detected the modern way, through chipsets that can detect memory directly. My guess is that the two are usually the same, but if they are different, then the "extended" numbers should be the ones trusted, unless they are zero, in which case the "configured" numbers should be used.

So the code that follows the service call might look like this:

		; Carry flag set if service doesn't work.
		jc	.error

		; Check for extended numbers set to zero.
		test	ax, ax
		jnz	.next_step
		test	bx, bx
		jnz	.next_step

		; If extended numbers are zero,
		; move configured numbers into AX and BX.
		mov	ax, cx
		mov	bx, dx
	.next_step:

Code

I can test this BIOS service simply by storing the returned information into memory and then dumping the memory to the screen. Here's the code:

Call the service as above, but this time just store the information to memory (at 0000:1000 — 4KB above the bottom of memory). If the service returns an error, just store all zeroes there.

main:
		mov	ax, 0xE801
		int	0x15
		jnc	.1
		xor	ax, ax
		xor	bx, bx
		xor	cx, cx
		xor	dx, dx
	.1:	mov	[0x1000], ax
		mov	[0x1002], bx
		mov	[0x1004], cx
		mov	[0x1006], dx

Now dump the memory to the screen. (I care only about the first eight of the sixteen bytes dumped.)

		xor	ax, ax
		mov	fs, ax
		mov	bx, 0x1000
		call	dump_16

Results

My laptop displays the following:

0000:1000: 00 3C 7F 0E 00 3C 7F 0E 00 00 00 00 00 00 00 00

This means that I have 15,360 ("00 3C" or 0x3C00) kilobytes of extended memory between the 1MB and 16MB marks, and 3,711 ("7F 0E" or 0x0E7F) 64-kilobyte blocks between the 16MB and 4GB marks. So first, I have a full fifteen megs of "high memory" below the 16MB mark — I can use memory above and below the 16MB mark as a single contiguous chunk of memory. Second, 3,711 times 64KB is 237,504 kilobytes, which is 231.9375 megabytes. So the BIOS is telling me that I have 246.9375 megabytes of high memory on my laptop.

Other pages

Physical memory: layout, determining RAM size, etc.
Uruk.org: three INT 15h functions
Memory detection using int 15h, AX=E820h etc.

Check the index for other entries.