/*
 * swi.c
 *
 * $Id: swi.c,v 1.12 2002/08/03 17:52:57 tomdean Exp $
 *
 * Functions
 *
 *  now
 *    wait timer (seconds)
 *    halt task (n)
 *    start task (n)
 *
 *  future
 *    send message to task (n) (message)
 *    recv message
 *    queue to task (n)
 *    dequeue
 *  
 */
#include <sys/regs.h>
#include <sys/ports_def.h>
#include <os.h>
#include <lcd.h>    /* cur_task location */

/*
 * At the entrance to swi(), after the current soft
 * registers are saved on the stack, we have
 *
 *  |         |
 *  +---------+-------
 *  |  PC     |
 *  +---------+
 *  |  Y      |
 *  +---------+  swi saves these
 *  |  X      |
 *  +---------+
 *  |  D      |
 *  +---------+
 *  |  CCR    |
 *  +---------+--------
 *  | _.tmp   |
 *  +---------+
 *  | _.z     |
 *  +---------+ pushed in swi()'s preamble
 *  | _.xy    | these will be removed at the
 *  +---------+ bottom of swi()
 *  | _.frame |
 *  +---------+--------
 *  | _.d1    | --> _.frame:  A context switch top part will put 
 *  +---------+               _.d1 ... _.d8 here to make the stack
 *  |         |               look like the context switch stack
 *  +---------+               described in rti.c
 *  |         |
 *  
 * The address of the previous frame, if any, is at _.frame+1
 *
 * THE SWI FUNCTION DEPENDS ON THE CONTENTS OF THE STACK AT THE
 * POINT WHERE THE SP IS SAVED, 'SAV_SP(context[cur_task]->sp);'.
 * IF CHANGES ARE MADE BETWEEN THE TOP OF THE FUNCTION AND THE
 * POINT WHERE THE SP IS SAVED, THAT CHANGE THE CONTENTS OF THE STACK,
 * EITHER ADJUST THE STACK, OR, CHANGE THE ENTIRE CONTEXT SWITCH PROCESS.
 * LOOK AT THE .s FILE OR USE 'm6811-elf-objdump -d'
 *
 */

/*
 * having this as a function call in swi() will prevent stack changes
 * that may violate context switch requirements.  do_operation() uses
 * some variables on the stack.  But the net effect in swi() is no
 * change to the stack contents.
 *
 * THIS FUNCTION OPERATES ON GLOBAL VARIABLES
 */
unsigned short do_operation() {
  /* default is no context switch */
  register unsigned short ret = 0;
  register unsigned char task;
  register msg_t *ptr;
  
  /*
   * do the requested operation or fall-thru
   * XXX fix me - what to do if a not-supported operation is requested?
   * XXX fix me - add sys_call status
   */
  task = SYS_TASK;
  switch(SYS_OP) {
  case sys_wait_timer: {
	/* op, ticks */
	if (sys_call[cur_task].timer != 0) {
	  timer[cur_task] = sys_call[cur_task].timer;
	  context[cur_task]->state = wait_timer;
	  /* we are waiting, do a context switch */
	  ret = 0xff;
	}
	break;
  }
  case sys_stop: {
	if ((task >= 1) && (task <= NUM_TASK))
	  context[task]->state = halt;
	/*
	 * lcd_row_3 XXX fix me - necessary?
	 * st st st st st st st st
	 * rt rt rt rt rt rt rt rt
	 */
	lcd_row_3[3*(task-1)+1] = 's';
	lcd_row_3[3*(task-1)+2] = task+'0';
	/* op, task_num */
	break;
  }
  case sys_start: {
	/* op, task_num */
	if ((task >= 1) && (task <= NUM_TASK))
	  context[task]->state = ready;
	/*
	 * lcd_row_3 XXX fix me - necessary?
	 * st st st st st st st st
	 * rt rt rt rt rt rt rt rt
	 */
	lcd_row_3[3*(task-1)+1] = 'r';
	lcd_row_3[3*(task-1)+2] = task+'0';
	break;
  }
  case sys_send: {
	/* op, task_num, message_ptr */
	hex_to_ascii_2(cur_task, error1);
	hex_to_ascii_2(task, error1+3);
	hex_to_ascii_4((unsigned short)sys_call[cur_task].msg_ptr, error1+6);
	ptr = msg_queue[task];
	if (ptr == NULL) {
	  /* new message head */
	  msg_queue[task] = sys_call[cur_task].msg_ptr;
	  ptr = msg_queue[task];
	} else {
	  /* append the message */
	  while (ptr->next != NULL) ptr = ptr->next;
	  ptr->next = sys_call[cur_task].msg_ptr;
	  ptr = ptr->next;
	}
	if (ptr != NULL) ptr->next = NULL;
	lcd_row_3[3*(task-1)+1] = 'm';
	lcd_row_3[3*(task-1)+2] = task+'0';
	break;
  }
  case sys_recv: {
	/* op, message_ptr */
	ptr = msg_queue[cur_task];
	if (ptr == NULL) {
	  /* return null */
	  sys_call[cur_task].msg_ptr = NULL;
	} else {
	  /* dequeue the message */
	  sys_call[cur_task].msg_ptr = ptr;
	  ptr = ptr->next;
	  msg_queue[cur_task] = ptr;
	}
	break;
  }
  case sys_recv_wait: {
	/* op, message_ptr */
	ptr = msg_queue[cur_task];
	if (ptr == NULL) {
	  /* go into wait msg state */
	  context[cur_task]->state = wait_msg;
	  ret = 0xff;
	} else {
	  /* dequeue the message */
	  sys_call[cur_task].msg_ptr = ptr;
	  ptr = ptr->next;
	  msg_queue[cur_task] = ptr;
	}
	break;
  }
  case sys_done: {
	/* task is done, for now, do a context switch */
	ret = 0xff;
	break;
  }
  default:
	/* XXX fix me - what to do about error here? */
	ret = 0xff;
  }
  return ret;
}

void dequeue_msg() {
  register msg_t *ptr;
  if (context[cur_task]->state == wait_msg) {
	ptr = msg_queue[cur_task];
	if (ptr != NULL) {
	  /* mark the task as ready and dequeue the message */
	  context[cur_task]->state = ready;
	  sys_call[cur_task].msg_ptr = ptr;
	  ptr = ptr->next;
	  msg_queue[cur_task] = ptr;
	}
  }
  return;
}

/* handler */
void swi() {

  if (do_operation()) {
	/* curtask is unsigned so it is always >= 0 */
	/*
	 * XXXXX fix me - if cur_task is wrong, what do we do?
	 */
	if (cur_task > 8) cur_task = 0;   /* null task in main */
	POP_FRAME_Y;
	PSH_SOFT_REG(__tmp);
	PSH_SOFT_REG(__z);
	PSH_SOFT_REG(__xy);
	PSH_FRAME_Y;
	PSH_SOFT_REG(__d1);
	PSH_SOFT_REG(__d2);
	PSH_SOFT_REG(__d3);
	PSH_SOFT_REG(__d4);
	PSH_SOFT_REG(__d5);
	PSH_SOFT_REG(__d6);
	PSH_SOFT_REG(__d7);
	PSH_SOFT_REG(__d8);
	SAV_SP(context[cur_task]->sp);

	/* mark task as ready to run */
	if (context[cur_task]->state == run) context[cur_task]->state = ready;
  
	/*
	 * using the soft registers avoids making room for variables on the stack
	 * all soft registers have been saved, so using them is free
	 */
  
	/* find a runable task */
	__d8 = cur_task;
	while (++cur_task != __d8) {
	  if (cur_task > NUM_TASK) cur_task = 1;
	  dequeue_msg();
	  if (context[cur_task]->state == ready) break;
	}
	if ((cur_task == __d8) && (context[cur_task]->state != ready)) {
	  cur_task = 0; /* null task is always ready to run */
	}

	/*
	 * display current task number
	 */
	*(char *)(CUR_TASK_LOC) = cur_task + '0';

	/* mark task as run */
	context[cur_task]->state = run;
  
	/*
	 * complete the last half of the context switch
	 */
	tocks = TICKS_PER_TOCK;
	SET_SP(context[cur_task]->sp);
	POP_SOFT_REG(__d8);
	POP_SOFT_REG(__d7);
	POP_SOFT_REG(__d6);
	POP_SOFT_REG(__d5);
	POP_SOFT_REG(__d4);
	POP_SOFT_REG(__d3);
	POP_SOFT_REG(__d2);
	POP_SOFT_REG(__d1);
	POP_FRAME_Y;
	POP_SOFT_REG(__xy);
	POP_SOFT_REG(__z);
	POP_SOFT_REG(__tmp);
	PSH_FRAME_Y;
	
  }
}
