/*******************************************************************************
+
+  LEDA 3.5
+
+  _dos_vga.c
+
+  This file is part of the LEDA research version (LEDA-R) that can be 
+  used free of charge in academic research and teaching. Any commercial
+  use of this software requires a license which is distributed by the
+  LEDA Software GmbH, Postfach 151101, 66041 Saarbruecken, FRG
+  (fax +49 681 31104).
+
+  Copyright (c) 1991-1997  by  Max-Planck-Institut fuer Informatik
+  Im Stadtwald, 66123 Saarbruecken, Germany     
+  All rights reserved.
+ 
*******************************************************************************/
/*
extern void  init_graphics(int mode, int& width, int& height, int& depth);
extern void  define_color(int i, int r, int g, int b);
extern void  draw_scan_segment(unsigned char* p, int x, int y, int len);
extern void  draw_pixel(int x, int y, unsigned char c);
extern int   check_next_event(int& val, int& x, int& y, int block);
*/


#if defined(__TURBOC__)
extern "C" unsigned char inportb(int port_id);
extern "C" void outportb(int portid, unsigned char value);
#define _PORT_DEFS
#endif

#include <dos.h>
#include <conio.h>

#if defined(__EMX__)
#include <sys/hw.h>
#define port_out(value,port) _outp32(port,value)
#define port_in(port)  _inp32(port)

#elif defined(__TURBOC__) || defined(__GNUG__)
#define port_out(value,port) outportb(port,value)
#define port_in(port)  inportb(port)

#else
#define port_out(value,port) outp(port,value)
#define port_in(port)  inp(port)

#endif


#if defined (__ZTC__) || defined(__TURBOC__)
typedef unsigned char far* VIDEO_PTR;
#else
typedef unsigned char* VIDEO_PTR;
#endif


/* 
 * basic graphics routines in VGA mode 640x480x16
 * (part of this code is based on "VGAlib" by Tommy Frandsen)
 */


static VIDEO_PTR VIDEO;

static int LINE_BYTES;
static int VGA_WIDTH;
static int VGA_MAX_X;
static int VGA_HEIGHT;
static int VGA_MAX_Y;
static int VGA_DEPTH;


#define FONT_SIZE  0x2000

/* 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 */


/* 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 */


/* 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 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];


static int initialized = 0;

static char font_buf1[FONT_SIZE];  /* saved font data - plane 2 */
static char font_buf2[FONT_SIZE];  /* saved font data - plane 3 */


static void 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);
    }
}

static void initialize()
{
    int  i, j;
    int mem_fd = -1;  /* /dev/mem file descriptor		     */

    if (initialized) return;

    initialized = 1;


#if defined(__EMX__)
    _portaccess(0x300,0x3FF);
    VIDEO = (VIDEO_PTR)_memaccess(0xa0000,0xaffff,1);
#elif defined(__GNUC__)
    VIDEO = (VIDEO_PTR)0xd0000000;
#elif defined(__ZTC__)
    VIDEO = (VIDEO_PTR)_x386_mk_protected_ptr(0xa0000);
#elif defined(__WATCOMC__)
    VIDEO = (VIDEO_PTR)0xa0000;
#else
    VIDEO = (VIDEO_PTR)MK_FP(0xa000,0);
#endif


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

    /* chose registers for color/monochrome emulation */
    if (color_text) {
	CRT_I = CRT_IC;
	CRT_D = CRT_DC;
	IS1_R = IS1_RC;
    } else {
	CRT_I = CRT_IM;
	CRT_D = CRT_DM;
	IS1_R = IS1_RM;
    }

    /* 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);
    for(i = 0; i < FONT_SIZE; i++) font_buf1[i] = VIDEO[i];

    /* save font data in plane 3 */
    port_out(0x04, GRA_I);
    port_out(0x03, GRA_D);
    for(i = 0; i < FONT_SIZE; i++) font_buf2[i] = VIDEO[i];
}



void  define_color(int index, int red, int green, int blue)
{
    volatile 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/4, PEL_D);
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
    port_out(green/4, PEL_D);
    for(i = 0; i < 10; i++) ;   /* delay (minimum 240ns) */
    port_out(blue/4, PEL_D);
}

static void clear_screen(int c)
{
  register VIDEO_PTR p;
  register VIDEO_PTR last;

  /* set color c */
  port_out(c, GRA_I );
  port_out(0, GRA_D );

  /* set mode 0 */
  port_out(0x03, GRA_I );
  port_out(0, GRA_D );

  /* write to all bits */
  port_out(0x08, GRA_I );
  port_out(0xFF, GRA_D );

  last  = VIDEO + VGA_HEIGHT*LINE_BYTES;

  for(p = VIDEO; p < last; p++)  *p = 0;

}




void  init_graphics(int mode, int& width, int& height, int& depth)
{ 
  // mode = 0: Text, 1: 640x480x16 
 
    int i;

    initialize();
    
    if (mode == 0)  // 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 );
        for(i = 0; i < FONT_SIZE; i++) VIDEO[i] = font_buf1[i];

        /* restore font data in plane 3 - necessary for Trident VGA's */
    	port_out(0x02, SEQ_I );
    	port_out(0x08, SEQ_D );
        for(i = 0; i < FONT_SIZE; i++) VIDEO[i] = font_buf2[i];

        /* 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++)
            define_color(i, text_red[i], text_green[i], text_blue[i]);

        VGA_WIDTH = 80;
        VGA_HEIGHT= 25;

      }
    else // graphics mode
      { 
        /* 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);

        /* set default palette */
/*
        for(i = 0; i < 16; i++)
          define_color(i, _R[i], _G[i], _B[i]);
        clear_screen(4);
*/

        LINE_BYTES = 80;
        VGA_WIDTH = 640;
        VGA_HEIGHT= 480;
        VGA_DEPTH = 4;
        VGA_MAX_X = VGA_WIDTH - 1;
        VGA_MAX_Y = VGA_HEIGHT - 1;
      }

    /* enable video */
    port_in(IS1_R);
    port_out(0x20, ATT_IW);


  width  = VGA_WIDTH;
  height = VGA_HEIGHT;
  depth  = VGA_DEPTH;

}



void draw_pixel(int x, int y, unsigned char c)
{ 
  if (x < 0 || x > VGA_MAX_X || y < 0 || y > VGA_MAX_Y) return;

  //set_color(c);
  port_out(0x00, GRA_I );
  port_out(c, GRA_D );

  VIDEO_PTR p = VIDEO + y*LINE_BYTES + (x>>3);
  port_out(8, GRA_I);
  port_out((0x80 >> (x & 7)), GRA_D);
  *p = *p;
 }


void draw_scan_segment(unsigned char* p, int x, int y, int len)
{
  int x0 = x;
  int x1 = x + len - 1;

  if (y < 0 || y > VGA_MAX_Y || x1 < 0 || x0 > VGA_MAX_X) return;
  if (x0 < 0) 
  { p -= x0; 
    x0 = 0;
   }

  if (x1 > VGA_MAX_X) x1 = VGA_MAX_X;

  VIDEO_PTR q = VIDEO + y*LINE_BYTES + x0/8;

  for(x = x0; x <= x1; x++) 
  { // setcolor 
    int col = *p++;
    port_out(0x00, GRA_I );
    port_out(col, GRA_D );
    // draw pixel
    port_out(8, GRA_I);
    port_out((0x80 >> (x & 7)), GRA_D);
    *q = *q;
  }

}


int   check_next_event(int& val, int& x, int& y, int block)
{ return 0; }
