#include         <alloc.h>
#include         <bios.h>
#include         <dos.h>
#include         <stdio.h>
#include         <stdlib.h>
#include         <string.h>

#include         "csimint.h"


int               start_kb_process(void(*task)(void),int stacksize)
   {
      int   proc_id;
      set_kb_process = 1;
      proc_id = start_process(task,stacksize);
      set_kb_process = 0;
      return(proc_id);
   }


int               start_process(void(*task)(void),int stacksize)
   {
      char                    *stack;
      struct   _simprocessT   *process;
      int                     *off_seg=(int *)&stack;
      unsigned   int           stackbytes;
      int                      oldinit;

      /*  Check to see if tack handler has been installed*/

         if(getvect(_INTRPT_)!=task_handler)
            /* if it has not been installed, attempt to */

            if(install_handler())
               /* if it doesn't install, report error and exit */
               {
                  printf("error installing task handler \n");
                  exit_processing(1);
               }

         if(stacksize > 0x0fdf)
            /* Max stack size in paragraphs (SEGMENT counts)*/
            {
               printf("stack size error: proc id : %d",totalprocs);
               exit_processing(2);
            }

         stackbytes = stacksize * 16;

      /* Setup Process header and stack */

         process =
               (struct _simprocessT *)
               calloc(sizeof(struct _simprocessT) + stackbytes,1);

         /* make sure there was enough memory */
         if(process==NULL)
            return(2);

         stack = (char *)(process+1);

         /* 'Normalize' the bottom of stack pointer, that is
            make sure the offset is less then 16, as would be
            the case if   the program where compiled in the huge
            memory model */

         if(off_seg[0]>16)
            for(;off_seg[0]>16;off_seg[1]++,off_seg[0]-=16);

         /* remember, the stack actually starts high and
            builds its way down, so put the pointer at the top.*/

         stack += stackbytes - 1;

         /* finish setup of stack and base pointers */

         process->reg_sp = FP_OFF(stack);
         process->reg_ss = FP_SEG(stack);

         process->reg_bp = FP_OFF(stack);

         process->proc_id = totalprocs++;
         process->init = 1;

      /* Setup start jump */
         process->reg_cs = FP_SEG(task);
         process->reg_ip = FP_OFF(task);

      /* link into current processes */
         if(processlist==NULL)
            {
               processlist =         process;
               processlist->next =   process;
               processlist->prev =   process;
            }
         else
            {
               process->prev =       processlist;
               process->next =       processlist->next;
               process->next->prev = process;
               processlist->next =   process;
               processlist = process;
            }
         if(set_kb_process)
            kbprocess = processlist;

         /* make sure we can get back to where we were... */
         glbl_ss = _SS;
         glbl_sp = _SP;
         glbl_bp = _BP;
         oldinit = _sim_init_val;

         /*   tell the task handler what routine the process uses */

         glbl_fc = task;

         /* tell the task handler that this proc is initing */

         _sim_init_val=1;

         /* set up the new stak and base pointers and ... */
         _SS = process->reg_ss;
         _SP = process->reg_sp;
         _BP = process->reg_bp;

         /* have the task_handle do the rest */
         geninterrupt(_INTRPT_);

         _SS = glbl_ss;
         _SP = glbl_sp;
         _BP = glbl_bp;
         glbl_fc = NULL;
         _sim_init_val = oldinit;
         processlist = processlist->prev;
         return(0);

   }


void               stop_process(int   process_id)
   {
      struct   _simprocessT   *process;
      int                      go;

      go = 1;
      process = processlist;

      while(go)
         {
            if(process_id == process->proc_id)
               {
                  process->status.kill_flag = 1;
                  process->start_time = _sim_system_time;
                  go = 0;
               }
            if(process->next == processlist)
               {
                  if(go)
                     printf("\nattempted to stop non-existant proc\n");
                  go = 0;
               }
            else
               {
                  process = process->next;
               }
         }
   }



void               sim_start(void)
   {
      _sim_init_val=0;
      _last_update_time = *systimer;
      geninterrupt(_INTRPT_);
   }


void               exit_processing(int condition)
   {
      struct   _simprocessT   *pholder;
      setvect(_INTRPT_,old_vector);
      printf("Exit processing, code : %d\n",condition);
      processlist->prev->next = NULL;
      do
         {
            pholder = processlist;
            processlist = processlist->next;
            free(pholder);
         }   while(processlist!=NULL);
      exit(condition);
   }


int               install_handler(void)
   {
      void               *new_vector;

      _sim_init_val=2;
      old_vector = getvect(_INTRPT_);
      setvect(_INTRPT_,task_handler);
      new_vector = getvect(_INTRPT_);
      if(new_vector==old_vector)
         return(1);
      return(0);
   }


void   interrupt   task_handler( unsigned bp,unsigned di,unsigned si,
                                 unsigned ds,unsigned es,unsigned dx,
                                 unsigned cx,unsigned bx,unsigned ax,
                                 unsigned ip,unsigned cs,unsigned flags)
   {
      int                    notfound;
      struct   _simprocessT *procpntr;

      if(_sim_init_val==2)
         /* if this ever happens, it means that the task handler
            was installed before it should have been, so de-install
            it and stop the program */
         {
            exit_processing(10);
         }
      if(_sim_init_val==0) /* sim_start has been run */
         {
            processlist->reg_bp      = _BP;
            processlist->reg_sp      = _SP;
            processlist->reg_ss      = _SS;

            if(processlist->status.kill_flag)
               {
                  if(processlist == kbprocess)
                     /* You can,t kill the kbproc */
                     processlist->status.kill_flag = 0;
                  else
                     {
                        if(processlist->next == processlist)
                           /* no more procs */
                           exit_processing(20);

                        /* break list */
                        processlist->prev->next = processlist->next;

                        /* move in front of break */
                        processlist = processlist->prev;

                        /* free dead proc */
                        free(processlist->next->prev);

                        /* fix break */
                        processlist->next->prev = processlist;
                     }
               }

            if((kbprocess!= NULL)&&(bioskey(1)))
               processlist = kbprocess;
            else
               {
                  if(_sim_time_ratio != 0.0)
                     {
                        for(notfound = 1;notfound;)
                           {
                              if((kbprocess != NULL)&&(bioskey(1)))
                                 {
                                    processlist = kbprocess;
                                    notfound = 0;
                                 }
                              else
                                 {
                                    processlist = processlist->next;
                                    if((*systimer - _last_update_time)>
                                       (1/_sim_time_ratio))
                                       {
                                          _sim_system_time +=
                                             (*systimer -
                                              _last_update_time) *
                                             _sim_time_ratio;
                                          _last_update_time = *systimer;
                                       }
                                    if((processlist->start_time <=
                                        _sim_system_time)&&
                                       (processlist->status.wait_flag==0))
                                       notfound = 0;
                                 }
                           }
                        _sim_system_time = processlist->start_time;
                     }
                  else
                     {
                        if(processlist->status.wait_flag)
                           {
                              for(procpntr=processlist->next;
                                  procpntr&&
                                  (procpntr->status.wait_flag)&&
                                  (procpntr->status.kill_flag);)
                                 {
                                    procpntr = procpntr->next;
                                    if(procpntr == processlist)
                                       procpntr = NULL;
                                 }
                              if(procpntr == NULL)
                                 {
                                    fprintf(stderr,"waitlock\n");
                                    exit_processing(1);
                                 }
                              _sim_system_time = procpntr->start_time;
                           }
                        else
                           _sim_system_time = processlist->start_time;
                        procpntr = processlist->next;
                        while(procpntr!= processlist)
                           {
                              if((!procpntr->status.wait_flag)&&
                                 (_sim_system_time > procpntr->start_time))
                                 _sim_system_time = procpntr->start_time;
                              procpntr = procpntr->next;
                           }
                        processlist = processlist->next;
                        while((processlist->status.wait_flag||
                               processlist->status.kill_flag)||
                              (processlist->start_time > _sim_system_time))
                           processlist = processlist->next;
                        _last_update_time = *systimer;
                     }
               }

            CurrentTime = _sim_system_time/18.2;
            disable();
            _SP     = processlist->reg_sp;
            _SS     = processlist->reg_ss;
            _BP     = processlist->reg_bp;
            enable();

            if(processlist->init==1)
               /* process is in its first step, so initialize it */
               {
                  ax       = processlist->reg_ax;
                  bx       = processlist->reg_bx;
                  cx       = processlist->reg_cx;
                  dx       = processlist->reg_dx;
                  es       = processlist->reg_es;
                  ds       = processlist->reg_ds;
                  si       = processlist->reg_si;
                  di       = processlist->reg_di;
                  bp       = processlist->reg_bp;
                  ip       = processlist->reg_ip;
                  cs       = processlist->reg_cs;
                  flags    = processlist->reg_flag;
                  processlist->init = 0;
               }
         }
      else  /* sim_start has not been run */
         {
            if(_sim_init_val==1)
               {
                  processlist->reg_ax      = ax;
                  processlist->reg_bx      = bx;
                  processlist->reg_cx      = cx;
                  processlist->reg_dx      = dx;
                  processlist->reg_es      = es;
                  processlist->reg_ds      = ds;
                  processlist->reg_si      = si;
                  processlist->reg_di      = di;
                  processlist->reg_ip      = FP_OFF(glbl_fc);
                  processlist->reg_cs      = FP_SEG(glbl_fc);
                  processlist->reg_flag    = flags;
                  processlist->reg_bp      = _BP;
                  processlist->reg_sp      = _SP;
                  processlist->reg_ss      = _SS;
                  processlist->init        = 1;
                  cs = cs;
                  ip = ip;
                  bp = bp;
               }
         }
   }


int               set_time_ratio      (float   ratio)
   {
      _sim_time_ratio = ratio;
      return(0);
   }


int               my_process_id(void)
   {
      return(processlist->proc_id);
   }


int               wait_until_time   (long unsigned   starttime)
   {
      if(starttime < (_sim_system_time/18.2))
         {
            printf("Waiting for a time that is past in %d",
                    processlist->proc_id);
            exit_processing(-1);
         }
      wait_for_time((float)(starttime*18.2 - _sim_system_time)/18.2);
      return(0);
   }


int               wait_for_time      (float   delaytime)
   {
      if(_sim_time_ratio == 0)
         {
            processlist->start_time =
               _sim_system_time + delaytime*18.2;
         }
      else
         processlist->start_time =
            _sim_system_time + (long)(18.2 * delaytime);

      geninterrupt(_INTRPT_);
      return(0);
   }


int               init_post            (char   *postname)
   {
      struct   _postT      *post;

      for(post = postlist;post != NULL;post = post->next)
         {
            if(strcmp((char *)&post->name,postname)==0)
               return(post->handle);
         }
      post = (struct _postT *)calloc(sizeof(struct _postT),1);
      if(post==NULL)
         return(-1);

      post->handle = totalposts++;
      post->value = NULL;
      strcpy((char *)&post->name,postname);

      post->waiting = calloc(1,sizeof(void *));
      post->waiting[0] = NULL;

      post->next = postlist;
      post->prev = NULL;

      postlist->prev = post;
      postlist = post;
      return(post->handle);
   }


int               set_post           (int    posthandle,void   *pointer)
   {
      struct   _postT       *post;
      struct   _simprocessT *procpntr;
      int                   loop;
      for(post = postlist;post != NULL;post = post->next)
         {
            if(post->handle==posthandle)
               {
                  if(post->value != NULL)
                     {
                        wait_for_time(0);
                        if(post->value != NULL)
                           return(post->handle);
                     }
                  if(post->waiting[0] != NULL)
                     {
                        post->waiting[0]->status.wait_flag = 0;
                        post->waiting[0]->start_time = _sim_system_time;
                        loop = 0;
                        procpntr = processlist->next;
                        while(procpntr != processlist)
                           {
                              if((procpntr->status.wait_flag)&&
                                 (procpntr->waitpost == post))
                                 {
                                    post->waiting[loop] = procpntr;
                                    loop++;
                                 }
                              procpntr = procpntr->next;
                           }
                        post->waiting[loop] = NULL;
                     }
                  post->value = pointer;
                  wait_for_time(0);
                  return(0);
               }
         }
      return(-posthandle);
   }


void              *get_post            (int    posthandle)
   {
      struct   _postT      *post;
      void                 *ret;

      for(post = postlist;post != NULL;post = post->next)
         {
            if(post->handle==posthandle)
               {
                  ret = post->value;

                  if(ret==NULL)
                     return(ret);

                  post->value = NULL;
                  return(ret);
               }
         }
      return(NULL);
   }


void              *wait_post         (int    posthandle)
   {
      struct   _postT      *post;
      void                 *ret;
      int                  loop;

      for(post = postlist;post != NULL;post = post->next)
         {
            if(post->handle==posthandle)
               {
                  ret = post->value;

                  if(ret==NULL)
                     {
                        for(loop=0;post->waiting[loop];)
                           loop++;
                        post->waiting =
                           realloc(post->waiting,(loop+2)*sizeof(void*));
                        post->waiting[loop] = processlist;
                        post->waiting[loop+1] = NULL;
                        while(ret == NULL)
                           {
                              processlist->status.wait_flag = 1;
                              processlist->waitpost = postlist;
                              wait_for_time(0);
                              ret = post->value;
                              post->value = NULL;
                           }
                        processlist->waitpost = NULL;
                        return(ret);
                     }

                  post->value = NULL;
                  return(ret);
               }
         }
      return(NULL);
   }
