#include <sys/syscall.h>
#include <xpseudo_asm.h>
#include <sys/process.h>
#include <sys/sema.h>
#include <sys/msg.h>
#include <sys/mem.h>
#include <sys/shm.h>
#include <xtime_l.h>
#include <xexception_l.h>

// extern void timer_int_handler(int);
/**
 * this function is part of the kernel and is not linked with user code 
 * this function is the interrupt handler for system call interrupts
 * @param baseaddr_p points to the syscall data area allocated in main.c and
 * used when registering this function as the handler for sys call interrupts
 * When a user makes a system call, the params are stored in the structure
 * pointed to by SPRG4 and the "sc" instruction is executed, causing a system 
 * call exception that passes the same address here as baseaddr_p
 */
void system_call_handler(void *baseaddr_p) {
  // int baseaddr = *((int *)baseaddr_p);
  struct t_syscall_data *sc_data = (struct t_syscall_data *)baseaddr_p;
 
//  xil_printf("in system call\n\r");
 // putnum(sc_data->system_call_num);
  /* a switch statement that calls the specific kernel function with params */
  switch(sc_data->system_call_num) {
#ifdef CONFIG_PROCESS
  case _MB_Process_Create: 
    sc_data->param1 = (void *)sys_process_create((unsigned int)sc_data->param2,
                       (unsigned int)sc_data->param3); 
    break;
#ifdef CONFIG_PROCESS_EXIT
  case _MB_Process_Exit: 
    sc_data->param1 = (void *)sys_exit();
    break;
#endif
#ifdef CONFIG_PROCESS_KILL
  case _MB_Process_Kill:
    sc_data->param1 = (void *)sys_process_kill((unsigned int)sc_data->param2);
    break;
#endif
  case _MB_Process_Status:
    sc_data->param1 = (void *)sys_process_status((unsigned int)sc_data->param2,
                       (p_stat *)sc_data->param3);
    break;
#ifdef CONFIG_PROCESS_SLEEP
  case _MB_Process_Sleep: 
    sc_data->param1 = (void *)sys_process_sleep((unsigned int)sc_data->param2);
    break;
#endif
#ifdef CONFIG_PROCESS_YIELD
  case _MB_Process_Yield: 
   sc_data->param1 = (void *) sys_process_yield(); 
    break;
#endif
  case _MB_Process_GetPID: 
    sc_data->param1 = (void *)sys_get_currentPID();
    break;
  case _MB_Process_GetPriority: 
    sc_data->param1 = (void *)sys_process_getpriority();
    break;
  case _MB_Process_SetPriority: 
    sc_data->param1 = 
      (void *)sys_process_setpriority((unsigned int)sc_data->param2);
    break;
#ifdef CONFIG_THREAD_SUPPORT
  case _MB_Thread_Create: 
    sc_data->param1 = 
      (void *)sys_thread_create(sc_data->param2,
                                (int)sc_data->param3,
                                (unsigned int)sc_data->param4);
    break;
  case _MB_Thread_Exit:
    sc_data->param1 = (void *)sys_thread_exit();
    break;
#endif
#endif
#ifdef CONFIG_SEMA
  case _MB_Sem_Init:
    sc_data->param1 = 
      (void *)sys_sema_init((semaphore **)sc_data->param2,
                            (unsigned int)sc_data->param3);
    break;
  case _MB_Sem_Wait:
    sc_data->param1 = (void *)sys_sema_wait((semaphore *)sc_data->param2);
    break;
  case _MB_Sem_Wait_BLOCKED:
    sc_data->param1 = (void *)sys_sema_wait_blocked((semaphore *)sc_data->param2);
    break;
  case _MB_Sem_TryWait:
    sc_data->param1 = (void *)sys_sema_trywait((semaphore *)sc_data->param2); 
    break;
  case _MB_Sem_Post:
    sc_data->param1 = (void *)sys_sema_post((semaphore *)sc_data->param2); 
    break;
  case _MB_Sem_Destroy:
    sc_data->param1 = (void *)sys_sema_destroy((semaphore *)sc_data->param2);  
    break;
#endif
#ifdef CONFIG_MSGQ
  case _MB_MsgGet: 
    sc_data->param1 = 
      (void *)sys_msgget((unsigned int)sc_data->param2,
                         (unsigned int)sc_data->param3,
                         (unsigned int)sc_data->param4,
                         (unsigned int)sc_data->param5);
    break;
  case _MB_MsgCtl:
    sc_data->param1 = 
      (void *)sys_msgctl((unsigned int)sc_data->param2,
                         (unsigned int)sc_data->param3,
                         (struct msgid_ds *)sc_data->param4);
    break;
  case _MB_MsgSend:
    sc_data->param1 = 
      (void *)sys_msgsend((unsigned int)sc_data->param2,
                          sc_data->param3,
                          (unsigned int)sc_data->param4,
                          (unsigned int)sc_data->param5); 
    break;
  case _MB_MsgRecv:
    sc_data->param1 = 
      (void *)sys_msgrecv((unsigned int)sc_data->param2,
                          sc_data->param3,
                          (unsigned int)sc_data->param4,
                          (unsigned int)sc_data->param5, 
                          (unsigned int)sc_data->param6);
    break;
#endif
#ifdef CONFIG_SHM
  case _MB_ShmGet:
    sc_data->param1 = 
      (void *) sys_shmget((unsigned int)sc_data->param2, 
                       (unsigned int) sc_data->param3, 
                       (unsigned int)sc_data->param4);
    break;
  case _MB_ShmCtl:
    sc_data->param1 = 
      (void *) sys_shmctl((unsigned int)sc_data->param2, 
                                (unsigned int) sc_data->param3, 
                                (struct shmid_ds *)sc_data->param4);
    break;
  case _MB_Shmat:
    sys_shmat((unsigned int)sc_data->param2, 
              sc_data->param3, 
              (unsigned int)sc_data->param4);
    break;
  case _MB_Shmdt:
    sc_data->param1 = (void *) sys_shmdt(sc_data->param2);
    break;
#endif
#ifdef CONFIG_MALLOC
  case _MB_Malloc: 
    sc_data->param1 = bufmalloc((unsigned int) sc_data->param2);
    break;
  case _MB_Free:
    buffree(sc_data->param2);
    break;
#endif
  // case _MB_Mutex_Init:
  //   sc_data->param1 = (void *)mutex_init((mutex **)sc_data->param2);
  //   break;
  // case _MB_Mutex_Lock:
  //   sc_data->param1 = (void *)mutex_lock((mutex *)sc_data->param2);
  //   break;
  // case _MB_Mutex_Unlock:
  //   sc_data->param1 = (void *)mutex_unlock((mutex *)sc_data->param2); 
  //   break;
  // case _MB_Mutex_Destroy:
  //   sc_data->param1 = (void *)mutex_destroy((mutex *)sc_data->param2); 
  //   break;
  case SYSCALL_POP_TO_SCHEDULER: 
    // timer_int_handler(0); // this invokes an rfi instruction so control normally never comes back here
    XExc_mDisableExceptions(XEXC_NON_CRITICAL);	// because we are still within a system call
    XTime_PITDisableInterrupt();
    process_scheduler();
    XTime_TSRClearStatusBits(XREG_TSR_PIT_INTERRUPT_STATUS);
    XTime_PITDisableInterrupt(); // make sure interrupts are cleared and disabled
    break; // results in restoring stack from context in USPRG0 and rfi instruction

  default: break;
  }

}
