/*
 *  Copyright (c) 1992, 1993 John E. Davis  (davis@amy.tch.harvard.edu)
 *  All Rights Reserved.
 */
#include <stdio.h>
#include <string.h>

#ifdef msdos
#include <conio.h>
#include <bios.h>
#include <mem.h>
#endif

#ifdef __GO32__
#include <pc.h>
#endif

#include <dos.h>

#include "config.h"
#include "sysdep.h"
#include "display.h"

int Scroll_r1 = 0, Scroll_r2 = 25, Cursor_Row = 1, Cursor_Col = 1;
int One60;

unsigned char *Video_Base;
int Attribute_Byte = 0;
int Msdos_Insert_Mode = 0;
static int Rev_Vid_Flag;

#ifdef msdos
int Cheap_Video = 0;
int Video_Status_Port;

#define SNOW_CHECK\
   if (Cheap_Video)\
     { while ((inp(0x3DA) & 0x08)); while (!(inp(0x3DA) & 0x08)); }

struct SREGS SegRegs;
#endif


void send_string_to_term(char *s)
{
   fputs(s, stdout);
}


void set_scroll_region(int r1, int r2)
{
    Scroll_r1 = r1 - 1;
    Scroll_r2 = r2 - 1;
}

void reset_scroll_region(void)
{
    Scroll_r1 = 0;
    Scroll_r2 = Screen_Height;
}

void goto_rc(int r, int c)
{
#ifdef __GO32__
   if (r > Screen_Height) r = Screen_Height; 
   if (c > Screen_Width) c = Screen_Width; 
   ScreenSetCursor(Scroll_r1 + r - 1, c - 1);
   Cursor_Row = r;
   Cursor_Col = c;
#else
   /* if (r > Screen_Height) r = Screen_Height; */
   asm mov ax, r
   asm mov bx, Screen_Height
   asm cmp ax, bx
   asm jle L1
   asm mov ax, bx
   L1:
   /* if (c > Screen_Width) c = Screen_Width; */
   asm mov cx, Screen_Width
   asm mov bx, c
   asm cmp bx, cx
   asm jle L2
   asm mov bx, cx
   L2:
   asm mov Cursor_Row, ax
   asm mov Cursor_Col, bx
   
   asm dec ax
   asm dec bx
   asm add ax, Scroll_r1
   asm xor dx, dx
   asm mov dh, al
   asm mov dl, bl
   asm xor bx, bx
   asm mov ax, 0x200
   asm int 0x10
#endif
}

void wherexy(void)
{
#ifdef __GO32__
   ScreenGetCursor(&Cursor_Row, &Cursor_Col);
   Cursor_Row++; Cursor_Col++;
#else
   asm mov ah, 3
   asm mov bh, 0
   asm int 10h
   asm xor ax, ax
   asm mov al, dh
   asm inc ax
   asm mov Cursor_Row, ax
   asm xor ax, ax
   asm mov al, dl
   asm inc ax
   asm mov Cursor_Col, ax
#endif
}

void begin_insert(void)
{
#ifdef __GO32__
   unsigned short *pmin;
#endif
   unsigned short *p; 
   int n;
   
   Msdos_Insert_Mode = 1;
   wherexy();
   
   /* move the stuff over 1 column */
   n = Screen_Width - Cursor_Col;
   
   p = (unsigned short *) (Video_Base + ((One60 * (Cursor_Row - 1) 
					  + 2 * (Screen_Width - 1))));
#ifdef __GO32__
    pmin = (unsigned short *) (Video_Base +
				(One60 * (Cursor_Row - 1) + 
				 2 * (Cursor_Col - 1)));
   
   while(p-- > pmin) *(p + 1)= *p; 
#else
   SNOW_CHECK;
   asm mov ax, ds
   asm mov bx, di
   asm mov dx, si
   
   asm mov cx, n
   asm les di, p
   asm lds si, p
   asm sub si, 2
   asm std
   asm rep movsw

   asm mov ds, ax
   asm mov di, bx
   asm mov si, dx
#endif
}

void end_insert(void)
{
   Msdos_Insert_Mode = 0;
}

void tt_delete_char()
{
#ifdef __GO32__
   register unsigned short *p1;
#endif
   unsigned short *p;
   int n;
   
   wherexy();
   /* move the stuff over 1 column */
   p = (unsigned short *) (Video_Base +  
			   (One60 * (Cursor_Row - 1) + 2 * (Cursor_Col - 1)));
   n = Screen_Width - Cursor_Col;
   
#ifdef __GO32__
    while(n--)
      {
	 p1 = p + 1;
	 *p = *p1;
	  p++;
      }
#else
   
   SNOW_CHECK;
   asm mov ax, ds
   asm mov bx, si
   asm mov dx, di
   
   asm mov cx, n
   asm les di, p
   asm lds si, p
   asm add si, 2
   asm cld
   asm rep movsw
   
   asm mov ds, ax
   asm mov si, bx
   asm mov di, dx
#endif
}

void tt_erase_line(void)
{
   unsigned short *p = (unsigned short *) (Video_Base + One60 * (Cursor_Row - 1));
   unsigned short w;
#ifdef __GO32__
   register unsigned short *pmax = p + Screen_Width;
   
   tt_normal_video();
   w = (Attribute_Byte << 8) | 32;
   while (p < pmax) *p++ = w;
#else
   tt_normal_video();
   w = (Attribute_Byte << 8) | 32;
   SNOW_CHECK;
   asm mov dx, di
   
   asm mov ax, w
   asm mov cx, Screen_Width
   asm les di, p
   asm cld
   asm rep stosw
   
   asm mov di, dx
#endif
}

void tt_delete_nlines(int n)
{
#ifdef __GO32__
   union REGS in;
   tt_normal_video();
   in.x.ax = n;
   in.x.cx = 0;
   in.h.ah = 6;
   in.h.ch = Scroll_r1;
   in.h.dl = Screen_Width - 1;
   in.h.dh = Scroll_r2;
   in.h.bh = Attribute_Byte;
   int86(0x10, &in, &in);
#else
   tt_normal_video();
   /* This has the effect of pulling all lines below it up */
   asm mov ax, n
   asm mov ah, 6		       /* int 6h */
   asm xor cx, cx
   asm mov ch, byte ptr Scroll_r1
   asm mov dx, Screen_Width
   asm dec dx
   asm mov dh, byte ptr Scroll_r2
   asm mov bh, byte ptr Attribute_Byte
   asm int 10h
#endif
}


void reverse_index(int n)
{
#ifdef __GO32__
   union REGS in;
   tt_normal_video();
   in.h.al = n;
   in.x.cx = 0;
   in.h.ah = 7;
   in.h.ch = Scroll_r1;
   in.h.dl = Screen_Width - 1;
   in.h.dh = Scroll_r2;
   in.h.bh = Attribute_Byte;
   int86(0x10, &in, &in);
#else
   tt_normal_video();
   asm xor cx, cx
   asm mov ch, byte ptr Scroll_r1
   asm mov dx, Screen_Width
   asm dec dx
   asm mov dh, byte ptr Scroll_r2
   asm mov bh, byte ptr Attribute_Byte
   asm mov ah, 7
   asm mov al, byte ptr n
   asm int 10h
#endif
}

int beep(void)
{
    /* clear the keyboard buffer since this is an error */
   if (!Ignore_Beep)
     {
	sound(1500);
	delay(100);
	nosound();
     }
   
   flush_input();
   return(0);
}

void tt_del_eol(void)
{
   unsigned short *p = (unsigned short *) (Video_Base +  (One60 * (Cursor_Row - 1) + 2 * (Cursor_Col - 1)));
   int n = Screen_Width - Cursor_Col;
   unsigned short w;
#ifdef __GO32__
   unsigned short *pmax = p + (n + 1);
   
   tt_normal_video();
   w = (Attribute_Byte << 8) + (unsigned short) ' ';
   while (p < pmax) *p++ = w;
#else
   
   tt_normal_video();
   w = (Attribute_Byte << 8) + (unsigned short) ' ';
   SNOW_CHECK;
   asm mov dx, di
   
   asm les di, p
   asm mov ax, w
   asm mov cx, n
   asm inc cx
   asm cld
   asm rep stosw
   
   asm mov di, dx
#endif
}

void tt_reverse_video(void)
{
   unsigned char fg, bg;

   if (Rev_Vid_Flag) return;
   fg = (Attribute_Byte & 0x07) << 4;
   bg = (Attribute_Byte & 0x70) >> 4;
   Attribute_Byte = fg | bg;
   Rev_Vid_Flag = 1;
}

void tt_normal_video(void)
{
   if (Rev_Vid_Flag == 0) return;
   Rev_Vid_Flag = 0;
   tt_reverse_video();
   Rev_Vid_Flag = 0;
}

#define CGA_STATUS 0x3DA
#define CGA_SETMODE 0x3D8

static unsigned short *video_write(register unsigned char *pp, 
				   register unsigned char *p,
				   register unsigned short *pos)
{
#ifdef __GO32__
   while (pp < p)
     {
	*pos++ = (Attribute_Byte << 8) + (unsigned short) (*pp++);
     }
   return pos;
#else
   int n = (int) (p - pp);
   
   asm push si
   asm push ds
   asm push di
   
   /* set up register for BOTH fast and slow */
   asm mov bx, Cheap_Video
   
   /* These are the registers needed for both fast AND slow */
   asm mov ah, byte ptr Attribute_Byte
   asm mov cx, n
   asm lds si, dword ptr pp
   asm les di, dword ptr pos
   asm cld
   
   asm cmp bx, 0		       /* cheap video test */
   asm je L_fast
   asm mov bx, ax
   asm mov dx, CGA_STATUS
   asm jg L_slow_blank
   
   /* slow video */
   asm cli
   /* wait for retrace */
   L_slow:
   asm in al, dx
   asm test al, 1
   asm jnz L_slow
   
   L_slow1:
   asm in al, dx
   asm test al, 1
   asm jz L_slow1
   
   /* move a character out */
   asm mov ah, bh
   asm lodsb
   asm stosw
   asm loop L_slow
   
   asm sti
   asm jmp done
   
/* -------------- slow video, vertical retace and pump --------------*/
   L_slow_blank:
   L_slow_blank_loop:
   asm in al, dx
   asm test al, 8
   asm jnz L_slow_blank_loop
   
   L_slow_blank1:
   asm in al, dx
   asm test al, 8
   asm jz L_slow_blank1
   
   /* write line */
   asm mov ah, bh
   L_slow_blank2:
   asm lodsb
   asm stosw
   asm loop L_slow_blank2
   
   asm jmp done
/*-------------- Fast video --------------*/   
   
   L_fast:
   asm lodsb
   asm stosw
   asm loop L_fast

   done:
   
   asm pop di
   asm pop ds
   asm pop si
   return (pos + n);
#endif
}

static void write_attributes(unsigned short *s, int row)
{
   unsigned char out[250], ch, attr;
   register unsigned char *p;
   register unsigned short sh, *pos;
   unsigned short *smax;
   
   Cursor_Row = row;
   Cursor_Col = 1;
   pos = (unsigned short *) (Video_Base +  (One60 * (Cursor_Row - 1)));
   smax = s + Screen_Width;
   
   
   p = out;
   while (s < smax)
     {
	sh = *s++;
	ch = sh & 0xFF;
	attr = sh >> 8;
	if ((attr == 0) && (Rev_Vid_Flag != 0))
	  {
	     if (p != out)
	       {
		  pos = video_write(out, p, pos);
		  p = out;
	       }
	     tt_normal_video();
	  }
	else if ((attr != 0) && (Rev_Vid_Flag == 0))
	  {
	     if (p != out)
	       {
		  pos = video_write(out, p, pos);
		  p = out;
	       }
	     tt_reverse_video();
	     /* Rev_Vid_Flag = 1; */
	  }
	*p++ = ch;
     }
   pos = video_write(out, p, pos);
}


   

void smart_puts(unsigned short *new_string, unsigned short *dummy, int row, int dum1)
{
#ifdef __GO32__
   write_attributes(new_string, row);
#else
   (void) dummy;  (void) dum1;
   Cursor_Row = row;
   Cursor_Col = 1;
   write_attributes(new_string, row);

#if 0
   p = (unsigned short *) (Video_Base +  (One60 * (Cursor_Row - 1)));
   
   /* video_off();*/
   
   /* SNOW_CHECK; */
   asm push si
   asm push ds
   asm push di
   
   /* set up register for BOTH fast and slow */
   asm mov bx, Cheap_Video
   
   /* These are the registers needed for both fast AND slow */
   asm mov ah, byte ptr Attribute_Byte
   asm mov cx, Screen_Width
   asm lds si, dword ptr new_string
   asm les di, dword ptr p
   asm cld
   
   asm cmp bx, 0		       /* cheap video test */
   asm je L_fast
   asm mov bx, ax
   asm mov dx, CGA_STATUS
   asm jg L_slow_blank
   
   /* slow video */
   asm cli
   /* wait for retrace */
   L_slow:
   asm in al, dx
   asm test al, 1
   asm jnz L_slow
   
   L_slow1:
   asm in al, dx
   asm test al, 1
   asm jz L_slow1
   
   /* move a character out */
   asm mov ah, bh
   asm lodsb
   asm stosw
   asm loop L_slow
   
   asm sti
   asm jmp done
   
/* -------------- slow video, vertical retace and pump --------------*/
   L_slow_blank:
   L_slow_blank_loop:
   asm in al, dx
   asm test al, 8
   asm jnz L_slow_blank_loop
   
   L_slow_blank1:
   asm in al, dx
   asm test al, 8
   asm jz L_slow_blank1
   
   /* write line */
   asm mov ah, bh
   L_slow_blank2:
   asm lodsb
   asm stosw
   asm loop L_slow_blank2
   
   asm jmp done
/*-------------- Fast video --------------*/   
   
   L_fast:
   asm lodsb
   asm stosw
   asm loop L_fast

   done:
   
   asm pop di
   asm pop ds
   asm pop si
#endif
/* not __GO32__ */
#endif
}

void reset_video(void)
{
}

void init_video(void)
{
#ifndef __GO32__
   unsigned char *p;
#endif
   Cursor_Row = Cursor_Col = 1;
   /* One60 = Screen_Width < 80 ?  80: Screen_Width; */
   One60 = Screen_Width;
   One60 *= 2;
#ifndef __GO32__
   p = (unsigned char far *) 0x00400049L;

   Video_Status_Port = 0x3DA;
   
   if (*p == 7)
     {
	Video_Status_Port = 0x3BA;
	Video_Base = (unsigned char *) MK_FP(0xb000,0000);
     }
   else Video_Base = (unsigned char *) MK_FP(0xb800,0000);

   /* test for video adapter type.  Of primary interest is whether there is
      snow or not.  Assume snow if the card is color and not EGA or greater.
   */

   /* Use Ralf Brown test for EGA or greater */
   asm mov ah, 0x12
   asm mov bl, 0x10
   asm mov bh, 0xFF
   asm int 10h
   asm cmp bh, 0xFF
   asm je L1
   
   /* (V)EGA */
   asm xor bx, bx
   asm mov Cheap_Video, bx
   asm mov ax, Attribute_Byte
   asm cmp ax, bx
   asm jne L2
   asm mov ax, 0x17
   asm mov Attribute_Byte, ax
   asm jmp L2
   
   L1:
   /* Not (V)EGA */
   asm mov ah, 0x0F
   asm int 10h
   asm cmp al, 7
   asm je L3
   asm mov ax, 1
   asm mov Cheap_Video, ax
   L3:
   asm mov ax, Attribute_Byte
   asm cmp ax, 0
   asm jne L2
   asm mov ax, 0x07
   asm mov Attribute_Byte, ax
   
   L2:
#else
   Video_Base = (unsigned char *) ScreenPrimary;
   if (Attribute_Byte == 0) Attribute_Byte = 0x17;
#endif
}

#if 0
void wide_width(void)
{
}

void narrow_width(void)
{
}
#endif

void cls(void)
{
   tt_normal_video();
#ifdef __GO32__
   set_scroll_region(1, Screen_Height);
   tt_delete_nlines(Screen_Height);
   
#else
   asm mov dx, Screen_Width
   asm dec dx
   asm mov ax, Screen_Height
   asm dec ax
   asm mov dh, al
   asm xor cx, cx
   asm xor ax, ax
   asm mov ah, 7
   asm mov bh, byte ptr Attribute_Byte
   asm int 10h
#endif
}

/* This function is called assuming that cursor is in correct 
   position */
void tt_putchar(char ch)
{
   unsigned short p, *pp;
   if (Rev_Vid_Flag) tt_normal_video();
   wherexy();
   switch(ch)
     {
      case 7: beep(); break;
      case 8: goto_rc(Cursor_Row, Cursor_Col - 1); break;
      case 13: goto_rc(Cursor_Row, 1); break;
	
      default: 
	pp = (unsigned short *) (Video_Base + (One60 * (Cursor_Row - 1)));
	pp += Cursor_Col - 1;
	p = (Attribute_Byte << 8) | (unsigned char) ch;
#ifndef __GO32__
	SNOW_CHECK;
#endif
	*pp = p;
	goto_rc(Cursor_Row, Cursor_Col + 1);
     }
}


	
