/*****************************************************************************/
/* This program's purpose is to allow switching between Back & Forth tasks   */
/* from the command line. The source code is provided for other programs to  */
/* build direct hooks into B&F.                                              */
/*                                                                           */
/* This code is written in Turbo-C and is compatible with Microsoft-C and    */
/* Watcom-C.                                                                 */
/*****************************************************************************/

/*****************************************************************************/
/* Needed header files...                                                    */
/*****************************************************************************/
#include "ctype.h"
#include "dos.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

/*****************************************************************************/
/* Back & Forth Kernel calls are initiated through interrupt 11h by setting  */
/* AX & CX to -2 and placing the function call in BX.                        */
/*                                                                           */
/* Current function calls:                                                   */
/*                                                                           */
/* BX = 0 -- B&F kernel status. Returns 1 if B&F is in memory.               */
/* BX = 2 -- Returns the # of defined programs and an array of defined       */
/*           program IDS. The array to place the defined program ids in      */
/*           should be passed in ES:DI. The # of programs defined will be    */
/*           returned in AX.                                                 */
/* BX = 3 -- Switch tasks! Place the program id in DX. If the specified      */
/*           program id is undefined, AX will be 0 else non-zero!            */
/*****************************************************************************/

/*****************************************************************************/
/* Local function prototypes...                                              */
/*****************************************************************************/
void build_program_id_list(void);
void check_if_bf_installed(void);
void display_program_id_list(void);
void display_switch_help(void);
void switch_task(int program_id);

/*****************************************************************************/
/* Local variables...                                                        */
/*****************************************************************************/
int no_of_defined_programs;      /* Current # of defined programs.           */
int program_ids[50];             /* Back & Forth currently has a limit of 50 */
                                 /* program definitions.                     */

/*****************************************************************************/
/* Switch starts here.                                                       */
/*                                                                           */
/* Syntax is:  switch <cc> or switch list                                    */
/*                                                                           */
/*             where <cc> is a the two character id of the task to switch    */
/*             to. If the word "list" is on the command line, a list of      */
/*             program ids will be listed. To bring up the Back & Forth      */
/*             menu, run switch with the word "menu".                        */
/*****************************************************************************/
void cdecl main(int argc,char *argv[])
{
  /* Make sure B&F is in memory */
  check_if_bf_installed();

  /* Make sure 2 arguments exactly */
  if (argc != 2)
  {
    display_switch_help();
  }

  /* Switch to menu ?? */
  if (memicmp(argv[1],"menu",4) == 0)
  {
    switch_task(0);
  }

  /* Display program id list ?? */
  if (memicmp(argv[1],"list",4) == 0)
  {
    /* Build program id list */
    build_program_id_list();

    display_program_id_list();
  }

  /* Switch task ?? Check for two letter id */
  if (isalnum(*argv[1]) && isalnum(*(argv[1]+1)) && *(argv[1]+2) == '\0')
  {
    /* Convert characters to uppercase */
    strupr(argv[1]);

    /* Reverse bytes because of stupid 8086/8088 architecture! */

    switch_task(*(int *) argv[1]);
  }

  /* Inform user of invalid argument and display instructions */
  printf("Invalid option '%s'!",argv[1]);
  display_switch_help();
}

/*****************************************************************************/
/* build_program_id_list()                                                   */
/*                                                                           */
/* Gets the list of program ids currently defined in Back & Forth! The id    */
/* list is returned in supplied integer array of 50. The address of the      */
/* array should be passed in ES:DI.                                          */
/*****************************************************************************/
void build_program_id_list(void)
{
  union  REGS  regs;
  struct SREGS sregs;

  /* Call B&F kernel */
  regs.x.ax = -2;
  regs.x.cx = -2;
  regs.x.bx =  2;
  regs.x.di = (unsigned) program_ids;
  sregs.es  = FP_SEG(((void far *) program_ids));
  int86x(0x11,&regs,&regs,&sregs);

  /* # of programs defined is returned in AX */
  no_of_defined_programs = regs.x.ax;
}

/*****************************************************************************/
/* check_if_bf_installed()                                                   */
/*                                                                           */
/* Makes sure Back & Forth is in memory. If not, exits to DOS with error msg.*/
/*****************************************************************************/
void check_if_bf_installed(void)
{
  union REGS regs;

  /* B&F memory resident ?? */
  regs.x.ax = -2;
  regs.x.cx = -2;
  regs.x.bx =  0;
  int86(0x11,&regs,&regs);

  /* AX == 1 if B&F is resident */
  if (regs.x.ax != 1)
  {
    puts("Back & Forth is not loaded!");
    exit(0);
  }
}

/*****************************************************************************/
/* display_program_id_list()                                                 */
/*                                                                           */
/* Displays currently defined program ids!                                   */
/*****************************************************************************/
void display_program_id_list(void)
{
  char  high_letter;
  int   id_no;
  char *id_ptr;
  char  low_letter;

  /* Add a blank line */
  puts("");

  /* Loop through list */
  id_ptr = (char *) program_ids;
  for (id_no = 1; id_no <= no_of_defined_programs; ++id_no)
  {
    /* Id defined ?? */
    if (*id_ptr != 0)
	{
	  low_letter   = *id_ptr++;
	  high_letter  = *id_ptr++;
	  printf("[%c%c] ",low_letter,high_letter);
    }
    else
    {
      printf("[??] ");
      ++id_ptr;
      ++id_ptr;
    }

    /* Start a new line after every 10 definitions */
    if ((id_no % 10) == 0)
    {
      puts("");
    }
  }

  /* Return to DOS */
  exit(0);
}

/*****************************************************************************/
/* display_switch_help()                                                     */
/*                                                                           */
/* Displays help for switch program.                                         */
/*****************************************************************************/
void display_switch_help(void)
{
  /* Display cumbersome text */
  puts("Switch Version 1.20 Copyright 1990 by Progressive Solutions, Inc\n");
  puts("Syntax is:  switch <cc> or switch <list> or switch <menu>\n");
  puts("where <cc>   is a the two character id of the task to switch to.");
  puts("      <list> will display a list of program ids.");
  puts("      <menu> will activate the Back & Forth menu.");

  /* Return to DOS */
  exit(0);
}

/*****************************************************************************/
/* switch_task()                                                             */
/*                                                                           */
/* Instructs the B&F kernel to switch to the specified task represented by   */
/* the passed program id. The task need not be opened yet!                   */
/*****************************************************************************/
void switch_task(int program_id)
{
  union REGS regs;

  /* B&F memory resident ?? */
  regs.x.ax = -2;
  regs.x.cx = -2;
  regs.x.bx =  3;
  regs.x.dx = program_id;
  int86(0x11,&regs,&regs);

  /* AX == 0 task is undefined */
  if (regs.x.ax == 0)
  {
    printf("No program defined with program id '%c%c'.",program_id & 0xff,program_id >> 8);
  }    

  /* Return to DOS */
  exit(0);
}
