/***************************************************************************************
 *  Genesis Plus
 *  Z80 bank access to 68k bus
 *
 *  Copyright (C) 1998-2003  Charles Mac Donald (original code)
 *  Copyright (C) 2007-2020  Eke-Eke (Genesis Plus GX)
 *
 *  Redistribution and use of this code or any derivative works are permitted
 *  provided that the following conditions are met:
 *
 *   - Redistributions may not be sold, nor may they be used in a commercial
 *     product or activity.
 *
 *   - Redistributions that are modified from the original source must include the
 *     complete source code, including the source code for all components used by a
 *     binary built from the modified sources. However, as a special exception, the
 *     source code distributed need not include anything that is normally distributed
 *     (in either source or binary form) with the major components (compiler, kernel,
 *     and so on) of the operating system on which the executable runs, unless that
 *     component itself accompanies the executable.
 *
 *   - Redistributions must reproduce the above copyright notice, this list of
 *     conditions and the following disclaimer in the documentation and/or other
 *     materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 *
 ****************************************************************************************/

#include "shared.h"


t_zbank_memory_map zbank_memory_map[256];

/*
  Handlers for access to unused addresses and those which make the
  machine lock up.
*/

unsigned int zbank_unused_r(unsigned int address)
{
#ifdef LOGERROR
  error("Z80 bank unused read %06X (%x)\n", address, Z80.pc.d);
#endif
  return 0xFF;
}

void zbank_unused_w(unsigned int address, unsigned int data)
{
#ifdef LOGERROR
  error("Z80 bank unused write %06X = %02X (%x)\n", address, data, Z80.pc.d);
#endif
}

unsigned int zbank_lockup_r(unsigned int address)
{
#ifdef LOGERROR
  error("Z80 bank lockup read %06X (%x)\n", address, Z80.pc.d);
#endif
  if (!config.force_dtack)
  {
    Z80.cycles = 0xFFFFFFFF;
    zstate = 0;
  }
  return 0xFF;
}

void zbank_lockup_w(unsigned int address, unsigned int data)
{
#ifdef LOGERROR
  error("Z80 bank lockup write %06X = %02X (%x)\n", address, data, Z80.pc.d);
#endif
  if (!config.force_dtack)
  {
    Z80.cycles = 0xFFFFFFFF;
    zstate = 0;
  }
}

/* I/O & Control registers */
unsigned int zbank_read_ctrl_io(unsigned int address)
{
  switch ((address >> 8) & 0xFF)
  {
    case 0x00:  /* I/O chip */
    {
      if (!(address & 0xE0))
      {
        return (io_68k_read((address >> 1) & 0x0F));
      }
      return zbank_unused_r(address);
    }

    case 0x11:  /* BUSACK */
    {
      if (address & 1)
      {
        return zbank_unused_r(address);
      }
      return 0xFF;
    }

    case 0x30:  /* TIME */
    {
      if (cart.hw.time_r)
      {
        unsigned int data = cart.hw.time_r(address);
        if (address & 1)
        {
          return (data & 0xFF);
        }
        return (data >> 8);
      }
      return zbank_unused_r(address);
    }

    case 0x41:  /* OS ROM */
    {
      if (address & 1)
      {
        return (gen_bankswitch_r() | 0xFE);
      }
      return zbank_unused_r(address);
    }

    case 0x10:  /* MEMORY MODE */
    case 0x12:  /* RESET */
    case 0x20:  /* MEGA-CD */
    case 0x40:  /* TMSS */
    case 0x44:  /* RADICA */
    case 0x50:  /* SVP REGISTERS */
    {
      return zbank_unused_r(address);
    }

    default:  /* Invalid address */
    {
      return zbank_lockup_r(address);
    }
  }
}

void zbank_write_ctrl_io(unsigned int address, unsigned int data)
{
  switch ((address >> 8) & 0xFF)
  {
    case 0x00:  /* I/O chip */
    {
      /* get /LWR only */
      if ((address & 0xE1) == 0x01)
      {
        io_68k_write((address >> 1) & 0x0F, data);
        return;
      }
      zbank_unused_w(address, data);
      return;
    }

    case 0x11:  /* BUSREQ */
    {
      if (!(address & 1))
      {
        gen_zbusreq_w(data & 1, Z80.cycles);
        return;
      }
      zbank_unused_w(address, data);
      return;
    }

    case 0x12:  /* RESET */
    {
      if (!(address & 1))
      {
        gen_zreset_w(data & 1, Z80.cycles);
        return;
      }
      zbank_unused_w(address, data);
      return;
    }

    case 0x30:  /* TIME */
    {
      cart.hw.time_w(address, data);
      return;
    }

    case 0x41:  /* OS ROM */
    {
      if ((config.bios & 1) && (address & 1))
      {
        gen_bankswitch_w(data & 1);
        return;
      }
      zbank_unused_w(address, data);
      return;
    }

    case 0x10:  /* MEMORY MODE */
    case 0x20:  /* MEGA-CD */
    case 0x40:  /* TMSS */
    case 0x44:  /* RADICA */
    case 0x50:  /* SVP REGISTERS */
    {
      zbank_unused_w(address, data);
      return;
    }

    default:  /* Invalid address */
    {
      zbank_lockup_w(address, data);
      return;
    }
  }
}


/* VDP */
unsigned int zbank_read_vdp(unsigned int address)
{
  switch (address & 0xFD)
  {
    case 0x00:    /* DATA */
    {
      return (vdp_68k_data_r() >> 8);
    }
      
    case 0x01:    /* DATA */
    {
      return (vdp_68k_data_r() & 0xFF);
    }
      
    case 0x04:    /* CTRL */
    {
      return (((vdp_68k_ctrl_r(Z80.cycles) >> 8) & 3) | 0xFC);
    }

    case 0x05:    /* CTRL */
    {
      return (vdp_68k_ctrl_r(Z80.cycles) & 0xFF);
    }
      
    case 0x08:    /* HVC */
    case 0x0C:
    {
      return (vdp_hvc_r(Z80.cycles) >> 8);
    }

    case 0x09:    /* HVC */
    case 0x0D:
    {
      return (vdp_hvc_r(Z80.cycles) & 0xFF);
    }

    case 0x18:    /* Unused */
    case 0x19:
    case 0x1C:
    case 0x1D:
    {
      return zbank_unused_r(address);
    }

    default:    /* Invalid address */
    {
      return zbank_lockup_r(address);
    }
  }
}

void zbank_write_vdp(unsigned int address, unsigned int data)
{
  switch (address & 0xFC)
  {
    case 0x00:  /* Data port */
    {
      vdp_68k_data_w(data << 8 | data);
      return;
    }

    case 0x04:  /* Control port */
    {
      vdp_68k_ctrl_w(data << 8 | data);
      return;
    }

    case 0x10:  /* PSG */
    case 0x14:
    {
      if (address & 1)
      {
        psg_write(Z80.cycles, data);
        return;
      }
      zbank_unused_w(address, data);
      return;
    }
             
    case 0x18: /* Unused */
    {
      zbank_unused_w(address, data);
      return;
    }

    case 0x1C:  /* TEST register */
    {
      vdp_test_w(data << 8 | data);
      return;
    }

    default:  /* Invalid address */
    {
      zbank_lockup_w(address, data);
      return;
    }
  }
}
