
#include "PicoInt.h"

static void VideoWrite(unsigned int d)
{
  struct PicoVideo *pvid=&Pico.video;
  unsigned int a=0;
  unsigned short value=(unsigned short)d;

  a=pvid->addr;
  if (a&1) d=((d<<8)&0xff00)|(d>>8); // If address is odd, bytes are swapped
  a>>=1;

  switch (pvid->type)
  {
    case 1: Pico.vram [a&0x7fff]=value; break;
    case 3: Pico.cram [a&0x003f]=value; Pico.m.dirtyPal=1; break;
    case 5: Pico.vsram[a&0x003f]=value; break;
  }
  
  // Increment address:
  pvid->addr=(unsigned short)(pvid->addr+pvid->reg[0xf]);
}

static int GetDmaSource()
{
  struct PicoVideo *pvid=&Pico.video;
  int source=0;
  source =pvid->reg[21]<<1;
  source|=pvid->reg[22]<<9;
  source|=pvid->reg[23]<<17;
  return source;
}

static int GetDmaLength()
{
  struct PicoVideo *pvid=&Pico.video;
  int len=0;
  // 16-bit words to transfer:
  len =pvid->reg[19];
  len|=pvid->reg[20]<<8;
  return len;
}

static void DmaSlow(int source,int len)
{
  int i=0; unsigned int d=0;

  for (i=0;i<len;i++)
  {
    d=PicoRead16(source);
    VideoWrite(d);
    source+=2;
  }
}

static void DmaFill(int data)
{
  int len=0,i=0;
  
  len=GetDmaLength();

  for (i=0;i<len+1;i++) VideoWrite(data);
}

static void CommandDma()
{
  struct PicoVideo *pvid=&Pico.video;
  int len=0,method=0,source=0;

  if ((pvid->reg[1]&0x10)==0) return; // DMA not enabled

  len=GetDmaLength();

  method=pvid->reg[23]>>6;
  if (method<2)
  {
    // 68000 to VDP
    source=GetDmaSource();
    if (source>=0x800000 && source<0xe00000) return; // Invalid source address

    DmaSlow(source,len);
  }
}

static void CommandChange()
{
  struct PicoVideo *pvid=&Pico.video;
  unsigned int cmd=0,addr=0;

  cmd=pvid->command;

  // Get type of transfer 0xc0000030 (v/c/vsram read/write)
  pvid->type=(unsigned char)(((cmd>>2)&0xc)|(cmd>>30));

  // Get address 0x3fff0003
  addr =(cmd>>16)&0x3fff;
  addr|=(cmd<<14)&0xc000;
  pvid->addr=(unsigned short)addr;

  // Check for dma:
  if (cmd&0x80) CommandDma();
}

void PicoVideoWrite(unsigned int a,unsigned int d)
{
  unsigned int ma=a&0xfffffc;
  struct PicoVideo *pvid=&Pico.video;

  d=(unsigned short)d;

  if (ma==0xc00000) // Data port 0 or 2
  {    
    if (pvid->pending) CommandChange();
    pvid->pending=0;

    // If a DMA fill has been set up, do it
    if ((pvid->command&0x80) && (pvid->reg[1]&0x10) && (pvid->reg[23]>>6)==2)
    {
      DmaFill(d);
    }
    else
    {
      VideoWrite(d);
    }
    return;
  }

  if (ma==0xc00004) // Command port 4 or 6
  {
    if (pvid->pending)
    {
      // Low word of command:
      pvid->command&=0xffff0000;
      pvid->command|=d;
      pvid->pending=0;
      CommandChange();
      return;
    }

    if ((d&0xc000)==0x8000)
    {
      // Register write:
      int num=(d>>8)&0x1f;
      pvid->reg[num]=(unsigned char)d;
      return;
    }

    // High word of command:
    pvid->command&=0x0000ffff;
    pvid->command|=d<<16;
    pvid->pending=1;
  }
}
