// license:BSD-3-Clause

/************************************************************************************************
 IDSA (Spain)
 ------------
   Hardware:
   ---------
		CPU:     Z80 @ 4 MHz
			INT: IRQ @ 977 Hz (4MHz/2048/2) or 488 Hz (4MHz/2048/4)
		IO:      DMA, AY8910 ports
		DISPLAY: bsktball: 7-digit 7-segment panels with PROM-based 5-bit BCD data (allowing a simple alphabet)
		         v1: 6-digit 7-segment panels with BCD decoding
		SOUND:	 2 x AY8910 @ 2 MHz plus SP0256 @ 3.12 MHz on board
 ************************************************************************************************/

#include "driver.h"
#include "core.h"
#include "cpu/z80/z80.h"

#define IDSA_CPUFREQ 4000000 /* CPU clock frequency */

/*----------------
/  Local variables
/-----------------*/
static struct {
  UINT8 dispData[4];
  int dispCol;
  int dispRow;
  core_tSeg segments;
  int isV1, lrq;
} locals;

static INTERRUPT_GEN(IDSA_irq) {
  cpu_set_irq_line(0, 0, PULSE_LINE);
}

/*-------------------------------
/  copy local data to interface
/--------------------------------*/
static INTERRUPT_GEN(IDSA_vblank) {
  memcpy(coreGlobals.segments, locals.segments, sizeof(coreGlobals.segments));
//  memset(locals.segments, 0, sizeof(locals.segments));
  core_updateSw(core_getSol(10));
}

static SWITCH_UPDATE(IDSA) {
  if (inports) {
    CORE_SETKEYSW(inports[CORE_COREINPORT] >> 12, 0x01, 0);
    CORE_SETKEYSW(inports[CORE_COREINPORT], 0x78, 1);
    CORE_SETKEYSW(inports[CORE_COREINPORT] >> 8, 0x02, locals.isV1 ? 2 : 3);
  }
}

// IDSA used small 32x8 6331 color PROMs to decode their 5-bit alphabet; I'd love to see this dumped someday! ;)
// Strings used by game: "Error (123456)", "P(Abc)(12)-F(01)", "FALtA", "1d5A", "Fin"
static UINT16 idsa2seg7(UINT8 data) {
  switch (data & 0x1f) {
    case 0x0a: return 0x77; // A !
    case 0x0b: return 0x7c; // b !
    case 0x0c: return 0x58; // c !
    case 0x0d: return 0x5e; // d !
    case 0x0e: return 0x79; // E !
    case 0x0f: return 0x71; // F !
    case 0x10: return 0x3d; // G ?
    case 0x11: return 0x76; // H ?
    case 0x12: return 0x10; // i !
    case 0x13: return 0x1e; // J / k ?
    case 0x14: return 0x38; // L !
    case 0x15: return 0x56; // m ? pretty much impossible with 7 segs...
    case 0x16: return 0x54; // n !
    case 0x17: return 0x55; // n with tilde ?
    case 0x18: return 0x5c; // o !
    case 0x19: return 0x73; // P !
    case 0x1a: return 0x50; // r !
    case 0x1b: return 0x40; // - !
    case 0x1c: return 0x78; // t !
    case 0x1d: return 0x1c; // u / v / w / x ?
    case 0x1e: return 0x6e; // y ?
    case 0x1f: return 0;
    default: return core_bcd2seg7[data & 0x0f];
  }
}

static WRITE_HANDLER(ay8910_0_portA_w) {
  if (locals.isV1) {
    coreGlobals.solenoids = (coreGlobals.solenoids & 0xfffffcff) | ((~data & 3) << 8);
    coreGlobals.lampMatrix[0] = ~data;
  } else
    coreGlobals.solenoids = (coreGlobals.solenoids & 0xffffff00) | (data ^ 0xff);
}
static WRITE_HANDLER(ay8910_0_portB_w) {
  if (locals.isV1)
    coreGlobals.solenoids = (coreGlobals.solenoids & 0xffffff00) | (data ^ 0xff);
  else
    coreGlobals.solenoids = (coreGlobals.solenoids & 0xff00ffff) | ((data ^ 0xff) << 16);
}
static WRITE_HANDLER(ay8910_1_portA_w) {
  if (locals.isV1) {
    coreGlobals.lampMatrix[3] = ~data;
    return;
  }
  if ((data >> 4) != 0x0f) {
    locals.dispCol = data >> 4;
  } else {
    locals.dispRow = 0;
    locals.segments[32 + locals.dispCol].w = idsa2seg7(locals.dispData[0]);
    locals.segments[16 + locals.dispCol].w = idsa2seg7(locals.dispData[1]);
    locals.segments[locals.dispCol].w = idsa2seg7(locals.dispData[2]);
    locals.segments[48].w = idsa2seg7(locals.dispData[3] >> 4);
    locals.segments[49].w = idsa2seg7(locals.dispData[3] & 0x0f);
  }
  coreGlobals.solenoids = (coreGlobals.solenoids & 0x00ffffff) | (((data & 0x0f) ^ 0x0f) << 24);
}
static WRITE_HANDLER(ay8910_1_portB_w) {
  if (locals.isV1)
    coreGlobals.lampMatrix[1] = ~data;
  else
    coreGlobals.solenoids = (coreGlobals.solenoids & 0xffff00ff) | ((data ^ 0xff) << 8);
}
struct AY8910interface IDSA_ay8910Int = {
	2,					/* 2 chips */
	2000000,			/* 2 MHz */
	{ MIXER(50,MIXER_PAN_LEFT), MIXER(50,MIXER_PAN_RIGHT) },	/* Volume */
	{ NULL },
	{ NULL },
	{ ay8910_0_portA_w, ay8910_1_portA_w },	/* Output Port A callback */
	{ ay8910_0_portB_w, ay8910_1_portB_w },	/* Output Port B callback */
};

static READ_HANDLER(snd_status_r) {
  switch (offset) {
    case 1: return locals.lrq ? 0xff : 0;
    default: return 0;
  }
}
static void lrq_callback(int state) {
	locals.lrq = !state;
}
struct sp0256_interface IDSA_sp0256Int = {
  100, /* volume */
  3120000, /* clock */
  lrq_callback,
  NULL,
  REGION_SOUND1
};

static READ_HANDLER(sw_r) {
  return ~coreGlobals.swMatrix[1 + (offset >> 4)];
}

static READ_HANDLER(dip_r) {
  return ~core_getDip(offset >> 4);
}

static READ_HANDLER(port_bd_r) {
  return 0x01 ^ coreGlobals.swMatrix[0];
}

static WRITE_HANDLER(row_w) {
  locals.dispRow++;
}

static WRITE_HANDLER(disp_w) {
  if (locals.dispRow < 4)
    locals.dispData[locals.dispRow] = data;
}

/*-----------------------------------------------
/ Load/Save static ram
/-------------------------------------------------*/
static UINT8 *IDSA_CMOS;
static NVRAM_HANDLER(IDSA) {
  core_nvram(file, read_or_write, IDSA_CMOS, 0x800, 0x00);
}
static WRITE_HANDLER(IDSA_CMOS_w) {
  UINT16 val16;
  IDSA_CMOS[offset] = data;
  if (locals.isV1) {
    if (offset > 0x0c && offset < 0x27)
      locals.segments[0x26 - offset].w = core_bcd2seg7e[data & 0x0f];
    else if (offset == 0x2e) { // lottery / space ship lamps
      val16 = 1 << (data & 0x0f);
      coreGlobals.lampMatrix[8] = val16 & 0xff;
      coreGlobals.lampMatrix[9] = val16 >> 8;
      val16 = 1 << (data >> 4);
      coreGlobals.lampMatrix[10] = val16 & 0xff;
      coreGlobals.lampMatrix[11] = val16 >> 8;
    } else if (offset == 0x2f) // ball in play / game over lamp!
      coreGlobals.lampMatrix[2] = data & 0xe0 ? data & 0xe0 : 1 << (data & 7);
    else if (offset == 0x30) { // bonus lamps
      val16 = 1 << (data & 0x0f);
      coreGlobals.lampMatrix[4] = val16 & 0xff;
      coreGlobals.lampMatrix[5] = val16 >> 8;
      val16 = 1 << (data >> 4);
      coreGlobals.lampMatrix[6] = val16 & 0xff;
      coreGlobals.lampMatrix[7] = val16 >> 8;
    } else if (offset > 0x81 && offset < 0x86) { // player up
      if (locals.segments[6 * (3-(offset - 0x82)) + 6].w)
        locals.segments[6 * (3-(offset - 0x82)) + 7].w = core_bcd2seg7e[data & 0x0f];
    } else if (offset == 0x107) { // game over does not update after game over
      if (data & 0xe0)
        coreGlobals.lampMatrix[2] = data & 0xe0;
    }
  } else if (offset > 0xa7 && offset < 0xb2)
    coreGlobals.lampMatrix[offset - 0xa8] = data;
}
static READ_HANDLER(IDSA_CMOS_r) {
  return IDSA_CMOS[offset];
}

static MEMORY_READ_START(IDSA_readmem)
  {0x0000,0x7fff, MRA_ROM},
  {0x8000,0x87ff, IDSA_CMOS_r},
MEMORY_END

static MEMORY_WRITE_START(IDSA_writemem)
  {0x0000,0x7fff, MWA_NOP},
  {0x8000,0x87ff, IDSA_CMOS_w, &IDSA_CMOS},
MEMORY_END

static PORT_READ_START(IDSA_readport)
  {0x00,0x50, sw_r},
  {0x60,0x70, dip_r},
  {0xb0,0xb3, snd_status_r},
  {0xbd,0xbd, port_bd_r},
MEMORY_END

static PORT_WRITE_START(IDSA_writeport)
  {0x80,0x8f, row_w},
  {0x90,0x90, disp_w},
  {0xd0,0xd0, sp0256_ALD_w},
  {0xe0,0xe0, AY8910_control_port_0_w},
  {0xe1,0xe1, AY8910_write_port_0_w},
  {0xf0,0xf0, AY8910_control_port_1_w},
  {0xf1,0xf1, AY8910_write_port_1_w},
MEMORY_END

static void reset_common(void) {
  static int inverted;
  memset(&locals, 0, sizeof locals);
  sp0256_reset();
  if (!inverted) {
    sp0256_bitrevbuff(memory_region(REGION_SOUND1), 0, 0x10000);
    inverted = 1;
  }
}

static MACHINE_RESET(IDSA) {
  reset_common();
}

MACHINE_DRIVER_START(idsa)
  MDRV_IMPORT_FROM(PinMAME)
  MDRV_CPU_ADD_TAG("mcpu", Z80, IDSA_CPUFREQ)
  MDRV_CPU_MEMORY(IDSA_readmem, IDSA_writemem)
  MDRV_CPU_PORTS(IDSA_readport, IDSA_writeport)
  MDRV_CPU_VBLANK_INT(IDSA_vblank, 1)
  MDRV_CPU_PERIODIC_INT(IDSA_irq, IDSA_CPUFREQ / 4096.)
  MDRV_CORE_INIT_RESET_STOP(NULL,IDSA,NULL)
  MDRV_NVRAM_HANDLER(IDSA)
  MDRV_DIPS(16)
  MDRV_SWITCH_UPDATE(IDSA)
  MDRV_SOUND_ADD(AY8910, IDSA_ay8910Int)
  MDRV_SOUND_ADD(SP0256, IDSA_sp0256Int)
  MDRV_SOUND_ATTRIBUTES(SOUND_SUPPORTS_STEREO)
MACHINE_DRIVER_END

// we need to "reset" the CPU manually as game reset calls the NMI routine and halts the CPU
static void v1_reset_timer(int dummy) {
  cpunum_set_reg(0, Z80_R, 0);
  cpunum_set_reg(0, Z80_HALT, 0);
  cpunum_set_reg(0, Z80_AF, 0x0040);
  cpunum_set_reg(0, Z80_SP, 0);
  cpunum_set_reg(0, Z80_PC, 0);
}

static MACHINE_RESET(v1) {
  reset_common();
  locals.isV1 = 1;
  // NMI routine saves the credits to NVRAM (called upon power down on real machine)
  cpu_set_nmi_line(0, PULSE_LINE);
  timer_adjust(timer_alloc(v1_reset_timer), TIME_IN_MSEC(1), 0, TIME_NEVER);
}

MACHINE_DRIVER_START(v1)
  MDRV_IMPORT_FROM(idsa)
  MDRV_CPU_MODIFY("mcpu")
  MDRV_CPU_PERIODIC_INT(IDSA_irq, IDSA_CPUFREQ / 8192.)
  MDRV_CORE_INIT_RESET_STOP(NULL,v1,NULL)
MACHINE_DRIVER_END

INPUT_PORTS_START(idsa)
  CORE_PORTS
  SIM_PORTS(1)
  PORT_START /* 0 */
    COREPORT_BIT     (0x0040, "Start",  KEYCODE_1)
    COREPORT_BIT     (0x0020, "Coin 1", KEYCODE_3)
    COREPORT_BIT     (0x0010, "Coin 2", KEYCODE_4)
    COREPORT_BIT     (0x0008, "Coin 3", KEYCODE_5)
    COREPORT_BIT     (0x0200, "Tilt",   KEYCODE_INSERT)
    COREPORT_BIT     (0x1000, "Slam Tilt", KEYCODE_HOME)
  PORT_START /* 1 */
    COREPORT_DIPNAME( 0x0001, 0x0000, "S1")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0001, "1" )
    COREPORT_DIPNAME( 0x0002, 0x0000, "S2")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0002, "1" )
    COREPORT_DIPNAME( 0x0004, 0x0000, "S3")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0004, "1" )
    COREPORT_DIPNAME( 0x0008, 0x0000, "S4")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0008, "1" )
    COREPORT_DIPNAME( 0x0010, 0x0000, "S5")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0010, "1" )
    COREPORT_DIPNAME( 0x0020, 0x0000, "S6")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0020, "1" )
    COREPORT_DIPNAME( 0x0040, 0x0000, "S7")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0040, "1" )
    COREPORT_DIPNAME( 0x0080, 0x0000, "S8")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0080, "1" )
    COREPORT_DIPNAME( 0x0100, 0x0000, "S9")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0100, "1" )
    COREPORT_DIPNAME( 0x0200, 0x0000, "S10")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0200, "1" )
    COREPORT_DIPNAME( 0x0400, 0x0000, "S11")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0400, "1" )
    COREPORT_DIPNAME( 0x0800, 0x0000, "S12")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x0800, "1" )
    COREPORT_DIPNAME( 0x1000, 0x0000, "S13")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x1000, "1" )
    COREPORT_DIPNAME( 0x2000, 0x0000, "S14")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x2000, "1" )
    COREPORT_DIPNAME( 0x4000, 0x0000, "S15")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x4000, "1" )
    COREPORT_DIPNAME( 0x8000, 0x0000, "S16")
      COREPORT_DIPSET(0x0000, "0" )
      COREPORT_DIPSET(0x8000, "1" )
INPUT_PORTS_END

core_tLCDLayout v1_disp[] = {
  {0, 0,20, 6, CORE_SEG7}, {0,20,14, 6, CORE_SEG7},
  {3, 0, 8, 6, CORE_SEG7}, {3,20, 2, 6, CORE_SEG7},
  {3,14, 0, 2, CORE_SEG7},
  {0}
};

#define SP0256_ROM \
  NORMALREGION(0x10000, REGION_SOUND1) \
  ROM_LOAD("sp0256-al2.bin", 0x0000, 0x0800, CRC(b504ac15) SHA1(e60fcb5fa16ff3f3b69d36c7a6e955744d3feafc) BAD_DUMP) \
  ROM_RELOAD(0x0800, 0x0800) \
  ROM_RELOAD(0x1000, 0x0800) \
  ROM_RELOAD(0x1800, 0x0800) \
  ROM_RELOAD(0x2000, 0x0800) \
  ROM_RELOAD(0x2800, 0x0800) \
  ROM_RELOAD(0x3000, 0x0800) \
  ROM_RELOAD(0x3800, 0x0800) \
  ROM_RELOAD(0x4000, 0x0800) \
  ROM_RELOAD(0x4800, 0x0800) \
  ROM_RELOAD(0x5000, 0x0800) \
  ROM_RELOAD(0x5800, 0x0800) \
  ROM_RELOAD(0x6000, 0x0800) \
  ROM_RELOAD(0x6800, 0x0800) \
  ROM_RELOAD(0x7000, 0x0800) \
  ROM_RELOAD(0x7800, 0x0800) \
  ROM_RELOAD(0x8000, 0x0800) \
  ROM_RELOAD(0x8800, 0x0800) \
  ROM_RELOAD(0x9000, 0x0800) \
  ROM_RELOAD(0x9800, 0x0800) \
  ROM_RELOAD(0xa000, 0x0800) \
  ROM_RELOAD(0xa800, 0x0800) \
  ROM_RELOAD(0xb000, 0x0800) \
  ROM_RELOAD(0xb800, 0x0800) \
  ROM_RELOAD(0xc000, 0x0800) \
  ROM_RELOAD(0xc800, 0x0800) \
  ROM_RELOAD(0xd000, 0x0800) \
  ROM_RELOAD(0xd800, 0x0800) \
  ROM_RELOAD(0xe000, 0x0800) \
  ROM_RELOAD(0xe800, 0x0800) \
  ROM_RELOAD(0xf000, 0x0800) \
  ROM_RELOAD(0xf800, 0x0800)

static core_tGameData v1GameData = {0,v1_disp,{FLIP_SW(FLIP_L),0,4}};
static void init_v1(void) {
  core_gameData = &v1GameData;
}
#define input_ports_v1 input_ports_idsa
ROM_START(v1)
  NORMALREGION(0x10000, REGION_CPU1)
  ROM_LOAD("v1.128", 0x0000, 0x4000, CRC(4e08f7bc) SHA1(eb6ef00e489888dd9c53010c525840de06bcd0f3))
  SP0256_ROM
ROM_END
CORE_GAMEDEFNV(v1, "V.1", 1985, "IDSA (Spain)", v1, 0)

core_tLCDLayout idsa_disp[] = {
  {0, 0, 0, 7, CORE_SEG7}, {0,16, 7, 7, CORE_SEG7},
  {3, 0,16, 7, CORE_SEG7}, {3,16,23, 7, CORE_SEG7},
  {6, 6,36, 1, CORE_SEG7}, {6,10,38, 2, CORE_SEG7}, {6,16,41, 1, CORE_SEG7}, {6,20,43, 2, CORE_SEG7},
  {9, 0,48, 2, CORE_SEG7},
  {0}
};

static core_tGameData bsktballGameData = {0, idsa_disp,{FLIP_SWNO(28,30),0,2}};
static void init_bsktball(void) {
  core_gameData = &bsktballGameData;
}
#define input_ports_bsktball input_ports_idsa
ROM_START(bsktball)
  NORMALREGION(0x10000, REGION_CPU1)
  ROM_LOAD("bsktball.256", 0x0000, 0x8000, CRC(d474e29b) SHA1(750cbacef34dde0b3dcb6c1e4679db78a73643fd))
  SP0256_ROM
ROM_END
CORE_GAMEDEFNV(bsktball, "Basketball", 1987, "IDSA (Spain)", idsa, 0)
