/*
  Raine notes :

  The bank switch for the z80 is particularly stupid : it's a bank of 0x8000 bytes
  but it changes by increments of 0x4000 bytes only !!! Since it's the first bank I see
  with a size > 0x4000 bytes, I made a hack in mz80help.c to always have a maximum size
  of 0x4000 bytes. (and it's really a rom bank apparently, not a data bank).

  priorities now use the priority bitmap because they don't apply between sprites (only
  between tiles or between a sprite and a tile). So the old priority queue becomes useless

*/

// Here are the comments from mame :

/******************************************************************************

  'Face' LINDA board
 driver by Paul Priest + David Haywood

*******************************************************************************

 Games on this Hardware

 Magical Cat Adventure (c)1993 Wintechno
 Nostradamus (c)1993 Face

*******************************************************************************

 Hardware Overview:

 Board Name : LINDA5 (Magical Cat) / LINDA25 (Nostradamus)

 Main CPU: 68000-16
 Sound CPU: Z80
 Sound Chip: YMF286-K

 Custom: FACE FX1037 x1
         038 x2 (As in Cave)

*******************************************************************************

Stephh's notes (based on the games M68000 code and some tests) :

1) "mcatadv*'

  - Player 1 Button 3 is only used in the "test" mode :
      * to select "OBJECT ROM CHECK"
      * in "BG ROM", to change the background number

  - Do NOT trust the "NORMAL TESTMODE" for the system inputs !

  - The Japan version has extra GFX/anims and it's harder than the other set.

2) 'nost*'

  - When entering the "test mode", you need to press SERVICE1 to cycle through
    the different screens.

*******************************************************************************

 todo:

 Flip Screen

*******************************************************************************

 trivia:

 Magical Cat Adventure tests for 'MASICAL CAT ADVENTURE' in RAM on start-up
 and will write it there if not found, expecting a reset, great engrish ;-)

******************************************************************************/

#include "gameinc.h"
#include "2610intf.h"
#include "sasound.h"
#include "timer.h"
#include "blit.h"
#include "emumain.h" // reset_game_hardware
#include "pdraw.h"
#include "priorities.h"

static struct ROM_INFO mcatadv_roms[] =
{
  { "mca-u30e", 0x80000, 0xc62fbb65, REGION_ROM1, 0x00000, LOAD_8_16 },
  { "mca-u29e", 0x80000, 0xcf21227c, REGION_ROM1, 0x00001, LOAD_8_16 },
  { "u9.bin", 0x20000, 0xfda05171, REGION_ROM2, 0x00000, LOAD_NORMAL },
  { "u9.bin", 0x20000, 0xfda05171, REGION_ROM2, 0x04000, LOAD_NORMAL },
  { "mca-u82.bin", 0x100000, 0x5f01d746, REGION_GFX1, 0x000000, LOAD_8_16 },
  { "mca-u83.bin", 0x100000, 0x4e1be5a6, REGION_GFX1, 0x000001, LOAD_8_16 },
  { "mca-u84.bin", 0x080000, 0xdf202790, REGION_GFX1, 0x200000, LOAD_8_16 },
  { "mca-u85.bin", 0x080000, 0xa85771d2, REGION_GFX1, 0x200001, LOAD_8_16 },
  { "mca-u86e", 0x080000, 0x017bf1da, REGION_GFX1, 0x400000, LOAD_8_16 },
  { "mca-u87e", 0x080000, 0xbc9dc9b9, REGION_GFX1, 0x400001, LOAD_8_16 },
  { "mca-u58.bin", 0x080000, 0x3a8186e2, REGION_GFX2, 0x000000, LOAD_NORMAL },
  { "mca-u60.bin", 0x100000, 0xc8942614, REGION_GFX3, 0x000000, LOAD_NORMAL },
  { "mca-u61.bin", 0x100000, 0x51af66c9, REGION_GFX3, 0x100000, LOAD_NORMAL },
  { "mca-u100", 0x080000, 0xb273f1b0, REGION_GFX3, 0x200000, LOAD_NORMAL },
  { "mca-u53.bin", 0x80000, 0x64c76e05, REGION_SMP1, 0x00000, LOAD_NORMAL },
  { NULL, 0, 0, 0, 0, 0 }
};

static struct ROM_INFO mcatadvj_roms[] =
{
  { "u30.bin", 0x80000, 0x05762f42, REGION_ROM1, 0x00000, LOAD_8_16 },
  { "u29.bin", 0x80000, 0x4c59d648, REGION_ROM1, 0x00001, LOAD_8_16 },
  { "mca-u82.bin", 0x100000, 0x5f01d746, REGION_GFX1, 0x000000, LOAD_8_16 },
  { "mca-u83.bin", 0x100000, 0x4e1be5a6, REGION_GFX1, 0x000001, LOAD_8_16 },
  { "mca-u84.bin", 0x080000, 0xdf202790, REGION_GFX1, 0x200000, LOAD_8_16 },
  { "mca-u85.bin", 0x080000, 0xa85771d2, REGION_GFX1, 0x200001, LOAD_8_16 },
  { "u86.bin", 0x080000, 0x2d3725ed, REGION_GFX1, 0x400000, LOAD_8_16 },
  { "u87.bin", 0x080000, 0x4ddefe08, REGION_GFX1, 0x400001, LOAD_8_16 },
  { "mca-u60.bin", 0x100000, 0xc8942614, REGION_GFX3, 0x000000, LOAD_NORMAL },
  { "mca-u61.bin", 0x100000, 0x51af66c9, REGION_GFX3, 0x100000, LOAD_NORMAL },
  { "u100.bin", 0x080000, 0xe2c311da, REGION_GFX3, 0x200000, LOAD_NORMAL },
  { NULL, 0, 0, 0, 0, 0 }
};

static struct ROM_INFO nost_roms[] =
{
  { "nos-pe-u.bin", 0x80000, 0x4b080149, REGION_ROM1, 0x00000, LOAD_8_16 },
  { "nos-po-u.bin", 0x80000, 0x9e3cd6d9, REGION_ROM1, 0x00001, LOAD_8_16 },
  { "nos-ps.u9", 0x40000, 0x832551e9, REGION_ROM2, 0x00000, LOAD_NORMAL },
  { "nos-ps.u9", 0x40000, 0x832551e9, REGION_ROM2, 0x08000, LOAD_NORMAL },
  { "nos-se-0.u82", 0x100000, 0x9d99108d, REGION_GFX1, 0x000000, LOAD_8_16 },
  { "nos-so-0.u83", 0x100000, 0x7df0fc7e, REGION_GFX1, 0x000001, LOAD_8_16 },
  { "nos-se-1.u84", 0x100000, 0xaad07607, REGION_GFX1, 0x200000, LOAD_8_16 },
  { "nos-so-1.u85", 0x100000, 0x83d0012c, REGION_GFX1, 0x200001, LOAD_8_16 },
  { "nos-se-2.u86", 0x080000, 0xd99e6005, REGION_GFX1, 0x400000, LOAD_8_16 },
  { "nos-so-2.u87", 0x080000, 0xf60e8ef3, REGION_GFX1, 0x400001, LOAD_8_16 },
  { "nos-b0-0.u58", 0x100000, 0x0214b0f2, REGION_GFX2, 0x000000, LOAD_NORMAL },
  { "nos-b0-1.u59", 0x080000, 0x3f8b6b34, REGION_GFX2, 0x100000, LOAD_NORMAL },
  { "nos-b1-0.u60", 0x100000, 0xba6fd0c7, REGION_GFX3, 0x000000, LOAD_NORMAL },
  { "nos-b1-1.u61", 0x080000, 0xdabd8009, REGION_GFX3, 0x100000, LOAD_NORMAL },
  { "nossn-00.u53", 0x100000, 0x3bd1bcbc, REGION_SMP1, 0x00000, LOAD_NORMAL },
  { NULL, 0, 0, 0, 0, 0 }
};

static struct INPUT_INFO mcatadv_inputs[] =
{
  { KB_DEF_P1_UP, MSG_P1_UP, 0x00, 0x01, BIT_ACTIVE_0 },
  { KB_DEF_P1_DOWN, MSG_P1_DOWN, 0x00, 0x02, BIT_ACTIVE_0 },
  { KB_DEF_P1_LEFT, MSG_P1_LEFT, 0x00, 0x04, BIT_ACTIVE_0 },
  { KB_DEF_P1_RIGHT, MSG_P1_RIGHT, 0x00, 0x08, BIT_ACTIVE_0 },
  { KB_DEF_P1_B1, MSG_P1_B1, 0x00, 0x10, BIT_ACTIVE_0 },
  { KB_DEF_P1_B2, MSG_P1_B2, 0x00, 0x20, BIT_ACTIVE_0 },
  { KB_DEF_P1_B3, MSG_P1_B3, 0x00, 0x40, BIT_ACTIVE_0 },
  { KB_DEF_P1_START, MSG_P1_START, 0x00, 0x80, BIT_ACTIVE_0 },
  { KB_DEF_COIN1, MSG_COIN1, 0x01, 0x01, BIT_ACTIVE_0 },

  { KB_DEF_P2_UP, MSG_P2_UP, 0x02, 0x01, BIT_ACTIVE_0 },
  { KB_DEF_P2_DOWN, MSG_P2_DOWN, 0x02, 0x02, BIT_ACTIVE_0 },
  { KB_DEF_P2_LEFT, MSG_P2_LEFT, 0x02, 0x04, BIT_ACTIVE_0 },
  { KB_DEF_P2_RIGHT, MSG_P2_RIGHT, 0x02, 0x08, BIT_ACTIVE_0 },
  { KB_DEF_P2_B1, MSG_P2_B1, 0x02, 0x10, BIT_ACTIVE_0 },
  { KB_DEF_P2_B2, MSG_P2_B2, 0x02, 0x20, BIT_ACTIVE_0 },
  { KB_DEF_P2_START, MSG_P2_START, 0x02, 0x80, BIT_ACTIVE_0 },
  { KB_DEF_COIN2, MSG_COIN2, 0x03, 0x01, BIT_ACTIVE_0 },
  { KB_DEF_SERVICE, MSG_SERVICE, 0x03, 0x02, BIT_ACTIVE_0 },
   { 0, NULL, 0, 0, 0 },
};

static struct DSW_DATA dsw_data_mcatadv_2[] =
{
  { MSG_DEMO_SOUND, 0x01, 2 },
  { MSG_OFF, 0x00, 0x00 },
  { MSG_ON, 0x01, 0x00 },
  { MSG_SCREEN, 0x02, 2 },
  { MSG_OFF, 0x02, 0x00 },
  { MSG_ON, 0x00, 0x00 },
  { MSG_SERVICE, 0x04,2 },
  { MSG_ON, 0,0 },
  { MSG_OFF, 0x04,0 },
  { "Coin Mode", 0x08, 2 },
  { "Mode 1" , 0x08, 0x00 },
  { "Mode 2" , 0x00, 0x00 },
  { MSG_COIN1, 0x30, 4 },
  { MSG_2COIN_1PLAY, 0x10, 0x00 },
  { MSG_1COIN_1PLAY, 0x30, 0x00 },
  { MSG_2COIN_3PLAY, 0x00, 0x00 },
  { MSG_1COIN_1PLAY, 0x20, 0x00 },
  { MSG_COIN2, 0xc0, 4 },
  { MSG_2COIN_1PLAY, 0x40, 0x00 },
  { MSG_1COIN_1PLAY, 0xc0, 0x00 },
  { MSG_2COIN_3PLAY, 0x00, 0x00 },
  { MSG_1COIN_1PLAY, 0x80, 0x00 },
  { NULL, 0, 0}
};

static struct DSW_DATA dsw_data_mcatadv_3[] =
{
  { MSG_DIFFICULTY, 0x03, 4 },
  { "Easy" , 0x02, 0x00 },
  { "Normal" , 0x03, 0x00 },
  { "Hard" , 0x01, 0x00 },
  { "Hardest" , 0x00, 0x00 },
  { MSG_LIVES, 0x0c, 4 },
  { "2" , 0x04, 0x00 },
  { "3" , 0x0c, 0x00 },
  { "4" , 0x08, 0x00 },
  { "5" , 0x00, 0x00 },
  { "Energy", 0x30, 4 },
  { "3" , 0x30, 0x00 },
  { "4" , 0x20, 0x00 },
  { "5" , 0x10, 0x00 },
  { "8" , 0x00, 0x00 },
  { MSG_CABINET, 0xc0, 3 },
  { "Upright 1 Player" , 0x40, 0x00 },
  { "Upright 2 Players" , 0xc0, 0x00 },
  { MSG_TABLE, 0x80, 0x00 },
  { NULL, 0, 0}
};

static struct DSW_INFO mcatadv_dsw[] =
{
  { 0x5, 0xff, dsw_data_mcatadv_2 },
  { 0x7, 0xff, dsw_data_mcatadv_3 },
  { 0, 0, NULL }
};

static struct DSW_DATA dsw_data_nost_2[] =
{
  { MSG_LIVES, 0x03, 4 },
  { "2", 0x02, 0x00 },
  { "3", 0x03, 0x00 },
  { "4", 0x01, 0x00 },
  { "5", 0x00, 0x00 },
  { MSG_DIFFICULTY, 0x0c, 4 },
  { MSG_EASY, 0x08, 0x00 },
  { MSG_NORMAL, 0x0c, 0x00 },
  { MSG_HARD, 0x04, 0x00 },
  { MSG_HARDEST, 0x00, 0x00 },
  { MSG_SCREEN, 0x10, 2 },
  { MSG_OFF, 0x10, 0x00 },
  { MSG_ON, 0x00, 0x00 },
  { MSG_DEMO_SOUND, 0x20, 2 },
  { MSG_OFF, 0x00, 0x00 },
  { MSG_ON, 0x20, 0x00 },
  { MSG_EXTRA_LIFE, 0xc0, 4 },
  { "500k 1000k", 0x80, 0x00 },
  { "800k 1500k", 0xc0, 0x00 },
  { "1000k 2000k", 0x40, 0x00 },
  { MSG_UNKNOWN, 0x00, 0x00 },
  { NULL, 0, 0}
};

static struct DSW_DATA dsw_data_nost_3[] =
{
  { MSG_COIN1, 0x07, 8 },
  { MSG_3COIN_1PLAY, 0x02, 0x00 },
  { MSG_2COIN_1PLAY, 0x04, 0x00 },
  { MSG_3COIN_2PLAY, 0x01, 0x00 },
  { MSG_1COIN_1PLAY, 0x07, 0x00 },
  { MSG_2COIN_3PLAY, 0x03, 0x00 },
  { MSG_1COIN_1PLAY, 0x06, 0x00 },
  { MSG_1COIN_3PLAY, 0x05, 0x00 },
  { MSG_FREE_PLAY, 0x00, 0x00 },
  { MSG_COIN2, 0x38, 8 },
  { MSG_4COIN_1PLAY, 0x00, 0x00 },
  { MSG_3COIN_1PLAY, 0x10, 0x00 },
  { MSG_2COIN_1PLAY, 0x20, 0x00 },
  { MSG_3COIN_2PLAY, 0x08, 0x00 },
  { MSG_1COIN_1PLAY, 0x38, 0x00 },
  { MSG_2COIN_3PLAY, 0x18, 0x00 },
  { MSG_1COIN_1PLAY, 0x30, 0x00 },
  { MSG_1COIN_3PLAY, 0x28, 0x00 },
  { MSG_UNUSED, 0x40, 2 },
  { MSG_OFF, 0x40, 0x00 },
  { MSG_ON, 0x00, 0x00 },
  { MSG_SERVICE, 0x80,2 },
  { MSG_ON, 0,0 },
  { MSG_OFF, 0x80,0 },
  { NULL, 0, 0}
};

static struct DSW_INFO nost_dsw[] =
{
  { 0x5, 0xff, dsw_data_nost_2 },
  { 0x7, 0xff, dsw_data_nost_3 },
  { 0, 0, NULL }
};

static void  mcat_soundlatch_w (UINT32 offset, UINT16 data) {
  latch = data;
  cpu_int_nmi(CPU_Z80_0);
  ExitOnEI=1;
  cpu_execute_cycles(CPU_Z80_0, 1000);
}

static UINT16  mcat_wd_r (UINT32 offset) {
  // watchdog_reset_r(0);
  return 0xc00;
}

static struct GfxLayout mcatadv_tiles16x16x4_layout =
{
	16,16,
	RGN_FRAC(1,1),
	4,
	{ STEP4(0,1) },
	{ STEP8(0,4), STEP8(32*8,4) },
	{ STEP8(0,32), STEP8(64*8,32) },
	128*8
};

static struct GFX_LIST mcatadv_gfxdecodeinfo[] =
{
 	{ REGION_GFX2, &mcatadv_tiles16x16x4_layout }, // 0x200 color banks
 	{ REGION_GFX3, &mcatadv_tiles16x16x4_layout }, // 0x200 color banks
	{ 0, NULL }
};

static struct YM2610interface mcatadv_ym2610_interface =
{
	1,
	16000000/2,	/* ? */
	{ YM2203_VOL(100,180) },
	{ 0 },	/* A_r */
	{ 0 },	/* B_r */
	{ 0 },	/* A_w */
	{ 0 },	/* B_w */
	{ z80_irq_handler },	/* irq */
	{ REGION_SOUND1 },	/* delta_t */
	{ REGION_SOUND1 },	/* adpcm */
	{ YM3012_VOL(220,MIXER_PAN_LEFT,220,MIXER_PAN_RIGHT) }
};

static struct SOUND_INFO mcatadv_sound[] =
  {
    { SOUND_YM2610, &mcatadv_ym2610_interface },
    { 0,             NULL,               },
  };

static UINT8 *scroll0, *scroll1, *ram_bg0, *ram_bg1, *ram_pal, *ram_spr, *video_regs,
  *ram_z80,*soundlatch2,*GFX_BG0_SOLID,*GFX_BG1_SOLID,*gfx_obj_solid,*gfx_obj_a,*gfx_obj_b,
  *gfx_obj_size;
static UINT32 sprite_size;

static int tile_max0, tile_max1;
static int layer_id_data[3];
static UINT16 *spriteram_old;
static UINT16 *vidregs_old;

#define spriteram_size 0x8000

static void write_soundlatch2(UINT32 offset, UINT8 data) {
  *soundlatch2 = data;
}

#define FRAME_Z80 CPU_FRAME_MHz(4,60)

static void mcatadv_setbank(UINT32 offset, UINT8 data)
{
  z80a_set_bank(0,data);
}

#ifdef RAINE_DEBUG
static void watchdog_reset16_w(UINT32 offset, UINT16 data) {
}
#endif

static int hack_applied,hack_adr,frame_68k;
static UINT8 hack_buffer[7];

static void undo_hack() {
  if (hack_applied) {
    memcpy(&ROM[hack_adr],hack_buffer,6);
    hack_applied = 0;
    frame_68k = CPU_FRAME_MHz(16,60);
  }
}

static void load_mcatadv() {
  RAMSize = 0x2901d; // 2721e
  hack_applied = 0;
  frame_68k = CPU_FRAME_MHz(16,60);
  set_reset_function(undo_hack);
  if(!(RAM=AllocateMem(RAMSize+0x10000)))     return;
  if(!(spriteram_old=(UINT16*)AllocateMem(spriteram_size)))     return;
  if (!(vidregs_old = (UINT16*)AllocateMem(16))) return;
  init_pbitmap();

  memset(spriteram_old,0,spriteram_size);
  memset(RAM,0,RAMSize);

  GFX_BG0_SOLID = make_solid_region_16x16(REGION_GFX2);
  GFX_BG1_SOLID = make_solid_region_16x16(REGION_GFX3);
  unpack_region_lsb(REGION_GFX1);
  gfx_obj_a = load_region[REGION_GFX1];

  // See the explanation of the copy of the sprites in draw_sprites
  // basically it's to be able to draw "sprites tiles"
  if (!(gfx_obj_b = AllocateMem(get_region_size(REGION_GFX1)))) return;
  sprite_size = get_region_size(REGION_GFX1)/0x100;
  if(!(gfx_obj_size=AllocateMem(sprite_size))) return;
  memset(gfx_obj_size,0x01,sprite_size);
  memcpy(gfx_obj_b,gfx_obj_a,sprite_size*0x100);
  gfx_obj_solid = make_solid_mask_16x16(gfx_obj_b, sprite_size);

  tile_max0 = get_region_size(REGION_GFX2)/0x100-1;
  tile_max1 = get_region_size(REGION_GFX3)/0x100-1;

  ByteSwap(ROM,get_region_size(REGION_ROM1));

  AddMemFetch(0x000000,0x0fffff,ROM);
  AddMemFetch(-1, -1, NULL);

  AddReadBW( 0x000000, 0x0fffff, NULL, ROM);
  AddRWBW(   0x100000, 0x10ffff, NULL, RAM);

  scroll0 = RAM + 0x10000;
  scroll1 = scroll0 + 6;

  AddRWBW(   0x200000, 0x200005, NULL, scroll0);
  AddRWBW(   0x300000, 0x300005, NULL, scroll1);

  ram_bg0 = scroll1 + 6;
  ram_bg1 = ram_bg0 + 0x2000;

  AddRWBW(   0x400000, 0x401fff, NULL, ram_bg0);
  AddRWBW(   0x500000, 0x501fff, NULL, ram_bg1);

  ram_pal = ram_bg1 + 0x2000;

  AddRWBW(   0x600000, 0x602fff, NULL, ram_pal);

  InitPaletteMap(ram_pal, 0x100, 16, 0x8000);
  set_colour_mapper(&col_map_xggg_ggrr_rrrb_bbbb);

  ram_spr = ram_pal + 0x3000;

  AddRWBW(   0x700000, 0x70ffff, NULL, ram_spr);

  video_regs = ram_spr + 0x10000;

  AddRWBW(0x800000, 0x800003, NULL, input_buffer); // inputs
  AddRWBW(0xa00000, 0xa00003, NULL, input_buffer + 4); // dsw

  AddRWBW(   0xb00000, 0xb0000f, NULL, video_regs);
  AddReadBW(  0xb0001e, 0xb0001f, mcat_wd_r, NULL);
#ifdef RAINE_DEBUG
  AddWriteBW(0xb00018, 0xb00019, watchdog_reset16_w,NULL); /* NOST Only */
#endif
  AddWriteBW( 0xc00000, 0xc00001, mcat_soundlatch_w, NULL);

  AddWriteByte(0xAA0000, 0xAA0001, Stop68000, NULL);			// Trap Idle 68000

  AddZ80AROMBase(Z80ROM, 0x0038, 0x0066);
  ram_z80 = video_regs + 0x10;

  z80_init_banks(0,REGION_ROM2,0x4000,0x8000);
  // z80_init_data_banks(0,REGION_ROM2,0,0x4000);
  // AddZ80ARead( 0x0000, 0x3fff, NULL, Z80ROM);
  AddZ80ARead( 0x0000, 0xbfff, NULL, NULL); // rom + bank
  AddZ80ARead( 0xc000, 0xdfff, NULL, ram_z80);
  AddZ80AWrite(0xc000, 0xdfff, NULL, ram_z80);

  if (!is_current_game("nost")) {
    AddZ80ARead( 0xe000, 0xe000, YM2610_status_port_0_A_r, NULL);
    AddZ80ARead( 0xe002, 0xe002, YM2610_status_port_0_B_r, NULL);
  }
  AddZ80ARead( 0x0000, 0xFFFF, DefBadReadZ80,		NULL);

  if (!is_current_game("nost")) {
    AddZ80AWrite(0xe000, 0xe000, YM2610_control_port_0_A_w, NULL);
    AddZ80AWrite(0xe001, 0xe001, YM2610_data_port_0_A_w, NULL);
    AddZ80AWrite(0xe002, 0xe002, YM2610_control_port_0_B_w, NULL);
    AddZ80AWrite(0xe003, 0xe003, YM2610_data_port_0_B_w, NULL);
    AddZ80AWrite(0xf000, 0xf000, mcatadv_setbank, NULL);
  }
  AddZ80AWrite(0x0000, 0xFFFF, DefBadWriteZ80,		NULL);

  soundlatch2 = ram_z80 + 0x2000;

  AddZ80AReadPort(0x80, 0x80, soundlatch_lo_r, NULL);
  AddZ80AWritePort(0x80, 0x80, write_soundlatch2, NULL);

  // specific to nost
  if (is_current_game("nost")) {
    AddZ80AReadPort(0x04, 0x05, YM2610_status_port_0_A_r, NULL);
    AddZ80AReadPort(0x06, 0x07, YM2610_status_port_0_B_r, NULL);

    AddZ80AWritePort(0x00, 0x00, YM2610_control_port_0_A_w, NULL);
    AddZ80AWritePort(0x01, 0x01, YM2610_data_port_0_A_w, NULL);
    AddZ80AWritePort(0x02, 0x02, YM2610_control_port_0_B_w, NULL);
    AddZ80AWritePort(0x03, 0x03, YM2610_data_port_0_B_w, NULL);
    AddZ80AWritePort(0x40, 0x40, mcatadv_setbank, NULL);
  }

  AddZ80AReadPort(0, 0xff, DefBadReadPort, NULL);
  AddZ80AWritePort(0, 0xff, DefBadWritePort, NULL);

  AddReadBW(  0xc00000, 0xc00001, NULL, soundlatch2);
  //  z80a_set_bank(0,0);
  AddReadByte(0x000000, 0xFFFFFF, DefBadReadByte, NULL);      // <Bad Reads>
  AddReadWord(0x000000, 0xFFFFFF, DefBadReadWord, NULL);      // <Bad Reads>
  AddWriteByte(0x000000, 0xFFFFFF, DefBadWriteByte, NULL);    // <Bad Writes>
  AddWriteWord(0x000000, 0xFFFFFF, DefBadWriteWord, NULL);    // <Bad Writes>
  AddRWBW(-1, -1, NULL, NULL);
  AddZ80AReadPort(-1, -1, NULL, NULL);
  AddZ80AWritePort(-1, -1, NULL, NULL);

  AddZ80AInit();
  AddInitMemory();   // Set Starscream mem pointers...
  layer_id_data[0] = add_layer_info("BG0");
  layer_id_data[1] = add_layer_info("BG1");
  layer_id_data[2] = add_layer_info("OBJ");

  setup_z80_frame(CPU_Z80_0,FRAME_Z80);
}

static void draw_tile(UINT8 *RAM_BG, UINT8 *scroll,int tile_max, int pal_base, UINT8 *GFX, UINT8 *GFX_SOLID) {
  int zzz,zzzz,x16,y16,zz,x,y,ta;
  UINT8 pri;

    UINT8 *MAP;

    MAKE_SCROLL_512x512_4_16(
      (ReadWord(&scroll[0])&0x1ff)-0x194,
      (ReadWord(&scroll[2])&0x1ff)-0x1df
      );

    START_SCROLL_512x512_4_16(16,16,320,224);

    ta=(ReadWord(&RAM_BG[2+zz]));

    if( ta && ta <= tile_max) {
      pri = RAM_BG[zz+1];

      MAP_PALETTE_MAPPED_NEW(
			     (pri&0x3F) | (pal_base),
			     16,
			     MAP
			     );

      pri = (pri>>6)+1; /* | i */;

      // QueueTile(ta, x,   y,   MAP, 0, pri);
      if(GFX_SOLID[ta]!=0){                      // No pixels; skip
	if((x > 0) && (x < 320+16) && (y > 0) && (y < 224+16)){
	  if(GFX_SOLID[ta]==1) {                   // Some pixels; trans
	    pdraw16x16_Mask_Trans_Mapped_Rot(&GFX[ta<<8],x,y,MAP,pri);
/* 	    Draw16x16_Trans_Mapped_Rot(&GFX[ta<<8],x,y,map); */
/* 	    Draw16x16_Mask_Trans_Rot(&GFX[ta<<8],x,y,my_pri); */
	  } else {                                        // all pixels; solid
	    pdraw16x16_Mask_Mapped_Rot(&GFX[ta<<8],x,y,MAP,pri);
/* 	    Draw16x16_Mapped_Rot(&GFX[ta<<8],x,y,map); */
/* 	    Draw16x16_Mask_Rot(&GFX[ta<<8],x,y,my_pri); */
	  }
	}
      }

    }

    END_SCROLL_512x512_4_16();
}

static void draw_sprites() {
  UINT16 *source = spriteram_old;
  UINT16 *finish = source + (spriteram_size/2)/2;
  int global_x = ReadWord(&video_regs[0])-0x184-16;
  int global_y = ReadWord(&video_regs[1*2])-0x1f1-16;

    /* Also double buffered because working on the old sprite ram */
    if( vidregs_old[2] == 0x0001 ) { /* Double Buffered */
      source += (spriteram_size/2)/2;
      finish += (spriteram_size/2)/2;
    }

    while ( source<finish ) {
      int pen = (source[0]&0x3f00)>>8;
      int tileno = source[1];
      int pri = source[0]>>14;
      /* In fact the hardware of this game seems to be an unfinished cave hardware :
	 no sprite zooming, and no relative priorities between sprites.
	 The consequence is that the character appears behind the nenuphar (plant
	 on water) when he is jumping (nenuphar = pri 3, char = pri 2).
	 So there are 2 ways to fix this : either the mame way : prepare a priority
	 bitmap with the tiles, and then draw the sprites alltogether using this
	 priority bitmap to find out if they are visible or not. It's very slow.
	 Or this hack. Normally it's not too dangerous : the "normal" priority seems
	 to be 2. But I should add the nost game to check these priorities again... */
      int x = source[2]&0x3ff;
      int y = source[3]&0x3ff;
      int flip = ((source[0] & 0x0040) >> 5) | // flipy
	((source[0] & 0x0080) >> 7); // flipx

      int height = ((source[3]&0xf000)>>12);
      int width = ((source[2]&0xf000)>>12);

      if (width && height) {
	if (x & 0x200) x-=0x400;
	if (y & 0x200) y-=0x400;

#if 0 /* For Flipscreen/Cocktail */
	if(mcatadv_vidregs[0]&0x8000)
	  {
	    flipx = !flipx;
	  }
	if(mcatadv_vidregs[1]&0x8000)
	  {
	    flipy = !flipy;
	  }
#endif

	if (source[3] != source[0] && /* 'hack' don't draw sprites while its testing the ram! */
	    tileno <= sprite_size) {
	  UINT8 *map;
	  int xx,w;

	  x -= global_x;
	  y -= global_y;

	  MAP_PALETTE_MAPPED_NEW(
				 pen,
				 16,
				 map
				 );

       /* This is taken directly from the cave driver. Except that the sprites are not
	  zoomed here, just flipped
	  Just a question : wouldn't it be faster to have something to draw directly
	  a sprite of wx16 x hx16 instead of recomputing the tiles everytime the size
	  changes ??? Since the size does not change often there must not be a big
	  difference of speed anyway, and writing such a drawing function would be much
	  longer... */

	  switch(flip){
	  case 0:
	    do{
	      if(gfx_obj_size[tileno] != width)
		make_tile_new(tileno, width,gfx_obj_a,gfx_obj_b,gfx_obj_solid,gfx_obj_size);
	      xx = x;
	      w = width;

	      do{
		if((xx > 0) && (xx < 320+16) && (y > 0) && (y < 224+16)){

		  if(gfx_obj_solid[tileno]){
		    if(gfx_obj_solid[tileno]==1)      // Some pixels; trans
		      pdraw16x16_Trans_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 0,
						       pri);
		    else {                         // all pixels; solid
		      pdraw16x16_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 0,
						pri);
		    }
		  }
		}
		xx = (xx + 16) & 0x3FF;
		tileno++; //tileno = (tileno + 1) & 0xFFFF;

	      }while(--w);

	      y = (y + 16) & 0x3FF;

	    }while(--height);
	    break;
	  case 1:
	    x = (x + ((width - 1) * 16)) & 0x3FF;
	    do{

	      if(gfx_obj_size[tileno] != width)
		make_tile_new(tileno, width,gfx_obj_a,gfx_obj_b,gfx_obj_solid,gfx_obj_size);
	      xx = x;
	      w = width;

	      do{

		if(gfx_obj_solid[tileno]){
		  if((xx > 0) && (xx < 320+16) && (y > 0) && (y < 224+16)){

		    if(gfx_obj_solid[tileno]==1)      // Some pixels; trans
		      pdraw16x16_Trans_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 1,
						       pri);
		    else                          // all pixels; solid
		      pdraw16x16_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 1,
						 pri);
		  }
		}

		xx = (xx - 16) & 0x3FF;
		tileno++;
	      }while(--w);

	      y = (y + 16) & 0x3FF;

	    }while(--height);
	    break;
	  case 2:
	    y = (y + ((height - 1) * 16)) & 0x3FF;
	    do{

	      if(gfx_obj_size[tileno] != width)
		make_tile_new(tileno, width,gfx_obj_a,gfx_obj_b,gfx_obj_solid,gfx_obj_size);
	      xx = x;
	      w = width;

	      do{

		if(gfx_obj_solid[tileno]){
		  if((xx > 0) && (xx < 320+16) && (y > 0) && (y < 224+16)){
		    if(gfx_obj_solid[tileno]==1)      // Some pixels; trans
		      pdraw16x16_Trans_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 2,
						       pri);
		    else {                         // all pixels; solid
		      pdraw16x16_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 2,
						 pri);
		    }
		  }
		}

		xx = (xx + 16) & 0x3FF;
		tileno++;

	      }while(--w);

	      y = (y - 16) & 0x3FF;

	    }while(--height);
	    break;
	  case 3:
	    x = (x + ((width - 1) * 16)) & 0x3FF;
	    y = (y + ((height - 1) * 16)) & 0x3FF;
	    do{

	      if(gfx_obj_size[tileno] != width)
		make_tile_new(tileno, width,gfx_obj_a,gfx_obj_b,gfx_obj_solid,gfx_obj_size);
	      xx = x;
	      w = width;

	      do{

		if((xx > 0) && (xx < 320+16) && (y > 0) && (y < 224+16)){

		  if(gfx_obj_solid[tileno]){
		    if(gfx_obj_solid[tileno]==1)      // Some pixels; trans
		      pdraw16x16_Trans_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 3,
						       pri);
		    else {                         // all pixels; solid
		      pdraw16x16_Mapped_flip_Rot(&gfx_obj_b[tileno<<8], xx, y, map, 3,
						 pri);
		    }
		    // textout_fast(buff,xx,y,get_white_pen());
		  }
		}
		xx = (xx - 16) & 0x3FF;
		tileno++;

	      }while(--w);

	      y = (y - 16) & 0x3FF;

	    }while(--height);
	    break;
	  } // switch(flip)

	  // QueueTileSize(tileno,x-global_x,y-global_y,map,flip,pri,width,height);
	} // tileno <= sprite_size
      } // width && height
      source+=4;
    } // while source < finish
}

static void draw_mcatadv() {
  UINT8 pal_base1 = ReadWord(&scroll0[4]) << 6;
  UINT8 pal_base2 = ReadWord(&scroll1[4]) << 6;
  clear_game_screen(0);
  ClearPaletteMap();
  // ClearTileQueue();

  clear_bitmap(pbitmap);

  // The priorities seem to be layer 0 first, then layer 1 and finally sprites
  // sprites are at the end or they hide the text in the japenese version intro
  if(check_layer_enabled(layer_id_data[0]))
    draw_tile(ram_bg0, scroll0,tile_max0,pal_base1, load_region[REGION_GFX2], GFX_BG0_SOLID);
  if(check_layer_enabled(layer_id_data[1]))
    draw_tile(ram_bg1, scroll1,tile_max1,pal_base2, load_region[REGION_GFX3], GFX_BG1_SOLID);

  if(check_layer_enabled(layer_id_data[2]))
    draw_sprites(); // outside the tile queue...

  // DrawTileQueue();

  memcpy(vidregs_old,video_regs,16);
  memcpy(spriteram_old,ram_spr,spriteram_size);
}

static struct VIDEO_INFO mcatadv_video =
  {
   draw_mcatadv,
   320,
   224,
   16,
   VIDEO_ROTATE_NORMAL |
   VIDEO_ROTATABLE,
    mcatadv_gfxdecodeinfo
  };

static struct VIDEO_INFO nost_video =
  {
   draw_mcatadv,
   320,
   224,
   16,
   VIDEO_ROTATE_270 |
   VIDEO_ROTATABLE,
    mcatadv_gfxdecodeinfo
  };

static struct DIR_INFO mcatadv_dirs[] =
  {
    { "mcatadv", },
    { NULL }
  };

static struct DIR_INFO nost_dirs[] =
  {
    { "nost", },
    { NULL }
  };

static struct DIR_INFO mcatadvj_dirs[] =
  {
    { "mcatadvj", },
   { ROMOF("mcatadv") },
   { CLONEOF("mcatadv") },
    { NULL }
  };

static void execute_mcatadv() {
  cpu_execute_cycles(CPU_68K_0, frame_68k);
   print_debug("PC:%06x SR:%04x\n",s68000context.pc,s68000context.sr);
   if (s68000context.pc == 0x36c ||
       (s68000context.pc == 0x11e && is_current_game("nost"))) {
     reset_game_hardware();
   } else if (s68000context.pc == 0x5a8c && !hack_applied && is_current_game("nost")) {
     memcpy(hack_buffer,&ROM[0x5a8a],6);
     WriteWord(&ROM[0x5a8a],0x4239);
     WriteWord(&ROM[0x5a8c],0xaa);
     WriteWord(&ROM[0x5a8e],0x0);
     hack_applied = 1;
     hack_adr = 0x5a8a;
     frame_68k = CPU_FRAME_MHz(32,60);
   }

  cpu_interrupt(CPU_68K_0, 1);

  execute_z80_audio_frame();
  print_debug("Z80:%04x\n",z80pc);
}

GAME( mcatadv ,
   mcatadv_dirs,
   mcatadv_roms,
   mcatadv_inputs,
   mcatadv_dsw,
   NULL,

   load_mcatadv,
   NULL,
   &mcatadv_video,
   execute_mcatadv,
   "mcatadv",
   "Magical Cat Adventure",
   "Magical Cat Adventure",
   COMPANY_ID_WINTECHNO,
   NULL,
   1993,
   mcatadv_sound,
   GAME_PLATFORM
);

GAME( nost ,
   nost_dirs,
   nost_roms,
   mcatadv_inputs,
   nost_dsw,
   NULL,

   load_mcatadv,
   NULL,
   &nost_video,
   execute_mcatadv,
   "nost",
   "Nostradamus",
   "Nostradamus",
   COMPANY_ID_FACE,
   NULL,
   1993,
   mcatadv_sound,
   GAME_SHOOT
);

GAME( mcatadvj ,
   mcatadvj_dirs,
   mcatadvj_roms,
   mcatadv_inputs,
   mcatadv_dsw,
   NULL,

   load_mcatadv,
   NULL,
   &mcatadv_video,
   execute_mcatadv,
   "mcatadvj",
   "Magical Cat Adventure (jap - harder)",
   "Magical Cat Adventure (jap)",
   COMPANY_ID_WINTECHNO,
   NULL,
   1993,
   mcatadv_sound,
   GAME_PLATFORM
);
