/*
	cp.C

	Version 1.5

	Copy & Paste

	Copyright (C) 1993, Geoff Friesen B.Sc.
	All rights reserved.

	Developed with: Borland C++ 3.1
*/

/*
	set ti=c:\borlandc\tsr\tsrlib
	t cp -hk -sc

	The BIOS memory location 0040:0084 is set to the number of
	screen rows less one.  For example, if the screen has been
	set to 25 lines then the value 24 would be stored here.  A
	value of 49 would be stored if 50-line mode were in effect
	(on VGA adaptors).  This address is used by recent adaptor
	BIOSes.  Older BIOSes do not use it.  Therefore, assume it
	to be valid if it returns a 24, 42, or 49.  The 42 is used
	in EGA 43-line mode.
*/

#if !defined(__TINY__)
#error Tiny Memory Model Expected
#endif

#include "tsrlib.H"

char *title = "\r\n"

"ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»\r\n"
"º  ±±±  ±±±±±                             º\r\n"
"º ±   ± ±   ±                             º\r\n"
"º ±     ±   ±                             º\r\n"
"º ±     ±±±±± Version 1.5                 º\r\n"
"º ±     ±                                 º\r\n"
"º ±   ± ±                                 º\r\n"
"º  ±±±  ±                                 º\r\n"
"º                                         º\r\n"
"º Copyright (C) 1993, Geoff Friesen B.Sc. º\r\n"
"º All rights reserved.                    º\r\n"
"ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼\r\n"
"$";

#define IGNORE 0

char buffer [8000];             /* buffer used as clipboard         */
char *buff_head = buffer;
char *buff_tail = buffer;
char paste = 0;                 /* flag used to signal ints ...    */
				/* ... that pasting is required    */

void    read_window     (int action, int left, int top, int right,
			 int bottom);
void    reverse_video   (int col, int row);
void    send_char       (int action, int c);

void interrupt int9 (void);
void interrupt (*oldint9) (void);
void interrupt int1c (void);
void interrupt (*oldint1c) (void);

void main (void)
{
   int cshape, key, vmode, x, y;
   int left = 1, top = 1, right = 80, bottom, col, row, rows;

   if ((vmode = v_getmode ()) != BW80 && vmode != C80 && vmode != MONO)
       return;

   rows = peekb(0x40, 0x84)+1;
   if (rows != 25 && rows != 43 && rows != 50)
       rows = 25;

   bottom = rows;

   cshape = v_getshape ();
   v_setshape (0x2000);

   x = v_wherex ();
   y = v_wherey ();

   do
   {
      reverse_video (left, top);

      key = toupper (k_fetch ());

      reverse_video (left, top);

      if (key == ESC)
	  break;

      if (key == LEFT)
      {
	  if (--left < 1)
	      left = 80;
	  continue;
      }

      if (key == RIGHT)
      {
	  if (++left > 80)
	      left = 1;
	  continue;
      }

      if (key == UP)
      {
	  if (--top < 1)
	      top = rows;
	  continue;
      }

      if (key == DOWN)
      {
	  if (++top > rows)
	      top = 1;
	  continue;
      }

      if (key == HOME)
      {
	  top = 1;
	  left = 1;
	  continue;
      }

      if (key == END)
      {
	  top = rows;
	  left = 80;
	  continue;
      }

      if (key == 'P')
      {
	  disable();
	  paste = (buff_head == buff_tail) ? 0 : 1;
	  enable();
	  break;
      }

      if (key != CR)
	  continue;

      bottom = top;
      right = left;
      reverse_video (left, top); /* highlight block at (top,left) */

      do
      {
	 col = right;
	 row = bottom;

	 if ((key = toupper (k_fetch ())) == UP)
	 {
	     if (--row < top)
		 continue;
	     bottom = row++;
	 }

	 if (key == DOWN)
	 {
	     if (++row > rows)
		 continue;
	     bottom = row;
	 }

	 if (key == UP || key == DOWN)
	 {
	     do
	     {
		 reverse_video (col--, row);
	     }
	     while (col >= left);
	     continue;
	 }

	 if (key == LEFT)
	 {
	     if (--col < left)
		 continue;
	     right = col++;
	 }

	 if (key == RIGHT)
	 {
	     if (++col > 80)
		 continue;
	     right = col;
	 }

	 if (key == LEFT || key == RIGHT)
	 {
	     do
	     {
		 reverse_video (col, row--);
	     }
	     while (row >= top);
	     continue;
	 }

	 if (key == ESC)
	 {
	     read_window (IGNORE, left, top, right, bottom);
	     break;
	 }

	 if (key == 'C')
	 {
	     read_window (!IGNORE, left, top, right, bottom);
	     break;
	 }
      }
      while (1);

      break;
   }
   while (1);

   v_gotoxy (x, y);
   v_setshape (cshape);
}

/*
	cleanup

	Restore interrupt vectors 9h and 1ch if possible.  This function
	is called by the installation kernel.

	Entry:

	CS = DS = SS
	ES = resident PSP segment

	Exit:

	DS preserved

	nonzero if error otherwise zero if ok
*/

int cleanup (void)
{
   int error;
   unsigned bx1, bx2, es1, es2, psp;

   asm push ds

   asm push es		// Set DS to resident segment PSP.
   asm pop ds

   psp = _ES;

   _AX = 0x3509;
   geninterrupt(0x21);
   bx1 = _BX;
   es1 = _ES;

   _AX = 0x351c;
   geninterrupt(0x21);
   bx2 = _BX;
   es2 = _ES;

   if (es1 != psp || bx1 != FP_OFF(int9) ||
       es2 != psp || bx2 != FP_OFF(int1c))
       error = 1;
   else
   {
       setvect (0x09, oldint9);
       setvect (0x1c, oldint1c);
       error = 0;
   }

   asm pop ds

   return error;
}

void read_window (int action, int left, int top,
			 int right, int bottom)
{
   char buf [2];
   int c, col, row;

   buff_tail = buffer;

   for (row = top; row <= bottom; row++)
   {
	for (col = left; col <= right; col++)
	{
	     reverse_video (col, row);
	     v_screen (SCREEN_SAVE, col, row, 1, 1, buf);
	     c = *buf & 255;
	     if (c != CR)
		 send_char (action, c);
	}
	send_char (action, CR);
   }
}

void reverse_video (int col, int row)
{
   char buffer [2];

   v_screen (SCREEN_SAVE, col, row, 1, 1, buffer);
   buffer [1] = ~buffer [1];
   v_screen (SCREEN_RESTORE, col, row, 1, 1, buffer);
}

void send_char (int action, int c)
{
   if (action != IGNORE)
       *buff_tail++ = c;
}

/*
	setup

	Redirect interrupt vectors 9h and 1ch.  This function is
	called by the installation kernel.

	Exit:

	nonzero if error otherwise zero if ok
*/

int setup (void)
{
   oldint9 = getvect (0x9);     /* redirect int 9h */
   setvect (0x09, int9);

   oldint1c = getvect (0x1c);   /* redirect int 1ch */
   setvect (0x1c, int1c);

   return 0;
}

void interrupt int9 (void)
{
   asm cmp BYTE PTR cs:_paste, 0
   asm jnz int9_1

   asm pushf
   asm call DWORD PTR cs:_oldint9

   return;

int9_1:

   asm push ax

   asm in al, 60h

   asm cmp al, 1
   asm jnz int9_2

   asm mov BYTE PTR cs:_paste, 0
   asm mov WORD PTR cs:_buff_head, OFFSET _buffer
   asm mov WORD PTR cs:_buff_tail, OFFSET _buffer

int9_2:

   asm in al, 61h
   asm jmp $+2
   asm mov ah, al
   asm or al, 10000000b
   asm out 61h, al
   asm jmp $+2
   asm mov al, ah
   asm out 61h, al
   asm jmp $+2

   asm mov al, 20h
   asm out 20h, al

   asm pop ax
}

void interrupt int1c (void)
{
   (*oldint1c) ();

   if (!paste)
       return;

   if (buff_head < buff_tail)
   {
       asm mov ax, 40h
       asm mov es, ax

       asm mov bx, cs:_buff_head
       asm mov al, cs:[bx]

       asm mov cx, es:[1ah]
       asm mov dx, es:[1ch]

       asm inc dx
       asm inc dx

       asm cmp dx, es:[82h]
       asm jnz int1c_1

       asm mov dx, es:[80h]

int1c_1:

       asm cmp cx, dx
       asm jz int1c_2

       asm mov di, es:[1ch]
       asm mov es:[di], ax

       asm mov es:[1ch], dx

       asm inc bx
       asm mov cs:_buff_head, bx

int1c_2:

   }
   else
   {
       paste = 0;
       buff_head = buff_tail = buffer;
   }
}

#ifndef INCL_GETVECT
#include "getvect.C"
#endif

#ifndef INCL_KFETCH
#include "kfetch.C"
#endif

#ifndef INCL_TOUPPER
#include "toupper.C"
#endif

#ifndef	INCL_VGETMODE
#include "vgetmode.C"
#endif

#ifndef INCL_VGETSHAPE
#include "vgetshap.C"
#endif

#ifndef INCL_VGOTOXY
#include "vgotoxy.C"
#endif

#ifndef INCL_VSCREEN
#include "vscreen.C"
#endif

#ifndef INCL_SETVECT
#include "setvect.C"
#endif

#ifndef INCL_VSETSHAPE
#include "vsetshap.C"
#endif

#ifndef INCL_VWHEREX
#include "vwherex.C"
#endif

#ifndef INCL_VWHEREY
#include "vwherey.C"
#endif