memtest86+: Initial commit of 5.01 codebase

This is the initial commit of memtest86+

The tarball was downloaded from www.memtest.org on Feb 24,2016
and unpacked.  No changes have yet been made.
Version: Memtest86+ V5.01 (27/09/2013)

sha1sum of tarball:
eb4b6d70114db3c9ed320785da44c0ba8349c10c *memtest86+-5.01.tar.gz

Change-Id: I1ed925c85925ff5d98e61a11ad1dc6b3c44f79b7
Signed-off-by: Martin Roth <martinroth@google.com>
Reviewed-on: https://review.coreboot.org/13818
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
Tested-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
diff --git a/FAQ b/FAQ
new file mode 100644
index 0000000..3c2a01c
--- /dev/null
+++ b/FAQ
@@ -0,0 +1,310 @@
+- What is memtest86+, what do I use it for?
+
+  Memtest86+ is a utility designed to test whether your memory is in working
+  order. It repeatedly writes an enormous amount of different patterns to all
+  memory locations and reads them back again and verifies whether the result
+  of the read is the same as what was written to memory.
+
+  There can be a multitude of reasons for running memtest, but foremost of all
+  is of course to test whether your memory modules might be bad. Whenever you
+  suspect your modules to be bad because of system crashes, lockups or reboots
+  it would be nice to know whether the modules are in working order.
+  Memtest86+ is a utility which tries to answer that question for you.
+
+  Another common use exists in the overclocking scene. When overclocking a
+  system you are essentially pushing your system to the limits and at some
+  point it will simply give way and break. Unfortunately there isn't a clear
+  cut way of deciding whether a system is still working correctly. Because of
+  the complexity of a computer a system which is pushed to the limits doesn't
+  just break completely when it starts to fail, instead little errors start
+  showing up in many different places in the system growing more frequent and
+  widespread the more the system is pushed. Each one of these little errors
+  can lead to a crash of your system but can also go unnoticed for days or
+  weeks in a running system. The art so to speak of overclocking is thus to
+  push the system as far as it can go without introducing any such errors. As
+  memory is usually one of the first places these such errors start coming up
+  a memory test is very useful.
+
+- How do I get it to run?
+
+  There are several ways to use memtest, which are described below:
+
+  + Run from floppydisk
+
+    Memtest86+ is directly executable by any modern x86 compatible machine, by
+    writing the bootable binary to a floppy disk one can boot from the disk to
+    run memtest.
+
+    Simply download the appropriate package, the Pre-Compiled Bootable Binary
+    (.gz) package for Linux users and the Pre-Compiled package for Floppy (DOS
+    - Win) for Windows users.
+
+    For Windows, unzip the package into a directory like C:\memtest, insert a
+    blank floppy into your a: disk drive and run the install.bat file. As the
+    install prompts you, to use memtest directly, leave the disk in the drive
+    and reboot your machine.
+
+    For Linux, unpack the package into your home directory, insert a blank
+    floppy into your floppy drive and execute 'dd if=~/memtest+-1.xx.bin.gz
+    of=/dev/fd0 conv=osync' replacing 1.xx with the correct version number of
+    the memtest86+ you downloaded. To run memtest immediately reboot your
+    machine.
+
+    Your machine should now boot from the disk, display the word Loading
+    folowed by a series of periods and then show a screen much like the
+    screenshots on the memtest86+ web page. The test is automatically started.
+
+    If your machine simply boots back into Windows/Linux you will most likely
+    have to configure your BIOS to attempt to boot from floppy disk on
+    startup, refer to your computer's/mainboard's manual how to do this.
+
+    When you are done testing simply remove the floppy and reset your
+    computer, if ever you want to execure the test again simply reinsert the
+    disk and reboot/start your computer.
+
+  + Run from CD
+
+    Memtest86+ is directly executable by any modern x86 compatible machine, by
+    writing the iso to a CD one can boot from the CD to run memtest.
+
+    Simply download the appropriate package, the Download - Pre-Compiled
+    Bootable ISO (.gz) for Linux users and the Pre-Compiled Bootable ISO
+    (.zip) for Windows users.
+
+    For Windows, unzip the package into a directory like C:\memtest. You will
+    now see a file called memtest86+-1.xx.iso in this directory. You will need
+    to burn this file to a CD with a CD recording program. Do note however
+    that you should not make a regular data CD on which you for instance write
+    your text documents and holiday photographs. Instead the iso file is a so
+    called image of a CD, it is a direct copy of a CD. Your CD recording
+    program will most likely have a feature called burn image or something to
+    that effect which you should use to burn the CD.
+
+    For linux, unzip the package into your home directory. and execute
+    'cdrecord dev=<your burner> ~/memtest86+-1.xx.iso' where you replace <your
+    burner> with the scsi address of your CD burner and replace 1.xx with the
+    correct version number of the memtest86+ your downloaded.
+
+    When the burning completed your drive will most likely have ejected the CD
+    and you should have a bootable memtest86+ CD. To run the test directly
+    reinsert the CD and reboot your machine.
+
+    Your machine should now boot from the CD, display the word Loading folowed
+    by a series of periods and then show a screen much like the screenshots on
+    the memtest86+ web page. The test is automatically started.
+
+    If your machine simply boots back into Windows/Linux you will most likely
+    have to configure your BIOS to attempt to boot from CD-ROM drive on
+    startup, refer to your computer's/mainboard's manual how to do this.
+
+    When you are done testing simply remove the CD and reset your computer, if
+    ever you want to execure the test again simply reinsert the CD and
+    reboot/start your computer.
+
+  + Run from USB Flash drive
+
+    FIXME
+
+  + Run from boot manager
+
+    FIXME
+
+- How long does memtest86+ run? How do I stop it?
+
+  Memtest86+ runs indefinately unless you stop it. It does however repeat the
+  same tests over and over again. Memtest86+ contains a number of different
+  tests which each take different approaches in trying to expose any errors in
+  your memory. In the top right of your screen you can see the progress of
+  each test in the lower of the two progress bars. The topmost progress bar
+  shows the progress of a pass, each pass consists of all the tests in the
+  memtest suite.
+
+  Thus all tests are executed in one pass, so does that mean that no errors
+  will show after the first pass if that pass didn't reveal any errors? Well
+  no, there are several reasons why errors might only show up after a number
+  of passes. Firstly as of this writing, the latest version of memtest also
+  includes a test which uses random test patterns, each pass these patterns
+  will of course be different. Secondly some types of errors simply don't show
+  up until the system has been running for a while or are very critical on a
+  certain timing condition, or other such conditions.
+
+  To conclude, one successful pass of memtest will give you a pretty good idea
+  that your memory is ok, only in rare cases will there be errors showing
+  after the first pass. To be sure though simply have the test run overnight
+  or even for a couple of days depending on the level of importance of the
+  system.
+  
+- How many errors are acceptable?
+
+  No errors are acceptable. Even if there is just one error, something is
+  amiss which can cause your system to crash. Of course what the cause of the
+  errors is you will still have to determine.
+
+- What do I do when I get errors?
+
+  Firstly, don't start drawing any conclusions. You only know that memtest86+
+  is giving your errors, not what the cause is. Unfortunately it is not a
+  straightforward exercise to decisively test the memory in an actual system.
+  This is because a computer is not just built up of some memory, but also
+  includes many other elements such as a memory controller, cache, a cache
+  controller, algorithmic and logic units, etc, all of which contribute to the
+  machine. If there are faults in any of these other parts of the computer you
+  will likely also see errors showing up in memtest.
+
+  So what to do? First verify that the BIOS settings of your machine are
+  correctly configured. Look up the memory timing settings applicable to the
+  brand and type of memory modules you have and check they match your BIOS
+  settings, correct them if they don't and run memtest again
+  
+  Ok, you have all the settings correctly set and you're still getting errors.
+  Well of course a very likely cause are the memory modules and the logical
+  course of action is to look into them further.
+
+  If you are well stocked, have a few other machines at your disposal, or just
+  want to spend the cash for some new modules the best way to test if the
+  cause are your memory modules is just to replace them and test again. If you
+  are less fortunate though there is still something you can do.
+
+  If you have more then one module in your system, test them one by one, if
+  one is consistently giving errors and another is consistently showing no
+  errors it's a pretty good bet that the module giving the errors is simply
+  defective. To exclude the possibility that a defective slot is throwing your
+  results, use the same slot to test each different module.
+  
+  If each module by itself shows no errors, but when you place two or more
+  modules into the machine at the same time you do get errors, you are most
+  likely stuck with a compatibility issue and unfortunately there isn't a
+  whole lot you can do about it. Be sure to check your computer/motherboard
+  manual to see if the setup you are trying is allowed, some boards require
+  special restrictions in the sizes of modules, the order of modules, the
+  placement of double sided and single sides modules and more of such things.
+
+  If you have only one module in your system, or all modules are giving
+  errors, there are only very few options left. The only thing you can do
+  really is to try the module(s) in another slot. Finally simply try out
+  different orders of the memory modules, although your manual might not
+  mention anything on the matter sometimes there simply exist timing or other
+  issues which can be resolved by changing the order of your modules.  And of
+  course test each slot by putting a single module into that slot and running
+  memtest on it.
+
+  In the end if you still have not been able to localize the problem you will
+  have to find a replacement module to establish whether the problem lies in
+  your modules. See if you can borrow a module from someone else.
+
+  When you have replaced the memory by new memory and the errors still
+  persist, first check if you can rule out any compatibility issues or timing
+  issues. If you are sure the memory should work in the system the cause of
+  the errors must obviously lie someplace else in the system.
+  
+  The only way to find out where, is by trial and error really. Simply start
+  replacing and/or removing parts of your computer one by one, running memtest
+  each time you changed anything, until the errors are resolved.
+
+- I'm getting errors in test #x, what doest that mean?
+
+  Interpreting memtest results is as scientific an endeavour as testing
+  whether a person is a witch by the methods used in Monty Python's Holy
+  Grail. In short, don't even start, it's not going to get you anywhere. Just
+  interpret any error as you should any other and use the methods descibed in
+  the previous question to determine the cause.
+
+- I'm getting errors in test #5 and/or #8 and have read a lot about it.
+
+  Yes there are just about enough discussions on the topic to fill a book, but
+  it all boils down to the answer given above. The only thing that can be said
+  is that many a times, when memory latencies are incorrectly set in the BIOS
+  you will experience errors in test #5 and #8. (Though #8 does not exist
+  anymore as of version 1.40 and might be reinstated as a different test in a
+  later version.) This does however NOT mean that errors in these tests are
+  always the cause of incorrect settings, your memory might just as well be
+  defective.
+
+- I'm getting errors in memtest on one machine, but not when I put the same
+  memory in another, what does that mean?
+
+  It can mean one of two things:
+    - The machine that is giving the errors is defective. Errors don't just
+      orginate from the memory module itself, but can also be caused by
+      defects in the cpu, chipset, motherboard, PSU and even by timing issues
+      introduced by any other component in the machine.
+    - The machine giving the errors is imposing stricter timing than the other
+      which the memory module simply can't cope with. If the module should
+      work with the machine according to its specifications then it most
+      likely is defective.
+
+- Which memory is tested?
+
+  As much as possible of the system memory is tested. Unfortunately memtest86+
+  can usually not test all of the memory. The reason for this is that todays
+  processors have become so complex that they require a small amount of memory
+  to keep accounting data of the processor state. If memtest were to write
+  over these areas the state of the processor becomes invalid and it's
+  behaviour unpredictable. Alas it is also impossible to relocate these areas
+  in the memory.
+
+  This means that a small area of your memory can not be tested by memtest. If
+  this part of the memory is defective you will know soon enough though as the
+  processor, or parts of the processor simply won't work correctly if this
+  part of your memory is defective. Do realise though that in very rare cases
+  memtest will show no errors even though the module is defective, not because
+  memtest can't detect the error, but because memtest can't test the area the
+  error is located in.
+
+- When I select BIOS-ALL I get many errors / my machine crashes.
+
+  This is normal. With todays computers this option should never be selected.
+  See the previous question about the reason for the errors.
+
+- I want to use memtest on a multiboot CD, how do I do this?
+
+  This is of course very dependent on which boot loader you use for your CD.
+  Below is a description of how to set up a multiboot CD including memtest+
+  with isolinux, if you have experience with any other bootloader(s) please
+  consider writing a small description of using memtest with that bootloader
+  for the FAQ.
+
+  -isolinux
+    
+    For general instructions on how to make a bootable CD with isolinux see
+    the syslinux website and the manual. What you need to do to get memtest
+    working is as follows.
+
+    Download the Pre-Compiled Bootable Binary, the .gz if you are working
+    under linux, the .zip if you are working under windows. Unpack the file
+    from the package and rename it to an 8.3 filename with an extension other
+    than .bin, renaming to memtest. (without an extension) is a good choice.
+
+    Put the file somewhere in your CD directory structure, for example in
+    images/memtest and edit your config file to include the following:
+
+    label memtest
+        kernel /images/memtest
+
+    If you want to boot memtest automatically insert or change a line at the
+    top to:
+
+    default memtest
+
+    If you want to display a prompt from which you can start memtest add or
+    change the lines at the top to: (Change the timeout to suit your needs)
+
+    prompt 1
+    timeout 200
+
+- If memtest86+ shows no errors does that mean my memory is not defective?
+
+  Of course no answers are definitive, no matter how good memtest86+ will
+  eventually become there is always the possibility that a particular type of
+  error will go unnoticed. As long as you are having no problems with the
+  system it will be pretty safe to say that the modules are good. If you are
+  having problems with the system however you will just have to check by trial
+  and error, ie swapping the modules for new ones and/or testing with modules
+  of a different brand/type.
+
+- When I run install.bat it doesn't write anything to floppy.
+
+  You most likely have unpacked the memtest+-2.xx.floppy.zip file into a
+  folder with a long pathname and/or containing + and - signs. It seems
+  rawrite doesn't like that. Just move the files you unpacked to a directory
+  like c:\memtest and execure it from there.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..9717a14
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,81 @@
+# Makefile for MemTest86+
+#
+# Author:		Chris Brady
+# Created:		January 1, 1996
+
+
+#
+# Path for the floppy disk device
+#
+FDISK=/dev/fd0
+
+AS=as -32
+CC=gcc
+
+CFLAGS= -Wall -march=i486 -m32 -O1 -fomit-frame-pointer -fno-builtin \
+	-ffreestanding -fPIC $(SMP_FL) -fno-stack-protector 
+	
+OBJS= head.o reloc.o main.o test.o init.o lib.o patn.o screen_buffer.o \
+      config.o cpuid.o linuxbios.o pci.o memsize.o spd.o error.o dmi.o controller.o \
+      smp.o vmem.o random.o
+      
+
+all: clean memtest.bin memtest 
+		 scp memtest.bin root@192.168.0.12:/srv/tftp/mt86plus
+
+# Link it statically once so I know I don't have undefined
+# symbols and then link it dynamically so I have full
+# relocation information
+memtest_shared: $(OBJS) memtest_shared.lds Makefile
+	$(LD) --warn-constructors --warn-common -static -T memtest_shared.lds \
+	 -o $@ $(OBJS) && \
+	$(LD) -shared -Bsymbolic -T memtest_shared.lds -o $@ $(OBJS)
+
+memtest_shared.bin: memtest_shared
+	objcopy -O binary $< memtest_shared.bin
+
+memtest: memtest_shared.bin memtest.lds
+	$(LD) -s -T memtest.lds -b binary memtest_shared.bin -o $@
+
+head.s: head.S config.h defs.h test.h
+	$(CC) -E -traditional $< -o $@
+
+bootsect.s: bootsect.S config.h defs.h
+	$(CC) -E -traditional $< -o $@
+
+setup.s: setup.S config.h defs.h
+	$(CC) -E -traditional $< -o $@
+
+memtest.bin: memtest_shared.bin bootsect.o setup.o memtest.bin.lds
+	$(LD) -T memtest.bin.lds bootsect.o setup.o -b binary \
+	memtest_shared.bin -o memtest.bin
+
+reloc.o: reloc.c
+	$(CC) -c $(CFLAGS) -fno-strict-aliasing reloc.c
+
+test.o: test.c
+	$(CC) -c -Wall -march=i486 -m32 -O0 -fomit-frame-pointer -fno-builtin -ffreestanding test.c
+
+random.o: random.c
+	$(CC) -c -Wall -march=i486 -m32 -O3 -fomit-frame-pointer -fno-builtin -ffreestanding random.c
+	
+# rule for build number generation  
+build_number:
+	sh make_buildnum.sh  
+
+clean:
+	rm -f *.o *.s *.iso memtest.bin memtest memtest_shared \
+		memtest_shared.bin memtest.iso
+
+iso:
+	make all
+	./makeiso.sh
+
+install: all
+	dd <memtest.bin >$(FDISK) bs=8192
+
+install-precomp:
+	dd <precomp.bin >$(FDISK) bs=8192
+	
+dos: all
+	cat mt86+_loader memtest.bin > memtest.exe
diff --git a/README b/README
new file mode 100644
index 0000000..f9df887
--- /dev/null
+++ b/README
@@ -0,0 +1,925 @@
+			====================
+			= MemTest-86 v4.0  =
+			=   28 Mar, 2011    =
+			=   Chris Brady    =
+			====================
+Table of Contents
+=================
+  1) Introduction
+  2) Licensing
+  3) Installation
+  4) Serial Port Console
+  5) Online Commands
+  6) Memory Sizing
+  7) Error Display
+  8) Trouble-shooting Memory Errors
+  9) Execution Time
+ 10) Memory Testing Philosophy
+ 11) Memtest86 Test Algorithms
+ 12) Individual Test Descriptions
+ 13) Problem Reporting - Contact Information
+ 14) Known Problems
+ 15) Planned Features List
+ 16) Change Log
+ 17) Acknowledgments
+
+
+1) Introduction
+===============
+Memtest86 is thorough, stand alone memory test for Intel/AMD x86 architecture
+systems.  BIOS based memory tests are only a quick check and often miss
+failures that are detected by Memtest86.
+
+For updates go to the Memtest86 web page:
+
+	http://www.memtest86.com
+
+
+2) Licensing
+============
+Memtest86 is released under the terms of the Gnu Public License (GPL). Other
+than the provisions of the GPL there are no restrictions for use, private or
+commercial.  See: http://www.gnu.org/licenses/gpl.html for details.
+
+
+3) Linux Installation
+============================
+Memtest86 is a stand alone program and can be loaded from either a disk
+partition or from a floppy disk.
+
+To build Memtest86:
+   1) Review the Makefile and adjust options as needed.
+   2) Type "make"
+
+This creates a file named "memtest.bin" which is a bootable image.  This
+image file may be copied to a floppy disk or may be loaded from a disk
+partition via Lilo or Grub image from a hard disk partition.
+
+  To create a Memtest86 bootdisk
+   1) Insert a blank write enabled floppy disk.
+   2) As root, Type "make install"
+
+  To boot from a disk partition via Grub
+   1) Copy the image file to a permanent location (ie. /boot/memtest.bin).
+   2) Add an entry in the Grub config file (/boot/grub/menu.lst) to boot
+      memtest86.  Only the title and kernel fields need to be specified. 
+      The following is a sample Grub entry for booting memtest86:
+
+	title Memtest86
+	    kernel (hd0,0)/memtest.bin
+
+  To boot from a disk partition via Lilo
+   1) Copy the image file to a permanent location (ie. /boot/memtest.bin).
+   2) Add an entry in the lilo config file (usually /etc/lilo.conf) to boot
+      memtest86.  Only the image and label fields need to be specified.
+      The following is a sample Lilo entry for booting memtest86:
+
+        image = /boot/memtest.bin
+        label = memtest86
+
+   3) As root,  type "lilo"
+
+If you encounter build problems a binary image has been included (precomp.bin).
+To create a boot-disk with this pre-built image do the following:
+   1) Insert a blank write enabled floppy disk.
+   2) Type "make install-precomp"
+
+
+4) Serial Console
+=================
+Memtest86 can be used on PC's equipped with a serial port for the console.
+By default serial port console support is not enabled since it slows
+down testing.  To enable change the SERIAL_CONSOLE_DEFAULT define in
+config.h from a zero to a one.  The serial console baud rate may also
+be set in config.h with the SERIAL_BAUD_RATE define.  The other serial
+port settings are no parity, 8 data bits, 1 stop bit.  All of the features
+used by memtest86 are accessible via the serial console.  However, the
+screen sometimes is garbled when the online commands are used.
+
+
+5) Online Commands
+==================
+Memtest86 has a limited number of online commands.  Online commands
+provide control over caching, test selection, address range and error
+scrolling.  A help bar is displayed at the bottom of the screen listing
+the available on-line commands. 
+
+  Command  Description
+
+  ESC   Exits the test and does a warm restart via the BIOS.
+
+  c     Enters test configuration menu
+	    Menu options are:
+               1) Test selection
+	       2) Address Range
+	       3) Error Report Mode
+	       4) CPU Selection Mode
+	       5) Refresh Screen 
+
+  SP    Set scroll lock (Stops scrolling of error messages)
+	Note: Testing is stalled when the scroll lock is
+	set and the scroll region is full.
+
+  CR    Clear scroll lock (Enables error message scrolling)
+
+
+6) Error Information
+======================
+Memtest has three options for reporting errors.  The default is an an error
+summary that displays the most relevant error information. The second option
+is reporting of individual errors.  In BadRAM Patterns mode patterns are
+created for use with the Linux BadRAM feature. This slick feature allows
+Linux to avoid bad memory pages. Details about the BadRAM feature can be
+found at:
+
+	http://home.zonnet.nl/vanrein/badram
+
+The error summary mode displays the following information:
+
+  Error Confidence Value:
+     A value that indicates the validity of the errors being reported with
+     larger values indicating greater validity. There is a high probability
+     that all errors reported are valid regardless of this value.  However,
+     when this value exceeds 100 it is nearly impossible that the reported
+     errors will be invalid.
+
+  Lowest Error Address:
+     The lowest address that where an error has been reported.
+
+  Highest Error Address:
+     The highest address that where an error has been reported.
+
+  Bits in Error Mask:
+     A mask of all bits that have been in error (hexadecimal).
+
+  Bits in Error:
+     Total bit in error for all error instances and the min, max and average
+     bit in error of each individual occurrence.
+
+  Max Contiguous Errors:
+     The maximum of contiguous addresses with errors.
+
+  ECC Correctable Errors:
+     The number of errors that have been corrected by ECC hardware.
+
+  Test  Errors:
+     On the right hand side of the screen the number of errors for each test
+     are displayed.
+
+For individual errors the following information is displayed when a memory
+error is detected.  An error message is only displayed for errors with a
+different address or failing bit pattern.  All displayed values are in
+hexadecimal.
+
+  Tst:			Test number
+  Failing Address:	Failing memory address 
+  Good:			Expected data pattern 
+  Bad:			Failing data pattern 
+  Err-Bits:		Exclusive or of good and bad data (this shows the
+			position of the failing bit(s))
+  Count:		Number of consecutive errors with the same address
+			and failing bits
+  CPU:			CPU that detected the error
+
+In BadRAM Patterns mode, Lines are printed in a form badram=F1,M1,F2,M2.
+In each F/M pair, the F represents a fault address, and the corresponding M
+is a bitmask for that address. These patterns state that faults have
+occurred in addresses that equal F on all "1" bits in M. Such a pattern may
+capture more errors that actually exist, but at least all the errors are
+captured. These patterns have been designed to capture regular patterns of
+errors caused by the hardware structure in a terse syntax.
+
+The BadRAM patterns are `grown' increment-ally rather than `designed' from an
+overview of all errors. The number of pairs is constrained to five for a
+number of practical reasons. As a result, handcrafting patterns from the
+output in address printing mode may, in exceptional cases, yield better
+results.
+
+
+7) Trouble-shooting Memory Errors
+================================
+Please be aware that not all errors reported by Memtest86 are due to
+bad memory. The test implicitly tests the CPU, L1 and L2 caches as well as
+the motherboard.  It is impossible for the test to determine what causes
+the failure to occur.  Most failures will be due to a problem with memory.
+When it is not, the only option is to replace parts until the failure is
+corrected.  
+
+Once a memory error has been detected, determining the failing
+module is not a clear cut procedure.  With the large number of motherboard
+vendors and possible combinations of simm slots it would be difficult if
+not impossible to assemble complete information about how a particular
+error would map to a failing memory module.  However, there are steps
+that may be taken to determine the failing module.  Here are three
+techniques that you may wish to use:
+
+1) Removing modules
+This is simplest method for isolating a failing modules, but may only be
+employed when one or more modules can be removed from the system.  By
+selectively removing modules from the system and then running the test
+you will be able to find the bad module(s).  Be sure to note exactly which
+modules are in the system when the test passes and when the test fails.
+
+2) Rotating modules
+When none of the modules can be removed then you may wish to rotate modules
+to find the failing one.  This technique can only be used if there are
+three or more modules in the system.  Change the location of two modules
+at a time.  For example put the module from slot 1 into slot 2 and put
+the module from slot 2 in slot 1.  Run the test and if either the failing
+bit or address changes then you know that the failing module is one of the
+ones just moved. By using several combinations of module movement you
+should be able to determine which module is failing.
+
+3) Replacing modules
+If you are unable to use either of the previous techniques then you are
+left to selective replacement of modules to find the failure.  
+
+4) Avoiding allocation
+The printing mode for BadRAM patterns is intended to construct boot time
+parameters for a Linux kernel that is compiled with BadRAM support. This
+work-around makes it possible for Linux to reliably run on defective
+RAM.  For more information on BadRAM support
+for Linux, sail to
+
+       http://home.zonnet.nl/vanrein/badram
+
+Sometimes memory errors show up due to component incompatibility.  A memory
+module may work fine in one system and not in another.  This is not
+uncommon and is a source of confusion.  The components are not necessarily
+bad but certain combinations may need to be avoided.
+
+I am often asked about the reliability of errors reported by Mestest86.
+In the vast majority of cases errors reported by the test are valid.
+There are some systems that cause Memtest86 to be confused about the size of
+memory and it will try to test non-existent memory.  This will cause a large
+number of consecutive addresses to be reported as bad and generally there
+will be many bits in error.  If you have a relatively small number of
+failing addresses and only one or two bits in error you can be certain
+that the errors are valid.  Also intermittent errors are always valid.
+
+All valid memory errors should be corrected.  It is possible that a
+particular error will never show up in normal operation. However, operating
+with marginal memory is risky and can result in data loss and even
+disk corruption.  You can be sure that Murphy will get you if you know
+about a memory error and ignore it.
+
+Memtest86 can not diagnose many types of PC failures.  For example a
+faulty CPU that causes Windows to crash will most likely just cause
+Memtest86 to crash in the same way.
+
+
+8) Execution Time
+==================
+The time required for a complete pass of Memtest86 will vary greatly
+depending on CPU speed, memory speed and memory size. Memtest86 executes 
+indefinitely.  The pass counter increments each time that all of the 
+selected tests have been run.  Generally a single pass is sufficient to 
+catch all but the most obscure errors. However, for complete confidence 
+when intermittent errors are suspected testing for a longer period is advised.
+
+9) Memory Testing Philosophy
+=============================
+There are many good approaches for testing memory.  However, many tests
+simply throw some patterns at memory without much thought or knowledge
+of memory architecture or how errors can best be detected. This
+works fine for hard memory failures but does little to find intermittent
+errors. BIOS based memory tests are useless for finding intermittent
+memory errors.
+
+Memory chips consist of a large array of tightly packed memory cells,
+one for each bit of data.  The vast majority of the intermittent failures
+are a result of interaction between these memory cells.  Often writing a
+memory cell can cause one of the adjacent cells to be written with the
+same data. An effective memory test attempts to test for this
+condition. Therefore, an ideal strategy for testing memory would be
+the following:
+
+  1) write a cell with a zero
+  2) write all of the adjacent cells with a one, one or more times
+  3) check that the first cell still has a zero
+
+It should be obvious that this strategy requires an exact knowledge
+of how the memory cells are laid out on the chip.  In addition there is a
+never ending number of possible chip layouts for different chip types
+and manufacturers making this strategy impractical.  However, there
+are testing algorithms that can approximate this ideal strategy. 
+
+
+11) Memtest86 Test Algorithms
+=============================
+Memtest86 uses two algorithms that provide a reasonable approximation
+of the ideal test strategy above.  The first of these strategies is called
+moving inversions.  The moving inversion test works as follows:
+
+  1) Fill memory with a pattern
+  2) Starting at the lowest address
+	2a check that the pattern has not changed
+	2b write the patterns complement
+	2c increment the address
+	repeat 2a - 2c
+  3) Starting at the highest address
+	3a check that the pattern has not changed
+	3b write the patterns complement
+	3c decrement the address
+	repeat 3a - 3c
+
+This algorithm is a good approximation of an ideal memory test but
+there are some limitations.  Most high density chips today store data
+4 to 16 bits wide.  With chips that are more than one bit wide it
+is impossible to selectively read or write just one bit.  This means
+that we cannot guarantee that all adjacent cells have been tested
+for interaction.  In this case the best we can do is to use some
+patterns to insure that all adjacent cells have at least been written
+with all possible one and zero combinations.
+
+It can also be seen that caching, buffering and out of order execution
+will interfere with the moving inversions algorithm and make less effective.
+It is possible to turn off cache but the memory buffering in new high
+performance chips can not be disabled.  To address this limitation a new
+algorithm I call Modulo-X was created.  This algorithm is not affected by
+cache or buffering.  The algorithm works as follows:
+  1) For starting offsets of 0 - 20 do
+	1a write every 20th location with a pattern
+	1b write all other locations with the patterns complement
+	   repeat 1b one or more times
+	1c check every 20th location for the pattern
+
+This algorithm accomplishes nearly the same level of adjacency testing
+as moving inversions but is not affected by caching or buffering.  Since
+separate write passes (1a, 1b) and the read pass (1c) are done for all of
+memory we can be assured that all of the buffers and cache have been
+flushed between passes.  The selection of 20 as the stride size was somewhat
+arbitrary.  Larger strides may be more effective but would take longer to
+execute.  The choice of 20 seemed to be a reasonable compromise between
+speed and thoroughness.
+
+
+11) Individual Test Descriptions
+================================
+Memtest86 executes a series of numbered test sections to check for
+errors.  These test sections consist of a combination of test
+algorithm, data pattern and caching. The execution order for these tests
+were arranged so that errors will be detected as rapidly as possible.
+A description of each of the test sections follows:
+
+Test 0 [Address test, walking ones, no cache]
+  Tests all address bits in all memory banks by using a walking ones
+  address pattern.  Errors from this test are not used to calculate
+  BadRAM patterns.
+
+Test 1 [Address test, own address Sequential]
+  Each address is written with its own address and then is checked
+  for consistency.  In theory previous tests should have caught any
+  memory addressing problems.  This test should catch any addressing
+  errors that somehow were not previously detected. This test is done
+  sequentially with each available CPU.
+
+Test 2 [Address test, own address Parallel]
+  Same as test 1 but the testing is done in parallel using all CPUs using
+  overlapping addresses. 
+
+Test 3 [Moving inversions, ones&zeros Sequential]
+  This test uses the moving inversions algorithm with patterns of all
+  ones and zeros.  Cache is enabled even though it interferes to some
+  degree with the test algorithm.  With cache enabled this test does not
+  take long and should quickly find all "hard" errors and some more
+  subtle errors.  This test is done sequentially with each available CPU.
+
+Test 4 [Moving inversions, ones&zeros Parallel]
+  Same as test 3 but the testing is done in parallel using all CPUs.
+ 
+Test 5 [Moving inversions, 8 bit pat]
+  This is the same as test 4 but uses a 8 bit wide pattern of
+  "walking" ones and zeros.  This test will better detect subtle errors
+  in "wide" memory chips.  A total of 20 data patterns are used.
+
+Test 6 [Moving inversions, random pattern]
+  Test 6 uses the same algorithm as test 4 but the data pattern is a
+  random number and it's complement. This test is particularly effective
+  in finding difficult to detect data sensitive errors. 
+  The random number sequence is different with each pass 
+  so multiple passes increase effectiveness. 
+  
+Test 7 [Block move, 64 moves]
+  This test stresses memory by using block move (movsl) instructions
+  and is based on Robert Redelmeier's burnBX test.  Memory is initialized
+  with shifting patterns that are inverted every 8 bytes.  Then 4MB blocks
+  of memory are moved around using the movsl instruction.  After the moves
+  are completed the data patterns are checked.  Because the data is checked
+  only after the memory moves are completed it is not possible to know
+  where the error occurred.  The addresses reported are only for where the
+  bad pattern was found.  Since the moves are constrained to a 8MB segment
+  of memory the failing address will always be lest than 8MB away from the
+  reported address.  Errors from this test are not used to calculate
+  BadRAM patterns.
+
+Test 8 [Moving inversions, 32 bit pat]
+  This is a variation of the moving inversions algorithm that shifts the data
+  pattern left one bit for each successive address. The starting bit position
+  is shifted left for each pass. To use all possible data patterns 32 passes
+  are required.  This test is quite effective at detecting data sensitive
+  errors but the execution time is long.
+ 
+Test 9 [Random number sequence]
+ This test writes a series of random numbers into memory. By resetting the
+ seed for the random number the same sequence of number can be created for
+ a reference. The initial pattern is checked and then complemented and
+ checked again on the next pass. However, unlike the moving inversions test
+ writing and checking can only be done in the forward direction.
+
+Test 10 [Modulo 20, random pattern]
+  Using the Modulo-X algorithm should uncover errors that are not
+  detected by moving inversions due to cache and buffering interference
+  with the the algorithm.  A 32 bit random pattern is used.
+
+Test 11 [Bit fade test, 2 patterns]
+  The bit fade test initializes all of memory with a pattern and then
+  sleeps for 5 minutes. Then memory is examined to see if any memory bits
+  have changed. All ones and all zero patterns are used.
+
+
+12) Problem Reporting - Contact Information
+===========================================
+Due to the growing popularity of Memtest86 (more than 200,000 downloads per
+month) I have been inundated by, questions, feedback, problem reports and
+requests for enhancements. I simply do not have time to respond to ANY Memtest86
+emails. Bug reports and suggestions are welcome but will typically not be
+responded to.
+
+*** NOTE: *** The Keyword MEM86 must appear in the subject of all emails or
+the message will be automaticly deleted before it gets to me. This thanks to
+spam and viruses!
+
+Problems/Bugs:
+Before submitting a problem report please check the Known Problems section
+to see if this problem has already been reported.  Be sure to include the
+version number and also any details that may be relevant.
+
+Chris Brady, Email: bugs@memtest86.com
+
+With some PC's Memtest86 will just die with no hints as to what went wrong.
+Without any details it is impossible to fix these failures.  Fixing these
+problems will require debugging on your part. There is no point in reporting
+these failures unless you have a Linux system and would be willing to debug
+the failure.
+
+Enhancements:
+If you would like to request an enhancement please see if is already on
+the Planned Features List before sending your request.  All requests will
+be considered, but not all can be implemented.  If you are be interested in
+contributing code please contact me so that the integration can be
+co-ordinated.
+
+Chris Brady, Email: enhance@memtest86.com
+
+Questions:
+Unfortunately, I do not have time to respond to any questions or provide
+assistance with troubleshooting problems. Please read the Troubleshooting
+and Known Problems sections for assistance with problems. These sections have
+the answers for the questions that I have answers to. If there is not an
+answer for your problem in these sections it is probably not something I can
+help you with.
+
+
+15) Known Problems
+==================
+Sometimes when booting from a floppy disk the following messages scroll up
+on the screen:
+        X:8000
+        AX:0212
+        BX:8600
+        CX:0201
+        DX:0000
+This the BIOS reporting floppy disk read errors.  Either re-write or toss
+the floppy disk.
+
+Memtest86 can not diagnose many types of PC failures.  For example a
+faulty CPU that causes Windows to crash will most likely just cause
+Memtest86 to crash in the same way.
+
+There have been numerous reports of errors in only the block move test. Often
+the memory works in a different system or the vendor insists that it is good.
+In these cases the memory is not necessarily bad but is not able to operate
+reliably high speeds.  Sometimes more conservative memory timings on the
+motherboard will correct these errors.  In other cases the only option is to
+replace the memory with better quality, higher speed memory.  Don't buy cheap
+memory and expect it to work at full speed.
+
+Memtest86 supports all types of memory.  If fact the test has absolutely
+no knowledge of the memory type nor does it need to.  This not a problem
+or bug but is listed here due to the many questions I get about this issue.
+
+Changes in the compiler and loader have caused problems with
+Memtest86 resulting in both build failures and errors in execution.  A
+binary image (precomp.bin) of the test is included and may be used if
+problems are encountered.
+
+
+15) Planned Features List
+=========================
+This is a list of enhancements planned for future releases of Memtest86.
+There is no timetable for when these will be implemented.
+
+  - Testing in 64 bit mode with 64 data patterns
+  - Support for reporting ECC errors was removed in the 4.0 release. A
+    simplified implementation of ECC reporting is planned for a future release.
+
+
+16) Change Log
+==============
+Enhancements in v4.0 (28/Mar/2011)
+
+  Full support for testing with multiple CPUs. All tests except for #11 (Bit
+  Fade) have been multi-threaded. A maximum of 16 CPUs will be used for testing.
+
+  CPU detection has been completely re-written to use the brand ID string
+  rather than the cumbersome, difficult to maintain and often out of date
+  CPUID family information. All new processors will now be correctly
+  identified without requiring code support. 
+
+  All code related to controller identification, PCI and DMI has been removed.
+  This may be a controversial decision and was not made lightly. The following
+  are justifications for the decision:
+
+    1. Controller identification has nothing to do with actual testing of
+       memory, the core purpose of Memtest86. 
+    2. This code needed to be updated with every new chipset. With the ever
+       growing number of chipsets it is not possible to keep up with the
+       changes. The result is that new chipsets were more often than not
+       reported in-correctly. In the authors opinion incorrect information is
+       worse than no information.
+    3. Probing for chipset information carries the risk of making the program
+       crash. 
+    4. The amount of code involved with controller identification was quite
+       large, making support more difficult.
+
+  Removing this code also had the unfortunate effect of removing reporting of
+  correctable ECC errors. The code to support ECC was hopelessly intertwined
+  the controller identification code. A fresh, streamlined implementation of
+  ECC reporting is planned for a future release.
+    
+  A surprising number of conditions existed that potentially cause problems
+  when testing more than 4 GB of memory. Most if not all of these conditions
+  have been identified and corrected.
+
+  A number of cases were corrected where not all of memory was being tested.
+  For most tests the last word of each test block was not tested. In addition
+  an error in the paging code was fixed that omitted from testing the last 256
+  bytes of each block above 2 GB.
+
+  The information display has been simplified and a number of details that were
+  not relevant to testing were removed.
+
+  Memory speed reporting has been parallelized for more accurate reporting for
+  multi channel memory controllers.
+
+  This is a major re-write of the Memtest86 with a large number of minor
+  bug-fixes and substantial cleanup and re-organization of the code. 
+
+
+Enhancements in v3.5 (3/Jan/2008)
+
+  Limited support for execution with multiple CPUs. CPUs are selected
+  round-robin or sequential for each test.
+
+  Support for additional chipsets. (from Memtest86+ v2.11).
+
+  Additions and corrections for CPU detection including reporting of L3 cache.
+
+  Reworked information display for better readability and new information.
+
+  Abbreviated iterations for first pass.
+
+  Enhancements to memory sizing.
+
+  Misc fixes.
+
+Enhancements in v3.4 (2/Aug/2007)
+
+  A new error summary display with error confidence analysis.
+
+  Support for additional chipsets. (from Memtest86+ v1.70).
+
+  Additions and corrections for CPU detection.
+
+  Support for memory module information reporting.
+
+  Misc bug fixes.
+
+Enhancements in v3.3 (12/Jan/2007)
+
+  Added support for additional chipsets. (from Memtest86+ v1.60)
+
+  Changed Modulo 20 test (#8) to use a more effective random pattern rather
+  than simple ones and zeros.
+
+  Fixed a bug that prevented testing of low memory.
+
+  Added an advanced menu option to display SPD info (only for selected
+  chipsets).
+
+  Updated CPU detection for new CPUs and corrected some bugs.
+
+  Reworked online command text for better clarity.
+
+  Added a fix to correct a Badram pattern bug.
+
+
+Enhancements in v3.2 (11/Nov/2004)
+
+  Added two new, highly effective tests that use random number patterns
+  (tests 4 and 6).
+
+  Reworked the online commands:
+	- Changed wording for better clarity
+	- Dropped Cache Mode menu
+
+  Updated CPU detection for newer AMD, Intel and Cyrix CPUs.
+
+  Reworked test sequence:
+	- Dropped ineffective non cached tests (Numbers 7-11) 
+	- Changed cache mode to "cached" for test 2 
+	- Fixed bug that did not allow some tests to be skipped
+	- Added bailout for Bit fade test
+
+  Error reports are highlighted in red to provide a more vivid error 
+  indication.
+	    
+  Added support for a large number of additional chipsets. (from Memtest86+
+  v1.30)
+
+  Added an advanced setup feature that with new chiset allows memory timings
+  to be altered from inside Memtest86. (from Memtest86+ v1.30)
+
+
+Enhancements in v3.1 (11/Mar/2004)
+
+   Added processor detection for newer AMD processors.
+
+   Added new "Bit Fade" extended test.
+
+   Fixed a compile time bug with gcc version 3.x.
+
+   E7500 memory controller ECC support
+
+   Added support for 16bit ECC syndromes
+
+   Option to keep the serial port baud rate of the boot loader
+
+
+Enhancements in v3.0 (22/May/2002) Provided by Eric Biederman
+
+   Testing of more than 2gb of memory is at last fixed (tested with 6Gb)
+
+   The infrastructure is to poll ecc error reporting chipset regisets,
+   and the support has been done for some chipsets.
+
+   Uses dynamic relocation information records to make itself PIC
+   instead of requiring 2 copies of memtest86 in the binary.
+
+   The serial console code does not do redundant writes to the serial port
+   Very little slow down at 9600 baud.
+
+   You can press ^l or just l to get a screen refresh, when you are
+   connecting and UN-connecting a serial cable.
+
+   Net-booting is working again
+
+   Linux-BIOS support (To get the memory size)
+
+   Many bug-fixes and code cleanup.
+
+Enhancements in v2.9 (29/Feb/2002)
+
+   The memory sizing code has been completely rewritten.  By default
+   Memtest86 gets a memory map from the BIOS that is now used to find 
+   available memory. A new online configuration option provides three
+   choices for how memory will be sized, including the old "probe" method.
+   The default mode generally will not test all of memory, but should be more
+   stable. See the "Memory Sizing" section for details.
+
+   Testing of more than 2gb of memory should now work.  A number of bugs
+   were found and corrected that prevented testing above 2gb.  Testing
+   with more than 2gb has been limited and there could be problems with a
+   full 4gb of memory.
+
+   Memory is divided into segments for testing.  This allow for frequent
+   progress updates and responsiveness to interactive commands.  The
+   memory segment size has been increased from 8 to 32mb.  This should
+   improve testing effectiveness but progress reports will be less frequent.
+
+   Minor bug fixes.
+
+Enhancements in v2.8 (18/Oct/2001)
+   Eric Biederman reworked the build process making it far simpler and also
+   to produce a network bootable ELF image.
+
+   Re-wrote the memory and cache speed detection code.  Previously the
+   reported numbers were inaccurate for Intel CPU's and completely wrong
+   for Athlon/Duron CPU's.
+
+   By default the serial console is disabled since this was slowing
+   down testing.
+
+   Added CPU detection for Pentium 4.
+
+   
+Enhancements in v2.7 (12/Jul/2001)
+   Expanded workaround for errors caused by BIOS USB keyboard support to
+   include test #5.
+
+   Re-worked L1 / L2 cache detection code to provide clearer reporting.
+
+   Fixed an obvious bug in the computation of cache and memory speeds.
+
+   Changed on-line menu to stay in the menu between option selections.
+
+   Fixed bugs in the test restart and redraw code.
+
+   Adjusted code size to fix compilation problems with RedHat 7.1.
+
+   Misc updates to the documentation.
+
+Enhancements in v2.6 (25/May/2001)
+   Added workaround for errors caused by BIOS USB keyboard support.
+
+   Fixed problems with reporting of 1 GHZ + processor speeds.
+
+   Fixed Duron cache detection.
+
+   Added screen buffer so that menus will work correctly from a serial
+   console.
+
+   The Memtest86 image is now built in ELF format.
+
+Enhancements in v2.5 (14/Dec/00)
+   Enhanced CPU and cache detection to correctly identify Duron CPU
+   and K6-III 1MB cache.
+
+   Added code to report cache-able memory size.
+
+   Added limited support for parity memory.
+
+   Support was added to allow use of on-line commands from a serial
+   port.
+
+   Dropped option for changing refresh rates.  This was not useful
+   and did not work on newer motherboards.
+
+   Improved fatal exception reporting to include a register and stack
+   dump.
+
+   The pass number is now displayed in the error report.
+
+   Fixed a bug that crashed the test when selecting one of the extended
+   tests.
+
+Enhancements in v2.4
+   The error report format was reworked for better clarity and now
+   includes a decimal address in megabytes.
+
+   A new memory move test was added (from Robert Redelmeier's CPU-Burn)
+
+   The test sequence and iterations were modified.
+
+   Fixed scrolling problems with the BadRAM patterns.
+
+
+Enhancements in v2.3
+   A progress meter was added to replace the spinner and dots.
+
+   Measurement and reporting of memory and cache performance  
+   was added.
+
+   Support for creating BadRAM patterns was added.
+
+   All of the test routines were rewritten in assembler to
+   improve both test performance and speed.
+
+   The screen layout was reworked to hopefully be more readable.
+
+   An error summary option was added to the online commands.
+
+
+Enhancements in v2.2
+   Added two new address tests
+
+   Added an on-line command for setting test address range
+
+   Optimized test code for faster execution (-O3, -funroll-loops and
+	-fomit-frame-pointer)
+
+   Added and elapsed time counter.
+
+   Adjusted menu options for better consistency
+
+
+Enhancements in v2.1
+   Fixed a bug in the CPU detection that caused the test to
+   hang or crash with some 486 and Cryrix CPU's
+
+   Added CPU detection for Cyrix CPU's
+
+   Extended and improved CPU detection for Intel and AMD CPU's
+
+   Added a compile time option (BIOS_MEMSZ) for obtaining the last
+   memory address from the BIOS.  This should fix problems with memory
+   sizing on certain motherboards.  This option is not enabled by default.
+   It may be enabled be default in a future release.
+
+Enhancements in v2.0
+   Added new Modulo-20 test algorithm.
+
+   Added a 32 bit shifting pattern to the moving inversions algorithm.
+
+   Created test sections to specify algorithm, pattern and caching.
+
+   Improved test progress indicators.
+
+   Created  popup menus for configuration.
+
+   Added menu for test selection.
+
+   Added CPU and cache identification.
+
+   Added a "bail out" feature to quit the current test when it does not
+   fit the test selection parameters.
+
+   Re-arranged the screen layout and colors.
+
+   Created local include files for I/O and serial interface definitions
+   rather than using the sometimes incompatible system include files. 
+
+   Broke up the "C" source code into four separate source modules.
+
+Enhancements in v1.5
+   Some additional changes were made to fix obscure memory sizing
+   problems.
+
+   The 4 bit wide data pattern was increased to 8 bits since 8 bit
+   wide memory chips are becoming more common.
+
+   A new test algorithm was added to improve detection of data
+   pattern sensitive errors. 
+
+
+Enhancements in v1.4
+   Changes to the memory sizing code to avoid problems with some
+   motherboards where memtest would find more memory than actually
+   exists.
+
+   Added support for a console serial port. (thanks to Doug Sisk)
+
+   On-line commands are now available for configuring Memtest86 on
+   the fly (see On-line Commands).
+	
+
+Enhancements in v1.3
+   Scrolling of memory errors is now provided.  Previously, only one screen
+   of error information was displayed.
+
+   Memtest86 can now be booted from any disk via lilo.
+
+   Testing of up to 4gb of memory has been fixed is now enabled by default.
+   This capability was clearly broken in v1.2a and should work correctly
+   now but has not been fully tested (4gb PC's are a bit rare).
+
+   The maximum memory size supported by the motherboard is now being
+   calculated correctly.  In previous versions there were cases where not
+   all of memory would be tested and the maximum memory size supported
+   was incorrect.
+
+   For some types of failures the good and bad values were reported to be
+   same with an Xor value of 0.  This has been fixed by retaining the data
+   read from memory and not re-reading the bad data in the error reporting
+   routine.
+
+   APM (advanced power management) is now disabled by Memtest86.  This
+   keeps the screen from blanking while the test is running.
+
+   Problems with enabling & disabling cache on some motherboards have been
+   corrected.
+
+
+17) Acknowledgments
+===================
+Memtest86 was developed by Chris Brady with the resources and assistance
+listed below:
+
+- The initial versions of the source files bootsect.S, setup.S, head.S and
+  build.c are from the Linux 1.2.1 kernel and have been heavily modified.
+
+- Doug Sisk provided code to support a console connected via a serial port.
+
+- Code to create BadRAM patterns was provided by Rick van Rein.
+
+- Tests 5 and 8 are based on Robert Redelmeier's burnBX test.
+
+- Screen buffer code was provided by Jani Averbach.
+
+- Eric Biederman provided all of the feature content for version 3.0
+  plus many bugfixes and significant code cleanup.
+
+- Major enhancements to hardware detection and reporting in version 3.2,
+  3.3 pnd 3.4 rovided by Samuel Demeulemeester (from Memtest86+ v1.11, v1.60
+  and v1.70).
diff --git a/README.background b/README.background
new file mode 100644
index 0000000..9c35250
--- /dev/null
+++ b/README.background
@@ -0,0 +1,156 @@
+                       The Anatomy & Physiology of Memtest86-SMP
+                       -----------------------------------------
+
+1. Binary layout
+                                            
+       ---------------------------------------------------------------
+       | bootsect.o      | setup.o          | head.o memtest_shared  |
+       ---------------------------------------------------------------
+Labels                                _start<-------memtest---------->_end
+       -----------------------------------------------------------
+addr   0               512        512+4*512 |
+       -----------------------------------------------------------
+
+2. The following steps occur after we power on.
+   a. The bootsect.o code gets loaded at 0x7c00 
+      and copies 
+      i.   itself to 0x90000
+      ii.  setup.o to 0x90200
+      iii. everything between _start and _end i.e memtest 
+           to 0x10000
+   b. jumps somewhere into the copied bootsect.o code at 0x90000
+      ,does some trivial stuff and jumps to setup.o
+   c. setup.o puts the processor in protected mode, with a basic
+      gdt and idt and does a long jump to the start of the 
+      memtest code (startup_32, see 4 below). The code and data 
+      segment base address are all set to 0x0. So a linear 
+      address range and no paging is enabled.
+   d. From now on we no longer required the bootsect.o and setup.o 
+      code.
+3. The code in memtest is compiled as position independent
+   code. Which implies that the code can be moved dynamically in 
+   the address space and can still work. Since we are now in head.o,
+   which is compiled with PIC , we no longer should use absolute 
+   addresses references while accessing functions or globals.  
+   All symbols are stored in a table called Global Offset Table(GOT)
+   and %ebx is set to point to the base of that table. So to get/set 
+   the value of a symbol we need to read (%ebx + symbolOffsetIntoGOT) to 
+   get the symbol value. For eg. if foo is global varible the assembly
+   code to store %eax value into foo will be changed from
+                    mov %eax, foo
+                        to 
+                    mov %eax, foo@GOTOFF(%ebx)
+4. (startup_32) The first step done in head.o is to change   
+   the gdtr and idtr register values to point to the final(!) 
+   gdt and ldt tables in head.o, since we can no longer use the 
+   gdt and ldt tables in setup.o, and call the dynamic linker 
+   stub in memtest_shared (see call _dl_start in head.S). This 
+   dynamic linker stub relocates all the code in memtest w.r.t 
+   the new base location i.e 0x1000. Finally we call the test_start() 
+   'C' routine.
+5. The test_start() C routine is the main routine which lets the BSP 
+   bring up the APs from their halt state, relocate the code 
+   (if necessary) to new address, move the APs to the newly 
+   relocated address and execute the tests. The BSP is the
+   master which controls the execution of the APs, and mostly
+   it is the one which manupulates the global variables. 
+   i.  we change the stack to a private per cpu stack.
+       (this step happens every time we move to a new location)
+   ii. We kick start the APs in the system by
+      a. Putting a temporary real mode code 
+         (_ap_trampoline_start - _ap_trampoline_protmode) 
+         at 0x9000, which puts the AP in protected mode and jumps 
+         to _ap_trampoline_protmode in head.o. The code in 
+         _ap_trampoline_protmode calls start_32 in head.o which 
+         reinitialises the AP's gdt and idt to point to the
+         final(!) gdt and idt. (see step 4 above)
+      b. Since the APs also traverse through the same initialisation
+         code(startup_32 in head.o), the APs also call test_start().
+         The APs just spin wait (see AP_SpinWaitStart) till the 
+         are instructed by the BSP to jump to a new location, 
+         which can either be a test execution or spin wait at a 
+         new location.
+  iii. The base address at which memtest tries to execute as far
+       as possible is 0x2000. This is the lowest possible address
+       memtest can put itself at. So the next step is to 
+       move to 0x2000, which it cannot directly, since copying
+       code to 0x2000 will override the existing code at 0x1000.
+       0x2000 +sizeof(memtest) will usually be greater than 0x1000.
+       so we temporarily relocated to 0x200000 and then relocate
+       back to 0x2000. Every time the BSP relocates the code to the
+       new location, it pulls up the APs spin waiting at the old 
+       location to spin wait at the corresponding relocated 
+       spin wait location, by making them jump to the new
+       statup_32 relocated location(see 4 above).
+       Hence forth during the tests 0x200000 is the only place 
+       we relocate to if we need to test a memory window 
+       (see v. below to get a description of what a window is)
+       which includes address range 0x2000.
+
+   Address map during normal execution.
+       --------------------------------------------------------------------
+             | head.o memtest_shared  |                                   |RAM_END
+       --------------------------------------------------------------------
+Labels _start<-------memtest---------->_end
+       --------------------------------------------------------------------
+addr   0x0   0x2000                   | Memory that is being tested..     |RAM_END
+       --------------------------------------------------------------------
+
+   Address map during relocated state.
+       --------------------------------------------------------------------
+                                      | head.o memtest_shared  |          |RAM_END
+       --------------------------------------------------------------------
+Labels                          _start<-------memtest---------->_end
+       --------------------------------------------------------------------
+addr   memory that is being tested... |0x200000                 |         |RAM_END
+       --------------------------------------------------------------------
+
+   iv. Once we are at 0x2000 we initialise the system, and 
+       determine the memory map ,usually via the bios e820 map. 
+       The sorted, and non-overlapping RAM page ranges are 
+       placed in v->pmap[] array. This array is the reference 
+       of the RAM memory map on the system. 
+    v. The memory range(in page numbers) which the 
+       memtest86 can test is partitioned into windows.
+       the current version of memtest86-smp has the capability
+       to test the memory from 0x0 - 0xFFFFFFFFF (max address
+       when pae mode is enabled). 
+       We then compute the linear memory address ranges(called
+       segments) for the window we are currently about to 
+       test. The windows are
+          a. 0  - 640K 
+          b. (0x2000 + (_end - _start))  - 4G (since the code is at 0x2000).
+          c. >4G to test pae address range, each window with size 
+             of 0x80000(2G), large enough to be mapped in one page directory
+             entry. So a window size of 0x80000 means we can map 1024 page 
+             table entries, with page size of 2M(pae mode), with one
+             page directory entry. Something similar to kseg entry
+             in linux. The upper bound page number is 0x1000000 which
+             corresponds to linear address 0xFFFFFFFFF + 1 which uses
+             all the 36 address bits. 
+       Each window is compared against the sorted & non-overlapping 
+       e820 map which we have stored in v->pmap[] array, since all 
+       memory in the selected window address range may correspond to
+       RAM or can be usable. A list of segments within the window is
+       created , which contain the usable portions of the window. 
+       This is stored in v->mmap[] array.
+   vi. Once the v->mmap[] array populated, we have the list of 
+       non-overlapping segments in the current window which are the
+       final address ranges that can be tested. The BSP executes the
+       test first and lets each AP execute the test one by one. Once
+       all the APs finish execting the same test, the BSP moves to the
+       next window follows the same procedure till all the windows 
+       are done. Once all the windows are done, the BSP moves to the
+       next test. Before executing in any window the BSP checks if
+       the window overlaps with the code/data of memtest86, if so
+       tries to relocate to 0x200000. If the window includes both 
+       0x2000 as well as 0x200000  the BSP skips that window.
+       Looking at the window values the only time the memtest
+       relocates is when testing the 0 - 640K window.
+
+Known Issues:
+* Memtest86-smp does not work on IBM-NUMA machines, x440 and friends.
+
+email comments to:
+Kalyan Rajasekharuni<kc_rajasekharuni@yahoo.com>
+Sub: Memtest86-SMP
diff --git a/README.build-process b/README.build-process
new file mode 100644
index 0000000..19edfcf
--- /dev/null
+++ b/README.build-process
@@ -0,0 +1,39 @@
+During memory testing memtest86 relocates itself in memory so it can test the
+memory it was previously running from.  memtest86 is compiled as position mostly
+independent code.  Some relocations records must be processed to achieve the
+affect of position independent code.  A 16 bit loader is prepended to memtest86
+so it can be loaded from a floppy, or from lilo.
+
+In restructuring the build process I had several goals.  Maintainability and
+comprehsibility of the build process.  Simplicity of the toolset. And the
+ability to build images bootable by both the legacy x86 bootloader,
+and images bootable by bootloaders that directly load static ELF images. 
+
+With the ability to proecess relocation records, memtest.bin has been
+reduced in size from 84480 bytes to 49308 bytes.  And now only requires one copy
+of memtest86.  A reduction in size of 35K.  And the build process can now ignore
+the size of memtest86.
+
+BIOS calls have been moved from setup.S to head.S making bootsect.S and
+setup.S exclusively for booting.
+
+memtest86 is built in three stages.  In the first stage the relocatable object
+files are built as with any program.  In the second stage the relocatable object
+files are linked together into memtest_shared, a shared library version
+of memtest86.  In the third stage a raw memory image of memtest_shared is formed
+and linked into memtest.bin, and memtest.
+
+memtest.bin is the floppy/lilo bootable target.
+
+memtest is the ELF bootable target.
+
+Another major change is now data in the bss segment is also preserved
+when memtest86 is relocated, and memtest86 can be relocated to any address. 
+
+The one thing to watch out for is pointers to data inside of memtest86.  Except
+for constant pointers to static data there is not enough information to generate
+relocation records for pointers so they will not change when memtest86 is
+relocated, which might lead to nasty surpises.
+
+Eric Biederman <ebiederman@lnxi.com>
+
diff --git a/bootsect.S b/bootsect.S
new file mode 100644
index 0000000..96aa22b
--- /dev/null
+++ b/bootsect.S
@@ -0,0 +1,383 @@
+/*

+ *	 bootsect.s		Copyright (C) 1991, 1992 Linus Torvalds

+ *

+ * bootsect.s is loaded at 0x7c00 by the bios-startup routines, and moves

+ * itself out of the way to address 0x90000, and jumps there.

+ *

+ * It then loads 'setup' directly after itself (0x90200), and the system

+ * at 0x10000, using BIOS interrupts.

+ *

+ * The loader has been made as simple as possible, and continuos

+ * read errors will result in a unbreakable loop. Reboot by hand. It

+ * loads pretty fast by getting whole tracks at a time whenever possible.

+ *

+ * 1-Jan-96 Modified by Chris Brady for use as a boot loader for MemTest-86.

+ */

+

+#include "defs.h"

+

+ROOT_DEV = 0

+

+.code16

+.section ".bootsect", "ax", @progbits

+_boot:

+

+

+# ld86 requires an entry symbol. This may as well be the usual one.

+.globl	_main

+_main:

+	movw	$BOOTSEG, %ax

+	movw	%ax, %ds

+	movw	$INITSEG, %ax

+	movw	%ax, %es

+	movw	$256, %cx

+	subw	%si, %si

+	subw	%di, %di

+	cld

+	rep

+	movsw

+	ljmp	$INITSEG, $go - _boot

+

+go:

+	movw	%cs, %ax

+	movw	$(0x4000-12), %dx	# 0x4000 is arbitrary value >= length of

+					# bootsect + length of setup + room for stack

+					# 12 is disk parm size

+

+# bde - changed 0xff00 to 0x4000 to use debugger at 0x6400 up (bde).  We

+# wouldn't have to worry about this if we checked the top of memory.  Also

+# my BIOS can be configured to put the wini drive tables in high memory

+# instead of in the vector table.  The old stack might have clobbered the

+# drive table.

+

+	movw	%ax, %ds

+	movw	%ax, %es

+	movw	%ax, %ss		# put stack at INITSEG:0x4000-12.

+	movw	%dx, %sp

+

+/*

+ *	Many BIOS's default disk parameter tables will not

+ *	recognize multi-sector reads beyond the maximum sector number

+ *	specified in the default diskette parameter tables - this may

+ *	mean 7 sectors in some cases.

+ *

+ *	Since single sector reads are slow and out of the question,

+ *	we must take care of this by creating new parameter tables

+ *	(for the first disk) in RAM.  We will set the maximum sector

+ *	count to 18 - the most we will encounter on an HD 1.44.

+ *

+ *	High doesn't hurt.  Low does.

+ *

+ *	Segments are as follows: ds=es=ss=cs - INITSEG,

+ *		fs = 0, gs = parameter table segment

+ */

+	pushw	$0

+	popw	%fs

+	movw	$0x78, %bx		# fs:bx is parameter table address

+	lgs	%fs:(%bx),%si		# gs:si is source

+

+	movw	%dx, %di		# es:di is destination

+	movw	$6, %cx			# copy 12 bytes

+	cld

+

+	rep	movsw %gs:(%si), (%di)

+

+	movw	%dx, %di

+	movb	$18, 4(%di)		# patch sector count

+

+	movw	%di, %fs:(%bx)

+	movw	%es, %fs:2(%bx)

+

+	movw	%cs, %ax

+	movw	%ax, %fs

+	movw	%ax, %gs

+

+	xorb	%ah, %ah		# reset FDC

+	xorb	%dl, %dl

+	int	$0x13

+

+# load the setup-sectors directly after the bootblock.

+# Note that 'es' is already set up.

+

+load_setup:

+	xorw	%dx, %dx			# drive 0, head 0

+	movw	$0x0002, %cx			# sector 2, track 0

+	movw	$0x0200, %bx			# address = 512, in INITSEG

+	movw	$(0x0200 + SETUPSECS), %ax	# service 2, nr of sectors

+						# (assume all on head 0, track 0)

+	int	$0x13				# read it

+	jnc	ok_load_setup			# ok - continue

+

+	pushw	%ax			# dump error code

+	call	print_nl

+	movw	%sp, %bp

+	call	print_hex

+	popw	%ax

+

+	xorb	%dl, %dl		# reset FDC

+	xorb	%ah, %ah

+	int	$0x13

+	jmp	load_setup

+

+ok_load_setup:

+

+# Get disk drive parameters, specifically nr of sectors/track

+

+

+/* It seems that there is no BIOS call to get the number of sectors.  Guess

+ * 18 sectors if sector 18 can be read, 15 if sector 15 can be read.

+ * Otherwise guess 9

+ */

+

+	xorw	%dx, %dx			# drive 0, head 0

+	movw	$0x0012, %cx			# sector 18, track 0

+	movw	$(0x200+(SETUPSECS*0x200)), %bx	# address after setup (es = cs)

+	movw	$0x0201, %ax			# service 2, 1 sector

+	int	$0x13

+	jnc	got_sectors

+	movb	$0x0f, %cl			# sector 15

+	movw	$0x0201, %ax			# service 2, 1 sector

+	int	$0x13

+	jnc	got_sectors

+	movb	$0x09, %cl

+

+got_sectors:

+	movw	%cx, %cs:sectors - _boot

+	movw	$INITSEG, %ax

+	movw	%ax, %es

+

+# Print some inane message

+

+	movb	$0x03, %ah		# read cursor pos

+	xorb	%bh, %bh

+	int	$0x10

+

+	movw	$9, %cx

+	movw	$0x0007, %bx		# page 0, attribute 7 (normal)

+	movw	$msg1 - _boot, %bp

+	movw	$0x1301, %ax		# write string, move cursor

+	int	$0x10

+

+# ok, we've written the message, now

+# we want to load the system (at 0x10000)

+

+	movw	$TSTLOAD, %ax

+	movw	%ax, %es		# segment of 0x010000

+	call	read_it

+	call	kill_motor

+	call  turnoffcursor

+	call	print_nl

+

+# after that (everyting loaded), we jump to

+# the setup-routine loaded directly after

+# the bootblock:

+

+	ljmp	$SETUPSEG,$0

+

+# This routine loads the system at address 0x10000, making sure

+# no 64kB boundaries are crossed. We try to load it as fast as

+# possible, loading whole tracks whenever we can.

+#

+# in:	es - starting address segment (normally 0x1000)

+#

+sread:	.word 1+SETUPSECS	# sectors read of current track

+head:	.word 0			# current head

+track:	.word 0			# current track

+

+read_it:

+	movw	%es, %ax

+	testw	$0x0fff, %ax

+die:

+	jne	die		# es must be at 64kB boundary

+	xorw	%bx,%bx		# bx is starting address within segment

+rp_read:

+	movw	%es, %ax

+	subw	$TSTLOAD, %ax	# have we loaded all yet?

+	cmpw	syssize - _boot, %ax

+	jbe	ok1_read

+	ret

+ok1_read:

+	movw	%cs:sectors - _boot, %ax

+	subw	sread - _boot, %ax

+	movw	%ax, %cx

+	shlw	$9, %cx

+	addw	%bx, %cx

+	jnc	ok2_read

+	je	ok2_read

+	xorw	%ax, %ax

+	subw	%bx, %ax

+	shrw	$9, %ax

+ok2_read:

+	call	read_track

+	movw	%ax, %cx

+	add	sread - _boot, %ax

+	cmpw	%cs:sectors - _boot, %ax

+	jne	ok3_read

+	movw	$1, %ax

+	subw	head - _boot, %ax

+	jne	ok4_read

+	incw	track - _boot

+ok4_read:

+	movw	%ax, head - _boot

+	xorw	%ax, %ax

+ok3_read:

+	movw	%ax, sread - _boot

+	shlw	$9, %cx

+	addw	%cx, %bx

+	jnc	rp_read

+	movw	%es, %ax

+	addb	$0x10, %ah

+	movw	%ax, %es

+	xorw	%bx, %bx

+	jmp	rp_read

+

+read_track:

+	pusha

+	pusha

+	movw	$0xe2e, %ax 	# loading... message 2e = .

+	movw	$7, %bx

+	int	$0x10

+	popa

+

+	movw	track - _boot, %dx

+	movw	sread - _boot, %cx

+	incw	%cx

+	movb	%dl, %ch

+	movw	head - _boot, %dx

+	movb	%dl, %dh

+	andw	$0x0100, %dx

+	movb	$2, %ah

+

+	pushw	%dx				# save for error dump

+	pushw	%cx

+	pushw	%bx

+	pushw	%ax

+

+	int	$0x13

+	jc	bad_rt

+	addw	$8, %sp

+	popa

+	ret

+

+bad_rt:

+	pushw	%ax				# save error code

+	call	print_all			# ah = error, al = read

+

+	xorb	%ah, %ah

+	xorb	%dl, %dl

+	int	$0x13

+

+	addw	$10, %sp

+	popa

+	jmp read_track

+

+/*

+ *	print_all is for debugging purposes.

+ *	It will print out all of the registers.  The assumption is that this is

+ *	called from a routine, with a stack frame like

+ *	dx

+ *	cx

+ *	bx

+ *	ax

+ *	error

+ *	ret <- sp

+ *

+*/

+

+print_all:

+	movw	$5, %cx		# error code + 4 registers

+	movw	%sp, %bp

+

+print_loop:

+	pushw	%cx		# save count left

+	call	print_nl	# nl for readability

+

+	cmpb	5, %cl		# see if register name is needed

+	jae	no_reg

+

+	movw	$(0xe05	+ 'A' - 1), %ax

+	subb	%cl, %al

+	int	$0x10

+	movb	$'X', %al

+	int	$0x10

+	movb	$':', %al

+	int	$0x10

+

+no_reg:

+	addw	$2, %bp		# next register

+	call	print_hex	# print it

+	popw	%cx

+	loop	print_loop

+	ret

+

+print_nl:

+	movw	$0xe0d, %ax	# CR

+	int	$0x10

+	movb	$0x0a, %al	# LF

+	int	$0x10

+	ret

+

+/*

+ *	print_hex is for debugging purposes, and prints the word

+ *	pointed to by ss:bp in hexadecmial.

+ */

+

+print_hex:

+	movw	$4, %cx		# 4 hex digits

+	movw	(%bp), %dx	# load word into dx

+

+print_digit:

+	rolw	$4, %dx		# rotate so that lowest 4 bits are used

+	movb	$0xe, %ah

+	movb	%dl, %al	# mask off so we have only next nibble

+	andb	$0xf, %al

+	addb	$'0', %al	# convert to 0-based digit

+	cmpb	$'9', %al	# check for overflow

+	jbe	good_digit

+	addb	$('A' - '0' - 10), %al

+

+good_digit:

+	int	$0x10

+	loop	print_digit

+	ret

+

+

+/*

+ * This procedure turns off the floppy drive motor, so

+ * that we enter the kernel in a known state, and

+ * don't have to worry about it later.

+ */

+kill_motor:

+	pushw	%dx

+	movw	$0x3f2, %dx

+	xorb	%al, %al

+	outb	%al, %dx

+	popw	%dx

+	ret

+

+turnoffcursor:

+  movb  $0x01, %ah      # turn off the cursor

+  movb  $0x00, %bh

+  movw  $0x2000, %cx

+  int   $0x10 

+	ret

+	

+sectors:

+	.word 0

+

+msg1:

+	.byte 13,10

+	.ascii "Loading"

+

+.org 497

+setup_sects:

+	.byte SETUPSECS

+.org 500

+syssize:

+	.word _syssize

+.org 508

+root_dev:

+	.word ROOT_DEV

+boot_flag:

+	.word 0xAA55

+_eboot:

diff --git a/build-number.txt b/build-number.txt
new file mode 100644
index 0000000..7f8f011
--- /dev/null
+++ b/build-number.txt
@@ -0,0 +1 @@
+7
diff --git a/build.number b/build.number
new file mode 100644
index 0000000..e110e8d
--- /dev/null
+++ b/build.number
@@ -0,0 +1 @@
+617
diff --git a/build_number.h b/build_number.h
new file mode 100644
index 0000000..d1f2abc
--- /dev/null
+++ b/build_number.h
@@ -0,0 +1,9 @@
+#ifndef BUILD_NUMBER_STR
+#define BUILD_NUMBER_STR "617"
+#endif
+#ifndef VERSION_STR
+#define VERSION_STR "4.99617 - Thu May  3 11:45:57 CEST 2012"
+#endif
+#ifndef VERSION_STR_SHORT
+#define VERSION_STR_SHORT "4.99617"
+#endif
diff --git a/buildnumber.mak b/buildnumber.mak
new file mode 100644
index 0000000..8d5b85d
--- /dev/null
+++ b/buildnumber.mak
@@ -0,0 +1,10 @@
+# Create an auto-incrementing build number.
+
+BUILD_NUMBER_LDFLAGS  = -Xlinker --defsym -Xlinker __BUILD_DATE=$$(date +'%Y%m%d')
+BUILD_NUMBER_LDFLAGS += -Xlinker --defsym -Xlinker __BUILD_NUMBER=$$(cat $(BUILD_NUMBER_FILE))
+
+# Build number file.  Increment if any object file changes.
+$(BUILD_NUMBER_FILE): $(OBJS)
+	@if ! test -f $(BUILD_NUMBER_FILE); then echo 0 > $(BUILD_NUMBER_FILE); fi
+	@echo $$(($$(cat $(BUILD_NUMBER_FILE)) + 1)) > $(BUILD_NUMBER_FILE)
+
diff --git a/changelog b/changelog
new file mode 100644
index 0000000..9379a5b
--- /dev/null
+++ b/changelog
@@ -0,0 +1,25 @@
+Memtest86+ V5.01 changelog

+----------------------------

+

+	- Added support for up to 2 TB of RAM on X64 CPUs

+	- Added experimental SMT support up to 32 cores

+	- Added complete detection for memory controllers.

+	- Added Motherboard Manufacturer & Model reporting

+	- Added CPU temperature reporting

+	- Added enhanced Fail Safe Mode (Press F1 at startup)

+	- Added support for Intel "Sandy Bridge-E" CPUs

+	- Added support for Intel "Ivy Bridge" CPUs

+	- Added preliminary support for Intel "Haswell" CPUs

+	- Added preliminary support for Intel "Haswell-ULT" CPUs

+	- Added support for AMD "Kabini" (K16) CPUs

+	- Added support for AMD "Bulldozer" CPUs

+	- Added support for AMD "Trinity" CPUs

+	- Added support for AMD E-/C-/G-/Z- "Bobcat" CPUs

+	- Added support for Intel Atom "Pineview" CPUs

+	- Added support for Intel Atom "Cedar Trail" CPUs

+	- Added SPD detection on most AMD Chipsets

+	- Enforced Coreboot support

+	- Optimized run time for faster memory error detection

+	- Rewriten lots of memory timings detection code

+	- Corrected bugs, bugs and more bugs

+	

diff --git a/config.c b/config.c
new file mode 100644
index 0000000..18d8887
--- /dev/null
+++ b/config.c
@@ -0,0 +1,531 @@
+/* config.c - MemTest-86  Version 3.4
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ * ----------------------------------------------------
+ * MemTest86+ V5.00 Specific code (GPL V2.0)
+ * By Samuel DEMEULEMEESTER, sdemeule@memtest.org
+ * http://www.x86-secret.com - http://www.memtest.org
+ */
+#include "test.h"
+#include "screen_buffer.h"
+#include "dmi.h"
+
+extern int bail, beepmode;
+extern struct tseq tseq[];
+extern short e820_nr;
+void performance();
+extern volatile short cpu_mode;
+extern volatile int test;
+extern void find_chunks();
+extern volatile short  start_seq;
+extern short restart_flag;
+extern short onepass;
+extern short btflag;
+
+extern void get_list(int x, int y, int len, char *buf);
+
+char save[2][POP_H][POP_W];
+char save2[2][POP2_H][POP2_W];
+
+void get_config()
+{
+	int flag = 0, sflag = 0, i, j, k, n, m, prt = 0;
+  int reprint_screen = 0;
+  char cp[64];
+	ulong page;
+
+	popup();
+	wait_keyup();
+	while(!flag) {
+		cprint(POP_Y+1,  POP_X+2, "Settings:");
+		cprint(POP_Y+3,  POP_X+6, "(1) Test Selection");
+		cprint(POP_Y+4,  POP_X+6, "(2) Address Range");
+		cprint(POP_Y+5,  POP_X+6, "(3) Error Report Mode");
+		cprint(POP_Y+6,  POP_X+6, "(4) Core Selection");
+		cprint(POP_Y+7,  POP_X+6, "(5) Refresh Screen");
+		cprint(POP_Y+8,  POP_X+6, "(6) Display DMI Data");
+		cprint(POP_Y+9,  POP_X+6, "(7) Display SPD Data");
+		cprint(POP_Y+11, POP_X+6,	"(0) Continue");
+
+		/* Wait for key release */
+		/* Fooey! This nuts'es up the serial input. */
+		sflag = 0;
+		switch(get_key()) {
+		case 2:
+			/* 1 - Test Selection */
+			popclear();
+			cprint(POP_Y+1, POP_X+2, "Test Selection:");
+			cprint(POP_Y+3, POP_X+6, "(1) Default Tests");
+			cprint(POP_Y+4, POP_X+6, "(2) Skip Current Test");
+			cprint(POP_Y+5, POP_X+6, "(3) Select Test");
+		  cprint(POP_Y+6, POP_X+6, "(4) Enter Test List");
+			cprint(POP_Y+7, POP_X+6, "(0) Cancel");
+			if (v->testsel < 0) {
+				cprint(POP_Y+3, POP_X+5, ">");
+			} else {
+				cprint(POP_Y+5, POP_X+5, ">");
+			}
+			wait_keyup();
+			while (!sflag) {
+				switch(get_key()) {
+				case 2:
+					/* Default - All tests */
+					i = 0;
+					while (tseq[i].cpu_sel) {
+					    tseq[i].sel = 1;
+					    i++;
+					}
+					find_ticks_for_pass();
+					sflag++;
+					break;
+				case 3:
+					/* Skip test */
+					bail++;
+					sflag++;
+					break;
+				case 4:
+					/* Select test */
+					popclear();
+					cprint(POP_Y+1, POP_X+3,
+						"Test Selection:");
+					cprint(POP_Y+4, POP_X+5,
+						"Test Number [1-11]: ");
+					n = getval(POP_Y+4, POP_X+24, 0) - 1;
+					if (n <= 11) 
+						{
+					    /* Deselect all tests */
+					    i = 0;
+					    while (tseq[i].cpu_sel) {
+					        tseq[i].sel = 0;
+					        i++;
+					    }
+					    /* Now set the selection */
+					    tseq[n].sel = 1;
+					    v->pass = -1;
+					    test = n;
+					    find_ticks_for_pass();
+					    sflag++;
+          		bail++;
+						}
+					break;
+				case 5:
+					/* Enter a test list */
+					popclear();
+					cprint(POP_Y+1, POP_X+3,
+				"Enter a comma separated list");
+					cprint(POP_Y+2, POP_X+3,
+				"of tests to execute:");
+					cprint(POP_Y+5, POP_X+5, "List: ");
+					/* Deselect all tests */
+					k = 0;
+					while (tseq[k].cpu_sel) {
+					    tseq[k].sel = 0;
+					    k++;
+					}
+
+					/* Get the list */
+					for (i=0; i<64; i++) cp[i] = 0;
+					get_list(POP_Y+5, POP_X+10, 64, cp);
+
+					/* Now enable all of the tests in the
+					 * list */
+					i = j = m = 0;
+					while (1) {
+					    if (isdigit(cp[i])) {
+						n = cp[i]-'0';
+						j = j*10 + n;
+						i++;
+						if (cp[i] == ',' || cp[i] == 0){
+						    if (j < k) {
+							tseq[j].sel = 1;
+							m++;
+						    }
+						    if (cp[i] == 0) break;
+						    j = 0;
+						    i++;
+						}
+					    }
+					}
+
+					/* If we didn't select at least one
+					 * test turn them all back on */
+					if (m == 0) {
+					    k = 0;
+					    while (tseq[k].cpu_sel) {
+					        tseq[k].sel = 1;
+					        k++;
+					    }
+					}
+					v->pass = -1;
+					test = n;
+					find_ticks_for_pass();
+					sflag++;
+          bail++;
+					break;
+				case 11:
+				case 57:
+					sflag++;
+					break;
+				}
+			}
+			popclear();
+			break;
+		case 3:
+			/* 2 - Address Range */
+			popclear();
+			cprint(POP_Y+1, POP_X+2, "Test Address Range:");
+			cprint(POP_Y+3, POP_X+6, "(1) Set Lower Limit");
+			cprint(POP_Y+4, POP_X+6, "(2) Set Upper Limit");
+			cprint(POP_Y+5, POP_X+6, "(3) Test All Memory");
+			cprint(POP_Y+6, POP_X+6, "(0) Cancel");
+			wait_keyup();
+			while (!sflag) {
+				switch(get_key()) {
+				case 2:
+					/* Lower Limit */
+					popclear();
+					cprint(POP_Y+2, POP_X+4,
+						"Lower Limit: ");
+					cprint(POP_Y+4, POP_X+4,
+						"Current: ");
+					aprint(POP_Y+4, POP_X+13, v->plim_lower);
+					cprint(POP_Y+6, POP_X+4,
+						"New: ");
+					page = getval(POP_Y+6, POP_X+9, 12);
+					if (page + 1 <= v->plim_upper) {
+						v->plim_lower = page;
+						test--;
+						bail++;
+					}
+					adj_mem();
+					find_chunks();
+					find_ticks_for_pass();
+					sflag++;
+					break;
+				case 3:
+					/* Upper Limit */
+					popclear();
+					cprint(POP_Y+2, POP_X+4,
+						"Upper Limit: ");
+					cprint(POP_Y+4, POP_X+4,
+						"Current: ");
+					aprint(POP_Y+4, POP_X+13, v->plim_upper);
+					cprint(POP_Y+6, POP_X+4,
+						"New: ");
+					page = getval(POP_Y+6, POP_X+9, 12);
+					if  (page - 1 >= v->plim_lower) {
+						v->plim_upper = page;
+						bail++;
+						test--;
+					}
+					adj_mem();
+					find_chunks();
+					find_ticks_for_pass();
+					sflag++;
+					break;
+				case 4:
+					/* All of memory */
+					v->plim_lower = 0;
+					v->plim_upper =
+						v->pmap[v->msegs - 1].end;
+					test--;
+					bail++;
+					adj_mem();
+					find_chunks();
+					find_ticks_for_pass();
+					sflag++;
+					break;
+				case 11:
+				case 57:
+					/* 0/CR - Continue */
+					sflag++;
+					break;
+				}
+			}
+			popclear();
+			break;
+		case 4:
+			/* Error Mode */
+			popclear();
+			cprint(POP_Y+1, POP_X+2, "Printing Mode:");
+			cprint(POP_Y+3, POP_X+6, "(1) Error Summary");
+			cprint(POP_Y+4, POP_X+6, "(2) Individual Errors");
+			cprint(POP_Y+5, POP_X+6, "(3) BadRAM Patterns");
+			cprint(POP_Y+6, POP_X+6, "(4) Error Counts Only");
+			cprint(POP_Y+7, POP_X+6, "(5) Beep on Error");
+			cprint(POP_Y+8, POP_X+6, "(0) Cancel");
+			cprint(POP_Y+3+v->printmode, POP_X+5, ">");
+			if (beepmode) { cprint(POP_Y+7, POP_X+5, ">"); }
+			wait_keyup();
+			while (!sflag) {
+				switch(get_key()) {
+				case 2:
+					/* Error Summary */
+					v->printmode=PRINTMODE_SUMMARY;
+					v->erri.eadr = 0;
+					v->erri.hdr_flag = 0;
+					sflag++;
+					break;
+				case 3:
+					/* Separate Addresses */
+					v->printmode=PRINTMODE_ADDRESSES;
+					v->erri.eadr = 0;
+					v->erri.hdr_flag = 0;
+					v->msg_line = LINE_SCROLL-1;
+					sflag++;
+					break;
+				case 4:
+					/* BadRAM Patterns */
+					v->printmode=PRINTMODE_PATTERNS;
+					v->erri.hdr_flag = 0;
+					sflag++;
+					prt++;
+					break;
+				case 5:
+					/* Error Counts Only */
+					v->printmode=PRINTMODE_NONE;
+					v->erri.hdr_flag = 0;
+					sflag++;
+					break;
+				case 6:
+					/* Set Beep On Error mode */
+					beepmode = !beepmode;
+					sflag++;
+					break;						
+				case 11:
+				case 57:
+					/* 0/CR - Continue */
+					sflag++;
+					break;
+				}
+			}
+			popclear();
+			break;
+		case 5:
+    			/* CPU Mode */
+    	reprint_screen = 1;
+			popclear();
+			cprint(POP_Y+1, POP_X+2, "CPU Selection Mode:");
+			cprint(POP_Y+3, POP_X+6, "(1) Parallel (All)");
+			cprint(POP_Y+4, POP_X+6, "(2) Round Robin (RRb)");
+			cprint(POP_Y+5, POP_X+6, "(3) Sequential (Seq)");
+			cprint(POP_Y+6, POP_X+6, "(0) Cancel");
+			cprint(POP_Y+2+cpu_mode, POP_X+5, ">");
+			wait_keyup();
+			while(!sflag) {
+				switch(get_key()) {
+				case 2:
+					if (cpu_mode != CPM_ALL) bail++;
+					cpu_mode = CPM_ALL;
+					sflag++;
+					popdown();
+					cprint(9,34,"All");
+					popup();
+					break;
+				case 3:
+					if (cpu_mode != CPM_RROBIN) bail++;
+					cpu_mode = CPM_RROBIN;
+					sflag++;
+					popdown();
+					cprint(9,34,"RRb");
+					popup();
+					break;
+				case 4:
+					if (cpu_mode != CPM_SEQ) bail++;
+					cpu_mode = CPM_SEQ;
+					sflag++;
+					popdown();
+					cprint(9,34,"Seq");
+					popup();
+					break;
+				case 11:
+				case 57:
+					/* 0/CR - Continue */
+					sflag++;
+					break;
+				}
+			}
+			popclear();
+			break;
+		case 6:
+			reprint_screen = 1;
+			flag++;
+			break;
+		case 7:
+			/* Display DMI Memory Info */
+			pop2up();
+      print_dmi_info();
+			pop2down();			
+			break;
+		case 8:
+			/* Display SPD Data */			
+			popdown();
+			show_spd();
+			popup();
+			sflag++;
+			break;
+		case 11:
+		case 57:
+		case 28:
+			/* 0/CR/SP - Continue */
+			flag++;
+			break;
+		}
+	}
+	popdown();
+	if (prt) {
+		printpatn();
+	}
+        if (reprint_screen){
+            tty_print_screen();
+        }
+}
+
+void popup()
+{
+	int i, j;
+	char *pp;
+	
+	for (i=POP_Y; i<POP_Y + POP_H; i++) { 
+		for (j=POP_X; j<POP_X + POP_W; j++) { 
+			pp = (char *)(SCREEN_ADR + (i * 160) + (j * 2));
+                        save[0][i-POP_Y][j-POP_X] = *pp;  /* Save screen */
+                        set_scrn_buf(i, j, ' ');
+			*pp = ' ';		/* Clear */                        
+			pp++;
+                        save[1][i-POP_Y][j-POP_X] = *pp;
+			*pp = 0x07;		/* Change Background to black */
+		}
+	}
+        tty_print_region(POP_Y, POP_X, POP_Y+POP_H, POP_X+POP_W);
+}
+
+void popdown()
+{
+	int i, j;
+	char *pp;
+	
+	for (i=POP_Y; i<POP_Y + POP_H; i++) { 
+		for (j=POP_X; j<POP_X + POP_W; j++) { 
+			pp = (char *)(SCREEN_ADR + (i * 160) + (j * 2));
+			*pp = save[0][i-POP_Y][j-POP_X]; /* Restore screen */
+                        set_scrn_buf(i, j, save[0][i-POP_Y][j-POP_X]);
+			pp++;
+			*pp = save[1][i-POP_Y][j-POP_X]; /* Restore color */
+		}
+	}
+        tty_print_region(POP_Y, POP_X, POP_Y+POP_H, POP_X+POP_W);
+}
+
+void popclear()
+{
+	int i, j;
+	char *pp;
+	
+	for (i=POP_Y; i<POP_Y + POP_H; i++) { 
+		for (j=POP_X; j<POP_X + POP_W; j++) { 
+			pp = (char *)(SCREEN_ADR + (i * 160) + (j * 2));
+			*pp = ' ';		/* Clear popup */
+                        set_scrn_buf(i, j, ' ');
+			pp++;
+		}
+	}
+        tty_print_region(POP_Y, POP_X, POP_Y+POP_H, POP_X+POP_W);
+}
+
+void pop2up()
+{
+	int i, j;
+	char *pp;
+
+	for (i=POP2_Y; i<POP2_Y + POP2_H; i++) { 
+		for (j=POP2_X; j<POP2_X + POP2_W; j++) { 
+			pp = (char *)(SCREEN_ADR + (i * 160) + (j * 2));
+			save2[0][i-POP2_Y][j-POP2_X] = *pp;  /* Save screen */
+			set_scrn_buf(i, j, ' ');
+			*pp = ' ';		/* Clear */
+			pp++;
+			save2[1][i-POP2_Y][j-POP2_X] = *pp;
+			*pp = 0x07;		/* Change Background to black */
+		}
+	}
+        tty_print_region(POP2_Y, POP2_X, POP2_Y+POP2_H, POP2_X+POP2_W);
+}
+
+void pop2down()
+{
+	int i, j;
+	char *pp;
+
+	for (i=POP2_Y; i<POP2_Y + POP2_H; i++) { 
+		for (j=POP2_X; j<POP2_X + POP2_W; j++) { 
+			pp = (char *)(SCREEN_ADR + (i * 160) + (j * 2));
+			*pp = save2[0][i-POP2_Y][j-POP2_X]; /* Restore screen */
+			set_scrn_buf(i, j, save2[0][i-POP2_Y][j-POP2_X]);
+			pp++;
+			*pp = save2[1][i-POP2_Y][j-POP2_X]; /* Restore color */
+		}
+	}
+        tty_print_region(POP2_Y, POP2_X, POP2_Y+POP2_H, POP2_X+POP2_W);
+}
+
+void pop2clear()
+{
+	int i, j;
+	char *pp;
+
+	for (i=POP2_Y; i<POP2_Y + POP2_H; i++) { 
+		for (j=POP2_X; j<POP2_X + POP2_W; j++) { 
+			pp = (char *)(SCREEN_ADR + (i * 160) + (j * 2));
+			*pp = ' ';		/* Clear popup */
+			set_scrn_buf(i, j, ' ');
+			pp++;
+		}
+	}
+        tty_print_region(POP2_Y, POP2_X, POP2_Y+POP2_H, POP2_X+POP2_W);
+}
+
+
+void adj_mem(void)
+{
+	int i;
+
+	v->selected_pages = 0;
+	for (i=0; i< v->msegs; i++) {
+		/* Segment inside limits ? */
+		if (v->pmap[i].start >= v->plim_lower &&
+				v->pmap[i].end <= v->plim_upper) {
+			v->selected_pages += (v->pmap[i].end - v->pmap[i].start);
+			continue;
+		}
+		/* Segment starts below limit? */
+		if (v->pmap[i].start < v->plim_lower) {
+			/* Also ends below limit? */
+			if (v->pmap[i].end < v->plim_lower) {
+				continue;
+			}
+			
+			/* Ends past upper limit? */
+			if (v->pmap[i].end > v->plim_upper) {
+				v->selected_pages += 
+					v->plim_upper - v->plim_lower;
+			} else {
+				/* Straddles lower limit */
+				v->selected_pages += 
+					(v->pmap[i].end - v->plim_lower);
+			}
+			continue;
+		}
+		/* Segment ends above limit? */
+		if (v->pmap[i].end > v->plim_upper) {
+			/* Also starts above limit? */
+			if (v->pmap[i].start > v->plim_upper) {
+				continue;
+			}
+			/* Straddles upper limit */
+			v->selected_pages += 
+				(v->plim_upper - v->pmap[i].start);
+		}
+	}
+}
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..aa4c9fb
--- /dev/null
+++ b/config.h
@@ -0,0 +1,52 @@
+/*
+ * MemTest86+ V5 Specific code (GPL V2.0)
+ * By Samuel DEMEULEMEESTER, sdemeule@memtest.org
+ * http://www.canardpc.com - http://www.memtest.org
+ * ------------------------------------------------
+ * config.h - MemTest-86  Version 3.3
+ *
+ * Compile time configuration options
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ */
+
+/* CONSERVATIVE_SMP - If set to 0, SMP will be enabled by default */
+/* Might be enabled in future revision after extensive testing */
+/* In all cases, SMP is disabled by defaut on server platform */
+#define CONSERVATIVE_SMP 1
+
+/* BEEP_MODE - Beep on error. Default off, Change to 1 to enable */
+#define BEEP_MODE 0
+
+/* BEEP_END_NO_ERROR - Beep at end of each pass without error. Default off, Change to 1 to enable */
+#define BEEP_END_NO_ERROR 0
+
+/* PARITY_MEM - Enables support for reporting memory parity errors */
+/*	Experimental, normally enabled */
+#define PARITY_MEM
+
+/* SERIAL_CONSOLE_DEFAULT -  The default state of the serial console. */
+/*	This is normally off since it slows down testing.  Change to a 1 */
+/*	to enable. */
+#define SERIAL_CONSOLE_DEFAULT 0
+
+/* SERIAL_TTY - The default serial port to use. 0=ttyS0, 1=ttyS1 */ 
+#define SERIAL_TTY 0
+
+/* SERIAL_BAUD_RATE - Baud rate for the serial console */
+#define SERIAL_BAUD_RATE 9600
+
+/* SCRN_DEBUG - extra check for SCREEN_BUFFER
+ */ 
+/* #define SCRN_DEBUG */
+
+/* APM - Turns off APM at boot time to avoid blanking the screen */
+/*	Normally enabled */
+#define APM_OFF
+
+/* USB_WAR - Enables a workaround for errors caused by BIOS USB keyboard */
+/*	and mouse support*/
+/*	Normally enabled */
+#define USB_WAR
+
diff --git a/controller.c b/controller.c
new file mode 100644
index 0000000..f4f7371
--- /dev/null
+++ b/controller.c
@@ -0,0 +1,4153 @@
+/* 
+ * MemTest86+ V5 Specific code (GPL V2.0)
+ * By Samuel DEMEULEMEESTER, sdemeule@memtest.org
+ * http://www.canardpc.com - http://www.memtest.org
+ */
+
+//#include "defs.h"
+#include "config.h"
+//#include "test.h"
+#include "pci.h"
+#include "controller.h"
+#include "spd.h"
+#include "test.h"
+#include "stdint.h"
+#include "cpuid.h"
+#include "msr.h"
+#include "dmi.h"
+
+int col, col2;
+int nhm_bus = 0x3F;
+	
+extern ulong extclock;
+extern unsigned long imc_type;
+extern struct cpu_ident cpu_id;
+/*
+#define rdmsr(msr,val1,val2) \
+	__asm__ __volatile__("rdmsr" \
+			  : "=a" (val1), "=d" (val2) \
+			  : "c" (msr) : "edi")
+
+#define wrmsr(msr,val1,val2) \
+	__asm__ __volatile__("wrmsr" \
+			  :  \
+			  : "c" (msr), "a" (val1), "d" (val2) : "edi")
+*/
+/* controller ECC capabilities and mode */
+#define __ECC_UNEXPECTED 1      /* Unknown ECC capability present */
+#define __ECC_DETECT     2	/* Can detect ECC errors */
+#define __ECC_CORRECT    4	/* Can correct some ECC errors */
+#define __ECC_SCRUB      8	/* Can scrub corrected ECC errors */
+#define __ECC_CHIPKILL  16	/* Can corrected multi-errors */
+
+#define ECC_UNKNOWN      (~0UL)    /* Unknown error correcting ability/status */
+#define ECC_NONE         0       /* Doesnt support ECC (or is BIOS disabled) */
+#define ECC_RESERVED     __ECC_UNEXPECTED  /* Reserved ECC type */
+#define ECC_DETECT       __ECC_DETECT
+#define ECC_CORRECT      (__ECC_DETECT | __ECC_CORRECT)
+#define ECC_CHIPKILL	 (__ECC_DETECT | __ECC_CORRECT | __ECC_CHIPKILL)
+#define ECC_SCRUB        (__ECC_DETECT | __ECC_CORRECT | __ECC_SCRUB)
+
+
+static struct ecc_info {
+	int index;
+	int poll;
+	unsigned bus;
+	unsigned dev;
+	unsigned fn;
+	unsigned cap;
+	unsigned mode;
+} ctrl =
+{
+	.index = 0,
+	/* I know of no case where the memory controller is not on the
+	 * host bridge, and the host bridge is not on bus 0  device 0
+	 * fn 0.  But just in case leave these as variables.
+	 */
+	.bus = 0,
+	.dev = 0,
+	.fn = 0,
+	/* Properties of the current memory controller */
+	.cap = ECC_UNKNOWN,
+	.mode = ECC_UNKNOWN,
+};
+
+
+void coretemp(void) 
+{
+	unsigned int msrl, msrh;
+	unsigned int tjunc, tabs, tnow;
+	unsigned long rtcr;
+	double amd_raw_temp;
+	
+	// Only enable coretemp if IMC is known
+	if(imc_type == 0) { return; }
+	
+	tnow = 0;
+	
+	// Intel  CPU
+	if(cpu_id.vend_id.char_array[0] == 'G' && cpu_id.max_cpuid >= 6)
+	{
+		if(cpu_id.dts_pmp & 1){
+			rdmsr(MSR_IA32_THERM_STATUS, msrl, msrh);
+			tabs = ((msrl >> 16) & 0x7F);
+			rdmsr(MSR_IA32_TEMPERATURE_TARGET, msrl, msrh);
+			tjunc = ((msrl >> 16) & 0x7F);
+			if(tjunc < 50 || tjunc > 125) { tjunc = 90; } // assume Tjunc = 90°C if boggus value received.
+			tnow = tjunc - tabs;		
+			dprint(LINE_CPU+1, 30, v->check_temp, 3, 0);	
+			v->check_temp = tnow;
+		}
+		return;
+	}
+	
+	// AMD CPU
+	if(cpu_id.vend_id.char_array[0] == 'A' && cpu_id.vers.bits.extendedFamily > 0)
+	{
+		pci_conf_read(0, 24, 3, 0xA4, 4, &rtcr);
+		amd_raw_temp = ((rtcr >> 21) & 0x7FF);
+		v->check_temp = (int)(amd_raw_temp / 8);
+		dprint(LINE_CPU+1, 30, v->check_temp, 3, 0);	
+	}	
+	
+				
+}
+
+void print_cpu_line(float dram_freq, float fsb_freq, int ram_type)
+{
+	int cur_col = COL_SPEC;
+	
+	cprint(LINE_CPU, cur_col, "RAM:                                ");
+	cur_col += 5;
+	dprint(LINE_CPU, cur_col, dram_freq, 4, 1);
+	cur_col += 4;
+	cprint(LINE_CPU, cur_col, "MHz (");
+	cur_col += 5;
+	
+	switch(ram_type)
+	{
+		default:
+		case 1:
+			cprint(LINE_CPU, cur_col, "DDR-");
+			cur_col += 4;
+			break;		
+		case 2:
+			cprint(LINE_CPU, cur_col, "DDR2-");
+			cur_col += 5;
+			break;	
+		case 3:
+			cprint(LINE_CPU, cur_col, "DDR3-");
+			cur_col += 5;
+			break;	
+	}		
+
+	if(dram_freq < 500)
+	{
+		dprint(LINE_CPU, cur_col, dram_freq*2, 3, 0);
+		cur_col += 3;
+	} else {
+		dprint(LINE_CPU, cur_col, dram_freq*2, 4, 0);
+		cur_col += 4;		
+	}
+	cprint(LINE_CPU, cur_col, ")");
+	cur_col++;	
+	
+	if(fsb_freq > 10)
+	{
+		cprint(LINE_CPU, cur_col, " - BCLK: ");
+		cur_col += 9;
+	
+		dprint(LINE_CPU, cur_col, fsb_freq, 3, 0);	
+	}
+	
+}
+
+void print_ram_line(float cas, int rcd, int rp, int ras, int chan)
+{
+	int cur_col = COL_SPEC;
+
+	cprint(LINE_RAM, cur_col, "Timings: CAS                        ");
+	cur_col += 13;	
+
+	// CAS Latency (tCAS)
+	if (cas == 1.5) {
+		cprint(LINE_RAM, cur_col, "1.5"); cur_col += 3;
+	} else if (cas == 2.5) {
+		cprint(LINE_RAM, cur_col, "2.5"); cur_col += 3;
+	} else if (cas < 10) {
+		dprint(LINE_RAM, cur_col, cas, 1, 0); cur_col += 1;
+	} else {
+		dprint(LINE_RAM, cur_col, cas, 2, 0); cur_col += 2;		
+	}
+	cprint(LINE_RAM, cur_col, "-"); cur_col += 1;
+
+	// RAS-To-CAS (tRCD)
+	if (rcd < 10) {
+		dprint(LINE_RAM, cur_col, rcd, 1, 0);
+		cur_col += 1;
+	} else {
+		dprint(LINE_RAM, cur_col, rcd, 2, 0);
+		cur_col += 2;		
+	}
+	cprint(LINE_RAM, cur_col, "-"); cur_col += 1;
+
+	// RAS Precharge (tRP)
+	if (rp < 10) {
+		dprint(LINE_RAM, cur_col, rp, 1, 0);
+		cur_col += 1;
+	} else {
+		dprint(LINE_RAM, cur_col, rp, 2, 0);
+		cur_col += 2;		
+	}
+	cprint(LINE_RAM, cur_col, "-"); cur_col += 1;
+
+	// RAS Active to precharge (tRAS)
+	if (ras < 10) {
+		dprint(LINE_RAM, cur_col, ras, 1, 0);
+		cur_col += 1;
+	} else {
+		dprint(LINE_RAM, cur_col, ras, 2, 0);
+		cur_col += 2;
+	}
+	
+		
+	switch(chan)
+	{
+		case 0:
+			break;
+		case 1:
+			cprint(LINE_RAM, cur_col, " @ 64-bit Mode");
+			break;			
+		case 2: 
+			cprint(LINE_RAM, cur_col, " @ 128-bit Mode");
+			break;			
+		case 3:
+			cprint(LINE_RAM, cur_col, " @ 192-bit Mode");
+			break;	
+		case 4:
+			cprint(LINE_RAM, cur_col, " @ 256-bit Mode");
+			break;	
+	}
+}
+
+static void poll_fsb_nothing(void)
+{
+
+	char *name;
+	
+	/* Print the controller name */
+	name = controllers[ctrl.index].name;
+	cprint(LINE_CPU, COL_SPEC, "Chipset:               ");	
+	cprint(LINE_CPU, COL_SPEC+9, name);
+	return;
+}
+
+static void poll_timings_nothing(void)
+{
+	char *ram_type;
+	
+	/* Print the controller name */
+	ram_type = controllers[ctrl.index].ram_type;
+	cprint(LINE_RAM, COL_SPEC, "RAM Type:              ");
+	cprint(LINE_RAM, COL_SPEC+10, ram_type);
+	return;
+}
+
+static void setup_nothing(void)
+{
+	ctrl.cap = ECC_NONE;
+	ctrl.mode = ECC_NONE;
+}
+
+static void poll_nothing(void)
+{
+/* Code to run when we don't know how, or can't ask the memory
+ * controller about memory errors.
+ */
+	return;
+}
+
+static void setup_wmr(void)
+{
+	ulong dev0;
+		
+	// Activate MMR I/O
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	if (!(dev0 & 0x1)) {
+		pci_conf_write( 0, 0, 0, 0x48, 1, dev0 | 1);
+	}
+
+}
+
+
+static void setup_nhm(void)
+{
+	static float possible_nhm_bus[] = {0xFF, 0x7F, 0x3F};
+	unsigned long did, vid, mc_control, mc_ssrcontrol;
+	int i;
+	
+	//Nehalem supports Scrubbing */
+	ctrl.cap = ECC_SCRUB;
+	ctrl.mode = ECC_NONE;
+
+	/* First, locate the PCI bus where the MCH is located */
+
+	for(i = 0; i < sizeof(possible_nhm_bus); i++) {
+		pci_conf_read( possible_nhm_bus[i], 3, 4, 0x00, 2, &vid);
+		pci_conf_read( possible_nhm_bus[i], 3, 4, 0x02, 2, &did);
+		vid &= 0xFFFF;
+		did &= 0xFF00;
+		if(vid == 0x8086 && did >= 0x2C00) { 
+			nhm_bus = possible_nhm_bus[i]; 
+			}
+}
+
+	/* Now, we have the last IMC bus number in nhm_bus */
+	/* Check for ECC & Scrub */
+	
+	pci_conf_read(nhm_bus, 3, 0, 0x4C, 2, &mc_control);	
+	if((mc_control >> 4) & 1) { 
+		ctrl.mode = ECC_CORRECT; 
+		pci_conf_read(nhm_bus, 3, 2, 0x48, 2, &mc_ssrcontrol);	
+		if(mc_ssrcontrol & 3) { 
+			ctrl.mode = ECC_SCRUB; 
+		}		
+	}
+	
+}
+
+static void setup_nhm32(void)
+{
+	static float possible_nhm_bus[] = {0xFF, 0x7F, 0x3F};
+	unsigned long did, vid, mc_control, mc_ssrcontrol;
+	int i;
+	
+	//Nehalem supports Scrubbing */
+	ctrl.cap = ECC_SCRUB;
+	ctrl.mode = ECC_NONE;
+
+	/* First, locate the PCI bus where the MCH is located */
+	for(i = 0; i < sizeof(possible_nhm_bus); i++) {
+		pci_conf_read( possible_nhm_bus[i], 3, 4, 0x00, 2, &vid);
+		pci_conf_read( possible_nhm_bus[i], 3, 4, 0x02, 2, &did);
+		vid &= 0xFFFF;
+		did &= 0xFF00;
+		if(vid == 0x8086 && did >= 0x2C00) { 
+			nhm_bus = possible_nhm_bus[i]; 
+			}
+	}
+
+	/* Now, we have the last IMC bus number in nhm_bus */
+	/* Check for ECC & Scrub */
+	pci_conf_read(nhm_bus, 3, 0, 0x48, 2, &mc_control);	
+	if((mc_control >> 1) & 1) { 
+		ctrl.mode = ECC_CORRECT; 
+		pci_conf_read(nhm_bus, 3, 2, 0x48, 2, &mc_ssrcontrol);	
+		if(mc_ssrcontrol & 1) { 
+			ctrl.mode = ECC_SCRUB; 
+		}		
+	}
+	
+}
+
+static void setup_amd64(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_CORRECT, ECC_RESERVED, ECC_CHIPKILL };
+	unsigned long nbxcfg;
+	unsigned int mcgsrl;
+	unsigned int mcgsth;
+	unsigned long mcanb;
+	unsigned long dramcl;
+
+	/* All AMD64 support Chipkill */
+	ctrl.cap = ECC_CHIPKILL;
+
+	/* Check First if ECC DRAM Modules are used */
+	pci_conf_read(0, 24, 2, 0x90, 4, &dramcl);
+	
+	if (cpu_id.vers.bits.extendedModel >= 4) {
+		/* NEW K8 0Fh Family 90 nm */
+		
+		if ((dramcl >> 19)&1){
+			/* Fill in the correct memory capabilites */
+			pci_conf_read(0, 24, 3, 0x44, 4, &nbxcfg);
+			ctrl.mode = ddim[(nbxcfg >> 22)&3];
+		} else {
+			ctrl.mode = ECC_NONE;
+		}
+		/* Enable NB ECC Logging by MSR Write */
+		rdmsr(0x017B, mcgsrl, mcgsth);
+		wrmsr(0x017B, 0x10, mcgsth);
+	
+		/* Clear any previous error */
+		pci_conf_read(0, 24, 3, 0x4C, 4, &mcanb);
+		pci_conf_write(0, 24, 3, 0x4C, 4, mcanb & 0x7FFFFFFF );		
+
+	} else { 
+		/* OLD K8 130 nm */
+		
+		if ((dramcl >> 17)&1){
+			/* Fill in the correct memory capabilites */
+			pci_conf_read(0, 24, 3, 0x44, 4, &nbxcfg);
+			ctrl.mode = ddim[(nbxcfg >> 22)&3];
+		} else {
+			ctrl.mode = ECC_NONE;
+		}
+		/* Enable NB ECC Logging by MSR Write */
+		rdmsr(0x017B, mcgsrl, mcgsth);
+		wrmsr(0x017B, 0x10, mcgsth);
+	
+		/* Clear any previous error */
+		pci_conf_read(0, 24, 3, 0x4C, 4, &mcanb);
+		pci_conf_write(0, 24, 3, 0x4C, 4, mcanb & 0x7F801EFC );
+	}
+}
+
+static void setup_k10(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_CORRECT, ECC_CHIPKILL, ECC_CHIPKILL };
+	unsigned long nbxcfg;
+	unsigned int mcgsrl;
+	unsigned int mcgsth;
+	unsigned long mcanb;
+	unsigned long dramcl;
+	ulong msr_low, msr_high;
+
+	// All AMD64 support Chipkill */
+	ctrl.cap = ECC_CHIPKILL;
+
+	// Check First if ECC DRAM Modules are used */
+	pci_conf_read(0, 24, 2, 0x90, 4, &dramcl);
+	
+		if ((dramcl >> 19)&1){
+			// Fill in the correct memory capabilites */
+			pci_conf_read(0, 24, 3, 0x44, 4, &nbxcfg);
+			ctrl.mode = ddim[(nbxcfg >> 22)&3];
+		} else {
+			ctrl.mode = ECC_NONE;
+		}
+		// Enable NB ECC Logging by MSR Write */
+		rdmsr(0x017B, mcgsrl, mcgsth);
+		wrmsr(0x017B, 0x10, mcgsth);
+	
+		// Clear any previous error */
+		pci_conf_read(0, 24, 3, 0x4C, 4, &mcanb);
+		pci_conf_write(0, 24, 3, 0x4C, 4, mcanb & 0x7FFFFFFF );	
+		
+		/* Enable ECS */
+		rdmsr(0xC001001F, msr_low,  msr_high);
+		wrmsr(0xC001001F, msr_low, (msr_high | 0x4000));
+		rdmsr(0xC001001F, msr_low,  msr_high);
+
+}
+
+static void setup_apu(void)
+{
+
+	ulong msr_low, msr_high;
+	
+	/* Enable ECS */
+	rdmsr(0xC001001F, msr_low,  msr_high);
+	wrmsr(0xC001001F, msr_low, (msr_high | 0x4000));
+	rdmsr(0xC001001F, msr_low,  msr_high);
+
+}
+
+/*
+static void poll_amd64(void)
+{
+
+	unsigned long mcanb;
+	unsigned long page, offset;
+	unsigned long celog_syndrome;
+	unsigned long mcanb_add;
+
+	pci_conf_read(0, 24, 3, 0x4C, 4, &mcanb);
+
+	if (((mcanb >> 31)&1) && ((mcanb >> 14)&1)) {
+		// Find out about the first correctable error 
+		// Syndrome code -> bits use a complex matrix. Will add this later 
+		// Read the error location 
+		pci_conf_read(0, 24, 3, 0x50, 4, &mcanb_add);
+
+		// Read the syndrome 
+		celog_syndrome = (mcanb >> 15)&0xFF;
+
+		// Parse the error location 
+		page = (mcanb_add >> 12);
+		offset = (mcanb_add >> 3) & 0xFFF;
+
+		// Report the error 
+		print_ecc_err(page, offset, 1, celog_syndrome, 0);
+
+		// Clear the error registers 
+		pci_conf_write(0, 24, 3, 0x4C, 4, mcanb & 0x7FFFFFFF );
+	}
+	if (((mcanb >> 31)&1) && ((mcanb >> 13)&1)) {
+		// Found out about the first uncorrectable error 
+		// Read the error location 
+		pci_conf_read(0, 24, 3, 0x50, 4, &mcanb_add);
+
+		// Parse the error location 
+		page = (mcanb_add >> 12);
+		offset = (mcanb_add >> 3) & 0xFFF;
+
+		// Report the error 
+		print_ecc_err(page, offset, 0, 0, 0);
+
+		// Clear the error registers 
+		pci_conf_write(0, 24, 3, 0x4C, 4, mcanb & 0x7FFFFFF );
+
+	}
+
+}
+*/
+
+static void setup_amd751(void)
+{
+	unsigned long dram_status;
+
+	/* Fill in the correct memory capabilites */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x5a, 2, &dram_status);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = (dram_status & (1 << 2))?ECC_CORRECT: ECC_NONE;
+}
+
+/*
+static void poll_amd751(void)
+{
+	unsigned long ecc_status;
+	unsigned long bank_addr;
+	unsigned long bank_info;
+	unsigned long page;
+	int bits;
+	int i;
+
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x58, 2, &ecc_status);
+	if (ecc_status & (3 << 8)) {
+		for(i = 0; i < 6; i++) {
+			if (!(ecc_status & (1 << i))) {
+				continue;
+			}
+			// Find the bank the error occured on 
+			bank_addr = 0x40 + (i << 1);
+
+			// Now get the information on the erroring bank 
+			pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, bank_addr, 2, &bank_info);
+
+			// Parse the error location and error type 
+			page = (bank_info & 0xFF80) << 4;
+			bits = (((ecc_status >> 8) &3) == 2)?1:2;
+
+			// Report the error 
+			print_ecc_err(page, 0, bits==1?1:0, 0, 0);
+
+		}
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0x58, 2, 0);
+	}
+}
+
+// Still waiting for the CORRECT intel datasheet
+static void setup_i85x(void)
+{
+	unsigned long drc;
+	ctrl.cap = ECC_CORRECT;
+
+	pci_conf_read(ctrl.bus, ctrl.dev, 1, 0x70, 4, &drc);
+	ctrl.mode = ((drc>>20)&1)?ECC_CORRECT:ECC_NONE;
+
+}
+*/
+
+static void setup_amd76x(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_CORRECT };
+	unsigned long ecc_mode_status;
+
+	/* Fill in the correct memory capabilites */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x48, 4, &ecc_mode_status);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ddim[(ecc_mode_status >> 10)&3];
+}
+
+/*
+static void poll_amd76x(void)
+{
+	unsigned long ecc_mode_status;
+	unsigned long bank_addr;
+	unsigned long bank_info;
+	unsigned long page;
+
+	// Read the error status
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x48, 4, &ecc_mode_status);
+	// Multibit error
+	if (ecc_mode_status & (1 << 9)) {
+		// Find the bank the error occured on 
+		bank_addr = 0xC0 + (((ecc_mode_status >> 4) & 0xf) << 2);
+
+		// Now get the information on the erroring bank 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, bank_addr, 4, &bank_info);
+
+		// Parse the error location and error type 
+		page = (bank_info & 0xFF800000) >> 12;
+
+		// Report the error 
+		print_ecc_err(page, 0, 1, 0, 0);
+
+	}
+	// Singlebit error 
+	if (ecc_mode_status & (1 << 8)) {
+		// Find the bank the error occured on 
+		bank_addr = 0xC0 + (((ecc_mode_status >> 0) & 0xf) << 2);
+
+		// Now get the information on the erroring bank 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, bank_addr, 4, &bank_info);
+
+		// Parse the error location and error type 
+		page = (bank_info & 0xFF800000) >> 12;
+
+		// Report the error 
+		print_ecc_err(page, 0, 0, 0, 0);
+
+	}
+	// Clear the error status 
+	if (ecc_mode_status & (3 << 8)) {
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0x48, 4, ecc_mode_status);
+	}
+}
+*/
+
+static void setup_cnb20(void)
+{
+	/* Fill in the correct memory capabilites */
+	ctrl.cap = ECC_CORRECT;
+
+	/* FIXME add ECC error polling.  I don't have the documentation
+	 * do it right now.
+	 */
+}
+
+static void setup_E5400(void)
+{
+	unsigned long mcs;
+
+
+	/* Read the hardware capabilities */
+	pci_conf_read(ctrl.bus, 16, 1, 0x40, 4, &mcs);
+
+	/* Fill in the correct memory capabilities */
+	ctrl.mode = 0;
+	ctrl.cap = ECC_SCRUB;
+
+	/* Checking and correcting enabled */
+	if (((mcs >> 5) & 1) == 1) {
+		ctrl.mode |= ECC_CORRECT;
+	}
+
+	/* scrub enabled */
+	if (((mcs >> 7) & 1) == 1) {
+		ctrl.mode |= __ECC_SCRUB;
+	}
+}
+
+
+static void setup_iE7xxx(void)
+{
+	unsigned long mchcfgns;
+	unsigned long drc;
+	unsigned long device;
+	unsigned long dvnp;
+
+	/* Read the hardare capabilities */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x52, 2, &mchcfgns);
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x7C, 4, &drc);
+
+	/* This is a check for E7205 */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x02, 2, &device);
+
+	/* Fill in the correct memory capabilities */
+	ctrl.mode = 0;
+	ctrl.cap = ECC_CORRECT;
+
+	/* checking and correcting enabled */
+	if (((drc >> 20) & 3) == 2) {
+		ctrl.mode |= ECC_CORRECT;
+	}
+
+	/* E7205 doesn't support scrubbing */
+	if (device != 0x255d) {
+		/* scrub enabled */
+		/* For E7501, valid SCRUB operations is bit 0 / D0:F0:R70-73 */
+		ctrl.cap = ECC_SCRUB;
+		if (mchcfgns & 1) {
+			ctrl.mode |= __ECC_SCRUB;
+		}
+
+		/* Now, we can active Dev1/Fun1 */
+		/* Thanks to Tyan for providing us the board to solve this */
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xE0, 2, &dvnp);
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn , 0xE0, 2, (dvnp & 0xFE));
+
+		/* Clear any routing of ECC errors to interrupts that the BIOS might have set up */
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x88, 1, 0x0);
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x8A, 1, 0x0);
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x8C, 1, 0x0);
+	
+
+	}
+
+	/* Clear any prexisting error reports */
+	pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 1, 3);
+	pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x82, 1, 3);
+
+
+}
+
+static void setup_iE7520(void)
+{
+	unsigned long mchscrb;
+	unsigned long drc;
+	unsigned long dvnp1;
+
+	/* Read the hardare capabilities */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x52, 2, &mchscrb);
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x7C, 4, &drc);
+
+	/* Fill in the correct memory capabilities */
+	ctrl.mode = 0;
+	ctrl.cap = ECC_CORRECT;
+
+	/* Checking and correcting enabled */
+	if (((drc >> 20) & 3) != 0) {
+		ctrl.mode |= ECC_CORRECT;
+	}
+
+	/* scrub enabled */
+	ctrl.cap = ECC_SCRUB;
+	if ((mchscrb & 3) == 2) {
+		ctrl.mode |= __ECC_SCRUB;
+	}
+
+	/* Now, we can activate Fun1 */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xF4, 1, &dvnp1);
+	pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn , 0xF4, 1, (dvnp1 | 0x20));
+
+	/* Clear any prexisting error reports */
+	pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 2, 0x4747);
+	pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x82, 2, 0x4747);
+
+}
+
+/*
+static void poll_iE7xxx(void)
+{
+	unsigned long ferr;
+	unsigned long nerr;
+
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 1, &ferr);
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x82, 1, &nerr);
+
+	if (ferr & 1) {
+		// Find out about the first correctable error 
+		unsigned long celog_add;
+		unsigned long celog_syndrome;
+		unsigned long page;
+
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0xA0, 4, &celog_add);
+		// Read the syndrome 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0xD0, 2, &celog_syndrome);
+
+		// Parse the error location 
+		page = (celog_add & 0x0FFFFFC0) >> 6;
+
+		// Report the error 
+		print_ecc_err(page, 0, 1, celog_syndrome, 0);
+
+		// Clear Bit 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 1, ferr & 3);
+	}
+
+	if (ferr & 2) {
+		// Found out about the first uncorrectable error 
+		unsigned long uccelog_add;
+		unsigned long page;
+
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0xB0, 4, &uccelog_add);
+
+		// Parse the error location 
+		page = (uccelog_add & 0x0FFFFFC0) >> 6;
+
+		// Report the error 
+		print_ecc_err(page, 0, 0, 0, 0);
+
+		// Clear Bit 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 1, ferr & 3);
+	}
+
+	// Check if DRAM_NERR contains data 
+	if (nerr & 3) {
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x82, 1, nerr & 3);
+	}
+
+}
+*/
+
+static void setup_i440gx(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_DETECT, ECC_CORRECT, ECC_CORRECT };
+	unsigned long nbxcfg;
+
+	/* Fill in the correct memory capabilites */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x50, 4, &nbxcfg);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ddim[(nbxcfg >> 7)&3];
+}
+
+/*
+static void poll_i440gx(void)
+{
+	unsigned long errsts;
+	unsigned long page;
+	int bits;
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x91, 2, &errsts);
+	if (errsts & 0x11) {
+		unsigned long eap;
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x80, 4, &eap);
+
+		// Parse the error location and error type 
+		page = (eap & 0xFFFFF000) >> 12;
+		bits = 0;
+		if (eap &3) {
+			bits = ((eap & 3) == 1)?1:2;
+		}
+
+		if (bits) {
+			// Report the error 
+			print_ecc_err(page, 0, bits==1?1:0, 0, 0);
+		}
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0x91, 2, 0x11);
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0x80, 4, 3);
+	}
+
+}
+*/
+
+
+static void setup_i840(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_CORRECT };
+	unsigned long mchcfg;
+
+	/* Fill in the correct memory capabilites */
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x50, 2, &mchcfg);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ddim[(mchcfg >> 7)&3];
+}
+
+/*
+static void poll_i840(void)
+{
+	unsigned long errsts;
+	unsigned long page;
+	unsigned long syndrome;
+	int channel;
+	int bits;
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	if (errsts & 3) {
+		unsigned long eap;
+		unsigned long derrctl_sts;
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xE4, 4, &eap);
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xE2, 2, &derrctl_sts);
+
+		// Parse the error location and error type 
+		page = (eap & 0xFFFFF800) >> 11;
+		channel = eap & 1;
+		syndrome = derrctl_sts & 0xFF;
+		bits = ((errsts & 3) == 1)?1:2;
+
+		// Report the error 
+		print_ecc_err(page, 0, bits==1?1:0, syndrome, channel);
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xE2, 2, 3 << 10);
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, 3);
+	}
+}
+*/
+
+
+static void setup_i875(void)
+{
+
+	long *ptr;
+	ulong dev0, dev6 ;
+
+	/* Fill in the correct memory capabilites */
+
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ECC_NONE;
+
+	/* From my article : http://www.x86-secret.com/articles/tweak/pat/patsecrets-2.htm */
+	/* Activate Device 6 */
+	pci_conf_read( 0, 0, 0, 0xF4, 1, &dev0);
+	pci_conf_write( 0, 0, 0, 0xF4, 1, (dev0 | 0x2));
+
+	/* Activate Device 6 MMR */
+	pci_conf_read( 0, 6, 0, 0x04, 2, &dev6);
+	pci_conf_write( 0, 6, 0, 0x04, 2, (dev6 | 0x2));
+
+	/* Read the MMR Base Address & Define the pointer*/
+	pci_conf_read( 0, 6, 0, 0x10, 4, &dev6);
+	ptr=(long*)(dev6+0x68);
+
+	if (((*ptr >> 18)&1) == 1) { ctrl.mode = ECC_CORRECT; }
+
+	/* Reseting state */
+	pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2,  0x81);
+}
+
+static void setup_i925(void)
+{
+
+	// Activate MMR I/O
+	ulong dev0, drc;
+	unsigned long tolm;
+	long *ptr;
+	
+	pci_conf_read( 0, 0, 0, 0x54, 4, &dev0);
+	dev0 = dev0 | 0x10000000;
+	pci_conf_write( 0, 0, 0, 0x54, 4, dev0);
+	
+	// CDH start
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	if (!(dev0 & 0xFFFFC000)) {
+		pci_conf_read( 0, 0, 0, 0x9C, 1, &tolm);
+		pci_conf_write( 0, 0, 0, 0x47, 1, tolm & 0xF8);
+	}
+	// CDH end
+
+	// ECC Checking
+	ctrl.cap = ECC_CORRECT;
+
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0x120);
+	drc = *ptr & 0xFFFFFFFF;
+	
+	if (((drc >> 20) & 3) == 2) { 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, 3);
+		ctrl.mode = ECC_CORRECT; 
+	} else { 
+		ctrl.mode = ECC_NONE; 
+	}
+
+}
+
+static void setup_p35(void)
+{
+
+	// Activate MMR I/O
+	ulong dev0, capid0;
+	
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	if (!(dev0 & 0x1)) {
+		pci_conf_write( 0, 0, 0, 0x48, 1, dev0 | 1);
+	}
+
+	// ECC Checking (No poll on X38/48 for now)
+	pci_conf_read( 0, 0, 0, 0xE4, 4, &capid0);
+	if ((capid0 >> 8) & 1) {
+		ctrl.cap = ECC_NONE;
+	} else {
+		ctrl.cap = ECC_CORRECT;	
+	}
+
+	ctrl.mode = ECC_NONE; 
+	
+	/*
+	ulong toto;
+	pci_conf_write(0, 31, 3, 0x40, 1,  0x1);
+	pci_conf_read(0, 31, 3, 0x0, 4, &toto);
+	hprint(11,0,toto);
+	pci_conf_read(0, 31, 3, 0x10, 4, &toto);
+	hprint(11,10,toto)	;
+	pci_conf_read(0, 31, 3, 0x20, 4, &toto);
+	hprint(11,20,toto)	;
+	pci_conf_read(0, 28, 0, 0x0, 4, &toto);
+	hprint(11,30,toto);
+	pci_conf_read(0, 31, 0, 0x0, 4, &toto);
+	hprint(11,40,toto)	;
+	pci_conf_read(0, 31, 1, 0x0, 4, &toto);
+	hprint(11,50,toto)	;
+	pci_conf_read(0, 31, 2, 0x0, 4, &toto);
+	hprint(11,60,toto)	;	
+	*/
+}
+
+/*
+static void poll_i875(void)
+{
+	unsigned long errsts;
+	unsigned long page;
+	unsigned long des;
+	unsigned long syndrome;
+	int channel;
+	int bits;
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	if (errsts & 0x81)  {
+		unsigned long eap;
+		unsigned long derrsyn;
+		// Read the error location, syndrome and channel 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x58, 4, &eap);
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x5C, 1, &derrsyn);
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x5D, 1, &des);
+
+		// Parse the error location and error type 
+		page = (eap & 0xFFFFF000) >> 12;
+		syndrome = derrsyn;
+		channel = des & 1;
+		bits = (errsts & 0x80)?0:1;
+
+		// Report the error 
+		print_ecc_err(page, 0, bits, syndrome, channel);
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2,  0x81);
+	}
+}
+*/
+
+static void setup_i845(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_RESERVED };
+	unsigned long drc;
+
+	// Fill in the correct memory capabilites 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x7C, 4, &drc);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ddim[(drc >> 20)&3];
+}
+
+/*
+static void poll_i845(void)
+{
+	unsigned long errsts;
+	unsigned long page, offset;
+	unsigned long syndrome;
+	int bits;
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	if (errsts & 3) {
+		unsigned long eap;
+		unsigned long derrsyn;
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x8C, 4, &eap);
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x86, 1, &derrsyn);
+
+		// Parse the error location and error type 
+		offset = (eap & 0xFE) << 4;
+		page = (eap & 0x3FFFFFFE) >> 8;
+		syndrome = derrsyn;
+		bits = ((errsts & 3) == 1)?1:2;
+
+		// Report the error 
+		print_ecc_err(page, offset, bits==1?1:0, syndrome, 0);
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, 3);
+	}
+}
+*/
+
+
+static void setup_i820(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_CORRECT };
+	unsigned long mchcfg;
+
+	// Fill in the correct memory capabilites 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xbe, 2, &mchcfg);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ddim[(mchcfg >> 7)&3];
+}
+
+/*
+static void poll_i820(void)
+{
+	unsigned long errsts;
+	unsigned long page;
+	unsigned long syndrome;
+	int bits;
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	if (errsts & 3) {
+		unsigned long eap;
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xc4, 4, &eap);
+
+		// Parse the error location and error type 
+		page = (eap & 0xFFFFF000) >> 4;
+		syndrome = eap & 0xFF;
+		bits = ((errsts & 3) == 1)?1:2;
+
+		// Report the error 
+		print_ecc_err(page, 0, bits==1?1:0, syndrome, 0);
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, 3);
+	}
+}
+*/
+
+static void setup_i850(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_RESERVED };
+	unsigned long mchcfg;
+
+	// Fill in the correct memory capabilites 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x50, 2, &mchcfg);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ddim[(mchcfg >> 7)&3];
+}
+
+/*
+static void poll_i850(void)
+{
+	unsigned long errsts;
+	unsigned long page;
+	unsigned long syndrome;
+	int channel;
+	int bits;
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	if (errsts & 3) {
+		unsigned long eap;
+		unsigned long derrctl_sts;
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xE4, 4, &eap);
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xE2, 2, &derrctl_sts);
+
+		// Parse the error location and error type 
+		page = (eap & 0xFFFFF800) >> 11;
+		channel = eap & 1;
+		syndrome = derrctl_sts & 0xFF;
+		bits = ((errsts & 3) == 1)?1:2;
+
+		// Report the error 
+		print_ecc_err(page, 0, bits==1?1:0, syndrome, channel);
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, errsts & 3);
+	}
+}
+*/
+
+static void setup_i860(void)
+{
+	static const int ddim[] = { ECC_NONE, ECC_RESERVED, ECC_CORRECT, ECC_RESERVED };
+	unsigned long mchcfg;
+	unsigned long errsts;
+
+	// Fill in the correct memory capabilites 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x50, 2, &mchcfg);
+	ctrl.cap = ECC_CORRECT;
+	ctrl.mode = ddim[(mchcfg >> 7)&3];
+
+	// Clear any prexisting error reports 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, errsts & 3);
+}
+
+/*
+static void poll_i860(void)
+{
+	unsigned long errsts;
+	unsigned long page;
+	unsigned char syndrome;
+	int channel;
+	int bits;
+	// Read the error status 
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	if (errsts & 3) {
+		unsigned long eap;
+		unsigned long derrctl_sts;
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xE4, 4, &eap);
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xE2, 2, &derrctl_sts);
+
+		// Parse the error location and error type 
+		page = (eap & 0xFFFFFE00) >> 9;
+		channel = eap & 1;
+		syndrome = derrctl_sts & 0xFF;
+		bits = ((errsts & 3) == 1)?1:2;
+
+		// Report the error 
+		print_ecc_err(page, 0, bits==1?1:0, syndrome, channel);
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, errsts & 3);
+	}
+}
+
+
+static void poll_iE7221(void)
+{
+	unsigned long errsts;
+	unsigned long page;
+	unsigned char syndrome;
+	int channel;
+	int bits;
+	int errocc;
+	
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, &errsts);
+	
+	errocc = errsts & 3;
+	
+	if ((errocc == 1) || (errocc == 2)) {
+		unsigned long eap, offset;
+		unsigned long derrctl_sts;		
+		
+		// Read the error location 
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x58, 4, &eap);
+		pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, 0x5C, 1, &derrctl_sts);		
+		
+		// Parse the error location and error type 
+		channel = eap & 1;
+		eap = eap & 0xFFFFFF80;
+		page = eap >> 12;
+		offset = eap & 0xFFF;
+		syndrome = derrctl_sts & 0xFF;		
+		bits = errocc & 1;
+
+		// Report the error 
+		print_ecc_err(page, offset, bits, syndrome, channel);
+
+		// Clear the error status 
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, errsts & 3);
+	} 
+	
+	else if (errocc == 3) {
+	
+		pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn, 0xC8, 2, errsts & 3);	
+	
+	}
+}
+
+
+static void poll_iE7520(void)
+{
+	unsigned long ferr;
+	unsigned long nerr;
+
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 2, &ferr);
+	pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x82, 2, &nerr);
+
+	if (ferr & 0x0101) {
+			// Find out about the first correctable error 
+			unsigned long celog_add;
+			unsigned long celog_syndrome;
+			unsigned long page;
+
+			// Read the error location 
+			pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0xA0, 4,&celog_add);
+			// Read the syndrome 
+			pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0xC4, 2, &celog_syndrome);
+
+			// Parse the error location 
+			page = (celog_add & 0x7FFFFFFC) >> 2;
+
+			// Report the error 
+			print_ecc_err(page, 0, 1, celog_syndrome, 0);
+
+			// Clear Bit 
+			pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 2, ferr& 0x0101);
+	}
+
+	if (ferr & 0x4646) {
+			// Found out about the first uncorrectable error 
+			unsigned long uccelog_add;
+			unsigned long page;
+
+			// Read the error location 
+			pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn +1, 0xA4, 4, &uccelog_add);
+
+			// Parse the error location 
+			page = (uccelog_add & 0x7FFFFFFC) >> 2;
+
+			// Report the error 
+			print_ecc_err(page, 0, 0, 0, 0);
+
+			// Clear Bit 
+			pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x80, 2, ferr & 0x4646);
+	}
+
+	// Check if DRAM_NERR contains data 
+	if (nerr & 0x4747) {
+			 pci_conf_write(ctrl.bus, ctrl.dev, ctrl.fn +1, 0x82, 2, nerr & 0x4747);
+	}
+}
+*/
+
+
+
+/* ----------------- Here's the code for FSB detection ----------------- */
+/* --------------------------------------------------------------------- */
+
+static float athloncoef[] = {11, 11.5, 12.0, 12.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0, 10.5};
+static float athloncoef2[] = {12, 19.0, 12.0, 20.0, 13.0, 13.5, 14.0, 21.0, 15.0, 22, 16.0, 16.5, 17.0, 18.0, 23.0, 24.0};
+static float p4model1ratios[] = {16, 17, 18, 19, 20, 21, 22, 23, 8, 9, 10, 11, 12, 13, 14, 15};
+
+static float getP4PMmultiplier(void)
+{
+	//unsigned int msr_lo, msr_hi;
+	int msr_lo, msr_hi;
+	float coef;
+
+	
+	/* Find multiplier (by MSR) */
+	if (cpu_id.vers.bits.family == 6) 
+	{
+		if(cpu_id.fid.bits.eist & 1) 
+		{
+			rdmsr(0x198, msr_lo, msr_hi);
+			coef = ((msr_lo) >> 8) & 0x1F;							
+			if ((msr_lo >> 14) & 0x1) { coef += 0.5f; }		
+			// Atom Fix
+			if(coef == 6)
+			{
+				coef = ((msr_hi) >> 8) & 0x1F;							
+				if ((msr_hi >> 14) & 0x1) { coef += 0.5f; }							
+			}
+		} else {
+			rdmsr(0x2A, msr_lo, msr_hi);
+			coef = (msr_lo >> 22) & 0x1F;
+		}
+	}
+	else
+	{
+		if (cpu_id.vers.bits.model < 2)
+		{
+			rdmsr(0x2A, msr_lo, msr_hi);
+			coef = (msr_lo >> 8) & 0xF;
+			coef = p4model1ratios[(int)coef];
+		}
+		else
+		{
+			rdmsr(0x2C, msr_lo, msr_hi);
+			coef = (msr_lo >> 24) & 0x1F;
+		}
+	}
+	
+	return coef;
+}
+
+static float getNHMmultiplier(void)
+{
+	unsigned int msr_lo, msr_hi;
+	float coef;
+	
+	/* Find multiplier (by MSR) */
+	/* First, check if Flexible Ratio is Enabled */
+	rdmsr(0x194, msr_lo, msr_hi);
+	if((msr_lo >> 16) & 1){
+		coef = (msr_lo >> 8) & 0xFF;
+	 } else {
+		rdmsr(0xCE, msr_lo, msr_hi);
+		coef = (msr_lo >> 8) & 0xFF;
+	 }
+
+	return coef;
+}
+static float getSNBmultiplier(void)
+{
+	unsigned int msr_lo, msr_hi;
+	float coef;
+	
+	rdmsr(0xCE, msr_lo, msr_hi);
+	coef = (msr_lo >> 8) & 0xFF;		
+
+	return coef;
+}
+
+static void poll_fsb_ct(void) 
+{
+	unsigned long mcr, mdr;
+	double dramratio, dramclock, fsb;
+	float coef = getP4PMmultiplier();
+
+	/* Build the MCR Message*/
+	mcr = (0x10 << 24); // 10h = Read - 11h = Write
+	mcr += (0x01 << 16); // DRAM Registers located on port 01h
+	mcr += (0x01 << 8); // DRP = 00h, DTR0 = 01h, DTR1 = 02h, DTR2 = 03h
+	mcr &= 0xFFFFFFF0; // bit 03:00 RSVD	
+	
+	/* Send Message to GMCH */
+	pci_conf_write(0, 0, 0, 0xD0, 4, mcr);	
+	
+	/* Read Answer from Sideband bus */
+	pci_conf_read(0, 0, 0, 0xD4, 4, &mdr);			
+	
+	/* Get RAM ratio */
+	switch (mdr & 0x3) {
+		default: 
+		case 0:	dramratio = 3.0f; break;
+		case 1:	dramratio = 4.0f; break;
+		case 2:	dramratio = 5.0f; break;
+		case 3:	dramratio = 6.0f; break;
+	}
+
+	// Compute FSB & RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio;
+
+	// Print'em all. Whoa !
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+static void poll_fsb_amd64(void) {
+
+	unsigned int mcgsrl;
+	unsigned int mcgsth;
+	unsigned long fid, temp2;
+	unsigned long dramchr;
+	float clockratio;
+	double dramclock;
+	unsigned int dummy[3];
+	int ram_type;
+
+	float coef = 10;
+
+	cpuid(0x80000007, &dummy[0], &dummy[1], &dummy[2], &dummy[3]);
+
+	/* First, got the FID by MSR */
+	/* First look if Cool 'n Quiet is supported to choose the best msr */
+	if (((dummy[3] >> 1) & 1) == 1) {
+		rdmsr(0xc0010042, mcgsrl, mcgsth);
+		fid = (mcgsrl & 0x3F);
+	} else {
+		rdmsr(0xc0010015, mcgsrl, mcgsth);
+		fid = ((mcgsrl >> 24)& 0x3F);
+	}
+	
+	/* Extreme simplification. */
+	coef = ( fid / 2 ) + 4.0;
+
+	/* Support for .5 coef */
+	if (fid & 1) { coef = coef + 0.5; }
+
+	/* Next, we need the clock ratio */
+	if (cpu_id.vers.bits.extendedModel >= 4) {
+	/* K8 0FH */
+		pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+		temp2 = (dramchr & 0x7);
+		clockratio = coef;
+		ram_type = 2;
+	
+		switch (temp2) {
+			case 0x0:
+				clockratio = (int)(coef);
+				break;
+			case 0x1:
+				clockratio = (int)(coef * 3.0f/4.0f);
+				break;
+			case 0x2:
+				clockratio = (int)(coef * 3.0f/5.0f);
+				break;
+			case 0x3:
+				clockratio = (int)(coef * 3.0f/6.0f);
+				break;
+			}	
+	
+	 } else {
+	 /* OLD K8 */
+		pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+		temp2 = (dramchr >> 20) & 0x7;
+		ram_type = 1;
+		clockratio = coef;
+	
+		switch (temp2) {
+			case 0x0:
+				clockratio = (int)(coef * 2.0f);
+				break;
+			case 0x2:
+				clockratio = (int)((coef * 3.0f/2.0f) + 0.81f);
+				break;
+			case 0x4:
+				clockratio = (int)((coef * 4.0f/3.0f) + 0.81f);
+				break;
+			case 0x5:
+				clockratio = (int)((coef * 6.0f/5.0f) + 0.81f);
+				break;
+			case 0x6:
+				clockratio = (int)((coef * 10.0f/9.0f) + 0.81f);
+				break;
+			case 0x7:
+				clockratio = (int)(coef + 0.81f);
+				break;
+			}
+	}
+
+	/* Compute the final DRAM Clock */
+	dramclock = (extclock / 1000) / clockratio;
+
+	/* ...and print */
+	print_cpu_line(dramclock, (extclock / 1000 / coef), ram_type);
+
+}
+
+static void poll_fsb_k10(void) {
+
+	unsigned int mcgsrl;
+	unsigned int mcgsth;
+	unsigned long temp2;
+	unsigned long dramchr;
+	unsigned long mainPllId;
+	double dramclock;
+	ulong offset = 0;
+	int ram_type = 2;
+	
+		/* First, we need the clock ratio */
+		pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+		temp2 = (dramchr & 0x7);
+
+		switch (temp2) {
+			case 0x7: temp2++;
+			case 0x6: temp2++;
+			case 0x5: temp2++;
+			case 0x4: temp2++;
+			default:  temp2 += 3;
+		}	
+
+	/* Compute the final DRAM Clock */
+	if (((cpu_id.vers.bits.extendedModel >> 4) & 0xFF) == 1) {
+		dramclock = ((temp2 * 200) / 3.0) + 0.25;
+	} else {
+		unsigned long target;
+		unsigned long dx;
+		unsigned      divisor;
+
+		target = temp2 * 400;
+
+		/* Get the FID by MSR */
+		rdmsr(0xc0010071, mcgsrl, mcgsth);
+
+		pci_conf_read(0, 24, 3, 0xD4, 4, &mainPllId);
+
+		if ( mainPllId & 0x40 )
+			mainPllId &= 0x3F;
+		else
+			mainPllId = 8;	/* FID for 1600 */
+
+		mcgsth = (mcgsth >> 17) & 0x3F;
+		if ( mcgsth ) {
+			if ( mainPllId > mcgsth )
+				mainPllId = mcgsth;
+		}
+
+		dx = (mainPllId + 8) * 1200;
+		for ( divisor = 3; divisor < 100; divisor++ )
+			if ( (dx / divisor) <= target )
+				break;
+
+		
+	pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+	
+	// If Channel A not enabled, switch to channel B
+	if(((dramchr>>14) & 0x1))
+	{
+		offset = 0x100;
+		pci_conf_read(0, 24, 2, 0x94+offset, 4, &dramchr);	
+	}
+	
+	//DDR2 or DDR3
+	if ((dramchr >> 8)&1) {
+		ram_type = 3;
+	} else {
+		ram_type = 2;;
+	}
+		
+		dramclock = ((dx / divisor) / 6.0) + 0.25;
+}
+
+	/* ...and print */
+	print_cpu_line(dramclock, 0, ram_type);
+
+}
+
+static void poll_fsb_k12(void) {
+
+	unsigned long temp2;
+	unsigned long dramchr;
+	double dramratio, dramclock, fsb, did;
+	unsigned int mcgsrl,mcgsth, fid, did_raw;
+	 
+	// Get current FID & DID
+ 	rdmsr(0xc0010071, mcgsrl, mcgsth);
+ 	did_raw = mcgsrl & 0xF;
+ 	fid = (mcgsrl >> 4) & 0xF;
+  
+	switch(did_raw)
+	{
+		default:
+		case 0x0:
+			did = 1.0f;
+			break;
+		case 0x1:
+			did = 1.5f;
+			break;
+		case 0x2:
+			did = 2.0f;
+			break;					
+		case 0x3:
+			did = 3.0f;
+			break;		
+		case 0x4:
+			did = 4.0f;
+			break;
+		case 0x5:
+			did = 6.0f;
+			break;
+		case 0x6:
+			did = 8.0f;
+			break;			
+		case 0x7:
+			did = 12.0f;
+			break;			
+		case 0x8:
+			did = 16.0f;
+			break;	
+	}
+
+  fsb = ((extclock / 1000.0f) / ((fid + 16.0f) / did));
+		
+	/* Finaly, we need the clock ratio */
+	pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+	
+	if(((dramchr >> 14) & 0x1) == 1)
+	{
+		pci_conf_read(0, 24, 2, 0x194, 4, &dramchr);				
+	}
+	
+	temp2 = (dramchr & 0x1F);
+
+	switch (temp2) {
+		default:
+		case 0x06: 
+			dramratio = 4.0f; 
+			break;
+		case 0x0A: 
+			dramratio = 16.0f / 3.0f; 
+			break;
+		case 0x0E: 
+			dramratio = 20.0f / 3.0f; 
+			break;
+		case 0x12: 
+			dramratio = 8.0f; 
+			break;
+		case 0x16: 
+			dramratio = 28.0f / 3.0f; 
+			break;						
+	}	
+	
+	dramclock = fsb * dramratio;
+	
+	/* print */
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+static void poll_fsb_k16(void) 
+{
+
+	unsigned long dramchr;
+	double dramratio, dramclock, fsb;
+
+	// FIXME: Unable to find a real way to detect multiplier. 
+	fsb = 100.0f;
+		
+	/* Clock ratio */
+	pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+
+	switch (dramchr & 0x1F) {
+		default:
+		case 0x04: /* 333 */
+			dramratio = 10.0f / 3.0f; 
+			break;			
+		case 0x06: /* 400 */
+			dramratio = 4.0f; 
+			break;
+		case 0x0A: /* 533 */
+			dramratio = 16.0f / 3.0f; 
+			break;
+		case 0x0E: /* 667 */
+			dramratio = 20.0f / 3.0f; 
+			break;
+		case 0x12: /* 800 */
+			dramratio = 8.0f; 
+			break;	
+		case 0x16: /* 933 */
+			dramratio = 28.0f / 3.0f;
+			break;
+		case 0x19: /* 1050 */
+			dramratio = 21.0f / 2.0f;
+			break;
+		case 0x1A: /* 1066 */
+			dramratio = 32.0f / 3.0f;
+			break;	
+	}	
+	
+	dramclock = fsb * dramratio;
+	
+	/* print */
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+static void poll_fsb_k15(void) {
+
+	unsigned long temp2;
+	unsigned long dramchr;
+	double dramratio, dramclock, fsb;
+	unsigned int mcgsrl,mcgsth, fid, did;
+	 
+	// Get current FID & DID
+ 	rdmsr(0xc0010071, mcgsrl, mcgsth);
+ 	fid = mcgsrl & 0x3F;
+ 	did = (mcgsrl >> 6) & 0x7;
+  
+  fsb = ((extclock / 1000.0f) / ((fid + 16.0f) / (2^did)) / 2);
+	
+	/* Finaly, we need the clock ratio */
+	pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+	
+	if(((dramchr >> 14) & 0x1) == 1)
+	{
+		pci_conf_read(0, 24, 2, 0x194, 4, &dramchr);				
+	}
+	
+	temp2 = (dramchr & 0x1F);
+
+	switch (temp2) {
+		case 0x04: 
+			dramratio = 10.0f / 3.0f; 
+			break;
+		default:
+		case 0x06: 
+			dramratio = 4.0f; 
+			break;
+		case 0x0A: 
+			dramratio = 16.0f / 3.0f; 
+			break;
+		case 0x0E: 
+			dramratio = 20.0f / 3.0f; 
+			break;
+		case 0x12: 
+			dramratio = 8.0f; 
+			break;
+		case 0x16: 
+			dramratio = 28.0f / 3.0f; 
+			break;						
+		case 0x1A: 
+			dramratio = 32.0f / 3.0f; 
+			break;	
+		case 0x1F: 
+			dramratio = 36.0f / 3.0f; 
+			break;				
+	}	
+	
+	dramclock = fsb * dramratio;
+	
+	/* print */
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+static void poll_fsb_k14(void) 
+{
+
+	unsigned long dramchr;
+	double dramratio, dramclock, fsb;
+
+	// FIXME: Unable to find a real way to detect multiplier. 
+  fsb = 100.0f;
+		
+	/* Clock ratio */
+	pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+
+	switch (dramchr & 0x1F) {
+		default:
+		case 0x06: 
+			dramratio = 4.0f; 
+			break;
+		case 0x0A: 
+			dramratio = 16.0f / 3.0f; 
+			break;
+		case 0x0E: 
+			dramratio = 20.0f / 3.0f; 
+			break;
+		case 0x12: 
+			dramratio = 8.0f; 
+			break;					
+	}	
+	
+	dramclock = fsb * dramratio;
+	
+	/* print */
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+
+static void poll_fsb_i925(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, mchcfg2, dev0, drc, idetect;
+	float coef = getP4PMmultiplier();
+	long *ptr;
+	int ddr_type;
+	
+	pci_conf_read( 0, 0, 0, 0x02, 2, &idetect);
+	
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	dev0 = dev0 & 0xFFFFC000;
+	ptr=(long*)(dev0+0xC00);
+	mchcfg = *ptr & 0xFFFF;
+	ptr=(long*)(dev0+0x120);
+	drc = *ptr & 0xFFFF;
+	dramratio = 1;
+
+	mchcfg2 = (mchcfg >> 4)&3;
+	
+	if ((drc&3) != 2) {
+		// We are in DDR1 Mode
+		if (mchcfg2 == 1) { dramratio = 0.8; } else { dramratio = 1; }
+		ddr_type = 1;
+	} else {
+		// We are in DDR2 Mode
+		ddr_type = 2;
+		if ((mchcfg >> 2)&1) {
+			// We are in FSB1066 Mode
+			if (mchcfg2 == 2) { dramratio = 0.75; } else { dramratio = 1; }
+		} else {
+			switch (mchcfg2) {
+				case 1:
+					dramratio = 0.66667;
+					break;
+				case 2:
+					if (idetect != 0x2590) { dramratio = 1; } else { dramratio = 1.5; }
+					break;
+				case 3:
+						// Checking for FSB533 Mode & Alviso
+						if ((mchcfg & 1) == 0) { dramratio = 1.33334; }
+						else if (idetect == 0x2590) { dramratio = 2; }
+						else { dramratio = 1.5; }
+			}
+		}
+	}
+	// Compute RAM Frequency 
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio;
+
+	
+	print_cpu_line(dramclock, fsb, ddr_type);
+	
+}
+
+static void poll_fsb_i945(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, dev0;
+	float coef = getP4PMmultiplier();
+	long *ptr;
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0xC00);
+	mchcfg = *ptr & 0xFFFF;
+	dramratio = 1;
+
+	switch ((mchcfg >> 4)&7) {
+		case 1:	dramratio = 1.0; break;
+		case 2:	dramratio = 1.33334; break;
+		case 3:	dramratio = 1.66667; break;
+		case 4:	dramratio = 2.0; break;
+	}
+
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+
+	dramclock = fsb * dramratio;
+
+	// Print
+	print_cpu_line(dramclock, fsb, 2);
+
+}
+
+static void poll_fsb_i945gme(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, dev0, fsb_mch;
+	float coef = getP4PMmultiplier();
+	long *ptr;
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0xC00);
+	mchcfg = *ptr & 0xFFFF;
+	dramratio = 1;
+
+	switch (mchcfg & 7) {
+		case 0: fsb_mch = 400; break;
+		default:
+		case 1: fsb_mch = 533; break;
+		case 2:	fsb_mch = 667; break;	
+	}
+
+
+	switch (fsb_mch) {
+	case 400:
+		switch ((mchcfg >> 4)&7) {
+			case 2:	dramratio = 1.0f; break;
+			case 3:	dramratio = 4.0f/3.0f; break;
+			case 4:	dramratio = 5.0f/3.0f; break;
+		}
+		break;
+		
+	default:
+	case 533:
+		switch ((mchcfg >> 4)&7) {
+			case 2:	dramratio = 3.0f/4.0f; break;
+			case 3:	dramratio = 1.0f; break;
+			case 4:	dramratio = 5.0f/4.0f; break;
+		}
+		break;
+
+	case 667:
+		switch ((mchcfg >> 4)&7) {
+			case 2:	dramratio = 3.0f/5.0f; break;
+			case 3:	dramratio = 4.0f/5.0f; break;
+			case 4:	dramratio = 1.0f; break;
+		}
+		break;
+	}
+
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio * 2;
+
+	print_cpu_line(dramclock, fsb, 2);
+
+}
+
+
+static void poll_fsb_i975(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, dev0, fsb_mch;
+	float coef = getP4PMmultiplier();
+	long *ptr;
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0xC00);
+	mchcfg = *ptr & 0xFFFF;
+	dramratio = 1;
+
+	switch (mchcfg & 7) {
+		case 1: fsb_mch = 533; break;
+		case 2:	fsb_mch = 800; break;
+		case 3:	fsb_mch = 667; break;				
+		default: fsb_mch = 1066; break;
+	}
+
+
+	switch (fsb_mch) {
+	case 533:
+		switch ((mchcfg >> 4)&7) {
+			case 0:	dramratio = 1.25; break;
+			case 1:	dramratio = 1.5; break;
+			case 2:	dramratio = 2.0; break;
+		}
+		break;
+		
+	default:
+	case 800:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 1.0; break;
+			case 2:	dramratio = 1.33334; break;
+			case 3:	dramratio = 1.66667; break;
+			case 4:	dramratio = 2.0; break;
+		}
+		break;
+
+	case 1066:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 0.75; break;
+			case 2:	dramratio = 1.0; break;
+			case 3:	dramratio = 1.25; break;
+			case 4:	dramratio = 1.5; break;
+		}
+		break;
+	}
+
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio;
+
+	print_cpu_line(dramclock, fsb, 2);
+
+}
+
+static void poll_fsb_i965(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, dev0, fsb_mch;
+	float coef = getP4PMmultiplier();
+	long *ptr;
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0xC00);
+	mchcfg = *ptr & 0xFFFF;
+	dramratio = 1;
+
+	switch (mchcfg & 7) {
+		case 0: fsb_mch = 1066; break;
+		case 1: fsb_mch = 533; break;
+		default: case 2:	fsb_mch = 800; break;
+		case 3:	fsb_mch = 667; break;		
+		case 4: fsb_mch = 1333; break;
+		case 6: fsb_mch = 1600; break;					
+	}
+
+
+	switch (fsb_mch) {
+	case 533:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 2.0; break;
+			case 2:	dramratio = 2.5; break;
+			case 3:	dramratio = 3.0; break;
+		}
+		break;
+		
+	default:
+	case 800:
+		switch ((mchcfg >> 4)&7) {
+			case 0:	dramratio = 1.0; break;
+			case 1:	dramratio = 5.0f/4.0f; break;
+			case 2:	dramratio = 5.0f/3.0f; break;
+			case 3:	dramratio = 2.0f; break;
+			case 4:	dramratio = 8.0f/3.0f; break;
+			case 5:	dramratio = 10.0f/3.0f; break;
+		}
+		break;
+
+	case 1066:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 1.0f; break;
+			case 2:	dramratio = 5.0f/4.0f; break;
+			case 3:	dramratio = 3.0f/2.0f; break;
+			case 4:	dramratio = 2.0f; break;
+			case 5:	dramratio = 5.0f/2.0f; break;
+		}
+		break;
+	
+	case 1333:
+		switch ((mchcfg >> 4)&7) {
+			case 2:	dramratio = 1.0f; break;
+			case 3:	dramratio = 6.0f/5.0f; break;
+			case 4:	dramratio = 8.0f/5.0f; break;
+			case 5:	dramratio = 2.0f; break;
+		}
+		break;
+
+	case 1600:
+		switch ((mchcfg >> 4)&7) {
+			case 3:	dramratio = 1.0f; break;
+			case 4:	dramratio = 4.0f/3.0f; break;
+			case 5:	dramratio = 3.0f/2.0f; break;
+			case 6:	dramratio = 2.0f; break;
+		}
+		break;
+
+}
+
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 2);
+
+}
+
+static void poll_fsb_p35(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, dev0, fsb_mch, Device_ID, Memory_Check,	c0ckectrl, offset;
+	float coef = getP4PMmultiplier();
+	long *ptr;
+	int ram_type;
+
+	pci_conf_read( 0, 0, 0, 0x02, 2, &Device_ID);
+	Device_ID &= 0xFFFF;
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	
+	ptr = (long*)(dev0+0x260);
+	c0ckectrl = *ptr & 0xFFFFFFFF;	
+
+	
+	// If DIMM 0 not populated, check DIMM 1
+	((c0ckectrl) >> 20 & 0xF)?(offset = 0):(offset = 0x400);
+	
+	ptr=(long*)(dev0+0xC00);
+	mchcfg = *ptr & 0xFFFF;
+	dramratio = 1;
+
+	switch (mchcfg & 7) {
+		case 0: fsb_mch = 1066; break;
+		case 1: fsb_mch = 533; break;
+		default: case 2:	fsb_mch = 800; break;
+		case 3:	fsb_mch = 667; break;		
+		case 4: fsb_mch = 1333; break;
+		case 6: fsb_mch = 1600; break;					
+	}
+
+
+	switch (fsb_mch) {
+	case 533:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 2.0; break;
+			case 2:	dramratio = 2.5; break;
+			case 3:	dramratio = 3.0; break;
+		}
+		break;
+		
+	default:
+	case 800:
+		switch ((mchcfg >> 4)&7) {
+			case 0:	dramratio = 1.0; break;
+			case 1:	dramratio = 5.0f/4.0f; break;
+			case 2:	dramratio = 5.0f/3.0f; break;
+			case 3:	dramratio = 2.0; break;
+			case 4:	dramratio = 8.0f/3.0f; break;
+			case 5:	dramratio = 10.0f/3.0f; break;
+		}
+		break;
+
+	case 1066:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 1.0f; break;
+			case 2:	dramratio = 5.0f/4.0f; break;
+			case 3:	dramratio = 3.0f/2.0f; break;
+			case 4:	dramratio = 2.0f; break;
+			case 5:	dramratio = 5.0f/2.0f; break;
+		}
+		break;
+	
+	case 1333:
+		switch ((mchcfg >> 4)&7) {
+			case 2:	dramratio = 1.0f; break;
+			case 3:	dramratio = 6.0f/5.0f; break;
+			case 4:	dramratio = 8.0f/5.0f; break;
+			case 5:	dramratio = 2.0f; break;
+		}
+		break;
+
+	case 1600:
+		switch ((mchcfg >> 4)&7) {
+			case 3:	dramratio = 1.0f; break;
+			case 4:	dramratio = 4.0f/3.0f; break;
+			case 5:	dramratio = 3.0f/2.0f; break;
+			case 6:	dramratio = 2.0f; break;
+		}
+		break;
+
+	}
+
+	// On P45, check 1A8
+	if(Device_ID > 0x2E00 && imc_type != 8) {
+		ptr = (long*)(dev0+offset+0x1A8);
+		Memory_Check = *ptr & 0xFFFFFFFF;	
+		Memory_Check >>= 2;
+		Memory_Check &= 1;
+		Memory_Check = !Memory_Check;
+	} else if (imc_type == 8) {
+		ptr = (long*)(dev0+offset+0x224);
+		Memory_Check = *ptr & 0xFFFFFFFF;	
+		Memory_Check &= 1;
+		Memory_Check = !Memory_Check;		
+	} else {
+		ptr = (long*)(dev0+offset+0x1E8);
+		Memory_Check = *ptr & 0xFFFFFFFF;		
+	}
+
+	//Determine DDR-II or DDR-III
+	if (Memory_Check & 1) {
+		ram_type = 2;
+	} else {
+		ram_type = 3;
+	}
+
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, ram_type);
+
+}
+
+static void poll_fsb_im965(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, dev0, fsb_mch;
+	float coef = getP4PMmultiplier();
+	long *ptr;
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0xC00);
+	mchcfg = *ptr & 0xFFFF;
+	dramratio = 1;
+
+	switch (mchcfg & 7) {
+		case 1: fsb_mch = 533; break;
+		default: case 2:	fsb_mch = 800; break;
+		case 3:	fsb_mch = 667; break;				
+		case 6:	fsb_mch = 1066; break;			
+	}
+
+
+	switch (fsb_mch) {
+	case 533:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 5.0f/4.0f; break;
+			case 2:	dramratio = 3.0f/2.0f; break;
+			case 3:	dramratio = 2.0f; break;
+		}
+		break;
+
+	case 667:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 1.0f; break;
+			case 2:	dramratio = 6.0f/5.0f; break;
+			case 3:	dramratio = 8.0f/5.0f; break;
+			case 4:	dramratio = 2.0f; break;
+			case 5:	dramratio = 12.0f/5.0f; break;
+		}
+		break;
+	default:
+	case 800:
+		switch ((mchcfg >> 4)&7) {
+			case 1:	dramratio = 5.0f/6.0f; break;
+			case 2:	dramratio = 1.0f; break;
+			case 3:	dramratio = 4.0f/3.0f; break;
+			case 4:	dramratio = 5.0f/3.0f; break;
+			case 5:	dramratio = 2.0f; break;
+		}
+		break;
+	case 1066:
+		switch ((mchcfg >> 4)&7) {
+			case 5:	dramratio = 3.0f/2.0f; break;
+			case 6:	dramratio = 2.0f; break;
+		}
+		break;
+}
+
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 2);
+
+}
+
+
+static void poll_fsb_5400(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long ambase_low, ambase_high, ddrfrq;
+	float coef = getP4PMmultiplier();
+
+	/* Find dramratio */
+	pci_conf_read( 0, 16, 0, 0x48, 4, &ambase_low);
+	ambase_low &= 0xFFFE0000;
+	pci_conf_read( 0, 16, 0, 0x4C, 4, &ambase_high);
+	ambase_high &= 0xFF;
+	pci_conf_read( 0, 16, 1, 0x56, 1, &ddrfrq);
+  ddrfrq &= 7;
+  dramratio = 1;
+
+	switch (ddrfrq) {
+			case 0:	
+			case 1:	
+			case 4:					
+				dramratio = 1.0; 
+				break;
+			case 2:	
+				dramratio = 5.0f/4.0f; 
+				break;
+			case 3:	
+			case 7:	
+				dramratio = 4.0f/5.0f; 
+				break;
+		}
+
+
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 2);
+
+}
+
+
+static void poll_fsb_nf4ie(void) {
+
+	double dramclock, dramratio, fsb;
+	float mratio, nratio;
+	unsigned long reg74, reg60;
+	float coef = getP4PMmultiplier();
+	
+	/* Find dramratio */
+	pci_conf_read(0, 0, 2, 0x74, 2, &reg74);
+	pci_conf_read(0, 0, 2, 0x60, 4, &reg60);
+	mratio = reg74 & 0xF;
+	nratio = (reg74 >> 4) & 0xF;
+
+	// If M or N = 0, then M or N = 16
+	if (mratio == 0) { mratio = 16; }
+	if (nratio == 0) { nratio = 16; }
+	
+	// Check if synchro or pseudo-synchro mode
+	if((reg60 >> 22) & 1) {
+		dramratio = 1;
+	} else {
+		dramratio = nratio / mratio;
+	}
+
+	/* Compute RAM Frequency */
+	fsb = ((extclock /1000) / coef);
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 2);
+	
+}
+
+static void poll_fsb_i875(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mchcfg, smfs;
+	float coef = getP4PMmultiplier();
+
+	/* Find dramratio */
+	pci_conf_read(0, 0, 0, 0xC6, 2, &mchcfg);
+	smfs = (mchcfg >> 10)&3;
+	dramratio = 1;
+
+	if ((mchcfg&3) == 3) { dramratio = 1; }
+	if ((mchcfg&3) == 2) {
+		if (smfs == 2) { dramratio = 1; }
+		if (smfs == 1) { dramratio = 1.25; }
+		if (smfs == 0) { dramratio = 1.5; }
+	}
+	if ((mchcfg&3) == 1) {
+		if (smfs == 2) { dramratio = 0.6666666666; }
+		if (smfs == 1) { dramratio = 0.8; }
+		if (smfs == 0) { dramratio = 1; }
+	}
+	if ((mchcfg&3) == 0) { dramratio = 0.75; }
+
+
+	/* Compute RAM Frequency */
+	dramclock = ((extclock /1000) / coef) / dramratio;
+	fsb = ((extclock /1000) / coef);
+
+	/* Print DRAM Freq */
+	print_cpu_line(dramclock, fsb, 2);
+}
+
+static void poll_fsb_p4(void) {
+
+	ulong fsb, idetect;
+	float coef = getP4PMmultiplier();
+	char *name;
+	int col,temp;
+
+	fsb = ((extclock /1000) / coef);
+
+	/* For synchro only chipsets */
+	pci_conf_read( 0, 0, 0, 0x02, 2, &idetect);
+	if (idetect == 0x2540 || idetect == 0x254C) 
+	{
+		print_cpu_line(fsb, fsb, 1);
+	} else {
+		/* Print the controller name */
+		col = COL_SPEC;
+		cprint(LINE_CPU, col, "Chipset:               ");	
+		col += 9;
+		/* Print the controller name */
+		name = controllers[ctrl.index].name;
+		cprint(LINE_CPU, col, name);
+		/* Now figure out how much I just printed */
+		temp = 20;
+		while(name[temp - 20] != '\0') {
+			col++;
+			temp++;
+		}
+		
+		if(temp < 36){
+			cprint(LINE_CPU, col +1, "- FSB : ");
+			col += 9;
+			dprint(LINE_CPU, col, fsb, 3,0);
+			col += 3;
+		}
+	
+	}
+}
+
+static void poll_fsb_i855(void) {
+
+
+	double dramclock, dramratio, fsb ;
+	unsigned int msr_lo, msr_hi;
+	ulong mchcfg, idetect;
+	int coef;
+
+	pci_conf_read( 0, 0, 0, 0x02, 2, &idetect);
+
+	/* Find multiplier (by MSR) */
+
+	/* Is it a Pentium M ? */
+	if (cpu_id.vers.bits.family == 6) {
+		rdmsr(0x2A, msr_lo, msr_hi);
+		coef = (msr_lo >> 22) & 0x1F;
+	} else {
+		rdmsr(0x2C, msr_lo, msr_hi);
+		coef = (msr_lo >> 24) & 0x1F;
+	}
+
+	fsb = ((extclock /1000) / coef);
+
+
+	/* Compute DRAM Clock */
+
+	dramratio = 1;
+	if (idetect == 0x3580) {
+		pci_conf_read( 0, 0, 3, 0xC0, 2, &mchcfg);
+		mchcfg = mchcfg & 0x7;
+
+		if (mchcfg == 1 || mchcfg == 2 || mchcfg == 4 || mchcfg == 5) {	dramratio = 1; }
+		if (mchcfg == 0 || mchcfg == 3) { dramratio = 1.333333333; }
+		if (mchcfg == 6) { dramratio = 1.25; }
+		if (mchcfg == 7) { dramratio = 1.666666667; }
+
+	} else {
+		pci_conf_read( 0, 0, 0, 0xC6, 2, &mchcfg);
+		if (((mchcfg >> 10)&3) == 0) { dramratio = 1; }
+		else if (((mchcfg >> 10)&3) == 1) { dramratio = 1.666667; }
+		else { dramratio = 1.333333333; }
+	}
+
+
+	dramclock = fsb * dramratio;
+
+	/* ...and print */
+	print_cpu_line(dramclock, fsb, 1);
+
+}
+
+static void poll_fsb_amd32(void) {
+
+	unsigned int mcgsrl;
+	unsigned int mcgsth;
+	unsigned long temp;
+	double dramclock;
+	double coef2;
+	int col;
+	char *name;
+
+	/* First, got the FID */
+	rdmsr(0x0c0010015, mcgsrl, mcgsth);
+	temp = (mcgsrl >> 24)&0x0F;
+
+	if ((mcgsrl >> 19)&1) { coef2 = athloncoef2[temp]; }
+	else { coef2 = athloncoef[temp]; }
+
+	if (coef2 == 0) { coef2 = 1; };
+
+	/* Compute the final FSB Clock */
+	dramclock = (extclock /1000) / coef2;
+
+	/* Print the controller name */
+	col = COL_SPEC;
+	cprint(LINE_CPU, col, "Chipset:               ");	
+	col += 9;
+	/* Print the controller name */
+	name = controllers[ctrl.index].name;
+	cprint(LINE_CPU, col, name);
+	/* Now figure out how much I just printed */
+	temp = 20;
+	while(name[temp - 20] != '\0') {
+		col++;
+		temp++;
+	}
+	
+	if(temp < 36){
+		cprint(LINE_CPU, col +1, "- FSB : ");
+		col += 9;
+		dprint(LINE_CPU, col, dramclock, 3,0);
+		col += 3;
+	}
+
+
+}
+
+static void poll_fsb_nf2(void) {
+
+	unsigned int mcgsrl;
+	unsigned int mcgsth;
+	unsigned long temp, mempll;
+	double dramclock, fsb;
+	double mem_m, mem_n;
+	float coef;
+	coef = 10;
+
+	/* First, got the FID */
+	rdmsr(0x0c0010015, mcgsrl, mcgsth);
+	temp = (mcgsrl >> 24)&0x0F;
+
+	if ((mcgsrl >> 19)&1) { coef = athloncoef2[temp]; }
+	else { coef = athloncoef[temp]; }
+
+	/* Get the coef (COEF = N/M) - Here is for Crush17 */
+	pci_conf_read(0, 0, 3, 0x70, 4, &mempll);
+	mem_m = (mempll&0x0F);
+	mem_n = ((mempll >> 4) & 0x0F);
+
+	/* If something goes wrong, the chipset is probably a Crush18 */
+	if ( mem_m == 0 || mem_n == 0 ) {
+		pci_conf_read(0, 0, 3, 0x7C, 4, &mempll);
+		mem_m = (mempll&0x0F);
+		mem_n = ((mempll >> 4) & 0x0F);
+	}
+
+	/* Compute the final FSB Clock */
+	dramclock = ((extclock /1000) / coef) * (mem_n/mem_m);
+	fsb = ((extclock /1000) / coef);
+
+	/* ...and print */
+	print_cpu_line(dramclock, fsb, 1);
+
+}
+
+static void poll_fsb_us15w(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long msr;
+
+	/* Find dramratio */
+	/* D0 MsgRd, 05 Zunit, 03 MSR */
+	pci_conf_write(0, 0, 0, 0xD0, 4, 0xD0050300 );		
+	pci_conf_read(0, 0, 0, 0xD4, 4, &msr );		
+	fsb = ( msr >> 3 ) & 1;
+
+	dramratio = 0.5; 
+
+	// Compute RAM Frequency
+	if (( msr >> 3 ) & 1) {
+		fsb = 533;
+	} else {
+		fsb = 400;
+	}
+	
+/*
+	switch (( msr >> 0 ) & 7) {
+		case 0:
+			gfx = 100;
+			break;
+		case 1:
+			gfx = 133;
+			break;
+		case 2:
+			gfx = 150;
+			break;
+		case 3:
+			gfx = 178;
+			break;
+		case 4:
+			gfx = 200;
+			break;
+		case 5:
+			gfx = 266;
+			break;
+		default:
+			gfx = 0;
+			break;
+	}	
+	*/
+	
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 1);
+
+}
+
+static void poll_fsb_nhm(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mc_dimm_clk_ratio;
+	float coef = getNHMmultiplier();
+	//unsigned long qpi_pll_status;
+	//float qpi_speed;
+
+
+	fsb = ((extclock /1000) / coef);
+	
+	/* Print QPI Speed (if ECC not supported) */
+	/*
+	if(ctrl.mode == ECC_NONE && cpu_id.vers.bits.model == 10) {
+		pci_conf_read(nhm_bus, 2, 1, 0x50, 2, &qpi_pll_status);
+		qpi_speed = (qpi_pll_status & 0x7F) * ((extclock / 1000) / coef) * 2;
+		cprint(LINE_CPU+5, col +1, "/ QPI : ");
+		col += 9;
+		dprint(LINE_CPU+5, col, qpi_speed/1000, 1,0);
+		col += 1;
+		cprint(LINE_CPU+5, col, ".");
+		col += 1;		
+		qpi_speed = ((qpi_speed / 1000) - (int)(qpi_speed / 1000)) * 10;
+		dprint(LINE_CPU+5, col, qpi_speed, 1,0);
+		col += 1;		
+		cprint(LINE_CPU+5, col +1, "GT/s");
+		col += 5;	
+	}
+	*/
+	
+	/* Get the clock ratio */
+	
+	pci_conf_read(nhm_bus, 3, 4, 0x54, 2, &mc_dimm_clk_ratio);
+	dramratio = (mc_dimm_clk_ratio & 0x1F);
+	
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio / 2;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+static void poll_fsb_nhm32(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long mc_dimm_clk_ratio;
+	float coef = getNHMmultiplier();
+	//unsigned long qpi_pll_status;
+	//float qpi_speed;
+
+	fsb = ((extclock /1000) / coef);
+
+	/* Print QPI Speed (if ECC not supported) */
+	/*
+	if(ctrl.mode == ECC_NONE && cpu_id.vers.bits.model == 12) {
+		pci_conf_read(nhm_bus, 2, 1, 0x50, 2, &qpi_pll_status);
+		qpi_speed = (qpi_pll_status & 0x7F) * ((extclock / 1000) / coef) * 2;
+		cprint(LINE_CPU+5, col +1, "/ QPI : ");
+		col += 9;
+		dprint(LINE_CPU+5, col, qpi_speed/1000, 1,0);
+		col += 1;
+		cprint(LINE_CPU+5, col, ".");
+		col += 1;		
+		qpi_speed = ((qpi_speed / 1000) - (int)(qpi_speed / 1000)) * 10;
+		dprint(LINE_CPU+5, col, qpi_speed, 1,0);
+		col += 1;		
+		cprint(LINE_CPU+5, col +1, "GT/s");
+		col += 5;	
+	}
+	*/
+	
+	/* Get the clock ratio */
+	
+	pci_conf_read(nhm_bus, 3, 4, 0x50, 2, &mc_dimm_clk_ratio);
+	dramratio = (mc_dimm_clk_ratio & 0x1F);
+	
+	// Compute RAM Frequency
+	fsb = ((extclock / 1000) / coef);
+	dramclock = fsb * dramratio / 2;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+static void poll_fsb_wmr(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long dev0;
+	float coef = getNHMmultiplier();
+	long *ptr;
+	
+	fsb = ((extclock / 1000) / coef);
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0x2C20);
+	dramratio = 1;
+	
+	/* Get the clock ratio */
+	dramratio = 0.25 * (float)(*ptr & 0x1F);
+	
+	// Compute RAM Frequency
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 3);
+
+}
+
+static void poll_fsb_snb(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long dev0;
+	float coef = getSNBmultiplier();
+	long *ptr;
+	
+	fsb = ((extclock / 1000) / coef);
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0x5E04);
+	dramratio = 1;
+	
+	/* Get the clock ratio */
+	dramratio = (float)(*ptr & 0x1F) * (133.34f / 100.0f);
+	
+	// Compute RAM Frequency
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 3);
+	
+}
+
+static void poll_fsb_ivb(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long dev0, mchcfg;
+	float coef = getSNBmultiplier();
+	long *ptr;
+	
+	fsb = ((extclock / 1000) / coef);
+
+	/* Find dramratio */
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	ptr=(long*)(dev0+0x5E04);
+	mchcfg = *ptr & 0xFFFF;
+	dramratio = 1;
+	
+	/* Get the clock ratio */
+	switch((mchcfg >> 8) & 0x01)
+	{
+		case 0x0:
+			dramratio = (float)(*ptr & 0x1F) * (133.34f / 100.0f);
+			break;
+		case 0x1:
+			dramratio = (float)(*ptr & 0x1F) * (100.0f / 100.0f);	
+			break;
+	}
+	
+	// Compute RAM Frequency
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 3);
+	
+}
+
+static void poll_fsb_snbe(void) {
+
+	double dramclock, dramratio, fsb;
+	unsigned long dev0;
+	float coef = getSNBmultiplier();
+	
+	fsb = ((extclock / 1000) / coef);
+
+	/* Find dramratio */
+	pci_conf_read( 0xFF, 10, 1, 0x98, 4, &dev0);
+	dev0 &= 0xFFFFFFFF;
+	dramratio = 1;
+	
+	/* Get the clock ratio */
+	dramratio = (float)(dev0 & 0x3F) * (66.67f / 100.0f);
+	
+	// Compute RAM Frequency
+	dramclock = fsb * dramratio;
+
+	// Print DRAM Freq
+	print_cpu_line(dramclock, fsb, 3);
+	
+	
+
+}
+
+/* ------------------ Here the code for Timings detection ------------------ */
+/* ------------------------------------------------------------------------- */
+
+static void poll_timings_nf4ie(void) {
+
+
+	ulong regd0, reg8c, reg9c, reg80;
+	int cas, rcd, rp, ras, chan;
+
+	//Now, read Registers
+	pci_conf_read( 0, 1, 1, 0xD0, 4, &regd0);
+	pci_conf_read( 0, 1, 1, 0x80, 1, &reg80);
+	pci_conf_read( 0, 1, 0, 0x8C, 4, &reg8c);
+	pci_conf_read( 0, 1, 0, 0x9C, 4, &reg9c);
+
+	// Then, detect timings
+	cas = (regd0 >> 4) & 0x7;
+	rcd = (reg8c >> 24) & 0xF;
+	rp = (reg9c >> 8) & 0xF;
+	ras = (reg8c >> 16) & 0x3F;
+	
+	if (reg80 & 0x3) {
+		chan = 2;
+	} else {
+		chan = 1;
+	}
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_i875(void) {
+
+	ulong dev6, dev62;
+	ulong temp;
+	float cas;
+	int rcd, rp, ras, chan;
+	long *ptr, *ptr2;
+
+	pci_conf_read( 0, 6, 0, 0x40, 4, &dev62);
+	ptr2=(long*)(dev6+0x68);
+
+	/* Read the MMR Base Address & Define the pointer */
+	pci_conf_read( 0, 6, 0, 0x10, 4, &dev6);
+
+	/* Now, we could check some additionnals timings infos) */
+	ptr=(long*)(dev6+0x60);
+	// CAS Latency (tCAS)
+	temp = ((*ptr >> 5)& 0x3);
+	if (temp == 0x0) { cas = 2.5; } else if (temp == 0x1) { cas = 2; } else { cas = 3; }
+
+	// RAS-To-CAS (tRCD)
+	temp = ((*ptr >> 2)& 0x3);
+	if (temp == 0x0) { rcd = 4; } else if (temp == 0x1) { rcd = 3; } else { rcd = 2; }
+
+	// RAS Precharge (tRP)
+	temp = (*ptr&0x3);
+	if (temp == 0x0) { rp = 4; } else if (temp == 0x1) { rp = 3; } else { rp = 2; }
+
+	// RAS Active to precharge (tRAS)
+	temp = ((*ptr >> 7)& 0x7);
+	ras = 10 - temp;
+
+	// Print 64 or 128 bits mode
+	if (((*ptr2 >> 21)&3) > 0) { 
+		chan = 2;
+	} else {
+		chan = 1;
+	}
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_i925(void) {
+
+	// Thanks for CDH optis
+	float cas;
+	int rcd,rp,ras,chan;
+	ulong dev0, drt, drc, dcc, idetect, temp;
+	long *ptr;
+
+	//Now, read MMR Base Address
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	pci_conf_read( 0, 0, 0, 0x02, 2, &idetect);
+	dev0 &= 0xFFFFC000;
+
+	//Set pointer for DRT
+	ptr=(long*)(dev0+0x114);
+	drt = *ptr & 0xFFFFFFFF;
+
+	//Set pointer for DRC
+	ptr=(long*)(dev0+0x120);
+	drc = *ptr & 0xFFFFFFFF;
+
+	//Set pointer for DCC
+	ptr=(long*)(dev0+0x200);
+	dcc = *ptr & 0xFFFFFFFF;
+
+	// CAS Latency (tCAS)
+	temp = ((drt >> 8)& 0x3);
+
+	if ((drc & 3) == 2){
+		// Timings DDR-II
+		if      (temp == 0x0) { cas = 5; }
+		else if (temp == 0x1) { cas = 4; }
+		else if (temp == 0x2) { cas = 3; }
+		else		      { cas = 6; }
+	} else {
+		// Timings DDR-I
+		if      (temp == 0x0) { cas = 3; }
+		else if (temp == 0x1) { cas = 2.5f;}
+		else		      { cas = 2; }
+	}
+
+	// RAS-To-CAS (tRCD)
+	rcd = ((drt >> 4)& 0x3)+2;
+
+	// RAS Precharge (tRP)
+	rp = (drt&0x3)+2;
+
+	// RAS Active to precharge (tRAS)
+	// If Lakeport, than change tRAS computation (Thanks to CDH, again)
+	if (idetect > 0x2700)
+		ras = ((drt >> 19)& 0x1F);
+	else
+		ras = ((drt >> 20)& 0x0F);
+
+
+	temp = (dcc&0x3);
+	if      (temp == 1) { chan = 2; }
+	else if (temp == 2) { chan = 2; }
+	else		    { chan = 1; }
+		
+	print_ram_line(cas, rcd, rp, ras, chan);
+
+}
+
+static void poll_timings_i965(void) {
+
+	// Thanks for CDH optis
+	ulong dev0, c0ckectrl, c1ckectrl, offset;
+	ulong ODT_Control_Register, Precharge_Register, ACT_Register, Read_Register;
+	long *ptr;
+	int rcd,rp,ras,chan;
+	float cas;
+
+	//Now, read MMR Base Address
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+
+	ptr = (long*)(dev0+0x260);
+	c0ckectrl = *ptr & 0xFFFFFFFF;	
+
+	ptr = (long*)(dev0+0x660);
+	c1ckectrl = *ptr & 0xFFFFFFFF;
+	
+	// If DIMM 0 not populated, check DIMM 1
+	((c0ckectrl) >> 20 & 0xF)?(offset = 0):(offset = 0x400);
+
+	ptr = (long*)(dev0+offset+0x29C);
+	ODT_Control_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x250);	
+	Precharge_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x252);
+	ACT_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x258);
+	Read_Register = *ptr & 0xFFFFFFFF;
+
+
+	// CAS Latency (tCAS)
+	cas = ((ODT_Control_Register >> 17)& 7) + 3.0f;
+
+	// RAS-To-CAS (tRCD)
+	rcd = (Read_Register >> 16) & 0xF;
+
+	// RAS Precharge (tRP)
+	rp = (ACT_Register >> 13) & 0xF;
+
+	// RAS Active to precharge (tRAS)
+	ras = (Precharge_Register >> 11) & 0x1F;
+
+	if ((c0ckectrl >> 20 & 0xF) && (c1ckectrl >> 20 & 0xF)) { 
+		chan = 2; 
+	}	else {
+		chan = 1; 
+	}
+
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_im965(void) {
+
+	// Thanks for CDH optis
+	ulong dev0, c0ckectrl, c1ckectrl, offset;
+	ulong ODT_Control_Register, Precharge_Register;
+	long *ptr;
+	int rcd,rp,ras,chan;
+	float cas;
+	
+	//Now, read MMR Base Address
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+
+	ptr = (long*)(dev0+0x1200);
+	c0ckectrl = *ptr & 0xFFFFFFFF;	
+
+	ptr = (long*)(dev0+0x1300);
+	c1ckectrl = *ptr & 0xFFFFFFFF;
+	
+	// If DIMM 0 not populated, check DIMM 1
+	((c0ckectrl) >> 20 & 0xF)?(offset = 0):(offset = 0x100);
+
+	ptr = (long*)(dev0+offset+0x121C);
+	ODT_Control_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x1214);	
+	Precharge_Register = *ptr & 0xFFFFFFFF;
+
+	// CAS Latency (tCAS)
+	cas = ((ODT_Control_Register >> 23)& 7) + 3.0f;
+
+	// RAS-To-CAS (tRCD)
+	rcd = ((Precharge_Register >> 5)& 7) + 2.0f;
+
+	// RAS Precharge (tRP)
+	rp = (Precharge_Register & 7) + 2.0f;
+
+	// RAS Active to precharge (tRAS)
+	ras = (Precharge_Register >> 21) & 0x1F;
+
+
+	if ((c0ckectrl >> 20 & 0xF) && (c1ckectrl >> 20 & 0xF)) { 
+		chan = 2;
+	}	else {
+		chan = 1;
+	}
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_p35(void) {
+
+	// Thanks for CDH optis
+	float cas;
+	int rcd, rp, ras, chan;
+	ulong dev0, Device_ID, c0ckectrl, c1ckectrl, offset;
+	ulong ODT_Control_Register, Precharge_Register, ACT_Register, Read_Register;
+	long *ptr;
+	
+	pci_conf_read( 0, 0, 0, 0x02, 2, &Device_ID);
+	Device_ID &= 0xFFFF;
+
+	//Now, read MMR Base Address
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+
+	ptr = (long*)(dev0+0x260);
+	c0ckectrl = *ptr & 0xFFFFFFFF;	
+
+	ptr = (long*)(dev0+0x660);
+	c1ckectrl = *ptr & 0xFFFFFFFF;
+	
+	// If DIMM 0 not populated, check DIMM 1
+	((c0ckectrl) >> 20 & 0xF)?(offset = 0):(offset = 0x400);
+
+	ptr = (long*)(dev0+offset+0x265);
+	ODT_Control_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x25D);	
+	Precharge_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x252);
+	ACT_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x258);
+	Read_Register = *ptr & 0xFFFFFFFF;
+
+
+	// CAS Latency (tCAS)
+	if(Device_ID > 0x2E00 && imc_type != 8) {
+		cas = ((ODT_Control_Register >> 8)& 0x3F) - 6.0f;
+	} else {
+		cas = ((ODT_Control_Register >> 8)& 0x3F) - 9.0f;
+	}
+
+	// RAS-To-CAS (tRCD)
+	rcd = (Read_Register >> 17) & 0xF;
+
+	// RAS Precharge (tRP)
+	rp = (ACT_Register >> 13) & 0xF;
+
+	// RAS Active to precharge (tRAS)
+	ras = Precharge_Register & 0x3F;
+	
+	if ((c0ckectrl >> 20 & 0xF) && (c1ckectrl >> 20 & 0xF)) { 
+		chan = 2; 
+	}	else {
+		chan = 1; 
+	}
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_wmr(void) {
+
+	float cas;
+	int rcd, rp, ras, chan;
+	ulong dev0, c0ckectrl, c1ckectrl, offset;
+	ulong ODT_Control_Register, Precharge_Register, ACT_Register, Read_Register, MRC_Register;
+	long *ptr;
+
+	//Now, read MMR Base Address
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+
+	ptr = (long*)(dev0+0x260);
+	c0ckectrl = *ptr & 0xFFFFFFFF;	
+
+	ptr = (long*)(dev0+0x660);
+	c1ckectrl = *ptr & 0xFFFFFFFF;
+	
+	// If DIMM 0 not populated, check DIMM 1
+	((c0ckectrl) >> 20 & 0xF)?(offset = 0):(offset = 0x400);
+
+	ptr = (long*)(dev0+offset+0x265);
+	ODT_Control_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x25D);	
+	Precharge_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x252);
+	ACT_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x258);
+	Read_Register = *ptr & 0xFFFFFFFF;
+
+	ptr = (long*)(dev0+offset+0x240);
+	MRC_Register = *ptr & 0xFFFFFFFF;
+
+	// CAS Latency (tCAS)
+	if(MRC_Register & 0xF) {
+		cas = (MRC_Register & 0xF) + 3.0f;
+	} else {
+		cas = ((ODT_Control_Register >> 8)& 0x3F) - 5.0f;
+	}
+
+	// RAS-To-CAS (tRCD)
+	rcd = (Read_Register >> 17) & 0xF;
+
+	// RAS Precharge (tRP)
+	rp = (ACT_Register >> 13) & 0xF;
+
+	// RAS Active to precharge (tRAS)
+	ras = Precharge_Register & 0x3F;
+	
+	if ((c0ckectrl >> 20 & 0xF) && (c1ckectrl >> 20 & 0xF)) { 
+		chan = 2; 
+	}	else {
+		chan = 1; 
+	}
+
+	print_ram_line(cas, rcd, rp, ras, chan);
+
+}
+
+static void poll_timings_snb(void) {
+
+	float cas;
+	int rcd, rp, ras, chan;
+	ulong dev0, offset;
+	ulong IMC_Register, MCMain0_Register, MCMain1_Register;
+	long *ptr;
+
+	//Now, read MMR Base Address
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+	
+	offset = 0x0000;
+
+	ptr = (long*)(dev0+offset+0x4000);
+	IMC_Register = *ptr & 0xFFFFFFFF;
+
+	// CAS Latency (tCAS)
+	cas = (float)((IMC_Register >> 8) & 0x0F);
+
+	// RAS-To-CAS (tRCD)
+	rcd = IMC_Register & 0x0F;
+
+	// RAS Precharge (tRP)
+	rp = (IMC_Register >> 4) & 0x0F;
+
+	// RAS Active to precharge (tRAS)
+	ras = (IMC_Register >> 16) & 0xFF;
+	
+	// Channels
+	ptr = (long*)(dev0+offset+0x5004);
+	MCMain0_Register = *ptr & 0xFFFF;
+	ptr = (long*)(dev0+offset+0x5008);
+	MCMain1_Register = *ptr & 0xFFFF;
+	
+	if(MCMain0_Register == 0 || MCMain1_Register == 0) {
+		chan = 1; 
+	} else {
+		chan = 2; 
+	}
+	
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_hsw(void) {
+
+	float cas;
+	int rcd, rp, ras, chan;
+	ulong dev0, offset = 0;
+	ulong IMC_Register, MCMain0_Register, MCMain1_Register;
+	long *ptr;
+
+	//Now, read MMR Base Address
+	pci_conf_read( 0, 0, 0, 0x48, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+
+	// Channels
+	ptr = (long*)(dev0+offset+0x5004);
+	MCMain0_Register = *ptr & 0xFFFF;
+	
+	ptr = (long*)(dev0+offset+0x5008);
+	MCMain1_Register = *ptr & 0xFFFF;
+	
+	if(MCMain0_Register && MCMain1_Register) {
+		chan = 2; 
+	} else {
+		chan = 1;
+	}
+	
+	if(MCMain0_Register) { offset = 0x0000; } else {	offset = 0x0400; }
+	
+	// CAS Latency (tCAS)
+	ptr = (long*)(dev0+offset+0x4014);
+	IMC_Register = *ptr & 0xFFFFFFFF;
+	cas = (float)(IMC_Register & 0x1F);
+
+	ptr = (long*)(dev0+offset+0x4000);
+	IMC_Register = *ptr & 0xFFFFFFFF;
+
+	// RAS-To-CAS (tRCD)
+	rcd = IMC_Register & 0x1F;
+
+	// RAS Precharge (tRP)
+	rp = (IMC_Register >> 5) & 0x1F;
+
+	// RAS Active to precharge (tRAS)
+	ras = (IMC_Register >> 10) & 0x3F;
+	
+
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_snbe(void) {
+
+	float cas;
+	int rcd, rp, ras;
+	int nb_channel = 0, current_channel = 0;
+	ulong temp, IMC_Register;
+	long *ptr;
+	
+	//Read Channel #1
+	pci_conf_read(0xFF, 16, 2, 0x80, 4, &temp);
+	temp &= 0x3F;
+	if(temp != 0xB) { current_channel = 0; nb_channel++; }
+
+	//Read Channel #2
+	pci_conf_read(0xFF, 16, 3, 0x80, 4, &temp);
+	temp &= 0x3F;
+	if(temp != 0xB) { current_channel = 1; nb_channel++; }
+
+	//Read Channel #3
+	pci_conf_read(0xFF, 16, 6, 0x80, 4, &temp);
+	temp &= 0x3F;
+	if(temp != 0xB) { current_channel = 4; nb_channel++; }
+	
+	//Read Channel #4
+	pci_conf_read(0xFF, 16, 7, 0x80, 4, &temp);
+	temp &= 0x3F;
+	if(temp != 0xB) { current_channel = 5; nb_channel++; }
+
+
+	pci_conf_read(0, 5, 0, 0x84, 4, &temp);
+	ptr = (long*)((temp & 0xFC000000) + (MAKE_PCIE_ADDRESS(0xFF,16,current_channel) | 0x200));
+	IMC_Register = *ptr & 0xFFFFFFFF;
+
+	// CAS Latency (tCAS)
+	cas = (float)((IMC_Register  >> 9) & 0x1F);
+
+	// RAS-To-CAS (tRCD)
+	rcd = IMC_Register & 0x1F;
+
+	// RAS Precharge (tRP)
+	rp = (IMC_Register >> 5) & 0x0F;
+
+	// RAS Active to precharge (tRAS)
+	ras = (IMC_Register >> 19) & 0x3F;
+	
+
+	print_ram_line(cas, rcd, rp, ras, nb_channel);
+
+}
+
+static void poll_timings_5400(void) {
+
+	// Thanks for CDH optis
+	ulong ambase, mtr1, mtr2, offset, mca;
+	long *ptr;
+	float cas;
+	int rcd, rp, ras, chan;
+	
+	//Hard-coded Ambase value (should not be realocated by software when using Memtest86+
+	ambase = 0xFE000000;
+  offset = mtr1 = mtr2 = 0;
+
+  // Will loop until a valid populated channel is found
+  // Bug  : DIMM 0 must be populated or it will fall in an endless loop  
+  while(((mtr2 & 0xF) < 3) || ((mtr2 & 0xF) > 6)) {
+		ptr = (long*)(ambase+0x378+offset);
+		mtr1 = *ptr & 0xFFFFFFFF;
+	
+		ptr = (long*)(ambase+0x37C+offset);	
+		mtr2 = *ptr & 0xFFFFFFFF;
+		offset += 0x8000;
+	}
+
+	pci_conf_read( 0, 16, 1, 0x58, 4, &mca);
+
+	//This chipset only supports FB-DIMM (Removed => too long)
+	//cprint(LINE_CPU+5, col +1, "- Type : FBD");
+
+	// Now, detect timings
+
+	// CAS Latency (tCAS)
+	cas = mtr2 & 0xF;
+
+	// RAS-To-CAS (tRCD)
+	rcd = 6 - ((mtr1 >> 10) & 3);
+
+	// RAS Precharge (tRP)
+	rp = 6 - ((mtr1 >> 8) & 3);
+
+	// RAS Active to precharge (tRAS)
+	ras = 16 - (3 * ((mtr1 >> 29) & 3)) + ((mtr1 >> 12) & 3);
+  if(((mtr1 >> 12) & 3) == 3 && ((mtr1 >> 29) & 3) == 2) { ras = 9; }
+
+
+	if ((mca >> 14) & 1) { 
+		chan = 1; 
+	}	else {
+		chan = 2; 
+	}
+
+	print_ram_line(cas, rcd, rp, ras, chan);
+
+}
+
+static void poll_timings_E7520(void) {
+
+	ulong drt, ddrcsr;
+	float cas;
+	int rcd, rp, ras, chan;
+
+	pci_conf_read( 0, 0, 0, 0x78, 4, &drt);
+	pci_conf_read( 0, 0, 0, 0x9A, 2, &ddrcsr);
+
+	cas = ((drt >> 2) & 3) + 2;
+	rcd = ((drt >> 10) & 1) + 3;
+	rp = ((drt >> 9) & 1) + 3;
+	ras = ((drt >> 14) & 3) + 11;
+	
+	if ((ddrcsr & 0xF) >= 0xC) {
+		chan = 2;
+	} else {
+		chan = 1;
+	}
+	
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+
+static void poll_timings_i855(void) {
+
+	ulong drt, temp;
+	float cas;
+	int rcd, rp, ras;
+	
+	pci_conf_read( 0, 0, 0, 0x78, 4, &drt);
+
+	/* Now, we could print some additionnals timings infos) */
+	cprint(LINE_CPU+6, col2 +1, "/ CAS : ");
+	col2 += 9;
+
+	// CAS Latency (tCAS)
+	temp = ((drt >> 4)&0x1);
+	if (temp == 0x0) { cas = 2.5;  }
+	else { cas = 2; }
+
+	// RAS-To-CAS (tRCD)
+	temp = ((drt >> 2)& 0x1);
+	if (temp == 0x0) { rcd = 3; }
+	else { rcd = 2; }
+
+	// RAS Precharge (tRP)
+	temp = (drt&0x1);
+	if (temp == 0x0) { rp = 3 ; }
+	else { rp = 2; }
+
+	// RAS Active to precharge (tRAS)
+	temp = 7-((drt >> 9)& 0x3);
+	if (temp == 0x0) { ras = 7; }
+	if (temp == 0x1) { ras = 6; }
+	if (temp == 0x2) { ras = 5; }
+	
+	print_ram_line(cas, rcd, rp, ras, 1);
+
+}
+
+static void poll_timings_E750x(void) {
+
+	ulong drt, drc, temp;
+	float cas;
+	int rcd, rp, ras, chan;
+
+	pci_conf_read( 0, 0, 0, 0x78, 4, &drt);
+	pci_conf_read( 0, 0, 0, 0x7C, 4, &drc);
+
+	if ((drt >> 4) & 1) { cas = 2; } else { cas = 2.5; };
+	if ((drt >> 1) & 1) { rcd = 2; } else { rcd = 3; };
+	if (drt & 1) { rp = 2; } else { rp = 3; };
+
+	temp = ((drt >> 9) & 3);
+	if (temp == 2) { ras = 5; } else if (temp == 1) { ras = 6; } else { ras = 7; }
+
+	if (((drc >> 22)&1) == 1) {
+		chan = 2;
+	} else {
+		chan = 1;
+	}
+
+	print_ram_line(cas, rcd, rp, ras, chan);
+
+}
+
+static void poll_timings_i852(void) {
+
+	ulong drt, temp;
+	float cas;
+	int rcd, rp, ras;
+	
+	pci_conf_read( 0, 0, 1, 0x60, 4, &drt);
+
+	/* Now, we could print some additionnals timings infos) */
+	cprint(LINE_CPU+6, col2 +1, "/ CAS : ");
+	col2 += 9;
+
+	// CAS Latency (tCAS)
+	temp = ((drt >> 5)&0x1);
+	if (temp == 0x0) { cas = 2.5;  }
+	else { cas = 2; }
+
+	// RAS-To-CAS (tRCD)
+	temp = ((drt >> 2)& 0x3);
+	if (temp == 0x0) { rcd = 4; }
+	if (temp == 0x1) { rcd = 3; }
+	else { rcd = 2; }
+
+	// RAS Precharge (tRP)
+	temp = (drt&0x3);
+	if (temp == 0x0) { rp = 4; }
+	if (temp == 0x1) { rp = 3; }
+	else { rp = 2; }
+
+	// RAS Active to precharge (tRAS)
+	temp = ((drt >> 9)& 0x3);
+	if (temp == 0x0) { ras = 8; }
+	if (temp == 0x1) { ras = 7; }
+	if (temp == 0x2) { ras = 6; }
+	if (temp == 0x3) { ras = 5; }
+
+	print_ram_line(cas, rcd, rp, ras, 1);
+
+}
+
+static void poll_timings_amd64(void) {
+
+	ulong dramtlr, dramclr;
+	int temp, chan;
+	float tcas;
+	int trcd, trp, tras ;
+
+	pci_conf_read(0, 24, 2, 0x88, 4, &dramtlr);
+	pci_conf_read(0, 24, 2, 0x90, 4, &dramclr);
+	
+	if (cpu_id.vers.bits.extendedModel >= 4) {
+		/* NEW K8 0Fh Family 90 nm (DDR2) */
+
+			// CAS Latency (tCAS)
+			tcas = (dramtlr & 0x7) + 1;
+		
+			// RAS-To-CAS (tRCD)
+			trcd = ((dramtlr >> 4) & 0x3) + 3;
+		
+			// RAS Precharge (tRP)
+			trp = ((dramtlr >> 8) & 0x3) + 3;
+		
+			// RAS Active to precharge (tRAS)
+			tras = ((dramtlr >> 12) & 0xF) + 3;
+		
+			// Print 64 or 128 bits mode
+			if ((dramclr >> 11)&1) {
+				chan = 2;
+			} else {
+				chan = 1;
+			}
+
+	} else {
+		/* OLD K8 (DDR1) */
+
+			// CAS Latency (tCAS)
+			temp = (dramtlr & 0x7);
+			if (temp == 0x1) { tcas = 2; }
+			if (temp == 0x2) { tcas = 3; }
+			if (temp == 0x5) { tcas = 2.5; }
+		
+			// RAS-To-CAS (tRCD)
+			trcd = ((dramtlr >> 12) & 0x7);
+		
+			// RAS Precharge (tRP)
+			trp = ((dramtlr >> 24) & 0x7);
+		
+			// RAS Active to precharge (tRAS)
+			tras = ((dramtlr >> 20) & 0xF);
+		
+			// Print 64 or 128 bits mode
+			if (((dramclr >> 16)&1) == 1) {
+				chan = 2;
+			} else {
+				chan = 1;
+			}
+	}
+	
+	print_ram_line(tcas, trcd, trp, tras, chan);
+	
+}
+
+static void poll_timings_k10(void) {
+
+	ulong dramtlr, dramclr, dramchr, dramchrb;
+	ulong offset = 0;
+	int cas, rcd, rp, ras, chan;
+	
+	pci_conf_read(0, 24, 2, 0x94, 4, &dramchr);
+	pci_conf_read(0, 24, 2, 0x194, 4, &dramchrb);
+	
+	if(((dramchr>>14) & 0x1) || ((dramchr>>14) & 0x1)) { chan = 1; } else { chan = 2; }
+	
+	// If Channel A not enabled, switch to channel B
+	if(((dramchr>>14) & 0x1))
+	{
+		offset = 0x100;
+		pci_conf_read(0, 24, 2, 0x94+offset, 4, &dramchr);	
+	}
+
+	pci_conf_read(0, 24, 2, 0x88+offset, 4, &dramtlr);
+	pci_conf_read(0, 24, 2, 0x110, 4, &dramclr);
+	
+	// CAS Latency (tCAS)
+	if(((dramchr >> 8)&1) || ((dramchr & 0x7) == 0x4)){
+		// DDR3 or DDR2-1066
+		cas = (dramtlr & 0xF) + 4;
+		rcd = ((dramtlr >> 4) & 0x7) + 5;
+		rp = ((dramtlr >> 7) & 0x7) + 5;
+	  ras = ((dramtlr >> 12) & 0xF) + 15;	
+	} else {
+	// DDR2-800 or less
+		cas = (dramtlr & 0xF) + 1;
+		rcd = ((dramtlr >> 4) & 0x3) + 3;
+		rp = ((dramtlr >> 8) & 0x3) + 3;
+	  ras = ((dramtlr >> 12) & 0xF) + 3;
+	}
+		
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_k12(void) {
+
+	ulong dramt0, dramlow, dimma, dimmb;
+	int cas, rcd, rp, ras, chan = 0;
+	
+	pci_conf_read(0, 24, 2, 0x94, 4, &dimma);
+	pci_conf_read(0, 24, 2, 0x194, 4, &dimmb);
+
+	if(((dimma >> 14) & 0x1) == 0) 
+	{ 
+		chan++; 
+		pci_conf_read(0, 24, 2, 0x88, 4, &dramlow); 
+		pci_conf_write(0, 24, 2, 0xF0, 4, 0x00000040);
+		pci_conf_read(0, 24, 2, 0xF4, 4, &dramt0);	
+	}
+	
+	if(((dimmb >> 14) & 0x1) == 0) 
+	{ 
+		chan++; 
+		pci_conf_read(0, 24, 2, 0x188, 4, &dramlow); 
+		pci_conf_write(0, 24, 2, 0x1F0, 4, 0x00000040);
+		pci_conf_read(0, 24, 2, 0x1F4, 4, &dramt0);	
+	}
+
+	cas = (dramlow & 0xF) + 4;
+	rcd = (dramt0 & 0xF) + 5;
+	rp = ((dramt0 >> 8) & 0xF) + 5;
+  ras = ((dramt0 >> 16) & 0x1F) + 15;
+	
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+
+static void poll_timings_k14(void) {
+
+	ulong dramt0, dramlow;
+	int cas, rcd, rp, ras;
+	
+	pci_conf_read(0, 24, 2, 0x88, 4, &dramlow); 
+	pci_conf_write(0, 24, 2, 0xF0, 4, 0x00000040);
+	pci_conf_read(0, 24, 2, 0xF4, 4, &dramt0);	
+
+	cas = (dramlow & 0xF) + 4;
+	rcd = (dramt0 & 0xF) + 5;
+	rp = ((dramt0 >> 8) & 0xF) + 5;
+  ras = ((dramt0 >> 16) & 0x1F) + 15;
+	
+	print_ram_line(cas, rcd, rp, ras, 1);
+}
+
+static void poll_timings_k15(void) {
+
+	ulong dramp1, dramp2, dimma, dimmb;
+	int cas, rcd, rp, ras, chan = 0;
+	
+	pci_conf_read(0, 24, 2, 0x94, 4, &dimma);
+	pci_conf_read(0, 24, 2, 0x194, 4, &dimmb);
+	if(((dimma>>14) & 0x1) || ((dimmb>>14) & 0x1)) { chan = 1; } else { chan = 2; }
+		
+	pci_conf_read(0, 24, 2, 0x200, 4, &dramp1); 
+	pci_conf_read(0, 24, 2, 0x204, 4, &dramp2); 
+	
+	cas = dramp1 & 0x1F;
+	rcd = (dramp1 >> 8) & 0x1F;
+	rp = (dramp1 >> 16) & 0x1F;
+  ras = (dramp1 >> 24) & 0x3F;
+	
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_k16(void) {
+
+	ulong dramt0, dramt1;
+	int cas, rcd, rp, rc, ras;
+	
+	pci_conf_read(0, 24, 2, 0x200, 4, &dramt0);
+	pci_conf_read(0, 24, 2, 0x204, 4, &dramt1);	
+
+	cas = (dramt0 & 0x1F);
+	rcd = ((dramt0 >> 8) & 0x1F);
+	rp = ((dramt0 >> 16) & 0x1F);
+	ras = ((dramt0 >> 24) & 0x3F);
+	
+	rc = (dramt1 & 0x3F);	
+	
+	print_ram_line(cas, rcd, rp, ras, 1);
+}
+
+static void poll_timings_EP80579(void) {
+
+	ulong drt1, drt2;
+	float cas;
+	int rcd, rp, ras;
+
+	pci_conf_read( 0, 0, 0, 0x78, 4, &drt1);
+	pci_conf_read( 0, 0, 0, 0x64, 4, &drt2);
+
+	cas = ((drt1 >> 3) & 0x7) + 3;
+	rcd = ((drt1 >> 9) & 0x7) + 3;
+	rp = ((drt1 >> 6) & 0x7) + 3;
+	ras = ((drt2 >> 28) & 0xF) + 8;
+
+	print_ram_line(cas, rcd, rp, ras, 0);
+}
+
+static void poll_timings_nf2(void) {
+
+	ulong dramtlr, dramtlr2, dramtlr3, temp;
+	ulong dimm1p, dimm2p, dimm3p;
+	float cas;
+	int rcd, rp, ras, chan;
+	
+	pci_conf_read(0, 0, 1, 0x90, 4, &dramtlr);
+	pci_conf_read(0, 0, 1, 0xA0, 4, &dramtlr2);
+	pci_conf_read(0, 0, 1, 0x84, 4, &dramtlr3);
+	pci_conf_read(0, 0, 2, 0x40, 4, &dimm1p);
+	pci_conf_read(0, 0, 2, 0x44, 4, &dimm2p);
+	pci_conf_read(0, 0, 2, 0x48, 4, &dimm3p);
+
+	// CAS Latency (tCAS)
+	temp = ((dramtlr2 >> 4) & 0x7);
+	if (temp == 0x2) { cas = 2; }
+	if (temp == 0x3) { cas = 3; }
+	if (temp == 0x6) { cas = 2.5; }
+		
+	// RAS-To-CAS (tRCD)
+	rcd = ((dramtlr >> 20) & 0xF);
+
+	// RAS Precharge (tRP)
+	rp = ((dramtlr >> 28) & 0xF);
+
+	// RAS Active to precharge (tRAS)
+	ras = ((dramtlr >> 15) & 0xF);
+
+	// Print 64 or 128 bits mode
+	// If DIMM1 & DIMM3 or DIMM1 & DIMM2 populated, than Dual Channel.
+
+	if ((dimm3p&1) + (dimm2p&1) == 2 || (dimm3p&1) + (dimm1p&1) == 2 ) {
+		chan = 2;
+	} else {
+		chan = 1;
+	}
+	print_ram_line(cas, rcd, rp, ras, chan);
+}
+
+static void poll_timings_us15w(void) {
+
+	// Thanks for CDH optis
+	ulong dtr;
+	float cas;
+	int rcd, rp;
+	
+	/* Find dramratio */
+	/* D0 MsgRd, 01 Dunit, 01 DTR */
+	pci_conf_write(0, 0, 0, 0xD0, 4, 0xD0010100 );		
+	pci_conf_read(0, 0, 0, 0xD4, 4, &dtr );		
+
+	// CAS Latency (tCAS)
+	cas = ((dtr >> 4) & 0x3) + 3;
+
+	// RAS-To-CAS (tRCD)
+	rcd = ((dtr >> 2) & 0x3) + 3;
+
+	// RAS Precharge (tRP)
+	rp = ((dtr >> 0) & 0x3) + 3;
+	
+	print_ram_line(cas, rcd, rp, 9, 1);
+
+}
+
+static void poll_timings_nhm(void) {
+
+	ulong mc_channel_bank_timing, mc_control, mc_channel_mrs_value;
+	float cas; 
+	int rcd, rp, ras, chan;
+	int fvc_bn = 4;
+
+	/* Find which channels are populated */
+	pci_conf_read(nhm_bus, 3, 0, 0x48, 2, &mc_control);		
+	mc_control = (mc_control >> 8) & 0x7;
+	
+	/* Get the first valid channel */
+	if(mc_control & 1) { 
+		fvc_bn = 4; 
+	} else if(mc_control & 2) { 
+		fvc_bn = 5; 
+	}	else if(mc_control & 4) { 
+		fvc_bn = 6; 
+	}
+
+	// Now, detect timings
+	// CAS Latency (tCAS) / RAS-To-CAS (tRCD) / RAS Precharge (tRP) / RAS Active to precharge (tRAS)
+	pci_conf_read(nhm_bus, fvc_bn, 0, 0x88, 4, &mc_channel_bank_timing);	
+	pci_conf_read(nhm_bus, fvc_bn, 0, 0x70, 4, &mc_channel_mrs_value);	
+	cas = ((mc_channel_mrs_value >> 4) & 0xF ) + 4.0f;
+	rcd = (mc_channel_bank_timing >> 9) & 0xF; 
+	ras = (mc_channel_bank_timing >> 4) & 0x1F; 
+	rp = mc_channel_bank_timing & 0xF;
+
+	// Print 1, 2 or 3 Channels
+	if (mc_control == 1 || mc_control == 2 || mc_control == 4 ) {
+		chan = 1;
+	} else if (mc_control == 7) {
+		chan = 3;
+	} else {
+		chan = 2;	
+	}
+	print_ram_line(cas, rcd, rp, ras, chan);
+	
+}
+
+static void poll_timings_ct(void) 
+{
+
+	unsigned long mcr,mdr;
+	float cas; 
+	int rcd, rp, ras;
+	
+	/* Build the MCR Message*/
+	mcr = (0x10 << 24); // 10h = Read - 11h = Write
+	mcr += (0x01 << 16); // DRAM Registers located on port 01h
+	mcr += (0x01 << 8); // DRP = 00h, DTR0 = 01h, DTR1 = 02h, DTR2 = 03h
+	mcr &= 0xFFFFFFF0; // bit 03:00 RSVD
+	
+	/* Send Message to GMCH */
+	pci_conf_write(0, 0, 0, 0xD0, 4, mcr);	
+	
+	/* Read Answer from Sideband bus */
+	pci_conf_read(0, 0, 0, 0xD4, 4, &mdr);			
+
+	// CAS Latency (tCAS)
+	cas = ((mdr >> 12)& 0x7) + 5.0f;
+
+	// RAS-To-CAS (tRCD)
+	rcd = ((mdr >> 8)& 0x7) + 5;
+
+	// RAS Precharge (tRP)
+	rp = ((mdr >> 4)& 0x7) + 5;
+
+	// RAS is in DTR1. Read Again.
+	mcr = 0x10010200; // Quick Mode ! Awesome !
+	pci_conf_write(0, 0, 0, 0xD0, 4, mcr);	
+	pci_conf_read(0, 0, 0, 0xD4, 4, &mdr);	
+			
+	// RAS Active to precharge (tRAS)		
+	ras = (mdr >> 20) & 0xF;
+
+	// Print
+	print_ram_line(cas, rcd, rp, ras, 1);
+
+}
+
+/* ------------------ Let's continue ------------------ */
+/* ---------------------------------------------------- */
+
+struct pci_memory_controller controllers[] = {
+	/* Default unknown chipset */
+	{ 0, 0, "","",                    0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+
+	/* AMD */
+	{ 0x1022, 0x7006, "AMD 751","SDRAM PC-100",   0, poll_fsb_nothing, poll_timings_nothing, setup_amd751, poll_nothing },
+	{ 0x1022, 0x700c, "AMD 762","DDR-SDRAM", 		  0, poll_fsb_nothing, poll_timings_nothing, setup_amd76x, poll_nothing },
+	{ 0x1022, 0x700e, "AMD 761","DDR-SDRAM", 		  0, poll_fsb_nothing, poll_timings_nothing, setup_amd76x, poll_nothing },
+
+	/* SiS */
+	{ 0x1039, 0x0600, "SiS 600","EDO/SDRAM",   			0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0620, "SiS 620","SDRAM",   					0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x5600, "SiS 5600","SDRAM",  					0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0645, "SiS 645","DDR-SDRAM",   			0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0646, "SiS 645DX","DDR-SDRAM", 			0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0630, "SiS 630","SDRAM",   					0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0650, "SiS 650","DDR-SDRAM",   			0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0651, "SiS 651","DDR-SDRAM",   			0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0730, "SiS 730","SDRAM",   					0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0735, "SiS 735","DDR-SDRAM",   			0, poll_fsb_amd32, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0740, "SiS 740","DDR-SDRAM",   			0, poll_fsb_amd32, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0745, "SiS 745","DDR-SDRAM",   			0, poll_fsb_amd32, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0748, "SiS 748","DDR-SDRAM",   			0, poll_fsb_amd32, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0655, "SiS 655","DDR-SDRAM",				0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0656, "SiS 656","DDR/DDR2-SDRAM",		0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0648, "SiS 648","DDR-SDRAM",   			0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0649, "SiS 649","DDR-SDRAM",   			0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0661, "SiS 661","DDR-SDRAM",   			0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0671, "SiS 671","DDR2-SDRAM",   		0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1039, 0x0672, "SiS 672","DDR2-SDRAM",   		0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },	
+
+	/* ALi */
+	{ 0x10b9, 0x1531, "ALi Aladdin 4","EDO/SDRAM", 0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x10b9, 0x1541, "ALi Aladdin 5","EDO/SDRAM", 0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x10b9, 0x1644, "ALi Aladdin M1644","SDRAM", 0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+
+	/* ATi */
+	{ 0x1002, 0x5830, "ATi Radeon 9100 IGP","DDR-SDRAM", 0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1002, 0x5831, "ATi Radeon 9100 IGP","DDR-SDRAM", 0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1002, 0x5832, "ATi Radeon 9100 IGP","DDR-SDRAM", 0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1002, 0x5833, "ATi Radeon 9100 IGP","DDR-SDRAM", 0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1002, 0x5954, "ATi Xpress 200","DDR-SDRAM", 		 0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1002, 0x5A41, "ATi Xpress 200","DDR-SDRAM",			 0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+
+	/* nVidia */
+	{ 0x10de, 0x01A4, "nVidia nForce","DDR-SDRAM", 0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x10de, 0x01E0, "nVidia nForce2 SPP","",		 0, poll_fsb_nf2, poll_timings_nf2, setup_nothing, poll_nothing },
+	{ 0x10de, 0x0071, "nForce4 SLI","", 					 0, poll_fsb_nf4ie, poll_timings_nf4ie, setup_nothing, poll_nothing },
+
+	/* VIA */
+	{ 0x1106, 0x0305, "VIA KT133/KT133A","SDRAM",    			0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0391, "VIA KX133","SDRAM",    						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0501, "VIA MVP4","EDO/SDRAM",    					0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0585, "VIA VP/VPX","EDO/SDRAM", 				  0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0595, "VIA VP2","EDO/SDRAM",  						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0597, "VIA VP3","EDO/SDRAM",  						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0598, "VIA MVP3","EDO/SDRAM",  						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0691, "VIA Apollo Pro 133(A)","SDRAM",  	0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0693, "VIA Apollo Pro+","SDRAM",  				0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0601, "VIA PLE133","SDRAM",  							0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x3099, "VIA KT266(A)/KT333","DDR-SDRAM",		0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x3189, "VIA KT400(A)/600","DDR-SDRAM",			0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0269, "VIA KT880","DDR-SDRAM", 						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x3205, "VIA KM400","DDR-SDRAM", 						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x3116, "VIA KM266","DDR-SDRAM", 						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x3156, "VIA KN266","DDR-SDRAM", 						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x3123, "VIA CLE266","DDR-SDRAM", 					0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x0198, "VIA PT800","DDR-SDRAM", 						0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x1106, 0x3258, "VIA PT880","DDR2-SDRAM", 					0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+
+	/* Serverworks */
+	{ 0x1166, 0x0008, "CNB20HE","SDRAM",   0, poll_fsb_nothing, poll_timings_nothing, setup_cnb20, poll_nothing },
+	{ 0x1166, 0x0009, "CNB20LE","SDRAM",   0, poll_fsb_nothing, poll_timings_nothing, setup_cnb20, poll_nothing },
+
+	/* Intel */
+	{ 0x8086, 0x1130, "Intel i815","SDRAM",  		0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x122d, "Intel i430FX","EDO DRAM",0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x1235, "Intel i430MX","EDO DRAM",0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x1237, "Intel i440FX","EDO DRAM",0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x1250, "Intel i430HX","EDO DRAM",0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x1A21, "Intel i840","RDRAM",  		0, poll_fsb_nothing, poll_timings_nothing, setup_i840, poll_nothing },
+	{ 0x8086, 0x1A30, "Intel i845","SDR/DDR",		0, poll_fsb_p4, poll_timings_nothing, setup_i845, poll_nothing },
+	{ 0x8086, 0x2560, "Intel i845E/G/PE/GE","",	0, poll_fsb_p4, poll_timings_nothing, setup_i845, poll_nothing },
+	{ 0x8086, 0x2500, "Intel i820","RDRAM",  		0, poll_fsb_nothing, poll_timings_nothing, setup_i820, poll_nothing },
+	{ 0x8086, 0x2530, "Intel i850","RDRAM",  		0, poll_fsb_p4, poll_timings_nothing, setup_i850, poll_nothing },
+	{ 0x8086, 0x2531, "Intel i860","RDRAM",  		0, poll_fsb_nothing, poll_timings_nothing, setup_i860, poll_nothing },
+	{ 0x8086, 0x7030, "Intel i430VX","SDRAM", 	0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x7100, "Intel i430TX","SDRAM", 	0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x7120, "Intel i810","SDRAM",  		0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x7122, "Intel i810","SDRAM",  		0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x7124, "Intel i810E","SDRAM", 		0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x7180, "Intel i440[LE]X","SDRAM",0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x7190, "Intel i440BX","SDRAM",		0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x7192, "Intel i440BX","SDRAM", 	0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x71A0, "Intel i440GX","SDRAM", 	0, poll_fsb_nothing, poll_timings_nothing, setup_i440gx, poll_nothing },
+	{ 0x8086, 0x71A2, "Intel i440GX","SDRAM",  	0, poll_fsb_nothing, poll_timings_nothing, setup_i440gx, poll_nothing },
+	{ 0x8086, 0x84C5, "Intel i450GX","SDRAM", 	0, poll_fsb_nothing, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x2540, "Intel E7500","DDR-SDRAM",0, poll_fsb_p4, poll_timings_E750x, setup_iE7xxx, poll_nothing },
+	{ 0x8086, 0x254C, "Intel E7501","DDR-SDRAM",0, poll_fsb_p4, poll_timings_E750x, setup_iE7xxx, poll_nothing },
+	{ 0x8086, 0x255d, "Intel E7205","DDR-SDRAM",0, poll_fsb_p4, poll_timings_nothing, setup_iE7xxx, poll_nothing },
+  { 0x8086, 0x3592, "Intel E7320","DDR-SDRAM",0, poll_fsb_p4, poll_timings_E7520, setup_iE7520, poll_nothing },
+  { 0x8086, 0x2588, "Intel E7221","DDR-SDRAM",0, poll_fsb_i925, poll_timings_i925, setup_i925, poll_nothing },
+  { 0x8086, 0x3590, "Intel E7520","DDR-SDRAM",0, poll_fsb_p4, poll_timings_E7520, setup_iE7520, poll_nothing },
+  { 0x8086, 0x2600, "Intel E8500","DDR-SDRAM",0, poll_fsb_p4, poll_timings_nothing, setup_nothing, poll_nothing },
+	{ 0x8086, 0x2570, "Intel i848/i865","", 		0, poll_fsb_i875, poll_timings_i875, setup_i875, poll_nothing },
+	{ 0x8086, 0x2578, "Intel i875P","",     		0, poll_fsb_i875, poll_timings_i875, setup_i875, poll_nothing },
+	{ 0x8086, 0x2550, "Intel E7505","DDR-SDRAM",0, poll_fsb_p4, poll_timings_nothing, setup_iE7xxx, poll_nothing },
+	{ 0x8086, 0x3580, "Intel i852P/i855G","",		0, poll_fsb_i855, poll_timings_i852, setup_nothing, poll_nothing },
+	{ 0x8086, 0x3340, "Intel i855PM","",    		0, poll_fsb_i855, poll_timings_i855, setup_nothing, poll_nothing },
+	{ 0x8086, 0x2580, "Intel i915P/G","",   		0, poll_fsb_i925, poll_timings_i925, setup_i925, poll_nothing },
+	{ 0x8086, 0x2590, "Intel i915PM/GM","", 		0, poll_fsb_i925, poll_timings_i925, setup_i925, poll_nothing },
+	{ 0x8086, 0x2584, "Intel i925X/XE","",  		0, poll_fsb_i925, poll_timings_i925, setup_i925, poll_nothing },
+	{ 0x8086, 0x2770, "Intel i945P/G","", 	 		0, poll_fsb_i945, poll_timings_i925, setup_i925, poll_nothing },
+	{ 0x8086, 0x27A0, "Intel i945GM/PM","", 		0, poll_fsb_i945, poll_timings_i925, setup_i925, poll_nothing },
+	{ 0x8086, 0x27AC, "Intel i945GME","", 	 		0, poll_fsb_i945gme, poll_timings_i925, setup_i925, poll_nothing },
+	{ 0x8086, 0x2774, "Intel i955X","", 		 		0, poll_fsb_i945, poll_timings_i925, setup_i925, poll_nothing},
+	{ 0x8086, 0x277C, "Intel i975X","", 		 		0, poll_fsb_i975, poll_timings_i925, setup_i925, poll_nothing},
+	{ 0x8086, 0x2970, "Intel i946PL/GZ","", 		0, poll_fsb_i965, poll_timings_i965, setup_p35, poll_nothing},
+	{ 0x8086, 0x2990, "Intel Q963/Q965","", 		0, poll_fsb_i965, poll_timings_i965, setup_p35, poll_nothing},
+	{ 0x8086, 0x29A0, "Intel P965/G965","", 		0, poll_fsb_i965, poll_timings_i965, setup_p35, poll_nothing},
+	{ 0x8086, 0x2A00, "Intel GM965/GL960","", 	0, poll_fsb_im965, poll_timings_im965, setup_p35, poll_nothing},
+	{ 0x8086, 0x2A10, "Intel GME965/GLE960","",	0, poll_fsb_im965, poll_timings_im965, setup_p35, poll_nothing},	
+	{ 0x8086, 0x2A40, "Intel PM/GM45/47","",		0, poll_fsb_im965, poll_timings_im965, setup_p35, poll_nothing},	
+	{ 0x8086, 0x29B0, "Intel Q35","", 	 		 		0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},	
+	{ 0x8086, 0x29C0, "Intel P35/G33","", 	 		0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},	
+	{ 0x8086, 0x29D0, "Intel Q33","",	  	 			0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},	
+	{ 0x8086, 0x29E0, "Intel X38/X48","", 	 		0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},			
+	{ 0x8086, 0x29F0, "Intel 3200/3210","", 		0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},	
+	{ 0x8086, 0x2E10, "Intel Q45/Q43","", 	 		0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},	
+	{ 0x8086, 0x2E20, "Intel P45/G45","",	  		0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},	
+	{ 0x8086, 0x2E30, "Intel G41","", 	 				0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},	
+	{ 0x8086, 0x4001, "Intel 5400A","", 		 		0, poll_fsb_5400, poll_timings_5400, setup_E5400, poll_nothing},		
+	{ 0x8086, 0x4003, "Intel 5400B","", 		 		0, poll_fsb_5400, poll_timings_5400, setup_E5400, poll_nothing},		
+	{ 0x8086, 0x25D8, "Intel 5000P","", 		 		0, poll_fsb_5400, poll_timings_5400, setup_E5400, poll_nothing},		
+	{ 0x8086, 0x25D4, "Intel 5000V","", 		 		0, poll_fsb_5400, poll_timings_5400, setup_E5400, poll_nothing},	
+	{ 0x8086, 0x25C0, "Intel 5000X","", 		 		0, poll_fsb_5400, poll_timings_5400, setup_E5400, poll_nothing},		
+	{ 0x8086, 0x25D0, "Intel 5000Z","", 		 		0, poll_fsb_5400, poll_timings_5400, setup_E5400, poll_nothing},	
+	{ 0x8086, 0x5020, "Intel EP80579","",    		0, poll_fsb_p4, 	poll_timings_EP80579, setup_nothing, poll_nothing },
+	{ 0x8086, 0x8100, "Intel US15W","",					0, poll_fsb_us15w, poll_timings_us15w, setup_nothing, poll_nothing},
+	{ 0x8086, 0x8101, "Intel UL11L/US15L","", 	0, poll_fsb_us15w, poll_timings_us15w, setup_nothing, poll_nothing},
+
+	/* INTEL IMC (Integrated Memory Controllers) */
+	{ 0xFFFF, 0x0001, "Core IMC","", 	 				0, poll_fsb_nhm, 	poll_timings_nhm, setup_nhm, poll_nothing},
+	{ 0xFFFF, 0x0002, "Core IMC","", 	 				0, poll_fsb_nhm32, 	poll_timings_nhm, setup_nhm32, poll_nothing},
+	{ 0xFFFF, 0x0003, "Core IMC","", 	 				0, poll_fsb_wmr, 	poll_timings_wmr, setup_wmr, poll_nothing},
+	{ 0xFFFF, 0x0004, "SNB IMC","", 	 				0, poll_fsb_snb, 	poll_timings_snb, setup_wmr, poll_nothing},
+	{ 0xFFFF, 0x0005, "SNB-E IMC","",		 	 		0, poll_fsb_snbe, poll_timings_snbe, setup_wmr, poll_nothing},
+	{ 0xFFFF, 0x0006, "IVB IMC","",			 	 		0, poll_fsb_ivb, 	poll_timings_snb, setup_wmr, poll_nothing},
+	{ 0xFFFF, 0x0007, "HSW IMC","",			 	 		0, poll_fsb_ivb, 	poll_timings_hsw, setup_wmr, poll_nothing},		
+	{ 0xFFFF, 0x0008, "PineView IMC","", 			0, poll_fsb_p35, poll_timings_p35, setup_p35, poll_nothing},
+	{ 0xFFFF, 0x0009, "CedarTrail IMC","",		0, poll_fsb_ct, poll_timings_ct, setup_nothing, poll_nothing},
+	
+	/* AMD IMC (Integrated Memory Controllers) */
+	{ 0xFFFF, 0x0100, "AMD K8 IMC","",				0, poll_fsb_amd64, poll_timings_amd64, setup_amd64, poll_nothing },
+	{ 0xFFFF, 0x0101, "AMD K10 IMC","",			  0, poll_fsb_k10, poll_timings_k10, setup_k10, poll_nothing },
+	{ 0xFFFF, 0x0102, "AMD K12 IMC","",			  0, poll_fsb_k12, poll_timings_k12, setup_apu, poll_nothing },
+	{ 0xFFFF, 0x0103, "AMD K14 IMC","",				0, poll_fsb_k14, poll_timings_k14, setup_apu, poll_nothing },
+	{ 0xFFFF, 0x0104, "AMD K15 IMC","",				0, poll_fsb_k15, poll_timings_k15, setup_apu, poll_nothing },
+	{ 0xFFFF, 0x0105, "AMD K16 IMC","",				0, poll_fsb_k16, poll_timings_k16, setup_apu, poll_nothing }
+};	
+
+static void print_memory_controller(void)
+{
+
+	/* Print memory controller info */
+	if (ctrl.index == 0) {
+		return;
+	}
+
+	/* Now print the memory controller capabilities */
+	/*
+	cprint(LINE_CPU+5, col, " "); col++;
+	if (ctrl.cap == ECC_UNKNOWN) {
+		return;
+	}
+	if (ctrl.cap & __ECC_DETECT) {
+		int on;
+		on = ctrl.mode & __ECC_DETECT;
+		cprint(LINE_CPU+5, col, "(ECC : ");
+		cprint(LINE_CPU+5, col +7, on?"Detect":"Disabled)");
+		on?(col += 13):(col += 16);
+	}
+	if (ctrl.mode & __ECC_CORRECT) {
+		int on;
+		on = ctrl.mode & __ECC_CORRECT;
+		cprint(LINE_CPU+5, col, " / ");
+		if (ctrl.cap & __ECC_CHIPKILL) {
+		cprint(LINE_CPU+5, col +3, on?"Correct -":"");
+		on?(col += 12):(col +=3);
+		} else {
+			cprint(LINE_CPU+5, col +3, on?"Correct)":"");
+			on?(col += 11):(col +=3);
+		}
+	}
+	if (ctrl.mode & __ECC_DETECT) {
+	if (ctrl.cap & __ECC_CHIPKILL) {
+		int on;
+		on = ctrl.mode & __ECC_CHIPKILL;
+		cprint(LINE_CPU+5, col, " Chipkill : ");
+		cprint(LINE_CPU+5, col +12, on?"On)":"Off)");
+		on?(col += 15):(col +=16);
+	}}
+	if (ctrl.mode & __ECC_SCRUB) {
+		int on;
+		on = ctrl.mode & __ECC_SCRUB;
+		cprint(LINE_CPU+5, col, " Scrub");
+		cprint(LINE_CPU+5, col +6, on?"+ ":"- ");
+		col += 7;
+	}
+	if (ctrl.cap & __ECC_UNEXPECTED) {
+		int on;
+		on = ctrl.mode & __ECC_UNEXPECTED;
+		cprint(LINE_CPU+5, col, "Unknown");
+		cprint(LINE_CPU+5, col +7, on?"+ ":"- ");
+		col += 9;
+	}
+	*/
+	
+	
+	
+	/* Print advanced caracteristics  */
+	col2 = 0;
+
+	controllers[ctrl.index].poll_fsb();
+	controllers[ctrl.index].poll_timings();
+
+}
+
+
+void find_controller(void)
+{
+	unsigned long vendor;
+	unsigned long device;
+	int i;
+	int result;
+	result = pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, PCI_VENDOR_ID, 2, &vendor);
+	result = pci_conf_read(ctrl.bus, ctrl.dev, ctrl.fn, PCI_DEVICE_ID, 2, &device);
+	
+	// Detect IMC by CPUID
+	if(imc_type) { vendor = 0xFFFF; device = imc_type; }
+	if(v->fail_safe & 1) { vendor = 0xFFFF; device = 0xFFFF; }
+		
+	//hprint(11,0,vendor); hprint(11,10,device);
+		
+	ctrl.index = 0;	
+		if (result == 0 || imc_type) {
+			for(i = 1; i < sizeof(controllers)/sizeof(controllers[0]); i++) {
+				if ((controllers[i].vendor == vendor) && (controllers[i].device == device)) {
+					ctrl.index = i;
+					break;
+				}
+			}
+		}
+	
+	controllers[ctrl.index].setup_ecc();
+	/* Don't enable ECC polling by default unless it has
+	 * been well tested.
+	 */
+	//set_ecc_polling(-1);
+	print_memory_controller();
+	
+	if(imc_type) { print_dmi_startup_info(); }
+
+}
+
+void poll_errors(void)
+{
+	if (ctrl.poll) {
+		controllers[ctrl.index].poll_errors();
+	}
+}
+
+/*
+void set_ecc_polling(int val)
+{
+	int tested = controllers[ctrl.index].tested;
+	if (val == -1) {
+		val = tested;
+	}
+	if (val && (ctrl.mode & __ECC_DETECT)) {
+		ctrl.poll = 1;
+		cprint(LINE_INFO, COL_ECC, tested? " on": " ON");
+	} else {
+		ctrl.poll = 0;
+		cprint(LINE_INFO, COL_ECC, "off");
+	}
+}
+*/
+
diff --git a/controller.h b/controller.h
new file mode 100644
index 0000000..da64ef1
--- /dev/null
+++ b/controller.h
@@ -0,0 +1,23 @@
+#ifndef MEMTEST_CONTROLLER_H
+#define MEMTEST_CONTROLLER_H
+
+struct pci_memory_controller {
+	unsigned vendor;
+	unsigned device;
+	char *name;
+	char *ram_type;
+	int tested;
+	void (*poll_fsb)(void);
+	void (*poll_timings)(void);
+	void (*setup_ecc)(void);
+	void (*poll_errors)(void);
+};
+
+void find_controller(void);
+void poll_errors(void);
+void set_ecc_polling(int val);
+void coretemp(void);
+extern struct pci_memory_controller controllers[];
+
+
+#endif /* MEMTEST_CONTROLLER_H */
diff --git a/cpuid.c b/cpuid.c
new file mode 100644
index 0000000..4c25ede
--- /dev/null
+++ b/cpuid.c
@@ -0,0 +1,87 @@
+/*
+ * cpuid.c --
+ *
+ *      Implements CPUID querying functions
+ *
+ */
+#include "stdin.h"
+#include "cpuid.h"
+
+struct cpu_ident cpu_id;
+
+void get_cpuid()
+{
+	unsigned int *v, dummy[3];
+	char *p, *q;
+
+	/* Get max std cpuid & vendor ID */
+	cpuid(0x0, &cpu_id.max_cpuid, &cpu_id.vend_id.uint32_array[0],
+	    &cpu_id.vend_id.uint32_array[2], &cpu_id.vend_id.uint32_array[1]);
+	cpu_id.vend_id.char_array[11] = 0;
+
+	/* Get processor family information & feature flags */
+	if (cpu_id.max_cpuid >= 1) {
+	    cpuid(0x00000001, &cpu_id.vers.flat, &cpu_id.info.flat,
+		&cpu_id.fid.uint32_array[1], &cpu_id.fid.uint32_array[0]);
+	}
+
+	/* Get the digital thermal sensor & power management status bits */
+	if(cpu_id.max_cpuid >= 6)	{
+		cpuid(0x00000006, &cpu_id.dts_pmp, &dummy[0], &dummy[1], &dummy[2]);
+	}
+	
+	/* Get the max extended cpuid */
+	cpuid(0x80000000, &cpu_id.max_xcpuid, &dummy[0], &dummy[1], &dummy[2]);
+
+	/* Get extended feature flags, only save EDX */
+	if (cpu_id.max_xcpuid >= 0x80000001) {
+	    cpuid(0x80000001, &dummy[0], &dummy[1],
+		&dummy[2], &cpu_id.fid.uint32_array[2]);
+	}
+
+	/* Get the brand ID */
+	if (cpu_id.max_xcpuid >= 0x80000004) {
+	    v = (unsigned int *)&cpu_id.brand_id;
+	    cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
+	    cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
+	    cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
+	    cpu_id.brand_id.char_array[47] = 0;
+	}
+        /*
+         * Intel chips right-justify this string for some dumb reason;
+         * undo that brain damage:
+         */
+        p = q = &cpu_id.brand_id.char_array[0];
+        while (*p == ' ')
+                p++;
+        if (p != q) {
+                while (*p)
+                        *q++ = *p++;
+                while (q <= &cpu_id.brand_id.char_array[48])
+                        *q++ = '\0';    /* Zero-pad the rest */
+	}
+
+	/* Get cache information */
+	switch(cpu_id.vend_id.char_array[0]) {
+        case 'A':
+            /* AMD Processors */
+	    /* The cache information is only in ecx and edx so only save
+	     * those registers */
+	    if (cpu_id.max_xcpuid >= 0x80000005) {
+		cpuid(0x80000005, &dummy[0], &dummy[1],
+		    &cpu_id.cache_info.uint[0], &cpu_id.cache_info.uint[1]);
+	    }
+	    if (cpu_id.max_xcpuid >= 0x80000006) {
+		cpuid(0x80000006, &dummy[0], &dummy[1],
+		    &cpu_id.cache_info.uint[2], &cpu_id.cache_info.uint[3]);
+	    }
+	    break;
+	case 'G':
+                /* Intel Processors, Need to do this in init.c */
+	    break;
+	}
+
+	/* Turn off mon bit since monitor based spin wait may not be reliable */
+	cpu_id.fid.bits.mon = 0;
+
+}
diff --git a/cpuid.h b/cpuid.h
new file mode 100644
index 0000000..caace1e
--- /dev/null
+++ b/cpuid.h
@@ -0,0 +1,196 @@
+/*
+ * cpuid.h --
+ *      contains the data structures required for CPUID 
+ *      implementation.
+ */
+
+#define CPUID_VENDOR_LENGTH     3               /* 3 GPRs hold vendor ID */
+#define CPUID_VENDOR_STR_LENGTH (CPUID_VENDOR_LENGTH * sizeof(uint32_t) + 1)
+#define CPUID_BRAND_LENGTH      12              /* 12 GPRs hold vendor ID */
+#define CPUID_BRAND_STR_LENGTH  (CPUID_BRAND_LENGTH * sizeof(uint32_t) + 1)
+
+extern struct cpu_ident cpu_id;
+
+static inline void __cpuid(unsigned int *eax, unsigned int *ebx,
+                                unsigned int *ecx, unsigned int *edx)
+{
+        /* ecx is often an input as well as an output. */
+        asm volatile("\t"
+      	    "push %%ebx; cpuid; mov %%ebx, %%edi; pop %%ebx"
+            : "=a" (*eax),
+              "=D" (*ebx),
+              "=c" (*ecx),
+              "=d" (*edx)
+            : "0" (*eax), "2" (*ecx));
+}
+
+static inline void cpuid(unsigned int op,
+                         unsigned int *eax, unsigned int *ebx,
+                         unsigned int *ecx, unsigned int *edx)
+{
+        *eax = op;
+        *ecx = 0;
+        __cpuid(eax, ebx, ecx, edx);
+}
+
+/* Some CPUID calls want 'count' to be placed in ecx */
+static inline void cpuid_count(unsigned int op, int count,
+                               unsigned int *eax, unsigned int *ebx,
+                               unsigned int *ecx, unsigned int *edx)
+{
+        *eax = op;
+        *ecx = count;
+        __cpuid(eax, ebx, ecx, edx);
+}
+
+/* Typedef for storing the Cache Information */
+typedef union {
+   unsigned char ch[48];
+   uint32_t      uint[12];
+   struct {
+      uint32_t    fill1:24;      /* Bit 0 */
+      uint32_t    l1_i_sz:8;
+      uint32_t    fill2:24; 
+      uint32_t    l1_d_sz:8;
+      uint32_t    fill3:16; 
+      uint32_t    l2_sz:16;
+      uint32_t    fill4:18; 
+      uint32_t    l3_sz:14;
+      uint32_t    fill5[8];
+   } amd;
+} cpuid_cache_info_t;
+
+/* Typedef for storing the CPUID Vendor String */
+typedef union {
+   /* Note: the extra byte in the char array is for '\0'. */
+   char           char_array[CPUID_VENDOR_STR_LENGTH];
+   uint32_t       uint32_array[CPUID_VENDOR_LENGTH];
+} cpuid_vendor_string_t;
+
+/* Typedef for storing the CPUID Brand String */
+typedef union {
+   /* Note: the extra byte in the char array is for '\0'. */
+   char           char_array[CPUID_BRAND_STR_LENGTH];
+   uint32_t       uint32_array[CPUID_BRAND_LENGTH];
+} cpuid_brand_string_t;
+
+/* Typedef for storing CPUID Version */
+typedef union {
+   uint32_t flat;
+   struct {
+      uint32_t    stepping:4;      /* Bit 0 */
+      uint32_t    model:4;
+      uint32_t    family:4;
+      uint32_t    processorType:2;
+      uint32_t    reserved1514:2;
+      uint32_t    extendedModel:4;
+      uint32_t    extendedFamily:8;
+      uint32_t    reserved3128:4;  /* Bit 31 */
+   } bits;      
+} cpuid_version_t;
+
+/* Typedef for storing CPUID Processor Information */
+typedef union {
+   uint32_t flat;
+   struct {
+      uint32_t    brandIndex:8;    /* Bit 0 */
+      uint32_t    cflushLineSize:8;
+      uint32_t    logicalProcessorCount:8;
+      uint32_t    apicID:8;        /* Bit 31 */
+   } bits;      
+} cpuid_proc_info_t;
+
+/* Typedef for storing CPUID Feature flags */
+typedef union {
+   uint32_t flat;
+   struct {
+      uint32_t    :1;           
+   } bits;
+} cpuid_custom_features;
+
+/* Typedef for storing CPUID Feature flags */
+typedef union {
+   uint32_t       uint32_array[3];
+   struct {
+      uint32_t    fpu:1;           /* EDX feature flags, bit 0 */
+      uint32_t    vme:1;
+      uint32_t    de:1;
+      uint32_t    pse:1;
+      uint32_t    rdtsc:1;
+      uint32_t    msr:1;
+      uint32_t    pae:1;
+      uint32_t    mce:1;
+      uint32_t    cx8:1;
+      uint32_t    apic:1;
+      uint32_t    bit10:1;
+      uint32_t    sep:1;
+      uint32_t    mtrr:1;
+      uint32_t    pge:1;
+      uint32_t    mca:1;
+      uint32_t    cmov:1;
+      uint32_t    pat:1;
+      uint32_t    pse36:1;
+      uint32_t    psn:1;
+      uint32_t    cflush:1;
+      uint32_t    bit20:1;
+      uint32_t    ds:1;
+      uint32_t    acpi:1;
+      uint32_t    mmx:1;
+      uint32_t    fxsr:1;
+      uint32_t    sse:1;
+      uint32_t    sse2:1;
+      uint32_t    ss:1;
+      uint32_t    htt:1;
+      uint32_t    tm:1;
+      uint32_t    bit30:1;
+      uint32_t    pbe:1;           /* EDX feature flags, bit 31 */
+      uint32_t    sse3:1;          /* ECX feature flags, bit 0 */
+      uint32_t    mulq:1;
+      uint32_t    bit2:1;
+      uint32_t    mon:1;
+      uint32_t    dscpl:1;
+      uint32_t    vmx:1;
+     	uint32_t    smx:1;      	
+     	uint32_t    eist:1;  
+     	uint32_t    tm2:1;       		     		
+      uint32_t    bits_9_31:23;
+      uint32_t    bits0_28:29;     /* EDX extended feature flags, bit 0 */
+      uint32_t    lm:1;		   /* Long Mode */
+      uint32_t    bits_30_31:2;    /* EDX extended feature flags, bit 32 */
+   } bits;
+} cpuid_feature_flags_t;
+
+/* An overall structure to cache all of the CPUID information */
+struct cpu_ident {
+	uint32_t max_cpuid;
+	uint32_t max_xcpuid;
+	uint32_t dts_pmp;
+	cpuid_version_t vers;
+	cpuid_proc_info_t info;
+	cpuid_feature_flags_t fid;
+	cpuid_vendor_string_t vend_id;
+	cpuid_brand_string_t brand_id;
+	cpuid_cache_info_t cache_info;
+	cpuid_custom_features custom;
+};
+
+struct cpuid4_eax {
+	uint32_t	ctype:5;
+	uint32_t	level:3;
+	uint32_t	is_self_initializing:1;
+	uint32_t	is_fully_associative:1;
+	uint32_t	reserved:4;
+	uint32_t	num_threads_sharing:12;
+	uint32_t	num_cores_on_die:6;
+};
+
+struct cpuid4_ebx {
+	uint32_t	coherency_line_size:12;
+	uint32_t	physical_line_partition:10;
+	uint32_t	ways_of_associativity:10;
+};
+
+struct cpuid4_ecx {
+	uint32_t	number_of_sets:32;
+};
+
diff --git a/defs.h b/defs.h
new file mode 100644
index 0000000..3891699
--- /dev/null
+++ b/defs.h
@@ -0,0 +1,26 @@
+/* defs.h - MemTest-86 Version 3.3
+ * assembler/compiler definitions
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ */ 
+
+#define SETUPSECS	4		/* Number of setup sectors */
+
+/*
+ * Caution!! There is magic in the build process.  Read
+ * README.build-process before you change anything.  
+ * Unlike earlier versions all of the settings are in defs.h
+ * so the build process should be more robust.
+ */
+#define LOW_TEST_ADR	0x00010000		/* Final adrs for test code */
+
+#define BOOTSEG		0x07c0			/* Segment adrs for inital boot */
+#define INITSEG		0x9000			/* Segment adrs for relocated boot */
+#define SETUPSEG	(INITSEG+0x20)		/* Segment adrs for relocated setup */
+#define TSTLOAD		0x1000			/* Segment adrs for load of test */
+
+#define KERNEL_CS	0x10			/* 32 bit segment adrs for code */
+#define KERNEL_DS	0x18			/* 32 bit segment adrs for data */
+#define REAL_CS		0x20			/* 16 bit segment adrs for code */
+#define REAL_DS		0x28			/* 16 bit segment adrs for data */
diff --git a/dmi.c b/dmi.c
new file mode 100644
index 0000000..1f11008
--- /dev/null
+++ b/dmi.c
@@ -0,0 +1,429 @@
+/* dmi.c  using the DMI from SMBIOS to read information about the hardware's
+ * memory devices capabilities and where they are mapped into the address space
+ *
+ * Copyright (c) Joachim Deguara, AMD 2006
+ *
+ * Release under the GPL version 2
+ * ----------------------------------------------------
+ * Memtest86+ V4.00 - Added compliance with SMBIOS Spec V2.6.1
+ */
+
+
+#include "test.h"
+#include <stdint.h>
+
+
+#define round_up(x,y) (((x) + (y) - 1) & ~((y)-1))
+#define round_down(x,y) ((x) & ~((y)-1))
+
+
+struct dmi_eps {
+	uint8_t  anchor[4];
+	int8_t   checksum;
+	uint8_t  length;
+	uint8_t  majorversion;
+	uint8_t  minorversion;
+	uint16_t maxstructsize;
+	uint8_t  revision;
+	uint8_t  pad[5];
+	uint8_t  intanchor[5];
+	int8_t   intchecksum;
+	uint16_t tablelength;
+	uint32_t tableaddress;
+	uint16_t numstructs;
+	uint8_t  SMBIOSrev;
+} __attribute__((packed));
+
+struct tstruct_header{
+	uint8_t  type;
+	uint8_t  length;
+	uint16_t handle;
+} __attribute__((packed));
+
+struct system_map {
+	struct tstruct_header header;
+	uint8_t	manufacturer;
+	uint8_t productname;
+	uint8_t version;
+	uint8_t serialnumber;
+	uint8_t uuidbytes[16];
+	uint8_t wut;
+} __attribute__((packed));
+
+struct cpu_map {
+	struct tstruct_header header;
+	uint8_t	cpu_socket;
+	uint8_t cpu_type;
+	uint8_t cpu_family;
+	uint8_t cpu_manufacturer;
+	uint32_t cpu_id;
+	uint8_t cpu_version;
+	uint8_t	cpu_voltage;
+	uint16_t	ext_clock;
+	uint16_t	max_speed;
+	uint16_t cur_speed;
+	uint8_t	cpu_status;
+	uint8_t	cpu_upgrade;
+	uint16_t l1_handle;
+	uint16_t l2_handle;
+	uint16_t l3_handle;
+	uint8_t	cpu_serial;	
+	uint8_t	cpu_asset_tag;
+	uint8_t cpu_part_number;
+	uint8_t	core_count;
+	uint8_t	core_enabled;
+	uint8_t	thread_count;
+	uint16_t cpu_specs;
+	uint16_t cpu_family_2;	
+} __attribute__((packed));
+
+struct mem_dev {
+	struct tstruct_header header;
+	uint16_t pma_handle;
+	uint16_t err_handle;
+	uint16_t tot_width;
+	uint16_t dat_width;
+	uint16_t size;
+	uint8_t  form;
+	uint8_t  set;
+	uint8_t  dev_locator;
+	uint8_t  bank_locator;
+	uint8_t  type;
+	uint16_t typedetail;
+	uint16_t speed;
+	uint8_t  manufacturer;
+	uint8_t  serialnum;
+	uint8_t  asset;
+	uint8_t  partnum;
+} __attribute__((packed));
+
+struct md_map{
+	struct tstruct_header header;
+	uint32_t start;
+	uint32_t end;
+	uint16_t md_handle;
+	uint16_t mama_handle;
+	uint8_t  row_pos;
+	uint8_t  interl_pos;
+	uint8_t  interl_depth;
+} __attribute__((packed));
+
+struct pma{
+	struct tstruct_header header;
+	uint8_t  location;
+	uint8_t  use;
+	uint8_t  ecc;
+	uint32_t capacity;
+	uint16_t errhandle;
+	uint16_t numdevs;
+} __attribute__((packed));
+
+static char *form_factors[] = {
+	"?",
+	"Other", "Unknown", "SIMM", "SIP", "Chip", "DIP", "ZIP",
+	"Proprietary Card", "DIMM", "TSOP", "Row of chips", "RIMM",
+	"SODIMM", "SRIMM", "FB-DIMM"
+};
+
+
+static char *memory_types[] = {
+	"?",
+	"Other", "????", "DRAM", "EDRAM", "VRAM", "SRAM", "RAM",
+	"ROM", "FLASH", "EEPROM", "FEPROM", "EPROM", "CDRAM", "3DRAM",
+	"SDRAM", "SGRAM", "RDRAM", "DDR", "DDR2", "DDR2 FB", "RSVD",
+  "RSVD","RSVD","DDR3","FBD2"
+};
+
+
+struct mem_dev * mem_devs[MAX_DMI_MEMDEVS];
+int mem_devs_count=0;
+struct md_map * md_maps[MAX_DMI_MEMDEVS];
+struct system_map * dmi_system_info;
+struct cpu_map * dmi_cpu_info;
+int md_maps_count=0;
+int dmi_err_cnts[MAX_DMI_MEMDEVS];
+short dmi_initialized=0;
+
+char * get_tstruct_string(struct tstruct_header *header, int n){
+	if(n<1)
+		return 0;
+	char * a = (char *)header + header->length;
+	n--;
+	do{
+		if (!*a)
+			n--;
+		if (!n && *a)
+			return a;
+		a++;
+	}while (!(*a==0 && *(a-1)==0));
+	return 0;
+}
+
+
+int open_dmi(void){
+	char *dmi, *dmi_search_start, *dmi_start;
+	int found=0;
+	struct dmi_eps *eps;
+	char *table_start;
+	int tstruct_count=0;
+	dmi_search_start = (char *)DMI_SEARCH_START;
+
+	//find anchor
+	for(dmi = dmi_search_start; dmi < dmi_search_start + 0xf0000; dmi +=16){
+		if( *dmi == '_' &&
+		    *(dmi+1) == 'S' &&
+		    *(dmi+2) == 'M' &&
+		    *(dmi+3) == '_'){
+			found =1;
+			break;
+		}
+	}
+	if (!found) {
+		return -1;
+	}
+	dmi_start=dmi;
+	eps=(struct dmi_eps *)dmi;
+
+	//check checksum
+	int8_t checksum=0;
+	for (; dmi < dmi_start + eps->length; dmi++)
+		checksum += *dmi;
+	if (checksum){
+		return -1;
+	}
+
+	//we need at least revision 2.1 of SMBIOS
+	if ( eps->majorversion < 2 &&
+	     eps->minorversion < 1){
+	    return -1;
+	}
+
+
+	table_start=(char *)eps->tableaddress;
+	dmi=table_start;
+//look at all structs
+	while(dmi < table_start + eps->tablelength){
+		struct tstruct_header *header = (struct tstruct_header *)dmi;
+		
+		if (header->type == 17)
+			mem_devs[mem_devs_count++] = (struct mem_dev *)dmi;
+		
+		// Need fix (SMBIOS/DDR3)
+		if (header->type == 20 || header->type == 1)
+			md_maps[md_maps_count++] = (struct md_map *)dmi;
+
+		// MB_SPEC
+		if (header->type == 2)
+		{
+			dmi_system_info = (struct system_map *)dmi;
+		}
+
+		// CPU_SPEC
+		if (header->type == 4)
+		{
+			dmi_cpu_info = (struct cpu_map *)dmi;
+		}
+			
+		dmi+=header->length;
+		
+		while( ! (*dmi == 0  && *(dmi+1) == 0 ) )
+			dmi++;
+		dmi+=2;
+
+		if (++tstruct_count > eps->numstructs)
+			return -1;
+	}
+	return 0;
+}
+
+void init_dmi(void){
+	int i;
+	for(i=0; i < MAX_DMI_MEMDEVS; i++)
+		dmi_err_cnts[i]=0;
+	open_dmi();
+	dmi_initialized=1;
+}
+
+void print_dmi_startup_info(void)
+{
+	char *string1;
+	char *string2;
+	char *string3;
+	int dmicol = 78;
+	int slenght;
+	int sl1, sl2, sl3;
+	
+	if(!dmi_initialized) { init_dmi(); }
+		
+	string1 = get_tstruct_string(&dmi_system_info->header,dmi_system_info->manufacturer);
+	sl1 = strlen(string1);
+	string2 = get_tstruct_string(&dmi_system_info->header,dmi_system_info->productname);	
+	sl2 = strlen(string2);
+	string3 = get_tstruct_string(&dmi_cpu_info->header,dmi_cpu_info->cpu_socket);
+	sl3 = strlen(string3);
+
+	slenght = sl1 + sl2;
+	if(sl3 > 2) { slenght += sl3 + 4; } else { slenght++; }
+	
+	if(sl1 && sl2)
+		{
+			//dmicol -= slenght; // right align
+			dmicol = 39 - slenght/2; // center align
+			cprint(LINE_DMI, dmicol, string1);
+			dmicol += sl1 + 1;
+			cprint(LINE_DMI, dmicol, string2);
+			dmicol += sl2 + 1;
+			
+			if(sl3 > 2){
+				cprint(LINE_DMI, dmicol, "(");
+				dmicol++;
+				cprint(LINE_DMI, dmicol, string3);
+				dmicol += sl3;
+				cprint(LINE_DMI, dmicol, ")");
+			}
+		}
+}
+
+void print_dmi_info(void){
+	int i,j,page;
+	char * string=0;
+
+	if(!dmi_initialized)
+		init_dmi();
+
+	if (mem_devs_count == 0){
+		cprint(POP2_Y+1, POP2_X+2, "No valid DMI Memory Devices info found");
+		while (get_key() == 0);
+		return;
+	}
+
+	for(page=1; page <= 1 + (mem_devs_count-1)/8; page++){
+		pop2clear();
+		cprint(POP2_Y+1, POP2_X+2, "DMI Memory Device Info  (page ");
+		itoa(string,page);
+		cprint(POP2_Y+1, POP2_X+32, string);
+		cprint(POP2_Y+1, POP2_X+33, "/");
+		itoa(string,1 + (mem_devs_count-1)/8);
+		cprint(POP2_Y+1, POP2_X+34, string);
+		cprint(POP2_Y+1, POP2_X+35, ")");
+
+		cprint(POP2_Y+3, POP2_X+4, "Location         Size(MB) Speed(MHz) Type   Form");
+		cprint(POP2_Y+4, POP2_X+4, "--------------------------------------------------------------");
+
+		for(i=8*(page-1); i<mem_devs_count && i<8*page; i++){
+			int size_in_mb;
+			int yof;
+
+			yof=POP2_Y+5+2*(i-8*(page-1));
+			cprint(yof, POP2_X+4, get_tstruct_string(&(mem_devs[i]->header), mem_devs[i]->dev_locator));
+
+			if (mem_devs[i]->size == 0){
+				cprint(yof, POP2_X+4+18, "Empty");
+			}else if (mem_devs[i]->size == 0xFFFF){
+				cprint(yof, POP2_X+4+18, "Unknown");
+			}else{
+				size_in_mb = 0xEFFF & mem_devs[i]->size;
+				if (mem_devs[i]->size & 0x8000)
+					size_in_mb = size_in_mb<<10;
+				itoa(string, size_in_mb);
+				cprint(yof, POP2_X+4+18, string);
+			}
+			
+			//this is the only field that needs to be SMBIOS 2.3+ 
+			if ( mem_devs[i]->speed && 
+			     mem_devs[i]->header.length > 21){
+				itoa(string, mem_devs[i]->speed);
+				cprint(yof, POP2_X+4+27, string);
+			}else{
+				cprint(yof, POP2_X+4+27, "Unknown");
+			}
+			cprint(yof, POP2_X+4+37, memory_types[mem_devs[i]->type]);
+			cprint(yof, POP2_X+4+44, form_factors[mem_devs[i]->form]);
+
+			//print mappings
+			int mapped=0,of=0;
+			cprint(yof+1, POP2_X+6,"mapped to: ");
+			for(j=0; j<md_maps_count; j++)
+			{
+				if (mem_devs[i]->header.handle != md_maps[j]->md_handle)
+					continue;
+				if (mapped++){
+					cprint(yof+1, POP2_X+17+of, ",");
+					of++;
+				}
+				hprint3(yof+1, POP2_X+17+of, md_maps[j]->start>>22, 4);
+				of += 4;
+				hprint3(yof+1, POP2_X+17+of, md_maps[j]->start<<10, 8);
+				of += 8;
+				cprint(yof+1, POP2_X+17+of, "-");
+				of++;
+				hprint3(yof+1, POP2_X+17+of, md_maps[j]->end>>22, 4);
+				of += 4;
+				hprint3(yof+1, POP2_X+17+of, ((md_maps[j]->end+1)<<10) - 1, 8);
+				of += 8;
+				if(md_maps[j]->end == 0) { hprint3(yof+1, POP2_X+17+of-8,0,8); }
+			}
+			if (!mapped)
+			{
+				cprint(yof+1, POP2_X+17, "No mapping (Interleaved Device)");
+			}
+
+		}
+
+		wait_keyup();
+		while (get_key() == 0);
+	}
+}
+	
+//return 1 if the list of bad memory devices changes, 0 otherwise, -1 if no mapped
+int add_dmi_err(ulong adr){
+	int i,j,found=-1;
+	
+	if(!dmi_initialized)
+		init_dmi();
+	
+	for(i=0; i < md_maps_count; i++){
+		if ( adr < (md_maps[i]->start<<10) ||
+		     adr > (md_maps[i]->end<<10) )
+			continue;
+
+		//matching map found, now check find corresponding dev
+		for(j=0; j < mem_devs_count; j++){
+			if (mem_devs[j]->header.handle != md_maps[i]->md_handle)
+				continue;
+			if (dmi_err_cnts[j]){
+				found=0;
+			}else{
+				found = dmi_err_cnts[j] = 1;
+			}
+		}
+	}
+	
+	return found;
+}
+	
+void print_dmi_err(void){
+	int i,count,of;
+	char *string;
+	
+	scroll();
+	
+	cprint(v->msg_line, 0,"Bad Memory Devices: ");
+	of=20;
+	for ( i=count=0; i < MAX_DMI_MEMDEVS; i++){
+		if (!dmi_err_cnts[i])
+			continue;
+		struct mem_dev *md = mem_devs[i];
+		if(count++){
+			cprint(v->msg_line, of, ", ");
+			of+=2;
+		}
+		string=get_tstruct_string((struct tstruct_header *)md,md->dev_locator);
+		if (strlen(string) + of > 80){
+			scroll();
+			of=7;
+		}
+		cprint(v->msg_line, of, string);
+		of += strlen(string);
+	}
+}
diff --git a/dmi.h b/dmi.h
new file mode 100644
index 0000000..c90fedd
--- /dev/null
+++ b/dmi.h
@@ -0,0 +1,7 @@
+#ifndef __DMI_H__
+#define __DMI_H__
+int add_dmi_err(ulong adr);
+void print_dmi_err(void);
+void print_dmi_info(void);
+void print_dmi_startup_info(void);
+#endif
diff --git a/elf.h b/elf.h
new file mode 100644
index 0000000..72efaac
--- /dev/null
+++ b/elf.h
@@ -0,0 +1,590 @@
+#ifndef ELF_H
+#define ELF_H
+
+#define EI_NIDENT	16	/* Size of e_ident array. */
+
+/* Values for e_type. */
+#define ET_NONE		0	/* No file type */
+#define ET_REL		1	/* Relocatable file */
+#define ET_EXEC		2	/* Executable file */
+#define ET_DYN		3	/* Shared object file */
+#define ET_CORE		4	/* Core file */
+
+/* Values for e_machine (architecute). */
+#define EM_NONE		 0		/* No machine */
+#define EM_M32		 1		/* AT&T WE 32100 */
+#define EM_SPARC	 2		/* SUN SPARC */
+#define EM_386		 3		/* Intel 80386+ */
+#define EM_68K		 4		/* Motorola m68k family */
+#define EM_88K		 5		/* Motorola m88k family */
+#define EM_486		 6		/* Perhaps disused */
+#define EM_860		 7		/* Intel 80860 */
+#define EM_MIPS		 8		/* MIPS R3000 big-endian */
+#define EM_S370		 9		/* IBM System/370 */
+#define EM_MIPS_RS3_LE	10		/* MIPS R3000 little-endian */
+
+#define EM_PARISC	15		/* HPPA */
+#define EM_VPP500	17		/* Fujitsu VPP500 */
+#define EM_SPARC32PLUS	18		/* Sun's "v8plus" */
+#define EM_960		19		/* Intel 80960 */
+#define EM_PPC		20		/* PowerPC */
+#define EM_PPC64	21		/* PowerPC 64-bit */
+#define EM_S390		22		/* IBM S390 */
+
+#define EM_V800		36		/* NEC V800 series */
+#define EM_FR20		37		/* Fujitsu FR20 */
+#define EM_RH32		38		/* TRW RH-32 */
+#define EM_RCE		39		/* Motorola RCE */
+#define EM_ARM		40		/* ARM */
+#define EM_FAKE_ALPHA	41		/* Digital Alpha */
+#define EM_SH		42		/* Hitachi SH */
+#define EM_SPARCV9	43		/* SPARC v9 64-bit */
+#define EM_TRICORE	44		/* Siemens Tricore */
+#define EM_ARC		45		/* Argonaut RISC Core */
+#define EM_H8_300	46		/* Hitachi H8/300 */
+#define EM_H8_300H	47		/* Hitachi H8/300H */
+#define EM_H8S		48		/* Hitachi H8S */
+#define EM_H8_500	49		/* Hitachi H8/500 */
+#define EM_IA_64	50		/* Intel Merced */
+#define EM_MIPS_X	51		/* Stanford MIPS-X */
+#define EM_COLDFIRE	52		/* Motorola Coldfire */
+#define EM_68HC12	53		/* Motorola M68HC12 */
+#define EM_MMA		54		/* Fujitsu MMA Multimedia Accelerator*/
+#define EM_PCP		55		/* Siemens PCP */
+#define EM_NCPU		56		/* Sony nCPU embeeded RISC */
+#define EM_NDR1		57		/* Denso NDR1 microprocessor */
+#define EM_STARCORE	58		/* Motorola Start*Core processor */
+#define EM_ME16		59		/* Toyota ME16 processor */
+#define EM_ST100	60		/* STMicroelectronic ST100 processor */
+#define EM_TINYJ	61		/* Advanced Logic Corp. Tinyj emb.fam*/
+#define EM_X86_64	62		/* AMD x86-64 architecture */
+#define EM_PDSP		63		/* Sony DSP Processor */
+
+#define EM_FX66		66		/* Siemens FX66 microcontroller */
+#define EM_ST9PLUS	67		/* STMicroelectronics ST9+ 8/16 mc */
+#define EM_ST7		68		/* STmicroelectronics ST7 8 bit mc */
+#define EM_68HC16	69		/* Motorola MC68HC16 microcontroller */
+#define EM_68HC11	70		/* Motorola MC68HC11 microcontroller */
+#define EM_68HC08	71		/* Motorola MC68HC08 microcontroller */
+#define EM_68HC05	72		/* Motorola MC68HC05 microcontroller */
+#define EM_SVX		73		/* Silicon Graphics SVx */
+#define EM_AT19		74		/* STMicroelectronics ST19 8 bit mc */
+#define EM_VAX		75		/* Digital VAX */
+#define EM_CRIS		76		/* Axis Communications 32-bit embedded processor */
+#define EM_JAVELIN	77		/* Infineon Technologies 32-bit embedded processor */
+#define EM_FIREPATH	78		/* Element 14 64-bit DSP Processor */
+#define EM_ZSP		79		/* LSI Logic 16-bit DSP Processor */
+#define EM_MMIX		80		/* Donald Knuth's educational 64-bit processor */
+#define EM_HUANY	81		/* Harvard University machine-independent object files */
+#define EM_PRISM	82		/* SiTera Prism */
+#define EM_AVR		83		/* Atmel AVR 8-bit microcontroller */
+#define EM_FR30		84		/* Fujitsu FR30 */
+#define EM_D10V		85		/* Mitsubishi D10V */
+#define EM_D30V		86		/* Mitsubishi D30V */
+#define EM_V850		87		/* NEC v850 */
+#define EM_M32R		88		/* Mitsubishi M32R */
+#define EM_MN10300	89		/* Matsushita MN10300 */
+#define EM_MN10200	90		/* Matsushita MN10200 */
+#define EM_PJ		91		/* picoJava */
+#define EM_OPENRISC	92		/* OpenRISC 32-bit embedded processor */
+#define EM_ARC_A5	93		/* ARC Cores Tangent-A5 */
+#define EM_XTENSA	94		/* Tensilica Xtensa Architecture */
+#define EM_NUM		95
+
+/* Values for p_type. */
+#define PT_NULL		0	/* Unused entry. */
+#define PT_LOAD		1	/* Loadable segment. */
+#define PT_DYNAMIC	2	/* Dynamic linking information segment. */
+#define PT_INTERP	3	/* Pathname of interpreter. */
+#define PT_NOTE		4	/* Auxiliary information. */
+#define PT_SHLIB	5	/* Reserved (not used). */
+#define PT_PHDR		6	/* Location of program header itself. */
+
+/* Values for p_flags. */
+#define PF_X		0x1	/* Executable. */
+#define PF_W		0x2	/* Writable. */
+#define PF_R		0x4	/* Readable. */
+
+
+#define	ELF_PROGRAM_RETURNS_BIT	0x8000000	/* e_flags bit 31 */
+
+#define EI_MAG0		0
+#define ELFMAG0		0x7f
+
+#define EI_MAG1		1
+#define ELFMAG1		'E'
+
+#define EI_MAG2		2
+#define ELFMAG2		'L'
+
+#define EI_MAG3		3
+#define ELFMAG3		'F'
+
+#define ELFMAG		"\177ELF"
+
+#define EI_CLASS	4	/* File class byte index */
+#define ELFCLASSNONE	0	/* Invalid class */
+#define ELFCLASS32	1	/* 32-bit objects */
+#define ELFCLASS64	2	/* 64-bit objects */
+
+#define EI_DATA		5	/* Data encodeing byte index */
+#define ELFDATANONE	0	/* Invalid data encoding */
+#define ELFDATA2LSB	1	/* 2's complement little endian */
+#define ELFDATA2MSB	2	/* 2's complement big endian */
+
+#define EI_VERSION	6	/* File version byte index */
+				/* Value must be EV_CURRENT */
+
+#define EV_NONE		0	/* Invalid ELF Version */
+#define EV_CURRENT	1	/* Current version */
+
+#define ELF32_PHDR_SIZE (8*4)	/* Size of an elf program header */
+
+#ifndef ASSEMBLY
+
+#include "stdint.h"
+
+/*
+ * ELF definitions common to all 32-bit architectures.
+ */
+
+typedef uint32_t	Elf32_Addr;
+typedef uint16_t	Elf32_Half;
+typedef uint32_t	Elf32_Off;
+typedef uint16_t	Elf32_Section;
+typedef int32_t		Elf32_Sword;
+typedef uint32_t	Elf32_Word;
+typedef uint32_t	Elf32_Size;
+
+typedef uint64_t	Elf64_Addr;
+typedef uint16_t	Elf64_Half;
+typedef uint64_t	Elf64_Off;
+typedef uint16_t	Elf64_Section;
+typedef int32_t		Elf64_Sword;
+typedef uint32_t	Elf64_Word;
+typedef uint64_t	Elf64_Size;
+typedef uint64_t	Elf64_Xword;
+typedef	int64_t		Elf64_Sxword;
+
+/*
+ * ELF header.
+ */
+typedef struct {
+	unsigned char	e_ident[EI_NIDENT];	/* File identification. */
+	Elf32_Half	e_type;		/* File type. */
+	Elf32_Half	e_machine;	/* Machine architecture. */
+	Elf32_Word	e_version;	/* ELF format version. */
+	Elf32_Addr	e_entry;	/* Entry point. */
+	Elf32_Off	e_phoff;	/* Program header file offset. */
+	Elf32_Off	e_shoff;	/* Section header file offset. */
+	Elf32_Word	e_flags;	/* Architecture-specific flags. */
+	Elf32_Half	e_ehsize;	/* Size of ELF header in bytes. */
+	Elf32_Half	e_phentsize;	/* Size of program header entry. */
+	Elf32_Half	e_phnum;	/* Number of program header entries. */
+	Elf32_Half	e_shentsize;	/* Size of section header entry. */
+	Elf32_Half	e_shnum;	/* Number of section header entries. */
+	Elf32_Half	e_shstrndx;	/* Section name strings section. */
+} Elf32_Ehdr;
+
+typedef struct {
+	unsigned char	e_ident[EI_NIDENT];	/* File identification. */
+	Elf64_Half	e_type;		/* File type. */
+	Elf64_Half	e_machine;	/* Machine architecture. */
+	Elf64_Word	e_version;	/* ELF format version. */
+	Elf64_Addr	e_entry;	/* Entry point. */
+	Elf64_Off	e_phoff;	/* Program header file offset. */
+	Elf64_Off	e_shoff;	/* Section header file offset. */
+	Elf64_Word	e_flags;	/* Architecture-specific flags. */
+	Elf64_Half	e_ehsize;	/* Size of ELF header in bytes. */
+	Elf64_Half	e_phentsize;	/* Size of program header entry. */
+	Elf64_Half	e_phnum;	/* Number of program header entries. */
+	Elf64_Half	e_shentsize;	/* Size of section header entry. */
+	Elf64_Half	e_shnum;	/* Number of section header entries. */
+	Elf64_Half	e_shstrndx;	/* Section name strings section. */
+} Elf64_Ehdr;
+
+/*
+ * Program header.
+ */
+typedef struct {
+	Elf32_Word	p_type;		/* Entry type. */
+	Elf32_Off	p_offset;	/* File offset of contents. */
+	Elf32_Addr	p_vaddr;	/* Virtual address (not used). */
+	Elf32_Addr	p_paddr;	/* Physical address. */
+	Elf32_Size	p_filesz;	/* Size of contents in file. */
+	Elf32_Size	p_memsz;	/* Size of contents in memory. */
+	Elf32_Word	p_flags;	/* Access permission flags. */
+	Elf32_Size	p_align;	/* Alignment in memory and file. */
+} Elf32_Phdr;
+
+typedef struct {
+	Elf64_Word	p_type;		/* Entry type. */
+	Elf64_Word	p_flags;	/* Access permission flags. */
+	Elf64_Off	p_offset;	/* File offset of contents. */
+	Elf64_Addr	p_vaddr;	/* Virtual address (not used). */
+	Elf64_Addr	p_paddr;	/* Physical address. */
+	Elf64_Size	p_filesz;	/* Size of contents in file. */
+	Elf64_Size	p_memsz;	/* Size of contents in memory. */
+	Elf64_Size	p_align;	/* Alignment in memory and file. */
+} Elf64_Phdr;
+
+
+/* Dynamic section entry.  */
+
+typedef struct
+{
+	Elf32_Sword	d_tag;			/* Dynamic entry type */
+	union
+	{
+		Elf32_Word d_val;			/* Integer value */
+		Elf32_Addr d_ptr;			/* Address value */
+	} d_un;
+} Elf32_Dyn;
+
+typedef struct
+{
+	Elf64_Sxword	d_tag;			/* Dynamic entry type */
+	union
+	{
+		Elf64_Xword d_val;		/* Integer value */
+		Elf64_Addr d_ptr;		/* Address value */
+	} d_un;
+} Elf64_Dyn;
+
+/* Legal values for d_tag (dynamic entry type).  */
+
+#define DT_NULL		0		/* Marks end of dynamic section */
+#define DT_NEEDED	1		/* Name of needed library */
+#define DT_PLTRELSZ	2		/* Size in bytes of PLT relocs */
+#define DT_PLTGOT	3		/* Processor defined value */
+#define DT_HASH		4		/* Address of symbol hash table */
+#define DT_STRTAB	5		/* Address of string table */
+#define DT_SYMTAB	6		/* Address of symbol table */
+#define DT_RELA		7		/* Address of Rela relocs */
+#define DT_RELASZ	8		/* Total size of Rela relocs */
+#define DT_RELAENT	9		/* Size of one Rela reloc */
+#define DT_STRSZ	10		/* Size of string table */
+#define DT_SYMENT	11		/* Size of one symbol table entry */
+#define DT_INIT		12		/* Address of init function */
+#define DT_FINI		13		/* Address of termination function */
+#define DT_SONAME	14		/* Name of shared object */
+#define DT_RPATH	15		/* Library search path (deprecated) */
+#define DT_SYMBOLIC	16		/* Start symbol search here */
+#define DT_REL		17		/* Address of Rel relocs */
+#define DT_RELSZ	18		/* Total size of Rel relocs */
+#define DT_RELENT	19		/* Size of one Rel reloc */
+#define DT_PLTREL	20		/* Type of reloc in PLT */
+#define DT_DEBUG	21		/* For debugging; unspecified */
+#define DT_TEXTREL	22		/* Reloc might modify .text */
+#define DT_JMPREL	23		/* Address of PLT relocs */
+#define	DT_BIND_NOW	24		/* Process relocations of object */
+#define	DT_INIT_ARRAY	25		/* Array with addresses of init fct */
+#define	DT_FINI_ARRAY	26		/* Array with addresses of fini fct */
+#define	DT_INIT_ARRAYSZ	27		/* Size in bytes of DT_INIT_ARRAY */
+#define	DT_FINI_ARRAYSZ	28		/* Size in bytes of DT_FINI_ARRAY */
+#define DT_RUNPATH	29		/* Library search path */
+#define DT_FLAGS	30		/* Flags for the object being loaded */
+#define DT_ENCODING	32		/* Start of encoded range */
+#define DT_PREINIT_ARRAY 32		/* Array with addresses of preinit fct*/
+#define DT_PREINIT_ARRAYSZ 33		/* size in bytes of DT_PREINIT_ARRAY */
+#define	DT_NUM		34		/* Number used */
+#define DT_LOOS		0x6000000d	/* Start of OS-specific */
+#define DT_HIOS		0x6ffff000	/* End of OS-specific */
+#define DT_LOPROC	0x70000000	/* Start of processor-specific */
+#define DT_HIPROC	0x7fffffff	/* End of processor-specific */
+#define	DT_PROCNUM	0x32		/* Most used by any processor */
+
+/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the
+   Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's
+   approach.  */
+#define DT_VALRNGLO	0x6ffffd00
+#define DT_GNU_PRELINKED 0x6ffffdf5	/* Prelinking timestamp */
+#define DT_GNU_CONFLICTSZ 0x6ffffdf6	/* Size of conflict section */
+#define DT_GNU_LIBLISTSZ 0x6ffffdf7	/* Size of library list */
+#define DT_CHECKSUM	0x6ffffdf8
+#define DT_PLTPADSZ	0x6ffffdf9
+#define DT_MOVEENT	0x6ffffdfa
+#define DT_MOVESZ	0x6ffffdfb
+#define DT_FEATURE_1	0x6ffffdfc	/* Feature selection (DTF_*).  */
+#define DT_POSFLAG_1	0x6ffffdfd	/* Flags for DT_* entries, effecting
+					   the following DT_* entry.  */
+#define DT_SYMINSZ	0x6ffffdfe	/* Size of syminfo table (in bytes) */
+#define DT_SYMINENT	0x6ffffdff	/* Entry size of syminfo */
+#define DT_VALRNGHI	0x6ffffdff
+#define DT_VALTAGIDX(tag)	(DT_VALRNGHI - (tag))	/* Reverse order! */
+#define DT_VALNUM 12
+
+/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the
+   Dyn.d_un.d_ptr field of the Elf*_Dyn structure.
+
+   If any adjustment is made to the ELF object after it has been
+   built these entries will need to be adjusted.  */
+#define DT_ADDRRNGLO	0x6ffffe00
+#define DT_GNU_CONFLICT	0x6ffffef8	/* Start of conflict section */
+#define DT_GNU_LIBLIST	0x6ffffef9	/* Library list */
+#define DT_CONFIG	0x6ffffefa	/* Configuration information.  */
+#define DT_DEPAUDIT	0x6ffffefb	/* Dependency auditing.  */
+#define DT_AUDIT	0x6ffffefc	/* Object auditing.  */
+#define	DT_PLTPAD	0x6ffffefd	/* PLT padding.  */
+#define	DT_MOVETAB	0x6ffffefe	/* Move table.  */
+#define DT_SYMINFO	0x6ffffeff	/* Syminfo table.  */
+#define DT_ADDRRNGHI	0x6ffffeff
+#define DT_ADDRTAGIDX(tag)	(DT_ADDRRNGHI - (tag))	/* Reverse order! */
+#define DT_ADDRNUM 10
+
+/* The versioning entry types.  The next are defined as part of the
+   GNU extension.  */
+#define DT_VERSYM	0x6ffffff0
+
+#define DT_RELACOUNT	0x6ffffff9
+#define DT_RELCOUNT	0x6ffffffa
+
+/* These were chosen by Sun.  */
+#define DT_FLAGS_1	0x6ffffffb	/* State flags, see DF_1_* below.  */
+#define	DT_VERDEF	0x6ffffffc	/* Address of version definition
+					   table */
+#define	DT_VERDEFNUM	0x6ffffffd	/* Number of version definitions */
+#define	DT_VERNEED	0x6ffffffe	/* Address of table with needed
+					   versions */
+#define	DT_VERNEEDNUM	0x6fffffff	/* Number of needed versions */
+#define DT_VERSIONTAGIDX(tag)	(DT_VERNEEDNUM - (tag))	/* Reverse order! */
+#define DT_VERSIONTAGNUM 16
+
+/* Sun added these machine-independent extensions in the "processor-specific"
+   range.  Be compatible.  */
+#define DT_AUXILIARY    0x7ffffffd      /* Shared object to load before self */
+#define DT_FILTER       0x7fffffff      /* Shared object to get values from */
+#define DT_EXTRATAGIDX(tag)	((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)
+#define DT_EXTRANUM	3
+
+/* Values of `d_un.d_val' in the DT_FLAGS entry.  */
+#define DF_ORIGIN	0x00000001	/* Object may use DF_ORIGIN */
+#define DF_SYMBOLIC	0x00000002	/* Symbol resolutions starts here */
+#define DF_TEXTREL	0x00000004	/* Object contains text relocations */
+#define DF_BIND_NOW	0x00000008	/* No lazy binding for this object */
+#define DF_STATIC_TLS	0x00000010	/* Module uses the static TLS model */
+
+/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1
+   entry in the dynamic section.  */
+#define DF_1_NOW	0x00000001	/* Set RTLD_NOW for this object.  */
+#define DF_1_GLOBAL	0x00000002	/* Set RTLD_GLOBAL for this object.  */
+#define DF_1_GROUP	0x00000004	/* Set RTLD_GROUP for this object.  */
+#define DF_1_NODELETE	0x00000008	/* Set RTLD_NODELETE for this object.*/
+#define DF_1_LOADFLTR	0x00000010	/* Trigger filtee loading at runtime.*/
+#define DF_1_INITFIRST	0x00000020	/* Set RTLD_INITFIRST for this object*/
+#define DF_1_NOOPEN	0x00000040	/* Set RTLD_NOOPEN for this object.  */
+#define DF_1_ORIGIN	0x00000080	/* $ORIGIN must be handled.  */
+#define DF_1_DIRECT	0x00000100	/* Direct binding enabled.  */
+#define DF_1_TRANS	0x00000200
+#define DF_1_INTERPOSE	0x00000400	/* Object is used to interpose.  */
+#define DF_1_NODEFLIB	0x00000800	/* Ignore default lib search path.  */
+#define DF_1_NODUMP	0x00001000	/* Object can't be dldump'ed.  */
+#define DF_1_CONFALT	0x00002000	/* Configuration alternative created.*/
+#define DF_1_ENDFILTEE	0x00004000	/* Filtee terminates filters search. */
+#define	DF_1_DISPRELDNE	0x00008000	/* Disp reloc applied at build time. */
+#define	DF_1_DISPRELPND	0x00010000	/* Disp reloc applied at run-time.  */
+
+/* Flags for the feature selection in DT_FEATURE_1.  */
+#define DTF_1_PARINIT	0x00000001
+#define DTF_1_CONFEXP	0x00000002
+
+/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry.  */
+#define DF_P1_LAZYLOAD	0x00000001	/* Lazyload following object.  */
+#define DF_P1_GROUPPERM	0x00000002	/* Symbols from next object are not
+					   generally available.  */
+
+/* Special section indices.  */
+
+#define SHN_UNDEF	0		/* Undefined section */
+#define SHN_LORESERVE	0xff00		/* Start of reserved indices */
+#define SHN_LOPROC	0xff00		/* Start of processor-specific */
+#define SHN_HIPROC	0xff1f		/* End of processor-specific */
+#define SHN_LOOS	0xff20		/* Start of OS-specific */
+#define SHN_HIOS	0xff3f		/* End of OS-specific */
+#define SHN_ABS		0xfff1		/* Associated symbol is absolute */
+#define SHN_COMMON	0xfff2		/* Associated symbol is common */
+#define SHN_XINDEX	0xffff		/* Index is in extra table.  */
+#define SHN_HIRESERVE	0xffff		/* End of reserved indices */
+
+/* Legal values for sh_type (section type).  */
+
+#define SHT_NULL	  0		/* Section header table entry unused */
+#define SHT_PROGBITS	  1		/* Program data */
+#define SHT_SYMTAB	  2		/* Symbol table */
+#define SHT_STRTAB	  3		/* String table */
+#define SHT_RELA	  4		/* Relocation entries with addends */
+#define SHT_HASH	  5		/* Symbol hash table */
+#define SHT_DYNAMIC	  6		/* Dynamic linking information */
+#define SHT_NOTE	  7		/* Notes */
+#define SHT_NOBITS	  8		/* Program space with no data (bss) */
+#define SHT_REL		  9		/* Relocation entries, no addends */
+#define SHT_SHLIB	  10		/* Reserved */
+#define SHT_DYNSYM	  11		/* Dynamic linker symbol table */
+#define SHT_INIT_ARRAY	  14		/* Array of constructors */
+#define SHT_FINI_ARRAY	  15		/* Array of destructors */
+#define SHT_PREINIT_ARRAY 16		/* Array of pre-constructors */
+#define SHT_GROUP	  17		/* Section group */
+#define SHT_SYMTAB_SHNDX  18		/* Extended section indeces */
+#define	SHT_NUM		  19		/* Number of defined types.  */
+#define SHT_LOOS	  0x60000000	/* Start OS-specific */
+#define SHT_GNU_LIBLIST	  0x6ffffff7	/* Prelink library list */
+#define SHT_CHECKSUM	  0x6ffffff8	/* Checksum for DSO content.  */
+#define SHT_LOSUNW	  0x6ffffffa	/* Sun-specific low bound.  */
+#define SHT_SUNW_move	  0x6ffffffa
+#define SHT_SUNW_COMDAT   0x6ffffffb
+#define SHT_SUNW_syminfo  0x6ffffffc
+#define SHT_GNU_verdef	  0x6ffffffd	/* Version definition section.  */
+#define SHT_GNU_verneed	  0x6ffffffe	/* Version needs section.  */
+#define SHT_GNU_versym	  0x6fffffff	/* Version symbol table.  */
+#define SHT_HISUNW	  0x6fffffff	/* Sun-specific high bound.  */
+#define SHT_HIOS	  0x6fffffff	/* End OS-specific type */
+#define SHT_LOPROC	  0x70000000	/* Start of processor-specific */
+#define SHT_HIPROC	  0x7fffffff	/* End of processor-specific */
+#define SHT_LOUSER	  0x80000000	/* Start of application-specific */
+#define SHT_HIUSER	  0x8fffffff	/* End of application-specific */
+
+/* Legal values for sh_flags (section flags).  */
+
+#define SHF_WRITE	     (1 << 0)	/* Writable */
+#define SHF_ALLOC	     (1 << 1)	/* Occupies memory during execution */
+#define SHF_EXECINSTR	     (1 << 2)	/* Executable */
+#define SHF_MERGE	     (1 << 4)	/* Might be merged */
+#define SHF_STRINGS	     (1 << 5)	/* Contains nul-terminated strings */
+#define SHF_INFO_LINK	     (1 << 6)	/* `sh_info' contains SHT index */
+#define SHF_LINK_ORDER	     (1 << 7)	/* Preserve order after combining */
+#define SHF_OS_NONCONFORMING (1 << 8)	/* Non-standard OS specific handling
+					   required */
+#define SHF_GROUP	     (1 << 9)	/* Section is member of a group.  */
+#define SHF_TLS		     (1 << 10)	/* Section hold thread-local data.  */
+#define SHF_MASKOS	     0x0ff00000	/* OS-specific.  */
+#define SHF_MASKPROC	     0xf0000000	/* Processor-specific */
+
+/* Section group handling.  */
+#define GRP_COMDAT	0x1		/* Mark group as COMDAT.  */
+
+/* Symbol table entry.  */
+
+typedef struct
+{
+	Elf32_Word	st_name;	/* Symbol name (string tbl index) */
+	Elf32_Addr	st_value;	/* Symbol value */
+	Elf32_Word	st_size;	/* Symbol size */
+	unsigned char	st_info;	/* Symbol type and binding */
+	unsigned char	st_other;	/* Symbol visibility */
+	Elf32_Section	st_shndx;	/* Section index */
+} Elf32_Sym;
+
+typedef struct
+{
+	Elf64_Word	st_name;	/* Symbol name (string tbl index) */
+	unsigned char	st_info;	/* Symbol type and binding */
+	unsigned char st_other;		/* Symbol visibility */
+	Elf64_Section	st_shndx;	/* Section index */
+	Elf64_Addr	st_value;	/* Symbol value */
+	Elf64_Xword	st_size;	/* Symbol size */
+} Elf64_Sym;
+
+/* Relocation table entry without addend (in section of type SHT_REL).  */
+
+typedef struct
+{
+	Elf32_Addr	r_offset;	/* Address */
+	Elf32_Word	r_info;		/* Relocation type and symbol index */
+} Elf32_Rel;
+
+/* I have seen two different definitions of the Elf64_Rel and
+   Elf64_Rela structures, so we'll leave them out until Novell (or
+   whoever) gets their act together.  */
+/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */
+
+typedef struct
+{
+	Elf64_Addr	r_offset;	/* Address */
+	Elf64_Xword	r_info;		/* Relocation type and symbol index */
+} Elf64_Rel;
+
+/* Relocation table entry with addend (in section of type SHT_RELA).  */
+
+typedef struct
+{
+	Elf32_Addr	r_offset;	/* Address */
+	Elf32_Word	r_info;		/* Relocation type and symbol index */
+	Elf32_Sword	r_addend;	/* Addend */
+} Elf32_Rela;
+
+typedef struct
+{
+	Elf64_Addr	r_offset;	/* Address */
+	Elf64_Xword	r_info;		/* Relocation type and symbol index */
+	Elf64_Sxword	r_addend;	/* Addend */
+} Elf64_Rela;
+
+/* How to extract and insert information held in the r_info field.  */
+
+#define ELF32_R_SYM(val)		((val) >> 8)
+#define ELF32_R_TYPE(val)		((val) & 0xff)
+#define ELF32_R_INFO(sym, type)		(((sym) << 8) + ((type) & 0xff))
+
+#define ELF64_R_SYM(i)			((i) >> 32)
+#define ELF64_R_TYPE(i)			((i) & 0xffffffff)
+#define ELF64_R_INFO(sym,type)		((((Elf64_Xword) (sym)) << 32) + (type))
+
+
+/* Intel 80386 specific definitions.  */
+
+/* i386 relocs.  */
+
+#define R_386_NONE	   0		/* No reloc */
+#define R_386_32	   1		/* Direct 32 bit  */
+#define R_386_PC32	   2		/* PC relative 32 bit */
+#define R_386_GOT32	   3		/* 32 bit GOT entry */
+#define R_386_PLT32	   4		/* 32 bit PLT address */
+#define R_386_COPY	   5		/* Copy symbol at runtime */
+#define R_386_GLOB_DAT	   6		/* Create GOT entry */
+#define R_386_JMP_SLOT	   7		/* Create PLT entry */
+#define R_386_RELATIVE	   8		/* Adjust by program base */
+#define R_386_GOTOFF	   9		/* 32 bit offset to GOT */
+#define R_386_GOTPC	   10		/* 32 bit PC relative offset to GOT */
+#define R_386_32PLT	   11
+#define R_386_TLS_TPOFF	   14		/* Offset in static TLS block */
+#define R_386_TLS_IE	   15		/* Address of GOT entry for static TLS
+					   block offset */
+#define R_386_TLS_GOTIE	   16		/* GOT entry for static TLS block
+					   offset */
+#define R_386_TLS_LE	   17		/* Offset relative to static TLS
+					   block */
+#define R_386_TLS_GD	   18		/* Direct 32 bit for GNU version of
+					   general dynamic thread local data */
+#define R_386_TLS_LDM	   19		/* Direct 32 bit for GNU version of
+					   local dynamic thread local data
+					   in LE code */
+#define R_386_16	   20
+#define R_386_PC16	   21
+#define R_386_8		   22
+#define R_386_PC8	   23
+#define R_386_TLS_GD_32	   24		/* Direct 32 bit for general dynamic
+					   thread local data */
+#define R_386_TLS_GD_PUSH  25		/* Tag for pushl in GD TLS code */
+#define R_386_TLS_GD_CALL  26		/* Relocation for call to
+					   __tls_get_addr() */
+#define R_386_TLS_GD_POP   27		/* Tag for popl in GD TLS code */
+#define R_386_TLS_LDM_32   28		/* Direct 32 bit for local dynamic
+					   thread local data in LE code */
+#define R_386_TLS_LDM_PUSH 29		/* Tag for pushl in LDM TLS code */
+#define R_386_TLS_LDM_CALL 30		/* Relocation for call to
+					   __tls_get_addr() in LDM code */
+#define R_386_TLS_LDM_POP  31		/* Tag for popl in LDM TLS code */
+#define R_386_TLS_LDO_32   32		/* Offset relative to TLS block */
+#define R_386_TLS_IE_32	   33		/* GOT entry for negated static TLS
+					   block offset */
+#define R_386_TLS_LE_32	   34		/* Negated offset relative to static
+					   TLS block */
+#define R_386_TLS_DTPMOD32 35		/* ID of module containing symbol */
+#define R_386_TLS_DTPOFF32 36		/* Offset in TLS block */
+#define R_386_TLS_TPOFF32  37		/* Negated offset in static TLS block */
+/* Keep this the last entry.  */
+#define R_386_NUM	   38
+
+#endif /* ASSEMBLY */
+
+#endif /* ELF_H */
diff --git a/error.c b/error.c
new file mode 100644
index 0000000..4218d8c
--- /dev/null
+++ b/error.c
@@ -0,0 +1,627 @@
+/* error.c - MemTest-86  Version 4.1
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ */
+#include "stddef.h"
+#include "stdint.h"
+#include "test.h"
+#include "config.h"
+#include "cpuid.h"
+#include "smp.h"
+#include "dmi.h"
+#include "controller.h"
+
+extern int dmi_err_cnts[MAX_DMI_MEMDEVS];
+extern int beepmode;
+extern short dmi_initialized;
+extern struct cpu_ident cpu_id;
+extern struct barrier_s *barr;
+extern int test_ticks, nticks;
+extern struct tseq tseq[];
+extern volatile int test;
+void poll_errors();
+extern int num_cpus;
+
+static void update_err_counts(void);
+static void print_err_counts(void);
+static void common_err();
+static int syn, chan, len=1;
+
+/*
+ * Display data error message. Don't display duplicate errors.
+ */
+void error(ulong *adr, ulong good, ulong bad)
+{
+	
+	ulong xor;
+
+	spin_lock(&barr->mutex);
+	
+	xor = good ^ bad;
+
+#ifdef USB_WAR
+	/* Skip any errrors that appear to be due to the BIOS using location
+	 * 0x4e0 for USB keyboard support.  This often happens with Intel
+   * 810, 815 and 820 chipsets.  It is possible that we will skip
+	 * a real error but the odds are very low.
+	 */
+	if ((ulong)adr == 0x4e0 || (ulong)adr == 0x410) {
+		return;
+	}
+#endif
+
+	/* A sporadic bug exists in test #6, with SMP enabled, that
+	 * reports false positives on < 65K-0.5MB range. I was
+	 * not able to solve this. After investigations, it seems
+	 * related to a BIOS issue similiar to the one solved by 
+	 * USB_WAR, but for MP Table.
+	 */
+	/* Solved 
+	if (test == 6 && (ulong)adr <= 0x07FFFF && num_cpus > 1) 
+	{
+	  cprint(6,78,"-"); // Debug
+		return;
+	}
+	*/
+	
+	common_err(adr, good, bad, xor, 0);
+	spin_unlock(&barr->mutex);
+}
+
+
+
+/*
+ * Display address error message.
+ * Since this is strictly an address test, trying to create BadRAM
+ * patterns does not make sense.  Just report the error.
+ */
+void ad_err1(ulong *adr1, ulong *mask, ulong bad, ulong good)
+{
+	spin_lock(&barr->mutex);
+	common_err(adr1, good, bad, (ulong)mask, 1);
+	spin_unlock(&barr->mutex);
+}
+
+/*
+ * Display address error message.
+ * Since this type of address error can also report data errors go
+ * ahead and generate BadRAM patterns.
+ */
+void ad_err2(ulong *adr, ulong bad)
+{
+	spin_lock(&barr->mutex);
+	common_err(adr, (ulong)adr, bad, ((ulong)adr) ^ bad, 0);
+	spin_unlock(&barr->mutex);
+}
+
+static void update_err_counts(void)
+{
+	if (beepmode){
+		beep(600);
+		beep(1000);
+	}
+	
+	if (v->pass && v->ecount == 0) {
+		cprint(LINE_MSG, COL_MSG,
+			"                                            ");
+	}
+	++(v->ecount);
+	tseq[test].errors++;
+		
+}
+
+static void print_err_counts(void)
+{
+	int i;
+	char *pp;
+
+	if ((v->ecount > 4096) && (v->ecount % 256 != 0)) return;
+
+	dprint(LINE_INFO, 72, v->ecount, 6, 0);
+/*
+	dprint(LINE_INFO, 56, v->ecc_ecount, 6, 0);
+*/
+
+	/* Paint the error messages on the screen red to provide a vivid */
+	/* indicator that an error has occured */ 
+	if ((v->printmode == PRINTMODE_ADDRESSES ||
+			v->printmode == PRINTMODE_PATTERNS) &&
+			v->msg_line < 24) {
+		for(i=0, pp=(char *)((SCREEN_ADR+v->msg_line*160+1));
+				 i<76; i++, pp+=2) {
+			*pp = 0x47;
+		}
+	}
+}
+
+/*
+ * Print an individual error
+ */
+void common_err( ulong *adr, ulong good, ulong bad, ulong xor, int type) 
+{
+	int i, j, n, x, flag=0;
+	ulong page, offset;
+	int patnchg;
+	ulong mb;
+
+	update_err_counts();
+
+	switch(v->printmode) {
+	case PRINTMODE_SUMMARY:
+		/* Don't do anything for a parity error. */
+		if (type == 3) {
+			return;
+		}
+
+		/* Address error */
+		if (type == 1) {
+			xor = good ^ bad;
+		}
+
+		/* Ecc correctable errors */
+		if (type == 2) {
+			/* the bad value is the corrected flag */
+			if (bad) {
+				v->erri.cor_err++;
+			}
+			page = (ulong)adr;
+			offset = good;
+		} else {
+			page = page_of(adr);
+			offset = (ulong)adr & 0xFFF;
+		}
+			
+		/* Calc upper and lower error addresses */
+		if (v->erri.low_addr.page > page) {
+			v->erri.low_addr.page = page;
+			v->erri.low_addr.offset = offset;
+			flag++;
+		} else if (v->erri.low_addr.page == page &&
+				v->erri.low_addr.offset > offset) {
+			v->erri.low_addr.offset = offset;
+			v->erri.high_addr.offset = offset;
+			flag++;
+		} else if (v->erri.high_addr.page < page) {
+			v->erri.high_addr.page = page;
+			flag++;
+		}
+		if (v->erri.high_addr.page == page &&
+				v->erri.high_addr.offset < offset) {
+			v->erri.high_addr.offset = offset;
+			flag++;
+		}
+
+		/* Calc bits in error */
+		for (i=0, n=0; i<32; i++) {
+			if (xor>>i & 1) {
+				n++;
+			}
+		}
+		v->erri.tbits += n;
+		if (n > v->erri.max_bits) {
+			v->erri.max_bits = n;
+			flag++;
+		}
+		if (n < v->erri.min_bits) {
+			v->erri.min_bits = n;
+			flag++;
+		}
+		if (v->erri.ebits ^ xor) {
+			flag++;
+		}
+		v->erri.ebits |= xor;
+
+	 	/* Calc max contig errors */
+		len = 1;
+		if ((ulong)adr == (ulong)v->erri.eadr+4 ||
+				(ulong)adr == (ulong)v->erri.eadr-4 ) {
+			len++;
+		}
+		if (len > v->erri.maxl) {
+			v->erri.maxl = len;
+			flag++;
+		}
+		v->erri.eadr = (ulong)adr;
+
+		if (v->erri.hdr_flag == 0) {
+			clear_scroll();
+			cprint(LINE_HEADER+0, 1,  "Error Confidence Value:");
+			cprint(LINE_HEADER+1, 1,  "  Lowest Error Address:");
+			cprint(LINE_HEADER+2, 1,  " Highest Error Address:");
+			cprint(LINE_HEADER+3, 1,  "    Bits in Error Mask:");
+			cprint(LINE_HEADER+4, 1,  " Bits in Error - Total:");
+			cprint(LINE_HEADER+4, 29,  "Min:    Max:    Avg:");
+			cprint(LINE_HEADER+5, 1,  " Max Contiguous Errors:");
+			x = 24;
+			if (dmi_initialized) {
+			  for ( i=0; i < MAX_DMI_MEMDEVS;){
+			    n = LINE_HEADER+7;
+			    for (j=0; j<4; j++) {
+				if (dmi_err_cnts[i] >= 0) {
+					dprint(n, x, i, 2, 0);
+					cprint(n, x+2, ": 0");
+				}
+				i++;
+				n++;
+			    }
+			    x += 10;
+			  }
+			}
+			
+			cprint(LINE_HEADER+0, 64,   "Test  Errors");
+			v->erri.hdr_flag++;
+		}
+		if (flag) {
+		  /* Calc bits in error */
+		  for (i=0, n=0; i<32; i++) {
+			if (v->erri.ebits>>i & 1) {
+				n++;
+			}
+		  }
+		  page = v->erri.low_addr.page;
+		  offset = v->erri.low_addr.offset;
+		  mb = page >> 8;
+		  hprint(LINE_HEADER+1, 25, page);
+		  hprint2(LINE_HEADER+1, 33, offset, 3);
+		  cprint(LINE_HEADER+1, 36, " -      . MB");
+		  dprint(LINE_HEADER+1, 39, mb, 5, 0);
+		  dprint(LINE_HEADER+1, 45, ((page & 0xF)*10)/16, 1, 0);
+		  page = v->erri.high_addr.page;
+		  offset = v->erri.high_addr.offset;
+		  mb = page >> 8;
+		  hprint(LINE_HEADER+2, 25, page);
+		  hprint2(LINE_HEADER+2, 33, offset, 3);
+		  cprint(LINE_HEADER+2, 36, " -      . MB");
+		  dprint(LINE_HEADER+2, 39, mb, 5, 0);
+		  dprint(LINE_HEADER+2, 45, ((page & 0xF)*10)/16, 1, 0);
+		  hprint(LINE_HEADER+3, 25, v->erri.ebits);
+		  dprint(LINE_HEADER+4, 25, n, 2, 1);
+		  dprint(LINE_HEADER+4, 34, v->erri.min_bits, 2, 1);
+		  dprint(LINE_HEADER+4, 42, v->erri.max_bits, 2, 1);
+		  dprint(LINE_HEADER+4, 50, v->erri.tbits/v->ecount, 2, 1);
+		  dprint(LINE_HEADER+5, 25, v->erri.maxl, 7, 1);
+		  x = 28;
+		  for ( i=0; i < MAX_DMI_MEMDEVS;){
+		  	n = LINE_HEADER+7;
+			for (j=0; j<4; j++) {
+				if (dmi_err_cnts[i] > 0) {
+					dprint (n, x, dmi_err_cnts[i], 7, 1);
+				}
+				i++;
+				n++;
+			}
+			x += 10;
+		  }
+		  			
+		  for (i=0; tseq[i].msg != NULL; i++) {
+			dprint(LINE_HEADER+1+i, 66, i, 2, 0);
+			dprint(LINE_HEADER+1+i, 68, tseq[i].errors, 8, 0);
+	  	  }
+		}
+		if (v->erri.cor_err) {
+		  dprint(LINE_HEADER+6, 25, v->erri.cor_err, 8, 1);
+		}
+		break;
+
+	case PRINTMODE_ADDRESSES:
+		/* Don't display duplicate errors */
+		if ((ulong)adr == (ulong)v->erri.eadr &&
+				 xor == v->erri.exor) {
+			return;
+		}
+		if (v->erri.hdr_flag == 0) {
+			clear_scroll();
+			cprint(LINE_HEADER, 0,
+"Tst  Pass   Failing Address          Good       Bad     Err-Bits  Count CPU");
+			cprint(LINE_HEADER+1, 0,
+"---  ----  -----------------------  --------  --------  --------  ----- ----");
+			v->erri.hdr_flag++;
+		}
+		/* Check for keyboard input */
+		check_input();
+		scroll();
+	
+		if ( type == 2 || type == 3) {
+			page = (ulong)adr;
+			offset = good;
+		} else {
+			page = page_of(adr);
+			offset = ((unsigned long)adr) & 0xFFF;
+		}
+		mb = page >> 8;
+		dprint(v->msg_line, 0, test+1, 3, 0);
+		dprint(v->msg_line, 4, v->pass, 5, 0);
+		hprint(v->msg_line, 11, page);
+		hprint2(v->msg_line, 19, offset, 3);
+		cprint(v->msg_line, 22, " -      . MB");
+		dprint(v->msg_line, 25, mb, 5, 0);
+		dprint(v->msg_line, 31, ((page & 0xF)*10)/16, 1, 0);
+
+		if (type == 3) {
+			/* ECC error */
+			cprint(v->msg_line, 36, 
+			  bad?"corrected           ": "uncorrected         ");
+			hprint2(v->msg_line, 60, syn, 4);
+			cprint(v->msg_line, 68, "ECC"); 
+			dprint(v->msg_line, 74, chan, 2, 0);
+		} else if (type == 2) {
+			cprint(v->msg_line, 36, "Parity error detected                ");
+		} else {
+			hprint(v->msg_line, 36, good);
+			hprint(v->msg_line, 46, bad);
+			hprint(v->msg_line, 56, xor);
+			dprint(v->msg_line, 66, v->ecount, 5, 0);
+			dprint(v->msg_line, 74, smp_my_cpu_num(), 2,1);
+			v->erri.exor = xor;
+		}
+		v->erri.eadr = (ulong)adr;
+		print_err_counts();
+		break;
+
+	case PRINTMODE_PATTERNS:
+		if (v->erri.hdr_flag == 0) {
+			clear_scroll();
+			v->erri.hdr_flag++;
+		}
+		/* Do not do badram patterns from test 0 or 5 */
+		if (test == 0 || test == 5) {
+			return;
+		}
+		/* Only do patterns for data errors */
+		if ( type != 0) {
+			return;
+		}
+		/* Process the address in the pattern administration */
+		patnchg=insertaddress ((ulong) adr);
+		if (patnchg) { 
+			printpatn();
+		}
+		break;
+
+	case PRINTMODE_NONE:
+		if (v->erri.hdr_flag == 0) {
+			clear_scroll();
+			v->erri.hdr_flag++;
+		}
+		break;
+	}
+}
+
+/*
+ * Print an ecc error
+ */
+void print_ecc_err(unsigned long page, unsigned long offset, 
+	int corrected, unsigned short syndrome, int channel)
+{
+	++(v->ecc_ecount);
+	syn = syndrome;
+	chan = channel;
+	common_err((ulong *)page, offset, corrected, 0, 2);
+}
+
+#ifdef PARITY_MEM
+/*
+ * Print a parity error message
+ */
+void parity_err( unsigned long edi, unsigned long esi) 
+{
+	unsigned long addr;
+
+	if (test == 5) {
+		addr = esi;
+	} else {
+		addr = edi;
+	}
+	common_err((ulong *)addr, addr & 0xFFF, 0, 0, 3);
+}
+#endif
+
+/*
+ * Print the pattern array as a LILO boot option addressing BadRAM support.
+ */
+void printpatn (void)
+{
+       int idx=0;
+       int x;
+
+	/* Check for keyboard input */
+	check_input();
+
+       if (v->numpatn == 0)
+               return;
+
+       scroll();
+
+       cprint (v->msg_line, 0, "badram=");
+       x=7;
+
+       for (idx = 0; idx < v->numpatn; idx++) {
+
+               if (x > 80-22) {
+                       scroll();
+                       x=7;
+               }
+               cprint (v->msg_line, x, "0x");
+               hprint (v->msg_line, x+2,  v->patn[idx].adr );
+               cprint (v->msg_line, x+10, ",0x");
+               hprint (v->msg_line, x+13, v->patn[idx].mask);
+               if (idx+1 < v->numpatn)
+                       cprint (v->msg_line, x+21, ",");
+               x+=22;
+       }
+}
+	
+/*
+ * Show progress by displaying elapsed time and update bar graphs
+ */
+short spin_idx[MAX_CPUS];
+char spin[4] = {'|','/','-','\\'};
+
+void do_tick(int me)
+{
+	int i, j, pct;
+	ulong h, l, n, t;
+	extern int mstr_cpu;
+
+	if (++spin_idx[me] > 3) {
+		spin_idx[me] = 0;
+	}
+	cplace(8, me+7, spin[spin_idx[me]]);
+	
+	
+	/* Check for keyboard input */
+	if (me == mstr_cpu) {
+		check_input();
+	}
+	/* A barrier here holds the other CPUs until the configuration
+	 * changes are done */
+	s_barrier();
+
+	/* Only the first selected CPU does the update */
+	if (me !=  mstr_cpu) {
+		return;
+	}
+
+	/* FIXME only print serial error messages from the tick handler */
+	if (v->ecount) {
+		print_err_counts();
+	}
+	
+	nticks++;
+	v->total_ticks++;
+
+	if (test_ticks) {
+		pct = 100*nticks/test_ticks;
+		if (pct > 100) {
+			pct = 100;
+		}
+	} else {
+		pct = 0;
+	}
+	dprint(2, COL_MID+4, pct, 3, 0);
+	i = (BAR_SIZE * pct) / 100;
+	while (i > v->tptr) {
+		if (v->tptr >= BAR_SIZE) {
+			break;
+		}
+		cprint(2, COL_MID+9+v->tptr, "#");
+		v->tptr++;
+	}
+	
+	if (v->pass_ticks) {
+		pct = 100*v->total_ticks/v->pass_ticks;
+		if (pct > 100) {
+			pct = 100;
+		}
+	} else {
+		pct = 0;
+        }
+	dprint(1, COL_MID+4, pct, 3, 0);
+	i = (BAR_SIZE * pct) / 100;
+	while (i > v->pptr) {
+		if (v->pptr >= BAR_SIZE) {
+			break;
+		}
+		cprint(1, COL_MID+9+v->pptr, "#");
+		v->pptr++;
+	}
+
+	if (v->ecount && v->printmode == PRINTMODE_SUMMARY) {
+		/* Compute confidence score */
+		pct = 0;
+
+		/* If there are no errors within 1mb of start - end addresses */
+		h = v->pmap[v->msegs - 1].end - 0x100;
+		if (v->erri.low_addr.page >  0x100 &&
+				 v->erri.high_addr.page < h) {
+			pct += 8;
+		}
+
+		/* Errors for only some tests */
+		if (v->pass) {
+			for (i=0, n=0; tseq[i].msg != NULL; i++) {
+				if (tseq[i].errors == 0) {
+					n++;
+				}
+			}
+			pct += n*3;
+		} else {
+			for (i=0, n=0; i<test; i++) {
+				if (tseq[i].errors == 0) {
+					n++;
+				}
+			}
+			pct += n*2;
+			
+		}
+
+		/* Only some bits in error */
+		n = 0;
+		if (v->erri.ebits & 0xf) n++;
+		if (v->erri.ebits & 0xf0) n++;
+		if (v->erri.ebits & 0xf00) n++;
+		if (v->erri.ebits & 0xf000) n++;
+		if (v->erri.ebits & 0xf0000) n++;
+		if (v->erri.ebits & 0xf00000) n++;
+		if (v->erri.ebits & 0xf000000) n++;
+		if (v->erri.ebits & 0xf0000000) n++;
+		pct += (8-n)*2;
+
+		/* Adjust the score */
+		pct = pct*100/22;
+/*
+		if (pct > 100) {
+			pct = 100;
+		}
+*/
+		dprint(LINE_HEADER+0, 25, pct, 3, 1);
+	}
+		
+
+	/* We can't do the elapsed time unless the rdtsc instruction
+	 * is supported
+	 */
+	if (cpu_id.fid.bits.rdtsc) {
+		asm __volatile__(
+			"rdtsc":"=a" (l),"=d" (h));
+		asm __volatile__ (
+			"subl %2,%0\n\t"
+			"sbbl %3,%1"
+			:"=a" (l), "=d" (h)
+			:"g" (v->startl), "g" (v->starth),
+			"0" (l), "1" (h));
+		t = h * ((unsigned)0xffffffff / v->clks_msec) / 1000;
+		t += (l / v->clks_msec) / 1000;
+		i = t % 60;
+		j = i % 10;
+	
+		if(j != v->each_sec)
+		{	
+			
+			dprint(LINE_TIME, COL_TIME+9, i % 10, 1, 0);
+			dprint(LINE_TIME, COL_TIME+8, i / 10, 1, 0);
+			t /= 60;
+			i = t % 60;
+			dprint(LINE_TIME, COL_TIME+6, i % 10, 1, 0);
+			dprint(LINE_TIME, COL_TIME+5, i / 10, 1, 0);
+			t /= 60;
+			dprint(LINE_TIME, COL_TIME, t, 4, 0);
+		
+			if(v->check_temp > 0 && !(v->fail_safe & 4))
+				{
+					coretemp();	
+				}	
+			v->each_sec = j;	
+		}
+	
+	}
+	
+
+
+	/* Poll for ECC errors */
+/*
+	poll_errors();
+*/
+}
+
diff --git a/extra.c b/extra.c
new file mode 100644
index 0000000..ff580f2
--- /dev/null
+++ b/extra.c
@@ -0,0 +1,981 @@
+// This is the extra stuff added to the memtest+ from memtest.org
+// Code from Eric Nelson and Wee
+// (Checked without vendor-specific optimization before adding)
+/* extra.c -
+ *
+ * Released under version 2 of the Gnu Public License.
+ *
+ */
+
+#include "test.h"
+#include "screen_buffer.h"
+#include "pci.h"
+#include "extra.h"
+
+static int ctrl = -1;
+
+struct memory_controller {
+	unsigned vendor;
+	unsigned device;
+	int worked;
+	void (*change_timing)(int cas, int rcd, int rp, int ras);
+};
+
+static struct memory_controller mem_ctr[] = {
+
+	/* AMD 64*/
+	{ 0x1022, 0x1100,  1, change_timing_amd64}, //AMD64 hypertransport link
+
+	/* nVidia */
+	{ 0x10de, 0x01E0,  0, change_timing_nf2},  // nforce2
+
+	/* Intel */
+	{ 0x8086, 0x2570,  0, change_timing_i875}, //Intel i848/i865
+	{ 0x8086, 0x2578,  0, change_timing_i875}, //Intel i875P
+	{ 0x8086, 0x2580,  0, change_timing_i925}, //Intel i915P/G
+	{ 0x8086, 0x2584,  0, change_timing_i925}, //Intel i925X
+	{ 0x8086, 0x2770,  0, change_timing_i925}, //Intel Lakeport
+	{ 0x8086, 0x3580,  0, change_timing_i852}, //Intel i852GM - i855GM/GME (But not i855PM)
+};
+
+struct drc {
+	unsigned t_rwt;
+	unsigned t_wrt;
+	unsigned t_ref;
+	unsigned t_en2t;
+	unsigned t_rwqb;
+	unsigned t_rct;
+	unsigned t_rrd;
+	unsigned t_wr;
+};
+
+static struct drc a64;
+
+void find_memctr(void)  // Basically copy from the find_controller function
+{
+	unsigned long vendor;
+	unsigned long device;
+	unsigned long a64;
+	int i= 0;
+	int result;
+
+	result = pci_conf_read(0, 0, 0, PCI_VENDOR_ID, 2, &vendor);
+	result = pci_conf_read(0, 0, 0, PCI_DEVICE_ID, 2, &device);
+
+	pci_conf_read(0, 24, 0, 0x00, 4, &a64);
+
+	if( a64 == 0x11001022) 	{
+		ctrl = 0;
+		return;
+	}
+
+	if (result == 0) {
+		for(i = 1; i < sizeof(mem_ctr)/sizeof(mem_ctr[0]); i++) {
+			if ((mem_ctr[i].vendor == vendor) &&
+				(mem_ctr[i].device == device))
+			{
+				ctrl = i;
+				return;
+			}
+		}
+	}
+	ctrl = -1;
+}
+
+void a64_parameter(void)
+{
+
+	ulong dramtlr;
+
+	if ( 0 == pci_conf_read(0, 24, 2, 0x88, 4, &dramtlr) )
+	{
+		a64.t_rct = 7 + ((dramtlr>>4) & 0x0F);
+		a64.t_rrd = 0 + ((dramtlr>>16) & 0x7);
+		a64.t_wr  = 2 + ((dramtlr>>28) & 0x1);
+	}
+
+	if ( 0 == pci_conf_read(0, 24, 2, 0x8C, 4, &dramtlr) )
+	{
+		a64.t_rwt = 1 + ((dramtlr>>4) & 0x07);
+		a64.t_wrt = 1 +  (dramtlr      & 0x1);
+		a64.t_ref = 1 + ((dramtlr>>11) & 0x3);
+	}
+
+	if ( 0 == pci_conf_read(0, 24, 2, 0x90, 4, &dramtlr) )
+	{
+		a64.t_en2t = 1 + ((dramtlr>>28) & 0x1);
+		a64.t_rwqb = 2 << ((dramtlr>>14) & 0x3);
+	}
+}
+
+
+
+void change_timing(int cas, int rcd, int rp, int ras)
+{
+	find_memctr();
+	if ((ctrl == -1) || ( ctrl > sizeof(mem_ctr)/sizeof(mem_ctr[0])))
+	{
+		return;
+	}
+
+	mem_ctr[ctrl].change_timing(cas, rcd, rp, ras);
+	restart();
+}
+
+void amd64_option()
+{
+	int rwt=0, wrt=0, ref=0, en2t=0, rct=0, rrd=0, rwqb=0, wr = 0, flag=0;
+
+	if ((ctrl == -1) || ( ctrl > sizeof(mem_ctr)/sizeof(mem_ctr[0])))
+	{
+		return;
+	}
+
+	if (mem_ctr[ctrl].worked)
+	{
+		a64_parameter();
+		cprint(POP_Y+1, POP_X+4, "AMD64 options");
+
+		cprint(POP_Y+3, POP_X+4, "(1) Rd-Wr Delay   : ");
+		dprint(POP_Y+3, POP_X+24, a64.t_rwt, 2, 0);
+
+		cprint(POP_Y+4, POP_X+4, "(2) Wr-Rd Delay   : ");
+		dprint(POP_Y+4, POP_X+24, a64.t_wrt, 2, 0);
+
+		cprint(POP_Y+5, POP_X+4, "(3) Rd/Wr Bypass  : ");
+		dprint(POP_Y+5, POP_X+24, a64.t_rwqb, 2, 0);
+
+		cprint(POP_Y+6, POP_X+4, "(4) Refresh Rate  : ");
+		switch ( a64.t_ref)
+		{
+		case 1 : cprint(POP_Y+6, POP_X+23, "15.6us"); break;
+		case 2 : cprint(POP_Y+6, POP_X+23, " 7.8us"); break;
+		case 3 : cprint(POP_Y+6, POP_X+23, " 3.9us"); break;
+		}
+		cprint(POP_Y+7, POP_X+4,  "(5) Command Rate  :");
+		dprint(POP_Y+7, POP_X+24, a64.t_en2t, 2, 0);
+		cprint(POP_Y+7, POP_X+26, "T ");
+
+		cprint(POP_Y+8, POP_X+4,  "(6) Row Cycle Time: ");
+		dprint(POP_Y+8, POP_X+24, a64.t_rct, 2, 0);
+
+		cprint(POP_Y+9, POP_X+4, "(7) RAS-RAS Delay : ");
+		dprint(POP_Y+9, POP_X+24, a64.t_rrd, 2, 0);
+
+		cprint(POP_Y+10, POP_X+4, "(8) Write Recovery: ");
+		dprint(POP_Y+10, POP_X+24, a64.t_wr, 2, 0);
+
+		cprint(POP_Y+11, POP_X+4,"(0) Cancel   ");
+
+		while(!flag)
+		{
+			switch(get_key())
+			{
+			case 2:
+				popclear();
+				// read-to-write delay
+				cprint(POP_Y+3, POP_X+4, "Rd-Wr delay ");
+				cprint(POP_Y+4, POP_X+4, " (2 - 6 cycles)");
+				cprint(POP_Y+5, POP_X+4, "Current: ");
+				dprint(POP_Y+5, POP_X+14, a64.t_rwt, 4, 0);
+				cprint(POP_Y+7, POP_X+4, "New: ");
+				rwt = getval(POP_Y+7, POP_X+12, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 3:
+				popclear();
+				// read-to-write delay
+				cprint(POP_Y+3, POP_X+4, "Wr-Rd delay ");
+				cprint(POP_Y+4, POP_X+4, " (1 - 2 cycles)");
+				cprint(POP_Y+5, POP_X+4, "Current: ");
+				dprint(POP_Y+5, POP_X+14, a64.t_wrt, 4, 0);
+				cprint(POP_Y+7, POP_X+4, "New: ");
+				wrt = getval(POP_Y+7, POP_X+12, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 4:
+				popclear();
+				// Read write queue bypass count
+				cprint(POP_Y+3, POP_X+4, "Rd/Wr bypass ");
+				cprint(POP_Y+4, POP_X+4, " (2, 4 or 8 )");
+				cprint(POP_Y+5, POP_X+4, "Current: ");
+				dprint(POP_Y+5, POP_X+14, a64.t_rwqb, 2, 0);
+				cprint(POP_Y+7, POP_X+4, "New: ");
+				rwqb = getval(POP_Y+7, POP_X+11, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 5:
+				popclear();
+				// refresh rate
+				cprint(POP_Y+3, POP_X+4, "Refresh rate ");
+				cprint(POP_Y+4, POP_X+4, "Current: ");
+				switch ( a64.t_ref){
+				case 1 : cprint(POP_Y+4, POP_X+14, "15.6us"); break;
+				case 2 : cprint(POP_Y+4, POP_X+14, "7.8us "); break;
+				case 3 : cprint(POP_Y+4, POP_X+14, "3.9us "); break;
+				}
+				cprint(POP_Y+6, POP_X+4, "New: ");
+				cprint(POP_Y+7, POP_X+4, "(1) 15.6us");
+				cprint(POP_Y+8, POP_X+4, "(2) 7.8us ");
+				cprint(POP_Y+9, POP_X+4, "(3) 3.9us ");
+				ref = getval(POP_Y+6, POP_X+11, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 6:
+				popclear();
+				//Enable 2T command and addressing
+				cprint(POP_Y+3, POP_X+4, "Command rate:");
+				cprint(POP_Y+5, POP_X+4, "(1) 1T "); //only supoprted by CG revision and later
+				cprint(POP_Y+6, POP_X+4, "(2) 2T ");
+				en2t = getval(POP_Y+3, POP_X+22, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 7:
+				popclear();
+				//Row cycle time
+				cprint(POP_Y+3, POP_X+4, "Row cycle time: ");
+				cprint(POP_Y+4, POP_X+4, " (7 - 20 cycles)");
+				cprint(POP_Y+5, POP_X+4, "Current: ");
+				dprint(POP_Y+5, POP_X+14, a64.t_rct, 4, 0);
+				cprint(POP_Y+7, POP_X+4, "New: ");
+				rct = getval(POP_Y+7, POP_X+12, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 8:
+				popclear();
+				//Active-to-Active RAS Delay
+				cprint(POP_Y+3, POP_X+4, "RAS-RAS Delay: ");
+				cprint(POP_Y+4, POP_X+4, " (2 - 4 cycles)");
+				cprint(POP_Y+5, POP_X+4, "Current: ");
+				dprint(POP_Y+5, POP_X+14, a64.t_rrd, 2, 0);
+				cprint(POP_Y+7, POP_X+4, "New: ");
+				rrd = getval(POP_Y+7, POP_X+12, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 9:
+				popclear();
+				//Active-to-Active RAS Delay
+				cprint(POP_Y+3, POP_X+4, "Write Recovery: ");
+				cprint(POP_Y+4, POP_X+4, " (2 - 3 cycles)");
+				cprint(POP_Y+5, POP_X+4, "Current: ");
+				dprint(POP_Y+5, POP_X+14, a64.t_wr, 2, 0);
+				cprint(POP_Y+7, POP_X+4, "New: ");
+				wr = getval(POP_Y+7, POP_X+12, 0);
+				amd64_tweak(rwt, wrt, ref,en2t, rct, rrd, rwqb, wr);
+				break;
+
+			case 11:
+			case 57:
+				flag++;
+				/* 0/CR - Cancel */
+				break;
+			}
+		}
+	}
+}
+
+void get_option()
+{
+	int cas =0, rp=0, rcd=0, ras=0, sflag = 0 ;
+
+	while(!sflag)
+	{
+		switch(get_key())
+		{
+		case 2:
+			popclear();
+			cas = get_cas();
+			popclear();
+
+			cprint(POP_Y+3, POP_X+8, "tRCD: ");
+			rcd = getval(POP_Y+3, POP_X+15, 0);
+			popclear();
+
+			cprint(POP_Y+3, POP_X+8, "tRP: ");
+			rp = getval(POP_Y+3, POP_X+15, 0);
+			popclear();
+
+			cprint(POP_Y+3, POP_X+8, "tRAS: ");
+			ras = getval(POP_Y+3, POP_X+15, 0);
+			popclear();
+			change_timing(cas, rcd, rp, ras);
+			break;
+
+		case 3:
+			popclear();
+			cas = get_cas();
+			change_timing(cas, 0, 0, 0);
+			sflag++;
+			break;
+
+		case 4:
+			popclear();
+			cprint(POP_Y+3, POP_X+8, "tRCD: ");
+			rcd =getval(POP_Y+3, POP_X+15, 0);
+			change_timing(0, rcd, 0, 0);
+			sflag++;
+			break;
+
+		case 5:
+			popclear();
+			cprint(POP_Y+3, POP_X+8, "tRP: ");
+			rp =getval(POP_Y+3, POP_X+15, 0);
+			change_timing(0, 0, rp, 0);
+			sflag++;
+			break;
+
+		case 6:
+			popclear();
+			cprint(POP_Y+3, POP_X+8, "tRAS: ");
+			ras =getval(POP_Y+3, POP_X+15, 0);
+			change_timing(0, 0, 0, ras);
+			sflag++;
+			break;
+
+		case 7:
+			popclear();
+			amd64_option();
+			sflag++;
+			popclear();
+			break;
+
+		case 8:
+			break;
+
+		case 11:
+		case 57:
+			sflag++;
+			/* 0/CR - Cancel */
+			break;
+		}
+	}
+}
+
+void get_option_1()
+{
+	int rp=0, rcd=0, ras=0, sflag = 0 ;
+
+	while(!sflag)
+	{
+		switch(get_key())
+		{
+		case 2:
+			popclear();
+			cprint(POP_Y+3, POP_X+8, "tRCD: ");
+			rcd = getval(POP_Y+3, POP_X+15, 0);
+			popclear();
+
+			cprint(POP_Y+3, POP_X+8, "tRP: ");
+			rp = getval(POP_Y+3, POP_X+15, 0);
+			popclear();
+
+			cprint(POP_Y+3, POP_X+8, "tRAS: ");
+			ras = getval(POP_Y+3, POP_X+15, 0);
+			popclear();
+			change_timing(0, rcd, rp, ras);
+			break;
+
+		case 3:
+			popclear();
+			cprint(POP_Y+3, POP_X+8, "tRCD: ");
+			rcd =getval(POP_Y+3, POP_X+15, 0);
+			change_timing(0, rcd, 0, 0);
+			break;
+
+		case 4:
+			popclear();
+			cprint(POP_Y+3, POP_X+8, "tRP: ");
+			rp =getval(POP_Y+3, POP_X+15, 0);
+			change_timing(0, 0, rp, 0);
+			break;
+
+		case 5:
+			popclear();
+			cprint(POP_Y+3, POP_X+8, "tRAS: ");
+			ras =getval(POP_Y+3, POP_X+15, 0);
+			change_timing(0, 0, 0, ras);
+			break;
+
+		case 6:
+			popclear();
+			amd64_option();
+			sflag++;
+			popclear();
+			break;
+
+		case 7:
+			break;
+
+		case 11:
+		case 57:
+			sflag++;
+			/* 0/CR - Cancel */
+			break;
+		}
+	}
+}
+
+
+void get_menu(void)
+{
+	int menu ;
+
+	find_memctr();
+
+	switch(ctrl)
+	{
+	case 0: menu = 2;	break;
+	case 1:
+	case 2:
+	case 3:
+	case 4:	menu = 0;	break;
+	case 5: menu = 1;	break;
+	case 6: menu = 0;	break;
+	default: menu = -1;	break;
+	}
+
+	if (menu == -1)
+	{
+		popclear();
+	}
+	else if (menu == 0)
+	{
+		cprint(POP_Y+1, POP_X+2, "Modify Timing:");
+		cprint(POP_Y+3, POP_X+5, "(1) Modify All   ");
+		cprint(POP_Y+4, POP_X+5, "(2) Modify tCAS  ");
+		cprint(POP_Y+5, POP_X+5, "(3) Modify tRCD  ");
+		cprint(POP_Y+6, POP_X+5, "(4) Modify tRP   ");
+		cprint(POP_Y+7, POP_X+5, "(5) Modify tRAS  ");
+		cprint(POP_Y+8, POP_X+5, "(0) Cancel");
+		wait_keyup();
+	 	get_option();
+	}
+	else if (menu == 1)
+	{
+		cprint(POP_Y+1, POP_X+2, "Modify Timing:");
+		cprint(POP_Y+3, POP_X+5, "(1) Modify All   ");
+		cprint(POP_Y+4, POP_X+5, "(2) Modify tRCD  ");
+		cprint(POP_Y+5, POP_X+5, "(3) Modify tRP   ");
+		cprint(POP_Y+6, POP_X+5, "(4) Modify tRAS  ");
+		cprint(POP_Y+7, POP_X+5, "(0) Cancel");
+		wait_keyup();
+	 	get_option();
+	}
+	else  // AMD64 special menu
+	{
+		cprint(POP_Y+1, POP_X+2, "Modify Timing:");
+		cprint(POP_Y+3, POP_X+5, "(1) Modify All   ");
+		cprint(POP_Y+4, POP_X+5, "(2) Modify tRCD  ");
+		cprint(POP_Y+5, POP_X+5, "(3) Modify tRP   ");
+		cprint(POP_Y+6, POP_X+5, "(4) Modify tRAS  ");
+		cprint(POP_Y+7, POP_X+5, "(5) AMD64 Options");
+		cprint(POP_Y+8, POP_X+5, "(0) Cancel");
+		wait_keyup();
+	 	get_option_1();
+	}
+}
+
+int get_cas(void)
+{
+	int i852=0, cas=0;
+	ulong drc, ddr;
+	long *ptr;
+
+	switch(ctrl)
+	{
+	case 0: ddr = 1; break;
+	case 1:
+	case 2:
+	case 3:	ddr = 1; break;
+	case 4:
+		pci_conf_read( 0, 0, 0, 0x44, 4, &ddr);
+		ddr &= 0xFFFFC000;
+		ptr=(long*)(ddr+0x120);
+		drc = *ptr;
+
+		if ((drc & 3) == 2) ddr = 2;
+		else ddr = 1;
+		break;
+	case 5: ddr = 2; break;
+	case 6: ddr = 1; i852 = 1; break;
+	default: ddr = 1;
+	}
+
+	if (ddr == 1)
+	{
+		cprint(POP_Y+3, POP_X+8, "tCAS:  ");
+		cprint(POP_Y+5, POP_X+8, "(1) CAS 2.5 ");
+		cprint(POP_Y+6, POP_X+8, "(2) CAS 2   ");
+		if(!i852) {
+			cprint(POP_Y+7, POP_X+8, "(3) CAS 3   ");
+		}
+		cas = getval(POP_Y+3, POP_X+15, 0);
+	}
+	else if (ddr == 2)
+	{
+		cprint(POP_Y+3, POP_X+8, "tCAS:  ");
+		cprint(POP_Y+5, POP_X+8, "(1) CAS 4 ");
+		cprint(POP_Y+6, POP_X+8, "(2) CAS 3 ");
+		cprint(POP_Y+7, POP_X+8, "(3) CAS 5 ");
+		cas = getval(POP_Y+3, POP_X+15, 0);
+	}
+	else
+	{
+		cas = -1;
+	}
+
+	popclear();
+	return (cas);
+}
+
+/////////////////////////////////////////////////////////
+// here we go for the exciting timing change part...   //
+/////////////////////////////////////////////////////////
+
+void change_timing_i852(int cas, int rcd, int rp, int ras) {
+
+	ulong dramtlr;
+	ulong int1, int2;
+
+	pci_conf_read(0, 0, 1, 0x60, 4, &dramtlr);
+
+	// CAS Latency (tCAS)
+	int1 = dramtlr & 0xFF9F;
+	if      (cas == 2) { int2 = int1 ^ 0x20; }
+	else if (cas == 1) { int2 = int1; }
+	else		   { int2 = dramtlr; }
+
+
+	// RAS-To-CAS (tRCD)
+	int1 = int2 & 0xFFF3;
+	if      (rcd == 2) { int2 = int1 ^ 0x8; }
+	else if (rcd == 3) { int2 = int1 ^ 0x4; }
+	else if (rcd == 4) { int2 = int1; }
+	// else		   { int2 = int2; }
+
+
+	// RAS Precharge (tRP)
+	int1 = int2 & 0xFFFC;
+	if      (rp == 2) { int2 = int1 ^ 0x2; }
+	else if (rp == 3) { int2 = int1 ^ 0x1; }
+	else if (rp == 4) { int2 = int1; }
+	// else		  { int2 = int2; }
+
+
+	// RAS Active to precharge (tRAS)
+	int1 = int2 & 0xF9FF;
+	if      (ras == 5)  { int2 = int1 ^ 0x0600; }
+	else if (ras == 6)  { int2 = int1 ^ 0x0400; }
+	else if (ras == 7)  { int2 = int1 ^ 0x0200; }
+	else if (ras == 8)  { int2 = int1; }
+	// else		    { int2 = int2; }
+
+	pci_conf_write(0, 0, 1, 0x60, 4, int2);
+	__delay(500);
+}
+
+void change_timing_i925(int cas, int rcd, int rp, int ras)
+{
+	ulong int1, dev0, temp;
+	long *ptr;
+
+	//read MMRBAR
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+
+	ptr=(long*)(dev0+0x114);
+	temp = *ptr;
+
+	// RAS-To-CAS (tRCD)
+	int1 = temp | 0x70;
+	if      (rcd == 2) { temp = int1 ^ 0x70; }
+	else if (rcd == 3) { temp = int1 ^ 0x60; }
+	else if (rcd == 4) { temp = int1 ^ 0x50; }
+	else if (rcd == 5) { temp = int1 ^ 0x40; }
+	// else		   { temp = temp;}
+
+	//RAS precharge (tRP)
+	int1 = temp | 0x7;
+	if      (rp == 2) { temp = int1 ^ 0x7; }
+	else if (rp == 3) { temp = int1 ^ 0x6; }
+	else if (rp == 4) { temp = int1 ^ 0x5; }
+	else if (rp == 5) { temp = int1 ^ 0x4; }
+	// else		  { temp = temp;}
+
+	if (mem_ctr[ctrl].device == 0x2770 )	// Lakeport?
+	{
+		// RAS Active to precharge (tRAS)
+		int1 = temp | 0xF80000;	// bits 23:19
+		if      (ras == 4)  { temp = int1 ^ 0xD80000; }
+		else if (ras == 5)  { temp = int1 ^ 0xD00000; }
+		else if (ras == 6)  { temp = int1 ^ 0xC80000; }
+		else if (ras == 7)  { temp = int1 ^ 0xC00000; }
+		else if (ras == 8)  { temp = int1 ^ 0xB80000; }
+		else if (ras == 9)  { temp = int1 ^ 0xB00000; }
+		else if (ras == 10) { temp = int1 ^ 0xA80000; }
+		else if (ras == 11) { temp = int1 ^ 0xA00000; }
+		else if (ras == 12) { temp = int1 ^ 0x980000; }
+		else if (ras == 13) { temp = int1 ^ 0x900000; }
+		else if (ras == 14) { temp = int1 ^ 0x880000; }
+		else if (ras == 15) { temp = int1 ^ 0x800000; }
+		// else		    { temp = temp;}
+	}
+	else
+	{
+		// RAS Active to precharge (tRAS)
+		int1 = temp | 0xF00000;	// bits 23:20
+		if      (ras == 4)  { temp = int1 ^ 0xB00000; }
+		else if (ras == 5)  { temp = int1 ^ 0xA00000; }
+		else if (ras == 6)  { temp = int1 ^ 0x900000; }
+		else if (ras == 7)  { temp = int1 ^ 0x800000; }
+		else if (ras == 8)  { temp = int1 ^ 0x700000; }
+		else if (ras == 9)  { temp = int1 ^ 0x600000; }
+		else if (ras == 10) { temp = int1 ^ 0x500000; }
+		else if (ras == 11) { temp = int1 ^ 0x400000; }
+		else if (ras == 12) { temp = int1 ^ 0x300000; }
+		else if (ras == 13) { temp = int1 ^ 0x200000; }
+		else if (ras == 14) { temp = int1 ^ 0x100000; }
+		else if (ras == 15) { temp = int1 ^ 0x000000; }
+		// else		    { temp = temp;}
+	}
+
+	// CAS Latency (tCAS)
+	int1 = temp | 0x0300;
+	if      (cas == 1) { temp = int1 ^ 0x200; }   // cas 2.5
+	else if (cas == 2) { temp = int1 ^ 0x100; }
+	else if (cas == 3) { temp = int1 ^ 0x300; }
+	// else		   { temp = temp;}
+
+	*ptr = temp;
+	__delay(500);
+	return;
+}
+
+void change_timing_Lakeport(int cas, int rcd, int rp, int ras)
+{
+	ulong int1, dev0, temp;
+	long *ptr;
+
+	//read MMRBAR
+	pci_conf_read( 0, 0, 0, 0x44, 4, &dev0);
+	dev0 &= 0xFFFFC000;
+
+	ptr=(long*)(dev0+0x114);
+	temp = *ptr;
+
+	// RAS-To-CAS (tRCD)
+	int1 = temp | 0x70;
+	if      (rcd == 2) { temp = int1 ^ 0x70; }
+	else if (rcd == 3) { temp = int1 ^ 0x60; }
+	else if (rcd == 4) { temp = int1 ^ 0x50; }
+	else if (rcd == 5) { temp = int1 ^ 0x40; }
+	// else		   { temp = temp;}
+
+	//RAS precharge (tRP)
+	int1 = temp | 0x7;
+	if      (rp == 2) { temp = int1 ^ 0x7; }
+	else if (rp == 3) { temp = int1 ^ 0x6; }
+	else if (rp == 4) { temp = int1 ^ 0x5; }
+	else if (rp == 5) { temp = int1 ^ 0x4; }
+	// else		  { temp = temp;}
+
+
+	// CAS Latency (tCAS)
+	int1 = temp | 0x0300;
+	if      (cas == 1) { temp = int1 ^ 0x200; }   // cas 2.5
+	else if (cas == 2) { temp = int1 ^ 0x100; }
+	else if (cas == 3) { temp = int1 ^ 0x300; }
+	// else		   { temp = temp;}
+
+	*ptr = temp;
+	__delay(500);
+	return;
+}
+
+void change_timing_i875(int cas, int rcd, int rp, int ras){
+
+	ulong int1, dev6, temp;
+	long *ptr;
+
+	/* Read the MMR Base Address & Define the pointer from the BAR6 overflow register */
+	pci_conf_read( 0, 6, 0, 0x10, 4, &dev6);
+
+	ptr=(long*)(dev6+0x60);
+
+	temp = *ptr;
+
+	// RAS-To-CAS (tRCD)
+	int1 = temp | 0xC;
+	if      (rcd == 2) { temp = int1 ^ 0x4; }
+	else if (rcd == 3) { temp = int1 ^ 0x8; }
+	else if (rcd == 4) { temp = int1 ^ 0xC; }
+	else if (rcd == 5) { temp = int1 ^ 0xC; }
+	// else		   { temp = temp;}
+
+
+	//RAS precharge (tRP)
+	int1 = temp | 0x3;
+	if      (rp == 2) { temp = int1 ^ 0x1; }
+	else if (rp == 3) { temp = int1 ^ 0x2; }
+	else if (rp == 4) { temp = int1 ^ 0x3; }
+	else if (rp == 5) { temp = int1 ^ 0x3; }
+	// else		  { temp = temp;}
+
+
+	// RAS Active to precharge (tRAS)
+	int1 = temp | 0x380;
+	if      (ras == 5)  { temp = int1 ^ 0x100; }
+	else if (ras == 6)  { temp = int1 ^ 0x180; }
+	else if (ras == 7)  { temp = int1 ^ 0x200; }
+	else if (ras == 8)  { temp = int1 ^ 0x280; }
+	else if (ras == 9)  { temp = int1 ^ 0x300; }
+	else if (ras == 10) { temp = int1 ^ 0x380; }
+	// else		    { temp = temp;}
+
+	// CAS Latency (tCAS)
+	int1 = temp | 0x60;
+	if      (cas == 1) { temp = int1 ^ 0x60; }   // cas 2.5
+	else if (cas == 2) { temp = int1 ^ 0x40; }
+	else if (cas == 3) { temp = int1 ^ 0x20; }
+	// else		   { temp = temp; }
+
+	*ptr = temp;
+	__delay(500);
+	return;
+}
+
+
+void change_timing_nf2(int cas, int rcd, int rp, int ras) {
+
+	ulong dramtlr, dramtlr2;
+	ulong int1, int2;
+
+	pci_conf_read(0, 0, 1, 0x90, 4, &dramtlr);
+	pci_conf_read(0, 0, 1, 0xA0, 4, &dramtlr2);
+
+
+	// CAS Latency (tCAS)
+	int1 = dramtlr2 | 0x0070;
+	if      (cas == 1) { int2 = int1 ^ 0x10; }  // cas = 2.5
+	else if (cas == 2) { int2 = int1 ^ 0x50; }
+	else if (cas == 3) { int2 = int1 ^ 0x40; }
+	else		   { int2 = dramtlr2; }
+
+	pci_conf_write(0, 0, 1, 0xA0, 4, int2);
+
+	// RAS-To-CAS (tRCD)
+
+	int1 = dramtlr | 0x700000;
+	if      (rcd == 2) { int2 = int1 ^ 0x500000; }
+	else if (rcd == 3) { int2 = int1 ^ 0x400000; }
+	else if (rcd == 4) { int2 = int1 ^ 0x300000; }
+	else if (rcd == 5) { int2 = int1 ^ 0x200000; }
+	else if (rcd == 6) { int2 = int1 ^ 0x100000; }
+	else		   { int2 = dramtlr;}
+
+
+	// RAS Precharge (tRP)
+	int1 = int2 | 0x70000000;
+	if      (rp == 2) { int2 = int1 ^ 0x50000000; }
+	else if (rp == 3) { int2 = int1 ^ 0x40000000; }
+	else if (rp == 4) { int2 = int1 ^ 0x30000000; }
+	else if (rp == 5) { int2 = int1 ^ 0x20000000; }
+	else if (rp == 6) { int2 = int1 ^ 0x10000000; }
+	// else		  { int2 = int2;}
+
+
+	// RAS Active to precharge (tRAS)
+
+	int1 = int2 | 0x78000;
+	if      (ras == 4)  { int2 = int1 ^ 0x58000; }
+	else if (ras == 5)  { int2 = int1 ^ 0x50000; }
+	else if (ras == 6)  { int2 = int1 ^ 0x48000; }
+	else if (ras == 7)  { int2 = int1 ^ 0x40000; }
+	else if (ras == 8)  { int2 = int1 ^ 0x38000; }
+	else if (ras == 9)  { int2 = int1 ^ 0x30000; }
+	else if (ras == 10) { int2 = int1 ^ 0x28000; }
+	else if (ras == 11) { int2 = int1 ^ 0x20000; }
+	else if (ras == 12) { int2 = int1 ^ 0x18000; }
+	else if (ras == 13) { int2 = int1 ^ 0x10000; }
+	else if (ras == 14) { int2 = int1 ^ 0x08000; }
+	// else		    { int2 = int2;}
+
+
+	pci_conf_write(0, 0, 1, 0x90, 4, int2);
+	__delay(500);
+}
+
+
+void change_timing_amd64(int cas, int rcd, int rp, int ras) {
+
+	ulong dramtlr;
+	ulong int1= 0x0;
+
+	pci_conf_read(0, 24, 2, 0x88, 4, &dramtlr);
+
+	// RAS-To-CAS (tRCD)
+	int1 = dramtlr | 0x7000;
+	if      (rcd == 2) { dramtlr = int1 ^ 0x5000; }
+	else if (rcd == 3) { dramtlr = int1 ^ 0x4000; }
+	else if (rcd == 4) { dramtlr = int1 ^ 0x3000; }
+	else if (rcd == 5) { dramtlr = int1 ^ 0x2000; }
+	else if (rcd == 6) { dramtlr = int1 ^ 0x1000; }
+	else if (rcd == 1) { dramtlr = int1 ^ 0x6000; }
+	// else		   { dramtlr = dramtlr;}
+
+
+	//RAS precharge (tRP)
+	int1 = dramtlr | 0x7000000;
+	if      (rp == 2) { dramtlr = int1 ^ 0x5000000; }
+	else if (rp == 3) { dramtlr = int1 ^ 0x4000000; }
+	else if (rp == 1) { dramtlr = int1 ^ 0x6000000; }
+	else if (rp == 4) { dramtlr = int1 ^ 0x3000000; }
+	else if (rp == 5) { dramtlr = int1 ^ 0x2000000; }
+	else if (rp == 6) { dramtlr = int1 ^ 0x1000000; }
+	// else		  { dramtlr = dramtlr;}
+
+
+	// RAS Active to precharge (tRAS)
+	int1 = dramtlr | 0xF00000;
+	if      (ras == 5)  { dramtlr = int1 ^ 0xA00000; }
+	else if (ras == 6)  { dramtlr = int1 ^ 0x900000; }
+	else if (ras == 7)  { dramtlr = int1 ^ 0x800000; }
+	else if (ras == 8)  { dramtlr = int1 ^ 0x700000; }
+	else if (ras == 9)  { dramtlr = int1 ^ 0x600000; }
+	else if (ras == 10) { dramtlr = int1 ^ 0x500000; }
+	else if (ras == 11) { dramtlr = int1 ^ 0x400000; }
+	else if (ras == 12) { dramtlr = int1 ^ 0x300000; }
+	else if (ras == 13) { dramtlr = int1 ^ 0x200000; }
+	else if (ras == 14) { dramtlr = int1 ^ 0x100000; }
+	// else		    { dramtlr = dramtlr;}
+
+
+	// CAS Latency (tCAS)
+	int1 = dramtlr | 0x7;	// some changes will cause the system hang, tried Draminit to no avail
+	if      (cas == 1) { dramtlr = int1 ^ 0x2; }   // cas 2.5
+	else if (cas == 2) { dramtlr = int1 ^ 0x6; }
+	else if (cas == 3) { dramtlr = int1 ^ 0x5; }
+	else if (cas == 4) { dramtlr = int1 ^ 0x7; } //cas 1.5 on a64
+	// else		   { dramtlr = dramtlr; }
+
+//	pci_conf_read(0, 24, 2, 0x90, 4, &dramcr);// use dram init
+	pci_conf_write(0, 24, 2, 0x88, 4, dramtlr);
+	__delay(500);
+
+////////////////////////////////////////////////////////////////
+// trying using the draminit, but do not work
+}
+
+// copy from lib.c code to add delay to chipset timing modification
+void __delay(ulong loops)
+{
+	int d0;
+	__asm__ __volatile__(
+		"\tjmp 1f\n"
+		".align 16\n"
+		"1:\tjmp 2f\n"
+		".align 16\n"
+		"2:\tdecl %0\n\tjns 2b"
+		:"=&a" (d0)
+		:"0" (loops));
+}
+
+void amd64_tweak(int rwt, int wrt, int ref, int en2t, int rct, int rrd, int rwqb, int wr)
+{
+	ulong dramtlr;
+	ulong int1= 0x0;
+
+	pci_conf_read(0, 24, 2, 0x88, 4, &dramtlr);
+
+	// Row Cycle time
+	int1 = dramtlr | 0xF0;
+	if      (rct == 7 ) { dramtlr = int1 ^ 0xF0; }
+	else if (rct == 8 ) { dramtlr = int1 ^ 0xE0; }
+	else if (rct == 9 ) { dramtlr = int1 ^ 0xD0; }
+	else if (rct == 10) { dramtlr = int1 ^ 0xC0; }
+	else if (rct == 11) { dramtlr = int1 ^ 0xB0; }
+	else if (rct == 12) { dramtlr = int1 ^ 0xA0; }
+	else if (rct == 13) { dramtlr = int1 ^ 0x90; }
+	else if (rct == 14) { dramtlr = int1 ^ 0x80; }
+	else if (rct == 15) { dramtlr = int1 ^ 0x70; }
+	else if (rct == 16) { dramtlr = int1 ^ 0x60; }
+	else if (rct == 17) { dramtlr = int1 ^ 0x50; }
+	else if (rct == 18) { dramtlr = int1 ^ 0x40; }
+	else if (rct == 19) { dramtlr = int1 ^ 0x30; }
+	else if (rct == 20) { dramtlr = int1 ^ 0x20; }
+	// else		    { dramtlr = dramtlr;}
+
+	//Active-avtive ras-ras delay
+	int1 = dramtlr | 0x70000;
+	if      (rrd == 2) { dramtlr = int1 ^ 0x50000; } // 2 bus clocks
+	else if (rrd == 3) { dramtlr = int1 ^ 0x40000; } // 3 bus clocks
+	else if (rrd == 4) { dramtlr = int1 ^ 0x30000; } // 4 bus clocks
+	// else		   { dramtlr = dramtlr;}
+
+	//Write recovery time
+	int1 = dramtlr | 0x10000000;
+	if      (wr == 2) { dramtlr = int1 ^ 0x10000000; } // 2 bus clocks
+	else if (wr == 3) { dramtlr = int1 ^ 0x00000000; } // 3 bus clocks
+	// else		  { dramtlr = dramtlr;}
+
+	pci_conf_write(0, 24, 2, 0x88, 4, dramtlr);
+	__delay(500);
+	//////////////////////////////////////////////
+
+	pci_conf_read(0, 24, 2, 0x8C, 4, &dramtlr);
+
+	// Write-to read delay
+	int1 = dramtlr | 0x1;
+	if      (wrt == 2) { dramtlr = int1 ^ 0x0; }
+	else if (wrt == 1) { dramtlr = int1 ^ 0x1; }
+	// else		   { dramtlr = dramtlr;}
+
+	// Read-to Write delay
+	int1 = dramtlr | 0x70;
+	if      (rwt == 1) { dramtlr = int1 ^ 0x70; }
+	else if (rwt == 2) { dramtlr = int1 ^ 0x60; }
+	else if (rwt == 3) { dramtlr = int1 ^ 0x50; }
+	else if (rwt == 4) { dramtlr = int1 ^ 0x40; }
+	else if (rwt == 5) { dramtlr = int1 ^ 0x30; }
+	else if (rwt == 6) { dramtlr = int1 ^ 0x20; }
+	// else		   { dramtlr = dramtlr;}
+
+	//Refresh Rate
+	int1 = dramtlr | 0x1800;
+	if      (ref == 1) { dramtlr = int1 ^ 0x1800; } // 15.6us
+	else if (ref == 2) { dramtlr = int1 ^ 0x1000; } // 7.8us
+	else if (ref == 3) { dramtlr = int1 ^ 0x0800; } // 3.9us
+	// else		   { dramtlr = dramtlr;}
+
+	pci_conf_write(0, 24, 2, 0x8c, 4, dramtlr);
+	__delay(500);
+	/////////////////////////////////////
+
+	pci_conf_read(0, 24, 2, 0x90, 4, &dramtlr);
+
+	// Enable 2t command
+	int1 = dramtlr | 0x10000000;
+	if      (en2t == 2) { dramtlr = int1 ^ 0x00000000; } // 2T
+	else if (en2t == 1) { dramtlr = int1 ^ 0x10000000; } // 1T
+	// else		    { dramtlr = dramtlr;}
+
+	// Read Write queue bypass count
+	int1 = dramtlr | 0xC000;
+	if      (rwqb == 2)  { dramtlr = int1 ^ 0xC000; }
+	else if (rwqb == 4)  { dramtlr = int1 ^ 0x8000; }
+	else if (rwqb == 8)  { dramtlr = int1 ^ 0x4000; }
+	else if (rwqb == 16) { dramtlr = int1 ^ 0x0000; }
+	// else		     { dramtlr = dramtlr;}
+
+	pci_conf_write(0, 24, 2, 0x90, 4, dramtlr);
+	__delay(500);
+	restart();
+}
+
diff --git a/extra.h b/extra.h
new file mode 100644
index 0000000..9bd7045
--- /dev/null
+++ b/extra.h
@@ -0,0 +1,27 @@
+// This is the extra stuff added to the memtest+ from memtest.org
+// Code from Eric Nelson and Wee
+/* extra.c
+ *
+ * Released under version 2 of the Gnu Public License.
+ *
+ */
+
+#ifndef MEMTEST_EXTRA_H
+#define MEMTEST_EXTRA_H
+
+void change_timing(int cas, int rcd, int rp, int ras);
+void find_memctr(void);
+void disclaimer(void); 
+void get_option(void);
+void get_menu(void);
+void a64_parameter(void);
+int get_cas(void);
+void change_timing_i852(int cas, int rcd, int rp, int ras);
+void change_timing_i925(int cas, int rcd, int rp, int ras);
+void change_timing_i875(int cas, int rcd, int rp, int ras);
+void change_timing_nf2(int cas, int rcd, int rp, int ras);
+void change_timing_amd64(int cas, int rcd, int rp, int ras);
+void amd64_tweak(int rwt, int wrt, int ref, int en2t, int rct, int rrd, int rwqb, int wr);
+void __delay(ulong loops);
+
+#endif /* MEMTEST_EXTRA_H */
diff --git a/head.S b/head.S
new file mode 100644
index 0000000..d551336
--- /dev/null
+++ b/head.S
@@ -0,0 +1,847 @@
+/*
+ *  linux/boot/head.S
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ *  head.S contains the 32-bit startup code.
+ *
+ *  1-Jan-96 Modified by Chris Brady for use as a boot/loader for MemTest-86.
+ *  Setup the memory management for flat non-paged linear addressing.
+ *  17 May 2004 : Added X86_PWRCAP for AMD64 (Memtest86+ - Samuel D.)
+ */
+
+.text
+#define __ASSEMBLY__
+#include "defs.h"
+#include "config.h"
+#include "test.h"
+
+	.code32
+	.globl startup_32
+startup_32:
+	cld
+	cli
+
+	/* Ensure I have a boot_stack pointer */
+	testl	%esp, %esp
+	jnz 0f
+	movl	$(LOW_TEST_ADR + _GLOBAL_OFFSET_TABLE_), %esp
+	leal	boot_stack_top@GOTOFF(%esp), %esp
+0:
+
+	/* Load the GOT pointer */
+	call	0f
+0:	popl	%ebx
+	addl	$_GLOBAL_OFFSET_TABLE_+[.-0b], %ebx
+
+	/* Pick the appropriate boot_stack address */
+	leal	boot_stack_top@GOTOFF(%ebx), %esp
+
+	/* Reload all of the segment registers */
+	leal	gdt@GOTOFF(%ebx), %eax
+	movl	%eax, 2 + gdt_descr@GOTOFF(%ebx)
+	lgdt	gdt_descr@GOTOFF(%ebx)
+	leal	flush@GOTOFF(%ebx), %eax
+	pushl	$KERNEL_CS
+	pushl	%eax
+	lret
+flush:	movl	$KERNEL_DS, %eax
+	movw	%ax, %ds
+	movw	%ax, %es
+	movw	%ax, %fs
+	movw	%ax, %gs
+	movw	%ax, %ss
+
+/*
+ *  Zero BSS
+ */
+	cmpl	$1, zerobss@GOTOFF(%ebx)
+	jnz	zerobss_done
+	xorl	%eax, %eax
+	leal	_bss@GOTOFF(%ebx), %edi
+	leal	_end@GOTOFF(%ebx), %ecx
+	subl	%edi, %ecx
+1:	movl	%eax, (%edi)
+	addl	$4, %edi
+	subl	$4, %ecx
+	jnz	1b
+	movl	$0, zerobss@GOTOFF(%ebx)
+zerobss_done:
+
+/*
+ * Setup an exception handler
+ */
+	leal	idt@GOTOFF(%ebx), %edi
+
+	leal	vec0@GOTOFF(%ebx), %edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx, %ax	/* selector = 0x0010 = cs */
+	movw	$0x8E00, %dx	/* interrupt gate - dpl=0, present */
+	movl	%eax, (%edi)
+	movl	%edx, 4(%edi)
+	addl	$8, %edi
+
+	leal	vec1@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec2@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec3@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec4@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec5@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec6@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec7@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec8@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec9@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec10@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec11@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec12@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec13@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec14@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec15@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec16@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec17@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec18@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	leal	vec19@GOTOFF(%ebx),%edx
+	movl	$(KERNEL_CS << 16),%eax
+	movw	%dx,%ax		   /* selector = 0x0010 = cs */
+	movw	$0x8E00,%dx	   /* interrupt gate - dpl=0, present */
+	movl	%eax,(%edi)
+	movl	%edx,4(%edi)
+	addl	$8,%edi
+
+	/* Now that it is initialized load the interrupt descriptor table */
+	leal	idt@GOTOFF(%ebx), %eax
+	movl	%eax, 2 + idt_descr@GOTOFF(%ebx)
+	lidt	idt_descr@GOTOFF(%ebx)
+
+	leal	_dl_start@GOTOFF(%ebx), %eax
+	call	*%eax
+
+	/* Never forget to initialize the FPU ... Never ! */ 
+	finit
+
+	call	test_start
+
+	/* In case we return simulate an exception */
+	pushfl
+	pushl	%cs
+	call	0f
+0:	pushl	$0 /* error code */
+	pushl	$257 /* vector */
+	jmp	int_hand
+
+vec0:
+	pushl	$0 /* error code */
+	pushl	$0 /* vector */
+	jmp int_hand
+vec1:
+	pushl	$0 /* error code */
+	pushl	$1 /* vector */
+	jmp int_hand
+
+vec2:
+	pushl	$0 /* error code */
+	pushl	$2 /* vector */
+	jmp int_hand
+
+vec3:
+	pushl	$0 /* error code */
+	pushl	$3 /* vector */
+	jmp	int_hand
+
+vec4:
+	pushl	$0 /* error code */
+	pushl	$4 /* vector */
+	jmp	int_hand
+
+vec5:
+	pushl	$0 /* error code */
+	pushl	$5 /* vector */
+	jmp	int_hand
+
+vec6:
+	pushl	$0 /* error code */
+	pushl	$6 /* vector */
+	jmp	int_hand
+
+vec7:
+	pushl	$0 /* error code */
+	pushl	$7 /* vector */
+	jmp	int_hand
+
+vec8:
+	/* error code */
+	pushl	$8 /* vector */
+	jmp	int_hand
+
+vec9:
+	pushl	$0 /* error code */
+	pushl	$9 /* vector */
+	jmp int_hand
+
+vec10:
+	/* error code */
+	pushl	$10 /* vector */
+	jmp	int_hand
+
+vec11:
+	/* error code */
+	pushl	$11 /* vector */
+	jmp	int_hand
+
+vec12:
+	/* error code */
+	pushl	$12 /* vector */
+	jmp	int_hand
+
+vec13:
+	/* error code */
+	pushl	$13 /* vector */
+	jmp	int_hand
+
+vec14:
+	/* error code */
+	pushl	$14 /* vector */
+	jmp	int_hand
+
+vec15:
+	pushl	$0 /* error code */
+	pushl	$15 /* vector */
+	jmp	int_hand
+
+vec16:
+	pushl	$0 /* error code */
+	pushl	$16 /* vector */
+	jmp	int_hand
+
+vec17:
+	/* error code */
+	pushl	$17 /* vector */
+	jmp	int_hand
+
+vec18:
+	pushl	$0 /* error code */
+	pushl	$18 /* vector */
+	jmp	int_hand
+
+vec19:
+	pushl	$0 /* error code */
+	pushl	$19 /* vector */
+	jmp	int_hand
+
+int_hand:
+	pushl	%eax
+	pushl	%ebx
+	pushl	%ecx
+	pushl	%edx
+	pushl	%edi
+	pushl	%esi
+	pushl	%ebp
+
+	/* original boot_stack pointer */
+	leal	48(%esp), %eax
+	pushl	%eax
+	pushl	%ds  
+	pushl	%ss 
+	pushl	%esp /* pointer to trap regs struct on the boot_stack */
+	call	inter
+	addl	$8, %esp
+
+	popl	%ebp
+	popl	%esi
+	popl	%edi
+	popl	%edx
+	popl	%ecx
+	popl	%ebx
+	popl	%eax
+	iret
+
+/*
+ * The interrupt descriptor table has room for 32 idt's
+ */
+.align 4
+.word 0
+idt_descr:
+	.word 20*8-1	       # idt contains 32 entries
+	.long 0
+
+idt:
+	.fill 20,8,0	       # idt is uninitialized
+
+gdt_descr:
+	.word gdt_end - gdt - 1
+	.long 0
+
+.align 4
+.globl gdt, gdt_end
+gdt:
+	.quad 0x0000000000000000	/* NULL descriptor */
+	.quad 0x0000000000000000	/* not used */
+	.quad 0x00cf9b000000ffff	/* 0x10 main 4gb code at 0x000000 */
+	.quad 0x00cf93000000ffff	/* 0x18 main 4gb data at 0x000000 */
+
+	.word	0xFFFF				# 16bit 64KB - (0x10000*1 = 64KB)
+	.word	0				# base address = SETUPSEG
+	.byte	0x00, 0x9b			# code read/exec/accessed
+	.byte	0x00, 0x00			# granularity = bytes
+
+
+	.word	0xFFFF				# 16bit 64KB - (0x10000*1 = 64KB)
+	.word	0				# base address = SETUPSEG
+	.byte	0x00, 0x93			# data read/write/accessed
+	.byte	0x00, 0x00			# granularity = bytes
+
+gdt_end:
+
+.data
+
+.macro ptes64 start, count=64
+.quad \start + 0x0000000 + 0xE3
+.quad \start + 0x0200000 + 0xE3
+.quad \start + 0x0400000 + 0xE3
+.quad \start + 0x0600000 + 0xE3
+.quad \start + 0x0800000 + 0xE3
+.quad \start + 0x0A00000 + 0xE3
+.quad \start + 0x0C00000 + 0xE3
+.quad \start + 0x0E00000 + 0xE3
+.if \count-1
+ptes64 "(\start+0x01000000)",\count-1
+.endif
+.endm
+
+.macro maxdepth depth=1
+.if \depth-1
+maxdepth \depth-1
+.endif
+.endm
+
+maxdepth
+
+# Page Directory Tables:
+# There are 4 tables, the first two map the first 2 GB of memory. The last two are used with # PAE to map
+# the rest of memory in 2 GB segments. The last two tables are changed in vmem.c to map each segment.
+# We use 2 MB pages so only the Page Directory Table is used (no page tables). 
+.balign 4096
+.globl pd0
+pd0:
+	ptes64 0x0000000000000000
+
+.balign 4096
+.globl pd1
+pd1:
+	ptes64 0x0000000040000000
+
+.balign 4096
+.globl pd2
+pd2:
+	ptes64 0x0000000080000000
+
+.balign 4096
+.globl pd3
+pd3:
+	ptes64 0x00000000C0000000
+
+# Legacy Mode Page Directory Pointer Table:
+# 4 Entries, pointing to the Page Directory Tables
+.balign 4096
+.globl pdp
+pdp:
+	.long pd0 + 1
+	.long 0
+	.long pd1 + 1
+	.long 0
+	.long pd2 + 1
+	.long 0
+	.long pd3 + 1
+	.long 0
+
+# Long Mode Page Directory Pointer Table:
+# 4 Entries, pointing to the Page Directory Tables
+.balign 4096
+lpdp:
+	.long pd0 + 3
+	.long 0
+	.long pd1 + 3
+	.long 0
+	.long pd2 + 3
+	.long 0
+	.long pd3 + 3
+	.long 0
+
+
+# The long mode level 4 page map table
+.balign 4096
+.globl pml4
+pml4:
+	.long lpdp + 3
+	.long 0
+.previous
+
+#define RSTART startup_32
+
+	.globl query_pcbios
+query_pcbios:
+	/* Save the caller save registers */
+	pushl	%ebx
+	pushl	%esi
+	pushl	%edi
+	pushl	%ebp
+	call	1f
+1:	popl	%ebx
+	addl	$_GLOBAL_OFFSET_TABLE_+[.-1b], %ebx
+
+	/* Compute the reloc address */
+	leal	RSTART@GOTOFF(%ebx), %esi
+
+	/* Fixup real code pointer */
+	movl	%esi, %eax
+	shrl	$4, %eax
+	movw	%ax, 2 + realptr@GOTOFF(%ebx)
+
+	/* Fixup protected code pointer */
+	leal	prot@GOTOFF(%ebx), %eax
+	movl	%eax, protptr@GOTOFF(%ebx)
+
+	/* Compute the gdt fixup */
+	movl	%esi, %eax
+	shll	$16, %eax	# Base low
+
+	movl	%esi, %ecx
+	shrl	$16, %ecx
+	andl	$0xff, %ecx
+
+	movl	%esi, %edx
+	andl	$0xff000000, %edx
+	orl	%edx, %ecx
+
+	/* Fixup the gdt */
+	andl	$0x0000ffff, REAL_CS + 0 + gdt@GOTOFF(%ebx)
+	orl	%eax,        REAL_CS + 0 + gdt@GOTOFF(%ebx)
+	andl	$0x00ffff00, REAL_CS + 4 + gdt@GOTOFF(%ebx)
+	orl	%ecx,        REAL_CS + 4 + gdt@GOTOFF(%ebx)
+	andl	$0x0000ffff, REAL_DS + 0 + gdt@GOTOFF(%ebx)
+	orl	%eax,        REAL_DS + 0 + gdt@GOTOFF(%ebx)
+	andl	$0x00ffff00, REAL_DS + 4 + gdt@GOTOFF(%ebx)
+	orl	%ecx,        REAL_DS + 4 + gdt@GOTOFF(%ebx)
+
+	/* Fixup the gdt_descr */
+	leal	gdt@GOTOFF(%ebx), %eax
+	movl	%eax, 2 + gdt_descr@GOTOFF(%ebx)
+
+	lidt	idt_real@GOTOFF(%ebx)
+
+	/* Don't disable the a20 line */
+
+	/* Load 16bit data segments, to ensure the segment limits are set */
+	movl	$REAL_DS, %eax
+	movl	%eax, %ds
+	movl	%eax, %es
+	movl	%eax, %ss
+	movl	%eax, %fs
+	movl	%eax, %gs
+
+	/* Compute the boot_stack base */
+	leal	boot_stack@GOTOFF(%ebx), %ecx
+	/* Compute the address of meminfo */
+	leal	mem_info@GOTOFF(%ebx), %edi
+
+	/* switch to 16bit mode */
+	ljmp	$REAL_CS, $1f - RSTART
+1:
+	.code16
+	/* Disable Paging and protected mode */
+	/* clear the PG & PE bits of CR0 */
+	movl	%cr0,%eax
+	andl	$~((1 << 31)|(1<<0)),%eax
+	movl	%eax,%cr0
+
+	/* make intersegment jmp to flush the processor pipeline
+	 * and reload %cs:%eip (to clear upper 16 bits of %eip).
+	 */
+	ljmp	*(realptr - RSTART)
+real:
+	/* we are in real mode now
+	 * set up the real mode segment registers : %ds, %ss, %es, %gs, %fs
+	 */
+	movw	%cs, %ax
+	movw	%ax, %ds
+	movw	%ax, %es
+	movw	%ax, %fs
+	movw	%ax, %gs
+	movw	%ax, %ss
+
+	/* Adjust the boot_stack pointer */
+	movl	%ecx, %eax
+	shrl	$4, %eax
+	movw	%ax, %ss
+	subl	%ecx, %esp
+
+	/* Save my base pointer */
+	pushl	%ebx
+
+	/* Setup %ds to point to my data area */
+	shrl	$4, %edi
+	movl	%edi, %ds
+
+	/* Enable interrupts or BIOS's go crazy */
+	sti
+
+# Get memory size (extended mem, kB)
+
+#define SMAP	0x534d4150
+
+	xorl	%eax, %eax
+	movl	%eax, (E88)
+	movl	%eax, (E801)
+	movl	%eax, (E820NR)
+
+# Try three different memory detection schemes.  First, try
+# e820h, which lets us assemble a memory map, then try e801h,
+# which returns a 32-bit memory size, and finally 88h, which
+# returns 0-64m
+
+# method E820H:
+# the memory map from hell.  e820h returns memory classified into
+# a whole bunch of different types, and allows memory holes and
+# everything.  We scan through this memory map and build a list
+# of the first 32 memory areas, which we return at [E820MAP].
+# This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm
+
+meme820:
+	xorl	%ebx, %ebx			# continuation counter
+	movw	$E820MAP, %di			# point into the whitelist
+						# so we can have the bios
+						# directly write into it.
+
+jmpe820:
+	movl	$0x0000e820, %eax		# e820, upper word zeroed
+	movl	$SMAP, %edx			# ascii 'SMAP'
+	movl	$20, %ecx			# size of the e820rec
+	pushw	%ds				# data record.
+	popw	%es
+	int	$0x15				# make the call
+	jc	bail820				# fall to e801 if it fails
+
+	cmpl	$SMAP, %eax			# check the return is `SMAP'
+	jne	bail820				# fall to e801 if it fails
+
+#	cmpl	$1, 16(%di)			# is this usable memory?
+#	jne	again820
+
+	# If this is usable memory, we save it by simply advancing %di by
+	# sizeof(e820rec).
+	#
+good820:
+	movb	(E820NR), %al			# up to 32 entries
+	cmpb	$E820MAX, %al
+	jnl	bail820
+
+	incb	(E820NR)
+	movw	%di, %ax
+	addw	$E820ENTRY_SIZE, %ax
+	movw	%ax, %di
+again820:
+	cmpl	$0, %ebx			# check to see if
+	jne	jmpe820				# %ebx is set to EOF
+bail820:
+
+
+# method E801H:
+# memory size is in 1k chunksizes, to avoid confusing loadlin.
+# we store the 0xe801 memory size in a completely different place,
+# because it will most likely be longer than 16 bits.
+
+meme801:
+	stc					# fix to work around buggy
+	xorw	%cx,%cx				# BIOSes which dont clear/set
+	xorw	%dx,%dx				# carry on pass/error of
+						# e801h memory size call
+						# or merely pass cx,dx though
+						# without changing them.
+	movw	$0xe801, %ax
+	int	$0x15
+	jc	mem88
+
+	cmpw	$0x0, %cx			# Kludge to handle BIOSes
+	jne	e801usecxdx			# which report their extended
+	cmpw	$0x0, %dx			# memory in AX/BX rather than
+	jne	e801usecxdx			# CX/DX.  The spec I have read
+	movw	%ax, %cx			# seems to indicate AX/BX
+	movw	%bx, %dx			# are more reasonable anyway...
+
+e801usecxdx:
+	andl	$0xffff, %edx			# clear sign extend
+	shll	$6, %edx			# and go from 64k to 1k chunks
+	movl	%edx, (E801)			# store extended memory size
+	andl	$0xffff, %ecx			# clear sign extend
+ 	addl	%ecx, (E801)			# and add lower memory into
+						# total size.
+
+# Ye Olde Traditional Methode.  Returns the memory size (up to 16mb or
+# 64mb, depending on the bios) in ax.
+mem88:
+
+	movb	$0x88, %ah
+	int	$0x15
+	movw	%ax, (E88)
+
+#ifdef APM_OFF
+# check for APM BIOS
+	movw	$0x5300, %ax    # APM BIOS installation check
+	xorw	%bx, %bx
+	int	$0x15
+	jc	done_apm_bios   # error -> no APM BIOS
+
+	cmpw	$0x504d, %bx    # check for "PM" signature
+	jne	done_apm_bios   # no signature -> no APM BIOS
+
+	movw	$0x5304, %ax    # Disconnect first just in case
+	xorw	%bx, %bx
+	int	$0x15           # ignore return code
+
+	movw	$0x5301, %ax    # Real Mode connect
+	xorw	%bx, %bx
+	int	$0x15
+	jc	done_apm_bios   # error
+
+	movw	$0x5308, %ax    # Disable APM
+	mov	$0xffff, %bx
+	xorw	%cx, %cx
+	int	$0x15
+
+done_apm_bios:
+#endif
+
+	/* O.k. the BIOS query is done switch back to protected mode */
+	cli
+
+	/* Restore my saved variables */
+	popl	%ebx
+
+	/* Get an convinient %ds */
+	movw	%cs, %ax
+	movw	%ax, %ds
+
+	/* Load the global descriptor table */
+	addr32 lgdt	gdt_descr - RSTART
+
+	/* Turn on protected mode */
+	/* Set the PE bit in CR0 */
+	movl	%cr0,%eax
+	orl	$(1<<0),%eax
+	movl	%eax,%cr0
+
+	/* flush the prefetch queue, and relaod %cs:%eip */
+	data32 ljmp	*(protptr - RSTART)
+prot:
+	.code32
+	/* Reload other segment registers */
+	movl	$KERNEL_DS, %eax
+	movl	%eax, %ds
+	movl	%eax, %es
+	movl	%eax, %fs
+	movl	%eax, %gs
+	movl	%eax, %ss
+
+	/* Adjust the boot_stack pointer */
+	leal	boot_stack@GOTOFF(%ebx), %eax
+	addl	%eax, %esp
+
+	/* Restore the caller saved registers */
+	popl	%ebp
+	popl	%edi
+	popl	%esi
+	popl	%ebx
+	movl	$1, %eax
+	ret
+
+realptr:
+	.word	real - RSTART
+	.word	0x0000
+protptr:
+	.long	0
+	.long	KERNEL_CS
+
+idt_real:
+	.word	0x400 - 1			# idt limit ( 256 entries)
+	.word	0, 0				# idt base = 0L
+
+/* _ap_trampoline_start is the entry point for cpus other than the
+ * bootstrap cpu. The code between _ap_trampoline_start to
+ * _ap_trampoline_protmode is copied to BootCodeStart(0x9000).
+ * The ljmp after turning on CR0.PE will jump to the
+ * relocatable code which usually resides at 0x10000 + _ap_trampoline_protmode.
+ *
+ * The trampoline code uses a temporary GDT. The entries of this temporary
+ * GDT must match the first few entries of the GDT used by the relocatble
+ * memtest code(see 'gdt' sybmol in this file).
+ *
+ */
+	.globl _ap_trampoline_start
+	.globl _ap_trampoline_protmode
+	.code16
+_ap_trampoline_start:
+	lgdt    0x0 /* will be fixed up later, see smp.c:BootAP()*/
+	movl	%cr0, %eax
+	orl	$1, %eax
+	movl	%eax, %cr0
+	data32 ljmp    $KERNEL_CS, $_ap_trampoline_protmode
+_ap_trampoline_protmode:
+	.code32
+	movw	$KERNEL_DS, %ax
+	movw	%ax, %ds
+	movw	%ax, %es
+	movw	%ax, %fs
+	movw	%ax, %gs
+	movw	%ax, %ss
+	movl	$(LOW_TEST_ADR + _GLOBAL_OFFSET_TABLE_), %esp
+	leal	boot_stack_top@GOTOFF(%esp), %esp
+	pushl   $0
+	popf
+	call    startup_32
+	/* if we ever return, we'll just loop forever */
+	cli
+2:	hlt
+	jmp 2b	
+.data
+zerobss:	.long	1
+.previous
+.data
+.balign 16
+	.globl mem_info
+mem_info:
+	. = . + MEMINFO_SIZE
+.previous
+.bss
+.balign 16
+boot_stack:
+	.globl boot_stack
+	. = . + 4096
+boot_stack_top:
+	.globl boot_stack_top
+.previous
diff --git a/init.c b/init.c
new file mode 100644
index 0000000..754b8d7
--- /dev/null
+++ b/init.c
@@ -0,0 +1,1301 @@
+/*
+ * MemTest86+ V5 Specific code (GPL V2.0)
+ * By Samuel DEMEULEMEESTER, sdemeule@memtest.org
+ * http://www.canardpc.com - http://www.memtest.org
+ * ------------------------------------------------
+ * init.c - MemTest-86  Version 3.6
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ */
+ 
+
+#include "stdin.h"
+#include "stddef.h"
+#include "test.h"
+#include "defs.h"
+#include "config.h"
+#include "cpuid.h"
+#include "smp.h"
+#include "io.h"
+#include "spd.h"
+#include "pci.h"
+#include "controller.h"
+
+extern struct tseq tseq[];
+extern short memsz_mode;
+extern int num_cpus;
+extern int act_cpus;
+extern int found_cpus;
+unsigned long imc_type = 0;
+extern int maxcpus;
+extern char cpu_mask[];
+extern void initialise_cpus();
+
+/* Here we store all of the cpuid data */
+extern struct cpu_ident cpu_id;
+
+int l1_cache=0, l2_cache=0, l3_cache=0;
+int tsc_invariable = 0;
+ulong extclock;
+
+ulong memspeed(ulong src, ulong len, int iter);
+static void cpu_type(void);
+static int cpuspeed(void);
+static void get_cache_size();
+static void cpu_cache_speed();
+void get_cpuid();
+int beepmode;
+extern short dmi_initialized;
+extern int dmi_err_cnts[MAX_DMI_MEMDEVS];
+
+/* Failsafe function */
+/* msec: number of ms to wait - scs: scancode expected to stop */
+/* bits: 0 = extended detection - 1: SMP - 2: Temp Check */
+/* 3: MP SMP - 4-7: RSVD */
+void failsafe(int msec, int scs)
+{
+	int i;
+	ulong sh, sl, l, h, t;
+	unsigned char c;
+	volatile char *pp;
+	
+	for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<40; i++, pp+=2) {
+		*pp = 0x1E;
+	}	
+	for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<3; i++, pp+=2) {
+		*pp = 0x9E;
+	}	
+	for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(55*2)+1); i<3; i++, pp+=2) {
+		*pp = 0x9E;
+	}	
+	
+	cprint(18, 18, "==> Press F1 to enter Fail-Safe Mode <==");
+	
+	if(v->fail_safe & 2)
+	{
+	cprint(19, 15, "==> Press F2 to force Multi-Threading (SMP) <==");				
+	}
+
+	/* save the starting time */
+	asm __volatile__(
+		"rdtsc":"=a" (sl),"=d" (sh));
+
+	/* loop for n seconds */
+	while (1) {
+		asm __volatile__(
+			"rdtsc":"=a" (l),"=d" (h));
+		asm __volatile__ (
+			"subl %2,%0\n\t"
+			"sbbl %3,%1"
+			:"=a" (l), "=d" (h)
+			:"g" (sl), "g" (sh),
+			"0" (l), "1" (h));
+
+		t = h * ((unsigned)0xffffffff / v->clks_msec);
+		t += (l / v->clks_msec);
+
+		/* Is the time up? */
+		if (t >= msec) { break;	}
+		
+		/* Is expected Scan code pressed? */
+		c = get_key();
+		c &= 0x7f;
+		
+		/* F1 */
+		if(c == scs) { v->fail_safe |= 1;	break; }
+					
+		/* F2 */
+		if(c == scs+1) 
+		{ 
+			v->fail_safe ^= 2;
+			break;
+
+		}
+		
+		/* F3 */
+		if(c == scs+2) 
+		{ 
+			if(v->fail_safe & 2) { v->fail_safe ^= 2; }
+			v->fail_safe |= 8;
+			break;
+		}				
+			
+	}
+	
+	cprint(18, 18, "                                          ");
+	cprint(19, 15, "                                                ");
+	
+	for(i=0, pp=(char *)(SCREEN_ADR+(18*160)+(18*2)+1); i<40; i++, pp+=2) {
+		*pp = 0x17;
+	}		
+	
+}
+
+
+
+static void display_init(void)
+{
+	int i;
+	volatile char *pp;
+	
+	/* Set HW cursor out of screen boundaries */
+	__outb(0x0F, 0x03D4);
+	__outb(0xFF, 0x03D5);
+
+	__outb(0x0E, 0x03D4);
+	__outb(0xFF, 0x03D5);
+
+
+	serial_echo_init();
+  serial_echo_print("INE_SCROLL;24r"); /* Set scroll area row 7-23 */
+  serial_echo_print("");   /* Clear Screen */
+  serial_echo_print("");
+  serial_echo_print("");
+  serial_echo_print("");
+
+	/* Clear screen & set background to blue */
+	for(i=0, pp=(char *)(SCREEN_ADR); i<80*24; i++) {
+		*pp++ = ' ';
+		*pp++ = 0x17;
+	}
+
+	/* Make the name background green */
+	for(i=0, pp=(char *)(SCREEN_ADR+1); i<TITLE_WIDTH; i++, pp+=2) {
+		*pp = 0x20;
+	}
+	cprint(0, 0, "      Memtest86  5.01        ");
+
+	/* Set Blinking "+" */
+	for(i=0, pp=(char *)(SCREEN_ADR+1); i<2; i++, pp+=30) {
+		*pp = 0xA4;
+	}
+	cprint(0, 15, "+");
+
+	/* Do reverse video for the bottom display line */
+	for(i=0, pp=(char *)(SCREEN_ADR+1+(24 * 160)); i<80; i++, pp+=2) {
+		*pp = 0x71;
+	}
+
+   serial_echo_print("");
+}
+
+/*
+ * Initialize test, setup screen and find out how much memory there is.
+ */
+void init(void)
+{
+	int i;
+	
+	outb(0x8, 0x3f2);  /* Kill Floppy Motor */
+
+	/* Turn on cache */
+	set_cache(1);
+
+	/* Setup the display */
+	display_init();
+	
+	cprint(5, 60, "| Time:   0:00:00");
+	cprint(1, COL_MID,"Pass   %");
+	cprint(2, COL_MID,"Test   %");
+	cprint(3, COL_MID,"Test #");
+	cprint(4, COL_MID,"Testing: ");
+	cprint(5, COL_MID,"Pattern: ");
+	cprint(1, 0, "CLK:           (32b Mode)");
+	cprint(2, 0, "L1 Cache: Unknown ");
+	cprint(3, 0, "L2 Cache: Unknown ");
+  cprint(4, 0, "L3 Cache:  None    ");
+  cprint(5, 0, "Memory  :         ");
+  cprint(6, 0, "------------------------------------------------------------------------------");
+	cprint(7, 0, "Core#:");
+	cprint(8, 0, "State:");
+	cprint(9, 0, "Cores:    Active /    Total (Run: All) | Pass:       0        Errors:      0  ");
+	cprint(10, 0, "------------------------------------------------------------------------------");
+
+	/*	
+	for(i=0, pp=(char *)(SCREEN_ADR+(5*160)+(53*2)+1); i<20; i++, pp+=2) {
+		*pp = 0x92;
+	}
+
+	for(i=0, pp=(char *)(SCREEN_ADR+0*160+1); i<80; i++, pp+=2) {
+		*pp = 0x47;
+	}
+	*/
+	
+	cprint(7, 39, "| Chipset : Unknown");
+	cprint(8, 39, "| Memory Type : Unknown");
+	
+
+	for(i=0; i < 6; i++) {
+		cprint(i, COL_MID-2, "| ");
+	}
+	
+	footer();
+
+  aprint(5, 10, v->test_pages);
+
+  v->pass = 0;
+  v->msg_line = 0;
+  v->ecount = 0;
+  v->ecc_ecount = 0;
+	v->testsel = -1;
+	v->msg_line = LINE_SCROLL-1;
+	v->scroll_start = v->msg_line * 160;
+	v->erri.low_addr.page = 0x7fffffff;
+	v->erri.low_addr.offset = 0xfff;
+	v->erri.high_addr.page = 0;
+	v->erri.high_addr.offset = 0;
+	v->erri.min_bits = 32;
+	v->erri.max_bits = 0;
+	v->erri.min_bits = 32;
+	v->erri.max_bits = 0;
+	v->erri.maxl = 0;
+	v->erri.cor_err = 0;
+	v->erri.ebits = 0;
+	v->erri.hdr_flag = 0;
+	v->erri.tbits = 0;
+	for (i=0; tseq[i].msg != NULL; i++) {
+		tseq[i].errors = 0;
+	}
+	if (dmi_initialized) {
+		for (i=0; i < MAX_DMI_MEMDEVS; i++){
+			if (dmi_err_cnts[i] > 0) {
+				dmi_err_cnts[i] = 0;
+			}
+		}
+	}
+	
+	/* setup beep mode */
+	beepmode = BEEP_MODE;
+	
+	/* Get the cpu and cache information */
+	get_cpuid();
+
+	/* setup pci */
+	pci_init(); 
+
+	get_cache_size(); 
+
+	cpu_type();
+
+	cpu_cache_speed();
+
+  /* Check fail safe */	
+	failsafe(5000, 0x3B);
+
+	/* Initalize SMP */
+	initialise_cpus();
+	
+	for (i = 0; i <num_cpus; i++) {
+		dprint(7, i+7, i%10, 1, 0);
+		cprint(8, i+7, "S");
+	}
+
+	dprint(9, 19, num_cpus, 2, 0);
+	
+	if((v->fail_safe & 3) == 2)
+	{
+			cprint(LINE_CPU,9, "(SMP: Disabled)");
+			cprint(LINE_RAM,9, "Running...");
+	}
+	// dprint(10, 5, found_cpus, 2, 0); 
+
+	/* Find Memory Specs */
+	if(v->fail_safe & 1) 
+		{ 	
+			cprint(LINE_CPU, COL_SPEC, " **** FAIL SAFE **** FAIL SAFE **** ");
+			cprint(LINE_RAM, COL_SPEC, "   No detection, same reliability   ");
+		} else {
+			find_controller();
+			get_spd_spec();
+			if(num_cpus <= 16 && !(v->fail_safe & 4)) { coretemp(); }
+		}
+	
+	if(v->check_temp > 0 && !(v->fail_safe & 4))
+	{
+		cprint(LINE_CPU, 26, "|  CPU Temp");
+		cprint(LINE_CPU+1, 26, "|      øC");
+	}
+	
+		beep(600);
+		beep(1000);
+	
+	/* Record the start time */
+  asm __volatile__ ("rdtsc":"=a" (v->startl),"=d" (v->starth));
+  v->snapl = v->startl;
+  v->snaph = v->starth;
+	if (l1_cache == 0) { l1_cache = 64; }
+	if (l2_cache == 0) { l1_cache = 512; }
+	v->printmode=PRINTMODE_ADDRESSES;
+	v->numpatn=0;
+}
+
+/* Get cache sizes for most AMD and Intel CPUs, exceptions for old CPUs are
+ * handled in CPU detection */
+void get_cache_size()
+{
+	int i, j, n, size;
+	unsigned int v[4];
+	unsigned char *dp = (unsigned char *)v;
+	struct cpuid4_eax *eax = (struct cpuid4_eax *)&v[0];
+	struct cpuid4_ebx *ebx = (struct cpuid4_ebx *)&v[1];
+	struct cpuid4_ecx *ecx = (struct cpuid4_ecx *)&v[2];
+
+	switch(cpu_id.vend_id.char_array[0]) {
+	/* AMD Processors */
+	case 'A':
+		//l1_cache = cpu_id.cache_info.amd.l1_i_sz;
+		l1_cache = cpu_id.cache_info.amd.l1_d_sz;
+		l2_cache = cpu_id.cache_info.amd.l2_sz;
+		l3_cache = cpu_id.cache_info.amd.l3_sz;
+    l3_cache *= 512;
+		break;
+	case 'G':
+		/* Intel Processors */
+		l1_cache = 0;
+		l2_cache = 0;
+		l3_cache = 0;
+
+		/* Use CPUID(4) if it is available */
+		if (cpu_id.max_cpuid > 3) {
+
+		   /* figure out how many cache leaves */
+		    n = -1;
+		    do 
+		    {
+					++n;
+					/* Do cpuid(4) loop to find out num_cache_leaves */
+					cpuid_count(4, n, &v[0], &v[1], &v[2], &v[3]);
+		    } while ((eax->ctype) != 0);
+
+		    /* loop through all of the leaves */
+		    for (i=0; i<n; i++) 
+		    {
+					cpuid_count(4, i, &v[0], &v[1], &v[2], &v[3]);
+
+					/* Check for a valid cache type */
+					if (eax->ctype == 1 || eax->ctype == 3) 
+					{
+
+			    	/* Compute the cache size */
+			    	size = (ecx->number_of_sets + 1) *
+            	              	  (ebx->coherency_line_size + 1) *
+              	            	  (ebx->physical_line_partition + 1) *
+                	          	  (ebx->ways_of_associativity + 1);
+			    	size /= 1024;
+
+				    switch (eax->level) 
+				    {
+					  	case 1:
+								l1_cache += size;
+								break;
+					    case 2:
+								l2_cache += size;
+								break;
+					    case 3:
+								l3_cache += size;
+								break;
+					  }
+					}
+		    }
+		    return;
+		}
+
+		/* No CPUID(4) so we use the older CPUID(2) method */
+		/* Get number of times to iterate */
+		cpuid(2, &v[0], &v[1], &v[2], &v[3]);
+		n = v[0] & 0xff;
+                for (i=0 ; i<n ; i++) {
+                    cpuid(2, &v[0], &v[1], &v[2], &v[3]);
+
+                    /* If bit 31 is set, this is an unknown format */
+                    for (j=0 ; j<3 ; j++) {
+                            if (v[j] & (1 << 31)) {
+                                    v[j] = 0;
+			    }
+		    }
+
+                    /* Byte 0 is level count, not a descriptor */
+                    for (j = 1 ; j < 16 ; j++) {
+			switch(dp[j]) {
+			case 0x6:
+			case 0xa:
+			case 0x66:
+				l1_cache += 8;
+				break;
+			case 0x8:
+			case 0xc:
+			case 0xd:
+			case 0x60:
+			case 0x67:
+				l1_cache += 16;
+				break;
+			case 0xe:
+				l1_cache += 24;
+				break;
+			case 0x9:
+			case 0x2c:
+			case 0x30:
+			case 0x68:
+				l1_cache += 32;
+				break;
+			case 0x39:
+			case 0x3b:
+			case 0x41:
+			case 0x79:
+				l2_cache += 128;
+				break;
+			case 0x3a:
+				l2_cache += 192;
+				break;
+			case 0x21:
+			case 0x3c:
+			case 0x3f:
+			case 0x42:
+			case 0x7a:
+			case 0x82:
+				l2_cache += 256;
+				break;
+			case 0x3d:
+				l2_cache += 384;
+				break;
+			case 0x3e:
+			case 0x43:
+			case 0x7b:
+			case 0x7f:
+			case 0x80:
+			case 0x83:
+			case 0x86:
+				l2_cache += 512;
+				break;
+			case 0x44:
+			case 0x78:
+			case 0x7c:
+			case 0x84:
+			case 0x87:
+				l2_cache += 1024;
+				break;
+			case 0x45:
+			case 0x7d:
+			case 0x85:
+				l2_cache += 2048;
+				break;
+			case 0x48:
+				l2_cache += 3072;
+				break;
+			case 0x4e:
+				l2_cache += 6144;
+				break;
+			case 0x23:
+			case 0xd0:
+				l3_cache += 512;
+				break;
+			case 0xd1:
+			case 0xd6:
+				l3_cache += 1024;
+				break;
+			case 0x25:
+			case 0xd2:
+			case 0xd7:
+			case 0xdc:
+			case 0xe2:
+				l3_cache += 2048;
+				break;
+			case 0x29:
+			case 0x46:
+			case 0x49:
+			case 0xd8:
+			case 0xdd:
+			case 0xe3:
+				l3_cache += 4096;
+				break;
+			case 0x4a:
+				l3_cache += 6144;
+				break;
+			case 0x47:
+			case 0x4b:
+			case 0xde:
+			case 0xe4:
+				l3_cache += 8192;
+				break;	
+			case 0x4c:
+			case 0xea:
+				l3_cache += 12288;
+				break;	
+			case 0x4d:
+				l3_cache += 16384;
+				break;	
+			case 0xeb:
+				l3_cache += 18432;
+				break;	
+			case 0xec:
+				l3_cache += 24576;
+				break;	
+			} /* end switch */
+		    } /* end for 1-16 */
+		} /* end for 0 - n */
+	}
+}
+
+/*
+ * Find IMC type and set global variables accordingly
+ */
+void detect_imc(void)
+{
+	// Check AMD IMC
+	if(cpu_id.vend_id.char_array[0] == 'A' && cpu_id.vers.bits.family == 0xF) 
+		{
+			switch(cpu_id.vers.bits.extendedFamily)
+					{
+						case 0x0:
+							imc_type = 0x0100; // Old K8
+							break;
+						case 0x1:
+						case 0x2:
+							imc_type = 0x0101; // K10 (Family 10h & 11h)
+							break;
+						case 0x3:
+							imc_type = 0x0102; // A-Series APU (Family 12h)
+							break;
+						case 0x5:
+							imc_type = 0x0103; // C- / E- / Z- Series APU (Family 14h)
+							break;	
+						case 0x6:
+							imc_type = 0x0104; // FX Series (Family 15h)
+							break;								
+						case 0x7:
+							imc_type = 0x0105; // Kabini & related (Family 16h)
+							break;			
+					}	
+			return;
+		}
+					
+	// Check Intel IMC	
+	if(cpu_id.vend_id.char_array[0] == 'G' && cpu_id.vers.bits.family == 6 && cpu_id.vers.bits.extendedModel) 
+		{					
+			switch(cpu_id.vers.bits.model)
+			{
+				case 0x5:
+					if(cpu_id.vers.bits.extendedModel == 2) { imc_type = 0x0003; } // Core i3/i5 1st Gen 45 nm (NHM)
+					if(cpu_id.vers.bits.extendedModel == 3) { v->fail_safe |= 4; } // Atom Clover Trail
+					if(cpu_id.vers.bits.extendedModel == 4) { imc_type = 0x0007; } // HSW-ULT
+					break;
+				case 0x6:
+					if(cpu_id.vers.bits.extendedModel == 3) { 
+						imc_type = 0x0009;  // Atom Cedar Trail
+						v->fail_safe |= 4; // Disable Core temp
+					}
+					break;
+				case 0xA:
+					switch(cpu_id.vers.bits.extendedModel)
+					{
+						case 0x1:
+							imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHME)
+							break;
+						case 0x2:
+							imc_type = 0x0004; // Core 2nd Gen (SNB)
+							break;	
+						case 0x3:
+							imc_type = 0x0006; // Core 3nd Gen (IVB)						
+							break;
+					}
+					break;
+				case 0xC:
+					switch(cpu_id.vers.bits.extendedModel)
+					{
+						case 0x1:
+							if(cpu_id.vers.bits.stepping > 9) { imc_type = 0x0008; } // Atom PineView	
+							v->fail_safe |= 4; // Disable Core temp
+							break;	
+						case 0x2:
+							imc_type = 0x0002; // Core i7 1st Gen 32 nm (WMR)	
+							break;	
+						case 0x3:
+							imc_type = 0x0007; // Core 4nd Gen (HSW)						
+							break;
+					}
+					break;			
+				case 0xD:
+					imc_type = 0x0005; // SNB-E
+					break;				
+				case 0xE:
+					imc_type = 0x0001; // Core i7 1st Gen 45 nm (NHM)
+					break;				
+			}
+		
+		if(imc_type) { tsc_invariable = 1; }
+		return;
+		}
+}
+
+void smp_default_mode(void)
+{
+	int i, result;
+	char *cpupsn = cpu_id.brand_id.char_array;
+  char *disabledcpu[] = { "Opteron", "Xeon", "Genuine Intel" };
+  
+  for(i = 0; i < 3; i++) 
+  {
+  	result = strstr(cpupsn , disabledcpu[i]);
+  	if(result != -1) { v->fail_safe |= 0b10; }
+  }
+  
+  // For 5.01 release, SMP disabled by defualt by config.h toggle
+  if(CONSERVATIVE_SMP) { v->fail_safe |= 0b10; }
+  	
+}
+
+/*
+ * Find CPU type
+ */
+void cpu_type(void)
+{
+	/* If we can get a brand string use it, and we are done */
+	if (cpu_id.max_xcpuid >= 0x80000004) {
+		cprint(0, COL_MID, cpu_id.brand_id.char_array);
+		//If we have a brand string, maybe we have an IMC. Check that.
+		detect_imc();
+		smp_default_mode();	
+		return;
+	}
+
+	/* The brand string is not available so we need to figure out 
+	 * CPU what we have */
+	switch(cpu_id.vend_id.char_array[0]) {
+	/* AMD Processors */
+	case 'A':
+		switch(cpu_id.vers.bits.family) {
+		case 4:
+			switch(cpu_id.vers.bits.model) {
+			case 3:
+				cprint(0, COL_MID, "AMD 486DX2");
+				break;
+			case 7:
+				cprint(0, COL_MID, "AMD 486DX2-WB");
+				break;
+			case 8:
+				cprint(0, COL_MID, "AMD 486DX4");
+				break;
+			case 9:
+				cprint(0, COL_MID, "AMD 486DX4-WB");
+				break;
+			case 14:
+				cprint(0, COL_MID, "AMD 5x86-WT");
+				break;
+			case 15:
+				cprint(0, COL_MID, "AMD 5x86-WB");
+				break;
+			}
+			/* Since we can't get CPU speed or cache info return */
+			return;
+		case 5:
+			switch(cpu_id.vers.bits.model) {
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+				cprint(0, COL_MID, "AMD K5");
+				l1_cache = 8;
+				break;
+			case 6:
+			case 7:
+				cprint(0, COL_MID, "AMD K6");
+				break;
+			case 8:
+				cprint(0, COL_MID, "AMD K6-2");
+				break;
+			case 9:
+				cprint(0, COL_MID, "AMD K6-III");
+				break;
+			case 13: 
+				cprint(0, COL_MID, "AMD K6-III+"); 
+				break;
+			}
+			break;
+		case 6:
+
+			switch(cpu_id.vers.bits.model) {
+			case 1:
+				cprint(0, COL_MID, "AMD Athlon (0.25)");
+				break;
+			case 2:
+			case 4:
+				cprint(0, COL_MID, "AMD Athlon (0.18)");
+				break;
+			case 6:
+				if (l2_cache == 64) {
+					cprint(0, COL_MID, "AMD Duron (0.18)");
+				} else {
+					cprint(0, COL_MID, "Athlon XP (0.18)");
+				}
+				break;
+			case 8:
+			case 10:
+				if (l2_cache == 64) {
+					cprint(0, COL_MID, "AMD Duron (0.13)");
+				} else {
+					cprint(0, COL_MID, "Athlon XP (0.13)");
+				}
+				break;
+			case 3:
+			case 7:
+				cprint(0, COL_MID, "AMD Duron");
+				/* Duron stepping 0 CPUID for L2 is broken */
+				/* (AMD errata T13)*/
+				if (cpu_id.vers.bits.stepping == 0) { /* stepping 0 */
+					/* Hard code the right L2 size */
+					l2_cache = 64;
+				} else {
+				}
+				break;
+			}
+			break;
+
+			/* All AMD family values >= 10 have the Brand ID
+			 * feature so we don't need to find the CPU type */
+		}
+		break;
+
+	/* Intel or Transmeta Processors */
+	case 'G':
+		if ( cpu_id.vend_id.char_array[7] == 'T' ) { /* GenuineTMx86 */
+			if (cpu_id.vers.bits.family == 5) {
+				cprint(0, COL_MID, "TM 5x00");
+			} else if (cpu_id.vers.bits.family == 15) {
+				cprint(0, COL_MID, "TM 8x00");
+			}
+			l1_cache = cpu_id.cache_info.ch[3] + cpu_id.cache_info.ch[7];
+			l2_cache = (cpu_id.cache_info.ch[11]*256) + cpu_id.cache_info.ch[10];
+		} else {				/* GenuineIntel */
+			if (cpu_id.vers.bits.family == 4) {
+			switch(cpu_id.vers.bits.model) {
+			case 0:
+			case 1:
+				cprint(0, COL_MID, "Intel 486DX");
+				break;
+			case 2:
+				cprint(0, COL_MID, "Intel 486SX");
+				break;
+			case 3:
+				cprint(0, COL_MID, "Intel 486DX2");
+				break;
+			case 4:
+				cprint(0, COL_MID, "Intel 486SL");
+				break;
+			case 5:
+				cprint(0, COL_MID, "Intel 486SX2");
+				break;
+			case 7:
+				cprint(0, COL_MID, "Intel 486DX2-WB");
+				break;
+			case 8:
+				cprint(0, COL_MID, "Intel 486DX4");
+				break;
+			case 9:
+				cprint(0, COL_MID, "Intel 486DX4-WB");
+				break;
+			}
+			/* Since we can't get CPU speed or cache info return */
+			return;
+		}
+
+
+		switch(cpu_id.vers.bits.family) {
+		case 5:
+			switch(cpu_id.vers.bits.model) {
+			case 0:
+			case 1:
+			case 2:
+			case 3:
+			case 7:
+				cprint(0, COL_MID, "Pentium");
+				if (l1_cache == 0) {
+					l1_cache = 8;
+				}
+				break;
+			case 4:
+			case 8:
+				cprint(0, COL_MID, "Pentium-MMX");
+				if (l1_cache == 0) {
+					l1_cache = 16;
+				}
+				break;
+			}
+			break;
+		case 6:
+			switch(cpu_id.vers.bits.model) {
+			case 0:
+			case 1:
+				cprint(0, COL_MID, "Pentium Pro");
+				break;
+			case 3:
+			case 4:
+				cprint(0, COL_MID, "Pentium II");
+				break;
+			case 5:
+				if (l2_cache == 0) {
+					cprint(0, COL_MID, "Celeron");
+				} else {
+					cprint(0, COL_MID, "Pentium II");
+				}
+				break;
+			case 6:
+				  if (l2_cache == 128) {
+					cprint(0, COL_MID, "Celeron");
+				  } else {
+					cprint(0, COL_MID, "Pentium II");
+				  }
+				}
+				break;
+			case 7:
+			case 8:
+			case 11:
+				if (l2_cache == 128) {
+					cprint(0, COL_MID, "Celeron");
+				} else {
+					cprint(0, COL_MID, "Pentium III");
+				}
+				break;
+			case 9:
+				if (l2_cache == 512) {
+					cprint(0, COL_MID, "Celeron M (0.13)");
+				} else {
+					cprint(0, COL_MID, "Pentium M (0.13)");
+				}
+				break;
+     			case 10:
+				cprint(0, COL_MID, "Pentium III Xeon");
+				break;
+			case 12:
+				l1_cache = 24;
+				cprint(0, COL_MID, "Atom (0.045)");
+				break;					
+			case 13:
+				if (l2_cache == 1024) {
+					cprint(0, COL_MID, "Celeron M (0.09)");
+				} else {
+					cprint(0, COL_MID, "Pentium M (0.09)");
+				}
+				break;
+			case 14:
+				cprint(0, COL_MID, "Intel Core");
+				break;				
+			case 15:
+				if (l2_cache == 1024) {
+					cprint(0, COL_MID, "Pentium E");
+				} else {
+					cprint(0, COL_MID, "Intel Core 2");
+				}
+				break;
+			}
+			break;
+		case 15:
+			switch(cpu_id.vers.bits.model) {
+			case 0:
+			case 1:			
+			case 2:
+				if (l2_cache == 128) {
+					cprint(0, COL_MID, "Celeron");
+				} else {
+					cprint(0, COL_MID, "Pentium 4");
+				}
+				break;
+			case 3:
+			case 4:
+				if (l2_cache == 256) {
+					cprint(0, COL_MID, "Celeron (0.09)");
+				} else {
+					cprint(0, COL_MID, "Pentium 4 (0.09)");
+				}
+				break;
+			case 6:
+				cprint(0, COL_MID, "Pentium D (65nm)");
+				break;
+			default:
+				cprint(0, COL_MID, "Unknown Intel");
+ 				break;
+			break;
+		    }
+
+		}
+		break;
+
+	/* VIA/Cyrix/Centaur Processors with CPUID */
+	case 'C':
+		if ( cpu_id.vend_id.char_array[1] == 'e' ) { /* CentaurHauls */
+			l1_cache = cpu_id.cache_info.ch[3] + cpu_id.cache_info.ch[7];
+			l2_cache = cpu_id.cache_info.ch[11];
+			switch(cpu_id.vers.bits.family){
+			case 5:
+				cprint(0, COL_MID, "Centaur 5x86");
+				break;
+			case 6: // VIA C3
+				switch(cpu_id.vers.bits.model){
+				default:
+				    if (cpu_id.vers.bits.stepping < 8) {
+					cprint(0, COL_MID, "VIA C3 Samuel2");
+				    } else {
+					cprint(0, COL_MID, "VIA C3 Eden");
+				    }
+				break;
+				case 10:
+					cprint(0, COL_MID, "VIA C7 (C5J)");
+					l1_cache = 64;
+					l2_cache = 128;
+					break;
+				case 13:
+					cprint(0, COL_MID, "VIA C7 (C5R)");
+					l1_cache = 64;
+					l2_cache = 128;
+					break;
+				case 15:
+					cprint(0, COL_MID, "VIA Isaiah (CN)");
+					l1_cache = 64;
+					l2_cache = 128;
+					break;
+				}
+			}
+		} else {				/* CyrixInstead */
+			switch(cpu_id.vers.bits.family) {
+			case 5:
+				switch(cpu_id.vers.bits.model) {
+				case 0:
+					cprint(0, COL_MID, "Cyrix 6x86MX/MII");
+					break;
+				case 4:
+					cprint(0, COL_MID, "Cyrix GXm");
+					break;
+				}
+				return;
+
+			case 6: // VIA C3
+				switch(cpu_id.vers.bits.model) {
+				case 6:
+					cprint(0, COL_MID, "Cyrix III");
+					break;
+				case 7:
+					if (cpu_id.vers.bits.stepping < 8) {
+						cprint(0, COL_MID, "VIA C3 Samuel2");
+					} else {
+						cprint(0, COL_MID, "VIA C3 Ezra-T");
+					}
+					break;
+				case 8:
+					cprint(0, COL_MID, "VIA C3 Ezra-T");
+					break;
+				case 9:
+					cprint(0, COL_MID, "VIA C3 Nehemiah");
+					break;
+				}
+				// L1 = L2 = 64 KB from Cyrix III to Nehemiah
+				l1_cache = 64;
+				l2_cache = 64;
+				break;
+			}
+		}
+		break;
+	/* Unknown processor */
+	default:
+		/* Make a guess at the family */
+		switch(cpu_id.vers.bits.family) {
+		case 5:
+			cprint(0, COL_MID, "586");
+		case 6:
+			cprint(0, COL_MID, "686");
+		default:
+			cprint(0, COL_MID, "Unidentified Processor");
+		}
+	}
+}
+
+#define STEST_ADDR 0x100000	/* Measure memory speed starting at 1MB */
+
+/* Measure and display CPU and cache sizes and speeds */
+void cpu_cache_speed()
+{
+	int i, off = 4;
+	ulong speed;
+
+
+	/* Print CPU speed */
+	if ((speed = cpuspeed()) > 0) {
+		if (speed < 999499) {
+			speed += 50; /* for rounding */
+			cprint(1, off, "    . MHz");
+			dprint(1, off+1, speed/1000, 3, 1);
+			dprint(1, off+5, (speed/100)%10, 1, 0);
+		} else {
+			speed += 500; /* for rounding */
+			cprint(1, off, "      MHz");
+			dprint(1, off, speed/1000, 5, 0);
+		}
+		extclock = speed;
+	}
+
+	/* Print out L1 cache info */
+	/* To measure L1 cache speed we use a block size that is 1/4th */
+	/* of the total L1 cache size since half of it is for instructions */
+	if (l1_cache) {
+		cprint(2, 0, "L1 Cache:     K  ");
+		dprint(2, 11, l1_cache, 3, 0);
+		if ((speed=memspeed(STEST_ADDR, (l1_cache/2)*1024, 200))) {
+			cprint(2, 16, "       MB/s");
+			dprint(2, 16, speed, 6, 0);
+		}
+	}
+
+	/* Print out L2 cache info */
+	/* We measure the L2 cache speed by using a block size that is */
+	/* the size of the L1 cache.  We have to fudge if the L1 */
+	/* cache is bigger than the L2 */
+	if (l2_cache) {
+		cprint(3, 0, "L2 Cache:     K  ");
+		dprint(3, 10, l2_cache, 4, 0);
+
+		if (l2_cache < l1_cache) {
+			i = l1_cache / 4 + l2_cache / 4;
+		} else {
+			i = l1_cache;
+		}
+		if ((speed=memspeed(STEST_ADDR, i*1024, 200))) {
+			cprint(3, 16, "       MB/s");
+			dprint(3, 16, speed, 6, 0);
+		}
+	}
+	/* Print out L3 cache info */
+	/* We measure the L3 cache speed by using a block size that is */
+	/* 2X the size of the L2 cache. */
+
+	if (l3_cache) 
+	{
+		cprint(4, 0, "L3 Cache:     K  ");
+   	aprint(4, 10, l3_cache/4);
+    //dprint(4, 10, l3_cache, 4, 0);
+    
+    		i = l2_cache*2;
+    
+    		if ((speed=memspeed(STEST_ADDR, i*1024, 150))) {
+    			cprint(4, 16, "       MB/s");
+    			dprint(4, 16, speed, 6, 0);
+    		}
+   }
+}
+
+/* Measure and display memory speed, multitasked using all CPUs */
+ulong spd[MAX_CPUS];
+void get_mem_speed(int me, int ncpus)
+{
+	int i;
+	ulong speed=0;
+
+   /* Determine memory speed.  To find the memory speed we use 
+   * A block size that is the sum of all the L1, L2 & L3 caches
+	 * in all cpus * 6 */
+   i = (l3_cache + l2_cache + l1_cache) * 4;
+
+	/* Make sure that we have enough memory to do the test */
+	/* If not use all we have */
+	if ((1 + (i * 2)) > (v->plim_upper << 2)) {
+		i = ((v->plim_upper <<2) - 1) / 2;
+	}
+	
+	speed = memspeed(STEST_ADDR, i * 1024, 100);
+	cprint(5, 16, "       MB/s");
+	dprint(5, 16, speed, 6, 0);
+	
+}
+
+/* #define TICKS 5 * 11832 (count = 6376)*/
+/* #define TICKS (65536 - 12752) */
+#define TICKS 59659	/* 50 ms */
+
+/* Returns CPU clock in khz */
+ulong stlow, sthigh;
+static int cpuspeed(void)
+{
+	int loops;
+	ulong end_low, end_high;
+
+	if (cpu_id.fid.bits.rdtsc == 0 ) {
+		return(-1);
+	}
+
+	/* Setup timer */
+	outb((inb(0x61) & ~0x02) | 0x01, 0x61);
+	outb(0xb0, 0x43); 
+	outb(TICKS & 0xff, 0x42);
+	outb(TICKS >> 8, 0x42);
+
+	asm __volatile__ ("rdtsc":"=a" (stlow),"=d" (sthigh));
+
+	loops = 0;
+	do {
+		loops++;
+	} while ((inb(0x61) & 0x20) == 0);
+
+	asm __volatile__ (
+		"rdtsc\n\t" \
+		"subl stlow,%%eax\n\t" \
+		"sbbl sthigh,%%edx\n\t" \
+		:"=a" (end_low), "=d" (end_high)
+	);
+
+	/* Make sure we have a credible result */
+	if (loops < 4 || end_low < 50000) {
+		return(-1);
+	}
+	v->clks_msec = end_low/50;
+
+	if (tsc_invariable) end_low = correct_tsc(end_low);
+
+	return(v->clks_msec);
+}
+
+/* Measure cache speed by copying a block of memory. */
+/* Returned value is kbytes/second */
+ulong memspeed(ulong src, ulong len, int iter)
+{
+	int i;
+	ulong dst, wlen;
+	ulong st_low, st_high;
+	ulong end_low, end_high;
+	ulong cal_low, cal_high;
+
+	if (cpu_id.fid.bits.rdtsc == 0 ) {
+		return(-1);
+	}
+	if (len == 0) return(-2);
+
+	dst = src + len;
+	wlen = len / 4;  /* Length is bytes */
+
+	/* Calibrate the overhead with a zero word copy */
+	asm __volatile__ ("rdtsc":"=a" (st_low),"=d" (st_high));
+	for (i=0; i<iter; i++) {
+		asm __volatile__ (
+			"movl %0,%%esi\n\t" \
+ 		 	"movl %1,%%edi\n\t" \
+ 		 	"movl %2,%%ecx\n\t" \
+ 		 	"cld\n\t" \
+ 		 	"rep\n\t" \
+ 		 	"movsl\n\t" \
+			:: "g" (src), "g" (dst), "g" (0)
+			: "esi", "edi", "ecx"
+		);
+	}
+	asm __volatile__ ("rdtsc":"=a" (cal_low),"=d" (cal_high));
+
+	/* Compute the overhead time */
+	asm __volatile__ (
+		"subl %2,%0\n\t"
+		"sbbl %3,%1"
+		:"=a" (cal_low), "=d" (cal_high)
+		:"g" (st_low), "g" (st_high),
+		"0" (cal_low), "1" (cal_high)
+	);
+
+
+	/* Now measure the speed */
+	/* Do the first copy to prime the cache */
+	asm __volatile__ (
+		"movl %0,%%esi\n\t" \
+		"movl %1,%%edi\n\t" \
+ 	 	"movl %2,%%ecx\n\t" \
+ 	 	"cld\n\t" \
+ 	 	"rep\n\t" \
+ 	 	"movsl\n\t" \
+		:: "g" (src), "g" (dst), "g" (wlen)
+		: "esi", "edi", "ecx"
+	);
+	asm __volatile__ ("rdtsc":"=a" (st_low),"=d" (st_high));
+	for (i=0; i<iter; i++) {
+	        asm __volatile__ (
+			"movl %0,%%esi\n\t" \
+			"movl %1,%%edi\n\t" \
+ 		 	"movl %2,%%ecx\n\t" \
+ 		 	"cld\n\t" \
+ 		 	"rep\n\t" \
+ 		 	"movsl\n\t" \
+			:: "g" (src), "g" (dst), "g" (wlen)
+			: "esi", "edi", "ecx"
+		);
+	}
+	asm __volatile__ ("rdtsc":"=a" (end_low),"=d" (end_high));
+
+	/* Compute the elapsed time */
+	asm __volatile__ (
+		"subl %2,%0\n\t"
+		"sbbl %3,%1"
+		:"=a" (end_low), "=d" (end_high)
+		:"g" (st_low), "g" (st_high),
+		"0" (end_low), "1" (end_high)
+	);
+	/* Subtract the overhead time */
+	asm __volatile__ (
+		"subl %2,%0\n\t"
+		"sbbl %3,%1"
+		:"=a" (end_low), "=d" (end_high)
+		:"g" (cal_low), "g" (cal_high),
+		"0" (end_low), "1" (end_high)
+	);
+
+	/* Make sure that the result fits in 32 bits */
+	//hprint(11,40,end_high);
+	if (end_high) {
+		return(-3);
+	}
+	end_low /= 2;
+
+	/* Convert to clocks/KB */
+	end_low /= len;
+	end_low *= 1024;
+	end_low /= iter;
+	if (end_low == 0) {
+		return(-4);
+	}
+
+	/* Convert to kbytes/sec */
+
+	if (tsc_invariable) end_low = correct_tsc(end_low);
+
+	return((v->clks_msec)/end_low);
+}
+
+#define rdmsr(msr,val1,val2) \
+	__asm__ __volatile__("rdmsr" \
+		  : "=a" (val1), "=d" (val2) \
+		  : "c" (msr))
+
+
+ulong correct_tsc(ulong el_org)
+{
+	float coef_now, coef_max;
+	int msr_lo, msr_hi, is_xe;
+	
+	rdmsr(0x198, msr_lo, msr_hi);
+	is_xe = (msr_lo >> 31) & 0x1;		
+	
+	if(is_xe){
+		rdmsr(0x198, msr_lo, msr_hi);
+		coef_max = ((msr_hi >> 8) & 0x1F);	
+		if ((msr_hi >> 14) & 0x1) { coef_max = coef_max + 0.5f; }
+	} else {
+		rdmsr(0x17, msr_lo, msr_hi);
+		coef_max = ((msr_lo >> 8) & 0x1F);
+		if ((msr_lo >> 14) & 0x1) { coef_max = coef_max + 0.5f; }
+	}
+	
+	if(cpu_id.fid.bits.eist) {
+		rdmsr(0x198, msr_lo, msr_hi);
+		coef_now = ((msr_lo >> 8) & 0x1F);
+		if ((msr_lo >> 14) & 0x1) { coef_now = coef_now + 0.5f; }
+	} else {
+		rdmsr(0x2A, msr_lo, msr_hi);
+		coef_now = (msr_lo >> 22) & 0x1F;
+	}
+	if(coef_max && coef_now) {
+		el_org = (ulong)(el_org * coef_now / coef_max);
+	}
+	return el_org;
+}
+
diff --git a/io.h b/io.h
new file mode 100644
index 0000000..4fda2de
--- /dev/null
+++ b/io.h
@@ -0,0 +1,118 @@
+#ifndef _ASM_IO_H
+#define _ASM_IO_H
+
+/*
+ * This file contains the definitions for the x86 IO instructions
+ * inb/inw/inl/outb/outw/outl and the "string versions" of the same
+ * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing"
+ * versions of the single-IO instructions (inb_p/inw_p/..).
+ *
+ * This file is not meant to be obfuscating: it's just complicated
+ * to (a) handle it all in a way that makes gcc able to optimize it
+ * as well as possible and (b) trying to avoid writing the same thing
+ * over and over again with slight variations and possibly making a
+ * mistake somewhere.
+ */
+
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#else
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#else
+#define SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+/*
+ * Talk about misusing macros..
+ */
+
+#define __OUT1(s,x) \
+extern inline void __out##s(unsigned x value, unsigned short port) {
+
+#define __OUT2(s,s1,s2) \
+__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
+
+#define __OUT(s,s1,x) \
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
+__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
+__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; }
+
+#define __IN1(s) \
+extern inline RETURN_TYPE __in##s(unsigned short port) { RETURN_TYPE _v;
+
+#define __IN2(s,s1,s2) \
+__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
+
+#define __IN(s,s1,i...) \
+__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
+__IN1(s##c) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \
+__IN1(s##_p) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
+__IN1(s##c_p) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; }
+
+#define __OUTS(s) \
+extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; outs" #s \
+: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define RETURN_TYPE unsigned char
+/* __IN(b,"b","0" (0)) */
+__IN(b,"")
+#undef RETURN_TYPE
+#define RETURN_TYPE unsigned short
+/* __IN(w,"w","0" (0)) */
+__IN(w,"")
+#undef RETURN_TYPE
+#define RETURN_TYPE unsigned int
+__IN(l,"")
+#undef RETURN_TYPE
+
+__OUT(b,"b",char)
+__OUT(w,"w",short)
+__OUT(l,,int)
+
+__OUTS(b)
+__OUTS(w)
+__OUTS(l)
+
+/*
+ * Note that due to the way __builtin_constant_p() works, you
+ *  - can't use it inside a inline function (it will never be true)
+ *  - you don't have to worry about side effects within the __builtin..
+ */
+#define outb(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+	__outbc((val),(port)) : \
+	__outb((val),(port)))
+
+#define inb(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+	__inbc(port) : \
+	__inb(port))
+
+
+#define outw(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+	__outwc((val),(port)) : \
+	__outw((val),(port)))
+
+#define inw(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+	__inwc(port) : \
+	__inw(port))
+
+
+#define outl(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+	__outlc((val),(port)) : \
+	__outl((val),(port)))
+
+#define inl(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+	__inlc(port) : \
+	__inl(port))
+#endif
diff --git a/jedec_id.h b/jedec_id.h
new file mode 100644
index 0000000..b670f96
--- /dev/null
+++ b/jedec_id.h
@@ -0,0 +1,986 @@
+/* MemTest86+ V5 Specific code (GPL V2.0)

+ * By Samuel DEMEULEMEESTER, sdemeule@memtest.org

+ * http://www.canardpc.com - http://www.memtest.org

+ * ------------------------------------------------

+ * Based on JEDEC JEP106-AG - January 2012

+ * All mo

+ */

+

+struct spd_jedec_manufacturer {

+    unsigned cont_code;

+    unsigned hex_byte;

+    char *name;

+};

+

+static struct spd_jedec_manufacturer jep106[] = {

+	 { 0, 0x01, "AMD"},

+	 { 0, 0x02, "AMI"},

+   { 0, 0x83, "Fairchild"},

+	 { 0, 0x04, "Fujitsu"},

+// { 0, 0x85, "GTE"},

+   { 0, 0x86, "Harris"},

+	 { 0, 0x07, "Hitachi"},

+   { 0, 0x08, "Inmos"},

+	 { 0, 0x89, "Intel"},

+   { 0, 0x8a, "I.T.T."},

+	 { 0, 0x0b, "Intersil"},

+   { 0, 0x8c, "Monolithic Memories"},

+	 { 0, 0x0d, "Mostek"},

+	 { 0, 0x0e, "Freescale"},

+	 { 0, 0x8f, "National"},

+	 { 0, 0x10, "NEC"},

+   { 0, 0x91, "RCA"},

+	 { 0, 0x92, "Raytheon"},

+	 { 0, 0x13, "Conexant"},

+// { 0, 0x94, "Seeq"},

+	 { 0, 0x15, "NXP"},

+   { 0, 0x16, "Synertek"},

+	 { 0, 0x97, "Texas Instruments"},

+	 { 0, 0x98, "Toshiba"},

+   { 0, 0x19, "Xicor"},

+   { 0, 0x1a, "Zilog"},

+// { 0, 0x9b, "Eurotechnique"},

+	 { 0, 0x1c, "Mitsubishi"},

+   { 0, 0x9d, "Lucent (AT&T)"},

+// { 0, 0x9e, "Exel"},

+	 { 0, 0x1f, "Atmel"},

+   { 0, 0x20, "SGS/Thomson"},

+// { 0, 0xa1, "Lattice Semi."},

+   { 0, 0xa2, "NCR"},

+// { 0, 0x23, "Wafer Scale Integration"},

+	 { 0, 0xa4, "IBM"},

+   { 0, 0x25, "Tristar"},

+// { 0, 0x26, "Visic"},

+	 { 0, 0xa7, "Intl. CMOS Technology"},

+// { 0, 0xa8, "SSSI"},

+	 { 0, 0x29, "MicrochipTechnology"},

+// { 0, 0x2a, "Ricoh"},

+// { 0, 0xab, "VLSI"},

+	 { 0, 0x2c, "Micron"},

+	 { 0, 0xad, "Hynix"},

+	 { 0, 0xae, "OKI Semiconductor"},

+// { 0, 0x2f, "ACTEL"},

+   { 0, 0xb0, "Sharp"},

+   { 0, 0x31, "Catalyst"},

+	 { 0, 0x32, "Panasonic"},

+	 { 0, 0xb3, "IDT"},

+   { 0, 0x34, "Cypress"},

+   { 0, 0xb5, "DEC"},

+   { 0, 0xb6, "LSI Logic"},

+// { 0, 0x37, "Zarlink (Plessey)"},

+   { 0, 0x38, "UTMC"},

+// { 0, 0xb9, "Thinking Machine"},

+   { 0, 0xba, "Thomson CSF"},

+// { 0, 0x3b, "Integrated CMOS (Vertex)"},

+// { 0, 0xbc, "Honeywell"},

+	 { 0, 0x3d, "Tektronix"},

+// { 0, 0x3e, "Oracle"},

+// { 0, 0xbf, "Silicon Storage Technology"},

+	 { 0, 0x40, "ProMos/Mosel"},

+	 { 0, 0xc1, "Infineon"},

+// { 0, 0xc2, "Macronix"},

+   { 0, 0x43, "Xerox"},

+// { 0, 0xc4, "Plus Logic"},

+	 { 0, 0x45, "SanDisk"},

+// { 0, 0x46, "Elan Circuit Tech."},

+// { 0, 0xc7, "European Silicon Str."},

+	 { 0, 0xc8, "Apple Computer"},

+	 { 0, 0x49, "Xilinx"},

+	 { 0, 0x4a, "Compaq"},

+// { 0, 0xcb, "Protocol Engines"},

+// { 0, 0x4c, "SCI"},

+	 { 0, 0xcd, "Seiko Instruments"},

+	 { 0, 0xce, "Samsung"},

+// { 0, 0x4f, "I3 Design System"},

+// { 0, 0xd0, "Klic"},

+// { 0, 0x51, "Crosspoint Solutions"},

+// { 0, 0x52, "Alliance Semiconductor"},

+// { 0, 0xd3, "Tandem"},

+	 { 0, 0x54, "Hewlett-Packard"},

+	 { 0, 0xd5, "Integrated Silicon Solutions"},

+// { 0, 0xd6, "Brooktree"},

+// { 0, 0x57, "New Media"},

+// { 0, 0x58, "MHS Electronic"},

+// { 0, 0xd9, "Performance Semi."},

+	 { 0, 0xda, "Winbond Electronic"},

+// { 0, 0x5b, "Kawasaki Steel"},

+// { 0, 0xdc, "Bright Micro"},

+// { 0, 0x5d, "TECMAR"},

+	 { 0, 0x5e, "Exar"},

+// { 0, 0xdf, "PCMCIA"},

+	 { 0, 0xe0, "LG"},

+// { 0, 0x61, "Northern Telecom"},

+	 { 0, 0x62, "Sanyo"},

+// { 0, 0xe3, "Array Microsystems"},

+// { 0, 0x64, "Crystal Semiconductor"},

+	 { 0, 0xe5, "Analog Devices"},

+// { 0, 0xe6, "PMC-Sierra"},

+// { 0, 0x67, "Asparix"},

+// { 0, 0x68, "Convex Computer"},

+// { 0, 0xe9, "Quality Semiconductor"},

+// { 0, 0xea, "Nimbus Technology"},

+// { 0, 0x6b, "Transwitch"},

+// { 0, 0xec, "Micronas (ITT Intermetall)"},

+   { 0, 0x6d, "Cannon"},

+// { 0, 0x6e, "Altera"},

+// { 0, 0xef, "NEXCOM"},

+// { 0, 0x70, "QUALCOMM"},

+	 { 0, 0xf1, "Sony"},

+// { 0, 0xf2, "Cray Research"},

+// { 0, 0x73, "AMS(Austria Micro)"},

+// { 0, 0xf4, "Vitesse"},

+// { 0, 0x75, "Aster Electronics"},

+// { 0, 0x76, "Bay Networks (Synoptic)"},

+// { 0, 0xf7, "Zentrum/ZMD"},

+// { 0, 0xf8, "TRW"},

+// { 0, 0x79, "Thesys"},

+// { 0, 0x7a, "Solbourne Computer"},

+   { 0, 0xfb, "Allied-Signal"},

+   { 0, 0x7c, "Dialog"},

+   { 0, 0xfd, "Media Vision"},

+// { 0, 0xfe, "Numonyx"},

+	 { 1, 0x01, "Cirrus Logic"},

+   { 1, 0x02, "National Instruments"},

+// { 1, 0x83, "ILC Data Device"},

+// { 1, 0x04, "Alcatel Mietec"},

+// { 1, 0x85, "Micro Linear"},

+// { 1, 0x86, "Univ. of NC"},

+// { 1, 0x07, "JTAG Technologies"},

+// { 1, 0x08, "BAE Systems (Loral)"},

+// { 1, 0x89, "Nchip"},

+// { 1, 0x8a, "Galileo Tech"},

+// { 1, 0x0b, "Bestlink Systems"},

+// { 1, 0x8c, "Graychip"},

+// { 1, 0x0d, "GENNUM"},

+// { 1, 0x0e, "VideoLogic"},

+// { 1, 0x8f, "Robert Bosch"},

+// { 1, 0x10, "Chip Express"},

+   { 1, 0x91, "DATARAM"},

+// { 1, 0x92, "United Microelectronics Corp."},

+// { 1, 0x13, "TCSI"},

+   { 1, 0x94, "Smart Modular"},

+// { 1, 0x15, "Hughes Aircraft"},

+// { 1, 0x16, "Lanstar Semiconductor"},

+// { 1, 0x97, "Qlogic"},

+	 { 1, 0x98, "Kingston"},

+// { 1, 0x19, "Music Semi"},

+// { 1, 0x1a, "Ericsson Components"},

+// { 1, 0x9b, "SpaSE"},

+// { 1, 0x1c, "Eon Silicon Devices"},

+// { 1, 0x9d, "Programmable Micro Corp"},

+// { 1, 0x9e, "DoD"},

+// { 1, 0x1f, "Integ. Memories Tech."},

+// { 1, 0x20, "Corollary"},

+// { 1, 0xa1, "Dallas Semiconductor"},

+// { 1, 0xa2, "Omnivision"},

+// { 1, 0x23, "EIV(Switzerland)"},

+// { 1, 0xa4, "Novatel Wireless"},

+// { 1, 0x25, "Zarlink (Mitel)"},

+// { 1, 0x26, "Clearpoint"},

+// { 1, 0xa7, "Cabletron"},

+// { 1, 0xa8, "STEC (Silicon Tech)"},

+// { 1, 0x29, "Vanguard"},

+// { 1, 0x2a, "Hagiwara Sys-Com"},

+// { 1, 0xab, "Vantis"},

+// { 1, 0x2c, "Celestica"},

+// { 1, 0xad, "Century"},

+// { 1, 0xae, "Hal Computers"},

+// { 1, 0x2f, "Rohm Company"},

+// { 1, 0xb0, "Juniper Networks"},

+// { 1, 0x31, "Libit Signal Processing"},

+	 { 1, 0x32, "Mushkin"},

+// { 1, 0xb3, "Tundra Semiconductor"},

+	 { 1, 0x34, "Adaptec"},

+// { 1, 0xb5, "LightSpeed Semi."},

+// { 1, 0xb6, "ZSP Corp."},

+// { 1, 0x37, "AMIC Technology"},

+// { 1, 0x38, "Adobe Systems"},

+// { 1, 0xb9, "Dynachip"},

+   { 1, 0xba, "PNY"},

+// { 1, 0x3b, "Newport Digital"},

+// { 1, 0xbc, "MMC Networks"},

+// { 1, 0x3d, "T Square"},

+// { 1, 0x3e, "Seiko Epson"},

+// { 1, 0xbf, "Broadcom"},

+// { 1, 0x40, "Viking Components"},

+// { 1, 0xc1, "V3 Semiconductor"},

+// { 1, 0xc2, "Flextronics (Orbit Semiconductor)"},

+// { 1, 0x43, "Suwa Electronics"},

+	 { 1, 0xc4, "Transmeta"},

+	 { 1, 0x45, "Micron CMS"},

+// { 1, 0x46, "American Computer & Digital Components"},

+// { 1, 0xc7, "Enhance 3000"},

+	 { 1, 0xc8, "Tower Semiconductor"},

+// { 1, 0x49, "CPU Design"},

+// { 1, 0x4a, "Price Point"},

+	 { 1, 0xcb, "Maxim Integrated Product"},

+// { 1, 0x4c, "Tellabs"},

+// { 1, 0xcd, "Centaur Technology"},

+   { 1, 0xce, "Unigen"},

+	 { 1, 0x4f, "Transcend"},

+   { 1, 0xd0, "Memory Card"},

+// { 1, 0x51, "CKD"},

+// { 1, 0x52, "Capital Instruments"},

+// { 1, 0xd3, "Aica Kogyo"},

+// { 1, 0x54, "Linvex Technology"},

+   { 1, 0xd5, "MSC"},

+// { 1, 0xd6, "AKM Company"},

+// { 1, 0x57, "Dynamem"},

+// { 1, 0x58, "NERA ASA"},

+// { 1, 0xd9, "GSI Technology"},

+	 { 1, 0xda, "Dane-Elec"},

+// { 1, 0x5b, "Acorn Computers"},

+// { 1, 0xdc, "Lara Technology"},

+// { 1, 0x5d, "Oak Technology"},

+   { 1, 0x5e, "Itec Memory"},

+// { 1, 0xdf, "Tanisys Technology"},

+// { 1, 0xe0, "Truevision"},

+   { 1, 0x61, "Wintec"},

+// { 1, 0x62, "Super PC Memory"},

+// { 1, 0xe3, "MGV Memory"},

+// { 1, 0x64, "Galvantech"},

+// { 1, 0xe5, "Gadzoox Networks"},

+// { 1, 0xe6, "Multi Dimensional Cons."},

+// { 1, 0x67, "GateField"},

+	 { 1, 0x68, "Integrated Memory System"},

+// { 1, 0xe9, "Triscend"},

+// { 1, 0xea, "XaQti"},

+// { 1, 0x6b, "Goldenram"},

+// { 1, 0xec, "Clear Logic"},

+// { 1, 0x6d, "Cimaron Communications"},

+// { 1, 0x6e, "Nippon Steel Semi. Corp."},

+// { 1, 0xef, "Advantage Memory"},

+// { 1, 0x70, "AMCC"},

+	 { 1, 0xf1, "LeCroy"},

+// { 1, 0xf2, "Yamaha"},

+// { 1, 0x73, "Digital Microwave"},

+// { 1, 0xf4, "NetLogic Microsystems"},

+// { 1, 0x75, "MIMOS Semiconductor"},

+// { 1, 0x76, "Advanced Fibre"},

+// { 1, 0xf7, "BF Goodrich Data."},

+// { 1, 0xf8, "Epigram"},

+// { 1, 0x79, "Acbel Polytech"},

+	 { 1, 0x7a, "Apacer Technology"},

+// { 1, 0xfb, "Admor Memory"},

+	 { 1, 0x7c, "FOXCONN"},

+// { 1, 0xfd, "Quadratics Superconductor"},

+// { 1, 0xfe, "3COM"},

+// { 2, 0x01, "Camintonn"},

+// { 2, 0x02, "ISOA"},

+// { 2, 0x83, "Agate Semiconductor"},

+// { 2, 0x04, "ADMtek"},

+// { 2, 0x85, "HYPERTEC"},

+// { 2, 0x86, "Adhoc Technologies"},

+// { 2, 0x07, "MOSAID Technologies"},

+// { 2, 0x08, "Ardent Technologies"},

+// { 2, 0x89, "Switchcore"},

+// { 2, 0x8a, "Cisco Systems"},

+// { 2, 0x0b, "Allayer Technologies"},

+// { 2, 0x8c, "WorkX AG (Wichman)"},

+// { 2, 0x0d, "Oasis Semiconductor"},

+// { 2, 0x0e, "Novanet Semiconductor"},

+// { 2, 0x8f, "E-M Solutions"},

+// { 2, 0x10, "Power General"},

+// { 2, 0x91, "Advanced Hardware Arch."},

+// { 2, 0x92, "Inova Semiconductors"},

+// { 2, 0x13, "Telocity"},

+// { 2, 0x94, "Delkin Devices"},

+// { 2, 0x15, "Symagery Microsystems"},

+// { 2, 0x16, "C-Port"},

+// { 2, 0x97, "SiberCore Technologies"},

+// { 2, 0x98, "Southland Microsystems"},

+// { 2, 0x19, "Malleable Technologies"},

+// { 2, 0x1a, "Kendin Communications"},

+// { 2, 0x9b, "Great Technology Microcomputer"},

+// { 2, 0x1c, "Sanmina"},

+// { 2, 0x9d, "HADCO"},

+	 { 2, 0x9e, "Corsair"},

+// { 2, 0x1f, "Actrans System"},

+// { 2, 0x20, "ALPHA Technologies"},

+// { 2, 0xa1, "Silicon Laboratories (Cygnal)"},

+// { 2, 0xa2, "Artesyn Technologies"},

+// { 2, 0x23, "Align Manufacturing"},

+// { 2, 0xa4, "Peregrine Semiconductor"},

+// { 2, 0x25, "Chameleon Systems"},

+// { 2, 0x26, "Aplus Flash Technology"},

+   { 2, 0xa7, "MIPS Technologies"},

+// { 2, 0xa8, "Chrysalis ITS"},

+// { 2, 0x29, "ADTEC"},

+   { 2, 0x2a, "Kentron Technologies"},

+// { 2, 0xab, "Win Technologies"},

+// { 2, 0x2c, "Tachyon Semiconductor (ASIC)"},

+// { 2, 0xad, "Extreme Packet Devices"},

+// { 2, 0xae, "RF Micro Devices"},

+   { 2, 0x2f, "Siemens AG"},

+// { 2, 0xb0, "Sarnoff"},

+// { 2, 0x31, "Itautec SA"},

+// { 2, 0x32, "Radiata"},

+// { 2, 0xb3, "Benchmark Elect. (AVEX)"},

+// { 2, 0x34, "Legend"},

+   { 2, 0xb5, "SpecTek"},

+// { 2, 0xb6, "Hi/fn"},

+// { 2, 0x37, "Enikia"},

+// { 2, 0x38, "SwitchOn Networks"},

+// { 2, 0xb9, "AANetcom"},

+// { 2, 0xba, "Micro Memory Bank"},

+   { 2, 0x3b, "ESS Technology"},

+// { 2, 0xbc, "Virata"},

+// { 2, 0x3d, "Excess Bandwidth"},

+// { 2, 0x3e, "West Bay Semiconductor"},

+// { 2, 0xbf, "DSP Group"},

+// { 2, 0x40, "Newport Communications"},

+// { 2, 0xc1, "Chip2Chip"},

+// { 2, 0xc2, "Phobos"},

+// { 2, 0x43, "Intellitech"},

+// { 2, 0xc4, "Nordic VLSI ASA"},

+// { 2, 0x45, "Ishoni Networks"},

+// { 2, 0x46, "Silicon Spice"},

+// { 2, 0xc7, "Alchemy Semiconductor"},

+   { 2, 0xc8, "Agilent Technologies"},

+// { 2, 0x49, "Centillium Communications"},

+// { 2, 0x4a, "W.L. Gore"},

+// { 2, 0xcb, "HanBit Electronics"},

+// { 2, 0x4c, "GlobeSpan"},

+// { 2, 0xcd, "Element 14"},

+// { 2, 0xce, "Pycon"},

+// { 2, 0x4f, "Saifun Semiconductors"},

+// { 2, 0xd0, "Sibyte,"},

+// { 2, 0x51, "MetaLink Technologies"},

+// { 2, 0x52, "Feiya Technology"},

+// { 2, 0xd3, "I & C Technology"},

+// { 2, 0x54, "Shikatronics"},

+// { 2, 0xd5, "Elektrobit"},

+// { 2, 0xd6, "Megic"},

+// { 2, 0x57, "Com-Tier"},

+// { 2, 0x58, "Malaysia Micro Solutions"},

+// { 2, 0xd9, "Hyperchip"},

+// { 2, 0xda, "Gemstone Communications"},

+// { 2, 0x5b, "Anadigm (Anadyne)"},

+// { 2, 0xdc, "3ParData"},

+// { 2, 0x5d, "Mellanox Technologies"},

+// { 2, 0x5e, "Tenx Technologies"},

+// { 2, 0xdf, "Helix AG"},

+// { 2, 0xe0, "Domosys"},

+// { 2, 0x61, "Skyup Technology"},

+// { 2, 0x62, "HiNT"},

+// { 2, 0xe3, "Chiaro"},

+   { 2, 0x64, "MDT"},

+// { 2, 0xe5, "Exbit Technology A/S"},

+// { 2, 0xe6, "Integrated Technology Express"},

+// { 2, 0x67, "AVED Memory"},

+// { 2, 0x68, "Legerity"},

+// { 2, 0xe9, "Jasmine Networks"},

+// { 2, 0xea, "Caspian Networks"},

+// { 2, 0x6b, "nCUBE"},

+// { 2, 0xec, "Silicon Access Networks"},

+// { 2, 0x6d, "FDK"},

+// { 2, 0x6e, "High Bandwidth Access"},

+// { 2, 0xef, "MultiLink Technology"},

+// { 2, 0x70, "BRECIS"},

+// { 2, 0xf1, "World Wide Packets"},

+// { 2, 0xf2, "APW"},

+// { 2, 0x73, "Chicory Systems"},

+// { 2, 0xf4, "Xstream Logic"},

+// { 2, 0x75, "Fast-Chip"},

+// { 2, 0x76, "Zucotto Wireless"},

+// { 2, 0xf7, "Realchip"},

+// { 2, 0xf8, "Galaxy Power"},

+// { 2, 0x79, "eSilicon"},

+// { 2, 0x7a, "Morphics Technology"},

+// { 2, 0xfb, "Accelerant Networks"},

+// { 2, 0x7c, "Silicon Wave"},

+// { 2, 0xfd, "SandCraft"},

+   { 2, 0xfe, "Elpida"},

+// { 3, 0x01, "Solectron"},

+   { 3, 0x02, "Optosys Technologies"},

+   { 3, 0x83, "Buffalo"},

+// { 3, 0x04, "TriMedia Technologies"},

+// { 3, 0x85, "Cyan Technologies"},

+// { 3, 0x86, "Global Locate"},

+// { 3, 0x07, "Optillion"},

+// { 3, 0x08, "Terago Communications"},

+// { 3, 0x89, "Ikanos Communications"},

+   { 3, 0x8a, "Princeton"},

+   { 3, 0x0b, "Nanya"},

+// { 3, 0x8c, "Elite Flash Storage"},

+// { 3, 0x0d, "Mysticom"},

+// { 3, 0x0e, "LightSand Communications"},

+   { 3, 0x8f, "ATI"},

+// { 3, 0x10, "Agere Systems"},

+// { 3, 0x91, "NeoMagic"},

+// { 3, 0x92, "AuroraNetics"},

+// { 3, 0x13, "Golden Empire"},

+   { 3, 0x94, "Mushkin"},

+// { 3, 0x15, "Tioga Technologies"},

+   { 3, 0x16, "Netlist"},

+// { 3, 0x97, "TeraLogic"},

+// { 3, 0x98, "Cicada Semiconductor"},

+   { 3, 0x19, "Centon"},

+   { 3, 0x1a, "Tyco Electronics"},

+// { 3, 0x9b, "Magis Works"},

+// { 3, 0x1c, "Zettacom"},

+// { 3, 0x9d, "Cogency Semiconductor"},

+// { 3, 0x9e, "Chipcon AS"},

+// { 3, 0x1f, "Aspex Technology"},

+// { 3, 0x20, "F5 Networks"},

+// { 3, 0xa1, "Programmable Silicon Solutions"},

+// { 3, 0xa2, "ChipWrights"},

+// { 3, 0x23, "Acorn Networks"},

+// { 3, 0xa4, "Quicklogic"},

+   { 3, 0x25, "Kingmax"},

+// { 3, 0x26, "BOPS"},

+// { 3, 0xa7, "Flasys"},

+// { 3, 0xa8, "BitBlitz Communications"},

+   { 3, 0x29, "eMemory Technology"},

+// { 3, 0x2a, "Procket Networks"},

+// { 3, 0xab, "Purple Ray"},

+// { 3, 0x2c, "Trebia Networks"},

+// { 3, 0xad, "Delta Electronics"},

+// { 3, 0xae, "Onex Communications"},

+// { 3, 0x2f, "Ample Communications"},

+   { 3, 0xb0, "Memory Experts"},

+// { 3, 0x31, "Astute Networks"},

+// { 3, 0x32, "Azanda Network Devices"},

+// { 3, 0xb3, "Dibcom"},

+// { 3, 0x34, "Tekmos"},

+// { 3, 0xb5, "API NetWorks"},

+// { 3, 0xb6, "Bay Microsystems"},

+// { 3, 0x37, "Firecron"},

+// { 3, 0x38, "Resonext Communications"},

+// { 3, 0xb9, "Tachys Technologies"},

+// { 3, 0xba, "Equator Technology"},

+// { 3, 0x3b, "Concept Computer"},

+// { 3, 0xbc, "SILCOM"},

+// { 3, 0x3d, "3Dlabs"},

+// { 3, 0x3e, "c’t Magazine"},

+// { 3, 0xbf, "Sanera Systems"},

+// { 3, 0x40, "Silicon Packets"},

+   { 3, 0xc1, "Viasystems Group"},

+   { 3, 0xc2, "Simtek"},

+// { 3, 0x43, "Semicon Devices Singapore"},

+// { 3, 0xc4, "Satron Handelsges"},

+// { 3, 0x45, "Improv Systems"},

+// { 3, 0x46, "INDUSYS"},

+// { 3, 0xc7, "Corrent"},

+// { 3, 0xc8, "Infrant Technologies"},

+// { 3, 0x49, "Ritek Corp"},

+// { 3, 0x4a, "empowerTel Networks"},

+// { 3, 0xcb, "Hypertec"},

+// { 3, 0x4c, "Cavium Networks"},

+   { 3, 0xcd, "PLX Technology"},

+// { 3, 0xce, "Massana Design"},

+// { 3, 0x4f, "Intrinsity"},

+// { 3, 0xd0, "Valence Semiconductor"},

+// { 3, 0x51, "Terawave Communications"},

+// { 3, 0x52, "IceFyre Semiconductor"},

+// { 3, 0xd3, "Primarion"},

+// { 3, 0x54, "Picochip Designs"},

+// { 3, 0xd5, "Silverback Systems"},

+   { 3, 0xd6, "Jade Star"},

+// { 3, 0x57, "Pijnenburg Securealink"},

+   { 3, 0x58, "takeMS"},

+// { 3, 0xd9, "Cambridge Silicon Radio"},

+   { 3, 0xda, "Swissbit"},

+// { 3, 0x5b, "Nazomi Communications"},

+// { 3, 0xdc, "eWave System"},

+// { 3, 0x5d, "Rockwell Collins"},

+// { 3, 0x5e, "Picocel (Paion)"},

+// { 3, 0xdf, "Alphamosaic"},

+// { 3, 0xe0, "Sandburst"},

+// { 3, 0x61, "SiCon Video"},

+// { 3, 0x62, "NanoAmp Solutions"},

+// { 3, 0xe3, "Ericsson Technology"},

+// { 3, 0x64, "PrairieComm"},

+   { 3, 0xe5, "Mitac International"},

+// { 3, 0xe6, "Layer N Networks"},

+// { 3, 0x67, "MtekVision (Atsana)"},

+// { 3, 0x68, "Allegro Networks"},

+// { 3, 0xe9, "Marvell Semiconductors"},

+// { 3, 0xea, "Netergy Microelectronic"},

+   { 3, 0x6b, "nVidia"},

+// { 3, 0xec, "Internet Machines"},

+// { 3, 0x6d, "Peak Electronics"},

+// { 3, 0x6e, "Litchfield Communication"},

+   { 3, 0xef, "Accton"},

+// { 3, 0x70, "Teradiant Networks"},

+// { 3, 0xf1, "Scaleo Chip"},

+// { 3, 0xf2, "Cortina Systems"},

+   { 3, 0x73, "RAM Components"},

+// { 3, 0xf4, "Raqia Networks"},

+// { 3, 0x75, "ClearSpeed"},

+// { 3, 0x76, "Matsushita Battery"},

+// { 3, 0xf7, "Xelerated"},

+// { 3, 0xf8, "SimpleTech"},

+   { 3, 0x79, "Utron"},

+// { 3, 0x7a, "Astec International"},

+// { 3, 0xfb, "AVM"},

+// { 3, 0x7c, "Redux Communications"},

+// { 3, 0xfd, "Dot Hill Systems"},

+   { 3, 0xfe, "TeraChip"},

+   { 4, 0x01, "T-RAM"},

+// { 4, 0x02, "Innovics Wireless"},

+// { 4, 0x83, "Teknovus"},

+// { 4, 0x04, "KeyEye Communications"},

+// { 4, 0x85, "Runcom Technologies"},

+// { 4, 0x86, "RedSwitch"},

+// { 4, 0x07, "Dotcast"},

+   { 4, 0x08, "Silicon Mountain Memory"},

+// { 4, 0x89, "Signia Technologies"},

+// { 4, 0x8a, "Pixim"},

+// { 4, 0x0b, "Galazar Networks"},

+// { 4, 0x8c, "White Electronic Designs"},

+// { 4, 0x0d, "Patriot Scientific"},

+// { 4, 0x0e, "Neoaxiom"},

+// { 4, 0x8f, "3Y Power Technology"},

+   { 4, 0x10, "Scaleo Chip"},

+// { 4, 0x91, "Potentia Power Systems"},

+// { 4, 0x92, "C-guys"},

+// { 4, 0x13, "Digital Communications Technology"},

+// { 4, 0x94, "Silicon-Based Technology"},

+// { 4, 0x15, "Fulcrum Microsystems"},

+// { 4, 0x16, "Positivo Informatica"},

+// { 4, 0x97, "XIOtech"},

+// { 4, 0x98, "PortalPlayer"},

+// { 4, 0x19, "Zhiying Software"},

+// { 4, 0x1a, "ParkerVision"},

+// { 4, 0x9b, "Phonex Broadband"},

+// { 4, 0x1c, "Skyworks Solutions"},

+// { 4, 0x9d, "Entropic Communications"},

+// { 4, 0x9e, "Pacific Force Technology"},

+// { 4, 0x1f, "Zensys A/S"},

+// { 4, 0x20, "Legend Silicon Corp."},

+// { 4, 0xa1, "Sci-worx"},

+// { 4, 0xa2, "SMSC (Standard Microsystems)"},

+   { 4, 0x23, "Renesas"},

+// { 4, 0xa4, "Raza Microelectronics"},

+// { 4, 0x25, "Phyworks"},

+// { 4, 0x26, "MediaTek"},

+// { 4, 0xa7, "Non-cents Productions"},

+// { 4, 0xa8, "US Modular"},

+// { 4, 0x29, "Wintegra"},

+// { 4, 0x2a, "Mathstar"},

+// { 4, 0xab, "StarCore"},

+// { 4, 0x2c, "Oplus Technologies"},

+// { 4, 0xad, "Mindspeed"},

+// { 4, 0xae, "Just Young Computer"},

+// { 4, 0x2f, "Radia Communications"},

+   { 4, 0xb0, "OCZ"},

+// { 4, 0x31, "Emuzed"},

+// { 4, 0x32, "LOGIC Devices"},

+// { 4, 0xb3, "Inphi"},

+// { 4, 0x34, "Quake Technologies"},

+// { 4, 0xb5, "Vixel"},

+// { 4, 0xb6, "SolusTek"},

+// { 4, 0x37, "Kongsberg Maritime"},

+// { 4, 0x38, "Faraday Technology"},

+   { 4, 0xb9, "Altium"},

+// { 4, 0xba, "Insyte"},

+   { 4, 0x3b, "ARM"},

+// { 4, 0xbc, "DigiVision"},

+// { 4, 0x3d, "Vativ Technologies"},

+// { 4, 0x3e, "Endicott Interconnect Technologies"},

+   { 4, 0xbf, "Pericom"},

+// { 4, 0x40, "Bandspeed"},

+// { 4, 0xc1, "LeWiz Communications"},

+// { 4, 0xc2, "CPU Technology"},

+ 	 { 4, 0x43, "Ramaxel"},

+// { 4, 0xc4, "DSP Group"},

+// { 4, 0x45, "Axis Communications"},

+   { 4, 0x46, "Legacy Electronics"},

+// { 4, 0xc7, "Chrontel"},

+// { 4, 0xc8, "Powerchip Semiconductor"},

+// { 4, 0x49, "MobilEye Technologies"},

+   { 4, 0x4a, "Excel Semiconductor"},

+   { 4, 0xcb, "A-DATA"},

+// { 4, 0x4c, "VirtualDigm"},

+   { 4, 0xcd, "G.Skill"},

+// { 4, 0xce, "Quanta Computer"},

+// { 4, 0x4f, "Yield Microelectronics"},

+// { 4, 0xd0, "Afa Technologies"},

+   { 4, 0x51, "Kingbox"},

+// { 4, 0x52, "Ceva"},

+// { 4, 0xd3, "iStor Networks"},

+// { 4, 0x54, "Advance Modules"},

+   { 4, 0xd5, "Microsoft"},

+// { 4, 0xd6, "Open-Silicon"},

+// { 4, 0x57, "Goal Semiconductor"},

+// { 4, 0x58, "ARC International"},

+   { 4, 0xd9, "Simmtec"},

+// { 4, 0xda, "Metanoia"},

+// { 4, 0x5b, "Key Stream"},

+// { 4, 0xdc, "Lowrance Electronics"},

+// { 4, 0x5d, "Adimos"},

+// { 4, 0x5e, "SiGe Semiconductor"},

+// { 4, 0xdf, "Fodus Communications"},

+// { 4, 0xe0, "Credence Systems Corp."},

+// { 4, 0x61, "Genesis Microchip"},

+// { 4, 0x62, "Vihana"},

+// { 4, 0xe3, "WIS Technologies"},

+// { 4, 0x64, "GateChange Technologies"},

+// { 4, 0xe5, "High Density Devices AS"},

+// { 4, 0xe6, "Synopsys"},

+// { 4, 0x67, "Gigaram"},

+// { 4, 0x68, "Enigma Semiconductor"},

+// { 4, 0xe9, "Century Micro"},

+// { 4, 0xea, "Icera Semiconductor"},

+// { 4, 0x6b, "Mediaworks Integrated Systems"},

+// { 4, 0xec, "O’Neil Product Development"},

+// { 4, 0x6d, "Supreme Top Technology"},

+// { 4, 0x6e, "MicroDisplay"},

+   { 4, 0xef, "Team Group"},

+// { 4, 0x70, "Sinett"},

+   { 4, 0xf1, "Toshiba"},

+// { 4, 0xf2, "Tensilica"},

+// { 4, 0x73, "SiRF Technology"},

+// { 4, 0xf4, "Bacoc"},

+// { 4, 0x75, "SMaL Camera Technologies"},

+   { 4, 0x76, "Thomson SC"},

+// { 4, 0xf7, "Airgo Networks"},

+// { 4, 0xf8, "Wisair"},

+// { 4, 0x79, "SigmaTel"},

+// { 4, 0x7a, "Arkados"},

+// { 4, 0xfb, "Compete IT KG"},

+// { 4, 0x7c, "Eudar Technology"},

+// { 4, 0xfd, "Focus Enhancements"},

+// { 4, 0xfe, "Xyratex"},

+// { 5, 0x01, "Specular Networks"},

+   { 5, 0x02, "Patriot Memory"},

+// { 5, 0x83, "U-Chip Technology Corp."},

+// { 5, 0x04, "Silicon Optix"},

+// { 5, 0x85, "Greenfield Networks"},

+   { 5, 0x86, "CompuRAM"},

+// { 5, 0x07, "Stargen"},

+// { 5, 0x08, "NetCell"},

+// { 5, 0x89, "Excalibrus Technologies"},

+// { 5, 0x8a, "SCM Microsystems"},

+// { 5, 0x0b, "Xsigo Systems"},

+// { 5, 0x8c, "CHIPS & Systems"},

+// { 5, 0x0d, "Tier"},

+// { 5, 0x0e, "CWRL Labs"},

+// { 5, 0x8f, "Teradici"},

+// { 5, 0x10, "Gigaram"},

+// { 5, 0x91, "g2 Microsystems"},

+// { 5, 0x92, "PowerFlash Semiconductor"},

+// { 5, 0x13, "P.A. Semi"},

+   { 5, 0x94, "NovaTech"},

+// { 5, 0x15, "c2 Microsystems"},

+// { 5, 0x16, "Level5 Networks"},

+   { 5, 0x97, "COS Memory"},

+// { 5, 0x98, "Innovasic Semiconductor"},

+// { 5, 0x19, "02IC"},

+// { 5, 0x1a, "Tabula"},

+   { 5, 0x9b, "Crucial"},

+// { 5, 0x1c, "Chelsio Communications"},

+// { 5, 0x9d, "Solarflare Communications"},

+// { 5, 0x9e, "Xambala"},

+// { 5, 0x1f, "EADS Astrium"},

+// { 5, 0x20, "Terra Semiconductor"},

+// { 5, 0xa1, "Imaging Works"},

+// { 5, 0xa2, "Astute Networks"},

+// { 5, 0x23, "Tzero"},

+// { 5, 0xa4, "Emulex"},

+// { 5, 0x25, "Power-One"},

+// { 5, 0x26, "Pulse~LINK"},

+// { 5, 0xa7, "Hon Hai Precision Industry"},

+// { 5, 0xa8, "White Rock Networks"},

+// { 5, 0x29, "Telegent Systems USA"},

+// { 5, 0x2a, "Atrua Technologies"},

+// { 5, 0xab, "Acbel Polytech"},

+// { 5, 0x2c, "eRide"},

+   { 5, 0xad, "ULi"},

+// { 5, 0xae, "Magnum Semiconductor"},

+// { 5, 0x2f, "neoOne Technology"},

+// { 5, 0xb0, "Connex Technology"},

+// { 5, 0x31, "Stream Processors"},

+// { 5, 0x32, "Focus Enhancements"},

+// { 5, 0xb3, "Telecis Wireless"},

+// { 5, 0x34, "uNav Microelectronics"},

+// { 5, 0xb5, "Tarari"},

+// { 5, 0xb6, "Ambric"},

+// { 5, 0x37, "Newport Media"},

+// { 5, 0x38, "VMTS"},

+// { 5, 0xb9, "Enuclia Semiconductor"},

+// { 5, 0xba, "Virtium Technology"},

+// { 5, 0x3b, "Solid State System"},

+// { 5, 0xbc, "Kian Tech LLC"},

+// { 5, 0x3d, "Artimi"},

+   { 5, 0x3e, "PQI"},

+// { 5, 0xbf, "Avago Technologies"},

+// { 5, 0x40, "ADTechnology"},

+   { 5, 0xc1, "Sigma Designs"},

+// { 5, 0xc2, "SiCortex"},

+// { 5, 0x43, "Ventura Technology Group"},

+// { 5, 0xc4, "eASIC"},

+// { 5, 0x45, "M.H.S. SAS"},

+   { 5, 0x46, "MSI"},

+// { 5, 0xc7, "Rapport"},

+// { 5, 0xc8, "Makway International"},

+// { 5, 0x49, "Broad Reach Engineering"},

+// { 5, 0x4a, "Semiconductor Mfg Intl Corp"},

+// { 5, 0xcb, "SiConnect"},

+// { 5, 0x4c, "FCI USA"},

+// { 5, 0xcd, "Validity Sensors"},

+// { 5, 0xce, "Coney Technology"},

+// { 5, 0x4f, "Spans Logic"},

+// { 5, 0xd0, "Neterion"},

+   { 5, 0x51, "Qimonda"},

+// { 5, 0x52, "New Japan Radio"},

+// { 5, 0xd3, "Velogix"},

+// { 5, 0x54, "Montalvo Systems"},

+// { 5, 0xd5, "iVivity"},

+   { 5, 0xd6, "Walton Chaintech"},

+   { 5, 0x57, "AENEON"},

+// { 5, 0x58, "Lorom Industrial"},

+// { 5, 0xd9, "Radiospire Networks"},

+// { 5, 0xda, "Sensio Technologies"},

+// { 5, 0x5b, "Nethra Imaging"},

+   { 5, 0xdc, "Hexon"},

+// { 5, 0x5d, "CompuStocx (CSX)"},

+// { 5, 0x5e, "Methode Electronics"},

+// { 5, 0xdf, "Connect One"},

+// { 5, 0xe0, "Opulan Technologies"},

+// { 5, 0x61, "Septentrio NV"},

+   { 5, 0x62, "Goldenmars"},

+   { 5, 0xe3, "Kreton"},

+// { 5, 0x64, "Cochlear"},

+// { 5, 0xe5, "Altair Semiconductor"},

+// { 5, 0xe6, "NetEffect"},

+// { 5, 0x67, "Spansion"},

+// { 5, 0x68, "Taiwan Semiconductor Mfg"},

+// { 5, 0xe9, "Emphany Systems"},

+// { 5, 0xea, "ApaceWave Technologies"},

+// { 5, 0x6b, "Mobilygen"},

+// { 5, 0xec, "Tego"},

+// { 5, 0x6d, "Cswitch"},

+// { 5, 0x6e, "Haier (Beijing) IC Design"},

+// { 5, 0xef, "MetaRAM"},

+// { 5, 0x70, "Axel Electronics"},

+// { 5, 0xf1, "Tilera"},

+// { 5, 0xf2, "Aquantia"},

+// { 5, 0x73, "Vivace Semiconductor"},

+// { 5, 0xf4, "Redpine Signals"},

+// { 5, 0x75, "Octalica"},

+// { 5, 0x76, "InterDigital Communications"},

+   { 5, 0xf7, "Avant Technology"},

+   { 5, 0xf8, "Asrock"},

+// { 5, 0x79, "Availink"},

+// { 5, 0x7a, "Quartics"},

+// { 5, 0xfb, "Element CXI"},

+// { 5, 0x7c, "Innovaciones Microelectronicas"},

+// { 5, 0xfd, "VeriSilicon Microelectronics"},

+// { 5, 0xfe, "W5 Networks"},

+// { 6, 0x01, "MOVEKING"},

+// { 6, 0x02, "Mavrix Technology"},

+// { 6, 0x83, "CellGuide"},

+// { 6, 0x04, "Faraday Technology"},

+// { 6, 0x85, "Diablo Technologies"},

+// { 6, 0x86, "Jennic"},

+// { 6, 0x07, "Octasic"},

+   { 6, 0x08, "Molex"},

+// { 6, 0x89, "3Leaf Networks"},

+// { 6, 0x8a, "Bright Micron Technology"},

+// { 6, 0x0b, "Netxen"},

+// { 6, 0x8c, "NextWave Broadband"},

+// { 6, 0x0d, "DisplayLink"},

+// { 6, 0x0e, "ZMOS Technology"},

+// { 6, 0x8f, "Tec-Hill"},

+// { 6, 0x10, "Multigig"},

+// { 6, 0x91, "Amimon"},

+// { 6, 0x92, "Euphonic Technologies"},

+// { 6, 0x13, "BRN Phoenix"},

+// { 6, 0x94, "InSilica"},

+// { 6, 0x15, "Ember"},

+   { 6, 0x16, "Avexir"},

+// { 6, 0x97, "Echelon"},

+// { 6, 0x98, "Edgewater Computer Systems"},

+// { 6, 0x19, "XMOS Semiconductor"},

+// { 6, 0x1a, "GENUSION"},

+   { 6, 0x9b, "Memory Corp NV"},

+// { 6, 0x1c, "SiliconBlue Technologies"},

+// { 6, 0x9d, "Rambus"},

+// { 6, 0x9e, "Andes Technology"},

+// { 6, 0x1f, "Coronis Systems"},

+// { 6, 0x20, "Achronix Semiconductor"},

+// { 6, 0xa1, "Siano Mobile Silicon"},

+// { 6, 0xa2, "Semtech"},

+// { 6, 0x23, "Pixelworks"},

+// { 6, 0xa4, "Gaisler Research AB"},

+// { 6, 0x25, "Teranetics"},

+// { 6, 0x26, "Toppan Printing"},

+// { 6, 0xa7, "Kingxcon"},

+   { 6, 0xa8, "SiS"},

+// { 6, 0x29, "I-O Data Device"},

+// { 6, 0x2a, "NDS Americas"},

+// { 6, 0xab, "Solomon Systech Limited"},

+// { 6, 0x2c, "On Demand Microelectronics"},

+// { 6, 0xad, "Amicus Wireless"},

+// { 6, 0xae, "SMARDTV SNC"},

+// { 6, 0x2f, "Comsys Communication"},

+// { 6, 0xb0, "Movidia"},

+// { 6, 0x31, "Javad GNSS"},

+// { 6, 0x32, "Montage Technology Group"},

+   { 6, 0xb3, "Trident"},

+   { 6, 0x34, "Super Talent"},

+// { 6, 0xb5, "Optichron"},

+// { 6, 0xb6, "Future Waves UK"},

+// { 6, 0x37, "SiBEAM"},

+// { 6, 0x38, "Inicore,"},

+// { 6, 0xb9, "Virident Systems"},

+// { 6, 0xba, "M2000"},

+// { 6, 0x3b, "ZeroG Wireless"},

+   { 6, 0xbc, "Gingle"},

+// { 6, 0x3d, "Space Micro"},

+// { 6, 0x3e, "Wilocity"},

+// { 6, 0xbf, "Novafora, Ic."},

+// { 6, 0x40, "iKoa"},

+   { 6, 0xc1, "ASint"},

+   { 6, 0xc2, "Ramtron"},

+// { 6, 0x43, "Plato Networks"},

+// { 6, 0xc4, "IPtronics AS"},

+// { 6, 0x45, "Infinite-Memories"},

+// { 6, 0x46, "Parade Technologies"},

+// { 6, 0xc7, "Dune Networks"},

+// { 6, 0xc8, "GigaDevice Semiconductor"},

+// { 6, 0x49, "Modu"},

+// { 6, 0x4a, "CEITEC"},

+// { 6, 0xcb, "Northrop Grumman"},

+// { 6, 0x4c, "XRONET"},

+// { 6, 0xcd, "Sicon Semiconductor AB"},

+// { 6, 0xce, "Atla Electronics"},

+   { 6, 0x4f, "TOPRAM"},

+// { 6, 0xd0, "Silego Technology"},

+   { 6, 0x51, "Kinglife"},

+// { 6, 0x52, "Ability Industries"},

+// { 6, 0xd3, "Silicon Power Computer & Communications"},

+// { 6, 0x54, "Augusta Technology"},

+// { 6, 0xd5, "Nantronics Semiconductors"},

+// { 6, 0xd6, "Hilscher Gesellschaft"},

+// { 6, 0x57, "Quixant"},

+// { 6, 0x58, "Percello"},

+// { 6, 0xd9, "NextIO"},

+// { 6, 0xda, "Scanimetrics"},

+// { 6, 0x5b, "FS-Semi Company"},

+// { 6, 0xdc, "Infinera"},

+   { 6, 0x5d, "SandForce"},

+   { 6, 0x5e, "Lexar Media"},

+// { 6, 0xdf, "Teradyne"},

+   { 6, 0xe0, "Memory Exchange Corp."},

+// { 6, 0x61, "Suzhou Smartek Electronics"},

+   { 6, 0x62, "Avantium"},

+// { 6, 0xe3, "ATP Electronics"},

+// { 6, 0x64, "Valens Semiconductor"},

+// { 6, 0xe5, "Agate Logic"},

+// { 6, 0xe6, "Netronome"},

+// { 6, 0x67, "Zenverge"},

+// { 6, 0x68, "N-trig"},

+// { 6, 0xe9, "SanMax Technologies"},

+// { 6, 0xea, "Contour Semiconductor"},

+   { 6, 0x6b, "TwinMOS"},

+   { 6, 0xec, "Silicon Systems"},

+// { 6, 0x6d, "V-Color Technology"},

+// { 6, 0x6e, "Certicom"},

+// { 6, 0xef, "JSC ICC Milandr"},

+// { 6, 0x70, "PhotoFast Global"},

+   { 6, 0xf1, "InnoDisk"},

+   { 6, 0xf2, "Muscle Power"},

+// { 6, 0x73, "Energy Micro"},

+// { 6, 0xf4, "Innofidei"},

+// { 6, 0x75, "CopperGate Communications"},

+// { 6, 0x76, "Holtek Semiconductor"},

+// { 6, 0xf7, "Myson Century"},

+// { 6, 0xf8, "FIDELIX"},

+// { 6, 0x79, "Red Digital Cinema"},

+// { 6, 0x7a, "Densbits Technology"},

+// { 6, 0xfb, "Zempro"},

+// { 6, 0x7c, "MoSys"},

+// { 6, 0xfd, "Provigent"},

+// { 6, 0xfe, "Triad Semiconductor"},

+// { 8, 0x01, "Siklu Communication"},

+// { 8, 0x02, "A Force Manufacturing"},

+   { 8, 0x83, "Strontium"},

+// { 8, 0x04, "Abilis Systems"},

+// { 8, 0x85, "Siglead"},

+// { 8, 0x86, "Ubicom"},

+// { 8, 0x07, "Unifosa"},

+// { 8, 0x08, "Stretch"},

+// { 8, 0x89, "Lantiq Deutschland"},

+// { 8, 0x8a, "Visipro."},

+   { 8, 0x0b, "EKMemory"},

+// { 8, 0x8c, "Microelectronics Institute ZTE"},

+// { 8, 0x0d, "Cognovo"},

+// { 8, 0x0e, "Carry Technology"},

+   { 8, 0x8f, "Nokia"},

+   { 8, 0x10, "King Tiger"},

+// { 8, 0x91, "Sierra Wireless"},

+   { 8, 0x92, "HT Micron"},

+   { 8, 0x13, "Albatron"},

+// { 8, 0x94, "Leica Geosystems AG"},

+// { 8, 0x15, "BroadLight"},

+// { 8, 0x16, "AEXEA"},

+// { 8, 0x97, "ClariPhy Communications"},

+// { 8, 0x98, "Green Plug"},

+// { 8, 0x19, "Design Art Networks"},

+// { 8, 0x1a, "Mach Xtreme Technology"},

+// { 8, 0x9b, "ATO Solutions"},

+// { 8, 0x1c, "Ramsta"},

+// { 8, 0x9d, "Greenliant Systems"},

+// { 8, 0x9e, "Teikon"},

+// { 8, 0x1f, "Antec Hadron"},

+// { 8, 0x20, "NavCom Technology"},

+// { 8, 0xa1, "Shanghai Fudan Microelectronics"},

+// { 8, 0xa2, "Calxeda"},

+// { 8, 0x23, "JSC EDC Electronics"},

+// { 8, 0xa4, "Kandit Technology"},

+// { 8, 0x25, "Ramos Technology"},

+// { 8, 0x26, "Goldenmars Technology"},

+// { 8, 0xa7, "XeL Technology"},

+// { 8, 0xa8, "Newzone"},

+   { 8, 0x29, "MercyPower"},

+// { 8, 0x2a, "Nanjing Yihuo Technology."},

+// { 8, 0xab, "Nethra Imaging"},

+// { 8, 0x2c, "SiTel Semiconductor BV"},

+// { 8, 0xad, "SolidGear"},

+   { 8, 0xae, "Topower"},

+// { 8, 0x2f, "Wilocity"},

+// { 8, 0xb0, "Profichip"},

+// { 8, 0x31, "Gerad Technologies"},

+   { 8, 0x32, "Ritek"},

+// { 8, 0xb3, "Gomos Technology Limited"},

+   { 8, 0x34, "Memoright"},

+// { 8, 0xb5, "D-Broad"},

+// { 8, 0xb6, "HiSilicon Technologies"},

+// { 8, 0x37, "Syndiant ."},

+// { 8, 0x38, "Enverv"},

+// { 8, 0xb9, "Cognex"},

+// { 8, 0xba, "Xinnova Technology"},

+   { 8, 0x3b, "Ultron"},

+// { 8, 0xbc, "Concord Idea"},

+// { 8, 0x3d, "AIM"},

+// { 8, 0x3e, "Lifetime Memory Products"},

+// { 8, 0xbf, "Ramsway"},

+// { 8, 0x40, "Recore Systems B.V."},

+// { 8, 0xc1, "Haotian Jinshibo Science Tech"},

+// { 8, 0xc2, "Being Advanced Memory"},

+// { 8, 0x43, "Adesto Technologies"},

+// { 8, 0xc4, "Giantec Semiconductor"},

+// { 8, 0x45, "HMD Electronics AG"},

+// { 8, 0x46, "Gloway International (HK)"},

+// { 8, 0xc7, "Kingcore"},

+// { 8, 0xc8, "Anucell Technology Holding"},

+// { 8, 0x49, "Accord Software & Systems Pvt."},

+// { 8, 0x4a, "Active-Semi"},

+// { 8, 0xcb, "Denso"},

+// { 8, 0x4c, "TLSI"},

+// { 8, 0xcd, "Shenzhen Daling Electronic"},

+// { 8, 0xce, "Mustang"},

+// { 8, 0x4f, "Orca Systems"},

+// { 8, 0xd0, "Passif Semiconductor"},

+// { 8, 0x51, "GigaDevice Semiconductor (Beijing)"},

+// { 8, 0x52, "Memphis Electronic"},

+// { 8, 0xd3, "Beckhoff Automation"},

+// { 8, 0x54, "Harmony Semiconductor Corp"},

+// { 8, 0xd5, "Air Computers SRL"},

+   { 8, 0xd6, "TMT Memory"},

+	 { 9, 0xff, ""}

+};

+

diff --git a/lib.c b/lib.c
new file mode 100644
index 0000000..a2b829d
--- /dev/null
+++ b/lib.c
@@ -0,0 +1,1199 @@
+/* lib.c - MemTest-86  Version 3.4
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ */
+#include "io.h"
+#include "serial.h"
+#include "test.h"
+#include "config.h"
+#include "screen_buffer.h"
+#include "stdint.h"
+#include "cpuid.h"
+#include "smp.h"
+
+
+int slock = 0, lsr = 0;
+short serial_cons = SERIAL_CONSOLE_DEFAULT;
+#if SERIAL_TTY != 0 && SERIAL_TTY != 1
+#error Bad SERIAL_TTY. Only ttyS0 and ttyS1 are supported.
+#endif
+short serial_tty = SERIAL_TTY;
+const short serial_base_ports[] = {0x3f8, 0x2f8};
+
+#if ((115200%SERIAL_BAUD_RATE) != 0)
+#error Bad default baud rate
+#endif
+int serial_baud_rate = SERIAL_BAUD_RATE;
+unsigned char serial_parity = 0;
+unsigned char serial_bits = 8;
+
+struct ascii_map_str {
+        int ascii;
+        int keycode;
+};
+
+inline void reboot(void)
+{
+	
+	/* tell the BIOS to do a cold start */
+	*((unsigned short *)0x472) = 0x0;
+	
+	while(1)
+	{
+		outb(0xFE, 0x64);
+		outb(0x02, 0xcf9); /* reset that doesn't rely on the keyboard controller */
+		outb(0x04, 0xcf9);
+		outb(0x0E, 0xcf9);
+	}
+}
+
+int strlen(char * string){
+	int i=0;
+	while(*string++){i++;};
+	return i;
+}
+
+int strstr(char *haystack, char * needle)
+{
+	int i=0,j=0;
+	int here=0;
+	while(1){
+		if(needle[i]==haystack[j])
+		{
+			if(here==0)
+				here=j;
+				i++;j++;
+				if(i>=strlen(needle))
+				{
+					return here;
+				}
+				if(j>=strlen(haystack))
+				{
+					return -1;
+				}
+		} else {
+			j++;i=0;here=0;
+		}
+	}
+}
+
+int memcmp(const void *s1, const void *s2, ulong count)
+{
+	const unsigned char *src1 = s1, *src2 = s2;
+	int i;
+	for(i = 0; i < count; i++) {
+		if (src1[i] != src2[i]) {
+			return (int)src1[i] - (int)src2[i];
+		}
+	}
+	return 0;
+}
+
+int strncmp(const char *s1, const char *s2, ulong n) {
+	signed char res = 0;
+	while (n) {
+		res = *s1 - *s2;
+		if (res != 0)
+			return res;
+		if (*s1 == '\0')
+			return 0;
+		++s1, ++s2;
+		--n;
+	}
+	return res;
+}
+
+void *memmove(void *dest, const void *src, ulong n)
+{
+	long i;
+	char *d = (char *)dest, *s = (char *)src;
+
+	/* If src == dest do nothing */
+	if (dest < src) {
+		for(i = 0; i < n; i++) {
+			d[i] = s[i];
+		}
+	}
+	else if (dest > src) {
+		for(i = n -1; i >= 0; i--) {
+			d[i] = s[i];
+		}
+	}
+	return dest;
+}
+
+char toupper(char c)
+{
+	if (c >= 'a' && c <= 'z')
+		return c + 'A' -'a';
+	else
+		return c;
+}
+
+int isdigit(char c)
+{
+	return c >= '0' && c <= '9';
+}
+
+int isxdigit(char c)
+{
+	return isdigit(c) || (toupper(c) >= 'A' && toupper(c) <= 'F'); }
+
+unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base) {
+	unsigned long result = 0, value;
+
+	if (!base) {
+		base = 10;
+		if (*cp == '0') {
+			base = 8;
+			cp++;
+			if (toupper(*cp) == 'X' && isxdigit(cp[1])) {
+				cp++;
+				base = 16;
+			}
+		}
+	} else if (base == 16) {
+		if (cp[0] == '0' && toupper(cp[1]) == 'X')
+			cp += 2;
+	}
+	while (isxdigit(*cp) &&
+		(value = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10) < base) {
+		result = result*base + value;
+		cp++;
+	}
+	if (endp)
+		*endp = (char *)cp;
+	return result;
+}
+
+/*
+ * Scroll the error message area of the screen as needed
+ * Starts at line LINE_SCROLL and ends at line 23
+ */
+void scroll(void) 
+{
+	int i, j;
+	char *s, tmp;
+
+	/* Only scroll if at the bottom of the screen */
+	if (v->msg_line < 23) {
+		v->msg_line++;
+	} else {
+		/* If scroll lock is on, loop till it is cleared */
+		while (slock) {
+			check_input();
+		}
+	        for (i=LINE_SCROLL; i<23; i++) {
+			s = (char *)(SCREEN_ADR + ((i+1) * 160));
+			for (j=0; j<160; j+=2, s+=2) {
+				*(s-160) = *s;
+                                tmp = get_scrn_buf(i+1, j/2);
+                                set_scrn_buf(i, j/2, tmp);
+			}
+		}
+		/* Clear the newly opened line */
+		s = (char *)(SCREEN_ADR + (23 * 160));
+		for (j=0; j<80; j++) {
+			*s = ' ';
+                        set_scrn_buf(23, j, ' ');
+			s += 2;
+		}
+                tty_print_region(LINE_SCROLL, 0, 23, 79);
+        }
+}
+
+/*
+ * Clear scroll region
+ */
+void clear_scroll(void)
+{
+	int i;
+	char *s;
+
+	s = (char*)(SCREEN_ADR+LINE_HEADER*160);
+        for(i=0; i<80*(24-LINE_HEADER); i++) {
+                *s++ = ' ';
+                *s++ = 0x17;
+        }
+}
+
+/*
+ * Place a single character on screen
+ */
+void cplace(int y, int x, const char c)
+{
+	char *dptr;
+
+	dptr = (char *)(SCREEN_ADR + (160*y) + (2*x));
+	*dptr = c;
+}
+
+/*
+ * Print characters on screen
+ */
+void cprint(int y, int x, const char *text)
+{
+	register int i;
+	char *dptr;
+
+	dptr = (char *)(SCREEN_ADR + (160*y) + (2*x));
+	for (i=0; text[i]; i++) {
+		*dptr = text[i];
+		dptr += 2;
+        }
+        tty_print_line(y, x, text);
+}
+
+void itoa(char s[], int n) 
+{
+  int i, sign;
+
+  if((sign = n) < 0)
+    n = -n;
+  i=0;
+  do {
+    s[i++] = n % 10 + '0';
+  } while ((n /= 10) > 0);
+  if(sign < 0)
+    s[i++] = '-';
+  s[i] = '\0';
+  reverse(s);
+}
+
+void reverse(char s[])
+{
+  int c, i, j;
+  for(j = 0; s[j] != 0; j++)
+	  ;
+
+  for(i=0, j = j - 1; i < j; i++, j--) {
+    c = s[i];
+    s[i] = s[j];
+    s[j] = c;
+  }
+}
+void memcpy (void *dst, void *src, int len)
+{
+	char *s = (char*)src;
+	char *d = (char*)dst;
+	int i;
+
+	if (len <= 0) {
+		return;
+	}
+	for (i = 0 ; i < len; i++) {
+		*d++ = *s++;
+	} 
+}
+
+/*
+ * Print a people friendly address
+ */
+void aprint(int y, int x, ulong page)
+{
+	/* page is in multiples of 4K */
+	if ((page << 2) < 9999) {
+		dprint(y, x, page << 2, 4, 0);
+		cprint(y, x+4, "K");
+	}
+	else if ((page >>8) < 9999) {
+		dprint(y, x, (page  + (1 << 7)) >> 8, 4, 0);
+		cprint(y, x+4, "M");
+	}
+	else if ((page >>18) < 9999) {
+		dprint(y, x, (page + (1 << 17)) >> 18, 4, 0);
+		cprint(y, x+4, "G");
+	}
+	else {
+		dprint(y, x, (page + (1 << 27)) >> 28, 4, 0);
+		cprint(y, x+4, "T");
+	}
+}
+
+/*
+ * Print a decimal number on screen
+ */
+void dprint(int y, int x, ulong val, int len, int right)
+{
+	ulong j, k;
+	int i, flag=0;
+	char buf[18];
+
+	if (val > 999999999 || len > 9) {
+		return;
+	}
+	for(i=0, j=1; i<len-1; i++) {
+		j *= 10;
+	}
+	if (!right) {
+		for (i=0; j>0; j/=10) {
+			k = val/j;
+			if (k > 9) {
+				j *= 100;
+				continue;
+			}
+			if (flag || k || j == 1) {
+				buf[i++] = k + '0';
+				flag++;
+			} else {
+				buf[i++] = ' ';
+			}
+			val -= k * j;
+		}
+	} else {
+		for(i=0; i<len; j/=10) {
+			if (j) {
+				k = val/j;
+					if (k > 9) {
+					j *= 100;
+					len++;
+					continue;
+				}
+				if (k == 0 && flag == 0) {
+					continue;				
+				}
+				buf[i++] = k + '0';
+				val -= k * j;
+			} else {
+                                if (flag == 0 &&  i < len-1) {
+                                        buf[i++] = '0';
+                                } else {
+                                        buf[i++] = ' ';
+                                }
+			}
+			flag++;
+		}
+	}
+	buf[i] = 0;
+	cprint(y,x,buf);
+}
+
+/*
+ * Print a hex number on screen at least digits long
+ */
+void hprint2(int y,int x, unsigned long val, int digits)
+{
+	unsigned long j;
+	int i, idx, flag = 0;
+	char buf[18];
+
+        for (i=0, idx=0; i<8; i++) {
+                j = val >> (28 - (4 * i));
+		j &= 0xf;
+		if (j < 10) {
+			if (flag || j || i == 7) {
+		                buf[idx++] = j + '0';
+				flag++;
+			} else {
+				buf[idx++] = '0';
+			}
+		} else {
+			buf[idx++] = j + 'a' - 10;
+			flag++;
+		}
+        }
+	if (digits > 8) {
+		digits = 8;
+	}
+	if (flag > digits) {
+		digits = flag;
+	}
+        buf[idx] = 0;
+	cprint(y,x,buf + (idx - digits));
+}
+
+/*
+ * Print a hex number on screen exactly digits long
+ */
+void hprint3(int y,int x, unsigned long val, int digits)
+{
+	unsigned long j;
+	int i, idx, flag = 0;
+	char buf[18];
+
+	for (i=0, idx=0; i<digits; i++) {
+		j = 0xf & val;
+		val /= 16;
+
+		if (j < 10) {
+			if (flag || j || i == 7) {
+				buf[digits - ++idx] = j + '0';
+				flag++;
+			} else {
+				buf[digits - ++idx] = '0';
+			}
+		} else {
+			buf[digits - ++idx] = j + 'a' - 10;
+			flag++;
+		}
+	}
+	buf[idx] = 0;
+	cprint(y,x,buf);
+}
+
+/*
+ * Print a hex number on screen
+ */
+void hprint(int y, int x, unsigned long val)
+{
+	return hprint2(y, x, val, 8);
+}
+
+/*
+ * Print an address in 0000m0000k0000 notation
+ */
+void xprint(int y,int x, ulong val)
+{
+        ulong j;
+
+	j = (val & 0xffc00000) >> 20;
+	dprint(y, x, j, 4, 0);
+	cprint(y, x+4, "m");
+	j = (val & 0xffc00) >> 10;
+	dprint(y, x+5, j, 4, 0);
+	cprint(y, x+9, "k");
+	j = val & 0x3ff;
+	dprint(y, x+10, j, 4, 0);
+}
+
+char *codes[] = {
+	"  Divide",
+	"   Debug",
+	"     NMI",
+	"  Brkpnt",
+	"Overflow",
+	"   Bound",
+	"  Inv_Op",
+	" No_Math",
+	"Double_Fault",
+	"Seg_Over",
+	" Inv_TSS",
+	"  Seg_NP",
+	"Stack_Fault",
+	"Gen_Prot",
+	"Page_Fault",
+	"   Resvd",
+	"     FPE",
+	"Alignment",
+	" Mch_Chk",
+	"SIMD FPE"
+};
+
+struct eregs {
+	ulong ss;
+	ulong ds;
+	ulong esp;
+	ulong ebp;
+	ulong esi;
+	ulong edi;
+	ulong edx;
+	ulong ecx;
+	ulong ebx;
+	ulong eax;
+	ulong vect;
+	ulong code;
+	ulong eip;
+	ulong cs;
+	ulong eflag;
+};
+	
+/* Handle an interrupt */
+void inter(struct eregs *trap_regs)
+{
+	int i, line;
+	unsigned char *pp;
+	ulong address = 0;
+	int my_cpu_num = smp_my_cpu_num();
+
+	/* Get the page fault address */
+	if (trap_regs->vect == 14) {
+		__asm__("movl %%cr2,%0":"=r" (address));
+	}
+#ifdef PARITY_MEM
+
+	/* Check for a parity error */
+	if (trap_regs->vect == 2) {
+		parity_err(trap_regs->edi, trap_regs->esi);
+		return;
+	}
+#endif
+
+	/* clear scrolling region */
+        pp=(unsigned char *)(SCREEN_ADR+(2*80*(LINE_SCROLL-2)));
+        for(i=0; i<2*80*(24-LINE_SCROLL-2); i++, pp+=2) {
+                *pp = ' ';
+        }
+	line = LINE_SCROLL-2;
+
+	cprint(line, 0, "Unexpected Interrupt - Halting CPU");
+	dprint(line, COL_MID + 4, my_cpu_num, 2, 1);
+	cprint(line+2, 0, " Type: ");
+	if (trap_regs->vect <= 19) {
+		cprint(line+2, 7, codes[trap_regs->vect]);
+	} else {
+		hprint(line+2, 7, trap_regs->vect);
+	}
+	cprint(line+3, 0, "   PC: ");
+	hprint(line+3, 7, trap_regs->eip);
+	cprint(line+4, 0, "   CS: ");
+	hprint(line+4, 7, trap_regs->cs);
+	cprint(line+5, 0, "Eflag: ");
+	hprint(line+5, 7, trap_regs->eflag);
+	cprint(line+6, 0, " Code: ");
+	hprint(line+6, 7, trap_regs->code);
+	cprint(line+7, 0, "   DS: ");
+	hprint(line+7, 7, trap_regs->ds);
+	cprint(line+8, 0, "   SS: ");
+	hprint(line+8, 7, trap_regs->ss);
+	if (trap_regs->vect == 14) {
+		/* Page fault address */
+		cprint(line+7, 0, " Addr: ");
+		hprint(line+7, 7, address);
+	}
+
+	cprint(line+2, 20, "eax: ");
+	hprint(line+2, 25, trap_regs->eax);
+	cprint(line+3, 20, "ebx: ");
+	hprint(line+3, 25, trap_regs->ebx);
+	cprint(line+4, 20, "ecx: ");
+	hprint(line+4, 25, trap_regs->ecx);
+	cprint(line+5, 20, "edx: ");
+	hprint(line+5, 25, trap_regs->edx);
+	cprint(line+6, 20, "edi: ");
+	hprint(line+6, 25, trap_regs->edi);
+	cprint(line+7, 20, "esi: ");
+	hprint(line+7, 25, trap_regs->esi);
+	cprint(line+8, 20, "ebp: ");
+	hprint(line+8, 25, trap_regs->ebp);
+	cprint(line+9, 20, "esp: ");
+	hprint(line+9, 25, trap_regs->esp);
+
+	cprint(line+1, 38, "Stack:");
+	for (i=0; i<10; i++) {
+		hprint(line+2+i, 38, trap_regs->esp+(4*i));
+		hprint(line+2+i, 47, *(ulong*)(trap_regs->esp+(4*i)));
+		hprint(line+2+i, 57, trap_regs->esp+(4*(i+10)));
+		hprint(line+2+i, 66, *(ulong*)(trap_regs->esp+(4*(i+10))));
+	}
+
+	cprint(line+11, 0, "CS:EIP:                          ");
+	pp = (unsigned char *)trap_regs->eip;
+	for(i = 0; i < 9; i++) {
+		hprint2(line+11, 8+(3*i), pp[i], 2);
+	}
+
+	while(1) {
+		check_input();
+	}
+}
+
+void set_cache(int val) 
+{
+	switch(val) {
+	case 0:
+		cache_off();	
+		break;
+	case 1:
+		cache_on();
+		break;
+	}
+}
+
+int get_key() {
+	int c;
+
+	c = inb(0x64);
+	if ((c & 1) == 0) {
+		if (serial_cons) {
+			int comstat;
+			comstat = serial_echo_inb(UART_LSR);
+			if (comstat & UART_LSR_DR) {
+				c = serial_echo_inb(UART_RX);
+				/* Pressing '.' has same effect as 'c'
+				   on a keyboard.
+				   Oct 056   Dec 46   Hex 2E   Ascii .
+				*/
+				return (ascii_to_keycode(c));
+			}
+		}
+		return(0);
+	}
+	c = inb(0x60);
+	return((c));
+}
+
+void check_input(void)
+{
+	unsigned char c;
+
+	if ((c = get_key())) {
+		switch(c & 0x7f) {
+		case 1:	
+			/* "ESC" key was pressed, bail out.  */
+			cprint(LINE_RANGE, COL_MID+23, "Halting... ");
+			reboot();
+			break;
+		case 46:
+			/* c - Configure */
+			get_config();
+			break;
+		case 28:
+			/* CR - clear scroll lock */
+			slock = 0;
+			footer();
+			break;
+		case 57:
+			/* SP - set scroll lock */
+			slock = 1;
+			footer();
+			break;
+		case 0x26:
+			/* ^L/L - redraw the display */
+			tty_print_screen();
+			break;
+		}
+	}
+}
+
+void footer()
+{
+	cprint(24, 0, "(ESC)exit  (c)configuration  (SP)scroll_lock  (CR)scroll_unlock");
+	if (slock) {
+		cprint(24, 74, "Locked");
+	} else {
+		cprint(24, 74, "      ");
+	}
+}
+
+ulong getval(int x, int y, int result_shift)
+{
+	unsigned long val;
+	int done;
+	int c;
+	int i, n;
+	int base;
+	int shift;
+	char buf[16];
+
+	for(i = 0; i < sizeof(buf)/sizeof(buf[0]); i++ ) {
+		buf[i] = ' ';
+	}
+	buf[sizeof(buf)/sizeof(buf[0]) -1] = '\0';
+	
+	wait_keyup();
+	done = 0;
+	n = 0;
+	base = 10;
+	while(!done) {
+		/* Read a new character and process it */
+		c = get_key();
+		switch(c) {
+		case 0x26: /* ^L/L - redraw the display */
+			tty_print_screen();
+			break;
+		case 0x1c: /* CR */
+			/* If something has been entered we are done */
+			if(n) done = 1;
+			break;
+		case 0x19: /* p */ buf[n] = 'p'; break;
+		case 0x22: /* g */ buf[n] = 'g'; break;
+		case 0x32: /* m */ buf[n] = 'm'; break;
+		case 0x25: /* k */ buf[n] = 'k'; break;
+		case 0x2d: /* x */
+			/* Only allow 'x' after an initial 0 */
+			if (n == 1 && (buf[0] == '0')) {
+				buf[n] = 'x';
+			}
+			break;
+		case 0x0e: /* BS */
+			if (n > 0) {
+				n -= 1;
+				buf[n] = ' ';
+			}
+			break;
+		/* Don't allow entering a number not in our current base */
+		case 0x0B: if (base >= 1) buf[n] = '0'; break;
+		case 0x02: if (base >= 2) buf[n] = '1'; break;
+		case 0x03: if (base >= 3) buf[n] = '2'; break;
+		case 0x04: if (base >= 4) buf[n] = '3'; break;
+		case 0x05: if (base >= 5) buf[n] = '4'; break;
+		case 0x06: if (base >= 6) buf[n] = '5'; break;
+		case 0x07: if (base >= 7) buf[n] = '6'; break;
+		case 0x08: if (base >= 8) buf[n] = '7'; break;
+		case 0x09: if (base >= 9) buf[n] = '8'; break;
+		case 0x0A: if (base >= 10) buf[n] = '9'; break;
+		case 0x1e: if (base >= 11) buf[n] = 'a'; break;
+		case 0x30: if (base >= 12) buf[n] = 'b'; break;
+		case 0x2e: if (base >= 13) buf[n] = 'c'; break;
+		case 0x20: if (base >= 14) buf[n] = 'd'; break;
+		case 0x12: if (base >= 15) buf[n] = 'e'; break;
+		case 0x21: if (base >= 16) buf[n] = 'f'; break;
+		default:
+			break;
+		}
+		/* Don't allow anything to be entered after a suffix */
+		if (n > 0 && (
+			(buf[n-1] == 'p') || (buf[n-1] == 'g') || 
+			(buf[n-1] == 'm') || (buf[n-1] == 'k'))) {
+			buf[n] = ' ';
+		}
+		/* If we have entered a character increment n */
+		if (buf[n] != ' ') {
+			n++;
+		}
+		buf[n] = ' ';
+		/* Print the current number */
+		cprint(x, y, buf);
+
+		/* Find the base we are entering numbers in */
+		base = 10;
+		if ((buf[0] == '0') && (buf[1] == 'x')) {
+			base = 16;
+		}
+		else if (buf[0] == '0') {
+			base = 8;
+		}
+	}
+	/* Compute our current shift */
+	shift = 0;
+	switch(buf[n-1]) {
+	case 'g': /* gig */  shift = 30; break;
+	case 'm': /* meg */  shift = 20; break;
+	case 'p': /* page */ shift = 12; break;
+	case 'k': /* kilo */ shift = 10; break;
+	}
+	shift -= result_shift;
+
+	/* Compute our current value */
+	val = simple_strtoul(buf, 0, base);
+	if (shift > 0) {
+		if (shift >= 32) {
+			val = 0xffffffff;
+		} else {
+			val <<= shift;
+		}
+	} else {
+		if (-shift >= 32) {
+			val = 0;
+		}
+		else {
+			val >>= -shift;
+		}
+	}
+	return val;
+}
+
+void ttyprint(int y, int x, const char *p)
+{
+	static char sx[3];
+	static char sy[3];
+	
+	sx[0]='\0';
+	sy[0]='\0';
+	x++; y++;
+	itoa(sx, x);
+	itoa(sy, y);
+	serial_echo_print("[");
+	serial_echo_print(sy);
+	serial_echo_print(";");
+	serial_echo_print(sx);
+	serial_echo_print("H");
+	serial_echo_print(p);
+}
+
+void serial_echo_init(void)
+{
+	int comstat, hi, lo, serial_div;
+	unsigned char lcr;	
+
+	/* read the Divisor Latch */
+	comstat = serial_echo_inb(UART_LCR);
+	serial_echo_outb(comstat | UART_LCR_DLAB, UART_LCR);
+	hi = serial_echo_inb(UART_DLM);
+	lo = serial_echo_inb(UART_DLL);
+	serial_echo_outb(comstat, UART_LCR);
+
+	/* now do hardwired init */
+	lcr = serial_parity | (serial_bits - 5);
+	serial_echo_outb(lcr, UART_LCR); /* No parity, 8 data bits, 1 stop */
+	serial_div = 115200 / serial_baud_rate;
+	serial_echo_outb(0x80|lcr, UART_LCR); /* Access divisor latch */
+	serial_echo_outb(serial_div & 0xff, UART_DLL);  /* baud rate divisor */
+	serial_echo_outb((serial_div >> 8) & 0xff, UART_DLM);
+	serial_echo_outb(lcr, UART_LCR); /* Done with divisor */
+
+	/* Prior to disabling interrupts, read the LSR and RBR
+	 * registers */
+	comstat = serial_echo_inb(UART_LSR); /* COM? LSR */
+	comstat = serial_echo_inb(UART_RX);	/* COM? RBR */
+	serial_echo_outb(0x00, UART_IER); /* Disable all interrupts */
+
+        clear_screen_buf();
+
+	return;
+}
+
+/*
+ * Get_number of digits
+ */
+int getnum(ulong val)
+{
+	int len = 0;
+	int i = 1;
+	
+	while(i <= val)
+	{
+		len++;
+		i *= 10;
+	}
+
+	return len;
+		
+}
+
+
+void serial_echo_print(const char *p)
+{
+	if (!serial_cons) {
+		return;
+	}
+	/* Now, do each character */
+	while (*p) {
+		WAIT_FOR_XMITR;
+
+		/* Send the character out. */
+		serial_echo_outb(*p, UART_TX);
+		if(*p==10) {
+			WAIT_FOR_XMITR;
+			serial_echo_outb(13, UART_TX);
+		}
+		p++;
+	}
+}
+
+/* Except for multi-character key sequences this mapping
+ * table is complete.  So it should not need to be updated
+ * when new keys are searched for.  However the key handling
+ * should really be turned around and only in get_key should
+ * we worry about the exact keycode that was pressed.  Everywhere
+ * else we should switch on the character...
+ */
+struct ascii_map_str ser_map[] =
+/*ascii keycode     ascii  keycode*/
+{ 
+  /* Special cases come first so I can leave
+   * their ``normal'' mapping in the table,
+   * without it being activated.
+   */
+  {  27,   0x01}, /* ^[/ESC -> ESC  */
+  { 127,   0x0e}, /*    DEL -> BS   */
+  {   8,   0x0e}, /* ^H/BS  -> BS   */
+  {  10,   0x1c}, /* ^L/NL  -> CR   */
+  {  13,   0x1c}, /* ^M/CR  -> CR   */
+  {   9,   0x0f}, /* ^I/TAB -> TAB  */
+  {  19,   0x39}, /* ^S     -> SP   */
+  {  17,     28}, /* ^Q     -> CR   */
+
+  { ' ',   0x39}, /*     SP -> SP   */
+  { 'a',   0x1e},
+  { 'A',   0x1e},
+  {   1,   0x1e}, /* ^A      -> A */
+  { 'b',   0x30},
+  { 'B',   0x30},
+  {   2,   0x30}, /* ^B      -> B */
+  { 'c',   0x2e},
+  { 'C',   0x2e},
+  {   3,   0x2e}, /* ^C      -> C */
+  { 'd',   0x20},
+  { 'D',   0x20},
+  {   4,   0x20}, /* ^D      -> D */
+  { 'e',   0x12},
+  { 'E',   0x12},
+  {   5,   0x12}, /* ^E      -> E */
+  { 'f',   0x21},
+  { 'F',   0x21},
+  {   6,   0x21}, /* ^F      -> F */
+  { 'g',   0x22},
+  { 'G',   0x22},
+  {   7,   0x22}, /* ^G      -> G */
+  { 'h',   0x23},
+  { 'H',   0x23},
+  {   8,   0x23}, /* ^H      -> H */
+  { 'i',   0x17},
+  { 'I',   0x17},
+  {   9,   0x17}, /* ^I      -> I */
+  { 'j',   0x24},
+  { 'J',   0x24},
+  {  10,   0x24}, /* ^J      -> J */
+  { 'k',   0x25},
+  { 'K',   0x25},
+  {  11,   0x25}, /* ^K      -> K */
+  { 'l',   0x26},
+  { 'L',   0x26},
+  {  12,   0x26}, /* ^L      -> L */
+  { 'm',   0x32},
+  { 'M',   0x32},
+  {  13,   0x32}, /* ^M      -> M */
+  { 'n',   0x31},
+  { 'N',   0x31},
+  {  14,   0x31}, /* ^N      -> N */
+  { 'o',   0x18},
+  { 'O',   0x18},
+  {  15,   0x18}, /* ^O      -> O */
+  { 'p',   0x19},
+  { 'P',   0x19},
+  {  16,   0x19}, /* ^P      -> P */
+  { 'q',   0x10},
+  { 'Q',   0x10},
+  {  17,   0x10}, /* ^Q      -> Q */
+  { 'r',   0x13},
+  { 'R',   0x13},
+  {  18,   0x13}, /* ^R      -> R */
+  { 's',   0x1f},
+  { 'S',   0x1f},
+  {  19,   0x1f}, /* ^S      -> S */
+  { 't',   0x14},
+  { 'T',   0x14},
+  {  20,   0x14}, /* ^T      -> T */
+  { 'u',   0x16},
+  { 'U',   0x16},
+  {  21,   0x16}, /* ^U      -> U */
+  { 'v',   0x2f},
+  { 'V',   0x2f},
+  {  22,   0x2f}, /* ^V      -> V */
+  { 'w',   0x11},
+  { 'W',   0x11},
+  {  23,   0x11}, /* ^W      -> W */
+  { 'x',   0x2d},
+  { 'X',   0x2d},
+  {  24,   0x2d}, /* ^X      -> X */
+  { 'y',   0x15},
+  { 'Y',   0x15},
+  {  25,   0x15}, /* ^Y      -> Y */
+  { 'z',   0x2c},
+  { 'Z',   0x2c},
+  {  26,   0x2c}, /* ^Z      -> Z */
+  { '-',   0x0c},
+  { '_',   0x0c},
+  {  31,   0x0c}, /* ^_      -> _ */
+  { '=',   0x0c},
+  { '+',   0x0c},
+  { '[',   0x1a},
+  { '{',   0x1a},
+  {  27,   0x1a}, /* ^[      -> [ */
+  { ']',   0x1b},
+  { '}',   0x1b},
+  {  29,   0x1b}, /* ^]      -> ] */
+  { ';',   0x27},
+  { ':',   0x27},
+  { '\'',  0x28},
+  { '"',   0x28},
+  { '`',   0x29},
+  { '~',   0x29},
+  { '\\',  0x2b},
+  { '|',   0x2b},
+  {  28,   0x2b}, /* ^\      -> \ */
+  { ',',   0x33},
+  { '<',   0x33},
+  { '.',   0x34},
+  { '>',   0x34},
+  { '/',   0x35},
+  { '?',   0x35},
+  { '1',   0x02},
+  { '!',   0x02},
+  { '2',   0x03},
+  { '@',   0x03},
+  { '3',   0x04},
+  { '#',   0x04},
+  { '4',   0x05},
+  { '$',   0x05},
+  { '5',   0x06},
+  { '%',   0x06},
+  { '6',   0x07},
+  { '^',   0x07},
+  {  30,   0x07}, /* ^^      -> 6 */
+  { '7',   0x08},
+  { '&',   0x08},
+  { '8',   0x09},
+  { '*',   0x09},
+  { '9',   0x0a},
+  { '(',   0x0a},
+  { '0',   0x0b},
+  { ')',   0x0b},
+  {   0,      0}
+};
+
+/*
+ * Given an ascii character, return the keycode
+ *
+ * Uses ser_map definition above.
+ *
+ * It would be more efficient to use an array of 255 characters
+ * and directly index into it.
+ */
+int ascii_to_keycode (int in)
+{
+	struct ascii_map_str *p;
+	for (p = ser_map; p->ascii; p++) {
+		if (in ==p->ascii)
+			return p->keycode;
+	}
+	return 0;
+}
+
+/*
+ * Call this when you want to wait for the user to lift the
+ * finger off of a key.  It is a noop if you are using a
+ * serial console.
+ */
+void wait_keyup( void ) {
+	/* Check to see if someone lifted the keyboard key */
+	while (1) {
+		if ((get_key() & 0x80) != 0) {
+			return;
+		}
+		/* Trying to simulate waiting for a key release with
+		 * the serial port is to nasty to let live.
+		 * In particular some menus don't even display until
+		 * you release the key that caused to to get there.
+		 * With the serial port this results in double pressing
+		 * or something worse for just about every key.
+		 */
+		if (serial_cons) {
+			return;
+		}
+	}
+}
+
+/*
+ * Handles "console=<param>" command line option
+ *
+ * Examples of accepted params:
+ *   ttyS0
+ *   ttyS1
+ *   ttyS0,115200
+ *   ttyS0,9600e8
+ */
+void serial_console_setup(char *param)
+{
+	char *option, *end;
+	unsigned long tty;
+	unsigned long baud_rate;
+	unsigned char parity, bits;
+
+	if (strncmp(param, "ttyS", 4))
+		return;   /* not a serial port */
+
+	param += 4;
+
+	tty = simple_strtoul(param, &option, 10);
+
+	if (option == param)
+		return;   /* there were no digits */
+
+	if (tty > 1)
+		return;   /* only ttyS0 and ttyS1 supported */
+
+	if (*option == '\0' || *option == ' ')
+		goto save_tty; /* no options given, just ttyS? */
+
+	if (*option != ',')
+		return;  /* missing the comma separator */
+
+	/* baud rate must follow */
+	option++;
+	baud_rate = simple_strtoul(option, &end, 10);
+
+	if (end == option)
+		return;  /* no baudrate after comma */
+
+	if (baud_rate == 0 || (115200 % baud_rate) != 0)
+		return;  /* wrong baud rate */
+
+	if (*end == '\0' || *end == ' ')
+		goto save_baud_rate;  /* no more options given */
+
+	switch (toupper(*end)) {
+		case 'N':
+			parity = 0;
+			break;
+		case 'O':
+			parity = UART_LCR_PARITY;
+			break;
+		case 'E':
+			parity = UART_LCR_PARITY | UART_LCR_EPAR;
+			break;
+		default:
+			/* Unknown parity */
+			return;
+	}
+
+	end++;
+	if (*end == '\0' || *end == ' ')
+		goto save_parity;
+
+	/* word length (bits) */
+	if (*end < '7' || *end > '8')
+		return;  /* invalid number of bits */
+
+	bits = *end - '0';
+
+	end++;
+
+	if (*end != '\0' || *end != ' ')
+		return;  /* garbage at the end */
+
+	serial_bits = bits;
+	save_parity:
+	serial_parity = parity;
+	save_baud_rate:
+	serial_baud_rate = (int) baud_rate;
+  save_tty:
+	serial_tty = (short) tty;
+	serial_cons = 1;
+}
+
+/* Get a comma seperated list of numbers */
+void get_list(int x, int y, int len, char *buf)
+{
+	int c, n = 0;
+
+	len--;
+	wait_keyup();
+	while(1) {
+		/* Read a new character and process it */
+		c = get_key();
+		switch(c) {
+		    case 0x1c: /* CR */
+			/* If something has been entered we are done */
+			if(n) {
+			    buf[n] = 0;
+			    return;
+			}
+			break;
+		    case 0x0e: /* BS */
+			if (n > 0) {
+				n -= 1;
+				buf[n] = ' ';
+			}
+			break;
+		    case 0x0B: buf[n++] = '0'; break;
+		    case 0x02: buf[n++] = '1'; break;
+		    case 0x03: buf[n++] = '2'; break;
+		    case 0x04: buf[n++] = '3'; break;
+		    case 0x05: buf[n++] = '4'; break;
+		    case 0x06: buf[n++] = '5'; break;
+		    case 0x07: buf[n++] = '6'; break;
+		    case 0x08: buf[n++] = '7'; break;
+		    case 0x09: buf[n++] = '8'; break;
+		    case 0x0a: buf[n++] = '9'; break;
+		    case 0x33: buf[n++] = ','; break;
+		}
+		cprint(x, y, buf);
+		if (n >= len) {
+		   buf[n] = 0;
+		   return;
+		}
+	}
+}
\ No newline at end of file
diff --git a/linuxbios.c b/linuxbios.c
new file mode 100644
index 0000000..75d9181
--- /dev/null
+++ b/linuxbios.c
@@ -0,0 +1,165 @@
+#include "linuxbios_tables.h"
+#include "test.h"
+
+static unsigned long ip_compute_csum(void *addr, unsigned long length)
+{
+	uint16_t *ptr;
+	unsigned long sum;
+	unsigned long len;
+	unsigned long laddr;
+	/* compute an ip style checksum */
+	laddr = (unsigned long )addr;
+	sum = 0;
+	if (laddr & 1) {
+		uint16_t buffer;
+		unsigned char *ptr;
+		/* copy the first byte into a 2 byte buffer.
+		 * This way automatically handles the endian question
+		 * of which byte (low or high) the last byte goes in.
+		 */
+		buffer = 0;
+		ptr = addr;
+		memmove(&buffer, ptr, 1);
+		sum += buffer;
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+		length -= 1;
+		addr = ptr +1;
+		
+	}
+	len = length >> 1;
+	ptr = addr;
+	while (len--) {
+		sum += *(ptr++);
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+	}
+	addr = ptr;
+	if (length & 1) {
+		uint16_t buffer;
+		unsigned char *ptr;
+		/* copy the last byte into a 2 byte buffer.
+		 * This way automatically handles the endian question
+		 * of which byte (low or high) the last byte goes in.
+		 */
+		buffer = 0;
+		ptr = addr;
+		memmove(&buffer, ptr, 1);
+		sum += buffer;
+		if (sum > 0xFFFF)
+			sum -= 0xFFFF;
+	}
+	return (~sum) & 0xFFFF;
+	
+}
+
+#define for_each_lbrec(head, rec) \
+	for(rec = (struct lb_record *)(((char *)head) + sizeof(*head)); \
+		(((char *)rec) < (((char *)head) + sizeof(*head) + head->table_bytes))  && \
+		(rec->size >= 1) && \
+		((((char *)rec) + rec->size) <= (((char *)head) + sizeof(*head) + head->table_bytes)); \
+		rec = (struct lb_record *)(((char *)rec) + rec->size)) 
+		
+
+static int count_lb_records(struct lb_header *head)
+{
+	struct lb_record *rec;
+	int count;
+	count = 0;
+	for_each_lbrec(head, rec) {
+		count++;
+	}
+	return count;
+}
+
+static struct lb_header * __find_lb_table(unsigned long start, unsigned long end)
+{
+	unsigned long addr;
+	/* For now be stupid.... */
+	for(addr = start; addr < end; addr += 16) {
+		struct lb_header *head = (struct lb_header *)addr;
+		struct lb_record *recs = (struct lb_record *)(addr + sizeof(*head));
+		if (memcmp(head->signature, "LBIO", 4) != 0)
+			continue;
+		if (head->header_bytes != sizeof(*head))
+			continue;
+		if (ip_compute_csum((unsigned char *)head, sizeof(*head)) != 0)
+			continue;
+		if (ip_compute_csum((unsigned char *)recs, head->table_bytes) 
+			!= head->table_checksum)
+			continue;
+		if (count_lb_records(head) != head->table_entries)
+			continue;
+		return head;
+	};
+	return 0;
+}
+
+static struct lb_header * find_lb_table(void)
+{
+	struct lb_header *head;
+	head = 0;
+	if (!head) {
+		/* First try at address 0 */
+		head = __find_lb_table(0x00000, 0x1000);
+	}
+	if (!head) {
+		/* Then try at address 0xf0000 */
+		head = __find_lb_table(0xf0000, 0x100000);
+	}
+	return head;
+}
+
+int query_linuxbios(void)
+{
+	struct lb_header *head;
+	struct lb_record *rec;
+	struct lb_memory *mem;
+	struct lb_forward *forward;
+	int i, entries;
+	
+	head = find_lb_table();
+	if (!head) {
+		return 0;
+	}
+
+	 /* coreboot also can forward the table to the high tables area. */
+	 rec = (struct lb_record *)(((char *)head) + sizeof(*head));
+	 if (rec->tag == LB_TAG_FORWARD) {
+		 forward = (struct lb_forward *)rec;
+		 head = (struct lb_header *)(unsigned long)(forward->forward);
+		 if (!head) { return 0;	}
+	 }
+
+	mem = 0;
+	for_each_lbrec(head, rec) {
+		if (rec->tag == LB_TAG_MEMORY) {
+			mem = (struct lb_memory *)rec;
+			break;
+		}
+	}
+	if (!mem) {
+		return 1;
+	}
+	entries = (mem->size - sizeof(*mem))/sizeof(mem->map[0]);
+	if (entries == 0)
+		return 1;
+	mem_info.e820_nr = 0;
+	for(i = 0; i < entries; i++) {
+		unsigned long long start;
+		unsigned long long size;
+		unsigned long type;
+		if (i >= E820MAX) {
+			break;
+		}
+		start = mem->map[i].start;
+		size = mem->map[i].size;
+		type = (mem->map[i].type == LB_MEM_RAM)?E820_RAM: E820_RESERVED;
+		mem_info.e820[mem_info.e820_nr].addr = start;
+		mem_info.e820[mem_info.e820_nr].size = size;
+		mem_info.e820[mem_info.e820_nr].type = type;
+		mem_info.e820_nr++;
+	}
+	return 1;
+}
+
diff --git a/linuxbios_tables.h b/linuxbios_tables.h
new file mode 100644
index 0000000..38f2038
--- /dev/null
+++ b/linuxbios_tables.h
@@ -0,0 +1,89 @@
+#ifndef LINUXBIOS_TABLES_H
+#define LINUXBIOS_TABLES_H
+
+#include "stdint.h"
+
+/* The linuxbios table information is for conveying information
+ * from the firmware to the loaded OS image.  Primarily this
+ * is expected to be information that cannot be discovered by
+ * other means, such as quering the hardware directly.
+ *
+ * All of the information should be Position Independent Data.  
+ * That is it should be safe to relocated any of the information
+ * without it's meaning/correctnes changing.   For table that
+ * can reasonably be used on multiple architectures the data
+ * size should be fixed.  This should ease the transition between
+ * 32 bit and 64 bit architectures etc.
+ *
+ * The completeness test for the information in this table is:
+ * - Can all of the hardware be detected?
+ * - Are the per motherboard constants available?
+ * - Is there enough to allow a kernel to run that was written before
+ *   a particular motherboard is constructed? (Assuming the kernel
+ *   has drivers for all of the hardware but it does not have
+ *   assumptions on how the hardware is connected together).
+ *
+ * With this test it should be straight forward to determine if a
+ * table entry is required or not.  This should remove much of the
+ * long term compatibility burden as table entries which are
+ * irrelevant or have been replaced by better alternatives may be
+ * dropped.  Of course it is polite and expidite to include extra
+ * table entries and be backwards compatible, but it is not required.
+ */
+
+
+struct lb_header
+{
+	uint8_t  signature[4]; /* LBIO */
+	uint32_t header_bytes;
+	uint32_t header_checksum;
+	uint32_t table_bytes;
+	uint32_t table_checksum;
+	uint32_t table_entries;
+};
+
+/* Every entry in the boot enviroment list will correspond to a boot
+ * info record.  Encoding both type and size.  The type is obviously
+ * so you can tell what it is.  The size allows you to skip that
+ * boot enviroment record if you don't know what it easy.  This allows
+ * forward compatibility with records not yet defined.
+ */
+struct lb_record {
+	uint32_t tag;		/* tag ID */
+	uint32_t size;		/* size of record (in bytes) */
+};
+
+#define LB_TAG_UNUSED	0x0000
+
+#define LB_TAG_MEMORY	0x0001
+#define LB_TAG_FORWARD 0x0011
+
+struct lb_memory_range {
+	uint64_t start;
+	uint64_t size;
+	uint32_t type;
+#define LB_MEM_RAM      1
+#define LB_MEM_RESERVED 2
+	
+};
+
+struct lb_memory {
+	uint32_t tag;
+	uint32_t size;
+	struct lb_memory_range map[0];
+};
+
+#define LB_TAG_HWRPB	0x0002
+struct lb_hwrpb {
+	uint32_t tag;
+	uint32_t size;
+	uint64_t hwrpb;
+};
+
+struct lb_forward {
+ uint32_t tag;
+ uint32_t size;
+ uint64_t forward;
+};
+
+#endif /* LINUXBIOS_TABLES_H */
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..0bc7ca0
--- /dev/null
+++ b/main.c
@@ -0,0 +1,1285 @@
+/*
+ * MemTest86+ V5 Specific code (GPL V2.0)
+ * By Samuel DEMEULEMEESTER, sdemeule@memtest.org
+ * http://www.canardpc.com - http://www.memtest.org
+ * ------------------------------------------------
+ * main.c - MemTest-86  Version 3.5
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ */
+ 
+#include "stdint.h"
+#include "stddef.h"
+#include "test.h"
+#include "defs.h"
+#include "cpuid.h"
+#include "smp.h"
+#include "config.h"
+#undef TEST_TIMES
+#define DEFTESTS 9
+#define FIRST_DIVISER 3
+
+/* The main stack is allocated during boot time. The stack size should
+ * preferably be a multiple of page size(4Kbytes)
+*/
+
+extern struct	cpu_ident cpu_id;
+extern char	toupper(char c);
+extern int	isxdigit(char c);
+extern void	reboot();
+extern void	bzero();
+extern void	smp_set_ordinal(int me, int ord);
+extern int	smp_my_ord_num(int me);
+extern int	smp_ord_to_cpu(int me);
+extern void	get_cpuid();
+extern void	initialise_cpus();
+extern ulong	rand(int cpu);
+extern void	get_mem_speed(int cpu, int ncpus);
+extern void	rand_seed(unsigned int seed1, unsigned int seed2, int cpu);
+extern struct	barrier_s *barr;
+extern int 	num_cpus;
+extern int 	act_cpus;
+
+static int	find_ticks_for_test(int test);
+void		find_ticks_for_pass(void);
+int		find_chunks(int test);
+static void	test_setup(void);
+static int	compute_segments(struct pmap map, int cpu);
+int		do_test(int ord);
+struct tseq tseq[] = {
+	{1, -1,  0,   6, 0, "[Address test, walking ones, no cache] "},
+	{1, -1,  1,   6, 0, "[Address test, own address Sequential] "},
+	{1, 32,  2,   6, 0, "[Address test, own address Parallel]   "},
+	{1, 32,  3,   6, 0, "[Moving inversions, 1s & 0s Parallel]  "},
+	{1, 32,  5,   3, 0, "[Moving inversions, 8 bit pattern]     "},
+	{1, 32,  6,  30, 0, "[Moving inversions, random pattern]    "},
+	{1, 32,  7,  81, 0, "[Block move]                           "}, 
+	{1,  1,  8,   3, 0, "[Moving inversions, 32 bit pattern]    "}, 
+	{1, 32,  9,  48, 0, "[Random number sequence]               "},
+  {1, 32, 10,   6, 0, "[Modulo 20, Random pattern]            "},
+	{1, 1,  11, 240, 0, "[Bit fade test, 2 patterns]            "},
+	{1, 0,   0,   0, 0, NULL}
+};
+
+volatile int    mstr_cpu;
+volatile int	run_cpus;
+volatile int	cpu_ord=0;
+int		maxcpus=MAX_CPUS;
+volatile short  cpu_sel;
+volatile short	cpu_mode;
+char		cpu_mask[MAX_CPUS];
+long 		bin_mask=0xffffffff;
+short		onepass;
+volatile short	btflag = 0;
+volatile int	test;
+short	        restart_flag;
+bool	        reloc_pending = FALSE;
+uint8_t volatile stacks[MAX_CPUS][STACKSIZE];
+int 		bitf_seq = 0;
+char		cmdline_parsed = 0;
+struct 		vars variables = {};
+struct 		vars * const v = &variables;
+volatile int 	bail;
+int 		nticks;
+int 		test_ticks;
+volatile int 	segs;
+static int	ltest;
+static int	pass_flag = 0;
+volatile short	start_seq = 0;
+static int	c_iter;
+ulong 		high_test_adr;
+volatile static int window;
+volatile static unsigned long win_next;
+volatile static ulong win0_start;	/* Start test address for window 0 */
+volatile static ulong win1_end;		/* End address for relocation */
+volatile static struct pmap winx;  	/* Window struct for mapping windows */
+
+/* Find the next selected test to run */
+void next_test()
+{
+	test++;
+	while (tseq[test].sel == 0 && tseq[test].cpu_sel != 0) {
+	    test++;
+	}
+
+	if (tseq[test].cpu_sel == 0) {
+	    /* We hit the end of the list so we completed a pass */
+	    pass_flag++;
+	    /* Find the next test to run, start searching from 0 */
+	    test = 0;
+	    while (tseq[test].sel == 0 && tseq[test].cpu_sel != 0) {
+		test++;
+	    }
+	}
+}
+
+/* Set default values for all parameters */
+void set_defaults()
+{
+	int i;
+
+	if (start_seq == 2) {
+		/* This is a restart so we reset everything */
+		onepass = 0;
+		i = 0;
+		while (tseq[i].cpu_sel) {
+			tseq[i].sel = 1;
+			i++;
+		}
+		test = 0;
+		if (tseq[0].sel == 0) {
+			next_test();
+		}
+	}
+	ltest = -1;
+	win_next = 0;
+	window = 0;
+	bail = 0;
+	cpu_mode = CPM_ALL;
+	cpu_sel = 0;
+	v->printmode=PRINTMODE_ADDRESSES;
+	v->numpatn=0;
+	v->plim_lower = 0;
+	v->plim_upper = v->pmap[v->msegs-1].end;
+	v->pass = 0;
+	v->msg_line = 0;
+	v->ecount = 0;
+	v->ecc_ecount = 0;
+	v->msg_line = LINE_SCROLL-1;
+	v->scroll_start = v->msg_line * 160;
+	v->erri.low_addr.page = 0x7fffffff;
+	v->erri.low_addr.offset = 0xfff;
+	v->erri.high_addr.page = 0;
+	v->erri.high_addr.offset = 0;
+	v->erri.min_bits = 32;
+	v->erri.max_bits = 0;
+	v->erri.min_bits = 32;
+	v->erri.max_bits = 0;
+	v->erri.maxl = 0;
+	v->erri.cor_err = 0;
+	v->erri.ebits = 0;
+	v->erri.hdr_flag = 0;
+	v->erri.tbits = 0;
+	for (i=0; tseq[i].msg != NULL; i++) {
+		tseq[i].errors = 0;
+	}
+	restart_flag = 0;
+	tseq[10].sel = 0;
+}
+
+/* Boot trace function */
+short tidx = 25;
+void btrace(int me, int line, char *msg, int wait, long v1, long v2)
+{
+	int y, x;
+
+	/* Is tracing turned on? */
+	if (btflag == 0) return;
+
+	spin_lock(&barr->mutex);
+	y = tidx%13;
+	x = tidx/13*40;
+	cplace(y+11, x+1, ' ');
+	if (++tidx > 25) {
+		tidx = 0;
+	}
+	y = tidx%13;
+	x = tidx/13*40;
+
+	cplace(y+11, x+1, '>');
+	dprint(y+11, x+2, me, 2, 0);
+	dprint(y+11, x+5, line, 4, 0);
+	cprint(y+11, x+10, msg);
+	hprint(y+11, x+22, v1);
+	hprint(y+11, x+31, v2);
+	if (wait) {
+		wait_keyup();
+	}
+	spin_unlock(&barr->mutex);
+}
+
+/* Relocate the test to a new address. Be careful to not overlap! */
+static void run_at(unsigned long addr, int cpu)
+{
+	ulong *ja = (ulong *)(addr + startup_32 - _start);
+
+	/* CPU 0, Copy memtest86+ code */
+	if (cpu == 0) {
+		memmove((void *)addr, &_start, _end - _start);
+	}
+
+	/* Wait for the copy */
+	barrier();
+
+	/* We use a lock to insure that only one CPU at a time jumps to
+	 * the new code. Some of the startup stuff is not thread safe! */
+  spin_lock(&barr->mutex);   
+
+	/* Jump to the start address */
+	goto *ja;
+}
+
+/* Switch from the boot stack to the main stack. First the main stack
+ * is allocated, then the contents of the boot stack are copied, then
+ * ESP is adjusted to point to the new stack.  
+ */
+static void
+switch_to_main_stack(unsigned cpu_num)
+{
+	extern uintptr_t boot_stack;
+	extern uintptr_t boot_stack_top; 
+	uintptr_t *src, *dst;
+	int offs;
+	uint8_t * stackAddr, *stackTop;
+   
+	stackAddr = (uint8_t *) &stacks[cpu_num][0];
+
+	stackTop  = stackAddr + STACKSIZE;
+   
+	src = (uintptr_t*)&boot_stack_top;
+	dst = (uintptr_t*)stackTop;
+	do {
+		src--; dst--;
+		*dst = *src;
+	} while ((uintptr_t *)src > (uintptr_t *)&boot_stack);
+
+	offs = (uint8_t *)&boot_stack_top - stackTop;
+	__asm__ __volatile__ (
+	"subl %%eax, %%esp" 
+		: /*no output*/
+		: "a" (offs) : "memory" 
+	);
+}
+
+void reloc_internal(int cpu)
+{
+	/* clear variables */
+        reloc_pending = FALSE;
+
+	run_at(LOW_TEST_ADR, cpu);
+}
+
+void reloc(void)
+{
+	bail++;
+        reloc_pending = TRUE;
+}
+
+/* command line passing using the 'old' boot protocol */
+#define MK_PTR(seg,off) ((void*)(((unsigned long)(seg) << 4) + (off)))
+#define OLD_CL_MAGIC_ADDR ((unsigned short*) MK_PTR(INITSEG,0x20))
+#define OLD_CL_MAGIC 0xA33F 
+#define OLD_CL_OFFSET_ADDR ((unsigned short*) MK_PTR(INITSEG,0x22))
+
+static void parse_command_line(void)
+{
+	long simple_strtoul(char *cmd, char *ptr, int base);
+	char *cp, dummy;
+	int i, j, k;
+
+	if (cmdline_parsed)
+		return;
+
+	/* Fill in the cpu mask array with the default */
+	for (i=0; i<MAX_CPUS; i++) {
+		cpu_mask[i] = 1;
+	}
+
+	if (*OLD_CL_MAGIC_ADDR != OLD_CL_MAGIC)
+		return;
+
+	unsigned short offset = *OLD_CL_OFFSET_ADDR;
+	cp = MK_PTR(INITSEG, offset);
+
+	/* skip leading spaces */
+	while (*cp == ' ')
+		cp++;
+
+	while (*cp) {
+		if (!strncmp(cp, "console=", 8)) {
+			cp += 8;
+			serial_console_setup(cp);
+		}
+		/* Enable boot trace? */
+		if (!strncmp(cp, "btrace", 6)) {
+			cp += 6;
+			btflag++;
+		}
+		/* Limit number of CPUs */
+		if (!strncmp(cp, "maxcpus=", 8)) {
+			cp += 8;
+			maxcpus=(int)simple_strtoul(cp, &dummy, 10);
+		}
+		/* Run one pass and exit if there are no errors */
+		if (!strncmp(cp, "onepass", 7)) {
+			cp += 7;
+			onepass++;
+		}
+		/* Setup a list of tests to run */
+		if (!strncmp(cp, "tstlist=", 8)) {
+			cp += 8;
+			/* Clear all of the tests first */
+			k = 0;
+			while (tseq[k].cpu_sel) {
+				tseq[k].sel = 0;
+				k++;
+			}
+
+			/* Now enable all of the tests in the list */
+			j = 0;
+			while(*cp && isdigit(*cp)) {
+			    i = *cp-'0';
+			    j = j*10 + i;
+			    cp++;
+			    if (*cp == ',' || !isdigit(*cp)) {
+				if (j < k) {
+				    tseq[j].sel = 1;
+				}
+				if (*cp != ',') break;
+				j = 0;
+			    	cp++;
+			    }
+			}
+		}
+		/* Set a CPU mask to select CPU's to use for testing */
+		if (!strncmp(cp, "cpumask=", 8)) {
+		    cp += 8;
+		    if (cp[0] == '0' && toupper(cp[1]) == 'X') cp += 2;
+		    while (*cp && *cp != ' ' && isxdigit(*cp)) {
+			i = isdigit(*cp) ? *cp-'0' : toupper(*cp)-'A'+10; 
+			bin_mask = bin_mask * 16 + i;
+			cp++;
+		    }
+		    /* Force CPU zero to always be selected */
+		    bin_mask |= 1;
+		    for (i=0; i<32; i++) {
+			if (((bin_mask>>i) & 1) == 0) {
+			     cpu_mask[i] = 0; 
+			}
+		    }
+		}
+		/* go to the next parameter */
+		while (*cp && *cp != ' ') cp++;
+		while (*cp == ' ') cp++;
+	}
+
+	cmdline_parsed = 1;
+}
+
+void clear_screen()
+{
+	int i;
+	char *pp;
+
+	/* Clear screen & set background to blue */
+	for(i=0, pp=(char *)(SCREEN_ADR); i<80*25; i++) {
+		*pp++ = ' ';
+		*pp++ = 0x17;
+	}
+	if (btflag) {
+	    cprint(1, 0, "Boot Trace Enabled");
+	    cprint(1, 0, "Press any key to advance to next trace point");
+	    cprint(9, 1,"CPU Line Message     Param #1 Param #2  CPU Line Message     Param #1 Param #2");
+	    cprint(10,1,"--- ---- ----------- -------- --------  --- ---- ----------- -------- --------");
+	}
+
+}
+/* This is the test entry point. We get here on statup and also whenever
+ * we relocate. */
+void test_start(void)
+{
+	int my_cpu_num, my_cpu_ord, run;
+
+	/* If this is the first time here we are CPU 0 */
+	if (start_seq == 0) {
+		my_cpu_num = 0;
+	} else {
+		my_cpu_num = smp_my_cpu_num();
+	}
+	/* First thing, switch to main stack */
+	switch_to_main_stack(my_cpu_num);
+
+	/* First time (for this CPU) initialization */
+	if (start_seq < 2) {
+
+	    /* These steps are only done by the boot cpu */
+	    if (my_cpu_num == 0) {
+		my_cpu_ord = cpu_ord++;
+		smp_set_ordinal(my_cpu_num, my_cpu_ord);
+		parse_command_line();
+		clear_screen();
+		/* Initialize the barrier so the lock in btrace will work.
+		 * Will get redone later when we know how many CPUs we have */
+		barrier_init(1);
+		btrace(my_cpu_num, __LINE__, "Begin     ", 1, 0, 0);
+		/* Find memory size */
+		 mem_size();	/* must be called before initialise_cpus(); */
+		/* Fill in the CPUID table */
+		get_cpuid();
+		/* Startup the other CPUs */
+		start_seq = 1;
+		//initialise_cpus();
+		btrace(my_cpu_num, __LINE__, "BeforeInit", 1, 0, 0);
+		/* Draw the screen and get system information */
+	  init();
+
+		/* Set defaults and initialize variables */
+		set_defaults();
+	
+		/* Setup base address for testing, 1 MB */
+		win0_start = 0x100;
+
+		/* Set relocation address to 32Mb if there is enough
+		 * memory. Otherwise set it to 3Mb */
+		/* Large reloc addr allows for more testing overlap */
+	        if ((ulong)v->pmap[v->msegs-1].end > 0x2f00) {
+			high_test_adr = 0x2000000;
+	        } else {
+			high_test_adr = 0x300000;
+		} 
+		win1_end = (high_test_adr >> 12);
+
+		/* Adjust the map to not test the page at 939k,
+		 *  reserved for locks */
+		v->pmap[0].end--;
+
+		find_ticks_for_pass();
+       	    } else {
+		/* APs only, Register the APs */
+		btrace(my_cpu_num, __LINE__, "AP_Start  ", 0, my_cpu_num,
+			cpu_ord);
+		smp_ap_booted(my_cpu_num);
+		/* Asign a sequential CPU ordinal to each active cpu */
+		spin_lock(&barr->mutex);
+		my_cpu_ord = cpu_ord++;
+		smp_set_ordinal(my_cpu_num, my_cpu_ord);
+		spin_unlock(&barr->mutex);
+		btrace(my_cpu_num, __LINE__, "AP_Done   ", 0, my_cpu_num,
+			my_cpu_ord);
+	    }
+
+	} else {
+	    /* Unlock after a relocation */
+	    spin_unlock(&barr->mutex);
+	    /* Get the CPU ordinal since it is lost during relocation */
+	    my_cpu_ord = smp_my_ord_num(my_cpu_num);
+	    btrace(my_cpu_num, __LINE__, "Reloc_Done",0,my_cpu_num,my_cpu_ord);
+	}
+
+	/* A barrier to insure that all of the CPUs are done with startup */
+	barrier();
+	btrace(my_cpu_num, __LINE__, "1st Barr  ", 1, my_cpu_num, my_cpu_ord);
+	
+
+	/* Setup Memory Management and measure memory speed, we do it here
+	 * because we need all of the available CPUs */
+	if (start_seq < 2) {
+
+	   /* Enable floating point processing */
+	   if (cpu_id.fid.bits.fpu)
+        	__asm__ __volatile__ (
+		    "movl %%cr0, %%eax\n\t"
+		    "andl $0x7, %%eax\n\t"
+		    "movl %%eax, %%cr0\n\t"
+                    : :
+                    : "ax"
+                );
+	   if (cpu_id.fid.bits.sse)
+        	__asm__ __volatile__ (
+                    "movl %%cr4, %%eax\n\t"
+                    "orl $0x00000200, %%eax\n\t"
+                    "movl %%eax, %%cr4\n\t"
+                    : :
+                    : "ax"
+                );
+
+	    btrace(my_cpu_num, __LINE__, "Mem Mgmnt ", 1, cpu_id.fid.bits.pae, cpu_id.fid.bits.lm);
+	    /* Setup memory management modes */
+	    /* If we have PAE, turn it on */
+	    if (cpu_id.fid.bits.pae == 1) {
+		__asm__ __volatile__(
+                    "movl %%cr4, %%eax\n\t"
+                    "orl $0x00000020, %%eax\n\t"
+                    "movl %%eax, %%cr4\n\t"
+                    : :
+                    : "ax"
+                );
+        cprint(LINE_TITLE+1, COL_MODE, "(PAE Mode)");
+       	    }
+	    /* If this is a 64 CPU enable long mode */
+	    if (cpu_id.fid.bits.lm == 1) {
+		__asm__ __volatile__(
+		    "movl $0xc0000080, %%ecx\n\t"
+		    "rdmsr\n\t"
+		    "orl $0x00000100, %%eax\n\t"
+		    "wrmsr\n\t"
+		    : :
+		    : "ax", "cx"
+		);
+		cprint(LINE_TITLE+1, COL_MODE, "(X64 Mode)");
+            }
+	    /* Get the memory Speed with all CPUs */
+	    get_mem_speed(my_cpu_num, num_cpus);
+	}
+
+	/* Set the initialized flag only after all of the CPU's have
+	 * Reached the barrier. This insures that relocation has
+	 * been completed for each CPU. */
+	btrace(my_cpu_num, __LINE__, "Start Done", 1, 0, 0);
+	start_seq = 2;
+
+	/* Loop through all tests */
+	while (1) {
+	    /* If the restart flag is set all initial params */
+	    if (restart_flag) {
+		set_defaults();
+		continue;
+	    }
+            /* Skip single CPU tests if we are using only one CPU */
+            if (tseq[test].cpu_sel == -1 && 
+                    (num_cpus == 1 || cpu_mode != CPM_ALL)) {
+                test++; 
+                continue;
+            }
+
+	    test_setup();
+
+	    /* Loop through all possible windows */
+	    while (win_next <= ((ulong)v->pmap[v->msegs-1].end + WIN_SZ)) {
+
+		/* Main scheduling barrier */
+		cprint(8, my_cpu_num+7, "W");
+		btrace(my_cpu_num, __LINE__, "Sched_Barr", 1,window,win_next);
+		barrier();
+
+		/* Don't go over the 8TB PAE limit */
+		if (win_next > MAX_MEM) {
+			break;
+		}
+
+		/* For the bit fade test, #11, we cannot relocate so bump the
+		 * window to 1 */
+		if (tseq[test].pat == 11 && window == 0) {
+			window = 1;
+		}
+
+		/* Relocate if required */
+		if (window != 0 && (ulong)&_start != LOW_TEST_ADR) {
+			btrace(my_cpu_num, __LINE__, "Sched_RelL", 1,0,0);
+			run_at(LOW_TEST_ADR, my_cpu_num);
+	        }
+		if (window == 0 && v->plim_lower >= win0_start) {
+			window++;
+		}
+		if (window == 0 && (ulong)&_start == LOW_TEST_ADR) {
+			btrace(my_cpu_num, __LINE__, "Sched_RelH", 1,0,0);
+			run_at(high_test_adr, my_cpu_num);
+		}
+
+		/* Decide which CPU(s) to use */
+		btrace(my_cpu_num, __LINE__, "Sched_CPU0",1,cpu_sel,
+			tseq[test].cpu_sel);
+		run = 1;
+		switch(cpu_mode) {
+		case CPM_RROBIN:
+		case CPM_SEQ:
+			/* Select a single CPU */
+			if (my_cpu_ord == cpu_sel) {
+				mstr_cpu = cpu_sel;
+				run_cpus = 1;
+	    		} else {
+				run = 0;
+			}
+			break;
+		case CPM_ALL:
+		    /* Use all CPUs */
+		    if (tseq[test].cpu_sel == -1) {
+			/* Round robin through all of the CPUs */
+			if (my_cpu_ord == cpu_sel) {
+				mstr_cpu = cpu_sel;
+				run_cpus = 1;
+	    		} else {
+				run = 0;
+			}
+		    } else {
+			/* Use the number of CPUs specified by the test,
+			 * Starting with zero */
+			if (my_cpu_ord >= tseq[test].cpu_sel) {
+				run = 0;
+			}
+			/* Set the master CPU to the highest CPU number 
+			 * that has been selected */
+			if (act_cpus < tseq[test].cpu_sel) {
+				mstr_cpu = act_cpus-1;
+				run_cpus = act_cpus;
+			} else {
+				mstr_cpu = tseq[test].cpu_sel-1;
+				run_cpus = tseq[test].cpu_sel;
+			}
+		    }
+		}
+		btrace(my_cpu_num, __LINE__, "Sched_CPU1",1,run_cpus,run);
+		barrier();
+		dprint(9, 7, run_cpus, 2, 0);
+
+		/* Setup a sub barrier for only the selected CPUs */
+		if (my_cpu_ord == mstr_cpu) {
+			s_barrier_init(run_cpus);
+		}
+
+		/* Make sure the the sub barrier is ready before proceeding */
+		barrier();
+
+		/* Not selected CPUs go back to the scheduling barrier */
+		if (run == 0 ) {
+			continue;
+		}
+		cprint(8, my_cpu_num+7, "-");
+		btrace(my_cpu_num, __LINE__, "Sched_Win0",1,window,win_next);
+
+		/* Do we need to exit */
+		if(reloc_pending) {
+		    reloc_internal(my_cpu_num);
+	 	}
+
+		if (my_cpu_ord == mstr_cpu) {
+		    switch (window) {
+		    /* Special case for relocation */
+		    case 0:
+			winx.start = 0;
+			winx.end = win1_end;
+			window++;
+			break;
+		    /* Special case for first segment */
+		    case 1:
+			winx.start = win0_start;
+			winx.end = WIN_SZ;
+			win_next += WIN_SZ;
+			window++;
+			break;
+		    /* For all other windows */
+		    default:
+			winx.start = win_next;
+			win_next += WIN_SZ;
+			winx.end = win_next;
+		    }
+		    btrace(my_cpu_num,__LINE__,"Sched_Win1",1,winx.start,
+				winx.end);
+
+	            /* Find the memory areas to test */
+	            segs = compute_segments(winx, my_cpu_num);
+		}
+		s_barrier();
+		btrace(my_cpu_num,__LINE__,"Sched_Win2",1,segs,
+			v->map[0].pbase_addr);
+
+	        if (segs == 0) {
+		/* No memory in this window so skip it */
+		    continue;
+	        }
+
+		/* map in the window... */
+		if (map_page(v->map[0].pbase_addr) < 0) {
+		    /* Either there is no PAE or we are at the PAE limit */
+		    break;
+		}
+
+		btrace(my_cpu_num, __LINE__, "Strt_Test ",1,my_cpu_num,
+			my_cpu_ord);
+		do_test(my_cpu_ord);
+		btrace(my_cpu_num, __LINE__, "End_Test  ",1,my_cpu_num,
+			my_cpu_ord);
+
+            	paging_off();
+
+	    } /* End of window loop */
+
+	    s_barrier();
+	    btrace(my_cpu_num, __LINE__, "End_Win   ",1,test, window);
+
+	    /* Setup for the next set of windows */
+	    win_next = 0;
+	    window = 0;
+	    bail = 0;
+
+	    /* Only the master CPU does the end of test housekeeping */
+	    if (my_cpu_ord != mstr_cpu) {
+		continue;
+	    }
+
+	    /* Special handling for the bit fade test #11 */
+	    if (tseq[test].pat == 11 && bitf_seq != 6) {
+		/* Keep going until the sequence is complete. */
+		bitf_seq++;
+		continue;
+	    } else {
+		bitf_seq = 0;
+	    }
+
+	    /* Select advancement of CPUs and next test */
+	    switch(cpu_mode) {
+	    case CPM_RROBIN:
+		if (++cpu_sel >= act_cpus) {
+		    cpu_sel = 0;
+		}
+		next_test();
+		break;
+	    case CPM_SEQ:
+		if (++cpu_sel >= act_cpus) {
+		    cpu_sel = 0;
+		    next_test();
+		}
+		break;
+	    case CPM_ALL:
+	      if (tseq[test].cpu_sel == -1) 
+	      	{
+			    /* Do the same test for each CPU */
+			    if (++cpu_sel >= act_cpus) 
+			    	{
+	            cpu_sel = 0;
+			        next_test();
+			    	} else {
+			        continue;
+			    	}
+	        } else {
+		    		next_test();
+					}
+	    } //????
+	    btrace(my_cpu_num, __LINE__, "Next_CPU  ",1,cpu_sel,test);
+
+	    /* If this was the last test then we finished a pass */
+	  if (pass_flag) 
+	  	{
+			pass_flag = 0;
+			
+			v->pass++;
+			
+			dprint(LINE_INFO, 49, v->pass, 5, 0);
+			find_ticks_for_pass();
+			ltest = -1;
+			
+			if (v->ecount == 0) 
+				{
+			    /* If onepass is enabled and we did not get any errors
+			     * reboot to exit the test */
+			    if (onepass) {	reboot();   }
+			    if (!btflag) cprint(LINE_MSG, COL_MSG-8, "** Pass complete, no errors, press Esc to exit **");
+					if(BEEP_END_NO_ERROR) 
+						{
+							beep(1000);
+							beep(2000);
+							beep(1000);
+							beep(2000);
+						}
+				}
+	    }
+
+	    bail=0;
+	} /* End test loop */
+}
+
+
+void test_setup()
+{
+	static int ltest = -1;
+
+	/* See if a specific test has been selected */
+	if (v->testsel >= 0) {
+                test = v->testsel;
+        }
+
+	/* Only do the setup if this is a new test */
+	if (test == ltest) {
+		return;
+	}
+	ltest = test;
+
+	/* Now setup the test parameters based on the current test number */
+	if (v->pass == 0) {
+		/* Reduce iterations for first pass */
+		c_iter = tseq[test].iter/FIRST_DIVISER;
+	} else {
+		c_iter = tseq[test].iter;
+	}
+
+	/* Set the number of iterations. We only do half of the iterations */
+        /* on the first pass */
+	//dprint(LINE_INFO, 28, c_iter, 3, 0);
+	test_ticks = find_ticks_for_test(test);
+	nticks = 0;
+	v->tptr = 0;
+
+	cprint(LINE_PAT, COL_PAT, "            ");
+	cprint(LINE_PAT, COL_PAT-3, "   ");
+	dprint(LINE_TST, COL_MID+6, tseq[test].pat, 2, 1);
+	cprint(LINE_TST, COL_MID+9, tseq[test].msg);
+	cprint(2, COL_MID+8, "                                         ");
+}
+
+/* A couple static variables for when all cpus share the same pattern */
+static ulong sp1, sp2;
+
+int do_test(int my_ord)
+{
+	int i=0, j=0;
+	static int bitf_sleep;
+	unsigned long p0=0, p1=0, p2=0;
+
+	if (my_ord == mstr_cpu) {
+	  if ((ulong)&_start > LOW_TEST_ADR) {
+		/* Relocated so we need to test all selected lower memory */
+		v->map[0].start = mapping(v->plim_lower);
+		
+		/* Good 'ol Legacy USB_WAR */
+		if (v->map[0].start < (ulong*)0x500) 
+		{
+    	v->map[0].start = (ulong*)0x500;
+		}
+		
+		cprint(LINE_PAT, COL_MID+25, " R");
+	    } else {
+		cprint(LINE_PAT, COL_MID+25, "  ");
+	    }
+
+	    /* Update display of memory segments being tested */
+	    p0 = page_of(v->map[0].start);
+	    p1 = page_of(v->map[segs-1].end);
+	    aprint(LINE_RANGE, COL_MID+9, p0);
+	    cprint(LINE_RANGE, COL_MID+14, " - ");
+	    aprint(LINE_RANGE, COL_MID+17, p1);
+	    aprint(LINE_RANGE, COL_MID+25, p1-p0);
+	    cprint(LINE_RANGE, COL_MID+30, " of ");
+	    aprint(LINE_RANGE, COL_MID+34, v->selected_pages);
+	}
+	
+	switch(tseq[test].pat) {
+
+	/* Do the testing according to the selected pattern */
+
+	case 0: /* Address test, walking ones (test #0) */
+		/* Run with cache turned off */
+		set_cache(0);
+		addr_tst1(my_ord);
+		set_cache(1);
+		BAILOUT;
+		break;
+
+	case 1:
+	case 2: /* Address test, own address (test #1, 2) */
+		addr_tst2(my_ord);
+		BAILOUT;
+		break;
+
+	case 3:
+	case 4:	/* Moving inversions, all ones and zeros (tests #3, 4) */
+		p1 = 0;
+		p2 = ~p1;
+		s_barrier();
+		movinv1(c_iter,p1,p2,my_ord);
+		BAILOUT;
+	
+		/* Switch patterns */
+		s_barrier();
+		movinv1(c_iter,p2,p1,my_ord);
+		BAILOUT;
+		break;
+		
+	case 5: /* Moving inversions, 8 bit walking ones and zeros (test #5) */
+		p0 = 0x80;
+		for (i=0; i<8; i++, p0=p0>>1) {
+			p1 = p0 | (p0<<8) | (p0<<16) | (p0<<24);
+			p2 = ~p1;
+			s_barrier();
+			movinv1(c_iter,p1,p2, my_ord);
+			BAILOUT;
+	
+			/* Switch patterns */
+			s_barrier();
+			movinv1(c_iter,p2,p1, my_ord);
+			BAILOUT
+		}
+		break;
+		
+	case 6: /* Random Data (test #6) */
+		/* Seed the random number generator */
+		if (my_ord == mstr_cpu) {
+		    if (cpu_id.fid.bits.rdtsc) {
+                	asm __volatile__ ("rdtsc":"=a" (sp1),"=d" (sp2));
+        	    } else {
+                	sp1 = 521288629 + v->pass;
+                	sp2 = 362436069 - v->pass;
+        	    }
+		    rand_seed(sp1, sp2, 0);
+		}
+
+		s_barrier();
+		for (i=0; i < c_iter; i++) {
+			if (my_ord == mstr_cpu) {
+				sp1 = rand(0);
+				sp2 = ~p1;
+			}
+			s_barrier();
+			movinv1(2,sp1,sp2, my_ord);
+			BAILOUT;
+		}
+		break;
+
+
+	case 7: /* Block move (test #7) */
+		block_move(c_iter, my_ord);
+		BAILOUT;
+		break;
+
+	case 8: /* Moving inversions, 32 bit shifting pattern (test #8) */
+		for (i=0, p1=1; p1; p1=p1<<1, i++) {
+			s_barrier();
+			movinv32(c_iter,p1, 1, 0x80000000, 0, i, my_ord);
+			BAILOUT
+			s_barrier();
+			movinv32(c_iter,~p1, 0xfffffffe,
+				0x7fffffff, 1, i, my_ord);
+			BAILOUT
+		}
+		break;
+
+	case 9: /* Random Data Sequence (test #9) */
+		for (i=0; i < c_iter; i++) {
+			s_barrier();
+			movinvr(my_ord);
+			BAILOUT;
+		}
+		break;
+
+	case 10: /* Modulo 20 check, Random pattern (test #10) */
+		for (j=0; j<c_iter; j++) {
+			p1 = rand(0);
+			for (i=0; i<MOD_SZ; i++) {
+				p2 = ~p1;
+				s_barrier();
+				modtst(i, 2, p1, p2, my_ord);
+				BAILOUT
+
+				/* Switch patterns */
+				s_barrier();
+				modtst(i, 2, p2, p1, my_ord);
+				BAILOUT
+			}
+		}
+		break;
+
+	case 11: /* Bit fade test, fill (test #11) */
+		/* Use a sequence to process all windows for each stage */
+		switch(bitf_seq) {
+		case 0:	/* Fill all of memory 0's */
+			bit_fade_fill(0, my_ord);
+			bitf_sleep = 1;
+			break;
+		case 1: /* Sleep for the specified time */
+			/* Only sleep once */
+			if (bitf_sleep) {
+				sleep(c_iter, 1, my_ord, 0);
+				bitf_sleep = 0;
+			}
+			break;
+		case 2: /* Now check all of memory for changes */
+			bit_fade_chk(0, my_ord);
+			break;
+		case 3:	/* Fill all of memory 1's */
+			bit_fade_fill(-1, my_ord);
+			bitf_sleep = 1;
+			break;
+		case 4: /* Sleep for the specified time */
+			/* Only sleep once */
+			if (bitf_sleep) {
+				sleep(c_iter, 1, my_ord, 0);
+				bitf_sleep = 0;
+			}
+			break;
+		case 5: /* Now check all of memory for changes */
+			bit_fade_chk(-1, my_ord);
+			break;
+		}
+		BAILOUT;
+		break;
+
+	case 90: /* Modulo 20 check, all ones and zeros (unused) */
+		p1=0;
+		for (i=0; i<MOD_SZ; i++) {
+			p2 = ~p1;
+			modtst(i, c_iter, p1, p2, my_ord);
+			BAILOUT
+
+			/* Switch patterns */
+			p2 = p1;
+			p1 = ~p2;
+			modtst(i, c_iter, p1,p2, my_ord);
+			BAILOUT
+		}
+		break;
+
+	case 91: /* Modulo 20 check, 8 bit pattern (unused) */
+		p0 = 0x80;
+		for (j=0; j<8; j++, p0=p0>>1) {
+			p1 = p0 | (p0<<8) | (p0<<16) | (p0<<24);
+			for (i=0; i<MOD_SZ; i++) {
+				p2 = ~p1;
+				modtst(i, c_iter, p1, p2, my_ord);
+				BAILOUT
+
+				/* Switch patterns */
+				p2 = p1;
+				p1 = ~p2;
+				modtst(i, c_iter, p1, p2, my_ord);
+				BAILOUT
+			}
+		}
+		break;
+	}
+	return(0);
+}
+
+/* Compute number of SPINSZ chunks being tested */
+int find_chunks(int tst) 
+{
+	int i, j, sg, wmax, ch;
+	struct pmap twin={0,0};
+	unsigned long wnxt = WIN_SZ;
+	unsigned long len;
+
+	wmax = MAX_MEM/WIN_SZ+2;  /* The number of 2 GB segments +2 */
+	/* Compute the number of SPINSZ memory segments */
+	ch = 0;
+	for(j = 0; j < wmax; j++) {
+		/* special case for relocation */
+		if (j == 0) {
+			twin.start = 0;
+			twin.end = win1_end;
+		}
+
+		/* special case for first 2 GB */
+		if (j == 1) {
+			twin.start = win0_start;
+			twin.end = WIN_SZ;
+		}
+
+		/* For all other windows */
+		if (j > 1) {
+			twin.start = wnxt;
+			wnxt += WIN_SZ;
+			twin.end = wnxt;
+		}
+
+	        /* Find the memory areas I am going to test */
+		sg = compute_segments(twin, -1);
+		for(i = 0; i < sg; i++) {
+			len = v->map[i].end - v->map[i].start;
+
+			if (cpu_mode == CPM_ALL && num_cpus > 1) {
+				switch(tseq[tst].pat) {
+				case 2:
+				case 4:
+				case 5:
+				case 6:
+				case 9:
+				case 10:
+				    len /= act_cpus;
+				    break;
+				case 7:
+				case 8:
+				    len /= act_cpus;
+				    break;
+				}
+			}
+			ch += (len + SPINSZ -1)/SPINSZ;
+		}
+	}
+	return(ch);
+}
+
+/* Compute the total number of ticks per pass */
+void find_ticks_for_pass(void)
+{
+	int i;
+
+	v->pptr = 0;
+	v->pass_ticks = 0;
+	v->total_ticks = 0;
+	cprint(1, COL_MID+8, "                                         ");
+	i = 0;
+	while (tseq[i].cpu_sel != 0) {
+		/* Skip tests 2 and 4 if we are using 1 cpu */
+		if (act_cpus == 1 && (i == 2 || i == 4)) { 
+		    i++;
+		    continue;
+		}
+		v->pass_ticks += find_ticks_for_test(i);
+		i++;
+	}
+}
+
+static int find_ticks_for_test(int tst)
+{
+	int ticks=0, c, ch;
+
+	if (tseq[tst].sel == 0) {
+		return(0);
+	}
+
+	/* Determine the number of chunks for this test */
+	ch = find_chunks(tst);
+
+	/* Set the number of iterations. We only do 1/2 of the iterations */
+        /* on the first pass */
+	if (v->pass == 0) {
+		c = tseq[tst].iter/FIRST_DIVISER;
+	} else {
+		c = tseq[tst].iter;
+	}
+
+	switch(tseq[tst].pat) {
+	case 0: /* Address test, walking ones */
+		ticks = 2;
+		break;
+	case 1: /* Address test, own address */
+	case 2:
+		ticks = 2;
+		break;
+	case 3: /* Moving inversions, all ones and zeros */
+	case 4:
+		ticks = 2 + 4 * c;
+		break;
+	case 5: /* Moving inversions, 8 bit walking ones and zeros */
+		ticks = 24 + 24 * c;
+		break;
+	case 6: /* Random Data */
+		ticks = c + 4 * c;
+		break;
+	case 7: /* Block move */
+		ticks = (ch + ch/act_cpus + c*ch);
+		break;
+	case 8: /* Moving inversions, 32 bit shifting pattern */
+		ticks = (1 + c * 2) * 64;
+		break;
+	case 9: /* Random Data Sequence */
+		ticks = 3 * c;
+		break;
+	case 10: /* Modulo 20 check, Random pattern */
+		ticks = 4 * 40 * c;
+		break;
+	case 11: /* Bit fade test */
+		ticks = c * 2 + 4 * ch;
+		break;
+	case 90: /* Modulo 20 check, all ones and zeros (unused) */
+		ticks = (2 + c) * 40;
+		break;
+	case 91: /* Modulo 20 check, 8 bit pattern (unused) */
+		ticks = (2 + c) * 40 * 8;
+		break;
+	}
+	if (cpu_mode == CPM_SEQ || tseq[tst].cpu_sel == -1) {
+		ticks *= act_cpus;
+	}
+	if (tseq[tst].pat == 7 || tseq[tst].pat == 11) {
+		return ticks;
+	}
+	return ticks*ch;
+}
+
+static int compute_segments(struct pmap win, int me)
+{
+	unsigned long wstart, wend;
+	int i, sg;
+
+	/* Compute the window I am testing memory in */
+	wstart = win.start;
+	wend = win.end;
+	sg = 0;
+
+	/* Now reduce my window to the area of memory I want to test */
+	if (wstart < v->plim_lower) {
+		wstart = v->plim_lower;
+	}
+	if (wend > v->plim_upper) {
+		wend = v->plim_upper;
+	}
+	if (wstart >= wend) {
+		return(0);
+	}
+	/* List the segments being tested */
+	for (i=0; i< v->msegs; i++) {
+		unsigned long start, end;
+		start = v->pmap[i].start;
+		end = v->pmap[i].end;
+		if (start <= wstart) {
+			start = wstart;
+		}
+		if (end >= wend) {
+			end = wend;
+		}
+#if 0
+		cprint(LINE_SCROLL+(2*i), 0, " (");
+		hprint(LINE_SCROLL+(2*i), 2, start);
+		cprint(LINE_SCROLL+(2*i), 10, ", ");
+		hprint(LINE_SCROLL+(2*i), 12, end);
+		cprint(LINE_SCROLL+(2*i), 20, ") ");
+
+		cprint(LINE_SCROLL+(2*i), 22, "r(");
+		hprint(LINE_SCROLL+(2*i), 24, wstart);
+		cprint(LINE_SCROLL+(2*i), 32, ", ");
+		hprint(LINE_SCROLL+(2*i), 34, wend);
+		cprint(LINE_SCROLL+(2*i), 42, ") ");
+
+		cprint(LINE_SCROLL+(2*i), 44, "p(");
+		hprint(LINE_SCROLL+(2*i), 46, v->plim_lower);
+		cprint(LINE_SCROLL+(2*i), 54, ", ");
+		hprint(LINE_SCROLL+(2*i), 56, v->plim_upper);
+		cprint(LINE_SCROLL+(2*i), 64, ") ");
+
+		cprint(LINE_SCROLL+(2*i+1),  0, "w(");
+		hprint(LINE_SCROLL+(2*i+1),  2, win.start);
+		cprint(LINE_SCROLL+(2*i+1), 10, ", ");
+		hprint(LINE_SCROLL+(2*i+1), 12, win.end);
+		cprint(LINE_SCROLL+(2*i+1), 20, ") ");
+
+		cprint(LINE_SCROLL+(2*i+1), 22, "m(");
+		hprint(LINE_SCROLL+(2*i+1), 24, v->pmap[i].start);
+		cprint(LINE_SCROLL+(2*i+1), 32, ", ");
+		hprint(LINE_SCROLL+(2*i+1), 34, v->pmap[i].end);
+		cprint(LINE_SCROLL+(2*i+1), 42, ") ");
+
+		cprint(LINE_SCROLL+(2*i+1), 44, "i=");
+		hprint(LINE_SCROLL+(2*i+1), 46, i);
+		
+		cprint(LINE_SCROLL+(2*i+2), 0, 
+			"                                        "
+			"                                        ");
+		cprint(LINE_SCROLL+(2*i+3), 0, 
+			"                                        "
+			"                                        ");
+#endif
+		if ((start < end) && (start < wend) && (end > wstart)) {
+			v->map[sg].pbase_addr = start;
+			v->map[sg].start = mapping(start);
+			v->map[sg].end = emapping(end);
+#if 0
+		hprint(LINE_SCROLL+(sg+1), 0, sg);
+		hprint(LINE_SCROLL+(sg+1), 12, v->map[sg].pbase_addr);
+		hprint(LINE_SCROLL+(sg+1), 22, start);
+		hprint(LINE_SCROLL+(sg+1), 32, end);
+		hprint(LINE_SCROLL+(sg+1), 42, mapping(start));
+		hprint(LINE_SCROLL+(sg+1), 52, emapping(end));
+		cprint(LINE_SCROLL+(sg+2), 0, 
+			"                                        "
+			"                                        ");
+#endif
+#if 0
+		cprint(LINE_SCROLL+(2*i+1), 54, ", sg=");
+		hprint(LINE_SCROLL+(2*i+1), 59, sg);
+#endif
+			sg++;
+		}
+	}
+	return (sg);
+}
+
diff --git a/major_version b/major_version
new file mode 100644
index 0000000..c6aaf52
--- /dev/null
+++ b/major_version
@@ -0,0 +1 @@
+4.99
diff --git a/make_buildnum.sh b/make_buildnum.sh
new file mode 100755
index 0000000..93927cd
--- /dev/null
+++ b/make_buildnum.sh
@@ -0,0 +1,20 @@
+#!sh  
+# FILE: make_buildnum.sh  
+version="`sed  's/^ *//' major_version`"  
+old="`sed  's/^ *//' build.number` +1"  
+echo $old | bc > build.number.temp  
+mv build.number.temp build.number  
+#versión..  
+echo "$version`sed  's/^ *//' build.number` - `date`" > version.number  
+#header  
+echo "#ifndef BUILD_NUMBER_STR" > build_number.h  
+echo "#define BUILD_NUMBER_STR \"`sed  's/^ *//' build.number`\"" >> build_number.h  
+echo "#endif" >> build_number.h  
+  
+echo "#ifndef VERSION_STR" >> build_number.h  
+echo "#define VERSION_STR \"$version`sed  's/^ *//' build.number` - `date`\"" >> build_number.h  
+echo "#endif" >> build_number.h  
+  
+echo "#ifndef VERSION_STR_SHORT" >> build_number.h  
+echo "#define VERSION_STR_SHORT \"$version`sed  's/^ *//' build.number`\"" >> build_number.h  
+echo "#endif" >> build_number.h
diff --git a/makedos.sh b/makedos.sh
new file mode 100755
index 0000000..adeb29d
--- /dev/null
+++ b/makedos.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+if [ -f memtest.bin ]
+then
+CSIZE="$(awk 'NR==16' mt86+_loader.asm | awk '{print $2}')";
+NSIZE="$(ls -l memtest.bin | awk '{print $5}')";
+sed "s/$CSIZE/$NSIZE/" mt86+_loader.asm > mt86+_loader.asm.new;
+mv mt86+_loader.asm.new mt86+_loader.asm;
+nasm mt86+_loader.asm;
+fi
diff --git a/makeiso.sh b/makeiso.sh
new file mode 100755
index 0000000..ae5a8c9
--- /dev/null
+++ b/makeiso.sh
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+# check to see if the correct tools are installed
+for X in wc genisoimage
+do
+	if [ "$(which $X)" = "" ]; then
+		echo "makeiso.sh error: $X is not in your path." >&2
+		exit 1
+	elif [ ! -x $(which $X) ]; then
+		echo "makeiso.sh error: $X is not executable." >&2
+		exit 1
+	fi 
+done
+
+#check to see if memtest.bin is present
+if [ ! -w memtest.bin ]; then 
+	echo "makeiso.sh error: cannot find memtest.bin, did you compile it?" >&2 
+	exit 1
+fi
+
+
+# enlarge the size of memtest.bin
+SIZE=$(wc -c memtest.bin | awk '{print $1}')
+FILL=$((1474560 - $SIZE))
+dd if=/dev/zero of=fill.tmp bs=$FILL count=1
+cat memtest.bin fill.tmp > memtest.img
+rm -f fill.tmp
+
+echo "Generating iso image ..."
+
+mkdir "cd"
+mkdir "cd/boot"
+mv memtest.img cd/boot
+cd cd
+
+# Create the cd.README
+echo -e "There is nothing to do here\r\r\nMemtest86+ is located on the bootsector of this CD\r\r\n" > README.TXT
+echo -e "Just boot from this CD and Memtest86+ will launch" >> README.TXT
+
+genisoimage -A "MKISOFS 1.1.2" -p "Memtest86+ 5.01" -publisher "Samuel D. <sdemeule@memtest.org>" -b boot/memtest.img -c boot/boot.catalog -V "MT501" -o memtest.iso .
+mv memtest.iso ../mt501.iso
+cd ..
+rm -rf cd
+
+echo "Done! Memtest86+ 5.01 ISO is mt501.iso"
diff --git a/memsize.c b/memsize.c
new file mode 100644
index 0000000..0fea141
--- /dev/null
+++ b/memsize.c
@@ -0,0 +1,347 @@
+/* memsize.c - MemTest-86  Version 3.3
+ *
+ * Released under version 2 of the Gnu Public License.
+ * By Chris Brady
+ */
+
+#include "test.h"
+#include "defs.h"
+#include "config.h"
+
+short e820_nr;
+short memsz_mode = SZ_MODE_BIOS;
+
+static ulong alt_mem_k;
+static ulong ext_mem_k;
+static struct e820entry e820[E820MAX];
+
+ulong p1, p2;
+ulong *p;
+
+static void sort_pmap(void);
+//static void memsize_bios(void);
+static void memsize_820(void);
+static void memsize_801(void);
+static int sanitize_e820_map(struct e820entry *orig_map,
+struct e820entry *new_bios, short old_nr);
+static void memsize_linuxbios();
+
+/*
+ * Find out how much memory there is.
+ */
+void mem_size(void)
+{
+	int i, flag=0;
+	v->test_pages = 0;
+
+	/* Get the memory size from the BIOS */
+        /* Determine the memory map */
+	if (query_linuxbios()) {
+		flag = 1;
+	} else if (query_pcbios()) {
+		flag = 2;
+	}
+
+	/* On the first time thru only */
+	/* Make a copy of the memory info table so that we can re-evaluate */
+	/* The memory map later */
+	if (e820_nr == 0 && alt_mem_k == 0 && ext_mem_k == 0) {
+		ext_mem_k = mem_info.e88_mem_k;
+		alt_mem_k = mem_info.e801_mem_k;
+		e820_nr   = mem_info.e820_nr;
+		for (i=0; i< mem_info.e820_nr; i++) {
+			e820[i].addr = mem_info.e820[i].addr;
+			e820[i].size = mem_info.e820[i].size;
+			e820[i].type = mem_info.e820[i].type;
+		}
+	}
+	if (flag == 1) {
+		memsize_linuxbios();
+	} else if (flag == 2) {
+		memsize_820();
+	}
+
+	/* Guarantee that pmap entries are in ascending order */
+	sort_pmap();
+	v->plim_lower = 0;
+	v->plim_upper = v->pmap[v->msegs-1].end;
+
+	adj_mem();
+}
+
+static void sort_pmap(void)
+{
+	int i, j;
+	/* Do an insertion sort on the pmap, on an already sorted
+	 * list this should be a O(1) algorithm.
+	 */
+	for(i = 0; i < v->msegs; i++) {
+		/* Find where to insert the current element */
+		for(j = i -1; j >= 0; j--) {
+			if (v->pmap[i].start > v->pmap[j].start) {
+				j++;
+				break;
+			}
+		}
+		/* Insert the current element */
+		if (i != j) {
+			struct pmap temp;
+			temp = v->pmap[i];
+			memmove(&v->pmap[j], &v->pmap[j+1], 
+				(i -j)* sizeof(temp));
+			v->pmap[j] = temp;
+		}
+	}
+}
+static void memsize_linuxbios(void)
+{
+	int i, n;
+	/* Build the memory map for testing */
+	n = 0;
+	for (i=0; i < e820_nr; i++) {
+		unsigned long long end;
+
+		if (e820[i].type != E820_RAM) {
+			continue;
+		}
+		end = e820[i].addr;
+		end += e820[i].size;
+		v->pmap[n].start = (e820[i].addr + 4095) >> 12;
+		v->pmap[n].end = end >> 12;
+		v->test_pages += v->pmap[n].end - v->pmap[n].start;
+		n++;
+	}
+	v->msegs = n;
+}
+static void memsize_820()
+{
+	int i, n, nr;
+	struct e820entry nm[E820MAX];
+	unsigned long long start;
+	unsigned long long end;
+
+	/* Clean up, adjust and copy the BIOS-supplied E820-map. */
+	nr = sanitize_e820_map(e820, nm, e820_nr);
+
+	/* If there is not a good 820 map use the BIOS 801/88 info */
+	if (nr < 1 || nr > E820MAX) {
+		memsize_801();
+		return;
+	}
+
+	/* Build the memory map for testing */
+	n = 0;
+	for (i=0; i<nr; i++) {
+		if (nm[i].type == E820_RAM || nm[i].type == E820_ACPI) {
+			start = nm[i].addr;
+			end = start + nm[i].size;
+
+			/* Don't ever use memory between 640 and 1024k */
+			if (start > RES_START && start < RES_END) {
+				if (end < RES_END) {
+					continue;
+				}
+				start = RES_END;
+			}
+			if (end > RES_START && end < RES_END) {
+				end = RES_START;
+			}
+			v->pmap[n].start = (start + 4095) >> 12;
+			v->pmap[n].end = end >> 12;
+			v->test_pages += v->pmap[n].end - v->pmap[n].start;
+			n++;
+#if 0			
+	 		int epmap = 0;
+	 		int lpmap = 0;
+	 		if(n > 12) { epmap = 34; lpmap = -12; }
+			hprint (11+n+lpmap,0+epmap,v->pmap[n-1].start);
+			hprint (11+n+lpmap,10+epmap,v->pmap[n-1].end);
+			hprint (11+n+lpmap,20+epmap,v->pmap[n-1].end - v->pmap[n-1].start);
+			dprint (11+n+lpmap,30+epmap,nm[i].type,0,0);	
+#endif				
+		}
+	}
+	v->msegs = n;
+}
+	
+static void memsize_801(void)
+{
+	ulong mem_size;
+
+	/* compare results from 88 and 801 methods and take the greater */
+	/* These sizes are for extended memory in 1k units. */
+
+	if (alt_mem_k < ext_mem_k) {
+		mem_size = ext_mem_k;
+	} else {
+		mem_size = alt_mem_k;
+	}
+	/* First we map in the first 640k */
+	v->pmap[0].start = 0;
+	v->pmap[0].end = RES_START >> 12;
+	v->test_pages = RES_START >> 12;
+
+	/* Now the extended memory */
+	v->pmap[1].start = (RES_END + 4095) >> 12;
+	v->pmap[1].end = (mem_size + 1024) >> 2;
+	v->test_pages += mem_size >> 2;
+	v->msegs = 2;
+}
+
+/*
+ * Sanitize the BIOS e820 map.
+ *
+ * Some e820 responses include overlapping entries.  The following 
+ * replaces the original e820 map with a new one, removing overlaps.
+ *
+ */
+static int sanitize_e820_map(struct e820entry *orig_map, struct e820entry *new_bios,
+	short old_nr)
+{
+	struct change_member {
+		struct e820entry *pbios; /* pointer to original bios entry */
+		unsigned long long addr; /* address for this change point */
+	};
+	struct change_member change_point_list[2*E820MAX];
+	struct change_member *change_point[2*E820MAX];
+	struct e820entry *overlap_list[E820MAX];
+	struct e820entry biosmap[E820MAX];
+	struct change_member *change_tmp;
+	ulong current_type, last_type;
+	unsigned long long last_addr;
+	int chgidx, still_changing;
+	int overlap_entries;
+	int new_bios_entry;
+	int i;
+
+	/*
+		Visually we're performing the following (1,2,3,4 = memory types)...
+		Sample memory map (w/overlaps):
+		   ____22__________________
+		   ______________________4_
+		   ____1111________________
+		   _44_____________________
+		   11111111________________
+		   ____________________33__
+		   ___________44___________
+		   __________33333_________
+		   ______________22________
+		   ___________________2222_
+		   _________111111111______
+		   _____________________11_
+		   _________________4______
+
+		Sanitized equivalent (no overlap):
+		   1_______________________
+		   _44_____________________
+		   ___1____________________
+		   ____22__________________
+		   ______11________________
+		   _________1______________
+		   __________3_____________
+		   ___________44___________
+		   _____________33_________
+		   _______________2________
+		   ________________1_______
+		   _________________4______
+		   ___________________2____
+		   ____________________33__
+		   ______________________4_
+	*/
+	/* First make a copy of the map */
+	for (i=0; i<old_nr; i++) {
+		biosmap[i].addr = orig_map[i].addr;
+		biosmap[i].size = orig_map[i].size;
+		biosmap[i].type = orig_map[i].type;
+	}
+
+	/* bail out if we find any unreasonable addresses in bios map */
+	for (i=0; i<old_nr; i++) {
+		if (biosmap[i].addr + biosmap[i].size < biosmap[i].addr)
+			return 0;
+	}
+
+	/* create pointers for initial change-point information (for sorting) */
+	for (i=0; i < 2*old_nr; i++)
+		change_point[i] = &change_point_list[i];
+
+	/* record all known change-points (starting and ending addresses) */
+	chgidx = 0;
+	for (i=0; i < old_nr; i++)	{
+		change_point[chgidx]->addr = biosmap[i].addr;
+		change_point[chgidx++]->pbios = &biosmap[i];
+		change_point[chgidx]->addr = biosmap[i].addr + biosmap[i].size;
+		change_point[chgidx++]->pbios = &biosmap[i];
+	}
+
+	/* sort change-point list by memory addresses (low -> high) */
+	still_changing = 1;
+	while (still_changing)	{
+		still_changing = 0;
+		for (i=1; i < 2*old_nr; i++)  {
+			/* if <current_addr> > <last_addr>, swap */
+			/* or, if current=<start_addr> & last=<end_addr>, swap */
+			if ((change_point[i]->addr < change_point[i-1]->addr) ||
+				((change_point[i]->addr == change_point[i-1]->addr) &&
+				 (change_point[i]->addr == change_point[i]->pbios->addr) &&
+				 (change_point[i-1]->addr != change_point[i-1]->pbios->addr))
+			   )
+			{
+				change_tmp = change_point[i];
+				change_point[i] = change_point[i-1];
+				change_point[i-1] = change_tmp;
+				still_changing=1;
+			}
+		}
+	}
+
+	/* create a new bios memory map, removing overlaps */
+	overlap_entries=0;	 /* number of entries in the overlap table */
+	new_bios_entry=0;	 /* index for creating new bios map entries */
+	last_type = 0;		 /* start with undefined memory type */
+	last_addr = 0;		 /* start with 0 as last starting address */
+	/* loop through change-points, determining affect on the new bios map */
+	for (chgidx=0; chgidx < 2*old_nr; chgidx++)
+	{
+		/* keep track of all overlapping bios entries */
+		if (change_point[chgidx]->addr == change_point[chgidx]->pbios->addr)
+		{
+			/* add map entry to overlap list (> 1 entry implies an overlap) */
+			overlap_list[overlap_entries++]=change_point[chgidx]->pbios;
+		}
+		else
+		{
+			/* remove entry from list (order independent, so swap with last) */
+			for (i=0; i<overlap_entries; i++)
+			{
+				if (overlap_list[i] == change_point[chgidx]->pbios)
+					overlap_list[i] = overlap_list[overlap_entries-1];
+			}
+			overlap_entries--;
+		}
+		/* if there are overlapping entries, decide which "type" to use */
+		/* (larger value takes precedence -- 1=usable, 2,3,4,4+=unusable) */
+		current_type = 0;
+		for (i=0; i<overlap_entries; i++)
+			if (overlap_list[i]->type > current_type)
+				current_type = overlap_list[i]->type;
+		/* continue building up new bios map based on this information */
+		if (current_type != last_type)	{
+			if (last_type != 0)	 {
+				new_bios[new_bios_entry].size =
+					change_point[chgidx]->addr - last_addr;
+				/* move forward only if the new size was non-zero */
+				if (new_bios[new_bios_entry].size != 0)
+					if (++new_bios_entry >= E820MAX)
+						break; 	/* no more space left for new bios entries */
+			}
+			if (current_type != 0)	{
+				new_bios[new_bios_entry].addr = change_point[chgidx]->addr;
+				new_bios[new_bios_entry].type = current_type;
+				last_addr=change_point[chgidx]->addr;
+			}
+			last_type = current_type;
+		}
+	}
+	return(new_bios_entry);
+}
diff --git a/memtest.bin.lds b/memtest.bin.lds
new file mode 100644
index 0000000..702cdb1
--- /dev/null
+++ b/memtest.bin.lds
@@ -0,0 +1,15 @@
+OUTPUT_FORMAT("binary")
+OUTPUT_ARCH("i386")
+
+ENTRY(_main);
+SECTIONS {
+	. = 0;
+	.bootsect : { *(.bootsect) }
+	.setup : { *(.setup) }
+	.memtest : { 
+		_start = . ;
+		*(.data) 
+		_end = . ;
+	}
+	_syssize = (_end - _start + 15) >> 4;
+}
diff --git a/memtest.lds b/memtest.lds
new file mode 100644
index 0000000..bbb190a
--- /dev/null
+++ b/memtest.lds
@@ -0,0 +1,11 @@
+OUTPUT_FORMAT("elf32-i386");
+OUTPUT_ARCH(i386);
+
+ENTRY(_start); 
+SECTIONS {
+	. = 0x10000;
+	_start = . ;
+	.data : {
+		*(.data)
+	}
+}
diff --git a/memtest_shared.lds b/memtest_shared.lds
new file mode 100644
index 0000000..603f012
--- /dev/null
+++ b/memtest_shared.lds
@@ -0,0 +1,53 @@
+OUTPUT_FORMAT("elf32-i386");
+OUTPUT_ARCH(i386);
+
+ENTRY(startup_32); 
+SECTIONS {
+	. = 0;
+	.text : {
+		_start = .;
+		*(.text)
+		*(.text.*)
+		*(.plt)
+		_etext = . ;
+	} = 0x9090
+	.rodata : {
+		*(.rodata)
+		*(.rodata.*)
+	}
+	.dynsym     : { *(.dynsym) }
+	.dynstr     : { *(.dynstr) }
+	.hash       : { *(.hash) }
+	.gnu.hash   : { *(.gnu.hash) }
+	.dynamic    : { *(.dynamic) }
+
+	.rel.text    : { *(.rel.text   .rel.text.*) }
+	.rel.rodata  : { *(.rel.rodata .rel.rodata.*) }
+	.rel.data    : { *(.rel.data   .rel.data.*) }
+	.rel.got     : { *(.rel.got    .rel.got.*) }
+	.rel.plt     : { *(.rel.plt    .rel.plt.*) }
+
+	. = ALIGN(4);
+	.data : {
+		 _data = .; 
+		*(.data) 
+		*(.data.*) 
+	}
+	.got : {
+		*(.got.plt)
+		*(.got)
+		_edata = . ;
+	}
+	. = ALIGN(4);
+	.bss : { 
+		_bss = .;
+		*(.dynbss)
+		*(.bss) 
+		*(.bss.*) 
+		*(COMMON) 
+		/* _end must be at least 256 byte aligned */
+		. = ALIGN(256); 
+		_end = .;
+	}
+	/DISCARD/ : { *(*) }	
+}
diff --git a/msr.h b/msr.h
new file mode 100644
index 0000000..af873a0
--- /dev/null
+++ b/msr.h
@@ -0,0 +1,145 @@
+#ifndef __ASM_MSR_H
+#define __ASM_MSR_H
+
+/*
+ * Access to machine-specific registers (available on 586 and better only)
+ * Note: the rd* operations modify the parameters directly (without using
+ * pointer indirection), this allows gcc to optimize better
+ */
+ 
+#define __FIXUP_ALIGN ".align 8" 
+#define __FIXUP_WORD ".quad"
+#define EFAULT	14 /* Bad address */
+
+#define rdmsr(msr,val1,val2) \
+     __asm__ __volatile__("rdmsr" \
+			  : "=a" (val1), "=d" (val2) \
+			  : "c" (msr) : "edi")
+
+/*
+#define rdmsr_safe(msr,val1,val2) ({\
+     int _rc; \
+     __asm__ __volatile__( \
+         "1: rdmsr\n2:\n" \
+         ".section .fixup,\"ax\"\n" \
+         "3: movl %5,%2\n; jmp 2b\n" \
+         ".previous\n" \
+         ".section __ex_table,\"a\"\n" \
+         "   "__FIXUP_ALIGN"\n" \
+         ".previous\n" \
+         : "=a" (val1), "=d" (val2), "=&r" (_rc) \
+				 : "c" (msr), "2" (0), "i" (-EFAULT)); \
+				 	_rc; })				 	
+*/
+
+#define wrmsr(msr,val1,val2) \
+     __asm__ __volatile__("wrmsr" \
+			  : /* no outputs */ \
+			  : "c" (msr), "a" (val1), "d" (val2))
+
+#define rdtsc(low,high) \
+     __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
+
+#define rdtscl(low) \
+     __asm__ __volatile__("rdtsc" : "=a" (low) : : "edx")
+
+#define rdtscll(val) \
+     __asm__ __volatile__("rdtsc" : "=A" (val))
+
+#define write_tsc(val1,val2) wrmsr(0x10, val1, val2)
+
+#define rdpmc(counter,low,high) \
+     __asm__ __volatile__("rdpmc" \
+			  : "=a" (low), "=d" (high) \
+			  : "c" (counter))
+
+/* symbolic names for some interesting MSRs */
+/* Intel defined MSRs. */
+#define MSR_IA32_P5_MC_ADDR		0
+#define MSR_IA32_P5_MC_TYPE		1
+#define MSR_IA32_PLATFORM_ID		0x17
+#define MSR_IA32_EBL_CR_POWERON		0x2a
+
+#define MSR_IA32_APICBASE		0x1b
+#define MSR_IA32_APICBASE_BSP		(1<<8)
+#define MSR_IA32_APICBASE_ENABLE	(1<<11)
+#define MSR_IA32_APICBASE_BASE		(0xfffff<<12)
+
+#define MSR_IA32_UCODE_WRITE		0x79
+#define MSR_IA32_UCODE_REV		0x8b
+
+#define MSR_IA32_BBL_CR_CTL		0x119
+
+#define MSR_IA32_MCG_CAP		0x179
+#define MSR_IA32_MCG_STATUS		0x17a
+#define MSR_IA32_MCG_CTL		0x17b
+
+#define MSR_IA32_THERM_CONTROL		0x19a
+#define MSR_IA32_THERM_INTERRUPT	0x19b
+#define MSR_IA32_THERM_STATUS		0x19c
+#define MSR_IA32_MISC_ENABLE		0x1a0
+#define MSR_IA32_TEMPERATURE_TARGET		0x1a2
+
+#define MSR_IA32_DEBUGCTLMSR		0x1d9
+#define MSR_IA32_LASTBRANCHFROMIP	0x1db
+#define MSR_IA32_LASTBRANCHTOIP		0x1dc
+#define MSR_IA32_LASTINTFROMIP		0x1dd
+#define MSR_IA32_LASTINTTOIP		0x1de
+
+#define MSR_IA32_MC0_CTL		0x400
+#define MSR_IA32_MC0_STATUS		0x401
+#define MSR_IA32_MC0_ADDR		0x402
+#define MSR_IA32_MC0_MISC		0x403
+
+#define MSR_P6_PERFCTR0			0xc1
+#define MSR_P6_PERFCTR1			0xc2
+#define MSR_P6_EVNTSEL0			0x186
+#define MSR_P6_EVNTSEL1			0x187
+
+#define MSR_IA32_PERF_STATUS		0x198
+#define MSR_IA32_PERF_CTL		0x199
+
+/* AMD Defined MSRs */
+#define MSR_K6_EFER			0xC0000080
+#define MSR_K6_STAR			0xC0000081
+#define MSR_K6_WHCR			0xC0000082
+#define MSR_K6_UWCCR			0xC0000085
+#define MSR_K6_EPMR			0xC0000086
+#define MSR_K6_PSOR			0xC0000087
+#define MSR_K6_PFIR			0xC0000088
+
+#define MSR_K7_EVNTSEL0			0xC0010000
+#define MSR_K7_PERFCTR0			0xC0010004
+#define MSR_K7_HWCR			0xC0010015
+#define MSR_K7_CLK_CTL			0xC001001b
+#define MSR_K7_FID_VID_CTL		0xC0010041
+#define MSR_K7_VID_STATUS		0xC0010042
+
+/* Centaur-Hauls/IDT defined MSRs. */
+#define MSR_IDT_FCR1			0x107
+#define MSR_IDT_FCR2			0x108
+#define MSR_IDT_FCR3			0x109
+#define MSR_IDT_FCR4			0x10a
+
+#define MSR_IDT_MCR0			0x110
+#define MSR_IDT_MCR1			0x111
+#define MSR_IDT_MCR2			0x112
+#define MSR_IDT_MCR3			0x113
+#define MSR_IDT_MCR4			0x114
+#define MSR_IDT_MCR5			0x115
+#define MSR_IDT_MCR6			0x116
+#define MSR_IDT_MCR7			0x117
+#define MSR_IDT_MCR_CTRL		0x120
+
+/* VIA Cyrix defined MSRs*/
+#define MSR_VIA_FCR			0x1107
+#define MSR_VIA_LONGHAUL		0x110a
+#define MSR_VIA_BCR2			0x1147
+
+/* Transmeta defined MSRs */
+#define MSR_TMTA_LONGRUN_CTRL		0x80868010
+#define MSR_TMTA_LONGRUN_FLAGS		0x80868011
+#define MSR_TMTA_LRTI_READOUT		0x80868018
+#define MSR_TMTA_LRTI_VOLT_MHZ		0x8086801a
+
+#endif /* __ASM_MSR_H */
diff --git a/mt86+_loader b/mt86+_loader
new file mode 100644
index 0000000..030ea1a
--- /dev/null
+++ b/mt86+_loader
Binary files differ
diff --git a/mt86+_loader.asm b/mt86+_loader.asm
new file mode 100644
index 0000000..8472262
--- /dev/null
+++ b/mt86+_loader.asm
@@ -0,0 +1,231 @@
+; A loader for www.memtest.org images, by Eric Auer 2003.
+; This assumes that the image starts with the boot sector,
+; which has the size of setup.S in sectors in a byte at offset
+; 1f1h (497). Further, I assume setup.S directly after the boot
+; sector and the actual memtest head.S after setup.S ...
+
+; This version is derived from memtestL loader, which loads
+; memtest.bin from a separate file. This version is meant to
+; be used like (DOS / Unix variants):
+; copy /b memteste.bin + memtest.bin memtest.exe
+; cat memteste.bin memtest.bin > memtest.exe
+; The good thing is that you get a single file which can be
+; compressed, for example with http://upx.sf.net/ (UPX).
+
+%define fullsize (150024 + buffer - exeh)
+	; 150024 is the size of memtest86+ V5.01, adjust as needed!
+
+%define stacksize 2048
+%define stackpara ((stacksize + 15) / 16)
+
+	; the trick is that NASM believes the header would be part
+	; of the loaded image, so we "org 20h bytes too early" to fix:
+	org 0e0h	; NASM thinks after header we have 100h
+			; which is what we want it to think.
+
+exeh:	db "MZ"
+	dw fullsize % 512		; how much to load from
+	dw (fullsize + 511) / 512	;      .exe to RAM
+	dw 0		; no relocations used
+	dw 2		; header size is 2 * 16 bytes
+	dw stackpara	; minimum heap is 128 * 16 bytes, for stack
+	dw stackpara	; we do not need more heap either
+	dw (fullsize + 15) / 16		; SS is after file
+			; segment offsets are relative to PSPseg+10h
+			; initial DS and ES point to PSPseg, and f