// license:BSD-3-Clause // copyright-holders:etabeta /****************************************** SVP related *****************************************/ /* * Emulator of memory controller in SVP chip * * Copyright 2008, Grazvydas Ignotas * based on RE work by Tasco Deluxe * * SSP1601 EXT registers are mapped as I/O ports due to their function * (they are interfaced through external bus), and are named as follows * (these are unofficial names, official ones are unknown): * EXT0: PM0 - programmable register 0 * EXT1: PM1 - ... 1 * EXT2: PM2 - ... 2 * EXT3: XST - external status. Can also act as PM. * EXT4: PM4 - ... 4 * EXT5: (unused) * EXT6: PMC - programmable memory register control (PMAC). * EXT7: AL - although internal to SSP1601, it still causes bus access * * Depending on GPO bits in status register, PM0, PM1, PM2 and XST can act as * external status registers, os as programmable memory registers. PM4 always * acts as PM register (independent on GPO bits). */ #include "emu.h" #include "svp.h" //------------------------------------------------- // md_rom_device - constructor //------------------------------------------------- const device_type MD_ROM_SVP = &device_creator; md_rom_svp_device::md_rom_svp_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : device_t(mconfig, type, name, tag, owner, clock, shortname, source), device_md_cart_interface( mconfig, *this ), m_svp(*this, "svp"), m_test_ipt(*this, "MEMORY_TEST") { } md_rom_svp_device::md_rom_svp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, MD_ROM_SVP, "MD Virtua Racing", tag, owner, clock, "md_rom_svp", __FILE__), device_md_cart_interface( mconfig, *this ), m_svp(*this, "svp"), m_test_ipt(*this, "MEMORY_TEST") { } #define SSP_PMC_HAVE_ADDR 1 // address written to PMAC, waiting for mode #define SSP_PMC_SET 2 // PMAC is set, PMx can be programmed #define MASTER_CLOCK_NTSC 53693175 // HELPERS INLINE int get_inc(int mode) { int inc = (mode >> 11) & 7; if (inc != 0) { if (inc != 7) inc--; inc = 1 << inc; // 0 1 2 4 8 16 32 128 if (mode & 0x8000) inc = -inc; // decrement mode } return inc; } INLINE void overwrite_write(UINT16 *dst, UINT16 d) { if (d & 0xf000) { *dst &= ~0xf000; *dst |= d & 0xf000; } if (d & 0x0f00) { *dst &= ~0x0f00; *dst |= d & 0x0f00; } if (d & 0x00f0) { *dst &= ~0x00f0; *dst |= d & 0x00f0; } if (d & 0x000f) { *dst &= ~0x000f; *dst |= d & 0x000f; } } UINT32 md_rom_svp_device::pm_io(int reg, int write, UINT32 d) { if (m_emu_status & SSP_PMC_SET) { if (write) m_pmac_write[reg] = m_pmc.d; else m_pmac_read[reg] = m_pmc.d; m_emu_status &= ~SSP_PMC_SET; return 0; } // just in case if (m_emu_status & SSP_PMC_HAVE_ADDR) m_emu_status &= ~SSP_PMC_HAVE_ADDR; if (reg == 4 || (m_svp->state().state_int(SSP_ST) & 0x60)) { #define CADDR ((((mode<<16)&0x7f0000)|addr)<<1) UINT16 *dram = (UINT16 *)m_dram; if (write) { int mode = m_pmac_write[reg] >> 16; int addr = m_pmac_write[reg] & 0xffff; if ((mode & 0x43ff) == 0x0018) // DRAM { int inc = get_inc(mode); if (mode & 0x0400) overwrite_write(&dram[addr], d); else dram[addr] = d; m_pmac_write[reg] += inc; } else if ((mode & 0xfbff) == 0x4018) // DRAM, cell inc { if (mode & 0x0400) overwrite_write(&dram[addr], d); else dram[addr] = d; m_pmac_write[reg] += (addr & 1) ? 31 : 1; } else if ((mode & 0x47ff) == 0x001c) // IRAM { int inc = get_inc(mode); ((UINT16 *)m_iram)[addr & 0x3ff] = d; m_pmac_write[reg] += inc; } else { logerror("ssp FIXME: PM%i unhandled write mode %04x, [%06x] %04x\n", reg, mode, CADDR, d); } } else { int mode = m_pmac_read[reg] >> 16; int addr = m_pmac_read[reg] & 0xffff; if ((mode & 0xfff0) == 0x0800) // ROM, inc 1, verified to be correct { UINT16 *ROM = (UINT16 *)get_rom_base(); m_pmac_read[reg] += 1; d = ROM[addr | ((mode & 0xf) << 16)]; } else if ((mode & 0x47ff) == 0x0018) // DRAM { int inc = get_inc(mode); d = dram[addr]; m_pmac_read[reg] += inc; } else { logerror("ssp FIXME: PM%i unhandled read mode %04x, [%06x]\n", reg, mode, CADDR); d = 0; } } // PMC value corresponds to last PMR accessed (not sure). if (write) m_pmc.d = m_pmac_write[reg]; else m_pmc.d = m_pmac_read[reg]; return d; } return (UINT32)-1; } READ16_MEMBER( md_rom_svp_device::read_pm0 ) { UINT32 d = pm_io(0, 0, 0); if (d != (UINT32)-1) return d; d = m_xst2; m_xst2 &= ~2; // ? return d; } WRITE16_MEMBER( md_rom_svp_device::write_pm0 ) { UINT32 r = pm_io(0, 1, data); if (r != (UINT32)-1) return; m_xst2 = data; // ? } READ16_MEMBER( md_rom_svp_device::read_pm1 ) { UINT32 r = pm_io(1, 0, 0); if (r != (UINT32)-1) return r; logerror("svp: PM1 acces in non PM mode?\n"); return 0; } WRITE16_MEMBER( md_rom_svp_device::write_pm1 ) { UINT32 r = pm_io(1, 1, data); if (r != (UINT32)-1) return; logerror("svp: PM1 acces in non PM mode?\n"); } READ16_MEMBER( md_rom_svp_device::read_pm2 ) { UINT32 r = pm_io(2, 0, 0); if (r != (UINT32)-1) return r; logerror("svp: PM2 acces in non PM mode?\n"); return 0; } WRITE16_MEMBER( md_rom_svp_device::write_pm2 ) { UINT32 r = pm_io(2, 1, data); if (r != (UINT32)-1) return; logerror("svp: PM2 acces in non PM mode?\n"); } READ16_MEMBER( md_rom_svp_device::read_xst ) { UINT32 d = pm_io(3, 0, 0); if (d != (UINT32)-1) return d; return m_xst; } WRITE16_MEMBER( md_rom_svp_device::write_xst ) { UINT32 r = pm_io(3, 1, data); if (r != (UINT32)-1) return; m_xst2 |= 1; m_xst = data; } READ16_MEMBER( md_rom_svp_device::read_pm4 ) { return pm_io(4, 0, 0); } WRITE16_MEMBER( md_rom_svp_device::write_pm4 ) { pm_io(4, 1, data); } READ16_MEMBER( md_rom_svp_device::read_pmc ) { if (m_emu_status & SSP_PMC_HAVE_ADDR) { m_emu_status |= SSP_PMC_SET; m_emu_status &= ~SSP_PMC_HAVE_ADDR; return ((m_pmc.w.l << 4) & 0xfff0) | ((m_pmc.w.l >> 4) & 0xf); } else { m_emu_status |= SSP_PMC_HAVE_ADDR; return m_pmc.w.l; } } WRITE16_MEMBER( md_rom_svp_device::write_pmc ) { if (m_emu_status & SSP_PMC_HAVE_ADDR) { m_emu_status |= SSP_PMC_SET; m_emu_status &= ~SSP_PMC_HAVE_ADDR; m_pmc.w.h = data; } else { m_emu_status |= SSP_PMC_HAVE_ADDR; m_pmc.w.l = data; } } READ16_MEMBER( md_rom_svp_device::read_al ) { m_emu_status &= ~(SSP_PMC_SET | SSP_PMC_HAVE_ADDR); return 0; } WRITE16_MEMBER( md_rom_svp_device::write_al ) { } READ16_MEMBER( md_rom_svp_device::rom_read1 ) { UINT16 *IRAM = (UINT16 *)m_iram; return IRAM[offset]; } READ16_MEMBER( md_rom_svp_device::rom_read2 ) { return m_rom[offset + 0x800/2]; } int md_rom_svp_device::read_test() { return m_test_ipt->read(); } static INPUT_PORTS_START( md_svp ) PORT_START("MEMORY_TEST") /* special memtest mode */ PORT_CONFNAME( 0x01, 0x00, "SVP Test" ) PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) PORT_CONFSETTING( 0x01, DEF_STR( On ) ) INPUT_PORTS_END //------------------------------------------------- // ADDRESS_MAP( svp_ssp_map ) //------------------------------------------------- ADDRESS_MAP_START( md_svp_ssp_map, AS_PROGRAM, 16, md_rom_svp_device ) // AM_RANGE(0x0000, 0x03ff) AM_READ(rom_read1) // AM_RANGE(0x0400, 0xffff) AM_READ(rom_read2) AM_RANGE(0x0000, 0x03ff) AM_ROMBANK("iram_svp") AM_RANGE(0x0400, 0xffff) AM_ROMBANK("cart_svp") ADDRESS_MAP_END //------------------------------------------------- // ADDRESS_MAP( svp_ext_map ) //------------------------------------------------- ADDRESS_MAP_START( md_svp_ext_map, AS_IO, 16, md_rom_svp_device ) ADDRESS_MAP_GLOBAL_MASK(0xf) AM_RANGE(0*2, 0*2+1) AM_READWRITE(read_pm0, write_pm0) AM_RANGE(1*2, 1*2+1) AM_READWRITE(read_pm1, write_pm1) AM_RANGE(2*2, 2*2+1) AM_READWRITE(read_pm2, write_pm2) AM_RANGE(3*2, 3*2+1) AM_READWRITE(read_xst, write_xst) AM_RANGE(4*2, 4*2+1) AM_READWRITE(read_pm4, write_pm4) AM_RANGE(6*2, 6*2+1) AM_READWRITE(read_pmc, write_pmc) AM_RANGE(7*2, 7*2+1) AM_READWRITE(read_al, write_al) ADDRESS_MAP_END //------------------------------------------------- // MACHINE_DRIVER( md_svp ) //------------------------------------------------- static MACHINE_CONFIG_FRAGMENT( md_svp ) MCFG_CPU_ADD("svp", SSP1601, MASTER_CLOCK_NTSC / 7 * 3) /* ~23 MHz (guessed) */ MCFG_CPU_PROGRAM_MAP(md_svp_ssp_map) MCFG_CPU_IO_MAP(md_svp_ext_map) MACHINE_CONFIG_END //------------------------------------------------- // machine_config_additions - device-specific // machine configurations //------------------------------------------------- machine_config_constructor md_rom_svp_device::device_mconfig_additions() const { return MACHINE_CONFIG_NAME( md_svp ); } ioport_constructor md_rom_svp_device::device_input_ports() const { return INPUT_PORTS_NAME( md_svp ); } void md_rom_svp_device::set_bank_to_rom(const char *banktag, UINT32 offset) { if (membank(banktag)) membank(banktag)->set_base(m_rom + offset); } void md_rom_svp_device::device_start() { memset(m_pmac_read, 0, ARRAY_LENGTH(m_pmac_read)); memset(m_pmac_write, 0, ARRAY_LENGTH(m_pmac_write)); m_pmc.d = 0; m_pmc.w.l = 0; m_pmc.w.h = 0; m_emu_status = 0; m_xst = 0; m_xst2 = 0; // SVP stuff this->membank("iram_svp")->set_base(m_iram); // the other bank, "cart_svp", is setup at call_load save_item(NAME(m_pmac_read)); save_item(NAME(m_pmac_write)); save_item(NAME(m_emu_status)); save_item(NAME(m_xst)); save_item(NAME(m_xst2)); save_item(NAME(m_pmc.d)); save_item(NAME(m_pmc.w.l)); save_item(NAME(m_pmc.w.h)); save_item(NAME(m_dram)); save_item(NAME(m_iram)); } READ16_MEMBER(md_rom_svp_device::read) { UINT16 *DRAM = (UINT16 *)m_dram; if (offset >= 0x300000/2 && offset < 0x320000/2) { return DRAM[offset - 0x300000/2]; } else if (offset >= 0x390000/2 && offset < 0x3a0000/2) { // this is rewritten 68k test code UINT32 a1 = offset - 0x390000/2; a1 = (a1 & 0x7001) | ((a1 & 0x3e) << 6) | ((a1 & 0xfc0) >> 5); return DRAM[a1]; } else if (offset >= 0x3a0000/2 && offset < 0x3b0000/2) { // this is rewritten 68k test code UINT32 a1 = offset - 0x3a0000/2; a1 = (a1 & 0x7801) | ((a1 & 0x1e) << 6) | ((a1 & 0x7e0) >> 4); return DRAM[a1]; } if (offset < 0x200000/2) return m_rom[offset]; else { printf("read out of bound\n"); return 0xffff; } } WRITE16_MEMBER(md_rom_svp_device::write) { if (offset >= 0x300000/2 && offset < 0x320000/2) { UINT32 a1 = offset - 0x300000/2; UINT16 *DRAM = (UINT16 *)m_dram; DRAM[a1] = data; } } READ16_MEMBER(md_rom_svp_device::read_a15) { UINT32 d; switch (offset) { // 0xa15000, 0xa15002 case 0: case 1: return m_xst; // 0xa15004 case 2: d = m_xst2; m_xst2 &= ~1; return d; default: logerror("unhandled SVP reg read @ %x\n", offset << 1); } return 0; } WRITE16_MEMBER(md_rom_svp_device::write_a15) { switch (offset) { // 0xa15000, 0xa15002 case 0: case 1: m_xst = data; m_xst2 |= 2; break; // 0xa15006 case 3: break; // possibly halts SSP1601 default: logerror("unhandled SVP reg write %04x @ %x\n", data, offset << 1); } }