//*****************************************************************************
//
// This task manages the scurrying about of a spider.
//
//*****************************************************************************
static void
SpiderTask(void *pvParameters)
{
    uint32_t ui32Dir, ui32Image, ui32Temp;
    int32_t i32X, i32Y, i32Spider;

    //
    // Get the spider number from the parameter.
    //
    i32Spider = (long)pvParameters;

    //
    // Add the current tick count to the random entropy pool.
    //
    RandomAddEntropy(xTaskGetTickCount());

    //
    // Reseed the random number generator.
    //
    RandomSeed();

    //
    // Indicate that this spider is alive.
    //
    HWREGBITW(&g_ui32SpiderAlive, i32Spider) = 1;

    //
    // Indicate that this spider is not dead yet.
    //
    HWREGBITW(&g_ui32SpiderDead, i32Spider) = 0;

    //
    // Get a local copy of the spider's starting position.
    //
    i32X = g_pi32SpiderX[i32Spider];
    i32Y = g_pi32SpiderY[i32Spider];

    //
    // Choose a random starting direction for the spider.
    //
    ui32Dir = RandomNumber() >> 29;

    //
    // Start by displaying the first of the two spider animation images.
    //
    ui32Image = 0;

    //
    // Loop forever.
    //
    while(1)
    {
        //
        // See if this spider has been killed.
        //
        if(HWREGBITW(&g_ui32SpiderDead, i32Spider) == 1)
        {
            //
            // Wait for 2 seconds.
            //
            vTaskDelay((1000 / portTICK_RATE_MS) * 2);

            //
            // Clear the spider from the display.
            //
            DisplayImage(i32X - (SPIDER_WIDTH / 2), i32Y - (SPIDER_HEIGHT / 2),
                         g_pui8SpiderBlankImage);

            //
            // Indicate that this spider is not alive.
            //
            HWREGBITW(&g_ui32SpiderAlive, i32Spider) = 0;

            //
            // Delete the current task.  This should never return.
            //
            vTaskDelete(NULL);

            //
            // In case it does return, loop forever.
            //
            while(1)
            {
            }
        }

        //
        // Enter a critical section while the next move for the spider is
        // determined.  Having more than one spider trying to move at a time
        // (via preemption) would make the collision detection check fail.
        //
        taskENTER_CRITICAL();

        //
        // Move the spider.
        //
        i32X += g_pi32SpiderStepX[ui32Dir];
        i32Y += g_pi32SpiderStepY[ui32Dir];

        //
        // See if the spider has cross the boundary of its area, if it has
        // collided with another spider, or if random chance says that the
        // spider should turn despite not having collided with anything.
        //
        if((i32X < SPIDER_MIN_X) || (i32X > SPIDER_MAX_X) ||
           (i32Y < SPIDER_MIN_Y) || (i32Y > SPIDER_MAX_Y) ||
           (SpiderCollide(i32Spider, i32X, i32Y) != -1) ||
           (RandomNumber() < 0x08000000))
        {
            //
            // Undo the previous movement of the spider.
            //
            i32X -= g_pi32SpiderStepX[ui32Dir];
            i32Y -= g_pi32SpiderStepY[ui32Dir];

            //
            // Get a random number to determine the turn to be made.
            //
            ui32Temp = RandomNumber();

            //
            // Determine how to turn the spider based on the random number.
            // Half the time the spider turns to the left and half the time it
            // turns to the right.  Of each half, it turns a quarter of a turn
            // 12.5% of the time and an eighth of a turn 87.5% of the time.
            //
            if(ui32Temp < 0x10000000)
            {
                ui32Dir = (ui32Dir + 2) & 7;
            }
            else if(ui32Temp < 0x80000000)
            {
                ui32Dir = (ui32Dir + 1) & 7;
            }
            else if(ui32Temp < 0xf0000000)
            {
                ui32Dir = (ui32Dir - 1) & 7;
            }
            else
            {
                ui32Dir = (ui32Dir - 2) & 7;
            }
        }

        //
        // Update the position of the spider.
        //
        g_pi32SpiderX[i32Spider] = i32X;
        g_pi32SpiderY[i32Spider] = i32Y;

        //
        // Exit the critical section now that the spider has been moved.
        //
        taskEXIT_CRITICAL();

        //
        // Have the display task draw the spider at the new position.  Since
        // there is a one pixel empty border around all the images, and the
        // position of the spider is incremented by only one pixel, this also
        // erases any traces of the spider in its previous position.
        //
        DisplayImage(i32X - (SPIDER_WIDTH / 2), i32Y - (SPIDER_HEIGHT / 2),
                     g_ppui8SpiderImage[(ui32Dir * 2) + ui32Image]);

        //
        // Toggle the spider animation index.
        //
        ui32Image ^= 1;

        //
        // Delay this task for an amount of time based on the direction the
        // spider is moving.
        //
        vTaskDelay(g_pui32SpiderDelay[ui32Dir & 1]);

        //
        // Add the new tick count to the random entropy pool.
        //
        RandomAddEntropy(xTaskGetTickCount());

        //
        // Reseed the random number generator.
        //
        RandomSeed();
    }
}
//*****************************************************************************
//
// Handles the SysTick timeout interrupt.
//
//*****************************************************************************
void
SysTickIntHandler(void)
{
    unsigned long ulData, ulDelta;

    //
    // Increment the tick count.
    //
    g_ulTickCount++;

    //
    // Indicate that a timer interrupt has occurred.
    //
    HWREGBITW(&g_ulFlags, FLAG_CLOCK_TICK) = 1;

    //
    // Increment the screen update count.
    //
    g_ucScreenUpdateCount++;

    //
    // See if 1/30th of a second has passed since the last screen update.
    //
    if(g_ucScreenUpdateCount == (CLOCK_RATE / 30))
    {
        //
        // Restart the screen update count.
        //
        g_ucScreenUpdateCount = 0;

        //
        // Request a screen update.
        //
        HWREGBITW(&g_ulFlags, FLAG_UPDATE) = 1;
    }

    //
    // Update the music/sound effects.
    //
    AudioHandler();

    //
    // Increment the application update count.
    //
    g_ucAppUpdateCount++;

    //
    // See if 1/100th of a second has passed since the last application update.
    //
    if(g_ucAppUpdateCount != (CLOCK_RATE / 100))
    {
        //
        // Return without doing any further processing.
        //
        return;
    }

    //
    // Restart the application update count.
    //
    g_ucAppUpdateCount = 0;

    //
    // Run the Ethernet handler.
    //
    EnetTick(10);

    //
    // Read the state of the push buttons.
    //
    ulData = (GPIOPinRead(GPIO_PORTE_BASE, (GPIO_PIN_0 | GPIO_PIN_1 |
                                            GPIO_PIN_2 | GPIO_PIN_3)) |
              (GPIOPinRead(GPIO_PORTF_BASE, GPIO_PIN_1) << 3));

    //
    // Determine the switches that are at a different state than the debounced
    // state.
    //
    ulDelta = ulData ^ g_ucSwitches;

    //
    // Increment the clocks by one.
    //
    g_ucSwitchClockA ^= g_ucSwitchClockB;
    g_ucSwitchClockB = ~g_ucSwitchClockB;

    //
    // Reset the clocks corresponding to switches that have not changed state.
    //
    g_ucSwitchClockA &= ulDelta;
    g_ucSwitchClockB &= ulDelta;

    //
    // Get the new debounced switch state.
    //
    g_ucSwitches &= g_ucSwitchClockA | g_ucSwitchClockB;
    g_ucSwitches |= (~(g_ucSwitchClockA | g_ucSwitchClockB)) & ulData;

    //
    // Determine the switches that just changed debounced state.
    //
    ulDelta ^= (g_ucSwitchClockA | g_ucSwitchClockB);

    //
    // See if any switches just changed debounced state.
    //
    if(ulDelta)
    {
        //
        // Add the current tick count to the entropy pool.
        //
        RandomAddEntropy(g_ulTickCount);
    }

    //
    // See if the select button was just pressed.
    //
    if((ulDelta & 0x10) && !(g_ucSwitches & 0x10))
    {
        //
        // Set a flag to indicate that the select button was just pressed.
        //
        HWREGBITW(&g_ulFlags, FLAG_BUTTON_PRESS) = 1;
    }
}