#include "system.h"

void Dispatcher(void) {
  /* pick a process to run */
  current_process = SelectProcessToRun();
  /* and run it */
  RunProcess(current_process);
  /* this procedure call will not return */
}

int SelectProcessToRun() {
  /* next_proc is a static variable so that we will remember the last
     process selected and start searching from there.  We start with a
     high value that will be wrapped to 1 the first time the
     dispatcher is called. */

  static int next_proc = MAX_PROCESSES;

  /* If the process was interrupted by a disk interrupt or made a
     system call that did not block, then we let it use the rest of
     the time slice that was alloted to it. */
  if (current_process > 0 
      && pd[current_process].slotAllocated
      && pd[current_process].state == READY
      && pd[current_process].timeLeft > 0)
    return current_process;

  /* We start from the current value of next_proc and wrap around when
     we get to the end.  i counts the number of iterations so we can
     easily tell when we have looked through the entire array. */
  for (int i=1; i < MAX_PROCESSES; i++) {
    /* if we have gotten to the end of the process table then wrap
       around to the beginning. */
    next_proc++;
    if (next_proc >= MAX_PROCESSES)
      /* slot 0 and pid 0 are reserved for the OS so start at 1. */
      next_proc = 1;

    /* next_proc is the process we are looking at */
    if (pd[next_proc].slotAllocated && pd[next_proc].state == READY) {
      pd[next_proc].timeLeft = TIME_QUANTUM;
      pd[next_proc].state = RUNNING;
      return next_proc;
    }
  }
  /* if we drop through the loop then no process is ready to run. */
  return -1;
}

void RunProcess(int pid) {
  int quantum;
  struct SaveArea *savearea;

  /* was a process picked to run? */
  if (pid > 0) {
    /* dispatch the process */
    savearea = &(pd[pid].sa);
    quantum = pd[pid].timeLeft;
    
    /* assembly code to handle restoring the hardware */
    asm {
      /* ia and psw will be taken from iia and ipsw by the rti instr. */
      load savearea+0, iia;
      load savearea+4, ipsw;
      /* restore base and bound registers and saved values */
      load savearea+8, base;
      load savearea+12, bound;
      /* restore all the saved general registers */
      loadall savearea+16;
      /* set the interval timer */
      load quantum, timer;
      /* return from interrupt restarts the process */
      rti;
      /* rti does not return, since it sets the ia register */
    }
    
  } else {
    /* if there is no process to run then they must all be waiting for
       an interrupt.  Loop the processor and wait for interrupts. */
    while (1) /* waitloop */;
    /* control never drops out of the tight loop */
  }
}

