/* -*- c -*- ------------------------------------------------------------- *
 *   
 *   Copyright 2004 Murali Krishnan Ganapathy - All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
 *   Bostom MA 02111-1307, USA; either version 2 of the License, or
 *   (at your option) any later version; incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/* This program can be compiled for DOS with the OpenWatcom compiler
 * (http://www.openwatcom.org/):
 *
 * wcl -3 -osx -mt getargs.c
 */

#include "string.h"
#include "biosio.h"

/*
 * Functions convention:
 * 1. Using the __asm generates inefficient assembly as it pushes
 *    and pops all registers before and after the __asm is executed.
 * 2. Using #pragma generates inline assembly, which will lead to a
 *    large code size (which in ROMable applications we want to avoid)
 * 3. So we implement routine X in C and assembly.
 *    the C version, just calls the assembly version, and the assembly
 *    version is inlined, since only the C version is calling it, we
 *    have best of both worlds!!!
 *
 */
 
/* BIOS Assisted output routines */
void csprint(char *str) // Print a C str (NULL terminated)
{
    int o,l;

    o = (long)(str) & 0xffff; // Offset of location
    l = strlen(str);
    str[l] = '$'; // Replace \0 with $
    asm_sprint(str);
    str[l] = '\0'; // Change it back.
}

void asm_sprint(const char *str);
#pragma aux asm_sprint = \
   " mov ah, 9h" \
   " int 21h " \
   parm [dx] \
   modify [ah] ;

void asm_cprint(char chr,char attr,int times,char disppage);
#pragma aux asm_cprint = \
   "mov ah, 9h" \
   "int 10h" \
   parm [al] [bl] [cx] [bh] \
   modify [ah];

void cprint(char chr,char attr,int times,char disppage)
{
    asm_cprint(chr,attr,times,disppage);
}

void asm_setdisppage(char num);
#pragma aux asm_setdisppage = \
   "mov ah, 5h" \
   "int 10h" \
   parm [al] \
   modify [ah];

void setdisppage(char num) // Set the display page to specified number
{
    asm_setdisppage(num);
}


char asm_getdisppage();
//#pragma aux asm_getdisppage value [bh]; // Return value is in register bh
#pragma aux asm_getdisppage = \
   "mov ah, 0fh" \
   "int 10h" \
   modify [ah] \
   value [bh];

char getdisppage() // Get current display page 
{
    return asm_getdisppage();
}

void clearwindow(char top,char left,char bot,char right, char page,char fillchar, char fillattr)
{
    char x;
    for (x=top; x < bot+1; x++)
    {
        gotoxy(x,left,page);
        asm_cprint(fillchar,fillattr,right-left+1,page);
    }
}

void cls()
{
    gotoxy(0,0,getdisppage());
    asm_cprint(' ',0x07,getnumrows()*getnumcols(),getdisppage());
}

void asm_gotoxy(char row,char col, char page);
#pragma aux asm_gotoxy = \
   "mov ah, 2h" \
   "int 10h" \
   parm [dh] [dl] [bh] \
   modify [ah];
   
void gotoxy(char row,char col, char page)
{
    asm_gotoxy(row,col,page);
}

void asm_getpos(char *row, char *col, char page);
#pragma aux asm_getpos = \
   "mov ah, 3h" \
   "int 10h" \
   "mov byte ptr [si], dh" \
   "mov byte ptr [di], dl" \
   parm [si] [di] [bh] \
   modify [ah];
   
void getpos(char * row, char * col, char page)
{
    asm_getpos(row,col,page);
/*
    char a,b;
    __asm {
        .386;
        
        mov bh, page;
        mov ah, 03;
        int 10h;
        mov a,dh;
        mov b,dl;
    }
    *row = a;
    *col = b;
*/
}

void asm_putchar(char x, char page);
#pragma aux asm_putchar = \
   " mov bh, 1   " \
   " mov ah, 0eh " \
   " int 10h     " \
   parm [al] [bh] \
   modify [bh bp] ;

char asm_getchar();
#pragma aux asm_getchar = \
   " mov ah, 8h " \
   " int 21h    " \
   parm [] \
   modify [ax] \
   value [al];
   
char asm_inputc(char *scancode);
#pragma aux asm_inputc = \
   "       mov ah, 10h           " \
   "       int 16h               " \
   "       cmp si, 0             " \
   "       je  done              " \
   "       mov byte ptr [si], ah " \
   "done:                        " \
   parm [si] \
   modify [ax] \
   value [al] ;

char inputc(char * scancode)
{
    return asm_inputc(scancode);
/*
    char a,b;
    __asm{
        .386;
        mov ah, 10h;
        int 16h;
        mov a,al;
        mov b,ah;
    }
    if (scancode != NULL) *scancode = b;
    return a;
*/
}

void asm_cursorshape(char start, char end);
#pragma aux asm_cursorshape = \
   "mov ah, 01h " \
   "int 10h" \
   parm [ch] [cl] \
   modify [ah];
   
void cursoroff() // Doesn't seem to work? (DOS box in windows)
{
    asm_cursorshape(38,3); // Start and end scan lines, 32 = invisible
}

void cursoron() // Doesn't seem to work in a DOS box 
{
    asm_cursorshape(6,7);
}

void getstring(char *str, unsigned int size)
// Reads a line of input from stdin. Replace CR with NUL byte
{
  char c;
  char *p = str;
  char page = asm_getdisppage();

  while ( (c = asm_getchar()) != '\r' ) {
    switch (c) {
    case '\0':                  /* Extended char prefix */
      asm_getchar();            /* Drop */
      break;
    case '\b':
      if ( p > str ) {
        p--;
        csprint("\b \b");
      }
      break;
    case '\x15':                /* Ctrl-U: kill input */
      while ( p > str ) {
        p--;
        csprint("\b \b");
      }
      break;
    default:
      if ( c >= ' ' && (unsigned int)(p-str) < size-1 ) {
        *p++ = c;
        asm_putchar(c, page);
      }
      break;
    }
  }
  *p = '\0';
  csprint("\r\n");
}

char asm_getnumcols();
#pragma aux asm_getnumcols = \
   " push 0h " \
   " pop fs  " \
   " mov di, 044ah " \
   " mov al, byte ptr fs:[di] " \
   parm [] \
   modify [di] \
   value [al];

char getnumcols()
{
    return asm_getnumcols();
}

char asm_getnumrows();
#pragma aux asm_getnumrows = \
   " push 0h " \
   " pop fs  " \
   " mov di, 0484h " \
   " mov al, byte ptr fs:[di] " \
   " inc al " \
   parm [] \
   modify [di] \
   value [al];

char getnumrows()
{
    return asm_getnumrows();
}

void asm_setvideomode(char mode);
#pragma aux asm_setvideomode = \
   " mov ah, 0h " \
   " int 10h    " \
   parm [al] \
   modify [ax] ;

void setvideomode(char mode)
{
    asm_setvideomode(mode);
}



