/*
 *  MBIBM.C - 4/20/88 - System dependent functions.
 *  For standard IBM PC, under MS-DOS 3.1 or later
 *
 * Uses calls thru int 14h to drivers that replace the regular
 *  interrupt 14h drivers.
 * Uses brkoff and brkon to disable/enable ctrl-brk by VE3GYQ
 * - VE3GYQ and NK6K
 *
 */

#include "mb.h"
#include <time.h>
#include <dos.h>

/*
 *  INT14 function codes.
 */

#define SER_OUT   1     /* Character out serial port     */
#define SER_IN    2     /* Character in from serial port */
#define SER_STAT  3     /* Check status of serial port   */
#define SER_INQ   4     /* Verify driver loaded          */
#define SET_BUSY  8     /* Set port busy flag            */
#define CLR_BUSY  9     /* Clear port busy flag          */
#define PP_FLAG  10     /* Store port flag in bios       */
#define PC_FLAG  11     /* Store command flag in bios    */
#define GP_FLAG  12     /* Retreive port flag            */
#define GC_FLAG  13     /* Retreive command flag         */
#define GCMD     14     /* Get command from bios         */
#define PCMD     15     /* Store command in bios         */

/*
 *  INT21 Functions codes.
 */

#define SETDATE  0x2b
#define SETTIME  0x2d

#define SETDTA   0x1a
#define GETDTA   0x2f
#define GETFRE   0x36
#define SRCFST   0x4e
#define SRCNXT   0x4f

#define DV_PAUSE   0x1000 /* Relinquish control to other     */
#define DV_BEGINC  0x101B /* Begin DV critical region        */
#define DV_ENDC    0x101C /* End critical region             */

char l_date[7], l_time[5];
long time();
static union REGS inreg, outreg, ingive, outgive;
char pt_flag = 0;
char bios_call;

#define CMDPORT   15   /* Port for passing commands */

/*
 *  Initialize the serial I/O system.
 *  Ports are initialized by loading driver and then running mbmode.
 */

ioinit()
{
  brkoff();   /* disable DOS ctrl-brk fn */
}

dosinit()
{
 if (s_flag & s_dv)
   {
     ingive.x.ax = DV_PAUSE;
     bios_call = 0x15;
     printf ("Set for DESQview.\n");
   }
 }

iooff()
{
  brkon();    /* restore DOS ctrl-brk fn */
}

/*
 *  Switch out of the current window.
 */

switchw()
{
  if (s_flag & s_dv)
  int86(bios_call, &ingive, &outgive);
}
/*
 *  Set the current I/O device.
 */

ioport(pp)
PORTS *pp;
{
  port = pp;
}

breakport()
{
  inreg.x.dx = port->idn;
  inreg.h.ah = 7;
  int86( 0x14, &inreg, &outreg);
}

isdcd()
{
   switch (port->dev)
   {
   case p_tnc:
   case p_serial:
      inreg.x.dx = port->idn;
      inreg.h.ah = SER_STAT;
      int86( 0x14, &inreg, &outreg );
      return (0x80 & outreg.h.al);
   default: return true;
   }
 }

/*
 *  Suspend multitasking permitting only calling task to
 *  continue execution.
 */

begin_lock()
{
   inreg.x.ax = DV_BEGINC;
   int86(0x15, &inreg, &outreg);
}

/*
 *  Resume multitasking.
 */

end_lock()
{
   inreg.x.ax = DV_ENDC;
   int86(0x15, &inreg, &outreg);
}

/*
 *  Mark all the ports in this window as busy.
 */

setbusy()
{
  inreg.h.ah = SET_BUSY;
  inreg.x.dx = CMDPORT;
  inreg.h.al = p_window;
  int86(0x14, &inreg, &outreg);
}

/*
 *  Mark all the ports in this window as idle.
 */

clrbusy()
{
  inreg.h.ah = CLR_BUSY;
  inreg.x.dx = CMDPORT;
  inreg.h.al = p_window;
  int86(0x14, &inreg, &outreg);
}

/*
 *  Store the port flag in the bios. Port flag bits set
 *  indicate that the corresponding port is initialized
 *  in a window.
 */

putp_flag(c)
byte c;
{
  inreg.h.ah = PP_FLAG;
  inreg.x.dx = CMDPORT;
  inreg.h.al = c;
  int86(0x14, &inreg, &outreg);

}
/*
 *  Store the command flag in the bios. Command flag bits set
 *  indicate that the corresponding port has a command waiting
 *  for it to process. 
 */

putc_flag(c)
byte c;
{
  inreg.h.ah = PC_FLAG;
  inreg.x.dx = CMDPORT;
  inreg.h.bl = c;
  int86(0x14, &inreg, &outreg);
}

/*
 *  Get the port flag from the bios. Port flag bits set
 *  indicate that the corresponding port is initialized
 *  in a window.
 */

byte getp_flag()
{
  inreg.h.ah = GP_FLAG;
  inreg.x.dx = CMDPORT;
  int86(0x14, &inreg, &outreg);
  return outreg.h.al;

}
/*
 *  Get the command flag from the bios. Command flag bits set
 *  indicate that the corresponding port has a command waiting
 *  for it to process. 
 */

getc_flag()
{
  inreg.h.ah = GC_FLAG;
  inreg.x.dx = CMDPORT;
  int86(0x14, &inreg, &outreg);
  c_flag = outreg.h.bl;
  b_flag = outreg.h.bh;
}

/*
 *  Recover two characters from the bios storage area for use
 *  as commands. Port->line will contain these on return.
 */

getcomd()
{
  inreg.h.ah = GCMD;
  inreg.x.dx = CMDPORT;
  int86(0x14, &inreg, &outreg);
  port->opt1 = outreg.h.bl;
  port->opt2 = outreg.h.bh;
}

/*
 *  Pass a two character command to the bios storage area.
 */

putcomd(c1, c2)
char c1, c2;
{
  inreg.h.ah = PCMD;
  inreg.x.dx = CMDPORT;
  inreg.h.bl = c1;
  inreg.h.bh = c2;
  int86(0x14, &inreg, &outreg);
}

/*
 *  Return character from the current port.
 */

char inchar()
{
  register char ch;

  if (port->dev is p_console)
  { ch = bdos(7, 0, 0);if (ch is '\020') pt_flag = !pt_flag; }
  else
  {
    inreg.h.ah = SER_IN;
    inreg.x.dx = port->idn;
    int86(0x14, &inreg, &outreg);
    ch = (0x00ff & outreg.h.al);
  }

  if (ch is del) ch = '\b';
  if (port->dev is p_console) return ch;

/*
 *  Input did not come from the console.
 *  Echo the input to the console.
 */

  if (!(port->ec)) return ch;

  if (ch isnt ctl_g)
  {
     bdos(2, ch, 0);
     if (ch is '\r') bdos(2, '\n', 0);
  }
  return ch;
}

/*
 *  Return non-zero if a character waits at current input.
 */

instat()
{
  register int st;

  if (port->dev is p_console) st = kbstat();
  else
  {
    inreg.x.dx = port->idn;
    inreg.h.ah = SER_STAT;
    int86(0x14, &inreg, &outreg);
    st = 0x01 & outreg.h.ah;
  }

  if (!st) if (s_flag & s_dv)
  if (port->flags & p_give)
    int86(bios_call, &ingive, &outgive);

  return st;
}

outi(ch)
char ch;
{
  inreg.x.dx = port->idn;
  inreg.h.ah = SER_STAT;

/*
 *  Check if cts on and xmtr rgstr empty.
 *  Else maybe give some time to other DV windows.
 */

  int86(0x14, &inreg, &outreg);
  while((0x2010 & outreg.x.ax) isnt 0x2010)
  {
/*
    if (s_flag & s_dv)
    if (port->flags & p_give) int86(bios_call, &ingive, &outgive);
*/
    int86(0x14, &inreg, &outreg);
  }

/*
 *  Stuff the character out the port.
 */

  inreg.h.ah = SER_OUT;
  inreg.h.al = ch;
  int86(0x14, &inreg, &outreg);
}

/*
 *  Put the character out the current port,
 *  echo to console if the current port is not the console.
 *  Note special handling of end-of-line character.
 */

outchar(ch)
char ch;
{
  if (ch is '\r') return;
  if (ch is '\n') ch = '\r';

/*
 *  Put it out to the console, if it is going there.
 */

  if ((port->dev is p_console) or port->ec)
  {
    bdos(2, ch, 0);
    if (ch is '\r') bdos(2,'\n',0);
    if (pt_flag) { bdos(5,ch,0); if(ch is '\r') bdos(5,'\n',0); }
  }

/*
 *  If the port is the console, nothing more to do.
 */

  if (port->dev is p_console) return;

/*
 *  Stuff this byte out the port.
 */

  outi(ch);
  if (ch is '\r') if (port->flags & p_lf) outi('\n');
}

outstr(cp)
char *cp;
{
  while(*cp) outchar(*cp++);
}

/*
 *  Set the clock.
 *  fld[1] has the date string.
 *  fld[2] has the time string.
 */

setime()
{
  inreg.h.ah = SETTIME;
  inreg.h.ch = two(port->fld[2]);       /* Hour */
  inreg.h.cl = two(port->fld[2]+2);     /* Minute */
  inreg.x.dx = 0;                       /* Seconds and hundredths */
  intdos(&inreg, &outreg);

  inreg.h.ah = SETDATE;
  inreg.h.dl = two(port->fld[1]+4);      /* Day   */
  inreg.h.dh = two(port->fld[1]+2);      /* Month */
  inreg.x.cx = 1900 + two(port->fld[1]); /* Year  */
  intdos(&inreg, &outreg);
}

two(cp)
char *cp;
{
  return (10 * (*cp - '0') + (*(cp + 1) - '0'));
}

/*
 *  Fill the globals "l_date", "l_time", and "dt" with
 *  the current date and time by reading the system clock.
 *  l_time is HHMM, l_date is YYMMDD.
 *  dt has the format of a directory item date/time.
 */

curtim()
{
  struct tm *ct;
  long ltime;

  time(&ltime);
  ct = localtime(&ltime);
  sprintf(l_time, "%02d%02d", ct->tm_hour, ct->tm_min);
  sprintf(l_date, "%02d%02d%02d", ct->tm_year, ct->tm_mon + 1, ct->tm_mday);
/* month returned by localtime is 0-11 */
}

/*
 *  Check a timer.
 */

chktmr(l)
long l;
{
  long cur;

  time(&cur);
  if (cur > l) return false;
  return true;
}

/*
 *  Set a timer.
 */

settmr(l, i)
long *l;
int i;
{
  long cur;

  time(&cur);
  *l = cur + (long)i;
}

/*
 *  Wait a bit.
 */

wait(sec)
int sec;
{
  long l;

  settmr(&l, sec);
  while (chktmr(l)) if (s_flag & s_dv)
  int86(bios_call, &ingive, &outgive);
}

/*
 *  Directory access routines.
 */

#define d_dir  0x10

/*
 *  A directory item.
 */

typedef struct
{
  char  res[21];
  char  attr;
  int   time;
  int   date;
  word  sizel;
  word  sizeh;
  char  name[13];
} FCB;

static int olddta;
static FCB fcb;

/*
 *  Open directory, return first item.
 */

diropen(cp, p, dirdef)
char   *cp;
DIRDEF *dirdef;
DIRENT *p;
{
  long  csize;

/*
 *  Save DOS current buffer.
 */

  inreg.h.ah = GETDTA;
  intdos(&inreg, &outreg);
  olddta = outreg.x.bx;

/*
 *  Point DOS at local buffer.
 */

  inreg.h.ah = SETDTA;
  inreg.x.dx = (int)&fcb;
  intdos(&inreg, &outreg);

/*
 *  Get disk free space.
 */

  inreg.h.ah = GETFRE;
  if (*(cp + 1) is ':') inreg.h.dl = toupper(*cp) - 'A' + 1; else inreg.h.dl = 0;

  intdos(&inreg, &outreg);

/*
 *  Compute device size.
 *  ax = sectors / cluster
 *  bx = available clusters
 *  cx = bytes / sector
 *  dx = total cluster / drive
 */

  csize = (long)outreg.x.ax * (long)outreg.x.cx;
  dirdef->size = (int)((csize * (long)outreg.x.dx + 1023l) / 1024l);
  dirdef->free = (int)((csize * (long)outreg.x.bx + 1023l) / 1024l);

/*
 *  Get the first directory entry.
 */

  if (port->flds is 1) strcat(cp, "*.*");
  if (port->mode & (local | sysop)) inreg.x.cx = d_dir; else inreg.x.cx = 0;
  inreg.x.dx = (int)cp;

  inreg.h.ah = SRCFST;
  while (true)
  {
    if (intdos(&inreg, &outreg))
    {
      p->size    = -1;
      inreg.h.ah = SETDTA;
      inreg.x.dx = olddta;
      intdos(&inreg, &outreg);
      return false;
    }
    if (dodir(p)) return true;
    inreg.h.ah = SRCNXT;
  }
}

/*
 *  Return second and succeding directory entries.
 */

dirnext(p, dirdef)
DIRENT *p;
DIRDEF *dirdef;
{
  inreg.h.ah = SRCNXT;
  while(true)
  {
    if (intdos(&inreg, &outreg))
    {
      p->size    = -1;
      inreg.h.ah = SETDTA;
      inreg.x.dx = olddta;
      intdos(&inreg, &outreg);
      return;
    }
    if (dodir(p)) return;
  }
}

dodir(p)
DIRENT *p;
{
  strcpy(p->name, fcb.name);

  if (fcb.attr & d_dir)
  {
    if (match(p->name, "."))  return false;
    if (match(p->name, "..")) return false;
    strcat(p->name, "\\");
  }
  p->size = (int)((65536l * (long)fcb.sizeh + (long)fcb.sizel + 1023l) / 1024l);
  return true;
}

