// Hey, Emacs, this a -*-C++-*- file !

// Copyright distributed.net 1997-1998 - All Rights Reserved
// For use in distributed.net projects only.
// Any other distribution or use of this source violates copyright.
// 
// $Log: clidos.cpp,v $
// Revision 1.5  1999/01/09 06:17:12  cyp
// DOS changes. Hot off the keyboard! woweee...
//
// Revision 1.3  1998/07/17 05:34:18  cyruspatel
// Latest DOS stuff 
//
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "clidos.h" //just for keeping prototypes in sync

#define YIELDTYPE_DESQVIEW 0x01
#define YIELDTYPE_WINOS2   0x02

/* ----------------------------------------------------------------- */

static struct
{
  int yieldtype;
  int scrheight;
  int scrwidth;
  int conisatty;
  int scrpage;
  int scrmode;
} dosstatics = {-1,-1,-1,-1,-1,-1};

/* ----------------------------------------------------------------- */

int dosCliConIsScreen(void)
{
  if (dosstatics.conisatty == -1)
    {
    dosstatics.conisatty = 0;
    if (isatty(fileno(stdout)))
      {
      char rows, cols, mode, page;
      _asm push bx
      _asm mov  ah,0Fh                 /* get page/mode/maxcol */
      _asm int  10h                    /* ->ah=# of cols,al=mode,bh=page */
      _asm mov  cols, ah
      _asm mov  mode, al
      _asm mov  page, bh
      _asm mov  bl, ah
      _asm xor  bh, bh
      _asm mov  dx, es
      _asm mov  ax, 40h
      _asm mov  es, ax
      _asm mov  ax, es:[4Ch]          /* size of regen buffer in bytes */
      _asm mov  es, dx
      _asm xor  dx, dx
      _asm shr  ax, 1                 /* 2 bytes per cell */
      _asm div  bx
      _asm mov  rows,al               
      _asm pop  bx
      dosstatics.scrwidth = cols;
      dosstatics.scrheight = rows;
      dosstatics.scrpage = page;
      dosstatics.scrmode = mode;
      dosstatics.conisatty = 1;
      }
    }
  return dosstatics.conisatty;
}

/* ----------------------------------------------------------------- */

int dosCliConGetPos( int *colP, int *rowP )
{
  if (dosCliConIsScreen() != 0)
    {
    char row, col, page = (char)dosstatics.scrpage;
    _asm push bx
    _asm mov  bh,page
    _asm mov  ah,3               /* get cursor size and pos */
    _asm int  10h                /* <-bh=page, -> cx=curs, dh=row, dl=col */
    _asm pop  bx                     
    _asm mov  row,dh
    _asm mov  col,dl
    if (colP) *colP = (int)col;
    if (rowP) *rowP = (int)row;
    return 0;
    }
  return -1;
}  

/* ----------------------------------------------------------------- */

int dosCliConSetPos( int acol, int arow )
{
  if (dosCliConIsScreen() != 0)
    {
    char row = (char)arow, col=(char)acol, page = (char)dosstatics.scrpage;
    _asm push bx
    _asm mov  bh,page
    _asm mov  dh,row
    _asm mov  dl,col
    _asm mov  ah,02h   /* set cursor pos */
    _asm int  10h      /* <-dh=row, dl=col, bh=page# */
    _asm pop  bx
    return 0;
    }
  return -1;
}  

/* ----------------------------------------------------------------- */

int dosCliConGetSize( int *cols, int *rows )
{
  if (dosCliConIsScreen() != 0)
    {
    if (rows) *rows = dosstatics.scrheight;
    if (cols) *cols = dosstatics.scrwidth;
    return 0;
    }
  return -1;
}
    
/* ----------------------------------------------------------------- */

int dosCliConClear(void)
{
  int rows, cols;
  if (dosCliConGetSize(&rows,&cols) == 0)
    {
    char buffer[256];
    size_t todo, done, totaldone=0, scrsize = (rows*cols);
    for (done=0;done<sizeof(buffer);done++)
      buffer[done]=' ';
    dosCliConSetPos(0,0);
    do{
      todo = sizeof(buffer);
      if (todo > (scrsize-totaldone))
        todo = (scrsize-totaldone);
      if ((done = fwrite(buffer,sizeof(char),todo,stdout)) == 0)
        break;
      totaldone+=done;
      } while (totaldone<scrsize);
    fflush(stdout);
    dosCliConSetPos(0,0);
    return 0;
    }
  return -1;
}

/* ----------------------------------------------------------------- */

int dosCliGetTimeOfDay(unsigned long *secs, unsigned long *usecs )
{
  struct timeb tb;
  ftime(&tb);
  if (secs) 
    *secs = (unsigned long)tb.time;
  if (usecs) 
    *usecs = ((unsigned long)(tb.millitm))*1000;
  return 0;
}  

/* ----------------------------------------------------------------- */

void dosCliYield(void)
{
  if (dosstatics.yieldtype == -1)
    {
    dosstatics.yieldtype = 0;
    dosCliGetEmulationDescription();
    }
#if 0
  if ((dosstatics.yieldtype & YIELDTYPE_DESQVIEW) != 0)
    {
    _asm mov ax,0x1000;                            /* DesqView idle */
    _asm int 0x15
    }
  else if ((dosstatics.yieldtype & YIELDTYPE_WINOS2) != 0)
    {    
    _asm mov ax,0x1680
    _asm int 0x2F
    }
#endif
  return;
}      

/* ----------------------------------------------------------------- */

static void __dosCliTimedYield(unsigned long secs, unsigned long usecs)
{
  int init = 0;
  unsigned long nowsecs, nowusecs;
  do{
    dosCliYield();
    if (secs == 0 && usecs == 0)
      break;
    dosCliGetTimeOfDay( &nowsecs, &nowusecs );
    if (init == 0)
      {
      init = 1;
      secs+=nowsecs;
      if ((usecs+=nowusecs)>=1000000)
        {
        secs+=(usecs/1000000);
        usecs%=1000000;
        }
      }
    } while (nowsecs < secs || ((secs == nowsecs) && (nowusecs < usecs)));
  return;
}

void dosCliUSleep(unsigned int usecs) { __dosCliTimedYield(0,usecs); }
void dosCliSleep( unsigned int secs ) { __dosCliTimedYield(secs, 0); }

/* ----------------------------------------------------------------- */

const char *dosCliGetEmulationDescription(void)
{
  static char emudesc[32];
  #pragma pack(1);
  union { struct { unsigned char lsb,msb; } c; unsigned short s; } 
      version, wversion;
  #pragma pack();
  unsigned short sversion;
  char *envp;
  const char *plat;

  emudesc[0]=0;
  dosstatics.yieldtype = 0;

  _asm  mov ax, 3306h
  _asm  mov bx, 0ffffh
  _asm  int 21h
  _asm  mov sversion, bx
  version.s = sversion;

  if (((((envp = getenv("OS"))!=NULL) && (stricmp( envp, "Windows_NT" )==0)) ||
    (version.c.lsb == 5 && version.c.msb==50)))
    {
    plat = "Windows NT";
    sprintf( emudesc, "in a %s/NTAS VDM", plat );
    dosstatics.yieldtype = YIELDTYPE_WINOS2;
    }
  else if (version.c.msb <=100 && (version.c.lsb == 10 || version.c.lsb==20))
    {
    plat = "OS/2";
    sprintf( emudesc, "in an %s %d.x DOS VM", plat,
            ((version.c.lsb==20)?((version.c.msb==30)?(3):(2)):(1)) );
    dosstatics.yieldtype = YIELDTYPE_WINOS2;
    }
  else
    {
    _asm  mov ax, 1600h
    _asm  int 2fh
    _asm  mov sversion, ax
    wversion.s = sversion;
    
    if (((wversion.c.lsb)!=0) && ((wversion.c.lsb & 0x7F)!=0x0))
      {
      plat = ((wversion.c.lsb >= 4)?("Win32"):("Windows")); 
      sprintf( emudesc, "in a %s v%d.%d DOS Box", plat,
           wversion.c.lsb, wversion.c.msb );
      dosstatics.yieldtype = YIELDTYPE_WINOS2;
      }
    else 
      {
      _asm mov ax,2B01h  /* DESQview installation check */
      _asm mov cx,4445h  /* 'DE' */
      _asm mov dx,5351h  /* 'SQ' */
      _asm mov bx,0      /* clear version # */
      _asm int 21h
      _asm cmp al,0FFh   /* set carry flag if supported (<0xFF) */
      _asm sbb ax,ax     /* make zero if not supported, 0xFFFF otherwise */
      _asm and bx,ax     /* clear version if not supported */
      _asm mov sversion,bx

      if (sversion != 0)
        {
        plat = "DesqView";
        sprintf( emudesc, "in a %s v%d.%d VM", plat,
           wversion.c.lsb, wversion.c.msb );
        dosstatics.yieldtype = YIELDTYPE_DESQVIEW;
        }
      else
        {
        _asm mov ax,0x1680
        _asm int 0x2F
        _asm cmp al,1
        _asm sbb ax,ax
        _asm mov sversion,ax
        
        if (sversion != 0) /* something that supports winos2 type yield */
          {
          dosstatics.yieldtype = YIELDTYPE_WINOS2;
          }
        }
      }
    }
  return emudesc;
}      
