/* FCE Ultra - NES/Famicom Emulator
 *
 * Copyright notice for this file:
 *  Copyright (C) 1998 \Firebug\
 *  Copyright (C) 2002 Xodnizel
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <stdio.h>
#include <string.h>
#include <vga.h>
#include <sys/io.h>

#define inportb inb
#define outportb(port, value) outb(value, port)
#define outportw(port, value) outw(value, port)

#include "main.h"
#include "svgalib.h"
#include "svga-video.h"


int vmode=1;

static int vidready=0;
static int conlock=0;

void LockConsole(void)
{
 if(!conlock)
 {
  vga_lockvc();
  conlock=1;
  FCEUI_DispMessage("Console locked.");
 }
}

void UnlockConsole(void)
{
 if(conlock)
 {
  vga_unlockvc();
  conlock=0;
  FCEUI_DispMessage("Console unlocked.");
 }
}

void SetBorder(void)
{
 if(!conlock) 
  vga_lockvc();
 inportb(0x3da);
 outportb(0x3c0,(0x11|0x20));
 outportb(0x3c0,0x80);
 if(!conlock) 
  vga_unlockvc();
}

#include "vgatweak.c"

void TweakVGA(int VGAMode)
{
  int I;

  if(!conlock)
   vga_lockvc();

  outportb(0x3C8,0x00);
  for(I=0;I<768;I++) outportb(0x3C9,0x00);

  outportb(0x3D4,0x11);
  I=inportb(0x3D5)&0x7F;
  outportb(0x3D4,0x11);
  outportb(0x3D5,I);

  switch(VGAMode)
  {
    case 1:  for(I=0;I<25;I++) VGAPortSet(v256x240[I]);break;
    case 2:  for(I=0;I<25;I++) VGAPortSet(v256x256[I]);break;
    case 3:  for(I=0;I<25;I++) VGAPortSet(v256x256S[I]);break;
    case 6:  for(I=0;I<25;I++) VGAPortSet(v256x224S[I]);break;
    case 8:  for(I=0;I<25;I++) VGAPortSet(v256x224_103[I]);break;
    default: break;
  }

  outportb(0x3da,0);
  if(!conlock) 
   vga_unlockvc();
}


static uint8 palettedbr[256],palettedbg[256],palettedbb[256];

static void FlushPalette(void)
{
 int x;
 for(x=0;x<256;x++)
 {
  int z=x;
  if(vmode==4 || vmode==5 || vmode==7) z^=0x80;
  vga_setpalette(z,palettedbr[x]>>2,palettedbg[x]>>2,palettedbb[x]>>2);
 }
}

void FCEUD_SetPalette(uint8 index, uint8 r, uint8 g, uint8 b)
{
  palettedbr[index]=r;
  palettedbg[index]=g;
  palettedbb[index]=b;

  if(vidready)
  {
   if(vmode==4 || vmode==5 || vmode==7) index^=0x80;
   vga_setpalette(index,r>>2,g>>2,b>>2);
  }
}


void FCEUD_GetPalette(uint8 i, uint8 *r, uint8 *g, uint8 *b)
{
 *r=palettedbr[i];
 *g=palettedbg[i];
 *b=palettedbb[i];
}

static void vcfix(void)
{
 int z;

 if(!conlock)
  vga_lockvc();
 z=inportb(0x3cc);
 if(!conlock) 
  vga_unlockvc();
 if(z!=0xe3 && z!=0xe7)		// Common value in all tweaked video modes(and not in 320x200 mode).
 {
  TweakVGA(vmode);
  SetBorder();
  FlushPalette();
 }
}

static uint8 *ScreenLoc;

int InitVideo(void)
{
 #ifdef DUMMY
 return(1);
 #endif
 vidready=0;

 if(vmode<=3 || vmode==6 || vmode==8)
 {
  if(vga_getcurrentchipset()==FBDEV)
  {
   puts("Tweaked VGA video modes will not work.  Using a 320x240 video mode instead...");
   vmode=7;
  }
 }

 switch(vmode)
 {
  default:
  case 1:
  case 2:
  case 3:
  case 6:
  case 8:
	 vga_setmode(G320x200x256);
	 vidready|=1;
         ScreenLoc=vga_getgraphmem();
         TweakVGA(vmode);
         SetBorder();
         memset(ScreenLoc,128,256*256);
         break;
  case 4:
  case 5:
         if(!(vga_getmodeinfo(G640x480x256)->flags & CAPABLE_LINEAR))
         {
          puts("Video:  No linear addressing mode available!");
          return 0;
         }
         if(vga_setmode(G640x480x256)==-1)
         {
          puts("Video:  Could not set 640x480x8bpp video mode!");
          return 0;
         }
	 vidready|=1;

         vga_setpage(0);
         if(vga_setlinearaddressing()!=-1)
          ScreenLoc=vga_getgraphmem();
         else
         {
          puts("Video:  Could not set linear addressing!");
          return 0;
         }
         memset(ScreenLoc,0,640*480);
         break;
  case 7:
         if(!(vga_getmodeinfo(G320x240x256V)->flags & CAPABLE_LINEAR))
         {
          puts("Video:  No linear addressing mode available!");
          return 0;
         }
         if(vga_setmode(G320x240x256V)==-1)
         {
          puts("Video:  Could not set 320x240x8bpp video mode!");
          return 0;
         }
         vidready|=1;

         vga_setpage(0);
         if(vga_setlinearaddressing()!=-1)
          ScreenLoc=vga_getgraphmem();
         else
         {
          puts("Video:  Could not set linear addressing!");
          return 0;
         }
         memset(ScreenLoc,0,320*240);
         break;
 }
 vidready|=2;
 FlushPalette(); // Needed for cheat console code(and it isn't a bad thing to do anyway...).
 return 1;
}

void KillVideo(void)
{
 if(vidready)
 {
  vga_setmode(TEXT);
  vidready=0;
 }
}


void BlitScreen(uint8 *XBuf)
{
 static int conto=0;
 uint8 *dest;
 int tlines;
 #ifdef DUMMY
 return;
 #endif

 if(doptions&DO_VSYNC && !NoWaiting)
 {
  vga_waitretrace();
 }

 tlines=erendline-srendline+1;

 dest=ScreenLoc;

 if(vmode!=4 && vmode!=5 && vmode!=7)
 {
  conto=(conto+1)&0x3F;
  if(!conto) vcfix();
 }
 switch(vmode)
 {
  case 1:dest+=(((240-tlines)>>1)<<8);break;
  case 2:
  case 3:dest+=(((256-tlines)>>1)<<8);break;
  case 4:
  case 5:dest+=(((240-tlines)>>1)*640+((640-512)>>1));break;
  case 8:
  case 6:if(tlines>224) tlines=224;dest+=(((224-tlines)>>1)<<8);break;
  case 7:dest+=(((240-tlines)>>1)*320)+32;break;
 }

 XBuf+=(srendline<<8)+(srendline<<4);

 if(eoptions&EO_CLIPSIDES)
 {
  if(vmode==5)
  {
   asm volatile(
     "xorl %%edx, %%edx\n\t"
     "ckoop1:\n\t"
     "movb $120,%%al     \n\t"
     "ckoop2:\n\t"
     "movb 1(%%esi),%%dl\n\t"
     "shl  $16,%%edx\n\t"
     "movb (%%esi),%%dl\n\t"
     "xorl $0x00800080,%%edx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "addl $2,%%esi\n\t"
     "addl $4,%%edi\n\t"
     "decb %%al\n\t"
     "jne ckoop2\n\t"

     "addl $32,%%esi\n\t"
     "addl $800,%%edi\n\t"
     "decb %%bl\n\t"
     "jne ckoop1\n\t"
     :
     : "S" (XBuf+8), "D" (dest+8), "b" (tlines)
     : "%al", "%edx", "%cc" );
  }
  else if(vmode==4)
  {
   asm volatile(
     "cyoop1:\n\t"
     "movb $120,%%al     \n\t"
     "cyoop2:\n\t"
     "movb 1(%%esi),%%dh\n\t"
     "movb %%dh,%%dl\n\t"
     "shl  $16,%%edx\n\t"
     "movb (%%esi),%%dl\n\t"
     "movb %%dl,%%dh\n\t"               // Ugh
     "xorl $0x80808080,%%edx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "addl $2,%%esi\n\t"
     "addl $4,%%edi\n\t"
     "decb %%al\n\t"
     "jne cyoop2\n\t"

     "addl $32,%%esi\n\t"
     "addl $800,%%edi\n\t"
     "decb %%bl\n\t"
     "jne cyoop1\n\t"
     :
     : "S" (XBuf+8), "D" (dest+8), "b" (tlines)
     : "%al", "%edx", "%cc" );
  }
  else if(vmode==7)
  {
   asm volatile(
     "cgoop81:\n\t"
     "movl $30,%%eax\n\t"
     "cgoop82:\n\t"
     "movl (%%esi),%%edx\n\t"
     "movl 4(%%esi),%%ecx\n\t"
     "xorl $0x80808080,%%edx\n\t"
     "xorl $0x80808080,%%ecx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "movl %%ecx,4(%%edi)\n\t"
     "addl $8,%%esi\n\t"
     "addl $8,%%edi\n\t"
     "decl %%eax\n\t"
     "jne cgoop82\n\t"
     "addl $80,%%edi\n\t"
     "addl $32,%%esi\n\t"
     "decb %%bl\n\t"
     "jne cgoop81\n\t"
     :
     : "S" (XBuf+8), "D" (dest+8), "b" (tlines)
     : "%eax","%cc","%edx","%ecx" );
  }
  else
  {
   asm volatile(
     "cgoop1:\n\t"
     "movl $30,%%eax\n\t"
     "cgoop2:\n\t"
     "movl (%%esi),%%edx\n\t"
     "movl 4(%%esi),%%ecx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "movl %%ecx,4(%%edi)\n\t"
     "addl $8,%%esi\n\t"
     "addl $8,%%edi\n\t"
     "decl %%eax\n\t"
     "jne cgoop2\n\t"
     "addl $32,%%esi\n\t"
     "addl $16,%%edi\n\t"
     "decb %%bl\n\t"
     "jne cgoop1\n\t"
     :
     : "S" (XBuf+8), "D" (dest+8), "b" (tlines)
     : "%eax","%cc","%edx","%ecx" );
  }
 }
 else
 {
  if(vmode==5)
  {
   asm volatile(
     "xorl %%edx, %%edx\n\t"
     "koop1:\n\t"
     "movb $128,%%al     \n\t"
     "koop2:\n\t"
     "movb 1(%%esi),%%dl\n\t"
     "shl  $16,%%edx\n\t"
     "movb (%%esi),%%dl\n\t"
     "xorl $0x00800080,%%edx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "addl $2,%%esi\n\t"
     "addl $4,%%edi\n\t"
     "decb %%al\n\t"
     "jne koop2\n\t"

     "addl $16,%%esi\n\t"
     "addl $768,%%edi\n\t"
     "decb %%bl\n\t"
     "jne koop1\n\t"
     :
     : "S" (XBuf), "D" (dest), "b" (tlines)
     : "%al", "%edx", "%cc" );
  }
  else if(vmode==4)
  {
   asm volatile(
     "yoop1:\n\t"
     "movb $128,%%al     \n\t"
     "yoop2:\n\t"
     "movb 1(%%esi),%%dh\n\t"
     "movb %%dh,%%dl\n\t"
     "shl  $16,%%edx\n\t"
     "movb (%%esi),%%dl\n\t"
     "movb %%dl,%%dh\n\t"               // Ugh
     "xorl $0x80808080,%%edx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "addl $2,%%esi\n\t"
     "addl $4,%%edi\n\t"
     "decb %%al\n\t"
     "jne yoop2\n\t"

     "addl $16,%%esi\n\t"
     "addl $768,%%edi\n\t"
     "decb %%bl\n\t"
     "jne yoop1\n\t"
     :
     : "S" (XBuf), "D" (dest), "b" (tlines)
     : "%al", "%edx", "%cc" );
  }
  else if(vmode==7)
  {
   asm volatile(
     "goop81:\n\t"
     "movl $32,%%eax\n\t"
     "goop82:\n\t"
     "movl (%%esi),%%edx\n\t"
     "movl 4(%%esi),%%ecx\n\t"
     "xorl $0x80808080,%%edx\n\t"
     "xorl $0x80808080,%%ecx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "movl %%ecx,4(%%edi)\n\t"
     "addl $8,%%esi\n\t"
     "addl $8,%%edi\n\t"
     "decl %%eax\n\t"
     "jne goop82\n\t"
     "addl $64,%%edi\n\t"
     "addl $16,%%esi\n\t"
     "decb %%bl\n\t"
     "jne goop81\n\t"
     :
     : "S" (XBuf), "D" (dest), "b" (tlines)
     : "%eax","%cc","%edx","%ecx" );
  }
  else
  {
   asm volatile(
     "goop1:\n\t"
     "movl $32,%%eax\n\t"
     "goop2:\n\t"
     "movl (%%esi),%%edx\n\t"
     "movl 4(%%esi),%%ecx\n\t"
     "movl %%edx,(%%edi)\n\t"
     "movl %%ecx,4(%%edi)\n\t"
     "addl $8,%%esi\n\t"
     "addl $8,%%edi\n\t"
     "decl %%eax\n\t"
     "jne goop2\n\t"
     "addl $16,%%esi\n\t"
     "decb %%bl\n\t"
     "jne goop1\n\t"
     :
     : "S" (XBuf), "D" (dest), "b" (tlines)
     : "%eax","%cc","%edx","%ecx" );
  }
 }
}


