Exemplo n.º 1
0
 protected void Tick(RexBoard mBoard)
 {
     for (int i = 0; i < NUM_TICKS; i++)
     {
         mBoard.Tick();
     }
 }
Exemplo n.º 2
0
        public override bool Mark(RexBoard mBoard)
        {
            Console.WriteLine("This might take a while...");
            Initialise(mBoard);
            for (uint i = 0; i <= 0xffff; i++)
            {
                ResetBoard(mBoard);

                // Flip some switches
                mBoard.Parallel.Switches = i;

                // Give a nice progress indicator
                if (i % (0xffff / 100) == 0)
                {
                    Console.Write($"\r{i / (0xffff / 100)}%");
                }

                // Let the program run
                bool passed = RunSerialTestCase("", "", $"{i:D5}", "", mBoard);
                if (!passed)
                {
                    return(false);
                }
            }
            Console.WriteLine();
            return(true);
        }
Exemplo n.º 3
0
        public RexBoardForm(string[] args)
        {
            InitializeComponent();

            //setup arguments
            mArgs = args;

            //Set up form contents
            ResetToolStripStatusLabel();

            //Set up all REX and WRAMP hardware
            mRexBoard = rexWidget1.mBoard;

            //Load WRAMPmon into ROM
            Stream wmon = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(Resources.monitor_srec));

            rexWidget1.LoadSrec(wmon);
            wmon.Close();

            //Set up the worker thread
            mWorkerEnabler = new ManualResetEvent(true); // CPU begins in a running state, since the reset is finished
            mWorker        = new Thread(new ThreadStart(Worker));
            mRexBoard.SetTickEnabler(mWorkerEnabler);

            //Set up all forms
            mSubforms       = new List <Form>();
            mSerialForm1    = new BasicSerialPortForm(mRexBoard.Serial1, this);
            mSerialForm2    = new BasicSerialPortForm(mRexBoard.Serial2, this);
            mGpRegisterForm = new RegisterForm(mRexBoard.CPU.mGpRegisters, false);
            mSpRegisterForm = new RegisterForm(mRexBoard.CPU.mSpRegisters, true);
            mRamForm        = new MemoryForm(mRexBoard.RAM);
            mRamForm.SetCpu(mRexBoard.CPU);
            mInterruptButtonForm = new PeripheralMemoryForm(mRexBoard.InterruptButton);
            mSerialConfigForm1   = new PeripheralMemoryForm(mRexBoard.Serial1);
            mSerialConfigForm2   = new PeripheralMemoryForm(mRexBoard.Serial2);
            mParallelConfigForm  = new PeripheralMemoryForm(mRexBoard.Parallel);
            mTimerConfigForm     = new PeripheralMemoryForm(mRexBoard.Timer);


            //Add all forms to the list of subforms
            mSubforms.Add(mSerialForm1);
            mSubforms.Add(mSerialForm2);
            mSubforms.Add(mGpRegisterForm);
            mSubforms.Add(mSpRegisterForm);
            mSubforms.Add(mRamForm);
            mSubforms.Add(mInterruptButtonForm);
            mSubforms.Add(mSerialConfigForm1);
            mSubforms.Add(mSerialConfigForm2);
            mSubforms.Add(mParallelConfigForm);
            mSubforms.Add(mTimerConfigForm);

            //Wire up event handlers
            foreach (Form f in mSubforms)
            {
                f.VisibleChanged += new EventHandler(SubForm_VisibleChanged);
            }

            //Set the GUI update timer going!
            updateTimer.Start();
        }
Exemplo n.º 4
0
        protected bool Test(uint switchCombination, uint expected, RexBoard board)
        {
            board.Parallel.Switches = switchCombination;

            for (int i = 0; i < CLOCK_CYCLE_LIMIT; i++)
            {
                board.Tick();
            }


            //HACK: If they've turned off Hex decoding, modify the expected value
            if ((board.Parallel.Control & 1u) == 0u)
            {
                uint[] bitPattern    = { 0x772F, 0x5B5B, 0x7D7C, 0x3F5E, 0x666F, 0x392F, 0x0771, 0x7C7F, 0x2F06 };
                uint   leftExpected  = (bitPattern[CountBits(switchCombination)] >> 8) & 0xFF;
                uint   rightExpected = bitPattern[CountBits(switchCombination)] & 0xFF;

                if (board.Parallel.LeftSSDOut != leftExpected || board.Parallel.RightSSDOut != rightExpected)
                {
                    mMessage += string.Format("Incorrect SSD segments lit (Switches {0:X2}).\r\nWarning: SSD Hex decoding is disabled; it is recommended that you turn this on instead of setting segments on/off manually.\r\n", board.Parallel.Switches);
                    return(false);
                }
            }
            else
            {
                uint result = board.Parallel.SSD;
                if (result != expected)
                {
                    mMessage += string.Format("SSD incorrect: was {0:X2}, expected {1:X2} (Switches {2:X2})\r\n", result, expected, board.Parallel.Switches);
                    return(false);
                }
            }

            return(true);
        }
Exemplo n.º 5
0
 /// <summary>
 /// Wags the dog.
 /// </summary>
 /// <param name="board"></param>
 protected int Wag(RexBoard board, int count = 10000)
 {
     for (int i = 0; i < count; i++)
     {
         board.Tick();
     }
     return(count);
 }
Exemplo n.º 6
0
        public RexBoardForm()
        {
            InitializeComponent();

            //allow drop event
            this.AllowDrop = true;

            //Set up all REX and WRAMP hardware
            mRexBoard = rexWidget1.mBoard;

            //Load WRAMPmon into ROM
            Stream wmon = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(Resources.monitor_srec));

            rexWidget1.LoadSrec(wmon);
            wmon.Close();

            //Set up the worker thread
            mWorker = new Thread(new ThreadStart(Worker));

            //Set up all forms
            mSubforms       = new List <Form>();
            mSerialForm1    = new BasicSerialPortForm(mRexBoard.Serial1);
            mSerialForm2    = new BasicSerialPortForm(mRexBoard.Serial2);
            mGpRegisterForm = new RegisterForm(mRexBoard.CPU.mGpRegisters, false);
            mSpRegisterForm = new RegisterForm(mRexBoard.CPU.mSpRegisters, true);
            mRamForm        = new MemoryForm(mRexBoard.RAM);
            mRamForm.SetCpu(mRexBoard.CPU);
            mInterruptButtonForm = new PeripheralMemoryForm(mRexBoard.InterruptButton);
            mSerialConfigForm1   = new PeripheralMemoryForm(mRexBoard.Serial1);
            mSerialConfigForm2   = new PeripheralMemoryForm(mRexBoard.Serial2);
            mParallelConfigForm  = new PeripheralMemoryForm(mRexBoard.Parallel);
            mTimerConfigForm     = new PeripheralMemoryForm(mRexBoard.Timer);


            //Add all forms to the list of subforms
            mSubforms.Add(mSerialForm1);
            mSubforms.Add(mSerialForm2);
            mSubforms.Add(mGpRegisterForm);
            mSubforms.Add(mSpRegisterForm);
            mSubforms.Add(mRamForm);
            mSubforms.Add(mInterruptButtonForm);
            mSubforms.Add(mSerialConfigForm1);
            mSubforms.Add(mSerialConfigForm2);
            mSubforms.Add(mParallelConfigForm);
            mSubforms.Add(mTimerConfigForm);

            //Wire up event handlers
            foreach (Form f in mSubforms)
            {
                f.VisibleChanged += new EventHandler(SubForm_VisibleChanged);
            }

            //Set the GUI update timer going!
            updateTimer.Start();
        }
Exemplo n.º 7
0
        /// <summary>
        /// Restore the state we set up in Initialise, plus some other stuff.
        /// </summary>
        protected void ResetBoard(RexBoard mBoard)
        {
            if (SupressReset)
            {
                return;
            }

            mBoard.CPU.PC = originalPC;

            mBoard.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.sp]   = 0x03b7f; //sensible value for the stack pointer
            mBoard.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.ra]   = 0xFFFFF; //when the CPU tries to jump here, the test is over.
            mBoard.CPU.mSpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.SpRegister.evec] = 0x80000; //when the CPU tries to jump here, the test is over (should jal to exit, which is syscall).
        }
Exemplo n.º 8
0
        public override bool Mark(RexBoard board)
        {
            Initialise(board);

            for (int c = 0; c < TEST_CASES.Length; c++)
            {
                if (!TryTestCase(board, TEST_CASES[c], false))
                {
                    return(false);
                }
            }
            return(true);
        }
Exemplo n.º 9
0
        protected override bool DoSubTest(RexBoard mBoard)
        {
            //Check that the SSDs are showing the right value
            if (mBoard.Parallel.SSD != 0x00)
            {
                mMessage += "Before pressing the user interrupt button, SSDs did not show '00'\r\n";
                return(false);
            }

            //Press the user interrupt button 10 times, see where it gets you.
            for (int i = 1; i < 10; i++)
            {
                mBoard.InterruptButton.PressButton();
                Wag(mBoard);
                if ((mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf) != i)
                {
                    mMessage += string.Format("SSDs showed \"{0:X2}\" after pressing the user interrupt button {1} times. Expected SSD to show \"{1:D2}\"\r\n", mBoard.Parallel.SSD, i);
                    return(false);
                }
                if (mBoard.InterruptButton.InterruptAck != 0)
                {
                    mMessage += "User Interrupt Button was not acknowledged.";
                    return(false);
                }
            }

            // Press the other buttons some, see what happens.
            for (int i = 10; i < 100; i++)
            {
                mBoard.Parallel.Buttons = (uint)(1 << (i % 3));
                Wag(mBoard);
                mBoard.Parallel.Buttons = 0;
                Wag(mBoard);
                if ((mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf) != i)
                {
                    mMessage += string.Format("SSDs showed \"{0:X2}\" after pressing the parallel buttons {1} times. Expected SSD to show \"{2:D2}\"\r\n", mBoard.Parallel.SSD, i - 10, i);
                    return(false);
                }
                if (mBoard.Parallel.InterruptAck != 0)
                {
                    mMessage += "Parallel Interrupts were not acknowledged.";
                    return(false);
                }
            }

            return(true);
        }
Exemplo n.º 10
0
        protected List <uint> RunTestCase(RexBoard mBoard, int tickLimit)
        {
            List <uint> ssdValues = new List <uint>();

            for (int i = 0; i < tickLimit; i++)
            {
                mBoard.Tick();

                //Record SSD values at each interrupt
                if (mBoard.CPU.PC == mBoard.CPU.mSpRegisters[RegisterFile.SpRegister.evec] + 1)
                {
                    uint currentSsdValue = (mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf);

                    if (ssdValues.Count == 0 || ssdValues.Last() != currentSsdValue)
                    {
                        ssdValues.Add(currentSsdValue);
                    }
                    Console.Write("SSDs: {0:D2}\r", currentSsdValue);
                }
            }

            return(ssdValues);
        }
Exemplo n.º 11
0
        /// <summary>
        /// Executes a test case.
        /// </summary>
        /// <param name="sendSP1">String to send to the serial port SP1</param>
        /// <param name="sendSP2">String to send to the serial port SP2</param>
        /// <param name="expectSP1">String expected to be received from SP1</param>
        /// <param name="expectSP2">String expected to be received from SP2</param>
        /// <param name="board">The board to run the tests on.</param>
        /// <returns>True if the test passes.</returns>
        protected bool RunSerialTestCase(string sendSP1, string sendSP2, string expectSP1, string expectSP2, RexBoard board)
        {
            if (Verbose)
            {
                Console.WriteLine("Test Case: ");
                if (sendSP1.Length > 0)
                {
                    Console.WriteLine("-Sending '{0}' to SP1", sendSP1);
                }
                if (sendSP2.Length > 0)
                {
                    Console.WriteLine("-Sending '{0}' to SP2", sendSP2);
                }
                if (expectSP1.Length > 0)
                {
                    Console.WriteLine("-Expecting '{0}' from SP1", expectSP1);
                }
                if (expectSP2.Length > 0)
                {
                    Console.WriteLine("-Expecting '{0}' from SP2", expectSP2);
                }
            }

            // We hook into the SerialIO devices' event handlers to correctly receive characters
            mSP1RecvBuf = mSP2RecvBuf = "";
            EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler1 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial1_SerialDataTransmitted);
            EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler2 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial2_SerialDataTransmitted);

            board.Serial1.SerialDataTransmitted += serialHandler1;
            board.Serial2.SerialDataTransmitted += serialHandler2;

            int clocks = CLOCK_CYCLE_LIMIT;

            //Transmit the data, and exit early if the received data happens sooner than we hoped
            string toSend1 = sendSP1;
            string toSend2 = sendSP2;

            while (clocks-- > 0)
            {
                board.Tick();
                if (toSend1.Length != 0 && board.Serial1.SendAsync(toSend1[0]))
                {
                    toSend1 = toSend1.Substring(1);
                }

                if (toSend2.Length != 0 && board.Serial2.SendAsync(toSend2[0]))
                {
                    toSend2 = toSend2.Substring(1);
                }

                if (expectSP1.Length != 0 && mSP1RecvBuf == expectSP1)
                {
                    break;
                }
                if (expectSP2.Length != 0 && mSP2RecvBuf == expectSP2)
                {
                    break;
                }
            }

            board.Serial1.SerialDataTransmitted -= serialHandler1;
            board.Serial2.SerialDataTransmitted -= serialHandler2;

            //Make sure the correct message was received
            if (mSP1RecvBuf != expectSP1)
            {
                if (sendSP1.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1);
                }
                if (sendSP2.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2);
                }

                mMessage += string.Format("Received \"{0}\" from SP1, expected \"{1}\"\r\n", mSP1RecvBuf, expectSP1);
                return(false);
            }

            if (mSP2RecvBuf != expectSP2)
            {
                if (sendSP1.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1);
                }
                if (sendSP2.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2);
                }

                mMessage += string.Format("Received \"{0}\" from SP2, expected \"{1}\"\r\n", mSP2RecvBuf, expectSP2);
                return(false);
            }

            return(true);
        }
Exemplo n.º 12
0
        public RexWidget()
        {
            InitializeComponent();

            mBoard = new RexBoard();
        }
Exemplo n.º 13
0
        protected override bool DoSubTest(RexBoard mBoard)
        {
            //Ensure timer is set up right
            if (mBoard.Timer.Control != 3)
            {
                mMessage += string.Format("Timer control register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Control);
                return(false);
            }
            if (mBoard.Timer.Load != 2400)
            {
                mMessage += string.Format("Timer load register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Load);
                return(false);
            }

            //Test for 100 seconds

            //Sneaky speedup to 10 interrupts per second, to save time.
            mBoard.Timer.Count = 0;
            mBoard.Timer.Load  = 240;

            List <uint> ssdValues = new List <uint>();

            long ticksAtStart = mBoard.TickCounter;
            int  tickLimit    = (int)((6.25e6 * TEST_DURATION_SECONDS + ticksAtStart) / 10);

            for (int i = 0; i < tickLimit; i++)
            //while(mBoard.TickCounter < 4e6 * TEST_DURATION_SECONDS / 10)
            {
                mBoard.Tick();

                //Record SSD values at each interrupt
                if (mBoard.CPU.PC == mBoard.CPU.mSpRegisters[RegisterFile.SpRegister.evec] + 1)
                {
                    if (ssdValues.Count == 0 || ssdValues.Last() != (mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf))
                    {
                        ssdValues.Add((mBoard.Parallel.LeftSSD & 0xf) * 10 + (mBoard.Parallel.RightSSD & 0xf));
                    }
                    Console.Write("{0:D2}%\r", (int)(100.0 * i / tickLimit));
                }
            }

            //Check for the correct sequence
            bool correctSequence = true;

            if (ssdValues.Count == TEST_DURATION_SECONDS)
            {
                for (uint i = 0; i < TEST_DURATION_SECONDS; i++)
                {
                    if (ssdValues[(int)i] != i)
                    {
                        correctSequence = false;
                        break;
                    }
                }
            }
            else
            {
                correctSequence = false;
            }

            if (!correctSequence)
            {
                mMessage += "Ran your program for 100 (simulated) seconds and observed:\r\n";
                foreach (uint i in ssdValues)
                {
                    mMessage += string.Format("{0:D2}, ", i);
                }

                mMessage += "\r\n\r\nExpected to see a sequence from \"00\" to \"99\" inclusive, in the correct order.\r\n";
                return(false);
            }

            return(true);
        }
Exemplo n.º 14
0
        protected override bool DoSubTest(RexBoard mBoard)
        {
            //Ensure timer is set up right
            if (mBoard.Timer.Control != 2)
            {
                mMessage += string.Format("Timer control register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Control);
                return(false);
            }
            if (mBoard.Timer.Load != 24)
            {
                mMessage += string.Format("Timer load register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Load);
                return(false);
            }

            //Test for 100 seconds

            List <uint> ssdValues;

            //10 seconds of nothing
            Console.WriteLine("Running program...");
            int tickLimit = (int)((6.25e6 * 10));

            ssdValues = RunTestCase(mBoard, tickLimit);

            if (ssdValues.Count != 0)
            {
                mMessage += "After running the program for 10 seconds without pressing any buttons, the following output was observed (expected nothing):\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nNote: SSD output is sampled when handling an exception. The timer should be disabled, so no exceptions should be taking place.\r\n";
                return(false);
            }

            //Press start, and make sure it starts counting
            Console.WriteLine("Pressed start; expecting SSDs to begin counting...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS));
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;

            ssdValues = RunTestCase(mBoard, tickLimit);
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2))
            {
                mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press reset, and make sure it continues counting
            Console.WriteLine("Pressed the lap button (at approx. 20 seconds); should have no effect on counting...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS));
            mBoard.Parallel.Buttons = (uint)Buttons.Reset;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 2, 4))
            {
                mMessage += "Ran your program for 40 (simulated) seconds; pressed the reset button at 20 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"39\" inclusive, in the correct order. The reset button should have no effect while the timer is running.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Check lap output
            if (mSerialBuf.Length == 0)
            {
                mMessage += "Did not observe the lap time printed to serial port 1\r\n";
                return(false);
            }
            else
            {
                double lapTime    = 0.0;
                string rawLapTime = mSerialBuf;//.Trim();
                mSerialBuf = "";

                if (Regex.Match(rawLapTime, @"\r\n\d\d\.\d\d").Success)
                {
                    //check numeric value
                    double.TryParse(rawLapTime.Trim(), out lapTime); //will succeed, because of rexex match above.
                    if (Math.Abs(20 - lapTime) > 0.200)
                    {
                        mMessage += "Expected to receive a lap time of approximately 20 seconds. Observed: \"" + rawLapTime.Trim() + "\"\r\n";
                        return(false);
                    }
                }
                else
                {
                    mMessage += "Lap output string was not in the correct format. Observed: \"" + rawLapTime.Trim() + "\". If this looks OK, make sure that you're correctly sending \\r and \\n before the numbers.";
                    return(false);
                }

                Console.WriteLine("Lap Time Received: {0}", rawLapTime.Trim());
            }

            //Change switches, and make sure it continues counting
            Console.WriteLine("Changed switch value; should have no effect...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS));
            mBoard.Parallel.Switches = 0x42;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6))
            {
                mMessage += "Ran your program for 60 (simulated) seconds; pressed the reset button at 20 seconds, and changed the switch value at 40 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order. Changing switches should have no effect.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press stop, and make sure it stops counting
            Console.WriteLine("Pressed stop; expecting the value shown on the SSDs to stop counting...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS));
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6))
            {
                mMessage += "Ran your program for 80 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, and pressed 'stop' at 60 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press start again, and make sure it continues counting
            Console.WriteLine("Pressed start; expecting the value shown on the SSDs to resume counting...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS));
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 4, 6))
            {
                mMessage += "Ran your program for 100 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, pressed 'stop' at 60 seconds, and 'start' at 80 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"79\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Hit the lap button again
            Console.WriteLine("Pressed the lap button again (at approx. 80 seconds); expecting counting to continue as usual...");
            mBoard.Parallel.Buttons = (uint)Buttons.Reset;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;
            Wag(mBoard, 1000000);

            //Check lap output
            if (mSerialBuf.Length == 0)
            {
                mMessage += "Did not observe the lap time printed to serial port 1\r\n";
                return(false);
            }
            else
            {
                double lapTime    = 0.0;
                string rawLapTime = mSerialBuf;//.Trim();
                mSerialBuf = "";

                if (Regex.Match(rawLapTime, @"\r\n\d\d\.\d\d").Success)
                {
                    //check numeric value
                    double.TryParse(rawLapTime.Trim(), out lapTime); //will succeed, because of rexex match above.
                    if (Math.Abs(80 - lapTime) > 0.500)
                    {
                        mMessage += "Expected to receive a lap time of approximately 80 seconds. Observed: \"" + rawLapTime.Trim() + "\"\r\n";
                        return(false);
                    }
                }
                else
                {
                    mMessage += "Lap output string was not in the correct format. Observed: \"" + rawLapTime.Trim() + "\". If this looks OK, make sure that you're correctly sending \\r and \\n before the numbers.";
                    return(false);
                }

                Console.WriteLine("Lap Time Received: {0}", rawLapTime.Trim());
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press stop, then reset, and make sure it goes back to zero
            Console.WriteLine("Pressed stop, then reset; expecting the timer to be disabled, and the value on the SSDs to be reset to zero...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS));
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.Reset;
            Wag(mBoard);
            ssdValues = RunTestCase(mBoard, tickLimit);
            if (!CheckSequence(ssdValues, 0, 0))
            {
                mMessage += "After pressing the reset button (button 1) while the timer is stopped, the SSDs are expected to be set to zero and the timer is supposed to remain stopped. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press start, and make sure it counts again
            Console.WriteLine("Pressed start again; expecting SSDs to start counting from zero...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS));
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;

            ssdValues = RunTestCase(mBoard, tickLimit);
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2))
            {
                mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            return(true);
        }
Exemplo n.º 15
0
        protected bool RunParallelTestCase(RexBoard mBoard, uint switches)
        {
            //Set switches
            mBoard.Parallel.Switches = switches;

            //Check that the SSDs don't change without waiting for a button press
            uint ssdOld = mBoard.Parallel.SSD & 0xFFFF;

            Tick(mBoard);
            if (ssdOld != (mBoard.Parallel.SSD & 0xFFFF))
            {
                mMessage += "SSDs changed value without waiting for a button press.\r\n";
                return(false);
            }

            //Press button 0
            mBoard.Parallel.Buttons = 1;
            Tick(mBoard);
            if (mBoard.Parallel.SSD != switches)
            {
                mMessage += string.Format("SSDs did not show the correct value after pressing button 0 (observed: {0:X4}, switches: {1:X4})\r\n", mBoard.Parallel.SSD, switches);
                return(false);
            }
            if ((switches & 0xFFFF) % 4 == 0)
            {
                if ((mBoard.Parallel.Leds & 0xFFFF) != 0xFFFF)
                {
                    mMessage += string.Format("LEDs were not high after pressing button 0 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches);
                    return(false);
                }
            }
            else
            {
                if ((mBoard.Parallel.Leds & 0xFFFF) != 0x0000)
                {
                    mMessage += string.Format("LEDs were not low after pressing button 0 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches);
                    return(false);
                }
            }

            //Press button 1
            mBoard.Parallel.Buttons = 2;
            Tick(mBoard);
            if ((mBoard.Parallel.SSD & 0xFFFF) != (~switches & 0xFFFF))
            {
                mMessage += string.Format("SSDs did not show the correct value after pressing button 1 (observed: {0:X4}, switches: {1:X4})\r\n", mBoard.Parallel.SSD, switches);
                return(false);
            }
            if ((~switches & 0xFFFF) % 4 == 0)
            {
                if ((mBoard.Parallel.Leds & 0xFFFF) != 0xFFFF)
                {
                    mMessage += string.Format("LEDs were not high after pressing button 1 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches);
                    return(false);
                }
            }
            else
            {
                if ((mBoard.Parallel.Leds & 0xFFFF) != 0x0000)
                {
                    mMessage += string.Format("LEDs were not low after pressing button 1 (observed: {0:X4}, switches {1:X4})\r\n", mBoard.Parallel.Leds, switches);
                    return(false);
                }
            }

            //Release buttons
            mBoard.Parallel.Buttons = 0;
            Tick(mBoard);
            return(true);
        }
Exemplo n.º 16
0
        internal bool SupressReset = false; //set true if you don't want to reset the board when calling .Mark()

        /// <summary>
        /// Executes a test case.
        /// </summary>
        /// <param name="sendSP1">String to send to the serial port SP1</param>
        /// <param name="sendSP2">String to send to the serial port SP2</param>
        /// <param name="expectSP1">String expected to be received from SP1</param>
        /// <param name="expectSP2">String expected to be received from SP2</param>
        /// <param name="board">The board to run the tests on.</param>
        /// <returns>True if the test passes.</returns>
        new protected bool RunSerialTestCase(string sendSP1, string sendSP2, string expectSP1, string expectSP2, RexBoard board)
        {
            /*Console.WriteLine("Test Case: ");
             * if (sendSP1.Length > 0)
             *  Console.WriteLine("-Sending '{0}' to SP1", sendSP1);
             * if (sendSP2.Length > 0)
             *  Console.WriteLine("-Sending '{0}' to SP2", sendSP2);
             * if (expectSP1.Length > 0)
             *  Console.WriteLine("-Expecting '{0}' from SP1", expectSP1);
             * if (expectSP2.Length > 0)
             *  Console.WriteLine("-Expecting '{0}' from SP2", expectSP2);*/

            mSP1RecvBuf = mSP2RecvBuf = "";
            EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler1 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial1_SerialDataTransmitted);
            EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs> serialHandler2 = new EventHandler <RexSimulator.Hardware.Rex.SerialIO.SerialEventArgs>(Serial2_SerialDataTransmitted);

            board.Serial1.SerialDataTransmitted += serialHandler1;
            board.Serial2.SerialDataTransmitted += serialHandler2;

            //uint pc = board.CPU.PC;
            //board.Reset(true);
            //board.CPU.PC = pc;
            //board.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.sp] = 0x07b7f; //sensible value for the stack pointer
            //board.CPU.mGpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.GpRegister.ra] = 0x80000; //when the CPU tries to jump here, the test is over.
            //board.CPU.mSpRegisters[RexSimulator.Hardware.Wramp.RegisterFile.SpRegister.evec] = 0x80000; //when the CPU tries to jump here, the test is over (should jal to exit, which is syscall).

            int clocks = CLOCK_CYCLE_LIMIT;

            //Transmit the data
            string toSend1 = sendSP1;
            string toSend2 = sendSP2;

            while (clocks-- > 0)
            {
                board.Tick();
                if (toSend1.Length != 0 && board.Serial1.SendAsync(toSend1[0]))
                {
                    toSend1 = toSend1.Substring(1);
                }

                if (toSend2.Length != 0 && board.Serial2.SendAsync(toSend2[0]))
                {
                    toSend2 = toSend2.Substring(1);
                }
            }

            //board.CPU.PC = pc;
            board.Serial1.SerialDataTransmitted -= serialHandler1;
            board.Serial2.SerialDataTransmitted -= serialHandler2;

            //Make sure the Done message was received
            if (mSP1RecvBuf != expectSP1)
            {
                if (sendSP1.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1);
                }
                if (sendSP2.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2);
                }

                mMessage += string.Format("Received \"{0}\" from SP1, expected \"{1}\"\r\n", mSP1RecvBuf, expectSP1);
                return(false);
            }

            if (mSP2RecvBuf != expectSP2)
            {
                if (sendSP1.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP1\r\n", sendSP1);
                }
                if (sendSP2.Length != 0)
                {
                    mMessage += string.Format("Sent \"{0}\" to SP2\r\n", sendSP2);
                }

                mMessage += string.Format("Received \"{0}\" from SP2, expected \"{1}\"\r\n", mSP2RecvBuf, expectSP2);
                return(false);
            }

            return(true);
        }
Exemplo n.º 17
0
 protected abstract bool DoSubTest(RexBoard mBoard);
Exemplo n.º 18
0
        internal bool SupressReset = false; //set true if you don't want to reset the board when calling .ResetBoard()

        /// <summary>
        /// Set up the state we want to restore when ResetBoard is called.false
        /// Note that this pair of functions won't preserve pre-initialised memory!
        /// </summary>
        protected void Initialise(RexBoard mBoard)
        {
            originalPC = mBoard.CPU.PC;
        }
Exemplo n.º 19
0
 /// <summary>
 /// Marks the program.
 /// </summary>
 /// <param name="mBoard">The REX board, pre-loaded with the program to test.</param>
 /// <returns>True if the program passes, false otherwise.</returns>
 public abstract bool Mark(RexBoard mBoard);
Exemplo n.º 20
0
        protected override bool DoSubTest(RexBoard mBoard)
        {
            //Ensure timer is set up right
            if (mBoard.Timer.Control != 2)
            {
                mMessage += string.Format("Timer control register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Control);
                return(false);
            }
            if (mBoard.Timer.Load != 2400)
            {
                mMessage += string.Format("Timer load register is incorrect. Was 0x{0:X8}.\r\n", mBoard.Timer.Load);
                return(false);
            }

            //Test for 100 seconds

            //Sneaky speedup to 10 interrupts per second, to save time.
            mBoard.Timer.Count = 0;
            mBoard.Timer.Load  = 240;

            List <uint> ssdValues;

            //10 seconds of nothing
            Console.WriteLine("Running program...");
            int tickLimit = (int)((6.25e6 * 10) / 10);

            ssdValues = RunTestCase(mBoard, tickLimit);

            if (ssdValues.Count != 0)
            {
                mMessage += "After running the program for 10 seconds without pressing any buttons, the following output was observed (expected nothing):\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nNote: SSD output is sampled when handling an exception. The timer should be disabled, so no exceptions should be taking place.\r\n";
                return(false);
            }

            //Press start, and make sure it starts counting
            Console.WriteLine("Pressed start; expecting SSDs to begin counting...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10);
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;

            ssdValues = RunTestCase(mBoard, tickLimit);
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2))
            {
                mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press reset, and make sure it continues counting
            Console.WriteLine("Pressed reset; should have no effect...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10);
            mBoard.Parallel.Buttons = (uint)Buttons.Reset;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 2, 4))
            {
                mMessage += "Ran your program for 40 (simulated) seconds; pressed the reset button at 20 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"39\" inclusive, in the correct order. The reset button should have no effect while the timer is running.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Change switches, and make sure it continues counting
            Console.WriteLine("Changed switch value; should have no effect...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10);
            mBoard.Parallel.Switches = 0x42;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6))
            {
                mMessage += "Ran your program for 60 (simulated) seconds; pressed the reset button at 20 seconds, and changed the switch value at 40 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order. Changing switches should have no effect.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press stop, and make sure it stops counting
            Console.WriteLine("Pressed stop; expecting the value shown on the SSDs to stop counting...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10);
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 3, 6))
            {
                mMessage += "Ran your program for 80 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, and pressed 'stop' at 60 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"59\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press start again, and make sure it continues counting
            Console.WriteLine("Pressed start; expecting the value shown on the SSDs to resume counting...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10);
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;
            ssdValues.AddRange(RunTestCase(mBoard, tickLimit));
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS * 4, 6))
            {
                mMessage += "Ran your program for 100 (simulated) seconds; pressed the reset button at 20 seconds, changed the switch value at 40 seconds, pressed 'stop' at 60 seconds, and 'start' at 80 seconds. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"79\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press stop, then reset, and make sure it goes back to zero
            Console.WriteLine("Pressed stop, reset; expecting the timer to be disabled, and the value on the SSDs to be reset to zero...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10);
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.Reset;
            Wag(mBoard);
            ssdValues = RunTestCase(mBoard, tickLimit);
            if (!CheckSequence(ssdValues, 0, 0))
            {
                mMessage += "After pressing the reset button while the timer is stopped, the SSDs are expected to be set to zero and the timer is supposed to remain stopped. Observed:\r\n";
                PrintSsdSequence(ssdValues);
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            //Press start, and make sure it counts again
            Console.WriteLine("Pressed start again; expecting SSDs to start counting from zero...");
            tickLimit = (int)((6.25e6 * SUBTEST_DURATION_SECONDS) / 10);
            mBoard.Parallel.Buttons = (uint)Buttons.Start;
            Wag(mBoard);
            mBoard.Parallel.Buttons = (uint)Buttons.None;

            ssdValues = RunTestCase(mBoard, tickLimit);
            if (!CheckSequence(ssdValues, SUBTEST_DURATION_SECONDS, 2))
            {
                mMessage += "Ran your program for 20 (simulated) seconds after pressing the start button, and observed:\r\n";
                PrintSsdSequence(ssdValues);
                mMessage += "\r\nExpected to see a sequence from \"00\" to \"19\" inclusive, in the correct order.\r\n";
                return(false);
            }
            if (mBoard.Parallel.InterruptAck != 0)
            {
                mMessage += "Parallel Interrupts were not acknowledged.";
                return(false);
            }

            // We can't test both quitting and a functioning GPF...

            /*
             * // Press quit, and make sure it quits
             * Console.WriteLine("Pressed quit; expecting the program to exit...");
             * mBoard.Parallel.Buttons = (uint)Buttons.Exit;
             * Wag(mBoard);
             * if (!(mBoard.CPU.PC >= WRAMPMON_ADDRESS))
             * {
             *  mMessage += "Pressed quit, and the program did not exit.";
             *  return false;
             * }
             *
             * if (!(mBoard.CPU.mSpRegisters[RegisterFile.SpRegister.evec] >= WRAMPMON_ADDRESS))
             * {
             *  mMessage += "Pressed quit, but $evec doesn't point to the original exception handler.\r\n";
             *  return false;
             * }
             */
            return(true);
        }
Exemplo n.º 21
0
        /// <summary>
        /// Executes a test case for questions 2 and 3. Initialise() should be called before this function.
        /// </summary>
        /// <param name="board">The RexBoard to use.</param>
        /// <param name="switches">The value to set the switches to for this case.</param>
        /// <param name="questionNumber">Either 2 or 3 depending on the question.</param>
        protected bool TryTestCase(RexBoard board, uint switches, bool startFirst)
        {
            ResetBoard(board);
            board.Parallel.Switches = switches;
            List <uint> ssdValues = new List <uint> {
            };

            uint start, end;

            if (startFirst)
            {
                start = (switches >> 8) & 0xFF;
                end   = switches & 0xFF;
            }
            else
            {
                end   = (switches >> 8) & 0xFF;
                start = switches & 0xFF;
            }

            // We start the SSDs at a value different from the one they should start at when
            // the program begins - this value is ignored, but a change is detected unless
            // the student writes the same number to the same register.
            // Of course, this number is pretty nonsensical so it's unlikely.
            board.Parallel.RightSSD = 0xdead1ed;
            uint oldLRSSD = board.Parallel.RightSSD;

            for (int i = 0; i < TEST_DURATION_TICKS; i++)
            {
                // Hacky way to detect when the program is in delay() and skip the loop
                // Speeds up marking by around a hundred thousand times, but relies on
                // the delay() function allocating its specialNumber local variable to
                // a particular register - Perfect for wcc!
                if (board.CPU.mGpRegisters[RegisterFile.GpRegister.r13] == 0xdeadf00d)
                {
                    // We use 5 to preserve the stack rather than jumping straight out of the
                    // function, and change the register so this doesn't happen several times.
                    board.CPU.PC += 5;
                    board.CPU.mGpRegisters[RegisterFile.GpRegister.r13] = 0xbeeff00d;
                }

                board.Tick();

                uint ssd = GetSSDNumber(board.Parallel.SSD);

                // We check the rightmost SSD since it's guaranteed to change on any counting operation
                if (board.Parallel.RightSSD != oldLRSSD)
                {
                    // Uncomment these if you want some more verbose output during the test.
                    // It happens so fast with the delay skip that it's not really worth keeping around.

                    /*
                     * try
                     * {
                     *  Console.Write($"{new string(' ', Console.WindowWidth)}\r");
                     *  Console.Write($"Test case {c+1}/{TEST_CASES.Length} (0x{switches:X4}): {ssd}\r");
                     * }
                     * catch (System.IO.IOException)
                     * {
                     *  // Git bash doesn't support the WindowWidth call.
                     * }
                     */

                    ssdValues.Add(ssd);

                    // Don't exit early if there should be no counting, just to make sure they don't count.
                    if (start != end && ssd == end)
                    {
                        break;
                    }
                    oldLRSSD = board.Parallel.RightSSD;
                }
            }
            try
            {
                Console.Write($"{new string(' ', Console.WindowWidth)}\r");
            }
            catch (System.IO.IOException)
            {
                // Git bash doesn't support the WindowWidth call.
            }

            // If there should be counting, but there isn't, complain.
            if (ssdValues.Count == 0)
            {
                if (start == end)
                {
                    return(true);
                }
                else
                {
                    mMessage = $"I didn't see any counting. The switches were {switches:X4}.";
                    return(false);
                }
            }
            // If there shouldn't be counting, but there is, complain.
            if (start == end)
            {
                if (ssdValues.Count == 1 && ssdValues[0] == start)
                {
                    return(true);
                }
                else
                {
                    mMessage  = $"You counted when you shouldn't have! The switches were {switches:X4}.";
                    mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues);
                }
            }

            // If they started or finished counting at the wrong place, complain.
            if (ssdValues[0] != start)
            {
                mMessage  = $"Counting started at {ssdValues[0]}, not {start} when the switches were 0x{switches:X4}.";
                mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues);
                return(false);
            }
            if (ssdValues[ssdValues.Count - 1] != end)
            {
                mMessage  = $"Counting ended at {ssdValues[ssdValues.Count - 1]}, not {end} when the switches were 0x{switches:X4}.";
                mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues);
                return(false);
            }

            // If they counted in a weird order, complain.
            // Of course it's good code when I'm duplicating everything except the direction!
            if (start < end)
            {
                uint previousValue = uint.MinValue;
                foreach (uint value in ssdValues)
                {
                    if (value < previousValue)
                    {
                        if (value == previousValue - 1)
                        {
                            mMessage = $"Counting was backwards when the switches were {switches:X4}.";
                        }
                        else
                        {
                            mMessage = $"Counting did not happen in the correct order when the switches were {switches:X4}.";
                        }
                        mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues);
                        return(false);
                    }
                    if (previousValue != uint.MinValue && value != previousValue + 1)
                    {
                        mMessage  = $"It's not counting when you go from {previousValue} to {value}! The switches were {switches:X4}";
                        mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues);
                        return(false);
                    }
                }
            }
            if (start > end)
            {
                uint previousValue = uint.MaxValue;
                foreach (uint value in ssdValues)
                {
                    if (value > previousValue)
                    {
                        if (value == previousValue + 1)
                        {
                            mMessage = $"Counting was backwards when the switches were {switches:X4}.";
                        }
                        else
                        {
                            mMessage = $"Counting did not happen in the correct order when the switches were {switches:X4}.";
                        }
                        mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues);
                        return(false);
                    }
                    if (previousValue != uint.MaxValue && value != previousValue - 1)
                    {
                        mMessage  = $"It's not counting when you go from {previousValue} to {value}! The switches were {switches:X4}";
                        mMessage += Environment.NewLine + "Got numbers: " + GetCappedLengthList(ssdValues);
                        return(false);
                    }
                }
            }
            return(true);
        }