Date: Mon, 7 Oct 1996 14:17:03 +0200 (IST) From: Eli Zaretskii To: "John M. Aldrich" Cc: Charles Sandmann , DJ Delorie , djgpp-workers AT delorie DOT com Subject: Re: Stub error messages (Was: Re: 'Cannot open') In-Reply-To: <9610041405.AA12939@clio.rice.edu> Message-Id: Mime-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII On Fri, 4 Oct 1996, Charles Sandmann wrote: > numbers might not be very meaningful. Your best bet is to examine > Int 15/XMS/VCPI memory before trying to run a PM program, which is why > my meminfo program was a TCC based image. Instead of falling back to Turbo C, why not read the CMOS? Surely, if there is *any* place in the PC which knows about the correct amount of memory, it will be there, right? The following small program reports the amount of installed physical RAM as known to the CMOS setup. The registers I access are all documented and thus should be supported by any AT and above. I tried this with 3 different machines (one of them a Pentium) with 3 different BIOSes, and it worked OK. I don't have access to MCA machines, and so don't know what will happen there, but at least in theory it should work. I also have no experience with CMOS compatibilities in the first 20h registers. DJ? The reported value sometimes excludes the memory put aside for shadowing, but that's usually something like 256K or 384K, so what the heck. Caveats: 1) The code reads and writes ports, so NT might not allow it. But I'm not sure that NT will at all let you use all of the available memory in the DOS box anyway. Charles? 2) The function which reads the CMOS disables NMI and re-enables it before exit. I would rather not change the NMI flag, but there is no way known to me that you can read that flag. Since reading CMOS involves writing to the same register which sets ot resets the NMI flag, I chose the least dangerous way of disabling and re-enabling it. Stll, if there are machines which run with NMI disabled (laptops?), this code will have a side-effect on them. If this is of any concern, you can always tell people to reboot after they run djverify. 3) My sources indicate that you should disable interrupts when reading the CMOS, for those cases where the interrupt handler accesses CMOS itself (like for reading the real-time clock maybe?). However, since the DPMI spec says that you cannot really disable interrupts under DPMI, only stop them from being delivered to *your program*, I didn't bother. I've run the program time after time and never saw a garbled value printed. But maybe a precaution of calling the function twice in a row and comparing the results is due. 4) The function that forces a delay between `inportb' and `outportb' is just a kludge. I wanted to prevent the optimizer from messing up with the integer division (which might yield zero) and so forced the compiler to go to floating point. But that is a bad idea for djverify, since it should run on machines without an FPU and without an emulator (if the installation is screwed up). So that code has to be changed. Other than that--enjoy. ------------------------ memsize.c ----------------------------------- #include static void usec_sleep (usec) { uclock_t start_time = uclock (); uclock_t end_time = start_time + usec * 0.000001 * UCLOCKS_PER_SEC; while (uclock () < end_time) ; } #include static unsigned char read_cmos (int reg) { unsigned char al = (reg & 0xff) | 0x80; /* disable NMI */ outportb (0x70, al); usec_sleep (2); /* delay for 2 microseconds */ al = inportb (0x71); usec_sleep (2); outportb (0x70, 0); /* enable NMI */ return al; } int installed_memory_size (void) { unsigned base_lo, base_hi, ext_lo, ext_hi; base_lo = read_cmos (0x15); base_hi = read_cmos (0x16); ext_lo = read_cmos (0x17); ext_hi = read_cmos (0x18); return ( (base_hi + ext_hi) << 8 ) + base_lo + ext_lo; } #ifdef TEST #include int main (void) { printf ("I think this machine has %dKB of main memory\n", installed_memory_size ()); return 0; } #endif