/***************************************************************************

  vidhrdw.c

  Functions to emulate the video hardware of the machine.

***************************************************************************/

#include "driver.h"
#include "vidhrdw/generic.h"

static struct osd_bitmap *polybitmap1,*polybitmap2,*charbitmap;
static struct osd_bitmap *polybitmap;
int irvg_running;

static unsigned char inverse_palette[256];

unsigned char *comRAM1,*comRAM2,*mbRAM, *mbROM;
static int ir_xmin, ir_ymin, ir_xmax, ir_ymax; /* clipping area */

extern int irmb_running;
extern unsigned char irobot_comswap;
extern unsigned char irobot_bufsel;
extern unsigned char irobot_alphamap;



/***************************************************************************

  Convert the color PROMs into a more useable format.

  5 bits from polygon ram address the palette ram

  Output of color RAM
  bit 8 -- inverter -- 1K ohm resistor  -- RED
  bit 7 -- inverter -- 2.2K ohm resistor  -- RED
        -- inverter -- 1K ohm resistor  -- GREEN
        -- inverter -- 2.2K ohm resistor  -- GREEN
        -- inverter -- 1K ohm resistor  -- BLUE
        -- inverter -- 2.2K ohm resistor  -- BLUE
        -- inverter -- 2.2K ohm resistor  -- INT
        -- inverter -- 4.7K ohm resistor  -- INT
  bit 0 -- inverter -- 9.1K ohm resistor  -- INT

  Alphanumeric colors are generated by ROM .125, it's outputs are connected
  to bits 1..8 as above. The inputs are:

  A0..1 - Character color
  A2    - Character image (1=pixel on/0=off)
  A3..4 - Alphamap 0,1 (appears that only Alphamap1 is used, it is set by
          the processor)

***************************************************************************/
void irobot_vh_convert_color_prom(unsigned char *palette, unsigned short *colortable,const unsigned char *color_prom)
{
	int i;
	#define TOTAL_COLORS(gfxn) (Machine->gfx[gfxn]->total_colors * Machine->gfx[gfxn]->color_granularity)
	#define COLOR(gfxn,offs) (colortable[Machine->drv->gfxdecodeinfo[gfxn].color_codes_start + offs])

	/* the palette will be initialized by the game. We just set it to some */
	/* pre-cooked values so the startup copyright notice can be displayed. */
	for (i = 0;i < 64;i++)
	{
		*(palette++) = ((i & 1) >> 0) * 0xff;
		*(palette++) = ((i & 2) >> 1) * 0xff;
		*(palette++) = ((i & 4) >> 2) * 0xff;
	}

	/* Convert the color prom for the text palette */
	for (i = 0;i < 32;i++)
	{
	    int r,g,b;
		int bits,intensity;
	    unsigned int color;

	    color = *color_prom;
	    intensity = color & 0x03;
	    bits = (color >> 6) & 0x03;
	    r = 16 * bits * intensity;
	    bits = (color >> 4) & 0x03;
	    g = 16 * bits * intensity;
	    bits = (color >> 2) & 0x03;
	    b = 16 * bits * intensity;
		*(palette++) = r;
		*(palette++) = g;
		*(palette++) = b;
		color_prom++;
	}


	/* polygons */
    for (i = 0;i < 64;i++)
         colortable[i] = i;

	/* text */
    for (i = 0;i < TOTAL_COLORS(0);i++)
	{
		COLOR(0,i) = ((i & 0x18) | ((i & 0x01) << 2) | ((i & 0x06) >> 1)) + 64;
	}
}

void irobot_paletteram_w(int offset,int data)
{
    int r,g,b;
	int bits,intensity;
    unsigned int color;

    color = (data << 1) | (offset & 0x01);
    intensity = color & 0x07;
    bits = (color >> 3) & 0x03;
    r = 8 * bits * intensity;
    bits = (color >> 5) & 0x03;
    g = 8 * bits * intensity;
    bits = (color >> 7) & 0x03;
    b = 8 * bits * intensity;
    palette_change_color((offset >> 1) & 0x3F,r,g,b);
}


/***************************************************************************

  Start the video hardware emulation.

***************************************************************************/
int irobot_vh_start(void)
{
    int i;

    /* Setup 2 bitmaps for the polygon generator */
    if ((polybitmap1 = osd_create_bitmap(Machine->drv->screen_width,Machine->drv->screen_height)) == 0)
		return 1;
    if ((polybitmap2 = osd_create_bitmap(Machine->drv->screen_width,Machine->drv->screen_height)) == 0)
		return 1;

    /* Character plane bitmaps */
	if ((charbitmap = osd_create_bitmap(Machine->drv->screen_width,Machine->drv->screen_height)) == 0)
	{
        osd_free_bitmap(polybitmap1);
        osd_free_bitmap(polybitmap2);
		return 1;
	}

	if ((dirtybuffer = malloc(videoram_size)) == 0)
	{
		osd_free_bitmap(charbitmap);
        osd_free_bitmap(polybitmap1);
        osd_free_bitmap(polybitmap2);
		return 1;
    }
	memset(dirtybuffer,1,videoram_size);

    /* Set clipping */
    ir_xmin=ir_ymin=0;
    ir_xmax = Machine->drv->screen_width;
    ir_ymax = Machine->drv->screen_height;
    irvg_running=0;

    /* Allocate other memory areas */
    comRAM1 = (unsigned char *)malloc(0x1000);
    comRAM2 = (unsigned char *)malloc(0x1000);
    mbRAM = (unsigned char *)malloc(0x2000);
    mbROM = (unsigned char *)malloc(0xC000);
    if (!comRAM1 || !comRAM2 || !mbRAM || !mbROM) {
        if (errorlog) fprintf(errorlog,"MALLOC ERROR\n");
        return 1;
    }

    for (i = 0;i < Machine->drv->total_colors;i++)
        inverse_palette[Machine->pens[i]] = i;

	return 0;
}

/***************************************************************************

  Stop the video hardware emulation.

***************************************************************************/
void irobot_vh_stop(void)
{
    free(comRAM1);
    free(comRAM2);
    free(mbRAM);
    free(mbROM);
    free(dirtybuffer);
    osd_free_bitmap(charbitmap);
	osd_free_bitmap(tmpbitmap);
}

/***************************************************************************

    Polygon Generator  (Preliminary information)
    The polygon communication ram works as follows (each location is a 16-bit word):

    0000-xxxx: Object pointer table
        bits 00..10: Address of object data
        bits 12..15: Object type
            0x4 = Polygon
            0x8 = Point
            0xC = Vector
        (0xFFFF means end of table)

   Point Object:
        Word 0, bits 8..15: X Position  (0xFFFF = end of point objects)
        Word 1, bits 8..15: Y Position
                bits 0..5: Color

    Vector Object:
        Word 0, bits 8..15: Ending Y   (0xFFFF = end of line objects)
        Word 1, bits 8..15: Starting Y
                bits 0..5: Color
        Word 2: Slope
        Word 3, bits 8..15: Starting X

    Polygon Object:
        Word 0, bits 0..10: Pointer to second half of polygon
        Word 1, bits 8..15: Starting X first half
        Word 2, bits 8..15: Starting X second half
        Word 4, bits 0..5: Color

        For each side:
            Word 0, Slope (0xFFFF = side done)
            Word 1, bits 8..15: Ending Y

        Each side is a continous set of vectors. Both sides are drawn at
        the same time and the space between them is filled in.

***************************************************************************/

void irobot_poly_clear(void) {
    if (irobot_bufsel)
        osd_clearbitmap(polybitmap2);
    else
        osd_clearbitmap(polybitmap1);
}

INLINE void irobot_draw_pixel (int x, int y, int col)
{

    if (x < ir_xmin || x >= ir_xmax)
		return;
    if (y < ir_ymin || y >= ir_ymax)
		return;

    polybitmap->line[y][x] = Machine->pens[col];
}

/*
     Line draw routine
     modified from a routine written by Andrew Caldwell
 */

void irobot_draw_line (int x1, int y1, int x2, int y2, int col)
{
    int dx,dy,sx,sy,cx,cy;

    dx = abs(x1-x2);
    dy = abs(y1-y2);
    sx = (x1 <= x2) ? 1: -1;
    sy = (y1 <= y2) ? 1: -1;
    cx = dx/2;
    cy = dy/2;

    if (dx>=dy)
    {
        for (;;)
        {
             irobot_draw_pixel (x1, y1, col);
             if (x1 == x2) break;
             x1 += sx;
             cx -= dy;
             if (cx < 0)
             {
                  y1 += sy;
                  cx += dx;
             }
        }
    }
    else
    {
        for (;;)
        {
            irobot_draw_pixel (x1, y1, col);
            if (y1 == y2) break;
            y1 += sy;
            cy -= dx;
            if (cy < 0)
            {
                 x1 += sx;
                 cy += dy;
             }
        }
    }
}

void run_video(void) {
    int sx,sy,ex,ey,sx2,sy2,ex2,ey2;
    int dx,color;
    unsigned int d1,d2;
    unsigned char *comRAM;
	int lpnt,spnt,spnt2;
	int shp;

    if (errorlog) fprintf(errorlog,"Starting Polygon Generator\n");
    if (!irobot_comswap) {
		comRAM = comRAM1;
	}
	else {
		comRAM = comRAM2;
	}

   if (irobot_bufsel)
        polybitmap = polybitmap2;
   else
        polybitmap = polybitmap1;

	lpnt=0;
	while (lpnt < 0xFFF) {
		d1 = (comRAM[lpnt] << 8) | comRAM[lpnt+1];
		lpnt+=2;
		if (d1 == 0xFFFF) break;
		spnt = (d1 & 0x07FF) << 1;
		shp = (d1 & 0xF000) >> 12;

/* Pixel */
        if (shp == 0x8) {
        if (errorlog) fprintf(errorlog,"Draw Pixel\n");

			while (spnt < 0xFFE) {
				if (((comRAM[spnt] << 8) | comRAM[spnt+1]) == 0xFFFF) break;
				sx = comRAM[spnt];
				sy = comRAM[spnt+2];
                color = comRAM[spnt+3] & 0x3F;
                if (errorlog) fprintf(errorlog,"Pixel: X=%x,Y=%x,C=%x\n",sx,sy,color);
                irobot_draw_pixel(sx,sy,color);
				spnt+=4;
			}
		}

/* Line */
		if (shp == 0xC) {

			while (spnt < 0xFFF) {
				if (((comRAM[spnt] << 8) | comRAM[spnt+1]) == 0xFFFF) break;
				ey = comRAM[spnt];
				sy = comRAM[spnt + 2];
				sx = comRAM[spnt + 6];
				d2 = (comRAM[spnt + 4] << 8) | comRAM[spnt+5];
				if (d2 & 0x8000) {
				 	d2 = (d2 ^ 0xFFFF) & 0x7FFF;
					if ((d2 & 0xFF) == 0xFF)
						dx = (d2 & 0xFF00) >> 8;
					else
						dx = (d2 * (ey - sy)) / 128;
					ex = sx - dx;
				}
				else {
					d2 = d2 & 0x7FFF;
					if ((d2 & 0xFF) == 0xFF)
						dx = (d2 & 0xFF00) >> 8;
					else
						dx = (d2 * (ey - sy)) / 128;
					ex = sx + dx;
				}
                color = comRAM[spnt+3] & 0x3F;
                irobot_draw_line(sx,sy,ex,ey,color+1);
                if (errorlog) fprintf(errorlog,"%x,%x-%x,%x:%x\n",sx,sy,ex,ey,color);
				spnt+=8;
			}
		}

/* Polygon */
		if (shp == 0x4) {
    if (errorlog) fprintf(errorlog,"Draw Poly\n");


			spnt2 = (comRAM[spnt] << 8) | (comRAM[spnt+1]);
			spnt2 = (spnt2 & 0x7FF) << 1;

			sx = comRAM[spnt+2];
			sx2 = comRAM[spnt+4];
			sy = sy2 = comRAM[spnt+6];
            color = comRAM[spnt+7] & 0x3F;
			spnt+=8;
			while (spnt < 0xFFF ) {
				if (((comRAM[spnt] << 8) | comRAM[spnt+1]) == 0xFFFF) break;
				d2 = (comRAM[spnt] << 8) | comRAM[spnt+1];
				ey = (comRAM[spnt + 2]);
				if (d2 & 0x8000) {
				 	d2 = (d2 ^ 0xFFFF) & 0x7FFF;
					if ((d2 & 0xFF) == 0xFF)
						dx = (d2 & 0xFF00) >> 8;
					else
						dx = (d2 * (ey - sy)) / 128;
					ex = sx - dx;
				}
				else {
					d2 = d2 & 0x7FFF;
					if ((d2 & 0xFF) == 0xFF)
						dx = (d2 & 0xFF00) >> 8;
					else
						dx = (d2 * (ey - sy)) / 128;
					ex = sx + dx;
				}
                irobot_draw_line(sx,sy,ex,ey,color);
				sx=ex;
				sy=ey;
				spnt+=4;

				d2 = (comRAM[spnt2] << 8) | comRAM[spnt2+1];
				ey2 = (comRAM[spnt2 + 2]);
				if (d2 & 0x8000) {
				 	d2 = (d2 ^ 0xFFFF) & 0x7FFF;
					if ((d2 & 0xFF) == 0xFF)
						dx = (d2 & 0xFF00) >> 8;
					else
						dx = (d2 * (ey2 - sy2)) / 128;
					ex2 = sx2 - dx;
				}
				else {
					d2 = d2 & 0x7FFF;
					if ((d2 & 0xFF) == 0xFF)
						dx = (d2 & 0xFF00) >> 8;
					else
						dx = (d2 * (ey2 - sy2)) / 128;
					ex2 = sx2 + dx;
				}
                irobot_draw_line(sx2,sy2,ex2,ey2 ,color);
				sx2=ex2;
				sy2=ey2;
				spnt2+=4;
			}
		}
	}
}



/***************************************************************************

  Draw the game screen in the given osd_bitmap.
  Do NOT call osd_update_display() from this function, it will be called by
  the main emulation engine.

***************************************************************************/
void irobot_vh_screenrefresh(struct osd_bitmap *bitmap,int full_refresh)
{
        int offs;
        int c,cl;

	/* for every character in the Video RAM, check if it has been modified */
	/* since last time and update it accordingly. */
	for (offs = videoram_size - 1;offs >= 0;offs--)
	{
		if (dirtybuffer[offs])
		{
        int sx,sy;

        sx = offs % 32;
        sy = offs / 32;
        c = videoram[offs] & 0x3F;
        cl = (videoram[offs] & 0xC0) >> 6;
			drawgfx(charbitmap,Machine->gfx[0],
                    c,cl,
					0,0,
					8*sx,8*sy,
					&Machine->drv->visible_area,TRANSPARENCY_NONE,0);
        }
	}


	/* copy the temporary bitmap to the screen */
        copybitmap(bitmap,charbitmap,0,0,0,0,&Machine->drv->visible_area,TRANSPARENCY_NONE,0);

    if (irobot_bufsel)
        copybitmap(bitmap,polybitmap2,0,0,0,0,&Machine->drv->visible_area,TRANSPARENCY_PEN,0);
    else
        copybitmap(bitmap,polybitmap1,0,0,0,0,&Machine->drv->visible_area,TRANSPARENCY_PEN,0);



}
