/* VGAlib version 1.0 - (c) 1992 Tommy Frandsen 		   */
/*								   */
/* This library is free software; you can redistribute it and/or   */
/* modify it without any restrictions. This library is distributed */
/* in the hope that it will be useful, but without any warranty;   */
/* without even the implied warranty of merchantability or fitness */
/* for a particular purpose.					   */

/******************************************/
/*   !!!!!!! NOTICE !!!!!!!!              */
/*  The vga.c in this package is modified */
/*  the origin version vga.c can find in  */
/*  Linux SLS distribution.               */
/******************************************/ 

#include "vga.h"
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <termios.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/kd.h>
#include <sys/types.h>
#include <sys/vt.h>
#include <linux/mm.h>
#undef free
#include <stdlib.h>

#define GRAPH_BASE 0xA0000
#define GRAPH_SIZE 0x10000
#define FONT_BASE  0xA0000
#define FONT_SIZE  0x2000
#define NULL_SIZE  0x1000

/* VGA index register ports */
#define CRT_IC  0x3D4   /* CRT Controller Index - color emulation */
#define CRT_IM  0x3B4   /* CRT Controller Index - mono emulation */
#define ATT_IW  0x3C0   /* Attribute Controller Index & Data Write Register */
#define GRA_I   0x3CE   /* Graphics Controller Index */
#define SEQ_I   0x3C4   /* Sequencer Index */
#define PEL_IW  0x3C8   /* PEL Write Index */
#define PEL_IR  0x3C7   /* PEL Read Index */

/* VGA data register ports */
#define CRT_DC  0x3D5   /* CRT Controller Data Register - color emulation */
#define CRT_DM  0x3B5   /* CRT Controller Data Register - mono emulation */
#define ATT_R   0x3C1   /* Attribute Controller Data Read Register */
#define GRA_D   0x3CF   /* Graphics Controller Data Register */
#define SEQ_D   0x3C5   /* Sequencer Data Register */
#define MIS_R   0x3CC   /* Misc Output Read Register */
#define MIS_W   0x3C2   /* Misc Output Write Register */
#define IS1_RC  0x3DA   /* Input Status Register 1 - color emulation */
#define IS1_RM  0x3BA   /* Input Status Register 1 - mono emulation */
#define PEL_D   0x3C9   /* PEL Data Register */

/* VGA indexes max counts */
#define CRT_C   24      /* 24 CRT Controller Registers */
#define ATT_C   21      /* 21 Attribute Controller Registers */
#define GRA_C   9       /* 9  Graphics Controller Registers */
#define SEQ_C   5       /* 5  Sequencer Registers */
#define MIS_C   1       /* 1  Misc Output Register */
 
/* VGA registers saving indexes */
#define CRT     0               /* CRT Controller Registers start */
#define ATT     CRT+CRT_C       /* Attribute Controller Registers start */
#define GRA     ATT+ATT_C       /* Graphics Controller Registers start */
#define SEQ     GRA+GRA_C       /* Sequencer Registers */
#define MIS     SEQ+SEQ_C       /* General Registers */
#define END     MIS+MIS_C       /* last */

#define ABS(a) (((a)<0) ? -(a) : (a))

/* variables used to shift between monchrome and color emulation */
static int CRT_I;		/* current CRT index register address */
static int CRT_D;		/* current CRT data register address */
static int IS1_R;		/* current input status register address */
static int color_text;		/* true if color text emulation */


/* graphics mode information */
struct info {
    int xdim;
    int ydim;
    int colors;
    int xbytes;
};



/* BIOS mode 12h - 640x480x16 */
static char g640x480x16_regs[60] = {
  0x5F,0x4F,0x50,0x82,0x54,0x80,0x0B,0x3E,0x00,0x40,0x00,0x00,
  0x00,0x00,0x00,0x00,0xEA,0x8C,0xDF,0x28,0x00,0xE7,0x04,0xE3,
  0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,
  0x0C,0x0D,0x0E,0x0F,0x01,0x00,0x0F,0x00,0x00,
  0x00,0x0F,0x00,0x20,0x00,0x00,0x05,0x0F,0xFF,
  0x03,0x01,0x0F,0x00,0x06,
  0xE3
};
static struct info g640x480x16_info = { 640, 480, 16, 80 };


/* default palette values */
static char default_red[256]   
             = { 0, 0, 0, 0,42,42,42,42,21,21,21,21,63,63,63,63,
		 0, 5, 8,11,14,17,20,24,28,32,36,40,45,50,56,63,
		 0,16,31,47,63,63,63,63,63,63,63,63,63,47,31,16,
		 0, 0, 0, 0, 0, 0, 0, 0,31,39,47,55,63,63,63,63,
		63,63,63,63,63,55,47,39,31,31,31,31,31,31,31,31,
		45,49,54,58,63,63,63,63,63,63,63,63,63,58,54,49,
		45,45,45,45,45,45,45,45, 0, 7,14,21,28,28,28,28,
		28,28,28,28,28,21,14, 7, 0, 0, 0, 0, 0, 0, 0, 0,
		14,17,21,24,28,28,28,28,28,28,28,28,28,24,21,17,
		14,14,14,14,14,14,14,14,20,22,24,26,28,28,28,28,
		28,28,28,28,28,26,24,22,20,20,20,20,20,20,20,20,
		 0, 4, 8,12,16,16,16,16,16,16,16,16,16,12, 8, 4,
		 0, 0, 0, 0, 0, 0, 0, 0, 8,10,12,14,16,16,16,16,
		16,16,16,16,16,14,12,10, 8, 8, 8, 8, 8, 8, 8, 8,
		11,12,13,15,16,16,16,16,16,16,16,16,16,15,13,12,
		11,11,11,11,11,11,11,11, 0, 0, 0, 0, 0, 0, 0, 0};
static char default_green[256] 
	     = { 0, 0,42,42, 0, 0,21,42,21,21,63,63,21,21,63,63,
		 0, 5, 8,11,14,17,20,24,28,32,36,40,45,50,56,63,
		 0, 0, 0, 0, 0, 0, 0, 0, 0,16,31,47,63,63,63,63,
		63,63,63,63,63,47,31,16,31,31,31,31,31,31,31,31,
		31,39,47,55,63,63,63,63,63,63,63,63,63,55,47,39,
		45,45,45,45,45,45,45,45,45,49,54,58,63,63,63,63,
		63,63,63,63,63,58,54,49, 0, 0, 0, 0, 0, 0, 0, 0,
		 0, 7,14,21,29,28,28,28,28,28,28,28,28,21,14, 7,
		14,14,14,14,14,14,14,14,14,17,21,24,28,28,28,28,
		28,28,28,28,28,24,21,17,20,20,20,20,20,20,20,20,
		20,22,24,26,28,28,28,28,28,28,28,28,28,26,24,22,
		 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 8,12,16,16,16,16,
		16,16,16,16,16,12, 8, 4, 8, 8, 8, 8, 8, 8, 8, 8,
		 8,10,12,14,16,16,16,16,16,16,16,16,16,14,12,10,
		11,11,11,11,11,11,11,11,11,12,13,15,16,16,16,16,
		16,16,16,16,16,15,13,12, 0, 0, 0, 0, 0, 0, 0, 0};
static char default_blue[256]  
             = { 0,42, 0,42, 0,42, 0,42,21,63,21,63,21,63,21,63,
		 0, 5, 8,11,14,17,20,24,28,32,36,40,45,50,56,63,
		63,63,63,63,63,47,31,16, 0, 0, 0, 0, 0, 0, 0, 0,
		 0,16,31,47,63,63,63,63,63,63,63,63,63,55,47,39,
		31,31,31,31,31,31,31,31,31,39,47,55,63,63,63,63,
		63,63,63,63,63,58,54,49,45,45,45,45,45,45,45,45,
		45,49,54,58,63,63,63,63,28,28,28,28,28,21,14, 7,
		 0, 0, 0, 0, 0, 0, 0, 0, 0, 7,14,21,28,28,28,28,
		28,28,28,28,28,24,21,17,14,14,14,14,14,14,14,14,
		14,17,21,24,28,28,28,28,28,28,28,28,28,26,24,22,
		20,20,20,20,20,20,20,20,20,22,24,26,28,28,28,28,
		16,16,16,16,16,12, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0,
		 0, 4, 8,12,16,16,16,16,16,16,16,16,16,14,12,10,
		 8, 8, 8, 8, 8, 8, 8, 8, 8,10,12,14,16,16,16,16,
		16,16,16,16,16,15,13,12,11,11,11,11,11,11,11,11,
		11,12,13,15,16,16,16,16, 0, 0, 0, 0, 0, 0, 0, 0};


/* used to decompose color value into bits (for fast scanline drawing) */
union bits {
    struct {
        char bit3;
        char bit2;
        char bit1;
        char bit0;
    } b;
    unsigned int i;
};

/* color decompositions */
static union bits color16[16] = {{0,0,0,0},
			         {0,0,0,1},
			      	 {0,0,1,0},
			         {0,0,1,1},
			         {0,1,0,0},
			         {0,1,0,1},
			         {0,1,1,0},
			         {0,1,1,1},
			         {1,0,0,0},
			         {1,0,0,1},
			         {1,0,1,0},
			         {1,0,1,1},
			         {1,1,0,0},
			         {1,1,0,1},
			         {1,1,1,0},
			         {1,1,1,1}};

/* display plane buffers (for fast scanline drawing) */
static char plane0[256];
static char plane1[256];
static char plane2[256];
static char plane3[256];


static char text_regs[60];   /* VGA registers for saved text mode */   

/* saved text mode palette values */ 
static char text_red[256];  
static char text_green[256];  
static char text_blue[256];  

/* saved graphics mode palette values */ 
static char graph_red[256];  
static char graph_green[256];  
static char graph_blue[256];  

static int         prv_mode  = TEXT;     /* previous video mode      */
static int         cur_mode  = TEXT;     /* current video mode       */
static int         flip_mode = TEXT;     /* flipped video mode       */
static struct info cur_info;             /* current video parameters */
int         cur_color;            /* current color            */

static int initialized = 0;   /* flag: initialize() called ?  */
static int flip        = 0;   /* flag: executing vga_flip() ? */

static int   tty0_fd;    /* /dev/tty0 file descriptor 		     */
static int   mem_fd;     /* /dev/mem file descriptor		     */
static FILE* console;    /* console input stream		     */
char *origin_graph_mem;   /* the location of vga mapping */
char* graph_mem;         /* now screen location */

static char* graph_buf = NULL;  /* saves graphics data during flip */

static char font_buf1[FONT_SIZE];  /* saved font data - plane 2 */
static char font_buf2[FONT_SIZE];  /* saved font data - plane 3 */
static char null_buf[NULL_SIZE];   /* used to speed up clear */

static struct termios text_termio;  /* text mode termio parameters     */
static struct termios graph_termio; /* graphics mode termio parameters */

static int flipchar = '\x1b';   /* flip character - initially  ESCAPE */

char vga_screenmask=0;
char vga_isunderline=0;
static struct vt_mode vt_mode,old_vt_mode;
int nowconsole;
static void (*SIGREL_oldint)(int),
           (*SIGACQ_oldint)(int),
           (*SIGINT_oldint)(int),
           (*SIGTERM_oldint)(int),
           (*SIGHUP_oldint)(int),
           (*SIGSEGV_oldint)(int);

static void inline port_out(char value, unsigned short port)
{
__asm__ volatile ("outb %0,%1"
		::"a" ((char) value),"d" ((unsigned short) port));
}


static unsigned char inline port_in(unsigned short port)
{
	unsigned char _v;
__asm__ volatile ("inb %1,%0"
		:"=a" (_v):"d" ((unsigned short) port));
	return _v;
}


static void set_graphtermio()
{
        /* set graphics mode termio parameters */
        ioctl(0, TCSETSW, &graph_termio);
}


static void set_texttermio()
{
    /* restore text mode termio parameters */
    ioctl(0, TCSETSW, &text_termio);
}


void disable_interrupt()
{
    struct termios cur_termio;

    ioctl(0, TCGETS, &cur_termio);
    cur_termio.c_lflag &= ~ISIG;
    ioctl(0, TCSETSW, &cur_termio);
}


void enable_interrupt()
{
    struct termios cur_termio;

    ioctl(0, TCGETS, &cur_termio);
    cur_termio.c_lflag |= ISIG;
    ioctl(0, TCSETSW, &cur_termio);
}


static int set_regs(char regs[])
{
    int i;

    /* disable video */
    port_in(IS1_R);	        
    port_out(0x00, ATT_IW);     
  
    /* update misc output register */
    port_out(regs[MIS], MIS_W);         

    /* synchronous reset on */
    port_out(0x00,SEQ_I); 
    port_out(0x01,SEQ_D);	        
  
    /* write sequencer registers */
    for (i = 1; i < SEQ_C; i++) {       
	port_out(i, SEQ_I); 
	port_out(regs[SEQ+i], SEQ_D); 
    }

    /* synchronous reset off */
    port_out(0x00, SEQ_I); 
    port_out(0x03, SEQ_D);	        
  
    /* deprotect CRT registers 0-7 */
    port_out(0x11, CRT_I);		  
    port_out(port_in(CRT_D)&0x7F, CRT_D);   
  
    /* write CRT registers */
    for (i = 0; i < CRT_C; i++) {       
	port_out(i, CRT_I); 
	port_out(regs[CRT+i], CRT_D); 
    }

    /* write graphics controller registers */
    for (i = 0; i < GRA_C; i++) {       
	port_out(i, GRA_I); 
	port_out(regs[GRA+i], GRA_D); 
    }
     
    /* write attribute controller registers */
    for (i = 0; i < ATT_C; i++) {       
	port_in(IS1_R);   /* reset flip-flop */
	port_out(i, ATT_IW);
	port_out(regs[ATT+i],ATT_IW);
    }
}


void vga_initialize_raw()
{
    int  i, j;
    FILE *fp;
    struct vt_stat vstate;

    /* save text mode termio parameters */
    ioctl(0, TCGETS, &text_termio);

    /* get I/O permissions for VGA registers */
    if (ioperm(CRT_IC, 1, 1)) {
	printf("VGAlib: can't get I/O permissions \n");
	exit (-1);
    }
    ioperm(CRT_IM,  1, 1);
    ioperm(ATT_IW, 1, 1);
    ioperm(GRA_I,  1, 1);
    ioperm(SEQ_I,  1, 1);
    ioperm(PEL_IW, 1, 1);
    ioperm(PEL_IR, 1, 1);
    ioperm(CRT_DC,  1, 1);
    ioperm(CRT_DM,  1, 1);
    ioperm(ATT_R,  1, 1);
    ioperm(GRA_D,  1, 1);
    ioperm(SEQ_D,  1, 1);
    ioperm(MIS_R,  1, 1);
    ioperm(MIS_W,  1, 1);
    ioperm(IS1_RC,  1, 1);
    ioperm(IS1_RM,  1, 1);
    ioperm(PEL_D,  1, 1);

    /* color or monochrome text emulation? */
    color_text = port_in(MIS_R)&0x01;

    /* chose registers for color/monochrome emulation */

    CRT_I = CRT_IC;
    CRT_D = CRT_DC;
    IS1_R = IS1_RC;

    /* open /dev/mem */
    if ((mem_fd = open("/dev/mem", O_RDWR) ) < 0) {
	printf("VGAlib: can't open /dev/mem \n");
	exit (-1);
    }

    /* mmap graphics memory */
    if ((graph_mem = malloc(GRAPH_SIZE + (PAGE_SIZE-1))) == NULL) {
	printf("VGAlib: allocation error \n");
	exit (-1);
    }
    if ((unsigned long)graph_mem % PAGE_SIZE)
	graph_mem += PAGE_SIZE - ((unsigned long)graph_mem % PAGE_SIZE);
    graph_mem = (unsigned char *)mmap(
	(caddr_t)graph_mem, 
	GRAPH_SIZE,
	PROT_READ|PROT_WRITE,
	MAP_SHARED|MAP_FIXED,
	mem_fd, 
	GRAPH_BASE
    );
    if ((long)graph_mem < 0) {
	printf("VGAlib: mmap error \n");
	exit (-1);
    }
    close(mem_fd);
    /* disable video */
    port_in(IS1_R);	        
    port_out(0x00, ATT_IW);     
  
    /* save text mode palette - first select palette index 0 */
    port_out(0, PEL_IR); 

    /* read RGB components - index is autoincremented */
    for(i = 0; i < 256; i++) {
	for(j = 0; j < 10; j++) ;   /* delay (minimum 240ns) */ 
	text_red[i] = port_in(PEL_D);
	for(j = 0; j < 10; j++) ;   /* delay (minimum 240ns) */ 
	text_green[i] = port_in(PEL_D);
	for(j = 0; j < 10; j++) ;   /* delay (minimum 240ns) */ 
	text_blue[i] = port_in(PEL_D);
    }

    /* save text mode VGA registers */
    for (i = 0; i < CRT_C; i++) {
	 port_out(i, CRT_I); 
	 text_regs[CRT+i] = port_in(CRT_D); 
    }
    for (i = 0; i < ATT_C; i++) {
      	 port_in(IS1_R);
         port_out(i, ATT_IW); 
         text_regs[ATT+i] = port_in(ATT_R); 
    }
    for (i = 0; i < GRA_C; i++) {
       	 port_out(i, GRA_I); 
       	 text_regs[GRA+i] = port_in(GRA_D); 
    }
    for (i = 0; i < SEQ_C; i++) {
       	 port_out(i, SEQ_I); 
       	 text_regs[SEQ+i] = port_in(SEQ_D); 
    }
    text_regs[MIS] = port_in(MIS_R); 

    /* shift to color emulation */
    CRT_I = CRT_IC;
    CRT_D = CRT_DC;
    IS1_R = IS1_RC;
    port_out(port_in(MIS_R)|0x01, MIS_W); 

    /* save font data - first select a 16 color graphics mode */
    set_regs(g640x480x16_regs);

    /* save font data in plane 2 */
    port_out(0x04, GRA_I); 
    port_out(0x02, GRA_D); 
    memcpy(font_buf1, graph_mem, FONT_SIZE);

    /* save font data in plane 3 */
    port_out(0x04, GRA_I); 
    port_out(0x03, GRA_D); 
    memcpy(font_buf2, graph_mem, FONT_SIZE);

    /* initialize buffer used when clearing in 256 color modes */
    for(i = 0; i < NULL_SIZE; i++)
        null_buf[i] = 0;

    initialized = 1;

  }


int vga_setmode(int mode) 
{
    int i;

    if (!initialized)
        vga_initialize_raw();

    disable_interrupt();

    prv_mode = cur_mode;
    cur_mode = mode;

    /* disable video */
    port_in(IS1_R); 		
    port_out(0x00, ATT_IW);	
  
    if (mode == TEXT) {    
        /* restore font data - first select a 16 color graphics mode */
        set_regs(g640x480x16_regs);

	/* disable Set/Reset Register */
    	port_out(0x01, GRA_I ); 
    	port_out(0x00, GRA_D );   

        /* restore font data in plane 2 - necessary for all VGA's */
    	port_out(0x02, SEQ_I ); 
    	port_out(0x04, SEQ_D );   
	memcpy(graph_mem, font_buf1, FONT_SIZE);

        /* restore font data in plane 3 - necessary for Trident VGA's */
    	port_out(0x02, SEQ_I ); 
    	port_out(0x08, SEQ_D );   
	memcpy(graph_mem, font_buf2, FONT_SIZE);

        /* change register adresses if monochrome text mode */
        if (!color_text) {
            CRT_I = CRT_IM;
            CRT_D = CRT_DM;
            IS1_R = IS1_RM;
            port_out(port_in(MIS_R)&0xFE, MIS_W); 
        }

	/* restore text mode VGA registers */
    	set_regs(text_regs);

        /* restore saved palette */
        for(i = 0; i < 256; i++)
            vga_setpalette(
                i, text_red[i], text_green[i], text_blue[i]
            );


        /* enable video */
        port_in(IS1_R); 
        port_out(0x20, ATT_IW); 
  
        /* restore text mode termio */
        set_texttermio();

    } else {
	/* disable text output */

        /* shift to color emulation */
        CRT_I = CRT_IC;
        CRT_D = CRT_DC;
        IS1_R = IS1_RC;
        port_out(port_in(MIS_R)|0x01, MIS_W); 

	
	set_regs(g640x480x16_regs);
	cur_info = g640x480x16_info;
	
	    
        if (!flip) {
            /* set default palette */
            for(i = 0; i < 256; i++)
                vga_setpalette(
                    i, default_red[i], default_green[i], default_blue[i]
                );
 
            /* clear screen (sets current color to 15) */
            vga_clear();
        }    

        /* enable video */
        port_in(IS1_R); 
        port_out(0x20, ATT_IW); 
  
        /* set graphics mode termio */
        set_graphtermio();
    }

    return 0;  
}

int vga_clearblock(int lx,int ty,int rx,int by)
{
  int c,i;

  c = cur_color;
  switch(cur_mode) 
    {
    case G640x480x16:
      vga_setcolor(c/256);

      /* write to all bits */
      port_out(0x08, GRA_I ); 
      port_out(0xFF, GRA_D );   
      
      /* write dummy values to clear video memory */
      for(i = ty; i < by; i++)
	memcpy(graph_mem+i*cur_info.xbytes+lx,null_buf,rx-lx+1);
      
      break;
    }
  vga_setcolor(c);
}

int vga_clear()
{
    int i,c;

    vga_screenoff();
    c = cur_color;
    vga_setcolor(c/256);

    /* write to all bits */
    port_out(0x08, GRA_I ); 
    port_out(0xFF, GRA_D );   
    
    /* write dummy values to clear video memory */
    for(i = 0; i < 16; i++)
      memcpy(graph_mem + i*NULL_SIZE, null_buf, NULL_SIZE);
    
    vga_setcolor(c);
    vga_screenon();

    return 0;
}


int inline vga_setcolor(int color)
{
  /* update set/reset register */
  port_out(0x00, GRA_I ); 
  port_out(color, GRA_D );   
  cur_color = color;
  return 0;
}


int vga_setpalette(int index, int red, int green, int blue)
{
    int i;

    /* select palette register */
    port_out(index, PEL_IW); 

    /* write RGB components */
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */ 
    port_out(red, PEL_D);
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */ 
    port_out(green, PEL_D);
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */ 
    port_out(blue, PEL_D);

    return 0;
}


int vga_getpalette(int index, int *red, int *green, int *blue)
{
    int i;

    /* select palette register */
    port_out(index, PEL_IR);

    /* read RGB components */
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
    *red = (int) port_in(PEL_D);
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
    *green = (int) port_in(PEL_D);
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
    *blue = (int) port_in(PEL_D);

    return 0;
}


int vga_setpalvec(int start, int num, int *pal)
{
    int i, j;

    /* select palette register */
    port_out(start, PEL_IW);

    for(j = 0; j < num; j++) {
        for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
        port_out(*(pal++), PEL_D);
        for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
        port_out(*(pal++), PEL_D);
        for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
        port_out(*(pal++), PEL_D);
    }

    return j;
}


int vga_getpalvec(int start, int num, int *pal)
{
    int i, j;

    /* select palette register */
    port_out(start, PEL_IR);

    for(j = 0; j < num; j++) {
        for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
        *(pal++) = (int) port_in(PEL_D);
        for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
        *(pal++) = (int) port_in(PEL_D);
        for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
        *(pal++) = (int) port_in(PEL_D);
    }

    return i;
}


int inline vga_drawpixel(int x, int y)
{
  unsigned long offset;

  /* select bit */
  port_out(8, GRA_I);
  port_out(0x80 >> (x & 7), GRA_D);   
  
  /* read into latch and write dummy back */
  offset = y*cur_info.xbytes + (x>>3);   
  graph_mem[offset] = graph_mem[offset];   
  
  return 0;
}


int vga_drawscanline(int line, char* colors)
{
  
  int i, j, k, first, last;
  union bits bytes;
  char* address;
  
  k = 0;
  for(i = 0; i < cur_info.xdim; i += 8) {
    bytes.i = 0;
    first = i;
    last  = i+8;
    for(j = first; j < last; j++)
      bytes.i = (bytes.i<<1) | color16[colors[j]].i;
    plane0[k]   = bytes.b.bit0;
    plane1[k]   = bytes.b.bit1;
    plane2[k]   = bytes.b.bit2;
    plane3[k++] = bytes.b.bit3;
  }
  
  address = graph_mem + line*cur_info.xbytes;
  
  /* disable Set/Reset Register */
  port_out(0x01, GRA_I ); 
  port_out(0x00, GRA_D ); 
  
  /* write to all bits */
  port_out(0x08, GRA_I ); 
  port_out(0xFF, GRA_D );   
  
  /* select map mask register */
  port_out(0x02, SEQ_I ); 
  
  /* write plane 0 */
  port_out(0x01, SEQ_D ); 
  memcpy(address, plane0, cur_info.xbytes);
  
  /* write plane 1 */
  port_out(0x02, SEQ_D ); 
  memcpy(address, plane1, cur_info.xbytes);
  
  /* write plane 2 */
  port_out(0x04, SEQ_D ); 
  memcpy(address, plane2, cur_info.xbytes);
  
  /* write plane 3 */
  port_out(0x08, SEQ_D ); 
  memcpy(address, plane3, cur_info.xbytes);

  /* restore map mask register */
  port_out(0x0F, SEQ_D ); 
  
  /* enable Set/Reset Register */
  port_out(0x01, GRA_I ); 
  port_out(0x0F, GRA_D );   
  return 0;
}


int vga_drawline(int x1, int y1, int x2, int y2)
{
    int dx = x2 - x1;
    int dy = y2 - y1;
    int ax = ABS(dx) << 1;
    int ay = ABS(dy) << 1;
    int sx = (dx >= 0) ? 1 : -1;
    int sy = (dy >= 0) ? 1 : -1;
 
    int x  = x1;
    int y  = y1;

    if (ax > ay) {
        int d = ay - (ax >> 1);
        while (x != x2) {
	    vga_drawpixel(x, y);

            if (d > 0 || d == 0 && sx == 1) {
                y += sy;
                d -= ax;
            }
            x += sx;
            d += ay;
        }
    } else {
        int d = ax - (ay >> 1);
        while (y != y2) {
	    vga_drawpixel(x, y);

            if (d > 0 || d == 0 && sy == 1) {
                x += sx;
                d -= ay;
            }
            y += sy;
            d += ax;
        }
    }
    vga_drawpixel(x, y);
     
    return 0;
}


int vga_screenoff()
{
    /* turn off screen for faster VGA memory acces */
    port_out(0x01, SEQ_I); 		  
    port_out(port_in(SEQ_D)|0x20, SEQ_D);   

    return 0;
}


int vga_screenon()
{
    /* turn screen back on */
    port_out(0x01, SEQ_I); 		  
    port_out(port_in(SEQ_D)&0xDF, SEQ_D);   

    return 0;
}


int vga_getxdim()
{
    return cur_info.xdim;
}


int vga_getydim()
{
    return cur_info.ydim;
}


int vga_getcolors()
{
    return cur_info.colors;
}
int vga_curcolor()
{
  return cur_color;
}

int vga_getch(int fd)
{
    char c;
    struct termio savetty,newtty;

    if (cur_mode == TEXT)
	return -1;

    ioctl(fd,TCGETA,&savetty);
    newtty = savetty;
    newtty.c_lflag &= ~(ECHO|ICANON|ISIG);
    newtty.c_cc[VMIN] = 1;
    newtty.c_cc[VTIME] = 1;
    ioctl(fd,TCSETAF,&newtty);
    
    read(fd, &c, 1);
    
    ioctl(fd,TCSETAF,&savetty);
    return c;
}


int vga_setflipchar(int c)
{
    flipchar = c;

    return 0;
}


int vga_flip()
{
    int i, j;

    if (cur_mode == TEXT && flip_mode == TEXT)
	return -1;

    flip = 1;

    disable_interrupt();

    /* disable video */
    port_in(IS1_R); 		
    port_out(0x00, ATT_IW);	
  
    if (cur_mode == TEXT) {
	/* disable text output */


        /* restore all four planes - first select a 16 color graphics mode */
        set_regs(g640x480x16_regs);

	/* disable Set/Reset Register */
    	port_out(0x01, GRA_I ); 
    	port_out(0x00, GRA_D );   

        for(i = 0; i < 4; i++) {
            /* restore plane i */
    	    port_out(0x02, SEQ_I ); 
    	    port_out(1<<i, SEQ_D );   
            memcpy(graph_mem, graph_buf + i*GRAPH_SIZE, GRAPH_SIZE);
	}

        free(graph_buf);

        /* restore saved palette */
        for(i = 0; i < 256; i++)
            vga_setpalette(
                i, graph_red[i], graph_green[i], graph_blue[i]
            );

        vga_setmode(flip_mode);

	flip_mode = TEXT;
    } else {
	/* save all four planes - first select a 16 color graphics mode */
        set_regs(g640x480x16_regs);

	/* allocate memory for saved graphics data - only if necessary */
        if ((graph_buf = malloc(4*GRAPH_SIZE)) == NULL) {
            printf("vga_flip: allocation error \n");
            exit (-1);
        }

        for(i = 0; i < 4; i++) {
            /* save plane i */
            port_out(0x04, GRA_I); 
            port_out(   i, GRA_D); 
            memcpy(graph_buf + i*GRAPH_SIZE, graph_mem, GRAPH_SIZE);
	}

    	/* save graphics mode palette - first select palette index 0 */
    	port_out(0, PEL_IR); 

    	/* read RGB components - index is autoincremented */
    	for(i = 0; i < 256; i++) {
            for(j = 0; j < 10; j++) ;   /* delay (minimum 240ns) */ 
            graph_red[i] = port_in(PEL_D);
    	    for(j = 0; j < 10; j++) ;   /* delay (minimum 240ns) */ 
            graph_green[i] = port_in(PEL_D);
    	    for(j = 0; j < 10; j++) ;   /* delay (minimum 240ns) */ 
            graph_blue[i] = port_in(PEL_D);
        }

        flip_mode = cur_mode;

	vga_setmode(TEXT);
    }

    flip = 0;

    return 0;
}
static void zero_latch();
int vga_buzy = 0;
void vga_drawctext(int x,int y,char *buf)
{
  int i,j,off,old,dummy,c;
  unsigned char mask;
  void asm_draw_ctext(int,int,char *,short int);

  vga_buzy = 1;
  asm_draw_ctext(x,y,buf,cur_color);
  vga_buzy = 0;
}
void vga_drawtext(int x,int y,char *buf)
{
  int i,j,off,old,dummy;
  unsigned char mask;
  void asm_draw_text(int,int,char *,short int);

  vga_buzy = 1;
        
  asm_draw_text(x,y,buf,cur_color);
  vga_buzy = 0;
}


void vga_clreol(int x,int y)
{
  int i,j,off,old,c;
  
  c = cur_color;
  vga_setcolor(0);
      
  port_out(8,GRA_I);
  port_out(0xff,GRA_D);
  for(j=y;j<y+16;j++)
    {
      off = j * cur_info.xbytes;
      for(i=x;i<cur_info.xbytes;i++)
	{
	  graph_mem[off+i] = graph_mem[off+i];
	}
    }
      
  vga_setcolor(c);
  zero_latch();

}

	      
void vga_scroll(int ul,int dl,int line)
{
  int i,j,soff,doff,c;
  char old;
  void asm_scroll(int,int,int,short);


  asm_scroll(ul,dl-1,line,cur_color);
/*
  port_out(5,GRA_I);
  old = port_in(GRA_D);
  port_out((old&0xfc)|1,GRA_D);
  
  for(j=ul;j < dl-line;j++)
    {
      doff = j*cur_info.xbytes;
      soff = (j+line)*cur_info.xbytes;
      for(i=0;i<cur_info.xbytes;i++)
	graph_mem[doff++] = graph_mem[soff++];
    }
  port_out(5,GRA_I);
  port_out(old,GRA_D);
  c = cur_color;
  port_out(8,GRA_I);
  port_out(0xff,GRA_D);
  vga_setcolor(0);
  for(j=dl-line;j < dl;j++)
    {
      doff = j*cur_info.xbytes;
      for(i=0;i<cur_info.xbytes;i++)
	graph_mem[doff+i] = graph_mem[doff+i];
    }
  vga_setcolor(c);
  
  zero_latch();
  */
}

void vga_rscroll(int ul,int dl,int line)
{
  int i,j,soff,doff,c;
  char old;

  
  port_out(5,GRA_I);
  old = port_in(GRA_D);
  port_out(9,GRA_D);
  
  for(j=dl-1;j>=ul+line;j--)
    {
      doff = j*cur_info.xbytes;
      soff = (j - line)*cur_info.xbytes;
      for(i=0;i<cur_info.xbytes;i++)
	graph_mem[doff++] = graph_mem[soff++];
    }
  port_out(5,GRA_I);
  port_out(old,GRA_D);
  c = cur_color;
  port_out(8,GRA_I);
  port_out(0xff,GRA_D);
  vga_setcolor(0);
  for(j=ul;j<ul+line;j++)
    {
      doff = j*cur_info.xbytes;
      for(i=0;i<cur_info.xbytes;i++)
	graph_mem[doff+i] = graph_mem[doff+i];
    }
  vga_setcolor(c);
      
  zero_latch();
}


int vga_cursor(int x,int y)
{
  int i,off,j,c,dummy,mask;
  char old,oldp;

  port_out(3,GRA_I);
  old = port_in(GRA_D);
  port_out( 0x18,GRA_D);
  port_out(8,GRA_I);
  oldp = port_in(GRA_D);
  port_out(0xff,GRA_D);
  for(j=y+13;j<y+15;j++)
    {
      off = j*cur_info.xbytes+x;
      graph_mem[off] = graph_mem[off];
    }
  port_out(3,GRA_I);
  port_out(old,GRA_D);
  port_out(8,GRA_I);
  port_out(oldp,GRA_D);
  
}


static void zero_latch()
{
  int c;
  char dummy;
  port_out(8,GRA_I);
  port_out(0xff,GRA_D);
  c = cur_color;
  
  vga_setcolor(0);
  
  /* load zero into latch */
  graph_mem[60000] = graph_mem[60000];
  dummy = graph_mem[60000];
  
  vga_setcolor(c);
}

void vga_moveleft(int lx,int rx,int y,int nc)
{
  int i,j;
  char old;

  port_out(5,GRA_I);
  old = port_in(GRA_D);
  port_out((old&0xfc)|1,GRA_D);
  
  for(j=y;j<y+16;j++)
    {
      for(i=lx+nc;i<=rx;i++)
      	graph_mem[j*cur_info.xbytes+i-nc] = graph_mem[j*cur_info.xbytes+i];
    }
    
  port_out(5,GRA_I);
  port_out(old,GRA_D);
  vga_clearblock(rx,y,rx,y+16);
}


void vga_invert(int i)
{
  if (i==0)
    vga_screenmask = 0;
  else
    vga_screenmask = 0xff;
	  
}
void vga_underline(int i)
{
  if (i==0)
    vga_isunderline = 0;
  else
    vga_isunderline = 1;
}

int vga_gbuf_size()
{
  return GRAPH_SIZE*4+256*3*sizeof(int);
}

void vga_setgscr(char *buf)
{
  int i;
  /* disable Set/Reset Register */
  port_out(0x01, GRA_I ); 
  port_out(0x00, GRA_D );   
  
  for(i = 0; i < 4; i++) {
    /* restore plane i */
    port_out(0x02, SEQ_I ); 
    port_out(1<<i, SEQ_D );
    memcpy(graph_mem, buf + i*GRAPH_SIZE, GRAPH_SIZE);
  }
  /* restore saved palette */
  vga_setpalvec(0,256,(int *)(buf+GRAPH_SIZE*4));
}

void vga_getgscr(char *buf)
{
  int i;

  for(i = 0; i < 4; i++) {
    /* save plane i */
    port_out(0x04, GRA_I);
    port_out(   i, GRA_D); 
    memcpy(graph_buf + i*GRAPH_SIZE, graph_mem, GRAPH_SIZE);
  }
  vga_getpalvec(0,256,(int *)(buf+GRAPH_SIZE*4));
}

