// Copyright (c) 2004 Rowley Associates Limited.
//
// This file may be distributed under the terms of the License Agreement
// provided with this software.
//
// THIS FILE IS PROVIDED AS IS WITH NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

// Test interruptible ISRs (NOTE: Requires timer to have higher priority than button)

#include <string.h>
#include <assert.h>
#include <ctl_api.h>

#define CALLSTACKSIZE 16 // this is only required for AVR builds
#define STACKSIZE 64
CTL_TASK_t mainTask, otherTask;
unsigned otherStack[1+STACKSIZE+1];
unsigned e1=0;
unsigned timerCount = 0;
unsigned buttonPressedCount = 0;

#define TIMER_EVENT (1<<0)

static int inButtonPressedISR;
static int inTimerISR;

void 
buttonPressedISR(void)
{
  /* Check buttonPressedISR doesn't re-enter */
  assert(!inButtonPressedISR);
  /* Check that timer interrupt hasn't been interrupted */
  assert(!inTimerISR);
  /* Signal that buttonPressedISR is executing */
  inButtonPressedISR = 1;
  /* Reset timer counter */
  timerCount = 0;
  /* Re-enable interrupts */
  ctl_global_interrupts_re_enable_from_isr();
  /* Wait for timer interrupt to trigger */
  while (timerCount == 0);
  ++buttonPressedCount;
  /* Disable interrupts */
  ctl_global_interrupts_un_re_enable_from_isr();
  /* Signal that buttonPressedISR is no longer executing */
  inButtonPressedISR = 0;
}

void
timerISR(void)
{
  /* Check that timerISR doesn't re-enter */
  assert(!inTimerISR);
  /* Signal that timerISR is executing */
  inTimerISR = 1;
  /* Set timer event only if timerISR has interrupted buttonPressedISR */
  if (inButtonPressedISR)
    ctl_events_set_clear(&e1, TIMER_EVENT, 0);
  ++timerCount;
  /* Signal that timerISR is no longer executing */
  inTimerISR = 0;
}

void 
other(void *p)
{
  unsigned leds = 0;
  while (1)
    {
      unsigned w=ctl_events_wait(CTL_EVENT_WAIT_ANY_EVENTS, &e1, TIMER_EVENT, 0, 0);
      if (w & TIMER_EVENT)
        {
          ctl_events_set_clear(&e1, 0, TIMER_EVENT);
          leds ^= 0xFFFFFFFF;
          ctl_board_set_leds(leds);
        }
        else
          ; // error case
    }
}

void
ctl_handle_error(CTL_ERROR_CODE_t e)
{
  while (1);
}

void
__assert(const char *expr, const char *file, int line)
{
  while (1);
}

int main(void)
{
  unsigned int v=0;
  ctl_board_init();
  ctl_task_init(&mainTask, 255, "main");  
  ctl_start_timer(timerISR);
  ctl_board_on_button_pressed(buttonPressedISR);
  memset(otherStack, 0xcd, sizeof(otherStack));
  otherStack[0]=otherStack[1+STACKSIZE]=0xfacefeed;
  ctl_task_run(&otherTask, 1, other, 0, "other", STACKSIZE, otherStack+1, CALLSTACKSIZE);
  ctl_task_set_priority(&mainTask, 0);
  while (1)
    {
      ;
    }
  return 0;
}
