/* This is mostly inspired from my work on neocdpsp, and from the source
 * of the neogeo driver in mame (to help to clarify things at some places!)
 * and got some info from an old version of the ncdz emu (mainly the array
 * to get the short names of the games)
 *
 * Notice that the cd audio emulation is based on values in RAM !!! 
 * The upload area which was a big mystery in the psp version is now correct
 * at least for the writes, 
 * 
 * Known problem : if you pause the game by going to the gui and come back,
 * the cd songs restarts from the start (the mp3 here). All this is because
 * the gui restarts the whole sound emulation in case the sample frequency
 * was changed, and so we loose the playing point in the mp3.
 */

#include "gameinc.h"
#include "sdl/dialogs/messagebox.h"
#include "pd4990a.h"
#include "files.h"
#include "2610intf.h"
#include "neocd.h"
#include "emumain.h" // reset_game_hardware
#include "cdrom.h"
#include "config.h"
#include "savegame.h"
#include "blit.h"
#include "zoom/16x16.h"
#include "cdda.h"
#include "dsw.h"
#include "profile.h"
#include "cache.h"
#include "default.h"

#define FRAME_NEO  CPU_FRAME_MHz(12,60)
char neocd_path[1024],neocd_dir[1024];

static struct INPUT_INFO neocd_inputs[] = // 4 players, 3 buttons
{
  { 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_B4, MSG_P1_B4, 0x00, 0x80, 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_B3, MSG_P2_B3, 0x02, 0x40, BIT_ACTIVE_0 },
  { KB_DEF_P2_B4, MSG_P2_B4, 0x02, 0x80, BIT_ACTIVE_0 },

  { KB_DEF_P1_START, MSG_P1_START, 0x04, 0x01, BIT_ACTIVE_0 },
  { KB_DEF_COIN1, MSG_COIN1, 0x04, 0x02, BIT_ACTIVE_0 },
  { KB_DEF_P2_START, MSG_P2_START, 0x04, 0x04, BIT_ACTIVE_0 },
  { KB_DEF_COIN2, MSG_COIN2, 0x04, 0x08, BIT_ACTIVE_0 },
  // Bit 4 (0x10) is 0 if the memory card is present !!!
  // From mame : d0 & D1 are presence bit indicator, d2 is write enabled
  // so the 3 bits must be set to 0 for a normal memory card behaviour.

  // { KB_DEF_TEST, MSG_TEST, 0x06, 0xff, BIT_ACTIVE_0 },

   { 0, NULL,        0,        0,    0            },
};

UINT8 *neocd_bios;

void setup_neocd_bios() {
  if (neocd_bios)
    return;
  neocd_bios = malloc(0x80000);
  // unsigned char rom_fix_usage[4096];
  if (!load_file(get_shared("neocd.bin"),neocd_bios,0x80000)) {
    if (!load_zipped(get_shared("neocd.zip"), "neocd.bin", 0x80000, 0, neocd_bios, 1)) {
      MessageBox("Error","Fatal Error: Could not load neocd.bin\nTried neocd.zip too","OK");
      exit(1);
    }
  }

#if 0
  UINT8 *fixtmp=malloc(65536);
  memcpy(fixtmp,&neocd_bios[0x70000],0x10000);
  fix_conv(fixtmp,&neocd_bios[0x70000],0x10000,rom_fix_usage);
  free(fixtmp);
  fixtmp=NULL;
#endif

  // Check BIOS validity
  if (ReadWord(&neocd_bios[0xA822]) != 0x4BF9)
  {
    ErrorMsg("Fatal Error: Invalid BIOS file.");
    exit(1);;
  }

#if 1
  /*** Patch BIOS CDneocd_bios Check ***/
  WriteWord(&neocd_bios[0xB040], 0x4E71);
  WriteWord(&neocd_bios[0xB042], 0x4E71);
  /*** Patch BIOS upload command ***/
  // WriteWord(&neocd_bios[0x546], 0x60fe); // 0xFAC1);
  // WriteWord(&neocd_bios[0x548], 0x4E75);

  /*** Patch BIOS CDDA check ***/
  /* 	*((short*)(neogeo_rom_memory+0x56A)) = 0xFAC3; */
  /* 	*((short*)(neogeo_rom_memory+0x56C)) = 0x4E75; */

  // WriteWord(&neocd_bios[0x56a],0x60fe);
  /*** Full reset, please ***/
  WriteWord(&neocd_bios[0xA87A], 0x4239);
  WriteWord(&neocd_bios[0xA87C], 0x0010);
  WriteWord(&neocd_bios[0xA87E], 0xFDAE);

  /*** Trap exceptions ***/
  // WriteWord(&neocd_bios[0xA5B6], 0x4AFC);
#endif
}

static UINT16 result_code,sound_code,pending_command,*neogeo_vidram,video_modulo,video_pointer;
static UINT8 neogeo_memorycard[8192];
UINT8 *neogeo_fix_memory,*video_fix_usage,*video_spr_usage; 

// Save ram : the neogeo hardware seems to have a non volatile ram, but it
// was not mapped in neocdpsp, so I don't know if it's used or not...
// To be tested...

static UINT16 *save_ram;
static UINT8 save_ram_unlocked;

static void set_save_ram_unlock(UINT8 data)
{
	save_ram_unlocked = data;
}

// the save ram seems to be ignored in neocd...
static void save_ram_wb(UINT32 offset, UINT8 data) {
  if (save_ram_unlocked) {
    offset &= 0xffff;
    WriteByte(&save_ram[offset^1],data);
    print_debug("save_ram_wb %x,%x\n",offset,data);
  }
}

static void save_ram_ww(UINT32 offset, UINT16 data) {
  if (save_ram_unlocked) {
    offset &= 0xffff;
    save_ram[offset] = data;
    print_debug("save_ram_ww %x,%x\n",offset,data);
  }
}

static UINT8 read_memorycard(UINT32 offset) {
  if ((offset & 1)) {
    return neogeo_memorycard[(offset & 0x3fff) >> 1];
  }
  return 0xff;
}

static UINT16 read_memorycardw(UINT32 offset) {
  return 0xff00 | neogeo_memorycard[(offset & 0x3fff) >> 1];
}

static int memcard_write;

static void write_memcard(UINT32 offset, UINT32 data) {
  if ((offset & 1)) {
    memcard_write = 1;
    neogeo_memorycard[(offset & 0x3fff) >> 1] = data;
  } 
}

static void write_memcardw(UINT32 offset, UINT32 data) {
  memcard_write = 1;
  neogeo_memorycard[(offset & 0x3fff) >> 1] = data;
}
  
static void set_res_code(UINT32 offset, UINT16 data) {
  result_code = data;
  print_debug("result_code = %x\n",data);
}

static UINT16 read_sound_cmd(UINT32 offset) {
  pending_command = 0;
  return sound_code;
}

static void write_sound_command(UINT32 offset, UINT16 data) {
  pending_command = 1;
  sound_code = data;
  print_debug("sound_code %x\n",data);
  cpu_int_nmi(CPU_Z80_0);
#if 1
  // Very few games seem to need this, but Ironclad is one of them (you loose
  // the z80 just after choosing "start game" if you don't do this !)
  // Also mslug produces bad cd songs without this !!!
  ExitOnEI = 1;
  int ticks = dwElapsedTicks;
  cpu_execute_cycles(CPU_Z80_0, 60000);
  dwElapsedTicks = ticks; // say this never happened
  ExitOnEI = 0;
#endif
}

static void write_sound_command_word(UINT32 offset, UINT16 data) {
  write_sound_command(offset,data >> 8);
}

static int cpu_readcoin(int addr)
{
    addr &= 0xFFFF;
    if (addr & 0x1) {
      // get calendar status - 2 bits
      /* Notice : lsb bits are not used in the neocd version, it's IN3 in mame
       * Here are the used bits :
	PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_SERVICE1 )
	PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_UNKNOWN )  // having this ACTIVE_HIGH causes you to start with 2 credits using USA bios roms
	PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_UNKNOWN ) // having this ACTIVE_HIGH causes you to start with 2 credits using USA bios roms
	PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_SPECIAL ) 
	*/
        int coinflip = pd4990a_testbit_r(0);
        int databit = pd4990a_databit_r(0);
        return 0xff ^ (coinflip << 6) ^ (databit << 7);
    }
    {
      int res = result_code;

      if (RaineSoundCard)
      {
	if (pending_command)
	  res &= 0x7f;
      }
      print_debug("read result_code %x sound_card %d\n",res,RaineSoundCard);

      return res;
    }
}

static int neogeo_frame_counter_speed,raster_frame,neogeo_frame_counter,
  irq2start,scanline,irq2control,watchdog_counter,disable_irq1,
// irq3_pending,
  display_position_interrupt_pending, vblank_interrupt_pending;
static UINT32 irq2pos;

static void get_scanline() {
  if (!raster_frame) {
    // scanline isn't available, find it
    int cycles = s68000readOdometer() % FRAME_NEO;
    scanline = cycles * 264 / FRAME_NEO;
  }
}

static void neo_irq2pos_w(int offset, UINT16 data)
{
  /* This is entierly from mame, so thanks to them ! */
  if (offset) {
    irq2pos = (irq2pos & 0xffff0000) | data;
    if (raster_frame) {
      print_debug("update irq2pos at line %d = %x\n",scanline,irq2pos);
    } else {
      print_debug("update irq2pos %x\n",irq2pos);
    }
    // irq2pos is in ticks of the neogeo pixel clock (6 MHz)
    // 1 tick happens for every pixel, not for every scanline.
    // So 1 scanline is 0x180 ticks, and you have 0x180*0x108 ticks for the
    // whole screen. Since the clock is at 6 MHz, this gives in 1s :
    // 6e6/(0x180*0x108) = 59.19 fps
    // Anyway for the current problem : some games like ridhero put 0x17d in
    // irq2pos to get 1 irq1 for every scanline. Since we use only a scanline
    // counter (irq2start), we must use (irq2pos + 3)/0x180 or we would not
    // get the interrupt repeat...
    if (irq2control & IRQ2CTRL_LOAD_RELATIVE)
    {
      /* This version is from the neocdpsp code.
       * Clearly this is a rough approximation (the irq2pos value is not
       * compared at all to any clock), but it seems to work well enough ! */
      int line = (irq2pos + 3) / 0x180; /* ridhero gives 0x17d */
      get_scanline();
      if (scanline == 263)
	irq2start = line;
      else
	irq2start = scanline + line;
      if (irq2pos == 0xffffffff)
	irq2start = -1;
      print_debug("irq2start = %d from direct write\n",irq2start);
    }
  } else {
    irq2pos = (irq2pos & 0x0000ffff) | (data << 16);
    if (raster_frame) {
      print_debug("update2 irq2pos at line %d = %x\n",scanline,irq2pos);
    } else {
      print_debug("update2 irq2pos %x\n",irq2pos);
    }
  }
}

static void update_interrupts(void)
{
  int level = 0;

  /* determine which interrupt is active - order implies priority */
  /* The odd thing is that vblank (level 1) is masked by hbl (level 2) ?!!!!
   * Anyway, this comes from mame, so I guess they spent months testing it even
   * if it seems crazy ! */
  if (display_position_interrupt_pending) level = 1;
  if (vblank_interrupt_pending) level = 2;
  // if (irq3_pending) level = 3;

  /* either set or clear the appropriate lines */
  if (level) {
    print_debug("68k irq %d\n",level);
    s68000interrupt(level, -1);
    // The vblank doesn't seem to require an ack : if we require an ack then
    // Fighters history dynamite masks its hbl and the game hangs.
    // So the ack process seems to be completely ignored in neocd...
    if (level == 2) vblank_interrupt_pending = 0; 
    else
      /* Finally, the hblank neither : in super sidekicks 3, sometimes the
       * programs acks the irq, but not always !!! If we don't disable it here
       * then we make a stack overflow !!! */
      display_position_interrupt_pending = 0;
  }
  else {
#ifdef RAINE_DEBUG
     if (s68000context.interrupts[0] & 7) 
       print_debug("should be cleared. %x\n",s68000context.interrupts[0]); 
#endif
    s68000context.interrupts[0] &= ~7;
  }
}

static void write_videoreg(UINT32 offset, UINT32 data) {
  switch((offset >> 1) & 7) {
    case 0x00: // printf("set_video_pointer %x\n",data);
      video_pointer = data; break;
    case 0x01: 
	neogeo_vidram[video_pointer] = data;
#if 0
	if (video_pointer == 0x8215)
	  printf("write %x,%x\n",video_pointer,data);
#endif

	/* auto increment/decrement the current offset - A15 is NOT effected */
	// video_pointer = (video_pointer & 0x8000) | ((video_pointer + video_modulo) & 0x7fff);
	if (raster_frame)
	  print_debug("line %d videoram %x = %x\n",scanline,video_pointer,data);
	video_pointer += video_modulo;
	break;
    case 0x02: video_modulo = data; break;
    case 0x03: 
	  neogeo_frame_counter_speed=((data>>8)&0xff)+1;
	  irq2control = data & 0xff;
	  break;
    case    4: neo_irq2pos_w(0,data); /* IRQ position */    break;
    case    5: neo_irq2pos_w(1,data); /* IRQ position */    break;
    case    6:    /* IRQ acknowledge */
	// if (data & 0x01) irq3_pending = 0;
	if (data & 0x02) display_position_interrupt_pending = 0;
	if (data & 0x04) vblank_interrupt_pending = 0;
	print_debug("interrupt ack %x\n",data);

	update_interrupts();
	break;
    case 0x07: break; /* unknown, see get_video_control */
  }
}

static void write_videoregb(UINT32 offset, UINT32 data) {
  print_debug("write video byte %x,%x\n",offset,data);
  if (offset & 1)
    // Write the same byte in LSB & MSB
    write_videoreg(offset,data | (data << 8));
}

static UINT16 read_videoreg(UINT32 offset) {
  switch((offset >> 1) & 7) {
    case 0:
    case 1: return neogeo_vidram[video_pointer];
    case 2: return video_modulo;
    case 3: 
	/*
	 * From mame :
	 *
        The format of this very important location is:  AAAA AAAA A??? BCCC

        A is the raster line counter. mosyougi relies solely on this to do the
          raster effects on the title screen; sdodgeb loops waiting for the top
          bit to be 1; zedblade heavily depends on it to work correctly (it
          checks the top bit in the IRQ2 handler).
        B is definitely a PAL/NTSC flag. Evidence:
          1) trally changes the position of the speed indicator depending on
             it (0 = lower 1 = higher).
          2) samsho3 sets a variable to 60 when the bit is 0 and 50 when it's 1.
             This is obviously the video refresh rate in Hz.
          3) samsho3 sets another variable to 256 or 307. This could be the total
             screen height (including vblank), or close to that.
          Some games (e.g. lstbld2, samsho3) do this (or similar):
          bclr    #$0, $3c000e.l
          when the bit is set, so 3c000e (whose function is unknown) has to be
          related
        C animation counter lower 3 bits
    */
	/* Ok, so to sum up, there are 264 video lines, only 224 displayed
	 * and the counter goes from 0xf8 to 0x1ff with a twist... ! */
	    {
	      get_scanline();
	      int vcounter = scanline + 0x100;
	      if (vcounter >= 0x200)
		vcounter -= 0x108; // That's the twist, the 0xf8-0xff is at the
		// end
		// Magician lord uses this counter a lot when entering the
		// score, if the scanline estimation is wrong then the game
		// freezes there !
	      return (vcounter << 7) | (neogeo_frame_counter & 7);
	    }
    default:
      return 0;
  }
  return 0xffff;
}

/*************************************
 * From mame :
 *
 *  Watchdog
 *
 *
 *    - The watchdog timer will reset the system after ~0.13 seconds
 *     On an MV-1F MVS system, the following code was used to test:
 *        000100  203C 0001 4F51             MOVE.L   #0x14F51,D0
 *        000106  13C0 0030 0001             MOVE.B   D0,0x300001
 *        00010C  5380                       SUBQ.L   #1,D0
 *        00010E  64FC                       BCC.S    *-0x2 [0x10C]
 *        000110  13C0 0030 0001             MOVE.B   D0,0x300001
 *        000116  60F8                       BRA.S    *-0x6 [0x110]
 *     This code loops long enough to sometimes cause a reset, sometimes not.
 *     The move takes 16 cycles, subq 8, bcc 10 if taken and 8 if not taken, so:
 *     (0x14F51 * 18 + 14) cycles / 12000000 cycles per second = 0.128762 seconds
 *     Newer games force a reset using the following code (this from kof99):
 *        009CDA  203C 0003 0D40             MOVE.L   #0x30D40,D0
 *        009CE0  5380                       SUBQ.L   #1,D0
 *        009CE2  64FC                       BCC.S    *-0x2 [0x9CE0]
 *     Note however that there is a valid code path after this loop.
 *
 *     The watchdog is used as a form of protecetion on a number of games,
 *     previously this was implemented as a specific hack which locked a single
 *     address of SRAM.
 *
 *     What actually happens is if the game doesn't find valid data in the
 *     backup ram it will initialize it, then sit in a loop.  The watchdog
 *     should then reset the system while it is in this loop.  If the watchdog
 *     fails to reset the system the code will continue and set a value in
 *     backup ram to indiate that the protection check has failed.
 *
 *************************************/

static void watchdog_w(UINT32 offset, UINT16 data)
{
	/* only an LSB write resets the watchdog */
    watchdog_counter = 18;  // 0.13 * 60 = 7.8, so I'll use 8 frames...
    // With 8, mslug can't start the game (reset before the game starts).
    // With 9, mslug resets at start of stage 3
    // I'll take 18 to have some margin, anyway this is in frames, for the
    // user it won't make much of a difference...
}

void neogeo_set_screen_dark(UINT32 bit) {
  // not supported, is it really usefull ???
}

static UINT8 game_vectors[0x80];

static void    neogeo_select_bios_vectors (int bit) {
  if (bit) {
    print_debug("set game vectors\n");
    memcpy(RAM,game_vectors,0x80);
    print_debug("after irq2 = %x\n",ReadLongSc(&RAM[0x68]));
  } else {
    print_debug("set bios vectors\n");
    memcpy(RAM, neocd_bios, 0x80);
  }
}

static int fixed_layer_source;

static void neogeo_set_fixed_layer_source(UINT8 data)
{
  // This is used to select the gfx source (cartridge or bios)
  // so maybe it's not used in the neocd version ?
	fixed_layer_source = data;
	print_ingame(600,"layer_source %d",data);
}

static int palbank;
extern UINT8 *RAM_PAL;

static void neogeo_set_palette_bank(int bit) {
  if (palbank != bit) {
    palbank = bit;
    RAM_PAL = RAM + 0x230000 + 0x2000*palbank;
    set_s68000_data_region_io(0,0x400000, 0x401fff, NULL, RAM_PAL);
    print_debug("palbank %d\n",bit);
  }
}

#if 0
static void write_pal(UINT32 offset, UINT16 data) {
  offset &= 0x1fff;
  WriteWord(&RAM_PAL[offset],data);
  if (raster_frame)
    print_debug("line %d, pal %x,%x\n",scanline,offset,data);
}
#endif

static void restore_bank() {
  int new_bank = palbank;
  palbank = -1;
  neogeo_set_palette_bank(new_bank);
  print_debug("palette bank restored\n");
}

static void system_control_w(UINT32 offset, UINT16 data)
{
  offset >>=1;
  UINT8 bit = (offset >> 3) & 0x01;

  switch (offset & 0x07)
  {
    default:
    case 0x00: neogeo_set_screen_dark(bit); break;
    case 0x01: neogeo_select_bios_vectors(bit); break;
    case 0x05: neogeo_set_fixed_layer_source(bit); break;
    case 0x06: set_save_ram_unlock(bit); break;
    case 0x07: neogeo_set_palette_bank(bit); break;

    case 0x02: /* unknown - HC32 middle pin 1 */
    case 0x03: /* unknown - uPD4990 pin ? */
    case 0x04: /* unknown - HC32 middle pin 10 */
	       print_debug("PC: %x  Unmapped system control write.  Offset: %x  Data: %x\n", cpu_get_pc(CPU_68K_0), offset & 0x07, bit);
	       break;
  }
}

static char config_game_name[80];

static struct YM2610interface ym2610_interface =
{
  1,
  8000000,
  { (255<<16) | 180 },
  { 0 },
  { 0 },
  { 0 },
  { 0 },
  { z80_irq_handler },	/* irq */
  { 0 },	/* delta_t */
  { 0 },	/* adpcm */
  { (255)|(0<<8),(255)|(255<<8) },
};

struct SOUND_INFO neocd_sound[] =
{
   { SOUND_YM2610,  &ym2610_interface,  },
   { 0,             NULL,               },
};

static void restore_memcard() {
  char path[1024];
  sprintf(path,"%ssavedata" SLASH "%s.bin", dir_cfg.exe_path, current_game->main_name); // 1st try game name in savedata
  FILE *f = fopen(path,"rb");
  memcard_write = 0;
  if (!f) {
    sprintf(path,"%s%smemcard.bin",neocd_dir,SLASH); // otherwise try this
    f = fopen(path,"rb");
  }
  if (f) {
    print_debug("memcard read from %s\n",path);
    fread(neogeo_memorycard,sizeof(neogeo_memorycard),1,f);
    fclose(f);
  }
}

static void save_memcard() {
  if (memcard_write) {
    char path[1024];
    sprintf(path,"%ssavedata" SLASH "%s.bin", dir_cfg.exe_path, current_game->main_name); // 1st try game name in savedata
    FILE *f = fopen(path,"wb");
    if (f) {
      fwrite(neogeo_memorycard,sizeof(neogeo_memorycard),1,f);
      fclose(f);
    }
    memcard_write = 0;
  }
}

int neocd_id;
static int offx, maxx;

typedef struct {
  char *name;
  int id,width;
} NEOCD_GAME;

// There seems to be a majority of games using 304x224, so the default value
// for the width is 304 (when left blank).
const NEOCD_GAME games[] =
{
       { "nam1975",    0x0001 },
       { "bstars",     0x0002, 320 },
       { "tpgolf",     0x0003, 304 },
       { "mahretsu",   0x0004, },
       { "maglord",    0x0005, 320 },
       { "ridhero",    0x0006 },
       { "alpham2",    0x0007 },
       { "ncombat",    0x0009 },
       { "cyberlip",   0x0010 },
       { "superspy",   0x0011 },
       { "mutnat",     0x0014 },
       { "sengoku",    0x0017, 320 },
       { "burningf",   0x0018 },
       { "lbowling",   0x0019 },
       { "gpilots",    0x0020 },
       { "joyjoy",     0x0021 },
       { "bjourney",   0x0022, 320 },
       { "lresort",    0x0024 },
       { "2020bb",     0x0030 },
       { "socbrawl",   0x0031 },
       { "roboarmy",   0x0032 },
       { "fatfury",    0x0033 },
       { "fbfrenzy",   0x0034 },
       { "crswords",   0x0037 },
       { "rallych",    0x0038 },
       { "kotm2",      0x0039 },
       { "sengoku2",   0x0040 },
       { "bstars2",    0x0041 },
       { "3countb",    0x0043, 320 },
       { "aof",        0x0044, 304 },
       { "samsho",     0x0045, 320 },
       { "tophuntr",   0x0046, 320 },
       { "fatfury2",   0x0047, 320 },
       // { "janshin",    0x0048 },
       { "androdun",   0x0049 },
       { "ncommand",   0x0050 },
       { "viewpoin",   0x0051 },
       { "ssideki",    0x0052 },
       { "wh1",        0x0053, 320 },
       { "crsword2",   0x0054 },
       { "kof94",      0x0055, 304 },
       { "aof2",       0x0056, 304 },
       { "wh2",        0x0057 },
       { "fatfursp",   0x0058, 320 },
       { "savagere",   0x0059 },
       { "ssideki2",   0x0061, 320 },
       { "samsho2",    0x0063, 320 },
       { "wh2j",       0x0064 },
       { "wjammers",   0x0065 },
       { "karnovr",    0x0066 },
       { "pspikes2",   0x0068, 320 },
       { "aodk",       0x0074 },
       { "sonicwi2",   0x0075, 320 },
       { "galaxyfg",   0x0078 },
       { "strhoop",    0x0079 },
       { "quizkof",    0x0080, 304 },
       { "ssideki3",   0x0081, 320 },
       { "doubledr",   0x0082, 320 },
       { "pbobblen",   0x0083, 320 },
       { "kof95",      0x0084, 304 },
       { "ssrpg",      0x0085 },
       { "samsho3",    0x0087 },
       { "stakwin",    0x0088, 320 },
       { "pulstar",    0x0089, 320 },
       { "whp",        0x0090, 320 },
       { "kabukikl",   0x0092, 320 },
       { "gowcaizr",   0x0094 },
       { "rbff1",      0x0095, 320 },
       { "aof3",       0x0096, 304 },
       { "sonicwi3",   0x0097, 320 },
       { "fromanc2",   0x0098 },
       { "turfmast",   0x0200 },
       { "mslug",      0x0201,304 },
       { "puzzledp",   0x0202 },
       { "mosyougi",   0x0203 },
       { "adkworld",   0x0204 },
       { "ngcdsp",     0x0205 },
       { "neomrdo",    0x0207 },
       { "zintrick",   0x0211 },
       { "overtop",    0x0212 },
       { "neodrift",   0x0213, 304 },
       { "kof96",      0x0214, 304 },
       { "ninjamas",   0x0217, 320 },
       { "ragnagrd",   0x0218, 320 },
       { "pgoal",      0x0219 },
       { "ironclad",   0x0220, 304 },
       { "magdrop2",   0x0221 },
       { "samsho4",    0x0222, 320 },
       { "rbffspec",   0x0223, 320 },
       { "twinspri",   0x0224 },
       { "kof96ngc",   0x0229 },
       { "breakers",   0x0230, 320 },
       { "kof97",      0x0232, 304 },
       { "lastblad",   0x0234, 320 },
       { "rbff2",      0x0240, 320 },
       { "mslug2",     0x0241 },
       { "kof98",      0x0242, 304 },
       { "lastbld2",   0x0243, 320 },
       { "kof99",      0x0251, 304 },
       { "fatfury3",   0x069c, 320 },
       { "neogeocd",   0x0000 },
       { NULL, 0 }
};

static const NEOCD_GAME *game;

// isprint is broken in windows, they allow non printable characters !!!
#define ischar(x) ((x)>=32 && (x)<=127)

void neogeo_read_gamename(void)
{
  unsigned char	*Ptr;
  int	temp;

  int region_code = GetLanguageSwitch();
  Ptr = RAM + ReadLongSc(&RAM[0x116]+4*region_code);
  memcpy(config_game_name,Ptr,80);
  ByteSwap((UINT8*)config_game_name,80);

  for(temp=0;temp<80;temp++) {
    if (!ischar(config_game_name[temp])) {
      config_game_name[temp]=0;
      break;
    }
  }
  while (config_game_name[temp-1] == ' ')
    temp--;
  config_game_name[temp] = 0;
  temp = 0;
  while (config_game_name[temp] == ' ')
    temp++;
  if (temp)
    memcpy(config_game_name,&config_game_name[temp],strlen(config_game_name)-temp+1);
  print_debug("game name : %s\n",config_game_name);
  current_game->long_name = (char*)config_game_name;
  
  neocd_id = ReadWord(&RAM[0x108]);
  // get the short name based on the id. This info is from neocdz...
  game = &games[0];
  while (game->name && game->id != neocd_id)
    game++;
  if (game->id == neocd_id)
    current_game->main_name = game->name;
  else {
    print_debug("warning could not find short name for this game\n");
    current_game->main_name = "neocd"; // resets name in case we don't find
  }
  print_debug("main_name %s\n",current_game->main_name);
  if (memcard_write)
    save_memcard(); // called after a reset
  else
    restore_memcard(); // called after loading

  /* update window title with game name */
  char neocd_wm_title[160];
  sprintf(neocd_wm_title,"NeoRaine - %s",config_game_name);
  SDL_WM_SetCaption(neocd_wm_title,neocd_wm_title);
}

static struct ROMSW_DATA romsw_data_neocd[] =
{
   { "Japan",           0x00 },
   { "USA",             0x01 },
   { "Europe",          0x02 },
   { NULL,                    0    },
};

static struct ROMSW_INFO neocd_romsw[] =
{
  { 0x10FD83, 0x2, romsw_data_neocd },
  // { 0xc00401, 0x2, romsw_data_neocd },
  { 0,        0,    NULL },
};

#include "cache.h"

/* Draw entire Character Foreground */
void video_draw_fix(void)
{
  UINT16 x, y;
  UINT16 code, colour;
  UINT16 *fixarea=&neogeo_vidram[0x7002];
  UINT8 *map;

  for (y=0; y < 28; y++)
  {
    for (x = 0; x < 40; x++)
    {
      code = fixarea[x << 5];

      colour = (code&0xf000)>>12;
      code  &= 0xfff;

      // Since some part of the fix area is in the bios, it would be
      // a mess to convert it to the unpacked version, so I'll keep it packed
      // for now...
      if(video_fix_usage[code]) {
	MAP_PALETTE_MAPPED_NEW(colour,16,map);
/*	if (video_fix_usage[code] == 2)
	  Draw8x8_Packed_Mapped_Rot(&neogeo_fix_memory[code<<5],x<<3,y<<3,map);
	else no opaque version for packed sprites !!! */
	 Draw8x8_Trans_Packed_Mapped_Rot(&neogeo_fix_memory[code<<5],(x<<3)+offx,(y<<3)+16,map);
      }
    }
    fixarea++;
  }
}

static void draw_sprites(int start, int end) {
   int         sx =0,sy =0,oy =0,rows =0,zx = 1, rzy = 1;
   int         offs,count,y;
   int         tileatr,y_control,zoom_control;
   UINT16 tileno;
   char         fullmode=0;
   int         rzx=16,zy=0;
   UINT8 *map;


   for (count=start;count<end;count++) {
      zoom_control = neogeo_vidram[0x8000 + count];
      y_control = neogeo_vidram[0x8200 + count];

      // If this bit is set this new column is placed next to last one
      if (y_control & 0x40) {
         sx += (rzx);

         // Get new zoom for this column
         zx = (zoom_control >> 8)&0x0F;

         sy = oy;
      } else {   // nope it is a new block
         // Sprite scaling
	sx = (neogeo_vidram[0x8400 + count]) >> 7;
	sy = 0x1F0 - (y_control >> 7);
	rows = y_control & 0x3f;
	zx = (zoom_control >> 8)&0x0F;

	rzy = zoom_control & 0xff;


         // Number of tiles in this strip
         if (rows == 0x20)
            fullmode = 1;
         else if (rows >= 0x21)
            fullmode = 2;   // most games use 0x21, but
         else
            fullmode = 0;   // Alpha Mission II uses 0x3f

         if (sy > 0x100) sy -= 0x200;

         if (fullmode == 2 || (fullmode == 1 && rzy == 0xff))
         {
            while (sy < -16) sy += 2 * (rzy + 1);
         }
         oy = sy;

	 if(rows==0x21) rows=0x20;
         else if(rzy!=0xff && rows!=0) {
	   rows=((rows*16*256)/(rzy+1) + 15)/16;
	 }

	 if(rows>0x20) rows=0x20;
      }

      rzx = zx+1;
      // skip if falls completely outside the screen
      if (sx >= 0x140 && sx <= 0x1f0) {
	// printf("%d,%d,%d continue sur sx count %x\n",sx,sy,rzx,count*2);
	continue;
      }

      if ( sx >= 0x1F0 )
	sx -= 0x200;

      // No point doing anything if tile strip is 0
      if ((rows==0)|| sx < 1-rzx || (sx>= maxx)) {
	 // printf("%d,%d,%d continue sur rows %d count %x\n",sx,sy,rzx,rows,count*2);
         continue;
      }

#if 0
      // Setup y zoom
      if(rzy==255)
         yskip=16;
      else
         dday=0;   // =256; NS990105 mslug fix
#endif

      offs = count<<6;

  // TODO : eventually find the precise correspondance between rzy ane zy, this
  // here is just a guess...
  if (rzy)
    zy = (rzy >> 4) + 1;
  else
    zy = 0;

      // rows holds the number of tiles in each vertical multisprite block
      for (y=0; y < rows ;y++) {
         tileno = neogeo_vidram[offs];
         tileatr = neogeo_vidram[offs+1];
	 offs += 2;
	 if (y)
	   // This is much more accurate for the zoomed bgs in aof/aof2
	   sy = oy + (((rzy+1)*y)>>4);

	 if (!(irq2control & IRQ2CTRL_AUTOANIM_STOP)) {
	   if (tileatr&0x8) {
	     // printf("animation tileno 8\n");
	     tileno = (tileno&~7)|(neogeo_frame_counter&7);
	   } else if (tileatr&0x4) {
	     // printf("animation tileno 4\n");
	     tileno = (tileno&~3)|(neogeo_frame_counter&3);
	   }
	 }

//         tileno &= 0x7FFF;
         if (tileno>0x7FFF) {
	     // printf("%d,%d continue sur tileno %x count %x\n",sx,sy,tileno,count*2);
            continue;
	 }

         if (fullmode == 2 || (fullmode == 1 && rzy == 0xff))
         {
            if (sy >= 248) {
	      sy -= 2 * (rzy + 1);
	    }
         }
         else if (fullmode == 1)
         {
            if (y >= 0x10) sy -= 2 * (rzy + 1);
         }
         else if (sy > 0x110) sy -= 0x200;


	 if (((tileatr>>8))&&(sy<224 && sy>=-15) && video_spr_usage[tileno])
	   {
	     // if (tileno == 0x52a0)
	     // if (frame_no++ >= 1800) 
	     // exit(1);
	     MAP_PALETTE_MAPPED_NEW(
	      (tileatr >> 8),
	      16,
	      map);
	        // printf("%d,%d,%x zx %d zy %d\n",sx,sy,tileno,rzx,zy);
	     if (video_spr_usage[tileno] == 2) // all solid
	       Draw16x16_Mapped_ZoomXY_flip_Rot(&GFX[tileno<<8],sx+offx,sy+16,map,rzx,zy,tileatr & 3);
	     else
	       Draw16x16_Trans_Mapped_ZoomXY_flip_Rot(&GFX[tileno<<8],sx+offx,sy+16,map,rzx,zy,tileatr & 3);
	   } 
      }  // for y
   }  // for count
}

static void draw_neocd() {
   UINT8 *map;
   static int fc;
   // Apparently there are only sprites to be drawn, zoomable and chainable
   // + an 8x8 text layer (fix) over them

  ClearPaletteMap();
  // printf("PC: %x\n",cpu_get_pc(CPU_68K_0));

  // printf("palbank %x colmax %x\n",palbank,SDL_MapRGB(color_format,0xf8,0xf8,0xf8));
  // for (a=0; a<224; a++) memset16(video_line_ptr[a],video_paletteram_pc[4095],320*2);
  MAP_PALETTE_MAPPED_NEW(
    0xff,
    16,
    map);
  switch(display_cfg.bpp) {
    case 8: clear_game_screen(map[15]); break;
    case 15:
    case 16: clear_game_screen(ReadWord(&map[15*2])); break;
    case 32: clear_game_screen(ReadLong(&map[15*4])); break;
  }

  int start = 0, end = 0x300 >> 1;
  if (neocd_id == 0x0085) {
    // pseudo priority code specific to ssrpg (samurai spirits rpg)
    // it draws the sprites at the begining of the list at the end to have them
    // on top. This code is taken from the japenese source of ncd 0.5 !
    if ((neogeo_vidram[(0x10400 + 4) >> 1] & 0x40) == 0 &&
	(neogeo_vidram[(0x10400 + 6) >> 1] & 0x40) != 0) {
      start = 6 >> 1;
      while ((neogeo_vidram[(0x10400 >> 1) + start] & 0x40) != 0)
	start++;
      if (start == 6 >> 1) start = 0;
    }

    do {
      draw_sprites(start,end);
      end = start;
      start = 0;
    } while (end != 0);
  } else
    draw_sprites(0,384);

   if (!(irq2control & IRQ2CTRL_AUTOANIM_STOP))
   {
       if (fc++ >= neogeo_frame_counter_speed) {
	 neogeo_frame_counter++;
	 fc=0;
       }
     }

   video_draw_fix();
}

static struct VIDEO_INFO neocd_video =
{
   draw_neocd,
   320,
   224,
   16,
   VIDEO_ROTATE_NORMAL |
   VIDEO_ROTATABLE,
   NULL,
};

static struct {
  int last_cmd, last_track;
} cdda;

static void neogeo_hreset(void)
{
  // The region_code can be set from the gui, even with an empty ram
  char *old_name = current_game->main_name;
  int region_code = GetLanguageSwitch();
  pd4990a_init();
  pending_command = sound_code = 0;
  cdda.last_cmd = 0;
  cdda.last_track = 0;
  memset(RAM,0,RAMSize);

  video_modulo = video_pointer = 0;

#if 1
  // Not sure this startup.bin is still usefull, maybe the watchdog now
  // initializes the ram correctly...
  if (!load_file(get_shared("startup.bin"),&RAM[0x10F300],3328)) {
    ErrorMsg("No startup.bin found !");
    ClearDefault();
    return;
  }
  ByteSwap(&RAM[0x10f300],3328);
  SetLanguageSwitch(region_code);
#endif

  /* Set System Region */
  neogeo_cdrom_load_title();
  if (!neogeo_cdrom_process_ipl(0)) {
    ErrorMsg("Error: Error while processing IPL.TXT.\n");
    ClearDefault();
    return;
  }

  /* read game name */
  neogeo_read_gamename();

  if (is_current_game("neodrift") || 
      is_current_game("turfmast") || 
      is_current_game("rallych") || 
      is_current_game("tpgolf") || 
      is_current_game("doubledr"))
    /* Neo Drift out doesn't seem to need irq1.
       Worse : if you enable it, then the image jumps from time to time, becoming
       unstable and almost unplayable.
       It probably means that I missed something in this irq1 emulation.
       Now I can't find what's wrong, and since it doesn't seem to need this irq1,
       the easiest fix is to just disable it ! 
       For neo turf masters, the screen is flashing when enabling it. It might
       be because of unemulated raster effects for now... */
    disable_irq1 = 1;
  else
    disable_irq1 = 0;

  // First time init
  M68000_context[0].pc = 0xc0a822;
  M68000_context[0].sr = 0x2700;
  M68000_context[0].areg[7] = 0x10F300;
  M68000_context[0].asp = 0x10F400;
  s68000SetContext(&M68000_context[0]);

  WriteLongSc(&RAM[0x10f6ee],ReadLongSc(&RAM[0x68]));
  if (RAM[0x107 ^ 1] & 0x7e) {
    if (ReadWord(&RAM[0x13a])) {
      // WriteLongSc(&RAM[0x10f6ea], (ReadWord(&RAM[0x13A])<<1) + 0xE00000);
    }

    else
    {
      // WriteLongSc(&RAM[0x10F6EA], 0);
      RAM[0x00013B^1] = 0x01;
    }
  }

  memcpy(game_vectors,RAM,0x80);
  print_debug("game vectors irq2 : %x\n",ReadLongSc(&game_vectors[0x68]));
  SetLanguageSwitch(region_code);
  if (old_name != current_game->main_name) {
    load_game_config();
    int region2 = GetLanguageSwitch();
    if (region2 != region_code)
      neogeo_read_gamename();
  }
  if (game->width == 320) {
    neocd_video.screen_x = 320;
    offx = 16;
    maxx = 320;
  } else {
    printf("mode 304\n");
    neocd_video.screen_x = 304;
    offx = 16-8;
    maxx = 320-8;
  }
}

/* Upload area : this area is NOT in the neogeo cartridge systems
 * it allows the 68k to access memory areas directly such as the z80 memory
 * the sprites memory and the fix memory to initialize them from the cd for
 * example. */

static int upload_type;

static void upload_type_w(UINT32 offset, UINT8 data) {
  print_debug("upload_type set to %x\n",data);
  upload_type = data;
}

static int get_upload_type() {
  // return a zone type suitable for the upload area from the upload type
  // This upload type is used for reading bytes from the z80 (instead of
  // whole blocks)
  int zone;
  switch(upload_type) {
    case 1: zone = PCM_TYPE; break;
    case 4: zone = Z80_TYPE; break;
    case 5: zone = FIX_TYPE; break;
  }
  return zone;
}

static int read_upload(int offset) {
  /* The read is confirmed at least during kof96 demo : it reads the main ram
   * (offset < 0x200000, zone 0) by the upload area instead of accessing
   * directly... so at least it shows this thing is really used after all ! */

    int zone = RAM[0x10FEDA ^ 1];
    int bank = RAM[0x10FEDB ^ 1];
    int offset2 = ReadLongSc(&RAM[0x10FEF8]);
    int offset_dst = ReadLongSc(&RAM[0x10FEF4]);
    int size = ReadLongSc(&RAM[0x10FEFC]);
    if (size == 0 && upload_type != 0xff) {
      zone = get_upload_type();
      // this thing finally explains what these upload reads/writes occuring
      // every frame were for in some games : to communicate with the z80,
      // certainly to see if it has some cd commands in store.
      // Now the next big mystery is how magician lord is supposed to get
      // his cd commands since it does not access the upload area for now ???
    }
    print_debug("read_upload: offset %x offset2 %x offset_dst %x zone %x bank %x size %x pc:%x\n",offset,offset2,offset_dst,zone,bank,size,s68000readPC());
   // int bank = m68k_read_memory_8(0x10FEDB);
   offset &= 0xfffff;

    switch (zone & 0xf) {
    case 0x00:  // /* 68000 */          return neogeo_prg_memory[offset^1];
      // return subcpu_memspace[(offset>>1)^1];
      if (offset < 0x200000)
	return RAM[offset];
      print_debug("read overflow\n");
      return 0xffff;

    case FIX_TYPE:
      offset >>=1;
      // the offsets are not verified, I don't know if this things needs to be
      // byteswapped or not
      int offsets[4] = { -16, -24, -0, -8 };
      if (offset < 23)
	return neogeo_fix_memory[offset ^ 1] | 0xff00;
      return neogeo_fix_memory[offset+offsets[offset & 3]] | 0xff00;
    case Z80_TYPE:
      if (offset < 0x20000) {
	return Z80ROM[offset >> 1];
      }
      return 0xff;
    default:
      //sprintf(mem_str,"read_upload unimplemented zone %x\n",zone);
      //debug_log(mem_str);
      // This is probably not the z80, or at least not this way.
      // try master of monsters 2 for a clear demonstration that
      // it can't possibly work this way
      print_debug("read_upload unmapped zone %x bank %x\n",zone,RAM[0x10FEDB ^ 1]);
      return -1;
    }
}

static void write_upload_word(int offset, UINT16 data) {
    int zone = RAM[0x10FEDA ^ 1];
    int bank = RAM[0x10FEDB ^ 1];
    int offset2,size;
    size = ReadLongSc(&RAM[0x10FEFC]);
    if (size == 0 && upload_type != 0xff) {
      zone = get_upload_type();
      if (zone == Z80_TYPE) {
	// The z80 seems to be the only interesting area for bytes accesses
	// like this...
	offset &= 0x1ffff;
	offset >>= 1;
	print_debug("direct write to z80 memory %x,%x from %x\n",offset,data,s68000readPC());
	Z80ROM[offset] = data;
	return;
      }
    }
    UINT8 *dest,*Source;

    if (size <= 0) {
      return;
    }
    offset2 = ReadLongSc(&RAM[0x10FEF8]);
    if (offset2 > 0xc00000) {
      offset2 -= 0xc00000;
      Source = neocd_bios + offset2;
    } else if (offset2 < 0x200000)
      Source = RAM + offset2;
    else {
      // never happens
      printf("offset source : %x ???\n",offset2);
      exit(1);
    }
    offset = ReadLongSc(&RAM[0x10FEF4]);
    // zone 2 starts from the end, but zone 0x12 starts from the start !!!
    // maybe it happens for the other areas as well (not confirmed yet)
    if (!(zone & 0x10)) offset -= size;

/* Awkward emulation of the upload area, the area used by the bios to transfer
 * different types of data to the system.
 * It's done with the help of some variables in RAM (instead of some hw
 * registers). Instead of emulating the transfers byte by byte, I try to
 * processs them as a whole, it makes much more sense for sprites for example
 * and is also more efficient. It might not work if a game tries to use this
 * area without using the bios, but I didn't find such a game yet ! */

    switch (zone & 0xf) {
      case    PRG_TYPE:
	if (offset > 0x200000) {
	  // never happens neither
	  printf("upload to outside the ram ??? %x\n",offset);
	  exit(1);
	}
	dest = RAM + offset;
	print_debug("upload PRG src %x dest %x size %x\n",ReadLongSc(&RAM[0x10FEF8]),offset,size);
	memcpy(dest, Source, size);
	WriteLongSc( &RAM[0x10FEF4], offset+size );
	break;

        case SPR_TYPE: /* SPR */
#if 0
            offset2=offset & ~0x02;

            offset2+=(bank<<20);

            if((offset2&0x7f)<64)
               offset2=(offset2&0xfff80)+((offset2&0x7f)<<1)+4;
            else
               offset2=(offset2&0xfff80)+((offset2&0x7f)<<1)-128;

            dest=&GFX[offset2*2];

            if (offset & 0x02) {
               /* second word */
			   WriteWord(&sprbuffer[2],data);
			   /* reformat sprite data */
			   sprbuffer[0] = (dest[1]<<4)|dest[0];
			   sprbuffer[1] = (dest[3]<<4)|dest[2];
			   ByteSwap(sprbuffer,sizeof(sprbuffer));
			   extract8(sprbuffer, dest);
			   // The extract8 works only on 4 bytes, so it's
			   // assmued this one arrives AFTER the 1st word
			   // (dirty hack !)
			   // mslug uses this when exiting to draw the cd
			   // interface (quite nice !)
			   for (i=4-1; i>=0; i--) {
			     dest[i*2+1] = dest[i]>>4;
			     dest[i*2] = dest[i] & 0xf;
			   }
			} else {
			   WriteWord(&sprbuffer[0],data);
			   /* reformat sprite data */
			   sprbuffer[2] = (dest[5]<<4)|dest[4];
			   sprbuffer[3] = (dest[7]<<4)|dest[6];
			   memcpy(dest,sprbuffer,4);
			   for (i=4-1; i>=0; i--) {
			     dest[i*2+1] = dest[i]>>4;
			     dest[i*2] = dest[i] & 0xf;
			   }
            }
            break;
#else
        offset += (bank<<20);
        dest = GFX + offset*2;
	file_cache("upload",offset*2,size*2,SPR_TYPE); // for the savegames
	if (offset + size > 0x400000) {
	  size = 0x400000 - offset;
	  print_debug("warning: size fixed for sprite upload %d\n",size);
	}
	if (size > 0) {
	  ByteSwap(Source,size);
	  spr_conv(Source, dest, size, video_spr_usage+(offset>>7));
	  ByteSwap(Source,size);
	}
	print_debug("upload SPR dest %x size %x\n",offset*2,size);

	offset += size;

	while (offset > 0x100000 )
	{
	  bank++;
	  offset -= 0x100000;
	}

	WriteLongSc( &RAM[0x10FEF4], offset );
	RAM[0x10FEDB ^ 1] = (bank>>8)&0xFF;
	RAM[0x10FEDC ^ 1] = bank&0xFF;

        break;
#endif
	case    FIX_TYPE:
	  dest = neogeo_fix_memory + (offset>>1);
	  if (ReadLongSc(&RAM[0x10FEF8]) < 0xc00000)
	    ByteSwap(Source,size);
	  fix_conv(Source, dest, size, video_fix_usage + (offset>>6));
	  if (ReadLongSc(&RAM[0x10FEF8]) < 0xc00000)
	    ByteSwap(Source,size);
	  print_debug("upload FIX dest %x size %x from %x zone %x\n",offset,size,ReadLongSc(&RAM[0x10FEF8]),zone);
	  file_cache("upload",offset/2,size,FIX_TYPE); // for the savegames

	  offset += (size<<1);
	  WriteLongSc( &RAM[0x10FEF4], offset);
        break;
    case    Z80_TYPE:    // Z80
#if 0
        print_debug("upload %x,%x\n",offset>>1,data);
	if (offset < 0x20000)
	  Z80ROM[offset>>1] = data;
#else
        dest = Z80ROM + (offset>>1);
	print_debug("upload Z80 dest %x size %x\n",offset>>1,size);
	memcpy(dest,Source,size);
	ByteSwap(dest,size);
        WriteLongSc( &RAM[0x10FEF4], offset + (size<<1) );
#endif
        break;
    case    PAT_TYPE:    // Z80 patch
	print_debug("upload PAT offset %x bank %x\n",offset,bank);
        neogeo_cdrom_apply_patch((short*)Source, offset, bank);
        break;
    case    PCM_TYPE:
        offset = (offset>>1) + (bank<<19);
	file_cache("upload",offset,size,PCM_TYPE);
        dest = PCMROM + offset;

	memcpy(dest,Source,size);
	ByteSwap(dest,size);
	print_debug("upload PCM offset %x size %x\n",offset,size);

        // Mise à jour des valeurs
        offset = ReadLongSc(&RAM[ 0x10FEF4] ) + (size<<1);

        while (offset > 0x100000 )
        {
            bank++;
            offset -= 0x100000;
        }

	WriteLongSc( &RAM[0x10FEF4], offset );
	RAM[0x10FEDB ^ 1] = (bank>>8)&0xFF;
	RAM[0x10FEDC ^ 1] = bank&0xFF;
        break;
    default:
         //sprintf(mem_str,"write_upload_word unimplemented zone %x\n",zone);
         //debug_log(mem_str);
	 print_debug("write_upload_word: unmapped zone %x bank %x\n",zone,bank);
			break;
    }
    WriteLongSc( &RAM[0x10FEFC], 0); // set the size to 0 to avoid to loop
    upload_type = 0xff; // and be sure to disable this too in this case.
}

static void write_upload(int offset, int data) {
  // int zone = RAM[0x10FEDA ^ 1];
  int zone = RAM[0x10FEDA ^ 1];
  // int size = ReadLongSc(&RAM[0x10FEFC]);
  print_debug("write_upload_byte %x zone %x called\n",offset,zone);
  write_upload_word(offset,data);
}

static void load_files(UINT32 offset, UINT16 data) {
  if (data == 0x550) {
    print_debug("load_files command\n");
    if (RAM[0x115A06 ^ 1]>32 && RAM[0x115A06 ^ 1] < 127) {
      neogeo_cdrom_load_files(&RAM[0x115a06]);
    } else
      print_debug("load_files: name at 115a06 = %x\n",RAM[0x115a06^1]);
  } else {
    print_debug("load_files:unknown cd command %d\n",data);
  }
}

static void test_end_loading(UINT32 offset,UINT16 data) {
  // This is a weird test for the end of the loading of files made by the bios
  // really this should be done differently, but I don't know most of the
  // commands sent to the cd, and it's too long to try to understand them...
  RAM[offset^1] = data; // normal write
  RAM[offset] = data; // duplicate
}


static void cdda_cmd(UINT32 offset, UINT8 data) {
  int track = RAM[0x10F6F9];
  RAM[0x10f6f6 ^ 1] = data;
  if (data <= 7) {
    if (data != cdda.last_cmd || cdda.last_track != track) {
      print_debug("data : %d %d pc:%x\n",RAM[0x10f6f7],RAM[0x10F6F9],s68000readPC());
      cdda.last_cmd = data;
      cdda.last_track = track;
      do_cdda(data,RAM[0x10f6f8 ^ 1]);
    }
  }
}

static void z80_enable(UINT32 offset, UINT8 data) {
  if (!data) {
    print_debug("reset z80 - command\n");
    cpu_reset(CPU_Z80_0);
    reset_timers();
  }
}

static int current_neo_frame, desired_68k_speed, stopped_68k;

static void myStop68000(UINT32 adr, UINT8 data) {
  if (!raster_frame) {
    Stop68000(0,0);
    stopped_68k = 1;
  }
}

static void load_neocd() {
  current_neo_frame = FRAME_NEO;
  desired_68k_speed = CPU_FRAME_MHz(24,60);
  watchdog_counter = 0; // disable watchdog at start
  init_cdda();
  init_load_type();
  upload_type = 0xff;
  memcard_write = 0;
  if (!neocd_bios)
    setup_neocd_bios(); // game was loaded from command line !
  clear_file_cache();
  setup_z80_frame(CPU_Z80_0,CPU_FRAME_MHz(4,60));
  RAMSize = 0x200000 + // main ram
            0x010000 + // z80 ram
	    0x020000 + // video ram
	    0x2000*2; // palette (2 banks)
  if(!(RAM=AllocateMem(RAMSize))) return;
  if(!(save_ram=(UINT16*)AllocateMem(0x10000))) return; // not to be saved with the ram
  if(!(GFX=AllocateMem(0x800000))) return; // sprites data, not ram (unpacked)
  if(!(neogeo_fix_memory=AllocateMem(0x20000))) return; 
  if(!(video_fix_usage=AllocateMem(4096))) return; // 0x20000/32 (packed)
  if(!(video_spr_usage=AllocateMem(0x800000/0x100))) return;
  if(!(PCMROM=AllocateMem(0x100000))) return;
  memset(video_fix_usage,0,4096);
  memset(video_spr_usage,0,0x8000);
  memset(neogeo_memorycard,0,sizeof(neogeo_memorycard));

  Z80ROM = &RAM[0x200000];
  neogeo_vidram = (UINT16*)(RAM + 0x210000);
  memset(neogeo_vidram,0,0x20000);
  RAM_PAL = RAM + 0x230000;
  palbank = 0;

  set_colour_mapper(&col_Map_15bit_xRGBRRRRGGGGBBBB);
  InitPaletteMap(RAM_PAL,0x100,0x10,0x1000);
  memset(RAM_PAL,0,0x4000);

  AddZ80AROMBase(Z80ROM, 0x0038, 0x0066);
  AddZ80ARW(0x0000, 0xffff, NULL, Z80ROM);

  AddZ80AWritePort(4, 4, YM2610_control_port_0_A_w, NULL);
  AddZ80AWritePort(5, 5, YM2610_data_port_0_A_w, NULL);
  AddZ80AWritePort(6, 6, YM2610_control_port_0_B_w, NULL);
  AddZ80AWritePort(7, 7, YM2610_data_port_0_B_w, NULL);
  /* Port 8 : NMI enable / acknowledge? (the data written doesn't matter)
   * Metal Slug Passes this 35, then 0 in sequence. After a
   * mission begins it switches to 1 */
  AddZ80AWritePort(0xc, 0xc, set_res_code, NULL);
  AddZ80AWritePort(0, 0xff, DefBadWritePortZ80, NULL);

  AddZ80AReadPort(0, 0, read_sound_cmd, NULL);
  AddZ80AReadPort(4, 4, YM2610_status_port_0_A_r, NULL);
  AddZ80AReadPort(5, 5, YM2610_read_port_0_r, NULL);
  AddZ80AReadPort(6, 6, YM2610_status_port_0_B_r, NULL);
  AddZ80AReadPort(0, 0xff, DefBadReadPortZ80, NULL);
  AddZ80AInit();

  AddMemFetch(0, 0x200000, RAM);
  AddMemFetch(0xc00000, 0xc7ffff, neocd_bios - 0xc00000);
  AddMemFetch(-1, -1, NULL);

  AddWriteByte(0x10f6f6, 0x10f6f6, cdda_cmd, NULL);
  AddWriteByte(0x10F651, 0x10F651, test_end_loading, NULL);
  AddRWBW(0, 0x200000, NULL, RAM);
  AddReadBW(0xc00000, 0xc7ffff, NULL,neocd_bios);
  AddReadByte(0x300000, 0x300000, NULL, &input_buffer[1]); // inputs, not confirmed
  AddWriteByte(0x300001, 0x300001, watchdog_w, NULL); 
  AddReadByte(0x320000, 0x320001, cpu_readcoin, NULL); // inputs, not confirmed
  AddReadByte(0x340000, 0x340000, NULL, &input_buffer[3]);
  AddReadByte(0x380000, 0x380000, NULL, &input_buffer[5]);

  AddReadByte(0x800000, 0x80ffff, read_memorycard, NULL);
  AddReadWord(0x800000, 0x80ffff, read_memorycardw, NULL);
  AddWriteByte(0x800000, 0x80ffff, write_memcard, NULL);
  AddWriteWord(0x800000, 0x80ffff, write_memcardw, NULL);

  AddReadWord(0x3c0000, 0x3c0007, read_videoreg, NULL);
  AddWriteByte(0x3c0000, 0x3c000f, write_videoregb, NULL);
  AddWriteWord(0x3c0000, 0x3c000f, write_videoreg, NULL);

  AddWriteByte(0x320000, 0x320001, write_sound_command, NULL);
  AddWriteWord(0x320000, 0x320000, write_sound_command_word, NULL);

  AddWriteBW(0x3a0000, 0x3a001f, system_control_w, NULL);
  // AddWriteWord(0x400000, 0x401fff, write_pal, NULL);
  AddRWBW(0x400000,0x401fff, NULL, RAM_PAL);
  AddSaveData(SAVE_USER_0, (UINT8*)&palbank, sizeof(palbank));
  prepare_cdda_save(SAVE_USER_1);
  AddSaveData(SAVE_USER_2, (UINT8 *)&cdda, sizeof(cdda));
  prepare_cache_save();
  AddLoadCallback(restore_bank);
  // is the save ram usefull ?!??? probably not with neocd...
  AddWriteByte(0xd00000, 0xd0ffff, save_ram_wb, NULL);
  AddWriteWord(0xd00000, 0xd0ffff, save_ram_ww, NULL);
  AddReadBW(0xd00000, 0xd0ffff, NULL, (UINT8*)save_ram);

  AddReadByte(0xe00000,0xe3ffff, read_upload, NULL);
  AddWriteByte(0xe00000,0xe3ffff, write_upload, NULL);
  AddWriteWord(0xe00000,0xe3ffff, write_upload_word, NULL);

  // cdrom : there are probably some more adresses of interest in this area
  // but I found only this one so far (still missing the ones used to control
  // the cd audio from the bios when exiting from a game).
  AddWriteWord(0xff0002, 0xff0003, load_files, NULL);
  AddWriteByte(0xff0105,0xff0105, upload_type_w, NULL);
  AddWriteByte(0xff0183, 0xff0183, z80_enable, NULL);
  // ff011c seems to be some kind of status, only bit 12 is tested but I
  // couldn't find what for, it doesn't seem to make any difference...
  // The ff0100 area seems to be related to the uploads, but there are many
  // adresses... there might be some kind of locking system, but no dma
  // apprently, it seems easier to emulate this from the ram area instead of
  // using these registers directly

  AddWriteByte(0xAA0000, 0xAA0001, myStop68000, NULL);			// Trap Idle 68000
  finish_conf_starscream();
  // There doesn't seem to be any irq3 in the neocd, irqs are very different
  // here
  // irq3_pending = 1;

  init_16x16_zoom();
  set_reset_function(neogeo_hreset);
  memset(input_buffer,0xff,10);
  input_buffer[4] = 0xf;
  result_code = 0;
  irq2control = 0;
}

static void apply_hack(int pc) {
  WriteWord(&RAM[pc],0x4239);
  WriteWord(&RAM[pc+2],0xaa);
  WriteWord(&RAM[pc+4],0);
  current_neo_frame = desired_68k_speed;
  print_ingame(60,"Applied speed hack");
  print_debug("Applied speed hack at %x\n",pc);
}

static void execute_neocd() {
  /* This code is still more or less experimental
   * the idea is to detect when the hblank interrupt is needed (raster_frame)
   * and to change the handling accordingly to save cycles.
   * Not sure this thing is 100% correct */

  if ((irq2control & IRQ2CTRL_ENABLE) && !disable_irq1) {
    print_debug("raster frame\n");

    raster_frame = 1;
    for (scanline = 0; scanline < 264; scanline++) {
      if (scanline == 0)	/* vblank */
      {

	if (irq2control & IRQ2CTRL_AUTOLOAD_VBLANK) {
	  if (irq2pos == 0xffffffff)
	    irq2start = -1;
	  else {
	    irq2start = ((irq2pos + 3) / 0x180);	/* ridhero gives 0x17d */
	    irq2start %= 264;
	  }
	  print_debug("irq2start %d on vblank (irq2pos %x)\n",irq2start,irq2pos);
	} else {
	  irq2start %= 264;
	  print_debug("irq2start cycle %d on vbl\n",irq2start);
	}

	vblank_interrupt_pending = 1;	   /* vertical blank */
      }

      if (scanline == irq2start && (irq2control & IRQ2CTRL_ENABLE)) {
	if (irq2control & IRQ2CTRL_AUTOLOAD_REPEAT) {
	  if (irq2pos == 0xffffffff)
	    irq2start = -1;
	  else {
	    irq2start += (irq2pos + 3) / 0x180;	/* ridhero gives 0x17d */
	    irq2start %= 264;
	  }
	  print_debug("irq2 autorepeat %d (scanline %d)\n",irq2start,scanline);
	  /* 		    if (irq2start < 40) */
	  /* 		      irq2start = 40; */
	}

	display_position_interrupt_pending = 1;
      }

      if (display_position_interrupt_pending || vblank_interrupt_pending) 
	update_interrupts();
      cpu_execute_cycles(CPU_68K_0,200000/264);
    }
    execute_z80_audio_frame(); // no need to interleave the z80 with the 68k
  } else { // normal frame (no raster)
    // the 68k frame does not need to be sliced any longer, we
    // execute cycles on the z80 upon receiving a command !
    stopped_68k = 0;
    raster_frame = 0;
    execute_z80_audio_frame();
    vblank_interrupt_pending = 1;	   /* vertical blank */
    update_interrupts();
    cpu_execute_cycles(CPU_68K_0, current_neo_frame);
    // print_debug("A7:%x return %x a0:%x a1:%x a2:%x a3:%x a4:%x a5:%x\n",s68000context.areg[7],ReadLongSc(&RAM[s68000context.areg[7]+8]),s68000context.areg[0],
    // s68000context.areg[1],s68000context.areg[2],s68000context.areg[3],s68000context.areg[4],s68000context.areg[5]);
    if (!stopped_68k && desired_68k_speed > FRAME_NEO && cpu_frame_count > 60) {
      int pc = s68000readPC();
      if (pc < 0x200000) {
	if (ReadWord(&RAM[pc]) == 0xb06e && ReadWord(&RAM[pc+4]) == 0x67fa) {
	  apply_hack(pc);
	} else if (ReadWord(&RAM[pc]) == 0x4a39 &&
		   ReadWord(&RAM[pc+6]) == 0x6bf8) { // tst.b/bmi
	  apply_hack(pc);
	  WriteWord(&RAM[pc+6],0x4e71); // nop
	} else if (ReadWord(&RAM[pc]) == 0x0839 &&
		   ReadWord(&RAM[pc+8]) == 0x66f2) {
	  apply_hack(pc);
	  WriteWord(&RAM[pc+6],0x4e71); // nop
	  WriteWord(&RAM[pc+8],0x4e71); // nop
	}
      }
    }
  }
  /* Add a timer tick to the pd4990a */
  pd4990a_addretrace();
  /* There doesn't seem to be any z80 irq, only nmi ?!!! wow !!! */
#if 1
  if (watchdog_counter > 0) {
    // Known place where the watchdog is used : when choosing "QUIT" from
    // the bios "memcard" menu. This allows to reset the machine instead of
    // staying on a black screen for ever...
    watchdog_counter--;
    if (watchdog_counter == 0) {
      print_debug("watchdog reset !\n");
      reset_game_hardware();
    }
  }
#endif
}

#if 0
// the draw_packed version which allowed me to debug the asm version in raine
// This one is the packed inverted finally (the bits are oriented in the intel
// way on the half-byte level !!!).
// I have changed the Draw8x8_Trans_Packed version in raine because no other
// driver seems to use it (all the f3 games use flipy version of this function)
static void draw_packed(UINT8 *src, int sx, int sy, UINT8 *map)
{
	UINT8 y;
	UINT32 mydword;
	UINT32 * fix=(UINT32 *)src;
	UINT16 * dest = ((UINT16*)GameBitmap->line[sy]) + sx;
	UINT16 * paldata=(UINT16*)map;
	UINT16 col;
	int pitch = (GameBitmap->line[1] - GameBitmap->line[0]) / 2;

	for(y=0;y<8;y++)
	{
		mydword  = *fix++;

		col = (mydword>> 0)&0x0f; if (col) dest[0] = paldata[col];
		col = (mydword>> 4)&0x0f; if (col) dest[1] = paldata[col];
		col = (mydword>> 8)&0x0f; if (col) dest[2] = paldata[col];
		col = (mydword>>12)&0x0f; if (col) dest[3] = paldata[col];
		col = (mydword>>16)&0x0f; if (col) dest[4] = paldata[col];
		col = (mydword>>20)&0x0f; if (col) dest[5] = paldata[col];
		col = (mydword>>24)&0x0f; if (col) dest[6] = paldata[col];
		col = (mydword>>28)&0x0f; if (col) dest[7] = paldata[col];
		dest += pitch;
	}
}
#endif

static void clear_neocd() {
  save_memcard();
  save_debug("neocd.bin",neocd_bios,0x80000,1);
  save_debug("ram.bin",RAM,0x200000,1);
  save_debug("z80",Z80ROM,0x10000,0);
#ifdef RAINE_DEBUG
  if (debug_mode)
    ByteSwap(neocd_bios,0x80000); // restore the bios for the next game
#endif
}

struct GAME_MAIN game_neocd = 
{
  __FILE__, /* source_file */ \
   NULL, // dirs
   NULL, // roms
   neocd_inputs,
   NULL, // dsw
   neocd_romsw,

   load_neocd,
   clear_neocd,
   &neocd_video,
   execute_neocd,
   "neocd",
   "neocd",
   "",
   COMPANY_ID_SNK,
   NULL,
   1998,
   neocd_sound,
   GAME_SHOOT
};


