/*
    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Charles Mac Donald

    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
*/
/*
    Memory handlers Z80 access to the banked V-bus address space.
*/

#include "shared.h"

/*
    Handlers for access to unused addresses and those which make the
    machine lock up.
*/
static inline void z80bank_unused_w(unsigned int address, unsigned int data)
{
#ifdef LOGERROR
    error("Z80 bank unused write %06X = %02X\n", address, data);
#endif
}

static inline unsigned int z80bank_unused_r(unsigned int address)
{
#ifdef LOGERROR
    error("Z80 bank unused read %06X\n", address);
#endif
	return (address & 1) ? 0x00 : 0xFF;
}

static inline void z80bank_lockup_w(unsigned int address, unsigned int data)
{
#ifdef LOGERROR
    error("Z80 bank lockup write %06X = %02X\n", address, data);
#endif
 	gen_running = force_dtack;
}

static inline unsigned int z80bank_lockup_r(unsigned int address)
{
#ifdef LOGERROR
    error("Z80 bank lockup read %06X\n", address);
#endif
 	gen_running = force_dtack;
    return 0xFF;
}

/* 
	Z80 memory handlers
*/
void z80_write_banked_memory (unsigned int address, unsigned int data)
{
	int offset = address >> 19;

	switch (m68k_writemap_8[offset])
    {
		case VDP:
			/* Valid VDP addresses */
			if (((address >> 16) & 0x07) == 0) 
			{
				switch (address & 0xff)
				{
					case 0x00:	/* Data port */
					case 0x01:
					case 0x02:
					case 0x03:
						vdp_data_w(data << 8 | data);
						return;

					case 0x04:	/* Control port */
					case 0x05:
					case 0x06:
					case 0x07:
						vdp_ctrl_w(data << 8 | data);
						return;

					case 0x10:	/* Unused */
					case 0x12:
					case 0x14:
					case 0x16:
						z80bank_unused_w(address, data);
						return;
					
					case 0x11:	/* PSG */
					case 0x13:
					case 0x15:
					case 0x17:
						psg_write(0, data);
						return;

					case 0x18: /* Unused */
					case 0x19:
					case 0x1a:
					case 0x1b:
						z80bank_unused_w(address, data);
						return;

					case 0x1c: /* Test register */
					case 0x1d:
					case 0x1e:
					case 0x1f:
						vdp_test_w(data << 8 | data);
						return;

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

			/* Invalid address */
			z80bank_lockup_w(address, data);
			return;


		case SYSTEM_IO:
		{
			unsigned int base = address >> 8;

			/* Z80 (access prohibited) */
			if (base <= 0xa0ff) 
	    	{
                z80bank_lockup_w(address, data);
                return;
            }

			/* CONTROL registers */
			if (base <= 0xa1ff)
			{  			
				switch (base & 0xff)
				{
					case 0x00:	/* I/O chip (only gets /LWR) */
						if ((address & 0xe1) == 0x01) io_write((address >> 1) & 0x0f, data);
						else z80bank_unused_w(address, data);
		  				return;

					case 0x11:	/* BUSREQ */
						if (address & 1) z80bank_unused_w(address, data);
						else gen_busreq_w(data & 1);
		  				return;

					case 0x12:	/* RESET */
						if (address & 1) z80bank_unused_w(address, data);
					  	else gen_reset_w(data & 1);
					  	return;

					case 0x30:	/* TIME */
						if (cart_hw.time_w) return cart_hw.time_w(address, data);
						else z80bank_unused_w(address, data);
						return;

					case 0x41:	/* BOOTROM */
						if (address & 1)
						{
    						if (data & 1)
    						{
    							rom_readmap[0] = &cart_rom[0];
    							rom_size = genromsize;
    						}
    						else
    						{
    							rom_readmap[0] = &bios_rom[0];
    							rom_size = 0x800;
    						}
    
    						if (!(bios_enabled & 2))
    						{
    							bios_enabled |= 2;
    							memcpy(bios_rom, cart_rom, 0x800);
    							memset(cart_rom, 0, 0x500000);
    						}
						}
						else z80bank_unused_w (address, data);
						return;

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

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

			/* Invalid address */
			z80bank_lockup_w(address, data);
			return;
		}

		case ROM:			
			WRITE_BYTE(rom_readmap[offset], address & 0x7ffff, data);
			return;
			
		case SRAM:
			if (address <= sram.end) WRITE_BYTE(sram.sram, address - sram.start, data);
			else z80bank_unused_w(address, data);
			return;
		
		case EEPROM:
			if ((address == sram.start) || (address == sram.end)) eeprom_write(address, data);
			else z80bank_unused_w(address, data);
			return;
			
    	case CART_HW:
			cart_hw.regs_w(address, data);
			return;
		
		case UNUSED: 
			z80bank_unused_w(address, data);
	      	return;

    	case ILLEGAL:
      		z80bank_lockup_w(address, data);
      		return;

    	default:	/* WRAM */
			WRITE_BYTE(work_ram, address & 0xffff, data);
      		return;
	}
}

unsigned int z80_read_banked_memory(unsigned int address)
{
	int offset = address >> 19;

	switch (m68k_readmap_8[offset])
	{
    	case WRAM: /* NOTE: can't be read on some Genesis models (!)*/
      		return READ_BYTE(work_ram, address & 0xffff);

		case SYSTEM_IO:
		{
			unsigned int base = address >> 8;

			/* Z80 (access prohibited) */
			if (base <= 0xa0ff) return z80bank_lockup_r(address);
						
			/* I/O & CONTROL registers */
			if (base <= 0xa1ff)
			{
				switch (base & 0xff)
				{
					case 0x00:	/* I/O chip */
						if (address & 0xe0) return z80bank_unused_r(address);
						return (io_read((address >> 1) & 0x0f));

					case 0x11:	/* BUSACK */
						if (address & 0x01) return 0xff;
						else return (zbusack | 0xfe);

					case 0x30:	/* TIME */
						if (cart_hw.time_r) return cart_hw.time_r(address);
						else z80bank_unused_r(address);

					case 0x10:	/* MEMORY MODE */
				    case 0x12:	/* RESET */
				    case 0x20:	/* MEGA-CD */
					case 0x40:	/* TMSS */
					case 0x41:	/* BOOTROM */
					case 0x44:	/* RADICA */
					case 0x50:	/* SVP REGISTERS */
						return z80bank_unused_r(address);
	
		    		default:	/* Invalid address */
		      			return z80bank_lockup_r(address);
		    	}
			}	

			/* Invalid address */
		    return z80bank_lockup_r(address);
		}

    	case VDP:
			/* Valid VDP addresses */
			if (((address >> 16) & 0x07) == 0) 
			{
			  	switch (address & 0xff)
			    {
				    case 0x00:		/* DATA */
				    case 0x02:
				      	return (vdp_data_r() >> 8);
			
				    case 0x01:		/* DATA */
				    case 0x03:
				      	return (vdp_data_r() & 0xff);
			
				    case 0x04:		/* CTRL */
				    case 0x06:
						return (0xfc | ((vdp_ctrl_r() >> 8) & 3));

				    case 0x05:		/* CTRL */
				    case 0x07:
				      	return (vdp_ctrl_r() & 0xff);
			
				    case 0x08:		/* HVC */
				    case 0x0a:
				    case 0x0c:
				    case 0x0e:
				      	return (vdp_hvc_r() >> 8);
			
				    case 0x09:		/* HVC */
				    case 0x0b:
				    case 0x0d:
				    case 0x0f:
				      	return (vdp_hvc_r() & 0xff);
			
				    case 0x18:		/* Unused */
				    case 0x19:
				    case 0x1a:
				    case 0x1b:
				    case 0x1c:
				    case 0x1d:
				    case 0x1e:
				    case 0x1f:
				      	return (z80bank_unused_r(address) | 0xff);

					default:		/* Invalid address */
				      	return z80bank_lockup_r(address);
				}
			}
      		
			/* Invalid address */
			return (z80bank_lockup_r (address));

		case SRAM:
			if (address <= sram.end) return READ_BYTE(sram.sram, address - sram.start);
			return READ_BYTE(rom_readmap[offset], address & 0x7ffff);

		case EEPROM:
			if ((address == sram.start) || (address == sram.end)) return eeprom_read(address);
			return READ_BYTE(rom_readmap[offset], address & 0x7ffff);

		case CART_HW:
			return cart_hw.regs_r(address);

		case UNUSED:
			return z80bank_unused_r(address);

		case ILLEGAL:
			return z80bank_lockup_r(address);

		default:	/* ROM */
			return READ_BYTE(rom_readmap[offset], address & 0x7ffff);
    }
}
