/****************************************************************************
 *  Genesis Plus
 *  Mega Drive cartridge hardware support
 *
 *  Copyright (C) 2007-2025  Eke-Eke (Genesis Plus GX)
 *
 *  Many cartridge protections were initially documented by Haze
 *  (http://haze.mameworld.info/)
 *
 *  Realtec mapper was documented by TascoDeluxe
 *
 *  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"
#include "eeprom_i2c.h"
#include "eeprom_spi.h"
#include "flash_cfi.h"
#include "yx5200.h"
#include "megasd.h"

/* Cart database entry */
typedef struct
{
  uint16 chk_1;       /* header checksum */
  uint16 chk_2;       /* real checksum */
  uint8 bank_start;   /* first mapped bank in $400000-$7fffff region */
  uint8 bank_end;     /* last mapped bank in $400000-$7fffff region */
  cart_hw_t cart_hw;  /* hardware description */
} md_entry_t;

/* Function prototypes */
static void mapper_sega_w(uint32 data);
static void mapper_512k_w(uint32 address, uint32 data);
static void mapper_ssf2_w(uint32 address, uint32 data);
static void mapper_sf001_w(uint32 address, uint32 data);
static void mapper_sf002_w(uint32 address, uint32 data);
static void mapper_sf004_w(uint32 address, uint32 data);
static uint32 mapper_sf004_r(uint32 address);
static void mapper_t5740_w(uint32 address, uint32 data);
static uint32 mapper_t5740_r(uint32 address);
static void mapper_flash_w8(uint32 address, uint32 data);
static void mapper_flash_w16(uint32 address, uint32 data);
static uint32 mapper_flash_r8(uint32 address);
static uint32 mapper_flash_r16(uint32 address);
static void mapper_kaiserwave_w(uint32 address, uint32 data);
static uint32 mapper_smw_64_r(uint32 address);
static void mapper_smw_64_w(uint32 address, uint32 data);
static void mapper_realtec_w(uint32 address, uint32 data);
static uint32 mapper_realtec_r(uint32 address);
static void mapper_seganet_w(uint32 address, uint32 data);
static void mapper_32k_w(uint32 data);
static void mapper_64k_w(uint32 data);
static void mapper_64k_multi_w(uint32 address);
static uint32 mapper_128k_multi_r(uint32 address);
static void mapper_256k_multi_w(uint32 address, uint32 data);
static void mapper_wd1601_w(uint32 address, uint32 data);
static uint32 mapper_64k_radica_r(uint32 address);
static uint32 mapper_128k_radica_r(uint32 address);
static void mapper_sr16v1_w(uint32 address, uint32 data);
static void default_time_w(uint32 address, uint32 data);
static void default_regs_w(uint32 address, uint32 data);
static uint32 default_regs_r(uint32 address);
static uint32 default_regs_r_16(uint32 address);
static uint32 custom_regs_r(uint32 address);
static void custom_regs_w(uint32 address, uint32 data);
static void custom_alt_regs_w(uint32 address, uint32 data);
static uint32 topshooter_r(uint32 address);
static void topshooter_w(uint32 address, uint32 data);
static uint32 tekken_regs_r(uint32 address);
static void tekken_regs_w(uint32 address, uint32 data);

/* Games that need extra hardware emulation:
  - copy protection device
  - custom ROM banking device
*/
static const md_entry_t rom_database[] =
{
/* Funny World & Balloon Boy */
  {0x0000,0x06ab,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},1,0,NULL,m68k_unused_8_w,NULL,mapper_realtec_w}},
/* Whac-a-Critter - Mallet Legend */
  {0xffff,0xf863,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},1,0,NULL,m68k_unused_8_w,NULL,mapper_realtec_w}},
/* Earth Defense */
  {0xffff,0x44fb,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},1,0,NULL,m68k_unused_8_w,NULL,mapper_realtec_w}},
/* Tom Clown */
  {0x0000,0xc0cd,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},1,0,NULL,m68k_unused_8_w,NULL,mapper_realtec_w}},

/* 1800-in-1 */
  {0x3296,0x2370,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,1,mapper_128k_multi_r,m68k_unused_8_w,NULL,NULL}},

/* Golden Mega 250-in-1 */
  {0xe43c,0x886f,0x08,0x08,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,1,NULL,m68k_unused_8_w,NULL,mapper_256k_multi_w}},

/* RADICA (Volume 1) (bad dump ?) */
  {0x0000,0x2326,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,1,mapper_64k_radica_r,m68k_unused_8_w,NULL,NULL}},
/* RADICA (Volume 1) */
  {0x24f4,0xfc84,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_64k_radica_r,m68k_unused_8_w,NULL,NULL}},
/* RADICA (Volume 2) */
  {0xd951,0x78d0,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_64k_radica_r,m68k_unused_8_w,NULL,NULL}},
/* RADICA (Volume 3 - Super Sonic Gold edition) */
  {0x0000,0x1f25,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_64k_radica_r,m68k_unused_8_w,NULL,NULL}},
/* RADICA (Street Fighter II CE edition) */
  {0x1add,0xa838,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_64k_radica_r,m68k_unused_8_w,NULL,NULL}},
/* RADICA (Street Fighter II CE edition) (PAL) */
  {0x104f,0x32e9,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_64k_radica_r,m68k_unused_8_w,NULL,NULL}},
/* RADICA (Sensible Soccer Plus edition) (PAL) */
  {0x0000,0x1f7f,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,mapper_128k_radica_r,m68k_unused_8_w,NULL,NULL}},

/* Tenchi wo Kurau III: Sangokushi Gaiden - Chinese Fighter */
  {0x9490,0x8180,0x40,0x6f,{{0x00,0x00,0x00,0x00},{0xf0000c,0xf0000c,0xf0000c,0xf0000c},{0x400000,0x400004,0x400008,0x40000c},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_alt_regs_w}},

/* Top Fighter */
  {0x4eb9,0x5d8b,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}},
/* Soul Edge VS Samurai Spirits  */
  {0x00ff,0x5d34,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}},
/* Mulan */
  {0x0404,0x1b40,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}},
/* Pocket Monsters II */
  {0x47f9,0x17e5,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}},
/* Lion King 3 */
  {0x0000,0x507c,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}},
/* Super King Kong 99 */
  {0x0000,0x7d6e,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}},
/* Super Donkey Kong 99 (bootleg version of Super King Kong 99 ? all protection checks seem patched except one, resulting in broken end game sequence if hardware is not implemented) */
  {0x0000,0x16dc,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}},
/* Gunfight 3-in-1 */
  {0x0000,0x6ff8,0x60,0x7f,{{0x00,0x00,0x00,0x00},{0xf00007,0xf00007,0xf00007,0xffffff},{0x600001,0x600003,0x600005,0x000000},0,1,NULL,m68k_unused_8_w,default_regs_r,custom_regs_w}}, 
/* Pokemon Stadium */
  {0x0000,0x843c,0x70,0x7f,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,1,NULL,m68k_unused_8_w,NULL,custom_regs_w}},

/* Tekken 3 Special (original dump) (a bootleg version also exists, with patched protection & different boot routine which reads unused !TIME mapped area) */
  {0x0000,0xc2f0,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,tekken_regs_r,tekken_regs_w}},

/* Lion King 2 */
  {0xffff,0x1d9b,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,default_regs_w}},
/* Squirell King */
  {0x0000,0x8ec8,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,default_regs_w}},
/* Tiny Toon Adventures 3 */
  {0x2020,0xed9c,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,default_regs_w}},
/* Barver Battle Saga - Tai Kong Zhan Shi (registers accessed by Z80, related to sound engine ?) */
  {0x30b9,0x1c2a,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,default_regs_w}},
/* Shui Hu Zhuan (registers accessed by Z80, related to sound engine ?) */
  {0x6001,0x0211,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,default_regs_w}},
/* Feng Shen Ying Jie Chuan (registers accessed by Z80, related to sound engine ?) */
  {0xffff,0x5d98,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,default_regs_w}},
/* (*) Shui Hu: Feng Yun Zhuan (patched ROM, unused registers) */
  {0x3332,0x872b,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xfffffd,0xfffffd,0xffffff,0xffffff},{0x400000,0x400004,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,default_regs_w}},

/* (*) Chao Ji Da Fu Weng (patched ROM, various words witten to register, long word also read from $7E0000, unknown banking hardware ?) */
  {0xa697,0xa697,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x000000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,NULL,default_regs_w}},

/* (*) Aq Renkan Awa - A Q Lian Huan Pao (patched ROM, ON/OFF bit sequence is written to register, unknown banking hardware ?) */
  {0x8104,0x0517,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400001,0x000000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,NULL,default_regs_w}},

/* (*) Tun Shi Tian Di III (patched ROM, unused register) */
  {0x0000,0x9c5e,0x40,0x40,{{0xab,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400046,0x000000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},

/* Chao Ji Mahjong Club (uses 16-bits reads)  */
  {0x0000,0x2d5c,0x40,0x40,{{0x90,0xd3,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r_16,NULL}},
/* Ma Jiang Qing Ren: Ji Ma Jiang Zhi */
  {0x0000,0x7037,0x40,0x40,{{0x90,0xd3,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x401000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Du Shen Zhi Meng Huan Poker */
  {0x0000,0x3b95,0x40,0x40,{{0x90,0xd3,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x401000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Feng Kuang Tao Hua Yuan (original version from Creaton Softec Inc) (a bootleg version also exists with patched protection and minor title screen variations) */
  {0x0000,0x9dc4,0x40,0x40,{{0x90,0xd3,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x401000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* (*) Zhan Qi - Chinese Battle Chess (patched ROM ? registers values are read and compared to expected values but comparison results are unused) */
  {0x0000,0x4704,0x40,0x40,{{0x90,0xd3,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x401000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},

/* (*) Jiu Ji Ma Jiang II: Ye Yan Bian (patched ROM, using expected register value $f0 crashes the game) (uses 16-bits reads) */
  {0x0c44,0xba81,0x40,0x40,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r_16,NULL}},
/* Shi Liu Zhang Ma Jiang - 16 Tiles Mahjong (uses 16-bits reads) */
  {0xfb40,0x4bed,0x40,0x40,{{0x00,0xaa,0x00,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x400002,0x000000,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r_16,NULL}},
/* Shi Liu Zhang Ma Jiang II - 16 Tiles Mahjong II (uses 16-bits reads) */
  {0xffff,0x0903,0x40,0x40,{{0x00,0x00,0xc9,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x400004,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r_16,NULL}},
/* Lei Dian Chuan Shuo II - Thunderbolt II (uses 16-bits reads) */
  {0x0000,0x1585,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r_16,NULL}},

/* Chao Ji Poker (correct ROM dump, original release is an overdump) */
  {0xffff,0xd7b0,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Super Bubble Bobble */
  {0x0000,0x16cd,0x40,0x40,{{0x55,0x0f,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* San Guo Yan Yi: Huo Shao Chi Bi - The Battle of Red Cliffs */
  {0x0000,0xed61,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Huan Le Tao Qi Shu - Smart Mouse */
  {0x0000,0x1a28,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Sichuan Style Mahjong */
  {0x0000,0x71e1,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* (*) Hei Tao 2 - Super Big 2 (patched ROM, unused registers) */
  {0x0000,0x5843,0x40,0x40,{{0x55,0x0f,0xaa,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Mighty Morphin Power Rangers - The Fighting Edition */
  {0x0000,0x2288,0x40,0x40,{{0x55,0x0f,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Ling Huan Dao Shi - Super Magican */
  {0x0080,0x3dba,0x40,0x40,{{0x55,0x0f,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Ya-Se Chuanshuo */
  {0xffff,0xd472,0x40,0x40,{{0x63,0x98,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Meng Huan Shui Guo Pan - 777 Casino (For first one, 0x55 works as well. Other values seem never used so they are guessed from other unlicensed games using similar mapper) */
  {0x0000,0xf8d9,0x40,0x40,{{0x63,0x98,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Wu Kong Wai Zhuan (original) (a bootleg version also exists, with patched protection & modified SRAM test routine ?) */
  {0x0000,0x19ff,0x40,0x40,{{0x63,0x98,0xc9,0x18},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Soul Blade */
  {0x0000,0x0c5b,0x40,0x40,{{0x63,0x98,0xc9,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Shi San Zhang Ma Jiang: Zhong Guo Mei Nv Pian (only $400004 seem checked, for which 0xaa works as well, other addresses/values are guessed from other unlicensed games using similar mapper) */
  {0xffff,0xd41d,0x40,0x40,{{0x63,0x98,0xc9,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Rockman X3 (original version) (only two last addresses seem checked, for which 0xaa/0x18 work as well, other addresses/values are guessed from other unlicensed games using similar mapper) */
  {0xffff,0x6fdd,0x40,0x40,{{0x63,0x98,0xc9,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0x400000,0x400002,0x400004,0x400006},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},

/* King of Fighter 98 */
  {0x0000,0xd0a0,0x48,0x4f,{{0x00,0x00,0xaa,0xf0},{0xffffff,0xffffff,0xfc0000,0xfc0000},{0x000000,0x000000,0x480000,0x4c0000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},

/* Rock Heaven */
  {0x6cca,0x2395,0x50,0x50,{{0x50,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x500008,0x000000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},
/* Rock World */
  {0x3547,0xa3da,0x50,0x50,{{0x50,0xa0,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x500008,0x500208,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,default_regs_r,NULL}},

/* Rockman X3 (bootleg version ? two last register returned values are ignored, note that 0xaa/0x18 would work as well) */
  {0x0000,0x9d0e,0x40,0x40,{{0x0c,0x00,0xc9,0xf0},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x400004,0x400006},0,0,default_regs_r,m68k_unused_8_w,default_regs_r,NULL}},

/* (*) Dragon Ball Final Bout  (patched ROM, in original code, different switches occurs depending on returned value $00-$0f) */
  {0xc65a,0xc65a,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,m68k_unused_8_w,NULL,NULL}},
/* (*) Yi Men Ying Lie: Yang Jia Jiang - Yang Warrior Family (patched ROM, register value unused) */
  {0x0000,0x96b0,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,m68k_unused_8_w,NULL,NULL}},
/* Super Mario 2 1998 */
  {0xffff,0x0474,0x00,0x00,{{0x0a,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,m68k_unused_8_w,NULL,NULL}},
/* Super Mario World */
  {0x2020,0xb4eb,0x00,0x00,{{0x1c,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0x000000,0x000000,0x000000},0,0,default_regs_r,m68k_unused_8_w,NULL,NULL}},

/* King of Fighter 99 */
  {0x0000,0x021e,0x00,0x00,{{0x00,0x01,0x1f,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0xa13002,0xa1303e,0x000000},0,0,custom_regs_r,default_regs_w,NULL,NULL}},
/* Pocket Monster */
  {0xd6fc,0x1eb1,0x00,0x00,{{0x00,0x01,0x1f,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0xa13002,0xa1303e,0x000000},0,0,custom_regs_r,default_regs_w,NULL,NULL}},
/* Pocket Monster (bootleg version ? two last register returned values are ignored & first register test has been modified) */
  {0xd6fc,0x6319,0x00,0x00,{{0x14,0x01,0x1f,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0xa13002,0xa1303e,0x000000},0,0,default_regs_r,m68k_unused_8_w,NULL,NULL}},
/* A Bug's Life (bootleg version ? two last register returned values are ignored & first register test has been modified ?) */
  {0x7f7f,0x2aad,0x00,0x00,{{0x28,0x01,0x1f,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0xa13000,0xa13002,0xa1303e,0x000000},0,0,default_regs_r,m68k_unused_8_w,NULL,NULL}},

/* Game no Kanzume Otokuyou */
  {0x0000,0xf9d1,0x00,0x00,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,NULL,mapper_seganet_w,NULL,NULL}},

/* Top Shooter (arcade hardware) */
  {0xffff,0x3632,0x20,0x20,{{0x00,0x00,0x00,0x00},{0xffffff,0xffffff,0xffffff,0xffffff},{0x000000,0x000000,0x000000,0x000000},0,0,NULL,m68k_unused_8_w,topshooter_r,topshooter_w}}
};


/************************************************************
          Cart Hardware initialization 
*************************************************************/

void md_cart_init(void)
{
  int i;

  /***************************************************************************************************************
                CARTRIDGE ROM MIRRORING                                                                                   
   ***************************************************************************************************************
  
    MD Cartridge area is mapped to $000000-$3fffff:

      -> when accessing ROM, 68k address lines A1 to A21 can be used by the internal cartridge hardware to decode
         full 4MB address range.
      -> depending on ROM total size and additional decoding hardware, some address lines might be ignored,
         resulting in ROM mirroring.

    Cartridges can use either 8-bits (x2) or 16-bits (x1, x2) Mask ROM chips, each chip size is a factor of 2 bytes:

      -> two 8-bits chips are equivalent to one 16-bits chip, no specific address decoding is required, needed
         address lines are simply connected to each chip, upper address lines are ignored and data lines are
         connected appropriately to each chip (D0-D7 to one chip, D8-D15 to the other one).
         ROM is generally mirrored each N bytes where N=2^(k+1) is the total ROM size (ROM1+ROM2,ROM1+ROM2,...)

      -> one single 16-bits chip do not need specific address decoding, address lines are simply connected
         depending on the ROM size, upper address lines being ignored.
         ROM is generally mirrored each N bytes where N=2^k is the size of the ROM chip (ROM1,ROM1,ROM1,...)

      -> two 16-bits chips of the same size are equivalent to one chip of double size, address decoding generally
         is the same except that specific hardware is used (one address line is generally used for chip selection,
         lower ones being used to address the chips and upper ones being ignored).
         ROM is generally mirrored each N bytes where N=2^(k+1) is the total ROM size (ROM1,ROM2,ROM1,ROM2,...)

      -> two 16-bits chips with different size are mapped differently. Address decoding is done the same way as
         above (one address line used for chip selection) but the ignored & required address lines differ from
         one chip to another, which makes ROM mirroring different.
         ROM2 size is generally half of ROM1 size and upper half ignored (ROM1,ROM2,XXXX,ROM1,ROM2,XXXX,...)

      From the emulator point of view, we only need to distinguish 2 cases:

      1/ total ROM size is a factor of 2: ROM is mirrored each 2^k bytes.

      2/ total ROM size is not a factor of 2: ROM is padded up to 2^k then mirrored each 2^k bytes.

  ******************************************************************************************************************/
  
  /* calculate nearest size with factor of 2 */
  unsigned int size = 0x10000;
  while (cart.romsize > size)
    size <<= 1;

  /* Sonic & Knuckles */
  if (strstr(rominfo.international,"SONIC & KNUCKLES"))
  {
    /* disable ROM mirroring at $200000-$3fffff (normally mapped to external cartridge) */
    size = 0x400000;
  }

  /* total ROM size is not a factor of 2  */
  /* TODO: handle all possible ROM configurations using cartridge database */
  if (cart.romsize < size)
  {
    if (size < MAXROMSIZE)
    {
      /* ROM is padded up to 2^k bytes */
      memset(cart.rom + cart.romsize, 0xff, size - cart.romsize);
    }
    else
    {
      /* ROM is padded up to max ROM size */
      memset(cart.rom + cart.romsize, 0xff, MAXROMSIZE - cart.romsize);
    }
  }

  /* ROM is mirrored each 2^k bytes */
  cart.mask = size - 1;

  /* no special external hardware required by default */
  cart.special = 0;

  /**********************************************
          DEFAULT CARTRIDGE MAPPING 
  ***********************************************/
  for (i=0; i<0x40; i++)
  {
    /* cartridge ROM */
    m68k.memory_map[i].base     = cart.rom + ((i<<16) & cart.mask);
    m68k.memory_map[i].read8    = NULL;
    m68k.memory_map[i].read16   = NULL;
    m68k.memory_map[i].write8   = m68k_unused_8_w;
    m68k.memory_map[i].write16  = m68k_unused_16_w;
    zbank_memory_map[i].read    = NULL;
    zbank_memory_map[i].write   = zbank_unused_w;
  }

  for (i=0x40; i<0x80; i++)
  {
    /* unused area */
    m68k.memory_map[i].base     = cart.rom + (i<<16);
    m68k.memory_map[i].read8    = m68k_read_bus_8;
    m68k.memory_map[i].read16   = m68k_read_bus_16;
    m68k.memory_map[i].write8   = m68k_unused_8_w;
    m68k.memory_map[i].write16  = m68k_unused_16_w;
    zbank_memory_map[i].read    = zbank_unused_r;
    zbank_memory_map[i].write   = zbank_unused_w;
  }

  /* support for Quackshot REV A original ROM dump (512KB) */
  if (strstr(rominfo.product,"00004054-01") && (cart.romsize == 0x80000) && (rominfo.checksum == 0xa4b3))
  {
    /* $000000-$0fffff: lower 256KB mirrored (VA18 and VA19 not connected to ROM chip) */
    for (i=0x00; i<0x10; i++)
    {
      /* $200000-$3fffff: mirror of $000000-$1fffff (VA21 not connected to ROM chip) */
      m68k.memory_map[i].base = m68k.memory_map[i + 0x20].base = cart.rom + ((i & 0x03) << 16);
    }

    /* $100000-$1fffff: upper 256KB mirrored (VA20 connected to ROM chip A19) */
    for (i=0x10; i<0x20; i++)
    {
      /* $200000-$3fffff: mirror of $000000-$1fffff (VA21 not connected to ROM chip) */
      m68k.memory_map[i].base = m68k.memory_map[i + 0x20].base = cart.rom + 0x40000 + ((i & 0x03) << 16);
    }
  }

  /**********************************************
          BACKUP MEMORY 
  ***********************************************/
  sram_init();
  eeprom_i2c_init();

  /* memory-mapped SRAM */
  if (sram.on && !sram.custom)
  {
    /* SRAM is mapped by default unless it overlaps with ROM area (Phantasy Star 4, Beyond Oasis/Legend of Thor, World Series Baseball 9x, Duke Nukem 3D,...) */
    if (sram.start >= cart.romsize)
    {
      /* except for Sonic the Hedgehog 3 (cartridge ROM mirrored in upper 2MB area at power on) */
      if (strstr(rominfo.international,"SONIC THE HEDGEHOG 3") == NULL)
      {
        m68k.memory_map[sram.start >> 16].base    = sram.sram;
        m68k.memory_map[sram.start >> 16].read8   = sram_read_byte;
        m68k.memory_map[sram.start >> 16].read16  = sram_read_word;
        m68k.memory_map[sram.start >> 16].write8  = sram_write_byte;
        m68k.memory_map[sram.start >> 16].write16 = sram_write_word;
        zbank_memory_map[sram.start >> 16].read   = sram_read_byte;
        zbank_memory_map[sram.start >> 16].write  = sram_write_byte;
      }

      /* Barkley Shut Up and Jam 2 needs SRAM to be mapped in whole upper 2MB area */
      if (strstr(rominfo.product,"T-119186") != NULL)
      {
        for (i=0x21; i<0x40; i++)
        {
          m68k.memory_map[i].base    = sram.sram;
          m68k.memory_map[i].read8   = sram_read_byte;
          m68k.memory_map[i].read16  = sram_read_word;
          m68k.memory_map[i].write8  = sram_write_byte;
          m68k.memory_map[i].write16 = sram_write_word;
          zbank_memory_map[i].read   = sram_read_byte;
          zbank_memory_map[i].write  = sram_write_byte;
        }
      }
    }

    /* support for Triple Play 96 & Triple Play - Gold Edition mapping */
    else if ((strstr(rominfo.product,"T-172026") != NULL) || (strstr(rominfo.product,"T-172116") != NULL))
    {
      /* $000000-$1fffff: cartridge ROM (lower 2MB) */
      /* $200000-$2fffff: SRAM (32KB mirrored) */
      /* NB: existing 4MB ROM dumps include SRAM data at ROM offsets 0x200000-0x2fffff */ 
      for (i=0x20; i<0x30; i++)
      {
        m68k.memory_map[i].base    = sram.sram;
        m68k.memory_map[i].read8   = sram_read_byte;
        m68k.memory_map[i].read16  = sram_read_word;
        m68k.memory_map[i].write8  = sram_write_byte;
        m68k.memory_map[i].write16 = sram_write_word;
        zbank_memory_map[i].read   = sram_read_byte;
        zbank_memory_map[i].write  = sram_write_byte;
      }

      /* $300000-$3fffff: cartridge ROM (upper 1MB) */
      /* NB: only real (3MB) Mask ROM dumps need ROM offsets 0x200000-0x2fffff to be remapped to this area */
      if (READ_BYTE(cart.rom, 0x200000) != 0xFF)
      {
        for (i=0x30; i<0x40; i++)
        {
          m68k.memory_map[i].base = cart.rom + ((i - 0x10) << 16);
        }
      }
    }
  }

  /**********************************************
          SVP CHIP 
  ***********************************************/
  svp = NULL;
  if ((READ_BYTE(cart.rom, 0x1c8) == 'S') && (READ_BYTE(cart.rom, 0x1c9) == 'V'))
  {
    svp_init();
  }

  /**********************************************
        CARTRIDGE EXTRA HARDWARE
  ***********************************************/
  memset(&cart.hw, 0, sizeof(cart.hw));

  /* initialize default $200000-$20ffff mapping (for games using SRAM & ROM bankswitching) */
  if (m68k.memory_map[0x20].base == sram.sram)
  {
    cart.hw.regs[0] = (0x200000 & cart.mask) >> 16;
  }

  /* search for game into database */
  for (i=0; i<(sizeof(rom_database)/sizeof(md_entry_t)); i++)
  {
    /* known cart found ! */
    if ((rominfo.checksum == rom_database[i].chk_1) &&
        (rominfo.realchecksum == rom_database[i].chk_2))
    {
      int j = rom_database[i].bank_start;

      /* retrieve hardware information */
      memcpy(&cart.hw, &(rom_database[i].cart_hw), sizeof(cart.hw));

      /* initialize memory handlers for $400000-$7FFFFF region */
      while (j <= rom_database[i].bank_end)
      {
        if (cart.hw.regs_r)
        {
          m68k.memory_map[j].read8    = cart.hw.regs_r;
          m68k.memory_map[j].read16   = cart.hw.regs_r;
          zbank_memory_map[j].read    = cart.hw.regs_r;
        }
        if (cart.hw.regs_w)
        {
          m68k.memory_map[j].write8   = cart.hw.regs_w;
          m68k.memory_map[j].write16  = cart.hw.regs_w;
          zbank_memory_map[j].write   = cart.hw.regs_w;
        }
        j++;
      }

      /* leave loop */
      break;
    }
  }

  /* Realtec mapper */
  if (cart.hw.realtec)
  {
    /* copy 8KB Boot ROM after cartridge ROM area */
    for (i=0; i<8; i++)
    {
      memcpy(cart.rom + 0x400000 + i*0x2000, cart.rom + 0x7e000, 0x2000);
    }

    /* specific read handler for ROM header area */
    m68k.memory_map[0].read16 = mapper_realtec_r;
  }

  /* detect specific mappers */
  else if (strstr(rominfo.international,"DEMONS OF ASTEBORG") ||
           strstr(rominfo.international,"ASTEBROS") ||
           strstr(rominfo.domestic,"DAEMON CLAW"))
  {
    /* default SSF2+SRAM mapper (fixes patched versions incorrectly indicating Everdrive extended SSF mapper in ROM header) */ 
    cart.hw.time_w = default_time_w;
  }
  else if (strstr(rominfo.consoletype,"SEGA SSF"))
  {
    /* Everdrive extended SSF mapper */
    cart.hw.time_w = mapper_512k_w;

    /* cartridge ROM mapping is reinitialized on /VRES */
    cart.hw.bankshift = 1;
  }
  else if ((strstr(rominfo.consoletype,"SEGA SSF2") != NULL) && (cart.romsize <= 0x800000))
  {
    /* MegaSD enhanced SSF2 mapper (max. 8MB ROM) */
    cart.special |= HW_MEGASD;
    cart.hw.time_w = megasd_enhanced_ssf2_mapper_w;

    /* cartridge ROM mapping is reinitialized on /VRES */
    cart.hw.bankshift = 1;
  }
  else if ((strstr(rominfo.consoletype,"SEGA MEGASD") != NULL) && (cart.romsize <= 0x400000))
  {
    /* MegaSD ROM write mapper (max. 4MB ROM) */
    cart.special |= HW_MEGASD;
    cart.hw.time_w = megasd_rom_mapper_w;
  }
  else if (strstr(rominfo.domestic,"SUPER STREET FIGHTER2"))
  {
    /* SSF2 mapper */
    cart.hw.time_w = mapper_ssf2_w;

    /* cartridge ROM mapping is reinitialized on /VRES */
    cart.hw.bankshift = 1;
  }
  else if (strstr(rominfo.product,"T-5740"))
  {
    /* T-5740XX-XX mapper */
    cart.hw.bankshift = 1;
    m68k.memory_map[0x01].read8 = mapper_t5740_r;
    zbank_memory_map[0x01].read = mapper_t5740_r;

    /* specific !TIME handlers */
    cart.hw.time_w = mapper_t5740_w;
    cart.hw.time_r = eeprom_spi_read;

    /* initialize SPI EEPROM board */
    eeprom_spi_init();
  }
  else if (strstr(rominfo.ROMType,"SF") && strstr(rominfo.product,"001"))
  {
    /* SF-001 mapper */
    m68k.memory_map[0x00].write8 = mapper_sf001_w;
    m68k.memory_map[0x00].write16 = mapper_sf001_w;
    zbank_memory_map[0x00].write = mapper_sf001_w;

    /* no !TIME handler */
    cart.hw.time_w = m68k_unused_8_w;

    /* cartridge ROM is mapped to $3C0000-$3FFFFF on reset */
    for (i=0x3c; i<0x40; i++)
    {
      m68k.memory_map[i].base     = cart.rom + (i << 16);
      m68k.memory_map[i].read8    = NULL;
      m68k.memory_map[i].read16   = NULL;
      m68k.memory_map[i].write8   = m68k_unused_8_w;
      m68k.memory_map[i].write16  = m68k_unused_16_w;
      zbank_memory_map[i].read    = NULL;
      zbank_memory_map[i].write   = m68k_unused_8_w;
    }
  }
  else if (strstr(rominfo.ROMType,"SF") && strstr(rominfo.product,"002"))
  {
    /* SF-002 mapper */
    m68k.memory_map[0x00].write8 = mapper_sf002_w;
    m68k.memory_map[0x00].write16 = mapper_sf002_w;
    zbank_memory_map[0x00].write = mapper_sf002_w;

    /* no !TIME handler */
    cart.hw.time_w = m68k_unused_8_w;
  }
  else if (strstr(rominfo.ROMType,"SF") && strstr(rominfo.product,"004"))
  {
    /* SF-004 mapper */
    m68k.memory_map[0x00].write8 = mapper_sf004_w;
    m68k.memory_map[0x00].write16 = mapper_sf004_w;
    zbank_memory_map[0x00].write = mapper_sf004_w;

    /* specific !TIME handlers */
    cart.hw.time_r = mapper_sf004_r;
    cart.hw.time_w = m68k_unused_8_w;

    /* first 256K ROM bank is mirrored into $000000-$1FFFFF on reset */
    for (i=0x00; i<0x20; i++)
    {
      m68k.memory_map[i].base = cart.rom + ((i & 0x03) << 16);
    }

    /* 32K static RAM mapped to $200000-$2FFFFF is disabled on reset */
    for (i=0x20; i<0x30; i++)
    {
      m68k.memory_map[i].base    = sram.sram;
      m68k.memory_map[i].read8   = m68k_read_bus_8;
      m68k.memory_map[i].read16  = m68k_read_bus_16;
      m68k.memory_map[i].write8  = m68k_unused_8_w;
      m68k.memory_map[i].write16 = m68k_unused_16_w;
      zbank_memory_map[i].read   = m68k_read_bus_8;
      zbank_memory_map[i].write  = zbank_unused_w;
    }

    /* $300000-$3FFFFF is not mapped */
    for (i=0x30; i<0x40; i++)
    {
      m68k.memory_map[i].read8   = m68k_read_bus_8;
      m68k.memory_map[i].read16  = m68k_read_bus_16;
      m68k.memory_map[i].write8  = m68k_unused_8_w;
      m68k.memory_map[i].write16 = m68k_unused_16_w;
      zbank_memory_map[i].read   = m68k_read_bus_8;
      zbank_memory_map[i].write  = zbank_unused_w;
    }
  }
  else if (strstr(rominfo.ROMType,"GM") && strstr(rominfo.product,"00000000-42")) /* Escape 2042 */
  {
    /* initialize CFI flash memory hardware */
    flash_cfi_init(M29W320EB, NULL);

    /* Flashkit-MD mapper (4MB ROM only) */
    m68k.memory_map[0x00].read8   = mapper_flash_r8;
    m68k.memory_map[0x00].read16  = mapper_flash_r16;
    m68k.memory_map[0x00].write8  = mapper_flash_w8;
    m68k.memory_map[0x00].write16 = mapper_flash_w16;
    zbank_memory_map[0x00].read   = mapper_flash_r8;
    zbank_memory_map[0x00].write  = mapper_flash_w8;
  }
  else if (strstr(rominfo.ROMType,"GM") && strstr(rominfo.product,"00000000-00") && 
           (((rominfo.checksum == 0xcdf5) && (rominfo.realchecksum == 0x603a)) || /* Life on Mars */
            ((rominfo.checksum == 0x6bd5) && (rominfo.realchecksum == 0x1fea))))  /* Life on Earth Reimagined */
  {
    /* initialize CFI flash memory hardware */
    flash_cfi_init(S29GL064N_04, NULL);

    /* custom flash-save mapper (4MB ROM with backup RAM mapped in last 64KB bank) */
    /* Note: upper 4MB of flash memory is apparently not accessible */
    m68k.memory_map[0x3f].base    = sram.sram;
    m68k.memory_map[0x00].read8   = m68k.memory_map[0x3f].read8   = mapper_flash_r8;
    m68k.memory_map[0x00].read16  = m68k.memory_map[0x3f].read16  = mapper_flash_r16;
    m68k.memory_map[0x00].write8  = m68k.memory_map[0x3f].write8  = mapper_flash_w8;
    m68k.memory_map[0x00].write16 = m68k.memory_map[0x3f].write16 = mapper_flash_w16;
    zbank_memory_map[0x00].read   = zbank_memory_map[0x3f].read   = mapper_flash_r8;
    zbank_memory_map[0x00].write  = zbank_memory_map[0x3f].write  = mapper_flash_w8;
  }
  else if (strstr(rominfo.ROMType,"GM") && strstr(rominfo.product,"00000000-00") && 
           (((rominfo.checksum == 0x45c1) && (rominfo.realchecksum == 0xc613))))  /*  The Secret Of The Four Winds */
  {
    /* KAISER WAVE board !TIME handler */ 
    cart.hw.time_w = mapper_kaiserwave_w;

    /* enable YX5200 hardware */
    cart.special |= HW_YX5200;

    /* allocate & clear blip buffer for YX5200 audio stream */
    snd.blips[3] = blip_new(snd.sample_rate / 10);
    if (snd.blips[3]) blip_clear(snd.blips[3]);

    /* initialize YX5200 audio */
    yx5200_init(snd.sample_rate);

    /* initialize CFI flash memory hardware */
    flash_cfi_init(S29GL064N_04, NULL);

    /* custom flash-save mapper (4MB ROM with backup RAM mapped in last 64KB bank) */
    m68k.memory_map[0x3f].base    = sram.sram;
    m68k.memory_map[0x00].read8   = m68k.memory_map[0x3f].read8   = mapper_flash_r8;
    m68k.memory_map[0x00].read16  = m68k.memory_map[0x3f].read16  = mapper_flash_r16;
    m68k.memory_map[0x00].write8  = m68k.memory_map[0x3f].write8  = mapper_flash_w8;
    m68k.memory_map[0x00].write16 = m68k.memory_map[0x3f].write16 = mapper_flash_w16;
    zbank_memory_map[0x00].read   = zbank_memory_map[0x3f].read   = mapper_flash_r8;
    zbank_memory_map[0x00].write  = zbank_memory_map[0x3f].write  = mapper_flash_w8;

    /* 64M extension mapping (upper 4MB of flash memory is apparently unused but still accessible as cartridge board uses same signals as described in https://gitlab.com/doragasu/flatmap64) */
    for (i=0x40; i<0x80; i++)
    {
      m68k.memory_map[i].read8    = NULL;
      m68k.memory_map[i].read16   = NULL;
      m68k.memory_map[i].write8   = m68k_unused_8_w;
      m68k.memory_map[i].write16  = m68k_unused_16_w;
      zbank_memory_map[i].read    = zbank_unused_r;
      zbank_memory_map[i].write   = zbank_unused_w;
    }
  }
  else if (strstr(rominfo.international, "COLOCODX")) /* ColocoDX */
  {
    /* CFI flash memory OTP area (credit to Stargazer) */
    const uint16 flash_otp[FLASH_OTP_AREA_SIZE] =
    {
      0x2923,0xBE84,0xE16C,0xD6AE,0x5290,0x49F1,0xF1BB,0xE9EB,0xB3A6,0xDB3C,0x870C,0x3E99,0x245E,0x0D1C,0x06B7,0x47DE,
      0xB312,0x4DC8,0x43BB,0x8BA6,0x1F03,0x5A7D,0x0938,0x251F,0x5DD4,0xCBFC,0x96F5,0x453B,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
      0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
      0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
      0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
      0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
      0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,
      0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
    };

    /* initialize CFI flash memory hardware */
    flash_cfi_init(S29GL064N_04, flash_otp);

    /* SGDK flash-save mapper (8MB ROM with backup RAM mapped in one of the two last 64KB banks, see https://gitlab.com/doragasu/sgdk-flash-save) */
    m68k.memory_map[0x7e].base    = sram.sram; /* SRAM initially mapped to bank #127 */
    m68k.memory_map[0x00].read8   = m68k.memory_map[0x7f].read8   = m68k.memory_map[0x7e].read8   = mapper_flash_r8;
    m68k.memory_map[0x00].read16  = m68k.memory_map[0x7f].read16  = m68k.memory_map[0x7e].read16  = mapper_flash_r16;
    m68k.memory_map[0x00].write8  = m68k.memory_map[0x7f].write8  = m68k.memory_map[0x7e].write8  = mapper_flash_w8;
    m68k.memory_map[0x00].write16 = m68k.memory_map[0x7f].write16 = m68k.memory_map[0x7e].write16 = mapper_flash_w16;
    zbank_memory_map[0x00].read   = zbank_memory_map[0x7f].read   = zbank_memory_map[0x7e].read   = mapper_flash_r8;
    zbank_memory_map[0x00].write  = zbank_memory_map[0x7f].write  = zbank_memory_map[0x7e].write  = mapper_flash_w8;

    /* 64M extension mapping (see https://gitlab.com/doragasu/flatmap64) */
    for (i=0x40; i<0x7e; i++)
    {
      m68k.memory_map[i].read8    = NULL;
      m68k.memory_map[i].read16   = NULL;
      m68k.memory_map[i].write8   = m68k_unused_8_w;
      m68k.memory_map[i].write16  = m68k_unused_16_w;
      zbank_memory_map[i].read    = zbank_unused_r;
      zbank_memory_map[i].write   = zbank_unused_w;
    }
  }
  else if ((cart.romsize == 0x400000) && 
           (READ_BYTE(cart.rom, 0x200150) == 'C') &&
           (READ_BYTE(cart.rom, 0x200151) == 'A') &&
           (READ_BYTE(cart.rom, 0x200152) == 'N') &&
           (READ_BYTE(cart.rom, 0x200153) == 'O') &&
           (READ_BYTE(cart.rom, 0x200154) == 'N'))
  {
    /* Canon - Legend of the new Gods (4MB dump) */
    cart.hw.time_w = mapper_wd1601_w;
    cart.hw.bankshift = 1;
    sram.on = 1;
    sram.start = 0x200000;
    sram.end = 0x201fff;
  }
  else if ((*(uint16 *)(cart.rom + 0x08) == 0x6000) && (*(uint16 *)(cart.rom + 0x0a) == 0x01f6) && (rominfo.realchecksum == 0xf894))
  {
    /* Super Mario World 64 (unlicensed) mapper */
    for (i=0x08; i<0x10; i++)
    {
      /* lower 512KB mirrored */
      m68k.memory_map[i].base = cart.rom + ((i & 7) << 16);
    }

    for (i=0x10; i<0x40; i++)
    {
      /* unused area */
      m68k.memory_map[i].read8   = m68k_read_bus_8;
      m68k.memory_map[i].read16  = m68k_read_bus_16;
      m68k.memory_map[i].write8  = m68k_unused_8_w;
      m68k.memory_map[i].write16 = m68k_unused_16_w;
      zbank_memory_map[i].read   = m68k_read_bus_8;
      zbank_memory_map[i].write  = zbank_unused_w;
    }

    for (i=0x60; i<0x70; i++)
    {
      /* custom hardware */
      m68k.memory_map[i].base   = cart.rom + 0x0f0000;
      m68k.memory_map[i].read8  = ((i & 0x07) < 0x04) ? NULL : mapper_smw_64_r;
      m68k.memory_map[i].read16  = ((i & 0x07) < 0x04) ? NULL : mapper_smw_64_r;
      m68k.memory_map[i].write8 = mapper_smw_64_w;
      m68k.memory_map[i].write16 = mapper_smw_64_w;
      zbank_memory_map[i].read  = ((i & 0x07) < 0x04) ? NULL : mapper_smw_64_r;
      zbank_memory_map[i].write = mapper_smw_64_w;
    }
  }
  else if ((*(uint16 *)(cart.rom + 0x04) == 0x0000) && (*(uint16 *)(cart.rom + 0x06) == 0x0104) && (rominfo.checksum == 0x31fc))
  {
    /* Micro Machines (USA) custom TMSS bypass logic */
    m68k.memory_map[0xa1].write8  = mapper_sr16v1_w;
  }
  else if (cart.romsize > 0x400000)
  {
    /* assume linear ROM mapping by default (max. 10MB) */
    for (i=0x40; i<0xA0; i++)
    {
      m68k.memory_map[i].base   = cart.rom + (i<<16);
      m68k.memory_map[i].read8  = NULL;
      m68k.memory_map[i].read16 = NULL;
      zbank_memory_map[i].read  = NULL;
    }
  }

  /* default write handler for !TIME range ($A130xx)*/
  if (!cart.hw.time_w)
  {
    cart.hw.time_w = default_time_w;
  }

  /**********************************************
          LOCK-ON 
  ***********************************************/

  /* clear existing patches */
  ggenie_shutdown();
  areplay_shutdown();

  /* initialize extra hardware */
  switch (config.lock_on)
  {
    case TYPE_GG:
    {
      ggenie_init();
      break;
    }

    case TYPE_AR:
    {
      areplay_init();
      break;
    }

    case TYPE_SK:
    {
      /* store Sonic & Knuckles ROM files after cartridge ROM area */
      if (cart.romsize > 0x400000) break;

      /* try to load Sonic & Knuckles ROM file (2MB) */
      if (load_archive(SK_ROM, cart.rom + 0x400000, 0x200000, NULL) == 0x200000)
      {
        /* check ROM header */
        if (!memcmp(cart.rom + 0x400000 + 0x120, "SONIC & KNUCKLES",16))
        {
          /* try to load Sonic 2 & Knuckles upmem ROM file (256KB) */
          if (load_archive(SK_UPMEM, cart.rom + 0x600000, 0x40000, NULL) == 0x40000)
          {
            /* $000000-$1FFFFF is mapped to S&K ROM */
            for (i=0x00; i<0x20; i++)
            {
              m68k.memory_map[i].base = cart.rom + 0x400000 + (i << 16);
            }

#ifdef LSB_FIRST
            for (i=0; i<0x200000; i+=2)
            {
              /* Byteswap ROM */
              uint8 temp = cart.rom[i + 0x400000];
              cart.rom[i + 0x400000] = cart.rom[i + 0x400000 + 1];
              cart.rom[i + 0x400000 + 1] = temp;
            }

            for (i=0; i<0x40000; i+=2)
            {
              /* Byteswap ROM */
              uint8 temp = cart.rom[i + 0x600000];
              cart.rom[i + 0x600000] = cart.rom[i + 0x600000 + 1];
              cart.rom[i + 0x600000 + 1] = temp;
            }
#endif
            cart.special |= HW_LOCK_ON;
          }
        }
      }
      break;
    }

    default:
    {
      break;
    }
  }

  /**********************************************
        MEGASD ADD-ON
  ***********************************************/
  /* enable MegaSD overlay for cartridge ROM (max. 8MB) when Mega CD hardware is disabled and either MegaSD add-on is forced enabled or automatic add-on detection is enabled and MegaSD compatible disc image is loaded */
  if ((cart.romsize <= 0x800000) && (system_hw == SYSTEM_MD) && ((config.add_on == HW_ADDON_MEGASD) || ((config.add_on | cdd.loaded) == HW_ADDON_MEGASD)))
  {
    cart.special |= HW_MEGASD;
  }

  /* force Mega CD sound hardware initialization when MegaSD overlay is enabled (if not already initialized)  */
  if ((cart.special & HW_MEGASD) && !snd.blips[1] && !snd.blips[2])
  {
    /* allocate & clear blip buffers for PCM and CD-DA audio streams */
    snd.blips[1] = blip_new(snd.sample_rate / 10);
    snd.blips[2] = blip_new(snd.sample_rate / 10);
    if (snd.blips[1]) blip_clear(snd.blips[1]);
    if (snd.blips[2]) blip_clear(snd.blips[2]);

    /* initialize PCM and CD-DA audio */
    audio_set_rate(snd.sample_rate, snd.frame_rate);
  }
}

/* hardware that need to be reseted on power on */
void md_cart_reset(int hard_reset)
{
  int i;

  /* Realtec mapper */
  if (cart.hw.realtec)
  {
    /* Boot ROM (8KB mirrored) is mapped to $000000-$3FFFFF */
    for (i=0x00; i<0x40; i++)
    {
      m68k.memory_map[i].base = cart.rom + 0x400000;
    }

    /* Reset mapper */
    cart.hw.regs[0] = 0;
    cart.hw.regs[1] = 0;
    cart.hw.regs[2] = 0;
  }

  /* reset cartridge mapping */
  else if (cart.hw.bankshift)
  {
    for (i=0x00; i<0x40; i++)
    {
      m68k.memory_map[i].base = cart.rom + ((i<<16) & cart.mask);
    }
  }

  /* MegaSD hardware */
  if (cart.special & HW_MEGASD)
  {
    megasd_reset();
  }

  /* YX5200 hardware */
  if (cart.special & HW_YX5200)
  {
    yx5200_reset();
  }

  /* SVP chip */
  if (svp)
  {
    svp_reset();
  }

  /* Lock-ON */
  switch (config.lock_on)
  {
    case TYPE_GG:
    {
      ggenie_reset(hard_reset);
      break;
    }

    case TYPE_AR:
    {
      areplay_reset(hard_reset);
      break;
    }

    case TYPE_SK:
    {
      if (cart.special & HW_LOCK_ON)
      {
        /* disable UPMEM chip at $300000-$3fffff */
        for (i=0x30; i<0x40; i++)
        {
          m68k.memory_map[i].base = cart.rom + ((i<<16) & cart.mask);
        }
      }
      break;
    }

    default:
    {
      break;
    }
  }
}

int md_cart_context_save(uint8 *state)
{
  int i;
  int bufferptr = 0;
  uint8 *base;

  /* cartridge mapping */
  for (i=0; i<0x40; i++)
  {
    /* get base address */
    base = m68k.memory_map[i].base;

    if (base == sram.sram)
    {
      /* SRAM */
      state[bufferptr++] = 0xff;
    }
    else if (base == boot_rom)
    {
      /* Boot ROM */
      state[bufferptr++] = 0xfe;
    }
    else
    {
      /* Cartridge ROM */
      state[bufferptr++] = ((base - cart.rom) >> 16) & 0xff;
    }
  }

  /* hardware registers */
  save_param(cart.hw.regs, sizeof(cart.hw.regs));

  /* SVP */
  if (svp)
  {
    save_param(svp->iram_rom, 0x800);
    save_param(svp->dram,sizeof(svp->dram));
    save_param(&svp->ssp1601,sizeof(ssp1601_t));
  }

  /* MegaSD hardware */
  if (cart.special & HW_MEGASD)
  {
    bufferptr += megasd_context_save(&state[bufferptr]);
  }

  /* Flash memory hardware */
  if (m68k.memory_map[0x00].read8 == mapper_flash_r8)
  {
    bufferptr += flash_cfi_context_save(&state[bufferptr]);
  }

  /* YX5200 hardware */
  if (cart.special & HW_YX5200)
  {
    bufferptr += yx5200_context_save(&state[bufferptr]);
  }

  return bufferptr;
}

int md_cart_context_load(uint8 *state)
{
  int i;
  int bufferptr = 0;
  uint8 offset;

  /* cartridge mapping */
  for (i=0; i<0x40; i++)
  {
    /* get offset */
    offset = state[bufferptr++];

    if (offset == 0xff)
    {
      /* SRAM */
      m68k.memory_map[i].base     = sram.sram;
      m68k.memory_map[i].read8    = sram_read_byte;
      m68k.memory_map[i].read16   = sram_read_word;
      m68k.memory_map[i].write8   = sram_write_byte;
      m68k.memory_map[i].write16  = sram_write_word;
      zbank_memory_map[i].read    = sram_read_byte;
      zbank_memory_map[i].write   = sram_write_byte;

    }
    else
    {
      /* check if SRAM was mapped there before loading state */
      if (m68k.memory_map[i].base == sram.sram)
      {
        m68k.memory_map[i].read8    = NULL;
        m68k.memory_map[i].read16   = NULL;
        m68k.memory_map[i].write8   = m68k_unused_8_w;
        m68k.memory_map[i].write16  = m68k_unused_16_w;
        zbank_memory_map[i].read    = NULL;
        zbank_memory_map[i].write   = zbank_unused_w;
      }

      /* ROM */
      m68k.memory_map[i].base = (offset == 0xfe) ? boot_rom : (cart.rom + (offset << 16));
    }
  }

  /* hardware registers */
  load_param(cart.hw.regs, sizeof(cart.hw.regs));

  /* SVP */
  if (svp)
  {
    load_param(svp->iram_rom, 0x800);
    load_param(svp->dram,sizeof(svp->dram));
    load_param(&svp->ssp1601,sizeof(ssp1601_t));
  }

  /* MegaSD hardware */
  if (cart.special & HW_MEGASD)
  {
    bufferptr += megasd_context_load(&state[bufferptr]);
  }

  /* Flash memory hardware */
  if (m68k.memory_map[0x00].read8 == mapper_flash_r8)
  {
    bufferptr += flash_cfi_context_load(&state[bufferptr]);
  }

  /* YX5200 hardware */
  if (cart.special & HW_YX5200)
  {
    bufferptr += yx5200_context_load(&state[bufferptr]);
  }

  return bufferptr;
}

/************************************************************
          MAPPER handlers 
*************************************************************/

/* 
  "official" ROM/SRAM bankswitch (Phantasy Star IV, Story of Thor/Beyond Oasis, Sonic 3 & Knuckles)
*/
static void mapper_sega_w(uint32 data)
{
  int i;

  if (data & 1)
  {
    /* check Backup RAM is enabled */
    if (sram.on)
    {
      /* check Backup RAM is currently not mapped */
      if (m68k.memory_map[0x20].base != sram.sram)
      {
        /* save current cartridge ROM bank mapped to $200000-$20ffff (for games using SRAM & ROM bankswitching) */
        cart.hw.regs[0] = ((m68k.memory_map[0x20].base - cart.rom) >> 16) & 0xff;

        /* Backup RAM mapped to $200000-$20ffff (normally mirrored up to $3fffff but this breaks Sonic Megamix and no game need it) */
        m68k.memory_map[0x20].base    = sram.sram;
        m68k.memory_map[0x20].read8   = sram_read_byte;
        m68k.memory_map[0x20].read16  = sram_read_word;
        zbank_memory_map[0x20].read   = sram_read_byte;
      }

      /* Backup RAM write protection */
      if (data & 2)
      {
        m68k.memory_map[0x20].write8  = m68k_unused_8_w;
        m68k.memory_map[0x20].write16 = m68k_unused_16_w;
        zbank_memory_map[0x20].write  = zbank_unused_w;
      }
      else
      {
        m68k.memory_map[0x20].write8  = sram_write_byte;
        m68k.memory_map[0x20].write16 = sram_write_word;
        zbank_memory_map[0x20].write  = sram_write_byte;
      }
    }

    /* S&K lock-on chip */
    if (cart.special & HW_LOCK_ON)
    {
      /* S2K upmem chip mapped to $300000-$3fffff (256KB mirrored) */
      for (i=0x30; i<0x40; i++)
      {
        m68k.memory_map[i].base = (cart.rom + 0x600000) + ((i & 3) << 16);
      }
    }
  }
  else
  {
    /* check Backup RAM is currently mapped */
    if (m68k.memory_map[0x20].base == sram.sram)
    {
      /* current cartridge ROM bank mapped to $200000-$20ffff */
      m68k.memory_map[0x20].base    = cart.rom + (cart.hw.regs[0] << 16);
      m68k.memory_map[0x20].read8   = NULL;
      m68k.memory_map[0x20].read16  = NULL;
      zbank_memory_map[0x20].read   = NULL;
      m68k.memory_map[0x20].write8  = m68k_unused_8_w;
      m68k.memory_map[0x20].write16 = m68k_unused_16_w;
      zbank_memory_map[0x20].write  = zbank_unused_w;
    }

    /* S&K lock-on chip */
    if (cart.special & HW_LOCK_ON)
    {
      /* cartridge ROM mapped to $300000-$3fffff */
      for (i=0x30; i<0x40; i++)
      {
        m68k.memory_map[i].base = cart.rom + ((i<<16) & cart.mask);
      }
    }
  }
}

/*
   Everdrive extended SSF ROM bankswitch
   documented by Krikzz (http://krikzz.com/pub/support/mega-ed/dev/extended_ssf.txt) 
*/
static void mapper_512k_w(uint32 address, uint32 data)
{
  uint32 i;

  /* 512K ROM paging */
  uint8 *src = cart.rom + ((data << 19) & cart.mask);

  /* cartridge area ($000000-$3FFFFF) is divided into 8 x 512K banks */
  address = (address << 2) & 0x38;
  
  /* remap selected ROM page to selected bank */
  for (i=0; i<8; i++)
  {
    m68k.memory_map[address++].base = src + (i<<16);
  }
}

/*
   Super Street Fighter 2 ROM bankswitch
   documented by Bart Trzynadlowski (http://emu-docs.org/Genesis/ssf2.txt) 
*/
static void mapper_ssf2_w(uint32 address, uint32 data)
{
  /* only banks 1-7 are remappable, bank 0 remains unchanged */
  if (address & 0x0E)
  {
    mapper_512k_w(address, data);
  }
}

/* 
  SF-001 mapper
*/
static void mapper_sf001_w(uint32 address, uint32 data)
{
  switch ((address >> 8) & 0xf)
  {
    case 0xe:
    {
      int i;

      /* bit 6: enable / disable cartridge access  */
      if (data & 0x40)
      {
        /* $000000-$3FFFFF is not mapped */
        for (i=0x00; i<0x40; i++)
        {
          m68k.memory_map[i].base     = cart.rom + (i << 16);
          m68k.memory_map[i].read8    = m68k_read_bus_8;
          m68k.memory_map[i].read16   = m68k_read_bus_16;
          m68k.memory_map[i].write8   = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
          m68k.memory_map[i].write16  = (i > 0x00) ? m68k_unused_16_w : mapper_sf001_w;
          zbank_memory_map[i].read    = zbank_unused_r;
          zbank_memory_map[i].write   = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
        }
      }

      /* bit 7: enable / disable SRAM & ROM bankswitching */
      else if (data & 0x80)
      {
        /* 256K ROM bank #15 mapped to $000000-$03FFFF  */
        for (i=0x00; i<0x04; i++)
        {
          m68k.memory_map[i].base     = cart.rom + ((0x38 + i) << 16);
          m68k.memory_map[i].read8    = NULL;
          m68k.memory_map[i].read16   = NULL;
          zbank_memory_map[i].read    = NULL;
        }

        /* 256K ROM banks #2 to #15 mapped to $040000-$3BFFFF (last revision) or $040000-$3FFFFF (older revisions) */
        for (i=0x04; i<(sram.start >> 16); i++)
        {
          m68k.memory_map[i].base     = cart.rom + (i << 16);
          m68k.memory_map[i].read8    = NULL;
          m68k.memory_map[i].read16   = NULL;
          zbank_memory_map[i].read    = NULL;
        }

        /* 32K static RAM mirrored into $3C0000-$3FFFFF (odd bytes only) (last revision only) */
        while (i<0x40)
        {
          m68k.memory_map[i].base     = sram.sram;
          m68k.memory_map[i].read8    = sram_read_byte;
          m68k.memory_map[i].read16   = sram_read_word;
          m68k.memory_map[i].write8   = sram_write_byte;
          m68k.memory_map[i].write16  = sram_write_word;
          zbank_memory_map[i].read    = sram_read_byte;
          zbank_memory_map[i].write   = sram_write_byte;
          i++;
        }
      }
      else
      {
        /* 256K ROM banks #1 to #16 mapped to $000000-$3FFFFF (default) */
        for (i=0x00; i<0x40; i++)
        {
          m68k.memory_map[i].base     = cart.rom + (i << 16);
          m68k.memory_map[i].read8    = NULL;
          m68k.memory_map[i].read16   = NULL;
          m68k.memory_map[i].write8   = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
          m68k.memory_map[i].write16  = (i > 0x00) ? m68k_unused_16_w : mapper_sf001_w;
          zbank_memory_map[i].read    = NULL;
          zbank_memory_map[i].write   = (i > 0x00) ? m68k_unused_8_w : mapper_sf001_w;
        }
      }

      /* bit 5: lock bankswitch hardware when set */
      if (data & 0x20)
      {
        /* disable bankswitch hardware access until hard reset */
        m68k.memory_map[0x00].write8  = m68k_unused_8_w;
        m68k.memory_map[0x00].write16 = m68k_unused_16_w;
        zbank_memory_map[0x00].write  = m68k_unused_8_w;
      }

      return;
    }

    default:
    {
      m68k_unused_8_w(address, data);
      return;
    }
  }
}

/* 
  SF-002 mapper
*/
static void mapper_sf002_w(uint32 address, uint32 data)
{
  int i;
  if (data & 0x80)
  {
    /* $000000-$1BFFFF mapped to $200000-$3BFFFF */
    for (i=0x20; i<0x3C; i++)
    {
      m68k.memory_map[i].base = cart.rom + ((i & 0x1F) << 16);
    }
  }
  else
  {
    /* $200000-$3BFFFF mapped to $200000-$3BFFFF */
    for (i=0x20; i<0x3C; i++)
    {
      m68k.memory_map[i].base = cart.rom + (i << 16);
    }
  }
}

/* 
  SF-004 mapper
*/
static void mapper_sf004_w(uint32 address, uint32 data)
{
  int i;
  switch ((address >> 8) & 0xf)
  {
    case 0xd:
    {
      /* bit 7: enable/disable static RAM access */
      if (data & 0x80)
      {
        /* 32KB static RAM mirrored into $200000-$2FFFFF (odd bytes only) */
        for (i=0x20; i<0x30; i++)
        {
          m68k.memory_map[i].read8   = sram_read_byte;
          m68k.memory_map[i].read16  = sram_read_word;
          m68k.memory_map[i].write8  = sram_write_byte;
          m68k.memory_map[i].write16 = sram_write_word;
          zbank_memory_map[i].read   = sram_read_byte;
          zbank_memory_map[i].write  = sram_write_byte;
        }
      }
      else
      {
        /* 32KB static RAM disabled at $200000-$2FFFFF */
        for (i=0x20; i<0x30; i++)
        {
          m68k.memory_map[i].read8   = m68k_read_bus_8;
          m68k.memory_map[i].read16  = m68k_read_bus_16;
          m68k.memory_map[i].write8  = m68k_unused_8_w;
          m68k.memory_map[i].write16 = m68k_unused_16_w;
          zbank_memory_map[i].read   = m68k_read_bus_8;
          zbank_memory_map[i].write  = m68k_unused_8_w;
        }
      }

      return;
    }

    case 0x0e:
    {
      /* bit 5: enable / disable cartridge ROM access */
      if (data & 0x20)
      {
        /* $000000-$1FFFFF is not mapped */
        for (i=0x00; i<0x20; i++)
        {
          m68k.memory_map[i].read8  = m68k_read_bus_8;
          m68k.memory_map[i].read16 = m68k_read_bus_16;
          zbank_memory_map[i].read  = m68k_read_bus_8;
        }
      }

      /* bit 6: enable / disable first page mirroring */
      else if (data & 0x40)
      {
        /* first page ROM bank */
        uint8 base = (m68k.memory_map[0x00].base - cart.rom) >> 16;

        /* 5 x 256K ROM banks mapped to $000000-$13FFFF, starting from first page ROM bank */
        for (i=0x00; i<0x14; i++)
        {
          m68k.memory_map[i].base   = cart.rom + (((base + i) & 0x1f) << 16);
          m68k.memory_map[i].read8  = NULL;
          m68k.memory_map[i].read16 = NULL;
          zbank_memory_map[i].read  = NULL;
        }

        /* $140000-$1FFFFF is not mapped */
        for (i=0x14; i<0x20; i++)
        {
          m68k.memory_map[i].read8  = m68k_read_bus_8;
          m68k.memory_map[i].read16 = m68k_read_bus_16;
          zbank_memory_map[i].read  = m68k_read_bus_8;
        }
      }
      else
      {
        /* first page 256K ROM bank mirrored into $000000-$1FFFFF */
        for (i=0x00; i<0x20; i++)
        {
          m68k.memory_map[i].base = m68k.memory_map[0].base + ((i & 0x03) << 16);
          m68k.memory_map[i].read8  = NULL;
          m68k.memory_map[i].read16 = NULL;
          zbank_memory_map[i].read  = NULL;
        }
      }

      /* bit 7: lock ROM bankswitching hardware when cleared */
      if (!(data & 0x80))
      {
        /* disable bankswitch hardware access */
        m68k.memory_map[0x00].write8 = m68k_unused_8_w;
        m68k.memory_map[0x00].write16 = m68k_unused_16_w;
        zbank_memory_map[0x00].write = m68k_unused_8_w;
      }

      return;
    }

    case 0x0f:
    {
      /* bits 6-4: select first page ROM bank (8 x 256K ROM banks) */
      uint8 base = ((data >> 4) & 7) << 2;

      if (m68k.memory_map[0].base == m68k.memory_map[4].base)
      {
        /* selected 256K ROM bank mirrored into $000000-$1FFFFF */
        for (i=0x00; i<0x20; i++)
        {
          m68k.memory_map[i].base = cart.rom + ((base + (i & 0x03)) << 16);
        }
      }
      else
      {
        /* 5 x 256K ROM banks mapped to $000000-$13FFFF, starting from selected bank */
        for (i=0x00; i<0x14; i++)
        {
          m68k.memory_map[i].base = cart.rom + (((base + i) & 0x1f) << 16);
        }
      }

      return;
    }

    default:
    {
      m68k_unused_8_w(address, data);
      return;
    }
  }
}

static uint32 mapper_sf004_r(uint32 address)
{
  /* return first page 256K bank index ($00,$10,$20,...,$70) */
  return (((m68k.memory_map[0x00].base - cart.rom) >> 18) << 4);
}

/* 
  T-5740xx-xx mapper
*/
static void mapper_t5740_w(uint32 address, uint32 data)
{
  int i;
  uint8 *base;

  switch (address & 0xff)
  {
    case 0x01: /* mode register */
    {
      /* bits 7-4: unused ?                           */
      /* bit 3: enable SPI registers access ?         */
      /* bit 2: not used ?                            */
      /* bit 1: enable bankswitch registers access ?  */
      /* bit 0: always set, enable hardware access ?  */
      return;
    }

    case 0x03: /* page #5 register */
    {
      /* map any of 16 x 512K ROM banks to $280000-$2FFFFF */
      base = cart.rom + ((data & 0x0f) << 19);
      for (i=0x28; i<0x30; i++)
      {
        m68k.memory_map[i].base = base + ((i & 0x07) << 16);
      }
      return;
    }

    case 0x05: /* page #6 register */
    {
      /* map any of 16 x 512K ROM banks to $300000-$37FFFF */
      base = cart.rom + ((data & 0x0f) << 19);
      for (i=0x30; i<0x38; i++)
      {
        m68k.memory_map[i].base = base + ((i & 0x07) << 16);
      }
      return;
    }

    case 0x07: /* page #7 register */
    {
      /* map any of 16 x 512K ROM banks to $380000-$3FFFFF */
      base = cart.rom + ((data & 0x0f) << 19);
      for (i=0x38; i<0x40; i++)
      {
        m68k.memory_map[i].base = base + ((i & 0x07) << 16);
      }
      return;
    }

    case 0x09: /* serial EEPROM SPI board support */
    {
      eeprom_spi_write(data);
      return;
    }

    default:
    {
      /* unknown registers */
      m68k_unused_8_w(address, data);
      return;
    }
  }
}

static uint32 mapper_t5740_r(uint32 address)
{
  /* By default, first 32K of each eight 512K pages mapped in $000000-$3FFFFF are mirrored in the 512K page   */
  /* mirroring is disabled/enabled when a specific number of words is being read from specific ROM addresses  */
  /* Exact decoding isn't known but mirrored data is expected on startup when reading a few times from $181xx */
  /* this area doesn't seem to be accessed as byte later so it seems safe to always return mirrored data here */
  if ((address & 0xff00) == 0x8100)
  {
    return READ_BYTE(cart.rom , (address & 0x7fff));
  }

  return READ_BYTE(cart.rom, address);
}

/* 
  Flash memory mappers
*/
static void mapper_flash_w8(uint32 address, uint32 data)
{
  /* only /LWR is connected (verified on PCB hardware) */
  if (address & 1)
  {
    flash_cfi_write(address >> 1, (data << 8) | (data & 0xff));
  }
  else
  {
    m68k_unused_8_w(address, data);
  }
}

static void mapper_flash_w16(uint32 address, uint32 data)
{
  flash_cfi_write(address >> 1, data);
}

static uint32 mapper_flash_r8(uint32 address)
{
  uint32 data = flash_cfi_read(address >> 1);
  return (address & 0x01) ? (data & 0xff) : (data >> 8);;
}

static uint32 mapper_flash_r16(uint32 address)
{
  return flash_cfi_read(address >> 1);;
}

/* 
  KAISER WAVE board mapper
*/
static void mapper_kaiserwave_w(uint32 address, uint32 data)
{
  /* /UWR only */
  if (!(address & 1))
  {
    /* YX5200 RX input line is mapped to D0 */
    yx5200_write(data & 0x01);
  }
  else
  {
    m68k_unused_8_w(address, data);
  }
}
 
/* 
  Super Mario World 64 (unlicensed) mapper
*/
static void mapper_smw_64_w(uint32 address, uint32 data)
{
  /* internal registers (saved to backup RAM) */
  switch ((address >> 16) & 0x07)
  {
    case 0x00:  /* $60xxxx */
    {
      if (address & 2)
      {
        /* $600003 data write mode ? */
        switch (sram.sram[0x00] & 0x07)
        {
          case 0x00:
          {
            /* update value returned at $660001-$660003 */
            sram.sram[0x06] = ((sram.sram[0x06] ^ sram.sram[0x01]) ^ data) & 0xFE;
            break;
          }

          case 0x01:
          {
            /* update value returned at $660005-$660007 */
            sram.sram[0x07] = data & 0xFE;
            break;
          }

          case 0x07:
          {
            /* update selected ROM bank (upper 512K) mapped at $610000-$61ffff */
            m68k.memory_map[0x61].base = m68k.memory_map[0x69].base = cart.rom + 0x080000 + ((data & 0x1c) << 14);
            break;
          }

          default:
          {
            /* unknown mode */
            break;
          }
        }

        /* $600003 data register */
        sram.sram[0x01] = data;
      }
      else
      {
        /* $600001 ctrl register */
        sram.sram[0x00] = data;
      }
      return;
    }

    case 0x01:  /* $61xxxx */
    {
      if (address & 2)
      {
        /* $610003 ctrl register */
        sram.sram[0x02] = data;
      }
      return;
    }

    case 0x04:  /* $64xxxx */
    {
      if (address & 2)
      {
        /* $640003 data register */
        sram.sram[0x04] = data;
      }
      else
      {
        /* $640001 data register */
        sram.sram[0x03] = data;
      }
      return;
    }

    case 0x06:  /* $66xxxx */
    {
      /* unknown */
      return;
    }

    case 0x07:  /* $67xxxx */
    {
      if (!(address & 2))
      {
        /* $670001 ctrl register */
        sram.sram[0x05] = data;

        /* upper 512K ROM bank-switching enabled ? */
        if (sram.sram[0x02] & 0x80)
        {
          /* update selected ROM bank (upper 512K) mapped at $600000-$60ffff */
          m68k.memory_map[0x60].base = m68k.memory_map[0x68].base = cart.rom + 0x080000 + ((data & 0x1c) << 14);
        }
      }
      return;
    }

    default:  /* not used */
    {
      m68k_unused_8_w(address, data);
      return;
    }
  }
}

static uint32 mapper_smw_64_r(uint32 address)
{
  /* internal registers (saved to backup RAM) */
  switch ((address >> 16) & 0x03)
  {
    case 0x02:  /* $66xxxx */
    {
      switch ((address >> 1) & 7)
      {
        case 0x00:  return sram.sram[0x06];
        case 0x01:  return sram.sram[0x06] + 1;
        case 0x02:  return sram.sram[0x07];
        case 0x03:  return sram.sram[0x07] + 1;
        case 0x04:  return sram.sram[0x08];
        case 0x05:  return sram.sram[0x08] + 1;
        case 0x06:  return sram.sram[0x08] + 2;
        case 0x07:  return sram.sram[0x08] + 3;
      }
    }

    case 0x03:  /* $67xxxx */
    {
      uint8 data = (sram.sram[0x02] & 0x80) ? ((sram.sram[0x05] & 0x40) ? (sram.sram[0x03] & sram.sram[0x04]) : (sram.sram[0x03] ^ 0xFF)) : 0x00;

      if (address & 2)
      {
        /* $670003 */
        data &= 0x7f;
      }
      else
      {
        /* $66xxxx data registers update */
        if (sram.sram[0x05] & 0x80)
        {
          if (sram.sram[0x05] & 0x20)
          {
            /* update $660009-$66000f data register */
            sram.sram[0x08] = (sram.sram[0x04] << 2) & 0xFC;
          }
          else
          {
            /* update $660001-$660003 data register */
            sram.sram[0x06] = (sram.sram[0x01] ^ (sram.sram[0x03] << 1)) & 0xFE;
          }
        }
      }

      return data;
    }

    default:  /* 64xxxx-$65xxxx */
    {
      return 0x00;
    }
  }
}

/* 
  Realtec ROM bankswitch (Earth Defend, Balloon Boy & Funny World, Whac-A-Critter, Tom Clown)
  Verified with real cartridge hardware (slightly different from behavior described in TascoDlx documentation)
*/
static void mapper_realtec_w(uint32 address, uint32 data)
{
  switch (address)
  {
    case 0x402000: 
    {
      /* fixed ROM bank size */
      /* when bit 0 is set, ROM A16 pin is forced to value configured in register below (connected to VA17 otherwise) */
      /* when bit 1 is set, ROM A17 pin is forced to value configured in register below (connected to VA18 otherwise) */
      /* other bits have no effect */
      cart.hw.regs[1] = (data & 3) << 1;
      return;
    }

    case 0x404000: 
    {
      /* fixed ROM bank selection (4 x 128KB banks) */
      /* bit 0 corresponds to ROM A16 pin value when forced */
      /* bit 1 corresponds to ROM A17 pin value when forced */
      /* other bits have no effect */
      cart.hw.regs[2] = (data & 3) << 1;
      return;
    }

    case 0x400000:  
    {
      /* ROM access enable */
      /* when bit 0 is set, ROM A17/A16 pins are set according to above registers and ROM A15/A12 pins are connected to VA16-VA13 (forced to 1 on reset) */
      /* other bits have no effect */
      if (data & 0x01)
      {
        /* once ROM access is enabled, ROM mapping can not be modified until next reset */
        if (!cart.hw.regs[0])
        {
          int i;
          for (i=0x00; i<0x40; i++)
          {
            /* 0x000000-0x07ffff mapped area is mirrored in 4MB cartridge range (VA21-VA19 not connected) */
            uint32 base = i & 7;

            /* adjust 64k mapped area ROM base address according to fixed ROM bank configuration (see above) */
            base = (base & ~cart.hw.regs[1]) | (cart.hw.regs[2] & cart.hw.regs[1]);

            m68k.memory_map[i].base = &cart.rom[base << 16];
          }

          cart.hw.regs[0] = data;
        }
      }
      return;
    }
  }
}

static uint32 mapper_realtec_r(uint32 address)
{
  /* /VRES is asserted to bypass TMSS licensing screen when first read access to cartridge ROM header is detected */
  if ((address == 0x100) && (m68k.memory_map[0].base = cart.base))
  {
    /* asserting /VRES from cartridge should only reset 68k CPU (TODO: confirm this on real hardware) */
    m68k_pulse_reset();
  }

  /* default ROM area read handler */
  return *(uint16 *)(m68k.memory_map[0].base + (address & 0xfffe));
}

/* Game no Kanzume Otokuyou ROM Mapper */
static void mapper_seganet_w(uint32 address, uint32 data)
{
  if ((address & 0xff) == 0xf1)
  {
    int i;
    if (data & 1)
    {
      /* ROM Write protected */
      for (i=0; i<0x40; i++)
      {
        m68k.memory_map[i].write8   = m68k_unused_8_w;
        m68k.memory_map[i].write16  = m68k_unused_16_w;
        zbank_memory_map[i].write   = zbank_unused_w;
      }
    }
    else
    {
      /* ROM Write enabled */
      for (i=0; i<0x40; i++)
      {
        m68k.memory_map[i].write8   = NULL;
        m68k.memory_map[i].write16  = NULL;
        zbank_memory_map[i].write   = NULL;
      }
    }
  }
}

/* 
  Custom ROM Bankswitch used in Soul Edge VS Samurai Spirits, Top Fighter, Mulan, Pocket Monsters II, Lion King 3, Super King Kong 99, Pokemon Stadium
*/
static void mapper_32k_w(uint32 data)
{
  int i;
  
  /* 64 x 32k banks */
  if (data)
  {
    for (i=0; i<0x10; i++)
    {
      /* Remap to unused ROM area  */
      m68k.memory_map[i].base = &cart.rom[0x400000 + (i << 16)];

      /* address = address OR (value << 15) */
      memcpy(m68k.memory_map[i].base, cart.rom + ((i << 16) | (data & 0x3f) << 15), 0x8000);
      memcpy(m68k.memory_map[i].base + 0x8000, cart.rom + ((i << 16) | ((data | 1) & 0x3f) << 15), 0x8000);
    }
  }
  else
  {
    /* reset default $000000-$0FFFFF mapping */
    for (i=0; i<16; i++)
    {
      m68k.memory_map[i].base = &cart.rom[i << 16];
    }
  }
}

/* 
  Custom ROM Bankswitch used in Chinese Fighter III
*/
static void mapper_64k_w(uint32 data)
{
  int i;

  /* 16 x 64k banks */
  if (data)
  {
    /* bank is mapped at $000000-$0FFFFF */
    for (i=0; i<16; i++)
    {
      m68k.memory_map[i].base = &cart.rom[(data & 0xf) << 16];
    }
  }
  else
  {
    /* reset default $000000-$0FFFFF mapping */
    for (i=0; i<16; i++)
    {
      m68k.memory_map[i].base = &cart.rom[(i & 0xf) << 16];
    }
  }
}

/* 
  Custom ROM Bankswitch used in pirate "Multi-in-1" cartridges, A Bug's Life, King of Fighter 99, Pocket Monster, Rockman X3
 */
static void mapper_64k_multi_w(uint32 address)
{
  int i;

  /* 64 x 64k banks */
  for (i=0; i<64; i++)
  {
    m68k.memory_map[i].base = &cart.rom[((address + i) & 0x3f) << 16];
  }
}

/* 
  Custom ROM Bankswitch used in pirate "1800-in-1" cartridge
 */
static uint32 mapper_128k_multi_r(uint32 address)
{
  int i;

  /* 16 x 128k banks (2MB ROM) */
  /* Bank index (B3 B2 B1 B0) is encoded in address lower byte = {0 X B0 B1 X B2 B3 0} */
  /* Note: {0 B0 X B1 X B2 B3 0} also works, see below for the 9 unique values being used for all menu entries
      read16 00A13000 (0002FBEE) => 0x000000-0x03ffff (2x128KB)
      read16 00A13018 (00FF2056) => 0x040000-0x07ffff (2x128KB)
      read16 00A13004 (00FF2120) => 0x080000-0x0bffff (2x128KB)
      read16 00A1301C (00FF20A6) => 0x0c0000-0x0fffff (2x128KB)
      read16 00A1300A (00FF20BA) => 0x100000-0x13ffff (2x128KB)
      read16 00A1301A (00FF20CE) => 0x140000-0x17ffff (2x128KB)
      read16 00A1300E (00FF20F4) => 0x180000-0x1bffff (2x128KB)
      read16 00A1301E (00FF2136) => 0x1c0000-0x1dffff (1x128KB)
      read16 00A1307E (00FF2142) => 0x1e0000-0x1fffff (1x128KB)
  */
  int bank = ((address & 0x02) << 2) | (address & 0x04) | ((address & 0x10) >> 3) | ((address & 0x20) >> 5);

  /* remap cartridge area (64 x 64k banks) */
  address = bank << 1;
  for (i=0x00; i<0x40; i++)
  {
    m68k.memory_map[i].base = &cart.rom[((address + i) & 0x3f) << 16];
  }

  /* returned value changes the menu title and number of entries in the 'game' list (the number of distinct games does not change though) */
  /* 0x00 => 9-in-1 */
  /* 0x01 => 190-in-1 */
  /* 0x02 => 888-in-1 */
  /* 0x03 => 1800-in-1 */
  /* real cartridge board has switches to select between the four different menus but here we force the largest menu selection (each other menus being a subset of the next larger menu) */
  return 0x03;
}

/* 
  Custom ROM Bankswitch used in pirate "Golden Mega 250-in-1" cartridge
 */
static void mapper_256k_multi_w(uint32 address, uint32 data)
{
  int i;

  /* 8 x 256k banks (2MB ROM) */
  /* Bank index (B2 B1 B0) is encoded in data lower byte = {B1 B0 X X 0 0 0 B2} */
  /* Note: {X B0 B1 B2 0 0 0 X}, {B1 B0 X B2 0 0 0 X} or {X B0 B1 X 0 0 0 B2} also work, see below for the 4 unique values being used for all menu entries
      write16 00089000 = 0000 (00FF0006) => 0x000000-0x03ffff (1x256KB)
      write16 00089000 = 0040 (00FF0006) => 0x040000-0x07ffff (1x256KB)
      write16 00089000 = 00A0 (00FF0006) => 0x080000-0x0fffff (2x256KB)
      write16 00089000 = 0011 (00FF0006) => 0x100000-0x1fffff (4x256KB)
  */
  int bank = ((data & 0x01) << 2) | ((data & 0xc0) >> 6);

  /* remap cartridge area (64 x 64k banks) */
  address = bank << 2;
  for (i=0x00; i<0x40; i++)
  {
    m68k.memory_map[i].base = &cart.rom[((address + i) & 0x3f) << 16];
  }
}

/* 
  Custom ROM Bankswitch used in "Canon - Legend of the New Gods" 
  (uses WD1601 QFPL V1.01 board also used in chinese X-in-1 pirates sold by mindkids)
 */
static void mapper_wd1601_w(uint32 address, uint32 data)
{
  int i;

  /* !TIME write16 0xA13002 = 0x3002 (00FFFE0C) */
  /* The board probably allows up to 256MB Flash ROM remapping but this game only has 4MB ROM chip */
  if ((address & 0xfe) == 0x02)
  {
    /* upper 2MB ROM mapped to $000000-$1fffff */
    for (i=0; i<0x20; i++)
    {
      m68k.memory_map[i].base = &cart.rom[(0x20 + i) << 16];
    }

    /* backup RAM (8KB) mapped to $2000000-$3fffff */
    for (i=0x20; i<0x40; i++)
    {
      m68k.memory_map[i].base    = sram.sram;
      m68k.memory_map[i].read8   = sram_read_byte;
      m68k.memory_map[i].read16  = sram_read_word;
      m68k.memory_map[i].write8  = sram_write_byte;
      m68k.memory_map[i].write16 = sram_write_word;
      zbank_memory_map[i].read   = sram_read_byte;
      zbank_memory_map[i].write  = sram_write_byte;
    }
  }
}

/*
  Custom ROM Bankswitch used in RADICA cartridges
  +++++++++++++++++++++++++++++++++++++++++++++++
   Two different boards seem to exist (one with support for 64KB banks mapping and another one supporting 128KB banks + battery-RAM).
   Radica Volume 1 requires 64KB banks mapping as the menu is located at a 64KB boundary.
   Sensible Soccer Plus edition requires 128KB banks mapping with only VA6-VA2 being used to select bank index (VA1 is ignored).
   Sensible Soccer Plus edition also requires 8KB backup RAM to be mapped in higher 2MB range.
   Other games support both 64KB or 128KB mapping so it's not clear what exact board they are using but none require SRAM so we use 64KB mapper by default.
   Note that Radica Volume 3 uses similar ROM mapping as Sensible Soccer Plus edition so it might be using same 128KB board, without any SRAM chip connected.
*/
static uint32 mapper_64k_radica_r(uint32 address)
{
  int i = 0;

  /* 64 x 64k banks */
  /*
    Volume 1
    --------
    000000h-0fffffh: Kid Chameleon                   : !TIME read16 0xA13000 (00FF103A)
    100000h-1fffffh: Dr Robotnik's Mean Bean Machine : !TIME read16 0xA13020 (00FF101E)
    200000h-27ffffh: Sonic The Hedgehog              : !TIME read16 0xA13040 (00FF101E)
    280000h-2fffffh: Golden Axe                      : !TIME read16 0xA13050 (00FF101E)
    300000h-37ffffh: Altered Beast                   : !TIME read16 0xA13060 (00FF101E)
    380000h-39ffffh: Flicky                          : !TIME read16 0xA13070 (00FF101E)
    3a0000h-3effffh: N/A                             : N/A
    3f0000h-3fffffh: Radica Menu (64 KB)             : !TIME read16 0xA1307E (00FF1006)

    Volume 2
    --------
    000000h-0fffffh: Sonic The Hedgehog 2            : !TIME read16 0xA13000 (00FF103A)
    100000h-1fffffh: The Ooze                        : !TIME read16 0xA13020 (00FF101E)
    200000h-2fffffh: Ecco The Dolphin                : !TIME read16 0xA13040 (00FF101E)
    300000h-37ffffh: Gain Ground                     : !TIME read16 0xA13060 (00FF101E)
    380000h-3bffffh: Alex Kidd in Enchanted Castle   : !TIME read16 0xA13070 (00FF101E)
    3c0000h-3dffffh: Columns                         : !TIME read16 0xA13078 (00FF101E)
    3e0000h-3fffffh: Radica Menu (128 KB)            : !TIME read16 0xA1307C (00FF1006)

    Volume 3 - Super Sonic Gold edition
    -----------------------------------
    000000h-01ffffh: Radica Menu (128 KB)            : N/A
    020000h-07ffffh: N/A                             : N/A
    080000h-0fffffh: Sonic The Hedgehog              : !TIME read16 0xA13010 (00FF1012)
    100000h-1fffffh: Sonic The Hedgehog 2            : !TIME read16 0xA13020 (00FF1012)
    200000h-2fffffh: Sonic Spinball                  : !TIME read16 0xA13040 (00FF1012)
    300000h-3fffffh: Dr Robotnik's Mean Bean Machine : !TIME read16 0xA13060 (00FF1012)

    Street Fighter 2 CE edition
    ---------------------------
    000000h-2fffffh: Street Fighter 2 CE             : !TIME read16 0xA13000 (00FF103A)
    300000h-3bffffh: Ghouls'n Ghosts                 : !TIME read16 0xA13060 (00FF101E)
    3c0000h-3dffffh: Radica Menu (128 KB)            : !TIME read16 0xA13078 (00FF1006)
    3e0000h-3fffffh: N/A                             : N/A
  */
  int index = (address >> 1) & 0x3F;

  /* $000000-$3fffff area is mapped to selected banks (OR gates between VA21-VA16 and selected index) */
  for (i = 0x00; i < 0x40; i++)
  {
    m68k.memory_map[i].base = &cart.rom[(index | i) << 16];
  }

  return 0xffff;
}

static uint32 mapper_128k_radica_r(uint32 address)
{
  int i = 0;

  /* 32 x 128k banks */
  /*
    Sensible Soccer Plus edition
    ----------------------------
    000000h-01ffffh: Radica Menu (128 KB)            : N/A
    020000h-07ffffh: N/A                             : N/A
    080000h-0fffffh: Sensible Soccer                 : !TIME read16 0xA13010 (00FF1012)
    100000h-1fffffh: Mega-Lo-Mania                   : !TIME read16 0xA13022 (00FF1012)
    200000h-37ffffh: Cannon Fodder                   : !TIME read16 0xA13042 (00FF1012)
    380000h-3fffffh: N/A                             : N/A

    Note: address bit 1 is ignored for bank selection but might be used to enable/disable SRAM mapping ? 
  */
  int index = (address >> 1) & 0x3E;

  /* $000000-$1fffff area is mapped to selected banks (OR gates between VA20-VA17 and selected index) */
  for (i = 0x00; i < 0x20; i++)
  {
    m68k.memory_map[i].base = &cart.rom[(index | i) << 16];
  }

  /* $200000-$3fffff area is mapped to 8KB SRAM (mirrored) */
  for (i = 0x20; i < 0x40; i++)
  {
    m68k.memory_map[i].base    = sram.sram;
    m68k.memory_map[i].read8   = sram_read_byte;
    m68k.memory_map[i].read16  = sram_read_word;
    m68k.memory_map[i].write8  = sram_write_byte;
    m68k.memory_map[i].write16 = sram_write_word;
    zbank_memory_map[i].read   = sram_read_byte;
    zbank_memory_map[i].write  = sram_write_byte;
  }

  return 0xffff;
}


/*
  Custom logic (ST 16S25HB1 PAL) used in Micro Machines USA cartridge (SR16V1.1 board)
  ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
   /VRES is asserted to bypass TMSS security checks when write access to 0xA141xx 
   with D0=1 is detected (access to cartridge ROM enabled instead of TMSS Boot ROM)
*/
static void mapper_sr16v1_w(uint32 address, uint32 data)
{
  /* default I/O and Control registers write handler */
  ctrl_io_write_byte(address, data);

  /* cartridge uses /LWR, /AS and VA1-VA18 (only VA8-VA17 are required to decode access to TMSS bankswitch register) */
  if ((address & 0xff01) == 0x4101)
  {
    /* check if cartridge ROM is enabled (D0=1) */
    if (data & 0x01)
    {
      /* asserting /VRES from cartridge should only reset 68k CPU (TODO: confirm this on real hardware) */
       m68k_pulse_reset();
    }
  }
}

/************************************************************
          default !TIME signal handler 
*************************************************************/

static void default_time_w(uint32 address, uint32 data)
{
  /* enable multi-game cartridge mapper by default */
  if (address < 0xa13060)
  {
    mapper_64k_multi_w(address);

    /* cartridge ROM mapping is reinitialized on /VRES */
    cart.hw.bankshift = 1;

    return;
  }

  /* enable "official" cartridge mapper by default */
  if (address > 0xa130f1)
  {
    mapper_512k_w(address, data);
  }
  else
  {
    mapper_sega_w(data);
  }
}


/************************************************************
          Internal register handlers
*************************************************************/

static uint32 default_regs_r(uint32 address)
{
  int i;
  for (i=0; i<4; i++)
  {
    if ((address & cart.hw.mask[i]) == cart.hw.addr[i])
    {
      return cart.hw.regs[i];
    }
  }
  return m68k_read_bus_8(address);
}

static uint32 default_regs_r_16(uint32 address)
{
  int i;
  for (i=0; i<4; i++)
  {
    if ((address & cart.hw.mask[i]) == cart.hw.addr[i])
    {
      return (cart.hw.regs[i] << 8);
    }
  }
  return m68k_read_bus_16(address);
}

static void default_regs_w(uint32 address, uint32 data)
{
  int i;
  for (i=0; i<4; i++)
  {
    if ((address & cart.hw.mask[i]) == cart.hw.addr[i])
    {
      cart.hw.regs[i] = data;
      return;
    }
  }
  m68k_unused_8_w(address, data);
}

/* basic register shifting hardware (Bug's Life, Pocket Monster) */
static uint32 custom_regs_r(uint32 address)
{
  int i;
  for (i=0; i<4; i++)
  {
    if ((address & cart.hw.mask[i]) == cart.hw.addr[i])
    {
      return cart.hw.regs[i] >> 1;
    }
  }

  return m68k_read_bus_8(address);
}

/* custom register hardware (Top Fighter, Lion King III, Super Donkey Kong  99, Mulan, Pocket Monsters II, Pokemon Stadium) */
static void custom_regs_w(uint32 address, uint32 data)
{
  uint8 temp;
  
  /* ROM bankswitch */
  if ((address >> 16) > 0x6f)
  {
    mapper_32k_w(data);
    return;
  }

  /* write register */
  default_regs_w(address, data);

  /* bitswapping */
  temp = cart.hw.regs[0];
  switch (cart.hw.regs[1] & 3)
  {
    case 0:
      cart.hw.regs[2] = (temp << 1);
      break;

    case 1:
      cart.hw.regs[2] = (temp >> 1);
      return;

    case 2:
      cart.hw.regs[2] = ((temp >> 4) | ((temp & 0x0F) << 4));
      return;

    default:
      cart.hw.regs[2] = (((temp >> 7) & 0x01) | ((temp >> 5) & 0x02) |
                         ((temp >> 3) & 0x04) | ((temp >> 1) & 0x08) |
                         ((temp << 1) & 0x10) | ((temp << 3) & 0x20) |
                         ((temp << 5) & 0x40) | ((temp << 7) & 0x80));
      return;
  }
}

/* alternate custom register hardware (Chinese Fighters III) */
static void custom_alt_regs_w(uint32 address, uint32 data)
{
  /* ROM bankswitch */
  if ((address >> 16) > 0x5f)
  {
    mapper_64k_w(data);
    return;
  }

  /* write regs */
  default_regs_w(address, data);
}


/* "Tekken 3 Special" custom register hardware */
static uint32 tekken_regs_r(uint32 address)
{
  /* data output */
  if ((address & 0x0e) == 0x02)
  {
    /* maybe depends on mode bits ? */
    return (cart.hw.regs[0] - 1);
  }

  return m68k_read_bus_16(address);
}

static void tekken_regs_w(uint32 address, uint32 data)
{
  switch (address & 0x0e)
  {
    case 0x00:
    {
      /* data output reset ? (game writes $FF before & after protection check) */
      cart.hw.regs[0]= 0x00;
      break;
    }
  
    case 0x02:
    {
      /* read only ? */
      break;
    }
  
    case 0x0c:
    {
      /* data output mode bit 0 ? (game writes $01) */
      break;
    }
  
    case 0x0e:
    {
      /* data output mode bit 1 ? (never written by game) */
      break;
    }
  
    default:
    {
      /* data input (only connected to D0 ?)*/
      if (data & 1)
      {
        /* 4-bit hardware register ($400004 corresponds to bit0, $400006 to bit1, etc) */
        cart.hw.regs[0] |= 1 << (((address - 0x04) >> 1) & 3);
      }
      break;
    }
  }
}

/* "Top Shooter" arcade board hardware */
static uint32 topshooter_r(uint32 address)
{
  if (address < 0x202000)
  {
    uint8 temp = 0xff;

    switch (address & 0xff)
    {
      case 0x43:
      {
        if (input.pad[0] & INPUT_A)     temp &= ~0x80; /* Shoot */
        if (input.pad[0] & INPUT_B)     temp &= ~0x10; /* Bet */
        if (input.pad[0] & INPUT_START) temp &= ~0x20; /* Start */
        break;
      }

      case 0x45:  /* ??? (DOWN) & Service Mode (UP) */
      {
        if (input.pad[0] & INPUT_UP)    temp &= ~0x08; /* Service Mode */
        if (input.pad[0] & INPUT_DOWN)  temp &= ~0x10; /* ???, used in service menu to select next option */
        break;
      }

      case 0x47:
      {
        if (input.pad[0] & INPUT_RIGHT) temp &= ~0x03; /* Insert 10 coins */
        break;
      }

      case 0x49:
      {
        if (input.pad[0] & INPUT_LEFT)  temp &= ~0x03; /* Clear coins */
        if (input.pad[0] & INPUT_C)     temp &= ~0x01; /* Insert XXX coins */
        break;
      }

      case 0x51:
      {
        temp = 0xA5;
        break;
      }

      default:
      {
        temp = m68k_read_bus_8(address);
        break;
      }
    }
    return temp;
  }

  return READ_BYTE(sram.sram , address & 0xffff);
}

static void topshooter_w(uint32 address, uint32 data)
{
  if (address >= 0x202000)
  {
    WRITE_BYTE(sram.sram , address & 0xffff, data);
    return;
  }

  m68k_unused_8_w(address, data);
}


/* Sega Channel hardware (not emulated) */
/* 

$A13004: BUSY ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 

Unused read16 00A13004 (00005B54)
Unused read16 00A13004 (00005B70)
Unused read16 00A13006 (00005B7C)

Unused read16 00A13004 (00005BC4)
Unused read16 00A13004 (00005BDA)

Unused write16 00A13032 = 0004 (00005706)
Unused write16 00A130F0 = 0000 (0000570E)

Unused write16 00A130F0 = 0000 (0000463E)
Unused write16 00A130F2 = 0001 (00004646)
Unused write16 00A130F4 = 0002 (0000464E)
Unused write16 00A130F6 = 0003 (00004656)
Unused write16 00A130F8 = 0004 (0000465E)
Unused write16 00A130FA = 0005 (00004666)

Unused write16 00A13032 = 0004 (00005706)
Unused write16 00A13032 = 0104 (0000579E)

Unused write16 00380000 = ACDC (00005718)
Unused write16 00380002 = 0000 (00005722)
Unused read16 00380000 (0000572C)
Unused write16 00A13032 = 0104 (0000579E)
Unused write16 00300000 = ACDC (000057B2)
Unused write16 00380000 = 0000 (000057BC)
Unused read16 00300000 (000057C6)

static uint32 sega_channel_r(uint32 address)
{
  return m68k_read_bus_16(address);;
}

static void sega_channel_w(uint32 address, uint32 data)
{
  m68k_unused_16_w(address, data);
}
*/
