/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    selftest.c

Abstract:

    This module contains the routines that perform the selftest of
    the IO devices.

Author:

    Lluis Abello (lluis) 03-Jan-1991

Environment:


Revision History:

--*/

#include "fwp.h"
#include "iodevice.h"
#include "led.h"
#include "selfmap.h"
#include "selftest.h"
#include "ioaccess.h"
#include "fwstring.h"
#include "stdio.h"


typedef CACHEERR *PCACHEERR;

#define SetCursorPosition(Row,Column) \
    FwPrint("\x9B"#Row";"#Column"H")
#define MoveCursorLeft(Spaces) \
    FwPrint("\x9B"#Spaces"D")
#define ClearScreen()   FwPrint("\x9B""2J");

PUCHAR  TranslationTable = "0123456789ABCDEF";
UCHAR   StationAddress[6];
BOOLEAN ProcessorBEnabled = FALSE;
BOOLEAN ValidEthernetAddress;  // True if station address is OK False Otherwise
BOOLEAN ConfigurationBit;      // read value from diagnostic register
BOOLEAN LoopOnError;           // read value from diagnostic register
BOOLEAN IgnoreErrors;          // read value from diagnostic register
extern volatile LONG TimerTicks;      // Counter for timeouts
BOOLEAN KeyboardInitialized;   // True if the keyboard is initialized
BOOLEAN MctadrRev2;            // True if this is a rev2 ASIC

PRTL_ALLOCATE_STRING_ROUTINE RtlAllocateStringRoutine =
    (PRTL_ALLOCATE_STRING_ROUTINE)FwAllocatePool;
PRTL_FREE_STRING_ROUTINE RtlFreeStringRoutine = FwpFreeStub;


#ifndef DUO
//
// Table of memory size for each valid configuration.
//
UCHAR ConfigurationSize[64]=
        {      0,      0,      0,      0,      4,     16,      0,      0,
               0,      0,      0,      0,      8,     20,     20,     32,
               0,      0,      0,      0,      8,     32,      0,      0,
               0,      0,      0,      0,     12,     36,     24,     48,
               0,      0,      0,      0,      0,      0,      0,      0,
               0,      0,      0,      0,      0,      0,      0,      0,
               0,      0,      0,      0,      0,      0,      0,      0,
               0,      0,      0,      0,     16,     40,     40,     64
        };

USHORT FConfigurationSize[256]=
//             0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F
        {      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 0
               4, 16, 64,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 1
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 2
               8, 20, 68,  0,  0, 32, 80,  0,  0,  0,128,  0,  0,  0,  0,  0, // 3
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 4
               8, 32,128,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 5
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 6
              12, 36,132,  0,  0, 48,144,  0,  0,  0,192,  0,  0,  0,  0,  0, // 7
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 8
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // 9
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // A
               0, 24, 72,  0,  0,  0, 96,  0,  0,  0,  0,  0,  0,  0,  0,  0, // B
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // C
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // D
               0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, // E
              16, 40,136,  0,  0, 64,160,  0,  0,  0,256,  0,  0,  0,  0,  0  // F
        };
#else
//
// This table indexed with the low 4 bits of the MemoryGroup config register,
// returns the size of memory installed in the group in Megabytes.
//
UCHAR   GroupSize[16]=
            {  0,  0,  0,  0,   // No SIMM installes
               4, 16, 64,  0,   // Single Sided
               0,  0,  0,  0,   // Reserved
               8, 32,128,  0    // Double Sided.
            };
#endif
//
// This variable is initialized to the size of the memory in Megabytes
//

ULONG MemorySize;

ULONG KSEG_BASE[3]={KSEG1_BASE,KSEG0_BASE,KSEG0_BASE};
ULONG XOR_KSEG_WRITE[3]={0x00000000,0xFFFFFFFF,0x01010101};
ULONG XOR_KSEG_READ[3]={0x00000000,0xFFFFFFFF,0x01010101};


//
// Declare function prototypes.
//
VOID
ReportJazzCacheErrorException(
    IN ULONG CacheErr
    );

VOID
ReportDuoCacheErrorException(
    IN ULONG CacheErr
    );

VOID
LoadDoubleWord(
    IN ULONG Address,
    OUT PVOID Result
    );

VOID
InitializePCR(
    );

VOID
FwBootSystem(
    IN VOID
    );

VOID
RomSelftest(
    IN VOID
    );

ARC_STATUS
SerialBootWrite(
    CHAR  Char,
    ULONG SP
    );

BOOLEAN
ValidNvram(
    OUT PUCHAR StationAddress
    );

VOID
WriteMemoryAddressTest(
    ULONG StartAddress,
    ULONG Size,
    ULONG Xorpattern
    );

PULONG
CheckMemoryAddressTest(
    ULONG StartAddress,
    ULONG Size,
    ULONG Xorpattern,
    ULONG LedDisplayValue
    );
VOID
WriteVideoMemoryAddressTest(
    ULONG StartAddress,
    ULONG Size
    );
ULONG
CheckVideoMemoryAddressTest(
    ULONG StartAddress,
    ULONG Size
    );

VOID
SetSmallDCacheBlock(
    IN VOID
    );

VOID
WildZeroMemory(
    ULONG StartAddress,
    ULONG Size
    );

VOID
SerialBootSetup(
    IN ULONG FileId
    );
VOID
FwSelftest(
    IN ULONG Cause,
    IN ULONG Arg1
    );


VOID
FwSelftest(
    IN ULONG Cause,
    IN ULONG Arg1
    )
/*++

Routine Description:

    This routine is the c routine called from the ROM. It must be placed
    at the beginning of the C code because the ROM copies the code from this
    point to the end and then jumps to it.

Arguments:

    Cause  - 0 on a normal reset/powerup
             1 on a softreset.
             2 if an NMI occurred.
             3 if a cache error/parity occurred.

    Arg1   - Error EPC if an NMI occurred
             CacheErr register if a cache error occurred

Return Value:

    Never returns.

--*/
{
    ULONG IntSrc;
#ifdef DUO
    ULONG Config;
    ULONG MemConfig;
    LONG  Group;
    CHAR Diag;
#endif
    CHAR String[128];
    ULONG ProcessorBootStatus;


#ifndef DUO
    //
    // Initialize MemorySize to the size of memory in MegaBytes.  Look for the
    // MCT_ADR REV2 Map Prom bit in the configuration register, if there this
    // is a REV2, otherwise REV1.
    //

    if (DMA_CONTROL->Configuration.Long & 0x400) {
        MemorySize = FConfigurationSize[DMA_CONTROL->Configuration.Long & 0xFF];
        MctadrRev2 = TRUE;
    } else {
        MemorySize = ConfigurationSize[DMA_CONTROL->Configuration.Long & 0x3F];
        MctadrRev2 = FALSE;
    }

#else
    if (READ_REGISTER_ULONG(&DMA_CONTROL->WhoAmI.Long)) {
        if (Cause == 2) {
            //
            // NMI
            //
            sprintf(String,
                    ST_NMI_MSG,
                    Arg1
                   );
            FwPrint(ST_PROCESSOR_B_MSG);
            FwPrint(String);
            FwPrint(FW_SYSTEM_HALT_MSG);
            for (;;) {
            }
        } else if (Cause == 3) {
            //
            // Cache error
            //
        } else {
            //
            // soft or cold reset.
            //
            ProcessorBMain();
        }
    }

    MemorySize = 0;

    for(Group=0;Group < 4; Group++) {
        MemConfig = DMA_CONTROL->MemoryConfig[Group].Long;
        MemorySize += GroupSize[MemConfig&0xF]; // plus size of group.
    }

#endif

    //
    // Set variables according to the bits in configuration register
    //

    ConfigurationBit = FALSE;
    IgnoreErrors = FALSE;
    LoopOnError = FALSE;
    DisplayOutput = FALSE;
    SerialOutput = FALSE;
    KeyboardInitialized = FALSE;

    //
    // Look for configuration register.
    //
#ifdef DUO
    Diag=READ_REGISTER_UCHAR(DIAGNOSTIC_VIRTUAL_BASE);
    if ((Diag & LOOP_ON_ERROR_MASK) == 0) {
        LoopOnError = TRUE;
    }
    if ((Diag & CONFIGURATION_MASK) == 0) {
        ConfigurationBit = TRUE;
    }
    if ((Diag & IGNORE_ERRORS_MASK) == 0) {
        IgnoreErrors = TRUE;
    }
#endif


//    Diag=READ_REGISTER_UCHAR(DIAGNOSTIC_VIRTUAL_BASE) & DIAGNOSTIC_MASK;
//    switch (Diag) {
//        case      0: IgnoreErrors = TRUE;
//                     break;
//        case (1<<7): LoopOnError = TRUE;
//                     break;
//        case (1<<6): ConfigurationBit = TRUE;
//                     break;
//    }

    //
    // Set interrupt lines to a known state.
    //

    WRITE_REGISTER_UCHAR(&SP1_WRITE->ModemControl,0x08);
    WRITE_REGISTER_UCHAR(&SP2_WRITE->ModemControl,0x08);
#ifndef DUO
    WRITE_REGISTER_UCHAR(&FLOPPY_WRITE->DigitalOutput,0x08);
#endif
    READ_REGISTER_UCHAR(&PARALLEL_READ->Status);

    //
    // Initialize the system parameter block.
    //

    SYSTEM_BLOCK->Signature = 0x53435241;
    SYSTEM_BLOCK->Length = sizeof(SYSTEM_PARAMETER_BLOCK);
    SYSTEM_BLOCK->Version = ARC_VERSION;
    SYSTEM_BLOCK->Revision = ARC_REVISION;

    //
    // Allocate the restart block.
    //

    SYSTEM_BLOCK->RestartBlock = (PRESTART_BLOCK)((ULONG)((PUCHAR)SYSTEM_BLOCK + sizeof(SYSTEM_PARAMETER_BLOCK)) | KSEG1_BASE);

    SYSTEM_BLOCK->DebugBlock = NULL;
#ifdef  DUO
    //
    // Link the second restart block for processor B.
    //
    SYSTEM_BLOCK->RestartBlock->NextRestartBlock = (PRESTART_BLOCK)(((ULONG)SYSTEM_BLOCK + sizeof(SYSTEM_PARAMETER_BLOCK) + sizeof(RESTART_BLOCK)) | KSEG1_BASE);

    SYSTEM_BLOCK->RestartBlock->NextRestartBlock->NextRestartBlock = NULL;
    SYSTEM_BLOCK->FirmwareVector = (PVOID)((ULONG)((PUCHAR)SYSTEM_BLOCK + sizeof(SYSTEM_PARAMETER_BLOCK) + 2*sizeof(RESTART_BLOCK)) | KSEG1_BASE);
#else
    SYSTEM_BLOCK->RestartBlock->NextRestartBlock = NULL;
    SYSTEM_BLOCK->FirmwareVector =
                (PVOID)((PUCHAR)SYSTEM_BLOCK + sizeof(SYSTEM_PARAMETER_BLOCK) + sizeof(RESTART_BLOCK));
#endif


    SYSTEM_BLOCK->FirmwareVectorLength = (ULONG)MaximumRoutine * sizeof(ULONG);
    SYSTEM_BLOCK->VendorVectorLength = (ULONG)MaximumVendorRoutine * sizeof(ULONG);
    SYSTEM_BLOCK->VendorVector =
                (PVOID)(SYSTEM_BLOCK->FirmwareVector +
                        SYSTEM_BLOCK->FirmwareVectorLength);

    //
    // If the configuration bit is set, go to the serial port.
    //

    PutLedDisplay(LED_NVRAM);
    if (ConfigurationBit) {
        //
        // Test the serial port if not a soft reset.
        //
        if (Cause == 0) {
            ExecuteTest((TestRoutine)RomSerialResetTest,LED_SERIAL_RESET);
            ExecuteTest((TestRoutine)RomSerial1RegistersTest,LED_SERIAL1_REG);
            ExecuteTest((TestRoutine)RomSerial2RegistersTest,LED_SERIAL2_REG);
            ExecuteTest((TestRoutine)RomSerial1LoopBackTest,LED_SERIAL1_LBACK);
            ExecuteTest((TestRoutine)RomSerial2LoopBackTest,LED_SERIAL2_LBACK);
            PutLedDisplay(LED_SERIAL_INIT);
        }
        //
        // Initialize the serial port as there is no configuration
        // data to initialize the video.
        //
        SerialBootSetup(COMPORT2_VIRTUAL_BASE);
        SerialOutput = TRUE;
        ClearScreen()

    } else {

        //
        // If there is a valid Ethernet station address, protect it.
        //

        if (ValidNvram(StationAddress) == TRUE) {
//            WRITE_REGISTER_ULONG (&DMA_CONTROL->SystemSecurity.Long,READ_ONLY_DISABLE_WRITE);
        }

        //
        // Test the video memory.
        // Doesn't work for VXL
        //
        if (Cause == 0) {
//            ExecuteTest((TestRoutine)RomVideoMemory,LED_VIDEOMEM);
            PutLedDisplay(LED_VIDEO_CONTROLLER);
        }

//TEMPTEMP
//        if (DisplayBootInitialize() != ESUCCESS) {
//            //
//            // If video is broken and the NVRAM was ok and the config
//            // bit was not set do not attempt to write to the serial port.
//            // No output device is available, so hang.
//            //
//            PutLedDisplay((LED_BLINK << 16) | LED_VIDEO_CONTROLLER);
//        } else {
//            //
//            // Video is ready to display messages.
//            //
//            DisplayOutput=TRUE;
//        }
//TEMPTEMP

        DisplayBootInitialize();
        DisplayOutput=TRUE;
    }
    if (Cause == 2) {
        //
        // This is an NMI. Display a message and hang.
        //
        sprintf(String,
               ST_NMI_MSG,
               Arg1
               );
        FwPrint(String);
        //
        // Read The Interrupt source register and check the cause of the NMI.
        //
#ifndef DUO
        IntSrc = READ_REGISTER_ULONG(&DMA_CONTROL->InterruptSource.Long);
        if (IntSrc & (1<<9)) {
            sprintf(String,
                    ST_INVALID_ADDRESS_MSG,
                    READ_REGISTER_ULONG(&DMA_CONTROL->InvalidAddress.Long)
                    );
            FwPrint(String);
        }
        if (IntSrc & (1<<10)) {
            sprintf(String,
                    ST_IO_CACHE_ADDRESS_MSG,
                    READ_REGISTER_ULONG(&DMA_CONTROL->MemoryFailedAddress.Long)
                    );
            FwPrint(String);
        }
        FwPrint(FW_SYSTEM_HALT_MSG);
#else
        IntSrc = READ_REGISTER_ULONG(&DMA_CONTROL->NmiSource.Long);
        if (IntSrc & (1<<1)) {
            sprintf(String,
                    ST_INVALID_ADDRESS_MSG,
                    READ_REGISTER_ULONG(&DMA_CONTROL->InvalidAddress.Long)
                    );
            FwPrint(String);
        }
        if (IntSrc & (1<<2)) {
            sprintf(String,
                    ST_IO_CACHE_ADDRESS_MSG,
                    READ_REGISTER_ULONG(&DMA_CONTROL->MemoryFailedAddress.Long)
                    );
            FwPrint(String);
        }

        //
        // Notify processor B
        //
        WRITE_REGISTER_ULONG(&DMA_CONTROL->IpInterruptRequest.Long,2);

#endif

        //
        // Hang blinking LED.
        //
        PutLedDisplay((LED_BLINK << 16) | LED_NMI);
    }
    if (Cause == 3) {
#ifndef DUO
        ReportJazzCacheErrorException(Arg1);
#else
        ReportDuoCacheErrorException(Arg1);
#endif
        PutLedDisplay((LED_BLINK << 16) | LED_PARITY);
    }
    //
    // Reset the EISA bus.  This is required because the EBC reset line on Jazz
    // is connected wrong.
    //

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->ExtendedNmiResetControl,0x01);
    FwStallExecution(10);
    WRITE_REGISTER_UCHAR(&EISA_CONTROL->ExtendedNmiResetControl,0x00);
    FwStallExecution(10);

    if (Cause == 0) {
        //
        // Execute selftest if it's not a soft reset.
        //

        FwPrintVersion();
        FwPrint(FW_CRLF_MSG);
        RomSelftest();
    } else {
        //
        // Need to initialize whatever the Selftest initializes.
        //
        PutLedDisplay(LED_KEYBOARD_CTRL);
        if (InitKeyboardController()) {
            FwPrint(ST_KEYBOARD_ERROR_MSG);
        }
        PutLedDisplay(LED_ISP);
        RomInitISP();
    }

    //
    // Initialize master processor boot status.
    //
    ProcessorBootStatus = 0;
    ((PBOOT_STATUS)&ProcessorBootStatus)->ProcessorReady = 1;
    ((PBOOT_STATUS)&ProcessorBootStatus)->ProcessorRunning = 1;
    ((PBOOT_STATUS)&ProcessorBootStatus)->ProcessorStart = 1;
    ((PBOOT_STATUS)&ProcessorBootStatus)->BootStarted = 1;
    SYSTEM_BLOCK->RestartBlock->BootStatus = *((PBOOT_STATUS)&ProcessorBootStatus);

    //
    // Zero the entire Memory.
    //
    PutLedDisplay(LED_ZEROMEM);

//    WildZeroMemory(FW_TOP_ADDRESS,MEMORY_SIZE - FW_TOP_ADDRESS);
    RtlZeroMemory((PVOID)(FW_TOP_ADDRESS | KSEG0_BASE),MEMORY_SIZE - FW_TOP_ADDRESS);

#ifdef DUO

    //
    // Initialize processor B boot status to not ready.
    //
    ProcessorBootStatus = 0;

    //
    // Processor B Has been idle all this time. Check if it is present
    // and enable it.
    //


    Diag=READ_REGISTER_UCHAR(DIAGNOSTIC_VIRTUAL_BASE);


    if (!(Diag & 1)) {
        InitializePCR();
        FwPrint(ST_ENABLE_PROCESSOR_B_MSG);

//        HalSweepDcache();
        //
        // TEMPTEMP we want to see all icache misses for now
        //
//        HalSweepIcache();

        //
        // end TEMPTEMP
        //

        Config = READ_REGISTER_ULONG(&DMA_CONTROL->Configuration.Long);
        WRITE_REGISTER_ULONG(&DMA_CONTROL->Configuration.Long,Config | ENABLE_PROCESSOR_B);

        //
        // Processor B has been enabled. It will execute the rom code to initialize
        // its caches and then call ProcessorBMain from where it will issue
        // an Ip interrupt to us.
        //
        if (WaitForIpInterrupt(5000) == FALSE) {
            FwPrint(ST_TIMEOUT_PROCESSOR_B_MSG);
        } else {
            FwPrint(FW_OK_MSG);

            //
            // If Processor B passes selftest, set processor ready bit in boot status.
            // Otherwise disable processor B
            //
            if (ProcessorBSelftest()) {
                ((PBOOT_STATUS)&ProcessorBootStatus)->ProcessorReady = 1;
                ProcessorBEnabled = TRUE;
            }
        }
        //
        //  If processor B failed selftest or never received its IP interrupt
        //  Disable It.
        //
        if (!ProcessorBEnabled) {
            WRITE_REGISTER_ULONG(&DMA_CONTROL->Configuration.Long,Config);
            FwPrint(ST_PROCESSOR_B_DISABLED_MSG);
        }
    } else {
        FwPrint(ST_PROCESSOR_B_NOT_PRESENT_MSG);
    }

    SYSTEM_BLOCK->RestartBlock->NextRestartBlock->BootStatus = *((PBOOT_STATUS)&ProcessorBootStatus);


    //
    // Disable all interrupts except IP and Local Device (for keyboard)
    //

    WRITE_REGISTER_ULONG(&DMA_CONTROL->InterruptEnable.Long,
                         (ENABLE_IP_INTERRUPTS | ENABLE_DEVICE_INTERRUPTS));

#endif

    InitializePCR();
    PutLedDisplay(LED_BLANK<<4);
    FwBootSystem();

    //
    // Hang if we come back.
    //

    PutLedDisplay((LED_BLINK << 16) | 0xBB);
}


VOID
RomSelftest (
    IN VOID
    )
/*++

Routine Description:

    This routine implements the self-test video phase.

Arguments:

    None.

Return Value:

    None.

--*/
{
ULONG Status;
ULONG i,j,Test,CurrentK;
LONG  BlockSize;
CHAR String[128];
ULONG SIZE_OF_UNTESTED_MEMORY = MEMORY_SIZE-(FW_TOP_ADDRESS);

//TEMPTEMP
PULONG Cached;

    PutLedDisplay(LED_MAIN_MEMORY);

    //
    // Display in the screen that the memory is being tested.
    //

    SetCursorPosition(4,1);
//    sprintf(String,"Total memory is %d MBytes (%x)\r\n",MemorySize,DMA_CONTROL->Configuration.Long);
//    FwPrint(String);
    FwPrint(ST_MEMORY_TEST_MSG);

    //
    // Zero the memory so that memtest can run cached.
    //
    //WildZeroMemory(FW_TOP_ADDRESS,SIZE_OF_UNTESTED_MEMORY);

    for (Test=0;Test<1;Test++) {        // 3!!
        //
        // display on the screen and LED that we start writing.
        //
//        SetCursorPosition(1,14);
//        sprintf(String,"%x",Test+1);
//        FwPrint(String);
        PutLedDisplay(LED_MAIN_MEMORY+Test+1);
        //
        // Write address test to the remaining untested memory
        //
        WriteMemoryAddressTest(KSEG0_BASE+FW_TOP_ADDRESS,
                               SIZE_OF_UNTESTED_MEMORY,
                               XOR_KSEG_WRITE[Test]);

        //
        // Adjust first block size to align the tested memory to a block boundary
        //

        BlockSize = KB_PER_TEST - TESTED_KB;
        while (BlockSize <= 0) {
           BlockSize += KB_PER_TEST;
        }
        CurrentK = TESTED_KB;
        do {

            //
            // Display what has been tested so far.
            //

            SetCursorPosition(4,15);
            sprintf(String,"%6d",CurrentK);
            FwPrint(String);
            Cached = CheckMemoryAddressTest(
                                     KSEG0_BASE + (CurrentK << 10), // cached address
                                     BlockSize << 10, // test up to next block boundary
                                     XOR_KSEG_READ[Test], // Xor Pattern
                                     (LED_BLINK << 16) + LED_MAIN_MEMORY);
            if (Cached != 0) {

                //
                // Memory Error found.
                //

                FwPrint(ST_MEMORY_ERROR_MSG);

                if (LoopOnError) {
                    // display in the LED that we start looping.
                    PutLedDisplay((LED_LOOP_ERROR << 16)+LED_MAIN_MEMORY);
                    for (;;) {
                       CheckMemoryAddressTest(
                                     KSEG0_BASE + (CurrentK << 10), // cached address
                                     BlockSize << 10, // test up to next block boundary
                                     XOR_KSEG_READ[Test], // Xor Pattern
                                     (LED_BLINK << 16) + LED_MAIN_MEMORY);
                    }
                } else {
                    FwPrint(ST_HANG_MSG);
                    // hang blinking the LED
                    PutLedDisplay((LED_BLINK << 16) + LED_MAIN_MEMORY + Test + 1);
                }

            }
            CurrentK += BlockSize;
            BlockSize = KB_PER_TEST;
        } while (CurrentK < KB_OF_MEMORY);

        //
        // display the final amount of tested memory.
        //

        SetCursorPosition(4,15);
        sprintf(String,"%6d",CurrentK);
        FwPrint(String);
    }

// TEMPTEMP This test won't work on a secondary cache machine, fix.
//    ExecuteTest((TestRoutine)RomReadMergeWrite,LED_READ_MERGE_WRITE);

    //
    // Test the rest of devices in the board.
    //

    FwPrint(ST_TEST_MSG);
    FwPrint(ST_MEMORY_CONTROLLER_MSG);
    if (ExecuteTest((TestRoutine)RomIOCacheTest,LED_IO_CACHE)) {
        return;
    }

    //
    // Enable interrupts and initialize dispatch table.
    //

    FwPrint(ST_TEST_MSG);
    FwPrint(ST_INTERRUPT_CONTROLLER_MSG);
    ConnectInterrupts();
    if (ExecuteTest((TestRoutine)InterruptControllerTest,LED_INTERRUPT)) {
        return;
    }
    FwPrint(ST_TEST_MSG);
    FwPrint(ST_KEYBOARD_CONTROLLER_MSG);
    if (ExecuteTest((TestRoutine)InitKeyboardController,LED_KEYBOARD_CTRL)) {
        return;
    }
    KeyboardInitialized=TRUE;
    MoveCursorLeft(3);
    FwPrint("   ");
    MoveCursorLeft(3);
    PutLedDisplay(LED_KEYBOARD_INIT);
    if (Status=InitKeyboard()) {
        FwPrint(ST_ERROR_MSG);
        if (Status == TIME_OUT) {
            FwPrint(ST_KEYBOARD_NOT_PRESENT_MSG);
        }
        if (LoopOnError) {
            PutLedDisplay((LED_LOOP_ERROR << 16) | LED_KEYBOARD_INIT);
            for (;;) {
               InitKeyboard();
            }
        } else {
            if (IgnoreErrors) {
               return;
            } else {
                FwPrint(ST_HANG_MSG);
                PutLedDisplay((LED_BLINK << 16) | LED_KEYBOARD_INIT); // does not return.
            }
        }
    } else {
        FwPrint(FW_OK_MSG);
    }

    //
    // Test the serial port if it's not being used as the output device.
    //

    if (SerialOutput == FALSE) {
        FwPrint(ST_TEST_MSG);
        FwPrint(ST_SERIAL_LINE_MSG);
        if (ExecuteTest((TestRoutine)RomSerialResetTest,LED_SERIAL_RESET)) {
            return;
        }
        if (ExecuteTest((TestRoutine)RomSerial1RegistersTest,LED_SERIAL1_REG)) {
            return;
        }
        if (ExecuteTest((TestRoutine)RomSerial2RegistersTest,LED_SERIAL2_REG)) {
            return;
        }
        if (ExecuteTest((TestRoutine)RomSerial1LoopBackTest,LED_SERIAL1_LBACK)) {
            return;
        }
        if (ExecuteTest((TestRoutine)RomSerial2LoopBackTest,LED_SERIAL2_LBACK)) {
            return;
        }
    }
    FwPrint(ST_TEST_MSG);
    FwPrint(ST_PARALLEL_PORT_MSG);
    if (ExecuteTest((TestRoutine)RomParallelRegistersTest,LED_PARALLEL_REG)) {
        return;
    }
#ifndef DUO
    FwPrint(ST_TEST_MSG);
    FwPrint(ST_FLOPPY_MSG);
    if (ExecuteTest((TestRoutine)RomFloppyResetTest,LED_FLOPPY_RESET)) {
        return;
    }
    if (ExecuteTest((TestRoutine)RomFloppyRegistersTest,LED_FLOPPY_REG)) {
        return;
    }
#endif
    FwPrint(ST_TEST_MSG);
    FwPrint(ST_SCSI_MSG);
    if (ExecuteTest((TestRoutine)RomScsiResetTest,LED_SCSI_RESET)) {
        return;
    }
    if (ExecuteTest((TestRoutine)RomScsiRegistersTest,LED_SCSI_REG)) {
        return;
    }
    FwPrint(ST_TEST_MSG);
    FwPrint(ST_ETHERNET_MSG);
    if (ExecuteTest((TestRoutine)RomSonicResetTest,LED_SONIC_RESET)) {
        return;
    }
    if (ExecuteTest((TestRoutine)RomSonicRegistersTest,LED_SONIC_REG)) {
        return;
    }

    //
    // Do a loopback test if a valid address was found in the NVRAM.
    //

    if (ValidEthernetAddress) {
        FwPrint(ST_ETHERNET_ADDRESS_MSG);
        j=0;
        for (i=0;i<6;i++) {
            String[j++]=TranslationTable[StationAddress[i]>>4];
            String[j++]=TranslationTable[StationAddress[i]&0xF];
        }
        String[j]='\0';
        FwPrint(String);
        FwPrint(ST_ETHERNET_LOOPBACK_MSG);
        if (ExecuteTest((TestRoutine)RomSonicLoopBackTest,LED_SONIC_LOOPBACK)) {
            return;
        }
    }

//    RomSoundTest();

    //
    // Invalidate DMA translation table
    //

    WRITE_REGISTER_ULONG(&DMA_CONTROL->TranslationInvalidate.Long,0);

    FwPrint(ST_TEST_MSG);
    FwPrint(ST_ISP_MSG);
    if (ExecuteTest((TestRoutine)RomInitISP,LED_ISP)) {
        return;
    }
    FwPrint(ST_TEST_MSG);
    FwPrint(ST_RTC_MSG);
    if (ExecuteTest((TestRoutine)RomRTCTest,LED_RTC)) {
        return;
    }

    FwPrint(FW_CRLF_MSG);
    //
    // Disable Interrupts so that the firmware can be initialized
    //

    DisableInterrupts();
}

BOOLEAN
ExecuteTest(
    IN TestRoutine Test,
    IN ULONG       LedVal
    )
/*++

Routine Description:

    This routine executes the specified test. If the test fails and
the loop-on-error bit is set it sets the dot in the LED and reexecutes
the test for ever. If the loop-on-error bit is not set it will display
an error message and hang blinking the test and subtest number in the LED.

Arguments:

    Test    -   Supplies a pointer to the rutine to execute.
    LedVal  -   Subtest identifier to be displayed in the LED in case of error.

Return Value:

    if the test passes returns FALSE.
    if the test fails:
        if Normal Mode and not KeyboardInitialized blinks test and subtest number in LED and hangs.
        if Normal Mode and KeyboardInitialized, prompts for keypress and then returns TRUE.
        if LoopOnError mode reexecutes the test indefinitly and hangs.
        if IgnoreErrors mode returns TRUE.

--*/
{
    PutLedDisplay(LedVal);
    if (Test()) {

        //
        // The test failed.
        //

        FwPrint(ST_ERROR_MSG);
        if (LoopOnError) {
            PutLedDisplay((LED_LOOP_ERROR << 16) | LedVal);
            for (;;) {
                Test();
            }
        } else {
            if (IgnoreErrors) {
                //
                // Invalidate TT and disable interrupts.
                //
                WRITE_REGISTER_ULONG(&DMA_CONTROL->TranslationInvalidate.Long,0);
                DisableInterrupts();
                return TRUE;
            } else {
                FwPrint(ST_HANG_MSG);
            }
        }
    } else {

        //
        // display ...OK. and move the cursor left 3 chars
        // so that if an error follows it overwrites OK.
        //

        FwPrint(FW_OK_MSG);
        FwPrint("\x9B""3D");
        return FALSE;
    }
}
VOID
RomPutLine(
    IN PCHAR String
    )
/*++

Routine Description:

    This routine displays the string in the video monitor or sends
    it to a terminal trough the serial port during selftest.

Arguments:

    String  -  String to display

Return Value:

    None.

--*/
{
ULONG Count;
    if (DisplayOutput) {

        //
        // Display message on the video.
        //

        DisplayWrite(0,String,strlen(String),&Count);
    }
    if (SerialOutput) {

        //
        // Display message on serial Line.
        //

        while (*String) {
            SerialBootWrite(*String,COMPORT2_VIRTUAL_BASE);
            String++;
        }
    }
}

ULONG
RomSerialRegistersTest(
    IN ULONG SP_BASE
    )
/*++

Routine Description:

    This routine verifies that the Serial port 2 registers can be writen and
    read.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;

    //
    // Test registers
    //

    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->LineControl,0xA5);    // sets DLAB
    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->TransmitBuffer,0xC);      // write DL (lsb) for 9600
    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->InterruptEnable,0x0); // write DL (msb) for 9600
    CHECK_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->ReceiveBuffer,0xC)                 // check DL (lsb)
    CHECK_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->InterruptEnable,0x0)            // check DL (msb)
    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->LineControl,0x3);     // Clears DLAB & sets8 bit no parity 1 stop bit
    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->InterruptEnable,0x0); // disable interrupts
    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->FifoControl,0x0);     // disable fifo
    CHECK_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->InterruptEnable,0x0)            // check Int enable
    CHECK_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->LineControl,0x3)
    return Errors;
}

ULONG
RomSerialLoopBackTest(
    IN ULONG SP_BASE
    )
/*++

Routine Description:

    This routine verifies that the Serial port is operational.
    It performs an internal loopback of the ACE.

Arguments:

    SP_BASE - Supplies the virtual address of the serial port.

Return Value:

    Number of Errors found.

--*/
{
    ULONG   Errors=0;
    ULONG   i;

    //
    // Set loopback mode.
    //

    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->ModemControl,0x18);
    READ_REGISTER_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->LineStatus);             // Clear status
    READ_REGISTER_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->ReceiveBuffer);          // Clear Data
    for (i=0;i<256;i++) {
        WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->TransmitBuffer,(UCHAR)i); // write to port
        TimerTicks=3;
        while (((READ_REGISTER_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->LineStatus) & 1) == 0) && TimerTicks) {
        }
        if (TimerTicks) {
            CHECK_UCHAR(&((PSP_READ_REGISTERS)SP_BASE)->ReceiveBuffer,(UCHAR)i)  // read and check
        } else {
            Errors++;                                       // Timeout
        }
    }

    //
    // clear loopback mode and enable Interrupt line so it's not floating
    //

    WRITE_REGISTER_UCHAR(&((PSP_WRITE_REGISTERS)SP_BASE)->ModemControl,0x08);
    return Errors;
}

ULONG
RomSerialResetTest(
    )
/*++

Routine Description:

    This routine verifies that the Serial port has been properly reset.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;

    //
    // Check that was properly reset by checking the reset values.
    //

    CHECK_UCHAR(&SP1_READ->InterruptEnable, 0)
    CHECK_UCHAR(&SP1_READ->InterruptId, 1)
    CHECK_UCHAR(&SP1_READ->LineControl, 0)
    CHECK_UCHAR(&SP1_READ->ModemControl, 0x8)

    //
    // Do the same with COM2
    //

    CHECK_UCHAR(&SP2_READ->InterruptEnable, 0)
    CHECK_UCHAR(&SP2_READ->InterruptId, 1)
    CHECK_UCHAR(&SP2_READ->LineControl, 0)
    CHECK_UCHAR(&SP2_READ->ModemControl, 0x8)
    return Errors;
}

ULONG
RomSerial1RegistersTest(
    )
/*++

Routine Description:

    This routine verifies that the Serial port 1 registers can be writen and
    read.

Arguments:

    None.

Return Value:

    Returns the number of errors found.

--*/
{
    return RomSerialRegistersTest(COMPORT1_VIRTUAL_BASE);
}

ULONG
RomSerial2RegistersTest(
    )
/*++

Routine Description:

    This routine verifies that the Serial port 1 registers can be writen and
    read.

Arguments:

    None.

Return Value:

    Returns the number of errors found.

--*/
{
    return RomSerialRegistersTest(COMPORT2_VIRTUAL_BASE);
}

ULONG
RomSerial1LoopBackTest(
    )
/*++

Routine Description:

    This routine verifies that the Serial is operational.
    It performs an internal loopback of the ACE.

Arguments:

    None.

Return Value:

    Number of Errors found.

--*/
{
    return RomSerialLoopBackTest(COMPORT1_VIRTUAL_BASE);
}

ULONG
RomSerial2LoopBackTest(
    )
/*++

Routine Description:

    This routine verifies that the Serial is operational.
    It performs an internal loopback of the ACE.

Arguments:

    None.

Return Value:

    Number of Errors found.

--*/
{
    return RomSerialLoopBackTest(COMPORT2_VIRTUAL_BASE);
}

ULONG
RomParallelRegistersTest(
    )
/*++

Routine Description:

    This routine verifies that the Parallel port registers can be writen and
    read.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG Errors=0;
    UCHAR DataByte;

    //
    // Test registers.  This only checks the static bits.  Note: bit 2 of the
    // status register and bit 5 of the control register are used in the second
    // silicon.
    //

    DataByte = READ_REGISTER_UCHAR(&PARALLEL_READ->Status);
    if ((DataByte & 0x1) != 0x1) {
        Errors++;
    }


    DataByte = READ_REGISTER_UCHAR(&PARALLEL_READ->Control);
    if ((DataByte & 0xC0) != 0xC0) {
        Errors++;
    }

    return Errors;
}

#ifndef DUO
ULONG
RomScsiRegistersTest(
    )
/*++

Routine Description:

    This routine verifies that the NCR 53c94 SCSI controller registers can be writen and
    read.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;

    //
    // Test Registers
    //

    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountLow,0xAA);
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountHigh,0x55);
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Command,0x80);    // Issue a DMA NOP to load the counter
    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountLow) != 0xAA) {    // check counter
        Errors++;
    }
    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountHigh) != 0x55) {  // check counter
        Errors++;
    }
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountLow,0x00);
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountHigh,0x00);
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Command,0x80);    // Issue a DMA NOP to load the counter
    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountLow) != 0x00) {    // check counter
        Errors++;
    }
    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,TransferCountHigh) != 0x00) {   // check counter
        Errors++;
    }
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Configuration1,0x57);
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Configuration2,0x58);
    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Configuration1) != 0x57) {      // check config1
        Errors++;
    }
    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Configuration2) != 0x58) {      // check config2
        Errors++;
    }
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Configuration1,0x00);
    SCSI_WRITE((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Configuration2,0x00);
    return Errors;
}
#else
ULONG
RomScsiRegistersTest(
    )
/*++

Routine Description:

    This routine verifies that the NCR C700 SCSI controller registers can be writen and
    read.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;
    ULONG   Entry;
    UCHAR   ByteLane;
    UCHAR   ScsiTestData[8] = {0x55,0xAA,0x11,0x22,0x44,0x88,0xFF,0x00};
    UCHAR   ScsiTestParity[8] = {0x8,0x0,0x8,0x8,0x0,0x0,0x8,0x0};
    UCHAR   Temp;
    ULONG   TempLong;
    //
    // Test SCSI 1 Registers
    //

    SCSI_WRITE_USHORT((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSP[0],0x5A0F);   // write pattern to DSP pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSPS[0],0xE1);        // write pattern to DSPS0 pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSPS[1],0xD2);        // write pattern to DSPS0 pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSPS[2],0xB4);        // write pattern to DSPS0 pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSPS[3],0x78);        // write pattern to DSPS0 pointer
    if ((Temp=SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSP[0])) != 0x0F) {   // check low 4 register
        FwPrint("\r\nRegister %s Expected %lx received %lx\r\n","DSP[0]",0xf,Temp);
        return 1;
        Errors++;
    }
    if ((Temp=SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSP[1])) != 0x5A) {   // check low 4 register
        FwPrint("\r\nRegister %s Expected %lx received %lx\r\n","DSP[1]",0x5A,Temp);
        return 1;
        Errors++;
    }
    if ((TempLong=SCSI_READ_ULONG((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,DSPS)) != 0x78B4D2E1) {   // check low 4 register
        FwPrint("\r\nRegister %s Expected %lx received %lx\r\n","DSPS",0x78B4D2E1,TempLong);
        return 1;
        Errors++;
    }

    //
    // Test SCSI 2 Registers
    //

    SCSI_WRITE_USHORT((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSP[0],0x5A0F);    // write pattern to DSP pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSPS[0],0xE1);         // write pattern to DSPS0 pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSPS[1],0xD2);         // write pattern to DSPS0 pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSPS[2],0xB4);         // write pattern to DSPS0 pointer
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSPS[3],0x78);         // write pattern to DSPS0 pointer
    if (SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSP[0]) != 0x0F) {  // check low 4 register
        FwPrint("Reg 22");
        return 1;
        Errors++;
    }
    if (SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSP[1]) != 0x5A) {  // check low 4 register
        FwPrint("Reg 33");
        Errors++;
    }
    if (SCSI_READ_ULONG((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,DSPS) != 0x78B4D2E1) {   // check low 4 register
        FwPrint("Reg 44");
        return 1;
        Errors++;
    }

    //
    // Test SCSI 1 DMA Fifo
    //
    for (ByteLane=0; ByteLane < 4; ByteLane++) {
        //
        // Select ByteLane
        //
        SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST4,4+ByteLane);

        //
        // Write
        //
        for (Entry=0;Entry<8;Entry++) {
            SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST7,ScsiTestParity[Entry]);
            SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST6,ScsiTestData[Entry]);
        }

        //
        // Read and check
        //
        for (Entry=0;Entry<8;Entry++) {
            if ((Temp=SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST6)) != ScsiTestData[Entry]) {
                FwPrint("Reg CTEST6 expected %lx received %lx\r\n", ScsiTestData[Entry], Temp);
                return 1;
                Errors++;
            }
            if (((Temp=SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST2))&0x8) != ScsiTestParity[Entry]) {
                FwPrint("Reg CTEST2 expected %lx received %lx\r\n", ScsiTestParity[Entry], Temp);
                return 1;
                Errors++;
            }
        }
    }
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST4,0);

    //
    // Test SCSI 2 DMA Fifo
    //
    for (ByteLane=0; ByteLane < 4; ByteLane++) {
        //
        // Select ByteLane
        //
        SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST4,4+ByteLane);

        //
        // Write
        //
        for (Entry=0;Entry<8;Entry++) {
            SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST7,ScsiTestParity[Entry]);
            SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST6,ScsiTestData[Entry]);
        }

        //
        // Read and check
        //
        for (Entry=0;Entry<8;Entry++) {
            if ((Temp = SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST6)) != ScsiTestData[Entry]) {
                FwPrint("Reg2 CTEST6 expected %lx received %lx\r\n", ScsiTestData[Entry], Temp);
                return 1;
                Errors++;
            }
            if (((Temp = SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST2))&0x8) != ScsiTestParity[Entry]) {
                FwPrint("Reg2 CTEST2 expected %lx received %lx\r\n", ScsiTestParity[Entry], Temp);
                return 1;
                Errors++;
            }
        }
    }
    SCSI_WRITE_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST4,0);
    return Errors;

}

#endif

#ifndef DUO
ULONG
RomFloppyRegistersTest(
    )
/*++

Routine Description:

    This routine verifies that the Floppy controller registers can be writen and
    read.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;

    //
    // Test registers
    //

    WRITE_REGISTER_UCHAR(&FLOPPY_WRITE->DigitalOutput,0xec);
    CHECK_UCHAR(&FLOPPY_READ->DigitalOutput,0xec )
    WRITE_REGISTER_UCHAR(&FLOPPY_WRITE->DigitalOutput,0x7B);
    CHECK_UCHAR(&FLOPPY_READ->DigitalOutput,0x7B )
    WRITE_REGISTER_UCHAR(&FLOPPY_WRITE->DigitalOutput,0x08);
    CHECK_UCHAR(&FLOPPY_READ->DigitalOutput, 0x08)
    return Errors;
}
#endif

ULONG
RomSonicRegistersTest(
    )
/*++

Routine Description:

    This routine verifies that the Sonic registers can be writen and
    read. It doesn't test all the accessible registers but all the
    address bits are toggled.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;
    PSONIC_REGISTERS SonicBase = (volatile PSONIC_REGISTERS) NET_VIRTUAL_BASE;
    USHORT Tmp;
    //
    // Test registers
    //

    WRITE_REGISTER_USHORT(&SonicBase->UTDA.Reg,0xAAAA);
    WRITE_REGISTER_USHORT(&SonicBase->CRDA.Reg,0x5555);
    WRITE_REGISTER_USHORT(&SonicBase->RRP.Reg,0x1E1E);
    WRITE_REGISTER_USHORT(&SonicBase->RWP.Reg,0xC33C);
    WRITE_REGISTER_USHORT(&SonicBase->WatchdogTimer0.Reg,0xFFFF);
    WRITE_REGISTER_USHORT(&SonicBase->WatchdogTimer1.Reg,0x0000);
    WRITE_REGISTER_USHORT(&SonicBase->FaeTally.Reg,0xFFFF); // clear counter
    CHECK_USHORT(&SonicBase->UTDA.Reg, 0xAAAA)
    CHECK_USHORT(&SonicBase->CRDA.Reg, 0x5555)
    CHECK_USHORT(&SonicBase->RRP.Reg, 0x1E1E)
    CHECK_USHORT(&SonicBase->RWP.Reg, 0xC33C)
    CHECK_USHORT(&SonicBase->WatchdogTimer0.Reg, 0xFFFF)
    CHECK_USHORT(&SonicBase->WatchdogTimer1.Reg, 0)
    CHECK_USHORT(&SonicBase->FaeTally.Reg,0)
    return Errors;
}

#ifndef DUO
ULONG
RomScsiResetTest(
    )
/*++

Routine Description:

    This routine verifies that the NCR 53c94 SCSI controller was properly reset.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;

    //
    // Test that SCSI was properly reset
    //

    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,Fifo) != 0) {           // check Fifo
        Errors++;
    }
    if (SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,FifoFlags) != 0 ) {     // check Fifo flags
        Errors++;
    }
    if ((SCSI_READ((PSCSI_REGISTERS)SCSI_VIRTUAL_BASE,ScsiStatus) & 0xF8) != 0) { // check status
        Errors++;
    }
    return Errors;
}
#else
ULONG
RomScsiResetTest(
    )
/*++

Routine Description:

    This routine verifies that the NCR c700 SCSI controller was properly reset.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;
    ULONG   Temp;
    UCHAR   Char;
    USHORT  Short;
    //
    // Test that SCSI 1 was properly reset
    //

    if ((Temp=SCSI_READ_ULONG((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,SCNTL0)) != 0xC0) {   // check low 4 register
        FwPrint("\r\nRegister %s received %lx\r\n","SCNTL0",Temp);
        Errors++;
    }
    if ((Temp=SCSI_READ_ULONG((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,SCID)) != 0x0) {     // check next 4 register
        FwPrint("\r\nRegister %s received %lx\r\n","SCID",Temp);
        Errors++;
    }
    if ((Char = SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST1)) != 0xF0) {  // check next register
        FwPrint("\r\nRegister %s received %lx\r\n","CTEST1",Char);
        Errors++;
    }
//
// This works on power up only.
//
//    if ((Short=SCSI_READ_USHORT((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,CTEST2)) != 0x21) { // check next 2 register
//        FwPrint("\r\nRegister %s received %lx\r\n","CTEST2",Short);
//        Errors++;
//    }

    SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,SSTAT0);
    if ((Char=SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI1_VIRTUAL_BASE,ISTAT)) != 0x4) {    // check next register
        FwPrint("\r\nRegister %s received %lx\r\n","ISTAT",Char);
        Errors++;
    }

    //
    // Test that SCSI 2 was properly reset
    //

    if ((Temp=SCSI_READ_ULONG((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,SCNTL0)) != 0xC0) {   // check low 4 register
        FwPrint("\r\nRegister2 %s received %lx\r\n","SCNTL0",Temp);
        Errors++;
    }
    if ((Temp=SCSI_READ_ULONG((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,SCID)) != 0x0) {     // check next 4 register
        FwPrint("\r\nRegister2 %s received %lx\r\n","SCID",Temp);
        Errors++;
    }
    if ((Char = SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST1)) != 0xF0) {  // check next register
        FwPrint("\r\nRegister2 %s received %lx\r\n","CTEST1",Char);
        Errors++;
    }

//    if ((Short=SCSI_READ_USHORT((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,CTEST2)) != 0x21) { // check next 2 register
//        FwPrint("\r\nRegister2 %s received %lx\r\n","CTEST2",Short);
//        Errors++;
//    }

    SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,SSTAT0);
    if ((Char=SCSI_READ_UCHAR((PSIOP_REGISTERS)SCSI2_VIRTUAL_BASE,ISTAT)) != 0x4) {    // check next register
        FwPrint("\r\nRegister2 %s received %lx\r\n","ISTAT",Char);
        Errors++;
    }

    return Errors;
}
#endif


#ifndef DUO
ULONG
RomFloppyResetTest(
    )
/*++

Routine Description:

    This routine verifies that the Floppy controller has been properly reset.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;

    //
    // Test that Floppy controller was properly reset
    //

    CHECK_UCHAR(&FLOPPY_READ->DigitalOutput, 0x8)
    return Errors;
}
#endif

ULONG
RomSonicResetTest(
    )
/*++

Routine Description:

    This routine verifies that the Sonic cotroller has been properly reset.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    ULONG   Errors=0;
    PSONIC_REGISTERS SonicBase = (volatile PSONIC_REGISTERS) NET_VIRTUAL_BASE;
    USHORT Tmp;
    //
    // Test that Sonic controller was properly reset
    //

    CHECK_USHORT(&SonicBase->Command.Reg, 0x94)
    CHECK_USHORT(&SonicBase->TransmitControl.Reg, 0x101)    // this line was commented out for duo
    CHECK_USHORT(&SonicBase->InterruptStatus.Reg, 0x0)
    CHECK_USHORT(&SonicBase->CamEnable.Reg, 0x0)
    CHECK_USHORT(&SonicBase->ReceiveSequenceCounter.Reg, 0x0)
    return Errors;
}

BOOLEAN
ValidNvram(
    OUT PUCHAR StationAddress
    )
/*++

Routine Description:

    This routine checks that the Nvram is operational.

Arguments:

    StationAddress -  Pointer to struct to fill with the station address found in the
                      Nvram if it's there.

Return Value:

    TRUE if the Nvram contains valid configuration data.
    FALSE Otherwise.

--*/
{
    ULONG   i,Errors=0;
    ULONG   CheckSum;
    PUCHAR NVRamReadWrite = (PUCHAR) (NVRAM_PAGE0);
    PUCHAR NVRamProtected = (PUCHAR) (NVRAM_PAGE1);
    PUCHAR NVRamReadOnly  = (PUCHAR) (NVRAM_PAGE2);

    //
    // Check the Station Address
    //

    CheckSum=0;
    for (i=0;i<6;i++) {
        CheckSum+=NVRamReadOnly[i];
        if (CheckSum >= 256) {          // carry
            CheckSum++;                 // Add the carry
            CheckSum &= 0xFF;          // remove it from bit 9
        }
    }
    if ((NVRamReadOnly[7]+CheckSum != 0xFF) || (NVRamReadOnly[6]!=0)) {
        ValidEthernetAddress=FALSE;
        return FALSE;
    } else {
        ValidEthernetAddress=TRUE;

        //
        // Copy the station address.
        //

        for (i=0;i<6;i++) {
            StationAddress[i]=NVRamReadOnly[i];
        }
        return TRUE;
    }
}

#ifndef DUO
ULONG
RomIOCacheTest(
    )
/*++

Routine Description:

    This routine tests that the Jazz mctadr registers can be written and read.

Arguments:

    None.

Return Value:

    0   if no errors found
    number of errors otherwise

--*/
{
    ULONG   Errors=0,i,Block;

    //
    // Check the 32 data path between mctadr and r4000 by writting to the
    // byte mask and forcing an invalidate afterwards.
    //

    for (Block=0;Block<8;Block++) {
        WRITE_REGISTER_ULONG(&DMA_CONTROL->CacheMaintenance.Long,Block<<2); // select cache block zero
        WRITE_REGISTER_ULONG(&DMA_CONTROL->LogicalTag.Long,0x80000001); // set LFN to zero
                                                                // page offsset to zero
                                                                // read from device
                                                                // and validate Tag
        WRITE_REGISTER_ULONG(&DMA_CONTROL->ByteMask.Long,0x0F0F0F0F); // set even words to valid.
        WRITE_REGISTER_ULONG(&DMA_CONTROL->PhysicalTag.Long,1); // set PFN to zero and validate Tag

        //
        // Write data in the IO cache.
        //

        for (i=0;i<4;i++) {  // ?? 2 because of the bug in the mctadr, should be 4
            WRITE_REGISTER_ULONG(&DMA_CONTROL->CacheMaintenance.Long,(Block<<2)+i);// select line & Bank
            WRITE_REGISTER_ULONG(&DMA_CONTROL->BufferWindowLow.Long,Block+i);
        }

        //
        // Read from memory (and flush buffers) at phys address zero which should match with the phys tag
        // thus invalidating it and clearing the byte mask.
        //

        for (i=0;i<2;i++) {
            CHECK_ULONG(0xA0000000+(i<<3),Block+i);
        }
    }
    return Errors;
}
#else
ULONG
RomIOCacheTest(
    )
/*++

Routine Description:

    This routine tests that the Duo mctadr registers can be written and read.

Arguments:

    None.

Return Value:

    0   if no errors found
    number of errors otherwise

--*/
{
    ULONG   Errors=0,i,Block;

    //
    // Check the 32 data path between mctadr and r4000 by writting to the
    // byte mask and forcing an invalidate afterwards.
    //

    for (Block=0;Block<8;Block++) {
        WRITE_REGISTER_ULONG(&DMA_CONTROL->IoCacheLogicalTag[Block].Long,0x80000001); // set LFN to zero
                                                                // page offsset to zero
                                                                // read from device
                                                                // and validate Tag
        WRITE_REGISTER_ULONG(&DMA_CONTROL->IoCacheLowByteMask[Block].Long,0x0F0F0F0F); // set even words to valid.
        WRITE_REGISTER_ULONG(&DMA_CONTROL->IoCacheHighByteMask[Block].Long,0x0F0F0F0F); // set even words to valid.
        WRITE_REGISTER_ULONG(&DMA_CONTROL->IoCachePhysicalTag[Block].Long,1); // set PFN to zero and validate Tag

        //
        // Write data in the IO cache.
        //

        for (i=0;i<8;i++) {  // for each buffer register
            WRITE_REGISTER_ULONG(&DMA_CONTROL->IoCacheBuffer[Block*8+i],Block+i);
        }

        //
        // Read from memory (and flush buffers) at phys address zero which should match with the phys tag
        // thus invalidating it and clearing the byte mask.
        //

        for (i=0;i<8;i++) {
            CHECK_ULONG(0xA0000000+(i<<3),Block+i);
        }
    }
    return Errors;
}
#endif

ULONG
InterruptControllerTest(
    )
/*++

Routine Description:

    This routine tests that the mctadr registers can be written and read.

Arguments:

    None.

Return Value:

    None.

--*/
{
    ULONG   Errors=0,i=0;

    TimerTicks=1;                           // set counter to 1

    // write a zero for 1msec interval.

    WRITE_REGISTER_ULONG(&DMA_CONTROL->InterruptInterval.Long,0);
    while (TimerTicks) {                    // test counter until it's zero
        READ_REGISTER_UCHAR(0xBFC00000);    // read from ROM to waste time
        if (i++ == 0x1FFFF) {               // if timeout
            Errors++;                       // return errors
            return Errors;
        }
    }
    return Errors;
}
VOID
RomBeep(
    )
/*++

Routine Description:

    This routine makes the speaker beep for a seconf.

Arguments:

    None.

Return Value:

    None.

--*/
{
    PutLedDisplay(LED_BEEP);
    WRITE_REGISTER_UCHAR(&ISP->IntTimer1CommandMode,0xB6);
    WRITE_REGISTER_UCHAR(&ISP->IntTimer1SpeakerTone,0x97); // LSB for 440hz
    WRITE_REGISTER_UCHAR(&ISP->IntTimer1SpeakerTone,0x0A); // MSB for 440Hz
    WRITE_REGISTER_UCHAR(&ISP->NMIStatus,3);    // Do notforce NMI just enable the speaker beep!
    while (TimerTicks>500) {                    // wait until counter reaches 500
    }
    WRITE_REGISTER_UCHAR(&ISP->NMIStatus,0);    // Disable the speaker beep.
}

UCHAR
ReadRTCRegister (
    UCHAR Register
    )
/*++

Routine Description:

    This routine reads the specified register of the RTC

Arguments:

    Register - Number of the register to read

Return Value:

    Returns the contents of the RTC register.

--*/
{
    //
    // Write the register number into the NMI register of the ISP
    // and disable NMI
    //

    WRITE_REGISTER_UCHAR(&ISP->NMIEnable,Register | (1 << 7));

    //
    // Read the register
    //

    return READ_REGISTER_UCHAR((PUCHAR)RTC_VIRTUAL_BASE);
}

VOID
WriteRTCRegister (
    UCHAR Register,
    UCHAR Value
    )
/*++

Routine Description:

    This routine writes the Value to the specified RTC register

Arguments:

    Register - Number of the register to write

    Value - Value to write to the RTC register.

Return Value:

    None

--*/

{
    //
    // Write the register number into the NMI register of the ISP
    // and disable NMI
    //

    WRITE_REGISTER_UCHAR(&ISP->NMIEnable,Register | (1 << 7));

    //
    // Write the register
    //

    WRITE_REGISTER_UCHAR((PUCHAR)RTC_VIRTUAL_BASE,Value);
}
ULONG
ReadTime(
    )
/*++

Routine Description:

    This routine reads the current time in the RTC.

Arguments:

    None.

Return Value:

    Number of seconds since time zero = Jan 1st 1900 12:00:00PM

--*/
{
    UCHAR DataByte;
    UCHAR Minutes,Seconds;
    ULONG TotalSeconds;

    //
    // Check for an Update In Progress
    //

    do {
        DataByte=ReadRTCRegister(RTC_CONTROL_REGISTERA);
    } while (((PRTC_CONTROL_REGISTER_A)(&DataByte))->UpdateInProgress==1);

    //
    // Read Time from RTC   and compute seconds.
    //

    Minutes = ReadRTCRegister(RTC_MINUTE);                  // read minutes
    Seconds = ReadRTCRegister(RTC_SECOND);                  // read seconds
    TotalSeconds = (Minutes * 60) + Seconds;                // compute seconds
    return TotalSeconds;
}

ULONG
RomRTCTest(
    )
/*++

Routine Description:

    This routine checks that the RTC is operating correctly by reading
    the time, waiting for a 1.1 seconds, reading time again and checking
    that the two times differ in 1 or 2 seconds.

    If the RTC is disabled, this routine enables it and sets the time
    to 12:00:00pm Jan 1st 1900. The RTC is set in Binary count mode, and
    12h time format.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/
{
    UCHAR   DataByte;
    ULONG   Time;

    DataByte = ReadRTCRegister(RTC_CONTROL_REGISTERA);
    if (((PRTC_CONTROL_REGISTER_A)(&DataByte))->TimebaseDivisor != 0x2) { // test DV bits to see if rtc is counting.

        //
        // Enable counter
        //

        DataByte = 0;
        ((PRTC_CONTROL_REGISTER_A)(&DataByte))->TimebaseDivisor = 2;
        WriteRTCRegister(RTC_CONTROL_REGISTERA, DataByte);
        Time = 0;
        TimerTicks = 600;           // set counter to 600
        while (TimerTicks) {        // Wait for an update to occur
        }

        //
        // Set RTC to 24 hour format and binary mode.  Set SET Bit
        //

        DataByte=0;
        ((PRTC_CONTROL_REGISTER_B)(&DataByte))->HoursFormat = 1;
        ((PRTC_CONTROL_REGISTER_B)(&DataByte))->DataMode = 1;
        ((PRTC_CONTROL_REGISTER_B)(&DataByte))->SetTime = 1;
        WriteRTCRegister(RTC_CONTROL_REGISTERB, DataByte);

        //
        // RTC disabled set date to 1-1-1900 0h:0m:0
        //

        WriteRTCRegister(RTC_SECOND,0);             //
        WriteRTCRegister(RTC_MINUTE,0);             //  12:00:00 PM
        WriteRTCRegister(RTC_HOUR,0x12);            //
        WriteRTCRegister(RTC_DAY_OF_WEEK,1);        //sunday
        WriteRTCRegister(RTC_DAY_OF_MONTH,1);       // 1st
        WriteRTCRegister(RTC_MONTH,1);              // january
        WriteRTCRegister(RTC_YEAR,0);               // 1900
        WriteRTCRegister(RTC_SECOND_ALARM,0xFF);    // Turn alarm
        WriteRTCRegister(RTC_MINUTE_ALARM,0xFF);    // off
        WriteRTCRegister(RTC_HOUR_ALARM,0xFF);      //

    }

    //
    // Clear SET bit and put in binary mode
    //

    DataByte=0;
    ((PRTC_CONTROL_REGISTER_B)(&DataByte))->HoursFormat = 1;
    ((PRTC_CONTROL_REGISTER_B)(&DataByte))->DataMode = 1;
    WriteRTCRegister(RTC_CONTROL_REGISTERB, DataByte);

    for (DataByte=0;DataByte<2;DataByte++) {    // do it twice in case Hour changes
        Time = ReadTime();          // Read Current time
        TimerTicks=1050;            // set timeout to 1.05 seconds
        RomBeep();                  // Do a beep in the meantime for 500ms
        while (TimerTicks) {        // Wait for an update
        }                           //
        Time = ReadTime() - Time;         // compute elapsed time
        if ((Time == 1) || (Time == 2)) { // 1 or 2 seconds passed
            return 0;                     // RTC counts properly
        }
    }   // if it didn't work even the second time it's screw up.
        return 1;
}
ULONG
RomVideoMemory(
    IN VOID
    )
/*++

Routine Description:

    This routine tests the video memory.

Arguments:

    None.

Return Value:

    Number of errors found.

--*/

{
    ULONG VideoMemoryBaseAddress;

#ifdef R3000
    VideoMemoryBaseAddress=VIDEO_MEMORY_PHYSICAL_BASE,
#else
    VideoMemoryBaseAddress=VIDEO_MEMORY_VIRTUAL_BASE,
#endif
    WriteVideoMemoryAddressTest(VideoMemoryBaseAddress,
                                VIDEO_MEMORY_SIZE
                                );
    return CheckVideoMemoryAddressTest(VideoMemoryBaseAddress,
                                VIDEO_MEMORY_SIZE
                                );
}

ULONG
RomInitISP (
    VOID
    )

/*++

Routine Description:

    This routine initializes the EISA interrupt controller.

Arguments:

    None.

Return Value:

    Returns the number of errors found.

--*/

{

    UCHAR DataByte;

    ULONG InterruptLevel;

    //
    // Initialize the EISA interrupt controller.  There are two cascaded
    // interrupt controllers, each of which must initialized with 4 initialize
    // control words.
    //

    DataByte = 0;
    ((PINITIALIZATION_COMMAND_1) &DataByte)->Icw4Needed = 1;
    ((PINITIALIZATION_COMMAND_1) &DataByte)->InitializationFlag = 1;

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt1ControlPort0,DataByte);
    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt2ControlPort0,DataByte);

    //
    // The second intitialization control word sets the iterrupt vector to
    // 0-15.
    //

    DataByte = 0;

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt1ControlPort1,DataByte);

    DataByte = 0x08;

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt2ControlPort1,DataByte);

    //
    // The thrid initialization control word set the controls for slave mode.
    // The master ICW3 uses bit position and the slave ICW3 uses a numberic.
    //

    DataByte = 1 << SLAVE_IRQL_LEVEL;

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt1ControlPort1,DataByte);

    DataByte = SLAVE_IRQL_LEVEL;

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt2ControlPort1,DataByte);

    //
    // The fourth initialization control word is used to specify normal
    // end-of-interrupt mode and not special-fully-nested mode.
    //

    DataByte = 0;
    ((PINITIALIZATION_COMMAND_4) &DataByte)->I80x86Mode = 1;

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt1ControlPort1,DataByte);
    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt2ControlPort1,DataByte);


    //
    // Mask all the interrupts.
    //

    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt1ControlPort1,0xFF);
    WRITE_REGISTER_UCHAR(&EISA_CONTROL->Interrupt2ControlPort1,0xFF);

    //
    // Check that the interrupt level is 7 (i.e. no interrupt is pending)
    //
#ifndef DUO
    InterruptLevel=READ_REGISTER_UCHAR(&DMA_CONTROL->InterruptAcknowledge);
    InterruptLevel=READ_REGISTER_UCHAR(&DMA_CONTROL->InterruptAcknowledge);
    if (InterruptLevel == 0x07) {
        return 0;
    } else {
        return 1;
    }
#else
    InterruptLevel=READ_REGISTER_ULONG(&DMA_CONTROL->EisaInterruptAcknowledge);
    InterruptLevel=READ_REGISTER_ULONG(&DMA_CONTROL->EisaInterruptAcknowledge);
    if (InterruptLevel == 0x0) {
        return 0;
    } else {
        FwPrint("\r\nEisa Interrupt level =%lx ",InterruptLevel);
        return 1;
    }
#endif

}


VOID
FwPrintVersion (
    VOID
    )

/*++

Routine Description:

     This routine prints the PROM version.

Arguments:

     None.

Return Value:

     None.

--*/

{
    FwPrint(ST_ARC_MULTIBOOT_MSG,*(PULONG)(PROM_ENTRY(2)));
    FwPrint(ST_COPYRIGHT_MSG);
    return;
}


#ifndef DUO
VOID
ReportJazzCacheErrorException(
    IN ULONG CacheErr
    )
/*++

Routine Description:

     This routine prints cache error / parity information.

Arguments:

     CacheErr - R4000 CacheErr register.

Return Value:

     None.

--*/

{

    ULONG ParityDiag[3];
    PULONG ParDiag;
    ULONG IntSrc;
    UCHAR String[128];

    //
    // Display cache error exception message
    //
    FwPrint(MON_CACHE_ERROR_MSG);
    FwPrint(MON_EXCEPTION_MSG);

    //
    // Align ParDiag to a double word.
    //
    ParDiag = (PULONG) ((ULONG)(&ParityDiag[1]) & 0xFFFFFFF8);

    if (((PCACHEERR)(&CacheErr))->EE == 1) {
        //
        // This is a parity error on the Memory or SysAd bus.
        //
        IntSrc = READ_REGISTER_ULONG(&DMA_CONTROL->InterruptSource.Long);
        if (IntSrc & (1 << 8)) {
            //
            // This a parity error in memory.
            // Display MFAR and Parity Diag.
            //
            LoadDoubleWord((ULONG)&DMA_CONTROL->ParityDiagnosticLow.Long,ParDiag);
            sprintf(String,
                    MON_MEM_PARITY_FAILED_MSG,
                    READ_REGISTER_ULONG(&DMA_CONTROL->MemoryFailedAddress.Long),
                    *ParDiag,
                    *(ParDiag+1)
            );
            FwPrint(String);
            return;
        } else {
            //
            // The error occurred in the system bus.
            //
            FwPrint(MON_BUS_PARITY_ERROR);
            return;
        }
    } else {
        //
        // Internal cache error.
        //
        sprintf(String,MON_CACHE_ERROR_REG_MSG,CacheErr);
        FwPrint(String);
        return;
    }
}
#else
VOID
ReportDuoCacheErrorException(
    IN ULONG CacheErr
    )
/*++

Routine Description:

     This routine prints cache error / ecc information.

Arguments:

     CacheErr - R4000 CacheErr register.

Return Value:

     None.

--*/

{
    UCHAR String[128];
    //
    // Display cache error exception message
    //
    FwPrint(MON_CACHE_ERROR_MSG);
    FwPrint(MON_EXCEPTION_MSG);
    if (READ_REGISTER_ULONG(&DMA_CONTROL->WhoAmI.Long)) {
        FwPrint(MON_PROCESSOR_B_EXCEPTION);
    } else {
        FwPrint(MON_PROCESSOR_A_EXCEPTION);
    }
    if (((PCACHEERR)(&CacheErr))->EE == 1) {
        //
        // The error occurred in the system bus.
        //
        FwPrint(MON_BUS_PARITY_ERROR);
        return;
    } else {
        //
        // Internal cache error.
        //
        sprintf(String,MON_CACHE_ERROR_REG_MSG, CacheErr);
        FwPrint(String);
        return;
    }
}
#endif
