#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include <string.h>
#include <math.h>
#include <bios.h>

#define INTR 0x70       // RTC interrupt vector
#define RTC_LATCH 0x70  // RTC latch address
#define RTC_DATA 0x71   // RTC data register

#define PIE 0x40        // RTC Periodic Interrupt Enable bit

#define LPT1 0x00400008 // BIOS printer address entries
#define LPT2 0x0040000a
#define LPT3 0x0040000c
#define LPT4 0x0040000e

#define LPT_ONE 0
#define LPT_TWO 1
#define LPT_THREE 2
#define LPT_FOUR 3
#define BADARG 4
#define BAD_COMMAND 5
#define MOTOR_NAME 6
#define STEP_VALUE 7
#define DIRECTION 8
#define STEPRATE 9
#define DELAY 10
#define INPUT1 11
#define INPUT2 12
#define ON_OFF 13

#define LEFT  0x4be0    // Left arrow
#define RIGHT 0x4de0    // Right arrow
#define UP    0x48e0    // Up arrow
#define DOWN  0x50e0    // Down arrow
#define ESC   0x011b    // Escape key


/********************** Function Prototypes ************************/

void get_arg(char ch);
void syntax(void);
void banner(void);
int parse_command(char *command);
void error(int err_number);
int get_direction(char *parameter);
void waitforx(void);
void waitfory(void);
void waitforxy(void);
void help(void);

/*******************************************************************/



/********************** Global variables here **********************/

unsigned int port=NULL,                 // Printer port base address
             xstep=0,                   // X Steps to be executed (0=stop)
             ystep=0,                   // Y Steps to be executed (0=stop
             xrate=5,                   // X motor delay between steps
             xrate_temp=5,
             yrate=5,                   // Y motor delay between steps
             yrate_temp=5,
             xindex=0,                  // Index into X motor drive table
             yindex=0;                  // Index into Y motor drive table

unsigned char xrun=0,                   // X motor run(=1)/step flag (=0)
              xdir=0,                   // X motor direction flag
                                        // 0=clockwise, 1=counter clockwise
              *xtab_ptr,                // Holds address of current drive
                                        // table.
              xbusy=0,                  // Set during X motor command

              yrun=0,                   // Y motor run(=1)/step flag (=0)
              ydir=0,                   // Y motor direction flag
                                        // 0=clockwise, 1=counter clockwise
              *ytab_ptr,                // Holds address of current drive
                                        // table.
              ybusy=0,                  // Set during Y motor command

// Drive tables

              wave[8]={0x88,0x44,0x22,0x11,0x88,0x44,0x22,0x11},

              two_phase[8]={0xcc,0x66,0x33,0x99,0xcc,0x66,0x33,0x99},

              h_step[8]={0x99,0x88,0xcc,0x44,0x66,0x22,0x33,0x11};


/*******************************************************************/



/********* Pointer to current Interrupt 70h handler ****************/

void interrupt (*oldhandler)(void);



/***************** New Interrupt 70h handler ***********************/


void interrupt newhandler(void)
{
  disable();

  outportb(RTC_LATCH,0x0c);             // Access RTC Register C
  inportb(RTC_DATA);                    // Clear down interrupt flags

  switch(xrun)   // Motor X
    {
      case 1: // Run mode

              if (--xrate_temp==0)
                {
                  xrate_temp=xrate;
                  if (xdir) xindex--; else xindex++;
                  xindex&=0x0007;
                  outportb(port,(inportb(port)&0xf0)|(*(xtab_ptr+xindex)&0x0f));
                }
              break;

      case 0: // Step mode

              if (xstep)
                {
                  if (--xrate_temp==0)
                    {
                      xrate_temp=xrate;
                      if (--xstep==0) xbusy=0;
                      if (xdir) xindex--; else xindex++;
                      xindex&=0x0007;
                      outportb(port,(inportb(port)&0xf0)|(*(xtab_ptr+xindex)&0x0f));
                    }
                }
              break;
    }

  switch(yrun)   // Motor Y
    {
      case 1: // Run mode

              if (--yrate_temp==0)
                {
                  yrate_temp=yrate;
                  if (ydir) yindex--; else yindex++;
                  yindex&=0x0007;
                  outportb(port,(inportb(port)&0x0f)|(*(ytab_ptr+yindex)&0xf0));
                }
              break;

      case 0: // Step mode

              if (ystep)
                {
                  if (--yrate_temp==0)
                    {
                      yrate_temp=yrate;
                      if (--ystep==0) ybusy=0;
                      if (ydir) yindex--; else yindex++;
                      yindex&=0x0007;
                      outportb(port,(inportb(port)&0x0f)|(*(ytab_ptr+yindex)&0xf0));
                    }
                }
              break;
    }

  enable();
  oldhandler();
}

/*******************************************************************/



int main(int argc, char *argv[])
{
  unsigned char rtc_rega,               // Temp store for RTC register A
                rtc_regb,               // Temp store for RTC register B
                pic_imr,                // Temp store for PIC IMR

                input_mask,             // Input mask byte

                quit=0;                 // Quit flag (1=quit)


  char buffer[80],                      // Holds current command
       *buffer1,                        // Copy of "buffer"
       *command,                        // Pointer to command name
       *parameter1,                     // Pointer to 1st command parameter
       *parameter2,                     // Pointer to 2nd command parameter
       *parameter3;                     // Pointer to 3rd command parameter

  unsigned int far *bios_printer_addr;

  unsigned int time_delay,              // Time delay value
               temp1,                   // Temp integer variables
               temp2;


  int echo_flag=1,                      // Command echo flag (1=echo command)
      manual=0;                         // Manual mode flag when set


  switch(argc)
    {
      case 1 : // No command line parameters

               get_arg('1');            // Default to LPT1
               break;


      case 2 : // One command line parameter

               if (argv[1][0]!='/')
                 {
                   syntax();
                   exit(1);
                 }

               if (strlen(argv[1])>2)
                 {
                   error(BADARG);
                   exit(1);
                 }

               bios_printer_addr=(unsigned int far *)LPT1;
               port=*(bios_printer_addr);

               if (port==NULL)
                 {
                   error(LPT_ONE);
                   exit(1);
                 }

               get_arg(argv[1][1]); // Process parameter
               break;

      case 3 : // Two command line parameters

               if (argv[1][0]!='/')
                 {
                   syntax();
                   exit(1);
                 }

               if (strlen(argv[1])>2)
                 {
                   error(BADARG);
                   exit(1);
                 }

               if (argv[2][0]!='/')
                 {
                   syntax();
                   exit(1);
                 }

               if (strlen(argv[2])>2)
                 {
                   error(BADARG);
                   exit(1);
                 }

               get_arg(argv[1][1]);   // Process first parameter
               get_arg(argv[2][1]);   // Process second parameter
               break;

      default: // Command line syntax error

               syntax();
               exit(1);
    }

  xtab_ptr=(unsigned char*)&two_phase;  // Set up default drive formats
  ytab_ptr=(unsigned char*)&two_phase;

  outportb(port,*two_phase);            // Set Power On Reset state
  delay(1);

  disable();

  oldhandler=getvect(INTR);             // Save existing interrupt handler
  setvect(INTR,newhandler);             // and set new one.

  pic_imr=inportb(0xa1);                // Save existing PIC interrupt mask
                                        // register
  outportb(0xa1,pic_imr&0xfe);          // Enable RTC interrupts

  outportb(RTC_LATCH,0x0a);             // Access RTC Register A
  rtc_rega=inportb(RTC_DATA);           // Save original contents

  outportb(RTC_LATCH,0x0a);                 // Access RTC Register A again
  outportb(RTC_DATA,(rtc_rega&0xf0)|0x06);  // Setup RTC for 976.5625uS
                                            // periodic interrupts

  outportb(RTC_LATCH,0x0b);             // Access RTC Register B
  rtc_regb=inportb(RTC_DATA);           // Save original contents

  outportb(RTC_LATCH,0x0b);             // Access RTC Register B again
  outportb(RTC_DATA,rtc_regb|PIE);      // Enable periodic interrupts

  outportb(RTC_LATCH,0x0c);             // Access RTC Register C
  inportb(RTC_DATA);                    // Clear down any flags that are set

  enable();

  strcpy(buffer1+6,"\n");               // Clear out "buffer1"

  banner();

  fputc('\n',stdout);


/********************** Main command loop **************************/

  while(!quit)
    {
      if (echo_flag) fputs("\nCommand: ",stdout);
      fgets(buffer,80,stdin);

      if (feof(stdin))
        {
          quit=1;
          continue;
        }

      if (strlen(buffer)==1) continue; // Check for carriage return only

      strtok(buffer,"\n");            // Remove terminating carriage return
      strcpy(buffer1,buffer);         // Copy "buffer" to "buffer1'

      strupr(buffer);                 // Convert command to upper case

      command=strtok(buffer," ");     // Create command string
      parameter1=strtok(NULL," ");    // Create 1st parameter string
      parameter2=strtok(NULL," ");    // Create 2nd parameter string
      parameter3=strtok(NULL," ");    // Create 3rd parameter string

      if (echo_flag)
        {
          fputs(buffer1,stdout);
          fputc('\n',stdout);
        }

      switch(parse_command(command))
        {
          case 0 : // BAD COMMAND

                   error(BAD_COMMAND);
                   break;

          case 1 : // END

                   waitforxy();

          case 2 : // QUIT

                   quit=1;
                   break;

          case 3 : // SPIN

                   if (strlen(parameter1)!=1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  waitforx();

                                  xstep=0;
                                  xbusy=0;
                                  xrun=1;
                                  break;

                       case '2' :
                       case 'Y' :
                                  waitfory();

                                  ystep=0;
                                  ybusy=0;
                                  yrun=1;
                                  break;

                       case 'B' :
                                  waitforxy();

                                  xstep=0;
                                  ystep=0;

                                  xbusy=0;
                                  ybusy=0;

                                  xrun=1;
                                  yrun=1;
                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 4 : // STOP

                   if (strlen(parameter1)>1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  xrun=0;
                                  xstep=0;
                                  xbusy=0;
                                  break;

                       case '2' :
                       case 'Y' :
                                  yrun=0;
                                  ystep=0;
                                  ybusy=0;
                                  break;

                       case 'B' :
                       case NULL:
                                  xrun=0;
                                  xstep=0;
                                  xbusy=0;
                                  yrun=0;
                                  ystep=0;
                                  ybusy=0;
                                  break;

                       default  : error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 5 : // STEP

                   if (strlen(parameter1)!=1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  if (parameter2==NULL)
                                    {
                                      error(STEP_VALUE);
                                      break;
                                    }

                                  waitforx();

                                  sscanf(parameter2,"%d",&temp1);

                                  if (temp1>50000)
                                    {
                                      error(STEP_VALUE);
                                      break;
                                    }

                                  xstep=temp1;
                                  xrun=0;
                                  break;

                       case '2' :
                       case 'Y' :
                                  if (parameter2==NULL)
                                    {
                                      error(STEP_VALUE);
                                      break;
                                    }

                                  waitfory();

                                  sscanf(parameter2,"%d",&temp1);

                                  if (temp1>50000)
                                    {
                                      error(STEP_VALUE);
                                      break;
                                    }

                                  ystep=temp1;
                                  yrun=0;
                                  break;

                       case 'B' :
                                  if (parameter2==NULL)
                                    {
                                      error(STEP_VALUE);
                                      break;
                                    }

                                  waitforxy();

                                  sscanf(parameter2,"%d",&temp1);

                                  if (parameter3==NULL) temp2=temp1;
                                  else sscanf(parameter3,"%d",&temp2);

                                  if (temp1>50000 || temp2>50000)
                                    {
                                      error(STEP_VALUE);
                                      break;
                                    }

                                  xstep=temp1;
                                  ystep=temp2;

                                  xrun=0;
                                  yrun=0;
                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 6 : // DIR

                   if (strlen(parameter1)!=1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  waitforx();

                                  switch(get_direction(parameter2))
                                    {
                                      case 0 : // CW

                                               xdir=0;
                                               break;

                                      case 1 : // CCW

                                               xdir=1;
                                               break;

                                      case 2 : // TOGGLE

                                               xdir^=1;
                                               break;

                                      default:
                                               error(DIRECTION);
                                               break;
                                    }

                                  break;

                       case '2' :
                       case 'Y' :
                                  waitfory();

                                  switch(get_direction(parameter2))
                                    {
                                      case 0 : // CW

                                               ydir=0;
                                               break;

                                      case 1 : // CCW

                                               ydir=1;
                                               break;

                                      case 2 : // TOGGLE

                                               ydir^=1;
                                               break;

                                      default:
                                               error(DIRECTION);
                                               break;
                                    }

                                  break;

                       case 'B' :
                                  waitforxy();

                                  switch(get_direction(parameter2))
                                    {
                                      case 0 : // CW

                                               xdir=0;
                                               ydir=0;
                                               break;

                                      case 1 : // CCW

                                               xdir=1;
                                               ydir=1;
                                               break;

                                      case 2 : // TOGGLE

                                               xdir^=1;
                                               ydir^=1;
                                               break;

                                      default:
                                               error(DIRECTION);
                                               break;
                                    }

                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 7 : // RATE

                   if (strlen(parameter1)!=1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  if (parameter2==NULL)
                                    {
                                      error(STEPRATE);
                                      break;
                                    }

                                  waitforx();

                                  sscanf(parameter2,"%d",&temp1);

                                  if (temp1<1 || temp1>50000)
                                    {
                                      error(STEPRATE);
                                      break;
                                    }

                                  xrate=xrate_temp=temp1;

                                  break;

                       case '2' :
                       case 'Y' :
                                  if (parameter2==NULL)
                                    {
                                      error(STEPRATE);
                                      break;
                                    }

                                  waitfory();

                                  sscanf(parameter2,"%d",&temp1);

                                  if (temp1<1 || temp1>50000)
                                    {
                                      error(STEPRATE);
                                      break;
                                    }

                                  yrate=yrate_temp=temp1;

                                  break;

                       case 'B' :
                                  if (parameter2==NULL)
                                    {
                                      error(STEPRATE);
                                      break;
                                    }

                                  waitforxy();

                                  sscanf(parameter2,"%d",&temp1);

                                  if (parameter3==NULL) temp2=temp1;
                                  else sscanf(parameter3,"%d",&temp2);

                                  if (temp1>50000 || temp2>50000)
                                    {
                                      error(STEPRATE);
                                      break;
                                    }

                                  xrate=xrate_temp=temp1;
                                  yrate=yrate_temp=temp2;

                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 8 : // VER

                   banner();
                   break;

          case 9 : // WAIT

                   if (strlen(parameter1)>1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  if (xstep) xbusy=1;
                                  break;

                       case '2' :
                       case 'Y' :
                                  if (ystep) ybusy=1;
                                  break;

                       case 'B' :
                                  if (xstep) xbusy=1;
                                  if (ystep) ybusy=1;
                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 10: // DELAY (milliseconds)

                   if (parameter1==NULL)  // Parameter1 is delay value
                     {
                       error(DELAY);
                       break;
                     }

                   sscanf(parameter1,"%d",&time_delay);

                   if (time_delay<1 || time_delay>60000) error(DELAY);
                   else delay(time_delay);

                   break;

          case 11: // ECHO

                   if (!strcmp(parameter1,"ON"))
                     {
                       echo_flag=1;
                       fputs(buffer1,stdout);
                       fputc('\n',stdout);
                       break;
                     }

                   if (!strcmp(parameter1,"OFF"))
                     {
                       echo_flag=0;
                       fputc('\n',stdout);
                       break;
                     }

                   error(ON_OFF);
                   break;

          case 12: // LOOPTIL

                   if (parameter1==NULL)  // Parameter1 is input name
                     {
                       error(INPUT1);
                       break;
                     }

                   if (!strcmp(parameter1,"INPUT1")) input_mask=0x08;
                   else if (!strcmp(parameter1,"INPUT2")) input_mask=0x40;
                        else if (!strcmp(parameter1,"INPUT3")) input_mask=0x20;
                             else if (!strcmp(parameter1,"INPUT4")) input_mask=0x10;
                                  else {
                                         error(INPUT1);
                                         break;
                                       }

                   if(!strcmp(parameter2,"LOW"))
                     {
                       while(1)
                         {
                           while(inportb(port+1)&input_mask);         // Wait for low input
                           delay(20);                                 // Debounce
                           if (!(inportb(port+1)&input_mask)) break;  // If still low, exit
                         }
                     }
                   else if(!strcmp(parameter2,"HIGH"))
                          {
                            while(1)
                              {
                                while(!inportb(port+1)&input_mask);    // Wait for high input
                                delay(20);                             // Debounce
                                if (inportb(port+1)&input_mask) break; // If still high, exit
                              }
                          }
                   else error(INPUT2);

                   break;

          case 13: // HSTEP

                   if (strlen(parameter1)!=1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  if (xbusy|xrun) break;
                                  xtab_ptr=(unsigned char*)&h_step;
                                  break;

                       case '2' :
                       case 'Y' :
                                  if (ybusy|yrun) break;
                                  ytab_ptr=(unsigned char*)&h_step;
                                  break;

                       case 'B' :
                                  if (xbusy|ybusy|xrun|yrun) break;
                                  xtab_ptr=(unsigned char*)&h_step;
                                  ytab_ptr=(unsigned char*)&h_step;
                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 14: // 2PHASE

                   if (strlen(parameter1)!=1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  if (xbusy|xrun) break;
                                  xtab_ptr=(unsigned char*)&two_phase;
                                  break;

                       case '2' :
                       case 'Y' :
                                  if (ybusy|yrun) break;
                                  ytab_ptr=(unsigned char*)&two_phase;
                                  break;

                       case 'B' :
                                  if (xbusy|ybusy|xrun|yrun) break;
                                  xtab_ptr=(unsigned char*)&two_phase;
                                  ytab_ptr=(unsigned char*)&two_phase;
                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 15: // WAVE

                   if (strlen(parameter1)!=1)
                     {
                       error(MOTOR_NAME);
                       break;
                     }

                   switch(*parameter1)
                     {
                       case '1' :
                       case 'X' :
                                  if (xbusy|xrun) break;
                                  xtab_ptr=(unsigned char*)&wave;
                                  break;

                       case '2' :
                       case 'Y' :
                                  if (ybusy|yrun) break;
                                  ytab_ptr=(unsigned char*)&wave;
                                  break;

                       case 'B' :
                                  if (xbusy|ybusy|xrun|yrun) break;
                                  xtab_ptr=(unsigned char*)&wave;
                                  ytab_ptr=(unsigned char*)&wave;
                                  break;

                       default  :
                                  error(MOTOR_NAME);
                                  break;
                     }
                   break;

          case 16: // PRINT

                   fputc('\n',stdout);
                   fputs(buffer1+6,stdout);
                   strcpy(buffer1+6,"\n");
                   fputc('\n',stdout);

                   break;

          case 17: // HELP

                   help();
                   break;

          case 18: // MANUAL

                   xrun=0;
                   xstep=0;
                   yrun=0;
                   ystep=0;
                   manual=1;

                   fputs("Manual Mode: Up/Down arrow steps X motor\n",stdout);
                   fputs("             Left/Right arrow steps Y motor\n",stdout);
                   fputs("             Escape to exit manual mode .....  ",stdout);


                   do
                     {
                       switch (bioskey(_NKEYBRD_READ))
                         {
                           case UP : /* Step X motor forward one step */

                                   xdir=0;
                                   xstep=1;
                                   break;

                           case DOWN : /* Step X motor back one step */

                                   xdir=1;
                                   xstep=1;
                                   break;

                           case RIGHT : /* Step Y motor forward one step */

                                   ydir=0;
                                   ystep=1;
                                   break;

                           case LEFT : /* Step Y motor back one step */

                                   ydir=1;
                                   ystep=1;
                                   break;

                           case ESC : /* Quit manual mode */

                                   manual=0;
                                   fputc('\n',stdout);
                                   break;
                         }
                     }
                   while (manual);
        }
    }

/*******************************************************************/


  disable();

  outportb(RTC_LATCH,0x0a);             // Access RTC Register A
  outportb(RTC_DATA,rtc_rega);          // Restore original contents

  outportb(RTC_LATCH,0x0b);             // Access RTC Register B
  outportb(RTC_DATA,rtc_regb);          // Restore original contents

  outportb(0xa1,pic_imr);               // Restore original PIC mask register
                                        // contents

  setvect(INTR,oldhandler);             // Restore original interrupt handler

  enable();

  outportb(port,0);                     // De-energize motors & exit to DOS

  return(0);
}


void banner(void)
{
  fputs("\nDual Stepper Motor Controller (Version 1.0)\n",stdout);
}


int parse_command(char *command)
{
  int x;

  char commands[][9]=
         {
           "END",       // 1
           "QUIT",      // 2
           "SPIN",      // 3
           "STOP",      // 4
           "STEP",      // 5
           "DIR",       // 6
           "RATE",      // 7
           "VER",       // 8
           "WAIT",      // 9
           "DELAY",     // 10
           "ECHO",      // 11
           "LOOPTIL",   // 12
           "HSTEP",     // 13
           "2PHASE",    // 14
           "WAVE",      // 15
           "PRINT",     // 16
           "HELP",      // 17
           "MANUAL"     // 18
         };

  for (x=0; x<18; x++)
    {
      if (!strcmp(command,commands[x])) return(x+1);
    }
  return(0);
}


void error(int err_number)
{
  char error_messages[][40]=
         {
           "LPT1 not detected\n",               // LPT_ONE
           "LPT2 not detected\n",               // LPT_TWO
           "LPT3 not detected\n",               // LPT_THREE
           "LPT4 not detected\n",               // LPT_FOUR
           "Bad command line parameter\n",      // BADARG
           "Bad command",                       // BAD_COMMAND
           "Bad or missing motor name",         // MOTOR_NAME
           "Bad or missing step value(s)",      // STEP_VALUE
           "Bad direction setting",             // DIRECTION
           "Bad or missing steprate value(s)",  // STEPRATE
           "Bad or missing delay value",        // DELAY
           "Bad or missing input name",         // INPUT1
           "Bad or missing input condition",    // INPUT2
           "Bad or missing ON/OFF value"        // ON_OFF
         };

  fputs("ERROR: ",stdout);
  fputs(error_messages[err_number],stdout);
  fputc('\n',stdout);
}

int get_direction(char *parameter2)
{
  if (!strcmp(parameter2,"CW")) return(0);
  if (!strcmp(parameter2,"CCW")) return(1);
  if (*parameter2==NULL) return(2);
  return(4);
}

void waitforx(void)
{
  while(xbusy)
    {
      if (kbhit())
        {
          getch();
          break;
        }
    }
}

void waitfory(void)
{
  while(ybusy)
    {
      if (kbhit())
        {
          getch();
          break;
        }
    }
}

void waitforxy(void)
{
  while(xbusy|ybusy)
    {
      if (kbhit())
        {
          getch();
          break;
        }
    }
}

void help(void)
{
  char *message=

      "\n"
      "HSTEP    motor...............Set motor for Half-Step drive sequence.\n"
      "2PHASE   motor...............Set motor for Two-Phase drive sequence.\n"
      "WAVE     motor...............Set motor for Wave drive sequence.\n"
      "RATE     motor val1 <val2>...Set delay rate between steps (in msecs).\n"
      "                             Range from 1 to 50000 msec.\n"
      "DIR      motor <CW,CCW>......If no direction given then reverse it.\n"
      "STEP     motor val1 <val2>...Step value range from 1 to 50000.\n"
      "WAIT     motor...............Wait for previous motor command to finish.\n"
      "SPIN     motor...............Continuously step motor.\n"
      "STOP    <motor>..............If no motor named then stop both.\n"
      "MANUAL   ....................Manually step motors.\n"
      "LOOPTIL  input(n) HIGH,LOW...goes High or Low. (where n = 1,2,3 or 4)\n"
      "DELAY    milliseconds........Range from 1 to 60000.\n"
      "ECHO     ON or OFF...........If ON (default), all commands echoed to output.\n"
      "PRINT    message.............to output.\n"
      "VER      ....................Print version number.\n"
      "END      ....................Wait for motors to finish then quit.\n"
      "QUIT     ....................Quit program immediately.\n\n"
      "where motor = 1 or X, 2 or Y, B(oth). Parameters in <> are optional.\n"
      "Commands and parameters can be in upper or lower case.\n";

  banner();
  fputs(message,stdout);
}

void syntax(void)
{
  char *message=

       "\nCommand syntax: DUALSTEP</port>\n\n"
       "where port = LPT port number (1,2,3 or 4)\n"
       "The port number is optional and defaults to LPT1 if not specified.\n\n"
       "For example: `DUALSTEP /2' uses LPT2.\n";

  banner();
  fputs(message,stdout);
}


void get_arg(char ch)
{

  unsigned int far *bios_printer_addr;

  switch(ch)
    {
      case '1' : // LPT1

                 bios_printer_addr=(unsigned int far *)LPT1;
                 port=*(bios_printer_addr);

                 if (port==NULL)
                   {
                     error(LPT_ONE);
                     exit(1);
                   }
                 break;

      case '2' : // LPT2

                 bios_printer_addr=(unsigned int far *)LPT2;
                 port=*(bios_printer_addr);

                 if (port==NULL)
                   {
                     error(LPT_TWO);
                     exit(1);
                   }
                 break;

      case '3' : // LPT3

                 bios_printer_addr=(unsigned int far *)LPT3;
                 port=*(bios_printer_addr);

                 if (port==NULL)
                   {
                     error(LPT_THREE);
                     exit(1);
                   }
                 break;

      case '4' : // LPT4

                 bios_printer_addr=(unsigned int far *)LPT4;
                 port=*(bios_printer_addr);

                 if (port==NULL)
                   {
                     error(LPT_FOUR);
                     exit(1);
                   }
                 break;

      case '?' : // Help with command syntax
      case 'h' :
      case 'H' :

                 syntax();
                 exit(0);

      default  : // Bad command line parameter

                 error(BADARG);
                 exit(1);
    }
}