diff options
Diffstat (limited to 'src/mame/sega/mie.cpp')
-rw-r--r-- | src/mame/sega/mie.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/src/mame/sega/mie.cpp b/src/mame/sega/mie.cpp new file mode 100644 index 00000000000..77639bd5ea8 --- /dev/null +++ b/src/mame/sega/mie.cpp @@ -0,0 +1,374 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert + +#include "emu.h" +#include "mie.h" +#include "maple-dc.h" + +// MIE aka Sega 315-6146, MAPLE-JVS bridge Z80-based MCU +// +// Todos: +// - ports 00-0f is identical to Sega 315-5338A/315-5649 I/O ICs +// (used in Sega H1, Model 2, etc). devicefication needed. +// +// - ports 10-15 and 20-25 is standard 8250/16xxx UARTs. +// +// - ports a0-af - external I/O 1, in JVS I/Os connected to NEC uPD71053 counter/timer +// +// - ports c0-cf - external I/O 2, not used +// in JVS I/Os can be connected to (unpopulated) 315-5296 I/O IC, if enabled by DIP switch +// +// - both memory and I/O address spaces can be directly accessed by host system (used in Hikaru) +// +// - Speed is all wrong + + +DEFINE_DEVICE_TYPE(MIE, mie_device, "mie", "Sega 315-6146 MIE") +DEFINE_DEVICE_TYPE(MIE_JVS, mie_jvs_device, "mie_jvs", "JVS (MIE)") + +void mie_device::mie_map(address_map &map) +{ + map(0x0000, 0x07ff).rom(); + map(0x0800, 0x6fff).r(FUNC(mie_device::read_ff)); + map(0x7000, 0x7002).rw(FUNC(mie_device::control_r), FUNC(mie_device::control_w)).mirror(0x07c0); + map(0x7003, 0x7003).rw(FUNC(mie_device::lreg_r), FUNC(mie_device::lreg_w)).mirror(0x07c0); + map(0x7004, 0x7023).rw(FUNC(mie_device::tbuf_r), FUNC(mie_device::tbuf_w)).mirror(0x07c0); + map(0x7024, 0x703f).r(FUNC(mie_device::read_00)).mirror(0x07c0); + map(0x7800, 0x7fff).r(FUNC(mie_device::read_78xx)); + map(0x8000, 0xffff).ram(); +} + +void mie_device::mie_port(address_map &map) +{ + map.global_mask(0xff); + map(0x00, 0x07).rw(FUNC(mie_device::gpio_r), FUNC(mie_device::gpio_w)); + map(0x08, 0x08).rw(FUNC(mie_device::gpiodir_r), FUNC(mie_device::gpiodir_w)); + map(0x0f, 0x0f).rw(FUNC(mie_device::adc_r), FUNC(mie_device::adc_w)); + map(0x10, 0x10).rw(FUNC(mie_device::jvs_r), FUNC(mie_device::jvs_w)); // ports 1x and 2x is standard UARTs, TODO handle it properly + map(0x12, 0x12).w(FUNC(mie_device::jvs_dest_w)); + map(0x13, 0x13).w(FUNC(mie_device::jvs_lcr_w)); + map(0x15, 0x15).r(FUNC(mie_device::jvs_status_r)); + map(0x30, 0x30).rw(FUNC(mie_device::irq_enable_r), FUNC(mie_device::irq_enable_w)); + map(0x50, 0x50).rw(FUNC(mie_device::maple_irqlevel_r), FUNC(mie_device::maple_irqlevel_w)); + map(0x70, 0x70).rw(FUNC(mie_device::irq_pending_r), FUNC(mie_device::irq_pending_w)); + map(0x90, 0x90).w(FUNC(mie_device::jvs_control_w)); + map(0x91, 0x91).r(FUNC(mie_device::jvs_sense_r)); +} + +ROM_START( mie ) + ROM_REGION( 0x800, "mie", 0 ) + ROM_LOAD( "315-6146.bin", 0x000, 0x800, CRC(9b197e35) SHA1(864d14d58732dd4e2ee538ccc71fa8df7013ba06)) +ROM_END + +const tiny_rom_entry *mie_device::device_rom_region() const +{ + return ROM_NAME(mie); +} + +void mie_device::device_add_mconfig(machine_config &config) +{ + Z80(config, cpu, DERIVED_CLOCK(1,1)); + cpu->set_addrmap(AS_PROGRAM, &mie_device::mie_map); + cpu->set_addrmap(AS_IO, &mie_device::mie_port); + cpu->set_irq_acknowledge_callback(FUNC(mie_device::irq_callback)); +} + +mie_jvs_device::mie_jvs_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : jvs_host(mconfig, MIE_JVS, tag, owner, clock) +{ +} + +mie_device::mie_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) + : maple_device(mconfig, MIE, tag, owner, clock) + , cpu(*this, "mie") + , jvs(*this, finder_base::DUMMY_TAG) + , gpio_port(*this, {finder_base::DUMMY_TAG, finder_base::DUMMY_TAG, finder_base::DUMMY_TAG, finder_base::DUMMY_TAG, finder_base::DUMMY_TAG, finder_base::DUMMY_TAG, finder_base::DUMMY_TAG, finder_base::DUMMY_TAG}) +{ +} + +void mie_device::device_start() +{ + maple_device::device_start(); + timer = timer_alloc(FUNC(mie_device::update_irq_reply), this); + + save_item(NAME(gpiodir)); + save_item(NAME(gpio_val)); + save_item(NAME(irq_enable)); + save_item(NAME(irq_pending)); + save_item(NAME(maple_irqlevel)); + + // patch out MIE RAM test + // TODO: figure out why SH4 code doesn't wait long enough for internal firmware's RAM test completed in the case of reset + uint32_t *rom = (uint32_t*)memregion("mie")->base(); + rom[0x144/4] = 0x0001d8c3; +} + +void mie_device::device_reset() +{ + lreg = 0; + control = 0; + jvs_control = 0; + jvs_dest = 0; + jvs_rpos = 0; + memset(gpio_val, 0, sizeof(gpio_val)); + gpiodir = 0xff; + maple_irqlevel = 0xff; + irq_enable = irq_pending = 0; + cpu->reset(); + timer->adjust(attotime::never); + memset(tbuf, 0, sizeof(tbuf)); +} + +uint8_t mie_device::control_r(offs_t offset) +{ + return control >> (8*offset); +} + +void mie_device::control_w(offs_t offset, uint8_t data) +{ + uint32_t prev_control = control; + int shift = offset*8; + control = (control & ~(255 << shift)) | (data << shift); + + if((!(prev_control & CTRL_TXB) && (control & CTRL_TXB)) || + (!(prev_control & CTRL_CTXB) && (control & CTRL_CTXB))) { + control &= ~(CTRL_TFB|CTRL_RXB|CTRL_RFB|CTRL_BFOV|CTRL_EMP); + reply_size = lreg+1; + if(reply_size > TBUF_SIZE) + reply_size = TBUF_SIZE; + memcpy(reply_buffer, tbuf, reply_size*4); + reply_partial = !(control & CTRL_ENDP); + + timer->adjust(attotime::from_usec(20)); + } +} + +TIMER_CALLBACK_MEMBER(mie_device::update_irq_reply) +{ + if(control & CTRL_RXB) { + control &= ~CTRL_RXB; + control |= CTRL_RFB; + raise_irq(maple_irqlevel); + } + if(control & (CTRL_TXB|CTRL_CTXB)) { + reply_ready(0); + lreg -= reply_size; + if(reply_partial) { + control &= ~CTRL_CTXB; + control |= CTRL_EMP; + } else { + control &= ~(CTRL_TXB|CTRL_CTXB); + control |= CTRL_TFB|CTRL_EMP; + } + } + if(control & CTRL_HRES) { + raise_irq(maple_irqlevel); + } +} + +void mie_device::maple_w(const uint32_t *data, uint32_t in_size) +{ + memcpy(tbuf, data, in_size*4); + lreg = in_size-1; + // currently not known how/then CTRL_HRES is cleared after reset, lets clear it at packet receive + control &= ~(CTRL_HRES|CTRL_TXB|CTRL_TFB|CTRL_RFB|CTRL_BFOV); + control |= CTRL_RXB; + + timer->adjust(attotime::from_usec(20)); +} + +uint8_t mie_device::read_ff() +{ + return 0xff; +} + +uint8_t mie_device::read_00() +{ + return 0x00; +} + +uint8_t mie_device::read_78xx(offs_t offset) +{ + // Internal rom tests (7800) & 80 and jumps to 8010 if non-zero + // What we return is what a memdump sees on a naomi2 board + return offset & 4 ? 0xff : 0x00; +} + +uint8_t mie_device::gpio_r(offs_t offset) +{ + if(gpiodir & (1 << offset)) + return gpio_port[offset] ? gpio_port[offset]->read() : 0xff; + else + return gpio_val[offset]; +} + +void mie_device::gpio_w(offs_t offset, uint8_t data) +{ + gpio_val[offset] = data; + if(!(gpiodir & (1 << offset)) && gpio_port[offset]) + gpio_port[offset]->write(data, 0xff); +} + +uint8_t mie_device::gpiodir_r() +{ + return gpiodir; +} + +void mie_device::gpiodir_w(uint8_t data) +{ + gpiodir = data; +} + +uint8_t mie_device::adc_r() +{ + return 0; +} + +void mie_device::adc_w(uint8_t data) +{ +} + +uint8_t mie_device::irq_enable_r() +{ + return irq_enable; +} + +void mie_device::irq_enable_w(uint8_t data) +{ + irq_enable = data; + recalc_irq(); +} + +uint8_t mie_device::maple_irqlevel_r() +{ + return maple_irqlevel; +} + +void mie_device::maple_irqlevel_w(uint8_t data) +{ + maple_irqlevel = data; +} + +uint8_t mie_device::irq_pending_r() +{ + return irq_pending; +} + +void mie_device::irq_pending_w(uint8_t data) +{ + irq_pending = data; + recalc_irq(); +} + +void mie_device::recalc_irq() +{ + cpu->set_input_line(0, irq_enable & irq_pending & 0x7f ? ASSERT_LINE : CLEAR_LINE); +} + +IRQ_CALLBACK_MEMBER(mie_device::irq_callback) +{ + if(!(irq_enable & irq_pending & 0x7f)) + throw emu_fatalerror("MIE irq callback called with enable=%02x, pending=%02x", irq_enable, irq_pending); + + int level = -1; + for(level = 0; level < 7; level++) + if((irq_enable & irq_pending) & (1 << level)) + break; + + irq_pending &= ~(1 << level); + recalc_irq(); + return 0xf2+2*level; +} + +void mie_device::raise_irq(int level) +{ + if(level>=0 && level<7) { + irq_pending |= 1<<level; + recalc_irq(); + } +} + +uint8_t mie_device::tbuf_r(offs_t offset) +{ + return tbuf[offset >> 2] >> (8*(offset & 3)); +} + +void mie_device::tbuf_w(offs_t offset, uint8_t data) +{ + int shift = (offset & 3)*8; + tbuf[offset >> 2] = (tbuf[offset >> 2] & ~(255 << shift)) | (data << shift); +} + +uint8_t mie_device::lreg_r() +{ + return lreg; +} + +void mie_device::lreg_w(uint8_t data) +{ + lreg = data; +} + +uint8_t mie_device::jvs_r() +{ + if(jvs_lcr & 0x80) + return 0; + + const uint8_t *buf; + uint32_t size; + jvs->get_encoded_reply(buf, size); + if(jvs_rpos >= size) + return 0; + return buf[jvs_rpos++]; +} + +void mie_device::jvs_w(uint8_t data) +{ + if(jvs_lcr & 0x80) + return; + + jvs->push(data); +} + +void mie_device::jvs_dest_w(uint8_t data) +{ + jvs_dest = data; +} + +uint8_t mie_device::jvs_status_r() +{ + // 01 = ready for reading + // 20 = ready for writing + // 40 = sending done + const uint8_t *buf; + uint32_t size; + jvs->get_encoded_reply(buf, size); + return 0x60 | (jvs_rpos < size ? 1 : 0); +} + +void mie_device::jvs_control_w(uint8_t data) +{ + if((jvs_control & 1) && !(data & 1)) { + jvs->commit_encoded(); + jvs_rpos = 0; + } + jvs_control = data; +} + +void mie_device::jvs_lcr_w(uint8_t data) +{ + jvs_lcr = data; +} + +uint8_t mie_device::jvs_sense_r() +{ + return 0x8c | (jvs->get_address_set_line() ? 2 : 0) | (jvs->get_presence_line() ? 0 : 1); +} + +void mie_device::maple_reset() +{ + control &= ~(CTRL_RXB|CTRL_TXB|CTRL_TFB|CTRL_RFB|CTRL_BFOV); + control |= CTRL_HRES; + + timer->adjust(attotime::from_usec(20)); +} |